Skip to content

Commit 8abe0a5

Browse files
committed
[DebugInfo] Don't set prologue_end behind line-zero call insts
In functions that have been seriously deformed during optimisation, there can be call instructions with line-zero immediately after frame setup (see C reproducer in the test added). Our previous algorithms for prologue_end ignored these, meaning someone entering a function at prologue_end would break-in after a function call had completed. Prefer instead to not emit prologue_end at all: there is no good place to put it.
1 parent 8989ec5 commit 8abe0a5

File tree

3 files changed

+139
-1
lines changed

3 files changed

+139
-1
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,6 +2291,15 @@ findPrologueEndLoc(const MachineFunction *MF) {
22912291
return *FoundInst;
22922292
}
22932293

2294+
// We choose to ignore line-zero locations when setting the prologue as they
2295+
// can't be stepped on anyway; however in very rare scenarios function calls
2296+
// can have line zero, and we shouldn't step over those. In these
2297+
// extraordinary conditions, just bail out and refuse to set a prologue_end.
2298+
if (CurInst->isCall())
2299+
if (const DILocation *Loc = CurInst->getDebugLoc().get())
2300+
if (Loc->getLine() == 0)
2301+
return std::make_pair(nullptr, true);
2302+
22942303
// Try to continue searching, but use a backup-location if substantive
22952304
// computation is happening.
22962305
auto NextInst = std::next(CurInst);

llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# CHECK: Ltmp0:
66
# CHECK: .loc 1 0 0
77
# CHECK-NOT: .loc 1 0 0
8-
# CHECK: .loc 1 37 1 prologue_end
8+
# CHECK: .loc 1 37 1
99

