Skip to content

Commit 5b6c19b

Browse files
committed
[DebugInfo] getMergedLocation: match scopes based on their location
GetNearestCommonScope finds a common scope for two locations by finding the common parent scope object. With this commit, GetNearestCommonScope considers two scopes equal if they have the same file, line and column values. Thus, it can find the "nearest common included location" when merging locations from different files. This may be useful for a case described here llvm#125780 (comment). DIScope's pointer equality isn't enough for scope equality check, since, for example, two `#include "x.inc"` directives showing up on different lines produce different DILocalScope objects representing the same file content. Thus, two (even same) locations from "x.inc" may have different parent scope objects representing the same source location. If input DILocations are from different files, or common scope is in another file than input locations, a textual inclusion case is assumed, and the location of common scope ("nearest common included location") is returned as a result of getMergedLocation. It fixes llvm#122846.
1 parent 382962b commit 5b6c19b

File tree

4 files changed

+691
-15
lines changed

4 files changed

+691
-15
lines changed

llvm/lib/IR/DebugInfoMetadata.cpp

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include "llvm/IR/DebugInfoMetadata.h"
1414
#include "LLVMContextImpl.h"
1515
#include "MetadataImpl.h"
16-
#include "llvm/ADT/SmallPtrSet.h"
16+
#include "llvm/ADT/SetVector.h"
1717
#include "llvm/ADT/StringSwitch.h"
1818
#include "llvm/BinaryFormat/Dwarf.h"
1919
#include "llvm/IR/DebugProgramInstruction.h"
@@ -125,6 +125,22 @@ DILocation *DILocation::getMergedLocations(ArrayRef<DILocation *> Locs) {
125125
return Merged;
126126
}
127127

128+
using LineColumn = std::pair<unsigned /* Line */, unsigned /* Column */>;
129+
130+
/// Returns the location of DILocalScope, if present, or a default value.
131+
static LineColumn getLocalScopeLocationOr(DIScope *S, LineColumn Default) {
132+
assert(isa<DILocalScope>(S) && "Expected DILocalScope.");
133+
134+
if (isa<DILexicalBlockFile>(S))
135+
return Default;
136+
if (auto *LB = dyn_cast<DILexicalBlock>(S))
137+
return {LB->getLine(), LB->getColumn()};
138+
if (auto *SP = dyn_cast<DISubprogram>(S))
139+
return {SP->getLine(), 0u};
140+
141+
llvm_unreachable("Unhandled type of DILocalScope.");
142+
}
143+
128144
DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
129145
if (!LocA || !LocB)
130146
return nullptr;
@@ -209,27 +225,67 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
209225
return nullptr;
210226

