TableGenにおけるMIRパターン¶
ユーザーガイド¶
このセクションは、TableGenファイルでMIRパターンを使用したい開発者向けです。
注意
: この機能はまだ開発中です。このドキュメントは時間の経過とともに古くなる可能性があります。何か間違いを見つけたら、更新してください。
ユースケース¶
MIRパターンは、次の場所でサポートされています。
GlobalISel
GICombineRule
GlobalISel
GICombinePatFrag
構文¶
MIRパターンは、TableGenでDAGデータ型を使用します。
(inst operand0, operand1, ...)
inst
は、Instruction
(例:G_FADD
)、Intrinsic
、またはGICombinePatFrag
を継承するdefである必要があります。
オペランドは基本的に次の2つのカテゴリのいずれかに分類されます。
即値
型なし、名前なし:
0
型なし、名前付き:
0:$y
型付き、名前なし:
(i32 0)
型付き、名前付き:
(i32 0):$y
マシンオペランド
型なし:
$x
型付き:
i32:$x
セマンティクス
型付きオペランドは、常にマッチャーにオペランド型チェックを追加します。
型を伝播するための簡単な型推論システムがあります。
例:
i32:$x
は、GICombinePatFrag
の代替パターンまたはGICombineRule
の任意のパターンで1回だけ使用する必要があります。その後、そのルール/代替の他のすべてのパターンは、単に$x
を使用できます(i32:$x
は冗長です)。
名前付きオペランドの動作は、その名前が以前に表示されたかどうかによって異なります。
マッチパターンの場合、オペランド名を再利用すると、オペランドが同一であることが確認されます(以下の例2を参照)。
適用パターンの場合、オペランド名を再利用すると、そのオペランドが新しい命令に単純にコピーされます(以下の例2を参照)。
オペランドは、MachineInstrの場合と同様に順序付けられます。def(out)が最初にあり、次にuse(in)があります。
パターンは通常、match
、apply
、またはpattern
などのダミー演算子を持つ別のDAGデータ型にグループ化されます。
最後に、TableGenの任意のDAGデータ型に名前を付けることができます。これはパターンにも当てはまります。例:次のものは有効です:(G_FOO $root, (i32 0):$cst):$mypat
。これは、問題をデバッグするのに役立つ場合もあります。パターンは常に名前付きであり、名前がない場合は、「匿名」の名前が付けられます。MIRパターンに関連するエラーをデバッグしようとしているが、エラーが匿名パターンについて言及している場合は、パターンに名前を付けて、問題が正確にどこにあるかを確認できます。
// Match
// %imp = G_IMPLICIT_DEF
// %root = G_MUL %x, %imp
(match (G_IMPLICIT_DEF $imp),
(G_MUL $root, $x, $imp))
// 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))
型¶
ValueType¶
ValueType
のサブクラスは有効な型です。例:i32
。
GITypeOf¶
GITypeOf<"$x">
は、別の(レジスタ)オペランドと同じ型のレジスタまたは即値を作成できるGISpecialType
です。
型パラメーター
文字列としてのオペランド名。プレフィックスは
$
。
セマンティクス
「apply」パターンでのみ出現できます。
使用されるオペランド名は、同じ
GICombineRule
の「match」パターンに表示される必要があります。
def mul_by_neg_one: GICombineRule <
(defs root:$root),
(match (G_MUL $dst, $x, -1)),
(apply (G_SUB $dst, (GITypeOf<"$x"> 0), $x))
>;
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<>
は、命令に残っている1つ以上のオペランドを照合できるGISpecialType
です。
型パラメーター
照合する追加オペランドの最小数。ゼロより大きくなければなりません。
デフォルトは1です。
照合する追加オペランドの最大数。最小値より厳密に大きくなければなりません。
0は、上限がないことを示すために使用できます。
デフォルトは0です。
セマンティクス
GIVariadic<>
オペランドは、可変引数命令でのみ出現できます。GIVariadic<>
オペランドはdefにすることはできません。GIVariadic<>
オペランドは、「match」パターンの最後のオペランドとしてのみ出現できます。「match」パターン内の各インスタンスには、一意の名前を付ける必要があります。
「apply」パターンで
GIVariadic<>
オペランドを再利用すると、照合されたすべてのオペランドが元の命令からコピーされます。最小/最大オペランドは、オペランドの数がその範囲内にあることをマッチャーがチェックする結果になります。
GIVariadic<>
オペランドは、ルール内のC++コードで使用できます。これにより、オペランド名がArrayRef<MachineOperand>
型の値に展開されます。
// 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))
>;
// 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))
>;
組み込み操作¶
MIRパターンは、「組み込み命令」とも呼ばれる組み込み操作も提供します。これらは、そうでない場合はC++コードの使用が必要となる強力な機能を提供します。
GIReplaceReg¶
(apply (GIReplaceReg $old, $new))
オペランド
$old
(out)照合された命令によって定義されたレジスタ$new
(in)レジスタ
セマンティクス
「apply」パターンでのみ出現できます。
old/newの両方が照合された命令のオペランドである場合、ルールを適用する前に
canReplaceReg
がチェックされます。
GIEraseRoot¶
(apply (GIEraseRoot))
セマンティクス
「apply」パターンリストの唯一のパターンとしてのみ出現できます。
ルートに出力オペランドを含めることはできません。
ルートはCodeGenInstructionである必要があります
命令フラグ¶
MIRパターンは、MIFlags
の照合と書き込みの両方をサポートしています。
def Test : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src, (MIFlags FmNoNans, FmNoInfs))),
(apply (G_BAR $dst, $src, (MIFlags FmReassoc)))>;
apply
パターンでは、照合された命令を参照してそのMIFlagsを「取得」することもサポートしています。
; 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)))>;
not
演算子を使用して、フラグが照合された命令に存在しないことを確認し、生成された命令からフラグを削除できます。
; 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))))>;
制限事項¶
これは、現時点でMIRパターンで既知の問題の非網羅的なリストです。
別の
GICombinePatFrag
内でGICombinePatFrag
を使用することはサポートされていません。GICombinePatFrag
は単一のルートしか持つことができません。複数のdefを持つ命令は、
GICombinePatFrag
のルートにすることはできません。GICombineRule
のapply
パターンでGICombinePatFrag
を使用することはサポートされていません。ルート以外の照合された命令を書き換えることはできません。
(CImm)即値> 64ビットの照合/作成はサポートされていません(
GIM_CheckConstantInt
のコメントを参照)。現在、2つのレジスタ/即値型を一致させる方法はありません。例えば、パターンが i32 と i64 の両方で動作する必要がある場合、型なしのままにして C++ で型をチェックするか、パターンを複製する必要があります。
GISpecialType
オペランドは、GICombinePatFrag
内では許可されていません。GIVariadic<>
でマッチしたオペランドは、それぞれ一意の名前を持つ必要があります。
GICombineRule¶
MIR パターンは、GICombineRule
の match
または apply
パターンに記述できます。
ルールの root
は、命令の def、または名前付きパターンのいずれかになります。後者は、マッチさせたい命令に 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))
>;
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 Rule では、C++ コードと MIR パターンを混在させることもできるため、マッチング時に追加のチェックを実行したり、マッチング後に C++ アクションを実行したりできます。
apply
パターンの C++ コードは、他のパターンと相互に排他的であることに注意してください。ただし、match
パターンでは、C++ コードを他の種類のパターンと自由に混在させることができます。match
パターンの C++ コードは、常に他のすべてのパターンがマッチングされた後に最後に実行されます。
// 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})")>;
MIR パターンでは、以下の展開が可能です。
オペランド名 (
MachineOperand &
)パターン名 (
match
の場合はMachineInstr *
、apply の場合はMachineInstrBuilder &
)
def Foo : GICombineRule<
(defs root:$root),
(match (G_ZEXT $root, $src):$mi),
(apply "foobar(${root}.getReg(), ${src}.getReg(), ${mi}->hasImplicitDef())")>;
共通パターン #1: レジスタを別のレジスタで置換する¶
'apply' パターンは、マッチルートによって定義されたすべてのオペランドを常に再定義する必要があります。場合によっては、命令を作成する必要はなく、単に def を別のマッチしたレジスタで置換するだけで済みます。GIReplaceReg
ビルトインは、まさにそれを行うことができます。
def Foo : GICombineRule<
(defs root:$dst),
(match (G_FNEG $tmp, $src), (G_FNEG $dst, $tmp)),
(apply (GIReplaceReg $dst, $src))>;
置換レジスタが apply
パターンからの一時レジスタである場合も、これは機能します。
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))>
共通パターン #2: Def なしのルートを消去する¶
単に def のないマッチルートを消去したい場合は、GIEraseRoot
ビルトインを使用できます。
def Foo : GICombineRule<
(defs root:$mi),
(match (G_STORE $a, $b):$mi),
(apply (GIEraseRoot))>;
共通パターン #3: 定数値をエミットする¶
即値オペランドが 'apply' パターンに現れる場合、その動作は型付きであるかどうかに依存します。
即値が型付きの場合、
MachineIRBuilder::buildConstant
がG_CONSTANT
を作成するために使用されます。G_BUILD_VECTOR
はベクトルに使用されます。即値が型なしの場合、単純な即値が追加されます (
MachineInstrBuilder::addImm
)。
もちろん、G_CONSTANT
には特別なケースがあります。G_CONSTANT
の即値は常に型付きである必要があり、CImm が追加されます (MachineInstrBuilder::addCImm
)。
// 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
は、MIR パターンの PatFrags
に相当するものです。これらには、主に2つのユースケースがあります。
共通パターンの
GICombinePatFrag
を作成することで、繰り返しを減らします(例1を参照)。パターンの複数のバリアントに対して、CombineRule を暗黙的に複製します(例2を参照)。
GICombinePatFrag
は、以下の3つの要素で構成されます。
0個以上の
in
(def) パラメータ0個以上の
out
パラメータマッチング可能な MIR パターンのリスト。
GICombinePatFrag
がパターン内で使用されると、マッチング可能な代替案ごとにパターンが一度複製されます。
パラメータは、以下の型を持つことができます。
gi_mo
。これは暗黙のデフォルトです(型なし =gi_mo
)。命令の任意のオペランド(レジスタ、BB参照、即値など)を参照します。
in
パラメータとout
パラメータの両方で使用できます。PatFrag のユーザーは、このパラメータにオペランド名のみを使用できます(例:
(my_pat_frag $foo)
)。
root
これは
gi_mo
と同じです。パターンのルートを宣言するために、
out
パラメータでのみ使用できます。空でない
out
パラメータリストは、常に正確に1つのroot
を持つ必要があります。
gi_imm
(型付きの可能性がある) 即値を参照します。
in
パラメータでのみ使用できます。PatFrag のユーザーは、このパラメータに即値のみを使用できます(例:
(my_pat_frag 0)
または(my_pat_frag (i32 0))
)。
out
オペランドは、GICombinePatFrag
に C++ コードのみが含まれている場合にのみ空にできます。フラグメントに命令パターンが含まれている場合は、少なくとも1つの root
型の out
オペランドを持つ必要があります。
in
オペランドには制約が少ないですが、覚えておくべき重要な概念が1つあります。つまり、GICombinePatFrag
がバインドする場合にのみ、「アンバウンド」のオペランド名を渡すことができます。以下の例 3 を参照してください。
GICombinePatFrag
は、他の命令と同じように使用されます。out
オペランドは def であるため、オペランドのリストの最初に現れることに注意してください。
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)>;
// 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))>;
// 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))>;
ギャラリー¶
意図を明確に示す正確なパターンを使用する必要があります。パターンで wip_match_opcode を使用することは避けてください。
// 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}); }])>;