mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-24 04:09:45 +00:00
[globalisel][tablegen] Add support for COPY_TO_REGCLASS.
Summary: As part of this * Emitted instructions now have named MachineInstr variables associated with them. This isn't particularly important yet but it's a small step towards multiple-insn emission. * constrainSelectedInstRegOperands() is no longer hardcoded. It's now added as the ConstrainOperandsToDefinitionAction() action. COPY_TO_REGCLASS uses an alternate constraint mechanism ConstrainOperandToRegClassAction() which supports arbitrary constraints such as that defined by COPY_TO_REGCLASS. Reviewers: ab, qcolombet, t.p.northover, rovka, kristof.beyls, aditya_nandakumar Reviewed By: ab Subscribers: javed.absar, igorb, llvm-commits Differential Revision: https://reviews.llvm.org/D33590 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@305791 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
35b14a7d5d
commit
a4b49d696a
@ -29,6 +29,7 @@ class MachineOperand;
|
||||
class MachineRegisterInfo;
|
||||
class RegisterBankInfo;
|
||||
class TargetInstrInfo;
|
||||
class TargetRegisterClass;
|
||||
class TargetRegisterInfo;
|
||||
|
||||
/// Container class for CodeGen predicate results.
|
||||
@ -79,6 +80,16 @@ protected:
|
||||
|
||||
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).
|
||||
|
@ -29,13 +29,26 @@ class RegisterBankInfo;
|
||||
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,
|
||||
|
@ -25,6 +25,18 @@ using namespace llvm;
|
||||
|
||||
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 {
|
||||
|
@ -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 @@ unsigned llvm::constrainOperandRegClass(
|
||||
"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,
|
||||
|
@ -95,7 +95,7 @@ regBankSelected: true
|
||||
|
||||
# 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 @@ regBankSelected: true
|
||||
|
||||
# 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 }
|
||||
|
@ -10,6 +10,8 @@ def MyTarget : Target { let InstructionSet = MyTargetISA; }
|
||||
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 XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
|
||||
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 ([&]() {
|
||||
|
@ -158,6 +158,16 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) {
|
||||
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 @@ public:
|
||||
/// 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 @@ private:
|
||||
}
|
||||
|
||||
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 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
OS << " MachineInstr &NewI = " << RecycleVarName << ";\n";
|
||||
OS << " MachineInstr &" << Name << " = " << RecycleVarName << ";\n";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1050,7 +1062,40 @@ public:
|
||||
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 @@ void RuleMatcher::emit(raw_ostream &OS,
|
||||
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 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
||||
auto *ChildRec = ChildDefInit->getDef();
|
||||
|
||||
// Check for register classes.
|
||||
if (ChildRec->isSubClassOf("RegisterClass")) {
|
||||
if (ChildRec->isSubClassOf("RegisterClass") ||
|
||||
ChildRec->isSubClassOf("RegisterOperand")) {
|
||||
OM.addPredicate<RegisterBankOperandMatcher>(
|
||||
Target.getRegisterClass(ChildRec));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
if (ChildRec->isSubClassOf("RegisterOperand")) {
|
||||
OM.addPredicate<RegisterBankOperandMatcher>(
|
||||
Target.getRegisterClass(ChildRec->getValueAsDef("RegClass")));
|
||||
Target.getRegisterClass(getInitValueAsRegClass(ChildDefInit)));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
@ -1554,22 +1593,33 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
|
||||
"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);
|
||||
|
||||
auto &DstMIBuilder = M.addAction<BuildMIAction>(&DstI, InsnMatcher);
|
||||
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>("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 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
|
||||
++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 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
|
||||
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 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user