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 11 commits into
base: main
Choose a base branch
from
23 changes: 18 additions & 5 deletions clang/docs/DebuggingCoroutines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,24 @@ important. This member identifies the suspension point at which the coroutine
is currently suspended.

However, it is non-trivial to map this number back to a source code location.
In simple cases, one might correctly guess the source code location. In more
complex cases, we can modify the C++ code to store additional information in
the promise type:
The compiler emits debug info labels for the suspension points. This allows us
to map the suspension point index back to a source code location. In gdb, we
can use the ``info line`` command to get the source code location of the
suspension point.

::

(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>.

LLDB does not support looking up labels. Furthmore, those labels are only emitted
starting with clang 21.0.

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 All @@ -221,8 +236,6 @@ the promise type:
void* _coro_return_address = nullptr;
};

#include <source_location>

// For all the awaiter types we need:
class awaiter {
...
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5316,8 +5316,9 @@ void CGDebugInfo::EmitLabel(const LabelDecl *D, CGBuilderTy &Builder) {
StringRef Name = D->getName();

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

// Insert an llvm.dbg.label into the current block.
DBuilder.insertLabel(L,
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/debug-label-inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ int f2(void) {

// CHECK: distinct !DISubprogram(name: "f1", {{.*}}, retainedNodes: [[ELEMENTS:!.*]])
// CHECK: [[ELEMENTS]] = !{{{.*}}, [[LABEL_METADATA]]}
// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 8)
// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 8, column: 1)
// CHECK: [[INLINEDAT:!.*]] = distinct !DILocation(line: 18,
// CHECK: [[LABEL_LOCATION]] = !DILocation(line: 8, {{.*}}, inlinedAt: [[INLINEDAT]])
2 changes: 1 addition & 1 deletion clang/test/CodeGen/debug-label.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ int f1(int a, int b) {
return sum;
}

// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 9)
// CHECK: [[LABEL_METADATA]] = !DILabel({{.*}}, name: "top", {{.*}}, line: 9, column: 1)
// CHECK: [[LABEL_LOCATION]] = !DILocation(line: 9,
21 changes: 15 additions & 6 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6988,16 +6988,25 @@ appear in the included source file.
DILabel
"""""""

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

Furthermore, a label can be marked as artificial, i.e. compiler-generated,
using ``isArtificial:``. Such artificial labels are generated, e.g., by
the ``CoroSplit`` pass. In addition, the ``CoroSplit`` pass also uses the
``coroSuspendIdx:`` field to identify the coroutine suspend points.

``scope:``, ``name:``, ``file:`` and ``line:`` are mandatory. The remaining
fields are optional.

.. code-block:: text

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

DICommonBlock
"""""""""""""
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.def
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ HANDLE_DW_AT(0x3e09, LLVM_ptrauth_authenticates_null_values, 0, LLVM)
HANDLE_DW_AT(0x3e0a, LLVM_ptrauth_authentication_mode, 0, LLVM)
HANDLE_DW_AT(0x3e0b, LLVM_num_extra_inhabitants, 0, LLVM)
HANDLE_DW_AT(0x3e0c, LLVM_stmt_sequence, 0, LLVM)
HANDLE_DW_AT(0x3e0d, LLVM_coro_suspend_idx, 0, LLVM)

// Apple extensions.

Expand Down
5 changes: 4 additions & 1 deletion llvm/include/llvm/IR/DIBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -901,7 +901,10 @@ namespace llvm {
/// \c Scope must be a \a DILocalScope, and thus its scope chain eventually
/// leads to a \a DISubprogram.
LLVM_ABI DILabel *createLabel(DIScope *Scope, StringRef Name, DIFile *File,
unsigned LineNo, bool AlwaysPreserve = false);
unsigned LineNo, unsigned Column,
bool IsArtificial,
std::optional<unsigned> CoroSuspendIdx,
bool AlwaysPreserve = false);

/// Create a new descriptor for a parameter variable.
///
Expand Down
43 changes: 30 additions & 13 deletions llvm/include/llvm/IR/DebugInfoMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -4112,35 +4112,49 @@ class DILabel : public DINode {
friend class LLVMContextImpl;
friend class MDNode;

DILabel(LLVMContext &C, StorageType Storage, unsigned Line,
unsigned Column;
std::optional<unsigned> CoroSuspendIdx;
bool IsArtificial;

DILabel(LLVMContext &C, StorageType Storage, unsigned Line, unsigned Column,
bool IsArtificial, std::optional<unsigned> CoroSuspendIdx,
ArrayRef<Metadata *> Ops);
~DILabel() = default;

static DILabel *getImpl(LLVMContext &Context, DIScope *Scope, StringRef Name,
DIFile *File, unsigned Line, StorageType Storage,
bool ShouldCreate = true) {
DIFile *File, unsigned Line, unsigned Column,
bool IsArtificial,
std::optional<unsigned> CoroSuspendIdx,
StorageType Storage, bool ShouldCreate = true) {
return getImpl(Context, Scope, getCanonicalMDString(Context, Name), File,
Line, Storage, ShouldCreate);
Line, Column, IsArtificial, CoroSuspendIdx, Storage,
ShouldCreate);
}
LLVM_ABI static DILabel *getImpl(LLVMContext &Context, Metadata *Scope,
MDString *Name, Metadata *File,
unsigned Line, StorageType Storage,
bool ShouldCreate = true);
LLVM_ABI static DILabel *
getImpl(LLVMContext &Context, Metadata *Scope, MDString *Name, Metadata *File,
unsigned Line, unsigned Column, bool IsArtificial,
std::optional<unsigned> CoroSuspendIdx, StorageType Storage,
bool ShouldCreate = true);

TempDILabel cloneImpl() const {
return getTemporary(getContext(), getScope(), getName(), getFile(),
getLine());
getLine(), getColumn(), isArtificial(),
getCoroSuspendIdx());
}

public:
DEFINE_MDNODE_GET(DILabel,
(DILocalScope * Scope, StringRef Name, DIFile *File,
unsigned Line),
(Scope, Name, File, Line))
unsigned Line, unsigned Column, bool IsArtificial,
std::optional<unsigned> CoroSuspendIdx),
(Scope, Name, File, Line, Column, IsArtificial,
CoroSuspendIdx))
DEFINE_MDNODE_GET(DILabel,
(Metadata * Scope, MDString *Name, Metadata *File,
unsigned Line),
(Scope, Name, File, Line))
unsigned Line, unsigned Column, bool IsArtificial,
std::optional<unsigned> CoroSuspendIdx),
(Scope, Name, File, Line, Column, IsArtificial,
CoroSuspendIdx))

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

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

