diff options
Diffstat (limited to 'llvm/lib/Passes/StandardInstrumentations.cpp')
-rw-r--r-- | llvm/lib/Passes/StandardInstrumentations.cpp | 310 |
1 files changed, 222 insertions, 88 deletions
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp index ad2504eca2fb..7eef511928ec 100644 --- a/llvm/lib/Passes/StandardInstrumentations.cpp +++ b/llvm/lib/Passes/StandardInstrumentations.cpp @@ -20,11 +20,11 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" -#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassInstrumentation.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/PrintPasses.h" +#include "llvm/IR/StructuralHash.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/CrashRecoveryContext.h" @@ -44,14 +44,14 @@ using namespace llvm; -cl::opt<bool> PreservedCFGCheckerInstrumentation::VerifyPreservedCFG( - "verify-cfg-preserved", cl::Hidden, -#ifdef NDEBUG - cl::init(false) +static cl::opt<bool> VerifyAnalysisInvalidation("verify-analysis-invalidation", + cl::Hidden, +#ifdef EXPENSIVE_CHECKS + cl::init(true) #else - cl::init(true) + cl::init(false) #endif - ); +); // An option that supports the -print-changed option. See // the description for -print-changed for an explanation of the use @@ -96,16 +96,30 @@ static cl::opt<std::string> DotCfgDir( cl::desc("Generate dot files into specified directory for changed IRs"), cl::Hidden, cl::init("./")); -// An option to print the IR that was being processed when a pass crashes. -static cl::opt<bool> - PrintCrashIR("print-on-crash", - cl::desc("Print the last form of the IR before crash"), - cl::Hidden); +// Options to print the IR that was being processed when a pass crashes. +static cl::opt<std::string> PrintOnCrashPath( + "print-on-crash-path", + cl::desc("Print the last form of the IR before crash to a file"), + cl::Hidden); + +static cl::opt<bool> PrintOnCrash( + "print-on-crash", + cl::desc("Print the last form of the IR before crash (use -print-on-crash-path to dump to a file)"), + cl::Hidden); static cl::opt<std::string> OptBisectPrintIRPath( "opt-bisect-print-ir-path", cl::desc("Print IR to path when opt-bisect-limit is reached"), cl::Hidden); +static cl::opt<bool> PrintPassNumbers( + "print-pass-numbers", cl::init(false), cl::Hidden, + cl::desc("Print pass names and their ordinals")); + +static cl::opt<unsigned> + PrintAtPassNumber("print-at-pass-number", cl::init(0), cl::Hidden, + cl::desc("Print IR at pass with this number as " + "reported by print-passes-names")); + namespace { // An option for specifying an executable that will be called with the IR @@ -691,13 +705,19 @@ void PrintIRInstrumentation::printBeforePass(StringRef PassID, Any IR) { // 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 (shouldPrintAfterPass(PassID)) + if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() || + shouldPrintAfterPass(PassID)) pushModuleDesc(PassID, IR); - if (!shouldPrintBeforePass(PassID)) + if (!shouldPrintIR(IR)) return; - if (!shouldPrintIR(IR)) + ++CurrentPassNumber; + + if (shouldPrintPassNumbers()) + dbgs() << " Running pass " << CurrentPassNumber << " " << PassID << "\n"; + + if (!shouldPrintBeforePass(PassID)) return; dbgs() << "*** IR Dump Before " << PassID << " on " << getIRName(IR) @@ -709,7 +729,8 @@ void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) { if (isIgnored(PassID)) return; - if (!shouldPrintAfterPass(PassID)) + if (!shouldPrintAfterPass(PassID) && !shouldPrintPassNumbers() && + !shouldPrintAtPassNumber()) return; const Module *M; @@ -718,19 +739,23 @@ void PrintIRInstrumentation::printAfterPass(StringRef PassID, Any IR) { std::tie(M, IRName, StoredPassID) = popModuleDesc(PassID); assert(StoredPassID == PassID && "mismatched PassID"); - if (!shouldPrintIR(IR)) + if (!shouldPrintIR(IR) || !shouldPrintAfterPass(PassID)) return; - dbgs() << "*** IR Dump After " << PassID << " on " << IRName << " ***\n"; + dbgs() << "*** IR Dump " + << (shouldPrintAtPassNumber() + ? StringRef(formatv("At {0}-{1}", CurrentPassNumber, PassID)) + : StringRef(formatv("After {0}", PassID))) + << " on " << IRName << " ***\n"; unwrapAndPrint(dbgs(), IR); } void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) { - StringRef PassName = PIC->getPassNameForClassName(PassID); - if (!shouldPrintAfterPass(PassName)) + if (isIgnored(PassID)) return; - if (isIgnored(PassID)) + if (!shouldPrintAfterPass(PassID) && !shouldPrintPassNumbers() && + !shouldPrintAtPassNumber()) return; const Module *M; @@ -740,11 +765,16 @@ void PrintIRInstrumentation::printAfterPassInvalidated(StringRef PassID) { assert(StoredPassID == PassID && "mismatched PassID"); // Additional filtering (e.g. -filter-print-func) can lead to module // printing being skipped. - if (!M) + if (!M || !shouldPrintAfterPass(PassID)) return; - SmallString<20> Banner = - formatv("*** IR Dump After {0} on {1} (invalidated) ***", PassID, IRName); + SmallString<20> Banner; + if (shouldPrintAtPassNumber()) + Banner = formatv("*** IR Dump At {0}-{1} on {2} (invalidated) ***", + CurrentPassNumber, PassID, IRName); + else + Banner = formatv("*** IR Dump After {0} on {1} (invalidated) ***", + PassID, IRName); dbgs() << Banner << "\n"; printIR(dbgs(), M); } @@ -761,21 +791,34 @@ bool PrintIRInstrumentation::shouldPrintAfterPass(StringRef PassID) { if (shouldPrintAfterAll()) return true; + if (shouldPrintAtPassNumber() && CurrentPassNumber == PrintAtPassNumber) + return true; + StringRef PassName = PIC->getPassNameForClassName(PassID); return is_contained(printAfterPasses(), PassName); } +bool PrintIRInstrumentation::shouldPrintPassNumbers() { + return PrintPassNumbers; +} + +bool PrintIRInstrumentation::shouldPrintAtPassNumber() { + return PrintAtPassNumber > 0; +} + void PrintIRInstrumentation::registerCallbacks( PassInstrumentationCallbacks &PIC) { this->PIC = &PIC; // BeforePass callback is not just for printing, it also saves a Module // for later use in AfterPassInvalidated. - if (shouldPrintBeforeSomePass() || shouldPrintAfterSomePass()) + if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() || + shouldPrintBeforeSomePass() || shouldPrintAfterSomePass()) PIC.registerBeforeNonSkippedPassCallback( [this](StringRef P, Any IR) { this->printBeforePass(P, IR); }); - if (shouldPrintAfterSomePass()) { + if (shouldPrintPassNumbers() || shouldPrintAtPassNumber() || + shouldPrintAfterSomePass()) { PIC.registerAfterPassCallback( [this](StringRef P, Any IR, const PreservedAnalyses &) { this->printAfterPass(P, IR); @@ -1050,6 +1093,40 @@ public: AnalysisKey PreservedCFGCheckerAnalysis::Key; +struct PreservedFunctionHashAnalysis + : public AnalysisInfoMixin<PreservedFunctionHashAnalysis> { + static AnalysisKey Key; + + struct FunctionHash { + uint64_t Hash; + }; + + using Result = FunctionHash; + + Result run(Function &F, FunctionAnalysisManager &FAM) { + return Result{StructuralHash(F)}; + } +}; + +AnalysisKey PreservedFunctionHashAnalysis::Key; + +struct PreservedModuleHashAnalysis + : public AnalysisInfoMixin<PreservedModuleHashAnalysis> { + static AnalysisKey Key; + + struct ModuleHash { + uint64_t Hash; + }; + + using Result = ModuleHash; + + Result run(Module &F, ModuleAnalysisManager &FAM) { + return Result{StructuralHash(F)}; + } +}; + +AnalysisKey PreservedModuleHashAnalysis::Key; + bool PreservedCFGCheckerInstrumentation::CFG::invalidate( Function &F, const PreservedAnalyses &PA, FunctionAnalysisManager::Invalidator &) { @@ -1058,39 +1135,52 @@ bool PreservedCFGCheckerInstrumentation::CFG::invalidate( PAC.preservedSet<CFGAnalyses>()); } -void PreservedCFGCheckerInstrumentation::registerCallbacks( - PassInstrumentationCallbacks &PIC, FunctionAnalysisManager &FAM) { - if (!VerifyPreservedCFG) - return; - - FAM.registerPass([&] { return PreservedCFGCheckerAnalysis(); }); +static SmallVector<Function *, 1> GetFunctions(Any IR) { + SmallVector<Function *, 1> Functions; - auto checkCFG = [](StringRef Pass, StringRef FuncName, const CFG &GraphBefore, - const CFG &GraphAfter) { - if (GraphAfter == GraphBefore) - return; + if (const auto **MaybeF = any_cast<const Function *>(&IR)) { + Functions.push_back(*const_cast<Function **>(MaybeF)); + } else if (const auto **MaybeM = any_cast<const Module *>(&IR)) { + for (Function &F : **const_cast<Module **>(MaybeM)) + Functions.push_back(&F); + } + return Functions; +} - dbgs() << "Error: " << Pass - << " does not invalidate CFG analyses but CFG changes detected in " - "function @" - << FuncName << ":\n"; - CFG::printDiff(dbgs(), GraphBefore, GraphAfter); - report_fatal_error(Twine("CFG unexpectedly changed by ", Pass)); - }; +void PreservedCFGCheckerInstrumentation::registerCallbacks( + PassInstrumentationCallbacks &PIC, ModuleAnalysisManager &MAM) { + if (!VerifyAnalysisInvalidation) + return; - PIC.registerBeforeNonSkippedPassCallback( - [this, &FAM](StringRef P, Any IR) { + bool Registered = false; + PIC.registerBeforeNonSkippedPassCallback([this, &MAM, Registered]( + StringRef P, Any IR) mutable { #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS - assert(&PassStack.emplace_back(P)); + assert(&PassStack.emplace_back(P)); #endif - (void)this; - const auto **F = any_cast<const Function *>(&IR); - if (!F) - return; + (void)this; - // Make sure a fresh CFG snapshot is available before the pass. - FAM.getResult<PreservedCFGCheckerAnalysis>(*const_cast<Function *>(*F)); - }); + auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>( + *const_cast<Module *>(unwrapModule(IR, /*Force=*/true))) + .getManager(); + if (!Registered) { + FAM.registerPass([&] { return PreservedCFGCheckerAnalysis(); }); + FAM.registerPass([&] { return PreservedFunctionHashAnalysis(); }); + MAM.registerPass([&] { return PreservedModuleHashAnalysis(); }); + Registered = true; + } + + for (Function *F : GetFunctions(IR)) { + // Make sure a fresh CFG snapshot is available before the pass. + FAM.getResult<PreservedCFGCheckerAnalysis>(*F); + FAM.getResult<PreservedFunctionHashAnalysis>(*F); + } + + if (auto *MaybeM = any_cast<const Module *>(&IR)) { + Module &M = **const_cast<Module **>(MaybeM); + MAM.getResult<PreservedModuleHashAnalysis>(M); + } + }); PIC.registerAfterPassInvalidatedCallback( [this](StringRef P, const PreservedAnalyses &PassPA) { @@ -1101,27 +1191,60 @@ void PreservedCFGCheckerInstrumentation::registerCallbacks( (void)this; }); - PIC.registerAfterPassCallback([this, &FAM, - checkCFG](StringRef P, Any IR, - const PreservedAnalyses &PassPA) { + PIC.registerAfterPassCallback([this, &MAM](StringRef P, Any IR, + const PreservedAnalyses &PassPA) { #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS assert(PassStack.pop_back_val() == P && "Before and After callbacks must correspond"); #endif (void)this; - const auto **F = any_cast<const Function *>(&IR); - if (!F) - return; + // We have to get the FAM via the MAM, rather than directly use a passed in + // FAM because if MAM has not cached the FAM, it won't invalidate function + // analyses in FAM. + auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>( + *const_cast<Module *>(unwrapModule(IR, /*Force=*/true))) + .getManager(); + + for (Function *F : GetFunctions(IR)) { + if (auto *HashBefore = + FAM.getCachedResult<PreservedFunctionHashAnalysis>(*F)) { + if (HashBefore->Hash != StructuralHash(*F)) { + report_fatal_error(formatv( + "Function @{0} changed by {1} without invalidating analyses", + F->getName(), P)); + } + } - if (!PassPA.allAnalysesInSetPreserved<CFGAnalyses>() && - !PassPA.allAnalysesInSetPreserved<AllAnalysesOn<Function>>()) - return; + auto CheckCFG = [](StringRef Pass, StringRef FuncName, + const CFG &GraphBefore, const CFG &GraphAfter) { + if (GraphAfter == GraphBefore) + return; - if (auto *GraphBefore = FAM.getCachedResult<PreservedCFGCheckerAnalysis>( - *const_cast<Function *>(*F))) - checkCFG(P, (*F)->getName(), *GraphBefore, - CFG(*F, /* TrackBBLifetime */ false)); + dbgs() + << "Error: " << Pass + << " does not invalidate CFG analyses but CFG changes detected in " + "function @" + << FuncName << ":\n"; + CFG::printDiff(dbgs(), GraphBefore, GraphAfter); + report_fatal_error(Twine("CFG unexpectedly changed by ", Pass)); + }; + + if (auto *GraphBefore = + FAM.getCachedResult<PreservedCFGCheckerAnalysis>(*F)) + CheckCFG(P, F->getName(), *GraphBefore, + CFG(F, /* TrackBBLifetime */ false)); + } + if (auto *MaybeM = any_cast<const Module *>(&IR)) { + Module &M = **const_cast<Module **>(MaybeM); + if (auto *HashBefore = + MAM.getCachedResult<PreservedModuleHashAnalysis>(M)) { + if (HashBefore->Hash != StructuralHash(M)) { + report_fatal_error(formatv( + "Module changed by {0} without invalidating analyses", P)); + } + } + } }); } @@ -2107,7 +2230,17 @@ StandardInstrumentations::StandardInstrumentations( PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter = nullptr; -void PrintCrashIRInstrumentation::reportCrashIR() { dbgs() << SavedIR; } +void PrintCrashIRInstrumentation::reportCrashIR() { + if (!PrintOnCrashPath.empty()) { + std::error_code EC; + raw_fd_ostream Out(PrintOnCrashPath, EC); + if (EC) + report_fatal_error(errorCodeToError(EC)); + Out << SavedIR; + } else { + dbgs() << SavedIR; + } +} void PrintCrashIRInstrumentation::SignalHandler(void *) { // Called by signal handlers so do not lock here @@ -2115,7 +2248,8 @@ void PrintCrashIRInstrumentation::SignalHandler(void *) { if (!CrashReporter) return; - assert(PrintCrashIR && "Did not expect to get here without option set."); + assert((PrintOnCrash || !PrintOnCrashPath.empty()) && + "Did not expect to get here without option set."); CrashReporter->reportCrashIR(); } @@ -2123,52 +2257,52 @@ PrintCrashIRInstrumentation::~PrintCrashIRInstrumentation() { if (!CrashReporter) return; - assert(PrintCrashIR && "Did not expect to get here without option set."); + assert((PrintOnCrash || !PrintOnCrashPath.empty()) && + "Did not expect to get here without option set."); CrashReporter = nullptr; } void PrintCrashIRInstrumentation::registerCallbacks( PassInstrumentationCallbacks &PIC) { - if (!PrintCrashIR || CrashReporter) + if ((!PrintOnCrash && PrintOnCrashPath.empty()) || CrashReporter) return; sys::AddSignalHandler(SignalHandler, nullptr); CrashReporter = this; - PIC.registerBeforeNonSkippedPassCallback([&PIC, this](StringRef PassID, - Any IR) { - SavedIR.clear(); - raw_string_ostream OS(SavedIR); - OS << formatv("*** Dump of {0}IR Before Last Pass {1}", - llvm::forcePrintModuleIR() ? "Module " : "", PassID); - if (!isInteresting(IR, PassID, PIC.getPassNameForClassName(PassID))) { - OS << " Filtered Out ***\n"; - return; - } - OS << " Started ***\n"; - unwrapAndPrint(OS, IR); - }); + PIC.registerBeforeNonSkippedPassCallback( + [&PIC, this](StringRef PassID, Any IR) { + SavedIR.clear(); + raw_string_ostream OS(SavedIR); + OS << formatv("*** Dump of {0}IR Before Last Pass {1}", + llvm::forcePrintModuleIR() ? "Module " : "", PassID); + if (!isInteresting(IR, PassID, PIC.getPassNameForClassName(PassID))) { + OS << " Filtered Out ***\n"; + return; + } + OS << " Started ***\n"; + unwrapAndPrint(OS, IR); + }); } void StandardInstrumentations::registerCallbacks( - PassInstrumentationCallbacks &PIC, FunctionAnalysisManager *FAM) { + PassInstrumentationCallbacks &PIC, ModuleAnalysisManager *MAM) { PrintIR.registerCallbacks(PIC); PrintPass.registerCallbacks(PIC); TimePasses.registerCallbacks(PIC); OptNone.registerCallbacks(PIC); OptPassGate.registerCallbacks(PIC); - if (FAM) - PreservedCFGChecker.registerCallbacks(PIC, *FAM); PrintChangedIR.registerCallbacks(PIC); PseudoProbeVerification.registerCallbacks(PIC); if (VerifyEach) Verify.registerCallbacks(PIC); PrintChangedDiff.registerCallbacks(PIC); WebsiteChangeReporter.registerCallbacks(PIC); - ChangeTester.registerCallbacks(PIC); - PrintCrashIR.registerCallbacks(PIC); + if (MAM) + PreservedCFGChecker.registerCallbacks(PIC, *MAM); + // TimeProfiling records the pass running time cost. // Its 'BeforePassCallback' can be appended at the tail of all the // BeforeCallbacks by calling `registerCallbacks` in the end. |