Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2469,6 +2469,9 @@ /// it cannot be a simple register location. bool isComplex() const; + /// Return whether this is an operation with a constant offset. + bool isOpWithOffset() const; + /// Append \p Ops with operations to apply the \p Offset. static void appendOffset(SmallVectorImpl &Ops, int64_t Offset); Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -246,8 +246,8 @@ // a call site parameter expression and if that expression is just a register // location, emit it with addBReg and offset 0, because we should emit a DWARF // expression representing a value, rather than a location. - if (!isMemoryLocation() && !HasComplexExpression && (!isParameterValue() || - isEntryValue())) { + if ((!isMemoryLocation() && !HasComplexExpression && !isParameterValue()) || + isEntryValue()) { for (auto &Reg : DwarfRegs) { if (Reg.DwarfRegNo >= 0) addReg(Reg.DwarfRegNo, Reg.Comment); @@ -257,8 +257,8 @@ if (isEntryValue()) finalizeEntryValue(); - if (isEntryValue() && !isParameterValue() && DwarfVersion >= 4) - emitOp(dwarf::DW_OP_stack_value); + if (isEntryValue() && !isParameterValue() && isRegisterLocation()) + LocationKind = Implicit; DwarfRegs.clear(); return true; Index: llvm/lib/CodeGen/LiveDebugValues.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues.cpp +++ llvm/lib/CodeGen/LiveDebugValues.cpp @@ -286,7 +286,11 @@ } }; - using DebugParamMap = SmallDenseMap; + using DebugParamMap = + SmallDenseMap>; + using EntryValTransferMap = SmallDenseMap; + using VarLocMap = UniqueVector; using VarLocSet = SparseBitVector<>; using VarLocInMBB = SmallDenseMap; @@ -387,15 +391,23 @@ unsigned NewReg = 0); void transferDebugValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs); + VarLocMap &VarLocIDs, DebugParamMap &DebugEntryVals, + EntryValTransferMap &DebugEntryValsTransfers); void transferSpillOrRestoreInst(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers); + void removeOrSalvageEntryValue(const MachineInstr &MI, + const DILocalVariable *Var, + const DIExpression *Expr, + DebugParamMap &DebugEntryVals, + EntryValTransferMap &DebugEntryValsTransfers); void emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, DebugParamMap &DebugEntryVals, SparseBitVector<> &KillSet); void transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, TransferMap &Transfers); + VarLocMap &VarLocIDs, TransferMap &Transfers, + DebugParamMap &DebugEntryVals, + EntryValTransferMap &DebugEntryValsTransfers); void transferRegisterDef(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, DebugParamMap &DebugEntryVals); @@ -405,8 +417,8 @@ void process(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocInMBB &OutLocs, VarLocMap &VarLocIDs, TransferMap &Transfers, DebugParamMap &DebugEntryVals, - OverlapMap &OverlapFragments, - VarToFragments &SeenFragments); + EntryValTransferMap &DebugEntryValsTransfers, + OverlapMap &OverlapFragments, VarToFragments &SeenFragments); void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments, OverlapMap &OLapMap); @@ -583,11 +595,64 @@ return {Reg, Offset}; } +/// Try to salvage the debug entry value if encounter a new debug value +/// describing the same parameter or stop tracking it. +void LiveDebugValues::removeOrSalvageEntryValue( + const MachineInstr &MI, const DILocalVariable *Var, + const DIExpression *Expr, DebugParamMap &DebugEntryVals, + EntryValTransferMap &DebugEntryValsTransfers) { + const MachineInstr *DebugEntryValue = DebugEntryVals[Var].first; + assert(DebugEntryValue->getOperand(0).isReg() && + "Entry value must be register location"); + + // Skip the DBG_VALUE which is the debug entry value itself. + if (MI.isIdenticalTo(*DebugEntryValue)) + return; + + Register EntryValueReg = DebugEntryValue->getOperand(0).getReg(); + Register EnltyValueSecondReg = 0; + if (DebugEntryValsTransfers.count(DebugEntryValue)) + EnltyValueSecondReg = + DebugEntryValsTransfers[const_cast(DebugEntryValue)]; + + // Remove the debug entry value from the map if we encounter a different + // DBG_VALUE describing the same parameter, because that can imply that the + // parameter's value has changed, so we can not rely on its entry value any + // more. + if (MI.getOperand(0).isReg() && MI.getOperand(0).getReg() != EntryValueReg && + MI.getOperand(0).getReg() != EnltyValueSecondReg) { + DebugEntryVals.erase(Var); + return; + } + + // If the expression is empty and the DBG_VALUE comes from a copy like + // instruction, it means that we can consider that the value of the parameter + // has not changed, but it is rather moved around due to optimizations + // reasons. + if (Expr->getNumElements() == 0) + return; + + // If the DBG_VALUE has non-empty expression it means that the parameter's + // value got changed, but we can try salvaging that. + if (Expr->isOpWithOffset()) { + DIExpression *EntryValExpr = DebugEntryVals[Var].second; + EntryValExpr = DIExpression::prepend(Expr, DIExpression::ApplyOffset); + DebugEntryVals[Var].second = EntryValExpr; + } else { + LLVM_DEBUG(dbgs() << "Deliting a DBG entry value because of: "; + MI.print(dbgs(), /*IsStandalone*/ false, + /*SkipOpers*/ false, /*SkipDebugLoc*/ false, + /*AddNewLine*/ true, TII)); + DebugEntryVals.erase(Var); + } +} + /// End all previous ranges related to @MI and start a new range from @MI /// if it is a DBG_VALUE instr. -void LiveDebugValues::transferDebugValue(const MachineInstr &MI, - OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs) { +void LiveDebugValues::transferDebugValue( + const MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, + DebugParamMap &DebugEntryVals, + EntryValTransferMap &DebugEntryValsTransfers) { if (!MI.isDebugValue()) return; const DILocalVariable *Var = MI.getDebugVariable(); @@ -597,6 +662,11 @@ assert(Var->isValidLocationForIntrinsic(DebugLoc) && "Expected inlined-at fields to agree"); + // Check if we should stop tracking the debug entry value. + if (Var->isParameter() && DebugEntryVals.count(Var)) + removeOrSalvageEntryValue(MI, Var, Expr, DebugEntryVals, + DebugEntryValsTransfers); + // End all previous ranges of Var. DebugVariable V(Var, Expr, InlinedAt); OpenRanges.erase(V); @@ -636,9 +706,20 @@ if (!DebugEntryVals.count(CurrDebugInstr->getDebugVariable())) continue; - auto ParamDebugInstr = DebugEntryVals[CurrDebugInstr->getDebugVariable()]; + auto ParamDebugInstr = + DebugEntryVals[CurrDebugInstr->getDebugVariable()].first; DIExpression *NewExpr = DIExpression::prepend( ParamDebugInstr->getDebugExpression(), DIExpression::EntryValue); + + // Follow the modification of the debug entry value if any. + if (NewExpr->getNumElements() != 0) { + ArrayRef elements = NewExpr->getElements(); + SmallVector Ops; + Ops.append(elements.begin(), elements.end()); + NewExpr = DIExpression::prependOpcodes( + DebugEntryVals[CurrDebugInstr->getDebugVariable()].second, Ops); + } + MachineInstr *EntryValDbgMI = BuildMI(*MF, ParamDebugInstr->getDebugLoc(), ParamDebugInstr->getDesc(), ParamDebugInstr->isIndirectDebugValue(), @@ -945,11 +1026,12 @@ void LiveDebugValues::transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, - TransferMap &Transfers) { + TransferMap &Transfers, + DebugParamMap &DebugEntryVals, + EntryValTransferMap &DebugEntryValsTransfers) { const MachineOperand *SrcRegOp, *DestRegOp; - if (!TII->isCopyInstr(MI, SrcRegOp, DestRegOp) || !SrcRegOp->isKill() || - !DestRegOp->isDef()) + if (!TII->isCopyInstr(MI, SrcRegOp, DestRegOp)) return; auto isCalleSavedReg = [&](unsigned Reg) { @@ -959,6 +1041,9 @@ return false; }; + if (!DestRegOp->isDef()) + return; + Register SrcReg = SrcRegOp->getReg(); Register DestReg = DestRegOp->getReg(); @@ -970,6 +1055,19 @@ if (!isCalleSavedReg(DestReg)) return; + // Remember an entry value movement. If we encounter a new debug value of + // a parameter describing only a moving of the value around, rather then + // modifying it, we are still able to use the entry value if needed. + for (auto EnltyValue : DebugEntryVals) { + if (EnltyValue.second.first->getOperand(0).getReg() == SrcReg) { + DebugEntryValsTransfers[EnltyValue.second.first] = DestReg; + break; + } + } + + if (!SrcRegOp->isKill()) + return; + for (unsigned ID : OpenRanges.getVarLocs()) { if (VarLocIDs[ID].isDescribedByReg() == SrcReg) { insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, ID, @@ -1070,12 +1168,14 @@ VarLocInMBB &OutLocs, VarLocMap &VarLocIDs, TransferMap &Transfers, DebugParamMap &DebugEntryVals, + EntryValTransferMap &DebugEntryValsTransfers, OverlapMap &OverlapFragments, VarToFragments &SeenFragments) { - transferDebugValue(MI, OpenRanges, VarLocIDs); - transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers, - DebugEntryVals); - transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers); + transferDebugValue(MI, OpenRanges, VarLocIDs, DebugEntryVals, + DebugEntryValsTransfers); + transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers, DebugEntryVals); + transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers, DebugEntryVals, + DebugEntryValsTransfers); transferSpillOrRestoreInst(MI, OpenRanges, VarLocIDs, Transfers); } @@ -1275,6 +1375,7 @@ // Working set of currently collected debug variables mapped to DBG_VALUEs // representing candidates for production of debug entry values. DebugParamMap DebugEntryVals; + EntryValTransferMap DebugEntryValsTransfers; MachineBasicBlock &First_MBB = *(MF.begin()); // Only in the case of entry MBB collect DBG_VALUEs representing @@ -1289,10 +1390,15 @@ // parameters entry values. for (auto &MI : First_MBB) if (MI.isDebugValue() && !MI.getDebugLoc()->getInlinedAt() && + MI.isDebugValue() && MI.getDebugVariable()->isParameter() && !MI.isIndirectDebugValue() && IsRegOtherThanSPAndFP(MI.getOperand(0)) && !DebugEntryVals.count(MI.getDebugVariable()) && - MI.getDebugExpression()->getNumElements() == 0) - DebugEntryVals[MI.getDebugVariable()] = &MI; + MI.getDebugExpression()->getNumElements() == 0) { + DIExpression *EntryValueModificationExpr = + DIExpression::get(MF.getFunction().getContext(), {}); + DebugEntryVals[MI.getDebugVariable()] = + std::make_pair(&MI, EntryValueModificationExpr); + } // Initialize per-block structures and scan for fragment overlaps. for (auto &MBB : MF) { @@ -1351,7 +1457,8 @@ OpenRanges.insertFromLocSet(PendingInLocs[MBB], VarLocIDs); for (auto &MI : *MBB) process(MI, OpenRanges, OutLocs, VarLocIDs, Transfers, - DebugEntryVals, OverlapFragments, SeenFragments); + DebugEntryVals, DebugEntryValsTransfers, + OverlapFragments, SeenFragments); OLChanged |= transferTerminator(MBB, OpenRanges, OutLocs, VarLocIDs); // Add any DBG_VALUE instructions necessitated by spills. Index: llvm/lib/IR/DebugInfoMetadata.cpp =================================================================== --- llvm/lib/IR/DebugInfoMetadata.cpp +++ llvm/lib/IR/DebugInfoMetadata.cpp @@ -896,8 +896,7 @@ // entry values of a simple register location. One reason for this is that // we currently can't calculate the size of the resulting DWARF block for // other expressions. - return I->get() == expr_op_begin()->get() && I->getArg(0) == 1 && - getNumElements() == 2; + return I->get() == expr_op_begin()->get() && I->getArg(0) == 1; } case dwarf::DW_OP_LLVM_convert: case dwarf::DW_OP_LLVM_tag_offset: @@ -964,6 +963,21 @@ return false; } +bool DIExpression::isOpWithOffset() const { + if (getNumElements() == 0) + return false; + + if (getNumElements() == 3 && Elements[0] == dwarf::DW_OP_plus_uconst) + return true; + + if (getNumElements() == 4 && Elements[0] == dwarf::DW_OP_constu) + if (Elements[2] == dwarf::DW_OP_plus || + Elements[2] == dwarf::DW_OP_minus) + return true; + + return false; +} + Optional DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) { for (auto I = Start; I != End; ++I) Index: llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir =================================================================== --- /dev/null +++ llvm/test/DebugInfo/MIR/X86/entry-value-of-modified-param.mir @@ -0,0 +1,118 @@ +# RUN: llc -debug-entry-values -run-pass=livedebugvalues -march=x86-64 -o - %s | FileCheck %s +# +#extern void fn1 (int, int, int); +# +#__attribute__((noinline)) +#int +#fn2 (int a, int b, int c) { +# int q = 2 + a; +# +# fn1 (5, 6, q); +# +# b = b + 7; +# if (b < 17) +# return 1; +# else +# return 0; +#} +# +# CHECK: DBG_VALUE $edi, $noreg, !16, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $edx, $noreg, !18, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $edi, $noreg, !16, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $esi, $noreg, !17, !DIExpression(DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 7, DW_OP_stack_value), debug-location {{.*}} +# +--- | + ; ModuleID = 'test.c' + 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: noinline nounwind uwtable + define dso_local i32 @fn2(i32 %a, i32 %b, i32 %c) local_unnamed_addr !dbg !12 { + entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !16, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %c, metadata !18, metadata !DIExpression()), !dbg !20 + %add = add nsw i32 %a, 2, !dbg !21 + call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !20 + tail call void @fn1(i32 5, i32 6, i32 %add) #3, !dbg !22 + call void @llvm.dbg.value(metadata i32 %b, metadata !17, metadata !DIExpression(DW_OP_plus_uconst, 7, DW_OP_stack_value)), !dbg !20 + %cmp = icmp slt i32 %b, 10, !dbg !23 + %. = zext i1 %cmp to i32, !dbg !25 + ret i32 %., !dbg !26 + } + + declare !dbg !4 dso_local void @fn1(i32, i32, i32) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: "/dir") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "fn1", scope: !1, file: !1, line: 1, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7, !7, !7} + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 2, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 5, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) + !13 = !DISubroutineType(types: !14) + !14 = !{!7, !7, !7, !7} + !15 = !{!16, !17, !18, !19} + !16 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !1, line: 5, type: !7) + !17 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !1, line: 5, type: !7) + !18 = !DILocalVariable(name: "c", arg: 3, scope: !12, file: !1, line: 5, type: !7) + !19 = !DILocalVariable(name: "q", scope: !12, file: !1, line: 7, type: !7) + !20 = !DILocation(line: 0, scope: !12) + !21 = !DILocation(line: 7, column: 15, scope: !12) + !22 = !DILocation(line: 9, column: 5, scope: !12) + !23 = !DILocation(line: 12, column: 11, scope: !24) + !24 = distinct !DILexicalBlock(scope: !12, file: !1, line: 12, column: 9) + !25 = !DILocation(line: 0, scope: !24) + !26 = !DILocation(line: 16, column: 1, scope: !12) + +... +--- +name: fn2 +alignment: 16 +callSites: + - { bb: 0, offset: 14, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$edx' } } +body: | + bb.0.entry: + liveins: $edi, $esi, $rbx + + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + DBG_VALUE $esi, $noreg, !17, !DIExpression(), debug-location !20 + DBG_VALUE $edx, $noreg, !18, !DIExpression(), debug-location !20 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + CFI_INSTRUCTION offset $rbx, -16 + $ebx = MOV32rr $esi + DBG_VALUE $ebx, $noreg, !17, !DIExpression(), debug-location !20 + renamable $edi = KILL $edi, implicit-def $rdi + DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !20 + renamable $edx = LEA64_32r killed renamable $rdi, 1, $noreg, 2, $noreg, debug-location !21 + DBG_VALUE $edx, $noreg, !19, !DIExpression(), debug-location !20 + $edi = MOV32ri 5, debug-location !22 + $esi = MOV32ri 6, debug-location !22 + CALL64pcrel32 @fn1, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $edx, implicit-def $rsp, implicit-def $ssp, debug-location !22 + DBG_VALUE $ebx, $noreg, !17, !DIExpression(DW_OP_plus_uconst, 7, DW_OP_stack_value), debug-location !20 + renamable $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !23 + CMP32ri8 killed renamable $ebx, 10, implicit-def $eflags, debug-location !23 + renamable $al = SETCCr 12, implicit killed $eflags, implicit killed $eax, implicit-def $eax, debug-location !23 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !26 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !26 + RETQ $eax, debug-location !26 + +...