Skip to content

Add debug info support for function arguments in async functions. #35028

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
void emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage,
llvm::DILocalVariable *Var, llvm::DIExpression *Expr,
unsigned Line, unsigned Col, llvm::DILocalScope *Scope,
const SILDebugScope *DS);
const SILDebugScope *DS, bool InCoroContext = false);
void emitGlobalVariableDeclaration(llvm::GlobalVariable *Storage,
StringRef Name, StringRef LinkageName,
DebugTypeInfo DebugType,
Expand Down Expand Up @@ -2367,7 +2367,7 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(

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

if (IsPiece) {
Expand All @@ -2389,7 +2389,9 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
Operands.push_back(SizeInBits);
}
emitDbgIntrinsic(Builder, Piece, Var, DBuilder.createExpression(Operands),
Line, Loc.Column, Scope, DS);
Line, Loc.Column, Scope, DS,
Indirection == CoroDirectValue ||
Indirection == CoroIndirectValue);
}

// Emit locationless intrinsic for variables that were optimized away.
Expand All @@ -2401,7 +2403,7 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
void IRGenDebugInfoImpl::emitDbgIntrinsic(
IRBuilder &Builder, llvm::Value *Storage, llvm::DILocalVariable *Var,
llvm::DIExpression *Expr, unsigned Line, unsigned Col,
llvm::DILocalScope *Scope, const SILDebugScope *DS) {
llvm::DILocalScope *Scope, const SILDebugScope *DS, bool InCoroContext) {
// Set the location/scope of the intrinsic.
auto *InlinedAt = createInlinedAt(DS);
auto DL = llvm::DebugLoc::get(Line, Col, Scope, InlinedAt);
Expand All @@ -2420,13 +2422,23 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic(
DBuilder.insertDeclare(Alloca, Var, Expr, DL, &*InsertBefore);
else
DBuilder.insertDeclare(Alloca, Var, Expr, DL, ParentBB);
} else if (isa<llvm::IntrinsicInst>(Storage) &&
cast<llvm::IntrinsicInst>(Storage)->getIntrinsicID() ==
llvm::Intrinsic::coro_alloca_get) {
} else if ((isa<llvm::IntrinsicInst>(Storage) &&
cast<llvm::IntrinsicInst>(Storage)->getIntrinsicID() ==
llvm::Intrinsic::coro_alloca_get)) {
// FIXME: The live range of a coroutine alloca within the function may be
// limited, so using a dbg.addr instead of a dbg.declare would be more
// appropriate.
DBuilder.insertDeclare(Storage, Var, Expr, DL, BB);
} else if (InCoroContext && (Var->getArg() || Var->isArtificial())) {
// Function arguments in async functions are emitted without a shadow copy
// (that would interfer with coroutine splitting) but with a dbg.declare to
// give CoroSplit.cpp license to emit a shadow copy for them pointing inside
// the Swift Context argument that is valid throughout the function.
auto &EntryBlock = BB->getParent()->getEntryBlock();
if (auto *InsertBefore = &*EntryBlock.getFirstInsertionPt())
DBuilder.insertDeclare(Storage, Var, Expr, DL, InsertBefore);
else
DBuilder.insertDeclare(Storage, Var, Expr, DL, &EntryBlock);
} else {
// Insert a dbg.value at the current insertion point.
DBuilder.insertDbgValueIntrinsic(Storage, Var, Expr, DL, BB);
Expand Down Expand Up @@ -2506,7 +2518,8 @@ void IRGenDebugInfoImpl::emitTypeMetadata(IRGenFunction &IGF,
// swift.type is already a pointer type,
// having a shadow copy doesn't add another
// layer of indirection.
DirectValue, ArtificialValue);
IGF.isAsync() ? CoroDirectValue : DirectValue,
ArtificialValue);
}

SILLocation::DebugLoc IRGenDebugInfoImpl::decodeSourceLoc(SourceLoc SL) {
Expand Down Expand Up @@ -2609,9 +2622,10 @@ void IRGenDebugInfo::emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage,
llvm::DILocalVariable *Var,
llvm::DIExpression *Expr, unsigned Line,
unsigned Col, llvm::DILocalScope *Scope,
const SILDebugScope *DS) {
const SILDebugScope *DS,
bool InCoroContext) {
static_cast<IRGenDebugInfoImpl *>(this)->emitDbgIntrinsic(
Builder, Storage, Var, Expr, Line, Col, Scope, DS);
Builder, Storage, Var, Expr, Line, Col, Scope, DS, InCoroContext);
}

