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_types::types::{
14    BasicType, BasicTypeId, BasicTypeKind, BasicTypeRef, OffsetMemoryItem, StructType, TaggedUnion,
15    TaggedUnionVariant, TupleType,
16};
17use swamp_vm_types::{
18    ANY_HEADER_ALIGNMENT, ANY_HEADER_SIZE, CountU16, GRID_HEADER_ALIGNMENT, GRID_HEADER_SIZE,
19    MAP_HEADER_ALIGNMENT, MemoryAlignment, MemoryOffset, MemorySize, PTR_ALIGNMENT, PTR_SIZE,
20    STRING_PTR_ALIGNMENT, STRING_PTR_SIZE, VEC_HEADER_ALIGNMENT, VEC_HEADER_SIZE,
21    adjust_size_to_alignment, align_to,
22};
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::Byte => {
295                create_basic_type(ty.id, BasicTypeKind::U8, MemorySize(1), MemoryAlignment::U8)
296            }
297            TypeKind::Codepoint => create_basic_type(
298                ty.id,
299                BasicTypeKind::U32,
300                MemorySize(4),
301                MemoryAlignment::U32,
302            ),
303            TypeKind::Int => create_basic_type(
304                ty.id,
305                BasicTypeKind::S32,
306                MemorySize(4),
307                MemoryAlignment::U32,
308            ),
309
310            TypeKind::Float => create_basic_type(
311                ty.id,
312                BasicTypeKind::Fixed32,
313                MemorySize(4),
314                MemoryAlignment::U32,
315            ),
316
317            TypeKind::Bool => {
318                create_basic_type(ty.id, BasicTypeKind::B8, MemorySize(1), MemoryAlignment::U8)
319            }
320
321            TypeKind::String(byte, char) => create_basic_type(
322                ty.id,
323                BasicTypeKind::StringView {
324                    byte: self.layout(byte),
325                    char: self.layout(char),
326                },
327                STRING_PTR_SIZE,
328                STRING_PTR_ALIGNMENT,
329            ),
330
331            TypeKind::AnonymousStruct(struct_type) => {
332                self.layout_struct(struct_type, "anonymous", ty.id)
333            }
334
335            TypeKind::NamedStruct(named_struct) => self.layout_named_struct(named_struct, ty.id),
336
337            TypeKind::Tuple(tuple_types) => self.layout_tuple(tuple_types, ty.id),
338
339            TypeKind::Optional(inner_type) => self.layout_optional_type(inner_type, ty.id),
340
341            TypeKind::Enum(enum_type) => {
342                let variants = enum_type.variants.values().cloned().collect::<Vec<_>>();
343                self.layout_enum(
344                    &enum_type.assigned_name,
345                    &variants,
346                    ty.id,
347                    enum_type.symbol_id,
348                )
349            }
350
351            TypeKind::FixedCapacityAndLengthArray(element_type, capacity) => {
352                let (element_layout, total_size, max_alignment) =
353                    self.layout_vec_like(element_type, *capacity);
354
355                let array_type = Rc::new(BasicType {
356                    id: BasicTypeId(ty.id.inner()),
357                    kind: BasicTypeKind::FixedCapacityArray(element_layout.clone(), *capacity),
358                    total_size,
359                    max_alignment,
360                });
361
362                // Also store the element type in id_to_layout
363                self.insert_layout(element_type.id, element_layout);
364
365                array_type
366            }
367
368            TypeKind::StringStorage(byte_type, char_type, capacity) => {
369                let (element_layout, total_size, max_alignment) =
370                    self.layout_vec_like(byte_type, *capacity);
371
372                let array_type = Rc::new(BasicType {
373                    id: BasicTypeId(ty.id.inner()),
374                    kind: BasicTypeKind::StringStorage {
375                        element_type: element_layout.clone(),
376                        char: self.layout(char_type),
377                        capacity: *capacity,
378                    },
379                    total_size,
380                    max_alignment,
381                });
382
383                // Also store the element type in id_to_layout
384                self.insert_layout(byte_type.id, element_layout);
385
386                array_type
387            }
388
389            TypeKind::Any => create_basic_type(
390                ty.id,
391                BasicTypeKind::Any,
392                ANY_HEADER_SIZE,
393                ANY_HEADER_ALIGNMENT,
394            ),
395
396            TypeKind::DynamicLengthVecView(element_type) => {
397                let (element_layout, _, _) = self.layout_vec_like(element_type, 0);
398
399                let vec_type = Rc::new(BasicType {
400                    id: BasicTypeId(ty.id.inner()),
401                    kind: BasicTypeKind::DynamicLengthVecView(element_layout.clone()),
402                    total_size: PTR_SIZE,
403                    max_alignment: PTR_ALIGNMENT,
404                });
405
406                self.insert_layout(element_type.id, element_layout);
407
408                vec_type
409            }
410
411            TypeKind::VecStorage(element_type, capacity) => {
412                let (element_layout, total_size, element_alignment) =
413                    self.layout_vec_like(element_type, *capacity);
414
415                let storage_type = Rc::new(BasicType {
416                    id: BasicTypeId(ty.id.inner()),
417                    kind: BasicTypeKind::VecStorage(element_layout.clone(), *capacity),
418                    total_size,
419                    max_alignment: max(VEC_HEADER_ALIGNMENT, element_alignment),
420                });
421
422                self.insert_layout(element_type.id, element_layout);
423
424                storage_type
425            }
426
427            TypeKind::DynamicLengthMapView(key_type, value_type) => {
428                // Layout key type
429                let key_layout = self.layout(key_type);
430                self.insert_layout(key_type.id, key_layout.clone());
431
432                // Layout value type
433                let value_layout = self.layout(value_type);
434                self.insert_layout(value_type.id, value_layout.clone());
435
436                let key_item = OffsetMemoryItem {
437                    offset: MemoryOffset(0),
438                    size: key_layout.total_size,
439                    name: "key".to_string(),
440                    ty: key_layout.clone(),
441                };
442
443                let value_offset = align_to(
444                    MemoryOffset(key_layout.total_size.0),
445                    value_layout.max_alignment,
446                );
447
448                let value_item = OffsetMemoryItem {
449                    offset: value_offset,
450                    size: value_layout.total_size,
451                    name: "value".to_string(),
452                    ty: value_layout.clone(),
453                };
454
455                Rc::new(BasicType {
456                    id: BasicTypeId(ty.id.inner()),
457                    kind: BasicTypeKind::DynamicLengthMapView(
458                        Box::new(key_item),
459                        Box::new(value_item),
460                    ),
461                    total_size: PTR_SIZE,
462                    max_alignment: PTR_ALIGNMENT,
463                })
464            }
465
466            TypeKind::MapStorage(key_type, value_type, logical_limit) => {
467                // Layout key type
468                let key_layout = self.layout(key_type);
469                self.insert_layout(key_type.id, key_layout.clone());
470
471                // Layout value type
472                let value_layout = self.layout(value_type);
473                self.insert_layout(value_type.id, value_layout.clone());
474
475                let logical_limit = *logical_limit;
476
477                let (_bucket_layout, map_init) = hashmap_mem::layout(
478                    key_layout.total_size.0,
479                    key_layout.max_alignment.into(),
480                    value_layout.total_size.0,
481                    value_layout.max_alignment.into(),
482                    logical_limit as u16,
483                );
484                let total_size = MemorySize(map_init.total_size);
485
486                Rc::new(BasicType {
487                    id: BasicTypeId(ty.id.inner()),
488                    kind: BasicTypeKind::MapStorage {
489                        key_type: key_layout,
490                        logical_limit,
491                        capacity: CountU16(map_init.capacity),
492                        value_type: value_layout,
493                    },
494                    total_size,
495                    max_alignment: MAP_HEADER_ALIGNMENT,
496                })
497            }
498
499            TypeKind::Range(_range_struct) => create_basic_type(
500                ty.id,
501                BasicTypeKind::InternalRangeHeader,
502                MemorySize(12),
503                MemoryAlignment::U32,
504            ),
505
506            TypeKind::GridView(element_type) => {
507                let element_layout = self.layout(element_type);
508                self.insert_layout(element_type.id, element_layout.clone());
509
510                Rc::new(BasicType {
511                    id: BasicTypeId(ty.id.inner()),
512                    kind: BasicTypeKind::GridView(element_layout),
513                    total_size: PTR_SIZE,
514                    max_alignment: PTR_ALIGNMENT,
515                })
516            }
517
518            TypeKind::GridStorage(element_type, width, height) => {
519                let element_layout = self.layout(element_type);
520                self.insert_layout(element_type.id, element_layout.clone());
521
522                let element_size = element_layout.total_size;
523                let element_alignment = element_layout.max_alignment;
524
525                Rc::new(BasicType {
526                    id: BasicTypeId(ty.id.inner()),
527                    kind: BasicTypeKind::GridStorage(element_layout, *width, *height),
528                    total_size: MemorySize(
529                        GRID_HEADER_SIZE.0 + element_size.0 * (*width as u32) * (*height as u32),
530                    ),
531                    max_alignment: max(GRID_HEADER_ALIGNMENT, element_alignment),
532                })
533            }
534
535            TypeKind::SliceView(element_type) => {
536                let element_layout = self.layout(element_type);
537                self.insert_layout(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                self.insert_layout(element_type.id, element_layout.clone());
562
563                Rc::new(BasicType {
564                    id: BasicTypeId(ty.id.inner()),
565                    kind: BasicTypeKind::SparseView(element_layout),
566                    total_size: PTR_SIZE,
567                    max_alignment: PTR_ALIGNMENT,
568                })
569            }
570
571            TypeKind::StackStorage(element_type, capacity) => {
572                let (element_layout, total_size, _max_alignment) =
573                    self.layout_vec_like(element_type, *capacity);
574
575                let storage_type = Rc::new(BasicType {
576                    id: BasicTypeId(ty.id.inner()),
577                    kind: BasicTypeKind::StackStorage(element_layout.clone(), *capacity),
578                    total_size,
579                    max_alignment: VEC_HEADER_ALIGNMENT,
580                });
581
582                self.insert_layout(element_type.id, element_layout);
583
584                storage_type
585            }
586
587            TypeKind::StackView(element_type) => {
588                let element_layout = self.layout(element_type);
589                self.insert_layout(element_type.id, element_layout.clone());
590
591                Rc::new(BasicType {
592                    id: BasicTypeId(ty.id.inner()),
593                    kind: BasicTypeKind::DynamicLengthVecView(element_layout),
594                    total_size: PTR_SIZE,
595                    max_alignment: PTR_ALIGNMENT,
596                })
597            }
598
599            TypeKind::QueueStorage(element_type, capacity) => {
600                let (element_layout, total_size, _max_alignment) =
601                    self.layout_vec_like(element_type, *capacity);
602
603                let storage_type = Rc::new(BasicType {
604                    id: BasicTypeId(ty.id.inner()),
605                    kind: BasicTypeKind::QueueStorage(element_layout.clone(), *capacity),
606                    total_size,
607                    max_alignment: VEC_HEADER_ALIGNMENT,
608                });
609
610                self.insert_layout(element_type.id, element_layout);
611
612                storage_type
613            }
614
615            TypeKind::QueueView(element_type) => {
616                let element_layout = self.layout(element_type);
617                self.insert_layout(element_type.id, element_layout.clone());
618
619                Rc::new(BasicType {
620                    id: BasicTypeId(ty.id.inner()),
621                    kind: BasicTypeKind::DynamicLengthVecView(element_layout),
622                    total_size: PTR_SIZE,
623                    max_alignment: PTR_ALIGNMENT,
624                })
625            }
626
627            TypeKind::Function(_) => {
628                panic!("function types can not be laid out")
629            }
630
631            TypeKind::Unit => create_basic_type(
632                ty.id,
633                BasicTypeKind::Empty,
634                MemorySize(0),
635                MemoryAlignment::U8,
636            ),
637        };
638
639        // Store in both caches
640        self.insert_layout(ty.id, basic_type.clone());
641        let _ = self
642            .kind_to_layout
643            .insert((*ty.kind).clone(), basic_type.clone());
644
645        basic_type
646    }
647
648    fn layout_named_struct(
649        &mut self,
650        named_struct_type: &NamedStructType,
651        type_id: TypeId,
652    ) -> BasicTypeRef {
653        // Check if we already have a layout for this kind
654        let struct_kind = TypeKind::NamedStruct(named_struct_type.clone());
655        if let Some(existing_layout) = self.kind_to_layout.get(&struct_kind) {
656            return existing_layout.clone();
657        }
658
659        // Extract AnonymousStructType from the TypeRef
660        let anon_struct = match &*named_struct_type.anon_struct_type.kind {
661            TypeKind::AnonymousStruct(anon_struct) => anon_struct,
662            _ => panic!("Expected AnonymousStruct in NamedStructType"),
663        };
664
665        let inner_struct = self.layout_struct_type(anon_struct, &named_struct_type.assigned_name);
666
667        // Use the provided TypeId
668        let basic_type = Rc::new(BasicType {
669            id: BasicTypeId(type_id.inner()), // Use the provided type ID
670            total_size: inner_struct.total_size,
671            max_alignment: inner_struct.max_alignment,
672            kind: BasicTypeKind::Struct(inner_struct),
673        });
674
675        // Store in kind_to_layout for future deduplication
676        let _ = self.kind_to_layout.insert(struct_kind, basic_type.clone());
677
678        basic_type
679    }
680
681    #[must_use]
682    pub fn layout_struct_type(
683        &mut self,
684        struct_type: &AnonymousStructType,
685        name: &str,
686    ) -> StructType {
687        // First pass: Layout all field types and determine maximum alignment requirement
688        let mut field_layouts = Vec::with_capacity(struct_type.field_name_sorted_fields.len());
689        let mut max_alignment = MemoryAlignment::U8;
690
691        for (field_name, field_type) in &struct_type.field_name_sorted_fields {
692            // Use layout instead of layout_type to ensure proper caching
693            let field_layout = self.layout(&field_type.field_type);
694            check_type_size(&field_layout, &format!("field {name}::{field_name}"));
695
696            // Make sure the field type is in the id_to_layout map
697            self.insert_layout(field_type.field_type.id, field_layout.clone());
698
699            if field_layout.max_alignment > max_alignment {
700                max_alignment = field_layout.max_alignment;
701            }
702
703            field_layouts.push((field_name.clone(), field_layout));
704        }
705
706        // Second pass: Layout fields using the maximum alignment
707        let mut offset = MemoryOffset(0);
708        let mut items = Vec::with_capacity(field_layouts.len());
709
710        for (field_name, field_layout) in field_layouts {
711            offset = align_to(offset, field_layout.max_alignment);
712
713            items.push(OffsetMemoryItem {
714                offset,
715                size: field_layout.total_size,
716                name: field_name,
717                ty: field_layout.clone(),
718            });
719
720            offset = offset + field_layout.total_size;
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).cloned() {
750            // Store the mapping from this type ID to the existing layout
751            self.insert_layout(type_id, existing_layout.clone());
752            return existing_layout;
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        self.insert_layout(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        self.insert_layout(inner_type.id, inner_layout.clone());
788        let _ = self
789            .kind_to_layout
790            .insert((*inner_type.kind).clone(), inner_layout);
791
792        // Create the optional type
793        let optional_union = self.layout_optional_type_items(inner_type);
794
795        // Use the provided type ID
796        let optional_id = type_id;
797
798        let basic_type = Rc::new(BasicType {
799            id: BasicTypeId(optional_id.inner()),
800            total_size: optional_union.total_size,
801            max_alignment: optional_union.max_alignment,
802            kind: BasicTypeKind::Optional(optional_union),
803        });
804
805        // Store in both caches
806        self.insert_layout(optional_id, basic_type.clone());
807        let _ = self
808            .kind_to_layout
809            .insert(optional_kind, basic_type.clone());
810
811        basic_type
812    }
813
814    #[must_use]
815    pub fn layout_optional_type_items(&mut self, inner_type: &TypeRef) -> TaggedUnion {
816        let gen_type = self.layout_type(inner_type);
817
818        let payload_tagged_variant = TaggedUnionVariant {
819            name: "Some".to_string(),
820            ty: gen_type,
821        };
822
823        let none_tagged_variant = TaggedUnionVariant {
824            name: "None".to_string(),
825            ty: Rc::new(BasicType {
826                id: BasicTypeId(0),
827                kind: BasicTypeKind::Empty,
828                total_size: MemorySize(0),
829                max_alignment: MemoryAlignment::U8,
830            }),
831        };
832
833        Self::layout_tagged_union(&[none_tagged_variant, payload_tagged_variant], "Maybe")
834    }
835
836    #[must_use]
837    pub fn layout_tuple_items(&mut self, types: &[TypeRef]) -> TupleType {
838        // First pass: Determine maximum alignment requirement
839        let mut max_alignment = MemoryAlignment::U8;
840        for ty in types {
841            // Use layout instead of layout_type to ensure proper caching
842            let elem_layout = self.layout(ty);
843
844            // Make sure the element type is in the id_to_layout map
845            self.insert_layout(ty.id, elem_layout.clone());
846
847            if elem_layout.max_alignment > max_alignment {
848                max_alignment = elem_layout.max_alignment;
849            }
850        }
851
852        // Second pass: Layout fields using the maximum alignment
853        let mut offset = MemoryOffset(0);
854        let mut items = Vec::with_capacity(types.len());
855
856        for (i, ty) in types.iter().enumerate() {
857            // Reuse the layout from the cache
858            let elem_layout = self.layout(ty);
859
860            offset = align_to(offset, elem_layout.max_alignment);
861
862            items.push(OffsetMemoryItem {
863                offset,
864                size: elem_layout.total_size,
865                name: i.to_string(),
866                ty: elem_layout.clone(),
867            });
868
869            offset = offset + elem_layout.total_size;
870        }
871
872        // Ensure total size is aligned to max_alignment
873        let total_size = adjust_size_to_alignment(offset.as_size(), max_alignment);
874
875        TupleType {
876            fields: items,
877            total_size,
878            max_alignment,
879        }
880    }
881
882    #[must_use]
883    pub fn layout_tuple(&mut self, types: &[TypeRef], tuple_id: TypeId) -> BasicTypeRef {
884        // Always process each inner type to ensure it's in the cache
885        for ty in types {
886            let inner_layout = self.layout(ty);
887            self.insert_layout(ty.id, inner_layout.clone());
888            let _ = self
889                .kind_to_layout
890                .insert((*ty.kind).clone(), inner_layout.clone());
891        }
892
893        // Check if we already have a layout for this kind
894        let tuple_kind = TypeKind::Tuple(types.to_vec());
895        if let Some(existing_layout) = self.kind_to_layout.get(&tuple_kind).cloned() {
896            // Store the mapping from this type ID to the existing layout
897            self.insert_layout(tuple_id, existing_layout.clone());
898            return existing_layout;
899        }
900
901        let tuple_layout = self.layout_tuple_items(types);
902
903        let basic_type = Rc::new(BasicType {
904            id: BasicTypeId(tuple_id.inner()),
905            total_size: tuple_layout.total_size,
906            max_alignment: tuple_layout.max_alignment,
907            kind: BasicTypeKind::Tuple(tuple_layout),
908        });
909
910        // Store in both caches
911        self.insert_layout(tuple_id, basic_type.clone());
912        let _ = self.kind_to_layout.insert(tuple_kind, basic_type.clone());
913
914        basic_type
915    }
916
917    /// Helper method to insert a layout into all relevant maps
918    fn insert_layout(&mut self, type_id: TypeId, layout: BasicTypeRef) {
919        let _ = self.id_to_layout.insert(type_id, layout.clone());
920
921        // Calculate universal hashes and store them
922        let universal_hash = layout.universal_hash_u64();
923        let _ = self
924            .universal_id_to_layout
925            .insert(universal_hash, layout.clone());
926        let _ = self
927            .universal_short_id_to_layout
928            .insert(universal_hash as u32, layout);
929    }
930
931    #[must_use]
932    pub fn universal_short_id(&self, short_id: u32) -> &BasicTypeRef {
933        if let Some(x) = self.universal_short_id_to_layout.get(&short_id) {
934            x
935        } else {
936            for (hash, ty) in &self.universal_short_id_to_layout {
937                eprintln!("{hash:X} ({hash}) {}", ty.kind);
938            }
939            panic!("not found")
940        }
941    }
942}
943
944fn create_basic_type(
945    type_id: TypeId,
946    kind: BasicTypeKind,
947    size: MemorySize,
948    alignment: MemoryAlignment,
949) -> BasicTypeRef {
950    let basic_type_id = BasicTypeId(type_id.inner());
951
952    Rc::new(BasicType {
953        id: basic_type_id,
954        kind,
955        total_size: size,
956        max_alignment: alignment,
957    })
958}
959
960pub const fn check_type_size(ty: &BasicType, _comment: &str) {
961    if ty.total_size.0 > 1024 * 1024 {
962        //eprintln!("suspicious allocation: {} for {ty}", ty.total_size);
963    }
964}