summaryrefslogtreecommitdiff
path: root/src/codegen/struct_layout.rs
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/struct_layout.rs
parent7fa654c699b61250787e53a300357f0c58c55b18 (diff)
add padding bytes to align strcture
Diffstat (limited to 'src/codegen/struct_layout.rs')
-rw-r--r--src/codegen/struct_layout.rs193
1 files changed, 193 insertions, 0 deletions
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);
+ }
+ }
+}