Skip to content

Commit 0e6a0b8

Browse files
committed
Add debug info support for function arguments in async functions.
This commit depends on changes to the coroutine-splitting pass in LLVM. Shadow copies are also turned off for async function arguments, because they make it impossible to track debug info during coroutine splitting. Instead we are relying on LLVM's CoroSplit.cpp to emit shadow copies. The Swift frontend gives CoroSplit license to move do this by describing the arguments using a dbg.declare intrinsic, even though it points to chain of load/GEP/bitcase instructions into the Swift context function argument. rdar://71866936
1 parent 08d9680 commit 0e6a0b8

File tree

5 files changed

+110
-26
lines changed

5 files changed

+110
-26
lines changed

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
197197
void emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage,
198198
llvm::DILocalVariable *Var, llvm::DIExpression *Expr,
199199
unsigned Line, unsigned Col, llvm::DILocalScope *Scope,
200-
const SILDebugScope *DS);
200+
const SILDebugScope *DS, bool InCoroContext = false);
201201
void emitGlobalVariableDeclaration(llvm::GlobalVariable *Storage,
202202
StringRef Name, StringRef LinkageName,
203203
DebugTypeInfo DebugType,
@@ -2367,7 +2367,7 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
23672367

23682368
for (llvm::Value *Piece : Storage) {
23692369
SmallVector<uint64_t, 3> Operands;
2370-
if (Indirection)
2370+
if (Indirection == IndirectValue || Indirection == CoroIndirectValue)
23712371
Operands.push_back(llvm::dwarf::DW_OP_deref);
23722372

23732373
if (IsPiece) {
@@ -2389,7 +2389,9 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
23892389
Operands.push_back(SizeInBits);
23902390
}
23912391
emitDbgIntrinsic(Builder, Piece, Var, DBuilder.createExpression(Operands),
2392-
Line, Loc.Column, Scope, DS);
2392+
Line, Loc.Column, Scope, DS,
2393+
Indirection == CoroDirectValue ||
2394+
Indirection == CoroIndirectValue);
23932395
}
23942396

23952397
// Emit locationless intrinsic for variables that were optimized away.
@@ -2401,7 +2403,7 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
24012403
void IRGenDebugInfoImpl::emitDbgIntrinsic(
24022404
IRBuilder &Builder, llvm::Value *Storage, llvm::DILocalVariable *Var,
24032405
llvm::DIExpression *Expr, unsigned Line, unsigned Col,
2404-
llvm::DILocalScope *Scope, const SILDebugScope *DS) {
2406+
llvm::DILocalScope *Scope, const SILDebugScope *DS, bool InCoroContext) {
24052407
// Set the location/scope of the intrinsic.
24062408
auto *InlinedAt = createInlinedAt(DS);
24072409
auto DL = llvm::DebugLoc::get(Line, Col, Scope, InlinedAt);
@@ -2420,13 +2422,23 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic(
24202422
DBuilder.insertDeclare(Alloca, Var, Expr, DL, &*InsertBefore);
24212423
else
24222424
DBuilder.insertDeclare(Alloca, Var, Expr, DL, ParentBB);
2423-
} else if (isa<llvm::IntrinsicInst>(Storage) &&
2424-
cast<llvm::IntrinsicInst>(Storage)->getIntrinsicID() ==
2425-
llvm::Intrinsic::coro_alloca_get) {
2425+
} else if ((isa<llvm::IntrinsicInst>(Storage) &&
2426+
cast<llvm::IntrinsicInst>(Storage)->getIntrinsicID() ==
2427+
llvm::Intrinsic::coro_alloca_get)) {
24262428
// FIXME: The live range of a coroutine alloca within the function may be
24272429
// limited, so using a dbg.addr instead of a dbg.declare would be more
24282430
// appropriate.
24292431
DBuilder.insertDeclare(Storage, Var, Expr, DL, BB);
2432+
} else if (InCoroContext && (Var->getArg() || Var->isArtificial())) {
2433+
// Function arguments in async functions are emitted without a shadow copy
2434+
// (that would interfer with coroutine splitting) but with a dbg.declare to
2435+
// give CoroSplit.cpp license to emit a shadow copy for them pointing inside
2436+
// the Swift Context argument that is valid throughout the function.
2437+
auto &EntryBlock = BB->getParent()->getEntryBlock();
2438+
if (auto *InsertBefore = &*EntryBlock.getFirstInsertionPt())
2439+
DBuilder.insertDeclare(Storage, Var, Expr, DL, InsertBefore);
2440+
else
2441+
DBuilder.insertDeclare(Storage, Var, Expr, DL, &EntryBlock);
24302442
} else {
24312443
// Insert a dbg.value at the current insertion point.
24322444
DBuilder.insertDbgValueIntrinsic(Storage, Var, Expr, DL, BB);
@@ -2506,7 +2518,8 @@ void IRGenDebugInfoImpl::emitTypeMetadata(IRGenFunction &IGF,
25062518
// swift.type is already a pointer type,
25072519
// having a shadow copy doesn't add another
25082520
// layer of indirection.
2509-
DirectValue, ArtificialValue);
2521+
IGF.isAsync() ? CoroDirectValue : DirectValue,
2522+
ArtificialValue);
25102523
}
25112524

