summaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorFlier Lu <flier.lu@gmail.com>2017-02-02 01:02:39 +0800
committerFlier Lu <flier.lu@nexusguard.com>2017-02-07 10:55:50 +0800
commit7fd707004b03a8110c6d82e82165f9e1a8ad6985 (patch)
tree94b170597b30a470edde2c6999a6e2a52e3db9c2 /src/codegen
parent7fa654c699b61250787e53a300357f0c58c55b18 (diff)
add padding bytes to align strcture
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/mod.rs119
-rw-r--r--src/codegen/struct_layout.rs193
2 files changed, 291 insertions, 21 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 3b0882af..f4da014e 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -1,6 +1,8 @@
mod helpers;
+mod struct_layout;
use self::helpers::{BlobTyBuilder, attributes};
+use self::struct_layout::StructLayoutTracker;
use aster;
use ir::annotations::FieldAccessorKind;
@@ -21,6 +23,7 @@ use ir::var::Var;
use std::borrow::Cow;
use std::cell::Cell;
+use std::cmp;
use std::collections::{HashSet, VecDeque};
use std::collections::hash_map::{Entry, HashMap};
use std::fmt::Write;
@@ -723,9 +726,9 @@ impl<'a> Bitfield<'a> {
fn codegen_fields(self,
ctx: &BindgenContext,
fields: &mut Vec<ast::StructField>,
- methods: &mut Vec<ast::ImplItem>) {
+ methods: &mut Vec<ast::ImplItem>)
+ -> Layout {
use aster::struct_field::StructFieldBuilder;
- use std::cmp;
let mut total_width = self.fields
.iter()
.fold(0u32, |acc, f| acc + f.bitfield().unwrap());
@@ -736,10 +739,9 @@ impl<'a> Bitfield<'a> {
debug_assert_eq!(total_width % 8, 0);
let total_width_in_bytes = total_width as usize / 8;
- let bitfield_type =
- BlobTyBuilder::new(Layout::new(total_width_in_bytes,
- total_width_in_bytes))
- .build();
+ let bitfield_layout = Layout::new(total_width_in_bytes,
+ total_width_in_bytes);
+ let bitfield_type = BlobTyBuilder::new(bitfield_layout).build();
let field_name = format!("_bitfield_{}", self.index);
let field_ident = ctx.ext_cx().ident_of(&field_name);
let field = StructFieldBuilder::named(&field_name)
@@ -805,6 +807,8 @@ impl<'a> Bitfield<'a> {
methods.extend(items.into_iter());
offset += width;
}
+
+ bitfield_layout
}
}
@@ -940,6 +944,7 @@ impl CodeGenerator for CompInfo {
// Also, we need to generate the vtable in such a way it "inherits" from
// the parent too.
let mut fields = vec![];
+ let mut struct_layout = StructLayoutTracker::new(ctx, self);
if self.needs_explicit_vtable(ctx) {
let vtable =
Vtable::new(item.id(), self.methods(), self.base_members());
@@ -951,6 +956,8 @@ impl CodeGenerator for CompInfo {
.pub_()
.build_ty(vtable_type);
+ struct_layout.saw_vtable();
+
fields.push(vtable_field);
}
@@ -985,6 +992,8 @@ impl CodeGenerator for CompInfo {
format!("_base_{}", i)
};
+ struct_layout.saw_base(base_ty);
+
let field = StructFieldBuilder::named(field_name)
.pub_()
.build_ty(inner);
@@ -1039,8 +1048,12 @@ impl CodeGenerator for CompInfo {
let bitfield_fields =
mem::replace(&mut current_bitfield_fields, vec![]);
bitfield_count += 1;
- Bitfield::new(bitfield_count, bitfield_fields)
+ let bitfield_layout = Bitfield::new(bitfield_count,
+ bitfield_fields)
.codegen_fields(ctx, &mut fields, &mut methods);
+
+ struct_layout.saw_bitfield(bitfield_layout);
+
current_bitfield_width = None;
current_bitfield_layout = None;
}
@@ -1100,6 +1113,11 @@ impl CodeGenerator for CompInfo {
}
};
+ if let Some(padding_field) =
+ struct_layout.pad_field(&field_name, field_ty, field.offset()) {
+ fields.push(padding_field);
+ }
+
let is_private = field.annotations()
.private_fields()
.unwrap_or(fields_should_be_private);
@@ -1192,8 +1210,11 @@ impl CodeGenerator for CompInfo {
let bitfield_fields = mem::replace(&mut current_bitfield_fields,
vec![]);
bitfield_count += 1;
- Bitfield::new(bitfield_count, bitfield_fields)
+ let bitfield_layout = Bitfield::new(bitfield_count,
+ bitfield_fields)
.codegen_fields(ctx, &mut fields, &mut methods);
+
+ struct_layout.saw_bitfield(bitfield_layout);
}
debug_assert!(current_bitfield_fields.is_empty());
@@ -1203,6 +1224,9 @@ impl CodeGenerator for CompInfo {
let field = StructFieldBuilder::named("bindgen_union_field")
.pub_()
.build_ty(ty);
+
+ struct_layout.saw_union(layout);
+
fields.push(field);
}
@@ -1227,6 +1251,16 @@ impl CodeGenerator for CompInfo {
warn!("Opaque type without layout! Expect dragons!");
}
}
+ } else if !is_union && !self.is_unsized(ctx) {
+ if let Some(padding_field) =
+ layout.and_then(|layout| struct_layout.pad_struct(layout)) {
+ fields.push(padding_field);
+ }
+
+ if let Some(align_field) =
+ layout.and_then(|layout| struct_layout.align_struct(layout)) {
+ fields.push(align_field);
+ }
}
// C requires every struct to be addressable, so what C compilers do is
@@ -1296,7 +1330,7 @@ impl CodeGenerator for CompInfo {
canonical_name);
}
- if applicable_template_args.is_empty() && !self.found_unknown_attr() {
+ if applicable_template_args.is_empty() {
for var in self.inner_vars() {
ctx.resolve_item(*var)
.codegen(ctx, result, whitelisted_items, &());
@@ -1313,11 +1347,57 @@ impl CodeGenerator for CompInfo {
::$prefix::mem::align_of::<$ident>());
let size = layout.size;
let align = layout.align;
+
+ let check_struct_align = if align > mem::size_of::<*mut ()>() {
+ // FIXME when [RFC 1358](https://github.com/rust-lang/rust/issues/33626) ready
+ None
+ } else {
+ quote_item!(ctx.ext_cx(),
+ assert_eq!($align_of_expr, $align);
+ )
+ };
+
+ // FIXME when [issue #465](https://github.com/servo/rust-bindgen/issues/465) ready
+ let too_many_base_vtables = self.base_members()
+ .iter()
+ .filter(|base| ctx.resolve_type(base.ty).has_vtable(ctx))
+ .count() >
+ 1;
+
+ let should_skip_field_offset_checks = item.is_opaque(ctx) ||
+ too_many_base_vtables;
+
+ let check_field_offset = if should_skip_field_offset_checks {
+ None
+ } else {
+ let type_name = ctx.rust_ident(&canonical_name);
+
+ let asserts = self.fields()
+ .iter()
+ .filter(|field| field.bitfield().is_none())
+ .flat_map(|field| {
+ field.name().and_then(|name| {
+ field.offset().and_then(|offset| {
+ let field_offset = offset / 8;
+ let field_name = ctx.rust_ident(name);
+
+ quote_item!(ctx.ext_cx(),
+ assert_eq!(unsafe { &(*(0 as *const $type_name)).$field_name as *const _ as usize }, $field_offset);
+ )
+ })
+ })
+ }).collect::<Vec<P<ast::Item>>>();
+
+ Some(asserts)
+ };
+
let item = quote_item!(ctx.ext_cx(),
#[test]
fn $fn_name() {
assert_eq!($size_of_expr, $size);
- assert_eq!($align_of_expr, $align);
+
+ $check_struct_align
+ $check_field_offset
})
.unwrap();
result.push(item);
@@ -2278,22 +2358,20 @@ impl CodeGenerator for ObjCInterface {
// Collect the actual used argument names
let arg_names: Vec<_> = fn_args.iter()
- .map(|ref arg| {
- match arg.pat.node {
- ast::PatKind::Ident(_, ref spanning, _) => {
- spanning.node.name.as_str().to_string()
- }
- _ => {
- panic!("odd argument!");
- }
+ .map(|ref arg| match arg.pat.node {
+ ast::PatKind::Ident(_, ref spanning, _) => {
+ spanning.node.name.as_str().to_string()
+ }
+ _ => {
+ panic!("odd argument!");
}
})
.collect();
let methods_and_args =
ctx.rust_ident(&method.format_method_call(&arg_names));
- let body =
- quote_stmt!(ctx.ext_cx(), msg_send![self, $methods_and_args])
+ let body = quote_stmt!(ctx.ext_cx(),
+ msg_send![self, $methods_and_args])
.unwrap();
let block = ast::Block {
stmts: vec![body],
@@ -2729,5 +2807,4 @@ mod utils {
}
}).collect::<Vec<_>>()
}
-
}
diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs
new file mode 100644
index 00000000..3006a31a
--- /dev/null
+++ b/src/codegen/struct_layout.rs
@@ -0,0 +1,193 @@
+//! Helpers for code generation that need struct layout
+
+use super::helpers::BlobTyBuilder;
+
+use aster::struct_field::StructFieldBuilder;
+
+use ir::comp::CompInfo;
+use ir::context::BindgenContext;
+use ir::layout::Layout;
+use ir::ty::Type;
+use std::cmp;
+use std::mem;
+
+use syntax::ast;
+
+/// Trace the layout of struct.
+pub struct StructLayoutTracker<'a, 'ctx: 'a> {
+ ctx: &'a BindgenContext<'ctx>,
+ comp: &'a CompInfo,
+ latest_offset: usize,
+ padding_count: usize,
+ latest_field_layout: Option<Layout>,
+ max_field_align: usize,
+}
+
+impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
+ pub fn new(ctx: &'a BindgenContext<'ctx>, comp: &'a CompInfo) -> Self {
+ StructLayoutTracker {
+ ctx: ctx,
+ comp: comp,
+ latest_offset: 0,
+ padding_count: 0,
+ latest_field_layout: None,
+ max_field_align: 0,
+ }
+ }
+
+ pub fn saw_vtable(&mut self) {
+ let ptr_size = mem::size_of::<*mut ()>();
+ self.latest_offset += ptr_size;
+ self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size));
+ self.max_field_align = ptr_size;
+ }
+
+ pub fn saw_base(&mut self, base_ty: &Type) {
+ self.align_to_latest_field();
+
+ if let Some(layout) = base_ty.layout(self.ctx) {
+ self.latest_offset += self.padding_bytes(layout) + layout.size;
+ self.latest_field_layout = Some(layout);
+ self.max_field_align = cmp::max(self.max_field_align, layout.align);
+ }
+ }
+
+ pub fn saw_bitfield(&mut self, layout: Layout) {
+ self.align_to_latest_field();
+
+ self.latest_offset += self.padding_bytes(layout) + layout.size;
+ self.latest_field_layout = Some(layout);
+ self.max_field_align = cmp::max(self.max_field_align, layout.align);
+ }
+
+ pub fn saw_union(&mut self, layout: Layout) {
+ self.align_to_latest_field();
+
+ self.latest_offset += self.padding_bytes(layout) + layout.size;
+ self.latest_field_layout = Some(layout);
+ self.max_field_align = cmp::max(self.max_field_align, layout.align);
+ }
+
+ pub fn pad_field(&mut self,
+ field_name: &str,
+ field_ty: &Type,
+ field_offset: Option<usize>)
+ -> Option<ast::StructField> {
+ field_ty.layout(self.ctx).and_then(|field_layout| {
+ self.align_to_latest_field();
+
+ let padding_layout = if self.comp.packed() {
+ None
+ } else {
+ let calculated_layout = field_ty.as_comp()
+ .and_then(|comp| comp.calc_layout(self.ctx))
+ .unwrap_or(field_layout);
+
+ let align = cmp::min(calculated_layout.align, mem::size_of::<*mut ()>());
+
+ let (padding_bytes, need_padding) = match field_offset {
+ Some(offset) if offset / 8 > self.latest_offset => {
+ (offset / 8 - self.latest_offset, true)
+ }
+ _ => {
+ (self.padding_bytes(field_layout), (self.latest_offset % field_layout.align) != 0)
+ }
+ };
+
+ self.latest_offset += padding_bytes;
+
+ debug!("align field {} to {}/{} with {} padding bytes {:?}, calculated {:?}",
+ field_name,
+ self.latest_offset,
+ field_offset.unwrap_or(0) / 8,
+ padding_bytes,
+ field_layout,
+ calculated_layout);
+
+ if need_padding &&
+ (padding_bytes > calculated_layout.align ||
+ field_layout.align > mem::size_of::<*mut ()>()) {
+ Some(Layout::new(padding_bytes, align))
+ } else {
+ None
+ }
+ };
+
+ self.latest_offset += field_ty.calc_size(self.ctx).unwrap_or(field_layout.size);
+
+ self.latest_field_layout = Some(field_layout);
+ self.max_field_align = cmp::max(self.max_field_align, field_layout.align);
+
+ padding_layout.map(|layout| self.padding_field(layout))
+ })
+ }
+
+ pub fn pad_struct(&mut self, layout: Layout) -> Option<ast::StructField> {
+ if layout.size < self.latest_offset {
+ warn!("calculate struct layout incorrect, too more {} bytes",
+ self.latest_offset - layout.size);
+
+ None
+ } else {
+ let padding_bytes = layout.size - self.latest_offset;
+ let struct_align = cmp::min(layout.align,
+ mem::size_of::<*mut ()>());
+
+ if padding_bytes > struct_align ||
+ (layout.align > mem::size_of::<*mut ()>() && padding_bytes > 0) {
+ let padding_align = if self.comp.packed() {
+ 1
+ } else {
+ cmp::min(1 << padding_bytes.trailing_zeros(),
+ mem::size_of::<*mut ()>())
+ };
+
+ Some(self.padding_field(Layout::new(padding_bytes, padding_align)))
+ } else {
+ None
+ }
+ }
+ }
+
+ pub fn align_struct(&self, layout: Layout) -> Option<ast::StructField> {
+ if self.max_field_align < layout.align &&
+ layout.align <= mem::size_of::<*mut ()>() {
+ let ty = BlobTyBuilder::new(Layout::new(0, layout.align)).build();
+
+ Some(StructFieldBuilder::named("__bindgen_align")
+ .pub_()
+ .build_ty(ty))
+ } else {
+ None
+ }
+ }
+
+ fn padding_bytes(&self, layout: Layout) -> usize {
+ if self.latest_offset % layout.align == 0 {
+ 0
+ } else {
+ layout.align - (self.latest_offset % layout.align)
+ }
+ }
+
+ fn padding_field(&mut self, layout: Layout) -> ast::StructField {
+ let ty = BlobTyBuilder::new(layout).build();
+ let padding_count = self.padding_count;
+
+ self.padding_count += 1;
+
+ let padding_field_name = format!("__bindgen_padding_{}", padding_count);
+
+ self.max_field_align = cmp::max(self.max_field_align, layout.align);
+
+ StructFieldBuilder::named(padding_field_name).pub_().build_ty(ty)
+ }
+
+ fn align_to_latest_field(&mut self) {
+ if self.comp.packed() {
+ // skip to align field when packed
+ } else if let Some(layout) = self.latest_field_layout {
+ self.latest_offset += self.padding_bytes(layout);
+ }
+ }
+}