From 94a13d9b0e90b966137670cc430b6a23b463102e Mon Sep 17 00:00:00 2001 From: Sergei Barannikov Date: Wed, 18 Dec 2024 16:47:10 +0300 Subject: [PATCH] [TableGen][GISel] Improve dead register handling A dead implicit def wasn't marked as dead if it is also an implicit use. The new approach should also be more straightforward and simplifies future changes for supporting optional defs and physical register defs. --- .../select-intrinsic-x86-flags-read-u32.mir | 2 +- .../GlobalISelEmitter-nested-subregs.td | 2 +- .../TableGen/GlobalISelEmitterRegSequence.td | 2 +- llvm/test/TableGen/GlobalISelEmitterSubreg.td | 8 +- llvm/utils/TableGen/GlobalISelEmitter.cpp | 77 +++++++------------ 5 files changed, 35 insertions(+), 56 deletions(-) diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir b/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir index 332ec2240c5b6..3d1857a274b4b 100644 --- a/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir +++ b/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir @@ -9,7 +9,7 @@ define void @read_flags() { ret void } ; CHECK-LABEL: name: read_flags ; CHECK: bb.0: - ; CHECK: [[RDFLAGS32_:%[0-9]+]]:gr32 = RDFLAGS32 implicit-def $esp, implicit $esp + ; CHECK: [[RDFLAGS32_:%[0-9]+]]:gr32 = RDFLAGS32 implicit-def dead $esp, implicit $esp ; CHECK: $eax = COPY [[RDFLAGS32_]] ... diff --git a/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td b/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td index 1fdb973c1f1ec..79e55ef2e8b8c 100644 --- a/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td +++ b/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td @@ -38,11 +38,11 @@ def A0 : RegisterClass<"MyTarget", [i32], 32, (add a0)>; // CHECK-NEXT: // MIs[0] src // CHECK-NEXT: GIM_RootCheckType, /*Op*/1, /*Type*/GILLT_s8, // CHECK-NEXT: // (anyext:{ *:[i16] } i8:{ *:[i8] }:$src) => (EXTRACT_SUBREG:{ *:[i16] } (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), A0b:{ *:[i8] }:$src, lo8:{ *:[i32] }), lo16:{ *:[i32] }) -// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(TargetOpcode::IMPLICIT_DEF), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2, +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::INSERT_SUBREG), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_AddSimpleTempRegister, /*InsnID*/1, /*TempRegID*/1, diff --git a/llvm/test/TableGen/GlobalISelEmitterRegSequence.td b/llvm/test/TableGen/GlobalISelEmitterRegSequence.td index 3829070b28efe..69f82eac49c16 100644 --- a/llvm/test/TableGen/GlobalISelEmitterRegSequence.td +++ b/llvm/test/TableGen/GlobalISelEmitterRegSequence.td @@ -39,12 +39,12 @@ def SUBSOME_INSN : I<(outs SRegs:$dst), (ins SOP:$src), []>; // CHECK-NEXT: GIM_RootCheckType, /*Op*/1, /*Type*/GILLT_s16, // CHECK-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(Test::SRegsRegClassID), // CHECK-NEXT: // (sext:{ *:[i32] } SOP:{ *:[i16] }:$src) => (REG_SEQUENCE:{ *:[i32] } DRegs:{ *:[i32] }, (SUBSOME_INSN:{ *:[i16] } SOP:{ *:[i16] }:$src), sub0:{ *:[i32] }, (SUBSOME_INSN:{ *:[i16] } SOP:{ *:[i16] }:$src), sub1:{ *:[i32] }) -// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16, // CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s16, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(MyTarget::SUBSOME_INSN), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/0, /*OpIdx*/1, // src // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2, +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(MyTarget::SUBSOME_INSN), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // src diff --git a/llvm/test/TableGen/GlobalISelEmitterSubreg.td b/llvm/test/TableGen/GlobalISelEmitterSubreg.td index 8df3238f6cc21..08e690f3e894d 100644 --- a/llvm/test/TableGen/GlobalISelEmitterSubreg.td +++ b/llvm/test/TableGen/GlobalISelEmitterSubreg.td @@ -59,13 +59,13 @@ def : Pat<(sub (complex DOP:$src1, DOP:$src2), 77), (SOME_INSN2 (EXTRACT_SUBREG DOP:$src1, sub0), (EXTRACT_SUBREG DOP:$src2, sub1))>; // CHECK-LABEL: // (sub:{ *:[i32] } (complex:{ *:[i32] } DOP:{ *:[i32] }:$src1, DOP:{ *:[i32] }:$src2), 77:{ *:[i32] }) => (SOME_INSN2:{ *:[i32] } (EXTRACT_SUBREG:{ *:[i32] } DOP:{ *:[i32] }:$src1, sub0:{ *:[i32] }), (EXTRACT_SUBREG:{ *:[i32] } DOP:{ *:[i32] }:$src2, sub1:{ *:[i32] })) -// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_ComplexSubOperandSubRegRenderer, /*InsnID*/2, /*RendererID*/GIMT_Encode2(0), /*SubOperand*/1, /*SubRegIdx*/GIMT_Encode2(2), // src2 // CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/2, /*Op*/0, GIMT_Encode2(Test::SRegsRegClassID), // CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/2, /*Op*/1, GIMT_Encode2(Test::DRegsRegClassID), +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_ComplexSubOperandSubRegRenderer, /*InsnID*/1, /*RendererID*/GIMT_Encode2(0), /*SubOperand*/0, /*SubRegIdx*/GIMT_Encode2(1), // src1 @@ -103,11 +103,11 @@ def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), SOP:$src // instruction. def : Pat<(i32 (anyext i16:$src)), (SOME_INSN (INSERT_SUBREG (i32 (IMPLICIT_DEF)), SOP:$src, sub0))>; // CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (SOME_INSN:{ *:[i32] } (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), SOP:{ *:[i16] }:$src, sub0:{ *:[i32] })) -// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(TargetOpcode::IMPLICIT_DEF), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2, +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::INSERT_SUBREG), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_AddSimpleTempRegister, /*InsnID*/1, /*TempRegID*/1, @@ -138,12 +138,12 @@ def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (COPY_TO_REGCLASS SOP:$sr // by a subinstruction. def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), (SUBSOME_INSN SOP:$src), sub0)>; // CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), (SUBSOME_INSN:{ *:[i16] } SOP:{ *:[i16] }:$src), sub0:{ *:[i32] }) -// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s16, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(MyTarget::SUBSOME_INSN), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/0, /*OpIdx*/1, // src // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2, +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::IMPLICIT_DEF), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/1, @@ -200,12 +200,12 @@ def : Pat<(i16 (trunc (bitreverse DOP:$src))), // CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/1, /*Op*/1, /*RC*/GIMT_Encode2(Test::DRegsRegClassID), // CHECK-NEXT: GIM_CheckIsSafeToFold, /*NumInsns*/1, // CHECK-NEXT: // (trunc:{ *:[i16] } (ctpop:{ *:[i32] } DOP:{ *:[i32] }:$src)) => (SUBSOME_INSN2:{ *:[i16] } (EXTRACT_SUBREG:{ *:[i16] } (SOME_INSN:{ *:[i32] } DOP:{ *:[i32] }:$src), sub0:{ *:[i32] })) -// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16, // CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(MyTarget::SOME_INSN), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/1, /*OpIdx*/1, // src // CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2, +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16, // CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY), // CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define), // CHECK-NEXT: GIR_AddTempSubRegister, /*InsnID*/1, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(0), GIMT_Encode2(sub0), diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 84f23985b6421..715c29c4636ae 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -324,8 +324,6 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter { void emitTestSimplePredicate(raw_ostream &OS) override; void emitRunCustomAction(raw_ostream &OS) override; - void postProcessRule(RuleMatcher &M); - const CodeGenTarget &getTarget() const override { return Target; } StringRef getClassName() const override { return ClassName; } @@ -1410,12 +1408,10 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer( DstMIBuilder.addRenderer(TempRegID, true); // Handle additional (ignored) results. - if (DstMIBuilder.getCGI()->Operands.NumDefs > 1) { - InsertPtOrError = importExplicitDefRenderers( - std::prev(*InsertPtOrError), M, DstMIBuilder, Src, Dst, /*Start=*/1); - if (auto Error = InsertPtOrError.takeError()) - return std::move(Error); - } + InsertPtOrError = importExplicitDefRenderers( + std::prev(*InsertPtOrError), M, DstMIBuilder, Src, Dst, /*Start=*/1); + if (auto Error = InsertPtOrError.takeError()) + return std::move(Error); InsertPtOrError = importExplicitUseRenderers(InsertPtOrError.get(), M, DstMIBuilder, Dst, Src); @@ -1454,25 +1450,26 @@ Expected GlobalISelEmitter::importExplicitDefRenderers( action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const TreePatternNode &Src, const TreePatternNode &Dst, unsigned Start) { const CodeGenInstruction *DstI = DstMIBuilder.getCGI(); - const unsigned SrcNumDefs = Src.getExtTypes().size(); - const unsigned DstNumDefs = DstI->Operands.NumDefs; - if (DstNumDefs == 0) - return InsertPt; - - for (unsigned I = Start; I < SrcNumDefs; ++I) { - std::string OpName = getMangledRootDefName(DstI->Operands[I].Name); - // CopyRenderer saves a StringRef, so cannot pass OpName itself - - // let's use a string with an appropriate lifetime. - StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName(); - DstMIBuilder.addRenderer(PermanentRef); - } // Some instructions have multiple defs, but are missing a type entry // (e.g. s_cc_out operands). - if (Dst.getExtTypes().size() < DstNumDefs) + if (Dst.getExtTypes().size() < DstI->Operands.NumDefs) return failedImport("unhandled discarded def"); - for (unsigned I = SrcNumDefs; I < DstNumDefs; ++I) { + // Process explicit defs. The caller may have already handled the first def. + for (unsigned I = Start, E = DstI->Operands.NumDefs; I != E; ++I) { + std::string OpName = getMangledRootDefName(DstI->Operands[I].Name); + + // If the def is used in the source DAG, forward it. + if (M.hasOperand(OpName)) { + // CopyRenderer saves a StringRef, so cannot pass OpName itself - + // let's use a string with an appropriate lifetime. + StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName(); + DstMIBuilder.addRenderer(PermanentRef); + continue; + } + + // The def is discarded, create a dead virtual register for it. const TypeSetByHwMode &ExtTy = Dst.getExtType(I); if (!ExtTy.isMachineValueType()) return failedImport("unsupported typeset"); @@ -1484,7 +1481,15 @@ Expected GlobalISelEmitter::importExplicitDefRenderers( unsigned TempRegID = M.allocateTempRegID(); InsertPt = M.insertAction(InsertPt, *OpTy, TempRegID); - DstMIBuilder.addRenderer(TempRegID, true, nullptr, true); + DstMIBuilder.addRenderer( + TempRegID, /*IsDef=*/true, /*SubReg=*/nullptr, /*IsDead=*/true); + } + + // Implicit defs are not currently supported, mark all of them as dead. + for (const Record *Reg : DstI->ImplicitDefs) { + std::string OpName = getMangledRootDefName(Reg->getName()); + assert(!M.hasOperand(OpName) && "The pattern should've been rejected"); + DstMIBuilder.setDeadImplicitDef(Reg); } return InsertPt; @@ -2299,31 +2304,6 @@ void GlobalISelEmitter::emitRunCustomAction(raw_ostream &OS) { << "}\n"; } -void GlobalISelEmitter::postProcessRule(RuleMatcher &M) { - SmallPtrSet UsedRegs; - - // TODO: deal with subregs? - for (auto &A : M.actions()) { - auto *MI = dyn_cast(A.get()); - if (!MI) - continue; - - for (auto *Use : MI->getCGI()->ImplicitUses) - UsedRegs.insert(Use); - } - - for (auto &A : M.actions()) { - auto *MI = dyn_cast(A.get()); - if (!MI) - continue; - - for (auto *Def : MI->getCGI()->ImplicitDefs) { - if (!UsedRegs.contains(Def)) - MI->setDeadImplicitDef(Def); - } - } -} - void GlobalISelEmitter::run(raw_ostream &OS) { if (!UseCoverageFile.empty()) { RuleCoverage = CodeGenCoverage(); @@ -2383,7 +2363,6 @@ void GlobalISelEmitter::run(raw_ostream &OS) { "Pattern is not covered by a test"); } Rules.push_back(std::move(MatcherOrErr.get())); - postProcessRule(Rules.back()); } // Comparison function to order records by name.