1 //===- Standard pass instrumentations handling ----------------*- C++ -*--===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// This file defines IR-printing pass instrumentation callbacks as well as
11 /// StandardInstrumentations class that manages standard pass instrumentations.
13 //===----------------------------------------------------------------------===//
15 #include "llvm/Passes/StandardInstrumentations.h"
16 #include "llvm/ADT/Any.h"
17 #include "llvm/ADT/StableHashing.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Analysis/CallGraphSCCPass.h"
20 #include "llvm/Analysis/LazyCallGraph.h"
21 #include "llvm/Analysis/LoopInfo.h"
22 #include "llvm/CodeGen/MIRPrinter.h"
23 #include "llvm/CodeGen/MachineFunction.h"
24 #include "llvm/CodeGen/MachineModuleInfo.h"
25 #include "llvm/CodeGen/MachineVerifier.h"
26 #include "llvm/IR/Constants.h"
27 #include "llvm/IR/Function.h"
28 #include "llvm/IR/Module.h"
29 #include "llvm/IR/PassInstrumentation.h"
30 #include "llvm/IR/PassManager.h"
31 #include "llvm/IR/PrintPasses.h"
32 #include "llvm/IR/StructuralHash.h"
33 #include "llvm/IR/Verifier.h"
34 #include "llvm/Support/CommandLine.h"
35 #include "llvm/Support/CrashRecoveryContext.h"
36 #include "llvm/Support/Debug.h"
37 #include "llvm/Support/Error.h"
38 #include "llvm/Support/FormatVariadic.h"
39 #include "llvm/Support/GraphWriter.h"
40 #include "llvm/Support/MemoryBuffer.h"
41 #include "llvm/Support/Path.h"
42 #include "llvm/Support/Program.h"
43 #include "llvm/Support/Regex.h"
44 #include "llvm/Support/Signals.h"
45 #include "llvm/Support/raw_ostream.h"
46 #include <unordered_map>
47 #include <unordered_set>
53 static cl::opt
<bool> VerifyAnalysisInvalidation("verify-analysis-invalidation",
55 #ifdef EXPENSIVE_CHECKS
62 // An option that supports the -print-changed option. See
63 // the description for -print-changed for an explanation of the use
64 // of this option. Note that this option has no effect without -print-changed.
66 PrintChangedBefore("print-before-changed",
67 cl::desc("Print before passes that change them"),
68 cl::init(false), cl::Hidden
);
70 // An option for specifying the dot used by
71 // print-changed=[dot-cfg | dot-cfg-quiet]
72 static cl::opt
<std::string
>
73 DotBinary("print-changed-dot-path", cl::Hidden
, cl::init("dot"),
74 cl::desc("system dot used by change reporters"));
76 // An option that determines the colour used for elements that are only
77 // in the before part. Must be a colour named in appendix J of
78 // https://graphviz.org/pdf/dotguide.pdf
79 static cl::opt
<std::string
>
80 BeforeColour("dot-cfg-before-color",
81 cl::desc("Color for dot-cfg before elements"), cl::Hidden
,
83 // An option that determines the colour used for elements that are only
84 // in the after part. Must be a colour named in appendix J of
85 // https://graphviz.org/pdf/dotguide.pdf
86 static cl::opt
<std::string
>
87 AfterColour("dot-cfg-after-color",
88 cl::desc("Color for dot-cfg after elements"), cl::Hidden
,
89 cl::init("forestgreen"));
90 // An option that determines the colour used for elements that are in both
91 // the before and after parts. Must be a colour named in appendix J of
92 // https://graphviz.org/pdf/dotguide.pdf
93 static cl::opt
<std::string
>
94 CommonColour("dot-cfg-common-color",
95 cl::desc("Color for dot-cfg common elements"), cl::Hidden
,
98 // An option that determines where the generated website file (named
99 // passes.html) and the associated pdf files (named diff_*.pdf) are saved.
100 static cl::opt
<std::string
> DotCfgDir(
102 cl::desc("Generate dot files into specified directory for changed IRs"),
103 cl::Hidden
, cl::init("./"));
105 // Options to print the IR that was being processed when a pass crashes.
106 static cl::opt
<std::string
> PrintOnCrashPath(
107 "print-on-crash-path",
108 cl::desc("Print the last form of the IR before crash to a file"),
111 static cl::opt
<bool> PrintOnCrash(
113 cl::desc("Print the last form of the IR before crash (use -print-on-crash-path to dump to a file)"),
116 static cl::opt
<std::string
> OptBisectPrintIRPath(
117 "opt-bisect-print-ir-path",
118 cl::desc("Print IR to path when opt-bisect-limit is reached"), cl::Hidden
);
120 static cl::opt
<bool> PrintPassNumbers(
121 "print-pass-numbers", cl::init(false), cl::Hidden
,
122 cl::desc("Print pass names and their ordinals"));
124 static cl::opt
<unsigned> PrintBeforePassNumber(
125 "print-before-pass-number", cl::init(0), cl::Hidden
,
126 cl::desc("Print IR before the pass with this number as "
127 "reported by print-pass-numbers"));
129 static cl::opt
<unsigned>
130 PrintAfterPassNumber("print-after-pass-number", cl::init(0), cl::Hidden
,
131 cl::desc("Print IR after the pass with this number as "
132 "reported by print-pass-numbers"));
134 static cl::opt
<std::string
> IRDumpDirectory(
136 cl::desc("If specified, IR printed using the "
137 "-print-[before|after]{-all} options will be dumped into "
138 "files in this directory rather than written to stderr"),
139 cl::Hidden
, cl::value_desc("filename"));
141 template <typename IRUnitT
> static const IRUnitT
*unwrapIR(Any IR
) {
142 const IRUnitT
**IRPtr
= llvm::any_cast
<const IRUnitT
*>(&IR
);
143 return IRPtr
? *IRPtr
: nullptr;
148 // An option for specifying an executable that will be called with the IR
149 // everytime it changes in the opt pipeline. It will also be called on
150 // the initial IR as it enters the pipeline. The executable will be passed
151 // the name of a temporary file containing the IR and the PassID. This may
152 // be used, for example, to call llc on the IR and run a test to determine
153 // which pass makes a change that changes the functioning of the IR.
154 // The usual modifier options work as expected.
155 static cl::opt
<std::string
>
156 TestChanged("exec-on-ir-change", cl::Hidden
, cl::init(""),
157 cl::desc("exe called with module IR after each pass that "
160 /// Extract Module out of \p IR unit. May return nullptr if \p IR does not match
161 /// certain global filters. Will never return nullptr if \p Force is true.
162 const Module
*unwrapModule(Any IR
, bool Force
= false) {
163 if (const auto *M
= unwrapIR
<Module
>(IR
))
166 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
167 if (!Force
&& !isFunctionInPrintList(F
->getName()))
170 return F
->getParent();
173 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
174 for (const LazyCallGraph::Node
&N
: *C
) {
175 const Function
&F
= N
.getFunction();
176 if (Force
|| (!F
.isDeclaration() && isFunctionInPrintList(F
.getName()))) {
177 return F
.getParent();
180 assert(!Force
&& "Expected a module");
184 if (const auto *L
= unwrapIR
<Loop
>(IR
)) {
185 const Function
*F
= L
->getHeader()->getParent();
186 if (!Force
&& !isFunctionInPrintList(F
->getName()))
188 return F
->getParent();
191 if (const auto *MF
= unwrapIR
<MachineFunction
>(IR
)) {
192 if (!Force
&& !isFunctionInPrintList(MF
->getName()))
194 return MF
->getFunction().getParent();
197 llvm_unreachable("Unknown IR unit");
200 void printIR(raw_ostream
&OS
, const Function
*F
) {
201 if (!isFunctionInPrintList(F
->getName()))
206 void printIR(raw_ostream
&OS
, const Module
*M
) {
207 if (isFunctionInPrintList("*") || forcePrintModuleIR()) {
208 M
->print(OS
, nullptr);
210 for (const auto &F
: M
->functions()) {
216 void printIR(raw_ostream
&OS
, const LazyCallGraph::SCC
*C
) {
217 for (const LazyCallGraph::Node
&N
: *C
) {
218 const Function
&F
= N
.getFunction();
219 if (!F
.isDeclaration() && isFunctionInPrintList(F
.getName())) {
225 void printIR(raw_ostream
&OS
, const Loop
*L
) {
226 const Function
*F
= L
->getHeader()->getParent();
227 if (!isFunctionInPrintList(F
->getName()))
229 printLoop(const_cast<Loop
&>(*L
), OS
);
232 void printIR(raw_ostream
&OS
, const MachineFunction
*MF
) {
233 if (!isFunctionInPrintList(MF
->getName()))
238 std::string
getIRName(Any IR
) {
239 if (unwrapIR
<Module
>(IR
))
242 if (const auto *F
= unwrapIR
<Function
>(IR
))
243 return F
->getName().str();
245 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
248 if (const auto *L
= unwrapIR
<Loop
>(IR
))
249 return "loop %" + L
->getName().str() + " in function " +
250 L
->getHeader()->getParent()->getName().str();
252 if (const auto *MF
= unwrapIR
<MachineFunction
>(IR
))
253 return MF
->getName().str();
255 llvm_unreachable("Unknown wrapped IR type");
258 bool moduleContainsFilterPrintFunc(const Module
&M
) {
259 return any_of(M
.functions(),
260 [](const Function
&F
) {
261 return isFunctionInPrintList(F
.getName());
263 isFunctionInPrintList("*");
266 bool sccContainsFilterPrintFunc(const LazyCallGraph::SCC
&C
) {
268 [](const LazyCallGraph::Node
&N
) {
269 return isFunctionInPrintList(N
.getName());
271 isFunctionInPrintList("*");
274 bool shouldPrintIR(Any IR
) {
275 if (const auto *M
= unwrapIR
<Module
>(IR
))
276 return moduleContainsFilterPrintFunc(*M
);
278 if (const auto *F
= unwrapIR
<Function
>(IR
))
279 return isFunctionInPrintList(F
->getName());
281 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
282 return sccContainsFilterPrintFunc(*C
);
284 if (const auto *L
= unwrapIR
<Loop
>(IR
))
285 return isFunctionInPrintList(L
->getHeader()->getParent()->getName());
287 if (const auto *MF
= unwrapIR
<MachineFunction
>(IR
))
288 return isFunctionInPrintList(MF
->getName());
289 llvm_unreachable("Unknown wrapped IR type");
292 /// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into
293 /// Any and does actual print job.
294 void unwrapAndPrint(raw_ostream
&OS
, Any IR
) {
295 if (!shouldPrintIR(IR
))
298 if (forcePrintModuleIR()) {
299 auto *M
= unwrapModule(IR
);
300 assert(M
&& "should have unwrapped module");
305 if (const auto *M
= unwrapIR
<Module
>(IR
)) {
310 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
315 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
320 if (const auto *L
= unwrapIR
<Loop
>(IR
)) {
325 if (const auto *MF
= unwrapIR
<MachineFunction
>(IR
)) {
329 llvm_unreachable("Unknown wrapped IR type");
332 // Return true when this is a pass for which changes should be ignored
333 bool isIgnored(StringRef PassID
) {
334 return isSpecialPass(PassID
,
335 {"PassManager", "PassAdaptor", "AnalysisManagerProxy",
336 "DevirtSCCRepeatedPass", "ModuleInlinerWrapperPass",
337 "VerifierPass", "PrintModulePass", "PrintMIRPass",
338 "PrintMIRPreparePass"});
341 std::string
makeHTMLReady(StringRef SR
) {
345 SR
.take_until([](char C
) { return C
== '<' || C
== '>'; });
346 S
.append(Clean
.str());
347 SR
= SR
.drop_front(Clean
.size());
350 S
.append(SR
[0] == '<' ? "<" : ">");
351 SR
= SR
.drop_front();
353 llvm_unreachable("problems converting string to HTML");
356 // Return the module when that is the appropriate level of comparison for \p IR.
357 const Module
*getModuleForComparison(Any IR
) {
358 if (const auto *M
= unwrapIR
<Module
>(IR
))
360 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
361 return C
->begin()->getFunction().getParent();
365 bool isInterestingFunction(const Function
&F
) {
366 return isFunctionInPrintList(F
.getName());
369 // Return true when this is a pass on IR for which printing
370 // of changes is desired.
371 bool isInteresting(Any IR
, StringRef PassID
, StringRef PassName
) {
372 if (isIgnored(PassID
) || !isPassInPrintList(PassName
))
374 if (const auto *F
= unwrapIR
<Function
>(IR
))
375 return isInterestingFunction(*F
);
381 template <typename T
> ChangeReporter
<T
>::~ChangeReporter() {
382 assert(BeforeStack
.empty() && "Problem with Change Printer stack.");
385 template <typename T
>
386 void ChangeReporter
<T
>::saveIRBeforePass(Any IR
, StringRef PassID
,
387 StringRef PassName
) {
388 // Is this the initial IR?
395 // Always need to place something on the stack because invalidated passes
396 // are not given the IR so it cannot be determined whether the pass was for
397 // something that was filtered out.
398 BeforeStack
.emplace_back();
400 if (!isInteresting(IR
, PassID
, PassName
))
403 // Save the IR representation on the stack.
404 T
&Data
= BeforeStack
.back();
405 generateIRRepresentation(IR
, PassID
, Data
);
408 template <typename T
>
409 void ChangeReporter
<T
>::handleIRAfterPass(Any IR
, StringRef PassID
,
410 StringRef PassName
) {
411 assert(!BeforeStack
.empty() && "Unexpected empty stack encountered.");
413 std::string Name
= getIRName(IR
);
415 if (isIgnored(PassID
)) {
417 handleIgnored(PassID
, Name
);
418 } else if (!isInteresting(IR
, PassID
, PassName
)) {
420 handleFiltered(PassID
, Name
);
422 // Get the before rep from the stack
423 T
&Before
= BeforeStack
.back();
424 // Create the after rep
426 generateIRRepresentation(IR
, PassID
, After
);
428 // Was there a change in IR?
429 if (Before
== After
) {
431 omitAfter(PassID
, Name
);
433 handleAfter(PassID
, Name
, Before
, After
, IR
);
435 BeforeStack
.pop_back();
438 template <typename T
>
439 void ChangeReporter
<T
>::handleInvalidatedPass(StringRef PassID
) {
440 assert(!BeforeStack
.empty() && "Unexpected empty stack encountered.");
442 // Always flag it as invalidated as we cannot determine when
443 // a pass for a filtered function is invalidated since we do not
444 // get the IR in the call. Also, the output is just alternate
445 // forms of the banner anyway.
447 handleInvalidated(PassID
);
448 BeforeStack
.pop_back();
451 template <typename T
>
452 void ChangeReporter
<T
>::registerRequiredCallbacks(
453 PassInstrumentationCallbacks
&PIC
) {
454 PIC
.registerBeforeNonSkippedPassCallback([&PIC
, this](StringRef P
, Any IR
) {
455 saveIRBeforePass(IR
, P
, PIC
.getPassNameForClassName(P
));
458 PIC
.registerAfterPassCallback(
459 [&PIC
, this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
460 handleIRAfterPass(IR
, P
, PIC
.getPassNameForClassName(P
));
462 PIC
.registerAfterPassInvalidatedCallback(
463 [this](StringRef P
, const PreservedAnalyses
&) {
464 handleInvalidatedPass(P
);
468 template <typename T
>
469 TextChangeReporter
<T
>::TextChangeReporter(bool Verbose
)
470 : ChangeReporter
<T
>(Verbose
), Out(dbgs()) {}
472 template <typename T
> void TextChangeReporter
<T
>::handleInitialIR(Any IR
) {
473 // Always print the module.
474 // Unwrap and print directly to avoid filtering problems in general routines.
475 auto *M
= unwrapModule(IR
, /*Force=*/true);
476 assert(M
&& "Expected module to be unwrapped when forced.");
477 Out
<< "*** IR Dump At Start ***\n";
478 M
->print(Out
, nullptr);
481 template <typename T
>
482 void TextChangeReporter
<T
>::omitAfter(StringRef PassID
, std::string
&Name
) {
483 Out
<< formatv("*** IR Dump After {0} on {1} omitted because no change ***\n",
487 template <typename T
>
488 void TextChangeReporter
<T
>::handleInvalidated(StringRef PassID
) {
489 Out
<< formatv("*** IR Pass {0} invalidated ***\n", PassID
);
492 template <typename T
>
493 void TextChangeReporter
<T
>::handleFiltered(StringRef PassID
,
495 SmallString
<20> Banner
=
496 formatv("*** IR Dump After {0} on {1} filtered out ***\n", PassID
, Name
);
500 template <typename T
>
501 void TextChangeReporter
<T
>::handleIgnored(StringRef PassID
, std::string
&Name
) {
502 Out
<< formatv("*** IR Pass {0} on {1} ignored ***\n", PassID
, Name
);
505 IRChangedPrinter::~IRChangedPrinter() = default;
507 void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
508 if (PrintChanged
== ChangePrinter::Verbose
||
509 PrintChanged
== ChangePrinter::Quiet
)
510 TextChangeReporter
<std::string
>::registerRequiredCallbacks(PIC
);
513 void IRChangedPrinter::generateIRRepresentation(Any IR
, StringRef PassID
,
514 std::string
&Output
) {
515 raw_string_ostream
OS(Output
);
516 unwrapAndPrint(OS
, IR
);
520 void IRChangedPrinter::handleAfter(StringRef PassID
, std::string
&Name
,
521 const std::string
&Before
,
522 const std::string
&After
, Any
) {
523 // Report the IR before the changes when requested.
524 if (PrintChangedBefore
)
525 Out
<< "*** IR Dump Before " << PassID
<< " on " << Name
<< " ***\n"
528 // We might not get anything to print if we only want to print a specific
529 // function but it gets deleted.
531 Out
<< "*** IR Deleted After " << PassID
<< " on " << Name
<< " ***\n";
535 Out
<< "*** IR Dump After " << PassID
<< " on " << Name
<< " ***\n" << After
;
538 IRChangedTester::~IRChangedTester() {}
540 void IRChangedTester::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
541 if (TestChanged
!= "")
542 TextChangeReporter
<std::string
>::registerRequiredCallbacks(PIC
);
545 void IRChangedTester::handleIR(const std::string
&S
, StringRef PassID
) {
546 // Store the body into a temporary file
547 static SmallVector
<int> FD
{-1};
548 SmallVector
<StringRef
> SR
{S
};
549 static SmallVector
<std::string
> FileName
{""};
550 if (prepareTempFiles(FD
, SR
, FileName
)) {
551 dbgs() << "Unable to create temporary file.";
554 static ErrorOr
<std::string
> Exe
= sys::findProgramByName(TestChanged
);
556 dbgs() << "Unable to find test-changed executable.";
560 StringRef Args
[] = {TestChanged
, FileName
[0], PassID
};
561 int Result
= sys::ExecuteAndWait(*Exe
, Args
);
563 dbgs() << "Error executing test-changed executable.";
567 if (cleanUpTempFiles(FileName
))
568 dbgs() << "Unable to remove temporary file.";
571 void IRChangedTester::handleInitialIR(Any IR
) {
572 // Always test the initial module.
573 // Unwrap and print directly to avoid filtering problems in general routines.
575 generateIRRepresentation(IR
, "Initial IR", S
);
576 handleIR(S
, "Initial IR");
579 void IRChangedTester::omitAfter(StringRef PassID
, std::string
&Name
) {}
580 void IRChangedTester::handleInvalidated(StringRef PassID
) {}
581 void IRChangedTester::handleFiltered(StringRef PassID
, std::string
&Name
) {}
582 void IRChangedTester::handleIgnored(StringRef PassID
, std::string
&Name
) {}
583 void IRChangedTester::handleAfter(StringRef PassID
, std::string
&Name
,
584 const std::string
&Before
,
585 const std::string
&After
, Any
) {
586 handleIR(After
, PassID
);
589 template <typename T
>
590 void OrderedChangedData
<T
>::report(
591 const OrderedChangedData
&Before
, const OrderedChangedData
&After
,
592 function_ref
<void(const T
*, const T
*)> HandlePair
) {
593 const auto &BFD
= Before
.getData();
594 const auto &AFD
= After
.getData();
595 std::vector
<std::string
>::const_iterator BI
= Before
.getOrder().begin();
596 std::vector
<std::string
>::const_iterator BE
= Before
.getOrder().end();
597 std::vector
<std::string
>::const_iterator AI
= After
.getOrder().begin();
598 std::vector
<std::string
>::const_iterator AE
= After
.getOrder().end();
600 auto HandlePotentiallyRemovedData
= [&](std::string S
) {
601 // The order in LLVM may have changed so check if still exists.
603 // This has been removed.
604 HandlePair(&BFD
.find(*BI
)->getValue(), nullptr);
607 auto HandleNewData
= [&](std::vector
<const T
*> &Q
) {
608 // Print out any queued up new sections
609 for (const T
*NBI
: Q
)
610 HandlePair(nullptr, NBI
);
614 // Print out the data in the after order, with before ones interspersed
615 // appropriately (ie, somewhere near where they were in the before list).
616 // Start at the beginning of both lists. Loop through the
617 // after list. If an element is common, then advance in the before list
618 // reporting the removed ones until the common one is reached. Report any
619 // queued up new ones and then report the common one. If an element is not
620 // common, then enqueue it for reporting. When the after list is exhausted,
621 // loop through the before list, reporting any removed ones. Finally,
622 // report the rest of the enqueued new ones.
623 std::vector
<const T
*> NewDataQueue
;
625 if (!BFD
.count(*AI
)) {
626 // This section is new so place it in the queue. This will cause it
627 // to be reported after deleted sections.
628 NewDataQueue
.emplace_back(&AFD
.find(*AI
)->getValue());
632 // This section is in both; advance and print out any before-only
633 // until we get to it.
634 // It's possible that this section has moved to be later than before. This
635 // will mess up printing most blocks side by side, but it's a rare case and
636 // it's better than crashing.
637 while (BI
!= BE
&& *BI
!= *AI
) {
638 HandlePotentiallyRemovedData(*BI
);
641 // Report any new sections that were queued up and waiting.
642 HandleNewData(NewDataQueue
);
644 const T
&AData
= AFD
.find(*AI
)->getValue();
645 const T
&BData
= BFD
.find(*AI
)->getValue();
646 HandlePair(&BData
, &AData
);
652 // Check any remaining before sections to see if they have been removed
654 HandlePotentiallyRemovedData(*BI
);
658 HandleNewData(NewDataQueue
);
661 template <typename T
>
662 void IRComparer
<T
>::compare(
664 std::function
<void(bool InModule
, unsigned Minor
,
665 const FuncDataT
<T
> &Before
, const FuncDataT
<T
> &After
)>
667 if (!CompareModule
) {
668 // Just handle the single function.
669 assert(Before
.getData().size() == 1 && After
.getData().size() == 1 &&
670 "Expected only one function.");
671 CompareFunc(false, 0, Before
.getData().begin()->getValue(),
672 After
.getData().begin()->getValue());
677 FuncDataT
<T
> Missing("");
678 IRDataT
<T
>::report(Before
, After
,
679 [&](const FuncDataT
<T
> *B
, const FuncDataT
<T
> *A
) {
680 assert((B
|| A
) && "Both functions cannot be missing.");
685 CompareFunc(true, Minor
++, *B
, *A
);
689 template <typename T
> void IRComparer
<T
>::analyzeIR(Any IR
, IRDataT
<T
> &Data
) {
690 if (const Module
*M
= getModuleForComparison(IR
)) {
691 // Create data for each existing/interesting function in the module.
692 for (const Function
&F
: *M
)
693 generateFunctionData(Data
, F
);
697 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
698 generateFunctionData(Data
, *F
);
702 if (const auto *L
= unwrapIR
<Loop
>(IR
)) {
703 auto *F
= L
->getHeader()->getParent();
704 generateFunctionData(Data
, *F
);
708 if (const auto *MF
= unwrapIR
<MachineFunction
>(IR
)) {
709 generateFunctionData(Data
, *MF
);
713 llvm_unreachable("Unknown IR unit");
716 static bool shouldGenerateData(const Function
&F
) {
717 return !F
.isDeclaration() && isFunctionInPrintList(F
.getName());
720 static bool shouldGenerateData(const MachineFunction
&MF
) {
721 return isFunctionInPrintList(MF
.getName());
724 template <typename T
>
725 template <typename FunctionT
>
726 bool IRComparer
<T
>::generateFunctionData(IRDataT
<T
> &Data
, const FunctionT
&F
) {
727 if (shouldGenerateData(F
)) {
728 FuncDataT
<T
> FD(F
.front().getName().str());
730 for (const auto &B
: F
) {
731 std::string BBName
= B
.getName().str();
732 if (BBName
.empty()) {
733 BBName
= formatv("{0}", I
);
736 FD
.getOrder().emplace_back(BBName
);
737 FD
.getData().insert({BBName
, B
});
739 Data
.getOrder().emplace_back(F
.getName());
740 Data
.getData().insert({F
.getName(), FD
});
746 PrintIRInstrumentation::~PrintIRInstrumentation() {
747 assert(PassRunDescriptorStack
.empty() &&
748 "PassRunDescriptorStack is not empty at exit");
751 static SmallString
<32> getIRFileDisplayName(Any IR
) {
752 SmallString
<32> Result
;
753 raw_svector_ostream
ResultStream(Result
);
754 const Module
*M
= unwrapModule(IR
);
755 stable_hash NameHash
= stable_hash_combine_string(M
->getName());
756 unsigned int MaxHashWidth
= sizeof(stable_hash
) * 8 / 4;
757 write_hex(ResultStream
, NameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
758 if (unwrapIR
<Module
>(IR
)) {
759 ResultStream
<< "-module";
760 } else if (const auto *F
= unwrapIR
<Function
>(IR
)) {
761 ResultStream
<< "-function-";
762 stable_hash FunctionNameHash
= stable_hash_combine_string(F
->getName());
763 write_hex(ResultStream
, FunctionNameHash
, HexPrintStyle::Lower
,
765 } else if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
766 ResultStream
<< "-scc-";
767 stable_hash SCCNameHash
= stable_hash_combine_string(C
->getName());
768 write_hex(ResultStream
, SCCNameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
769 } else if (const auto *L
= unwrapIR
<Loop
>(IR
)) {
770 ResultStream
<< "-loop-";
771 stable_hash LoopNameHash
= stable_hash_combine_string(L
->getName());
772 write_hex(ResultStream
, LoopNameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
773 } else if (const auto *MF
= unwrapIR
<MachineFunction
>(IR
)) {
774 ResultStream
<< "-machine-function-";
775 stable_hash MachineFunctionNameHash
=
776 stable_hash_combine_string(MF
->getName());
777 write_hex(ResultStream
, MachineFunctionNameHash
, HexPrintStyle::Lower
,
780 llvm_unreachable("Unknown wrapped IR type");
785 std::string
PrintIRInstrumentation::fetchDumpFilename(StringRef PassName
,
787 const StringRef RootDirectory
= IRDumpDirectory
;
788 assert(!RootDirectory
.empty() &&
789 "The flag -ir-dump-directory must be passed to dump IR to files");
790 SmallString
<128> ResultPath
;
791 ResultPath
+= RootDirectory
;
792 SmallString
<64> Filename
;
793 raw_svector_ostream
FilenameStream(Filename
);
794 FilenameStream
<< CurrentPassNumber
;
795 FilenameStream
<< "-";
796 FilenameStream
<< getIRFileDisplayName(IR
);
797 FilenameStream
<< "-";
798 FilenameStream
<< PassName
;
799 sys::path::append(ResultPath
, Filename
);
800 return std::string(ResultPath
);
803 enum class IRDumpFileSuffixType
{
809 static StringRef
getFileSuffix(IRDumpFileSuffixType Type
) {
810 static constexpr std::array FileSuffixes
= {"-before.ll", "-after.ll",
812 return FileSuffixes
[static_cast<size_t>(Type
)];
815 void PrintIRInstrumentation::pushPassRunDescriptor(
816 StringRef PassID
, Any IR
, std::string
&DumpIRFilename
) {
817 const Module
*M
= unwrapModule(IR
);
818 PassRunDescriptorStack
.emplace_back(
819 PassRunDescriptor(M
, DumpIRFilename
, getIRName(IR
), PassID
));
822 PrintIRInstrumentation::PassRunDescriptor
823 PrintIRInstrumentation::popPassRunDescriptor(StringRef PassID
) {
824 assert(!PassRunDescriptorStack
.empty() && "empty PassRunDescriptorStack");
825 PassRunDescriptor Descriptor
= PassRunDescriptorStack
.pop_back_val();
826 assert(Descriptor
.PassID
== PassID
&& "malformed PassRunDescriptorStack");
830 // Callers are responsible for closing the returned file descriptor
831 static int prepareDumpIRFileDescriptor(const StringRef DumpIRFilename
) {
833 auto ParentPath
= llvm::sys::path::parent_path(DumpIRFilename
);
834 if (!ParentPath
.empty()) {
835 std::error_code EC
= llvm::sys::fs::create_directories(ParentPath
);
837 report_fatal_error(Twine("Failed to create directory ") + ParentPath
+
838 " to support -ir-dump-directory: " + EC
.message());
841 EC
= sys::fs::openFile(DumpIRFilename
, Result
, sys::fs::CD_OpenAlways
,
842 sys::fs::FA_Write
, sys::fs::OF_Text
);
844 report_fatal_error(Twine("Failed to open ") + DumpIRFilename
+
845 " to support -ir-dump-directory: " + EC
.message());
849 void PrintIRInstrumentation::printBeforePass(StringRef PassID
, Any IR
) {
850 if (isIgnored(PassID
))
853 std::string DumpIRFilename
;
854 if (!IRDumpDirectory
.empty() &&
855 (shouldPrintBeforePass(PassID
) || shouldPrintAfterPass(PassID
) ||
856 shouldPrintBeforeCurrentPassNumber() ||
857 shouldPrintAfterCurrentPassNumber()))
858 DumpIRFilename
= fetchDumpFilename(PassID
, IR
);
860 // Saving Module for AfterPassInvalidated operations.
861 // Note: here we rely on a fact that we do not change modules while
862 // traversing the pipeline, so the latest captured module is good
863 // for all print operations that has not happen yet.
864 if (shouldPrintAfterPass(PassID
))
865 pushPassRunDescriptor(PassID
, IR
, DumpIRFilename
);
867 if (!shouldPrintIR(IR
))
872 if (shouldPrintPassNumbers())
873 dbgs() << " Running pass " << CurrentPassNumber
<< " " << PassID
874 << " on " << getIRName(IR
) << "\n";
876 if (shouldPrintAfterCurrentPassNumber())
877 pushPassRunDescriptor(PassID
, IR
, DumpIRFilename
);
879 if (!shouldPrintBeforePass(PassID
) && !shouldPrintBeforeCurrentPassNumber())
882 auto WriteIRToStream
= [&](raw_ostream
&Stream
) {
883 Stream
<< "; *** IR Dump Before ";
884 if (shouldPrintBeforeSomePassNumber())
885 Stream
<< CurrentPassNumber
<< "-";
886 Stream
<< PassID
<< " on " << getIRName(IR
) << " ***\n";
887 unwrapAndPrint(Stream
, IR
);
890 if (!DumpIRFilename
.empty()) {
891 DumpIRFilename
+= getFileSuffix(IRDumpFileSuffixType::Before
);
892 llvm::raw_fd_ostream DumpIRFileStream
{
893 prepareDumpIRFileDescriptor(DumpIRFilename
), /* shouldClose */ true};
894 WriteIRToStream(DumpIRFileStream
);
896 WriteIRToStream(dbgs());
900 void PrintIRInstrumentation::printAfterPass(StringRef PassID
, Any IR
) {
901 if (isIgnored(PassID
))
904 if (!shouldPrintAfterPass(PassID
) && !shouldPrintAfterCurrentPassNumber())
907 auto [M
, DumpIRFilename
, IRName
, StoredPassID
] = popPassRunDescriptor(PassID
);
908 assert(StoredPassID
== PassID
&& "mismatched PassID");
910 if (!shouldPrintIR(IR
) ||
911 (!shouldPrintAfterPass(PassID
) && !shouldPrintAfterCurrentPassNumber()))
914 auto WriteIRToStream
= [&](raw_ostream
&Stream
, const StringRef IRName
) {
915 Stream
<< "; *** IR Dump After ";
916 if (shouldPrintAfterSomePassNumber())
917 Stream
<< CurrentPassNumber
<< "-";
918 Stream
<< StringRef(formatv("{0}", PassID
)) << " on " << IRName
<< " ***\n";
919 unwrapAndPrint(Stream
, IR
);
922 if (!IRDumpDirectory
.empty()) {
923 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
924 "should be set in printBeforePass");
925 const std::string DumpIRFilenameWithSuffix
=
926 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::After
).str();
927 llvm::raw_fd_ostream DumpIRFileStream
{
928 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
929 /* shouldClose */ true};
930 WriteIRToStream(DumpIRFileStream
, IRName
);
932 WriteIRToStream(dbgs(), IRName
);
936 void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID
) {
937 if (isIgnored(PassID
))
940 if (!shouldPrintAfterPass(PassID
) && !shouldPrintAfterCurrentPassNumber())
943 auto [M
, DumpIRFilename
, IRName
, StoredPassID
] = popPassRunDescriptor(PassID
);
944 assert(StoredPassID
== PassID
&& "mismatched PassID");
945 // Additional filtering (e.g. -filter-print-func) can lead to module
946 // printing being skipped.
948 (!shouldPrintAfterPass(PassID
) && !shouldPrintAfterCurrentPassNumber()))
951 auto WriteIRToStream
= [&](raw_ostream
&Stream
, const Module
*M
,
952 const StringRef IRName
) {
953 SmallString
<20> Banner
;
954 Banner
= formatv("; *** IR Dump After {0} on {1} (invalidated) ***", PassID
,
956 Stream
<< Banner
<< "\n";
960 if (!IRDumpDirectory
.empty()) {
961 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
962 "should be set in printBeforePass");
963 const std::string DumpIRFilenameWithSuffix
=
964 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::Invalidated
).str();
965 llvm::raw_fd_ostream DumpIRFileStream
{
966 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
967 /* shouldClose */ true};
968 WriteIRToStream(DumpIRFileStream
, M
, IRName
);
970 WriteIRToStream(dbgs(), M
, IRName
);
974 bool PrintIRInstrumentation::shouldPrintBeforePass(StringRef PassID
) {
975 if (shouldPrintBeforeAll())
978 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
979 return is_contained(printBeforePasses(), PassName
);
982 bool PrintIRInstrumentation::shouldPrintAfterPass(StringRef PassID
) {
983 if (shouldPrintAfterAll())
986 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
987 return is_contained(printAfterPasses(), PassName
);
990 bool PrintIRInstrumentation::shouldPrintBeforeCurrentPassNumber() {
991 return shouldPrintBeforeSomePassNumber() &&
992 (CurrentPassNumber
== PrintBeforePassNumber
);
995 bool PrintIRInstrumentation::shouldPrintAfterCurrentPassNumber() {
996 return shouldPrintAfterSomePassNumber() &&
997 (CurrentPassNumber
== PrintAfterPassNumber
);
1000 bool PrintIRInstrumentation::shouldPrintPassNumbers() {
1001 return PrintPassNumbers
;
1004 bool PrintIRInstrumentation::shouldPrintBeforeSomePassNumber() {
1005 return PrintBeforePassNumber
> 0;
1008 bool PrintIRInstrumentation::shouldPrintAfterSomePassNumber() {
1009 return PrintAfterPassNumber
> 0;
1012 void PrintIRInstrumentation::registerCallbacks(
1013 PassInstrumentationCallbacks
&PIC
) {
1016 // BeforePass callback is not just for printing, it also saves a Module
1017 // for later use in AfterPassInvalidated and keeps tracks of the
1018 // CurrentPassNumber.
1019 if (shouldPrintPassNumbers() || shouldPrintBeforeSomePassNumber() ||
1020 shouldPrintAfterSomePassNumber() || shouldPrintBeforeSomePass() ||
1021 shouldPrintAfterSomePass())
1022 PIC
.registerBeforeNonSkippedPassCallback(
1023 [this](StringRef P
, Any IR
) { this->printBeforePass(P
, IR
); });
1025 if (shouldPrintAfterSomePass() || shouldPrintAfterSomePassNumber()) {
1026 PIC
.registerAfterPassCallback(
1027 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
1028 this->printAfterPass(P
, IR
);
1030 PIC
.registerAfterPassInvalidatedCallback(
1031 [this](StringRef P
, const PreservedAnalyses
&) {
1032 this->printAfterPassInvalidated(P
);
1037 void OptNoneInstrumentation::registerCallbacks(
1038 PassInstrumentationCallbacks
&PIC
) {
1039 PIC
.registerShouldRunOptionalPassCallback(
1040 [this](StringRef P
, Any IR
) { return this->shouldRun(P
, IR
); });
1043 bool OptNoneInstrumentation::shouldRun(StringRef PassID
, Any IR
) {
1044 const auto *F
= unwrapIR
<Function
>(IR
);
1046 if (const auto *L
= unwrapIR
<Loop
>(IR
))
1047 F
= L
->getHeader()->getParent();
1049 bool ShouldRun
= !(F
&& F
->hasOptNone());
1050 if (!ShouldRun
&& DebugLogging
) {
1051 errs() << "Skipping pass " << PassID
<< " on " << F
->getName()
1052 << " due to optnone attribute\n";
1057 bool OptPassGateInstrumentation::shouldRun(StringRef PassName
, Any IR
) {
1058 if (isIgnored(PassName
))
1062 Context
.getOptPassGate().shouldRunPass(PassName
, getIRName(IR
));
1063 if (!ShouldRun
&& !this->HasWrittenIR
&& !OptBisectPrintIRPath
.empty()) {
1064 // FIXME: print IR if limit is higher than number of opt-bisect
1066 this->HasWrittenIR
= true;
1067 const Module
*M
= unwrapModule(IR
, /*Force=*/true);
1068 assert((M
&& &M
->getContext() == &Context
) && "Missing/Mismatching Module");
1070 raw_fd_ostream
OS(OptBisectPrintIRPath
, EC
);
1072 report_fatal_error(errorCodeToError(EC
));
1073 M
->print(OS
, nullptr);
1078 void OptPassGateInstrumentation::registerCallbacks(
1079 PassInstrumentationCallbacks
&PIC
) {
1080 OptPassGate
&PassGate
= Context
.getOptPassGate();
1081 if (!PassGate
.isEnabled())
1084 PIC
.registerShouldRunOptionalPassCallback([this](StringRef PassName
, Any IR
) {
1085 return this->shouldRun(PassName
, IR
);
1089 raw_ostream
&PrintPassInstrumentation::print() {
1091 assert(Indent
>= 0);
1092 dbgs().indent(Indent
);
1097 void PrintPassInstrumentation::registerCallbacks(
1098 PassInstrumentationCallbacks
&PIC
) {
1102 std::vector
<StringRef
> SpecialPasses
;
1103 if (!Opts
.Verbose
) {
1104 SpecialPasses
.emplace_back("PassManager");
1105 SpecialPasses
.emplace_back("PassAdaptor");
1108 PIC
.registerBeforeSkippedPassCallback([this, SpecialPasses
](StringRef PassID
,
1110 assert(!isSpecialPass(PassID
, SpecialPasses
) &&
1111 "Unexpectedly skipping special pass");
1113 print() << "Skipping pass: " << PassID
<< " on " << getIRName(IR
) << "\n";
1115 PIC
.registerBeforeNonSkippedPassCallback([this, SpecialPasses
](
1116 StringRef PassID
, Any IR
) {
1117 if (isSpecialPass(PassID
, SpecialPasses
))
1121 OS
<< "Running pass: " << PassID
<< " on " << getIRName(IR
);
1122 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
1123 unsigned Count
= F
->getInstructionCount();
1124 OS
<< " (" << Count
<< " instruction";
1128 } else if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
1129 int Count
= C
->size();
1130 OS
<< " (" << Count
<< " node";
1138 PIC
.registerAfterPassCallback(
1139 [this, SpecialPasses
](StringRef PassID
, Any IR
,
1140 const PreservedAnalyses
&) {
1141 if (isSpecialPass(PassID
, SpecialPasses
))
1146 PIC
.registerAfterPassInvalidatedCallback(
1147 [this, SpecialPasses
](StringRef PassID
, Any IR
) {
1148 if (isSpecialPass(PassID
, SpecialPasses
))
1154 if (!Opts
.SkipAnalyses
) {
1155 PIC
.registerBeforeAnalysisCallback([this](StringRef PassID
, Any IR
) {
1156 print() << "Running analysis: " << PassID
<< " on " << getIRName(IR
)
1160 PIC
.registerAfterAnalysisCallback(
1161 [this](StringRef PassID
, Any IR
) { Indent
-= 2; });
1162 PIC
.registerAnalysisInvalidatedCallback([this](StringRef PassID
, Any IR
) {
1163 print() << "Invalidating analysis: " << PassID
<< " on " << getIRName(IR
)
1166 PIC
.registerAnalysesClearedCallback([this](StringRef IRName
) {
1167 print() << "Clearing all analysis results for: " << IRName
<< "\n";
1172 PreservedCFGCheckerInstrumentation::CFG::CFG(const Function
*F
,
1173 bool TrackBBLifetime
) {
1174 if (TrackBBLifetime
)
1175 BBGuards
= DenseMap
<intptr_t, BBGuard
>(F
->size());
1176 for (const auto &BB
: *F
) {
1178 BBGuards
->try_emplace(intptr_t(&BB
), &BB
);
1179 for (const auto *Succ
: successors(&BB
)) {
1182 BBGuards
->try_emplace(intptr_t(Succ
), Succ
);
1187 static void printBBName(raw_ostream
&out
, const BasicBlock
*BB
) {
1188 if (BB
->hasName()) {
1189 out
<< BB
->getName() << "<" << BB
<< ">";
1193 if (!BB
->getParent()) {
1194 out
<< "unnamed_removed<" << BB
<< ">";
1198 if (BB
->isEntryBlock()) {
1200 << "<" << BB
<< ">";
1204 unsigned FuncOrderBlockNum
= 0;
1205 for (auto &FuncBB
: *BB
->getParent()) {
1208 FuncOrderBlockNum
++;
1210 out
<< "unnamed_" << FuncOrderBlockNum
<< "<" << BB
<< ">";
1213 void PreservedCFGCheckerInstrumentation::CFG::printDiff(raw_ostream
&out
,
1216 assert(!After
.isPoisoned());
1217 if (Before
.isPoisoned()) {
1218 out
<< "Some blocks were deleted\n";
1222 // Find and print graph differences.
1223 if (Before
.Graph
.size() != After
.Graph
.size())
1224 out
<< "Different number of non-leaf basic blocks: before="
1225 << Before
.Graph
.size() << ", after=" << After
.Graph
.size() << "\n";
1227 for (auto &BB
: Before
.Graph
) {
1228 auto BA
= After
.Graph
.find(BB
.first
);
1229 if (BA
== After
.Graph
.end()) {
1230 out
<< "Non-leaf block ";
1231 printBBName(out
, BB
.first
);
1232 out
<< " is removed (" << BB
.second
.size() << " successors)\n";
1236 for (auto &BA
: After
.Graph
) {
1237 auto BB
= Before
.Graph
.find(BA
.first
);
1238 if (BB
== Before
.Graph
.end()) {
1239 out
<< "Non-leaf block ";
1240 printBBName(out
, BA
.first
);
1241 out
<< " is added (" << BA
.second
.size() << " successors)\n";
1245 if (BB
->second
== BA
.second
)
1248 out
<< "Different successors of block ";
1249 printBBName(out
, BA
.first
);
1250 out
<< " (unordered):\n";
1251 out
<< "- before (" << BB
->second
.size() << "): ";
1252 for (auto &SuccB
: BB
->second
) {
1253 printBBName(out
, SuccB
.first
);
1254 if (SuccB
.second
!= 1)
1255 out
<< "(" << SuccB
.second
<< "), ";
1260 out
<< "- after (" << BA
.second
.size() << "): ";
1261 for (auto &SuccA
: BA
.second
) {
1262 printBBName(out
, SuccA
.first
);
1263 if (SuccA
.second
!= 1)
1264 out
<< "(" << SuccA
.second
<< "), ";
1272 // PreservedCFGCheckerInstrumentation uses PreservedCFGCheckerAnalysis to check
1273 // passes, that reported they kept CFG analyses up-to-date, did not actually
1274 // change CFG. This check is done as follows. Before every functional pass in
1275 // BeforeNonSkippedPassCallback a CFG snapshot (an instance of
1276 // PreservedCFGCheckerInstrumentation::CFG) is requested from
1277 // FunctionAnalysisManager as a result of PreservedCFGCheckerAnalysis. When the
1278 // functional pass finishes and reports that CFGAnalyses or AllAnalyses are
1279 // up-to-date then the cached result of PreservedCFGCheckerAnalysis (if
1280 // available) is checked to be equal to a freshly created CFG snapshot.
1281 struct PreservedCFGCheckerAnalysis
1282 : public AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
> {
1283 friend AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
>;
1285 static AnalysisKey Key
;
1288 /// Provide the result type for this analysis pass.
1289 using Result
= PreservedCFGCheckerInstrumentation::CFG
;
1291 /// Run the analysis pass over a function and produce CFG.
1292 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1293 return Result(&F
, /* TrackBBLifetime */ true);
1297 AnalysisKey
PreservedCFGCheckerAnalysis::Key
;
1299 struct PreservedFunctionHashAnalysis
1300 : public AnalysisInfoMixin
<PreservedFunctionHashAnalysis
> {
1301 static AnalysisKey Key
;
1303 struct FunctionHash
{
1307 using Result
= FunctionHash
;
1309 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1310 return Result
{StructuralHash(F
)};
1314 AnalysisKey
PreservedFunctionHashAnalysis::Key
;
1316 struct PreservedModuleHashAnalysis
1317 : public AnalysisInfoMixin
<PreservedModuleHashAnalysis
> {
1318 static AnalysisKey Key
;
1324 using Result
= ModuleHash
;
1326 Result
run(Module
&F
, ModuleAnalysisManager
&FAM
) {
1327 return Result
{StructuralHash(F
)};
1331 AnalysisKey
PreservedModuleHashAnalysis::Key
;
1333 bool PreservedCFGCheckerInstrumentation::CFG::invalidate(
1334 Function
&F
, const PreservedAnalyses
&PA
,
1335 FunctionAnalysisManager::Invalidator
&) {
1336 auto PAC
= PA
.getChecker
<PreservedCFGCheckerAnalysis
>();
1337 return !(PAC
.preserved() || PAC
.preservedSet
<AllAnalysesOn
<Function
>>() ||
1338 PAC
.preservedSet
<CFGAnalyses
>());
1341 static SmallVector
<Function
*, 1> GetFunctions(Any IR
) {
1342 SmallVector
<Function
*, 1> Functions
;
1344 if (const auto *MaybeF
= unwrapIR
<Function
>(IR
)) {
1345 Functions
.push_back(const_cast<Function
*>(MaybeF
));
1346 } else if (const auto *MaybeM
= unwrapIR
<Module
>(IR
)) {
1347 for (Function
&F
: *const_cast<Module
*>(MaybeM
))
1348 Functions
.push_back(&F
);
1353 void PreservedCFGCheckerInstrumentation::registerCallbacks(
1354 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
&MAM
) {
1355 if (!VerifyAnalysisInvalidation
)
1358 bool Registered
= false;
1359 PIC
.registerBeforeNonSkippedPassCallback([this, &MAM
, Registered
](
1360 StringRef P
, Any IR
) mutable {
1361 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1362 assert(&PassStack
.emplace_back(P
));
1366 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1367 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1370 FAM
.registerPass([&] { return PreservedCFGCheckerAnalysis(); });
1371 FAM
.registerPass([&] { return PreservedFunctionHashAnalysis(); });
1372 MAM
.registerPass([&] { return PreservedModuleHashAnalysis(); });
1376 for (Function
*F
: GetFunctions(IR
)) {
1377 // Make sure a fresh CFG snapshot is available before the pass.
1378 FAM
.getResult
<PreservedCFGCheckerAnalysis
>(*F
);
1379 FAM
.getResult
<PreservedFunctionHashAnalysis
>(*F
);
1382 if (const auto *MPtr
= unwrapIR
<Module
>(IR
)) {
1383 auto &M
= *const_cast<Module
*>(MPtr
);
1384 MAM
.getResult
<PreservedModuleHashAnalysis
>(M
);
1388 PIC
.registerAfterPassInvalidatedCallback(
1389 [this](StringRef P
, const PreservedAnalyses
&PassPA
) {
1390 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1391 assert(PassStack
.pop_back_val() == P
&&
1392 "Before and After callbacks must correspond");
1397 PIC
.registerAfterPassCallback([this, &MAM
](StringRef P
, Any IR
,
1398 const PreservedAnalyses
&PassPA
) {
1399 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1400 assert(PassStack
.pop_back_val() == P
&&
1401 "Before and After callbacks must correspond");
1405 // We have to get the FAM via the MAM, rather than directly use a passed in
1406 // FAM because if MAM has not cached the FAM, it won't invalidate function
1408 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1409 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1412 for (Function
*F
: GetFunctions(IR
)) {
1413 if (auto *HashBefore
=
1414 FAM
.getCachedResult
<PreservedFunctionHashAnalysis
>(*F
)) {
1415 if (HashBefore
->Hash
!= StructuralHash(*F
)) {
1416 report_fatal_error(formatv(
1417 "Function @{0} changed by {1} without invalidating analyses",
1422 auto CheckCFG
= [](StringRef Pass
, StringRef FuncName
,
1423 const CFG
&GraphBefore
, const CFG
&GraphAfter
) {
1424 if (GraphAfter
== GraphBefore
)
1428 << "Error: " << Pass
1429 << " does not invalidate CFG analyses but CFG changes detected in "
1431 << FuncName
<< ":\n";
1432 CFG::printDiff(dbgs(), GraphBefore
, GraphAfter
);
1433 report_fatal_error(Twine("CFG unexpectedly changed by ", Pass
));
1436 if (auto *GraphBefore
=
1437 FAM
.getCachedResult
<PreservedCFGCheckerAnalysis
>(*F
))
1438 CheckCFG(P
, F
->getName(), *GraphBefore
,
1439 CFG(F
, /* TrackBBLifetime */ false));
1441 if (const auto *MPtr
= unwrapIR
<Module
>(IR
)) {
1442 auto &M
= *const_cast<Module
*>(MPtr
);
1443 if (auto *HashBefore
=
1444 MAM
.getCachedResult
<PreservedModuleHashAnalysis
>(M
)) {
1445 if (HashBefore
->Hash
!= StructuralHash(M
)) {
1446 report_fatal_error(formatv(
1447 "Module changed by {0} without invalidating analyses", P
));
1454 void VerifyInstrumentation::registerCallbacks(PassInstrumentationCallbacks
&PIC
,
1455 ModuleAnalysisManager
*MAM
) {
1456 PIC
.registerAfterPassCallback(
1457 [this, MAM
](StringRef P
, Any IR
, const PreservedAnalyses
&PassPA
) {
1458 if (isIgnored(P
) || P
== "VerifierPass")
1460 const auto *F
= unwrapIR
<Function
>(IR
);
1462 if (const auto *L
= unwrapIR
<Loop
>(IR
))
1463 F
= L
->getHeader()->getParent();
1468 dbgs() << "Verifying function " << F
->getName() << "\n";
1470 if (verifyFunction(*F
, &errs()))
1471 report_fatal_error(formatv("Broken function found after pass "
1472 "\"{0}\", compilation aborted!",
1475 const auto *M
= unwrapIR
<Module
>(IR
);
1477 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
1478 M
= C
->begin()->getFunction().getParent();
1483 dbgs() << "Verifying module " << M
->getName() << "\n";
1485 if (verifyModule(*M
, &errs()))
1486 report_fatal_error(formatv("Broken module found after pass "
1487 "\"{0}\", compilation aborted!",
1491 if (auto *MF
= unwrapIR
<MachineFunction
>(IR
)) {
1493 dbgs() << "Verifying machine function " << MF
->getName() << '\n';
1494 std::string Banner
=
1495 formatv("Broken machine function found after pass "
1496 "\"{0}\", compilation aborted!",
1499 Module
&M
= const_cast<Module
&>(*MF
->getFunction().getParent());
1501 MAM
->getResult
<MachineFunctionAnalysisManagerModuleProxy
>(M
)
1503 MachineVerifierPass
Verifier(Banner
);
1504 Verifier
.run(const_cast<MachineFunction
&>(*MF
), MFAM
);
1506 verifyMachineFunction(Banner
, *MF
);
1513 InLineChangePrinter::~InLineChangePrinter() = default;
1515 void InLineChangePrinter::generateIRRepresentation(Any IR
,
1517 IRDataT
<EmptyData
> &D
) {
1518 IRComparer
<EmptyData
>::analyzeIR(IR
, D
);
1521 void InLineChangePrinter::handleAfter(StringRef PassID
, std::string
&Name
,
1522 const IRDataT
<EmptyData
> &Before
,
1523 const IRDataT
<EmptyData
> &After
,
1525 SmallString
<20> Banner
=
1526 formatv("*** IR Dump After {0} on {1} ***\n", PassID
, Name
);
1528 IRComparer
<EmptyData
>(Before
, After
)
1529 .compare(getModuleForComparison(IR
),
1530 [&](bool InModule
, unsigned Minor
,
1531 const FuncDataT
<EmptyData
> &Before
,
1532 const FuncDataT
<EmptyData
> &After
) -> void {
1533 handleFunctionCompare(Name
, "", PassID
, " on ", InModule
,
1534 Minor
, Before
, After
);
1539 void InLineChangePrinter::handleFunctionCompare(
1540 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
1541 bool InModule
, unsigned Minor
, const FuncDataT
<EmptyData
> &Before
,
1542 const FuncDataT
<EmptyData
> &After
) {
1543 // Print a banner when this is being shown in the context of a module
1545 Out
<< "\n*** IR for function " << Name
<< " ***\n";
1547 FuncDataT
<EmptyData
>::report(
1549 [&](const BlockDataT
<EmptyData
> *B
, const BlockDataT
<EmptyData
> *A
) {
1550 StringRef BStr
= B
? B
->getBody() : "\n";
1551 StringRef AStr
= A
? A
->getBody() : "\n";
1552 const std::string Removed
=
1553 UseColour
? "\033[31m-%l\033[0m\n" : "-%l\n";
1554 const std::string Added
= UseColour
? "\033[32m+%l\033[0m\n" : "+%l\n";
1555 const std::string NoChange
= " %l\n";
1556 Out
<< doSystemDiff(BStr
, AStr
, Removed
, Added
, NoChange
);
1560 void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
1561 if (PrintChanged
== ChangePrinter::DiffVerbose
||
1562 PrintChanged
== ChangePrinter::DiffQuiet
||
1563 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
1564 PrintChanged
== ChangePrinter::ColourDiffQuiet
)
1565 TextChangeReporter
<IRDataT
<EmptyData
>>::registerRequiredCallbacks(PIC
);
1568 TimeProfilingPassesHandler::TimeProfilingPassesHandler() {}
1570 void TimeProfilingPassesHandler::registerCallbacks(
1571 PassInstrumentationCallbacks
&PIC
) {
1572 if (!getTimeTraceProfilerInstance())
1574 PIC
.registerBeforeNonSkippedPassCallback(
1575 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1576 PIC
.registerAfterPassCallback(
1577 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
1578 this->runAfterPass();
1581 PIC
.registerAfterPassInvalidatedCallback(
1582 [this](StringRef P
, const PreservedAnalyses
&) { this->runAfterPass(); },
1584 PIC
.registerBeforeAnalysisCallback(
1585 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1586 PIC
.registerAfterAnalysisCallback(
1587 [this](StringRef P
, Any IR
) { this->runAfterPass(); }, true);
1590 void TimeProfilingPassesHandler::runBeforePass(StringRef PassID
, Any IR
) {
1591 timeTraceProfilerBegin(PassID
, getIRName(IR
));
1594 void TimeProfilingPassesHandler::runAfterPass() { timeTraceProfilerEnd(); }
1599 class DotCfgDiffDisplayGraph
;
1601 // Base class for a node or edge in the dot-cfg-changes graph.
1602 class DisplayElement
{
1604 // Is this in before, after, or both?
1605 StringRef
getColour() const { return Colour
; }
1608 DisplayElement(StringRef Colour
) : Colour(Colour
) {}
1609 const StringRef Colour
;
1612 // An edge representing a transition between basic blocks in the
1613 // dot-cfg-changes graph.
1614 class DisplayEdge
: public DisplayElement
{
1616 DisplayEdge(std::string Value
, DisplayNode
&Node
, StringRef Colour
)
1617 : DisplayElement(Colour
), Value(Value
), Node(Node
) {}
1618 // The value on which the transition is made.
1619 std::string
getValue() const { return Value
; }
1620 // The node (representing a basic block) reached by this transition.
1621 const DisplayNode
&getDestinationNode() const { return Node
; }
1625 const DisplayNode
&Node
;
1628 // A node in the dot-cfg-changes graph which represents a basic block.
1629 class DisplayNode
: public DisplayElement
{
1631 // \p C is the content for the node, \p T indicates the colour for the
1632 // outline of the node
1633 DisplayNode(std::string Content
, StringRef Colour
)
1634 : DisplayElement(Colour
), Content(Content
) {}
1636 // Iterator to the child nodes. Required by GraphWriter.
1637 using ChildIterator
= std::unordered_set
<DisplayNode
*>::const_iterator
;
1638 ChildIterator
children_begin() const { return Children
.cbegin(); }
1639 ChildIterator
children_end() const { return Children
.cend(); }
1641 // Iterator for the edges. Required by GraphWriter.
1642 using EdgeIterator
= std::vector
<DisplayEdge
*>::const_iterator
;
1643 EdgeIterator
edges_begin() const { return EdgePtrs
.cbegin(); }
1644 EdgeIterator
edges_end() const { return EdgePtrs
.cend(); }
1646 // Create an edge to \p Node on value \p Value, with colour \p Colour.
1647 void createEdge(StringRef Value
, DisplayNode
&Node
, StringRef Colour
);
1649 // Return the content of this node.
1650 std::string
getContent() const { return Content
; }
1652 // Return the edge to node \p S.
1653 const DisplayEdge
&getEdge(const DisplayNode
&To
) const {
1654 assert(EdgeMap
.find(&To
) != EdgeMap
.end() && "Expected to find edge.");
1655 return *EdgeMap
.find(&To
)->second
;
1658 // Return the value for the transition to basic block \p S.
1659 // Required by GraphWriter.
1660 std::string
getEdgeSourceLabel(const DisplayNode
&Sink
) const {
1661 return getEdge(Sink
).getValue();
1664 void createEdgeMap();
1667 const std::string Content
;
1669 // Place to collect all of the edges. Once they are all in the vector,
1670 // the vector will not reallocate so then we can use pointers to them,
1671 // which are required by the graph writing routines.
1672 std::vector
<DisplayEdge
> Edges
;
1674 std::vector
<DisplayEdge
*> EdgePtrs
;
1675 std::unordered_set
<DisplayNode
*> Children
;
1676 std::unordered_map
<const DisplayNode
*, const DisplayEdge
*> EdgeMap
;
1678 // Safeguard adding of edges.
1679 bool AllEdgesCreated
= false;
1682 // Class representing a difference display (corresponds to a pdf file).
1683 class DotCfgDiffDisplayGraph
{
1685 DotCfgDiffDisplayGraph(std::string Name
) : GraphName(Name
) {}
1687 // Generate the file into \p DotFile.
1688 void generateDotFile(StringRef DotFile
);
1690 // Iterator to the nodes. Required by GraphWriter.
1691 using NodeIterator
= std::vector
<DisplayNode
*>::const_iterator
;
1692 NodeIterator
nodes_begin() const {
1693 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1694 return NodePtrs
.cbegin();
1696 NodeIterator
nodes_end() const {
1697 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1698 return NodePtrs
.cend();
1701 // Record the index of the entry node. At this point, we can build up
1702 // vectors of pointers that are required by the graph routines.
1703 void setEntryNode(unsigned N
) {
1704 // At this point, there will be no new nodes.
1705 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1706 NodeGenerationComplete
= true;
1707 for (auto &N
: Nodes
)
1708 NodePtrs
.emplace_back(&N
);
1710 EntryNode
= NodePtrs
[N
];
1714 void createNode(std::string C
, StringRef Colour
) {
1715 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1716 Nodes
.emplace_back(C
, Colour
);
1718 // Return the node at index \p N to avoid problems with vectors reallocating.
1719 DisplayNode
&getNode(unsigned N
) {
1720 assert(N
< Nodes
.size() && "Node is out of bounds");
1723 unsigned size() const {
1724 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1725 return Nodes
.size();
1728 // Return the name of the graph. Required by GraphWriter.
1729 std::string
getGraphName() const { return GraphName
; }
1731 // Return the string representing the differences for basic block \p Node.
1732 // Required by GraphWriter.
1733 std::string
getNodeLabel(const DisplayNode
&Node
) const {
1734 return Node
.getContent();
1737 // Return a string with colour information for Dot. Required by GraphWriter.
1738 std::string
getNodeAttributes(const DisplayNode
&Node
) const {
1739 return attribute(Node
.getColour());
1742 // Return a string with colour information for Dot. Required by GraphWriter.
1743 std::string
getEdgeColorAttr(const DisplayNode
&From
,
1744 const DisplayNode
&To
) const {
1745 return attribute(From
.getEdge(To
).getColour());
1748 // Get the starting basic block. Required by GraphWriter.
1749 DisplayNode
*getEntryNode() const {
1750 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1755 // Return the string containing the colour to use as a Dot attribute.
1756 std::string
attribute(StringRef Colour
) const {
1757 return "color=" + Colour
.str();
1760 bool NodeGenerationComplete
= false;
1761 const std::string GraphName
;
1762 std::vector
<DisplayNode
> Nodes
;
1763 std::vector
<DisplayNode
*> NodePtrs
;
1764 DisplayNode
*EntryNode
= nullptr;
1767 void DisplayNode::createEdge(StringRef Value
, DisplayNode
&Node
,
1769 assert(!AllEdgesCreated
&& "Expected to be able to still create edges.");
1770 Edges
.emplace_back(Value
.str(), Node
, Colour
);
1771 Children
.insert(&Node
);
1774 void DisplayNode::createEdgeMap() {
1775 // No more edges will be added so we can now use pointers to the edges
1776 // as the vector will not grow and reallocate.
1777 AllEdgesCreated
= true;
1778 for (auto &E
: Edges
)
1779 EdgeMap
.insert({&E
.getDestinationNode(), &E
});
1782 class DotCfgDiffNode
;
1785 // A class representing a basic block in the Dot difference graph.
1786 class DotCfgDiffNode
{
1788 DotCfgDiffNode() = delete;
1790 // Create a node in Dot difference graph \p G representing the basic block
1791 // represented by \p BD with colour \p Colour (where it exists).
1792 DotCfgDiffNode(DotCfgDiff
&G
, unsigned N
, const BlockDataT
<DCData
> &BD
,
1794 : Graph(G
), N(N
), Data
{&BD
, nullptr}, Colour(Colour
) {}
1795 DotCfgDiffNode(const DotCfgDiffNode
&DN
)
1796 : Graph(DN
.Graph
), N(DN
.N
), Data
{DN
.Data
[0], DN
.Data
[1]},
1797 Colour(DN
.Colour
), EdgesMap(DN
.EdgesMap
), Children(DN
.Children
),
1800 unsigned getIndex() const { return N
; }
1802 // The label of the basic block
1803 StringRef
getLabel() const {
1804 assert(Data
[0] && "Expected Data[0] to be set.");
1805 return Data
[0]->getLabel();
1807 // Return the colour for this block
1808 StringRef
getColour() const { return Colour
; }
1809 // Change this basic block from being only in before to being common.
1810 // Save the pointer to \p Other.
1811 void setCommon(const BlockDataT
<DCData
> &Other
) {
1812 assert(!Data
[1] && "Expected only one block datum");
1814 Colour
= CommonColour
;
1816 // Add an edge to \p E of colour {\p Value, \p Colour}.
1817 void addEdge(unsigned E
, StringRef Value
, StringRef Colour
) {
1818 // This is a new edge or it is an edge being made common.
1819 assert((EdgesMap
.count(E
) == 0 || Colour
== CommonColour
) &&
1820 "Unexpected edge count and color.");
1821 EdgesMap
[E
] = {Value
.str(), Colour
};
1823 // Record the children and create edges.
1824 void finalize(DotCfgDiff
&G
);
1826 // Return the colour of the edge to node \p S.
1827 StringRef
getEdgeColour(const unsigned S
) const {
1828 assert(EdgesMap
.count(S
) == 1 && "Expected to find edge.");
1829 return EdgesMap
.at(S
).second
;
1832 // Return the string representing the basic block.
1833 std::string
getBodyContent() const;
1835 void createDisplayEdges(DotCfgDiffDisplayGraph
&Graph
, unsigned DisplayNode
,
1836 std::map
<const unsigned, unsigned> &NodeMap
) const;
1841 const BlockDataT
<DCData
> *Data
[2];
1843 std::map
<const unsigned, std::pair
<std::string
, StringRef
>> EdgesMap
;
1844 std::vector
<unsigned> Children
;
1845 std::vector
<unsigned> Edges
;
1848 // Class representing the difference graph between two functions.
1851 // \p Title is the title given to the graph. \p EntryNodeName is the
1852 // entry node for the function. \p Before and \p After are the before
1853 // after versions of the function, respectively. \p Dir is the directory
1854 // in which to store the results.
1855 DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1856 const FuncDataT
<DCData
> &After
);
1858 DotCfgDiff(const DotCfgDiff
&) = delete;
1859 DotCfgDiff
&operator=(const DotCfgDiff
&) = delete;
1861 DotCfgDiffDisplayGraph
createDisplayGraph(StringRef Title
,
1862 StringRef EntryNodeName
);
1864 // Return a string consisting of the labels for the \p Source and \p Sink.
1865 // The combination allows distinguishing changing transitions on the
1866 // same value (ie, a transition went to X before and goes to Y after).
1867 // Required by GraphWriter.
1868 StringRef
getEdgeSourceLabel(const unsigned &Source
,
1869 const unsigned &Sink
) const {
1871 getNode(Source
).getLabel().str() + " " + getNode(Sink
).getLabel().str();
1872 assert(EdgeLabels
.count(S
) == 1 && "Expected to find edge label.");
1873 return EdgeLabels
.find(S
)->getValue();
1876 // Return the number of basic blocks (nodes). Required by GraphWriter.
1877 unsigned size() const { return Nodes
.size(); }
1879 const DotCfgDiffNode
&getNode(unsigned N
) const {
1880 assert(N
< Nodes
.size() && "Unexpected index for node reference");
1885 // Return the string surrounded by HTML to make it the appropriate colour.
1886 std::string
colourize(std::string S
, StringRef Colour
) const;
1888 void createNode(StringRef Label
, const BlockDataT
<DCData
> &BD
, StringRef C
) {
1889 unsigned Pos
= Nodes
.size();
1890 Nodes
.emplace_back(*this, Pos
, BD
, C
);
1891 NodePosition
.insert({Label
, Pos
});
1894 // TODO Nodes should probably be a StringMap<DotCfgDiffNode> after the
1895 // display graph is separated out, which would remove the need for
1897 std::vector
<DotCfgDiffNode
> Nodes
;
1898 StringMap
<unsigned> NodePosition
;
1899 const std::string GraphName
;
1901 StringMap
<std::string
> EdgeLabels
;
1904 std::string
DotCfgDiffNode::getBodyContent() const {
1905 if (Colour
== CommonColour
) {
1906 assert(Data
[1] && "Expected Data[1] to be set.");
1909 for (unsigned I
= 0; I
< 2; ++I
) {
1910 SR
[I
] = Data
[I
]->getBody();
1911 // drop initial '\n' if present
1912 SR
[I
].consume_front("\n");
1913 // drop predecessors as they can be big and are redundant
1914 SR
[I
] = SR
[I
].drop_until([](char C
) { return C
== '\n'; }).drop_front();
1917 SmallString
<80> OldLineFormat
= formatv(
1918 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", BeforeColour
);
1919 SmallString
<80> NewLineFormat
= formatv(
1920 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", AfterColour
);
1921 SmallString
<80> UnchangedLineFormat
= formatv(
1922 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", CommonColour
);
1923 std::string Diff
= Data
[0]->getLabel().str();
1924 Diff
+= ":\n<BR align=\"left\"/>" +
1925 doSystemDiff(makeHTMLReady(SR
[0]), makeHTMLReady(SR
[1]),
1926 OldLineFormat
, NewLineFormat
, UnchangedLineFormat
);
1928 // Diff adds in some empty colour changes which are not valid HTML
1929 // so remove them. Colours are all lowercase alpha characters (as
1930 // listed in https://graphviz.org/pdf/dotguide.pdf).
1931 Regex
R("<FONT COLOR=\"\\w+\"></FONT>");
1934 std::string S
= R
.sub("", Diff
, &Error
);
1941 llvm_unreachable("Should not get here");
1944 // Put node out in the appropriate colour.
1945 assert(!Data
[1] && "Data[1] is set unexpectedly.");
1946 std::string Body
= makeHTMLReady(Data
[0]->getBody());
1947 const StringRef BS
= Body
;
1949 // Drop leading newline, if present.
1950 if (BS
.front() == '\n')
1951 BS1
= BS1
.drop_front(1);
1953 StringRef Label
= BS1
.take_until([](char C
) { return C
== ':'; });
1954 // drop predecessors as they can be big and are redundant
1955 BS1
= BS1
.drop_until([](char C
) { return C
== '\n'; }).drop_front();
1957 std::string S
= "<FONT COLOR=\"" + Colour
.str() + "\">" + Label
.str() + ":";
1959 // align each line to the left.
1960 while (BS1
.size()) {
1961 S
.append("<BR align=\"left\"/>");
1962 StringRef Line
= BS1
.take_until([](char C
) { return C
== '\n'; });
1963 S
.append(Line
.str());
1964 BS1
= BS1
.drop_front(Line
.size() + 1);
1966 S
.append("<BR align=\"left\"/></FONT>");
1970 std::string
DotCfgDiff::colourize(std::string S
, StringRef Colour
) const {
1971 if (S
.length() == 0)
1973 return "<FONT COLOR=\"" + Colour
.str() + "\">" + S
+ "</FONT>";
1976 DotCfgDiff::DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1977 const FuncDataT
<DCData
> &After
)
1978 : GraphName(Title
.str()) {
1979 StringMap
<StringRef
> EdgesMap
;
1981 // Handle each basic block in the before IR.
1982 for (auto &B
: Before
.getData()) {
1983 StringRef Label
= B
.getKey();
1984 const BlockDataT
<DCData
> &BD
= B
.getValue();
1985 createNode(Label
, BD
, BeforeColour
);
1987 // Create transitions with names made up of the from block label, the value
1988 // on which the transition is made and the to block label.
1989 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
1990 E
= BD
.getData().end();
1991 Sink
!= E
; ++Sink
) {
1992 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
1993 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
1994 EdgesMap
.insert({Key
, BeforeColour
});
1998 // Handle each basic block in the after IR
1999 for (auto &A
: After
.getData()) {
2000 StringRef Label
= A
.getKey();
2001 const BlockDataT
<DCData
> &BD
= A
.getValue();
2002 unsigned C
= NodePosition
.count(Label
);
2004 // This only exists in the after IR. Create the node.
2005 createNode(Label
, BD
, AfterColour
);
2007 assert(C
== 1 && "Unexpected multiple nodes.");
2008 Nodes
[NodePosition
[Label
]].setCommon(BD
);
2010 // Add in the edges between the nodes (as common or only in after).
2011 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
2012 E
= BD
.getData().end();
2013 Sink
!= E
; ++Sink
) {
2014 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
2015 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
2016 unsigned C
= EdgesMap
.count(Key
);
2018 EdgesMap
.insert({Key
, AfterColour
});
2020 EdgesMap
[Key
] = CommonColour
;
2025 // Now go through the map of edges and add them to the node.
2026 for (auto &E
: EdgesMap
) {
2027 // Extract the source, sink and value from the edge key.
2028 StringRef S
= E
.getKey();
2029 auto SP1
= S
.rsplit(' ');
2030 auto &SourceSink
= SP1
.first
;
2031 auto SP2
= SourceSink
.split(' ');
2032 StringRef Source
= SP2
.first
;
2033 StringRef Sink
= SP2
.second
;
2034 StringRef Value
= SP1
.second
;
2036 assert(NodePosition
.count(Source
) == 1 && "Expected to find node.");
2037 DotCfgDiffNode
&SourceNode
= Nodes
[NodePosition
[Source
]];
2038 assert(NodePosition
.count(Sink
) == 1 && "Expected to find node.");
2039 unsigned SinkNode
= NodePosition
[Sink
];
2040 StringRef Colour
= E
.second
;
2042 // Look for an edge from Source to Sink
2043 if (EdgeLabels
.count(SourceSink
) == 0)
2044 EdgeLabels
.insert({SourceSink
, colourize(Value
.str(), Colour
)});
2046 StringRef V
= EdgeLabels
.find(SourceSink
)->getValue();
2047 std::string NV
= colourize(V
.str() + " " + Value
.str(), Colour
);
2048 Colour
= CommonColour
;
2049 EdgeLabels
[SourceSink
] = NV
;
2051 SourceNode
.addEdge(SinkNode
, Value
, Colour
);
2053 for (auto &I
: Nodes
)
2057 DotCfgDiffDisplayGraph
DotCfgDiff::createDisplayGraph(StringRef Title
,
2058 StringRef EntryNodeName
) {
2059 assert(NodePosition
.count(EntryNodeName
) == 1 &&
2060 "Expected to find entry block in map.");
2061 unsigned Entry
= NodePosition
[EntryNodeName
];
2062 assert(Entry
< Nodes
.size() && "Expected to find entry node");
2063 DotCfgDiffDisplayGraph
G(Title
.str());
2065 std::map
<const unsigned, unsigned> NodeMap
;
2067 int EntryIndex
= -1;
2069 for (auto &I
: Nodes
) {
2070 if (I
.getIndex() == Entry
)
2072 G
.createNode(I
.getBodyContent(), I
.getColour());
2073 NodeMap
.insert({I
.getIndex(), Index
++});
2075 assert(EntryIndex
>= 0 && "Expected entry node index to be set.");
2076 G
.setEntryNode(EntryIndex
);
2078 for (auto &I
: NodeMap
) {
2079 unsigned SourceNode
= I
.first
;
2080 unsigned DisplayNode
= I
.second
;
2081 getNode(SourceNode
).createDisplayEdges(G
, DisplayNode
, NodeMap
);
2086 void DotCfgDiffNode::createDisplayEdges(
2087 DotCfgDiffDisplayGraph
&DisplayGraph
, unsigned DisplayNodeIndex
,
2088 std::map
<const unsigned, unsigned> &NodeMap
) const {
2090 DisplayNode
&SourceDisplayNode
= DisplayGraph
.getNode(DisplayNodeIndex
);
2092 for (auto I
: Edges
) {
2093 unsigned SinkNodeIndex
= I
;
2094 StringRef Colour
= getEdgeColour(SinkNodeIndex
);
2095 const DotCfgDiffNode
*SinkNode
= &Graph
.getNode(SinkNodeIndex
);
2097 StringRef Label
= Graph
.getEdgeSourceLabel(getIndex(), SinkNodeIndex
);
2098 DisplayNode
&SinkDisplayNode
= DisplayGraph
.getNode(SinkNode
->getIndex());
2099 SourceDisplayNode
.createEdge(Label
, SinkDisplayNode
, Colour
);
2101 SourceDisplayNode
.createEdgeMap();
2104 void DotCfgDiffNode::finalize(DotCfgDiff
&G
) {
2105 for (auto E
: EdgesMap
) {
2106 Children
.emplace_back(E
.first
);
2107 Edges
.emplace_back(E
.first
);
2115 template <> struct GraphTraits
<DotCfgDiffDisplayGraph
*> {
2116 using NodeRef
= const DisplayNode
*;
2117 using ChildIteratorType
= DisplayNode::ChildIterator
;
2118 using nodes_iterator
= DotCfgDiffDisplayGraph::NodeIterator
;
2119 using EdgeRef
= const DisplayEdge
*;
2120 using ChildEdgeIterator
= DisplayNode::EdgeIterator
;
2122 static NodeRef
getEntryNode(const DotCfgDiffDisplayGraph
*G
) {
2123 return G
->getEntryNode();
2125 static ChildIteratorType
child_begin(NodeRef N
) {
2126 return N
->children_begin();
2128 static ChildIteratorType
child_end(NodeRef N
) { return N
->children_end(); }
2129 static nodes_iterator
nodes_begin(const DotCfgDiffDisplayGraph
*G
) {
2130 return G
->nodes_begin();
2132 static nodes_iterator
nodes_end(const DotCfgDiffDisplayGraph
*G
) {
2133 return G
->nodes_end();
2135 static ChildEdgeIterator
child_edge_begin(NodeRef N
) {
2136 return N
->edges_begin();
2138 static ChildEdgeIterator
child_edge_end(NodeRef N
) { return N
->edges_end(); }
2139 static NodeRef
edge_dest(EdgeRef E
) { return &E
->getDestinationNode(); }
2140 static unsigned size(const DotCfgDiffDisplayGraph
*G
) { return G
->size(); }
2144 struct DOTGraphTraits
<DotCfgDiffDisplayGraph
*> : public DefaultDOTGraphTraits
{
2145 explicit DOTGraphTraits(bool Simple
= false)
2146 : DefaultDOTGraphTraits(Simple
) {}
2148 static bool renderNodesUsingHTML() { return true; }
2149 static std::string
getGraphName(const DotCfgDiffDisplayGraph
*DiffData
) {
2150 return DiffData
->getGraphName();
2153 getGraphProperties(const DotCfgDiffDisplayGraph
*DiffData
) {
2154 return "\tsize=\"190, 190\";\n";
2156 static std::string
getNodeLabel(const DisplayNode
*Node
,
2157 const DotCfgDiffDisplayGraph
*DiffData
) {
2158 return DiffData
->getNodeLabel(*Node
);
2160 static std::string
getNodeAttributes(const DisplayNode
*Node
,
2161 const DotCfgDiffDisplayGraph
*DiffData
) {
2162 return DiffData
->getNodeAttributes(*Node
);
2164 static std::string
getEdgeSourceLabel(const DisplayNode
*From
,
2165 DisplayNode::ChildIterator
&To
) {
2166 return From
->getEdgeSourceLabel(**To
);
2168 static std::string
getEdgeAttributes(const DisplayNode
*From
,
2169 DisplayNode::ChildIterator
&To
,
2170 const DotCfgDiffDisplayGraph
*DiffData
) {
2171 return DiffData
->getEdgeColorAttr(*From
, **To
);
2179 void DotCfgDiffDisplayGraph::generateDotFile(StringRef DotFile
) {
2181 raw_fd_ostream
OutStream(DotFile
, EC
);
2183 errs() << "Error: " << EC
.message() << "\n";
2186 WriteGraph(OutStream
, this, false);
2195 DCData::DCData(const BasicBlock
&B
) {
2196 // Build up transition labels.
2197 const Instruction
*Term
= B
.getTerminator();
2198 if (const BranchInst
*Br
= dyn_cast
<const BranchInst
>(Term
))
2199 if (Br
->isUnconditional())
2200 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "");
2202 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "true");
2203 addSuccessorLabel(Br
->getSuccessor(1)->getName().str(), "false");
2205 else if (const SwitchInst
*Sw
= dyn_cast
<const SwitchInst
>(Term
)) {
2206 addSuccessorLabel(Sw
->case_default()->getCaseSuccessor()->getName().str(),
2208 for (auto &C
: Sw
->cases()) {
2209 assert(C
.getCaseValue() && "Expected to find case value.");
2210 SmallString
<20> Value
= formatv("{0}", C
.getCaseValue()->getSExtValue());
2211 addSuccessorLabel(C
.getCaseSuccessor()->getName().str(), Value
);
2214 for (const BasicBlock
*Succ
: successors(&B
))
2215 addSuccessorLabel(Succ
->getName().str(), "");
2218 DCData::DCData(const MachineBasicBlock
&B
) {
2219 for (const MachineBasicBlock
*Succ
: successors(&B
))
2220 addSuccessorLabel(Succ
->getName().str(), "");
2223 DotCfgChangeReporter::DotCfgChangeReporter(bool Verbose
)
2224 : ChangeReporter
<IRDataT
<DCData
>>(Verbose
) {}
2226 void DotCfgChangeReporter::handleFunctionCompare(
2227 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
2228 bool InModule
, unsigned Minor
, const FuncDataT
<DCData
> &Before
,
2229 const FuncDataT
<DCData
> &After
) {
2230 assert(HTML
&& "Expected outstream to be set");
2231 SmallString
<8> Extender
;
2232 SmallString
<8> Number
;
2233 // Handle numbering and file names.
2235 Extender
= formatv("{0}_{1}", N
, Minor
);
2236 Number
= formatv("{0}.{1}", N
, Minor
);
2238 Extender
= formatv("{0}", N
);
2239 Number
= formatv("{0}", N
);
2241 // Create a temporary file name for the dot file.
2242 SmallVector
<char, 128> SV
;
2243 sys::fs::createUniquePath("cfgdot-%%%%%%.dot", SV
, true);
2244 std::string DotFile
= Twine(SV
).str();
2246 SmallString
<20> PDFFileName
= formatv("diff_{0}.pdf", Extender
);
2247 SmallString
<200> Text
;
2249 Text
= formatv("{0}.{1}{2}{3}{4}", Number
, Prefix
, makeHTMLReady(PassID
),
2252 DotCfgDiff
Diff(Text
, Before
, After
);
2253 std::string EntryBlockName
= After
.getEntryBlockName();
2254 // Use the before entry block if the after entry block was removed.
2255 if (EntryBlockName
== "")
2256 EntryBlockName
= Before
.getEntryBlockName();
2257 assert(EntryBlockName
!= "" && "Expected to find entry block");
2259 DotCfgDiffDisplayGraph DG
= Diff
.createDisplayGraph(Text
, EntryBlockName
);
2260 DG
.generateDotFile(DotFile
);
2262 *HTML
<< genHTML(Text
, DotFile
, PDFFileName
);
2263 std::error_code EC
= sys::fs::remove(DotFile
);
2265 errs() << "Error: " << EC
.message() << "\n";
2268 std::string
DotCfgChangeReporter::genHTML(StringRef Text
, StringRef DotFile
,
2269 StringRef PDFFileName
) {
2270 SmallString
<20> PDFFile
= formatv("{0}/{1}", DotCfgDir
, PDFFileName
);
2271 // Create the PDF file.
2272 static ErrorOr
<std::string
> DotExe
= sys::findProgramByName(DotBinary
);
2274 return "Unable to find dot executable.";
2276 StringRef Args
[] = {DotBinary
, "-Tpdf", "-o", PDFFile
, DotFile
};
2277 int Result
= sys::ExecuteAndWait(*DotExe
, Args
, std::nullopt
);
2279 return "Error executing system dot.";
2281 // Create the HTML tag refering to the PDF file.
2282 SmallString
<200> S
= formatv(
2283 " <a href=\"{0}\" target=\"_blank\">{1}</a><br/>\n", PDFFileName
, Text
);
2287 void DotCfgChangeReporter::handleInitialIR(Any IR
) {
2288 assert(HTML
&& "Expected outstream to be set");
2289 *HTML
<< "<button type=\"button\" class=\"collapsible\">0. "
2290 << "Initial IR (by function)</button>\n"
2291 << "<div class=\"content\">\n"
2293 // Create representation of IR
2294 IRDataT
<DCData
> Data
;
2295 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2296 // Now compare it against itself, which will have everything the
2297 // same and will generate the files.
2298 IRComparer
<DCData
>(Data
, Data
)
2299 .compare(getModuleForComparison(IR
),
2300 [&](bool InModule
, unsigned Minor
,
2301 const FuncDataT
<DCData
> &Before
,
2302 const FuncDataT
<DCData
> &After
) -> void {
2303 handleFunctionCompare("", " ", "Initial IR", "", InModule
,
2304 Minor
, Before
, After
);
2311 void DotCfgChangeReporter::generateIRRepresentation(Any IR
, StringRef PassID
,
2312 IRDataT
<DCData
> &Data
) {
2313 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2316 void DotCfgChangeReporter::omitAfter(StringRef PassID
, std::string
&Name
) {
2317 assert(HTML
&& "Expected outstream to be set");
2318 SmallString
<20> Banner
=
2319 formatv(" <a>{0}. Pass {1} on {2} omitted because no change</a><br/>\n",
2320 N
, makeHTMLReady(PassID
), Name
);
2325 void DotCfgChangeReporter::handleAfter(StringRef PassID
, std::string
&Name
,
2326 const IRDataT
<DCData
> &Before
,
2327 const IRDataT
<DCData
> &After
, Any IR
) {
2328 assert(HTML
&& "Expected outstream to be set");
2329 IRComparer
<DCData
>(Before
, After
)
2330 .compare(getModuleForComparison(IR
),
2331 [&](bool InModule
, unsigned Minor
,
2332 const FuncDataT
<DCData
> &Before
,
2333 const FuncDataT
<DCData
> &After
) -> void {
2334 handleFunctionCompare(Name
, " Pass ", PassID
, " on ", InModule
,
2335 Minor
, Before
, After
);
2337 *HTML
<< " </p></div>\n";
2341 void DotCfgChangeReporter::handleInvalidated(StringRef PassID
) {
2342 assert(HTML
&& "Expected outstream to be set");
2343 SmallString
<20> Banner
=
2344 formatv(" <a>{0}. {1} invalidated</a><br/>\n", N
, makeHTMLReady(PassID
));
2349 void DotCfgChangeReporter::handleFiltered(StringRef PassID
, std::string
&Name
) {
2350 assert(HTML
&& "Expected outstream to be set");
2351 SmallString
<20> Banner
=
2352 formatv(" <a>{0}. Pass {1} on {2} filtered out</a><br/>\n", N
,
2353 makeHTMLReady(PassID
), Name
);
2358 void DotCfgChangeReporter::handleIgnored(StringRef PassID
, std::string
&Name
) {
2359 assert(HTML
&& "Expected outstream to be set");
2360 SmallString
<20> Banner
= formatv(" <a>{0}. {1} on {2} ignored</a><br/>\n", N
,
2361 makeHTMLReady(PassID
), Name
);
2366 bool DotCfgChangeReporter::initializeHTML() {
2368 HTML
= std::make_unique
<raw_fd_ostream
>(DotCfgDir
+ "/passes.html", EC
);
2374 *HTML
<< "<!doctype html>"
2377 << "<style>.collapsible { "
2378 << "background-color: #777;"
2380 << " cursor: pointer;"
2381 << " padding: 18px;"
2384 << " text-align: left;"
2385 << " outline: none;"
2386 << " font-size: 15px;"
2387 << "} .active, .collapsible:hover {"
2388 << " background-color: #555;"
2390 << " padding: 0 18px;"
2391 << " display: none;"
2392 << " overflow: hidden;"
2393 << " background-color: #f1f1f1;"
2396 << "<title>passes.html</title>"
2402 DotCfgChangeReporter::~DotCfgChangeReporter() {
2406 << "<script>var coll = document.getElementsByClassName(\"collapsible\");"
2408 << "for (i = 0; i < coll.length; i++) {"
2409 << "coll[i].addEventListener(\"click\", function() {"
2410 << " this.classList.toggle(\"active\");"
2411 << " var content = this.nextElementSibling;"
2412 << " if (content.style.display === \"block\"){"
2413 << " content.style.display = \"none\";"
2416 << " content.style.display= \"block\";"
2427 void DotCfgChangeReporter::registerCallbacks(
2428 PassInstrumentationCallbacks
&PIC
) {
2429 if (PrintChanged
== ChangePrinter::DotCfgVerbose
||
2430 PrintChanged
== ChangePrinter::DotCfgQuiet
) {
2431 SmallString
<128> OutputDir
;
2432 sys::fs::expand_tilde(DotCfgDir
, OutputDir
);
2433 sys::fs::make_absolute(OutputDir
);
2434 assert(!OutputDir
.empty() && "expected output dir to be non-empty");
2435 DotCfgDir
= OutputDir
.c_str();
2436 if (initializeHTML()) {
2437 ChangeReporter
<IRDataT
<DCData
>>::registerRequiredCallbacks(PIC
);
2440 dbgs() << "Unable to open output stream for -cfg-dot-changed\n";
2444 StandardInstrumentations::StandardInstrumentations(
2445 LLVMContext
&Context
, bool DebugLogging
, bool VerifyEach
,
2446 PrintPassOptions PrintPassOpts
)
2447 : PrintPass(DebugLogging
, PrintPassOpts
),
2448 OptNone(DebugLogging
),
2449 OptPassGate(Context
),
2450 PrintChangedIR(PrintChanged
== ChangePrinter::Verbose
),
2451 PrintChangedDiff(PrintChanged
== ChangePrinter::DiffVerbose
||
2452 PrintChanged
== ChangePrinter::ColourDiffVerbose
,
2453 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
2454 PrintChanged
== ChangePrinter::ColourDiffQuiet
),
2455 WebsiteChangeReporter(PrintChanged
== ChangePrinter::DotCfgVerbose
),
2456 Verify(DebugLogging
), VerifyEach(VerifyEach
) {}
2458 PrintCrashIRInstrumentation
*PrintCrashIRInstrumentation::CrashReporter
=
2461 void PrintCrashIRInstrumentation::reportCrashIR() {
2462 if (!PrintOnCrashPath
.empty()) {
2464 raw_fd_ostream
Out(PrintOnCrashPath
, EC
);
2466 report_fatal_error(errorCodeToError(EC
));
2473 void PrintCrashIRInstrumentation::SignalHandler(void *) {
2474 // Called by signal handlers so do not lock here
2475 // Is the PrintCrashIRInstrumentation still alive?
2479 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2480 "Did not expect to get here without option set.");
2481 CrashReporter
->reportCrashIR();
2484 PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
2488 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2489 "Did not expect to get here without option set.");
2490 CrashReporter
= nullptr;
2493 void PrintCrashIRInstrumentation::registerCallbacks(
2494 PassInstrumentationCallbacks
&PIC
) {
2495 if ((!PrintOnCrash
&& PrintOnCrashPath
.empty()) || CrashReporter
)
2498 sys::AddSignalHandler(SignalHandler
, nullptr);
2499 CrashReporter
= this;
2501 PIC
.registerBeforeNonSkippedPassCallback(
2502 [&PIC
, this](StringRef PassID
, Any IR
) {
2504 raw_string_ostream
OS(SavedIR
);
2505 OS
<< formatv("*** Dump of {0}IR Before Last Pass {1}",
2506 llvm::forcePrintModuleIR() ? "Module " : "", PassID
);
2507 if (!isInteresting(IR
, PassID
, PIC
.getPassNameForClassName(PassID
))) {
2508 OS
<< " Filtered Out ***\n";
2511 OS
<< " Started ***\n";
2512 unwrapAndPrint(OS
, IR
);
2516 void StandardInstrumentations::registerCallbacks(
2517 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
*MAM
) {
2518 PrintIR
.registerCallbacks(PIC
);
2519 PrintPass
.registerCallbacks(PIC
);
2520 TimePasses
.registerCallbacks(PIC
);
2521 OptNone
.registerCallbacks(PIC
);
2522 OptPassGate
.registerCallbacks(PIC
);
2523 PrintChangedIR
.registerCallbacks(PIC
);
2524 PseudoProbeVerification
.registerCallbacks(PIC
);
2526 Verify
.registerCallbacks(PIC
, MAM
);
2527 PrintChangedDiff
.registerCallbacks(PIC
);
2528 WebsiteChangeReporter
.registerCallbacks(PIC
);
2529 ChangeTester
.registerCallbacks(PIC
);
2530 PrintCrashIR
.registerCallbacks(PIC
);
2532 PreservedCFGChecker
.registerCallbacks(PIC
, *MAM
);
2534 // TimeProfiling records the pass running time cost.
2535 // Its 'BeforePassCallback' can be appended at the tail of all the
2536 // BeforeCallbacks by calling `registerCallbacks` in the end.
2537 // Its 'AfterPassCallback' is put at the front of all the
2538 // AfterCallbacks by its `registerCallbacks`. This is necessary
2539 // to ensure that other callbacks are not included in the timings.
2540 TimeProfilingPasses
.registerCallbacks(PIC
);
2543 template class ChangeReporter
<std::string
>;
2544 template class TextChangeReporter
<std::string
>;
2546 template class BlockDataT
<EmptyData
>;
2547 template class FuncDataT
<EmptyData
>;
2548 template class IRDataT
<EmptyData
>;
2549 template class ChangeReporter
<IRDataT
<EmptyData
>>;
2550 template class TextChangeReporter
<IRDataT
<EmptyData
>>;
2551 template class IRComparer
<EmptyData
>;