Skip to content

Commit 24edc5c

Browse files
committed
[wip] Rework how bitfields are handled.
1 parent f1caa10 commit 24edc5c

16 files changed

+255
-760
lines changed

src/codegen/mod.rs

+80-78
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod helpers;
22
mod struct_layout;
33

44
use self::helpers::{BlobTyBuilder, attributes};
5-
use self::struct_layout::StructLayoutTracker;
5+
use self::struct_layout::{align_to, bytes_from_bits, StructLayoutTracker};
66
use aster;
77

88
use ir::annotations::FieldAccessorKind;
@@ -717,12 +717,12 @@ impl<'a> ItemToRustTy for Vtable<'a> {
717717
}
718718

719719
struct Bitfield<'a> {
720-
index: usize,
720+
index: &'a mut usize,
721721
fields: Vec<&'a Field>,
722722
}
723723

724724
impl<'a> Bitfield<'a> {
725-
fn new(index: usize, fields: Vec<&'a Field>) -> Self {
725+
fn new(index: &'a mut usize, fields: Vec<&'a Field>) -> Self {
726726
Bitfield {
727727
index: index,
728728
fields: fields,
@@ -732,89 +732,96 @@ impl<'a> Bitfield<'a> {
732732
fn codegen_fields(self,
733733
ctx: &BindgenContext,
734734
fields: &mut Vec<ast::StructField>,
735-
methods: &mut Vec<ast::ImplItem>)
735+
_methods: &mut Vec<ast::ImplItem>)
736736
-> Layout {
737737
use aster::struct_field::StructFieldBuilder;
738-
let mut total_width = self.fields
739-
.iter()
740-
.fold(0u32, |acc, f| acc + f.bitfield().unwrap());
741-
742-
if !total_width.is_power_of_two() || total_width < 8 {
743-
total_width = cmp::max(8, total_width.next_power_of_two());
744-
}
745-
debug_assert_eq!(total_width % 8, 0);
746-
let total_width_in_bytes = total_width as usize / 8;
747-
748-
let bitfield_layout = Layout::new(total_width_in_bytes,
749-
total_width_in_bytes);
750-
let bitfield_type = BlobTyBuilder::new(bitfield_layout).build();
751-
let field_name = format!("_bitfield_{}", self.index);
752-
let field_ident = ctx.ext_cx().ident_of(&field_name);
753-
let field = StructFieldBuilder::named(&field_name)
754-
.pub_()
755-
.build_ty(bitfield_type.clone());
756-
fields.push(field);
757738

739+
// NOTE: What follows is reverse-engineered from LLVM's
740+
// lib/AST/RecordLayoutBuilder.cpp
741+
//
742+
// FIXME(emilio): There are some differences between Microsoft and the
743+
// Itanium ABI, but we'll ignore those and stick to Itanium for now.
744+
//
745+
// Also, we need to handle packed bitfields and stuff.
746+
// TODO(emilio): Take into account C++'s wide bitfields, and
747+
// packing, sigh.
748+
let mut total_size_in_bits = 0;
749+
let mut max_align = 0;
750+
let mut unfilled_bits_in_last_unit = 0;
751+
let mut field_size_in_bits = 0;
752+
*self.index += 1;
753+
let mut last_field_name = format!("_bitfield_{}", self.index);
754+
let mut last_field_align = 0;
758755

759-
let mut offset = 0;
760756
for field in self.fields {
761757
let width = field.bitfield().unwrap();
762-
let field_name = field.name()
763-
.map(ToOwned::to_owned)
764-
.unwrap_or_else(|| format!("at_offset_{}", offset));
765-
766758
let field_item = ctx.resolve_item(field.ty());
767759
let field_ty_layout = field_item.kind()
768760
.expect_type()
769761
.layout(ctx)
770762
.expect("Bitfield without layout? Gah!");
771763

772-
let field_type = field_item.to_rust_ty(ctx);
773-
let int_type = BlobTyBuilder::new(field_ty_layout).build();
764+
let field_align = field_ty_layout.align;
774765

775-
let getter_name = ctx.rust_ident(&field_name);
776-
let setter_name = ctx.ext_cx()
777-
.ident_of(&format!("set_{}", &field_name));
778-
let mask = ((1usize << width) - 1) << offset;
779-
let prefix = ctx.trait_prefix();
780-
// The transmute is unfortunate, but it's needed for enums in
781-
// bitfields.
782-
let item = quote_item!(ctx.ext_cx(),
783-
impl X {
784-
#[inline]
785-
pub fn $getter_name(&self) -> $field_type {
786-
unsafe {
787-
::$prefix::mem::transmute(
788-
(
789-
(self.$field_ident &
790-
($mask as $bitfield_type))
791-
>> $offset
792-
) as $int_type
793-
)
794-
}
795-
}
766+
if field_size_in_bits != 0 &&
767+
(width == 0 || width as usize > unfilled_bits_in_last_unit) {
768+
field_size_in_bits = align_to(field_size_in_bits, field_align);
769+
// Push the new field.
770+
let ty =
771+
BlobTyBuilder::new(Layout::new(bytes_from_bits(field_size_in_bits),
772+
bytes_from_bits(last_field_align)))
773+
.build();
796774

797-
#[inline]
798-
pub fn $setter_name(&mut self, val: $field_type) {
799-
self.$field_ident &= !($mask as $bitfield_type);
800-
self.$field_ident |=
801-
(val as $int_type as $bitfield_type << $offset) &
802-
($mask as $bitfield_type);
803-
}
804-
}
805-
)
806-
.unwrap();
775+
let field = StructFieldBuilder::named(&last_field_name)
776+
.pub_()
777+
.build_ty(ty);
778+
fields.push(field);
807779

808-
let items = match item.unwrap().node {
809-
ast::ItemKind::Impl(_, _, _, _, _, items) => items,
810-
_ => unreachable!(),
811-
};
780+
// TODO(emilio): dedup this.
781+
*self.index += 1;
782+
last_field_name = format!("_bitfield_{}", self.index);
812783

813-
methods.extend(items.into_iter());
814-
offset += width;
784+
// Now reset the size and the rest of stuff.
785+
// unfilled_bits_in_last_unit = 0;
786+
field_size_in_bits = 0;
787+
last_field_align = 0;
788+
}
789+
790+
// TODO(emilio): Create the accessors. Problem here is that we still
791+
// don't know which one is going to be the final alignment of the
792+
// bitfield, and whether we have to index in it. Thus, we don't know
793+
// which integer type do we need.
794+
//
795+
// We could push them to a Vec or something, but given how buggy
796+
// they where maybe it's not a great idea?
797+
field_size_in_bits += width as usize;
798+
total_size_in_bits += width as usize;
799+
800+
801+
let data_size = align_to(field_size_in_bits, field_align * 8);
802+
803+
max_align = cmp::max(max_align, field_align);
804+
805+
// NB: The width here is completely, absolutely intentional.
806+
last_field_align = cmp::max(last_field_align, width as usize);
807+
808+
unfilled_bits_in_last_unit = data_size - field_size_in_bits;
815809
}
816810

817-
bitfield_layout
811+
if field_size_in_bits != 0 {
812+
// Push the last field.
813+
let ty =
814+
BlobTyBuilder::new(Layout::new(bytes_from_bits(field_size_in_bits),
815+
bytes_from_bits(last_field_align)))
816+
.build();
817+
818+
let field = StructFieldBuilder::named(&last_field_name)
819+
.pub_()
820+
.build_ty(ty);
821+
fields.push(field);
822+
}
823+
824+
Layout::new(bytes_from_bits(total_size_in_bits), max_align)
818825
}
819826
}
820827

@@ -1062,12 +1069,10 @@ impl CodeGenerator for CompInfo {
10621069
debug_assert!(!current_bitfield_fields.is_empty());
10631070
let bitfield_fields =
10641071
mem::replace(&mut current_bitfield_fields, vec![]);
1065-
bitfield_count += 1;
1066-
let bitfield_layout = Bitfield::new(bitfield_count,
1072+
let bitfield_layout = Bitfield::new(&mut bitfield_count,
10671073
bitfield_fields)
10681074
.codegen_fields(ctx, &mut fields, &mut methods);
1069-
1070-
struct_layout.saw_bitfield(bitfield_layout);
1075+
struct_layout.saw_bitfield_batch(bitfield_layout);
10711076

10721077
current_bitfield_width = None;
10731078
current_bitfield_layout = None;
@@ -1099,8 +1104,7 @@ impl CodeGenerator for CompInfo {
10991104
} else {
11001105
quote_ty!(ctx.ext_cx(), __BindgenUnionField<$ty>)
11011106
}
1102-
} else if let Some(item) =
1103-
field_ty.is_incomplete_array(ctx) {
1107+
} else if let Some(item) = field_ty.is_incomplete_array(ctx) {
11041108
result.saw_incomplete_array();
11051109

11061110
let inner = item.to_rust_ty(ctx);
@@ -1224,12 +1228,10 @@ impl CodeGenerator for CompInfo {
12241228
debug_assert!(!current_bitfield_fields.is_empty());
12251229
let bitfield_fields = mem::replace(&mut current_bitfield_fields,
12261230
vec![]);
1227-
bitfield_count += 1;
1228-
let bitfield_layout = Bitfield::new(bitfield_count,
1231+
let bitfield_layout = Bitfield::new(&mut bitfield_count,
12291232
bitfield_fields)
12301233
.codegen_fields(ctx, &mut fields, &mut methods);
1231-
1232-
struct_layout.saw_bitfield(bitfield_layout);
1234+
struct_layout.saw_bitfield_batch(bitfield_layout);
12331235
}
12341236
debug_assert!(current_bitfield_fields.is_empty());
12351237

src/codegen/struct_layout.rs

+59-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,60 @@ pub struct StructLayoutTracker<'a, 'ctx: 'a> {
2323
max_field_align: usize,
2424
}
2525

26+
/// Returns a size aligned to a given value.
27+
pub fn align_to(size: usize, align: usize) -> usize {
28+
if align == 0 {
29+
return size;
30+
}
31+
32+
let rem = size % align;
33+
if rem == 0 {
34+
return size;
35+
}
36+
37+
size + align - rem
38+
}
39+
40+
/// 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 {
42+
if n == 0 {
43+
return 0;
44+
}
45+
46+
if n <= 8 {
47+
return 1;
48+
}
49+
50+
if !n.is_power_of_two() {
51+
n = n.next_power_of_two();
52+
}
53+
54+
n / 8
55+
}
56+
57+
#[test]
58+
fn test_align_to() {
59+
assert_eq!(align_to(1, 1), 1);
60+
assert_eq!(align_to(1, 2), 2);
61+
assert_eq!(align_to(1, 4), 4);
62+
assert_eq!(align_to(5, 1), 5);
63+
assert_eq!(align_to(17, 4), 20);
64+
}
65+
66+
#[test]
67+
fn test_bytes_from_bits() {
68+
assert_eq!(bytes_from_bits(0), 0);
69+
for i in 1..9 {
70+
assert_eq!(bytes_from_bits(i), 1);
71+
}
72+
for i in 9..17 {
73+
assert_eq!(bytes_from_bits(i), 2);
74+
}
75+
for i in 17..33 {
76+
assert_eq!(bytes_from_bits(i), 4);
77+
}
78+
}
79+
2680
impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
2781
pub fn new(ctx: &'a BindgenContext<'ctx>, comp: &'a CompInfo) -> Self {
2882
StructLayoutTracker {
@@ -51,13 +105,14 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
51105
self.max_field_align = cmp::max(self.max_field_align, layout.align);
52106
}
53107
}
54-
55-
pub fn saw_bitfield(&mut self, layout: Layout) {
108+
pub fn saw_bitfield_batch(&mut self, layout: Layout) {
56109
self.align_to_latest_field();
57110

58111
self.latest_offset += self.padding_bytes(layout) + layout.size;
59112
self.latest_field_layout = Some(layout);
60-
self.max_field_align = cmp::max(self.max_field_align, layout.align);
113+
// NB: We intentionally don't update the max_field_align here, since our
114+
// bitfields code doesn't necessarily guarantee it, so we need to
115+
// actually generate the dummy alignment.
61116
}
62117

63118
pub fn saw_union(&mut self, layout: Layout) {
@@ -166,15 +221,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
166221
}
167222

168223
fn padding_bytes(&self, layout: Layout) -> usize {
169-
if layout.align == 0 {
170-
warn!("try to padding bytes without layout");
171-
172-
0
173-
} else if self.latest_offset % layout.align == 0 {
174-
0
175-
} else {
176-
layout.align - (self.latest_offset % layout.align)
177-
}
224+
align_to(self.latest_offset, layout.align) - self.latest_offset
178225
}
179226

180227
fn padding_field(&mut self, layout: Layout) -> ast::StructField {

src/ir/ty.rs

+3
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ impl Type {
383383

384384
/// If this type has a known size, return it (in bytes).
385385
pub fn calc_size(&self, ctx: &BindgenContext) -> Option<usize> {
386+
if let Some(layout) = self.layout {
387+
return Some(layout.size);
388+
}
386389
match self.kind {
387390
TypeKind::Comp(ref ci) => {
388391
ci.calc_layout(ctx).map(|layout| layout.size)

0 commit comments

Comments
 (0)