Skip to content

Commit bd3f96d

Browse files
author
Alex B
committed
[DebugInfo][DWARF] Emit Per-Function Line Table Offsets and End Sequences
1 parent 8c4331c commit bd3f96d

File tree

7 files changed

+169
-6
lines changed

7 files changed

+169
-6
lines changed

llvm/include/llvm/BinaryFormat/Dwarf.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ HANDLE_DW_AT(0x3e08, LLVM_ptrauth_isa_pointer, 0, LLVM)
618618
HANDLE_DW_AT(0x3e09, LLVM_ptrauth_authenticates_null_values, 0, LLVM)
619619
HANDLE_DW_AT(0x3e0a, LLVM_ptrauth_authentication_mode, 0, LLVM)
620620
HANDLE_DW_AT(0x3e0b, LLVM_num_extra_inhabitants, 0, LLVM)
621+
HANDLE_DW_AT(0x3e0c, LLVM_stmt_sequence, 0, LLVM)
621622

622623
// Apple extensions.
623624

llvm/include/llvm/MC/MCDwarf.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ class MCDwarfLoc {
123123
friend class MCContext;
124124
friend class MCDwarfLineEntry;
125125

126+
// DwarfDebug::endFunctionImpl needs to construct MCDwarfLoc(IsEndOfFunction)
127+
friend class DwarfDebug;
128+
126129
MCDwarfLoc(unsigned fileNum, unsigned line, unsigned column, unsigned flags,
127130
unsigned isa, unsigned discriminator)
128131
: FileNum(fileNum), Line(line), Column(column), Flags(flags), Isa(isa),
@@ -239,7 +242,7 @@ class MCLineSection {
239242

240243
// Add an end entry by cloning the last entry, if exists, for the section
241244
// the given EndLabel belongs to. The label is replaced by the given EndLabel.
242-
void addEndEntry(MCSymbol *EndLabel);
245+
void addEndEntry(MCSymbol *EndLabel, bool generatingFuncLineTableOffsets);
243246

244247
using MCDwarfLineEntryCollection = std::vector<MCDwarfLineEntry>;
245248
using iterator = MCDwarfLineEntryCollection::iterator;

llvm/include/llvm/MC/MCStreamer.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,15 @@ class MCStreamer {
251251
/// discussion for future inclusion.
252252
bool AllowAutoPadding = false;
253253

254+
// Flag specifying weather functions will have an offset into the line table
255+
// where the line data for that function starts
256+
bool GenerateFuncLineTableOffsets = false;
257+
258+
// Symbol that tracks the stream symbol for first line of the current function
259+
// being generated. This symbol can be used to reference where the line
260+
// entries for the function start in the generated line table.
261+
MCSymbol *CurrentFuncFirstLineStreamSym;
262+
254263
protected:
255264
MCFragment *CurFrag = nullptr;
256265

@@ -313,6 +322,24 @@ class MCStreamer {
313322
void setAllowAutoPadding(bool v) { AllowAutoPadding = v; }
314323
bool getAllowAutoPadding() const { return AllowAutoPadding; }
315324

325+
void setGenerateFuncLineTableOffsets(bool v) {
326+
GenerateFuncLineTableOffsets = v;
327+
}
328+
bool getGenerateFuncLineTableOffsets() const {
329+
return GenerateFuncLineTableOffsets;
330+
}
331+
332+
// Use the below functions to track the symbol that points to the current
333+
// function's line info in the output stream.
334+
void beginFunction() { CurrentFuncFirstLineStreamSym = nullptr; }
335+
void emittedLineStreamSym(MCSymbol *StreamSym) {
336+
if (!CurrentFuncFirstLineStreamSym)
337+
CurrentFuncFirstLineStreamSym = StreamSym;
338+
}
339+
MCSymbol *getCurrentFuncFirstLineStreamSym() {
340+
return CurrentFuncFirstLineStreamSym;
341+
}
342+
316343
/// When emitting an object file, create and emit a real label. When emitting
317344
/// textual assembly, this should do nothing to avoid polluting our output.
318345
virtual MCSymbol *emitCFILabel();

llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,14 @@ DIE &DwarfCompileUnit::updateSubprogramScopeDIE(const DISubprogram *SP) {
526526
*DD->getCurrentFunction()))
527527
addFlag(*SPDie, dwarf::DW_AT_APPLE_omit_frame_ptr);
528528

529+
if (Asm->OutStreamer->getGenerateFuncLineTableOffsets() &&
530+
Asm->OutStreamer->getCurrentFuncFirstLineStreamSym()) {
531+
addSectionLabel(
532+
*SPDie, dwarf::DW_AT_LLVM_stmt_sequence,
533+
Asm->OutStreamer->getCurrentFuncFirstLineStreamSym(),
534+
Asm->getObjFileLowering().getDwarfLineSection()->getBeginSymbol());
535+
}
536+
529537
// Only include DW_AT_frame_base in full debug info
530538
if (!includeMinimalInlineScopes()) {
531539
const TargetFrameLowering *TFI = Asm->MF->getSubtarget().getFrameLowering();

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ static cl::opt<DwarfDebug::MinimizeAddrInV5> MinimizeAddrInV5Option(
170170
"Stuff")),
171171
cl::init(DwarfDebug::MinimizeAddrInV5::Default));
172172

173+
static cl::opt<bool> EmitFuncLineTableOffsetsOption(
174+
"emit-func-debug-line-table-offsets", cl::Hidden,
175+
cl::desc("Include line table offset in function's debug info and emit end "
176+
"sequence after each function's line data."),
177+
cl::init(false));
178+
173179
static constexpr unsigned ULEB128PadSize = 4;
174180

175181
void DebugLocDwarfExpression::emitOp(uint8_t Op, const char *Comment) {
@@ -441,6 +447,8 @@ DwarfDebug::DwarfDebug(AsmPrinter *A)
441447
Asm->OutStreamer->getContext().setDwarfVersion(DwarfVersion);
442448
Asm->OutStreamer->getContext().setDwarfFormat(Dwarf64 ? dwarf::DWARF64
443449
: dwarf::DWARF32);
450+
Asm->OutStreamer->setGenerateFuncLineTableOffsets(
451+
EmitFuncLineTableOffsetsOption);
444452
}
445453

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

2233+
// Notify the streamer that we are beginning a function - this will reset the
2234+
// label pointing to the currently generated function's first line entry
2235+
Asm->OutStreamer->beginFunction();
2236+
22252237
DwarfCompileUnit &CU = getOrCreateDwarfCompileUnit(SP->getUnit());
22262238

22272239
Asm->OutStreamer->getContext().setDwarfCompileUnitID(
@@ -2250,7 +2262,8 @@ void DwarfDebug::terminateLineTable(const DwarfCompileUnit *CU) {
22502262
getDwarfCompileUnitIDForLineTable(*CU));
22512263
// Add the last range label for the given CU.
22522264
LineTable.getMCLineSections().addEndEntry(
2253-
const_cast<MCSymbol *>(CURanges.back().End));
2265+
const_cast<MCSymbol *>(CURanges.back().End),
2266+
EmitFuncLineTableOffsetsOption);
22542267
}
22552268

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

2359+
// If we're emitting line table offsets, we also need to emit an end label
2360+
// after all function's line entries
2361+
if (EmitFuncLineTableOffsetsOption) {
2362+
MCSymbol *LineSym = Asm->OutStreamer->getContext().createTempSymbol();
2363+
Asm->OutStreamer->emitLabel(LineSym);
2364+
MCDwarfLoc DwarfLoc(
2365+
1, 1, 0, DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0, 0, 0);
2366+
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc);
2367+
Asm->OutStreamer->getContext()
2368+
.getMCDwarfLineTable(
2369+
Asm->OutStreamer->getContext().getDwarfCompileUnitID())
2370+
.getMCLineSections()
2371+
.addLineEntry(LineEntry, Asm->OutStreamer->getCurrentSectionOnly());
2372+
}
2373+
23462374
// Clear debug info
23472375
// Ownership of DbgVariables is a bit subtle - ScopeVariables owns all the
23482376
// DbgVariables except those that are also in AbstractVariables (since they

