Index: llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-add.mir =================================================================== --- llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-add.mir +++ llvm/test/CodeGen/AMDGPU/GlobalISel/inst-select-add.mir @@ -94,8 +94,7 @@ ; 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 4294967232, 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 Index: llvm/test/TableGen/Common/GlobalISelEmitterCommon.td =================================================================== --- llvm/test/TableGen/Common/GlobalISelEmitterCommon.td +++ llvm/test/TableGen/Common/GlobalISelEmitterCommon.td @@ -12,6 +12,9 @@ def F0 : Register<"f0"> { let Namespace = "MyTarget"; } def FPR32 : RegisterClass<"MyTarget", [f32], 32, (add F0)>; def FPR32Op : RegisterOperand; +def B0 : Register<"b0"> { let Namespace = "MyTarget"; } +def GPR8 : RegisterClass<"MyTarget", [i8], 8, (add B0)>; + def p0 : PtrValueType ; class I Pat> Index: llvm/test/TableGen/GlobalISelEmitter-output-discard.td =================================================================== --- /dev/null +++ llvm/test/TableGen/GlobalISelEmitter-output-discard.td @@ -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) +>; Index: llvm/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -2628,12 +2628,14 @@ 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 @@ << 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 @@ Expected createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst); - void importExplicitDefRenderers(BuildMIAction &DstMIBuilder); + + Expected + importExplicitDefRenderers(action_iterator InsertPt, RuleMatcher &M, + BuildMIAction &DstMIBuilder, + const TreePatternNode *Dst); Expected importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M, @@ -4220,7 +4230,9 @@ CopyToPhysRegMIBuilder.addRenderer(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 @@ DstI); } -void GlobalISelEmitter::importExplicitDefRenderers( - BuildMIAction &DstMIBuilder) { +Expected 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(DstIOperand.Name); + const unsigned NumDefs = DstI->Operands.NumDefs; + if (NumDefs == 0) + return InsertPt; + + DstMIBuilder.addRenderer(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(InsertPt, *OpTy, TempRegID); + DstMIBuilder.addRenderer(TempRegID, true, nullptr, true); } + + return InsertPt; } Expected GlobalISelEmitter::importExplicitUseRenderers( @@ -4814,8 +4847,8 @@ 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))");