diff --git a/llvm/include/llvm/MC/MCDwarf.h b/llvm/include/llvm/MC/MCDwarf.h --- a/llvm/include/llvm/MC/MCDwarf.h +++ b/llvm/include/llvm/MC/MCDwarf.h @@ -188,6 +188,15 @@ MCSymbol *getLabel() const { return Label; } + // This indicates the line entry is synthesized for an end entry. + bool IsEndEntry = false; + + // Override the label with the given EndLabel. + void setEndLabel(MCSymbol *EndLabel) { + Label = EndLabel; + IsEndEntry = true; + } + // This is called when an instruction is assembled into the specified // section and if there is information from the last .loc directive that // has yet to have a line entry made for it is made. @@ -205,6 +214,10 @@ MCLineDivisions[Sec].push_back(LineEntry); } + // Add an end entry by cloning the last entry, if exists, for the section + // the given EndLabel belongs to. The label is replaced by the given EndLabel. + void addEndEntry(MCSymbol *EndLabel); + using MCDwarfLineEntryCollection = std::vector; using iterator = MCDwarfLineEntryCollection::iterator; using const_iterator = MCDwarfLineEntryCollection::const_iterator; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -367,7 +367,8 @@ void DwarfCompileUnit::addRange(RangeSpan Range) { DD->insertSectionLabel(Range.Begin); - bool SameAsPrevCU = this == DD->getPrevCU(); + auto *PrevCU = DD->getPrevCU(); + bool SameAsPrevCU = this == PrevCU; DD->setPrevCU(this); // If we have no current ranges just add the range and return, otherwise, // check the current section and CU against the previous section and CU we @@ -376,6 +377,9 @@ if (CURanges.empty() || !SameAsPrevCU || (&CURanges.back().End->getSection() != &Range.End->getSection())) { + // Before a new range is added, always terminate the prior line table. + if (PrevCU) + DD->terminateLineTable(PrevCU); CURanges.push_back(Range); return; } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -781,6 +781,9 @@ const DwarfCompileUnit *getPrevCU() const { return PrevCU; } void setPrevCU(const DwarfCompileUnit *PrevCU) { this->PrevCU = PrevCU; } + /// Terminate the line table by adding the last range label. + void terminateLineTable(const DwarfCompileUnit *CU); + /// Returns the entries for the .debug_loc section. const DebugLocStream &getDebugLocs() const { return DebugLocs; } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1407,6 +1407,10 @@ // Emit all Dwarf sections that should come after the content. void DwarfDebug::endModule() { + // Terminate the pending line table. + if (PrevCU) + terminateLineTable(PrevCU); + PrevCU = nullptr; assert(CurFn == nullptr); assert(CurMI == nullptr); @@ -2172,10 +2176,22 @@ return CU.getUniqueID(); } +void DwarfDebug::terminateLineTable(const DwarfCompileUnit *CU) { + const auto &CURanges = CU->getRanges(); + auto &LineTable = Asm->OutStreamer->getContext().getMCDwarfLineTable( + getDwarfCompileUnitIDForLineTable(*CU)); + // Add the last range label for the given CU. + LineTable.getMCLineSections().addEndEntry( + const_cast(CURanges.back().End)); +} + void DwarfDebug::skippedNonDebugFunction() { // If we don't have a subprogram for this function then there will be a hole // in the range information. Keep note of this by setting the previously used // section to nullptr. + // Terminate the pending line table. + if (PrevCU) + terminateLineTable(PrevCU); PrevCU = nullptr; CurFn = nullptr; } diff --git a/llvm/lib/MC/MCDwarf.cpp b/llvm/lib/MC/MCDwarf.cpp --- a/llvm/lib/MC/MCDwarf.cpp +++ b/llvm/lib/MC/MCDwarf.cpp @@ -141,6 +141,24 @@ return Res; } +void MCLineSection::addEndEntry(MCSymbol *EndLabel) { + auto *Sec = &EndLabel->getSection(); + // The line table may be empty, which we should skip adding an end entry. + // There are two cases: + // (1) MCAsmStreamer - emitDwarfLocDirective emits a location directive in + // place instead of adding a line entry if the target has + // usesDwarfFileAndLocDirectives. + // (2) MCObjectStreamer - if a function has incomplete debug info where + // instructions don't have DILocations, the line entries are missing. + auto I = MCLineDivisions.find(Sec); + if (I != MCLineDivisions.end()) { + auto &Entries = I->second; + auto EndEntry = Entries.back(); + EndEntry.setEndLabel(EndLabel); + Entries.push_back(EndEntry); + } +} + // // This emits the Dwarf line table for the specified section from the entries // in the LineSection. @@ -148,16 +166,33 @@ void MCDwarfLineTable::emitOne( MCStreamer *MCOS, MCSection *Section, const MCLineSection::MCDwarfLineEntryCollection &LineEntries) { - unsigned FileNum = 1; - unsigned LastLine = 1; - unsigned Column = 0; - unsigned Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; - unsigned Isa = 0; - unsigned Discriminator = 0; - MCSymbol *LastLabel = nullptr; + + unsigned FileNum, LastLine, Column, Flags, Isa, Discriminator; + MCSymbol *LastLabel; + auto init = [&]() { + FileNum = 1; + LastLine = 1; + Column = 0; + Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; + Isa = 0; + Discriminator = 0; + LastLabel = nullptr; + }; + init(); // Loop through each MCDwarfLineEntry and encode the dwarf line number table. + bool EndEntryEmitted = false; for (const MCDwarfLineEntry &LineEntry : LineEntries) { + MCSymbol *Label = LineEntry.getLabel(); + const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo(); + if (LineEntry.IsEndEntry) { + MCOS->emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, Label, + asmInfo->getCodePointerSize()); + init(); + EndEntryEmitted = true; + continue; + } + int64_t LineDelta = static_cast(LineEntry.getLine()) - LastLine; if (FileNum != LineEntry.getFileNum()) { @@ -195,12 +230,9 @@ if (LineEntry.getFlags() & DWARF2_FLAG_EPILOGUE_BEGIN) MCOS->emitInt8(dwarf::DW_LNS_set_epilogue_begin); - MCSymbol *Label = LineEntry.getLabel(); - // At this point we want to emit/create the sequence to encode the delta in // line numbers and the increment of the address from the previous Label // and the current Label. - const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo(); MCOS->emitDwarfAdvanceLineAddr(LineDelta, LastLabel, Label, asmInfo->getCodePointerSize()); @@ -210,7 +242,12 @@ } // Generate DWARF line end entry. - MCOS->emitDwarfLineEndEntry(Section, LastLabel); + // 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) + MCOS->emitDwarfLineEndEntry(Section, LastLabel); } // diff --git a/llvm/test/DebugInfo/XCOFF/empty.ll b/llvm/test/DebugInfo/XCOFF/empty.ll --- a/llvm/test/DebugInfo/XCOFF/empty.ll +++ b/llvm/test/DebugInfo/XCOFF/empty.ll @@ -227,10 +227,10 @@ ; ASM32-NEXT: .byte 3 # Advance line 1 ; ASM32-NEXT: .byte 1 ; ASM32-NEXT: .byte 1 -; ASM32-NEXT: .byte 0 # Set address to L..sec_end0 +; ASM32-NEXT: .byte 0 # Set address to L..func_end0 ; ASM32-NEXT: .byte 5 ; ASM32-NEXT: .byte 2 -; ASM32-NEXT: .vbyte 4, L..sec_end0 +; ASM32-NEXT: .vbyte 4, L..func_end0 ; ASM32-NEXT: .byte 0 # End sequence ; ASM32-NEXT: .byte 1 ; ASM32-NEXT: .byte 1 @@ -428,10 +428,10 @@ ; ASM64-NEXT: .byte 3 # Advance line 1 ; ASM64-NEXT: .byte 1 ; ASM64-NEXT: .byte 1 -; ASM64-NEXT: .byte 0 # Set address to L..sec_end0 +; ASM64-NEXT: .byte 0 # Set address to L..func_end0 ; ASM64-NEXT: .byte 9 ; ASM64-NEXT: .byte 2 -; ASM64-NEXT: .vbyte 8, L..sec_end0 +; ASM64-NEXT: .vbyte 8, L..func_end0 ; ASM64-NEXT: .byte 0 # End sequence ; ASM64-NEXT: .byte 1 ; ASM64-NEXT: .byte 1 diff --git a/llvm/test/DebugInfo/XCOFF/explicit-section.ll b/llvm/test/DebugInfo/XCOFF/explicit-section.ll --- a/llvm/test/DebugInfo/XCOFF/explicit-section.ll +++ b/llvm/test/DebugInfo/XCOFF/explicit-section.ll @@ -296,10 +296,10 @@ ; CHECK-NEXT: .byte 3 # Advance line 0 ; CHECK-NEXT: .byte 0 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end0 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end0 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 @@ -328,10 +328,10 @@ ; CHECK-NEXT: .byte 3 # Advance line 0 ; CHECK-NEXT: .byte 0 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end1 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end1 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 diff --git a/llvm/test/DebugInfo/XCOFF/function-sections.ll b/llvm/test/DebugInfo/XCOFF/function-sections.ll --- a/llvm/test/DebugInfo/XCOFF/function-sections.ll +++ b/llvm/test/DebugInfo/XCOFF/function-sections.ll @@ -283,10 +283,10 @@ ; CHECK-NEXT: .byte 3 # Advance line 1 ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end0 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end0 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 @@ -305,10 +305,10 @@ ; CHECK-NEXT: .byte 3 # Advance line 1 ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 -; CHECK-NEXT: .byte 0 # Set address to L..sec_end0 +; CHECK-NEXT: .byte 0 # Set address to L..func_end1 ; CHECK-NEXT: .byte 5 ; CHECK-NEXT: .byte 2 -; CHECK-NEXT: .vbyte 4, L..sec_end0 +; CHECK-NEXT: .vbyte 4, L..func_end1 ; CHECK-NEXT: .byte 0 # End sequence ; CHECK-NEXT: .byte 1 ; CHECK-NEXT: .byte 1 diff --git a/llvm/test/DebugInfo/debugline-endsequence.ll b/llvm/test/DebugInfo/debugline-endsequence.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/debugline-endsequence.ll @@ -0,0 +1,61 @@ +; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump --debug-line - | FileCheck %s + +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-macosx12.0.0" + +; Check if the end_sequences are emitted for each debug range. + +; CU1 Line table +; CHECK: 0x0000000000000004 [[T:.*]] end_sequence +; CHECK: 0x0000000000000010 [[T:.*]] end_sequence +; +; CU2 Line table +; CHECK: 0x0000000000000008 [[T:.*]] end_sequence + +; CU1 (0x0 ~ 0x4) +define void @f1() !dbg !15 { + ret void, !dbg !18 +} + +; CU2 (0x4 ~ 0x8) +define void @f2() !dbg !21 { + ret void, !dbg !22 +} + +; CU2 (nodebug) - (0x8 ~ 0xc) +define void @f3() { + ret void +} + +; CU1 (0xc ~ 0x10) +define void @f4() !dbg !19 { + ret void, !dbg !20 +} + +!llvm.dbg.cu = !{!0, !3} +!llvm.ident = !{!5, !5} +!llvm.module.flags = !{!6, !7, !8, !9, !10, !11, !12, !13, !14} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "LLVM", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None, sysroot: "/") +!1 = !DIFile(filename: "", directory: "/") +!2 = !{} +!3 = distinct !DICompileUnit(language: DW_LANG_C99, file: !4, producer: "LLVM", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None, sysroot: "/") +!4 = !DIFile(filename: "", directory: "/") +!5 = !{!"Apple clang version 13.0.0 (clang-1300.0.29.3)"} +!6 = !{i32 2, !"SDK Version", [2 x i32] [i32 11, i32 3]} +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 1, !"branch-target-enforcement", i32 0} +!11 = !{i32 1, !"sign-return-address", i32 0} +!12 = !{i32 1, !"sign-return-address-all", i32 0} +!13 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!14 = !{i32 7, !"PIC Level", i32 2} +!15 = distinct !DISubprogram(name: "f1", scope: !1, file: !1, line: 1, type: !16, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!16 = !DISubroutineType(types: !17) +!17 = !{null} +!18 = !DILocation(line: 2, column: 1, scope: !15) +!19 = distinct !DISubprogram(name: "f4", scope: !1, file: !1, line: 4, type: !16, scopeLine: 4, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!20 = !DILocation(line: 5, column: 1, scope: !19) +!21 = distinct !DISubprogram(name: "f2", scope: !4, file: !4, line: 1, type: !16, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !2) +!22 = !DILocation(line: 2, column: 1, scope: !21) diff --git a/llvm/test/DebugInfo/debugline-endsequence.s b/llvm/test/DebugInfo/debugline-endsequence.s new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/debugline-endsequence.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o - | llvm-dwarfdump --debug-line - | FileCheck %s + +# The line table is open in the MC path. +# The end sequence is emitted using the section end label. + +# CHECK: 0x0000000000000001 [[T:.*]] end_sequence +# CHECK: 0x0000000000000001 [[T:.*]] end_sequence + + .text + .section .text.f1 +f1: + .file 1 "/" "t1.c" + .loc 1 1 0 + nop + + .section .text.f2 +f2: + .loc 1 2 0 + nop