Skip to content

Conversation

@daniilavdeev
Copy link
Contributor

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.

@llvmbot
Copy link
Member

llvmbot commented Nov 5, 2025

@llvm/pr-subscribers-debuginfo

Author: None (dlav-sc)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/166597.diff

4 Files Affected:

  • (modified) llvm/include/llvm/MC/MCSymbol.h (+2)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp (+33-25)
  • (modified) llvm/lib/MC/MCSymbol.cpp (+18-3)
  • (added) llvm/test/DebugInfo/RISCV/relax_dwo_ranges.ll (+187)
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 567acf75d1b8d..46895a3ccb027 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -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;
 
@@ -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
@@ -3373,12 +3372,21 @@ 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);
+        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);
@@ -3399,14 +3407,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) {
@@ -3628,8 +3636,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) {});
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<const MCFragment *> 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 ([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)

@llvmbot
Copy link
Member

llvmbot commented Nov 5, 2025

@llvm/pr-subscribers-backend-risc-v

Author: None (dlav-sc)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/166597.diff

4 Files Affected:

  • (modified) llvm/include/llvm/MC/MCSymbol.h (+2)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp (+33-25)
  • (modified) llvm/lib/MC/MCSymbol.cpp (+18-3)
  • (added) llvm/test/DebugInfo/RISCV/relax_dwo_ranges.ll (+187)
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 567acf75d1b8d..46895a3ccb027 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -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;
 
@@ -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
@@ -3373,12 +3372,21 @@ 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);
+        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);
@@ -3399,14 +3407,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) {
@@ -3628,8 +3636,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) {});
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<const MCFragment *> 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 ([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)

@llvmbot
Copy link
Member

llvmbot commented Nov 5, 2025

@llvm/pr-subscribers-llvm-mc

Author: None (dlav-sc)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/166597.diff

4 Files Affected:

  • (modified) llvm/include/llvm/MC/MCSymbol.h (+2)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp (+33-25)
  • (modified) llvm/lib/MC/MCSymbol.cpp (+18-3)
  • (added) llvm/test/DebugInfo/RISCV/relax_dwo_ranges.ll (+187)
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 567acf75d1b8d..46895a3ccb027 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -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;
 
@@ -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
@@ -3373,12 +3372,21 @@ 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);
+        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);
@@ -3399,14 +3407,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) {
@@ -3628,8 +3636,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) {});
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<const MCFragment *> 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 ([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)

@daniilavdeev
Copy link
Contributor Author

@dwblaikie, I've addressed your comments from #164812:

Why is there a need for this vector? Rather than computing IsRelaxableRange during the walk over the fragments directly?

I just wanted to separate consernse here, but yeah, we can make everything in a single loop.

Rather than having two loops and filtering - could this be inside the one/pre-existing loop, and choose which encoding to use there?

I've moved this logic into the existing loop as well.

@dwblaikie
Copy link
Collaborator

@dwblaikie, I've addressed your comments from #164812:

Why is there a need for this vector? Rather than computing IsRelaxableRange during the walk over the fragments directly?

I just wanted to separate consernse here, but yeah, we can make everything in a single loop.

Yeah, please make that change.

@daniilavdeev
Copy link
Contributor Author

Yeah, please make that change.

Done

@daniilavdeev
Copy link
Contributor Author

@lenary,

Please can we not re-implement absoluteSymbolDiff from MCObjectStreamer.cpp - given the logic there is already quite complex, maybe we can re-use that function rather than missing this one in a future update?

I'm not sure absoluteSymbolDiff is suitable here. It has a similar signature, but looks like a completely different functionality... so I have no idea how we can apply it in this case.

@dwblaikie
Copy link
Collaborator

@lenary,

Please can we not re-implement absoluteSymbolDiff from MCObjectStreamer.cpp - given the logic there is already quite complex, maybe we can re-use that function rather than missing this one in a future update?

I'm not sure absoluteSymbolDiff is suitable here. It has a similar signature, but looks like a completely different functionality... so I have no idea how we can apply it in this case.

Doesn't seem so different? It computes the difference between two labels if it can be known at compile time - so... if there is no compile-time answer, that means it's relaxable & so can't use a label difference range encoding.

@lenary thanks for pointing to the existing functionality

@github-actions
Copy link

github-actions bot commented Nov 6, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@daniilavdeev daniilavdeev force-pushed the users/dlav-sc/dwarf_split_relaxable_range_2 branch from dde52a1 to 6794a76 Compare November 6, 2025 21:49
@daniilavdeev
Copy link
Contributor Author

daniilavdeev commented Nov 6, 2025

Doesn't seem so different? It computes the difference between two labels if it can be known at compile time - so... if there is no compile-time answer, that means it's relaxable & so can't use a label difference range encoding.

Well, perhaps not entirely different, but as I understand, one of the conditions for absoluteSymbolDiff to return true is that the Hi and Lo symbols must be in the same fragment. I'm not sure there are no scenarios where function boundaries reside in different fragments but the function remains non-relaxable. In contrast, isRangeRelaxable collects all fragments between two labels and ensures there are no relaxable fragments between them.

Therefore, absoluteSymbolDiff imposes a stricter condition than isRangeRelaxable, which could result in less optimal DWARF information. However, I believe this is acceptable, and we can proceed with generating slightly less optimal DWARF.

I attempted to use absoluteSymbolDiff but observed some degradation for x86 in the second part of the patch (see llvm/test/DebugInfo/X86/fission-ranges.ll in #164813) . Rather than using the addr+offset form for low/high PC description, it now uses addrx/addrx for some functions. If this is acceptable, we can keep the current version of the patch.

@dwblaikie
Copy link
Collaborator

Doesn't seem so different? It computes the difference between two labels if it can be known at compile time - so... if there is no compile-time answer, that means it's relaxable & so can't use a label difference range encoding.

Well, perhaps not entirely different, but as I understand, one of the conditions for absoluteSymbolDiff to return true is that the Hi and Lo symbols must be in the same fragment. I'm not sure there are no scenarios where function boundaries reside in different fragments but the function remains non-relaxable. In contrast, isRangeRelaxable collects all fragments between two labels and ensures there are no relaxable fragments between them.

Fair point - might be a minor performance/optimality bug?

I attempted to use absoluteSymbolDiff but observed some degradation for x86 in the second part of the patch (see llvm/test/DebugInfo/X86/fission-ranges.ll in #164813) . Rather than using the addr+offset form for low/high PC description, it now uses addrx/addrx for some functions. If this is acceptable, we can keep the current version of the patch.

I don't think that's acceptable - could you look into why that happens? Perhaps it provides a concrete example of the multi-fragment case you outlined above. Perhaps the absoluteSymbolDiff function can be improved to cover this case (walking multiple fragments, so long as each has an absolute/fixed size, etc)

@daniilavdeev
Copy link
Contributor Author

Perhaps the absoluteSymbolDiff function can be improved to cover this case (walking multiple fragments, so long as each has an absolute/fixed size, etc)

I attempted to handle multiple fragment cases in absoluteSymbolDiff, but I now believe this won't work. The main reason is that we can't calculate the difference between two labels in different fragments before layout (see MCAssembler::layout()), since MCFragment doesn't know its final position within the section before layout.

Moreover even if it were possible, I don't think absoluteSymbolDiff provides what I need. From my understanding, absoluteSymbolDiff returns a difference only when the offset between labels remains constant both at assembly time and at link time. What I actually need is to check whether the linker might change the offset between labels, therefore I need to ensure the offset remains constant only at link time. This is a much less restrictive condition than what absoluteSymbolDiff implies.

If we can't compute the label difference with absoluteSymbolDiff, but have a guarantee that the linker won't affect the difference, we could simply emit an appropriate assembly directive (e.g. .uleb128 Hi-Lo) and use the addr+offset DWARF format anyway. This approach works in most cases, see MCObjectStreamer::emitAbsoluteSymbolDiffAsULEB128. When that function can't obtain the difference between labels with absoluteSymbolDiff, it emits the appropriate assembly directive.

isRangeRelaxable simply checks that the fragments' sizes between labels won't change at link time. It's acceptable if they change at assembly time (e.g., during fixup resolution by assembler), because we have the .uleb128 directive in that case.

Therefore, I believe absoluteSymbolDiff and isRangeRelaxable serve different purposes. The former aims to eliminate redundant .uleb128 directives (i.e. primarily for optimization), while the latter determines whether a dwarf form should be handled by the linker.

I might be mistaken about something, so please feel free to share any concerns you have.

@jmorse
Copy link
Member

jmorse commented Nov 10, 2025

Drive by comment -- I believe your assessment @dlav-sc is totally correct, about the purposes of absoluteSymbolDiff and isRangeRelaxable. I've run into similar issues in #134677 . AFAIUI the RISC-V linetable is emitted slightly inefficiently because of this distinction. (I don't have anything to say about this PR, but figured I'd confirm the understanding in the comment above).

@dwblaikie
Copy link
Collaborator

fair enough - thanks for the write up (& thanks for the prior experience/validation, @jmorse) - let's go with the isRangeRelaxable, then.

@lenary
Copy link
Member

lenary commented Nov 10, 2025

Thank you for investigating this more deeply. I learned from your analysis.

I now understand from your explanation that absoluteSymbolDiff is a conservative approximation of isRangeRelaxable, which can also compute a distance. If absoluteSymbolDiff(...).has_value() == true, then isRangeRelaxable(...) == false, but when absoluteSymbolDiff(...).has_value() == false, isRangeRelaxable(...) could return either true or false. Both are written to be able to be used pre-layout, but isRangeRelaxable crosses fragment boundaries so cannot return a concrete distance like absoluteSymbolDiff might.

It would be good if you could summarise your analysis above into comments on the functions themselves, to help anyone that comes afterwards :)

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.
@daniilavdeev daniilavdeev force-pushed the users/dlav-sc/dwarf_split_relaxable_range_2 branch from 6794a76 to 453d7b6 Compare November 21, 2025 04:33
@daniilavdeev
Copy link
Contributor Author

I've rolled back to isRangeRelaxable variant, take a look, please.

It would be good if you could summarise your analysis above into comments on the functions themselves

Yep, I've added an appropriate comment on the isRangeRelaxable function and a corresponding comment in debug emission logic explaining why we can't use absoluteSymbolDiff instead.

One thing I'm not sure about is where to place isRangeRelaxable function. Maybe you know a better place for it than MCSymbol.h? On the other hand, I don’t think it’s particularly significant, so we can leave it as it.

@github-actions
Copy link

🐧 Linux x64 Test Results

  • 186432 tests passed
  • 4864 tests skipped

@lenary
Copy link
Member

lenary commented Nov 21, 2025

I've rolled back to isRangeRelaxable variant, take a look, please.

It would be good if you could summarise your analysis above into comments on the functions themselves

Yep, I've added an appropriate comment on the isRangeRelaxable function and a corresponding comment in debug emission logic explaining why we can't use absoluteSymbolDiff instead.

One thing I'm not sure about is where to place isRangeRelaxable function. Maybe you know a better place for it than MCSymbol.h? On the other hand, I don’t think it’s particularly significant, so we can leave it as it.

Probably on MCStreamer, if you think it should be on any object. I don't think it's a problem that it's a free function right now though. We can always move it somewhere else later.

Copy link
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

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

LGTM.


; 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants