//===- StandardInstrumentations.h ------------------------------*- 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 header defines a class that provides bookkeeping for all standard /// (i.e in-tree) pass instrumentations. /// //===----------------------------------------------------------------------===// #ifndef LLVM_PASSES_STANDARDINSTRUMENTATIONS_H #define LLVM_PASSES_STANDARDINSTRUMENTATIONS_H #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/OptBisect.h" #include "llvm/IR/PassTimingInfo.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Transforms/IPO/SampleProfileProbe.h" #include #include namespace llvm { class Module; class Function; class PassInstrumentationCallbacks; /// Instrumentation to print IR before/after passes. /// /// Needs state to be able to print module after pass that invalidates IR unit /// (typically Loop or SCC). class PrintIRInstrumentation { public: ~PrintIRInstrumentation(); void registerCallbacks(PassInstrumentationCallbacks &PIC); private: struct PassRunDescriptor { const Module *M; const std::string DumpIRFilename; const std::string IRName; const StringRef PassID; PassRunDescriptor(const Module *M, std::string DumpIRFilename, std::string IRName, const StringRef PassID) : M{M}, DumpIRFilename{DumpIRFilename}, IRName{IRName}, PassID(PassID) { } }; void printBeforePass(StringRef PassID, Any IR); void printAfterPass(StringRef PassID, Any IR); void printAfterPassInvalidated(StringRef PassID); bool shouldPrintBeforePass(StringRef PassID); bool shouldPrintAfterPass(StringRef PassID); bool shouldPrintPassNumbers(); bool shouldPrintBeforePassNumber(); void pushPassRunDescriptor(StringRef PassID, Any IR, std::string &DumpIRFilename); PassRunDescriptor popPassRunDescriptor(StringRef PassID); std::string fetchDumpFilename(StringRef PassId, Any IR); PassInstrumentationCallbacks *PIC; /// Stack of Pass Run descriptions, enough to print the IR unit after a given /// pass. SmallVector PassRunDescriptorStack; /// Used for print-at-pass-number unsigned CurrentPassNumber = 0; }; class OptNoneInstrumentation { public: OptNoneInstrumentation(bool DebugLogging) : DebugLogging(DebugLogging) {} void registerCallbacks(PassInstrumentationCallbacks &PIC); private: bool DebugLogging; bool shouldRun(StringRef PassID, Any IR); }; class OptPassGateInstrumentation { LLVMContext &Context; bool HasWrittenIR = false; public: OptPassGateInstrumentation(LLVMContext &Context) : Context(Context) {} bool shouldRun(StringRef PassName, Any IR); void registerCallbacks(PassInstrumentationCallbacks &PIC); }; struct PrintPassOptions { /// Print adaptors and pass managers. bool Verbose = false; /// Don't print information for analyses. bool SkipAnalyses = false; /// Indent based on hierarchy. bool Indent = false; }; // Debug logging for transformation and analysis passes. class PrintPassInstrumentation { raw_ostream &print(); public: PrintPassInstrumentation(bool Enabled, PrintPassOptions Opts) : Enabled(Enabled), Opts(Opts) {} void registerCallbacks(PassInstrumentationCallbacks &PIC); private: bool Enabled; PrintPassOptions Opts; int Indent = 0; }; class PreservedCFGCheckerInstrumentation { public: // Keeps sticky poisoned flag for the given basic block once it has been // deleted or RAUWed. struct BBGuard final : public CallbackVH { BBGuard(const BasicBlock *BB) : CallbackVH(BB) {} void deleted() override { CallbackVH::deleted(); } void allUsesReplacedWith(Value *) override { CallbackVH::deleted(); } bool isPoisoned() const { return !getValPtr(); } }; // CFG is a map BB -> {(Succ, Multiplicity)}, where BB is a non-leaf basic // block, {(Succ, Multiplicity)} set of all pairs of the block's successors // and the multiplicity of the edge (BB->Succ). As the mapped sets are // unordered the order of successors is not tracked by the CFG. In other words // this allows basic block successors to be swapped by a pass without // reporting a CFG change. CFG can be guarded by basic block tracking pointers // in the Graph (BBGuard). That is if any of the block is deleted or RAUWed // then the CFG is treated poisoned and no block pointer of the Graph is used. struct CFG { std::optional> BBGuards; DenseMap> Graph; CFG(const Function *F, bool TrackBBLifetime); bool operator==(const CFG &G) const { return !isPoisoned() && !G.isPoisoned() && Graph == G.Graph; } bool isPoisoned() const { return BBGuards && llvm::any_of(*BBGuards, [](const auto &BB) { return BB.second.isPoisoned(); }); } static void printDiff(raw_ostream &out, const CFG &Before, const CFG &After); bool invalidate(Function &F, const PreservedAnalyses &PA, FunctionAnalysisManager::Invalidator &); }; #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS SmallVector PassStack; #endif void registerCallbacks(PassInstrumentationCallbacks &PIC, ModuleAnalysisManager &MAM); }; // Base class for classes that report changes to the IR. // It presents an interface for such classes and provides calls // on various events as the new pass manager transforms the IR. // It also provides filtering of information based on hidden options // specifying which functions are interesting. // Calls are made for the following events/queries: // 1. The initial IR processed. // 2. To get the representation of the IR (of type \p T). // 3. When a pass does not change the IR. // 4. When a pass changes the IR (given both before and after representations // of type \p T). // 5. When an IR is invalidated. // 6. When a pass is run on an IR that is not interesting (based on options). // 7. When a pass is ignored (pass manager or adapter pass). // 8. To compare two IR representations (of type \p T). template class ChangeReporter { protected: ChangeReporter(bool RunInVerboseMode) : VerboseMode(RunInVerboseMode) {} public: virtual ~ChangeReporter(); // Determine if this pass/IR is interesting and if so, save the IR // otherwise it is left on the stack without data. void saveIRBeforePass(Any IR, StringRef PassID, StringRef PassName); // Compare the IR from before the pass after the pass. void handleIRAfterPass(Any IR, StringRef PassID, StringRef PassName); // Handle the situation where a pass is invalidated. void handleInvalidatedPass(StringRef PassID); protected: // Register required callbacks. void registerRequiredCallbacks(PassInstrumentationCallbacks &PIC); // Called on the first IR processed. virtual void handleInitialIR(Any IR) = 0; // Called before and after a pass to get the representation of the IR. virtual void generateIRRepresentation(Any IR, StringRef PassID, IRUnitT &Output) = 0; // Called when the pass is not iteresting. virtual void omitAfter(StringRef PassID, std::string &Name) = 0; // Called when an interesting IR has changed. virtual void handleAfter(StringRef PassID, std::string &Name, const IRUnitT &Before, const IRUnitT &After, Any) = 0; // Called when an interesting pass is invalidated. virtual void handleInvalidated(StringRef PassID) = 0; // Called when the IR or pass is not interesting. virtual void handleFiltered(StringRef PassID, std::string &Name) = 0; // Called when an ignored pass is encountered. virtual void handleIgnored(StringRef PassID, std::string &Name) = 0; // Stack of IRs before passes. std::vector BeforeStack; // Is this the first IR seen? bool InitialIR = true; // Run in verbose mode, printing everything? const bool VerboseMode; }; // An abstract template base class that handles printing banners and // reporting when things have not changed or are filtered out. template class TextChangeReporter : public ChangeReporter { protected: TextChangeReporter(bool Verbose); // Print a module dump of the first IR that is changed. void handleInitialIR(Any IR) override; // Report that the IR was omitted because it did not change. void omitAfter(StringRef PassID, std::string &Name) override; // Report that the pass was invalidated. void handleInvalidated(StringRef PassID) override; // Report that the IR was filtered out. void handleFiltered(StringRef PassID, std::string &Name) override; // Report that the pass was ignored. void handleIgnored(StringRef PassID, std::string &Name) override; // Make substitutions in \p S suitable for reporting changes // after the pass and then print it. raw_ostream &Out; }; // A change printer based on the string representation of the IR as created // by unwrapAndPrint. The string representation is stored in a std::string // to preserve it as the IR changes in each pass. Note that the banner is // included in this representation but it is massaged before reporting. class IRChangedPrinter : public TextChangeReporter { public: IRChangedPrinter(bool VerboseMode) : TextChangeReporter(VerboseMode) {} ~IRChangedPrinter() override; void registerCallbacks(PassInstrumentationCallbacks &PIC); protected: // Called before and after a pass to get the representation of the IR. void generateIRRepresentation(Any IR, StringRef PassID, std::string &Output) override; // Called when an interesting IR has changed. void handleAfter(StringRef PassID, std::string &Name, const std::string &Before, const std::string &After, Any) override; }; class IRChangedTester : public IRChangedPrinter { public: IRChangedTester() : IRChangedPrinter(true) {} ~IRChangedTester() override; void registerCallbacks(PassInstrumentationCallbacks &PIC); protected: void handleIR(const std::string &IR, StringRef PassID); // Check initial IR void handleInitialIR(Any IR) override; // Do nothing. void omitAfter(StringRef PassID, std::string &Name) override; // Do nothing. void handleInvalidated(StringRef PassID) override; // Do nothing. void handleFiltered(StringRef PassID, std::string &Name) override; // Do nothing. void handleIgnored(StringRef PassID, std::string &Name) override; // Call test as interesting IR has changed. void handleAfter(StringRef PassID, std::string &Name, const std::string &Before, const std::string &After, Any) override; }; // Information that needs to be saved for a basic block in order to compare // before and after the pass to determine if it was changed by a pass. template class BlockDataT { public: BlockDataT(const BasicBlock &B) : Label(B.getName().str()), Data(B) { raw_string_ostream SS(Body); B.print(SS, nullptr, true, true); } bool operator==(const BlockDataT &That) const { return Body == That.Body; } bool operator!=(const BlockDataT &That) const { return Body != That.Body; } // Return the label of the represented basic block. StringRef getLabel() const { return Label; } // Return the string representation of the basic block. StringRef getBody() const { return Body; } // Return the associated data const T &getData() const { return Data; } protected: std::string Label; std::string Body; // Extra data associated with a basic block T Data; }; template class OrderedChangedData { public: // Return the names in the order they were saved std::vector &getOrder() { return Order; } const std::vector &getOrder() const { return Order; } // Return a map of names to saved representations StringMap &getData() { return Data; } const StringMap &getData() const { return Data; } bool operator==(const OrderedChangedData &That) const { return Data == That.getData(); } // Call the lambda \p HandlePair on each corresponding pair of data from // \p Before and \p After. The order is based on the order in \p After // with ones that are only in \p Before interspersed based on where they // occur in \p Before. This is used to present the output in an order // based on how the data is ordered in LLVM. static void report(const OrderedChangedData &Before, const OrderedChangedData &After, function_ref HandlePair); protected: std::vector Order; StringMap Data; }; // Do not need extra information for patch-style change reporter. class EmptyData { public: EmptyData(const BasicBlock &) {} }; // The data saved for comparing functions. template class FuncDataT : public OrderedChangedData> { public: FuncDataT(std::string S) : EntryBlockName(S) {} // Return the name of the entry block std::string getEntryBlockName() const { return EntryBlockName; } protected: std::string EntryBlockName; }; // The data saved for comparing IRs. template class IRDataT : public OrderedChangedData> {}; // Abstract template base class for a class that compares two IRs. The // class is created with the 2 IRs to compare and then compare is called. // The static function analyzeIR is used to build up the IR representation. template class IRComparer { public: IRComparer(const IRDataT &Before, const IRDataT &After) : Before(Before), After(After) {} // Compare the 2 IRs. \p handleFunctionCompare is called to handle the // compare of a function. When \p InModule is set, // this function is being handled as part of comparing a module. void compare( bool CompareModule, std::function &Before, const FuncDataT &After)> CompareFunc); // Analyze \p IR and build the IR representation in \p Data. static void analyzeIR(Any IR, IRDataT &Data); protected: // Generate the data for \p F into \p Data. static bool generateFunctionData(IRDataT &Data, const Function &F); const IRDataT &Before; const IRDataT &After; }; // A change printer that prints out in-line differences in the basic // blocks. It uses an InlineComparer to do the comparison so it shows // the differences prefixed with '-' and '+' for code that is removed // and added, respectively. Changes to the IR that do not affect basic // blocks are not reported as having changed the IR. The option // -print-module-scope does not affect this change reporter. class InLineChangePrinter : public TextChangeReporter> { public: InLineChangePrinter(bool VerboseMode, bool ColourMode) : TextChangeReporter>(VerboseMode), UseColour(ColourMode) {} ~InLineChangePrinter() override; void registerCallbacks(PassInstrumentationCallbacks &PIC); protected: // Create a representation of the IR. void generateIRRepresentation(Any IR, StringRef PassID, IRDataT &Output) override; // Called when an interesting IR has changed. void handleAfter(StringRef PassID, std::string &Name, const IRDataT &Before, const IRDataT &After, Any) override; void handleFunctionCompare(StringRef Name, StringRef Prefix, StringRef PassID, StringRef Divider, bool InModule, unsigned Minor, const FuncDataT &Before, const FuncDataT &After); bool UseColour; }; class VerifyInstrumentation { bool DebugLogging; public: VerifyInstrumentation(bool DebugLogging) : DebugLogging(DebugLogging) {} void registerCallbacks(PassInstrumentationCallbacks &PIC); }; /// This class implements --time-trace functionality for new pass manager. /// It provides the pass-instrumentation callbacks that measure the pass /// execution time. They collect time tracing info by TimeProfiler. class TimeProfilingPassesHandler { public: TimeProfilingPassesHandler(); // We intend this to be unique per-compilation, thus no copies. TimeProfilingPassesHandler(const TimeProfilingPassesHandler &) = delete; void operator=(const TimeProfilingPassesHandler &) = delete; void registerCallbacks(PassInstrumentationCallbacks &PIC); private: // Implementation of pass instrumentation callbacks. void runBeforePass(StringRef PassID, Any IR); void runAfterPass(); }; // Class that holds transitions between basic blocks. The transitions // are contained in a map of values to names of basic blocks. class DCData { public: // Fill the map with the transitions from basic block \p B. DCData(const BasicBlock &B); // Return an iterator to the names of the successor blocks. StringMap::const_iterator begin() const { return Successors.begin(); } StringMap::const_iterator end() const { return Successors.end(); } // Return the label of the basic block reached on a transition on \p S. StringRef getSuccessorLabel(StringRef S) const { assert(Successors.count(S) == 1 && "Expected to find successor."); return Successors.find(S)->getValue(); } protected: // Add a transition to \p Succ on \p Label void addSuccessorLabel(StringRef Succ, StringRef Label) { std::pair SS{Succ.str(), Label.str()}; Successors.insert(SS); } StringMap Successors; }; // A change reporter that builds a website with links to pdf files showing // dot control flow graphs with changed instructions shown in colour. class DotCfgChangeReporter : public ChangeReporter> { public: DotCfgChangeReporter(bool Verbose); ~DotCfgChangeReporter() override; void registerCallbacks(PassInstrumentationCallbacks &PIC); protected: // Initialize the HTML file and output the header. bool initializeHTML(); // Called on the first IR processed. void handleInitialIR(Any IR) override; // Called before and after a pass to get the representation of the IR. void generateIRRepresentation(Any IR, StringRef PassID, IRDataT &Output) override; // Called when the pass is not iteresting. void omitAfter(StringRef PassID, std::string &Name) override; // Called when an interesting IR has changed. void handleAfter(StringRef PassID, std::string &Name, const IRDataT &Before, const IRDataT &After, Any) override; // Called when an interesting pass is invalidated. void handleInvalidated(StringRef PassID) override; // Called when the IR or pass is not interesting. void handleFiltered(StringRef PassID, std::string &Name) override; // Called when an ignored pass is encountered. void handleIgnored(StringRef PassID, std::string &Name) override; // Generate the pdf file into \p Dir / \p PDFFileName using \p DotFile as // input and return the html tag with \Text as the content. static std::string genHTML(StringRef Text, StringRef DotFile, StringRef PDFFileName); void handleFunctionCompare(StringRef Name, StringRef Prefix, StringRef PassID, StringRef Divider, bool InModule, unsigned Minor, const FuncDataT &Before, const FuncDataT &After); unsigned N = 0; std::unique_ptr HTML; }; // Print IR on crash. class PrintCrashIRInstrumentation { public: PrintCrashIRInstrumentation() : SavedIR("*** Dump of IR Before Last Pass Unknown ***") {} ~PrintCrashIRInstrumentation(); void registerCallbacks(PassInstrumentationCallbacks &PIC); void reportCrashIR(); protected: std::string SavedIR; private: // The crash reporter that will report on a crash. static PrintCrashIRInstrumentation *CrashReporter; // Crash handler registered when print-on-crash is specified. static void SignalHandler(void *); }; /// This class provides an interface to register all the standard pass /// instrumentations and manages their state (if any). class StandardInstrumentations { PrintIRInstrumentation PrintIR; PrintPassInstrumentation PrintPass; TimePassesHandler TimePasses; TimeProfilingPassesHandler TimeProfilingPasses; OptNoneInstrumentation OptNone; OptPassGateInstrumentation OptPassGate; PreservedCFGCheckerInstrumentation PreservedCFGChecker; IRChangedPrinter PrintChangedIR; PseudoProbeVerifier PseudoProbeVerification; InLineChangePrinter PrintChangedDiff; DotCfgChangeReporter WebsiteChangeReporter; PrintCrashIRInstrumentation PrintCrashIR; IRChangedTester ChangeTester; VerifyInstrumentation Verify; bool VerifyEach; public: StandardInstrumentations(LLVMContext &Context, bool DebugLogging, bool VerifyEach = false, PrintPassOptions PrintPassOpts = PrintPassOptions()); // Register all the standard instrumentation callbacks. If \p FAM is nullptr // then PreservedCFGChecker is not enabled. void registerCallbacks(PassInstrumentationCallbacks &PIC, ModuleAnalysisManager *MAM = nullptr); TimePassesHandler &getTimePasses() { return TimePasses; } }; extern template class ChangeReporter; extern template class TextChangeReporter; extern template class BlockDataT; extern template class FuncDataT; extern template class IRDataT; extern template class ChangeReporter>; extern template class TextChangeReporter>; extern template class IRComparer; } // namespace llvm #endif