aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Passes/StandardInstrumentations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Passes/StandardInstrumentations.cpp')
-rw-r--r--llvm/lib/Passes/StandardInstrumentations.cpp310
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.