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