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