swamp_vm_layout/
lib.rs

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