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 print() << "Running pass: " << PassID << " on " << getIRName(IR) << "\n"; 944 Indent += 2; 945 }); 946 PIC.registerAfterPassCallback( 947 [this, SpecialPasses](StringRef PassID, Any IR, 948 const PreservedAnalyses &) { 949 if (isSpecialPass(PassID, SpecialPasses)) 950 return; 951 952 Indent -= 2; 953 }); 954 PIC.registerAfterPassInvalidatedCallback( 955 [this, SpecialPasses](StringRef PassID, Any IR) { 956 if (isSpecialPass(PassID, SpecialPasses)) 957 return; 958 959 Indent -= 2; 960 }); 961 962 if (!Opts.SkipAnalyses) { 963 PIC.registerBeforeAnalysisCallback([this](StringRef PassID, Any IR) { 964 print() << "Running analysis: " << PassID << " on " << getIRName(IR) 965 << "\n"; 966 Indent += 2; 967 }); 968 PIC.registerAfterAnalysisCallback( 969 [this](StringRef PassID, Any IR) { Indent -= 2; }); 970 PIC.registerAnalysisInvalidatedCallback([this](StringRef PassID, Any IR) { 971 print() << "Invalidating analysis: " << PassID << " on " << getIRName(IR) 972 << "\n"; 973 }); 974 PIC.registerAnalysesClearedCallback([this](StringRef IRName) { 975 print() << "Clearing all analysis results for: " << IRName << "\n"; 976 }); 977 } 978 } 979 980 PreservedCFGCheckerInstrumentation::CFG::CFG(const Function *F, 981 bool TrackBBLifetime) { 982 if (TrackBBLifetime) 983 BBGuards = DenseMap<intptr_t, BBGuard>(F->size()); 984 for (const auto &BB : *F) { 985 if (BBGuards) 986 BBGuards->try_emplace(intptr_t(&BB), &BB); 987 for (auto *Succ : successors(&BB)) { 988 Graph[&BB][Succ]++; 989 if (BBGuards) 990 BBGuards->try_emplace(intptr_t(Succ), Succ); 991 } 992 } 993 } 994 995 static void printBBName(raw_ostream &out, const BasicBlock *BB) { 996 if (BB->hasName()) { 997 out << BB->getName() << "<" << BB << ">"; 998 return; 999 } 1000 1001 if (!BB->getParent()) { 1002 out << "unnamed_removed<" << BB << ">"; 1003 return; 1004 } 1005 1006 if (BB->isEntryBlock()) { 1007 out << "entry" 1008 << "<" << BB << ">"; 1009 return; 1010 } 1011 1012 unsigned FuncOrderBlockNum = 0; 1013 for (auto &FuncBB : *BB->getParent()) { 1014 if (&FuncBB == BB) 1015 break; 1016 FuncOrderBlockNum++; 1017 } 1018 out << "unnamed_" << FuncOrderBlockNum << "<" << BB << ">"; 1019 } 1020 1021 void PreservedCFGCheckerInstrumentation::CFG::printDiff(raw_ostream &out, 1022 const CFG &Before, 1023 const CFG &After) { 1024 assert(!After.isPoisoned()); 1025 if (Before.isPoisoned()) { 1026 out << "Some blocks were deleted\n"; 1027 return; 1028 } 1029 1030 // Find and print graph differences. 1031 if (Before.Graph.size() != After.Graph.size()) 1032 out << "Different number of non-leaf basic blocks: before=" 1033 << Before.Graph.size() << ", after=" << After.Graph.size() << "\n"; 1034 1035 for (auto &BB : Before.Graph) { 1036 auto BA = After.Graph.find(BB.first); 1037 if (BA == After.Graph.end()) { 1038 out << "Non-leaf block "; 1039 printBBName(out, BB.first); 1040 out << " is removed (" << BB.second.size() << " successors)\n"; 1041 } 1042 } 1043 1044 for (auto &BA : After.Graph) { 1045 auto BB = Before.Graph.find(BA.first); 1046 if (BB == Before.Graph.end()) { 1047 out << "Non-leaf block "; 1048 printBBName(out, BA.first); 1049 out << " is added (" << BA.second.size() << " successors)\n"; 1050 continue; 1051 } 1052 1053 if (BB->second == BA.second) 1054 continue; 1055 1056 out << "Different successors of block "; 1057 printBBName(out, BA.first); 1058 out << " (unordered):\n"; 1059 out << "- before (" << BB->second.size() << "): "; 1060 for (auto &SuccB : BB->second) { 1061 printBBName(out, SuccB.first); 1062 if (SuccB.second != 1) 1063 out << "(" << SuccB.second << "), "; 1064 else 1065 out << ", "; 1066 } 1067 out << "\n"; 1068 out << "- after (" << BA.second.size() << "): "; 1069 for (auto &SuccA : BA.second) { 1070 printBBName(out, SuccA.first); 1071 if (SuccA.second != 1) 1072 out << "(" << SuccA.second << "), "; 1073 else 1074 out << ", "; 1075 } 1076 out << "\n"; 1077 } 1078 } 1079 1080 // PreservedCFGCheckerInstrumentation uses PreservedCFGCheckerAnalysis to check 1081 // passes, that reported they kept CFG analyses up-to-date, did not actually 1082 // change CFG. This check is done as follows. Before every functional pass in 1083 // BeforeNonSkippedPassCallback a CFG snapshot (an instance of 1084 // PreservedCFGCheckerInstrumentation::CFG) is requested from 1085 // FunctionAnalysisManager as a result of PreservedCFGCheckerAnalysis. When the 1086 // functional pass finishes and reports that CFGAnalyses or AllAnalyses are 1087 // up-to-date then the cached result of PreservedCFGCheckerAnalysis (if 1088 // available) is checked to be equal to a freshly created CFG snapshot. 1089 struct PreservedCFGCheckerAnalysis 1090 : public AnalysisInfoMixin<PreservedCFGCheckerAnalysis> { 1091 friend AnalysisInfoMixin<PreservedCFGCheckerAnalysis>; 1092 1093 static AnalysisKey Key; 1094 1095 public: 1096 /// Provide the result type for this analysis pass. 1097 using Result = PreservedCFGCheckerInstrumentation::CFG; 1098 1099 /// Run the analysis pass over a function and produce CFG. 1100 Result run(Function &F, FunctionAnalysisManager &FAM) { 1101 return Result(&F, /* TrackBBLifetime */ true); 1102 } 1103 }; 1104 1105 AnalysisKey PreservedCFGCheckerAnalysis::Key; 1106 1107 bool PreservedCFGCheckerInstrumentation::CFG::invalidate( 1108 Function &F, const PreservedAnalyses &PA, 1109 FunctionAnalysisManager::Invalidator &) { 1110 auto PAC = PA.getChecker<PreservedCFGCheckerAnalysis>(); 1111 return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>() || 1112 PAC.preservedSet<CFGAnalyses>()); 1113 } 1114 1115 void PreservedCFGCheckerInstrumentation::registerCallbacks( 1116 PassInstrumentationCallbacks &PIC, FunctionAnalysisManager &FAM) { 1117 if (!VerifyPreservedCFG) 1118 return; 1119 1120 FAM.registerPass([&] { return PreservedCFGCheckerAnalysis(); }); 1121 1122 auto checkCFG = [](StringRef Pass, StringRef FuncName, const CFG &GraphBefore, 1123 const CFG &GraphAfter) { 1124 if (GraphAfter == GraphBefore) 1125 return; 1126 1127 dbgs() << "Error: " << Pass 1128 << " does not invalidate CFG analyses but CFG changes detected in " 1129 "function @" 1130 << FuncName << ":\n"; 1131 CFG::printDiff(dbgs(), GraphBefore, GraphAfter); 1132 report_fatal_error(Twine("CFG unexpectedly changed by ", Pass)); 1133 }; 1134 1135 PIC.registerBeforeNonSkippedPassCallback([this, &FAM](StringRef P, Any IR) { 1136 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS 1137 assert(&PassStack.emplace_back(P)); 1138 #endif 1139 (void)this; 1140 if (!any_isa<const Function *>(IR)) 1141 return; 1142 1143 const auto *F = any_cast<const Function *>(IR); 1144 // Make sure a fresh CFG snapshot is available before the pass. 1145 FAM.getResult<PreservedCFGCheckerAnalysis>(*const_cast<Function *>(F)); 1146 }); 1147 1148 PIC.registerAfterPassInvalidatedCallback( 1149 [this](StringRef P, const PreservedAnalyses &PassPA) { 1150 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS 1151 assert(PassStack.pop_back_val() == P && 1152 "Before and After callbacks must correspond"); 1153 #endif 1154 (void)this; 1155 }); 1156 1157 PIC.registerAfterPassCallback([this, &FAM, 1158 checkCFG](StringRef P, Any IR, 1159 const PreservedAnalyses &PassPA) { 1160 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS 1161 assert(PassStack.pop_back_val() == P && 1162 "Before and After callbacks must correspond"); 1163 #endif 1164 (void)this; 1165 1166 if (!any_isa<const Function *>(IR)) 1167 return; 1168 1169 if (!PassPA.allAnalysesInSetPreserved<CFGAnalyses>() && 1170 !PassPA.allAnalysesInSetPreserved<AllAnalysesOn<Function>>()) 1171 return; 1172 1173 const auto *F = any_cast<const Function *>(IR); 1174 if (auto *GraphBefore = FAM.getCachedResult<PreservedCFGCheckerAnalysis>( 1175 *const_cast<Function *>(F))) 1176 checkCFG(P, F->getName(), *GraphBefore, 1177 CFG(F, /* TrackBBLifetime */ false)); 1178 }); 1179 } 1180 1181 void VerifyInstrumentation::registerCallbacks( 1182 PassInstrumentationCallbacks &PIC) { 1183 PIC.registerAfterPassCallback( 1184 [this](StringRef P, Any IR, const PreservedAnalyses &PassPA) { 1185 if (isIgnored(P) || P == "VerifierPass") 1186 return; 1187 if (any_isa<const Function *>(IR) || any_isa<const Loop *>(IR)) { 1188 const Function *F; 1189 if (any_isa<const Loop *>(IR)) 1190 F = any_cast<const Loop *>(IR)->getHeader()->getParent(); 1191 else 1192 F = any_cast<const Function *>(IR); 1193 if (DebugLogging) 1194 dbgs() << "Verifying function " << F->getName() << "\n"; 1195 1196 if (verifyFunction(*F, &errs())) 1197 report_fatal_error("Broken function found, compilation aborted!"); 1198 } else if (any_isa<const Module *>(IR) || 1199 any_isa<const LazyCallGraph::SCC *>(IR)) { 1200 const Module *M; 1201 if (any_isa<const LazyCallGraph::SCC *>(IR)) 1202 M = any_cast<const LazyCallGraph::SCC *>(IR) 1203 ->begin() 1204 ->getFunction() 1205 .getParent(); 1206 else 1207 M = any_cast<const Module *>(IR); 1208 if (DebugLogging) 1209 dbgs() << "Verifying module " << M->getName() << "\n"; 1210 1211 if (verifyModule(*M, &errs())) 1212 report_fatal_error("Broken module found, compilation aborted!"); 1213 } 1214 }); 1215 } 1216 1217 InLineChangePrinter::~InLineChangePrinter() = default; 1218 1219 void InLineChangePrinter::generateIRRepresentation(Any IR, StringRef PassID, 1220 IRDataT<EmptyData> &D) { 1221 IRComparer<EmptyData>::analyzeIR(IR, D); 1222 } 1223 1224 void InLineChangePrinter::handleAfter(StringRef PassID, std::string &Name, 1225 const IRDataT<EmptyData> &Before, 1226 const IRDataT<EmptyData> &After, Any IR) { 1227 SmallString<20> Banner = 1228 formatv("*** IR Dump After {0} on {1} ***\n", PassID, Name); 1229 Out << Banner; 1230 IRComparer<EmptyData>(Before, After) 1231 .compare(getModuleForComparison(IR), 1232 [&](bool InModule, unsigned Minor, 1233 const FuncDataT<EmptyData> &Before, 1234 const FuncDataT<EmptyData> &After) -> void { 1235 handleFunctionCompare(Name, "", PassID, " on ", InModule, 1236 Minor, Before, After); 1237 }); 1238 Out << "\n"; 1239 } 1240 1241 void InLineChangePrinter::handleFunctionCompare( 1242 StringRef Name, StringRef Prefix, StringRef PassID, StringRef Divider, 1243 bool InModule, unsigned Minor, const FuncDataT<EmptyData> &Before, 1244 const FuncDataT<EmptyData> &After) { 1245 // Print a banner when this is being shown in the context of a module 1246 if (InModule) 1247 Out << "\n*** IR for function " << Name << " ***\n"; 1248 1249 FuncDataT<EmptyData>::report( 1250 Before, After, 1251 [&](const BlockDataT<EmptyData> *B, const BlockDataT<EmptyData> *A) { 1252 StringRef BStr = B ? B->getBody() : "\n"; 1253 StringRef AStr = A ? A->getBody() : "\n"; 1254 const std::string Removed = 1255 UseColour ? "\033[31m-%l\033[0m\n" : "-%l\n"; 1256 const std::string Added = UseColour ? "\033[32m+%l\033[0m\n" : "+%l\n"; 1257 const std::string NoChange = " %l\n"; 1258 Out << doSystemDiff(BStr, AStr, Removed, Added, NoChange); 1259 }); 1260 } 1261 1262 void InLineChangePrinter::registerCallbacks(PassInstrumentationCallbacks &PIC) { 1263 if (PrintChanged == ChangePrinter::PrintChangedDiffVerbose || 1264 PrintChanged == ChangePrinter::PrintChangedDiffQuiet || 1265 PrintChanged == ChangePrinter::PrintChangedColourDiffVerbose || 1266 PrintChanged == ChangePrinter::PrintChangedColourDiffQuiet) 1267 TextChangeReporter<IRDataT<EmptyData>>::registerRequiredCallbacks(PIC); 1268 } 1269 1270 namespace { 1271 1272 class DisplayNode; 1273 class DotCfgDiffDisplayGraph; 1274 1275 // Base class for a node or edge in the dot-cfg-changes graph. 1276 class DisplayElement { 1277 public: 1278 // Is this in before, after, or both? 1279 StringRef getColour() const { return Colour; } 1280 1281 protected: 1282 DisplayElement(StringRef Colour) : Colour(Colour) {} 1283 const StringRef Colour; 1284 }; 1285 1286 // An edge representing a transition between basic blocks in the 1287 // dot-cfg-changes graph. 1288 class DisplayEdge : public DisplayElement { 1289 public: 1290 DisplayEdge(std::string Value, DisplayNode &Node, StringRef Colour) 1291 : DisplayElement(Colour), Value(Value), Node(Node) {} 1292 // The value on which the transition is made. 1293 std::string getValue() const { return Value; } 1294 // The node (representing a basic block) reached by this transition. 1295 const DisplayNode &getDestinationNode() const { return Node; } 1296 1297 protected: 1298 std::string Value; 1299 const DisplayNode &Node; 1300 }; 1301 1302 // A node in the dot-cfg-changes graph which represents a basic block. 1303 class DisplayNode : public DisplayElement { 1304 public: 1305 // \p C is the content for the node, \p T indicates the colour for the 1306 // outline of the node 1307 DisplayNode(std::string Content, StringRef Colour) 1308 : DisplayElement(Colour), Content(Content) {} 1309 1310 // Iterator to the child nodes. Required by GraphWriter. 1311 using ChildIterator = std::unordered_set<DisplayNode *>::const_iterator; 1312 ChildIterator children_begin() const { return Children.cbegin(); } 1313 ChildIterator children_end() const { return Children.cend(); } 1314 1315 // Iterator for the edges. Required by GraphWriter. 1316 using EdgeIterator = std::vector<DisplayEdge *>::const_iterator; 1317 EdgeIterator edges_begin() const { return EdgePtrs.cbegin(); } 1318 EdgeIterator edges_end() const { return EdgePtrs.cend(); } 1319 1320 // Create an edge to \p Node on value \p Value, with colour \p Colour. 1321 void createEdge(StringRef Value, DisplayNode &Node, StringRef Colour); 1322 1323 // Return the content of this node. 1324 std::string getContent() const { return Content; } 1325 1326 // Return the edge to node \p S. 1327 const DisplayEdge &getEdge(const DisplayNode &To) const { 1328 assert(EdgeMap.find(&To) != EdgeMap.end() && "Expected to find edge."); 1329 return *EdgeMap.find(&To)->second; 1330 } 1331 1332 // Return the value for the transition to basic block \p S. 1333 // Required by GraphWriter. 1334 std::string getEdgeSourceLabel(const DisplayNode &Sink) const { 1335 return getEdge(Sink).getValue(); 1336 } 1337 1338 void createEdgeMap(); 1339 1340 protected: 1341 const std::string Content; 1342 1343 // Place to collect all of the edges. Once they are all in the vector, 1344 // the vector will not reallocate so then we can use pointers to them, 1345 // which are required by the graph writing routines. 1346 std::vector<DisplayEdge> Edges; 1347 1348 std::vector<DisplayEdge *> EdgePtrs; 1349 std::unordered_set<DisplayNode *> Children; 1350 std::unordered_map<const DisplayNode *, const DisplayEdge *> EdgeMap; 1351 1352 // Safeguard adding of edges. 1353 bool AllEdgesCreated = false; 1354 }; 1355 1356 // Class representing a difference display (corresponds to a pdf file). 1357 class DotCfgDiffDisplayGraph { 1358 public: 1359 DotCfgDiffDisplayGraph(std::string Name) : GraphName(Name) {} 1360 1361 // Generate the file into \p DotFile. 1362 void generateDotFile(StringRef DotFile); 1363 1364 // Iterator to the nodes. Required by GraphWriter. 1365 using NodeIterator = std::vector<DisplayNode *>::const_iterator; 1366 NodeIterator nodes_begin() const { 1367 assert(NodeGenerationComplete && "Unexpected children iterator creation"); 1368 return NodePtrs.cbegin(); 1369 } 1370 NodeIterator nodes_end() const { 1371 assert(NodeGenerationComplete && "Unexpected children iterator creation"); 1372 return NodePtrs.cend(); 1373 } 1374 1375 // Record the index of the entry node. At this point, we can build up 1376 // vectors of pointers that are required by the graph routines. 1377 void setEntryNode(unsigned N) { 1378 // At this point, there will be no new nodes. 1379 assert(!NodeGenerationComplete && "Unexpected node creation"); 1380 NodeGenerationComplete = true; 1381 for (auto &N : Nodes) 1382 NodePtrs.emplace_back(&N); 1383 1384 EntryNode = NodePtrs[N]; 1385 } 1386 1387 // Create a node. 1388 void createNode(std::string C, StringRef Colour) { 1389 assert(!NodeGenerationComplete && "Unexpected node creation"); 1390 Nodes.emplace_back(C, Colour); 1391 } 1392 // Return the node at index \p N to avoid problems with vectors reallocating. 1393 DisplayNode &getNode(unsigned N) { 1394 assert(N < Nodes.size() && "Node is out of bounds"); 1395 return Nodes[N]; 1396 } 1397 unsigned size() const { 1398 assert(NodeGenerationComplete && "Unexpected children iterator creation"); 1399 return Nodes.size(); 1400 } 1401 1402 // Return the name of the graph. Required by GraphWriter. 1403 std::string getGraphName() const { return GraphName; } 1404 1405 // Return the string representing the differences for basic block \p Node. 1406 // Required by GraphWriter. 1407 std::string getNodeLabel(const DisplayNode &Node) const { 1408 return Node.getContent(); 1409 } 1410 1411 // Return a string with colour information for Dot. Required by GraphWriter. 1412 std::string getNodeAttributes(const DisplayNode &Node) const { 1413 return attribute(Node.getColour()); 1414 } 1415 1416 // Return a string with colour information for Dot. Required by GraphWriter. 1417 std::string getEdgeColorAttr(const DisplayNode &From, 1418 const DisplayNode &To) const { 1419 return attribute(From.getEdge(To).getColour()); 1420 } 1421 1422 // Get the starting basic block. Required by GraphWriter. 1423 DisplayNode *getEntryNode() const { 1424 assert(NodeGenerationComplete && "Unexpected children iterator creation"); 1425 return EntryNode; 1426 } 1427 1428 protected: 1429 // Return the string containing the colour to use as a Dot attribute. 1430 std::string attribute(StringRef Colour) const { 1431 return "color=" + Colour.str(); 1432 } 1433 1434 bool NodeGenerationComplete = false; 1435 const std::string GraphName; 1436 std::vector<DisplayNode> Nodes; 1437 std::vector<DisplayNode *> NodePtrs; 1438 DisplayNode *EntryNode = nullptr; 1439 }; 1440 1441 void DisplayNode::createEdge(StringRef Value, DisplayNode &Node, 1442 StringRef Colour) { 1443 assert(!AllEdgesCreated && "Expected to be able to still create edges."); 1444 Edges.emplace_back(Value.str(), Node, Colour); 1445 Children.insert(&Node); 1446 } 1447 1448 void DisplayNode::createEdgeMap() { 1449 // No more edges will be added so we can now use pointers to the edges 1450 // as the vector will not grow and reallocate. 1451 AllEdgesCreated = true; 1452 for (auto &E : Edges) 1453 EdgeMap.insert({&E.getDestinationNode(), &E}); 1454 } 1455 1456 class DotCfgDiffNode; 1457 class DotCfgDiff; 1458 1459 // A class representing a basic block in the Dot difference graph. 1460 class DotCfgDiffNode { 1461 public: 1462 DotCfgDiffNode() = delete; 1463 1464 // Create a node in Dot difference graph \p G representing the basic block 1465 // represented by \p BD with colour \p Colour (where it exists). 1466 DotCfgDiffNode(DotCfgDiff &G, unsigned N, const BlockDataT<DCData> &BD, 1467 StringRef Colour) 1468 : Graph(G), N(N), Data{&BD, nullptr}, Colour(Colour) {} 1469 DotCfgDiffNode(const DotCfgDiffNode &DN) 1470 : Graph(DN.Graph), N(DN.N), Data{DN.Data[0], DN.Data[1]}, 1471 Colour(DN.Colour), EdgesMap(DN.EdgesMap), Children(DN.Children), 1472 Edges(DN.Edges) {} 1473 1474 unsigned getIndex() const { return N; } 1475 1476 // The label of the basic block 1477 StringRef getLabel() const { 1478 assert(Data[0] && "Expected Data[0] to be set."); 1479 return Data[0]->getLabel(); 1480 } 1481 // Return the colour for this block 1482 StringRef getColour() const { return Colour; } 1483 // Change this basic block from being only in before to being common. 1484 // Save the pointer to \p Other. 1485 void setCommon(const BlockDataT<DCData> &Other) { 1486 assert(!Data[1] && "Expected only one block datum"); 1487 Data[1] = &Other; 1488 Colour = CommonColour; 1489 } 1490 // Add an edge to \p E of colour {\p Value, \p Colour}. 1491 void addEdge(unsigned E, StringRef Value, StringRef Colour) { 1492 // This is a new edge or it is an edge being made common. 1493 assert((EdgesMap.count(E) == 0 || Colour == CommonColour) && 1494 "Unexpected edge count and color."); 1495 EdgesMap[E] = {Value.str(), Colour}; 1496 } 1497 // Record the children and create edges. 1498 void finalize(DotCfgDiff &G); 1499 1500 // Return the colour of the edge to node \p S. 1501 StringRef getEdgeColour(const unsigned S) const { 1502 assert(EdgesMap.count(S) == 1 && "Expected to find edge."); 1503 return EdgesMap.at(S).second; 1504 } 1505 1506 // Return the string representing the basic block. 1507 std::string getBodyContent() const; 1508 1509 void createDisplayEdges(DotCfgDiffDisplayGraph &Graph, unsigned DisplayNode, 1510 std::map<const unsigned, unsigned> &NodeMap) const; 1511 1512 protected: 1513 DotCfgDiff &Graph; 1514 const unsigned N; 1515 const BlockDataT<DCData> *Data[2]; 1516 StringRef Colour; 1517 std::map<const unsigned, std::pair<std::string, StringRef>> EdgesMap; 1518 std::vector<unsigned> Children; 1519 std::vector<unsigned> Edges; 1520 }; 1521 1522 // Class representing the difference graph between two functions. 1523 class DotCfgDiff { 1524 public: 1525 // \p Title is the title given to the graph. \p EntryNodeName is the 1526 // entry node for the function. \p Before and \p After are the before 1527 // after versions of the function, respectively. \p Dir is the directory 1528 // in which to store the results. 1529 DotCfgDiff(StringRef Title, const FuncDataT<DCData> &Before, 1530 const FuncDataT<DCData> &After); 1531 1532 DotCfgDiff(const DotCfgDiff &) = delete; 1533 DotCfgDiff &operator=(const DotCfgDiff &) = delete; 1534 1535 DotCfgDiffDisplayGraph createDisplayGraph(StringRef Title, 1536 StringRef EntryNodeName); 1537 1538 // Return a string consisting of the labels for the \p Source and \p Sink. 1539 // The combination allows distinguishing changing transitions on the 1540 // same value (ie, a transition went to X before and goes to Y after). 1541 // Required by GraphWriter. 1542 StringRef getEdgeSourceLabel(const unsigned &Source, 1543 const unsigned &Sink) const { 1544 std::string S = 1545 getNode(Source).getLabel().str() + " " + getNode(Sink).getLabel().str(); 1546 assert(EdgeLabels.count(S) == 1 && "Expected to find edge label."); 1547 return EdgeLabels.find(S)->getValue(); 1548 } 1549 1550 // Return the number of basic blocks (nodes). Required by GraphWriter. 1551 unsigned size() const { return Nodes.size(); } 1552 1553 const DotCfgDiffNode &getNode(unsigned N) const { 1554 assert(N < Nodes.size() && "Unexpected index for node reference"); 1555 return Nodes[N]; 1556 } 1557 1558 protected: 1559 // Return the string surrounded by HTML to make it the appropriate colour. 1560 std::string colourize(std::string S, StringRef Colour) const; 1561 1562 void createNode(StringRef Label, const BlockDataT<DCData> &BD, StringRef C) { 1563 unsigned Pos = Nodes.size(); 1564 Nodes.emplace_back(*this, Pos, BD, C); 1565 NodePosition.insert({Label, Pos}); 1566 } 1567 1568 // TODO Nodes should probably be a StringMap<DotCfgDiffNode> after the 1569 // display graph is separated out, which would remove the need for 1570 // NodePosition. 1571 std::vector<DotCfgDiffNode> Nodes; 1572 StringMap<unsigned> NodePosition; 1573 const std::string GraphName; 1574 1575 StringMap<std::string> EdgeLabels; 1576 }; 1577 1578 std::string DotCfgDiffNode::getBodyContent() const { 1579 if (Colour == CommonColour) { 1580 assert(Data[1] && "Expected Data[1] to be set."); 1581 1582 StringRef SR[2]; 1583 for (unsigned I = 0; I < 2; ++I) { 1584 SR[I] = Data[I]->getBody(); 1585 // drop initial '\n' if present 1586 if (SR[I][0] == '\n') 1587 SR[I] = SR[I].drop_front(); 1588 // drop predecessors as they can be big and are redundant 1589 SR[I] = SR[I].drop_until([](char C) { return C == '\n'; }).drop_front(); 1590 } 1591 1592 SmallString<80> OldLineFormat = formatv( 1593 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", BeforeColour); 1594 SmallString<80> NewLineFormat = formatv( 1595 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", AfterColour); 1596 SmallString<80> UnchangedLineFormat = formatv( 1597 "<FONT COLOR=\"{0}\">%l</FONT><BR align=\"left\"/>", CommonColour); 1598 std::string Diff = Data[0]->getLabel().str(); 1599 Diff += ":\n<BR align=\"left\"/>" + 1600 doSystemDiff(makeHTMLReady(SR[0]), makeHTMLReady(SR[1]), 1601 OldLineFormat, NewLineFormat, UnchangedLineFormat); 1602 1603 // Diff adds in some empty colour changes which are not valid HTML 1604 // so remove them. Colours are all lowercase alpha characters (as 1605 // listed in https://graphviz.org/pdf/dotguide.pdf). 1606 Regex R("<FONT COLOR=\"\\w+\"></FONT>"); 1607 while (true) { 1608 std::string Error; 1609 std::string S = R.sub("", Diff, &Error); 1610 if (Error != "") 1611 return Error; 1612 if (S == Diff) 1613 return Diff; 1614 Diff = S; 1615 } 1616 llvm_unreachable("Should not get here"); 1617 } 1618 1619 // Put node out in the appropriate colour. 1620 assert(!Data[1] && "Data[1] is set unexpectedly."); 1621 std::string Body = makeHTMLReady(Data[0]->getBody()); 1622 const StringRef BS = Body; 1623 StringRef BS1 = BS; 1624 // Drop leading newline, if present. 1625 if (BS.front() == '\n') 1626 BS1 = BS1.drop_front(1); 1627 // Get label. 1628 StringRef Label = BS1.take_until([](char C) { return C == ':'; }); 1629 // drop predecessors as they can be big and are redundant 1630 BS1 = BS1.drop_until([](char C) { return C == '\n'; }).drop_front(); 1631 1632 std::string S = "<FONT COLOR=\"" + Colour.str() + "\">" + Label.str() + ":"; 1633 1634 // align each line to the left. 1635 while (BS1.size()) { 1636 S.append("<BR align=\"left\"/>"); 1637 StringRef Line = BS1.take_until([](char C) { return C == '\n'; }); 1638 S.append(Line.str()); 1639 BS1 = BS1.drop_front(Line.size() + 1); 1640 } 1641 S.append("<BR align=\"left\"/></FONT>"); 1642 return S; 1643 } 1644 1645 std::string DotCfgDiff::colourize(std::string S, StringRef Colour) const { 1646 if (S.length() == 0) 1647 return S; 1648 return "<FONT COLOR=\"" + Colour.str() + "\">" + S + "</FONT>"; 1649 } 1650 1651 DotCfgDiff::DotCfgDiff(StringRef Title, const FuncDataT<DCData> &Before, 1652 const FuncDataT<DCData> &After) 1653 : GraphName(Title.str()) { 1654 StringMap<StringRef> EdgesMap; 1655 1656 // Handle each basic block in the before IR. 1657 for (auto &B : Before.getData()) { 1658 StringRef Label = B.getKey(); 1659 const BlockDataT<DCData> &BD = B.getValue(); 1660 createNode(Label, BD, BeforeColour); 1661 1662 // Create transitions with names made up of the from block label, the value 1663 // on which the transition is made and the to block label. 1664 for (StringMap<std::string>::const_iterator Sink = BD.getData().begin(), 1665 E = BD.getData().end(); 1666 Sink != E; ++Sink) { 1667 std::string Key = (Label + " " + Sink->getKey().str()).str() + " " + 1668 BD.getData().getSuccessorLabel(Sink->getKey()).str(); 1669 EdgesMap.insert({Key, BeforeColour}); 1670 } 1671 } 1672 1673 // Handle each basic block in the after IR 1674 for (auto &A : After.getData()) { 1675 StringRef Label = A.getKey(); 1676 const BlockDataT<DCData> &BD = A.getValue(); 1677 unsigned C = NodePosition.count(Label); 1678 if (C == 0) 1679 // This only exists in the after IR. Create the node. 1680 createNode(Label, BD, AfterColour); 1681 else { 1682 assert(C == 1 && "Unexpected multiple nodes."); 1683 Nodes[NodePosition[Label]].setCommon(BD); 1684 } 1685 // Add in the edges between the nodes (as common or only in after). 1686 for (StringMap<std::string>::const_iterator Sink = BD.getData().begin(), 1687 E = BD.getData().end(); 1688 Sink != E; ++Sink) { 1689 std::string Key = (Label + " " + Sink->getKey().str()).str() + " " + 1690 BD.getData().getSuccessorLabel(Sink->getKey()).str(); 1691 unsigned C = EdgesMap.count(Key); 1692 if (C == 0) 1693 EdgesMap.insert({Key, AfterColour}); 1694 else { 1695 EdgesMap[Key] = CommonColour; 1696 } 1697 } 1698 } 1699 1700 // Now go through the map of edges and add them to the node. 1701 for (auto &E : EdgesMap) { 1702 // Extract the source, sink and value from the edge key. 1703 StringRef S = E.getKey(); 1704 auto SP1 = S.rsplit(' '); 1705 auto &SourceSink = SP1.first; 1706 auto SP2 = SourceSink.split(' '); 1707 StringRef Source = SP2.first; 1708 StringRef Sink = SP2.second; 1709 StringRef Value = SP1.second; 1710 1711 assert(NodePosition.count(Source) == 1 && "Expected to find node."); 1712 DotCfgDiffNode &SourceNode = Nodes[NodePosition[Source]]; 1713 assert(NodePosition.count(Sink) == 1 && "Expected to find node."); 1714 unsigned SinkNode = NodePosition[Sink]; 1715 StringRef Colour = E.second; 1716 1717 // Look for an edge from Source to Sink 1718 if (EdgeLabels.count(SourceSink) == 0) 1719 EdgeLabels.insert({SourceSink, colourize(Value.str(), Colour)}); 1720 else { 1721 StringRef V = EdgeLabels.find(SourceSink)->getValue(); 1722 std::string NV = colourize(V.str() + " " + Value.str(), Colour); 1723 Colour = CommonColour; 1724 EdgeLabels[SourceSink] = NV; 1725 } 1726 SourceNode.addEdge(SinkNode, Value, Colour); 1727 } 1728 for (auto &I : Nodes) 1729 I.finalize(*this); 1730 } 1731 1732 DotCfgDiffDisplayGraph DotCfgDiff::createDisplayGraph(StringRef Title, 1733 StringRef EntryNodeName) { 1734 assert(NodePosition.count(EntryNodeName) == 1 && 1735 "Expected to find entry block in map."); 1736 unsigned Entry = NodePosition[EntryNodeName]; 1737 assert(Entry < Nodes.size() && "Expected to find entry node"); 1738 DotCfgDiffDisplayGraph G(Title.str()); 1739 1740 std::map<const unsigned, unsigned> NodeMap; 1741 1742 int EntryIndex = -1; 1743 unsigned Index = 0; 1744 for (auto &I : Nodes) { 1745 if (I.getIndex() == Entry) 1746 EntryIndex = Index; 1747 G.createNode(I.getBodyContent(), I.getColour()); 1748 NodeMap.insert({I.getIndex(), Index++}); 1749 } 1750 assert(EntryIndex >= 0 && "Expected entry node index to be set."); 1751 G.setEntryNode(EntryIndex); 1752 1753 for (auto &I : NodeMap) { 1754 unsigned SourceNode = I.first; 1755 unsigned DisplayNode = I.second; 1756 getNode(SourceNode).createDisplayEdges(G, DisplayNode, NodeMap); 1757 } 1758 return G; 1759 } 1760 1761 void DotCfgDiffNode::createDisplayEdges( 1762 DotCfgDiffDisplayGraph &DisplayGraph, unsigned DisplayNodeIndex, 1763 std::map<const unsigned, unsigned> &NodeMap) const { 1764 1765 DisplayNode &SourceDisplayNode = DisplayGraph.getNode(DisplayNodeIndex); 1766 1767 for (auto I : Edges) { 1768 unsigned SinkNodeIndex = I; 1769 StringRef Colour = getEdgeColour(SinkNodeIndex); 1770 const DotCfgDiffNode *SinkNode = &Graph.getNode(SinkNodeIndex); 1771 1772 StringRef Label = Graph.getEdgeSourceLabel(getIndex(), SinkNodeIndex); 1773 DisplayNode &SinkDisplayNode = DisplayGraph.getNode(SinkNode->getIndex()); 1774 SourceDisplayNode.createEdge(Label, SinkDisplayNode, Colour); 1775 } 1776 SourceDisplayNode.createEdgeMap(); 1777 } 1778 1779 void DotCfgDiffNode::finalize(DotCfgDiff &G) { 1780 for (auto E : EdgesMap) { 1781 Children.emplace_back(E.first); 1782 Edges.emplace_back(E.first); 1783 } 1784 } 1785 1786 } // namespace 1787 1788 namespace llvm { 1789 1790 template <> struct GraphTraits<DotCfgDiffDisplayGraph *> { 1791 using NodeRef = const DisplayNode *; 1792 using ChildIteratorType = DisplayNode::ChildIterator; 1793 using nodes_iterator = DotCfgDiffDisplayGraph::NodeIterator; 1794 using EdgeRef = const DisplayEdge *; 1795 using ChildEdgeIterator = DisplayNode::EdgeIterator; 1796 1797 static NodeRef getEntryNode(const DotCfgDiffDisplayGraph *G) { 1798 return G->getEntryNode(); 1799 } 1800 static ChildIteratorType child_begin(NodeRef N) { 1801 return N->children_begin(); 1802 } 1803 static ChildIteratorType child_end(NodeRef N) { return N->children_end(); } 1804 static nodes_iterator nodes_begin(const DotCfgDiffDisplayGraph *G) { 1805 return G->nodes_begin(); 1806 } 1807 static nodes_iterator nodes_end(const DotCfgDiffDisplayGraph *G) { 1808 return G->nodes_end(); 1809 } 1810 static ChildEdgeIterator child_edge_begin(NodeRef N) { 1811 return N->edges_begin(); 1812 } 1813 static ChildEdgeIterator child_edge_end(NodeRef N) { return N->edges_end(); } 1814 static NodeRef edge_dest(EdgeRef E) { return &E->getDestinationNode(); } 1815 static unsigned size(const DotCfgDiffDisplayGraph *G) { return G->size(); } 1816 }; 1817 1818 template <> 1819 struct DOTGraphTraits<DotCfgDiffDisplayGraph *> : public DefaultDOTGraphTraits { 1820 explicit DOTGraphTraits(bool Simple = false) 1821 : DefaultDOTGraphTraits(Simple) {} 1822 1823 static bool renderNodesUsingHTML() { return true; } 1824 static std::string getGraphName(const DotCfgDiffDisplayGraph *DiffData) { 1825 return DiffData->getGraphName(); 1826 } 1827 static std::string 1828 getGraphProperties(const DotCfgDiffDisplayGraph *DiffData) { 1829 return "\tsize=\"190, 190\";\n"; 1830 } 1831 static std::string getNodeLabel(const DisplayNode *Node, 1832 const DotCfgDiffDisplayGraph *DiffData) { 1833 return DiffData->getNodeLabel(*Node); 1834 } 1835 static std::string getNodeAttributes(const DisplayNode *Node, 1836 const DotCfgDiffDisplayGraph *DiffData) { 1837 return DiffData->getNodeAttributes(*Node); 1838 } 1839 static std::string getEdgeSourceLabel(const DisplayNode *From, 1840 DisplayNode::ChildIterator &To) { 1841 return From->getEdgeSourceLabel(**To); 1842 } 1843 static std::string getEdgeAttributes(const DisplayNode *From, 1844 DisplayNode::ChildIterator &To, 1845 const DotCfgDiffDisplayGraph *DiffData) { 1846 return DiffData->getEdgeColorAttr(*From, **To); 1847 } 1848 }; 1849 1850 } // namespace llvm 1851 1852 namespace { 1853 1854 void DotCfgDiffDisplayGraph::generateDotFile(StringRef DotFile) { 1855 std::error_code EC; 1856 raw_fd_ostream OutStream(DotFile, EC); 1857 if (EC) { 1858 errs() << "Error: " << EC.message() << "\n"; 1859 return; 1860 } 1861 WriteGraph(OutStream, this, false); 1862 OutStream.flush(); 1863 OutStream.close(); 1864 } 1865 1866 } // namespace 1867 1868 namespace llvm { 1869 1870 DCData::DCData(const BasicBlock &B) { 1871 // Build up transition labels. 1872 const Instruction *Term = B.getTerminator(); 1873 if (const BranchInst *Br = dyn_cast<const BranchInst>(Term)) 1874 if (Br->isUnconditional()) 1875 addSuccessorLabel(Br->getSuccessor(0)->getName().str(), ""); 1876 else { 1877 addSuccessorLabel(Br->getSuccessor(0)->getName().str(), "true"); 1878 addSuccessorLabel(Br->getSuccessor(1)->getName().str(), "false"); 1879 } 1880 else if (const SwitchInst *Sw = dyn_cast<const SwitchInst>(Term)) { 1881 addSuccessorLabel(Sw->case_default()->getCaseSuccessor()->getName().str(), 1882 "default"); 1883 for (auto &C : Sw->cases()) { 1884 assert(C.getCaseValue() && "Expected to find case value."); 1885 SmallString<20> Value = formatv("{0}", C.getCaseValue()->getSExtValue()); 1886 addSuccessorLabel(C.getCaseSuccessor()->getName().str(), Value); 1887 } 1888 } else 1889 for (const_succ_iterator I = succ_begin(&B), E = succ_end(&B); I != E; ++I) 1890 addSuccessorLabel((*I)->getName().str(), ""); 1891 } 1892 1893 DotCfgChangeReporter::DotCfgChangeReporter(bool Verbose) 1894 : ChangeReporter<IRDataT<DCData>>(Verbose) {} 1895 1896 void DotCfgChangeReporter::handleFunctionCompare( 1897 StringRef Name, StringRef Prefix, StringRef PassID, StringRef Divider, 1898 bool InModule, unsigned Minor, const FuncDataT<DCData> &Before, 1899 const FuncDataT<DCData> &After) { 1900 assert(HTML && "Expected outstream to be set"); 1901 SmallString<8> Extender; 1902 SmallString<8> Number; 1903 // Handle numbering and file names. 1904 if (InModule) { 1905 Extender = formatv("{0}_{1}", N, Minor); 1906 Number = formatv("{0}.{1}", N, Minor); 1907 } else { 1908 Extender = formatv("{0}", N); 1909 Number = formatv("{0}", N); 1910 } 1911 // Create a temporary file name for the dot file. 1912 SmallVector<char, 128> SV; 1913 sys::fs::createUniquePath("cfgdot-%%%%%%.dot", SV, true); 1914 std::string DotFile = Twine(SV).str(); 1915 1916 SmallString<20> PDFFileName = formatv("diff_{0}.pdf", Extender); 1917 SmallString<200> Text; 1918 1919 Text = formatv("{0}.{1}{2}{3}{4}", Number, Prefix, makeHTMLReady(PassID), 1920 Divider, Name); 1921 1922 DotCfgDiff Diff(Text, Before, After); 1923 std::string EntryBlockName = After.getEntryBlockName(); 1924 // Use the before entry block if the after entry block was removed. 1925 if (EntryBlockName == "") 1926 EntryBlockName = Before.getEntryBlockName(); 1927 assert(EntryBlockName != "" && "Expected to find entry block"); 1928 1929 DotCfgDiffDisplayGraph DG = Diff.createDisplayGraph(Text, EntryBlockName); 1930 DG.generateDotFile(DotFile); 1931 1932 *HTML << genHTML(Text, DotFile, PDFFileName); 1933 std::error_code EC = sys::fs::remove(DotFile); 1934 if (EC) 1935 errs() << "Error: " << EC.message() << "\n"; 1936 } 1937 1938 std::string DotCfgChangeReporter::genHTML(StringRef Text, StringRef DotFile, 1939 StringRef PDFFileName) { 1940 SmallString<20> PDFFile = formatv("{0}/{1}", DotCfgDir, PDFFileName); 1941 // Create the PDF file. 1942 static ErrorOr<std::string> DotExe = sys::findProgramByName(DotBinary); 1943 if (!DotExe) 1944 return "Unable to find dot executable."; 1945 1946 StringRef Args[] = {DotBinary, "-Tpdf", "-o", PDFFile, DotFile}; 1947 int Result = sys::ExecuteAndWait(*DotExe, Args, None); 1948 if (Result < 0) 1949 return "Error executing system dot."; 1950 1951 // Create the HTML tag refering to the PDF file. 1952 SmallString<200> S = formatv( 1953 " <a href=\"{0}\" target=\"_blank\">{1}</a><br/>\n", PDFFileName, Text); 1954 return S.c_str(); 1955 } 1956 1957 void DotCfgChangeReporter::handleInitialIR(Any IR) { 1958 assert(HTML && "Expected outstream to be set"); 1959 *HTML << "<button type=\"button\" class=\"collapsible\">0. " 1960 << "Initial IR (by function)</button>\n" 1961 << "<div class=\"content\">\n" 1962 << " <p>\n"; 1963 // Create representation of IR 1964 IRDataT<DCData> Data; 1965 IRComparer<DCData>::analyzeIR(IR, Data); 1966 // Now compare it against itself, which will have everything the 1967 // same and will generate the files. 1968 IRComparer<DCData>(Data, Data) 1969 .compare(getModuleForComparison(IR), 1970 [&](bool InModule, unsigned Minor, 1971 const FuncDataT<DCData> &Before, 1972 const FuncDataT<DCData> &After) -> void { 1973 handleFunctionCompare("", " ", "Initial IR", "", InModule, 1974 Minor, Before, After); 1975 }); 1976 *HTML << " </p>\n" 1977 << "</div><br/>\n"; 1978 ++N; 1979 } 1980 1981 void DotCfgChangeReporter::generateIRRepresentation(Any IR, StringRef PassID, 1982 IRDataT<DCData> &Data) { 1983 IRComparer<DCData>::analyzeIR(IR, Data); 1984 } 1985 1986 void DotCfgChangeReporter::omitAfter(StringRef PassID, std::string &Name) { 1987 assert(HTML && "Expected outstream to be set"); 1988 SmallString<20> Banner = 1989 formatv(" <a>{0}. Pass {1} on {2} omitted because no change</a><br/>\n", 1990 N, makeHTMLReady(PassID), Name); 1991 *HTML << Banner; 1992 ++N; 1993 } 1994 1995 void DotCfgChangeReporter::handleAfter(StringRef PassID, std::string &Name, 1996 const IRDataT<DCData> &Before, 1997 const IRDataT<DCData> &After, Any IR) { 1998 assert(HTML && "Expected outstream to be set"); 1999 IRComparer<DCData>(Before, After) 2000 .compare(getModuleForComparison(IR), 2001 [&](bool InModule, unsigned Minor, 2002 const FuncDataT<DCData> &Before, 2003 const FuncDataT<DCData> &After) -> void { 2004 handleFunctionCompare(Name, " Pass ", PassID, " on ", InModule, 2005 Minor, Before, After); 2006 }); 2007 *HTML << " </p></div>\n"; 2008 ++N; 2009 } 2010 2011 void DotCfgChangeReporter::handleInvalidated(StringRef PassID) { 2012 assert(HTML && "Expected outstream to be set"); 2013 SmallString<20> Banner = 2014 formatv(" <a>{0}. {1} invalidated</a><br/>\n", N, makeHTMLReady(PassID)); 2015 *HTML << Banner; 2016 ++N; 2017 } 2018 2019 void DotCfgChangeReporter::handleFiltered(StringRef PassID, std::string &Name) { 2020 assert(HTML && "Expected outstream to be set"); 2021 SmallString<20> Banner = 2022 formatv(" <a>{0}. Pass {1} on {2} filtered out</a><br/>\n", N, 2023 makeHTMLReady(PassID), Name); 2024 *HTML << Banner; 2025 ++N; 2026 } 2027 2028 void DotCfgChangeReporter::handleIgnored(StringRef PassID, std::string &Name) { 2029 assert(HTML && "Expected outstream to be set"); 2030 SmallString<20> Banner = formatv(" <a>{0}. {1} on {2} ignored</a><br/>\n", N, 2031 makeHTMLReady(PassID), Name); 2032 *HTML << Banner; 2033 ++N; 2034 } 2035 2036 bool DotCfgChangeReporter::initializeHTML() { 2037 std::error_code EC; 2038 HTML = std::make_unique<raw_fd_ostream>(DotCfgDir + "/passes.html", EC); 2039 if (EC) { 2040 HTML = nullptr; 2041 return false; 2042 } 2043 2044 *HTML << "<!doctype html>" 2045 << "<html>" 2046 << "<head>" 2047 << "<style>.collapsible { " 2048 << "background-color: #777;" 2049 << " color: white;" 2050 << " cursor: pointer;" 2051 << " padding: 18px;" 2052 << " width: 100%;" 2053 << " border: none;" 2054 << " text-align: left;" 2055 << " outline: none;" 2056 << " font-size: 15px;" 2057 << "} .active, .collapsible:hover {" 2058 << " background-color: #555;" 2059 << "} .content {" 2060 << " padding: 0 18px;" 2061 << " display: none;" 2062 << " overflow: hidden;" 2063 << " background-color: #f1f1f1;" 2064 << "}" 2065 << "</style>" 2066 << "<title>passes.html</title>" 2067 << "</head>\n" 2068 << "<body>"; 2069 return true; 2070 } 2071 2072 DotCfgChangeReporter::~DotCfgChangeReporter() { 2073 if (!HTML) 2074 return; 2075 *HTML 2076 << "<script>var coll = document.getElementsByClassName(\"collapsible\");" 2077 << "var i;" 2078 << "for (i = 0; i < coll.length; i++) {" 2079 << "coll[i].addEventListener(\"click\", function() {" 2080 << " this.classList.toggle(\"active\");" 2081 << " var content = this.nextElementSibling;" 2082 << " if (content.style.display === \"block\"){" 2083 << " content.style.display = \"none\";" 2084 << " }" 2085 << " else {" 2086 << " content.style.display= \"block\";" 2087 << " }" 2088 << " });" 2089 << " }" 2090 << "</script>" 2091 << "</body>" 2092 << "</html>\n"; 2093 HTML->flush(); 2094 HTML->close(); 2095 } 2096 2097 void DotCfgChangeReporter::registerCallbacks( 2098 PassInstrumentationCallbacks &PIC) { 2099 if ((PrintChanged == ChangePrinter::PrintChangedDotCfgVerbose || 2100 PrintChanged == ChangePrinter::PrintChangedDotCfgQuiet)) { 2101 SmallString<128> OutputDir; 2102 sys::fs::expand_tilde(DotCfgDir, OutputDir); 2103 sys::fs::make_absolute(OutputDir); 2104 assert(!OutputDir.empty() && "expected output dir to be non-empty"); 2105 DotCfgDir = OutputDir.c_str(); 2106 if (initializeHTML()) { 2107 ChangeReporter<IRDataT<DCData>>::registerRequiredCallbacks(PIC); 2108 return; 2109 } 2110 dbgs() << "Unable to open output stream for -cfg-dot-changed\n"; 2111 } 2112 } 2113 2114 StandardInstrumentations::StandardInstrumentations( 2115 bool DebugLogging, bool VerifyEach, PrintPassOptions PrintPassOpts) 2116 : PrintPass(DebugLogging, PrintPassOpts), OptNone(DebugLogging), 2117 PrintChangedIR(PrintChanged == ChangePrinter::PrintChangedVerbose), 2118 PrintChangedDiff( 2119 PrintChanged == ChangePrinter::PrintChangedDiffVerbose || 2120 PrintChanged == ChangePrinter::PrintChangedColourDiffVerbose, 2121 PrintChanged == ChangePrinter::PrintChangedColourDiffVerbose || 2122 PrintChanged == ChangePrinter::PrintChangedColourDiffQuiet), 2123 WebsiteChangeReporter(PrintChanged == 2124 ChangePrinter::PrintChangedDotCfgVerbose), 2125 Verify(DebugLogging), VerifyEach(VerifyEach) {} 2126 2127 PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter = 2128 nullptr; 2129 2130 void PrintCrashIRInstrumentation::reportCrashIR() { dbgs() << SavedIR; } 2131 2132 void PrintCrashIRInstrumentation::SignalHandler(void *) { 2133 // Called by signal handlers so do not lock here 2134 // Is the PrintCrashIRInstrumentation still alive? 2135 if (!CrashReporter) 2136 return; 2137 2138 assert(PrintCrashIR && "Did not expect to get here without option set."); 2139 CrashReporter->reportCrashIR(); 2140 } 2141 2142 PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() { 2143 if (!CrashReporter) 2144 return; 2145 2146 assert(PrintCrashIR && "Did not expect to get here without option set."); 2147 CrashReporter = nullptr; 2148 } 2149 2150 void PrintCrashIRInstrumentation::registerCallbacks( 2151 PassInstrumentationCallbacks &PIC) { 2152 if (!PrintCrashIR || CrashReporter) 2153 return; 2154 2155 sys::AddSignalHandler(SignalHandler, nullptr); 2156 CrashReporter = this; 2157 2158 PIC.registerBeforeNonSkippedPassCallback([this](StringRef PassID, Any IR) { 2159 SavedIR.clear(); 2160 raw_string_ostream OS(SavedIR); 2161 OS << formatv("*** Dump of {0}IR Before Last Pass {1}", 2162 llvm::forcePrintModuleIR() ? "Module " : "", PassID); 2163 if (!isInteresting(IR, PassID)) { 2164 OS << " Filtered Out ***\n"; 2165 return; 2166 } 2167 OS << " Started ***\n"; 2168 unwrapAndPrint(OS, IR); 2169 }); 2170 } 2171 2172 void StandardInstrumentations::registerCallbacks( 2173 PassInstrumentationCallbacks &PIC, FunctionAnalysisManager *FAM) { 2174 PrintIR.registerCallbacks(PIC); 2175 PrintPass.registerCallbacks(PIC); 2176 TimePasses.registerCallbacks(PIC); 2177 OptNone.registerCallbacks(PIC); 2178 OptBisect.registerCallbacks(PIC); 2179 if (FAM) 2180 PreservedCFGChecker.registerCallbacks(PIC, *FAM); 2181 PrintChangedIR.registerCallbacks(PIC); 2182 PseudoProbeVerification.registerCallbacks(PIC); 2183 if (VerifyEach) 2184 Verify.registerCallbacks(PIC); 2185 PrintChangedDiff.registerCallbacks(PIC); 2186 WebsiteChangeReporter.registerCallbacks(PIC); 2187 PrintCrashIR.registerCallbacks(PIC); 2188 } 2189 2190 template class ChangeReporter<std::string>; 2191 template class TextChangeReporter<std::string>; 2192 2193 template class BlockDataT<EmptyData>; 2194 template class FuncDataT<EmptyData>; 2195 template class IRDataT<EmptyData>; 2196 template class ChangeReporter<IRDataT<EmptyData>>; 2197 template class TextChangeReporter<IRDataT<EmptyData>>; 2198 template class IRComparer<EmptyData>; 2199 2200 } // namespace llvm 2201