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
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions clang/docs/DebuggingCoroutines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -311,17 +311,25 @@ variables in the C++ source.
Get the suspended points
========================

An important requirement for debugging coroutines is to understand suspended
points, which are where the coroutine is currently suspended and awaiting.
An important requirement for debugging coroutines is to understand at which
point a coroutine is currently suspended and awaiting.

For simple cases like the above, inspecting the value of the `__coro_index`
variable in the coroutine frame works well.
The value of the `__coro_index` inside a couroutine frame indicates the current
suspension point. To map this id back to a source code location, you can lookup
the source location of a special, compiler-generated label `__coro_suspend_<N>`.

However, it is not quite so simple in really complex situations. In these
cases, it is necessary to use the coroutine libraries to insert the
line-number.
::

For example:
(gdb) info line -function coro_task -label __coro_resume_2
Line 45 of "llvm-example.cpp" starts at address 0x1b1b <_ZL9coro_taski.resume+555> and ends at 0x1b46 <_ZL9coro_taski.resume+598>.
Line 45 of "llvm-example.cpp" starts at address 0x201b <_ZL9coro_taski.destroy+555> and ends at 0x2046 <_ZL9coro_taski.destroy+598>.
Line 45 of "llvm-example.cpp" starts at address 0x253b <_ZL9coro_taski.cleanup+555> and ends at 0x2566 <_ZL9coro_taski.cleanup+598>.

Older versions of LLVM/clang might not yet emit those labels, though. For
simple cases, you might still be able to guess the suspension point correctly.

Alternatively, you might also want to modify your coroutine library to store
the line number of the current suspension point in the promise:

.. code-block:: c++

Expand Down
6 changes: 3 additions & 3 deletions llvm/lib/Transforms/Coroutines/CoroFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,9 +689,9 @@ 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()) ||
Expand Down
29 changes: 28 additions & 1 deletion llvm/lib/Transforms/Coroutines/CoroSplit.cpp
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

Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1492,6 +1493,18 @@ 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
Expand All @@ -1514,6 +1527,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);
Expand Down Expand Up @@ -1552,6 +1566,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
//
Expand All @@ -1574,11 +1589,23 @@ 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;
}
Expand Down
Loading