Skip to content

Commit 96e9786

Browse files
authored
[TableGen][GlobalISel] Add MIFlags matching & rewriting (#71179)
Also disables generation of MutateOpcode. It's almost never used in combiners anyway. If we really want to use it, it needs to be investigated & properly fixed (see TODO) Fixes #70780
1 parent 1a4754c commit 96e9786

File tree

11 files changed

+548
-10
lines changed

11 files changed

+548
-10
lines changed

llvm/docs/GlobalISel/MIRPatterns.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,44 @@ Semantics:
183183
* The root cannot have any output operands.
184184
* The root must be a CodeGenInstruction
185185

186+
Instruction Flags
187+
-----------------
188+
189+
MIR Patterns support both matching & writing ``MIFlags``.
190+
191+
.. code-block:: text
192+
:caption: Example
193+
194+
def Test : GICombineRule<
195+
(defs root:$dst),
196+
(match (G_FOO $dst, $src, (MIFlags FmNoNans, FmNoInfs))),
197+
(apply (G_BAR $dst, $src, (MIFlags FmReassoc)))>;
198+
199+
In ``apply`` patterns, we also support referring to a matched instruction to
200+
"take" its MIFlags.
201+
202+
.. code-block:: text
203+
:caption: Example
204+
205+
; We match NoNans/NoInfs, but $zext may have more flags.
206+
; Copy them all into the output instruction, and set Reassoc on the output inst.
207+
def TestCpyFlags : GICombineRule<
208+
(defs root:$dst),
209+
(match (G_FOO $dst, $src, (MIFlags FmNoNans, FmNoInfs)):$zext),
210+
(apply (G_BAR $dst, $src, (MIFlags $zext, FmReassoc)))>;
211+
212+
The ``not`` operator can be used to check that a flag is NOT present
213+
on a matched instruction, and to remove a flag from a generated instruction.
214+
215+
.. code-block:: text
216+
:caption: Example
217+
218+
; We match NoInfs but we don't want NoNans/Reassoc to be set. $zext may have more flags.
219+
; Copy them all into the output instruction but remove NoInfs on the output inst.
220+
def TestNot : GICombineRule<
221+
(defs root:$dst),
222+
(match (G_FOO $dst, $src, (MIFlags FmNoInfs, (not FmNoNans, FmReassoc))):$zext),
223+
(apply (G_BAR $dst, $src, (MIFlags $zext, (not FmNoInfs))))>;
186224
187225
Limitations
188226
-----------

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,13 @@ enum {
266266
/// - NewOpIdx
267267
GIM_CheckCanReplaceReg,
268268

269+
/// Check that a matched instruction has, or doesn't have a MIFlag.
270+
///
271+
/// - InsnID - Instruction to check.
272+
/// - Flag(s) - (can be one or more flags OR'd together)
273+
GIM_MIFlags,
274+
GIM_MIFlagsNot,
275+
269276
/// Predicates with 'let PredicateCodeUsesOperands = 1' need to examine some
270277
/// named operands that will be recorded in RecordedOperands. Names of these
271278
/// operands are referenced in predicate argument list. Emitter determines
@@ -344,6 +351,20 @@ enum {
344351
/// OpIdx starts at 0 for the first implicit def.
345352
GIR_SetImplicitDefDead,
346353

354+
/// Set or unset a MIFlag on an instruction.
355+
///
356+
/// - InsnID - Instruction to modify.
357+
/// - Flag(s) - (can be one or more flags OR'd together)
358+
GIR_SetMIFlags,
359+
GIR_UnsetMIFlags,
360+
361+
/// Copy the MIFlags of a matched instruction into an
362+
/// output instruction. The flags are OR'd together.
363+
///
364+
/// - InsnID - Instruction to modify.
365+
/// - OldInsnID - Matched instruction to copy flags from.
366+
GIR_CopyMIFlags,
367+
347368
/// Add a temporary register to the specified instruction
348369
/// - InsnID - Instruction ID to modify
349370
/// - TempRegID - The temporary register ID to add

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

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ bool GIMatchTableExecutor::executeMatchTable(
8888
if (Observer)
8989
Observer->changedInstr(*MIB);
9090
}
91-
92-
return true;
9391
};
9492

9593
// If the index is >= 0, it's an index in the type objects generated by
@@ -919,6 +917,32 @@ bool GIMatchTableExecutor::executeMatchTable(
919917
}
920918
break;
921919
}
920+
case GIM_MIFlags: {
921+
int64_t InsnID = MatchTable[CurrentIdx++];
922+
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];
923+
924+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
925+
dbgs() << CurrentIdx << ": GIM_MIFlags(MIs[" << InsnID
926+
<< "], " << Flags << ")\n");
927+
if ((State.MIs[InsnID]->getFlags() & Flags) != Flags) {
928+
if (handleReject() == RejectAndGiveUp)
929+
return false;
930+
}
931+
break;
932+
}
933+
case GIM_MIFlagsNot: {
934+
int64_t InsnID = MatchTable[CurrentIdx++];
935+
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];
936+
937+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
938+
dbgs() << CurrentIdx << ": GIM_MIFlagsNot(MIs[" << InsnID
939+
<< "], " << Flags << ")\n");
940+
if ((State.MIs[InsnID]->getFlags() & Flags)) {
941+
if (handleReject() == RejectAndGiveUp)
942+
return false;
943+
}
944+
break;
945+
}
922946
case GIM_Reject:
923947
DEBUG_WITH_TYPE(TgtExecutor::getName(),
924948
dbgs() << CurrentIdx << ": GIM_Reject\n");
@@ -1062,6 +1086,39 @@ bool GIMatchTableExecutor::executeMatchTable(
10621086
MI->getOperand(MI->getNumExplicitOperands() + OpIdx).setIsDead();
10631087
break;
10641088
}
1089+
case GIR_SetMIFlags: {
1090+
int64_t InsnID = MatchTable[CurrentIdx++];
1091+
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];
1092+
1093+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
1094+
dbgs() << CurrentIdx << ": GIR_SetMIFlags(OutMIs["
1095+
<< InsnID << "], " << Flags << ")\n");
1096+
MachineInstr *MI = OutMIs[InsnID];
1097+
MI->setFlags(MI->getFlags() | Flags);
1098+
break;
1099+
}
1100+
case GIR_UnsetMIFlags: {
1101+
int64_t InsnID = MatchTable[CurrentIdx++];
1102+
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];
1103+
1104+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
1105+
dbgs() << CurrentIdx << ": GIR_UnsetMIFlags(OutMIs["
1106+
<< InsnID << "], " << Flags << ")\n");
1107+
MachineInstr *MI = OutMIs[InsnID];
1108+
MI->setFlags(MI->getFlags() & ~Flags);
1109+
break;
1110+
}
1111+
case GIR_CopyMIFlags: {
1112+
int64_t InsnID = MatchTable[CurrentIdx++];
1113+
int64_t OldInsnID = MatchTable[CurrentIdx++];
1114+
1115+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
1116+
dbgs() << CurrentIdx << ": GIR_CopyMIFlags(OutMIs["
1117+
<< InsnID << "], MIs[" << OldInsnID << "])\n");
1118+
MachineInstr *MI = OutMIs[InsnID];
1119+
MI->setFlags(MI->getFlags() | State.MIs[OldInsnID]->getFlags());
1120+
break;
1121+
}
10651122
case GIR_AddTempRegister:
10661123
case GIR_AddTempSubRegister: {
10671124
int64_t InsnID = MatchTable[CurrentIdx++];

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,25 @@ def GIReplaceReg : GIBuiltinInst;
164164
// TODO: Allow using this directly, like (apply GIEraseRoot)
165165
def GIEraseRoot : GIBuiltinInst;
166166

167+
//===----------------------------------------------------------------------===//
168+
// Pattern MIFlags
169+
//===----------------------------------------------------------------------===//
170+
171+
class MIFlagEnum<string enumName> {
172+
string EnumName = "MachineInstr::" # enumName;
173+
}
174+
175+
def FmNoNans : MIFlagEnum<"FmNoNans">;
176+
def FmNoInfs : MIFlagEnum<"FmNoInfs">;
177+
def FmNsz : MIFlagEnum<"FmNsz">;
178+
def FmArcp : MIFlagEnum<"FmArcp">;
179+
def FmContract : MIFlagEnum<"FmContract">;
180+
def FmAfn : MIFlagEnum<"FmAfn">;
181+
def FmReassoc : MIFlagEnum<"FmReassoc">;
182+
183+
def MIFlags;
184+
// def not; -> Already defined as a SDNode
185+
167186
//===----------------------------------------------------------------------===//
168187

169188
def extending_load_matchdata : GIDefMatchData<"PreferredTuple">;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: llvm-tblgen -I %p/../../../include -gen-global-isel-combiner \
2+
// RUN: -combiners=MyCombiner %s | \
3+
// RUN: FileCheck %s
4+
5+
include "llvm/Target/Target.td"
6+
include "llvm/Target/GlobalISel/Combine.td"
7+
8+
def MyTargetISA : InstrInfo;
9+
def MyTarget : Target { let InstructionSet = MyTargetISA; }
10+
11+
def MIFlagsTest : GICombineRule<
12+
(defs root:$dst),
13+
(match (G_SEXT $dst, $tmp), (G_ZEXT $tmp, $src, (MIFlags FmReassoc, FmNsz, (not FmNoNans, FmArcp))):$mi),
14+
(apply (G_MUL $dst, $src, $src, (MIFlags $mi, FmReassoc, (not FmNsz, FmArcp))))>;
15+
16+
def MyCombiner: GICombiner<"GenMyCombiner", [MIFlagsTest]>;
17+
18+
// CHECK: const int64_t *GenMyCombiner::getMatchTable() const {
19+
// CHECK-NEXT: constexpr static int64_t MatchTable0[] = {
20+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ 49, // Rule ID 0 //
21+
// CHECK-NEXT: GIM_CheckSimplePredicate, GICXXPred_Simple_IsRule0Enabled,
22+
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SEXT,
23+
// CHECK-NEXT: // MIs[0] dst
24+
// CHECK-NEXT: // No operand predicates
25+
// CHECK-NEXT: // MIs[0] tmp
26+
// CHECK-NEXT: GIM_RecordInsnIgnoreCopies, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
27+
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_ZEXT,
28+
// CHECK-NEXT: GIM_MIFlags, /*MI*/1, MachineInstr::FmNsz | MachineInstr::FmReassoc,
29+
// CHECK-NEXT: GIM_MIFlagsNot, /*MI*/1, MachineInstr::FmArcp | MachineInstr::FmNoNans,
30+
// CHECK-NEXT: // MIs[1] src
31+
// CHECK-NEXT: // No operand predicates
32+
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
33+
// CHECK-NEXT: // Combiner Rule #0: MIFlagsTest
34+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::G_MUL,
35+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
36+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src
37+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src
38+
// CHECK-NEXT: GIR_CopyMIFlags, /*InsnID*/0, /*OldInsnID*/1,
39+
// CHECK-NEXT: GIR_SetMIFlags, /*InsnID*/0, MachineInstr::FmReassoc,
40+
// CHECK-NEXT: GIR_UnsetMIFlags, /*InsnID*/0, MachineInstr::FmNsz | MachineInstr::FmArcp,
41+
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
42+
// CHECK-NEXT: GIR_Done,
43+
// CHECK-NEXT: // Label 0: @49
44+
// CHECK-NEXT: GIM_Reject,
45+
// CHECK-NEXT: };
46+
// CHECK-NEXT: return MatchTable0;
47+
// CHECK-NEXT: }

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,19 @@ def root_def_has_multi_defs : GICombineRule<
271271
(match (RootDefHasMultiDefs $root, (i32 10))),
272272
(apply (COPY $root, (i32 0)))>;
273273

274+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: matching/writing MIFlags is only allowed on CodeGenInstruction patterns
275+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(DummyCXXPF ?:$x, (MIFlags FmArcp))'
276+
def miflags_in_pf : GICombineRule<
277+
(defs root:$x),
278+
(match (COPY $x, $y), (DummyCXXPF $x, (MIFlags FmArcp))),
279+
(apply (COPY $x, $y))>;
280+
281+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: '$pf' does not refer to a CodeGenInstruction in MIFlags of '__badtype_for_flagref_in_apply_apply_0'
282+
def badtype_for_flagref_in_apply : GICombineRule<
283+
(defs root:$dst),
284+
(match (G_ZEXT $dst, $src), (DummyCXXPF $src):$pf),
285+
(apply (G_MUL $dst, $src, $src, (MIFlags $pf)))>;
286+
274287
// CHECK: error: Failed to parse one or more rules
275288

276289
def MyCombiner: GICombiner<"GenMyCombiner", [
@@ -293,5 +306,7 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
293306
patfrag_in_apply,
294307
patfrag_cannot_be_root,
295308
inconsistent_arg_type,
296-
root_def_has_multi_defs
309+
root_def_has_multi_defs,
310+
miflags_in_pf,
311+
badtype_for_flagref_in_apply
297312
]>;

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,50 @@ def def_named_imm_apply : GICombineRule<
217217
(apply (COPY i32:$tmp, $y),
218218
(COPY $x, (i32 0):$tmp):$foo)>;
219219

220+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: MIFlags can only be present once on an instruction
221+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_ZEXT ?:$dst, ?:$src, (MIFlags FmArcp), (MIFlags FmArcp))'
222+
def multi_miflags : GICombineRule<
223+
(defs root:$dst),
224+
(match (G_ZEXT $dst, $src, (MIFlags FmArcp), (MIFlags FmArcp)):$mi),
225+
(apply (G_MUL $dst, $src, $src))>;
226+
227+
def NotAMIFlagEnum;
228+
229+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'NotAMIFlagEnum' is not a subclass of 'MIFlagEnum'
230+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_ZEXT ?:$dst, ?:$src, (MIFlags NotAMIFlagEnum))'
231+
def not_miflagenum_1 : GICombineRule<
232+
(defs root:$dst),
233+
(match (G_ZEXT $dst, $src, (MIFlags NotAMIFlagEnum)):$mi),
234+
(apply (G_MUL $dst, $src, $src))>;
235+
236+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'NotAMIFlagEnum' is not a subclass of 'MIFlagEnum'
237+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_ZEXT ?:$dst, ?:$src, (MIFlags (not NotAMIFlagEnum)))'
238+
def not_miflagenum_2 : GICombineRule<
239+
(defs root:$dst),
240+
(match (G_ZEXT $dst, $src, (MIFlags (not NotAMIFlagEnum))):$mi),
241+
242+
(apply (G_MUL $dst, $src, $src))>;
243+
244+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: matching/writing MIFlags is only allowed on CodeGenInstruction patterns
245+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceReg ?:$x, ?:$y, (MIFlags FmArcp))'
246+
def miflags_in_builtin : GICombineRule<
247+
(defs root:$x),
248+
(match (COPY $x, $y)),
249+
(apply (GIReplaceReg $x, $y, (MIFlags FmArcp)))>;
250+
251+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'match' patterns cannot refer to flags from other instructions
252+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: MIFlags in 'mi' refer to: impostor
253+
def using_flagref_in_match : GICombineRule<
254+
(defs root:$dst),
255+
(match (G_ZEXT $dst, $src, (MIFlags $impostor)):$mi),
256+
(apply (G_MUL $dst, $src, $src))>;
257+
258+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: unknown instruction '$impostor' referenced in MIFlags of '__badflagref_in_apply_apply_0'
259+
def badflagref_in_apply : GICombineRule<
260+
(defs root:$dst),
261+
(match (G_ZEXT $dst, $src):$mi),
262+
(apply (G_MUL $dst, $src, $src, (MIFlags $impostor)))>;
263+
220264
// CHECK: error: Failed to parse one or more rules
221265

