mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-29 06:10:48 +00:00
TableGen/GlobalISel: Allow output instructions with multiple defs
The DAG behavior allows matchching input patterns with a single result to the first result of an output instruction that defines multiple results. The remaining defs are implicitly dead. This starts to fix using manual selection for AMDGPU add/sub (although it's still needed, mostly because it's also still needed for G_PTR_ADD).
This commit is contained in:
parent
79f86bfe26
commit
60ee3e2e8b
@ -94,8 +94,7 @@ body: |
|
||||
; GFX6-LABEL: name: add_neg_inline_const_64_to_sub_s32_v
|
||||
; GFX6: liveins: $vgpr0
|
||||
; GFX6: [[COPY:%[0-9]+]]:vgpr_32 = COPY $vgpr0
|
||||
; GFX6: [[V_MOV_B32_e32_:%[0-9]+]]:vgpr_32 = V_MOV_B32_e32 -64, implicit $exec
|
||||
; GFX6: %2:vgpr_32, dead %3:sreg_64_xexec = V_ADD_CO_U32_e64 [[COPY]], [[V_MOV_B32_e32_]], 0, implicit $exec
|
||||
; GFX6: %2:vgpr_32, dead %3:sreg_64 = V_SUB_CO_U32_e64 [[COPY]], 64, 0, implicit $exec
|
||||
; GFX6: S_ENDPGM 0, implicit %2
|
||||
; GFX9-LABEL: name: add_neg_inline_const_64_to_sub_s32_v
|
||||
; GFX9: liveins: $vgpr0
|
||||
|
@ -12,6 +12,9 @@ def GPR32Op : RegisterOperand<GPR32>;
|
||||
def F0 : Register<"f0"> { let Namespace = "MyTarget"; }
|
||||
def FPR32 : RegisterClass<"MyTarget", [f32], 32, (add F0)>;
|
||||
def FPR32Op : RegisterOperand<FPR32>;
|
||||
def B0 : Register<"b0"> { let Namespace = "MyTarget"; }
|
||||
def GPR8 : RegisterClass<"MyTarget", [i8], 8, (add B0)>;
|
||||
|
||||
def p0 : PtrValueType <i32, 0>;
|
||||
|
||||
class I<dag OOps, dag IOps, list<dag> Pat>
|
||||
|
27
test/TableGen/GlobalISelEmitter-output-discard.td
Normal file
27
test/TableGen/GlobalISelEmitter-output-discard.td
Normal file
@ -0,0 +1,27 @@
|
||||
// RUN: llvm-tblgen -gen-global-isel -warn-on-skipped-patterns -I %p/../../include -I %p/Common %s -o - < %s | FileCheck -check-prefix=GISEL %s
|
||||
|
||||
include "llvm/Target/Target.td"
|
||||
include "GlobalISelEmitterCommon.td"
|
||||
|
||||
// Test that extra explicit results are treated as dead defs.
|
||||
def ADD_CO : I<(outs GPR32:$dst, GPR8:$flag),
|
||||
(ins GPR32Op:$src0, GPR32Op:$src1), []>;
|
||||
|
||||
// GISEL: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ADD,
|
||||
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
|
||||
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
|
||||
// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// GISEL-NEXT: // (add:{ *:[i32] } i32:{ *:[i32] }:$src0, i32:{ *:[i32] }:$src1) => (ADD_CO:{ *:[i32] }:{ *:[i8] } GPR32:{ *:[i32] }:$src0, GPR32:{ *:[i32] }:$src1)
|
||||
// GISEL-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s8,
|
||||
// GISEL-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::ADD_CO,
|
||||
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// GISEL-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/RegState::Define|RegState::Dead,
|
||||
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src0
|
||||
// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // src1
|
||||
// GISEL-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// GISEL-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
def : Pat <
|
||||
(add i32:$src0, i32:$src1),
|
||||
(ADD_CO GPR32:$src0, GPR32:$src1)
|
||||
>;
|
@ -2628,12 +2628,14 @@ protected:
|
||||
unsigned TempRegID;
|
||||
const CodeGenSubRegIndex *SubRegIdx;
|
||||
bool IsDef;
|
||||
bool IsDead;
|
||||
|
||||
public:
|
||||
TempRegRenderer(unsigned InsnID, unsigned TempRegID, bool IsDef = false,
|
||||
const CodeGenSubRegIndex *SubReg = nullptr)
|
||||
const CodeGenSubRegIndex *SubReg = nullptr,
|
||||
bool IsDead = false)
|
||||
: OperandRenderer(OR_Register), InsnID(InsnID), TempRegID(TempRegID),
|
||||
SubRegIdx(SubReg), IsDef(IsDef) {}
|
||||
SubRegIdx(SubReg), IsDef(IsDef), IsDead(IsDead) {}
|
||||
|
||||
static bool classof(const OperandRenderer *R) {
|
||||
return R->getKind() == OR_TempRegister;
|
||||
@ -2650,9 +2652,13 @@ public:
|
||||
<< MatchTable::Comment("TempRegID") << MatchTable::IntValue(TempRegID)
|
||||
<< MatchTable::Comment("TempRegFlags");
|
||||
|
||||
if (IsDef)
|
||||
Table << MatchTable::NamedValue("RegState::Define");
|
||||
else
|
||||
if (IsDef) {
|
||||
SmallString<32> RegFlags;
|
||||
RegFlags += "RegState::Define";
|
||||
if (IsDead)
|
||||
RegFlags += "|RegState::Dead";
|
||||
Table << MatchTable::NamedValue(RegFlags);
|
||||
} else
|
||||
Table << MatchTable::IntValue(0);
|
||||
|
||||
if (SubRegIdx)
|
||||
@ -3394,7 +3400,11 @@ private:
|
||||
Expected<action_iterator>
|
||||
createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M,
|
||||
const TreePatternNode *Dst);
|
||||
void importExplicitDefRenderers(BuildMIAction &DstMIBuilder);
|
||||
|
||||
Expected<action_iterator>
|
||||
importExplicitDefRenderers(action_iterator InsertPt, RuleMatcher &M,
|
||||
BuildMIAction &DstMIBuilder,
|
||||
const TreePatternNode *Dst);
|
||||
|
||||
Expected<action_iterator>
|
||||
importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M,
|
||||
@ -4220,7 +4230,9 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
|
||||
CopyToPhysRegMIBuilder.addRenderer<CopyPhysRegRenderer>(PhysInput.first);
|
||||
}
|
||||
|
||||
importExplicitDefRenderers(DstMIBuilder);
|
||||
if (auto Error = importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Dst)
|
||||
.takeError())
|
||||
return std::move(Error);
|
||||
|
||||
if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst)
|
||||
.takeError())
|
||||
@ -4372,13 +4384,34 @@ Expected<action_iterator> GlobalISelEmitter::createInstructionRenderer(
|
||||
DstI);
|
||||
}
|
||||
|
||||
void GlobalISelEmitter::importExplicitDefRenderers(
|
||||
BuildMIAction &DstMIBuilder) {
|
||||
Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
|
||||
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
|
||||
const TreePatternNode *Dst) {
|
||||
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
|
||||
for (unsigned I = 0; I < DstI->Operands.NumDefs; ++I) {
|
||||
const CGIOperandList::OperandInfo &DstIOperand = DstI->Operands[I];
|
||||
DstMIBuilder.addRenderer<CopyRenderer>(DstIOperand.Name);
|
||||
const unsigned NumDefs = DstI->Operands.NumDefs;
|
||||
if (NumDefs == 0)
|
||||
return InsertPt;
|
||||
|
||||
DstMIBuilder.addRenderer<CopyRenderer>(DstI->Operands[0].Name);
|
||||
|
||||
// Patterns only handle a single result, so any result after the first is an
|
||||
// implicitly dead def.
|
||||
for (unsigned I = 1; I < NumDefs; ++I) {
|
||||
const TypeSetByHwMode &ExtTy = Dst->getExtType(I);
|
||||
if (!ExtTy.isMachineValueType())
|
||||
return failedImport("unsupported typeset");
|
||||
|
||||
auto OpTy = MVTToLLT(ExtTy.getMachineValueType().SimpleTy);
|
||||
if (!OpTy)
|
||||
return failedImport("unsupported type");
|
||||
|
||||
unsigned TempRegID = M.allocateTempRegID();
|
||||
InsertPt =
|
||||
M.insertAction<MakeTempRegisterAction>(InsertPt, *OpTy, TempRegID);
|
||||
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true, nullptr, true);
|
||||
}
|
||||
|
||||
return InsertPt;
|
||||
}
|
||||
|
||||
Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
|
||||
@ -4814,8 +4847,8 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
auto &DstI = Target.getInstruction(DstOp);
|
||||
StringRef DstIName = DstI.TheDef->getName();
|
||||
|
||||
if (DstI.Operands.NumDefs != Src->getExtTypes().size())
|
||||
return failedImport("Src pattern results and dst MI defs are different (" +
|
||||
if (DstI.Operands.NumDefs < Src->getExtTypes().size())
|
||||
return failedImport("Src pattern result has more defs than dst MI (" +
|
||||
to_string(Src->getExtTypes().size()) + " def(s) vs " +
|
||||
to_string(DstI.Operands.NumDefs) + " def(s))");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user