wasmtime_environ/
builtin.rs

1/// Helper macro to iterate over all builtin functions and their signatures.
2#[macro_export]
3macro_rules! foreach_builtin_function {
4    ($mac:ident) => {
5        $mac! {
6            // Returns an index for wasm's `memory.grow` builtin function.
7            memory32_grow(vmctx: vmctx, delta: u64, index: u32) -> pointer;
8            // Returns an index for wasm's `table.copy` when both tables are locally
9            // defined.
10            table_copy(vmctx: vmctx, dst_index: u32, src_index: u32, dst: u64, src: u64, len: u64) -> bool;
11            // Returns an index for wasm's `table.init`.
12            table_init(vmctx: vmctx, table: u32, elem: u32, dst: u64, src: u64, len: u64) -> bool;
13            // Returns an index for wasm's `elem.drop`.
14            elem_drop(vmctx: vmctx, elem: u32);
15            // Returns an index for wasm's `memory.copy`
16            memory_copy(vmctx: vmctx, dst_index: u32, dst: u64, src_index: u32, src: u64, len: u64) -> bool;
17            // Returns an index for wasm's `memory.fill` instruction.
18            memory_fill(vmctx: vmctx, memory: u32, dst: u64, val: u32, len: u64) -> bool;
19            // Returns an index for wasm's `memory.init` instruction.
20            memory_init(vmctx: vmctx, memory: u32, data: u32, dst: u64, src: u32, len: u32) -> bool;
21            // Returns a value for wasm's `ref.func` instruction.
22            ref_func(vmctx: vmctx, func: u32) -> pointer;
23            // Returns an index for wasm's `data.drop` instruction.
24            data_drop(vmctx: vmctx, data: u32);
25            // Returns a table entry after lazily initializing it.
26            table_get_lazy_init_func_ref(vmctx: vmctx, table: u32, index: u64) -> pointer;
27            // Returns an index for Wasm's `table.grow` instruction for `funcref`s.
28            table_grow_func_ref(vmctx: vmctx, table: u32, delta: u64, init: pointer) -> pointer;
29            // Returns an index for Wasm's `table.fill` instruction for `funcref`s.
30            table_fill_func_ref(vmctx: vmctx, table: u32, dst: u64, val: pointer, len: u64) -> bool;
31            // Returns an index for wasm's `memory.atomic.notify` instruction.
32            #[cfg(feature = "threads")]
33            memory_atomic_notify(vmctx: vmctx, memory: u32, addr: u64, count: u32) -> u64;
34            // Returns an index for wasm's `memory.atomic.wait32` instruction.
35            #[cfg(feature = "threads")]
36            memory_atomic_wait32(vmctx: vmctx, memory: u32, addr: u64, expected: u32, timeout: u64) -> u64;
37            // Returns an index for wasm's `memory.atomic.wait64` instruction.
38            #[cfg(feature = "threads")]
39            memory_atomic_wait64(vmctx: vmctx, memory: u32, addr: u64, expected: u64, timeout: u64) -> u64;
40            // Invoked when fuel has run out while executing a function.
41            out_of_gas(vmctx: vmctx) -> bool;
42            // Invoked when we reach a new epoch.
43            #[cfg(target_has_atomic = "64")]
44            new_epoch(vmctx: vmctx) -> u64;
45            // Invoked before malloc returns.
46            #[cfg(feature = "wmemcheck")]
47            check_malloc(vmctx: vmctx, addr: u32, len: u32) -> bool;
48            // Invoked before the free returns.
49            #[cfg(feature = "wmemcheck")]
50            check_free(vmctx: vmctx, addr: u32) -> bool;
51            // Invoked before a load is executed.
52            #[cfg(feature = "wmemcheck")]
53            check_load(vmctx: vmctx, num_bytes: u32, addr: u32, offset: u32) -> bool;
54            // Invoked before a store is executed.
55            #[cfg(feature = "wmemcheck")]
56            check_store(vmctx: vmctx, num_bytes: u32, addr: u32, offset: u32) -> bool;
57            // Invoked after malloc is called.
58            #[cfg(feature = "wmemcheck")]
59            malloc_start(vmctx: vmctx);
60            // Invoked after free is called.
61            #[cfg(feature = "wmemcheck")]
62            free_start(vmctx: vmctx);
63            // Invoked when wasm stack pointer is updated.
64            #[cfg(feature = "wmemcheck")]
65            update_stack_pointer(vmctx: vmctx, value: u32);
66            // Invoked before memory.grow is called.
67            #[cfg(feature = "wmemcheck")]
68            update_mem_size(vmctx: vmctx, num_bytes: u32);
69
70            // Drop a non-stack GC reference (eg an overwritten table entry)
71            // once it will no longer be used again. (Note: `val` is not of type
72            // `reference` because it needn't appear in any stack maps, as it
73            // must not be live after this call.)
74            #[cfg(feature = "gc-drc")]
75            drop_gc_ref(vmctx: vmctx, val: u32);
76
77            // Grow the GC heap by `bytes_needed` bytes.
78            //
79            // Traps if growing the GC heap fails.
80            #[cfg(feature = "gc-null")]
81            grow_gc_heap(vmctx: vmctx, bytes_needed: u64) -> bool;
82
83            // Do a GC, treating the optional `root` as a GC root and returning
84            // the updated `root` (so that, in the case of moving collectors,
85            // callers have a valid version of `root` again).
86            #[cfg(feature = "gc-drc")]
87            gc(vmctx: vmctx, root: u32) -> u64;
88
89            // Allocate a new, uninitialized GC object and return a reference to
90            // it.
91            #[cfg(feature = "gc-drc")]
92            gc_alloc_raw(
93                vmctx: vmctx,
94                kind: u32,
95                module_interned_type_index: u32,
96                size: u32,
97                align: u32
98            ) -> u32;
99
100            // Intern a `funcref` into the GC heap, returning its
101            // `FuncRefTableId`.
102            //
103            // This libcall may not GC.
104            #[cfg(feature = "gc")]
105            intern_func_ref_for_gc_heap(
106                vmctx: vmctx,
107                func_ref: pointer
108            ) -> u64;
109
110            // Get the raw `VMFuncRef` pointer associated with a
111            // `FuncRefTableId` from an earlier `intern_func_ref_for_gc_heap`
112            // call.
113            //
114            // This libcall may not GC.
115            //
116            // Passes in the `ModuleInternedTypeIndex` of the funcref's expected
117            // type, or `ModuleInternedTypeIndex::reserved_value()` if we are
118            // getting the function reference as an untyped `funcref` rather
119            // than a typed `(ref $ty)`.
120            //
121            // TODO: We will want to eventually expose the table directly to
122            // Wasm code, so that it doesn't need to make a libcall to go from
123            // id to `VMFuncRef`. That will be a little tricky: it will also
124            // require updating the pointer to the slab in the `VMContext` (or
125            // `VMStoreContext` or wherever we put it) when the slab is
126            // resized.
127            #[cfg(feature = "gc")]
128            get_interned_func_ref(
129                vmctx: vmctx,
130                func_ref_id: u32,
131                module_interned_type_index: u32
132            ) -> pointer;
133
134            // Builtin implementation of the `array.new_data` instruction.
135            #[cfg(feature = "gc")]
136            array_new_data(
137                vmctx: vmctx,
138                array_interned_type_index: u32,
139                data_index: u32,
140                data_offset: u32,
141                len: u32
142            ) -> u32;
143
144            // Builtin implementation of the `array.new_elem` instruction.
145            #[cfg(feature = "gc")]
146            array_new_elem(
147                vmctx: vmctx,
148                array_interned_type_index: u32,
149                elem_index: u32,
150                elem_offset: u32,
151                len: u32
152            ) -> u32;
153
154            // Builtin implementation of the `array.copy` instruction.
155            #[cfg(feature = "gc")]
156            array_copy(
157                vmctx: vmctx,
158                dst_array: u32,
159                dst_index: u32,
160                src_array: u32,
161                src_index: u32,
162                len: u32
163            ) -> bool;
164
165            // Builtin implementation of the `array.init_data` instruction.
166            #[cfg(feature = "gc")]
167            array_init_data(
168                vmctx: vmctx,
169                array_interned_type_index: u32,
170                array: u32,
171                dst_index: u32,
172                data_index: u32,
173                data_offset: u32,
174                len: u32
175            ) -> bool;
176
177            // Builtin implementation of the `array.init_elem` instruction.
178            #[cfg(feature = "gc")]
179            array_init_elem(
180                vmctx: vmctx,
181                array_interned_type_index: u32,
182                array: u32,
183                dst: u32,
184                elem_index: u32,
185                src: u32,
186                len: u32
187            ) -> bool;
188
189            // Returns whether `actual_engine_type` is a subtype of
190            // `expected_engine_type`.
191            #[cfg(feature = "gc")]
192            is_subtype(
193                vmctx: vmctx,
194                actual_engine_type: u32,
195                expected_engine_type: u32
196            ) -> u32;
197
198            // Returns an index for Wasm's `table.grow` instruction for GC references.
199            #[cfg(feature = "gc")]
200            table_grow_gc_ref(vmctx: vmctx, table: u32, delta: u64, init: u32) -> pointer;
201
202            // Returns an index for Wasm's `table.fill` instruction for GC references.
203            #[cfg(feature = "gc")]
204            table_fill_gc_ref(vmctx: vmctx, table: u32, dst: u64, val: u32, len: u64) -> bool;
205
206            // Wasm floating-point routines for when the CPU instructions aren't available.
207            ceil_f32(vmctx: vmctx, x: f32) -> f32;
208            ceil_f64(vmctx: vmctx, x: f64) -> f64;
209            floor_f32(vmctx: vmctx, x: f32) -> f32;
210            floor_f64(vmctx: vmctx, x: f64) -> f64;
211            trunc_f32(vmctx: vmctx, x: f32) -> f32;
212            trunc_f64(vmctx: vmctx, x: f64) -> f64;
213            nearest_f32(vmctx: vmctx, x: f32) -> f32;
214            nearest_f64(vmctx: vmctx, x: f64) -> f64;
215            i8x16_swizzle(vmctx: vmctx, a: i8x16, b: i8x16) -> i8x16;
216            i8x16_shuffle(vmctx: vmctx, a: i8x16, b: i8x16, c: i8x16) -> i8x16;
217            fma_f32x4(vmctx: vmctx, x: f32x4, y: f32x4, z: f32x4) -> f32x4;
218            fma_f64x2(vmctx: vmctx, x: f64x2, y: f64x2, z: f64x2) -> f64x2;
219
220            // Raises an unconditional trap with the specified code.
221            //
222            // This is used when signals-based-traps are disabled for backends
223            // when an illegal instruction can't be executed for example.
224            trap(vmctx: vmctx, code: u8);
225
226            // Raises an unconditional trap where the trap information must have
227            // been previously filled in.
228            raise(vmctx: vmctx);
229        }
230    };
231}
232
233/// Helper macro to define a builtin type such as `BuiltinFunctionIndex` and
234/// `ComponentBuiltinFunctionIndex` using the iterator macro, e.g.
235/// `foreach_builtin_function`, as the way to generate accessor methods.
236macro_rules! declare_builtin_index {
237    ($index_name:ident, $iter:ident) => {
238        /// An index type for builtin functions.
239        #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
240        pub struct $index_name(u32);
241
242        impl $index_name {
243            /// Create a new builtin from its raw index
244            pub const fn from_u32(i: u32) -> Self {
245                assert!(i < Self::len());
246                Self(i)
247            }
248
249            /// Return the index as an u32 number.
250            pub const fn index(&self) -> u32 {
251                self.0
252            }
253
254            $iter!(declare_builtin_index_constructors);
255        }
256    };
257}
258
259/// Helper macro used by the above macro.
260macro_rules! declare_builtin_index_constructors {
261    (
262        $(
263            $( #[$attr:meta] )*
264            $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
265        )*
266    ) => {
267        declare_builtin_index_constructors!(
268            @indices;
269            0;
270            $( $( #[$attr] )* $name; )*
271        );
272
273        /// Returns a symbol name for this builtin.
274        pub fn name(&self) -> &'static str {
275            $(
276                if *self == Self::$name() {
277                    return stringify!($name);
278                }
279            )*
280            unreachable!()
281        }
282    };
283
284    // Base case: no more indices to declare, so define the total number of
285    // function indices.
286    (
287        @indices;
288        $len:expr;
289    ) => {
290        /// Returns the total number of builtin functions.
291        pub const fn len() -> u32 {
292            $len
293        }
294    };
295
296    // Recursive case: declare the next index, and then keep declaring the rest of
297    // the indices.
298    (
299         @indices;
300         $index:expr;
301         $( #[$this_attr:meta] )*
302         $this_name:ident;
303         $(
304             $( #[$rest_attr:meta] )*
305             $rest_name:ident;
306         )*
307    ) => {
308        #[expect(missing_docs, reason = "macro-generated")]
309        pub const fn $this_name() -> Self {
310            Self($index)
311        }
312
313        declare_builtin_index_constructors!(
314            @indices;
315            ($index + 1);
316            $( $( #[$rest_attr] )* $rest_name; )*
317        );
318    }
319}
320
321// Define `struct BuiltinFunctionIndex`
322declare_builtin_index!(BuiltinFunctionIndex, foreach_builtin_function);
323
324/// Return value of [`BuiltinFunctionIndex::trap_sentinel`].
325pub enum TrapSentinel {
326    /// A falsy or zero value indicates a trap.
327    Falsy,
328    /// The value `-2` indicates a trap (used for growth-related builtins).
329    NegativeTwo,
330    /// The value `-1` indicates a trap .
331    NegativeOne,
332    /// Any negative value indicates a trap.
333    Negative,
334}
335
336impl BuiltinFunctionIndex {
337    /// Describes the return value of this builtin and what represents a trap.
338    ///
339    /// Libcalls don't raise traps themselves and instead delegate to compilers
340    /// to do so. This means that some return values of libcalls indicate a trap
341    /// is happening and this is represented with sentinel values. This function
342    /// returns the description of the sentinel value which indicates a trap, if
343    /// any. If `None` is returned from this function then this builtin cannot
344    /// generate a trap.
345    #[allow(unreachable_code, unused_macro_rules, reason = "macro-generated code")]
346    pub fn trap_sentinel(&self) -> Option<TrapSentinel> {
347        macro_rules! trap_sentinel {
348            (
349                $(
350                    $( #[$attr:meta] )*
351                    $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
352                )*
353            ) => {{
354                $(
355                    $(#[$attr])*
356                    if *self == BuiltinFunctionIndex::$name() {
357                        let mut _ret = None;
358                        $(_ret = Some(trap_sentinel!(@get $name $result));)?
359                        return _ret;
360                    }
361                )*
362
363                None
364            }};
365
366            // Growth-related functions return -2 as a sentinel.
367            (@get memory32_grow pointer) => (TrapSentinel::NegativeTwo);
368            (@get table_grow_func_ref pointer) => (TrapSentinel::NegativeTwo);
369            (@get table_grow_gc_ref pointer) => (TrapSentinel::NegativeTwo);
370
371            // Atomics-related functions return a negative value indicating trap
372            // indicate a trap.
373            (@get memory_atomic_notify u64) => (TrapSentinel::Negative);
374            (@get memory_atomic_wait32 u64) => (TrapSentinel::Negative);
375            (@get memory_atomic_wait64 u64) => (TrapSentinel::Negative);
376
377            // GC returns an optional GC ref, encoded as a `u64` with a negative
378            // value indicating a trap.
379            (@get gc u64) => (TrapSentinel::Negative);
380
381            // GC allocation functions return a u32 which is zero to indicate a
382            // trap.
383            (@get gc_alloc_raw u32) => (TrapSentinel::Falsy);
384            (@get array_new_data u32) => (TrapSentinel::Falsy);
385            (@get array_new_elem u32) => (TrapSentinel::Falsy);
386
387            // The final epoch represents a trap
388            (@get new_epoch u64) => (TrapSentinel::NegativeOne);
389
390            // These libcalls can't trap
391            (@get ref_func pointer) => (return None);
392            (@get table_get_lazy_init_func_ref pointer) => (return None);
393            (@get get_interned_func_ref pointer) => (return None);
394            (@get intern_func_ref_for_gc_heap u64) => (return None);
395            (@get is_subtype u32) => (return None);
396            (@get ceil_f32 f32) => (return None);
397            (@get ceil_f64 f64) => (return None);
398            (@get floor_f32 f32) => (return None);
399            (@get floor_f64 f64) => (return None);
400            (@get trunc_f32 f32) => (return None);
401            (@get trunc_f64 f64) => (return None);
402            (@get nearest_f32 f32) => (return None);
403            (@get nearest_f64 f64) => (return None);
404            (@get i8x16_swizzle i8x16) => (return None);
405            (@get i8x16_shuffle i8x16) => (return None);
406            (@get fma_f32x4 f32x4) => (return None);
407            (@get fma_f64x2 f64x2) => (return None);
408
409            // Bool-returning functions use `false` as an indicator of a trap.
410            (@get $name:ident bool) => (TrapSentinel::Falsy);
411
412            (@get $name:ident $ret:ident) => (
413                compile_error!(concat!("no trap sentinel registered for ", stringify!($name)))
414            )
415        }
416
417        foreach_builtin_function!(trap_sentinel)
418    }
419}