diff options
author | Christian Poveda <christian.poveda@ferrous-systems.com> | 2022-09-23 21:36:14 -0500 |
---|---|---|
committer | Christian Poveda <christian.poveda@ferrous-systems.com> | 2022-10-04 20:47:17 -0500 |
commit | 0296f9e86c7756e718b6b82836ce1e09b5f8d08a (patch) | |
tree | b5954c6680b243c0b1671a80ea973ef90877e462 /src/codegen/struct_layout.rs | |
parent | a900f8f863d1313ad76603234aaeea22bb9ba7b3 (diff) |
split the repo into a workspace
remove `clap` dependency :tada:
update the book installation instructions
Diffstat (limited to 'src/codegen/struct_layout.rs')
-rw-r--r-- | src/codegen/struct_layout.rs | 444 |
1 files changed, 0 insertions, 444 deletions
diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs deleted file mode 100644 index ddac1b0a..00000000 --- a/src/codegen/struct_layout.rs +++ /dev/null @@ -1,444 +0,0 @@ -//! Helpers for code generation that need struct layout - -use super::helpers; - -use crate::ir::comp::CompInfo; -use crate::ir::context::BindgenContext; -use crate::ir::layout::Layout; -use crate::ir::ty::{Type, TypeKind}; -use proc_macro2::{self, Ident, Span}; -use std::cmp; - -const MAX_GUARANTEED_ALIGN: usize = 8; - -/// Trace the layout of struct. -#[derive(Debug)] -pub struct StructLayoutTracker<'a> { - name: &'a str, - ctx: &'a BindgenContext, - comp: &'a CompInfo, - is_packed: bool, - known_type_layout: Option<Layout>, - is_rust_union: bool, - can_copy_union_fields: bool, - latest_offset: usize, - padding_count: usize, - latest_field_layout: Option<Layout>, - max_field_align: usize, - last_field_was_bitfield: bool, -} - -/// Returns a size aligned to a given value. -pub fn align_to(size: usize, align: usize) -> usize { - if align == 0 { - return size; - } - - let rem = size % align; - if rem == 0 { - return size; - } - - size + align - rem -} - -/// Returns the lower power of two byte count that can hold at most n bits. -pub fn bytes_from_bits_pow2(mut n: usize) -> usize { - if n == 0 { - return 0; - } - - if n <= 8 { - return 1; - } - - if !n.is_power_of_two() { - n = n.next_power_of_two(); - } - - n / 8 -} - -#[test] -fn test_align_to() { - assert_eq!(align_to(1, 1), 1); - assert_eq!(align_to(1, 2), 2); - assert_eq!(align_to(1, 4), 4); - assert_eq!(align_to(5, 1), 5); - assert_eq!(align_to(17, 4), 20); -} - -#[test] -fn test_bytes_from_bits_pow2() { - assert_eq!(bytes_from_bits_pow2(0), 0); - for i in 1..9 { - assert_eq!(bytes_from_bits_pow2(i), 1); - } - for i in 9..17 { - assert_eq!(bytes_from_bits_pow2(i), 2); - } - for i in 17..33 { - assert_eq!(bytes_from_bits_pow2(i), 4); - } -} - -impl<'a> StructLayoutTracker<'a> { - pub fn new( - ctx: &'a BindgenContext, - comp: &'a CompInfo, - ty: &'a Type, - name: &'a str, - ) -> Self { - let known_type_layout = ty.layout(ctx); - let is_packed = comp.is_packed(ctx, known_type_layout.as_ref()); - let (is_rust_union, can_copy_union_fields) = - comp.is_rust_union(ctx, known_type_layout.as_ref(), name); - StructLayoutTracker { - name, - ctx, - comp, - is_packed, - known_type_layout, - is_rust_union, - can_copy_union_fields, - latest_offset: 0, - padding_count: 0, - latest_field_layout: None, - max_field_align: 0, - last_field_was_bitfield: false, - } - } - - pub fn can_copy_union_fields(&self) -> bool { - self.can_copy_union_fields - } - - pub fn is_rust_union(&self) -> bool { - self.is_rust_union - } - - pub fn saw_vtable(&mut self) { - debug!("saw vtable for {}", self.name); - - let ptr_size = self.ctx.target_pointer_size(); - 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) { - debug!("saw base for {}", self.name); - if let Some(layout) = base_ty.layout(self.ctx) { - self.align_to_latest_field(layout); - - 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_unit(&mut self, layout: Layout) { - debug!("saw bitfield unit for {}: {:?}", self.name, layout); - - self.align_to_latest_field(layout); - - self.latest_offset += layout.size; - - debug!( - "Offset: <bitfield>: {} -> {}", - self.latest_offset - layout.size, - self.latest_offset - ); - - self.latest_field_layout = Some(layout); - self.last_field_was_bitfield = true; - // NB: We intentionally don't update the max_field_align here, since our - // bitfields code doesn't necessarily guarantee it, so we need to - // actually generate the dummy alignment. - } - - /// Returns a padding field if necessary for a given new field _before_ - /// adding that field. - pub fn saw_field( - &mut self, - field_name: &str, - field_ty: &Type, - field_offset: Option<usize>, - ) -> Option<proc_macro2::TokenStream> { - let mut field_layout = field_ty.layout(self.ctx)?; - - if let TypeKind::Array(inner, len) = - *field_ty.canonical_type(self.ctx).kind() - { - // FIXME(emilio): As an _ultra_ hack, we correct the layout returned - // by arrays of structs that have a bigger alignment than what we - // can support. - // - // This means that the structs in the array are super-unsafe to - // access, since they won't be properly aligned, but there's not too - // much we can do about it. - if let Some(layout) = self.ctx.resolve_type(inner).layout(self.ctx) - { - if layout.align > MAX_GUARANTEED_ALIGN { - field_layout.size = - align_to(layout.size, layout.align) * len; - field_layout.align = MAX_GUARANTEED_ALIGN; - } - } - } - self.saw_field_with_layout(field_name, field_layout, field_offset) - } - - pub fn saw_field_with_layout( - &mut self, - field_name: &str, - field_layout: Layout, - field_offset: Option<usize>, - ) -> Option<proc_macro2::TokenStream> { - let will_merge_with_bitfield = self.align_to_latest_field(field_layout); - - let is_union = self.comp.is_union(); - let padding_bytes = match field_offset { - Some(offset) if offset / 8 > self.latest_offset => { - offset / 8 - self.latest_offset - } - _ => { - if will_merge_with_bitfield || - field_layout.align == 0 || - is_union - { - 0 - } else if !self.is_packed { - self.padding_bytes(field_layout) - } else if let Some(l) = self.known_type_layout { - self.padding_bytes(l) - } else { - 0 - } - } - }; - - self.latest_offset += padding_bytes; - - let padding_layout = if self.is_packed || is_union { - None - } else { - let force_padding = self.ctx.options().force_explicit_padding; - - // Otherwise the padding is useless. - let need_padding = force_padding || - padding_bytes >= field_layout.align || - field_layout.align > MAX_GUARANTEED_ALIGN; - - debug!( - "Offset: <padding>: {} -> {}", - self.latest_offset - padding_bytes, - self.latest_offset - ); - - debug!( - "align field {} to {}/{} with {} padding bytes {:?}", - field_name, - self.latest_offset, - field_offset.unwrap_or(0) / 8, - padding_bytes, - field_layout - ); - - let padding_align = if force_padding { - 1 - } else { - cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN) - }; - - if need_padding && padding_bytes != 0 { - Some(Layout::new(padding_bytes, padding_align)) - } else { - None - } - }; - - self.latest_offset += field_layout.size; - self.latest_field_layout = Some(field_layout); - self.max_field_align = - cmp::max(self.max_field_align, field_layout.align); - self.last_field_was_bitfield = false; - - debug!( - "Offset: {}: {} -> {}", - field_name, - self.latest_offset - field_layout.size, - self.latest_offset - ); - - padding_layout.map(|layout| self.padding_field(layout)) - } - - pub fn add_tail_padding( - &mut self, - comp_name: &str, - comp_layout: Layout, - ) -> Option<proc_macro2::TokenStream> { - // Only emit an padding field at the end of a struct if the - // user configures explicit padding. - if !self.ctx.options().force_explicit_padding { - return None; - } - - // Padding doesn't make sense for rust unions. - if self.is_rust_union { - return None; - } - - if self.latest_offset == comp_layout.size { - // This struct does not contain tail padding. - return None; - } - - trace!( - "need a tail padding field for {}: offset {} -> size {}", - comp_name, - self.latest_offset, - comp_layout.size - ); - let size = comp_layout.size - self.latest_offset; - Some(self.padding_field(Layout::new(size, 0))) - } - - pub fn pad_struct( - &mut self, - layout: Layout, - ) -> Option<proc_macro2::TokenStream> { - debug!( - "pad_struct:\n\tself = {:#?}\n\tlayout = {:#?}", - self, layout - ); - - if layout.size < self.latest_offset { - warn!( - "Calculated wrong layout for {}, too more {} bytes", - self.name, - self.latest_offset - layout.size - ); - return None; - } - - let padding_bytes = layout.size - self.latest_offset; - if padding_bytes == 0 { - return None; - } - - let repr_align = self.ctx.options().rust_features().repr_align; - - // We always pad to get to the correct size if the struct is one of - // those we can't align properly. - // - // Note that if the last field we saw was a bitfield, we may need to pad - // regardless, because bitfields don't respect alignment as strictly as - // other fields. - if padding_bytes >= layout.align || - (self.last_field_was_bitfield && - padding_bytes >= self.latest_field_layout.unwrap().align) || - (!repr_align && layout.align > MAX_GUARANTEED_ALIGN) - { - let layout = if self.is_packed { - Layout::new(padding_bytes, 1) - } else if self.last_field_was_bitfield || - layout.align > MAX_GUARANTEED_ALIGN - { - // We've already given up on alignment here. - Layout::for_size(self.ctx, padding_bytes) - } else { - Layout::new(padding_bytes, layout.align) - }; - - debug!("pad bytes to struct {}, {:?}", self.name, layout); - - Some(self.padding_field(layout)) - } else { - None - } - } - - pub fn requires_explicit_align(&self, layout: Layout) -> bool { - let repr_align = self.ctx.options().rust_features().repr_align; - - // Always force explicit repr(align) for stuff more than 16-byte aligned - // to work-around https://github.com/rust-lang/rust/issues/54341. - // - // Worst-case this just generates redundant alignment attributes. - if repr_align && self.max_field_align >= 16 { - return true; - } - - if self.max_field_align >= layout.align { - return false; - } - - // We can only generate up-to a 8-bytes of alignment unless we support - // repr(align). - repr_align || layout.align <= MAX_GUARANTEED_ALIGN - } - - fn padding_bytes(&self, layout: Layout) -> usize { - align_to(self.latest_offset, layout.align) - self.latest_offset - } - - fn padding_field(&mut self, layout: Layout) -> proc_macro2::TokenStream { - let ty = helpers::blob(self.ctx, layout); - let padding_count = self.padding_count; - - self.padding_count += 1; - - let padding_field_name = Ident::new( - &format!("__bindgen_padding_{}", padding_count), - Span::call_site(), - ); - - self.max_field_align = cmp::max(self.max_field_align, layout.align); - - quote! { - pub #padding_field_name : #ty , - } - } - - /// Returns whether the new field is known to merge with a bitfield. - /// - /// This is just to avoid doing the same check also in pad_field. - fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool { - if self.is_packed { - // Skip to align fields when packed. - return false; - } - - let layout = match self.latest_field_layout { - Some(l) => l, - None => return false, - }; - - // If it was, we may or may not need to align, depending on what the - // current field alignment and the bitfield size and alignment are. - debug!( - "align_to_bitfield? {}: {:?} {:?}", - self.last_field_was_bitfield, layout, new_field_layout - ); - - // Avoid divide-by-zero errors if align is 0. - let align = cmp::max(1, layout.align); - - if self.last_field_was_bitfield && - new_field_layout.align <= layout.size % align && - new_field_layout.size <= layout.size % align - { - // The new field will be coalesced into some of the remaining bits. - // - // FIXME(emilio): I think this may not catch everything? - debug!("Will merge with bitfield"); - return true; - } - - // Else, just align the obvious way. - self.latest_offset += self.padding_bytes(layout); - false - } -} |