Skip to content

[DebugInfo] Add flag to enable function-level debug line attribution #93985

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.def
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,8 @@ HANDLE_DW_AT(0x2904, GO_runtime_type, 0, GO)

HANDLE_DW_AT(0x3210, UPC_threads_scaled, 0, UPC)

HANDLE_DW_AT(0x3600, META_stmt_sequence, 0, META)

HANDLE_DW_AT(0x393e, IBM_wsa_addr, 0, IBM)
HANDLE_DW_AT(0x393f, IBM_home_location, 0, IBM)
HANDLE_DW_AT(0x3940, IBM_alt_srcview, 0, IBM)
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ enum LLVMConstants : uint32_t {
DWARF_VENDOR_PGI,
DWARF_VENDOR_SUN,
DWARF_VENDOR_UPC,
DWARF_VENDOR_META,
///\}
};

Expand Down
21 changes: 18 additions & 3 deletions llvm/include/llvm/MC/MCDwarf.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ class MCDwarfLoc {
private: // MCContext manages these
friend class MCContext;
friend class MCDwarfLineEntry;
// DwarfDebug::endFunctionImpl needs to construct MCDwarfLoc(IsEndOfFunction)
friend class DwarfDebug;

MCDwarfLoc(unsigned fileNum, unsigned line, unsigned column, unsigned flags,
unsigned isa, unsigned discriminator)
Expand Down Expand Up @@ -194,14 +196,27 @@ 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,
bool isEndOfFunction = false,
MCSymbol *streamLabel = nullptr)
: MCDwarfLoc(loc), Label(label), IsEndOfFunction(isEndOfFunction),
StreamLabel(streamLabel) {}

MCSymbol *getLabel() const { return Label; }

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

// This indicates that the current line entry denotes the end of a function,
// it is used to emit a DW_LNE_end_sequnece to reset the state machine
// registers.
bool IsEndOfFunction;

// Optional symbol to be emitted just before the line is written into the
// output stream. It can be used to reference the position of the start of
// this line's data in the output stream.
MCSymbol *StreamLabel;

