diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h index 7dba67efa22fa..bea79545d1ab9 100644 --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -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; @@ -365,6 +377,9 @@ class MCDwarfLineTable { emitOne(MCStreamer *MCOS, MCSection *Section, const MCLineSection::MCDwarfLineEntryCollection &LineEntries); + void endCurrentSeqAndEmitLineStreamLabel(MCStreamer *MCOS, SMLoc DefLoc, + StringRef Name); + Expected tryGetFile(StringRef &Directory, StringRef &FileName, std::optional Checksum, std::optional Source, diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h index 4241ec1e1881b..aaa13be6b2986 100644 --- a/llvm/include/llvm/MC/MCObjectStreamer.h +++ b/llvm/include/llvm/MC/MCObjectStreamer.h @@ -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, diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 42b1114266adb..e5d9dbbac8dba 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -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. @@ -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 diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 295531f1d5f11..31b519a3e5c56 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -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, @@ -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, @@ -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. @@ -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 @@ -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()); } diff --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp index 0dd113797385d..8ff097f29aebd 100644 --- a/llvm/lib/MC/MCDwarf.cpp +++ b/llvm/lib/MC/MCDwarf.cpp @@ -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; @@ -181,6 +182,7 @@ void MCDwarfLineTable::emitOne( Isa = 0; Discriminator = 0; LastLabel = nullptr; + IsAtStartSeq = true; }; init(); @@ -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()); @@ -243,6 +256,7 @@ void MCDwarfLineTable::emitOne( Discriminator = 0; LastLine = LineEntry.getLine(); LastLabel = Label; + IsAtStartSeq = false; } // Generate DWARF line end entry. @@ -250,10 +264,26 @@ void MCDwarfLineTable::emitOne( // 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. // diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index 9dc3974fd8f0d..b2b21435fa4af 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -467,12 +467,16 @@ 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. @@ -480,7 +484,7 @@ void MCObjectStreamer::emitDwarfLineEndEntry(MCSection *Section, switchSection(Ctx.getObjectFileInfo()->getDwarfLineSection()); const MCAsmInfo *AsmInfo = Ctx.getAsmInfo(); - emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, SectionEnd, + emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, EndLabel, AsmInfo->getCodePointerSize()); } diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 14f98db3d1a00..a86c092b4c69f 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -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, @@ -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", @@ -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: @@ -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() { @@ -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; diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 1594bd3231abe..13b162768578c 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -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()) { diff --git a/llvm/test/MC/ELF/debug-loc-label.s b/llvm/test/MC/ELF/debug-loc-label.s new file mode 100644 index 0000000000000..6b5d04777bef4 --- /dev/null +++ b/llvm/test/MC/ELF/debug-loc-label.s @@ -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