[llvm-branch-commits] [mlir] e9987ad - [mlir][docs] Tidy up the pass infrastructure documentation
River Riddle via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Dec 11 17:58:08 PST 2020
Author: River Riddle
Date: 2020-12-11T17:53:33-08:00
New Revision: e9987ad8783be164b63bdf2fffdd64a6e289e18f
URL: https://github.com/llvm/llvm-project/commit/e9987ad8783be164b63bdf2fffdd64a6e289e18f
DIFF: https://github.com/llvm/llvm-project/commit/e9987ad8783be164b63bdf2fffdd64a6e289e18f.diff
LOG: [mlir][docs] Tidy up the pass infrastructure documentation
The doc has grown stale and is missing some recent changes to the infrastructure.
Differential Revision: https://reviews.llvm.org/D93081
Added:
Modified:
mlir/docs/PassManagement.md
Removed:
################################################################################
diff --git a/mlir/docs/PassManagement.md b/mlir/docs/PassManagement.md
index 6e577db4501c..a3a5a49444a9 100644
--- a/mlir/docs/PassManagement.md
+++ b/mlir/docs/PassManagement.md
@@ -3,15 +3,15 @@
[TOC]
Passes represent the basic infrastructure for transformation and optimization.
-This document provides a quickstart to the pass infrastructure in MLIR and how
-to use it.
+This document provides an overview of the pass infrastructure in MLIR and how to
+use it.
See [MLIR specification](LangRef.md) for more information about MLIR and its
core aspects, such as the IR structure and operations.
See [MLIR Rewrites](Tutorials/QuickstartRewrites.md) for a quick start on graph
-rewriting in MLIR. If your transformation involves pattern matching operation
-DAGs, this is a great place to start.
+rewriting in MLIR. If a transformation involves pattern matching operation DAGs,
+this is a great place to start.
## Operation Pass
@@ -23,24 +23,27 @@ further below. All passes in MLIR derive from `OperationPass` and adhere to the
following restrictions; any noncompliance will lead to problematic behavior in
multithreaded and other advanced scenarios:
-* Modify anything within the parent block/region/operation/etc, outside of the
- current operation being operated on. This includes adding or removing
- operations from the parent block.
-* Maintain pass state across invocations of `runOnOperation`. A pass may be
- run on several
diff erent operations with no guarantee of execution order.
- * When multithreading, a specific pass instance may not even execute on
- all operations within the module. As such, a pass should not rely on
- running on all operations.
+* Modify any state referenced or relied upon outside the current being
+ operated on. This includes adding or removing operations from the parent
+ block, changing the attributes(depending on the contract of the current
+ operation)/operands/results/successors of the current operation.
* Modify the state of another operation not nested within the current
operation being operated on.
- * Other threads may be operating on
diff erent operations within the module
- simultaneously.
+ * Other threads may be operating on these operations simultaneously.
+* Inspect the state of sibling operations.
+ * Other threads may be modifying these operations in parallel.
+ * Inspecting the state of ancestor/parent operations is permitted.
+* Maintain mutable pass state across invocations of `runOnOperation`. A pass
+ may be run on many
diff erent operations with no guarantee of execution
+ order.
+ * When multithreading, a specific pass instance may not even execute on
+ all operations within the IR. As such, a pass should not rely on running
+ on all operations.
* Maintain any global mutable state, e.g. static variables within the source
file. All mutable state should be maintained by an instance of the pass.
-* Must be copy-constructible, multiple instances of the pass may be created by
- the pass manager to process operations in parallel.
-* Inspect the IR of sibling operations. Other threads may be modifying these
- operations in parallel.
+* Must be copy-constructible
+ * Multiple instances of the pass may be created by the pass manager to
+ process operations in parallel.
When creating an operation pass, there are two
diff erent types to choose from
depending on the usage scenario:
@@ -62,7 +65,12 @@ A simple pass may look like:
```c++
namespace {
-struct MyFunctionPass : public OperationPass<MyFunctionPass, FuncOp> {
+/// Here we utilize the CRTP `PassWrapper` utility class to provide some
+/// necessary utility hooks. This is only necessary for passes defined directly
+/// in C++. Passes defined declaratively use a cleaner mechanism for providing
+/// these utilities.
+struct MyFunctionPass : public PassWrapper<OperationPass<FuncOp>,
+ MyFunctionPass> {
void runOnOperation() override {
// Get the current FuncOp operation being operated on.
FuncOp f = getOperation();
@@ -75,17 +83,23 @@ struct MyFunctionPass : public OperationPass<MyFunctionPass, FuncOp> {
};
} // end anonymous namespace
-// Register this pass to make it accessible to utilities like mlir-opt.
-// (Pass registration is discussed more below)
-static PassRegistration<MyFunctionPass> pass(
+/// Register this pass so that it can be built via from a textual pass pipeline.
+/// (Pass registration is discussed more below)
+void registerMyPass() {
+ PassRegistration<MyFunctionPass>(
"flag-name-to-invoke-pass-via-mlir-opt", "Pass description here");
+}
```
### OperationPass : Op-Agnostic
An `op-agnostic` pass operates on the operation type of the pass manager that it
-is added to. This means that a pass that operates on several
diff erent operation
-types in the same way only needs one implementation.
+is added to. This means that passes of this type may operate on several
+
diff erent operation types. Passes of this type are generally written generically
+using operation [interfaces](Interfaces.md) and [traits](Traits.md). Examples of
+this type of pass are
+[Common Sub-Expression Elimination](Passes.md#-cse-eliminate-common-sub-expressions)
+and [Inlining](Passes.md#-inline-inline-function-calls).
To create an operation pass, a derived class must adhere to the following:
@@ -95,7 +109,11 @@ To create an operation pass, a derived class must adhere to the following:
A simple pass may look like:
```c++
-struct MyOperationPass : public OperationPass<MyOperationPass> {
+/// Here we utilize the CRTP `PassWrapper` utility class to provide some
+/// necessary utility hooks. This is only necessary for passes defined directly
+/// in C++. Passes defined declaratively use a cleaner mechanism for providing
+/// these utilities.
+struct MyOperationPass : public PassWrapper<OperationPass<>, MyOperationPass> {
void runOnOperation() override {
// Get the current operation being operated on.
Operation *op = getOperation();
@@ -107,11 +125,11 @@ struct MyOperationPass : public OperationPass<MyOperationPass> {
### Dependent Dialects
Dialects must be loaded in the MLIRContext before entities from these dialects
-(operations, types, attributes, ...) can be created. Dialects must be loaded
-before starting the multi-threaded pass pipeline execution. To this end, a pass
-that can create an entity from a dialect that isn't already loaded must express
-this by overriding the `getDependentDialects()` method and declare this list of
-Dialects explicitly.
+(operations, types, attributes, ...) can be created. Dialects must also be
+loaded before starting the execution of a multi-threaded pass pipeline. To this
+end, a pass that may create an entity from a dialect that isn't guaranteed to
+already ne loaded must express this by overriding the `getDependentDialects()`
+method and declare this list of Dialects explicitly.
## Analysis Management
@@ -212,51 +230,53 @@ void MyOperationPass::runOnOperation() {
Passes in MLIR are allowed to gracefully fail. This may happen if some invariant
of the pass was broken, potentially leaving the IR in some invalid state. If
such a situation occurs, the pass can directly signal a failure to the pass
-manager. If a pass signaled a failure when executing, no other passes in the
-pipeline will execute and the `PassManager::run` will return failure. Failure
-signaling is provided in the form of a `signalPassFailure` method.
+manager via the `signalPassFailure` method. If a pass signaled a failure when
+executing, no other passes in the pipeline will execute and the top-level call
+to `PassManager::run` will return `failure`.
```c++
-void MyPass::runOnOperation() {
+void MyOperationPass::runOnOperation() {
// Signal failure on a broken invariant.
- if (some_broken_invariant) {
- signalPassFailure();
- return;
- }
+ if (some_broken_invariant)
+ return signalPassFailure();
}
```
## Pass Manager
-Above we introduced the
diff erent types of passes and their constraints. Now
-that we have our pass we need to be able to run it over a specific module. This
-is where the pass manager comes into play. The `PassManager` class is used to
-configure and run a pipeline. The `OpPassManager` class is used to schedule
-passes to run at a specific level of nesting.
+The above sections introduced the
diff erent types of passes and their
+invariants. This section introduces the concept of a PassManager, and how it can
+be used to configure and schedule a pass pipeline. There are two main classes
+related to pass management, the `PassManager` and the `OpPassManager`. The
+`PassManager` class acts as the top-level entry point, and contains various
+configurations used for the entire pass pipeline. The `OpPassManager` class is
+used to schedule passes to run at a specific level of nesting. The top-level
+`PassManager` also functions as an `OpPassManager`.
### OpPassManager
An `OpPassManager` is essentially a collection of passes to execute on an
-operation of a given type. This operation type must adhere to the following
+operation of a specific type. This operation type must adhere to the following
requirement:
-* Must be registered and marked `IsolatedFromAbove`.
+* Must be registered and marked
+ [`IsolatedFromAbove`](Traits.md#isolatedfromabove).
* Passes are expected to not modify operations at or above the current
operation being processed. If the operation is not isolated, it may
- inadvertently modify the use-list of an operation it is not supposed to
- modify.
+ inadvertently modify or traverse the SSA use-list of an operation it is
+ not supposed to.
Passes can be added to a pass manager via `addPass`. The pass must either be an
`op-specific` pass operating on the same operation type as `OpPassManager`, or
an `op-agnostic` pass.
-An `OpPassManager` cannot be created directly, but must be explicitly nested
-within another `OpPassManager` via the `nest<>` method. This method takes the
+An `OpPassManager` is generally creted by explicitly nesting a pipeline within
+another existing `OpPassManager` via the `nest<>` method. This method takes the
operation type that the nested pass manager will operate on. At the top-level, a
-`PassManager` acts as an `OpPassManager` that operates on the
-[`module`](LangRef.md#module) operation. Nesting in this sense, corresponds to
-the structural nesting within [Regions](LangRef.md#regions) of the IR.
+`PassManager` acts as an `OpPassManager`. Nesting in this sense, corresponds to
+the [structural](Tutorials/UnderstandingTheIRStructure.md) nesting within
+[Regions](LangRef.md#regions) of the IR.
For example, the following `.mlir`:
@@ -282,13 +302,17 @@ Below is an example of constructing a pipeline that operates on the above
structure:
```c++
+// Create a top-level `PassManager` class. If an operation type is not
+// explicitly specific, the default is the builtin `module` operation.
PassManager pm(ctx);
+// Note: We could also create the above `PassManager` this way.
+PassManager pm(ctx, /*operationName=*/"module");
// Add a pass on the top-level module operation.
pm.addPass(std::make_unique<MyModulePass>());
-// Nest a pass manager that operates on spirv module operations nested directly
-// under the top-level module.
+// Nest a pass manager that operates on `spirv.module` operations nested
+// directly under the top-level module.
OpPassManager &nestedModulePM = pm.nest<spirv::ModuleOp>();
nestedModulePM.addPass(std::make_unique<MySPIRVModulePass>());
@@ -298,12 +322,12 @@ OpPassManager &nestedFunctionPM = nestedModulePM.nest<FuncOp>();
nestedFunctionPM.addPass(std::make_unique<MyFunctionPass>());
// Run the pass manager on the top-level module.
-Module m = ...;
+ModuleOp m = ...;
if (failed(pm.run(m)))
... // One of the passes signaled a failure.
```
-The above pass manager would contain the following pipeline structure:
+The above pass manager contains the following pipeline structure:
```
OpPassManager<ModuleOp>
@@ -326,146 +350,62 @@ program has been run through the passes. This provides several benefits:
that need to be scheduled, as well as increasing the efficiency of each job.
An entire function pipeline can be run on each function asynchronously.
-## Pass Registration
-
-Briefly shown in the example definitions of the various pass types is the
-`PassRegistration` class. This is a utility to register derived pass classes so
-that they may be created, and inspected, by utilities like mlir-opt. Registering
-a pass class takes the form:
-
-```c++
-static PassRegistration<MyPass> pass("command-line-arg", "description");
-```
-
-* `MyPass` is the name of the derived pass class.
-* "command-line-arg" is the argument to use on the command line to invoke the
- pass from `mlir-opt`.
-* "description" is a description of the pass.
+## Dynamic Pass Pipelines
-For passes that cannot be default-constructed, `PassRegistration` accepts an
-optional third argument that takes a callback to create the pass:
+In some situations it may be useful to run a pass pipeline within another pass,
+to allow configuring or filtering based on some invariants of the current
+operation being operated on. For example, the
+[Inliner Pass](Passes.md#-inline-inline-function-calls) may want to run
+intraprocedural simplification passes while it is inlining to produce a better
+cost model, and provide more optimal inlining. To enable this, passes may run an
+arbitrary `OpPassManager` on the current operation being operated on or any
+operation nested within the current operation via the `LogicalResult
+Pass::runPipeline(OpPassManager &, Operation *)` method. This method returns
+whether the dynamic pipeline succeeded or failed, similarly to the result of the
+top-level `PassManager::run` method. A simple example is shown below:
```c++
-static PassRegistration<MyParametricPass> pass(
- "command-line-arg", "description",
- []() -> std::unique_ptr<Pass> {
- std::unique_ptr<Pass> p = std::make_unique<MyParametricPass>(/*options*/);
- /*... non-trivial-logic to configure the pass ...*/;
- return p;
- });
-```
-
-This variant of registration can be used, for example, to accept the
-configuration of a pass from command-line arguments and pass it over to the pass
-constructor. Make sure that the pass is copy-constructible in a way that does
-not share data as the [pass manager](#pass-manager) may create copies of the
-pass to run in parallel.
-
-### Pass Pipeline Registration
-
-Described above is the mechanism used for registering a specific derived pass
-class. On top of that, MLIR allows for registering custom pass pipelines in a
-similar fashion. This allows for custom pipelines to be available to tools like
-mlir-opt in the same way that passes are, which is useful for encapsulating
-common pipelines like the "-O1" series of passes. Pipelines are registered via a
-similar mechanism to passes in the form of `PassPipelineRegistration`. Compared
-to `PassRegistration`, this class takes an additional parameter in the form of a
-pipeline builder that modifies a provided `OpPassManager`.
-
-```c++
-void pipelineBuilder(OpPassManager &pm) {
- pm.addPass(std::make_unique<MyPass>());
- pm.addPass(std::make_unique<MyOtherPass>());
+void MyModulePass::runOnOperation() {
+ ModuleOp module = getOperation();
+ if (hasSomeSpecificProperty(module)) {
+ OpPassManager dynamicPM("module");
+ ...; // Build the dynamic pipeline.
+ if (failed(runPipeline(dynamicPM, module)))
+ return signalPassFailure();
+ }
}
-
-// Register an existing pipeline builder function.
-static PassPipelineRegistration<> pipeline(
- "command-line-arg", "description", pipelineBuilder);
-
-// Register an inline pipeline builder.
-static PassPipelineRegistration<> pipeline(
- "command-line-arg", "description", [](OpPassManager &pm) {
- pm.addPass(std::make_unique<MyPass>());
- pm.addPass(std::make_unique<MyOtherPass>());
- });
```
-Pipeline registration also allows for simplified registration of
-specializations for existing passes:
+Note: though above the dynamic pipeline was constructed within the
+`runOnOperation` method, this is not necessary and pipelines should be cached
+when possible as the `OpPassManager` class can be safely copy constructed.
-```c++
-static PassPipelineRegistration<> foo10(
- "foo-10", "Foo Pass 10", [] { return std::make_unique<FooPass>(10); } );
-```
+The mechanism described in this section should be used whenever a pass pipeline
+should run in a nested fashion, i.e. when the nested pipeline cannot be
+scheduled statically along with the rest of the main pass pipeline. More
+specifically, a `PassManager` should generally never need to be constructed
+within a `Pass`. Using `runPipeline` also ensures that all analyses,
+[instrumentations](#pass-instrumentation), and other pass manager related
+components are integrated with the dynamic pipeline being executed.
-### Textual Pass Pipeline Specification
+## Instance Specific Pass Options
-In the previous sections, we showed how to register passes and pass pipelines
-with a specific argument and description. Once registered, these can be used on
-the command line to configure a pass manager. The limitation of using these
-arguments directly is that they cannot build a nested pipeline. For example, if
-our module has another module nested underneath, with just `-my-module-pass`
-there is no way to specify that this pass should run on the nested module and
-not the top-level module. This is due to the flattened nature of the command
-line.
-
-To circumvent this limitation, MLIR also supports a textual description of a
-pass pipeline. This allows for explicitly specifying the structure of the
-pipeline to add to the pass manager. This includes the nesting structure, as
-well as the passes and pass pipelines to run. A textual pipeline is defined as a
-series of names, each of which may in itself recursively contain a nested
-pipeline description. The syntax for this specification is as follows:
-
-```ebnf
-pipeline ::= op-name `(` pipeline-element (`,` pipeline-element)* `)`
-pipeline-element ::= pipeline | (pass-name | pass-pipeline-name) options?
-options ::= '{' (key ('=' value)?)+ '}'
-```
-
-* `op-name`
- * This corresponds to the mnemonic name of an operation to run passes on,
- e.g. `func` or `module`.
-* `pass-name` | `pass-pipeline-name`
- * This corresponds to the command-line argument of a registered pass or
- pass pipeline, e.g. `cse` or `canonicalize`.
-* `options`
- * Options are pass specific key value pairs that are handled as described
- in the [instance specific pass options](#instance-specific-pass-options)
- section.
-
-For example, the following pipeline:
-
-```shell
-$ mlir-opt foo.mlir -cse -canonicalize -convert-std-to-llvm
-```
-
-Can also be specified as (via the `-pass-pipeline` flag):
-
-```shell
-$ mlir-opt foo.mlir -pass-pipeline='func(cse, canonicalize), convert-std-to-llvm'
-```
-
-In order to support round-tripping your pass to the textual representation using
-`OpPassManager::printAsTextualPipeline(raw_ostream&)`, override
-`Pass::printAsTextualPipeline(raw_ostream&)` to format your pass-name and
-options in the format described above.
-
-### Instance Specific Pass Options
-
-Options may be specified for a parametric pass. Individual options are defined
-using the [LLVM command line](https://llvm.org/docs/CommandLine.html) flag
-definition rules. These options will then be parsed at pass construction time
-independently for each instance of the pass. To provide options for passes, the
-`Option<>` and `OptionList<>` classes may be used:
+MLIR provides a builtin mechanism for passes to specify options that configure
+its behavior. These options are parsed at pass construction time independently
+for each instance of the pass. Options are defined using the `Option<>` and
+`ListOption<>` classes, and follow the
+[LLVM command line](https://llvm.org/docs/CommandLine.html) flag definition
+rules. See below for a few examples:
```c++
struct MyPass ... {
/// Make sure that we have a valid default constructor and copy constructor to
- /// make sure that the options are initialized properly.
+ /// ensure that the options are initialized properly.
MyPass() = default;
MyPass(const MyPass& pass) {}
- // These just forward onto llvm::cl::list and llvm::cl::opt respectively.
+ /// Any parameters after the description are forwarded to llvm::cl::list and
+ /// llvm::cl::opt respectively.
Option<int> exampleOption{*this, "flag-name", llvm::cl::desc("...")};
ListOption<int> exampleListOption{*this, "list-flag-name",
llvm::cl::desc("...")};
@@ -473,43 +413,44 @@ struct MyPass ... {
```
For pass pipelines, the `PassPipelineRegistration` templates take an additional
-optional template parameter that is the Option struct definition to be used for
-that pipeline. To use pipeline specific options, create a class that inherits
-from `mlir::PassPipelineOptions` that contains the desired options. When using
-`PassPipelineRegistration`, the constructor now takes a function with the
-signature `void (OpPassManager &pm, const MyPipelineOptions&)` which should
-construct the passes from the options and pass them to the pm:
+template parameter for an optional `Option` struct definition. This struct
+should inherit from `mlir::PassPipelineOptions` and contain the desired pipeline
+options. When using `PassPipelineRegistration`, the constructor now takes a
+function with the signature `void (OpPassManager &pm, const MyPipelineOptions&)`
+which should construct the passes from the options and pass them to the pm:
```c++
struct MyPipelineOptions : public PassPipelineOptions {
- // These just forward onto llvm::cl::list and llvm::cl::opt respectively.
+ // The structure of these options is the same as those for pass options.
Option<int> exampleOption{*this, "flag-name", llvm::cl::desc("...")};
ListOption<int> exampleListOption{*this, "list-flag-name",
llvm::cl::desc("...")};
};
-
-static mlir::PassPipelineRegistration<MyPipelineOptions> pipeline(
+void registerMyPasses() {
+ PassPipelineRegistration<MyPipelineOptions>(
"example-pipeline", "Run an example pipeline.",
[](OpPassManager &pm, const MyPipelineOptions &pipelineOptions) {
// Initialize the pass manager.
});
+}
```
## Pass Statistics
Statistics are a way to keep track of what the compiler is doing and how
effective various transformations are. It is often useful to see what effect
-specific transformations have on a particular program, and how often they
-trigger. Pass statistics are instance specific which allow for taking this a
-step further as you are able to see the effect of placing a particular
-transformation at specific places within the pass pipeline. For example, they
-help answer questions like `What happens if I run CSE again here?`.
+specific transformations have on a particular input, and how often they trigger.
+Pass statistics are specific to each pass instance, which allow for seeing the
+effect of placing a particular transformation at specific places within the pass
+pipeline. For example, they help answer questions like "What happens if I run
+CSE again here?".
Statistics can be added to a pass by using the 'Pass::Statistic' class. This
class takes as a constructor arguments: the parent pass, a name, and a
-description. This class acts like an unsigned integer, and may be incremented
-and updated accordingly. These statistics use the same infrastructure as
+description. This class acts like an atomic unsigned integer, and may be
+incremented and updated accordingly. These statistics rely on the same
+infrastructure as
[`llvm::Statistic`](http://llvm.org/docs/ProgrammersManual.html#the-statistic-class-stats-option)
and thus have similar usage constraints. Collected statistics can be dumped by
the [pass manager](#pass-manager) programmatically via
@@ -519,14 +460,20 @@ the [pass manager](#pass-manager) programmatically via
An example is shown below:
```c++
-struct MyPass : public OperationPass<MyPass> {
- Statistic testStat{this, "testStat", "A test statistic"};
+struct MyPass ... {
+ /// Make sure that we have a valid default constructor and copy constructor to
+ /// ensure that the options are initialized properly.
+ MyPass() = default;
+ MyPass(const MyPass& pass) {}
+
+ /// Define the statistic to track during the execution of MyPass.
+ Statistic exampleStat{this, "exampleStat", "An example statistic"};
void runOnOperation() {
...
- // Update our statistic after some invariant was hit.
- ++testStat;
+ // Update the statistic after some invariant was hit.
+ ++exampleStat;
...
}
@@ -546,15 +493,16 @@ $ mlir-opt -pass-pipeline='func(my-pass,my-pass)' foo.mlir -pass-statistics
===-------------------------------------------------------------------------===
'func' Pipeline
MyPass
- (S) 15 testStat - A test statistic
+ (S) 15 exampleStat - An example statistic
VerifierPass
MyPass
- (S) 6 testStat - A test statistic
+ (S) 6 exampleStat - An example statistic
VerifierPass
VerifierPass
```
-And a list view that aggregates all instances of a specific pass together:
+A list view that aggregates the statistics of all instances of a specific pass
+together:
```shell
$ mlir-opt -pass-pipeline='func(my-pass, my-pass)' foo.mlir -pass-statistics -pass-statistics-display=list
@@ -563,24 +511,148 @@ $ mlir-opt -pass-pipeline='func(my-pass, my-pass)' foo.mlir -pass-statistics -pa
... Pass statistics report ...
===-------------------------------------------------------------------------===
MyPass
- (S) 21 testStat - A test statistic
+ (S) 21 exampleStat - An example statistic
+```
+
+## Pass Registration
+
+Briefly shown in the example definitions of the various pass types is the
+`PassRegistration` class. This mechanism allows for registering pass classes so
+that they may be created within a
+[textual pass pipeline description](#textual-pass-pipeline-specification). An
+example registration is shown below:
+
+```c++
+void registerMyPass() {
+ PassRegistration<MyPass>("argument", "description");
+}
+```
+
+* `MyPass` is the name of the derived pass class.
+* "argument" is the argument used to refer to the pass in the textual format.
+* "description" is a brief description of the pass.
+
+For passes that cannot be default-constructed, `PassRegistration` accepts an
+optional third argument that takes a callback to create the pass:
+
+```c++
+void registerMyPass() {
+ PassRegistration<MyParametricPass>(
+ "argument", "description",
+ []() -> std::unique_ptr<Pass> {
+ std::unique_ptr<Pass> p = std::make_unique<MyParametricPass>(/*options*/);
+ /*... non-trivial-logic to configure the pass ...*/;
+ return p;
+ });
+}
```
+This variant of registration can be used, for example, to accept the
+configuration of a pass from command-line arguments and pass it to the pass
+constructor.
+
+Note: Make sure that the pass is copy-constructible in a way that does not share
+data as the [pass manager](#pass-manager) may create copies of the pass to run
+in parallel.
+
+### Pass Pipeline Registration
+
+Described above is the mechanism used for registering a specific derived pass
+class. On top of that, MLIR allows for registering custom pass pipelines in a
+similar fashion. This allows for custom pipelines to be available to tools like
+mlir-opt in the same way that passes are, which is useful for encapsulating
+common pipelines like the "-O1" series of passes. Pipelines are registered via a
+similar mechanism to passes in the form of `PassPipelineRegistration`. Compared
+to `PassRegistration`, this class takes an additional parameter in the form of a
+pipeline builder that modifies a provided `OpPassManager`.
+
+```c++
+void pipelineBuilder(OpPassManager &pm) {
+ pm.addPass(std::make_unique<MyPass>());
+ pm.addPass(std::make_unique<MyOtherPass>());
+}
+
+void registerMyPasses() {
+ // Register an existing pipeline builder function.
+ PassPipelineRegistration<>(
+ "argument", "description", pipelineBuilder);
+
+ // Register an inline pipeline builder.
+ PassPipelineRegistration<>(
+ "argument", "description", [](OpPassManager &pm) {
+ pm.addPass(std::make_unique<MyPass>());
+ pm.addPass(std::make_unique<MyOtherPass>());
+ });
+}
+```
+
+### Textual Pass Pipeline Specification
+
+The previous sections detailed how to register passes and pass pipelines with a
+specific argument and description. Once registered, these can be used to
+configure a pass manager from a string description. This is especially useful
+for tools like `mlir-opt`, that configure pass managers from the command line,
+or as options to passes that utilize
+[dynamic pass pipelines](#dynamic-pass-pipelines).
+
+To support the ability to describe the full structure of pass pipelines, MLIR
+supports a custom textual description of pass pipelines. The textual description
+includes the nesting structure, the arguments of the passes and pass pipelines
+to run, and any options for those passes and pipelines. A textual pipeline is
+defined as a series of names, each of which may in itself recursively contain a
+nested pipeline description. The syntax for this specification is as follows:
+
+```ebnf
+pipeline ::= op-name `(` pipeline-element (`,` pipeline-element)* `)`
+pipeline-element ::= pipeline | (pass-name | pass-pipeline-name) options?
+options ::= '{' (key ('=' value)?)+ '}'
+```
+
+* `op-name`
+ * This corresponds to the mnemonic name of an operation to run passes on,
+ e.g. `func` or `module`.
+* `pass-name` | `pass-pipeline-name`
+ * This corresponds to the argument of a registered pass or pass pipeline,
+ e.g. `cse` or `canonicalize`.
+* `options`
+ * Options are specific key value pairs representing options defined by a
+ pass or pass pipeline, as described in the
+ ["Instance Specific Pass Options"](#instance-specific-pass-options)
+ section. See this section for an example usage in a textual pipeline.
+
+For example, the following pipeline:
+
+```shell
+$ mlir-opt foo.mlir -cse -canonicalize -convert-std-to-llvm='use-bare-ptr-memref-call-conv=1'
+```
+
+Can also be specified as (via the `-pass-pipeline` flag):
+
+```shell
+$ mlir-opt foo.mlir -pass-pipeline='func(cse,canonicalize),convert-std-to-llvm{use-bare-ptr-memref-call-conv=1}'
+```
+
+In order to support round-tripping a pass to the textual representation using
+`OpPassManager::printAsTextualPipeline(raw_ostream&)`, override `StringRef
+Pass::getArgument()` to specify the argument used when registering a pass.
+
## Declarative Pass Specification
Some aspects of a Pass may be specified declaratively, in a form similar to
-[operations](OpDefinitions.md). This specification simplifies several
-mechanisms used when defining passes. It can be used for generating pass
-registration calls, defining boilerplate pass utilities, and generating pass
-documentation.
+[operations](OpDefinitions.md). This specification simplifies several mechanisms
+used when defining passes. It can be used for generating pass registration
+calls, defining boilerplate pass utilities, and generating pass documentation.
Consider the following pass specified in C++:
```c++
struct MyPass : PassWrapper<MyPass, OperationPass<ModuleOp>> {
+ MyPass() = default;
+ MyPass(const MyPass &) {}
+
...
- /// Options.
+ // Specify any options.
Option<bool> option{
*this, "example-option",
llvm::cl::desc("An example option"), llvm::cl::init(true)};
@@ -589,7 +661,7 @@ struct MyPass : PassWrapper<MyPass, OperationPass<ModuleOp>> {
llvm::cl::desc("An example list option"), llvm::cl::ZeroOrMore,
llvm::cl::MiscFlags::CommaSeparated};
- /// Statistics.
+ // Specify any statistics.
Statistic statistic{this, "example-statistic", "An example statistic"};
};
@@ -598,7 +670,10 @@ std::unique_ptr<Pass> foo::createMyPass() {
return std::make_unique<MyPass>();
}
-static PassRegistration<MyPass> pass("my-pass", "My pass summary");
+/// Register this pass.
+void foo::registerMyPass() {
+ PassRegistration<MyPass>("my-pass", "My pass summary");
+}
```
This pass may be specified declaratively as so:
@@ -631,33 +706,35 @@ def MyPass : Pass<"my-pass", "ModuleOp"> {
}
```
-Using the `gen-pass-decls` generator, we can generate the much of the
-boilerplater above automatically. This generator takes as an input a `-name`
-parameter, that provides a tag for the group of passes that are being generated.
-This generator produces two chunks of output:
+Using the `gen-pass-decls` generator, we can generate most of the boilerplate
+above automatically. This generator takes as an input a `-name` parameter, that
+provides a tag for the group of passes that are being generated. This generator
+produces two chunks of output:
-The first is the code for registering the declarative passes with the global
+The first is a code block for registering the declarative passes with the global
registry. For each pass, the generator produces a `registerFooPass` where `Foo`
is the name of the definition specified in tablegen. It also generates a
`registerGroupPasses`, where `Group` is the tag provided via the `-name` input
parameter, that registers all of the passes present.
```c++
+// gen-pass-decls -name="Example"
+
#define GEN_PASS_REGISTRATION
#include "Passes.h.inc"
void registerMyPasses() {
- // Register all of our passes.
- registerMyPasses();
+ // Register all of the passes.
+ registerExamplePasses();
// Register `MyPass` specifically.
registerMyPassPass();
}
```
-The second is a base class for each of the passes, with each containing most of
-the boiler plate related to pass definition. These classes are named in the form
-of `MyPassBase`, where `MyPass` is the name of the definition in tablegen. We
+The second is a base class for each of the passes, containing most of the boiler
+plate related to pass definitions. These classes are named in the form of
+`MyPassBase`, where `MyPass` is the name of the pass definition in tablegen. We
can update the original C++ pass definition as so:
```c++
@@ -665,9 +742,15 @@ can update the original C++ pass definition as so:
#define GEN_PASS_CLASSES
#include "Passes.h.inc"
-// Define the main class as deriving from the generated base class.
+/// Define the main class as deriving from the generated base class.
struct MyPass : MyPassBase<MyPass> {
+ /// The explicit constructor is no longer explicitly necessary when defining
+ /// pass options and statistics, the base class takes care of that
+ /// automatically.
...
+
+ /// The definitions of the options and statistics are now generated within
+ /// the base class, but are accessible in the same way.
};
/// Expose this pass to the outside world.
@@ -676,41 +759,42 @@ std::unique_ptr<Pass> foo::createMyPass() {
}
```
-Using the `gen-pass-doc` generator, we can generate markdown documentation for
-each of our passes. See [Passes.md](Passes.md) for example output of real MLIR
-passes.
+Using the `gen-pass-doc` generator, markdown documentation for each of the
+passes can be generated. See [Passes.md](Passes.md) for example output of real
+MLIR passes.
### Tablegen Specification
The `Pass` class is used to begin a new pass definition. This class takes as an
-argument the command line argument to attribute to the pass, as well as an
-optional string corresponding to the operation type that the pass operates on.
-It contains the following fields:
+argument the registry argument to attribute to the pass, as well as an optional
+string corresponding to the operation type that the pass operates on. The class
+contains the following fields:
-* summary
+* `summary`
- A short one line summary of the pass, used as the description when
registering the pass.
-* description
+* `description`
- A longer, more detailed description of the pass. This is used when
generating pass documentation.
-* dependentDialects
- - A list of strings that are the Dialect classes this pass can introduce.
-* constructor
- - A piece of C++ code used to create a default instance of the pass.
-* options
+* `dependentDialects`
+ - A list of strings representing the `Dialect` classes this pass may
+ introduce entities, Attributes/Operations/Types/etc., of.
+* `constructor`
+ - A code block used to create a default instance of the pass.
+* `options`
- A list of pass options used by the pass.
-* statistics
+* `statistics`
- A list of pass statistics used by the pass.
#### Options
-Options can be specified by the `Option` and `ListOption` classes. The `Option`
-class takes the following fields:
+Options may be specified via the `Option` and `ListOption` classes. The `Option`
+class takes the following template parameters:
* C++ variable name
- A name to use for the generated option variable.
* argument
- - The command line argument of the option.
+ - The argument name of the option.
* type
- The C++ type of the option.
* default value
@@ -721,12 +805,21 @@ class takes the following fields:
- A string containing any additional options necessary to construct the
option.
+```tablegen
+def MyPass : Pass<"my-pass"> {
+ let options = [
+ Option<"option", "example-option", "bool", /*default=*/"true",
+ "An example option">,
+ ];
+}
+```
+
The `ListOption` class takes the following fields:
* C++ variable name
- A name to use for the generated option variable.
* argument
- - The command line argument of the option.
+ - The argument name of the option.
* element type
- The C++ type of the list element.
* description
@@ -735,10 +828,20 @@ The `ListOption` class takes the following fields:
- A string containing any additional options necessary to construct the
option.
+```tablegen
+def MyPass : Pass<"my-pass"> {
+ let options = [
+ ListOption<"listOption", "example-list", "int64_t",
+ "An example list option",
+ "llvm::cl::ZeroOrMore, llvm::cl::MiscFlags::CommaSeparated">
+ ];
+}
+```
+
#### Statistic
-Statistics can be specified via the `Statistic`, which takes the following
-fields:
+Statistics may be specified via the `Statistic`, which takes the following
+template parameters:
* C++ variable name
- A name to use for the generated statistic variable.
@@ -747,11 +850,19 @@ fields:
* description
- A one line description of the statistic.
+```tablegen
+def MyPass : Pass<"my-pass"> {
+ let statistics = [
+ Statistic<"statistic", "example-statistic", "An example statistic">
+ ];
+}
+```
+
## Pass Instrumentation
MLIR provides a customizable framework to instrument pass execution and analysis
-computation. This is provided via the `PassInstrumentation` class. This class
-provides hooks into the PassManager that observe various pass events:
+computation, via the `PassInstrumentation` class. This class provides hooks into
+the PassManager that observe various events:
* `runBeforePipeline`
* This callback is run just before a pass pipeline, i.e. pass manager, is
@@ -763,24 +874,27 @@ provides hooks into the PassManager that observe various pass events:
* This callback is run just before a pass is executed.
* `runAfterPass`
* This callback is run right after a pass has been successfully executed.
- If this hook is executed, runAfterPassFailed will not be.
+ If this hook is executed, `runAfterPassFailed` will *not* be.
* `runAfterPassFailed`
* This callback is run right after a pass execution fails. If this hook is
- executed, runAfterPass will not be.
+ executed, `runAfterPass` will *not* be.
* `runBeforeAnalysis`
* This callback is run just before an analysis is computed.
* `runAfterAnalysis`
* This callback is run right after an analysis is computed.
-PassInstrumentation objects can be registered directly with a
+PassInstrumentation instances may be registered directly with a
[PassManager](#pass-manager) instance via the `addInstrumentation` method.
Instrumentations added to the PassManager are run in a stack like fashion, i.e.
the last instrumentation to execute a `runBefore*` hook will be the first to
-execute the respective `runAfter*` hook. Below in an example instrumentation
-that counts the number of times DominanceInfo is computed:
+execute the respective `runAfter*` hook. The hooks of a `PassInstrumentation`
+class are guaranteed to be executed in a thread safe fashion, so additional
+synchronization is not necessary. Below in an example instrumentation that
+counts the number of times the `DominanceInfo` analysis is computed:
```c++
struct DominanceCounterInstrumentation : public PassInstrumentation {
+ /// The cumulative count of how many times dominance has been calculated.
unsigned &count;
DominanceCounterInstrumentation(unsigned &count) : count(count) {}
@@ -809,15 +923,15 @@ llvm::errs() << "DominanceInfo was computed " << domInfoCount << " times!\n";
### Standard Instrumentations
MLIR utilizes the pass instrumentation framework to provide a few useful
-developer tools and utilities. Each of these instrumentations are immediately
+developer tools and utilities. Each of these instrumentations are directly
available to all users of the MLIR pass framework.
#### Pass Timing
The PassTiming instrumentation provides timing information about the execution
of passes and computation of analyses. This provides a quick glimpse into what
-passes are taking the most time to execute, as well as how much of an effect
-your pass has on the total execution time of the pipeline. Users can enable this
+passes are taking the most time to execute, as well as how much of an effect a
+pass has on the total execution time of the pipeline. Users can enable this
instrumentation directly on the PassManager via `enableTiming`. This
instrumentation is also made available in mlir-opt via the `-pass-timing` flag.
The PassTiming instrumentation provides several
diff erent display modes for the
@@ -1014,7 +1128,7 @@ was executing, as well as the initial IR before any passes were run. A potential
reproducible may have the form:
```mlir
-// configuration: -pass-pipeline='func(cse, canonicalize), inline'
+// configuration: -pass-pipeline='func(cse,canonicalize),inline'
// note: verifyPasses=false
module {
More information about the llvm-branch-commits
mailing list