blob: 8253edf46356287c8f40370f9117e504f152d02b [file] [view] [edit]
(tblgen-mirpats)=
# MIR Patterns in TableGen
## User's Guide
This section is intended for developers who want to use MIR patterns in their
TableGen files.
`NOTE`:
This feature is still in active development. This document may become outdated
over time. If you see something that's incorrect, please update it.
### Use Cases
MIR patterns are supported in the following places:
- GlobalISel `GICombineRule`
- GlobalISel `GICombinePatFrag`
### Syntax
MIR patterns use the DAG datatype in TableGen.
```text
(inst operand0, operand1, ...)
```
`inst` must be a def which inherits from `Instruction` (e.g. `G_FADD`),
`Intrinsic` or `GICombinePatFrag`.
Operands essentially fall into one of two categories:
- immediates
- untyped, unnamed: `0`
- untyped, named: `0:$y`
- typed, unnamed: `(i32 0)`
- typed, named: `(i32 0):$y`
- machine operands
- untyped: `$x`
- typed: `i32:$x`
Semantics:
- A typed operand always adds an operand type check to the matcher.
- There is a trivial type inference system to propagate types.
- e.g. You only need to use `i32:$x` once in any pattern of a
`GICombinePatFrag` alternative or `GICombineRule`, then all
other patterns in that rule/alternative can simply use `$x`
(`i32:$x` is redundant).
- A named operand's behavior depends on whether the name has been seen before.
- For match patterns, reusing an operand name checks that the operands
are identical (see example 2 below).
- For apply patterns, reusing an operand name simply copies that operand into
the new instruction (see example 2 below).
Operands are ordered just like they would be in a MachineInstr: the defs (outs)
come first, then the uses (ins).
Patterns are generally grouped into another DAG datatype with a dummy operator
such as `match`, `apply`, `combine` or `pattern`.
Finally, any DAG datatype in TableGen can be named. This also holds for
patterns. e.g. the following is valid: `(G_FOO $root, (i32 0):$cst):$mypat`.
This may also be helpful to debug issues. Patterns are *always* named, and if
they don't have a name, an "anonymous" one is given to them. If you're trying
to debug an error related to a MIR pattern, but the error mentions an anonymous
pattern, you can try naming your patterns to see exactly where the issue is.
```{code-block} text
:caption: Pattern Example 1
// Match
// %imp = G_IMPLICIT_DEF
// %root = G_MUL %x, %imp
(match (G_IMPLICIT_DEF $imp),
(G_MUL $root, $x, $imp))
```
```{code-block} text
:caption: Pattern Example 2
// using $x twice here checks that the operand 1 and 2 of the G_AND are
// identical.
(match (G_AND $root, $x, $x))
// using $x again here copies operand 1 from G_AND into the new inst.
(apply (COPY $root, $x))
```
### Types
#### ValueType
Subclasses of `ValueType` are valid types, e.g. `i32`.
#### GITypeOf
`GITypeOf<"$x">` is a `GISpecialType` that allows for the creation of a
register or immediate with the same type as another (register) operand.
Type Parameters:
- An operand name as a string, prefixed by `$`.
Semantics:
- Can only appear in an 'apply' pattern.
- The operand name used must appear in the 'match' pattern of the
same `GICombineRule`.
```{code-block} text
:caption: 'Example: Immediate'
def mul_by_neg_one: GICombineRule <
(defs root:$root),
(match (G_MUL $dst, $x, -1)),
(apply (G_SUB $dst, (GITypeOf<"$x"> 0), $x))
>;
```
```{code-block} text
:caption: 'Example: Temp Reg'
def Test0 : GICombineRule<
(defs root:$dst),
(match (G_FMUL $dst, $src, -1)),
(apply (G_FSUB $dst, $src, $tmp),
(G_FNEG GITypeOf<"$dst">:$tmp, $src))>;
```
#### GIVariadic
`GIVariadic<>` is a `GISpecialType` that allows for matching 1 or
more operands remaining on an instruction.
Type Parameters:
- The minimum number of additional operands to match. Must be greater than zero.
- Default is 1.
- The maximum number of additional operands to match. Must be strictly greater
than the minimum.
- 0 can be used to indicate there is no upper limit.
- Default is 0.
Semantics:
- `GIVariadic<>` operands can only appear on variadic instructions.
- `GIVariadic<>` operands cannot be defs.
- `GIVariadic<>` operands can only appear as the last operand in a 'match' pattern.
- Each instance within a 'match' pattern must be uniquely named.
- Re-using a `GIVariadic<>` operand in an 'apply' pattern will result in all
the matched operands being copied from the original instruction.
- The min/max operands will result in the matcher checking that the number of operands
falls within that range.
- `GIVariadic<>` operands can be used in C++ code within a rule, which will
result in the operand name being expanded to a value of type `ArrayRef<MachineOperand>`.
```text
// bool checkBuildVectorToUnmerge(ArrayRef<MachineOperand>);
def build_vector_to_unmerge: GICombineRule <
(defs root:$root),
(match (G_BUILD_VECTOR $root, GIVariadic<>:$args),
[{ return checkBuildVectorToUnmerge(${args}); }]),
(apply (G_UNMERGE_VALUES $root, $args))
>;
```
```text
// Will additionally check the number of operands is >= 3 and <= 5.
// ($root is one operand, then 2 to 4 variadic operands).
def build_vector_to_unmerge: GICombineRule <
(defs root:$root),
(match (G_BUILD_VECTOR $root, GIVariadic<2, 4>:$two_to_four),
[{ return checkBuildVectorToUnmerge(${two_to_four}); }]),
(apply (G_UNMERGE_VALUES $root, $two_to_four))
>;
```
### Builtin Operations
MIR Patterns also offer builtin operations, also called "builtin instructions".
They offer some powerful features that would otherwise require use of C++ code.
#### GIReplaceReg
```{code-block} text
:caption: Usage
(apply (GIReplaceReg $old, $new))
```
Operands:
- `$old` (out) register defined by a matched instruction
- `$new` (in) register
Semantics:
- Can only appear in an 'apply' pattern.
- If both old/new are operands of matched instructions,
`canReplaceReg` is checked before applying the rule.
#### GIEraseRoot
```{code-block} text
:caption: Usage
(apply (GIEraseRoot))
```
Semantics:
- Can only appear as the only pattern of an 'apply' pattern list.
- The root cannot have any output operands.
- The root must be a CodeGenInstruction
### Instruction Flags
MIR Patterns support both matching & writing `MIFlags`.
```{code-block} text
:caption: Example
def Test : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src, (MIFlags FmNoNans, FmNoInfs))),
(apply (G_BAR $dst, $src, (MIFlags FmReassoc)))>;
```
In `apply` patterns, we also support referring to a matched instruction to
"take" its MIFlags.
```{code-block} text
:caption: Example
; We match NoNans/NoInfs, but $zext may have more flags.
; Copy them all into the output instruction, and set Reassoc on the output inst.
def TestCpyFlags : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src, (MIFlags FmNoNans, FmNoInfs)):$zext),
(apply (G_BAR $dst, $src, (MIFlags $zext, FmReassoc)))>;
```
The `not` operator can be used to check that a flag is NOT present
on a matched instruction, and to remove a flag from a generated instruction.
```{code-block} text
:caption: Example
; We match NoInfs but we don't want NoNans/Reassoc to be set. $zext may have more flags.
; Copy them all into the output instruction but remove NoInfs on the output inst.
def TestNot : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src, (MIFlags FmNoInfs, (not FmNoNans, FmReassoc))):$zext),
(apply (G_BAR $dst, $src, (MIFlags $zext, (not FmNoInfs))))>;
```
### Limitations
This a non-exhaustive list of known issues with MIR patterns at this time.
- Using `GICombinePatFrag` within another `GICombinePatFrag` is not
supported.
- `GICombinePatFrag` can only have a single root.
- Instructions with multiple defs cannot be the root of a `GICombinePatFrag`.
- Using `GICombinePatFrag` in the `apply` pattern of a `GICombineRule`
is not supported.
- We cannot rewrite a matched instruction other than the root.
- Matching/creating a (CImm) immediate >64 bits is not supported
(see comment in `GIM_CheckConstantInt`)
- There is currently no way to constrain two register/immediate types to
match. e.g. if a pattern needs to work on both i32 and i64, you either
need to leave it untyped and check the type in C++, or duplicate the
pattern.
- `GISpecialType` operands are not allowed within a `GICombinePatFrag`.
- `GIVariadic<>` matched operands must each have a unique name.
### GICombineRule
MIR patterns can appear in the `match` or `apply` patterns of a
`GICombineRule`.
The `root` of the rule can either be a def of an instruction, or a
named pattern. The latter is helpful when the instruction you want
to match has no defs. The former is generally preferred because
it's less verbose.
```{code-block} text
:caption: Combine Rule root is a def
// Fold x op 1 -> x
def right_identity_one: GICombineRule<
(defs root:$dst),
(match (G_MUL $dst, $x, 1)),
// Note: Patterns always need to create something, we can't just replace $dst with $x, so we need a COPY.
(apply (COPY $dst, $x))
>;
```
```{code-block} text
:caption: Combine Rule root is a named pattern
def Foo : GICombineRule<
(defs root:$root),
(match (G_ZEXT $tmp, (i32 0)),
(G_STORE $tmp, $ptr):$root),
(apply (G_STORE (i32 0), $ptr):$root)>;
```
Combine Rules also allow mixing C++ code with MIR patterns, so that you
may perform additional checks when matching, or run a C++ action after
matching.
Note that C++ code in `apply` pattern is mutually exclusive with
other patterns. However, you can freely mix C++ code with other
types of patterns in `match` patterns.
C++ code in `match` patterns is always run last, after all other
patterns matched.
```{code-block} text
:caption: Apply Pattern Examples with C++ code
// Valid
def Foo : GICombineRule<
(defs root:$root),
(match (G_ZEXT $tmp, (i32 0)),
(G_STORE $tmp, $ptr):$root,
"return myFinalCheck()"),
(apply "runMyAction(${root})")>;
// error: 'apply' patterns cannot mix C++ code with other types of patterns
def Bar : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src):$mi),
(apply (G_MUL $dst, $src, $src),
"runMyAction(${root})")>;
```
The following expansions are available for MIR patterns:
- operand names (`MachineOperand &`)
- pattern names (`MachineInstr *` for `match`,
`MachineInstrBuilder &` for apply)
```{code-block} text
:caption: Example C++ Expansions
def Foo : GICombineRule<
(defs root:$root),
(match (G_ZEXT $root, $src):$mi),
(apply "foobar(${root}.getReg(), ${src}.getReg(), ${mi}->hasImplicitDef())")>;
```
#### `combine` Operator
`GICombineRule` also supports a single `combine` pattern, which is a shorter way to
declare patterns that just match one or more instructions, then defer all remaining matching
and rewriting logic to C++ code.
```{code-block} text
:caption: Example usage of the combine operator.
// match + apply
def FooLong : GICombineRule<
(defs root:$root),
(match (G_ZEXT $root, $src):$mi, "return matchFoo(${mi});"),
(apply "applyFoo(${mi});")>;
// combine
def FooShort : GICombineRule<
(defs root:$root),
(combine (G_ZEXT $root, $src):$mi, "return combineFoo(${mi});")>;
```
This has a couple of advantages:
- We only need one C++ function, not two.
- We no longer need to use `GIDefMatchData` to pass information between the match/apply functions.
As described above, this is syntactic sugar for the match+apply form. In a `combine` pattern:
- Everything except C++ code is considered the `match` part.
- The C++ code is the `apply` part. C++ code is emitted in order of appearance.
:::{note}
The C++ code **must** return true if it changed any instruction. Returning false when changing
instructions is undefined behavior.
:::
#### Common Pattern #1: Replace a Register with Another
The 'apply' pattern must always redefine all operands defined by the match root.
Sometimes, we do not need to create instructions, simply replace a def with
another matched register. The `GIReplaceReg` builtin can do just that.
```text
def Foo : GICombineRule<
(defs root:$dst),
(match (G_FNEG $tmp, $src), (G_FNEG $dst, $tmp)),
(apply (GIReplaceReg $dst, $src))>;
```
This also works if the replacement register is a temporary register from the
`apply` pattern.
```text
def ReplaceTemp : GICombineRule<
(defs root:$a),
(match (G_BUILD_VECTOR $tmp, $x, $y),
(G_UNMERGE_VALUES $a, $b, $tmp)),
(apply (G_UNMERGE_VALUES $a, i32:$new, $y),
(GIReplaceReg $b, $new))>
```
#### Common Pattern #2: Erasing a Def-less Root
If we simply want to erase a def-less match root, we can use the
`GIEraseRoot` builtin.
```text
def Foo : GICombineRule<
(defs root:$mi),
(match (G_STORE $a, $b):$mi),
(apply (GIEraseRoot))>;
```
#### Common Pattern #3: Emitting a Constant Value
When an immediate operand appears in an 'apply' pattern, the behavior
depends on whether it's typed or not.
- If the immediate is typed, `MachineIRBuilder::buildConstant` is used
to create a `G_CONSTANT`. A `G_BUILD_VECTOR` will be used for vectors.
- If the immediate is untyped, a simple immediate is added
(`MachineInstrBuilder::addImm`).
There is of course a special case for `G_CONSTANT`. Immediates for
`G_CONSTANT` must always be typed, and a CImm is added
(`MachineInstrBuilder::addCImm`).
```{code-block} text
:caption: 'Constant Emission Examples:'
// Example output:
// %0 = G_CONSTANT i32 0
// %dst = COPY %0
def Foo : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src)),
(apply (COPY $dst, (i32 0)))>;
// Example output:
// %dst = COPY 0
// Note that this would be ill-formed because COPY
// expects a register operand!
def Bar : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src)),
(apply (COPY $dst, (i32 0)))>;
// Example output:
// %dst = G_CONSTANT i32 0
def Bux : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src)),
(apply (G_CONSTANT $dst, (i32 0)))>;
```
### GICombinePatFrag
`GICombinePatFrag` is an equivalent of `PatFrags` for MIR patterns.
They have two main usecases:
- Reduce repetition by creating a `GICombinePatFrag` for common
patterns (see example 1).
- Implicitly duplicate a CombineRule for multiple variants of a
pattern (see example 2).
A `GICombinePatFrag` is composed of three elements:
- zero or more `in` (def) parameter
- zero or more `out` parameter
- A list of MIR patterns that can match.
- When a `GICombinePatFrag` is used within a pattern, the pattern is
cloned once for each alternative that can match.
Parameters can have the following types:
- `gi_mo`, which is the implicit default (no type = `gi_mo`).
- Refers to any operand of an instruction (register, BB ref, imm, etc.).
- Can be used in both `in` and `out` parameters.
- Users of the PatFrag can only use an operand name for this
parameter (e.g. `(my_pat_frag $foo)`).
- `root`
- This is identical to `gi_mo`.
- Can only be used in `out` parameters to declare the root of the
pattern.
- Non-empty `out` parameter lists must always have exactly one `root`.
- `gi_imm`
- Refers to an (potentially typed) immediate.
- Can only be used in `in` parameters.
- Users of the PatFrag can only use an immediate for this parameter
(e.g. `(my_pat_frag 0)` or `(my_pat_frag (i32 0))`)
`out` operands can only be empty if the `GICombinePatFrag` only contains
C++ code. If the fragment contains instruction patterns, it has to have at
least one `out` operand of type `root`.
`in` operands are less restricted, but there is one important concept to
remember: you can pass "unbound" operand names, but only if the
`GICombinePatFrag` binds it. See example 3 below.
`GICombinePatFrag` are used just like any other instructions.
Note that the `out` operands are defs, so they come first in the list
of operands.
```{code-block} text
:caption: 'Example 1: Reduce Repetition'
def zext_cst : GICombinePatFrag<(outs root:$dst, $cst), (ins gi_imm:$val),
[(pattern (G_CONSTANT $cst, $val),
(G_ZEXT $dst, $cst))]
>;
def foo_to_impdef : GICombineRule<
(defs root:$dst),
(match (zext_cst $y, $cst, (i32 0))
(G_FOO $dst, $y)),
(apply (G_IMPLICIT_DEF $dst))>;
def store_ext_zero : GICombineRule<
(defs root:$root),
(match (zext_cst $y, $cst, (i32 0))
(G_STORE $y, $ptr):$root),
(apply (G_STORE $cst, $ptr):$root)>;
```
```{code-block} text
:caption: 'Example 2: Generate Multiple Rules at Once'
// Fold (freeze (freeze x)) -> (freeze x).
// Fold (fabs (fabs x)) -> (fabs x).
// Fold (fcanonicalize (fcanonicalize x)) -> (fcanonicalize x).
def idempotent_prop_frags : GICombinePatFrag<(outs root:$dst, $src), (ins),
[
(pattern (G_FREEZE $dst, $src), (G_FREEZE $src, $x)),
(pattern (G_FABS $dst, $src), (G_FABS $src, $x)),
(pattern (G_FCANONICALIZE $dst, $src), (G_FCANONICALIZE $src, $x))
]
>;
def idempotent_prop : GICombineRule<
(defs root:$dst),
(match (idempotent_prop_frags $dst, $src)),
(apply (COPY $dst, $src))>;
```
```{code-block} text
:caption: 'Example 3: Unbound Operand Names'
// This fragment binds $x to an operand in all of its
// alternative patterns.
def always_binds : GICombinePatFrag<
(outs root:$dst), (ins $x),
[
(pattern (G_FREEZE $dst, $x)),
(pattern (G_FABS $dst, $x)),
]
>;
// This fragment does not bind $x to an operand in any
// of its alternative patterns.
def does_not_bind : GICombinePatFrag<
(outs root:$dst), (ins $x),
[
(pattern (G_FREEZE $dst, $x)), // binds $x
(pattern (G_FOO $dst (i32 0))), // does not bind $x
(pattern "return myCheck(${x}.getReg())"), // does not bind $x
]
>;
// Here we pass $x, which is unbound, to always_binds.
// This works because if $x is unbound, always_binds will bind it for us.
def test0 : GICombineRule<
(defs root:$dst),
(match (always_binds $dst, $x)),
(apply (COPY $dst, $x))>;
// Here we pass $x, which is unbound, to does_not_bind.
// This cannot work because $x may not have been initialized in 'apply'.
// error: operand 'x' (for parameter 'src' of 'does_not_bind') cannot be unbound
def test1 : GICombineRule<
(defs root:$dst),
(match (does_not_bind $dst, $x)),
(apply (COPY $dst, $x))>;
// Here we pass $x, which is bound, to does_not_bind.
// This is fine because $x will always be bound when emitting does_not_bind
def test2 : GICombineRule<
(defs root:$dst),
(match (does_not_bind $tmp, $x)
(G_MUL $dst, $x, $tmp)),
(apply (COPY $dst, $x))>;
```
## Gallery
We should use precise patterns that state our intentions. Please avoid
using wip_match_opcode in patterns. It can lead to imprecise patterns.
```{code-block} text
:caption: Example fold zext(trunc:nuw)
// Imprecise: matches any G_ZEXT
def zext : GICombineRule<
(defs root:$root),
(match (wip_match_opcode G_ZEXT):$root,
[{ return Helper.matchZextOfTrunc(*${root}, ${matchinfo}); }]),
(apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
// Imprecise: matches G_ZEXT of G_TRUNC
def zext_of_trunc : GICombineRule<
(defs root:$root),
(match (G_TRUNC $src, $x),
(G_ZEXT $root, $src),
[{ return Helper.matchZextOfTrunc(${root}, ${matchinfo}); }]),
(apply [{ Helper.applyBuildFnMO(${root}, ${matchinfo}); }])>;
// Precise: matches G_ZEXT of G_TRUNC with nuw flag
def zext_of_trunc_nuw : GICombineRule<
(defs root:$root),
(match (G_TRUNC $src, $x, (MIFlags NoUWrap)),
(G_ZEXT $root, $src),
[{ return Helper.matchZextOfTrunc(${root}, ${matchinfo}); }]),
(apply [{ Helper.applyBuildFnMO(${root}, ${matchinfo}); }])>;
// Precise: lists all combine combinations
class ext_of_ext_opcodes<Instruction ext1Opcode, Instruction ext2Opcode> : GICombineRule <
(defs root:$root, build_fn_matchinfo:$matchinfo),
(match (ext2Opcode $second, $src):$Second,
(ext1Opcode $root, $second):$First,
[{ return Helper.matchExtOfExt(*${First}, *${Second}, ${matchinfo}); }]),
(apply [{ Helper.applyBuildFn(*${First}, ${matchinfo}); }])>;
def zext_of_zext : ext_of_ext_opcodes<G_ZEXT, G_ZEXT>;
def zext_of_anyext : ext_of_ext_opcodes<G_ZEXT, G_ANYEXT>;
def sext_of_sext : ext_of_ext_opcodes<G_SEXT, G_SEXT>;
def sext_of_anyext : ext_of_ext_opcodes<G_SEXT, G_ANYEXT>;
def anyext_of_anyext : ext_of_ext_opcodes<G_ANYEXT, G_ANYEXT>;
def anyext_of_zext : ext_of_ext_opcodes<G_ANYEXT, G_ZEXT>;
def anyext_of_sext : ext_of_ext_opcodes<G_ANYEXT, G_SEXT>;
```