Skip to content

Commit 972c029

Browse files
authored
[GlobalISel][TableGen] MIR Pattern Variadics (#100563)
Allow for matching & rewriting a variable number of arguments in an instructions. Solves #87459
1 parent 129a8e1 commit 972c029

File tree

15 files changed

+625
-103
lines changed

15 files changed

+625
-103
lines changed

llvm/docs/GlobalISel/MIRPatterns.rst

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ GITypeOf
115115
``GITypeOf<"$x">`` is a ``GISpecialType`` that allows for the creation of a
116116
register or immediate with the same type as another (register) operand.
117117

118-
Operand:
118+
Type Parameters:
119119

120120
* An operand name as a string, prefixed by ``$``.
121121

@@ -143,6 +143,59 @@ Semantics:
143143
(apply (G_FSUB $dst, $src, $tmp),
144144
(G_FNEG GITypeOf<"$dst">:$tmp, $src))>;
145145
146+
GIVariadic
147+
~~~~~~~~~~
148+
149+
``GIVariadic<>`` is a ``GISpecialType`` that allows for matching 1 or
150+
more operands remaining on an instruction.
151+
152+
Type Parameters:
153+
154+
* The minimum number of additional operands to match. Must be greater than zero.
155+
156+
* Default is 1.
157+
158+
* The maximum number of additional operands to match. Must be strictly greater
159+
than the minimum.
160+
161+
* 0 can be used to indicate there is no upper limit.
162+
* Default is 0.
163+
164+
Semantics:
165+
166+
* ``GIVariadic<>`` operands can only appear on variadic instructions.
167+
* ``GIVariadic<>`` operands cannot be defs.
168+
* ``GIVariadic<>`` operands can only appear as the last operand in a 'match' pattern.
169+
* Each instance within a 'match' pattern must be uniquely named.
170+
* Re-using a ``GIVariadic<>`` operand in an 'apply' pattern will result in all
171+
the matched operands being copied from the original instruction.
172+
* The min/max operands will result in the matcher checking that the number of operands
173+
falls within that range.
174+
* ``GIVariadic<>`` operands can be used in C++ code within a rule, which will
175+
result in the operand name being expanded to a value of type ``ArrayRef<MachineOperand>``.
176+
177+
.. code-block:: text
178+
179+
// bool checkBuildVectorToUnmerge(ArrayRef<MachineOperand>);
180+
181+
def build_vector_to_unmerge: GICombineRule <
182+
(defs root:$root),
183+
(match (G_BUILD_VECTOR $root, GIVariadic<>:$args),
184+
[{ return checkBuildVectorToUnmerge(${args}); }]),
185+
(apply (G_UNMERGE_VALUES $root, $args))
186+
>;
187+
188+
.. code-block:: text
189+
190+
// Will additionally check the number of operands is >= 3 and <= 5.
191+
// ($root is one operand, then 2 to 4 variadic operands).
192+
def build_vector_to_unmerge: GICombineRule <
193+
(defs root:$root),
194+
(match (G_BUILD_VECTOR $root, GIVariadic<2, 4>:$two_to_four),
195+
[{ return checkBuildVectorToUnmerge(${two_to_four}); }]),
196+
(apply (G_UNMERGE_VALUES $root, $two_to_four))
197+
>;
198+
146199
Builtin Operations
147200
------------------
148201

@@ -240,6 +293,8 @@ This a non-exhaustive list of known issues with MIR patterns at this time.
240293
match. e.g. if a pattern needs to work on both i32 and i64, you either
241294
need to leave it untyped and check the type in C++, or duplicate the
242295
pattern.
296+
* ``GISpecialType`` operands are not allowed within a ``GICombinePatFrag``.
297+
* ``GIVariadic<>`` matched operands must each have a unique name.
243298

244299
GICombineRule
245300
-------------

llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
#ifndef LLVM_CODEGEN_GLOBALISEL_GIMATCHTABLEEXECUTOR_H
1616
#define LLVM_CODEGEN_GLOBALISEL_GIMATCHTABLEEXECUTOR_H
1717

18+
#include "llvm/ADT/ArrayRef.h"
1819
#include "llvm/ADT/Bitset.h"
1920
#include "llvm/ADT/DenseMap.h"
2021
#include "llvm/ADT/SmallVector.h"
2122
#include "llvm/CodeGen/GlobalISel/Utils.h"
2223
#include "llvm/CodeGen/MachineFunction.h"
24+
#include "llvm/CodeGen/MachineInstr.h"
2325
#include "llvm/CodeGenTypes/LowLevelType.h"
2426
#include "llvm/IR/Function.h"
2527
#include <bitset>
@@ -133,6 +135,12 @@ enum {
133135
/// - Ops(ULEB128) - Expected number of operands
134136
GIM_CheckNumOperands,
135137

138+
/// Check the instruction has a number of operands <= or >= than given number.
139+
/// - InsnID(ULEB128) - Instruction ID
140+
/// - Ops(ULEB128) - Number of operands
141+
GIM_CheckNumOperandsLE,
142+
GIM_CheckNumOperandsGE,
143+
136144
/// Check an immediate predicate on the specified instruction
137145
/// - InsnID(ULEB128) - Instruction ID
138146
/// - Pred(2) - The predicate to test
@@ -294,12 +302,15 @@ enum {
294302
/// Check the specified operands are identical.
295303
/// The IgnoreCopies variant looks through COPY instructions before
296304
/// comparing the operands.
305+
/// The "All" variants check all operands starting from the index.
297306
/// - InsnID(ULEB128) - Instruction ID
298307
/// - OpIdx(ULEB128) - Operand index
299308
/// - OtherInsnID(ULEB128) - Other instruction ID
300309
/// - OtherOpIdx(ULEB128) - Other operand index
301310
GIM_CheckIsSameOperand,
302311
GIM_CheckIsSameOperandIgnoreCopies,
312+
GIM_CheckAllSameOperand,
313+
GIM_CheckAllSameOperandIgnoreCopies,
303314

304315
/// Check we can replace all uses of a register with another.
305316
/// - OldInsnID(ULEB128)
@@ -362,6 +373,13 @@ enum {
362373
/// GIR_Copy but with both New/OldInsnIDs omitted and defaulting to zero.
363374
GIR_RootToRootCopy,
364375

376+
/// Copies all operand starting from OpIdx in OldInsnID into the new
377+
/// instruction NewInsnID.
378+
/// - NewInsnID(ULEB128) - Instruction ID to modify
379+
/// - OldInsnID(ULEB128) - Instruction ID to copy from
380+
/// - OpIdx(ULEB128) - The first operand to copy
381+
GIR_CopyRemaining,
382+
365383
/// Copy an operand to the specified instruction or add a zero register if the
366384
/// operand is a zero immediate.
367385
/// - NewInsnID(ULEB128) - Instruction ID to modify
@@ -713,6 +731,12 @@ class GIMatchTableExecutor {
713731
return Ret;
714732
}
715733

734+
static ArrayRef<MachineOperand> getRemainingOperands(const MachineInstr &MI,
735+
unsigned FirstVarOp) {
736+
auto Operands = drop_begin(MI.operands(), FirstVarOp);
737+
return {Operands.begin(), Operands.end()};
738+
}
739+
716740
public:
717741
// Faster ULEB128 decoder tailored for the Match Table Executor.
718742
//

llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,23 @@ bool GIMatchTableExecutor::executeMatchTable(
309309
break;
310310
}
311311

312+
case GIM_CheckNumOperandsGE:
313+
case GIM_CheckNumOperandsLE: {
314+
uint64_t InsnID = readULEB();
315+
uint64_t Expected = readULEB();
316+
const bool IsLE = (MatcherOpcode == GIM_CheckNumOperandsLE);
317+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
318+
dbgs() << CurrentIdx << ": GIM_CheckNumOperands"
319+
<< (IsLE ? "LE" : "GE") << "(MIs[" << InsnID
320+
<< "], Expected=" << Expected << ")\n");
321+
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
322+
const unsigned NumOps = State.MIs[InsnID]->getNumOperands();
323+
if (IsLE ? (NumOps <= Expected) : (NumOps >= Expected)) {
324+
if (handleReject() == RejectAndGiveUp)
325+
return false;
326+
}
327+
break;
328+
}
312329
case GIM_CheckNumOperands: {
313330
uint64_t InsnID = readULEB();
314331
uint64_t Expected = readULEB();
@@ -1081,6 +1098,22 @@ bool GIMatchTableExecutor::executeMatchTable(
10811098
break;
10821099
}
10831100

1101+
case GIR_CopyRemaining: {
1102+
uint64_t NewInsnID = readULEB();
1103+
uint64_t OldInsnID = readULEB();
1104+
uint64_t OpIdx = readULEB();
1105+
assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction");
1106+
MachineInstr &OldMI = *State.MIs[OldInsnID];
1107+
MachineInstrBuilder &NewMI = OutMIs[NewInsnID];
1108+
for (const auto &Op : drop_begin(OldMI.operands(), OpIdx))
1109+
NewMI.add(Op);
1110+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
1111+
dbgs() << CurrentIdx << ": GIR_CopyRemaining(OutMIs["
1112+
<< NewInsnID << "], MIs[" << OldInsnID
1113+
<< "], /*start=*/" << OpIdx << ")\n");
1114+
break;
1115+
}
1116+
10841117
case GIR_CopyOrAddZeroReg: {
10851118
uint64_t NewInsnID = readULEB();
10861119
uint64_t OldInsnID = readULEB();

llvm/include/llvm/Target/GlobalISel/Combine.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ class GITypeOf<string opName> : GISpecialType {
128128
string OpName = opName;
129129
}
130130

131+
// The type of an operand that can match a variable amount of operands.
132+
// This type contains a minimum and maximum number of operands to match.
133+
// The minimum must be 1 or more, as we cannot have an operand representing
134+
// zero operands, and the max can be zero (which means "unlimited") or a value
135+
// greater than the minimum.
136+
class GIVariadic<int min = 1, int max = 0> : GISpecialType {
137+
int MinArgs = min;
138+
int MaxArgs = max;
139+
}
140+
131141
//===----------------------------------------------------------------------===//
132142
// Pattern Builtins
133143
//===----------------------------------------------------------------------===//

llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-variadics.td

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,48 @@ def InstTest3 : GICombineRule<
2828
(match (G_UNMERGE_VALUES $a, $b, $c, $d)),
2929
(apply [{ APPLY }])>;
3030

31+
def VariadicTypeTestCxx : GICombineRule<
32+
(defs root:$a),
33+
(match (G_BUILD_VECTOR $a, GIVariadic<2, 4>:$b)),
34+
(apply [{ ${b} }])>;
35+
36+
def VariadicTypeTestReuse : GICombineRule<
37+
(defs root:$a),
38+
(match (G_BUILD_VECTOR $a, $c, GIVariadic<2, 4>:$b)),
39+
(apply (G_MERGE_VALUES $a, $b, $c))>;
40+
3141
def MyCombiner: GICombiner<"GenMyCombiner", [
3242
InstTest0,
3343
InstTest1,
3444
InstTest2,
35-
InstTest3
45+
InstTest3,
46+
VariadicTypeTestCxx,
47+
VariadicTypeTestReuse
3648
]>;
3749

50+
// CHECK: bool GenMyCombiner::runCustomAction(unsigned ApplyID, const MatcherState &State, NewMIVector &OutMIs) const {
51+
// CHECK-NEXT: Helper.getBuilder().setInstrAndDebugLoc(*State.MIs[0]);
52+
// CHECK-NEXT: switch(ApplyID) {
53+
// CHECK-NEXT: case GICXXCustomAction_GICombiner0:{
54+
// CHECK-NEXT: // Apply Patterns
55+
// CHECK-NEXT: APPLY
56+
// CHECK-NEXT: return true;
57+
// CHECK-NEXT: }
58+
// CHECK-NEXT: case GICXXCustomAction_GICombiner1:{
59+
// CHECK-NEXT: // Apply Patterns
60+
// CHECK-NEXT: getRemainingOperands(*State.MIs[0], 1)
61+
// CHECK-NEXT: return true;
62+
// CHECK-NEXT: }
63+
// CHECK-NEXT: }
64+
// CHECK-NEXT: llvm_unreachable("Unknown Apply Action");
65+
// CHECK-NEXT: }
66+
3867
// CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
3968
// CHECK-NEXT: constexpr static uint8_t MatchTable0[] = {
40-
// CHECK-NEXT: GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2([[#LOWER:]]), GIMT_Encode2([[#UPPER:]]), /*)*//*default:*//*Label 2*/ GIMT_Encode4([[#DEFAULT:]]),
69+
// CHECK-NEXT: GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(70), GIMT_Encode2(74), /*)*//*default:*//*Label 2*/ GIMT_Encode4(127),
4170
// CHECK-NEXT: /*TargetOpcode::G_UNMERGE_VALUES*//*Label 0*/ GIMT_Encode4(26), GIMT_Encode4(0), GIMT_Encode4(0),
4271
// CHECK-NEXT: /*TargetOpcode::G_BUILD_VECTOR*//*Label 1*/ GIMT_Encode4(55),
43-
// CHECK-NEXT: // Label 0: @[[#%u, mul(UPPER-LOWER, 4) + 10]]
72+
// CHECK-NEXT: // Label 0: @26
4473
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 3*/ GIMT_Encode4(40), // Rule ID 2 //
4574
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule2Enabled),
4675
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
@@ -77,7 +106,35 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
77106
// CHECK-NEXT: // Combiner Rule #1: InstTest1
78107
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner0),
79108
// CHECK-NEXT: // Label 5: @69
80-
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(83), // Rule ID 0 //
109+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(86), // Rule ID 4 //
110+
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule4Enabled),
111+
// CHECK-NEXT: GIM_CheckNumOperandsGE, /*MI*/0, /*Expected*/3,
112+
// CHECK-NEXT: GIM_CheckNumOperandsLE, /*MI*/0, /*Expected*/5,
113+
// CHECK-NEXT: // MIs[0] a
114+
// CHECK-NEXT: // No operand predicates
115+
// CHECK-NEXT: // MIs[0] b
116+
// CHECK-NEXT: // No operand predicates
117+
// CHECK-NEXT: // Combiner Rule #4: VariadicTypeTestCxx
118+
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner1),
119+
// CHECK-NEXT: // Label 6: @86
120+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(112), // Rule ID 5 //
121+
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule5Enabled),
122+
// CHECK-NEXT: GIM_CheckNumOperandsGE, /*MI*/0, /*Expected*/4,
123+
// CHECK-NEXT: GIM_CheckNumOperandsLE, /*MI*/0, /*Expected*/6,
124+
// CHECK-NEXT: // MIs[0] a
125+
// CHECK-NEXT: // No operand predicates
126+
// CHECK-NEXT: // MIs[0] c
127+
// CHECK-NEXT: // No operand predicates
128+
// CHECK-NEXT: // MIs[0] b
129+
// CHECK-NEXT: // No operand predicates
130+
// CHECK-NEXT: // Combiner Rule #5: VariadicTypeTestReuse
131+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(TargetOpcode::G_MERGE_VALUES),
132+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // a
133+
// CHECK-NEXT: GIR_CopyRemaining, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // b
134+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/1, // c
135+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
136+
// CHECK-NEXT: // Label 7: @112
137+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(126), // Rule ID 0 //
81138
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule0Enabled),
82139
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/4,
83140
// CHECK-NEXT: // MIs[0] a
@@ -90,10 +147,10 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
90147
// CHECK-NEXT: // No operand predicates
91148
// CHECK-NEXT: // Combiner Rule #0: InstTest0
92149
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner0),
93-
// CHECK-NEXT: // Label 6: @83
150+
// CHECK-NEXT: // Label 8: @126
94151
// CHECK-NEXT: GIM_Reject,
95-
// CHECK-NEXT: // Label 2: @[[#%u, DEFAULT]]
152+
// CHECK-NEXT: // Label 2: @127
96153
// CHECK-NEXT: GIM_Reject,
97-
// CHECK-NEXT: }; // Size: [[#%u, DEFAULT + 1]] bytes
154+
// CHECK-NEXT: }; // Size: 128 bytes
98155
// CHECK-NEXT: return MatchTable0;
99156
// CHECK-NEXT: }

