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