Skip to content
Open
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/MC/MCSymbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ inline raw_ostream &operator<<(raw_ostream &OS, const MCSymbol &Sym) {
return OS;
}

bool isRangeRelaxable(const MCSymbol *Begin, const MCSymbol *End);

} // end namespace llvm

#endif // LLVM_MC_MCSYMBOL_H
64 changes: 39 additions & 25 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3291,14 +3291,12 @@ static MCSymbol *emitLoclistsTableHeader(AsmPrinter *Asm,
}

template <typename Ranges, typename PayloadEmitter>
static void emitRangeList(
DwarfDebug &DD, AsmPrinter *Asm, MCSymbol *Sym, const Ranges &R,
const DwarfCompileUnit &CU, unsigned BaseAddressx, unsigned OffsetPair,
unsigned StartxLength, unsigned EndOfList,
StringRef (*StringifyEnum)(unsigned),
bool ShouldUseBaseAddress,
PayloadEmitter EmitPayload) {

static void
emitRangeList(DwarfDebug &DD, AsmPrinter *Asm, MCSymbol *Sym, const Ranges &R,
const DwarfCompileUnit &CU, unsigned BaseAddressx,
unsigned OffsetPair, unsigned StartxLength, unsigned StartxEndx,
unsigned EndOfList, StringRef (*StringifyEnum)(unsigned),
bool ShouldUseBaseAddress, PayloadEmitter EmitPayload) {
auto Size = Asm->MAI->getCodePointerSize();
bool UseDwarf5 = DD.getDwarfVersion() >= 5;

Expand All @@ -3317,7 +3315,8 @@ static void emitRangeList(
bool BaseIsSet = false;
for (const auto &P : SectionRanges) {
auto *Base = CUBase;
if ((Asm->TM.getTargetTriple().isNVPTX() && DD.tuneForGDB())) {
if ((Asm->TM.getTargetTriple().isNVPTX() && DD.tuneForGDB()) ||
(DD.useSplitDwarf() && UseDwarf5 && P.first->isLinkerRelaxable())) {
// PTX does not support subtracting labels from the code section in the
// debug_loc section. To work around this, the NVPTX backend needs the
// compile unit to have no low_pc in order to have a zero base_address
Expand Down Expand Up @@ -3373,12 +3372,27 @@ static void emitRangeList(
Asm->emitLabelDifference(End, Base, Size);
}
} else if (UseDwarf5) {
Asm->OutStreamer->AddComment(StringifyEnum(StartxLength));
Asm->emitInt8(StartxLength);
Asm->OutStreamer->AddComment(" start index");
Asm->emitULEB128(DD.getAddressPool().getIndex(Begin));
Asm->OutStreamer->AddComment(" length");
Asm->emitLabelDifferenceAsULEB128(End, Begin);
// NOTE: We can't use absoluteSymbolDiff here instead of
// isRangeRelaxable. While isRangeRelaxable only checks that the offset
// between labels won't change at link time (which is exactly what we
// need), absoluteSymbolDiff also requires that the offset remain
// unchanged at assembly time, imposing a much stricter condition.
// Consequently, this would lead to less optimal debug info emission.
if (DD.useSplitDwarf() && llvm::isRangeRelaxable(Begin, End)) {
Asm->OutStreamer->AddComment(StringifyEnum(StartxEndx));
Asm->emitInt8(StartxEndx);
Asm->OutStreamer->AddComment(" start index");
Asm->emitULEB128(DD.getAddressPool().getIndex(Begin));
Asm->OutStreamer->AddComment(" end index");
Asm->emitULEB128(DD.getAddressPool().getIndex(End));
} else {
Asm->OutStreamer->AddComment(StringifyEnum(StartxLength));
Asm->emitInt8(StartxLength);
Asm->OutStreamer->AddComment(" start index");
Asm->emitULEB128(DD.getAddressPool().getIndex(Begin));
Asm->OutStreamer->AddComment(" length");
Asm->emitLabelDifferenceAsULEB128(End, Begin);
}
} else {
Asm->OutStreamer->emitSymbolValue(Begin, Size);
Asm->OutStreamer->emitSymbolValue(End, Size);
Expand All @@ -3399,14 +3413,14 @@ static void emitRangeList(

// Handles emission of both debug_loclist / debug_loclist.dwo
static void emitLocList(DwarfDebug &DD, AsmPrinter *Asm, const DebugLocStream::List &List) {
emitRangeList(DD, Asm, List.Label, DD.getDebugLocs().getEntries(List),
*List.CU, dwarf::DW_LLE_base_addressx,
dwarf::DW_LLE_offset_pair, dwarf::DW_LLE_startx_length,
dwarf::DW_LLE_end_of_list, llvm::dwarf::LocListEncodingString,
/* ShouldUseBaseAddress */ true,
[&](const DebugLocStream::Entry &E) {
DD.emitDebugLocEntryLocation(E, List.CU);
});
emitRangeList(
DD, Asm, List.Label, DD.getDebugLocs().getEntries(List), *List.CU,
dwarf::DW_LLE_base_addressx, dwarf::DW_LLE_offset_pair,
dwarf::DW_LLE_startx_length, dwarf::DW_LLE_startx_endx,
dwarf::DW_LLE_end_of_list, llvm::dwarf::LocListEncodingString,
/* ShouldUseBaseAddress */ true, [&](const DebugLocStream::Entry &E) {
DD.emitDebugLocEntryLocation(E, List.CU);
});
}

void DwarfDebug::emitDebugLocImpl(MCSection *Sec) {
Expand Down Expand Up @@ -3628,8 +3642,8 @@ static void emitRangeList(DwarfDebug &DD, AsmPrinter *Asm,
const RangeSpanList &List) {
emitRangeList(DD, Asm, List.Label, List.Ranges, *List.CU,
dwarf::DW_RLE_base_addressx, dwarf::DW_RLE_offset_pair,
dwarf::DW_RLE_startx_length, dwarf::DW_RLE_end_of_list,
llvm::dwarf::RangeListEncodingString,
dwarf::DW_RLE_startx_length, dwarf::DW_RLE_startx_endx,
dwarf::DW_RLE_end_of_list, llvm::dwarf::RangeListEncodingString,
List.CU->getCUNode()->getRangesBaseAddress() ||
DD.getDwarfVersion() >= 5,
[](auto) {});
Expand Down
20 changes: 17 additions & 3 deletions llvm/lib/MC/MCSymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,21 @@ void MCSymbol::print(raw_ostream &OS, const MCAsmInfo *MAI) const {
}

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void MCSymbol::dump() const {
dbgs() << *this;
}
LLVM_DUMP_METHOD void MCSymbol::dump() const { dbgs() << *this; }
#endif

// Determine whether the offset between two labels can change at link time.
// Currently, this function is used only in DWARF info emission logic, where it
// helps generate more optimal debug info when the offset between labels is
// constant at link time.
bool llvm::isRangeRelaxable(const MCSymbol *Begin, const MCSymbol *End) {
assert(Begin && "Range without a begin symbol?");
assert(End && "Range without an end symbol?");
for (const auto *Fragment = Begin->getFragment();
Fragment != End->getFragment(); Fragment = Fragment->getNext()) {
assert(Fragment);
if (Fragment->isLinkerRelaxable())
return true;
}
return false;
}
187 changes: 187 additions & 0 deletions llvm/test/DebugInfo/RISCV/relax_dwo_ranges.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
; RUN: llc -dwarf-version=5 -split-dwarf-file=foo.dwo -O0 %s -mtriple=riscv64-unknown-linux-gnu -filetype=obj -o %t
; RUN: llvm-dwarfdump -v %t | FileCheck --check-prefix=DWARF5 %s
; RUN: llvm-dwarfdump --debug-info %t 2> %t.txt
; RUN: FileCheck --input-file=%t.txt %s --check-prefix=RELOCS --implicit-check-not=warning:

; RUN: llc -dwarf-version=4 -split-dwarf-file=foo.dwo -O0 %s -mtriple=riscv64-unknown-linux-gnu -filetype=obj -o %t
; RUN: llvm-dwarfdump -v %t | FileCheck --check-prefix=DWARF4 %s
; RUN: llvm-dwarfdump --debug-info %t 2> %t.txt
; RUN: FileCheck --input-file=%t.txt %s --check-prefix=RELOCS --implicit-check-not=warning:

; In the RISC-V architecture, the .text section is subject to
; relaxation, meaning the start address of each function can change
; during the linking process. Therefore, the .debug_rnglists.dwo
; section must obtain function's start addresses from the .debug_addr
; section.

; Generally, a function's body can be relaxed (for example, the
; square() and main() functions in this test, which contain call
; instructions). For such code ranges, the linker must place the
; start and end addresses into the .debug_addr section and use
; the DW_RLE_startx_endx entry form in the .debug_rnglists.dwo
; section within the .dwo file.

; However, some functions may not contain any relaxable instructions
; (for example, the boo() function in this test). In these cases,
; it is possible to use the more space-efficient DW_RLE_startx_length
; range entry form.

; From the code:

; __attribute__((noinline)) int boo();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://llvm.org/docs/TestingGuide.html#elaborated-tests for tests requiring elaborate IR or assembly files where cleanup is less practical (e.g., a large amount of debug information output from Clang), you can include generation instructions within split-file part called gen. Then, run llvm/utils/update_test_body.py on the test file to generate the needed content.


; int square(int num) {
; int num1 = boo();
; return num1 * num;
; }

; __attribute__((noinline)) int boo() {
; return 8;
; }

; int main() {
; int a = 10;
; int squared = square(a);
; return squared;
; }

; compiled with

; clang -g -S -gsplit-dwarf --target=riscv64 -march=rv64gc -O0 relax_dwo_ranges.cpp

; Currently, square() still uses an offset to represent the function's end address,
; which requires a relocation here.
; RELOCS: warning: unexpected relocations for dwo section '.debug_info.dwo'

; DWARF5: .debug_info.dwo contents:
; DWARF5: DW_TAG_subprogram
; DWARF5-NEXT: DW_AT_low_pc [DW_FORM_addrx] (indexed (00000000) address = 0x0000000000000000 ".text")
; DWARF5-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000000)
; DWARF5: DW_AT_name {{.*}} "square")
; DWARF5: DW_TAG_formal_parameter

; Ensure there is no unnecessary addresses in .o file
; DWARF5: .debug_addr contents:
; DWARF5: Addrs: [
; DWARF5-NEXT: 0x0000000000000000
; DWARF5-NEXT: 0x0000000000000046
; DWARF5-NEXT: 0x000000000000006c
; DWARF5-NEXT: 0x00000000000000b0
; DWARF5-NEXT: ]

; Ensure that 'boo()' and 'main()' use DW_RLE_startx_length and DW_RLE_startx_endx
; entries respectively
; DWARF5: .debug_rnglists.dwo contents:
; DWARF5: ranges:
; DWARF5-NEXT: 0x00000014: [DW_RLE_startx_length]: 0x0000000000000001, 0x0000000000000024 => [0x0000000000000046, 0x000000000000006a)
; DWARF5-NEXT: 0x00000017: [DW_RLE_end_of_list ]
; DWARF5-NEXT: 0x00000018: [DW_RLE_startx_endx ]: 0x0000000000000002, 0x0000000000000003 => [0x000000000000006c, 0x00000000000000b0)
; DWARF5-NEXT: 0x0000001b: [DW_RLE_end_of_list ]
; DWARF5-EMPTY:

; DWARF4: .debug_info.dwo contents:
; DWARF4: DW_TAG_subprogram
; DWARF4-NEXT: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000000) address = 0x0000000000000000 ".text")
; DWARF4-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000000)
; DWARF4: DW_AT_name {{.*}} "square")

; DWARF4: DW_TAG_subprogram
; DWARF4-NEXT: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000001) address = 0x0000000000000046 ".text")
; DWARF4-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000024)
; DWARF4: DW_AT_name {{.*}} "boo")

