Skip to content

Commit 3145dc1

Browse files
committed
Salvage debug info for function arguments in coro-split funclets.
This patch improves the availability for variables stored in the coroutine frame by emitting an alloca to hold the pointer to the frame object and rewriting dbg.declare intrinsics to point inside the frame object using salvaged DIExpressions. rdar://71866936
1 parent 4903d45 commit 3145dc1

File tree

5 files changed

+99
-11
lines changed

5 files changed

+99
-11
lines changed

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,7 @@ static Instruction *insertSpills(const SpillInfo &Spills, coro::Shape &Shape) {
804804
CurrentValue->getName() + Twine(".reload"));
805805
};
806806

807+
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
807808
Value *GEP = nullptr, *CurrentGEP = nullptr;
808809
for (auto const &E : Spills) {
809810
// If we have not seen the value, generate a spill.
@@ -896,12 +897,21 @@ static Instruction *insertSpills(const SpillInfo &Spills, coro::Shape &Shape) {
896897
if (CurrentGEP != GEP) {
897898
CurrentGEP = GEP;
898899
TinyPtrVector<DbgDeclareInst *> DIs = FindDbgDeclareUses(CurrentValue);
899-
if (!DIs.empty())
900-
DIBuilder(*CurrentBlock->getParent()->getParent(),
901-
/*AllowUnresolved*/ false)
902-
.insertDeclare(CurrentGEP, DIs.front()->getVariable(),
903-
DIs.front()->getExpression(),
904-
DIs.front()->getDebugLoc(), DIs.front());
900+
if (!DIs.empty()) {
901+
auto *DDI = DIs.front();
902+
bool AllowUnresolved = false;
903+
// This dbg.declare is preserved for all coro-split function
904+
// fragments. It will be unreachable in the main function, and
905+
// processed by coro::salvageDebugInfo() by CoroCloner.
906+
DIBuilder(*CurrentBlock->getParent()->getParent(), AllowUnresolved)
907+
.insertDeclare(CurrentGEP, DDI->getVariable(),
908+
DDI->getExpression(),
909+
DDI->getDebugLoc(),
910+
&*Builder.GetInsertPoint());
911+
// This dbg.declare is for the main function entry point. It
912+
// will be deleted in all coro-split functions.
913+
coro::salvageDebugInfo(DbgPtrAllocaCache, DDI);
914+
}
905915
}
906916

907917
// Replace all uses of CurrentValue in the current instruction with reload.
@@ -1633,6 +1643,55 @@ static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape,
16331643
}
16341644
}
16351645

1646+
void coro::salvageDebugInfo(
1647+
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
1648+
DbgDeclareInst *DDI, bool LoadFromFramePtr) {
1649+
Function *F = DDI->getFunction();
1650+
IRBuilder<> Builder(F->getContext());
1651+
auto InsertPt = F->getEntryBlock().getFirstInsertionPt();
1652+
while (isa<IntrinsicInst>(InsertPt))
1653+
++InsertPt;
1654+
Builder.SetInsertPoint(&F->getEntryBlock(), InsertPt);
1655+
DIExpression *Expr = DDI->getExpression();
1656+
// Follow the pointer arithmetic all the way to the incoming
1657+
// function argument and convert into a DIExpression.
1658+
Value *Storage = DDI->getAddress();
1659+
while (Storage) {
1660+
if (auto *LdInst = dyn_cast<LoadInst>(Storage)) {
1661+
Storage = LdInst->getOperand(0);
1662+
} else if (auto *GEPInst = dyn_cast<GetElementPtrInst>(Storage)) {
1663+
Expr = llvm::salvageDebugInfoImpl(*GEPInst, Expr,
1664+
/*WithStackValue=*/false);
1665+
Storage = GEPInst->getOperand(0);
1666+
} else if (auto *BCInst = dyn_cast<llvm::BitCastInst>(Storage))
1667+
Storage = BCInst->getOperand(0);
1668+
else
1669+
break;
1670+
}
1671+
// Store a pointer to the coroutine frame object in an alloca so it
1672+
// is available throughout the function when producing unoptimized
1673+
// code. Extending the lifetime this way is correct because the
1674+
// variable has been declared by a dbg.declare intrinsic.
1675+
if (auto Arg = dyn_cast_or_null<llvm::Argument>(Storage)) {
1676+
auto &Cached = DbgPtrAllocaCache[Storage];
1677+
if (!Cached) {
1678+
Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
1679+
Arg->getName() + ".debug");
1680+
Builder.CreateStore(Storage, Cached);
1681+
}
1682+
Storage = Cached;
1683+
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
1684+
}
1685+
// The FramePtr object adds one extra layer of indirection that
1686+
// needs to be unwrapped.
1687+
if (LoadFromFramePtr)
1688+
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
1689+
auto &VMContext = DDI->getFunction()->getContext();
1690+
DDI->setOperand(
1691+
0, MetadataAsValue::get(VMContext, ValueAsMetadata::get(Storage)));
1692+
DDI->setOperand(2, MetadataAsValue::get(VMContext, Expr));
1693+
}
1694+
16361695
void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
16371696
eliminateSwiftError(F, Shape);
16381697

llvm/lib/Transforms/Coroutines/CoroInternal.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ void replaceAllCoroFrees(CoroBeginInst *CB, Value *Replacement);
5252
void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
5353
void updateCallGraph(Function &Caller, ArrayRef<Function *> Funcs,
5454
CallGraph &CG, CallGraphSCC &SCC);
55+
/// Recover a dbg.declare prepared by the frontend and emit an alloca
56+
/// holding a pointer to the coroutine frame.
57+
void salvageDebugInfo(
58+
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
59+
DbgDeclareInst *DDI, bool LoadFromCoroFrame = false);
5560

