Skip to content

Commit ef3526f

Browse files
Merge pull request #2223 from adrian-prantl/71866936
Salvage debug info for function arguments in coro-split funclets.
2 parents e6329a0 + 3145dc1 commit ef3526f

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)