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