xref: /llvm-project/mlir/docs/PatternRewriter.md (revision 09dfc5713d7e2342bea4c8447d1ed76c85eb8225)
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