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