Skip to content

Commit e86ebb7

Browse files
committed
TypeGraph: Support bitfields
- Change member and parent offsets to work in bits, not bytes - Printer still displays offsets in bytes, with decimals when using bitfields - AddPadding: Don't pad bitfields - CodeGen: Emit code for bitfields
1 parent c9bcf5e commit e86ebb7

14 files changed

+398
-90
lines changed

oi/CodeGen.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,11 @@ void genDefsClass(const Class& c, std::string& code) {
247247

248248
code += c.name() + " {\n";
249249
for (const auto& mem : c.members) {
250-
code += " " + mem.type->name() + " " + mem.name + ";\n";
250+
code += " " + mem.type->name() + " " + mem.name;
251+
if (mem.bitsize) {
252+
code += " : " + std::to_string(mem.bitsize);
253+
}
254+
code += ";\n";
251255
}
252256
code += "};\n\n";
253257
}
@@ -271,8 +275,10 @@ void genStaticAssertsClass(const Class& c, std::string& code) {
271275
") == " + std::to_string(c.size()) +
272276
", \"Unexpected size of struct " + c.name() + "\");\n";
273277
for (const auto& member : c.members) {
278+
if (member.bitsize > 0)
279+
continue;
274280
code += "static_assert(offsetof(" + c.name() + ", " + member.name +
275-
") == " + std::to_string(member.offset) +
281+
") == " + std::to_string(member.bitOffset / 8) +
276282
", \"Unexpected offset of " + c.name() + "::" + member.name +
277283
"\");\n";
278284
}
@@ -407,7 +413,8 @@ void CodeGen::getClassSizeFuncConcrete(std::string_view funcName,
407413
}
408414

409415
code += " JLOG(\"" + member.name + " @\");\n";
410-
code += " JLOGPTR(&t." + member.name + ");\n";
416+
if (member.bitsize == 0)
417+
code += " JLOGPTR(&t." + member.name + ");\n";
411418
code += " getSizeType(t." + member.name + ", returnArg);\n";
412419
}
413420
code += "}\n";

oi/type_graph/AddPadding.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ void AddPadding::visit(Class& c) {
6565
paddedMembers.reserve(c.members.size());
6666
for (size_t i = 0; i < c.members.size(); i++) {
6767
if (i >= 1) {
68-
addPadding(c.members[i - 1], c.members[i].offset, paddedMembers);
68+
addPadding(c.members[i - 1], c.members[i].bitOffset, paddedMembers);
6969
}
7070
paddedMembers.push_back(c.members[i]);
7171
}
7272

7373
if (!c.members.empty()) {
74-
addPadding(c.members.back(), c.size(), paddedMembers);
74+
addPadding(c.members.back(), c.size() * 8, paddedMembers);
7575
}
7676

7777
c.members = std::move(paddedMembers);
@@ -82,16 +82,32 @@ void AddPadding::visit(Class& c) {
8282
}
8383

8484
void AddPadding::addPadding(const Member& prevMember,
85-
uint64_t paddingEnd,
85+
uint64_t paddingEndBits,
8686
std::vector<Member>& paddedMembers) {
87-
uint64_t prevMemberEnd = prevMember.offset + prevMember.type->size();
88-
uint64_t padding = paddingEnd - prevMemberEnd;
89-
if (padding == 0)
87+
uint64_t prevMemberSizeBits;
88+
if (prevMember.bitsize == 0) {
89+
prevMemberSizeBits = prevMember.type->size() * 8;
90+
} else {
91+
prevMemberSizeBits = prevMember.bitsize;
92+
}
93+
94+
uint64_t prevMemberEndBits = prevMember.bitOffset + prevMemberSizeBits;
95+
uint64_t paddingBits = paddingEndBits - prevMemberEndBits;
96+
if (paddingBits == 0)
9097
return;
9198

92-
auto* primitive = typeGraph_.make_type<Primitive>(Primitive::Kind::Int8);
93-
auto* paddingArray = typeGraph_.make_type<Array>(primitive, padding);
94-
paddedMembers.emplace_back(paddingArray, MemberPrefix, prevMemberEnd);
99+
if (paddingBits % 8 == 0) {
100+
// Pad with an array of bytes
101+
auto* primitive = typeGraph_.make_type<Primitive>(Primitive::Kind::Int8);
102+
auto* paddingArray =
103+
typeGraph_.make_type<Array>(primitive, paddingBits / 8);
104+
paddedMembers.emplace_back(paddingArray, MemberPrefix, prevMemberEndBits);
105+
} else {
106+
// Pad with a bitfield
107+
auto* primitive = typeGraph_.make_type<Primitive>(Primitive::Kind::Int64);
108+
paddedMembers.emplace_back(primitive, MemberPrefix, prevMemberEndBits,
109+
paddingBits);
110+
}
95111
}
96112

97113
} // namespace type_graph

