Index: llvm/lib/CodeGen/LiveDebugValues.cpp =================================================================== --- llvm/lib/CodeGen/LiveDebugValues.cpp +++ llvm/lib/CodeGen/LiveDebugValues.cpp @@ -89,6 +89,19 @@ return MI.getOperand(0).isReg() ? MI.getOperand(0).getReg() : Register(); } +static bool isRegOtherThanSPAndFP(const MachineOperand &Op, MachineInstr &MI, + const TargetRegisterInfo *TRI) { + if (!Op.isReg()) + return false; + + MachineFunction *MF = MI.getParent()->getParent(); + const TargetLowering *TLI = MF->getSubtarget().getTargetLowering(); + unsigned SP = TLI->getStackPointerRegisterToSaveRestore(); + Register FP = TRI->getFrameRegister(*MF); + + return Op.getReg() != SP && Op.getReg() != FP; +} + namespace { class LiveDebugValues : public MachineFunctionPass { @@ -205,7 +218,8 @@ RegisterKind, SpillLocKind, ImmediateKind, - EntryValueKind + EntryValueKind, + EntryValueCopyRegKind, } Kind = InvalidKind; /// The value location. Stored separately to avoid repeatedly @@ -227,7 +241,7 @@ assert(MI.isDebugValue() && "not a DBG_VALUE"); assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); if (int RegNo = isDbgValueDescribedByReg(MI)) { - Kind = MI.isDebugEntryValue() ? EntryValueKind : RegisterKind; + Kind = RegisterKind; Loc.RegNo = RegNo; } else if (MI.getOperand(0).isImm()) { Kind = ImmediateKind; @@ -239,8 +253,10 @@ Kind = ImmediateKind; Loc.CImm = MI.getOperand(0).getCImm(); } - assert((Kind != ImmediateKind || !MI.isDebugEntryValue()) && - "entry values must be register locations"); + + // We create the debug entry values from the factory functions rather than + // from this ctor. + assert(Kind != EntryValueKind && Kind != EntryValueCopyRegKind); } /// Take the variable and machine-location in DBG_VALUE MI, and build an @@ -253,6 +269,19 @@ return VL; } + /// Take the variable and the entry value expression from an entry DBG_VALUE, + /// and make a copy location of the debug entry value by setting the + /// register to the NewReg. + static VarLoc CreateCopyEntryLoc(const MachineInstr &MI, LexicalScopes &LS, + const DIExpression *EntryExpr, + unsigned NewReg) { + VarLoc VL(MI, LS); + VL.Kind = EntryValueCopyRegKind; + VL.Expr = EntryExpr; + VL.Loc.RegNo = NewReg; + return VL; + } + /// Copy the register location in DBG_VALUE MI, updating the register to /// be NewReg. static VarLoc CreateCopyLoc(const MachineInstr &MI, LexicalScopes &LS, @@ -287,9 +316,13 @@ switch (Kind) { case EntryValueKind: + case EntryValueCopyRegKind: // An entry value is a register location -- but with an updated - // expression. - return BuildMI(MF, DbgLoc, IID, Indirect, Loc.RegNo, Var, Expr); + // expression. The register location of such DBG_VALUE is always the one + // from the entry DBG_VALUE, it does not matter if the entry value was + // copied in to another register due to some optimizations. + return BuildMI(MF, DbgLoc, IID, Indirect, MI.getOperand(0).getReg(), + Var, Expr); case RegisterKind: // Register locations are like the source DBG_VALUE, but with the // register number from this VarLoc. @@ -319,7 +352,7 @@ /// If this variable is described by a register, return it, /// otherwise return 0. unsigned isDescribedByReg() const { - if (Kind == RegisterKind) + if (Kind == RegisterKind || Kind == EntryValueCopyRegKind) return Loc.RegNo; return 0; } @@ -335,6 +368,7 @@ switch (Kind) { case RegisterKind: case EntryValueKind: + case EntryValueCopyRegKind: dbgs() << printReg(Loc.RegNo, TRI); break; case SpillLocKind: @@ -368,7 +402,21 @@ } }; - using DebugParamMap = SmallDenseMap; + // Types for recording a parameter entry value. For a given parameter we + // record its debug entry value and potential movement of such value. + struct EntryValue { + // The debug entry value. + MachineInstr *DbgEntryValue; + + // A copy like instruction that moves the entry value around. + MachineInstr *DbgEntryValueMovement; + + void setDbgEntryValueMovement(MachineInstr *MI) { + DbgEntryValueMovement = MI; + } + }; + using DebugParamMap = SmallDenseMap; + using VarLocMap = UniqueVector; using VarLocSet = SparseBitVector<>; using VarLocInMBB = SmallDenseMap; @@ -469,15 +517,20 @@ unsigned NewReg = 0); void transferDebugValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs); + VarLocMap &VarLocIDs, DebugParamMap &DebugEntryVals); void transferSpillOrRestoreInst(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers); + void removeOrSalvageEntryValue(const MachineInstr &MI, + const DILocalVariable *Var, + const DIExpression *Expr, + DebugParamMap &DebugEntryVals); 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); void transferRegisterDef(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, DebugParamMap &DebugEntryVals); @@ -487,8 +540,7 @@ void process(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocInMBB &OutLocs, VarLocMap &VarLocIDs, TransferMap &Transfers, DebugParamMap &DebugEntryVals, - OverlapMap &OverlapFragments, - VarToFragments &SeenFragments); + OverlapMap &OverlapFragments, VarToFragments &SeenFragments); void accumulateFragmentMap(MachineInstr &MI, VarToFragments &SeenFragments, OverlapMap &OLapMap); @@ -665,11 +717,53 @@ 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) { + const MachineInstr *DebugEntryValue = + const_cast(DebugEntryVals[Var].DbgEntryValue); + 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; + + // Check if the DBG_VALUE describes the register holding a copy of the debug + // entry value. + if (DebugEntryVals[Var].DbgEntryValueMovement) { + Register EntryValueCopyReg = + DebugEntryVals[Var].DbgEntryValueMovement->getOperand(0).getReg(); + if (MI.getOperand(0).isReg() && + MI.getOperand(0).getReg() == EntryValueCopyReg) { + if (MI.getDebugExpression()->getNumElements() == 0) + return; + } + } + + // 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. + Register EntryValueReg = DebugEntryValue->getOperand(0).getReg(); + if (MI.getOperand(0).isReg() && MI.getOperand(0).getReg() != EntryValueReg) { + LLVM_DEBUG(dbgs() << "Deleting a DBG entry value because of: "; + MI.print(dbgs(), /*IsStandalone*/ false, + /*SkipOpers*/ false, /*SkipDebugLoc*/ false, + /*AddNewLine*/ true, TII)); + DebugEntryVals.erase(Var); + return; + } +} + /// 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) { if (!MI.isDebugValue()) return; const DILocalVariable *Var = MI.getDebugVariable(); @@ -679,6 +773,10 @@ 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); + // End all previous ranges of Var. DebugVariable V(Var, Expr, InlinedAt); OpenRanges.erase(V); @@ -714,14 +812,23 @@ // If parameter's DBG_VALUE is not in the map that means we can't // generate parameter's entry value. - if (!DebugEntryVals.count(CurrDebugInstr->getDebugVariable())) + auto ParamEntryValue = + DebugEntryVals.find(CurrDebugInstr->getDebugVariable()); + if (ParamEntryValue == DebugEntryVals.end()) continue; - auto ParamDebugInstr = DebugEntryVals[CurrDebugInstr->getDebugVariable()]; + auto ParamDebugInstr = ParamEntryValue->second.DbgEntryValue; DIExpression *NewExpr = DIExpression::prepend( ParamDebugInstr->getDebugExpression(), DIExpression::EntryValue); - VarLoc EntryLoc = VarLoc::CreateEntryLoc(*ParamDebugInstr, LS, NewExpr); + VarLoc EntryLoc = + DebugEntryVals[CurrDebugInstr->getDebugVariable()].DbgEntryValueMovement + ? VarLoc::CreateCopyEntryLoc( + *ParamDebugInstr, LS, NewExpr, + DebugEntryVals[CurrDebugInstr->getDebugVariable()] + .DbgEntryValueMovement->getOperand(0) + .getReg()) + : VarLoc::CreateEntryLoc(*ParamDebugInstr, LS, NewExpr); unsigned EntryValLocID = VarLocIDs.insert(EntryLoc); Transfers.push_back({&MI, EntryValLocID}); @@ -787,8 +894,6 @@ case TransferKind::TransferRestore: { assert(NewReg && "No register supplied when handling a restore of a debug value"); - MachineFunction *MF = MI.getMF(); - DIBuilder DIB(*const_cast(MF->getFunction()).getParent()); // DebugInstr refers to the pre-spill location, therefore we can reuse // its expression. VarLoc VL = VarLoc::CreateCopyLoc(*DebugInstr, LS, NewReg); @@ -999,11 +1104,11 @@ void LiveDebugValues::transferRegisterCopy(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, - TransferMap &Transfers) { + TransferMap &Transfers, + DebugParamMap &DebugEntryVals) { 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) { @@ -1013,6 +1118,9 @@ return false; }; + if (!DestRegOp->isDef()) + return; + Register SrcReg = SrcRegOp->getReg(); Register DestReg = DestRegOp->getReg(); @@ -1024,6 +1132,34 @@ if (!isCalleSavedReg(DestReg)) return; + auto isCopyOfTheEntryValue = [SrcReg](EntryValue &ParamEntryValue) { + // Check if it is a copy of the entry value register. + if (ParamEntryValue.DbgEntryValue->getOperand(0).getReg() == SrcReg) + return true; + + // Check if it is a copy of the register holding a copy of the entry value. + if (ParamEntryValue.DbgEntryValueMovement && + ParamEntryValue.DbgEntryValueMovement->getOperand(0).getReg() == SrcReg) + return true; + + return false; + }; + + // 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. + if (isRegOtherThanSPAndFP(*DestRegOp, MI, TRI)) { + for (auto &EnltyValue : DebugEntryVals) { + if (isCopyOfTheEntryValue(EnltyValue.second)) { + EnltyValue.second.setDbgEntryValueMovement(&MI); + break; + } + } + } + + if (!SrcRegOp->isKill()) + return; + for (unsigned ID : OpenRanges.getVarLocs()) { if (VarLocIDs[ID].isDescribedByReg() == SrcReg) { insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, ID, @@ -1123,10 +1259,9 @@ DebugParamMap &DebugEntryVals, OverlapMap &OverlapFragments, VarToFragments &SeenFragments) { - transferDebugValue(MI, OpenRanges, VarLocIDs); - transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers, - DebugEntryVals); - transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers); + transferDebugValue(MI, OpenRanges, VarLocIDs, DebugEntryVals); + transferRegisterDef(MI, OpenRanges, VarLocIDs, Transfers, DebugEntryVals); + transferRegisterCopy(MI, OpenRanges, VarLocIDs, Transfers, DebugEntryVals); transferSpillOrRestoreInst(MI, OpenRanges, VarLocIDs, Transfers); } @@ -1290,13 +1425,6 @@ std::greater> Pending; - const TargetLowering *TLI = MF.getSubtarget().getTargetLowering(); - unsigned SP = TLI->getStackPointerRegisterToSaveRestore(); - Register FP = TRI->getFrameRegister(MF); - auto IsRegOtherThanSPAndFP = [&](const MachineOperand &Op) -> bool { - return Op.isReg() && Op.getReg() != SP && Op.getReg() != FP; - }; - // Working set of currently collected debug variables mapped to DBG_VALUEs // representing candidates for production of debug entry values. DebugParamMap DebugEntryVals; @@ -1314,10 +1442,12 @@ // parameters entry values. for (auto &MI : First_MBB) if (MI.isDebugValue() && !MI.getDebugLoc()->getInlinedAt() && - !MI.isIndirectDebugValue() && IsRegOtherThanSPAndFP(MI.getOperand(0)) && + MI.isDebugValue() && MI.getDebugVariable()->isParameter() && + !MI.isIndirectDebugValue() && + isRegOtherThanSPAndFP(MI.getOperand(0), MI, TRI) && !DebugEntryVals.count(MI.getDebugVariable()) && MI.getDebugExpression()->getNumElements() == 0) - DebugEntryVals[MI.getDebugVariable()] = &MI; + DebugEntryVals[MI.getDebugVariable()] = EntryValue{&MI, nullptr}; // Initialize per-block structures and scan for fragment overlaps. for (auto &MBB : MF) { 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-NOT: 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 + +...