1# Pass Infrastructure
2
3[TOC]
4
5Passes represent the basic infrastructure for transformation and optimization.
6This document provides an overview of the pass infrastructure in MLIR and how to
7use it.
8
9See [MLIR specification](LangRef.md) for more information about MLIR and its
10core aspects, such as the IR structure and operations.
11
12See [MLIR Rewrites](Tutorials/QuickstartRewrites.md) for a quick start on graph
13rewriting in MLIR. If a transformation involves pattern matching operation DAGs,
14this is a great place to start.
15
16## Operation Pass
17
18In MLIR, the main unit of abstraction and transformation is an
19[operation](LangRef.md/#operations). As such, the pass manager is designed to
20work on instances of operations at different levels of nesting. The structure of
21the [pass manager](#pass-manager), and the concept of nesting, is detailed
22further below. All passes in MLIR derive from `OperationPass` and adhere to the
23following restrictions; any noncompliance will lead to problematic behavior in
24multithreaded and other advanced scenarios:
25
26*   Must not modify any state referenced or relied upon outside the current
27    operation being operated on. This includes adding or removing operations
28    from the parent block, changing the attributes(depending on the contract
29    of the current operation)/operands/results/successors of the current operation.
30*   Must not modify the state of another operation not nested within the current
31    operation being operated on.
32    *   Other threads may be operating on these operations simultaneously.
33*   Must not inspect the state of sibling operations.
34    *   Other threads may be modifying these operations in parallel.
35    *   Inspecting the state of ancestor/parent operations is permitted.
36*   Must not maintain mutable pass state across invocations of `runOnOperation`.
37    A pass may be run on many different operations with no guarantee of
38    execution order.
39    *   When multithreading, a specific pass instance may not even execute on
40        all operations within the IR. As such, a pass should not rely on running
41        on all operations.
42*   Must not maintain any global mutable state, e.g. static variables within the
43    source file. All mutable state should be maintained by an instance of the
44    pass.
45*   Must be copy-constructible
46    *   Multiple instances of the pass may be created by the pass manager to
47        process operations in parallel.
48
49### Op-Agnostic Operation Passes
50
51By default, an operation pass is `op-agnostic`, meaning that it operates on the
52operation type of the pass manager that it is added to. This means a pass may operate
53on many different types of operations. Agnostic passes should be written such that
54they do not make assumptions on the operation they run on. Examples of this type of pass are
55[Canonicalization](Pass.md/-canonicalize-canonicalize-operations)
56[Common Sub-Expression Elimination](Passes.md/#-cse-eliminate-common-sub-expressions).
57
58To create an agnostic operation pass, a derived class must adhere to the following:
59
60*   Inherit from the CRTP class `OperationPass`.
61*   Override the virtual `void runOnOperation()` method.
62
63A simple pass may look like:
64
65```c++
66/// Here we utilize the CRTP `PassWrapper` utility class to provide some
67/// necessary utility hooks. This is only necessary for passes defined directly
68/// in C++. Passes defined declaratively use a cleaner mechanism for providing
69/// these utilities.
70struct MyOperationPass : public PassWrapper<MyOperationPass, OperationPass<>> {
71  void runOnOperation() override {
72    // Get the current operation being operated on.
73    Operation *op = getOperation();
74    ...
75  }
76};
77```
78
79### Filtered Operation Pass
80
81If a pass needs to constrain its execution to specific types or classes of operations,
82additional filtering may be applied on top. This transforms a once `agnostic` pass into
83one more specific to a certain context. There are various ways in which to filter the
84execution of a pass, and different contexts in which filtering may apply:
85
86### Operation Pass: Static Schedule Filtering
87
88Static filtering allows for applying additional constraints on the operation types a
89pass may be scheduled on. This type of filtering generally allows for building more
90constrained passes that can only be scheduled on operations that satisfy the necessary
91constraints. For example, this allows for specifying passes that only run on operations
92of a certain, those that provide a certain interface, trait, or some other constraint that
93applies to all instances of that operation type. Below is an example of a pass that only
94permits scheduling on operations that implement `FunctionOpInterface`:
95
96```c++
97struct MyFunctionPass : ... {
98  /// This method is used to provide additional static filtering, and returns if the
99  /// pass may be scheduled on the given operation type.
100  bool canScheduleOn(RegisteredOperationName opInfo) const override {
101    return opInfo.hasInterface<FunctionOpInterface>();
102  }
103
104  void runOnOperation() {
105    // Here we can freely cast to FunctionOpInterface, because our `canScheduleOn` ensures
106    // that our pass is only executed on operations implementing that interface.
107    FunctionOpInterface op = cast<FunctionOpInterface>(getOperation());
108  }
109};
110```
111
112When a pass with static filtering is added to an [`op-specific` pass manager](#oppassmanager),
113it asserts that the operation type of the pass manager satisfies the static constraints of the
114pass. When added to an [`op-agnostic` pass manager](#oppassmanager), that pass manager, and all
115passes contained within, inherits the static constraints of the pass. For example, if the pass
116filters on `FunctionOpInterface`, as in the `MyFunctionPass` example above, only operations that
117implement `FunctionOpInterface` will be considered when executing **any** passes within the pass
118manager. This invariant is important to keep in mind, as each pass added to an `op-agnostic` pass
119manager further constrains the operations that may be scheduled on it. Consider the following example:
120
121```mlir
122func.func @foo() {
123  // ...
124  return
125}
126
127module @someModule {
128  // ...
129}
130```
131
132If we were to apply the op-agnostic pipeline, `any(cse,my-function-pass)`, to the above MLIR snippet
133it would only run on the `foo` function operation. This is because the `my-function-pass` has a
134static filtering constraint to only schedule on operations implementing `FunctionOpInterface`. Remember
135that this constraint is inherited by the entire pass manager, so we never consider `someModule` for
136any of the passes, including `cse` which normally can be scheduled on any operation.
137
138#### Operation Pass: Static Filtering By Op Type
139
140In the above section, we detailed a general mechanism for statically filtering the types of operations
141that a pass may be scheduled on. Sugar is provided on top of that mechanism to simplify the definition
142of passes that are restricted to scheduling on a single operation type. In these cases, a pass simply
143needs to provide the type of operation to the `OperationPass` base class. This will automatically
144instill filtering on that operation type:
145
146```c++
147/// Here we utilize the CRTP `PassWrapper` utility class to provide some
148/// necessary utility hooks. This is only necessary for passes defined directly
149/// in C++. Passes defined declaratively use a cleaner mechanism for providing
150/// these utilities.
151struct MyFunctionPass : public PassWrapper<MyOperationPass, OperationPass<func::FuncOp>> {
152  void runOnOperation() {
153    // Get the current operation being operated on.
154    func::FuncOp op = getOperation();
155  }
156};
157```
158
159#### Operation Pass: Static Filtering By Interface
160
161In the above section, we detailed a general mechanism for statically filtering the types of operations
162that a pass may be scheduled on. Sugar is provided on top of that mechanism to simplify the definition
163of passes that are restricted to scheduling on a specific operation interface. In these cases, a pass
164simply needs to inherit from the `InterfacePass` base class. This class is similar to `OperationPass`,
165but expects the type of interface to operate on. This will automatically instill filtering on that
166interface type:
167
168```c++
169/// Here we utilize the CRTP `PassWrapper` utility class to provide some
170/// necessary utility hooks. This is only necessary for passes defined directly
171/// in C++. Passes defined declaratively use a cleaner mechanism for providing
172/// these utilities.
173struct MyFunctionPass : public PassWrapper<MyOperationPass, InterfacePass<FunctionOpInterface>> {
174  void runOnOperation() {
175    // Get the current operation being operated on.
176    FunctionOpInterface op = getOperation();
177  }
178};
179```
180
181### Dependent Dialects
182
183Dialects must be loaded in the MLIRContext before entities from these dialects
184(operations, types, attributes, ...) can be created. Dialects must also be
185loaded before starting the execution of a multi-threaded pass pipeline. To this
186end, a pass that may create an entity from a dialect that isn't guaranteed to
187already be loaded must express this by overriding the `getDependentDialects()`
188method and declare this list of Dialects explicitly.
189
190### Initialization
191
192In certain situations, a Pass may contain state that is constructed dynamically,
193but is potentially expensive to recompute in successive runs of the Pass. One
194such example is when using [`PDL`-based](Dialects/PDLOps.md)
195[patterns](PatternRewriter.md), which are compiled into a bytecode during
196runtime. In these situations, a pass may override the following hook to
197initialize this heavy state:
198
199*   `LogicalResult initialize(MLIRContext *context)`
200
201This hook is executed once per run of a full pass pipeline, meaning that it does
202not have access to the state available during a `runOnOperation` call. More
203concretely, all necessary accesses to an `MLIRContext` should be driven via the
204provided `context` parameter, and methods that utilize "per-run" state such as
205`getContext`/`getOperation`/`getAnalysis`/etc. must not be used.
206In case of an error during initialization, the pass is expected to emit an error
207diagnostic and return a `failure()` which will abort the pass pipeline execution.
208
209## Analysis Management
210
211An important concept, along with transformation passes, are analyses. These are
212conceptually similar to transformation passes, except that they compute
213information on a specific operation without modifying it. In MLIR, analyses are
214not passes but free-standing classes that are computed lazily on-demand and
215cached to avoid unnecessary recomputation. An analysis in MLIR must adhere to
216the following:
217
218*   Provide a valid constructor taking either an `Operation*` or `Operation*`
219    and `AnalysisManager &`.
220    *   The provided `AnalysisManager &` should be used to query any necessary
221        analysis dependencies.
222*   Must not modify the given operation.
223
224An analysis may provide additional hooks to control various behavior:
225
226*   `bool isInvalidated(const AnalysisManager::PreservedAnalyses &)`
227
228Given a preserved analysis set, the analysis returns true if it should truly be
229invalidated. This allows for more fine-tuned invalidation in cases where an
230analysis wasn't explicitly marked preserved, but may be preserved (or
231invalidated) based upon other properties such as analyses sets. If the analysis
232uses any other analysis as a dependency, it must also check if the dependency
233was invalidated.
234
235### Querying Analyses
236
237The base `OperationPass` class provides utilities for querying and preserving
238analyses for the current operation being processed.
239
240*   OperationPass automatically provides the following utilities for querying
241    analyses:
242    *   `getAnalysis<>`
243        -   Get an analysis for the current operation, constructing it if
244            necessary.
245    *   `getCachedAnalysis<>`
246        -   Get an analysis for the current operation, if it already exists.
247    *   `getCachedParentAnalysis<>`
248        -   Get an analysis for a given parent operation, if it exists.
249    *   `getCachedChildAnalysis<>`
250        -   Get an analysis for a given child operation, if it exists.
251    *   `getChildAnalysis<>`
252        -   Get an analysis for a given child operation, constructing it if
253            necessary.
254
255Using the example passes defined above, let's see some examples:
256
257```c++
258/// An interesting analysis.
259struct MyOperationAnalysis {
260  // Compute this analysis with the provided operation.
261  MyOperationAnalysis(Operation *op);
262};
263
264struct MyOperationAnalysisWithDependency {
265  MyOperationAnalysisWithDependency(Operation *op, AnalysisManager &am) {
266    // Request other analysis as dependency
267    MyOperationAnalysis &otherAnalysis = am.getAnalysis<MyOperationAnalysis>();
268    ...
269  }
270
271  bool isInvalidated(const AnalysisManager::PreservedAnalyses &pa) {
272    // Check if analysis or its dependency were invalidated
273    return !pa.isPreserved<MyOperationAnalysisWithDependency>() ||
274           !pa.isPreserved<MyOperationAnalysis>();
275  }
276};
277
278void MyOperationPass::runOnOperation() {
279  // Query MyOperationAnalysis for the current operation.
280  MyOperationAnalysis &myAnalysis = getAnalysis<MyOperationAnalysis>();
281
282  // Query a cached instance of MyOperationAnalysis for the current operation.
283  // It will not be computed if it doesn't exist.
284  auto optionalAnalysis = getCachedAnalysis<MyOperationAnalysis>();
285  if (optionalAnalysis)
286    ...
287
288  // Query a cached instance of MyOperationAnalysis for the parent operation of
289  // the current operation. It will not be computed if it doesn't exist.
290  auto optionalAnalysis = getCachedParentAnalysis<MyOperationAnalysis>();
291  if (optionalAnalysis)
292    ...
293}
294```
295
296### Preserving Analyses
297
298Analyses that are constructed after being queried by a pass are cached to avoid
299unnecessary computation if they are requested again later. To avoid stale
300analyses, all analyses are assumed to be invalidated by a pass. To avoid
301invalidation, a pass must specifically mark analyses that are known to be
302preserved.
303
304*   All Pass classes automatically provide the following utilities for
305    preserving analyses:
306    *   `markAllAnalysesPreserved`
307    *   `markAnalysesPreserved<>`
308
309```c++
310void MyOperationPass::runOnOperation() {
311  // Mark all analyses as preserved. This is useful if a pass can guarantee
312  // that no transformation was performed.
313  markAllAnalysesPreserved();
314
315  // Mark specific analyses as preserved. This is used if some transformation
316  // was performed, but some analyses were either unaffected or explicitly
317  // preserved.
318  markAnalysesPreserved<MyAnalysis, MyAnalyses...>();
319}
320```
321
322## Pass Failure
323
324Passes in MLIR are allowed to gracefully fail. This may happen if some invariant
325of the pass was broken, potentially leaving the IR in some invalid state. If
326such a situation occurs, the pass can directly signal a failure to the pass
327manager via the `signalPassFailure` method. If a pass signaled a failure when
328executing, no other passes in the pipeline will execute and the top-level call
329to `PassManager::run` will return `failure`.
330
331```c++
332void MyOperationPass::runOnOperation() {
333  // Signal failure on a broken invariant.
334  if (some_broken_invariant)
335    return signalPassFailure();
336}
337```
338
339## Pass Manager
340
341The above sections introduced the different types of passes and their
342invariants. This section introduces the concept of a PassManager, and how it can
343be used to configure and schedule a pass pipeline. There are two main classes
344related to pass management, the `PassManager` and the `OpPassManager`. The
345`PassManager` class acts as the top-level entry point, and contains various
346configurations used for the entire pass pipeline. The `OpPassManager` class is
347used to schedule passes to run at a specific level of nesting. The top-level
348`PassManager` also functions as an `OpPassManager`.
349
350### OpPassManager
351
352An `OpPassManager` is essentially a collection of passes anchored to execute on
353operations at a given level of nesting. A pass manager may be `op-specific`
354(anchored on a specific operation type), or `op-agnostic` (not restricted to any
355specific operation, and executed on any viable operation type). Operation types that
356anchor pass managers must adhere to the following requirement:
357
358*   Must be registered and marked
359    [`IsolatedFromAbove`](Traits.md/#isolatedfromabove).
360
361    *   Passes are expected not to modify operations at or above the current
362        operation being processed. If the operation is not isolated, it may
363        inadvertently modify or traverse the SSA use-list of an operation it is
364        not supposed to.
365
366Passes can be added to a pass manager via `addPass`.
367
368An `OpPassManager` is generally created by explicitly nesting a pipeline within
369another existing `OpPassManager` via the `nest<OpT>` or `nestAny` methods. The
370former method takes the operation type that the nested pass manager will operate on.
371The latter method nests an `op-agnostic` pass manager, that may run on any viable
372operation type. Nesting in this sense, corresponds to the
373[structural](Tutorials/UnderstandingTheIRStructure.md) nesting within
374[Regions](LangRef.md/#regions) of the IR.
375
376For example, the following `.mlir`:
377
378```mlir
379module {
380  spv.module "Logical" "GLSL450" {
381    func @foo() {
382      ...
383    }
384  }
385}
386```
387
388Has the nesting structure of:
389
390```
391`builtin.module`
392  `spv.module`
393    `spv.func`
394```
395
396Below is an example of constructing a pipeline that operates on the above
397structure:
398
399```c++
400// Create a top-level `PassManager` class. If an operation type is not
401// explicitly specific, the default is the builtin `module` operation.
402PassManager pm(ctx);
403// Note: We could also create the above `PassManager` this way.
404PassManager pm(ctx, /*operationName=*/"builtin.module");
405
406// Add a pass on the top-level module operation.
407pm.addPass(std::make_unique<MyModulePass>());
408
409// Nest a pass manager that operates on `spirv.module` operations nested
410// directly under the top-level module.
411OpPassManager &nestedModulePM = pm.nest<spirv::ModuleOp>();
412nestedModulePM.addPass(std::make_unique<MySPIRVModulePass>());
413
414// Nest a pass manager that operates on functions within the nested SPIRV
415// module.
416OpPassManager &nestedFunctionPM = nestedModulePM.nest<func::FuncOp>();
417nestedFunctionPM.addPass(std::make_unique<MyFunctionPass>());
418
419// Nest an op-agnostic pass manager. This will operate on any viable
420// operation, e.g. func.func, spv.func, spv.module, builtin.module, etc.
421OpPassManager &nestedAnyPM = nestedModulePM.nestAny();
422nestedAnyPM.addPass(createCanonicalizePass());
423nestedAnyPM.addPass(createCSEPass());
424
425// Run the pass manager on the top-level module.
426ModuleOp m = ...;
427if (failed(pm.run(m)))
428    ... // One of the passes signaled a failure.
429```
430
431The above pass manager contains the following pipeline structure:
432
433```
434OpPassManager<ModuleOp>
435  MyModulePass
436  OpPassManager<spirv::ModuleOp>
437    MySPIRVModulePass
438    OpPassManager<func::FuncOp>
439      MyFunctionPass
440    OpPassManager<>
441      Canonicalizer
442      CSE
443```
444
445These pipelines are then run over a single operation at a time. This means that,
446for example, given a series of consecutive passes on func::FuncOp, it will execute all
447on the first function, then all on the second function, etc. until the entire
448program has been run through the passes. This provides several benefits:
449
450*   This improves the cache behavior of the compiler, because it is only
451    touching a single function at a time, instead of traversing the entire
452    program.
453*   This improves multi-threading performance by reducing the number of jobs
454    that need to be scheduled, as well as increasing the efficiency of each job.
455    An entire function pipeline can be run on each function asynchronously.
456
457## Dynamic Pass Pipelines
458
459In some situations it may be useful to run a pass pipeline within another pass,
460to allow configuring or filtering based on some invariants of the current
461operation being operated on. For example, the
462[Inliner Pass](Passes.md/#-inline-inline-function-calls) may want to run
463intraprocedural simplification passes while it is inlining to produce a better
464cost model, and provide more optimal inlining. To enable this, passes may run an
465arbitrary `OpPassManager` on the current operation being operated on or any
466operation nested within the current operation via the `LogicalResult
467Pass::runPipeline(OpPassManager &, Operation *)` method. This method returns
468whether the dynamic pipeline succeeded or failed, similarly to the result of the
469top-level `PassManager::run` method. A simple example is shown below:
470
471```c++
472void MyModulePass::runOnOperation() {
473  ModuleOp module = getOperation();
474  if (hasSomeSpecificProperty(module)) {
475    OpPassManager dynamicPM("builtin.module");
476    ...; // Build the dynamic pipeline.
477    if (failed(runPipeline(dynamicPM, module)))
478      return signalPassFailure();
479  }
480}
481```
482
483Note: though above the dynamic pipeline was constructed within the
484`runOnOperation` method, this is not necessary and pipelines should be cached
485when possible as the `OpPassManager` class can be safely copy constructed.
486
487The mechanism described in this section should be used whenever a pass pipeline
488should run in a nested fashion, i.e. when the nested pipeline cannot be
489scheduled statically along with the rest of the main pass pipeline. More
490specifically, a `PassManager` should generally never need to be constructed
491within a `Pass`. Using `runPipeline` also ensures that all analyses,
492[instrumentations](#pass-instrumentation), and other pass manager related
493components are integrated with the dynamic pipeline being executed.
494
495## Instance Specific Pass Options
496
497MLIR provides a builtin mechanism for passes to specify options that configure
498its behavior. These options are parsed at pass construction time independently
499for each instance of the pass. Options are defined using the `Option<>` and
500`ListOption<>` classes, and generally follow the
501[LLVM command line](https://llvm.org/docs/CommandLine.html) flag definition
502rules. One major distinction from the LLVM command line functionality is that
503all `ListOption`s are comma-separated, and delimited sub-ranges within individual
504elements of the list may contain commas that are not treated as separators for the
505top-level list.
506
507```c++
508struct MyPass ... {
509  /// Make sure that we have a valid default constructor and copy constructor to
510  /// ensure that the options are initialized properly.
511  MyPass() = default;
512  MyPass(const MyPass& pass) {}
513
514  /// Any parameters after the description are forwarded to llvm::cl::list and
515  /// llvm::cl::opt respectively.
516  Option<int> exampleOption{*this, "flag-name", llvm::cl::desc("...")};
517  ListOption<int> exampleListOption{*this, "list-flag-name", llvm::cl::desc("...")};
518};
519```
520
521For pass pipelines, the `PassPipelineRegistration` templates take an additional
522template parameter for an optional `Option` struct definition. This struct
523should inherit from `mlir::PassPipelineOptions` and contain the desired pipeline
524options. When using `PassPipelineRegistration`, the constructor now takes a
525function with the signature `void (OpPassManager &pm, const MyPipelineOptions&)`
526which should construct the passes from the options and pass them to the pm:
527
528```c++
529struct MyPipelineOptions : public PassPipelineOptions {
530  // The structure of these options is the same as those for pass options.
531  Option<int> exampleOption{*this, "flag-name", llvm::cl::desc("...")};
532  ListOption<int> exampleListOption{*this, "list-flag-name",
533                                    llvm::cl::desc("...")};
534};
535
536void registerMyPasses() {
537  PassPipelineRegistration<MyPipelineOptions>(
538    "example-pipeline", "Run an example pipeline.",
539    [](OpPassManager &pm, const MyPipelineOptions &pipelineOptions) {
540      // Initialize the pass manager.
541    });
542}
543```
544
545## Pass Statistics
546
547Statistics are a way to keep track of what the compiler is doing and how
548effective various transformations are. It is often useful to see what effect
549specific transformations have on a particular input, and how often they trigger.
550Pass statistics are specific to each pass instance, which allow for seeing the
551effect of placing a particular transformation at specific places within the pass
552pipeline. For example, they help answer questions like "What happens if I run
553CSE again here?".
554
555Statistics can be added to a pass by using the 'Pass::Statistic' class. This
556class takes as a constructor arguments: the parent pass, a name, and a
557description. This class acts like an atomic unsigned integer, and may be
558incremented and updated accordingly. These statistics rely on the same
559infrastructure as
560[`llvm::Statistic`](http://llvm.org/docs/ProgrammersManual.html#the-statistic-class-stats-option)
561and thus have similar usage constraints. Collected statistics can be dumped by
562the [pass manager](#pass-manager) programmatically via
563`PassManager::enableStatistics`; or via `-mlir-pass-statistics` and
564`-mlir-pass-statistics-display` on the command line.
565
566An example is shown below:
567
568```c++
569struct MyPass ... {
570  /// Make sure that we have a valid default constructor and copy constructor to
571  /// ensure that the options are initialized properly.
572  MyPass() = default;
573  MyPass(const MyPass& pass) {}
574  StringRef getArgument() const final {
575    // This is the argument used to refer to the pass in
576    // the textual format (on the commandline for example).
577    return "argument";
578  }
579  StringRef getDescription() const final {
580    // This is a brief description of the pass.
581    return  "description";
582  }
583  /// Define the statistic to track during the execution of MyPass.
584  Statistic exampleStat{this, "exampleStat", "An example statistic"};
585
586  void runOnOperation() {
587    ...
588
589    // Update the statistic after some invariant was hit.
590    ++exampleStat;
591
592    ...
593  }
594};
595```
596
597The collected statistics may be aggregated in two types of views:
598
599A pipeline view that models the structure of the pass manager, this is the
600default view:
601
602```shell
603$ mlir-opt -pass-pipeline='func.func(my-pass,my-pass)' foo.mlir -mlir-pass-statistics
604
605===-------------------------------------------------------------------------===
606                         ... Pass statistics report ...
607===-------------------------------------------------------------------------===
608'func.func' Pipeline
609  MyPass
610    (S) 15 exampleStat - An example statistic
611  VerifierPass
612  MyPass
613    (S)  6 exampleStat - An example statistic
614  VerifierPass
615VerifierPass
616```
617
618A list view that aggregates the statistics of all instances of a specific pass
619together:
620
621```shell
622$ mlir-opt -pass-pipeline='func.func(my-pass, my-pass)' foo.mlir -mlir-pass-statistics -mlir-pass-statistics-display=list
623
624===-------------------------------------------------------------------------===
625                         ... Pass statistics report ...
626===-------------------------------------------------------------------------===
627MyPass
628  (S) 21 exampleStat - An example statistic
629```
630
631## Pass Registration
632
633Briefly shown in the example definitions of the various pass types is the
634`PassRegistration` class. This mechanism allows for registering pass classes so
635that they may be created within a
636[textual pass pipeline description](#textual-pass-pipeline-specification). An
637example registration is shown below:
638
639```c++
640void registerMyPass() {
641  PassRegistration<MyPass>();
642}
643```
644
645*   `MyPass` is the name of the derived pass class.
646*   The pass `getArgument()` method is used to get the identifier that will be
647    used to refer to the pass.
648*   The pass `getDescription()` method provides a short summary describing the
649    pass.
650
651For passes that cannot be default-constructed, `PassRegistration` accepts an
652optional argument that takes a callback to create the pass:
653
654```c++
655void registerMyPass() {
656  PassRegistration<MyParametricPass>(
657    []() -> std::unique_ptr<Pass> {
658      std::unique_ptr<Pass> p = std::make_unique<MyParametricPass>(/*options*/);
659      /*... non-trivial-logic to configure the pass ...*/;
660      return p;
661    });
662}
663```
664
665This variant of registration can be used, for example, to accept the
666configuration of a pass from command-line arguments and pass it to the pass
667constructor.
668
669Note: Make sure that the pass is copy-constructible in a way that does not share
670data as the [pass manager](#pass-manager) may create copies of the pass to run
671in parallel.
672
673### Pass Pipeline Registration
674
675Described above is the mechanism used for registering a specific derived pass
676class. On top of that, MLIR allows for registering custom pass pipelines in a
677similar fashion. This allows for custom pipelines to be available to tools like
678mlir-opt in the same way that passes are, which is useful for encapsulating
679common pipelines like the "-O1" series of passes. Pipelines are registered via a
680similar mechanism to passes in the form of `PassPipelineRegistration`. Compared
681to `PassRegistration`, this class takes an additional parameter in the form of a
682pipeline builder that modifies a provided `OpPassManager`.
683
684```c++
685void pipelineBuilder(OpPassManager &pm) {
686  pm.addPass(std::make_unique<MyPass>());
687  pm.addPass(std::make_unique<MyOtherPass>());
688}
689
690void registerMyPasses() {
691  // Register an existing pipeline builder function.
692  PassPipelineRegistration<>(
693    "argument", "description", pipelineBuilder);
694
695  // Register an inline pipeline builder.
696  PassPipelineRegistration<>(
697    "argument", "description", [](OpPassManager &pm) {
698      pm.addPass(std::make_unique<MyPass>());
699      pm.addPass(std::make_unique<MyOtherPass>());
700    });
701}
702```
703
704### Textual Pass Pipeline Specification
705
706The previous sections detailed how to register passes and pass pipelines with a
707specific argument and description. Once registered, these can be used to
708configure a pass manager from a string description. This is especially useful
709for tools like `mlir-opt`, that configure pass managers from the command line,
710or as options to passes that utilize
711[dynamic pass pipelines](#dynamic-pass-pipelines).
712
713To support the ability to describe the full structure of pass pipelines, MLIR
714supports a custom textual description of pass pipelines. The textual description
715includes the nesting structure, the arguments of the passes and pass pipelines
716to run, and any options for those passes and pipelines. A textual pipeline is
717defined as a series of names, each of which may in itself recursively contain a
718nested pipeline description. The syntax for this specification is as follows:
719
720```ebnf
721pipeline          ::= op-anchor `(` pipeline-element (`,` pipeline-element)* `)`
722pipeline-element  ::= pipeline | (pass-name | pass-pipeline-name) options?
723options           ::= '{' (key ('=' value)?)+ '}'
724```
725
726*   `op-anchor`
727    *   This corresponds to the mnemonic name that anchors the execution of the
728        pass manager. This is either the name of an operation to run passes on,
729        e.g. `func.func` or `builtin.module`, or `any`, for op-agnostic pass
730        managers that execute on any viable operation (i.e. any operation that
731        can be used to anchor a pass manager).
732*   `pass-name` | `pass-pipeline-name`
733    *   This corresponds to the argument of a registered pass or pass pipeline,
734        e.g. `cse` or `canonicalize`.
735*   `options`
736    *   Options are specific key value pairs representing options defined by a
737        pass or pass pipeline, as described in the
738        ["Instance Specific Pass Options"](#instance-specific-pass-options)
739        section. See this section for an example usage in a textual pipeline.
740
741For example, the following pipeline:
742
743```shell
744$ mlir-opt foo.mlir -cse -canonicalize -convert-func-to-llvm='use-bare-ptr-memref-call-conv=1'
745```
746
747Can also be specified as (via the `-pass-pipeline` flag):
748
749```shell
750# Anchor the cse and canonicalize passes on the `func.func` operation.
751$ mlir-opt foo.mlir -pass-pipeline='func.func(cse,canonicalize),convert-func-to-llvm{use-bare-ptr-memref-call-conv=1}'
752
753# Anchor the cse and canonicalize passes on "any" viable root operation.
754$ mlir-opt foo.mlir -pass-pipeline='any(cse,canonicalize),convert-func-to-llvm{use-bare-ptr-memref-call-conv=1}'
755```
756
757In order to support round-tripping a pass to the textual representation using
758`OpPassManager::printAsTextualPipeline(raw_ostream&)`, override `StringRef
759Pass::getArgument()` to specify the argument used when registering a pass.
760
761## Declarative Pass Specification
762
763Some aspects of a Pass may be specified declaratively, in a form similar to
764[operations](OpDefinitions.md). This specification simplifies several mechanisms
765used when defining passes. It can be used for generating pass registration
766calls, defining boilerplate pass utilities, and generating pass documentation.
767
768Consider the following pass specified in C++:
769
770```c++
771struct MyPass : PassWrapper<MyPass, OperationPass<ModuleOp>> {
772  MyPass() = default;
773  MyPass(const MyPass &) {}
774
775  ...
776
777  // Specify any options.
778  Option<bool> option{
779      *this, "example-option",
780      llvm::cl::desc("An example option"), llvm::cl::init(true)};
781  ListOption<int64_t> listOption{
782      *this, "example-list",
783      llvm::cl::desc("An example list option")};
784
785  // Specify any statistics.
786  Statistic statistic{this, "example-statistic", "An example statistic"};
787};
788
789/// Expose this pass to the outside world.
790std::unique_ptr<Pass> foo::createMyPass() {
791  return std::make_unique<MyPass>();
792}
793
794/// Register this pass.
795void foo::registerMyPass() {
796  PassRegistration<MyPass>();
797}
798```
799
800This pass may be specified declaratively as so:
801
802```tablegen
803def MyPass : Pass<"my-pass", "ModuleOp"> {
804  let summary = "My Pass Summary";
805  let description = [{
806    Here we can now give a much larger description of `MyPass`, including all of
807    its various constraints and behavior.
808  }];
809
810  // A constructor must be provided to specify how to create a default instance
811  // of MyPass.
812  let constructor = "foo::createMyPass()";
813
814  // Specify any options.
815  let options = [
816    Option<"option", "example-option", "bool", /*default=*/"true",
817           "An example option">,
818    ListOption<"listOption", "example-list", "int64_t",
819               "An example list option">
820  ];
821
822  // Specify any statistics.
823  let statistics = [
824    Statistic<"statistic", "example-statistic", "An example statistic">
825  ];
826}
827```
828
829Using the `gen-pass-decls` generator, we can generate most of the boilerplate
830above automatically. This generator takes as an input a `-name` parameter, that
831provides a tag for the group of passes that are being generated. This generator
832produces two chunks of output:
833
834The first is a code block for registering the declarative passes with the global
835registry. For each pass, the generator produces a `registerFooPass` where `Foo`
836is the name of the definition specified in tablegen. It also generates a
837`registerGroupPasses`, where `Group` is the tag provided via the `-name` input
838parameter, that registers all of the passes present.
839
840```c++
841// gen-pass-decls -name="Example"
842
843#define GEN_PASS_REGISTRATION
844#include "Passes.h.inc"
845
846void registerMyPasses() {
847  // Register all of the passes.
848  registerExamplePasses();
849
850  // Register `MyPass` specifically.
851  registerMyPassPass();
852}
853```
854
855The second is a base class for each of the passes, containing most of the boiler
856plate related to pass definitions. These classes are named in the form of
857`MyPassBase`, where `MyPass` is the name of the pass definition in tablegen. We
858can update the original C++ pass definition as so:
859
860```c++
861/// Include the generated base pass class definitions.
862#define GEN_PASS_CLASSES
863#include "Passes.h.inc"
864
865/// Define the main class as deriving from the generated base class.
866struct MyPass : MyPassBase<MyPass> {
867  /// The explicit constructor is no longer explicitly necessary when defining
868  /// pass options and statistics, the base class takes care of that
869  /// automatically.
870  ...
871
872  /// The definitions of the options and statistics are now generated within
873  /// the base class, but are accessible in the same way.
874};
875
876/// Expose this pass to the outside world.
877std::unique_ptr<Pass> foo::createMyPass() {
878  return std::make_unique<MyPass>();
879}
880```
881
882Using the `gen-pass-doc` generator, markdown documentation for each of the
883passes can be generated. See [Passes.md](Passes.md) for example output of real
884MLIR passes.
885
886### Tablegen Specification
887
888The `Pass` class is used to begin a new pass definition. This class takes as an
889argument the registry argument to attribute to the pass, as well as an optional
890string corresponding to the operation type that the pass operates on. The class
891contains the following fields:
892
893*   `summary`
894    -   A short one-line summary of the pass, used as the description when
895        registering the pass.
896*   `description`
897    -   A longer, more detailed description of the pass. This is used when
898        generating pass documentation.
899*   `dependentDialects`
900    -   A list of strings representing the `Dialect` classes this pass may
901        introduce entities, Attributes/Operations/Types/etc., of.
902*   `constructor`
903    -   A code block used to create a default instance of the pass.
904*   `options`
905    -   A list of pass options used by the pass.
906*   `statistics`
907    -   A list of pass statistics used by the pass.
908
909#### Options
910
911Options may be specified via the `Option` and `ListOption` classes. The `Option`
912class takes the following template parameters:
913
914*   C++ variable name
915    -   A name to use for the generated option variable.
916*   argument
917    -   The argument name of the option.
918*   type
919    -   The C++ type of the option.
920*   default value
921    -   The default option value.
922*   description
923    -   A one-line description of the option.
924*   additional option flags
925    -   A string containing any additional options necessary to construct the
926        option.
927
928```tablegen
929def MyPass : Pass<"my-pass"> {
930  let options = [
931    Option<"option", "example-option", "bool", /*default=*/"true",
932           "An example option">,
933  ];
934}
935```
936
937The `ListOption` class takes the following fields:
938
939*   C++ variable name
940    -   A name to use for the generated option variable.
941*   argument
942    -   The argument name of the option.
943*   element type
944    -   The C++ type of the list element.
945*   description
946    -   A one-line description of the option.
947*   additional option flags
948    -   A string containing any additional options necessary to construct the
949        option.
950
951```tablegen
952def MyPass : Pass<"my-pass"> {
953  let options = [
954    ListOption<"listOption", "example-list", "int64_t",
955               "An example list option">
956  ];
957}
958```
959
960#### Statistic
961
962Statistics may be specified via the `Statistic`, which takes the following
963template parameters:
964
965*   C++ variable name
966    -   A name to use for the generated statistic variable.
967*   display name
968    -   The name used when displaying the statistic.
969*   description
970    -   A one-line description of the statistic.
971
972```tablegen
973def MyPass : Pass<"my-pass"> {
974  let statistics = [
975    Statistic<"statistic", "example-statistic", "An example statistic">
976  ];
977}
978```
979
980## Pass Instrumentation
981
982MLIR provides a customizable framework to instrument pass execution and analysis
983computation, via the `PassInstrumentation` class. This class provides hooks into
984the PassManager that observe various events:
985
986*   `runBeforePipeline`
987    *   This callback is run just before a pass pipeline, i.e. pass manager, is
988        executed.
989*   `runAfterPipeline`
990    *   This callback is run right after a pass pipeline has been executed,
991        successfully or not.
992*   `runBeforePass`
993    *   This callback is run just before a pass is executed.
994*   `runAfterPass`
995    *   This callback is run right after a pass has been successfully executed.
996        If this hook is executed, `runAfterPassFailed` will *not* be.
997*   `runAfterPassFailed`
998    *   This callback is run right after a pass execution fails. If this hook is
999        executed, `runAfterPass` will *not* be.
1000*   `runBeforeAnalysis`
1001    *   This callback is run just before an analysis is computed.
1002    *   If the analysis requested another analysis as a dependency, the
1003        `runBeforeAnalysis`/`runAfterAnalysis` pair for the dependency can be
1004        called from inside of the current `runBeforeAnalysis`/`runAfterAnalysis`
1005        pair.
1006*   `runAfterAnalysis`
1007    *   This callback is run right after an analysis is computed.
1008
1009PassInstrumentation instances may be registered directly with a
1010[PassManager](#pass-manager) instance via the `addInstrumentation` method.
1011Instrumentations added to the PassManager are run in a stack like fashion, i.e.
1012the last instrumentation to execute a `runBefore*` hook will be the first to
1013execute the respective `runAfter*` hook. The hooks of a `PassInstrumentation`
1014class are guaranteed to be executed in a thread-safe fashion, so additional
1015synchronization is not necessary. Below in an example instrumentation that
1016counts the number of times the `DominanceInfo` analysis is computed:
1017
1018```c++
1019struct DominanceCounterInstrumentation : public PassInstrumentation {
1020  /// The cumulative count of how many times dominance has been calculated.
1021  unsigned &count;
1022
1023  DominanceCounterInstrumentation(unsigned &count) : count(count) {}
1024  void runAfterAnalysis(llvm::StringRef, TypeID id, Operation *) override {
1025    if (id == TypeID::get<DominanceInfo>())
1026      ++count;
1027  }
1028};
1029
1030MLIRContext *ctx = ...;
1031PassManager pm(ctx);
1032
1033// Add the instrumentation to the pass manager.
1034unsigned domInfoCount;
1035pm.addInstrumentation(
1036    std::make_unique<DominanceCounterInstrumentation>(domInfoCount));
1037
1038// Run the pass manager on a module operation.
1039ModuleOp m = ...;
1040if (failed(pm.run(m)))
1041    ...
1042
1043llvm::errs() << "DominanceInfo was computed " << domInfoCount << " times!\n";
1044```
1045
1046### Standard Instrumentations
1047
1048MLIR utilizes the pass instrumentation framework to provide a few useful
1049developer tools and utilities. Each of these instrumentations are directly
1050available to all users of the MLIR pass framework.
1051
1052#### Pass Timing
1053
1054The PassTiming instrumentation provides timing information about the execution
1055of passes and computation of analyses. This provides a quick glimpse into what
1056passes are taking the most time to execute, as well as how much of an effect a
1057pass has on the total execution time of the pipeline. Users can enable this
1058instrumentation directly on the PassManager via `enableTiming`. This
1059instrumentation is also made available in mlir-opt via the `-mlir-timing` flag.
1060The PassTiming instrumentation provides several different display modes for the
1061timing results, each of which is described below:
1062
1063##### List Display Mode
1064
1065In this mode, the results are displayed in a list sorted by total time with each
1066pass/analysis instance aggregated into one unique result. This view is useful
1067for getting an overview of what analyses/passes are taking the most time in a
1068pipeline. This display mode is available in mlir-opt via
1069`-mlir-timing-display=list`.
1070
1071```shell
1072$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline='func.func(cse,canonicalize)' -convert-func-to-llvm -mlir-timing -mlir-timing-display=list
1073
1074===-------------------------------------------------------------------------===
1075                      ... Pass execution timing report ...
1076===-------------------------------------------------------------------------===
1077  Total Execution Time: 0.0203 seconds
1078
1079   ---Wall Time---  --- Name ---
1080   0.0047 ( 55.9%)  Canonicalizer
1081   0.0019 ( 22.2%)  VerifierPass
1082   0.0016 ( 18.5%)  LLVMLoweringPass
1083   0.0003 (  3.4%)  CSE
1084   0.0002 (  1.9%)  (A) DominanceInfo
1085   0.0084 (100.0%)  Total
1086```
1087
1088##### Tree Display Mode
1089
1090In this mode, the results are displayed in a nested pipeline view that mirrors
1091the internal pass pipeline that is being executed in the pass manager. This view
1092is useful for understanding specifically which parts of the pipeline are taking
1093the most time, and can also be used to identify when analyses are being
1094invalidated and recomputed. This is the default display mode.
1095
1096```shell
1097$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline='func.func(cse,canonicalize)' -convert-func-to-llvm -mlir-timing
1098
1099===-------------------------------------------------------------------------===
1100                      ... Pass execution timing report ...
1101===-------------------------------------------------------------------------===
1102  Total Execution Time: 0.0249 seconds
1103
1104   ---Wall Time---  --- Name ---
1105   0.0058 ( 70.8%)  'func.func' Pipeline
1106   0.0004 (  4.3%)    CSE
1107   0.0002 (  2.6%)      (A) DominanceInfo
1108   0.0004 (  4.8%)    VerifierPass
1109   0.0046 ( 55.4%)    Canonicalizer
1110   0.0005 (  6.2%)    VerifierPass
1111   0.0005 (  5.8%)  VerifierPass
1112   0.0014 ( 17.2%)  LLVMLoweringPass
1113   0.0005 (  6.2%)  VerifierPass
1114   0.0082 (100.0%)  Total
1115```
1116
1117##### Multi-threaded Pass Timing
1118
1119When multi-threading is enabled in the pass manager the meaning of the display
1120slightly changes. First, a new timing column is added, `User Time`, that
1121displays the total time spent across all threads. Secondly, the `Wall Time`
1122column displays the longest individual time spent amongst all of the threads.
1123This means that the `Wall Time` column will continue to give an indicator on the
1124perceived time, or clock time, whereas the `User Time` will display the total
1125cpu time.
1126
1127```shell
1128$ mlir-opt foo.mlir -pass-pipeline='func.func(cse,canonicalize)' -convert-func-to-llvm -mlir-timing
1129
1130===-------------------------------------------------------------------------===
1131                      ... Pass execution timing report ...
1132===-------------------------------------------------------------------------===
1133  Total Execution Time: 0.0078 seconds
1134
1135   ---User Time---   ---Wall Time---  --- Name ---
1136   0.0177 ( 88.5%)     0.0057 ( 71.3%)  'func.func' Pipeline
1137   0.0044 ( 22.0%)     0.0015 ( 18.9%)    CSE
1138   0.0029 ( 14.5%)     0.0012 ( 15.2%)      (A) DominanceInfo
1139   0.0038 ( 18.9%)     0.0015 ( 18.7%)    VerifierPass
1140   0.0089 ( 44.6%)     0.0025 ( 31.1%)    Canonicalizer
1141   0.0006 (  3.0%)     0.0002 (  2.6%)    VerifierPass
1142   0.0004 (  2.2%)     0.0004 (  5.4%)  VerifierPass
1143   0.0013 (  6.5%)     0.0013 ( 16.3%)  LLVMLoweringPass
1144   0.0006 (  2.8%)     0.0006 (  7.0%)  VerifierPass
1145   0.0200 (100.0%)     0.0081 (100.0%)  Total
1146```
1147
1148#### IR Printing
1149
1150When debugging it is often useful to dump the IR at various stages of a pass
1151pipeline. This is where the IR printing instrumentation comes into play. This
1152instrumentation allows for conditionally printing the IR before and after pass
1153execution by optionally filtering on the pass being executed. This
1154instrumentation can be added directly to the PassManager via the
1155`enableIRPrinting` method. `mlir-opt` provides a few useful flags for utilizing
1156this instrumentation:
1157
1158*   `mlir-print-ir-before=(comma-separated-pass-list)`
1159    *   Print the IR before each of the passes provided within the pass list.
1160*   `mlir-print-ir-before-all`
1161    *   Print the IR before every pass in the pipeline.
1162
1163```shell
1164$ mlir-opt foo.mlir -pass-pipeline='func.func(cse)' -mlir-print-ir-before=cse
1165
1166*** IR Dump Before CSE ***
1167func.func @simple_constant() -> (i32, i32) {
1168  %c1_i32 = arith.constant 1 : i32
1169  %c1_i32_0 = arith.constant 1 : i32
1170  return %c1_i32, %c1_i32_0 : i32, i32
1171}
1172```
1173
1174*   `mlir-print-ir-after=(comma-separated-pass-list)`
1175    *   Print the IR after each of the passes provided within the pass list.
1176*   `mlir-print-ir-after-all`
1177    *   Print the IR after every pass in the pipeline.
1178
1179```shell
1180$ mlir-opt foo.mlir -pass-pipeline='func.func(cse)' -mlir-print-ir-after=cse
1181
1182*** IR Dump After CSE ***
1183func.func @simple_constant() -> (i32, i32) {
1184  %c1_i32 = arith.constant 1 : i32
1185  return %c1_i32, %c1_i32 : i32, i32
1186}
1187```
1188
1189*   `mlir-print-ir-after-change`
1190    *   Only print the IR after a pass if the pass mutated the IR. This helps to
1191        reduce the number of IR dumps for "uninteresting" passes.
1192    *   Note: Changes are detected by comparing a hash of the operation before
1193        and after the pass. This adds additional run-time to compute the hash of
1194        the IR, and in some rare cases may result in false-positives depending
1195        on the collision rate of the hash algorithm used.
1196    *   Note: This option should be used in unison with one of the other
1197        'mlir-print-ir-after' options above, as this option alone does not enable
1198        printing.
1199
1200```shell
1201$ mlir-opt foo.mlir -pass-pipeline='func.func(cse,cse)' -mlir-print-ir-after=cse -mlir-print-ir-after-change
1202
1203*** IR Dump After CSE ***
1204func.func @simple_constant() -> (i32, i32) {
1205  %c1_i32 = arith.constant 1 : i32
1206  return %c1_i32, %c1_i32 : i32, i32
1207}
1208```
1209
1210*   `mlir-print-ir-after-failure`
1211    *   Only print IR after a pass failure.
1212    *   This option should *not* be used with the other `mlir-print-ir-after` flags
1213        above.
1214
1215```shell
1216$ mlir-opt foo.mlir -pass-pipeline='func.func(cse,bad-pass)' -mlir-print-ir-after-failure
1217
1218*** IR Dump After BadPass Failed ***
1219func.func @simple_constant() -> (i32, i32) {
1220  %c1_i32 = arith.constant 1 : i32
1221  return %c1_i32, %c1_i32 : i32, i32
1222}
1223```
1224
1225*   `mlir-print-ir-module-scope`
1226    *   Always print the top-level module operation, regardless of pass type or
1227        operation nesting level.
1228    *   Note: Printing at module scope should only be used when multi-threading
1229        is disabled(`-mlir-disable-threading`)
1230
1231```shell
1232$ mlir-opt foo.mlir -mlir-disable-threading -pass-pipeline='func.func(cse)' -mlir-print-ir-after=cse -mlir-print-ir-module-scope
1233
1234*** IR Dump After CSE ***  ('func.func' operation: @bar)
1235func.func @bar(%arg0: f32, %arg1: f32) -> f32 {
1236  ...
1237}
1238
1239func.func @simple_constant() -> (i32, i32) {
1240  %c1_i32 = arith.constant 1 : i32
1241  %c1_i32_0 = arith.constant 1 : i32
1242  return %c1_i32, %c1_i32_0 : i32, i32
1243}
1244
1245*** IR Dump After CSE ***  ('func.func' operation: @simple_constant)
1246func.func @bar(%arg0: f32, %arg1: f32) -> f32 {
1247  ...
1248}
1249
1250func.func @simple_constant() -> (i32, i32) {
1251  %c1_i32 = arith.constant 1 : i32
1252  return %c1_i32, %c1_i32 : i32, i32
1253}
1254```
1255
1256## Crash and Failure Reproduction
1257
1258The [pass manager](#pass-manager) in MLIR contains a builtin mechanism to
1259generate reproducibles in the event of a crash, or a
1260[pass failure](#pass-failure). This functionality can be enabled via
1261`PassManager::enableCrashReproducerGeneration` or via the command line flag
1262`mlir-pass-pipeline-crash-reproducer`. In either case, an argument is provided that
1263corresponds to the output `.mlir` file name that the reproducible should be
1264written to. The reproducible contains the configuration of the pass manager that
1265was executing, as well as the initial IR before any passes were run. The reproducer
1266is stored within the assembly format as an external resource. A potential reproducible
1267may have the form:
1268
1269```mlir
1270module {
1271  func.func @foo() {
1272    ...
1273  }
1274}
1275
1276{-#
1277  external_resources: {
1278    mlir_reproducer: {
1279      pipeline: "func.func(cse,canonicalize),inline",
1280      disable_threading: true,
1281      verify_each: true
1282    }
1283  }
1284#-}
1285```
1286
1287The configuration dumped can be passed to `mlir-opt`. This will result in
1288parsing the configuration of the reproducer and adjusting the necessary opt
1289state, e.g. configuring the pass manager, context, etc.
1290
1291Beyond specifying a filename, one can also register a `ReproducerStreamFactory`
1292function that would be invoked in the case of a crash and the reproducer written
1293to its stream.
1294
1295### Local Reproducer Generation
1296
1297An additional flag may be passed to
1298`PassManager::enableCrashReproducerGeneration`, and specified via
1299`mlir-pass-pipeline-local-reproducer` on the command line, that signals that the pass
1300manager should attempt to generate a "local" reproducer. This will attempt to
1301generate a reproducer containing IR right before the pass that fails. This is
1302useful for situations where the crash is known to be within a specific pass, or
1303when the original input relies on components (like dialects or passes) that may
1304not always be available.
1305
1306Note: Local reproducer generation requires that multi-threading is
1307disabled(`-mlir-disable-threading`)
1308
1309For example, if the failure in the previous example came from the `canonicalize` pass,
1310the following reproducer would be generated:
1311
1312```mlir
1313module {
1314  func.func @foo() {
1315    ...
1316  }
1317}
1318
1319{-#
1320  external_resources: {
1321    mlir_reproducer: {
1322      pipeline: "func.func(canonicalize)",
1323      disable_threading: true,
1324      verify_each: true
1325    }
1326  }
1327#-}
1328```
1329