Skip to main content

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            memory_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) -> bool;
15            // Returns an index for wasm's `memory.copy`
16            memory_copy(vmctx: vmctx, dst: pointer, src: pointer, len: size);
17            // Returns an index for wasm's `memory.fill` instruction.
18            memory_fill(vmctx: vmctx, dst: pointer, val: u32, len: size);
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) -> bool;
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            // Allocate a new, uninitialized GC object and return a reference to
84            // it.
85            #[cfg(any(feature = "gc-drc", feature = "gc-copying"))]
86            gc_alloc_raw(
87                vmctx: vmctx,
88                kind: u32,
89                shared_type_index: u32,
90                size: u32,
91                align: u32
92            ) -> u32;
93
94            // Intern a `funcref` into the GC heap, returning its
95            // `FuncRefTableId`.
96            //
97            // This libcall may not GC.
98            #[cfg(feature = "gc")]
99            intern_func_ref_for_gc_heap(
100                vmctx: vmctx,
101                func_ref: pointer
102            ) -> u64;
103
104            // Get the raw `VMFuncRef` pointer associated with a
105            // `FuncRefTableId` from an earlier `intern_func_ref_for_gc_heap`
106            // call.
107            //
108            // This libcall may not GC.
109            //
110            // Passes in the `ModuleInternedTypeIndex` of the funcref's expected
111            // type, or `ModuleInternedTypeIndex::reserved_value()` if we are
112            // getting the function reference as an untyped `funcref` rather
113            // than a typed `(ref $ty)`.
114            //
115            // TODO: We will want to eventually expose the table directly to
116            // Wasm code, so that it doesn't need to make a libcall to go from
117            // id to `VMFuncRef`. That will be a little tricky: it will also
118            // require updating the pointer to the slab in the `VMContext` (or
119            // `VMStoreContext` or wherever we put it) when the slab is
120            // resized.
121            #[cfg(feature = "gc")]
122            get_interned_func_ref(
123                vmctx: vmctx,
124                func_ref_id: u32,
125                module_interned_type_index: u32
126            ) -> pointer;
127
128            // Builtin implementation of the `array.new_data` instruction.
129            #[cfg(feature = "gc")]
130            array_new_data(
131                vmctx: vmctx,
132                array_interned_type_index: u32,
133                data_index: u32,
134                data_offset: u32,
135                len: u32
136            ) -> u32;
137
138            // Builtin implementation of the `array.new_elem` instruction.
139            #[cfg(feature = "gc")]
140            array_new_elem(
141                vmctx: vmctx,
142                array_interned_type_index: u32,
143                elem_index: u32,
144                elem_offset: u32,
145                len: u32
146            ) -> u32;
147
148            // Builtin implementation of the `array.init_data` instruction.
149            #[cfg(feature = "gc")]
150            array_init_data(
151                vmctx: vmctx,
152                array_interned_type_index: u32,
153                array: u32,
154                dst_index: u32,
155                data_index: u32,
156                data_offset: u32,
157                len: u32
158            ) -> bool;
159
160            // Builtin implementation of the `array.init_elem` instruction.
161            #[cfg(feature = "gc")]
162            array_init_elem(
163                vmctx: vmctx,
164                array_interned_type_index: u32,
165                array: u32,
166                dst: u32,
167                elem_index: u32,
168                src: u32,
169                len: u32
170            ) -> bool;
171
172            // Returns whether `actual_engine_type` is a subtype of
173            // `expected_engine_type`.
174            #[cfg(feature = "gc")]
175            is_subtype(
176                vmctx: vmctx,
177                actual_engine_type: u32,
178                expected_engine_type: u32
179            ) -> u32;
180
181            // Returns an index for Wasm's `table.grow` instruction for GC references.
182            #[cfg(feature = "gc")]
183            table_grow_gc_ref(vmctx: vmctx, table: u32, delta: u64, init: u32) -> pointer;
184
185            // Returns an index for Wasm's `table.fill` instruction for GC references.
186            #[cfg(feature = "gc")]
187            table_fill_gc_ref(vmctx: vmctx, table: u32, dst: u64, val: u32, len: u64) -> bool;
188
189            // Wasm floating-point routines for when the CPU instructions aren't available.
190            ceil_f32(vmctx: vmctx, x: f32) -> f32;
191            ceil_f64(vmctx: vmctx, x: f64) -> f64;
192            floor_f32(vmctx: vmctx, x: f32) -> f32;
193            floor_f64(vmctx: vmctx, x: f64) -> f64;
194            trunc_f32(vmctx: vmctx, x: f32) -> f32;
195            trunc_f64(vmctx: vmctx, x: f64) -> f64;
196            nearest_f32(vmctx: vmctx, x: f32) -> f32;
197            nearest_f64(vmctx: vmctx, x: f64) -> f64;
198            i8x16_swizzle(vmctx: vmctx, a: i8x16, b: i8x16) -> i8x16;
199            i8x16_shuffle(vmctx: vmctx, a: i8x16, b: i8x16, c: i8x16) -> i8x16;
200            fma_f32x4(vmctx: vmctx, x: f32x4, y: f32x4, z: f32x4) -> f32x4;
201            fma_f64x2(vmctx: vmctx, x: f64x2, y: f64x2, z: f64x2) -> f64x2;
202
203            // Raises an unconditional trap with the specified code.
204            //
205            // This is used when signals-based-traps are disabled for backends
206            // when an illegal instruction can't be executed for example.
207            trap(vmctx: vmctx, code: u8);
208
209            // Raises an unconditional trap where the trap information must have
210            // been previously filled in.
211            raise(vmctx: vmctx);
212
213            // Creates a new continuation from a funcref.
214            #[cfg(feature = "stack-switching")]
215            cont_new(vmctx: vmctx, r: pointer, param_count: u32, result_count: u32) -> pointer;
216
217            // Returns an index for Wasm's `table.grow` instruction
218            // for `contobj`s.  Note that the initial
219            // Option<VMContObj> (i.e., the value to fill the new
220            // slots with) is split into two arguments: The underlying
221            // continuation reference and the revision count.  To
222            // denote the continuation being `None`, `init_contref`
223            // may be 0.
224            #[cfg(feature = "stack-switching")]
225            table_grow_cont_obj(vmctx: vmctx, table: u32, delta: u64, init_contref: pointer, init_revision: size) -> pointer;
226
227            // `value_contref` and `value_revision` together encode
228            // the Option<VMContObj>, as in previous libcall.
229            #[cfg(feature = "stack-switching")]
230            table_fill_cont_obj(vmctx: vmctx, table: u32, dst: u64, value_contref: pointer, value_revision: size, len: u64) -> bool;
231
232            // Return the instance ID for a given vmctx.
233            #[cfg(feature = "gc")]
234            get_instance_id(vmctx: vmctx) -> u32;
235
236            // Throw an exception.
237            #[cfg(feature = "gc")]
238            throw_ref(vmctx: vmctx, exnref: u32) -> bool;
239
240            // Process a debug breakpoint.
241            breakpoint(vmctx: vmctx) -> bool;
242        }
243    };
244}
245
246/// Helper macro to define a builtin type such as `BuiltinFunctionIndex` and
247/// `ComponentBuiltinFunctionIndex` using the iterator macro, e.g.
248/// `foreach_builtin_function`, as the way to generate accessor methods.
249macro_rules! declare_builtin_index {
250    (
251        $(#[$attr:meta])*
252        pub struct $index_name:ident : $for_each_builtin:ident ;
253    ) => {
254        $(#[$attr])*
255        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
256        pub struct $index_name(u32);
257
258        impl core::fmt::Debug for $index_name {
259            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
260                f.debug_struct(stringify!($index_name))
261                    .field("index", &self.0)
262                    .field("ctor", &self.ctor_name())
263                    .finish()
264            }
265        }
266
267        impl $index_name {
268            /// Create a new builtin from its raw index
269            pub const fn from_u32(i: u32) -> Self {
270                assert!(i < Self::len());
271                Self(i)
272            }
273
274            /// Return the index as an u32 number.
275            pub const fn index(&self) -> u32 {
276                self.0
277            }
278
279            $for_each_builtin!(define_ctor_name);
280
281            $for_each_builtin!(declare_builtin_index_constructors);
282        }
283
284        #[cfg(test)]
285        impl arbitrary::Arbitrary<'_> for $index_name {
286            fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
287                Ok(Self(u.int_in_range(0..=Self::len() - 1)?))
288            }
289        }
290    };
291}
292
293/// Helper macro used by the above macro.
294macro_rules! define_ctor_name {
295    (
296        $(
297            $( #[$attr:meta] )*
298                $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
299        )*
300    ) => {
301        /// Returns the name of the constructor that creates this index.
302        pub fn ctor_name(&self) -> &'static str {
303            let mut _i = self.0;
304            $(
305                if _i == 0 {
306                    return stringify!($name);
307                }
308                _i -= 1;
309            )*
310            unreachable!()
311        }
312    }
313}
314
315/// Helper macro used by the above macro.
316macro_rules! declare_builtin_index_constructors {
317    (
318        $(
319            $( #[$attr:meta] )*
320            $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
321        )*
322    ) => {
323        declare_builtin_index_constructors!(
324            @indices;
325            0;
326            $( $( #[$attr] )* $name; )*
327        );
328
329        /// Returns a symbol name for this builtin.
330        pub fn name(&self) -> &'static str {
331            $(
332                if *self == Self::$name() {
333                    return stringify!($name);
334                }
335            )*
336            unreachable!()
337        }
338    };
339
340    // Base case: no more indices to declare, so define the total number of
341    // function indices.
342    (
343        @indices;
344        $len:expr;
345    ) => {
346        /// Returns the total number of builtin functions.
347        pub const fn len() -> u32 {
348            $len
349        }
350    };
351
352    // Recursive case: declare the next index, and then keep declaring the rest of
353    // the indices.
354    (
355         @indices;
356         $index:expr;
357         $( #[$this_attr:meta] )*
358         $this_name:ident;
359         $(
360             $( #[$rest_attr:meta] )*
361             $rest_name:ident;
362         )*
363    ) => {
364        #[expect(missing_docs, reason = "macro-generated")]
365        pub const fn $this_name() -> Self {
366            Self($index)
367        }
368
369        declare_builtin_index_constructors!(
370            @indices;
371            ($index + 1);
372            $( $( #[$rest_attr] )* $rest_name; )*
373        );
374    }
375}
376
377// Define `struct BuiltinFunctionIndex`
378declare_builtin_index! {
379    /// An index type for builtin functions.
380    pub struct BuiltinFunctionIndex : foreach_builtin_function;
381}
382
383/// Return value of [`BuiltinFunctionIndex::trap_sentinel`].
384pub enum TrapSentinel {
385    /// A falsy or zero value indicates a trap.
386    Falsy,
387    /// The value `-2` indicates a trap (used for growth-related builtins).
388    NegativeTwo,
389    /// The value `-1` indicates a trap .
390    NegativeOne,
391    /// Any negative value indicates a trap.
392    Negative,
393}
394
395impl BuiltinFunctionIndex {
396    /// Describes the return value of this builtin and what represents a trap.
397    ///
398    /// Libcalls don't raise traps themselves and instead delegate to compilers
399    /// to do so. This means that some return values of libcalls indicate a trap
400    /// is happening and this is represented with sentinel values. This function
401    /// returns the description of the sentinel value which indicates a trap, if
402    /// any. If `None` is returned from this function then this builtin cannot
403    /// generate a trap.
404    #[allow(unreachable_code, unused_macro_rules, reason = "macro-generated code")]
405    pub fn trap_sentinel(&self) -> Option<TrapSentinel> {
406        macro_rules! trap_sentinel {
407            (
408                $(
409                    $( #[$attr:meta] )*
410                    $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
411                )*
412            ) => {{
413                $(
414                    $(#[$attr])*
415                    if *self == BuiltinFunctionIndex::$name() {
416                        let mut _ret = None;
417                        $(_ret = Some(trap_sentinel!(@get $name $result));)?
418                        return _ret;
419                    }
420                )*
421
422                None
423            }};
424
425            // Growth-related functions return -2 as a sentinel.
426            (@get memory_grow pointer) => (TrapSentinel::NegativeTwo);
427            (@get table_grow_func_ref pointer) => (TrapSentinel::NegativeTwo);
428            (@get table_grow_gc_ref pointer) => (TrapSentinel::NegativeTwo);
429            (@get table_grow_cont_obj pointer) => (TrapSentinel::NegativeTwo);
430
431            // Atomics-related functions return a negative value to indicate a trap.
432            (@get memory_atomic_notify u64) => (TrapSentinel::Negative);
433            (@get memory_atomic_wait32 u64) => (TrapSentinel::Negative);
434            (@get memory_atomic_wait64 u64) => (TrapSentinel::Negative);
435
436            // GC allocation functions return a u32 which is zero to indicate a
437            // trap.
438            (@get gc_alloc_raw u32) => (TrapSentinel::Falsy);
439            (@get array_new_data u32) => (TrapSentinel::Falsy);
440            (@get array_new_elem u32) => (TrapSentinel::Falsy);
441
442            // The final epoch represents a trap
443            (@get new_epoch u64) => (TrapSentinel::NegativeOne);
444
445            // These libcalls can't trap
446            (@get ref_func pointer) => (return None);
447            (@get table_get_lazy_init_func_ref pointer) => (return None);
448            (@get get_interned_func_ref pointer) => (return None);
449            (@get intern_func_ref_for_gc_heap u64) => (return None);
450            (@get is_subtype u32) => (return None);
451            (@get ceil_f32 f32) => (return None);
452            (@get ceil_f64 f64) => (return None);
453            (@get floor_f32 f32) => (return None);
454            (@get floor_f64 f64) => (return None);
455            (@get trunc_f32 f32) => (return None);
456            (@get trunc_f64 f64) => (return None);
457            (@get nearest_f32 f32) => (return None);
458            (@get nearest_f64 f64) => (return None);
459            (@get i8x16_swizzle i8x16) => (return None);
460            (@get i8x16_shuffle i8x16) => (return None);
461            (@get fma_f32x4 f32x4) => (return None);
462            (@get fma_f64x2 f64x2) => (return None);
463
464            (@get cont_new pointer) => (TrapSentinel::Negative);
465
466            (@get get_instance_id u32) => (return None);
467
468            // Bool-returning functions use `false` as an indicator of a trap.
469            (@get $name:ident bool) => (TrapSentinel::Falsy);
470
471            (@get $name:ident $ret:ident) => (
472                compile_error!(concat!("no trap sentinel registered for ", stringify!($name)))
473            )
474        }
475
476        foreach_builtin_function!(trap_sentinel)
477    }
478}