Index: llvm/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/lib/Transforms/Utils/Local.cpp +++ llvm/lib/Transforms/Utils/Local.cpp @@ -1736,6 +1736,40 @@ salvageDebugInfoForDbgValues(I, DbgUsers); } +/// Salvage the address component of \p DAI. +static void salvageDbgAssignAddress(DbgAssignIntrinsic *DAI) { + Instruction *I = dyn_cast(DAI->getAddress()); + // Only instructions can be salvaged at the moment. + if (!I) + return; + + assert(!DAI->getAddressExpression()->getFragmentInfo().hasValue() && + "address-expression shouldn't have fragment info"); + + // The address component of a dbg.assign cannot be variadic. + uint64_t CurrentLocOps = 0; + SmallVector AdditionalValues; + SmallVector Ops; + Value *NewV = salvageDebugInfoImpl(*I, CurrentLocOps, Ops, AdditionalValues); + + // Check if the salvage failed. + if (!NewV) + return; + + DIExpression *SalvagedExpr = DIExpression::appendOpsToArg( + DAI->getAddressExpression(), Ops, 0, /*StackValue=*/false); + assert(!SalvagedExpr->getFragmentInfo().hasValue() && + "address-expression shouldn't have fragment info"); + + // Salvage succeeds if no additional values are required. + if (AdditionalValues.empty()) { + DAI->setAddress(NewV); + DAI->setAddressExpression(SalvagedExpr); + } else { + DAI->setAddress(UndefValue::get(I->getType())); + } +} + void llvm::salvageDebugInfoForDbgValues( Instruction &I, ArrayRef DbgUsers) { // These are arbitrary chosen limits on the maximum number of values and the @@ -1746,14 +1780,25 @@ bool Salvaged = false; for (auto *DII : DbgUsers) { + bool DbgAssignAddrUse = false; + if (auto *DAI = dyn_cast(DII)) { + if (DAI->getAddress() == &I) { + DbgAssignAddrUse = true; + salvageDbgAssignAddress(DAI); + Salvaged = true; + } + if (DAI->getValue() != &I) + continue; + } + // Do not add DW_OP_stack_value for DbgDeclare and DbgAddr, because they // are implicitly pointing out the value as a DWARF memory location // description. bool StackValue = isa(DII); auto DIILocation = DII->location_ops(); - assert( - is_contained(DIILocation, &I) && - "DbgVariableIntrinsic must use salvaged instruction as its location"); + assert((is_contained(DIILocation, &I) || DbgAssignAddrUse) && + "DbgVariableIntrinsic must use salvaged " + "instruction as its location"); SmallVector AdditionalValues; // `I` may appear more than once in DII's location ops, and each use of `I` // must be updated in the DIExpression and potentially have additional @@ -1782,13 +1827,15 @@ bool IsValidSalvageExpr = SalvagedExpr->getNumElements() <= MaxExpressionSize; if (AdditionalValues.empty() && IsValidSalvageExpr) { DII->setExpression(SalvagedExpr); - } else if (isa(DII) && IsValidSalvageExpr && + } else if (isa(DII) && !isa(DII) && + IsValidSalvageExpr && DII->getNumVariableLocationOps() + AdditionalValues.size() <= MaxDebugArgs) { DII->addVariableLocationOps(AdditionalValues, SalvagedExpr); } else { // Do not salvage using DIArgList for dbg.addr/dbg.declare, as it is - // currently only valid for stack value expressions. + // currently only valid for stack value expressions. Do not salvage + // using DIArgList for dbg.assign yet. FIXME: support this. // Also do not salvage if the resulting DIArgList would contain an // unreasonably large number of values. Value *Undef = UndefValue::get(I.getOperand(0)->getType()); Index: llvm/test/DebugInfo/Generic/assignment-tracking/salvage-value.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/salvage-value.ll @@ -0,0 +1,72 @@ +; RUN: opt %s -S -o - -passes=instcombine | FileCheck %s + +;; Hand-written (the debug info doesn't necessarily make sense and isn't fully +;; formed). Test salvaging a dbg.assign value and address. Checks and comments +;; are inline. + +define dso_local void @fun(i32 %x, i32 %y, i32* %p) !dbg !7 { +entry: + %add = add nsw i32 %x, 1, !dbg !22 + call void @llvm.dbg.assign(metadata i32 %add, metadata !14, metadata !DIExpression(), metadata !28, metadata i32* %p, metadata !DIExpression()), !dbg !16 +;; %add is salvaged. +; CHECK: call void @llvm.dbg.assign(metadata i32 %x,{{.+}}metadata !DIExpression(DW_OP_plus_uconst, 1, DW_OP_stack_value),{{.+}}, metadata i32* %p, metadata !DIExpression()) + + %add1 = add nsw i32 %x, %y, !dbg !29 + call void @llvm.dbg.assign(metadata i32 %add1, metadata !14, metadata !DIExpression(), metadata !31, metadata i32* %p, metadata !DIExpression()), !dbg !16 +;; %add1 is not salvaged as it requires two values and DIArgList is +;; not (yet) supported for dbg.assigns. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 undef,{{.+}}, metadata !DIExpression(),{{.+}}, metadata i32* %p, metadata !DIExpression()) + + %arrayidx0 = getelementptr inbounds i32, i32* %p, i32 0 + call void @llvm.dbg.assign(metadata i32 %x, metadata !14, metadata !DIExpression(), metadata !17, metadata i32* %arrayidx0, metadata !DIExpression()), !dbg !16 +;; %arrayidx0 is salvaged (zero offset, so the gep is just replaced with %p). +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %x,{{.+}}, metadata !DIExpression(),{{.+}}, metadata i32* %p, metadata !DIExpression()) + + %arrayidx1 = getelementptr inbounds i32, i32* %p, i32 1 + call void @llvm.dbg.assign(metadata i32 %x, metadata !14, metadata !DIExpression(), metadata !18, metadata i32* %arrayidx1, metadata !DIExpression()), !dbg !16 +;; %arrayidx1 is salvaged. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %x,{{.+}}, metadata !DIExpression(),{{.+}}, metadata i32* %p, metadata !DIExpression(DW_OP_plus_uconst, 4)) + + %arrayidx2 = getelementptr inbounds i32, i32* %p, i32 %x + call void @llvm.dbg.assign(metadata i32 %x, metadata !14, metadata !DIExpression(), metadata !19, metadata i32* %arrayidx2, metadata !DIExpression()), !dbg !16 +;; Variadic DIExpressions for dbg.assign address component is not supported - +;; set undef. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %x,{{.+}}, metadata !DIExpression(),{{.+}}, metadata i32* undef, metadata !DIExpression()) + + ret void +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "fun", linkageName: "fun", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{null, !10, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12, !13, !14} +!12 = !DILocalVariable(name: "x", arg: 1, scope: !7, file: !1, line: 2, type: !10) +!13 = !DILocalVariable(name: "y", arg: 2, scope: !7, file: !1, line: 2, type: !10) +!14 = !DILocalVariable(name: "Local", scope: !7, file: !1, line: 3, type: !10) +!16 = !DILocation(line: 0, scope: !7) +!17 = distinct !DIAssignID() +!18 = distinct !DIAssignID() +!19 = distinct !DIAssignID() +!21 = !DILocation(line: 3, column: 3, scope: !7) +!22 = !DILocation(line: 3, column: 17, scope: !7) +!24 = !{!25, !25, i64 0} +!25 = !{!"int", !26, i64 0} +!26 = !{!"omnipotent char", !27, i64 0} +!27 = !{!"Simple C++ TBAA"} +!28 = distinct !DIAssignID() +!29 = !DILocation(line: 4, column: 13, scope: !7) +!31 = distinct !DIAssignID()