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