1010
--- |
1111
; ModuleID = '<stdin>'
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# RUN: llc %s -start-after=livedebugvalues -o - -filetype=obj | llvm-dwarfdump - --debug-line | FileCheck %s --implicit-check-not=prologue_end
2+
#
3+
## Original code, compiled clang -O2 -g -c
4+
##
5+
## void ext();
6+
## int main(int argc, char **argv) {
7+
## if (argc == 1)
8+
## ext();
9+
## else
10+
## ext();
11+
## return 0;
12+
## }
13+
##
14+
## In the code sequence above, the call to ext is given line zero during
15+
## optimisation, because the code is duplicated down all function paths thus
16+
## gets merged. We get something like this as the output:
17+
##
18+
## 0: 50 push %rax
19+
## 1: 31 c0 xor %eax,%eax
20+
## 3: e8 00 00 00 00 call 8 <main+0x8>
21+
## 4: R_X86_64_PLT32 ext-0x4
22+
## 8: 31 c0 xor %eax,%eax
23+
## a: 59 pop %rcx
24+
## b: c3 ret
25+
##
26+
## Where prologue_end is placed on address 8, the clearing of the return
27+
## register, because it's the first "real" instruction that isn't line zero.
28+
## This then causes debuggers to skip over the call instruction when entering
29+
## the function, which is catastrophic.
30+
##
31+
## Instead: we shouldn't put a prologue_end on this function at all. It's too
32+
## deformed from the original code to truly have a position (with a line number)
33+
## that is both true, and after frame setup. This gives comsumers the
34+
## opportunity to recognise "this is a crazy function" and act accordingly.
35+
##
36+
## Check lines ensure that there's something meaningful in the line table
37+
## involving line 2, the implicit-check-not is making sure there isn't a
38+
## prologue_end flag on any line entry.
39+
#
40+
# CHECK: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0
41+
#
42+
# CHECK: Address Line Column File
43+
# CHECK: 2 0 0
44+
# CHECK: end_sequence
45+
--- |
46+
; ModuleID = '/tmp/test.c'
47+
source_filename = "/tmp/test.c"
48+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
49+
target triple = "x86_64-unknown-linux-gnu"
50+
51+
; Function Attrs: nounwind uwtable
52+
define dso_local noundef i32 @main(i32 noundef %argc, ptr noundef readnone captures(none) %argv) local_unnamed_addr !dbg !10 {
53+
entry:
54+
#dbg_value(i32 %argc, !19, !DIExpression(), !21)
55+
#dbg_value(ptr %argv, !20, !DIExpression(), !21)
56+
tail call void (...) @ext(), !dbg !22
57+
ret i32 0, !dbg !24
58+
}
59+
60+
declare !dbg !25 void @ext(...) local_unnamed_addr
61+
62+
!llvm.dbg.cu = !{!0}
63+
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
64+
!llvm.ident = !{!9}
65+
66+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (/fast/fs/llvm4 8989ec5439dc2df2aeb7e5ea3e6c255ce8e9634d)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
67+
!1 = !DIFile(filename: "/tmp/test.c", directory: "/fast/fs/llvm-stage/debug", checksumkind: CSK_MD5, checksum: "9862df54ae1fdd9354308eae69de364a")
68+
!2 = !{i32 7, !"Dwarf Version", i32 5}
69+
!3 = !{i32 2, !"Debug Info Version", i32 3}
70+
!4 = !{i32 1, !"wchar_size", i32 4}
71+
!5 = !{i32 8, !"PIC Level", i32 2}
72+
!6 = !{i32 7, !"PIE Level", i32 2}
73+
!7 = !{i32 7, !"uwtable", i32 2}
74+
!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
75+
!9 = !{!"clang version 22.0.0git (/fast/fs/llvm4 8989ec5439dc2df2aeb7e5ea3e6c255ce8e9634d)"}
76+
!10 = distinct !DISubprogram(name: "main", scope: !11, file: !11, line: 2, type: !12, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !18, keyInstructions: true)
77+
!11 = !DIFile(filename: "/tmp/test.c", directory: "", checksumkind: CSK_MD5, checksum: "9862df54ae1fdd9354308eae69de364a")
78+
!12 = !DISubroutineType(types: !13)
79+
!13 = !{!14, !14, !15}
80+
!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
81+
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
82+
!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64)
83+
!17 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
84+
!18 = !{!19, !20}
85+
!19 = !DILocalVariable(name: "argc", arg: 1, scope: !10, file: !11, line: 2, type: !14)
86+
!20 = !DILocalVariable(name: "argv", arg: 2, scope: !10, file: !11, line: 2, type: !15)
87+
!21 = !DILocation(line: 0, scope: !10)
88+
!22 = !DILocation(line: 0, scope: !23)
89+
!23 = distinct !DILexicalBlock(scope: !10, file: !11, line: 3, column: 7)
90+
!24 = !DILocation(line: 7, column: 4, scope: !10, atomGroup: 2, atomRank: 1)
91+
!25 = !DISubprogram(name: "ext", scope: !11, file: !11, line: 1, type: !26, spFlags: DISPFlagOptimized)
92+
!26 = !DISubroutineType(types: !27)
93+
!27 = !{null}
94+
...
95+
---
96+
name: main
97+
alignment: 16
98+
tracksRegLiveness: true
99+
noPhis: true
100+
isSSA: false
101+
noVRegs: true
102+
hasFakeUses: false
103+
debugInstrRef: true
104+
tracksDebugUserValues: true
105+
frameInfo:
106+
stackSize: 8
107+
offsetAdjustment: -8
108+
maxAlignment: 1
109+
adjustsStack: true
110+
hasCalls: true
111+
maxCallFrameSize: 0
112+
isCalleeSavedInfoValid: true
113+
machineFunctionInfo:
114+
amxProgModel: None
115+
body: |
116+
bb.0.entry:
117+
DBG_VALUE $edi, $noreg, !19, !DIExpression(), debug-location !21
118+
DBG_VALUE $rsi, $noreg, !20, !DIExpression(), debug-location !21
119+
frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
120+
frame-setup CFI_INSTRUCTION def_cfa_offset 16
121+
dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !22
122+
CALL64pcrel32 target-flags(x86-plt) @ext, csr_64, implicit $rsp, implicit $ssp, implicit killed $al, implicit-def $rsp, implicit-def $ssp, debug-location !22
123+
DBG_VALUE $rsi, $noreg, !20, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21
124+
DBG_VALUE $edi, $noreg, !19, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21
125+
$eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !24
126+
$rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24
127+
frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !24
128+
RET64 $eax, debug-location !24
129+
...

0 commit comments

Comments
 (0)