llvm/lib/MC/MCDwarf.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,17 @@ void MCDwarfLineEntry::make(MCStreamer *MCOS, MCSection *Section) {
104104
// Get the current .loc info saved in the context.
105105
const MCDwarfLoc &DwarfLoc = MCOS->getContext().getCurrentDwarfLoc();
106106

107+
MCSymbol *LineStreamLabel = nullptr;
108+
// If functions need offsets into the generated line table, then we need to
109+
// create a label referencing where the line was generated in the output
110+
// stream
111+
if (MCOS->getGenerateFuncLineTableOffsets()) {
112+
LineStreamLabel = MCOS->getContext().createTempSymbol();
113+
MCOS->emittedLineStreamSym(LineStreamLabel);
114+
}
115+
107116
// Create a (local) line entry with the symbol and the current .loc info.
108-
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc);
117+
MCDwarfLineEntry LineEntry(LineSym, DwarfLoc, LineStreamLabel);
109118

110119
// clear DwarfLocSeen saying the current .loc info is now used.
111120
MCOS->getContext().clearDwarfLocSeen();
@@ -145,7 +154,8 @@ makeStartPlusIntExpr(MCContext &Ctx, const MCSymbol &Start, int IntVal) {
145154
return Res;
146155
}
147156

148-
void MCLineSection::addEndEntry(MCSymbol *EndLabel) {
157+
void MCLineSection::addEndEntry(MCSymbol *EndLabel,
158+
bool generatingFuncLineTableOffsets) {
149159
auto *Sec = &EndLabel->getSection();
150160
// The line table may be empty, which we should skip adding an end entry.
151161
// There are two cases:
@@ -158,8 +168,12 @@ void MCLineSection::addEndEntry(MCSymbol *EndLabel) {
158168
if (I != MCLineDivisions.end()) {
159169
auto &Entries = I->second;
160170
auto EndEntry = Entries.back();
161-
EndEntry.setEndLabel(EndLabel);
162-
Entries.push_back(EndEntry);
171+
// If generatingFuncLineTableOffsets is set, then we already generated an
172+
// end label at the end of the last function, so skip generating another one
173+
if (!generatingFuncLineTableOffsets) {
174+
EndEntry.setEndLabel(EndLabel);
175+
Entries.push_back(EndEntry);
176+
}
163177
}
164178
}
165179

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
; RUN: llc -mtriple=i686-w64-mingw32 -o %t -filetype=obj %s
2+
; RUN: llvm-dwarfdump -v -all %t | FileCheck %s -check-prefix=NO_STMT_SEQ
3+
4+
; RUN: llc -mtriple=i686-w64-mingw32 -o %t -filetype=obj %s -emit-func-debug-line-table-offsets
5+
; RUN: llvm-dwarfdump -v -all %t | FileCheck %s -check-prefix=STMT_SEQ
6+
7+
; NO_STMT_SEQ-NOT: DW_AT_LLVM_stmt_sequence
8+
9+
; STMT_SEQ: [[[ABBREV_CODE:[0-9]+]]] DW_TAG_subprogram
10+
; STMT_SEQ: DW_AT_LLVM_stmt_sequence DW_FORM_sec_offset
11+
; STMT_SEQ: DW_TAG_subprogram [[[ABBREV_CODE]]]
12+
; STMT_SEQ: DW_AT_LLVM_stmt_sequence [DW_FORM_sec_offset] (0x00000028)
13+
; STMT_SEQ: DW_AT_name {{.*}}func01
14+
; STMT_SEQ: DW_TAG_subprogram [[[ABBREV_CODE]]]
15+
; STMT_SEQ: DW_AT_LLVM_stmt_sequence [DW_FORM_sec_offset] (0x00000033)
16+
; STMT_SEQ: DW_AT_name {{.*}}main
17+
18+
;; Check that the line table starts at 0x00000028 (first function)
19+
; STMT_SEQ: Address Line Column File ISA Discriminator OpIndex Flags
20+
; STMT_SEQ-NEXT: ------------------ ------ ------ ------ --- ------------- ------- -------------
21+
; STMT_SEQ-NEXT: 0x00000028: 00 DW_LNE_set_address (0x00000006)
22+
23+
;; Check that we have an 'end_sequence' just before the next function (0x00000033)
24+
; STMT_SEQ: 0x0000000000000006 1 0 1 0 0 0 is_stmt end_sequence
25+
; STMT_SEQ-NEXT: 0x00000033: 00 DW_LNE_set_address (0x00000027)
26+
27+
;; Check that the end of the line table still has an 'end_sequence'
28+
; STMT_SEQ 0x00000049: 00 DW_LNE_end_sequence
29+
; STMT_SEQ-NEXT 0x0000000000000027 6 3 1 0 0 0 end_sequence
30+
31+
32+
; generated from:
33+
; clang -g -S -emit-llvm test.c -o test.ll
34+
; ======= test.c ======
35+
; int func01() {
36+
; return 1;
37+
; }
38+
; int main() {
39+
; return 0;
40+
; }
41+
; =====================
42+
43+
44+
; ModuleID = 'test.c'
45+
source_filename = "test.c"
46+
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
47+
target triple = "arm64-apple-macosx14.0.0"
48+
49+
; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
50+
define i32 @func01() #0 !dbg !9 {
51+
ret i32 1, !dbg !13
52+
}
53+
54+
; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
55+
define i32 @main() #0 !dbg !14 {
56+
%1 = alloca i32, align 4
57+
store i32 0, ptr %1, align 4
58+
ret i32 0, !dbg !15
59+
}
60+
61+
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" }
62+
63+
!llvm.dbg.cu = !{!0}
64+
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
65+
!llvm.ident = !{!8}
66+
67+
!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")
68+
!1 = !DIFile(filename: "test.c", directory: "/tmp/clang_test")
69+
!2 = !{i32 7, !"Dwarf Version", i32 4}
70+
!3 = !{i32 2, !"Debug Info Version", i32 3}
71+
!4 = !{i32 1, !"wchar_size", i32 4}
72+
!5 = !{i32 8, !"PIC Level", i32 2}
73+
!6 = !{i32 7, !"uwtable", i32 1}
74+
!7 = !{i32 7, !"frame-pointer", i32 1}
75+
!8 = !{!"Homebrew clang version 17.0.6"}
76+
!9 = distinct !DISubprogram(name: "func01", scope: !1, file: !1, line: 1, type: !10, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0)
77+
!10 = !DISubroutineType(types: !11)
78+
!11 = !{!12}
79+
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
80+
!13 = !DILocation(line: 2, column: 3, scope: !9)
81+
!14 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 5, type: !10, scopeLine: 5, spFlags: DISPFlagDefinition, unit: !0)
82+
!15 = !DILocation(line: 6, column: 3, scope: !14)

0 commit comments

Comments
 (0)