Changeset View
Changeset View
Standalone View
Standalone View
mlir/lib/Pass/Pass.cpp
Show First 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | |||||
namespace mlir { | namespace mlir { | ||||
namespace detail { | namespace detail { | ||||
struct OpPassManagerImpl { | struct OpPassManagerImpl { | ||||
OpPassManagerImpl(OperationName name, bool disableThreads, bool verifyPasses) | OpPassManagerImpl(OperationName name, bool disableThreads, bool verifyPasses) | ||||
: name(name), disableThreads(disableThreads), verifyPasses(verifyPasses) { | : name(name), disableThreads(disableThreads), verifyPasses(verifyPasses) { | ||||
} | } | ||||
/// Merge the passes of this pass manager into the one provided. | /// Merge the passes of this pass manager into the one provided. | ||||
void mergeInto(OpPassManagerImpl &rhs) { | void mergeInto(OpPassManagerImpl &rhs); | ||||
assert(name == rhs.name && "merging unrelated pass managers"); | |||||
for (auto &pass : passes) | /// Nest a new operation pass manager for the given operation kind under this | ||||
rhs.passes.push_back(std::move(pass)); | /// pass manager. | ||||
passes.clear(); | OpPassManager &nest(const OperationName &nestedName); | ||||
OpPassManager &nest(StringRef nestedName) { | |||||
return nest(OperationName(nestedName, getContext())); | |||||
} | } | ||||
/// Add the given pass to this pass manager. If this pass has a concrete | |||||
/// operation type, it must be the same type as this pass manager. | |||||
void addPass(std::unique_ptr<Pass> pass); | |||||
/// Coalesce adjacent AdaptorPasses into one large adaptor. This runs | /// Coalesce adjacent AdaptorPasses into one large adaptor. This runs | ||||
/// recursively through the pipeline graph. | /// recursively through the pipeline graph. | ||||
void coalesceAdjacentAdaptorPasses(); | void coalesceAdjacentAdaptorPasses(); | ||||
/// Split all of AdaptorPasses such that each adaptor only contains one leaf | |||||
/// pass. | |||||
void splitAdaptorPasses(); | |||||
/// Return an instance of the context. | |||||
MLIRContext *getContext() const { | |||||
return name.getAbstractOperation()->dialect.getContext(); | |||||
} | |||||
/// The name of the operation that passes of this pass manager operate on. | /// The name of the operation that passes of this pass manager operate on. | ||||
OperationName name; | OperationName name; | ||||
/// Flag to disable multi-threading of passes. | /// Flag to disable multi-threading of passes. | ||||
bool disableThreads : 1; | bool disableThreads : 1; | ||||
/// Flag that specifies if the IR should be verified after each pass has run. | /// Flag that specifies if the IR should be verified after each pass has run. | ||||
bool verifyPasses : 1; | bool verifyPasses : 1; | ||||
/// The set of passes to run as part of this pass manager. | /// The set of passes to run as part of this pass manager. | ||||
std::vector<std::unique_ptr<Pass>> passes; | std::vector<std::unique_ptr<Pass>> passes; | ||||
}; | }; | ||||
} // end namespace detail | } // end namespace detail | ||||
} // end namespace mlir | } // end namespace mlir | ||||
/// Coalesce adjacent AdaptorPasses into one large adaptor. This runs | void OpPassManagerImpl::mergeInto(OpPassManagerImpl &rhs) { | ||||
/// recursively through the pipeline graph. | assert(name == rhs.name && "merging unrelated pass managers"); | ||||
for (auto &pass : passes) | |||||
rhs.passes.push_back(std::move(pass)); | |||||
passes.clear(); | |||||
} | |||||
OpPassManager &OpPassManagerImpl::nest(const OperationName &nestedName) { | |||||
OpPassManager nested(nestedName, disableThreads, verifyPasses); | |||||
auto *adaptor = new OpToOpPassAdaptor(std::move(nested)); | |||||
addPass(std::unique_ptr<Pass>(adaptor)); | |||||
return adaptor->getPassManagers().front(); | |||||
} | |||||
void OpPassManagerImpl::addPass(std::unique_ptr<Pass> pass) { | |||||
// If this pass runs on a different operation than this pass manager, then | |||||
// implicitly nest a pass manager for this operation. | |||||
auto passOpName = pass->getOpName(); | |||||
if (passOpName && passOpName != name.getStringRef()) | |||||
return nest(*passOpName).addPass(std::move(pass)); | |||||
passes.emplace_back(std::move(pass)); | |||||
if (verifyPasses) | |||||
passes.emplace_back(std::make_unique<VerifierPass>()); | |||||
} | |||||
void OpPassManagerImpl::coalesceAdjacentAdaptorPasses() { | void OpPassManagerImpl::coalesceAdjacentAdaptorPasses() { | ||||
// Bail out early if there are no adaptor passes. | // Bail out early if there are no adaptor passes. | ||||
if (llvm::none_of(passes, [](std::unique_ptr<Pass> &pass) { | if (llvm::none_of(passes, [](std::unique_ptr<Pass> &pass) { | ||||
return isa<OpToOpPassAdaptor>(pass.get()); | return isa<OpToOpPassAdaptor>(pass.get()); | ||||
})) | })) | ||||
return; | return; | ||||
// Walk the pass list and merge adjacent adaptors. | // Walk the pass list and merge adjacent adaptors. | ||||
Show All 34 Lines | for (auto &pm : lastAdaptor->getPassManagers()) | ||||
pm.getImpl().coalesceAdjacentAdaptorPasses(); | pm.getImpl().coalesceAdjacentAdaptorPasses(); | ||||
} | } | ||||
// Now that the adaptors have been merged, erase the empty slot corresponding | // Now that the adaptors have been merged, erase the empty slot corresponding | ||||
// to the merged adaptors that were nulled-out in the loop above. | // to the merged adaptors that were nulled-out in the loop above. | ||||
llvm::erase_if(passes, std::logical_not<std::unique_ptr<Pass>>()); | llvm::erase_if(passes, std::logical_not<std::unique_ptr<Pass>>()); | ||||
} | } | ||||
void OpPassManagerImpl::splitAdaptorPasses() { | |||||
std::vector<std::unique_ptr<Pass>> oldPasses; | |||||
std::swap(passes, oldPasses); | |||||
for (std::unique_ptr<Pass> &pass : oldPasses) { | |||||
// If this pass isn't an adaptor, move it directly to the new pass list. | |||||
auto *currentAdaptor = dyn_cast<OpToOpPassAdaptor>(pass.get()); | |||||
if (!currentAdaptor) { | |||||
passes.push_back(std::move(pass)); | |||||
continue; | |||||
} | |||||
// Otherwise, split the adaptors of each manager within the adaptor. | |||||
for (OpPassManager &adaptorPM : currentAdaptor->getPassManagers()) { | |||||
adaptorPM.getImpl().splitAdaptorPasses(); | |||||
// Add all non-verifier passes to this pass manager. | |||||
for (std::unique_ptr<Pass> &nestedPass : adaptorPM.getImpl().passes) { | |||||
if (!isa<VerifierPass>(nestedPass.get())) | |||||
nest(adaptorPM.getOpName()).addPass(std::move(nestedPass)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// OpPassManager | // OpPassManager | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
OpPassManager::OpPassManager(OperationName name, bool disableThreads, | OpPassManager::OpPassManager(OperationName name, bool disableThreads, | ||||
bool verifyPasses) | bool verifyPasses) | ||||
: impl(new OpPassManagerImpl(name, disableThreads, verifyPasses)) { | : impl(new OpPassManagerImpl(name, disableThreads, verifyPasses)) { | ||||
assert(name.getAbstractOperation() && | assert(name.getAbstractOperation() && | ||||
Show All 27 Lines | for (auto &pass : impl->passes) | ||||
if (failed(pass->run(op, am))) | if (failed(pass->run(op, am))) | ||||
return failure(); | return failure(); | ||||
return success(); | return success(); | ||||
} | } | ||||
/// Nest a new operation pass manager for the given operation kind under this | /// Nest a new operation pass manager for the given operation kind under this | ||||
/// pass manager. | /// pass manager. | ||||
OpPassManager &OpPassManager::nest(const OperationName &nestedName) { | OpPassManager &OpPassManager::nest(const OperationName &nestedName) { | ||||
OpPassManager nested(nestedName, impl->disableThreads, impl->verifyPasses); | return impl->nest(nestedName); | ||||
auto *adaptor = new OpToOpPassAdaptor(std::move(nested)); | |||||
addPass(std::unique_ptr<Pass>(adaptor)); | |||||
return adaptor->getPassManagers().front(); | |||||
} | } | ||||
OpPassManager &OpPassManager::nest(StringRef nestedName) { | OpPassManager &OpPassManager::nest(StringRef nestedName) { | ||||
return nest(OperationName(nestedName, getContext())); | return impl->nest(nestedName); | ||||
} | } | ||||
/// Add the given pass to this pass manager. If this pass has a concrete | /// Add the given pass to this pass manager. If this pass has a concrete | ||||
/// operation type, it must be the same type as this pass manager. | /// operation type, it must be the same type as this pass manager. | ||||
void OpPassManager::addPass(std::unique_ptr<Pass> pass) { | void OpPassManager::addPass(std::unique_ptr<Pass> pass) { | ||||
// If this pass runs on a different operation than this pass manager, then | impl->addPass(std::move(pass)); | ||||
// implicitly nest a pass manager for this operation. | |||||
auto passOpName = pass->getOpName(); | |||||
if (passOpName && passOpName != impl->name.getStringRef()) | |||||
return nest(*passOpName).addPass(std::move(pass)); | |||||
impl->passes.emplace_back(std::move(pass)); | |||||
if (impl->verifyPasses) | |||||
impl->passes.emplace_back(std::make_unique<VerifierPass>()); | |||||
} | } | ||||
/// Returns the number of passes held by this manager. | /// Returns the number of passes held by this manager. | ||||
size_t OpPassManager::size() const { return impl->passes.size(); } | size_t OpPassManager::size() const { return impl->passes.size(); } | ||||
/// Returns the internal implementation instance. | /// Returns the internal implementation instance. | ||||
OpPassManagerImpl &OpPassManager::getImpl() { return *impl; } | OpPassManagerImpl &OpPassManager::getImpl() { return *impl; } | ||||
/// Return an instance of the context. | /// Return an instance of the context. | ||||
MLIRContext *OpPassManager::getContext() const { | MLIRContext *OpPassManager::getContext() const { return impl->getContext(); } | ||||
return impl->name.getAbstractOperation()->dialect.getContext(); | |||||
} | |||||
/// Return the operation name that this pass manager operates on. | /// Return the operation name that this pass manager operates on. | ||||
const OperationName &OpPassManager::getOpName() const { return impl->name; } | const OperationName &OpPassManager::getOpName() const { return impl->name; } | ||||
/// Prints out the passes of the pass manager as the textual representation | /// Prints out the given passes as the textual representation of a pipeline. | ||||
/// of pipelines. | static void printAsTextualPipeline(ArrayRef<std::unique_ptr<Pass>> passes, | ||||
void OpPassManager::printAsTextualPipeline(raw_ostream &os) { | raw_ostream &os) { | ||||
// Filter out passes that are not part of the public pipeline. | // Filter out passes that are not part of the public pipeline. | ||||
auto filteredPasses = llvm::make_filter_range( | auto filteredPasses = | ||||
impl->passes, [](const std::unique_ptr<Pass> &pass) { | llvm::make_filter_range(passes, [](const std::unique_ptr<Pass> &pass) { | ||||
return !isa<VerifierPass>(pass); | return !isa<VerifierPass>(pass); | ||||
}); | }); | ||||
llvm::interleaveComma(filteredPasses, os, | llvm::interleaveComma(filteredPasses, os, | ||||
[&](const std::unique_ptr<Pass> &pass) { | [&](const std::unique_ptr<Pass> &pass) { | ||||
pass->printAsTextualPipeline(os); | pass->printAsTextualPipeline(os); | ||||
}); | }); | ||||
} | } | ||||
/// Prints out the passes of the pass manager as the textual representation | |||||
/// of pipelines. | |||||
void OpPassManager::printAsTextualPipeline(raw_ostream &os) { | |||||
::printAsTextualPipeline(impl->passes, os); | |||||
} | |||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// OpToOpPassAdaptor | // OpToOpPassAdaptor | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
/// Utility to run the given operation and analysis manager on a provided op | /// Utility to run the given operation and analysis manager on a provided op | ||||
/// pass manager. | /// pass manager. | ||||
static LogicalResult runPipeline(OpPassManager &pm, Operation *op, | static LogicalResult runPipeline(OpPassManager &pm, Operation *op, | ||||
AnalysisManager am) { | AnalysisManager am) { | ||||
▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | void OpToOpPassAdaptor::runOnOperationAsyncImpl() { | ||||
if (passFailed) | if (passFailed) | ||||
signalPassFailure(); | signalPassFailure(); | ||||
} | } | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// PassCrashReproducer | // PassCrashReproducer | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
/// Safely run the pass manager over the given module, creating a reproducible | /// Run the pass manager with crash recover enabled. | ||||
/// on failure or crash. | LogicalResult PassManager::runWithCrashRecovery(ModuleOp module, | ||||
static LogicalResult runWithCrashRecovery(OpPassManager &pm, | AnalysisManager am) { | ||||
ModuleAnalysisManager &am, | // If this isn't a local producer, run all of the passes in recovery mode. | ||||
ModuleOp module, | if (!localReproducer) | ||||
StringRef crashReproducerFileName) { | return runWithCrashRecovery(impl->passes, module, am); | ||||
// Split the passes within adaptors to ensure that each pass can be run in | |||||
// isolation. | |||||
impl->splitAdaptorPasses(); | |||||
// If this is a local producer, run each of the passes individually. If the | |||||
// verifier is enabled, each pass will have a verifier after. This is included | |||||
// in the recovery run. | |||||
unsigned stride = impl->verifyPasses ? 2 : 1; | |||||
MutableArrayRef<std::unique_ptr<Pass>> passes = impl->passes; | |||||
for (unsigned i = 0, e = passes.size(); i != e; i += stride) { | |||||
if (failed(runWithCrashRecovery(passes.slice(i, stride), module, am))) | |||||
return failure(); | |||||
} | |||||
return success(); | |||||
} | |||||
/// Run the given passes with crash recover enabled. | |||||
LogicalResult | |||||
PassManager::runWithCrashRecovery(MutableArrayRef<std::unique_ptr<Pass>> passes, | |||||
ModuleOp module, AnalysisManager am) { | |||||
/// Enable crash recovery. | /// Enable crash recovery. | ||||
llvm::CrashRecoveryContext::Enable(); | llvm::CrashRecoveryContext::Enable(); | ||||
// Grab the textual pipeline executing within the pass manager first, just in | // Grab the textual pipeline being executed first, just in case the passes | ||||
// case the pass manager becomes compromised. | // become compromised. | ||||
std::string pipeline; | std::string pipeline; | ||||
{ | { | ||||
llvm::raw_string_ostream pipelineOS(pipeline); | llvm::raw_string_ostream pipelineOS(pipeline); | ||||
pm.printAsTextualPipeline(pipelineOS); | ::printAsTextualPipeline(passes, pipelineOS); | ||||
} | } | ||||
// Clone the initial module before running it through the pass pipeline. | // Clone the initial module before running it through the pass pipeline. | ||||
OwningModuleRef reproducerModule = module.clone(); | OwningModuleRef reproducerModule = module.clone(); | ||||
// Safely invoke the pass manager within a recovery context. | // Safely invoke the passes within a recovery context. | ||||
LogicalResult passManagerResult = failure(); | LogicalResult passManagerResult = failure(); | ||||
llvm::CrashRecoveryContext recoveryContext; | llvm::CrashRecoveryContext recoveryContext; | ||||
recoveryContext.RunSafelyOnThread( | recoveryContext.RunSafelyOnThread([&] { | ||||
[&] { passManagerResult = pm.run(module, am); }); | for (std::unique_ptr<Pass> &pass : passes) | ||||
if (failed(pass->run(module, am))) | |||||
/// Disable crash recovery. | return; | ||||
passManagerResult = success(); | |||||
}); | |||||
llvm::CrashRecoveryContext::Disable(); | llvm::CrashRecoveryContext::Disable(); | ||||
if (succeeded(passManagerResult)) | if (succeeded(passManagerResult)) | ||||
return success(); | return success(); | ||||
// The conversion failed, so generate a reproducible. | |||||
std::string error; | std::string error; | ||||
std::unique_ptr<llvm::ToolOutputFile> outputFile = | std::unique_ptr<llvm::ToolOutputFile> outputFile = | ||||
mlir::openOutputFile(crashReproducerFileName, &error); | mlir::openOutputFile(*crashReproducerFileName, &error); | ||||
if (!outputFile) | if (!outputFile) | ||||
return emitError(UnknownLoc::get(pm.getContext()), | return module.emitError("<MLIR-PassManager-Crash-Reproducer>: ") << error; | ||||
"<MLIR-PassManager-Crash-Reproducer>: ") | |||||
<< error; | |||||
auto &outputOS = outputFile->os(); | auto &outputOS = outputFile->os(); | ||||
// Output the current pass manager configuration. | // Output the current pass manager configuration. | ||||
outputOS << "// configuration: -pass-pipeline='" << pipeline << "'"; | outputOS << "// configuration: -pass-pipeline='" << pipeline << "'"; | ||||
if (pm.getImpl().disableThreads) | if (impl->disableThreads) | ||||
outputOS << " -disable-pass-threading"; | outputOS << " -disable-pass-threading"; | ||||
// TODO(riverriddle) Should this also be configured with a pass manager flag? | // TODO: Should this also be configured with a pass manager flag? | ||||
outputOS << "\n// note: verifyPasses=" | outputOS << "\n// note: verifyPasses=" | ||||
<< (pm.getImpl().verifyPasses ? "true" : "false") << "\n"; | << (impl->verifyPasses ? "true" : "false") << "\n"; | ||||
// Output the .mlir module. | // Output the .mlir module. | ||||
reproducerModule->print(outputOS); | reproducerModule->print(outputOS); | ||||
outputFile->keep(); | outputFile->keep(); | ||||
return reproducerModule->emitError() | return reproducerModule->emitError() | ||||
<< "A failure has been detected while processing the MLIR module, a " | << "A failure has been detected while processing the MLIR module, a " | ||||
"reproducer has been generated in '" | "reproducer has been generated in '" | ||||
<< crashReproducerFileName << "'"; | << *crashReproducerFileName << "'"; | ||||
} | } | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// PassManager | // PassManager | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
PassManager::PassManager(MLIRContext *ctx, bool verifyPasses) | PassManager::PassManager(MLIRContext *ctx, bool verifyPasses) | ||||
: OpPassManager(OperationName(ModuleOp::getOperationName(), ctx), | : OpPassManager(OperationName(ModuleOp::getOperationName(), ctx), | ||||
/*disableThreads=*/false, verifyPasses), | /*disableThreads=*/false, verifyPasses), | ||||
passTiming(false) {} | passTiming(false), localReproducer(false) {} | ||||
PassManager::~PassManager() {} | PassManager::~PassManager() {} | ||||
/// Run the passes within this manager on the provided module. | /// Run the passes within this manager on the provided module. | ||||
LogicalResult PassManager::run(ModuleOp module) { | LogicalResult PassManager::run(ModuleOp module) { | ||||
// Before running, make sure to coalesce any adjacent pass adaptors in the | // Before running, make sure to coalesce any adjacent pass adaptors in the | ||||
// pipeline. | // pipeline. | ||||
getImpl().coalesceAdjacentAdaptorPasses(); | getImpl().coalesceAdjacentAdaptorPasses(); | ||||
// Construct an analysis manager for the pipeline. | // Construct an analysis manager for the pipeline. | ||||
ModuleAnalysisManager am(module, instrumentor.get()); | ModuleAnalysisManager am(module, instrumentor.get()); | ||||
// If reproducer generation is enabled, run the pass manager with crash | // If reproducer generation is enabled, run the pass manager with crash | ||||
// handling enabled. | // handling enabled. | ||||
LogicalResult result = | LogicalResult result = crashReproducerFileName | ||||
crashReproducerFileName | ? runWithCrashRecovery(module, am) | ||||
? runWithCrashRecovery(*this, am, module, *crashReproducerFileName) | |||||
: OpPassManager::run(module, am); | : OpPassManager::run(module, am); | ||||
// Dump all of the pass statistics if necessary. | // Dump all of the pass statistics if necessary. | ||||
if (passStatisticsMode) | if (passStatisticsMode) | ||||
dumpStatistics(); | dumpStatistics(); | ||||
return result; | return result; | ||||
} | } | ||||
/// Disable support for multi-threading within the pass manager. | /// Disable support for multi-threading within the pass manager. | ||||
void PassManager::disableMultithreading(bool disable) { | void PassManager::disableMultithreading(bool disable) { | ||||
getImpl().disableThreads = disable; | getImpl().disableThreads = disable; | ||||
} | } | ||||
bool PassManager::isMultithreadingEnabled() { | bool PassManager::isMultithreadingEnabled() { | ||||
return !getImpl().disableThreads; | return !getImpl().disableThreads; | ||||
} | } | ||||
/// Enable support for the pass manager to generate a reproducer on the event | /// Enable support for the pass manager to generate a reproducer on the event | ||||
/// of a crash or a pass failure. `outputFile` is a .mlir filename used to write | /// of a crash or a pass failure. `outputFile` is a .mlir filename used to write | ||||
/// the generated reproducer. | /// the generated reproducer. If `genLocalReproducer` is true, the pass manager | ||||
void PassManager::enableCrashReproducerGeneration(StringRef outputFile) { | /// will attempt to generate a local reproducer that contains the smallest | ||||
/// pipeline. | |||||
void PassManager::enableCrashReproducerGeneration(StringRef outputFile, | |||||
bool genLocalReproducer) { | |||||
crashReproducerFileName = std::string(outputFile); | crashReproducerFileName = std::string(outputFile); | ||||
localReproducer = genLocalReproducer; | |||||
} | } | ||||
/// Add the provided instrumentation to the pass manager. | /// Add the provided instrumentation to the pass manager. | ||||
void PassManager::addInstrumentation(std::unique_ptr<PassInstrumentation> pi) { | void PassManager::addInstrumentation(std::unique_ptr<PassInstrumentation> pi) { | ||||
if (!instrumentor) | if (!instrumentor) | ||||
instrumentor = std::make_unique<PassInstrumentor>(); | instrumentor = std::make_unique<PassInstrumentor>(); | ||||
instrumentor->addInstrumentation(std::move(pi)); | instrumentor->addInstrumentation(std::move(pi)); | ||||
▲ Show 20 Lines • Show All 141 Lines • Show Last 20 Lines |