1use crate::string;
2use crate::vec;
3
4use crate::ir;
5
6pub trait Layout {
8 fn head_size() -> usize;
10}
11
12impl<T> Layout for &T
13where
14 T: Layout,
15{
16 fn head_size() -> usize {
17 T::head_size()
18 }
19}
20
21impl Layout for bool {
22 fn head_size() -> usize {
23 8
24 }
25}
26
27impl Layout for i8 {
28 fn head_size() -> usize {
29 8
30 }
31}
32impl Layout for i16 {
33 fn head_size() -> usize {
34 16
35 }
36}
37impl Layout for i32 {
38 fn head_size() -> usize {
39 32
40 }
41}
42impl Layout for i64 {
43 fn head_size() -> usize {
44 64
45 }
46}
47impl Layout for u8 {
48 fn head_size() -> usize {
49 8
50 }
51}
52impl Layout for u16 {
53 fn head_size() -> usize {
54 16
55 }
56}
57impl Layout for u32 {
58 fn head_size() -> usize {
59 32
60 }
61}
62impl Layout for u64 {
63 fn head_size() -> usize {
64 64
65 }
66}
67impl Layout for f32 {
68 fn head_size() -> usize {
69 32
70 }
71}
72impl Layout for f64 {
73 fn head_size() -> usize {
74 64
75 }
76}
77
78impl Layout for str {
79 fn head_size() -> usize {
80 64
81 }
82}
83impl Layout for string::String {
84 fn head_size() -> usize {
85 64
86 }
87}
88
89impl<I> Layout for vec::Vec<I> {
90 fn head_size() -> usize {
91 64
92 }
93}
94
95impl<I: Layout> Layout for Option<I> {
96 fn head_size() -> usize {
97 if I::head_size() == 0 { 8 } else { 40 }
98 }
99}
100
101impl<T: Layout, E: Layout> Layout for core::result::Result<T, E> {
102 fn head_size() -> usize {
103 let max_bits = T::head_size().max(E::head_size());
105 8 + max_bits.min(32)
106 }
107}
108
109impl Layout for () {
110 fn head_size() -> usize {
111 0
112 }
113}
114
115pub fn compute(ty: &ir::Ty) -> Option<ir::TyLayout> {
118 Some(match &ty.kind {
119 ir::TyKind::Array(_) | ir::TyKind::Primitive(ir::TyPrimitive::text) => ir::TyLayout {
120 head_size: 64,
121 body_ptrs: vec![0],
122 },
123
124 ir::TyKind::Primitive(prim) => {
125 let head_size = match prim {
126 ir::TyPrimitive::int8 => 8,
127 ir::TyPrimitive::int16 => 16,
128 ir::TyPrimitive::int32 => 32,
129 ir::TyPrimitive::int64 => 64,
130 ir::TyPrimitive::uint8 => 8,
131 ir::TyPrimitive::uint16 => 16,
132 ir::TyPrimitive::uint32 => 32,
133 ir::TyPrimitive::uint64 => 64,
134 ir::TyPrimitive::float32 => 32,
135 ir::TyPrimitive::float64 => 64,
136 ir::TyPrimitive::bool => 8,
137 ir::TyPrimitive::text => unreachable!(),
138 };
139 ir::TyLayout {
140 head_size,
141 body_ptrs: vec![],
142 }
143 }
144
145 ir::TyKind::Tuple(fields) => {
146 let mut head_size: u32 = 0;
147 let mut body_ptrs = vec::Vec::new();
148 for f in fields {
149 let Some(layout) = &f.ty.layout else {
150 return None;
151 };
152
153 let field_offset = head_size.div_ceil(8);
154 head_size += layout.head_size;
155 body_ptrs.extend(layout.body_ptrs.iter().map(|p| p + field_offset));
156 }
157
158 ir::TyLayout {
159 head_size,
160 body_ptrs,
161 }
162 }
163
164 ir::TyKind::Enum(variants) => {
165 let head = enum_head_format(variants, &ty.variants_recursive);
166
167 let head_size = (head.tag_bytes + head.inner_bytes) * 8;
168
169 let body_ptrs = if head.has_ptr {
170 vec![head.tag_bytes]
171 } else {
172 vec![]
176 };
177
178 ir::TyLayout {
179 head_size,
180 body_ptrs,
181 }
182 }
183 _ => return None,
184 })
185}
186
187pub fn tuple_field_offsets(ty: &ir::Ty) -> vec::Vec<u32> {
188 let ir::TyKind::Tuple(ty_fields) = &ty.kind else {
189 panic!("got: {:?}", ty.kind)
190 };
191
192 let mut field_offsets = vec::Vec::with_capacity(ty_fields.len());
193 let mut offset = 0_u32;
194 for field in ty_fields {
195 field_offsets.push(offset);
196
197 let layout = field.ty.layout.as_ref().unwrap();
198 offset += (layout.head_size).div_ceil(8);
199 }
200 field_offsets
201}
202
203pub fn tuple_field_offset(ty: &ir::Ty, position: u16) -> u32 {
204 *tuple_field_offsets(ty).get(position as usize).unwrap()
205}
206
207pub fn does_enum_variant_contain_recursive(enum_ty: &ir::Ty, variant_index: u16) -> bool {
208 enum_ty.variants_recursive.contains(&variant_index)
209}
210
211pub use crate::generated::layout::EnumFormat;
212
213pub fn enum_format(variants: &[ir::TyEnumVariant], variants_recursive: &[u16]) -> EnumFormat {
214 let head = enum_head_format(variants, variants_recursive);
215 let variants = variants
216 .iter()
217 .map(|v| enum_variant_format(&head, &v.ty))
218 .collect();
219 EnumFormat {
220 tag_bytes: head.tag_bytes as u8,
221 inner_bytes: head.inner_bytes as u8,
222 has_ptr: head.has_ptr,
223 variants,
224 }
225}
226
227#[derive(Debug)]
228pub struct EnumHeadFormat {
229 pub tag_bytes: u32,
230 pub inner_bytes: u32,
231 pub has_ptr: bool,
232}
233
234pub fn enum_head_format(
235 variants: &[ir::TyEnumVariant],
236 variants_recursive: &[u16],
237) -> EnumHeadFormat {
238 let t = enum_tag_size(variants.len());
239
240 let force_ptr = !variants_recursive.is_empty();
241
242 let max_head = if force_ptr {
243 u32::MAX
244 } else {
245 enum_max_variant_head_size(variants)
246 };
247
248 let mut has_ptr = false;
249 let inner = if max_head > 32 {
250 has_ptr = true;
252 32
253 } else {
254 max_head
255 };
256
257 EnumHeadFormat {
258 tag_bytes: t.div_ceil(8),
259 inner_bytes: inner.div_ceil(8),
260 has_ptr,
261 }
262}
263
264pub use crate::generated::layout::EnumVariantFormat;
265
266pub fn enum_variant_format(head: &EnumHeadFormat, variant_ty: &ir::Ty) -> EnumVariantFormat {
267 let inner_head_size = variant_ty.layout.as_ref().unwrap().head_size;
268 let inner_head_bytes = inner_head_size.div_ceil(8);
269
270 let is_unit = inner_head_bytes == 0;
271
272 let padding_bytes = if head.has_ptr {
273 if is_unit {
274 4
276 } else {
277 0
278 }
279 } else {
280 head.inner_bytes.saturating_sub(inner_head_bytes) as u8
281 };
282
283 EnumVariantFormat {
284 padding_bytes,
285 is_unit,
286 }
287}
288
289fn enum_max_variant_head_size(variants: &[ir::TyEnumVariant]) -> u32 {
290 let mut i = 0;
291 for v in variants {
292 let Some(ty_layout) = &v.ty.layout else {
293 panic!("missing layout: {:?}", v.ty)
294 };
295 let size = ty_layout.head_size;
296 i = i.max(size);
297 }
298 i
299}
300
301fn enum_tag_size(variants_len: usize) -> u32 {
302 enum_tag_size_used(variants_len).div_ceil(8) * 8
304}
305
306fn enum_tag_size_used(variants_len: usize) -> u32 {
307 variants_len
308 .saturating_sub(1)
309 .checked_ilog2()
310 .map(|x| x + 1)
311 .unwrap_or_default()
312}
313
314#[test]
315fn test_enum_tag_size() {
316 assert_eq!(0, enum_tag_size_used(0));
317 assert_eq!(0, enum_tag_size_used(1));
318 assert_eq!(1, enum_tag_size_used(2));
319 assert_eq!(2, enum_tag_size_used(3));
320 assert_eq!(2, enum_tag_size_used(4));
321 assert_eq!(3, enum_tag_size_used(5));
322 assert_eq!(3, enum_tag_size_used(6));
323 assert_eq!(3, enum_tag_size_used(7));
324 assert_eq!(3, enum_tag_size_used(8));
325 assert_eq!(4, enum_tag_size_used(9));
326 assert_eq!(4, enum_tag_size_used(10));
327 assert_eq!(4, enum_tag_size_used(11));
328 assert_eq!(4, enum_tag_size_used(12));
329 assert_eq!(4, enum_tag_size_used(13));
330 assert_eq!(4, enum_tag_size_used(14));
331 assert_eq!(4, enum_tag_size_used(15));
332 assert_eq!(4, enum_tag_size_used(16));
333 assert_eq!(5, enum_tag_size_used(17));
334 assert_eq!(5, enum_tag_size_used(18));
335 assert_eq!(5, enum_tag_size_used(19));
336 assert_eq!(5, enum_tag_size_used(20));
337 assert_eq!(5, enum_tag_size_used(21));
338 assert_eq!(5, enum_tag_size_used(22));
339 assert_eq!(8, enum_tag_size_used(256));
340 assert_eq!(9, enum_tag_size_used(257));
341
342 assert_eq!(0, enum_tag_size(0));
343 assert_eq!(0, enum_tag_size(1));
344 assert_eq!(8, enum_tag_size(2));
345 assert_eq!(8, enum_tag_size(3));
346 assert_eq!(8, enum_tag_size(4));
347 assert_eq!(8, enum_tag_size(5));
348 assert_eq!(8, enum_tag_size(6));
349 assert_eq!(8, enum_tag_size(7));
350 assert_eq!(8, enum_tag_size(8));
351 assert_eq!(8, enum_tag_size(9));
352 assert_eq!(8, enum_tag_size(10));
353 assert_eq!(8, enum_tag_size(11));
354 assert_eq!(8, enum_tag_size(12));
355 assert_eq!(8, enum_tag_size(13));
356 assert_eq!(8, enum_tag_size(14));
357 assert_eq!(8, enum_tag_size(15));
358 assert_eq!(8, enum_tag_size(16));
359 assert_eq!(8, enum_tag_size(17));
360 assert_eq!(8, enum_tag_size(18));
361 assert_eq!(8, enum_tag_size(19));
362 assert_eq!(8, enum_tag_size(20));
363 assert_eq!(8, enum_tag_size(21));
364 assert_eq!(8, enum_tag_size(22));
365 assert_eq!(8, enum_tag_size(256));
366 assert_eq!(16, enum_tag_size(257));
367}