Skip to main content

wasmtime_environ/gc/
copying.rs

1//! Layout of Wasm GC objects in the copying garbage collector.
2
3use super::*;
4use core::mem;
5
6/// The size of the `VMCopyingHeader` header for GC objects.
7///
8/// Note: This is 16 (not 12) because `VMGcHeader` has `align(8)`, so the
9/// `repr(C)` struct has 4 bytes of trailing padding after the `object_size`
10/// field.
11pub const HEADER_SIZE: u32 = 16;
12
13/// The alignment of all GC objects in the copying collector.
14///
15/// All objects and all layouts must be a multiple of this alignment.
16pub const ALIGN: u32 = 16;
17
18/// The offset of the length field in a `VMCopyingArrayHeader`.
19pub const ARRAY_LENGTH_OFFSET: u32 = HEADER_SIZE;
20
21/// The offset of the tag-instance-index field in an exception header.
22pub const EXCEPTION_TAG_INSTANCE_OFFSET: u32 = HEADER_SIZE;
23
24/// The offset of the tag-defined-index field in an exception header.
25pub const EXCEPTION_TAG_DEFINED_OFFSET: u32 = HEADER_SIZE + 4;
26
27/// The bit within a `VMCopyingHeader`'s reserved bits that represents whether,
28/// during a collection, the object has already been copied into the new
29/// semi-space.
30pub const HEADER_COPIED_BIT: u32 = 1 << 0;
31
32/// The offset within this GC object, which must have the `HEADER_COPIED_BIT`
33/// set and must reside within the old semi-space, where the new copy of this
34/// object is located within the new semi-space.
35pub const FORWARDING_REF_OFFSET: u32 = HEADER_SIZE;
36
37/// The minimum object size: every object must have enough room for the
38/// forwarding reference that the copying collector writes during collection.
39pub const MIN_OBJECT_SIZE: u32 = FORWARDING_REF_OFFSET + mem::size_of::<u32>() as u32;
40
41/// The layout of Wasm GC objects in the copying collector.
42#[derive(Default)]
43pub struct CopyingTypeLayouts;
44
45/// The inline trace info encoded in a `VMCopyingHeader`'s reserved bits.
46#[derive(Clone, Copy, Debug)]
47pub enum InlineTraceInfo {
48    /// The trace info is not encoded inline. Look it up from `TraceInfos`.
49    OutOfLine,
50
51    /// Inline trace info for an array type.
52    Array {
53        /// Whether the array's elements are GC references that need tracing.
54        elems_are_gc_refs: bool,
55    },
56
57    /// Inline trace info for a struct, exception, or externref type.
58    Struct {
59        /// A bitmap where the `i`th bit is set iff the `i`th `u32` in the
60        /// object's data (after the header) is a `VMGcRef` that needs
61        /// tracing. Only the lower 23 bits are meaningful.
62        gc_ref_bitmap: u32,
63    },
64}
65
66impl InlineTraceInfo {
67    const IS_INLINE_BIT: u32 = 1 << 1;
68    const IS_ARRAY_BIT: u32 = 1 << 2;
69    const ELEMS_ARE_GC_REFS_BIT: u32 = 1 << 3;
70    const STRUCT_BITMAP_SHIFT: u32 = 3;
71    const STRUCT_BITMAP_BITS: u32 = 23;
72
73    /// Inline trace info for an object with no GC-reference edges (e.g.
74    /// externrefs). This is a `Struct` with an empty bitmap.
75    pub const NO_GC_EDGES: InlineTraceInfo = InlineTraceInfo::Struct { gc_ref_bitmap: 0 };
76
77    /// Create inline trace info for the given GC layout.
78    pub fn new(layout: &GcLayout) -> Self {
79        match layout {
80            GcLayout::Array(a) => Self::array(a),
81            GcLayout::Struct(s) => Self::r#struct(s),
82        }
83    }
84
85    /// Create inline trace info for an array type.
86    ///
87    /// Arrays can always be represented inline (only one bit of payload is
88    /// needed), so this never returns `OutOfLine`.
89    pub fn array(layout: &GcArrayLayout) -> Self {
90        InlineTraceInfo::Array {
91            elems_are_gc_refs: layout.elems_are_gc_refs,
92        }
93    }
94
95    /// Create inline trace info for a struct or exception type.
96    ///
97    /// Returns `OutOfLine` only when there is a GC-reference field whose
98    /// offset cannot be represented in the 23-bit bitmap. Non-GC-reference
99    /// fields are ignored regardless of offset.
100    pub fn r#struct(layout: &GcStructLayout) -> Self {
101        let mut bitmap: u32 = 0;
102        for f in layout.fields.iter() {
103            if !f.is_gc_ref {
104                continue;
105            }
106            let Some(data_offset) = f.offset.checked_sub(HEADER_SIZE) else {
107                return Self::OutOfLine;
108            };
109            if data_offset % 4 != 0 {
110                return Self::OutOfLine;
111            }
112            let slot = data_offset / 4;
113            if slot >= Self::STRUCT_BITMAP_BITS {
114                return Self::OutOfLine;
115            }
116            bitmap |= 1u32 << slot;
117        }
118        InlineTraceInfo::Struct {
119            gc_ref_bitmap: bitmap,
120        }
121    }
122
123    /// Encode this inline trace info as its bit-packed representation for
124    /// storage in a `VMGcHeader`'s reserved bits.
125    pub fn encode(&self) -> u32 {
126        match self {
127            Self::OutOfLine => 0,
128            Self::Array { elems_are_gc_refs } => {
129                Self::IS_INLINE_BIT
130                    | Self::IS_ARRAY_BIT
131                    | if *elems_are_gc_refs {
132                        Self::ELEMS_ARE_GC_REFS_BIT
133                    } else {
134                        0
135                    }
136            }
137            Self::Struct { gc_ref_bitmap } => {
138                Self::IS_INLINE_BIT | (*gc_ref_bitmap << Self::STRUCT_BITMAP_SHIFT)
139            }
140        }
141    }
142
143    /// Decode inline trace info from the reserved bits of a `VMGcHeader`.
144    pub fn decode(reserved: u32) -> Self {
145        if reserved & Self::IS_INLINE_BIT == 0 {
146            return Self::OutOfLine;
147        }
148        if reserved & Self::IS_ARRAY_BIT != 0 {
149            Self::Array {
150                elems_are_gc_refs: reserved & Self::ELEMS_ARE_GC_REFS_BIT != 0,
151            }
152        } else {
153            Self::Struct {
154                gc_ref_bitmap: (reserved >> Self::STRUCT_BITMAP_SHIFT)
155                    & ((1u32 << Self::STRUCT_BITMAP_BITS) - 1),
156            }
157        }
158    }
159}
160
161impl GcTypeLayouts for CopyingTypeLayouts {
162    fn array_length_field_offset(&self) -> u32 {
163        ARRAY_LENGTH_OFFSET
164    }
165
166    fn exception_tag_instance_offset(&self) -> u32 {
167        EXCEPTION_TAG_INSTANCE_OFFSET
168    }
169
170    fn exception_tag_defined_offset(&self) -> u32 {
171        EXCEPTION_TAG_DEFINED_OFFSET
172    }
173
174    fn array_layout(&self, ty: &WasmArrayType) -> GcArrayLayout {
175        let mut layout = common_array_layout(ty, HEADER_SIZE, ALIGN, ARRAY_LENGTH_OFFSET);
176        debug_assert!(layout.align <= ALIGN);
177        layout.align = ALIGN;
178        debug_assert!(layout.base_size >= MIN_OBJECT_SIZE);
179        layout
180    }
181
182    fn struct_layout(&self, ty: &WasmStructType) -> Result<GcStructLayout, OutOfMemory> {
183        let mut layout = common_struct_layout(ty, HEADER_SIZE, ALIGN)?;
184        // Ensure there is always space for the forwarding reference, even if
185        // the struct has no fields.
186        if layout.size < MIN_OBJECT_SIZE {
187            layout.size = MIN_OBJECT_SIZE;
188        }
189        layout.size = layout.size.next_multiple_of(ALIGN);
190        debug_assert!(layout.align <= ALIGN);
191        layout.align = ALIGN;
192        debug_assert!(layout.size >= MIN_OBJECT_SIZE);
193        Ok(layout)
194    }
195
196    fn exn_layout(&self, ty: &WasmExnType) -> Result<GcStructLayout, OutOfMemory> {
197        let mut layout = common_exn_layout(ty, HEADER_SIZE, ALIGN)?;
198        layout.size = layout.size.next_multiple_of(ALIGN);
199        debug_assert!(layout.align <= ALIGN);
200        layout.align = ALIGN;
201        debug_assert!(layout.size >= MIN_OBJECT_SIZE);
202        Ok(layout)
203    }
204}