wasmtime_internal_cranelift/func_environ/
gc.rs

1//! Interface to compiling GC-related things.
2//!
3//! This module and its interface are implemented twice: once when the `gc`
4//! cargo feature is enabled and once when the feature is disabled. The goal is
5//! to have just a single `cfg(feature = "gc")` for the whole crate, which
6//! selects between these two implementations.
7
8use crate::func_environ::FuncEnvironment;
9use cranelift_codegen::ir;
10use cranelift_frontend::FunctionBuilder;
11use wasmtime_environ::{GcTypeLayouts, TagIndex, TypeIndex, WasmRefType, WasmResult};
12
13#[cfg(feature = "gc")]
14mod enabled;
15#[cfg(feature = "gc")]
16use enabled as imp;
17
18#[cfg(not(feature = "gc"))]
19mod disabled;
20#[cfg(not(feature = "gc"))]
21use disabled as imp;
22
23// Re-export the GC compilation interface from the implementation that we chose
24// based on the compile-time features enabled.
25pub use imp::*;
26
27/// How to initialize a newly-allocated array's elements.
28#[derive(Clone, Copy)]
29#[cfg_attr(not(feature = "gc"), expect(dead_code))]
30pub enum ArrayInit<'a> {
31    /// Initialize the array's elements with the given values.
32    Elems(&'a [ir::Value]),
33
34    /// Initialize the array's elements with `elem` repeated `len` times.
35    Fill { elem: ir::Value, len: ir::Value },
36}
37
38/// A trait for different collectors to emit any GC barriers they might require.
39pub trait GcCompiler {
40    /// Get the GC type layouts for this GC compiler.
41    #[cfg_attr(not(feature = "gc"), allow(dead_code))]
42    fn layouts(&self) -> &dyn GcTypeLayouts;
43
44    /// Emit code to allocate a new array.
45    ///
46    /// The array should be of the given type and its elements initialized as
47    /// described by the given `ArrayInit`.
48    #[cfg_attr(not(feature = "gc"), allow(dead_code))]
49    fn alloc_array(
50        &mut self,
51        func_env: &mut FuncEnvironment<'_>,
52        builder: &mut FunctionBuilder<'_>,
53        array_type_index: TypeIndex,
54        init: ArrayInit<'_>,
55    ) -> WasmResult<ir::Value>;
56
57    /// Emit code to allocate a new struct.
58    ///
59    /// The struct should be of the given type and its fields initialized to the
60    /// given values.
61    #[cfg_attr(not(feature = "gc"), allow(dead_code))]
62    fn alloc_struct(
63        &mut self,
64        func_env: &mut FuncEnvironment<'_>,
65        builder: &mut FunctionBuilder<'_>,
66        struct_type_index: TypeIndex,
67        fields: &[ir::Value],
68    ) -> WasmResult<ir::Value>;
69
70    /// Emit code to allocate a new exception object.
71    ///
72    /// The exception object should be of the given type and its
73    /// fields initialized to the given values. The tag field is left
74    /// uninitialized; that is the responsibility of generated code to
75    /// fill in. `tag_index` is used only to look up the appropriate
76    /// exception object type.
77    #[cfg_attr(not(feature = "gc"), allow(dead_code))]
78    fn alloc_exn(
79        &mut self,
80        func_env: &mut FuncEnvironment<'_>,
81        builder: &mut FunctionBuilder<'_>,
82        tag_index: TagIndex,
83        fields: &[ir::Value],
84        instance_id: ir::Value,
85        tag: ir::Value,
86    ) -> WasmResult<ir::Value>;
87
88    /// Emit a read barrier for when we are cloning a GC reference onto the Wasm
89    /// stack.
90    ///
91    /// This is used, for example, when reading from a global or a table
92    /// element.
93    ///
94    /// In pseudocode, this is the following operation:
95    ///
96    /// ```ignore
97    /// x = *src;
98    /// ```
99    ///
100    /// Parameters:
101    ///
102    /// * `func_env`: The function environment that this GC compiler is
103    ///   operating within.
104    ///
105    /// * `builder`: Function builder. Currently at the position where the read
106    ///   should be inserted. Upon return, should be positioned where control
107    ///   continues just after the read completes. Any intermediate blocks
108    ///   created in the process of emitting the read barrier should be added to
109    ///   the layout and sealed.
110    ///
111    /// * `ty`: The Wasm reference type that is being read.
112    ///
113    /// * `src`: A pointer to the GC reference that should be read; this is an
114    ///   instance of a `*mut Option<VMGcRef>`.
115    ///
116    /// * `flags`: The memory flags that should be used when accessing `src`.
117    ///
118    /// This method should return the cloned GC reference (an instance of
119    /// `VMGcRef`) of type `i32`.
120    fn translate_read_gc_reference(
121        &mut self,
122        func_env: &mut FuncEnvironment<'_>,
123        builder: &mut FunctionBuilder,
124        ty: WasmRefType,
125        src: ir::Value,
126        flags: ir::MemFlags,
127    ) -> WasmResult<ir::Value>;
128
129    /// Emit a write barrier for when we are writing a GC reference over another
130    /// GC reference.
131    ///
132    /// This is used, for example, when writing to a global or a table element.
133    ///
134    /// In pseudocode, this is the following operation:
135    ///
136    /// ```ignore
137    /// *dst = new_val;
138    /// ```
139    ///
140    /// Parameters:
141    ///
142    /// * `func_env`: The function environment that this GC compiler is
143    ///   operating within.
144    ///
145    /// * `builder`: Function builder. Currently at the position where the write
146    ///   should be inserted. Upon return, should be positioned where control
147    ///   continues just after the write completes. Any intermediate blocks
148    ///   created in the process of emitting the read barrier should be added to
149    ///   the layout and sealed.
150    ///
151    /// * `ty`: The Wasm reference type that is being written.
152    ///
153    /// * `dst`: A pointer to the GC reference that will be overwritten; note
154    ///   that is this is an instance of a `*mut VMGcRef`, *not* a `VMGcRef`
155    ///   itself or a `*mut VMGcHeader`!
156    ///
157    /// * `new_val`: The new value that should be written into `dst`. This is a
158    ///   `VMGcRef` of Cranelift type `i32`; not a `*mut VMGcRef`.
159    ///
160    /// * `flags`: The memory flags that should be used when accessing `dst`.
161    fn translate_write_gc_reference(
162        &mut self,
163        func_env: &mut FuncEnvironment<'_>,
164        builder: &mut FunctionBuilder,
165        ty: WasmRefType,
166        dst: ir::Value,
167        new_val: ir::Value,
168        flags: ir::MemFlags,
169    ) -> WasmResult<()>;
170}
171
172pub mod builtins {
173    use super::*;
174
175    macro_rules! define_builtin_accessors {
176        ( $( $name:ident , )* ) => {
177            $(
178                #[inline]
179                pub fn $name(
180                    func_env: &mut FuncEnvironment<'_>,
181                    func: &mut ir::Function,
182                ) -> WasmResult<ir::FuncRef> {
183                    #[cfg(feature = "gc")]
184                    {
185                        func_env.needs_gc_heap = true;
186                        return Ok(func_env.builtin_functions.$name(func));
187                    }
188
189                    #[cfg(not(feature = "gc"))]
190                    {
191                        let _ = (func, func_env);
192                        return Err(wasmtime_environ::wasm_unsupported!(
193                            "support for Wasm GC disabled at compile time because the `gc` cargo \
194                             feature was not enabled"
195                        ));
196                    }
197                }
198            )*
199        };
200    }
201
202    define_builtin_accessors! {
203        table_grow_gc_ref,
204        table_fill_gc_ref,
205        array_new_data,
206        array_new_elem,
207        array_copy,
208        array_init_data,
209        array_init_elem,
210    }
211}