Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/builtin.rs
3069 views
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")]
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
491