diff --git a/llvm/include/llvm/MC/MCSymbol.h b/llvm/include/llvm/MC/MCSymbol.h index e31d0374baf4a..eef248354b70f 100644 --- a/llvm/include/llvm/MC/MCSymbol.h +++ b/llvm/include/llvm/MC/MCSymbol.h @@ -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 diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 71818a16e862c..2c6a77b979036 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -3304,6 +3304,7 @@ template <> struct DwarfRangeListTraits { static constexpr unsigned BaseAddressx = dwarf::DW_LLE_base_addressx; static constexpr unsigned OffsetPair = dwarf::DW_LLE_offset_pair; static constexpr unsigned StartxLength = dwarf::DW_LLE_startx_length; + static constexpr unsigned StartxEndx = dwarf::DW_LLE_startx_endx; static constexpr unsigned EndOfList = dwarf::DW_LLE_end_of_list; static StringRef StringifyRangeKind(unsigned Encoding) { @@ -3320,6 +3321,7 @@ template <> struct DwarfRangeListTraits { static constexpr unsigned BaseAddressx = dwarf::DW_RLE_base_addressx; static constexpr unsigned OffsetPair = dwarf::DW_RLE_offset_pair; static constexpr unsigned StartxLength = dwarf::DW_RLE_startx_length; + static constexpr unsigned StartxEndx = dwarf::DW_RLE_startx_endx; static constexpr unsigned EndOfList = dwarf::DW_RLE_end_of_list; static StringRef StringifyRangeKind(unsigned Encoding) { @@ -3355,7 +3357,8 @@ static void emitRangeList(DwarfDebug &DD, AsmPrinter *Asm, const Ranges &R, 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 @@ -3394,7 +3397,46 @@ static void emitRangeList(DwarfDebug &DD, AsmPrinter *Asm, const Ranges &R, Asm->OutStreamer->emitIntValue(0, Size); } - for (const auto *RS : P.second) { + if (DD.useSplitDwarf() && UseDwarf5) { + // In .dwo files, we must ensure no relocations are present. For + // .debug_ranges.dwo. this means that if there is at least one + // relocation between the start and end of a range, we must + // represent the range boundaries using indirect addresses from + // the .debug_addr section. + // + // The DWARFv5 specification (section 2.17.3) does not require + // range entries to be ordered. Therefore, we emit such range + // entries here to allow using more optimal formats (e.g. + // StartxLength) for other ranges. + + auto RelaxableRanges = llvm::make_filter_range(P.second, [](auto &&RS) { + return llvm::isRangeRelaxable(RS->Begin, RS->End); + }); + + for (auto &&RS : RelaxableRanges) { + const auto *Begin = RS->Begin; + const auto *End = RS->End; + Asm->OutStreamer->AddComment( + DwarfRangeListTraits::StringifyRangeKind( + DwarfRangeListTraits::StartxEndx)); + Asm->emitInt8(DwarfRangeListTraits::StartxEndx); + Asm->OutStreamer->AddComment(" start index"); + Asm->emitULEB128(DD.getAddressPool().getIndex(Begin)); + Asm->OutStreamer->AddComment(" end index"); + Asm->emitULEB128(DD.getAddressPool().getIndex(End)); + DwarfRangeListTraits::EmitPayload(DD, R.CU, *RS); + } + } + + auto NonRelaxableRanges = llvm::make_filter_range( + P.second, + [HasRelaxableRanges = DD.useSplitDwarf() && UseDwarf5](auto &&RS) { + if (!HasRelaxableRanges) + return true; + return !llvm::isRangeRelaxable(RS->Begin, RS->End); + }); + + for (const auto *RS : NonRelaxableRanges) { const MCSymbol *Begin = RS->Begin; const MCSymbol *End = RS->End; assert(Begin && "Range without a begin symbol?"); diff --git a/llvm/lib/MC/MCSymbol.cpp b/llvm/lib/MC/MCSymbol.cpp index b86873824cb00..7440fd8687b4d 100644 --- a/llvm/lib/MC/MCSymbol.cpp +++ b/llvm/lib/MC/MCSymbol.cpp @@ -84,7 +84,22 @@ 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 + +bool llvm::isRangeRelaxable(const MCSymbol *Begin, const MCSymbol *End) { + assert(Begin && "Range without a begin symbol?"); + assert(End && "Range without an end symbol?"); + llvm::SmallVector RangeFragments{}; + for (const auto *Fragment = Begin->getFragment(); + Fragment != End->getFragment(); Fragment = Fragment->getNext()) { + RangeFragments.push_back(Fragment); + } + RangeFragments.push_back(End->getFragment()); + + bool IsRelaxableRange = llvm::any_of(RangeFragments, [](auto &&Fragment) { + assert(Fragment); + return Fragment->isLinkerRelaxable(); + }); + return IsRelaxableRange; +} diff --git a/llvm/test/DebugInfo/RISCV/relax_dwo_ranges.ll b/llvm/test/DebugInfo/RISCV/relax_dwo_ranges.ll new file mode 100644 index 0000000000000..f8ab7fc5ad900 --- /dev/null +++ b/llvm/test/DebugInfo/RISCV/relax_dwo_ranges.ll @@ -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(); + +; 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 (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) +!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 (git@github.com: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)