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/IR/Constants.h"
23 #include "llvm/IR/Function.h"
24 #include "llvm/IR/Module.h"
25 #include "llvm/IR/PassInstrumentation.h"
26 #include "llvm/IR/PassManager.h"
27 #include "llvm/IR/PrintPasses.h"
28 #include "llvm/IR/StructuralHash.h"
29 #include "llvm/IR/Verifier.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Support/CrashRecoveryContext.h"
32 #include "llvm/Support/Debug.h"
33 #include "llvm/Support/Error.h"
34 #include "llvm/Support/FormatVariadic.h"
35 #include "llvm/Support/GraphWriter.h"
36 #include "llvm/Support/MemoryBuffer.h"
37 #include "llvm/Support/Path.h"
38 #include "llvm/Support/Program.h"
39 #include "llvm/Support/Regex.h"
40 #include "llvm/Support/Signals.h"
41 #include "llvm/Support/raw_ostream.h"
42 #include <unordered_map>
43 #include <unordered_set>
49 static cl::opt
<bool> VerifyAnalysisInvalidation("verify-analysis-invalidation",
51 #ifdef EXPENSIVE_CHECKS
58 // An option that supports the -print-changed option. See
59 // the description for -print-changed for an explanation of the use
60 // of this option. Note that this option has no effect without -print-changed.
62 PrintChangedBefore("print-before-changed",
63 cl::desc("Print before passes that change them"),
64 cl::init(false), cl::Hidden
);
66 // An option for specifying the dot used by
67 // print-changed=[dot-cfg | dot-cfg-quiet]
68 static cl::opt
<std::string
>
69 DotBinary("print-changed-dot-path", cl::Hidden
, cl::init("dot"),
70 cl::desc("system dot used by change reporters"));
72 // An option that determines the colour used for elements that are only
73 // in the before part. Must be a colour named in appendix J of
74 // https://graphviz.org/pdf/dotguide.pdf
75 static cl::opt
<std::string
>
76 BeforeColour("dot-cfg-before-color",
77 cl::desc("Color for dot-cfg before elements"), cl::Hidden
,
79 // An option that determines the colour used for elements that are only
80 // in the after part. Must be a colour named in appendix J of
81 // https://graphviz.org/pdf/dotguide.pdf
82 static cl::opt
<std::string
>
83 AfterColour("dot-cfg-after-color",
84 cl::desc("Color for dot-cfg after elements"), cl::Hidden
,
85 cl::init("forestgreen"));
86 // An option that determines the colour used for elements that are in both
87 // the before and after parts. Must be a colour named in appendix J of
88 // https://graphviz.org/pdf/dotguide.pdf
89 static cl::opt
<std::string
>
90 CommonColour("dot-cfg-common-color",
91 cl::desc("Color for dot-cfg common elements"), cl::Hidden
,
94 // An option that determines where the generated website file (named
95 // passes.html) and the associated pdf files (named diff_*.pdf) are saved.
96 static cl::opt
<std::string
> DotCfgDir(
98 cl::desc("Generate dot files into specified directory for changed IRs"),
99 cl::Hidden
, cl::init("./"));
101 // Options to print the IR that was being processed when a pass crashes.
102 static cl::opt
<std::string
> PrintOnCrashPath(
103 "print-on-crash-path",
104 cl::desc("Print the last form of the IR before crash to a file"),
107 static cl::opt
<bool> PrintOnCrash(
109 cl::desc("Print the last form of the IR before crash (use -print-on-crash-path to dump to a file)"),
112 static cl::opt
<std::string
> OptBisectPrintIRPath(
113 "opt-bisect-print-ir-path",
114 cl::desc("Print IR to path when opt-bisect-limit is reached"), cl::Hidden
);
116 static cl::opt
<bool> PrintPassNumbers(
117 "print-pass-numbers", cl::init(false), cl::Hidden
,
118 cl::desc("Print pass names and their ordinals"));
120 static cl::opt
<unsigned>
121 PrintAtPassNumber("print-at-pass-number", cl::init(0), cl::Hidden
,
122 cl::desc("Print IR at pass with this number as "
123 "reported by print-passes-names"));
125 static cl::opt
<std::string
> IRDumpDirectory(
127 cl::desc("If specified, IR printed using the "
128 "-print-[before|after]{-all} options will be dumped into "
129 "files in this directory rather than written to stderr"),
130 cl::Hidden
, cl::value_desc("filename"));
134 // An option for specifying an executable that will be called with the IR
135 // everytime it changes in the opt pipeline. It will also be called on
136 // the initial IR as it enters the pipeline. The executable will be passed
137 // the name of a temporary file containing the IR and the PassID. This may
138 // be used, for example, to call llc on the IR and run a test to determine
139 // which pass makes a change that changes the functioning of the IR.
140 // The usual modifier options work as expected.
141 static cl::opt
<std::string
>
142 TestChanged("exec-on-ir-change", cl::Hidden
, cl::init(""),
143 cl::desc("exe called with module IR after each pass that "
146 /// Extract Module out of \p IR unit. May return nullptr if \p IR does not match
147 /// certain global filters. Will never return nullptr if \p Force is true.
148 const Module
*unwrapModule(Any IR
, bool Force
= false) {
149 if (const auto **M
= llvm::any_cast
<const Module
*>(&IR
))
152 if (const auto **F
= llvm::any_cast
<const Function
*>(&IR
)) {
153 if (!Force
&& !isFunctionInPrintList((*F
)->getName()))
156 return (*F
)->getParent();
159 if (const auto **C
= llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
)) {
160 for (const LazyCallGraph::Node
&N
: **C
) {
161 const Function
&F
= N
.getFunction();
162 if (Force
|| (!F
.isDeclaration() && isFunctionInPrintList(F
.getName()))) {
163 return F
.getParent();
166 assert(!Force
&& "Expected a module");
170 if (const auto **L
= llvm::any_cast
<const Loop
*>(&IR
)) {
171 const Function
*F
= (*L
)->getHeader()->getParent();
172 if (!Force
&& !isFunctionInPrintList(F
->getName()))
174 return F
->getParent();
177 llvm_unreachable("Unknown IR unit");
180 void printIR(raw_ostream
&OS
, const Function
*F
) {
181 if (!isFunctionInPrintList(F
->getName()))
186 void printIR(raw_ostream
&OS
, const Module
*M
) {
187 if (isFunctionInPrintList("*") || forcePrintModuleIR()) {
188 M
->print(OS
, nullptr);
190 for (const auto &F
: M
->functions()) {
196 void printIR(raw_ostream
&OS
, const LazyCallGraph::SCC
*C
) {
197 for (const LazyCallGraph::Node
&N
: *C
) {
198 const Function
&F
= N
.getFunction();
199 if (!F
.isDeclaration() && isFunctionInPrintList(F
.getName())) {
205 void printIR(raw_ostream
&OS
, const Loop
*L
) {
206 const Function
*F
= L
->getHeader()->getParent();
207 if (!isFunctionInPrintList(F
->getName()))
209 printLoop(const_cast<Loop
&>(*L
), OS
);
212 std::string
getIRName(Any IR
) {
213 if (llvm::any_cast
<const Module
*>(&IR
))
216 if (const auto **F
= llvm::any_cast
<const Function
*>(&IR
))
217 return (*F
)->getName().str();
219 if (const auto **C
= llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
))
220 return (*C
)->getName();
222 if (const auto **L
= llvm::any_cast
<const Loop
*>(&IR
))
223 return (*L
)->getName().str();
225 llvm_unreachable("Unknown wrapped IR type");
228 bool moduleContainsFilterPrintFunc(const Module
&M
) {
229 return any_of(M
.functions(),
230 [](const Function
&F
) {
231 return isFunctionInPrintList(F
.getName());
233 isFunctionInPrintList("*");
236 bool sccContainsFilterPrintFunc(const LazyCallGraph::SCC
&C
) {
238 [](const LazyCallGraph::Node
&N
) {
239 return isFunctionInPrintList(N
.getName());
241 isFunctionInPrintList("*");
244 bool shouldPrintIR(Any IR
) {
245 if (const auto **M
= llvm::any_cast
<const Module
*>(&IR
))
246 return moduleContainsFilterPrintFunc(**M
);
248 if (const auto **F
= llvm::any_cast
<const Function
*>(&IR
))
249 return isFunctionInPrintList((*F
)->getName());
251 if (const auto **C
= llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
))
252 return sccContainsFilterPrintFunc(**C
);
254 if (const auto **L
= llvm::any_cast
<const Loop
*>(&IR
))
255 return isFunctionInPrintList((*L
)->getHeader()->getParent()->getName());
256 llvm_unreachable("Unknown wrapped IR type");
259 /// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into
260 /// Any and does actual print job.
261 void unwrapAndPrint(raw_ostream
&OS
, Any IR
) {
262 if (!shouldPrintIR(IR
))
265 if (forcePrintModuleIR()) {
266 auto *M
= unwrapModule(IR
);
267 assert(M
&& "should have unwrapped module");
272 if (const auto **M
= llvm::any_cast
<const Module
*>(&IR
)) {
277 if (const auto **F
= llvm::any_cast
<const Function
*>(&IR
)) {
282 if (const auto **C
= llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
)) {
287 if (const auto **L
= llvm::any_cast
<const Loop
*>(&IR
)) {
291 llvm_unreachable("Unknown wrapped IR type");
294 // Return true when this is a pass for which changes should be ignored
295 bool isIgnored(StringRef PassID
) {
296 return isSpecialPass(PassID
,
297 {"PassManager", "PassAdaptor", "AnalysisManagerProxy",
298 "DevirtSCCRepeatedPass", "ModuleInlinerWrapperPass",
299 "VerifierPass", "PrintModulePass"});
302 std::string
makeHTMLReady(StringRef SR
) {
306 SR
.take_until([](char C
) { return C
== '<' || C
== '>'; });
307 S
.append(Clean
.str());
308 SR
= SR
.drop_front(Clean
.size());
311 S
.append(SR
[0] == '<' ? "<" : ">");
312 SR
= SR
.drop_front();
314 llvm_unreachable("problems converting string to HTML");
317 // Return the module when that is the appropriate level of comparison for \p IR.
318 const Module
*getModuleForComparison(Any IR
) {
319 if (const auto **M
= llvm::any_cast
<const Module
*>(&IR
))
321 if (const auto **C
= llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
))
329 bool isInterestingFunction(const Function
&F
) {
330 return isFunctionInPrintList(F
.getName());
333 // Return true when this is a pass on IR for which printing
334 // of changes is desired.
335 bool isInteresting(Any IR
, StringRef PassID
, StringRef PassName
) {
336 if (isIgnored(PassID
) || !isPassInPrintList(PassName
))
338 if (const auto **F
= llvm::any_cast
<const Function
*>(&IR
))
339 return isInterestingFunction(**F
);
345 template <typename T
> ChangeReporter
<T
>::~ChangeReporter() {
346 assert(BeforeStack
.empty() && "Problem with Change Printer stack.");
349 template <typename T
>
350 void ChangeReporter
<T
>::saveIRBeforePass(Any IR
, StringRef PassID
,
351 StringRef PassName
) {
352 // Is this the initial IR?
359 // Always need to place something on the stack because invalidated passes
360 // are not given the IR so it cannot be determined whether the pass was for
361 // something that was filtered out.
362 BeforeStack
.emplace_back();
364 if (!isInteresting(IR
, PassID
, PassName
))
367 // Save the IR representation on the stack.
368 T
&Data
= BeforeStack
.back();
369 generateIRRepresentation(IR
, PassID
, Data
);
372 template <typename T
>
373 void ChangeReporter
<T
>::handleIRAfterPass(Any IR
, StringRef PassID
,
374 StringRef PassName
) {
375 assert(!BeforeStack
.empty() && "Unexpected empty stack encountered.");
377 std::string Name
= getIRName(IR
);
379 if (isIgnored(PassID
)) {
381 handleIgnored(PassID
, Name
);
382 } else if (!isInteresting(IR
, PassID
, PassName
)) {
384 handleFiltered(PassID
, Name
);
386 // Get the before rep from the stack
387 T
&Before
= BeforeStack
.back();
388 // Create the after rep
390 generateIRRepresentation(IR
, PassID
, After
);
392 // Was there a change in IR?
393 if (Before
== After
) {
395 omitAfter(PassID
, Name
);
397 handleAfter(PassID
, Name
, Before
, After
, IR
);
399 BeforeStack
.pop_back();
402 template <typename T
>
403 void ChangeReporter
<T
>::handleInvalidatedPass(StringRef PassID
) {
404 assert(!BeforeStack
.empty() && "Unexpected empty stack encountered.");
406 // Always flag it as invalidated as we cannot determine when
407 // a pass for a filtered function is invalidated since we do not
408 // get the IR in the call. Also, the output is just alternate
409 // forms of the banner anyway.
411 handleInvalidated(PassID
);
412 BeforeStack
.pop_back();
415 template <typename T
>
416 void ChangeReporter
<T
>::registerRequiredCallbacks(
417 PassInstrumentationCallbacks
&PIC
) {
418 PIC
.registerBeforeNonSkippedPassCallback([&PIC
, this](StringRef P
, Any IR
) {
419 saveIRBeforePass(IR
, P
, PIC
.getPassNameForClassName(P
));
422 PIC
.registerAfterPassCallback(
423 [&PIC
, this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
424 handleIRAfterPass(IR
, P
, PIC
.getPassNameForClassName(P
));
426 PIC
.registerAfterPassInvalidatedCallback(
427 [this](StringRef P
, const PreservedAnalyses
&) {
428 handleInvalidatedPass(P
);
432 template <typename T
>
433 TextChangeReporter
<T
>::TextChangeReporter(bool Verbose
)
434 : ChangeReporter
<T
>(Verbose
), Out(dbgs()) {}
436 template <typename T
> void TextChangeReporter
<T
>::handleInitialIR(Any IR
) {
437 // Always print the module.
438 // Unwrap and print directly to avoid filtering problems in general routines.
439 auto *M
= unwrapModule(IR
, /*Force=*/true);
440 assert(M
&& "Expected module to be unwrapped when forced.");
441 Out
<< "*** IR Dump At Start ***\n";
442 M
->print(Out
, nullptr);
445 template <typename T
>
446 void TextChangeReporter
<T
>::omitAfter(StringRef PassID
, std::string
&Name
) {
447 Out
<< formatv("*** IR Dump After {0} on {1} omitted because no change ***\n",
451 template <typename T
>
452 void TextChangeReporter
<T
>::handleInvalidated(StringRef PassID
) {
453 Out
<< formatv("*** IR Pass {0} invalidated ***\n", PassID
);
456 template <typename T
>
457 void TextChangeReporter
<T
>::handleFiltered(StringRef PassID
,
459 SmallString
<20> Banner
=
460 formatv("*** IR Dump After {0} on {1} filtered out ***\n", PassID
, Name
);
464 template <typename T
>
465 void TextChangeReporter
<T
>::handleIgnored(StringRef PassID
, std::string
&Name
) {
466 Out
<< formatv("*** IR Pass {0} on {1} ignored ***\n", PassID
, Name
);
469 IRChangedPrinter::~IRChangedPrinter() = default;
471 void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
472 if (PrintChanged
== ChangePrinter::Verbose
||
473 PrintChanged
== ChangePrinter::Quiet
)
474 TextChangeReporter
<std::string
>::registerRequiredCallbacks(PIC
);
477 void IRChangedPrinter::generateIRRepresentation(Any IR
, StringRef PassID
,
478 std::string
&Output
) {
479 raw_string_ostream
OS(Output
);
480 unwrapAndPrint(OS
, IR
);
484 void IRChangedPrinter::handleAfter(StringRef PassID
, std::string
&Name
,
485 const std::string
&Before
,
486 const std::string
&After
, Any
) {
487 // Report the IR before the changes when requested.
488 if (PrintChangedBefore
)
489 Out
<< "*** IR Dump Before " << PassID
<< " on " << Name
<< " ***\n"
492 // We might not get anything to print if we only want to print a specific
493 // function but it gets deleted.
495 Out
<< "*** IR Deleted After " << PassID
<< " on " << Name
<< " ***\n";
499 Out
<< "*** IR Dump After " << PassID
<< " on " << Name
<< " ***\n" << After
;
502 IRChangedTester::~IRChangedTester() {}
504 void IRChangedTester::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
505 if (TestChanged
!= "")
506 TextChangeReporter
<std::string
>::registerRequiredCallbacks(PIC
);
509 void IRChangedTester::handleIR(const std::string
&S
, StringRef PassID
) {
510 // Store the body into a temporary file
511 static SmallVector
<int> FD
{-1};
512 SmallVector
<StringRef
> SR
{S
};
513 static SmallVector
<std::string
> FileName
{""};
514 if (prepareTempFiles(FD
, SR
, FileName
)) {
515 dbgs() << "Unable to create temporary file.";
518 static ErrorOr
<std::string
> Exe
= sys::findProgramByName(TestChanged
);
520 dbgs() << "Unable to find test-changed executable.";
524 StringRef Args
[] = {TestChanged
, FileName
[0], PassID
};
525 int Result
= sys::ExecuteAndWait(*Exe
, Args
);
527 dbgs() << "Error executing test-changed executable.";
531 if (cleanUpTempFiles(FileName
))
532 dbgs() << "Unable to remove temporary file.";
535 void IRChangedTester::handleInitialIR(Any IR
) {
536 // Always test the initial module.
537 // Unwrap and print directly to avoid filtering problems in general routines.
539 generateIRRepresentation(IR
, "Initial IR", S
);
540 handleIR(S
, "Initial IR");
543 void IRChangedTester::omitAfter(StringRef PassID
, std::string
&Name
) {}
544 void IRChangedTester::handleInvalidated(StringRef PassID
) {}
545 void IRChangedTester::handleFiltered(StringRef PassID
, std::string
&Name
) {}
546 void IRChangedTester::handleIgnored(StringRef PassID
, std::string
&Name
) {}
547 void IRChangedTester::handleAfter(StringRef PassID
, std::string
&Name
,
548 const std::string
&Before
,
549 const std::string
&After
, Any
) {
550 handleIR(After
, PassID
);
553 template <typename T
>
554 void OrderedChangedData
<T
>::report(
555 const OrderedChangedData
&Before
, const OrderedChangedData
&After
,
556 function_ref
<void(const T
*, const T
*)> HandlePair
) {
557 const auto &BFD
= Before
.getData();
558 const auto &AFD
= After
.getData();
559 std::vector
<std::string
>::const_iterator BI
= Before
.getOrder().begin();
560 std::vector
<std::string
>::const_iterator BE
= Before
.getOrder().end();
561 std::vector
<std::string
>::const_iterator AI
= After
.getOrder().begin();
562 std::vector
<std::string
>::const_iterator AE
= After
.getOrder().end();
564 auto HandlePotentiallyRemovedData
= [&](std::string S
) {
565 // The order in LLVM may have changed so check if still exists.
567 // This has been removed.
568 HandlePair(&BFD
.find(*BI
)->getValue(), nullptr);
571 auto HandleNewData
= [&](std::vector
<const T
*> &Q
) {
572 // Print out any queued up new sections
573 for (const T
*NBI
: Q
)
574 HandlePair(nullptr, NBI
);
578 // Print out the data in the after order, with before ones interspersed
579 // appropriately (ie, somewhere near where they were in the before list).
580 // Start at the beginning of both lists. Loop through the
581 // after list. If an element is common, then advance in the before list
582 // reporting the removed ones until the common one is reached. Report any
583 // queued up new ones and then report the common one. If an element is not
584 // common, then enqueue it for reporting. When the after list is exhausted,
585 // loop through the before list, reporting any removed ones. Finally,
586 // report the rest of the enqueued new ones.
587 std::vector
<const T
*> NewDataQueue
;
589 if (!BFD
.count(*AI
)) {
590 // This section is new so place it in the queue. This will cause it
591 // to be reported after deleted sections.
592 NewDataQueue
.emplace_back(&AFD
.find(*AI
)->getValue());
596 // This section is in both; advance and print out any before-only
597 // until we get to it.
598 // It's possible that this section has moved to be later than before. This
599 // will mess up printing most blocks side by side, but it's a rare case and
600 // it's better than crashing.
601 while (BI
!= BE
&& *BI
!= *AI
) {
602 HandlePotentiallyRemovedData(*BI
);
605 // Report any new sections that were queued up and waiting.
606 HandleNewData(NewDataQueue
);
608 const T
&AData
= AFD
.find(*AI
)->getValue();
609 const T
&BData
= BFD
.find(*AI
)->getValue();
610 HandlePair(&BData
, &AData
);
616 // Check any remaining before sections to see if they have been removed
618 HandlePotentiallyRemovedData(*BI
);
622 HandleNewData(NewDataQueue
);
625 template <typename T
>
626 void IRComparer
<T
>::compare(
628 std::function
<void(bool InModule
, unsigned Minor
,
629 const FuncDataT
<T
> &Before
, const FuncDataT
<T
> &After
)>
631 if (!CompareModule
) {
632 // Just handle the single function.
633 assert(Before
.getData().size() == 1 && After
.getData().size() == 1 &&
634 "Expected only one function.");
635 CompareFunc(false, 0, Before
.getData().begin()->getValue(),
636 After
.getData().begin()->getValue());
641 FuncDataT
<T
> Missing("");
642 IRDataT
<T
>::report(Before
, After
,
643 [&](const FuncDataT
<T
> *B
, const FuncDataT
<T
> *A
) {
644 assert((B
|| A
) && "Both functions cannot be missing.");
649 CompareFunc(true, Minor
++, *B
, *A
);
653 template <typename T
> void IRComparer
<T
>::analyzeIR(Any IR
, IRDataT
<T
> &Data
) {
654 if (const Module
*M
= getModuleForComparison(IR
)) {
655 // Create data for each existing/interesting function in the module.
656 for (const Function
&F
: *M
)
657 generateFunctionData(Data
, F
);
661 const Function
**FPtr
= llvm::any_cast
<const Function
*>(&IR
);
662 const Function
*F
= FPtr
? *FPtr
: nullptr;
664 const Loop
**L
= llvm::any_cast
<const Loop
*>(&IR
);
665 assert(L
&& "Unknown IR unit.");
666 F
= (*L
)->getHeader()->getParent();
668 assert(F
&& "Unknown IR unit.");
669 generateFunctionData(Data
, *F
);
672 template <typename T
>
673 bool IRComparer
<T
>::generateFunctionData(IRDataT
<T
> &Data
, const Function
&F
) {
674 if (!F
.isDeclaration() && isFunctionInPrintList(F
.getName())) {
675 FuncDataT
<T
> FD(F
.getEntryBlock().getName().str());
677 for (const auto &B
: F
) {
678 std::string BBName
= B
.getName().str();
679 if (BBName
.empty()) {
680 BBName
= formatv("{0}", I
);
683 FD
.getOrder().emplace_back(BBName
);
684 FD
.getData().insert({BBName
, B
});
686 Data
.getOrder().emplace_back(F
.getName());
687 Data
.getData().insert({F
.getName(), FD
});
693 PrintIRInstrumentation::~PrintIRInstrumentation() {
694 assert(PassRunDescriptorStack
.empty() &&
695 "PassRunDescriptorStack is not empty at exit");
698 static SmallString
<32> getIRFileDisplayName(Any IR
) {
699 SmallString
<32> Result
;
700 raw_svector_ostream
ResultStream(Result
);
701 const Module
*M
= unwrapModule(IR
);
702 stable_hash NameHash
= stable_hash_combine_string(M
->getName());
703 unsigned int MaxHashWidth
= sizeof(stable_hash
) * 8 / 4;
704 write_hex(ResultStream
, NameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
705 if (llvm::any_cast
<const Module
*>(&IR
)) {
706 ResultStream
<< "-module";
707 } else if (const Function
**F
= llvm::any_cast
<const Function
*>(&IR
)) {
708 ResultStream
<< "-function-";
709 stable_hash FunctionNameHash
= stable_hash_combine_string((*F
)->getName());
710 write_hex(ResultStream
, FunctionNameHash
, HexPrintStyle::Lower
,
712 } else if (const LazyCallGraph::SCC
**C
=
713 llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
)) {
714 ResultStream
<< "-scc-";
715 stable_hash SCCNameHash
= stable_hash_combine_string((*C
)->getName());
716 write_hex(ResultStream
, SCCNameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
717 } else if (const Loop
**L
= llvm::any_cast
<const Loop
*>(&IR
)) {
718 ResultStream
<< "-loop-";
719 stable_hash LoopNameHash
= stable_hash_combine_string((*L
)->getName());
720 write_hex(ResultStream
, LoopNameHash
, HexPrintStyle::Lower
, MaxHashWidth
);
722 llvm_unreachable("Unknown wrapped IR type");
727 std::string
PrintIRInstrumentation::fetchDumpFilename(StringRef PassName
,
729 const StringRef RootDirectory
= IRDumpDirectory
;
730 assert(!RootDirectory
.empty() &&
731 "The flag -ir-dump-directory must be passed to dump IR to files");
732 SmallString
<128> ResultPath
;
733 ResultPath
+= RootDirectory
;
734 SmallString
<64> Filename
;
735 raw_svector_ostream
FilenameStream(Filename
);
736 FilenameStream
<< CurrentPassNumber
;
737 FilenameStream
<< "-";
738 FilenameStream
<< getIRFileDisplayName(IR
);
739 FilenameStream
<< "-";
740 FilenameStream
<< PassName
;
741 sys::path::append(ResultPath
, Filename
);
742 return std::string(ResultPath
);
745 enum class IRDumpFileSuffixType
{
751 static StringRef
getFileSuffix(IRDumpFileSuffixType Type
) {
752 static constexpr std::array FileSuffixes
= {"-before.ll", "-after.ll",
754 return FileSuffixes
[static_cast<size_t>(Type
)];
757 void PrintIRInstrumentation::pushPassRunDescriptor(
758 StringRef PassID
, Any IR
, std::string
&DumpIRFilename
) {
759 const Module
*M
= unwrapModule(IR
);
760 PassRunDescriptorStack
.emplace_back(
761 PassRunDescriptor(M
, DumpIRFilename
, getIRName(IR
), PassID
));
764 PrintIRInstrumentation::PassRunDescriptor
765 PrintIRInstrumentation::popPassRunDescriptor(StringRef PassID
) {
766 assert(!PassRunDescriptorStack
.empty() && "empty PassRunDescriptorStack");
767 PassRunDescriptor Descriptor
= PassRunDescriptorStack
.pop_back_val();
768 assert(Descriptor
.PassID
.equals(PassID
) &&
769 "malformed PassRunDescriptorStack");
773 // Callers are responsible for closing the returned file descriptor
774 static int prepareDumpIRFileDescriptor(const StringRef DumpIRFilename
) {
776 auto ParentPath
= llvm::sys::path::parent_path(DumpIRFilename
);
777 if (!ParentPath
.empty()) {
778 std::error_code EC
= llvm::sys::fs::create_directories(ParentPath
);
780 report_fatal_error(Twine("Failed to create directory ") + ParentPath
+
781 " to support -ir-dump-directory: " + EC
.message());
784 EC
= sys::fs::openFile(DumpIRFilename
, Result
, sys::fs::CD_OpenAlways
,
785 sys::fs::FA_Write
, sys::fs::OF_None
);
787 report_fatal_error(Twine("Failed to open ") + DumpIRFilename
+
788 " to support -ir-dump-directory: " + EC
.message());
792 void PrintIRInstrumentation::printBeforePass(StringRef PassID
, Any IR
) {
793 if (isIgnored(PassID
))
796 std::string DumpIRFilename
;
797 if (!IRDumpDirectory
.empty() &&
798 (shouldPrintBeforePass(PassID
) || shouldPrintAfterPass(PassID
)))
799 DumpIRFilename
= fetchDumpFilename(PassID
, IR
);
801 // Saving Module for AfterPassInvalidated operations.
802 // Note: here we rely on a fact that we do not change modules while
803 // traversing the pipeline, so the latest captured module is good
804 // for all print operations that has not happen yet.
805 if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() ||
806 shouldPrintAfterPass(PassID
))
807 pushPassRunDescriptor(PassID
, IR
, DumpIRFilename
);
809 if (!shouldPrintIR(IR
))
814 if (shouldPrintPassNumbers())
815 dbgs() << " Running pass " << CurrentPassNumber
<< " " << PassID
816 << " on " << getIRName(IR
) << "\n";
818 if (!shouldPrintBeforePass(PassID
))
821 auto WriteIRToStream
= [&](raw_ostream
&Stream
) {
822 Stream
<< "; *** IR Dump Before " << PassID
<< " on " << getIRName(IR
)
824 unwrapAndPrint(Stream
, IR
);
827 if (!DumpIRFilename
.empty()) {
828 DumpIRFilename
+= getFileSuffix(IRDumpFileSuffixType::Before
);
829 llvm::raw_fd_ostream DumpIRFileStream
{
830 prepareDumpIRFileDescriptor(DumpIRFilename
), /* shouldClose */ true};
831 WriteIRToStream(DumpIRFileStream
);
833 WriteIRToStream(dbgs());
837 void PrintIRInstrumentation::printAfterPass(StringRef PassID
, Any IR
) {
838 if (isIgnored(PassID
))
841 if (!shouldPrintAfterPass(PassID
) && !shouldPrintPassNumbers() &&
842 !shouldPrintAtPassNumber())
845 auto [M
, DumpIRFilename
, IRName
, StoredPassID
] = popPassRunDescriptor(PassID
);
846 assert(StoredPassID
== PassID
&& "mismatched PassID");
848 if (!shouldPrintIR(IR
) || !shouldPrintAfterPass(PassID
))
851 auto WriteIRToStream
= [&](raw_ostream
&Stream
, const StringRef IRName
) {
852 Stream
<< "; *** IR Dump "
853 << (shouldPrintAtPassNumber()
854 ? StringRef(formatv("At {0}-{1}", CurrentPassNumber
, PassID
))
855 : StringRef(formatv("After {0}", PassID
)))
856 << " on " << IRName
<< " ***\n";
857 unwrapAndPrint(Stream
, IR
);
860 if (!IRDumpDirectory
.empty()) {
861 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
862 "should be set in printBeforePass");
863 const std::string DumpIRFilenameWithSuffix
=
864 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::After
).str();
865 llvm::raw_fd_ostream DumpIRFileStream
{
866 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
867 /* shouldClose */ true};
868 WriteIRToStream(DumpIRFileStream
, IRName
);
870 WriteIRToStream(dbgs(), IRName
);
874 void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID
) {
875 if (isIgnored(PassID
))
878 if (!shouldPrintAfterPass(PassID
) && !shouldPrintPassNumbers() &&
879 !shouldPrintAtPassNumber())
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 if (shouldPrintAtPassNumber())
893 Banner
= formatv("; *** IR Dump At {0}-{1} on {2} (invalidated) ***",
894 CurrentPassNumber
, PassID
, IRName
);
896 Banner
= formatv("; *** IR Dump After {0} on {1} (invalidated) ***",
898 Stream
<< Banner
<< "\n";
902 if (!IRDumpDirectory
.empty()) {
903 assert(!DumpIRFilename
.empty() && "DumpIRFilename must not be empty and "
904 "should be set in printBeforePass");
905 const std::string DumpIRFilenameWithSuffix
=
906 DumpIRFilename
+ getFileSuffix(IRDumpFileSuffixType::Invalidated
).str();
907 llvm::raw_fd_ostream DumpIRFileStream
{
908 prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix
),
909 /* shouldClose */ true};
910 WriteIRToStream(DumpIRFileStream
, M
, IRName
);
912 WriteIRToStream(dbgs(), M
, IRName
);
916 bool PrintIRInstrumentation::shouldPrintBeforePass(StringRef PassID
) {
917 if (shouldPrintBeforeAll())
920 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
921 return is_contained(printBeforePasses(), PassName
);
924 bool PrintIRInstrumentation::shouldPrintAfterPass(StringRef PassID
) {
925 if (shouldPrintAfterAll())
928 if (shouldPrintAtPassNumber() && CurrentPassNumber
== PrintAtPassNumber
)
931 StringRef PassName
= PIC
->getPassNameForClassName(PassID
);
932 return is_contained(printAfterPasses(), PassName
);
935 bool PrintIRInstrumentation::shouldPrintPassNumbers() {
936 return PrintPassNumbers
;
939 bool PrintIRInstrumentation::shouldPrintAtPassNumber() {
940 return PrintAtPassNumber
> 0;
943 void PrintIRInstrumentation::registerCallbacks(
944 PassInstrumentationCallbacks
&PIC
) {
947 // BeforePass callback is not just for printing, it also saves a Module
948 // for later use in AfterPassInvalidated.
949 if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() ||
950 shouldPrintBeforeSomePass() || shouldPrintAfterSomePass())
951 PIC
.registerBeforeNonSkippedPassCallback(
952 [this](StringRef P
, Any IR
) { this->printBeforePass(P
, IR
); });
954 if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() ||
955 shouldPrintAfterSomePass()) {
956 PIC
.registerAfterPassCallback(
957 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
958 this->printAfterPass(P
, IR
);
960 PIC
.registerAfterPassInvalidatedCallback(
961 [this](StringRef P
, const PreservedAnalyses
&) {
962 this->printAfterPassInvalidated(P
);
967 void OptNoneInstrumentation::registerCallbacks(
968 PassInstrumentationCallbacks
&PIC
) {
969 PIC
.registerShouldRunOptionalPassCallback(
970 [this](StringRef P
, Any IR
) { return this->shouldRun(P
, IR
); });
973 bool OptNoneInstrumentation::shouldRun(StringRef PassID
, Any IR
) {
974 const Function
**FPtr
= llvm::any_cast
<const Function
*>(&IR
);
975 const Function
*F
= FPtr
? *FPtr
: nullptr;
977 if (const auto **L
= llvm::any_cast
<const Loop
*>(&IR
))
978 F
= (*L
)->getHeader()->getParent();
980 bool ShouldRun
= !(F
&& F
->hasOptNone());
981 if (!ShouldRun
&& DebugLogging
) {
982 errs() << "Skipping pass " << PassID
<< " on " << F
->getName()
983 << " due to optnone attribute\n";
988 bool OptPassGateInstrumentation::shouldRun(StringRef PassName
, Any IR
) {
989 if (isIgnored(PassName
))
993 Context
.getOptPassGate().shouldRunPass(PassName
, getIRName(IR
));
994 if (!ShouldRun
&& !this->HasWrittenIR
&& !OptBisectPrintIRPath
.empty()) {
995 // FIXME: print IR if limit is higher than number of opt-bisect
997 this->HasWrittenIR
= true;
998 const Module
*M
= unwrapModule(IR
, /*Force=*/true);
999 assert((M
&& &M
->getContext() == &Context
) && "Missing/Mismatching Module");
1001 raw_fd_ostream
OS(OptBisectPrintIRPath
, EC
);
1003 report_fatal_error(errorCodeToError(EC
));
1004 M
->print(OS
, nullptr);
1009 void OptPassGateInstrumentation::registerCallbacks(
1010 PassInstrumentationCallbacks
&PIC
) {
1011 OptPassGate
&PassGate
= Context
.getOptPassGate();
1012 if (!PassGate
.isEnabled())
1015 PIC
.registerShouldRunOptionalPassCallback([this](StringRef PassName
, Any IR
) {
1016 return this->shouldRun(PassName
, IR
);
1020 raw_ostream
&PrintPassInstrumentation::print() {
1022 assert(Indent
>= 0);
1023 dbgs().indent(Indent
);
1028 void PrintPassInstrumentation::registerCallbacks(
1029 PassInstrumentationCallbacks
&PIC
) {
1033 std::vector
<StringRef
> SpecialPasses
;
1034 if (!Opts
.Verbose
) {
1035 SpecialPasses
.emplace_back("PassManager");
1036 SpecialPasses
.emplace_back("PassAdaptor");
1039 PIC
.registerBeforeSkippedPassCallback([this, SpecialPasses
](StringRef PassID
,
1041 assert(!isSpecialPass(PassID
, SpecialPasses
) &&
1042 "Unexpectedly skipping special pass");
1044 print() << "Skipping pass: " << PassID
<< " on " << getIRName(IR
) << "\n";
1046 PIC
.registerBeforeNonSkippedPassCallback([this, SpecialPasses
](
1047 StringRef PassID
, Any IR
) {
1048 if (isSpecialPass(PassID
, SpecialPasses
))
1052 OS
<< "Running pass: " << PassID
<< " on " << getIRName(IR
);
1053 if (const auto **F
= llvm::any_cast
<const Function
*>(&IR
)) {
1054 unsigned Count
= (*F
)->getInstructionCount();
1055 OS
<< " (" << Count
<< " instruction";
1059 } else if (const auto **C
=
1060 llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
)) {
1061 int Count
= (*C
)->size();
1062 OS
<< " (" << Count
<< " node";
1070 PIC
.registerAfterPassCallback(
1071 [this, SpecialPasses
](StringRef PassID
, Any IR
,
1072 const PreservedAnalyses
&) {
1073 if (isSpecialPass(PassID
, SpecialPasses
))
1078 PIC
.registerAfterPassInvalidatedCallback(
1079 [this, SpecialPasses
](StringRef PassID
, Any IR
) {
1080 if (isSpecialPass(PassID
, SpecialPasses
))
1086 if (!Opts
.SkipAnalyses
) {
1087 PIC
.registerBeforeAnalysisCallback([this](StringRef PassID
, Any IR
) {
1088 print() << "Running analysis: " << PassID
<< " on " << getIRName(IR
)
1092 PIC
.registerAfterAnalysisCallback(
1093 [this](StringRef PassID
, Any IR
) { Indent
-= 2; });
1094 PIC
.registerAnalysisInvalidatedCallback([this](StringRef PassID
, Any IR
) {
1095 print() << "Invalidating analysis: " << PassID
<< " on " << getIRName(IR
)
1098 PIC
.registerAnalysesClearedCallback([this](StringRef IRName
) {
1099 print() << "Clearing all analysis results for: " << IRName
<< "\n";
1104 PreservedCFGCheckerInstrumentation::CFG::CFG(const Function
*F
,
1105 bool TrackBBLifetime
) {
1106 if (TrackBBLifetime
)
1107 BBGuards
= DenseMap
<intptr_t, BBGuard
>(F
->size());
1108 for (const auto &BB
: *F
) {
1110 BBGuards
->try_emplace(intptr_t(&BB
), &BB
);
1111 for (const auto *Succ
: successors(&BB
)) {
1114 BBGuards
->try_emplace(intptr_t(Succ
), Succ
);
1119 static void printBBName(raw_ostream
&out
, const BasicBlock
*BB
) {
1120 if (BB
->hasName()) {
1121 out
<< BB
->getName() << "<" << BB
<< ">";
1125 if (!BB
->getParent()) {
1126 out
<< "unnamed_removed<" << BB
<< ">";
1130 if (BB
->isEntryBlock()) {
1132 << "<" << BB
<< ">";
1136 unsigned FuncOrderBlockNum
= 0;
1137 for (auto &FuncBB
: *BB
->getParent()) {
1140 FuncOrderBlockNum
++;
1142 out
<< "unnamed_" << FuncOrderBlockNum
<< "<" << BB
<< ">";
1145 void PreservedCFGCheckerInstrumentation::CFG::printDiff(raw_ostream
&out
,
1148 assert(!After
.isPoisoned());
1149 if (Before
.isPoisoned()) {
1150 out
<< "Some blocks were deleted\n";
1154 // Find and print graph differences.
1155 if (Before
.Graph
.size() != After
.Graph
.size())
1156 out
<< "Different number of non-leaf basic blocks: before="
1157 << Before
.Graph
.size() << ", after=" << After
.Graph
.size() << "\n";
1159 for (auto &BB
: Before
.Graph
) {
1160 auto BA
= After
.Graph
.find(BB
.first
);
1161 if (BA
== After
.Graph
.end()) {
1162 out
<< "Non-leaf block ";
1163 printBBName(out
, BB
.first
);
1164 out
<< " is removed (" << BB
.second
.size() << " successors)\n";
1168 for (auto &BA
: After
.Graph
) {
1169 auto BB
= Before
.Graph
.find(BA
.first
);
1170 if (BB
== Before
.Graph
.end()) {
1171 out
<< "Non-leaf block ";
1172 printBBName(out
, BA
.first
);
1173 out
<< " is added (" << BA
.second
.size() << " successors)\n";
1177 if (BB
->second
== BA
.second
)
1180 out
<< "Different successors of block ";
1181 printBBName(out
, BA
.first
);
1182 out
<< " (unordered):\n";
1183 out
<< "- before (" << BB
->second
.size() << "): ";
1184 for (auto &SuccB
: BB
->second
) {
1185 printBBName(out
, SuccB
.first
);
1186 if (SuccB
.second
!= 1)
1187 out
<< "(" << SuccB
.second
<< "), ";
1192 out
<< "- after (" << BA
.second
.size() << "): ";
1193 for (auto &SuccA
: BA
.second
) {
1194 printBBName(out
, SuccA
.first
);
1195 if (SuccA
.second
!= 1)
1196 out
<< "(" << SuccA
.second
<< "), ";
1204 // PreservedCFGCheckerInstrumentation uses PreservedCFGCheckerAnalysis to check
1205 // passes, that reported they kept CFG analyses up-to-date, did not actually
1206 // change CFG. This check is done as follows. Before every functional pass in
1207 // BeforeNonSkippedPassCallback a CFG snapshot (an instance of
1208 // PreservedCFGCheckerInstrumentation::CFG) is requested from
1209 // FunctionAnalysisManager as a result of PreservedCFGCheckerAnalysis. When the
1210 // functional pass finishes and reports that CFGAnalyses or AllAnalyses are
1211 // up-to-date then the cached result of PreservedCFGCheckerAnalysis (if
1212 // available) is checked to be equal to a freshly created CFG snapshot.
1213 struct PreservedCFGCheckerAnalysis
1214 : public AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
> {
1215 friend AnalysisInfoMixin
<PreservedCFGCheckerAnalysis
>;
1217 static AnalysisKey Key
;
1220 /// Provide the result type for this analysis pass.
1221 using Result
= PreservedCFGCheckerInstrumentation::CFG
;
1223 /// Run the analysis pass over a function and produce CFG.
1224 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1225 return Result(&F
, /* TrackBBLifetime */ true);
1229 AnalysisKey
PreservedCFGCheckerAnalysis::Key
;
1231 struct PreservedFunctionHashAnalysis
1232 : public AnalysisInfoMixin
<PreservedFunctionHashAnalysis
> {
1233 static AnalysisKey Key
;
1235 struct FunctionHash
{
1239 using Result
= FunctionHash
;
1241 Result
run(Function
&F
, FunctionAnalysisManager
&FAM
) {
1242 return Result
{StructuralHash(F
)};
1246 AnalysisKey
PreservedFunctionHashAnalysis::Key
;
1248 struct PreservedModuleHashAnalysis
1249 : public AnalysisInfoMixin
<PreservedModuleHashAnalysis
> {
1250 static AnalysisKey Key
;
1256 using Result
= ModuleHash
;
1258 Result
run(Module
&F
, ModuleAnalysisManager
&FAM
) {
1259 return Result
{StructuralHash(F
)};
1263 AnalysisKey
PreservedModuleHashAnalysis::Key
;
1265 bool PreservedCFGCheckerInstrumentation::CFG::invalidate(
1266 Function
&F
, const PreservedAnalyses
&PA
,
1267 FunctionAnalysisManager::Invalidator
&) {
1268 auto PAC
= PA
.getChecker
<PreservedCFGCheckerAnalysis
>();
1269 return !(PAC
.preserved() || PAC
.preservedSet
<AllAnalysesOn
<Function
>>() ||
1270 PAC
.preservedSet
<CFGAnalyses
>());
1273 static SmallVector
<Function
*, 1> GetFunctions(Any IR
) {
1274 SmallVector
<Function
*, 1> Functions
;
1276 if (const auto **MaybeF
= llvm::any_cast
<const Function
*>(&IR
)) {
1277 Functions
.push_back(*const_cast<Function
**>(MaybeF
));
1278 } else if (const auto **MaybeM
= llvm::any_cast
<const Module
*>(&IR
)) {
1279 for (Function
&F
: **const_cast<Module
**>(MaybeM
))
1280 Functions
.push_back(&F
);
1285 void PreservedCFGCheckerInstrumentation::registerCallbacks(
1286 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
&MAM
) {
1287 if (!VerifyAnalysisInvalidation
)
1290 bool Registered
= false;
1291 PIC
.registerBeforeNonSkippedPassCallback([this, &MAM
, Registered
](
1292 StringRef P
, Any IR
) mutable {
1293 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1294 assert(&PassStack
.emplace_back(P
));
1298 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1299 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1302 FAM
.registerPass([&] { return PreservedCFGCheckerAnalysis(); });
1303 FAM
.registerPass([&] { return PreservedFunctionHashAnalysis(); });
1304 MAM
.registerPass([&] { return PreservedModuleHashAnalysis(); });
1308 for (Function
*F
: GetFunctions(IR
)) {
1309 // Make sure a fresh CFG snapshot is available before the pass.
1310 FAM
.getResult
<PreservedCFGCheckerAnalysis
>(*F
);
1311 FAM
.getResult
<PreservedFunctionHashAnalysis
>(*F
);
1314 if (auto *MaybeM
= llvm::any_cast
<const Module
*>(&IR
)) {
1315 Module
&M
= **const_cast<Module
**>(MaybeM
);
1316 MAM
.getResult
<PreservedModuleHashAnalysis
>(M
);
1320 PIC
.registerAfterPassInvalidatedCallback(
1321 [this](StringRef P
, const PreservedAnalyses
&PassPA
) {
1322 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1323 assert(PassStack
.pop_back_val() == P
&&
1324 "Before and After callbacks must correspond");
1329 PIC
.registerAfterPassCallback([this, &MAM
](StringRef P
, Any IR
,
1330 const PreservedAnalyses
&PassPA
) {
1331 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1332 assert(PassStack
.pop_back_val() == P
&&
1333 "Before and After callbacks must correspond");
1337 // We have to get the FAM via the MAM, rather than directly use a passed in
1338 // FAM because if MAM has not cached the FAM, it won't invalidate function
1340 auto &FAM
= MAM
.getResult
<FunctionAnalysisManagerModuleProxy
>(
1341 *const_cast<Module
*>(unwrapModule(IR
, /*Force=*/true)))
1344 for (Function
*F
: GetFunctions(IR
)) {
1345 if (auto *HashBefore
=
1346 FAM
.getCachedResult
<PreservedFunctionHashAnalysis
>(*F
)) {
1347 if (HashBefore
->Hash
!= StructuralHash(*F
)) {
1348 report_fatal_error(formatv(
1349 "Function @{0} changed by {1} without invalidating analyses",
1354 auto CheckCFG
= [](StringRef Pass
, StringRef FuncName
,
1355 const CFG
&GraphBefore
, const CFG
&GraphAfter
) {
1356 if (GraphAfter
== GraphBefore
)
1360 << "Error: " << Pass
1361 << " does not invalidate CFG analyses but CFG changes detected in "
1363 << FuncName
<< ":\n";
1364 CFG::printDiff(dbgs(), GraphBefore
, GraphAfter
);
1365 report_fatal_error(Twine("CFG unexpectedly changed by ", Pass
));
1368 if (auto *GraphBefore
=
1369 FAM
.getCachedResult
<PreservedCFGCheckerAnalysis
>(*F
))
1370 CheckCFG(P
, F
->getName(), *GraphBefore
,
1371 CFG(F
, /* TrackBBLifetime */ false));
1373 if (auto *MaybeM
= llvm::any_cast
<const Module
*>(&IR
)) {
1374 Module
&M
= **const_cast<Module
**>(MaybeM
);
1375 if (auto *HashBefore
=
1376 MAM
.getCachedResult
<PreservedModuleHashAnalysis
>(M
)) {
1377 if (HashBefore
->Hash
!= StructuralHash(M
)) {
1378 report_fatal_error(formatv(
1379 "Module changed by {0} without invalidating analyses", P
));
1386 void VerifyInstrumentation::registerCallbacks(
1387 PassInstrumentationCallbacks
&PIC
) {
1388 PIC
.registerAfterPassCallback(
1389 [this](StringRef P
, Any IR
, const PreservedAnalyses
&PassPA
) {
1390 if (isIgnored(P
) || P
== "VerifierPass")
1392 const Function
**FPtr
= llvm::any_cast
<const Function
*>(&IR
);
1393 const Function
*F
= FPtr
? *FPtr
: nullptr;
1395 if (const auto **L
= llvm::any_cast
<const Loop
*>(&IR
))
1396 F
= (*L
)->getHeader()->getParent();
1401 dbgs() << "Verifying function " << F
->getName() << "\n";
1403 if (verifyFunction(*F
, &errs()))
1404 report_fatal_error("Broken function found, compilation aborted!");
1406 const Module
**MPtr
= llvm::any_cast
<const Module
*>(&IR
);
1407 const Module
*M
= MPtr
? *MPtr
: nullptr;
1409 if (const auto **C
=
1410 llvm::any_cast
<const LazyCallGraph::SCC
*>(&IR
))
1411 M
= (*C
)->begin()->getFunction().getParent();
1416 dbgs() << "Verifying module " << M
->getName() << "\n";
1418 if (verifyModule(*M
, &errs()))
1419 report_fatal_error("Broken module found, compilation aborted!");
1425 InLineChangePrinter::~InLineChangePrinter() = default;
1427 void InLineChangePrinter::generateIRRepresentation(Any IR
,
1429 IRDataT
<EmptyData
> &D
) {
1430 IRComparer
<EmptyData
>::analyzeIR(IR
, D
);
1433 void InLineChangePrinter::handleAfter(StringRef PassID
, std::string
&Name
,
1434 const IRDataT
<EmptyData
> &Before
,
1435 const IRDataT
<EmptyData
> &After
,
1437 SmallString
<20> Banner
=
1438 formatv("*** IR Dump After {0} on {1} ***\n", PassID
, Name
);
1440 IRComparer
<EmptyData
>(Before
, After
)
1441 .compare(getModuleForComparison(IR
),
1442 [&](bool InModule
, unsigned Minor
,
1443 const FuncDataT
<EmptyData
> &Before
,
1444 const FuncDataT
<EmptyData
> &After
) -> void {
1445 handleFunctionCompare(Name
, "", PassID
, " on ", InModule
,
1446 Minor
, Before
, After
);
1451 void InLineChangePrinter::handleFunctionCompare(
1452 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
1453 bool InModule
, unsigned Minor
, const FuncDataT
<EmptyData
> &Before
,
1454 const FuncDataT
<EmptyData
> &After
) {
1455 // Print a banner when this is being shown in the context of a module
1457 Out
<< "\n*** IR for function " << Name
<< " ***\n";
1459 FuncDataT
<EmptyData
>::report(
1461 [&](const BlockDataT
<EmptyData
> *B
, const BlockDataT
<EmptyData
> *A
) {
1462 StringRef BStr
= B
? B
->getBody() : "\n";
1463 StringRef AStr
= A
? A
->getBody() : "\n";
1464 const std::string Removed
=
1465 UseColour
? "\033[31m-%l\033[0m\n" : "-%l\n";
1466 const std::string Added
= UseColour
? "\033[32m+%l\033[0m\n" : "+%l\n";
1467 const std::string NoChange
= " %l\n";
1468 Out
<< doSystemDiff(BStr
, AStr
, Removed
, Added
, NoChange
);
1472 void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks
&PIC
) {
1473 if (PrintChanged
== ChangePrinter::DiffVerbose
||
1474 PrintChanged
== ChangePrinter::DiffQuiet
||
1475 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
1476 PrintChanged
== ChangePrinter::ColourDiffQuiet
)
1477 TextChangeReporter
<IRDataT
<EmptyData
>>::registerRequiredCallbacks(PIC
);
1480 TimeProfilingPassesHandler::TimeProfilingPassesHandler() {}
1482 void TimeProfilingPassesHandler::registerCallbacks(
1483 PassInstrumentationCallbacks
&PIC
) {
1484 if (!getTimeTraceProfilerInstance())
1486 PIC
.registerBeforeNonSkippedPassCallback(
1487 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1488 PIC
.registerAfterPassCallback(
1489 [this](StringRef P
, Any IR
, const PreservedAnalyses
&) {
1490 this->runAfterPass();
1493 PIC
.registerAfterPassInvalidatedCallback(
1494 [this](StringRef P
, const PreservedAnalyses
&) { this->runAfterPass(); },
1496 PIC
.registerBeforeAnalysisCallback(
1497 [this](StringRef P
, Any IR
) { this->runBeforePass(P
, IR
); });
1498 PIC
.registerAfterAnalysisCallback(
1499 [this](StringRef P
, Any IR
) { this->runAfterPass(); }, true);
1502 void TimeProfilingPassesHandler::runBeforePass(StringRef PassID
, Any IR
) {
1503 timeTraceProfilerBegin(PassID
, getIRName(IR
));
1506 void TimeProfilingPassesHandler::runAfterPass() { timeTraceProfilerEnd(); }
1511 class DotCfgDiffDisplayGraph
;
1513 // Base class for a node or edge in the dot-cfg-changes graph.
1514 class DisplayElement
{
1516 // Is this in before, after, or both?
1517 StringRef
getColour() const { return Colour
; }
1520 DisplayElement(StringRef Colour
) : Colour(Colour
) {}
1521 const StringRef Colour
;
1524 // An edge representing a transition between basic blocks in the
1525 // dot-cfg-changes graph.
1526 class DisplayEdge
: public DisplayElement
{
1528 DisplayEdge(std::string Value
, DisplayNode
&Node
, StringRef Colour
)
1529 : DisplayElement(Colour
), Value(Value
), Node(Node
) {}
1530 // The value on which the transition is made.
1531 std::string
getValue() const { return Value
; }
1532 // The node (representing a basic block) reached by this transition.
1533 const DisplayNode
&getDestinationNode() const { return Node
; }
1537 const DisplayNode
&Node
;
1540 // A node in the dot-cfg-changes graph which represents a basic block.
1541 class DisplayNode
: public DisplayElement
{
1543 // \p C is the content for the node, \p T indicates the colour for the
1544 // outline of the node
1545 DisplayNode(std::string Content
, StringRef Colour
)
1546 : DisplayElement(Colour
), Content(Content
) {}
1548 // Iterator to the child nodes. Required by GraphWriter.
1549 using ChildIterator
= std::unordered_set
<DisplayNode
*>::const_iterator
;
1550 ChildIterator
children_begin() const { return Children
.cbegin(); }
1551 ChildIterator
children_end() const { return Children
.cend(); }
1553 // Iterator for the edges. Required by GraphWriter.
1554 using EdgeIterator
= std::vector
<DisplayEdge
*>::const_iterator
;
1555 EdgeIterator
edges_begin() const { return EdgePtrs
.cbegin(); }
1556 EdgeIterator
edges_end() const { return EdgePtrs
.cend(); }
1558 // Create an edge to \p Node on value \p Value, with colour \p Colour.
1559 void createEdge(StringRef Value
, DisplayNode
&Node
, StringRef Colour
);
1561 // Return the content of this node.
1562 std::string
getContent() const { return Content
; }
1564 // Return the edge to node \p S.
1565 const DisplayEdge
&getEdge(const DisplayNode
&To
) const {
1566 assert(EdgeMap
.find(&To
) != EdgeMap
.end() && "Expected to find edge.");
1567 return *EdgeMap
.find(&To
)->second
;
1570 // Return the value for the transition to basic block \p S.
1571 // Required by GraphWriter.
1572 std::string
getEdgeSourceLabel(const DisplayNode
&Sink
) const {
1573 return getEdge(Sink
).getValue();
1576 void createEdgeMap();
1579 const std::string Content
;
1581 // Place to collect all of the edges. Once they are all in the vector,
1582 // the vector will not reallocate so then we can use pointers to them,
1583 // which are required by the graph writing routines.
1584 std::vector
<DisplayEdge
> Edges
;
1586 std::vector
<DisplayEdge
*> EdgePtrs
;
1587 std::unordered_set
<DisplayNode
*> Children
;
1588 std::unordered_map
<const DisplayNode
*, const DisplayEdge
*> EdgeMap
;
1590 // Safeguard adding of edges.
1591 bool AllEdgesCreated
= false;
1594 // Class representing a difference display (corresponds to a pdf file).
1595 class DotCfgDiffDisplayGraph
{
1597 DotCfgDiffDisplayGraph(std::string Name
) : GraphName(Name
) {}
1599 // Generate the file into \p DotFile.
1600 void generateDotFile(StringRef DotFile
);
1602 // Iterator to the nodes. Required by GraphWriter.
1603 using NodeIterator
= std::vector
<DisplayNode
*>::const_iterator
;
1604 NodeIterator
nodes_begin() const {
1605 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1606 return NodePtrs
.cbegin();
1608 NodeIterator
nodes_end() const {
1609 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1610 return NodePtrs
.cend();
1613 // Record the index of the entry node. At this point, we can build up
1614 // vectors of pointers that are required by the graph routines.
1615 void setEntryNode(unsigned N
) {
1616 // At this point, there will be no new nodes.
1617 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1618 NodeGenerationComplete
= true;
1619 for (auto &N
: Nodes
)
1620 NodePtrs
.emplace_back(&N
);
1622 EntryNode
= NodePtrs
[N
];
1626 void createNode(std::string C
, StringRef Colour
) {
1627 assert(!NodeGenerationComplete
&& "Unexpected node creation");
1628 Nodes
.emplace_back(C
, Colour
);
1630 // Return the node at index \p N to avoid problems with vectors reallocating.
1631 DisplayNode
&getNode(unsigned N
) {
1632 assert(N
< Nodes
.size() && "Node is out of bounds");
1635 unsigned size() const {
1636 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1637 return Nodes
.size();
1640 // Return the name of the graph. Required by GraphWriter.
1641 std::string
getGraphName() const { return GraphName
; }
1643 // Return the string representing the differences for basic block \p Node.
1644 // Required by GraphWriter.
1645 std::string
getNodeLabel(const DisplayNode
&Node
) const {
1646 return Node
.getContent();
1649 // Return a string with colour information for Dot. Required by GraphWriter.
1650 std::string
getNodeAttributes(const DisplayNode
&Node
) const {
1651 return attribute(Node
.getColour());
1654 // Return a string with colour information for Dot. Required by GraphWriter.
1655 std::string
getEdgeColorAttr(const DisplayNode
&From
,
1656 const DisplayNode
&To
) const {
1657 return attribute(From
.getEdge(To
).getColour());
1660 // Get the starting basic block. Required by GraphWriter.
1661 DisplayNode
*getEntryNode() const {
1662 assert(NodeGenerationComplete
&& "Unexpected children iterator creation");
1667 // Return the string containing the colour to use as a Dot attribute.
1668 std::string
attribute(StringRef Colour
) const {
1669 return "color=" + Colour
.str();
1672 bool NodeGenerationComplete
= false;
1673 const std::string GraphName
;
1674 std::vector
<DisplayNode
> Nodes
;
1675 std::vector
<DisplayNode
*> NodePtrs
;
1676 DisplayNode
*EntryNode
= nullptr;
1679 void DisplayNode::createEdge(StringRef Value
, DisplayNode
&Node
,
1681 assert(!AllEdgesCreated
&& "Expected to be able to still create edges.");
1682 Edges
.emplace_back(Value
.str(), Node
, Colour
);
1683 Children
.insert(&Node
);
1686 void DisplayNode::createEdgeMap() {
1687 // No more edges will be added so we can now use pointers to the edges
1688 // as the vector will not grow and reallocate.
1689 AllEdgesCreated
= true;
1690 for (auto &E
: Edges
)
1691 EdgeMap
.insert({&E
.getDestinationNode(), &E
});
1694 class DotCfgDiffNode
;
1697 // A class representing a basic block in the Dot difference graph.
1698 class DotCfgDiffNode
{
1700 DotCfgDiffNode() = delete;
1702 // Create a node in Dot difference graph \p G representing the basic block
1703 // represented by \p BD with colour \p Colour (where it exists).
1704 DotCfgDiffNode(DotCfgDiff
&G
, unsigned N
, const BlockDataT
<DCData
> &BD
,
1706 : Graph(G
), N(N
), Data
{&BD
, nullptr}, Colour(Colour
) {}
1707 DotCfgDiffNode(const DotCfgDiffNode
&DN
)
1708 : Graph(DN
.Graph
), N(DN
.N
), Data
{DN
.Data
[0], DN
.Data
[1]},
1709 Colour(DN
.Colour
), EdgesMap(DN
.EdgesMap
), Children(DN
.Children
),
1712 unsigned getIndex() const { return N
; }
1714 // The label of the basic block
1715 StringRef
getLabel() const {
1716 assert(Data
[0] && "Expected Data[0] to be set.");
1717 return Data
[0]->getLabel();
1719 // Return the colour for this block
1720 StringRef
getColour() const { return Colour
; }
1721 // Change this basic block from being only in before to being common.
1722 // Save the pointer to \p Other.
1723 void setCommon(const BlockDataT
<DCData
> &Other
) {
1724 assert(!Data
[1] && "Expected only one block datum");
1726 Colour
= CommonColour
;
1728 // Add an edge to \p E of colour {\p Value, \p Colour}.
1729 void addEdge(unsigned E
, StringRef Value
, StringRef Colour
) {
1730 // This is a new edge or it is an edge being made common.
1731 assert((EdgesMap
.count(E
) == 0 || Colour
== CommonColour
) &&
1732 "Unexpected edge count and color.");
1733 EdgesMap
[E
] = {Value
.str(), Colour
};
1735 // Record the children and create edges.
1736 void finalize(DotCfgDiff
&G
);
1738 // Return the colour of the edge to node \p S.
1739 StringRef
getEdgeColour(const unsigned S
) const {
1740 assert(EdgesMap
.count(S
) == 1 && "Expected to find edge.");
1741 return EdgesMap
.at(S
).second
;
1744 // Return the string representing the basic block.
1745 std::string
getBodyContent() const;
1747 void createDisplayEdges(DotCfgDiffDisplayGraph
&Graph
, unsigned DisplayNode
,
1748 std::map
<const unsigned, unsigned> &NodeMap
) const;
1753 const BlockDataT
<DCData
> *Data
[2];
1755 std::map
<const unsigned, std::pair
<std::string
, StringRef
>> EdgesMap
;
1756 std::vector
<unsigned> Children
;
1757 std::vector
<unsigned> Edges
;
1760 // Class representing the difference graph between two functions.
1763 // \p Title is the title given to the graph. \p EntryNodeName is the
1764 // entry node for the function. \p Before and \p After are the before
1765 // after versions of the function, respectively. \p Dir is the directory
1766 // in which to store the results.
1767 DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1768 const FuncDataT
<DCData
> &After
);
1770 DotCfgDiff(const DotCfgDiff
&) = delete;
1771 DotCfgDiff
&operator=(const DotCfgDiff
&) = delete;
1773 DotCfgDiffDisplayGraph
createDisplayGraph(StringRef Title
,
1774 StringRef EntryNodeName
);
1776 // Return a string consisting of the labels for the \p Source and \p Sink.
1777 // The combination allows distinguishing changing transitions on the
1778 // same value (ie, a transition went to X before and goes to Y after).
1779 // Required by GraphWriter.
1780 StringRef
getEdgeSourceLabel(const unsigned &Source
,
1781 const unsigned &Sink
) const {
1783 getNode(Source
).getLabel().str() + " " + getNode(Sink
).getLabel().str();
1784 assert(EdgeLabels
.count(S
) == 1 && "Expected to find edge label.");
1785 return EdgeLabels
.find(S
)->getValue();
1788 // Return the number of basic blocks (nodes). Required by GraphWriter.
1789 unsigned size() const { return Nodes
.size(); }
1791 const DotCfgDiffNode
&getNode(unsigned N
) const {
1792 assert(N
< Nodes
.size() && "Unexpected index for node reference");
1797 // Return the string surrounded by HTML to make it the appropriate colour.
1798 std::string
colourize(std::string S
, StringRef Colour
) const;
1800 void createNode(StringRef Label
, const BlockDataT
<DCData
> &BD
, StringRef C
) {
1801 unsigned Pos
= Nodes
.size();
1802 Nodes
.emplace_back(*this, Pos
, BD
, C
);
1803 NodePosition
.insert({Label
, Pos
});
1806 // TODO Nodes should probably be a StringMap<DotCfgDiffNode> after the
1807 // display graph is separated out, which would remove the need for
1809 std::vector
<DotCfgDiffNode
> Nodes
;
1810 StringMap
<unsigned> NodePosition
;
1811 const std::string GraphName
;
1813 StringMap
<std::string
> EdgeLabels
;
1816 std::string
DotCfgDiffNode::getBodyContent() const {
1817 if (Colour
== CommonColour
) {
1818 assert(Data
[1] && "Expected Data[1] to be set.");
1821 for (unsigned I
= 0; I
< 2; ++I
) {
1822 SR
[I
] = Data
[I
]->getBody();
1823 // drop initial '\n' if present
1824 if (SR
[I
][0] == '\n')
1825 SR
[I
] = SR
[I
].drop_front();
1826 // drop predecessors as they can be big and are redundant
1827 SR
[I
] = SR
[I
].drop_until([](char C
) { return C
== '\n'; }).drop_front();
1830 SmallString
<80> OldLineFormat
= formatv(
1831 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", BeforeColour
);
1832 SmallString
<80> NewLineFormat
= formatv(
1833 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", AfterColour
);
1834 SmallString
<80> UnchangedLineFormat
= formatv(
1835 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", CommonColour
);
1836 std::string Diff
= Data
[0]->getLabel().str();
1837 Diff
+= ":\n<BR align=\"left\"/>" +
1838 doSystemDiff(makeHTMLReady(SR
[0]), makeHTMLReady(SR
[1]),
1839 OldLineFormat
, NewLineFormat
, UnchangedLineFormat
);
1841 // Diff adds in some empty colour changes which are not valid HTML
1842 // so remove them. Colours are all lowercase alpha characters (as
1843 // listed in https://graphviz.org/pdf/dotguide.pdf).
1844 Regex
R("<FONT COLOR=\"\\w+\"></FONT>");
1847 std::string S
= R
.sub("", Diff
, &Error
);
1854 llvm_unreachable("Should not get here");
1857 // Put node out in the appropriate colour.
1858 assert(!Data
[1] && "Data[1] is set unexpectedly.");
1859 std::string Body
= makeHTMLReady(Data
[0]->getBody());
1860 const StringRef BS
= Body
;
1862 // Drop leading newline, if present.
1863 if (BS
.front() == '\n')
1864 BS1
= BS1
.drop_front(1);
1866 StringRef Label
= BS1
.take_until([](char C
) { return C
== ':'; });
1867 // drop predecessors as they can be big and are redundant
1868 BS1
= BS1
.drop_until([](char C
) { return C
== '\n'; }).drop_front();
1870 std::string S
= "<FONT COLOR=\"" + Colour
.str() + "\">" + Label
.str() + ":";
1872 // align each line to the left.
1873 while (BS1
.size()) {
1874 S
.append("<BR align=\"left\"/>");
1875 StringRef Line
= BS1
.take_until([](char C
) { return C
== '\n'; });
1876 S
.append(Line
.str());
1877 BS1
= BS1
.drop_front(Line
.size() + 1);
1879 S
.append("<BR align=\"left\"/></FONT>");
1883 std::string
DotCfgDiff::colourize(std::string S
, StringRef Colour
) const {
1884 if (S
.length() == 0)
1886 return "<FONT COLOR=\"" + Colour
.str() + "\">" + S
+ "</FONT>";
1889 DotCfgDiff::DotCfgDiff(StringRef Title
, const FuncDataT
<DCData
> &Before
,
1890 const FuncDataT
<DCData
> &After
)
1891 : GraphName(Title
.str()) {
1892 StringMap
<StringRef
> EdgesMap
;
1894 // Handle each basic block in the before IR.
1895 for (auto &B
: Before
.getData()) {
1896 StringRef Label
= B
.getKey();
1897 const BlockDataT
<DCData
> &BD
= B
.getValue();
1898 createNode(Label
, BD
, BeforeColour
);
1900 // Create transitions with names made up of the from block label, the value
1901 // on which the transition is made and the to block label.
1902 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
1903 E
= BD
.getData().end();
1904 Sink
!= E
; ++Sink
) {
1905 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
1906 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
1907 EdgesMap
.insert({Key
, BeforeColour
});
1911 // Handle each basic block in the after IR
1912 for (auto &A
: After
.getData()) {
1913 StringRef Label
= A
.getKey();
1914 const BlockDataT
<DCData
> &BD
= A
.getValue();
1915 unsigned C
= NodePosition
.count(Label
);
1917 // This only exists in the after IR. Create the node.
1918 createNode(Label
, BD
, AfterColour
);
1920 assert(C
== 1 && "Unexpected multiple nodes.");
1921 Nodes
[NodePosition
[Label
]].setCommon(BD
);
1923 // Add in the edges between the nodes (as common or only in after).
1924 for (StringMap
<std::string
>::const_iterator Sink
= BD
.getData().begin(),
1925 E
= BD
.getData().end();
1926 Sink
!= E
; ++Sink
) {
1927 std::string Key
= (Label
+ " " + Sink
->getKey().str()).str() + " " +
1928 BD
.getData().getSuccessorLabel(Sink
->getKey()).str();
1929 unsigned C
= EdgesMap
.count(Key
);
1931 EdgesMap
.insert({Key
, AfterColour
});
1933 EdgesMap
[Key
] = CommonColour
;
1938 // Now go through the map of edges and add them to the node.
1939 for (auto &E
: EdgesMap
) {
1940 // Extract the source, sink and value from the edge key.
1941 StringRef S
= E
.getKey();
1942 auto SP1
= S
.rsplit(' ');
1943 auto &SourceSink
= SP1
.first
;
1944 auto SP2
= SourceSink
.split(' ');
1945 StringRef Source
= SP2
.first
;
1946 StringRef Sink
= SP2
.second
;
1947 StringRef Value
= SP1
.second
;
1949 assert(NodePosition
.count(Source
) == 1 && "Expected to find node.");
1950 DotCfgDiffNode
&SourceNode
= Nodes
[NodePosition
[Source
]];
1951 assert(NodePosition
.count(Sink
) == 1 && "Expected to find node.");
1952 unsigned SinkNode
= NodePosition
[Sink
];
1953 StringRef Colour
= E
.second
;
1955 // Look for an edge from Source to Sink
1956 if (EdgeLabels
.count(SourceSink
) == 0)
1957 EdgeLabels
.insert({SourceSink
, colourize(Value
.str(), Colour
)});
1959 StringRef V
= EdgeLabels
.find(SourceSink
)->getValue();
1960 std::string NV
= colourize(V
.str() + " " + Value
.str(), Colour
);
1961 Colour
= CommonColour
;
1962 EdgeLabels
[SourceSink
] = NV
;
1964 SourceNode
.addEdge(SinkNode
, Value
, Colour
);
1966 for (auto &I
: Nodes
)
1970 DotCfgDiffDisplayGraph
DotCfgDiff::createDisplayGraph(StringRef Title
,
1971 StringRef EntryNodeName
) {
1972 assert(NodePosition
.count(EntryNodeName
) == 1 &&
1973 "Expected to find entry block in map.");
1974 unsigned Entry
= NodePosition
[EntryNodeName
];
1975 assert(Entry
< Nodes
.size() && "Expected to find entry node");
1976 DotCfgDiffDisplayGraph
G(Title
.str());
1978 std::map
<const unsigned, unsigned> NodeMap
;
1980 int EntryIndex
= -1;
1982 for (auto &I
: Nodes
) {
1983 if (I
.getIndex() == Entry
)
1985 G
.createNode(I
.getBodyContent(), I
.getColour());
1986 NodeMap
.insert({I
.getIndex(), Index
++});
1988 assert(EntryIndex
>= 0 && "Expected entry node index to be set.");
1989 G
.setEntryNode(EntryIndex
);
1991 for (auto &I
: NodeMap
) {
1992 unsigned SourceNode
= I
.first
;
1993 unsigned DisplayNode
= I
.second
;
1994 getNode(SourceNode
).createDisplayEdges(G
, DisplayNode
, NodeMap
);
1999 void DotCfgDiffNode::createDisplayEdges(
2000 DotCfgDiffDisplayGraph
&DisplayGraph
, unsigned DisplayNodeIndex
,
2001 std::map
<const unsigned, unsigned> &NodeMap
) const {
2003 DisplayNode
&SourceDisplayNode
= DisplayGraph
.getNode(DisplayNodeIndex
);
2005 for (auto I
: Edges
) {
2006 unsigned SinkNodeIndex
= I
;
2007 StringRef Colour
= getEdgeColour(SinkNodeIndex
);
2008 const DotCfgDiffNode
*SinkNode
= &Graph
.getNode(SinkNodeIndex
);
2010 StringRef Label
= Graph
.getEdgeSourceLabel(getIndex(), SinkNodeIndex
);
2011 DisplayNode
&SinkDisplayNode
= DisplayGraph
.getNode(SinkNode
->getIndex());
2012 SourceDisplayNode
.createEdge(Label
, SinkDisplayNode
, Colour
);
2014 SourceDisplayNode
.createEdgeMap();
2017 void DotCfgDiffNode::finalize(DotCfgDiff
&G
) {
2018 for (auto E
: EdgesMap
) {
2019 Children
.emplace_back(E
.first
);
2020 Edges
.emplace_back(E
.first
);
2028 template <> struct GraphTraits
<DotCfgDiffDisplayGraph
*> {
2029 using NodeRef
= const DisplayNode
*;
2030 using ChildIteratorType
= DisplayNode::ChildIterator
;
2031 using nodes_iterator
= DotCfgDiffDisplayGraph::NodeIterator
;
2032 using EdgeRef
= const DisplayEdge
*;
2033 using ChildEdgeIterator
= DisplayNode::EdgeIterator
;
2035 static NodeRef
getEntryNode(const DotCfgDiffDisplayGraph
*G
) {
2036 return G
->getEntryNode();
2038 static ChildIteratorType
child_begin(NodeRef N
) {
2039 return N
->children_begin();
2041 static ChildIteratorType
child_end(NodeRef N
) { return N
->children_end(); }
2042 static nodes_iterator
nodes_begin(const DotCfgDiffDisplayGraph
*G
) {
2043 return G
->nodes_begin();
2045 static nodes_iterator
nodes_end(const DotCfgDiffDisplayGraph
*G
) {
2046 return G
->nodes_end();
2048 static ChildEdgeIterator
child_edge_begin(NodeRef N
) {
2049 return N
->edges_begin();
2051 static ChildEdgeIterator
child_edge_end(NodeRef N
) { return N
->edges_end(); }
2052 static NodeRef
edge_dest(EdgeRef E
) { return &E
->getDestinationNode(); }
2053 static unsigned size(const DotCfgDiffDisplayGraph
*G
) { return G
->size(); }
2057 struct DOTGraphTraits
<DotCfgDiffDisplayGraph
*> : public DefaultDOTGraphTraits
{
2058 explicit DOTGraphTraits(bool Simple
= false)
2059 : DefaultDOTGraphTraits(Simple
) {}
2061 static bool renderNodesUsingHTML() { return true; }
2062 static std::string
getGraphName(const DotCfgDiffDisplayGraph
*DiffData
) {
2063 return DiffData
->getGraphName();
2066 getGraphProperties(const DotCfgDiffDisplayGraph
*DiffData
) {
2067 return "\tsize=\"190, 190\";\n";
2069 static std::string
getNodeLabel(const DisplayNode
*Node
,
2070 const DotCfgDiffDisplayGraph
*DiffData
) {
2071 return DiffData
->getNodeLabel(*Node
);
2073 static std::string
getNodeAttributes(const DisplayNode
*Node
,
2074 const DotCfgDiffDisplayGraph
*DiffData
) {
2075 return DiffData
->getNodeAttributes(*Node
);
2077 static std::string
getEdgeSourceLabel(const DisplayNode
*From
,
2078 DisplayNode::ChildIterator
&To
) {
2079 return From
->getEdgeSourceLabel(**To
);
2081 static std::string
getEdgeAttributes(const DisplayNode
*From
,
2082 DisplayNode::ChildIterator
&To
,
2083 const DotCfgDiffDisplayGraph
*DiffData
) {
2084 return DiffData
->getEdgeColorAttr(*From
, **To
);
2092 void DotCfgDiffDisplayGraph::generateDotFile(StringRef DotFile
) {
2094 raw_fd_ostream
OutStream(DotFile
, EC
);
2096 errs() << "Error: " << EC
.message() << "\n";
2099 WriteGraph(OutStream
, this, false);
2108 DCData::DCData(const BasicBlock
&B
) {
2109 // Build up transition labels.
2110 const Instruction
*Term
= B
.getTerminator();
2111 if (const BranchInst
*Br
= dyn_cast
<const BranchInst
>(Term
))
2112 if (Br
->isUnconditional())
2113 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "");
2115 addSuccessorLabel(Br
->getSuccessor(0)->getName().str(), "true");
2116 addSuccessorLabel(Br
->getSuccessor(1)->getName().str(), "false");
2118 else if (const SwitchInst
*Sw
= dyn_cast
<const SwitchInst
>(Term
)) {
2119 addSuccessorLabel(Sw
->case_default()->getCaseSuccessor()->getName().str(),
2121 for (auto &C
: Sw
->cases()) {
2122 assert(C
.getCaseValue() && "Expected to find case value.");
2123 SmallString
<20> Value
= formatv("{0}", C
.getCaseValue()->getSExtValue());
2124 addSuccessorLabel(C
.getCaseSuccessor()->getName().str(), Value
);
2127 for (const_succ_iterator I
= succ_begin(&B
), E
= succ_end(&B
); I
!= E
; ++I
)
2128 addSuccessorLabel((*I
)->getName().str(), "");
2131 DotCfgChangeReporter::DotCfgChangeReporter(bool Verbose
)
2132 : ChangeReporter
<IRDataT
<DCData
>>(Verbose
) {}
2134 void DotCfgChangeReporter::handleFunctionCompare(
2135 StringRef Name
, StringRef Prefix
, StringRef PassID
, StringRef Divider
,
2136 bool InModule
, unsigned Minor
, const FuncDataT
<DCData
> &Before
,
2137 const FuncDataT
<DCData
> &After
) {
2138 assert(HTML
&& "Expected outstream to be set");
2139 SmallString
<8> Extender
;
2140 SmallString
<8> Number
;
2141 // Handle numbering and file names.
2143 Extender
= formatv("{0}_{1}", N
, Minor
);
2144 Number
= formatv("{0}.{1}", N
, Minor
);
2146 Extender
= formatv("{0}", N
);
2147 Number
= formatv("{0}", N
);
2149 // Create a temporary file name for the dot file.
2150 SmallVector
<char, 128> SV
;
2151 sys::fs::createUniquePath("cfgdot-%%%%%%.dot", SV
, true);
2152 std::string DotFile
= Twine(SV
).str();
2154 SmallString
<20> PDFFileName
= formatv("diff_{0}.pdf", Extender
);
2155 SmallString
<200> Text
;
2157 Text
= formatv("{0}.{1}{2}{3}{4}", Number
, Prefix
, makeHTMLReady(PassID
),
2160 DotCfgDiff
Diff(Text
, Before
, After
);
2161 std::string EntryBlockName
= After
.getEntryBlockName();
2162 // Use the before entry block if the after entry block was removed.
2163 if (EntryBlockName
== "")
2164 EntryBlockName
= Before
.getEntryBlockName();
2165 assert(EntryBlockName
!= "" && "Expected to find entry block");
2167 DotCfgDiffDisplayGraph DG
= Diff
.createDisplayGraph(Text
, EntryBlockName
);
2168 DG
.generateDotFile(DotFile
);
2170 *HTML
<< genHTML(Text
, DotFile
, PDFFileName
);
2171 std::error_code EC
= sys::fs::remove(DotFile
);
2173 errs() << "Error: " << EC
.message() << "\n";
2176 std::string
DotCfgChangeReporter::genHTML(StringRef Text
, StringRef DotFile
,
2177 StringRef PDFFileName
) {
2178 SmallString
<20> PDFFile
= formatv("{0}/{1}", DotCfgDir
, PDFFileName
);
2179 // Create the PDF file.
2180 static ErrorOr
<std::string
> DotExe
= sys::findProgramByName(DotBinary
);
2182 return "Unable to find dot executable.";
2184 StringRef Args
[] = {DotBinary
, "-Tpdf", "-o", PDFFile
, DotFile
};
2185 int Result
= sys::ExecuteAndWait(*DotExe
, Args
, std::nullopt
);
2187 return "Error executing system dot.";
2189 // Create the HTML tag refering to the PDF file.
2190 SmallString
<200> S
= formatv(
2191 " <a href=\"{0}\" target=\"_blank\">{1}</a><br/>\n", PDFFileName
, Text
);
2195 void DotCfgChangeReporter::handleInitialIR(Any IR
) {
2196 assert(HTML
&& "Expected outstream to be set");
2197 *HTML
<< "<button type=\"button\" class=\"collapsible\">0. "
2198 << "Initial IR (by function)</button>\n"
2199 << "<div class=\"content\">\n"
2201 // Create representation of IR
2202 IRDataT
<DCData
> Data
;
2203 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2204 // Now compare it against itself, which will have everything the
2205 // same and will generate the files.
2206 IRComparer
<DCData
>(Data
, Data
)
2207 .compare(getModuleForComparison(IR
),
2208 [&](bool InModule
, unsigned Minor
,
2209 const FuncDataT
<DCData
> &Before
,
2210 const FuncDataT
<DCData
> &After
) -> void {
2211 handleFunctionCompare("", " ", "Initial IR", "", InModule
,
2212 Minor
, Before
, After
);
2219 void DotCfgChangeReporter::generateIRRepresentation(Any IR
, StringRef PassID
,
2220 IRDataT
<DCData
> &Data
) {
2221 IRComparer
<DCData
>::analyzeIR(IR
, Data
);
2224 void DotCfgChangeReporter::omitAfter(StringRef PassID
, std::string
&Name
) {
2225 assert(HTML
&& "Expected outstream to be set");
2226 SmallString
<20> Banner
=
2227 formatv(" <a>{0}. Pass {1} on {2} omitted because no change</a><br/>\n",
2228 N
, makeHTMLReady(PassID
), Name
);
2233 void DotCfgChangeReporter::handleAfter(StringRef PassID
, std::string
&Name
,
2234 const IRDataT
<DCData
> &Before
,
2235 const IRDataT
<DCData
> &After
, Any IR
) {
2236 assert(HTML
&& "Expected outstream to be set");
2237 IRComparer
<DCData
>(Before
, After
)
2238 .compare(getModuleForComparison(IR
),
2239 [&](bool InModule
, unsigned Minor
,
2240 const FuncDataT
<DCData
> &Before
,
2241 const FuncDataT
<DCData
> &After
) -> void {
2242 handleFunctionCompare(Name
, " Pass ", PassID
, " on ", InModule
,
2243 Minor
, Before
, After
);
2245 *HTML
<< " </p></div>\n";
2249 void DotCfgChangeReporter::handleInvalidated(StringRef PassID
) {
2250 assert(HTML
&& "Expected outstream to be set");
2251 SmallString
<20> Banner
=
2252 formatv(" <a>{0}. {1} invalidated</a><br/>\n", N
, makeHTMLReady(PassID
));
2257 void DotCfgChangeReporter::handleFiltered(StringRef PassID
, std::string
&Name
) {
2258 assert(HTML
&& "Expected outstream to be set");
2259 SmallString
<20> Banner
=
2260 formatv(" <a>{0}. Pass {1} on {2} filtered out</a><br/>\n", N
,
2261 makeHTMLReady(PassID
), Name
);
2266 void DotCfgChangeReporter::handleIgnored(StringRef PassID
, std::string
&Name
) {
2267 assert(HTML
&& "Expected outstream to be set");
2268 SmallString
<20> Banner
= formatv(" <a>{0}. {1} on {2} ignored</a><br/>\n", N
,
2269 makeHTMLReady(PassID
), Name
);
2274 bool DotCfgChangeReporter::initializeHTML() {
2276 HTML
= std::make_unique
<raw_fd_ostream
>(DotCfgDir
+ "/passes.html", EC
);
2282 *HTML
<< "<!doctype html>"
2285 << "<style>.collapsible { "
2286 << "background-color: #777;"
2288 << " cursor: pointer;"
2289 << " padding: 18px;"
2292 << " text-align: left;"
2293 << " outline: none;"
2294 << " font-size: 15px;"
2295 << "} .active, .collapsible:hover {"
2296 << " background-color: #555;"
2298 << " padding: 0 18px;"
2299 << " display: none;"
2300 << " overflow: hidden;"
2301 << " background-color: #f1f1f1;"
2304 << "<title>passes.html</title>"
2310 DotCfgChangeReporter::~DotCfgChangeReporter() {
2314 << "<script>var coll = document.getElementsByClassName(\"collapsible\");"
2316 << "for (i = 0; i < coll.length; i++) {"
2317 << "coll[i].addEventListener(\"click\", function() {"
2318 << " this.classList.toggle(\"active\");"
2319 << " var content = this.nextElementSibling;"
2320 << " if (content.style.display === \"block\"){"
2321 << " content.style.display = \"none\";"
2324 << " content.style.display= \"block\";"
2335 void DotCfgChangeReporter::registerCallbacks(
2336 PassInstrumentationCallbacks
&PIC
) {
2337 if (PrintChanged
== ChangePrinter::DotCfgVerbose
||
2338 PrintChanged
== ChangePrinter::DotCfgQuiet
) {
2339 SmallString
<128> OutputDir
;
2340 sys::fs::expand_tilde(DotCfgDir
, OutputDir
);
2341 sys::fs::make_absolute(OutputDir
);
2342 assert(!OutputDir
.empty() && "expected output dir to be non-empty");
2343 DotCfgDir
= OutputDir
.c_str();
2344 if (initializeHTML()) {
2345 ChangeReporter
<IRDataT
<DCData
>>::registerRequiredCallbacks(PIC
);
2348 dbgs() << "Unable to open output stream for -cfg-dot-changed\n";
2352 StandardInstrumentations::StandardInstrumentations(
2353 LLVMContext
&Context
, bool DebugLogging
, bool VerifyEach
,
2354 PrintPassOptions PrintPassOpts
)
2355 : PrintPass(DebugLogging
, PrintPassOpts
),
2356 OptNone(DebugLogging
),
2357 OptPassGate(Context
),
2358 PrintChangedIR(PrintChanged
== ChangePrinter::Verbose
),
2359 PrintChangedDiff(PrintChanged
== ChangePrinter::DiffVerbose
||
2360 PrintChanged
== ChangePrinter::ColourDiffVerbose
,
2361 PrintChanged
== ChangePrinter::ColourDiffVerbose
||
2362 PrintChanged
== ChangePrinter::ColourDiffQuiet
),
2363 WebsiteChangeReporter(PrintChanged
== ChangePrinter::DotCfgVerbose
),
2364 Verify(DebugLogging
), VerifyEach(VerifyEach
) {}
2366 PrintCrashIRInstrumentation
*PrintCrashIRInstrumentation::CrashReporter
=
2369 void PrintCrashIRInstrumentation::reportCrashIR() {
2370 if (!PrintOnCrashPath
.empty()) {
2372 raw_fd_ostream
Out(PrintOnCrashPath
, EC
);
2374 report_fatal_error(errorCodeToError(EC
));
2381 void PrintCrashIRInstrumentation::SignalHandler(void *) {
2382 // Called by signal handlers so do not lock here
2383 // Is the PrintCrashIRInstrumentation still alive?
2387 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2388 "Did not expect to get here without option set.");
2389 CrashReporter
->reportCrashIR();
2392 PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
2396 assert((PrintOnCrash
|| !PrintOnCrashPath
.empty()) &&
2397 "Did not expect to get here without option set.");
2398 CrashReporter
= nullptr;
2401 void PrintCrashIRInstrumentation::registerCallbacks(
2402 PassInstrumentationCallbacks
&PIC
) {
2403 if ((!PrintOnCrash
&& PrintOnCrashPath
.empty()) || CrashReporter
)
2406 sys::AddSignalHandler(SignalHandler
, nullptr);
2407 CrashReporter
= this;
2409 PIC
.registerBeforeNonSkippedPassCallback(
2410 [&PIC
, this](StringRef PassID
, Any IR
) {
2412 raw_string_ostream
OS(SavedIR
);
2413 OS
<< formatv("*** Dump of {0}IR Before Last Pass {1}",
2414 llvm::forcePrintModuleIR() ? "Module " : "", PassID
);
2415 if (!isInteresting(IR
, PassID
, PIC
.getPassNameForClassName(PassID
))) {
2416 OS
<< " Filtered Out ***\n";
2419 OS
<< " Started ***\n";
2420 unwrapAndPrint(OS
, IR
);
2424 void StandardInstrumentations::registerCallbacks(
2425 PassInstrumentationCallbacks
&PIC
, ModuleAnalysisManager
*MAM
) {
2426 PrintIR
.registerCallbacks(PIC
);
2427 PrintPass
.registerCallbacks(PIC
);
2428 TimePasses
.registerCallbacks(PIC
);
2429 OptNone
.registerCallbacks(PIC
);
2430 OptPassGate
.registerCallbacks(PIC
);
2431 PrintChangedIR
.registerCallbacks(PIC
);
2432 PseudoProbeVerification
.registerCallbacks(PIC
);
2434 Verify
.registerCallbacks(PIC
);
2435 PrintChangedDiff
.registerCallbacks(PIC
);
2436 WebsiteChangeReporter
.registerCallbacks(PIC
);
2437 ChangeTester
.registerCallbacks(PIC
);
2438 PrintCrashIR
.registerCallbacks(PIC
);
2440 PreservedCFGChecker
.registerCallbacks(PIC
, *MAM
);
2442 // TimeProfiling records the pass running time cost.
2443 // Its 'BeforePassCallback' can be appended at the tail of all the
2444 // BeforeCallbacks by calling `registerCallbacks` in the end.
2445 // Its 'AfterPassCallback' is put at the front of all the
2446 // AfterCallbacks by its `registerCallbacks`. This is necessary
2447 // to ensure that other callbacks are not included in the timings.
2448 TimeProfilingPasses
.registerCallbacks(PIC
);
2451 template class ChangeReporter
<std::string
>;
2452 template class TextChangeReporter
<std::string
>;
2454 template class BlockDataT
<EmptyData
>;
2455 template class FuncDataT
<EmptyData
>;
2456 template class IRDataT
<EmptyData
>;
2457 template class ChangeReporter
<IRDataT
<EmptyData
>>;
2458 template class TextChangeReporter
<IRDataT
<EmptyData
>>;
2459 template class IRComparer
<EmptyData
>;