Index: include/llvm/CodeGen/MachineOperand.h =================================================================== --- include/llvm/CodeGen/MachineOperand.h +++ include/llvm/CodeGen/MachineOperand.h @@ -65,6 +65,7 @@ MO_CFIIndex, ///< MCCFIInstruction index. MO_IntrinsicID, ///< Intrinsic ID for ISel MO_Predicate, ///< Generic predicate for ISel + MO_Placeholder, ///< Placeholder for GlobalISel ComplexPattern result. }; private: @@ -767,6 +768,11 @@ return Op; } + static MachineOperand CreatePlaceholder() { + MachineOperand Op(MachineOperand::MO_Placeholder); + return Op; + } + friend class MachineInstr; friend class MachineRegisterInfo; private: Index: include/llvm/Target/GlobalISel/SelectionDAGCompat.td =================================================================== --- include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -43,3 +43,9 @@ def : GINodeEquiv; def : GINodeEquiv; + +// Specifies the GlobalISel equivalents for SelectionDAG's ComplexPattern. +// Should be used on defs that subclass GIComplexOperandMatcher<>. +class GIComplexPatternEquiv { + ComplexPattern SelDAGEquivalent = seldag; +} Index: include/llvm/Target/GlobalISel/Target.td =================================================================== --- /dev/null +++ include/llvm/Target/GlobalISel/Target.td @@ -0,0 +1,55 @@ +//===- Target.td - Define GlobalISel rules -----------------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the target-independent interfaces used to support +// SelectionDAG instruction selection patterns (specified in +// TargetSelectionDAG.td) when generating GlobalISel instruction selectors. +// +// This is intended as a compatibility layer, to enable reuse of target +// descriptions written for SelectionDAG without requiring explicit GlobalISel +// support. It will eventually supersede SelectionDAG patterns. +// +//===----------------------------------------------------------------------===// + +// Definitions that inherit from LLT define types that will be used in the +// GlobalISel matcher. +class LLT; + +def s32 : LLT; + +// Defines a matcher for complex operands. This is analogous to ComplexPattern +// from SelectionDAG. +// +// Definitions that inherit from this may also inherit from +// GIComplexPatternEquiv to enable the import of SelectionDAG patterns involving +// those ComplexPatterns. +class GIComplexOperandMatcher { + // The expected type of the root of the match. + // + // TODO: We should probably support, any-type, any-scalar, and multiple types + // in the future. + LLT Type = type; + + // The operands that result from a successful match + // Should be of the form '(ops ty1, ty2, ...)' where ty1/ty2 are definitions + // that inherit from Operand. + // + // FIXME: Which definition is used for ty1/ty2 doesn't actually matter at the + // moment. Only the number of operands is used. + dag Operands = operands; + + // The function that determines whether the operand matches. It should be of + // the form: + // bool select(const MatchOperand &Root, MatchOperand &Result1) + // and should have the same number of ResultX arguments as the number of + // result operands. It must return true on successful match and false + // otherwise. If it returns true, then all the ResultX arguments must be + // overwritten. + string MatcherFn = matcherfn; +} Index: include/llvm/Target/Target.td =================================================================== --- include/llvm/Target/Target.td +++ include/llvm/Target/Target.td @@ -1357,6 +1357,11 @@ include "llvm/Target/GlobalISel/RegisterBank.td" //===----------------------------------------------------------------------===// +// Pull in the common support for DAG isel generation. +// +include "llvm/Target/GlobalISel/Target.td" + +//===----------------------------------------------------------------------===// // Pull in the common support for the Global ISel DAG-based selector generation. // include "llvm/Target/GlobalISel/SelectionDAGCompat.td" Index: lib/CodeGen/MIRPrinter.cpp =================================================================== --- lib/CodeGen/MIRPrinter.cpp +++ lib/CodeGen/MIRPrinter.cpp @@ -906,6 +906,9 @@ << CmpInst::getPredicateName(Pred) << ')'; break; } + case MachineOperand::MO_Placeholder: + OS << ""; + break; } } Index: lib/CodeGen/MachineInstr.cpp =================================================================== --- lib/CodeGen/MachineInstr.cpp +++ lib/CodeGen/MachineInstr.cpp @@ -274,6 +274,8 @@ return getIntrinsicID() == Other.getIntrinsicID(); case MachineOperand::MO_Predicate: return getPredicate() == Other.getPredicate(); + case MachineOperand::MO_Placeholder: + return true; } llvm_unreachable("Invalid machine operand type"); } @@ -322,6 +324,8 @@ return hash_combine(MO.getType(), MO.getTargetFlags(), MO.getIntrinsicID()); case MachineOperand::MO_Predicate: return hash_combine(MO.getType(), MO.getTargetFlags(), MO.getPredicate()); + case MachineOperand::MO_Placeholder: + return hash_combine(); } llvm_unreachable("Invalid machine operand type"); } @@ -491,7 +495,11 @@ auto Pred = static_cast(getPredicate()); OS << '<' << (CmpInst::isIntPredicate(Pred) ? "intpred" : "floatpred") << CmpInst::getPredicateName(Pred) << '>'; + break; } + case MachineOperand::MO_Placeholder: + OS << ""; + break; } if (unsigned TF = getTargetFlags()) OS << "[TF=" << TF << ']'; Index: lib/Target/AArch64/AArch64InstrFormats.td =================================================================== --- lib/Target/AArch64/AArch64InstrFormats.td +++ lib/Target/AArch64/AArch64InstrFormats.td @@ -689,6 +689,10 @@ def addsub_shifted_imm32_neg : addsub_shifted_imm_neg; def addsub_shifted_imm64_neg : addsub_shifted_imm_neg; +def gi_addsub_shifted_imm32 : + GIComplexOperandMatcher, + GIComplexPatternEquiv; + class neg_addsub_shifted_imm : Operand, ComplexPattern { let PrintMethod = "printAddSubImm"; Index: lib/Target/AArch64/AArch64InstructionSelector.h =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.h +++ lib/Target/AArch64/AArch64InstructionSelector.h @@ -15,6 +15,7 @@ #define LLVM_LIB_TARGET_AARCH64_AARCH64INSTRUCTIONSELECTOR_H #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/MachineOperand.h" namespace llvm { @@ -23,6 +24,7 @@ class AArch64RegisterInfo; class AArch64Subtarget; class AArch64TargetMachine; +class MachineOperand; class MachineFunction; class MachineRegisterInfo; @@ -45,11 +47,20 @@ /// the patterns that don't require complex C++. bool selectImpl(MachineInstr &I) const; + bool selectArithImmed(MachineOperand &Root, MachineOperand &Result1, + MachineOperand &Result2) const; + const AArch64TargetMachine &TM; const AArch64Subtarget &STI; const AArch64InstrInfo &TII; const AArch64RegisterInfo &TRI; const AArch64RegisterBankInfo &RBI; + +// We declare the temporaries used by selectImpl() in the class to minimize the +// cost of constructing placeholder values. +#define GET_GLOBALISEL_TEMPORARIES_DECL +#include "AArch64GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_DECL }; } // end namespace llvm Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.cpp +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -37,13 +37,20 @@ #error "You shouldn't build this" #endif +#define GET_GLOBALISEL_IMPL #include "AArch64GenGlobalISel.inc" +#undef GET_GLOBALISEL_IMPL AArch64InstructionSelector::AArch64InstructionSelector( const AArch64TargetMachine &TM, const AArch64Subtarget &STI, const AArch64RegisterBankInfo &RBI) - : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()), - TRI(*STI.getRegisterInfo()), RBI(RBI) {} + : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()), + TRI(*STI.getRegisterInfo()), RBI(RBI) +#define GET_GLOBALISEL_TEMPORARIES_INIT +#include "AArch64GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_INIT +{ +} // FIXME: This should be target-independent, inferred from the types declared // for each class in the bank. @@ -1213,3 +1220,50 @@ return false; } + +/// SelectArithImmed - Select an immediate value that can be represented as +/// a 12-bit value shifted left by either 0 or 12. If so, return true with +/// Val set to the 12-bit value and Shift set to the shifter operand. +bool AArch64InstructionSelector::selectArithImmed( + MachineOperand &Root, MachineOperand &Result1, + MachineOperand &Result2) const { + MachineInstr &MI = *Root.getParent(); + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + // This function is called from the addsub_shifted_imm ComplexPattern, + // which lists [imm] as the list of opcode it's interested in, however + // we still need to check whether the operand is actually an immediate + // here because the ComplexPattern opcode list is only used in + // root-level opcode matching. + uint64_t Immed; + if (Root.isImm()) + Immed = Root.getImm(); + else if (Root.isCImm()) + Immed = Root.getCImm()->getZExtValue(); + else if (Root.isReg()) { + MachineInstr *Def = MRI.getVRegDef(Root.getReg()); + if (Def->getOpcode() != TargetOpcode::G_CONSTANT) + return false; + Immed = Def->getOperand(1).getImm(); + } else + return false; + + unsigned ShiftAmt; + + if (Immed >> 12 == 0) { + ShiftAmt = 0; + } else if ((Immed & 0xfff) == 0 && Immed >> 24 == 0) { + ShiftAmt = 12; + Immed = Immed >> 12; + } else + return false; + + unsigned ShVal = AArch64_AM::getShifterImm(AArch64_AM::LSL, ShiftAmt); + Result1.ChangeToImmediate(Immed); + Result1.clearParent(); + Result2.ChangeToImmediate(ShVal); + Result2.clearParent(); + return true; +} Index: lib/Target/ARM/ARMExpandPseudoInsts.cpp =================================================================== --- lib/Target/ARM/ARMExpandPseudoInsts.cpp +++ lib/Target/ARM/ARMExpandPseudoInsts.cpp @@ -661,6 +661,7 @@ return false; case MachineOperand::MO_IntrinsicID: case MachineOperand::MO_Predicate: + case MachineOperand::MO_Placeholder: llvm_unreachable("should not exist post-isel"); } llvm_unreachable("unhandled machine operand type"); Index: lib/Target/X86/X86InstructionSelector.h =================================================================== --- lib/Target/X86/X86InstructionSelector.h +++ lib/Target/X86/X86InstructionSelector.h @@ -14,6 +14,7 @@ #define LLVM_LIB_TARGET_X86_X86INSTRUCTIONSELECTOR_H #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/MachineOperand.h" namespace llvm { @@ -49,6 +50,10 @@ const X86InstrInfo &TII; const X86RegisterInfo &TRI; const X86RegisterBankInfo &RBI; + +#define GET_GLOBALISEL_TEMPORARIES_DECL +#include "X86GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_DECL }; } // end namespace llvm Index: lib/Target/X86/X86InstructionSelector.cpp =================================================================== --- lib/Target/X86/X86InstructionSelector.cpp +++ lib/Target/X86/X86InstructionSelector.cpp @@ -35,12 +35,19 @@ #error "You shouldn't build this" #endif +#define GET_GLOBALISEL_IMPL #include "X86GenGlobalISel.inc" +#undef GET_GLOBALISEL_IMPL X86InstructionSelector::X86InstructionSelector(const X86Subtarget &STI, const X86RegisterBankInfo &RBI) : InstructionSelector(), STI(STI), TII(*STI.getInstrInfo()), - TRI(*STI.getRegisterInfo()), RBI(RBI) {} + TRI(*STI.getRegisterInfo()), RBI(RBI) +#define GET_GLOBALISEL_TEMPORARIES_INIT +#include "X86GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_INIT +{ +} // FIXME: This should be target-independent, inferred from the types declared // for each class in the bank. Index: test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir =================================================================== --- test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir +++ test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir @@ -11,6 +11,9 @@ define void @add_s32_gpr() { ret void } define void @add_s64_gpr() { ret void } + define void @add_imm_s32_gpr() { ret void } + define void @add_imm_s32_gpr_bb() { ret void } + define void @sub_s32_gpr() { ret void } define void @sub_s64_gpr() { ret void } @@ -204,6 +207,69 @@ ... --- +# Check that we select a 32-bit GPR G_ADD into ADDWrr on GPR32. +# Also check that we constrain the register class of the COPY to GPR32. +# CHECK-LABEL: name: add_imm_s32_gpr +name: add_imm_s32_gpr +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32sp } +# CHECK-NEXT: - { id: 1, class: gpr32 } +# CHECK-NEXT: - { id: 2, class: gpr32sp } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: %2 = ADDWri %0, 1, 0 +body: | + bb.0: + liveins: %w0, %w1 + + %0(s32) = COPY %w0 + %1(s32) = G_CONSTANT 1 + %2(s32) = G_ADD %0, %1 +... + +--- +# Check that we select a 32-bit GPR G_ADD into ADDWrr on GPR32. +# Also check that we constrain the register class of the COPY to GPR32. +# CHECK-LABEL: name: add_imm_s32_gpr_bb +name: add_imm_s32_gpr_bb +legalized: true +regBankSelected: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32sp } +# CHECK-NEXT: - { id: 1, class: gpr32 } +# CHECK-NEXT: - { id: 2, class: gpr32sp } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: bb.1: +# CHECK: %2 = ADDWri %0, 1, 0 +body: | + bb.0: + liveins: %w0, %w1 + successors: %bb.1 + + %0(s32) = COPY %w0 + %1(s32) = G_CONSTANT 1 + G_BR %bb.1 + + bb.1: + %2(s32) = G_ADD %0, %1 +... + +--- # Same as add_s32_gpr, for G_SUB operations. # CHECK-LABEL: name: sub_s32_gpr name: sub_s32_gpr Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -21,7 +21,8 @@ //===- Test the function definition boilerplate. --------------------------===// // CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const { -// CHECK: const MachineRegisterInfo &MRI = I.getParent()->getParent()->getRegInfo(); +// CHECK: MachineFunction &MF = *I.getParent()->getParent(); +// CHECK: const MachineRegisterInfo &MRI = MF.getRegInfo(); //===- Test a simple pattern with regclass operands. ----------------------===// Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -42,6 +42,7 @@ #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include +#include using namespace llvm; #define DEBUG_TYPE "gisel-emitter" @@ -80,6 +81,55 @@ } llvm_unreachable("Unhandled LLT"); } + + const LLT &get() const { return Ty; } +}; + +class InstructionMatcher; +class OperandPlaceholder { +private: + enum PlaceholderKind { + OP_MatchReference, + OP_Temporary, + } Kind; + + struct MatchReferenceData { + InstructionMatcher *InsnMatcher; + StringRef InsnVarName; + StringRef SymbolicName; + }; + + struct TemporaryData { + unsigned OpIdx; + }; + + union { + struct MatchReferenceData MatchReference; + struct TemporaryData Temporary; + }; + + OperandPlaceholder(PlaceholderKind Kind) : Kind(Kind) {} + +public: + ~OperandPlaceholder() {} + + static OperandPlaceholder + CreateMatchReference(InstructionMatcher *InsnMatcher, + const StringRef InsnVarName, const StringRef SymbolicName) { + OperandPlaceholder Result(OP_MatchReference); + Result.MatchReference.InsnMatcher = InsnMatcher; + Result.MatchReference.InsnVarName = InsnVarName; + Result.MatchReference.SymbolicName = SymbolicName; + return Result; + } + + static OperandPlaceholder CreateTemporary(unsigned OpIdx) { + OperandPlaceholder Result(OP_Temporary); + Result.Temporary.OpIdx = OpIdx; + return Result; + } + + void emitCxxValueExpr(raw_ostream &OS) const; }; /// Convert an MVT to an equivalent LLT if possible, or the invalid LLT() for @@ -154,6 +204,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_ComplexPattern, OPM_Int, OPM_LLT, OPM_RegBank, @@ -179,6 +230,10 @@ virtual bool isHigherPriorityThan(const OperandPredicateMatcher &B) const { return Kind < B.Kind; }; + + /// Report the maximum number of temporary operands needed by the predicate + /// matcher. + virtual unsigned countTemporaryOperands() const { return 0; } }; /// Generates code to check that an operand is a particular LLT. @@ -202,6 +257,39 @@ } }; +/// Generates code to check that an operand is a particular target constant. +class ComplexPatternOperandMatcher : public OperandPredicateMatcher { +protected: + const Record &TheDef; + /// The index of the first temporary operand to allocate to this + /// ComplexPattern. + unsigned BaseTemporaryID; + + unsigned getNumOperands() const { + return TheDef.getValueAsDag("Operands")->getNumArgs(); + } + +public: + ComplexPatternOperandMatcher(const Record &TheDef, unsigned BaseTemporaryID) + : OperandPredicateMatcher(OPM_ComplexPattern), TheDef(TheDef), + BaseTemporaryID(BaseTemporaryID) {} + + void emitCxxPredicateExpr(raw_ostream &OS, + StringRef OperandExpr) const override { + OS << TheDef.getValueAsString("MatcherFn") << "(" << OperandExpr; + for (unsigned I = 0; I < getNumOperands(); ++I) { + OS << ", "; + OperandPlaceholder::CreateTemporary(BaseTemporaryID + I) + .emitCxxValueExpr(OS); + } + OS << ")"; + } + + unsigned countTemporaryOperands() const override { + return getNumOperands(); + } +}; + /// Generates code to check that an operand is in a particular register bank. class RegisterBankOperandMatcher : public OperandPredicateMatcher { protected: @@ -309,6 +397,17 @@ return false; }; + + /// Report the maximum number of temporary operands needed by the operand + /// matcher. + unsigned countTemporaryOperands() const { + return std::accumulate( + predicates().begin(), predicates().end(), 0, + [](unsigned A, + const std::unique_ptr &Predicate) { + return A + Predicate->countTemporaryOperands(); + }); + } }; /// Generates code to check a predicate on an instruction. @@ -344,6 +443,10 @@ virtual bool isHigherPriorityThan(const InstructionPredicateMatcher &B) const { return Kind < B.Kind; }; + + /// Report the maximum number of temporary operands needed by the predicate + /// matcher. + virtual unsigned countTemporaryOperands() const { return 0; } }; /// Generates code to check the opcode of an instruction. @@ -466,14 +569,40 @@ return false; }; + + /// Report the maximum number of temporary operands needed by the instruction + /// matcher. + unsigned countTemporaryOperands() const { + return std::accumulate(predicates().begin(), predicates().end(), 0, + [](unsigned A, + const std::unique_ptr + &Predicate) { + return A + Predicate->countTemporaryOperands(); + }) + + std::accumulate(Operands.begin(), Operands.end(), 0, + [](unsigned A, const OperandMatcher &Operand) { + return A + Operand.countTemporaryOperands(); + }); + } }; //===- Actions ------------------------------------------------------------===// +void OperandPlaceholder::emitCxxValueExpr(raw_ostream &OS) const { + switch (Kind) { + case OP_MatchReference: + OS << MatchReference.InsnMatcher->getOperand(MatchReference.SymbolicName) + .getOperandExpr(MatchReference.InsnVarName); + break; + case OP_Temporary: + OS << "TempOp" << Temporary.OpIdx; + break; + } +} namespace { class OperandRenderer { public: - enum RendererKind { OR_Copy, OR_Register }; + enum RendererKind { OR_Copy, OR_Register, OR_ComplexPattern }; protected: RendererKind Kind; @@ -539,6 +668,34 @@ } }; +class RenderComplexPatternOperand : public OperandRenderer { +private: + const Record &TheDef; + std::vector Sources; + + unsigned getNumOperands() const { + return TheDef.getValueAsDag("Operands")->getNumArgs(); + } + +public: + RenderComplexPatternOperand(const Record &TheDef, + const ArrayRef Sources) + : OperandRenderer(OR_ComplexPattern), TheDef(TheDef), Sources(Sources) {} + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_ComplexPattern; + } + + void emitCxxRenderStmts(raw_ostream &OS) const override { + assert(Sources.size() == getNumOperands() && "Inconsistent number of operands"); + for (const auto &Source : Sources) { + OS << "MIB.add("; + Source.emitCxxValueExpr(OS); + OS << ");\n"; + } + } +}; + /// An action taken when all Matcher predicates succeeded for a parent rule. /// /// Typical actions include: @@ -701,6 +858,15 @@ return false; }; + + /// Report the maximum number of temporary operands needed by the rule + /// matcher. + unsigned countTemporaryOperands() const { + return std::accumulate(Matchers.begin(), Matchers.end(), 0, + [](unsigned A, const std::unique_ptr &Matcher) { + return A + Matcher->countTemporaryOperands(); + }); + } }; //===- GlobalISelEmitter class --------------------------------------------===// @@ -719,6 +885,11 @@ /// This is defined using 'GINodeEquiv' in the target description. DenseMap NodeEquivs; + /// Keep track of the equivalence between ComplexPattern's and + /// GIComplexOperandMatcher. Map entries are specified by subclassing + /// GIComplexPatternEquiv. + DenseMap ComplexPatternEquivs; + void gatherNodeEquivs(); const CodeGenInstruction *findNodeEquiv(Record *N); @@ -732,6 +903,14 @@ for (Record *Equiv : RK.getAllDerivedDefinitions("GINodeEquiv")) NodeEquivs[Equiv->getValueAsDef("Node")] = &Target.getInstruction(Equiv->getValueAsDef("I")); + + assert(ComplexPatternEquivs.empty()); + for (Record *Equiv : RK.getAllDerivedDefinitions("GIComplexPatternEquiv")) { + Record *SelDAGEquiv = Equiv->getValueAsDef("SelDAGEquivalent"); + if (!SelDAGEquiv) + continue; + ComplexPatternEquivs[SelDAGEquiv] = Equiv; + } } const CodeGenInstruction *GlobalISelEmitter::findNodeEquiv(Record *N) { @@ -749,6 +928,8 @@ } Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { + unsigned TempOpIdx = 0; + // Keep track of the matchers and actions to emit. RuleMatcher M; M.addAction(P); @@ -856,18 +1037,32 @@ if (auto *ChildDefInit = dyn_cast(SrcChild->getLeafValue())) { auto *ChildRec = ChildDefInit->getDef(); - // Otherwise, we're looking for a bog-standard RegisterClass operand. - if (!ChildRec->isSubClassOf("RegisterClass")) - return failedImport("Src pattern child isn't a RegisterClass"); + if (ChildRec->isSubClassOf("RegisterClass")) { + OM.addPredicate( + Target.getRegisterClass(ChildRec)); + continue; + } - OM.addPredicate( - Target.getRegisterClass(ChildRec)); - continue; + if (ChildRec->isSubClassOf("ComplexPattern")) { + const auto &ComplexPattern = ComplexPatternEquivs.find(ChildRec); + if (ComplexPattern == ComplexPatternEquivs.end()) + return failedImport( + "SelectionDAG ComplexPattern not mapped to GlobalISel"); + + const auto &Predicate = OM.addPredicate( + *ComplexPattern->second, TempOpIdx); + TempOpIdx += Predicate.countTemporaryOperands(); + continue; + } + + return failedImport( + "Src pattern child def is an unsupported tablegen class"); } return failedImport("Src pattern child is an unsupported kind"); } + TempOpIdx = 0; // Finally render the used operands (i.e., the children of the root operator). for (unsigned i = 0, e = Dst->getNumChildren(); i != e; ++i) { auto *DstChild = Dst->getChild(i); @@ -912,11 +1107,30 @@ continue; } + if (ChildRec->isSubClassOf("ComplexPattern")) { + const auto &ComplexPattern = ComplexPatternEquivs.find(ChildRec); + if (ComplexPattern == ComplexPatternEquivs.end()) + return failedImport( + "SelectionDAG ComplexPattern not mapped to GlobalISel"); + + SmallVector RenderedOperands; + for (unsigned I = 0; I < InsnMatcher.getOperand(DstChild->getName()) + .countTemporaryOperands(); + ++I) { + RenderedOperands.push_back(OperandPlaceholder::CreateTemporary(I)); + TempOpIdx++; + } + DstMIBuilder.addRenderer( + *ComplexPattern->second, + RenderedOperands); + continue; + } + return failedImport( "Dst pattern child def is an unsupported tablegen class"); } - return failedImport("Src pattern child is an unsupported kind"); + return failedImport("Dst pattern child is an unsupported kind"); } // We're done with this pattern! It's eligible for GISel emission; return it. @@ -930,11 +1144,6 @@ emitSourceFileHeader(("Global Instruction Selector for the " + Target.getName() + " target").str(), OS); - OS << "bool " << Target.getName() - << "InstructionSelector::selectImpl" - "(MachineInstr &I) const {\n const MachineRegisterInfo &MRI = " - "I.getParent()->getParent()->getRegInfo();\n\n"; - std::vector Rules; // Look through the SelectionDAG patterns we found, possibly emitting some. for (const PatternToMatch &Pat : CGP.ptms()) { @@ -968,12 +1177,34 @@ return false; }); + unsigned MaxTemporaries = 0; + for (const auto &Rule : Rules) + MaxTemporaries = std::max(MaxTemporaries, Rule.countTemporaryOperands()); + + OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_DECL\n"; + for (unsigned I = 0; I < MaxTemporaries; ++I) + OS << " mutable MachineOperand TempOp" << I << ";\n"; + OS << "#endif // ifdef GET_GLOBALISEL_TEMPORARIES_DECL\n\n"; + + OS << "#ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n"; + for (unsigned I = 0; I < MaxTemporaries; ++I) + OS << ", TempOp" << I << "(MachineOperand::CreatePlaceholder())\n"; + OS << "#endif // ifdef GET_GLOBALISEL_TEMPORARIES_INIT\n\n"; + + OS << "#ifdef GET_GLOBALISEL_IMPL\n" + << "bool " << Target.getName() + << "InstructionSelector::selectImpl(MachineInstr &I) const {\n" + << " MachineFunction &MF = *I.getParent()->getParent();\n" + << " const MachineRegisterInfo &MRI = MF.getRegInfo();\n"; + for (const auto &Rule : Rules) { Rule.emit(OS); ++NumPatternEmitted; } - OS << " return false;\n}\n"; + OS << " return false;\n" + << "}\n" + << "#endif // ifdef GET_GLOBALISEL_IMPL\n"; } } // end anonymous namespace