Skip to content

Commit 9ed0248

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 db9afd4 commit 9ed0248

File tree

2 files changed

+192
-72
lines changed

2 files changed

+192
-72
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp

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

1005-
uint64_t Size = CTy->getSizeInBits() >> 3;
10061005
uint16_t Tag = Buffer.getTag();
10071006

10081007
switch (Tag) {
@@ -1165,15 +1164,29 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) {
11651164
if (Tag == dwarf::DW_TAG_enumeration_type ||
11661165
Tag == dwarf::DW_TAG_class_type || Tag == dwarf::DW_TAG_structure_type ||
11671166
Tag == dwarf::DW_TAG_union_type) {
1168-
// Add size if non-zero (derived types might be zero-sized.)
1169-
// Ignore the size if it's a non-enum forward decl.
1170-
// TODO: Do we care about size for enum forward declarations?
1171-
if (Size &&
1172-
(!CTy->isForwardDecl() || Tag == dwarf::DW_TAG_enumeration_type))
1173-
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, Size);
1174-
else if (!CTy->isForwardDecl())
1175-
// Add zero size if it is not a forward declaration.
1176-
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, 0);
1167+
if (auto *Var = dyn_cast_or_null<DIVariable>(CTy->getRawSizeInBits())) {
1168+
if (auto *VarDIE = getDIE(Var)) {
1169+
addDIEEntry(Buffer, dwarf::DW_AT_bit_size, *VarDIE);
1170+
}
1171+
} else if (auto *Exp =
1172+
dyn_cast_or_null<DIExpression>(CTy->getRawSizeInBits())) {
1173+
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
1174+
DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc);
1175+
DwarfExpr.setMemoryLocationKind();
1176+
DwarfExpr.addExpression(Exp);
1177+
addBlock(Buffer, dwarf::DW_AT_bit_size, DwarfExpr.finalize());
1178+
} else {
1179+
uint64_t Size = CTy->getSizeInBits() >> 3;
1180+
// Add size if non-zero (derived types might be zero-sized.)
1181+
// Ignore the size if it's a non-enum forward decl.
1182+
// TODO: Do we care about size for enum forward declarations?
1183+
if (Size &&
1184+
(!CTy->isForwardDecl() || Tag == dwarf::DW_TAG_enumeration_type))
1185+
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, Size);
1186+
else if (!CTy->isForwardDecl())
1187+
// Add zero size if it is not a forward declaration.
1188+
addUInt(Buffer, dwarf::DW_AT_byte_size, std::nullopt, 0);
1189+
}
11771190

11781191
// If we're a forward decl, say so.
11791192
if (CTy->isForwardDecl())
@@ -1840,74 +1853,119 @@ DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) {
18401853

18411854
addBlock(MemberDie, dwarf::DW_AT_data_member_location, VBaseLocationDie);
18421855
} else {
1843-
uint64_t Size = DT->getSizeInBits();
1844-
uint64_t FieldSize = DD->getBaseTypeSize(DT);
1845-
uint32_t AlignInBytes = DT->getAlignInBytes();
1846-
uint64_t OffsetInBytes;
1856+
uint64_t Size = 0;
1857+
uint64_t FieldSize = 0;
18471858

18481859
bool IsBitfield = DT->isBitField();
1849-
if (IsBitfield) {
1850-
// Handle bitfield, assume bytes are 8 bits.
1851-
if (DD->useDWARF2Bitfields())
1852-
addUInt(MemberDie, dwarf::DW_AT_byte_size, std::nullopt, FieldSize / 8);
1853-
addUInt(MemberDie, dwarf::DW_AT_bit_size, std::nullopt, Size);
1854-
1855-
assert(DT->getOffsetInBits() <=
1856-
(uint64_t)std::numeric_limits<int64_t>::max());
1857-
int64_t Offset = DT->getOffsetInBits();
1858-
// We can't use DT->getAlignInBits() here: AlignInBits for member type
1859-
// is non-zero if and only if alignment was forced (e.g. _Alignas()),
1860-
// which can't be done with bitfields. Thus we use FieldSize here.
1861-
uint32_t AlignInBits = FieldSize;
1862-
uint32_t AlignMask = ~(AlignInBits - 1);
1863-
// The bits from the start of the storage unit to the start of the field.
1864-
uint64_t StartBitOffset = Offset - (Offset & AlignMask);
1865-
// The byte offset of the field's aligned storage unit inside the struct.
1866-
OffsetInBytes = (Offset - StartBitOffset) / 8;
1867-
1868-
if (DD->useDWARF2Bitfields()) {
1869-
uint64_t HiMark = (Offset + FieldSize) & AlignMask;
1870-
uint64_t FieldOffset = (HiMark - FieldSize);
1871-
Offset -= FieldOffset;
1872-
1873-
// Maybe we need to work from the other end.
1874-
if (Asm->getDataLayout().isLittleEndian())
1875-
Offset = FieldSize - (Offset + Size);
1876-
1877-
if (Offset < 0)
1878-
addSInt(MemberDie, dwarf::DW_AT_bit_offset, dwarf::DW_FORM_sdata,
1879-
Offset);
1880-
else
1881-
addUInt(MemberDie, dwarf::DW_AT_bit_offset, std::nullopt,
1882-
(uint64_t)Offset);
1883-
OffsetInBytes = FieldOffset >> 3;
1884-
} else {
1885-
addUInt(MemberDie, dwarf::DW_AT_data_bit_offset, std::nullopt, Offset);
1860+
1861+
// Handle the size.
1862+
if (auto *Var = dyn_cast_or_null<DIVariable>(DT->getRawSizeInBits())) {
1863+
if (auto *VarDIE = getDIE(Var)) {
1864+
addDIEEntry(MemberDie, dwarf::DW_AT_bit_size, *VarDIE);
18861865
}
1866+
} else if (auto *Exp =
1867+
dyn_cast_or_null<DIExpression>(DT->getRawSizeInBits())) {
1868+
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
1869+
DIEDwarfExpression DwarfExpr(*Asm, getCU(), *Loc);
1870+
DwarfExpr.setMemoryLocationKind();
1871+
DwarfExpr.addExpression(Exp);
1872+
addBlock(MemberDie, dwarf::DW_AT_bit_size, DwarfExpr.finalize());
18871873
} else {
1888-
// This is not a bitfield.
1889-
OffsetInBytes = DT->getOffsetInBits() / 8;
1890-
if (AlignInBytes)
1891-
addUInt(MemberDie, dwarf::DW_AT_alignment, dwarf::DW_FORM_udata,
1892-
AlignInBytes);
1874+
Size = DT->getSizeInBits();
1875+
FieldSize = DD->getBaseTypeSize(DT);
1876+
if (IsBitfield) {
1877+
// Handle bitfield, assume bytes are 8 bits.
1878+
if (DD->useDWARF2Bitfields())
1879+
addUInt(MemberDie, dwarf::DW_AT_byte_size, std::nullopt,
1880+
FieldSize / 8);
1881+
addUInt(MemberDie, dwarf::DW_AT_bit_size, std::nullopt, Size);
1882+
}
18931883
}
18941884

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

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)