Skip to content

Commit db8df55

Browse files
[InstrRef] Skip clobbered EntryValue register recovery (llvm#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 llvm#77938 but also fixes the bug with InstrRef based LiveDebugValues, where entry values were not being propagated in a diamond-shaped CFG. (cherry picked from commit 32ef4ce)
1 parent d25ca79 commit db8df55

File tree

4 files changed

+258
-5
lines changed

4 files changed

+258
-5
lines changed

llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ class TransferTracker {
365365
SmallVector<ResolvedDbgOp> ResolvedDbgOps;
366366
bool IsValueValid = true;
367367
unsigned LastUseBeforeDef = 0;
368+
bool DbgLocAvailableAndIsEntryVal = false;
368369

369370
// If every value used by the incoming DbgValue is available at block
370371
// entry, ResolvedDbgOps will contain the machine locations/constants for
@@ -418,6 +419,8 @@ class TransferTracker {
418419
// live range.
419420
LocIdx M = ValuesPreferredLoc->second.getLoc();
420421
ResolvedDbgOps.push_back(M);
422+
if (Value.Properties.DIExpr->isEntryValue())
423+
DbgLocAvailableAndIsEntryVal = true;
421424
}
422425

423426
// If we cannot produce a valid value for the LiveIn value within this
@@ -431,6 +434,16 @@ class TransferTracker {
431434
return;
432435
}
433436

437+
auto &[Var, DILoc] = DVMap.lookupDVID(VarID);
438+
PendingDbgValues.push_back(
439+
std::make_pair(VarID, &*MTracker->emitLoc(ResolvedDbgOps, Var, DILoc,
440+
Value.Properties)));
441+
442+
// If the location is available at block entry and is an entry value, skip
443+
// tracking and recording thr transfer.
444+
if (DbgLocAvailableAndIsEntryVal)
445+
return;
446+
434447
// The LiveIn value is available at block entry, begin tracking and record
435448
// the transfer.
436449
for (const ResolvedDbgOp &Op : ResolvedDbgOps)
@@ -440,10 +453,6 @@ class TransferTracker {
440453
auto Result = ActiveVLocs.insert(std::make_pair(VarID, NewValue));
441454
if (!Result.second)
442455
Result.first->second = NewValue;
443-
auto &[Var, DILoc] = DVMap.lookupDVID(VarID);
444-
PendingDbgValues.push_back(
445-
std::make_pair(VarID, &*MTracker->emitLoc(ResolvedDbgOps, Var, DILoc,
446-
Value.Properties)));
447456
}
448457

449458
/// Load object with live-in variable values. \p mlocs contains the live-in
@@ -684,6 +693,16 @@ class TransferTracker {
684693

685694
auto &[Var, DILoc] = DVMap.lookupDVID(VarID);
686695

696+
// If the expression is a DW_OP_entry_value, emit the variable location
697+
// as-is.
698+
if (DIExpr->isEntryValue()) {
699+
Register Reg = MTracker->LocIdxToLocID[Num.getLoc()];
700+
MachineOperand MO = MachineOperand::CreateReg(Reg, false);
701+
PendingDbgValues.push_back(std::make_pair(
702+
VarID, &*emitMOLoc(MO, Var, {DIExpr, Prop.Indirect, false})));
703+
return true;
704+
}
705+
687706
// Is the variable appropriate for entry values (i.e., is a parameter).
688707
if (!isEntryValueVariable(Var, DIExpr))
689708
return false;
@@ -710,7 +729,7 @@ class TransferTracker {
710729
DebugVariableID VarID = DVMap.getDVID(Var);
711730

712731
// Ignore non-register locations, we don't transfer those.
713-
if (MI.isUndefDebugValue() ||
732+
if (MI.isUndefDebugValue() || MI.getDebugExpression()->isEntryValue() ||
714733
all_of(MI.debug_operands(),
715734
[](const MachineOperand &MO) { return !MO.isReg(); })) {
716735
auto It = ActiveVLocs.find(VarID);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# RUN: llc --run-pass=livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE
2+
# REQUIRES: aarch64-registered-target
3+
4+
# This test covers the scenario where a DBG_VALUE created prior to LiveDebugValues has an entry-value expression.
5+
# It ensures that a clobbered stack copy doesn't crash if used as an entry-value because entry-values can't be clobbered.
6+
7+
--- |
8+
target triple = "aarch64-"
9+
define i32 @baz(i32 swiftasync %arg1, i32 noundef %arg2, i1 %cond) !dbg !4 {
10+
br i1 %cond, label %if.then, label %if.else, !dbg !14
11+
if.then: ; preds = %0
12+
%call = call i32 @foo(i32 noundef %arg1), !dbg !15
13+
br label %if.end, !dbg !18
14+
if.else: ; preds = %0
15+
%call1 = call i32 @foo(i32 noundef %arg2), !dbg !19
16+
br label %if.end
17+
if.end: ; preds = %if.else, %if.then
18+
%temp.0 = phi i32 [ %call, %if.then ], [ %call1, %if.else ], !dbg !21
19+
ret i32 %temp.0, !dbg !22
20+
}
21+
declare i32 @foo(i32)
22+
!llvm.dbg.cu = !{!0}
23+
!llvm.module.flags = !{!2, !3}
24+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "ha", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
25+
!1 = !DIFile(filename: "test.c", directory: "hah")
26+
!2 = !{i32 7, !"Dwarf Version", i32 4}
27+
!3 = !{i32 2, !"Debug Info Version", i32 3}
28+
!4 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8)
29+
!5 = !DISubroutineType(types: !6)
30+
!6 = !{!7, !7, !7, !7}
31+
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
32+
!8 = !{!9, !10, !11, !12}
33+
!9 = !DILocalVariable(name: "arg1", arg: 1, scope: !4, file: !1, line: 3, type: !7)
34+
!10 = !DILocalVariable(name: "arg2", arg: 2, scope: !4, file: !1, line: 3, type: !7)
35+
!11 = !DILocalVariable(name: "cond", arg: 3, scope: !4, file: !1, line: 3, type: !7)
36+
!12 = !DILocalVariable(name: "local", scope: !4, file: !1, line: 4, type: !7)
37+
!13 = !DILocation(line: 0, scope: !4)
38+
!14 = !DILocation(line: 7, column: 7, scope: !4)
39+
!15 = !DILocation(line: 8, column: 12, scope: !16)
40+
!16 = distinct !DILexicalBlock(scope: !17, file: !1, line: 7, column: 13)
41+
!17 = distinct !DILexicalBlock(scope: !4, file: !1, line: 7, column: 7)
42+
!18 = !DILocation(line: 9, column: 3, scope: !16)
43+
!19 = !DILocation(line: 10, column: 12, scope: !20)
44+
!20 = distinct !DILexicalBlock(scope: !17, file: !1, line: 9, column: 10)
45+
!21 = !DILocation(line: 0, scope: !17)
46+
!22 = !DILocation(line: 13, column: 3, scope: !4)
47+
name: baz
48+
debugInstrRef: true
49+
stack:
50+
- { id: 1, name: '', type: spill-slot, offset: -24, size: 4, alignment: 4,
51+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
52+
body: |
53+
bb.0 (%ir-block.0):
54+
DBG_VALUE $w1, $noreg, !12, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !13
55+
56+
bb.1.if.then:
57+
$w0 = LDRWui $sp, 2
58+
BL @foo, csr_darwin_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $w0, debug-location !15
59+
$w1 = MOVi32imm 0
60+
61+
bb.2.if.else:
62+
$w0 = LDRWui $sp, 3
63+
BL @foo, csr_darwin_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $w0, debug-location !19
64+
STRWui killed $w0, $sp, 1
65+
B %bb.3
66+
67+
bb.3.if.end:
68+
$w0 = LDRWui $sp, 1
69+
$fp, $lr = frame-destroy LDPXi $sp, 2, debug-location !22
70+
$sp = frame-destroy ADDXri $sp, 32, 0, debug-location !22
71+
RET undef $lr, implicit killed $w0, debug-location !22
72+
73+
# CHECK-LABEL: bb.0
74+
# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
75+
# CHECK-LABEL: bb.1.if.then:
76+
# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
77+
# CHECK-NEXT: $w0 = LDRWui $sp, 2
78+
# CHECK-NEXT: BL @foo
79+
# CHECK-NEXT: $w1 = MOVi32imm 0
80+
# CHECK-NOT: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
81+
# CHECK-LABEL: bb.2.if.else:
82+
# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
83+
# CHECK-LABEL: bb.3.if.end:
84+
# CHECK: DBG_VALUE $w1, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# RUN: llc --run-pass=livedebugvalues -o - %s | FileCheck %s
2+
# REQUIRES: x86-registered-target
3+
4+
# 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.
5+
6+
--- |
7+
target triple = "x86_64-"
8+
define void @foo(ptr swiftasync %0) !dbg !4 {
9+
call void @llvm.dbg.value(metadata ptr %0, metadata !9, metadata !DIExpression(DW_OP_LLVM_entry_value, 1)), !dbg !17
10+
ret void
11+
}
12+
declare void @llvm.dbg.value(metadata, metadata, metadata)
13+
14+
!llvm.module.flags = !{!0}
15+
!llvm.dbg.cu = !{!1}
16+
17+
!0 = !{i32 2, !"Debug Info Version", i32 3}
18+
!1 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !2, producer: "blah", isOptimized: true, flags: "blah", runtimeVersion: 5, emissionKind: FullDebug)
19+
!2 = !DIFile(filename: "blah", directory: "blah")
20+
!3 = !{}
21+
!4 = distinct !DISubprogram(name: "blah", linkageName: "blah", scope: !2, file: !2, line: 284, type: !7, unit: !1)
22+
!7 = !DISubroutineType(types: !3)
23+
!9 = !DILocalVariable(name: "self", arg: 3, scope: !4, file: !2, line: 328, type: !12, flags: DIFlagArtificial)
24+
!12 = !DICompositeType(tag: DW_TAG_structure_type, name: "blah", scope: !2, file: !2, size: 64, elements: !3)
25+
!17 = !DILocation(line: 328, column: 17, scope: !4)
26+
27+
...
28+
---
29+
name: foo
30+
alignment: 16
31+
debugInstrRef: true
32+
tracksDebugUserValues: true
33+
liveins:
34+
- { reg: '$r14', virtual-reg: '' }
35+
stack:
36+
- { id: 0, name: '', type: spill-slot, offset: -64, size: 8, alignment: 8,
37+
stack-id: default, callee-saved-register: '', callee-saved-restored: true,
38+
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
39+
body: |
40+
bb.0:
41+
liveins: $r14
42+
; Put a copy of r14 on the stack.
43+
MOV64mr $rbp, 1, $noreg, -48, $noreg, $r14 :: (store (s64) into %stack.0)
44+
DBG_VALUE $r14, $noreg, !9, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !17
45+
MOV64mi32 $noreg, 1, $noreg, 0, $noreg, 0, debug-location !17 :: (store (s64) into `ptr null`)
46+
$r14 = MOV64rr killed $r13
47+
; Clobber $r14
48+
RETI64 24
49+
# CHECK: bb.0:
50+
# CHECK: MOV64mr $rbp, 1, $noreg, -48, $noreg, $r14
51+
# CHECK-NEXT: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
52+
# CHECK-NOT: DBG_VALUE
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# RUN: llc --run-pass=livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE
2+
# REQUIRES: x86-registered-target
3+
4+
# This test covers the scenario where a DBG_VALUE created prior to LiveDebugValues has an entry-value expression.
5+
# It ensures that a clobbered stack copy doesn't crash if used as an entry-value because entry-values can't be clobbered.
6+
7+
--- |
8+
target triple = "x86_64-"
9+
10+
define i32 @baz(i32 swiftasync %arg1, i32 noundef %arg2, i1 %cond) !dbg !9 {
11+
tail call void @llvm.dbg.value(metadata i32 %arg1, metadata !17, metadata !DIExpression(DW_OP_LLVM_entry_value, 1)), !dbg !19
12+
br i1 %cond, label %if.then, label %if.else, !dbg !22
13+
if.then:
14+
%call = call i32 @foo(i32 noundef %arg1), !dbg !23
15+
br label %if.end, !dbg !25
16+
if.else:
17+
%call1 = call i32 @foo(i32 noundef %arg2), !dbg !26
18+
br label %if.end
19+
if.end:
20+
%temp.0 = phi i32 [ %call, %if.then ], [ %call1, %if.else ], !dbg !28
21+
ret i32 %temp.0, !dbg !29
22+
}
23+
24+
declare i32 @foo(i32)
25+
declare void @llvm.dbg.value(metadata, metadata, metadata)
26+
27+
!llvm.dbg.cu = !{!0}
28+
!llvm.module.flags = !{!2, !3}
29+
30+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "ha", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
31+
!1 = !DIFile(filename: "test.c", directory: "hah")
32+
!2 = !{i32 7, !"Dwarf Version", i32 4}
33+
!3 = !{i32 2, !"Debug Info Version", i32 3}
34+
!9 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 3, type: !10, scopeLine: 3, unit: !0, retainedNodes: !13)
35+
!10 = !DISubroutineType(types: !11)
36+
!11 = !{!12, !12, !12, !12}
37+
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
38+
!13 = !{!14, !15, !16, !17}
39+
!14 = !DILocalVariable(name: "arg1", arg: 1, scope: !9, file: !1, line: 3, type: !12)
40+
!15 = !DILocalVariable(name: "arg2", arg: 2, scope: !9, file: !1, line: 3, type: !12)
41+
!16 = !DILocalVariable(name: "cond", arg: 3, scope: !9, file: !1, line: 3, type: !12)
42+
!17 = !DILocalVariable(name: "local", scope: !9, file: !1, line: 4, type: !12)
43+
!19 = !DILocation(line: 0, scope: !9)
44+
!20 = !DILocation(line: 7, column: 7, scope: !21)
45+
!21 = distinct !DILexicalBlock(scope: !9, file: !1, line: 7, column: 7)
46+
!22 = !DILocation(line: 7, column: 7, scope: !9)
47+
!23 = !DILocation(line: 8, column: 12, scope: !24)
48+
!24 = distinct !DILexicalBlock(scope: !21, file: !1, line: 7, column: 13)
49+
!25 = !DILocation(line: 9, column: 3, scope: !24)
50+
!26 = !DILocation(line: 10, column: 12, scope: !27)
51+
!27 = distinct !DILexicalBlock(scope: !21, file: !1, line: 9, column: 10)
52+
!28 = !DILocation(line: 0, scope: !21)
53+
!29 = !DILocation(line: 13, column: 3, scope: !9)
54+
55+
...
56+
---
57+
name: baz
58+
alignment: 16
59+
debugInstrRef: true
60+
tracksDebugUserValues: true
61+
liveins:
62+
- { reg: '$r14', virtual-reg: '' }
63+
- { reg: '$edi', virtual-reg: '' }
64+
- { reg: '$esi', virtual-reg: '' }
65+
- { reg: '$edx', virtual-reg: '' }
66+
body: |
67+
bb.0:
68+
successors: %bb.2(0x40000000), %bb.1(0x40000000)
69+
liveins: $r14, $edi, $edx, $esi
70+
DBG_VALUE $r14, $noreg, !14, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !19
71+
CMP32ri killed renamable $edx, 0, implicit-def $eflags, debug-location !20
72+
JCC_1 %bb.2, 4, implicit killed $eflags, debug-location !22
73+
bb.1.if.then:
74+
successors: %bb.3(0x80000000)
75+
liveins: $edi, $r13
76+
CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $eax, debug-location !23
77+
$r14 = MOV64ri 0, debug-location !20
78+
JMP_1 %bb.3, debug-location !25
79+
bb.2.if.else:
80+
successors: %bb.3(0x80000000)
81+
liveins: $esi, $r13
82+
$edi = MOV32rr killed $esi, debug-location !26
83+
CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $eax, debug-location !26
84+
bb.3.if.end:
85+
liveins: $eax
86+
$rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !29
87+
RET64 implicit $eax, debug-location !29
88+
# CHECK-LABEL: bb.0:
89+
# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
90+
# CHECK-LABEL: bb.1.if.then:
91+
# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
92+
# CHECK-NEXT: CALL64pcrel32 @foo
93+
# CHECK-NEXT: $r14 = MOV64ri 0
94+
# CHECK-NOT: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
95+
# CHECK-LABEL: bb.2.if.else:
96+
# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
97+
# CHECK-LABEL: bb.3.if.end:
98+
# CHECK: DBG_VALUE $r14, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)

0 commit comments

Comments
 (0)