Skip to content

Commit fdea868

Browse files
author
bors-servo
authored
Auto merge of #567 - fitzgen:bitfield-accessors, r=emilio
Reintroduce bitfield accessors This commit reintroduces accessor methods for bitfields in the generated bindings. Fixes #519 r? @emilio
2 parents 2afecec + 50ee737 commit fdea868

14 files changed

+1550
-36
lines changed

bindgen-integration/cpp/Test.cc

+28
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,31 @@ Test::Test(double foo)
2020
: m_int(0)
2121
, m_double(foo)
2222
{}
23+
24+
namespace bitfields {
25+
26+
bool
27+
First::assert(unsigned char first,
28+
unsigned char second,
29+
unsigned char third)
30+
{
31+
return three_bits_byte_one == first &&
32+
six_bits_byte_two == second &&
33+
two_bits_byte_two == third;
34+
}
35+
36+
bool
37+
Second::assert(int first, bool second)
38+
{
39+
return thirty_one_bits == first && one_bit == second;
40+
}
41+
42+
bool
43+
Third::assert(int first, bool second, ItemKind third)
44+
{
45+
return flags == first &&
46+
is_whatever == second &&
47+
kind == third;
48+
}
49+
50+
}

bindgen-integration/cpp/Test.h

+42
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,45 @@ typedef Test TypeAlias;
2020
} // namespace testing
2121

2222
typedef testing::TypeAlias TypeAlias;
23+
24+
namespace bitfields {
25+
26+
struct First {
27+
unsigned char three_bits_byte_one : 3;
28+
// This starts a new byte, leaving 5 bits unused.
29+
unsigned char :0;
30+
31+
unsigned char six_bits_byte_two : 6;
32+
unsigned char two_bits_byte_two : 2;
33+
34+
/// Returns true if the bitfields match the arguments, false otherwise.
35+
bool assert(unsigned char first,
36+
unsigned char second,
37+
unsigned char third);
38+
};
39+
40+
struct Second {
41+
int thirty_one_bits : 31;
42+
bool one_bit : 1;
43+
44+
/// Returns true if the bitfields match the arguments, false otherwise.
45+
bool assert(int first,
46+
bool second);
47+
};
48+
49+
enum ItemKind {
50+
ITEM_KIND_UNO,
51+
ITEM_KIND_DOS,
52+
ITEM_KIND_TRES,
53+
};
54+
55+
struct Third {
56+
int flags : 28;
57+
bool is_whatever : 1;
58+
ItemKind kind : 3;
59+
60+
/// Returns true if the bitfields match the arguments, false otherwise.
61+
bool assert(int first, bool second, ItemKind third);
62+
};
63+
64+
} // namespace bitfields

bindgen-integration/src/lib.rs

100644100755
+54
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
#![allow(warnings)]
2+
13
mod bindings {
24
include!(concat!(env!("OUT_DIR"), "/test.rs"));
35
}
46

57
use std::ffi::CStr;
68
use std::os::raw::c_int;
9+
use std::mem;
710

