Skip to content

Commit 7c1c7b6

Browse files
committed
Emit debug locations for hop_to_executor instructions.
The function prologue of async funclets inherits its source location from the hop_to_executor instruction. This makes it easier to produce logical backtraces, since the PC in logical frames will always point to the start if the function. rdar://89776340
1 parent 37b8391 commit 7c1c7b6

File tree

8 files changed

+117
-34
lines changed

8 files changed

+117
-34
lines changed

include/swift/SIL/SILLocation.h

+20-8
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ class SILLocation {
267267

268268
SILLocation(ExtendedASTNodeLoc *ext, LocationKind K)
269269
: storage(ext), kindAndFlags(K, ExtendedASTNodeKind) {
270-
assert(ext && !ext->primary.isNull() && !ext->forDebugging.isNull());
270+
assert(ext && !ext->forDebugging.isNull());
271271
}
272272

273273
SILLocation(SourceLoc L, LocationKind K)
@@ -299,9 +299,9 @@ class SILLocation {
299299
/// Artificial locations and the top-level module locations will be null.
300300
bool isNull() const {
301301
switch (getStorageKind()) {
302-
case ASTNodeKind: return storage.ASTNodeLoc.isNull();
303-
case ExtendedASTNodeKind: return false;
304-
case FilenameAndLocationKind: return storage.filePositionLoc == nullptr;;
302+
case ASTNodeKind:
303+
case ExtendedASTNodeKind: return !getPrimaryASTNode();
304+
case FilenameAndLocationKind: return storage.filePositionLoc == nullptr;
305305
case SourceLocKind: return storage.sourceLoc.isInvalid();
306306
}
307307
llvm_unreachable("covered switch");
@@ -344,12 +344,13 @@ class SILLocation {
344344

345345
/// Returns true if the location represents an artificially generated
346346
/// body, such as thunks or default destructors.
347-
///
348-
/// These locations should not be included in the debug line table.
349-
/// These might also need special handling by the debugger since they might
350-
/// contain calls, which the debugger could be able to step into.
351347
bool isAutoGenerated() const { return kindAndFlags.fields.autoGenerated; }
352348

349+
/// Returns false if the location should be represented in debuginfo.
350+
bool isHiddenFromDebugInfo() const {
351+
return isAutoGenerated() && !hasASTNodeForDebugging();
352+
}
353+
353354
/// Returns true if the line number of this location is zero.
354355
bool isLineZero(const SourceManager &SM) const {
355356
return decodeForDebugging(SM).line == 0;
@@ -454,6 +455,7 @@ class RegularLocation : public SILLocation {
454455
RegularLocation(Decl *D) : SILLocation(ASTNodeTy(D), RegularKind) {}
455456
RegularLocation(Pattern *P) : SILLocation(ASTNodeTy(P), RegularKind) {}
456457
RegularLocation(Stmt *S, Pattern *P, SILModule &Module);
458+
RegularLocation(SILLocation ForDebuggingOnly, SILModule &Module);
457459
RegularLocation(SourceLoc L) : SILLocation(L, RegularKind) {}
458460
RegularLocation(FilenameAndLocation *filePos)
459461
: SILLocation(filePos, RegularKind) {}
@@ -485,12 +487,22 @@ class RegularLocation : public SILLocation {
485487
return AL;
486488
}
487489

490+
/// Returns a location that is empty for diagnostics, and L for the debug info
491+
/// Used for \c hop_to_executor instructions.
492+
static RegularLocation getDebugOnlyLocation(SILLocation L, SILModule &M) {
493+
if (L.isASTNode())
494+
return RegularLocation(L, M);
495+
return getAutoGeneratedLocation(L);
496+
}
497+
488498
static bool isKind(const SILLocation& L) {
489499
return L.getKind() == RegularKind;
490500
}
491501

492502
private:
493503
RegularLocation() : SILLocation(RegularKind) {}
504+
static SILLocation::ExtendedASTNodeLoc *
505+
getDebugOnlyExtendedASTNodeLoc(SILLocation L, SILModule &Module);
494506
};
495507

496508
/// Used to represent a return instruction in user code.

lib/IRGen/IRGenDebugInfo.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -2049,7 +2049,7 @@ void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder,
20492049
SILFunction *Fn = DS->getInlinedFunction();
20502050
if (Fn && (Fn->isThunk() || Fn->isTransparent())) {
20512051
L = *SILLocation::getCompilerGeneratedLoc();
2052-
} else if (DS == LastScope && Loc.isAutoGenerated()) {
2052+
} else if (DS == LastScope && Loc.isHiddenFromDebugInfo()) {
20532053
// Reuse the last source location if we are still in the same
20542054
// scope to get a more contiguous line table.
20552055
L = LastFilenameAndLocation;
@@ -2068,7 +2068,7 @@ void IRGenDebugInfoImpl::setCurrentLoc(IRBuilder &Builder,
20682068
// Otherwise use a line 0 artificial location, but the file from the
20692069
// location. If we are emitting CodeView, we do not want to use line zero
20702070
// since it does not represent an artificial line location.
2071-
if (Loc.isAutoGenerated() &&
2071+
if (Loc.isHiddenFromDebugInfo() &&
20722072
Opts.DebugInfoFormat != IRGenDebugInfoFormat::CodeView) {
20732073
L.line = 0;
20742074
L.column = 0;

lib/SIL/IR/SILLocation.cpp

+23-3
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,15 @@ SourceLoc SILLocation::getSourceLoc(ASTNodeTy N) const {
7373
}
7474

7575
SourceLoc SILLocation::getSourceLocForDebugging() const {
76+
if (hasASTNodeForDebugging())
77+
return getSourceLoc(storage.extendedASTNodeLoc->forDebugging);
78+
7679
if (isNull())
7780
return SourceLoc();
7881

7982
if (isSILFile())
8083
return storage.sourceLoc;
8184

82-
if (hasASTNodeForDebugging())
83-
return getSourceLoc(storage.extendedASTNodeLoc->forDebugging);
84-
8585
return getSourceLoc(getPrimaryASTNode());
8686
}
8787

@@ -209,6 +209,26 @@ void SILLocation::print(raw_ostream &OS, const SourceManager &SM) const {
209209
RegularLocation::RegularLocation(Stmt *S, Pattern *P, SILModule &Module) :
210210
SILLocation(new (Module) ExtendedASTNodeLoc(S, P), RegularKind) {}
211211

212+
SILLocation::ExtendedASTNodeLoc *
213+
RegularLocation::getDebugOnlyExtendedASTNodeLoc(SILLocation L,
214+
SILModule &Module) {
215+
if (auto D = L.getAsASTNode<Decl>())
216+
return new (Module) ExtendedASTNodeLoc((Decl *)nullptr, D);
217+
if (auto E = L.getAsASTNode<Expr>())
218+
return new (Module) ExtendedASTNodeLoc((Decl *)nullptr, E);
219+
if (auto S = L.getAsASTNode<Stmt>())
220+
return new (Module) ExtendedASTNodeLoc((Decl *)nullptr, S);
221+
auto P = L.getAsASTNode<Pattern>();
222+
return new (Module) ExtendedASTNodeLoc((Decl *)nullptr, P);
223+
}
224+
225+
RegularLocation::RegularLocation(SILLocation ForDebuggingOnly,
226+
SILModule &Module)
227+
: SILLocation(getDebugOnlyExtendedASTNodeLoc(ForDebuggingOnly, Module),
228+
RegularKind) {
229+
markAutoGenerated();
230+
}
231+
212232
ReturnLocation::ReturnLocation(ReturnStmt *RS) :
213233
SILLocation(ASTNodeTy(RS), ReturnKind) {}
214234

lib/SILGen/SILGen.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -735,12 +735,21 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
735735
return emitted;
736736
}
737737

738+
auto getBestLocation = [](SILDeclRef ref) -> SILLocation {
739+
if (ref.hasDecl())
740+
return ref.getDecl();
741+
if (ref.loc.isNull())
742+
return {(Decl *)nullptr};
743+
if (auto *ace = ref.getAbstractClosureExpr())
744+
return {ace};
745+
return {(Decl *)nullptr};
746+
};
747+
738748
// Note: Do not provide any SILLocation. You can set it afterwards.
739749
SILGenFunctionBuilder builder(*this);
740750
auto &IGM = *this;
741751
auto *F = builder.getOrCreateFunction(
742-
constant.hasDecl() ? constant.getDecl() : (Decl *)nullptr, constant,
743-
forDefinition,
752+
getBestLocation(constant), constant, forDefinition,
744753
[&IGM](SILLocation loc, SILDeclRef constant) -> SILFunction * {
745754
return IGM.getFunction(constant, NotForDefinition);
746755
});

lib/SILGen/SILGenProlog.cpp

+12-7
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,9 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
675675
if (F.isAsync()) {
676676
// For an async function, hop to the executor.
677677
B.createHopToExecutor(
678-
RegularLocation::getAutoGeneratedLocation(F.getLocation()),
679-
ExpectedExecutor, /*mandatory*/ false);
678+
RegularLocation::getDebugOnlyLocation(F.getLocation(), getModule()),
679+
ExpectedExecutor,
680+
/*mandatory*/ false);
680681
} else {
681682
// For a synchronous function, check that we're on the same executor.
682683
// Note: if we "know" that the code is completely Sendable-safe, this
@@ -733,7 +734,8 @@ SILValue SILGenFunction::emitGenericExecutor(SILLocation loc) {
733734
void SILGenFunction::emitPrologGlobalActorHop(SILLocation loc,
734735
Type globalActor) {
735736
ExpectedExecutor = emitLoadGlobalActorExecutor(globalActor);
736-
B.createHopToExecutor(loc, ExpectedExecutor, /*mandatory*/ false);
737+
B.createHopToExecutor(RegularLocation::getDebugOnlyLocation(loc, getModule()),
738+
ExpectedExecutor, /*mandatory*/ false);
737739
}
738740

739741
SILValue SILGenFunction::emitLoadGlobalActorExecutor(Type globalActor) {
@@ -794,7 +796,8 @@ ExecutorBreadcrumb SILGenFunction::emitHopToTargetExecutor(
794796
SILLocation loc, SILValue executor) {
795797
// Record that we need to hop back to the current executor.
796798
auto breadcrumb = ExecutorBreadcrumb(true);
797-
B.createHopToExecutor(loc.asAutoGenerated(), executor, /*mandatory*/ false);
799+
B.createHopToExecutor(RegularLocation::getDebugOnlyLocation(loc, getModule()),
800+
executor, /*mandatory*/ false);
798801
return breadcrumb;
799802
}
800803

@@ -835,7 +838,8 @@ void SILGenFunction::emitHopToActorValue(SILLocation loc, ManagedValue actor) {
835838
"Builtin.hopToActor must be in an actor-independent function");
836839
}
837840
SILValue executor = emitLoadActorExecutor(loc, actor);
838-
B.createHopToExecutor(loc.asAutoGenerated(), executor, /*mandatory*/ true);
841+
B.createHopToExecutor(RegularLocation::getDebugOnlyLocation(loc, getModule()),
842+
executor, /*mandatory*/ true);
839843
}
840844

841845
void SILGenFunction::emitPreconditionCheckExpectedExecutor(
@@ -874,8 +878,9 @@ void ExecutorBreadcrumb::emit(SILGenFunction &SGF, SILLocation loc) {
874878
if (mustReturnToExecutor) {
875879
assert(SGF.ExpectedExecutor || SGF.unsafelyInheritsExecutor());
876880
if (auto executor = SGF.ExpectedExecutor)
877-
SGF.B.createHopToExecutor(loc.asAutoGenerated(), executor,
878-
/*mandatory*/ false);
881+
SGF.B.createHopToExecutor(
882+
RegularLocation::getDebugOnlyLocation(loc, SGF.getModule()), executor,
883+
/*mandatory*/ false);
879884
}
880885
}
881886

lib/SILOptimizer/Mandatory/LowerHopToActor.cpp

+10-11
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ namespace {
4848
/// IRGen expects hops to be to executors before it runs.
4949
class LowerHopToActor {
5050
SILFunction *F;
51-
SILBuilder B;
5251
DominanceInfo *Dominance;
5352

5453
/// A map from an actor value to the executor we've derived for it.
@@ -57,11 +56,12 @@ class LowerHopToActor {
5756
bool processHop(HopToExecutorInst *hop);
5857
bool processExtract(ExtractExecutorInst *extract);
5958

60-
SILValue emitGetExecutor(SILLocation loc, SILValue actor, bool makeOptional);
59+
SILValue emitGetExecutor(SILBuilderWithScope &B, SILLocation loc,
60+
SILValue actor, bool makeOptional);
6161

6262
public:
6363
LowerHopToActor(SILFunction *f, DominanceInfo *dominance)
64-
: F(f), B(*F), Dominance(dominance) { }
64+
: F(f), Dominance(dominance) { }
6565

6666
/// The entry point to the transformation.
6767
bool run();
@@ -99,9 +99,7 @@ bool LowerHopToActor::processHop(HopToExecutorInst *hop) {
9999
if (isOptionalBuiltinExecutor(actor->getType()))
100100
return false;
101101

102-
B.setInsertionPoint(hop);
103-
B.setCurrentDebugScope(hop->getDebugScope());
104-
102+
SILBuilderWithScope B(hop);
105103
SILValue executor;
106104
if (actor->getType().is<BuiltinExecutorType>()) {
107105
// IRGen expects an optional Builtin.Executor, not a Builtin.Executor
@@ -112,7 +110,7 @@ bool LowerHopToActor::processHop(HopToExecutorInst *hop) {
112110
} else {
113111
// Get the dominating executor value for this actor, if available,
114112
// or else emit code to derive it.
115-
executor = emitGetExecutor(hop->getLoc(), actor, /*optional*/true);
113+
executor = emitGetExecutor(B, hop->getLoc(), actor, /*optional*/true);
116114
}
117115
assert(executor && "executor not set");
118116

@@ -127,9 +125,9 @@ bool LowerHopToActor::processExtract(ExtractExecutorInst *extract) {
127125
// Dig out the executor.
128126
auto executor = extract->getExpectedExecutor();
129127
if (!isOptionalBuiltinExecutor(executor->getType())) {
130-
B.setInsertionPoint(extract);
131-
B.setCurrentDebugScope(extract->getDebugScope());
132-
executor = emitGetExecutor(extract->getLoc(), executor, /*optional*/false);
128+
SILBuilderWithScope B(extract);
129+
executor =
130+
emitGetExecutor(B, extract->getLoc(), executor, /*optional*/ false);
133131
}
134132

135133
// Unconditionally replace the extract with the executor.
@@ -156,7 +154,8 @@ static AccessorDecl *getUnownedExecutorGetter(ASTContext &ctx,
156154
return nullptr;
157155
}
158156

159-
SILValue LowerHopToActor::emitGetExecutor(SILLocation loc, SILValue actor,
157+
SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
158+
SILLocation loc, SILValue actor,
160159
bool makeOptional) {
161160
// Get the dominating executor value for this actor, if available,
162161
// or else emit code to derive it.

test/DebugInfo/async-let.swift

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %target-swift-frontend %s -emit-ir -g -o - \
2+
// RUN: -module-name M -disable-availability-checking \
3+
// RUN: -parse-as-library | %FileCheck %s --check-prefix=CHECK
4+
// REQUIRES: concurrency
5+
6+
public actor Alice {
7+
let bob = Bob()
8+
9+
// CHECK: define {{.*}}$s1M5AliceC4callyyYaFTY0_{{.*}} !dbg ![[SCOPE0:[0-9]+]]
10+
// CHECK: call i8* @__swift_async_resume_get_context{{.*}}!dbg ![[HOP0:[0-9]+]]
11+
12+
// CHECK: define {{.*}}$s1M5AliceC4callyyYaFTY1_{{.*}} !dbg ![[SCOPE1:[0-9]+]]
13+
// CHECK: call i8* @__swift_async_resume_get_context{{.*}}!dbg ![[HOP1:[0-9]+]]
14+
15+
// CHECK: define {{.*}}$s1M5AliceC4callyyYaFSiyYaYbcfu_TY0_{{.*}} !dbg ![[LET_SCOPE0:[0-9]+]]
16+
// CHECK: call i8* @__swift_async_resume_get_context{{.*}}!dbg ![[LET_HOP0:[0-9]+]]
17+
18+
// CHECK: define {{.*}}$s1M5AliceC4callyyYaFSiyYaYbcfu_TY2_{{.*}} !dbg ![[LET_SCOPE1:[0-9]+]]
19+
// CHECK: call i8* @__swift_async_resume_get_context{{.*}}!dbg ![[LET_HOP1:[0-9]+]]
20+
public func call() async {
21+
// CHECK: ![[SCOPE0]] = distinct !DISubprogram({{.*}}line: [[@LINE-1]]
22+
// CHECK: ![[HOP0]] = !DILocation(line: [[@LINE-2]], column: 15
23+
async let a = bob.call(x: 1)
24+
// CHECK: ![[SCOPE1]] = distinct !DISubprogram({{.*}}line: [[@LINE-4]]
25+
// CHECK: ![[HOP1]] = !DILocation(line: [[@LINE+5]], column: 17
26+
// CHECK: ![[LET_SCOPE0]] = distinct !DISubprogram({{.*}}line: [[@LINE-3]]
27+
// CHECK: ![[LET_HOP0]] = !DILocation(line: [[@LINE-4]], column: 19
28+
// CHECK: ![[LET_SCOPE1]] = distinct !DISubprogram({{.*}}line: [[@LINE-5]]
29+
// CHECK: ![[LET_HOP1]] = !DILocation(line: [[@LINE-6]], column: 23
30+
print(await a)
31+
}
32+
}
33+
34+
public actor Bob {
35+
public func call(x: Int) async -> Int {
36+
return x + x
37+
}
38+
}

test/SILGen/concurrent_prologue.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// CHECK: %1 = function_ref {{.*}}loc "{{.*}}.swift":[[@LINE-3]]:17,{{.*}}:auto_gen
1111
// CHECK: %2 = apply %1(%0) {{.*}}loc "{{.*}}.swift":[[@LINE-4]]:17,{{.*}}:auto_gen
1212
// CHECK: begin_borrow {{.*}}loc "{{.*}}.swift":[[@LINE-5]]:17,{{.*}}:auto_gen
13-
// CHECK: hop_to_executor {{.*}}loc "{{.*}}.swift":[[@LINE-6]]:17,{{.*}}:auto_gen
13+
// CHECK: hop_to_executor {{.*}}loc "{{.*}}.swift":[[@LINE-6]]:17,{{.*}}
1414
// CHECK: // end sil function '$s1a1fSiyYaF'
1515
return 23
1616
}

0 commit comments

Comments
 (0)