25122525
SILLocation::DebugLoc IRGenDebugInfoImpl::decodeSourceLoc(SourceLoc SL) {
@@ -2609,9 +2622,10 @@ void IRGenDebugInfo::emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage,
26092622
llvm::DILocalVariable *Var,
26102623
llvm::DIExpression *Expr, unsigned Line,
26112624
unsigned Col, llvm::DILocalScope *Scope,
2612-
const SILDebugScope *DS) {
2625+
const SILDebugScope *DS,
2626+
bool InCoroContext) {
26132627
static_cast<IRGenDebugInfoImpl *>(this)->emitDbgIntrinsic(
2614-
Builder, Storage, Var, Expr, Line, Col, Scope, DS);
2628+
Builder, Storage, Var, Expr, Line, Col, Scope, DS, InCoroContext);
26152629
}
26162630

26172631
void IRGenDebugInfo::emitGlobalVariableDeclaration(

lib/IRGen/IRGenDebugInfo.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ class IRBuilder;
3838
class IRGenFunction;
3939
class IRGenModule;
4040

41-
enum IndirectionKind : bool { DirectValue = false, IndirectValue = true };
41+
enum IndirectionKind {
42+
DirectValue,
43+
IndirectValue,
44+
CoroDirectValue,
45+
CoroIndirectValue
46+
};
4247
enum ArtificialKind : bool { RealValue = false, ArtificialValue = true };
4348

4449
/// Helper object that keeps track of the current CompileUnit, File,
@@ -61,10 +66,11 @@ class IRGenDebugInfo {
6166
void setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
6267
SILLocation Loc);
6368

64-
/// Replace the current debug location in \p Builder with the same location, but contained in an
65-
/// inlined function which is named like \p failureMsg.
69+
/// Replace the current debug location in \p Builder with the same location,
70+
/// but contained in an inlined function which is named like \p failureMsg.
6671
///
67-
/// This lets the debugger display the \p failureMsg as an inlined function frame.
72+
/// This lets the debugger display the \p failureMsg as an inlined function
73+
/// frame.
6874
void addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg);
6975

7076
void clearLoc(IRBuilder &Builder);
@@ -143,7 +149,7 @@ class IRGenDebugInfo {
143149
void emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage,
144150
llvm::DILocalVariable *Var, llvm::DIExpression *Expr,
145151
unsigned Line, unsigned Col, llvm::DILocalScope *Scope,
146-
const SILDebugScope *DS);
152+
const SILDebugScope *DS, bool InCoroContext = false);
147153

148154
enum { NotHeapAllocated = false };
149155

lib/IRGen/IRGenSIL.cpp

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -726,9 +726,9 @@ class IRGenSILFunction :
726726

