Skip to content

Commit

Permalink
[MC] Add .loc_label instruction (#99710)
Browse files Browse the repository at this point in the history
As discussed in [the
RFC](https://discourse.llvm.org/t/rfc-extending-llvm-mc-loc-directive-with-labeling-support/79608)
we need a way to create labels in the assembler-generated line section
in order to support the future addition of the
[DW_AT_LLVM_stmt_sequence](https://discourse.llvm.org/t/rfc-new-dwarf-attribute-for-symbolication-of-merged-functions/79434)
attribute.

We have a similar precedent for such behavior with the
[.cfi_label](#97922)
instruction - so we add the `.loc_label THE_LABEL_NAME` instruction
which:
- Terminates the current line sequence in the line section
- Creates a new label with the specified label name in the `.debug_line`
section
  • Loading branch information
alx32 authored Sep 20, 2024
1 parent 42b696d commit 28646d0
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 16 deletions.
19 changes: 17 additions & 2 deletions llvm/include/llvm/MC/MCDwarf.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,23 @@ class MCDwarfLineEntry : public MCDwarfLoc {

public:
// Constructor to create an MCDwarfLineEntry given a symbol and the dwarf loc.
MCDwarfLineEntry(MCSymbol *label, const MCDwarfLoc loc)
: MCDwarfLoc(loc), Label(label) {}
MCDwarfLineEntry(MCSymbol *label, const MCDwarfLoc loc,
MCSymbol *lineStreamLabel = nullptr,
SMLoc streamLabelDefLoc = {})
: MCDwarfLoc(loc), Label(label), LineStreamLabel(lineStreamLabel),
StreamLabelDefLoc(streamLabelDefLoc) {}

MCSymbol *getLabel() const { return Label; }

// This is the label that is to be emitted into the line stream. If this is
// non-null and we need to emit a label, also make sure to restart the current
// line sequence.
MCSymbol *LineStreamLabel;

// Location where LineStreamLabel was defined. If there is an error emitting
// LineStreamLabel, we can use the SMLoc to report an error.
SMLoc StreamLabelDefLoc;

// This indicates the line entry is synthesized for an end entry.
bool IsEndEntry = false;

Expand Down Expand Up @@ -365,6 +377,9 @@ class MCDwarfLineTable {
emitOne(MCStreamer *MCOS, MCSection *Section,
const MCLineSection::MCDwarfLineEntryCollection &LineEntries);

void endCurrentSeqAndEmitLineStreamLabel(MCStreamer *MCOS, SMLoc DefLoc,
StringRef Name);

Expected<unsigned> tryGetFile(StringRef &Directory, StringRef &FileName,
std::optional<MD5::MD5Result> Checksum,
std::optional<StringRef> Source,
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/MC/MCObjectStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ class MCObjectStreamer : public MCStreamer {
void emitDwarfAdvanceLineAddr(int64_t LineDelta, const MCSymbol *LastLabel,
const MCSymbol *Label,
unsigned PointerSize) override;
void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel) override;
void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel,
MCSymbol *EndLabel = nullptr) override;
void emitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel,
const MCSymbol *Label, SMLoc Loc);
void emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line,
Expand Down
6 changes: 5 additions & 1 deletion llvm/include/llvm/MC/MCStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,9 @@ class MCStreamer {
unsigned Isa, unsigned Discriminator,
StringRef FileName);

/// This implements the '.loc_label Name' directive.
virtual void emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name);

/// Associate a filename with a specified logical file number, and also
/// specify that file's checksum information. This implements the '.cv_file 4
/// "foo.c"' assembler directive. Returns true on success.
Expand Down Expand Up @@ -1119,7 +1122,8 @@ class MCStreamer {
virtual void emitDwarfLineStartLabel(MCSymbol *StartSym);

/// Emit the debug line end entry.
virtual void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel) {}
virtual void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel,
MCSymbol *EndLabel = nullptr) {}

/// If targets does not support representing debug line section by .loc/.file
/// directives in assembly output, we need to populate debug line section with
Expand Down
19 changes: 15 additions & 4 deletions llvm/lib/MC/MCAsmStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ class MCAsmStreamer final : public MCStreamer {
unsigned Flags, unsigned Isa,
unsigned Discriminator,
StringRef FileName) override;
virtual void emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name) override;

MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override;

bool emitCVFileDirective(unsigned FileNo, StringRef Filename,
Expand Down Expand Up @@ -429,7 +431,8 @@ class MCAsmStreamer final : public MCStreamer {

void emitDwarfLineStartLabel(MCSymbol *StartSym) override;

void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel) override;
void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel,
MCSymbol *EndLabel = nullptr) override;

void emitDwarfAdvanceLineAddr(int64_t LineDelta, const MCSymbol *LastLabel,
const MCSymbol *Label,
Expand Down Expand Up @@ -1767,6 +1770,12 @@ void MCAsmStreamer::emitDwarfLocDirective(unsigned FileNo, unsigned Line,
Discriminator, FileName);
}

void MCAsmStreamer::emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name) {
MCStreamer::emitDwarfLocLabelDirective(Loc, Name);
OS << ".loc_label\t" << Name;
EmitEOL();
}

MCSymbol *MCAsmStreamer::getDwarfLineTableSymbol(unsigned CUID) {
// Always use the zeroth line table, since asm syntax only supports one line
// table for now.
Expand Down Expand Up @@ -2579,7 +2588,8 @@ void MCAsmStreamer::emitDwarfLineStartLabel(MCSymbol *StartSym) {
}

void MCAsmStreamer::emitDwarfLineEndEntry(MCSection *Section,
MCSymbol *LastLabel) {
MCSymbol *LastLabel,
MCSymbol *EndLabel) {
// If the targets write the raw debug line data for assembly output (We can
// not switch to Section and add the end symbol there for assembly output)
// we currently use the .text end label as any section end. This will not
Expand All @@ -2596,9 +2606,10 @@ void MCAsmStreamer::emitDwarfLineEndEntry(MCSection *Section,
MCSection *TextSection = Ctx.getObjectFileInfo()->getTextSection();
assert(TextSection->hasEnded() && ".text section is not end!");

MCSymbol *SectionEnd = TextSection->getEndSymbol(Ctx);
if (!EndLabel)
EndLabel = TextSection->getEndSymbol(Ctx);
const MCAsmInfo *AsmInfo = Ctx.getAsmInfo();
emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, SectionEnd,
emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, EndLabel,
AsmInfo->getCodePointerSize());
}

Expand Down
32 changes: 31 additions & 1 deletion llvm/lib/MC/MCDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ void MCDwarfLineTable::emitOne(
const MCLineSection::MCDwarfLineEntryCollection &LineEntries) {

unsigned FileNum, LastLine, Column, Flags, Isa, Discriminator;
bool IsAtStartSeq;
MCSymbol *LastLabel;
auto init = [&]() {
FileNum = 1;
Expand All @@ -181,6 +182,7 @@ void MCDwarfLineTable::emitOne(
Isa = 0;
Discriminator = 0;
LastLabel = nullptr;
IsAtStartSeq = true;
};
init();

Expand All @@ -189,6 +191,17 @@ void MCDwarfLineTable::emitOne(
for (const MCDwarfLineEntry &LineEntry : LineEntries) {
MCSymbol *Label = LineEntry.getLabel();
const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo();

if (LineEntry.LineStreamLabel) {
if (!IsAtStartSeq) {
MCOS->emitDwarfLineEndEntry(Section, LastLabel,
/*EndLabel =*/LastLabel);
init();
}
MCOS->emitLabel(LineEntry.LineStreamLabel, LineEntry.StreamLabelDefLoc);
continue;
}

if (LineEntry.IsEndEntry) {
MCOS->emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, Label,
asmInfo->getCodePointerSize());
Expand Down Expand Up @@ -243,17 +256,34 @@ void MCDwarfLineTable::emitOne(
Discriminator = 0;
LastLine = LineEntry.getLine();
LastLabel = Label;
IsAtStartSeq = false;
}

// Generate DWARF line end entry.
// We do not need this for DwarfDebug that explicitly terminates the line
// table using ranges whenever CU or section changes. However, the MC path
// does not track ranges nor terminate the line table. In that case,
// conservatively use the section end symbol to end the line table.
if (!EndEntryEmitted)
if (!EndEntryEmitted && !IsAtStartSeq)
MCOS->emitDwarfLineEndEntry(Section, LastLabel);
}

void MCDwarfLineTable::endCurrentSeqAndEmitLineStreamLabel(MCStreamer *MCOS,
SMLoc DefLoc,
StringRef Name) {
auto &ctx = MCOS->getContext();
auto *LineStreamLabel = ctx.getOrCreateSymbol(Name);
auto *LineSym = ctx.createTempSymbol();
MCOS->emitLabel(LineSym);
const MCDwarfLoc &DwarfLoc = ctx.getCurrentDwarfLoc();

// Create a 'fake' line entry by having LineStreamLabel be non-null. This
// won't actually emit any line information, it will reset the line table
// sequence and emit a label at the start of the new line table sequence.
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc, LineStreamLabel, DefLoc);
getMCLineSections().addLineEntry(LineEntry, MCOS->getCurrentSectionOnly());
}

//
// This emits the Dwarf file and the line tables.
//
Expand Down
16 changes: 10 additions & 6 deletions llvm/lib/MC/MCObjectStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,20 +467,24 @@ void MCObjectStreamer::emitDwarfAdvanceLineAddr(int64_t LineDelta,
}

void MCObjectStreamer::emitDwarfLineEndEntry(MCSection *Section,
MCSymbol *LastLabel) {
// Emit a DW_LNE_end_sequence for the end of the section.
// Use the section end label to compute the address delta and use INT64_MAX
// as the line delta which is the signal that this is actually a
MCSymbol *LastLabel,
MCSymbol *EndLabel) {
// Emit a DW_LNE_end_sequence into the line table. When EndLabel is null, it
// means we should emit the entry for the end of the section and therefore we
// use the section end label for the reference label. After having the
// appropriate reference label, we emit the address delta and use INT64_MAX as
// the line delta which is the signal that this is actually a
// DW_LNE_end_sequence.
MCSymbol *SectionEnd = endSection(Section);
if (!EndLabel)
EndLabel = endSection(Section);

// Switch back the dwarf line section, in case endSection had to switch the
// section.
MCContext &Ctx = getContext();
switchSection(Ctx.getObjectFileInfo()->getDwarfLineSection());

const MCAsmInfo *AsmInfo = Ctx.getAsmInfo();
emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, SectionEnd,
emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, EndLabel,
AsmInfo->getCodePointerSize());
}

Expand Down
20 changes: 19 additions & 1 deletion llvm/lib/MC/MCParser/AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ class AsmParser : public MCAsmParser {
DK_FILE,
DK_LINE,
DK_LOC,
DK_LOC_LABEL,
DK_STABS,
DK_CV_FILE,
DK_CV_FUNC_ID,
Expand Down Expand Up @@ -580,10 +581,11 @@ class AsmParser : public MCAsmParser {
// ".align{,32}", ".p2align{,w,l}"
bool parseDirectiveAlign(bool IsPow2, unsigned ValueSize);

// ".file", ".line", ".loc", ".stabs"
// ".file", ".line", ".loc", ".loc_label", ".stabs"
bool parseDirectiveFile(SMLoc DirectiveLoc);
bool parseDirectiveLine();
bool parseDirectiveLoc();
bool parseDirectiveLocLabel(SMLoc DirectiveLoc);
bool parseDirectiveStabs();

// ".cv_file", ".cv_func_id", ".cv_inline_site_id", ".cv_loc", ".cv_linetable",
Expand Down Expand Up @@ -2156,6 +2158,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info,
return parseDirectiveLine();
case DK_LOC:
return parseDirectiveLoc();
case DK_LOC_LABEL:
return parseDirectiveLocLabel(IDLoc);
case DK_STABS:
return parseDirectiveStabs();
case DK_CV_FILE:
Expand Down Expand Up @@ -3733,6 +3737,19 @@ bool AsmParser::parseDirectiveLoc() {
return false;
}

/// parseDirectiveLoc
/// ::= .loc_label label
bool AsmParser::parseDirectiveLocLabel(SMLoc DirectiveLoc) {
StringRef Name;
DirectiveLoc = Lexer.getLoc();
if (parseIdentifier(Name))
return TokError("expected identifier");
if (parseEOL())
return true;
getStreamer().emitDwarfLocLabelDirective(DirectiveLoc, Name);
return false;
}

/// parseDirectiveStabs
/// ::= .stabs string, number, number, number
bool AsmParser::parseDirectiveStabs() {
Expand Down Expand Up @@ -5541,6 +5558,7 @@ void AsmParser::initializeDirectiveKindMap() {
DirectiveKindMap[".file"] = DK_FILE;
DirectiveKindMap[".line"] = DK_LINE;
DirectiveKindMap[".loc"] = DK_LOC;
DirectiveKindMap[".loc_label"] = DK_LOC_LABEL;
DirectiveKindMap[".stabs"] = DK_STABS;
DirectiveKindMap[".cv_file"] = DK_CV_FILE;
DirectiveKindMap[".cv_func_id"] = DK_CV_FUNC_ID;
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/MC/MCStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ void MCStreamer::emitDwarfLocDirective(unsigned FileNo, unsigned Line,
Discriminator);
}

void MCStreamer::emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name) {
getContext()
.getMCDwarfLineTable(getContext().getDwarfCompileUnitID())
.endCurrentSeqAndEmitLineStreamLabel(this, Loc, Name);
}

MCSymbol *MCStreamer::getDwarfLineTableSymbol(unsigned CUID) {
MCDwarfLineTable &Table = getContext().getMCDwarfLineTable(CUID);
if (!Table.getLabel()) {
Expand Down
101 changes: 101 additions & 0 deletions llvm/test/MC/ELF/debug-loc-label.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Verify that the .loc_label instruction resets the line sequence and generates
// the requested label at the correct position in the line stream

// RUN: llvm-mc -filetype obj -triple x86_64 %s -o %t.o
// RUN: llvm-dwarfdump -v --debug-line %t.o | FileCheck %s --check-prefix=CHECK-LINE-TABLE
// RUN: llvm-readelf -s %t.o | FileCheck %s --check-prefix=CHECK-SYM
// RUN: llvm-objdump -s -j .offsets %t.o | FileCheck %s --check-prefix=CHECK-OFFSETS

// RUN: not llvm-mc -filetype obj -triple x86_64 --defsym ERR=1 %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error:
// RUN: not llvm-mc -filetype obj -triple x86_64 --defsym ERR2=1 %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR2 --implicit-check-not=error:



# CHECK-LINE-TABLE: Address Line Column File ISA Discriminator OpIndex Flags
# CHECK-LINE-TABLE-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
# CHECK-LINE-TABLE-NEXT: 0x00000028: 05 DW_LNS_set_column (1)
# CHECK-LINE-TABLE-NEXT: 0x0000002a: 00 DW_LNE_set_address (0x0000000000000000)
# CHECK-LINE-TABLE-NEXT: 0x00000035: 01 DW_LNS_copy
# CHECK-LINE-TABLE-NEXT: 0x0000000000000000 1 1 1 0 0 0 is_stmt
# CHECK-LINE-TABLE-NEXT: 0x00000036: 00 DW_LNE_end_sequence
# CHECK-LINE-TABLE-NEXT: 0x0000000000000000 1 1 1 0 0 0 is_stmt end_sequence
# CHECK-LINE-TABLE-NEXT: 0x00000039: 05 DW_LNS_set_column (2)
# CHECK-LINE-TABLE-NEXT: 0x0000003b: 00 DW_LNE_set_address (0x0000000000000008)
# CHECK-LINE-TABLE-NEXT: 0x00000046: 01 DW_LNS_copy
# CHECK-LINE-TABLE-NEXT: 0x0000000000000008 1 2 1 0 0 0 is_stmt
# CHECK-LINE-TABLE-NEXT: 0x00000047: 00 DW_LNE_end_sequence
# CHECK-LINE-TABLE-NEXT: 0x0000000000000008 1 2 1 0 0 0 is_stmt end_sequence
# CHECK-LINE-TABLE-NEXT: 0x0000004a: 05 DW_LNS_set_column (3)
# CHECK-LINE-TABLE-NEXT: 0x0000004c: 00 DW_LNE_set_address (0x0000000000000010)
# CHECK-LINE-TABLE-NEXT: 0x00000057: 01 DW_LNS_copy
# CHECK-LINE-TABLE-NEXT: 0x0000000000000010 1 3 1 0 0 0 is_stmt
# CHECK-LINE-TABLE-NEXT: 0x00000058: 00 DW_LNE_end_sequence
# CHECK-LINE-TABLE-NEXT: 0x0000000000000010 1 3 1 0 0 0 is_stmt end_sequence
# CHECK-LINE-TABLE-NEXT: 0x0000005b: 05 DW_LNS_set_column (4)
# CHECK-LINE-TABLE-NEXT: 0x0000005d: 00 DW_LNE_set_address (0x0000000000000018)
# CHECK-LINE-TABLE-NEXT: 0x00000068: 01 DW_LNS_copy
# CHECK-LINE-TABLE-NEXT: 0x0000000000000018 1 4 1 0 0 0 is_stmt
# CHECK-LINE-TABLE-NEXT: 0x00000069: 05 DW_LNS_set_column (5)
# CHECK-LINE-TABLE-NEXT: 0x0000006b: 01 DW_LNS_copy
# CHECK-LINE-TABLE-NEXT: 0x0000000000000018 1 5 1 0 0 0 is_stmt
# CHECK-LINE-TABLE-NEXT: 0x0000006c: 00 DW_LNE_end_sequence
# CHECK-LINE-TABLE-NEXT: 0x0000000000000018 1 5 1 0 0 0 is_stmt end_sequence

# CHECK-SYM: Symbol table '.symtab' contains 9 entries:
# CHECK-SYM-NEXT: Num: Value Size Type Bind Vis Ndx Name
# CHECK-SYM-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
# CHECK-SYM-NEXT: 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
# CHECK-SYM-NEXT: 2: 0000000000000000 0 SECTION LOCAL DEFAULT 2 .text
# CHECK-SYM-NEXT: 3: 0000000000000039 0 NOTYPE LOCAL DEFAULT 3 my_label_02
# CHECK-SYM-NEXT: 4: 000000000000004a 0 NOTYPE LOCAL DEFAULT 3 my_label_03
# CHECK-SYM-NEXT: 5: 000000000000005b 0 NOTYPE LOCAL DEFAULT 3 my_label_04
# CHECK-SYM-NEXT: 6: 000000000000004a 0 NOTYPE LOCAL DEFAULT 3 my_label_03.1
# CHECK-SYM-NEXT: 7: 000000000000006f 0 NOTYPE LOCAL DEFAULT 3 my_label_05
# CHECK-SYM-NEXT: 8: 0000000000000000 0 FUNC GLOBAL DEFAULT 2 foo

# CHECK-OFFSETS: 0000 39000000 4a000000 5b000000

.text
.file "test.c"
.globl foo
.align 16, 0x90
.type foo,@function
foo:
.Lfunc_begin0:
.file 1 "test.c"
.cfi_startproc
.loc 1 1 1
mov %rax, 0x01
.loc_label my_label_02
.loc 1 1 2
mov %rax, 0x02
.loc 1 1 3
.loc_label my_label_03
.loc_label my_label_03.1
mov %rax, 0x03
.loc 1 1 4
.loc_label my_label_04
.loc 1 1 5
.ifdef ERR
.loc_label my_label_04
# ERR: [[#@LINE+1]]:13: error: expected identifier
.loc_label
# ERR: [[#@LINE+1]]:19: error: expected newline
.loc_label aaaa bbbb
.endif
.ifdef ERR2
# ERR2: [[#@LINE+1]]:14: error: symbol 'my_label_04' is already defined
.loc_label my_label_04
.endif
mov %rax, 0x04
.loc_label my_label_05
ret
.cfi_endproc

.section .debug_line,"",@progbits
.Lline_table_start0:

.section .offsets,"",@progbits
.long my_label_02-.Lline_table_start0
.long my_label_03-.Lline_table_start0
.long my_label_04-.Lline_table_start0

0 comments on commit 28646d0

Please sign in to comment.