Skip to content

[debuginfo][coro] Emit debug info labels for coroutine resume points #141937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

vogelsgesang
Copy link
Member

@vogelsgesang vogelsgesang commented May 29, 2025

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

Copy link

github-actions bot commented May 29, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp -- llvm/lib/Transforms/Coroutines/CoroFrame.cpp llvm/lib/Transforms/Coroutines/CoroSplit.cpp
View the diff from clang-format here.
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index ae5f84dab..f46ee9f47 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -1593,7 +1593,7 @@ private:
         if (DebugLoc SuspendLoc = S->getDebugLoc()) {
           std::string LabelName =
               ("__coro_resume_" + Twine(SuspendIndex)).str();
-          DILocation& DILoc = *SuspendLoc.get();
+          DILocation &DILoc = *SuspendLoc.get();
           DILabel *ResumeLabel = DBuilder.createLabel(
               DIS, LabelName, DILoc.getFile(), SuspendLoc.getLine());
           DBuilder.insertLabel(ResumeLabel, &DILoc, ResumeBB->begin());

@vogelsgesang vogelsgesang marked this pull request as ready for review May 29, 2025 17:07
@llvmbot
Copy link
Member

llvmbot commented May 29, 2025

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-debuginfo
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-coroutines

Author: Adrian Vogelsgesang (vogelsgesang)

Changes

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_&lt;N&gt; where &lt;N&gt; 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_&lt;N&gt; 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 --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;
   }

With this commit, we add `DILabel` debug infos (lowering to DW_TAG_label
in DWARF) to the various 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.
@vogelsgesang vogelsgesang force-pushed the coro-suspend-labels-2 branch from 8629fc6 to 8eef12d Compare May 29, 2025 19:41
@llvmbot llvmbot added the clang Clang issues not falling into any other category label May 29, 2025
Copy link
Member Author

@vogelsgesang vogelsgesang May 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add test cases before merging this, if the general approach gets accepted in the RFC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category coroutines C++20 coroutines debuginfo llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants