blob: f8a88bc24b19672650b4620676ae30c79bd7ed22 [file] [log] [blame]
//! 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)
}
_ if field_layout.align != 0 => {
(self.padding_bytes(field_layout), (self.latest_offset % field_layout.align) != 0)
}
_ => {
(0, false)
}
};
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 layout.align == 0 {
warn!("try to padding bytes without layout");
0
} else 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);
}
}
}