swamp_vm_layout/
lib.rs

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