Skip to content

Commit de3c841

Browse files
authored
[debuginfo][coro] Emit debug info labels for coroutine resume points (#141937)
RFC on discourse: https://discourse.llvm.org/t/rfc-debug-info-for-coroutine-suspension-locations-take-2/86606 With this commit, we add `DILabel` debug infos to the resume points of a coroutine. Those labels can be used by debugging scripts to figure out the exact line and column at which a coroutine was suspended by looking up current `__coro_index` value inside the coroutines frame, and then searching for the corresponding label inside the coroutine's resume function. The DWARF information generated for such a label looks like: ``` 0x00000f71: DW_TAG_label DW_AT_name ("__coro_resume_1") DW_AT_decl_file ("generator-example.cpp") DW_AT_decl_line (5) DW_AT_decl_column (3) DW_AT_artificial (true) DW_AT_LLVM_coro_suspend_idx (0x01) DW_AT_low_pc (0x00000000000019be) ``` The labels can be mapped to their corresponding `__coro_idx` values either via their naming convention `__coro_resume_<N>` or using the new `DW_AT_LLVM_coro_suspend_idx` attribute. In gdb, those line numebrs can be looked up using `info line -function my_coroutine -label __coro_resume_1`. LLDB unfortunately does not understand DW_TAG_label debug information, yet. Given this is an artificial compiler-generated label, I did apply the DW_AT_artificial tag to it. The DWARFv5 standard only allows that tag on type and variable definitions, but this is a natural extension and was also blessed in the RFC on discourse. Also, this commit adds `DW_AT_decl_column` to labels, not only for coroutines but also for normal C and C++ labels. While not strictly necessary, I am doing so now because it would be harder to do so later without breaking the binary LLVM-IR format Drive-by fixes: While reading the existing test cases to understand how to write my own test case, I did a couple of small typo fixes and comment improvements
1 parent 2fcf1f8 commit de3c841

29 files changed

+389
-85
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
...

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5316,8 +5316,9 @@ void CGDebugInfo::EmitLabel(const LabelDecl *D, CGBuilderTy &Builder) {
53165316
StringRef Name = D->getName();
53175317

53185318
// Create the descriptor for the label.
5319-
auto *L =
5320-
DBuilder.createLabel(Scope, Name, Unit, Line, CGM.getLangOpts().Optimize);
5319+
auto *L = DBuilder.createLabel(
5320+
Scope, Name, Unit, Line, Column, /*IsArtificial=*/false,
5321+
/*CoroSuspendIdx=*/std::nullopt, CGM.getLangOpts().Optimize);
53215322

53225323
// Insert an llvm.dbg.label into the current block.
53235324
DBuilder.insertLabel(L,

clang/test/CodeGen/debug-label-inline.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ int f2(void) {
2323

2424
// CHECK: distinct !DISubprogram(name: "f1", {{.*}}, retainedNodes: [[ELEMENTS:!.*]])
2525
// CHECK: [[ELEMENTS]] = !{{{.*}}, [[LABEL_METADATA]]}
26-
// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 8)
26+
// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 8, column: 1)
2727
// CHECK: [[INLINEDAT:!.*]] = distinct !DILocation(line: 18,
2828
// CHECK: [[LABEL_LOCATION]] = !DILocation(line: 8, {{.*}}, inlinedAt: [[INLINEDAT]])

clang/test/CodeGen/debug-label.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ int f1(int a, int b) {
1212
return sum;
1313
}
1414

15-
// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 9)
15+
// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 9, column: 1)
1616
// CHECK: [[LABEL_LOCATION]] = !DILocation(line: 9,

llvm/docs/LangRef.rst

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6988,16 +6988,25 @@ appear in the included source file.
69886988
DILabel
69896989
"""""""
69906990

6991-
``DILabel`` nodes represent labels within a :ref:`DISubprogram`. All fields of
6992-
a ``DILabel`` are mandatory. The ``scope:`` field must be one of either a
6993-
:ref:`DILexicalBlockFile`, a :ref:`DILexicalBlock`, or a :ref:`DISubprogram`.
6994-
The ``name:`` field is the label identifier. The ``file:`` field is the
6995-
:ref:`DIFile` the label is present in. The ``line:`` field is the source line
6991+
``DILabel`` nodes represent labels within a :ref:`DISubprogram`. The ``scope:``
6992+
field must be one of either a :ref:`DILexicalBlockFile`, a
6993+
:ref:`DILexicalBlock`, or a :ref:`DISubprogram`. The ``name:`` field is the
6994+
label identifier. The ``file:`` field is the :ref:`DIFile` the label is
6995+
present in. The ``line:`` and ``column:`` field are the source line and column
69966996
within the file where the label is declared.
69976997

6998+
Furthermore, a label can be marked as artificial, i.e. compiler-generated,
6999+
using ``isArtificial:``. Such artificial labels are generated, e.g., by
7000+
the ``CoroSplit`` pass. In addition, the ``CoroSplit`` pass also uses the
7001+
``coroSuspendIdx:`` field to identify the coroutine suspend points.
7002+
7003+
``scope:``, ``name:``, ``file:`` and ``line:`` are mandatory. The remaining
7004+
fields are optional.
7005+
69987006
.. code-block:: text
69997007

7000-
!2 = !DILabel(scope: !0, name: "foo", file: !1, line: 7)
7008+
!2 = !DILabel(scope: !0, name: "foo", file: !1, line: 7, column: 4)
7009+
!3 = !DILabel(scope: !0, name: "__coro_resume_3", file: !1, line: 9, column: 3, isArtificial: true, coroSuspendIdx: 3)
70017010

70027011
DICommonBlock
70037012
"""""""""""""

llvm/include/llvm/BinaryFormat/Dwarf.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ HANDLE_DW_AT(0x3e09, LLVM_ptrauth_authenticates_null_values, 0, LLVM)
624624
HANDLE_DW_AT(0x3e0a, LLVM_ptrauth_authentication_mode, 0, LLVM)
625625
HANDLE_DW_AT(0x3e0b, LLVM_num_extra_inhabitants, 0, LLVM)
626626
HANDLE_DW_AT(0x3e0c, LLVM_stmt_sequence, 0, LLVM)
627+
HANDLE_DW_AT(0x3e0d, LLVM_coro_suspend_idx, 0, LLVM)
627628

628629
// Apple extensions.
629630

llvm/include/llvm/IR/DIBuilder.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,10 @@ namespace llvm {
901901
/// \c Scope must be a \a DILocalScope, and thus its scope chain eventually
902902
/// leads to a \a DISubprogram.
903903
LLVM_ABI DILabel *createLabel(DIScope *Scope, StringRef Name, DIFile *File,
904-
unsigned LineNo, bool AlwaysPreserve = false);
904+
unsigned LineNo, unsigned Column,
905+
bool IsArtificial,
906+
std::optional<unsigned> CoroSuspendIdx,
907+
bool AlwaysPreserve = false);
905908

906909
/// Create a new descriptor for a parameter variable.
907910
///

llvm/include/llvm/IR/DebugInfoMetadata.h

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4112,35 +4112,49 @@ class DILabel : public DINode {
41124112
friend class LLVMContextImpl;
41134113
friend class MDNode;
41144114

4115-
DILabel(LLVMContext &C, StorageType Storage, unsigned Line,
4115+
unsigned Column;
4116+
std::optional<unsigned> CoroSuspendIdx;
4117+
bool IsArtificial;
4118+
4119+
DILabel(LLVMContext &C, StorageType Storage, unsigned Line, unsigned Column,
4120+
bool IsArtificial, std::optional<unsigned> CoroSuspendIdx,
41164121
ArrayRef<Metadata *> Ops);
41174122
~DILabel() = default;
41184123

41194124
static DILabel *getImpl(LLVMContext &Context, DIScope *Scope, StringRef Name,
4120-
DIFile *File, unsigned Line, StorageType Storage,
4121-
bool ShouldCreate = true) {
4125+
DIFile *File, unsigned Line, unsigned Column,
4126+
bool IsArtificial,
4127+
std::optional<unsigned> CoroSuspendIdx,
4128+
StorageType Storage, bool ShouldCreate = true) {
41224129
return getImpl(Context, Scope, getCanonicalMDString(Context, Name), File,
4123-
Line, Storage, ShouldCreate);
4130+
Line, Column, IsArtificial, CoroSuspendIdx, Storage,
4131+
ShouldCreate);
41244132
}
4125-
LLVM_ABI static DILabel *getImpl(LLVMContext &Context, Metadata *Scope,
4126-
MDString *Name, Metadata *File,
4127-
unsigned Line, StorageType Storage,
4128-
bool ShouldCreate = true);
4133+
LLVM_ABI static DILabel *
4134+
getImpl(LLVMContext &Context, Metadata *Scope, MDString *Name, Metadata *File,
4135+
unsigned Line, unsigned Column, bool IsArtificial,
4136+
std::optional<unsigned> CoroSuspendIdx, StorageType Storage,
4137+
bool ShouldCreate = true);
41294138

41304139
TempDILabel cloneImpl() const {
41314140
return getTemporary(getContext(), getScope(), getName(), getFile(),
4132-
getLine());
4141+
getLine(), getColumn(), isArtificial(),
4142+
getCoroSuspendIdx());
41334143
}
41344144

41354145
public:
41364146
DEFINE_MDNODE_GET(DILabel,
41374147
(DILocalScope * Scope, StringRef Name, DIFile *File,
4138-
unsigned Line),
4139-
(Scope, Name, File, Line))
4148+
unsigned Line, unsigned Column, bool IsArtificial,
4149+
std::optional<unsigned> CoroSuspendIdx),
4150+
(Scope, Name, File, Line, Column, IsArtificial,
4151+
CoroSuspendIdx))
41404152
DEFINE_MDNODE_GET(DILabel,
41414153
(Metadata * Scope, MDString *Name, Metadata *File,
4142-
unsigned Line),
4143-
(Scope, Name, File, Line))
4154+
unsigned Line, unsigned Column, bool IsArtificial,
4155+
std::optional<unsigned> CoroSuspendIdx),
4156+
(Scope, Name, File, Line, Column, IsArtificial,
4157+
CoroSuspendIdx))
41444158