Metadata *getRawScope() const { return getOperand(0); }
MDString *getRawName() const { return getOperandAs<MDString>(1); }
Expand Down
14 changes: 11 additions & 3 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6115,18 +6115,26 @@ bool LLParser::parseDILocalVariable(MDNode *&Result, bool IsDistinct) {
}

/// parseDILabel:
/// ::= !DILabel(scope: !0, name: "foo", file: !1, line: 7)
/// ::= !DILabel(scope: !0, name: "foo", file: !1, line: 7, column: 4)
bool LLParser::parseDILabel(MDNode *&Result, bool IsDistinct) {
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
REQUIRED(scope, MDField, (/* AllowNull */ false)); \
REQUIRED(name, MDStringField, ); \
REQUIRED(file, MDField, ); \
REQUIRED(line, LineField, );
REQUIRED(line, LineField, ); \
OPTIONAL(column, ColumnField, ); \
OPTIONAL(isArtificial, MDBoolField, ); \
OPTIONAL(coroSuspendIdx, MDUnsignedField, );
PARSE_MD_FIELDS();
#undef VISIT_MD_FIELDS

std::optional<unsigned> CoroSuspendIdx =
coroSuspendIdx.Seen ? std::optional<unsigned>(coroSuspendIdx.Val)
: std::nullopt;

Result = GET_OR_DISTINCT(DILabel,
(Context, scope.Val, name.Val, file.Val, line.Val));
(Context, scope.Val, name.Val, file.Val, line.Val,
column.Val, isArtificial.Val, CoroSuspendIdx));
return false;
}

