1# Pattern Rewriting : Generic DAG-to-DAG Rewriting 2 3[TOC] 4 5This document details the design and API of the pattern rewriting infrastructure 6present in MLIR, a general DAG-to-DAG transformation framework. This framework 7is widely used throughout MLIR for canonicalization, conversion, and general 8transformation. 9 10For an introduction to DAG-to-DAG transformation, and the rationale behind this 11framework please take a look at the 12[Generic DAG Rewriter Rationale](Rationale/RationaleGenericDAGRewriter.md). 13 14## Introduction 15 16The pattern rewriting framework can largely be decomposed into two parts: 17Pattern Definition and Pattern Application. 18 19## Defining Patterns 20 21Patterns are defined by inheriting from the `RewritePattern` class. This class 22represents the base class of all rewrite patterns within MLIR, and is comprised 23of the following components: 24 25### Benefit 26 27This is the expected benefit of applying a given pattern. This benefit is static 28upon construction of the pattern, but may be computed dynamically at pattern 29initialization time, e.g. allowing the benefit to be derived from domain 30specific information (like the target architecture). This limitation allows for 31performing pattern fusion and compiling patterns into an efficient state 32machine, and 33[Thier, Ertl, and Krall](https://dl.acm.org/citation.cfm?id=3179501) have shown 34that match predicates eliminate the need for dynamically computed costs in 35almost all cases: you can simply instantiate the same pattern one time for each 36possible cost and use the predicate to guard the match. 37 38### Root Operation Name (Optional) 39 40The name of the root operation that this pattern matches against. If specified, 41only operations with the given root name will be provided to the `match` and 42`rewrite` implementation. If not specified, any operation type may be provided. 43The root operation name should be provided whenever possible, because it 44simplifies the analysis of patterns when applying a cost model. To match any 45operation type, a special tag must be provided to make the intent explicit: 46`MatchAnyOpTypeTag`. 47 48### `match` and `rewrite` implementation 49 50This is the chunk of code that matches a given root `Operation` and performs a 51rewrite of the IR. A `RewritePattern` can specify this implementation either via 52separate `match` and `rewrite` methods, or via a combined `matchAndRewrite` 53method. When using the combined `matchAndRewrite` method, no IR mutation should 54take place before the match is deemed successful. The combined `matchAndRewrite` 55is useful when non-trivially recomputable information is required by the 56matching and rewriting phase. See below for examples: 57 58```c++ 59class MyPattern : public RewritePattern { 60public: 61 /// This overload constructs a pattern that only matches operations with the 62 /// root name of `MyOp`. 63 MyPattern(PatternBenefit benefit, MLIRContext *context) 64 : RewritePattern(MyOp::getOperationName(), benefit, context) {} 65 /// This overload constructs a pattern that matches any operation type. 66 MyPattern(PatternBenefit benefit) 67 : RewritePattern(benefit, MatchAnyOpTypeTag()) {} 68 69 /// In this section, the `match` and `rewrite` implementation is specified 70 /// using the separate hooks. 71 LogicalResult match(Operation *op) const override { 72 // The `match` method returns `success()` if the pattern is a match, failure 73 // otherwise. 74 // ... 75 } 76 void rewrite(Operation *op, PatternRewriter &rewriter) const override { 77 // The `rewrite` method performs mutations on the IR rooted at `op` using 78 // the provided rewriter. All mutations must go through the provided 79 // rewriter. 80 } 81 82 /// In this section, the `match` and `rewrite` implementation is specified 83 /// using a single hook. 84 LogicalResult matchAndRewrite(Operation *op, PatternRewriter &rewriter) const override { 85 // The `matchAndRewrite` method performs both the matching and the mutation. 86 // Note that the match must reach a successful point before IR mutation may 87 // take place. 88 } 89}; 90``` 91 92#### Restrictions 93 94Within the `match` section of a pattern, the following constraints apply: 95 96* No mutation of the IR is allowed. 97 98Within the `rewrite` section of a pattern, the following constraints apply: 99 100* All IR mutations, including creation, *must* be performed by the given 101 `PatternRewriter`. This class provides hooks for performing all of the 102 possible mutations that may take place within a pattern. For example, this 103 means that an operation should not be erased via its `erase` method. To 104 erase an operation, the appropriate `PatternRewriter` hook (in this case 105 `eraseOp`) should be used instead. 106* The root operation is required to either be: updated in-place, replaced, or 107 erased. 108 109### Application Recursion 110 111Recursion is an important topic in the context of pattern rewrites, as a pattern 112may often be applicable to its own result. For example, imagine a pattern that 113peels a single iteration from a loop operation. If the loop has multiple 114peelable iterations, this pattern may apply multiple times during the 115application process. By looking at the implementation of this pattern, the bound 116for recursive application may be obvious, e.g. there are no peelable iterations 117within the loop, but from the perspective of the pattern driver this recursion 118is potentially dangerous. Often times the recursive application of a pattern 119indicates a bug in the matching logic. These types of bugs generally do not 120cause crashes, but create infinite loops within the application process. Given 121this, the pattern rewriting infrastructure conservatively assumes that no 122patterns have a proper bounded recursion, and will fail if recursion is 123detected. A pattern that is known to have proper support for handling recursion 124can signal this by calling `setHasBoundedRewriteRecursion` when initializing the 125pattern. This will signal to the pattern driver that recursive application of 126this pattern may happen, and the pattern is equipped to safely handle it. 127 128### Debug Names and Labels 129 130To aid in debugging, patterns may specify: a debug name (via `setDebugName`), 131which should correspond to an identifier that uniquely identifies the specific 132pattern; and a set of debug labels (via `addDebugLabels`), which correspond to 133identifiers that uniquely identify groups of patterns. This information is used 134by various utilities to aid in the debugging of pattern rewrites, e.g. in debug 135logs, to provide pattern filtering, etc. A simple code example is shown below: 136 137```c++ 138class MyPattern : public RewritePattern { 139public: 140 /// Inherit constructors from RewritePattern. 141 using RewritePattern::RewritePattern; 142 143 void initialize() { 144 setDebugName("MyPattern"); 145 addDebugLabels("MyRewritePass"); 146 } 147 148 // ... 149}; 150 151void populateMyPatterns(RewritePatternSet &patterns, MLIRContext *ctx) { 152 // Debug labels may also be attached to patterns during insertion. This allows 153 // for easily attaching common labels to groups of patterns. 154 patterns.addWithLabel<MyPattern, ...>("MyRewritePatterns", ctx); 155} 156``` 157 158### Initialization 159 160Several pieces of pattern state require explicit initialization by the pattern, 161for example setting `setHasBoundedRewriteRecursion` if a pattern safely handles 162recursive application. This pattern state can be initialized either in the 163constructor of the pattern or via the utility `initialize` hook. Using the 164`initialize` hook removes the need to redefine pattern constructors just to 165inject additional pattern state initialization. An example is shown below: 166 167```c++ 168class MyPattern : public RewritePattern { 169public: 170 /// Inherit the constructors from RewritePattern. 171 using RewritePattern::RewritePattern; 172 173 /// Initialize the pattern. 174 void initialize() { 175 /// Signal that this pattern safely handles recursive application. 176 setHasBoundedRewriteRecursion(); 177 } 178 179 // ... 180}; 181``` 182 183### Construction 184 185Constructing a RewritePattern should be performed by using the static 186`RewritePattern::create<T>` utility method. This method ensures that the pattern 187is properly initialized and prepared for insertion into a `RewritePatternSet`. 188 189## Pattern Rewriter 190 191A `PatternRewriter` is a special class that allows for a pattern to communicate 192with the driver of pattern application. As noted above, *all* IR mutations, 193including creations, are required to be performed via the `PatternRewriter` 194class. This is required because the underlying pattern driver may have state 195that would be invalidated when a mutation takes place. Examples of some of the 196more prevalent `PatternRewriter` API is shown below, please refer to the 197[class documentation](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/PatternMatch.h#L235) 198for a more up-to-date listing of the available API: 199 200* Erase an Operation : `eraseOp` 201 202This method erases an operation that either has no results, or whose results are 203all known to have no uses. 204 205* Notify why a `match` failed : `notifyMatchFailure` 206 207This method allows for providing a diagnostic message within a `matchAndRewrite` 208as to why a pattern failed to match. How this message is displayed back to the 209user is determined by the specific pattern driver. 210 211* Replace an Operation : `replaceOp`/`replaceOpWithNewOp` 212 213This method replaces an operation's results with a set of provided values, and 214erases the operation. 215 216* Update an Operation in-place : `(start|cancel|finalize)OpModification` 217 218This is a collection of methods that provide a transaction-like API for updating 219the attributes, location, operands, or successors of an operation in-place 220within a pattern. An in-place update transaction is started with 221`startOpModification`, and may either be canceled or finalized with 222`cancelOpModification` and `finalizeOpModification` respectively. A convenience 223wrapper, `modifyOpInPlace`, is provided that wraps a `start` and `finalize` 224around a callback. 225 226* OpBuilder API 227 228The `PatternRewriter` inherits from the `OpBuilder` class, and thus provides all 229of the same functionality present within an `OpBuilder`. This includes operation 230creation, as well as many useful attribute and type construction methods. 231 232## Pattern Application 233 234After a set of patterns have been defined, they are collected and provided to a 235specific driver for application. A driver consists of several high level parts: 236 237* Input `RewritePatternSet` 238 239The input patterns to a driver are provided in the form of an 240`RewritePatternSet`. This class provides a simplified API for building a 241list of patterns. 242 243* Driver-specific `PatternRewriter` 244 245To ensure that the driver state does not become invalidated by IR mutations 246within the pattern rewriters, a driver must provide a `PatternRewriter` instance 247with the necessary hooks overridden. If a driver does not need to hook into 248certain mutations, a default implementation is provided that will perform the 249mutation directly. 250 251* Pattern Application and Cost Model 252 253Each driver is responsible for defining its own operation visitation order as 254well as pattern cost model, but the final application is performed via a 255`PatternApplicator` class. This class takes as input the 256`RewritePatternSet` and transforms the patterns based upon a provided 257cost model. This cost model computes a final benefit for a given pattern, using 258whatever driver specific information necessary. After a cost model has been 259computed, the driver may begin to match patterns against operations using 260`PatternApplicator::matchAndRewrite`. 261 262An example is shown below: 263 264```c++ 265class MyPattern : public RewritePattern { 266public: 267 MyPattern(PatternBenefit benefit, MLIRContext *context) 268 : RewritePattern(MyOp::getOperationName(), benefit, context) {} 269}; 270 271/// Populate the pattern list. 272void collectMyPatterns(RewritePatternSet &patterns, MLIRContext *ctx) { 273 patterns.add<MyPattern>(/*benefit=*/1, ctx); 274} 275 276/// Define a custom PatternRewriter for use by the driver. 277class MyPatternRewriter : public PatternRewriter { 278public: 279 MyPatternRewriter(MLIRContext *ctx) : PatternRewriter(ctx) {} 280 281 /// Override the necessary PatternRewriter hooks here. 282}; 283 284/// Apply the custom driver to `op`. 285void applyMyPatternDriver(Operation *op, 286 const FrozenRewritePatternSet &patterns) { 287 // Initialize the custom PatternRewriter. 288 MyPatternRewriter rewriter(op->getContext()); 289 290 // Create the applicator and apply our cost model. 291 PatternApplicator applicator(patterns); 292 applicator.applyCostModel([](const Pattern &pattern) { 293 // Apply a default cost model. 294 // Note: This is just for demonstration, if the default cost model is truly 295 // desired `applicator.applyDefaultCostModel()` should be used 296 // instead. 297 return pattern.getBenefit(); 298 }); 299 300 // Try to match and apply a pattern. 301 LogicalResult result = applicator.matchAndRewrite(op, rewriter); 302 if (failed(result)) { 303 // ... No patterns were applied. 304 } 305 // ... A pattern was successfully applied. 306} 307``` 308 309## Common Pattern Drivers 310 311MLIR provides several common pattern drivers that serve a variety of different 312use cases. 313 314### Dialect Conversion Driver 315 316This driver provides a framework in which to perform operation conversions 317between, and within dialects using a concept of "legality". This framework 318allows for transforming illegal operations to those supported by a provided 319conversion target, via a set of pattern-based operation rewriting patterns. This 320framework also provides support for type conversions. More information on this 321driver can be found [here](DialectConversion.md). 322 323### Walk Pattern Rewrite Driver 324 325This is a fast and simple driver that walks the given op and applies patterns 326that locally have the most benefit. The benefit of a pattern is decided solely 327by the benefit specified on the pattern, and the relative order of the pattern 328within the pattern list (when two patterns have the same local benefit). 329 330The driver performs a post-order traversal. Note that it walks regions of the 331given op but does not visit the op. 332 333This driver does not (re)visit modified or newly replaced ops, and does not 334allow for progressive rewrites of the same op. Op and block erasure is only 335supported for the currently matched op and its descendant. If your pattern 336set requires these, consider using the Greedy Pattern Rewrite Driver instead, 337at the expense of extra overhead. 338 339This driver is exposed using the `walkAndApplyPatterns` function. 340 341Note: This driver listens for IR changes via the callbacks provided by 342`RewriterBase`. It is important that patterns announce all IR changes to the 343rewriter and do not bypass the rewriter API by modifying ops directly. 344 345#### Debugging 346 347You can debug the Walk Pattern Rewrite Driver by passing the 348`--debug-only=walk-rewriter` CLI flag. This will print the visited and matched 349ops. 350 351### Greedy Pattern Rewrite Driver 352 353This driver processes ops in a worklist-driven fashion and greedily applies the 354patterns that locally have the most benefit (same as the Walk Pattern Rewrite 355Driver). Patterns are iteratively applied to operations until a fixed point is 356reached or until the configurable maximum number of iterations exhausted, at 357which point the driver finishes. 358 359This driver comes in two fashions: 360 361* `applyPatternsGreedily` ("region-based driver") applies patterns to 362 all ops in a given region or a given container op (but not the container op 363 itself). I.e., the worklist is initialized with all containing ops. 364* `applyOpPatternsAndFold` ("op-based driver") applies patterns to the 365 provided list of operations. I.e., the worklist is initialized with the 366 specified list of ops. 367 368The driver is configurable via `GreedyRewriteConfig`. The region-based driver 369supports two modes for populating the initial worklist: 370 371* Top-down traversal: Traverse the container op/region top down and in 372 pre-order. This is generally more efficient in compile time. 373* Bottom-up traversal: This is the default setting. It builds the initial 374 worklist with a postorder traversal and then reverses the worklist. This may 375 match larger patterns with ambiguous pattern sets. 376 377By default, ops that were modified in-place and newly created are added back to 378the worklist. Ops that are outside of the configurable "scope" of the driver are 379not added to the worklist. Furthermore, "strict mode" can exclude certain ops 380from being added to the worklist throughout the rewrite process: 381 382* `GreedyRewriteStrictness::AnyOp`: No ops are excluded (apart from the ones 383 that are out of scope). 384* `GreedyRewriteStrictness::ExistingAndNewOps`: Only pre-existing ops (with 385 which the worklist was initialized) and newly created ops are added to the 386 worklist. 387* `GreedyRewriteStrictness::ExistingOps`: Only pre-existing ops (with which 388 the worklist was initialized) are added to the worklist. 389 390Note: This driver listens for IR changes via the callbacks provided by 391`RewriterBase`. It is important that patterns announce all IR changes to the 392rewriter and do not bypass the rewriter API by modifying ops directly. 393 394Note: This driver is the one used by the [canonicalization](Canonicalization.md) 395[pass](Passes.md/#-canonicalize) in MLIR. 396 397#### Debugging 398 399To debug the execution of the greedy pattern rewrite driver, 400`-debug-only=greedy-rewriter` may be used. This command line flag activates 401LLVM's debug logging infrastructure solely for the greedy pattern rewriter. The 402output is formatted as a tree structure, mirroring the structure of the pattern 403application process. This output contains all of the actions performed by the 404rewriter, how operations get processed and patterns are applied, and why they 405fail. 406 407Example output is shown below: 408 409``` 410//===-------------------------------------------===// 411Processing operation : 'cf.cond_br'(0x60f000001120) { 412 "cf.cond_br"(%arg0)[^bb2, ^bb2] {operandSegmentSizes = array<i32: 1, 0, 0>} : (i1) -> () 413 414 * Pattern SimplifyConstCondBranchPred : 'cf.cond_br -> ()' { 415 } -> failure : pattern failed to match 416 417 * Pattern SimplifyCondBranchIdenticalSuccessors : 'cf.cond_br -> ()' { 418 ** Insert : 'cf.br'(0x60b000003690) 419 ** Replace : 'cf.cond_br'(0x60f000001120) 420 } -> success : pattern applied successfully 421} -> success : pattern matched 422//===-------------------------------------------===// 423``` 424 425This output is describing the processing of a `cf.cond_br` operation. We first 426try to apply the `SimplifyConstCondBranchPred`, which fails. From there, another 427pattern (`SimplifyCondBranchIdenticalSuccessors`) is applied that matches the 428`cf.cond_br` and replaces it with a `cf.br`. 429 430## Debugging 431 432### Pattern Filtering 433 434To simplify test case definition and reduction, the `FrozenRewritePatternSet` 435class provides built-in support for filtering which patterns should be provided 436to the pattern driver for application. Filtering behavior is specified by 437providing a `disabledPatterns` and `enabledPatterns` list when constructing the 438`FrozenRewritePatternSet`. The `disabledPatterns` list should contain a set of 439debug names or labels for patterns that are disabled during pattern application, 440i.e. which patterns should be filtered out. The `enabledPatterns` list should 441contain a set of debug names or labels for patterns that are enabled during 442pattern application, patterns that do not satisfy this constraint are filtered 443out. Note that patterns specified by the `disabledPatterns` list will be 444filtered out even if they match criteria in the `enabledPatterns` list. An 445example is shown below: 446 447```c++ 448void MyPass::initialize(MLIRContext *context) { 449 // No patterns are explicitly disabled. 450 SmallVector<std::string> disabledPatterns; 451 // Enable only patterns with a debug name or label of `MyRewritePatterns`. 452 SmallVector<std::string> enabledPatterns(1, "MyRewritePatterns"); 453 454 RewritePatternSet rewritePatterns(context); 455 // ... 456 frozenPatterns = FrozenRewritePatternSet(rewritePatterns, disabledPatterns, 457 enabledPatterns); 458} 459``` 460 461### Common Pass Utilities 462 463Passes that utilize rewrite patterns should aim to provide a common set of 464options and toggles to simplify the debugging experience when switching between 465different passes/projects/etc. To aid in this endeavor, MLIR provides a common 466set of utilities that can be easily included when defining a custom pass. These 467are defined in `mlir/Rewrite/PassUtil.td`; an example usage is shown below: 468 469```tablegen 470def MyRewritePass : Pass<"..."> { 471 let summary = "..."; 472 let constructor = "createMyRewritePass()"; 473 474 // Inherit the common pattern rewrite options from `RewritePassUtils`. 475 let options = RewritePassUtils.options; 476} 477``` 478 479#### Rewrite Pass Options 480 481This section documents common pass options that are useful for controlling the 482behavior of rewrite pattern application. 483 484##### Pattern Filtering 485 486Two common pattern filtering options are exposed, `disable-patterns` and 487`enable-patterns`, matching the behavior of the `disabledPatterns` and 488`enabledPatterns` lists described in the [Pattern Filtering](#pattern-filtering) 489section above. A snippet of the tablegen definition of these options is shown 490below: 491 492```tablegen 493ListOption<"disabledPatterns", "disable-patterns", "std::string", 494 "Labels of patterns that should be filtered out during application">, 495ListOption<"enabledPatterns", "enable-patterns", "std::string", 496 "Labels of patterns that should be used during application, all " 497 "other patterns are filtered out">, 498``` 499 500These options may be used to provide filtering behavior when constructing any 501`FrozenRewritePatternSet`s within the pass: 502 503```c++ 504void MyRewritePass::initialize(MLIRContext *context) { 505 RewritePatternSet rewritePatterns(context); 506 // ... 507 508 // When constructing the `FrozenRewritePatternSet`, we provide the filter 509 // list options. 510 frozenPatterns = FrozenRewritePatternSet(rewritePatterns, disabledPatterns, 511 enabledPatterns); 512} 513``` 514