[globalisel][tablegen] Simplify named operand/operator lookups and fix a wrong-code bug this revealed.

Summary:
Operand variable lookups are now performed by the RuleMatcher rather than
searching the whole matcher hierarchy for a match. This revealed a wrong-code
bug that currently affects ARM and X86 where patterns that use a variable more
than once in the match pattern will be imported but won't check that the
operands are identical. This can cause the tablegen-erated matcher to
accept matches that should be rejected.

Depends on D36569

Reviewers: ab, t.p.northover, qcolombet, rovka, aditya_nandakumar

Subscribers: aemerson, igorb, llvm-commits, kristof.beyls

Differential Revision: https://reviews.llvm.org/D36618

llvm-svn: 315780
This commit is contained in:
Daniel Sanders 2017-10-14 00:31:58 +00:00
parent aafbc1c4db
commit bfa9e2cae7
5 changed files with 274 additions and 108 deletions

View File

@ -150,6 +150,13 @@ enum {
/// - InsnID - Instruction ID
GIM_CheckIsSafeToFold,
/// Check the specified operands are identical.
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - OtherInsnID - Other instruction ID
/// - OtherOpIdx - Other operand index
GIM_CheckIsSameOperand,
/// Fail the current try-block, or completely fail to match if there is no
/// current try-block.
GIM_Reject,

View File

@ -334,7 +334,25 @@ bool InstructionSelector::executeMatchTable(
}
break;
}
case GIM_CheckIsSameOperand: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OpIdx = MatchTable[CurrentIdx++];
int64_t OtherInsnID = MatchTable[CurrentIdx++];
int64_t OtherOpIdx = MatchTable[CurrentIdx++];
DEBUG(dbgs() << CurrentIdx << ": GIM_CheckIsSameOperand(MIs[" << InsnID
<< "][" << OpIdx << "], MIs[" << OtherInsnID << "]["
<< OtherOpIdx << "])\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
assert(State.MIs[OtherInsnID] != nullptr && "Used insn before defined");
State.MIs[InsnID]->getOperand(OpIdx).dump();
State.MIs[OtherInsnID]->getOperand(OtherOpIdx).dump();
if (!State.MIs[InsnID]->getOperand(OpIdx).isIdenticalTo(
State.MIs[OtherInsnID]->getOperand(OtherInsnID))) {
if (handleReject() == RejectAndGiveUp)
return false;
}
break;
}
case GIM_Reject:
DEBUG(dbgs() << CurrentIdx << ": GIM_Reject");
if (handleReject() == RejectAndGiveUp)

View File

@ -0,0 +1,58 @@
# RUN: llc -mtriple=x86_64-linux-gnu -mattr=+bmi -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
#
# Test that rules where multiple operands must be the same operand successfully
# match. Also test that the rules do not match when they're not the same
# operand.
---
name: test_blsr32rr
# CHECK-LABEL: name: test_blsr32rr
alignment: 4
legalized: true
regBankSelected: true
# CHECK: registers:
# CHECK-NEXT: - { id: 0, class: gr32, preferred-register: '' }
# CHECK-NEXT: - { id: 1, class: gpr, preferred-register: '' }
# CHECK-NEXT: - { id: 2, class: gpr, preferred-register: '' }
# CHECK-NEXT: - { id: 3, class: gr32, preferred-register: '' }
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
# G_ADD and G_AND both use %0 so we should match this.
# CHECK: %3 = BLSR32rr %0
body: |
bb.1:
liveins: %edi
%0(s32) = COPY %edi
%1(s32) = G_CONSTANT i32 -1
%2(s32) = G_ADD %0, %1
%3(s32) = G_AND %2, %0
%edi = COPY %3
...
---
name: test_blsr32rr_nomatch
# CHECK-LABEL: name: test_blsr32rr_nomatch
alignment: 4
legalized: true
regBankSelected: true
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
# G_ADD and G_AND use different operands so we shouldn't match this.
# CHECK-NOT: BLSR32rr
body: |
bb.1:
liveins: %edi
%0(s32) = COPY %edi
%1(s32) = G_CONSTANT i32 -1
%2(s32) = G_ADD %1, %1
%3(s32) = G_AND %2, %0
%edi = COPY %3
...

