1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 10:32:48 +02:00

GlobalISel: Support physical register inputs in patterns

llvm-svn: 371253
This commit is contained in:
Matt Arsenault 2019-09-06 20:32:37 +00:00
parent 4610005335
commit 0f15b35531
10 changed files with 324 additions and 31 deletions

View File

@ -830,11 +830,13 @@ bool InstructionSelector::executeMatchTable(
case GIR_AddRegister: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t RegNum = MatchTable[CurrentIdx++];
uint64_t RegFlags = MatchTable[CurrentIdx++];
assert(OutMIs[InsnID] && "Attempted to add to undefined instruction");
OutMIs[InsnID].addReg(RegNum);
DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
dbgs() << CurrentIdx << ": GIR_AddRegister(OutMIs["
<< InsnID << "], " << RegNum << ")\n");
OutMIs[InsnID].addReg(RegNum, RegFlags);
DEBUG_WITH_TYPE(
TgtInstructionSelector::getName(),
dbgs() << CurrentIdx << ": GIR_AddRegister(OutMIs["
<< InsnID << "], " << RegNum << ", " << RegFlags << ")\n");
break;
}

View File

@ -303,16 +303,17 @@ AMDGPURegisterBankInfo::getInstrAlternativeMappingsIntrinsicWSideEffects(
}
case Intrinsic::amdgcn_s_sendmsg:
case Intrinsic::amdgcn_s_sendmsghalt: {
static const OpRegBankEntry<1> Table[2] = {
// FIXME: Should have no register for immediate
static const OpRegBankEntry<2> Table[2] = {
// Perfectly legal.
{ { AMDGPU::SGPRRegBankID }, 1 },
{ { AMDGPU::SGPRRegBankID, AMDGPU::SGPRRegBankID }, 1 },
// Need readlane
{ { AMDGPU::VGPRRegBankID }, 3 }
{ { AMDGPU::SGPRRegBankID, AMDGPU::VGPRRegBankID }, 3 }
};
const std::array<unsigned, 1> RegSrcOpIdx = { { 2 } };
return addMappingFromTable<1>(MI, MRI, RegSrcOpIdx, makeArrayRef(Table));
const std::array<unsigned, 2> RegSrcOpIdx = { { 1, 2 } };
return addMappingFromTable<2>(MI, MRI, RegSrcOpIdx, makeArrayRef(Table));
}
default:
return RegisterBankInfo::getInstrAlternativeMappings(MI);
@ -2179,6 +2180,7 @@ AMDGPURegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
// This must be an SGPR, but accept a VGPR.
unsigned Bank = getRegBankID(MI.getOperand(2).getReg(), MRI, *TRI,
AMDGPU::SGPRRegBankID);
OpdsMapping[1] = AMDGPU::getValueMapping(Bank, 32);
OpdsMapping[2] = AMDGPU::getValueMapping(Bank, 32);
break;
}

View File

@ -0,0 +1,25 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -march=amdgcn -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s -check-prefix=GCN
---
name: test_sendmsg
legalized: true
regBankSelected: true
tracksRegLiveness: true
body: |
bb.0:
liveins: $sgpr0
; GCN-LABEL: name: test_sendmsg
; GCN: liveins: $sgpr0
; GCN: [[COPY:%[0-9]+]]:sreg_32_xm0 = COPY $sgpr0
; GCN: $m0 = COPY [[COPY]]
; GCN: S_SENDMSG 1, implicit $exec, implicit $m0
; GCN: S_ENDPGM 0
%0:sgpr(s32) = COPY $sgpr0
%2:sgpr(s32) = G_CONSTANT i32 1
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), %2(s32), %0(s32)
S_ENDPGM 0
...

View File

