Skip to content

Commit 89367a8

Browse files
committed
[dwarf] make dwarf fission compatible with RISCV relaxations 1/2
Currently, -gsplit-dwarf and -mrelax are incompatible options in Clang. The issue is that .dwo files should not contain any relocations, as they are not processed by the linker. However, relaxable code emits relocations in DWARF for debug ranges that reside in the .dwo file when DWARF fission is enabled. This patch makes DWARF fission compatible with RISC-V relaxations. It uses the StartxEndx DWARF forms in .debug_rnglists.dwo, which allow referencing addresses from .debug_addr instead of using absolute addresses. This approach eliminates relocations from .dwo files.
1 parent 4613ca9 commit 89367a8

File tree

4 files changed

+251
-5
lines changed

4 files changed

+251
-5
lines changed

llvm/include/llvm/MC/MCSymbol.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,8 @@ inline raw_ostream &operator<<(raw_ostream &OS, const MCSymbol &Sym) {
383383
return OS;
384384
}
385385

386+
bool isRangeRelaxable(const MCSymbol *Begin, const MCSymbol *End);
387+
386388
} // end namespace llvm
387389

388390
#endif // LLVM_MC_MCSYMBOL_H

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,6 +3304,7 @@ template <> struct DwarfRangeListTraits<DebugLocSpanList> {
33043304
static constexpr unsigned BaseAddressx = dwarf::DW_LLE_base_addressx;
33053305
static constexpr unsigned OffsetPair = dwarf::DW_LLE_offset_pair;
33063306
static constexpr unsigned StartxLength = dwarf::DW_LLE_startx_length;
3307+
static constexpr unsigned StartxEndx = dwarf::DW_LLE_startx_endx;
33073308
static constexpr unsigned EndOfList = dwarf::DW_LLE_end_of_list;
33083309

33093310
static StringRef StringifyRangeKind(unsigned Encoding) {
@@ -3320,6 +3321,7 @@ template <> struct DwarfRangeListTraits<RangeSpanList> {
33203321
static constexpr unsigned BaseAddressx = dwarf::DW_RLE_base_addressx;
33213322
static constexpr unsigned OffsetPair = dwarf::DW_RLE_offset_pair;
33223323
static constexpr unsigned StartxLength = dwarf::DW_RLE_startx_length;
3324+
static constexpr unsigned StartxEndx = dwarf::DW_RLE_startx_endx;
33233325
static constexpr unsigned EndOfList = dwarf::DW_RLE_end_of_list;
33243326

33253327
static StringRef StringifyRangeKind(unsigned Encoding) {
@@ -3355,7 +3357,8 @@ static void emitRangeList(DwarfDebug &DD, AsmPrinter *Asm, const Ranges &R,
33553357
bool BaseIsSet = false;
33563358
for (const auto &P : SectionRanges) {
33573359
auto *Base = CUBase;
3358-
if ((Asm->TM.getTargetTriple().isNVPTX() && DD.tuneForGDB())) {
3360+
if ((Asm->TM.getTargetTriple().isNVPTX() && DD.tuneForGDB()) ||
3361+
(DD.useSplitDwarf() && UseDwarf5 && P.first->isLinkerRelaxable())) {
33593362
// PTX does not support subtracting labels from the code section in the
33603363
// debug_loc section. To work around this, the NVPTX backend needs the
33613364
// compile unit to have no low_pc in order to have a zero base_address
@@ -3394,7 +3397,46 @@ static void emitRangeList(DwarfDebug &DD, AsmPrinter *Asm, const Ranges &R,
33943397
Asm->OutStreamer->emitIntValue(0, Size);
33953398
}
33963399

3397-
for (const auto *RS : P.second) {
3400+
if (DD.useSplitDwarf() && UseDwarf5) {
3401+
// In .dwo files, we must ensure no relocations are present. For
3402+
// .debug_ranges.dwo. this means that if there is at least one
3403+
// relocation between the start and end of a range, we must
3404+
// represent the range boundaries using indirect addresses from
3405+
// the .debug_addr section.
3406+
//
3407+
// The DWARFv5 specification (section 2.17.3) does not require
3408+
// range entries to be ordered. Therefore, we emit such range
3409+
// entries here to allow using more optimal formats (e.g.
3410+
// StartxLength) for other ranges.
3411+
3412+
auto RelaxableRanges = llvm::make_filter_range(P.second, [](auto &&RS) {
3413+
return llvm::isRangeRelaxable(RS->Begin, RS->End);
3414+
});
3415+
3416+
for (auto &&RS : RelaxableRanges) {
3417+
const auto *Begin = RS->Begin;
3418+
const auto *End = RS->End;
3419+
Asm->OutStreamer->AddComment(
3420+
DwarfRangeListTraits<Ranges>::StringifyRangeKind(
3421+
DwarfRangeListTraits<Ranges>::StartxEndx));
3422+
Asm->emitInt8(DwarfRangeListTraits<Ranges>::StartxEndx);
3423+
Asm->OutStreamer->AddComment(" start index");
3424+
Asm->emitULEB128(DD.getAddressPool().getIndex(Begin));
3425+
Asm->OutStreamer->AddComment(" end index");
3426+
Asm->emitULEB128(DD.getAddressPool().getIndex(End));
3427+
DwarfRangeListTraits<Ranges>::EmitPayload(DD, R.CU, *RS);
3428+
}
3429+
}
3430+
3431+
auto NonRelaxableRanges = llvm::make_filter_range(
3432+
P.second,
3433+
[HasRelaxableRanges = DD.useSplitDwarf() && UseDwarf5](auto &&RS) {
3434+
if (!HasRelaxableRanges)
3435+
return true;
3436+
return !llvm::isRangeRelaxable(RS->Begin, RS->End);
3437+
});
3438+
3439+
for (const auto *RS : NonRelaxableRanges) {
33983440
const MCSymbol *Begin = RS->Begin;
33993441
const MCSymbol *End = RS->End;
34003442
assert(Begin && "Range without a begin symbol?");

llvm/lib/MC/MCSymbol.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,22 @@ void MCSymbol::print(raw_ostream &OS, const MCAsmInfo *MAI) const {
8484
}
8585

8686
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
87-
LLVM_DUMP_METHOD void MCSymbol::dump() const {
88-
dbgs() << *this;
89-
}
87+
LLVM_DUMP_METHOD void MCSymbol::dump() const { dbgs() << *this; }
9088
#endif
89+
90+
bool llvm::isRangeRelaxable(const MCSymbol *Begin, const MCSymbol *End) {
91+
assert(Begin && "Range without a begin symbol?");
92+
assert(End && "Range without an end symbol?");
93+
llvm::SmallVector<const MCFragment *> RangeFragments{};
94+
for (const auto *Fragment = Begin->getFragment();
95+
Fragment != End->getFragment(); Fragment = Fragment->getNext()) {
96+
RangeFragments.push_back(Fragment);
97+
}
98+
RangeFragments.push_back(End->getFragment());
99+
100+
bool IsRelaxableRange = llvm::any_of(RangeFragments, [](auto &&Fragment) {
101+
assert(Fragment);
102+
return Fragment->isLinkerRelaxable();
103+
});
104+
return IsRelaxableRange;
105+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
; RUN: llc -dwarf-version=5 -split-dwarf-file=foo.dwo -O0 %s -mtriple=riscv64-unknown-linux-gnu -filetype=obj -o %t
2+
; RUN: llvm-dwarfdump -v %t | FileCheck --check-prefix=DWARF5 %s
3+
; RUN: llvm-dwarfdump --debug-info %t 2> %t.txt
4+
; RUN: FileCheck --input-file=%t.txt %s --check-prefix=RELOCS --implicit-check-not=warning:
5+
6+
; RUN: llc -dwarf-version=4 -split-dwarf-file=foo.dwo -O0 %s -mtriple=riscv64-unknown-linux-gnu -filetype=obj -o %t
7+
; RUN: llvm-dwarfdump -v %t | FileCheck --check-prefix=DWARF4 %s
8+
; RUN: llvm-dwarfdump --debug-info %t 2> %t.txt
9+
; RUN: FileCheck --input-file=%t.txt %s --check-prefix=RELOCS --implicit-check-not=warning:
10+
11+
; In the RISC-V architecture, the .text section is subject to
12+
; relaxation, meaning the start address of each function can change
13+
; during the linking process. Therefore, the .debug_rnglists.dwo
14+
; section must obtain function's start addresses from the .debug_addr
15+
; section.
16+
17+
; Generally, a function's body can be relaxed (for example, the
18+
; square() and main() functions in this test, which contain call
19+
; instructions). For such code ranges, the linker must place the
20+
; start and end addresses into the .debug_addr section and use
21+
; the DW_RLE_startx_endx entry form in the .debug_rnglists.dwo
22+
; section within the .dwo file.
23+
24+
; However, some functions may not contain any relaxable instructions
25+
; (for example, the boo() function in this test). In these cases,
26+
; it is possible to use the more space-efficient DW_RLE_startx_length
27+
; range entry form.
28+
29+
; From the code:
30+
31+
; __attribute__((noinline)) int boo();
32+
33+
; int square(int num) {
34+
; int num1 = boo();
35+
; return num1 * num;
36+
; }
37+
38+
; __attribute__((noinline)) int boo() {
39+
; return 8;
40+
; }
41+
42+
; int main() {
43+
; int a = 10;
44+
; int squared = square(a);
45+
; return squared;
46+
; }
47+
48+
; compiled with
49+
50+
; clang -g -S -gsplit-dwarf --target=riscv64 -march=rv64gc -O0 relax_dwo_ranges.cpp
51+
52+
; Currently, square() still uses an offset to represent the function's end address,
53+
; which requires a relocation here.
54+
; RELOCS: warning: unexpected relocations for dwo section '.debug_info.dwo'
55+
56+
; DWARF5: .debug_info.dwo contents:
57+
; DWARF5: DW_TAG_subprogram
58+
; DWARF5-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000000) address = 0x0000000000000000 ".text")
59+
; DWARF5-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000000)
60+
; DWARF5: DW_AT_name {{.*}} "square")
61+
; DWARF5: DW_TAG_formal_parameter
62+
63+
; Ensure there is no unnecessary addresses in .o file
64+
; DWARF5: .debug_addr contents:
65+
; DWARF5: Addrs: [
66+
; DWARF5-NEXT: 0x0000000000000000
67+
; DWARF5-NEXT: 0x0000000000000046
68+
; DWARF5-NEXT: 0x000000000000006c
69+
; DWARF5-NEXT: 0x00000000000000b0
70+
; DWARF5-NEXT: ]
71+
72+
; Ensure that 'boo()' and 'main()' use DW_RLE_startx_length and DW_RLE_startx_endx
73+
; entries respectively
74+
; DWARF5: .debug_rnglists.dwo contents:
75+
; DWARF5: ranges:
76+
; DWARF5-NEXT: 0x00000014: [DW_RLE_startx_length]: 0x0000000000000001, 0x0000000000000024 => [0x0000000000000046, 0x000000000000006a)
77+
; DWARF5-NEXT: 0x00000017: [DW_RLE_end_of_list ]
78+
; DWARF5-NEXT: 0x00000018: [DW_RLE_startx_endx ]: 0x0000000000000002, 0x0000000000000003 => [0x000000000000006c, 0x00000000000000b0)
79+
; DWARF5-NEXT: 0x0000001b: [DW_RLE_end_of_list ]
80+
; DWARF5-EMPTY:
81+
82+
; DWARF4: .debug_info.dwo contents:
83+
; DWARF4: DW_TAG_subprogram
84+
; DWARF4-NEXT: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000000) address = 0x0000000000000000 ".text")
85+
; DWARF4-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000000)
86+
; DWARF4: DW_AT_name {{.*}} "square")
87+
88+
; DWARF4: DW_TAG_subprogram
89+
; DWARF4-NEXT: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000001) address = 0x0000000000000046 ".text")
90+
; DWARF4-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000024)
91+
; DWARF4: DW_AT_name {{.*}} "boo")
92+
93+
; DWARF4: DW_TAG_subprogram
94+
; DWARF4-NEXT: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000002) address = 0x000000000000006c ".text")
95+
; DWARF4-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000000)
96+
; DWARF4: DW_AT_name {{.*}} "main")
97+
98+
; Ensure there is no unnecessary addresses in .o file
99+
; DWARF4: .debug_addr contents:
100+
; DWARF4: Addrs: [
101+
; DWARF4-NEXT: 0x0000000000000000
102+
; DWARF4-NEXT: 0x0000000000000046
103+
; DWARF4-NEXT: 0x000000000000006c
104+
; DWARF4-NEXT: ]
105+
106+
; Function Attrs: mustprogress noinline optnone
107+
define dso_local noundef signext i32 @_Z6squarei(i32 noundef signext %0) #0 !dbg !11 {
108+
%2 = alloca i32, align 4
109+
%3 = alloca i32, align 4
110+
store i32 %0, ptr %2, align 4
111+
#dbg_declare(ptr %2, !16, !DIExpression(), !17)
112+
#dbg_declare(ptr %3, !18, !DIExpression(), !19)
113+
%4 = call noundef signext i32 @_Z3boov(), !dbg !20
114+
store i32 %4, ptr %3, align 4, !dbg !19
115+
%5 = load i32, ptr %3, align 4, !dbg !21
116+
%6 = load i32, ptr %2, align 4, !dbg !22
117+
%7 = mul nsw i32 %5, %6, !dbg !23
118+
ret i32 %7, !dbg !24
119+
}
120+
121+
; Function Attrs: mustprogress noinline nounwind optnone
122+
define dso_local noundef signext i32 @_Z3boov() #1 !dbg !25 {
123+
ret i32 8, !dbg !28
124+
}
125+
126+
; Function Attrs: mustprogress noinline norecurse optnone
127+
define dso_local noundef signext i32 @main() #2 !dbg !29 {
128+
%1 = alloca i32, align 4
129+
%2 = alloca i32, align 4
130+
%3 = alloca i32, align 4
131+
store i32 0, ptr %1, align 4
132+
#dbg_declare(ptr %2, !30, !DIExpression(), !31)
133+
store i32 10, ptr %2, align 4, !dbg !31
134+
#dbg_declare(ptr %3, !32, !DIExpression(), !33)
135+
%4 = load i32, ptr %2, align 4, !dbg !34
136+
%5 = call noundef signext i32 @_Z6squarei(i32 noundef signext %4), !dbg !35
137+
store i32 %5, ptr %3, align 4, !dbg !33
138+
%6 = load i32, ptr %3, align 4, !dbg !36
139+
ret i32 %6, !dbg !37
140+
}
141+
142+
attributes #0 = { mustprogress noinline optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit,+relax,+f,+d" }
143+
attributes #1 = { mustprogress noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit,+relax,+f,+d" }
144+
attributes #2 = { mustprogress noinline norecurse optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic-rv64" "target-features"="+64bit,+relax,+f,+d" }
145+
146+
!llvm.dbg.cu = !{!0}
147+
!llvm.module.flags = !{!2, !3, !4, !5, !6, !8, !9}
148+
!llvm.ident = !{!10}
149+
150+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 22.0.0git (git@github.com:dlav-sc/llvm-project.git 972928c7a5fecec79f36c6899f1df779d0a17202)", isOptimized: false, runtimeVersion: 0, splitDebugFilename: "riscv_relax_dwo_ranges.dwo", emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: GNU)
151+
!1 = !DIFile(filename: "riscv_relax_dwo_ranges.cpp", directory: "/root/test/dwarf/generate", checksumkind: CSK_MD5, checksum: "ea48d4b4acc770ff327714eaf1348b92")
152+
!2 = !{i32 7, !"Dwarf Version", i32 5}
153+
!3 = !{i32 2, !"Debug Info Version", i32 3}
154+
!4 = !{i32 1, !"wchar_size", i32 4}
155+
!5 = !{i32 1, !"target-abi", !"lp64d"}
156+
!6 = !{i32 6, !"riscv-isa", !7}
157+
!7 = !{!"rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0"}
158+
!8 = !{i32 7, !"frame-pointer", i32 2}
159+
!9 = !{i32 8, !"SmallDataLimit", i32 0}
160+
!10 = !{!"clang version 22.0.0git (git@github.com:dlav-sc/llvm-project.git 972928c7a5fecec79f36c6899f1df779d0a17202)"}
161+
!11 = distinct !DISubprogram(name: "square", linkageName: "_Z6squarei", scope: !1, file: !1, line: 3, type: !12, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !15)
162+
!12 = !DISubroutineType(types: !13)
163+
!13 = !{!14, !14}
164+
!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
165+
!15 = !{}
166+
!16 = !DILocalVariable(name: "num", arg: 1, scope: !11, file: !1, line: 3, type: !14)
167+
!17 = !DILocation(line: 3, column: 16, scope: !11)
168+
!18 = !DILocalVariable(name: "num1", scope: !11, file: !1, line: 4, type: !14)
169+
!19 = !DILocation(line: 4, column: 7, scope: !11)
170+
!20 = !DILocation(line: 4, column: 14, scope: !11)
171+
!21 = !DILocation(line: 5, column: 10, scope: !11)
172+
!22 = !DILocation(line: 5, column: 17, scope: !11)
173+
!23 = !DILocation(line: 5, column: 15, scope: !11)
174+
!24 = !DILocation(line: 5, column: 3, scope: !11)
175+
!25 = distinct !DISubprogram(name: "boo", linkageName: "_Z3boov", scope: !1, file: !1, line: 8, type: !26, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
176+
!26 = !DISubroutineType(types: !27)
177+
!27 = !{!14}
178+
!28 = !DILocation(line: 9, column: 3, scope: !25)
179+
!29 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 12, type: !26, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !15)
180+
!30 = !DILocalVariable(name: "a", scope: !29, file: !1, line: 13, type: !14)
181+
!31 = !DILocation(line: 13, column: 7, scope: !29)
182+
!32 = !DILocalVariable(name: "squared", scope: !29, file: !1, line: 14, type: !14)
183+
!33 = !DILocation(line: 14, column: 7, scope: !29)
184+
!34 = !DILocation(line: 14, column: 24, scope: !29)
185+
!35 = !DILocation(line: 14, column: 17, scope: !29)
186+
!36 = !DILocation(line: 15, column: 10, scope: !29)
187+
!37 = !DILocation(line: 15, column: 3, scope: !29)

0 commit comments

Comments
 (0)