[llvm] Two DWARF variant part improvements (PR #138953)

via llvm-commits llvm-commits at lists.llvm.org
Wed May 7 13:15:05 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-debuginfo

Author: Tom Tromey (tromey)

<details>
<summary>Changes</summary>

This patch adds a couple of improvements to the LLVM emission of DWARF variant parts.  One of these is desirable for Ada, and the other is required.

Currently, when emitting a discriminant, LLVM follows the precise letter of the DWARF standard, which says:

    If the variant part has a discriminant, the discriminant is
    represented by a separate debugging information entry which is a
    child of the variant part entry.

However, for Ada this does not really make sense.  In Ada, the discriminant field exists outside of any variant part, and it makes more sense to emit it separately rather than redundantly emit the field once for each variant part.

This extension was arrived at when this was implemented in GCC, and was accepted for DWARF 6, see:

    https://dwarfstd.org/issues/180123.1.html

Here the patch simply lifts this restriction: if the discriminant field was already emitted, it isn't re-emitted.  This approach allows the Ada compiler to do what it needs without affecting the Rust output.

Second, this patch extends the discriminant to allow multiple values. This is needed by Ada.  Here, I chose to use a ConstantDataArray of pairs of integers, with each pair representing a range, as Ada also allows ranges here.  This seemed like a reasonably convenient representation.

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


4 Files Affected:

- (modified) llvm/include/llvm/IR/DIBuilder.h (+3-1) 
- (modified) llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp (+53-8) 
- (modified) llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h (+5-1) 
- (added) llvm/test/DebugInfo/Generic/discriminant-member.ll (+74) 


``````````diff
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index 8e62b810ff147..d293c28f5b450 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -394,7 +394,9 @@ namespace llvm {
     /// \param OffsetInBits Member offset.
     /// \param Flags        Flags to encode member attribute, e.g. private
     /// \param Discriminant The discriminant for this branch; null for
-    ///                     the default branch
+    ///                     the default branch.  This may be a
+    ///                     ConstantDataArray if the variant applies
+    ///                     for multiple discriminants.
     /// \param Ty           Parent type.
     DIDerivedType *createVariantMemberType(DIScope *Scope, StringRef Name,
 					   DIFile *File, unsigned LineNo,
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index bf166cbb043de..b5c8b6d0479d4 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -275,7 +275,7 @@ void DwarfUnit::addSInt(DIEValueList &Die, dwarf::Attribute Attribute,
   addAttribute(Die, Attribute, *Form, DIEInteger(Integer));
 }
 
-void DwarfUnit::addSInt(DIELoc &Die, std::optional<dwarf::Form> Form,
+void DwarfUnit::addSInt(DIEValueList &Die, std::optional<dwarf::Form> Form,
                         int64_t Integer) {
   addSInt(Die, (dwarf::Attribute)0, Form, Integer);
 }
@@ -961,6 +961,43 @@ void DwarfUnit::addAnnotation(DIE &Buffer, DINodeArray Annotations) {
   }
 }
 
+void DwarfUnit::addDiscriminant(DIE &Variant, Constant *Discriminant,
+                                bool IsUnsigned) {
+  if (const auto *CI = dyn_cast_or_null<ConstantInt>(Discriminant)) {
+    addInt(Variant, dwarf::DW_AT_discr_value, CI->getValue(), IsUnsigned);
+  } else if (const auto *CA =
+                 dyn_cast_or_null<ConstantDataArray>(Discriminant)) {
+    // Must have an even number of operands.
+    unsigned NElems = CA->getNumElements();
+    if (NElems % 2 != 0) {
+      return;
+    }
+
+    DIEBlock *Block = new (DIEValueAllocator) DIEBlock;
+
+    const auto AddInt = [&](const APInt &Val) {
+      if (IsUnsigned)
+        addUInt(*Block, dwarf::DW_FORM_udata, Val.getZExtValue());
+      else
+        addSInt(*Block, dwarf::DW_FORM_sdata, Val.getSExtValue());
+    };
+
+    for (unsigned I = 0; I < NElems; I += 2) {
+      const APInt LV = CA->getElementAsAPInt(I);
+      const APInt HV = CA->getElementAsAPInt(I + 1);
+      if (LV == HV) {
+        addUInt(*Block, dwarf::DW_FORM_data1, dwarf::DW_DSC_label);
+        AddInt(LV);
+      } else {
+        addUInt(*Block, dwarf::DW_FORM_data1, dwarf::DW_DSC_range);
+        AddInt(LV);
+        AddInt(HV);
+      }
+    }
+    addBlock(Variant, dwarf::DW_AT_discr_list, Block);
+  }
+}
+
 void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
   // Add name if not anonymous or intermediate type.
   StringRef Name = CTy->getName();
@@ -989,8 +1026,17 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
         //    If the variant part has a discriminant, the discriminant is
         //    represented by a separate debugging information entry which is
         //    a child of the variant part entry.
-        DIE &DiscMember = constructMemberDIE(Buffer, Discriminator);
-        addDIEEntry(Buffer, dwarf::DW_AT_discr, DiscMember);
+        // However, for a language like Ada, this yields a weird
+        // result: a discriminant field would have to be emitted
+        // multiple times, once per variant part.  Instead, this DWARF
+        // restriction was lifted for DWARF 6 (see
+        // https://dwarfstd.org/issues/180123.1.html) and so we allow
+        // this here.
+        DIE *DiscDIE = getDIE(Discriminator);
+        if (DiscDIE == nullptr) {
+          DiscDIE = &constructMemberDIE(Buffer, Discriminator);
+        }
+        addDIEEntry(Buffer, dwarf::DW_AT_discr, *DiscDIE);
       }
     }
 
@@ -1016,10 +1062,9 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
           // When emitting a variant part, wrap each member in
           // DW_TAG_variant.
           DIE &Variant = createAndAddDIE(dwarf::DW_TAG_variant, Buffer);
-          if (const ConstantInt *CI =
-              dyn_cast_or_null<ConstantInt>(DDTy->getDiscriminantValue())) {
-	    addInt(Variant, dwarf::DW_AT_discr_value, CI->getValue(),
-		   DD->isUnsignedDIType(Discriminator->getBaseType()));
+          if (Constant *CI = DDTy->getDiscriminantValue()) {
+            addDiscriminant(Variant, CI,
+                            DD->isUnsignedDIType(Discriminator->getBaseType()));
           }
           constructMemberDIE(Variant, DDTy);
         } else {
@@ -1755,7 +1800,7 @@ void DwarfUnit::constructContainingTypeDIEs() {
 }
 
 DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
-  DIE &MemberDie = createAndAddDIE(DT->getTag(), Buffer);
+  DIE &MemberDie = createAndAddDIE(DT->getTag(), Buffer, DT);
   StringRef Name = DT->getName();
   if (!Name.empty())
     addString(MemberDie, dwarf::DW_AT_name, Name);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
index 055d7173daec5..e1156bccfb1ab 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
@@ -165,7 +165,8 @@ class DwarfUnit : public DIEUnit {
   void addSInt(DIEValueList &Die, dwarf::Attribute Attribute,
                std::optional<dwarf::Form> Form, int64_t Integer);
 
-  void addSInt(DIELoc &Die, std::optional<dwarf::Form> Form, int64_t Integer);
+  void addSInt(DIEValueList &Die, std::optional<dwarf::Form> Form,
+               int64_t Integer);
 
   /// Add an integer attribute data and value; value may be any width.
   void addInt(DIE &Die, dwarf::Attribute Attribute, const APInt &Integer,
@@ -342,6 +343,9 @@ class DwarfUnit : public DIEUnit {
   /// form.
   void addIntAsBlock(DIE &Die, dwarf::Attribute Attribute, const APInt &Val);
 
+  // Add discriminant constants to a DW_TAG_variant DIE.
+  void addDiscriminant(DIE &Variant, Constant *Discriminant, bool IsUnsigned);
+
   void constructTypeDIE(DIE &Buffer, const DIBasicType *BTy);
   void constructTypeDIE(DIE &Buffer, const DIFixedPointType *BTy);
   void constructTypeDIE(DIE &Buffer, const DIStringType *BTy);
diff --git a/llvm/test/DebugInfo/Generic/discriminant-member.ll b/llvm/test/DebugInfo/Generic/discriminant-member.ll
new file mode 100644
index 0000000000000..8359aef870e14
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/discriminant-member.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 that has two members, where one uses a list
+; of discriminants, and where both refer to a DIE that is not a child
+; of the variant.
+
+; CHECK: DW_AT_name [DW_FORM_strp]  ({{.*}} = "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_list [DW_FORM_block1] (<0x05> 00 17 01 61 6c )
+;       CHECK: DW_TAG_member
+;         CHECK: DW_AT_name [DW_FORM_strp]  ({{.*}} = "var0")
+;         CHECK: DW_AT_type
+;         CHECK: DW_AT_alignment
+;         CHECK: DW_AT_data_member_location [DW_FORM_data1]	(0x00)
+;     CHECK: DW_TAG_variant
+;       CHECK: DW_AT_discr_value [DW_FORM_data1] (0x4b)
+;       CHECK: DW_TAG_member
+;         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] }
+%"F::Nope" = type {}
+
+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 !28), !dbg !29
+  store ptr null, ptr %qq, !dbg !29
+  ret void, !dbg !30
+}
+
+; 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: 8)
+!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: !27, 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, !24}
+!17 = !DIDerivedType(tag: DW_TAG_member, name: "var0", scope: !15, file: !7, baseType: !18, size: 128, align: 64, extraData: [4 x i32] [i32 23, i32 23, i32 97, i32 108])
+!18 = !DICompositeType(tag: DW_TAG_structure_type, name: "Yep", scope: !12, file: !7, size: 128, align: 64, elements: !19, identifier: "7ce1efff6b82281ab9ceb730566e7e20::Yep")
+!19 = !{!20, !22}
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "var1", scope: !18, file: !7, baseType: !21, size: 8, align: 8, offset: 64)
+!21 = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned)
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "var2", scope: !18, file: !7, baseType: !23, size: 64, align: 64)
+!23 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&u8", baseType: !21, size: 64, align: 64)
+!24 = !DIDerivedType(tag: DW_TAG_member, scope: !15, file: !7, baseType: !25, size: 128, align: 64, extraData: i32 75)
+!25 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nope", scope: !12, file: !7, size: 128, align: 64, elements: !4, identifier: "7ce1efff6b82281ab9ceb730566e7e20::Nope")
+!27 = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned)
+!28 = !DIExpression()
+!29 = !DILocation(line: 3, scope: !11)
+!30 = !DILocation(line: 4, scope: !5)

``````````

</details>


https://github.com/llvm/llvm-project/pull/138953


More information about the llvm-commits mailing list