mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
[Coroutines] Salvege Debug.values
Summary: The previous implementation of coro-split didn't collect values used by dbg instructions into the spills which made a log debug info unavailable with optimization on. This patch tries to collect these uses which are used by dbg.values. In this way, the debugbility of coroutine could be as powerful as normal functions with optimization on. To avoid enlarging the coroutine frame, this patch only collects `dbg.value` whose value is already in the coroutine frame. This decision may make some debug info getting unavailable. But if we are with optimization on, the performance issue should be considered first. And this patch would make the debugbility of coroutine to be better only without changing the layout of the frame. Test-plan: check-llvm Reviewed By: aprantl, lxfind Differential Revision: https://reviews.llvm.org/D97673
This commit is contained in:
parent
3c655f8e18
commit
5dd2faa10a
@ -162,6 +162,16 @@ struct SuspendCrossingInfo {
|
|||||||
|
|
||||||
return isDefinitionAcrossSuspend(DefBB, U);
|
return isDefinitionAcrossSuspend(DefBB, U);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isDefinitionAcrossSuspend(Value &V, User *U) const {
|
||||||
|
if (auto *Arg = dyn_cast<Argument>(&V))
|
||||||
|
return isDefinitionAcrossSuspend(*Arg, U);
|
||||||
|
if (auto *Inst = dyn_cast<Instruction>(&V))
|
||||||
|
return isDefinitionAcrossSuspend(*Inst, U);
|
||||||
|
|
||||||
|
llvm_unreachable(
|
||||||
|
"Coroutine could only collect Argument and Instruction now.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
@ -1655,8 +1665,8 @@ static Instruction *insertSpills(const FrameDataInfo &FrameData,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we found any alloca, replace all of their remaining uses with GEP
|
// If we found any alloca, replace all of their remaining uses with GEP
|
||||||
// instructions. Because new dbg.declare have been created for these alloca,
|
// instructions. To remain debugbility, we replace the uses of allocas for
|
||||||
// we also delete the original dbg.declare and replace other uses with undef.
|
// dbg.declares and dbg.values with the reload from the frame.
|
||||||
// Note: We cannot replace the alloca with GEP instructions indiscriminately,
|
// Note: We cannot replace the alloca with GEP instructions indiscriminately,
|
||||||
// as some of the uses may not be dominated by CoroBegin.
|
// as some of the uses may not be dominated by CoroBegin.
|
||||||
Builder.SetInsertPoint(&Shape.AllocaSpillBlock->front());
|
Builder.SetInsertPoint(&Shape.AllocaSpillBlock->front());
|
||||||
@ -1674,16 +1684,10 @@ static Instruction *insertSpills(const FrameDataInfo &FrameData,
|
|||||||
auto *G = GetFramePointer(Alloca);
|
auto *G = GetFramePointer(Alloca);
|
||||||
G->setName(Alloca->getName() + Twine(".reload.addr"));
|
G->setName(Alloca->getName() + Twine(".reload.addr"));
|
||||||
|
|
||||||
TinyPtrVector<DbgDeclareInst *> DIs = FindDbgDeclareUses(Alloca);
|
SmallVector<DbgVariableIntrinsic *, 4> DIs;
|
||||||
if (!DIs.empty())
|
findDbgUsers(DIs, Alloca);
|
||||||
DIBuilder(*Alloca->getModule(),
|
for (auto *DVI : DIs)
|
||||||
/*AllowUnresolved*/ false)
|
DVI->replaceUsesOfWith(Alloca, G);
|
||||||
.insertDeclare(G, DIs.front()->getVariable(),
|
|
||||||
DIs.front()->getExpression(),
|
|
||||||
DIs.front()->getDebugLoc(), DIs.front());
|
|
||||||
for (auto *DI : FindDbgDeclareUses(Alloca))
|
|
||||||
DI->eraseFromParent();
|
|
||||||
replaceDbgUsesWithUndef(Alloca);
|
|
||||||
|
|
||||||
for (Instruction *I : UsersToUpdate)
|
for (Instruction *I : UsersToUpdate)
|
||||||
I->replaceUsesOfWith(Alloca, G);
|
I->replaceUsesOfWith(Alloca, G);
|
||||||
@ -2435,18 +2439,18 @@ static void collectFrameAllocas(Function &F, coro::Shape &Shape,
|
|||||||
|
|
||||||
void coro::salvageDebugInfo(
|
void coro::salvageDebugInfo(
|
||||||
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
|
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
|
||||||
DbgDeclareInst *DDI, bool ReuseFrameSlot) {
|
DbgVariableIntrinsic *DVI, bool ReuseFrameSlot) {
|
||||||
Function *F = DDI->getFunction();
|
Function *F = DVI->getFunction();
|
||||||
IRBuilder<> Builder(F->getContext());
|
IRBuilder<> Builder(F->getContext());
|
||||||
auto InsertPt = F->getEntryBlock().getFirstInsertionPt();
|
auto InsertPt = F->getEntryBlock().getFirstInsertionPt();
|
||||||
while (isa<IntrinsicInst>(InsertPt))
|
while (isa<IntrinsicInst>(InsertPt))
|
||||||
++InsertPt;
|
++InsertPt;
|
||||||
Builder.SetInsertPoint(&F->getEntryBlock(), InsertPt);
|
Builder.SetInsertPoint(&F->getEntryBlock(), InsertPt);
|
||||||
DIExpression *Expr = DDI->getExpression();
|
DIExpression *Expr = DVI->getExpression();
|
||||||
// Follow the pointer arithmetic all the way to the incoming
|
// Follow the pointer arithmetic all the way to the incoming
|
||||||
// function argument and convert into a DIExpression.
|
// function argument and convert into a DIExpression.
|
||||||
bool OutermostLoad = true;
|
bool OutermostLoad = true;
|
||||||
Value *Storage = DDI->getAddress();
|
Value *Storage = DVI->getVariableLocationOp(0);
|
||||||
Value *OriginalStorage = Storage;
|
Value *OriginalStorage = Storage;
|
||||||
while (Storage) {
|
while (Storage) {
|
||||||
if (auto *LdInst = dyn_cast<LoadInst>(Storage)) {
|
if (auto *LdInst = dyn_cast<LoadInst>(Storage)) {
|
||||||
@ -2502,12 +2506,16 @@ void coro::salvageDebugInfo(
|
|||||||
if (Expr && Expr->isComplex())
|
if (Expr && Expr->isComplex())
|
||||||
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
|
Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
|
||||||
}
|
}
|
||||||
DDI->replaceVariableLocationOp(OriginalStorage, Storage);
|
|
||||||
DDI->setExpression(Expr);
|
DVI->replaceVariableLocationOp(OriginalStorage, Storage);
|
||||||
if (auto *InsertPt = dyn_cast<Instruction>(Storage))
|
DVI->setExpression(Expr);
|
||||||
DDI->moveAfter(InsertPt);
|
/// It makes no sense to move the dbg.value intrinsic.
|
||||||
else if (isa<Argument>(Storage))
|
if (!isa<DbgValueInst>(DVI)) {
|
||||||
DDI->moveAfter(F->getEntryBlock().getFirstNonPHI());
|
if (auto *InsertPt = dyn_cast<Instruction>(Storage))
|
||||||
|
DVI->moveAfter(InsertPt);
|
||||||
|
else if (isa<Argument>(Storage))
|
||||||
|
DVI->moveAfter(F->getEntryBlock().getFirstNonPHI());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
|
void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
|
||||||
@ -2567,11 +2575,19 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
|
|||||||
for (int Repeat = 0; Repeat < 4; ++Repeat) {
|
for (int Repeat = 0; Repeat < 4; ++Repeat) {
|
||||||
// See if there are materializable instructions across suspend points.
|
// See if there are materializable instructions across suspend points.
|
||||||
for (Instruction &I : instructions(F))
|
for (Instruction &I : instructions(F))
|
||||||
if (materializable(I))
|
if (materializable(I)) {
|
||||||
for (User *U : I.users())
|
for (User *U : I.users())
|
||||||
if (Checker.isDefinitionAcrossSuspend(I, U))
|
if (Checker.isDefinitionAcrossSuspend(I, U))
|
||||||
Spills[&I].push_back(cast<Instruction>(U));
|
Spills[&I].push_back(cast<Instruction>(U));
|
||||||
|
|
||||||
|
// Manually add dbg.value metadata uses of I.
|
||||||
|
SmallVector<DbgValueInst *, 16> DVIs;
|
||||||
|
findDbgValues(DVIs, &I);
|
||||||
|
for (auto *DVI : DVIs)
|
||||||
|
if (Checker.isDefinitionAcrossSuspend(I, DVI))
|
||||||
|
Spills[&I].push_back(DVI);
|
||||||
|
}
|
||||||
|
|
||||||
if (Spills.empty())
|
if (Spills.empty())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2644,6 +2660,21 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
|
|||||||
FrameData.Spills[&I].push_back(cast<Instruction>(U));
|
FrameData.Spills[&I].push_back(cast<Instruction>(U));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We don't want the layout of coroutine frame to be affected
|
||||||
|
// by debug information. So we only choose to salvage DbgValueInst for
|
||||||
|
// whose value is already in the frame.
|
||||||
|
// We would handle the dbg.values for allocas specially
|
||||||
|
for (auto &Iter : FrameData.Spills) {
|
||||||
|
auto *V = Iter.first;
|
||||||
|
SmallVector<DbgValueInst *, 16> DVIs;
|
||||||
|
findDbgValues(DVIs, V);
|
||||||
|
llvm::for_each(DVIs, [&](DbgValueInst *DVI) {
|
||||||
|
if (Checker.isDefinitionAcrossSuspend(*V, DVI))
|
||||||
|
FrameData.Spills[V].push_back(DVI);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
LLVM_DEBUG(dumpSpills("Spills", FrameData.Spills));
|
LLVM_DEBUG(dumpSpills("Spills", FrameData.Spills));
|
||||||
if (Shape.ABI == coro::ABI::Retcon || Shape.ABI == coro::ABI::RetconOnce ||
|
if (Shape.ABI == coro::ABI::Retcon || Shape.ABI == coro::ABI::RetconOnce ||
|
||||||
Shape.ABI == coro::ABI::Async)
|
Shape.ABI == coro::ABI::Async)
|
||||||
|
@ -54,7 +54,7 @@ void updateCallGraph(Function &Caller, ArrayRef<Function *> Funcs,
|
|||||||
/// holding a pointer to the coroutine frame.
|
/// holding a pointer to the coroutine frame.
|
||||||
void salvageDebugInfo(
|
void salvageDebugInfo(
|
||||||
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
|
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
|
||||||
DbgDeclareInst *DDI, bool ReuseFrameSlot);
|
DbgVariableIntrinsic *DVI, bool ReuseFrameSlot);
|
||||||
|
|
||||||
// Keeps data and helper functions for lowering coroutine intrinsics.
|
// Keeps data and helper functions for lowering coroutine intrinsics.
|
||||||
struct LowererBase {
|
struct LowererBase {
|
||||||
|
@ -647,32 +647,32 @@ void CoroCloner::replaceSwiftErrorOps() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CoroCloner::salvageDebugInfo() {
|
void CoroCloner::salvageDebugInfo() {
|
||||||
SmallVector<DbgDeclareInst *, 8> Worklist;
|
SmallVector<DbgVariableIntrinsic *, 8> Worklist;
|
||||||
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
|
SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
|
||||||
for (auto &BB : *NewF)
|
for (auto &BB : *NewF)
|
||||||
for (auto &I : BB)
|
for (auto &I : BB)
|
||||||
if (auto *DDI = dyn_cast<DbgDeclareInst>(&I))
|
if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))
|
||||||
Worklist.push_back(DDI);
|
Worklist.push_back(DVI);
|
||||||
for (DbgDeclareInst *DDI : Worklist)
|
for (DbgVariableIntrinsic *DVI : Worklist)
|
||||||
coro::salvageDebugInfo(DbgPtrAllocaCache, DDI, Shape.ReuseFrameSlot);
|
coro::salvageDebugInfo(DbgPtrAllocaCache, DVI, Shape.ReuseFrameSlot);
|
||||||
|
|
||||||
// Remove all salvaged dbg.declare intrinsics that became
|
// Remove all salvaged dbg.declare intrinsics that became
|
||||||
// either unreachable or stale due to the CoroSplit transformation.
|
// either unreachable or stale due to the CoroSplit transformation.
|
||||||
auto IsUnreachableBlock = [&](BasicBlock *BB) {
|
auto IsUnreachableBlock = [&](BasicBlock *BB) {
|
||||||
return BB->hasNPredecessors(0) && BB != &NewF->getEntryBlock();
|
return BB->hasNPredecessors(0) && BB != &NewF->getEntryBlock();
|
||||||
};
|
};
|
||||||
for (DbgDeclareInst *DDI : Worklist) {
|
for (DbgVariableIntrinsic *DVI : Worklist) {
|
||||||
if (IsUnreachableBlock(DDI->getParent()))
|
if (IsUnreachableBlock(DVI->getParent()))
|
||||||
DDI->eraseFromParent();
|
DVI->eraseFromParent();
|
||||||
else if (dyn_cast_or_null<AllocaInst>(DDI->getAddress())) {
|
else if (dyn_cast_or_null<AllocaInst>(DVI->getVariableLocationOp(0))) {
|
||||||
// Count all non-debuginfo uses in reachable blocks.
|
// Count all non-debuginfo uses in reachable blocks.
|
||||||
unsigned Uses = 0;
|
unsigned Uses = 0;
|
||||||
for (auto *User : DDI->getAddress()->users())
|
for (auto *User : DVI->getVariableLocationOp(0)->users())
|
||||||
if (auto *I = dyn_cast<Instruction>(User))
|
if (auto *I = dyn_cast<Instruction>(User))
|
||||||
if (!isa<AllocaInst>(I) && !IsUnreachableBlock(I->getParent()))
|
if (!isa<AllocaInst>(I) && !IsUnreachableBlock(I->getParent()))
|
||||||
++Uses;
|
++Uses;
|
||||||
if (!Uses)
|
if (!Uses)
|
||||||
DDI->eraseFromParent();
|
DVI->eraseFromParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,219 @@
|
|||||||
|
; Tests whether resume function would remain dbg.value infomation if corresponding values are not used in the frame.
|
||||||
|
; RUN: opt < %s -coro-early -coro-split -coro-split -S | FileCheck %s
|
||||||
|
;
|
||||||
|
; This file is based on coro-debug-frame-variable.ll.
|
||||||
|
; CHECK: define internal fastcc void @f.resume(%f.Frame* noalias nonnull align 16 dereferenceable(80) %FramePtr) !dbg ![[RESUME_FN_DBG_NUM:[0-9]+]]
|
||||||
|
; CHECK: await.ready:
|
||||||
|
; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata ![[IVAR_RESUME:[0-9]+]], metadata !DIExpression(
|
||||||
|
; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata ![[JVAR_RESUME:[0-9]+]], metadata !DIExpression(
|
||||||
|
;
|
||||||
|
; CHECK: ![[RESUME_FN_DBG_NUM]] = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov"
|
||||||
|
; CHECK: ![[IVAR_RESUME]] = !DILocalVariable(name: "i"
|
||||||
|
; CHECK: ![[JVAR_RESUME]] = !DILocalVariable(name: "j"
|
||||||
|
|
||||||
|
source_filename = "../llvm/test/Transforms/Coroutines/coro-debug-dbg.values-O2.ll"
|
||||||
|
|
||||||
|
define void @f(i32 %i, i32 %j) !dbg !8 {
|
||||||
|
entry:
|
||||||
|
%__promise = alloca i8, align 8
|
||||||
|
%x = alloca [10 x i32], align 16
|
||||||
|
%id = call token @llvm.coro.id(i32 16, i8* %__promise, i8* null, i8* null)
|
||||||
|
%alloc = call i1 @llvm.coro.alloc(token %id)
|
||||||
|
br i1 %alloc, label %coro.alloc, label %coro.init
|
||||||
|
|
||||||
|
coro.alloc: ; preds = %entry
|
||||||
|
%size = call i64 @llvm.coro.size.i64()
|
||||||
|
%memory = call i8* @new(i64 %size)
|
||||||
|
br label %coro.init
|
||||||
|
|
||||||
|
coro.init: ; preds = %coro.alloc, %entry
|
||||||
|
%phi.entry.alloc = phi i8* [ null, %entry ], [ %memory, %coro.alloc ]
|
||||||
|
%begin = call i8* @llvm.coro.begin(token %id, i8* %phi.entry.alloc)
|
||||||
|
%ready = call i1 @await_ready()
|
||||||
|
br i1 %ready, label %init.ready, label %init.suspend
|
||||||
|
|
||||||
|
init.suspend: ; preds = %coro.init
|
||||||
|
%save = call token @llvm.coro.save(i8* null)
|
||||||
|
call void @await_suspend()
|
||||||
|
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
|
||||||
|
switch i8 %suspend, label %coro.ret [
|
||||||
|
i8 0, label %init.ready
|
||||||
|
i8 1, label %init.cleanup
|
||||||
|
]
|
||||||
|
|
||||||
|
init.cleanup: ; preds = %init.suspend
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
init.ready: ; preds = %init.suspend, %coro.init
|
||||||
|
call void @await_resume()
|
||||||
|
call void @llvm.dbg.value(metadata i32 0, metadata !6, metadata !DIExpression()), !dbg !11
|
||||||
|
%i.init.ready.inc = add nsw i32 0, 1
|
||||||
|
call void @llvm.dbg.value(metadata i32 %i.init.ready.inc, metadata !6, metadata !DIExpression()), !dbg !11
|
||||||
|
call void @llvm.dbg.declare(metadata [10 x i32]* %x, metadata !12, metadata !DIExpression()), !dbg !17
|
||||||
|
%memset = bitcast [10 x i32]* %x to i8*, !dbg !17
|
||||||
|
call void @llvm.memset.p0i8.i64(i8* align 16 %memset, i8 0, i64 40, i1 false), !dbg !17
|
||||||
|
call void @print(i32 %i.init.ready.inc)
|
||||||
|
%ready.again = call zeroext i1 @await_ready()
|
||||||
|
br i1 %ready.again, label %await.ready, label %await.suspend
|
||||||
|
|
||||||
|
await.suspend: ; preds = %init.ready
|
||||||
|
%save.again = call token @llvm.coro.save(i8* null)
|
||||||
|
%from.address = call i8* @from_address(i8* %begin)
|
||||||
|
call void @await_suspend()
|
||||||
|
%suspend.again = call i8 @llvm.coro.suspend(token %save.again, i1 false)
|
||||||
|
switch i8 %suspend.again, label %coro.ret [
|
||||||
|
i8 0, label %await.ready
|
||||||
|
i8 1, label %await.cleanup
|
||||||
|
]
|
||||||
|
|
||||||
|
await.cleanup: ; preds = %await.suspend
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
await.ready: ; preds = %await.suspend, %init.ready
|
||||||
|
call void @await_resume()
|
||||||
|
call void @llvm.dbg.value(metadata i32 0, metadata !18, metadata !DIExpression()), !dbg !11
|
||||||
|
%arrayidx0 = getelementptr inbounds [10 x i32], [10 x i32]* %x, i64 0, i64 0, !dbg !19
|
||||||
|
store i32 1, i32* %arrayidx0, align 16, !dbg !20
|
||||||
|
%arrayidx1 = getelementptr inbounds [10 x i32], [10 x i32]* %x, i64 0, i64 1, !dbg !21
|
||||||
|
store i32 2, i32* %arrayidx1, align 4, !dbg !22
|
||||||
|
%i.await.ready.inc = add nsw i32 %i.init.ready.inc, 1
|
||||||
|
call void @llvm.dbg.value(metadata i32 %i, metadata !6, metadata !DIExpression()), !dbg !11
|
||||||
|
call void @llvm.dbg.value(metadata i32 %j, metadata !18, metadata !DIExpression()), !dbg !11
|
||||||
|
call void @print(i32 %i.await.ready.inc)
|
||||||
|
call void @return_void()
|
||||||
|
br label %coro.final
|
||||||
|
|
||||||
|
coro.final: ; preds = %await.ready
|
||||||
|
call void @final_suspend()
|
||||||
|
%coro.final.await_ready = call i1 @await_ready()
|
||||||
|
br i1 %coro.final.await_ready, label %final.ready, label %final.suspend
|
||||||
|
|
||||||
|
final.suspend: ; preds = %coro.final
|
||||||
|
%final.suspend.coro.save = call token @llvm.coro.save(i8* null)
|
||||||
|
%final.suspend.from_address = call i8* @from_address(i8* %begin)
|
||||||
|
call void @await_suspend()
|
||||||
|
%final.suspend.coro.suspend = call i8 @llvm.coro.suspend(token %final.suspend.coro.save, i1 true)
|
||||||
|
switch i8 %final.suspend.coro.suspend, label %coro.ret [
|
||||||
|
i8 0, label %final.ready
|
||||||
|
i8 1, label %final.cleanup
|
||||||
|
]
|
||||||
|
|
||||||
|
final.cleanup: ; preds = %final.suspend
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
final.ready: ; preds = %final.suspend, %coro.final
|
||||||
|
call void @await_resume()
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
cleanup: ; preds = %final.ready, %final.cleanup, %await.cleanup, %init.cleanup
|
||||||
|
%cleanup.dest.slot.0 = phi i32 [ 0, %final.ready ], [ 2, %final.cleanup ], [ 2, %await.cleanup ], [ 2, %init.cleanup ]
|
||||||
|
%free.memory = call i8* @llvm.coro.free(token %id, i8* %begin)
|
||||||
|
%free = icmp ne i8* %free.memory, null
|
||||||
|
br i1 %free, label %coro.free, label %after.coro.free
|
||||||
|
|
||||||
|
coro.free: ; preds = %cleanup
|
||||||
|
call void @delete(i8* %free.memory)
|
||||||
|
br label %after.coro.free
|
||||||
|
|
||||||
|
after.coro.free: ; preds = %coro.free, %cleanup
|
||||||
|
switch i32 %cleanup.dest.slot.0, label %unreachable [
|
||||||
|
i32 0, label %cleanup.cont
|
||||||
|
i32 2, label %coro.ret
|
||||||
|
]
|
||||||
|
|
||||||
|
cleanup.cont: ; preds = %after.coro.free
|
||||||
|
br label %coro.ret
|
||||||
|
|
||||||
|
coro.ret: ; preds = %cleanup.cont, %after.coro.free, %final.suspend, %await.suspend, %init.suspend
|
||||||
|
%end = call i1 @llvm.coro.end(i8* null, i1 false)
|
||||||
|
ret void
|
||||||
|
|
||||||
|
unreachable: ; preds = %after.coro.free
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||||
|
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind readonly
|
||||||
|
declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i1 @llvm.coro.alloc(token) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind readnone
|
||||||
|
declare i64 @llvm.coro.size.i64() #3
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare token @llvm.coro.save(i8*) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i8* @llvm.coro.begin(token, i8* writeonly) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i8 @llvm.coro.suspend(token, i1) #2
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind readonly
|
||||||
|
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i1 @llvm.coro.end(i8*, i1) #2
|
||||||
|
|
||||||
|
declare i8* @new(i64)
|
||||||
|
|
||||||
|
declare void @delete(i8*)
|
||||||
|
|
||||||
|
declare i1 @await_ready()
|
||||||
|
|
||||||
|
declare void @await_suspend()
|
||||||
|
|
||||||
|
declare void @await_resume()
|
||||||
|
|
||||||
|
declare void @print(i32)
|
||||||
|
|
||||||
|
declare i8* @from_address(i8*)
|
||||||
|
|
||||||
|
declare void @return_void()
|
||||||
|
|
||||||
|
declare void @final_suspend()
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly
|
||||||
|
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #4
|
||||||
|
|
||||||
|
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||||
|
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
|
||||||
|
|
||||||
|
attributes #0 = { nofree nosync nounwind readnone speculatable willreturn }
|
||||||
|
attributes #1 = { argmemonly nounwind readonly }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
attributes #3 = { nounwind readnone }
|
||||||
|
attributes #4 = { argmemonly nofree nosync nounwind willreturn writeonly }
|
||||||
|
|
||||||
|
!llvm.dbg.cu = !{!0}
|
||||||
|
!llvm.linker.options = !{}
|
||||||
|
!llvm.module.flags = !{!3, !4}
|
||||||
|
!llvm.ident = !{!5}
|
||||||
|
|
||||||
|
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
|
||||||
|
!1 = !DIFile(filename: "repro.cpp", directory: ".")
|
||||||
|
!2 = !{}
|
||||||
|
!3 = !{i32 7, !"Dwarf Version", i32 4}
|
||||||
|
!4 = !{i32 2, !"Debug Info Version", i32 3}
|
||||||
|
!5 = !{!"clang version 11.0.0"}
|
||||||
|
!6 = !DILocalVariable(name: "i", scope: !7, file: !1, line: 24, type: !10)
|
||||||
|
!7 = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12)
|
||||||
|
!8 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 23, type: !9, scopeLine: 23, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
|
||||||
|
!9 = !DISubroutineType(types: !2)
|
||||||
|
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
||||||
|
!11 = !DILocation(line: 0, scope: !7)
|
||||||
|
!12 = !DILocalVariable(name: "x", scope: !13, file: !1, line: 34, type: !14)
|
||||||
|
!13 = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12)
|
||||||
|
!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !10, size: 320, elements: !15)
|
||||||
|
!15 = !{!16}
|
||||||
|
!16 = !DISubrange(count: 10)
|
||||||
|
!17 = !DILocation(line: 24, column: 7, scope: !7)
|
||||||
|
!18 = !DILocalVariable(name: "j", scope: !7, file: !1, line: 32, type: !10)
|
||||||
|
!19 = !DILocation(line: 42, column: 3, scope: !7)
|
||||||
|
!20 = !DILocation(line: 42, column: 8, scope: !7)
|
||||||
|
!21 = !DILocation(line: 43, column: 3, scope: !7)
|
||||||
|
!22 = !DILocation(line: 43, column: 8, scope: !7)
|
233
test/Transforms/Coroutines/coro-debug-dbg.values.ll
Normal file
233
test/Transforms/Coroutines/coro-debug-dbg.values.ll
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
; Tests whether resume function would remain dbg.value infomation.
|
||||||
|
; RUN: opt < %s -coro-early -coro-split -coro-split -S | FileCheck %s
|
||||||
|
;
|
||||||
|
; This file is based on coro-debug-frame-variable.ll.
|
||||||
|
; CHECK: define internal fastcc void @f.resume(%f.Frame* noalias nonnull align 16 dereferenceable(80) %FramePtr) !dbg ![[RESUME_FN_DBG_NUM:[0-9]+]]
|
||||||
|
; CHECK: init.ready:
|
||||||
|
; CHECK: call void @llvm.dbg.value(metadata %f.Frame** %FramePtr.debug, metadata ![[XVAR_RESUME:[0-9]+]],
|
||||||
|
; CHECK: await.ready:
|
||||||
|
; CHECK: call void @llvm.dbg.value(metadata %f.Frame** %FramePtr.debug, metadata ![[SPILL_RESUME:[0-9]+]]
|
||||||
|
; CHECK: call void @llvm.dbg.value(metadata %f.Frame** %FramePtr.debug, metadata ![[IVAR_RESUME:[0-9]+]], metadata !DIExpression(
|
||||||
|
; CHECK: call void @llvm.dbg.value(metadata %f.Frame** %FramePtr.debug, metadata ![[JVAR_RESUME:[0-9]+]], metadata !DIExpression(
|
||||||
|
;
|
||||||
|
; CHECK: ![[RESUME_FN_DBG_NUM]] = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov"
|
||||||
|
; CHECK: ![[IVAR_RESUME]] = !DILocalVariable(name: "i"
|
||||||
|
; CHECK: ![[XVAR_RESUME]] = !DILocalVariable(name: "x"
|
||||||
|
; CHECK: ![[JVAR_RESUME]] = !DILocalVariable(name: "j"
|
||||||
|
; CHECK: ![[SPILL_RESUME]] = !DILocalVariable(name: "produced"
|
||||||
|
|
||||||
|
source_filename = "../llvm/test/Transforms/Coroutines/coro-debug-dbg.values-O2.ll"
|
||||||
|
declare void @consume(i32)
|
||||||
|
|
||||||
|
define void @f(i32 %i, i32 %j) !dbg !8 {
|
||||||
|
entry:
|
||||||
|
%__promise = alloca i8, align 8
|
||||||
|
%x = alloca [10 x i32], align 16
|
||||||
|
%produced = call i32 @value_producer()
|
||||||
|
%id = call token @llvm.coro.id(i32 16, i8* %__promise, i8* null, i8* null)
|
||||||
|
%alloc = call i1 @llvm.coro.alloc(token %id)
|
||||||
|
br i1 %alloc, label %coro.alloc, label %coro.init
|
||||||
|
|
||||||
|
coro.alloc: ; preds = %entry
|
||||||
|
%size = call i64 @llvm.coro.size.i64()
|
||||||
|
%memory = call i8* @new(i64 %size)
|
||||||
|
br label %coro.init
|
||||||
|
|
||||||
|
coro.init: ; preds = %coro.alloc, %entry
|
||||||
|
%phi.entry.alloc = phi i8* [ null, %entry ], [ %memory, %coro.alloc ]
|
||||||
|
%begin = call i8* @llvm.coro.begin(token %id, i8* %phi.entry.alloc)
|
||||||
|
%ready = call i1 @await_ready()
|
||||||
|
br i1 %ready, label %init.ready, label %init.suspend
|
||||||
|
|
||||||
|
init.suspend: ; preds = %coro.init
|
||||||
|
%save = call token @llvm.coro.save(i8* null)
|
||||||
|
call void @await_suspend()
|
||||||
|
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
|
||||||
|
switch i8 %suspend, label %coro.ret [
|
||||||
|
i8 0, label %init.ready
|
||||||
|
i8 1, label %init.cleanup
|
||||||
|
]
|
||||||
|
|
||||||
|
init.cleanup: ; preds = %init.suspend
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
init.ready: ; preds = %init.suspend, %coro.init
|
||||||
|
call void @await_resume()
|
||||||
|
call void @llvm.dbg.value(metadata i32 0, metadata !6, metadata !DIExpression()), !dbg !11
|
||||||
|
%i.init.ready.inc = add nsw i32 0, 1
|
||||||
|
call void @llvm.dbg.value(metadata i32 %i.init.ready.inc, metadata !6, metadata !DIExpression()), !dbg !11
|
||||||
|
call void @llvm.dbg.value(metadata [10 x i32]* %x, metadata !12, metadata !DIExpression()), !dbg !17
|
||||||
|
%memset = bitcast [10 x i32]* %x to i8*, !dbg !17
|
||||||
|
call void @llvm.memset.p0i8.i64(i8* align 16 %memset, i8 0, i64 40, i1 false), !dbg !17
|
||||||
|
call void @print(i32 %i.init.ready.inc)
|
||||||
|
%ready.again = call zeroext i1 @await_ready()
|
||||||
|
br i1 %ready.again, label %await.ready, label %await.suspend
|
||||||
|
|
||||||
|
await.suspend: ; preds = %init.ready
|
||||||
|
%save.again = call token @llvm.coro.save(i8* null)
|
||||||
|
%from.address = call i8* @from_address(i8* %begin)
|
||||||
|
call void @await_suspend()
|
||||||
|
%suspend.again = call i8 @llvm.coro.suspend(token %save.again, i1 false)
|
||||||
|
switch i8 %suspend.again, label %coro.ret [
|
||||||
|
i8 0, label %await.ready
|
||||||
|
i8 1, label %await.cleanup
|
||||||
|
]
|
||||||
|
|
||||||
|
await.cleanup: ; preds = %await.suspend
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
await.ready: ; preds = %await.suspend, %init.ready
|
||||||
|
call void @await_resume()
|
||||||
|
call void @llvm.dbg.value(metadata i32 0, metadata !18, metadata !DIExpression()), !dbg !11
|
||||||
|
%arrayidx0 = getelementptr inbounds [10 x i32], [10 x i32]* %x, i64 0, i64 0, !dbg !19
|
||||||
|
store i32 1, i32* %arrayidx0, align 16, !dbg !20
|
||||||
|
%arrayidx1 = getelementptr inbounds [10 x i32], [10 x i32]* %x, i64 0, i64 1, !dbg !21
|
||||||
|
store i32 2, i32* %arrayidx1, align 4, !dbg !22
|
||||||
|
%i.await.ready.inc = add nsw i32 %i.init.ready.inc, 1
|
||||||
|
call void @consume(i32 %produced)
|
||||||
|
call void @consume(i32 %i)
|
||||||
|
call void @consume(i32 %j)
|
||||||
|
call void @llvm.dbg.value(metadata i32 %produced, metadata !23, metadata !DIExpression()), !dbg !11
|
||||||
|
call void @llvm.dbg.value(metadata i32 %i, metadata !6, metadata !DIExpression()), !dbg !11
|
||||||
|
call void @llvm.dbg.value(metadata i32 %j, metadata !18, metadata !DIExpression()), !dbg !11
|
||||||
|
call void @print(i32 %i.await.ready.inc)
|
||||||
|
call void @return_void()
|
||||||
|
br label %coro.final
|
||||||
|
|
||||||
|
coro.final: ; preds = %await.ready
|
||||||
|
call void @final_suspend()
|
||||||
|
%coro.final.await_ready = call i1 @await_ready()
|
||||||
|
br i1 %coro.final.await_ready, label %final.ready, label %final.suspend
|
||||||
|
|
||||||
|
final.suspend: ; preds = %coro.final
|
||||||
|
%final.suspend.coro.save = call token @llvm.coro.save(i8* null)
|
||||||
|
%final.suspend.from_address = call i8* @from_address(i8* %begin)
|
||||||
|
call void @await_suspend()
|
||||||
|
%final.suspend.coro.suspend = call i8 @llvm.coro.suspend(token %final.suspend.coro.save, i1 true)
|
||||||
|
switch i8 %final.suspend.coro.suspend, label %coro.ret [
|
||||||
|
i8 0, label %final.ready
|
||||||
|
i8 1, label %final.cleanup
|
||||||
|
]
|
||||||
|
|
||||||
|
final.cleanup: ; preds = %final.suspend
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
final.ready: ; preds = %final.suspend, %coro.final
|
||||||
|
call void @await_resume()
|
||||||
|
br label %cleanup
|
||||||
|
|
||||||
|
cleanup: ; preds = %final.ready, %final.cleanup, %await.cleanup, %init.cleanup
|
||||||
|
%cleanup.dest.slot.0 = phi i32 [ 0, %final.ready ], [ 2, %final.cleanup ], [ 2, %await.cleanup ], [ 2, %init.cleanup ]
|
||||||
|
%free.memory = call i8* @llvm.coro.free(token %id, i8* %begin)
|
||||||
|
%free = icmp ne i8* %free.memory, null
|
||||||
|
br i1 %free, label %coro.free, label %after.coro.free
|
||||||
|
|
||||||
|
coro.free: ; preds = %cleanup
|
||||||
|
call void @delete(i8* %free.memory)
|
||||||
|
br label %after.coro.free
|
||||||
|
|
||||||
|
after.coro.free: ; preds = %coro.free, %cleanup
|
||||||
|
switch i32 %cleanup.dest.slot.0, label %unreachable [
|
||||||
|
i32 0, label %cleanup.cont
|
||||||
|
i32 2, label %coro.ret
|
||||||
|
]
|
||||||
|
|
||||||
|
cleanup.cont: ; preds = %after.coro.free
|
||||||
|
br label %coro.ret
|
||||||
|
|
||||||
|
coro.ret: ; preds = %cleanup.cont, %after.coro.free, %final.suspend, %await.suspend, %init.suspend
|
||||||
|
%end = call i1 @llvm.coro.end(i8* null, i1 false)
|
||||||
|
ret void
|
||||||
|
|
||||||
|
unreachable: ; preds = %after.coro.free
|
||||||
|
unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||||
|
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind readonly
|
||||||
|
declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i1 @llvm.coro.alloc(token) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind readnone
|
||||||
|
declare i64 @llvm.coro.size.i64() #3
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare token @llvm.coro.save(i8*) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i8* @llvm.coro.begin(token, i8* writeonly) #2
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i8 @llvm.coro.suspend(token, i1) #2
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nounwind readonly
|
||||||
|
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #1
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
declare i1 @llvm.coro.end(i8*, i1) #2
|
||||||
|
|
||||||
|
declare i8* @new(i64)
|
||||||
|
|
||||||
|
declare void @delete(i8*)
|
||||||
|
|
||||||
|
declare i1 @await_ready()
|
||||||
|
|
||||||
|
declare void @await_suspend()
|
||||||
|
|
||||||
|
declare void @await_resume()
|
||||||
|
|
||||||
|
declare void @print(i32)
|
||||||
|
|
||||||
|
declare i8* @from_address(i8*)
|
||||||
|
|
||||||
|
declare void @return_void()
|
||||||
|
|
||||||
|
declare void @final_suspend()
|
||||||
|
|
||||||
|
declare i32 @value_producer()
|
||||||
|
|
||||||
|
; Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly
|
||||||
|
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #4
|
||||||
|
|
||||||
|
; Function Attrs: nofree nosync nounwind readnone speculatable willreturn
|
||||||
|
declare void @llvm.dbg.value(metadata, metadata, metadata) #0
|
||||||
|
|
||||||
|
attributes #0 = { nofree nosync nounwind readnone speculatable willreturn }
|
||||||
|
attributes #1 = { argmemonly nounwind readonly }
|
||||||
|
attributes #2 = { nounwind }
|
||||||
|
attributes #3 = { nounwind readnone }
|
||||||
|
attributes #4 = { argmemonly nofree nosync nounwind willreturn writeonly }
|
||||||
|
|
||||||
|
!llvm.dbg.cu = !{!0}
|
||||||
|
!llvm.linker.options = !{}
|
||||||
|
!llvm.module.flags = !{!3, !4}
|
||||||
|
!llvm.ident = !{!5}
|
||||||
|
|
||||||
|
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
|
||||||
|
!1 = !DIFile(filename: "repro.cpp", directory: ".")
|
||||||
|
!2 = !{}
|
||||||
|
!3 = !{i32 7, !"Dwarf Version", i32 4}
|
||||||
|
!4 = !{i32 2, !"Debug Info Version", i32 3}
|
||||||
|
!5 = !{!"clang version 11.0.0"}
|
||||||
|
!6 = !DILocalVariable(name: "i", scope: !7, file: !1, line: 24, type: !10)
|
||||||
|
!7 = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12)
|
||||||
|
!8 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 23, type: !9, scopeLine: 23, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
|
||||||
|
!9 = !DISubroutineType(types: !2)
|
||||||
|
!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
||||||
|
!11 = !DILocation(line: 0, scope: !7)
|
||||||
|
!12 = !DILocalVariable(name: "x", scope: !13, file: !1, line: 34, type: !14)
|
||||||
|
!13 = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12)
|
||||||
|
!14 = !DICompositeType(tag: DW_TAG_array_type, baseType: !10, size: 320, elements: !15)
|
||||||
|
!15 = !{!16}
|
||||||
|
!16 = !DISubrange(count: 10)
|
||||||
|
!17 = !DILocation(line: 24, column: 7, scope: !7)
|
||||||
|
!18 = !DILocalVariable(name: "j", scope: !7, file: !1, line: 32, type: !10)
|
||||||
|
!19 = !DILocation(line: 42, column: 3, scope: !7)
|
||||||
|
!20 = !DILocation(line: 42, column: 8, scope: !7)
|
||||||
|
!21 = !DILocation(line: 43, column: 3, scope: !7)
|
||||||
|
!22 = !DILocation(line: 43, column: 8, scope: !7)
|
||||||
|
!23 = !DILocalVariable(name: "produced", scope: !7, file: !1, line:24, type: !10)
|
Loading…
Reference in New Issue
Block a user