Index: llvm/lib/Transforms/Coroutines/CoroFrame.cpp =================================================================== --- llvm/lib/Transforms/Coroutines/CoroFrame.cpp +++ llvm/lib/Transforms/Coroutines/CoroFrame.cpp @@ -48,6 +48,12 @@ frame for allocas whose liferanges are not overlapped, for testing purposes"), llvm::cl::init(false)); +static cl::opt EnhanceDebugability( + "enhance-debug-with-coroutine", cl::Hidden, + cl::desc("Try to salvage as many debug infomation as possible. \ + This option may enlarge the size of coroutine frame."), + llvm::cl::init(false)); + enum { SmallVectorThreshold = 32 }; // Provides two way mapping between the blocks and numbers. @@ -1261,6 +1267,19 @@ continue; } + // Replace DbgValueInst manually since replaceUsesOfWith wouldn't work. + if (auto *DVI = dyn_cast(U)) { + llvm::dbgs() << "Find Dbg Value User in frame: " << *DVI << "\n"; + auto *NewDVI = DIBuilder(*CurrentBlock->getParent()->getParent(), + /*AllowUnresolved=*/false) + .insertDbgValueIntrinsic( + CurrentReload, DVI->getVariable(), + DVI->getExpression(), DVI->getDebugLoc(), DVI); + llvm::dbgs() << "New DVI: " << *NewDVI << "\n"; + DVI->eraseFromParent(); + continue; + } + // Replace all uses of CurrentValue in the current instruction with // reload. U->replaceUsesOfWith(Def, CurrentReload); @@ -1294,7 +1313,7 @@ // 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. + // we also delete the original dbg.declare. // 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()); @@ -1321,7 +1340,21 @@ DIs.front()->getDebugLoc(), DIs.front()); for (auto *DI : FindDbgDeclareUses(Alloca)) DI->eraseFromParent(); - replaceDbgUsesWithUndef(Alloca); + + // Replace alloca uses in dbg.value manually. + // Since Alloca->users() doesn't contain dbg.value. + SmallVector DVIs; + findDbgValues(DVIs, Alloca); + for (auto *DVI : DVIs) { + if (DT.dominates(CB, DVI)) { + DIBuilder(*Alloca->getModule(), + /*AllowUnresolved*/ false) + .insertDbgValueIntrinsic(G, DVI->getVariable(), + DVI->getExpression(), DVI->getDebugLoc(), + DVI); + DVI->eraseFromParent(); + } + } for (Instruction *I : UsersToUpdate) I->replaceUsesOfWith(Alloca, G); @@ -2144,18 +2177,18 @@ void coro::salvageDebugInfo( SmallDenseMap &DbgPtrAllocaCache, - DbgDeclareInst *DDI) { - Function *F = DDI->getFunction(); + DbgVariableIntrinsic *DVI) { + 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)) { @@ -2207,10 +2240,11 @@ if (Expr && Expr->isComplex()) Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore); } - DDI->replaceVariableLocationOp(OriginalStorage, Storage); - DDI->setExpression(Expr); + DVI->replaceVariableLocationOp(OriginalStorage, Storage); + DVI->setExpression(Expr); if (auto *InsertPt = dyn_cast_or_null(Storage)) - DDI->moveAfter(InsertPt); + if (isa(DVI)) + DVI->moveAfter(InsertPt); } void coro::buildCoroutineFrame(Function &F, Shape &Shape) { @@ -2292,10 +2326,20 @@ LLVM_DEBUG(dumpAllocas(FrameData.Allocas)); // Collect the spills for arguments and other not-materializable values. - for (Argument &A : F.args()) + for (Argument &A : F.args()) { for (User *U : A.users()) if (Checker.isDefinitionAcrossSuspend(A, U)) FrameData.Spills[&A].push_back(cast(U)); + if (EnhanceDebugability) { + SmallVector DVIs; + findDbgValues(DVIs, &A); + for (auto *DVI : DVIs) + if (Checker.isDefinitionAcrossSuspend(A, DVI)) { + llvm::dbgs() << "Find DVI: " << *DVI << " for " << A << "\n"; + FrameData.Spills[&A].push_back(DVI); + } + } + } for (Instruction &I : instructions(F)) { // Values returned from coroutine structure intrinsics should not be part @@ -2338,6 +2382,16 @@ if (isa(I)) continue; + if (EnhanceDebugability) { + SmallVector DVIs; + findDbgValues(DVIs, &I); + for (auto *DVI : DVIs) + if (Checker.isDefinitionAcrossSuspend(I, DVI)) { + llvm::dbgs() << "Find DVI: " << *DVI << " for " << I << "\n"; + FrameData.Spills[&I].push_back(DVI); + } + } + for (User *U : I.users()) if (Checker.isDefinitionAcrossSuspend(I, U)) { // We cannot spill a token. Index: llvm/lib/Transforms/Coroutines/CoroInternal.h =================================================================== --- llvm/lib/Transforms/Coroutines/CoroInternal.h +++ llvm/lib/Transforms/Coroutines/CoroInternal.h @@ -54,7 +54,7 @@ /// holding a pointer to the coroutine frame. void salvageDebugInfo( SmallDenseMap &DbgPtrAllocaCache, - DbgDeclareInst *DDI); + DbgVariableIntrinsic *DDI); // Keeps data and helper functions for lowering coroutine intrinsics. struct LowererBase { Index: llvm/lib/Transforms/Coroutines/CoroSplit.cpp =================================================================== --- llvm/lib/Transforms/Coroutines/CoroSplit.cpp +++ 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); + if (auto *DVI = dyn_cast(&I)) + Worklist.push_back(DVI); + for (DbgVariableIntrinsic *DVI : Worklist) + coro::salvageDebugInfo(DbgPtrAllocaCache, DVI); // 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(); } } } Index: llvm/test/Transforms/Coroutines/coro-debug-dbg.values-O2.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Coroutines/coro-debug-dbg.values-O2.ll @@ -0,0 +1,219 @@ +; Tests whether resume function would remain dbg.value infomation. +; RUN: opt < %s -enhance-debug-with-coroutine=1 -coro-early -coro-split -coro-split -S | FileCheck %s +; +; This file is based on coro-debug-frame-variable.ll. +; CHECK-LABEL: define internal fastcc void @f.resume({{.*}}) { +; CHECK: entry.resume: +; CHECK: call void @llvm.dbg.value(metadata %f.Frame** %FramePtr{{.*}}, metadata ![[IVAR_RESUME:[0-9]+]], metadata !DIExpression( +; CHECK: call void @llvm.dbg.value(metadata %f.Frame** %FramePtr{{.*}}, metadata ![[JVAR_RESUME:[0-9]+]], metadata !DIExpression( +; +; 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(i64 %s.coerce0) { +entry: + %__promise = alloca i8, align 8 + %x = alloca [10 x i32], align 16 + %s.sroa.0.0.extract.trunc = trunc i64 %s.coerce0 to i32 + %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 %s.sroa.0.0.extract.trunc, metadata !6, metadata !DIExpression()), !dbg !11 + call void @llvm.dbg.value(metadata i32 %s.sroa.0.0.extract.trunc, 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)