repc_impl/builder/sysv_like/
mingw.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2use crate::builder::sysv_like::{Dialect, RecordLayoutBuilder};
3use crate::layout::{FieldLayout, RecordField, RecordKind, Type, TypeLayout};
4use crate::result::{err, ErrorType, Result};
5use crate::target::Target;
6use crate::util::{
7    align_to, annotation_alignment, is_attr_packed, size_add, MaxAssign, MinAssign, BITS_PER_BYTE,
8};
9
10pub(crate) fn compute_layout(target: Target, ty: &Type<()>) -> Result<Type<TypeLayout>> {
11    super::compute_layout(target, ty, Dialect::Mingw)
12}
13
14pub(super) struct OngoingBitfield {
15    // The size of the storage unit of the previous bitfield. This is the size of the underlying
16    // type, e.g., `int`.
17    ty_size_bits: u64,
18    // The number of bits that remain unused in the storage unit. This can be 0 if all of the bits
19    // have been used.
20    unused_size_bits: u64,
21}
22
23pub(super) fn layout_fields(
24    rlb: &mut RecordLayoutBuilder,
25    fields: &[RecordField<()>],
26) -> Result<()> {
27    for f in fields {
28        layout_field(rlb, f)?;
29    }
30    Ok(())
31}
32
33fn layout_field(rlb: &mut RecordLayoutBuilder, field: &RecordField<()>) -> Result<()> {
34    let ty = compute_layout(rlb.target, &field.ty)?;
35    let annotation_alignment_bits =
36        annotation_alignment(rlb.target, &field.annotations).unwrap_or(BITS_PER_BYTE);
37    // __attribute__((packed)) on the record is identical to __attribute__((packed)) on each
38    // field. See test case 0067.
39    let is_attr_packed = rlb.attr_packed || is_attr_packed(&field.annotations);
40    // The alignment of the field is calculated in the usual way except that the alignment of
41    // the underlying type is ignored in three cases
42    // - the field is packed
43    // - the field is a bit-field and the previous field was a non-zero-sized bit-field with the same type size
44    // - the field is a zero-sized bit-field and the previous field was not a non-zero-sized bit-field
45    // See test case 0068.
46    let ignore_type_alignment = match (is_attr_packed, field.bit_width, &rlb.ongoing_bitfield) {
47        (true, _, _) => true,
48        (_, Some(_), Some(o)) if o.ty_size_bits == ty.layout.size_bits => true,
49        (_, Some(0), None) => true,
50        _ => false,
51    };
52    let mut field_alignment_bits = ty.layout.field_alignment_bits;
53    if ignore_type_alignment {
54        field_alignment_bits = BITS_PER_BYTE;
55    }
56    field_alignment_bits.assign_max(annotation_alignment_bits);
57    field_alignment_bits.assign_min(rlb.max_field_alignment_bits);
58    // The field affects the record alignment in one of three cases
59    // - the field is a regular field
60    // - the field is a zero-width bit-field following a non-zero-width bit-field
61    // - the field is a non-zero-width bit-field and not packed.
62    // See test case 0069.
63    let update_record_alignment = field.bit_width == None
64        || field.bit_width == Some(0) && rlb.ongoing_bitfield.is_some()
65        || field.bit_width != Some(0) && !is_attr_packed;
66    // If a field affects the alignment of a record, the alignment is calculated in the
67    // usual way except that __attribute__((packed)) is ignored on a zero-width bit-field.
68    // See test case 0068.
69    if update_record_alignment {
70        let mut ty_alignment_bits = ty.layout.field_alignment_bits;
71        if is_attr_packed && field.bit_width != Some(0) {
72            ty_alignment_bits = BITS_PER_BYTE;
73        }
74        ty_alignment_bits.assign_max(annotation_alignment_bits);
75        ty_alignment_bits.assign_min(rlb.max_field_alignment_bits);
76        rlb.alignment_bits.assign_max(ty_alignment_bits);
77    }
78    // NOTE: ty_alignment_bits and field_alignment_bits are different in the following case:
79    // Y = { size: 64, alignment: 64 }struct {
80    //     { offset: 0, size: 1 }c { size: 8, alignment: 8 }char:1,
81    //     @attr_packed _ { size: 64, alignment: 64 }long long:0,
82    //     { offset: 8, size: 8 }d { size: 8, alignment: 8 }char,
83    // }
84
85    // These functions return `None` if and only if the field is unnamed.
86    let layout = match field.bit_width {
87        Some(width) => layout_bit_field(
88            rlb,
89            ty.layout.size_bits,
90            field_alignment_bits,
91            field.named,
92            width,
93        ),
94        None => layout_regular_field(rlb, ty.layout.size_bits, field_alignment_bits),
95    }?;
96    rlb.record_fields.push(RecordField {
97        layout,
98        annotations: field.annotations.clone(),
99        named: field.named,
100        bit_width: field.bit_width,
101        ty,
102    });
103    Ok(())
104}
105
106fn layout_bit_field(
107    rlb: &mut RecordLayoutBuilder,
108    ty_size_bits: u64,
109    field_alignment_bits: u64,
110    named: bool,
111    width: u64,
112) -> Result<Option<FieldLayout>> {
113    macro_rules! ok {
114        ($offset:expr) => {
115            Ok(match named {
116                true => Some(FieldLayout {
117                    offset_bits: $offset,
118                    size_bits: width,
119                }),
120                false => None,
121            })
122        };
123    }
124    if width > ty_size_bits {
125        return Err(err(ErrorType::OversizedBitfield));
126    }
127    // In a union, the size of the underlying type does not affect the size of the union.
128    // See test case 0070.
129    if rlb.kind == RecordKind::Union {
130        rlb.size_bits.assign_max(width);
131        return ok!(0);
132    }
133    match width {
134        0 => rlb.ongoing_bitfield = None,
135        _ => {
136            // If there is an ongoing bit-field in a struct whose underlying type has the same size and
137            // if there is enough space left to place this bit-field, then this bit-field is placed in
138            // the ongoing bit-field and the size of the struct is not affected by this
139            // bit-field. See test case 0037.
140            if let Some(ref mut p) = &mut rlb.ongoing_bitfield {
141                if p.ty_size_bits == ty_size_bits && p.unused_size_bits >= width {
142                    let offset_bits = rlb.size_bits - p.unused_size_bits;
143                    p.unused_size_bits -= width;
144                    return ok!(offset_bits);
145                }
146            }
147            // Otherwise this field is part of a new ongoing bit-field.
148            rlb.ongoing_bitfield = Some(OngoingBitfield {
149                ty_size_bits,
150                unused_size_bits: ty_size_bits - width,
151            });
152        }
153    }
154    let offset_bits = align_to(rlb.size_bits, field_alignment_bits)?;
155    rlb.size_bits = match width {
156        // A zero-width bitfield only increases the size of the struct to the
157        // offset a non-zero-width bitfield with the same alignment would
158        // start. See test case 0039.
159        0 => offset_bits,
160        // A non-zero-width bitfield always increases the size by the full
161        // size of the underlying type. Even if we are in a packed context.
162        // See test cases 0040 and 0071.
163        _ => size_add(offset_bits, ty_size_bits)?,
164    };
165    ok!(offset_bits)
166}
167
168fn layout_regular_field(
169    rlb: &mut RecordLayoutBuilder,
170    ty_size_bits: u64,
171    field_alignment_bits: u64,
172) -> Result<Option<FieldLayout>> {
173    rlb.ongoing_bitfield = None;
174    let offset_bits = match rlb.kind {
175        // A struct field starts at the next offset in the struct that is properly
176        // aligned with respect to the start of the struct. See test case 0033.
177        RecordKind::Struct => align_to(rlb.size_bits, field_alignment_bits)?,
178        // A union field always starts at offset 0.
179        RecordKind::Union => 0,
180    };
181    // Set the size of the record to the maximum of the current size and the end of
182    // the field. See test case 0034.
183    rlb.size_bits
184        .assign_max(size_add(offset_bits, ty_size_bits)?);
185    Ok(Some(FieldLayout {
186        offset_bits,
187        size_bits: ty_size_bits,
188    }))
189}