Skip to content

Commit 14a8d29

Browse files
committed
comp: Do a better effort of computing packedness before bitfield units.
Fixes #2067
1 parent b60339e commit 14a8d29

File tree

4 files changed

+283
-34
lines changed

4 files changed

+283
-34
lines changed

src/ir/comp.rs

+59-29
Original file line numberDiff line numberDiff line change
@@ -1113,21 +1113,17 @@ impl CompInfo {
11131113
}
11141114

11151115
// empty union case
1116-
if self.fields().is_empty() {
1116+
if !self.has_fields() {
11171117
return None;
11181118
}
11191119

11201120
let mut max_size = 0;
11211121
// Don't allow align(0)
11221122
let mut max_align = 1;
1123-
for field in self.fields() {
1124-
let field_layout = field.layout(ctx);
1125-
1126-
if let Some(layout) = field_layout {
1127-
max_size = cmp::max(max_size, layout.size);
1128-
max_align = cmp::max(max_align, layout.align);
1129-
}
1130-
}
1123+
self.each_known_field_layout(ctx, |layout| {
1124+
max_size = cmp::max(max_size, layout.size);
1125+
max_align = cmp::max(max_align, layout.align);
1126+
});
11311127

11321128
Some(Layout::new(max_size, max_align))
11331129
}
@@ -1139,12 +1135,49 @@ impl CompInfo {
11391135
CompFields::AfterComputingBitfieldUnits { ref fields, .. } => {
11401136
fields
11411137
}
1142-
CompFields::BeforeComputingBitfieldUnits(_) => {
1138+
CompFields::BeforeComputingBitfieldUnits(..) => {
11431139
panic!("Should always have computed bitfield units first");
11441140
}
11451141
}
11461142
}
11471143