5661
// Keeps data and helper functions for lowering coroutine intrinsics.
5762
struct LowererBase {

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ class CoroCloner {
159159
void replaceCoroSuspends();
160160
void replaceCoroEnds();
161161
void replaceSwiftErrorOps();
162+
void salvageDebugInfo();
162163
void handleFinalSuspend();
163164
void maybeFreeContinuationStorage();
164165
};
@@ -584,6 +585,24 @@ void CoroCloner::replaceSwiftErrorOps() {
584585
::replaceSwiftErrorOps(*NewF, Shape, &VMap);
585586
}
586587

588+
void CoroCloner::salvageDebugInfo() {
589+
SmallVector<DbgDeclareInst *, 8> Worklist;
590+
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
591+
for (auto &BB : *NewF)
592+
for (auto &I : BB)
593+
if (auto *DDI = dyn_cast<DbgDeclareInst>(&I))
594+
Worklist.push_back(DDI);
595+
for (DbgDeclareInst *DDI : Worklist) {
596+
// Remove the allocas inserted by CoroFrame for the sake of the
597+
// cloned function.
598+
if (isa<AllocaInst>(DDI->getAddress()))
599+
DDI->eraseFromParent();
600+
else
601+
coro::salvageDebugInfo(DbgPtrAllocaCache, DDI,
602+
/*LoadFromFramePointer*/ true);
603+
}
604+
}
605+
587606
void CoroCloner::replaceEntryBlock() {
588607
// In the original function, the AllocaSpillBlock is a block immediately
589608
// following the allocation of the frame object which defines GEPs for
@@ -853,6 +872,9 @@ void CoroCloner::create() {
853872
// Remove coro.end intrinsics.
854873
replaceCoroEnds();
855874

875+
// Salvage debug info that points into the coroutine frame.
876+
salvageDebugInfo();
877+
856878
// Eliminate coro.free from the clones, replacing it with 'null' in cleanup,
857879
// to suppress deallocation code.
858880
if (Shape.ABI == coro::ABI::Switch)

llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@
3333
; CHECK: call void @llvm.dbg.declare(metadata i32* [[JGEP]], metadata ![[JVAR:[0-9]+]], metadata !DIExpression()), !dbg ![[JDBGLOC:[0-9]+]]
3434
;
3535
; CHECK-LABEL: define internal fastcc void @f.resume({{.*}}) {
36+
; CHECK: %[[DBG_PTR:.*]] = alloca %f.Frame*
37+
; CHECK: store %f.Frame* {{.*}}, %f.Frame** %[[DBG_PTR]]
3638
; CHECK: init.ready:
37-
; CHECK: [[IGEP_RESUME:%.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4
38-
; CHECK: call void @llvm.dbg.declare(metadata i32* [[IGEP_RESUME]], metadata ![[IVAR_RESUME:[0-9]+]], metadata !DIExpression()), !dbg ![[IDBGLOC_RESUME:[0-9]+]]
39+
; CHECK: call void @llvm.dbg.declare(metadata %f.Frame** %[[DBG_PTR]], metadata ![[IVAR_RESUME:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_deref, DW_OP_plus_uconst, 20)), !dbg ![[IDBGLOC_RESUME:[0-9]+]]
3940
; CHECK: await.ready:
40-
; CHECK: [[JGEP_RESUME:%.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5
41-
; CHECK: call void @llvm.dbg.declare(metadata i32* [[JGEP_RESUME]], metadata ![[JVAR_RESUME:[0-9]+]], metadata !DIExpression()), !dbg ![[JDBGLOC_RESUME:[0-9]+]]
41+
; CHECK: call void @llvm.dbg.declare(metadata %f.Frame** %[[DBG_PTR]], metadata ![[JVAR_RESUME:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_deref, DW_OP_plus_uconst, 24)), !dbg ![[JDBGLOC_RESUME:[0-9]+]]
4242
;
4343
; CHECK: ![[IVAR]] = !DILocalVariable(name: "i"
4444
; CHECK: ![[SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12)

llvm/test/Transforms/Coroutines/coro-debug.ll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,10 @@ attributes #7 = { noduplicate }
130130
; CHECK: define i8* @f(i32 %x) #0 !dbg ![[ORIG:[0-9]+]]
131131
; CHECK: define internal fastcc void @f.resume(%f.Frame* noalias nonnull align 8 dereferenceable(32) %FramePtr) #0 !dbg ![[RESUME:[0-9]+]]
132132
; CHECK: entry.resume:
133+
; CHECK-NEXT: %[[DBG_PTR:.*]] = alloca %f.Frame*
134+
; CHECK-NEXT: store %f.Frame* {{.*}}, %f.Frame** %[[DBG_PTR]]
133135
; CHECK-NEXT: call void @coro.devirt.trigger(i8* null)
134-
; CHECK-NEXT: call void @llvm.dbg.declare(metadata i32* %x.addr.reload.addr, metadata ![[RESUME_VAR:[0-9]+]]
136+
; CHECK: call void @llvm.dbg.declare(metadata %f.Frame** %[[DBG_PTR]], metadata ![[RESUME_VAR:[0-9]+]], metadata !DIExpression(DW_OP_deref, DW_OP_deref, DW_OP_plus_uconst,
135137
; CHECK: define internal fastcc void @f.destroy(%f.Frame* noalias nonnull align 8 dereferenceable(32) %FramePtr) #0 !dbg ![[DESTROY:[0-9]+]]
136138
; CHECK: define internal fastcc void @f.cleanup(%f.Frame* noalias nonnull align 8 dereferenceable(32) %FramePtr) #0 !dbg ![[CLEANUP:[0-9]+]]
137139

0 commit comments

Comments
 (0)