swamp_vm_layout/
lib.rs

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