Skip to content

Commit f9193f3

Browse files
snehasishamharc
andauthored
[DebugInfo] Preserve line and column number when merging debug info. (#129960)
This patch introduces a new option `-preserve-merged-debug-info` to preserve an arbitrary but deterministic version of debug information when DILocations are merged. This is intended to be used in production environments from which sample based profiles are derived such as AutoFDO and MemProf. With this patch we have see a 0.2% improvement on an internal workload at Google when generating AutoFDO profiles. It also significantly improves the ability for MemProf by preserving debug info for merged call instructions used in the contextual profile. --------- Co-authored-by: Krzysztof Pszeniczny <[email protected]>
1 parent 5acab1b commit f9193f3

File tree

4 files changed

+121
-4
lines changed

4 files changed

+121
-4
lines changed

llvm/docs/HowToUpdateDebugInfo.rst

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ Introduction
99
============
1010

1111
Certain kinds of code transformations can inadvertently result in a loss of
12-
debug info, or worse, make debug info misrepresent the state of a program.
12+
debug info, or worse, make debug info misrepresent the state of a program. Debug
13+
info availability is also essential for SamplePGO.
1314

1415
This document specifies how to correctly update debug info in various kinds of
1516
code transformations, and offers suggestions for how to create targeted debug
@@ -89,9 +90,14 @@ has a location with an accurate scope attached, and b) to prevent misleading
8990
single-stepping (or breakpoint) behavior. Often, merged instructions are memory
9091
accesses which can trap: having an accurate scope attached greatly assists in
9192
crash triage by identifying the (possibly inlined) function where the bad
92-
memory access occurred. This rule is also meant to assist SamplePGO by banning
93-
scenarios in which a sample of a block containing a merged instruction is
94-
misattributed to a block containing one of the instructions-to-be-merged.
93+
memory access occurred.
94+
95+
To maintain distinct source locations for SamplePGO, it is often beneficial to
96+
retain an arbitrary but deterministic location instead of discarding line and
97+
column information as part of merging. In particular, loss of location
98+
information for calls inhibit optimizations such as indirect call promotion.
99+
This behavior can be optionally enabled until support for accurately
100+
representing merged instructions in the line table is implemented.
95101

96102
Examples of transformations that should follow this rule include:
97103

llvm/docs/SourceLevelDebugging.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ the stored debug information into source-language specific information. As
5555
such, a debugger must be aware of the source-language, and is thus tied to a
5656
specific language or family of languages.
5757

58+
.. _intro_consumers:
59+
5860
Debug information consumers
5961
---------------------------
6062

@@ -71,6 +73,17 @@ as Visual Studio and WinDBG. LLVM's debug information format is mostly derived
7173
from and inspired by DWARF, but it is feasible to translate into other target
7274
debug info formats such as STABS.
7375

76+
SamplePGO (also known as `AutoFDO <https://gcc.gnu.org/wiki/AutoFDO>`_)
77+
is a variant of profile guided optimizations which uses hardware sampling based
78+
profilers to collect branch frequency data with low overhead in production
79+
environments. It relies on debug information to associate profile information
80+
to LLVM IR which is then used to guide optimization heuristics. Maintaining
81+
deterministic and distinct source locations is necessary to maximize the
82+
accuracy of mapping hardware sample counts to LLVM IR. For example, DWARF
83+
`discriminators <https://wiki.dwarfstd.org/Path_Discriminators.md>`_ allow
84+
SamplePGO to distinguish between multiple paths of execution which map to the
85+
same source line.
86+
7487
It would also be reasonable to use debug information to feed profiling tools
7588
for analysis of generated code, or, tools for reconstructing the original
7689
source from generated code.

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/IR/IntrinsicInst.h"
2222
#include "llvm/IR/Type.h"
2323
#include "llvm/IR/Value.h"
24+
#include "llvm/Support/CommandLine.h"
2425

2526
#include <numeric>
2627
#include <optional>
@@ -34,6 +35,12 @@ cl::opt<bool> EnableFSDiscriminator(
3435
cl::desc("Enable adding flow sensitive discriminators"));
3536
} // namespace llvm
3637

38+
// When true, preserves line and column number by picking one of the merged
39+
// location info in a deterministic manner to assist sample based PGO.
40+
static cl::opt<bool> PickMergedSourceLocations(
41+
"pick-merged-source-locations", cl::init(false), cl::Hidden,
42+
cl::desc("Preserve line and column number when merging locations."));
43+
3744
uint32_t DIType::getAlignInBits() const {
3845
return (getTag() == dwarf::DW_TAG_LLVM_ptrauth_type ? 0 : SubclassData32);
3946
}
@@ -125,6 +132,20 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
125132
if (LocA == LocB)
126133
return LocA;
127134

135+
// For some use cases (SamplePGO), it is important to retain distinct source
136+
// locations. When this flag is set, we choose arbitrarily between A and B,
137+
// rather than computing a merged location using line 0, which is typically
138+
// not useful for PGO.
139+
if (PickMergedSourceLocations) {
140+
auto A = std::make_tuple(LocA->getLine(), LocA->getColumn(),
141+
LocA->getDiscriminator(), LocA->getFilename(),
142+
LocA->getDirectory());
143+
auto B = std::make_tuple(LocB->getLine(), LocB->getColumn(),
144+
LocB->getDiscriminator(), LocB->getFilename(),
145+
LocB->getDirectory());
146+
return A < B ? LocA : LocB;
147+
}
148+
128149
LLVMContext &C = LocA->getContext();
129150

