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