@ -11,9 +11,11 @@ body: |
liveins: $sgpr0
; CHECK-LABEL: name: sendmsg_s
; CHECK: [[COPY:%[0-9]+]]:sgpr(s32) = COPY $sgpr0
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), 0, [[COPY]](s32)
; CHECK: [[C:%[0-9]+]]:sgpr(s32) = G_CONSTANT i32 0
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), [[C]](s32), [[COPY]](s32)
%0:_(s32) = COPY $sgpr0
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), 0, %0
%1:_(s32) = G_CONSTANT i32 0 ; FIXME: Should not be a constant
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), %1, %0
...
---
@ -25,8 +27,11 @@ body: |
liveins: $vgpr0
; CHECK-LABEL: name: sendmsg_v
; CHECK: [[COPY:%[0-9]+]]:vgpr_32(s32) = COPY $vgpr0
; CHECK: [[C:%[0-9]+]]:sgpr(s32) = G_CONSTANT i32 0
; CHECK: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
; CHECK: [[V_READFIRSTLANE_B32_:%[0-9]+]]:sreg_32_xm0 = V_READFIRSTLANE_B32 [[COPY]](s32), implicit $exec
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), 0, [[V_READFIRSTLANE_B32_]]
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), [[C]](s32), [[V_READFIRSTLANE_B32_]]
%0:_(s32) = COPY $vgpr0
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), 0, %0
%1:_(s32) = G_CONSTANT i32 0 ; FIXME: Should not be a constant
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), %1, %0
...

View File

@ -11,9 +11,11 @@ body: |
liveins: $sgpr0
; CHECK-LABEL: name: sendmsghalt_s
; CHECK: [[COPY:%[0-9]+]]:sgpr(s32) = COPY $sgpr0
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), 0, [[COPY]](s32)
; CHECK: [[C:%[0-9]+]]:sgpr(s32) = G_CONSTANT i32 0
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), [[C]](s32), [[COPY]](s32)
%0:_(s32) = COPY $sgpr0
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), 0, %0
%1:_(s32) = G_CONSTANT i32 0 ; FIXME: Should not be a constant
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), %1, %0
...
---
@ -25,8 +27,11 @@ body: |
liveins: $vgpr0
; CHECK-LABEL: name: sendmsghalt_v
; CHECK: [[COPY:%[0-9]+]]:vgpr_32(s32) = COPY $vgpr0
; CHECK: [[C:%[0-9]+]]:sgpr(s32) = G_CONSTANT i32 0
; CHECK: [[COPY1:%[0-9]+]]:vgpr(s32) = COPY [[C]](s32)
; CHECK: [[V_READFIRSTLANE_B32_:%[0-9]+]]:sreg_32_xm0 = V_READFIRSTLANE_B32 [[COPY]](s32), implicit $exec
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), 0, [[V_READFIRSTLANE_B32_]]
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), [[C]](s32), [[V_READFIRSTLANE_B32_]]
%0:_(s32) = COPY $vgpr0
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), 0, %0
%1:_(s32) = G_CONSTANT i32 0 ; FIXME: Should not be a constant
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsghalt), %1, %0
...

View File

@ -2,7 +2,6 @@
# RUN: llc -march=amdgcn -mcpu=hawaii -run-pass=regbankselect -regbankselect-fast -verify-machineinstrs -o - %s | FileCheck %s
# RUN: llc -march=amdgcn -mcpu=hawaii -run-pass=regbankselect -regbankselect-greedy -verify-machineinstrs -o - %s | FileCheck %s
# FIXME: The constant bank should have been chosen as VGPR
---
name: test_constant_s32_vgpr_use
legalized: true
@ -27,8 +26,12 @@ body: |
bb.0:
; CHECK-LABEL: name: test_constant_s32_sgpr_use
; CHECK: [[C:%[0-9]+]]:sgpr(s32) = G_CONSTANT i32 1
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), 0, [[C]](s32)
; CHECK: [[C1:%[0-9]+]]:sgpr(s32) = G_CONSTANT i32 0
; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), [[C1]](s32), [[C]](s32)
%0:_(s32) = G_CONSTANT i32 1
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), 0, %0
; FIXME: Should not be a constant
%1:_(s32) = G_CONSTANT i32 0
G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), %1, %0
...

View File

