| //===- Standard pass instrumentations handling ----------------*- C++ -*--===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// \file | |
| /// | |
| /// This file defines IR-printing pass instrumentation callbacks as well as | |
| /// StandardInstrumentations class that manages standard pass instrumentations. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Passes/StandardInstrumentations.h" | |
| #include "llvm/ADT/Optional.h" | |
| #include "llvm/Analysis/CallGraphSCCPass.h" | |
| #include "llvm/Analysis/LazyCallGraph.h" | |
| #include "llvm/Analysis/LoopInfo.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/IRPrintingPasses.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/IR/PassInstrumentation.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| namespace { | |
| /// Extracting Module out of \p IR unit. Also fills a textual description | |
| /// of \p IR for use in header when printing. | |
| Optional<std::pair<const Module *, std::string>> unwrapModule(Any IR) { | |
| if (any_isa<const Module *>(IR)) | |
| return std::make_pair(any_cast<const Module *>(IR), std::string()); | |
| if (any_isa<const Function *>(IR)) { | |
| const Function *F = any_cast<const Function *>(IR); | |
| if (!llvm::isFunctionInPrintList(F->getName())) | |
| return None; | |
| const Module *M = F->getParent(); | |
| return std::make_pair(M, formatv(" (function: {0})", F->getName()).str()); | |
| } | |
| if (any_isa<const LazyCallGraph::SCC *>(IR)) { | |
| const LazyCallGraph::SCC *C = any_cast<const LazyCallGraph::SCC *>(IR); | |
| for (const LazyCallGraph::Node &N : *C) { | |
| const Function &F = N.getFunction(); | |
| if (!F.isDeclaration() && isFunctionInPrintList(F.getName())) { | |
| const Module *M = F.getParent(); | |
| return std::make_pair(M, formatv(" (scc: {0})", C->getName()).str()); | |
| } | |
| } | |
| return None; | |
| } | |
| if (any_isa<const Loop *>(IR)) { | |
| const Loop *L = any_cast<const Loop *>(IR); | |
| const Function *F = L->getHeader()->getParent(); | |
| if (!isFunctionInPrintList(F->getName())) | |
| return None; | |
| const Module *M = F->getParent(); | |
| std::string LoopName; | |
| raw_string_ostream ss(LoopName); | |
| L->getHeader()->printAsOperand(ss, false); | |
| return std::make_pair(M, formatv(" (loop: {0})", ss.str()).str()); | |
| } | |
| llvm_unreachable("Unknown IR unit"); | |
| } | |
| void printIR(const Module *M, StringRef Banner, StringRef Extra = StringRef()) { | |
| dbgs() << Banner << Extra << "\n"; | |
| M->print(dbgs(), nullptr, false); | |
| } | |
| void printIR(const Function *F, StringRef Banner, | |
| StringRef Extra = StringRef()) { | |
| if (!llvm::isFunctionInPrintList(F->getName())) | |
| return; | |
| dbgs() << Banner << Extra << "\n" << static_cast<const Value &>(*F); | |
| } | |
| void printIR(const LazyCallGraph::SCC *C, StringRef Banner, | |
| StringRef Extra = StringRef()) { | |
| bool BannerPrinted = false; | |
| for (const LazyCallGraph::Node &N : *C) { | |
| const Function &F = N.getFunction(); | |
| if (!F.isDeclaration() && llvm::isFunctionInPrintList(F.getName())) { | |
| if (!BannerPrinted) { | |
| dbgs() << Banner << Extra << "\n"; | |
| BannerPrinted = true; | |
| } | |
| F.print(dbgs()); | |
| } | |
| } | |
| } | |
| void printIR(const Loop *L, StringRef Banner) { | |
| const Function *F = L->getHeader()->getParent(); | |
| if (!llvm::isFunctionInPrintList(F->getName())) | |
| return; | |
| llvm::printLoop(const_cast<Loop &>(*L), dbgs(), Banner); | |
| } | |
| /// Generic IR-printing helper that unpacks a pointer to IRUnit wrapped into | |
| /// llvm::Any and does actual print job. | |
| void unwrapAndPrint(Any IR, StringRef Banner, bool ForceModule = false) { | |
| if (ForceModule) { | |
| if (auto UnwrappedModule = unwrapModule(IR)) | |
| printIR(UnwrappedModule->first, Banner, UnwrappedModule->second); | |
| return; | |
| } | |
| if (any_isa<const Module *>(IR)) { | |
| const Module *M = any_cast<const Module *>(IR); | |
| assert(M && "module should be valid for printing"); | |
| printIR(M, Banner); | |
| return; | |
| } | |
| if (any_isa<const Function *>(IR)) { | |
| const Function *F = any_cast<const Function *>(IR); | |
| assert(F && "function should be valid for printing"); | |
| printIR(F, Banner); | |
| return; | |
| } | |
| if (any_isa<const LazyCallGraph::SCC *>(IR)) { | |
| const LazyCallGraph::SCC *C = any_cast<const LazyCallGraph::SCC *>(IR); | |
| assert(C && "scc should be valid for printing"); | |
| std::string Extra = formatv(" (scc: {0})", C->getName()); | |
| printIR(C, Banner, Extra); | |
| return; | |
| } | |
| if (any_isa<const Loop *>(IR)) { | |
| const Loop *L = any_cast<const Loop *>(IR); | |
| assert(L && "Loop should be valid for printing"); | |
| printIR(L, Banner); | |
| return; | |
| } | |
| llvm_unreachable("Unknown wrapped IR type"); | |
| } | |
| } // namespace | |
| PrintIRInstrumentation::~PrintIRInstrumentation() { | |
| assert(ModuleDescStack.empty() && "ModuleDescStack is not empty at exit"); | |
| } | |
| void PrintIRInstrumentation::pushModuleDesc(StringRef PassID, Any IR) { | |
| assert(StoreModuleDesc); | |
| const Module *M = nullptr; | |
| std::string Extra; | |
| if (auto UnwrappedModule = unwrapModule(IR)) | |
| std::tie(M, Extra) = UnwrappedModule.getValue(); | |
| ModuleDescStack.emplace_back(M, Extra, PassID); | |
| } | |
| PrintIRInstrumentation::PrintModuleDesc | |
| PrintIRInstrumentation::popModuleDesc(StringRef PassID) { | |
| assert(!ModuleDescStack.empty() && "empty ModuleDescStack"); | |
| PrintModuleDesc ModuleDesc = ModuleDescStack.pop_back_val(); | |
| assert(std::get<2>(ModuleDesc).equals(PassID) && "malformed ModuleDescStack"); | |
| return ModuleDesc; | |
| } | |
| bool PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) { | |
| if (PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<")) | |
| return true; | |
| // Saving Module for AfterPassInvalidated operations. | |
| // Note: here we rely on a fact that we do not change modules while | |
| // traversing the pipeline, so the latest captured module is good | |
| // for all print operations that has not happen yet. | |
| if (StoreModuleDesc && llvm::shouldPrintAfterPass(PassID)) | |
| pushModuleDesc(PassID, IR); | |
| if (!llvm::shouldPrintBeforePass(PassID)) | |
| return true; | |
| SmallString<20> Banner = formatv("*** IR Dump Before {0} ***", PassID); | |
| unwrapAndPrint(IR, Banner, llvm::forcePrintModuleIR()); | |
| return true; | |
| } | |
| void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) { | |
| if (PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<")) | |
| return; | |
| if (!llvm::shouldPrintAfterPass(PassID)) | |
| return; | |
| if (StoreModuleDesc) | |
| popModuleDesc(PassID); | |
| SmallString<20> Banner = formatv("*** IR Dump After {0} ***", PassID); | |
| unwrapAndPrint(IR, Banner, llvm::forcePrintModuleIR()); | |
| } | |
| void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) { | |
| if (!StoreModuleDesc || !llvm::shouldPrintAfterPass(PassID)) | |
| return; | |
| if (PassID.startswith("PassManager<") || PassID.contains("PassAdaptor<")) | |
| return; | |
| const Module *M; | |
| std::string Extra; | |
| StringRef StoredPassID; | |
| std::tie(M, Extra, StoredPassID) = popModuleDesc(PassID); | |
| // Additional filtering (e.g. -filter-print-func) can lead to module | |
| // printing being skipped. | |
| if (!M) | |
| return; | |
| SmallString<20> Banner = | |
| formatv("*** IR Dump After {0} *** invalidated: ", PassID); | |
| printIR(M, Banner, Extra); | |
| } | |
| void PrintIRInstrumentation::registerCallbacks( | |
| PassInstrumentationCallbacks &PIC) { | |
| // BeforePass callback is not just for printing, it also saves a Module | |
| // for later use in AfterPassInvalidated. | |
| StoreModuleDesc = llvm::forcePrintModuleIR() && llvm::shouldPrintAfterPass(); | |
| if (llvm::shouldPrintBeforePass() || StoreModuleDesc) | |
| PIC.registerBeforePassCallback( | |
| [this](StringRef P, Any IR) { return this->printBeforePass(P, IR); }); | |
| if (llvm::shouldPrintAfterPass()) { | |
| PIC.registerAfterPassCallback( | |
| [this](StringRef P, Any IR) { this->printAfterPass(P, IR); }); | |
| PIC.registerAfterPassInvalidatedCallback( | |
| [this](StringRef P) { this->printAfterPassInvalidated(P); }); | |
| } | |
| } | |
| void StandardInstrumentations::registerCallbacks( | |
| PassInstrumentationCallbacks &PIC) { | |
| PrintIR.registerCallbacks(PIC); | |
| TimePasses.registerCallbacks(PIC); | |
| } |