diff --git a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp @@ -155,6 +155,7 @@ #include #include #include +#include #include #include #include @@ -284,6 +285,8 @@ LexicalScopes LS; VarLocSet::Allocator Alloc; + const MachineInstr *LastNonDbgMI; + enum struct TransferKind { TransferCopy, TransferSpill, TransferRestore }; using FragmentInfo = DIExpression::FragmentInfo; @@ -789,6 +792,10 @@ LocIndex LocationID; ///< Location number for the transfer dest. }; using TransferMap = SmallVector; + // Types for recording Entry Var Locations emitted by a single MachineInstr, + // as well as recording MachineInstr which last defined a register. + using InstToEntryLocMap = std::multimap; + using RegDefToInstMap = 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 @@ -962,13 +969,22 @@ Register NewReg = Register()); void transferDebugValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs); + VarLocMap &VarLocIDs, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &RegSetInstrs); void transferSpillOrRestoreInst(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers); - bool removeEntryValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, const VarLoc &EntryVL); + void cleanupEntryValueTransfers(const MachineInstr *MI, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, const VarLoc &EntryVL, + InstToEntryLocMap &EntryValTransfers); + void removeEntryValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, const VarLoc &EntryVL, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &RegSetInstrs); void emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers, + VarLocMap &VarLocIDs, + InstToEntryLocMap &EntryValTransfers, VarLocsInRange &KillSet); void recordEntryValue(const MachineInstr &MI, const DefinedRegsSet &DefinedRegs, @@ -976,12 +992,16 @@ void transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers); void transferRegisterDef(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers); + VarLocMap &VarLocIDs, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &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, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &RegSetInstrs); void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments, OverlapMap &OLapMap); @@ -1214,62 +1234,100 @@ return {Reg, Offset}; } +/// Do cleanup of \p EntryValTransfers created by \p TRInst, by removing the +/// Transfer, which uses the to-be-deleted \p EntryVL. +void VarLocBasedLDV::cleanupEntryValueTransfers( + const MachineInstr *TRInst, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, + const VarLoc &EntryVL, InstToEntryLocMap &EntryValTransfers) { + if (EntryValTransfers.empty() || TRInst == nullptr) + return; + + auto TransRange = EntryValTransfers.equal_range(TRInst); + for (auto TDPair : llvm::make_range(TransRange.first, TransRange.second)) { + const VarLoc &EmittedEV = VarLocIDs[TDPair.second]; + if (std::tie(EntryVL.Var, EntryVL.Locs[0].Value.RegNo, EntryVL.Expr) == + std::tie(EmittedEV.Var, EmittedEV.Locs[0].Value.RegNo, + EmittedEV.Expr)) { + OpenRanges.erase(EmittedEV); + EntryValTransfers.erase(TRInst); + break; + } + } +} + /// 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. -bool VarLocBasedLDV::removeEntryValue(const MachineInstr &MI, - OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, - const VarLoc &EntryVL) { +/// true if we should stop tracking the entry value and do the cleanup of +/// emitted Entry Value Transfers, otherwise return false. +void VarLocBasedLDV::removeEntryValue(const MachineInstr &MI, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, + const VarLoc &EntryVL, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &RegSetInstrs) { // Skip the DBG_VALUE which is the debug entry value itself. - if (MI.isIdenticalTo(EntryVL.MI)) - return false; + if (&MI == &EntryVL.MI) + return; // 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; - - // 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. + // 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; + + // 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 entry MBB. + if (!TransferInst && !LastNonDbgMI && MI.getParent()->isEntryBlock()) + return; + + // 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 && 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); - if (!DestSrc) - return true; - - SrcRegOp = DestSrc->Source; - DestRegOp = DestSrc->Destination; - if (Reg != DestRegOp->getReg()) - return true; - - for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { - const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)]; - if (VL.isEntryValueCopyBackupReg(Reg) && - // Entry Values should not be variadic. - VL.MI.getDebugOperand(0).getReg() == SrcRegOp->getReg()) - return false; + auto DestSrc = TII->isCopyInstr(*TransferInst); + if (DestSrc) { + const MachineOperand *SrcRegOp, *DestRegOp; + SrcRegOp = DestSrc->Source; + DestRegOp = DestSrc->Destination; + if (Reg == DestRegOp->getReg()) { + for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { + const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)]; + if (VL.isEntryValueCopyBackupReg(Reg) && + // Entry Values should not be variadic. + VL.MI.getDebugOperand(0).getReg() == SrcRegOp->getReg()) + return; + } + } } } - return true; + LLVM_DEBUG(dbgs() << "Deleting a DBG entry value because of: "; + MI.print(dbgs(), /*IsStandalone*/ false, + /*SkipOpers*/ false, /*SkipDebugLoc*/ false, + /*AddNewLine*/ true, TII)); + cleanupEntryValueTransfers(TransferInst, OpenRanges, VarLocIDs, EntryVL, + EntryValTransfers); + OpenRanges.erase(EntryVL); } /// 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, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &RegSetInstrs) { if (!MI.isDebugValue()) return; const DILocalVariable *Var = MI.getDebugVariable(); @@ -1286,13 +1344,8 @@ auto EntryValBackupID = OpenRanges.getEntryValueBackup(V); if (Var->isParameter() && EntryValBackupID) { const VarLoc &EntryVL = VarLocIDs[EntryValBackupID->back()]; - if (removeEntryValue(MI, OpenRanges, VarLocIDs, EntryVL)) { - LLVM_DEBUG(dbgs() << "Deleting a DBG entry value because of: "; - MI.print(dbgs(), /*IsStandalone*/ false, - /*SkipOpers*/ false, /*SkipDebugLoc*/ false, - /*AddNewLine*/ true, TII)); - OpenRanges.erase(EntryVL); - } + removeEntryValue(MI, OpenRanges, VarLocIDs, EntryVL, EntryValTransfers, + RegSetInstrs); } if (all_of(MI.debug_operands(), [](const MachineOperand &MO) { @@ -1340,7 +1393,7 @@ void VarLocBasedLDV::emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, - TransferMap &Transfers, + InstToEntryLocMap &EntryValTransfers, VarLocsInRange &KillSet) { // Do not insert entry value locations after a terminator. if (MI.isTerminator()) @@ -1366,7 +1419,9 @@ 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()}); + assert(EntryValueIDs.size() == 1 && + "EntryValue loc should not be variadic"); + EntryValTransfers.insert({&MI, EntryValueIDs.back()}); OpenRanges.insert(EntryValueIDs, EntryLoc); } } @@ -1443,9 +1498,11 @@ } /// A definition of a register may mark the end of a range. -void VarLocBasedLDV::transferRegisterDef( - MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, - TransferMap &Transfers) { +void VarLocBasedLDV::transferRegisterDef(MachineInstr &MI, + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &RegSetInstrs) { // Meta Instructions do not affect the debug liveness of any register they // define. @@ -1468,6 +1525,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()); } @@ -1494,6 +1554,11 @@ }); if (AnyRegMaskKillsReg) DeadRegs.insert(Reg); + if (AnyRegMaskKillsReg) { + if (RegSetInstrs.find(Reg) != RegSetInstrs.end()) + RegSetInstrs.erase(Reg); + RegSetInstrs.insert({Reg, &MI}); + } } } @@ -1507,7 +1572,7 @@ if (TPC) { auto &TM = TPC->getTM(); if (TM.Options.ShouldEmitDebugEntryValues()) - emitEntryValues(MI, OpenRanges, VarLocIDs, Transfers, KillSet); + emitEntryValues(MI, OpenRanges, VarLocIDs, EntryValTransfers, KillSet); } } @@ -1840,9 +1905,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, + InstToEntryLocMap &EntryValTransfers, + RegDefToInstMap &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); } @@ -2070,6 +2141,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. + InstToEntryLocMap EntryValTransfers; + // Map a Register to the last MI which clobbered it. + RegDefToInstMap RegSetInstrs; VarToFragments SeenFragments; @@ -2166,8 +2241,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, @@ -2201,6 +2279,18 @@ } Transfers.clear(); + // 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(); + // Deferred inlocs will not have had any DBG_VALUE insts created; do // that now. flushPendingLocs(InLocs, VarLocIDs); diff --git a/llvm/test/DebugInfo/MIR/X86/backup-entry-values-usage.mir b/llvm/test/DebugInfo/MIR/X86/backup-entry-values-usage.mir new file mode 100644 --- /dev/null +++ b/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 + +... diff --git a/llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir b/llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir --- a/llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir +++ b/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 diff --git a/llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir b/llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir --- a/llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir +++ b/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) # --- | diff --git a/llvm/test/DebugInfo/MIR/X86/remove-entry-value-from-loop.mir b/llvm/test/DebugInfo/MIR/X86/remove-entry-value-from-loop.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/remove-entry-value-from-loop.mir @@ -0,0 +1,167 @@ +# RUN: llc -emit-call-site-info -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s + +## Compiled from source: +## __attribute__((noinline)) +## int foo (int x, int y, int a) { +## int i; +## for (i = 0; i < x * y; i++) { +## if (i < x) { +## a = a * x; +## break; +## } +## } +## return a; +## } +## Using commands: +## $ clang -g -O1 test.c -emit-llvm -S -o test.ll +## $ llc -emit-call-site-info -stop-after stackmap-liveness test.ll -o test.mir + +## Artificially added DBG_VALUE $esi, $noreg, !14, !DIExpression() into +## for.cond Basic Block to prevent further usage of Entry Value in the loop. + +# CHECK: ![[ARG_Y:.*]] = !DILocalVariable(name: "y" +# CHECK: for.cond: +# CHECK: DBG_VALUE $esi, $noreg, ![[ARG_Y]], !DIExpression() +# CHECK-NOT: DBG_VALUE $esi, $noreg, ![[ARG_Y]], !DIExpression(DW_OP_LLVM_entry_value, 1) +--- | + ; ModuleID = 'test.ll' + source_filename = "test.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" + + ; Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable + define dso_local i32 @foo(i32 %x, i32 %y, i32 %a) local_unnamed_addr !dbg !8 { + entry: + call void @llvm.dbg.value(metadata i32 %x, metadata !13, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 %y, metadata !14, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 %a, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 0, metadata !16, metadata !DIExpression()), !dbg !17 + %mul = mul nsw i32 %y, %x + call void @llvm.dbg.value(metadata i32 0, metadata !16, metadata !DIExpression()), !dbg !17 + %cmp9 = icmp sgt i32 %mul, 0, !dbg !18 + br i1 %cmp9, label %for.body.preheader, label %for.end, !dbg !21 + + for.body.preheader: ; preds = %entry + %0 = mul i32 %y, %x, !dbg !21 + br label %for.body, !dbg !21 + + for.cond: ; preds = %for.body + call void @llvm.dbg.value(metadata i32 undef, metadata !16, metadata !DIExpression()), !dbg !17 + %lsr.iv.next = add i32 %lsr.iv, -1, !dbg !18 + %exitcond.not = icmp eq i32 %lsr.iv.next, 0, !dbg !18 + br i1 %exitcond.not, label %for.end, label %for.body, !dbg !21, !llvm.loop !22 + + for.body: ; preds = %for.cond, %for.body.preheader + %lsr.iv = phi i32 [ %lsr.iv.next, %for.cond ], [ %0, %for.body.preheader ] + %1 = icmp sgt i32 %x, 0 + call void @llvm.dbg.value(metadata i32 undef, metadata !16, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 undef, metadata !16, metadata !DIExpression()), !dbg !17 + br i1 %1, label %if.then, label %for.cond, !dbg !26 + + if.then: ; preds = %for.body + %mul2 = mul nsw i32 %a, %x, !dbg !28 + call void @llvm.dbg.value(metadata i32 %mul2, metadata !15, metadata !DIExpression()), !dbg !17 + br label %for.end, !dbg !31 + + for.end: ; preds = %for.cond, %if.then, %entry + %a.addr.0 = phi i32 [ %mul2, %if.then ], [ %a, %entry ], [ %a, %for.cond ] + call void @llvm.dbg.value(metadata i32 %a.addr.0, metadata !15, metadata !DIExpression()), !dbg !17 + ret i32 %a.addr.0, !dbg !32 + } + + ; 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: "test.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: 2, type: !9, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) + !9 = !DISubroutineType(types: !10) + !10 = !{!11, !11, !11, !11} + !11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !12 = !{!13, !14, !15, !16} + !13 = !DILocalVariable(name: "x", arg: 1, scope: !8, file: !1, line: 2, type: !11) + !14 = !DILocalVariable(name: "y", arg: 2, scope: !8, file: !1, line: 2, type: !11) + !15 = !DILocalVariable(name: "a", arg: 3, scope: !8, file: !1, line: 2, type: !11) + !16 = !DILocalVariable(name: "i", scope: !8, file: !1, line: 3, type: !11) + !17 = !DILocation(line: 0, scope: !8) + !18 = !DILocation(line: 4, column: 17, scope: !19) + !19 = distinct !DILexicalBlock(scope: !20, file: !1, line: 4, column: 3) + !20 = distinct !DILexicalBlock(scope: !8, file: !1, line: 4, column: 3) + !21 = !DILocation(line: 4, column: 3, scope: !20) + !22 = distinct !{!22, !21, !23, !24, !25} + !23 = !DILocation(line: 9, column: 3, scope: !20) + !24 = !{!"llvm.loop.mustprogress"} + !25 = !{!"llvm.loop.unroll.disable"} + !26 = !DILocation(line: 5, column: 9, scope: !27) + !27 = distinct !DILexicalBlock(scope: !19, file: !1, line: 4, column: 31) + !28 = !DILocation(line: 6, column: 13, scope: !29) + !29 = distinct !DILexicalBlock(scope: !30, file: !1, line: 5, column: 16) + !30 = distinct !DILexicalBlock(scope: !27, file: !1, line: 5, column: 9) + !31 = !DILocation(line: 7, column: 7, scope: !29) + !32 = !DILocation(line: 10, column: 3, scope: !8) + +... +--- +name: foo +alignment: 16 +liveins: + - { reg: '$edi', virtual-reg: '' } + - { reg: '$esi', virtual-reg: '' } + - { reg: '$edx', virtual-reg: '' } +body: | + bb.0.entry: + successors: %bb.2(0x50000000), %bb.4(0x30000000) + liveins: $edi, $edx, $esi + + DBG_VALUE $edi, $noreg, !13, !DIExpression(), debug-location !17 + DBG_VALUE $esi, $noreg, !14, !DIExpression(), debug-location !17 + DBG_VALUE $edx, $noreg, !15, !DIExpression(), debug-location !17 + $eax = MOV32rr $edx + DBG_VALUE 0, $noreg, !16, !DIExpression(), debug-location !17 + DBG_VALUE $eax, $noreg, !15, !DIExpression(), debug-location !17 + renamable $esi = nsw IMUL32rr killed renamable $esi, renamable $edi, implicit-def dead $eflags + TEST32rr renamable $esi, renamable $esi, implicit-def $eflags, debug-location !18 + JCC_1 %bb.4, 14, implicit $eflags, debug-location !21 + + bb.2.for.body (align 16): + successors: %bb.3(0x04000000), %bb.1(0x7c000000) + liveins: $eax, $edi, $esi + + TEST32rr renamable $edi, renamable $edi, implicit-def $eflags + DBG_VALUE $noreg, $noreg, !16, !DIExpression(), debug-location !17 + JCC_1 %bb.3, 15, implicit $eflags, debug-location !26 + + bb.1.for.cond: + successors: %bb.4(0x04000000), %bb.2(0x7c000000) + liveins: $eax, $edi, $esi + + DBG_VALUE $esi, $noreg, !14, !DIExpression(), debug-location !17 + DBG_VALUE $noreg, $noreg, !16, !DIExpression(), debug-location !17 + renamable $esi = ADD32ri8 killed renamable $esi, -1, implicit-def $eflags, debug-location !18 + JCC_1 %bb.2, 5, implicit $eflags, debug-location !21 + + bb.4.for.end: + liveins: $eax + + DBG_VALUE $eax, $noreg, !15, !DIExpression(), debug-location !17 + RETQ $eax, debug-location !32 + + bb.3.if.then: + liveins: $eax, $edi + + renamable $eax = nsw IMUL32rr killed renamable $eax, killed renamable $edi, implicit-def dead $eflags, debug-location !28 + DBG_VALUE $eax, $noreg, !15, !DIExpression(), debug-location !17 + DBG_VALUE $eax, $noreg, !15, !DIExpression(), debug-location !17 + RETQ $eax, debug-location !32 + +...