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