llvm/test/TableGen/GlobalISelCombinerEmitter/operand-types.td

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,32 @@ def TypeOfProp : GICombineRule<
104104
(apply (G_ANYEXT $x, GITypeOf<"$y">:$tmp),
105105
(G_ANYEXT $tmp, $y))>;
106106

107+
// CHECK: (CombineRule name:VariadicTypeTest id:3 root:a
108+
// CHECK-NEXT: (MatchPats
109+
// CHECK-NEXT: <match_root>__VariadicTypeTest_match_0:(CodeGenInstructionPattern G_UNMERGE_VALUES operands:[<def>$a, <def>$b, GIVariadic<1,0>:$z])
110+
// CHECK-NEXT: )
111+
// CHECK-NEXT: (ApplyPats
112+
// CHECK-NEXT: <apply_root>__VariadicTypeTest_apply_0:(CodeGenInstructionPattern G_UNMERGE_VALUES operands:[<def>$a, <def>$b, GIVariadic<1,0>:$z])
113+
// CHECK-NEXT: )
114+
// CHECK-NEXT: (OperandTable MatchPats
115+
// CHECK-NEXT: a -> __VariadicTypeTest_match_0
116+
// CHECK-NEXT: b -> __VariadicTypeTest_match_0
117+
// CHECK-NEXT: z -> <live-in>
118+
// CHECK-NEXT: )
119+
// CHECK-NEXT: (OperandTable ApplyPats
120+
// CHECK-NEXT: a -> __VariadicTypeTest_apply_0
121+
// CHECK-NEXT: b -> __VariadicTypeTest_apply_0
122+
// CHECK-NEXT: z -> <live-in>
123+
// CHECK-NEXT: )
124+
// CHECK-NEXT: )
125+
def VariadicTypeTest: GICombineRule<
126+
(defs root:$a),
127+
(match (G_UNMERGE_VALUES $a, $b, GIVariadic<>:$z)),
128+
(apply (G_UNMERGE_VALUES $a, $b, $z))>;
129+
107130
def MyCombiner: GICombiner<"GenMyCombiner", [
108131
InstTest0,
109132
PatFragTest0,
110-
TypeOfProp
133+
TypeOfProp,
134+
VariadicTypeTest,
111135
]>;

