Skip to content

Commit b6a7d69

Browse files
committed
Debug Info: Fix the lowering of the SILDebugScope tree to the LLVM
inlined-at chain. The previous implementation was only correct for cases where the inliner inlined bottom-up in the call graph, which happened to cover the majority of all cases. rdar://problem/24462475
1 parent 44951f0 commit b6a7d69

File tree

5 files changed

+145
-38
lines changed

5 files changed

+145
-38
lines changed

include/swift/SIL/SILDebugScope.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ class SILDebugScope : public SILAllocated<SILDebugScope> {
4444
/// holds only the location of the call site. The parent scope will be
4545
/// the scope of the inlined call site.
4646
///
47-
/// Note that compared to the inlinedAt field in llvm::DILocation
48-
/// the inlined call site chain in SILDebugScope uses the reversed order.
47+
/// Note that compared to the inlinedAt chain in llvm::DILocation
48+
/// SILDebugScope represents an inline tree.
4949
const SILDebugScope *InlinedCallSite;
5050

5151
SILDebugScope(SILLocation Loc, SILFunction &SILFn,
@@ -70,6 +70,9 @@ class SILDebugScope : public SILAllocated<SILDebugScope> {
7070
assert(CallSiteScope && CalleeScope);
7171
assert(CalleeScope->getParentFunction()->isInlined() &&
7272
"function of inlined debug scope is not inlined");
73+
if (InlinedCallSite)
74+
assert(!InlinedCallSite->InlinedCallSite &&
75+
"a call site scope cannot have an inlined call site");
7376
}
7477

7578
/// Return the function this scope originated from before being inlined.
@@ -84,17 +87,36 @@ class SILDebugScope : public SILAllocated<SILDebugScope> {
8487
return Scope->Parent.get<SILFunction *>();
8588
}
8689

90+
/// Return this scope without inline information.
91+
const SILDebugScope *getInlinedScope() const {
92+
return InlinedCallSite ? Parent.get<const SILDebugScope*>() : this;
93+
}
94+
8795
/// Return the parent function of this scope. If the scope was
8896
/// inlined this recursively returns the function it was inlined
8997
/// into.
9098
SILFunction *getParentFunction() const {
9199
if (InlinedCallSite)
92-
return InlinedCallSite->getParentFunction();
100+
return InlinedCallSite->Parent.get<const SILDebugScope *>()
101+
->getParentFunction();
93102
if (auto *ParentScope = Parent.dyn_cast<const SILDebugScope *>())
94103
return ParentScope->getParentFunction();
95104
return Parent.get<SILFunction *>();
96105
}
97106

107+
typedef SmallVector<const SILDebugScope *, 8> InlineScopeList;
108+
static void flatten(const SILDebugScope *DS, InlineScopeList &List);
109+
110+
// Return a flattened representation of the inline scope tree that
111+
// is equivalent to the reversed inlined-at chain.
112+
InlineScopeList flattenedInlineTree() const {
113+
InlineScopeList List;
114+
flatten(this, List);
115+
return List;
116+
}
117+
118+
void dump(SourceManager &SM, llvm::raw_ostream &OS = llvm::errs(),
119+
unsigned Indent = 0) const;
98120
};
99121

100122
#ifndef NDEBUG

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -284,30 +284,22 @@ static bool isAbstractClosure(const SILLocation &Loc) {
284284
return false;
285285
}
286286

287-
/// Construct an inlined-at location from a SILScope.
288-
llvm::MDNode *
289-
IRGenDebugInfo::createInlinedAt(const SILDebugScope *CallSite) {
290-
if (!CallSite)
291-
return nullptr;
292-
293-
// In SIL the inlined-at information is part of the scopes, in LLVM
294-
// IR it is part of the location. Transforming the inlined-at SIL scope
295-
// to a location means skipping the inlined-at scope.
296-
llvm::DIScope * ParentScope;
297-
if (auto *Parent = CallSite->Parent.dyn_cast<const SILDebugScope *>())
298-
ParentScope = getOrCreateScope(Parent);
299-
else
300-
ParentScope = getOrCreateScope(CallSite);
301-
287+
/// Construct an LLVM inlined-at location from a SILDebugScope,
288+
/// reversing the order in the process.
289+
llvm::MDNode *IRGenDebugInfo::createInlinedAt(const SILDebugScope *DS) {
302290
llvm::MDNode *InlinedAt = nullptr;
303-
304-
// If this is itself an inlined location, recursively create the
305-
// inlined-at location for it.
306-
InlinedAt = createInlinedAt(CallSite->InlinedCallSite);
307-
308-
auto InlineLoc = getLoc(SM, CallSite->Loc.getDebugSourceLoc());
309-
return llvm::DebugLoc::get(InlineLoc.Line, InlineLoc.Col, ParentScope,
310-
InlinedAt);
291+
if (DS) {
292+
for (auto *CS : DS->flattenedInlineTree()) {
293+
// In SIL the inlined-at information is part of the scopes, in
294+
// LLVM IR it is part of the location. Transforming the inlined-at
295+
// SIL scope to a location means skipping the inlined-at scope.
296+
auto *Parent = CS->Parent.get<const SILDebugScope *>();
297+
auto *ParentScope = getOrCreateScope(Parent);
298+
auto L = getLoc(SM, CS->Loc.getDebugSourceLoc());
299+
InlinedAt = llvm::DebugLoc::get(L.Line, L.Col, ParentScope, InlinedAt);
300+
}
301+
}
302+
return InlinedAt;
311303
}
312304

313305
#ifndef NDEBUG
@@ -339,7 +331,9 @@ bool IRGenDebugInfo::lineNumberIsSane(IRBuilder &Builder, unsigned Line) {
339331
void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
340332
Optional<SILLocation> Loc) {
341333
assert(DS && "empty scope");
342-
auto *Scope = getOrCreateScope(DS);
334+
// Inline info is emitted as part of the location below; extract the
335+
// original scope here.
336+
auto *Scope = getOrCreateScope(DS->getInlinedScope());
343337
if (!Scope)
344338
return;
345339

@@ -380,7 +374,7 @@ void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
380374
LastDebugLoc = L;
381375
LastScope = DS;
382376

383-
auto *InlinedAt = createInlinedAt(DS->InlinedCallSite);
377+
auto *InlinedAt = createInlinedAt(DS);
384378
assert(((!InlinedAt) || (InlinedAt && Scope)) && "inlined w/o scope");
385379
assert(parentScopesAreSane(DS) && "parent scope sanity check failed");
386380
auto DL = llvm::DebugLoc::get(L.Line, L.Col, Scope, InlinedAt);
@@ -1082,8 +1076,9 @@ void IRGenDebugInfo::emitDbgIntrinsic(llvm::BasicBlock *BB,
10821076
unsigned Col, llvm::DILocalScope *Scope,
10831077
const SILDebugScope *DS) {
10841078
// Set the location/scope of the intrinsic.
1085-
auto *InlinedAt = createInlinedAt(DS->InlinedCallSite);
1079+
auto *InlinedAt = createInlinedAt(DS);
10861080
auto DL = llvm::DebugLoc::get(Line, Col, Scope, InlinedAt);
1081+
10871082
// An alloca may only be described by exactly one dbg.declare.
10881083
if (isa<llvm::AllocaInst>(Storage) && llvm::FindAllocaDbgDeclare(Storage))
10891084
return;

lib/SIL/SILPrinter.cpp

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -611,15 +611,16 @@ class SILPrinter : public SILVisitor<SILPrinter> {
611611
}
612612

613613
// Print inlined-at location, if any.
614-
while (DS && DS->InlinedCallSite) {
615-
*this << ": perf_inlined_at ";
616-
auto CallSite = DS->InlinedCallSite->Loc;
617-
if (!CallSite.isNull())
618-
CallSite.getSourceLoc().print(
614+
if (DS) {
615+
for (auto *CS : reversed(DS->flattenedInlineTree())) {
616+
*this << ": perf_inlined_at ";
617+
auto CallSite = CS->Loc;//DS->InlinedCallSite->Loc;
618+
if (!CallSite.isNull())
619+
CallSite.getSourceLoc().print(
619620
PrintState.OS, M.getASTContext().SourceMgr, LastBufferID);
620-
else
621-
*this << "?";
622-
DS = DS->Parent.dyn_cast<const SILDebugScope *>();
621+
else
622+
*this << "?";
623+
}
623624
}
624625
}
625626

@@ -1999,3 +2000,41 @@ void SILCoverageMap::print(llvm::raw_ostream &OS, bool ShouldSort,
19992000
void SILCoverageMap::dump() const {
20002001
print(llvm::errs());
20012002
}
2003+
2004+
void SILDebugScope::flatten(const SILDebugScope *DS,
2005+
SILDebugScope::InlineScopeList &List) {
2006+
if (DS) {
2007+
if (auto *CS = DS->InlinedCallSite) {
2008+
flatten(CS->Parent.dyn_cast<const SILDebugScope *>(), List);
2009+
List.push_back(CS);
2010+
}
2011+
flatten(DS->Parent.dyn_cast<const SILDebugScope *>(), List);
2012+
}
2013+
}
2014+
2015+
void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
2016+
unsigned Indent) const {
2017+
OS << "{\n";
2018+
OS.indent(Indent);
2019+
Loc.getSourceLoc().print(OS, SM);
2020+
OS << "\n";
2021+
OS.indent(Indent + 2);
2022+
OS << " parent: ";
2023+
if (auto *P = Parent.dyn_cast<const SILDebugScope *>()) {
2024+
P->dump(SM, OS, Indent + 2);
2025+
OS.indent(Indent + 2);
2026+
}
2027+
else if (auto *F = Parent.dyn_cast<SILFunction *>())
2028+
OS << "@" << F->getName();
2029+
else
2030+
OS << "nullptr";
2031+
2032+
OS << "\n";
2033+
OS.indent(Indent + 2);
2034+
if (auto *CS = InlinedCallSite) {
2035+
OS << "inlinedCallSite: ";
2036+
CS->dump(SM, OS, Indent + 2);
2037+
OS.indent(Indent + 2);
2038+
}
2039+
OS << "}\n";
2040+
}

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ bool SILInliner::inlineFunction(FullApplySite AI, ArrayRef<SILValue> Args) {
3737
return false;
3838

3939
SILFunction &F = getBuilder().getFunction();
40+
if (CalleeFunction->getName() == "_TTSg5Vs4Int8___TFVs12_ArrayBufferg9_isNativeSb"
41+
&& F.getName() == "_TTSg5Vs4Int8___TFVs12_ArrayBufferg8endIndexSi")
42+
llvm::errs();
4043

4144
assert(AI.getFunction() && AI.getFunction() == &F &&
4245
"Inliner called on apply instruction in wrong function?");
@@ -72,7 +75,7 @@ bool SILInliner::inlineFunction(FullApplySite AI, ArrayRef<SILValue> Args) {
7275
// Performance inlining. Construct a proper inline scope pointing
7376
// back to the call site.
7477
CallSiteScope = new (F.getModule())
75-
SILDebugScope(AI.getLoc(), F, AIScope, AIScope->InlinedCallSite);
78+
SILDebugScope(AI.getLoc(), F, AIScope);
7679
assert(CallSiteScope->getParentFunction() == &F);
7780
}
7881
assert(CallSiteScope && "call site has no scope");

test/DebugInfo/inlinedAt.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-swift-frontend %s -O -I %t -emit-sil -emit-verbose-sil -o - \
2+
// RUN: | FileCheck %s --check-prefix=CHECK-SIL
3+
// RUN: %target-swift-frontend %s -O -I %t -emit-ir -g -o - | FileCheck %s
4+
5+
#line 100 "abc.swift"
6+
@inline(__always)
7+
func h(k : Int) -> Int { // 101
8+
return k // 102
9+
}
10+
11+
#line 200 "abc.swift"
12+
@inline(__always)
13+
func g(j : Int) -> Int { // 201
14+
return h(j) // 202
15+
}
16+
17+
#line 301 "abc.swift"
18+
public func f(i : Int) -> Int { // 301
19+
return g(i) // 302
20+
}
21+
22+
// CHECK-SIL: sil {{.*}}@_TF9inlinedAt1fFSiSi :
23+
// CHECK-SIL-NOT: return
24+
// CHECK-SIL: debug_value %0 : $Int, let, name "k", argno 1
25+
// CHECK-SIL-SAME: line:101:8:in_prologue
26+
// CHECK-SIL-SAME: perf_inlined_at line:202:10
27+
// CHECK-SIL-SAME: perf_inlined_at line:302:10
28+
29+
// CHECK: define {{.*}}@_TF9inlinedAt1fFSiSi(i64)
30+
// CHECK-NOT: ret
31+
// CHECK: @llvm.dbg.value
32+
// CHECK: @llvm.dbg.value
33+
// CHECK: @llvm.dbg.value({{.*}}), !dbg ![[L1:.*]]
34+
35+
// CHECK: ![[F:.*]] = distinct !DISubprogram(name: "f",
36+
// CHECK: ![[G:.*]] = distinct !DISubprogram(name: "g",
37+
// CHECK: ![[H:.*]] = distinct !DISubprogram(name: "h",
38+
39+
// CHECK: ![[L3:.*]] = !DILocation(line: 302, column: 13,
40+
// CHECK-SAME: scope: ![[F_SCOPE:.*]])
41+
// CHECK: ![[F_SCOPE]] = distinct !DILexicalBlock(scope: ![[F]],
42+
// CHECK-SAME: line: 301, column: 31)
43+
// CHECK: ![[L1]] = !DILocation(line: 101, column: 8, scope: ![[H]],
44+
// CHECK-SAME: inlinedAt: ![[L2:.*]])
45+
// CHECK: ![[L2]] = !DILocation(line: 202, column: 13, scope: ![[G_SCOPE:.*]],
46+
// CHECK-SAME: inlinedAt: ![[L3]])
47+
// CHECK: ![[G_SCOPE]] = distinct !DILexicalBlock(scope: ![[G]],
48+
// CHECK-SAME: line: 201, column: 24)

0 commit comments

Comments
 (0)