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_CheckTie, + /// 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 @@ -298,6 +298,25 @@ } break; } + case GIM_CheckTie: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t OpIdx = MatchTable[CurrentIdx++]; + int64_t OtherInsnID = MatchTable[CurrentIdx++]; + int64_t OtherOpIdx = MatchTable[CurrentIdx++]; + DEBUG(dbgs() << CurrentIdx << ": GIM_CheckTie(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,36 @@ +# RUN: llc -mtriple=x86_64-linux-gnu -mattr=+bmi -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s + +--- | + define i32 @test_blsr32rr() { + ret i32 0 + } + +... +--- +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 } +# 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 + +... Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -210,10 +210,34 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), [(set GPR32:$dst, (add GPR32:$src1, GPR32:$src2))]>; +//===- 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, +// 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] 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 an intrinsic. ---------------------------===// // -// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 3*/ [[LABEL:[0-9]+]], +// 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_INTRINSIC, // CHECK-NEXT: // MIs[0] dst @@ -232,14 +256,14 @@ // CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, -// CHECK-NEXT: // Label 3: @[[LABEL]] +// CHECK-NEXT: // Label 4: @[[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 4*/ [[LABEL:[0-9]+]], +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ [[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] @@ -272,10 +296,10 @@ // 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]] // We also get a second rule by commutativity. -// 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*/2, @@ -308,7 +332,7 @@ // 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]] def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3), [(set GPR32:$dst, @@ -317,7 +341,7 @@ //===- Test another simple pattern with regclass operands. ----------------===// -// 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_HasB_HasC, // CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3, // CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_MUL, @@ -338,7 +362,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 MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1), [(set GPR32:$dst, (mul GPR32:$src1, GPR32:$src2))]>, @@ -346,7 +370,7 @@ //===- Test a more complex multi-instruction match. -----------------------===// -// 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, // CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3, // CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1] @@ -391,7 +415,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 INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, GPR32:$src4), [(set GPR32:$dst, @@ -401,7 +425,7 @@ //===- Test a pattern with ComplexPattern operands. -----------------------===// // -// 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_CheckNumOperands, /*MI*/0, /*Expected*/3, // CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SUB, // CHECK-NEXT: // MIs[0] dst @@ -421,7 +445,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 INSN1 : I<(outs GPR32:$dst), (ins GPR32:$src1, complex:$src2), []>; def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>; @@ -429,7 +453,7 @@ //===- Test a simple pattern with a default operand. ----------------------===// // -// 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_XOR, // CHECK-NEXT: // MIs[0] dst @@ -449,7 +473,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]] // The -2 is just to distinguish it from the 'not' case below. def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1), @@ -458,7 +482,7 @@ //===- Test a simple pattern with a default register 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 @@ -478,7 +502,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 -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), @@ -487,7 +511,7 @@ //===- Test a simple pattern with a multiple default operands. ------------===// // -// 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 @@ -508,7 +532,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 -4 is just to distinguish it from the other 'not' cases. def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1), @@ -517,7 +541,7 @@ //===- Test a simple pattern with multiple operands with defaults. --------===// // -// 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 @@ -539,7 +563,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 -5 is just to distinguish it from the other cases. def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1), @@ -550,7 +574,7 @@ // This must precede the 3-register variants because constant immediates have // priority over register banks. -// 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 @@ -570,7 +594,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]] def ORN : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>; def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>; @@ -578,7 +602,7 @@ //===- Test a COPY_TO_REGCLASS --------------------------------------------===// // -// 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*/2, // CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BITCAST, // CHECK-NEXT: // MIs[0] dst @@ -591,14 +615,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 14: @[[LABEL]] +// CHECK-NEXT: // Label 15: @[[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 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_CONSTANT, // CHECK-NEXT: // MIs[0] dst @@ -612,13 +636,13 @@ // CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, -// CHECK-NEXT: // Label 15: @[[LABEL]] +// CHECK-NEXT: // Label 16: @[[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 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: GIM_CheckI64ImmPredicate, /*MI*/0, /*Predicate*/GIPFP_I64_Predicate_simm8, @@ -634,14 +658,14 @@ // 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 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 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_CheckAPIntImmPredicate, /*MI*/0, /*Predicate*/GIPFP_APInt_Predicate_simm9, @@ -657,14 +681,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 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 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: // MIs[0] dst @@ -679,13 +703,13 @@ // 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 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 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_FCONSTANT, // CHECK-NEXT: GIM_CheckAPFloatImmPredicate, /*MI*/0, /*Predicate*/GIPFP_APFloat_Predicate_fpimmz, @@ -701,14 +725,14 @@ // 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 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 20*/ [[LABEL:[0-9]+]], +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 21*/ [[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 @@ -717,7 +741,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 20: @[[LABEL]] +// CHECK-NEXT: // Label 21: @[[LABEL]] def BR : I<(outs), (ins unknown:$target), [(br bb:$target)]>; Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -466,14 +466,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; @@ -501,7 +508,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); @@ -576,6 +586,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, @@ -595,17 +606,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 @@ -628,6 +628,23 @@ virtual unsigned countRendererFns() const { return 0; } }; +/// Generates code to check that a register operand is defined by the same exact +/// one as another. +class TieOperandMatcher : public OperandPredicateMatcher { + std::string TiedTo; + +public: + TieOperandMatcher(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: @@ -834,19 +851,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. @@ -907,6 +911,13 @@ unsigned getAllocatedTemporariesBaseID() const { return AllocatedTemporariesBaseID; } + + bool isTied() const { + for (const auto &Predicate : predicates()) + if (isa(Predicate)) + return true; + return false; + } }; unsigned ComplexPatternOperandMatcher::getAllocatedTemporariesBaseID() const { @@ -1034,6 +1045,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; @@ -1041,13 +1054,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(); } @@ -1061,24 +1080,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(); } @@ -1179,9 +1180,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; @@ -1189,12 +1190,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); @@ -1262,7 +1257,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") @@ -1362,7 +1357,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) @@ -1502,13 +1497,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; @@ -1533,7 +1528,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") @@ -1641,7 +1636,7 @@ }; InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) { - Matchers.emplace_back(new InstructionMatcher(SymbolicName)); + Matchers.emplace_back(new InstructionMatcher(*this, SymbolicName)); return *Matchers.back(); } @@ -1686,6 +1681,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) @@ -1695,6 +1701,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) { @@ -1848,6 +1864,22 @@ return Kind < B.Kind; } +void TieOperandMatcher::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_CheckTie") << 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 { @@ -1887,7 +1919,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, @@ -2002,8 +2035,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.isTied()) + OM.addPredicate(SrcIntInit->getValue()); } else return failedImport( "Unable to deduce gMIR opcode to handle Src (which is a leaf)"); @@ -2029,7 +2064,8 @@ if (const CodeGenIntrinsic *II = Src->getIntrinsicInfo(CGP)) { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, SrcChild->getName(), TempOpIdx); - OM.addPredicate(II); + if (!OM.isTied()) + OM.addPredicate(II); continue; } @@ -2051,6 +2087,8 @@ unsigned &TempOpIdx) const { OperandMatcher &OM = InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx); + if (OM.isTied()) + return Error::success(); ArrayRef ChildTypes = SrcChild->getExtTypes(); if (ChildTypes.size() != 1) @@ -2076,7 +2114,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()) @@ -2129,7 +2168,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 " + @@ -2195,7 +2234,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()); @@ -2296,7 +2335,7 @@ } if (auto Error = importExplicitUseRenderer( - DstMIBuilder, Dst->getChild(Child), InsnMatcher)) + M, DstMIBuilder, Dst->getChild(Child), InsnMatcher)) return std::move(Error); ++Child; } @@ -2349,7 +2388,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()->getValues())) @@ -2388,6 +2427,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); @@ -2448,6 +2488,7 @@ OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); OM.setSymbolicName(DstIOperand.Name); + M.defineOperand(OM.getSymbolicName(), OM); OM.addPredicate( Target.getRegisterClass(DstIOpRec)); ++OpIdx;