1 //===- Standard pass instrumentations handling ----------------*- C++ -*--===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 /// \file
9 ///
10 /// This file defines IR-printing pass instrumentation callbacks as well as
11 /// StandardInstrumentations class that manages standard pass instrumentations.
12 ///
13 //===----------------------------------------------------------------------===//
14 
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>
45 #include <utility>
46 #include <vector>
47 
48 using namespace llvm;
49 
50 static cl::opt<bool> VerifyAnalysisInvalidation("verify-analysis-invalidation",
51                                                 cl::Hidden,
52 #ifdef EXPENSIVE_CHECKS
53                                                 cl::init(true)
54 #else
55                                                 cl::init(false)
56 #endif
57 );
58 
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.
62 static cl::opt<bool>
63     PrintChangedBefore("print-before-changed",
64                        cl::desc("Print before passes that change them"),
65                        cl::init(false), cl::Hidden);
66 
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"));
72 
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,
79                  cl::init("red"));
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,
93                  cl::init("black"));
94 
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(
98     "dot-cfg-dir",
99     cl::desc("Generate dot files into specified directory for changed IRs"),
100     cl::Hidden, cl::init("./"));
101 
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"),
106     cl::Hidden);
107 
108 static cl::opt<bool> PrintOnCrash(
109     "print-on-crash",
110     cl::desc("Print the last form of the IR before crash (use -print-on-crash-path to dump to a file)"),
111     cl::Hidden);
112 
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);
116 
117 static cl::opt<bool> PrintPassNumbers(
118     "print-pass-numbers", cl::init(false), cl::Hidden,
119     cl::desc("Print pass names and their ordinals"));
120 
121 static cl::opt<unsigned> PrintBeforePassNumber(
122     "print-before-pass-number", cl::init(0), cl::Hidden,
123     cl::desc("Print IR before the pass with this number as "
124              "reported by print-pass-numbers"));
125 
126 static cl::opt<std::string> IRDumpDirectory(
127     "ir-dump-directory",
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"));
132 
unwrapIR(Any IR)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;
136 }
137 
138 namespace {
139 
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 "
150                          "changes it"));
151 
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.
unwrapModule(Any IR,bool Force=false)154 const Module *unwrapModule(Any IR, bool Force = false) {
155   if (const auto *M = unwrapIR<Module>(IR))
156     return M;
157 
158   if (const auto *F = unwrapIR<Function>(IR)) {
159     if (!Force && !isFunctionInPrintList(F->getName()))
160       return nullptr;
161 
162     return F->getParent();
163   }
164 
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();
170       }
171     }
172     assert(!Force && "Expected a module");
173     return nullptr;
174   }
175 
176   if (const auto *L = unwrapIR<Loop>(IR)) {
177     const Function *F = L->getHeader()->getParent();
178     if (!Force && !isFunctionInPrintList(F->getName()))
179       return nullptr;
180     return F->getParent();
181   }
182 
183   llvm_unreachable("Unknown IR unit");
184 }
185 
printIR(raw_ostream & OS,const Function * F)186 void printIR(raw_ostream &OS, const Function *F) {
187   if (!isFunctionInPrintList(F->getName()))
188     return;
189   OS << *F;
190 }
191 
printIR(raw_ostream & OS,const Module * M)192 void printIR(raw_ostream &OS, const Module *M) {
193   if (isFunctionInPrintList("*") || forcePrintModuleIR()) {
194     M->print(OS, nullptr);
195   } else {
196     for (const auto &F : M->functions()) {
197       printIR(OS, &F);
198     }
199   }
200 }
201 
printIR(raw_ostream & OS,const LazyCallGraph::SCC * C)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())) {
206       F.print(OS);
207     }
208   }
209 }
210 
printIR(raw_ostream & OS,const Loop * L)211 void printIR(raw_ostream &OS, const Loop *L) {
212   const Function *F = L->getHeader()->getParent();
213   if (!isFunctionInPrintList(F->getName()))
214     return;
215   printLoop(const_cast<Loop &>(*L), OS);
216 }
217 
getIRName(Any IR)218 std::string getIRName(Any IR) {
219   if (unwrapIR<Module>(IR))
220     return "[module]";
221 
222   if (const auto *F = unwrapIR<Function>(IR))
223     return F->getName().str();
224 
225   if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR))
226     return C->getName();
227 
228   if (const auto *L = unwrapIR<Loop>(IR))
229     return L->getName().str();
230 
231   if (const auto *MF = unwrapIR<MachineFunction>(IR))
232     return MF->getName().str();
233 
234   llvm_unreachable("Unknown wrapped IR type");
235 }
236 
moduleContainsFilterPrintFunc(const Module & M)237 bool moduleContainsFilterPrintFunc(const Module &M) {
238   return any_of(M.functions(),
239                 [](const Function &F) {
240                   return isFunctionInPrintList(F.getName());
241                 }) ||
242          isFunctionInPrintList("*");
243 }
244 
sccContainsFilterPrintFunc(const LazyCallGraph::SCC & C)245 bool sccContainsFilterPrintFunc(const LazyCallGraph::SCC &C) {
246   return any_of(C,
247                 [](const LazyCallGraph::Node &N) {
248                   return isFunctionInPrintList(N.getName());
249                 }) ||
250          isFunctionInPrintList("*");
251 }
252 
shouldPrintIR(Any IR)253 bool shouldPrintIR(Any IR) {
254   if (const auto *M = unwrapIR<Module>(IR))
255     return moduleContainsFilterPrintFunc(*M);
256 
257   if (const auto *F = unwrapIR<Function>(IR))
258     return isFunctionInPrintList(F->getName());
259 
260   if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR))
261     return sccContainsFilterPrintFunc(*C);
262 
263   if (const auto *L = unwrapIR<Loop>(IR))
264     return isFunctionInPrintList(L->getHeader()->getParent()->getName());
265   llvm_unreachable("Unknown wrapped IR type");
266 }
267 
268 /// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into
269 /// Any and does actual print job.
unwrapAndPrint(raw_ostream & OS,Any IR)270 void unwrapAndPrint(raw_ostream &OS, Any IR) {
271   if (!shouldPrintIR(IR))
272     return;
273 
274   if (forcePrintModuleIR()) {
275     auto *M = unwrapModule(IR);
276     assert(M && "should have unwrapped module");
277     printIR(OS, M);
278     return;
279   }
280 
281   if (const auto *M = unwrapIR<Module>(IR)) {
282     printIR(OS, M);
283     return;
284   }
285 
286   if (const auto *F = unwrapIR<Function>(IR)) {
287     printIR(OS, F);
288     return;
289   }
290 
291   if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR)) {
292     printIR(OS, C);
293     return;
294   }
295 
296   if (const auto *L = unwrapIR<Loop>(IR)) {
297     printIR(OS, L);
298     return;
299   }
300   llvm_unreachable("Unknown wrapped IR type");
301 }
302 
303 // Return true when this is a pass for which changes should be ignored
isIgnored(StringRef PassID)304 bool isIgnored(StringRef PassID) {
305   return isSpecialPass(PassID,
306                        {"PassManager", "PassAdaptor", "AnalysisManagerProxy",
307                         "DevirtSCCRepeatedPass", "ModuleInlinerWrapperPass",
308                         "VerifierPass", "PrintModulePass"});
309 }
310 
makeHTMLReady(StringRef SR)311 std::string makeHTMLReady(StringRef SR) {
312   std::string S;
313   while (true) {
314     StringRef Clean =
315         SR.take_until([](char C) { return C == '<' || C == '>'; });
316     S.append(Clean.str());
317     SR = SR.drop_front(Clean.size());
318     if (SR.size() == 0)
319       return S;
320     S.append(SR[0] == '<' ? "&lt;" : "&gt;");
321     SR = SR.drop_front();
322   }
323   llvm_unreachable("problems converting string to HTML");
324 }
325 
326 // Return the module when that is the appropriate level of comparison for \p IR.
getModuleForComparison(Any IR)327 const Module *getModuleForComparison(Any IR) {
328   if (const auto *M = unwrapIR<Module>(IR))
329     return M;
330   if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR))
331     return C->begin()->getFunction().getParent();
332   return nullptr;
333 }
334 
isInterestingFunction(const Function & F)335 bool isInterestingFunction(const Function &F) {
336   return isFunctionInPrintList(F.getName());
337 }
338 
339 // Return true when this is a pass on IR for which printing
340 // of changes is desired.
isInteresting(Any IR,StringRef PassID,StringRef PassName)341 bool isInteresting(Any IR, StringRef PassID, StringRef PassName) {
342   if (isIgnored(PassID) || !isPassInPrintList(PassName))
343     return false;
344   if (const auto *F = unwrapIR<Function>(IR))
345     return isInterestingFunction(*F);
346   return true;
347 }
348 
349 } // namespace
350 
~ChangeReporter()351 template <typename T> ChangeReporter<T>::~ChangeReporter() {
352   assert(BeforeStack.empty() && "Problem with Change Printer stack.");
353 }
354 
355 template <typename T>
saveIRBeforePass(Any IR,StringRef PassID,StringRef PassName)356 void ChangeReporter<T>::saveIRBeforePass(Any IR, StringRef PassID,
357                                          StringRef PassName) {
358   // Is this the initial IR?
359   if (InitialIR) {
360     InitialIR = false;
361     if (VerboseMode)
362       handleInitialIR(IR);
363   }
364 
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();
369 
370   if (!isInteresting(IR, PassID, PassName))
371     return;
372 
373   // Save the IR representation on the stack.
374   T &Data = BeforeStack.back();
375   generateIRRepresentation(IR, PassID, Data);
376 }
377 
378 template <typename T>
handleIRAfterPass(Any IR,StringRef PassID,StringRef PassName)379 void ChangeReporter<T>::handleIRAfterPass(Any IR, StringRef PassID,
380                                           StringRef PassName) {
381   assert(!BeforeStack.empty() && "Unexpected empty stack encountered.");
382 
383   std::string Name = getIRName(IR);
384 
385   if (isIgnored(PassID)) {
386     if (VerboseMode)
387       handleIgnored(PassID, Name);
388   } else if (!isInteresting(IR, PassID, PassName)) {
389     if (VerboseMode)
390       handleFiltered(PassID, Name);
391   } else {
392     // Get the before rep from the stack
393     T &Before = BeforeStack.back();
394     // Create the after rep
395     T After;
396     generateIRRepresentation(IR, PassID, After);
397 
398     // Was there a change in IR?
399     if (Before == After) {
400       if (VerboseMode)
401         omitAfter(PassID, Name);
402     } else
403       handleAfter(PassID, Name, Before, After, IR);
404   }
405   BeforeStack.pop_back();
406 }
407 
408 template <typename T>
handleInvalidatedPass(StringRef PassID)409 void ChangeReporter<T>::handleInvalidatedPass(StringRef PassID) {
410   assert(!BeforeStack.empty() && "Unexpected empty stack encountered.");
411 
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.
416   if (VerboseMode)
417     handleInvalidated(PassID);
418   BeforeStack.pop_back();
419 }
420 
421 template <typename T>
registerRequiredCallbacks(PassInstrumentationCallbacks & PIC)422 void ChangeReporter<T>::registerRequiredCallbacks(
423     PassInstrumentationCallbacks &PIC) {
424   PIC.registerBeforeNonSkippedPassCallback([&PIC, this](StringRef P, Any IR) {
425     saveIRBeforePass(IR, P, PIC.getPassNameForClassName(P));
426   });
427 
428   PIC.registerAfterPassCallback(
429       [&PIC, this](StringRef P, Any IR, const PreservedAnalyses &) {
430         handleIRAfterPass(IR, P, PIC.getPassNameForClassName(P));
431       });
432   PIC.registerAfterPassInvalidatedCallback(
433       [this](StringRef P, const PreservedAnalyses &) {
434         handleInvalidatedPass(P);
435       });
436 }
437 
438 template <typename T>
TextChangeReporter(bool Verbose)439 TextChangeReporter<T>::TextChangeReporter(bool Verbose)
440     : ChangeReporter<T>(Verbose), Out(dbgs()) {}
441 
handleInitialIR(Any IR)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);
449 }
450 
451 template <typename T>
omitAfter(StringRef PassID,std::string & Name)452 void TextChangeReporter<T>::omitAfter(StringRef PassID, std::string &Name) {
453   Out << formatv("*** IR Dump After {0} on {1} omitted because no change ***\n",
454                  PassID, Name);
455 }
456 
457 template <typename T>
handleInvalidated(StringRef PassID)458 void TextChangeReporter<T>::handleInvalidated(StringRef PassID) {
459   Out << formatv("*** IR Pass {0} invalidated ***\n", PassID);
460 }
461 
462 template <typename T>
handleFiltered(StringRef PassID,std::string & Name)463 void TextChangeReporter<T>::handleFiltered(StringRef PassID,
464                                            std::string &Name) {
465   SmallString<20> Banner =
466       formatv("*** IR Dump After {0} on {1} filtered out ***\n", PassID, Name);
467   Out << Banner;
468 }
469 
470 template <typename T>
handleIgnored(StringRef PassID,std::string & Name)471 void TextChangeReporter<T>::handleIgnored(StringRef PassID, std::string &Name) {
472   Out << formatv("*** IR Pass {0} on {1} ignored ***\n", PassID, Name);
473 }
474 
475 IRChangedPrinter::~IRChangedPrinter() = default;
476 
registerCallbacks(PassInstrumentationCallbacks & PIC)477 void IRChangedPrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) {
478   if (PrintChanged == ChangePrinter::Verbose ||
479       PrintChanged == ChangePrinter::Quiet)
480     TextChangeReporter<std::string>::registerRequiredCallbacks(PIC);
481 }
482 
generateIRRepresentation(Any IR,StringRef PassID,std::string & Output)483 void IRChangedPrinter::generateIRRepresentation(Any IR, StringRef PassID,
484                                                 std::string &Output) {
485   raw_string_ostream OS(Output);
486   unwrapAndPrint(OS, IR);
487   OS.str();
488 }
489 
handleAfter(StringRef PassID,std::string & Name,const std::string & Before,const std::string & After,Any)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"
496         << Before;
497 
498   // We might not get anything to print if we only want to print a specific
499   // function but it gets deleted.
500   if (After.empty()) {
501     Out << "*** IR Deleted After " << PassID << " on " << Name << " ***\n";
502     return;
503   }
504 
505   Out << "*** IR Dump After " << PassID << " on " << Name << " ***\n" << After;
506 }
507 
~IRChangedTester()508 IRChangedTester::~IRChangedTester() {}
509 
registerCallbacks(PassInstrumentationCallbacks & PIC)510 void IRChangedTester::registerCallbacks(PassInstrumentationCallbacks &PIC) {
511   if (TestChanged != "")
512     TextChangeReporter<std::string>::registerRequiredCallbacks(PIC);
513 }
514 
handleIR(const std::string & S,StringRef PassID)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.";
522     return;
523   }
524   static ErrorOr<std::string> Exe = sys::findProgramByName(TestChanged);
525   if (!Exe) {
526     dbgs() << "Unable to find test-changed executable.";
527     return;
528   }
529 
530   StringRef Args[] = {TestChanged, FileName[0], PassID};
531   int Result = sys::ExecuteAndWait(*Exe, Args);
532   if (Result < 0) {
533     dbgs() << "Error executing test-changed executable.";
534     return;
535   }
536 
537   if (cleanUpTempFiles(FileName))
538     dbgs() << "Unable to remove temporary file.";
539 }
540 
handleInitialIR(Any IR)541 void IRChangedTester::handleInitialIR(Any IR) {
542   // Always test the initial module.
543   // Unwrap and print directly to avoid filtering problems in general routines.
544   std::string S;
545   generateIRRepresentation(IR, "Initial IR", S);
546   handleIR(S, "Initial IR");
547 }
548 
omitAfter(StringRef PassID,std::string & Name)549 void IRChangedTester::omitAfter(StringRef PassID, std::string &Name) {}
handleInvalidated(StringRef PassID)550 void IRChangedTester::handleInvalidated(StringRef PassID) {}
handleFiltered(StringRef PassID,std::string & Name)551 void IRChangedTester::handleFiltered(StringRef PassID, std::string &Name) {}
handleIgnored(StringRef PassID,std::string & Name)552 void IRChangedTester::handleIgnored(StringRef PassID, std::string &Name) {}
handleAfter(StringRef PassID,std::string & Name,const std::string & Before,const std::string & After,Any)553 void IRChangedTester::handleAfter(StringRef PassID, std::string &Name,
554                                   const std::string &Before,
555                                   const std::string &After, Any) {
556   handleIR(After, PassID);
557 }
558 
559 template <typename T>
report(const OrderedChangedData & Before,const OrderedChangedData & After,function_ref<void (const T *,const T *)> HandlePair)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();
569 
570   auto HandlePotentiallyRemovedData = [&](std::string S) {
571     // The order in LLVM may have changed so check if still exists.
572     if (!AFD.count(S)) {
573       // This has been removed.
574       HandlePair(&BFD.find(*BI)->getValue(), nullptr);
575     }
576   };
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);
581     Q.clear();
582   };
583 
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;
594   while (AI != AE) {
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());
599       ++AI;
600       continue;
601     }
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);
609       ++BI;
610     }
611     // Report any new sections that were queued up and waiting.
612     HandleNewData(NewDataQueue);
613 
614     const T &AData = AFD.find(*AI)->getValue();
615     const T &BData = BFD.find(*AI)->getValue();
616     HandlePair(&BData, &AData);
617     if (BI != BE)
618       ++BI;
619     ++AI;
620   }
621 
622   // Check any remaining before sections to see if they have been removed
623   while (BI != BE) {
624     HandlePotentiallyRemovedData(*BI);
625     ++BI;
626   }
627 
628   HandleNewData(NewDataQueue);
629 }
630 
631 template <typename T>
compare(bool CompareModule,std::function<void (bool InModule,unsigned Minor,const FuncDataT<T> & Before,const FuncDataT<T> & After)> CompareFunc)632 void IRComparer<T>::compare(
633     bool CompareModule,
634     std::function<void(bool InModule, unsigned Minor,
635                        const FuncDataT<T> &Before, const FuncDataT<T> &After)>
636         CompareFunc) {
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());
643     return;
644   }
645 
646   unsigned Minor = 0;
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.");
651                        if (!B)
652                          B = &Missing;
653                        else if (!A)
654                          A = &Missing;
655                        CompareFunc(true, Minor++, *B, *A);
656                      });
657 }
658 
analyzeIR(Any IR,IRDataT<T> & Data)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);
664     return;
665   }
666 
667   const auto *F = unwrapIR<Function>(IR);
668   if (!F) {
669     const auto *L = unwrapIR<Loop>(IR);
670     assert(L && "Unknown IR unit.");
671     F = L->getHeader()->getParent();
672   }
673   assert(F && "Unknown IR unit.");
674   generateFunctionData(Data, *F);
675 }
676 
677 template <typename T>
generateFunctionData(IRDataT<T> & Data,const Function & F)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());
681     int I = 0;
682     for (const auto &B : F) {
683       std::string BBName = B.getName().str();
684       if (BBName.empty()) {
685         BBName = formatv("{0}", I);
686         ++I;
687       }
688       FD.getOrder().emplace_back(BBName);
689       FD.getData().insert({BBName, B});
690     }
691     Data.getOrder().emplace_back(F.getName());
692     Data.getData().insert({F.getName(), FD});
693     return true;
694   }
695   return false;
696 }
697 
~PrintIRInstrumentation()698 PrintIRInstrumentation::~PrintIRInstrumentation() {
699   assert(PassRunDescriptorStack.empty() &&
700          "PassRunDescriptorStack is not empty at exit");
701 }
702 
getIRFileDisplayName(Any IR)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,
716               MaxHashWidth);
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);
725   } else {
726     llvm_unreachable("Unknown wrapped IR type");
727   }
728   return Result;
729 }
730 
fetchDumpFilename(StringRef PassName,Any IR)731 std::string PrintIRInstrumentation::fetchDumpFilename(StringRef PassName,
732                                                       Any IR) {
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);
747 }
748 
749 enum class IRDumpFileSuffixType {
750   Before,
751   After,
752   Invalidated,
753 };
754 
getFileSuffix(IRDumpFileSuffixType Type)755 static StringRef getFileSuffix(IRDumpFileSuffixType Type) {
756   static constexpr std::array FileSuffixes = {"-before.ll", "-after.ll",
757                                               "-invalidated.ll"};
758   return FileSuffixes[static_cast<size_t>(Type)];
759 }
760 
pushPassRunDescriptor(StringRef PassID,Any IR,std::string & DumpIRFilename)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));
766 }
767 
768 PrintIRInstrumentation::PassRunDescriptor
popPassRunDescriptor(StringRef PassID)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");
774   return Descriptor;
775 }
776 
777 // Callers are responsible for closing the returned file descriptor
prepareDumpIRFileDescriptor(const StringRef DumpIRFilename)778 static int prepareDumpIRFileDescriptor(const StringRef DumpIRFilename) {
779   std::error_code EC;
780   auto ParentPath = llvm::sys::path::parent_path(DumpIRFilename);
781   if (!ParentPath.empty()) {
782     std::error_code EC = llvm::sys::fs::create_directories(ParentPath);
783     if (EC)
784       report_fatal_error(Twine("Failed to create directory ") + ParentPath +
785                          " to support -ir-dump-directory: " + EC.message());
786   }
787   int Result = 0;
788   EC = sys::fs::openFile(DumpIRFilename, Result, sys::fs::CD_OpenAlways,
789                          sys::fs::FA_Write, sys::fs::OF_None);
790   if (EC)
791     report_fatal_error(Twine("Failed to open ") + DumpIRFilename +
792                        " to support -ir-dump-directory: " + EC.message());
793   return Result;
794 }
795 
printBeforePass(StringRef PassID,Any IR)796 void PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) {
797   if (isIgnored(PassID))
798     return;
799 
800   std::string DumpIRFilename;
801   if (!IRDumpDirectory.empty() &&
802       (shouldPrintBeforePass(PassID) || shouldPrintAfterPass(PassID)))
803     DumpIRFilename = fetchDumpFilename(PassID, IR);
804 
805   // Saving Module for AfterPassInvalidated operations.
806   // Note: here we rely on a fact that we do not change modules while
807   // traversing the pipeline, so the latest captured module is good
808   // for all print operations that has not happen yet.
809   if (shouldPrintAfterPass(PassID))
810     pushPassRunDescriptor(PassID, IR, DumpIRFilename);
811 
812   if (!shouldPrintIR(IR))
813     return;
814 
815   ++CurrentPassNumber;
816 
817   if (shouldPrintPassNumbers())
818     dbgs() << " Running pass " << CurrentPassNumber << " " << PassID
819            << " on " << getIRName(IR) << "\n";
820 
821   if (!shouldPrintBeforePass(PassID))
822     return;
823 
824   auto WriteIRToStream = [&](raw_ostream &Stream) {
825     Stream << "; *** IR Dump Before ";
826     if (shouldPrintBeforePassNumber())
827       Stream << CurrentPassNumber << "-";
828     Stream << PassID << " on " << getIRName(IR) << " ***\n";
829     unwrapAndPrint(Stream, IR);
830   };
831 
832   if (!DumpIRFilename.empty()) {
833     DumpIRFilename += getFileSuffix(IRDumpFileSuffixType::Before);
834     llvm::raw_fd_ostream DumpIRFileStream{
835         prepareDumpIRFileDescriptor(DumpIRFilename), /* shouldClose */ true};
836     WriteIRToStream(DumpIRFileStream);
837   } else {
838     WriteIRToStream(dbgs());
839   }
840 }
841 
printAfterPass(StringRef PassID,Any IR)842 void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) {
843   if (isIgnored(PassID))
844     return;
845 
846   if (!shouldPrintAfterPass(PassID))
847     return;
848 
849   auto [M, DumpIRFilename, IRName, StoredPassID] = popPassRunDescriptor(PassID);
850   assert(StoredPassID == PassID && "mismatched PassID");
851 
852   if (!shouldPrintIR(IR) || !shouldPrintAfterPass(PassID))
853     return;
854 
855   auto WriteIRToStream = [&](raw_ostream &Stream, const StringRef IRName) {
856     Stream << "; *** IR Dump " << StringRef(formatv("After {0}", PassID))
857            << " on " << IRName << " ***\n";
858     unwrapAndPrint(Stream, IR);
859   };
860 
861   if (!IRDumpDirectory.empty()) {
862     assert(!DumpIRFilename.empty() && "DumpIRFilename must not be empty and "
863                                       "should be set in printBeforePass");
864     const std::string DumpIRFilenameWithSuffix =
865         DumpIRFilename + getFileSuffix(IRDumpFileSuffixType::After).str();
866     llvm::raw_fd_ostream DumpIRFileStream{
867         prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix),
868         /* shouldClose */ true};
869     WriteIRToStream(DumpIRFileStream, IRName);
870   } else {
871     WriteIRToStream(dbgs(), IRName);
872   }
873 }
874 
printAfterPassInvalidated(StringRef PassID)875 void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) {
876   if (isIgnored(PassID))
877     return;
878 
879   if (!shouldPrintAfterPass(PassID))
880     return;
881 
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))
887     return;
888 
889   auto WriteIRToStream = [&](raw_ostream &Stream, const Module *M,
890                              const StringRef IRName) {
891     SmallString<20> Banner;
892     Banner = formatv("; *** IR Dump After {0} on {1} (invalidated) ***", PassID,
893                      IRName);
894     Stream << Banner << "\n";
895     printIR(Stream, M);
896   };
897 
898   if (!IRDumpDirectory.empty()) {
899     assert(!DumpIRFilename.empty() && "DumpIRFilename must not be empty and "
900                                       "should be set in printBeforePass");
901     const std::string DumpIRFilenameWithSuffix =
902         DumpIRFilename + getFileSuffix(IRDumpFileSuffixType::Invalidated).str();
903     llvm::raw_fd_ostream DumpIRFileStream{
904         prepareDumpIRFileDescriptor(DumpIRFilenameWithSuffix),
905         /* shouldClose */ true};
906     WriteIRToStream(DumpIRFileStream, M, IRName);
907   } else {
908     WriteIRToStream(dbgs(), M, IRName);
909   }
910 }
911 
shouldPrintBeforePass(StringRef PassID)912 bool PrintIRInstrumentation::shouldPrintBeforePass(StringRef PassID) {
913   if (shouldPrintBeforeAll())
914     return true;
915 
916   if (shouldPrintBeforePassNumber() &&
917       CurrentPassNumber == PrintBeforePassNumber)
918     return true;
919 
920   StringRef PassName = PIC->getPassNameForClassName(PassID);
921   return is_contained(printBeforePasses(), PassName);
922 }
923 
shouldPrintAfterPass(StringRef PassID)924 bool PrintIRInstrumentation::shouldPrintAfterPass(StringRef PassID) {
925   if (shouldPrintAfterAll())
926     return true;
927 
928   StringRef PassName = PIC->getPassNameForClassName(PassID);
929   return is_contained(printAfterPasses(), PassName);
930 }
931 
shouldPrintPassNumbers()932 bool PrintIRInstrumentation::shouldPrintPassNumbers() {
933   return PrintPassNumbers;
934 }
935 
shouldPrintBeforePassNumber()936 bool PrintIRInstrumentation::shouldPrintBeforePassNumber() {
937   return PrintBeforePassNumber > 0;
938 }
939 
registerCallbacks(PassInstrumentationCallbacks & PIC)940 void PrintIRInstrumentation::registerCallbacks(
941     PassInstrumentationCallbacks &PIC) {
942   this->PIC = &PIC;
943 
944   // BeforePass callback is not just for printing, it also saves a Module
945   // for later use in AfterPassInvalidated.
946   if (shouldPrintPassNumbers() || shouldPrintBeforePassNumber() ||
947       shouldPrintBeforeSomePass() || shouldPrintAfterSomePass())
948     PIC.registerBeforeNonSkippedPassCallback(
949         [this](StringRef P, Any IR) { this->printBeforePass(P, IR); });
950 
951   if (shouldPrintAfterSomePass()) {
952     PIC.registerAfterPassCallback(
953         [this](StringRef P, Any IR, const PreservedAnalyses &) {
954           this->printAfterPass(P, IR);
955         });
956     PIC.registerAfterPassInvalidatedCallback(
957         [this](StringRef P, const PreservedAnalyses &) {
958           this->printAfterPassInvalidated(P);
959         });
960   }
961 }
962 
registerCallbacks(PassInstrumentationCallbacks & PIC)963 void OptNoneInstrumentation::registerCallbacks(
964     PassInstrumentationCallbacks &PIC) {
965   PIC.registerShouldRunOptionalPassCallback(
966       [this](StringRef P, Any IR) { return this->shouldRun(P, IR); });
967 }
968 
shouldRun(StringRef PassID,Any IR)969 bool OptNoneInstrumentation::shouldRun(StringRef PassID, Any IR) {
970   const auto *F = unwrapIR<Function>(IR);
971   if (!F) {
972     if (const auto *L = unwrapIR<Loop>(IR))
973       F = L->getHeader()->getParent();
974   }
975   bool ShouldRun = !(F && F->hasOptNone());
976   if (!ShouldRun && DebugLogging) {
977     errs() << "Skipping pass " << PassID << " on " << F->getName()
978            << " due to optnone attribute\n";
979   }
980   return ShouldRun;
981 }
982 
shouldRun(StringRef PassName,Any IR)983 bool OptPassGateInstrumentation::shouldRun(StringRef PassName, Any IR) {
984   if (isIgnored(PassName))
985     return true;
986 
987   bool ShouldRun =
988       Context.getOptPassGate().shouldRunPass(PassName, getIRName(IR));
989   if (!ShouldRun && !this->HasWrittenIR && !OptBisectPrintIRPath.empty()) {
990     // FIXME: print IR if limit is higher than number of opt-bisect
991     // invocations
992     this->HasWrittenIR = true;
993     const Module *M = unwrapModule(IR, /*Force=*/true);
994     assert((M && &M->getContext() == &Context) && "Missing/Mismatching Module");
995     std::error_code EC;
996     raw_fd_ostream OS(OptBisectPrintIRPath, EC);
997     if (EC)
998       report_fatal_error(errorCodeToError(EC));
999     M->print(OS, nullptr);
1000   }
1001   return ShouldRun;
1002 }
1003 
registerCallbacks(PassInstrumentationCallbacks & PIC)1004 void OptPassGateInstrumentation::registerCallbacks(
1005     PassInstrumentationCallbacks &PIC) {
1006   OptPassGate &PassGate = Context.getOptPassGate();
1007   if (!PassGate.isEnabled())
1008     return;
1009 
1010   PIC.registerShouldRunOptionalPassCallback([this](StringRef PassName, Any IR) {
1011     return this->shouldRun(PassName, IR);
1012   });
1013 }
1014 
print()1015 raw_ostream &PrintPassInstrumentation::print() {
1016   if (Opts.Indent) {
1017     assert(Indent >= 0);
1018     dbgs().indent(Indent);
1019   }
1020   return dbgs();
1021 }
1022 
registerCallbacks(PassInstrumentationCallbacks & PIC)1023 void PrintPassInstrumentation::registerCallbacks(
1024     PassInstrumentationCallbacks &PIC) {
1025   if (!Enabled)
1026     return;
1027 
1028   std::vector<StringRef> SpecialPasses;
1029   if (!Opts.Verbose) {
1030     SpecialPasses.emplace_back("PassManager");
1031     SpecialPasses.emplace_back("PassAdaptor");
1032   }
1033 
1034   PIC.registerBeforeSkippedPassCallback([this, SpecialPasses](StringRef PassID,
1035                                                               Any IR) {
1036     assert(!isSpecialPass(PassID, SpecialPasses) &&
1037            "Unexpectedly skipping special pass");
1038 
1039     print() << "Skipping pass: " << PassID << " on " << getIRName(IR) << "\n";
1040   });
1041   PIC.registerBeforeNonSkippedPassCallback([this, SpecialPasses](
1042                                                StringRef PassID, Any IR) {
1043     if (isSpecialPass(PassID, SpecialPasses))
1044       return;
1045 
1046     auto &OS = print();
1047     OS << "Running pass: " << PassID << " on " << getIRName(IR);
1048     if (const auto *F = unwrapIR<Function>(IR)) {
1049       unsigned Count = F->getInstructionCount();
1050       OS << " (" << Count << " instruction";
1051       if (Count != 1)
1052         OS << 's';
1053       OS << ')';
1054     } else if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR)) {
1055       int Count = C->size();
1056       OS << " (" << Count << " node";
1057       if (Count != 1)
1058         OS << 's';
1059       OS << ')';
1060     }
1061     OS << "\n";
1062     Indent += 2;
1063   });
1064   PIC.registerAfterPassCallback(
1065       [this, SpecialPasses](StringRef PassID, Any IR,
1066                             const PreservedAnalyses &) {
1067         if (isSpecialPass(PassID, SpecialPasses))
1068           return;
1069 
1070         Indent -= 2;
1071       });
1072   PIC.registerAfterPassInvalidatedCallback(
1073       [this, SpecialPasses](StringRef PassID, Any IR) {
1074         if (isSpecialPass(PassID, SpecialPasses))
1075           return;
1076 
1077         Indent -= 2;
1078       });
1079 
1080   if (!Opts.SkipAnalyses) {
1081     PIC.registerBeforeAnalysisCallback([this](StringRef PassID, Any IR) {
1082       print() << "Running analysis: " << PassID << " on " << getIRName(IR)
1083               << "\n";
1084       Indent += 2;
1085     });
1086     PIC.registerAfterAnalysisCallback(
1087         [this](StringRef PassID, Any IR) { Indent -= 2; });
1088     PIC.registerAnalysisInvalidatedCallback([this](StringRef PassID, Any IR) {
1089       print() << "Invalidating analysis: " << PassID << " on " << getIRName(IR)
1090               << "\n";
1091     });
1092     PIC.registerAnalysesClearedCallback([this](StringRef IRName) {
1093       print() << "Clearing all analysis results for: " << IRName << "\n";
1094     });
1095   }
1096 }
1097 
CFG(const Function * F,bool TrackBBLifetime)1098 PreservedCFGCheckerInstrumentation::CFG::CFG(const Function *F,
1099                                              bool TrackBBLifetime) {
1100   if (TrackBBLifetime)
1101     BBGuards = DenseMap<intptr_t, BBGuard>(F->size());
1102   for (const auto &BB : *F) {
1103     if (BBGuards)
1104       BBGuards->try_emplace(intptr_t(&BB), &BB);
1105     for (const auto *Succ : successors(&BB)) {
1106       Graph[&BB][Succ]++;
1107       if (BBGuards)
1108         BBGuards->try_emplace(intptr_t(Succ), Succ);
1109     }
1110   }
1111 }
1112 
printBBName(raw_ostream & out,const BasicBlock * BB)1113 static void printBBName(raw_ostream &out, const BasicBlock *BB) {
1114   if (BB->hasName()) {
1115     out << BB->getName() << "<" << BB << ">";
1116     return;
1117   }
1118 
1119   if (!BB->getParent()) {
1120     out << "unnamed_removed<" << BB << ">";
1121     return;
1122   }
1123 
1124   if (BB->isEntryBlock()) {
1125     out << "entry"
1126         << "<" << BB << ">";
1127     return;
1128   }
1129 
1130   unsigned FuncOrderBlockNum = 0;
1131   for (auto &FuncBB : *BB->getParent()) {
1132     if (&FuncBB == BB)
1133       break;
1134     FuncOrderBlockNum++;
1135   }
1136   out << "unnamed_" << FuncOrderBlockNum << "<" << BB << ">";
1137 }
1138 
printDiff(raw_ostream & out,const CFG & Before,const CFG & After)1139 void PreservedCFGCheckerInstrumentation::CFG::printDiff(raw_ostream &out,
1140                                                         const CFG &Before,
1141                                                         const CFG &After) {
1142   assert(!After.isPoisoned());
1143   if (Before.isPoisoned()) {
1144     out << "Some blocks were deleted\n";
1145     return;
1146   }
1147 
1148   // Find and print graph differences.
1149   if (Before.Graph.size() != After.Graph.size())
1150     out << "Different number of non-leaf basic blocks: before="
1151         << Before.Graph.size() << ", after=" << After.Graph.size() << "\n";
1152 
1153   for (auto &BB : Before.Graph) {
1154     auto BA = After.Graph.find(BB.first);
1155     if (BA == After.Graph.end()) {
1156       out << "Non-leaf block ";
1157       printBBName(out, BB.first);
1158       out << " is removed (" << BB.second.size() << " successors)\n";
1159     }
1160   }
1161 
1162   for (auto &BA : After.Graph) {
1163     auto BB = Before.Graph.find(BA.first);
1164     if (BB == Before.Graph.end()) {
1165       out << "Non-leaf block ";
1166       printBBName(out, BA.first);
1167       out << " is added (" << BA.second.size() << " successors)\n";
1168       continue;
1169     }
1170 
1171     if (BB->second == BA.second)
1172       continue;
1173 
1174     out << "Different successors of block ";
1175     printBBName(out, BA.first);
1176     out << " (unordered):\n";
1177     out << "- before (" << BB->second.size() << "): ";
1178     for (auto &SuccB : BB->second) {
1179       printBBName(out, SuccB.first);
1180       if (SuccB.second != 1)
1181         out << "(" << SuccB.second << "), ";
1182       else
1183         out << ", ";
1184     }
1185     out << "\n";
1186     out << "- after (" << BA.second.size() << "): ";
1187     for (auto &SuccA : BA.second) {
1188       printBBName(out, SuccA.first);
1189       if (SuccA.second != 1)
1190         out << "(" << SuccA.second << "), ";
1191       else
1192         out << ", ";
1193     }
1194     out << "\n";
1195   }
1196 }
1197 
1198 // PreservedCFGCheckerInstrumentation uses PreservedCFGCheckerAnalysis to check
1199 // passes, that reported they kept CFG analyses up-to-date, did not actually
1200 // change CFG. This check is done as follows. Before every functional pass in
1201 // BeforeNonSkippedPassCallback a CFG snapshot (an instance of
1202 // PreservedCFGCheckerInstrumentation::CFG) is requested from
1203 // FunctionAnalysisManager as a result of PreservedCFGCheckerAnalysis. When the
1204 // functional pass finishes and reports that CFGAnalyses or AllAnalyses are
1205 // up-to-date then the cached result of PreservedCFGCheckerAnalysis (if
1206 // available) is checked to be equal to a freshly created CFG snapshot.
1207 struct PreservedCFGCheckerAnalysis
1208     : public AnalysisInfoMixin<PreservedCFGCheckerAnalysis> {
1209   friend AnalysisInfoMixin<PreservedCFGCheckerAnalysis>;
1210 
1211   static AnalysisKey Key;
1212 
1213 public:
1214   /// Provide the result type for this analysis pass.
1215   using Result = PreservedCFGCheckerInstrumentation::CFG;
1216 
1217   /// Run the analysis pass over a function and produce CFG.
runPreservedCFGCheckerAnalysis1218   Result run(Function &F, FunctionAnalysisManager &FAM) {
1219     return Result(&F, /* TrackBBLifetime */ true);
1220   }
1221 };
1222 
1223 AnalysisKey PreservedCFGCheckerAnalysis::Key;
1224 
1225 struct PreservedFunctionHashAnalysis
1226     : public AnalysisInfoMixin<PreservedFunctionHashAnalysis> {
1227   static AnalysisKey Key;
1228 
1229   struct FunctionHash {
1230     uint64_t Hash;
1231   };
1232 
1233   using Result = FunctionHash;
1234 
runPreservedFunctionHashAnalysis1235   Result run(Function &F, FunctionAnalysisManager &FAM) {
1236     return Result{StructuralHash(F)};
1237   }
1238 };
1239 
1240 AnalysisKey PreservedFunctionHashAnalysis::Key;
1241 
1242 struct PreservedModuleHashAnalysis
1243     : public AnalysisInfoMixin<PreservedModuleHashAnalysis> {
1244   static AnalysisKey Key;
1245 
1246   struct ModuleHash {
1247     uint64_t Hash;
1248   };
1249 
1250   using Result = ModuleHash;
1251 
runPreservedModuleHashAnalysis1252   Result run(Module &F, ModuleAnalysisManager &FAM) {
1253     return Result{StructuralHash(F)};
1254   }
1255 };
1256 
1257 AnalysisKey PreservedModuleHashAnalysis::Key;
1258 
invalidate(Function & F,const PreservedAnalyses & PA,FunctionAnalysisManager::Invalidator &)1259 bool PreservedCFGCheckerInstrumentation::CFG::invalidate(
1260     Function &F, const PreservedAnalyses &PA,
1261     FunctionAnalysisManager::Invalidator &) {
1262   auto PAC = PA.getChecker<PreservedCFGCheckerAnalysis>();
1263   return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>() ||
1264            PAC.preservedSet<CFGAnalyses>());
1265 }
1266 
GetFunctions(Any IR)1267 static SmallVector<Function *, 1> GetFunctions(Any IR) {
1268   SmallVector<Function *, 1> Functions;
1269 
1270   if (const auto *MaybeF = unwrapIR<Function>(IR)) {
1271     Functions.push_back(const_cast<Function *>(MaybeF));
1272   } else if (const auto *MaybeM = unwrapIR<Module>(IR)) {
1273     for (Function &F : *const_cast<Module *>(MaybeM))
1274       Functions.push_back(&F);
1275   }
1276   return Functions;
1277 }
1278 
registerCallbacks(PassInstrumentationCallbacks & PIC,ModuleAnalysisManager & MAM)1279 void PreservedCFGCheckerInstrumentation::registerCallbacks(
1280     PassInstrumentationCallbacks &PIC, ModuleAnalysisManager &MAM) {
1281   if (!VerifyAnalysisInvalidation)
1282     return;
1283 
1284   bool Registered = false;
1285   PIC.registerBeforeNonSkippedPassCallback([this, &MAM, Registered](
1286                                                StringRef P, Any IR) mutable {
1287 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1288     assert(&PassStack.emplace_back(P));
1289 #endif
1290     (void)this;
1291 
1292     auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(
1293                        *const_cast<Module *>(unwrapModule(IR, /*Force=*/true)))
1294                     .getManager();
1295     if (!Registered) {
1296       FAM.registerPass([&] { return PreservedCFGCheckerAnalysis(); });
1297       FAM.registerPass([&] { return PreservedFunctionHashAnalysis(); });
1298       MAM.registerPass([&] { return PreservedModuleHashAnalysis(); });
1299       Registered = true;
1300     }
1301 
1302     for (Function *F : GetFunctions(IR)) {
1303       // Make sure a fresh CFG snapshot is available before the pass.
1304       FAM.getResult<PreservedCFGCheckerAnalysis>(*F);
1305       FAM.getResult<PreservedFunctionHashAnalysis>(*F);
1306     }
1307 
1308     if (const auto *MPtr = unwrapIR<Module>(IR)) {
1309       auto &M = *const_cast<Module *>(MPtr);
1310       MAM.getResult<PreservedModuleHashAnalysis>(M);
1311     }
1312   });
1313 
1314   PIC.registerAfterPassInvalidatedCallback(
1315       [this](StringRef P, const PreservedAnalyses &PassPA) {
1316 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1317         assert(PassStack.pop_back_val() == P &&
1318                "Before and After callbacks must correspond");
1319 #endif
1320         (void)this;
1321       });
1322 
1323   PIC.registerAfterPassCallback([this, &MAM](StringRef P, Any IR,
1324                                              const PreservedAnalyses &PassPA) {
1325 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
1326     assert(PassStack.pop_back_val() == P &&
1327            "Before and After callbacks must correspond");
1328 #endif
1329     (void)this;
1330 
1331     // We have to get the FAM via the MAM, rather than directly use a passed in
1332     // FAM because if MAM has not cached the FAM, it won't invalidate function
1333     // analyses in FAM.
1334     auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(
1335                        *const_cast<Module *>(unwrapModule(IR, /*Force=*/true)))
1336                     .getManager();
1337 
1338     for (Function *F : GetFunctions(IR)) {
1339       if (auto *HashBefore =
1340               FAM.getCachedResult<PreservedFunctionHashAnalysis>(*F)) {
1341         if (HashBefore->Hash != StructuralHash(*F)) {
1342           report_fatal_error(formatv(
1343               "Function @{0} changed by {1} without invalidating analyses",
1344               F->getName(), P));
1345         }
1346       }
1347 
1348       auto CheckCFG = [](StringRef Pass, StringRef FuncName,
1349                          const CFG &GraphBefore, const CFG &GraphAfter) {
1350         if (GraphAfter == GraphBefore)
1351           return;
1352 
1353         dbgs()
1354             << "Error: " << Pass
1355             << " does not invalidate CFG analyses but CFG changes detected in "
1356                "function @"
1357             << FuncName << ":\n";
1358         CFG::printDiff(dbgs(), GraphBefore, GraphAfter);
1359         report_fatal_error(Twine("CFG unexpectedly changed by ", Pass));
1360       };
1361 
1362       if (auto *GraphBefore =
1363               FAM.getCachedResult<PreservedCFGCheckerAnalysis>(*F))
1364         CheckCFG(P, F->getName(), *GraphBefore,
1365                  CFG(F, /* TrackBBLifetime */ false));
1366     }
1367     if (const auto *MPtr = unwrapIR<Module>(IR)) {
1368       auto &M = *const_cast<Module *>(MPtr);
1369       if (auto *HashBefore =
1370               MAM.getCachedResult<PreservedModuleHashAnalysis>(M)) {
1371         if (HashBefore->Hash != StructuralHash(M)) {
1372           report_fatal_error(formatv(
1373               "Module changed by {0} without invalidating analyses", P));
1374         }
1375       }
1376     }
1377   });
1378 }
1379 
registerCallbacks(PassInstrumentationCallbacks & PIC)1380 void VerifyInstrumentation::registerCallbacks(
1381     PassInstrumentationCallbacks &PIC) {
1382   PIC.registerAfterPassCallback(
1383       [this](StringRef P, Any IR, const PreservedAnalyses &PassPA) {
1384         if (isIgnored(P) || P == "VerifierPass")
1385           return;
1386         const auto *F = unwrapIR<Function>(IR);
1387         if (!F) {
1388           if (const auto *L = unwrapIR<Loop>(IR))
1389             F = L->getHeader()->getParent();
1390         }
1391 
1392         if (F) {
1393           if (DebugLogging)
1394             dbgs() << "Verifying function " << F->getName() << "\n";
1395 
1396           if (verifyFunction(*F, &errs()))
1397             report_fatal_error(formatv("Broken function found after pass "
1398                                        "\"{0}\", compilation aborted!",
1399                                        P));
1400         } else {
1401           const auto *M = unwrapIR<Module>(IR);
1402           if (!M) {
1403             if (const auto *C = unwrapIR<LazyCallGraph::SCC>(IR))
1404               M = C->begin()->getFunction().getParent();
1405           }
1406 
1407           if (M) {
1408             if (DebugLogging)
1409               dbgs() << "Verifying module " << M->getName() << "\n";
1410 
1411             if (verifyModule(*M, &errs()))
1412               report_fatal_error(formatv("Broken module found after pass "
1413                                          "\"{0}\", compilation aborted!",
1414                                          P));
1415           }
1416         }
1417       });
1418 }
1419 
1420 InLineChangePrinter::~InLineChangePrinter() = default;
1421 
generateIRRepresentation(Any IR,StringRef PassID,IRDataT<EmptyData> & D)1422 void InLineChangePrinter::generateIRRepresentation(Any IR,
1423                                                    StringRef PassID,
1424                                                    IRDataT<EmptyData> &D) {
1425   IRComparer<EmptyData>::analyzeIR(IR, D);
1426 }
1427 
handleAfter(StringRef PassID,std::string & Name,const IRDataT<EmptyData> & Before,const IRDataT<EmptyData> & After,Any IR)1428 void InLineChangePrinter::handleAfter(StringRef PassID, std::string &Name,
1429                                       const IRDataT<EmptyData> &Before,
1430                                       const IRDataT<EmptyData> &After,
1431                                       Any IR) {
1432   SmallString<20> Banner =
1433       formatv("*** IR Dump After {0} on {1} ***\n", PassID, Name);
1434   Out << Banner;
1435   IRComparer<EmptyData>(Before, After)
1436       .compare(getModuleForComparison(IR),
1437                [&](bool InModule, unsigned Minor,
1438                    const FuncDataT<EmptyData> &Before,
1439                    const FuncDataT<EmptyData> &After) -> void {
1440                  handleFunctionCompare(Name, "", PassID, " on ", InModule,
1441                                        Minor, Before, After);
1442                });
1443   Out << "\n";
1444 }
1445 
handleFunctionCompare(StringRef Name,StringRef Prefix,StringRef PassID,StringRef Divider,bool InModule,unsigned Minor,const FuncDataT<EmptyData> & Before,const FuncDataT<EmptyData> & After)1446 void InLineChangePrinter::handleFunctionCompare(
1447     StringRef Name, StringRef Prefix, StringRef PassID, StringRef Divider,
1448     bool InModule, unsigned Minor, const FuncDataT<EmptyData> &Before,
1449     const FuncDataT<EmptyData> &After) {
1450   // Print a banner when this is being shown in the context of a module
1451   if (InModule)
1452     Out << "\n*** IR for function " << Name << " ***\n";
1453 
1454   FuncDataT<EmptyData>::report(
1455       Before, After,
1456       [&](const BlockDataT<EmptyData> *B, const BlockDataT<EmptyData> *A) {
1457         StringRef BStr = B ? B->getBody() : "\n";
1458         StringRef AStr = A ? A->getBody() : "\n";
1459         const std::string Removed =
1460             UseColour ? "\033[31m-%l\033[0m\n" : "-%l\n";
1461         const std::string Added = UseColour ? "\033[32m+%l\033[0m\n" : "+%l\n";
1462         const std::string NoChange = " %l\n";
1463         Out << doSystemDiff(BStr, AStr, Removed, Added, NoChange);
1464       });
1465 }
1466 
registerCallbacks(PassInstrumentationCallbacks & PIC)1467 void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) {
1468   if (PrintChanged == ChangePrinter::DiffVerbose ||
1469       PrintChanged == ChangePrinter::DiffQuiet ||
1470       PrintChanged == ChangePrinter::ColourDiffVerbose ||
1471       PrintChanged == ChangePrinter::ColourDiffQuiet)
1472     TextChangeReporter<IRDataT<EmptyData>>::registerRequiredCallbacks(PIC);
1473 }
1474 
TimeProfilingPassesHandler()1475 TimeProfilingPassesHandler::TimeProfilingPassesHandler() {}
1476 
registerCallbacks(PassInstrumentationCallbacks & PIC)1477 void TimeProfilingPassesHandler::registerCallbacks(
1478     PassInstrumentationCallbacks &PIC) {
1479   if (!getTimeTraceProfilerInstance())
1480     return;
1481   PIC.registerBeforeNonSkippedPassCallback(
1482       [this](StringRef P, Any IR) { this->runBeforePass(P, IR); });
1483   PIC.registerAfterPassCallback(
1484       [this](StringRef P, Any IR, const PreservedAnalyses &) {
1485         this->runAfterPass();
1486       },
1487       true);
1488   PIC.registerAfterPassInvalidatedCallback(
1489       [this](StringRef P, const PreservedAnalyses &) { this->runAfterPass(); },
1490       true);
1491   PIC.registerBeforeAnalysisCallback(
1492       [this](StringRef P, Any IR) { this->runBeforePass(P, IR); });
1493   PIC.registerAfterAnalysisCallback(
1494       [this](StringRef P, Any IR) { this->runAfterPass(); }, true);
1495 }
1496 
runBeforePass(StringRef PassID,Any IR)1497 void TimeProfilingPassesHandler::runBeforePass(StringRef PassID, Any IR) {
1498   timeTraceProfilerBegin(PassID, getIRName(IR));
1499 }
1500 
runAfterPass()1501 void TimeProfilingPassesHandler::runAfterPass() { timeTraceProfilerEnd(); }
1502 
1503 namespace {
1504 
1505 class DisplayNode;
1506 class DotCfgDiffDisplayGraph;
1507 
1508 // Base class for a node or edge in the dot-cfg-changes graph.
1509 class DisplayElement {
1510 public:
1511   // Is this in before, after, or both?
getColour() const1512   StringRef getColour() const { return Colour; }
1513 
1514 protected:
DisplayElement(StringRef Colour)1515   DisplayElement(StringRef Colour) : Colour(Colour) {}
1516   const StringRef Colour;
1517 };
1518 
1519 // An edge representing a transition between basic blocks in the
1520 // dot-cfg-changes graph.
1521 class DisplayEdge : public DisplayElement {
1522 public:
DisplayEdge(std::string Value,DisplayNode & Node,StringRef Colour)1523   DisplayEdge(std::string Value, DisplayNode &Node, StringRef Colour)
1524       : DisplayElement(Colour), Value(Value), Node(Node) {}
1525   // The value on which the transition is made.
getValue() const1526   std::string getValue() const { return Value; }
1527   // The node (representing a basic block) reached by this transition.
getDestinationNode() const1528   const DisplayNode &getDestinationNode() const { return Node; }
1529 
1530 protected:
1531   std::string Value;
1532   const DisplayNode &Node;
1533 };
1534 
1535 // A node in the dot-cfg-changes graph which represents a basic block.
1536 class DisplayNode : public DisplayElement {
1537 public:
1538   // \p C is the content for the node, \p T indicates the colour for the
1539   // outline of the node
DisplayNode(std::string Content,StringRef Colour)1540   DisplayNode(std::string Content, StringRef Colour)
1541       : DisplayElement(Colour), Content(Content) {}
1542 
1543   // Iterator to the child nodes.  Required by GraphWriter.
1544   using ChildIterator = std::unordered_set<DisplayNode *>::const_iterator;
children_begin() const1545   ChildIterator children_begin() const { return Children.cbegin(); }
children_end() const1546   ChildIterator children_end() const { return Children.cend(); }
1547 
1548   // Iterator for the edges.  Required by GraphWriter.
1549   using EdgeIterator = std::vector<DisplayEdge *>::const_iterator;
edges_begin() const1550   EdgeIterator edges_begin() const { return EdgePtrs.cbegin(); }
edges_end() const1551   EdgeIterator edges_end() const { return EdgePtrs.cend(); }
1552 
1553   // Create an edge to \p Node on value \p Value, with colour \p Colour.
1554   void createEdge(StringRef Value, DisplayNode &Node, StringRef Colour);
1555 
1556   // Return the content of this node.
getContent() const1557   std::string getContent() const { return Content; }
1558 
1559   // Return the edge to node \p S.
getEdge(const DisplayNode & To) const1560   const DisplayEdge &getEdge(const DisplayNode &To) const {
1561     assert(EdgeMap.find(&To) != EdgeMap.end() && "Expected to find edge.");
1562     return *EdgeMap.find(&To)->second;
1563   }
1564 
1565   // Return the value for the transition to basic block \p S.
1566   // Required by GraphWriter.
getEdgeSourceLabel(const DisplayNode & Sink) const1567   std::string getEdgeSourceLabel(const DisplayNode &Sink) const {
1568     return getEdge(Sink).getValue();
1569   }
1570 
1571   void createEdgeMap();
1572 
1573 protected:
1574   const std::string Content;
1575 
1576   // Place to collect all of the edges.  Once they are all in the vector,
1577   // the vector will not reallocate so then we can use pointers to them,
1578   // which are required by the graph writing routines.
1579   std::vector<DisplayEdge> Edges;
1580 
1581   std::vector<DisplayEdge *> EdgePtrs;
1582   std::unordered_set<DisplayNode *> Children;
1583   std::unordered_map<const DisplayNode *, const DisplayEdge *> EdgeMap;
1584 
1585   // Safeguard adding of edges.
1586   bool AllEdgesCreated = false;
1587 };
1588 
1589 // Class representing a difference display (corresponds to a pdf file).
1590 class DotCfgDiffDisplayGraph {
1591 public:
DotCfgDiffDisplayGraph(std::string Name)1592   DotCfgDiffDisplayGraph(std::string Name) : GraphName(Name) {}
1593 
1594   // Generate the file into \p DotFile.
1595   void generateDotFile(StringRef DotFile);
1596 
1597   // Iterator to the nodes.  Required by GraphWriter.
1598   using NodeIterator = std::vector<DisplayNode *>::const_iterator;
nodes_begin() const1599   NodeIterator nodes_begin() const {
1600     assert(NodeGenerationComplete && "Unexpected children iterator creation");
1601     return NodePtrs.cbegin();
1602   }
nodes_end() const1603   NodeIterator nodes_end() const {
1604     assert(NodeGenerationComplete && "Unexpected children iterator creation");
1605     return NodePtrs.cend();
1606   }
1607 
1608   // Record the index of the entry node.  At this point, we can build up
1609   // vectors of pointers that are required by the graph routines.
setEntryNode(unsigned N)1610   void setEntryNode(unsigned N) {
1611     // At this point, there will be no new nodes.
1612     assert(!NodeGenerationComplete && "Unexpected node creation");
1613     NodeGenerationComplete = true;
1614     for (auto &N : Nodes)
1615       NodePtrs.emplace_back(&N);
1616 
1617     EntryNode = NodePtrs[N];
1618   }
1619 
1620   // Create a node.
createNode(std::string C,StringRef Colour)1621   void createNode(std::string C, StringRef Colour) {
1622     assert(!NodeGenerationComplete && "Unexpected node creation");
1623     Nodes.emplace_back(C, Colour);
1624   }
1625   // Return the node at index \p N to avoid problems with vectors reallocating.
getNode(unsigned N)1626   DisplayNode &getNode(unsigned N) {
1627     assert(N < Nodes.size() && "Node is out of bounds");
1628     return Nodes[N];
1629   }
size() const1630   unsigned size() const {
1631     assert(NodeGenerationComplete && "Unexpected children iterator creation");
1632     return Nodes.size();
1633   }
1634 
1635   // Return the name of the graph.  Required by GraphWriter.
getGraphName() const1636   std::string getGraphName() const { return GraphName; }
1637 
1638   // Return the string representing the differences for basic block \p Node.
1639   // Required by GraphWriter.
getNodeLabel(const DisplayNode & Node) const1640   std::string getNodeLabel(const DisplayNode &Node) const {
1641     return Node.getContent();
1642   }
1643 
1644   // Return a string with colour information for Dot.  Required by GraphWriter.
getNodeAttributes(const DisplayNode & Node) const1645   std::string getNodeAttributes(const DisplayNode &Node) const {
1646     return attribute(Node.getColour());
1647   }
1648 
1649   // Return a string with colour information for Dot.  Required by GraphWriter.
getEdgeColorAttr(const DisplayNode & From,const DisplayNode & To) const1650   std::string getEdgeColorAttr(const DisplayNode &From,
1651                                const DisplayNode &To) const {
1652     return attribute(From.getEdge(To).getColour());
1653   }
1654 
1655   // Get the starting basic block.  Required by GraphWriter.
getEntryNode() const1656   DisplayNode *getEntryNode() const {
1657     assert(NodeGenerationComplete && "Unexpected children iterator creation");
1658     return EntryNode;
1659   }
1660 
1661 protected:
1662   // Return the string containing the colour to use as a Dot attribute.
attribute(StringRef Colour) const1663   std::string attribute(StringRef Colour) const {
1664     return "color=" + Colour.str();
1665   }
1666 
1667   bool NodeGenerationComplete = false;
1668   const std::string GraphName;
1669   std::vector<DisplayNode> Nodes;
1670   std::vector<DisplayNode *> NodePtrs;
1671   DisplayNode *EntryNode = nullptr;
1672 };
1673 
createEdge(StringRef Value,DisplayNode & Node,StringRef Colour)1674 void DisplayNode::createEdge(StringRef Value, DisplayNode &Node,
1675                              StringRef Colour) {
1676   assert(!AllEdgesCreated && "Expected to be able to still create edges.");
1677   Edges.emplace_back(Value.str(), Node, Colour);
1678   Children.insert(&Node);
1679 }
1680 
createEdgeMap()1681 void DisplayNode::createEdgeMap() {
1682   // No more edges will be added so we can now use pointers to the edges
1683   // as the vector will not grow and reallocate.
1684   AllEdgesCreated = true;
1685   for (auto &E : Edges)
1686     EdgeMap.insert({&E.getDestinationNode(), &E});
1687 }
1688 
1689 class DotCfgDiffNode;
1690 class DotCfgDiff;
1691 
1692 // A class representing a basic block in the Dot difference graph.
1693 class DotCfgDiffNode {
1694 public:
1695   DotCfgDiffNode() = delete;
1696 
1697   // Create a node in Dot difference graph \p G representing the basic block
1698   // represented by \p BD with colour \p Colour (where it exists).
DotCfgDiffNode(DotCfgDiff & G,unsigned N,const BlockDataT<DCData> & BD,StringRef Colour)1699   DotCfgDiffNode(DotCfgDiff &G, unsigned N, const BlockDataT<DCData> &BD,
1700                  StringRef Colour)
1701       : Graph(G), N(N), Data{&BD, nullptr}, Colour(Colour) {}
DotCfgDiffNode(const DotCfgDiffNode & DN)1702   DotCfgDiffNode(const DotCfgDiffNode &DN)
1703       : Graph(DN.Graph), N(DN.N), Data{DN.Data[0], DN.Data[1]},
1704         Colour(DN.Colour), EdgesMap(DN.EdgesMap), Children(DN.Children),
1705         Edges(DN.Edges) {}
1706 
getIndex() const1707   unsigned getIndex() const { return N; }
1708 
1709   // The label of the basic block
getLabel() const1710   StringRef getLabel() const {
1711     assert(Data[0] && "Expected Data[0] to be set.");
1712     return Data[0]->getLabel();
1713   }
1714   // Return the colour for this block
getColour() const1715   StringRef getColour() const { return Colour; }
1716   // Change this basic block from being only in before to being common.
1717   // Save the pointer to \p Other.
setCommon(const BlockDataT<DCData> & Other)1718   void setCommon(const BlockDataT<DCData> &Other) {
1719     assert(!Data[1] && "Expected only one block datum");
1720     Data[1] = &Other;
1721     Colour = CommonColour;
1722   }
1723   // Add an edge to \p E of colour {\p Value, \p Colour}.
addEdge(unsigned E,StringRef Value,StringRef Colour)1724   void addEdge(unsigned E, StringRef Value, StringRef Colour) {
1725     // This is a new edge or it is an edge being made common.
1726     assert((EdgesMap.count(E) == 0 || Colour == CommonColour) &&
1727            "Unexpected edge count and color.");
1728     EdgesMap[E] = {Value.str(), Colour};
1729   }
1730   // Record the children and create edges.
1731   void finalize(DotCfgDiff &G);
1732 
1733   // Return the colour of the edge to node \p S.
getEdgeColour(const unsigned S) const1734   StringRef getEdgeColour(const unsigned S) const {
1735     assert(EdgesMap.count(S) == 1 && "Expected to find edge.");
1736     return EdgesMap.at(S).second;
1737   }
1738 
1739   // Return the string representing the basic block.
1740   std::string getBodyContent() const;
1741 
1742   void createDisplayEdges(DotCfgDiffDisplayGraph &Graph, unsigned DisplayNode,
1743                           std::map<const unsigned, unsigned> &NodeMap) const;
1744 
1745 protected:
1746   DotCfgDiff &Graph;
1747   const unsigned N;
1748   const BlockDataT<DCData> *Data[2];
1749   StringRef Colour;
1750   std::map<const unsigned, std::pair<std::string, StringRef>> EdgesMap;
1751   std::vector<unsigned> Children;
1752   std::vector<unsigned> Edges;
1753 };
1754 
1755 // Class representing the difference graph between two functions.
1756 class DotCfgDiff {
1757 public:
1758   // \p Title is the title given to the graph.  \p EntryNodeName is the
1759   // entry node for the function.  \p Before and \p After are the before
1760   // after versions of the function, respectively.  \p Dir is the directory
1761   // in which to store the results.
1762   DotCfgDiff(StringRef Title, const FuncDataT<DCData> &Before,
1763              const FuncDataT<DCData> &After);
1764 
1765   DotCfgDiff(const DotCfgDiff &) = delete;
1766   DotCfgDiff &operator=(const DotCfgDiff &) = delete;
1767 
1768   DotCfgDiffDisplayGraph createDisplayGraph(StringRef Title,
1769                                             StringRef EntryNodeName);
1770 
1771   // Return a string consisting of the labels for the \p Source and \p Sink.
1772   // The combination allows distinguishing changing transitions on the
1773   // same value (ie, a transition went to X before and goes to Y after).
1774   // Required by GraphWriter.
getEdgeSourceLabel(const unsigned & Source,const unsigned & Sink) const1775   StringRef getEdgeSourceLabel(const unsigned &Source,
1776                                const unsigned &Sink) const {
1777     std::string S =
1778         getNode(Source).getLabel().str() + " " + getNode(Sink).getLabel().str();
1779     assert(EdgeLabels.count(S) == 1 && "Expected to find edge label.");
1780     return EdgeLabels.find(S)->getValue();
1781   }
1782 
1783   // Return the number of basic blocks (nodes).  Required by GraphWriter.
size() const1784   unsigned size() const { return Nodes.size(); }
1785 
getNode(unsigned N) const1786   const DotCfgDiffNode &getNode(unsigned N) const {
1787     assert(N < Nodes.size() && "Unexpected index for node reference");
1788     return Nodes[N];
1789   }
1790 
1791 protected:
1792   // Return the string surrounded by HTML to make it the appropriate colour.
1793   std::string colourize(std::string S, StringRef Colour) const;
1794 
createNode(StringRef Label,const BlockDataT<DCData> & BD,StringRef C)1795   void createNode(StringRef Label, const BlockDataT<DCData> &BD, StringRef C) {
1796     unsigned Pos = Nodes.size();
1797     Nodes.emplace_back(*this, Pos, BD, C);
1798     NodePosition.insert({Label, Pos});
1799   }
1800 
1801   // TODO Nodes should probably be a StringMap<DotCfgDiffNode> after the
1802   // display graph is separated out, which would remove the need for
1803   // NodePosition.
1804   std::vector<DotCfgDiffNode> Nodes;
1805   StringMap<unsigned> NodePosition;
1806   const std::string GraphName;
1807 
1808   StringMap<std::string> EdgeLabels;
1809 };
1810 
getBodyContent() const1811 std::string DotCfgDiffNode::getBodyContent() const {
1812   if (Colour == CommonColour) {
1813     assert(Data[1] && "Expected Data[1] to be set.");
1814 
1815     StringRef SR[2];
1816     for (unsigned I = 0; I < 2; ++I) {
1817       SR[I] = Data[I]->getBody();
1818       // drop initial '\n' if present
1819       if (SR[I][0] == '\n')
1820         SR[I] = SR[I].drop_front();
1821       // drop predecessors as they can be big and are redundant
1822       SR[I] = SR[I].drop_until([](char C) { return C == '\n'; }).drop_front();
1823     }
1824 
1825     SmallString<80> OldLineFormat = formatv(
1826         "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", BeforeColour);
1827     SmallString<80> NewLineFormat = formatv(
1828         "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", AfterColour);
1829     SmallString<80> UnchangedLineFormat = formatv(
1830         "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", CommonColour);
1831     std::string Diff = Data[0]->getLabel().str();
1832     Diff += ":\n<BR align=\"left\"/>" +
1833             doSystemDiff(makeHTMLReady(SR[0]), makeHTMLReady(SR[1]),
1834                          OldLineFormat, NewLineFormat, UnchangedLineFormat);
1835 
1836     // Diff adds in some empty colour changes which are not valid HTML
1837     // so remove them.  Colours are all lowercase alpha characters (as
1838     // listed in https://graphviz.org/pdf/dotguide.pdf).
1839     Regex R("<FONT COLOR=\"\\w+\"></FONT>");
1840     while (true) {
1841       std::string Error;
1842       std::string S = R.sub("", Diff, &Error);
1843       if (Error != "")
1844         return Error;
1845       if (S == Diff)
1846         return Diff;
1847       Diff = S;
1848     }
1849     llvm_unreachable("Should not get here");
1850   }
1851 
1852   // Put node out in the appropriate colour.
1853   assert(!Data[1] && "Data[1] is set unexpectedly.");
1854   std::string Body = makeHTMLReady(Data[0]->getBody());
1855   const StringRef BS = Body;
1856   StringRef BS1 = BS;
1857   // Drop leading newline, if present.
1858   if (BS.front() == '\n')
1859     BS1 = BS1.drop_front(1);
1860   // Get label.
1861   StringRef Label = BS1.take_until([](char C) { return C == ':'; });
1862   // drop predecessors as they can be big and are redundant
1863   BS1 = BS1.drop_until([](char C) { return C == '\n'; }).drop_front();
1864 
1865   std::string S = "<FONT COLOR=\"" + Colour.str() + "\">" + Label.str() + ":";
1866 
1867   // align each line to the left.
1868   while (BS1.size()) {
1869     S.append("<BR align=\"left\"/>");
1870     StringRef Line = BS1.take_until([](char C) { return C == '\n'; });
1871     S.append(Line.str());
1872     BS1 = BS1.drop_front(Line.size() + 1);
1873   }
1874   S.append("<BR align=\"left\"/></FONT>");
1875   return S;
1876 }
1877 
colourize(std::string S,StringRef Colour) const1878 std::string DotCfgDiff::colourize(std::string S, StringRef Colour) const {
1879   if (S.length() == 0)
1880     return S;
1881   return "<FONT COLOR=\"" + Colour.str() + "\">" + S + "</FONT>";
1882 }
1883 
DotCfgDiff(StringRef Title,const FuncDataT<DCData> & Before,const FuncDataT<DCData> & After)1884 DotCfgDiff::DotCfgDiff(StringRef Title, const FuncDataT<DCData> &Before,
1885                        const FuncDataT<DCData> &After)
1886     : GraphName(Title.str()) {
1887   StringMap<StringRef> EdgesMap;
1888 
1889   // Handle each basic block in the before IR.
1890   for (auto &B : Before.getData()) {
1891     StringRef Label = B.getKey();
1892     const BlockDataT<DCData> &BD = B.getValue();
1893     createNode(Label, BD, BeforeColour);
1894 
1895     // Create transitions with names made up of the from block label, the value
1896     // on which the transition is made and the to block label.
1897     for (StringMap<std::string>::const_iterator Sink = BD.getData().begin(),
1898                                                 E = BD.getData().end();
1899          Sink != E; ++Sink) {
1900       std::string Key = (Label + " " + Sink->getKey().str()).str() + " " +
1901                         BD.getData().getSuccessorLabel(Sink->getKey()).str();
1902       EdgesMap.insert({Key, BeforeColour});
1903     }
1904   }
1905 
1906   // Handle each basic block in the after IR
1907   for (auto &A : After.getData()) {
1908     StringRef Label = A.getKey();
1909     const BlockDataT<DCData> &BD = A.getValue();
1910     unsigned C = NodePosition.count(Label);
1911     if (C == 0)
1912       // This only exists in the after IR.  Create the node.
1913       createNode(Label, BD, AfterColour);
1914     else {
1915       assert(C == 1 && "Unexpected multiple nodes.");
1916       Nodes[NodePosition[Label]].setCommon(BD);
1917     }
1918     // Add in the edges between the nodes (as common or only in after).
1919     for (StringMap<std::string>::const_iterator Sink = BD.getData().begin(),
1920                                                 E = BD.getData().end();
1921          Sink != E; ++Sink) {
1922       std::string Key = (Label + " " + Sink->getKey().str()).str() + " " +
1923                         BD.getData().getSuccessorLabel(Sink->getKey()).str();
1924       unsigned C = EdgesMap.count(Key);
1925       if (C == 0)
1926         EdgesMap.insert({Key, AfterColour});
1927       else {
1928         EdgesMap[Key] = CommonColour;
1929       }
1930     }
1931   }
1932 
1933   // Now go through the map of edges and add them to the node.
1934   for (auto &E : EdgesMap) {
1935     // Extract the source, sink and value from the edge key.
1936     StringRef S = E.getKey();
1937     auto SP1 = S.rsplit(' ');
1938     auto &SourceSink = SP1.first;
1939     auto SP2 = SourceSink.split(' ');
1940     StringRef Source = SP2.first;
1941     StringRef Sink = SP2.second;
1942     StringRef Value = SP1.second;
1943 
1944     assert(NodePosition.count(Source) == 1 && "Expected to find node.");
1945     DotCfgDiffNode &SourceNode = Nodes[NodePosition[Source]];
1946     assert(NodePosition.count(Sink) == 1 && "Expected to find node.");
1947     unsigned SinkNode = NodePosition[Sink];
1948     StringRef Colour = E.second;
1949 
1950     // Look for an edge from Source to Sink
1951     if (EdgeLabels.count(SourceSink) == 0)
1952       EdgeLabels.insert({SourceSink, colourize(Value.str(), Colour)});
1953     else {
1954       StringRef V = EdgeLabels.find(SourceSink)->getValue();
1955       std::string NV = colourize(V.str() + " " + Value.str(), Colour);
1956       Colour = CommonColour;
1957       EdgeLabels[SourceSink] = NV;
1958     }
1959     SourceNode.addEdge(SinkNode, Value, Colour);
1960   }
1961   for (auto &I : Nodes)
1962     I.finalize(*this);
1963 }
1964 
createDisplayGraph(StringRef Title,StringRef EntryNodeName)1965 DotCfgDiffDisplayGraph DotCfgDiff::createDisplayGraph(StringRef Title,
1966                                                       StringRef EntryNodeName) {
1967   assert(NodePosition.count(EntryNodeName) == 1 &&
1968          "Expected to find entry block in map.");
1969   unsigned Entry = NodePosition[EntryNodeName];
1970   assert(Entry < Nodes.size() && "Expected to find entry node");
1971   DotCfgDiffDisplayGraph G(Title.str());
1972 
1973   std::map<const unsigned, unsigned> NodeMap;
1974 
1975   int EntryIndex = -1;
1976   unsigned Index = 0;
1977   for (auto &I : Nodes) {
1978     if (I.getIndex() == Entry)
1979       EntryIndex = Index;
1980     G.createNode(I.getBodyContent(), I.getColour());
1981     NodeMap.insert({I.getIndex(), Index++});
1982   }
1983   assert(EntryIndex >= 0 && "Expected entry node index to be set.");
1984   G.setEntryNode(EntryIndex);
1985 
1986   for (auto &I : NodeMap) {
1987     unsigned SourceNode = I.first;
1988     unsigned DisplayNode = I.second;
1989     getNode(SourceNode).createDisplayEdges(G, DisplayNode, NodeMap);
1990   }
1991   return G;
1992 }
1993 
createDisplayEdges(DotCfgDiffDisplayGraph & DisplayGraph,unsigned DisplayNodeIndex,std::map<const unsigned,unsigned> & NodeMap) const1994 void DotCfgDiffNode::createDisplayEdges(
1995     DotCfgDiffDisplayGraph &DisplayGraph, unsigned DisplayNodeIndex,
1996     std::map<const unsigned, unsigned> &NodeMap) const {
1997 
1998   DisplayNode &SourceDisplayNode = DisplayGraph.getNode(DisplayNodeIndex);
1999 
2000   for (auto I : Edges) {
2001     unsigned SinkNodeIndex = I;
2002     StringRef Colour = getEdgeColour(SinkNodeIndex);
2003     const DotCfgDiffNode *SinkNode = &Graph.getNode(SinkNodeIndex);
2004 
2005     StringRef Label = Graph.getEdgeSourceLabel(getIndex(), SinkNodeIndex);
2006     DisplayNode &SinkDisplayNode = DisplayGraph.getNode(SinkNode->getIndex());
2007     SourceDisplayNode.createEdge(Label, SinkDisplayNode, Colour);
2008   }
2009   SourceDisplayNode.createEdgeMap();
2010 }
2011 
finalize(DotCfgDiff & G)2012 void DotCfgDiffNode::finalize(DotCfgDiff &G) {
2013   for (auto E : EdgesMap) {
2014     Children.emplace_back(E.first);
2015     Edges.emplace_back(E.first);
2016   }
2017 }
2018 
2019 } // namespace
2020 
2021 namespace llvm {
2022 
2023 template <> struct GraphTraits<DotCfgDiffDisplayGraph *> {
2024   using NodeRef = const DisplayNode *;
2025   using ChildIteratorType = DisplayNode::ChildIterator;
2026   using nodes_iterator = DotCfgDiffDisplayGraph::NodeIterator;
2027   using EdgeRef = const DisplayEdge *;
2028   using ChildEdgeIterator = DisplayNode::EdgeIterator;
2029 
getEntryNodellvm::GraphTraits2030   static NodeRef getEntryNode(const DotCfgDiffDisplayGraph *G) {
2031     return G->getEntryNode();
2032   }
child_beginllvm::GraphTraits2033   static ChildIteratorType child_begin(NodeRef N) {
2034     return N->children_begin();
2035   }
child_endllvm::GraphTraits2036   static ChildIteratorType child_end(NodeRef N) { return N->children_end(); }
nodes_beginllvm::GraphTraits2037   static nodes_iterator nodes_begin(const DotCfgDiffDisplayGraph *G) {
2038     return G->nodes_begin();
2039   }
nodes_endllvm::GraphTraits2040   static nodes_iterator nodes_end(const DotCfgDiffDisplayGraph *G) {
2041     return G->nodes_end();
2042   }
child_edge_beginllvm::GraphTraits2043   static ChildEdgeIterator child_edge_begin(NodeRef N) {
2044     return N->edges_begin();
2045   }
child_edge_endllvm::GraphTraits2046   static ChildEdgeIterator child_edge_end(NodeRef N) { return N->edges_end(); }
edge_destllvm::GraphTraits2047   static NodeRef edge_dest(EdgeRef E) { return &E->getDestinationNode(); }
sizellvm::GraphTraits2048   static unsigned size(const DotCfgDiffDisplayGraph *G) { return G->size(); }
2049 };
2050 
2051 template <>
2052 struct DOTGraphTraits<DotCfgDiffDisplayGraph *> : public DefaultDOTGraphTraits {
DOTGraphTraitsllvm::DOTGraphTraits2053   explicit DOTGraphTraits(bool Simple = false)
2054       : DefaultDOTGraphTraits(Simple) {}
2055 
renderNodesUsingHTMLllvm::DOTGraphTraits2056   static bool renderNodesUsingHTML() { return true; }
getGraphNamellvm::DOTGraphTraits2057   static std::string getGraphName(const DotCfgDiffDisplayGraph *DiffData) {
2058     return DiffData->getGraphName();
2059   }
2060   static std::string
getGraphPropertiesllvm::DOTGraphTraits2061   getGraphProperties(const DotCfgDiffDisplayGraph *DiffData) {
2062     return "\tsize=\"190, 190\";\n";
2063   }
getNodeLabelllvm::DOTGraphTraits2064   static std::string getNodeLabel(const DisplayNode *Node,
2065                                   const DotCfgDiffDisplayGraph *DiffData) {
2066     return DiffData->getNodeLabel(*Node);
2067   }
getNodeAttributesllvm::DOTGraphTraits2068   static std::string getNodeAttributes(const DisplayNode *Node,
2069                                        const DotCfgDiffDisplayGraph *DiffData) {
2070     return DiffData->getNodeAttributes(*Node);
2071   }
getEdgeSourceLabelllvm::DOTGraphTraits2072   static std::string getEdgeSourceLabel(const DisplayNode *From,
2073                                         DisplayNode::ChildIterator &To) {
2074     return From->getEdgeSourceLabel(**To);
2075   }
getEdgeAttributesllvm::DOTGraphTraits2076   static std::string getEdgeAttributes(const DisplayNode *From,
2077                                        DisplayNode::ChildIterator &To,
2078                                        const DotCfgDiffDisplayGraph *DiffData) {
2079     return DiffData->getEdgeColorAttr(*From, **To);
2080   }
2081 };
2082 
2083 } // namespace llvm
2084 
2085 namespace {
2086 
generateDotFile(StringRef DotFile)2087 void DotCfgDiffDisplayGraph::generateDotFile(StringRef DotFile) {
2088   std::error_code EC;
2089   raw_fd_ostream OutStream(DotFile, EC);
2090   if (EC) {
2091     errs() << "Error: " << EC.message() << "\n";
2092     return;
2093   }
2094   WriteGraph(OutStream, this, false);
2095   OutStream.flush();
2096   OutStream.close();
2097 }
2098 
2099 } // namespace
2100 
2101 namespace llvm {
2102 
DCData(const BasicBlock & B)2103 DCData::DCData(const BasicBlock &B) {
2104   // Build up transition labels.
2105   const Instruction *Term = B.getTerminator();
2106   if (const BranchInst *Br = dyn_cast<const BranchInst>(Term))
2107     if (Br->isUnconditional())
2108       addSuccessorLabel(Br->getSuccessor(0)->getName().str(), "");
2109     else {
2110       addSuccessorLabel(Br->getSuccessor(0)->getName().str(), "true");
2111       addSuccessorLabel(Br->getSuccessor(1)->getName().str(), "false");
2112     }
2113   else if (const SwitchInst *Sw = dyn_cast<const SwitchInst>(Term)) {
2114     addSuccessorLabel(Sw->case_default()->getCaseSuccessor()->getName().str(),
2115                       "default");
2116     for (auto &C : Sw->cases()) {
2117       assert(C.getCaseValue() && "Expected to find case value.");
2118       SmallString<20> Value = formatv("{0}", C.getCaseValue()->getSExtValue());
2119       addSuccessorLabel(C.getCaseSuccessor()->getName().str(), Value);
2120     }
2121   } else
2122     for (const BasicBlock *Succ : successors(&B))
2123       addSuccessorLabel(Succ->getName().str(), "");
2124 }
2125 
DotCfgChangeReporter(bool Verbose)2126 DotCfgChangeReporter::DotCfgChangeReporter(bool Verbose)
2127     : ChangeReporter<IRDataT<DCData>>(Verbose) {}
2128 
handleFunctionCompare(StringRef Name,StringRef Prefix,StringRef PassID,StringRef Divider,bool InModule,unsigned Minor,const FuncDataT<DCData> & Before,const FuncDataT<DCData> & After)2129 void DotCfgChangeReporter::handleFunctionCompare(
2130     StringRef Name, StringRef Prefix, StringRef PassID, StringRef Divider,
2131     bool InModule, unsigned Minor, const FuncDataT<DCData> &Before,
2132     const FuncDataT<DCData> &After) {
2133   assert(HTML && "Expected outstream to be set");
2134   SmallString<8> Extender;
2135   SmallString<8> Number;
2136   // Handle numbering and file names.
2137   if (InModule) {
2138     Extender = formatv("{0}_{1}", N, Minor);
2139     Number = formatv("{0}.{1}", N, Minor);
2140   } else {
2141     Extender = formatv("{0}", N);
2142     Number = formatv("{0}", N);
2143   }
2144   // Create a temporary file name for the dot file.
2145   SmallVector<char, 128> SV;
2146   sys::fs::createUniquePath("cfgdot-%%%%%%.dot", SV, true);
2147   std::string DotFile = Twine(SV).str();
2148 
2149   SmallString<20> PDFFileName = formatv("diff_{0}.pdf", Extender);
2150   SmallString<200> Text;
2151 
2152   Text = formatv("{0}.{1}{2}{3}{4}", Number, Prefix, makeHTMLReady(PassID),
2153                  Divider, Name);
2154 
2155   DotCfgDiff Diff(Text, Before, After);
2156   std::string EntryBlockName = After.getEntryBlockName();
2157   // Use the before entry block if the after entry block was removed.
2158   if (EntryBlockName == "")
2159     EntryBlockName = Before.getEntryBlockName();
2160   assert(EntryBlockName != "" && "Expected to find entry block");
2161 
2162   DotCfgDiffDisplayGraph DG = Diff.createDisplayGraph(Text, EntryBlockName);
2163   DG.generateDotFile(DotFile);
2164 
2165   *HTML << genHTML(Text, DotFile, PDFFileName);
2166   std::error_code EC = sys::fs::remove(DotFile);
2167   if (EC)
2168     errs() << "Error: " << EC.message() << "\n";
2169 }
2170 
genHTML(StringRef Text,StringRef DotFile,StringRef PDFFileName)2171 std::string DotCfgChangeReporter::genHTML(StringRef Text, StringRef DotFile,
2172                                           StringRef PDFFileName) {
2173   SmallString<20> PDFFile = formatv("{0}/{1}", DotCfgDir, PDFFileName);
2174   // Create the PDF file.
2175   static ErrorOr<std::string> DotExe = sys::findProgramByName(DotBinary);
2176   if (!DotExe)
2177     return "Unable to find dot executable.";
2178 
2179   StringRef Args[] = {DotBinary, "-Tpdf", "-o", PDFFile, DotFile};
2180   int Result = sys::ExecuteAndWait(*DotExe, Args, std::nullopt);
2181   if (Result < 0)
2182     return "Error executing system dot.";
2183 
2184   // Create the HTML tag refering to the PDF file.
2185   SmallString<200> S = formatv(
2186       "  <a href=\"{0}\" target=\"_blank\">{1}</a><br/>\n", PDFFileName, Text);
2187   return S.c_str();
2188 }
2189 
handleInitialIR(Any IR)2190 void DotCfgChangeReporter::handleInitialIR(Any IR) {
2191   assert(HTML && "Expected outstream to be set");
2192   *HTML << "<button type=\"button\" class=\"collapsible\">0. "
2193         << "Initial IR (by function)</button>\n"
2194         << "<div class=\"content\">\n"
2195         << "  <p>\n";
2196   // Create representation of IR
2197   IRDataT<DCData> Data;
2198   IRComparer<DCData>::analyzeIR(IR, Data);
2199   // Now compare it against itself, which will have everything the
2200   // same and will generate the files.
2201   IRComparer<DCData>(Data, Data)
2202       .compare(getModuleForComparison(IR),
2203                [&](bool InModule, unsigned Minor,
2204                    const FuncDataT<DCData> &Before,
2205                    const FuncDataT<DCData> &After) -> void {
2206                  handleFunctionCompare("", " ", "Initial IR", "", InModule,
2207                                        Minor, Before, After);
2208                });
2209   *HTML << "  </p>\n"
2210         << "</div><br/>\n";
2211   ++N;
2212 }
2213 
generateIRRepresentation(Any IR,StringRef PassID,IRDataT<DCData> & Data)2214 void DotCfgChangeReporter::generateIRRepresentation(Any IR, StringRef PassID,
2215                                                     IRDataT<DCData> &Data) {
2216   IRComparer<DCData>::analyzeIR(IR, Data);
2217 }
2218 
omitAfter(StringRef PassID,std::string & Name)2219 void DotCfgChangeReporter::omitAfter(StringRef PassID, std::string &Name) {
2220   assert(HTML && "Expected outstream to be set");
2221   SmallString<20> Banner =
2222       formatv("  <a>{0}. Pass {1} on {2} omitted because no change</a><br/>\n",
2223               N, makeHTMLReady(PassID), Name);
2224   *HTML << Banner;
2225   ++N;
2226 }
2227 
handleAfter(StringRef PassID,std::string & Name,const IRDataT<DCData> & Before,const IRDataT<DCData> & After,Any IR)2228 void DotCfgChangeReporter::handleAfter(StringRef PassID, std::string &Name,
2229                                        const IRDataT<DCData> &Before,
2230                                        const IRDataT<DCData> &After, Any IR) {
2231   assert(HTML && "Expected outstream to be set");
2232   IRComparer<DCData>(Before, After)
2233       .compare(getModuleForComparison(IR),
2234                [&](bool InModule, unsigned Minor,
2235                    const FuncDataT<DCData> &Before,
2236                    const FuncDataT<DCData> &After) -> void {
2237                  handleFunctionCompare(Name, " Pass ", PassID, " on ", InModule,
2238                                        Minor, Before, After);
2239                });
2240   *HTML << "    </p></div>\n";
2241   ++N;
2242 }
2243 
handleInvalidated(StringRef PassID)2244 void DotCfgChangeReporter::handleInvalidated(StringRef PassID) {
2245   assert(HTML && "Expected outstream to be set");
2246   SmallString<20> Banner =
2247       formatv("  <a>{0}. {1} invalidated</a><br/>\n", N, makeHTMLReady(PassID));
2248   *HTML << Banner;
2249   ++N;
2250 }
2251 
handleFiltered(StringRef PassID,std::string & Name)2252 void DotCfgChangeReporter::handleFiltered(StringRef PassID, std::string &Name) {
2253   assert(HTML && "Expected outstream to be set");
2254   SmallString<20> Banner =
2255       formatv("  <a>{0}. Pass {1} on {2} filtered out</a><br/>\n", N,
2256               makeHTMLReady(PassID), Name);
2257   *HTML << Banner;
2258   ++N;
2259 }
2260 
handleIgnored(StringRef PassID,std::string & Name)2261 void DotCfgChangeReporter::handleIgnored(StringRef PassID, std::string &Name) {
2262   assert(HTML && "Expected outstream to be set");
2263   SmallString<20> Banner = formatv("  <a>{0}. {1} on {2} ignored</a><br/>\n", N,
2264                                    makeHTMLReady(PassID), Name);
2265   *HTML << Banner;
2266   ++N;
2267 }
2268 
initializeHTML()2269 bool DotCfgChangeReporter::initializeHTML() {
2270   std::error_code EC;
2271   HTML = std::make_unique<raw_fd_ostream>(DotCfgDir + "/passes.html", EC);
2272   if (EC) {
2273     HTML = nullptr;
2274     return false;
2275   }
2276 
2277   *HTML << "<!doctype html>"
2278         << "<html>"
2279         << "<head>"
2280         << "<style>.collapsible { "
2281         << "background-color: #777;"
2282         << " color: white;"
2283         << " cursor: pointer;"
2284         << " padding: 18px;"
2285         << " width: 100%;"
2286         << " border: none;"
2287         << " text-align: left;"
2288         << " outline: none;"
2289         << " font-size: 15px;"
2290         << "} .active, .collapsible:hover {"
2291         << " background-color: #555;"
2292         << "} .content {"
2293         << " padding: 0 18px;"
2294         << " display: none;"
2295         << " overflow: hidden;"
2296         << " background-color: #f1f1f1;"
2297         << "}"
2298         << "</style>"
2299         << "<title>passes.html</title>"
2300         << "</head>\n"
2301         << "<body>";
2302   return true;
2303 }
2304 
~DotCfgChangeReporter()2305 DotCfgChangeReporter::~DotCfgChangeReporter() {
2306   if (!HTML)
2307     return;
2308   *HTML
2309       << "<script>var coll = document.getElementsByClassName(\"collapsible\");"
2310       << "var i;"
2311       << "for (i = 0; i < coll.length; i++) {"
2312       << "coll[i].addEventListener(\"click\", function() {"
2313       << " this.classList.toggle(\"active\");"
2314       << " var content = this.nextElementSibling;"
2315       << " if (content.style.display === \"block\"){"
2316       << " content.style.display = \"none\";"
2317       << " }"
2318       << " else {"
2319       << " content.style.display= \"block\";"
2320       << " }"
2321       << " });"
2322       << " }"
2323       << "</script>"
2324       << "</body>"
2325       << "</html>\n";
2326   HTML->flush();
2327   HTML->close();
2328 }
2329 
registerCallbacks(PassInstrumentationCallbacks & PIC)2330 void DotCfgChangeReporter::registerCallbacks(
2331     PassInstrumentationCallbacks &PIC) {
2332   if (PrintChanged == ChangePrinter::DotCfgVerbose ||
2333        PrintChanged == ChangePrinter::DotCfgQuiet) {
2334     SmallString<128> OutputDir;
2335     sys::fs::expand_tilde(DotCfgDir, OutputDir);
2336     sys::fs::make_absolute(OutputDir);
2337     assert(!OutputDir.empty() && "expected output dir to be non-empty");
2338     DotCfgDir = OutputDir.c_str();
2339     if (initializeHTML()) {
2340       ChangeReporter<IRDataT<DCData>>::registerRequiredCallbacks(PIC);
2341       return;
2342     }
2343     dbgs() << "Unable to open output stream for -cfg-dot-changed\n";
2344   }
2345 }
2346 
StandardInstrumentations(LLVMContext & Context,bool DebugLogging,bool VerifyEach,PrintPassOptions PrintPassOpts)2347 StandardInstrumentations::StandardInstrumentations(
2348     LLVMContext &Context, bool DebugLogging, bool VerifyEach,
2349     PrintPassOptions PrintPassOpts)
2350     : PrintPass(DebugLogging, PrintPassOpts),
2351       OptNone(DebugLogging),
2352       OptPassGate(Context),
2353       PrintChangedIR(PrintChanged == ChangePrinter::Verbose),
2354       PrintChangedDiff(PrintChanged == ChangePrinter::DiffVerbose ||
2355                            PrintChanged == ChangePrinter::ColourDiffVerbose,
2356                        PrintChanged == ChangePrinter::ColourDiffVerbose ||
2357                            PrintChanged == ChangePrinter::ColourDiffQuiet),
2358       WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose),
2359       Verify(DebugLogging), VerifyEach(VerifyEach) {}
2360 
2361 PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
2362     nullptr;
2363 
reportCrashIR()2364 void PrintCrashIRInstrumentation::reportCrashIR() {
2365   if (!PrintOnCrashPath.empty()) {
2366     std::error_code EC;
2367     raw_fd_ostream Out(PrintOnCrashPath, EC);
2368     if (EC)
2369       report_fatal_error(errorCodeToError(EC));
2370     Out << SavedIR;
2371   } else {
2372     dbgs() << SavedIR;
2373   }
2374 }
2375 
SignalHandler(void *)2376 void PrintCrashIRInstrumentation::SignalHandler(void *) {
2377   // Called by signal handlers so do not lock here
2378   // Is the PrintCrashIRInstrumentation still alive?
2379   if (!CrashReporter)
2380     return;
2381 
2382   assert((PrintOnCrash || !PrintOnCrashPath.empty()) &&
2383          "Did not expect to get here without option set.");
2384   CrashReporter->reportCrashIR();
2385 }
2386 
~PrintCrashIRInstrumentation()2387 PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() {
2388   if (!CrashReporter)
2389     return;
2390 
2391   assert((PrintOnCrash || !PrintOnCrashPath.empty()) &&
2392          "Did not expect to get here without option set.");
2393   CrashReporter = nullptr;
2394 }
2395 
registerCallbacks(PassInstrumentationCallbacks & PIC)2396 void PrintCrashIRInstrumentation::registerCallbacks(
2397     PassInstrumentationCallbacks &PIC) {
2398   if ((!PrintOnCrash && PrintOnCrashPath.empty()) || CrashReporter)
2399     return;
2400 
2401   sys::AddSignalHandler(SignalHandler, nullptr);
2402   CrashReporter = this;
2403 
2404   PIC.registerBeforeNonSkippedPassCallback(
2405       [&PIC, this](StringRef PassID, Any IR) {
2406         SavedIR.clear();
2407         raw_string_ostream OS(SavedIR);
2408         OS << formatv("*** Dump of {0}IR Before Last Pass {1}",
2409                       llvm::forcePrintModuleIR() ? "Module " : "", PassID);
2410         if (!isInteresting(IR, PassID, PIC.getPassNameForClassName(PassID))) {
2411           OS << " Filtered Out ***\n";
2412           return;
2413         }
2414         OS << " Started ***\n";
2415         unwrapAndPrint(OS, IR);
2416       });
2417 }
2418 
registerCallbacks(PassInstrumentationCallbacks & PIC,ModuleAnalysisManager * MAM)2419 void StandardInstrumentations::registerCallbacks(
2420     PassInstrumentationCallbacks &PIC, ModuleAnalysisManager *MAM) {
2421   PrintIR.registerCallbacks(PIC);
2422   PrintPass.registerCallbacks(PIC);
2423   TimePasses.registerCallbacks(PIC);
2424   OptNone.registerCallbacks(PIC);
2425   OptPassGate.registerCallbacks(PIC);
2426   PrintChangedIR.registerCallbacks(PIC);
2427   PseudoProbeVerification.registerCallbacks(PIC);
2428   if (VerifyEach)
2429     Verify.registerCallbacks(PIC);
2430   PrintChangedDiff.registerCallbacks(PIC);
2431   WebsiteChangeReporter.registerCallbacks(PIC);
2432   ChangeTester.registerCallbacks(PIC);
2433   PrintCrashIR.registerCallbacks(PIC);
2434   if (MAM)
2435     PreservedCFGChecker.registerCallbacks(PIC, *MAM);
2436 
2437   // TimeProfiling records the pass running time cost.
2438   // Its 'BeforePassCallback' can be appended at the tail of all the
2439   // BeforeCallbacks by calling `registerCallbacks` in the end.
2440   // Its 'AfterPassCallback' is put at the front of all the
2441   // AfterCallbacks by its `registerCallbacks`. This is necessary
2442   // to ensure that other callbacks are not included in the timings.
2443   TimeProfilingPasses.registerCallbacks(PIC);
2444 }
2445 
2446 template class ChangeReporter<std::string>;
2447 template class TextChangeReporter<std::string>;
2448 
2449 template class BlockDataT<EmptyData>;
2450 template class FuncDataT<EmptyData>;
2451 template class IRDataT<EmptyData>;
2452 template class ChangeReporter<IRDataT<EmptyData>>;
2453 template class TextChangeReporter<IRDataT<EmptyData>>;
2454 template class IRComparer<EmptyData>;
2455 
2456 } // namespace llvm
2457