From 38eec2399193aa06dc5cb4f644a452d99895b4af Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Thu, 8 May 2025 13:35:36 -0600 Subject: [PATCH] Allow multi-member variants in DWARF 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. --- llvm/docs/LangRef.rst | 12 ++++ llvm/include/llvm/IR/DIBuilder.h | 13 ++++ llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 13 +++- llvm/lib/IR/DIBuilder.cpp | 13 ++++ llvm/lib/IR/Verifier.cpp | 1 + llvm/test/DebugInfo/Generic/multi-variant.ll | 74 ++++++++++++++++++++ 6 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 llvm/test/DebugInfo/Generic/multi-variant.ll 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 ` 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 ` 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(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: "", 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)