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>
122 PrintAtPassNumber("print-at-pass-number", cl::init(0), cl::Hidden
,
123 cl::desc("Print IR at pass with this number as "
124 "reported by print-passes-names"));
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 (shouldPrintPassNumbers() || shouldPrintAtPassNumber() ||
810 shouldPrintAfterPass(PassID
))
811 pushPassRunDescriptor(PassID
, IR
, DumpIRFilename
);
813 if (!shouldPrintIR(IR
))
818 if (shouldPrintPassNumbers())
819 dbgs() << " Running pass " << CurrentPassNumber
<< " " << PassID
820 << " on " << getIRName(IR
) << "\n";
822 if (!shouldPrintBeforePass(PassID
))
825 auto WriteIRToStream
= [&](raw_ostream
&Stream
) {
826 Stream
<< "; *** IR Dump Before " << PassID
<< " on " << getIRName(IR
)
828 unwrapAndPrint(Stream
, IR
);
831 if (!DumpIRFilename
.empty()) {
832 DumpIRFilename
+= getFileSuffix(IRDumpFileSuffixType::Before
);
833 llvm::raw_fd_ostream DumpIRFileStream
{
834 prepareDumpIRFileDescriptor(DumpIRFilename
), /* shouldClose */ true};
835 WriteIRToStream(DumpIRFileStream
);
837 WriteIRToStream(dbgs());
841 void PrintIRInstrumentation::printAfterPass(StringRef PassID
, Any IR
) {
842 if (isIgnored(PassID
))
845 if (!shouldPrintAfterPass(PassID
) && !shouldPrintPassNumbers() &&
846 !shouldPrintAtPassNumber())
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 "
857 << (shouldPrintAtPassNumber()
858 ? StringRef(formatv("At {0}-{1}", CurrentPassNumber
, PassID
))
859 : StringRef(formatv("After {0}", PassID
)))
860 << " on " << IRName
<< " ***\n";
861 unwrapAndPrint(Stream
, IR
);
864 if (!IRDumpDirectory
.empty()) {
865 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
866 "should be set in printBeforePass");
867 const std::string DumpIRFilenameWithSuffix
=
868 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::After
).str();
869 llvm::raw_fd_ostream DumpIRFileStream
{
870 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
871 /* shouldClose */ true};
872 WriteIRToStream(DumpIRFileStream
, IRName
);
874 WriteIRToStream(dbgs(), IRName
);
878 void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID
) {
879 if (isIgnored(PassID
))
882 if (!shouldPrintAfterPass(PassID
) && !shouldPrintPassNumbers() &&
883 !shouldPrintAtPassNumber())
886 auto [M
, DumpIRFilename
, IRName
, StoredPassID
] = popPassRunDescriptor(PassID
);
887 assert(StoredPassID
== PassID
&& "mismatched PassID");
888 // Additional filtering (e.g. -filter-print-func) can lead to module
889 // printing being skipped.
890 if (!M
|| !shouldPrintAfterPass(PassID
))
893 auto WriteIRToStream
= [&](raw_ostream
&Stream
, const Module
*M
,
894 const StringRef IRName
) {
895 SmallString
<20> Banner
;
896 if (shouldPrintAtPassNumber())
897 Banner
= formatv("; *** IR Dump At {0}-{1} on {2} (invalidated) ***",
898 CurrentPassNumber
, PassID
, IRName
);
900 Banner
= formatv("; *** IR Dump After {0} on {1} (invalidated) ***",
902 Stream
<< Banner
<< "\n";
906 if (!IRDumpDirectory
.empty()) {
907 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
908 "should be set in printBeforePass");
909 const std::string DumpIRFilenameWithSuffix
=
910 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::Invalidated
).str();
911 llvm::raw_fd_ostream DumpIRFileStream
{
912 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
913 /* shouldClose */ true};
914 WriteIRToStream(DumpIRFileStream
, M
, IRName
);
916 WriteIRToStream(dbgs(), M
, IRName
);
920 bool PrintIRInstrumentation::shouldPrintBeforePass(StringRef PassID
) {
921 if (shouldPrintBeforeAll())
924 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
925 return is_contained(printBeforePasses(), PassName
);
928 bool PrintIRInstrumentation::shouldPrintAfterPass(StringRef PassID
) {
929 if (shouldPrintAfterAll())
932 if (shouldPrintAtPassNumber() && CurrentPassNumber
== PrintAtPassNumber
)
935 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
936 return is_contained(printAfterPasses(), PassName
);
939 bool PrintIRInstrumentation::shouldPrintPassNumbers() {
940 return PrintPassNumbers
;
943 bool PrintIRInstrumentation::shouldPrintAtPassNumber() {
944 return PrintAtPassNumber
> 0;
947 void PrintIRInstrumentation::registerCallbacks(
948 PassInstrumentationCallbacks
&PIC
) {
951 // BeforePass callback is not just for printing, it also saves a Module
952 // for later use in AfterPassInvalidated.
953 if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() ||
954 shouldPrintBeforeSomePass() || shouldPrintAfterSomePass())
955 PIC
.registerBeforeNonSkippedPassCallback(
956 [this](StringRef P
, Any IR
) { this->printBeforePass(P
, IR
); });
958 if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() ||
959 shouldPrintAfterSomePass()) {
960 PIC
.registerAfterPassCallback(
961 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
962 this->printAfterPass(P
, IR
);
964 PIC
.registerAfterPassInvalidatedCallback(
965 [this](StringRef P
, const PreservedAnalyses
&) {
966 this->printAfterPassInvalidated(P
);
971 void OptNoneInstrumentation::registerCallbacks(
972 PassInstrumentationCallbacks
&PIC
) {
973 PIC
.registerShouldRunOptionalPassCallback(
974 [this](StringRef P
, Any IR
) { return this->shouldRun(P
, IR
); });
977 bool OptNoneInstrumentation::shouldRun(StringRef PassID
, Any IR
) {
978 const auto *F
= unwrapIR
<Function
>(IR
);
980 if (const auto *L
= unwrapIR
<Loop
>(IR
))
981 F
= L
->getHeader()->getParent();
983 bool ShouldRun
= !(F
&& F
->hasOptNone());
984 if (!ShouldRun
&& DebugLogging
) {
985 errs() << "Skipping pass " << PassID
<< " on " << F
->getName()
986 << " due to optnone attribute\n";
991 bool OptPassGateInstrumentation::shouldRun(StringRef PassName
, Any IR
) {
992 if (isIgnored(PassName
))
996 Context
.getOptPassGate().shouldRunPass(PassName
, getIRName(IR
));
997 if (!ShouldRun
&& !this->HasWrittenIR
&& !OptBisectPrintIRPath
.empty()) {
998 // FIXME: print IR if limit is higher than number of opt-bisect
1000 this->HasWrittenIR
= true;
1001 const Module
*M
= unwrapModule(IR
, /*Force=*/true);
1002 assert((M
&& &M
->getContext() == &Context
) && "Missing/Mismatching Module");
1004 raw_fd_ostream
OS(OptBisectPrintIRPath
, EC
);
1006 report_fatal_error(errorCodeToError(EC
));
1007 M
->print(OS
, nullptr);
1012 void OptPassGateInstrumentation::registerCallbacks(
1013 PassInstrumentationCallbacks
&PIC
) {
1014 OptPassGate
&PassGate
= Context
.getOptPassGate();
1015 if (!PassGate
.isEnabled())
1018 PIC
.registerShouldRunOptionalPassCallback([this](StringRef PassName
, Any IR
) {
1019 return this->shouldRun(PassName
, IR
);
1023 raw_ostream
&PrintPassInstrumentation::print() {
1025 assert(Indent
>= 0);
1026 dbgs().indent(Indent
);
1031 void PrintPassInstrumentation::registerCallbacks(
1032 PassInstrumentationCallbacks
&PIC
) {
1036 std::vector
<StringRef
> SpecialPasses
;
1037 if (!Opts
.Verbose
) {
1038 SpecialPasses
.emplace_back("PassManager");
1039 SpecialPasses
.emplace_back("PassAdaptor");
1042 PIC
.registerBeforeSkippedPassCallback([this, SpecialPasses
](StringRef PassID
,
1044 assert(!isSpecialPass(PassID
, SpecialPasses
) &&
1045 "Unexpectedly skipping special pass");
1047 print() << "Skipping pass: " << PassID
<< " on " << getIRName(IR
) << "\n";
1049 PIC
.registerBeforeNonSkippedPassCallback([this, SpecialPasses
](
1050 StringRef PassID
, Any IR
) {
1051 if (isSpecialPass(PassID
, SpecialPasses
))
1055 OS
<< "Running pass: " << PassID
<< " on " << getIRName(IR
);
1056 if (const auto *F
= unwrapIR
<Function
>(IR
)) {
1057 unsigned Count
= F
->getInstructionCount();
1058 OS
<< " (" << Count
<< " instruction";
1062 } else if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
)) {
1063 int Count
= C
->size();
1064 OS
<< " (" << Count
<< " node";
1072 PIC
.registerAfterPassCallback(
1073 [this, SpecialPasses
](StringRef PassID
, Any IR
,
1074 const PreservedAnalyses
&) {
1075 if (isSpecialPass(PassID
, SpecialPasses
))
1080 PIC
.registerAfterPassInvalidatedCallback(
1081 [this, SpecialPasses
](StringRef PassID
, Any IR
) {
1082 if (isSpecialPass(PassID
, SpecialPasses
))
1088 if (!Opts
.SkipAnalyses
) {
1089 PIC
.registerBeforeAnalysisCallback([this](StringRef PassID
, Any IR
) {
1090 print() << "Running analysis: " << PassID
<< " on " << getIRName(IR
)
1094 PIC
.registerAfterAnalysisCallback(
1095 [this](StringRef PassID
, Any IR
) { Indent
-= 2; });
1096 PIC
.registerAnalysisInvalidatedCallback([this](StringRef PassID
, Any IR
) {
1097 print() << "Invalidating analysis: " << PassID
<< " on " << getIRName(IR
)
1100 PIC
.registerAnalysesClearedCallback([this](StringRef IRName
) {
1101 print() << "Clearing all analysis results for: " << IRName
<< "\n";
1106 PreservedCFGCheckerInstrumentation::CFG::CFG(const Function
*F
,
1107 bool TrackBBLifetime
) {
1108 if (TrackBBLifetime
)
1109 BBGuards
= DenseMap
<intptr_t, BBGuard
>(F
->size());
1110 for (const auto &BB
: *F
) {
1112 BBGuards
->try_emplace(intptr_t(&BB
), &BB
);
1113 for (const auto *Succ
: successors(&BB
)) {
1116 BBGuards
->try_emplace(intptr_t(Succ
), Succ
);
1121 static void printBBName(raw_ostream
&out
, const BasicBlock
*BB
) {
1122 if (BB
->hasName()) {
1123 out
<< BB
->getName() << "<" << BB
<< ">";
1127 if (!BB
->getParent()) {
1128 out
<< "unnamed_removed<" << BB
<< ">";
1132 if (BB
->isEntryBlock()) {
1134 << "<" << BB
<< ">";
1138 unsigned FuncOrderBlockNum
= 0;
1139 for (auto &FuncBB
: *BB
->getParent()) {
1142 FuncOrderBlockNum
++;
1144 out
<< "unnamed_" << FuncOrderBlockNum
<< "<" << BB
<< ">";
1147 void PreservedCFGCheckerInstrumentation::CFG::printDiff(raw_ostream
&out
,
1150 assert(!After
.isPoisoned());
1151 if (Before
.isPoisoned()) {
1152 out
<< "Some blocks were deleted\n";
1156 // Find and print graph differences.
1157 if (Before
.Graph
.size() != After
.Graph
.size())
1158 out
<< "Different number of non-leaf basic blocks: before="
1159 << Before
.Graph
.size() << ", after=" << After
.Graph
.size() << "\n";
1161 for (auto &BB
: Before
.Graph
) {
1162 auto BA
= After
.Graph
.find(BB
.first
);
1163 if (BA
== After
.Graph
.end()) {
1164 out
<< "Non-leaf block ";
1165 printBBName(out
, BB
.first
);
1166 out
<< " is removed (" << BB
.second
.size() << " successors)\n";
1170 for (auto &BA
: After
.Graph
) {
1171 auto BB
= Before
.Graph
.find(BA
.first
);
1172 if (BB
== Before
.Graph
.end()) {
1173 out
<< "Non-leaf block ";
1174 printBBName(out
, BA
.first
);
1175 out
<< " is added (" << BA
.second
.size() << " successors)\n";
1179 if (BB
->second
== BA
.second
)
1182 out
<< "Different successors of block ";
1183 printBBName(out
, BA
.first
);
1184 out
<< " (unordered):\n";
1185 out
<< "- before (" << BB
->second
.size() << "): ";
1186 for (auto &SuccB
: BB
->second
) {
1187 printBBName(out
, SuccB
.first
);
1188 if (SuccB
.second
!= 1)
1189 out
<< "(" << SuccB
.second
<< "), ";
1194 out
<< "- after (" << BA
.second
.size() << "): ";
1195 for (auto &SuccA
: BA
.second
) {
1196 printBBName(out
, SuccA
.first
);
1197 if (SuccA
.second
!= 1)
1198 out
<< "(" << SuccA
.second
<< "), ";
1206 // PreservedCFGCheckerInstrumentation uses PreservedCFGCheckerAnalysis to check
1207 // passes, that reported they kept CFG analyses up-to-date, did not actually
1208 // change CFG. This check is done as follows. Before every functional pass in
1209 // BeforeNonSkippedPassCallback a CFG snapshot (an instance of
1210 // PreservedCFGCheckerInstrumentation::CFG) is requested from
1211 // FunctionAnalysisManager as a result of PreservedCFGCheckerAnalysis. When the
1212 // functional pass finishes and reports that CFGAnalyses or AllAnalyses are
1213 // up-to-date then the cached result of PreservedCFGCheckerAnalysis (if
1214 // available) is checked to be equal to a freshly created CFG snapshot.
1215 struct PreservedCFGCheckerAnalysis
1216 : public AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
> {
1217 friend AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
>;
1219 static AnalysisKey Key
;
1222 /// Provide the result type for this analysis pass.
1223 using Result
= PreservedCFGCheckerInstrumentation::CFG
;
1225 /// Run the analysis pass over a function and produce CFG.
1226 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1227 return Result(&F
, /* TrackBBLifetime */ true);
1231 AnalysisKey
PreservedCFGCheckerAnalysis::Key
;
1233 struct PreservedFunctionHashAnalysis
1234 : public AnalysisInfoMixin
<PreservedFunctionHashAnalysis
> {
1235 static AnalysisKey Key
;
1237 struct FunctionHash
{
1241 using Result
= FunctionHash
;
1243 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1244 return Result
{StructuralHash(F
)};
1248 AnalysisKey
PreservedFunctionHashAnalysis::Key
;
1250 struct PreservedModuleHashAnalysis
1251 : public AnalysisInfoMixin
<PreservedModuleHashAnalysis
> {
1252 static AnalysisKey Key
;
1258 using Result
= ModuleHash
;
1260 Result
run(Module
&F
, ModuleAnalysisManager
&FAM
) {
1261 return Result
{StructuralHash(F
)};
1265 AnalysisKey
PreservedModuleHashAnalysis::Key
;
1267 bool PreservedCFGCheckerInstrumentation::CFG::invalidate(
1268 Function
&F
, const PreservedAnalyses
&PA
,
1269 FunctionAnalysisManager::Invalidator
&) {
1270 auto PAC
= PA
.getChecker
<PreservedCFGCheckerAnalysis
>();
1271 return !(PAC
.preserved() || PAC
.preservedSet
<AllAnalysesOn
<Function
>>() ||
1272 PAC
.preservedSet
<CFGAnalyses
>());
1275 static SmallVector
<Function
*, 1> GetFunctions(Any IR
) {
1276 SmallVector
<Function
*, 1> Functions
;
1278 if (const auto *MaybeF
= unwrapIR
<Function
>(IR
)) {
1279 Functions
.push_back(const_cast<Function
*>(MaybeF
));
1280 } else if (const auto *MaybeM
= unwrapIR
<Module
>(IR
)) {
1281 for (Function
&F
: *const_cast<Module
*>(MaybeM
))
1282 Functions
.push_back(&F
);
1287 void PreservedCFGCheckerInstrumentation::registerCallbacks(
1288 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
&MAM
) {
1289 if (!VerifyAnalysisInvalidation
)
1292 bool Registered
= false;
1293 PIC
.registerBeforeNonSkippedPassCallback([this, &MAM
, Registered
](
1294 StringRef P
, Any IR
) mutable {
1295 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1296 assert(&PassStack
.emplace_back(P
));
1300 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1301 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1304 FAM
.registerPass([&] { return PreservedCFGCheckerAnalysis(); });
1305 FAM
.registerPass([&] { return PreservedFunctionHashAnalysis(); });
1306 MAM
.registerPass([&] { return PreservedModuleHashAnalysis(); });
1310 for (Function
*F
: GetFunctions(IR
)) {
1311 // Make sure a fresh CFG snapshot is available before the pass.
1312 FAM
.getResult
<PreservedCFGCheckerAnalysis
>(*F
);
1313 FAM
.getResult
<PreservedFunctionHashAnalysis
>(*F
);
1316 if (const auto *MPtr
= unwrapIR
<Module
>(IR
)) {
1317 auto &M
= *const_cast<Module
*>(MPtr
);
1318 MAM
.getResult
<PreservedModuleHashAnalysis
>(M
);
1322 PIC
.registerAfterPassInvalidatedCallback(
1323 [this](StringRef P
, const PreservedAnalyses
&PassPA
) {
1324 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1325 assert(PassStack
.pop_back_val() == P
&&
1326 "Before and After callbacks must correspond");
1331 PIC
.registerAfterPassCallback([this, &MAM
](StringRef P
, Any IR
,
1332 const PreservedAnalyses
&PassPA
) {
1333 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1334 assert(PassStack
.pop_back_val() == P
&&
1335 "Before and After callbacks must correspond");
1339 // We have to get the FAM via the MAM, rather than directly use a passed in
1340 // FAM because if MAM has not cached the FAM, it won't invalidate function
1342 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1343 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1346 for (Function
*F
: GetFunctions(IR
)) {
1347 if (auto *HashBefore
=
1348 FAM
.getCachedResult
<PreservedFunctionHashAnalysis
>(*F
)) {
1349 if (HashBefore
->Hash
!= StructuralHash(*F
)) {
1350 report_fatal_error(formatv(
1351 "Function @{0} changed by {1} without invalidating analyses",
1356 auto CheckCFG
= [](StringRef Pass
, StringRef FuncName
,
1357 const CFG
&GraphBefore
, const CFG
&GraphAfter
) {
1358 if (GraphAfter
== GraphBefore
)
1362 << "Error: " << Pass
1363 << " does not invalidate CFG analyses but CFG changes detected in "
1365 << FuncName
<< ":\n";
1366 CFG::printDiff(dbgs(), GraphBefore
, GraphAfter
);
1367 report_fatal_error(Twine("CFG unexpectedly changed by ", Pass
));
1370 if (auto *GraphBefore
=
1371 FAM
.getCachedResult
<PreservedCFGCheckerAnalysis
>(*F
))
1372 CheckCFG(P
, F
->getName(), *GraphBefore
,
1373 CFG(F
, /* TrackBBLifetime */ false));
1375 if (const auto *MPtr
= unwrapIR
<Module
>(IR
)) {
1376 auto &M
= *const_cast<Module
*>(MPtr
);
1377 if (auto *HashBefore
=
1378 MAM
.getCachedResult
<PreservedModuleHashAnalysis
>(M
)) {
1379 if (HashBefore
->Hash
!= StructuralHash(M
)) {
1380 report_fatal_error(formatv(
1381 "Module changed by {0} without invalidating analyses", P
));
1388 void VerifyInstrumentation::registerCallbacks(
1389 PassInstrumentationCallbacks
&PIC
) {
1390 PIC
.registerAfterPassCallback(
1391 [this](StringRef P
, Any IR
, const PreservedAnalyses
&PassPA
) {
1392 if (isIgnored(P
) || P
== "VerifierPass")
1394 const auto *F
= unwrapIR
<Function
>(IR
);
1396 if (const auto *L
= unwrapIR
<Loop
>(IR
))
1397 F
= L
->getHeader()->getParent();
1402 dbgs() << "Verifying function " << F
->getName() << "\n";
1404 if (verifyFunction(*F
, &errs()))
1405 report_fatal_error(formatv("Broken function found after pass "
1406 "\"{0}\", compilation aborted!",
1409 const auto *M
= unwrapIR
<Module
>(IR
);
1411 if (const auto *C
= unwrapIR
<LazyCallGraph::SCC
>(IR
))
1412 M
= C
->begin()->getFunction().getParent();
1417 dbgs() << "Verifying module " << M
->getName() << "\n";
1419 if (verifyModule(*M
, &errs()))
1420 report_fatal_error(formatv("Broken module found after pass "
1421 "\"{0}\", compilation aborted!",
1428 InLineChangePrinter::~InLineChangePrinter() = default;
1430 void InLineChangePrinter::generateIRRepresentation(Any IR
,
1432 IRDataT
<EmptyData
> &D
) {
1433 IRComparer
<EmptyData
>::analyzeIR(IR
, D
);
1436 void InLineChangePrinter::handleAfter(StringRef PassID
, std::string
&Name
,
1437 const IRDataT
<EmptyData
> &Before
,
1438 const IRDataT
<EmptyData
> &After
,
1440 SmallString
<20> Banner
=
1441 formatv("*** IR Dump After {0} on {1} ***\n", PassID
, Name
);
1443 IRComparer
<EmptyData
>(Before
, After
)
1444 .compare(getModuleForComparison(IR
),
1445 [&](bool InModule
, unsigned Minor
,
1446 const FuncDataT
<EmptyData
> &Before
,
1447 const FuncDataT
<EmptyData
> &After
) -> void {
1448 handleFunctionCompare(Name
, "", PassID
, " on ", InModule
,
1449 Minor
, Before
, After
);
1454 void InLineChangePrinter::handleFunctionCompare(
1455 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
1456 bool InModule
, unsigned Minor
, const FuncDataT
<EmptyData
> &Before
,
1457 const FuncDataT
<EmptyData
> &After
) {
1458 // Print a banner when this is being shown in the context of a module
1460 Out
<< "\n*** IR for function " << Name
<< " ***\n";
1462 FuncDataT
<EmptyData
>::report(
1464 [&](const BlockDataT
<EmptyData
> *B
, const BlockDataT
<EmptyData
> *A
) {
1465 StringRef BStr
= B
? B
->getBody() : "\n";
1466 StringRef AStr
= A
? A
->getBody() : "\n";
1467 const std::string Removed
=
1468 UseColour
? "\033[31m-%l\033[0m\n" : "-%l\n";
1469 const std::string Added
= UseColour
? "\033[32m+%l\033[0m\n" : "+%l\n";
1470 const std::string NoChange
= " %l\n";
1471 Out
<< doSystemDiff(BStr
, AStr
, Removed
, Added
, NoChange
);
1475 void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
1476 if (PrintChanged
== ChangePrinter::DiffVerbose
||
1477 PrintChanged
== ChangePrinter::DiffQuiet
||
1478 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
1479 PrintChanged
== ChangePrinter::ColourDiffQuiet
)
1480 TextChangeReporter
<IRDataT
<EmptyData
>>::registerRequiredCallbacks(PIC
);
1483 TimeProfilingPassesHandler::TimeProfilingPassesHandler() {}
1485 void TimeProfilingPassesHandler::registerCallbacks(
1486 PassInstrumentationCallbacks
&PIC
) {
1487 if (!getTimeTraceProfilerInstance())
1489 PIC
.registerBeforeNonSkippedPassCallback(
1490 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1491 PIC
.registerAfterPassCallback(
1492 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
1493 this->runAfterPass();
1496 PIC
.registerAfterPassInvalidatedCallback(
1497 [this](StringRef P
, const PreservedAnalyses
&) { this->runAfterPass(); },
1499 PIC
.registerBeforeAnalysisCallback(
1500 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1501 PIC
.registerAfterAnalysisCallback(
1502 [this](StringRef P
, Any IR
) { this->runAfterPass(); }, true);
1505 void TimeProfilingPassesHandler::runBeforePass(StringRef PassID
, Any IR
) {
1506 timeTraceProfilerBegin(PassID
, getIRName(IR
));
1509 void TimeProfilingPassesHandler::runAfterPass() { timeTraceProfilerEnd(); }
1514 class DotCfgDiffDisplayGraph
;
1516 // Base class for a node or edge in the dot-cfg-changes graph.
1517 class DisplayElement
{
1519 // Is this in before, after, or both?
1520 StringRef
getColour() const { return Colour
; }
1523 DisplayElement(StringRef Colour
) : Colour(Colour
) {}
1524 const StringRef Colour
;
1527 // An edge representing a transition between basic blocks in the
1528 // dot-cfg-changes graph.
1529 class DisplayEdge
: public DisplayElement
{
1531 DisplayEdge(std::string Value
, DisplayNode
&Node
, StringRef Colour
)
1532 : DisplayElement(Colour
), Value(Value
), Node(Node
) {}
1533 // The value on which the transition is made.
1534 std::string
getValue() const { return Value
; }
1535 // The node (representing a basic block) reached by this transition.
1536 const DisplayNode
&getDestinationNode() const { return Node
; }
1540 const DisplayNode
&Node
;
1543 // A node in the dot-cfg-changes graph which represents a basic block.
1544 class DisplayNode
: public DisplayElement
{
1546 // \p C is the content for the node, \p T indicates the colour for the
1547 // outline of the node
1548 DisplayNode(std::string Content
, StringRef Colour
)
1549 : DisplayElement(Colour
), Content(Content
) {}
1551 // Iterator to the child nodes. Required by GraphWriter.
1552 using ChildIterator
= std::unordered_set
<DisplayNode
*>::const_iterator
;
1553 ChildIterator
children_begin() const { return Children
.cbegin(); }
1554 ChildIterator
children_end() const { return Children
.cend(); }
1556 // Iterator for the edges. Required by GraphWriter.
1557 using EdgeIterator
= std::vector
<DisplayEdge
*>::const_iterator
;
1558 EdgeIterator
edges_begin() const { return EdgePtrs
.cbegin(); }
1559 EdgeIterator
edges_end() const { return EdgePtrs
.cend(); }
1561 // Create an edge to \p Node on value \p Value, with colour \p Colour.
1562 void createEdge(StringRef Value
, DisplayNode
&Node
, StringRef Colour
);
1564 // Return the content of this node.
1565 std::string
getContent() const { return Content
; }
1567 // Return the edge to node \p S.
1568 const DisplayEdge
&getEdge(const DisplayNode
&To
) const {
1569 assert(EdgeMap
.find(&To
) != EdgeMap
.end() && "Expected to find edge.");
1570 return *EdgeMap
.find(&To
)->second
;
1573 // Return the value for the transition to basic block \p S.
1574 // Required by GraphWriter.
1575 std::string
getEdgeSourceLabel(const DisplayNode
&Sink
) const {
1576 return getEdge(Sink
).getValue();
1579 void createEdgeMap();
1582 const std::string Content
;
1584 // Place to collect all of the edges. Once they are all in the vector,
1585 // the vector will not reallocate so then we can use pointers to them,
1586 // which are required by the graph writing routines.
1587 std::vector
<DisplayEdge
> Edges
;
1589 std::vector
<DisplayEdge
*> EdgePtrs
;
1590 std::unordered_set
<DisplayNode
*> Children
;
1591 std::unordered_map
<const DisplayNode
*, const DisplayEdge
*> EdgeMap
;
1593 // Safeguard adding of edges.
1594 bool AllEdgesCreated
= false;
1597 // Class representing a difference display (corresponds to a pdf file).
1598 class DotCfgDiffDisplayGraph
{
1600 DotCfgDiffDisplayGraph(std::string Name
) : GraphName(Name
) {}
1602 // Generate the file into \p DotFile.
1603 void generateDotFile(StringRef DotFile
);
1605 // Iterator to the nodes. Required by GraphWriter.
1606 using NodeIterator
= std::vector
<DisplayNode
*>::const_iterator
;
1607 NodeIterator
nodes_begin() const {
1608 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1609 return NodePtrs
.cbegin();
1611 NodeIterator
nodes_end() const {
1612 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1613 return NodePtrs
.cend();
1616 // Record the index of the entry node. At this point, we can build up
1617 // vectors of pointers that are required by the graph routines.
1618 void setEntryNode(unsigned N
) {
1619 // At this point, there will be no new nodes.
1620 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1621 NodeGenerationComplete
= true;
1622 for (auto &N
: Nodes
)
1623 NodePtrs
.emplace_back(&N
);
1625 EntryNode
= NodePtrs
[N
];
1629 void createNode(std::string C
, StringRef Colour
) {
1630 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1631 Nodes
.emplace_back(C
, Colour
);
1633 // Return the node at index \p N to avoid problems with vectors reallocating.
1634 DisplayNode
&getNode(unsigned N
) {
1635 assert(N
< Nodes
.size() && "Node is out of bounds");
1638 unsigned size() const {
1639 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1640 return Nodes
.size();
1643 // Return the name of the graph. Required by GraphWriter.
1644 std::string
getGraphName() const { return GraphName
; }
1646 // Return the string representing the differences for basic block \p Node.
1647 // Required by GraphWriter.
1648 std::string
getNodeLabel(const DisplayNode
&Node
) const {
1649 return Node
.getContent();
1652 // Return a string with colour information for Dot. Required by GraphWriter.
1653 std::string
getNodeAttributes(const DisplayNode
&Node
) const {
1654 return attribute(Node
.getColour());
1657 // Return a string with colour information for Dot. Required by GraphWriter.
1658 std::string
getEdgeColorAttr(const DisplayNode
&From
,
1659 const DisplayNode
&To
) const {
1660 return attribute(From
.getEdge(To
).getColour());
1663 // Get the starting basic block. Required by GraphWriter.
1664 DisplayNode
*getEntryNode() const {
1665 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1670 // Return the string containing the colour to use as a Dot attribute.
1671 std::string
attribute(StringRef Colour
) const {
1672 return "color=" + Colour
.str();
1675 bool NodeGenerationComplete
= false;
1676 const std::string GraphName
;
1677 std::vector
<DisplayNode
> Nodes
;
1678 std::vector
<DisplayNode
*> NodePtrs
;
1679 DisplayNode
*EntryNode
= nullptr;
1682 void DisplayNode::createEdge(StringRef Value
, DisplayNode
&Node
,
1684 assert(!AllEdgesCreated
&& "Expected to be able to still create edges.");
1685 Edges
.emplace_back(Value
.str(), Node
, Colour
);
1686 Children
.insert(&Node
);
1689 void DisplayNode::createEdgeMap() {
1690 // No more edges will be added so we can now use pointers to the edges
1691 // as the vector will not grow and reallocate.
1692 AllEdgesCreated
= true;
1693 for (auto &E
: Edges
)
1694 EdgeMap
.insert({&E
.getDestinationNode(), &E
});
1697 class DotCfgDiffNode
;
1700 // A class representing a basic block in the Dot difference graph.
1701 class DotCfgDiffNode
{
1703 DotCfgDiffNode() = delete;
1705 // Create a node in Dot difference graph \p G representing the basic block
1706 // represented by \p BD with colour \p Colour (where it exists).
1707 DotCfgDiffNode(DotCfgDiff
&G
, unsigned N
, const BlockDataT
<DCData
> &BD
,
1709 : Graph(G
), N(N
), Data
{&BD
, nullptr}, Colour(Colour
) {}
1710 DotCfgDiffNode(const DotCfgDiffNode
&DN
)
1711 : Graph(DN
.Graph
), N(DN
.N
), Data
{DN
.Data
[0], DN
.Data
[1]},
1712 Colour(DN
.Colour
), EdgesMap(DN
.EdgesMap
), Children(DN
.Children
),
1715 unsigned getIndex() const { return N
; }
1717 // The label of the basic block
1718 StringRef
getLabel() const {
1719 assert(Data
[0] && "Expected Data[0] to be set.");
1720 return Data
[0]->getLabel();
1722 // Return the colour for this block
1723 StringRef
getColour() const { return Colour
; }
1724 // Change this basic block from being only in before to being common.
1725 // Save the pointer to \p Other.
1726 void setCommon(const BlockDataT
<DCData
> &Other
) {
1727 assert(!Data
[1] && "Expected only one block datum");
1729 Colour
= CommonColour
;
1731 // Add an edge to \p E of colour {\p Value, \p Colour}.
1732 void addEdge(unsigned E
, StringRef Value
, StringRef Colour
) {
1733 // This is a new edge or it is an edge being made common.
1734 assert((EdgesMap
.count(E
) == 0 || Colour
== CommonColour
) &&
1735 "Unexpected edge count and color.");
1736 EdgesMap
[E
] = {Value
.str(), Colour
};
1738 // Record the children and create edges.
1739 void finalize(DotCfgDiff
&G
);
1741 // Return the colour of the edge to node \p S.
1742 StringRef
getEdgeColour(const unsigned S
) const {
1743 assert(EdgesMap
.count(S
) == 1 && "Expected to find edge.");
1744 return EdgesMap
.at(S
).second
;
1747 // Return the string representing the basic block.
1748 std::string
getBodyContent() const;
1750 void createDisplayEdges(DotCfgDiffDisplayGraph
&Graph
, unsigned DisplayNode
,
1751 std::map
<const unsigned, unsigned> &NodeMap
) const;
1756 const BlockDataT
<DCData
> *Data
[2];
1758 std::map
<const unsigned, std::pair
<std::string
, StringRef
>> EdgesMap
;
1759 std::vector
<unsigned> Children
;
1760 std::vector
<unsigned> Edges
;
1763 // Class representing the difference graph between two functions.
1766 // \p Title is the title given to the graph. \p EntryNodeName is the
1767 // entry node for the function. \p Before and \p After are the before
1768 // after versions of the function, respectively. \p Dir is the directory
1769 // in which to store the results.
1770 DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1771 const FuncDataT
<DCData
> &After
);
1773 DotCfgDiff(const DotCfgDiff
&) = delete;
1774 DotCfgDiff
&operator=(const DotCfgDiff
&) = delete;
1776 DotCfgDiffDisplayGraph
createDisplayGraph(StringRef Title
,
1777 StringRef EntryNodeName
);
1779 // Return a string consisting of the labels for the \p Source and \p Sink.
1780 // The combination allows distinguishing changing transitions on the
1781 // same value (ie, a transition went to X before and goes to Y after).
1782 // Required by GraphWriter.
1783 StringRef
getEdgeSourceLabel(const unsigned &Source
,
1784 const unsigned &Sink
) const {
1786 getNode(Source
).getLabel().str() + " " + getNode(Sink
).getLabel().str();
1787 assert(EdgeLabels
.count(S
) == 1 && "Expected to find edge label.");
1788 return EdgeLabels
.find(S
)->getValue();
1791 // Return the number of basic blocks (nodes). Required by GraphWriter.
1792 unsigned size() const { return Nodes
.size(); }
1794 const DotCfgDiffNode
&getNode(unsigned N
) const {
1795 assert(N
< Nodes
.size() && "Unexpected index for node reference");
1800 // Return the string surrounded by HTML to make it the appropriate colour.
1801 std::string
colourize(std::string S
, StringRef Colour
) const;
1803 void createNode(StringRef Label
, const BlockDataT
<DCData
> &BD
, StringRef C
) {
1804 unsigned Pos
= Nodes
.size();
1805 Nodes
.emplace_back(*this, Pos
, BD
, C
);
1806 NodePosition
.insert({Label
, Pos
});
1809 // TODO Nodes should probably be a StringMap<DotCfgDiffNode> after the
1810 // display graph is separated out, which would remove the need for
1812 std::vector
<DotCfgDiffNode
> Nodes
;
1813 StringMap
<unsigned> NodePosition
;
1814 const std::string GraphName
;
1816 StringMap
<std::string
> EdgeLabels
;
1819 std::string
DotCfgDiffNode::getBodyContent() const {
1820 if (Colour
== CommonColour
) {
1821 assert(Data
[1] && "Expected Data[1] to be set.");
1824 for (unsigned I
= 0; I
< 2; ++I
) {
1825 SR
[I
] = Data
[I
]->getBody();
1826 // drop initial '\n' if present
1827 if (SR
[I
][0] == '\n')
1828 SR
[I
] = SR
[I
].drop_front();
1829 // drop predecessors as they can be big and are redundant
1830 SR
[I
] = SR
[I
].drop_until([](char C
) { return C
== '\n'; }).drop_front();
1833 SmallString
<80> OldLineFormat
= formatv(
1834 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", BeforeColour
);
1835 SmallString
<80> NewLineFormat
= formatv(
1836 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", AfterColour
);
1837 SmallString
<80> UnchangedLineFormat
= formatv(
1838 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", CommonColour
);
1839 std::string Diff
= Data
[0]->getLabel().str();
1840 Diff
+= ":\n<BR align=\"left\"/>" +
1841 doSystemDiff(makeHTMLReady(SR
[0]), makeHTMLReady(SR
[1]),
1842 OldLineFormat
, NewLineFormat
, UnchangedLineFormat
);
1844 // Diff adds in some empty colour changes which are not valid HTML
1845 // so remove them. Colours are all lowercase alpha characters (as
1846 // listed in https://graphviz.org/pdf/dotguide.pdf).
1847 Regex
R("<FONT COLOR=\"\\w+\"></FONT>");
1850 std::string S
= R
.sub("", Diff
, &Error
);
1857 llvm_unreachable("Should not get here");
1860 // Put node out in the appropriate colour.
1861 assert(!Data
[1] && "Data[1] is set unexpectedly.");
1862 std::string Body
= makeHTMLReady(Data
[0]->getBody());
1863 const StringRef BS
= Body
;
1865 // Drop leading newline, if present.
1866 if (BS
.front() == '\n')
1867 BS1
= BS1
.drop_front(1);
1869 StringRef Label
= BS1
.take_until([](char C
) { return C
== ':'; });
1870 // drop predecessors as they can be big and are redundant
1871 BS1
= BS1
.drop_until([](char C
) { return C
== '\n'; }).drop_front();
1873 std::string S
= "<FONT COLOR=\"" + Colour
.str() + "\">" + Label
.str() + ":";
1875 // align each line to the left.
1876 while (BS1
.size()) {
1877 S
.append("<BR align=\"left\"/>");
1878 StringRef Line
= BS1
.take_until([](char C
) { return C
== '\n'; });
1879 S
.append(Line
.str());
1880 BS1
= BS1
.drop_front(Line
.size() + 1);
1882 S
.append("<BR align=\"left\"/></FONT>");
1886 std::string
DotCfgDiff::colourize(std::string S
, StringRef Colour
) const {
1887 if (S
.length() == 0)
1889 return "<FONT COLOR=\"" + Colour
.str() + "\">" + S
+ "</FONT>";
1892 DotCfgDiff::DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1893 const FuncDataT
<DCData
> &After
)
1894 : GraphName(Title
.str()) {
1895 StringMap
<StringRef
> EdgesMap
;
1897 // Handle each basic block in the before IR.
1898 for (auto &B
: Before
.getData()) {
1899 StringRef Label
= B
.getKey();
1900 const BlockDataT
<DCData
> &BD
= B
.getValue();
1901 createNode(Label
, BD
, BeforeColour
);
1903 // Create transitions with names made up of the from block label, the value
1904 // on which the transition is made and the to block label.
1905 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
1906 E
= BD
.getData().end();
1907 Sink
!= E
; ++Sink
) {
1908 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
1909 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
1910 EdgesMap
.insert({Key
, BeforeColour
});
1914 // Handle each basic block in the after IR
1915 for (auto &A
: After
.getData()) {
1916 StringRef Label
= A
.getKey();
1917 const BlockDataT
<DCData
> &BD
= A
.getValue();
1918 unsigned C
= NodePosition
.count(Label
);
1920 // This only exists in the after IR. Create the node.
1921 createNode(Label
, BD
, AfterColour
);
1923 assert(C
== 1 && "Unexpected multiple nodes.");
1924 Nodes
[NodePosition
[Label
]].setCommon(BD
);
1926 // Add in the edges between the nodes (as common or only in after).
1927 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
1928 E
= BD
.getData().end();
1929 Sink
!= E
; ++Sink
) {
1930 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
1931 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
1932 unsigned C
= EdgesMap
.count(Key
);
1934 EdgesMap
.insert({Key
, AfterColour
});
1936 EdgesMap
[Key
] = CommonColour
;
1941 // Now go through the map of edges and add them to the node.
1942 for (auto &E
: EdgesMap
) {
1943 // Extract the source, sink and value from the edge key.
1944 StringRef S
= E
.getKey();
1945 auto SP1
= S
.rsplit(' ');
1946 auto &SourceSink
= SP1
.first
;
1947 auto SP2
= SourceSink
.split(' ');
1948 StringRef Source
= SP2
.first
;
1949 StringRef Sink
= SP2
.second
;
1950 StringRef Value
= SP1
.second
;
1952 assert(NodePosition
.count(Source
) == 1 && "Expected to find node.");
1953 DotCfgDiffNode
&SourceNode
= Nodes
[NodePosition
[Source
]];
1954 assert(NodePosition
.count(Sink
) == 1 && "Expected to find node.");
1955 unsigned SinkNode
= NodePosition
[Sink
];
1956 StringRef Colour
= E
.second
;
1958 // Look for an edge from Source to Sink
1959 if (EdgeLabels
.count(SourceSink
) == 0)
1960 EdgeLabels
.insert({SourceSink
, colourize(Value
.str(), Colour
)});
1962 StringRef V
= EdgeLabels
.find(SourceSink
)->getValue();
1963 std::string NV
= colourize(V
.str() + " " + Value
.str(), Colour
);
1964 Colour
= CommonColour
;
1965 EdgeLabels
[SourceSink
] = NV
;
1967 SourceNode
.addEdge(SinkNode
, Value
, Colour
);
1969 for (auto &I
: Nodes
)
1973 DotCfgDiffDisplayGraph
DotCfgDiff::createDisplayGraph(StringRef Title
,
1974 StringRef EntryNodeName
) {
1975 assert(NodePosition
.count(EntryNodeName
) == 1 &&
1976 "Expected to find entry block in map.");
1977 unsigned Entry
= NodePosition
[EntryNodeName
];
1978 assert(Entry
< Nodes
.size() && "Expected to find entry node");
1979 DotCfgDiffDisplayGraph
G(Title
.str());
1981 std::map
<const unsigned, unsigned> NodeMap
;
1983 int EntryIndex
= -1;
1985 for (auto &I
: Nodes
) {
1986 if (I
.getIndex() == Entry
)
1988 G
.createNode(I
.getBodyContent(), I
.getColour());
1989 NodeMap
.insert({I
.getIndex(), Index
++});
1991 assert(EntryIndex
>= 0 && "Expected entry node index to be set.");
1992 G
.setEntryNode(EntryIndex
);
1994 for (auto &I
: NodeMap
) {
1995 unsigned SourceNode
= I
.first
;
1996 unsigned DisplayNode
= I
.second
;
1997 getNode(SourceNode
).createDisplayEdges(G
, DisplayNode
, NodeMap
);
2002 void DotCfgDiffNode::createDisplayEdges(
2003 DotCfgDiffDisplayGraph
&DisplayGraph
, unsigned DisplayNodeIndex
,
2004 std::map
<const unsigned, unsigned> &NodeMap
) const {
2006 DisplayNode
&SourceDisplayNode
= DisplayGraph
.getNode(DisplayNodeIndex
);
2008 for (auto I
: Edges
) {
2009 unsigned SinkNodeIndex
= I
;
2010 StringRef Colour
= getEdgeColour(SinkNodeIndex
);
2011 const DotCfgDiffNode
*SinkNode
= &Graph
.getNode(SinkNodeIndex
);
2013 StringRef Label
= Graph
.getEdgeSourceLabel(getIndex(), SinkNodeIndex
);
2014 DisplayNode
&SinkDisplayNode
= DisplayGraph
.getNode(SinkNode
->getIndex());
2015 SourceDisplayNode
.createEdge(Label
, SinkDisplayNode
, Colour
);
2017 SourceDisplayNode
.createEdgeMap();
2020 void DotCfgDiffNode::finalize(DotCfgDiff
&G
) {
2021 for (auto E
: EdgesMap
) {
2022 Children
.emplace_back(E
.first
);
2023 Edges
.emplace_back(E
.first
);
2031 template <> struct GraphTraits
<DotCfgDiffDisplayGraph
*> {
2032 using NodeRef
= const DisplayNode
*;
2033 using ChildIteratorType
= DisplayNode::ChildIterator
;
2034 using nodes_iterator
= DotCfgDiffDisplayGraph::NodeIterator
;
2035 using EdgeRef
= const DisplayEdge
*;
2036 using ChildEdgeIterator
= DisplayNode::EdgeIterator
;
2038 static NodeRef
getEntryNode(const DotCfgDiffDisplayGraph
*G
) {
2039 return G
->getEntryNode();
2041 static ChildIteratorType
child_begin(NodeRef N
) {
2042 return N
->children_begin();
2044 static ChildIteratorType
child_end(NodeRef N
) { return N
->children_end(); }
2045 static nodes_iterator
nodes_begin(const DotCfgDiffDisplayGraph
*G
) {
2046 return G
->nodes_begin();
2048 static nodes_iterator
nodes_end(const DotCfgDiffDisplayGraph
*G
) {
2049 return G
->nodes_end();
2051 static ChildEdgeIterator
child_edge_begin(NodeRef N
) {
2052 return N
->edges_begin();
2054 static ChildEdgeIterator
child_edge_end(NodeRef N
) { return N
->edges_end(); }
2055 static NodeRef
edge_dest(EdgeRef E
) { return &E
->getDestinationNode(); }
2056 static unsigned size(const DotCfgDiffDisplayGraph
*G
) { return G
->size(); }
2060 struct DOTGraphTraits
<DotCfgDiffDisplayGraph
*> : public DefaultDOTGraphTraits
{
2061 explicit DOTGraphTraits(bool Simple
= false)
2062 : DefaultDOTGraphTraits(Simple
) {}
2064 static bool renderNodesUsingHTML() { return true; }
2065 static std::string
getGraphName(const DotCfgDiffDisplayGraph
*DiffData
) {
2066 return DiffData
->getGraphName();
2069 getGraphProperties(const DotCfgDiffDisplayGraph
*DiffData
) {
2070 return "\tsize=\"190, 190\";\n";
2072 static std::string
getNodeLabel(const DisplayNode
*Node
,
2073 const DotCfgDiffDisplayGraph
*DiffData
) {
2074 return DiffData
->getNodeLabel(*Node
);
2076 static std::string
getNodeAttributes(const DisplayNode
*Node
,
2077 const DotCfgDiffDisplayGraph
*DiffData
) {
2078 return DiffData
->getNodeAttributes(*Node
);
2080 static std::string
getEdgeSourceLabel(const DisplayNode
*From
,
2081 DisplayNode::ChildIterator
&To
) {
2082 return From
->getEdgeSourceLabel(**To
);
2084 static std::string
getEdgeAttributes(const DisplayNode
*From
,
2085 DisplayNode::ChildIterator
&To
,
2086 const DotCfgDiffDisplayGraph
*DiffData
) {
2087 return DiffData
->getEdgeColorAttr(*From
, **To
);
2095 void DotCfgDiffDisplayGraph::generateDotFile(StringRef DotFile
) {
2097 raw_fd_ostream
OutStream(DotFile
, EC
);
2099 errs() << "Error: " << EC
.message() << "\n";
2102 WriteGraph(OutStream
, this, false);
2111 DCData::DCData(const BasicBlock
&B
) {
2112 // Build up transition labels.
2113 const Instruction
*Term
= B
.getTerminator();
2114 if (const BranchInst
*Br
= dyn_cast
<const BranchInst
>(Term
))
2115 if (Br
->isUnconditional())
2116 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "");
2118 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "true");
2119 addSuccessorLabel(Br
->getSuccessor(1)->getName().str(), "false");
2121 else if (const SwitchInst
*Sw
= dyn_cast
<const SwitchInst
>(Term
)) {
2122 addSuccessorLabel(Sw
->case_default()->getCaseSuccessor()->getName().str(),
2124 for (auto &C
: Sw
->cases()) {
2125 assert(C
.getCaseValue() && "Expected to find case value.");
2126 SmallString
<20> Value
= formatv("{0}", C
.getCaseValue()->getSExtValue());
2127 addSuccessorLabel(C
.getCaseSuccessor()->getName().str(), Value
);
2130 for (const_succ_iterator I
= succ_begin(&B
), E
= succ_end(&B
); I
!= E
; ++I
)
2131 addSuccessorLabel((*I
)->getName().str(), "");
2134 DotCfgChangeReporter::DotCfgChangeReporter(bool Verbose
)
2135 : ChangeReporter
<IRDataT
<DCData
>>(Verbose
) {}
2137 void DotCfgChangeReporter::handleFunctionCompare(
2138 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
2139 bool InModule
, unsigned Minor
, const FuncDataT
<DCData
> &Before
,
2140 const FuncDataT
<DCData
> &After
) {
2141 assert(HTML
&& "Expected outstream to be set");
2142 SmallString
<8> Extender
;
2143 SmallString
<8> Number
;
2144 // Handle numbering and file names.
2146 Extender
= formatv("{0}_{1}", N
, Minor
);
2147 Number
= formatv("{0}.{1}", N
, Minor
);
2149 Extender
= formatv("{0}", N
);
2150 Number
= formatv("{0}", N
);
2152 // Create a temporary file name for the dot file.
2153 SmallVector
<char, 128> SV
;
2154 sys::fs::createUniquePath("cfgdot-%%%%%%.dot", SV
, true);
2155 std::string DotFile
= Twine(SV
).str();
2157 SmallString
<20> PDFFileName
= formatv("diff_{0}.pdf", Extender
);
2158 SmallString
<200> Text
;
2160 Text
= formatv("{0}.{1}{2}{3}{4}", Number
, Prefix
, makeHTMLReady(PassID
),
2163 DotCfgDiff
Diff(Text
, Before
, After
);
2164 std::string EntryBlockName
= After
.getEntryBlockName();
2165 // Use the before entry block if the after entry block was removed.
2166 if (EntryBlockName
== "")
2167 EntryBlockName
= Before
.getEntryBlockName();
2168 assert(EntryBlockName
!= "" && "Expected to find entry block");
2170 DotCfgDiffDisplayGraph DG
= Diff
.createDisplayGraph(Text
, EntryBlockName
);
2171 DG
.generateDotFile(DotFile
);
2173 *HTML
<< genHTML(Text
, DotFile
, PDFFileName
);
2174 std::error_code EC
= sys::fs::remove(DotFile
);
2176 errs() << "Error: " << EC
.message() << "\n";
2179 std::string
DotCfgChangeReporter::genHTML(StringRef Text
, StringRef DotFile
,
2180 StringRef PDFFileName
) {
2181 SmallString
<20> PDFFile
= formatv("{0}/{1}", DotCfgDir
, PDFFileName
);
2182 // Create the PDF file.
2183 static ErrorOr
<std::string
> DotExe
= sys::findProgramByName(DotBinary
);
2185 return "Unable to find dot executable.";
2187 StringRef Args
[] = {DotBinary
, "-Tpdf", "-o", PDFFile
, DotFile
};
2188 int Result
= sys::ExecuteAndWait(*DotExe
, Args
, std::nullopt
);
2190 return "Error executing system dot.";
2192 // Create the HTML tag refering to the PDF file.
2193 SmallString
<200> S
= formatv(
2194 " <a href=\"{0}\" target=\"_blank\">{1}</a><br/>\n", PDFFileName
, Text
);
2198 void DotCfgChangeReporter::handleInitialIR(Any IR
) {
2199 assert(HTML
&& "Expected outstream to be set");
2200 *HTML
<< "<button type=\"button\" class=\"collapsible\">0. "
2201 << "Initial IR (by function)</button>\n"
2202 << "<div class=\"content\">\n"
2204 // Create representation of IR
2205 IRDataT
<DCData
> Data
;
2206 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2207 // Now compare it against itself, which will have everything the
2208 // same and will generate the files.
2209 IRComparer
<DCData
>(Data
, Data
)
2210 .compare(getModuleForComparison(IR
),
2211 [&](bool InModule
, unsigned Minor
,
2212 const FuncDataT
<DCData
> &Before
,
2213 const FuncDataT
<DCData
> &After
) -> void {
2214 handleFunctionCompare("", " ", "Initial IR", "", InModule
,
2215 Minor
, Before
, After
);
2222 void DotCfgChangeReporter::generateIRRepresentation(Any IR
, StringRef PassID
,
2223 IRDataT
<DCData
> &Data
) {
2224 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2227 void DotCfgChangeReporter::omitAfter(StringRef PassID
, std::string
&Name
) {
2228 assert(HTML
&& "Expected outstream to be set");
2229 SmallString
<20> Banner
=
2230 formatv(" <a>{0}. Pass {1} on {2} omitted because no change</a><br/>\n",
2231 N
, makeHTMLReady(PassID
), Name
);
2236 void DotCfgChangeReporter::handleAfter(StringRef PassID
, std::string
&Name
,
2237 const IRDataT
<DCData
> &Before
,
2238 const IRDataT
<DCData
> &After
, Any IR
) {
2239 assert(HTML
&& "Expected outstream to be set");
2240 IRComparer
<DCData
>(Before
, After
)
2241 .compare(getModuleForComparison(IR
),
2242 [&](bool InModule
, unsigned Minor
,
2243 const FuncDataT
<DCData
> &Before
,
2244 const FuncDataT
<DCData
> &After
) -> void {
2245 handleFunctionCompare(Name
, " Pass ", PassID
, " on ", InModule
,
2246 Minor
, Before
, After
);
2248 *HTML
<< " </p></div>\n";
2252 void DotCfgChangeReporter::handleInvalidated(StringRef PassID
) {
2253 assert(HTML
&& "Expected outstream to be set");
2254 SmallString
<20> Banner
=
2255 formatv(" <a>{0}. {1} invalidated</a><br/>\n", N
, makeHTMLReady(PassID
));
2260 void DotCfgChangeReporter::handleFiltered(StringRef PassID
, std::string
&Name
) {
2261 assert(HTML
&& "Expected outstream to be set");
2262 SmallString
<20> Banner
=
2263 formatv(" <a>{0}. Pass {1} on {2} filtered out</a><br/>\n", N
,
2264 makeHTMLReady(PassID
), Name
);
2269 void DotCfgChangeReporter::handleIgnored(StringRef PassID
, std::string
&Name
) {
2270 assert(HTML
&& "Expected outstream to be set");
2271 SmallString
<20> Banner
= formatv(" <a>{0}. {1} on {2} ignored</a><br/>\n", N
,
2272 makeHTMLReady(PassID
), Name
);
2277 bool DotCfgChangeReporter::initializeHTML() {
2279 HTML
= std::make_unique
<raw_fd_ostream
>(DotCfgDir
+ "/passes.html", EC
);
2285 *HTML
<< "<!doctype html>"
2288 << "<style>.collapsible { "
2289 << "background-color: #777;"
2291 << " cursor: pointer;"
2292 << " padding: 18px;"
2295 << " text-align: left;"
2296 << " outline: none;"
2297 << " font-size: 15px;"
2298 << "} .active, .collapsible:hover {"
2299 << " background-color: #555;"
2301 << " padding: 0 18px;"
2302 << " display: none;"
2303 << " overflow: hidden;"
2304 << " background-color: #f1f1f1;"
2307 << "<title>passes.html</title>"
2313 DotCfgChangeReporter::~DotCfgChangeReporter() {
2317 << "<script>var coll = document.getElementsByClassName(\"collapsible\");"
2319 << "for (i = 0; i < coll.length; i++) {"
2320 << "coll[i].addEventListener(\"click\", function() {"
2321 << " this.classList.toggle(\"active\");"
2322 << " var content = this.nextElementSibling;"
2323 << " if (content.style.display === \"block\"){"
2324 << " content.style.display = \"none\";"
2327 << " content.style.display= \"block\";"
2338 void DotCfgChangeReporter::registerCallbacks(
2339 PassInstrumentationCallbacks
&PIC
) {
2340 if (PrintChanged
== ChangePrinter::DotCfgVerbose
||
2341 PrintChanged
== ChangePrinter::DotCfgQuiet
) {
2342 SmallString
<128> OutputDir
;
2343 sys::fs::expand_tilde(DotCfgDir
, OutputDir
);
2344 sys::fs::make_absolute(OutputDir
);
2345 assert(!OutputDir
.empty() && "expected output dir to be non-empty");
2346 DotCfgDir
= OutputDir
.c_str();
2347 if (initializeHTML()) {
2348 ChangeReporter
<IRDataT
<DCData
>>::registerRequiredCallbacks(PIC
);
2351 dbgs() << "Unable to open output stream for -cfg-dot-changed\n";
2355 StandardInstrumentations::StandardInstrumentations(
2356 LLVMContext
&Context
, bool DebugLogging
, bool VerifyEach
,
2357 PrintPassOptions PrintPassOpts
)
2358 : PrintPass(DebugLogging
, PrintPassOpts
),
2359 OptNone(DebugLogging
),
2360 OptPassGate(Context
),
2361 PrintChangedIR(PrintChanged
== ChangePrinter::Verbose
),
2362 PrintChangedDiff(PrintChanged
== ChangePrinter::DiffVerbose
||
2363 PrintChanged
== ChangePrinter::ColourDiffVerbose
,
2364 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
2365 PrintChanged
== ChangePrinter::ColourDiffQuiet
),
2366 WebsiteChangeReporter(PrintChanged
== ChangePrinter::DotCfgVerbose
),
2367 Verify(DebugLogging
), VerifyEach(VerifyEach
) {}
2369 PrintCrashIRInstrumentation
*PrintCrashIRInstrumentation::CrashReporter
=
2372 void PrintCrashIRInstrumentation::reportCrashIR() {
2373 if (!PrintOnCrashPath
.empty()) {
2375 raw_fd_ostream
Out(PrintOnCrashPath
, EC
);
2377 report_fatal_error(errorCodeToError(EC
));
2384 void PrintCrashIRInstrumentation::SignalHandler(void *) {
2385 // Called by signal handlers so do not lock here
2386 // Is the PrintCrashIRInstrumentation still alive?
2390 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2391 "Did not expect to get here without option set.");
2392 CrashReporter
->reportCrashIR();
2395 PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
2399 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2400 "Did not expect to get here without option set.");
2401 CrashReporter
= nullptr;
2404 void PrintCrashIRInstrumentation::registerCallbacks(
2405 PassInstrumentationCallbacks
&PIC
) {
2406 if ((!PrintOnCrash
&& PrintOnCrashPath
.empty()) || CrashReporter
)
2409 sys::AddSignalHandler(SignalHandler
, nullptr);
2410 CrashReporter
= this;
2412 PIC
.registerBeforeNonSkippedPassCallback(
2413 [&PIC
, this](StringRef PassID
, Any IR
) {
2415 raw_string_ostream
OS(SavedIR
);
2416 OS
<< formatv("*** Dump of {0}IR Before Last Pass {1}",
2417 llvm::forcePrintModuleIR() ? "Module " : "", PassID
);
2418 if (!isInteresting(IR
, PassID
, PIC
.getPassNameForClassName(PassID
))) {
2419 OS
<< " Filtered Out ***\n";
2422 OS
<< " Started ***\n";
2423 unwrapAndPrint(OS
, IR
);
2427 void StandardInstrumentations::registerCallbacks(
2428 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
*MAM
) {
2429 PrintIR
.registerCallbacks(PIC
);
2430 PrintPass
.registerCallbacks(PIC
);
2431 TimePasses
.registerCallbacks(PIC
);
2432 OptNone
.registerCallbacks(PIC
);
2433 OptPassGate
.registerCallbacks(PIC
);
2434 PrintChangedIR
.registerCallbacks(PIC
);
2435 PseudoProbeVerification
.registerCallbacks(PIC
);
2437 Verify
.registerCallbacks(PIC
);
2438 PrintChangedDiff
.registerCallbacks(PIC
);
2439 WebsiteChangeReporter
.registerCallbacks(PIC
);
2440 ChangeTester
.registerCallbacks(PIC
);
2441 PrintCrashIR
.registerCallbacks(PIC
);
2443 PreservedCFGChecker
.registerCallbacks(PIC
, *MAM
);
2445 // TimeProfiling records the pass running time cost.
2446 // Its 'BeforePassCallback' can be appended at the tail of all the
2447 // BeforeCallbacks by calling `registerCallbacks` in the end.
2448 // Its 'AfterPassCallback' is put at the front of all the
2449 // AfterCallbacks by its `registerCallbacks`. This is necessary
2450 // to ensure that other callbacks are not included in the timings.
2451 TimeProfilingPasses
.registerCallbacks(PIC
);
2454 template class ChangeReporter
<std::string
>;
2455 template class TextChangeReporter
<std::string
>;
2457 template class BlockDataT
<EmptyData
>;
2458 template class FuncDataT
<EmptyData
>;
2459 template class IRDataT
<EmptyData
>;
2460 template class ChangeReporter
<IRDataT
<EmptyData
>>;
2461 template class TextChangeReporter
<IRDataT
<EmptyData
>>;
2462 template class IRComparer
<EmptyData
>;