[llvm] [debuginfo][coro] Emit debug info labels for coroutine resume points (PR #141937)

via llvm-commits llvm-commits at lists.llvm.org
Thu May 29 10:08:05 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-coroutines

Author: Adrian Vogelsgesang (vogelsgesang)

<details>
<summary>Changes</summary>

With this commit, we add `DILabel` debug infos (lowering to DW_TAG_label in DWARF) to the resume points of a coroutine. Those labels use the naming convention `__coro_resume_<N>` where `<N>` is the suspension point id.

That way, debugging scripts can figure out the exact line / column at which a coroutine was suspended by looking up current `__coro_index` value inside the coroutines frame, and then searching for the corresponding `__coro_resume_<N>` inside the coroutine's resume function.

While this is an artificial compiler-generated label, I did not apply the DW_AT_artificial tag to it. The DWARFv5 standard only allows that tag on type and variable definitions, but not on labels.

In gdb, those line numebers can then be looked up using the command `info line -function my_coroutine -label __coro_resume_1`.

LLDB unfortunately does not parse DW_TAG_label debug information, yet. As such, this debug information is currently not useful in LLDB.

Corresponding RFC in https://discourse.llvm.org/t/rfc-debug-info-for-coroutine-suspension-locations-take-2/86606

---
Full diff: https://github.com/llvm/llvm-project/pull/141937.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Coroutines/CoroFrame.cpp (+2-3) 
- (modified) llvm/lib/Transforms/Coroutines/CoroSplit.cpp (+22-1) 


``````````diff
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 2f5f1089067bf..7fe19ca0dd02a 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -689,9 +689,8 @@ static DIType *solveDIType(DIBuilder &Builder, Type *Ty,
 static void buildFrameDebugInfo(Function &F, coro::Shape &Shape,
                                 FrameDataInfo &FrameData) {
   DISubprogram *DIS = F.getSubprogram();
-  // If there is no DISubprogram for F, it implies the Function are not compiled
-  // with debug info. So we also don't need to generate debug info for the frame
-  // neither.
+  // If there is no DISubprogram for F, it implies the function is compiled without
+  // debug info. So we also don't generate debug info for the frame, either.
   if (!DIS || !DIS->getUnit() ||
       !dwarf::isCPlusPlus(
           (dwarf::SourceLanguage)DIS->getUnit()->getSourceLanguage()) ||
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index f9a6c70fedc2d..d84886cd1ca4e 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -42,6 +42,7 @@
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/CallingConv.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DerivedTypes.h"
@@ -302,7 +303,7 @@ static void replaceFallthroughCoroEnd(AnyCoroEndInst *End,
 }
 
 // Mark a coroutine as done, which implies that the coroutine is finished and
-// never get resumed.
+// never gets resumed.
 //
 // In resume-switched ABI, the done state is represented by storing zero in
 // ResumeFnAddr.
@@ -1492,6 +1493,14 @@ struct SwitchCoroutineSplitter {
   static void createResumeEntryBlock(Function &F, coro::Shape &Shape) {
     LLVMContext &C = F.getContext();
 
+    DIBuilder DBuilder(*F.getParent(), /*AllowUnresolved*/ false);
+    DISubprogram *DIS = F.getSubprogram();
+    // If there is no DISubprogram for F, it implies the function is compiled without
+    // debug info. So we also don't generate debug info for the suspension points, either.
+    bool AddDebugLabels = (DIS && DIS->getUnit() &&
+        (DIS->getUnit()->getEmissionKind() == DICompileUnit::DebugEmissionKind::FullDebug ||
+         DIS->getUnit()->getEmissionKind() == DICompileUnit::DebugEmissionKind::LineTablesOnly));
+
     // resume.entry:
     //  %index.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32
     //  0, i32 2 % index = load i32, i32* %index.addr switch i32 %index, label
@@ -1514,6 +1523,7 @@ struct SwitchCoroutineSplitter {
         Builder.CreateSwitch(Index, UnreachBB, Shape.CoroSuspends.size());
     Shape.SwitchLowering.ResumeSwitch = Switch;
 
+    // Split all coro.suspend calls
     size_t SuspendIndex = 0;
     for (auto *AnyS : Shape.CoroSuspends) {
       auto *S = cast<CoroSuspendInst>(AnyS);
@@ -1552,6 +1562,7 @@ struct SwitchCoroutineSplitter {
       //     br label %resume.0.landing
       //
       //  resume.0: ; <--- jump from the switch in the resume.entry
+      //     XXX: label
       //     %0 = tail call i8 @llvm.coro.suspend(token none, i1 false)
       //     br label %resume.0.landing
       //
@@ -1574,11 +1585,21 @@ struct SwitchCoroutineSplitter {
       PN->addIncoming(Builder.getInt8(-1), SuspendBB);
       PN->addIncoming(S, ResumeBB);
 
+      if (AddDebugLabels) {
+         if (DebugLoc SuspendLoc = S->getDebugLoc()) {
+            std::string labelName = ("__coro_resume_" + Twine(SuspendIndex)).str();
+            DILocation& DILoc = *SuspendLoc.get();
+            DILabel *ResumeLabel = DBuilder.createLabel(DIS, labelName, DILoc.getFile(), SuspendLoc.getLine());
+            DBuilder.insertLabel(ResumeLabel, &DILoc, ResumeBB->begin());
+         }
+      }
+
       ++SuspendIndex;
     }
 
     Builder.SetInsertPoint(UnreachBB);
     Builder.CreateUnreachable();
+    DBuilder.finalize();
 
     Shape.SwitchLowering.ResumeEntryBlock = NewEntry;
   }

``````````

</details>


https://github.com/llvm/llvm-project/pull/141937


More information about the llvm-commits mailing list