oi/type_graph/AddPadding.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class AddPadding final : public RecursiveVisitor {
4545
void visit(Type& type) override;
4646
void visit(Class& c) override;
4747

48-
static const inline std::string MemberPrefix = "__oid_padding";
48+
static const inline std::string MemberPrefix = "__oi_padding";
4949

5050
private:
5151
std::unordered_set<Type*> visited_;

oi/type_graph/DrgnParser.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,14 @@ void DrgnParser::enumerateClassParents(struct drgn_type* type,
212212
}
213213

214214
auto ptype = enumerateType(parent_qual_type.type);
215-
uint64_t poffset = drgn_parents[i].bit_offset / 8;
215+
uint64_t poffset = drgn_parents[i].bit_offset;
216216
Parent p(ptype, poffset);
217217
parents.push_back(p);
218218
}
219219

220-
std::sort(parents.begin(), parents.end(),
221-
[](const auto& a, const auto& b) { return a.offset < b.offset; });
220+
std::sort(parents.begin(), parents.end(), [](const auto& a, const auto& b) {
221+
return a.bitOffset < b.bitOffset;
222+
});
222223
}
223224

224225
void DrgnParser::enumerateClassMembers(struct drgn_type* type,
@@ -257,17 +258,16 @@ void DrgnParser::enumerateClassMembers(struct drgn_type* type,
257258
if (drgn_members[i].name)
258259
member_name = drgn_members[i].name;
259260

260-
// TODO bitfields
261-
262261
auto mtype = enumerateType(member_type);
263-
uint64_t moffset = drgn_members[i].bit_offset / 8;
262+
uint64_t moffset = drgn_members[i].bit_offset;
264263

265-
Member m(mtype, member_name, moffset); // TODO
264+
Member m{mtype, member_name, moffset, bit_field_size};
266265
members.push_back(m);
267266
}
268267

269-
std::sort(members.begin(), members.end(),
270-
[](const auto& a, const auto& b) { return a.offset < b.offset; });
268+
std::sort(members.begin(), members.end(), [](const auto& a, const auto& b) {
269+
return a.bitOffset < b.bitOffset;
270+
});
271271
}
272272