@ -0,0 +1,85 @@
// RUN: llvm-tblgen -gen-global-isel -optimize-match-table=false -I %p/../../include %s -o - < %s | FileCheck -check-prefix=GISEL %s
include "llvm/Target/Target.td"
def TestTargetInstrInfo : InstrInfo;
def TestTarget : Target {
let InstructionSet = TestTargetInstrInfo;
}
def R0 : Register<"r0"> { let Namespace = "MyTarget"; }
def SPECIAL : Register<"special"> { let Namespace = "MyTarget"; }
def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>;
def Special32 : RegisterClass<"MyTarget", [i32], 32, (add SPECIAL)>;
class I<dag OOps, dag IOps, list<dag> Pat>
: Instruction {
let Namespace = "MyTarget";
let OutOperandList = OOps;
let InOperandList = IOps;
let Pattern = Pat;
}
// Try a normal physical register use.
// GISEL: GIM_Try,
// GISEL-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// GISEL-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ADD,
// GISEL-NEXT: // MIs[0] dst
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// GISEL-NEXT: // MIs[0] src0
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
// GISEL-NEXT: // MIs[0] Operand 2
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/MyTarget::Special32RegClassID,
// GISEL-NEXT: // (add:{ *:[i32] } GPR32:{ *:[i32] }:$src0, SPECIAL:{ *:[i32] }) => (ADD_PHYS:{ *:[i32] } GPR32:{ *:[i32] }:$src0)
// GISEL-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::COPY,
// GISEL-NEXT: GIR_AddRegister, /*InsnID*/1, MyTarget::SPECIAL, /*AddRegisterRegFlags*/RegState::Define,
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/2, // SPECIAL
// GISEL-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::ADD_PHYS,
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src0
// GISEL-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// GISEL-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
def ADD_PHYS : I<(outs GPR32:$dst), (ins GPR32:$src0),
[(set GPR32:$dst, (add GPR32:$src0, SPECIAL))]> {
let Uses = [SPECIAL];
}
// Try using the name of the physreg in another operand.
// GISEL: GIM_Try,
// GISEL-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
// GISEL-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_MUL,
// GISEL-NEXT: // MIs[0] dst
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// GISEL-NEXT: // MIs[0] SPECIAL
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
// GISEL-NEXT: // MIs[0] Operand 2
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/MyTarget::Special32RegClassID,
// GISEL-NEXT: // (mul:{ *:[i32] } GPR32:{ *:[i32] }:$SPECIAL, SPECIAL:{ *:[i32] }) => (MUL_PHYS:{ *:[i32] } GPR32:{ *:[i32] }:$SPECIAL)
// GISEL-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::COPY,
// GISEL-NEXT: GIR_AddRegister, /*InsnID*/1, MyTarget::SPECIAL, /*AddRegisterRegFlags*/RegState::Define,
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/2, // SPECIAL
// GISEL-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MUL_PHYS,
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // SPECIAL
// GISEL-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// GISEL-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
def MUL_PHYS : I<(outs GPR32:$dst), (ins GPR32:$SPECIAL),
[(set GPR32:$dst, (mul GPR32:$SPECIAL, SPECIAL))]> {
let Uses = [SPECIAL];
}
// Try giving the physical operand a name
// def ADD_PHYS : I<(outs GPR32:$dst), (ins GPR32:$src0),
// [(set GPR32:$dst, (add GPR32:$src0, SPECIAL:$special))]> {
// let Uses = [SPECIAL];
// }

View File

