1
0
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:
Chuanqi Xu 2021-05-13 13:05:05 +08:00
parent 3c655f8e18
commit 5dd2faa10a
5 changed files with 518 additions and 35 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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();
} }
} }
} }

View File

@ -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)

View 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)