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 @@ -196,11 +196,21 @@ // A collection of MCDwarfLineEntry for each section. MCLineDivisionMap MCLineDivisions; + // Points to the last end label of the function ranges. + MCSymbol *EndLabel = nullptr; + public: // Returns the collection of MCDwarfLineEntry for a given Compile Unit ID. const MCLineDivisionMap &getMCLineEntries() const { return MCLineDivisions; } + + // Update the end label from the end of a function range. + // This needs to be called in order to point to the end label for the CU. + void updateEndLabel(MCSymbol *FuncEndLabel) { EndLabel = FuncEndLabel; } + + // Get the end label for the CU. + MCSymbol *getEndLabel() const { return EndLabel; } }; struct MCDwarfLineTableParams { diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h --- a/llvm/include/llvm/MC/MCObjectStreamer.h +++ b/llvm/include/llvm/MC/MCObjectStreamer.h @@ -148,7 +148,8 @@ 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) override; void emitDwarfAdvanceFrameAddr(const MCSymbol *LastLabel, const MCSymbol *Label); void emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -1106,7 +1106,8 @@ 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) {} /// 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/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 @@ -612,7 +612,7 @@ DenseSet &ProcessedVars); /// Build the location list for all DBG_VALUEs in the - /// function that describe the same variable. If the resulting + /// function that describe the same variable. If the resulting /// list has only one entry that is valid for entire variable's /// scope return true. bool buildLocationList(SmallVectorImpl &DebugLoc, @@ -632,6 +632,9 @@ /// Gather and emit post-function debug information. void endFunctionImpl(const MachineFunction *MF) override; + /// Get Dwarf compile unit ID. + unsigned getDwarfCompileUnitID(const MachineFunction *MF); + void skippedNonDebugFunction() override; public: 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 @@ -2147,18 +2147,26 @@ DwarfCompileUnit &CU = getOrCreateDwarfCompileUnit(SP->getUnit()); + Asm->OutStreamer->getContext().setDwarfCompileUnitID( + getDwarfCompileUnitID(MF)); + + // Record beginning of function. + PrologEndLoc = emitInitialLocDirective( + *MF, Asm->OutStreamer->getContext().getDwarfCompileUnitID()); +} + +unsigned DwarfDebug::getDwarfCompileUnitID(const MachineFunction *MF) { + auto *SP = MF->getFunction().getSubprogram(); + DwarfCompileUnit &CU = getOrCreateDwarfCompileUnit(SP->getUnit()); + // Set DwarfDwarfCompileUnitID in MCContext to the Compile Unit this function // belongs to so that we add to the correct per-cu line table in the // non-asm case. if (Asm->OutStreamer->hasRawTextSupport()) // Use a single line table if we are generating assembly. - Asm->OutStreamer->getContext().setDwarfCompileUnitID(0); + return 0; else - Asm->OutStreamer->getContext().setDwarfCompileUnitID(CU.getUniqueID()); - - // Record beginning of function. - PrologEndLoc = emitInitialLocDirective( - *MF, Asm->OutStreamer->getContext().getDwarfCompileUnitID()); + return CU.getUniqueID(); } void DwarfDebug::skippedNonDebugFunction() { @@ -2193,9 +2201,16 @@ // Add the range of this function to the list of ranges for the CU. // With basic block sections, add ranges for all basic block sections. - for (const auto &R : Asm->MBBSectionRanges) + for (const auto &R : Asm->MBBSectionRanges) { TheCU.addRange({R.second.BeginLabel, R.second.EndLabel}); + // Update the end label in place to the CU's line table. + Asm->OutStreamer->getContext() + .getMCDwarfLineTable(getDwarfCompileUnitID(MF)) + .getMCLineSections() + .updateEndLabel(R.second.EndLabel); + } + // Under -gmlt, skip building the subprogram if there are no inlined // subroutines inside it. But with -fdebug-info-for-profiling, the subprogram // is still needed as we need its source location. diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -393,7 +393,8 @@ 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, @@ -2403,7 +2404,8 @@ } 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 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 @@ -23,6 +23,7 @@ #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" @@ -162,9 +163,10 @@ // This emits the Dwarf line table for the specified section from the entries // in the LineSection. // -static inline void emitDwarfLineTable( - MCStreamer *MCOS, MCSection *Section, - const MCLineSection::MCDwarfLineEntryCollection &LineEntries) { +static inline void +emitDwarfLineTable(MCStreamer *MCOS, MCSection *Section, + const MCLineSection::MCDwarfLineEntryCollection &LineEntries, + MCSymbol *EndLabel) { unsigned FileNum = 1; unsigned LastLine = 1; unsigned Column = 0; @@ -227,7 +229,7 @@ } // Generate DWARF line end entry. - MCOS->emitDwarfLineEndEntry(Section, LastLabel); + MCOS->emitDwarfLineEndEntry(Section, LastLabel, EndLabel); } // @@ -522,7 +524,8 @@ // Put out the line tables. for (const auto &LineSec : MCLineSections.getMCLineEntries()) - emitDwarfLineTable(MCOS, LineSec.first, LineSec.second); + emitDwarfLineTable(MCOS, LineSec.first, LineSec.second, + MCLineSections.getEndLabel()); // This is the end of the section, so set the value of the symbol at the end // of this section (that was used in a previous expression). diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -503,12 +503,21 @@ } 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 + MCSymbol *LastLabel, + MCSymbol *EndLabel) { + // Emit a DW_LNE_end_sequence. When EndLabel is provided, + // check if the address delta can be fully resolved to be used for it. Or, + // 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 // DW_LNE_end_sequence. - MCSymbol *SectionEnd = endSection(Section); + auto *EndSequence = endSection(Section); + if (EndLabel && LastLabel) { + auto &Assembler = getAssembler(); + auto &Writer = Assembler.getWriter(); + if (Writer.isSymbolRefDifferenceFullyResolvedImpl(Assembler, *LastLabel, + *EndLabel, false)) + EndSequence = EndLabel; + } // Switch back the dwarf line section, in case endSection had to switch the // section. @@ -516,7 +525,7 @@ SwitchSection(Ctx.getObjectFileInfo()->getDwarfLineSection()); const MCAsmInfo *AsmInfo = Ctx.getAsmInfo(); - emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, SectionEnd, + emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, EndSequence, AsmInfo->getCodePointerSize()); } diff --git a/llvm/test/DebugInfo/Inputs/lto-debugline-foo.ll b/llvm/test/DebugInfo/Inputs/lto-debugline-foo.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Inputs/lto-debugline-foo.ll @@ -0,0 +1,45 @@ +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx11.0.0" + +@g = local_unnamed_addr global i32 0, align 4, !dbg !0 + +define i32 @foo(i32 %0) !dbg !14 { + call void @llvm.dbg.value(metadata i32 %0, metadata !18, metadata !DIExpression()), !dbg !19 + %2 = sub i32 3, %0, !dbg !20 + %3 = load i32, i32* @g, align 4, !dbg !21 + %4 = add nsw i32 %3, 1, !dbg !21 + store i32 %4, i32* @g, align 4, !dbg !21 + %5 = add nsw i32 %2, %3, !dbg !26 + ret i32 %5, !dbg !27 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10, !11, !12} +!llvm.ident = !{!13} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "LLVM", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None, sysroot: "/") +!3 = !DIFile(filename: "lto-debugline-foo.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 7, !"PIC Level", i32 2} +!11 = !{i32 7, !"uwtable", i32 1} +!12 = !{i32 7, !"frame-pointer", i32 2} +!13 = !{!"clang version 14.0.0"} +!14 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !15, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17) +!15 = !DISubroutineType(types: !16) +!16 = !{!6, !6} +!17 = !{!18} +!18 = !DILocalVariable(name: "a", arg: 1, scope: !14, file: !3, line: 3, type: !6) +!19 = !DILocation(line: 0, scope: !14) +!20 = !DILocation(line: 4, column: 11, scope: !14) +!21 = !DILocation(line: 4, column: 18, scope: !14) +!26 = !DILocation(line: 4, column: 15, scope: !14) +!27 = !DILocation(line: 4, column: 2, scope: !14) diff --git a/llvm/test/DebugInfo/lto-debugline-main.ll b/llvm/test/DebugInfo/lto-debugline-main.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/lto-debugline-main.ll @@ -0,0 +1,57 @@ +; RUN: opt -o %t1.bc < %s +; RUN: opt -o %t2.bc < %p/Inputs/lto-debugline-foo.ll +; RUN: llvm-lto -enable-lto-internalization=false %t1.bc %t2.bc -o %t3 +; RUN: llvm-dwarfdump --all %t3 | FileCheck %s + +; CHECK: DW_AT_name ("lto-debugline-foo.c") +; CHECK: DW_AT_high_pc ([[EndSeq1:.*]]) +; CHECK: DW_AT_name ("lto-debugline-main.c") +; CHECK: DW_AT_high_pc ([[EndSeq2:.*]]) + +; Expect the line table ends with the highest address for each CU. +; CHECK: [[EndSeq1]] [[T1:.*]] end_sequence +; CHECK: [[EndSeq2]] [[T2:.*]] end_sequence + +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx11.0.0" + +@g = external global i32, align 4 + +define i32 @main() !dbg !10 { + %1 = load i32, i32* @g, align 4, !dbg !14 + %2 = add nsw i32 %1, 1, !dbg !14 + store i32 %2, i32* @g, align 4, !dbg !14 + %3 = tail call i32 @foo(i32 2), !dbg !19 + %4 = tail call i32 @foo(i32 3), !dbg !20 + %5 = add nsw i32 %4, %3, !dbg !21 + ret i32 %5, !dbg !22 +} + +declare !dbg !23 i32 @foo(i32) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "LLVM", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None, sysroot: "/") +!1 = !DIFile(filename: "lto-debugline-main.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 1} +!8 = !{i32 7, !"frame-pointer", i32 2} +!9 = !{!"clang version 14.0.0"} +!10 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 4, type: !11, scopeLine: 4, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!11 = !DISubroutineType(types: !12) +!12 = !{!13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !DILocation(line: 5, column: 2, scope: !10) +!19 = !DILocation(line: 6, column: 9, scope: !10) +!20 = !DILocation(line: 6, column: 18, scope: !10) +!21 = !DILocation(line: 6, column: 16, scope: !10) +!22 = !DILocation(line: 6, column: 2, scope: !10) +!23 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !24, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!24 = !DISubroutineType(types: !25) +!25 = !{!13, !13}