Skip to content

Commit 5b4e056

Browse files
committed
[IRGen] Fix debug info for direct arguments/return values
Emit shadow copies in async functions and emit a load to get the value.
1 parent 3c125a7 commit 5b4e056

File tree

6 files changed

+78
-128
lines changed

6 files changed

+78
-128
lines changed

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,6 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
199199
llvm::DILocalVariable *Var, llvm::DIExpression *Expr,
200200
unsigned Line, unsigned Col, llvm::DILocalScope *Scope,
201201
const SILDebugScope *DS, bool InCoroContext = false);
202-
#ifndef NDEBUG
203-
bool verifyCoroutineArgument(llvm::Value *Addr);
204-
#endif
205202

206203
void emitGlobalVariableDeclaration(llvm::GlobalVariable *Storage,
207204
StringRef Name, StringRef LinkageName,
@@ -2476,27 +2473,6 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic(
24762473
}
24772474
}
24782475

2479-
#ifndef NDEBUG
2480-
bool IRGenDebugInfoImpl::verifyCoroutineArgument(llvm::Value *Addr) {
2481-
llvm::Value *Storage = Addr;
2482-
while (Storage) {
2483-
if (auto *LdInst = dyn_cast<llvm::LoadInst>(Storage))
2484-
Storage = LdInst->getOperand(0);
2485-
else if (auto *GEPInst = dyn_cast<llvm::GetElementPtrInst>(Storage))
2486-
Storage = GEPInst->getOperand(0);
2487-
else if (auto *BCInst = dyn_cast<llvm::BitCastInst>(Storage))
2488-
Storage = BCInst->getOperand(0);
2489-
else if (auto *CallInst = dyn_cast<llvm::CallInst>(Storage)) {
2490-
assert(CallInst->getCalledFunction() == IGM.getProjectBoxFn() &&
2491-
"unhandled projection");
2492-
Storage = CallInst->getArgOperand(0);
2493-
} else
2494-
break;
2495-
}
2496-
return llvm::isa<llvm::Argument>(Storage);
2497-
}
2498-
#endif
2499-
25002476
void IRGenDebugInfoImpl::emitGlobalVariableDeclaration(
25012477
llvm::GlobalVariable *Var, StringRef Name, StringRef LinkageName,
25022478
DebugTypeInfo DbgTy, bool IsLocalToUnit, bool InFixedBuffer,
@@ -2680,12 +2656,6 @@ void IRGenDebugInfo::emitDbgIntrinsic(IRBuilder &Builder, llvm::Value *Storage,
26802656
Builder, Storage, Var, Expr, Line, Col, Scope, DS, InCoroContext);
26812657
}
26822658

