[mlir:Pass] Generate a reproducer as early as possible

This avoids keeping references to passes that may be freed by
the time that the pass manager has finished executing (in the
non-crash case).

Fixes PR#52069

Differential Revision: https://reviews.llvm.org/D111106
This commit is contained in:
River Riddle 2021-10-05 00:24:24 +00:00
parent 8cf5319aff
commit b8ffcb12e2
3 changed files with 31 additions and 14 deletions

View File

@ -227,6 +227,10 @@ formatPassOpReproducerMessage(Diagnostic &os,
void PassCrashReproducerGenerator::finalize(Operation *rootOp,
LogicalResult executionResult) {
// Don't generate a reproducer if we have no active contexts.
if (impl->activeContexts.empty())
return;
// If the pass manager execution succeeded, we don't generate any reproducers.
if (succeeded(executionResult))
return impl->activeContexts.clear();
@ -345,20 +349,20 @@ struct CrashReproducerInstrumentation : public PassInstrumentation {
: generator(generator) {}
~CrashReproducerInstrumentation() override = default;
/// A callback to run before a pass is executed.
void runBeforePass(Pass *pass, Operation *op) override {
if (!isa<OpToOpPassAdaptor>(pass))
generator.prepareReproducerFor(pass, op);
}
/// A callback to run after a pass is successfully executed. This function
/// takes a pointer to the pass to be executed, as well as the current
/// operation being operated on.
void runAfterPass(Pass *pass, Operation *op) override {
if (!isa<OpToOpPassAdaptor>(pass))
generator.removeLastReproducerFor(pass, op);
}
void runAfterPassFailed(Pass *pass, Operation *op) override {
generator.finalize(op, /*executionResult=*/failure());
}
private:
/// The generator used to create crash reproducers.
PassCrashReproducerGenerator &generator;

View File

@ -0,0 +1,17 @@
// Check that local reproducers will also traverse dynamic pass pipelines.
// RUN: mlir-opt %s -pass-pipeline='test-module-pass,test-dynamic-pipeline{op-name=inner_mod1 run-on-nested-operations=1 dynamic-pipeline=test-pass-failure}' -pass-pipeline-crash-reproducer=%t -verify-diagnostics -pass-pipeline-local-reproducer --mlir-disable-threading
// RUN: cat %t | FileCheck -check-prefix=REPRO_LOCAL_DYNAMIC_FAILURE %s
// The crash recovery mechanism will leak memory allocated in the crashing thread.
// UNSUPPORTED: asan
module @inner_mod1 {
// expected-error@below {{Failures have been detected while processing an MLIR pass pipeline}}
// expected-note@below {{Pipeline failed while executing}}
module @foo {}
}
// REPRO_LOCAL_DYNAMIC_FAILURE: configuration: -pass-pipeline='builtin.module(test-pass-failure)'
// REPRO_LOCAL_DYNAMIC_FAILURE: module @inner_mod1
// REPRO_LOCAL_DYNAMIC_FAILURE: module @foo {

View File

@ -56,16 +56,14 @@ public:
llvm::errs() << "dynamic-pipeline skip op name: " << opName << "\n";
return;
}
if (!pm) {
pm = std::make_unique<OpPassManager>(currentOp->getName().getIdentifier(),
OpPassManager::Nesting::Implicit);
(void)parsePassPipeline(pipeline, *pm, llvm::errs());
}
OpPassManager pm(currentOp->getName().getIdentifier(),
OpPassManager::Nesting::Implicit);
(void)parsePassPipeline(pipeline, pm, llvm::errs());
// Check that running on the parent operation always immediately fails.
if (runOnParent) {
if (currentOp->getParentOp())
if (!failed(runPipeline(*pm, currentOp->getParentOp())))
if (!failed(runPipeline(pm, currentOp->getParentOp())))
signalPassFailure();
return;
}
@ -78,18 +76,16 @@ public:
return;
llvm::errs() << "Run on " << *op << "\n";
// Run on the current operation
if (failed(runPipeline(*pm, op)))
if (failed(runPipeline(pm, op)))
signalPassFailure();
});
} else {
// Run on the current operation
if (failed(runPipeline(*pm, currentOp)))
if (failed(runPipeline(pm, currentOp)))
signalPassFailure();
}
}
std::unique_ptr<OpPassManager> pm;
Option<bool> runOnNestedOp{
*this, "run-on-nested-operations",
llvm::cl::desc("This will apply the pipeline on nested operations under "