Skip to content

Commit 7d81062

Browse files
authored
[GlobalISel] Refactor Combiner MatchData & Apply C++ Code Handling (#92239)
Combiners that use C++ code in their "apply" pattern only use that. They never mix it with MIR patterns as that has little added value. This patch restricts C++ apply code so that if C++ is used, we cannot use MIR patterns or builtins with it. Adding this restriction allows us to merge calls to match and apply C++ code together, which in turns makes it so we can just have MatchData variables on the stack. So before, we would have ``` GIM_CheckCxxInsnPredicate // match GIM_CheckCxxInsnPredicate // apply GIR_Done ``` Alongside a massive C++ struct holding the MatchData of all rules possible (which was a big space/perf issue). Now we just have ``` GIR_DoneWithCustomAction ``` And the function being ran just does ``` unsigned SomeMatchData; if (match(SomeMatchData)) apply(SomeMatchData) ``` This approach solves multiple issues in one: - MatchData handling is greatly simplified and more efficient, "don't pay for what you don't use" - We reduce the size of the match table - Calling C++ code has a certain overhead (we need a switch), and this overhead is only paid once now. Handling of C++ code inside PatFrags is unchanged though, that still emits a `GIM_CheckCxxInsnPredicate`. This is completely fine as they can't use MatchDatas.
1 parent 0bc1ec5 commit 7d81062

26 files changed

+498
-466
lines changed

llvm/docs/GlobalISel/MIRPatterns.rst

+26-2
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,32 @@ it's less verbose.
274274
275275
276276
Combine Rules also allow mixing C++ code with MIR patterns, so that you
277-
may perform additional checks when matching, or run additional code after
278-
rewriting a pattern.
277+
may perform additional checks when matching, or run a C++ action after
278+
matching.
279+
280+
Note that C++ code in ``apply`` pattern is mutually exclusive with
281+
other patterns. However, you can freely mix C++ code with other
282+
types of patterns in ``match`` patterns.
283+
C++ code in ``match`` patterns is always run last, after all other
284+
patterns matched.
285+
286+
.. code-block:: text
287+
:caption: Apply Pattern Examples with C++ code
288+
289+
// Valid
290+
def Foo : GICombineRule<
291+
(defs root:$root),
292+
(match (G_ZEXT $tmp, (i32 0)),
293+
(G_STORE $tmp, $ptr):$root,
294+
"return myFinalCheck()"),
295+
(apply "runMyAction(${root})")>;
296+
297+
// error: 'apply' patterns cannot mix C++ code with other types of patterns
298+
def Bar : GICombineRule<
299+
(defs root:$dst),
300+
(match (G_ZEXT $dst, $src):$mi),
301+
(apply (G_MUL $dst, $src, $src),
302+
"runMyAction(${root})")>;
279303
280304
The following expansions are available for MIR patterns:
281305

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

+5-10
Original file line numberDiff line numberDiff line change
@@ -471,16 +471,11 @@ enum {
471471
/// - RendererFnID(2) - Custom renderer function to call
472472
GIR_CustomRenderer,
473473

474-
/// Calls a C++ function to perform an action when a match is complete.
475-
/// The MatcherState is passed to the function to allow it to modify
476-
/// instructions.
477-
/// This is less constrained than a custom renderer and can update
478-
/// instructions
479-
/// in the state.
474+
/// Calls a C++ function that concludes the current match.
475+
/// The C++ function is free to return false and reject the match, or
476+
/// return true and mutate the instruction(s) (or do nothing, even).
480477
/// - FnID(2) - The function to call.
481-
/// TODO: Remove this at some point when combiners aren't reliant on it. It's
482-
/// a bit of a hack.
483-
GIR_CustomAction,
478+
GIR_DoneWithCustomAction,
484479

485480
/// Render operands to the specified instruction using a custom function,
486481
/// reading from a specific operand.
@@ -688,7 +683,7 @@ class GIMatchTableExecutor {
688683
llvm_unreachable("Subclass does not implement testSimplePredicate!");
689684
}
690685

691-
virtual void runCustomAction(unsigned, const MatcherState &State,
686+
virtual bool runCustomAction(unsigned, const MatcherState &State,
692687
NewMIVector &OutMIs) const {
693688
llvm_unreachable("Subclass does not implement runCustomAction!");
694689
}

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

+10-4
Original file line numberDiff line numberDiff line change
@@ -1335,13 +1335,19 @@ bool GIMatchTableExecutor::executeMatchTable(
13351335
-1); // Not a source operand of the old instruction.
13361336
break;
13371337
}
1338-
case GIR_CustomAction: {
1338+
case GIR_DoneWithCustomAction: {
13391339
uint16_t FnID = readU16();
13401340
DEBUG_WITH_TYPE(TgtExecutor::getName(),
1341-
dbgs() << CurrentIdx << ": GIR_CustomAction(FnID=" << FnID
1342-
<< ")\n");
1341+
dbgs() << CurrentIdx << ": GIR_DoneWithCustomAction(FnID="
1342+
<< FnID << ")\n");
13431343
assert(FnID > GICXXCustomAction_Invalid && "Expected a valid FnID");
1344-
runCustomAction(FnID, State, OutMIs);
1344+
if (runCustomAction(FnID, State, OutMIs)) {
1345+
propagateFlags();
1346+
return true;
1347+
}
1348+
1349+
if (handleReject() == RejectAndGiveUp)
1350+
return false;
13451351
break;
13461352
}
13471353
case GIR_CustomOperandRenderer: {

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ def bitreverse_shl : GICombineRule<
338338

339339
// Combine bitreverse(lshr (bitreverse x), y)) -> (shl x, y)
340340
def bitreverse_lshr : GICombineRule<
341-
(defs root:$d, build_fn_matchinfo:$matchinfo),
341+
(defs root:$d),
342342
(match (G_BITREVERSE $rev, $val),
343343
(G_LSHR $src, $rev, $amt):$mi,
344344
(G_BITREVERSE $d, $src),
@@ -1352,7 +1352,7 @@ def match_extract_of_element : GICombineRule<
13521352
(apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
13531353

13541354
def extract_vector_element_not_const : GICombineRule<
1355-
(defs root:$root, build_fn_matchinfo:$matchinfo),
1355+
(defs root:$root),
13561356
(match (G_INSERT_VECTOR_ELT $src, $x, $value, $idx),
13571357
(G_EXTRACT_VECTOR_ELT $root, $src, $idx)),
13581358
(apply (GIReplaceReg $root, $value))>;
@@ -1567,20 +1567,20 @@ def combine_shuffle_concat : GICombineRule<
15671567
(apply [{ Helper.applyCombineShuffleConcat(*${root}, ${matchinfo}); }])>;
15681568

15691569
def insert_vector_element_idx_undef : GICombineRule<
1570-
(defs root:$root, build_fn_matchinfo:$matchinfo),
1570+
(defs root:$root),
15711571
(match (G_IMPLICIT_DEF $idx),
15721572
(G_INSERT_VECTOR_ELT $root, $src, $elt, $idx)),
15731573
(apply (G_IMPLICIT_DEF $root))>;
15741574

15751575
def insert_vector_element_elt_undef : GICombineRule<
1576-
(defs root:$root, build_fn_matchinfo:$matchinfo),
1576+
(defs root:$root),
15771577
(match (G_IMPLICIT_DEF $elt),
15781578
(G_INSERT_VECTOR_ELT $root, $src, $elt, $idx),
15791579
[{ return isGuaranteedNotToBePoison(${src}.getReg(), MRI); }]),
15801580
(apply (GIReplaceReg $root, $src))>;
15811581

15821582
def insert_vector_element_extract_vector_element : GICombineRule<
1583-
(defs root:$root, build_fn_matchinfo:$matchinfo),
1583+
(defs root:$root),
15841584
(match (G_EXTRACT_VECTOR_ELT $elt, $src, $idx),
15851585
(G_INSERT_VECTOR_ELT $root, $src, $elt, $idx)),
15861586
(apply (GIReplaceReg $root, $src))>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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 OneMatchOneApply : GICombineRule<
12+
(defs root:$a),
13+
(match (G_FABS $a, $b), "return MATCH0;"),
14+
(apply "APPLY0")>;
15+
16+
def TwoMatchTwoApply : GICombineRule<
17+
(defs root:$a),
18+
(match (G_FNEG $a, $b), "return MATCH0;", "return MATCH1;"),
19+
(apply "APPLY0", "APPLY1")>;
20+
21+
def TwoMatchNoApply : GICombineRule<
22+
(defs root:$a),
23+
(match (G_STORE $x, $y):$a, "return MATCH0;", "return MATCH1;"),
24+
(apply (GIEraseRoot))>;
25+
26+
def NoMatchTwoApply : GICombineRule<
27+
(defs root:$a),
28+
(match (G_SEXT $a, $y)),
29+
(apply "APPLY0", "APPLY1")>;
30+
31+
def MyCombiner: GICombiner<"GenMyCombiner", [
32+
OneMatchOneApply,
33+
TwoMatchTwoApply,
34+
TwoMatchNoApply,
35+
NoMatchTwoApply
36+
]>;
37+
38+
// CHECK: bool GenMyCombiner::testMIPredicate_MI(unsigned PredicateID, const MachineInstr & MI, const MatcherState &State) const {
39+
// CHECK-NEXT: switch (PredicateID) {
40+
// CHECK-NEXT: case GICXXPred_MI_Predicate_GICombiner0: {
41+
// CHECK-NEXT: return MATCH0;
42+
// CHECK-NEXT: }
43+
// CHECK-NEXT: case GICXXPred_MI_Predicate_GICombiner1: {
44+
// CHECK-NEXT: return MATCH1;
45+
// CHECK-NEXT: }
46+
// CHECK-NEXT: }
47+
// CHECK-NEXT: llvm_unreachable("Unknown predicate");
48+
// CHECK-NEXT: return false;
49+
// CHECK-NEXT: }
50+
51+
// CHECK: bool GenMyCombiner::runCustomAction(unsigned ApplyID, const MatcherState &State, NewMIVector &OutMIs) const {
52+
// CHECK-NEXT: Helper.getBuilder().setInstrAndDebugLoc(*State.MIs[0]);
53+
// CHECK-NEXT: switch(ApplyID) {
54+
// CHECK-NEXT: case GICXXCustomAction_GICombiner0:{
55+
// CHECK-NEXT: // Match Patterns
56+
// CHECK-NEXT: if(![&](){return MATCH0;}()) {
57+
// CHECK-NEXT: return false;
58+
// CHECK-NEXT: }
59+
// CHECK-NEXT: // Apply Patterns
60+
// CHECK-NEXT: APPLY0
61+
// CHECK-NEXT: return true;
62+
// CHECK-NEXT: }
63+
// CHECK-NEXT: case GICXXCustomAction_GICombiner1:{
64+
// CHECK-NEXT: // Match Patterns
65+
// CHECK-NEXT: if(![&](){return MATCH0;}()) {
66+
// CHECK-NEXT: return false;
67+
// CHECK-NEXT: }
68+
// CHECK-NEXT: if(![&](){return MATCH1;}()) {
69+
// CHECK-NEXT: return false;
70+
// CHECK-NEXT: }
71+
// CHECK-NEXT: // Apply Patterns
72+
// CHECK-NEXT: APPLY0
73+
// CHECK-NEXT: APPLY1
74+
// CHECK-NEXT: return true;
75+
// CHECK-NEXT: }
76+
// CHECK-NEXT: case GICXXCustomAction_GICombiner2:{
77+
// CHECK-NEXT: // Apply Patterns
78+
// CHECK-NEXT: APPLY0
79+
// CHECK-NEXT: APPLY1
80+
// CHECK-NEXT: return true;
81+
// CHECK-NEXT: }
82+
// CHECK-NEXT: }
83+
// CHECK-NEXT: llvm_unreachable("Unknown Apply Action");
84+
// CHECK-NEXT: }
85+
86+
// CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
87+
// CHECK-NEXT: constexpr static uint8_t MatchTable0[] = {
88+
// CHECK-NEXT: GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(94), GIMT_Encode2(194), /*)*//*default:*//*Label 4*/ GIMT_Encode4(464),
89+
// CHECK-NEXT: /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(410), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
90+
// CHECK-NEXT: /*TargetOpcode::G_SEXT*//*Label 1*/ GIMT_Encode4(428), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
91+
// CHECK-NEXT: /*TargetOpcode::G_FNEG*//*Label 2*/ GIMT_Encode4(440), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0), GIMT_Encode4(0),
92+
// CHECK-NEXT: /*TargetOpcode::G_FABS*//*Label 3*/ GIMT_Encode4(452),
93+
// CHECK-NEXT: // Label 0: @410
94+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ GIMT_Encode4(427), // Rule ID 2 //
95+
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule2Enabled),
96+
// CHECK-NEXT: // MIs[0] x
97+
// CHECK-NEXT: // No operand predicates
98+
// CHECK-NEXT: // MIs[0] y
99+
// CHECK-NEXT: // No operand predicates
100+
// CHECK-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_GICombiner0),
101+
// CHECK-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_GICombiner1),
102+
// CHECK-NEXT: // Combiner Rule #2: TwoMatchNoApply
103+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
104+
// CHECK-NEXT: // Label 5: @427
105+
// CHECK-NEXT: GIM_Reject,
106+
// CHECK-NEXT: // Label 1: @428
107+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(439), // Rule ID 3 //
108+
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule3Enabled),
109+
// CHECK-NEXT: // MIs[0] a
110+
// CHECK-NEXT: // No operand predicates
111+
// CHECK-NEXT: // MIs[0] y
112+
// CHECK-NEXT: // No operand predicates
113+
// CHECK-NEXT: // Combiner Rule #3: NoMatchTwoApply
114+
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner2),
115+
// CHECK-NEXT: // Label 6: @439
116+
// CHECK-NEXT: GIM_Reject,
117+
// CHECK-NEXT: // Label 2: @440
118+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(451), // Rule ID 1 //
119+
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule1Enabled),
120+
// CHECK-NEXT: // MIs[0] a
121+
// CHECK-NEXT: // No operand predicates
122+
// CHECK-NEXT: // MIs[0] b
123+
// CHECK-NEXT: // No operand predicates
124+
// CHECK-NEXT: // Combiner Rule #1: TwoMatchTwoApply
125+
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner1),
126+
// CHECK-NEXT: // Label 7: @451
127+
// CHECK-NEXT: GIM_Reject,
128+
// CHECK-NEXT: // Label 3: @452
129+
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(463), // Rule ID 0 //
130+
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule0Enabled),
131+
// CHECK-NEXT: // MIs[0] a
132+
// CHECK-NEXT: // No operand predicates
133+
// CHECK-NEXT: // MIs[0] b
134+
// CHECK-NEXT: // No operand predicates
135+
// CHECK-NEXT: // Combiner Rule #0: OneMatchOneApply
136+
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner0),
137+
// CHECK-NEXT: // Label 8: @463
138+
// CHECK-NEXT: GIM_Reject,
139+
// CHECK-NEXT: // Label 4: @464
140+
// CHECK-NEXT: GIM_Reject,
141+
// CHECK-NEXT: }; // Size: 465 bytes
142+
// CHECK-NEXT: return MatchTable0;
143+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)