727727
/// Unconditionally emit a stack shadow copy of an \c llvm::Value.
728728
llvm::Value *emitShadowCopy(llvm::Value *Storage, const SILDebugScope *Scope,
729-
SILDebugVariable VarInfo, llvm::Optional<Alignment> _Align) {
729+
SILDebugVariable VarInfo,
730+
llvm::Optional<Alignment> _Align) {
730731
auto Align = _Align.getValueOr(IGM.getPointerAlignment());
731-
732732
unsigned ArgNo = VarInfo.ArgNo;
733733
auto &Alloca = ShadowStackSlots[{ArgNo, {Scope, VarInfo.Name}}];
734734
if (!Alloca.isValid())
@@ -750,10 +750,13 @@ class IRGenSILFunction :
750750
SILDebugVariable VarInfo,
751751
bool IsAnonymous,
752752
llvm::Optional<Alignment> Align = None) {
753-
// Never emit shadow copies when optimizing, or if already on the stack.
754-
// No debug info is emitted for refcounts either.
753+
// Never emit shadow copies when optimizing, or if already on the stack. No
754+
// debug info is emitted for refcounts either. Shadow copies are also
755+
// turned off for async functions, because they make it impossible to track
756+
// debug info during coroutine splitting. Instead we are relying on LLVM's
757+
// CoroSplit.cpp to emit shadow copies.
755758
if (IGM.IRGen.Opts.DisableDebuggerShadowCopies ||
756-
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous ||
759+
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || CurSILFn->isAsync() ||
757760
isa<llvm::AllocaInst>(Storage) || isa<llvm::UndefValue>(Storage) ||
758761
!needsShadowCopy(Storage))
759762
return Storage;
@@ -780,7 +783,7 @@ class IRGenSILFunction :
780783

781784
// Only do this at -O0.
782785
if (IGM.IRGen.Opts.DisableDebuggerShadowCopies ||
783-
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous) {
786+
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || CurSILFn->isAsync()) {
784787
auto vals = e.claimAll();
785788
copy.append(vals.begin(), vals.end());
786789
return;
@@ -4334,8 +4337,28 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) {
43344337
assert(VarInfo && "debug_value_addr without debug info");
43354338
bool IsAnonymous = false;
43364339
bool IsLoadablyByAddress = isa<AllocStackInst>(SILVal);
4340+
IndirectionKind Indirection =
4341+
(IsLoadablyByAddress) ? DirectValue : IndirectValue;
43374342
VarInfo->Name = getVarName(i, IsAnonymous);
4338-
auto Addr = getLoweredAddress(SILVal).getAddress();
4343+
auto *Addr = getLoweredAddress(SILVal).getAddress();
4344+
if (CurSILFn->isAsync() && VarInfo->ArgNo) {
4345+
#ifndef NDEBUG
4346+
llvm::Value *Storage = Addr;
4347+
while (Storage) {
4348+
if (auto *LdInst = dyn_cast<llvm::LoadInst>(Storage))
4349+
Storage = LdInst->getOperand(0);
4350+
else if (auto *GEPInst = dyn_cast<llvm::GetElementPtrInst>(Storage))
4351+
Storage = GEPInst->getOperand(0);
4352+
else if (auto *BCInst = dyn_cast<llvm::BitCastInst>(Storage))
4353+
Storage = BCInst->getOperand(0);
4354+
else
4355+
break;
4356+
}
4357+
assert(llvm::isa<llvm::Argument>(Storage) &&
4358+
"arg expected to be load from inside %swift.context");
4359+
#endif
4360+
Indirection = CoroIndirectValue;
4361+
}
43394362
SILType SILTy = SILVal->getType();
43404363
auto RealType = SILTy.getASTType();
43414364

@@ -4349,8 +4372,7 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) {
43494372
// intrinsic.
43504373
emitDebugVariableDeclaration(
43514374
emitShadowCopyIfNeeded(Addr, i->getDebugScope(), *VarInfo, IsAnonymous),
4352-
DbgTy, SILType(), i->getDebugScope(), Decl, *VarInfo,
4353-
(IsLoadablyByAddress) ? DirectValue : IndirectValue);
4375+
DbgTy, SILType(), i->getDebugScope(), Decl, *VarInfo, Indirection);
43544376
}
43554377

