diff --git a/llvm/docs/GlobalISel/MIRPatterns.rst b/llvm/docs/GlobalISel/MIRPatterns.rst index 42ceb1883f7de..4e4ff1e897915 100644 --- a/llvm/docs/GlobalISel/MIRPatterns.rst +++ b/llvm/docs/GlobalISel/MIRPatterns.rst @@ -74,7 +74,7 @@ Operands are ordered just like they would be in a MachineInstr: the defs (outs) come first, then the uses (ins). Patterns are generally grouped into another DAG datatype with a dummy operator -such as ``match``, ``apply`` or ``pattern``. +such as ``match``, ``apply``, ``combine`` or ``pattern``. Finally, any DAG datatype in TableGen can be named. This also holds for patterns. e.g. the following is valid: ``(G_FOO $root, (i32 0):$cst):$mypat``. @@ -370,6 +370,42 @@ The following expansions are available for MIR patterns: (match (G_ZEXT $root, $src):$mi), (apply "foobar(${root}.getReg(), ${src}.getReg(), ${mi}->hasImplicitDef())")>; +``combine`` Operator +~~~~~~~~~~~~~~~~~~~~ + +``GICombineRule`` also supports a single ``combine`` pattern, which is a shorter way to +declare patterns that just match one or more instructions, then defer all remaining matching +and rewriting logic to C++ code. + +.. code-block:: text + :caption: Example usage of the combine operator. + + // match + apply + def FooLong : GICombineRule< + (defs root:$root), + (match (G_ZEXT $root, $src):$mi, "return matchFoo(${mi});"), + (apply "applyFoo(${mi});")>; + + // combine + def FooShort : GICombineRule< + (defs root:$root), + (combine (G_ZEXT $root, $src):$mi, "return combineFoo(${mi});")>; + +This has a couple of advantages: + +* We only need one C++ function, not two. +* We no longer need to use ``GIDefMatchData`` to pass information between the match/apply functions. + +As described above, this is syntactic sugar for the match+apply form. In a ``combine`` pattern: + +* Everything except C++ code is considered the ``match`` part. +* The C++ code is the ``apply`` part. C++ code is emitted in order of appearance. + +.. note:: + + The C++ code **must** return true if it changed any instruction. Returning false when changing + instructions is undefined behavior. + Common Pattern #1: Replace a Register with Another ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td index e5e19a1d93486..efd88524a159e 100644 --- a/llvm/include/llvm/Target/GlobalISel/Combine.td +++ b/llvm/include/llvm/Target/GlobalISel/Combine.td @@ -31,6 +31,8 @@ def defs; def pattern; def match; def apply; +def combine; +def empty_action; def wip_match_opcode; @@ -67,18 +69,17 @@ class GIDefMatchData { string Type = type; } -class GICombineRule : GICombine { +class GICombineRule : GICombine { /// Defines the external interface of the match rule. This includes: /// * The names of the root nodes (requires at least one) /// See GIDefKind for details. dag Defs = defs; - /// Defines the things which must be true for the pattern to match - dag Match = match; - - /// Defines the things which happen after the decision is made to apply a - /// combine rule. - dag Apply = apply; + // The patterns that will be used. Two types of list can exist: + // match (Action0) + apply (Action1). + // combine (Action0) + empty_action (Action1). + dag Action0 = a0; + dag Action1 = a1; /// Defines the predicates that are checked before the match function /// is called. Targets can use this to, for instance, check Subtarget diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td index 9a1a15c8d30df..2cc0700b66fee 100644 --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-cxx.td @@ -28,11 +28,16 @@ def NoMatchTwoApply : GICombineRule< (match (G_SEXT $a, $y)), (apply "APPLY0", "APPLY1")>; +def CombineCXXOrder : GICombineRule< + (defs root:$a), + (combine (G_ZEXT $a, $y), "A0", "return A1")>; + def MyCombiner: GICombiner<"GenMyCombiner", [ OneMatchOneApply, TwoMatchTwoApply, TwoMatchNoApply, - NoMatchTwoApply + NoMatchTwoApply, + CombineCXXOrder ]>; // CHECK: bool GenMyCombiner::testMIPredicate_MI(unsigned PredicateID, const MachineInstr & MI, const MatcherState &State) const { @@ -79,65 +84,83 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ // CHECK-NEXT: APPLY1 // CHECK-NEXT: return true; // CHECK-NEXT: } +// CHECK-NEXT: case GICXXCustomAction_GICombiner3:{ +// CHECK-NEXT: // Apply Patterns +// CHECK-NEXT: A0 +// CHECK-NEXT: return A1 +// 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([[#LOWER:]]), GIMT_Encode2([[#UPPER:]]), /*)*//*default:*//*Label 4*/ GIMT_Encode4([[#DEFAULT:]]), -// CHECK-NEXT: /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4([[L418:[0-9]+]]), 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([[L436:[0-9]+]]), 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([[L448:[0-9]+]]), 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([[L460:[0-9]+]]), -// CHECK-NEXT: // Label 0: @[[#%u, mul(UPPER-LOWER, 4) + 10]] -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ GIMT_Encode4([[L435:[0-9]+]]), // 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: @[[L435]] -// CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: // Label 1: @[[L436]] -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4([[L447:[0-9]+]]), // 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: @[[L447]] -// CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: // Label 2: @[[L448]] -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4([[L459:[0-9]+]]), // 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: @[[L459]] -// CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: // Label 3: @[[L460]] -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4([[L471:[0-9]+]]), // 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: @[[L471]] -// CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: // Label 4: @[[#%u, DEFAULT]] -// CHECK-NEXT: GIM_Reject, -// CHECK-NEXT: }; // Size: [[#%u, DEFAULT + 1]] bytes +// CHECK-NEXT: /* 0 */ GIM_SwitchOpcode, /*MI*/0, /*[*/GIMT_Encode2(99), GIMT_Encode2(205), /*)*//*default:*//*Label 5*/ GIMT_Encode4(500), +// CHECK-NEXT: /* 10 */ /*TargetOpcode::G_STORE*//*Label 0*/ GIMT_Encode4(434), 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: /* 162 */ /*TargetOpcode::G_SEXT*//*Label 1*/ GIMT_Encode4(452), GIMT_Encode4(0), +// CHECK-NEXT: /* 170 */ /*TargetOpcode::G_ZEXT*//*Label 2*/ GIMT_Encode4(464), 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: /* 394 */ /*TargetOpcode::G_FNEG*//*Label 3*/ GIMT_Encode4(476), 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: /* 430 */ /*TargetOpcode::G_FABS*//*Label 4*/ GIMT_Encode4(488), +// CHECK-NEXT: /* 434 */ // Label 0: @434 +// CHECK-NEXT: /* 434 */ GIM_Try, /*On fail goto*//*Label 6*/ GIMT_Encode4(451), // Rule ID 2 // +// CHECK-NEXT: /* 439 */ GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule2Enabled), +// CHECK-NEXT: /* 442 */ // MIs[0] x +// CHECK-NEXT: /* 442 */ // No operand predicates +// CHECK-NEXT: /* 442 */ // MIs[0] y +// CHECK-NEXT: /* 442 */ // No operand predicates +// CHECK-NEXT: /* 442 */ GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_GICombiner0), +// CHECK-NEXT: /* 446 */ GIM_CheckCxxInsnPredicate, /*MI*/0, /*FnId*/GIMT_Encode2(GICXXPred_MI_Predicate_GICombiner1), +// CHECK-NEXT: /* 450 */ // Combiner Rule #2: TwoMatchNoApply +// CHECK-NEXT: /* 450 */ GIR_EraseRootFromParent_Done, +// CHECK-NEXT: /* 451 */ // Label 6: @451 +// CHECK-NEXT: /* 451 */ GIM_Reject, +// CHECK-NEXT: /* 452 */ // Label 1: @452 +// CHECK-NEXT: /* 452 */ GIM_Try, /*On fail goto*//*Label 7*/ GIMT_Encode4(463), // Rule ID 3 // +// CHECK-NEXT: /* 457 */ GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule3Enabled), +// CHECK-NEXT: /* 460 */ // MIs[0] a +// CHECK-NEXT: /* 460 */ // No operand predicates +// CHECK-NEXT: /* 460 */ // MIs[0] y +// CHECK-NEXT: /* 460 */ // No operand predicates +// CHECK-NEXT: /* 460 */ // Combiner Rule #3: NoMatchTwoApply +// CHECK-NEXT: /* 460 */ GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner2), +// CHECK-NEXT: /* 463 */ // Label 7: @463 +// CHECK-NEXT: /* 463 */ GIM_Reject, +// CHECK-NEXT: /* 464 */ // Label 2: @464 +// CHECK-NEXT: /* 464 */ GIM_Try, /*On fail goto*//*Label 8*/ GIMT_Encode4(475), // Rule ID 4 // +// CHECK-NEXT: /* 469 */ GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule4Enabled), +// CHECK-NEXT: /* 472 */ // MIs[0] a +// CHECK-NEXT: /* 472 */ // No operand predicates +// CHECK-NEXT: /* 472 */ // MIs[0] y +// CHECK-NEXT: /* 472 */ // No operand predicates +// CHECK-NEXT: /* 472 */ // Combiner Rule #4: CombineCXXOrder +// CHECK-NEXT: /* 472 */ GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner3), +// CHECK-NEXT: /* 475 */ // Label 8: @475 +// CHECK-NEXT: /* 475 */ GIM_Reject, +// CHECK-NEXT: /* 476 */ // Label 3: @476 +// CHECK-NEXT: /* 476 */ GIM_Try, /*On fail goto*//*Label 9*/ GIMT_Encode4(487), // Rule ID 1 // +// CHECK-NEXT: /* 481 */ GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule1Enabled), +// CHECK-NEXT: /* 484 */ // MIs[0] a +// CHECK-NEXT: /* 484 */ // No operand predicates +// CHECK-NEXT: /* 484 */ // MIs[0] b +// CHECK-NEXT: /* 484 */ // No operand predicates +// CHECK-NEXT: /* 484 */ // Combiner Rule #1: TwoMatchTwoApply +// CHECK-NEXT: /* 484 */ GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner1), +// CHECK-NEXT: /* 487 */ // Label 9: @487 +// CHECK-NEXT: /* 487 */ GIM_Reject, +// CHECK-NEXT: /* 488 */ // Label 4: @488 +// CHECK-NEXT: /* 488 */ GIM_Try, /*On fail goto*//*Label 10*/ GIMT_Encode4(499), // Rule ID 0 // +// CHECK-NEXT: /* 493 */ GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule0Enabled), +// CHECK-NEXT: /* 496 */ // MIs[0] a +// CHECK-NEXT: /* 496 */ // No operand predicates +// CHECK-NEXT: /* 496 */ // MIs[0] b +// CHECK-NEXT: /* 496 */ // No operand predicates +// CHECK-NEXT: /* 496 */ // Combiner Rule #0: OneMatchOneApply +// CHECK-NEXT: /* 496 */ GIR_DoneWithCustomAction, /*Fn*/GIMT_Encode2(GICXXCustomAction_GICombiner0), +// CHECK-NEXT: /* 499 */ // Label 10: @499 +// CHECK-NEXT: /* 499 */ GIM_Reject, +// CHECK-NEXT: /* 500 */ // Label 5: @500 +// CHECK-NEXT: /* 500 */ GIM_Reject, +// CHECK-NEXT: /* 501 */ }; // Size: 501 bytes // CHECK-NEXT: return MatchTable0; // CHECK-NEXT: } diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td index 5017672f5cbd7..4f1b4283458ba 100644 --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td @@ -283,6 +283,33 @@ def matchdata_without_cxx_apply : GICombineRule< (match (G_ZEXT $dst, $src):$mi), (apply (G_MUL $dst, $src, $src))>; +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: expected both a 'match' and 'apply' action in combine rule, or a single 'combine' action +def missing_apply : GICombineRule< + (defs root:$dst), + (match (G_ZEXT $dst, $src))>; + +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'combine' action needs at least one pattern to match, and C++ code to apply +def combineop_missing_cxx : GICombineRule< + (defs root:$d), + (combine (wip_match_opcode G_TRUNC):$d)>; + +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'combine' action needs at least one pattern to match, and C++ code to apply +def combineop_missing_mir : GICombineRule< + (defs root:$d), + (combine "return APPLY;")>; + +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: expected both a 'match' and 'apply' action in combine rule, or a single 'combine' action +def mixed_combine_match : GICombineRule< + (defs root:$d), + (combine (G_ZEXT $d, $y), "return APPLY;"), + (match (G_ZEXT $d, $y))>; + +// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: expected both a 'match' and 'apply' action in combine rule, or a single 'combine' action +def mixed_combine_apply : GICombineRule< + (defs root:$d), + (combine "return APPLY;"), + (apply (G_ZEXT $d, $y))>; + // CHECK: error: Failed to parse one or more rules def MyCombiner: GICombiner<"GenMyCombiner", [ @@ -326,5 +353,10 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ using_flagref_in_match, badflagref_in_apply, mixed_cxx_apply, - matchdata_without_cxx_apply + matchdata_without_cxx_apply, + missing_apply, + combineop_missing_cxx, + combineop_missing_mir, + mixed_combine_match, + mixed_combine_apply ]>; diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td index 85075359df737..7541639480fc5 100644 --- a/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td +++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td @@ -383,6 +383,37 @@ def IntrinTest1 : GICombineRule< (match (int_convergent_1in_1out $a, $b)), (apply (int_convergent_sideeffects_1in_1out $a, $b))>; +// CHECK: (CombineRule name:CombineOperator0 id:14 root:d +// CHECK-NEXT: (MatchPats +// CHECK-NEXT: d:(AnyOpcodePattern [G_TRUNC]) +// CHECK-NEXT: ) +// CHECK-NEXT: (ApplyPats +// CHECK-NEXT: __CombineOperator0_combine_1:(CXXPattern apply code:"return APPLY;") +// CHECK-NEXT: ) +// CHECK-NEXT: (OperandTable MatchPats ) +// CHECK-NEXT: (OperandTable ApplyPats ) +// CHECK-NEXT: ) +def CombineOperator0 : GICombineRule< + (defs root:$d), + (combine (wip_match_opcode G_TRUNC):$d, "return APPLY;")>; + +// CHECK: (CombineRule name:CombineOperator1 id:15 root:a +// CHECK-NEXT: (MatchPats +// CHECK-NEXT: __CombineOperator1_combine_0:(CodeGenInstructionPattern G_TRUNC operands:[$a, $b]) +// CHECK-NEXT: ) +// CHECK-NEXT: (ApplyPats +// CHECK-NEXT: __CombineOperator1_combine_1:(CXXPattern apply code:"return APPLY ${a} ${b};") +// CHECK-NEXT: ) +// CHECK-NEXT: (OperandTable MatchPats +// CHECK-NEXT: a -> __CombineOperator1_combine_0 +// CHECK-NEXT: b -> +// CHECK-NEXT: ) +// CHECK-NEXT: (OperandTable ApplyPats ) +// CHECK-NEXT: ) +def CombineOperator1 : GICombineRule< + (defs root:$a), + (combine (G_TRUNC $a, $b), "return APPLY ${a} ${b};")>; + def MyCombiner: GICombiner<"GenMyCombiner", [ WipOpcodeTest0, WipOpcodeTest1, @@ -397,5 +428,7 @@ def MyCombiner: GICombiner<"GenMyCombiner", [ TypeOfTest, MIFlagsTest, IntrinTest0, - IntrinTest1 + IntrinTest1, + CombineOperator0, + CombineOperator1 ]>; diff --git a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp index 3259f8caeb773..ad656aaefe54e 100644 --- a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp @@ -798,17 +798,45 @@ bool CombineRuleBuilder::parseAll() { if (!parseDefs(*RuleDef.getValueAsDag("Defs"))) return false; - if (!Parser.parsePatternList( - *RuleDef.getValueAsDag("Match"), - [this](auto Pat) { return addMatchPattern(std::move(Pat)); }, "match", - (RuleDef.getName() + "_match").str())) - return false; + const DagInit &Act0 = *RuleDef.getValueAsDag("Action0"); + const DagInit &Act1 = *RuleDef.getValueAsDag("Action1"); + + StringRef Act0Op = Act0.getOperatorAsDef(RuleDef.getLoc())->getName(); + StringRef Act1Op = Act1.getOperatorAsDef(RuleDef.getLoc())->getName(); + + if (Act0Op == "match" && Act1Op == "apply") { + if (!Parser.parsePatternList( + Act0, [this](auto Pat) { return addMatchPattern(std::move(Pat)); }, + "match", (RuleDef.getName() + "_match").str())) + return false; + + if (!Parser.parsePatternList( + Act1, [this](auto Pat) { return addApplyPattern(std::move(Pat)); }, + "apply", (RuleDef.getName() + "_apply").str())) + return false; + + } else if (Act0Op == "combine" && Act1Op == "empty_action") { + // combine: everything is a "match" except C++ code which is an apply. + const auto AddCombinePat = [this](std::unique_ptr Pat) { + if (isa(Pat.get())) + return addApplyPattern(std::move(Pat)); + return addMatchPattern(std::move(Pat)); + }; - if (!Parser.parsePatternList( - *RuleDef.getValueAsDag("Apply"), - [this](auto Pat) { return addApplyPattern(std::move(Pat)); }, "apply", - (RuleDef.getName() + "_apply").str())) + if (!Parser.parsePatternList(Act0, AddCombinePat, "combine", + (RuleDef.getName() + "_combine").str())) + return false; + + if (MatchPats.empty() || ApplyPats.empty()) { + PrintError("'combine' action needs at least one pattern to match, and " + "C++ code to apply"); + return false; + } + } else { + PrintError("expected both a 'match' and 'apply' action in combine rule, " + "or a single 'combine' action"); return false; + } if (!buildRuleOperandsTable() || !typecheckPatterns() || !findRoots() || !checkSemantics() || !buildPermutationsToEmit()) @@ -1340,6 +1368,8 @@ bool CombineRuleBuilder::checkSemantics() { } } + // TODO: Diagnose uses of MatchDatas if the Rule doesn't have C++ on both the + // match and apply. It's useless in such cases. if (!hasOnlyCXXApplyPatterns() && !MatchDatas.empty()) { PrintError(MatchDataClassName + " can only be used if 'apply' in entirely written in C++");