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/MachineFunction.h"
23 #include "llvm/IR/Constants.h"
24 #include "llvm/IR/Function.h"
25 #include "llvm/IR/Module.h"
26 #include "llvm/IR/PassInstrumentation.h"
27 #include "llvm/IR/PassManager.h"
28 #include "llvm/IR/PrintPasses.h"
29 #include "llvm/IR/StructuralHash.h"
30 #include "llvm/IR/Verifier.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/CrashRecoveryContext.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/Error.h"
35 #include "llvm/Support/FormatVariadic.h"
36 #include "llvm/Support/GraphWriter.h"
37 #include "llvm/Support/MemoryBuffer.h"
38 #include "llvm/Support/Path.h"
39 #include "llvm/Support/Program.h"
40 #include "llvm/Support/Regex.h"
41 #include "llvm/Support/Signals.h"
42 #include "llvm/Support/raw_ostream.h"
43 #include <unordered_map>
44 #include <unordered_set>
50 static cl::opt
<bool> VerifyAnalysisInvalidation("verify-analysis-invalidation",
52 #ifdef EXPENSIVE_CHECKS
59 // An option that supports the -print-changed option. See
60 // the description for -print-changed for an explanation of the use
61 // of this option. Note that this option has no effect without -print-changed.
63 PrintChangedBefore("print-before-changed",
64 cl::desc("Print before passes that change them"),
65 cl::init(false), cl::Hidden
);
67 // An option for specifying the dot used by
68 // print-changed=[dot-cfg | dot-cfg-quiet]
69 static cl::opt
<std::string
>
70 DotBinary("print-changed-dot-path", cl::Hidden
, cl::init("dot"),
71 cl::desc("system dot used by change reporters"));
73 // An option that determines the colour used for elements that are only
74 // in the before part. Must be a colour named in appendix J of
75 // https://graphviz.org/pdf/dotguide.pdf
76 static cl::opt
<std::string
>
77 BeforeColour("dot-cfg-before-color",
78 cl::desc("Color for dot-cfg before elements"), cl::Hidden
,
80 // An option that determines the colour used for elements that are only
81 // in the after part. Must be a colour named in appendix J of
82 // https://graphviz.org/pdf/dotguide.pdf
83 static cl::opt
<std::string
>
84 AfterColour("dot-cfg-after-color",
85 cl::desc("Color for dot-cfg after elements"), cl::Hidden
,
86 cl::init("forestgreen"));
87 // An option that determines the colour used for elements that are in both
88 // the before and after parts. Must be a colour named in appendix J of
89 // https://graphviz.org/pdf/dotguide.pdf
90 static cl::opt
<std::string
>
91 CommonColour("dot-cfg-common-color",
92 cl::desc("Color for dot-cfg common elements"), cl::Hidden
,
95 // An option that determines where the generated website file (named
96 // passes.html) and the associated pdf files (named diff_*.pdf) are saved.
97 static cl::opt
<std::string
> DotCfgDir(
99 cl::desc("Generate dot files into specified directory for changed IRs"),
100 cl::Hidden
, cl::init("./"));
102 // Options to print the IR that was being processed when a pass crashes.
103 static cl::opt
<std::string
> PrintOnCrashPath(
104 "print-on-crash-path",
105 cl::desc("Print the last form of the IR before crash to a file"),
108 static cl::opt
<bool> PrintOnCrash(
110 cl::desc("Print the last form of the IR before crash (use -print-on-crash-path to dump to a file)"),
113 static cl::opt
<std::string
> OptBisectPrintIRPath(
114 "opt-bisect-print-ir-path",
115 cl::desc("Print IR to path when opt-bisect-limit is reached"), cl::Hidden
);
117 static cl::opt
<bool> PrintPassNumbers(
118 "print-pass-numbers", cl::init(false), cl::Hidden
,
119 cl::desc("Print pass names and their ordinals"));
121 static cl::opt
<unsigned> PrintBeforePassNumber(
122 "print-before-pass-number", cl::init(0), cl::Hidden
,
123 cl::desc("Print IR before the pass with this number as "
124 "reported by print-pass-numbers"));
126 static cl::opt
<std::string
> IRDumpDirectory(
128 cl::desc("If specified, IR printed using the "
129 "-print-[before|after]{-all} options will be dumped into "
130 "files in this directory rather than written to stderr"),
131 cl::Hidden
, cl::value_desc("filename"));
133 template <typename IRUnitT
> static const IRUnitT
*unwrapIR(Any IR
) {
134 const IRUnitT
**IRPtr
= llvm::any_cast
<const IRUnitT
*>(&IR
);
135 return IRPtr
? *IRPtr
: nullptr;
140 // An option for specifying an executable that will be called with the IR
141 // everytime it changes in the opt pipeline. It will also be called on
142 // the initial IR as it enters the pipeline. The executable will be passed
143 // the name of a temporary file containing the IR and the PassID. This may
144 // be used, for example, to call llc on the IR and run a test to determine
145 // which pass makes a change that changes the functioning of the IR.
146 // The usual modifier options work as expected.
147 static cl::opt
<std::string
>
148 TestChanged("exec-on-ir-change", cl::Hidden
, cl::init(""),
149 cl::desc("exe called with module IR after each pass that "
152 /// Extract Module out of \p IR unit. May return nullptr if \p IR does not match
153 /// certain global filters. Will never return nullptr if \p Force is true.
154 const Module
*unwrapModule(Any IR
, bool Force
= false) {
155 if (const auto *M
= unwrapIR
<Module
>(IR
))
158 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
159 if (!Force
&& !isFunctionInPrintList(F
->getName()))
162 return F
->getParent();
165 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
166 for (const LazyCallGraph::Node
&N
: *C
) {
167 const Function
&F
= N
.getFunction();
168 if (Force
|| (!F
.isDeclaration() && isFunctionInPrintList(F
.getName()))) {
169 return F
.getParent();
172 assert(!Force
&& "Expected a module");
176 if (const auto *L
= unwrapIR
<Loop
>(IR
)) {
177 const Function
*F
= L
->getHeader()->getParent();
178 if (!Force
&& !isFunctionInPrintList(F
->getName()))
180 return F
->getParent();
183 llvm_unreachable("Unknown IR unit");
186 void printIR(raw_ostream
&OS
, const Function
*F
) {
187 if (!isFunctionInPrintList(F
->getName()))
192 void printIR(raw_ostream
&OS
, const Module
*M
) {
193 if (isFunctionInPrintList("*") || forcePrintModuleIR()) {
194 M
->print(OS
, nullptr);
196 for (const auto &F
: M
->functions()) {
202 void printIR(raw_ostream
&OS
, const LazyCallGraph::SCC
*C
) {
203 for (const LazyCallGraph::Node
&N
: *C
) {
204 const Function
&F
= N
.getFunction();
205 if (!F
.isDeclaration() && isFunctionInPrintList(F
.getName())) {
211 void printIR(raw_ostream
&OS
, const Loop
*L
) {
212 const Function
*F
= L
->getHeader()->getParent();
213 if (!isFunctionInPrintList(F
->getName()))
215 printLoop(const_cast<Loop
&>(*L
), OS
);
218 std::string
getIRName(Any IR
) {
219 if (unwrapIR
<Module
>(IR
))
222 if (const auto *F
= unwrapIR
<Function
>(IR
))
223 return F
->getName().str();
225 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
228 if (const auto *L
= unwrapIR
<Loop
>(IR
))
229 return L
->getName().str();
231 if (const auto *MF
= unwrapIR
<MachineFunction
>(IR
))
232 return MF
->getName().str();
234 llvm_unreachable("Unknown wrapped IR type");
237 bool moduleContainsFilterPrintFunc(const Module
&M
) {
238 return any_of(M
.functions(),
239 [](const Function
&F
) {
240 return isFunctionInPrintList(F
.getName());
242 isFunctionInPrintList("*");
245 bool sccContainsFilterPrintFunc(const LazyCallGraph::SCC
&C
) {
247 [](const LazyCallGraph::Node
&N
) {
248 return isFunctionInPrintList(N
.getName());
250 isFunctionInPrintList("*");
253 bool shouldPrintIR(Any IR
) {
254 if (const auto *M
= unwrapIR
<Module
>(IR
))
255 return moduleContainsFilterPrintFunc(*M
);
257 if (const auto *F
= unwrapIR
<Function
>(IR
))
258 return isFunctionInPrintList(F
->getName());
260 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
261 return sccContainsFilterPrintFunc(*C
);
263 if (const auto *L
= unwrapIR
<Loop
>(IR
))
264 return isFunctionInPrintList(L
->getHeader()->getParent()->getName());
265 llvm_unreachable("Unknown wrapped IR type");
268 /// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into
269 /// Any and does actual print job.
270 void unwrapAndPrint(raw_ostream
&OS
, Any IR
) {
271 if (!shouldPrintIR(IR
))
274 if (forcePrintModuleIR()) {
275 auto *M
= unwrapModule(IR
);
276 assert(M
&& "should have unwrapped module");
281 if (const auto *M
= unwrapIR
<Module
>(IR
)) {
286 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
291 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
296 if (const auto *L
= unwrapIR
<Loop
>(IR
)) {
300 llvm_unreachable("Unknown wrapped IR type");
303 // Return true when this is a pass for which changes should be ignored
304 bool isIgnored(StringRef PassID
) {
305 return isSpecialPass(PassID
,
306 {"PassManager", "PassAdaptor", "AnalysisManagerProxy",
307 "DevirtSCCRepeatedPass", "ModuleInlinerWrapperPass",
308 "VerifierPass", "PrintModulePass"});
311 std::string
makeHTMLReady(StringRef SR
) {
315 SR
.take_until([](char C
) { return C
== '<' || C
== '>'; });
316 S
.append(Clean
.str());
317 SR
= SR
.drop_front(Clean
.size());
320 S
.append(SR
[0] == '<' ? "<" : ">");
321 SR
= SR
.drop_front();
323 llvm_unreachable("problems converting string to HTML");
326 // Return the module when that is the appropriate level of comparison for \p IR.
327 const Module
*getModuleForComparison(Any IR
) {
328 if (const auto *M
= unwrapIR
<Module
>(IR
))
330 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
331 return C
->begin()->getFunction().getParent();
335 bool isInterestingFunction(const Function
&F
) {
336 return isFunctionInPrintList(F
.getName());
339 // Return true when this is a pass on IR for which printing
340 // of changes is desired.
341 bool isInteresting(Any IR
, StringRef PassID
, StringRef PassName
) {
342 if (isIgnored(PassID
) || !isPassInPrintList(PassName
))
344 if (const auto *F
= unwrapIR
<Function
>(IR
))
345 return isInterestingFunction(*F
);
351 template <typename T
> ChangeReporter
<T
>::~ChangeReporter() {
352 assert(BeforeStack
.empty() && "Problem with Change Printer stack.");
355 template <typename T
>
356 void ChangeReporter
<T
>::saveIRBeforePass(Any IR
, StringRef PassID
,
357 StringRef PassName
) {
358 // Is this the initial IR?
365 // Always need to place something on the stack because invalidated passes
366 // are not given the IR so it cannot be determined whether the pass was for
367 // something that was filtered out.
368 BeforeStack
.emplace_back();
370 if (!isInteresting(IR
, PassID
, PassName
))
373 // Save the IR representation on the stack.
374 T
&Data
= BeforeStack
.back();
375 generateIRRepresentation(IR
, PassID
, Data
);
378 template <typename T
>
379 void ChangeReporter
<T
>::handleIRAfterPass(Any IR
, StringRef PassID
,
380 StringRef PassName
) {
381 assert(!BeforeStack
.empty() && "Unexpected empty stack encountered.");
383 std::string Name
= getIRName(IR
);
385 if (isIgnored(PassID
)) {
387 handleIgnored(PassID
, Name
);
388 } else if (!isInteresting(IR
, PassID
, PassName
)) {
390 handleFiltered(PassID
, Name
);
392 // Get the before rep from the stack
393 T
&Before
= BeforeStack
.back();
394 // Create the after rep
396 generateIRRepresentation(IR
, PassID
, After
);
398 // Was there a change in IR?
399 if (Before
== After
) {
401 omitAfter(PassID
, Name
);
403 handleAfter(PassID
, Name
, Before
, After
, IR
);
405 BeforeStack
.pop_back();
408 template <typename T
>
409 void ChangeReporter
<T
>::handleInvalidatedPass(StringRef PassID
) {
410 assert(!BeforeStack
.empty() && "Unexpected empty stack encountered.");
412 // Always flag it as invalidated as we cannot determine when
413 // a pass for a filtered function is invalidated since we do not
414 // get the IR in the call. Also, the output is just alternate
415 // forms of the banner anyway.
417 handleInvalidated(PassID
);
418 BeforeStack
.pop_back();
421 template <typename T
>
422 void ChangeReporter
<T
>::registerRequiredCallbacks(
423 PassInstrumentationCallbacks
&PIC
) {
424 PIC
.registerBeforeNonSkippedPassCallback([&PIC
, this](StringRef P
, Any IR
) {
425 saveIRBeforePass(IR
, P
, PIC
.getPassNameForClassName(P
));
428 PIC
.registerAfterPassCallback(
429 [&PIC
, this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
430 handleIRAfterPass(IR
, P
, PIC
.getPassNameForClassName(P
));
432 PIC
.registerAfterPassInvalidatedCallback(
433 [this](StringRef P
, const PreservedAnalyses
&) {
434 handleInvalidatedPass(P
);
438 template <typename T
>
439 TextChangeReporter
<T
>::TextChangeReporter(bool Verbose
)
440 : ChangeReporter
<T
>(Verbose
), Out(dbgs()) {}
442 template <typename T
> void TextChangeReporter
<T
>::handleInitialIR(Any IR
) {
443 // Always print the module.
444 // Unwrap and print directly to avoid filtering problems in general routines.
445 auto *M
= unwrapModule(IR
, /*Force=*/true);
446 assert(M
&& "Expected module to be unwrapped when forced.");
447 Out
<< "*** IR Dump At Start ***\n";
448 M
->print(Out
, nullptr);
451 template <typename T
>
452 void TextChangeReporter
<T
>::omitAfter(StringRef PassID
, std::string
&Name
) {
453 Out
<< formatv("*** IR Dump After {0} on {1} omitted because no change ***\n",
457 template <typename T
>
458 void TextChangeReporter
<T
>::handleInvalidated(StringRef PassID
) {
459 Out
<< formatv("*** IR Pass {0} invalidated ***\n", PassID
);
462 template <typename T
>
463 void TextChangeReporter
<T
>::handleFiltered(StringRef PassID
,
465 SmallString
<20> Banner
=
466 formatv("*** IR Dump After {0} on {1} filtered out ***\n", PassID
, Name
);
470 template <typename T
>
471 void TextChangeReporter
<T
>::handleIgnored(StringRef PassID
, std::string
&Name
) {
472 Out
<< formatv("*** IR Pass {0} on {1} ignored ***\n", PassID
, Name
);
475 IRChangedPrinter::~IRChangedPrinter() = default;
477 void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
478 if (PrintChanged
== ChangePrinter::Verbose
||
479 PrintChanged
== ChangePrinter::Quiet
)
480 TextChangeReporter
<std::string
>::registerRequiredCallbacks(PIC
);
483 void IRChangedPrinter::generateIRRepresentation(Any IR
, StringRef PassID
,
484 std::string
&Output
) {
485 raw_string_ostream
OS(Output
);
486 unwrapAndPrint(OS
, IR
);
490 void IRChangedPrinter::handleAfter(StringRef PassID
, std::string
&Name
,
491 const std::string
&Before
,
492 const std::string
&After
, Any
) {
493 // Report the IR before the changes when requested.
494 if (PrintChangedBefore
)
495 Out
<< "*** IR Dump Before " << PassID
<< " on " << Name
<< " ***\n"
498 // We might not get anything to print if we only want to print a specific
499 // function but it gets deleted.
501 Out
<< "*** IR Deleted After " << PassID
<< " on " << Name
<< " ***\n";
505 Out
<< "*** IR Dump After " << PassID
<< " on " << Name
<< " ***\n" << After
;
508 IRChangedTester::~IRChangedTester() {}
510 void IRChangedTester::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
511 if (TestChanged
!= "")
512 TextChangeReporter
<std::string
>::registerRequiredCallbacks(PIC
);
515 void IRChangedTester::handleIR(const std::string
&S
, StringRef PassID
) {
516 // Store the body into a temporary file
517 static SmallVector
<int> FD
{-1};
518 SmallVector
<StringRef
> SR
{S
};
519 static SmallVector
<std::string
> FileName
{""};
520 if (prepareTempFiles(FD
, SR
, FileName
)) {
521 dbgs() << "Unable to create temporary file.";
524 static ErrorOr
<std::string
> Exe
= sys::findProgramByName(TestChanged
);
526 dbgs() << "Unable to find test-changed executable.";
530 StringRef Args
[] = {TestChanged
, FileName
[0], PassID
};
531 int Result
= sys::ExecuteAndWait(*Exe
, Args
);
533 dbgs() << "Error executing test-changed executable.";
537 if (cleanUpTempFiles(FileName
))
538 dbgs() << "Unable to remove temporary file.";
541 void IRChangedTester::handleInitialIR(Any IR
) {
542 // Always test the initial module.
543 // Unwrap and print directly to avoid filtering problems in general routines.
545 generateIRRepresentation(IR
, "Initial IR", S
);
546 handleIR(S
, "Initial IR");
549 void IRChangedTester::omitAfter(StringRef PassID
, std::string
&Name
) {}
550 void IRChangedTester::handleInvalidated(StringRef PassID
) {}
551 void IRChangedTester::handleFiltered(StringRef PassID
, std::string
&Name
) {}
552 void IRChangedTester::handleIgnored(StringRef PassID
, std::string
&Name
) {}
553 void IRChangedTester::handleAfter(StringRef PassID
, std::string
&Name
,
554 const std::string
&Before
,
555 const std::string
&After
, Any
) {
556 handleIR(After
, PassID
);
559 template <typename T
>
560 void OrderedChangedData
<T
>::report(
561 const OrderedChangedData
&Before
, const OrderedChangedData
&After
,
562 function_ref
<void(const T
*, const T
*)> HandlePair
) {
563 const auto &BFD
= Before
.getData();
564 const auto &AFD
= After
.getData();
565 std::vector
<std::string
>::const_iterator BI
= Before
.getOrder().begin();
566 std::vector
<std::string
>::const_iterator BE
= Before
.getOrder().end();
567 std::vector
<std::string
>::const_iterator AI
= After
.getOrder().begin();
568 std::vector
<std::string
>::const_iterator AE
= After
.getOrder().end();
570 auto HandlePotentiallyRemovedData
= [&](std::string S
) {
571 // The order in LLVM may have changed so check if still exists.
573 // This has been removed.
574 HandlePair(&BFD
.find(*BI
)->getValue(), nullptr);
577 auto HandleNewData
= [&](std::vector
<const T
*> &Q
) {
578 // Print out any queued up new sections
579 for (const T
*NBI
: Q
)
580 HandlePair(nullptr, NBI
);
584 // Print out the data in the after order, with before ones interspersed
585 // appropriately (ie, somewhere near where they were in the before list).
586 // Start at the beginning of both lists. Loop through the
587 // after list. If an element is common, then advance in the before list
588 // reporting the removed ones until the common one is reached. Report any
589 // queued up new ones and then report the common one. If an element is not
590 // common, then enqueue it for reporting. When the after list is exhausted,
591 // loop through the before list, reporting any removed ones. Finally,
592 // report the rest of the enqueued new ones.
593 std::vector
<const T
*> NewDataQueue
;
595 if (!BFD
.count(*AI
)) {
596 // This section is new so place it in the queue. This will cause it
597 // to be reported after deleted sections.
598 NewDataQueue
.emplace_back(&AFD
.find(*AI
)->getValue());
602 // This section is in both; advance and print out any before-only
603 // until we get to it.
604 // It's possible that this section has moved to be later than before. This
605 // will mess up printing most blocks side by side, but it's a rare case and
606 // it's better than crashing.
607 while (BI
!= BE
&& *BI
!= *AI
) {
608 HandlePotentiallyRemovedData(*BI
);
611 // Report any new sections that were queued up and waiting.
612 HandleNewData(NewDataQueue
);
614 const T
&AData
= AFD
.find(*AI
)->getValue();
615 const T
&BData
= BFD
.find(*AI
)->getValue();
616 HandlePair(&BData
, &AData
);
622 // Check any remaining before sections to see if they have been removed
624 HandlePotentiallyRemovedData(*BI
);
628 HandleNewData(NewDataQueue
);
631 template <typename T
>
632 void IRComparer
<T
>::compare(
634 std::function
<void(bool InModule
, unsigned Minor
,
635 const FuncDataT
<T
> &Before
, const FuncDataT
<T
> &After
)>
637 if (!CompareModule
) {
638 // Just handle the single function.
639 assert(Before
.getData().size() == 1 && After
.getData().size() == 1 &&
640 "Expected only one function.");
641 CompareFunc(false, 0, Before
.getData().begin()->getValue(),
642 After
.getData().begin()->getValue());
647 FuncDataT
<T
> Missing("");
648 IRDataT
<T
>::report(Before
, After
,
649 [&](const FuncDataT
<T
> *B
, const FuncDataT
<T
> *A
) {
650 assert((B
|| A
) && "Both functions cannot be missing.");
655 CompareFunc(true, Minor
++, *B
, *A
);
659 template <typename T
> void IRComparer
<T
>::analyzeIR(Any IR
, IRDataT
<T
> &Data
) {
660 if (const Module
*M
= getModuleForComparison(IR
)) {
661 // Create data for each existing/interesting function in the module.
662 for (const Function
&F
: *M
)
663 generateFunctionData(Data
, F
);
667 const auto *F
= unwrapIR
<Function
>(IR
);
669 const auto *L
= unwrapIR
<Loop
>(IR
);
670 assert(L
&& "Unknown IR unit.");
671 F
= L
->getHeader()->getParent();
673 assert(F
&& "Unknown IR unit.");
674 generateFunctionData(Data
, *F
);
677 template <typename T
>
678 bool IRComparer
<T
>::generateFunctionData(IRDataT
<T
> &Data
, const Function
&F
) {
679 if (!F
.isDeclaration() && isFunctionInPrintList(F
.getName())) {
680 FuncDataT
<T
> FD(F
.getEntryBlock().getName().str());
682 for (const auto &B
: F
) {
683 std::string BBName
= B
.getName().str();
684 if (BBName
.empty()) {
685 BBName
= formatv("{0}", I
);
688 FD
.getOrder().emplace_back(BBName
);
689 FD
.getData().insert({BBName
, B
});
691 Data
.getOrder().emplace_back(F
.getName());
692 Data
.getData().insert({F
.getName(), FD
});
698 PrintIRInstrumentation::~PrintIRInstrumentation() {
699 assert(PassRunDescriptorStack
.empty() &&
700 "PassRunDescriptorStack is not empty at exit");
703 static SmallString
<32> getIRFileDisplayName(Any IR
) {
704 SmallString
<32> Result
;
705 raw_svector_ostream
ResultStream(Result
);
706 const Module
*M
= unwrapModule(IR
);
707 stable_hash NameHash
= stable_hash_combine_string(M
->getName());
708 unsigned int MaxHashWidth
= sizeof(stable_hash
) * 8 / 4;
709 write_hex(ResultStream
, NameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
710 if (unwrapIR
<Module
>(IR
)) {
711 ResultStream
<< "-module";
712 } else if (const auto *F
= unwrapIR
<Function
>(IR
)) {
713 ResultStream
<< "-function-";
714 stable_hash FunctionNameHash
= stable_hash_combine_string(F
->getName());
715 write_hex(ResultStream
, FunctionNameHash
, HexPrintStyle::Lower
,
717 } else if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
718 ResultStream
<< "-scc-";
719 stable_hash SCCNameHash
= stable_hash_combine_string(C
->getName());
720 write_hex(ResultStream
, SCCNameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
721 } else if (const auto *L
= unwrapIR
<Loop
>(IR
)) {
722 ResultStream
<< "-loop-";
723 stable_hash LoopNameHash
= stable_hash_combine_string(L
->getName());
724 write_hex(ResultStream
, LoopNameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
726 llvm_unreachable("Unknown wrapped IR type");
731 std::string
PrintIRInstrumentation::fetchDumpFilename(StringRef PassName
,
733 const StringRef RootDirectory
= IRDumpDirectory
;
734 assert(!RootDirectory
.empty() &&
735 "The flag -ir-dump-directory must be passed to dump IR to files");
736 SmallString
<128> ResultPath
;
737 ResultPath
+= RootDirectory
;
738 SmallString
<64> Filename
;
739 raw_svector_ostream
FilenameStream(Filename
);
740 FilenameStream
<< CurrentPassNumber
;
741 FilenameStream
<< "-";
742 FilenameStream
<< getIRFileDisplayName(IR
);
743 FilenameStream
<< "-";
744 FilenameStream
<< PassName
;
745 sys::path::append(ResultPath
, Filename
);
746 return std::string(ResultPath
);
749 enum class IRDumpFileSuffixType
{
755 static StringRef
getFileSuffix(IRDumpFileSuffixType Type
) {
756 static constexpr std::array FileSuffixes
= {"-before.ll", "-after.ll",
758 return FileSuffixes
[static_cast<size_t>(Type
)];
761 void PrintIRInstrumentation::pushPassRunDescriptor(
762 StringRef PassID
, Any IR
, std::string
&DumpIRFilename
) {
763 const Module
*M
= unwrapModule(IR
);
764 PassRunDescriptorStack
.emplace_back(
765 PassRunDescriptor(M
, DumpIRFilename
, getIRName(IR
), PassID
));
768 PrintIRInstrumentation::PassRunDescriptor
769 PrintIRInstrumentation::popPassRunDescriptor(StringRef PassID
) {
770 assert(!PassRunDescriptorStack
.empty() && "empty PassRunDescriptorStack");
771 PassRunDescriptor Descriptor
= PassRunDescriptorStack
.pop_back_val();
772 assert(Descriptor
.PassID
.equals(PassID
) &&
773 "malformed PassRunDescriptorStack");
777 // Callers are responsible for closing the returned file descriptor
778 static int prepareDumpIRFileDescriptor(const StringRef DumpIRFilename
) {
780 auto ParentPath
= llvm::sys::path::parent_path(DumpIRFilename
);
781 if (!ParentPath
.empty()) {
782 std::error_code EC
= llvm::sys::fs::create_directories(ParentPath
);
784 report_fatal_error(Twine("Failed to create directory ") + ParentPath
+
785 " to support -ir-dump-directory: " + EC
.message());
788 EC
= sys::fs::openFile(DumpIRFilename
, Result
, sys::fs::CD_OpenAlways
,
789 sys::fs::FA_Write
, sys::fs::OF_None
);
791 report_fatal_error(Twine("Failed to open ") + DumpIRFilename
+
792 " to support -ir-dump-directory: " + EC
.message());
796 void PrintIRInstrumentation::printBeforePass(StringRef PassID
, Any IR
) {
797 if (isIgnored(PassID
))
800 std::string DumpIRFilename
;
801 if (!IRDumpDirectory
.empty() &&
802 (shouldPrintBeforePass(PassID
) || shouldPrintAfterPass(PassID
)))
803 DumpIRFilename
= fetchDumpFilename(PassID
, IR
);
805 // Saving Module for AfterPassInvalidated operations.
806 // Note: here we rely on a fact that we do not change modules while
807 // traversing the pipeline, so the latest captured module is good
808 // for all print operations that has not happen yet.
809 if (shouldPrintAfterPass(PassID
))
810 pushPassRunDescriptor(PassID
, IR
, DumpIRFilename
);
812 if (!shouldPrintIR(IR
))
817 if (shouldPrintPassNumbers())
818 dbgs() << " Running pass " << CurrentPassNumber
<< " " << PassID
819 << " on " << getIRName(IR
) << "\n";
821 if (!shouldPrintBeforePass(PassID
))
824 auto WriteIRToStream
= [&](raw_ostream
&Stream
) {
825 Stream
<< "; *** IR Dump Before ";
826 if (shouldPrintBeforePassNumber())
827 Stream
<< CurrentPassNumber
<< "-";
828 Stream
<< PassID
<< " on " << getIRName(IR
) << " ***\n";
829 unwrapAndPrint(Stream
, IR
);
832 if (!DumpIRFilename
.empty()) {
833 DumpIRFilename
+= getFileSuffix(IRDumpFileSuffixType::Before
);
834 llvm::raw_fd_ostream DumpIRFileStream
{
835 prepareDumpIRFileDescriptor(DumpIRFilename
), /* shouldClose */ true};
836 WriteIRToStream(DumpIRFileStream
);
838 WriteIRToStream(dbgs());
842 void PrintIRInstrumentation::printAfterPass(StringRef PassID
, Any IR
) {
843 if (isIgnored(PassID
))
846 if (!shouldPrintAfterPass(PassID
))
849 auto [M
, DumpIRFilename
, IRName
, StoredPassID
] = popPassRunDescriptor(PassID
);
850 assert(StoredPassID
== PassID
&& "mismatched PassID");
852 if (!shouldPrintIR(IR
) || !shouldPrintAfterPass(PassID
))
855 auto WriteIRToStream
= [&](raw_ostream
&Stream
, const StringRef IRName
) {
856 Stream
<< "; *** IR Dump " << StringRef(formatv("After {0}", PassID
))
857 << " on " << IRName
<< " ***\n";
858 unwrapAndPrint(Stream
, IR
);
861 if (!IRDumpDirectory
.empty()) {
862 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
863 "should be set in printBeforePass");
864 const std::string DumpIRFilenameWithSuffix
=
865 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::After
).str();
866 llvm::raw_fd_ostream DumpIRFileStream
{
867 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
868 /* shouldClose */ true};
869 WriteIRToStream(DumpIRFileStream
, IRName
);
871 WriteIRToStream(dbgs(), IRName
);
875 void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID
) {
876 if (isIgnored(PassID
))
879 if (!shouldPrintAfterPass(PassID
))
882 auto [M
, DumpIRFilename
, IRName
, StoredPassID
] = popPassRunDescriptor(PassID
);
883 assert(StoredPassID
== PassID
&& "mismatched PassID");
884 // Additional filtering (e.g. -filter-print-func) can lead to module
885 // printing being skipped.
886 if (!M
|| !shouldPrintAfterPass(PassID
))
889 auto WriteIRToStream
= [&](raw_ostream
&Stream
, const Module
*M
,
890 const StringRef IRName
) {
891 SmallString
<20> Banner
;
892 Banner
= formatv("; *** IR Dump After {0} on {1} (invalidated) ***", PassID
,
894 Stream
<< Banner
<< "\n";
898 if (!IRDumpDirectory
.empty()) {
899 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
900 "should be set in printBeforePass");
901 const std::string DumpIRFilenameWithSuffix
=
902 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::Invalidated
).str();
903 llvm::raw_fd_ostream DumpIRFileStream
{
904 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
905 /* shouldClose */ true};
906 WriteIRToStream(DumpIRFileStream
, M
, IRName
);
908 WriteIRToStream(dbgs(), M
, IRName
);
912 bool PrintIRInstrumentation::shouldPrintBeforePass(StringRef PassID
) {
913 if (shouldPrintBeforeAll())
916 if (shouldPrintBeforePassNumber() &&
917 CurrentPassNumber
== PrintBeforePassNumber
)
920 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
921 return is_contained(printBeforePasses(), PassName
);
924 bool PrintIRInstrumentation::shouldPrintAfterPass(StringRef PassID
) {
925 if (shouldPrintAfterAll())
928 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
929 return is_contained(printAfterPasses(), PassName
);
932 bool PrintIRInstrumentation::shouldPrintPassNumbers() {
933 return PrintPassNumbers
;
936 bool PrintIRInstrumentation::shouldPrintBeforePassNumber() {
937 return PrintBeforePassNumber
> 0;
940 void PrintIRInstrumentation::registerCallbacks(
941 PassInstrumentationCallbacks
&PIC
) {
944 // BeforePass callback is not just for printing, it also saves a Module
945 // for later use in AfterPassInvalidated.
946 if (shouldPrintPassNumbers() || shouldPrintBeforePassNumber() ||
947 shouldPrintBeforeSomePass() || shouldPrintAfterSomePass())
948 PIC
.registerBeforeNonSkippedPassCallback(
949 [this](StringRef P
, Any IR
) { this->printBeforePass(P
, IR
); });
951 if (shouldPrintAfterSomePass()) {
952 PIC
.registerAfterPassCallback(
953 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
954 this->printAfterPass(P
, IR
);
956 PIC
.registerAfterPassInvalidatedCallback(
957 [this](StringRef P
, const PreservedAnalyses
&) {
958 this->printAfterPassInvalidated(P
);
963 void OptNoneInstrumentation::registerCallbacks(
964 PassInstrumentationCallbacks
&PIC
) {
965 PIC
.registerShouldRunOptionalPassCallback(
966 [this](StringRef P
, Any IR
) { return this->shouldRun(P
, IR
); });
969 bool OptNoneInstrumentation::shouldRun(StringRef PassID
, Any IR
) {
970 const auto *F
= unwrapIR
<Function
>(IR
);
972 if (const auto *L
= unwrapIR
<Loop
>(IR
))
973 F
= L
->getHeader()->getParent();
975 bool ShouldRun
= !(F
&& F
->hasOptNone());
976 if (!ShouldRun
&& DebugLogging
) {
977 errs() << "Skipping pass " << PassID
<< " on " << F
->getName()
978 << " due to optnone attribute\n";
983 bool OptPassGateInstrumentation::shouldRun(StringRef PassName
, Any IR
) {
984 if (isIgnored(PassName
))
988 Context
.getOptPassGate().shouldRunPass(PassName
, getIRName(IR
));
989 if (!ShouldRun
&& !this->HasWrittenIR
&& !OptBisectPrintIRPath
.empty()) {
990 // FIXME: print IR if limit is higher than number of opt-bisect
992 this->HasWrittenIR
= true;
993 const Module
*M
= unwrapModule(IR
, /*Force=*/true);
994 assert((M
&& &M
->getContext() == &Context
) && "Missing/Mismatching Module");
996 raw_fd_ostream
OS(OptBisectPrintIRPath
, EC
);
998 report_fatal_error(errorCodeToError(EC
));
999 M
->print(OS
, nullptr);
1004 void OptPassGateInstrumentation::registerCallbacks(
1005 PassInstrumentationCallbacks
&PIC
) {
1006 OptPassGate
&PassGate
= Context
.getOptPassGate();
1007 if (!PassGate
.isEnabled())
1010 PIC
.registerShouldRunOptionalPassCallback([this](StringRef PassName
, Any IR
) {
1011 return this->shouldRun(PassName
, IR
);
1015 raw_ostream
&PrintPassInstrumentation::print() {
1017 assert(Indent
>= 0);
1018 dbgs().indent(Indent
);
1023 void PrintPassInstrumentation::registerCallbacks(
1024 PassInstrumentationCallbacks
&PIC
) {
1028 std::vector
<StringRef
> SpecialPasses
;
1029 if (!Opts
.Verbose
) {
1030 SpecialPasses
.emplace_back("PassManager");
1031 SpecialPasses
.emplace_back("PassAdaptor");
1034 PIC
.registerBeforeSkippedPassCallback([this, SpecialPasses
](StringRef PassID
,
1036 assert(!isSpecialPass(PassID
, SpecialPasses
) &&
1037 "Unexpectedly skipping special pass");
1039 print() << "Skipping pass: " << PassID
<< " on " << getIRName(IR
) << "\n";
1041 PIC
.registerBeforeNonSkippedPassCallback([this, SpecialPasses
](
1042 StringRef PassID
, Any IR
) {
1043 if (isSpecialPass(PassID
, SpecialPasses
))
1047 OS
<< "Running pass: " << PassID
<< " on " << getIRName(IR
);
1048 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
1049 unsigned Count
= F
->getInstructionCount();
1050 OS
<< " (" << Count
<< " instruction";
1054 } else if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
1055 int Count
= C
->size();
1056 OS
<< " (" << Count
<< " node";
1064 PIC
.registerAfterPassCallback(
1065 [this, SpecialPasses
](StringRef PassID
, Any IR
,
1066 const PreservedAnalyses
&) {
1067 if (isSpecialPass(PassID
, SpecialPasses
))
1072 PIC
.registerAfterPassInvalidatedCallback(
1073 [this, SpecialPasses
](StringRef PassID
, Any IR
) {
1074 if (isSpecialPass(PassID
, SpecialPasses
))
1080 if (!Opts
.SkipAnalyses
) {
1081 PIC
.registerBeforeAnalysisCallback([this](StringRef PassID
, Any IR
) {
1082 print() << "Running analysis: " << PassID
<< " on " << getIRName(IR
)
1086 PIC
.registerAfterAnalysisCallback(
1087 [this](StringRef PassID
, Any IR
) { Indent
-= 2; });
1088 PIC
.registerAnalysisInvalidatedCallback([this](StringRef PassID
, Any IR
) {
1089 print() << "Invalidating analysis: " << PassID
<< " on " << getIRName(IR
)
1092 PIC
.registerAnalysesClearedCallback([this](StringRef IRName
) {
1093 print() << "Clearing all analysis results for: " << IRName
<< "\n";
1098 PreservedCFGCheckerInstrumentation::CFG::CFG(const Function
*F
,
1099 bool TrackBBLifetime
) {
1100 if (TrackBBLifetime
)
1101 BBGuards
= DenseMap
<intptr_t, BBGuard
>(F
->size());
1102 for (const auto &BB
: *F
) {
1104 BBGuards
->try_emplace(intptr_t(&BB
), &BB
);
1105 for (const auto *Succ
: successors(&BB
)) {
1108 BBGuards
->try_emplace(intptr_t(Succ
), Succ
);
1113 static void printBBName(raw_ostream
&out
, const BasicBlock
*BB
) {
1114 if (BB
->hasName()) {
1115 out
<< BB
->getName() << "<" << BB
<< ">";
1119 if (!BB
->getParent()) {
1120 out
<< "unnamed_removed<" << BB
<< ">";
1124 if (BB
->isEntryBlock()) {
1126 << "<" << BB
<< ">";
1130 unsigned FuncOrderBlockNum
= 0;
1131 for (auto &FuncBB
: *BB
->getParent()) {
1134 FuncOrderBlockNum
++;
1136 out
<< "unnamed_" << FuncOrderBlockNum
<< "<" << BB
<< ">";
1139 void PreservedCFGCheckerInstrumentation::CFG::printDiff(raw_ostream
&out
,
1142 assert(!After
.isPoisoned());
1143 if (Before
.isPoisoned()) {
1144 out
<< "Some blocks were deleted\n";
1148 // Find and print graph differences.
1149 if (Before
.Graph
.size() != After
.Graph
.size())
1150 out
<< "Different number of non-leaf basic blocks: before="
1151 << Before
.Graph
.size() << ", after=" << After
.Graph
.size() << "\n";
1153 for (auto &BB
: Before
.Graph
) {
1154 auto BA
= After
.Graph
.find(BB
.first
);
1155 if (BA
== After
.Graph
.end()) {
1156 out
<< "Non-leaf block ";
1157 printBBName(out
, BB
.first
);
1158 out
<< " is removed (" << BB
.second
.size() << " successors)\n";
1162 for (auto &BA
: After
.Graph
) {
1163 auto BB
= Before
.Graph
.find(BA
.first
);
1164 if (BB
== Before
.Graph
.end()) {
1165 out
<< "Non-leaf block ";
1166 printBBName(out
, BA
.first
);
1167 out
<< " is added (" << BA
.second
.size() << " successors)\n";
1171 if (BB
->second
== BA
.second
)
1174 out
<< "Different successors of block ";
1175 printBBName(out
, BA
.first
);
1176 out
<< " (unordered):\n";
1177 out
<< "- before (" << BB
->second
.size() << "): ";
1178 for (auto &SuccB
: BB
->second
) {
1179 printBBName(out
, SuccB
.first
);
1180 if (SuccB
.second
!= 1)
1181 out
<< "(" << SuccB
.second
<< "), ";
1186 out
<< "- after (" << BA
.second
.size() << "): ";
1187 for (auto &SuccA
: BA
.second
) {
1188 printBBName(out
, SuccA
.first
);
1189 if (SuccA
.second
!= 1)
1190 out
<< "(" << SuccA
.second
<< "), ";
1198 // PreservedCFGCheckerInstrumentation uses PreservedCFGCheckerAnalysis to check
1199 // passes, that reported they kept CFG analyses up-to-date, did not actually
1200 // change CFG. This check is done as follows. Before every functional pass in
1201 // BeforeNonSkippedPassCallback a CFG snapshot (an instance of
1202 // PreservedCFGCheckerInstrumentation::CFG) is requested from
1203 // FunctionAnalysisManager as a result of PreservedCFGCheckerAnalysis. When the
1204 // functional pass finishes and reports that CFGAnalyses or AllAnalyses are
1205 // up-to-date then the cached result of PreservedCFGCheckerAnalysis (if
1206 // available) is checked to be equal to a freshly created CFG snapshot.
1207 struct PreservedCFGCheckerAnalysis
1208 : public AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
> {
1209 friend AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
>;
1211 static AnalysisKey Key
;
1214 /// Provide the result type for this analysis pass.
1215 using Result
= PreservedCFGCheckerInstrumentation::CFG
;
1217 /// Run the analysis pass over a function and produce CFG.
1218 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1219 return Result(&F
, /* TrackBBLifetime */ true);
1223 AnalysisKey
PreservedCFGCheckerAnalysis::Key
;
1225 struct PreservedFunctionHashAnalysis
1226 : public AnalysisInfoMixin
<PreservedFunctionHashAnalysis
> {
1227 static AnalysisKey Key
;
1229 struct FunctionHash
{
1233 using Result
= FunctionHash
;
1235 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1236 return Result
{StructuralHash(F
)};
1240 AnalysisKey
PreservedFunctionHashAnalysis::Key
;
1242 struct PreservedModuleHashAnalysis
1243 : public AnalysisInfoMixin
<PreservedModuleHashAnalysis
> {
1244 static AnalysisKey Key
;
1250 using Result
= ModuleHash
;
1252 Result
run(Module
&F
, ModuleAnalysisManager
&FAM
) {
1253 return Result
{StructuralHash(F
)};
1257 AnalysisKey
PreservedModuleHashAnalysis::Key
;
1259 bool PreservedCFGCheckerInstrumentation::CFG::invalidate(
1260 Function
&F
, const PreservedAnalyses
&PA
,
1261 FunctionAnalysisManager::Invalidator
&) {
1262 auto PAC
= PA
.getChecker
<PreservedCFGCheckerAnalysis
>();
1263 return !(PAC
.preserved() || PAC
.preservedSet
<AllAnalysesOn
<Function
>>() ||
1264 PAC
.preservedSet
<CFGAnalyses
>());
1267 static SmallVector
<Function
*, 1> GetFunctions(Any IR
) {
1268 SmallVector
<Function
*, 1> Functions
;
1270 if (const auto *MaybeF
= unwrapIR
<Function
>(IR
)) {
1271 Functions
.push_back(const_cast<Function
*>(MaybeF
));
1272 } else if (const auto *MaybeM
= unwrapIR
<Module
>(IR
)) {
1273 for (Function
&F
: *const_cast<Module
*>(MaybeM
))
1274 Functions
.push_back(&F
);
1279 void PreservedCFGCheckerInstrumentation::registerCallbacks(
1280 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
&MAM
) {
1281 if (!VerifyAnalysisInvalidation
)
1284 bool Registered
= false;
1285 PIC
.registerBeforeNonSkippedPassCallback([this, &MAM
, Registered
](
1286 StringRef P
, Any IR
) mutable {
1287 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1288 assert(&PassStack
.emplace_back(P
));
1292 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1293 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1296 FAM
.registerPass([&] { return PreservedCFGCheckerAnalysis(); });
1297 FAM
.registerPass([&] { return PreservedFunctionHashAnalysis(); });
1298 MAM
.registerPass([&] { return PreservedModuleHashAnalysis(); });
1302 for (Function
*F
: GetFunctions(IR
)) {
1303 // Make sure a fresh CFG snapshot is available before the pass.
1304 FAM
.getResult
<PreservedCFGCheckerAnalysis
>(*F
);
1305 FAM
.getResult
<PreservedFunctionHashAnalysis
>(*F
);
1308 if (const auto *MPtr
= unwrapIR
<Module
>(IR
)) {
1309 auto &M
= *const_cast<Module
*>(MPtr
);
1310 MAM
.getResult
<PreservedModuleHashAnalysis
>(M
);
1314 PIC
.registerAfterPassInvalidatedCallback(
1315 [this](StringRef P
, const PreservedAnalyses
&PassPA
) {
1316 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1317 assert(PassStack
.pop_back_val() == P
&&
1318 "Before and After callbacks must correspond");
1323 PIC
.registerAfterPassCallback([this, &MAM
](StringRef P
, Any IR
,
1324 const PreservedAnalyses
&PassPA
) {
1325 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1326 assert(PassStack
.pop_back_val() == P
&&
1327 "Before and After callbacks must correspond");
1331 // We have to get the FAM via the MAM, rather than directly use a passed in
1332 // FAM because if MAM has not cached the FAM, it won't invalidate function
1334 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1335 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1338 for (Function
*F
: GetFunctions(IR
)) {
1339 if (auto *HashBefore
=
1340 FAM
.getCachedResult
<PreservedFunctionHashAnalysis
>(*F
)) {
1341 if (HashBefore
->Hash
!= StructuralHash(*F
)) {
1342 report_fatal_error(formatv(
1343 "Function @{0} changed by {1} without invalidating analyses",
1348 auto CheckCFG
= [](StringRef Pass
, StringRef FuncName
,
1349 const CFG
&GraphBefore
, const CFG
&GraphAfter
) {
1350 if (GraphAfter
== GraphBefore
)
1354 << "Error: " << Pass
1355 << " does not invalidate CFG analyses but CFG changes detected in "
1357 << FuncName
<< ":\n";
1358 CFG::printDiff(dbgs(), GraphBefore
, GraphAfter
);
1359 report_fatal_error(Twine("CFG unexpectedly changed by ", Pass
));
1362 if (auto *GraphBefore
=
1363 FAM
.getCachedResult
<PreservedCFGCheckerAnalysis
>(*F
))
1364 CheckCFG(P
, F
->getName(), *GraphBefore
,
1365 CFG(F
, /* TrackBBLifetime */ false));
1367 if (const auto *MPtr
= unwrapIR
<Module
>(IR
)) {
1368 auto &M
= *const_cast<Module
*>(MPtr
);
1369 if (auto *HashBefore
=
1370 MAM
.getCachedResult
<PreservedModuleHashAnalysis
>(M
)) {
1371 if (HashBefore
->Hash
!= StructuralHash(M
)) {
1372 report_fatal_error(formatv(
1373 "Module changed by {0} without invalidating analyses", P
));
1380 void VerifyInstrumentation::registerCallbacks(
1381 PassInstrumentationCallbacks
&PIC
) {
1382 PIC
.registerAfterPassCallback(
1383 [this](StringRef P
, Any IR
, const PreservedAnalyses
&PassPA
) {
1384 if (isIgnored(P
) || P
== "VerifierPass")
1386 const auto *F
= unwrapIR
<Function
>(IR
);
1388 if (const auto *L
= unwrapIR
<Loop
>(IR
))
1389 F
= L
->getHeader()->getParent();
1394 dbgs() << "Verifying function " << F
->getName() << "\n";
1396 if (verifyFunction(*F
, &errs()))
1397 report_fatal_error(formatv("Broken function found after pass "
1398 "\"{0}\", compilation aborted!",
1401 const auto *M
= unwrapIR
<Module
>(IR
);
1403 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
1404 M
= C
->begin()->getFunction().getParent();
1409 dbgs() << "Verifying module " << M
->getName() << "\n";
1411 if (verifyModule(*M
, &errs()))
1412 report_fatal_error(formatv("Broken module found after pass "
1413 "\"{0}\", compilation aborted!",
1420 InLineChangePrinter::~InLineChangePrinter() = default;
1422 void InLineChangePrinter::generateIRRepresentation(Any IR
,
1424 IRDataT
<EmptyData
> &D
) {
1425 IRComparer
<EmptyData
>::analyzeIR(IR
, D
);
1428 void InLineChangePrinter::handleAfter(StringRef PassID
, std::string
&Name
,
1429 const IRDataT
<EmptyData
> &Before
,
1430 const IRDataT
<EmptyData
> &After
,
1432 SmallString
<20> Banner
=
1433 formatv("*** IR Dump After {0} on {1} ***\n", PassID
, Name
);
1435 IRComparer
<EmptyData
>(Before
, After
)
1436 .compare(getModuleForComparison(IR
),
1437 [&](bool InModule
, unsigned Minor
,
1438 const FuncDataT
<EmptyData
> &Before
,
1439 const FuncDataT
<EmptyData
> &After
) -> void {
1440 handleFunctionCompare(Name
, "", PassID
, " on ", InModule
,
1441 Minor
, Before
, After
);
1446 void InLineChangePrinter::handleFunctionCompare(
1447 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
1448 bool InModule
, unsigned Minor
, const FuncDataT
<EmptyData
> &Before
,
1449 const FuncDataT
<EmptyData
> &After
) {
1450 // Print a banner when this is being shown in the context of a module
1452 Out
<< "\n*** IR for function " << Name
<< " ***\n";
1454 FuncDataT
<EmptyData
>::report(
1456 [&](const BlockDataT
<EmptyData
> *B
, const BlockDataT
<EmptyData
> *A
) {
1457 StringRef BStr
= B
? B
->getBody() : "\n";
1458 StringRef AStr
= A
? A
->getBody() : "\n";
1459 const std::string Removed
=
1460 UseColour
? "\033[31m-%l\033[0m\n" : "-%l\n";
1461 const std::string Added
= UseColour
? "\033[32m+%l\033[0m\n" : "+%l\n";
1462 const std::string NoChange
= " %l\n";
1463 Out
<< doSystemDiff(BStr
, AStr
, Removed
, Added
, NoChange
);
1467 void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
1468 if (PrintChanged
== ChangePrinter::DiffVerbose
||
1469 PrintChanged
== ChangePrinter::DiffQuiet
||
1470 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
1471 PrintChanged
== ChangePrinter::ColourDiffQuiet
)
1472 TextChangeReporter
<IRDataT
<EmptyData
>>::registerRequiredCallbacks(PIC
);
1475 TimeProfilingPassesHandler::TimeProfilingPassesHandler() {}
1477 void TimeProfilingPassesHandler::registerCallbacks(
1478 PassInstrumentationCallbacks
&PIC
) {
1479 if (!getTimeTraceProfilerInstance())
1481 PIC
.registerBeforeNonSkippedPassCallback(
1482 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1483 PIC
.registerAfterPassCallback(
1484 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
1485 this->runAfterPass();
1488 PIC
.registerAfterPassInvalidatedCallback(
1489 [this](StringRef P
, const PreservedAnalyses
&) { this->runAfterPass(); },
1491 PIC
.registerBeforeAnalysisCallback(
1492 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1493 PIC
.registerAfterAnalysisCallback(
1494 [this](StringRef P
, Any IR
) { this->runAfterPass(); }, true);
1497 void TimeProfilingPassesHandler::runBeforePass(StringRef PassID
, Any IR
) {
1498 timeTraceProfilerBegin(PassID
, getIRName(IR
));
1501 void TimeProfilingPassesHandler::runAfterPass() { timeTraceProfilerEnd(); }
1506 class DotCfgDiffDisplayGraph
;
1508 // Base class for a node or edge in the dot-cfg-changes graph.
1509 class DisplayElement
{
1511 // Is this in before, after, or both?
1512 StringRef
getColour() const { return Colour
; }
1515 DisplayElement(StringRef Colour
) : Colour(Colour
) {}
1516 const StringRef Colour
;
1519 // An edge representing a transition between basic blocks in the
1520 // dot-cfg-changes graph.
1521 class DisplayEdge
: public DisplayElement
{
1523 DisplayEdge(std::string Value
, DisplayNode
&Node
, StringRef Colour
)
1524 : DisplayElement(Colour
), Value(Value
), Node(Node
) {}
1525 // The value on which the transition is made.
1526 std::string
getValue() const { return Value
; }
1527 // The node (representing a basic block) reached by this transition.
1528 const DisplayNode
&getDestinationNode() const { return Node
; }
1532 const DisplayNode
&Node
;
1535 // A node in the dot-cfg-changes graph which represents a basic block.
1536 class DisplayNode
: public DisplayElement
{
1538 // \p C is the content for the node, \p T indicates the colour for the
1539 // outline of the node
1540 DisplayNode(std::string Content
, StringRef Colour
)
1541 : DisplayElement(Colour
), Content(Content
) {}
1543 // Iterator to the child nodes. Required by GraphWriter.
1544 using ChildIterator
= std::unordered_set
<DisplayNode
*>::const_iterator
;
1545 ChildIterator
children_begin() const { return Children
.cbegin(); }
1546 ChildIterator
children_end() const { return Children
.cend(); }
1548 // Iterator for the edges. Required by GraphWriter.
1549 using EdgeIterator
= std::vector
<DisplayEdge
*>::const_iterator
;
1550 EdgeIterator
edges_begin() const { return EdgePtrs
.cbegin(); }
1551 EdgeIterator
edges_end() const { return EdgePtrs
.cend(); }
1553 // Create an edge to \p Node on value \p Value, with colour \p Colour.
1554 void createEdge(StringRef Value
, DisplayNode
&Node
, StringRef Colour
);
1556 // Return the content of this node.
1557 std::string
getContent() const { return Content
; }
1559 // Return the edge to node \p S.
1560 const DisplayEdge
&getEdge(const DisplayNode
&To
) const {
1561 assert(EdgeMap
.find(&To
) != EdgeMap
.end() && "Expected to find edge.");
1562 return *EdgeMap
.find(&To
)->second
;
1565 // Return the value for the transition to basic block \p S.
1566 // Required by GraphWriter.
1567 std::string
getEdgeSourceLabel(const DisplayNode
&Sink
) const {
1568 return getEdge(Sink
).getValue();
1571 void createEdgeMap();
1574 const std::string Content
;
1576 // Place to collect all of the edges. Once they are all in the vector,
1577 // the vector will not reallocate so then we can use pointers to them,
1578 // which are required by the graph writing routines.
1579 std::vector
<DisplayEdge
> Edges
;
1581 std::vector
<DisplayEdge
*> EdgePtrs
;
1582 std::unordered_set
<DisplayNode
*> Children
;
1583 std::unordered_map
<const DisplayNode
*, const DisplayEdge
*> EdgeMap
;
1585 // Safeguard adding of edges.
1586 bool AllEdgesCreated
= false;
1589 // Class representing a difference display (corresponds to a pdf file).
1590 class DotCfgDiffDisplayGraph
{
1592 DotCfgDiffDisplayGraph(std::string Name
) : GraphName(Name
) {}
1594 // Generate the file into \p DotFile.
1595 void generateDotFile(StringRef DotFile
);
1597 // Iterator to the nodes. Required by GraphWriter.
1598 using NodeIterator
= std::vector
<DisplayNode
*>::const_iterator
;
1599 NodeIterator
nodes_begin() const {
1600 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1601 return NodePtrs
.cbegin();
1603 NodeIterator
nodes_end() const {
1604 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1605 return NodePtrs
.cend();
1608 // Record the index of the entry node. At this point, we can build up
1609 // vectors of pointers that are required by the graph routines.
1610 void setEntryNode(unsigned N
) {
1611 // At this point, there will be no new nodes.
1612 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1613 NodeGenerationComplete
= true;
1614 for (auto &N
: Nodes
)
1615 NodePtrs
.emplace_back(&N
);
1617 EntryNode
= NodePtrs
[N
];
1621 void createNode(std::string C
, StringRef Colour
) {
1622 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1623 Nodes
.emplace_back(C
, Colour
);
1625 // Return the node at index \p N to avoid problems with vectors reallocating.
1626 DisplayNode
&getNode(unsigned N
) {
1627 assert(N
< Nodes
.size() && "Node is out of bounds");
1630 unsigned size() const {
1631 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1632 return Nodes
.size();
1635 // Return the name of the graph. Required by GraphWriter.
1636 std::string
getGraphName() const { return GraphName
; }
1638 // Return the string representing the differences for basic block \p Node.
1639 // Required by GraphWriter.
1640 std::string
getNodeLabel(const DisplayNode
&Node
) const {
1641 return Node
.getContent();
1644 // Return a string with colour information for Dot. Required by GraphWriter.
1645 std::string
getNodeAttributes(const DisplayNode
&Node
) const {
1646 return attribute(Node
.getColour());
1649 // Return a string with colour information for Dot. Required by GraphWriter.
1650 std::string
getEdgeColorAttr(const DisplayNode
&From
,
1651 const DisplayNode
&To
) const {
1652 return attribute(From
.getEdge(To
).getColour());
1655 // Get the starting basic block. Required by GraphWriter.
1656 DisplayNode
*getEntryNode() const {
1657 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1662 // Return the string containing the colour to use as a Dot attribute.
1663 std::string
attribute(StringRef Colour
) const {
1664 return "color=" + Colour
.str();
1667 bool NodeGenerationComplete
= false;
1668 const std::string GraphName
;
1669 std::vector
<DisplayNode
> Nodes
;
1670 std::vector
<DisplayNode
*> NodePtrs
;
1671 DisplayNode
*EntryNode
= nullptr;
1674 void DisplayNode::createEdge(StringRef Value
, DisplayNode
&Node
,
1676 assert(!AllEdgesCreated
&& "Expected to be able to still create edges.");
1677 Edges
.emplace_back(Value
.str(), Node
, Colour
);
1678 Children
.insert(&Node
);
1681 void DisplayNode::createEdgeMap() {
1682 // No more edges will be added so we can now use pointers to the edges
1683 // as the vector will not grow and reallocate.
1684 AllEdgesCreated
= true;
1685 for (auto &E
: Edges
)
1686 EdgeMap
.insert({&E
.getDestinationNode(), &E
});
1689 class DotCfgDiffNode
;
1692 // A class representing a basic block in the Dot difference graph.
1693 class DotCfgDiffNode
{
1695 DotCfgDiffNode() = delete;
1697 // Create a node in Dot difference graph \p G representing the basic block
1698 // represented by \p BD with colour \p Colour (where it exists).
1699 DotCfgDiffNode(DotCfgDiff
&G
, unsigned N
, const BlockDataT
<DCData
> &BD
,
1701 : Graph(G
), N(N
), Data
{&BD
, nullptr}, Colour(Colour
) {}
1702 DotCfgDiffNode(const DotCfgDiffNode
&DN
)
1703 : Graph(DN
.Graph
), N(DN
.N
), Data
{DN
.Data
[0], DN
.Data
[1]},
1704 Colour(DN
.Colour
), EdgesMap(DN
.EdgesMap
), Children(DN
.Children
),
1707 unsigned getIndex() const { return N
; }
1709 // The label of the basic block
1710 StringRef
getLabel() const {
1711 assert(Data
[0] && "Expected Data[0] to be set.");
1712 return Data
[0]->getLabel();
1714 // Return the colour for this block
1715 StringRef
getColour() const { return Colour
; }
1716 // Change this basic block from being only in before to being common.
1717 // Save the pointer to \p Other.
1718 void setCommon(const BlockDataT
<DCData
> &Other
) {
1719 assert(!Data
[1] && "Expected only one block datum");
1721 Colour
= CommonColour
;
1723 // Add an edge to \p E of colour {\p Value, \p Colour}.
1724 void addEdge(unsigned E
, StringRef Value
, StringRef Colour
) {
1725 // This is a new edge or it is an edge being made common.
1726 assert((EdgesMap
.count(E
) == 0 || Colour
== CommonColour
) &&
1727 "Unexpected edge count and color.");
1728 EdgesMap
[E
] = {Value
.str(), Colour
};
1730 // Record the children and create edges.
1731 void finalize(DotCfgDiff
&G
);
1733 // Return the colour of the edge to node \p S.
1734 StringRef
getEdgeColour(const unsigned S
) const {
1735 assert(EdgesMap
.count(S
) == 1 && "Expected to find edge.");
1736 return EdgesMap
.at(S
).second
;
1739 // Return the string representing the basic block.
1740 std::string
getBodyContent() const;
1742 void createDisplayEdges(DotCfgDiffDisplayGraph
&Graph
, unsigned DisplayNode
,
1743 std::map
<const unsigned, unsigned> &NodeMap
) const;
1748 const BlockDataT
<DCData
> *Data
[2];
1750 std::map
<const unsigned, std::pair
<std::string
, StringRef
>> EdgesMap
;
1751 std::vector
<unsigned> Children
;
1752 std::vector
<unsigned> Edges
;
1755 // Class representing the difference graph between two functions.
1758 // \p Title is the title given to the graph. \p EntryNodeName is the
1759 // entry node for the function. \p Before and \p After are the before
1760 // after versions of the function, respectively. \p Dir is the directory
1761 // in which to store the results.
1762 DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1763 const FuncDataT
<DCData
> &After
);
1765 DotCfgDiff(const DotCfgDiff
&) = delete;
1766 DotCfgDiff
&operator=(const DotCfgDiff
&) = delete;
1768 DotCfgDiffDisplayGraph
createDisplayGraph(StringRef Title
,
1769 StringRef EntryNodeName
);
1771 // Return a string consisting of the labels for the \p Source and \p Sink.
1772 // The combination allows distinguishing changing transitions on the
1773 // same value (ie, a transition went to X before and goes to Y after).
1774 // Required by GraphWriter.
1775 StringRef
getEdgeSourceLabel(const unsigned &Source
,
1776 const unsigned &Sink
) const {
1778 getNode(Source
).getLabel().str() + " " + getNode(Sink
).getLabel().str();
1779 assert(EdgeLabels
.count(S
) == 1 && "Expected to find edge label.");
1780 return EdgeLabels
.find(S
)->getValue();
1783 // Return the number of basic blocks (nodes). Required by GraphWriter.
1784 unsigned size() const { return Nodes
.size(); }
1786 const DotCfgDiffNode
&getNode(unsigned N
) const {
1787 assert(N
< Nodes
.size() && "Unexpected index for node reference");
1792 // Return the string surrounded by HTML to make it the appropriate colour.
1793 std::string
colourize(std::string S
, StringRef Colour
) const;
1795 void createNode(StringRef Label
, const BlockDataT
<DCData
> &BD
, StringRef C
) {
1796 unsigned Pos
= Nodes
.size();
1797 Nodes
.emplace_back(*this, Pos
, BD
, C
);
1798 NodePosition
.insert({Label
, Pos
});
1801 // TODO Nodes should probably be a StringMap<DotCfgDiffNode> after the
1802 // display graph is separated out, which would remove the need for
1804 std::vector
<DotCfgDiffNode
> Nodes
;
1805 StringMap
<unsigned> NodePosition
;
1806 const std::string GraphName
;
1808 StringMap
<std::string
> EdgeLabels
;
1811 std::string
DotCfgDiffNode::getBodyContent() const {
1812 if (Colour
== CommonColour
) {
1813 assert(Data
[1] && "Expected Data[1] to be set.");
1816 for (unsigned I
= 0; I
< 2; ++I
) {
1817 SR
[I
] = Data
[I
]->getBody();
1818 // drop initial '\n' if present
1819 if (SR
[I
][0] == '\n')
1820 SR
[I
] = SR
[I
].drop_front();
1821 // drop predecessors as they can be big and are redundant
1822 SR
[I
] = SR
[I
].drop_until([](char C
) { return C
== '\n'; }).drop_front();
1825 SmallString
<80> OldLineFormat
= formatv(
1826 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", BeforeColour
);
1827 SmallString
<80> NewLineFormat
= formatv(
1828 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", AfterColour
);
1829 SmallString
<80> UnchangedLineFormat
= formatv(
1830 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", CommonColour
);
1831 std::string Diff
= Data
[0]->getLabel().str();
1832 Diff
+= ":\n<BR align=\"left\"/>" +
1833 doSystemDiff(makeHTMLReady(SR
[0]), makeHTMLReady(SR
[1]),
1834 OldLineFormat
, NewLineFormat
, UnchangedLineFormat
);
1836 // Diff adds in some empty colour changes which are not valid HTML
1837 // so remove them. Colours are all lowercase alpha characters (as
1838 // listed in https://graphviz.org/pdf/dotguide.pdf).
1839 Regex
R("<FONT COLOR=\"\\w+\"></FONT>");
1842 std::string S
= R
.sub("", Diff
, &Error
);
1849 llvm_unreachable("Should not get here");
1852 // Put node out in the appropriate colour.
1853 assert(!Data
[1] && "Data[1] is set unexpectedly.");
1854 std::string Body
= makeHTMLReady(Data
[0]->getBody());
1855 const StringRef BS
= Body
;
1857 // Drop leading newline, if present.
1858 if (BS
.front() == '\n')
1859 BS1
= BS1
.drop_front(1);
1861 StringRef Label
= BS1
.take_until([](char C
) { return C
== ':'; });
1862 // drop predecessors as they can be big and are redundant
1863 BS1
= BS1
.drop_until([](char C
) { return C
== '\n'; }).drop_front();
1865 std::string S
= "<FONT COLOR=\"" + Colour
.str() + "\">" + Label
.str() + ":";
1867 // align each line to the left.
1868 while (BS1
.size()) {
1869 S
.append("<BR align=\"left\"/>");
1870 StringRef Line
= BS1
.take_until([](char C
) { return C
== '\n'; });
1871 S
.append(Line
.str());
1872 BS1
= BS1
.drop_front(Line
.size() + 1);
1874 S
.append("<BR align=\"left\"/></FONT>");
1878 std::string
DotCfgDiff::colourize(std::string S
, StringRef Colour
) const {
1879 if (S
.length() == 0)
1881 return "<FONT COLOR=\"" + Colour
.str() + "\">" + S
+ "</FONT>";
1884 DotCfgDiff::DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1885 const FuncDataT
<DCData
> &After
)
1886 : GraphName(Title
.str()) {
1887 StringMap
<StringRef
> EdgesMap
;
1889 // Handle each basic block in the before IR.
1890 for (auto &B
: Before
.getData()) {
1891 StringRef Label
= B
.getKey();
1892 const BlockDataT
<DCData
> &BD
= B
.getValue();
1893 createNode(Label
, BD
, BeforeColour
);
1895 // Create transitions with names made up of the from block label, the value
1896 // on which the transition is made and the to block label.
1897 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
1898 E
= BD
.getData().end();
1899 Sink
!= E
; ++Sink
) {
1900 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
1901 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
1902 EdgesMap
.insert({Key
, BeforeColour
});
1906 // Handle each basic block in the after IR
1907 for (auto &A
: After
.getData()) {
1908 StringRef Label
= A
.getKey();
1909 const BlockDataT
<DCData
> &BD
= A
.getValue();
1910 unsigned C
= NodePosition
.count(Label
);
1912 // This only exists in the after IR. Create the node.
1913 createNode(Label
, BD
, AfterColour
);
1915 assert(C
== 1 && "Unexpected multiple nodes.");
1916 Nodes
[NodePosition
[Label
]].setCommon(BD
);
1918 // Add in the edges between the nodes (as common or only in after).
1919 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
1920 E
= BD
.getData().end();
1921 Sink
!= E
; ++Sink
) {
1922 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
1923 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
1924 unsigned C
= EdgesMap
.count(Key
);
1926 EdgesMap
.insert({Key
, AfterColour
});
1928 EdgesMap
[Key
] = CommonColour
;
1933 // Now go through the map of edges and add them to the node.
1934 for (auto &E
: EdgesMap
) {
1935 // Extract the source, sink and value from the edge key.
1936 StringRef S
= E
.getKey();
1937 auto SP1
= S
.rsplit(' ');
1938 auto &SourceSink
= SP1
.first
;
1939 auto SP2
= SourceSink
.split(' ');
1940 StringRef Source
= SP2
.first
;
1941 StringRef Sink
= SP2
.second
;
1942 StringRef Value
= SP1
.second
;
1944 assert(NodePosition
.count(Source
) == 1 && "Expected to find node.");
1945 DotCfgDiffNode
&SourceNode
= Nodes
[NodePosition
[Source
]];
1946 assert(NodePosition
.count(Sink
) == 1 && "Expected to find node.");
1947 unsigned SinkNode
= NodePosition
[Sink
];
1948 StringRef Colour
= E
.second
;
1950 // Look for an edge from Source to Sink
1951 if (EdgeLabels
.count(SourceSink
) == 0)
1952 EdgeLabels
.insert({SourceSink
, colourize(Value
.str(), Colour
)});
1954 StringRef V
= EdgeLabels
.find(SourceSink
)->getValue();
1955 std::string NV
= colourize(V
.str() + " " + Value
.str(), Colour
);
1956 Colour
= CommonColour
;
1957 EdgeLabels
[SourceSink
] = NV
;
1959 SourceNode
.addEdge(SinkNode
, Value
, Colour
);
1961 for (auto &I
: Nodes
)
1965 DotCfgDiffDisplayGraph
DotCfgDiff::createDisplayGraph(StringRef Title
,
1966 StringRef EntryNodeName
) {
1967 assert(NodePosition
.count(EntryNodeName
) == 1 &&
1968 "Expected to find entry block in map.");
1969 unsigned Entry
= NodePosition
[EntryNodeName
];
1970 assert(Entry
< Nodes
.size() && "Expected to find entry node");
1971 DotCfgDiffDisplayGraph
G(Title
.str());
1973 std::map
<const unsigned, unsigned> NodeMap
;
1975 int EntryIndex
= -1;
1977 for (auto &I
: Nodes
) {
1978 if (I
.getIndex() == Entry
)
1980 G
.createNode(I
.getBodyContent(), I
.getColour());
1981 NodeMap
.insert({I
.getIndex(), Index
++});
1983 assert(EntryIndex
>= 0 && "Expected entry node index to be set.");
1984 G
.setEntryNode(EntryIndex
);
1986 for (auto &I
: NodeMap
) {
1987 unsigned SourceNode
= I
.first
;
1988 unsigned DisplayNode
= I
.second
;
1989 getNode(SourceNode
).createDisplayEdges(G
, DisplayNode
, NodeMap
);
1994 void DotCfgDiffNode::createDisplayEdges(
1995 DotCfgDiffDisplayGraph
&DisplayGraph
, unsigned DisplayNodeIndex
,
1996 std::map
<const unsigned, unsigned> &NodeMap
) const {
1998 DisplayNode
&SourceDisplayNode
= DisplayGraph
.getNode(DisplayNodeIndex
);
2000 for (auto I
: Edges
) {
2001 unsigned SinkNodeIndex
= I
;
2002 StringRef Colour
= getEdgeColour(SinkNodeIndex
);
2003 const DotCfgDiffNode
*SinkNode
= &Graph
.getNode(SinkNodeIndex
);
2005 StringRef Label
= Graph
.getEdgeSourceLabel(getIndex(), SinkNodeIndex
);
2006 DisplayNode
&SinkDisplayNode
= DisplayGraph
.getNode(SinkNode
->getIndex());
2007 SourceDisplayNode
.createEdge(Label
, SinkDisplayNode
, Colour
);
2009 SourceDisplayNode
.createEdgeMap();
2012 void DotCfgDiffNode::finalize(DotCfgDiff
&G
) {
2013 for (auto E
: EdgesMap
) {
2014 Children
.emplace_back(E
.first
);
2015 Edges
.emplace_back(E
.first
);
2023 template <> struct GraphTraits
<DotCfgDiffDisplayGraph
*> {
2024 using NodeRef
= const DisplayNode
*;
2025 using ChildIteratorType
= DisplayNode::ChildIterator
;
2026 using nodes_iterator
= DotCfgDiffDisplayGraph::NodeIterator
;
2027 using EdgeRef
= const DisplayEdge
*;
2028 using ChildEdgeIterator
= DisplayNode::EdgeIterator
;
2030 static NodeRef
getEntryNode(const DotCfgDiffDisplayGraph
*G
) {
2031 return G
->getEntryNode();
2033 static ChildIteratorType
child_begin(NodeRef N
) {
2034 return N
->children_begin();
2036 static ChildIteratorType
child_end(NodeRef N
) { return N
->children_end(); }
2037 static nodes_iterator
nodes_begin(const DotCfgDiffDisplayGraph
*G
) {
2038 return G
->nodes_begin();
2040 static nodes_iterator
nodes_end(const DotCfgDiffDisplayGraph
*G
) {
2041 return G
->nodes_end();
2043 static ChildEdgeIterator
child_edge_begin(NodeRef N
) {
2044 return N
->edges_begin();
2046 static ChildEdgeIterator
child_edge_end(NodeRef N
) { return N
->edges_end(); }
2047 static NodeRef
edge_dest(EdgeRef E
) { return &E
->getDestinationNode(); }
2048 static unsigned size(const DotCfgDiffDisplayGraph
*G
) { return G
->size(); }
2052 struct DOTGraphTraits
<DotCfgDiffDisplayGraph
*> : public DefaultDOTGraphTraits
{
2053 explicit DOTGraphTraits(bool Simple
= false)
2054 : DefaultDOTGraphTraits(Simple
) {}
2056 static bool renderNodesUsingHTML() { return true; }
2057 static std::string
getGraphName(const DotCfgDiffDisplayGraph
*DiffData
) {
2058 return DiffData
->getGraphName();
2061 getGraphProperties(const DotCfgDiffDisplayGraph
*DiffData
) {
2062 return "\tsize=\"190, 190\";\n";
2064 static std::string
getNodeLabel(const DisplayNode
*Node
,
2065 const DotCfgDiffDisplayGraph
*DiffData
) {
2066 return DiffData
->getNodeLabel(*Node
);
2068 static std::string
getNodeAttributes(const DisplayNode
*Node
,
2069 const DotCfgDiffDisplayGraph
*DiffData
) {
2070 return DiffData
->getNodeAttributes(*Node
);
2072 static std::string
getEdgeSourceLabel(const DisplayNode
*From
,
2073 DisplayNode::ChildIterator
&To
) {
2074 return From
->getEdgeSourceLabel(**To
);
2076 static std::string
getEdgeAttributes(const DisplayNode
*From
,
2077 DisplayNode::ChildIterator
&To
,
2078 const DotCfgDiffDisplayGraph
*DiffData
) {
2079 return DiffData
->getEdgeColorAttr(*From
, **To
);
2087 void DotCfgDiffDisplayGraph::generateDotFile(StringRef DotFile
) {
2089 raw_fd_ostream
OutStream(DotFile
, EC
);
2091 errs() << "Error: " << EC
.message() << "\n";
2094 WriteGraph(OutStream
, this, false);
2103 DCData::DCData(const BasicBlock
&B
) {
2104 // Build up transition labels.
2105 const Instruction
*Term
= B
.getTerminator();
2106 if (const BranchInst
*Br
= dyn_cast
<const BranchInst
>(Term
))
2107 if (Br
->isUnconditional())
2108 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "");
2110 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "true");
2111 addSuccessorLabel(Br
->getSuccessor(1)->getName().str(), "false");
2113 else if (const SwitchInst
*Sw
= dyn_cast
<const SwitchInst
>(Term
)) {
2114 addSuccessorLabel(Sw
->case_default()->getCaseSuccessor()->getName().str(),
2116 for (auto &C
: Sw
->cases()) {
2117 assert(C
.getCaseValue() && "Expected to find case value.");
2118 SmallString
<20> Value
= formatv("{0}", C
.getCaseValue()->getSExtValue());
2119 addSuccessorLabel(C
.getCaseSuccessor()->getName().str(), Value
);
2122 for (const BasicBlock
*Succ
: successors(&B
))
2123 addSuccessorLabel(Succ
->getName().str(), "");
2126 DotCfgChangeReporter::DotCfgChangeReporter(bool Verbose
)
2127 : ChangeReporter
<IRDataT
<DCData
>>(Verbose
) {}
2129 void DotCfgChangeReporter::handleFunctionCompare(
2130 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
2131 bool InModule
, unsigned Minor
, const FuncDataT
<DCData
> &Before
,
2132 const FuncDataT
<DCData
> &After
) {
2133 assert(HTML
&& "Expected outstream to be set");
2134 SmallString
<8> Extender
;
2135 SmallString
<8> Number
;
2136 // Handle numbering and file names.
2138 Extender
= formatv("{0}_{1}", N
, Minor
);
2139 Number
= formatv("{0}.{1}", N
, Minor
);
2141 Extender
= formatv("{0}", N
);
2142 Number
= formatv("{0}", N
);
2144 // Create a temporary file name for the dot file.
2145 SmallVector
<char, 128> SV
;
2146 sys::fs::createUniquePath("cfgdot-%%%%%%.dot", SV
, true);
2147 std::string DotFile
= Twine(SV
).str();
2149 SmallString
<20> PDFFileName
= formatv("diff_{0}.pdf", Extender
);
2150 SmallString
<200> Text
;
2152 Text
= formatv("{0}.{1}{2}{3}{4}", Number
, Prefix
, makeHTMLReady(PassID
),
2155 DotCfgDiff
Diff(Text
, Before
, After
);
2156 std::string EntryBlockName
= After
.getEntryBlockName();
2157 // Use the before entry block if the after entry block was removed.
2158 if (EntryBlockName
== "")
2159 EntryBlockName
= Before
.getEntryBlockName();
2160 assert(EntryBlockName
!= "" && "Expected to find entry block");
2162 DotCfgDiffDisplayGraph DG
= Diff
.createDisplayGraph(Text
, EntryBlockName
);
2163 DG
.generateDotFile(DotFile
);
2165 *HTML
<< genHTML(Text
, DotFile
, PDFFileName
);
2166 std::error_code EC
= sys::fs::remove(DotFile
);
2168 errs() << "Error: " << EC
.message() << "\n";
2171 std::string
DotCfgChangeReporter::genHTML(StringRef Text
, StringRef DotFile
,
2172 StringRef PDFFileName
) {
2173 SmallString
<20> PDFFile
= formatv("{0}/{1}", DotCfgDir
, PDFFileName
);
2174 // Create the PDF file.
2175 static ErrorOr
<std::string
> DotExe
= sys::findProgramByName(DotBinary
);
2177 return "Unable to find dot executable.";
2179 StringRef Args
[] = {DotBinary
, "-Tpdf", "-o", PDFFile
, DotFile
};
2180 int Result
= sys::ExecuteAndWait(*DotExe
, Args
, std::nullopt
);
2182 return "Error executing system dot.";
2184 // Create the HTML tag refering to the PDF file.
2185 SmallString
<200> S
= formatv(
2186 " <a href=\"{0}\" target=\"_blank\">{1}</a><br/>\n", PDFFileName
, Text
);
2190 void DotCfgChangeReporter::handleInitialIR(Any IR
) {
2191 assert(HTML
&& "Expected outstream to be set");
2192 *HTML
<< "<button type=\"button\" class=\"collapsible\">0. "
2193 << "Initial IR (by function)</button>\n"
2194 << "<div class=\"content\">\n"
2196 // Create representation of IR
2197 IRDataT
<DCData
> Data
;
2198 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2199 // Now compare it against itself, which will have everything the
2200 // same and will generate the files.
2201 IRComparer
<DCData
>(Data
, Data
)
2202 .compare(getModuleForComparison(IR
),
2203 [&](bool InModule
, unsigned Minor
,
2204 const FuncDataT
<DCData
> &Before
,
2205 const FuncDataT
<DCData
> &After
) -> void {
2206 handleFunctionCompare("", " ", "Initial IR", "", InModule
,
2207 Minor
, Before
, After
);
2214 void DotCfgChangeReporter::generateIRRepresentation(Any IR
, StringRef PassID
,
2215 IRDataT
<DCData
> &Data
) {
2216 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2219 void DotCfgChangeReporter::omitAfter(StringRef PassID
, std::string
&Name
) {
2220 assert(HTML
&& "Expected outstream to be set");
2221 SmallString
<20> Banner
=
2222 formatv(" <a>{0}. Pass {1} on {2} omitted because no change</a><br/>\n",
2223 N
, makeHTMLReady(PassID
), Name
);
2228 void DotCfgChangeReporter::handleAfter(StringRef PassID
, std::string
&Name
,
2229 const IRDataT
<DCData
> &Before
,
2230 const IRDataT
<DCData
> &After
, Any IR
) {
2231 assert(HTML
&& "Expected outstream to be set");
2232 IRComparer
<DCData
>(Before
, After
)
2233 .compare(getModuleForComparison(IR
),
2234 [&](bool InModule
, unsigned Minor
,
2235 const FuncDataT
<DCData
> &Before
,
2236 const FuncDataT
<DCData
> &After
) -> void {
2237 handleFunctionCompare(Name
, " Pass ", PassID
, " on ", InModule
,
2238 Minor
, Before
, After
);
2240 *HTML
<< " </p></div>\n";
2244 void DotCfgChangeReporter::handleInvalidated(StringRef PassID
) {
2245 assert(HTML
&& "Expected outstream to be set");
2246 SmallString
<20> Banner
=
2247 formatv(" <a>{0}. {1} invalidated</a><br/>\n", N
, makeHTMLReady(PassID
));
2252 void DotCfgChangeReporter::handleFiltered(StringRef PassID
, std::string
&Name
) {
2253 assert(HTML
&& "Expected outstream to be set");
2254 SmallString
<20> Banner
=
2255 formatv(" <a>{0}. Pass {1} on {2} filtered out</a><br/>\n", N
,
2256 makeHTMLReady(PassID
), Name
);
2261 void DotCfgChangeReporter::handleIgnored(StringRef PassID
, std::string
&Name
) {
2262 assert(HTML
&& "Expected outstream to be set");
2263 SmallString
<20> Banner
= formatv(" <a>{0}. {1} on {2} ignored</a><br/>\n", N
,
2264 makeHTMLReady(PassID
), Name
);
2269 bool DotCfgChangeReporter::initializeHTML() {
2271 HTML
= std::make_unique
<raw_fd_ostream
>(DotCfgDir
+ "/passes.html", EC
);
2277 *HTML
<< "<!doctype html>"
2280 << "<style>.collapsible { "
2281 << "background-color: #777;"
2283 << " cursor: pointer;"
2284 << " padding: 18px;"
2287 << " text-align: left;"
2288 << " outline: none;"
2289 << " font-size: 15px;"
2290 << "} .active, .collapsible:hover {"
2291 << " background-color: #555;"
2293 << " padding: 0 18px;"
2294 << " display: none;"
2295 << " overflow: hidden;"
2296 << " background-color: #f1f1f1;"
2299 << "<title>passes.html</title>"
2305 DotCfgChangeReporter::~DotCfgChangeReporter() {
2309 << "<script>var coll = document.getElementsByClassName(\"collapsible\");"
2311 << "for (i = 0; i < coll.length; i++) {"
2312 << "coll[i].addEventListener(\"click\", function() {"
2313 << " this.classList.toggle(\"active\");"
2314 << " var content = this.nextElementSibling;"
2315 << " if (content.style.display === \"block\"){"
2316 << " content.style.display = \"none\";"
2319 << " content.style.display= \"block\";"
2330 void DotCfgChangeReporter::registerCallbacks(
2331 PassInstrumentationCallbacks
&PIC
) {
2332 if (PrintChanged
== ChangePrinter::DotCfgVerbose
||
2333 PrintChanged
== ChangePrinter::DotCfgQuiet
) {
2334 SmallString
<128> OutputDir
;
2335 sys::fs::expand_tilde(DotCfgDir
, OutputDir
);
2336 sys::fs::make_absolute(OutputDir
);
2337 assert(!OutputDir
.empty() && "expected output dir to be non-empty");
2338 DotCfgDir
= OutputDir
.c_str();
2339 if (initializeHTML()) {
2340 ChangeReporter
<IRDataT
<DCData
>>::registerRequiredCallbacks(PIC
);
2343 dbgs() << "Unable to open output stream for -cfg-dot-changed\n";
2347 StandardInstrumentations::StandardInstrumentations(
2348 LLVMContext
&Context
, bool DebugLogging
, bool VerifyEach
,
2349 PrintPassOptions PrintPassOpts
)
2350 : PrintPass(DebugLogging
, PrintPassOpts
),
2351 OptNone(DebugLogging
),
2352 OptPassGate(Context
),
2353 PrintChangedIR(PrintChanged
== ChangePrinter::Verbose
),
2354 PrintChangedDiff(PrintChanged
== ChangePrinter::DiffVerbose
||
2355 PrintChanged
== ChangePrinter::ColourDiffVerbose
,
2356 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
2357 PrintChanged
== ChangePrinter::ColourDiffQuiet
),
2358 WebsiteChangeReporter(PrintChanged
== ChangePrinter::DotCfgVerbose
),
2359 Verify(DebugLogging
), VerifyEach(VerifyEach
) {}
2361 PrintCrashIRInstrumentation
*PrintCrashIRInstrumentation::CrashReporter
=
2364 void PrintCrashIRInstrumentation::reportCrashIR() {
2365 if (!PrintOnCrashPath
.empty()) {
2367 raw_fd_ostream
Out(PrintOnCrashPath
, EC
);
2369 report_fatal_error(errorCodeToError(EC
));
2376 void PrintCrashIRInstrumentation::SignalHandler(void *) {
2377 // Called by signal handlers so do not lock here
2378 // Is the PrintCrashIRInstrumentation still alive?
2382 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2383 "Did not expect to get here without option set.");
2384 CrashReporter
->reportCrashIR();
2387 PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
2391 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2392 "Did not expect to get here without option set.");
2393 CrashReporter
= nullptr;
2396 void PrintCrashIRInstrumentation::registerCallbacks(
2397 PassInstrumentationCallbacks
&PIC
) {
2398 if ((!PrintOnCrash
&& PrintOnCrashPath
.empty()) || CrashReporter
)
2401 sys::AddSignalHandler(SignalHandler
, nullptr);
2402 CrashReporter
= this;
2404 PIC
.registerBeforeNonSkippedPassCallback(
2405 [&PIC
, this](StringRef PassID
, Any IR
) {
2407 raw_string_ostream
OS(SavedIR
);
2408 OS
<< formatv("*** Dump of {0}IR Before Last Pass {1}",
2409 llvm::forcePrintModuleIR() ? "Module " : "", PassID
);
2410 if (!isInteresting(IR
, PassID
, PIC
.getPassNameForClassName(PassID
))) {
2411 OS
<< " Filtered Out ***\n";
2414 OS
<< " Started ***\n";
2415 unwrapAndPrint(OS
, IR
);
2419 void StandardInstrumentations::registerCallbacks(
2420 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
*MAM
) {
2421 PrintIR
.registerCallbacks(PIC
);
2422 PrintPass
.registerCallbacks(PIC
);
2423 TimePasses
.registerCallbacks(PIC
);
2424 OptNone
.registerCallbacks(PIC
);
2425 OptPassGate
.registerCallbacks(PIC
);
2426 PrintChangedIR
.registerCallbacks(PIC
);
2427 PseudoProbeVerification
.registerCallbacks(PIC
);
2429 Verify
.registerCallbacks(PIC
);
2430 PrintChangedDiff
.registerCallbacks(PIC
);
2431 WebsiteChangeReporter
.registerCallbacks(PIC
);
2432 ChangeTester
.registerCallbacks(PIC
);
2433 PrintCrashIR
.registerCallbacks(PIC
);
2435 PreservedCFGChecker
.registerCallbacks(PIC
, *MAM
);
2437 // TimeProfiling records the pass running time cost.
2438 // Its 'BeforePassCallback' can be appended at the tail of all the
2439 // BeforeCallbacks by calling `registerCallbacks` in the end.
2440 // Its 'AfterPassCallback' is put at the front of all the
2441 // AfterCallbacks by its `registerCallbacks`. This is necessary
2442 // to ensure that other callbacks are not included in the timings.
2443 TimeProfilingPasses
.registerCallbacks(PIC
);
2446 template class ChangeReporter
<std::string
>;
2447 template class TextChangeReporter
<std::string
>;
2449 template class BlockDataT
<EmptyData
>;
2450 template class FuncDataT
<EmptyData
>;
2451 template class IRDataT
<EmptyData
>;
2452 template class ChangeReporter
<IRDataT
<EmptyData
>>;
2453 template class TextChangeReporter
<IRDataT
<EmptyData
>>;
2454 template class IRComparer
<EmptyData
>;