Index: llvm/include/llvm/CodeGen/FunctionLoweringInfo.h =================================================================== --- llvm/include/llvm/CodeGen/FunctionLoweringInfo.h +++ llvm/include/llvm/CodeGen/FunctionLoweringInfo.h @@ -77,6 +77,9 @@ /// cross-basic-block values. DenseMap ValueMap; + /// A mapping from the argument values to their virtual registers. + DenseMap ArgValueMap; + /// VirtReg2Value map is needed by the Divergence Analysis driven /// instruction selection. It is reverted ValueMap. It is computed /// in lazy style - on demand. It is used to get the Value corresponding Index: llvm/include/llvm/CodeGen/SelectionDAG.h =================================================================== --- llvm/include/llvm/CodeGen/SelectionDAG.h +++ llvm/include/llvm/CodeGen/SelectionDAG.h @@ -1474,7 +1474,8 @@ /// Creates a SDDbgValue node. SDDbgValue *getDbgValue(DIVariable *Var, DIExpression *Expr, SDNode *N, unsigned R, bool IsIndirect, const DebugLoc &DL, - unsigned O); + unsigned O, unsigned vRegForEntryValue = 0, + DIExpression *EntryExpr = nullptr); /// Creates a constant SDDbgValue node. SDDbgValue *getConstantDbgValue(DIVariable *Var, DIExpression *Expr, Index: llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -30,6 +30,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.h" #include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/Utils/Local.h" using namespace llvm; #define DEBUG_TYPE "instr-emitter" @@ -706,11 +707,23 @@ SD->setIsEmitted(); if (SD->isInvalidated()) { + Register Reg = SD->getVRegForEntryValue();; + DIExpression *EntryValueExpr = SD->getEntryExpression(); + + // It the node has been invalidated, try to salvage the value + // if an entry value is available for the variable. Entry values + // act as backups here, and it will end up in an expression with + // a DW_OP_entry_value. + + if (Reg) + Expr = DIExpression::prepend(cast(EntryValueExpr), + DIExpression::EntryValue); + // An invalidated SDNode must generate an undef DBG_VALUE: although the // original value is no longer computed, earlier DBG_VALUEs live ranges // must not leak into later code. auto MIB = BuildMI(*MF, DL, TII->get(TargetOpcode::DBG_VALUE)); - MIB.addReg(0U); + MIB.addReg(Reg, RegState::Debug); MIB.addReg(0U, RegState::Debug); MIB.addMetadata(Var); MIB.addMetadata(Expr); Index: llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h +++ llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h @@ -47,8 +47,10 @@ } u; DIVariable *Var; DIExpression *Expr; + DIExpression *EntryExpr; DebugLoc DL; unsigned Order; + unsigned VRegForEntryValue = 0; enum DbgValueKind kind; bool IsIndirect; bool Invalid = false; @@ -57,11 +59,14 @@ public: /// Constructor for non-constants. SDDbgValue(DIVariable *Var, DIExpression *Expr, SDNode *N, unsigned R, - bool indir, DebugLoc dl, unsigned O) - : Var(Var), Expr(Expr), DL(std::move(dl)), Order(O), IsIndirect(indir) { + bool indir, DebugLoc dl, unsigned O, unsigned vRegForEntryValue, + DIExpression *EntryExpr) + : Var(Var), Expr(Expr), EntryExpr(EntryExpr), DL(std::move(dl)), Order(O), + IsIndirect(indir) { kind = SDNODE; u.s.Node = N; u.s.ResNo = R; + VRegForEntryValue = vRegForEntryValue; } /// Constructor for constants. @@ -110,6 +115,12 @@ /// Returns the Virtual Register for a VReg unsigned getVReg() const { assert (kind==VREG); return u.VReg; } + /// Returns the Virtual Register for an entry value. + unsigned getVRegForEntryValue() const { return VRegForEntryValue; } + + /// Returns an expression that applies onto entry value. + DIExpression *getEntryExpression() const { return EntryExpr; } + /// Returns whether this is an indirect value. bool isIndirect() const { return IsIndirect; } Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -8279,11 +8279,14 @@ /// SDNode SDDbgValue *SelectionDAG::getDbgValue(DIVariable *Var, DIExpression *Expr, SDNode *N, unsigned R, bool IsIndirect, - const DebugLoc &DL, unsigned O) { + const DebugLoc &DL, unsigned O, + unsigned vRegForEntryValue, + DIExpression *EntryExpr) { assert(cast(Var)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree"); return new (DbgInfo->getAlloc()) - SDDbgValue(Var, Expr, N, R, IsIndirect, DL, O); + SDDbgValue(Var, Expr, N, R, IsIndirect, DL, O, vRegForEntryValue, + EntryExpr); } /// Constant Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -511,7 +511,9 @@ /// SelectionDAG. bool handleDebugValue(const Value *V, DILocalVariable *Var, DIExpression *Expr, DebugLoc CurDL, - DebugLoc InstDL, unsigned Order); + DebugLoc InstDL, unsigned Order, + const Value *EntryValue = nullptr, + DIExpression *EntryExpr = nullptr); /// Evict any dangling debug information, attempting to salvage it first. void resolveOrClearDbgInfo(); @@ -811,7 +813,9 @@ /// Return the appropriate SDDbgValue based on N. SDDbgValue *getDbgValue(SDValue N, DILocalVariable *Variable, DIExpression *Expr, const DebugLoc &dl, - unsigned DbgSDNodeOrder); + unsigned DbgSDNodeOrder, + unsigned vRegForEntryValue = 0, + DIExpression *EntryExpr = nullptr); /// Lowers CallInst to an external symbol. void lowerCallToExternalSymbol(const CallInst &I, const char *FunctionName); Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -1301,7 +1301,9 @@ bool SelectionDAGBuilder::handleDebugValue(const Value *V, DILocalVariable *Var, DIExpression *Expr, DebugLoc dl, - DebugLoc InstDL, unsigned Order) { + DebugLoc InstDL, unsigned Order, + const Value *EntryValue, + DIExpression *EntryExpr) { const TargetLowering &TLI = DAG.getTargetLoweringInfo(); SDDbgValue *SDV; if (isa(V) || isa(V) || isa(V) || @@ -1334,7 +1336,12 @@ if (N.getNode()) { if (EmitFuncArgumentDbgValue(V, Var, Expr, dl, false, N)) return true; - SDV = getDbgValue(N, Var, Expr, dl, SDNodeOrder); + unsigned EntryValueReg = 0; + auto EVMI = FuncInfo.ArgValueMap.find(EntryValue); + if (EVMI != FuncInfo.ArgValueMap.end()) + EntryValueReg = EVMI->second; + SDV = getDbgValue(N, Var, Expr, dl, SDNodeOrder, EntryValueReg, + EntryExpr); DAG.AddDbgValue(SDV, N.getNode(), false); return true; } @@ -5470,6 +5477,11 @@ Op = MachineOperand::CreateReg(Reg, false); IsIndirect = IsDbgDeclare; } + + // Map the register into the Value, so it can be used for debug + // info recovering. + if (FuncInfo.ArgValueMap.find(V) == FuncInfo.ArgValueMap.end()) + FuncInfo.ArgValueMap[V] = Reg; } if (!Op && N.getNode()) { @@ -5562,7 +5574,9 @@ DILocalVariable *Variable, DIExpression *Expr, const DebugLoc &dl, - unsigned DbgSDNodeOrder) { + unsigned DbgSDNodeOrder, + unsigned vRegForEntryValue, + DIExpression *EntryExpr) { if (auto *FISDN = dyn_cast(N.getNode())) { // Construct a FrameIndexDbgValue for FrameIndexSDNodes so we can describe // stack slot locations. @@ -5578,7 +5592,8 @@ /*IsIndirect*/ false, dl, DbgSDNodeOrder); } return DAG.getDbgValue(Variable, Expr, N.getNode(), N.getResNo(), - /*IsIndirect*/ false, dl, DbgSDNodeOrder); + /*IsIndirect*/ false, dl, DbgSDNodeOrder, + vRegForEntryValue, EntryExpr); } static unsigned FixedPointIntrinsicToOpcode(unsigned Intrinsic) { @@ -5954,8 +5969,11 @@ if (!V) return; + const Value *EntryValue = DI.getVariableEntryValue(); + DIExpression *EntryExpression = DI.getEntryExpression(); + if (handleDebugValue(V, Variable, Expression, dl, DI.getDebugLoc(), - SDNodeOrder)) + SDNodeOrder, EntryValue, EntryExpression)) return; // TODO: Dangling debug info will eventually either be resolved or produce Index: llvm/test/DebugInfo/X86/entry-val-invalidated-node-modifed-param-with-add.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/entry-val-invalidated-node-modifed-param-with-add.ll @@ -0,0 +1,71 @@ +; RUN: llc < %s -O2 -stop-before=finalize-isel | FileCheck %s +; RUN: llc -O2 %s -o %t -filetype=obj +; RUN: llvm-dwarfdump %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP + +; C producer: +; void baz(); +; void bar(); +; +; void foo(int param, int param2) { +; baz(); +; param = param2 + 2; +; bar(); +; } + +; $ clang -g -O2 test.c -S -emit-llvm + +; CHECK: DBG_VALUE $edi, $noreg, !12, !DIExpression() +; CHECK: DBG_VALUE $esi, $noreg, !12, !DIExpression(DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 2, DW_OP_stack_value) + +; CHECK-DWARFDUMP: DW_OP_GNU_entry_value(DW_OP_reg4 RSI), DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_plus_uconst 0x2, DW_OP_stack_value +; CHECK-DWARFDUMP: DW_OP_GNU_entry_value(DW_OP_reg4 RSI), DW_OP_stack_value + +; 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: nounwind uwtable +define dso_local void @foo(i32 %param, i32 %param2) local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 %param, metadata !12, metadata !DIExpression(), metadata i32 %param, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32 %param2, metadata !13, metadata !DIExpression(), metadata i32 %param2, metadata !DIExpression()), !dbg !14 + tail call void (...) @baz(), !dbg !15 + call void @llvm.dbg.value(metadata i32 %param2, metadata !12, metadata !DIExpression(DW_OP_plus_uconst, 2, DW_OP_stack_value), metadata i32 %param2, metadata !DIExpression(DW_OP_plus_uconst, 2, DW_OP_stack_value)), !dbg !14 + tail call void (...) @bar(), !dbg !16 + ret void, !dbg !17 +} + +declare !dbg !18 dso_local void @baz(...) local_unnamed_addr + +declare !dbg !21 dso_local void @bar(...) local_unnamed_addr + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test2.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, 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} +!12 = !DILocalVariable(name: "param", arg: 1, scope: !7, file: !1, line: 4, type: !10) +!13 = !DILocalVariable(name: "param2", arg: 2, scope: !7, file: !1, line: 4, type: !10) +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocation(line: 5, column: 5, scope: !7) +!16 = !DILocation(line: 7, column: 5, scope: !7) +!17 = !DILocation(line: 8, column: 1, scope: !7) +!18 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 1, type: !19, spFlags: DISPFlagOptimized, retainedNodes: !2) +!19 = !DISubroutineType(types: !20) +!20 = !{null, null} +!21 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 2, type: !19, spFlags: DISPFlagOptimized, retainedNodes: !2) Index: llvm/test/DebugInfo/X86/entry-val-invalidated-node-modifed-param.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/entry-val-invalidated-node-modifed-param.ll @@ -0,0 +1,71 @@ +; RUN: llc < %s -O2 -stop-before=finalize-isel | FileCheck %s +; RUN: llc -O2 %s -o %t -filetype=obj +; RUN: llvm-dwarfdump %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP + +; C producer: +; void baz(); +; void bar(); +; +; void foo(int param, int param2) { +; baz(); +; param = param2; +; bar(); +; } + +; $ clang -g -O2 test.c -S -emit-llvm + +; CHECK: DBG_VALUE $edi, $noreg, !12, !DIExpression() +; CHECK: DBG_VALUE $esi, $noreg, !12, !DIExpression(DW_OP_LLVM_entry_value, 1) + +; CHECK-DWARFDUMP: DW_OP_GNU_entry_value(DW_OP_reg4 RSI), DW_OP_stack_value +; CHECK-DWARFDUMP: DW_OP_GNU_entry_value(DW_OP_reg4 RSI), DW_OP_stack_value + +; 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: nounwind uwtable +define dso_local void @foo(i32 %param, i32 %param2) local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 %param, metadata !12, metadata !DIExpression(), metadata i32 %param, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32 %param2, metadata !13, metadata !DIExpression(), metadata i32 %param2, metadata !DIExpression()), !dbg !14 + tail call void (...) @baz(), !dbg !15 + call void @llvm.dbg.value(metadata i32 %param2, metadata !12, metadata !DIExpression(), metadata i32 %param2, metadata !DIExpression()), !dbg !14 + tail call void (...) @bar(), !dbg !16 + ret void, !dbg !17 +} + +declare !dbg !18 dso_local void @baz(...) local_unnamed_addr + +declare !dbg !21 dso_local void @bar(...) local_unnamed_addr + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, 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} +!12 = !DILocalVariable(name: "param", arg: 1, scope: !7, file: !1, line: 4, type: !10) +!13 = !DILocalVariable(name: "param2", arg: 2, scope: !7, file: !1, line: 4, type: !10) +!14 = !DILocation(line: 0, scope: !7) +!15 = !DILocation(line: 5, column: 5, scope: !7) +!16 = !DILocation(line: 7, column: 5, scope: !7) +!17 = !DILocation(line: 8, column: 1, scope: !7) +!18 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 1, type: !19, spFlags: DISPFlagOptimized, retainedNodes: !2) +!19 = !DISubroutineType(types: !20) +!20 = !{null, null} +!21 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 2, type: !19, spFlags: DISPFlagOptimized, retainedNodes: !2) Index: llvm/test/DebugInfo/X86/entry-values-for-isel-invalidated-nodes.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/entry-values-for-isel-invalidated-nodes.ll @@ -0,0 +1,61 @@ +; RUN: llc < %s -O2 -stop-before=finalize-isel | FileCheck %s +; RUN: llc -O2 %s -o %t -filetype=obj +; RUN: llvm-dwarfdump %t | FileCheck %s --check-prefix=CHECK-DWARFDUMP + +; C producer: +; void f1(int); +; void f2(int i) { +; f1(1); +; i = i + 5; +; f1(3); +; } +; $ clang -g -O2 test.c -S -emit-llvm + +; CHECK: DBG_VALUE $edi, $noreg, !{{.*}}, !DIExpression() +; CHECK: DBG_VALUE $edi, $noreg, !{{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 5, DW_OP_stack_value) + +; CHECK-DWARFDUMP: DW_OP_GNU_entry_value(DW_OP_reg5 RDI), DW_OP_stack_value +; CHECK-DWARFDUMP: DW_OP_GNU_entry_value(DW_OP_reg5 RDI), DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_plus_uconst 0x5, DW_OP_stack_value + +; 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: nounwind uwtable +define dso_local void @f2(i32 %i) local_unnamed_addr #0 !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 %i, metadata !12, metadata !DIExpression(), metadata i32 %i, metadata !DIExpression()), !dbg !13 + tail call void @f1(i32 1) #3, !dbg !14 + call void @llvm.dbg.value(metadata i32 %i, metadata !12, metadata !DIExpression(DW_OP_plus_uconst, 5, DW_OP_stack_value), metadata i32 %i, metadata !DIExpression(DW_OP_plus_uconst, 5, DW_OP_stack_value)), !dbg !13 + tail call void @f1(i32 3) #3, !dbg !15 + ret void, !dbg !16 +} + +declare !dbg !17 dso_local void @f1(i32) local_unnamed_addr + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.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 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "f2", 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 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "i", arg: 1, scope: !7, file: !1, line: 2, type: !10) +!13 = !DILocation(line: 0, scope: !7) +!14 = !DILocation(line: 3, column: 4, scope: !7) +!15 = !DILocation(line: 5, column: 4, scope: !7) +!16 = !DILocation(line: 6, column: 1, scope: !7) +!17 = !DISubprogram(name: "f1", scope: !1, file: !1, line: 1, type: !8, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)