@ -2368,6 +2368,21 @@ CodeGenRegBank::getRegClassForRegister(Record *R) {
return FoundRC;
}
const CodeGenRegisterClass *
CodeGenRegBank::getMinimalPhysRegClass(Record *RegRecord,
ValueTypeByHwMode *VT) {
const CodeGenRegister *Reg = getReg(RegRecord);
const CodeGenRegisterClass *BestRC = nullptr;
for (const auto &RC : getRegClasses()) {
if ((!VT || RC.hasType(*VT)) &&
RC.contains(Reg) && (!BestRC || BestRC->hasSubClass(&RC)))
BestRC = &RC;
}
assert(BestRC && "Couldn't find the register class");
return BestRC;
}
BitVector CodeGenRegBank::computeCoveredRegisters(ArrayRef<Record*> Regs) {
SetVector<const CodeGenRegister*> Set;

View File

@ -348,6 +348,10 @@ namespace llvm {
ArrayRef<ValueTypeByHwMode> getValueTypes() const { return VTs; }
unsigned getNumValueTypes() const { return VTs.size(); }
bool hasType(const ValueTypeByHwMode &VT) const {
return std::find(VTs.begin(), VTs.end(), VT) != VTs.end();
}
const ValueTypeByHwMode &getValueTypeNum(unsigned VTNum) const {
if (VTNum < VTs.size())
return VTs[VTNum];
@ -709,6 +713,13 @@ namespace llvm {
/// return the superclass. Otherwise return null.
const CodeGenRegisterClass* getRegClassForRegister(Record *R);
// Analog of TargetRegisterInfo::getMinimalPhysRegClass. Unlike
// getRegClassForRegister, this tries to find the smallest class containing
// the physical register. If \p VT is specified, it will only find classes
// with a matching type
const CodeGenRegisterClass *
getMinimalPhysRegClass(Record *RegRecord, ValueTypeByHwMode *VT = nullptr);
// Get the sum of unit weights.
unsigned getRegUnitSetWeight(const std::vector<unsigned> &Units) const {
unsigned Weight = 0;

View File

@ -829,6 +829,10 @@ protected:
/// the renderers.
StringMap<OperandMatcher *> DefinedOperands;
/// A map of anonymous physical register operands defined by the matchers that
/// may be referenced by the renderers.
DenseMap<Record *, OperandMatcher *> PhysRegOperands;
/// ID for the next instruction variable defined with implicitlyDefineInsnVar()
unsigned NextInsnVarID;
@ -911,6 +915,8 @@ public:
void defineOperand(StringRef SymbolicName, OperandMatcher &OM);
void definePhysRegOperand(Record *Reg, OperandMatcher &OM);
Error defineComplexSubOperand(StringRef SymbolicName, Record *ComplexPattern,
unsigned RendererID, unsigned SubOperandID) {
if (ComplexSubOperands.count(SymbolicName))
@ -934,6 +940,7 @@ public:
InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const;
const OperandMatcher &getOperandMatcher(StringRef Name) const;
const OperandMatcher &getPhysRegOperandMatcher(Record *) const;
void optimize() override;
void emit(MatchTable &Table) override;
@ -2018,6 +2025,11 @@ protected:
std::string SymbolicName;
unsigned InsnVarID;
/// PhysRegInputs - List list has an entry for each explicitly specified
/// physreg input to the pattern. The first elt is the Register node, the
/// second is the recorded slot number the input pattern match saved it in.
SmallVector<std::pair<Record *, unsigned>, 2> PhysRegInputs;
public:
InstructionMatcher(RuleMatcher &Rule, StringRef SymbolicName)
: Rule(Rule), SymbolicName(SymbolicName) {
@ -2059,6 +2071,20 @@ public:
llvm_unreachable("Failed to lookup operand");
}
OperandMatcher &addPhysRegInput(Record *Reg, unsigned OpIdx,
unsigned TempOpIdx) {
assert(SymbolicName.empty());
OperandMatcher *OM = new OperandMatcher(*this, OpIdx, "", TempOpIdx);
Operands.emplace_back(OM);
Rule.definePhysRegOperand(Reg, *OM);
PhysRegInputs.emplace_back(Reg, OpIdx);
return *OM;
}
ArrayRef<std::pair<Record *, unsigned>> getPhysRegInputs() const {
return PhysRegInputs;
}
StringRef getSymbolicName() const { return SymbolicName; }
unsigned getNumOperands() const { return Operands.size(); }
OperandVec::iterator operands_begin() { return Operands.begin(); }
@ -2266,6 +2292,7 @@ public:
OR_Copy,
OR_CopyOrAddZeroReg,
OR_CopySubReg,
OR_CopyPhysReg,
OR_CopyConstantAsImm,
OR_CopyFConstantAsFPImm,
OR_Imm,
@ -2320,6 +2347,38 @@ public:
}
};
/// A CopyRenderer emits code to copy a virtual register to a specific physical
/// register.
class CopyPhysRegRenderer : public OperandRenderer {
protected:
unsigned NewInsnID;
Record *PhysReg;
public:
CopyPhysRegRenderer(unsigned NewInsnID, Record *Reg)
: OperandRenderer(OR_CopyPhysReg), NewInsnID(NewInsnID),
PhysReg(Reg) {
assert(PhysReg);
}
static bool classof(const OperandRenderer *R) {
return R->getKind() == OR_CopyPhysReg;
}
Record *getPhysReg() const { return PhysReg; }
void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
const OperandMatcher &Operand = Rule.getPhysRegOperandMatcher(PhysReg);
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
Table << MatchTable::Opcode("GIR_Copy") << MatchTable::Comment("NewInsnID")
<< MatchTable::IntValue(NewInsnID) << MatchTable::Comment("OldInsnID")
<< MatchTable::IntValue(OldInsnVarID) << MatchTable::Comment("OpIdx")
<< MatchTable::IntValue(Operand.getOpIdx())
<< MatchTable::Comment(PhysReg->getName())
<< MatchTable::LineBreak;
}
};
/// A CopyOrAddZeroRegRenderer emits code to copy a single operand from an
/// existing instruction to the one being built. If the operand turns out to be
/// a 'G_CONSTANT 0' then it replaces the operand with a zero register.
@ -2466,11 +2525,13 @@ class AddRegisterRenderer : public OperandRenderer {
protected:
unsigned InsnID;
const Record *RegisterDef;
bool IsDef;
public:
AddRegisterRenderer(unsigned InsnID, const Record *RegisterDef)
: OperandRenderer(OR_Register), InsnID(InsnID), RegisterDef(RegisterDef) {
}
AddRegisterRenderer(unsigned InsnID, const Record *RegisterDef,
bool IsDef = false)
: OperandRenderer(OR_Register), InsnID(InsnID), RegisterDef(RegisterDef),
IsDef(IsDef) {}
static bool classof(const OperandRenderer *R) {
return R->getKind() == OR_Register;
@ -2484,7 +2545,16 @@ public:
? RegisterDef->getValueAsString("Namespace")
: ""),
RegisterDef->getName())
<< MatchTable::LineBreak;
<< MatchTable::Comment("AddRegisterRegFlags");
// TODO: This is encoded as a 64-bit element, but only 16 or 32-bits are
// really needed for a physical register reference. We can pack the
// register and flags in a single field.
if (IsDef)
Table << MatchTable::NamedValue("RegState::Define");
else
Table << MatchTable::IntValue(0);
Table << MatchTable::LineBreak;
}
};
@ -2896,6 +2966,13 @@ void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) {
OM.addPredicate<SameOperandMatcher>(OM.getSymbolicName());
}
void RuleMatcher::definePhysRegOperand(Record *Reg, OperandMatcher &OM) {
if (PhysRegOperands.find(Reg) == PhysRegOperands.end()) {
PhysRegOperands[Reg] = &OM;
return;
}
}
InstructionMatcher &
RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const {
for (const auto &I : InsnVariableIDs)
@ -2905,6 +2982,18 @@ RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const {
("Failed to lookup instruction " + SymbolicName).str().c_str());
}
const OperandMatcher &
RuleMatcher::getPhysRegOperandMatcher(Record *Reg) const {
const auto &I = PhysRegOperands.find(Reg);
if (I == PhysRegOperands.end()) {
PrintFatalError(SrcLoc, "Register " + Reg->getName() +
" was not declared in matcher");
}
return *I->second;
}
const OperandMatcher &
RuleMatcher::getOperandMatcher(StringRef Name) const {
const auto &I = DefinedOperands.find(Name);
@ -3152,9 +3241,9 @@ private:
bool OperandIsAPointer, unsigned OpIdx,
unsigned &TempOpIdx);
Expected<BuildMIAction &>
createAndImportInstructionRenderer(RuleMatcher &M,
const TreePatternNode *Dst);
Expected<BuildMIAction &> createAndImportInstructionRenderer(
RuleMatcher &M, InstructionMatcher &InsnMatcher,
const TreePatternNode *Src, const TreePatternNode *Dst);
Expected<action_iterator> createAndImportSubInstructionRenderer(
action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst,
unsigned TempReg);
@ -3162,6 +3251,7 @@ private:
createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M,
const TreePatternNode *Dst);
void importExplicitDefRenderers(BuildMIAction &DstMIBuilder);
Expected<action_iterator>
importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M,
BuildMIAction &DstMIBuilder,
@ -3629,14 +3719,37 @@ Error GlobalISelEmitter::importComplexPatternOperandMatcher(
return Error::success();
}
// Get the name to use for a pattern operand. For an anonymous physical register
// input, this should use the register name.
static StringRef getSrcChildName(const TreePatternNode *SrcChild,
Record *&PhysReg) {
StringRef SrcChildName = SrcChild->getName();
if (SrcChildName.empty() && SrcChild->isLeaf()) {
if (auto *ChildDefInit = dyn_cast<DefInit>(SrcChild->getLeafValue())) {
auto *ChildRec = ChildDefInit->getDef();
if (ChildRec->isSubClassOf("Register")) {
SrcChildName = ChildRec->getName();
PhysReg = ChildRec;
}
}
}
return SrcChildName;
}
Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule,
InstructionMatcher &InsnMatcher,
const TreePatternNode *SrcChild,
bool OperandIsAPointer,
unsigned OpIdx,
unsigned &TempOpIdx) {
OperandMatcher &OM =
InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx);
Record *PhysReg = nullptr;
StringRef SrcChildName = getSrcChildName(SrcChild, PhysReg);
OperandMatcher &OM = PhysReg ?
InsnMatcher.addPhysRegInput(PhysReg, OpIdx, TempOpIdx) :
InsnMatcher.addOperand(OpIdx, SrcChildName, TempOpIdx);
if (OM.isSameAsAnotherOperand())
return Error::success();
@ -3725,6 +3838,20 @@ Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule,
return Error::success();
}
if (ChildRec->isSubClassOf("Register")) {
// This just be emitted as a copy to the specific register.
ValueTypeByHwMode VT = ChildTypes.front().getValueTypeByHwMode();
const CodeGenRegisterClass *RC
= CGRegs.getMinimalPhysRegClass(ChildRec, &VT);
if (!RC) {
return failedImport(
"Could not determine physical register class of pattern source");
}
OM.addPredicate<RegisterBankOperandMatcher>(*RC);
return Error::success();
}
// Check for ValueType.
if (ChildRec->isSubClassOf("ValueType")) {
// We already added a type check as standard practice so this doesn't need
@ -3891,7 +4018,8 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
}
Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
RuleMatcher &M, const TreePatternNode *Dst) {
RuleMatcher &M, InstructionMatcher &InsnMatcher, const TreePatternNode *Src,
const TreePatternNode *Dst) {
auto InsertPtOrError = createInstructionRenderer(M.actions_end(), M, Dst);
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
@ -3899,6 +4027,17 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
action_iterator InsertPt = InsertPtOrError.get();
BuildMIAction &DstMIBuilder = *static_cast<BuildMIAction *>(InsertPt->get());
for (auto PhysInput : InsnMatcher.getPhysRegInputs()) {
InsertPt = M.insertAction<BuildMIAction>(
InsertPt, M.allocateOutputInsnID(),
&Target.getInstruction(RK.getDef("COPY")));
BuildMIAction &CopyToPhysRegMIBuilder =
*static_cast<BuildMIAction *>(InsertPt->get());
CopyToPhysRegMIBuilder.addRenderer<AddRegisterRenderer>(PhysInput.first,
true);
CopyToPhysRegMIBuilder.addRenderer<CopyPhysRegRenderer>(PhysInput.first);
}
importExplicitDefRenderers(DstMIBuilder);
if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst)
@ -4464,7 +4603,8 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
++OpIdx;
}
auto DstMIBuilderOrError = createAndImportInstructionRenderer(M, Dst);
auto DstMIBuilderOrError =
createAndImportInstructionRenderer(M, InsnMatcher, Src, Dst);
if (auto Error = DstMIBuilderOrError.takeError())
return std::move(Error);
BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get();