From: Daniel Sanders Date: Fri, 13 Oct 2017 21:28:03 +0000 (+0000) Subject: [globalisel][tablegen] Add support for fpimm and import of APInt/APFloat based ImmLeaf. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=94aa10e94b0c4f44947a6943236dd09408ca4035;p=llvm [globalisel][tablegen] Add support for fpimm and import of APInt/APFloat based ImmLeaf. Summary: There's only a tablegen testcase for IntImmLeaf and not a CodeGen one because the relevant rules are rejected for other reasons at the moment. On AArch64, it's because there's an SDNodeXForm attached to the operand. On X86, it's because the rule either emits multiple instructions or has another predicate using PatFrag which cannot easily be supported at the same time. Reviewers: ab, t.p.northover, qcolombet, rovka, aditya_nandakumar Reviewed By: qcolombet Subscribers: aemerson, javed.absar, igorb, llvm-commits, kristof.beyls Differential Revision: https://reviews.llvm.org/D36569 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@315761 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/include/llvm/CodeGen/GlobalISel/InstructionSelector.h index 9f7b9da8735..08c813a79c2 100644 --- a/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ b/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -26,6 +26,8 @@ namespace llvm { +class APInt; +class APFloat; class LLT; class MachineInstr; class MachineInstrBuilder; @@ -96,7 +98,15 @@ enum { /// Check an immediate predicate on the specified instruction /// - InsnID - Instruction ID /// - The predicate to test - GIM_CheckImmPredicate, + GIM_CheckI64ImmPredicate, + /// Check an immediate predicate on the specified instruction via an APInt. + /// - InsnID - Instruction ID + /// - The predicate to test + GIM_CheckAPIntImmPredicate, + /// Check a floating point immediate predicate on the specified instruction. + /// - InsnID - Instruction ID + /// - The predicate to test + GIM_CheckAPFloatImmPredicate, /// Check the type for the specified operand /// - InsnID - Instruction ID @@ -226,7 +236,9 @@ enum { /// Provides the logic to select generic machine instructions. class InstructionSelector { public: - using ImmediatePredicateFn = bool (*)(int64_t); + using I64ImmediatePredicateFn = bool (*)(int64_t); + using APIntImmediatePredicateFn = bool (*)(const APInt &); + using APFloatImmediatePredicateFn = bool (*)(const APFloat &); virtual ~InstructionSelector() = default; @@ -259,7 +271,9 @@ public: struct MatcherInfoTy { const LLT *TypeObjects; const PredicateBitset *FeatureBitsets; - const ImmediatePredicateFn *ImmPredicateFns; + const I64ImmediatePredicateFn *I64ImmPredicateFns; + const APIntImmediatePredicateFn *APIntImmPredicateFns; + const APFloatImmediatePredicateFn *APFloatImmPredicateFns; const std::vector ComplexPredicates; }; diff --git a/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h index a43c663bd6e..6bab06dca2c 100644 --- a/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ b/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -36,7 +36,9 @@ namespace llvm { /// GlobalISel PatFrag Predicates enum { - GIPFP_Invalid, + GIPFP_I64_Invalid = 0, + GIPFP_APInt_Invalid = 0, + GIPFP_APFloat_Invalid = 0, }; template getOpcode() == TargetOpcode::G_CONSTANT && "Expected G_CONSTANT"); - assert(Predicate > GIPFP_Invalid && "Expected a valid predicate"); + assert(Predicate > GIPFP_I64_Invalid && "Expected a valid predicate"); int64_t Value = 0; if (State.MIs[InsnID]->getOperand(1).isCImm()) Value = State.MIs[InsnID]->getOperand(1).getCImm()->getSExtValue(); @@ -167,7 +168,43 @@ bool InstructionSelector::executeMatchTable( else llvm_unreachable("Expected Imm or CImm operand"); - if (!MatcherInfo.ImmPredicateFns[Predicate](Value)) + if (!MatcherInfo.I64ImmPredicateFns[Predicate](Value)) + if (handleReject() == RejectAndGiveUp) + return false; + break; + } + case GIM_CheckAPIntImmPredicate: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t Predicate = MatchTable[CurrentIdx++]; + DEBUG(dbgs() << CurrentIdx << ": GIM_CheckAPIntImmPredicate(MIs[" + << InsnID << "], Predicate=" << Predicate << ")\n"); + assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); + assert(State.MIs[InsnID]->getOpcode() && "Expected G_CONSTANT"); + assert(Predicate > GIPFP_APInt_Invalid && "Expected a valid predicate"); + APInt Value; + if (State.MIs[InsnID]->getOperand(1).isCImm()) + Value = State.MIs[InsnID]->getOperand(1).getCImm()->getValue(); + else + llvm_unreachable("Expected Imm or CImm operand"); + + if (!MatcherInfo.APIntImmPredicateFns[Predicate](Value)) + if (handleReject() == RejectAndGiveUp) + return false; + break; + } + case GIM_CheckAPFloatImmPredicate: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t Predicate = MatchTable[CurrentIdx++]; + DEBUG(dbgs() << CurrentIdx << ": GIM_CheckAPFloatImmPredicate(MIs[" << InsnID + << "], Predicate=" << Predicate << ")\n"); + assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); + assert(State.MIs[InsnID]->getOpcode() == TargetOpcode::G_FCONSTANT && + "Expected G_FCONSTANT"); + assert(State.MIs[InsnID]->getOperand(1).isFPImm() && "Expected FPImm operand"); + assert(Predicate > GIPFP_APFloat_Invalid && "Expected a valid predicate"); + APFloat Value = State.MIs[InsnID]->getOperand(1).getFPImm()->getValueAPF(); + + if (!MatcherInfo.APFloatImmPredicateFns[Predicate](Value)) if (handleReject() == RejectAndGiveUp) return false; break; diff --git a/include/llvm/Target/GlobalISel/SelectionDAGCompat.td b/include/llvm/Target/GlobalISel/SelectionDAGCompat.td index f6da58ba796..96245b251f9 100644 --- a/include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ b/include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -34,7 +34,7 @@ def : GINodeEquiv; // G_INTTOPTR - SelectionDAG has no equivalent. // G_PTRTOINT - SelectionDAG has no equivalent. def : GINodeEquiv; -// G_FCONSTANT - Not needed since constants aren't operators. +def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; def : GINodeEquiv; diff --git a/lib/Target/AArch64/AArch64InstructionSelector.cpp b/lib/Target/AArch64/AArch64InstructionSelector.cpp index 58624f24ec0..88259ffdf00 100644 --- a/lib/Target/AArch64/AArch64InstructionSelector.cpp +++ b/lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -705,6 +705,11 @@ bool AArch64InstructionSelector::select(MachineInstr &I) const { << " constant on bank: " << RB << ", expected: FPR\n"); return false; } + + // The case when we have 0.0 is covered by tablegen. Reject it here so we + // can be sure tablegen works correctly and isn't rescued by this code. + if (I.getOperand(1).getFPImm()->getValueAPF().isExactlyValue(0.0)) + return false; } else { // s32 and s64 are covered by tablegen. if (Ty != p0) { diff --git a/lib/Target/X86/X86InstrFPStack.td b/lib/Target/X86/X86InstrFPStack.td index 78608c43028..f096f51d6be 100644 --- a/lib/Target/X86/X86InstrFPStack.td +++ b/lib/Target/X86/X86InstrFPStack.td @@ -57,20 +57,20 @@ def X86fp_cwd_get16 : SDNode<"X86ISD::FNSTCW16m", SDTX86CwdStore, // FPStack pattern fragments //===----------------------------------------------------------------------===// -def fpimm0 : PatLeaf<(fpimm), [{ - return N->isExactlyValue(+0.0); +def fpimm0 : FPImmLeaf; -def fpimmneg0 : PatLeaf<(fpimm), [{ - return N->isExactlyValue(-0.0); +def fpimmneg0 : FPImmLeaf; -def fpimm1 : PatLeaf<(fpimm), [{ - return N->isExactlyValue(+1.0); +def fpimm1 : FPImmLeaf; -def fpimmneg1 : PatLeaf<(fpimm), [{ - return N->isExactlyValue(-1.0); +def fpimmneg1 : FPImmLeaf; // Some 'special' instructions diff --git a/test/CodeGen/AArch64/GlobalISel/select-constant.mir b/test/CodeGen/AArch64/GlobalISel/select-constant.mir index 1a5bac9fb7d..8e3dbb4c416 100644 --- a/test/CodeGen/AArch64/GlobalISel/select-constant.mir +++ b/test/CodeGen/AArch64/GlobalISel/select-constant.mir @@ -8,6 +8,8 @@ define i32 @fconst_s32() { ret i32 42 } define i64 @fconst_s64() { ret i64 1234567890123 } + define float @fconst_s32_0() { ret float 0.0 } + define double @fconst_s64_0() { ret double 0.0 } ... --- @@ -75,3 +77,37 @@ body: | %0(s64) = G_FCONSTANT double 1.0 %d0 = COPY %0(s64) ... + +--- +# CHECK-LABEL: name: fconst_s32_0 +name: fconst_s32_0 +legalized: true +regBankSelected: true +registers: + - { id: 0, class: fpr } + +# CHECK: body: +# CHECK: [[TMP:%[0-9]+]] = FMOVS0 +# CHECK: %s0 = COPY [[TMP]] +body: | + bb.0: + %0(s32) = G_FCONSTANT float 0.0 + %s0 = COPY %0(s32) +... + +--- +# CHECK-LABEL: name: fconst_s64_0 +name: fconst_s64_0 +legalized: true +regBankSelected: true +registers: + - { id: 0, class: fpr } + +# CHECK: body: +# CHECK: [[TMP:%[0-9]+]] = FMOVD0 +# CHECK: %s0 = COPY [[TMP]] +body: | + bb.0: + %0(s64) = G_FCONSTANT double 0.0 + %s0 = COPY %0(s64) +... diff --git a/test/TableGen/GlobalISelEmitter.td b/test/TableGen/GlobalISelEmitter.td index d08134034ad..be9bc3b9b8f 100644 --- a/test/TableGen/GlobalISelEmitter.td +++ b/test/TableGen/GlobalISelEmitter.td @@ -53,7 +53,7 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; } // CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT // CHECK-NEXT: , State(2), -// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, { +// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, I64ImmPredicateFns, APIntImmPredicateFns, APFloatImmPredicateFns, { // CHECK-NEXT: nullptr, // GICP_Invalid // CHECK-NEXT: &MyTargetInstructionSelector::selectComplexPattern, // gi_complex // CHECK-NEXT: }}) @@ -111,14 +111,34 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; } // CHECK-LABEL: // PatFrag predicates. // CHECK-NEXT: enum { -// CHECK-NEXT: GIPFP_Predicate_simm8 = GIPFP_Invalid + 1, +// CHECK-NEXT: GIPFP_I64_Predicate_simm8 = GIPFP_I64_Invalid + 1, // CHECK-NEXT: }; // CHECK-NEXT: static bool Predicate_simm8(int64_t Imm) { return isInt<8>(Imm); } -// CHECK-NEXT: static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = { +// CHECK-NEXT: static InstructionSelector::I64ImmediatePredicateFn I64ImmPredicateFns[] = { // CHECK-NEXT: nullptr, // CHECK-NEXT: Predicate_simm8, // CHECK-NEXT: }; +// CHECK-LABEL: // PatFrag predicates. +// CHECK-NEXT: enum { +// CHECK-NEXT: GIPFP_APFloat_Predicate_fpimmz = GIPFP_APFloat_Invalid + 1, +// CHECK-NEXT: }; +// CHECK-NEXT: static bool Predicate_fpimmz(const APFloat & Imm) { return Imm->isExactlyValue(0.0); } +// CHECK-NEXT: static InstructionSelector::APFloatImmediatePredicateFn APFloatImmPredicateFns[] = { +// CHECK-NEXT: nullptr, +// CHECK-NEXT: Predicate_fpimmz, +// CHECK-NEXT: }; + +// CHECK-LABEL: // PatFrag predicates. +// CHECK-NEXT: enum { +// CHECK-NEXT: GIPFP_APInt_Predicate_simm9 = GIPFP_APInt_Invalid + 1, +// CHECK-NEXT: }; +// CHECK-NEXT: static bool Predicate_simm9(const APInt & Imm) { return isInt<9>(Imm->getSExtValue()); } +// CHECK-NEXT: static InstructionSelector::APIntImmediatePredicateFn APIntImmPredicateFns[] = { +// CHECK-NEXT: nullptr, +// CHECK-NEXT: Predicate_simm9, +// CHECK-NEXT: }; + // CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const { // CHECK-NEXT: MachineFunction &MF = *I.getParent()->getParent(); // CHECK-NEXT: MachineRegisterInfo &MRI = MF.getRegInfo(); @@ -671,7 +691,7 @@ def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>; // 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_CheckImmPredicate, /*MI*/0, /*Predicate*/GIPFP_Predicate_simm8, +// CHECK-NEXT: GIM_CheckI64ImmPredicate, /*MI*/0, /*Predicate*/GIPFP_I64_Predicate_simm8, // 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, @@ -689,11 +709,34 @@ def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>; def simm8 : ImmLeaf(Imm); }]>; def MOVimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm8:$imm)]>; -//===- Test a simple pattern with just a leaf immediate. ------------------===// +//===- Same again but use an IntImmLeaf. ----------------------------------===// // 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, +// 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] Operand 1 +// CHECK-NEXT: // No operand predicates +// CHECK-NEXT: // (imm:{ *:[i32] })<>:$imm => (MOVimm9:{ *:[i32] } (imm:{ *:[i32] }):$imm) +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVimm9, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_CopyConstantAsSImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +// CHECK-NEXT: GIR_Done, +// 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 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 // CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, // CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, @@ -706,13 +749,36 @@ 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 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_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, +// 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::FPR32RegClassID, +// CHECK-NEXT: // MIs[0] Operand 1 +// CHECK-NEXT: // No operand predicates +// CHECK-NEXT: // (fpimm:{ *:[f32] })<>:$imm => (MOVfpimmz:{ *:[f32] } (fpimm:{ *:[f32] }):$imm) +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVfpimmz, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_CopyFConstantAsFPImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +// CHECK-NEXT: GIR_Done, +// 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 19*/ [[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 @@ -721,7 +787,7 @@ def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm) // CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, -// CHECK-NEXT: // Label 19: @[[LABEL]] +// CHECK-NEXT: // Label 21: @[[LABEL]] def BR : I<(outs), (ins unknown:$target), [(br bb:$target)]>; diff --git a/utils/TableGen/CodeGenDAGPatterns.cpp b/utils/TableGen/CodeGenDAGPatterns.cpp index a9f1b0be816..ced3e979c04 100644 --- a/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/utils/TableGen/CodeGenDAGPatterns.cpp @@ -881,6 +881,14 @@ std::string TreePredicateFn::getImmType() const { return "int64_t"; } +std::string TreePredicateFn::getImmTypeIdentifier() const { + if (immCodeUsesAPInt()) + return "APInt"; + else if (immCodeUsesAPFloat()) + return "APFloat"; + return "I64"; +} + /// isAlwaysTrue - Return true if this is a noop predicate. bool TreePredicateFn::isAlwaysTrue() const { return getPredCode().empty() && getImmCode().empty(); diff --git a/utils/TableGen/CodeGenDAGPatterns.h b/utils/TableGen/CodeGenDAGPatterns.h index 3033986fdd4..5918e901a3d 100644 --- a/utils/TableGen/CodeGenDAGPatterns.h +++ b/utils/TableGen/CodeGenDAGPatterns.h @@ -478,6 +478,10 @@ public: /// Get the data type of the argument to getImmediatePredicateCode(). std::string getImmType() const; + /// Get a string that describes the type returned by getImmType() but is + /// usable as part of an identifier. + std::string getImmTypeIdentifier() const; + private: std::string getPredCode() const; std::string getImmCode() const; diff --git a/utils/TableGen/GlobalISelEmitter.cpp b/utils/TableGen/GlobalISelEmitter.cpp index 18eb271d4ef..2e86b17d0b9 100644 --- a/utils/TableGen/GlobalISelEmitter.cpp +++ b/utils/TableGen/GlobalISelEmitter.cpp @@ -65,6 +65,18 @@ static cl::opt WarnOnSkippedPatterns( namespace { //===- Helper functions ---------------------------------------------------===// + +/// Get the name of the enum value used to number the predicate function. +std::string getEnumNameForPredicate(const TreePredicateFn &Predicate) { + return "GIPFP_" + Predicate.getImmTypeIdentifier() + "_" + + Predicate.getFnName(); +} + +/// Get the opcode used to check this predicate. +std::string getMatchOpcodeForPredicate(const TreePredicateFn &Predicate) { + return "GIM_Check" + Predicate.getImmTypeIdentifier() + "ImmPredicate"; +} + /// This class stands in for LLT wherever we want to tablegen-erate an /// equivalent at compiler run-time. class LLTCodeGen { @@ -1057,10 +1069,10 @@ public: void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule, unsigned InsnVarID) const override { - Table << MatchTable::Opcode("GIM_CheckImmPredicate") + Table << MatchTable::Opcode(getMatchOpcodeForPredicate(Predicate)) << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Predicate") - << MatchTable::NamedValue("GIPFP_" + Predicate.getFnName()) + << MatchTable::NamedValue(getEnumNameForPredicate(Predicate)) << MatchTable::LineBreak; } }; @@ -1258,6 +1270,7 @@ public: OR_Copy, OR_CopySubReg, OR_CopyConstantAsImm, + OR_CopyFConstantAsFPImm, OR_Imm, OR_Register, OR_ComplexPattern @@ -1345,6 +1358,36 @@ public: } }; +/// A CopyFConstantAsFPImmRenderer emits code to render a G_FCONSTANT +/// instruction to an extended immediate operand. +class CopyFConstantAsFPImmRenderer : public OperandRenderer { +protected: + unsigned NewInsnID; + /// The name of the operand. + const std::string SymbolicName; + +public: + CopyFConstantAsFPImmRenderer(unsigned NewInsnID, StringRef SymbolicName) + : OperandRenderer(OR_CopyFConstantAsFPImm), NewInsnID(NewInsnID), + SymbolicName(SymbolicName) {} + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_CopyFConstantAsFPImm; + } + + const StringRef getSymbolicName() const { return SymbolicName; } + + void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + const InstructionMatcher &InsnMatcher = Rule.getInstructionMatcher(SymbolicName); + unsigned OldInsnVarID = Rule.getInsnVarID(InsnMatcher); + Table << MatchTable::Opcode("GIR_CopyFConstantAsFPImm") + << MatchTable::Comment("NewInsnID") << MatchTable::IntValue(NewInsnID) + << MatchTable::Comment("OldInsnID") + << MatchTable::IntValue(OldInsnVarID) + << MatchTable::Comment(SymbolicName) << MatchTable::LineBreak; + } +}; + /// A CopySubRegRenderer emits code to copy a single register operand from an /// existing instruction to the one being built and indicate that only a /// subregister should be copied. @@ -1913,7 +1956,8 @@ private: importImplicitDefRenderers(BuildMIAction &DstMIBuilder, const std::vector &ImplicitDefs) const; - void emitImmPredicates(raw_ostream &OS, + void emitImmPredicates(raw_ostream &OS, StringRef TypeIdentifier, + StringRef Type, std::function Filter); /// Analyze pattern \p P, returning a matcher for it if possible. @@ -2029,8 +2073,9 @@ GlobalISelEmitter::createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher, } else { assert(SrcGIOrNull && "Expected to have already found an equivalent Instruction"); - if (SrcGIOrNull->TheDef->getName() == "G_CONSTANT") { - // imm still has an operand but we don't need to do anything with it + if (SrcGIOrNull->TheDef->getName() == "G_CONSTANT" || + SrcGIOrNull->TheDef->getName() == "G_FCONSTANT") { + // imm/fpimm still have operands but we don't need to do anything with it // here since we don't support ImmLeaf predicates yet. However, we still // need to note the hidden operand to get GIM_CheckNumOperands correct. InsnMatcher.addOperand(OpIdx++, "", TempOpIdx); @@ -2185,6 +2230,10 @@ Error GlobalISelEmitter::importExplicitUseRenderer( DstMIBuilder.addRenderer(0, DstChild->getName()); return Error::success(); + } else if (DstChild->getOperator()->getName() == "fpimm") { + DstMIBuilder.addRenderer( + 0, DstChild->getName()); + return Error::success(); } return failedImport("Dst pattern child isn't a leaf node or an MBB" + llvm::to_string(*DstChild)); @@ -2566,7 +2615,8 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { // The 'Predicate_' part of the name is redundant but eliminating it is more // trouble than it's worth. void GlobalISelEmitter::emitImmPredicates( - raw_ostream &OS, std::function Filter) { + raw_ostream &OS, StringRef TypeIdentifier, StringRef Type, + std::function Filter) { std::vector MatchedRecords; const auto &Defs = RK.getAllDerivedDefinitions("PatFrag"); std::copy_if(Defs.begin(), Defs.end(), std::back_inserter(MatchedRecords), @@ -2575,19 +2625,25 @@ void GlobalISelEmitter::emitImmPredicates( Filter(Record); }); - OS << "// PatFrag predicates.\n" - << "enum {\n"; - StringRef EnumeratorSeparator = " = GIPFP_Invalid + 1,\n"; - for (const auto *Record : MatchedRecords) { - OS << " GIPFP_Predicate_" << Record->getName() << EnumeratorSeparator; - EnumeratorSeparator = ",\n"; + if (!MatchedRecords.empty()) { + OS << "// PatFrag predicates.\n" + << "enum {\n"; + StringRef EnumeratorSeparator = + (" = GIPFP_" + TypeIdentifier + "_Invalid + 1,\n").str(); + for (const auto *Record : MatchedRecords) { + OS << " GIPFP_" << TypeIdentifier << "_Predicate_" << Record->getName() + << EnumeratorSeparator; + EnumeratorSeparator = ",\n"; + } + OS << "};\n"; } - OS << "};\n"; + for (const auto *Record : MatchedRecords) - OS << " static bool Predicate_" << Record->getName() << "(int64_t Imm) {" - << Record->getValueAsString("ImmediateCode") << " }\n"; - OS << "static InstructionSelector::ImmediatePredicateFn ImmPredicateFns[] = " - "{\n" + OS << "static bool Predicate_" << Record->getName() << "(" << Type + << " Imm) {" << Record->getValueAsString("ImmediateCode") << "}\n"; + + OS << "static InstructionSelector::" << TypeIdentifier + << "ImmediatePredicateFn " << TypeIdentifier << "ImmPredicateFns[] = {\n" << " nullptr,\n"; for (const auto *Record : MatchedRecords) OS << " Predicate_" << Record->getName() << ",\n"; @@ -2664,7 +2720,8 @@ void GlobalISelEmitter::run(raw_ostream &OS) { OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n" << ", State(" << MaxTemporaries << "),\n" - << "MatcherInfo({TypeObjects, FeatureBitsets, ImmPredicateFns, {\n" + << "MatcherInfo({TypeObjects, FeatureBitsets, I64ImmPredicateFns, " + "APIntImmPredicateFns, APFloatImmPredicateFns, {\n" << " nullptr, // GICP_Invalid\n"; for (const auto &Record : ComplexPredicates) OS << " &" << Target.getName() @@ -2777,11 +2834,18 @@ void GlobalISelEmitter::run(raw_ostream &OS) { OS << "};\n" << "// See constructor for table contents\n\n"; - emitImmPredicates(OS, [](const Record *R) { + emitImmPredicates(OS, "I64", "int64_t", [](const Record *R) { bool Unset; return !R->getValueAsBitOrUnset("IsAPFloat", Unset) && !R->getValueAsBit("IsAPInt"); }); + emitImmPredicates(OS, "APFloat", "const APFloat &", [](const Record *R) { + bool Unset; + return R->getValueAsBitOrUnset("IsAPFloat", Unset); + }); + emitImmPredicates(OS, "APInt", "const APInt &", [](const Record *R) { + return R->getValueAsBit("IsAPInt"); + }); OS << "bool " << Target.getName() << "InstructionSelector::selectImpl(MachineInstr &I) const {\n"