use crate::builder::common::{
ignore_non_zero_sized_bitfield_type_alignment, ignore_zero_sized_bitfield_type_alignmont,
min_zero_width_bitfield_alignment, unnamed_field_affects_record_alignment,
};
use crate::builder::sysv_like::{Dialect, RecordLayoutBuilder};
use crate::layout::{FieldLayout, RecordField, RecordKind, Type, TypeLayout};
use crate::result::{err, ErrorType, Result};
use crate::target::{system_compiler, Compiler, Target};
use crate::util::{
align_to, annotation_alignment, is_attr_packed, size_add, MaxAssign, MinAssign, MinExt,
BITS_PER_BYTE,
};
pub(crate) fn compute_layout(target: Target, ty: &Type<()>) -> Result<Type<TypeLayout>> {
super::compute_layout(target, ty, Dialect::Sysv)
}
pub(super) fn layout_fields(
rlb: &mut RecordLayoutBuilder,
fields: &[RecordField<()>],
) -> Result<()> {
for f in fields {
layout_field(rlb, f)?;
}
Ok(())
}
fn layout_field(rlb: &mut RecordLayoutBuilder, field: &RecordField<()>) -> Result<()> {
let ty = super::compute_layout(rlb.target, &field.ty, Dialect::Sysv)?;
let layout = match field.bit_width {
Some(size_bits) => layout_bit_field(
rlb,
ty.layout.size_bits,
ty.layout.field_alignment_bits,
field,
size_bits,
),
None => layout_regular_field(rlb, ty.layout, field),
}?;
rlb.record_fields.push(RecordField {
layout,
annotations: field.annotations.clone(),
named: field.named,
bit_width: field.bit_width,
ty,
});
Ok(())
}
fn layout_bit_field(
rlb: &mut RecordLayoutBuilder,
ty_size_bits: u64,
mut ty_field_alignment_bits: u64,
field: &RecordField<()>,
width: u64,
) -> Result<Option<FieldLayout>> {
if width > 0 {
if width > ty_size_bits {
return Err(err(ErrorType::OversizedBitfield));
}
if ignore_non_zero_sized_bitfield_type_alignment(rlb.target) {
ty_field_alignment_bits = 1;
}
} else {
if ignore_zero_sized_bitfield_type_alignmont(rlb.target) {
ty_field_alignment_bits = 1;
}
ty_field_alignment_bits.assign_max(min_zero_width_bitfield_alignment(rlb.target));
}
let attr_packed = rlb.attr_packed || is_attr_packed(&field.annotations);
let has_packing_annotations = attr_packed || rlb.max_field_alignment_bits.is_some();
let annotation_alignment = annotation_alignment(rlb.target, &field.annotations).unwrap_or(1);
let first_unused_bit = match rlb.kind {
RecordKind::Union => 0,
RecordKind::Struct => rlb.size_bits,
};
let mut field_alignment_bits = 1;
if width == 0 {
field_alignment_bits = ty_field_alignment_bits.max(annotation_alignment);
} else if system_compiler(rlb.target) == Compiler::Gcc {
field_alignment_bits = annotation_alignment.min2(rlb.max_field_alignment_bits);
if !has_packing_annotations {
let start_bit = align_to(first_unused_bit, field_alignment_bits)?;
let field_crosses_storage_boundary =
start_bit % ty_field_alignment_bits + width > ty_size_bits;
if ty_field_alignment_bits > ty_size_bits || field_crosses_storage_boundary {
field_alignment_bits.assign_max(ty_field_alignment_bits);
}
}
} else {
assert_eq!(system_compiler(rlb.target), Compiler::Clang);
if annotation_alignment <= rlb.max_field_alignment_bits.unwrap_or(u64::MAX) {
field_alignment_bits.assign_max(annotation_alignment);
}
if !has_packing_annotations {
let field_crosses_storage_boundary =
first_unused_bit % ty_field_alignment_bits + width > ty_size_bits;
if field_crosses_storage_boundary {
field_alignment_bits.assign_max(ty_field_alignment_bits);
}
}
}
let offset_bits = align_to(first_unused_bit, field_alignment_bits)?;
rlb.size_bits.assign_max(size_add(offset_bits, width)?);
if field.named || unnamed_field_affects_record_alignment(rlb.target) {
let inherited_alignment_bits;
if width == 0 {
inherited_alignment_bits = ty_field_alignment_bits.max(annotation_alignment);
} else if let Some(max_field_alignment_bits) = rlb.max_field_alignment_bits {
inherited_alignment_bits = ty_field_alignment_bits
.max(annotation_alignment)
.min(max_field_alignment_bits);
} else if attr_packed {
inherited_alignment_bits = annotation_alignment;
} else {
inherited_alignment_bits = ty_field_alignment_bits.max(annotation_alignment);
}
rlb.alignment_bits.assign_max(inherited_alignment_bits);
}
match field.named {
true => Ok(Some(FieldLayout {
offset_bits,
size_bits: width,
})),
false => Ok(None),
}
}
fn layout_regular_field(
rlb: &mut RecordLayoutBuilder,
type_layout: TypeLayout,
field: &RecordField<()>,
) -> Result<Option<FieldLayout>> {
let mut field_alignment_bits = type_layout.field_alignment_bits;
if rlb.attr_packed || is_attr_packed(&field.annotations) {
field_alignment_bits = BITS_PER_BYTE;
}
field_alignment_bits.assign_max(annotation_alignment(rlb.target, &field.annotations));
field_alignment_bits.assign_min(rlb.max_field_alignment_bits);
let offset_bits = match rlb.kind {
RecordKind::Struct => align_to(rlb.size_bits, field_alignment_bits)?,
RecordKind::Union => 0,
};
let size_bits = type_layout.size_bits;
rlb.size_bits.assign_max(size_add(offset_bits, size_bits)?);
rlb.alignment_bits.assign_max(field_alignment_bits);
Ok(Some(FieldLayout {
offset_bits,
size_bits,
}))
}