Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.cpp +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -940,6 +940,9 @@ const LLT DstTy = MRI.getType(I.getOperand(0).getReg()); const LLT SrcTy = MRI.getType(I.getOperand(1).getReg()); + if (DstTy == LLT::scalar(32) && SrcTy == LLT::scalar(64)) + llvm_unreachable("Tablegen code can import this"); + const unsigned DstReg = I.getOperand(0).getReg(); const unsigned SrcReg = I.getOperand(1).getReg(); Index: test/CodeGen/AArch64/GlobalISel/select-trunc.mir =================================================================== --- test/CodeGen/AArch64/GlobalISel/select-trunc.mir +++ test/CodeGen/AArch64/GlobalISel/select-trunc.mir @@ -15,8 +15,8 @@ regBankSelected: true # CHECK: registers: -# CHECK-NEXT: - { id: 0, class: gpr64 } -# CHECK-NEXT: - { id: 1, class: gpr32 } +# CHECK-NEXT: - { id: 0, class: gpr64all } +# CHECK-NEXT: - { id: 1, class: gpr32all } registers: - { id: 0, class: gpr } - { id: 1, class: gpr } Index: utils/TableGen/CodeGenRegisters.h =================================================================== --- utils/TableGen/CodeGenRegisters.h +++ utils/TableGen/CodeGenRegisters.h @@ -329,6 +329,9 @@ const std::string &getName() const { return Name; } std::string getQualifiedName() const; ArrayRef getValueTypes() const {return VTs;} + bool hasValueType(MVT::SimpleValueType VT) const { + return std::find(VTs.begin(), VTs.end(), VT) != VTs.end(); + } unsigned getNumValueTypes() const { return VTs.size(); } MVT::SimpleValueType getValueTypeNum(unsigned VTNum) const { @@ -370,7 +373,7 @@ void getSuperRegClasses(const CodeGenSubRegIndex *SubIdx, BitVector &Out) const; - // addSuperRegClass - Add a class containing only SudIdx super-registers. + // addSuperRegClass - Add a class containing only SubIdx super-registers. void addSuperRegClass(CodeGenSubRegIndex *SubIdx, CodeGenRegisterClass *SuperRC) { SuperRegClasses[SubIdx].insert(SuperRC); Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -806,7 +806,13 @@ //===- Actions ------------------------------------------------------------===// class OperandRenderer { public: - enum RendererKind { OR_Copy, OR_Imm, OR_Register, OR_ComplexPattern }; + enum RendererKind { + OR_Copy, + OR_CopySubReg, + OR_Imm, + OR_Register, + OR_ComplexPattern + }; protected: RendererKind Kind; @@ -851,6 +857,42 @@ } }; +/// 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. +class CopySubRegRenderer : public OperandRenderer { +protected: + /// The matcher for the instruction that this operand is copied from. + /// This provides the facility for looking up an a operand by it's name so + /// that it can be used as a source for the instruction being built. + const InstructionMatcher &Matched; + /// The name of the operand. + const StringRef SymbolicName; + // The subregister to extract. + const CodeGenSubRegIndex *SubReg; + +public: + CopySubRegRenderer(const InstructionMatcher &Matched, StringRef SymbolicName, + const CodeGenSubRegIndex *SubReg) + : OperandRenderer(OR_CopySubReg), Matched(Matched), + SymbolicName(SymbolicName), SubReg(SubReg) {} + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_CopySubReg; + } + + const StringRef getSymbolicName() const { return SymbolicName; } + + void emitCxxRenderStmts(raw_ostream &OS, RuleMatcher &Rule) const override { + const OperandMatcher &Operand = Matched.getOperand(SymbolicName); + StringRef InsnVarName = + Rule.getInsnVarName(Operand.getInstructionMatcher()); + std::string OperandExpr = Operand.getOperandExpr(InsnVarName); + OS << " MIB.addReg(" << OperandExpr << ".getReg() /*" << SymbolicName + << "*/, 0, " << SubReg->EnumValue << ");\n"; + } +}; + /// Adds a specific physical register to the instruction being built. /// This is typically useful for WZR/XZR on AArch64. class AddRegisterRenderer : public OperandRenderer { @@ -1257,6 +1299,7 @@ const RecordKeeper &RK; const CodeGenDAGPatterns CGP; const CodeGenTarget &Target; + CodeGenRegBank CGRegs; /// Keep track of the equivalence between SDNodes and Instruction. /// This is defined using 'GINodeEquiv' in the target description. @@ -1280,9 +1323,9 @@ Error importChildMatcher(InstructionMatcher &InsnMatcher, TreePatternNode *SrcChild, unsigned OpIdx, unsigned &TempOpIdx) const; - Expected createAndImportInstructionRenderer( - RuleMatcher &M, const TreePatternNode *Dst, - const InstructionMatcher &InsnMatcher) const; + Expected + createAndImportInstructionRenderer(RuleMatcher &M, const TreePatternNode *Dst, + const InstructionMatcher &InsnMatcher); Error importExplicitUseRenderer(BuildMIAction &DstMIBuilder, TreePatternNode *DstChild, const InstructionMatcher &InsnMatcher) const; @@ -1319,7 +1362,7 @@ } GlobalISelEmitter::GlobalISelEmitter(RecordKeeper &RK) - : RK(RK), CGP(RK), Target(CGP.getTargetInfo()) {} + : RK(RK), CGP(RK), Target(CGP.getTargetInfo()), CGRegs(RK) {} //===- Emitter ------------------------------------------------------------===// @@ -1532,7 +1575,7 @@ Expected GlobalISelEmitter::createAndImportInstructionRenderer( RuleMatcher &M, const TreePatternNode *Dst, - const InstructionMatcher &InsnMatcher) const { + const InstructionMatcher &InsnMatcher) { Record *DstOp = Dst->getOperator(); if (!DstOp->isSubClassOf("Instruction")) { if (DstOp->isSubClassOf("ValueType")) @@ -1544,13 +1587,17 @@ unsigned DstINumUses = DstI->Operands.size() - DstI->Operands.NumDefs; unsigned ExpectedDstINumUses = Dst->getNumChildren(); + bool IsExtractSubReg = false; // COPY_TO_REGCLASS is just a copy with a ConstrainOperandToRegClassAction - // attached. + // attached. Similarly for EXTRACT_SUBREG except that's a subregister copy. if (DstI->TheDef->getName() == "COPY_TO_REGCLASS") { DstI = &Target.getInstruction(RK.getDef("COPY")); DstINumUses--; // Ignore the class constraint. ExpectedDstINumUses--; + } else if (DstI->TheDef->getName() == "EXTRACT_SUBREG") { + DstI = &Target.getInstruction(RK.getDef("COPY")); + IsExtractSubReg = true; } auto &DstMIBuilder = M.addAction("NewI", DstI, InsnMatcher); @@ -1561,6 +1608,21 @@ DstMIBuilder.addRenderer(InsnMatcher, DstIOperand.Name); } + // EXTRACT_SUBREG needs to use a subregister COPY. + if (IsExtractSubReg) { + if (!Dst->getChild(0)->isLeaf()) + return failedImport("EXTRACT_SUBREG child #1 is not a leaf"); + + if (DefInit *SubRegInit = dyn_cast(Dst->getChild(1)->getLeafValue())) { + CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); + DstMIBuilder.addRenderer( + InsnMatcher, Dst->getChild(0)->getName(), SubIdx); + return DstMIBuilder; + } + + return failedImport("EXTRACT_SUBREG child #1 is not a subreg index"); + } + // Render the explicit uses. unsigned Child = 0; unsigned NumDefaultOps = 0; @@ -1683,6 +1745,16 @@ if (DstIOpRec == nullptr) return failedImport( "COPY_TO_REGCLASS operand #1 isn't a register class"); + } else if (DstI.TheDef->getName() == "EXTRACT_SUBREG") { + if (!Dst->getChild(0)->isLeaf()) + return failedImport("EXTRACT_SUBREG operand #0 isn't a leaf"); + + // We can assume that a subregister is in the same bank as it's super register. + DstIOpRec = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); + + if (DstIOpRec == nullptr) + return failedImport( + "EXTRACT_SUBREG operand #0 isn't a register class"); } else if (DstIOpRec->isSubClassOf("RegisterOperand")) DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); else if (!DstIOpRec->isSubClassOf("RegisterClass")) @@ -1719,6 +1791,44 @@ M.addAction( "NewI", 0, Target.getRegisterClass(DstIOpRec)); + } else if (DstI.TheDef->getName() == "EXTRACT_SUBREG") { + // EXTRACT_SUBREG selects into a subregister COPY but unlike most + // instructions, the result register class is controlled by the + // subregisters of the operand. As a result, we must constrain the result + // class rather than check that it's already the right one. + if (!Dst->getChild(0)->isLeaf()) + return failedImport("EXTRACT_SUBREG child #1 is not a leaf"); + + if (DefInit *SubRegInit = + dyn_cast(Dst->getChild(1)->getLeafValue())) { + // Constrain the result to the same register bank as the operand. + Record *DstIOpRec = + getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); + + if (DstIOpRec == nullptr) + return failedImport( + "COPY_TO_REGCLASS operand #1 isn't a register class"); + + // It would be nice to leave this constraint implicit but we're required + // to pick a register class so constrain the result to a register class + // that can hold the correct MVT. + // + // FIXME: This may introduce an extra copy if the chosen class doesn't + // actually contain the subregisters. + assert(Src->getExtTypes().size() == 1); + for (CodeGenRegisterClass &PossibleSubClass : CGRegs.getRegClasses()) { + if (PossibleSubClass.hasValueType( + Src->getExtTypes().front().getConcrete())) { + M.addAction("NewI", 0, + PossibleSubClass); + break; + } + } + + return M; + } + + return failedImport("EXTRACT_SUBREG child #1 is not a subreg index"); } else M.addAction("NewI");