273273
void DrgnParser::enumerateTemplateParam(drgn_type_template_parameter* tparams,

oi/type_graph/Flattener.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void flattenParent(const Parent& parent,
6262
for (size_t i = 0; i < parentClass->members.size(); i++) {
6363
const auto& member = parentClass->members[i];
6464
flattenedMembers.push_back(member);
65-
flattenedMembers.back().offset += parent.offset;
65+
flattenedMembers.back().bitOffset += parent.bitOffset;
6666
if (i == 0) {
6767
flattenedMembers.back().align =
6868
std::max(flattenedMembers.back().align, parentClass->align());
@@ -71,7 +71,8 @@ void flattenParent(const Parent& parent,
7171
} else if (Container* parentContainer =
7272
dynamic_cast<Container*>(&parentType)) {
7373
// Create a new member to represent this parent container
74-
flattenedMembers.emplace_back(parentContainer, "__parent", parent.offset);
74+
flattenedMembers.emplace_back(parentContainer, "__parent",
75+
parent.bitOffset);
7576
} else {
7677
throw std::runtime_error("Invalid type for parent");
7778
}
@@ -169,8 +170,8 @@ void Flattener::visit(Class& c) {
169170
std::size_t member_idx = 0;
170171
std::size_t parent_idx = 0;
171172
while (member_idx < c.members.size() && parent_idx < c.parents.size()) {
172-
auto member_offset = c.members[member_idx].offset;
173-
auto parent_offset = c.parents[parent_idx].offset;
173+
auto member_offset = c.members[member_idx].bitOffset;
174+
auto parent_offset = c.parents[parent_idx].bitOffset;
174175
if (member_offset < parent_offset) {
175176
// Add our own member
176177
const auto& member = c.members[member_idx++];

oi/type_graph/Printer.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,16 +166,22 @@ void Printer::print_param(const TemplateParam& param) {
166166
void Printer::print_parent(const Parent& parent) {
167167
depth_++;
168168
prefix();
169-
out_ << "Parent (offset: " << parent.offset << ")" << std::endl;
169+
out_ << "Parent (offset: " << static_cast<double>(parent.bitOffset) / 8 << ")"
170+
<< std::endl;
170171
print(*parent.type);
171172
depth_--;
172173
}
173174

174175
void Printer::print_member(const Member& member) {
175176
depth_++;
176177
prefix();
177-
out_ << "Member: " << member.name << " (offset: " << member.offset
178-
<< align_str(member.align) << ")" << std::endl;
178+
out_ << "Member: " << member.name
179+
<< " (offset: " << static_cast<double>(member.bitOffset) / 8;
180+
out_ << align_str(member.align);
181+
if (member.bitsize != 0) {
182+
out_ << ", bitsize: " << member.bitsize;
183+
}
184+
out_ << ")" << std::endl;
179185
print(*member.type);
180186
depth_--;
181187
}

oi/type_graph/RemoveIgnored.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ void RemoveIgnored::visit(Class& c) {
4747
auto* primitive = typeGraph_.make_type<Primitive>(Primitive::Kind::Int8);
4848
auto* paddingArray =
4949
typeGraph_.make_type<Array>(primitive, c.members[i].type->size());
50-
c.members[i] = Member{paddingArray, c.members[i].name, c.members[i].offset};
50+
c.members[i] =
51+
Member{paddingArray, c.members[i].name, c.members[i].bitOffset};
5152
}
5253
}
5354

oi/type_graph/Types.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,15 @@ class Type {
6969
struct Member {
7070
Member(Type* type,
7171
const std::string& name,
72-
uint64_t offset,
73-
uint64_t align = 0)
74-
: type(type), name(name), offset(offset), align(align) {
72+
uint64_t bitOffset,
73+
uint64_t bitsize = 0)
74+
: type(type), name(name), bitOffset(bitOffset), bitsize(bitsize) {
7575
}
7676

7777
Type* type;
78-
std::string name; // TODO make optional?
79-
uint64_t offset;
78+
std::string name;
79+
uint64_t bitOffset;
80+
uint64_t bitsize;
8081
uint64_t align = 0;
8182
};
8283

@@ -91,11 +92,11 @@ struct Function {
9192

9293
class Class;
9394
struct Parent {
94-
Parent(Type* type, uint64_t offset) : type(type), offset(offset) {
95+
Parent(Type* type, uint64_t bitOffset) : type(type), bitOffset(bitOffset) {
9596
}
9697

9798
Type* type;
98-
uint64_t offset;
99+
uint64_t bitOffset;
99100
};
100101

101102
struct TemplateParam {

test/integration/bitfields.toml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
definitions = '''
2+
struct Single {
3+
int bitfield : 3;
4+
};
5+
6+
struct WithinBytes {
7+
char a : 3;
8+
char b : 5;
9+
char c : 7;
10+
};
11+
12+
struct StraddleBytes {
13+
char a : 7;
14+
char b : 7;
15+
char c : 2;
16+
};
17+
18+
struct Mixed {
19+
int a;
20+
char b : 4;
21+
short c : 12;
22+
char d;
23+
int e : 22;
24+
};
25+
26+
#pragma clang diagnostic push
27+
#pragma clang diagnostic ignored "-Wbitfield-width"
28+
// The bitfield will max out at the size of its type. Extra bits act as padding.
29+
struct MoreBitsThanType {
30+
char a : 29;
31+
};
32+
#pragma clang diagnostic pop
33+
34+
// A zero-sized bitfield adds default padding between neighbouring bitfields
35+
struct ZeroBits {
36+
char b1 : 3;
37+
char : 0;
38+
char b2 : 2;
39+
};
40+
41+
enum class MyEnum {
42+
One,
43+
Two,
44+
Three,
45+
};
46+
47+
struct Enum {
48+
MyEnum e : 2;
49+
MyEnum f : 4;
50+
};
51+
'''
52+
# TODO The sizes do not take bitfields into account. They count each field as
53+
# if they were regular primitives.
54+
[cases]
55+
[cases.single]
56+
cli_options = ["-ftype-graph"]
57+
oil_skip = "not implemented"
58+
param_types = ["Single&"]
59+
setup = "return {};"
60+
expect_json = '''[
61+
{"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "members":[
62+
{"staticSize":4, "dynamicSize":0, "exclusiveSize":4}
63+
]}]'''
64+
[cases.within_bytes]
65+
cli_options = ["-ftype-graph"]
66+
oil_skip = "not implemented"
67+
param_types = ["WithinBytes&"]
68+
setup = "return {};"
69+
expect_json = '''[
70+
{"staticSize":2, "dynamicSize":0, "exclusiveSize":0, "members":[
71+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
72+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
73+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1}
74+
]}]'''
75+
[cases.straddle_bytes]
76+
cli_options = ["-ftype-graph"]
77+
oil_skip = "not implemented"
78+
param_types = ["StraddleBytes&"]
79+
setup = "return {};"
80+
expect_json = '''[
81+
{"staticSize":3, "dynamicSize":0, "exclusiveSize":0, "members":[
82+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
83+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
84+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1}
85+
]}]'''
86+
[cases.mixed]
87+
cli_options = ["-ftype-graph"]
88+
oil_skip = "not implemented"
89+
param_types = ["Mixed&"]
90+
setup = "return {};"
91+
expect_json = '''[
92+
{"staticSize":12, "dynamicSize":0, "exclusiveSize":0, "members":[
93+
{"staticSize":4, "dynamicSize":0, "exclusiveSize":4},
94+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
95+
{"staticSize":2, "dynamicSize":0, "exclusiveSize":2},
96+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
97+
{"staticSize":4, "dynamicSize":0, "exclusiveSize":4}
98+
]}]'''
99+
[cases.more_bits_than_type] # TODO member sizes are wrong
100+
skip = "drgn errors out"
101+
cli_options = ["-ftype-graph"]
102+
oil_skip = "not implemented"
103+
param_types = ["MoreBitsThanType&"]
104+
setup = "return {};"
105+
expect_json = '"TODO"'
106+
[cases.zero_bits]
107+
cli_options = ["-ftype-graph"]
108+
oil_skip = "not implemented"
109+
param_types = ["ZeroBits&"]
110+
setup = "return {};"
111+
expect_json = '''[
112+
{"staticSize":2, "dynamicSize":0, "exclusiveSize":0, "members":[
113+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1},
114+
{"staticSize":1, "dynamicSize":0, "exclusiveSize":1}
115+
]}]'''
116+
[cases.enum]
117+
cli_options = ["-ftype-graph"]
118+
oil_skip = "not implemented"
119+
param_types = ["Enum&"]
120+
setup = "return {};"
121+
expect_json = '''[
122+
{"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "members":[
123+
{"staticSize":4, "dynamicSize":0, "exclusiveSize":4},
124+
{"staticSize":4, "dynamicSize":0, "exclusiveSize":4}
125+
]}]'''

0 commit comments

Comments
 (0)