41454159
TempDILabel clone() const { return cloneImpl(); }
41464160

@@ -4151,8 +4165,11 @@ class DILabel : public DINode {
41514165
return cast_or_null<DILocalScope>(getRawScope());
41524166
}
41534167
unsigned getLine() const { return SubclassData32; }
4168+
unsigned getColumn() const { return Column; }
41544169
StringRef getName() const { return getStringOperand(1); }
41554170
DIFile *getFile() const { return cast_or_null<DIFile>(getRawFile()); }
4171+
bool isArtificial() const { return IsArtificial; }
4172+
std::optional<unsigned> getCoroSuspendIdx() const { return CoroSuspendIdx; }
41564173

41574174
Metadata *getRawScope() const { return getOperand(0); }
41584175
MDString *getRawName() const { return getOperandAs<MDString>(1); }

llvm/lib/AsmParser/LLParser.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6115,18 +6115,26 @@ bool LLParser::parseDILocalVariable(MDNode *&Result, bool IsDistinct) {
61156115
}
61166116

61176117
/// parseDILabel:
6118-
/// ::= !DILabel(scope: !0, name: "foo", file: !1, line: 7)
6118+
/// ::= !DILabel(scope: !0, name: "foo", file: !1, line: 7, column: 4)
61196119
bool LLParser::parseDILabel(MDNode *&Result, bool IsDistinct) {
61206120
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
61216121
REQUIRED(scope, MDField, (/* AllowNull */ false)); \
61226122
REQUIRED(name, MDStringField, ); \
61236123
REQUIRED(file, MDField, ); \
6124-
REQUIRED(line, LineField, );
6124+
REQUIRED(line, LineField, ); \
6125+
OPTIONAL(column, ColumnField, ); \
6126+
OPTIONAL(isArtificial, MDBoolField, ); \
6127+
OPTIONAL(coroSuspendIdx, MDUnsignedField, );
61256128
PARSE_MD_FIELDS();
61266129
#undef VISIT_MD_FIELDS
61276130

6131+
std::optional<unsigned> CoroSuspendIdx =
6132+
coroSuspendIdx.Seen ? std::optional<unsigned>(coroSuspendIdx.Val)
6133+
: std::nullopt;
6134+
61286135
Result = GET_OR_DISTINCT(DILabel,
6129-
(Context, scope.Val, name.Val, file.Val, line.Val));
6136+
(Context, scope.Val, name.Val, file.Val, line.Val,
6137+
column.Val, isArtificial.Val, CoroSuspendIdx));
61306138
return false;
61316139
}
61326140

