summaryrefslogtreecommitdiff
path: root/src/codegen/struct_layout.rs
diff options
context:
space:
mode:
authorChristian Poveda <christian.poveda@ferrous-systems.com>2022-09-23 21:36:14 -0500
committerChristian Poveda <christian.poveda@ferrous-systems.com>2022-10-04 20:47:17 -0500
commit0296f9e86c7756e718b6b82836ce1e09b5f8d08a (patch)
treeb5954c6680b243c0b1671a80ea973ef90877e462 /src/codegen/struct_layout.rs
parenta900f8f863d1313ad76603234aaeea22bb9ba7b3 (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.rs444
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
- }
-}