[llvm] [DWARF] Disable generation of cross-CU references for DWARF V2 (PR #192516)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 16 12:07:03 PDT 2026


https://github.com/Nadharm created https://github.com/llvm/llvm-project/pull/192516

Though LLVM's treatment of DW_FORM_ref_addr for DWARF V2 follows the spec, it does not follow the recommendation of the FAQ. This works for consumers which expect the V2 spec definition, but breaks those which expect producers to follow the FAQ.

The DWARF V2 spec defines DW_FORM_ref_addr as:

"... the address of any debugging information entry within the same executable or shared object... This type of reference (DW_FORM_ref_addr) is the size of an address on the target architecture..."

And the DWARF FAQ says:

"The DWARF V2 definition never made any sense and was a mistake in the DWARF V2 specification: the field DW_FORM_ref_addr defines is an offset, not an address. If you are producing DWARF V2, please use the DWARF V3 definition of DW_FORM_ref_addr."

Currently for DWARF V2, LLVM continues using address-sizing rather than fully adopting the V3 definition (4 bytes for DWARF32, 8 bytes for DWARF64) as directed by the FAQ.

Given that the DWARF V2 is rather old and that the worst case size increase would match that of a non-LTO compilation flow, this MR disables de-duplication of DIEs across CUs to prevent the generation of cross-CU references using DW_FORM_ref_addr for DWARF V2.

Assisted-by: Claude Code (test)

>From 516aac68879eb1e60bce7735d877153b8aefedc2 Mon Sep 17 00:00:00 2001
From: Nadharm Dhiantravan <ndhiantravan at nvidia.com>
Date: Thu, 16 Apr 2026 11:51:45 -0700
Subject: [PATCH] [DWARF] Disable generation of cross-CU references for DWARF
 V2

Though LLVM's treatment of DW_FORM_ref_addr for DWARF V2 follows the
spec, it does not follow the recommendation of the FAQ. This works for
consumers which expect the V2 spec definition, but breaks those which
expect producers to follow the FAQ.

The DWARF V2 spec defines DW_FORM_ref_addr as:

"... the address of any debugging information entry within the same
executable or shared object... This type of reference (DW_FORM_ref_addr)
is the size of an address on the target architecture..."

And the DWARF FAQ says:

"The DWARF V2 definition never made any sense and was a mistake in the
DWARF V2 specification: the field DW_FORM_ref_addr defines is an offset,
not an address. If you are producing DWARF V2, please use the DWARF V3
definition of DW_FORM_ref_addr."

Currently for DWARF V2, LLVM continues using address-sizing rather than
fully adopting the V3 definition (4 bytes for DWARF32, 8 bytes for
DWARF64) as directed by the FAQ.

Given that the DWARF V2 is rather old and that the worst case size
increase would match that of a non-LTO compilation flow, this MR
disables de-duplication of DIEs across CUs to prevent the generation of
cross-CU references using DW_FORM_ref_addr for DWARF V2.
---
 llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp     |  7 +++
 .../Generic/cross-cu-no-ref-addr-dwarf-v2.ll  | 47 +++++++++++++++++++
 2 files changed, 54 insertions(+)
 create mode 100644 llvm/test/DebugInfo/Generic/cross-cu-no-ref-addr-dwarf-v2.ll

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 35663f2acaae9..98726c30af952 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -185,6 +185,13 @@ bool DwarfUnit::isShareableAcrossCUs(const DINode *D) const {
   // level already) but may be implementable for some value in projects
   // building multiple independent libraries with LTO and then linking those
   // together.
+
+  // Prevent generation of cross-CU references for DWARF v2 due to conflicts
+  // resulting from the FAQ recommendation: "If you are producing DWARF V2,
+  // please use the DWARF V3 definition of DW_FORM_ref_addr."
+  // (https://dwarfstd.org/faq.html)
+  if (DD->getDwarfVersion() == 2)
+    return false;
   if (isDwoUnit() && !DD->shareAcrossDWOCUs())
     return false;
   return (isa<DIType>(D) ||
diff --git a/llvm/test/DebugInfo/Generic/cross-cu-no-ref-addr-dwarf-v2.ll b/llvm/test/DebugInfo/Generic/cross-cu-no-ref-addr-dwarf-v2.ll
new file mode 100644
index 0000000000000..0ed4fac68b99b
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/cross-cu-no-ref-addr-dwarf-v2.ll
@@ -0,0 +1,47 @@
+; RUN: %llc_dwarf -dwarf-version=3 -O0 -filetype=obj < %s | llvm-dwarfdump -v -debug-info - | FileCheck --check-prefix=CHECK-V3 %s
+; RUN: %llc_dwarf -dwarf-version=2 -O0 -filetype=obj < %s | llvm-dwarfdump -v -debug-info - | FileCheck --check-prefix=CHECK-V2 --implicit-check-not=DW_FORM_ref_addr %s
+
+; DWARF V3: Expect cross-CU type deduplication resulting in only one "int"
+; definition and a DW_FORM_ref_addr.
+
+; CHECK-V3: DW_TAG_compile_unit
+; CHECK-V3: DW_AT_name{{.*}}"a.cpp"
+; CHECK-V3: DW_TAG_base_type
+; CHECK-V3: DW_AT_name{{.*}}"int"
+; CHECK-V3: DW_TAG_compile_unit
+; CHECK-V3: DW_AT_name{{.*}}"b.cpp"
+; CHECK-V3: DW_AT_type [DW_FORM_ref_addr]
+
+; DWARF V2: cross-CU type deduplication disabled resulting in two "int"
+; definitions and no DW_FORM_ref_addr (cross-CU ref)
+
+; CHECK-V2: DW_TAG_compile_unit
+; CHECK-V2: DW_AT_name{{.*}}"a.cpp"
+; CHECK-V2: DW_TAG_base_type
+; CHECK-V2: DW_AT_name{{.*}}"int"
+; CHECK-V2: DW_TAG_compile_unit
+; CHECK-V2: DW_AT_name{{.*}}"b.cpp"
+; CHECK-V2: DW_TAG_base_type
+; CHECK-V2: DW_AT_name{{.*}}"int"
+
+ at x = global i32 0, !dbg !0
+ at y = global i32 0, !dbg !7
+
+!llvm.dbg.cu = !{!2, !9}
+!llvm.module.flags = !{!13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "x", scope: !2, file: !3, line: 1, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4)
+!3 = !DIFile(filename: "a.cpp", directory: "/tmp/dbginfo")
+!4 = !{!0}
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression())
+!8 = distinct !DIGlobalVariable(name: "y", scope: !9, file: !10, line: 1, type: !5, isLocal: false, isDefinition: true)
+!9 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !10, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !11)
+!10 = !DIFile(filename: "b.cpp", directory: "/tmp/dbginfo")
+!11 = !{!7}
+!13 = !{i32 2, !"Dwarf Version", i32 4}
+!14 = !{i32 2, !"Debug Info Version", i32 3}
+!15 = !{!"clang"}



More information about the llvm-commits mailing list