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) -> 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 };
175}
176
177/// Helper macro to define a builtin type such as `BuiltinFunctionIndex` and
178/// `ComponentBuiltinFunctionIndex` using the iterator macro, e.g.
179/// `foreach_builtin_function`, as the way to generate accessor methods.
180macro_rules! declare_builtin_index {
181 (
182 $(#[$attr:meta])*
183 pub struct $index_name:ident : $for_each_builtin:ident ;
184 ) => {
185 $(#[$attr])*
186 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
187 pub struct $index_name(u32);
188
189 impl core::fmt::Debug for $index_name {
190 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
191 f.debug_struct(stringify!($index_name))
192 .field("index", &self.0)
193 .field("ctor", &self.ctor_name())
194 .finish()
195 }
196 }
197
198 impl $index_name {
199 /// Create a new builtin from its raw index
200 pub const fn from_u32(i: u32) -> Self {
201 assert!(i < Self::len());
202 Self(i)
203 }
204
205 /// Return the index as an u32 number.
206 pub const fn index(&self) -> u32 {
207 self.0
208 }
209
210 $for_each_builtin!(define_ctor_name);
211
212 $for_each_builtin!(declare_builtin_index_constructors);
213 }
214
215 // Builtins are reached by indexing the array of function pointers that
216 // a vmctx points at.
217 impl $crate::VmctxArrayIndex for $index_name {
218 #[inline]
219 fn vmctx_array_index(self) -> u32 {
220 self.index()
221 }
222 }
223
224 #[cfg(test)]
225 impl arbitrary::Arbitrary<'_> for $index_name {
226 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
227 Ok(Self(u.int_in_range(0..=Self::len() - 1)?))
228 }
229 }
230 };
231}
232
233/// Helper macro used by the above macro.
234macro_rules! define_ctor_name {
235 (
236 $(
237 $( #[$attr:meta] )*
238 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
239 )*
240 ) => {
241 /// Returns the name of the constructor that creates this index.
242 pub fn ctor_name(&self) -> &'static str {
243 let mut _i = self.0;
244 $(
245 if _i == 0 {
246 return stringify!($name);
247 }
248 _i -= 1;
249 )*
250 unreachable!()
251 }
252 }
253}
254
255/// Helper macro used by the above macro.
256macro_rules! declare_builtin_index_constructors {
257 (
258 $(
259 $( #[$attr:meta] )*
260 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
261 )*
262 ) => {
263 declare_builtin_index_constructors!(
264 @indices;
265 0;
266 $( $( #[$attr] )* $name; )*
267 );
268
269 /// Returns a symbol name for this builtin.
270 pub fn name(&self) -> &'static str {
271 $(
272 if *self == Self::$name() {
273 return stringify!($name);
274 }
275 )*
276 unreachable!()
277 }
278 };
279
280 // Base case: no more indices to declare, so define the total number of
281 // function indices.
282 (
283 @indices;
284 $len:expr;
285 ) => {
286 /// Returns the total number of builtin functions.
287 pub const fn len() -> u32 {
288 $len
289 }
290 };
291
292 // Recursive case: declare the next index, and then keep declaring the rest of
293 // the indices.
294 (
295 @indices;
296 $index:expr;
297 $( #[$this_attr:meta] )*
298 $this_name:ident;
299 $(
300 $( #[$rest_attr:meta] )*
301 $rest_name:ident;
302 )*
303 ) => {
304 #[expect(missing_docs, reason = "macro-generated")]
305 pub const fn $this_name() -> Self {
306 Self($index)
307 }
308
309 declare_builtin_index_constructors!(
310 @indices;
311 ($index + 1);
312 $( $( #[$rest_attr] )* $rest_name; )*
313 );
314 }
315}
316
317// Define `struct BuiltinFunctionIndex`
318declare_builtin_index! {
319 /// An index type for builtin functions.
320 pub struct BuiltinFunctionIndex : foreach_builtin_function;
321}
322
323/// Return value of [`BuiltinFunctionIndex::trap_sentinel`].
324pub enum TrapSentinel {
325 /// A falsy or zero value indicates a trap.
326 Falsy,
327 /// The value `-2` indicates a trap (used for growth-related builtins).
328 NegativeTwo,
329 /// The value `-1` indicates a trap .
330 NegativeOne,
331 /// Any negative value indicates a trap.
332 Negative,
333}
334
335impl BuiltinFunctionIndex {
336 /// Describes the return value of this builtin and what represents a trap.
337 ///
338 /// Libcalls don't raise traps themselves and instead delegate to compilers
339 /// to do so. This means that some return values of libcalls indicate a trap
340 /// is happening and this is represented with sentinel values. This function
341 /// returns the description of the sentinel value which indicates a trap, if
342 /// any. If `None` is returned from this function then this builtin cannot
343 /// generate a trap.
344 #[allow(unreachable_code, unused_macro_rules, reason = "macro-generated code")]
345 pub fn trap_sentinel(&self) -> Option<TrapSentinel> {
346 macro_rules! trap_sentinel {
347 (
348 $(
349 $( #[$attr:meta] )*
350 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
351 )*
352 ) => {{
353 $(
354 $(#[$attr])*
355 if *self == BuiltinFunctionIndex::$name() {
356 let mut _ret = None;
357 $(_ret = Some(trap_sentinel!(@get $name $result));)?
358 return _ret;
359 }
360 )*
361
362 None
363 }};
364
365 // Growth-related functions return -2 as a sentinel.
366 (@get memory_grow pointer) => (TrapSentinel::NegativeTwo);
367 (@get table_grow pointer) => (TrapSentinel::NegativeTwo);
368
369 // Atomics-related functions return a negative value to indicate a trap.
370 (@get memory_atomic_notify u64) => (TrapSentinel::Negative);
371 (@get memory_atomic_wait32 u64) => (TrapSentinel::Negative);
372 (@get memory_atomic_wait64 u64) => (TrapSentinel::Negative);
373
374 // GC allocation functions return a u32 which is zero to indicate a
375 // trap.
376 (@get gc_alloc_raw u32) => (TrapSentinel::Falsy);
377 (@get array_new_data u32) => (TrapSentinel::Falsy);
378 (@get array_new_elem u32) => (TrapSentinel::Falsy);
379
380 // The final epoch represents a trap
381 (@get new_epoch u64) => (TrapSentinel::NegativeOne);
382
383 // Failure here indicates GC heap corruption.
384 (@get get_interned_func_ref pointer) => (TrapSentinel::NegativeOne);
385
386 // These libcalls can't trap
387 (@get ref_func pointer) => (return None);
388 (@get table_get_lazy_init_func_ref pointer) => (return None);
389 (@get intern_func_ref_for_gc_heap u64) => (return None);
390 (@get is_subtype u32) => (return None);
391 (@get ceil_f32 f32) => (return None);
392 (@get ceil_f64 f64) => (return None);
393 (@get floor_f32 f32) => (return None);
394 (@get floor_f64 f64) => (return None);
395 (@get trunc_f32 f32) => (return None);
396 (@get trunc_f64 f64) => (return None);
397 (@get nearest_f32 f32) => (return None);
398 (@get nearest_f64 f64) => (return None);
399 (@get i8x16_swizzle i8x16) => (return None);
400 (@get i8x16_shuffle i8x16) => (return None);
401 (@get fma_f32x4 f32x4) => (return None);
402 (@get fma_f64x2 f64x2) => (return None);
403 (@get passive_data_segment_base pointer) => (return None);
404 (@get passive_elem_segment_len size) => (return None);
405 (@get passive_elem_segment_base pointer) => (return None);
406
407 (@get cont_new pointer) => (TrapSentinel::Negative);
408
409 (@get get_instance_id u32) => (return None);
410
411 // Bool-returning functions use `false` as an indicator of a trap.
412 (@get $name:ident bool) => (TrapSentinel::Falsy);
413
414 (@get $name:ident $ret:ident) => (
415 compile_error!(concat!("no trap sentinel registered for ", stringify!($name)))
416 )
417 }
418
419 foreach_builtin_function!(trap_sentinel)
420 }
421}