Skip to content

[GlobalISel] Refactor Combiner MatchData & Apply C++ Code Handling #92239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions llvm/docs/GlobalISel/MIRPatterns.rst
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,32 @@ it's less verbose.


Combine Rules also allow mixing C++ code with MIR patterns, so that you
may perform additional checks when matching, or run additional code after
rewriting a pattern.
may perform additional checks when matching, or run a C++ action after
matching.

Note that C++ code in ``apply`` pattern is mutually exclusive with
other patterns. However, you can freely mix C++ code with other
types of patterns in ``match`` patterns.
C++ code in ``match`` patterns is always run last, after all other
patterns matched.

.. code-block:: text
:caption: Apply Pattern Examples with C++ code

// 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})")>;

The following expansions are available for MIR patterns:

Expand Down
15 changes: 5 additions & 10 deletions llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,16 +471,11 @@ enum {
/// - RendererFnID(2) - Custom renderer function to call
GIR_CustomRenderer,

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

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

virtual void runCustomAction(unsigned, const MatcherState &State,
virtual bool runCustomAction(unsigned, const MatcherState &State,
NewMIVector &OutMIs) const {
llvm_unreachable("Subclass does not implement runCustomAction!");
}
Expand Down
14 changes: 10 additions & 4 deletions llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1335,13 +1335,19 @@ bool GIMatchTableExecutor::executeMatchTable(
-1); // Not a source operand of the old instruction.
break;
}
case GIR_CustomAction: {
case GIR_DoneWithCustomAction: {
uint16_t FnID = readU16();
DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIR_CustomAction(FnID=" << FnID
<< ")\n");
dbgs() << CurrentIdx << ": GIR_DoneWithCustomAction(FnID="
<< FnID << ")\n");
assert(FnID > GICXXCustomAction_Invalid && "Expected a valid FnID");
runCustomAction(FnID, State, OutMIs);
if (runCustomAction(FnID, State, OutMIs)) {
propagateFlags();
return true;
}

if (handleReject() == RejectAndGiveUp)
return false;
break;
}
case GIR_CustomOperandRenderer: {
Expand Down
10 changes: 5 additions & 5 deletions llvm/include/llvm/Target/GlobalISel/Combine.td
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ def bitreverse_shl : GICombineRule<

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

def extract_vector_element_not_const : GICombineRule<
(defs root:$root, build_fn_matchinfo:$matchinfo),
(defs root:$root),
(match (G_INSERT_VECTOR_ELT $src, $x, $value, $idx),
(G_EXTRACT_VECTOR_ELT $root, $src, $idx)),
(apply (GIReplaceReg $root, $value))>;
Expand Down Expand Up @@ -1567,20 +1567,20 @@ def combine_shuffle_concat : GICombineRule<
(apply [{ Helper.applyCombineShuffleConcat(*${root}, ${matchinfo}); }])>;

def insert_vector_element_idx_undef : GICombineRule<
(defs root:$root, build_fn_matchinfo:$matchinfo),
(defs root:$root),
(match (G_IMPLICIT_DEF $idx),
(G_INSERT_VECTOR_ELT $root, $src, $elt, $idx)),
(apply (G_IMPLICIT_DEF $root))>;

def insert_vector_element_elt_undef : GICombineRule<
(defs root:$root, build_fn_matchinfo:$matchinfo),
(defs root:$root),
(match (G_IMPLICIT_DEF $elt),
(G_INSERT_VECTOR_ELT $root, $src, $elt, $idx),
[{ return isGuaranteedNotToBePoison(${src}.getReg(), MRI); }]),
(apply (GIReplaceReg $root, $src))>;

def insert_vector_element_extract_vector_element : GICombineRule<
(defs root:$root, build_fn_matchinfo:$matchinfo),
(defs root:$root),
(match (G_EXTRACT_VECTOR_ELT $elt, $src, $idx),
(G_INSERT_VECTOR_ELT $root, $src, $elt, $idx)),
(apply (GIReplaceReg $root, $src))>;
Expand Down
143 changes: 143 additions & 0 deletions llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// RUN: llvm-tblgen -I %p/../../../include -gen-global-isel-combiner \
// RUN: -combiners=MyCombiner %s | \
// RUN: FileCheck %s

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

def MyTargetISA : InstrInfo;
def MyTarget : Target { let InstructionSet = MyTargetISA; }

def OneMatchOneApply : GICombineRule<
(defs root:$a),
(match (G_FABS $a, $b), "return MATCH0;"),
(apply "APPLY0")>;

def TwoMatchTwoApply : GICombineRule<
(defs root:$a),
(match (G_FNEG $a, $b), "return MATCH0;", "return MATCH1;"),
(apply "APPLY0", "APPLY1")>;

def TwoMatchNoApply : GICombineRule<
(defs root:$a),
(match (G_STORE $x, $y):$a, "return MATCH0;", "return MATCH1;"),
(apply (GIEraseRoot))>;

def NoMatchTwoApply : GICombineRule<
(defs root:$a),
(match (G_SEXT $a, $y)),
(apply "APPLY0", "APPLY1")>;

def MyCombiner: GICombiner<"GenMyCombiner", [
OneMatchOneApply,
TwoMatchTwoApply,
TwoMatchNoApply,
NoMatchTwoApply
]>;

// CHECK: bool GenMyCombiner::testMIPredicate_MI(unsigned PredicateID, const MachineInstr & MI, const MatcherState &State) const {
// CHECK-NEXT: switch (PredicateID) {
// CHECK-NEXT: case GICXXPred_MI_Predicate_GICombiner0: {
// CHECK-NEXT: return MATCH0;
// CHECK-NEXT: }
// CHECK-NEXT: case GICXXPred_MI_Predicate_GICombiner1: {
// CHECK-NEXT: return MATCH1;
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: llvm_unreachable("Unknown predicate");
// CHECK-NEXT: return false;
// CHECK-NEXT: }

// CHECK: bool GenMyCombiner::runCustomAction(unsigned ApplyID, const MatcherState &State, NewMIVector &OutMIs) const {
// CHECK-NEXT: Helper.getBuilder().setInstrAndDebugLoc(*State.MIs[0]);
// CHECK-NEXT: switch(ApplyID) {
// CHECK-NEXT: case GICXXCustomAction_GICombiner0:{
// CHECK-NEXT: // Match Patterns
// CHECK-NEXT: if(![&](){return MATCH0;}()) {
// CHECK-NEXT: return false;
// CHECK-NEXT: }
// CHECK-NEXT: // Apply Patterns
// CHECK-NEXT: APPLY0
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-NEXT: case GICXXCustomAction_GICombiner1:{
// CHECK-NEXT: // Match Patterns
// CHECK-NEXT: if(![&](){return MATCH0;}()) {
// CHECK-NEXT: return false;
// CHECK-NEXT: }
// CHECK-NEXT: if(![&](){return MATCH1;}()) {
// CHECK-NEXT: return false;
// CHECK-NEXT: }
// CHECK-NEXT: // Apply Patterns
// CHECK-NEXT: APPLY0
// CHECK-NEXT: APPLY1
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-NEXT: case GICXXCustomAction_GICombiner2:{
// CHECK-NEXT: // Apply Patterns
// CHECK-NEXT: APPLY0
// CHECK-NEXT: APPLY1
// CHECK-NEXT: return true;
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: llvm_unreachable("Unknown Apply Action");
// CHECK-NEXT: }

// CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
// CHECK-NEXT: constexpr static uint8_t MatchTable0[] = {
// CHECK-NEXT: GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(94), GIMT_Encode2(194), /*)*//*default:*//*Label 4*/ GIMT_Encode4(464),
// 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),
// 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),
// 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),
// CHECK-NEXT: /*TargetOpcode::G_FABS*//*Label 3*/ GIMT_Encode4(452),
// CHECK-NEXT: // Label 0: @410
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ GIMT_Encode4(427), // Rule ID 2 //
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule2Enabled),
// CHECK-NEXT: // MIs[0] x
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // MIs[0] y
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_GICombiner0),
// CHECK-NEXT: GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_GICombiner1),
// CHECK-NEXT: // Combiner Rule #2: TwoMatchNoApply
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
// CHECK-NEXT: // Label 5: @427
// CHECK-NEXT: GIM_Reject,
// CHECK-NEXT: // Label 1: @428
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(439), // Rule ID 3 //
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule3Enabled),
// CHECK-NEXT: // MIs[0] a
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // MIs[0] y
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // Combiner Rule #3: NoMatchTwoApply
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner2),
// CHECK-NEXT: // Label 6: @439
// CHECK-NEXT: GIM_Reject,
// CHECK-NEXT: // Label 2: @440
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(451), // Rule ID 1 //
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule1Enabled),
// CHECK-NEXT: // MIs[0] a
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // MIs[0] b
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // Combiner Rule #1: TwoMatchTwoApply
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner1),
// CHECK-NEXT: // Label 7: @451
// CHECK-NEXT: GIM_Reject,
// CHECK-NEXT: // Label 3: @452
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(463), // Rule ID 0 //
// CHECK-NEXT: GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule0Enabled),
// CHECK-NEXT: // MIs[0] a
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // MIs[0] b
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // Combiner Rule #0: OneMatchOneApply
// CHECK-NEXT: GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner0),
// CHECK-NEXT: // Label 8: @463
// CHECK-NEXT: GIM_Reject,
// CHECK-NEXT: // Label 4: @464
// CHECK-NEXT: GIM_Reject,
// CHECK-NEXT: }; // Size: 465 bytes
// CHECK-NEXT: return MatchTable0;
// CHECK-NEXT: }
Loading
Loading