1 # Pattern Rewriting : Generic DAG-to-DAG Rewriting
5 This document details the design and API of the pattern rewriting infrastructure
6 present in MLIR, a general DAG-to-DAG transformation framework. This framework
7 is widely used throughout MLIR for canonicalization, conversion, and general
10 For an introduction to DAG-to-DAG transformation, and the rationale behind this
11 framework please take a look at the
12 [Generic DAG Rewriter Rationale](Rationale/RationaleGenericDAGRewriter.md).
16 The pattern rewriting framework can largely be decomposed into two parts:
17 Pattern Definition and Pattern Application.
21 Patterns are defined by inheriting from the `RewritePattern` class. This class
22 represents the base class of all rewrite patterns within MLIR, and is comprised
23 of the following components:
27 This is the expected benefit of applying a given pattern. This benefit is static
28 upon construction of the pattern, but may be computed dynamically at pattern
29 initialization time, e.g. allowing the benefit to be derived from domain
30 specific information (like the target architecture). This limitation allows for
31 performing pattern fusion and compiling patterns into an efficient state
33 [Thier, Ertl, and Krall](https://dl.acm.org/citation.cfm?id=3179501) have shown
34 that match predicates eliminate the need for dynamically computed costs in
35 almost all cases: you can simply instantiate the same pattern one time for each
36 possible cost and use the predicate to guard the match.
38 ### Root Operation Name (Optional)
40 The name of the root operation that this pattern matches against. If specified,
41 only 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.
43 The root operation name should be provided whenever possible, because it
44 simplifies the analysis of patterns when applying a cost model. To match any
45 operation type, a special tag must be provided to make the intent explicit:
48 ### `match` and `rewrite` implementation
50 This is the chunk of code that matches a given root `Operation` and performs a
51 rewrite of the IR. A `RewritePattern` can specify this implementation either via
52 separate `match` and `rewrite` methods, or via a combined `matchAndRewrite`
53 method. When using the combined `matchAndRewrite` method, no IR mutation should
54 take place before the match is deemed successful. The combined `matchAndRewrite`
55 is useful when non-trivially recomputable information is required by the
56 matching and rewriting phase. See below for examples:
59 class MyPattern : public RewritePattern {
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()) {}
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
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
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
94 Within the `match` section of a pattern, the following constraints apply:
96 * No mutation of the IR is allowed.
98 Within the `rewrite` section of a pattern, the following constraints apply:
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
109 ### Application Recursion
111 Recursion is an important topic in the context of pattern rewrites, as a pattern
112 may often be applicable to its own result. For example, imagine a pattern that
113 peels a single iteration from a loop operation. If the loop has multiple
114 peelable iterations, this pattern may apply multiple times during the
115 application process. By looking at the implementation of this pattern, the bound
116 for recursive application may be obvious, e.g. there are no peelable iterations
117 within the loop, but from the perspective of the pattern driver this recursion
118 is potentially dangerous. Often times the recursive application of a pattern
119 indicates a bug in the matching logic. These types of bugs generally do not
120 cause crashes, but create infinite loops within the application process. Given
121 this, the pattern rewriting infrastructure conservatively assumes that no
122 patterns have a proper bounded recursion, and will fail if recursion is
123 detected. A pattern that is known to have proper support for handling recursion
124 can signal this by calling `setHasBoundedRewriteRecursion` when initializing the
125 pattern. This will signal to the pattern driver that recursive application of
126 this pattern may happen, and the pattern is equipped to safely handle it.
128 ### Debug Names and Labels
130 To aid in debugging, patterns may specify: a debug name (via `setDebugName`),
131 which should correspond to an identifier that uniquely identifies the specific
132 pattern; and a set of debug labels (via `addDebugLabels`), which correspond to
133 identifiers that uniquely identify groups of patterns. This information is used
134 by various utilities to aid in the debugging of pattern rewrites, e.g. in debug
135 logs, to provide pattern filtering, etc. A simple code example is shown below:
138 class MyPattern : public RewritePattern {
140 /// Inherit constructors from RewritePattern.
141 using RewritePattern::RewritePattern;
144 setDebugName("MyPattern");
145 addDebugLabels("MyRewritePass");
151 void 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);
160 Several pieces of pattern state require explicit initialization by the pattern,
161 for example setting `setHasBoundedRewriteRecursion` if a pattern safely handles
162 recursive application. This pattern state can be initialized either in the
163 constructor of the pattern or via the utility `initialize` hook. Using the
164 `initialize` hook removes the need to redefine pattern constructors just to
165 inject additional pattern state initialization. An example is shown below:
168 class MyPattern : public RewritePattern {
170 /// Inherit the constructors from RewritePattern.
171 using RewritePattern::RewritePattern;
173 /// Initialize the pattern.
175 /// Signal that this pattern safely handles recursive application.
176 setHasBoundedRewriteRecursion();
185 Constructing a RewritePattern should be performed by using the static
186 `RewritePattern::create<T>` utility method. This method ensures that the pattern
187 is properly initialized and prepared for insertion into a `RewritePatternSet`.
191 A `PatternRewriter` is a special class that allows for a pattern to communicate
192 with the driver of pattern application. As noted above, *all* IR mutations,
193 including creations, are required to be performed via the `PatternRewriter`
194 class. This is required because the underlying pattern driver may have state
195 that would be invalidated when a mutation takes place. Examples of some of the
196 more 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)
198 for a more up-to-date listing of the available API:
200 * Erase an Operation : `eraseOp`
202 This method erases an operation that either has no results, or whose results are
203 all known to have no uses.
205 * Notify why a `match` failed : `notifyMatchFailure`
207 This method allows for providing a diagnostic message within a `matchAndRewrite`
208 as to why a pattern failed to match. How this message is displayed back to the
209 user is determined by the specific pattern driver.
211 * Replace an Operation : `replaceOp`/`replaceOpWithNewOp`
213 This method replaces an operation's results with a set of provided values, and
214 erases the operation.
216 * Update an Operation in-place : `(start|cancel|finalize)OpModification`
218 This is a collection of methods that provide a transaction-like API for updating
219 the attributes, location, operands, or successors of an operation in-place
220 within 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
223 wrapper, `modifyOpInPlace`, is provided that wraps a `start` and `finalize`
228 The `PatternRewriter` inherits from the `OpBuilder` class, and thus provides all
229 of the same functionality present within an `OpBuilder`. This includes operation
230 creation, as well as many useful attribute and type construction methods.
232 ## Pattern Application
234 After a set of patterns have been defined, they are collected and provided to a
235 specific driver for application. A driver consists of several high level parts:
237 * Input `RewritePatternSet`
239 The input patterns to a driver are provided in the form of an
240 `RewritePatternSet`. This class provides a simplified API for building a
243 * Driver-specific `PatternRewriter`
245 To ensure that the driver state does not become invalidated by IR mutations
246 within the pattern rewriters, a driver must provide a `PatternRewriter` instance
247 with the necessary hooks overridden. If a driver does not need to hook into
248 certain mutations, a default implementation is provided that will perform the
251 * Pattern Application and Cost Model
253 Each driver is responsible for defining its own operation visitation order as
254 well 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
257 cost model. This cost model computes a final benefit for a given pattern, using
258 whatever driver specific information necessary. After a cost model has been
259 computed, the driver may begin to match patterns against operations using
260 `PatternApplicator::matchAndRewrite`.
262 An example is shown below:
265 class MyPattern : public RewritePattern {
267 MyPattern(PatternBenefit benefit, MLIRContext *context)
268 : RewritePattern(MyOp::getOperationName(), benefit, context) {}
271 /// Populate the pattern list.
272 void collectMyPatterns(RewritePatternSet &patterns, MLIRContext *ctx) {
273 patterns.add<MyPattern>(/*benefit=*/1, ctx);
276 /// Define a custom PatternRewriter for use by the driver.
277 class MyPatternRewriter : public PatternRewriter {
279 MyPatternRewriter(MLIRContext *ctx) : PatternRewriter(ctx) {}
281 /// Override the necessary PatternRewriter hooks here.
284 /// Apply the custom driver to `op`.
285 void applyMyPatternDriver(Operation *op,
286 const FrozenRewritePatternSet &patterns) {
287 // Initialize the custom PatternRewriter.
288 MyPatternRewriter rewriter(op->getContext());
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
297 return pattern.getBenefit();
300 // Try to match and apply a pattern.
301 LogicalResult result = applicator.matchAndRewrite(op, rewriter);
302 if (failed(result)) {
303 // ... No patterns were applied.
305 // ... A pattern was successfully applied.
309 ## Common Pattern Drivers
311 MLIR provides several common pattern drivers that serve a variety of different
314 ### Dialect Conversion Driver
316 This driver provides a framework in which to perform operation conversions
317 between, and within dialects using a concept of "legality". This framework
318 allows for transforming illegal operations to those supported by a provided
319 conversion target, via a set of pattern-based operation rewriting patterns. This
320 framework also provides support for type conversions. More information on this
321 driver can be found [here](DialectConversion.md).
323 ### Walk Pattern Rewrite Driver
325 This is a fast and simple driver that walks the given op and applies patterns
326 that locally have the most benefit. The benefit of a pattern is decided solely
327 by the benefit specified on the pattern, and the relative order of the pattern
328 within the pattern list (when two patterns have the same local benefit).
330 The driver performs a post-order traversal. Note that it walks regions of the
331 given op but does not visit the op.
333 This driver does not (re)visit modified or newly replaced ops, and does not
334 allow for progressive rewrites of the same op. Op and block erasure is only
335 supported for the currently matched op and its descendant. If your pattern
336 set requires these, consider using the Greedy Pattern Rewrite Driver instead,
337 at the expense of extra overhead.
339 This driver is exposed using the `walkAndApplyPatterns` function.
341 Note: This driver listens for IR changes via the callbacks provided by
342 `RewriterBase`. It is important that patterns announce all IR changes to the
343 rewriter and do not bypass the rewriter API by modifying ops directly.
347 You can debug the Walk Pattern Rewrite Driver by passing the
348 `--debug-only=walk-rewriter` CLI flag. This will print the visited and matched
351 ### Greedy Pattern Rewrite Driver
353 This driver processes ops in a worklist-driven fashion and greedily applies the
354 patterns that locally have the most benefit (same as the Walk Pattern Rewrite
355 Driver). Patterns are iteratively applied to operations until a fixed point is
356 reached or until the configurable maximum number of iterations exhausted, at
357 which point the driver finishes.
359 This driver comes in two fashions:
361 * `applyPatternsAndFoldGreedily` ("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.
368 The driver is configurable via `GreedyRewriteConfig`. The region-based driver
369 supports two modes for populating the initial worklist:
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.
377 By default, ops that were modified in-place and newly created are added back to
378 the worklist. Ops that are outside of the configurable "scope" of the driver are
379 not added to the worklist. Furthermore, "strict mode" can exclude certain ops
380 from being added to the worklist throughout the rewrite process:
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
387 * `GreedyRewriteStrictness::ExistingOps`: Only pre-existing ops (with which
388 the worklist was initialized) are added to the worklist.
390 Note: This driver listens for IR changes via the callbacks provided by
391 `RewriterBase`. It is important that patterns announce all IR changes to the
392 rewriter and do not bypass the rewriter API by modifying ops directly.
394 Note: This driver is the one used by the [canonicalization](Canonicalization.md)
395 [pass](Passes.md/#-canonicalize) in MLIR.
399 To debug the execution of the greedy pattern rewrite driver,
400 `-debug-only=greedy-rewriter` may be used. This command line flag activates
401 LLVM's debug logging infrastructure solely for the greedy pattern rewriter. The
402 output is formatted as a tree structure, mirroring the structure of the pattern
403 application process. This output contains all of the actions performed by the
404 rewriter, how operations get processed and patterns are applied, and why they
407 Example output is shown below:
410 //===-------------------------------------------===//
411 Processing operation : 'cf.cond_br'(0x60f000001120) {
412 "cf.cond_br"(%arg0)[^bb2, ^bb2] {operandSegmentSizes = array<i32: 1, 0, 0>} : (i1) -> ()
414 * Pattern SimplifyConstCondBranchPred : 'cf.cond_br -> ()' {
415 } -> failure : pattern failed to match
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 //===-------------------------------------------===//
425 This output is describing the processing of a `cf.cond_br` operation. We first
426 try to apply the `SimplifyConstCondBranchPred`, which fails. From there, another
427 pattern (`SimplifyCondBranchIdenticalSuccessors`) is applied that matches the
428 `cf.cond_br` and replaces it with a `cf.br`.
432 ### Pattern Filtering
434 To simplify test case definition and reduction, the `FrozenRewritePatternSet`
435 class provides built-in support for filtering which patterns should be provided
436 to the pattern driver for application. Filtering behavior is specified by
437 providing a `disabledPatterns` and `enabledPatterns` list when constructing the
438 `FrozenRewritePatternSet`. The `disabledPatterns` list should contain a set of
439 debug names or labels for patterns that are disabled during pattern application,
440 i.e. which patterns should be filtered out. The `enabledPatterns` list should
441 contain a set of debug names or labels for patterns that are enabled during
442 pattern application, patterns that do not satisfy this constraint are filtered
443 out. Note that patterns specified by the `disabledPatterns` list will be
444 filtered out even if they match criteria in the `enabledPatterns` list. An
445 example is shown below:
448 void 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");
454 RewritePatternSet rewritePatterns(context);
456 frozenPatterns = FrozenRewritePatternSet(rewritePatterns, disabledPatterns,
461 ### Common Pass Utilities
463 Passes that utilize rewrite patterns should aim to provide a common set of
464 options and toggles to simplify the debugging experience when switching between
465 different passes/projects/etc. To aid in this endeavor, MLIR provides a common
466 set of utilities that can be easily included when defining a custom pass. These
467 are defined in `mlir/RewritePassUtil.td`; an example usage is shown below:
470 def MyRewritePass : Pass<"..."> {
472 let constructor = "createMyRewritePass()";
474 // Inherit the common pattern rewrite options from `RewritePassUtils`.
475 let options = RewritePassUtils.options;
479 #### Rewrite Pass Options
481 This section documents common pass options that are useful for controlling the
482 behavior of rewrite pattern application.
484 ##### Pattern Filtering
486 Two 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)
489 section above. A snippet of the tablegen definition of these options is shown
493 ListOption<"disabledPatterns", "disable-patterns", "std::string",
494 "Labels of patterns that should be filtered out during application">,
495 ListOption<"enabledPatterns", "enable-patterns", "std::string",
496 "Labels of patterns that should be used during application, all "
497 "other patterns are filtered out">,
500 These options may be used to provide filtering behavior when constructing any
501 `FrozenRewritePatternSet`s within the pass:
504 void MyRewritePass::initialize(MLIRContext *context) {
505 RewritePatternSet rewritePatterns(context);
508 // When constructing the `FrozenRewritePatternSet`, we provide the filter
510 frozenPatterns = FrozenRewritePatternSet(rewritePatterns, disabledPatterns,