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:
Matt Arsenault 2020-07-13 08:59:38 -04:00
parent 79f86bfe26
commit 60ee3e2e8b
4 changed files with 78 additions and 16 deletions

View File

@ -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

View File

@ -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>

View 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)
>;

View File

@ -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))");