Skip to content

Commit 185be04

Browse files
committed
[debuginfo][coro] Emit debug info labels for coroutine resume points
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.
1 parent b8f1228 commit 185be04

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

clang/docs/DebuggingCoroutines.rst

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,24 @@ important. This member identifies the suspension point at which the coroutine
209209
is currently suspended.
210210

211211
However, it is non-trivial to map this number back to a source code location.
212-
In simple cases, one might correctly guess the source code location. In more
213-
complex cases, we can modify the C++ code to store additional information in
214-
the promise type:
212+
The compiler emits debug info labels for the suspension points. This allows us
213+
to map the suspension point index back to a source code location. In gdb, we
214+
can use the ``info line`` command to get the source code location of the
215+
suspension point.
216+
217+
::
218+
219+
(gdb) info line -function coro_task -label __coro_resume_2
220+
Line 45 of "llvm-example.cpp" starts at address 0x1b1b <_ZL9coro_taski.resume+555> and ends at 0x1b46 <_ZL9coro_taski.resume+598>.
221+
Line 45 of "llvm-example.cpp" starts at address 0x201b <_ZL9coro_taski.destroy+555> and ends at 0x2046 <_ZL9coro_taski.destroy+598>.
222+
Line 45 of "llvm-example.cpp" starts at address 0x253b <_ZL9coro_taski.cleanup+555> and ends at 0x2566 <_ZL9coro_taski.cleanup+598>.
223+
224+
LLDB does not support looking up labels. Furthmore, those labels are only emitted
225+
starting with clang 21.0.
226+
227+
For simple cases, you might still be able to guess the suspension point correctly.
228+
Alternatively, you might also want to modify your coroutine library to store
229+
the line number of the current suspension point in the promise:
215230

216231
.. code-block:: c++
217232

@@ -221,8 +236,6 @@ the promise type:
221236
void* _coro_return_address = nullptr;
222237
};
223238

224-
#include <source_location>
225-
226239
// For all the awaiter types we need:
227240
class awaiter {
228241
...

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -688,9 +688,9 @@ static DIType *solveDIType(DIBuilder &Builder, Type *Ty,
688688
static void buildFrameDebugInfo(Function &F, coro::Shape &Shape,
689689
FrameDataInfo &FrameData) {
690690
DISubprogram *DIS = F.getSubprogram();
691-
// If there is no DISubprogram for F, it implies the Function are not compiled
692-
// with debug info. So we also don't need to generate debug info for the frame
693-
// neither.
691+
// If there is no DISubprogram for F, it implies the function is compiled
692+
// without debug info. So we also don't generate debug info for the frame,
693+
// either.
694694
if (!DIS || !DIS->getUnit() ||
695695
!dwarf::isCPlusPlus(
696696
(dwarf::SourceLanguage)DIS->getUnit()->getSourceLanguage()) ||

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "llvm/IR/CFG.h"
4343
#include "llvm/IR/CallingConv.h"
4444
#include "llvm/IR/Constants.h"
45+
#include "llvm/IR/DIBuilder.h"
4546
#include "llvm/IR/DataLayout.h"
4647
#include "llvm/IR/DebugInfo.h"
4748
#include "llvm/IR/DerivedTypes.h"
@@ -302,7 +303,7 @@ static void replaceFallthroughCoroEnd(AnyCoroEndInst *End,
302303
}
303304

304305
// Mark a coroutine as done, which implies that the coroutine is finished and
305-
// never get resumed.
306+
// never gets resumed.
306307
//
307308
// In resume-switched ABI, the done state is represented by storing zero in
308309
// ResumeFnAddr.
@@ -1478,6 +1479,18 @@ struct SwitchCoroutineSplitter {
14781479
static void createResumeEntryBlock(Function &F, coro::Shape &Shape) {
14791480
LLVMContext &C = F.getContext();
14801481

1482+
DIBuilder DBuilder(*F.getParent(), /*AllowUnresolved*/ false);
1483+
DISubprogram *DIS = F.getSubprogram();
1484+
// If there is no DISubprogram for F, it implies the function is compiled
1485+
// without debug info. So we also don't generate debug info for the
1486+
// suspension points, either.
1487+
bool AddDebugLabels =
1488+
(DIS && DIS->getUnit() &&
1489+
(DIS->getUnit()->getEmissionKind() ==
1490+
DICompileUnit::DebugEmissionKind::FullDebug ||
1491+
DIS->getUnit()->getEmissionKind() ==
1492+
DICompileUnit::DebugEmissionKind::LineTablesOnly));
1493+
14811494
// resume.entry:
14821495
// %index.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32
14831496
// 0, i32 2 % index = load i32, i32* %index.addr switch i32 %index, label
@@ -1500,6 +1513,7 @@ struct SwitchCoroutineSplitter {
15001513
Builder.CreateSwitch(Index, UnreachBB, Shape.CoroSuspends.size());
15011514
Shape.SwitchLowering.ResumeSwitch = Switch;
15021515

1516+
// Split all coro.suspend calls
15031517
size_t SuspendIndex = 0;
15041518
for (auto *AnyS : Shape.CoroSuspends) {
15051519
auto *S = cast<CoroSuspendInst>(AnyS);
@@ -1538,6 +1552,7 @@ struct SwitchCoroutineSplitter {
15381552
// br label %resume.0.landing
15391553
//
15401554
// resume.0: ; <--- jump from the switch in the resume.entry
1555+
// XXX: label
15411556
// %0 = tail call i8 @llvm.coro.suspend(token none, i1 false)
15421557
// br label %resume.0.landing
15431558
//
@@ -1560,11 +1575,23 @@ struct SwitchCoroutineSplitter {
15601575
PN->addIncoming(Builder.getInt8(-1), SuspendBB);
15611576
PN->addIncoming(S, ResumeBB);
15621577

1578+
if (AddDebugLabels) {
1579+
if (DebugLoc SuspendLoc = S->getDebugLoc()) {
1580+
std::string LabelName =
1581+
("__coro_resume_" + Twine(SuspendIndex)).str();
1582+
DILocation& DILoc = *SuspendLoc.get();
1583+
DILabel *ResumeLabel = DBuilder.createLabel(
1584+
DIS, LabelName, DILoc.getFile(), SuspendLoc.getLine());
1585+
DBuilder.insertLabel(ResumeLabel, &DILoc, ResumeBB->begin());
1586+
}
1587+
}
1588+
15631589
++SuspendIndex;
15641590
}
15651591

15661592
Builder.SetInsertPoint(UnreachBB);
15671593
Builder.CreateUnreachable();
1594+
DBuilder.finalize();
15681595

15691596
Shape.SwitchLowering.ResumeEntryBlock = NewEntry;
15701597
}

0 commit comments

Comments
 (0)