130151
using LocVec = SmallVector<const DILocation *>;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
;; This test verifies that we assign a deterministic location for merged
2+
;; instructions when -pick-merged-source-locations is enabled. We use the
3+
;; simplifycfg pass to test this behaviour since it was a common source of
4+
;; merged instructions, however we intend this to apply to all users of the
5+
;; getMergedLocation API.
6+
7+
;; Run simplifycfg and check that only 1 call to bar remains and it's debug
8+
;; location has a valid line number (lexicographically smallest).
9+
; RUN: opt %s -passes=simplifycfg -hoist-common-insts -pick-merged-source-locations -S | FileCheck %s --check-prefix=ENABLED
10+
; ENABLED: call i32 @bar{{.*!dbg !}}[[TAG:[0-9]+]]
11+
; ENABLED-NOT: call i32 @bar
12+
; ENABLED: ![[TAG]] = !DILocation(line: 9, column: 16, scope: !9)
13+
14+
;; Run simplifycfg without the pass to ensure that we don't spuriously start
15+
;; passing the test if simplifycfg behaviour changes.
16+
; RUN: opt %s -passes=simplifycfg -hoist-common-insts -pick-merged-source-locations=false -S | FileCheck %s --check-prefix=DISABLED
17+
; DISABLED: call i32 @bar{{.*!dbg !}}[[TAG:[0-9]+]]
18+
; DISABLED-NOT: call i32 @bar
19+
; DISABLED: ![[TAG]] = !DILocation(line: 0, scope: !9)
20+
21+
; ModuleID = '../llvm/test/DebugInfo/Inputs/debug-info-merge-call.c'
22+
source_filename = "../llvm/test/DebugInfo/Inputs/debug-info-merge-call.c"
23+
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"
24+
target triple = "x86_64-unknown-linux-gnu"
25+
26+
; Function Attrs: nounwind uwtable
27+
define dso_local i32 @test(i32 %n) !dbg !9 {
28+
entry:
29+
%call = call i32 @foo(i32 %n), !dbg !12
30+
%cmp1 = icmp sgt i32 %n, 100, !dbg !13
31+
br i1 %cmp1, label %if.then, label %if.else, !dbg !13
32+
33+
if.then: ; preds = %entry
34+
%call2 = call i32 @bar(i32 %n), !dbg !14
35+
%add = add nsw i32 %call2, %call, !dbg !15
36+
br label %if.end, !dbg !16
37+
38+
if.else: ; preds = %entry
39+
%call4 = call i32 @bar(i32 %n), !dbg !17
40+
br label %if.end
41+
42+
if.end: ; preds = %if.else, %if.then
43+
%r.0 = phi i32 [ %add, %if.then ], [ %call4, %if.else ], !dbg !18
44+
ret i32 %r.0, !dbg !19
45+
}
46+
47+
declare !dbg !20 i32 @foo(i32)
48+
49+
declare !dbg !21 i32 @bar(i32)
50+
51+
!llvm.dbg.cu = !{!0}
52+
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
53+
!llvm.ident = !{!8}
54+
55+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git ([email protected]:snehasish/llvm-project.git 6ce41db6b0275d060d6e60f88b96a1657024345c)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None)
56+
!1 = !DIFile(filename: "../llvm/test/DebugInfo/Inputs/debug-info-merge-call.c", directory: "/usr/local/google/home/snehasishk/working/llvm-project/build-assert", checksumkind: CSK_MD5, checksum: "ac1be6c40dad11691922d600f9d55c55")
57+
!2 = !{i32 7, !"Dwarf Version", i32 5}
58+
!3 = !{i32 2, !"Debug Info Version", i32 3}
59+
!4 = !{i32 1, !"wchar_size", i32 4}
60+
!5 = !{i32 8, !"PIC Level", i32 2}
61+
!6 = !{i32 7, !"PIE Level", i32 2}
62+
!7 = !{i32 7, !"uwtable", i32 2}
63+
!8 = !{!"clang version 21.0.0git ([email protected]:snehasish/llvm-project.git 6ce41db6b0275d060d6e60f88b96a1657024345c)"}
64+
!9 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 5, type: !10, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
65+
!10 = !DISubroutineType(types: !11)
66+
!11 = !{}
67+
!12 = !DILocation(line: 7, column: 13, scope: !9)
68+
!13 = !DILocation(line: 8, column: 8, scope: !9)
69+
!14 = !DILocation(line: 9, column: 16, scope: !9)
70+
!15 = !DILocation(line: 9, column: 14, scope: !9)
71+
!16 = !DILocation(line: 10, column: 3, scope: !9)
72+
!17 = !DILocation(line: 11, column: 10, scope: !9)
73+
!18 = !DILocation(line: 0, scope: !9)
74+
!19 = !DILocation(line: 13, column: 3, scope: !9)
75+
!20 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !10, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
76+
!21 = !DISubprogram(name: "bar", scope: !1, file: !1, line: 1, type: !10, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized)
77+

0 commit comments

Comments
 (0)