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);
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 // 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.
260macro_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.
305macro_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.
327macro_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`
389declare_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`].
395pub 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
406impl 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")]
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 indicating trap
443 // indicate a trap.
444 (@get memory_atomic_notify u64) => (TrapSentinel::Negative);
445 (@get memory_atomic_wait32 u64) => (TrapSentinel::Negative);
446 (@get memory_atomic_wait64 u64) => (TrapSentinel::Negative);
447
448 // GC returns an optional GC ref, encoded as a `u64` with a negative
449 // value indicating a trap.
450 (@get gc u64) => (TrapSentinel::Negative);
451
452 // GC allocation functions return a u32 which is zero to indicate a
453 // trap.
454 (@get gc_alloc_raw u32) => (TrapSentinel::Falsy);
455 (@get array_new_data u32) => (TrapSentinel::Falsy);
456 (@get array_new_elem u32) => (TrapSentinel::Falsy);
457
458 // The final epoch represents a trap
459 (@get new_epoch u64) => (TrapSentinel::NegativeOne);
460
461 // These libcalls can't trap
462 (@get ref_func pointer) => (return None);
463 (@get table_get_lazy_init_func_ref pointer) => (return None);
464 (@get get_interned_func_ref pointer) => (return None);
465 (@get intern_func_ref_for_gc_heap u64) => (return None);
466 (@get is_subtype u32) => (return None);
467 (@get ceil_f32 f32) => (return None);
468 (@get ceil_f64 f64) => (return None);
469 (@get floor_f32 f32) => (return None);
470 (@get floor_f64 f64) => (return None);
471 (@get trunc_f32 f32) => (return None);
472 (@get trunc_f64 f64) => (return None);
473 (@get nearest_f32 f32) => (return None);
474 (@get nearest_f64 f64) => (return None);
475 (@get i8x16_swizzle i8x16) => (return None);
476 (@get i8x16_shuffle i8x16) => (return None);
477 (@get fma_f32x4 f32x4) => (return None);
478 (@get fma_f64x2 f64x2) => (return None);
479
480 (@get cont_new pointer) => (TrapSentinel::Negative);
481
482 (@get get_instance_id u32) => (return None);
483
484 // Bool-returning functions use `false` as an indicator of a trap.
485 (@get $name:ident bool) => (TrapSentinel::Falsy);
486
487 (@get $name:ident $ret:ident) => (
488 compile_error!(concat!("no trap sentinel registered for ", stringify!($name)))
489 )
490 }
491
492 foreach_builtin_function!(trap_sentinel)
493 }
494}