View File

@ -259,7 +259,7 @@ def : Pat<(select GPR32:$src1, complex:$src2, complex:$src3),
def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
[(set GPR32:$dst, (add GPR32:$src1, GPR32:$src2))]>;
//===- Test a simple pattern with ValueType operands. ----------------------===//
//===- Test a pattern with a tied operand in the matcher ------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 3*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
@ -267,6 +267,30 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
// CHECK-NEXT: // MIs[0] dst
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// CHECK-NEXT: // MIs[0] src{{$}}
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
// CHECK-NEXT: // MIs[0] src{{$}}
// CHECK-NEXT: GIM_CheckIsSameOperand, /*MI*/0, /*OpIdx*/2, /*OtherMI*/0, /*OtherOpIdx*/1,
// CHECK-NEXT: // (add:{ *:[i32] } GPR32:{ *:[i32] }:$src, GPR32:{ *:[i32] }:$src) => (DOUBLE:{ *:[i32] } GPR32:{ *:[i32] }:$src)
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::DOUBLE,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 3: @[[LABEL]]
def DOUBLE : I<(outs GPR32:$dst), (ins GPR32:$src), [(set GPR32:$dst, (add GPR32:$src, GPR32:$src))]>;
//===- Test a simple pattern with ValueType operands. ----------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 4*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ADD,
// CHECK-NEXT: // MIs[0] dst
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// CHECK-NEXT: // MIs[0] src1
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
// CHECK-NEXT: // MIs[0] src2
@ -275,7 +299,7 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::ADD,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 3: @[[LABEL]]
// CHECK-NEXT: // Label 4: @[[LABEL]]
def : Pat<(add i32:$src1, i32:$src2),
(ADD i32:$src1, i32:$src2)>;
@ -283,7 +307,7 @@ def : Pat<(add i32:$src1, i32:$src2),
//===- Test a simple pattern with an intrinsic. ---------------------------===//
//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 4*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_INTRINSIC,
// CHECK-NEXT: // MIs[0] dst
@ -302,14 +326,14 @@ def : Pat<(add i32:$src1, i32:$src2),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 4: @[[LABEL]]
// CHECK-NEXT: // Label 5: @[[LABEL]]
def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
[(set GPR32:$dst, (int_mytarget_nop GPR32:$src1))]>;
//===- Test a nested instruction match. -----------------------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA,
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
@ -342,10 +366,10 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 5: @[[LABEL]]
// CHECK-NEXT: // Label 6: @[[LABEL]]
// We also get a second rule by commutativity.
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA,
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/2,
@ -378,7 +402,7 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 6: @[[LABEL]]
// CHECK-NEXT: // Label 7: @[[LABEL]]
def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
[(set GPR32:$dst,
@ -387,7 +411,7 @@ def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
//===- Test another simple pattern with regclass operands. ----------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA_HasB_HasC,
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_MUL,
@ -408,7 +432,7 @@ def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 7: @[[LABEL]]
// CHECK-NEXT: // Label 8: @[[LABEL]]
def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
[(set GPR32:$dst, (mul GPR32:$src1, GPR32:$src2))]>,
@ -416,7 +440,7 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
//===- Test a more complex multi-instruction match. -----------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 9*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA,
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
@ -461,7 +485,7 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 8: @[[LABEL]]
// CHECK-NEXT: // Label 9: @[[LABEL]]
def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, GPR32:$src4),
[(set GPR32:$dst,
@ -471,7 +495,7 @@ def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, G
//===- Test a pattern with ComplexPattern operands. -----------------------===//
//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 9*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 10*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SUB,
// CHECK-NEXT: // MIs[0] dst
@ -491,7 +515,7 @@ def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, G
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 9: @[[LABEL]]
// CHECK-NEXT: // Label 10: @[[LABEL]]
def INSN1 : I<(outs GPR32:$dst), (ins GPR32:$src1, complex:$src2), []>;
def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
@ -499,7 +523,7 @@ def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
//===- Test a simple pattern with a default operand. ----------------------===//
//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 10*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 11*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
// CHECK-NEXT: // MIs[0] dst
@ -519,7 +543,7 @@ def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 10: @[[LABEL]]
// CHECK-NEXT: // Label 11: @[[LABEL]]
// The -2 is just to distinguish it from the 'not' case below.
def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
@ -528,7 +552,7 @@ def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
//===- Test a simple pattern with a default register operand. -------------===//
//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 11*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 12*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
// CHECK-NEXT: // MIs[0] dst
@ -548,7 +572,7 @@ def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 11: @[[LABEL]]
// CHECK-NEXT: // Label 12: @[[LABEL]]
// The -3 is just to distinguish it from the 'not' case below and the other default op case above.
def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
@ -557,7 +581,7 @@ def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
//===- Test a simple pattern with a multiple default operands. ------------===//
//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 12*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 13*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
// CHECK-NEXT: // MIs[0] dst
@ -578,7 +602,7 @@ def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 12: @[[LABEL]]
// CHECK-NEXT: // Label 13: @[[LABEL]]
// The -4 is just to distinguish it from the other 'not' cases.
def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
@ -587,7 +611,7 @@ def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
//===- Test a simple pattern with multiple operands with defaults. --------===//
//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 13*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 14*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
// CHECK-NEXT: // MIs[0] dst
@ -609,7 +633,7 @@ def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 13: @[[LABEL]]
// CHECK-NEXT: // Label 14: @[[LABEL]]
// The -5 is just to distinguish it from the other cases.
def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1),
@ -620,7 +644,7 @@ def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
// This must precede the 3-register variants because constant immediates have
// priority over register banks.
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 14*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 15*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
// CHECK-NEXT: // MIs[0] dst
@ -640,7 +664,7 @@ def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 14: @[[LABEL]]
// CHECK-NEXT: // Label 15: @[[LABEL]]
def ORN : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>;
def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
@ -648,7 +672,7 @@ def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
//===- Test a COPY_TO_REGCLASS --------------------------------------------===//
//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 15*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 16*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BITCAST,
// CHECK-NEXT: // MIs[0] dst
@ -661,14 +685,14 @@ def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/TargetOpcode::COPY,
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC GPR32*/1,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 15: @[[LABEL]]
// CHECK-NEXT: // Label 16: @[[LABEL]]
def : Pat<(i32 (bitconvert FPR32:$src1)),
(COPY_TO_REGCLASS FPR32:$src1, GPR32)>;
//===- Test a simple pattern with just a specific leaf immediate. ---------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 16*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 17*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
// CHECK-NEXT: // MIs[0] dst
@ -682,13 +706,13 @@ def : Pat<(i32 (bitconvert FPR32:$src1)),
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 16: @[[LABEL]]
// CHECK-NEXT: // Label 17: @[[LABEL]]
def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
//===- Test a simple pattern with a leaf immediate and a predicate. -------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 17*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 18*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
// CHECK-NEXT: GIM_CheckI64ImmPredicate, /*MI*/0, /*Predicate*/GIPFP_I64_Predicate_simm8,
@ -704,14 +728,14 @@ def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 17: @[[LABEL]]
// CHECK-NEXT: // Label 18: @[[LABEL]]
def simm8 : ImmLeaf<i32, [{ return isInt<8>(Imm); }]>;
def MOVimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm8:$imm)]>;
//===- Same again but use an IntImmLeaf. ----------------------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 18*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 19*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
// CHECK-NEXT: GIM_CheckAPIntImmPredicate, /*MI*/0, /*Predicate*/GIPFP_APInt_Predicate_simm9,
@ -727,14 +751,14 @@ def MOVimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm8:$i
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 18: @[[LABEL]]
// CHECK-NEXT: // Label 19: @[[LABEL]]
def simm9 : IntImmLeaf<i32, [{ return isInt<9>(Imm->getSExtValue()); }]>;
def MOVimm9 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm9:$imm)]>;
//===- Test a simple pattern with just a leaf immediate. ------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 19*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 20*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
// CHECK-NEXT: // MIs[0] dst
@ -749,13 +773,13 @@ def MOVimm9 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm9:$i
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 19: @[[LABEL]]
// CHECK-NEXT: // Label 20: @[[LABEL]]
def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>;
//===- Test a simple pattern with a FP immediate and a predicate. ---------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 20*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 21*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_FCONSTANT,
// CHECK-NEXT: GIM_CheckAPFloatImmPredicate, /*MI*/0, /*Predicate*/GIPFP_APFloat_Predicate_fpimmz,
@ -771,14 +795,14 @@ def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 20: @[[LABEL]]
// CHECK-NEXT: // Label 21: @[[LABEL]]
def fpimmz : FPImmLeaf<f32, [{ return Imm->isExactlyValue(0.0); }]>;
def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz:$imm)]>;
//===- Test a pattern with an MBB operand. --------------------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 21*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 22*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/1,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR,
// CHECK-NEXT: // MIs[0] target
@ -787,7 +811,7 @@ def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 21: @[[LABEL]]
// CHECK-NEXT: // Label 22: @[[LABEL]]
def BR : I<(outs), (ins unknown:$target),
[(br bb:$target)]>;

