Page MenuHomePhabricator

D33590.diff
No OneTemporary

File Metadata

Created
Fri, Jan 24, 4:17 PM

D33590.diff

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<GPR32>;
+def F0 : Register<"f0"> { let Namespace = "MyTarget"; }
+def FPR32 : RegisterClass<"MyTarget", [f32], 32, (add F0)>;
class I<dag OOps, dag IOps, list<dag> 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<DefInit>(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<std::unique_ptr<OperandRenderer>> 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 <class Kind, class... Args>
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<RegisterBankOperandMatcher>(
- Target.getRegisterClass(ChildRec));
- return Error::success();
- }
-
- if (ChildRec->isSubClassOf("RegisterOperand")) {
+ if (ChildRec->isSubClassOf("RegisterClass") ||
+ ChildRec->isSubClassOf("RegisterOperand")) {
OM.addPredicate<RegisterBankOperandMatcher>(
- 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<BuildMIAction>(&DstI, InsnMatcher);
+ auto &DstMIBuilder = M.addAction<BuildMIAction>("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<CopyRenderer>(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<ConstrainOperandToRegClassAction>(
+ "NewI", 0, Target.getRegisterClass(DstIOpRec));
+ } else
+ M.addAction<ConstrainOperandsToDefinitionAction>("NewI");
+
// We're done with this pattern! It's eligible for GISel emission; return it.
++NumPatternImported;
return std::move(M);

Event Timeline