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