View File

@ -478,14 +478,21 @@ class RuleMatcher {
/// emitCaptureOpcodes().
DefinedInsnVariablesMap InsnVariableIDs;
/// A map of named operands defined by the matchers that may be referenced by
/// the renderers.
StringMap<OperandMatcher *> DefinedOperands;
/// ID for the next instruction variable defined with defineInsnVar()
unsigned NextInsnVarID;
std::vector<Record *> RequiredFeatures;
ArrayRef<SMLoc> SrcLoc;
public:
RuleMatcher()
: Matchers(), Actions(), InsnVariableIDs(), NextInsnVarID(0) {}
RuleMatcher(ArrayRef<SMLoc> SrcLoc)
: Matchers(), Actions(), InsnVariableIDs(), DefinedOperands(),
NextInsnVarID(0), SrcLoc(SrcLoc) {}
RuleMatcher(RuleMatcher &&Other) = default;
RuleMatcher &operator=(RuleMatcher &&Other) = default;
@ -513,7 +520,10 @@ public:
return make_range(defined_insn_vars_begin(), defined_insn_vars_end());
}
void defineOperand(StringRef SymbolicName, OperandMatcher &OM);
const InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const;
const OperandMatcher &getOperandMatcher(StringRef Name) const;
void emitCaptureOpcodes(MatchTable &Table);
@ -544,10 +554,10 @@ private:
public:
/// Construct a new operand predicate and add it to the matcher.
template <class Kind, class... Args>
Kind &addPredicate(Args&&... args) {
Optional<Kind *> addPredicate(Args&&... args) {
Predicates.emplace_back(
llvm::make_unique<Kind>(std::forward<Args>(args)...));
return *static_cast<Kind *>(Predicates.back().get());
return static_cast<Kind *>(Predicates.back().get());
}
typename PredicateVec::const_iterator predicates_begin() const {
@ -593,6 +603,7 @@ public:
/// but OPM_Int must have priority over OPM_RegBank since constant integers
/// are represented by a virtual register defined by a G_CONSTANT instruction.
enum PredicateKind {
OPM_Tie,
OPM_ComplexPattern,
OPM_IntrinsicID,
OPM_Instruction,
@ -612,17 +623,6 @@ public:
PredicateKind getKind() const { return Kind; }
/// Return the OperandMatcher for the specified operand or nullptr if there
/// isn't one by that name in this operand predicate matcher.
///
/// InstructionOperandMatcher is the only subclass that can return non-null
/// for this.
virtual Optional<const OperandMatcher *>
getOptionalOperand(StringRef SymbolicName) const {
assert(!SymbolicName.empty() && "Cannot lookup unnamed operand");
return None;
}
/// Emit MatchTable opcodes to capture instructions into the MIs table.
///
/// Only InstructionOperandMatcher needs to do anything for this method the
@ -651,6 +651,23 @@ PredicateListMatcher<OperandPredicateMatcher>::getNoPredicateComment() const {
return "No operand predicates";
}
/// Generates code to check that a register operand is defined by the same exact
/// one as another.
class SameOperandMatcher : public OperandPredicateMatcher {
std::string TiedTo;
public:
SameOperandMatcher(StringRef TiedTo)
: OperandPredicateMatcher(OPM_Tie), TiedTo(TiedTo) {}
static bool classof(const OperandPredicateMatcher *P) {
return P->getKind() == OPM_Tie;
}
void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule,
unsigned InsnVarID, unsigned OpIdx) const override;
};
/// Generates code to check that an operand is a particular LLT.
class LLTOperandMatcher : public OperandPredicateMatcher {
protected:
@ -857,19 +874,6 @@ public:
llvm::to_string(OpIdx) + ")";
}
Optional<const OperandMatcher *>
getOptionalOperand(StringRef DesiredSymbolicName) const {
assert(!DesiredSymbolicName.empty() && "Cannot lookup unnamed operand");
if (DesiredSymbolicName == SymbolicName)
return this;
for (const auto &OP : predicates()) {
const auto &MaybeOperand = OP->getOptionalOperand(DesiredSymbolicName);
if (MaybeOperand.hasValue())
return MaybeOperand.getValue();
}
return None;
}
InstructionMatcher &getInstructionMatcher() const { return Insn; }
/// Emit MatchTable opcodes to capture instructions into the MIs table.
@ -930,8 +934,27 @@ public:
unsigned getAllocatedTemporariesBaseID() const {
return AllocatedTemporariesBaseID;
}
bool isSameAsAnotherOperand() const {
for (const auto &Predicate : predicates())
if (isa<SameOperandMatcher>(Predicate))
return true;
return false;
}
};
// Specialize OperandMatcher::addPredicate() to refrain from adding redundant
// predicates.
template <>
template <class Kind, class... Args>
Optional<Kind *>
PredicateListMatcher<OperandPredicateMatcher>::addPredicate(Args &&... args) {
if (static_cast<OperandMatcher *>(this)->isSameAsAnotherOperand())
return None;
Predicates.emplace_back(llvm::make_unique<Kind>(std::forward<Args>(args)...));
return static_cast<Kind *>(Predicates.back().get());
}
unsigned ComplexPatternOperandMatcher::getAllocatedTemporariesBaseID() const {
return Operand.getAllocatedTemporariesBaseID();
}
@ -1088,6 +1111,8 @@ class InstructionMatcher
protected:
typedef std::vector<std::unique_ptr<OperandMatcher>> OperandVec;
RuleMatcher &Rule;
/// The operands to match. All rendered operands must be present even if the
/// condition is always true.
OperandVec Operands;
@ -1095,13 +1120,19 @@ protected:
std::string SymbolicName;
public:
InstructionMatcher(StringRef SymbolicName) : SymbolicName(SymbolicName) {}
InstructionMatcher(RuleMatcher &Rule, StringRef SymbolicName)
: Rule(Rule), SymbolicName(SymbolicName) {}
RuleMatcher &getRuleMatcher() const { return Rule; }
/// Add an operand to the matcher.
OperandMatcher &addOperand(unsigned OpIdx, const std::string &SymbolicName,
unsigned AllocatedTemporariesBaseID) {
Operands.emplace_back(new OperandMatcher(*this, OpIdx, SymbolicName,
AllocatedTemporariesBaseID));
if (!SymbolicName.empty())
Rule.defineOperand(SymbolicName, *Operands.back());
return *Operands.back();
}
@ -1115,24 +1146,6 @@ public:
llvm_unreachable("Failed to lookup operand");
}
Optional<const OperandMatcher *>
getOptionalOperand(StringRef SymbolicName) const {
assert(!SymbolicName.empty() && "Cannot lookup unnamed operand");
for (const auto &Operand : Operands) {
const auto &OM = Operand->getOptionalOperand(SymbolicName);
if (OM.hasValue())
return OM.getValue();
}
return None;
}
const OperandMatcher &getOperand(StringRef SymbolicName) const {
Optional<const OperandMatcher *>OM = getOptionalOperand(SymbolicName);
if (OM.hasValue())
return *OM.getValue();
llvm_unreachable("Failed to lookup operand");
}
StringRef getSymbolicName() const { return SymbolicName; }
unsigned getNumOperands() const { return Operands.size(); }
OperandVec::iterator operands_begin() { return Operands.begin(); }
@ -1233,9 +1246,9 @@ protected:
std::unique_ptr<InstructionMatcher> InsnMatcher;
public:
InstructionOperandMatcher(StringRef SymbolicName)
InstructionOperandMatcher(RuleMatcher &Rule, StringRef SymbolicName)
: OperandPredicateMatcher(OPM_Instruction),
InsnMatcher(new InstructionMatcher(SymbolicName)) {}
InsnMatcher(new InstructionMatcher(Rule, SymbolicName)) {}
static bool classof(const OperandPredicateMatcher *P) {
return P->getKind() == OPM_Instruction;
@ -1243,12 +1256,6 @@ public:
InstructionMatcher &getInsnMatcher() const { return *InsnMatcher; }
Optional<const OperandMatcher *>
getOptionalOperand(StringRef SymbolicName) const override {
assert(!SymbolicName.empty() && "Cannot lookup unnamed operand");
return InsnMatcher->getOptionalOperand(SymbolicName);
}
void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule,
unsigned InsnID, unsigned OpIdx) const override {
unsigned InsnVarID = Rule.defineInsnVar(Table, *InsnMatcher, InsnID, OpIdx);
@ -1316,7 +1323,7 @@ public:
const StringRef getSymbolicName() const { return SymbolicName; }
void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
const OperandMatcher &Operand = Matched.getOperand(SymbolicName);
const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName);
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
Table << MatchTable::Opcode("GIR_Copy") << MatchTable::Comment("NewInsnID")
<< MatchTable::IntValue(NewInsnID) << MatchTable::Comment("OldInsnID")
@ -1416,7 +1423,7 @@ public:
const StringRef getSymbolicName() const { return SymbolicName; }
void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
const OperandMatcher &Operand = Matched.getOperand(SymbolicName);
const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName);
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
Table << MatchTable::Opcode("GIR_CopySubReg")
<< MatchTable::Comment("NewInsnID") << MatchTable::IntValue(NewInsnID)
@ -1556,13 +1563,13 @@ private:
std::vector<std::unique_ptr<OperandRenderer>> OperandRenderers;
/// True if the instruction can be built solely by mutating the opcode.
bool canMutate() const {
bool canMutate(RuleMatcher &Rule) const {
if (OperandRenderers.size() != Matched.getNumOperands())
return false;
for (const auto &Renderer : enumerate(OperandRenderers)) {
if (const auto *Copy = dyn_cast<CopyRenderer>(&*Renderer.value())) {
const OperandMatcher &OM = Matched.getOperand(Copy->getSymbolicName());
const OperandMatcher &OM = Rule.getOperandMatcher(Copy->getSymbolicName());
if (&Matched != &OM.getInstructionMatcher() ||
OM.getOperandIndex() != Renderer.index())
return false;
@ -1587,7 +1594,7 @@ public:
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule,
unsigned RecycleInsnID) const override {
if (canMutate()) {
if (canMutate(Rule)) {
Table << MatchTable::Opcode("GIR_MutateOpcode")
<< MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
<< MatchTable::Comment("RecycleInsnID")
@ -1695,7 +1702,7 @@ public:
};
InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) {
Matchers.emplace_back(new InstructionMatcher(SymbolicName));
Matchers.emplace_back(new InstructionMatcher(*this, SymbolicName));
return *Matchers.back();
}
@ -1740,6 +1747,17 @@ unsigned RuleMatcher::getInsnVarID(const InstructionMatcher &InsnMatcher) const
llvm_unreachable("Matched Insn was not captured in a local variable");
}
void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) {
if (DefinedOperands.find(SymbolicName) == DefinedOperands.end()) {
DefinedOperands[SymbolicName] = &OM;
return;
}
// If the operand is already defined, then we must ensure both references in
// the matcher have the exact same node.
OM.addPredicate<SameOperandMatcher>(OM.getSymbolicName());
}
const InstructionMatcher &
RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const {
for (const auto &I : InsnVariableIDs)
@ -1749,6 +1767,16 @@ RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const {
("Failed to lookup instruction " + SymbolicName).str().c_str());
}
const OperandMatcher &
RuleMatcher::getOperandMatcher(StringRef Name) const {
const auto &I = DefinedOperands.find(Name);
if (I == DefinedOperands.end())
PrintFatalError(SrcLoc, "Operand " + Name + " was not declared in matcher");
return *I->second;
}
/// Emit MatchTable opcodes to check the shape of the match and capture
/// instructions into local variables.
void RuleMatcher::emitCaptureOpcodes(MatchTable &Table) {
@ -1908,6 +1936,23 @@ bool OperandPredicateMatcher::isHigherPriorityThan(
return Kind < B.Kind;
}
void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
RuleMatcher &Rule,
unsigned InsnVarID,
unsigned OpIdx) const {
const OperandMatcher &OtherOM = Rule.getOperandMatcher(TiedTo);
unsigned OtherInsnVarID = Rule.getInsnVarID(OtherOM.getInstructionMatcher());
Table << MatchTable::Opcode("GIM_CheckIsSameOperand")
<< MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID)
<< MatchTable::Comment("OpIdx") << MatchTable::IntValue(OpIdx)
<< MatchTable::Comment("OtherMI")
<< MatchTable::IntValue(OtherInsnVarID)
<< MatchTable::Comment("OtherOpIdx")
<< MatchTable::IntValue(OtherOM.getOperandIndex())
<< MatchTable::LineBreak;
}
//===- GlobalISelEmitter class --------------------------------------------===//
class GlobalISelEmitter {
@ -1947,7 +1992,8 @@ private:
Expected<BuildMIAction &>
createAndImportInstructionRenderer(RuleMatcher &M, const TreePatternNode *Dst,
const InstructionMatcher &InsnMatcher);
Error importExplicitUseRenderer(BuildMIAction &DstMIBuilder,
Error importExplicitUseRenderer(RuleMatcher &Rule,
BuildMIAction &DstMIBuilder,
TreePatternNode *DstChild,
const InstructionMatcher &InsnMatcher) const;
Error importDefaultOperandRenderers(BuildMIAction &DstMIBuilder,
@ -2065,7 +2111,8 @@ GlobalISelEmitter::createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher,
if (Src->isLeaf()) {
Init *SrcInit = Src->getLeafValue();
if (IntInit *SrcIntInit = dyn_cast<IntInit>(SrcInit)) {
OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx);
OperandMatcher &OM =
InsnMatcher.addOperand(OpIdx++, Src->getName(), TempOpIdx);
OM.addPredicate<LiteralIntOperandMatcher>(SrcIntInit->getValue());
} else
return failedImport(
@ -2116,6 +2163,8 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
unsigned &TempOpIdx) const {
OperandMatcher &OM =
InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx);
if (OM.isSameAsAnotherOperand())
return Error::success();
ArrayRef<TypeSetByHwMode> ChildTypes = SrcChild->getExtTypes();
if (ChildTypes.size() != 1)
@ -2141,9 +2190,17 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
// Check for nested instructions.
if (!SrcChild->isLeaf()) {
auto MaybeInsnOperand = OM.addPredicate<InstructionOperandMatcher>(
InsnMatcher.getRuleMatcher(), SrcChild->getName());
if (!MaybeInsnOperand.hasValue()) {
// This isn't strictly true. If the user were to provide exactly the same
// matchers as the original operand then we could allow it. However, it's
// simpler to not permit the redundant specification.
return failedImport("Nested instruction cannot be the same as another operand");
}
// Map the node to a gMIR instruction.
InstructionOperandMatcher &InsnOperand =
OM.addPredicate<InstructionOperandMatcher>(SrcChild->getName());
InstructionOperandMatcher &InsnOperand = **MaybeInsnOperand;
auto InsnMatcherOrError = createAndImportSelDAGMatcher(
InsnOperand.getInsnMatcher(), SrcChild, TempOpIdx);
if (auto Error = InsnMatcherOrError.takeError())
@ -2203,7 +2260,7 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
}
Error GlobalISelEmitter::importExplicitUseRenderer(
BuildMIAction &DstMIBuilder, TreePatternNode *DstChild,
RuleMatcher &Rule, BuildMIAction &DstMIBuilder, TreePatternNode *DstChild,
const InstructionMatcher &InsnMatcher) const {
if (DstChild->getTransformFn() != nullptr) {
return failedImport("Dst pattern child has transform fn " +
@ -2272,7 +2329,7 @@ Error GlobalISelEmitter::importExplicitUseRenderer(
return failedImport(
"SelectionDAG ComplexPattern not mapped to GlobalISel");
const OperandMatcher &OM = InsnMatcher.getOperand(DstChild->getName());
const OperandMatcher &OM = Rule.getOperandMatcher(DstChild->getName());
DstMIBuilder.addRenderer<RenderComplexPatternOperand>(
0, *ComplexPattern->second, DstChild->getName(),
OM.getAllocatedTemporariesBaseID());
@ -2373,7 +2430,7 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
}
if (auto Error = importExplicitUseRenderer(
DstMIBuilder, Dst->getChild(Child), InsnMatcher))
M, DstMIBuilder, Dst->getChild(Child), InsnMatcher))
return std::move(Error);
++Child;
}
@ -2426,7 +2483,7 @@ Error GlobalISelEmitter::importImplicitDefRenderers(
Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
// Keep track of the matchers and actions to emit.
RuleMatcher M;
RuleMatcher M(P.getSrcRecord()->getLoc());
M.addAction<DebugCommentAction>(P);
if (auto Error = importRulePredicates(M, P.getPredicates()))
@ -2465,6 +2522,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
OperandMatcher &OM0 = InsnMatcher.getOperand(0);
OM0.setSymbolicName(DstIOperand.Name);
M.defineOperand(OM0.getSymbolicName(), OM0);
OM0.addPredicate<RegisterBankOperandMatcher>(RC);
auto &DstMIBuilder = M.addAction<BuildMIAction>(0, &DstI, InsnMatcher);
@ -2525,6 +2583,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
OperandMatcher &OM = InsnMatcher.getOperand(OpIdx);
OM.setSymbolicName(DstIOperand.Name);
M.defineOperand(OM.getSymbolicName(), OM);
OM.addPredicate<RegisterBankOperandMatcher>(
Target.getRegisterClass(DstIOpRec));
++OpIdx;