Index: llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp +++ llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp @@ -159,6 +159,7 @@ #include #include #include +#include using namespace llvm; @@ -296,6 +297,8 @@ LexicalScopes LS; VarLocSet::Allocator Alloc; + const MachineInstr *LastNonDbgMI; + enum struct TransferKind { TransferCopy, TransferSpill, TransferRestore }; using FragmentInfo = DIExpression::FragmentInfo; @@ -801,6 +804,8 @@ LocIndex LocationID; ///< Location number for the transfer dest. }; using TransferMap = SmallVector; + using TDPMap = std::multimap; + using RegSetterInstructionMap = DenseMap; // Types for recording sets of variable fragments that overlap. For a given // local variable, we record all other fragments of that variable that could @@ -974,13 +979,21 @@ Register NewReg = Register()); void transferDebugValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs); + VarLocMap &VarLocIDs, TDPMap &EntryValTransfers, + RegSetterInstructionMap &RegSetInstrs); void transferSpillOrRestoreInst(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers); + VarLocMap &VarLocIDs, + TransferMap &Transfers); + bool cleanupEntryValueTransfers(const MachineInstr *MI, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, const VarLoc &EntryVL, + TDPMap &EntryValTransfers); bool removeEntryValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, const VarLoc &EntryVL); + VarLocMap &VarLocIDs, const VarLoc &EntryVL, + TDPMap &EntryValTransfers, + RegSetterInstructionMap &RegSetInstrs); void emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers, + VarLocMap &VarLocIDs, TDPMap &EntryValTransfers, VarLocsInRange &KillSet); void recordEntryValue(const MachineInstr &MI, const DefinedRegsSet &DefinedRegs, @@ -988,12 +1001,15 @@ void transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers); void transferRegisterDef(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers); + VarLocMap &VarLocIDs, TDPMap &EntryValTransfers, + RegSetterInstructionMap &RegSetInstrs); bool transferTerminator(MachineBasicBlock *MBB, OpenRangesSet &OpenRanges, VarLocInMBB &OutLocs, const VarLocMap &VarLocIDs); void process(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers); + VarLocMap &VarLocIDs, TransferMap &Transfers, + TDPMap &EntryValTransfers, + RegSetterInstructionMap &RegSetInstrs); void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments, OverlapMap &OLapMap); @@ -1225,44 +1241,87 @@ return {Reg, Offset}; } +/// Do cleanup of \p EntryValTransfers created by \p TRInst, by removing the +/// Transfer, which uses the to-be-deleted \p EntryVL. +bool VarLocBasedLDV::cleanupEntryValueTransfers(const MachineInstr *TRInst, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, + const VarLoc &EntryVL, + TDPMap &EntryValTransfers) { + if (EntryValTransfers.empty() || TRInst == nullptr) + return true; + + auto TransRange = EntryValTransfers.equal_range(TRInst); + for (auto TDPair = TransRange.first, TDPEnd = TransRange.second; + TDPair != TDPEnd; ++TDPair) { + const VarLoc &EmittedEV = VarLocIDs[TDPair->second]; + if (EntryVL.Var == EmittedEV.Var && + EntryVL.Locs[0].Value.RegNo == EmittedEV.Locs[0].Value.RegNo && + EntryVL.Expr == EmittedEV.Expr) { + OpenRanges.erase(EmittedEV); + EntryValTransfers.erase(TRInst); + break; + } + } + + return true; +} + /// Try to salvage the debug entry value if we encounter a new debug value /// describing the same parameter, otherwise stop tracking the value. Return -/// true if we should stop tracking the entry value, otherwise return false. +/// true if we should stop tracking the entry value and do the cleanup of +/// emitted Entry Value Transfers, otherwise return false. bool VarLocBasedLDV::removeEntryValue(const MachineInstr &MI, - OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, - const VarLoc &EntryVL) { + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, + const VarLoc &EntryVL, + TDPMap &EntryValTransfers, + RegSetterInstructionMap &RegSetInstrs) { // Skip the DBG_VALUE which is the debug entry value itself. - if (MI.isIdenticalTo(EntryVL.MI)) + if (&MI == &EntryVL.MI) return false; // If the parameter's location is not register location, we can not track - // the entry value any more. In addition, if the debug expression from the - // DBG_VALUE is not empty, we can assume the parameter's value has changed - // indicating that we should stop tracking its entry value as well. - if (!MI.getDebugOperand(0).isReg() || - MI.getDebugExpression()->getNumElements() != 0) - return true; + // the entry value any more. It doesn't have the TransferInst which defines + // register, so no Entry Value Transfers have been emitted already. + if (!MI.getDebugOperand(0).isReg()) + return true; - // If the DBG_VALUE comes from a copy instruction that copies the entry value, - // it means the parameter's value has not changed and we should be able to use - // its entry value. + // Try to get non-debug instruction responsible for the DBG_VALUE. + const MachineInstr *TransferInst = nullptr; Register Reg = MI.getDebugOperand(0).getReg(); - auto I = std::next(MI.getReverseIterator()); - const MachineOperand *SrcRegOp, *DestRegOp; - if (I != MI.getParent()->rend()) { + if (Reg.isValid() && RegSetInstrs.find(Reg) != RegSetInstrs.end()) + TransferInst = RegSetInstrs.find(Reg)->second; + // Case of the parameter's DBG_VALUE at the start of MBB. + if (!TransferInst && !LastNonDbgMI) + return false; + // If the debug expression from the DBG_VALUE is not empty, we can assume the + // parameter's value has changed indicating that we should stop tracking its + // entry value as well. + if (MI.getDebugExpression()->getNumElements() != 0) + return cleanupEntryValueTransfers(TransferInst, OpenRanges, VarLocIDs, + EntryVL, EntryValTransfers); + + if (TransferInst) { + + // If the DBG_VALUE comes from a copy instruction that copies the entry + // value, it means the parameter's value has not changed and we should be + // able to use its entry value. // TODO: Try to keep tracking of an entry value if we encounter a propagated // DBG_VALUE describing the copy of the entry value. (Propagated entry value // does not indicate the parameter modification.) - auto DestSrc = TII->isCopyInstr(*I); + auto DestSrc = TII->isCopyInstr(*TransferInst); if (!DestSrc) - return true; + return cleanupEntryValueTransfers(TransferInst, OpenRanges, VarLocIDs, + EntryVL, EntryValTransfers); + const MachineOperand *SrcRegOp, *DestRegOp; SrcRegOp = DestSrc->Source; DestRegOp = DestSrc->Destination; if (Reg != DestRegOp->getReg()) - return true; + return cleanupEntryValueTransfers(TransferInst, OpenRanges, VarLocIDs, + EntryVL, EntryValTransfers); for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)]; @@ -1273,14 +1332,17 @@ } } - return true; + return cleanupEntryValueTransfers(TransferInst, OpenRanges, VarLocIDs, + EntryVL, EntryValTransfers); } /// End all previous ranges related to @MI and start a new range from @MI /// if it is a DBG_VALUE instr. void VarLocBasedLDV::transferDebugValue(const MachineInstr &MI, - OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs) { + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, + TDPMap &EntryValTransfers, + RegSetterInstructionMap &RegSetInstrs) { if (!MI.isDebugValue()) return; const DILocalVariable *Var = MI.getDebugVariable(); @@ -1297,7 +1359,8 @@ auto EntryValBackupID = OpenRanges.getEntryValueBackup(V); if (Var->isParameter() && EntryValBackupID) { const VarLoc &EntryVL = VarLocIDs[EntryValBackupID->back()]; - if (removeEntryValue(MI, OpenRanges, VarLocIDs, EntryVL)) { + if (removeEntryValue(MI, OpenRanges, VarLocIDs, EntryVL, EntryValTransfers, + RegSetInstrs)) { LLVM_DEBUG(dbgs() << "Deleting a DBG entry value because of: "; MI.print(dbgs(), /*IsStandalone*/ false, /*SkipOpers*/ false, /*SkipDebugLoc*/ false, @@ -1351,7 +1414,7 @@ void VarLocBasedLDV::emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, - TransferMap &Transfers, + TDPMap &EntryValTransfers, VarLocsInRange &KillSet) { // Do not insert entry value locations after a terminator. if (MI.isTerminator()) @@ -1377,7 +1440,7 @@ VarLoc EntryLoc = VarLoc::CreateEntryLoc(EntryVL.MI, LS, EntryVL.Expr, EntryVL.Locs[0].Value.RegNo); LocIndices EntryValueIDs = VarLocIDs.insert(EntryLoc); - Transfers.push_back({&MI, EntryValueIDs.back()}); + EntryValTransfers.insert({&MI, EntryValueIDs.back()}); OpenRanges.insert(EntryValueIDs, EntryLoc); } } @@ -1456,7 +1519,7 @@ /// A definition of a register may mark the end of a range. void VarLocBasedLDV::transferRegisterDef( MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, - TransferMap &Transfers) { + TDPMap &EntryValTransfers, RegSetterInstructionMap &RegSetInstrs) { // Meta Instructions do not affect the debug liveness of any register they // define. @@ -1479,6 +1542,9 @@ for (MCRegAliasIterator RAI(MO.getReg(), TRI, true); RAI.isValid(); ++RAI) // FIXME: Can we break out of this loop early if no insertion occurs? DeadRegs.insert(*RAI); + if (RegSetInstrs.find(MO.getReg()) != RegSetInstrs.end()) + RegSetInstrs.erase(MO.getReg()); + RegSetInstrs.insert({MO.getReg(), &MI}); } else if (MO.isRegMask()) { RegMasks.push_back(MO.getRegMask()); } @@ -1505,6 +1571,11 @@ }); if (AnyRegMaskKillsReg) DeadRegs.insert(Reg); + if (AnyRegMaskKillsReg) { + if (RegSetInstrs.find(Reg) != RegSetInstrs.end()) + RegSetInstrs.erase(Reg); + RegSetInstrs.insert({Reg, &MI}); + } } } @@ -1518,7 +1589,7 @@ if (TPC) { auto &TM = TPC->getTM(); if (TM.Options.ShouldEmitDebugEntryValues()) - emitEntryValues(MI, OpenRanges, VarLocIDs, Transfers, KillSet); + emitEntryValues(MI, OpenRanges, VarLocIDs, EntryValTransfers, KillSet); } } @@ -1851,9 +1922,15 @@ /// This routine creates OpenRanges. void VarLocBasedLDV::process(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers) { - transferDebugValue(MI, OpenRanges, VarLocIDs); - transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers); + VarLocMap &VarLocIDs, TransferMap &Transfers, + TDPMap &EntryValTransfers, + RegSetterInstructionMap &RegSetInstrs) { + if (!MI.isDebugInstr()) + LastNonDbgMI = &MI; + transferDebugValue(MI, OpenRanges, VarLocIDs, EntryValTransfers, + RegSetInstrs); + transferRegisterDef(MI, OpenRanges, VarLocIDs, EntryValTransfers, + RegSetInstrs); transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers); transferSpillOrRestoreInst(MI, OpenRanges, VarLocIDs, Transfers); } @@ -2079,6 +2156,10 @@ VarLocInMBB InLocs; // Ranges that are incoming after joining. TransferMap Transfers; // DBG_VALUEs associated with transfers (such as // spills, copies and restores). + // Map responsible MI to attached Transfer emitted from Backup Entry Value. + TDPMap EntryValTransfers; + // Map a Register to the last MI which clobbered it. + RegSetterInstructionMap RegSetInstrs; VarToFragments SeenFragments; @@ -2175,8 +2256,11 @@ // operate with registers that correspond to user variables. // First load any pending inlocs. OpenRanges.insertFromLocSet(getVarLocsInMBB(MBB, InLocs), VarLocIDs); + LastNonDbgMI = nullptr; + RegSetInstrs.clear(); for (auto &MI : *MBB) - process(MI, OpenRanges, VarLocIDs, Transfers); + process(MI, OpenRanges, VarLocIDs, Transfers, EntryValTransfers, + RegSetInstrs); OLChanged |= transferTerminator(MBB, OpenRanges, OutLocs, VarLocIDs); LLVM_DEBUG(printVarLocInMBB(MF, OutLocs, VarLocIDs, @@ -2199,6 +2283,17 @@ assert(Pending.empty() && "Pending should be empty"); } + // Add DBG_VALUEs created using Backup Entry Value location. + for (auto &TR : EntryValTransfers) { + MachineInstr *TRInst = const_cast(TR.first); + assert(!TRInst->isTerminator() && "Cannot insert DBG_VALUE after terminator"); + MachineBasicBlock *MBB = TRInst->getParent(); + const VarLoc &VL = VarLocIDs[TR.second]; + MachineInstr *MI = VL.BuildDbgValue(MF); + MBB->insertAfterBundle(TRInst->getIterator(), MI); + } + EntryValTransfers.clear(); + // Add any DBG_VALUE instructions created by location transfers. for (auto &TR : Transfers) { assert(!TR.TransferInst->isTerminator() && Index: llvm/test/DebugInfo/MIR/X86/backup-entry-values-usage.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/X86/backup-entry-values-usage.mir @@ -0,0 +1,106 @@ +# RUN: llc -run-pass=livedebugvalues -emit-call-site-info -march=x86-64 -o - %s | FileCheck %s + +## Test from PR47628. +## Compiled from source: +## extern void bar(int); +## extern int side_effect, value; +## int foo(int param) { +## side_effect = param; +## param = value; +## bar(param); +## return 0; +## } +## Using command: +## $ clang m.c -O1 -g -c -mllvm -stop-before=livedebugvalues + +## Confirm that Backup Entry Value is not used for modified parameter "param". + +# CHECK: ![[PARAM:.*]] = !DILocalVariable(name: "param" +# CHECK: renamable $edi = MOV32rm $rip, 1, $noreg +# CHECK-NOT: DBG_VALUE $edi, $noreg, ![[PARAM]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# CHECK-NEXT: DBG_VALUE $edi, $noreg, ![[PARAM]], !DIExpression() +# CHECK-NEXT: CALL64pcrel32 @bar +# CHECK-NOT: DBG_VALUE $edi, $noreg, ![[PARAM]], !DIExpression(DW_OP_LLVM_entry_value, 1) + +--- | + ; ModuleID = 'm.c' + source_filename = "m.c" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + @side_effect = external dso_local local_unnamed_addr global i32, align 4 + @value = external dso_local local_unnamed_addr global i32, align 4 + + ; Function Attrs: nounwind uwtable + define dso_local i32 @foo(i32 %param) local_unnamed_addr !dbg !8 { + entry: + call void @llvm.dbg.value(metadata i32 %param, metadata !13, metadata !DIExpression()), !dbg !14 + store i32 %param, i32* @side_effect, align 4, !dbg !15, !tbaa !16 + %0 = load i32, i32* @value, align 4, !dbg !20, !tbaa !16 + call void @llvm.dbg.value(metadata i32 %0, metadata !13, metadata !DIExpression()), !dbg !14 + call void @bar(i32 %0), !dbg !21 + ret i32 0, !dbg !22 + } + + declare !dbg !23 dso_local void @bar(i32) local_unnamed_addr + + ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5, !6} + !llvm.ident = !{!7} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 13.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "m.c", directory: "/dir") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{i32 7, !"uwtable", i32 1} + !7 = !{!"clang version 13.0.0"} + !8 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 4, type: !9, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) + !9 = !DISubroutineType(types: !10) + !10 = !{!11, !11} + !11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !12 = !{!13} + !13 = !DILocalVariable(name: "param", arg: 1, scope: !8, file: !1, line: 4, type: !11) + !14 = !DILocation(line: 0, scope: !8) + !15 = !DILocation(line: 5, column: 17, scope: !8) + !16 = !{!17, !17, i64 0} + !17 = !{!"int", !18, i64 0} + !18 = !{!"omnipotent char", !19, i64 0} + !19 = !{!"Simple C/C++ TBAA"} + !20 = !DILocation(line: 6, column: 13, scope: !8) + !21 = !DILocation(line: 7, column: 5, scope: !8) + !22 = !DILocation(line: 8, column: 5, scope: !8) + !23 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 1, type: !24, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !24 = !DISubroutineType(types: !25) + !25 = !{null, !11} + +... +--- +name: foo +alignment: 16 +liveins: + - { reg: '$edi', virtual-reg: '' } +callSites: + - { bb: 0, offset: 6, fwdArgRegs: + - { arg: 0, reg: '$edi' } } +body: | + bb.0.entry: + liveins: $edi + + DBG_VALUE $edi, $noreg, !13, !DIExpression(), debug-location !14 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + MOV32mr $rip, 1, $noreg, @side_effect, $noreg, killed renamable $edi, debug-location !15 :: (store 4 into @side_effect, !tbaa !16) + renamable $edi = MOV32rm $rip, 1, $noreg, @value, $noreg, debug-location !20 :: (dereferenceable load 4 from @value, !tbaa !16) + DBG_VALUE $edi, $noreg, !13, !DIExpression(), debug-location !14 + CALL64pcrel32 @bar, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp, debug-location !21 + $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !22 + $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !22 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !22 + RETQ killed $eax, debug-location !22 + +... Index: llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir =================================================================== --- llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir +++ llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir @@ -109,7 +109,6 @@ frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp CFI_INSTRUCTION def_cfa_offset 16 $rsi = MOV64rr $rsp - DBG_VALUE $rdi, $noreg, !32, !DIExpression(), debug-location !35 CALL64pcrel32 @_ZN1CC2E1B, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, debug-location !36 $rax = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !36 CFI_INSTRUCTION def_cfa_offset 8, debug-location !36 Index: llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir =================================================================== --- llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir +++ llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir @@ -1,5 +1,5 @@ -# RUN: llc -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s -# RUN: llc -force-instr-ref-livedebugvalues=1 -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s +# RUN: llc -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s --check-prefixes=CHECK,VARLOCLDV +# RUN: llc -force-instr-ref-livedebugvalues=1 -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s --check-prefixes=CHECK,INSTRREFLDV # #extern void fn1 (int, int, int); # @@ -20,8 +20,11 @@ # CHECK: ![[ARG_A:.*]] = !DILocalVariable(name: "a" # CHECK: ![[ARG_B:.*]] = !DILocalVariable(name: "b" # CHECK: ![[ARG_C:.*]] = !DILocalVariable(name: "c" +## TODO: Support KILL instruction, which doesn't clobber parameter value. +# VARLOCLDV: DBG_VALUE $edi, $noreg, ![[ARG_A]], !DIExpression() # CHECK: DBG_VALUE $edx, $noreg, ![[ARG_C]], !DIExpression(DW_OP_LLVM_entry_value, 1) -# CHECK: DBG_VALUE $edi, $noreg, ![[ARG_A]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# VARLOCLDV-NOT: DBG_VALUE $edi, $noreg, ![[ARG_A]], !DIExpression(DW_OP_LLVM_entry_value, 1) +# INSTRREFLDV: DBG_VALUE $edi, $noreg, ![[ARG_A]], !DIExpression(DW_OP_LLVM_entry_value, 1) # CHECK-NOT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1) # --- |