From 805558861e45e78a0fda96229a2458dd59bea435 Mon Sep 17 00:00:00 2001 From: Jeremy Morse Date: Thu, 4 Sep 2025 11:31:11 +0100 Subject: [PATCH 1/5] [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. --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 9 ++ llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir | 2 +- .../X86/no-prologue-end-after-line0-calls.mir | 129 ++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 30db817ba3144..42b525f2dd6f7 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -2305,6 +2305,15 @@ findPrologueEndLoc(const MachineFunction *MF) { return *FoundInst; } + // We choose to ignore line-zero locations when setting the prologue as they + // can't be stepped on anyway; however in very rare scenarios function calls + // can have line zero, and we shouldn't step over those. In these + // extraordinary conditions, just bail out and refuse to set a prologue_end. + if (CurInst->isCall()) + if (const DILocation *Loc = CurInst->getDebugLoc().get()) + if (Loc->getLine() == 0) + return std::make_pair(nullptr, true); + // Try to continue searching, but use a backup-location if substantive // computation is happening. auto NextInst = std::next(CurInst); diff --git a/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir b/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir index 01862f5905f9c..71489d5a5e485 100644 --- a/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir +++ b/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir @@ -5,7 +5,7 @@ # CHECK: Ltmp0: # CHECK: .loc 1 0 0 # CHECK-NOT: .loc 1 0 0 -# CHECK: .loc 1 37 1 prologue_end +# CHECK: .loc 1 37 1 --- | ; ModuleID = '' diff --git a/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir new file mode 100644 index 0000000000000..604b9d1d2eafb --- /dev/null +++ b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir @@ -0,0 +1,129 @@ +# RUN: llc %s -start-after=livedebugvalues -o - -filetype=obj | llvm-dwarfdump - --debug-line | FileCheck %s --implicit-check-not=prologue_end +# +## Original code, compiled clang -O2 -g -c +## +## void ext(); +## int main(int argc, char **argv) { +## if (argc == 1) +## ext(); +## else +## ext(); +## return 0; +## } +## +## In the code sequence above, the call to ext is given line zero during +## optimisation, because the code is duplicated down all function paths thus +## gets merged. We get something like this as the output: +## +## 0: 50 push %rax +## 1: 31 c0 xor %eax,%eax +## 3: e8 00 00 00 00 call 8 +## 4: R_X86_64_PLT32 ext-0x4 +## 8: 31 c0 xor %eax,%eax +## a: 59 pop %rcx +## b: c3 ret +## +## Where prologue_end is placed on address 8, the clearing of the return +## register, because it's the first "real" instruction that isn't line zero. +## This then causes debuggers to skip over the call instruction when entering +## the function, which is catastrophic. +## +## Instead: we shouldn't put a prologue_end on this function at all. It's too +## deformed from the original code to truly have a position (with a line number) +## that is both true, and after frame setup. This gives comsumers the +## opportunity to recognise "this is a crazy function" and act accordingly. +## +## Check lines ensure that there's something meaningful in the line table +## involving line 2, the implicit-check-not is making sure there isn't a +## prologue_end flag on any line entry. +# +# CHECK: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0 +# +# CHECK: Address Line Column File +# CHECK: 2 0 0 +# CHECK: end_sequence +--- | + ; ModuleID = '/tmp/test.c' + source_filename = "/tmp/test.c" + 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" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: nounwind uwtable + define dso_local noundef i32 @main(i32 noundef %argc, ptr noundef readnone captures(none) %argv) local_unnamed_addr !dbg !10 { + entry: + #dbg_value(i32 %argc, !19, !DIExpression(), !21) + #dbg_value(ptr %argv, !20, !DIExpression(), !21) + tail call void (...) @ext(), !dbg !22 + ret i32 0, !dbg !24 + } + + declare !dbg !25 void @ext(...) local_unnamed_addr + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} + !llvm.ident = !{!9} + + !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) + !1 = !DIFile(filename: "/tmp/test.c", directory: "/fast/fs/llvm-stage/debug", checksumkind: CSK_MD5, checksum: "9862df54ae1fdd9354308eae69de364a") + !2 = !{i32 7, !"Dwarf Version", i32 5} + !3 = !{i32 2, !"Debug Info Version", i32 3} + !4 = !{i32 1, !"wchar_size", i32 4} + !5 = !{i32 8, !"PIC Level", i32 2} + !6 = !{i32 7, !"PIE Level", i32 2} + !7 = !{i32 7, !"uwtable", i32 2} + !8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} + !9 = !{!"clang version 22.0.0git (/fast/fs/llvm4 8989ec5439dc2df2aeb7e5ea3e6c255ce8e9634d)"} + !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) + !11 = !DIFile(filename: "/tmp/test.c", directory: "", checksumkind: CSK_MD5, checksum: "9862df54ae1fdd9354308eae69de364a") + !12 = !DISubroutineType(types: !13) + !13 = !{!14, !14, !15} + !14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) + !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64) + !17 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) + !18 = !{!19, !20} + !19 = !DILocalVariable(name: "argc", arg: 1, scope: !10, file: !11, line: 2, type: !14) + !20 = !DILocalVariable(name: "argv", arg: 2, scope: !10, file: !11, line: 2, type: !15) + !21 = !DILocation(line: 0, scope: !10) + !22 = !DILocation(line: 0, scope: !23) + !23 = distinct !DILexicalBlock(scope: !10, file: !11, line: 3, column: 7) + !24 = !DILocation(line: 7, column: 4, scope: !10, atomGroup: 2, atomRank: 1) + !25 = !DISubprogram(name: "ext", scope: !11, file: !11, line: 1, type: !26, spFlags: DISPFlagOptimized) + !26 = !DISubroutineType(types: !27) + !27 = !{null} +... +--- +name: main +alignment: 16 +tracksRegLiveness: true +noPhis: true +isSSA: false +noVRegs: true +hasFakeUses: false +debugInstrRef: true +tracksDebugUserValues: true +frameInfo: + stackSize: 8 + offsetAdjustment: -8 + maxAlignment: 1 + adjustsStack: true + hasCalls: true + maxCallFrameSize: 0 + isCalleeSavedInfoValid: true +machineFunctionInfo: + amxProgModel: None +body: | + bb.0.entry: + DBG_VALUE $edi, $noreg, !19, !DIExpression(), debug-location !21 + DBG_VALUE $rsi, $noreg, !20, !DIExpression(), debug-location !21 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + frame-setup CFI_INSTRUCTION def_cfa_offset 16 + dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !22 + CALL64pcrel32 target-flags(x86-plt) @ext, csr_64, implicit $rsp, implicit $ssp, implicit killed $al, implicit-def $rsp, implicit-def $ssp, debug-location !22 + DBG_VALUE $rsi, $noreg, !20, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21 + DBG_VALUE $edi, $noreg, !19, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21 + $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !24 + $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24 + frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !24 + RET64 $eax, debug-location !24 +... From 30a0c4e1f25271c71d5671521b702d8022a0c530 Mon Sep 17 00:00:00 2001 From: Jeremy Morse Date: Thu, 11 Sep 2025 16:27:24 +0100 Subject: [PATCH 2/5] Spelling, strings, spacing --- .../X86/no-prologue-end-after-line0-calls.mir | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir index 604b9d1d2eafb..b3efc0321c2af 100644 --- a/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir +++ b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir @@ -18,7 +18,7 @@ ## 0: 50 push %rax ## 1: 31 c0 xor %eax,%eax ## 3: e8 00 00 00 00 call 8 -## 4: R_X86_64_PLT32 ext-0x4 +## 4: R_X86_64_PLT32 ext-0x4 ## 8: 31 c0 xor %eax,%eax ## a: 59 pop %rcx ## b: c3 ret @@ -30,7 +30,7 @@ ## ## Instead: we shouldn't put a prologue_end on this function at all. It's too ## deformed from the original code to truly have a position (with a line number) -## that is both true, and after frame setup. This gives comsumers the +## that is both true, and after frame setup. This gives consumers the ## opportunity to recognise "this is a crazy function" and act accordingly. ## ## Check lines ensure that there's something meaningful in the line table @@ -43,8 +43,6 @@ # CHECK: 2 0 0 # CHECK: end_sequence --- | - ; ModuleID = '/tmp/test.c' - source_filename = "/tmp/test.c" 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" target triple = "x86_64-unknown-linux-gnu" @@ -63,8 +61,8 @@ !llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} !llvm.ident = !{!9} - !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) - !1 = !DIFile(filename: "/tmp/test.c", directory: "/fast/fs/llvm-stage/debug", checksumkind: CSK_MD5, checksum: "9862df54ae1fdd9354308eae69de364a") + !0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "/tmp/test.c", directory: "") !2 = !{i32 7, !"Dwarf Version", i32 5} !3 = !{i32 2, !"Debug Info Version", i32 3} !4 = !{i32 1, !"wchar_size", i32 4} @@ -72,9 +70,9 @@ !6 = !{i32 7, !"PIE Level", i32 2} !7 = !{i32 7, !"uwtable", i32 2} !8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} - !9 = !{!"clang version 22.0.0git (/fast/fs/llvm4 8989ec5439dc2df2aeb7e5ea3e6c255ce8e9634d)"} + !9 = !{!"clang"} !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) - !11 = !DIFile(filename: "/tmp/test.c", directory: "", checksumkind: CSK_MD5, checksum: "9862df54ae1fdd9354308eae69de364a") + !11 = !DIFile(filename: "/tmp/test.c", directory: "") !12 = !DISubroutineType(types: !13) !13 = !{!14, !14, !15} !14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) From fe4f6355d6528ebc88158f1e4c5e0fb2e3fcca9c Mon Sep 17 00:00:00 2001 From: Jeremy Morse Date: Fri, 14 Nov 2025 09:02:24 +0000 Subject: [PATCH 3/5] Revise prologue-end setting as per Davids feedback --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 36 +++++++++++++++---- .../X86/no-prologue-end-after-line0-calls.mir | 32 ++++++++--------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 42b525f2dd6f7..a8904a46dd571 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -2218,6 +2218,7 @@ findPrologueEndLoc(const MachineFunction *MF) { const auto &TII = *MF->getSubtarget().getInstrInfo(); const MachineInstr *NonTrivialInst = nullptr; const Function &F = MF->getFunction(); + DISubprogram *SP = const_cast(F.getSubprogram()); // Some instructions may be inserted into prologue after this function. Must // keep prologue for these cases. @@ -2305,15 +2306,36 @@ findPrologueEndLoc(const MachineFunction *MF) { return *FoundInst; } - // We choose to ignore line-zero locations when setting the prologue as they - // can't be stepped on anyway; however in very rare scenarios function calls - // can have line zero, and we shouldn't step over those. In these - // extraordinary conditions, just bail out and refuse to set a prologue_end. - if (CurInst->isCall()) - if (const DILocation *Loc = CurInst->getDebugLoc().get()) - if (Loc->getLine() == 0) + // In very rare scenarios function calls can have line zero, and we + // shouldn't step over such a call while trying to reach prologue_end. In + // these extraordinary conditions, force an earlier setup instruction to + // have the scope line and put prologue_end there. This will be suboptimal, + // and might still be in setup code, but is less catastrophic than missing + // a call. + if (CurInst->isCall()) { + if (const DILocation *Loc = CurInst->getDebugLoc().get(); + Loc && Loc->getLine() == 0) { + // Go back one instruction. + auto RIt = std::next(CurInst->getIterator().getReverse()); + // In the radically unlikely event that there's no prior instruction, + // meaning the first instruction in the function is a call, don't set a + // prologue_end at all. + if (RIt == CurInst->getParent()->rend()) return std::make_pair(nullptr, true); + // The prior instruction was either line-zero or unset, or a setup + // instruction, or otherwise uninteresting. Force it to have the + // scope line. + unsigned ScopeLine = SP->getScopeLine(); + DILocation *ScopeLineDILoc = + DILocation::get(SP->getContext(), ScopeLine, 0, SP); + const_cast(&*RIt)->setDebugLoc(ScopeLineDILoc); + + // Consider this position to be where prologue_end is placed. + return std::make_pair(&*RIt, false); + } + } + // Try to continue searching, but use a backup-location if substantive // computation is happening. auto NextInst = std::next(CurInst); diff --git a/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir index b3efc0321c2af..cf16455b88602 100644 --- a/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir +++ b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir @@ -1,4 +1,4 @@ -# RUN: llc %s -start-after=livedebugvalues -o - -filetype=obj | llvm-dwarfdump - --debug-line | FileCheck %s --implicit-check-not=prologue_end +# RUN: llc %s -start-after=livedebugvalues -o - -filetype=obj | llvm-dwarfdump - --debug-line | FileCheck %s # ## Original code, compiled clang -O2 -g -c ## @@ -23,25 +23,25 @@ ## a: 59 pop %rcx ## b: c3 ret ## -## Where prologue_end is placed on address 8, the clearing of the return -## register, because it's the first "real" instruction that isn't line zero. -## This then causes debuggers to skip over the call instruction when entering -## the function, which is catastrophic. +## And we could choose to set prologue_end on address 8, the clearing of the +## return register, because it's the first "real" instruction that isn't line +## zero. But this then causes debuggers to skip over the call instruction when +## entering the function, which is catastrophic. ## -## Instead: we shouldn't put a prologue_end on this function at all. It's too -## deformed from the original code to truly have a position (with a line number) -## that is both true, and after frame setup. This gives consumers the +## Instead: we force the xor at address 1 to have a source location (the +## function scope line number), and put a prologue_end there. While it's a +## setup instruction, it's better to have a prologue_end that's still slightly +## in the prologue than to step over the call. This gives consumers the ## opportunity to recognise "this is a crazy function" and act accordingly. ## -## Check lines ensure that there's something meaningful in the line table -## involving line 2, the implicit-check-not is making sure there isn't a -## prologue_end flag on any line entry. +## Check lines: the first entry is the start-of-function scope line, the second +## entry is the prologue_end on the xor, while the third is the zero-line-number +## call instruction. # -# CHECK: standard_opcode_lengths[DW_LNS_set_prologue_end] = 0 -# -# CHECK: Address Line Column File -# CHECK: 2 0 0 -# CHECK: end_sequence +# CHECK: 2 0 0 0 0 0 is_stmt +# CHECK-NEXT: 2 0 0 0 0 0 is_stmt prologue_end +# CHECK-NEXT: 0 0 0 0 0 0 + --- | 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" target triple = "x86_64-unknown-linux-gnu" From ccb24a303b477c42b0201698cced3608404b00e4 Mon Sep 17 00:00:00 2001 From: Jeremy Morse Date: Mon, 17 Nov 2025 13:37:15 +0000 Subject: [PATCH 4/5] Put prologue_end on the line-zero call, rotate tests --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 29 ++++++--------- .../X86/no-prologue-end-after-line0-calls.mir | 37 +++++++++++-------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index a8904a46dd571..2a765a795f4cd 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -2211,6 +2211,11 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) { PrevInstLoc = DL; } +// Returns the position where we should place prologue_end, potentially nullptr, +// which means "no good place to put prologue_end". Returns true in the second +// return value if there are no setup instructions in this function at all, +// meaning we should not emit a start-of-function linetable entry, because it +// would be zero-lengthed. static std::pair findPrologueEndLoc(const MachineFunction *MF) { // First known non-DBG_VALUE and non-frame setup location marks @@ -2308,31 +2313,21 @@ findPrologueEndLoc(const MachineFunction *MF) { // In very rare scenarios function calls can have line zero, and we // shouldn't step over such a call while trying to reach prologue_end. In - // these extraordinary conditions, force an earlier setup instruction to - // have the scope line and put prologue_end there. This will be suboptimal, - // and might still be in setup code, but is less catastrophic than missing - // a call. + // these extraordinary conditions, force the call to have the scope line + // and put prologue_end there. This isn't ideal, but signals that the call + // is where execution in the function starts, and is less catastrophic than + // stepping over the call. if (CurInst->isCall()) { if (const DILocation *Loc = CurInst->getDebugLoc().get(); Loc && Loc->getLine() == 0) { - // Go back one instruction. - auto RIt = std::next(CurInst->getIterator().getReverse()); - // In the radically unlikely event that there's no prior instruction, - // meaning the first instruction in the function is a call, don't set a - // prologue_end at all. - if (RIt == CurInst->getParent()->rend()) - return std::make_pair(nullptr, true); - - // The prior instruction was either line-zero or unset, or a setup - // instruction, or otherwise uninteresting. Force it to have the - // scope line. + // Create and assign the scope-line position. unsigned ScopeLine = SP->getScopeLine(); DILocation *ScopeLineDILoc = DILocation::get(SP->getContext(), ScopeLine, 0, SP); - const_cast(&*RIt)->setDebugLoc(ScopeLineDILoc); + const_cast(&*CurInst)->setDebugLoc(ScopeLineDILoc); // Consider this position to be where prologue_end is placed. - return std::make_pair(&*RIt, false); + return std::make_pair(&*CurInst, false); } } diff --git a/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir index cf16455b88602..abd7eb2528cb0 100644 --- a/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir +++ b/llvm/test/DebugInfo/X86/no-prologue-end-after-line0-calls.mir @@ -1,4 +1,4 @@ -# RUN: llc %s -start-after=livedebugvalues -o - -filetype=obj | llvm-dwarfdump - --debug-line | FileCheck %s +# RUN: llc %s -start-after=livedebugvalues -o - | FileCheck %s # ## Original code, compiled clang -O2 -g -c ## @@ -28,19 +28,30 @@ ## zero. But this then causes debuggers to skip over the call instruction when ## entering the function, which is catastrophic. ## -## Instead: we force the xor at address 1 to have a source location (the -## function scope line number), and put a prologue_end there. While it's a -## setup instruction, it's better to have a prologue_end that's still slightly +## Instead: force the call itself to have a source location (the function scope +## line number), and put a prologue_end there. While it's not the original +## source of the call, it's better to have a prologue_end that means we'll stop ## in the prologue than to step over the call. This gives consumers the ## opportunity to recognise "this is a crazy function" and act accordingly. ## -## Check lines: the first entry is the start-of-function scope line, the second -## entry is the prologue_end on the xor, while the third is the zero-line-number -## call instruction. +## Check lines: ensure that we set prologue_end. The first entry is the +## start-of-function scope line, the second entry is the prologue_end on the +## call. # -# CHECK: 2 0 0 0 0 0 is_stmt -# CHECK-NEXT: 2 0 0 0 0 0 is_stmt prologue_end -# CHECK-NEXT: 0 0 0 0 0 0 +# +# CHECK: main: +# CHECK-NEXT: .Lfunc_begin0: +# CHECK-NEXT: .file 0 "/tmp/test.c" +# CHECK-NEXT: .loc 0 2 0 +# CHECK-NEXT: .cfi_startproc +# CHECK-NEXT: # %bb.0: +# CHECK-NEXT: pushq %rax +# CHECK-NEXT: .cfi_def_cfa_offset 16 +# CHECK-NEXT: .Ltmp0: +# CHECK-NEXT: .loc 0 0 0 is_stmt 0 +# CHECK-NEXT: xorl %eax, %eax +# CHECK-NEXT: .loc 0 2 0 prologue_end is_stmt 1 +# CHECK-NEXT: callq ext@PLT --- | 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,8 +60,6 @@ ; Function Attrs: nounwind uwtable define dso_local noundef i32 @main(i32 noundef %argc, ptr noundef readnone captures(none) %argv) local_unnamed_addr !dbg !10 { entry: - #dbg_value(i32 %argc, !19, !DIExpression(), !21) - #dbg_value(ptr %argv, !20, !DIExpression(), !21) tail call void (...) @ext(), !dbg !22 ret i32 0, !dbg !24 } @@ -112,14 +121,10 @@ machineFunctionInfo: amxProgModel: None body: | bb.0.entry: - DBG_VALUE $edi, $noreg, !19, !DIExpression(), debug-location !21 - DBG_VALUE $rsi, $noreg, !20, !DIExpression(), debug-location !21 frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp frame-setup CFI_INSTRUCTION def_cfa_offset 16 dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !22 CALL64pcrel32 target-flags(x86-plt) @ext, csr_64, implicit $rsp, implicit $ssp, implicit killed $al, implicit-def $rsp, implicit-def $ssp, debug-location !22 - DBG_VALUE $rsi, $noreg, !20, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21 - DBG_VALUE $edi, $noreg, !19, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21 $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !24 $rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24 frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !24 From ce590f8a1409ce35909a855fd545835dee52e17e Mon Sep 17 00:00:00 2001 From: Jeremy Morse Date: Mon, 17 Nov 2025 13:41:12 +0000 Subject: [PATCH 5/5] clang-format --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 2a765a795f4cd..a50bde1c37cbb 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -2223,7 +2223,7 @@ findPrologueEndLoc(const MachineFunction *MF) { const auto &TII = *MF->getSubtarget().getInstrInfo(); const MachineInstr *NonTrivialInst = nullptr; const Function &F = MF->getFunction(); - DISubprogram *SP = const_cast(F.getSubprogram()); + DISubprogram *SP = const_cast(F.getSubprogram()); // Some instructions may be inserted into prologue after this function. Must // keep prologue for these cases. @@ -2323,8 +2323,8 @@ findPrologueEndLoc(const MachineFunction *MF) { // Create and assign the scope-line position. unsigned ScopeLine = SP->getScopeLine(); DILocation *ScopeLineDILoc = - DILocation::get(SP->getContext(), ScopeLine, 0, SP); - const_cast(&*CurInst)->setDebugLoc(ScopeLineDILoc); + DILocation::get(SP->getContext(), ScopeLine, 0, SP); + const_cast(&*CurInst)->setDebugLoc(ScopeLineDILoc); // Consider this position to be where prologue_end is placed. return std::make_pair(&*CurInst, false);