43564378
void IRGenSILFunction::visitFixLifetimeInst(swift::FixLifetimeInst *i) {

lib/IRGen/LocalTypeData.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,9 @@ static void maybeEmitDebugInfoForLocalTypeData(IRGenFunction &IGF,
344344

345345
llvm::Value *data = value.getMetadata();
346346

347-
// At -O0, create an alloca to keep the type alive.
348-
if (!IGF.IGM.IRGen.Opts.shouldOptimize()) {
347+
// At -O0, create an alloca to keep the type alive. Not for async functions
348+
// though; see the comment in IRGenFunctionSIL::emitShadowCopyIfNeeded().
349+
if (!IGF.IGM.IRGen.Opts.shouldOptimize() && !IGF.isAsync()) {
349350
auto alloca =
350351
IGF.createAlloca(data->getType(), IGF.IGM.getPointerAlignment(), name);
351352
IGF.Builder.CreateStore(data, alloca);

test/DebugInfo/async-args.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %target-swift-frontend %s -emit-ir -g -o - \
2+
// RUN: -module-name M -enable-experimental-concurrency | %FileCheck %s
3+
4+
func use<T>(_ t: T) {}
5+
func forceSplit() async {
6+
}
7+
func withGenericArg<T>(_ msg: T) async {
8+
// This odd debug info is part of a contract with CoroSplit/CoroFrame to fix
9+
// this up after coroutine splitting.
10+
// CHECK-LABEL: {{^define .*}} @"$s1M14withGenericArgyyxYlF"(%swift.task* %0, %swift.executor* %1, %swift.context* %2)
11+
// CHECK: call void @llvm.dbg.declare(metadata %swift.context** %[[ALLOCA:[^,]*]],
12+
// CHECK-SAME: metadata ![[MSG:[0-9]+]], metadata !DIExpression(
13+
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_deref))
14+
// CHECK: call void @llvm.dbg.declare(metadata %swift.context** %[[ALLOCA]],
15+
// CHECK-SAME: metadata ![[TAU:[0-9]+]], metadata !DIExpression(
16+
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, {{[0-9]+}}))
17+
// CHECK: store %swift.context* %2, %swift.context** %[[ALLOCA]], align
18+
19+
await forceSplit()
20+
// CHECK-LABEL: {{^define .*}} @"$s1M14withGenericArgyyxYlF.resume.0"(i8* %0, i8* %1, i8* %2)
21+
// CHECK: store i8* %2, i8** %[[ALLOCA:.*]], align
22+
// CHECK: call void @llvm.dbg.declare(metadata i8** %[[ALLOCA]],
23+
// CHECK-SAME: metadata ![[TAU_R:[0-9]+]], metadata !DIExpression(
24+
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, [[OFFSET:[0-9]+]],
25+
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}))
26+
// CHECK: call void @llvm.dbg.declare(metadata i8** %[[ALLOCA]],
27+
// CHECK-SAME: metadata ![[MSG_R:[0-9]+]], metadata !DIExpression(
28+
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, [[OFFSET]],
29+
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_deref))
30+
31+
use(msg)
32+
}
33+
// CHECK-LABEL: {{^define }}
34+
runAsyncAndBlock {
35+
await withGenericArg("hello (asynchronously)")
36+
}
37+
// CHECK: ![[MSG]] = !DILocalVariable(name: "msg", arg: 1,
38+
// CHECK: ![[TAU]] = !DILocalVariable(name: "$\CF\84_0_0",
39+
// CHECK: ![[TAU_R]] = !DILocalVariable(name: "$\CF\84_0_0",
40+
// CHECK: ![[MSG_R]] = !DILocalVariable(name: "msg", arg: 1,
41+

0 commit comments

Comments
 (0)