1144+
fn has_fields(&self) -> bool {
1145+
match self.fields {
1146+
CompFields::ErrorComputingBitfieldUnits => false,
1147+
CompFields::AfterComputingBitfieldUnits { ref fields, .. } => {
1148+
!fields.is_empty()
1149+
}
1150+
CompFields::BeforeComputingBitfieldUnits(ref raw_fields) => {
1151+
!raw_fields.is_empty()
1152+
}
1153+
}
1154+
}
1155+
1156+
fn each_known_field_layout(
1157+
&self,
1158+
ctx: &BindgenContext,
1159+
mut callback: impl FnMut(Layout),
1160+
) {
1161+
match self.fields {
1162+
CompFields::ErrorComputingBitfieldUnits => return,
1163+
CompFields::AfterComputingBitfieldUnits { ref fields, .. } => {
1164+
for field in fields.iter() {
1165+
if let Some(layout) = field.layout(ctx) {
1166+
callback(layout);
1167+
}
1168+
}
1169+
}
1170+
CompFields::BeforeComputingBitfieldUnits(ref raw_fields) => {
1171+
for field in raw_fields.iter() {
1172+
let field_ty = ctx.resolve_type(field.0.ty);
1173+
if let Some(layout) = field_ty.layout(ctx) {
1174+
callback(layout);
1175+
}
1176+
}
1177+
}
1178+
}
1179+
}
1180+
11481181
fn has_bitfields(&self) -> bool {
11491182
match self.fields {
11501183
CompFields::ErrorComputingBitfieldUnits => false,
@@ -1598,23 +1631,17 @@ impl CompInfo {
15981631
// Even though `libclang` doesn't expose `#pragma packed(...)`, we can
15991632
// detect it through its effects.
16001633
if let Some(parent_layout) = layout {
1601-
if self.fields().iter().any(|f| match *f {
1602-
Field::Bitfields(ref unit) => {
1603-
unit.layout().align > parent_layout.align
1604-
}
1605-
Field::DataMember(ref data) => {
1606-
let field_ty = ctx.resolve_type(data.ty());
1607-
field_ty.layout(ctx).map_or(false, |field_ty_layout| {
1608-
field_ty_layout.align > parent_layout.align
1609-
})
1610-
}
1611-
}) {
1634+
let mut packed = false;
1635+
self.each_known_field_layout(ctx, |layout| {
1636+
packed = packed || layout.align > parent_layout.align;
1637+
});
1638+
if packed {
16121639
info!("Found a struct that was defined within `#pragma packed(...)`");
16131640
return true;
1614-
} else if self.has_own_virtual_method {
1615-
if parent_layout.align == 1 {
1616-
return true;
1617-
}
1641+
}
1642+
1643+
if self.has_own_virtual_method && parent_layout.align == 1 {
1644+
return true;
16181645
}
16191646
}
16201647

@@ -1627,10 +1654,13 @@ impl CompInfo {
16271654
}
16281655

16291656
/// Compute this compound structure's bitfield allocation units.
1630-
pub fn compute_bitfield_units(&mut self, ctx: &BindgenContext) {
1631-
// TODO(emilio): If we could detect #pragma packed here we'd fix layout
1632-
// tests in divide-by-zero-in-struct-layout.rs
1633-
self.fields.compute_bitfield_units(ctx, self.packed_attr)
1657+
pub fn compute_bitfield_units(
1658+
&mut self,
1659+
ctx: &BindgenContext,
1660+
layout: Option<&Layout>,
1661+
) {
1662+
let packed = self.is_packed(ctx, layout);
1663+
self.fields.compute_bitfield_units(ctx, packed)
16341664
}
16351665

16361666
/// Assign for each anonymous field a generated name.

src/ir/context.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -981,12 +981,11 @@ If you encounter an error missing from this list, please file an issue or a PR!"
981981
mem::replace(&mut self.need_bitfield_allocation, vec![]);
982982
for id in need_bitfield_allocation {
983983
self.with_loaned_item(id, |ctx, item| {
984-
item.kind_mut()
985-
.as_type_mut()
986-
.unwrap()
987-
.as_comp_mut()
984+
let ty = item.kind_mut().as_type_mut().unwrap();
985+
let layout = ty.layout(ctx);
986+
ty.as_comp_mut()
988987
.unwrap()
989-
.compute_bitfield_units(ctx);
988+
.compute_bitfield_units(ctx, layout.as_ref());
990989
});
991990
}
992991
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
#[repr(C)]
9+
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
10+
pub struct __BindgenBitfieldUnit<Storage> {
11+
storage: Storage,
12+
}
13+
impl<Storage> __BindgenBitfieldUnit<Storage> {
14+
#[inline]
15+
pub const fn new(storage: Storage) -> Self {
16+
Self { storage }
17+
}
18+
}
19+
impl<Storage> __BindgenBitfieldUnit<Storage>
20+
where
21+
Storage: AsRef<[u8]> + AsMut<[u8]>,
22+
{
23+
#[inline]
24+
pub fn get_bit(&self, index: usize) -> bool {
25+
debug_assert!(index / 8 < self.storage.as_ref().len());
26+
let byte_index = index / 8;
27+
let byte = self.storage.as_ref()[byte_index];
28+
let bit_index = if cfg!(target_endian = "big") {
29+
7 - (index % 8)
30+
} else {
31+
index % 8
32+
};
33+
let mask = 1 << bit_index;
34+
byte & mask == mask
35+
}
36+
#[inline]
37+
pub fn set_bit(&mut self, index: usize, val: bool) {
38+
debug_assert!(index / 8 < self.storage.as_ref().len());
39+
let byte_index = index / 8;
40+
let byte = &mut self.storage.as_mut()[byte_index];
41+
let bit_index = if cfg!(target_endian = "big") {
42+
7 - (index % 8)
43+
} else {
44+
index % 8
45+
};
46+
let mask = 1 << bit_index;
47+
if val {
48+
*byte |= mask;
49+
} else {
50+
*byte &= !mask;
51+
}
52+
}
53+
#[inline]
54+
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
55+
debug_assert!(bit_width <= 64);
56+
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
57+
debug_assert!(
58+
(bit_offset + (bit_width as usize)) / 8 <=
59+
self.storage.as_ref().len()
60+
);
61+
let mut val = 0;
62+
for i in 0..(bit_width as usize) {
63+
if self.get_bit(i + bit_offset) {
64+
let index = if cfg!(target_endian = "big") {
65+
bit_width as usize - 1 - i
66+
} else {
67+
i
68+
};
69+
val |= 1 << index;
70+
}
71+
}
72+
val
73+
}
74+
#[inline]
75+
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
76+
debug_assert!(bit_width <= 64);
77+
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
78+
debug_assert!(
79+
(bit_offset + (bit_width as usize)) / 8 <=
80+
self.storage.as_ref().len()
81+
);
82+
for i in 0..(bit_width as usize) {
83+
let mask = 1 << i;
84+
let val_bit_is_set = val & mask == mask;
85+
let index = if cfg!(target_endian = "big") {
86+
bit_width as usize - 1 - i
87+
} else {
88+
i
89+
};
90+
self.set_bit(index + bit_offset, val_bit_is_set);
91+
}
92+
}
93+
}
94+
#[repr(C, packed)]
95+
#[derive(Debug, Default, Copy, Clone)]
96+
pub struct Struct {
97+
pub _bitfield_align_1: [u8; 0],
98+
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize]>,
99+
}
100+
#[test]
101+
fn bindgen_test_layout_Struct() {
102+
assert_eq!(
103+
::std::mem::size_of::<Struct>(),
104+
4usize,
105+
concat!("Size of: ", stringify!(Struct))
106+
);
107+
assert_eq!(
108+
::std::mem::align_of::<Struct>(),
109+
1usize,
110+
concat!("Alignment of ", stringify!(Struct))
111+
);
112+
}
113+
impl Struct {
114+
#[inline]
115+
pub fn a(&self) -> ::std::os::raw::c_uchar {
116+
unsafe {
117+
::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u8)
118+
}
119+
}
120+
#[inline]
121+
pub fn set_a(&mut self, val: ::std::os::raw::c_uchar) {
122+
unsafe {
123+
let val: u8 = ::std::mem::transmute(val);
124+
self._bitfield_1.set(0usize, 1u8, val as u64)
125+
}
126+
}
127+
#[inline]
128+
pub fn b(&self) -> ::std::os::raw::c_uchar {
129+
unsafe {
130+
::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u8)
131+
}
132+
}
133+
#[inline]
134+
pub fn set_b(&mut self, val: ::std::os::raw::c_uchar) {
135+
unsafe {
136+
let val: u8 = ::std::mem::transmute(val);
137+
self._bitfield_1.set(1usize, 1u8, val as u64)
138+
}
139+
}
140+
#[inline]
141+
pub fn c(&self) -> ::std::os::raw::c_uchar {
142+
unsafe {
143+
::std::mem::transmute(self._bitfield_1.get(2usize, 6u8) as u8)
144+
}
145+
}
146+
#[inline]
147+
pub fn set_c(&mut self, val: ::std::os::raw::c_uchar) {
148+
unsafe {
149+
let val: u8 = ::std::mem::transmute(val);
150+
self._bitfield_1.set(2usize, 6u8, val as u64)
151+
}
152+
}
153+
#[inline]
154+
pub fn d(&self) -> ::std::os::raw::c_ushort {
155+
unsafe {
156+
::std::mem::transmute(self._bitfield_1.get(8usize, 16u8) as u16)
157+
}
158+
}
159+
#[inline]
160+
pub fn set_d(&mut self, val: ::std::os::raw::c_ushort) {
161+
unsafe {
162+
let val: u16 = ::std::mem::transmute(val);
163+
self._bitfield_1.set(8usize, 16u8, val as u64)
164+
}
165+
}
166+
#[inline]
167+
pub fn e(&self) -> ::std::os::raw::c_uchar {
168+
unsafe {
169+
::std::mem::transmute(self._bitfield_1.get(24usize, 8u8) as u8)
170+
}
171+
}
172+
#[inline]
173+
pub fn set_e(&mut self, val: ::std::os::raw::c_uchar) {
174+
unsafe {
175+
let val: u8 = ::std::mem::transmute(val);
176+
self._bitfield_1.set(24usize, 8u8, val as u64)
177+
}
178+
}
179+
#[inline]
180+
pub fn new_bitfield_1(
181+
a: ::std::os::raw::c_uchar,
182+
b: ::std::os::raw::c_uchar,
183+
c: ::std::os::raw::c_uchar,
184+
d: ::std::os::raw::c_ushort,
185+
e: ::std::os::raw::c_uchar,
186+
) -> __BindgenBitfieldUnit<[u8; 4usize]> {
187+
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 4usize]> =
188+
Default::default();
189+
__bindgen_bitfield_unit.set(0usize, 1u8, {
190+
let a: u8 = unsafe { ::std::mem::transmute(a) };
191+
a as u64
192+
});
193+
__bindgen_bitfield_unit.set(1usize, 1u8, {
194+
let b: u8 = unsafe { ::std::mem::transmute(b) };
195+
b as u64
196+
});
197+
__bindgen_bitfield_unit.set(2usize, 6u8, {
198+
let c: u8 = unsafe { ::std::mem::transmute(c) };
199+
c as u64
200+
});
201+
__bindgen_bitfield_unit.set(8usize, 16u8, {
202+
let d: u16 = unsafe { ::std::mem::transmute(d) };
203+
d as u64
204+
});
205+
__bindgen_bitfield_unit.set(24usize, 8u8, {
206+
let e: u8 = unsafe { ::std::mem::transmute(e) };
207+
e as u64
208+
});
209+
__bindgen_bitfield_unit
210+
}
211+
}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma pack(push, 1)
2+
struct Struct {
3+
unsigned char a : 1;
4+
unsigned char b : 1;
5+
unsigned char c : 6;
6+
unsigned short int d : 16;
7+
unsigned char e : 8;
8+
};
9+
#pragma pack(pop)

0 commit comments

Comments
 (0)