From 9cbe7c7f9348762c59c42fcad54df742180786bd Mon Sep 17 00:00:00 2001 From: Daniel Sanders Date: Wed, 1 Nov 2017 19:57:57 +0000 Subject: [PATCH] [globalisel][tablegen] Add support for multi-insn emission The importer will now accept nested instructions in the result pattern such as (ADDWrr $a, (SUBWrr $b, $c)). This is only valid when the nested instruction def's a single vreg and the parent instruction consumes a single vreg where a nested instruction is specified. The importer will automatically create a vreg to connect the two using the type information from the pattern. This vreg will be constrained to the register classes given in the instruction definitions*. * REG_SEQUENCE is explicitly rejected because of this. The definition doesn't constrain to a register class and it therefore needs special handling. llvm-svn: 317117 --- .../llvm/CodeGen/GlobalISel/InstructionSelector.h | 10 ++ .../CodeGen/GlobalISel/InstructionSelectorImpl.h | 52 ++++++-- .../GlobalISel/select-bitcast-bigendian.mir | 19 +++ .../GlobalISel/select-intrinsic-crypto-aesmc.mir | 26 ++++ llvm/test/TableGen/GlobalISelEmitter.td | 23 ++-- llvm/utils/TableGen/GlobalISelEmitter.cpp | 138 +++++++++++++++++++-- 6 files changed, 238 insertions(+), 30 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/select-bitcast-bigendian.mir create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/select-intrinsic-crypto-aesmc.mir diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h index 0a3f133..4935e90 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -16,6 +16,7 @@ #ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H #define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Optional.h" #include @@ -212,6 +213,10 @@ enum { /// - InsnID - Instruction ID to modify /// - RegNum - The register to add GIR_AddRegister, + /// Add a a temporary register to the specified instruction + /// - InsnID - Instruction ID to modify + /// - TempRegID - The temporary register ID to add + GIR_AddTempRegister, /// Add an immediate to the specified instruction /// - InsnID - Instruction ID to modify /// - Imm - The immediate to add @@ -250,6 +255,10 @@ enum { /// Erase from parent. /// - InsnID - Instruction ID to erase GIR_EraseFromParent, + /// Create a new temporary register that's not constrained. + /// - TempRegID - The temporary register ID to initialize. + /// - Expected type + GIR_MakeTempReg, /// A successful emission GIR_Done, @@ -291,6 +300,7 @@ protected: struct MatcherState { std::vector Renderers; RecordedMIVector MIs; + DenseMap TempRegisters; MatcherState(unsigned MaxRenderers); }; diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h index 647aa32..ae9396d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -434,12 +434,13 @@ bool InstructionSelector::executeMatchTable( case GIR_MutateOpcode: { int64_t OldInsnID = MatchTable[CurrentIdx++]; - int64_t NewInsnID = MatchTable[CurrentIdx++]; + uint64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t NewOpcode = MatchTable[CurrentIdx++]; - assert((size_t)NewInsnID == OutMIs.size() && - "Expected to store MIs in order"); - OutMIs.push_back(MachineInstrBuilder(*State.MIs[OldInsnID]->getMF(), - State.MIs[OldInsnID])); + if (NewInsnID >= OutMIs.size()) + OutMIs.resize(NewInsnID + 1); + + OutMIs[NewInsnID] = MachineInstrBuilder(*State.MIs[OldInsnID]->getMF(), + State.MIs[OldInsnID]); OutMIs[NewInsnID]->setDesc(TII.get(NewOpcode)); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_MutateOpcode(OutMIs[" @@ -449,16 +450,16 @@ bool InstructionSelector::executeMatchTable( } case GIR_BuildMI: { - int64_t InsnID = MatchTable[CurrentIdx++]; + uint64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t Opcode = MatchTable[CurrentIdx++]; - assert((size_t)InsnID == OutMIs.size() && - "Expected to store MIs in order"); - (void)InsnID; - OutMIs.push_back(BuildMI(*State.MIs[0]->getParent(), State.MIs[0], - State.MIs[0]->getDebugLoc(), TII.get(Opcode))); + if (NewInsnID >= OutMIs.size()) + OutMIs.resize(NewInsnID + 1); + + OutMIs[NewInsnID] = BuildMI(*State.MIs[0]->getParent(), State.MIs[0], + State.MIs[0]->getDebugLoc(), TII.get(Opcode)); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), - dbgs() << CurrentIdx << ": GIR_BuildMI(OutMIs[" << InsnID - << "], " << Opcode << ")\n"); + dbgs() << CurrentIdx << ": GIR_BuildMI(OutMIs[" + << NewInsnID << "], " << Opcode << ")\n"); break; } @@ -541,6 +542,19 @@ bool InstructionSelector::executeMatchTable( break; } + case GIR_AddTempRegister: { + int64_t InsnID = MatchTable[CurrentIdx++]; + int64_t TempRegID = MatchTable[CurrentIdx++]; + uint64_t TempRegFlags = MatchTable[CurrentIdx++]; + assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); + OutMIs[InsnID].addReg(State.TempRegisters[TempRegID], TempRegFlags); + DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), + dbgs() << CurrentIdx << ": GIR_AddTempRegister(OutMIs[" + << InsnID << "], TempRegisters[" << TempRegID + << "], " << TempRegFlags << ")\n"); + break; + } + case GIR_AddImm: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Imm = MatchTable[CurrentIdx++]; @@ -651,6 +665,18 @@ bool InstructionSelector::executeMatchTable( break; } + case GIR_MakeTempReg: { + int64_t TempRegID = MatchTable[CurrentIdx++]; + int64_t TypeID = MatchTable[CurrentIdx++]; + + State.TempRegisters[TempRegID] = + MRI.createGenericVirtualRegister(MatcherInfo.TypeObjects[TypeID]); + DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), + dbgs() << CurrentIdx << ": TempRegs[" << TempRegID + << "] = GIR_MakeTempReg(" << TypeID << ")\n"); + break; + } + case GIR_Done: DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_Done"); diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-bitcast-bigendian.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-bitcast-bigendian.mir new file mode 100644 index 0000000..35d39c8 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-bitcast-bigendian.mir @@ -0,0 +1,19 @@ +# RUN: llc -O0 -mtriple=arm64eb-- -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s +--- +name: bitcast_v2f32_to_s64 +legalized: true +regBankSelected: true + +body: | + bb.0: + liveins: %x0 + + ; CHECK-LABEL: name: bitcast_v2f32_to_s64 + ; CHECK: [[COPY:%[0-9]+]]:fpr64 = COPY %x0 + ; CHECK: [[COPY1:%[0-9]+]]:fpr64 = COPY [[COPY]] + ; CHECK: [[REV:%[0-9]+]]:fpr64 = REV64v2i32 [[COPY1]] + ; CHECK: %x0 = COPY [[REV]] + %0:fpr(<2 x s32>) = COPY %x0 + %1:fpr(s64) = G_BITCAST %0 + %x0 = COPY %1(s64) +... diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-intrinsic-crypto-aesmc.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-intrinsic-crypto-aesmc.mir new file mode 100644 index 0000000..fe457b8 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-intrinsic-crypto-aesmc.mir @@ -0,0 +1,26 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=aarch64-- -mattr=+fuse-aes -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s + +--- +# Check that we select the aarch64_crypto_aesmc and aarch64_crypto_aese +# intrinsics into an ARSMCrrTied and AESErr instruction sequence. +name: aesmc_aese +legalized: true +regBankSelected: true + +body: | + bb.0: + liveins: %q0, %q1 + + ; CHECK-LABEL: name: aesmc_aese + ; CHECK: [[COPY:%[0-9]+]]:fpr128 = COPY %q0 + ; CHECK: [[COPY1:%[0-9]+]]:fpr128 = COPY %q1 + ; CHECK: [[T0:%[0-9]+]]:fpr128 = AESErr [[COPY]], [[COPY1]] + ; CHECK: [[T1:%[0-9]+]]:fpr128 = AESMCrrTied [[T0]] + ; CHECK: %q0 = COPY [[T1]] + %0:fpr(<16 x s8>) = COPY %q0 + %1:fpr(<16 x s8>) = COPY %q1 + %2:fpr(<16 x s8>) = G_INTRINSIC intrinsic(@llvm.aarch64.crypto.aese), %0, %1 + %3:fpr(<16 x s8>) = G_INTRINSIC intrinsic(@llvm.aarch64.crypto.aesmc), %2 + %q0 = COPY %3(<16 x s8>) +... diff --git a/llvm/test/TableGen/GlobalISelEmitter.td b/llvm/test/TableGen/GlobalISelEmitter.td index 9e2ca1b..dd80640 100644 --- a/llvm/test/TableGen/GlobalISelEmitter.td +++ b/llvm/test/TableGen/GlobalISelEmitter.td @@ -192,13 +192,16 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; } // CHECK-NEXT: // Label 0: @[[LABEL]] def INSN3 : I<(outs GPR32:$dst), - (ins GPR32Op:$src1, GPR32:$src2a, GPR32:$src2b, GPR32:$src3, complex:$src4, i32imm:$src5a, i32imm:$src5b), []>; + (ins GPR32Op:$src1, GPR32:$src2a, GPR32:$src2b, GPR32:$scr), []>; +def INSN4 : I<(outs GPR32:$scr), + (ins GPR32:$src3, complex:$src4, i32imm:$src5a, i32imm:$src5b), []>; def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b), (select GPR32:$src3, complex:$src4, (complex i32imm:$src5a, i32imm:$src5b))), - (INSN3 GPR32:$src1, GPR32:$src2b, GPR32:$src2a, GPR32:$src3, - complex:$src4, i32imm:$src5a, i32imm:$src5b)>; + (INSN3 GPR32:$src1, GPR32:$src2b, GPR32:$src2a, + (INSN4 GPR32:$src3, complex:$src4, i32imm:$src5a, + i32imm:$src5b))>; //===- Test a pattern with multiple ComplexPattern operands. --------------===// // @@ -232,16 +235,20 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b), // CHECK-NEXT: GIM_CheckType, /*MI*/1, /*Op*/3, /*Type*/GILLT_s32, // CHECK-NEXT: GIM_CheckComplexPattern, /*MI*/1, /*Op*/3, /*Renderer*/2, GICP_gi_complex, // CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1, -// CHECK-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, (complex_rr:{ *:[i32] } GPR32:{ *:[i32] }:$src2a, GPR32:{ *:[i32] }:$src2b), (select:{ *:[i32] } GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, (complex:{ *:[i32] } i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b))) => (INSN3:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2b, GPR32:{ *:[i32] }:$src2a, GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b) +// CHECK-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, (complex_rr:{ *:[i32] } GPR32:{ *:[i32] }:$src2a, GPR32:{ *:[i32] }:$src2b), (select:{ *:[i32] } GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, (complex:{ *:[i32] } i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b))) => (INSN3:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2b, GPR32:{ *:[i32] }:$src2a, (INSN4:{ *:[i32] } GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b)) +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/MyTarget::INSN4, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/1, /*OpIdx*/1, // src3 +// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/1, /*RendererID*/1, +// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/1, /*RendererID*/2, /*SubOperand*/0, // src5a +// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/1, /*RendererID*/2, /*SubOperand*/1, // src5b // CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN3, // CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst // CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1 // CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/1, // src2b // CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/0, // src2a -// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src3 -// CHECK-NEXT: GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/1, -// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/2, /*SubOperand*/0, // src5a -// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/2, /*SubOperand*/1, // src5b +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0, // CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, // CHECK-NEXT: GIR_Done, diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index e79bca7..fed8ae5 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -555,6 +555,9 @@ protected: /// ID for the next output instruction allocated with allocateOutputInsnID() unsigned NextOutputInsnID; + /// ID for the next temporary register ID allocated with allocateTempRegID() + unsigned NextTempRegID; + std::vector RequiredFeatures; ArrayRef SrcLoc; @@ -570,7 +573,7 @@ public: RuleMatcher(ArrayRef SrcLoc) : Matchers(), Actions(), InsnVariableIDs(), MutatableInsns(), DefinedOperands(), NextInsnVarID(0), NextOutputInsnID(0), - SrcLoc(SrcLoc), ComplexSubOperands() {} + NextTempRegID(0), SrcLoc(SrcLoc), ComplexSubOperands() {} RuleMatcher(RuleMatcher &&Other) = default; RuleMatcher &operator=(RuleMatcher &&Other) = default; @@ -658,6 +661,7 @@ public: InstructionMatcher &insnmatcher_front() const { return *Matchers.front(); } unsigned allocateOutputInsnID() { return NextOutputInsnID++; } + unsigned allocateTempRegID() { return NextTempRegID++; } }; using action_iterator = RuleMatcher::action_iterator; @@ -1476,6 +1480,7 @@ public: OR_CopyFConstantAsFPImm, OR_Imm, OR_Register, + OR_TempRegister, OR_ComplexPattern }; @@ -1692,6 +1697,37 @@ public: } }; +/// Adds a specific temporary virtual register to the instruction being built. +/// This is used to chain instructions together when emitting multiple +/// instructions. +class TempRegRenderer : public OperandRenderer { +protected: + unsigned InsnID; + unsigned TempRegID; + bool IsDef; + +public: + TempRegRenderer(unsigned InsnID, unsigned TempRegID, bool IsDef = false) + : OperandRenderer(OR_Register), InsnID(InsnID), TempRegID(TempRegID), + IsDef(IsDef) {} + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_TempRegister; + } + + void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + Table << MatchTable::Opcode("GIR_AddTempRegister") + << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) + << MatchTable::Comment("TempRegID") << MatchTable::IntValue(TempRegID) + << MatchTable::Comment("TempRegFlags"); + if (IsDef) + Table << MatchTable::NamedValue("RegState::Define"); + else + Table << MatchTable::IntValue(0); + Table << MatchTable::LineBreak; + } +}; + /// Adds a specific immediate to the instruction being built. class ImmRenderer : public OperandRenderer { protected: @@ -1906,9 +1942,13 @@ public: << MatchTable::LineBreak; } - Table << MatchTable::Opcode("GIR_EraseFromParent") - << MatchTable::Comment("InsnID") << MatchTable::IntValue(0) - << MatchTable::LineBreak; + // FIXME: This is a hack but it's sufficient for ISel. We'll need to do + // better for combines. Particularly when there are multiple match + // roots. + if (InsnID == 0) + Table << MatchTable::Opcode("GIR_EraseFromParent") + << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) + << MatchTable::LineBreak; } }; @@ -1948,6 +1988,26 @@ public: } }; +/// Generates code to create a temporary register which can be used to chain +/// instructions together. +class MakeTempRegisterAction : public MatchAction { +private: + LLTCodeGen Ty; + unsigned TempRegID; + +public: + MakeTempRegisterAction(const LLTCodeGen &Ty, unsigned TempRegID) + : Ty(Ty), TempRegID(TempRegID) {} + + void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + Table << MatchTable::Opcode("GIR_MakeTempReg") + << MatchTable::Comment("TempRegID") << MatchTable::IntValue(TempRegID) + << MatchTable::Comment("TypeID") + << MatchTable::NamedValue(Ty.getCxxEnumValue()) + << MatchTable::LineBreak; + } +}; + InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) { Matchers.emplace_back(new InstructionMatcher(*this, SymbolicName)); MutatableInsns.insert(Matchers.back().get()); @@ -1974,6 +2034,7 @@ Kind &RuleMatcher::addAction(Args &&... args) { Actions.emplace_back(llvm::make_unique(std::forward(args)...)); return *static_cast(Actions.back().get()); } + // Emplaces an action of the specified Kind before the given insertion point. // // Returns an iterator pointing at the newly created instruction. @@ -1984,7 +2045,8 @@ Kind &RuleMatcher::addAction(Args &&... args) { template action_iterator RuleMatcher::insertAction(action_iterator InsertPt, Args &&... args) { - return Actions.insert(InsertPt, llvm::make_unique(std::forward(args)...)); + return Actions.emplace(InsertPt, + llvm::make_unique(std::forward(args)...)); } unsigned @@ -2264,6 +2326,9 @@ private: Expected createAndImportInstructionRenderer(RuleMatcher &M, const TreePatternNode *Dst); + Expected createAndImportSubInstructionRenderer( + action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst, + unsigned TempReg); Expected createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst); @@ -2275,7 +2340,7 @@ private: Expected importExplicitUseRenderer(action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, - TreePatternNode *DstChild) const; + TreePatternNode *DstChild); Error importDefaultOperandRenderers(BuildMIAction &DstMIBuilder, DagInit *DefaultOps) const; Error @@ -2607,7 +2672,7 @@ Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule, Expected GlobalISelEmitter::importExplicitUseRenderer( action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, - TreePatternNode *DstChild) const { + TreePatternNode *DstChild) { if (DstChild->getTransformFn() != nullptr) { return failedImport("Dst pattern child has transform fn " + DstChild->getTransformFn()->getName()); @@ -2645,6 +2710,30 @@ Expected GlobalISelEmitter::importExplicitUseRenderer( return InsertPt; } + if (DstChild->getOperator()->isSubClassOf("Instruction")) { + ArrayRef ChildTypes = DstChild->getExtTypes(); + if (ChildTypes.size() != 1) + return failedImport("Dst pattern child has multiple results"); + + Optional OpTyOrNone = None; + if (ChildTypes.front().isMachineValueType()) + OpTyOrNone = + MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); + if (!OpTyOrNone) + return failedImport("Dst operand has an unsupported type"); + + unsigned TempRegID = Rule.allocateTempRegID(); + InsertPt = Rule.insertAction( + InsertPt, OpTyOrNone.getValue(), TempRegID); + DstMIBuilder.addRenderer(TempRegID); + + auto InsertPtOrError = createAndImportSubInstructionRenderer( + ++InsertPt, Rule, DstChild, TempRegID); + if (auto Error = InsertPtOrError.takeError()) + return std::move(Error); + return InsertPtOrError.get(); + } + return failedImport("Dst pattern child isn't a leaf node or an MBB" + llvm::to_string(*DstChild)); } @@ -2723,6 +2812,31 @@ Expected GlobalISelEmitter::createAndImportInstructionRenderer( return DstMIBuilder; } +Expected +GlobalISelEmitter::createAndImportSubInstructionRenderer( + action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst, + unsigned TempRegID) { + auto InsertPtOrError = createInstructionRenderer(InsertPt, M, Dst); + + // TODO: Assert there's exactly one result. + + if (auto Error = InsertPtOrError.takeError()) + return std::move(Error); + InsertPt = InsertPtOrError.get(); + + BuildMIAction &DstMIBuilder = + *static_cast(InsertPtOrError.get()->get()); + + // Assign the result to TempReg. + DstMIBuilder.addRenderer(TempRegID, true); + + InsertPtOrError = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst); + if (auto Error = InsertPtOrError.takeError()) + return std::move(Error); + + return InsertPtOrError.get(); +} + Expected GlobalISelEmitter::createInstructionRenderer( action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst) { Record *DstOp = Dst->getOperator(); @@ -2740,6 +2854,8 @@ Expected GlobalISelEmitter::createInstructionRenderer( DstI = &Target.getInstruction(RK.getDef("COPY")); else if (DstI->TheDef->getName() == "EXTRACT_SUBREG") DstI = &Target.getInstruction(RK.getDef("COPY")); + else if (DstI->TheDef->getName() == "REG_SEQUENCE") + return failedImport("Unable to emit REG_SEQUENCE"); return M.insertAction(InsertPt, M.allocateOutputInsnID(), DstI); @@ -2767,8 +2883,12 @@ Expected GlobalISelEmitter::importExplicitUseRenderers( if (DefInit *SubRegInit = dyn_cast(Dst->getChild(1)->getLeafValue())) { - CodeGenRegisterClass *RC = CGRegs.getRegClass( - getInitValueAsRegClass(Dst->getChild(0)->getLeafValue())); + Record *RCDef = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); + if (!RCDef) + return failedImport("EXTRACT_SUBREG child #0 could not " + "be coerced to a register class"); + + CodeGenRegisterClass *RC = CGRegs.getRegClass(RCDef); CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); const auto &SrcRCDstRCPair = -- 2.7.4