rucc_types/layout.rs
1//! How large a type is and what it has to be aligned to, computed from the target description.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.1 and `spec/18-package-layout.md`
4//! section 18.2, which is the rule that none of this may be a `#[cfg]`.
5//!
6//! Every number here comes out of [`TargetInfo`] rather than out of the host. That is not
7//! pedantry: `long` is four bytes on Windows and eight on Linux, `long double` is eight bytes
8//! on Apple and sixteen on SysV x86-64, and a cross compiler that asks its own platform gets
9//! both of them wrong. The widths were checked against GCC 13 on x86-64 Linux and against
10//! clang on AArch64 Darwin rather than recalled.
11//!
12//! [`integer_info`] is here for the same reason and answers a neighbouring question: not how
13//! large the object is but how wide the value in it is, which is not the same number for `bool`
14//! or for a `_BitInt` and is what folding a constant depends on.
15//!
16//! Records are the one thing not computed here. Their layout depends on their members, on
17//! bit-field packing and on attributes, so it is computed by whoever walks the members and
18//! recorded with [`Types::complete_record`](crate::Types::complete_record); this module reads
19//! it back.
20
21use rucc_base::float::Format;
22use rucc_target::TargetInfo;
23
24use crate::classify::bare;
25use crate::kind::{ArrayLen, FloatKind, IntKind, TypeKind};
26use crate::types::{TypeId, Types};
27
28/// The size and alignment of a complete object type.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30pub struct Layout {
31 /// The size in bytes, which is what `sizeof` answers.
32 pub size: u64,
33 /// The alignment in bytes, which is what `_Alignof` answers. Always a power of two.
34 pub align: u64,
35}
36
37impl Layout {
38 /// A layout with the given size and alignment.
39 #[must_use]
40 pub const fn new(size: u64, align: u64) -> Layout {
41 Layout { size, align }
42 }
43
44 /// A scalar that is as aligned as it is large, which every one on a 64-bit target is.
45 #[must_use]
46 const fn scalar(size: u64) -> Layout {
47 Layout { size, align: size }
48 }
49}
50
51/// Why a type has no layout.
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum LayoutError {
54 /// The type is incomplete: `void`, an array with no size, or a record or enumeration whose
55 /// definition has not been seen. GNU C gives `sizeof(void)` the value one, and that is a
56 /// dialect decision made where there is a warning to emit, not here.
57 Incomplete,
58 /// The type is a function type, which has no size at all. GNU C gives it the value one for
59 /// the same reason it does for `void`.
60 Function,
61 /// The type is complete and describes an object larger than the address space, which an
62 /// array declaration can ask for by multiplying two innocent looking numbers.
63 TooLarge,
64}
65
66impl std::fmt::Display for LayoutError {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 let text = match self {
69 LayoutError::Incomplete => "the type is incomplete",
70 LayoutError::Function => "a function type has no size",
71 LayoutError::TooLarge => "the type is larger than the address space",
72 };
73 f.write_str(text)
74 }
75}
76
77impl std::error::Error for LayoutError {}
78
79/// The size and alignment of `id` on `target`.
80///
81/// # Errors
82///
83/// [`LayoutError`] when the type has no layout, which is a normal answer rather than a bug:
84/// `sizeof` an incomplete type is a diagnostic, and the caller is the one holding the span.
85pub fn layout(types: &Types, id: TypeId, target: &TargetInfo) -> Result<Layout, LayoutError> {
86 // Sugar has whatever layout the type behind it has, and a typedef of an array of a typedef
87 // is common enough that resolving it once here beats resolving it at every arm below.
88 let id = types.canonical(id);
89 match types.kind(id) {
90 TypeKind::Void => Err(LayoutError::Incomplete),
91 TypeKind::Bool => Ok(Layout::scalar(1)),
92 TypeKind::Int(kind) => Ok(Layout::scalar(u64::from(int_width(kind, target) / 8))),
93 TypeKind::Float(kind) => Ok(Layout::scalar(u64::from(float_width(kind, target) / 8))),
94 TypeKind::Complex(kind) => {
95 // Two of the component, adjacent, with the component's own alignment rather than
96 // the pair's. `_Complex long double` on SysV x86-64 is thirty two bytes aligned to
97 // sixteen, which is what both GCC and clang report.
98 let part = Layout::scalar(u64::from(float_width(kind, target) / 8));
99 Ok(Layout::new(part.size * 2, part.align))
100 }
101 TypeKind::BitInt { width, .. } => Ok(bit_int_layout(width, target)),
102 TypeKind::Pointer(_) => Ok(Layout::scalar(u64::from(target.pointer_width / 8))),
103 TypeKind::Function(_) => Err(LayoutError::Function),
104 TypeKind::Atomic(inner) => {
105 let inner = layout(types, inner, target)?;
106 Ok(atomic_layout(inner))
107 }
108 TypeKind::Array { elem, len } => {
109 let ArrayLen::Fixed(count) = len else {
110 return Err(LayoutError::Incomplete);
111 };
112 let elem = layout(types, elem, target)?;
113 let size = elem.size.checked_mul(count).ok_or(LayoutError::TooLarge)?;
114 Ok(Layout::new(size, elem.align))
115 }
116 TypeKind::Vector { elem, len } => {
117 let elem = layout(types, elem, target)?;
118 let raw = elem.size.checked_mul(u64::from(len)).ok_or(LayoutError::TooLarge)?;
119 Ok(vector_layout(raw))
120 }
121 TypeKind::Record(record) => types.record_info(record).layout.ok_or(LayoutError::Incomplete),
122 TypeKind::Enum(id) => {
123 let underlying = types.enum_info(id).underlying.ok_or(LayoutError::Incomplete)?;
124 layout(types, underlying, target)
125 }
126 // Unreachable in practice: the id was canonicalised on the way in. Answering rather
127 // than panicking, because a wrong size is easier to find than a crash in a compiler.
128 TypeKind::Typedef { underlying, .. } => layout(types, underlying, target),
129 }
130}
131
132/// The width of a standard integer type in bits.
133#[must_use]
134pub fn int_width(kind: IntKind, target: &TargetInfo) -> u32 {
135 match kind {
136 IntKind::Char | IntKind::SChar | IntKind::UChar => 8,
137 IntKind::Short | IntKind::UShort => 16,
138 IntKind::Int | IntKind::UInt => 32,
139 IntKind::Long | IntKind::ULong => target.long_width,
140 IntKind::LongLong | IntKind::ULongLong => 64,
141 IntKind::Int128 | IntKind::UInt128 => 128,
142 }
143}
144
145/// What an integer type is once it no longer matters how it was spelled.
146///
147/// A width and a signedness, which between them are everything the value of an integer constant
148/// depends on. `int`, an enumeration represented in `int`, and `_BitInt(32)` are three different
149/// types with one [`IntegerInfo`], and every question about what a constant of any of them holds
150/// has the same answer for all three.
151///
152/// The width is the value's and not the object's. `bool` is one bit here and one byte in
153/// [`layout`], and `_BitInt(37)` is thirty seven bits here and eight bytes there.
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
155pub struct IntegerInfo {
156 /// Whether the type can hold a negative value.
157 pub signed: bool,
158 /// How many bits of a value the type keeps.
159 pub width: u32,
160}
161
162impl IntegerInfo {
163 /// An integer type of the given signedness and width.
164 #[must_use]
165 pub const fn new(signed: bool, width: u32) -> IntegerInfo {
166 IntegerInfo { signed, width }
167 }
168
169 /// The value `raw` becomes once it is stored in a type of this shape.
170 ///
171 /// The low `width` bits of it, extended into the rest by the signedness. That is the form a
172 /// folded constant is held in, so `300` wrapped by a `char` is `44`, `-1` wrapped by an
173 /// `unsigned int` is `4294967295`, and a value of a hundred and twenty eight bit type is
174 /// itself, because there is nothing wider left to extend it into.
175 #[must_use]
176 pub const fn wrap(self, raw: i128) -> i128 {
177 if self.width == 0 {
178 return 0;
179 }
180 if self.width >= 128 {
181 return raw;
182 }
183 let unused = 128 - self.width;
184 if self.signed {
185 (raw << unused) >> unused
186 } else {
187 (((raw as u128) << unused) >> unused) as i128
188 }
189 }
190
191 /// Whether `raw` is a value a type of this shape can hold.
192 ///
193 /// Every hundred and twenty eight bit pattern is a value of a hundred and twenty eight bit
194 /// type, of either signedness, which is why this is a question about the width rather than
195 /// a comparison against a pair of bounds: `unsigned __int128` has a greatest value that no
196 /// [`i128`] can be handed to ask about.
197 #[must_use]
198 pub const fn holds(self, raw: i128) -> bool {
199 self.wrap(raw) == raw
200 }
201}
202
203/// The signedness and width of an integer type, and [`None`] when `id` is not one.
204///
205/// Every integer type C has. `bool` is one bit and unsigned, an enumeration answers as whatever
206/// it is represented in, a `_BitInt` answers with the width it was written with, and `_Atomic`
207/// and a typedef name answer as the type underneath. The coverage is the point: the shape used
208/// by the conversion ranks in `convert.rs` deliberately covers only the two the ranks are
209/// defined over, and folding a constant with that one would get `bool` and every enumeration
210/// wrong rather than refusing them.
211#[must_use]
212pub fn integer_info(types: &Types, id: TypeId, target: &TargetInfo) -> Option<IntegerInfo> {
213 match bare(types, id) {
214 TypeKind::Bool => Some(IntegerInfo::new(false, 1)),
215 TypeKind::Int(kind) => {
216 Some(IntegerInfo::new(kind.is_signed(target.char_is_signed), int_width(kind, target)))
217 }
218 TypeKind::BitInt { signed, width } => Some(IntegerInfo::new(signed, width)),
219 // An enumeration is represented in some integer type, and until its definition has been
220 // seen there is no answer to give. Saying so beats picking `int`, because a caller that
221 // folds a constant in a width the type does not have folds it wrongly and silently.
222 TypeKind::Enum(id) => {
223 let underlying = types.enum_info(id).underlying?;
224 integer_info(types, underlying, target)
225 }
226 _ => None,
227 }
228}
229
230/// The width of a real floating type in bits, including the padding `long double` carries.
231///
232/// The number for `long double` is storage rather than precision. Eighty bits of x87 occupy
233/// sixteen bytes on SysV x86-64, and it is the sixteen that `sizeof` answers with.
234#[must_use]
235pub fn float_width(kind: FloatKind, target: &TargetInfo) -> u32 {
236 match kind {
237 FloatKind::Float => 32,
238 FloatKind::Double => 64,
239 FloatKind::LongDouble => target.long_double_width,
240 }
241}
242
243/// The binary format a real floating type has on `target`.
244///
245/// Not derivable from [`float_width`], which is why it is a separate question: the width of a
246/// `long double` on SysV x86-64 is a hundred and twenty eight bits and its format is the eighty
247/// bit x87 one, and a compiler that picked the format by the size would fold every `long double`
248/// constant on that target with seventeen decimal digits too many.
249#[must_use]
250pub fn float_format(kind: FloatKind, target: &TargetInfo) -> Format {
251 match kind {
252 FloatKind::Float => Format::Single,
253 FloatKind::Double => Format::Double,
254 FloatKind::LongDouble => target.long_double_format,
255 }
256}
257
258/// The layout of `_BitInt(width)`.
259///
260/// Up to 64 bits a `_BitInt` is laid out like the smallest standard integer type that holds
261/// it, so the size is the byte count rounded up to a power of two and the alignment is the
262/// size. Above that the psABIs treat it as an array of a granule instead, and the granule is
263/// not the same everywhere: it is 64 bits on x86-64 and RISC-V and 128 on AArch64, which is
264/// why `_BitInt(65)` is sixteen bytes aligned to eight on the first and sixteen bytes aligned
265/// to sixteen on the second. Measured with clang 18 on x86-64 Linux and clang on AArch64
266/// Darwin, including the cases above 128 bits where the size keeps growing by a granule.
267fn bit_int_layout(width: u32, target: &TargetInfo) -> Layout {
268 let bytes = u64::from(width).div_ceil(8);
269 if bytes <= 8 {
270 return Layout::scalar(bytes.max(1).next_power_of_two());
271 }
272 let granule = u64::from(target.bit_int_granule / 8);
273 Layout::new(bytes.next_multiple_of(granule), granule)
274}
275
276/// The layout of `_Atomic(T)` given the layout of `T`.
277///
278/// Same size, and an alignment raised to the size when the size is one of the widths the
279/// target can do a lock free access at. That is why `_Atomic` is a type and not a qualifier:
280/// a sixteen byte structure is aligned to eight and `_Atomic` of it is aligned to sixteen, and
281/// a type system that treated the two as one type would silently disagree with itself about
282/// where the object goes. Checked against GCC 13 on x86-64 Linux and clang on AArch64 Darwin,
283/// which report exactly that.
284fn atomic_layout(inner: Layout) -> Layout {
285 if inner.size.is_power_of_two() && inner.size <= 16 {
286 return Layout::new(inner.size, inner.align.max(inner.size));
287 }
288 inner
289}
290
291/// The layout of a GNU vector whose elements occupy `raw` bytes in total.
292///
293/// Rounded up to a power of two and aligned to the whole thing, which is what GCC does with a
294/// `vector_size` that is not already one. GCC rejects an element count that is not a power of
295/// two and clang rounds instead, so this rounds and leaves the rejecting to whoever is holding
296/// the attribute and the dialect.
297fn vector_layout(raw: u64) -> Layout {
298 let size = raw.max(1).next_power_of_two();
299 Layout::scalar(size)
300}