811
#[test]
912
fn test_static_array() {
@@ -47,3 +50,54 @@ fn test_overload() {
4750
assert_eq!(test.m_int, 0);
4851
assert_eq!(test.m_double, 5.0);
4952
}
53+
54+
#[test]
55+
fn test_bitfields_first() {
56+
let mut first: bindings::bitfields::First = unsafe {
57+
mem::zeroed()
58+
};
59+
assert!(unsafe {
60+
first.assert(0, 0, 0)
61+
});
62+
first.set_three_bits_byte_one(2);
63+
first.set_six_bits_byte_two(42);
64+
first.set_two_bits_byte_two(1);
65+
assert!(unsafe {
66+
first.assert(2, 42, 1)
67+
});
68+
}
69+
70+
#[test]
71+
fn test_bitfields_second() {
72+
let mut second: bindings::bitfields::Second = unsafe {
73+
mem::zeroed()
74+
};
75+
assert!(unsafe {
76+
second.assert(0, false)
77+
});
78+
second.set_thirty_one_bits(1337);
79+
second.set_one_bit(true);
80+
assert!(unsafe {
81+
second.assert(1337, true)
82+
});
83+
}
84+
85+
#[test]
86+
fn test_bitfields_third() {
87+
let mut third: bindings::bitfields::Third = unsafe {
88+
mem::zeroed()
89+
};
90+
assert!(unsafe {
91+
third.assert(0,
92+
false,
93+
bindings::bitfields::ItemKind::ITEM_KIND_UNO)
94+
});
95+
third.set_flags(12345);
96+
third.set_is_whatever(true);
97+
third.set_kind(bindings::bitfields::ItemKind::ITEM_KIND_TRES);
98+
assert!(unsafe {
99+
third.assert(12345,
100+
true,
101+
bindings::bitfields::ItemKind::ITEM_KIND_TRES)
102+
});
103+
}

src/codegen/mod.rs

+116-36
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,8 @@ impl<'a> Bitfield<'a> {
736736
fn codegen_fields(self,
737737
ctx: &BindgenContext,
738738
fields: &mut Vec<ast::StructField>,
739-
_methods: &mut Vec<ast::ImplItem>)
739+
methods: &mut Vec<ast::ImplItem>)
740740
-> Layout {
741-
use aster::struct_field::StructFieldBuilder;
742-
743741
// NOTE: What follows is reverse-engineered from LLVM's
744742
// lib/AST/RecordLayoutBuilder.cpp
745743
//
@@ -757,29 +755,28 @@ impl<'a> Bitfield<'a> {
757755
let mut last_field_name = format!("_bitfield_{}", self.index);
758756
let mut last_field_align = 0;
759757

758+
// (name, mask, width, bitfield's type, bitfield's layout)
759+
let mut bitfields: Vec<(&str, usize, usize, ast::Ty, Layout)> = vec![];
760+
760761
for field in self.fields {
761-
let width = field.bitfield().unwrap();
762+
let width = field.bitfield().unwrap() as usize;
762763
let field_item = ctx.resolve_item(field.ty());
763764
let field_ty_layout = field_item.kind()
764765
.expect_type()
765766
.layout(ctx)
766767
.expect("Bitfield without layout? Gah!");
767-
768768
let field_align = field_ty_layout.align;
769769

770770
if field_size_in_bits != 0 &&
771-
(width == 0 || width as usize > unfilled_bits_in_last_unit) {
771+
(width == 0 || width > unfilled_bits_in_last_unit) {
772+
// We've finished a physical field, so flush it and its bitfields.
772773
field_size_in_bits = align_to(field_size_in_bits, field_align);
773-
// Push the new field.
774-
let ty =
775-
BlobTyBuilder::new(Layout::new(bytes_from_bits_pow2(field_size_in_bits),
776-
bytes_from_bits_pow2(last_field_align)))
777-
.build();
778-
779-
let field = StructFieldBuilder::named(&last_field_name)
780-
.pub_()
781-
.build_ty(ty);
782-
fields.push(field);
774+
fields.push(flush_bitfields(ctx,
775+
field_size_in_bits,
776+
last_field_align,
777+
&last_field_name,
778+
bitfields.drain(..),
779+
methods));
783780

784781
// TODO(emilio): dedup this.
785782
*self.index += 1;
@@ -791,44 +788,127 @@ impl<'a> Bitfield<'a> {
791788
last_field_align = 0;
792789
}
793790

794-
// TODO(emilio): Create the accessors. Problem here is that we still
795-
// don't know which one is going to be the final alignment of the
796-
// bitfield, and whether we have to index in it. Thus, we don't know
797-
// which integer type do we need.
798-
//
799-
// We could push them to a Vec or something, but given how buggy
800-
// they where maybe it's not a great idea?
801-
field_size_in_bits += width as usize;
802-
total_size_in_bits += width as usize;
791+
if let Some(name) = field.name() {
792+
bitfields.push((name,
793+
field_size_in_bits,
794+
width,
795+
field_item.to_rust_ty(ctx).unwrap(),
796+
field_ty_layout));
797+
}
803798

799+
field_size_in_bits += width;
800+
total_size_in_bits += width;
804801

805802
let data_size = align_to(field_size_in_bits, field_align * 8);
806803

807804
max_align = cmp::max(max_align, field_align);
808805

809806
// NB: The width here is completely, absolutely intentional.
810-
last_field_align = cmp::max(last_field_align, width as usize);
807+
last_field_align = cmp::max(last_field_align, width);
811808

812809
unfilled_bits_in_last_unit = data_size - field_size_in_bits;
813810
}
814811

815812
if field_size_in_bits != 0 {
816-
// Push the last field.
817-
let ty =
818-
BlobTyBuilder::new(Layout::new(bytes_from_bits_pow2(field_size_in_bits),
819-
bytes_from_bits_pow2(last_field_align)))
820-
.build();
821-
822-
let field = StructFieldBuilder::named(&last_field_name)
823-
.pub_()
824-
.build_ty(ty);
825-
fields.push(field);
813+
// Flush the last physical field and its bitfields.
814+
fields.push(flush_bitfields(ctx,
815+
field_size_in_bits,
816+
last_field_align,
817+
&last_field_name,
818+
bitfields.drain(..),
819+
methods));
826820
}
827821

828822
Layout::new(bytes_from_bits(total_size_in_bits), max_align)
829823
}
830824
}
831825

826+
/// A physical field (which is a word or byte or ...) has many logical bitfields
827+
/// contained within it, but not all bitfields are in the same physical field of
828+
/// a struct. This function creates a single physical field and flushes all the
829+
/// accessors for the logical `bitfields` within that physical field to the
830+
/// outgoing `methods`.
831+
fn flush_bitfields<'a, I>(ctx: &BindgenContext,
832+
field_size_in_bits: usize,
833+
field_align: usize,
834+
field_name: &str,
835+
bitfields: I,
836+
methods: &mut Vec<ast::ImplItem>) -> ast::StructField
837+
where I: IntoIterator<Item = (&'a str, usize, usize, ast::Ty, Layout)>
838+
{
839+
use aster::struct_field::StructFieldBuilder;
840+
841+
let field_layout = Layout::new(bytes_from_bits_pow2(field_size_in_bits),
842+
bytes_from_bits_pow2(field_align));
843+
let field_ty = BlobTyBuilder::new(field_layout).build();
844+
845+
let field = StructFieldBuilder::named(field_name)
846+
.pub_()
847+
.build_ty(field_ty.clone());
848+
849+
for (name, offset, width, bitfield_ty, bitfield_layout) in bitfields {
850+
let prefix = ctx.trait_prefix();
851+
let getter_name = ctx.rust_ident(name);
852+
let setter_name = ctx.ext_cx()
853+
.ident_of(&format!("set_{}", &name));
854+
let field_ident = ctx.ext_cx().ident_of(field_name);
855+
856+
let field_int_ty = match field_layout.size {
857+
8 => quote_ty!(ctx.ext_cx(), u64),
858+
4 => quote_ty!(ctx.ext_cx(), u32),
859+
2 => quote_ty!(ctx.ext_cx(), u16),
860+
1 => quote_ty!(ctx.ext_cx(), u8),
861+
_ => panic!("physical field containing bitfields should be sized \
862+
8, 4, 2, or 1 bytes")
863+
};
864+
let bitfield_int_ty = BlobTyBuilder::new(bitfield_layout).build();
865+
866+
let mask: usize = ((1usize << width) - 1usize) << offset;
867+
868+
let impl_item = quote_item!(
869+
ctx.ext_cx(),
870+
impl XxxIgnored {
871+
#[inline]
872+
pub fn $getter_name(&self) -> $bitfield_ty {
873+
let mask = $mask as $field_int_ty;
874+
let field_val: $field_int_ty = unsafe {
875+
::$prefix::mem::transmute(self.$field_ident)
876+
};
877+
let val = (field_val & mask) >> $offset;
878+
unsafe {
879+
::$prefix::mem::transmute(val as $bitfield_int_ty)
880+
}
881+
}
882+
883+
#[inline]
884+
pub fn $setter_name(&mut self, val: $bitfield_ty) {
885+
let mask = $mask as $field_int_ty;
886+
let val = val as $bitfield_int_ty as $field_int_ty;
887+
888+
let mut field_val: $field_int_ty = unsafe {
889+
::$prefix::mem::transmute(self.$field_ident)
890+
};
891+
field_val &= !mask;
892+
field_val |= (val << $offset) & mask;
893+
894+
self.$field_ident = unsafe {
895+
::$prefix::mem::transmute(field_val)
896+
};
897+
}
898+
}
899+
).unwrap();
900+
901+
match impl_item.unwrap().node {
902+
ast::ItemKind::Impl(_, _, _, _, _, items) => {
903+
methods.extend(items.into_iter());
904+
},
905+
_ => unreachable!(),
906+
};
907+
}
908+
909+
field
910+
}
911+
832912
impl CodeGenerator for TemplateInstantiation {
833913
type Extra = Item;
834914

0 commit comments

Comments
 (0)