xref: /llvm-project-15.0.7/mlir/docs/PDLL.md (revision 9f186bb1)
1# PDLL - PDL Language
2
3This document details the PDL Language (PDLL), a custom frontend language for
4writing pattern rewrites targeting MLIR.
5
6Note: This document assumes a familiarity with MLIR concepts; more specifically
7the concepts detailed within the
8[MLIR Pattern Rewriting](https://mlir.llvm.org/docs/PatternRewriter/) and
9[Operation Definition Specification (ODS)](https://mlir.llvm.org/docs/OpDefinitions/)
10documentation.
11
12[TOC]
13
14## Introduction
15
16Pattern matching is an extremely important component within MLIR, as it
17encompasses many different facets of the compiler. From canonicalization, to
18optimization, to conversion; every MLIR based compiler will heavily rely on the
19pattern matching infrastructure in some capacity.
20
21The PDL Language (PDLL) provides a declarative pattern language designed from
22the ground up for representing MLIR pattern rewrites. PDLL is designed to
23natively support writing matchers on all of MLIRs constructs via an intuitive
24interface that may be used for both ahead-of-time (AOT) and just-in-time (JIT)
25pattern compilation.
26
27## Rationale
28
29This section provides details on various design decisions, their rationale, and
30alternatives considered when designing PDLL. Given the nature of software
31development, this section may include references to areas of the MLIR compiler
32that no longer exist.
33
34### Why build a new language instead of improving TableGen DRR?
35
36Note: This section assumes familiarity with
37[TDRR](https://mlir.llvm.org/docs/DeclarativeRewrites/), please refer the
38relevant documentation before continuing.
39
40Tablegen DRR (TDRR), i.e.
41[Table-driven Declarative Rewrite Rules](https://mlir.llvm.org/docs/DeclarativeRewrites/),
42is a declarative DSL for defining MLIR pattern rewrites within the
43[TableGen](https://llvm.org/docs/TableGen/index.html) language. This
44infrastructure is currently the main way in which patterns may be defined
45declaratively within MLIR. TDRR utilizes TableGen's `dag` support to enable
46defining MLIR patterns that fit nicely within a DAG structure; in a similar way
47in which tablegen has been used to defined patterns for LLVM's backend
48infrastructure (SelectionDAG/Global Isel/etc.). Unfortunately however, the
49TableGen language is not as amenable to the structure of MLIR patterns as it has
50been for LLVM.
51
52The issues with TDRR largely stem from the use of TableGen as the host language
53for the DSL. These issues have risen from a mismatch in the structure of
54TableGen compared to the structure of MLIR, and from TableGen having different
55motivational goals than MLIR. A majority (or all depending on how stubborn you
56are) of the issues that we've come across with TDRR have been addressable in
57some form; the sticking point here is that the solutions to these problems have
58often been more "creative" than we'd like. This is a problem, and why we decided
59not to invest a larger effort into improving TDRR; users generally don't want
60"creative" APIs, they want something that is intuitive to read/write.
61
62To highlight some of these issues, below we will take a tour through some of the
63problems that have arisen, and how we "fixed" them.
64
65#### Multi-result operations
66
67MLIR natively supports a variable number of operation results. For the DAG based
68structure of TDRR, any form of multiple results (operations in this instance)
69creates a problem. This is because the DAG wants a single root node, and does
70not have nice facilities for indexing or naming the multiple results. Let's take
71a look at a quick example to see how this manifests:
72
73```tablegen
74// Suppose we have a three result operation, defined as seen below.
75def ThreeResultOp : Op<"three_result_op"> {
76    let arguments = (ins ...);
77
78    let results = (outs
79      AnyTensor:$output1,
80      AnyTensor:$output2,
81      AnyTensor:$output3
82    );
83}
84
85// To bind the results of `ThreeResultOp` in a TDRR pattern, we bind all results
86// to a single name and use a special naming convention: `__N`, where `N` is the
87// N-th result.
88def : Pattern<(ThreeResultOp:$results ...),
89              [(... $results__0), ..., (... $results__2), ...]>;
90```
91
92In TDRR, we "solved" the problem of accessing multiple results, but this isn't a
93very intuitive interface for users. Magical naming conventions obfuscate the
94code and can easily introduce bugs and other errors. There are various things
95that we could try to improve this situation, but there is a fundamental limit to
96what we can do given the limits of the TableGen dag structure. In PDLL, however,
97we have the freedom and flexibility to provide a proper interface into
98operations, regardless of their structure:
99
100```pdll
101// Import our definition of `ThreeResultOp`.
102#include "ops.td"
103
104Pattern {
105  ...
106
107  // In PDLL, we can directly reference the results of an operation variable.
108  // This provides a closer mental model to what the user expects.
109  let threeResultOp = op<my_dialect.three_result_op>;
110  let userOp = op<my_dialect.user_op>(threeResultOp.output1, ..., threeResultOp.output3);
111
112  ...
113}
114```
115
116#### Constraints
117
118In TDRR, the match dag defines the general structure of the input IR to match.
119Any non-structural/non-type constraints on the input are generally relegated to
120a list of constraints specified after the rewrite dag. For very simple patterns
121this may suffice, but with larger patterns it becomes quite problematic as it
122separates the constraint from the entity it constrains and negatively impacts
123the readability of the pattern. As an example, let's look at a simple pattern
124that adds additional constraints to its inputs:
125
126```tablegen
127// Suppose we have a two result operation, defined as seen below.
128def TwoResultOp : Op<"two_result_op"> {
129    let arguments = (ins ...);
130
131    let results = (outs
132      AnyTensor:$output1,
133      AnyTensor:$output2
134    );
135}
136
137// A simple constraint to check if a value is use_empty.
138def HasNoUseOf: Constraint<CPred<"$_self.use_empty()">, "has no use">;
139
140// Check if two values have a ShapedType with the same element type.
141def HasSameElementType : Constraint<
142    CPred<"$0.getType().cast<ShapedType>().getElementType() == "
143          "$1.getType().cast<ShapedType>().getElementType()">,
144    "values have same element type">;
145
146def : Pattern<(TwoResultOp:$results $input),
147              [(...), (...)],
148              [(HasNoUseOf:$results__1),
149               (HasSameElementType $results__0, $input)]>;
150```
151
152Above, when observing the constraints we need to search through the input dag
153for the inputs (also keeping in mind the magic naming convention for multiple
154results). For this simple pattern it may be just a few lines above, but complex
155patterns often grow to 10s of lines long. In PDLL, these constraints can be
156applied directly on or next to the entities they apply to:
157
158```pdll
159// The same constraints that we defined above:
160Constraint HasNoUseOf(value: Value) [{
161  return success(value.use_empty());
162}];
163Constraint HasSameElementType(value1: Value, value2: Value) [{
164  return success(value1.getType().cast<ShapedType>().getElementType() ==
165                 value2.getType().cast<ShapedType>().getElementType());
166}];
167
168Pattern {
169  // In PDLL, we can apply the constraint as early (or as late) as we want. This
170  // enables better structuring of the matcher code, and improves the
171  // readability/maintainability of the pattern.
172  let op = op<my_dialect.two_result_op>(input: Value);
173  HasNoUseOf(op.output2);
174  HasSameElementType(input, op.output2);
175
176  // ...
177}
178```
179
180#### Replacing Multiple Operations
181
182Often times a pattern will transform N number of input operations into N number
183of result operations. In PDLL, replacing multiple operations is as simple as
184adding two [`replace` statements](#replace-statement). In TDRR, the situation is
185a bit more nuanced. Given the single root structure of the TableGen dag,
186replacing a non-root operation is not nicely supported. It currently isn't
187natively possible, and instead requires using multiple patterns. We could
188potentially add another special rewrite directive, or extend `replaceWithValue`,
189but this simply highlights how even a basic IR transformation is muddled by the
190complexity of the host language.
191
192### Why not build a DSL in "X"?
193
194Yes! Well yes and no. To understand why, we have to consider what types of users
195we are trying to serve and what constraints we enforce upon them. The goal of
196PDLL is to provide a default and effective pattern language for MLIR that all
197users of MLIR can interact with immediately, regardless of their host
198environment. This language is available with no extra dependencies and comes
199"free" along with MLIR. If we were to use an existing host language to build our
200new DSL, we would need to make compromises along with it depending on the
201language. For some, there are questions of how to enforce matching environments
202(python2 or python3?, which version?), performance considerations, integration,
203etc. As an LLVM project, this could also mean enforcing a new language
204dependency on the users of MLIR (many of which may not want/need such a
205dependency otherwise). Another issue that comes along with any DSL that is
206embeded in another language: mitigating the user impedance mismatch between what
207the user expects from the host language and what our "backend" supports. For
208example, the PDL IR abstraction only contains limited support for control flow.
209If we were to build a DSL in python, we would need to ensure that complex
210control flow is either handled completely or effectively errors out. Even with
211ideal error handling, not having the expected features available creates user
212frustration. In addition to the environment constraints, there is also the issue
213of language tooling. With PDLL we intend to build a very robust and modern
214toolset that is designed to cater the needs of pattern developers, including
215code completion, signature help, and many more features that are specific to the
216problem we are solving. Integrating custom language tooling into existing
217languages can be difficult, and in some cases impossible (as our DSL would
218merely be a small subset of the existing language).
219
220These various points have led us to the initial conclusion that the most
221effective tool we can provide for our users is a custom tool designed for the
222problem at hand. With all of that being said, we understand that not all users
223have the same constraints that we have placed upon ourselves. We absolutely
224encourage and support the existence of various PDL frontends defined in
225different languages. This is one of the original motivating factors around
226building the PDL IR abstraction in the first place; to enable innovation and
227flexibility for our users (and in turn their users). For some, such as those in
228research and the Machine Learning space, they may already have a certain
229language (such as Python) heavily integrated into their workflow. For these
230users, a PDL DSL in their language may be ideal and we will remain committed to
231supporting and endorsing that from an infrastructure point-of-view.
232
233## Language Specification
234
235Note: PDLL is still under active development, and the designs discussed below
236are not necessarily final and may be subject to change.
237
238The design of PDLL is heavily influenced and centered around the
239[PDL IR abstraction](https://mlir.llvm.org/docs/Dialects/PDLOps/), which in turn
240is designed as an abstract model of the core MLIR structures. This leads to a
241design and structure that feels very similar to if you were directly writing the
242IR you want to match.
243
244### Includes
245
246PDLL supports an `include` directive to import content defined within other
247source files. There are two types of files that may be included: `.pdll` and
248`.td` files.
249
250#### `.pdll` includes
251
252When including a `.pdll` file, the contents of that file are copied directly into
253the current file being processed. This means that any patterns, constraints,
254rewrites, etc., defined within that file are processed along with those within
255the current file.
256
257#### `.td` includes
258
259When including a `.td` file, PDLL will automatically import any pertinent
260[ODS](https://mlir.llvm.org/docs/OpDefinitions/) information within that file.
261This includes any defined operations, constraints, interfaces, and more, making
262them implicitly accessible within PDLL. This is important, as ODS information
263allows for certain PDLL constructs, such as the
264[`operation` expression](#operation), to become much more powerful.
265
266### Patterns
267
268In any pattern descriptor language, pattern definition is at the core. In PDLL,
269patterns start with `Pattern` optionally followed by a name and a set of pattern
270metadata, and finally terminated by a pattern body. A few simple examples are
271shown below:
272
273```pdll
274// Here we have defined an anonymous pattern:
275Pattern {
276  // Pattern bodies are separated into two components:
277  // * Match Section
278  //    - Describes the input IR.
279  let root = op<toy.reshape>(op<toy.reshape>(arg: Value));
280
281  // * Rewrite Section
282  //    - Describes how to transform the IR.
283  //    - Last statement starts the rewrite.
284  replace root with op<toy.reshape>(arg);
285}
286
287// Here we have defined a pattern named `ReshapeReshapeOptPattern` with a
288// benefit of 10:
289Pattern ReshapeReshapeOptPattern with benefit(10) {
290  replace op<toy.reshape>(op<toy.reshape>(arg: Value))
291    with op<toy.reshape>(arg);
292}
293```
294
295After the definition of the pattern metadata, we specify the pattern body. The
296structure of a pattern body is comprised of two main sections, the `match`
297section and the `rewrite` section. The `match` section of a pattern describes
298the expected input IR, whereas the `rewrite` section describes how to transform
299that IR. This distinction is an important one to make, as PDLL handles certain
300variables and expressions differently within the different sections. When
301relevant in each of the sections below, we shall explicitly call out any
302behavioral differences.
303
304The general layout of the `match` and `rewrite` section is as follows: the
305*last* statement of the pattern body is required to be a
306[`operation rewrite statement`](#operation-rewrite-statements), and denotes the
307`rewrite` section; every statement before denotes the `match` section.
308
309#### Pattern metadata
310
311Rewrite patterns in MLIR have a set of metadata that allow for controlling
312certain behaviors, and providing information to the rewrite driver applying the
313pattern. In PDLL, a pattern can provide a non-default value for this metadata
314after the pattern name. Below, examples are shown for the different types of
315metadata supported:
316
317##### Benefit
318
319The benefit of a Pattern is an integer value that represents the "benefit" of
320matching that pattern. It is used by pattern drivers to determine the relative
321priorities of patterns during application; a pattern with a higher benefit is
322generally applied before one with a lower benefit.
323
324In PDLL, a pattern has a default benefit set to the number of input operations,
325i.e. the number of distinct `Op` expressions/variables, in the match section. This
326rule is driven by an observation that larger matches are more beneficial than smaller
327ones, and if a smaller one is applied first the larger one may not apply anymore.
328Patterns can override this behavior by specifying the benefit in the metadata section
329of the pattern:
330
331```pdll
332// Here we specify that this pattern has a benefit of `10`, overriding the
333// default behavior.
334Pattern with benefit(10) {
335  ...
336}
337```
338
339##### Bounded Rewrite Recursion
340
341During pattern application, there are situations in which a pattern may be
342applicable to the result of a previous application of that same pattern. If the
343pattern does not properly handle this recusive application, the pattern driver
344could become stuck in an infinite loop of application. To prevent this, patterns
345by-default are assumed to not have proper recursive bounding and will not be
346recursively applied. A pattern can signal that it does have proper handling for
347recursion by specifying the `recusion` flag in the pattern metadata section:
348
349```pdll
350// Here we signal that this pattern properly bounds recursive application.
351Pattern with recusion {
352  ...
353}
354```
355
356#### Single Line "Lambda" Body
357
358Patterns generally define their body using a compound block of statements, as
359shown below:
360
361```pdll
362Pattern {
363  replace op<my_dialect.foo>(operands: ValueRange) with operands;
364}
365```
366
367Patterns also support a lambda-like syntax for specifying simple single line
368bodies. The lambda body of a Pattern expects a single
369[operation rewrite statement](#operation-rewrite-statements):
370
371```pdll
372Pattern => replace op<my_dialect.foo>(operands: ValueRange) with operands;
373```
374
375### Variables
376
377Variables in PDLL represent specific instances of IR entities, such as `Value`s,
378`Operation`s, `Type`s, etc. Consider the simple pattern below:
379
380```pdll
381Pattern {
382  let value: Value;
383  let root = op<mydialect.foo>(value);
384
385  replace root with value;
386}
387```
388
389In this pattern we define two variables, `value` and `root`, using the `let`
390statement. The `let` statement allows for defining variables and constraining
391them. Every variable in PDLL is of a certain type, which defines the type of IR
392entity the variable represents. The type of a variable may be determined via
393either a constraint, or an initializer expression.
394
395#### Variable "Binding"
396
397In addition to having a type, variables must also be "bound", either via an initializer
398expression or to a non-native constraint or rewrite use within the `match` section of the
399pattern. "Binding" a variable contextually identifies that variable within either the
400input (i.e. `match` section) or output (i.e. `rewrite` section) IR. In the `match` section,
401this allows for building the match tree from the pattern's root operation, which must be
402"bound" to the [operation rewrite statement](#operation-rewrite-statements) that denotes the
403`rewrite` section of the pattern. All non-root variables within the `match`
404section must be bound in some way to the "root" operation. To help illustrate
405the concept, let's take a look at a quick example. Consider the `.mlir` snippet
406below:
407
408```mlir
409func.func @baz(%arg: i32) {
410  %result = my_dialect.foo %arg, %arg -> i32
411}
412```
413
414Say that we want to write a pattern that matches `my_dialect.foo` and replaces
415it with its unique input argument. A naive way to write this pattern in PDLL is
416shown below:
417
418```pdll
419Pattern {
420  // ** match section ** //
421  let arg: Value;
422  let root = op<my_dialect.foo>(arg, arg);
423
424  // ** rewrite section ** //
425  replace root with arg;
426}
427```
428
429In the above pattern, the `arg` variable is "bound" to the first and second operands
430of the `root` operation. Every use of `arg` is constrained to be the same `Value`, i.e.
431the first and second operands of `root` will be constrained to refer to the same input
432Value. The same is true for the `root` operation, it is bound to the "root" operation of the
433pattern as it is used in input of the top-level [`replace` statement](#replace-statement)
434of the `rewrite` section of the pattern. Writing this pattern using the C++ API, the concept
435of "binding" becomes more clear:
436
437```c++
438struct Pattern : public OpRewritePattern<my_dialect::FooOp> {
439  LogicalResult matchAndRewrite(my_dialect::FooOp root, PatternRewriter &rewriter) {
440    Value arg = root->getOperand(0);
441    if (arg != root->getOperand(1))
442      return failure();
443
444    rewriter.replaceOp(root, arg);
445    return success();
446  }
447};
448```
449
450If a variable is not "bound" properly, PDLL won't be able to identify what value
451it would correspond to in the IR. As a final example, let's consider a variable
452that hasn't been bound:
453
454```pdll
455Pattern {
456  // ** match section ** //
457  let arg: Value;
458  let root = op<my_dialect.foo>
459
460  // ** rewrite section ** //
461  replace root with arg;
462}
463```
464
465If we were to write this exact pattern in C++, we would end up with:
466
467```c++
468struct Pattern : public OpRewritePattern<my_dialect::FooOp> {
469  LogicalResult matchAndRewrite(my_dialect::FooOp root, PatternRewriter &rewriter) {
470    // `arg` was never bound, so we don't know what input Value it was meant to
471    // correspond to.
472    Value arg;
473
474    rewriter.replaceOp(root, arg);
475    return success();
476  }
477};
478```
479
480#### Variable Constraints
481
482```pdll
483// This statement defines a variable `value` that is constrained to be a `Value`.
484let value: Value;
485
486// This statement defines a variable `value` that is constrained to be a `Value`
487// *and* constrained to have a single use.
488let value: [Value, HasOneUse];
489```
490
491Any number of single entity constraints may be attached directly to a variable
492upon declaration. Within the `matcher` section, these constraints may add
493additional checks on the input IR. Within the `rewriter` section, constraints
494are *only* used to define the type of the variable. There are a number of
495builtin constraints that correlate to the core MLIR constructs: `Attr`, `Op`,
496`Type`, `TypeRange`, `Value`, `ValueRange`. Along with these, users may define
497custom constraints that are implemented within PDLL, or natively (i.e. outside
498of PDLL). See the general [Constraints](#constraints) section for more detailed
499information.
500
501#### Inline Variable Definition
502
503Along with the `let` statement, variables may also be defined inline by
504specifying the constraint list along with the desired variable name in the first
505place that the variable would be used. After definition, the variable is visible
506from all points forward. See below for an example:
507
508```pdll
509// `value` is used as an operand to the operation `root`:
510let value: Value;
511let root = op<my_dialect.foo>(value);
512replace root with value;
513
514// `value` could also be defined "inline":
515let root = op<my_dialect.foo>(value: Value);
516replace root with value;
517```
518
519Note that the point of definition of an inline variable is the point of reference,
520meaning that an inline variable can be used immediately in the same parent
521expression within which it was defined:
522
523```pdll
524let root = op<my_dialect.foo>(value: Value, _: Value, value);
525replace root with value;
526```
527
528##### Wildcard Variable Definition
529
530Often times when defining a variable inline, the variable isn't intended to be
531used anywhere else in the pattern. For example, this may happen if you want to
532attach constraints to a variable but have no other use for it. In these
533situations, the "wildcard" variable can be used to remove the need to provide a
534name, as "wildcard" variables are not visible outside of the point of
535definition. An example is shown below:
536
537```pdll
538Pattern {
539  let root = op<my_dialect.foo>(arg: Value, _: Value, _: [Value, I64Value], arg);
540  replace root with arg;
541}
542```
543
544In the above example, the second operand isn't needed for the pattern but we
545need to provide it to signal that a second operand does exist (we just don't
546care what it is in this pattern).
547
548### Operation Expression
549
550An operation expression in PDLL represents an MLIR operation. In the `match`
551section of the pattern, this expression models one of the input operations to
552the pattern. In the `rewrite` section of the pattern, this expression models one
553of the operations to create. The general structure of the operation expression
554is very similar to that of the "generic form" of textual MLIR assembly:
555
556```pdll
557let root = op<my_dialect.foo>(operands: ValueRange) {attr = attr: Attr} -> (resultTypes: TypeRange);
558```
559
560Let's walk through each of the different components of the expression:
561
562#### Operation name
563
564The operation name signifies which type of MLIR Op this operation corresponds
565to. In the `match` section of the pattern, the name may be elided. This would
566cause this pattern to match *any* operation type that satifies the rest of the
567constraints of the operation. In the `rewrite` section, the name is required.
568
569```pdll
570// `root` corresponds to an instance of a `my_dialect.foo` operation.
571let root = op<my_dialect.foo>;
572
573// `root` could be an instance of any operation type.
574let root = op<>;
575```
576
577#### Operands
578
579The operands section corresponds to the operands of the operation. This section
580of an operation expression may be elided, which within a `match` section means
581that the operands are not constrained in any way. If elided within a `rewrite`
582section, the operation is treated as having no operands. When present, the
583operands of an operation expression are interpreted in the following ways:
584
5851) A single instance of type `ValueRange`:
586
587In this case, the single range is treated as all of the operands of the
588operation:
589
590```pdll
591// Define an instance with single range of operands.
592let root = op<my_dialect.foo>(allOperands: ValueRange);
593```
594
5952) A variadic number of either `Value` or `ValueRange`:
596
597In this case, the inputs are expected to correspond with the operand groups as
598defined on the operation in ODS.
599
600Given the following operation definition in ODS:
601
602```tablegen
603def MyIndirectCallOp {
604  let arguments = (ins FunctionType:$call, Variadic<AnyType>:$args);
605}
606```
607
608We can match the operands as so:
609
610```pdll
611let root = op<my_dialect.indirect_call>(call: Value, args: ValueRange);
612```
613
614#### Results
615
616The results section corresponds to the result types of the operation. This section
617of an operation expression may be elided, which within a `match` section means
618that the result types are not constrained in any way. If elided within a `rewrite`
619section, the results of the operation are [inferred](#inferred-results). When present,
620the result types of an operation expression are interpreted in the following ways:
621
6221) A single instance of type `TypeRange`:
623
624In this case, the single range is treated as all of the result types of the
625operation:
626
627```pdll
628// Define an instance with single range of types.
629let root = op<my_dialect.foo> -> (allResultTypes: TypeRange);
630```
631
6322) A variadic number of either `Type` or `TypeRange`:
633
634In this case, the inputs are expected to correspond with the result groups as
635defined on the operation in ODS.
636
637Given the following operation definition in ODS:
638
639```tablegen
640def MyOp {
641  let results = (outs SomeType:$result, Variadic<SomeType>:$otherResults);
642}
643```
644
645We can match the result types as so:
646
647```pdll
648let root = op<my_dialect.op> -> (result: Type, otherResults: TypeRange);
649```
650
651#### Inferred Results
652
653Within the `rewrite` section of a pattern, the result types of an
654operation are inferred if they are elided or otherwise not
655previously bound. The ["variable binding"](#variable-binding) section above
656discusses the concept of "binding" in more detail. Below are various examples
657that build upon this to help showcase how a result type may be "bound":
658
659* Binding to a [constant](#type-expression):
660
661```pdll
662op<my_dialect.op> -> (type<"i32">);
663```
664
665* Binding to types within the `match` section:
666
667```pdll
668Pattern {
669  replace op<dialect.inputOp> -> (resultTypes: TypeRange)
670    with op<dialect.outputOp> -> (resultTypes);
671}
672```
673
674* Binding to previously inferred types:
675
676```pdll
677Pattern {
678  rewrite root: Op with {
679    // `resultTypes` here is *not* yet bound, and will be inferred when
680    // creating `dialect.op`. Any uses of `resultTypes` after this expression,
681    // will use the types inferred when creating this operation.
682    op<dialect.op> -> (resultTypes: TypeRange);
683
684    // `resultTypes` here is bound to the types inferred when creating `dialect.op`.
685    op<dialect.bar> -> (resultTypes);
686  };
687}
688```
689
690* Binding to a [`Native Rewrite`](#native-rewriters) method result:
691
692```pdll
693Rewrite BuildTypes() -> TypeRange;
694
695Pattern {
696  rewrite root: Op with {
697    op<dialect.op> -> (BuildTypes());
698  };
699}
700```
701
702Below are the set of contexts in which result type inferrence is supported:
703
704##### Inferred Results of Replacement Operation
705
706Replacements have the invariant that the types of the replacement values must
707match the result types of the input operation. This means that when replacing
708one operation with another, the result types of the replacement operation may
709be inferred from the result types of the operation being replaced. For example,
710consider the following pattern:
711
712```pdll
713Pattern => replace op<dialect.inputOp> with op<dialect.outputOp>;
714```
715
716This pattern could be written in a more explicit way as:
717
718```pdll
719Pattern {
720  replace op<dialect.inputOp> -> (resultTypes: TypeRange)
721    with op<dialect.outputOp> -> (resultTypes);
722}
723```
724
725##### Inferred Results with InferTypeOpInterface
726
727`InferTypeOpInterface` is an interface that enables operations to infer its result
728types from its input attributes, operands, regions, etc. When the result types of
729an operation cannot be inferred from any other context, this interface is invoked
730to infer the result types of the operation.
731
732#### Attributes
733
734The attributes section of the operation expression corresponds to the attribute
735dictionary of the operation. This section of an operation expression may be
736elided, in which case the attributes are not constrained in any way. The
737composition of this component maps exactly to how attribute dictionaries are
738structured in the MLIR textual assembly format:
739
740```pdll
741let root = op<my_dialect.foo> {attr1 = attrValue: Attr, attr2 = attrValue2: Attr};
742```
743
744Within the `{}` attribute entries are specified by an identifier or string name,
745corresponding to the attribute name, followed by an assignment to the attribute
746value. If the attribute value is elided, the value of the attribute is
747implicitly defined as a
748[`UnitAttr`](https://mlir.llvm.org/docs/Dialects/Builtin/#unitattr).
749
750```pdll
751let unitConstant = op<my_dialect.constant> {value};
752```
753
754##### Accessing Operation Results
755
756In multi-operation patterns, the result of one operation often feeds as an input
757into another. The result groups of an operation may be accessed by name or by
758index via the `.` operator:
759
760Note: Remember to import the definition of your operation via
761[include](#`.td`_includes) to ensure it is visible to PDLL.
762
763Given the following operation definition in ODS:
764
765```tablegen
766def MyResultOp {
767  let results = (outs SomeType:$result);
768}
769def MyInputOp {
770  let arguments = (ins SomeType:$input, SomeType:$input);
771}
772```
773
774We can write a pattern where `MyResultOp` feeds into `MyInputOp` as so:
775
776```pdll
777// In this example, we use both `result`(the name) and `0`(the index) to refer to
778// the first result group of `resultOp`.
779// Note: If we elide the result types section within the match section, it means
780//       they aren't constrained, not that the operation has no results.
781let resultOp = op<my_dialect.result_op>;
782let inputOp = op<my_dialect.input_op>(resultOp.result, resultOp.0);
783```
784
785Along with result name access, variables of `Op` type may implicitly convert to
786`Value` or `ValueRange`. If these variables are registered (has ODS entry), they
787are converted to `Value` when they are known to only have one result, otherwise
788they will be converted to `ValueRange`:
789
790```pdll
791// `resultOp` may also convert implicitly to a Value for use in `inputOp`:
792let resultOp = op<my_dialect.result_op>;
793let inputOp = op<my_dialect.input_op>(resultOp);
794
795// We could also inline `resultOp` directly:
796let inputOp = op<my_dialect.input_op>(op<my_dialect.result_op>);
797```
798
799#### Unregistered Operations
800
801A variable of unregistered op is still available for numeric result indexing.
802Given that we don't have knowledge of its result groups, numeric indexing
803returns a Value corresponding to the individual result at the given index.
804
805```pdll
806// Use the index `0` to refer to the first result value of the unregistered op.
807let inputOp = op<my_dialect.input_op>(op<my_dialect.unregistered_op>.0);
808```
809
810### Attribute Expression
811
812An attribute expression represents a literal MLIR attribute. It allows for
813statically specifying an MLIR attribute to use, by specifying the textual form
814of that attribute.
815
816```pdll
817let trueConstant = op<arith.constant> {value = attr<"true">};
818
819let applyResult = op<affine.apply>(args: ValueRange) {map = attr<"affine_map<(d0, d1) -> (d1 - 3)>">}
820```
821
822### Type Expression
823
824A type expression represents a literal MLIR type. It allows for statically
825specifying an MLIR type to use, by specifying the textual form of that type.
826
827```pdll
828let i32Constant = op<arith.constant> -> (type<"i32">);
829```
830
831### Tuples
832
833PDLL provides native support for tuples, which are used to group multiple
834elements into a single compound value. The values in a tuple can be of any type,
835and do not need to be of the same type. There is also no limit to the number of
836elements held by a tuple. The elements of a tuple can be accessed by index:
837
838```pdll
839let tupleValue = (op<my_dialect.foo>, attr<"10 : i32">, type<"i32">);
840
841let opValue = tupleValue.0;
842let attrValue = tupleValue.1;
843let typeValue = tupleValue.2;
844```
845
846You can also name the elements of a tuple and use those names to refer to the
847values of the individual elements. An element name consists of an identifier
848followed immediately by an equal (=).
849
850```pdll
851let tupleValue = (
852  opValue = op<my_dialect.foo>,
853  attr<"10 : i32">,
854  typeValue = type<"i32">
855);
856
857let opValue = tupleValue.opValue;
858let attrValue = tupleValue.1;
859let typeValue = tupleValue.typeValue;
860```
861
862Tuples are used to represent multiple results from a
863[constraint](#constraints-with-multiple-results) or
864[rewrite](#rewrites-with-multiple-results).
865
866### Constraints
867
868Constraints provide the ability to inject additional checks on the input IR
869within the `match` section of a pattern. Constraints can be applied anywhere
870within the `match` section, and depending on the type can either be applied via
871the constraint list of a [variable](#variables) or via the call operator (e.g.
872`MyConstraint(...)`). There are three main categories of constraints:
873
874#### Core Constraints
875
876PDLL defines a number of core constraints that constrain the type of the IR
877entity. These constraints can only be applied via the
878[constraint list](#variable-constraints) of a variable.
879
880*   `Attr` (`<` type `>`)?
881
882A single entity constraint that corresponds to an `mlir::Attribute`. This
883constraint optionally takes a type component that constrains the result type of
884the attribute.
885
886```pdll
887// Define a simple variable using the `Attr` constraint.
888let attr: Attr;
889let constant = op<arith.constant> {value = attr};
890
891// Define a simple variable using the `Attr` constraint, that has its type
892// constrained as well.
893let attrType: Type;
894let attr: Attr<attrType>;
895let constant = op<arith.constant> {value = attr};
896```
897
898*   `Op` (`<` op-name `>`)?
899
900A single entity constraint that corresponds to an `mlir::Operation *`.
901
902```pdll
903// Match only when the input is from another operation.
904let inputOp: Op;
905let root = op<my_dialect.foo>(inputOp);
906
907// Match only when the input is from another `my_dialect.foo` operation.
908let inputOp: Op<my_dialect.foo>;
909let root = op<my_dialect.foo>(inputOp);
910```
911
912*   `Type`
913
914A single entity constraint that corresponds to an `mlir::Type`.
915
916```pdll
917// Define a simple variable using the `Type` constraint.
918let resultType: Type;
919let root = op<my_dialect.foo> -> (resultType);
920```
921
922*   `TypeRange`
923
924A single entity constraint that corresponds to a `mlir::TypeRange`.
925
926```pdll
927// Define a simple variable using the `TypeRange` constraint.
928let resultTypes: TypeRange;
929let root = op<my_dialect.foo> -> (resultTypes);
930```
931
932*   `Value` (`<` type-expr `>`)?
933
934A single entity constraint that corresponds to an `mlir::Value`. This constraint
935optionally takes a type component that constrains the result type of the value.
936
937```pdll
938// Define a simple variable using the `Value` constraint.
939let value: Value;
940let root = op<my_dialect.foo>(value);
941
942// Define a variable using the `Value` constraint, that has its type constrained
943// to be same as the result type of the `root` op.
944let valueType: Type;
945let input: Value<valueType>;
946let root = op<my_dialect.foo>(input) -> (valueType);
947```
948
949*   `ValueRange` (`<` type-expr `>`)?
950
951A single entity constraint that corresponds to a `mlir::ValueRange`. This
952constraint optionally takes a type component that constrains the result types of
953the value range.
954
955```pdll
956// Define a simple variable using the `ValueRange` constraint.
957let inputs: ValueRange;
958let root = op<my_dialect.foo>(inputs);
959
960// Define a variable using the `ValueRange` constraint, that has its types
961// constrained to be same as the result types of the `root` op.
962let valueTypes: TypeRange;
963let inputs: ValueRange<valueTypes>;
964let root = op<my_dialect.foo>(inputs) -> (valueTypes);
965```
966
967#### Defining Constraints in PDLL
968
969Aside from the core constraints, additional constraints can also be defined
970within PDLL. This allows for building matcher fragments that can be composed
971across many different patterns. A constraint in PDLL is defined similarly to a
972function in traditional programming languages; it contains a name, a set of
973input arguments, a set of result types, and a body. Results of a constraint are
974returned via a `return` statement. A few examples are shown below:
975
976```pdll
977/// A constraint that takes an input and constrains the use to an operation of
978/// a given type.
979Constraint UsedByFooOp(value: Value) {
980  op<my_dialect.foo>(value);
981}
982
983/// A constraint that returns a result of an existing operation.
984Constraint ExtractResult(op: Op<my_dialect.foo>) -> Value {
985  return op.result;
986}
987
988Pattern {
989  let value = ExtractResult(op<my_dialect.foo>);
990  UsedByFooOp(value);
991}
992```
993
994##### Constraints with multiple results
995
996Constraints can return multiple results by returning a tuple of values. When
997returning multiple results, each result can also be assigned a name to use when
998indexing that tuple element. Tuple elements can be referenced by their index
999number, or by name if they were assigned one.
1000
1001```pdll
1002// A constraint that returns multiple results, with some of the results assigned
1003// a more readable name.
1004Constraint ExtractMultipleResults(op: Op<my_dialect.foo>) -> (Value, result1: Value) {
1005  return (op.result1, op.result2);
1006}
1007
1008Pattern {
1009  // Return a tuple of values.
1010  let result = ExtractMultipleResults(op: op<my_dialect.foo>);
1011
1012  // Index the tuple elements by index, or by name.
1013  replace op<my_dialect.foo> with (result.0, result.1, result.result1);
1014}
1015```
1016
1017##### Constraint result type inference
1018
1019In addition to explicitly specifying the results of the constraint via the
1020constraint signature, PDLL defined constraints also support inferring the result
1021type from the return statement. Result type inference is active whenever the
1022constraint is defined with no result constraints:
1023
1024```pdll
1025// This constraint returns a derived operation.
1026Constraint ReturnSelf(op: Op<my_dialect.foo>) {
1027  return op;
1028}
1029// This constraint returns a tuple of two Values.
1030Constraint ExtractMultipleResults(op: Op<my_dialect.foo>) {
1031  return (result1 = op.result1, result2 = op.result2);
1032}
1033
1034Pattern {
1035  let values = ExtractMultipleResults(op<my_dialect.foo>);
1036  replace op<my_dialect.foo> with (values.result1, values.result2);
1037}
1038```
1039
1040##### Single Line "Lambda" Body
1041
1042Constraints generally define their body using a compound block of statements, as
1043shown below:
1044
1045```pdll
1046Constraint ReturnSelf(op: Op<my_dialect.foo>) {
1047  return op;
1048}
1049Constraint ExtractMultipleResults(op: Op<my_dialect.foo>) {
1050  return (result1 = op.result1, result2 = op.result2);
1051}
1052```
1053
1054Constraints also support a lambda-like syntax for specifying simple single line
1055bodies. The lambda body of a Constraint expects a single expression, which is
1056implicitly returned:
1057
1058```pdll
1059Constraint ReturnSelf(op: Op<my_dialect.foo>) => op;
1060
1061Constraint ExtractMultipleResults(op: Op<my_dialect.foo>)
1062  => (result1 = op.result1, result2 = op.result2);
1063```
1064
1065#### Native Constraints
1066
1067Constraints may also be defined outside of PDLL, and registered natively within
1068the C++ API.
1069
1070##### Importing existing Native Constraints
1071
1072Constraints defined externally can be imported into PDLL by specifying a
1073constraint "declaration". This is similar to the PDLL form of defining a
1074constraint but omits the body. Importing the declaration in this form allows for
1075PDLL to statically know the expected input and output types.
1076
1077```pdll
1078// Import a single entity value native constraint that checks if the value has a
1079// single use. This constraint must be registered by the consumer of the
1080// compiled PDL.
1081Constraint HasOneUse(value: Value);
1082
1083// Import a multi-entity type constraint that checks if two values have the same
1084// element type.
1085Constraint HasSameElementType(value1: Value, value2: Value);
1086
1087Pattern {
1088  // A single entity constraint can be applied via the variable argument list.
1089  let value: HasOneUse;
1090
1091  // Otherwise, constraints can be applied via the call operator:
1092  let value: Value = ...;
1093  let value2: Value = ...;
1094  HasOneUse(value);
1095  HasSameElementType(value, value2);
1096}
1097```
1098
1099External constraints are those registered explicitly with the `RewritePatternSet` via
1100the C++ PDL API. For example, the constraints above may be registered as:
1101
1102```c++
1103static LogicalResult hasOneUseImpl(PatternRewriter &rewriter, Value value) {
1104  return success(value.hasOneUse());
1105}
1106static LogicalResult hasSameElementTypeImpl(PatternRewriter &rewriter,
1107                                            Value value1, Value Value2) {
1108  return success(value1.getType().cast<ShapedType>().getElementType() ==
1109                 value2.getType().cast<ShapedType>().getElementType());
1110}
1111
1112void registerNativeConstraints(RewritePatternSet &patterns) {
1113    patternList.getPDLPatterns().registerConstraintFunction(
1114        "HasOneUse", hasOneUseImpl);
1115    patternList.getPDLPatterns().registerConstraintFunction(
1116        "HasSameElementType", hasSameElementTypeImpl);
1117}
1118```
1119
1120##### Defining Native Constraints in PDLL
1121
1122In addition to importing native constraints, PDLL also supports defining native
1123constraints directly when compiling ahead-of-time (AOT) for C++. These
1124constraints can be defined by specifying a string code block after the
1125constraint declaration:
1126
1127```pdll
1128Constraint HasOneUse(value: Value) [{
1129  return success(value.hasOneUse());
1130}];
1131Constraint HasSameElementType(value1: Value, value2: Value) [{
1132  return success(value1.getType().cast<ShapedType>().getElementType() ==
1133                 value2.getType().cast<ShapedType>().getElementType());
1134}];
1135
1136Pattern {
1137  // A single entity constraint can be applied via the variable argument list.
1138  let value: HasOneUse;
1139
1140  // Otherwise, constraints can be applied via the call operator:
1141  let value: Value = ...;
1142  let value2: Value = ...;
1143  HasOneUse(value);
1144  HasSameElementType(value, value2);
1145}
1146```
1147
1148The arguments of the constraint are accessible within the code block via the
1149same name. See the ["type translation"](#native-constraint-type-translations) below for
1150detailed information on how PDLL types are converted to native types. In addition to the
1151PDLL arguments, the code block may also access the current `PatternRewriter` using
1152`rewriter`. The result type of the native constraint function is implicitly defined
1153as a `::mlir::LogicalResult`.
1154
1155Taking the constraints defined above as an example, these function would roughly be
1156translated into:
1157
1158```c++
1159LogicalResult HasOneUse(PatternRewriter &rewriter, Value value) {
1160  return success(value.hasOneUse());
1161}
1162LogicalResult HasSameElementType(Value value1, Value value2) {
1163  return success(value1.getType().cast<ShapedType>().getElementType() ==
1164                 value2.getType().cast<ShapedType>().getElementType());
1165}
1166```
1167
1168TODO: Native constraints should also be allowed to return values in certain cases.
1169
1170###### Native Constraint Type Translations
1171
1172The types of argument and result variables are generally mapped to the corresponding
1173MLIR type of the [constraint](#constraints) used. Below is a detailed description
1174of how the mapped type of a variable is determined for the various different types of
1175constraints.
1176
1177* Attr, Op, Type, TypeRange, Value, ValueRange:
1178
1179These are all core constraints, and are mapped directly to the MLIR equivalent
1180(that their names suggest), namely:
1181
1182  * `Attr`       -> "::mlir::Attribute"
1183  * `Op`         -> "::mlir::Operation *"
1184  * `Type`       -> "::mlir::Type"
1185  * `TypeRange`  -> "::mlir::TypeRange"
1186  * `Value`      -> "::mlir::Value"
1187  * `ValueRange` -> "::mlir::ValueRange"
1188
1189* Op<dialect.name>
1190
1191A named operation constraint has a unique translation. If the ODS registration of the
1192referenced operation has been included, the qualified C++ is used. If the ODS information
1193is not available, this constraint maps to "::mlir::Operation *", similarly to the unnamed
1194variant. For example, given the following:
1195
1196```pdll
1197// `my_ops.td` provides the ODS definition of the `my_dialect` operations, such as
1198// `my_dialect.bar` used below.
1199#include "my_ops.td"
1200
1201Constraint Cst(op: Op<my_dialect.bar>) [{
1202  return success(op ... );
1203}];
1204```
1205
1206The native type used for `op` may be of the form `my_dialect::BarOp`, as opposed to the
1207default `::mlir::Operation *`. Below is a sample translation of the above constraint:
1208
1209```c++
1210LogicalResult Cst(my_dialect::BarOp op) {
1211  return success(op ... );
1212}
1213```
1214
1215* Imported ODS Constraints
1216
1217Aside from the core constraints, certain constraints imported from ODS may use a unique
1218native type. How to enable this unique type depends on the ODS constraint construct that
1219was imported:
1220
1221  * `Attr` constraints
1222    - Imported `Attr` constraints utilize the `storageType` field for native type translation.
1223
1224  * `Type` constraints
1225    - Imported `Type` constraints utilize the `cppClassName` field for native type translation.
1226
1227  * `AttrInterface`/`OpInterface`/`TypeInterface` constraints
1228    - Imported interfaces utilize the `cppInterfaceName` field for native type translation.
1229
1230#### Defining Constraints Inline
1231
1232In addition to global scope, PDLL Constraints and Native Constraints defined in
1233PDLL may be specified *inline* at any level of nesting. This means that they may
1234be defined in Patterns, other Constraints, Rewrites, etc:
1235
1236```pdll
1237Constraint GlobalConstraint() {
1238  Constraint LocalConstraint(value: Value) {
1239    ...
1240  };
1241  Constraint LocalNativeConstraint(value: Value) [{
1242    ...
1243  }];
1244  let someValue: [LocalConstraint, LocalNativeConstraint] = ...;
1245}
1246```
1247
1248Constraints that are defined inline may also elide the name when used directly:
1249
1250```pdll
1251Constraint GlobalConstraint(inputValue: Value) {
1252  Constraint(value: Value) { ... }(inputValue);
1253  Constraint(value: Value) [{ ... }](inputValue);
1254}
1255```
1256
1257When defined inline, PDLL constraints may reference any previously defined
1258variable:
1259
1260```pdll
1261Constraint GlobalConstraint(op: Op<my_dialect.foo>) {
1262  Constraint LocalConstraint() {
1263    let results = op.results;
1264  };
1265}
1266```
1267
1268### Rewriters
1269
1270Rewriters define the set of transformations to be performed within the `rewrite`
1271section of a pattern, and, more specifically, how to transform the input IR
1272after a successful pattern match. All PDLL rewrites must be defined within the
1273`rewrite` section of the pattern. The `rewrite` section is denoted by the last
1274statement within the body of the `Pattern`, which is required to be an
1275[operation rewrite statement](#operation-rewrite-statements). There are two main
1276categories of rewrites in PDLL: operation rewrite statements, and user defined
1277rewrites.
1278
1279#### Operation Rewrite statements
1280
1281Operation rewrite statements are builtin PDLL statements that perform an IR
1282transformation given a root operation. These statements are the only ones able
1283to start the `rewrite` section of a pattern, as they allow for properly
1284["binding"](#variable-binding) the root operation of the pattern.
1285
1286##### `erase` statement
1287
1288```pdll
1289// A pattern that erases all `my_dialect.foo` operations.
1290Pattern => erase op<my_dialect.foo>;
1291```
1292
1293The `erase` statement erases a given operation.
1294
1295##### `replace` statement
1296
1297```pdll
1298// A pattern that replaces the root operation with its input value.
1299Pattern {
1300  let root = op<my_dialect.foo>(input: Value);
1301  replace root with input;
1302}
1303
1304// A pattern that replaces the root operation with multiple input values.
1305Pattern {
1306  let root = op<my_dialect.foo>(input: Value, _: Value, input2: Value);
1307  replace root with (input, input2);
1308}
1309
1310// A pattern that replaces the root operation with another operation.
1311// Note that when an operation is used as the replacement, we can infer its
1312// result types from the input operation. In these cases, the result
1313// types of replacement operation may be elided.
1314Pattern {
1315  // Note: In this pattern we also inlined the `root` expression.
1316  replace op<my_dialect.foo> with op<my_dialect.bar>;
1317}
1318```
1319
1320The `replace` statement allows for replacing a given root operation with either
1321another operation, or a set of input `Value` and `ValueRange` values. When an operation
1322is used as the replacement, we allow infering the result types from the input operation.
1323In these cases, the result types of replacement operation may be elided. Note that no
1324other components aside from the result types will be inferred from the input operation
1325during the replacement.
1326
1327##### `rewrite` statement
1328
1329```pdll
1330// A simple pattern that replaces the root operation with its input value.
1331Pattern {
1332  let root = op<my_dialect.foo>(input: Value);
1333  rewrite root with {
1334    ...
1335
1336    replace root with input;
1337  };
1338}
1339```
1340
1341The `rewrite` statement allows for rewriting a given root operation with a block
1342of nested rewriters. The root operation is not implicitly erased or replaced,
1343and any transformations to it must be expressed within the nested rewrite block.
1344The inner body may contain any number of other rewrite statements, variables, or
1345expressions.
1346
1347#### Defining Rewriters in PDLL
1348
1349Additional rewrites can also be defined within PDLL, which allows for building
1350rewrite fragments that can be composed across many different patterns. A
1351rewriter in PDLL is defined similarly to a function in traditional programming
1352languages; it contains a name, a set of input arguments, a set of result types,
1353and a body. Results of a rewrite are returned via a `return` statement. A few
1354examples are shown below:
1355
1356```pdll
1357// A rewrite that constructs and returns a new operation, given an input value.
1358Rewrite BuildFooOp(value: Value) -> Op {
1359  return op<my_dialect.foo>(value);
1360}
1361
1362Pattern {
1363  // We invoke the rewrite in the same way as functions in traditional
1364  // languages.
1365  replace op<my_dialect.old_op>(input: Value) with BuildFooOp(input);
1366}
1367```
1368
1369##### Rewrites with multiple results
1370
1371Rewrites can return multiple results by returning a tuple of values. When
1372returning multiple results, each result can also be assigned a name to use when
1373indexing that tuple element. Tuple elements can be referenced by their index
1374number, or by name if they were assigned one.
1375
1376```pdll
1377// A rewrite that returns multiple results, with some of the results assigned
1378// a more readable name.
1379Rewrite CreateRewriteOps() -> (Op, result1: ValueRange) {
1380  return (op<my_dialect.bar>, op<my_dialect.foo>);
1381}
1382
1383Pattern {
1384  rewrite root: Op<my_dialect.foo> with {
1385    // Invoke the rewrite, which returns a tuple of values.
1386    let result = CreateRewriteOps();
1387
1388    // Index the tuple elements by index, or by name.
1389    replace root with (result.0, result.1, result.result1);
1390  }
1391}
1392```
1393
1394##### Rewrite result type inference
1395
1396In addition to explicitly specifying the results of the rewrite via the rewrite
1397signature, PDLL defined rewrites also support inferring the result type from the
1398return statement. Result type inference is active whenever the rewrite is
1399defined with no result constraints:
1400
1401```pdll
1402// This rewrite returns a derived operation.
1403Rewrite ReturnSelf(op: Op<my_dialect.foo>) => op;
1404// This rewrite returns a tuple of two Values.
1405Rewrite ExtractMultipleResults(op: Op<my_dialect.foo>) {
1406  return (result1 = op.result1, result2 = op.result2);
1407}
1408
1409Pattern {
1410  rewrite root: Op<my_dialect.foo> with {
1411    let values = ExtractMultipleResults(op<my_dialect.foo>);
1412    replace root with (values.result1, values.result2);
1413  }
1414}
1415```
1416
1417##### Single Line "Lambda" Body
1418
1419Rewrites generally define their body using a compound block of statements, as
1420shown below:
1421
1422```pdll
1423Rewrite ReturnSelf(op: Op<my_dialect.foo>) {
1424  return op;
1425}
1426Rewrite EraseOp(op: Op) {
1427  erase op;
1428}
1429```
1430
1431Rewrites also support a lambda-like syntax for specifying simple single line
1432bodies. The lambda body of a Rewrite expects a single expression, which is
1433implicitly returned, or a single
1434[operation rewrite statement](#operation-rewrite-statements):
1435
1436```pdll
1437Rewrite ReturnSelf(op: Op<my_dialect.foo>) => op;
1438Rewrite EraseOp(op: Op) => erase op;
1439```
1440
1441#### Native Rewriters
1442
1443Rewriters may also be defined outside of PDLL, and registered natively within
1444the C++ API.
1445
1446##### Importing existing Native Rewrites
1447
1448Rewrites defined externally can be imported into PDLL by specifying a
1449rewrite "declaration". This is similar to the PDLL form of defining a
1450rewrite but omits the body. Importing the declaration in this form allows for
1451PDLL to statically know the expected input and output types.
1452
1453```pdll
1454// Import a single input native rewrite that returns a new operation. This
1455// rewrite must be registered by the consumer of the compiled PDL.
1456Rewrite BuildOp(value: Value) -> Op;
1457
1458Pattern {
1459  replace op<my_dialect.old_op>(input: Value) with BuildOp(input);
1460}
1461```
1462
1463External rewrites are those registered explicitly with the `RewritePatternSet` via
1464the C++ PDL API. For example, the rewrite above may be registered as:
1465
1466```c++
1467static Operation *buildOpImpl(PDLResultList &results, Value value) {
1468  // insert special rewrite logic here.
1469  Operation *resultOp = ...;
1470  return resultOp;
1471}
1472
1473void registerNativeRewrite(RewritePatternSet &patterns) {
1474  patterns.getPDLPatterns().registerRewriteFunction("BuildOp", buildOpImpl);
1475}
1476```
1477
1478##### Defining Native Rewrites in PDLL
1479
1480In addition to importing native rewrites, PDLL also supports defining native
1481rewrites directly when compiling ahead-of-time (AOT) for C++. These rewrites can
1482be defined by specifying a string code block after the rewrite declaration:
1483
1484```pdll
1485Rewrite BuildOp(value: Value) -> (foo: Op<my_dialect.foo>, bar: Op<my_dialect.bar>) [{
1486  return {rewriter.create<my_dialect::FooOp>(value), rewriter.create<my_dialect::BarOp>()};
1487}];
1488
1489Pattern {
1490  let root = op<my_dialect.foo>(input: Value);
1491  rewrite root with {
1492    // Invoke the native rewrite and use the results when replacing the root.
1493    let results = BuildOp(input);
1494    replace root with (results.foo, results.bar);
1495  }
1496}
1497```
1498
1499The arguments of the rewrite are accessible within the code block via the
1500same name. See the ["type translation"](#native-rewrite-type-translations) below for
1501detailed information on how PDLL types are converted to native types. In addition to the
1502PDLL arguments, the code block may also access the current `PatternRewriter` using
1503`rewriter`. See the ["result translation"](#native-rewrite-result-translation) section
1504for detailed information on how the result type of the native function is determined.
1505
1506Taking the rewrite defined above as an example, this function would roughly be
1507translated into:
1508
1509```c++
1510std::tuple<my_dialect::FooOp, my_dialect::BarOp> BuildOp(Value value) {
1511  return {rewriter.create<my_dialect::FooOp>(value), rewriter.create<my_dialect::BarOp>()};
1512}
1513```
1514
1515###### Native Rewrite Type Translations
1516
1517The types of argument and result variables are generally mapped to the corresponding
1518MLIR type of the [constraint](#constraints) used. The rules of native `Rewrite` type translation
1519are identical to those of native `Constraint`s, please view the corresponding
1520[native `Constraint` type translation](#native-constraint-type-translations) section for a
1521detailed description of how the mapped type of a variable is determined.
1522
1523###### Native Rewrite Result Translation
1524
1525The results of a native rewrite are directly translated to the results of the native function,
1526using the type translation rules [described above](#native-rewrite-type-translations). The section
1527below describes the various result translation scenarios:
1528
1529* Zero Result
1530
1531```pdll
1532Rewrite createOp() [{
1533  rewriter.create<my_dialect::FooOp>();
1534}];
1535```
1536
1537In the case where a native `Rewrite` has no results, the native function returns `void`:
1538
1539```c++
1540void createOp(PatternRewriter &rewriter) {
1541  rewriter.create<my_dialect::FooOp>();
1542}
1543```
1544
1545* Single Result
1546
1547```pdll
1548Rewrite createOp() -> Op<my_dialect.foo> [{
1549  return rewriter.create<my_dialect::FooOp>();
1550}];
1551```
1552
1553In the case where a native `Rewrite` has a single result, the native function returns the corresponding
1554native type for that single result:
1555
1556```c++
1557my_dialect::FooOp createOp(PatternRewriter &rewriter) {
1558  return rewriter.create<my_dialect::FooOp>();
1559}
1560```
1561
1562* Multi Result
1563
1564```pdll
1565Rewrite complexRewrite(value: Value) -> (Op<my_dialect.foo>, FunctionOpInterface) [{
1566  ...
1567}];
1568```
1569
1570In the case where a native `Rewrite` has multiple results, the native function returns a `std::tuple<...>`
1571containing the corresponding native types for each of the results:
1572
1573```c++
1574std::tuple<my_dialect::FooOp, FunctionOpInterface>
1575complexRewrite(PatternRewriter &rewriter, Value value) {
1576  ...
1577}
1578```
1579
1580#### Defining Rewrites Inline
1581
1582In addition to global scope, PDLL Rewrites and Native Rewrites defined in PDLL
1583may be specified *inline* at any level of nesting. This means that they may be
1584defined in Patterns, other Rewrites, etc:
1585
1586```pdll
1587Rewrite GlobalRewrite(inputValue: Value) {
1588  Rewrite localRewrite(value: Value) {
1589    ...
1590  };
1591  Rewrite localNativeRewrite(value: Value) [{
1592    ...
1593  }];
1594  localRewrite(inputValue);
1595  localNativeRewrite(inputValue);
1596}
1597```
1598
1599Rewrites that are defined inline may also elide the name when used directly:
1600
1601```pdll
1602Rewrite GlobalRewrite(inputValue: Value) {
1603  Rewrite(value: Value) { ... }(inputValue);
1604  Rewrite(value: Value) [{ ... }](inputValue);
1605}
1606```
1607
1608When defined inline, PDLL rewrites may reference any previously defined
1609variable:
1610
1611```pdll
1612Rewrite GlobalRewrite(op: Op<my_dialect.foo>) {
1613  Rewrite localRewrite() {
1614    let results = op.results;
1615  };
1616}
1617```
1618