llvm/lib/Bitcode/Reader/MetadataLoader.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,14 +2242,28 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
22422242
break;
22432243
}
22442244
case bitc::METADATA_LABEL: {
2245-
if (Record.size() != 5)
2245+
if (Record.size() < 5 || Record.size() > 7)
22462246
return error("Invalid record");
22472247

22482248
IsDistinct = Record[0] & 1;
2249+
uint64_t Line = Record[4];
2250+
uint64_t Column = Record.size() > 5 ? Record[5] : 0;
2251+
bool IsArtificial = Record[0] & 2;
2252+
std::optional<unsigned> CoroSuspendIdx;
2253+
if (Record.size() > 6) {
2254+
uint64_t RawSuspendIdx = Record[6];
2255+
if (RawSuspendIdx != std::numeric_limits<uint64_t>::max()) {
2256+
if (RawSuspendIdx > (uint64_t)std::numeric_limits<unsigned>::max())
2257+
return error("CoroSuspendIdx value is too large");
2258+
CoroSuspendIdx = RawSuspendIdx;
2259+
}
2260+
}
2261+
22492262
MetadataList.assignValue(
2250-
GET_OR_DISTINCT(DILabel, (Context, getMDOrNull(Record[1]),
2251-
getMDString(Record[2]),
2252-
getMDOrNull(Record[3]), Record[4])),
2263+
GET_OR_DISTINCT(DILabel,
2264+
(Context, getMDOrNull(Record[1]),
2265+
getMDString(Record[2]), getMDOrNull(Record[3]), Line,
2266+
Column, IsArtificial, CoroSuspendIdx)),
22532267
NextMetadataNo);
22542268
NextMetadataNo++;
22552269
break;

0 commit comments

Comments
 (0)