2019-02-20 09:17:46 +08:00
|
|
|
//===- Pass.cpp - Pass infrastructure implementation ----------------------===//
|
|
|
|
//
|
2020-01-26 11:58:30 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
2019-12-24 01:35:36 +08:00
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2019-02-20 09:17:46 +08:00
|
|
|
//
|
2019-12-24 01:35:36 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-02-20 09:17:46 +08:00
|
|
|
//
|
|
|
|
// This file implements common pass infrastructure.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "mlir/Pass/Pass.h"
|
2019-03-11 06:44:47 +08:00
|
|
|
#include "PassDetail.h"
|
2019-05-02 02:14:15 +08:00
|
|
|
#include "mlir/IR/Diagnostics.h"
|
2019-09-03 10:24:47 +08:00
|
|
|
#include "mlir/IR/Dialect.h"
|
2019-02-20 09:17:46 +08:00
|
|
|
#include "mlir/IR/Module.h"
|
2020-05-01 04:09:13 +08:00
|
|
|
#include "mlir/IR/Verifier.h"
|
2019-10-11 10:19:11 +08:00
|
|
|
#include "mlir/Support/FileUtilities.h"
|
2019-09-23 17:33:51 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2020-08-27 12:42:38 +08:00
|
|
|
#include "llvm/ADT/ScopeExit.h"
|
2020-04-30 06:08:25 +08:00
|
|
|
#include "llvm/ADT/SetVector.h"
|
2019-03-27 12:15:54 +08:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
2019-10-11 10:19:11 +08:00
|
|
|
#include "llvm/Support/CrashRecoveryContext.h"
|
2019-03-19 02:56:18 +08:00
|
|
|
#include "llvm/Support/Mutex.h"
|
2019-03-27 12:15:54 +08:00
|
|
|
#include "llvm/Support/Parallel.h"
|
2020-04-30 06:08:25 +08:00
|
|
|
#include "llvm/Support/Signals.h"
|
2019-03-27 12:15:54 +08:00
|
|
|
#include "llvm/Support/Threading.h"
|
2019-10-11 10:19:11 +08:00
|
|
|
#include "llvm/Support/ToolOutputFile.h"
|
2019-02-20 09:17:46 +08:00
|
|
|
|
|
|
|
using namespace mlir;
|
2019-02-28 09:49:51 +08:00
|
|
|
using namespace mlir::detail;
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Pass
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-02-20 09:17:46 +08:00
|
|
|
|
|
|
|
/// Out of line virtual method to ensure vtables and metadata are emitted to a
|
|
|
|
/// single .o file.
|
|
|
|
void Pass::anchor() {}
|
|
|
|
|
2019-12-24 07:54:55 +08:00
|
|
|
/// Attempt to initialize the options of this pass from the given string.
|
|
|
|
LogicalResult Pass::initializeOptions(StringRef options) {
|
|
|
|
return passOptions.parseFromString(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Copy the option values from 'other', which is another instance of this
|
|
|
|
/// pass.
|
|
|
|
void Pass::copyOptionValuesFrom(const Pass *other) {
|
|
|
|
passOptions.copyOptionValuesFrom(other->passOptions);
|
|
|
|
}
|
|
|
|
|
2019-10-10 04:48:38 +08:00
|
|
|
/// Prints out the pass in the textual representation of pipelines. If this is
|
|
|
|
/// an adaptor pass, print with the op_name(sub_pass,...) format.
|
|
|
|
void Pass::printAsTextualPipeline(raw_ostream &os) {
|
|
|
|
// Special case for adaptors to use the 'op_name(sub_passes)' format.
|
2020-04-30 06:08:05 +08:00
|
|
|
if (auto *adaptor = dyn_cast<OpToOpPassAdaptor>(this)) {
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(adaptor->getPassManagers(), os,
|
|
|
|
[&](OpPassManager &pm) {
|
|
|
|
os << pm.getOpName() << "(";
|
|
|
|
pm.printAsTextualPipeline(os);
|
|
|
|
os << ")";
|
|
|
|
});
|
2019-12-24 07:54:55 +08:00
|
|
|
return;
|
|
|
|
}
|
2020-04-11 13:48:58 +08:00
|
|
|
// Otherwise, print the pass argument followed by its options. If the pass
|
|
|
|
// doesn't have an argument, print the name of the pass to give some indicator
|
|
|
|
// of what pass was run.
|
|
|
|
StringRef argument = getArgument();
|
|
|
|
if (!argument.empty())
|
|
|
|
os << argument;
|
2019-12-24 07:54:55 +08:00
|
|
|
else
|
2020-04-11 13:48:58 +08:00
|
|
|
os << "unknown<" << getName() << ">";
|
2019-12-24 07:54:55 +08:00
|
|
|
passOptions.print(os);
|
2019-10-10 04:48:38 +08:00
|
|
|
}
|
|
|
|
|
2019-02-28 02:57:59 +08:00
|
|
|
|
2019-02-28 09:49:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-09-03 10:24:47 +08:00
|
|
|
// Verifier Passes
|
2019-02-28 09:49:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
void VerifierPass::runOnOperation() {
|
|
|
|
if (failed(verify(getOperation())))
|
|
|
|
signalPassFailure();
|
|
|
|
markAllAnalysesPreserved();
|
2019-03-27 12:15:54 +08:00
|
|
|
}
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-09-10 00:51:59 +08:00
|
|
|
// OpPassManagerImpl
|
2019-09-03 10:24:47 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace mlir {
|
|
|
|
namespace detail {
|
|
|
|
struct OpPassManagerImpl {
|
2020-05-03 03:28:57 +08:00
|
|
|
OpPassManagerImpl(OperationName name, bool verifyPasses)
|
|
|
|
: name(name), verifyPasses(verifyPasses) {}
|
2019-09-03 10:24:47 +08:00
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
/// Merge the passes of this pass manager into the one provided.
|
2020-04-30 06:08:15 +08:00
|
|
|
void mergeInto(OpPassManagerImpl &rhs);
|
|
|
|
|
|
|
|
/// Nest a new operation pass manager for the given operation kind under this
|
|
|
|
/// pass manager.
|
|
|
|
OpPassManager &nest(const OperationName &nestedName);
|
|
|
|
OpPassManager &nest(StringRef nestedName) {
|
|
|
|
return nest(OperationName(nestedName, getContext()));
|
2019-09-03 10:24:47 +08:00
|
|
|
}
|
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
/// 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);
|
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
/// Coalesce adjacent AdaptorPasses into one large adaptor. This runs
|
|
|
|
/// recursively through the pipeline graph.
|
|
|
|
void coalesceAdjacentAdaptorPasses();
|
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
/// 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();
|
|
|
|
}
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
/// The name of the operation that passes of this pass manager operate on.
|
|
|
|
OperationName name;
|
|
|
|
|
|
|
|
/// Flag that specifies if the IR should be verified after each pass has run.
|
|
|
|
bool verifyPasses : 1;
|
|
|
|
|
|
|
|
/// The set of passes to run as part of this pass manager.
|
|
|
|
std::vector<std::unique_ptr<Pass>> passes;
|
|
|
|
};
|
|
|
|
} // end namespace detail
|
|
|
|
} // end namespace mlir
|
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
void OpPassManagerImpl::mergeInto(OpPassManagerImpl &rhs) {
|
|
|
|
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) {
|
2020-05-03 03:28:57 +08:00
|
|
|
OpPassManager nested(nestedName, verifyPasses);
|
2020-04-30 06:08:15 +08:00
|
|
|
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>());
|
|
|
|
}
|
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
void OpPassManagerImpl::coalesceAdjacentAdaptorPasses() {
|
|
|
|
// Bail out early if there are no adaptor passes.
|
|
|
|
if (llvm::none_of(passes, [](std::unique_ptr<Pass> &pass) {
|
2020-04-30 06:08:05 +08:00
|
|
|
return isa<OpToOpPassAdaptor>(pass.get());
|
2019-09-10 00:51:59 +08:00
|
|
|
}))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Walk the pass list and merge adjacent adaptors.
|
2020-04-30 06:08:05 +08:00
|
|
|
OpToOpPassAdaptor *lastAdaptor = nullptr;
|
2019-09-10 00:51:59 +08:00
|
|
|
for (auto it = passes.begin(), e = passes.end(); it != e; ++it) {
|
|
|
|
// Check to see if this pass is an adaptor.
|
2020-04-30 06:08:05 +08:00
|
|
|
if (auto *currentAdaptor = dyn_cast<OpToOpPassAdaptor>(it->get())) {
|
2019-09-10 00:51:59 +08:00
|
|
|
// If it is the first adaptor in a possible chain, remember it and
|
|
|
|
// continue.
|
|
|
|
if (!lastAdaptor) {
|
|
|
|
lastAdaptor = currentAdaptor;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, merge into the existing adaptor and delete the current one.
|
|
|
|
currentAdaptor->mergeInto(*lastAdaptor);
|
|
|
|
it->reset();
|
|
|
|
|
|
|
|
// If the verifier is enabled, then next pass is a verifier run so
|
|
|
|
// drop it. Verifier passes are inserted after every pass, so this one
|
|
|
|
// would be a duplicate.
|
|
|
|
if (verifyPasses) {
|
|
|
|
assert(std::next(it) != e && isa<VerifierPass>(*std::next(it)));
|
|
|
|
(++it)->reset();
|
|
|
|
}
|
|
|
|
} else if (lastAdaptor && !isa<VerifierPass>(*it)) {
|
|
|
|
// If this pass is not an adaptor and not a verifier pass, then coalesce
|
|
|
|
// and forget any existing adaptor.
|
|
|
|
for (auto &pm : lastAdaptor->getPassManagers())
|
|
|
|
pm.getImpl().coalesceAdjacentAdaptorPasses();
|
|
|
|
lastAdaptor = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there was an adaptor at the end of the manager, coalesce it as well.
|
|
|
|
if (lastAdaptor) {
|
|
|
|
for (auto &pm : lastAdaptor->getPassManagers())
|
|
|
|
pm.getImpl().coalesceAdjacentAdaptorPasses();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that the adaptors have been merged, erase the empty slot corresponding
|
|
|
|
// to the merged adaptors that were nulled-out in the loop above.
|
|
|
|
llvm::erase_if(passes, std::logical_not<std::unique_ptr<Pass>>());
|
|
|
|
}
|
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OpPassManager
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-05-03 03:28:57 +08:00
|
|
|
OpPassManager::OpPassManager(OperationName name, bool verifyPasses)
|
|
|
|
: impl(new OpPassManagerImpl(name, verifyPasses)) {
|
2019-09-03 10:24:47 +08:00
|
|
|
assert(name.getAbstractOperation() &&
|
|
|
|
"OpPassManager can only operate on registered operations");
|
|
|
|
assert(name.getAbstractOperation()->hasProperty(
|
|
|
|
OperationProperty::IsolatedFromAbove) &&
|
|
|
|
"OpPassManager only supports operating on operations marked as "
|
|
|
|
"'IsolatedFromAbove'");
|
2019-02-28 02:57:59 +08:00
|
|
|
}
|
2019-09-10 04:43:51 +08:00
|
|
|
OpPassManager::OpPassManager(OpPassManager &&rhs) : impl(std::move(rhs.impl)) {}
|
2019-09-10 00:51:59 +08:00
|
|
|
OpPassManager::OpPassManager(const OpPassManager &rhs) { *this = rhs; }
|
|
|
|
OpPassManager &OpPassManager::operator=(const OpPassManager &rhs) {
|
2020-05-03 03:28:57 +08:00
|
|
|
impl.reset(new OpPassManagerImpl(rhs.impl->name, rhs.impl->verifyPasses));
|
2019-09-03 10:24:47 +08:00
|
|
|
for (auto &pass : rhs.impl->passes)
|
|
|
|
impl->passes.emplace_back(pass->clone());
|
2019-09-10 00:51:59 +08:00
|
|
|
return *this;
|
2019-09-03 10:24:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
OpPassManager::~OpPassManager() {}
|
2019-02-28 02:57:59 +08:00
|
|
|
|
2019-12-06 03:52:58 +08:00
|
|
|
OpPassManager::pass_iterator OpPassManager::begin() {
|
2020-08-27 12:42:38 +08:00
|
|
|
return MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin();
|
|
|
|
}
|
|
|
|
OpPassManager::pass_iterator OpPassManager::end() {
|
|
|
|
return MutableArrayRef<std::unique_ptr<Pass>>{impl->passes}.end();
|
2019-12-06 03:52:58 +08:00
|
|
|
}
|
|
|
|
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
OpPassManager::const_pass_iterator OpPassManager::begin() const {
|
2020-08-27 12:42:38 +08:00
|
|
|
return ArrayRef<std::unique_ptr<Pass>>{impl->passes}.begin();
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
}
|
|
|
|
OpPassManager::const_pass_iterator OpPassManager::end() const {
|
2020-08-27 12:42:38 +08:00
|
|
|
return ArrayRef<std::unique_ptr<Pass>>{impl->passes}.end();
|
2019-02-28 02:57:59 +08:00
|
|
|
}
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
/// Nest a new operation pass manager for the given operation kind under this
|
|
|
|
/// pass manager.
|
|
|
|
OpPassManager &OpPassManager::nest(const OperationName &nestedName) {
|
2020-04-30 06:08:15 +08:00
|
|
|
return impl->nest(nestedName);
|
2019-09-03 10:24:47 +08:00
|
|
|
}
|
|
|
|
OpPassManager &OpPassManager::nest(StringRef nestedName) {
|
2020-04-30 06:08:15 +08:00
|
|
|
return impl->nest(nestedName);
|
2019-09-03 10:24:47 +08:00
|
|
|
}
|
|
|
|
|
2019-09-15 08:37:03 +08:00
|
|
|
/// 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.
|
2019-09-03 10:24:47 +08:00
|
|
|
void OpPassManager::addPass(std::unique_ptr<Pass> pass) {
|
2020-04-30 06:08:15 +08:00
|
|
|
impl->addPass(std::move(pass));
|
2019-09-03 10:24:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the number of passes held by this manager.
|
|
|
|
size_t OpPassManager::size() const { return impl->passes.size(); }
|
|
|
|
|
|
|
|
/// Returns the internal implementation instance.
|
|
|
|
OpPassManagerImpl &OpPassManager::getImpl() { return *impl; }
|
|
|
|
|
|
|
|
/// Return an instance of the context.
|
2020-04-30 06:08:15 +08:00
|
|
|
MLIRContext *OpPassManager::getContext() const { return impl->getContext(); }
|
2019-09-03 10:24:47 +08:00
|
|
|
|
|
|
|
/// Return the operation name that this pass manager operates on.
|
|
|
|
const OperationName &OpPassManager::getOpName() const { return impl->name; }
|
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
/// Prints out the given passes as the textual representation of a pipeline.
|
|
|
|
static void printAsTextualPipeline(ArrayRef<std::unique_ptr<Pass>> passes,
|
|
|
|
raw_ostream &os) {
|
2019-10-11 10:19:11 +08:00
|
|
|
// Filter out passes that are not part of the public pipeline.
|
2020-04-30 06:08:15 +08:00
|
|
|
auto filteredPasses =
|
|
|
|
llvm::make_filter_range(passes, [](const std::unique_ptr<Pass> &pass) {
|
2019-10-11 10:19:11 +08:00
|
|
|
return !isa<VerifierPass>(pass);
|
|
|
|
});
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(filteredPasses, os,
|
|
|
|
[&](const std::unique_ptr<Pass> &pass) {
|
|
|
|
pass->printAsTextualPipeline(os);
|
|
|
|
});
|
2019-10-10 04:48:38 +08:00
|
|
|
}
|
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
/// Prints out the passes of the pass manager as the textual representation
|
|
|
|
/// of pipelines.
|
|
|
|
void OpPassManager::printAsTextualPipeline(raw_ostream &os) {
|
|
|
|
::printAsTextualPipeline(impl->passes, os);
|
|
|
|
}
|
|
|
|
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
static void registerDialectsForPipeline(const OpPassManager &pm,
|
|
|
|
DialectRegistry &dialects) {
|
|
|
|
for (const Pass &pass : pm.getPasses())
|
|
|
|
pass.getDependentDialects(dialects);
|
|
|
|
}
|
|
|
|
|
|
|
|
void OpPassManager::getDependentDialects(DialectRegistry &dialects) const {
|
|
|
|
registerDialectsForPipeline(*this, dialects);
|
|
|
|
}
|
|
|
|
|
2019-02-28 09:49:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-09-03 10:24:47 +08:00
|
|
|
// OpToOpPassAdaptor
|
2019-02-28 09:49:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-08-27 12:42:38 +08:00
|
|
|
LogicalResult OpToOpPassAdaptor::run(Pass *pass, Operation *op,
|
|
|
|
AnalysisManager am) {
|
|
|
|
pass->passState.emplace(op, am);
|
|
|
|
|
|
|
|
// Instrument before the pass has run.
|
|
|
|
PassInstrumentor *pi = am.getPassInstrumentor();
|
|
|
|
if (pi)
|
|
|
|
pi->runBeforePass(pass, op);
|
|
|
|
|
|
|
|
// Invoke the virtual runOnOperation method.
|
|
|
|
pass->runOnOperation();
|
|
|
|
|
|
|
|
// Invalidate any non preserved analyses.
|
|
|
|
am.invalidate(pass->passState->preservedAnalyses);
|
|
|
|
|
|
|
|
// Instrument after the pass has run.
|
|
|
|
bool passFailed = pass->passState->irAndPassFailed.getInt();
|
|
|
|
if (pi) {
|
|
|
|
if (passFailed)
|
|
|
|
pi->runAfterPassFailed(pass, op);
|
|
|
|
else
|
|
|
|
pi->runAfterPass(pass, op);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return if the pass signaled a failure.
|
|
|
|
return failure(passFailed);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Run the given operation and analysis manager on a provided op pass manager.
|
|
|
|
LogicalResult OpToOpPassAdaptor::runPipeline(
|
|
|
|
iterator_range<OpPassManager::pass_iterator> passes, Operation *op,
|
|
|
|
AnalysisManager am) {
|
|
|
|
auto scope_exit = llvm::make_scope_exit([&] {
|
|
|
|
// Clear out any computed operation analyses. These analyses won't be used
|
|
|
|
// any more in this pipeline, and this helps reduce the current working set
|
|
|
|
// of memory. If preserving these analyses becomes important in the future
|
|
|
|
// we can re-evaluate this.
|
|
|
|
am.clear();
|
|
|
|
});
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
// Run the pipeline over the provided operation.
|
2020-08-27 12:42:38 +08:00
|
|
|
for (Pass &pass : passes)
|
|
|
|
if (failed(run(&pass, op, am)))
|
|
|
|
return failure();
|
2019-03-27 12:15:54 +08:00
|
|
|
|
2020-08-27 12:42:38 +08:00
|
|
|
return success();
|
2019-03-27 12:15:54 +08:00
|
|
|
}
|
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
/// Find an operation pass manager that can operate on an operation of the given
|
|
|
|
/// type, or nullptr if one does not exist.
|
|
|
|
static OpPassManager *findPassManagerFor(MutableArrayRef<OpPassManager> mgrs,
|
|
|
|
const OperationName &name) {
|
|
|
|
auto it = llvm::find_if(
|
|
|
|
mgrs, [&](OpPassManager &mgr) { return mgr.getOpName() == name; });
|
|
|
|
return it == mgrs.end() ? nullptr : &*it;
|
|
|
|
}
|
|
|
|
|
2020-04-30 06:08:05 +08:00
|
|
|
OpToOpPassAdaptor::OpToOpPassAdaptor(OpPassManager &&mgr) {
|
2019-09-10 00:51:59 +08:00
|
|
|
mgrs.emplace_back(std::move(mgr));
|
|
|
|
}
|
|
|
|
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
void OpToOpPassAdaptor::getDependentDialects(DialectRegistry &dialects) const {
|
|
|
|
for (auto &pm : mgrs)
|
|
|
|
pm.getDependentDialects(dialects);
|
|
|
|
}
|
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
/// Merge the current pass adaptor into given 'rhs'.
|
2020-04-30 06:08:05 +08:00
|
|
|
void OpToOpPassAdaptor::mergeInto(OpToOpPassAdaptor &rhs) {
|
2019-09-10 00:51:59 +08:00
|
|
|
for (auto &pm : mgrs) {
|
|
|
|
// If an existing pass manager exists, then merge the given pass manager
|
|
|
|
// into it.
|
|
|
|
if (auto *existingPM = findPassManagerFor(rhs.mgrs, pm.getOpName())) {
|
|
|
|
pm.getImpl().mergeInto(existingPM->getImpl());
|
|
|
|
} else {
|
|
|
|
// Otherwise, add the given pass manager to the list.
|
|
|
|
rhs.mgrs.emplace_back(std::move(pm));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mgrs.clear();
|
|
|
|
|
|
|
|
// After coalescing, sort the pass managers within rhs by name.
|
|
|
|
llvm::array_pod_sort(rhs.mgrs.begin(), rhs.mgrs.end(),
|
|
|
|
[](const OpPassManager *lhs, const OpPassManager *rhs) {
|
|
|
|
return lhs->getOpName().getStringRef().compare(
|
|
|
|
rhs->getOpName().getStringRef());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-12-06 03:52:58 +08:00
|
|
|
/// Returns the adaptor pass name.
|
2020-04-30 06:08:05 +08:00
|
|
|
std::string OpToOpPassAdaptor::getAdaptorName() {
|
2019-12-06 03:52:58 +08:00
|
|
|
std::string name = "Pipeline Collection : [";
|
|
|
|
llvm::raw_string_ostream os(name);
|
2020-04-15 05:53:28 +08:00
|
|
|
llvm::interleaveComma(getPassManagers(), os, [&](OpPassManager &pm) {
|
2019-12-06 03:52:58 +08:00
|
|
|
os << '\'' << pm.getOpName() << '\'';
|
|
|
|
});
|
|
|
|
os << ']';
|
|
|
|
return os.str();
|
|
|
|
}
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
/// Run the held pipeline over all nested operations.
|
|
|
|
void OpToOpPassAdaptor::runOnOperation() {
|
2020-05-03 03:28:57 +08:00
|
|
|
if (getContext().isMultithreadingEnabled())
|
2020-04-30 06:08:05 +08:00
|
|
|
runOnOperationAsyncImpl();
|
2020-05-03 03:28:57 +08:00
|
|
|
else
|
|
|
|
runOnOperationImpl();
|
2020-04-30 06:08:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Run this pass adaptor synchronously.
|
|
|
|
void OpToOpPassAdaptor::runOnOperationImpl() {
|
2019-09-03 10:24:47 +08:00
|
|
|
auto am = getAnalysisManager();
|
2019-10-01 08:44:31 +08:00
|
|
|
PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(),
|
|
|
|
this};
|
2019-09-09 10:57:25 +08:00
|
|
|
auto *instrumentor = am.getPassInstrumentor();
|
2019-09-03 10:24:47 +08:00
|
|
|
for (auto ®ion : getOperation()->getRegions()) {
|
|
|
|
for (auto &block : region) {
|
|
|
|
for (auto &op : block) {
|
2019-09-10 00:51:59 +08:00
|
|
|
auto *mgr = findPassManagerFor(mgrs, op.getName());
|
|
|
|
if (!mgr)
|
2019-09-09 10:57:25 +08:00
|
|
|
continue;
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
// Run the held pipeline over the current operation.
|
2019-09-09 10:57:25 +08:00
|
|
|
if (instrumentor)
|
2019-10-01 08:44:31 +08:00
|
|
|
instrumentor->runBeforePipeline(mgr->getOpName(), parentInfo);
|
2020-08-27 12:42:38 +08:00
|
|
|
auto result = runPipeline(mgr->getPasses(), &op, am.slice(&op));
|
2019-09-09 10:57:25 +08:00
|
|
|
if (instrumentor)
|
2019-10-01 08:44:31 +08:00
|
|
|
instrumentor->runAfterPipeline(mgr->getOpName(), parentInfo);
|
2019-09-09 10:57:25 +08:00
|
|
|
|
|
|
|
if (failed(result))
|
2019-09-03 10:24:47 +08:00
|
|
|
return signalPassFailure();
|
|
|
|
}
|
|
|
|
}
|
2019-02-28 02:57:59 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-28 09:49:51 +08:00
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
/// Utility functor that checks if the two ranges of pass managers have a size
|
|
|
|
/// mismatch.
|
|
|
|
static bool hasSizeMismatch(ArrayRef<OpPassManager> lhs,
|
|
|
|
ArrayRef<OpPassManager> rhs) {
|
|
|
|
return lhs.size() != rhs.size() ||
|
|
|
|
llvm::any_of(llvm::seq<size_t>(0, lhs.size()),
|
|
|
|
[&](size_t i) { return lhs[i].size() != rhs[i].size(); });
|
|
|
|
}
|
2019-09-03 10:24:47 +08:00
|
|
|
|
2020-04-30 06:08:05 +08:00
|
|
|
/// Run this pass adaptor synchronously.
|
|
|
|
void OpToOpPassAdaptor::runOnOperationAsyncImpl() {
|
2019-08-29 06:10:37 +08:00
|
|
|
AnalysisManager am = getAnalysisManager();
|
2019-03-27 12:15:54 +08:00
|
|
|
|
|
|
|
// Create the async executors if they haven't been created, or if the main
|
2019-09-03 10:24:47 +08:00
|
|
|
// pipeline has changed.
|
2019-09-10 00:51:59 +08:00
|
|
|
if (asyncExecutors.empty() || hasSizeMismatch(asyncExecutors.front(), mgrs))
|
[Support] On Windows, ensure hardware_concurrency() extends to all CPU sockets and all NUMA groups
The goal of this patch is to maximize CPU utilization on multi-socket or high core count systems, so that parallel computations such as LLD/ThinLTO can use all hardware threads in the system. Before this patch, on Windows, a maximum of 64 hardware threads could be used at most, in some cases dispatched only on one CPU socket.
== Background ==
Windows doesn't have a flat cpu_set_t like Linux. Instead, it projects hardware CPUs (or NUMA nodes) to applications through a concept of "processor groups". A "processor" is the smallest unit of execution on a CPU, that is, an hyper-thread if SMT is active; a core otherwise. There's a limit of 32-bit processors on older 32-bit versions of Windows, which later was raised to 64-processors with 64-bit versions of Windows. This limit comes from the affinity mask, which historically is represented by the sizeof(void*). Consequently, the concept of "processor groups" was introduced for dealing with systems with more than 64 hyper-threads.
By default, the Windows OS assigns only one "processor group" to each starting application, in a round-robin manner. If the application wants to use more processors, it needs to programmatically enable it, by assigning threads to other "processor groups". This also means that affinity cannot cross "processor group" boundaries; one can only specify a "preferred" group on start-up, but the application is free to allocate more groups if it wants to.
This creates a peculiar situation, where newer CPUs like the AMD EPYC 7702P (64-cores, 128-hyperthreads) are projected by the OS as two (2) "processor groups". This means that by default, an application can only use half of the cores. This situation could only get worse in the years to come, as dies with more cores will appear on the market.
== The problem ==
The heavyweight_hardware_concurrency() API was introduced so that only *one hardware thread per core* was used. Once that API returns, that original intention is lost, only the number of threads is retained. Consider a situation, on Windows, where the system has 2 CPU sockets, 18 cores each, each core having 2 hyper-threads, for a total of 72 hyper-threads. Both heavyweight_hardware_concurrency() and hardware_concurrency() currently return 36, because on Windows they are simply wrappers over std::thread::hardware_concurrency() -- which can only return processors from the current "processor group".
== The changes in this patch ==
To solve this situation, we capture (and retain) the initial intention until the point of usage, through a new ThreadPoolStrategy class. The number of threads to use is deferred as late as possible, until the moment where the std::threads are created (ThreadPool in the case of ThinLTO).
When using hardware_concurrency(), setting ThreadCount to 0 now means to use all the possible hardware CPU (SMT) threads. Providing a ThreadCount above to the maximum number of threads will have no effect, the maximum will be used instead.
The heavyweight_hardware_concurrency() is similar to hardware_concurrency(), except that only one thread per hardware *core* will be used.
When LLVM_ENABLE_THREADS is OFF, the threading APIs will always return 1, to ensure any caller loops will be exercised at least once.
Differential Revision: https://reviews.llvm.org/D71775
2020-02-14 11:49:57 +08:00
|
|
|
asyncExecutors.assign(llvm::hardware_concurrency().compute_thread_count(),
|
|
|
|
mgrs);
|
2019-09-03 10:24:47 +08:00
|
|
|
|
|
|
|
// Run a prepass over the module to collect the operations to execute over.
|
|
|
|
// This ensures that an analysis manager exists for each operation, as well as
|
|
|
|
// providing a queue of operations to execute over.
|
|
|
|
std::vector<std::pair<Operation *, AnalysisManager>> opAMPairs;
|
|
|
|
for (auto ®ion : getOperation()->getRegions()) {
|
|
|
|
for (auto &block : region) {
|
|
|
|
for (auto &op : block) {
|
2019-09-10 00:51:59 +08:00
|
|
|
// Add this operation iff the name matches the any of the pass managers.
|
|
|
|
if (findPassManagerFor(mgrs, op.getName()))
|
2019-09-03 10:24:47 +08:00
|
|
|
opAMPairs.emplace_back(&op, am.slice(&op));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-27 12:15:54 +08:00
|
|
|
|
2019-03-29 02:40:17 +08:00
|
|
|
// A parallel diagnostic handler that provides deterministic diagnostic
|
|
|
|
// ordering.
|
2019-05-22 05:29:20 +08:00
|
|
|
ParallelDiagnosticHandler diagHandler(&getContext());
|
2019-03-29 23:52:06 +08:00
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
// An index for the current operation/analysis manager pair.
|
|
|
|
std::atomic<unsigned> opIt(0);
|
2019-03-27 12:15:54 +08:00
|
|
|
|
2019-09-09 10:57:25 +08:00
|
|
|
// Get the current thread for this adaptor.
|
2019-10-01 08:44:31 +08:00
|
|
|
PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(),
|
|
|
|
this};
|
2019-09-09 10:57:25 +08:00
|
|
|
auto *instrumentor = am.getPassInstrumentor();
|
|
|
|
|
2019-03-27 12:15:54 +08:00
|
|
|
// An atomic failure variable for the async executors.
|
2019-03-28 03:36:57 +08:00
|
|
|
std::atomic<bool> passFailed(false);
|
[Support] Move LLD's parallel algorithm wrappers to support
Essentially takes the lld/Common/Threads.h wrappers and moves them to
the llvm/Support/Paralle.h algorithm header.
The changes are:
- Remove policy parameter, since all clients use `par`.
- Rename the methods to `parallelSort` etc to match LLVM style, since
they are no longer C++17 pstl compatible.
- Move algorithms from llvm::parallel:: to llvm::, since they have
"parallel" in the name and are no longer overloads of the regular
algorithms.
- Add range overloads
- Use the sequential algorithm directly when 1 thread is requested
(skips task grouping)
- Fix the index type of parallelForEachN to size_t. Nobody in LLVM was
using any other parameter, and it made overload resolution hard for
for_each_n(par, 0, foo.size(), ...) because 0 is int, not size_t.
Remove Threads.h and update LLD for that.
This is a prerequisite for parallel public symbol processing in the PDB
library, which is in LLVM.
Reviewed By: MaskRay, aganea
Differential Revision: https://reviews.llvm.org/D79390
2020-05-05 11:03:19 +08:00
|
|
|
llvm::parallelForEach(
|
|
|
|
asyncExecutors.begin(),
|
2019-03-27 12:15:54 +08:00
|
|
|
std::next(asyncExecutors.begin(),
|
2019-09-03 10:24:47 +08:00
|
|
|
std::min(asyncExecutors.size(), opAMPairs.size())),
|
2019-09-10 00:51:59 +08:00
|
|
|
[&](MutableArrayRef<OpPassManager> pms) {
|
2019-09-03 10:24:47 +08:00
|
|
|
for (auto e = opAMPairs.size(); !passFailed && opIt < e;) {
|
|
|
|
// Get the next available operation index.
|
|
|
|
unsigned nextID = opIt++;
|
2019-03-27 12:15:54 +08:00
|
|
|
if (nextID >= e)
|
|
|
|
break;
|
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
// Set the order id for this thread in the diagnostic handler.
|
2019-05-22 05:29:20 +08:00
|
|
|
diagHandler.setOrderIDForThread(nextID);
|
2019-03-29 02:40:17 +08:00
|
|
|
|
2019-09-10 00:51:59 +08:00
|
|
|
// Get the pass manager for this operation and execute it.
|
2019-09-03 10:24:47 +08:00
|
|
|
auto &it = opAMPairs[nextID];
|
2019-09-10 00:51:59 +08:00
|
|
|
auto *pm = findPassManagerFor(pms, it.first->getName());
|
|
|
|
assert(pm && "expected valid pass manager for operation");
|
2019-09-09 10:57:25 +08:00
|
|
|
|
|
|
|
if (instrumentor)
|
2019-10-01 08:44:31 +08:00
|
|
|
instrumentor->runBeforePipeline(pm->getOpName(), parentInfo);
|
2020-08-27 12:42:38 +08:00
|
|
|
auto pipelineResult =
|
|
|
|
runPipeline(pm->getPasses(), it.first, it.second);
|
2019-09-09 10:57:25 +08:00
|
|
|
if (instrumentor)
|
2019-10-01 08:44:31 +08:00
|
|
|
instrumentor->runAfterPipeline(pm->getOpName(), parentInfo);
|
2019-09-09 10:57:25 +08:00
|
|
|
|
2019-09-14 04:18:44 +08:00
|
|
|
// Drop this thread from being tracked by the diagnostic handler.
|
|
|
|
// After this task has finished, the thread may be used outside of
|
|
|
|
// this pass manager context meaning that we don't want to track
|
|
|
|
// diagnostics from it anymore.
|
|
|
|
diagHandler.eraseOrderIDForThread();
|
|
|
|
|
2019-09-09 10:57:25 +08:00
|
|
|
// Handle a failed pipeline result.
|
|
|
|
if (failed(pipelineResult)) {
|
2019-03-27 12:15:54 +08:00
|
|
|
passFailed = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Signal a failure if any of the executors failed.
|
|
|
|
if (passFailed)
|
|
|
|
signalPassFailure();
|
|
|
|
}
|
|
|
|
|
2019-10-11 10:19:11 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PassCrashReproducer
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-04-30 06:08:25 +08:00
|
|
|
namespace {
|
|
|
|
/// This class contains all of the context for generating a recovery reproducer.
|
|
|
|
/// Each recovery context is registered globally to allow for generating
|
|
|
|
/// reproducers when a signal is raised, such as a segfault.
|
|
|
|
struct RecoveryReproducerContext {
|
|
|
|
RecoveryReproducerContext(MutableArrayRef<std::unique_ptr<Pass>> passes,
|
|
|
|
ModuleOp module, StringRef filename,
|
|
|
|
bool disableThreads, bool verifyPasses);
|
|
|
|
~RecoveryReproducerContext();
|
|
|
|
|
|
|
|
/// Generate a reproducer with the current context.
|
|
|
|
LogicalResult generate(std::string &error);
|
|
|
|
|
|
|
|
private:
|
|
|
|
/// This function is invoked in the event of a crash.
|
|
|
|
static void crashHandler(void *);
|
|
|
|
|
|
|
|
/// Register a signal handler to run in the event of a crash.
|
|
|
|
static void registerSignalHandler();
|
|
|
|
|
|
|
|
/// The textual description of the currently executing pipeline.
|
|
|
|
std::string pipeline;
|
|
|
|
|
|
|
|
/// The MLIR module representing the IR before the crash.
|
|
|
|
OwningModuleRef module;
|
|
|
|
|
|
|
|
/// The filename to use when generating the reproducer.
|
|
|
|
StringRef filename;
|
|
|
|
|
2020-05-03 03:28:57 +08:00
|
|
|
/// Various pass manager and context flags.
|
2020-04-30 06:08:25 +08:00
|
|
|
bool disableThreads;
|
|
|
|
bool verifyPasses;
|
|
|
|
|
|
|
|
/// The current set of active reproducer contexts. This is used in the event
|
|
|
|
/// of a crash. This is not thread_local as the pass manager may produce any
|
|
|
|
/// number of child threads. This uses a set to allow for multiple MLIR pass
|
|
|
|
/// managers to be running at the same time.
|
|
|
|
static llvm::ManagedStatic<llvm::sys::SmartMutex<true>> reproducerMutex;
|
|
|
|
static llvm::ManagedStatic<
|
|
|
|
llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
|
|
|
|
reproducerSet;
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
llvm::ManagedStatic<llvm::sys::SmartMutex<true>>
|
|
|
|
RecoveryReproducerContext::reproducerMutex;
|
|
|
|
llvm::ManagedStatic<llvm::SmallSetVector<RecoveryReproducerContext *, 1>>
|
|
|
|
RecoveryReproducerContext::reproducerSet;
|
|
|
|
|
|
|
|
RecoveryReproducerContext::RecoveryReproducerContext(
|
|
|
|
MutableArrayRef<std::unique_ptr<Pass>> passes, ModuleOp module,
|
|
|
|
StringRef filename, bool disableThreads, bool verifyPasses)
|
|
|
|
: module(module.clone()), filename(filename),
|
|
|
|
disableThreads(disableThreads), verifyPasses(verifyPasses) {
|
|
|
|
// Grab the textual pipeline being executed..
|
|
|
|
{
|
|
|
|
llvm::raw_string_ostream pipelineOS(pipeline);
|
|
|
|
::printAsTextualPipeline(passes, pipelineOS);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that the handler is registered, and update the current context.
|
|
|
|
llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex);
|
|
|
|
registerSignalHandler();
|
|
|
|
reproducerSet->insert(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
RecoveryReproducerContext::~RecoveryReproducerContext() {
|
|
|
|
llvm::sys::SmartScopedLock<true> producerLock(*reproducerMutex);
|
|
|
|
reproducerSet->remove(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
LogicalResult RecoveryReproducerContext::generate(std::string &error) {
|
|
|
|
std::unique_ptr<llvm::ToolOutputFile> outputFile =
|
|
|
|
mlir::openOutputFile(filename, &error);
|
|
|
|
if (!outputFile)
|
|
|
|
return failure();
|
|
|
|
auto &outputOS = outputFile->os();
|
|
|
|
|
|
|
|
// Output the current pass manager configuration.
|
|
|
|
outputOS << "// configuration: -pass-pipeline='" << pipeline << "'";
|
|
|
|
if (disableThreads)
|
2020-05-03 03:28:57 +08:00
|
|
|
outputOS << " -mlir-disable-threading";
|
2020-04-30 06:08:25 +08:00
|
|
|
|
|
|
|
// TODO: Should this also be configured with a pass manager flag?
|
|
|
|
outputOS << "\n// note: verifyPasses=" << (verifyPasses ? "true" : "false")
|
|
|
|
<< "\n";
|
|
|
|
|
|
|
|
// Output the .mlir module.
|
|
|
|
module->print(outputOS);
|
|
|
|
outputFile->keep();
|
|
|
|
return success();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RecoveryReproducerContext::crashHandler(void *) {
|
|
|
|
// Walk the current stack of contexts and generate a reproducer for each one.
|
|
|
|
// We can't know for certain which one was the cause, so we need to generate
|
|
|
|
// a reproducer for all of them.
|
|
|
|
std::string ignored;
|
|
|
|
for (RecoveryReproducerContext *context : *reproducerSet)
|
|
|
|
context->generate(ignored);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RecoveryReproducerContext::registerSignalHandler() {
|
|
|
|
// Ensure that the handler is only registered once.
|
|
|
|
static bool registered =
|
|
|
|
(llvm::sys::AddSignalHandler(crashHandler, nullptr), false);
|
|
|
|
(void)registered;
|
|
|
|
}
|
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
/// Run the pass manager with crash recover enabled.
|
|
|
|
LogicalResult PassManager::runWithCrashRecovery(ModuleOp module,
|
|
|
|
AnalysisManager am) {
|
|
|
|
// If this isn't a local producer, run all of the passes in recovery mode.
|
|
|
|
if (!localReproducer)
|
|
|
|
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) {
|
2020-04-30 06:08:25 +08:00
|
|
|
RecoveryReproducerContext context(passes, module, *crashReproducerFileName,
|
2020-05-03 03:28:57 +08:00
|
|
|
!getContext()->isMultithreadingEnabled(),
|
|
|
|
impl->verifyPasses);
|
2019-10-11 10:19:11 +08:00
|
|
|
|
2020-04-30 06:08:15 +08:00
|
|
|
// Safely invoke the passes within a recovery context.
|
2020-04-30 06:08:25 +08:00
|
|
|
llvm::CrashRecoveryContext::Enable();
|
2019-10-11 10:19:11 +08:00
|
|
|
LogicalResult passManagerResult = failure();
|
|
|
|
llvm::CrashRecoveryContext recoveryContext;
|
2020-04-30 06:08:15 +08:00
|
|
|
recoveryContext.RunSafelyOnThread([&] {
|
|
|
|
for (std::unique_ptr<Pass> &pass : passes)
|
2020-08-27 12:42:38 +08:00
|
|
|
if (failed(OpToOpPassAdaptor::run(pass.get(), module, am)))
|
2020-04-30 06:08:15 +08:00
|
|
|
return;
|
|
|
|
passManagerResult = success();
|
|
|
|
});
|
2019-10-11 10:19:11 +08:00
|
|
|
llvm::CrashRecoveryContext::Disable();
|
|
|
|
if (succeeded(passManagerResult))
|
|
|
|
return success();
|
|
|
|
|
|
|
|
std::string error;
|
2020-04-30 06:08:25 +08:00
|
|
|
if (failed(context.generate(error)))
|
2020-04-30 06:08:15 +08:00
|
|
|
return module.emitError("<MLIR-PassManager-Crash-Reproducer>: ") << error;
|
2020-04-30 06:08:25 +08:00
|
|
|
return module.emitError()
|
2019-12-04 06:00:36 +08:00
|
|
|
<< "A failure has been detected while processing the MLIR module, a "
|
2019-10-11 10:19:11 +08:00
|
|
|
"reproducer has been generated in '"
|
2020-04-30 06:08:15 +08:00
|
|
|
<< *crashReproducerFileName << "'";
|
2019-10-11 10:19:11 +08:00
|
|
|
}
|
|
|
|
|
2019-07-13 02:20:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PassManager
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-03-05 04:33:13 +08:00
|
|
|
|
2019-09-03 10:24:47 +08:00
|
|
|
PassManager::PassManager(MLIRContext *ctx, bool verifyPasses)
|
2019-09-14 03:09:50 +08:00
|
|
|
: OpPassManager(OperationName(ModuleOp::getOperationName(), ctx),
|
2020-05-03 03:28:57 +08:00
|
|
|
verifyPasses),
|
2020-04-30 06:08:15 +08:00
|
|
|
passTiming(false), localReproducer(false) {}
|
2019-02-28 09:49:51 +08:00
|
|
|
|
|
|
|
PassManager::~PassManager() {}
|
|
|
|
|
2019-03-13 04:08:48 +08:00
|
|
|
/// Run the passes within this manager on the provided module.
|
2019-07-11 01:07:49 +08:00
|
|
|
LogicalResult PassManager::run(ModuleOp module) {
|
2019-09-10 00:51:59 +08:00
|
|
|
// Before running, make sure to coalesce any adjacent pass adaptors in the
|
|
|
|
// pipeline.
|
2019-09-14 03:09:50 +08:00
|
|
|
getImpl().coalesceAdjacentAdaptorPasses();
|
2019-09-10 00:51:59 +08:00
|
|
|
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-19 04:01:19 +08:00
|
|
|
// Register all dialects for the current pipeline.
|
|
|
|
DialectRegistry dependentDialects;
|
|
|
|
getDependentDialects(dependentDialects);
|
|
|
|
dependentDialects.loadAll(module.getContext());
|
|
|
|
|
2019-10-11 10:19:11 +08:00
|
|
|
// Construct an analysis manager for the pipeline.
|
2019-08-29 06:10:37 +08:00
|
|
|
ModuleAnalysisManager am(module, instrumentor.get());
|
2019-10-11 10:19:11 +08:00
|
|
|
|
2020-08-24 13:03:59 +08:00
|
|
|
// Notify the context that we start running a pipeline for book keeping.
|
|
|
|
module.getContext()->enterMultiThreadedExecution();
|
|
|
|
|
2019-10-11 10:19:11 +08:00
|
|
|
// If reproducer generation is enabled, run the pass manager with crash
|
|
|
|
// handling enabled.
|
2020-08-27 12:42:38 +08:00
|
|
|
LogicalResult result =
|
|
|
|
crashReproducerFileName
|
|
|
|
? runWithCrashRecovery(module, am)
|
|
|
|
: OpToOpPassAdaptor::runPipeline(getPasses(), module, am);
|
2019-12-06 03:52:58 +08:00
|
|
|
|
2020-08-24 13:03:59 +08:00
|
|
|
// Notify the context that the run is done.
|
|
|
|
module.getContext()->exitMultiThreadedExecution();
|
|
|
|
|
2019-12-06 03:52:58 +08:00
|
|
|
// Dump all of the pass statistics if necessary.
|
|
|
|
if (passStatisticsMode)
|
|
|
|
dumpStatistics();
|
|
|
|
return result;
|
2019-03-13 04:08:48 +08:00
|
|
|
}
|
|
|
|
|
2019-10-11 10:19:11 +08:00
|
|
|
/// 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
|
2020-04-30 06:08:15 +08:00
|
|
|
/// the generated reproducer. If `genLocalReproducer` is true, the pass manager
|
|
|
|
/// will attempt to generate a local reproducer that contains the smallest
|
|
|
|
/// pipeline.
|
|
|
|
void PassManager::enableCrashReproducerGeneration(StringRef outputFile,
|
|
|
|
bool genLocalReproducer) {
|
2020-01-29 03:23:46 +08:00
|
|
|
crashReproducerFileName = std::string(outputFile);
|
2020-04-30 06:08:15 +08:00
|
|
|
localReproducer = genLocalReproducer;
|
2019-10-11 10:19:11 +08:00
|
|
|
}
|
|
|
|
|
2019-09-15 08:44:10 +08:00
|
|
|
/// Add the provided instrumentation to the pass manager.
|
|
|
|
void PassManager::addInstrumentation(std::unique_ptr<PassInstrumentation> pi) {
|
2019-03-11 05:45:47 +08:00
|
|
|
if (!instrumentor)
|
2019-09-23 17:33:51 +08:00
|
|
|
instrumentor = std::make_unique<PassInstrumentor>();
|
2019-03-11 05:45:47 +08:00
|
|
|
|
2019-09-15 08:44:10 +08:00
|
|
|
instrumentor->addInstrumentation(std::move(pi));
|
2019-03-11 05:45:47 +08:00
|
|
|
}
|
|
|
|
|
Implement the initial AnalysisManagement infrastructure, with the introduction of the FunctionAnalysisManager and ModuleAnalysisManager classes. These classes provide analysis computation, caching, and invalidation for a specific IR unit. The invalidation is currently limited to either all or none, i.e. you cannot yet preserve specific analyses.
An analysis can be any class, but it must provide the following:
* A constructor for a given IR unit.
struct MyAnalysis {
// Compute this analysis with the provided module.
MyAnalysis(Module *module);
};
Analyses can be accessed from a Pass by calling either the 'getAnalysisResult<AnalysisT>' or 'getCachedAnalysisResult<AnalysisT>' methods. A FunctionPass may query for a cached analysis on the parent module with 'getCachedModuleAnalysisResult'. Similary, a ModulePass may query an analysis, it doesn't need to be cached, on a child function with 'getFunctionAnalysisResult'.
By default, when running a pass all cached analyses are set to be invalidated. If no transformation was performed, a pass can use the method 'markAllAnalysesPreserved' to preserve all analysis results. As noted above, preserving specific analyses is not yet supported.
PiperOrigin-RevId: 236505642
2019-03-03 13:46:58 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AnalysisManager
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2019-08-29 06:10:37 +08:00
|
|
|
/// Returns a pass instrumentation object for the current operation.
|
|
|
|
PassInstrumentor *AnalysisManager::getPassInstrumentor() const {
|
|
|
|
ParentPointerT curParent = parent;
|
|
|
|
while (auto *parentAM = curParent.dyn_cast<const AnalysisManager *>())
|
|
|
|
curParent = parentAM->parent;
|
|
|
|
return curParent.get<const ModuleAnalysisManager *>()->getPassInstrumentor();
|
2019-03-11 05:45:47 +08:00
|
|
|
}
|
|
|
|
|
2019-08-29 06:10:37 +08:00
|
|
|
/// Get an analysis manager for the given child operation.
|
|
|
|
AnalysisManager AnalysisManager::slice(Operation *op) {
|
|
|
|
assert(op->getParentOp() == impl->getOperation() &&
|
|
|
|
"'op' has a different parent operation");
|
|
|
|
auto it = impl->childAnalyses.find(op);
|
|
|
|
if (it == impl->childAnalyses.end())
|
|
|
|
it = impl->childAnalyses
|
|
|
|
.try_emplace(op, std::make_unique<NestedAnalysisMap>(op))
|
|
|
|
.first;
|
2019-05-16 11:20:11 +08:00
|
|
|
return {this, it->second.get()};
|
Implement the initial AnalysisManagement infrastructure, with the introduction of the FunctionAnalysisManager and ModuleAnalysisManager classes. These classes provide analysis computation, caching, and invalidation for a specific IR unit. The invalidation is currently limited to either all or none, i.e. you cannot yet preserve specific analyses.
An analysis can be any class, but it must provide the following:
* A constructor for a given IR unit.
struct MyAnalysis {
// Compute this analysis with the provided module.
MyAnalysis(Module *module);
};
Analyses can be accessed from a Pass by calling either the 'getAnalysisResult<AnalysisT>' or 'getCachedAnalysisResult<AnalysisT>' methods. A FunctionPass may query for a cached analysis on the parent module with 'getCachedModuleAnalysisResult'. Similary, a ModulePass may query an analysis, it doesn't need to be cached, on a child function with 'getFunctionAnalysisResult'.
By default, when running a pass all cached analyses are set to be invalidated. If no transformation was performed, a pass can use the method 'markAllAnalysesPreserved' to preserve all analysis results. As noted above, preserving specific analyses is not yet supported.
PiperOrigin-RevId: 236505642
2019-03-03 13:46:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Invalidate any non preserved analyses.
|
2019-08-29 06:10:37 +08:00
|
|
|
void detail::NestedAnalysisMap::invalidate(
|
|
|
|
const detail::PreservedAnalyses &pa) {
|
2019-03-07 03:04:22 +08:00
|
|
|
// If all analyses were preserved, then there is nothing to do here.
|
Implement the initial AnalysisManagement infrastructure, with the introduction of the FunctionAnalysisManager and ModuleAnalysisManager classes. These classes provide analysis computation, caching, and invalidation for a specific IR unit. The invalidation is currently limited to either all or none, i.e. you cannot yet preserve specific analyses.
An analysis can be any class, but it must provide the following:
* A constructor for a given IR unit.
struct MyAnalysis {
// Compute this analysis with the provided module.
MyAnalysis(Module *module);
};
Analyses can be accessed from a Pass by calling either the 'getAnalysisResult<AnalysisT>' or 'getCachedAnalysisResult<AnalysisT>' methods. A FunctionPass may query for a cached analysis on the parent module with 'getCachedModuleAnalysisResult'. Similary, a ModulePass may query an analysis, it doesn't need to be cached, on a child function with 'getFunctionAnalysisResult'.
By default, when running a pass all cached analyses are set to be invalidated. If no transformation was performed, a pass can use the method 'markAllAnalysesPreserved' to preserve all analysis results. As noted above, preserving specific analyses is not yet supported.
PiperOrigin-RevId: 236505642
2019-03-03 13:46:58 +08:00
|
|
|
if (pa.isAll())
|
|
|
|
return;
|
|
|
|
|
2019-08-29 06:10:37 +08:00
|
|
|
// Invalidate the analyses for the current operation directly.
|
|
|
|
analyses.invalidate(pa);
|
2019-03-07 03:04:22 +08:00
|
|
|
|
2019-08-29 06:10:37 +08:00
|
|
|
// If no analyses were preserved, then just simply clear out the child
|
2019-03-07 03:04:22 +08:00
|
|
|
// analysis results.
|
|
|
|
if (pa.isNone()) {
|
2019-08-29 06:10:37 +08:00
|
|
|
childAnalyses.clear();
|
2019-03-07 03:04:22 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-29 06:10:37 +08:00
|
|
|
// Otherwise, invalidate each child analysis map.
|
|
|
|
SmallVector<NestedAnalysisMap *, 8> mapsToInvalidate(1, this);
|
|
|
|
while (!mapsToInvalidate.empty()) {
|
|
|
|
auto *map = mapsToInvalidate.pop_back_val();
|
|
|
|
for (auto &analysisPair : map->childAnalyses) {
|
|
|
|
analysisPair.second->invalidate(pa);
|
|
|
|
if (!analysisPair.second->childAnalyses.empty())
|
|
|
|
mapsToInvalidate.push_back(analysisPair.second.get());
|
|
|
|
}
|
|
|
|
}
|
Implement the initial AnalysisManagement infrastructure, with the introduction of the FunctionAnalysisManager and ModuleAnalysisManager classes. These classes provide analysis computation, caching, and invalidation for a specific IR unit. The invalidation is currently limited to either all or none, i.e. you cannot yet preserve specific analyses.
An analysis can be any class, but it must provide the following:
* A constructor for a given IR unit.
struct MyAnalysis {
// Compute this analysis with the provided module.
MyAnalysis(Module *module);
};
Analyses can be accessed from a Pass by calling either the 'getAnalysisResult<AnalysisT>' or 'getCachedAnalysisResult<AnalysisT>' methods. A FunctionPass may query for a cached analysis on the parent module with 'getCachedModuleAnalysisResult'. Similary, a ModulePass may query an analysis, it doesn't need to be cached, on a child function with 'getFunctionAnalysisResult'.
By default, when running a pass all cached analyses are set to be invalidated. If no transformation was performed, a pass can use the method 'markAllAnalysesPreserved' to preserve all analysis results. As noted above, preserving specific analyses is not yet supported.
PiperOrigin-RevId: 236505642
2019-03-03 13:46:58 +08:00
|
|
|
}
|
2019-03-11 06:44:47 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PassInstrumentation
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
PassInstrumentation::~PassInstrumentation() {}
|
2019-03-19 02:56:18 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PassInstrumentor
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace mlir {
|
|
|
|
namespace detail {
|
|
|
|
struct PassInstrumentorImpl {
|
|
|
|
/// Mutex to keep instrumentation access thread-safe.
|
|
|
|
llvm::sys::SmartMutex<true> mutex;
|
|
|
|
|
|
|
|
/// Set of registered instrumentations.
|
|
|
|
std::vector<std::unique_ptr<PassInstrumentation>> instrumentations;
|
|
|
|
};
|
|
|
|
} // end namespace detail
|
|
|
|
} // end namespace mlir
|
|
|
|
|
|
|
|
PassInstrumentor::PassInstrumentor() : impl(new PassInstrumentorImpl()) {}
|
|
|
|
PassInstrumentor::~PassInstrumentor() {}
|
|
|
|
|
2019-09-09 10:57:25 +08:00
|
|
|
/// See PassInstrumentation::runBeforePipeline for details.
|
2019-10-01 08:44:31 +08:00
|
|
|
void PassInstrumentor::runBeforePipeline(
|
|
|
|
const OperationName &name,
|
|
|
|
const PassInstrumentation::PipelineParentInfo &parentInfo) {
|
2019-09-09 10:57:25 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
|
|
|
for (auto &instr : impl->instrumentations)
|
2019-10-01 08:44:31 +08:00
|
|
|
instr->runBeforePipeline(name, parentInfo);
|
2019-09-09 10:57:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// See PassInstrumentation::runAfterPipeline for details.
|
2019-10-01 08:44:31 +08:00
|
|
|
void PassInstrumentor::runAfterPipeline(
|
|
|
|
const OperationName &name,
|
|
|
|
const PassInstrumentation::PipelineParentInfo &parentInfo) {
|
2019-09-09 10:57:25 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
2019-10-01 08:44:31 +08:00
|
|
|
for (auto &instr : llvm::reverse(impl->instrumentations))
|
|
|
|
instr->runAfterPipeline(name, parentInfo);
|
2019-09-09 10:57:25 +08:00
|
|
|
}
|
|
|
|
|
2019-03-19 02:56:18 +08:00
|
|
|
/// See PassInstrumentation::runBeforePass for details.
|
2019-08-17 08:59:03 +08:00
|
|
|
void PassInstrumentor::runBeforePass(Pass *pass, Operation *op) {
|
2019-03-19 02:56:18 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
|
|
|
for (auto &instr : impl->instrumentations)
|
2019-08-17 08:59:03 +08:00
|
|
|
instr->runBeforePass(pass, op);
|
2019-03-19 02:56:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// See PassInstrumentation::runAfterPass for details.
|
2019-08-17 08:59:03 +08:00
|
|
|
void PassInstrumentor::runAfterPass(Pass *pass, Operation *op) {
|
2019-03-19 02:56:18 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
|
|
|
for (auto &instr : llvm::reverse(impl->instrumentations))
|
2019-08-17 08:59:03 +08:00
|
|
|
instr->runAfterPass(pass, op);
|
2019-03-19 02:56:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// See PassInstrumentation::runAfterPassFailed for details.
|
2019-08-17 08:59:03 +08:00
|
|
|
void PassInstrumentor::runAfterPassFailed(Pass *pass, Operation *op) {
|
2019-03-19 02:56:18 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
|
|
|
for (auto &instr : llvm::reverse(impl->instrumentations))
|
2019-08-17 08:59:03 +08:00
|
|
|
instr->runAfterPassFailed(pass, op);
|
2019-03-19 02:56:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// See PassInstrumentation::runBeforeAnalysis for details.
|
2020-04-11 14:46:52 +08:00
|
|
|
void PassInstrumentor::runBeforeAnalysis(StringRef name, TypeID id,
|
2019-08-17 08:59:03 +08:00
|
|
|
Operation *op) {
|
2019-03-19 02:56:18 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
|
|
|
for (auto &instr : impl->instrumentations)
|
2019-08-17 08:59:03 +08:00
|
|
|
instr->runBeforeAnalysis(name, id, op);
|
2019-03-19 02:56:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// See PassInstrumentation::runAfterAnalysis for details.
|
2020-04-11 14:46:52 +08:00
|
|
|
void PassInstrumentor::runAfterAnalysis(StringRef name, TypeID id,
|
2019-08-17 08:59:03 +08:00
|
|
|
Operation *op) {
|
2019-03-19 02:56:18 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
|
|
|
for (auto &instr : llvm::reverse(impl->instrumentations))
|
2019-08-17 08:59:03 +08:00
|
|
|
instr->runAfterAnalysis(name, id, op);
|
2019-03-19 02:56:18 +08:00
|
|
|
}
|
|
|
|
|
2019-09-15 08:44:10 +08:00
|
|
|
/// Add the given instrumentation to the collection.
|
|
|
|
void PassInstrumentor::addInstrumentation(
|
|
|
|
std::unique_ptr<PassInstrumentation> pi) {
|
2019-03-19 02:56:18 +08:00
|
|
|
llvm::sys::SmartScopedLock<true> instrumentationLock(impl->mutex);
|
2019-09-15 08:44:10 +08:00
|
|
|
impl->instrumentations.emplace_back(std::move(pi));
|
2019-03-19 02:56:18 +08:00
|
|
|
}
|