// Override the label with the given EndLabel.
void setEndLabel(MCSymbol *EndLabel) {
Label = EndLabel;
Expand All @@ -227,7 +242,7 @@ class MCLineSection {

// 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);
void addEndEntry(MCSymbol *EndLabel, bool generatingFuncLineTableOffsets);

using MCDwarfLineEntryCollection = std::vector<MCDwarfLineEntry>;
using iterator = MCDwarfLineEntryCollection::iterator;
Expand Down
27 changes: 27 additions & 0 deletions llvm/include/llvm/MC/MCStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,15 @@ class MCStreamer {
/// discussion for future inclusion.
bool AllowAutoPadding = false;

// Flag specyfing weather functions will have an offset into the line table
// where the line data for that function starts
bool GenerateFuncLineTableOffsets = false;

// Symbol that tracks the stream symbol for first line of the current function
// being generated. This symbol can be used to reference where the line
// entries for the function start in the generated line table.
MCSymbol *CurrentFuncFirstLineStreamSym;

protected:
MCStreamer(MCContext &Ctx);

Expand Down Expand Up @@ -310,6 +319,24 @@ class MCStreamer {
void setAllowAutoPadding(bool v) { AllowAutoPadding = v; }
bool getAllowAutoPadding() const { return AllowAutoPadding; }

void setGenerateFuncLineTableOffsets(bool v) {
GenerateFuncLineTableOffsets = v;
}
bool getGenerateFuncLineTableOffsets() const {
return GenerateFuncLineTableOffsets;
}

// Use the below functions to track the symbol that points to the current
// function's line info in the output stream.
void beginFunction() { CurrentFuncFirstLineStreamSym = nullptr; }
void emittedLineStreamSym(MCSymbol *StreamSym) {
if (!CurrentFuncFirstLineStreamSym)
CurrentFuncFirstLineStreamSym = StreamSym;
}
MCSymbol *getCurrentFuncFirstLineStreamSym() {
return CurrentFuncFirstLineStreamSym;
}

/// When emitting an object file, create and emit a real label. When emitting
/// textual assembly, this should do nothing to avoid polluting our output.
virtual MCSymbol *emitCFILabel();
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,14 @@ DIE &DwarfCompileUnit::updateSubprogramScopeDIE(const DISubprogram *SP) {
*DD->getCurrentFunction()))
addFlag(*SPDie, dwarf::DW_AT_APPLE_omit_frame_ptr);

if (Asm->OutStreamer->getGenerateFuncLineTableOffsets() &&
Asm->OutStreamer->getCurrentFuncFirstLineStreamSym()) {
addSectionLabel(
*SPDie, dwarf::DW_AT_META_stmt_sequence,
Asm->OutStreamer->getCurrentFuncFirstLineStreamSym(),
Asm->getObjFileLowering().getDwarfLineSection()->getBeginSymbol());
}

// Only include DW_AT_frame_base in full debug info
if (!includeMinimalInlineScopes()) {
const TargetFrameLowering *TFI = Asm->MF->getSubtarget().getFrameLowering();
Expand Down
30 changes: 29 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ static cl::opt<DwarfDebug::MinimizeAddrInV5> MinimizeAddrInV5Option(
"Stuff")),
cl::init(DwarfDebug::MinimizeAddrInV5::Default));

static cl::opt<bool> EmitFuncLineTableOffsetsOption(
"emit-func-debug-line-table-offsets", cl::Hidden,
cl::desc("Include line table offset in function's debug info and emit end "
"sequence after each function's line data."),
cl::init(false));

static constexpr unsigned ULEB128PadSize = 4;

void DebugLocDwarfExpression::emitOp(uint8_t Op, const char *Comment) {
Expand Down Expand Up @@ -440,6 +446,8 @@ DwarfDebug::DwarfDebug(AsmPrinter *A)
Asm->OutStreamer->getContext().setDwarfVersion(DwarfVersion);
Asm->OutStreamer->getContext().setDwarfFormat(Dwarf64 ? dwarf::DWARF64
: dwarf::DWARF32);
Asm->OutStreamer->setGenerateFuncLineTableOffsets(
EmitFuncLineTableOffsetsOption);
}

// Define out of line so we don't have to include DwarfUnit.h in DwarfDebug.h.
Expand Down Expand Up @@ -2222,6 +2230,10 @@ void DwarfDebug::beginFunctionImpl(const MachineFunction *MF) {
if (SP->getUnit()->getEmissionKind() == DICompileUnit::NoDebug)
return;

// Notify the streamer that we are beginning a function - this will reset the
// label pointing to the currently generated function's first line entry
Asm->OutStreamer->beginFunction();

DwarfCompileUnit &CU = getOrCreateDwarfCompileUnit(SP->getUnit());

Asm->OutStreamer->getContext().setDwarfCompileUnitID(
Expand Down Expand Up @@ -2250,7 +2262,8 @@ void DwarfDebug::terminateLineTable(const DwarfCompileUnit *CU) {
getDwarfCompileUnitIDForLineTable(*CU));
// Add the last range label for the given CU.
LineTable.getMCLineSections().addEndEntry(
const_cast<MCSymbol *>(CURanges.back().End));
const_cast<MCSymbol *>(CURanges.back().End),
EmitFuncLineTableOffsetsOption);
}

void DwarfDebug::skippedNonDebugFunction() {
Expand Down Expand Up @@ -2343,6 +2356,21 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) {
// Construct call site entries.
constructCallSiteEntryDIEs(*SP, TheCU, ScopeDIE, *MF);

// If we're emitting line table offsets, we also need to emit an end label
// after all function's line entries
if (EmitFuncLineTableOffsetsOption) {
MCSymbol *LineSym = Asm->OutStreamer->getContext().createTempSymbol();
Asm->OutStreamer->emitLabel(LineSym);
MCDwarfLoc DwarfLoc(
1, 1, 0, DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0, 0, 0);
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc, /*IsEndOfFunction*/ true);
Asm->OutStreamer->getContext()
.getMCDwarfLineTable(
Asm->OutStreamer->getContext().getDwarfCompileUnitID())
.getMCLineSections()
.addLineEntry(LineEntry, Asm->OutStreamer->getCurrentSectionOnly());
}

// Clear debug info
// Ownership of DbgVariables is a bit subtle - ScopeVariables owns all the
// DbgVariables except those that are also in AbstractVariables (since they
Expand Down
28 changes: 23 additions & 5 deletions llvm/lib/MC/MCDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,18 @@ void MCDwarfLineEntry::make(MCStreamer *MCOS, MCSection *Section) {
// Get the current .loc info saved in the context.
const MCDwarfLoc &DwarfLoc = MCOS->getContext().getCurrentDwarfLoc();

MCSymbol *StreamLabel = nullptr;
// If functions need offsets into the generated line table, then we need to
// create a label referencing where the line was generated in the output
// stream
if (MCOS->getGenerateFuncLineTableOffsets()) {
StreamLabel = MCOS->getContext().createTempSymbol();
MCOS->emittedLineStreamSym(StreamLabel);
}

// Create a (local) line entry with the symbol and the current .loc info.
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc);
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc, /*isEndOfFunction=*/false,
StreamLabel);

// clear DwarfLocSeen saying the current .loc info is now used.
MCOS->getContext().clearDwarfLocSeen();
Expand Down Expand Up @@ -144,7 +154,8 @@ makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) {
return Res;
}

