Skip to content

Commit b26e6a8

Browse files
authored
[GlobalISel] Add GITypeOf special type (#66079)
Allows creating a register/immediate that uses the same type as a matched operand.
1 parent 03934e7 commit b26e6a8

File tree

13 files changed

+621
-79
lines changed

13 files changed

+621
-79
lines changed

llvm/docs/GlobalISel/MIRPatterns.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,48 @@ pattern, you can try naming your patterns to see exactly where the issue is.
101101
// using $x again here copies operand 1 from G_AND into the new inst.
102102
(apply (COPY $root, $x))
103103
104+
Types
105+
-----
106+
107+
ValueType
108+
~~~~~~~~~
109+
110+
Subclasses of ``ValueType`` are valid types, e.g. ``i32``.
111+
112+
GITypeOf
113+
~~~~~~~~
114+
115+
``GITypeOf<"$x">`` is a ``GISpecialType`` that allows for the creation of a
116+
register or immediate with the same type as another (register) operand.
117+
118+
Operand:
119+
120+
* An operand name as a string, prefixed by ``$``.
121+
122+
Semantics:
123+
124+
* Can only appear in an 'apply' pattern.
125+
* The operand name used must appear in the 'match' pattern of the
126+
same ``GICombineRule``.
127+
128+
.. code-block:: text
129+
:caption: Example: Immediate
130+
131+
def mul_by_neg_one: GICombineRule <
132+
(defs root:$root),
133+
(match (G_MUL $dst, $x, -1)),
134+
(apply (G_SUB $dst, (GITypeOf<"$x"> 0), $x))
135+
>;
136+
137+
.. code-block:: text
138+
:caption: Example: Temp Reg
139+
140+
def Test0 : GICombineRule<
141+
(defs root:$dst),
142+
(match (G_FMUL $dst, $src, -1)),
143+
(apply (G_FSUB $dst, $src, $tmp),
144+
(G_FNEG GITypeOf<"$dst">:$tmp, $src))>;
145+
104146
Builtin Operations
105147
------------------
106148

llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,6 @@ class CombinerHelper {
405405
void applyCombineTruncOfShift(MachineInstr &MI,
406406
std::pair<MachineInstr *, LLT> &MatchInfo);
407407

408-
/// Transform G_MUL(x, -1) to G_SUB(0, x)
409-
void applyCombineMulByNegativeOne(MachineInstr &MI);
410-
411408
/// Return true if any explicit use operand on \p MI is defined by a
412409
/// G_IMPLICIT_DEF.
413410
bool matchAnyExplicitUseIsUndef(MachineInstr &MI);

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,12 @@ enum {
275275
/// - StoreIdx - Store location in RecordedOperands.
276276
GIM_RecordNamedOperand,
277277

278+
/// Records an operand's register type into the set of temporary types.
279+
/// - InsnID - Instruction ID
280+
/// - OpIdx - Operand index
281+
/// - TempTypeIdx - Temp Type Index, always negative.
282+
GIM_RecordRegType,
283+
278284
/// Fail the current try-block, or completely fail to match if there is no
279285
/// current try-block.
280286
GIM_Reject,
@@ -522,6 +528,10 @@ class GIMatchTableExecutor {
522528
/// list. Currently such predicates don't have more then 3 arguments.
523529
std::array<const MachineOperand *, 3> RecordedOperands;
524530

531+
/// Types extracted from an instruction's operand.
532+
/// Whenever a type index is negative, we look here instead.
533+
SmallVector<LLT, 4> RecordedTypes;
534+
525535
MatcherState(unsigned MaxRenderers);
526536
};
527537

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ bool GIMatchTableExecutor::executeMatchTable(
9292
return true;
9393
};
9494

95+
// If the index is >= 0, it's an index in the type objects generated by
96+
// TableGen. If the index is <0, it's an index in the recorded types object.
97+
auto getTypeFromIdx = [&](int64_t Idx) -> LLT {
98+
if (Idx >= 0)
99+
return ExecInfo.TypeObjects[Idx];
100+
return State.RecordedTypes[1 - Idx];
101+
};
102+
95103
while (true) {
96104
assert(CurrentIdx != ~0u && "Invalid MatchTable index");
97105
int64_t MatcherOpcode = MatchTable[CurrentIdx++];
@@ -627,8 +635,7 @@ bool GIMatchTableExecutor::executeMatchTable(
627635
<< "), TypeID=" << TypeID << ")\n");
628636
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
629637
MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx);
630-
if (!MO.isReg() ||
631-
MRI.getType(MO.getReg()) != ExecInfo.TypeObjects[TypeID]) {
638+
if (!MO.isReg() || MRI.getType(MO.getReg()) != getTypeFromIdx(TypeID)) {
632639
if (handleReject() == RejectAndGiveUp)
633640
return false;
634641
}
@@ -679,6 +686,25 @@ bool GIMatchTableExecutor::executeMatchTable(
679686
State.RecordedOperands[StoreIdx] = &State.MIs[InsnID]->getOperand(OpIdx);
680687
break;
681688
}
689+
case GIM_RecordRegType: {
690+
int64_t InsnID = MatchTable[CurrentIdx++];
691+
int64_t OpIdx = MatchTable[CurrentIdx++];
692+
int64_t TypeIdx = MatchTable[CurrentIdx++];
693+
694+
DEBUG_WITH_TYPE(TgtExecutor::getName(),
695+
dbgs() << CurrentIdx << ": GIM_RecordRegType(MIs["
696+
<< InsnID << "]->getOperand(" << OpIdx
697+
<< "), TypeIdx=" << TypeIdx << ")\n");
698+
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
699+
assert(TypeIdx <= 0 && "Temp types always have negative indexes!");
700+
// Indexes start at -1.
701+
TypeIdx = 1 - TypeIdx;
702+
const auto &Op = State.MIs[InsnID]->getOperand(OpIdx);
703+
if (State.RecordedTypes.size() <= (uint64_t)TypeIdx)
704+
State.RecordedTypes.resize(TypeIdx + 1, LLT());
705+
State.RecordedTypes[TypeIdx] = MRI.getType(Op.getReg());
706+
break;
707+
}
682708
case GIM_CheckRegBankForClass: {
683709
int64_t InsnID = MatchTable[CurrentIdx++];
684710
int64_t OpIdx = MatchTable[CurrentIdx++];
@@ -1275,7 +1301,7 @@ bool GIMatchTableExecutor::executeMatchTable(
12751301
int64_t TypeID = MatchTable[CurrentIdx++];
12761302

12771303
State.TempRegisters[TempRegID] =
1278-
MRI.createGenericVirtualRegister(ExecInfo.TypeObjects[TypeID]);
1304+
MRI.createGenericVirtualRegister(getTypeFromIdx(TypeID));
12791305
DEBUG_WITH_TYPE(TgtExecutor::getName(),
12801306
dbgs() << CurrentIdx << ": TempRegs[" << TempRegID
12811307
<< "] = GIR_MakeTempReg(" << TypeID << ")\n");

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,24 @@ class GICombinePatFrag<dag outs, dag ins, list<dag> alts> {
110110
list<dag> Alternatives = alts;
111111
}
112112

113+
//===----------------------------------------------------------------------===//
114+
// Pattern Special Types
115+
//===----------------------------------------------------------------------===//
116+
117+
class GISpecialType;
118+
119+
// In an apply pattern, GITypeOf can be used to set the type of a new temporary
120+
// register to match the type of a matched register.
121+
//
122+
// This can only be used on temporary registers defined by the apply pattern.
123+
//
124+
// TODO: Make this work in matchers as well?
125+
//
126+
// FIXME: Syntax is very ugly.
127+
class GITypeOf<string opName> : GISpecialType {
128+
string OpName = opName;
129+
}
130+
113131
//===----------------------------------------------------------------------===//
114132
// Pattern Builtins
115133
//===----------------------------------------------------------------------===//
@@ -776,10 +794,9 @@ def trunc_shift: GICombineRule <
776794

777795
// Transform (mul x, -1) -> (sub 0, x)
778796
def mul_by_neg_one: GICombineRule <
779-
(defs root:$root),
780-
(match (wip_match_opcode G_MUL):$root,
781-
[{ return Helper.matchConstantOp(${root}->getOperand(2), -1); }]),
782-
(apply [{ Helper.applyCombineMulByNegativeOne(*${root}); }])
797+
(defs root:$dst),
798+
(match (G_MUL $dst, $x, -1)),
799+
(apply (G_SUB $dst, (GITypeOf<"$x"> 0), $x))
783800
>;
784801

785802
// Fold (xor (and x, y), y) -> (and (not x), y)

llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2351,18 +2351,6 @@ void CombinerHelper::applyCombineExtOfExt(
23512351
}
23522352
}
23532353

2354-
void CombinerHelper::applyCombineMulByNegativeOne(MachineInstr &MI) {
2355-
assert(MI.getOpcode() == TargetOpcode::G_MUL && "Expected a G_MUL");
2356-
Register DstReg = MI.getOperand(0).getReg();
2357-
Register SrcReg = MI.getOperand(1).getReg();
2358-
LLT DstTy = MRI.getType(DstReg);
2359-
2360-
Builder.setInstrAndDebugLoc(MI);
2361-
Builder.buildSub(DstReg, Builder.buildConstant(DstTy, 0), SrcReg,
2362-
MI.getFlags());
2363-
MI.eraseFromParent();
2364-
}
2365-
23662354
bool CombinerHelper::matchCombineTruncOfExt(
23672355
MachineInstr &MI, std::pair<Register, unsigned> &MatchInfo) {
23682356
assert(MI.getOpcode() == TargetOpcode::G_TRUNC && "Expected a G_TRUNC");
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 Test0 : GICombineRule<
12+
(defs root:$dst),
13+
(match (G_MUL $dst, $src, -1)),
14+
(apply (G_SUB $dst, (GITypeOf<"$src"> 0), $tmp),
15+
(G_CONSTANT GITypeOf<"$dst">:$tmp, (GITypeOf<"$src"> 42)))>;
16+
17+
// CHECK: const int64_t *GenMyCombiner::getMatchTable() const {
18+
// CHECK-NEXT: constexpr static int64_t MatchTable0[] = {
19+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ 57, // Rule ID 0 //
20+
// CHECK-NEXT: GIM_CheckSimplePredicate, GICXXPred_Simple_IsRule0Enabled,
21+
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_MUL,
22+
// CHECK-NEXT: // MIs[0] dst
23+
// CHECK-NEXT: GIM_RecordRegType, /*MI*/0, /*Op*/0, /*TempTypeIdx*/-1,
24+
// CHECK-NEXT: // MIs[0] src
25+
// CHECK-NEXT: GIM_RecordRegType, /*MI*/0, /*Op*/1, /*TempTypeIdx*/-2,
26+
// CHECK-NEXT: // MIs[0] Operand 2
27+
// CHECK-NEXT: GIM_CheckConstantInt, /*MI*/0, /*Op*/2, -1,
28+
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/-2,
29+
// CHECK-NEXT: GIR_BuildConstant, /*TempRegID*/1, /*Val*/0,
30+
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/-1,
31+
// CHECK-NEXT: // Combiner Rule #0: Test0
32+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::G_CONSTANT,
33+
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0,
34+
// CHECK-NEXT: GIR_AddCImm, /*InsnID*/0, /*Type*/-2, /*Imm*/42,
35+
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
36+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::G_SUB,
37+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/0, // dst
38+
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/1, /*TempRegFlags*/0,
39+
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/0,
40+
// CHECK-NEXT: GIR_Done,
41+
// CHECK-NEXT: // Label 0: @57
42+
// CHECK-NEXT: GIM_Reject,
43+
// CHECK-NEXT: };
44+
// CHECK-NEXT: return MatchTable0;
45+
// CHECK-NEXT: }
46+
47+
def MyCombiner: GICombiner<"GenMyCombiner", [
48+
Test0
49+
]>;

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,33 @@ def PatFragTest0 : GICombineRule<
7979
(match (FooPF $dst)),
8080
(apply (COPY $dst, (i32 0)))>;
8181

82+
83+
// CHECK: (CombineRule name:TypeOfProp id:2 root:x
84+
// CHECK-NEXT: (MatchPats
85+
// CHECK-NEXT: <match_root>__TypeOfProp_match_0:(CodeGenInstructionPattern G_ZEXT operands:[<def>$x, $y])
86+
// CHECK-NEXT: )
87+
// CHECK-NEXT: (ApplyPats
88+
// CHECK-NEXT: <apply_root>__TypeOfProp_apply_0:(CodeGenInstructionPattern G_ANYEXT operands:[<def>$x, GITypeOf<$y>:$tmp])
89+
// CHECK-NEXT: __TypeOfProp_apply_1:(CodeGenInstructionPattern G_ANYEXT operands:[<def>GITypeOf<$y>:$tmp, $y])
90+
// CHECK-NEXT: )
91+
// CHECK-NEXT: (OperandTable MatchPats
92+
// CHECK-NEXT: x -> __TypeOfProp_match_0
93+
// CHECK-NEXT: y -> <live-in>
94+
// CHECK-NEXT: )
95+
// CHECK-NEXT: (OperandTable ApplyPats
96+
// CHECK-NEXT: tmp -> __TypeOfProp_apply_1
97+
// CHECK-NEXT: x -> __TypeOfProp_apply_0
98+
// CHECK-NEXT: y -> <live-in>
99+
// CHECK-NEXT: )
100+
// CHECK-NEXT: )
101+
def TypeOfProp : GICombineRule<
102+
(defs root:$x),
103+
(match (G_ZEXT $x, $y)),
104+
(apply (G_ANYEXT $x, GITypeOf<"$y">:$tmp),
105+
(G_ANYEXT $tmp, $y))>;
106+
82107
def MyCombiner: GICombiner<"GenMyCombiner", [
83108
InstTest0,
84-
PatFragTest0
109+
PatFragTest0,
110+
TypeOfProp
85111
]>;

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,28 @@ def VariadicsOutTest : GICombineRule<
297297
(apply (COPY $a, (i32 0)),
298298
(COPY $b, (i32 0)))>;
299299

300+
// CHECK: (CombineRule name:TypeOfTest id:10 root:dst
301+
// CHECK-NEXT: (MatchPats
302+
// CHECK-NEXT: <match_root>__TypeOfTest_match_0:(CodeGenInstructionPattern COPY operands:[<def>$dst, $tmp])
303+
// CHECK-NEXT: __TypeOfTest_match_1:(CodeGenInstructionPattern G_ZEXT operands:[<def>$tmp, $src])
304+
// CHECK-NEXT: )
305+
// CHECK-NEXT: (ApplyPats
306+
// CHECK-NEXT: <apply_root>__TypeOfTest_apply_0:(CodeGenInstructionPattern G_MUL operands:[<def>$dst, (GITypeOf<$src> 0), (GITypeOf<$dst> -1)])
307+
// CHECK-NEXT: )
308+
// CHECK-NEXT: (OperandTable MatchPats
309+
// CHECK-NEXT: dst -> __TypeOfTest_match_0
310+
// CHECK-NEXT: src -> <live-in>
311+
// CHECK-NEXT: tmp -> __TypeOfTest_match_1
312+
// CHECK-NEXT: )
313+
// CHECK-NEXT: (OperandTable ApplyPats
314+
// CHECK-NEXT: dst -> __TypeOfTest_apply_0
315+
// CHECK-NEXT: )
316+
// CHECK-NEXT: )
317+
def TypeOfTest : GICombineRule<
318+
(defs root:$dst),
319+
(match (COPY $dst, $tmp),
320+
(G_ZEXT $tmp, $src)),
321+
(apply (G_MUL $dst, (GITypeOf<"$src"> 0), (GITypeOf<"$dst"> -1)))>;
300322

301323
def MyCombiner: GICombiner<"GenMyCombiner", [
302324
WipOpcodeTest0,
@@ -308,5 +330,6 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
308330
PatFragTest0,
309331
PatFragTest1,
310332
VariadicsInTest,
311-
VariadicsOutTest
333+
VariadicsOutTest,
334+
TypeOfTest
312335
]>;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// RUN: not llvm-tblgen -I %p/../../../include -gen-global-isel-combiner \
2+
// RUN: -combiners=MyCombiner %s 2>&1| \
3+
// RUN: FileCheck %s -implicit-check-not=error:
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+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: invalid operand name format 'unknown' in GITypeOf: expected '$' followed by an operand name
12+
def NoDollarSign : GICombineRule<
13+
(defs root:$dst),
14+
(match (G_ZEXT $dst, $src)),
15+
(apply (G_ANYEXT $dst, (GITypeOf<"unknown"> 0)))>;
16+
17+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'unknown' ('GITypeOf<$unknown>') does not refer to a matched operand!
18+
def UnknownOperand : GICombineRule<
19+
(defs root:$dst),
20+
(match (G_ZEXT $dst, $src)),
21+
(apply (G_ANYEXT $dst, (GITypeOf<"$unknown"> 0)))>;
22+
23+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GISpecialType is not supported in 'match' patterns
24+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: operand 1 of '__UseInMatch_match_0' has type 'GITypeOf<$dst>'
25+
def UseInMatch : GICombineRule<
26+
(defs root:$dst),
27+
(match (G_ZEXT $dst, (GITypeOf<"$dst"> 0))),
28+
(apply (G_ANYEXT $dst, (i32 0)))>;
29+
30+
// CHECK: :[[@LINE+3]]:{{[0-9]+}}: error: GISpecialType is not supported in GICombinePatFrag
31+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: note: operand 1 of '__PFWithTypeOF_alt0_pattern_0' has type 'GITypeOf<$dst>
32+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Could not parse GICombinePatFrag 'PFWithTypeOF'
33+
def PFWithTypeOF: GICombinePatFrag<
34+
(outs $dst), (ins),
35+
[(pattern (G_ANYEXT $dst, (GITypeOf<"$dst"> 0)))]>;
36+
37+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(PFWithTypeOF ?:$dst)'
38+
def UseInPF: GICombineRule<
39+
(defs root:$dst),
40+
(match (PFWithTypeOF $dst)),
41+
(apply (G_ANYEXT $dst, (i32 0)))>;
42+
43+
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GISpecialType is not supported in 'match' patterns
44+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: operand 1 of '__InferredUseInMatch_match_0' has type 'GITypeOf<$dst>'
45+
def InferredUseInMatch : GICombineRule<
46+
(defs root:$dst),
47+
(match (G_ZEXT $dst, $src)),
48+
(apply (G_ANYEXT $dst, GITypeOf<"$dst">:$src))>;
49+
50+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: conflicting types for operand 'src': first seen with 'i32' in '__InferenceConflict_match_0, now seen with 'GITypeOf<$dst>' in '__InferenceConflict_apply_0'
51+
def InferenceConflict : GICombineRule<
52+
(defs root:$dst),
53+
(match (G_ZEXT $dst, i32:$src)),
54+
(apply (G_ANYEXT $dst, GITypeOf<"$dst">:$src))>;
55+
56+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'tmp' ('GITypeOf<$tmp>') does not refer to a matched operand!
57+
def TypeOfApplyTmp : GICombineRule<
58+
(defs root:$dst),
59+
(match (G_ZEXT $dst, $src)),
60+
(apply (G_ANYEXT $dst, i32:$tmp),
61+
(G_ANYEXT $tmp, (GITypeOf<"$tmp"> 0)))>;
62+
63+
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse one or more rules
64+
def MyCombiner: GICombiner<"GenMyCombiner", [
65+
NoDollarSign,
66+
UnknownOperand,
67+
UseInMatch,
68+
UseInPF,
69+
InferredUseInMatch,
70+
InferenceConflict,
71+
TypeOfApplyTmp
72+
]>;

0 commit comments

Comments
 (0)