Index: llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -29,6 +29,7 @@ class MachineRegisterInfo; class RegisterBankInfo; class TargetInstrInfo; +class TargetRegisterClass; class TargetRegisterInfo; /// Container class for CodeGen predicate results. @@ -79,6 +80,16 @@ InstructionSelector(); + /// Constrain a register operand of an instruction \p I to a specified + /// register class. This could involve inserting COPYs before (for uses) or + /// after (for defs) and may replace the operand of \p I. + /// \returns whether operand regclass constraining succeeded. + bool constrainOperandRegToRegClass(MachineInstr &I, unsigned OpIdx, + const TargetRegisterClass &RC, + const TargetInstrInfo &TII, + const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) const; + /// Mutate the newly-selected instruction \p I to constrain its (possibly /// generic) virtual register operands to the instruction's register class. /// This could involve inserting COPYs before (for uses) or after (for defs). Index: llvm/trunk/include/llvm/CodeGen/GlobalISel/Utils.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GlobalISel/Utils.h +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/Utils.h @@ -29,13 +29,26 @@ class TargetInstrInfo; class TargetPassConfig; class TargetRegisterInfo; +class TargetRegisterClass; class Twine; class ConstantFP; +/// Try to constrain Reg to the specified register class. If this fails, +/// create a new virtual register in the correct class and insert a COPY before +/// \p InsertPt. The debug location of \p InsertPt is used for the new copy. +/// +/// \return The virtual register constrained to the right register class. +unsigned constrainRegToClass(MachineRegisterInfo &MRI, + const TargetInstrInfo &TII, + const RegisterBankInfo &RBI, + MachineInstr &InsertPt, unsigned Reg, + const TargetRegisterClass &RegClass); + /// Try to constrain Reg so that it is usable by argument OpIdx of the /// provided MCInstrDesc \p II. If this fails, create a new virtual /// register in the correct class and insert a COPY before \p InsertPt. -/// The debug location of \p InsertPt is used for the new copy. +/// This is equivalent to constrainRegToClass() with RegClass obtained from the +/// MCInstrDesc. The debug location of \p InsertPt is used for the new copy. /// /// \return The virtual register constrained to the right register class. unsigned constrainOperandRegClass(const MachineFunction &MF, Index: llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelector.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelector.cpp +++ llvm/trunk/lib/CodeGen/GlobalISel/InstructionSelector.cpp @@ -25,6 +25,18 @@ InstructionSelector::InstructionSelector() {} +bool InstructionSelector::constrainOperandRegToRegClass( + MachineInstr &I, unsigned OpIdx, const TargetRegisterClass &RC, + const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) const { + MachineBasicBlock &MBB = *I.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + return llvm::constrainRegToClass(MRI, TII, RBI, I, + I.getOperand(OpIdx).getReg(), RC); +} + bool InstructionSelector::constrainSelectedInstRegOperands( MachineInstr &I, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI) const { Index: llvm/trunk/lib/CodeGen/GlobalISel/Utils.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GlobalISel/Utils.cpp +++ llvm/trunk/lib/CodeGen/GlobalISel/Utils.cpp @@ -26,6 +26,23 @@ using namespace llvm; +unsigned llvm::constrainRegToClass(MachineRegisterInfo &MRI, + const TargetInstrInfo &TII, + const RegisterBankInfo &RBI, + MachineInstr &InsertPt, unsigned Reg, + const TargetRegisterClass &RegClass) { + if (!RBI.constrainGenericRegister(Reg, RegClass, MRI)) { + unsigned NewReg = MRI.createVirtualRegister(&RegClass); + BuildMI(*InsertPt.getParent(), InsertPt, InsertPt.getDebugLoc(), + TII.get(TargetOpcode::COPY), NewReg) + .addReg(Reg); + return NewReg; + } + + return Reg; +} + + unsigned llvm::constrainOperandRegClass( const MachineFunction &MF, const TargetRegisterInfo &TRI, MachineRegisterInfo &MRI, const TargetInstrInfo &TII, @@ -36,16 +53,7 @@ "PhysReg not implemented"); const TargetRegisterClass *RegClass = TII.getRegClass(II, OpIdx, &TRI, MF); - - if (!RBI.constrainGenericRegister(Reg, *RegClass, MRI)) { - unsigned NewReg = MRI.createVirtualRegister(RegClass); - BuildMI(*InsertPt.getParent(), InsertPt, InsertPt.getDebugLoc(), - TII.get(TargetOpcode::COPY), NewReg) - .addReg(Reg); - return NewReg; - } - - return Reg; + return constrainRegToClass(MRI, TII, RBI, InsertPt, Reg, *RegClass); } bool llvm::isTriviallyDead(const MachineInstr &MI, Index: llvm/trunk/test/CodeGen/AArch64/GlobalISel/select-bitcast.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/GlobalISel/select-bitcast.mir +++ llvm/trunk/test/CodeGen/AArch64/GlobalISel/select-bitcast.mir @@ -95,7 +95,7 @@ # CHECK: registers: # CHECK-NEXT: - { id: 0, class: fpr32, preferred-register: '' } -# CHECK-NEXT: - { id: 1, class: gpr32all, preferred-register: '' } +# CHECK-NEXT: - { id: 1, class: gpr32, preferred-register: '' } registers: - { id: 0, class: fpr } - { id: 1, class: gpr } @@ -194,7 +194,7 @@ # CHECK: registers: # CHECK-NEXT: - { id: 0, class: fpr64, preferred-register: '' } -# CHECK-NEXT: - { id: 1, class: gpr64all, preferred-register: '' } +# CHECK-NEXT: - { id: 1, class: gpr64, preferred-register: '' } registers: - { id: 0, class: fpr } - { id: 1, class: gpr } Index: llvm/trunk/test/TableGen/GlobalISelEmitter.td =================================================================== --- llvm/trunk/test/TableGen/GlobalISelEmitter.td +++ llvm/trunk/test/TableGen/GlobalISelEmitter.td @@ -10,6 +10,8 @@ def R0 : Register<"r0"> { let Namespace = "MyTarget"; } def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>; def GPR32Op : RegisterOperand; +def F0 : Register<"f0"> { let Namespace = "MyTarget"; } +def FPR32 : RegisterClass<"MyTarget", [f32], 32, (add F0)>; class I Pat> : Instruction { @@ -462,6 +464,30 @@ def ORN : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>; def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>; +//===- Test a COPY_TO_REGCLASS --------------------------------------------===// +// + +// CHECK-LABEL: if ([&]() { +// CHECK-NEXT: MachineInstr &MI0 = I; +// CHECK-NEXT: if (MI0.getNumOperands() < 2) +// CHECK-NEXT: return false; +// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_BITCAST) && +// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* src1 */ (MRI.getType(MI0.getOperand(1).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::FPR32RegClass) == RBI.getRegBank(MI0.getOperand(1).getReg(), MRI, TRI)))))) +// CHECK-NEXT: // (bitconvert:i32 FPR32:f32:$src1) => (COPY_TO_REGCLASS:i32 FPR32:f32:$src1, GPR32:i32) +// CHECK-NEXT: I.setDesc(TII.get(TargetOpcode::COPY)); +// CHECK-NEXT: MachineInstr &NewI = I; +// CHECK-NEXT: constrainOperandRegToRegClass(NewI, 0, MyTarget::GPR32RegClass, TII, TRI, RBI); +// CHECK-NEXT: return true; +// CHECK-NEXT: } +// CHECK-NEXT: return false; +// CHECK-NEXT: }()) { return true; } + +def : Pat<(i32 (bitconvert FPR32:$src1)), + (COPY_TO_REGCLASS FPR32:$src1, GPR32)>; + //===- Test a simple pattern with just a leaf immediate. ------------------===// // CHECK-LABEL: if ([&]() { Index: llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp @@ -158,6 +158,16 @@ return failedImport(Explanation); } +static Record *getInitValueAsRegClass(Init *V) { + if (DefInit *VDefInit = dyn_cast(V)) { + if (VDefInit->getDef()->isSubClassOf("RegisterOperand")) + return VDefInit->getDef()->getValueAsDef("RegClass"); + if (VDefInit->getDef()->isSubClassOf("RegisterClass")) + return VDefInit->getDef(); + } + return nullptr; +} + //===- Matchers -----------------------------------------------------------===// class OperandMatcher; @@ -973,6 +983,7 @@ /// into the desired instruction when this is possible. class BuildMIAction : public MatchAction { private: + std::string Name; const CodeGenInstruction *I; const InstructionMatcher &Matched; std::vector> OperandRenderers; @@ -996,8 +1007,9 @@ } public: - BuildMIAction(const CodeGenInstruction *I, const InstructionMatcher &Matched) - : I(I), Matched(Matched) {} + BuildMIAction(const StringRef Name, const CodeGenInstruction *I, + const InstructionMatcher &Matched) + : Name(Name), I(I), Matched(Matched) {} template Kind &addRenderer(Args&&... args) { @@ -1032,7 +1044,7 @@ } } - OS << " MachineInstr &NewI = " << RecycleVarName << ";\n"; + OS << " MachineInstr &" << Name << " = " << RecycleVarName << ";\n"; return; } @@ -1050,7 +1062,40 @@ OS << " for (const auto &MMO : FromMI->memoperands())\n"; OS << " MIB.addMemOperand(MMO);\n"; OS << " " << RecycleVarName << ".eraseFromParent();\n"; - OS << " MachineInstr &NewI = *MIB;\n"; + OS << " MachineInstr &" << Name << " = *MIB;\n"; + } +}; + +/// Generates code to constrain the operands of an output instruction to the +/// register classes specified by the definition of that instruction. +class ConstrainOperandsToDefinitionAction : public MatchAction { + std::string Name; + +public: + ConstrainOperandsToDefinitionAction(const StringRef Name) : Name(Name) {} + + void emitCxxActionStmts(raw_ostream &OS, RuleMatcher &Rule, + StringRef RecycleVarName) const override { + OS << " constrainSelectedInstRegOperands(" << Name << ", TII, TRI, RBI);\n"; + } +}; + +/// Generates code to constrain the specified operand of an output instruction +/// to the specified register class. +class ConstrainOperandToRegClassAction : public MatchAction { + std::string Name; + unsigned OpIdx; + const CodeGenRegisterClass &RC; + +public: + ConstrainOperandToRegClassAction(const StringRef Name, unsigned OpIdx, + const CodeGenRegisterClass &RC) + : Name(Name), OpIdx(OpIdx), RC(RC) {} + + void emitCxxActionStmts(raw_ostream &OS, RuleMatcher &Rule, + StringRef RecycleVarName) const override { + OS << " constrainOperandRegToRegClass(" << Name << ", " << OpIdx + << ", " << RC.getQualifiedName() << "RegClass, TII, TRI, RBI);\n"; } }; @@ -1205,7 +1250,6 @@ MA->emitCxxActionStmts(OS, *this, "I"); } - OS << " constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);\n"; OS << " return true;\n"; OS << " }\n"; OS << " return false;\n"; @@ -1439,15 +1483,10 @@ auto *ChildRec = ChildDefInit->getDef(); // Check for register classes. - if (ChildRec->isSubClassOf("RegisterClass")) { - OM.addPredicate( - Target.getRegisterClass(ChildRec)); - return Error::success(); - } - - if (ChildRec->isSubClassOf("RegisterOperand")) { + if (ChildRec->isSubClassOf("RegisterClass") || + ChildRec->isSubClassOf("RegisterOperand")) { OM.addPredicate( - Target.getRegisterClass(ChildRec->getValueAsDef("RegClass"))); + Target.getRegisterClass(getInitValueAsRegClass(ChildDefInit))); return Error::success(); } @@ -1554,22 +1593,33 @@ "Pattern operator isn't an instruction (it's a ValueType)"); return failedImport("Pattern operator isn't an instruction"); } - auto &DstI = Target.getInstruction(DstOp); + CodeGenInstruction *DstI = &Target.getInstruction(DstOp); + + unsigned DstINumUses = DstI->Operands.size() - DstI->Operands.NumDefs; + unsigned ExpectedDstINumUses = Dst->getNumChildren(); + + // COPY_TO_REGCLASS is just a copy with a ConstrainOperandToRegClassAction + // attached. + if (DstI->TheDef->getName() == "COPY_TO_REGCLASS") { + DstI = &Target.getInstruction(RK.getDef("COPY")); + DstINumUses--; // Ignore the class constraint. + ExpectedDstINumUses--; + } - auto &DstMIBuilder = M.addAction(&DstI, InsnMatcher); + auto &DstMIBuilder = M.addAction("NewI", DstI, InsnMatcher); // Render the explicit defs. - for (unsigned I = 0; I < DstI.Operands.NumDefs; ++I) { - const auto &DstIOperand = DstI.Operands[I]; + for (unsigned I = 0; I < DstI->Operands.NumDefs; ++I) { + const CGIOperandList::OperandInfo &DstIOperand = DstI->Operands[I]; DstMIBuilder.addRenderer(InsnMatcher, DstIOperand.Name); } // Render the explicit uses. unsigned Child = 0; - unsigned DstINumUses = DstI.Operands.size() - DstI.Operands.NumDefs; unsigned NumDefaultOps = 0; for (unsigned I = 0; I != DstINumUses; ++I) { - const auto &DstIOperand = DstI.Operands[DstI.Operands.NumDefs + I]; + const CGIOperandList::OperandInfo &DstIOperand = + DstI->Operands[DstI->Operands.NumDefs + I]; // If the operand has default values, introduce them now. // FIXME: Until we have a decent test case that dictates we should do @@ -1590,10 +1640,10 @@ ++Child; } - if (NumDefaultOps + Dst->getNumChildren() != DstINumUses) + if (NumDefaultOps + ExpectedDstINumUses != DstINumUses) return failedImport("Expected " + llvm::to_string(DstINumUses) + " used operands but found " + - llvm::to_string(Dst->getNumChildren()) + + llvm::to_string(ExpectedDstINumUses) + " explicit ones and " + llvm::to_string(NumDefaultOps) + " default ones"); @@ -1684,10 +1734,16 @@ const auto &DstIOperand = DstI.Operands[OpIdx]; Record *DstIOpRec = DstIOperand.Rec; - if (DstIOpRec->isSubClassOf("RegisterOperand")) + if (DstI.TheDef->getName() == "COPY_TO_REGCLASS") { + DstIOpRec = getInitValueAsRegClass(Dst->getChild(1)->getLeafValue()); + + if (DstIOpRec == nullptr) + return failedImport( + "COPY_TO_REGCLASS operand #1 isn't a register class"); + } else if (DstIOpRec->isSubClassOf("RegisterOperand")) DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); - if (!DstIOpRec->isSubClassOf("RegisterClass")) - return failedImport("Dst MI def isn't a register class"); + else if (!DstIOpRec->isSubClassOf("RegisterClass")) + return failedImport("Dst MI def isn't a register class" + to_string(*Dst)); OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); OM.setSymbolicName(DstIOperand.Name); @@ -1707,6 +1763,22 @@ if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs())) return std::move(Error); + // Constrain the registers to classes. This is normally derived from the + // emitted instruction but a few instructions require special handling. + if (DstI.TheDef->getName() == "COPY_TO_REGCLASS") { + // COPY_TO_REGCLASS does not provide operand constraints itself but the + // result is constrained to the class given by the second child. + Record *DstIOpRec = + getInitValueAsRegClass(Dst->getChild(1)->getLeafValue()); + + if (DstIOpRec == nullptr) + return failedImport("COPY_TO_REGCLASS operand #1 isn't a register class"); + + M.addAction( + "NewI", 0, Target.getRegisterClass(DstIOpRec)); + } else + M.addAction("NewI"); + // We're done with this pattern! It's eligible for GISel emission; return it. ++NumPatternImported; return std::move(M);