2683-
#ifndef NDEBUG
2684-
bool IRGenDebugInfo::verifyCoroutineArgument(llvm::Value *Addr) {
2685-
return static_cast<IRGenDebugInfoImpl *>(this)->verifyCoroutineArgument(Addr);
2686-
}
2687-
#endif
2688-
26892659
void IRGenDebugInfo::emitGlobalVariableDeclaration(
26902660
llvm::GlobalVariable *Storage, StringRef Name, StringRef LinkageName,
26912661
DebugTypeInfo DebugType, bool IsLocalToUnit, bool InFixedBuffer,

lib/IRGen/IRGenDebugInfo.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,6 @@ class IRGenDebugInfo {
150150
llvm::DILocalVariable *Var, llvm::DIExpression *Expr,
151151
unsigned Line, unsigned Col, llvm::DILocalScope *Scope,
152152
const SILDebugScope *DS, bool InCoroContext = false);
153-
#ifndef NDEBUG
154-
/// Verify that Addr can be processed by llvm's CoroFrame/CoroSplit.
155-
bool verifyCoroutineArgument(llvm::Value *Addr);
156-
#endif
157153

158154
enum { NotHeapAllocated = false };
159155

lib/IRGen/IRGenSIL.cpp

Lines changed: 69 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,9 @@ class IRGenSILFunction :
402402
llvm::SmallDenseMap<llvm::Value *, Address, 8> TaskAllocStackSlots;
403403
llvm::SmallDenseMap<Decl *, SmallString<4>, 8> AnonymousVariables;
404404
/// To avoid inserting elements into ValueDomPoints twice.
405-
llvm::SmallDenseSet<llvm::Instruction *, 8> ValueVariables;
405+
llvm::SmallDenseSet<llvm::Value *, 8> ValueVariables;
406406
/// Holds the DominancePoint of values that are storage for a source variable.
407-
SmallVector<std::pair<llvm::Instruction *, DominancePoint>, 8> ValueDomPoints;
407+
SmallVector<std::pair<llvm::Value *, DominancePoint>, 8> ValueDomPoints;
408408
unsigned NumAnonVars = 0;
409409

410410
/// Accumulative amount of allocated bytes on the stack. Used to limit the
@@ -742,7 +742,7 @@ class IRGenSILFunction :
742742
if (IGM.IRGen.Opts.shouldOptimize())
743743
return;
744744
for (auto &Variable : ValueDomPoints) {
745-
llvm::Instruction *Var = Variable.first;
745+
llvm::Value *Var = Variable.first;
746746
DominancePoint VarDominancePoint = Variable.second;
747747
if (getActiveDominancePoint() == VarDominancePoint ||
748748
isActiveDominancePointDominatedBy(VarDominancePoint)) {
@@ -754,7 +754,10 @@ class IRGenSILFunction :
754754
// that this shouldn't be necessary. LiveDebugValues should be doing
755755
// this but can't in general because it currently only tracks register
756756
// locations.
757-
llvm::BasicBlock *BB = Var->getParent();
757+
llvm::BasicBlock *BB =
758+
isa<llvm::Instruction>(Var)
759+
? cast<llvm::Instruction>(Var)->getParent()
760+
: &cast<llvm::Argument>(Var)->getParent()->getEntryBlock();
758761
llvm::BasicBlock *CurBB = Builder.GetInsertBlock();
759762
if (BB == CurBB)
760763
// The current basic block must be a successor of the dbg.value().
@@ -841,56 +844,6 @@ class IRGenSILFunction :
841844
return false;
842845
}
843846

844-
/// Emit a direct path to an Argument.
845-
llvm::Value *getDirectCoroutineArgument(llvm::Value *Addr) {
846-
auto getDirect = [&](llvm::Instruction *Orig) {
847-
llvm::Value *Buffered = Orig->getOperand(0);
848-
llvm::Value *Direct = getDirectCoroutineArgument(Buffered);
849-
if (Buffered == Direct)
850-
return Orig;
851-
llvm::Instruction *Cloned = Orig->clone();
852-
Cloned->setOperand(0, Direct);
853-
Cloned->insertBefore(Orig);
854-
return Cloned;
855-
};
856-
if (auto *LdInst = dyn_cast<llvm::LoadInst>(Addr))
857-
return getDirect(LdInst);
858-
if (auto *GEPInst = dyn_cast<llvm::GetElementPtrInst>(Addr))
859-
return getDirect(GEPInst);
860-
if (auto *BCInst = dyn_cast<llvm::BitCastInst>(Addr))
861-
return getDirect(BCInst);
862-
if (auto *CallInst = dyn_cast<llvm::CallInst>(Addr)) {
863-
llvm::Value *Buffered = CallInst->getArgOperand(0);
864-
if (CallInst->getCalledFunction() != IGM.getProjectBoxFn()) {
865-
assert(false && "unhandled projection");
866-
return CallInst;
867-
}
868-
llvm::Value *Direct = getDirectCoroutineArgument(Buffered);
869-
if (Buffered == Direct)
870-
return CallInst;
871-
auto *Cloned = cast<llvm::CallInst>(CallInst->clone());
872-
Cloned->setArgOperand(0, Direct);
873-
Cloned->insertBefore(CallInst);
874-
return Cloned;
875-
}
876-
if (auto *AllocaInst = dyn_cast<llvm::AllocaInst>(Addr)) {
877-
llvm::Value *Direct = nullptr;
878-
unsigned NumStores = 0;
879-
for (auto &AIUse : AllocaInst->uses()) {
880-
llvm::User *U = AIUse.getUser();
881-
if (llvm::StoreInst *StInst = llvm::dyn_cast<llvm::StoreInst>(U)) {
882-
++NumStores;
883-
Direct = StInst->getOperand(0);
884-
}
885-
}
886-
if (NumStores == 1)
887-
return Direct;
888-
}
889-
return Addr;
890-
}
891-
892-
// This returns shadow alloca when \p init is false or the shadowed value
893-
// derived from that alloca with \p init is true.
894847
llvm::Value *emitTaskAllocShadowCopy(llvm::Value *Storage,
895848
const SILDebugScope *Scope,
896849
bool Init) {
@@ -933,10 +886,6 @@ class IRGenSILFunction :
933886
llvm::Value *emitShadowCopy(llvm::Value *Storage, const SILDebugScope *Scope,
934887
SILDebugVariable VarInfo,
935888
llvm::Optional<Alignment> _Align, bool Init) {
936-
if (CurSILFn->isAsync())
937-
if (isTaskAlloc(Storage))
938-
return emitTaskAllocShadowCopy(Storage, Scope, Init);
939-
940889
auto Align = _Align.getValueOr(IGM.getPointerAlignment());
941890
unsigned ArgNo = VarInfo.ArgNo;
942891
auto &Alloca = ShadowStackSlots[{ArgNo, {Scope, VarInfo.Name}}];
@@ -955,8 +904,7 @@ class IRGenSILFunction :
955904
bool shouldShadowVariable(SILDebugVariable varInfo, bool isAnonymous) {
956905
return !IGM.IRGen.Opts.DisableDebuggerShadowCopies
957906
&& !IGM.IRGen.Opts.shouldOptimize()
958-
&& !isAnonymous
959-
&& !CurSILFn->isAsync();
907+
&& !isAnonymous;
960908
}
961909

962910
bool shouldShadowStorage(llvm::Value *Storage) {
@@ -975,26 +923,40 @@ class IRGenSILFunction :
975923
bool IsAnonymous,
976924
llvm::Optional<Alignment> Align = None) {
977925
// Never emit shadow copies when optimizing, or if already on the stack. No
978-
// debug info is emitted for refcounts either. Shadow copies are also
979-
// turned off for async functions, because they make it impossible to track
980-
// debug info during coroutine splitting. Instead we are relying on LLVM's
981-
// CoroSplit.cpp to emit shadow copies.
926+
// debug info is emitted for refcounts either
982927

983-
// Mark variables in async functions for lifetime extension, so they get
984-
// spilled into the async context.
928+
// Mark variables in async functions that don't generate a shadow copy for
929+
// lifetime extension, so they get spilled into the async context.
985930
if (!IGM.IRGen.Opts.shouldOptimize() && CurSILFn->isAsync())
986-
if (auto *Value = dyn_cast<llvm::Instruction>(Storage))
987-
if (emitLifetimeExtendingUse(Value))
988-
if (ValueVariables.insert(Value).second)
989-
ValueDomPoints.push_back({Value, getActiveDominancePoint()});
931+
if (isa<llvm::AllocaInst>(Storage) || isa<llvm::Constant>(Storage)) {
932+
if (emitLifetimeExtendingUse(Storage))
933+
if (ValueVariables.insert(Storage).second)
934+
ValueDomPoints.push_back({Storage, getActiveDominancePoint()});
935+
}
936+
990937
// This condition must be consistent with emitPoisonDebugValueInst to avoid
991938
// generating extra shadow copies for debug_value [poison].
992939
if (!shouldShadowVariable(VarInfo, IsAnonymous)
993940
|| !shouldShadowStorage(Storage)) {
994941
return Storage;
995942
}
943+
996944
// Emit a shadow copy.
997-
return emitShadowCopy(Storage, Scope, VarInfo, Align, true);
945+
auto shadow = emitShadowCopy(Storage, Scope, VarInfo, Align, true);
946+
947+
// Mark variables in async functions for lifetime extension, so they get
948+
// spilled into the async context.
949+
if (!IGM.IRGen.Opts.shouldOptimize() && CurSILFn->isAsync()) {
950+
if (emitLifetimeExtendingUse(shadow)) {
951+
if (ValueVariables.insert(shadow).second)
952+
ValueDomPoints.push_back({shadow, getActiveDominancePoint()});
953+
}
954+
auto inst = cast<llvm::Instruction>(shadow);
955+
llvm::IRBuilder<> builder(inst->getNextNode());
956+
shadow = builder.CreateLoad(shadow);
957+
}
958+
959+
return shadow;
998960
}
999961

1000962
/// Like \c emitShadowCopyIfNeeded() but takes an \c Address instead of an
@@ -1021,11 +983,13 @@ class IRGenSILFunction :
1021983
// Mark variables in async functions for lifetime extension, so they get
1022984
// spilled into the async context.
1023985
if (!IGM.IRGen.Opts.shouldOptimize() && CurSILFn->isAsync())
1024-
if (vals.begin() != vals.end())
1025-
if (auto *Value = dyn_cast<llvm::Instruction>(vals.front()))
986+
if (vals.begin() != vals.end()) {
987+
auto Value = vals.front();
988+
if (isa<llvm::Instruction>(Value) || isa<llvm::Argument>(Value))
1026989
if (emitLifetimeExtendingUse(Value))
1027990
if (ValueVariables.insert(Value).second)
1028991
ValueDomPoints.push_back({Value, getActiveDominancePoint()});
992+
}
1029993
return;
1030994
}
1031995

@@ -1043,6 +1007,15 @@ class IRGenSILFunction :
10431007
auto &Alloca = ShadowStackSlots[{ArgNo, {Scope, VarInfo.Name}}];
10441008
if (Alloca.isValid()) {
10451009
(void)e.claimAll();
1010+
// Async functions use the value of the artificial address.
1011+
if (CurSILFn->isAsync()) {
1012+
auto shadow = Alloca.getAddress();
1013+
auto inst = cast<llvm::Instruction>(shadow);
1014+
llvm::IRBuilder<> builder(inst->getNextNode());
1015+
shadow = builder.CreateLoad(shadow);
1016+
copy.push_back(shadow);
1017+
return;
1018+
}
10461019
} else {
10471020
SILType Type = SILVal->getType();
10481021
auto &LTI = cast<LoadableTypeInfo>(IGM.getTypeInfo(Type));
@@ -1051,6 +1024,18 @@ class IRGenSILFunction :
10511024
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
10521025
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
10531026
LTI.initialize(*this, e, Alloca, false /* isOutlined */);
1027+
auto shadow = Alloca.getAddress();
1028+
// Async functions use the value of the artificial address.
1029+
if (CurSILFn->isAsync() && emitLifetimeExtendingUse(shadow)) {
1030+
if (ValueVariables.insert(shadow).second)
1031+
ValueDomPoints.push_back({shadow, getActiveDominancePoint()});
1032+
auto inst = cast<llvm::Instruction>(shadow);
1033+
llvm::IRBuilder<> builder(inst->getNextNode());
1034+
shadow = builder.CreateLoad(shadow);
1035+
copy.push_back(shadow);
1036+
return;
1037+
}
1038+
10541039
}
10551040
copy.push_back(Alloca.getAddress());
10561041
}
@@ -4676,7 +4661,7 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) {
46764661
}
46774662
if (i->getDebugScope()->getInlinedFunction()->isTransparent())
46784663
return;
4679-
4664+
46804665
auto VarInfo = i->getVarInfo();
46814666
assert(VarInfo && "debug_value without debug info");
46824667
auto SILVal = i->getOperand();
@@ -4716,12 +4701,6 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) {
47164701
IndirectionKind Indirection = DirectValue;
47174702
if (CurSILFn->isAsync() &&
47184703
!i->getDebugScope()->InlinedCallSite) {
4719-
if (VarInfo->ArgNo)
4720-
for (auto &Val : Copy) {
4721-
Val = getDirectCoroutineArgument(Val);
4722-
assert(IGM.DebugInfo->verifyCoroutineArgument(Val) &&
4723-
"arg expected to be load from inside %swift.context");
4724-
}
47254704
Indirection = CoroDirectValue;
47264705
}
47274706

@@ -4752,9 +4731,6 @@ void IRGenSILFunction::visitDebugValueAddrInst(DebugValueAddrInst *i) {
47524731
SILType SILTy = SILVal->getType();
47534732
auto RealType = SILTy.getASTType();
47544733
if (CurSILFn->isAsync() && !i->getDebugScope()->InlinedCallSite) {
4755-
if (IGM.DebugInfo && VarInfo->ArgNo)
4756-
assert(IGM.DebugInfo->verifyCoroutineArgument(Addr) &&
4757-
"arg expected to be load from inside %swift.context");
47584734
Indirection = CoroIndirectValue;
47594735
if (auto *PBI = dyn_cast<ProjectBoxInst>(i->getOperand())) {
47604736
// Usually debug info only ever describes the *result* of a projectBox
@@ -5109,6 +5085,16 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i,
51095085
SILType SILTy = i->getType();
51105086
auto RealType = SILTy.getASTType();
51115087
auto DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type);
5088+
// Async functions use the value of the artificial address.
5089+
auto shadow = addr;
5090+
if (CurSILFn->isAsync() && emitLifetimeExtendingUse(shadow) &&
5091+
!isa<llvm::UndefValue>(shadow)) {
5092+
if (ValueVariables.insert(shadow).second)
5093+
ValueDomPoints.push_back({shadow, getActiveDominancePoint()});
5094+
auto inst = cast<llvm::Instruction>(shadow);
5095+
llvm::IRBuilder<> builder(inst->getNextNode());
5096+
addr = builder.CreateLoad(shadow);
5097+
}
51125098

51135099
bindArchetypes(DbgTy.getType());
51145100
if (IGM.DebugInfo)

test/DebugInfo/async-args.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func forceSplit() async {
1010
func withGenericArg<T>(_ msg: T) async {
1111
// This odd debug info is part of a contract with CoroSplit/CoroFrame to fix
1212
// this up after coroutine splitting.
13-
// CHECK-LABEL: {{^define .*}} @"$s1M14withGenericArgyyxYlF"(%swift.context* swiftasync %0)
13+
// CHECK-LABEL: {{^define .*}} @"$s1M14withGenericArgyyxYlF"(%swift.context* swiftasync %0
1414
// CHECK: call void @llvm.dbg.declare(metadata %swift.context* %0,
1515
// CHECK-SAME: metadata ![[MSG:[0-9]+]], metadata !DIExpression(
1616
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_deref))
@@ -21,14 +21,13 @@ func withGenericArg<T>(_ msg: T) async {
2121
await forceSplit()
2222
// CHECK-LABEL: {{^define .*}} @"$s1M14withGenericArgyyxYlF.resume.0"(i8* swiftasync %0)
2323
// CHECK: call void @llvm.dbg.declare(metadata i8* %0,
24-
// CHECK-SAME: metadata ![[TAU_R:[0-9]+]], metadata !DIExpression(
24+
// CHECK-SAME: metadata ![[MSG_R:[0-9]+]], metadata !DIExpression(DW_OP_deref,
2525
// CHECK-SAME: DW_OP_plus_uconst, [[OFFSET:[0-9]+]],
26-
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}))
26+
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_deref))
2727
// CHECK: call void @llvm.dbg.declare(metadata i8* %0,
28-
// CHECK-SAME: metadata ![[MSG_R:[0-9]+]], metadata !DIExpression(
28+
// CHECK-SAME: metadata ![[TAU_R:[0-9]+]], metadata !DIExpression(DW_OP_deref,
2929
// CHECK-SAME: DW_OP_plus_uconst, [[OFFSET]],
30-
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_deref))
31-
30+
// CHECK-SAME: DW_OP_plus_uconst, {{[0-9]+}}))
3231
use(msg)
3332
}
3433
// CHECK-LABEL: {{^define }}
@@ -39,6 +38,5 @@ func withGenericArg<T>(_ msg: T) async {
3938
}
4039
// CHECK: ![[MSG]] = !DILocalVariable(name: "msg", arg: 1,
4140
// CHECK: ![[TAU]] = !DILocalVariable(name: "$\CF\84_0_0",
42-
// CHECK: ![[TAU_R]] = !DILocalVariable(name: "$\CF\84_0_0",
4341
// CHECK: ![[MSG_R]] = !DILocalVariable(name: "msg", arg: 1,
44-
42+
// CHECK: ![[TAU_R]] = !DILocalVariable(name: "$\CF\84_0_0",

