Skip to content

Commit b0bf48d

Browse files
authored
Two DWARF variant part improvements (#138953)
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.
1 parent cf2f558 commit b0bf48d

File tree

4 files changed

+135
-10
lines changed

4 files changed

+135
-10
lines changed

llvm/include/llvm/IR/DIBuilder.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,9 @@ namespace llvm {
394394
/// \param OffsetInBits Member offset.
395395
/// \param Flags Flags to encode member attribute, e.g. private
396396
/// \param Discriminant The discriminant for this branch; null for
397-
/// the default branch
397+
/// the default branch. This may be a
398+
/// ConstantDataArray if the variant applies
399+
/// for multiple discriminants.
398400
/// \param Ty Parent type.
399401
DIDerivedType *createVariantMemberType(DIScope *Scope, StringRef Name,
400402
DIFile *File, unsigned LineNo,

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ void DwarfUnit::addSInt(DIEValueList &Die, dwarf::Attribute Attribute,
275275
addAttribute(Die, Attribute, *Form, DIEInteger(Integer));
276276
}
277277

278-
void DwarfUnit::addSInt(DIELoc &Die, std::optional<dwarf::Form> Form,
278+
void DwarfUnit::addSInt(DIEValueList &Die, std::optional<dwarf::Form> Form,
279279
int64_t Integer) {
280280
addSInt(Die, (dwarf::Attribute)0, Form, Integer);
281281
}
@@ -961,6 +961,43 @@ void DwarfUnit::addAnnotation(DIE &Buffer, DINodeArray Annotations) {
961961
}
962962
}
963963

964+
void DwarfUnit::addDiscriminant(DIE &Variant, Constant *Discriminant,
965+
bool IsUnsigned) {
966+
if (const auto *CI = dyn_cast_or_null<ConstantInt>(Discriminant)) {
967+
addInt(Variant, dwarf::DW_AT_discr_value, CI->getValue(), IsUnsigned);
968+
} else if (const auto *CA =
969+
dyn_cast_or_null<ConstantDataArray>(Discriminant)) {
970+
// Must have an even number of operands.
971+
unsigned NElems = CA->getNumElements();
972+
if (NElems % 2 != 0) {
973+
return;
974+
}
975+
976+
DIEBlock *Block = new (DIEValueAllocator) DIEBlock;
977+
978+
auto AddInt = [&](const APInt &Val) {
979+
if (IsUnsigned)
980+
addUInt(*Block, dwarf::DW_FORM_udata, Val.getZExtValue());
981+
else
982+
addSInt(*Block, dwarf::DW_FORM_sdata, Val.getSExtValue());
983+
};
984+
985+
for (unsigned I = 0; I < NElems; I += 2) {
986+
APInt LV = CA->getElementAsAPInt(I);
987+
APInt HV = CA->getElementAsAPInt(I + 1);
988+
if (LV == HV) {
989+
addUInt(*Block, dwarf::DW_FORM_data1, dwarf::DW_DSC_label);
990+
AddInt(LV);
991+
} else {
992+
addUInt(*Block, dwarf::DW_FORM_data1, dwarf::DW_DSC_range);
993+
AddInt(LV);
994+
AddInt(HV);
995+
}
996+
}
997+
addBlock(Variant, dwarf::DW_AT_discr_list, Block);
998+
}
999+
}
1000+
9641001
void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
9651002
// Add name if not anonymous or intermediate type.
9661003
StringRef Name = CTy->getName();
@@ -989,8 +1026,17 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
9891026
// If the variant part has a discriminant, the discriminant is
9901027
// represented by a separate debugging information entry which is
9911028
// a child of the variant part entry.
992-
DIE &DiscMember = constructMemberDIE(Buffer, Discriminator);
993-
addDIEEntry(Buffer, dwarf::DW_AT_discr, DiscMember);
1029+
// However, for a language like Ada, this yields a weird
1030+
// result: a discriminant field would have to be emitted
1031+
// multiple times, once per variant part. Instead, this DWARF
1032+
// restriction was lifted for DWARF 6 (see
1033+
// https://dwarfstd.org/issues/180123.1.html) and so we allow
1034+
// this here.
1035+
DIE *DiscDIE = getDIE(Discriminator);
1036+
if (DiscDIE == nullptr) {
1037+
DiscDIE = &constructMemberDIE(Buffer, Discriminator);
1038+
}
1039+
addDIEEntry(Buffer, dwarf::DW_AT_discr, *DiscDIE);
9941040
}
9951041
}
9961042

@@ -1016,10 +1062,9 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
10161062
// When emitting a variant part, wrap each member in
10171063
// DW_TAG_variant.
10181064
DIE &Variant = createAndAddDIE(dwarf::DW_TAG_variant, Buffer);
1019-
if (const ConstantInt *CI =
1020-
dyn_cast_or_null<ConstantInt>(DDTy->getDiscriminantValue())) {
1021-
addInt(Variant, dwarf::DW_AT_discr_value, CI->getValue(),
1022-
DD->isUnsignedDIType(Discriminator->getBaseType()));
1065+
if (Constant *CI = DDTy->getDiscriminantValue()) {
1066+
addDiscriminant(Variant, CI,
1067+
DD->isUnsignedDIType(Discriminator->getBaseType()));
10231068
}
10241069
constructMemberDIE(Variant, DDTy);
10251070
} else {
@@ -1755,7 +1800,7 @@ void DwarfUnit::constructContainingTypeDIEs() {
17551800
}
17561801

17571802
DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
1758-
DIE &MemberDie = createAndAddDIE(DT->getTag(), Buffer);
1803+
DIE &MemberDie = createAndAddDIE(DT->getTag(), Buffer, DT);
17591804
StringRef Name = DT->getName();
17601805
if (!Name.empty())
17611806
addString(MemberDie, dwarf::DW_AT_name, Name);

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ class DwarfUnit : public DIEUnit {
165165
void addSInt(DIEValueList &Die, dwarf::Attribute Attribute,
166166
std::optional<dwarf::Form> Form, int64_t Integer);
167167

168-
void addSInt(DIELoc &Die, std::optional<dwarf::Form> Form, int64_t Integer);
168+
void addSInt(DIEValueList &Die, std::optional<dwarf::Form> Form,
169+
int64_t Integer);
169170

170171
/// Add an integer attribute data and value; value may be any width.
171172
void addInt(DIE &Die, dwarf::Attribute Attribute, const APInt &Integer,
@@ -342,6 +343,9 @@ class DwarfUnit : public DIEUnit {
342343
/// form.
343344
void addIntAsBlock(DIE &Die, dwarf::Attribute Attribute, const APInt &Val);
344345

346+
// Add discriminant constants to a DW_TAG_variant DIE.
347+
void addDiscriminant(DIE &Variant, Constant *Discriminant, bool IsUnsigned);
348+
345349
void constructTypeDIE(DIE &Buffer, const DIBasicType *BTy);
346350
void constructTypeDIE(DIE &Buffer, const DIFixedPointType *BTy);
347351
void constructTypeDIE(DIE &Buffer, const DIStringType *BTy);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
; RUN: %llc_dwarf -O0 -filetype=obj < %s > %t
2+
; RUN: llvm-dwarfdump -v -debug-info %t | FileCheck %s
3+
4+
; Check for a variant part that has two members, where one uses a list
5+
; of discriminants, and where both refer to a DIE that is not a child
6+
; of the variant.
7+
8+
; CHECK: DW_AT_name [DW_FORM_strp] ({{.*}} = "Discr")
9+
; CHECK: DW_TAG_variant_part
10+
; CHECK-NOT: TAG
11+
; CHECK: DW_AT_discr [DW_FORM_ref4] (cu + {{0x[0-9a-fA-F]+}} => {[[OFFSET:0x[0-9a-fA-F]+]]})
12+
; CHECK: DW_TAG_variant
13+
; CHECK: DW_AT_discr_list [DW_FORM_block1] (<0x05> 00 17 01 61 6c )
14+
; CHECK: DW_TAG_member
15+
; CHECK: DW_AT_name [DW_FORM_strp] ({{.*}} = "var0")
16+
; CHECK: DW_AT_type
17+
; CHECK: DW_AT_alignment
18+
; CHECK: DW_AT_data_member_location [DW_FORM_data1] (0x00)
19+
; CHECK: DW_TAG_variant
20+
; CHECK: DW_AT_discr_value [DW_FORM_data1] (0x4b)
21+
; CHECK: DW_TAG_member
22+
; CHECK: DW_AT_type
23+
; CHECK: DW_AT_alignment
24+
; CHECK: DW_AT_data_member_location [DW_FORM_data1] (0x00)
25+
26+
%F = type { [0 x i8], ptr, [8 x i8] }
27+
%"F::Nope" = type {}
28+
29+
define internal void @_ZN2e34main17h934ff72f9a38d4bbE() unnamed_addr #0 !dbg !5 {
30+
start:
31+
%qq = alloca %F, align 8
32+
call void @llvm.dbg.declare(metadata ptr %qq, metadata !10, metadata !28), !dbg !29
33+
store ptr null, ptr %qq, !dbg !29
34+
ret void, !dbg !30
35+
}
36+
37+
; Function Attrs: nounwind readnone
38+
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
39+
40+
attributes #0 = { nounwind uwtable }
41+
42+
!llvm.module.flags = !{!0, !1}
43+
!llvm.dbg.cu = !{!2}
44+
45+
!0 = !{i32 1, !"PIE Level", i32 2}
46+
!1 = !{i32 2, !"Debug Info Version", i32 3}
47+
!2 = distinct !DICompileUnit(language: DW_LANG_Ada95, file: !3, producer: "gnat-llvm", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4)
48+
!3 = !DIFile(filename: "e3.rs", directory: "/home/tromey/Ada")
49+
!4 = !{}
50+
!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)
51+
!6 = !DINamespace(name: "e3", scope: null)
52+
!7 = !DIFile(filename: "<unknown>", directory: "")
53+
!8 = !DISubroutineType(types: !9)
54+
!9 = !{null}
55+
!10 = !DILocalVariable(name: "qq", scope: !11, file: !3, line: 3, type: !12, align: 8)
56+
!11 = distinct !DILexicalBlock(scope: !5, file: !3, line: 3, column: 4)
57+
!12 = !DICompositeType(tag: DW_TAG_structure_type, name: "F", scope: !6, file: !7, size: 128, align: 64, elements: !13, identifier: "7ce1efff6b82281ab9ceb730566e7e20")
58+
!13 = !{!14, !15}
59+
!14 = !DIDerivedType(tag: DW_TAG_member, name: "Discr", scope: !12, file: !7, baseType: !27, size: 64, align: 64)
60+
!15 = !DICompositeType(tag: DW_TAG_variant_part, scope: !12, file: !7, size: 128, align: 64, elements: !16, identifier: "7ce1efff6b82281ab9ceb730566e7e20", discriminator: !14)
61+
!16 = !{!17, !24}
62+
!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])
63+
!18 = !DICompositeType(tag: DW_TAG_structure_type, name: "Yep", scope: !12, file: !7, size: 128, align: 64, elements: !19, identifier: "7ce1efff6b82281ab9ceb730566e7e20::Yep")
64+
!19 = !{!20, !22}
65+
!20 = !DIDerivedType(tag: DW_TAG_member, name: "var1", scope: !18, file: !7, baseType: !21, size: 8, align: 8, offset: 64)
66+
!21 = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned)
67+
!22 = !DIDerivedType(tag: DW_TAG_member, name: "var2", scope: !18, file: !7, baseType: !23, size: 64, align: 64)
68+
!23 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "&u8", baseType: !21, size: 64, align: 64)
69+
!24 = !DIDerivedType(tag: DW_TAG_member, scope: !15, file: !7, baseType: !25, size: 128, align: 64, extraData: i32 75)
70+
!25 = !DICompositeType(tag: DW_TAG_structure_type, name: "Nope", scope: !12, file: !7, size: 128, align: 64, elements: !4, identifier: "7ce1efff6b82281ab9ceb730566e7e20::Nope")
71+
!27 = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned)
72+
!28 = !DIExpression()
73+
!29 = !DILocation(line: 3, scope: !11)
74+
!30 = !DILocation(line: 4, scope: !5)

0 commit comments

Comments
 (0)