[llvm] 32ef4ce - [InstrRef] Skip clobbered EntryValue register recovery (#142478)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 27 10:30:33 PDT 2025


Author: Shubham Sandeep Rastogi
Date: 2025-06-27T10:30:29-07:00
New Revision: 32ef4ceec03d6510ba19a098a2894a1caeb2704e

URL: https://github.com/llvm/llvm-project/commit/32ef4ceec03d6510ba19a098a2894a1caeb2704e
DIFF: https://github.com/llvm/llvm-project/commit/32ef4ceec03d6510ba19a098a2894a1caeb2704e.diff

LOG: [InstrRef] Skip clobbered EntryValue register recovery (#142478)

This changes the final stage of InstrRef, i.e. the TransferTracker
(which combines the values locations with the variable values), so that
it treats a DEBUG_VALUE of an EntryValue just like a DEBUG_VALUE of a
constant: a location that is never clobbered and can be propagated to
subsequent BBs as long as no other DEBUG_VALUE intrinsics updated the
variable.

We add two tests here:

1. `entry_value_clobbered_stack_copy` that saves a register on the
stack, uses this register as an entry value DBG_VALUE location, and then
clobbers it. Prior to this patch, this test would crash because we would
try to describe a new location for the variable in terms of what was
saved on the stack, and use an invalid expression to do so. This is not
needed as an EntryValue can never be clobbered.

2. `entry_value_gets_propagated`, that tests that an EntryValue
DBG_VALUE is propagated in a diamond-shaped CFG.

This patch is trying to reland
https://github.com/llvm/llvm-project/pull/77938 but also fixes the bug
with InstrRef based LiveDebugValues, where entry values were not being
propagated in a diamond-shaped CFG.

Added: 
    llvm/test/DebugInfo/MIR/AArch64/entry_value_gets_propagated_aarch64.mir
    llvm/test/DebugInfo/MIR/X86/entry_value_clobbered_stack_copy.mir
    llvm/test/DebugInfo/MIR/X86/entry_value_gets_propagated_X86.mir

Modified: 
    llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
index fdf50188fbcd8..a8143bd8f4273 100644
--- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
+++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
@@ -359,6 +359,7 @@ class TransferTracker {
     SmallVector<ResolvedDbgOp> ResolvedDbgOps;
     bool IsValueValid = true;
     unsigned LastUseBeforeDef = 0;
+    bool DbgLocAvailableAndIsEntryVal = false;
 
     // If every value used by the incoming DbgValue is available at block
     // entry, ResolvedDbgOps will contain the machine locations/constants for
@@ -412,6 +413,8 @@ class TransferTracker {
       // live range.
       LocIdx M = ValuesPreferredLoc->second.getLoc();
       ResolvedDbgOps.push_back(M);
+      if (Value.Properties.DIExpr->isEntryValue())
+        DbgLocAvailableAndIsEntryVal = true;
     }
 
     // If we cannot produce a valid value for the LiveIn value within this
@@ -425,6 +428,16 @@ class TransferTracker {
       return;
     }
 
+    auto &[Var, DILoc] = DVMap.lookupDVID(VarID);
+    PendingDbgValues.push_back(
+        std::make_pair(VarID, &*MTracker->emitLoc(ResolvedDbgOps, Var, DILoc,
+                                                  Value.Properties)));
+
+    // If the location is available at block entry and is an entry value, skip
+    // tracking and recording thr transfer.
+    if (DbgLocAvailableAndIsEntryVal)
+      return;
+
     // The LiveIn value is available at block entry, begin tracking and record
     // the transfer.
     for (const ResolvedDbgOp &Op : ResolvedDbgOps)
@@ -434,10 +447,6 @@ class TransferTracker {
     auto Result = ActiveVLocs.insert(std::make_pair(VarID, NewValue));
     if (!Result.second)
       Result.first->second = NewValue;
-    auto &[Var, DILoc] = DVMap.lookupDVID(VarID);
-    PendingDbgValues.push_back(
-        std::make_pair(VarID, &*MTracker->emitLoc(ResolvedDbgOps, Var, DILoc,
-                                                  Value.Properties)));
   }
 
   /// Load object with live-in variable values. \p mlocs contains the live-in
@@ -668,6 +677,16 @@ class TransferTracker {
 
     auto &[Var, DILoc] = DVMap.lookupDVID(VarID);
 
+    // If the expression is a DW_OP_entry_value, emit the variable location
+    // as-is.
+    if (DIExpr->isEntryValue()) {
+      Register Reg = MTracker->LocIdxToLocID[Num.getLoc()];
+      MachineOperand MO = MachineOperand::CreateReg(Reg, false);
+      PendingDbgValues.push_back(std::make_pair(
+          VarID, &*emitMOLoc(MO, Var, {DIExpr, Prop.Indirect, false})));
+      return true;
+    }
+
     // Is the variable appropriate for entry values (i.e., is a parameter).
     if (!isEntryValueVariable(Var, DIExpr))
       return false;
@@ -694,7 +713,7 @@ class TransferTracker {
     DebugVariableID VarID = DVMap.getDVID(Var);
 
     // Ignore non-register locations, we don't transfer those.
-    if (MI.isUndefDebugValue() ||
+    if (MI.isUndefDebugValue() || MI.getDebugExpression()->isEntryValue() ||
         all_of(MI.debug_operands(),
                [](const MachineOperand &MO) { return !MO.isReg(); })) {
       auto It = ActiveVLocs.find(VarID);

diff  --git a/llvm/test/DebugInfo/MIR/AArch64/entry_value_gets_propagated_aarch64.mir b/llvm/test/DebugInfo/MIR/AArch64/entry_value_gets_propagated_aarch64.mir
new file mode 100644
index 0000000000000..7366ba4de64f4
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/AArch64/entry_value_gets_propagated_aarch64.mir
@@ -0,0 +1,84 @@
+# RUN: llc --run-pass=livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE 
+# REQUIRES: aarch64-registered-target
+
+# This test covers the scenario where a DBG_VALUE created prior to LiveDebugValues has an entry-value expression.
+# It ensures that a clobbered stack copy doesn't crash if used as an entry-value because entry-values can't be clobbered.
+
+--- |
+  target triple = "aarch64-"
+  define i32 @baz(i32 swiftasync %arg1, i32 noundef %arg2, i1 %cond) !dbg !4 {
+    br i1 %cond, label %if.then, label %if.else, !dbg !14
+  if.then:                                          ; preds = %0
+    %call = call i32 @foo(i32 noundef %arg1), !dbg !15
+    br label %if.end, !dbg !18
+  if.else:                                          ; preds = %0
+    %call1 = call i32 @foo(i32 noundef %arg2), !dbg !19
+    br label %if.end
+  if.end:                                           ; preds = %if.else, %if.then
+    %temp.0 = phi i32 [ %call, %if.then ], [ %call1, %if.else ], !dbg !21
+    ret i32 %temp.0, !dbg !22
+  }
+  declare i32 @foo(i32)
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2, !3}
+  !0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "ha", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+  !1 = !DIFile(filename: "test.c", directory: "hah")
+  !2 = !{i32 7, !"Dwarf Version", i32 4}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !4 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8)
+  !5 = !DISubroutineType(types: !6)
+  !6 = !{!7, !7, !7, !7}
+  !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !8 = !{!9, !10, !11, !12}
+  !9 = !DILocalVariable(name: "arg1", arg: 1, scope: !4, file: !1, line: 3, type: !7)
+  !10 = !DILocalVariable(name: "arg2", arg: 2, scope: !4, file: !1, line: 3, type: !7)
+  !11 = !DILocalVariable(name: "cond", arg: 3, scope: !4, file: !1, line: 3, type: !7)
+  !12 = !DILocalVariable(name: "local", scope: !4, file: !1, line: 4, type: !7)
+  !13 = !DILocation(line: 0, scope: !4)
+  !14 = !DILocation(line: 7, column: 7, scope: !4)
+  !15 = !DILocation(line: 8, column: 12, scope: !16)
+  !16 = distinct !DILexicalBlock(scope: !17, file: !1, line: 7, column: 13)
+  !17 = distinct !DILexicalBlock(scope: !4, file: !1, line: 7, column: 7)
+  !18 = !DILocation(line: 9, column: 3, scope: !16)
+  !19 = !DILocation(line: 10, column: 12, scope: !20)
+  !20 = distinct !DILexicalBlock(scope: !17, file: !1, line: 9, column: 10)
+  !21 = !DILocation(line: 0, scope: !17)
+  !22 = !DILocation(line: 13, column: 3, scope: !4)
+name:            baz
+debugInstrRef:   true
+stack:
+  - { id: 1, name: '', type: spill-slot, offset: -24, size: 4, alignment: 4, 
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+body:             |
+  bb.0 (%ir-block.0):
+    DBG_VALUE $w1, $noreg, !12, !DIExpression(DW_OP_LLVM_entry_value, 1),  debug-location !13
+
+  bb.1.if.then:
+    $w0 = LDRWui $sp, 2
+    BL @foo, csr_darwin_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $w0,  debug-location !15
+    $w1 = MOVi32imm 0
+
+  bb.2.if.else:
+    $w0 = LDRWui $sp, 3
+    BL @foo, csr_darwin_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $w0,  debug-location !19
+    STRWui killed $w0, $sp, 1
+    B %bb.3
+  
+  bb.3.if.end:
+    $w0 = LDRWui $sp, 1
+    $fp, $lr = frame-destroy LDPXi $sp, 2,  debug-location !22
+    $sp = frame-destroy ADDXri $sp, 32, 0,  debug-location !22
+    RET undef $lr, implicit killed $w0,  debug-location !22
+
+# CHECK-LABEL: bb.0
+# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-LABEL: bb.1.if.then:
+# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-NEXT: $w0 = LDRWui $sp, 2
+# CHECK-NEXT: BL @foo
+# CHECK-NEXT: $w1 = MOVi32imm 0
+# CHECK-NOT: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-LABEL: bb.2.if.else:
+# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-LABEL: bb.3.if.end:
+# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
\ No newline at end of file

diff  --git a/llvm/test/DebugInfo/MIR/X86/entry_value_clobbered_stack_copy.mir b/llvm/test/DebugInfo/MIR/X86/entry_value_clobbered_stack_copy.mir
new file mode 100644
index 0000000000000..8fe3f7b75a765
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/entry_value_clobbered_stack_copy.mir
@@ -0,0 +1,52 @@
+# RUN: llc --run-pass=livedebugvalues -o - %s | FileCheck %s
+# REQUIRES: x86-registered-target
+
+# This test covers the scenario that saves a register on the stack, uses this register as an entry value DBG_VALUE location, and then clobbers it.
+
+--- |
+  target triple = "x86_64-"
+  define void @foo(ptr swiftasync %0) !dbg !4 {
+    call void @llvm.dbg.value(metadata ptr %0, metadata !9, metadata !DIExpression(DW_OP_LLVM_entry_value, 1)), !dbg !17
+    ret void
+  }
+  declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+  !llvm.module.flags = !{!0}
+  !llvm.dbg.cu = !{!1}
+
+  !0 = !{i32 2, !"Debug Info Version", i32 3}
+  !1 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !2, producer: "blah", isOptimized: true, flags: "blah", runtimeVersion: 5, emissionKind: FullDebug)
+  !2 = !DIFile(filename: "blah", directory: "blah")
+  !3 = !{}
+  !4 = distinct !DISubprogram(name: "blah", linkageName: "blah", scope: !2, file: !2, line: 284, type: !7, unit: !1)
+  !7 = !DISubroutineType(types: !3)
+  !9 = !DILocalVariable(name: "self", arg: 3, scope: !4, file: !2, line: 328, type: !12, flags: DIFlagArtificial)
+  !12 = !DICompositeType(tag: DW_TAG_structure_type, name: "blah", scope: !2, file: !2, size: 64, elements: !3)
+  !17 = !DILocation(line: 328, column: 17, scope: !4)
+
+...
+---
+name:            foo
+alignment:       16
+debugInstrRef:   true
+tracksDebugUserValues: true
+liveins:
+  - { reg: '$r14', virtual-reg: '' }
+stack:
+  - { id: 0, name: '', type: spill-slot, offset: -64, size: 8, alignment: 8,
+      stack-id: default, callee-saved-register: '', callee-saved-restored: true,
+      debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
+body:             |
+  bb.0:
+    liveins: $r14
+    ; Put a copy of r14 on the stack.
+    MOV64mr $rbp, 1, $noreg, -48, $noreg, $r14 :: (store (s64) into %stack.0)
+    DBG_VALUE $r14, $noreg, !9, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !17
+    MOV64mi32 $noreg, 1, $noreg, 0, $noreg, 0, debug-location !17 :: (store (s64) into `ptr null`)
+    $r14 = MOV64rr killed $r13
+    ; Clobber $r14
+    RETI64 24
+# CHECK: bb.0:
+# CHECK:      MOV64mr $rbp, 1, $noreg, -48, $noreg, $r14
+# CHECK-NEXT: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-NOT:  DBG_VALUE

diff  --git a/llvm/test/DebugInfo/MIR/X86/entry_value_gets_propagated_X86.mir b/llvm/test/DebugInfo/MIR/X86/entry_value_gets_propagated_X86.mir
new file mode 100644
index 0000000000000..4e8863064c4fc
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/entry_value_gets_propagated_X86.mir
@@ -0,0 +1,98 @@
+# RUN: llc --run-pass=livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE 
+# REQUIRES: x86-registered-target
+
+# This test covers the scenario where a DBG_VALUE created prior to LiveDebugValues has an entry-value expression.
+# It ensures that a clobbered stack copy doesn't crash if used as an entry-value because entry-values can't be clobbered.
+
+--- |
+  target triple = "x86_64-"
+
+  define i32 @baz(i32 swiftasync %arg1, i32 noundef %arg2, i1 %cond) !dbg !9 {
+    tail call void @llvm.dbg.value(metadata i32 %arg1, metadata !17, metadata !DIExpression(DW_OP_LLVM_entry_value, 1)), !dbg !19
+    br i1 %cond, label %if.then, label %if.else, !dbg !22
+  if.then:
+    %call = call i32 @foo(i32 noundef %arg1), !dbg !23
+    br label %if.end, !dbg !25
+  if.else:
+    %call1 = call i32 @foo(i32 noundef %arg2), !dbg !26
+    br label %if.end
+  if.end:
+    %temp.0 = phi i32 [ %call, %if.then ], [ %call1, %if.else ], !dbg !28
+    ret i32 %temp.0, !dbg !29
+  }
+
+  declare i32 @foo(i32)
+  declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2, !3}
+
+  !0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "ha", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
+  !1 = !DIFile(filename: "test.c", directory: "hah")
+  !2 = !{i32 7, !"Dwarf Version", i32 4}
+  !3 = !{i32 2, !"Debug Info Version", i32 3}
+  !9 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 3, type: !10, scopeLine: 3, unit: !0, retainedNodes: !13)
+  !10 = !DISubroutineType(types: !11)
+  !11 = !{!12, !12, !12, !12}
+  !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !13 = !{!14, !15, !16, !17}
+  !14 = !DILocalVariable(name: "arg1", arg: 1, scope: !9, file: !1, line: 3, type: !12)
+  !15 = !DILocalVariable(name: "arg2", arg: 2, scope: !9, file: !1, line: 3, type: !12)
+  !16 = !DILocalVariable(name: "cond", arg: 3, scope: !9, file: !1, line: 3, type: !12)
+  !17 = !DILocalVariable(name: "local", scope: !9, file: !1, line: 4, type: !12)
+  !19 = !DILocation(line: 0, scope: !9)
+  !20 = !DILocation(line: 7, column: 7, scope: !21)
+  !21 = distinct !DILexicalBlock(scope: !9, file: !1, line: 7, column: 7)
+  !22 = !DILocation(line: 7, column: 7, scope: !9)
+  !23 = !DILocation(line: 8, column: 12, scope: !24)
+  !24 = distinct !DILexicalBlock(scope: !21, file: !1, line: 7, column: 13)
+  !25 = !DILocation(line: 9, column: 3, scope: !24)
+  !26 = !DILocation(line: 10, column: 12, scope: !27)
+  !27 = distinct !DILexicalBlock(scope: !21, file: !1, line: 9, column: 10)
+  !28 = !DILocation(line: 0, scope: !21)
+  !29 = !DILocation(line: 13, column: 3, scope: !9)
+
+...
+---
+name:            baz
+alignment:       16
+debugInstrRef: true
+tracksDebugUserValues: true
+liveins:
+  - { reg: '$r14', virtual-reg: '' }
+  - { reg: '$edi', virtual-reg: '' }
+  - { reg: '$esi', virtual-reg: '' }
+  - { reg: '$edx', virtual-reg: '' }
+body:             |
+  bb.0:
+    successors: %bb.2(0x40000000), %bb.1(0x40000000)
+    liveins: $r14, $edi, $edx, $esi
+    DBG_VALUE $r14, $noreg, !14, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !19
+    CMP32ri killed renamable $edx, 0, implicit-def $eflags, debug-location !20
+    JCC_1 %bb.2, 4, implicit killed $eflags, debug-location !22
+  bb.1.if.then:
+    successors: %bb.3(0x80000000)
+    liveins: $edi, $r13
+    CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $eax, debug-location !23
+    $r14 = MOV64ri 0, debug-location !20
+    JMP_1 %bb.3, debug-location !25
+  bb.2.if.else:
+    successors: %bb.3(0x80000000)
+    liveins: $esi, $r13
+    $edi = MOV32rr killed $esi, debug-location !26
+    CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $eax, debug-location !26
+  bb.3.if.end:
+    liveins: $eax
+    $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !29
+    RET64 implicit $eax, debug-location !29
+# CHECK-LABEL: bb.0:
+# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-LABEL: bb.1.if.then:
+# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-NEXT: CALL64pcrel32 @foo
+# CHECK-NEXT: $r14 = MOV64ri 0
+# CHECK-NOT: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-LABEL: bb.2.if.else:
+# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
+# CHECK-LABEL: bb.3.if.end:
+# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)


        


More information about the llvm-commits mailing list