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