Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -235,6 +235,11 @@ /// - RendererID - The renderer to call /// - RenderOpID - The suboperand to render. GIR_ComplexSubOperandRenderer, + /// Render operands to the specified instruction using a custom function + /// - InsnID - Instruction ID to modify + /// - OldInsnID - Instruction ID to get the matched operand from + /// - RendererFnID - Custom renderer function to call + GIR_CustomRenderer, /// Render a G_CONSTANT operator as a sign-extended immediate. /// - NewInsnID - Instruction ID to modify @@ -311,11 +316,13 @@ }; public: - template - struct MatcherInfoTy { + template + struct ISelInfoTy { const LLT *TypeObjects; const PredicateBitset *FeatureBitsets; const ComplexMatcherMemFn *ComplexPredicates; + const CustomRendererFn *CustomRenderers; }; protected: @@ -324,10 +331,11 @@ /// Execute a given matcher table and return true if the match was successful /// and false otherwise. template + class ComplexMatcherMemFn, class CustomRendererFn> bool executeMatchTable( TgtInstructionSelector &ISel, NewMIVector &OutMIs, MatcherState &State, - const MatcherInfoTy &MatcherInfo, + const ISelInfoTy + &ISelInfo, const int64_t *MatchTable, const TargetInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures, Index: include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -43,10 +43,11 @@ }; template + class ComplexMatcherMemFn, class CustomRendererFn> bool InstructionSelector::executeMatchTable( TgtInstructionSelector &ISel, NewMIVector &OutMIs, MatcherState &State, - const MatcherInfoTy &MatcherInfo, + const ISelInfoTy + &ISelInfo, const int64_t *MatchTable, const TargetInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures, @@ -124,8 +125,8 @@ dbgs() << CurrentIdx << ": GIM_CheckFeatures(ExpectedBitsetID=" << ExpectedBitsetID << ")\n"); - if ((AvailableFeatures & MatcherInfo.FeatureBitsets[ExpectedBitsetID]) != - MatcherInfo.FeatureBitsets[ExpectedBitsetID]) { + if ((AvailableFeatures & ISelInfo.FeatureBitsets[ExpectedBitsetID]) != + ISelInfo.FeatureBitsets[ExpectedBitsetID]) { if (handleReject() == RejectAndGiveUp) return false; } @@ -292,7 +293,7 @@ << "), TypeID=" << TypeID << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (MRI.getType(State.MIs[InsnID]->getOperand(OpIdx).getReg()) != - MatcherInfo.TypeObjects[TypeID]) { + ISelInfo.TypeObjects[TypeID]) { if (handleReject() == RejectAndGiveUp) return false; } @@ -356,7 +357,7 @@ assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); // FIXME: Use std::invoke() when it's available. ComplexRendererFns Renderer = - (ISel.*MatcherInfo.ComplexPredicates[ComplexPredicateID])( + (ISel.*ISelInfo.ComplexPredicates[ComplexPredicateID])( State.MIs[InsnID]->getOperand(OpIdx)); if (Renderer.hasValue()) State.Renderers[RendererID] = Renderer.getValue(); @@ -649,6 +650,19 @@ break; } + case GIR_CustomRenderer: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t OldInsnID = MatchTable[CurrentIdx++]; + int64_t RendererFnID = MatchTable[CurrentIdx++]; + assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); + DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), + dbgs() << CurrentIdx << ": GIR_CustomRenderer(OutMIs[" + << InsnID << "], MIs[" << OldInsnID << "], " + << RendererFnID << ")\n"); + (ISel.*ISelInfo.CustomRenderers[RendererFnID])(OutMIs[InsnID], + *State.MIs[OldInsnID]); + break; + } case GIR_ConstrainOperandRC: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; @@ -710,7 +724,7 @@ int64_t TypeID = MatchTable[CurrentIdx++]; State.TempRegisters[TempRegID] = - MRI.createGenericVirtualRegister(MatcherInfo.TypeObjects[TypeID]); + MRI.createGenericVirtualRegister(ISelInfo.TypeObjects[TypeID]); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": TempRegs[" << TempRegID << "] = GIR_MakeTempReg(" << TypeID << ")\n"); Index: include/llvm/Target/GlobalISel/SelectionDAGCompat.td =================================================================== --- include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -112,3 +112,9 @@ class GIComplexPatternEquiv { ComplexPattern SelDAGEquivalent = seldag; } + +// Specifies the GlobalISel equivalents for SelectionDAG's SDNodeXForm. +// Should be used on defs that subclass GICustomOperandRenderer<>. +class GISDNodeXFormEquiv { + SDNodeXForm SelDAGEquivalent = seldag; +} Index: include/llvm/Target/GlobalISel/Target.td =================================================================== --- include/llvm/Target/GlobalISel/Target.td +++ include/llvm/Target/GlobalISel/Target.td @@ -46,3 +46,16 @@ // overwritten. string MatcherFn = matcherfn; } + +// Defines a custom renderer. This is analogous to SDNodeXForm from +// SelectionDAG. Unlike SDNodeXForm, this matches a MachineInstr and +// renders directly to the result instruction without an intermediate node. +// +// Definitions that inherit from this may also inherit from GISDNodeXFormEquiv +// to enable the import of SelectionDAG patterns involving those SDNodeXForms. +class GICustomOperandRenderer { + // The function renders the operand(s) of the matched instruction to + // the specified instruction. It should be of the form: + // void render(MachineInstrBuilder &MIB, const MachineInstr &MI) + string RendererFn = rendererfn; +} Index: lib/Target/AArch64/AArch64InstrInfo.td =================================================================== --- lib/Target/AArch64/AArch64InstrInfo.td +++ lib/Target/AArch64/AArch64InstrInfo.td @@ -678,6 +678,9 @@ return CurDAG->getTargetConstant(N->getZExtValue(), SDLoc(N), MVT::i32); }]>; +def gi_trunc_imm : GICustomOperandRenderer<"renderTruncImm">, + GISDNodeXFormEquiv; + def : Pat<(i64 i64imm_32bit:$src), (SUBREG_TO_REG (i64 0), (MOVi32imm (trunc_imm imm:$src)), sub_32)>; Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.cpp +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -92,6 +92,8 @@ return selectAddrModeIndexed(Root, Width / 8); } + void renderTruncImm(MachineInstrBuilder &MIB, const MachineInstr &MI) const; + const AArch64TargetMachine &TM; const AArch64Subtarget &STI; const AArch64InstrInfo &TII; @@ -1522,6 +1524,15 @@ }}; } +void AArch64InstructionSelector::renderTruncImm(MachineInstrBuilder &MIB, + const MachineInstr &MI) const { + const MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); + assert(MI.getOpcode() == TargetOpcode::G_CONSTANT && "Expected G_CONSTANT"); + Optional CstVal = getConstantVRegVal(MI.getOperand(0).getReg(), MRI); + assert(CstVal && "Expected constant value"); + MIB.addImm(CstVal.getValue()); +} + namespace llvm { InstructionSelector * createAArch64InstructionSelector(const AArch64TargetMachine &TM, Index: test/CodeGen/AArch64/GlobalISel/select-mul.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/select-mul.mir @@ -0,0 +1,34 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -O0 -mtriple=aarch64-- -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s +--- +name: mul_i64_sext_imm32 +legalized: true +regBankSelected: true + +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + - { id: 3, class: gpr } + +body: | + bb.0: + liveins: %w0 + + ; Make sure InstructionSelector is able to match a pattern + ; with an SDNodeXForm, trunc_imm. + ; def : Pat<(i64 (mul (sext GPR32:$Rn), (s64imm_32bit:$C))), + ; (SMADDLrrr GPR32:$Rn, (MOVi32imm (trunc_imm imm:$C)), XZR)>; + ; CHECK-LABEL: name: mul_i64_sext_imm32 + ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY %w0 + ; CHECK: [[MOVi32imm:%[0-9]+]]:gpr32 = MOVi32imm 3 + ; CHECK: [[SMADDLrrr:%[0-9]+]]:gpr64 = SMADDLrrr [[COPY]], [[MOVi32imm]], %xzr + ; CHECK: %x0 = COPY [[SMADDLrrr]] + %0:gpr(s32) = COPY %w0 + %1:gpr(s64) = G_SEXT %0(s32) + %2:gpr(s64) = G_CONSTANT i64 3 + %3:gpr(s64) = G_MUL %1, %2 + %x0 = COPY %3(s64) +... + + Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -45,6 +45,16 @@ GIComplexOperandMatcher, GIComplexPatternEquiv; +def cimm8_xform : SDNodeXFormgetZExtValue() << 1; + return CurDAG->getTargetConstant(Val, SDLoc(N), MVT::i64); + }]>; + +def cimm8 : Operand, ImmLeaf(Imm);}], cimm8_xform>; + +def gi_cimm8 : GICustomOperandRenderer<"renderImm8">, + GISDNodeXFormEquiv; + def m1 : OperandWithDefaultOps ; def Z : OperandWithDefaultOps ; def m1Z : OperandWithDefaultOps ; @@ -61,8 +71,10 @@ // CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_DECL // CHECK-NEXT: mutable MatcherState State; // CHECK-NEXT: typedef ComplexRendererFns(MyTargetInstructionSelector::*ComplexMatcherMemFn)(MachineOperand &) const; -// CHECK-NEXT: const MatcherInfoTy MatcherInfo; +// CHECK-NEXT: typedef void(MyTargetInstructionSelector::*CustomRendererFn)(MachineInstrBuilder &, const MachineInstr&) const; +// CHECK-NEXT: const ISelInfoTy ISelInfo; // CHECK-NEXT: static MyTargetInstructionSelector::ComplexMatcherMemFn ComplexPredicateFns[]; +// CHECK-NEXT: static MyTargetInstructionSelector::CustomRendererFn CustomRenderers[]; // CHECK-NEXT: bool testImmPredicate_I64(unsigned PredicateID, int64_t Imm) const override; // CHECK-NEXT: bool testImmPredicate_APInt(unsigned PredicateID, const APInt &Imm) const override; // CHECK-NEXT: bool testImmPredicate_APFloat(unsigned PredicateID, const APFloat &Imm) const override; @@ -70,7 +82,7 @@ // CHECK-LABEL: #ifdef GET_GLOBALISEL_TEMPORARIES_INIT // CHECK-NEXT: , State(2), -// CHECK-NEXT: MatcherInfo({TypeObjects, FeatureBitsets, ComplexPredicateFns}) +// CHECK-NEXT: ISelInfo({TypeObjects, FeatureBitsets, ComplexPredicateFns, CustomRenderers}) // CHECK-NEXT: #endif // ifdef GET_GLOBALISEL_TEMPORARIES_INIT // CHECK-LABEL: enum SubtargetFeatureBits : uint8_t { @@ -128,19 +140,27 @@ // CHECK-LABEL: // PatFrag predicates. // CHECK-NEXT: enum { -// CHECK-NEXT: GIPFP_I64_Predicate_simm8 = GIPFP_I64_Invalid + 1, +// CHECK-NEXT: GIPFP_I64_Predicate_cimm8 = GIPFP_I64_Invalid + 1, +// CHECK-NEXT: GIPFP_I64_Predicate_simm8, // CHECK-NEXT: }; -// CHECK-NEXT: bool MyTargetInstructionSelector::testImmPredicate_I64(unsigned PredicateID, int64_t Imm) const { -// CHECK-NEXT: switch (PredicateID) { -// CHECK-NEXT: case GIPFP_I64_Predicate_simm8: { + + +// CHECK-NEXT: bool MyTargetInstructionSelector::testImmPredicate_I64(unsigned PredicateID, int64_t Imm) const { +// CHECK-NEXT: switch (PredicateID) { +// CHECK-NEXT: case GIPFP_I64_Predicate_cimm8: { +// CHECK-NEXT: return isInt<8>(Imm); +// CHECK-NEXT: llvm_unreachable("ImmediateCode should have returned"); +// CHECK-NEXT: return false; +// CHECK-NEXT: } +// CHECK-NEXT: case GIPFP_I64_Predicate_simm8: { // CHECK-NEXT: return isInt<8>(Imm); -// CHECK-NEXT: llvm_unreachable("ImmediateCode should have returned"); -// CHECK-NEXT: return false; -// CHECK-NEXT: } -// CHECK-NEXT: } -// CHECK-NEXT: llvm_unreachable("Unknown predicate"); -// CHECK-NEXT: return false; -// CHECK-NEXT: } +// CHECK-NEXT: llvm_unreachable("ImmediateCode should have returned"); +// CHECK-NEXT: return false; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: llvm_unreachable("Unknown predicate"); +// CHECK-NEXT: return false; +// CHECK-NEXT: } // CHECK-LABEL: // PatFrag predicates. // CHECK-NEXT: enum { @@ -181,6 +201,17 @@ // CHECK-NEXT: &MyTargetInstructionSelector::selectComplexPatternRR, // gi_complex_rr // CHECK-NEXT: } +// CHECK-LABEL: // Custom renderers. +// CHECK-NEXT: enum { +// CHECK-NEXT: GICR_Invalid, +// CHECK-NEXT: GICR_renderImm8, +// CHECK-NEXT: }; +// CHECK-NEXT: MyTargetInstructionSelector::CustomRendererFn +// CHECK-NEXT: MyTargetInstructionSelector::CustomRenderers[] = { +// CHECK-NEXT: nullptr, // GICP_Invalid +// CHECK-NEXT: &MyTargetInstructionSelector::renderImm8, // gi_cimm8 +// CHECK-NEXT: }; + // CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const { // CHECK-NEXT: MachineFunction &MF = *I.getParent()->getParent(); // CHECK-NEXT: MachineRegisterInfo &MRI = MF.getRegInfo(); @@ -892,12 +923,33 @@ // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, // CHECK-NEXT: // Label [[LABEL_NUM]]: @[[LABEL]] + +def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>; + + +//===- Test a pattern with a custom renderer. -----------------------------===// +// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label [[LABEL_NUM:[0-9]+]]*/ [[LABEL:[0-9]+]], +// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2, +// NOOPT-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT, +// CHECK-NEXT: GIM_CheckI64ImmPredicate, /*MI*/0, /*Predicate*/GIPFP_I64_Predicate_cimm8, +// 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 => (MOVcimm8:{ *:[i32] } (cimm8_xform:{ *:[i32] } (imm:{ *:[i32] }):$imm)) +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVcimm8, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_CustomRenderer, /*InsnID*/0, /*OldInsnID*/0, /*Renderer*/GICR_renderImm8, // imm +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +// CHECK-NEXT: GIR_Done, +// CHECK-NEXT: // Label [[LABEL_NUM]]: @[[LABEL]] // Closing the G_CONSTANT group. // OPT-NEXT: GIM_Reject, // OPT-NEXT: GIR_Done, // OPT-NEXT: // Label [[GRP_LABEL_NUM]]: @[[GRP_LABEL]] - -def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>; +def MOVcimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, cimm8:$imm)]>; //===- Test a simple pattern with a FP immediate and a predicate. ---------===// @@ -1020,6 +1072,6 @@ // CHECK-NEXT: GIM_Reject, // CHECK-NEXT: }; -// CHECK-NEXT: if (executeMatchTable(*this, OutMIs, State, MatcherInfo, MatchTable0, TII, MRI, TRI, RBI, AvailableFeatures, CoverageInfo)) { +// CHECK-NEXT: if (executeMatchTable(*this, OutMIs, State, ISelInfo, MatchTable0, TII, MRI, TRI, RBI, AvailableFeatures, CoverageInfo)) { // CHECK-NEXT: return true; // CHECK-NEXT: } Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -260,6 +260,11 @@ ")") .str(); + if (Operator->isSubClassOf("SDNodeXForm")) + return (" (Operator is an unmapped SDNodeXForm, " + Operator->getName() + + ")") + .str(); + return (" (Operator " + Operator->getName() + " not understood)").str(); } @@ -315,12 +320,7 @@ break; } - if (N->getTransformFn()) { - Explanation += Separator + "Has a transform function"; - Separator = ", "; - } - - if (!HasUnsupportedPredicate && !N->getTransformFn()) + if (!HasUnsupportedPredicate) return Error::success(); return failedImport(Explanation); @@ -1706,7 +1706,8 @@ OR_Imm, OR_Register, OR_TempRegister, - OR_ComplexPattern + OR_ComplexPattern, + OR_Custom }; protected: @@ -2018,6 +2019,38 @@ } }; +class CustomRenderer : public OperandRenderer { +protected: + unsigned InsnID; + const Record &Renderer; + /// The name of the operand. + const std::string SymbolicName; + +public: + CustomRenderer(unsigned InsnID, const Record &Renderer, + StringRef SymbolicName) + : OperandRenderer(OR_Custom), InsnID(InsnID), Renderer(Renderer), + SymbolicName(SymbolicName) {} + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_Custom; + } + + void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + const InstructionMatcher &InsnMatcher = + Rule.getInstructionMatcher(SymbolicName); + unsigned OldInsnVarID = Rule.getInsnVarID(InsnMatcher); + Table << MatchTable::Opcode("GIR_CustomRenderer") + << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) + << MatchTable::Comment("OldInsnID") + << MatchTable::IntValue(OldInsnVarID) + << MatchTable::Comment("Renderer") + << MatchTable::NamedValue( + "GICR_" + Renderer.getValueAsString("RendererFn").str()) + << MatchTable::Comment(SymbolicName) << MatchTable::LineBreak; + } +}; + /// An action taken when all Matcher predicates succeeded for a parent rule. /// /// Typical actions include: @@ -2541,6 +2574,11 @@ /// GIComplexPatternEquiv. DenseMap ComplexPatternEquivs; + /// Keep track of the equivalence between SDNodeXForm's and + /// GICustomOperandRenderer. Map entries are specified by subclassing + /// GISDNodeXFormEquiv. + DenseMap SDNodeXFormEquivs; + // Map of predicates to their subtarget features. SubtargetFeatureInfoMap SubtargetFeatures; @@ -2645,6 +2683,14 @@ continue; ComplexPatternEquivs[SelDAGEquiv] = Equiv; } + + assert(SDNodeXFormEquivs.empty()); + for (Record *Equiv : RK.getAllDerivedDefinitions("GISDNodeXFormEquiv")) { + Record *SelDAGEquiv = Equiv->getValueAsDef("SelDAGEquivalent"); + if (!SelDAGEquiv) + continue; + SDNodeXFormEquivs[SelDAGEquiv] = Equiv; + } } Record *GlobalISelEmitter::findNodeEquiv(Record *N) const { @@ -2986,10 +3032,6 @@ Expected GlobalISelEmitter::importExplicitUseRenderer( action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, TreePatternNode *DstChild) { - if (DstChild->getTransformFn() != nullptr) { - return failedImport("Dst pattern child has transform fn " + - DstChild->getTransformFn()->getName()); - } const auto &SubOperand = Rule.getComplexSubOperand(DstChild->getName()); if (SubOperand.hasValue()) { @@ -3000,6 +3042,18 @@ } if (!DstChild->isLeaf()) { + + if (DstChild->getOperator()->isSubClassOf("SDNodeXForm")) { + auto Child = DstChild->getChild(0); + auto I = SDNodeXFormEquivs.find(DstChild->getOperator()); + if (I != SDNodeXFormEquivs.end()) { + DstMIBuilder.addRenderer(*I->second, Child->getName()); + return InsertPt; + } + return failedImport("SDNodeXForm " + Child->getName() + + " has no custom renderer"); + } + // We accept 'bb' here. It's an operator because BasicBlockSDNode isn't // inline, but in MI it's just another operand. if (DstChild->getOperator()->isSubClassOf("SDNode")) { @@ -3104,10 +3158,6 @@ return InsertPt; } - if (ChildRec->isSubClassOf("SDNodeXForm")) - return failedImport("Dst pattern child def is an unsupported tablegen " - "class (SDNodeXForm)"); - return failedImport( "Dst pattern child def is an unsupported tablegen class"); } @@ -3652,14 +3702,19 @@ Rules.push_back(std::move(MatcherOrErr.get())); } + // Comparison function to order records by name. + auto orderByName = [](const Record *A, const Record *B) { + return A->getName() < B->getName(); + }; + std::vector ComplexPredicates = RK.getAllDerivedDefinitions("GIComplexOperandMatcher"); - std::sort(ComplexPredicates.begin(), ComplexPredicates.end(), - [](const Record *A, const Record *B) { - if (A->getName() < B->getName()) - return true; - return false; - }); + std::sort(ComplexPredicates.begin(), ComplexPredicates.end(), orderByName); + + std::vector CustomRendererFns = + RK.getAllDerivedDefinitions("GICustomOperandRenderer"); + std::sort(CustomRendererFns.begin(), CustomRendererFns.end(), orderByName); + unsigned MaxTemporaries = 0; for (const auto &Rule : Rules) MaxTemporaries = std::max(MaxTemporaries, Rule.countRendererFns()); @@ -3677,10 +3732,18 @@ "ComplexRendererFns(" << Target.getName() << "InstructionSelector::*ComplexMatcherMemFn)(MachineOperand &) const;\n" - << " const MatcherInfoTy " - "MatcherInfo;\n" - << " static " << Target.getName() + + << " typedef void(" << Target.getName() + << "InstructionSelector::*CustomRendererFn)(MachineInstrBuilder &, const " + "MachineInstr&) " + "const;\n" + << " const ISelInfoTy " + "ISelInfo;\n"; + OS << " static " << Target.getName() << "InstructionSelector::ComplexMatcherMemFn ComplexPredicateFns[];\n" + << " static " << Target.getName() + << "InstructionSelector::CustomRendererFn CustomRenderers[];\n" << "bool testImmPredicate_I64(unsigned PredicateID, int64_t Imm) const " "override;\n" << "bool testImmPredicate_APInt(unsigned PredicateID, const APInt &Imm) " @@ -3691,7 +3754,8 @@ OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n" << ", State(" << MaxTemporaries << "),\n" - << "MatcherInfo({TypeObjects, FeatureBitsets, ComplexPredicateFns})\n" + << "ISelInfo({TypeObjects, FeatureBitsets, ComplexPredicateFns, " + "CustomRenderers})\n" << "#endif // ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n\n"; OS << "#ifdef GET_GLOBALISEL_IMPL\n"; @@ -3821,6 +3885,22 @@ << ", // " << Record->getName() << "\n"; OS << "};\n\n"; + OS << "// Custom renderers.\n" + << "enum {\n" + << " GICR_Invalid,\n"; + for (const auto &Record : CustomRendererFns) + OS << " GICR_" << Record->getValueAsString("RendererFn") << ", \n"; + OS << "};\n"; + + OS << Target.getName() << "InstructionSelector::CustomRendererFn\n" + << Target.getName() << "InstructionSelector::CustomRenderers[] = {\n" + << " nullptr, // GICP_Invalid\n"; + for (const auto &Record : CustomRendererFns) + OS << " &" << Target.getName() + << "InstructionSelector::" << Record->getValueAsString("RendererFn") + << ", // " << Record->getName() << "\n"; + OS << "};\n\n"; + OS << "bool " << Target.getName() << "InstructionSelector::selectImpl(MachineInstr &I, CodeGenCoverage " "&CoverageInfo) const {\n" @@ -3862,7 +3942,7 @@ } Table << MatchTable::Opcode("GIM_Reject") << MatchTable::LineBreak; Table.emitDeclaration(OS); - OS << " if (executeMatchTable(*this, OutMIs, State, MatcherInfo, "; + OS << " if (executeMatchTable(*this, OutMIs, State, ISelInfo, "; Table.emitUse(OS); OS << ", TII, MRI, TRI, RBI, AvailableFeatures, CoverageInfo)) {\n" << " return true;\n"