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