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