Expand Down
22 changes: 18 additions & 4 deletions llvm/lib/Bitcode/Reader/MetadataLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2242,14 +2242,28 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
break;
}
case bitc::METADATA_LABEL: {
if (Record.size() != 5)
if (Record.size() < 5 || Record.size() > 7)
return error("Invalid record");

IsDistinct = Record[0] & 1;
uint64_t Line = Record[4];
uint64_t Column = Record.size() > 5 ? Record[5] : 0;
bool IsArtificial = Record[0] & 2;
std::optional<unsigned> CoroSuspendIdx;
if (Record.size() > 6) {
uint64_t RawSuspendIdx = Record[6];
if (RawSuspendIdx != std::numeric_limits<uint64_t>::max()) {
if (RawSuspendIdx > (uint64_t)std::numeric_limits<unsigned>::max())
return error("CoroSuspendIdx value is too large");
CoroSuspendIdx = RawSuspendIdx;
}
}

MetadataList.assignValue(
GET_OR_DISTINCT(DILabel, (Context, getMDOrNull(Record[1]),
getMDString(Record[2]),
getMDOrNull(Record[3]), Record[4])),
GET_OR_DISTINCT(DILabel,
(Context, getMDOrNull(Record[1]),
getMDString(Record[2]), getMDOrNull(Record[3]), Line,
Column, IsArtificial, CoroSuspendIdx)),
NextMetadataNo);
NextMetadataNo++;
break;
Expand Down
7 changes: 6 additions & 1 deletion llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2351,11 +2351,16 @@ void ModuleBitcodeWriter::writeDILocalVariable(
void ModuleBitcodeWriter::writeDILabel(
const DILabel *N, SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {
Record.push_back((uint64_t)N->isDistinct());
uint64_t IsArtificialFlag = uint64_t(N->isArtificial()) << 1;
Record.push_back((uint64_t)N->isDistinct() | IsArtificialFlag);
Record.push_back(VE.getMetadataOrNullID(N->getScope()));
Record.push_back(VE.getMetadataOrNullID(N->getRawName()));
Record.push_back(VE.getMetadataOrNullID(N->getFile()));
Record.push_back(N->getLine());
Record.push_back(N->getColumn());
Record.push_back(N->getCoroSuspendIdx().has_value()
? (uint64_t)N->getCoroSuspendIdx().value()
: std::numeric_limits<uint64_t>::max());

Stream.EmitRecord(bitc::METADATA_LABEL, Record, Abbrev);
Record.clear();
Expand Down
9 changes: 7 additions & 2 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ DIE *DwarfCompileUnit::getOrCreateCommonBlock(
addString(NDie, dwarf::DW_AT_name, Name);
addGlobalName(Name, NDie, CB->getScope());
if (CB->getFile())
addSourceLine(NDie, CB->getLineNo(), CB->getFile());
addSourceLine(NDie, CB->getLineNo(), /*Column*/ 0, CB->getFile());
if (DIGlobalVariable *V = CB->getDecl())
getCU().addLocationAttribute(&NDie, V, GlobalExprs);
return &NDie;
Expand Down Expand Up @@ -1404,7 +1404,7 @@ DIE *DwarfCompileUnit::constructImportedEntityDIE(
else
EntityDie = getDIE(Entity);
assert(EntityDie);
addSourceLine(*IMDie, Module->getLine(), Module->getFile());
addSourceLine(*IMDie, Module->getLine(), /*Column*/ 0, Module->getFile());
addDIEEntry(*IMDie, dwarf::DW_AT_import, *EntityDie);
StringRef Name = Module->getName();
if (!Name.empty()) {
Expand Down Expand Up @@ -1701,6 +1701,11 @@ void DwarfCompileUnit::applyLabelAttributes(const DbgLabel &Label,
addString(LabelDie, dwarf::DW_AT_name, Name);
const auto *DILabel = Label.getLabel();
addSourceLine(LabelDie, DILabel);
if (DILabel->isArtificial())
addFlag(LabelDie, dwarf::DW_AT_artificial);
if (DILabel->getCoroSuspendIdx())
addUInt(LabelDie, dwarf::DW_AT_LLVM_coro_suspend_idx, std::nullopt,
*DILabel->getCoroSuspendIdx());
}

/// Add a Dwarf expression attribute data and value.
Expand Down
18 changes: 11 additions & 7 deletions llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,49 +442,53 @@ void DwarfUnit::addBlock(DIE &Die, dwarf::Attribute Attribute,
addBlock(Die, Attribute, Block->BestForm(), Block);
}

void DwarfUnit::addSourceLine(DIE &Die, unsigned Line, const DIFile *File) {
void DwarfUnit::addSourceLine(DIE &Die, unsigned Line, unsigned Column,
const DIFile *File) {
if (Line == 0)
return;

unsigned FileID = getOrCreateSourceID(File);
addUInt(Die, dwarf::DW_AT_decl_file, std::nullopt, FileID);
addUInt(Die, dwarf::DW_AT_decl_line, std::nullopt, Line);

if (Column != 0)
addUInt(Die, dwarf::DW_AT_decl_column, std::nullopt, Column);
}

void DwarfUnit::addSourceLine(DIE &Die, const DILocalVariable *V) {
assert(V);

addSourceLine(Die, V->getLine(), V->getFile());
addSourceLine(Die, V->getLine(), /*Column*/ 0, V->getFile());
}

void DwarfUnit::addSourceLine(DIE &Die, const DIGlobalVariable *G) {
assert(G);

addSourceLine(Die, G->getLine(), G->getFile());
addSourceLine(Die, G->getLine(), /*Column*/ 0, G->getFile());
}

void DwarfUnit::addSourceLine(DIE &Die, const DISubprogram *SP) {
assert(SP);

addSourceLine(Die, SP->getLine(), SP->getFile());
addSourceLine(Die, SP->getLine(), /*Column*/ 0, SP->getFile());
}

void DwarfUnit::addSourceLine(DIE &Die, const DILabel *L) {
assert(L);

addSourceLine(Die, L->getLine(), L->getFile());
addSourceLine(Die, L->getLine(), L->getColumn(), L->getFile());
}

void DwarfUnit::addSourceLine(DIE &Die, const DIType *Ty) {
assert(Ty);

addSourceLine(Die, Ty->getLine(), Ty->getFile());
addSourceLine(Die, Ty->getLine(), /*Column*/ 0, Ty->getFile());
}

void DwarfUnit::addSourceLine(DIE &Die, const DIObjCProperty *Ty) {
assert(Ty);

addSourceLine(Die, Ty->getLine(), Ty->getFile());
addSourceLine(Die, Ty->getLine(), /*Column*/ 0, Ty->getFile());
}

void DwarfUnit::addConstantFPValue(DIE &Die, const ConstantFP *CFP) {
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ class DwarfUnit : public DIEUnit {
DIEBlock *Block);

/// Add location information to specified debug information entry.
void addSourceLine(DIE &Die, unsigned Line, const DIFile *File);
void addSourceLine(DIE &Die, unsigned Line, unsigned Column,
const DIFile *File);
void addSourceLine(DIE &Die, const DILocalVariable *V);
void addSourceLine(DIE &Die, const DIGlobalVariable *G);
void addSourceLine(DIE &Die, const DISubprogram *SP);
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2611,6 +2611,11 @@ static void writeDILabel(raw_ostream &Out, const DILabel *N,
Printer.printString("name", N->getName());
Printer.printMetadata("file", N->getRawFile());
Printer.printInt("line", N->getLine());
Printer.printInt("column", N->getColumn());
Printer.printBool("isArtificial", N->isArtificial(), false);
if (N->getCoroSuspendIdx())
Printer.printInt("coroSuspendIdx", *N->getCoroSuspendIdx(),
/* ShouldSkipZero */ false);
Out << ")";
}

Expand Down
Loading
Loading