Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -150,6 +150,13 @@ /// - 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, Index: include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -334,7 +334,25 @@ } 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) Index: test/CodeGen/X86/GlobalISel/select-blsr.mir =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/select-blsr.mir @@ -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 +... Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -259,7 +259,7 @@ 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 @@ // 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_CheckTie, /*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 @@ // 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 @@ //===- 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 @@ // 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 @@ // 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 @@ // 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 @@ //===- 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 @@ // 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 @@ //===- 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 @@ // 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 @@ //===- 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 @@ // 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 @@ //===- 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 @@ // 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 @@ //===- 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 @@ // 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 @@ //===- 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 @@ // 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 @@ //===- 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 @@ // 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 @@ // 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 @@ // 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 @@ //===- 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 @@ // 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 @@ // 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 @@ // 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(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 @@ // 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(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 @@ // 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 @@ // 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 : FPImmLeafisExactlyValue(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 @@ // 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)]>; Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -478,14 +478,21 @@ /// emitCaptureOpcodes(). DefinedInsnVariablesMap InsnVariableIDs; + /// A map of named operands defined by the matchers that may be referenced by + /// the renderers. + StringMap DefinedOperands; + /// ID for the next instruction variable defined with defineInsnVar() unsigned NextInsnVarID; std::vector RequiredFeatures; + ArrayRef SrcLoc; + public: - RuleMatcher() - : Matchers(), Actions(), InsnVariableIDs(), NextInsnVarID(0) {} + RuleMatcher(ArrayRef SrcLoc) + : Matchers(), Actions(), InsnVariableIDs(), DefinedOperands(), + NextInsnVarID(0), SrcLoc(SrcLoc) {} RuleMatcher(RuleMatcher &&Other) = default; RuleMatcher &operator=(RuleMatcher &&Other) = default; @@ -513,7 +520,10 @@ 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); @@ -593,6 +603,7 @@ /// 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 @@ 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 - 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 @@ 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 @@ llvm::to_string(OpIdx) + ")"; } - Optional - 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,6 +934,13 @@ unsigned getAllocatedTemporariesBaseID() const { return AllocatedTemporariesBaseID; } + + bool isSameAsAnotherOperand() const { + for (const auto &Predicate : predicates()) + if (isa(Predicate)) + return true; + return false; + } }; unsigned ComplexPatternOperandMatcher::getAllocatedTemporariesBaseID() const { @@ -1088,6 +1099,8 @@ protected: typedef std::vector> OperandVec; + RuleMatcher &Rule; + /// The operands to match. All rendered operands must be present even if the /// condition is always true. OperandVec Operands; @@ -1095,13 +1108,19 @@ 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 +1134,6 @@ llvm_unreachable("Failed to lookup operand"); } - Optional - 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 { - OptionalOM = 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 +1234,9 @@ std::unique_ptr 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 +1244,6 @@ InstructionMatcher &getInsnMatcher() const { return *InsnMatcher; } - Optional - 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 +1311,7 @@ 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 +1411,7 @@ 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 +1551,13 @@ std::vector> 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(&*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 +1582,7 @@ 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 +1690,7 @@ }; InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) { - Matchers.emplace_back(new InstructionMatcher(SymbolicName)); + Matchers.emplace_back(new InstructionMatcher(*this, SymbolicName)); return *Matchers.back(); } @@ -1740,6 +1735,17 @@ 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(OM.getSymbolicName()); +} + const InstructionMatcher & RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const { for (const auto &I : InsnVariableIDs) @@ -1749,6 +1755,16 @@ ("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 +1924,23 @@ 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 +1980,8 @@ Expected 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,8 +2099,10 @@ if (Src->isLeaf()) { Init *SrcInit = Src->getLeafValue(); if (IntInit *SrcIntInit = dyn_cast(SrcInit)) { - OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx); - OM.addPredicate(SrcIntInit->getValue()); + OperandMatcher &OM = + InsnMatcher.addOperand(OpIdx++, Src->getName(), TempOpIdx); + if (!OM.isSameAsAnotherOperand()) + OM.addPredicate(SrcIntInit->getValue()); } else return failedImport( "Unable to deduce gMIR opcode to handle Src (which is a leaf)"); @@ -2094,7 +2130,8 @@ if (const CodeGenIntrinsic *II = Src->getIntrinsicInfo(CGP)) { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, SrcChild->getName(), TempOpIdx); - OM.addPredicate(II); + if (!OM.isSameAsAnotherOperand()) + OM.addPredicate(II); continue; } @@ -2116,6 +2153,8 @@ unsigned &TempOpIdx) const { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx); + if (OM.isSameAsAnotherOperand()) + return Error::success(); ArrayRef ChildTypes = SrcChild->getExtTypes(); if (ChildTypes.size() != 1) @@ -2143,7 +2182,8 @@ if (!SrcChild->isLeaf()) { // Map the node to a gMIR instruction. InstructionOperandMatcher &InsnOperand = - OM.addPredicate(SrcChild->getName()); + OM.addPredicate(InsnMatcher.getRuleMatcher(), + SrcChild->getName()); auto InsnMatcherOrError = createAndImportSelDAGMatcher( InsnOperand.getInsnMatcher(), SrcChild, TempOpIdx); if (auto Error = InsnMatcherOrError.takeError()) @@ -2203,7 +2243,7 @@ } 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 +2312,7 @@ return failedImport( "SelectionDAG ComplexPattern not mapped to GlobalISel"); - const OperandMatcher &OM = InsnMatcher.getOperand(DstChild->getName()); + const OperandMatcher &OM = Rule.getOperandMatcher(DstChild->getName()); DstMIBuilder.addRenderer( 0, *ComplexPattern->second, DstChild->getName(), OM.getAllocatedTemporariesBaseID()); @@ -2373,7 +2413,7 @@ } if (auto Error = importExplicitUseRenderer( - DstMIBuilder, Dst->getChild(Child), InsnMatcher)) + M, DstMIBuilder, Dst->getChild(Child), InsnMatcher)) return std::move(Error); ++Child; } @@ -2426,7 +2466,7 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { // Keep track of the matchers and actions to emit. - RuleMatcher M; + RuleMatcher M(P.getSrcRecord()->getLoc()); M.addAction(P); if (auto Error = importRulePredicates(M, P.getPredicates())) @@ -2465,6 +2505,7 @@ OperandMatcher &OM0 = InsnMatcher.getOperand(0); OM0.setSymbolicName(DstIOperand.Name); + M.defineOperand(OM0.getSymbolicName(), OM0); OM0.addPredicate(RC); auto &DstMIBuilder = M.addAction(0, &DstI, InsnMatcher); @@ -2525,6 +2566,7 @@ OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); OM.setSymbolicName(DstIOperand.Name); + M.defineOperand(OM.getSymbolicName(), OM); OM.addPredicate( Target.getRegisterClass(DstIOpRec)); ++OpIdx;