llvm/test/TableGen/GlobalISelCombinerEmitter/typeof-errors.td

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def UnknownOperand : GICombineRule<
2121
(match (G_ZEXT $dst, $src)),
2222
(apply (G_ANYEXT $dst, (GITypeOf<"$unknown"> 0)))>;
2323

24-
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GISpecialType is not supported in 'match' patterns
24+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GITypeOf is not supported in 'match' patterns
2525
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: operand 1 of '__UseInMatch_match_0' has type 'GITypeOf<$dst>'
2626
def UseInMatch : GICombineRule<
2727
(defs root:$dst),
@@ -41,7 +41,7 @@ def UseInPF: GICombineRule<
4141
(match (PFWithTypeOF $dst)),
4242
(apply (G_ANYEXT $dst, (i32 0)))>;
4343

44-
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GISpecialType is not supported in 'match' patterns
44+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GITypeOf is not supported in 'match' patterns
4545
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: operand 1 of '__InferredUseInMatch_match_0' has type 'GITypeOf<$dst>'
4646
def InferredUseInMatch : GICombineRule<
4747
(defs root:$dst),
@@ -63,6 +63,13 @@ def TypeOfApplyTmp : GICombineRule<
6363
(apply (G_ANYEXT $dst, i32:$tmp),
6464
(G_ANYEXT $tmp, (GITypeOf<"$tmp"> 0)))>;
6565

66+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: type 'GITypeOf<$src>' is ill-formed: 'src' is a variadic pack operand
67+
def TypeOfVariadic : GICombineRule<
68+
(defs root:$dst),
69+
(match (G_BUILD_VECTOR $dst, $x, GIVariadic<>:$src)),
70+
(apply (G_ANYEXT GITypeOf<"$src">:$tmp, $x),
71+
(G_ANYEXT $dst, $tmp))>;
72+
6673
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse one or more rules
6774
def MyCombiner: GICombiner<"GenMyCombiner", [
6875
NoDollarSign,
@@ -71,5 +78,6 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
7178
UseInPF,
7279
InferredUseInMatch,
7380
InferenceConflict,
74-
TypeOfApplyTmp
81+
TypeOfApplyTmp,
82+
TypeOfVariadic
7583
]>;

0 commit comments

Comments
 (0)