void IRGenDebugInfo::emitGlobalVariableDeclaration(
Expand Down
16 changes: 11 additions & 5 deletions lib/IRGen/IRGenDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ class IRBuilder;
class IRGenFunction;
class IRGenModule;

enum IndirectionKind : bool { DirectValue = false, IndirectValue = true };
enum IndirectionKind {
DirectValue,
IndirectValue,
CoroDirectValue,
CoroIndirectValue
};
enum ArtificialKind : bool { RealValue = false, ArtificialValue = true };

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

/// Replace the current debug location in \p Builder with the same location, but contained in an
/// inlined function which is named like \p failureMsg.
/// Replace the current debug location in \p Builder with the same location,
/// but contained in an inlined function which is named like \p failureMsg.
///
/// This lets the debugger display the \p failureMsg as an inlined function frame.
/// This lets the debugger display the \p failureMsg as an inlined function
/// frame.
void addFailureMessageToCurrentLoc(IRBuilder &Builder, StringRef failureMsg);

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

enum { NotHeapAllocated = false };

Expand Down
40 changes: 31 additions & 9 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,9 +726,9 @@ class IRGenSILFunction :

/// Unconditionally emit a stack shadow copy of an \c llvm::Value.
llvm::Value *emitShadowCopy(llvm::Value *Storage, const SILDebugScope *Scope,
SILDebugVariable VarInfo, llvm::Optional<Alignment> _Align) {
SILDebugVariable VarInfo,
llvm::Optional<Alignment> _Align) {
auto Align = _Align.getValueOr(IGM.getPointerAlignment());

unsigned ArgNo = VarInfo.ArgNo;
auto &Alloca = ShadowStackSlots[{ArgNo, {Scope, VarInfo.Name}}];
if (!Alloca.isValid())
Expand All @@ -750,10 +750,13 @@ class IRGenSILFunction :
SILDebugVariable VarInfo,
bool IsAnonymous,
llvm::Optional<Alignment> Align = None) {
// Never emit shadow copies when optimizing, or if already on the stack.
// No debug info is emitted for refcounts either.
// Never emit shadow copies when optimizing, or if already on the stack. No
// debug info is emitted for refcounts either. Shadow copies are also
// turned off for async functions, 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.
if (IGM.IRGen.Opts.DisableDebuggerShadowCopies ||
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous ||
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || CurSILFn->isAsync() ||
isa<llvm::AllocaInst>(Storage) || isa<llvm::UndefValue>(Storage) ||
!needsShadowCopy(Storage))
return Storage;
Expand All @@ -780,7 +783,7 @@ class IRGenSILFunction :

// Only do this at -O0.
if (IGM.IRGen.Opts.DisableDebuggerShadowCopies ||
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous) {
IGM.IRGen.Opts.shouldOptimize() || IsAnonymous || CurSILFn->isAsync()) {
auto vals = e.claimAll();
copy.append(vals.begin(), vals.end());
return;
Expand Down Expand Up @@ -4334,8 +4337,28 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) {
assert(VarInfo && "debug_value_addr without debug info");
bool IsAnonymous = false;
bool IsLoadablyByAddress = isa<AllocStackInst>(SILVal);
IndirectionKind Indirection =
(IsLoadablyByAddress) ? DirectValue : IndirectValue;
VarInfo->Name = getVarName(i, IsAnonymous);
auto Addr = getLoweredAddress(SILVal).getAddress();
auto *Addr = getLoweredAddress(SILVal).getAddress();
if (CurSILFn->isAsync() && VarInfo->ArgNo) {
#ifndef NDEBUG
llvm::Value *Storage = Addr;
while (Storage) {
if (auto *LdInst = dyn_cast<llvm::LoadInst>(Storage))
Storage = LdInst->getOperand(0);
else if (auto *GEPInst = dyn_cast<llvm::GetElementPtrInst>(Storage))
Storage = GEPInst->getOperand(0);
else if (auto *BCInst = dyn_cast<llvm::BitCastInst>(Storage))
Storage = BCInst->getOperand(0);
else
break;
}
assert(llvm::isa<llvm::Argument>(Storage) &&
"arg expected to be load from inside %swift.context");
#endif
Indirection = CoroIndirectValue;
}
SILType SILTy = SILVal->getType();
auto RealType = SILTy.getASTType();

Expand All @@ -4349,8 +4372,7 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) {
// intrinsic.
emitDebugVariableDeclaration(
emitShadowCopyIfNeeded(Addr, i->getDebugScope(), *VarInfo, IsAnonymous),
DbgTy, SILType(), i->getDebugScope(), Decl, *VarInfo,
(IsLoadablyByAddress) ? DirectValue : IndirectValue);
DbgTy, SILType(), i->getDebugScope(), Decl, *VarInfo, Indirection);
}

void IRGenSILFunction::visitFixLifetimeInst(swift::FixLifetimeInst *i) {
Expand Down
5 changes: 3 additions & 2 deletions lib/IRGen/LocalTypeData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,9 @@ static void maybeEmitDebugInfoForLocalTypeData(IRGenFunction &IGF,

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

// At -O0, create an alloca to keep the type alive.
if (!IGF.IGM.IRGen.Opts.shouldOptimize()) {
// At -O0, create an alloca to keep the type alive. Not for async functions
// though; see the comment in IRGenFunctionSIL::emitShadowCopyIfNeeded().
if (!IGF.IGM.IRGen.Opts.shouldOptimize() && !IGF.isAsync()) {
auto alloca =
IGF.createAlloca(data->getType(), IGF.IGM.getPointerAlignment(), name);
IGF.Builder.CreateStore(data, alloca);
Expand Down
41 changes: 41 additions & 0 deletions test/DebugInfo/async-args.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// RUN: %target-swift-frontend %s -emit-ir -g -o - \
// RUN: -module-name M -enable-experimental-concurrency | %FileCheck %s

func use<T>(_ t: T) {}
func forceSplit() async {
}
func withGenericArg<T>(_ msg: T) async {
// This odd debug info is part of a contract with CoroSplit/CoroFrame to fix
// this up after coroutine splitting.
// CHECK-LABEL: {{^define .*}} @"$s1M14withGenericArgyyxYlF"(%swift.task* %0, %swift.executor* %1, %swift.context* %2)
// CHECK: call void @llvm.dbg.declare(metadata %swift.context** %[[ALLOCA:[^,]*]],
// CHECK-SAME: metadata ![[MSG:[0-9]+]], metadata !DIExpression(
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_deref))
// CHECK: call void @llvm.dbg.declare(metadata %swift.context** %[[ALLOCA]],
// CHECK-SAME: metadata ![[TAU:[0-9]+]], metadata !DIExpression(
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, {{[0-9]+}}))
// CHECK: store %swift.context* %2, %swift.context** %[[ALLOCA]], align

await forceSplit()
// CHECK-LABEL: {{^define .*}} @"$s1M14withGenericArgyyxYlF.resume.0"(i8* %0, i8* %1, i8* %2)
// CHECK: store i8* %2, i8** %[[ALLOCA:.*]], align
// CHECK: call void @llvm.dbg.declare(metadata i8** %[[ALLOCA]],
// CHECK-SAME: metadata ![[TAU_R:[0-9]+]], metadata !DIExpression(
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, [[OFFSET:[0-9]+]],
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}))
// CHECK: call void @llvm.dbg.declare(metadata i8** %[[ALLOCA]],
// CHECK-SAME: metadata ![[MSG_R:[0-9]+]], metadata !DIExpression(
// CHECK-SAME: DW_OP_deref, DW_OP_plus_uconst, [[OFFSET]],
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_deref))

use(msg)
}
// CHECK-LABEL: {{^define }}
runAsyncAndBlock {
await withGenericArg("hello (asynchronously)")
}
// CHECK: ![[MSG]] = !DILocalVariable(name: "msg", arg: 1,
// CHECK: ![[TAU]] = !DILocalVariable(name: "$\CF\84_0_0",
// CHECK: ![[TAU_R]] = !DILocalVariable(name: "$\CF\84_0_0",
// CHECK: ![[MSG_R]] = !DILocalVariable(name: "msg", arg: 1,