swamp_vm_layout/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5//! Layouts analyzed into Vm Types (`BasicType`)
6
7use seq_map::SeqMap;
8use std::cmp::max;
9use std::rc::Rc;
10use swamp_types::prelude::{AnonymousStructType, EnumType, EnumVariantType, NamedStructType};
11use swamp_types::{TypeId, TypeKind, TypeRef};
12use swamp_vm_types::types::{
13    BasicType, BasicTypeId, BasicTypeKind, BasicTypeRef, OffsetMemoryItem, StructType, TaggedUnion,
14    TaggedUnionVariant, TupleType,
15};
16use swamp_vm_types::{
17    ANY_HEADER_ALIGNMENT, ANY_HEADER_SIZE, CountU16, GRID_HEADER_ALIGNMENT, GRID_HEADER_SIZE,
18    MAP_HEADER_ALIGNMENT, MemoryAlignment, MemoryOffset, MemorySize, PTR_ALIGNMENT, PTR_SIZE,
19    STRING_PTR_ALIGNMENT, STRING_PTR_SIZE, VEC_HEADER_ALIGNMENT, VEC_HEADER_SIZE,
20    adjust_size_to_alignment, align_to,
21};
22
23#[derive(Clone)]
24pub struct LayoutCache {
25    pub id_to_layout: SeqMap<TypeId, BasicTypeRef>,
26    pub kind_to_layout: SeqMap<TypeKind, BasicTypeRef>,
27}
28
29impl LayoutCache {}
30
31impl Default for LayoutCache {
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37impl LayoutCache {
38    #[must_use]
39    pub fn new() -> Self {
40        Self {
41            id_to_layout: SeqMap::default(),
42            kind_to_layout: SeqMap::default(),
43        }
44    }
45
46    #[must_use]
47    pub fn layout(&mut self, analyzed_type: &TypeRef) -> BasicTypeRef {
48        // First check if we already have a layout for this type ID
49        if let Some(x) = self.id_to_layout.get(&analyzed_type.id) {
50            return x.clone();
51        }
52
53        // Check if we already have a layout for this kind of type
54        if let Some(existing_layout) = self.kind_to_layout.get(&analyzed_type.kind) {
55            // For deduplication, we reuse the existing layout directly
56            // This ensures pointer equality for structurally identical types
57            let _ = self
58                .id_to_layout
59                .insert(analyzed_type.id, existing_layout.clone());
60            return existing_layout.clone();
61        }
62
63        let basic_type = self.layout_type(analyzed_type);
64
65        let _ = self
66            .id_to_layout
67            .insert(analyzed_type.id, basic_type.clone());
68
69        // Also store in kind_to_layout for future deduplication
70        let _ = self
71            .kind_to_layout
72            .insert((*analyzed_type.kind).clone(), basic_type.clone());
73
74        basic_type
75    }
76
77    #[must_use]
78    fn layout_tagged_union(variants: &[TaggedUnionVariant], name: &str) -> TaggedUnion {
79        let num_variants = variants.len();
80        let (tag_size, tag_alignment) = if num_variants <= 0xFF {
81            (MemorySize(1), MemoryAlignment::U8)
82        } else if num_variants <= 0xFFFF {
83            (MemorySize(2), MemoryAlignment::U16)
84        } else {
85            (MemorySize(4), MemoryAlignment::U32)
86        };
87
88        let max_payload_size = variants
89            .iter()
90            .map(|v| v.ty.total_size)
91            .max()
92            .unwrap_or(MemorySize(0));
93        let max_payload_alignment = variants
94            .iter()
95            .map(|v| v.ty.max_alignment)
96            .max()
97            .unwrap_or(MemoryAlignment::U8);
98
99        let payload_offset = align_to(MemoryOffset(tag_size.0), max_payload_alignment);
100        let max_alignment = max(tag_alignment, max_payload_alignment);
101
102        let complete_size_before_alignment = MemorySize(payload_offset.0 + max_payload_size.0);
103        let total_size = adjust_size_to_alignment(complete_size_before_alignment, max_alignment);
104
105        TaggedUnion {
106            name: name.to_string(),
107            tag_offset: MemoryOffset(0),
108            tag_size,
109            tag_alignment,
110            payload_offset,
111            payload_max_size: max_payload_size,
112            max_payload_alignment,
113            total_size,
114            max_alignment,
115            variants: variants.to_vec(),
116        }
117    }
118
119    #[allow(clippy::too_many_lines)]
120    #[must_use]
121    pub fn layout_enum_into_tagged_union(
122        &mut self,
123        name: &str,
124        v: &[EnumVariantType],
125    ) -> TaggedUnion {
126        let variant_layouts: Vec<_> = v
127            .iter()
128            .map(|variant| {
129                let gen_payload_type = self.layout_type(&variant.payload_type);
130
131                TaggedUnionVariant {
132                    name: variant.common.assigned_name.clone(),
133                    ty: gen_payload_type,
134                }
135            })
136            .collect();
137
138        Self::layout_tagged_union(&variant_layouts, name)
139    }
140
141    #[must_use]
142    pub fn layout_enum(
143        &mut self,
144        name: &str,
145        variants: &[EnumVariantType],
146        type_id: TypeId,
147    ) -> BasicTypeRef {
148        // Check if we already have a layout for this kind
149        let enum_kind = TypeKind::Enum(EnumType::new(
150            source_map_node::Node::default(),
151            name,
152            vec![String::new()],
153        ));
154        if let Some(existing_layout) = self.kind_to_layout.get(&enum_kind) {
155            return existing_layout.clone();
156        }
157
158        let tagged_union = self.layout_enum_into_tagged_union(name, variants);
159
160        let basic_type = Rc::new(BasicType {
161            id: BasicTypeId(type_id.inner()),
162            total_size: tagged_union.total_size,
163            max_alignment: tagged_union.max_alignment,
164            kind: BasicTypeKind::TaggedUnion(tagged_union),
165        });
166
167        // Store in kind_to_layout
168        let _ = self.kind_to_layout.insert(enum_kind, basic_type.clone());
169
170        basic_type
171    }
172
173    #[must_use]
174    fn layout_vec_like(
175        &mut self,
176        element_type: &TypeRef,
177        capacity: usize,
178    ) -> (BasicTypeRef, MemorySize, MemoryAlignment) {
179        let element_type_basic = self.layout_type(element_type);
180        let (mem_size, mem_alignment) =
181            self.layout_vec_like_from_basic(&element_type_basic, capacity);
182
183        (element_type_basic, mem_size, mem_alignment)
184    }
185
186    #[must_use]
187    fn layout_vec_like_from_basic(
188        &mut self,
189        element_type_basic: &BasicTypeRef,
190        capacity: usize,
191    ) -> (MemorySize, MemoryAlignment) {
192        let total_size =
193            element_type_basic.total_size.0 as usize * capacity + VEC_HEADER_SIZE.0 as usize;
194        let max_alignment = max(element_type_basic.max_alignment, MemoryAlignment::U32);
195
196        (MemorySize(total_size as u32), max_alignment)
197    }
198
199    /// Computes the memory layout for a type in the target architecture.
200    ///
201    /// In compiler terminology:
202    ///
203    /// - "layout" determines size, alignment, and field offsets of types
204    /// - "materialization" refers to how types are represented in memory
205    /// - "lowering" is the process of mapping source types to machine types
206    ///
207    /// This function performs type lowering by mapping high-level types to their
208    /// concrete memory representations, handling:
209    ///
210    /// - Primitive, scalar types (integers, floats, booleans)
211    /// - Aggregate types (structs, tuples, enums)
212    /// - Reference types (pointers, slices)
213    /// - Collection types (vectors, maps)
214    ///
215    /// The layout process considers:
216    ///
217    /// - Size: Total bytes needed for the type
218    /// - Alignment: Memory boundary requirements
219    /// - Padding: Gaps needed for proper field alignment
220    /// - ABI: Target architecture requirements
221    ///
222    /// # Panics
223    ///
224    #[allow(clippy::too_many_lines)]
225    #[must_use]
226    fn layout_type(&mut self, ty: &TypeRef) -> BasicTypeRef {
227        // First check if we already have a layout for this type ID
228        if let Some(x) = self.id_to_layout.get(&ty.id) {
229            return x.clone();
230        }
231
232        // Check if we already have a layout for this kind of type
233        if let Some(existing_layout) = self.kind_to_layout.get(&ty.kind) {
234            // For deduplication, we need to create a new BasicType with the same structure
235            // but with the original TypeId, so that pointer equality works for the same
236            // structural types, but we still have separate entries for each TypeId
237            let new_basic_type = Rc::new(BasicType {
238                id: BasicTypeId(ty.id.inner()),
239                total_size: existing_layout.total_size,
240                max_alignment: existing_layout.max_alignment,
241                kind: existing_layout.kind.clone(),
242            });
243
244            // Store the mapping from this type ID to the new layout
245            let _ = self.id_to_layout.insert(ty.id, new_basic_type.clone());
246
247            // For nested types, we need to properly track all component types
248            // This is essential for the deduplication to work correctly
249            match &*ty.kind {
250                TypeKind::AnonymousStruct(struct_type) => {
251                    // Process each field type
252                    for field in struct_type.field_name_sorted_fields.values() {
253                        let field_type = &field.field_type;
254                        let field_layout = self.layout(field_type);
255                        let _ = self.id_to_layout.insert(field_type.id, field_layout);
256                    }
257                }
258
259                TypeKind::NamedStruct(named_struct) => {
260                    // Process each field type in the anonymous struct
261                    if let TypeKind::AnonymousStruct(anon_struct) =
262                        &*named_struct.anon_struct_type.kind
263                    {
264                        for field in anon_struct.field_name_sorted_fields.values() {
265                            let field_type = &field.field_type;
266                            let field_layout = self.layout(field_type);
267                            let _ = self.id_to_layout.insert(field_type.id, field_layout);
268                        }
269                    }
270                }
271
272                TypeKind::Tuple(tuple_types) => {
273                    // Process each tuple element type
274                    for elem_type in tuple_types {
275                        let elem_layout = self.layout(elem_type);
276                        let _ = self.id_to_layout.insert(elem_type.id, elem_layout);
277                    }
278                }
279                _ => {}
280            }
281
282            return new_basic_type;
283        }
284
285        let basic_type = match &*ty.kind {
286            TypeKind::Never => {
287                create_basic_type(ty.id, BasicTypeKind::U8, MemorySize(0), MemoryAlignment::U8)
288            }
289            TypeKind::Byte => {
290                create_basic_type(ty.id, BasicTypeKind::U8, MemorySize(1), MemoryAlignment::U8)
291            }
292            TypeKind::Codepoint => create_basic_type(
293                ty.id,
294                BasicTypeKind::U32,
295                MemorySize(4),
296                MemoryAlignment::U32,
297            ),
298            TypeKind::Int => create_basic_type(
299                ty.id,
300                BasicTypeKind::S32,
301                MemorySize(4),
302                MemoryAlignment::U32,
303            ),
304
305            TypeKind::Float => create_basic_type(
306                ty.id,
307                BasicTypeKind::Fixed32,
308                MemorySize(4),
309                MemoryAlignment::U32,
310            ),
311
312            TypeKind::Bool => {
313                create_basic_type(ty.id, BasicTypeKind::B8, MemorySize(1), MemoryAlignment::U8)
314            }
315
316            TypeKind::String(byte, char) => create_basic_type(
317                ty.id,
318                BasicTypeKind::StringView {
319                    byte: self.layout(byte),
320                    char: self.layout(char),
321                },
322                STRING_PTR_SIZE,
323                STRING_PTR_ALIGNMENT,
324            ),
325
326            TypeKind::AnonymousStruct(struct_type) => {
327                self.layout_struct(struct_type, "anonymous", ty.id)
328            }
329
330            TypeKind::NamedStruct(named_struct) => self.layout_named_struct(named_struct, ty.id),
331
332            TypeKind::Tuple(tuple_types) => self.layout_tuple(tuple_types, ty.id),
333
334            TypeKind::Optional(inner_type) => self.layout_optional_type(inner_type, ty.id),
335
336            TypeKind::Enum(enum_type) => {
337                let variants = enum_type.variants.values().cloned().collect::<Vec<_>>();
338                self.layout_enum(&enum_type.assigned_name, &variants, ty.id)
339            }
340
341            TypeKind::FixedCapacityAndLengthArray(element_type, capacity) => {
342                let (element_layout, total_size, max_alignment) =
343                    self.layout_vec_like(element_type, *capacity);
344
345                let array_type = Rc::new(BasicType {
346                    id: BasicTypeId(ty.id.inner()),
347                    kind: BasicTypeKind::FixedCapacityArray(element_layout.clone(), *capacity),
348                    total_size,
349                    max_alignment,
350                });
351
352                // Also store the element type in id_to_layout
353                let _ = self.id_to_layout.insert(element_type.id, element_layout);
354
355                array_type
356            }
357
358            TypeKind::StringStorage(byte_type, char_type, capacity) => {
359                let (element_layout, total_size, max_alignment) =
360                    self.layout_vec_like(byte_type, *capacity);
361
362                let array_type = Rc::new(BasicType {
363                    id: BasicTypeId(ty.id.inner()),
364                    kind: BasicTypeKind::StringStorage {
365                        element_type: element_layout.clone(),
366                        char: self.layout(char_type),
367                        capacity: *capacity,
368                    },
369                    total_size,
370                    max_alignment,
371                });
372
373                // Also store the element type in id_to_layout
374                let _ = self.id_to_layout.insert(byte_type.id, element_layout);
375
376                array_type
377            }
378
379            TypeKind::Any => create_basic_type(
380                ty.id,
381                BasicTypeKind::Any,
382                ANY_HEADER_SIZE,
383                ANY_HEADER_ALIGNMENT,
384            ),
385
386            TypeKind::DynamicLengthVecView(element_type) => {
387                let (element_layout, _, _) = self.layout_vec_like(element_type, 0);
388
389                let vec_type = Rc::new(BasicType {
390                    id: BasicTypeId(ty.id.inner()),
391                    kind: BasicTypeKind::DynamicLengthVecView(element_layout.clone()),
392                    total_size: PTR_SIZE,
393                    max_alignment: PTR_ALIGNMENT,
394                });
395
396                let _ = self.id_to_layout.insert(element_type.id, element_layout);
397
398                vec_type
399            }
400
401            TypeKind::VecStorage(element_type, capacity) => {
402                let (element_layout, total_size, element_alignment) =
403                    self.layout_vec_like(element_type, *capacity);
404
405                let storage_type = Rc::new(BasicType {
406                    id: BasicTypeId(ty.id.inner()),
407                    kind: BasicTypeKind::VecStorage(element_layout.clone(), *capacity),
408                    total_size,
409                    max_alignment: max(VEC_HEADER_ALIGNMENT, element_alignment),
410                });
411
412                let _ = self.id_to_layout.insert(element_type.id, element_layout);
413
414                storage_type
415            }
416
417            TypeKind::DynamicLengthMapView(key_type, value_type) => {
418                // Layout key type
419                let key_layout = self.layout(key_type);
420                let _ = self.id_to_layout.insert(key_type.id, key_layout.clone());
421
422                // Layout value type
423                let value_layout = self.layout(value_type);
424                let _ = self
425                    .id_to_layout
426                    .insert(value_type.id, value_layout.clone());
427
428                let key_item = OffsetMemoryItem {
429                    offset: MemoryOffset(0),
430                    size: key_layout.total_size,
431                    name: "key".to_string(),
432                    ty: key_layout.clone(),
433                };
434
435                let value_offset = align_to(
436                    MemoryOffset(key_layout.total_size.0),
437                    value_layout.max_alignment,
438                );
439
440                let value_item = OffsetMemoryItem {
441                    offset: value_offset,
442                    size: value_layout.total_size,
443                    name: "value".to_string(),
444                    ty: value_layout.clone(),
445                };
446
447                Rc::new(BasicType {
448                    id: BasicTypeId(ty.id.inner()),
449                    kind: BasicTypeKind::DynamicLengthMapView(
450                        Box::new(key_item),
451                        Box::new(value_item),
452                    ),
453                    total_size: PTR_SIZE,
454                    max_alignment: PTR_ALIGNMENT,
455                })
456            }
457
458            TypeKind::MapStorage(key_type, value_type, logical_limit) => {
459                // Layout key type
460                let key_layout = self.layout(key_type);
461                let _ = self.id_to_layout.insert(key_type.id, key_layout.clone());
462
463                // Layout value type
464                let value_layout = self.layout(value_type);
465                let _ = self
466                    .id_to_layout
467                    .insert(value_type.id, value_layout.clone());
468
469                let logical_limit = *logical_limit;
470
471                let (_bucket_layout, map_init) = hashmap_mem::layout(
472                    key_layout.total_size.0,
473                    key_layout.max_alignment.into(),
474                    value_layout.total_size.0,
475                    value_layout.max_alignment.into(),
476                    logical_limit as u16,
477                );
478                let total_size = MemorySize(map_init.total_size);
479
480                Rc::new(BasicType {
481                    id: BasicTypeId(ty.id.inner()),
482                    kind: BasicTypeKind::MapStorage {
483                        key_type: key_layout,
484                        logical_limit,
485                        capacity: CountU16(map_init.capacity),
486                        value_type: value_layout,
487                    },
488                    total_size,
489                    max_alignment: MAP_HEADER_ALIGNMENT,
490                })
491            }
492
493            TypeKind::Range(_range_struct) => create_basic_type(
494                ty.id,
495                BasicTypeKind::InternalRangeHeader,
496                MemorySize(12),
497                MemoryAlignment::U32,
498            ),
499
500            TypeKind::GridView(element_type) => {
501                let element_layout = self.layout(element_type);
502                let _ = self
503                    .id_to_layout
504                    .insert(element_type.id, element_layout.clone());
505
506                Rc::new(BasicType {
507                    id: BasicTypeId(ty.id.inner()),
508                    kind: BasicTypeKind::GridView(element_layout),
509                    total_size: PTR_SIZE,
510                    max_alignment: PTR_ALIGNMENT,
511                })
512            }
513
514            TypeKind::GridStorage(element_type, width, height) => {
515                let element_layout = self.layout(element_type);
516                let _ = self
517                    .id_to_layout
518                    .insert(element_type.id, element_layout.clone());
519
520                let element_size = element_layout.total_size;
521                let element_alignment = element_layout.max_alignment;
522
523                Rc::new(BasicType {
524                    id: BasicTypeId(ty.id.inner()),
525                    kind: BasicTypeKind::GridStorage(element_layout, *width, *height),
526                    total_size: MemorySize(
527                        GRID_HEADER_SIZE.0 + element_size.0 * (*width as u32) * (*height as u32),
528                    ),
529                    max_alignment: max(GRID_HEADER_ALIGNMENT, element_alignment),
530                })
531            }
532
533            TypeKind::SliceView(element_type) => {
534                let element_layout = self.layout(element_type);
535                let _ = self
536                    .id_to_layout
537                    .insert(element_type.id, element_layout.clone());
538
539                Rc::new(BasicType {
540                    id: BasicTypeId(ty.id.inner()),
541                    kind: BasicTypeKind::SliceView(element_layout),
542                    total_size: PTR_SIZE,
543                    max_alignment: PTR_ALIGNMENT,
544                })
545            }
546
547            TypeKind::SparseStorage(element_type, capacity) => {
548                let element_layout = self.layout(element_type);
549                let size = sparse_mem::layout_size(*capacity as u16, element_layout.total_size.0);
550
551                Rc::new(BasicType {
552                    id: BasicTypeId(ty.id.inner()),
553                    kind: BasicTypeKind::SparseStorage(element_layout, *capacity),
554                    total_size: MemorySize(size as u32),
555                    max_alignment: MemoryAlignment::U64,
556                })
557            }
558
559            TypeKind::SparseView(element_type) => {
560                let element_layout = self.layout(element_type);
561                let _ = self
562                    .id_to_layout
563                    .insert(element_type.id, element_layout.clone());
564
565                Rc::new(BasicType {
566                    id: BasicTypeId(ty.id.inner()),
567                    kind: BasicTypeKind::SparseView(element_layout),
568                    total_size: PTR_SIZE,
569                    max_alignment: PTR_ALIGNMENT,
570                })
571            }
572
573            TypeKind::StackStorage(element_type, capacity) => {
574                let (element_layout, total_size, _max_alignment) =
575                    self.layout_vec_like(element_type, *capacity);
576
577                let storage_type = Rc::new(BasicType {
578                    id: BasicTypeId(ty.id.inner()),
579                    kind: BasicTypeKind::StackStorage(element_layout.clone(), *capacity),
580                    total_size,
581                    max_alignment: VEC_HEADER_ALIGNMENT,
582                });
583
584                let _ = self.id_to_layout.insert(element_type.id, element_layout);
585
586                storage_type
587            }
588
589            TypeKind::StackView(element_type) => {
590                let element_layout = self.layout(element_type);
591                let _ = self
592                    .id_to_layout
593                    .insert(element_type.id, element_layout.clone());
594
595                Rc::new(BasicType {
596                    id: BasicTypeId(ty.id.inner()),
597                    kind: BasicTypeKind::DynamicLengthVecView(element_layout),
598                    total_size: PTR_SIZE,
599                    max_alignment: PTR_ALIGNMENT,
600                })
601            }
602
603            TypeKind::QueueStorage(element_type, capacity) => {
604                let (element_layout, total_size, _max_alignment) =
605                    self.layout_vec_like(element_type, *capacity);
606
607                let storage_type = Rc::new(BasicType {
608                    id: BasicTypeId(ty.id.inner()),
609                    kind: BasicTypeKind::QueueStorage(element_layout.clone(), *capacity),
610                    total_size,
611                    max_alignment: VEC_HEADER_ALIGNMENT,
612                });
613
614                let _ = self.id_to_layout.insert(element_type.id, element_layout);
615
616                storage_type
617            }
618
619            TypeKind::QueueView(element_type) => {
620                let element_layout = self.layout(element_type);
621                let _ = self
622                    .id_to_layout
623                    .insert(element_type.id, element_layout.clone());
624
625                Rc::new(BasicType {
626                    id: BasicTypeId(ty.id.inner()),
627                    kind: BasicTypeKind::DynamicLengthVecView(element_layout),
628                    total_size: PTR_SIZE,
629                    max_alignment: PTR_ALIGNMENT,
630                })
631            }
632
633            TypeKind::Function(_) => {
634                panic!("function types can not be laid out")
635            }
636
637            TypeKind::Unit => create_basic_type(
638                ty.id,
639                BasicTypeKind::Empty,
640                MemorySize(0),
641                MemoryAlignment::U8,
642            ),
643        };
644
645        // Store in both caches
646        let _ = self.id_to_layout.insert(ty.id, basic_type.clone());
647        let _ = self
648            .kind_to_layout
649            .insert((*ty.kind).clone(), basic_type.clone());
650
651        basic_type
652    }
653
654    fn layout_named_struct(
655        &mut self,
656        named_struct_type: &NamedStructType,
657        type_id: TypeId,
658    ) -> BasicTypeRef {
659        // Check if we already have a layout for this kind
660        let struct_kind = TypeKind::NamedStruct(named_struct_type.clone());
661        if let Some(existing_layout) = self.kind_to_layout.get(&struct_kind) {
662            return existing_layout.clone();
663        }
664
665        // Extract AnonymousStructType from the TypeRef
666        let anon_struct = match &*named_struct_type.anon_struct_type.kind {
667            TypeKind::AnonymousStruct(anon_struct) => anon_struct,
668            _ => panic!("Expected AnonymousStruct in NamedStructType"),
669        };
670
671        let inner_struct = self.layout_struct_type(anon_struct, &named_struct_type.assigned_name);
672
673        // Use the provided TypeId
674        let basic_type = Rc::new(BasicType {
675            id: BasicTypeId(type_id.inner()), // Use the provided type ID
676            total_size: inner_struct.total_size,
677            max_alignment: inner_struct.max_alignment,
678            kind: BasicTypeKind::Struct(inner_struct),
679        });
680
681        // Store in kind_to_layout for future deduplication
682        let _ = self.kind_to_layout.insert(struct_kind, basic_type.clone());
683
684        basic_type
685    }
686
687    #[must_use]
688    pub fn layout_struct_type(
689        &mut self,
690        struct_type: &AnonymousStructType,
691        name: &str,
692    ) -> StructType {
693        let mut offset = MemoryOffset(0);
694        let mut max_alignment = MemoryAlignment::U8;
695        let mut items = Vec::with_capacity(struct_type.field_name_sorted_fields.len());
696
697        for (field_name, field_type) in &struct_type.field_name_sorted_fields {
698            // Use layout instead of layout_type to ensure proper caching
699            let field_layout = self.layout(&field_type.field_type);
700            check_type_size(&field_layout, &format!("field {name}::{field_name}"));
701
702            // Make sure the field type is in the id_to_layout map
703            let _ = self
704                .id_to_layout
705                .insert(field_type.field_type.id, field_layout.clone());
706
707            offset = align_to(offset, field_layout.max_alignment);
708
709            items.push(OffsetMemoryItem {
710                offset,
711                size: field_layout.total_size,
712                name: field_name.clone(),
713                ty: field_layout.clone(),
714            });
715
716            offset = offset + field_layout.total_size;
717
718            if field_layout.max_alignment > max_alignment {
719                max_alignment = field_layout.max_alignment;
720            }
721        }
722
723        let total_size = adjust_size_to_alignment(offset.as_size(), max_alignment);
724
725        StructType {
726            name: name.to_string(),
727            fields: items,
728            total_size,
729            max_alignment,
730        }
731    }
732
733    pub fn layout_struct(
734        &mut self,
735        struct_type: &AnonymousStructType,
736        name: &str,
737        type_id: TypeId,
738    ) -> BasicTypeRef {
739        // Always process each field's type to ensure it's in the cache
740        for (_field_name, struct_field) in &struct_type.field_name_sorted_fields {
741            let field_type = &struct_field.field_type;
742            let _field_layout = self.layout(field_type);
743
744            // The layout method already handles storing in both caches
745        }
746
747        // Check if we already have a layout for this kind
748        let struct_kind = TypeKind::AnonymousStruct(struct_type.clone());
749        if let Some(existing_layout) = self.kind_to_layout.get(&struct_kind) {
750            // Store the mapping from this type ID to the existing layout
751            let _ = self.id_to_layout.insert(type_id, existing_layout.clone());
752            return existing_layout.clone();
753        }
754
755        let struct_layout = self.layout_struct_type(struct_type, name);
756
757        // Use the provided type ID
758        let struct_id = type_id;
759
760        let basic_type = Rc::new(BasicType {
761            id: BasicTypeId(struct_id.inner()),
762            total_size: struct_layout.total_size,
763            max_alignment: struct_layout.max_alignment,
764            kind: BasicTypeKind::Struct(struct_layout),
765        });
766
767        // Store in both caches
768        let _ = self.id_to_layout.insert(struct_id, basic_type.clone());
769        let _ = self.kind_to_layout.insert(struct_kind, basic_type.clone());
770
771        basic_type
772    }
773
774    #[must_use]
775    pub fn layout_optional_type(&mut self, inner_type: &TypeRef, type_id: TypeId) -> BasicTypeRef {
776        // Check if we already have a layout for this kind
777        let optional_kind = TypeKind::Optional(inner_type.clone());
778        if let Some(existing_layout) = self.kind_to_layout.get(&optional_kind) {
779            assert!(matches!(&existing_layout.kind, BasicTypeKind::Optional(_)));
780            return existing_layout.clone();
781        }
782
783        // Layout the inner type first
784        let inner_layout = self.layout(inner_type);
785
786        // Store the inner type in both caches
787        let _ = self
788            .id_to_layout
789            .insert(inner_type.id, inner_layout.clone());
790        let _ = self
791            .kind_to_layout
792            .insert((*inner_type.kind).clone(), inner_layout);
793
794        // Create the optional type
795        let optional_union = self.layout_optional_type_items(inner_type);
796
797        // Use the provided type ID
798        let optional_id = type_id;
799
800        let basic_type = Rc::new(BasicType {
801            id: BasicTypeId(optional_id.inner()),
802            total_size: optional_union.total_size,
803            max_alignment: optional_union.max_alignment,
804            kind: BasicTypeKind::Optional(optional_union),
805        });
806
807        // Store in both caches
808        let _ = self.id_to_layout.insert(optional_id, basic_type.clone());
809        let _ = self
810            .kind_to_layout
811            .insert(optional_kind, basic_type.clone());
812
813        basic_type
814    }
815
816    #[must_use]
817    pub fn layout_optional_type_items(&mut self, inner_type: &TypeRef) -> TaggedUnion {
818        let gen_type = self.layout_type(inner_type);
819
820        let payload_tagged_variant = TaggedUnionVariant {
821            name: "Some".to_string(),
822            ty: gen_type,
823        };
824
825        let none_tagged_variant = TaggedUnionVariant {
826            name: "None".to_string(),
827            ty: Rc::new(BasicType {
828                id: BasicTypeId(0),
829                kind: BasicTypeKind::Empty,
830                total_size: MemorySize(0),
831                max_alignment: MemoryAlignment::U8,
832            }),
833        };
834
835        Self::layout_tagged_union(&[none_tagged_variant, payload_tagged_variant], "Maybe")
836    }
837
838    #[must_use]
839    pub fn layout_tuple_items(&mut self, types: &[TypeRef]) -> TupleType {
840        // First pass: Determine maximum alignment requirement
841        let mut max_alignment = MemoryAlignment::U8;
842        for ty in types {
843            // Use layout instead of layout_type to ensure proper caching
844            let elem_layout = self.layout(ty);
845
846            // Make sure the element type is in the id_to_layout map
847            let _ = self.id_to_layout.insert(ty.id, elem_layout.clone());
848
849            if elem_layout.max_alignment > max_alignment {
850                max_alignment = elem_layout.max_alignment;
851            }
852        }
853
854        // Second pass: Layout fields using the maximum alignment
855        let mut offset = MemoryOffset(0);
856        let mut items = Vec::with_capacity(types.len());
857
858        for (i, ty) in types.iter().enumerate() {
859            // Reuse the layout from the cache
860            let elem_layout = self.layout(ty);
861
862            offset = align_to(offset, elem_layout.max_alignment);
863
864            items.push(OffsetMemoryItem {
865                offset,
866                size: elem_layout.total_size,
867                name: i.to_string(),
868                ty: elem_layout.clone(),
869            });
870
871            offset = offset + elem_layout.total_size;
872        }
873
874        // Ensure total size is aligned to max_alignment
875        let total_size = adjust_size_to_alignment(offset.as_size(), max_alignment);
876
877        TupleType {
878            fields: items,
879            total_size,
880            max_alignment,
881        }
882    }
883
884    #[must_use]
885    pub fn layout_tuple(&mut self, types: &[TypeRef], tuple_id: TypeId) -> BasicTypeRef {
886        // Always process each inner type to ensure it's in the cache
887        for ty in types {
888            let inner_layout = self.layout(ty);
889            let _ = self.id_to_layout.insert(ty.id, inner_layout.clone());
890            let _ = self
891                .kind_to_layout
892                .insert((*ty.kind).clone(), inner_layout.clone());
893        }
894
895        // Check if we already have a layout for this kind
896        let tuple_kind = TypeKind::Tuple(types.to_vec());
897        if let Some(existing_layout) = self.kind_to_layout.get(&tuple_kind) {
898            // Store the mapping from this type ID to the existing layout
899            let _ = self.id_to_layout.insert(tuple_id, existing_layout.clone());
900            return existing_layout.clone();
901        }
902
903        let tuple_layout = self.layout_tuple_items(types);
904
905        let basic_type = Rc::new(BasicType {
906            id: BasicTypeId(tuple_id.inner()),
907            total_size: tuple_layout.total_size,
908            max_alignment: tuple_layout.max_alignment,
909            kind: BasicTypeKind::Tuple(tuple_layout),
910        });
911
912        // Store in both caches
913        let _ = self.id_to_layout.insert(tuple_id, basic_type.clone());
914        let _ = self.kind_to_layout.insert(tuple_kind, basic_type.clone());
915
916        basic_type
917    }
918}
919
920fn create_basic_type(
921    type_id: TypeId,
922    kind: BasicTypeKind,
923    size: MemorySize,
924    alignment: MemoryAlignment,
925) -> BasicTypeRef {
926    let basic_type_id = BasicTypeId(type_id.inner());
927
928    Rc::new(BasicType {
929        id: basic_type_id,
930        kind,
931        total_size: size,
932        max_alignment: alignment,
933    })
934}
935
936pub const fn check_type_size(ty: &BasicType, _comment: &str) {
937    if ty.total_size.0 > 1024 * 1024 {
938        //eprintln!("suspicious allocation: {} for {ty}", ty.total_size);
939    }
940}