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 Layout for () {
102 fn head_size() -> usize {
103 0
104 }
105}
106
107pub fn compute(ty: &ir::Ty) -> Option<ir::TyLayout> {
110 Some(match &ty.kind {
111 ir::TyKind::Array(_) | ir::TyKind::Primitive(ir::TyPrimitive::text) => ir::TyLayout {
112 head_size: 64,
113 body_ptrs: vec![0],
114 },
115
116 ir::TyKind::Primitive(prim) => {
117 let head_size = match prim {
118 ir::TyPrimitive::int8 => 8,
119 ir::TyPrimitive::int16 => 16,
120 ir::TyPrimitive::int32 => 32,
121 ir::TyPrimitive::int64 => 64,
122 ir::TyPrimitive::uint8 => 8,
123 ir::TyPrimitive::uint16 => 16,
124 ir::TyPrimitive::uint32 => 32,
125 ir::TyPrimitive::uint64 => 64,
126 ir::TyPrimitive::float32 => 32,
127 ir::TyPrimitive::float64 => 64,
128 ir::TyPrimitive::bool => 8,
129 ir::TyPrimitive::text => unreachable!(),
130 };
131 ir::TyLayout {
132 head_size,
133 body_ptrs: vec![],
134 }
135 }
136
137 ir::TyKind::Tuple(fields) => {
138 let mut head_size: u32 = 0;
139 let mut body_ptrs = vec::Vec::new();
140 for f in fields {
141 let Some(layout) = &f.ty.layout else {
142 return None;
143 };
144
145 let field_offset = head_size.div_ceil(8);
146 head_size += layout.head_size;
147 body_ptrs.extend(layout.body_ptrs.iter().map(|p| p + field_offset));
148 }
149
150 ir::TyLayout {
151 head_size,
152 body_ptrs,
153 }
154 }
155
156 ir::TyKind::Enum(variants) => {
157 let head = enum_head_format(variants, &ty.variants_recursive);
158
159 let head_size = (head.tag_bytes + head.inner_bytes) * 8;
160
161 let body_ptrs = if head.has_ptr {
162 vec![head.tag_bytes]
163 } else {
164 vec![]
168 };
169
170 ir::TyLayout {
171 head_size,
172 body_ptrs,
173 }
174 }
175 _ => return None,
176 })
177}
178
179pub fn tuple_field_offsets(ty: &ir::Ty) -> vec::Vec<u32> {
180 let ir::TyKind::Tuple(ty_fields) = &ty.kind else {
181 panic!("got: {:?}", ty.kind)
182 };
183
184 let mut field_offsets = vec::Vec::with_capacity(ty_fields.len());
185 let mut offset = 0_u32;
186 for field in ty_fields {
187 field_offsets.push(offset);
188
189 let layout = field.ty.layout.as_ref().unwrap();
190 offset += (layout.head_size).div_ceil(8);
191 }
192 field_offsets
193}
194
195pub fn tuple_field_offset(ty: &ir::Ty, position: u16) -> u32 {
196 *tuple_field_offsets(ty).get(position as usize).unwrap()
197}
198
199pub fn does_enum_variant_contain_recursive(enum_ty: &ir::Ty, variant_index: u16) -> bool {
200 enum_ty.variants_recursive.contains(&variant_index)
201}
202
203pub use crate::generated::layout::EnumFormat;
204
205pub fn enum_format(variants: &[ir::TyEnumVariant], variants_recursive: &[u16]) -> EnumFormat {
206 let head = enum_head_format(variants, variants_recursive);
207 let variants = variants
208 .iter()
209 .map(|v| enum_variant_format(&head, &v.ty))
210 .collect();
211 EnumFormat {
212 tag_bytes: head.tag_bytes as u8,
213 inner_bytes: head.inner_bytes as u8,
214 has_ptr: head.has_ptr,
215 variants,
216 }
217}
218
219#[derive(Debug)]
220pub struct EnumHeadFormat {
221 pub tag_bytes: u32,
222 pub inner_bytes: u32,
223 pub has_ptr: bool,
224}
225
226pub fn enum_head_format(
227 variants: &[ir::TyEnumVariant],
228 variants_recursive: &[u16],
229) -> EnumHeadFormat {
230 let t = enum_tag_size(variants.len());
231
232 let force_ptr = !variants_recursive.is_empty();
233
234 let max_head = if force_ptr {
235 u32::MAX
236 } else {
237 enum_max_variant_head_size(variants)
238 };
239
240 let mut has_ptr = false;
241 let inner = if max_head > 32 {
242 has_ptr = true;
244 32
245 } else {
246 max_head
247 };
248
249 EnumHeadFormat {
250 tag_bytes: t.div_ceil(8),
251 inner_bytes: inner.div_ceil(8),
252 has_ptr,
253 }
254}
255
256pub use crate::generated::layout::EnumVariantFormat;
257
258pub fn enum_variant_format(head: &EnumHeadFormat, variant_ty: &ir::Ty) -> EnumVariantFormat {
259 let inner_head_size = variant_ty.layout.as_ref().unwrap().head_size;
260 let inner_head_bytes = inner_head_size.div_ceil(8);
261
262 let is_unit = inner_head_bytes == 0;
263
264 let padding_bytes = if head.has_ptr {
265 if is_unit {
266 4
268 } else {
269 0
270 }
271 } else {
272 head.inner_bytes.saturating_sub(inner_head_bytes) as u8
273 };
274
275 EnumVariantFormat {
276 padding_bytes,
277 is_unit,
278 }
279}
280
281fn enum_max_variant_head_size(variants: &[ir::TyEnumVariant]) -> u32 {
282 let mut i = 0;
283 for v in variants {
284 let Some(ty_layout) = &v.ty.layout else {
285 panic!("missing layout: {:?}", v.ty)
286 };
287 let size = ty_layout.head_size;
288 i = i.max(size);
289 }
290 i
291}
292
293fn enum_tag_size(variants_len: usize) -> u32 {
294 enum_tag_size_used(variants_len).div_ceil(8) * 8
296}
297
298fn enum_tag_size_used(variants_len: usize) -> u32 {
299 variants_len
300 .saturating_sub(1)
301 .checked_ilog2()
302 .map(|x| x + 1)
303 .unwrap_or_default()
304}
305
306#[test]
307fn test_enum_tag_size() {
308 assert_eq!(0, enum_tag_size_used(0));
309 assert_eq!(0, enum_tag_size_used(1));
310 assert_eq!(1, enum_tag_size_used(2));
311 assert_eq!(2, enum_tag_size_used(3));
312 assert_eq!(2, enum_tag_size_used(4));
313 assert_eq!(3, enum_tag_size_used(5));
314 assert_eq!(3, enum_tag_size_used(6));
315 assert_eq!(3, enum_tag_size_used(7));
316 assert_eq!(3, enum_tag_size_used(8));
317 assert_eq!(4, enum_tag_size_used(9));
318 assert_eq!(4, enum_tag_size_used(10));
319 assert_eq!(4, enum_tag_size_used(11));
320 assert_eq!(4, enum_tag_size_used(12));
321 assert_eq!(4, enum_tag_size_used(13));
322 assert_eq!(4, enum_tag_size_used(14));
323 assert_eq!(4, enum_tag_size_used(15));
324 assert_eq!(4, enum_tag_size_used(16));
325 assert_eq!(5, enum_tag_size_used(17));
326 assert_eq!(5, enum_tag_size_used(18));
327 assert_eq!(5, enum_tag_size_used(19));
328 assert_eq!(5, enum_tag_size_used(20));
329 assert_eq!(5, enum_tag_size_used(21));
330 assert_eq!(5, enum_tag_size_used(22));
331 assert_eq!(8, enum_tag_size_used(256));
332 assert_eq!(9, enum_tag_size_used(257));
333
334 assert_eq!(0, enum_tag_size(0));
335 assert_eq!(0, enum_tag_size(1));
336 assert_eq!(8, enum_tag_size(2));
337 assert_eq!(8, enum_tag_size(3));
338 assert_eq!(8, enum_tag_size(4));
339 assert_eq!(8, enum_tag_size(5));
340 assert_eq!(8, enum_tag_size(6));
341 assert_eq!(8, enum_tag_size(7));
342 assert_eq!(8, enum_tag_size(8));
343 assert_eq!(8, enum_tag_size(9));
344 assert_eq!(8, enum_tag_size(10));
345 assert_eq!(8, enum_tag_size(11));
346 assert_eq!(8, enum_tag_size(12));
347 assert_eq!(8, enum_tag_size(13));
348 assert_eq!(8, enum_tag_size(14));
349 assert_eq!(8, enum_tag_size(15));
350 assert_eq!(8, enum_tag_size(16));
351 assert_eq!(8, enum_tag_size(17));
352 assert_eq!(8, enum_tag_size(18));
353 assert_eq!(8, enum_tag_size(19));
354 assert_eq!(8, enum_tag_size(20));
355 assert_eq!(8, enum_tag_size(21));
356 assert_eq!(8, enum_tag_size(22));
357 assert_eq!(8, enum_tag_size(256));
358 assert_eq!(16, enum_tag_size(257));
359}