; DWARF4: DW_TAG_subprogram
; DWARF4-NEXT: DW_AT_low_pc [DW_FORM_GNU_addr_index] (indexed (00000002) address = 0x000000000000006c ".text")
; DWARF4-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000000)
; DWARF4: DW_AT_name {{.*}} "main")

; Ensure there is no unnecessary addresses in .o file
; DWARF4: .debug_addr contents:
; DWARF4: Addrs: [
; DWARF4-NEXT: 0x0000000000000000
; DWARF4-NEXT: 0x0000000000000046
; DWARF4-NEXT: 0x000000000000006c
; DWARF4-NEXT: ]

; Function Attrs: mustprogress noinline optnone
define dso_local noundef signext i32 @_Z6squarei(i32 noundef signext %0) #0 !dbg !11 {
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 %0, ptr %2, align 4
#dbg_declare(ptr %2, !16, !DIExpression(), !17)
#dbg_declare(ptr %3, !18, !DIExpression(), !19)
%4 = call noundef signext i32 @_Z3boov(), !dbg !20
store i32 %4, ptr %3, align 4, !dbg !19
%5 = load i32, ptr %3, align 4, !dbg !21
%6 = load i32, ptr %2, align 4, !dbg !22
%7 = mul nsw i32 %5, %6, !dbg !23
ret i32 %7, !dbg !24
}

; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef signext i32 @_Z3boov() #1 !dbg !25 {
ret i32 8, !dbg !28
}

; Function Attrs: mustprogress noinline norecurse optnone
define dso_local noundef signext i32 @main() #2 !dbg !29 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 0, ptr %1, align 4
#dbg_declare(ptr %2, !30, !DIExpression(), !31)
store i32 10, ptr %2, align 4, !dbg !31
#dbg_declare(ptr %3, !32, !DIExpression(), !33)
%4 = load i32, ptr %2, align 4, !dbg !34
%5 = call noundef signext i32 @_Z6squarei(i32 noundef signext %4), !dbg !35
store i32 %5, ptr %3, align 4, !dbg !33
%6 = load i32, ptr %3, align 4, !dbg !36
ret i32 %6, !dbg !37
}

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" }
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" }
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" }

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

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 22.0.0git ([email protected]:dlav-sc/llvm-project.git 972928c7a5fecec79f36c6899f1df779d0a17202)", isOptimized: false, runtimeVersion: 0, splitDebugFilename: "riscv_relax_dwo_ranges.dwo", emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: GNU)
!1 = !DIFile(filename: "riscv_relax_dwo_ranges.cpp", directory: "/root/test/dwarf/generate", checksumkind: CSK_MD5, checksum: "ea48d4b4acc770ff327714eaf1348b92")
!2 = !{i32 7, !"Dwarf Version", i32 5}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{i32 1, !"target-abi", !"lp64d"}
!6 = !{i32 6, !"riscv-isa", !7}
!7 = !{!"rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0"}
!8 = !{i32 7, !"frame-pointer", i32 2}
!9 = !{i32 8, !"SmallDataLimit", i32 0}
!10 = !{!"clang version 22.0.0git ([email protected]:dlav-sc/llvm-project.git 972928c7a5fecec79f36c6899f1df779d0a17202)"}
!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)
!12 = !DISubroutineType(types: !13)
!13 = !{!14, !14}
!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!15 = !{}
!16 = !DILocalVariable(name: "num", arg: 1, scope: !11, file: !1, line: 3, type: !14)
!17 = !DILocation(line: 3, column: 16, scope: !11)
!18 = !DILocalVariable(name: "num1", scope: !11, file: !1, line: 4, type: !14)
!19 = !DILocation(line: 4, column: 7, scope: !11)
!20 = !DILocation(line: 4, column: 14, scope: !11)
!21 = !DILocation(line: 5, column: 10, scope: !11)
!22 = !DILocation(line: 5, column: 17, scope: !11)
!23 = !DILocation(line: 5, column: 15, scope: !11)
!24 = !DILocation(line: 5, column: 3, scope: !11)
!25 = distinct !DISubprogram(name: "boo", linkageName: "_Z3boov", scope: !1, file: !1, line: 8, type: !26, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
!26 = !DISubroutineType(types: !27)
!27 = !{!14}
!28 = !DILocation(line: 9, column: 3, scope: !25)
!29 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 12, type: !26, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !15)
!30 = !DILocalVariable(name: "a", scope: !29, file: !1, line: 13, type: !14)
!31 = !DILocation(line: 13, column: 7, scope: !29)
!32 = !DILocalVariable(name: "squared", scope: !29, file: !1, line: 14, type: !14)
!33 = !DILocation(line: 14, column: 7, scope: !29)
!34 = !DILocation(line: 14, column: 24, scope: !29)
!35 = !DILocation(line: 14, column: 17, scope: !29)
!36 = !DILocation(line: 15, column: 10, scope: !29)
!37 = !DILocation(line: 15, column: 3, scope: !29)