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