[llvm-branch-commits] [llvm] release/23.x: [DWARFLinker] Keep address-independent block attributes in the type unit (#215932) (PR #216190)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Aug 13 14:57:59 PDT 2026
https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/216190
Backport 3ae0d109f87dedd2a3f5bdf517556ba3d44283d6
Requested by: @JDevlieghere
>From 984bd3ac13027ddb28e49ea8a36c2c4057f5eeb6 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Thu, 13 Aug 2026 14:49:23 -0700
Subject: [PATCH] [DWARFLinker] Keep address-independent block attributes in
the type unit (#215932)
cloneBlockAttr dropped every block and exprloc attribute whose DIE was
placed in the artificial type unit. The intent was to discard location
expressions that resolve against a single origin compile unit, since a
type unit is shared by every unit referencing the type, but the check
was incorrectly applied to all block-form attributes.
Clang encodes DW_AT_data_member_location as DW_FORM_block1 at DWARF 2
and as a constant from DWARF 3 onward, so at DWARF 2 every member and
inheritance offset of a deduplicated record collapsed to zero. This
caused LLDB to crash when trying to build types using the wrong offsets,
but that's a separate issue.
Drop only expressions that carry a target address or reference a base
type DIE, which are the cases the type unit cannot resolve.
(cherry picked from commit 3ae0d109f87dedd2a3f5bdf517556ba3d44283d6)
---
.../Parallel/DIEAttributeCloner.cpp | 29 +-
.../dwarf2-member-location.s | 261 ++++++++++++++++++
2 files changed, 287 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s
diff --git a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
index c58d2937f3ed0..f58869d20291c 100644
--- a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
@@ -549,13 +549,30 @@ size_t DIEAttributeCloner::cloneScalarAttr(
return Result.second;
}
+static bool expressionDependsOnOriginUnit(const DWARFExpression &Expr) {
+ using Encoding = DWARFExpression::Operation::Encoding;
+
+ for (const DWARFExpression::Operation &Op : Expr) {
+ switch (Op.getCode()) {
+ case dwarf::DW_OP_addr:
+ case dwarf::DW_OP_addrx:
+ case dwarf::DW_OP_constx:
+ return true;
+ default:
+ break;
+ }
+
+ if (llvm::is_contained(Op.getDescription().Op, Encoding::BaseTypeRef))
+ return true;
+ }
+
+ return false;
+}
+
size_t DIEAttributeCloner::cloneBlockAttr(
const DWARFFormValue &Val,
const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
- if (OutUnit.isTypeUnit())
- return 0;
-
size_t NumberOfPatchesAtStart = PatchesOffsets.size();
// If the block is a DWARF Expression, clone it into the temporary
@@ -569,6 +586,12 @@ size_t DIEAttributeCloner::cloneBlockAttr(
DWARFExpression Expr(Data, InUnit.getOrigUnit().getAddressByteSize(),
InUnit.getFormParams().Format);
+ // A type unit is shared by every compile unit that references the type, so
+ // an expression resolving against one origin unit has no single correct
+ // value there.
+ if (OutUnit.isTypeUnit() && expressionDependsOnOriginUnit(Expr))
+ return 0;
+
InUnit.cloneDieAttrExpression(Expr, Buffer, DebugInfoOutputSection,
VarAddressAdjustment, PatchesOffsets);
Bytes = Buffer;
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s
new file mode 100644
index 0000000000000..81d5e5d28a976
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s
@@ -0,0 +1,261 @@
+# DWARF 2 encodes DW_AT_data_member_location as a location expression rather
+# than a constant, and DW_AT_vtable_elem_location is an expression at every
+# DWARF version. Both must survive placement in the artificial type unit.
+
+# RUN: llvm-mc -triple x86_64-apple-darwin -filetype=obj %s -o %t.o
+# RUN: llvm-dwarfdump --verify %t.o
+
+# RUN: echo '---' > %t.map
+# RUN: echo "triple: 'x86_64-apple-darwin'" >> %t.map
+# RUN: echo 'objects:' >> %t.map
+# RUN: echo " - filename: '%t.o'" >> %t.map
+# RUN: echo ' symbols:' >> %t.map
+# RUN: echo ' - { sym: __Z4keepP7Derived, objAddr: 0x0, binAddr: 0x10000, size: 0x1 }' >> %t.map
+# RUN: echo '...' >> %t.map
+
+# RUN: dsymutil --linker=parallel -y %t.map -f -o %t.parallel.dSYM
+# RUN: llvm-dwarfdump --verify %t.parallel.dSYM
+# RUN: llvm-dwarfdump -debug-info %t.parallel.dSYM \
+# RUN: | FileCheck %s --check-prefix=PARALLEL
+
+# RUN: dsymutil --linker=classic -y %t.map -f -o %t.classic.dSYM
+# RUN: llvm-dwarfdump --verify %t.classic.dSYM
+# RUN: llvm-dwarfdump -debug-info %t.classic.dSYM \
+# RUN: | FileCheck %s --check-prefix=CLASSIC
+
+# The parallel linker moves the deduplicated types into the artificial type unit
+# and the classic linker keeps them in the compile unit, so the two emit them in
+# different orders.
+
+# PARALLEL: DW_AT_name{{.*}}"__artificial_type_unit"
+# PARALLEL: DW_TAG_structure_type
+# PARALLEL: DW_AT_name{{.*}}"Base"
+# PARALLEL: DW_TAG_member
+# PARALLEL: DW_AT_name{{.*}}"first"
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x0)
+# PARALLEL: DW_TAG_member
+# PARALLEL: DW_AT_name{{.*}}"second"
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x4)
+# PARALLEL: DW_TAG_structure_type
+# PARALLEL: DW_AT_name{{.*}}"Derived"
+# PARALLEL: DW_TAG_inheritance
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x8)
+# PARALLEL: DW_TAG_member
+# PARALLEL: DW_AT_name{{.*}}"third"
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x10)
+# PARALLEL: DW_TAG_subprogram
+# PARALLEL: DW_AT_name{{.*}}"virt"
+# PARALLEL: DW_AT_vtable_elem_location{{.*}}(DW_OP_constu 0x0)
+
+# CLASSIC: DW_TAG_structure_type
+# CLASSIC: DW_AT_name{{.*}}"Derived"
+# CLASSIC: DW_TAG_inheritance
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x8)
+# CLASSIC: DW_TAG_member
+# CLASSIC: DW_AT_name{{.*}}"third"
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x10)
+# CLASSIC: DW_TAG_subprogram
+# CLASSIC: DW_AT_name{{.*}}"virt"
+# CLASSIC: DW_AT_vtable_elem_location{{.*}}(DW_OP_constu 0x0)
+# CLASSIC: DW_TAG_structure_type
+# CLASSIC: DW_AT_name{{.*}}"Base"
+# CLASSIC: DW_TAG_member
+# CLASSIC: DW_AT_name{{.*}}"first"
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x0)
+# CLASSIC: DW_TAG_member
+# CLASSIC: DW_AT_name{{.*}}"second"
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x4)
+
+ .section __TEXT,__text,regular,pure_instructions
+ .globl __Z4keepP7Derived
+__Z4keepP7Derived:
+Lfunc_begin0:
+ retq
+Lfunc_end0:
+
+ .section __DWARF,__debug_abbrev,regular,debug
+Lsection_abbrev:
+ .byte 1 ## Abbreviation Code
+ .byte 17 ## DW_TAG_compile_unit
+ .byte 1 ## DW_CHILDREN_yes
+ .byte 37 ## DW_AT_producer
+ .byte 8 ## DW_FORM_string
+ .byte 19 ## DW_AT_language
+ .byte 5 ## DW_FORM_data2
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 0, 0
+
+ .byte 2 ## Abbreviation Code
+ .byte 46 ## DW_TAG_subprogram
+ .byte 1 ## DW_CHILDREN_yes
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 0x87, 0x40 ## DW_AT_MIPS_linkage_name (0x2007)
+ .byte 8 ## DW_FORM_string
+ .byte 17 ## DW_AT_low_pc
+ .byte 1 ## DW_FORM_addr
+ .byte 18 ## DW_AT_high_pc
+ .byte 1 ## DW_FORM_addr
+ .byte 63 ## DW_AT_external
+ .byte 12 ## DW_FORM_flag
+ .byte 0, 0
+
+ .byte 3 ## Abbreviation Code
+ .byte 5 ## DW_TAG_formal_parameter
+ .byte 0 ## DW_CHILDREN_no
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 0, 0
+
+ .byte 4 ## Abbreviation Code
+ .byte 19 ## DW_TAG_structure_type
+ .byte 1 ## DW_CHILDREN_yes
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 11 ## DW_AT_byte_size
+ .byte 11 ## DW_FORM_data1
+ .byte 0, 0
+
+ .byte 5 ## Abbreviation Code
+ .byte 28 ## DW_TAG_inheritance
+ .byte 0 ## DW_CHILDREN_no
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 56 ## DW_AT_data_member_location
+ .byte 10 ## DW_FORM_block1
+ .byte 0, 0
+
+ .byte 6 ## Abbreviation Code
+ .byte 13 ## DW_TAG_member
+ .byte 0 ## DW_CHILDREN_no
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 56 ## DW_AT_data_member_location
+ .byte 10 ## DW_FORM_block1
+ .byte 0, 0
+
+ .byte 7 ## Abbreviation Code
+ .byte 46 ## DW_TAG_subprogram
+ .byte 0 ## DW_CHILDREN_no
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 0x87, 0x40 ## DW_AT_MIPS_linkage_name (0x2007)
+ .byte 8 ## DW_FORM_string
+ .byte 76 ## DW_AT_virtuality
+ .byte 11 ## DW_FORM_data1
+ .byte 77 ## DW_AT_vtable_elem_location
+ .byte 10 ## DW_FORM_block1
+ .byte 60 ## DW_AT_declaration
+ .byte 12 ## DW_FORM_flag
+ .byte 0, 0
+
+ .byte 8 ## Abbreviation Code
+ .byte 36 ## DW_TAG_base_type
+ .byte 0 ## DW_CHILDREN_no
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 11 ## DW_AT_byte_size
+ .byte 11 ## DW_FORM_data1
+ .byte 62 ## DW_AT_encoding
+ .byte 11 ## DW_FORM_data1
+ .byte 0, 0
+
+ .byte 9 ## Abbreviation Code
+ .byte 15 ## DW_TAG_pointer_type
+ .byte 0 ## DW_CHILDREN_no
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 0, 0
+
+ .byte 0 ## EOM(3)
+
+ .section __DWARF,__debug_info,regular,debug
+Lsection_info:
+ .long Lcu_end - Lcu_start ## Length of Unit
+Lcu_start:
+ .short 2 ## DWARF version number
+ .long 0 ## Offset Into Abbrev. Section
+ .byte 8 ## Address Size (in bytes)
+
+ .byte 1 ## Abbrev [1] DW_TAG_compile_unit
+ .asciz "hand-written" ## DW_AT_producer
+ .short 0x0004 ## DW_AT_language (DW_LANG_C_plus_plus)
+ .asciz "dwarf2-member-location.cpp" ## DW_AT_name
+
+ .byte 2 ## Abbrev [2] DW_TAG_subprogram
+ .asciz "keep" ## DW_AT_name
+ .asciz "__Z4keepP7Derived" ## DW_AT_MIPS_linkage_name
+ .quad Lfunc_begin0 ## DW_AT_low_pc
+ .quad Lfunc_end0 ## DW_AT_high_pc
+ .byte 1 ## DW_AT_external
+
+ .byte 3 ## Abbrev [3] DW_TAG_formal_parameter
+ .long Lderived_ptr - Lsection_info ## DW_AT_type
+
+ .byte 0 ## End Of Children Mark (keep)
+
+Lderived:
+ .byte 4 ## Abbrev [4] DW_TAG_structure_type
+ .asciz "Derived" ## DW_AT_name
+ .byte 0x18 ## DW_AT_byte_size
+
+ .byte 5 ## Abbrev [5] DW_TAG_inheritance
+ .long Lbase - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x08 ## 0x8
+
+ .byte 6 ## Abbrev [6] DW_TAG_member
+ .asciz "third" ## DW_AT_name
+ .long Lint - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x10 ## 0x10
+
+ .byte 7 ## Abbrev [7] DW_TAG_subprogram
+ .asciz "virt" ## DW_AT_name
+ .asciz "__ZN7Derived4virtEv" ## DW_AT_MIPS_linkage_name
+ .byte 1 ## DW_AT_virtuality (DW_VIRTUALITY_virtual)
+ .byte 2 ## DW_AT_vtable_elem_location length
+ .byte 0x10 ## DW_OP_constu
+ .byte 0x00 ## 0x0
+ .byte 1 ## DW_AT_declaration
+
+ .byte 0 ## End Of Children Mark (Derived)
+
+Lbase:
+ .byte 4 ## Abbrev [4] DW_TAG_structure_type
+ .asciz "Base" ## DW_AT_name
+ .byte 0x08 ## DW_AT_byte_size
+
+ .byte 6 ## Abbrev [6] DW_TAG_member
+ .asciz "first" ## DW_AT_name
+ .long Lint - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x00 ## 0x0
+
+ .byte 6 ## Abbrev [6] DW_TAG_member
+ .asciz "second" ## DW_AT_name
+ .long Lint - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x04 ## 0x4
+
+ .byte 0 ## End Of Children Mark (Base)
+
+Lint:
+ .byte 8 ## Abbrev [8] DW_TAG_base_type
+ .asciz "int" ## DW_AT_name
+ .byte 4 ## DW_AT_byte_size
+ .byte 5 ## DW_AT_encoding (DW_ATE_signed)
+
+Lderived_ptr:
+ .byte 9 ## Abbrev [9] DW_TAG_pointer_type
+ .long Lderived - Lsection_info ## DW_AT_type
+
+ .byte 0 ## End Of Children Mark (CU)
+Lcu_end:
More information about the llvm-branch-commits
mailing list