Skip to content

Commit d1949cd

Browse files
committed
Change DWARF writer to handle dynamic offsets and sizes
This updates the DWARF writer to emit dynamic offsets and sizes for members.
1 parent 06bdfeb commit d1949cd

File tree

2 files changed

+189
-72
lines changed

2 files changed

+189
-72
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp

Lines changed: 127 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,6 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
10131013
// Add name if not anonymous or intermediate type.
10141014
StringRef Name = CTy->getName();
10151015

1016-
uint64_t Size = CTy->getSizeInBits() >> 3;
10171016
uint16_t Tag = Buffer.getTag();
10181017

10191018
switch (Tag) {
@@ -1176,15 +1175,28 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
11761175
if (Tag == dwarf::DW_TAG_enumeration_type ||
11771176
Tag == dwarf::DW_TAG_class_type || Tag == dwarf::DW_TAG_structure_type ||
11781177
Tag == dwarf::DW_TAG_union_type) {
1179-
// Add size if non-zero (derived types might be zero-sized.)
1180-
// Ignore the size if it's a non-enum forward decl.
1181-
// TODO: Do we care about size for enum forward declarations?
1182-
if (Size &&
1183-
(!CTy->isForwardDecl() || Tag == dwarf::DW_TAG_enumeration_type))
1184-
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, Size);
1185-
else if (!CTy->isForwardDecl())
1186-
// Add zero size if it is not a forward declaration.
1187-
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, 0);
1178+
if (auto *Var = dyn_cast_or_null<DIVariable>(CTy->getRawSizeInBits())) {
1179+
if (auto *VarDIE = getDIE(Var))
1180+
addDIEEntry(Buffer, dwarf::DW_AT_bit_size, *VarDIE);
1181+
} else if (auto *Exp =
1182+
dyn_cast_or_null<DIExpression>(CTy->getRawSizeInBits())) {
1183+
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
1184+
DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc);
1185+
DwarfExpr.setMemoryLocationKind();
1186+
DwarfExpr.addExpression(Exp);
1187+
addBlock(Buffer, dwarf::DW_AT_bit_size, DwarfExpr.finalize());
1188+
} else {
1189+
uint64_t Size = CTy->getSizeInBits() >> 3;
1190+
// Add size if non-zero (derived types might be zero-sized.)
1191+
// Ignore the size if it's a non-enum forward decl.
1192+
// TODO: Do we care about size for enum forward declarations?
1193+
if (Size &&
1194+
(!CTy->isForwardDecl() || Tag == dwarf::DW_TAG_enumeration_type))
1195+
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, Size);
1196+
else if (!CTy->isForwardDecl())
1197+
// Add zero size if it is not a forward declaration.
1198+
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, 0);
1199+
}
11881200

11891201
// If we're a forward decl, say so.
11901202
if (CTy->isForwardDecl())
@@ -1864,74 +1876,117 @@ DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
18641876

