//! 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 crate::FieldVisibilityKind;
use proc_macro2::{
self, Ident, Span};
use std::cmp;
const MAX_GUARANTEED_ALIGN: usize =
8;
/// Trace the layout of struct.
#[derive(Debug)]
pub(
crate)
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,
visibility: FieldVisibilityKind,
}
/// Returns a size aligned to a given value.
pub(
crate)
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(
crate)
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(
crate)
fn new(
ctx: &
'a BindgenContext,
comp: &
'a CompInfo,
ty: &
'a Type,
name: &
'a str,
visibility: FieldVisibilityKind,
is_packed: bool,
) ->
Self {
let known_type_layout = ty.layout(ctx);
let (is_rust_union, can_copy_union_fields) =
comp.is_rust_union(ctx, known_type_layout.as_ref(), name);
StructLayoutTracker {
name,
ctx,
comp,
visibility,
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(
crate)
fn can_copy_union_fields(&
self) -> bool {
self.can_copy_union_fields
}
pub(
crate)
fn is_rust_union(&
self) -> bool {
self.is_rust_union
}
pub(
crate)
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(
crate)
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(
crate)
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;
self.max_field_align = cmp::max(
self.max_field_align, layout.align);
}
/// Returns a padding field if necessary for a given new field _before_
/// adding that field.
pub(
crate)
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(
crate)
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(
mut l) =
self.known_type_layout {
if field_layout.align < l.align {
l.align = field_layout.align;
}
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(
crate)
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(
crate)
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(
crate)
fn requires_explicit_align(&
self, layout: Layout) -> b
ool {
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);
let vis = super::access_specifier(self.visibility);
quote! {
#vis #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
}
}