void MCLineSection::addEndEntry(MCSymbol *EndLabel) {
void MCLineSection::addEndEntry(MCSymbol *EndLabel,
bool generatingFuncLineTableOffsets) {
auto *Sec = &EndLabel->getSection();
// The line table may be empty, which we should skip adding an end entry.
// There are two cases:
Expand All @@ -157,8 +168,12 @@ void MCLineSection::addEndEntry(MCSymbol *EndLabel) {
if (I != MCLineDivisions.end()) {
auto &Entries = I->second;
auto EndEntry = Entries.back();
EndEntry.setEndLabel(EndLabel);
Entries.push_back(EndEntry);
// If generatingFuncLineTableOffsets is set, then we already generated an
// end label at the end of the last function, so skip generating another one
if (!generatingFuncLineTableOffsets) {
EndEntry.setEndLabel(EndLabel);
Entries.push_back(EndEntry);
}
}
}

Expand Down Expand Up @@ -187,8 +202,11 @@ void MCDwarfLineTable::emitOne(
bool EndEntryEmitted = false;
for (const MCDwarfLineEntry &LineEntry : LineEntries) {
MCSymbol *Label = LineEntry.getLabel();
if (LineEntry.StreamLabel && MCOS->getGenerateFuncLineTableOffsets()) {
MCOS->emitLabel(LineEntry.StreamLabel);
}
const MCAsmInfo *asmInfo = MCOS->getContext().getAsmInfo();
if (LineEntry.IsEndEntry) {
if (LineEntry.IsEndEntry || LineEntry.IsEndOfFunction) {
MCOS->emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, Label,
asmInfo->getCodePointerSize());
init();
Expand Down
82 changes: 82 additions & 0 deletions llvm/test/DebugInfo/X86/DW_AT_META_stmt_seq_sec_offset.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
; RUN: llc -mtriple=i686-w64-mingw32 -o %t -filetype=obj %s
; RUN: llvm-dwarfdump -v -all %t | FileCheck %s -check-prefix=NO_STMT_SEQ

; RUN: llc -mtriple=i686-w64-mingw32 -o %t -filetype=obj %s -emit-func-debug-line-table-offsets
; RUN: llvm-dwarfdump -v -all %t | FileCheck %s -check-prefix=STMT_SEQ

; NO_STMT_SEQ-NOT: DW_AT_META_stmt_sequence

; STMT_SEQ: [[[ABBREV_CODE:[0-9]+]]] DW_TAG_subprogram
; STMT_SEQ: DW_AT_META_stmt_sequence DW_FORM_sec_offset
; STMT_SEQ: DW_TAG_subprogram [[[ABBREV_CODE]]]
; STMT_SEQ: DW_AT_META_stmt_sequence [DW_FORM_sec_offset] (0x00000028)
; STMT_SEQ: DW_AT_name {{.*}}func01
; STMT_SEQ: DW_TAG_subprogram [[[ABBREV_CODE]]]
; STMT_SEQ: DW_AT_META_stmt_sequence [DW_FORM_sec_offset] (0x00000038)
; STMT_SEQ: DW_AT_name {{.*}}main

;; Check that the line table starts at 0x00000028 (first function)
; STMT_SEQ: Address Line Column File ISA Discriminator OpIndex Flags
; STMT_SEQ-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
; STMT_SEQ-NEXT: 0x00000028: 05 DW_LNS_set_column (3)

;; Check that we have an 'end_sequence' just before the next function (0x00000038)
; STMT_SEQ: 0x0000000000000006 2 3 1 0 0 0 is_stmt end_sequence
; STMT_SEQ-NEXT: 0x00000038: 00 DW_LNE_set_address (0x00000010)

;; Check that the end of the line table still has an 'end_sequence'
; STMT_SEQ 0x00000049: 00 DW_LNE_end_sequence
; STMT_SEQ-NEXT 0x0000000000000027 6 3 1 0 0 0 end_sequence


; generated from:
; clang -g -S -emit-llvm test.c -o test.ll
; ======= test.c ======
; int func01() {
; return 1;
; }
; int main() {
; return 0;
; }
; =====================


; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "arm64-apple-macosx14.0.0"

; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
define i32 @func01() #0 !dbg !9 {
ret i32 1, !dbg !13
}

; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
define i32 @main() #0 !dbg !14 {
%1 = alloca i32, align 4
store i32 0, ptr %1, align 4
ret i32 0, !dbg !15
}

attributes #0 = { noinline nounwind optnone ssp uwtable(sync) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+crc,+dotprod,+fp-armv8,+fp16fml,+fullfp16,+lse,+neon,+ras,+rcpc,+rdm,+sha2,+sha3,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8.5a,+v8a,+zcm,+zcz" }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
!llvm.ident = !{!8}

!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "Homebrew clang version 17.0.6", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/Library/Developer/CommandLineTools/SDKs/MacOSX14.sdk", sdk: "MacOSX14.sdk")
!1 = !DIFile(filename: "test.c", directory: "/tmp/clang_test")
!2 = !{i32 7, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{i32 8, !"PIC Level", i32 2}
!6 = !{i32 7, !"uwtable", i32 1}
!7 = !{i32 7, !"frame-pointer", i32 1}
!8 = !{!"Homebrew clang version 17.0.6"}
!9 = distinct !DISubprogram(name: "func01", scope: !1, file: !1, line: 1, type: !10, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
!10 = !DISubroutineType(types: !11)
!11 = !{!12}
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !DILocation(line: 2, column: 3, scope: !9)
!14 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 5, type: !10, scopeLine: 5, spFlags: DISPFlagDefinition, unit: !0)
!15 = !DILocation(line: 6, column: 3, scope: !14)
Loading