Skip to content

Commit 71e43ed

Browse files
committed
fixup! [wip] Rework how bitfields are handled.
1 parent a721557 commit 71e43ed

File tree

6 files changed

+158
-25
lines changed

6 files changed

+158
-25
lines changed

src/codegen/mod.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ mod helpers;
22
mod struct_layout;
33

44
use self::helpers::{BlobTyBuilder, attributes};
5-
use self::struct_layout::{align_to, bytes_from_bits, StructLayoutTracker};
5+
use self::struct_layout::{align_to, bytes_from_bits};
6+
use self::struct_layout::{bytes_from_bits_pow2, StructLayoutTracker};
67
use aster;
78

89
use ir::annotations::FieldAccessorKind;
@@ -767,8 +768,8 @@ impl<'a> Bitfield<'a> {
767768
field_size_in_bits = align_to(field_size_in_bits, field_align);
768769
// Push the new field.
769770
let ty =
770-
BlobTyBuilder::new(Layout::new(bytes_from_bits(field_size_in_bits),
771-
bytes_from_bits(last_field_align)))
771+
BlobTyBuilder::new(Layout::new(bytes_from_bits_pow2(field_size_in_bits),
772+
bytes_from_bits_pow2(last_field_align)))
772773
.build();
773774

774775
let field = StructFieldBuilder::named(&last_field_name)
@@ -810,8 +811,8 @@ impl<'a> Bitfield<'a> {
810811
if field_size_in_bits != 0 {
811812
// Push the last field.
812813
let ty =
813-
BlobTyBuilder::new(Layout::new(bytes_from_bits(field_size_in_bits),
814-
bytes_from_bits(last_field_align)))
814+
BlobTyBuilder::new(Layout::new(bytes_from_bits_pow2(field_size_in_bits),
815+
bytes_from_bits_pow2(last_field_align)))
815816
.build();
816817

817818
let field = StructFieldBuilder::named(&last_field_name)

src/codegen/struct_layout.rs

+95-20
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub struct StructLayoutTracker<'a, 'ctx: 'a> {
2121
padding_count: usize,
2222
latest_field_layout: Option<Layout>,
2323
max_field_align: usize,
24+
last_field_was_bitfield: bool,
2425
}
2526

2627
/// Returns a size aligned to a given value.
@@ -37,8 +38,17 @@ pub fn align_to(size: usize, align: usize) -> usize {
3738
size + align - rem
3839
}
3940

41+
/// Returns the amount of bytes from a given amount of bytes, rounding up.
42+
pub fn bytes_from_bits(n: usize) -> usize {
43+
if n % 8 == 0 {
44+
return n / 8;
45+
}
46+
47+
n / 8 + 1
48+
}
49+
4050
/// Returns the lower power of two byte count that can hold at most n bits.
41-
pub fn bytes_from_bits(mut n: usize) -> usize {
51+
pub fn bytes_from_bits_pow2(mut n: usize) -> usize {
4252
if n == 0 {
4353
return 0;
4454
}
@@ -63,6 +73,20 @@ fn test_align_to() {
6373
assert_eq!(align_to(17, 4), 20);
6474
}
6575

76+
#[test]
77+
fn test_bytes_from_bits_pow2() {
78+
assert_eq!(bytes_from_bits_pow2(0), 0);
79+
for i in 1..9 {
80+
assert_eq!(bytes_from_bits_pow2(i), 1);
81+
}
82+
for i in 9..17 {
83+
assert_eq!(bytes_from_bits_pow2(i), 2);
84+
}
85+
for i in 17..33 {
86+
assert_eq!(bytes_from_bits_pow2(i), 4);
87+
}
88+
}
89+
6690
#[test]
6791
fn test_bytes_from_bits() {
6892
assert_eq!(bytes_from_bits(0), 0);
@@ -72,8 +96,8 @@ fn test_bytes_from_bits() {
7296
for i in 9..17 {
7397
assert_eq!(bytes_from_bits(i), 2);
7498
}
75-
for i in 17..33 {
76-
assert_eq!(bytes_from_bits(i), 4);
99+
for i in 17..25 {
100+
assert_eq!(bytes_from_bits(i), 3);
77101
}
78102
}
79103

@@ -86,6 +110,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
86110
padding_count: 0,
87111
latest_field_layout: None,
88112
max_field_align: 0,
113+
last_field_was_bitfield: false,
89114
}
90115
}
91116

@@ -97,26 +122,33 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
97122
}
98123

99124
pub fn saw_base(&mut self, base_ty: &Type) {
100-
self.align_to_latest_field();
101-
102125
if let Some(layout) = base_ty.layout(self.ctx) {
126+
self.align_to_latest_field(layout);
127+
103128
self.latest_offset += self.padding_bytes(layout) + layout.size;
104129
self.latest_field_layout = Some(layout);
105130
self.max_field_align = cmp::max(self.max_field_align, layout.align);
106131
}
107132
}
133+
108134
pub fn saw_bitfield_batch(&mut self, layout: Layout) {
109-
self.align_to_latest_field();
135+
self.align_to_latest_field(layout);
136+
137+
self.latest_offset += layout.size;
138+
139+
debug!("Offset: <bitfield>: {} -> {}",
140+
self.latest_offset - layout.size,
141+
self.latest_offset);
110142

111-
self.latest_offset += self.padding_bytes(layout) + layout.size;
112143
self.latest_field_layout = Some(layout);
144+
self.last_field_was_bitfield = true;
113145
// NB: We intentionally don't update the max_field_align here, since our
114146
// bitfields code doesn't necessarily guarantee it, so we need to
115147
// actually generate the dummy alignment.
116148
}
117149

118150
pub fn saw_union(&mut self, layout: Layout) {
119-
self.align_to_latest_field();
151+
self.align_to_latest_field(layout);
120152

121153
self.latest_offset += self.padding_bytes(layout) + layout.size;
122154
self.latest_field_layout = Some(layout);
@@ -151,7 +183,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
151183
}
152184
}
153185

154-
self.align_to_latest_field();
186+
let will_merge_with_bitfield = self.align_to_latest_field(field_layout);
155187

156188
let padding_layout = if self.comp.packed() {
157189
None
@@ -160,17 +192,19 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
160192
Some(offset) if offset / 8 > self.latest_offset => {
161193
offset / 8 - self.latest_offset
162194
}
163-
_ if field_layout.align == 0 => 0,
164-
_ => {
165-
self.padding_bytes(field_layout)
166-
}
195+
_ if will_merge_with_bitfield || field_layout.align == 0 => 0,
196+
_ => self.padding_bytes(field_layout),
167197
};
168198

169199
// Otherwise the padding is useless.
170200
let need_padding = padding_bytes >= field_layout.align;
171201

172202
self.latest_offset += padding_bytes;
173203

204+
debug!("Offset: <padding>: {} -> {}",
205+
self.latest_offset - padding_bytes,
206+
self.latest_offset);
207+
174208
debug!("align field {} to {}/{} with {} padding bytes {:?}",
175209
field_name,
176210
self.latest_offset,
@@ -188,14 +222,22 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
188222
self.latest_offset += field_layout.size;
189223
self.latest_field_layout = Some(field_layout);
190224
self.max_field_align = cmp::max(self.max_field_align, field_layout.align);
225+
self.last_field_was_bitfield = false;
226+
227+
debug!("Offset: {}: {} -> {}",
228+
field_name,
229+
self.latest_offset - field_layout.size,
230+
self.latest_offset);
191231

192232
padding_layout.map(|layout| self.padding_field(layout))
193233
}
194234

195235
pub fn pad_struct(&mut self, layout: Layout) -> Option<ast::StructField> {
196236
if layout.size < self.latest_offset {
197-
warn!("calculate struct layout incorrect, too more {} bytes",
198-
self.latest_offset - layout.size);
237+
if cfg!(debug_assertions) {
238+
panic!("calculate struct layout incorrect, too more {} bytes",
239+
self.latest_offset - layout.size);
240+
}
199241

200242
return None
201243
}
@@ -204,12 +246,19 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
204246

205247
// We always pad to get to the correct size if the struct is one of
206248
// those we can't align properly.
249+
//
250+
// Note that if the last field we saw was a bitfield, we may need to pad
251+
// regardless, because bitfields don't respect alignment as strictly as
252+
// other fields.
207253
if padding_bytes > 0 &&
208254
(padding_bytes >= layout.align ||
255+
(self.last_field_was_bitfield &&
256+
padding_bytes >= self.latest_field_layout.unwrap().align) ||
209257
layout.align > mem::size_of::<*mut ()>()) {
210258
let layout = if self.comp.packed() {
211259
Layout::new(padding_bytes, 1)
212-
} else if layout.align > mem::size_of::<*mut ()>() {
260+
} else if self.last_field_was_bitfield ||
261+
layout.align > mem::size_of::<*mut ()>() {
213262
// We've already given up on alignment here.
214263
Layout::for_size(padding_bytes)
215264
} else {
@@ -252,11 +301,37 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
252301
StructFieldBuilder::named(padding_field_name).pub_().build_ty(ty)
253302
}
254303

255-
fn align_to_latest_field(&mut self) {
304+
/// Returns whether the new field is known to merge with a bitfield.
305+
///
306+
/// This is just to avoid doing the same check also in pad_field.
307+
fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool {
256308
if self.comp.packed() {
257-
// skip to align field when packed
258-
} else if let Some(layout) = self.latest_field_layout.take() {
259-
self.latest_offset += self.padding_bytes(layout);
309+
// Skip to align fields when packed.
310+
return false;
260311
}
312+
313+
let layout = match self.latest_field_layout {
314+
Some(l) => l,
315+
None => return false,
316+
};
317+
318+
// If it was, we may or may not need to align, depending on what the
319+
// current field alignment and the bitfield size and alignment are.
320+
debug!("align_to_bitfield? {}: {:?} {:?}", self.last_field_was_bitfield,
321+
layout, new_field_layout);
322+
323+
if self.last_field_was_bitfield &&
324+
new_field_layout.align <= layout.size % layout.align &&
325+
new_field_layout.size <= layout.size % layout.align {
326+
// The new field will be coalesced into some of the remaining bits.
327+
//
328+
// FIXME(emilio): I think this may not catch everything?
329+
debug!("Will merge with bitfield");
330+
return true;
331+
}
332+
333+
// Else, just align the obvious way.
334+
self.latest_offset += self.padding_bytes(layout);
335+
return false;
261336
}
262337
}

tests/expectations/tests/bitfield_align.rs

+40
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,43 @@ fn bindgen_test_layout_C() {
7272
impl Clone for C {
7373
fn clone(&self) -> Self { *self }
7474
}
75+
#[repr(C)]
76+
#[derive(Debug, Default, Copy)]
77+
pub struct Date1 {
78+
pub _bitfield_1: [u8; 2usize],
79+
pub _bitfield_2: u8,
80+
pub __bindgen_align: [u16; 0usize],
81+
}
82+
#[test]
83+
fn bindgen_test_layout_Date1() {
84+
assert_eq!(::std::mem::size_of::<Date1>() , 4usize , concat ! (
85+
"Size of: " , stringify ! ( Date1 ) ));
86+
assert_eq! (::std::mem::align_of::<Date1>() , 2usize , concat ! (
87+
"Alignment of " , stringify ! ( Date1 ) ));
88+
}
89+
impl Clone for Date1 {
90+
fn clone(&self) -> Self { *self }
91+
}
92+
#[repr(C)]
93+
#[derive(Debug, Default, Copy)]
94+
pub struct Date2 {
95+
pub _bitfield_1: [u8; 2usize],
96+
pub _bitfield_2: u8,
97+
pub byte: ::std::os::raw::c_uchar,
98+
pub __bindgen_align: [u16; 0usize],
99+
}
100+
#[test]
101+
fn bindgen_test_layout_Date2() {
102+
assert_eq!(::std::mem::size_of::<Date2>() , 4usize , concat ! (
103+
"Size of: " , stringify ! ( Date2 ) ));
104+
assert_eq! (::std::mem::align_of::<Date2>() , 2usize , concat ! (
105+
"Alignment of " , stringify ! ( Date2 ) ));
106+
assert_eq! (unsafe {
107+
& ( * ( 0 as * const Date2 ) ) . byte as * const _ as usize }
108+
, 3usize , concat ! (
109+
"Alignment of field: " , stringify ! ( Date2 ) , "::" ,
110+
stringify ! ( byte ) ));
111+
}
112+
impl Clone for Date2 {
113+
fn clone(&self) -> Self { *self }
114+
}

tests/expectations/tests/layout_align.rs

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub struct rte_eth_link {
7171
/**< ETH_SPEED_NUM_ */
7272
pub link_speed: u32,
7373
pub _bitfield_1: u8,
74+
pub __bindgen_padding_0: [u8; 3usize],
7475
pub __bindgen_align: [u64; 0usize],
7576
}
7677
#[test]

tests/expectations/tests/layout_eth_conf.rs

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ pub struct rte_eth_txmode {
140140
pub mq_mode: rte_eth_tx_mq_mode,
141141
pub pvid: u16,
142142
pub _bitfield_1: u8,
143+
pub __bindgen_padding_0: u8,
143144
}
144145
#[test]
145146
fn bindgen_test_layout_rte_eth_txmode() {

tests/headers/bitfield_align.h

+15
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,18 @@ struct C {
2424
unsigned b2 : 1;
2525
unsigned baz;
2626
};
27+
28+
struct Date1 {
29+
unsigned short nWeekDay : 3; // 0..7 (3 bits)
30+
unsigned short nMonthDay : 6; // 0..31 (6 bits)
31+
unsigned short nMonth : 5; // 0..12 (5 bits)
32+
unsigned short nYear : 8; // 0..100 (8 bits)
33+
};
34+
35+
struct Date2 {
36+
unsigned short nWeekDay : 3; // 0..7 (3 bits)
37+
unsigned short nMonthDay : 6; // 0..31 (6 bits)
38+
unsigned short nMonth : 5; // 0..12 (5 bits)
39+
unsigned short nYear : 8; // 0..100 (8 bits)
40+
unsigned char byte;
41+
};

0 commit comments

Comments
 (0)