222266
def MyCombiner: GICombiner<"GenMyCombiner", [
@@ -251,5 +295,11 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
251295
bad_mo_type_not_a_valuetype,
252296
untyped_new_reg_in_apply,
253297
def_named_imm_match,
254-
def_named_imm_apply
298+
def_named_imm_apply,
299+
multi_miflags,
300+
not_miflagenum_1,
301+
not_miflagenum_2,
302+
miflags_in_builtin,
303+
using_flagref_in_match,
304+
badflagref_in_apply
255305
]>;

llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,28 @@ def TypeOfTest : GICombineRule<
320320
(G_ZEXT $tmp, $src)),
321321
(apply (G_MUL $dst, (GITypeOf<"$src"> 0), (GITypeOf<"$dst"> -1)))>;
322322

323+
324+
// CHECK: (CombineRule name:MIFlagsTest id:11 root:dst
325+
// CHECK-NEXT: (MatchPats
326+
// CHECK-NEXT: <match_root>mi:(CodeGenInstructionPattern G_ZEXT operands:[<def>$dst, $src] (MIFlags (set MachineInstr::FmReassoc) (unset MachineInstr::FmNoNans, MachineInstr::FmArcp)))
327+
// CHECK-NEXT: )
328+
// CHECK-NEXT: (ApplyPats
329+
// CHECK-NEXT: <apply_root>__MIFlagsTest_apply_0:(CodeGenInstructionPattern G_MUL operands:[<def>$dst, $src, $src] (MIFlags (set MachineInstr::FmReassoc) (unset MachineInstr::FmNsz, MachineInstr::FmArcp) (copy mi)))
330+
// CHECK-NEXT: )
331+
// CHECK-NEXT: (OperandTable MatchPats
332+
// CHECK-NEXT: dst -> mi
333+
// CHECK-NEXT: src -> <live-in>
334+
// CHECK-NEXT: )
335+
// CHECK-NEXT: (OperandTable ApplyPats
336+
// CHECK-NEXT: dst -> __MIFlagsTest_apply_0
337+
// CHECK-NEXT: src -> <live-in>
338+
// CHECK-NEXT: )
339+
// CHECK-NEXT: )
340+
def MIFlagsTest : GICombineRule<
341+
(defs root:$dst),
342+
(match (G_ZEXT $dst, $src, (MIFlags FmReassoc, (not FmNoNans, FmArcp))):$mi),
343+
(apply (G_MUL $dst, $src, $src, (MIFlags $mi, FmReassoc, (not FmNsz, FmArcp))))>;
344+
323345
def MyCombiner: GICombiner<"GenMyCombiner", [
324346
WipOpcodeTest0,
325347
WipOpcodeTest1,
@@ -331,5 +353,6 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
331353
PatFragTest1,
332354
VariadicsInTest,
333355
VariadicsOutTest,
334-
TypeOfTest
356+
TypeOfTest,
357+
MIFlagsTest
335358
]>;

0 commit comments

Comments
 (0)