18651877
addBlock(MemberDie, dwarf::DW_AT_data_member_location, VBaseLocationDie);
18661878
} else {
1867-
uint64_t Size = DT->getSizeInBits();
1868-
uint64_t FieldSize = DD->getBaseTypeSize(DT);
1869-
uint32_t AlignInBytes = DT->getAlignInBytes();
1870-
uint64_t OffsetInBytes;
1879+
uint64_t Size = 0;
1880+
uint64_t FieldSize = 0;
18711881

18721882
bool IsBitfield = DT->isBitField();
1873-
if (IsBitfield) {
1874-
// Handle bitfield, assume bytes are 8 bits.
1875-
if (DD->useDWARF2Bitfields())
1876-
addUInt(MemberDie, dwarf::DW_AT_byte_size, std::nullopt, FieldSize / 8);
1877-
addUInt(MemberDie, dwarf::DW_AT_bit_size, std::nullopt, Size);
1878-
1879-
assert(DT->getOffsetInBits() <=
1880-
(uint64_t)std::numeric_limits<int64_t>::max());
1881-
int64_t Offset = DT->getOffsetInBits();
1882-
// We can't use DT->getAlignInBits() here: AlignInBits for member type
1883-
// is non-zero if and only if alignment was forced (e.g. _Alignas()),
1884-
// which can't be done with bitfields. Thus we use FieldSize here.
1885-
uint32_t AlignInBits = FieldSize;
1886-
uint32_t AlignMask = ~(AlignInBits - 1);
1887-
// The bits from the start of the storage unit to the start of the field.
1888-
uint64_t StartBitOffset = Offset - (Offset & AlignMask);
1889-
// The byte offset of the field's aligned storage unit inside the struct.
1890-
OffsetInBytes = (Offset - StartBitOffset) / 8;
1891-
1892-
if (DD->useDWARF2Bitfields()) {
1893-
uint64_t HiMark = (Offset + FieldSize) & AlignMask;
1894-
uint64_t FieldOffset = (HiMark - FieldSize);
1895-
Offset -= FieldOffset;
1896-
1897-
// Maybe we need to work from the other end.
1898-
if (Asm->getDataLayout().isLittleEndian())
1899-
Offset = FieldSize - (Offset + Size);
1900-
1901-
if (Offset < 0)
1902-
addSInt(MemberDie, dwarf::DW_AT_bit_offset, dwarf::DW_FORM_sdata,
1883+
1884+
// Handle the size.
1885+
if (auto *Var = dyn_cast_or_null<DIVariable>(DT->getRawSizeInBits())) {
1886+
if (auto *VarDIE = getDIE(Var))
1887+
addDIEEntry(MemberDie, dwarf::DW_AT_bit_size, *VarDIE);
1888+
} else if (auto *Exp =
1889+
dyn_cast_or_null<DIExpression>(DT->getRawSizeInBits())) {
1890+
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
1891+
DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc);
1892+
DwarfExpr.setMemoryLocationKind();
1893+
DwarfExpr.addExpression(Exp);
1894+
addBlock(MemberDie, dwarf::DW_AT_bit_size, DwarfExpr.finalize());
1895+
} else {
1896+
Size = DT->getSizeInBits();
1897+
FieldSize = DD->getBaseTypeSize(DT);
1898+
if (IsBitfield) {
1899+
// Handle bitfield, assume bytes are 8 bits.
1900+
if (DD->useDWARF2Bitfields())
1901+
addUInt(MemberDie, dwarf::DW_AT_byte_size, std::nullopt,
1902+
FieldSize / 8);
1903+
addUInt(MemberDie, dwarf::DW_AT_bit_size, std::nullopt, Size);
1904+
}
1905+
}
1906+
1907+
// Handle the location. DW_AT_data_bit_offset won't allow an
1908+
// expression until DWARF 6, but it can be used as an extension.
1909+
// See https://dwarfstd.org/issues/250501.1.html
1910+
if (auto *Var = dyn_cast_or_null<DIVariable>(DT->getRawOffsetInBits())) {
1911+
if (!Asm->TM.Options.DebugStrictDwarf || DD->getDwarfVersion() >= 6) {
1912+
if (auto *VarDIE = getDIE(Var))
1913+
addDIEEntry(MemberDie, dwarf::DW_AT_data_bit_offset, *VarDIE);
1914+
}
1915+
} else if (auto *Expr =
1916+
dyn_cast_or_null<DIExpression>(DT->getRawOffsetInBits())) {
1917+
if (!Asm->TM.Options.DebugStrictDwarf || DD->getDwarfVersion() >= 6) {
1918+
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
1919+
DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc);
1920+
DwarfExpr.setMemoryLocationKind();
1921+
DwarfExpr.addExpression(Expr);
1922+
addBlock(MemberDie, dwarf::DW_AT_data_bit_offset, DwarfExpr.finalize());
1923+
}
1924+
} else {
1925+
uint32_t AlignInBytes = DT->getAlignInBytes();
1926+
uint64_t OffsetInBytes;
1927+
1928+
if (IsBitfield) {
1929+
assert(DT->getOffsetInBits() <=
1930+
(uint64_t)std::numeric_limits<int64_t>::max());
1931+
int64_t Offset = DT->getOffsetInBits();
1932+
// We can't use DT->getAlignInBits() here: AlignInBits for member type
1933+
// is non-zero if and only if alignment was forced (e.g. _Alignas()),
1934+
// which can't be done with bitfields. Thus we use FieldSize here.
1935+
uint32_t AlignInBits = FieldSize;
1936+
uint32_t AlignMask = ~(AlignInBits - 1);
1937+
// The bits from the start of the storage unit to the start of the
1938+
// field.
1939+
uint64_t StartBitOffset = Offset - (Offset & AlignMask);
1940+
// The byte offset of the field's aligned storage unit inside the
1941+
// struct.
1942+
OffsetInBytes = (Offset - StartBitOffset) / 8;
1943+
1944+
if (DD->useDWARF2Bitfields()) {
1945+
uint64_t HiMark = (Offset + FieldSize) & AlignMask;
1946+
uint64_t FieldOffset = (HiMark - FieldSize);
1947+
Offset -= FieldOffset;
1948+
1949+
// Maybe we need to work from the other end.
1950+
if (Asm->getDataLayout().isLittleEndian())
1951+
Offset = FieldSize - (Offset + Size);
1952+
1953+
if (Offset < 0)
1954+
addSInt(MemberDie, dwarf::DW_AT_bit_offset, dwarf::DW_FORM_sdata,
1955+
Offset);
1956+
else
1957+
addUInt(MemberDie, dwarf::DW_AT_bit_offset, std::nullopt,
1958+
(uint64_t)Offset);
1959+
OffsetInBytes = FieldOffset >> 3;
1960+
} else {
1961+
addUInt(MemberDie, dwarf::DW_AT_data_bit_offset, std::nullopt,
19031962
Offset);
1904-
else
1905-
addUInt(MemberDie, dwarf::DW_AT_bit_offset, std::nullopt,
1906-
(uint64_t)Offset);
1907-
OffsetInBytes = FieldOffset >> 3;
1963+
}
19081964
} else {
1909-
addUInt(MemberDie, dwarf::DW_AT_data_bit_offset, std::nullopt, Offset);
1965+
// This is not a bitfield.
1966+
OffsetInBytes = DT->getOffsetInBits() / 8;
1967+
if (AlignInBytes)
1968+
addUInt(MemberDie, dwarf::DW_AT_alignment, dwarf::DW_FORM_udata,
1969+
AlignInBytes);
19101970
}
1911-
} else {
1912-
// This is not a bitfield.
1913-
OffsetInBytes = DT->getOffsetInBits() / 8;
1914-
if (AlignInBytes)
1915-
addUInt(MemberDie, dwarf::DW_AT_alignment, dwarf::DW_FORM_udata,
1916-
AlignInBytes);
1917-
}
19181971

1919-
if (DD->getDwarfVersion() <= 2) {
1920-
DIELoc *MemLocationDie = new (DIEValueAllocator) DIELoc;
1921-
addUInt(*MemLocationDie, dwarf::DW_FORM_data1, dwarf::DW_OP_plus_uconst);
1922-
addUInt(*MemLocationDie, dwarf::DW_FORM_udata, OffsetInBytes);
1923-
addBlock(MemberDie, dwarf::DW_AT_data_member_location, MemLocationDie);
1924-
} else if (!IsBitfield || DD->useDWARF2Bitfields()) {
1925-
// In DWARF v3, DW_FORM_data4/8 in DW_AT_data_member_location are
1926-
// interpreted as location-list pointers. Interpreting constants as
1927-
// pointers is not expected, so we use DW_FORM_udata to encode the
1928-
// constants here.
1929-
if (DD->getDwarfVersion() == 3)
1930-
addUInt(MemberDie, dwarf::DW_AT_data_member_location,
1931-
dwarf::DW_FORM_udata, OffsetInBytes);
1932-
else
1933-
addUInt(MemberDie, dwarf::DW_AT_data_member_location, std::nullopt,
1934-
OffsetInBytes);
1972+
if (DD->getDwarfVersion() <= 2) {
1973+
DIELoc *MemLocationDie = new (DIEValueAllocator) DIELoc;
1974+
addUInt(*MemLocationDie, dwarf::DW_FORM_data1,
1975+
dwarf::DW_OP_plus_uconst);
1976+
addUInt(*MemLocationDie, dwarf::DW_FORM_udata, OffsetInBytes);
1977+
addBlock(MemberDie, dwarf::DW_AT_data_member_location, MemLocationDie);
1978+
} else if (!IsBitfield || DD->useDWARF2Bitfields()) {
1979+
// In DWARF v3, DW_FORM_data4/8 in DW_AT_data_member_location are
1980+
// interpreted as location-list pointers. Interpreting constants as
1981+
// pointers is not expected, so we use DW_FORM_udata to encode the
1982+
// constants here.
1983+
if (DD->getDwarfVersion() == 3)
1984+
addUInt(MemberDie, dwarf::DW_AT_data_member_location,
1985+
dwarf::DW_FORM_udata, OffsetInBytes);
1986+
else
1987+
addUInt(MemberDie, dwarf::DW_AT_data_member_location, std::nullopt,
1988+
OffsetInBytes);
1989+
}
19351990
}
19361991
}
19371992

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
; RUN: llc -O0 -filetype=obj -o - %s | llvm-dwarfdump -v -debug-info - | FileCheck %s
2+
3+
; A basic test of using a DIExpression for DW_AT_data_bit_offset and
4+
; DW_AT_bit_size.
5+
6+
source_filename = "bitfield.c"
7+
8+
%struct.PackedBits = type <{ i8, i32 }>
9+
10+
@s = common global %struct.PackedBits zeroinitializer, align 1, !dbg !2
11+
@value = common global i32 zeroinitializer, align 4, !dbg !0
12+
13+
!llvm.dbg.cu = !{!4}
14+
!llvm.module.flags = !{!17, !18, !19}
15+
!llvm.ident = !{!20}
16+
17+
!0 = distinct !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
18+
!1 = !DIGlobalVariable(name: "value", scope: !4, file: !5, line: 8, type: !15, isLocal: false, isDefinition: true)
19+
!2 = distinct !DIGlobalVariableExpression(var: !3, expr: !DIExpression())
20+
!3 = !DIGlobalVariable(name: "s", scope: !4, file: !5, line: 8, type: !8, isLocal: false, isDefinition: true)
21+
22+
23+
!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !5, producer: "clang version 3.9.0 (trunk 267633)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !6, globals: !7)
24+
!5 = !DIFile(filename: "bitfield.c", directory: "/Volumes/Data/llvm")
25+
!6 = !{}
26+
!7 = !{!0, !2}
27+
!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "PackedBits", file: !5, line: 3, size: 40, elements: !9)
28+
!9 = !{!10, !12, !16}
29+
!10 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !8, file: !5, line: 5, baseType: !11, size: 8)
30+
; CHECK: DW_TAG_member
31+
; CHECK-NEXT: DW_AT_name{{.*}}"a"
32+
; CHECK-NOT: DW_TAG
33+
; CHECK-NOT: DW_AT_bit_offset
34+
; CHECK-NOT: DW_AT_data_bit_offset
35+
; CHECK: DW_AT_data_member_location [DW_FORM_data1] (0x00)
36+
!11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
37+
!12 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !8, file: !5, line: 6, baseType: !13, size: !3, offset: !3, flags: DIFlagBitField)
38+
!13 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint32_t", file: !14, line: 183, baseType: !15)
39+
!14 = !DIFile(filename: "/Volumes/Data/llvm/_build.ninja.release/bin/../lib/clang/3.9.0/include/stdint.h", directory: "/Volumes/Data/llvm")
40+
!15 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
41+
; CHECK: DW_TAG_member
42+
; CHECK-NEXT: DW_AT_name{{.*}}"b"
43+
; CHECK-NOT: DW_TAG
44+
; CHECK-NOT: DW_AT_bit_offset
45+
; CHECK-NOT: DW_AT_byte_size
46+
; CHECK: DW_AT_bit_size [DW_FORM_ref4] ({{.*}})
47+
; CHECK-NEXT: DW_AT_data_bit_offset [DW_FORM_ref4] ({{.*}})
48+
; CHECK-NOT: DW_AT_data_member_location
49+
!16 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !8, file: !5, line: 7, baseType: !13, size: !DIExpression(DW_OP_constu, 27), offset: !DIExpression(DW_OP_constu, 13), flags: DIFlagBitField)
50+
!17 = !{i32 2, !"Dwarf Version", i32 4}
51+
!18 = !{i32 2, !"Debug Info Version", i32 3}
52+
!19 = !{i32 1, !"PIC Level", i32 2}
53+
; CHECK: DW_TAG_member
54+
; CHECK-NEXT: DW_AT_name{{.*}}"c"
55+
; CHECK-NOT: DW_TAG
56+
; CHECK-NOT: DW_AT_bit_offset
57+
; CHECK-NOT: DW_AT_byte_size
58+
; CHECK: DW_AT_bit_size [DW_FORM_exprloc] (DW_OP_lit27)
59+
; CHECK-NEXT: DW_AT_data_bit_offset [DW_FORM_exprloc] (DW_OP_lit13)
60+
; CHECK-NOT: DW_AT_data_member_location
61+
; CHECK: DW_TAG
62+
!20 = !{!"clang version 3.9.0 (trunk 267633)"}

0 commit comments

Comments
 (0)