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