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) { 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) { 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### Initialization 129 130Several pieces of pattern state require explicit initialization by the pattern, 131for example setting `setHasBoundedRewriteRecursion` if a pattern safely handles 132recursive application. This pattern state can be initialized either in the 133constructor of the pattern or via the utility `initialize` hook. Using the 134`initialize` hook removes the need to redefine pattern constructors just to 135inject additional pattern state initialization. An example is shown below: 136 137```c++ 138class MyPattern : public RewritePattern { 139public: 140 /// Inherit the constructors from RewritePattern. 141 using RewritePattern::RewritePattern; 142 143 /// Initialize the pattern. 144 void initialize() { 145 /// Signal that this pattern safely handles recursive application. 146 setHasBoundedRewriteRecursion(); 147 } 148 149 // ... 150}; 151``` 152 153### Construction 154 155Constructing a RewritePattern should be performed by using the static 156`RewritePattern::create<T>` utility method. This method ensures that the pattern 157is properly initialized and prepared for insertion into a `RewritePatternSet`. 158 159## Pattern Rewriter 160 161A `PatternRewriter` is a special class that allows for a pattern to communicate 162with the driver of pattern application. As noted above, *all* IR mutations, 163including creations, are required to be performed via the `PatternRewriter` 164class. This is required because the underlying pattern driver may have state 165that would be invalidated when a mutation takes place. Examples of some of the 166more prevalent `PatternRewriter` API is shown below, please refer to the 167[class documentation](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/IR/PatternMatch.h#L235) 168for a more up-to-date listing of the available API: 169 170* Erase an Operation : `eraseOp` 171 172This method erases an operation that either has no results, or whose results are 173all known to have no uses. 174 175* Notify why a `match` failed : `notifyMatchFailure` 176 177This method allows for providing a diagnostic message within a `matchAndRewrite` 178as to why a pattern failed to match. How this message is displayed back to the 179user is determined by the specific pattern driver. 180 181* Replace an Operation : `replaceOp`/`replaceOpWithNewOp` 182 183This method replaces an operation's results with a set of provided values, and 184erases the operation. 185 186* Update an Operation in-place : `(start|cancel|finalize)RootUpdate` 187 188This is a collection of methods that provide a transaction-like API for updating 189the attributes, location, operands, or successors of an operation in-place 190within a pattern. An in-place update transaction is started with 191`startRootUpdate`, and may either be canceled or finalized with 192`cancelRootUpdate` and `finalizeRootUpdate` respectively. A convenience wrapper, 193`updateRootInPlace`, is provided that wraps a `start` and `finalize` around a 194callback. 195 196* OpBuilder API 197 198The `PatternRewriter` inherits from the `OpBuilder` class, and thus provides all 199of the same functionality present within an `OpBuilder`. This includes operation 200creation, as well as many useful attribute and type construction methods. 201 202## Pattern Application 203 204After a set of patterns have been defined, they are collected and provided to a 205specific driver for application. A driver consists of several high levels parts: 206 207* Input `RewritePatternSet` 208 209The input patterns to a driver are provided in the form of an 210`RewritePatternSet`. This class provides a simplified API for building a 211list of patterns. 212 213* Driver-specific `PatternRewriter` 214 215To ensure that the driver state does not become invalidated by IR mutations 216within the pattern rewriters, a driver must provide a `PatternRewriter` instance 217with the necessary hooks overridden. If a driver does not need to hook into 218certain mutations, a default implementation is provided that will perform the 219mutation directly. 220 221* Pattern Application and Cost Model 222 223Each driver is responsible for defining its own operation visitation order as 224well as pattern cost model, but the final application is performed via a 225`PatternApplicator` class. This class takes as input the 226`RewritePatternSet` and transforms the patterns based upon a provided 227cost model. This cost model computes a final benefit for a given pattern, using 228whatever driver specific information necessary. After a cost model has been 229computed, the driver may begin to match patterns against operations using 230`PatternApplicator::matchAndRewrite`. 231 232An example is shown below: 233 234```c++ 235class MyPattern : public RewritePattern { 236public: 237 MyPattern(PatternBenefit benefit, MLIRContext *context) 238 : RewritePattern(MyOp::getOperationName(), benefit, context) {} 239}; 240 241/// Populate the pattern list. 242void collectMyPatterns(RewritePatternSet &patterns, MLIRContext *ctx) { 243 patterns.add<MyPattern>(/*benefit=*/1, ctx); 244} 245 246/// Define a custom PatternRewriter for use by the driver. 247class MyPatternRewriter : public PatternRewriter { 248public: 249 MyPatternRewriter(MLIRContext *ctx) : PatternRewriter(ctx) {} 250 251 /// Override the necessary PatternRewriter hooks here. 252}; 253 254/// Apply the custom driver to `op`. 255void applyMyPatternDriver(Operation *op, 256 const RewritePatternSet &patterns) { 257 // Initialize the custom PatternRewriter. 258 MyPatternRewriter rewriter(op->getContext()); 259 260 // Create the applicator and apply our cost model. 261 PatternApplicator applicator(patterns); 262 applicator.applyCostModel([](const Pattern &pattern) { 263 // Apply a default cost model. 264 // Note: This is just for demonstration, if the default cost model is truly 265 // desired `applicator.applyDefaultCostModel()` should be used 266 // instead. 267 return pattern.getBenefit(); 268 }); 269 270 // Try to match and apply a pattern. 271 LogicalResult result = applicator.matchAndRewrite(op, rewriter); 272 if (failed(result)) { 273 // ... No patterns were applied. 274 } 275 // ... A pattern was successfully applied. 276} 277``` 278 279## Common Pattern Drivers 280 281MLIR provides several common pattern drivers that serve a variety of different 282use cases. 283 284### Dialect Conversion Driver 285 286This driver provides a framework in which to perform operation conversions 287between, and within dialects using a concept of "legality". This framework 288allows for transforming illegal operations to those supported by a provided 289conversion target, via a set of pattern-based operation rewriting patterns. This 290framework also provides support for type conversions. More information on this 291driver can be found [here](DialectConversion.md). 292 293### Greedy Pattern Rewrite Driver 294 295This driver walks the provided operations and greedily applies the patterns that 296locally have the most benefit. The benefit of 297a pattern is decided solely by the benefit specified on the pattern, and the 298relative order of the pattern within the pattern list (when two patterns have 299the same local benefit). Patterns are iteratively applied to operations until a 300fixed point is reached, at which point the driver finishes. This driver may be 301used via the following: `applyPatternsAndFoldGreedily` and 302`applyOpPatternsAndFold`. The latter of which only applies patterns to the 303provided operation, and will not traverse the IR. 304 305The driver is configurable and supports two modes: 1) you may opt-in to a 306"top-down" traversal, which seeds the worklist with each operation top down and 307in a pre-order over the region tree. This is generally more efficient in 308compile time. 2) the default is a "bottom up" traversal, which builds the 309initial worklist with a postorder traversal of the region tree. This may 310match larger patterns with ambiguous pattern sets. 311 312Note: This driver is the one used by the [canonicalization](Canonicalization.md) 313[pass](Passes.md/#-canonicalize-canonicalize-operations) in MLIR. 314