-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Reapply ccddb6ffad1, "Emit a worst-case prologue_end" #116084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
In 39b2979 Pavel has kindly refined the implementation of a test in such a way that it doesn't trip up over this patch -- the test wishes to stimulate LLDBs presentation of line0 locations, rather than wanting to always step on line-zero on entry to artificial_location.c. As that's what was tripping up this change, reapply. Original commit message follows. [DWARF] Emit a worst-case prologue_end flag for pathological inputs (llvm#107849) prologue_end usually indicates where the end of the function-initialization lies, and is where debuggers usually choose to put the initial breakpoint for a function. Our current algorithm piggy-backs it on the first available source-location: which doesn't necessarily have anything to do with the start of the function. To avoid this in heavily-optimised code that lacks many useful source locations, pick a worst-case "if all else fails" prologue_end location, of the first instruction that appears to do meaningful computation. It'll be given the function-scope line number, which should run-on from the start of the function anyway. This means if your code is completely inverted by the optimiser, you can at least put a breakpoint at the _start_ like you expect, even if it's difficult to then step through. This patch also attempts to preserve some good behaviour we have without optimisations -- at O0, if the prologue immediately falls into a loop body without any computation happening, then prologue_end lands at the start of that loop. This is desirable; but does mean we need to do more work to detect and support those situations.
@llvm/pr-subscribers-debuginfo @llvm/pr-subscribers-backend-x86 Author: Jeremy Morse (jmorse) Changes[This is a PR to trigger the precommit CI, this commit is a reapplication of #107849, hence the skip-approval label] In 39b2979 Pavel has kindly refined the implementation of a test in such a way that it doesn't trip up over this patch -- the test wishes to stimulate LLDBs presentation of line0 locations, rather than wanting to always step on line-zero on entry to artificial_location.c. As that's what was tripping up this change, reapply. Original commit message follows. [DWARF] Emit a worst-case prologue_end flag for pathological inputs (#107849) prologue_end usually indicates where the end of the function-initialization lies, and is where debuggers usually choose to put the initial breakpoint for a function. Our current algorithm piggy-backs it on the first available source-location: which doesn't necessarily have anything to do with the start of the function. To avoid this in heavily-optimised code that lacks many useful source locations, pick a worst-case "if all else fails" prologue_end location, of the first instruction that appears to do meaningful computation. It'll be given the function-scope line number, which should run-on from the start of the function anyway. This means if your code is completely inverted by the optimiser, you can at least put a breakpoint at the start like you expect, even if it's difficult to then step through. This patch also attempts to preserve some good behaviour we have without optimisations -- at O0, if the prologue immediately falls into a loop body without any computation happening, then prologue_end lands at the start of that loop. This is desirable; but does mean we need to do more work to detect and support those situations. Patch is 23.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/116084.diff 9 Files Affected:
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 9f872b75207c05..65be55b2840c32 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -2062,6 +2062,16 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
unsigned LastAsmLine =
Asm->OutStreamer->getContext().getCurrentDwarfLoc().getLine();
+ if (!DL && MI == PrologEndLoc) {
+ // In rare situations, we might want to place the end of the prologue
+ // somewhere that doesn't have a source location already. It should be in
+ // the entry block.
+ assert(MI->getParent() == &*MI->getMF()->begin());
+ recordSourceLine(SP->getScopeLine(), 0, SP,
+ DWARF2_FLAG_PROLOGUE_END | DWARF2_FLAG_IS_STMT);
+ return;
+ }
+
bool PrevInstInSameSection =
(!PrevInstBB ||
PrevInstBB->getSectionID() == MI->getParent()->getSectionID());
@@ -2138,32 +2148,109 @@ static std::pair<const MachineInstr *, bool>
findPrologueEndLoc(const MachineFunction *MF) {
// First known non-DBG_VALUE and non-frame setup location marks
// the beginning of the function body.
- const MachineInstr *LineZeroLoc = nullptr;
+ const auto &TII = *MF->getSubtarget().getInstrInfo();
+ const MachineInstr *NonTrivialInst = nullptr;
const Function &F = MF->getFunction();
// Some instructions may be inserted into prologue after this function. Must
// keep prologue for these cases.
bool IsEmptyPrologue =
!(F.hasPrologueData() || F.getMetadata(LLVMContext::MD_func_sanitize));
- for (const auto &MBB : *MF) {
- for (const auto &MI : MBB) {
- if (!MI.isMetaInstruction()) {
- if (!MI.getFlag(MachineInstr::FrameSetup) && MI.getDebugLoc()) {
- // Scan forward to try to find a non-zero line number. The
- // prologue_end marks the first breakpoint in the function after the
- // frame setup, and a compiler-generated line 0 location is not a
- // meaningful breakpoint. If none is found, return the first
- // location after the frame setup.
- if (MI.getDebugLoc().getLine())
- return std::make_pair(&MI, IsEmptyPrologue);
-
- LineZeroLoc = &MI;
- }
- IsEmptyPrologue = false;
- }
+
+ // Helper lambda to examine each instruction and potentially return it
+ // as the prologue_end point.
+ auto ExamineInst = [&](const MachineInstr &MI)
+ -> std::optional<std::pair<const MachineInstr *, bool>> {
+ // Is this instruction trivial data shuffling or frame-setup?
+ bool isCopy = (TII.isCopyInstr(MI) ? true : false);
+ bool isTrivRemat = TII.isTriviallyReMaterializable(MI);
+ bool isFrameSetup = MI.getFlag(MachineInstr::FrameSetup);
+
+ if (!isFrameSetup && MI.getDebugLoc()) {
+ // Scan forward to try to find a non-zero line number. The
+ // prologue_end marks the first breakpoint in the function after the
+ // frame setup, and a compiler-generated line 0 location is not a
+ // meaningful breakpoint. If none is found, return the first
+ // location after the frame setup.
+ if (MI.getDebugLoc().getLine())
+ return std::make_pair(&MI, IsEmptyPrologue);
+ }
+
+ // Keep track of the first "non-trivial" instruction seen, i.e. anything
+ // that doesn't involve shuffling data around or is a frame-setup.
+ if (!isCopy && !isTrivRemat && !isFrameSetup && !NonTrivialInst)
+ NonTrivialInst = &MI;
+
+ IsEmptyPrologue = false;
+ return std::nullopt;
+ };
+
+ // Examine all the instructions at the start of the function. This doesn't
+ // necessarily mean just the entry block: unoptimised code can fall-through
+ // into an initial loop, and it makes sense to put the initial breakpoint on
+ // the first instruction of such a loop. However, if we pass branches, we're
+ // better off synthesising an early prologue_end.
+ auto CurBlock = MF->begin();
+ auto CurInst = CurBlock->begin();
+ while (true) {
+ // Skip empty blocks, in rare cases the entry can be empty.
+ if (CurInst == CurBlock->end()) {
+ ++CurBlock;
+ CurInst = CurBlock->begin();
+ continue;
}
+
+ // Check whether this non-meta instruction a good position for prologue_end.
+ if (!CurInst->isMetaInstruction()) {
+ auto FoundInst = ExamineInst(*CurInst);
+ if (FoundInst)
+ return *FoundInst;
+ }
+
+ // Try to continue searching, but use a backup-location if substantive
+ // computation is happening.
+ auto NextInst = std::next(CurInst);
+ if (NextInst != CurInst->getParent()->end()) {
+ // Continue examining the current block.
+ CurInst = NextInst;
+ continue;
+ }
+
+ // We've reached the end of the block. Did we just look at a terminator?
+ if (CurInst->isTerminator()) {
+ // Some kind of "real" control flow is occurring. At the very least
+ // we would have to start exploring the CFG, a good signal that the
+ // prologue is over.
+ break;
+ }
+
+ // If we've already fallen through into a loop, don't fall through
+ // further, use a backup-location.
+ if (CurBlock->pred_size() > 1)
+ break;
+
+ // Fall-through from entry to the next block. This is common at -O0 when
+ // there's no initialisation in the function. Bail if we're also at the
+ // end of the function.
+ if (++CurBlock == MF->end())
+ break;
+ CurInst = CurBlock->begin();
+ }
+
+ // We couldn't find any source-location, suggesting all meaningful information
+ // got optimised away. Set the prologue_end to be the first non-trivial
+ // instruction, which will get the scope line number. This is better than
+ // nothing.
+ // Only do this in the entry block, as we'll be giving it the scope line for
+ // the function. Return IsEmptyPrologue==true if we've picked the first
+ // instruction.
+ if (NonTrivialInst && NonTrivialInst->getParent() == &*MF->begin()) {
+ IsEmptyPrologue = NonTrivialInst == &*MF->begin()->begin();
+ return std::make_pair(NonTrivialInst, IsEmptyPrologue);
}
- return std::make_pair(LineZeroLoc, IsEmptyPrologue);
+
+ // If the entry path is empty, just don't have a prologue_end at all.
+ return std::make_pair(nullptr, IsEmptyPrologue);
}
/// Register a source line with debug info. Returns the unique label that was
@@ -2195,12 +2282,21 @@ DwarfDebug::emitInitialLocDirective(const MachineFunction &MF, unsigned CUID) {
bool IsEmptyPrologue = PrologEnd.second;
// If the prolog is empty, no need to generate scope line for the proc.
- if (IsEmptyPrologue)
- // In degenerate cases, we can have functions with no source locations
- // at all. These want a scope line, to avoid a totally empty function.
- // Thus, only skip scope line if there's location to place prologue_end.
- if (PrologEndLoc)
- return PrologEndLoc;
+ if (IsEmptyPrologue) {
+ // If there's nowhere to put a prologue_end flag, emit a scope line in case
+ // there are simply no source locations anywhere in the function.
+ if (PrologEndLoc) {
+ // Avoid trying to assign prologue_end to a line-zero location.
+ // Instructions with no DebugLoc at all are fine, they'll be given the
+ // scope line nuumber.
+ const DebugLoc &DL = PrologEndLoc->getDebugLoc();
+ if (!DL || DL->getLine() != 0)
+ return PrologEndLoc;
+
+ // Later, don't place the prologue_end flag on this line-zero location.
+ PrologEndLoc = nullptr;
+ }
+ }
// Ensure the compile unit is created if the function is called before
// beginFunction().
diff --git a/llvm/test/CodeGen/X86/no-non-zero-debug-loc-prologue.ll b/llvm/test/CodeGen/X86/no-non-zero-debug-loc-prologue.ll
index a9c71d1d9c36b2..6ea373b67bad41 100644
--- a/llvm/test/CodeGen/X86/no-non-zero-debug-loc-prologue.ll
+++ b/llvm/test/CodeGen/X86/no-non-zero-debug-loc-prologue.ll
@@ -1,10 +1,10 @@
-; RUN: llc -filetype=asm -mtriple=x86_64-apple-macosx12.0.0 -O0 %s -o - | FileCheck %s
+; RUN: llc -filetype=asm -mtriple=x86_64-apple-macosx12.0.0 -O0 %s -o - | FileCheck %s --implicit-check-not=prologue_end
; CHECK: Lfunc_begin0:
; CHECK-NEXT: .file{{.+}}
; CHECK-NEXT: .loc 1 1 0 ## test-small.c:1:0{{$}}
; CHECK-NEXT: .cfi_startproc
; CHECK-NEXT: ## %bb.{{[0-9]+}}:
-; CHECK-NEXT: .loc 1 0 1 prologue_end{{.*}}
+; CHECK-NEXT: .loc 1 0 1
define void @test() #0 !dbg !9 {
ret void, !dbg !12
}
@@ -19,4 +19,4 @@ define void @test() #0 !dbg !9 {
!9 = distinct !DISubprogram(name: "test", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !7)
!10 = !DIFile(filename: "test-small.c", directory: "/Users/shubham/Development/test")
!11 = !DISubroutineType(types: !7)
-!12 = !DILocation(line: 0, column: 1, scope: !9)
\ No newline at end of file
+!12 = !DILocation(line: 0, column: 1, scope: !9)
diff --git a/llvm/test/CodeGen/X86/pseudo_cmov_lower2.ll b/llvm/test/CodeGen/X86/pseudo_cmov_lower2.ll
index ee3d31bb73dca4..6091e2bf5bc56b 100644
--- a/llvm/test/CodeGen/X86/pseudo_cmov_lower2.ll
+++ b/llvm/test/CodeGen/X86/pseudo_cmov_lower2.ll
@@ -200,9 +200,9 @@ declare void @llvm.dbg.value(metadata, metadata, metadata)
; minus the DEBUG_VALUE line and changes in labels..
define double @foo1_g(float %p1, double %p2, double %p3) nounwind !dbg !4 {
; CHECK-LABEL: foo1_g:
-; CHECK: .file 1 "." "test.c"
-; CHECK-NEXT: .loc 1 3 0
; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: .file 1 "." "test.c"
+; CHECK-NEXT: .loc 1 3 0 prologue_end
; CHECK-NEXT: xorps %xmm3, %xmm3
; CHECK-NEXT: ucomiss %xmm3, %xmm0
; CHECK-NEXT: movsd {{.*#+}} xmm0 = [1.25E+0,0.0E+0]
diff --git a/llvm/test/DebugInfo/MIR/X86/dbg-prologue-backup-loc2.mir b/llvm/test/DebugInfo/MIR/X86/dbg-prologue-backup-loc2.mir
new file mode 100644
index 00000000000000..c27655ac801316
--- /dev/null
+++ b/llvm/test/DebugInfo/MIR/X86/dbg-prologue-backup-loc2.mir
@@ -0,0 +1,134 @@
+# RUN: llc %s -start-before=livedebugvalues -o - | \
+# RUN: FileCheck %s --implicit-check-not=prologue_end
+#
+## When picking a "backup" location of the first non-trivial instruction in
+## a function, don't select a location outside of the entry block. We have to
+## give it the function's scope-line, and installing that outside of the entry
+## block is liable to be misleading.
+##
+## Produced from the C below with "clang -O2 -g -mllvm
+## -stop-before=livedebugvalues", then modified to unrotate and shift early
+## insts into the loop block. This means the MIR is meaningless, we only test
+## whether the scope-line will leak into the loop block or not.
+##
+## int glob = 0;
+## int foo(int arg, int sum) {
+## arg += sum;
+## while (arg) {
+## glob--;
+## arg %= glob;
+## }
+## return 0;
+## }
+#
+# CHECK-LABEL: foo:
+# CHECK: .loc 0 2 0
+# CHECK: # %bb.0:
+# CHECK-NEXT: movl %edi, %edx
+# CHECK-NEXT: .loc 0 0 0 is_stmt 0
+# CHECK-NEXT: .Ltmp0:
+# CHECK-NEXT: .p2align 4
+# CHECK-NEXT: .LBB0_1:
+# CHECK-LABEL: addl %esi, %edx
+
+
+--- |
+ ; ModuleID = 'out2.ll'
+ source_filename = "foo.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"
+
+ @glob = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0
+
+ define dso_local noundef i32 @foo(i32 noundef %arg, i32 noundef %sum) local_unnamed_addr !dbg !9 {
+ entry:
+ %add = add nsw i32 %sum, %arg
+ br label %while.body.preheader
+
+ while.body.preheader: ; preds = %entry
+ %glob.promoted = load i32, ptr @glob, align 4
+ br label %while.body, !dbg !13
+
+ while.body: ; preds = %while.body, %while.body.preheader
+ %arg.addr.06 = phi i32 [ %rem, %while.body ], [ %add, %while.body.preheader ]
+ %dec35 = phi i32 [ %dec, %while.body ], [ %glob.promoted, %while.body.preheader ]
+ %dec = add nsw i32 %dec35, -1, !dbg !14
+ %0 = add i32 %dec35, -1, !dbg !16
+ %rem = srem i32 %arg.addr.06, %0, !dbg !16
+ %tobool.not = icmp eq i32 %rem, 0, !dbg !13
+ br i1 %tobool.not, label %while.cond.while.end_crit_edge, label %while.body, !dbg !13
+
+ while.cond.while.end_crit_edge: ; preds = %while.body
+ store i32 %dec, ptr @glob, align 4, !dbg !14
+ br label %while.end, !dbg !13
+
+ while.end: ; preds = %while.cond.while.end_crit_edge
+ ret i32 0, !dbg !17
+ }
+
+ !llvm.dbg.cu = !{!2}
+ !llvm.module.flags = !{!6, !7}
+ !llvm.ident = !{!8}
+
+ !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+ !1 = distinct !DIGlobalVariable(name: "glob", scope: !2, file: !3, line: 1, type: !5, isLocal: false, isDefinition: true)
+ !2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+ !3 = !DIFile(filename: "foo.c", directory: "")
+ !4 = !{!0}
+ !5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+ !6 = !{i32 7, !"Dwarf Version", i32 5}
+ !7 = !{i32 2, !"Debug Info Version", i32 3}
+ !8 = !{!"clang"}
+ !9 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 2, type: !10, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !12)
+ !10 = !DISubroutineType(types: !11)
+ !11 = !{!5, !5, !5}
+ !12 = !{}
+ !13 = !DILocation(line: 4, column: 3, scope: !9)
+ !14 = !DILocation(line: 5, column: 9, scope: !15)
+ !15 = distinct !DILexicalBlock(scope: !9, file: !3, line: 4, column: 15)
+ !16 = !DILocation(line: 6, column: 9, scope: !15)
+ !17 = !DILocation(line: 8, column: 3, scope: !9)
+
+...
+---
+name: foo
+alignment: 16
+tracksRegLiveness: true
+debugInstrRef: true
+tracksDebugUserValues: true
+liveins:
+ - { reg: '$edi' }
+ - { reg: '$esi' }
+frameInfo:
+ maxAlignment: 1
+ maxCallFrameSize: 0
+ isCalleeSavedInfoValid: true
+machineFunctionInfo:
+ amxProgModel: None
+body: |
+ bb.0.entry:
+ liveins: $edi, $esi
+
+ $edx = MOV32rr $edi
+
+ bb.1.while.body (align 16):
+ successors: %bb.2(0x04000000), %bb.1(0x7c000000)
+ liveins: $ecx, $edx, $esi
+
+ renamable $edx = nsw ADD32rr killed renamable $edx, renamable $esi, implicit-def dead $eflags
+ renamable $ecx = MOV32rm $rip, 1, $noreg, @glob, $noreg :: (dereferenceable load (s32) from @glob)
+ renamable $ecx = DEC32r killed renamable $ecx, implicit-def dead $eflags
+ $eax = MOV32rr killed $edx
+ CDQ implicit-def $eax, implicit-def $edx, implicit $eax
+ IDIV32r renamable $ecx, implicit-def dead $eax, implicit-def $edx, implicit-def dead $eflags, implicit $eax, implicit $edx
+ TEST32rr renamable $edx, renamable $edx, implicit-def $eflags
+ JCC_1 %bb.1, 5, implicit killed $eflags
+
+ bb.2.while.cond.while.end_crit_edge:
+ liveins: $ecx, $esi
+
+ MOV32mr $rip, 1, $noreg, @glob, $noreg, killed renamable $ecx, debug-location !14 :: (store (s32) into @glob)
+ $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, debug-location !17
+ RET64 $eax, debug-location !17
+
+...
diff --git a/llvm/test/DebugInfo/MIR/X86/empty-inline.mir b/llvm/test/DebugInfo/MIR/X86/empty-inline.mir
index 695b7c60365b1d..9a4a5a3c5cb741 100644
--- a/llvm/test/DebugInfo/MIR/X86/empty-inline.mir
+++ b/llvm/test/DebugInfo/MIR/X86/empty-inline.mir
@@ -13,9 +13,10 @@
#
# CHECK: Address Line Column File ISA Discriminator OpIndex Flags
# CHECK-NEXT: ---
-# CHECK-NEXT: 25 0 1 0 0 0 is_stmt
-# CHECK-NEXT: 29 28 1 0 0 0 is_stmt prologue_end
+# CHECK-NEXT: 25 0 1 0 0 0 is_stmt prologue_end
+# CHECK-NEXT: 29 28 1 0 0 0 is_stmt
# CHECK-NEXT: 29 28 1 0 0 0 is_stmt end_sequence
+
--- |
source_filename = "t.ll"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/llvm/test/DebugInfo/X86/dbg-prolog-end-backup-loc.ll b/llvm/test/DebugInfo/X86/dbg-prolog-end-backup-loc.ll
new file mode 100644
index 00000000000000..2425df608275c5
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/dbg-prolog-end-backup-loc.ll
@@ -0,0 +1,86 @@
+; RUN: llc %s -o - | FileCheck %s
+
+;; This test has had source-locations removed from the prologue, to simulate
+;; heavily-optimised scenarios where a lot of debug-info gets dropped. Check
+;; that we can pick a "worst-case" prologue_end position, of the first
+;; instruction that does any meaningful computation (the add). It's better to
+;; put the prologue_end flag here rather than deeper into the loop, past the
+;; early-exit check.
+;;
+;; Generated from this code at -O2 -g in clang, with source locations then
+;; deleted.
+;;
+;; int glob = 0;
+;; int foo(int arg, int sum) {
+;; arg += sum;
+;; while (arg) {
+;; glob--;
+;; arg %= glob;
+;; }
+;; return 0;
+;; }
+
+; CHECK-LABEL: foo:
+;; Scope-line location:
+; CHECK: .loc 0 2 0
+;; Entry block:
+; CHECK: movl %edi, %edx
+; CHECK-NEXT: .loc 0 2 0 prologue_end
+; CHECK-NEXT: addl %esi, %edx
+; CHECK-NEXT: je .LBB0_4
+; CHECK-LABEL: # %bb.1:
+
+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"
+
+@glob = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0
+
+define dso_local noundef i32 @foo(i32 noundef %arg, i32 noundef %sum) local_unnamed_addr !dbg !9 {
+entry:
+ %add = add nsw i32 %sum, %arg
+ %tobool.not4 = icmp eq i32 %add, 0
+ br i1 %tobool.not4, label %while.end, label %while.body.preheader
+
+while.body.preheader:
+ %glob.promoted = load i32, ptr @glob, align 4
+ br label %while.body, !dbg !14
+
+while.body:
+ %arg.addr.06 = phi i32 [ %rem, %while.body ], [ %add, %while.body.preheader ]
+ %dec35 = phi i32 [ %dec, %while.body ], [ %glob.promoted, %while.body.preheader ]
+ %dec = add nsw i32 %dec35, -1, !dbg !15
+ %rem = srem i32 %arg.addr.06, %dec, !dbg !17
+ %tobool.not = icmp eq i32 %rem, 0, !dbg !14
+ br i1 %tobool.not, label %while.cond.while.end_crit_edge, label %while.body, !dbg !14
+
+while.cond.while.end_crit_edge:
+ store i32 %dec, ptr @glob, align 4, !dbg !15
+ br label %while.end, !dbg !14
+
+while.end:
+ ret i32 0, !dbg !18
+}
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!6, !7}
+!llvm.ident = !{!8}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "glob", scope: !2, file: !3, line: 1, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "foo.c", directory: "")
+!4 = !{!0}
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!6 = !{i32 7, !"Dwarf Version", i32 5}
+!7 = !{i32 2, !"Debug Info Version", i32 3}
+!8 = !{!"clang"}
+!9 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 2, type: !10, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !12)
+!10 = !DISubroutineType(types: !11)
+!11 = !{!5, !5, !5}
+!12 = !{}
+!13 = !DILocation(line: 3, column: 7, scope: !9)
+!14 = !DILocation(line: 4, column: 3, scope: !9)
+!15 = !DILocation(line: 5, column: 9, scope: !16)
+!16 = distinct !DILexicalBlock(scope: !9, file: !3, line: 4, column: 15)
+!17 = !DILocation(line: 6, column: 9, scope: !16)
+!18 = !DILocation(line: 8, column: 3, scope: !9)
diff --git a/llvm/test/DebugInfo/X86/dbg-prolog-end.ll b/llvm/test/DebugInfo/X86/dbg-prolog-end.ll
index 1703323fc7ee1d..3d29f8e266301f 100644
--- a/llvm/test/DebugInfo/X86/dbg-prolog-end.ll
+++ b/llvm/test/DebugInfo/X86/dbg-prolog-end.ll
@@ -36,6 +36,50 @@ entry:
ret i32 %call, !dbg !16
}
+;; int foo(int arg) {
+;; while (arg)
+;; arg--;
+;; return 0;
+;; }
+;;
+;; In this function, the entry block will fall thr...
[truncated]
|
[This is a PR to trigger the precommit CI, this commit is a reapplication of #107849, hence the skip-approval label]
In 39b2979 Pavel has kindly refined the implementation of a test in such a way that it doesn't trip up over this patch -- the test wishes to stimulate LLDBs presentation of line0 locations, rather than wanting to always step on line-zero on entry to artificial_location.c. As that's what was tripping up this change, reapply.
Original commit message follows.
[DWARF] Emit a worst-case prologue_end flag for pathological inputs (#107849)
prologue_end usually indicates where the end of the function-initialization lies, and is where debuggers usually choose to put the initial breakpoint for a function. Our current algorithm piggy-backs it on the first available source-location: which doesn't necessarily have anything to do with the start of the function.
To avoid this in heavily-optimised code that lacks many useful source locations, pick a worst-case "if all else fails" prologue_end location, of the first instruction that appears to do meaningful computation. It'll be given the function-scope line number, which should run-on from the start of the function anyway. This means if your code is completely inverted by the optimiser, you can at least put a breakpoint at the start like you expect, even if it's difficult to then step through.
This patch also attempts to preserve some good behaviour we have without optimisations -- at O0, if the prologue immediately falls into a loop body without any computation happening, then prologue_end lands at the start of that loop. This is desirable; but does mean we need to do more work to detect and support those situations.