[llvm] Allow multi-member variants in DWARF (PR #139300)
via llvm-commits
llvm-commits at lists.llvm.org
Fri May 9 11:01:37 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Tom Tromey (tromey)
<details>
<summary>Changes</summary>
Currently, each variant in the variant part of a structure type can only contain a single member. This was sufficient for Rust, where each variant is represented as its own type.
However, this isn't really enough for Ada, where a variant can have multiple members.
This patch adds support for this scenario. This is done by allowing the use of DW_TAG_variant by DICompositeType, and then changing the DWARF generator to recognize when a DIDerivedType representing a variant holds one of these. In this case, the fields from the DW_TAG_variant are inlined into the variant, like so:
<4><7d>: Abbrev Number: 9 (DW_TAG_variant)
<7e> DW_AT_discr_value : 74
<5><7f>: Abbrev Number: 7 (DW_TAG_member)
<80> DW_AT_name : (indirect string, offset: 0x43): field0
<84> DW_AT_type : <0xa7>
<88> DW_AT_alignment : 8
<89> DW_AT_data_member_location: 0
<5><8a>: Abbrev Number: 7 (DW_TAG_member)
<8b> DW_AT_name : (indirect string, offset: 0x4a): field1
<8f> DW_AT_type : <0xa7>
<93> DW_AT_alignment : 8
<94> DW_AT_data_member_location: 8
Note that the intermediate DIDerivedType is still needed in this situation, because that is where the discriminants are stored.
---
Full diff: https://github.com/llvm/llvm-project/pull/139300.diff
6 Files Affected:
- (modified) llvm/docs/LangRef.rst (+12)
- (modified) llvm/include/llvm/IR/DIBuilder.h (+13)
- (modified) llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp (+12-1)
- (modified) llvm/lib/IR/DIBuilder.cpp (+13)
- (modified) llvm/lib/IR/Verifier.cpp (+1)
- (added) llvm/test/DebugInfo/Generic/multi-variant.ll (+74)
``````````diff
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 7296bb84b7d95..5f14726c36672 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -6363,6 +6363,8 @@ The following ``tag:`` values are valid:
DW_TAG_enumeration_type = 4
DW_TAG_structure_type = 19
DW_TAG_union_type = 23
+ DW_TAG_variant = 25
+ DW_TAG_variant_part = 51
For ``DW_TAG_array_type``, the ``elements:`` should be :ref:`subrange
descriptors <DISubrange>` or :ref:`subrange descriptors
@@ -6398,6 +6400,16 @@ For ``DW_TAG_structure_type``, ``DW_TAG_class_type``, and
``tag: DW_TAG_friend``; or :ref:`subprograms <DISubprogram>` with
``isDefinition: false``.
+``DW_TAG_variant_part`` introduces a variant part of a structure type.
+This should have a discriminant, a member that is used to decide which
+elements are active. The elements of the variant part should each be
+a ``DW_TAG_member``; if a member has a non-null ``ExtraData``, then it
+is a ``ConstantInt`` or ``ConstantDataArray`` indicating the values of
+the discriminant member that cause the activation of this branch. A
+member itself may be of composite type with tag ``DW_TAG_variant``; in
+this case the members of that composite type are inlined into the
+current one.
+
.. _DISubrange:
DISubrange
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index d293c28f5b450..4ce71bd3dad58 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -406,6 +406,19 @@ namespace llvm {
Constant *Discriminant,
DINode::DIFlags Flags, DIType *Ty);
+ /// Create debugging information entry for a variant. A variant
+ /// created this way "inlines" multiple members into the enclosing
+ /// variant part.
+ /// \param Scope Scope in which this variant is defined.
+ /// \param Elements Variant elements.
+ /// \param Discriminant The discriminant for this branch; null for
+ /// the default branch. This may be a
+ /// ConstantDataArray if the variant applies
+ /// for multiple discriminants.
+ /// \param Ty Parent type.
+ DIDerivedType *createVariantMemberType(DIScope *Scope, DINodeArray Elements,
+ Constant *Discriminant, DIType *Ty);
+
/// Create debugging information entry for a bit field member.
/// \param Scope Member scope.
/// \param Name Member name.
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 5f04e8b0d18ba..5a1ac5d662704 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -1013,6 +1013,7 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
constructEnumTypeDIE(Buffer, CTy);
break;
case dwarf::DW_TAG_variant_part:
+ case dwarf::DW_TAG_variant:
case dwarf::DW_TAG_structure_type:
case dwarf::DW_TAG_union_type:
case dwarf::DW_TAG_class_type:
@@ -1066,7 +1067,17 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
addDiscriminant(Variant, CI,
DD->isUnsignedDIType(Discriminator->getBaseType()));
}
- constructMemberDIE(Variant, DDTy);
+ // If the variant holds a composite type with tag
+ // DW_TAG_variant, inline those members into the variant
+ // DIE.
+ if (auto *Composite =
+ dyn_cast_or_null<DICompositeType>(DDTy->getBaseType());
+ Composite != nullptr &&
+ Composite->getTag() == dwarf::DW_TAG_variant) {
+ constructTypeDIE(Variant, Composite);
+ } else {
+ constructMemberDIE(Variant, DDTy);
+ }
} else {
constructMemberDIE(Buffer, DDTy);
}
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index d9cc49fdad89c..90da9f3acfe57 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -444,6 +444,19 @@ DIDerivedType *DIBuilder::createVariantMemberType(
std::nullopt, std::nullopt, Flags, getConstantOrNull(Discriminant));
}
+DIDerivedType *DIBuilder::createVariantMemberType(DIScope *Scope,
+ DINodeArray Elements,
+ Constant *Discriminant,
+ DIType *Ty) {
+ auto *V = DICompositeType::get(VMContext, dwarf::DW_TAG_variant, {}, nullptr,
+ 0, getNonCompileUnitScope(Scope), {}, 0, 0, 0,
+ DINode::FlagZero, Elements, 0, {}, nullptr);
+
+ trackIfUnresolved(V);
+ return createVariantMemberType(Scope, {}, nullptr, 0, 0, 0, 0, Discriminant,
+ DINode::FlagZero, V);
+}
+
DIDerivedType *DIBuilder::createBitFieldMemberType(
DIScope *Scope, StringRef Name, DIFile *File, unsigned LineNumber,
uint64_t SizeInBits, uint64_t OffsetInBits, uint64_t StorageOffsetInBits,
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 83c1264aef12b..2cfd3822ea05d 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1343,6 +1343,7 @@ void Verifier::visitDICompositeType(const DICompositeType &N) {
N.getTag() == dwarf::DW_TAG_enumeration_type ||
N.getTag() == dwarf::DW_TAG_class_type ||
N.getTag() == dwarf::DW_TAG_variant_part ||
+ N.getTag() == dwarf::DW_TAG_variant ||
N.getTag() == dwarf::DW_TAG_namelist,
"invalid tag", &N);
diff --git a/llvm/test/DebugInfo/Generic/multi-variant.ll b/llvm/test/DebugInfo/Generic/multi-variant.ll
new file mode 100644
index 0000000000000..1c680b371bb8f
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/multi-variant.ll
@@ -0,0 +1,74 @@
+; RUN: %llc_dwarf -O0 -filetype=obj < %s > %t
+; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck %s
+
+; Check for a variant part where a variant has multiple members.
+
+; CHECK: DW_AT_name [DW_FORM_str{{[a-z]+}}] ({{.*}} = "Discr")
+; CHECK: DW_TAG_variant_part
+; CHECK-NOT: TAG
+; CHECK: DW_AT_discr [DW_FORM_ref4] (cu + {{0x[0-9a-fA-F]+}} => {[[OFFSET:0x[0-9a-fA-F]+]]})
+; CHECK: DW_TAG_variant
+; CHECK: DW_AT_discr_value [DW_FORM_data1] (0x4a)
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_name [DW_FORM_str{{[a-z]+}}] ({{.*}} = "field0")
+; CHECK: DW_AT_type
+; CHECK: DW_AT_alignment
+; CHECK: DW_AT_data_member_location [DW_FORM_data1] (0x00)
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_name [DW_FORM_str{{[a-z]+}}] ({{.*}} = "field1")
+; CHECK: DW_AT_type
+; CHECK: DW_AT_alignment
+; CHECK: DW_AT_data_member_location [DW_FORM_data1] (0x08)
+; CHECK: DW_TAG_variant
+; CHECK: DW_AT_discr_value [DW_FORM_data1] (0x4b)
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_name [DW_FORM_str{{[a-z]+}}] ({{.*}} = "field2")
+; CHECK: DW_AT_type
+; CHECK: DW_AT_alignment
+; CHECK: DW_AT_data_member_location [DW_FORM_data1] (0x00)
+
+%F = type { [0 x i8], ptr, [8 x i8] }
+
+define internal void @_ZN2e34main17h934ff72f9a38d4bbE() unnamed_addr #0 !dbg !5 {
+start:
+ %qq = alloca %F, align 8
+ call void @llvm.dbg.declare(metadata ptr %qq, metadata !10, metadata !24), !dbg !25
+ store ptr null, ptr %qq, !dbg !25
+ ret void, !dbg !26
+}
+
+; Function Attrs: nounwind readnone
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
+
+attributes #0 = { nounwind uwtable }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.dbg.cu = !{!2}
+
+!0 = !{i32 1, !"PIE Level", i32 2}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = distinct !DICompileUnit(language: DW_LANG_Ada95, file: !3, producer: "gnat-llvm", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4)
+!3 = !DIFile(filename: "e3.rs", directory: "/home/tromey/Ada")
+!4 = !{}
+!5 = distinct !DISubprogram(name: "main", linkageName: "_ZN2e34mainE", scope: !6, file: !3, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagMainSubprogram, isOptimized: false, unit: !2, templateParams: !4, retainedNodes: !4)
+!6 = !DINamespace(name: "e3", scope: null)
+!7 = !DIFile(filename: "<unknown>", directory: "")
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !DILocalVariable(name: "qq", scope: !11, file: !3, line: 3, type: !12, align: 64)
+!11 = distinct !DILexicalBlock(scope: !5, file: !3, line: 3, column: 4)
+!12 = !DICompositeType(tag: DW_TAG_structure_type, name: "F", scope: !6, file: !7, size: 128, align: 64, elements: !13, identifier: "7ce1efff6b82281ab9ceb730566e7e20")
+!13 = !{!14, !15}
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "Discr", scope: !12, file: !7, baseType: !23, size: 64, align: 64)
+!15 = !DICompositeType(tag: DW_TAG_variant_part, scope: !12, file: !7, size: 128, align: 64, elements: !16, identifier: "7ce1efff6b82281ab9ceb730566e7e20", discriminator: !14)
+!16 = !{!17, !22}
+!17 = !DIDerivedType(tag: DW_TAG_member, scope: !15, file: !7, baseType: !18, size: 128, align: 64, extraData: i32 74)
+!18 = !DICompositeType(tag: DW_TAG_variant, scope: !15, file: !7, size: 128, align: 64, elements: !19)
+!19 = !{!20, !21}
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "field0", scope: !18, file: !7, baseType: !23, size: 64, align: 64, offset: 0)
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "field1", scope: !18, file: !7, baseType: !23, size: 64, align: 64, offset: 64)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "field2", scope: !15, file: !7, baseType: !23, size: 64, align: 64, offset: 0, extraData: i32 75)
+!23 = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned)
+!24 = !DIExpression()
+!25 = !DILocation(line: 3, scope: !11)
+!26 = !DILocation(line: 4, scope: !5)
``````````
</details>
https://github.com/llvm/llvm-project/pull/139300
More information about the llvm-commits
mailing list