test/DebugInfo/async-let-await.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-frontend %s -emit-ir -g -o - \
22
// RUN: -module-name M -enable-experimental-concurrency \
3-
// RUN: -parse-as-library | %FileCheck %s
3+
// RUN: -parse-as-library | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
44
// REQUIRES: concurrency
55
// UNSUPPORTED: CPU=arm64e
66

test/DebugInfo/async-lifetime-extension.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
// CHECK-LABEL: define {{.*}} void @"$s1a4fiboyS2iYF.resume.0"
1212
// CHECK-NEXT: entryresume.0:
1313
// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[RHS:[0-9]+]], {{.*}}!DIExpression(DW_OP
14-
// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[R:[0-9]+]], {{.*}}!DIExpression(DW_OP
1514
// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[LHS:[0-9]+]], {{.*}}!DIExpression(DW_OP
1615
// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[N:[0-9]+]], {{.*}}!DIExpression(DW_OP
16+
// CHECK-NEXT: call void @llvm.dbg.declare(metadata {{.*}}%0, metadata ![[R:[0-9]+]], {{.*}}!DIExpression(DW_OP
1717
// CHECK-NOT: {{ ret }}
1818
// CHECK: call void asm sideeffect ""
1919
// CHECK: ![[RHS]] = !DILocalVariable(name: "rhs"
20-
// CHECK: ![[R]] = !DILocalVariable(name: "retval"
2120
// CHECK: ![[LHS]] = !DILocalVariable(name: "lhs"
2221
// CHECK: ![[N]] = !DILocalVariable(name: "n"
22+
// CHECK: ![[R]] = !DILocalVariable(name: "retval"
2323
public func fibo(_ n: Int) async -> Int {
2424
var retval = n
2525
if retval < 2 { return 1 }

0 commit comments

Comments
 (0)