Skip to content

Commit fa84d8d

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 1a2539e commit fa84d8d

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"
@@ -118,6 +118,22 @@ DILocation *DILocation::getMergedLocations(ArrayRef<DILocation *> Locs) {
118118
return Merged;
119119
}
120120

121+
using LineColumn = std::pair<unsigned /* Line */, unsigned /* Column */>;
122+
123+
/// Returns the location of DILocalScope, if present, or a default value.
124+
static LineColumn getLocalScopeLocationOr(DIScope *S, LineColumn Default) {
125+
assert(isa<DILocalScope>(S) && "Expected DILocalScope.");
126+
127+
if (isa<DILexicalBlockFile>(S))
128+
return Default;
129+
if (auto *LB = dyn_cast<DILexicalBlock>(S))
130+
return {LB->getLine(), LB->getColumn()};
131+
if (auto *SP = dyn_cast<DISubprogram>(S))
132+
return {SP->getLine(), 0u};
133+
134+
llvm_unreachable("Unhandled type of DILocalScope.");
135+
}
136+
121137
DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
122138
if (!LocA || !LocB)
123139
return nullptr;
@@ -188,27 +204,67 @@ DILocation *DILocation::getMergedLocation(DILocation *LocA, DILocation *LocB) {
188204
return nullptr;
189205

190206
// Return the nearest common scope inside a subprogram.
191-
auto GetNearestCommonScope = [](DIScope *S1, DIScope *S2) -> DIScope * {
192-
SmallPtrSet<DIScope *, 8> Scopes;
207+
auto GetNearestCommonScope =
208+
[](const DILocation *L1,
209+
const DILocation *L2) -> std::pair<DIScope *, LineColumn> {
210+
DIScope *S1 = L1->getScope();
211+
DIScope *S2 = L2->getScope();
212+
213+
SmallMapVector<std::tuple<DIFile *, LineColumn>,
214+
SmallSetVector<DIScope *, 8>, 8>
215+
Scopes;
216+
217+
// When matching DILexicalBlockFile's, ignore column numbers, so that
218+
// DILocation's having different columns within the same
219+
// DILexicalBlockFile will match.
220+
auto getLocForBlockFile = [](LineColumn L) {
221+
L.second = 0;
222+
return L;
223+
};
224+
225+
LineColumn Loc1(L1->getLine(), L1->getColumn());
193226
for (; S1; S1 = S1->getScope()) {
194-
Scopes.insert(S1);
227+
Loc1 = getLocalScopeLocationOr(S1, getLocForBlockFile(Loc1));
228+
Scopes[{S1->getFile(), Loc1}].insert(S1);
229+
195230
if (isa<DISubprogram>(S1))
196231
break;
197232
}
198233

234+
LineColumn Loc2(L2->getLine(), L2->getColumn());
199235
for (; S2; S2 = S2->getScope()) {
200-
if (Scopes.count(S2))
201-
return S2;
236+
Loc2 = getLocalScopeLocationOr(S2, getLocForBlockFile(Loc2));
237+
238+
auto ScopesAtLoc = Scopes.find({S2->getFile(), Loc2});
239+
// No scope found with the same file, line and column as S2.
240+
if (ScopesAtLoc == Scopes.end())
241+
continue;
242+
243+
// Return S2 if it is L1's parent.
244+
if (ScopesAtLoc->second.contains(S2))
245+
return std::make_pair(S2, Loc2);
246+
247+
// Return any L1's parent with the same file, line and column as S2.
248+
if (!ScopesAtLoc->second.empty())
249+
return std::make_pair(*ScopesAtLoc->second.begin(), Loc2);
250+
202251
if (isa<DISubprogram>(S2))
203252
break;
204253
}
205254

206-
return nullptr;
255+
return std::make_pair(nullptr,
256+
LineColumn(L2->getLine(), L2->getColumn()));
207257
};
208258

209-
auto Scope = GetNearestCommonScope(L1->getScope(), L2->getScope());
259+
auto [Scope, ScopeLoc] = GetNearestCommonScope(L1, L2);
210260
assert(Scope && "No common scope in the same subprogram?");
211261

262+
// Use inclusion location if files are different.
263+
if (Scope->getFile() != L1->getFile() || L1->getFile() != L2->getFile()) {
264+
return DILocation::get(C, ScopeLoc.first, ScopeLoc.second, Scope,
265+
InlinedAt);
266+
}
267+
212268
bool SameLine = L1->getLine() == L2->getLine();
213269
bool SameCol = L1->getColumn() == L2->getColumn();
214270
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)