diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp --- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -162,6 +162,16 @@ return isDefinitionAcrossSuspend(DefBB, U); } + + bool isDefinitionAcrossSuspend(Value &V, User *U) const { + if (auto *Arg = dyn_cast(&V)) + return isDefinitionAcrossSuspend(*Arg, U); + if (auto *Inst = dyn_cast(&V)) + return isDefinitionAcrossSuspend(*Inst, U); + + llvm_unreachable( + "Coroutine could only collect Argument and Instruction now."); + } }; } // end anonymous namespace @@ -1655,8 +1665,8 @@ } // If we found any alloca, replace all of their remaining uses with GEP - // instructions. Because new dbg.declare have been created for these alloca, - // we also delete the original dbg.declare and replace other uses with undef. + // instructions. To remain debugbility, we replace the uses of allocas for + // dbg.declares and dbg.values with the reload from the frame. // Note: We cannot replace the alloca with GEP instructions indiscriminately, // as some of the uses may not be dominated by CoroBegin. Builder.SetInsertPoint(&Shape.AllocaSpillBlock->front()); @@ -1674,16 +1684,10 @@ auto *G = GetFramePointer(Alloca); G->setName(Alloca->getName() + Twine(".reload.addr")); - TinyPtrVector DIs = FindDbgDeclareUses(Alloca); - if (!DIs.empty()) - DIBuilder(*Alloca->getModule(), - /*AllowUnresolved*/ false) - .insertDeclare(G, DIs.front()->getVariable(), - DIs.front()->getExpression(), - DIs.front()->getDebugLoc(), DIs.front()); - for (auto *DI : FindDbgDeclareUses(Alloca)) - DI->eraseFromParent(); - replaceDbgUsesWithUndef(Alloca); + SmallVector DIs; + findDbgUsers(DIs, Alloca); + for (auto *DVI : DIs) + DVI->replaceUsesOfWith(Alloca, G); for (Instruction *I : UsersToUpdate) I->replaceUsesOfWith(Alloca, G); @@ -2435,18 +2439,18 @@ void coro::salvageDebugInfo( SmallDenseMap &DbgPtrAllocaCache, - DbgDeclareInst *DDI, bool ReuseFrameSlot) { - Function *F = DDI->getFunction(); + DbgVariableIntrinsic *DVI, bool ReuseFrameSlot) { + Function *F = DVI->getFunction(); IRBuilder<> Builder(F->getContext()); auto InsertPt = F->getEntryBlock().getFirstInsertionPt(); while (isa(InsertPt)) ++InsertPt; Builder.SetInsertPoint(&F->getEntryBlock(), InsertPt); - DIExpression *Expr = DDI->getExpression(); + DIExpression *Expr = DVI->getExpression(); // Follow the pointer arithmetic all the way to the incoming // function argument and convert into a DIExpression. bool OutermostLoad = true; - Value *Storage = DDI->getAddress(); + Value *Storage = DVI->getVariableLocationOp(0); Value *OriginalStorage = Storage; while (Storage) { if (auto *LdInst = dyn_cast(Storage)) { @@ -2502,12 +2506,16 @@ if (Expr && Expr->isComplex()) Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore); } - DDI->replaceVariableLocationOp(OriginalStorage, Storage); - DDI->setExpression(Expr); - if (auto *InsertPt = dyn_cast(Storage)) - DDI->moveAfter(InsertPt); - else if (isa(Storage)) - DDI->moveAfter(F->getEntryBlock().getFirstNonPHI()); + + DVI->replaceVariableLocationOp(OriginalStorage, Storage); + DVI->setExpression(Expr); + /// It makes no sense to move the dbg.value intrinsic. + if (!isa(DVI)) { + if (auto *InsertPt = dyn_cast(Storage)) + DVI->moveAfter(InsertPt); + else if (isa(Storage)) + DVI->moveAfter(F->getEntryBlock().getFirstNonPHI()); + } } void coro::buildCoroutineFrame(Function &F, Shape &Shape) { @@ -2567,11 +2575,19 @@ for (int Repeat = 0; Repeat < 4; ++Repeat) { // See if there are materializable instructions across suspend points. for (Instruction &I : instructions(F)) - if (materializable(I)) + if (materializable(I)) { for (User *U : I.users()) if (Checker.isDefinitionAcrossSuspend(I, U)) Spills[&I].push_back(cast(U)); + // Manually add dbg.value metadata uses of I. + SmallVector DVIs; + findDbgValues(DVIs, &I); + for (auto *DVI : DVIs) + if (Checker.isDefinitionAcrossSuspend(I, DVI)) + Spills[&I].push_back(DVI); + } + if (Spills.empty()) break; @@ -2644,6 +2660,21 @@ FrameData.Spills[&I].push_back(cast(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 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)); if (Shape.ABI == coro::ABI::Retcon || Shape.ABI == coro::ABI::RetconOnce || Shape.ABI == coro::ABI::Async) diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h --- a/llvm/lib/Transforms/Coroutines/CoroInternal.h +++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -54,7 +54,7 @@ /// holding a pointer to the coroutine frame. void salvageDebugInfo( SmallDenseMap &DbgPtrAllocaCache, - DbgDeclareInst *DDI, bool ReuseFrameSlot); + DbgVariableIntrinsic *DVI, bool ReuseFrameSlot); // Keeps data and helper functions for lowering coroutine intrinsics. struct LowererBase { diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp --- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp @@ -647,32 +647,32 @@ } void CoroCloner::salvageDebugInfo() { - SmallVector Worklist; + SmallVector Worklist; SmallDenseMap DbgPtrAllocaCache; for (auto &BB : *NewF) for (auto &I : BB) - if (auto *DDI = dyn_cast(&I)) - Worklist.push_back(DDI); - for (DbgDeclareInst *DDI : Worklist) - coro::salvageDebugInfo(DbgPtrAllocaCache, DDI, Shape.ReuseFrameSlot); + if (auto *DVI = dyn_cast(&I)) + Worklist.push_back(DVI); + for (DbgVariableIntrinsic *DVI : Worklist) + coro::salvageDebugInfo(DbgPtrAllocaCache, DVI, Shape.ReuseFrameSlot); // Remove all salvaged dbg.declare intrinsics that became // either unreachable or stale due to the CoroSplit transformation. auto IsUnreachableBlock = [&](BasicBlock *BB) { return BB->hasNPredecessors(0) && BB != &NewF->getEntryBlock(); }; - for (DbgDeclareInst *DDI : Worklist) { - if (IsUnreachableBlock(DDI->getParent())) - DDI->eraseFromParent(); - else if (dyn_cast_or_null(DDI->getAddress())) { + for (DbgVariableIntrinsic *DVI : Worklist) { + if (IsUnreachableBlock(DVI->getParent())) + DVI->eraseFromParent(); + else if (dyn_cast_or_null(DVI->getVariableLocationOp(0))) { // Count all non-debuginfo uses in reachable blocks. unsigned Uses = 0; - for (auto *User : DDI->getAddress()->users()) + for (auto *User : DVI->getVariableLocationOp(0)->users()) if (auto *I = dyn_cast(User)) if (!isa(I) && !IsUnreachableBlock(I->getParent())) ++Uses; if (!Uses) - DDI->eraseFromParent(); + DVI->eraseFromParent(); } } } diff --git a/llvm/test/Transforms/Coroutines/coro-debug-dbg.values-not_used_in_frame.ll b/llvm/test/Transforms/Coroutines/coro-debug-dbg.values-not_used_in_frame.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-debug-dbg.values-not_used_in_frame.ll @@ -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) diff --git a/llvm/test/Transforms/Coroutines/coro-debug-dbg.values.ll b/llvm/test/Transforms/Coroutines/coro-debug-dbg.values.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Coroutines/coro-debug-dbg.values.ll @@ -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)