211227
// Return the nearest common scope inside a subprogram.
212-
auto GetNearestCommonScope = [](DIScope *S1, DIScope *S2) -> DIScope * {
213-
SmallPtrSet<DIScope *, 8> Scopes;
228+
auto GetNearestCommonScope =
229+
[](const DILocation *L1,
230+
const DILocation *L2) -> std::pair<DIScope *, LineColumn> {
231+
DIScope *S1 = L1->getScope();
232+
DIScope *S2 = L2->getScope();
233+
234+
SmallMapVector<std::tuple<DIFile *, LineColumn>,
235+
SmallSetVector<DIScope *, 8>, 8>
236+
Scopes;
237+
238+
// When matching DILexicalBlockFile's, ignore column numbers, so that
239+
// DILocation's having different columns within the same
240+
// DILexicalBlockFile will match.
241+
auto getLocForBlockFile = [](LineColumn L) {
242+
L.second = 0;
243+
return L;
244+
};
245+
246+
LineColumn Loc1(L1->getLine(), L1->getColumn());
214247
for (; S1; S1 = S1->getScope()) {
215-
Scopes.insert(S1);
248+
Loc1 = getLocalScopeLocationOr(S1, getLocForBlockFile(Loc1));
249+
Scopes[{S1->getFile(), Loc1}].insert(S1);
250+
216251
if (isa<DISubprogram>(S1))
217252
break;
218253
}
219254

255+
LineColumn Loc2(L2->getLine(), L2->getColumn());
220256
for (; S2; S2 = S2->getScope()) {
221-
if (Scopes.count(S2))
222-
return S2;
257+
Loc2 = getLocalScopeLocationOr(S2, getLocForBlockFile(Loc2));
258+
259+
auto ScopesAtLoc = Scopes.find({S2->getFile(), Loc2});
260+
// No scope found with the same file, line and column as S2.
261+
if (ScopesAtLoc == Scopes.end())
262+
continue;
263+
264+
// Return S2 if it is L1's parent.
265+
if (ScopesAtLoc->second.contains(S2))
266+
return std::make_pair(S2, Loc2);
267+
268+
// Return any L1's parent with the same file, line and column as S2.
269+
if (!ScopesAtLoc->second.empty())
270+
return std::make_pair(*ScopesAtLoc->second.begin(), Loc2);
271+
223272
if (isa<DISubprogram>(S2))
224273
break;
225274
}
226275

227-
return nullptr;
276+
return std::make_pair(nullptr,
277+
LineColumn(L2->getLine(), L2->getColumn()));
228278
};
229279

230-
auto Scope = GetNearestCommonScope(L1->getScope(), L2->getScope());
280+
auto [Scope, ScopeLoc] = GetNearestCommonScope(L1, L2);
231281
assert(Scope && "No common scope in the same subprogram?");
232282

283+
// Use inclusion location if files are different.
284+
if (Scope->getFile() != L1->getFile() || L1->getFile() != L2->getFile()) {
285+
return DILocation::get(C, ScopeLoc.first, ScopeLoc.second, Scope,
286+
InlinedAt);
287+
}
288+
233289
bool SameLine = L1->getLine() == L2->getLine();
234290
bool SameCol = L1->getColumn() == L2->getColumn();
235291
unsigned Line = SameLine ? L1->getLine() : 0;
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
; RUN: opt -mtriple=aarch64-unknown-linux-gnu -S %s -passes=sroa -o - | FileCheck %s
2+
3+
; In this test we want to ensure that the location of phi instruction merged from locations
4+
; of %mul3 and %mul9 belongs to the correct scope (DILexicalBlockFile), so that line
5+
; number of that location belongs to the corresponding file.
6+
7+
; Generated with clang from
8+
; # 1 "1.c" 1
9+
; # 1 "1.c" 2
10+
; int foo(int a) {
11+
; int i = 0;
12+
; if ((a & 1) == 1) {
13+
; a -= 1;
14+
; # 1 "m.c" 1
15+
; # 40 "m.c"
16+
; i += a;
17+
; i -= 10*a;
18+
; i *= a*a;
19+
; # 6 "1.c" 2
20+
; } else {
21+
; a += 3;
22+
; # 1 "m.c" 1
23+
; # 40 "m.c"
24+
; i += a;
25+
; i -= 10*a;
26+
; i *= a*a;
27+
; # 9 "1.c" 2
28+
; }
29+
; return i;
30+
; }
31+
32+
source_filename = "repro.c"
33+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
34+
target triple = "aarch64-unknown-linux-gnu"
35+
36+
; Function Attrs: nounwind uwtable
37+
define dso_local i32 @foo(i32 noundef %a) !dbg !9 {
38+
; CHECK: phi i32 {{.*}}, !dbg [[PHILOC:![0-9]+]]
39+
;
40+
entry:
41+
%a.addr = alloca i32, align 4, !DIAssignID !17
42+
#dbg_assign(i1 undef, !15, !DIExpression(), !17, ptr %a.addr, !DIExpression(), !18)
43+
%i = alloca i32, align 4, !DIAssignID !19
44+
#dbg_assign(i1 undef, !16, !DIExpression(), !19, ptr %i, !DIExpression(), !18)
45+
store i32 %a, ptr %a.addr, align 4, !tbaa !20, !DIAssignID !24
46+
#dbg_assign(i32 %a, !15, !DIExpression(), !24, ptr %a.addr, !DIExpression(), !18)
47+
call void @llvm.lifetime.start.p0(i64 4, ptr %i), !dbg !25
48+
store i32 0, ptr %i, align 4, !dbg !26, !tbaa !20, !DIAssignID !27
49+
#dbg_assign(i32 0, !16, !DIExpression(), !27, ptr %i, !DIExpression(), !18)
50+
%0 = load i32, ptr %a.addr, align 4, !dbg !28, !tbaa !20
51+
%and = and i32 %0, 1, !dbg !30
52+
%cmp = icmp eq i32 %and, 1, !dbg !31
53+
br i1 %cmp, label %if.then, label %if.else, !dbg !31
54+
55+
if.then: ; preds = %entry
56+
%1 = load i32, ptr %a.addr, align 4, !dbg !32, !tbaa !20
57+
%sub = sub nsw i32 %1, 1, !dbg !32
58+
store i32 %sub, ptr %a.addr, align 4, !dbg !32, !tbaa !20, !DIAssignID !34
59+
#dbg_assign(i32 %sub, !15, !DIExpression(), !34, ptr %a.addr, !DIExpression(), !18)
60+
%2 = load i32, ptr %a.addr, align 4, !dbg !35, !tbaa !20
61+
%3 = load i32, ptr %i, align 4, !dbg !38, !tbaa !20
62+
%add = add nsw i32 %3, %2, !dbg !38
63+
store i32 %add, ptr %i, align 4, !dbg !38, !tbaa !20, !DIAssignID !39
64+
#dbg_assign(i32 %add, !16, !DIExpression(), !39, ptr %i, !DIExpression(), !18)
65+
%4 = load i32, ptr %a.addr, align 4, !dbg !40, !tbaa !20
66+
%mul = mul nsw i32 10, %4, !dbg !41
67+
%5 = load i32, ptr %i, align 4, !dbg !42, !tbaa !20
68+
%sub1 = sub nsw i32 %5, %mul, !dbg !42
69+
store i32 %sub1, ptr %i, align 4, !dbg !42, !tbaa !20, !DIAssignID !43
70+
#dbg_assign(i32 %sub1, !16, !DIExpression(), !43, ptr %i, !DIExpression(), !18)
71+
%6 = load i32, ptr %a.addr, align 4, !dbg !44, !tbaa !20
72+
%7 = load i32, ptr %a.addr, align 4, !dbg !45, !tbaa !20
73+
%mul2 = mul nsw i32 %6, %7, !dbg !46
74+
%8 = load i32, ptr %i, align 4, !dbg !47, !tbaa !20
75+
%mul3 = mul nsw i32 %8, %mul2, !dbg !47
76+
store i32 %mul3, ptr %i, align 4, !dbg !47, !tbaa !20, !DIAssignID !48
77+
#dbg_assign(i32 %mul3, !16, !DIExpression(), !48, ptr %i, !DIExpression(), !18)
78+
br label %if.end, !dbg !49
79+
80+
if.else: ; preds = %entry
81+
%9 = load i32, ptr %a.addr, align 4, !dbg !51, !tbaa !20
82+
%add4 = add nsw i32 %9, 3, !dbg !51
83+
store i32 %add4, ptr %a.addr, align 4, !dbg !51, !tbaa !20, !DIAssignID !53
84+
#dbg_assign(i32 %add4, !15, !DIExpression(), !53, ptr %a.addr, !DIExpression(), !18)
85+
%10 = load i32, ptr %a.addr, align 4, !dbg !54, !tbaa !20
86+
%11 = load i32, ptr %i, align 4, !dbg !56, !tbaa !20
87+
%add5 = add nsw i32 %11, %10, !dbg !56
88+
store i32 %add5, ptr %i, align 4, !dbg !56, !tbaa !20, !DIAssignID !57
89+
#dbg_assign(i32 %add5, !16, !DIExpression(), !57, ptr %i, !DIExpression(), !18)
90+
%12 = load i32, ptr %a.addr, align 4, !dbg !58, !tbaa !20
91+
%mul6 = mul nsw i32 10, %12, !dbg !59
92+
%13 = load i32, ptr %i, align 4, !dbg !60, !tbaa !20
93+
%sub7 = sub nsw i32 %13, %mul6, !dbg !60
94+
store i32 %sub7, ptr %i, align 4, !dbg !60, !tbaa !20, !DIAssignID !61
95+
#dbg_assign(i32 %sub7, !16, !DIExpression(), !61, ptr %i, !DIExpression(), !18)
96+
%14 = load i32, ptr %a.addr, align 4, !dbg !62, !tbaa !20
97+
%15 = load i32, ptr %a.addr, align 4, !dbg !63, !tbaa !20
98+
%mul8 = mul nsw i32 %14, %15, !dbg !64
99+
%16 = load i32, ptr %i, align 4, !dbg !65, !tbaa !20
100+
%mul9 = mul nsw i32 %16, %mul8, !dbg !65
101+
store i32 %mul9, ptr %i, align 4, !dbg !65, !tbaa !20, !DIAssignID !66
102+
#dbg_assign(i32 %mul9, !16, !DIExpression(), !66, ptr %i, !DIExpression(), !18)
103+
br label %if.end
104+
105+
if.end: ; preds = %if.else, %if.then
106+
%17 = load i32, ptr %i, align 4, !dbg !67, !tbaa !20
107+
call void @llvm.lifetime.end.p0(i64 4, ptr %i), !dbg !68
108+
ret i32 %17, !dbg !69
109+
}
110+
111+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
112+
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture)
113+
114+
; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
115+
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture)
116+
117+
!llvm.dbg.cu = !{!0}
118+
!llvm.module.flags = !{!2, !3}
119+
!llvm.ident = !{!8}
120+
121+
!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
122+
!1 = !DIFile(filename: "repro.c", directory: "")
123+
!2 = !{i32 7, !"Dwarf Version", i32 5}
124+
!3 = !{i32 2, !"Debug Info Version", i32 3}
125+
!8 = !{!"clang version 21.0.0git"}
126+
!9 = distinct !DISubprogram(name: "foo", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
127+
!10 = !DIFile(filename: "1.c", directory: "")
128+
!11 = !DISubroutineType(types: !12)
129+
!12 = !{!13, !13}
130+
!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
131+
!14 = !{!15, !16}
132+
!15 = !DILocalVariable(name: "a", arg: 1, scope: !9, file: !10, line: 1, type: !13)
133+
!16 = !DILocalVariable(name: "i", scope: !9, file: !10, line: 2, type: !13)
134+
!17 = distinct !DIAssignID()
135+
!18 = !DILocation(line: 0, scope: !9)
136+
!19 = distinct !DIAssignID()
137+
!20 = !{!21, !21, i64 0}
138+
!21 = !{!"int", !22, i64 0}
139+
!22 = !{!"omnipotent char", !23, i64 0}
140+
!23 = !{!"Simple C/C++ TBAA"}
141+
!24 = distinct !DIAssignID()
142+
!25 = !DILocation(line: 2, column: 3, scope: !9)
143+
!26 = !DILocation(line: 2, column: 7, scope: !9)
144+
!27 = distinct !DIAssignID()
145+
!28 = !DILocation(line: 3, column: 8, scope: !29)
146+
!29 = distinct !DILexicalBlock(scope: !9, file: !10, line: 3, column: 7)
147+
!30 = !DILocation(line: 3, column: 10, scope: !29)
148+
!31 = !DILocation(line: 3, column: 15, scope: !29)
149+
!32 = !DILocation(line: 4, column: 7, scope: !33)
150+
!33 = distinct !DILexicalBlock(scope: !29, file: !10, line: 3, column: 21)
151+
!34 = distinct !DIAssignID()
152+
!35 = !DILocation(line: 40, column: 6, scope: !36)
153+
!36 = !DILexicalBlockFile(scope: !33, file: !37, discriminator: 0)
154+
!37 = !DIFile(filename: "m.c", directory: "")
155+
!38 = !DILocation(line: 40, column: 3, scope: !36)
156+
!39 = distinct !DIAssignID()
157+
!40 = !DILocation(line: 41, column: 9, scope: !36)
158+
!41 = !DILocation(line: 41, column: 8, scope: !36)
159+
!42 = !DILocation(line: 41, column: 3, scope: !36)
160+
!43 = distinct !DIAssignID()
161+
!44 = !DILocation(line: 42, column: 6, scope: !36)
162+
!45 = !DILocation(line: 42, column: 8, scope: !36)
163+
!46 = !DILocation(line: 42, column: 7, scope: !36)
164+
!47 = !DILocation(line: 42, column: 3, scope: !36)
165+
!48 = distinct !DIAssignID()
166+
!49 = !DILocation(line: 6, column: 2, scope: !50)
167+
!50 = !DILexicalBlockFile(scope: !33, file: !10, discriminator: 0)
168+
!51 = !DILocation(line: 7, column: 7, scope: !52)
169+
!52 = distinct !DILexicalBlock(scope: !29, file: !10, line: 6, column: 9)
170+
!53 = distinct !DIAssignID()
171+
!54 = !DILocation(line: 40, column: 6, scope: !55)
172+
!55 = !DILexicalBlockFile(scope: !52, file: !37, discriminator: 0)
173+
!56 = !DILocation(line: 40, column: 3, scope: !55)
174+
!57 = distinct !DIAssignID()
175+
!58 = !DILocation(line: 41, column: 9, scope: !55)
176+
!59 = !DILocation(line: 41, column: 8, scope: !55)
177+
!60 = !DILocation(line: 41, column: 3, scope: !55)
178+
!61 = distinct !DIAssignID()
179+
!62 = !DILocation(line: 42, column: 6, scope: !55)
180+
!63 = !DILocation(line: 42, column: 8, scope: !55)
181+
!64 = !DILocation(line: 42, column: 7, scope: !55)
182+
!65 = !DILocation(line: 42, column: 3, scope: !55)
183+
!66 = distinct !DIAssignID()
184+
!67 = !DILocation(line: 10, column: 10, scope: !9)
185+
!68 = !DILocation(line: 11, column: 1, scope: !9)
186+
!69 = !DILocation(line: 10, column: 3, scope: !9)
187+
188+
; CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "foo", scope: [[FILE1:![0-9]+]], file: [[FILE1]], line: 1
189+
; CHECK: [[FILE1]] = !DIFile(filename: "1.c", directory: "")
190+
; CHECK: [[LB1:![0-9]+]] = distinct !DILexicalBlock(scope: [[SP]], file: [[FILE1]], line: 3, column: 7)
191+
; CHECK: [[LB2:![0-9]+]] = distinct !DILexicalBlock(scope: [[LB1]], file: [[FILE1]], line: 3, column: 21)
192+
; CHECK: [[LBF:![0-9]+]] = !DILexicalBlockFile(scope: [[LB2]], file: [[FILE2:![0-9]+]], discriminator: 0)
193+
; CHECK: [[FILE2]] = !DIFile(filename: "m.c", directory: "")
194+
; CHECK: [[PHILOC]] = !DILocation(line: 42, column: 3, scope: [[LBF]])

0 commit comments

Comments
 (0)