[llvm] c1359ef - [Coroutines] Salvege Debug.values

Chuanqi Xu via llvm-commits llvm-commits at lists.llvm.org
Wed May 12 22:07:18 PDT 2021


Author: Chuanqi Xu
Date: 2021-05-13T13:06:33+08:00
New Revision: c1359ef07e8240b4350802b4998aedc1239a91d0

URL: https://github.com/llvm/llvm-project/commit/c1359ef07e8240b4350802b4998aedc1239a91d0
DIFF: https://github.com/llvm/llvm-project/commit/c1359ef07e8240b4350802b4998aedc1239a91d0.diff

LOG: [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

Added: 
    llvm/test/Transforms/Coroutines/coro-debug-dbg.values-not_used_in_frame.ll
    llvm/test/Transforms/Coroutines/coro-debug-dbg.values.ll

Modified: 
    llvm/lib/Transforms/Coroutines/CoroFrame.cpp
    llvm/lib/Transforms/Coroutines/CoroInternal.h
    llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index ff12a3f36ce1..3230c790859b 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -162,6 +162,16 @@ struct SuspendCrossingInfo {
 
     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
 
@@ -1655,8 +1665,8 @@ static Instruction *insertSpills(const FrameDataInfo &FrameData,
   }
 
   // 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 @@ static Instruction *insertSpills(const FrameDataInfo &FrameData,
     auto *G = GetFramePointer(Alloca);
     G->setName(Alloca->getName() + Twine(".reload.addr"));
 
-    TinyPtrVector<DbgDeclareInst *> 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<DbgVariableIntrinsic *, 4> DIs;
+    findDbgUsers(DIs, Alloca);
+    for (auto *DVI : DIs)
+      DVI->replaceUsesOfWith(Alloca, G);
 
     for (Instruction *I : UsersToUpdate)
       I->replaceUsesOfWith(Alloca, G);
@@ -2435,18 +2439,18 @@ static void collectFrameAllocas(Function &F, coro::Shape &Shape,
 
 void coro::salvageDebugInfo(
     SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &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<IntrinsicInst>(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<LoadInst>(Storage)) {
@@ -2502,12 +2506,16 @@ void coro::salvageDebugInfo(
       if (Expr && Expr->isComplex())
         Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore);
     }
-  DDI->replaceVariableLocationOp(OriginalStorage, Storage);
-  DDI->setExpression(Expr);
-  if (auto *InsertPt = dyn_cast<Instruction>(Storage))
-    DDI->moveAfter(InsertPt);
-  else if (isa<Argument>(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<DbgValueInst>(DVI)) {
+    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) {
@@ -2567,11 +2575,19 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
     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<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())
         break;
 
@@ -2644,6 +2660,21 @@ void coro::buildCoroutineFrame(Function &F, Shape &Shape) {
         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));
   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
index 26334966a17c..27ba8524f975 100644
--- a/llvm/lib/Transforms/Coroutines/CoroInternal.h
+++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h
@@ -54,7 +54,7 @@ void updateCallGraph(Function &Caller, ArrayRef<Function *> Funcs,
 /// holding a pointer to the coroutine frame.
 void salvageDebugInfo(
     SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &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
index 7a6cbdab60c7..4efebfd809cb 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -647,32 +647,32 @@ void CoroCloner::replaceSwiftErrorOps() {
 }
 
 void CoroCloner::salvageDebugInfo() {
-  SmallVector<DbgDeclareInst *, 8> Worklist;
+  SmallVector<DbgVariableIntrinsic *, 8> Worklist;
   SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> DbgPtrAllocaCache;
   for (auto &BB : *NewF)
     for (auto &I : BB)
-      if (auto *DDI = dyn_cast<DbgDeclareInst>(&I))
-        Worklist.push_back(DDI);
-  for (DbgDeclareInst *DDI : Worklist)
-    coro::salvageDebugInfo(DbgPtrAllocaCache, DDI, Shape.ReuseFrameSlot);
+      if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&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<AllocaInst>(DDI->getAddress())) {
+  for (DbgVariableIntrinsic *DVI : Worklist) {
+    if (IsUnreachableBlock(DVI->getParent()))
+      DVI->eraseFromParent();
+    else if (dyn_cast_or_null<AllocaInst>(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<Instruction>(User))
           if (!isa<AllocaInst>(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
index 000000000000..93bac9e7f209
--- /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
index 000000000000..04a75a31dcdb
--- /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)


        


More information about the llvm-commits mailing list