forked from OSchip/llvm-project
GlobalISel: Support physical register inputs in patterns
llvm-svn: 371253
This commit is contained in:
parent
e8b3553992
commit
3e45c70288
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
...
|
|
@ -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
|
||||
...
|
||||
|
|
|
@ -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
|
||||
...
|
||||
|
|
|
@ -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
|
||||
|
||||
...
|
||||
|
|
|
@ -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];
|
||||
// }
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue