Path: blob/main/crates/environ/src/builtin.rs
1691 views
/// Helper macro to iterate over all builtin functions and their signatures.1#[macro_export]2macro_rules! foreach_builtin_function {3($mac:ident) => {4$mac! {5// Returns an index for wasm's `memory.grow` builtin function.6memory_grow(vmctx: vmctx, delta: u64, index: u32) -> pointer;7// Returns an index for wasm's `table.copy` when both tables are locally8// defined.9table_copy(vmctx: vmctx, dst_index: u32, src_index: u32, dst: u64, src: u64, len: u64) -> bool;10// Returns an index for wasm's `table.init`.11table_init(vmctx: vmctx, table: u32, elem: u32, dst: u64, src: u64, len: u64) -> bool;12// Returns an index for wasm's `elem.drop`.13elem_drop(vmctx: vmctx, elem: u32);14// Returns an index for wasm's `memory.copy`15memory_copy(vmctx: vmctx, dst_index: u32, dst: u64, src_index: u32, src: u64, len: u64) -> bool;16// Returns an index for wasm's `memory.fill` instruction.17memory_fill(vmctx: vmctx, memory: u32, dst: u64, val: u32, len: u64) -> bool;18// Returns an index for wasm's `memory.init` instruction.19memory_init(vmctx: vmctx, memory: u32, data: u32, dst: u64, src: u32, len: u32) -> bool;20// Returns a value for wasm's `ref.func` instruction.21ref_func(vmctx: vmctx, func: u32) -> pointer;22// Returns an index for wasm's `data.drop` instruction.23data_drop(vmctx: vmctx, data: u32);24// Returns a table entry after lazily initializing it.25table_get_lazy_init_func_ref(vmctx: vmctx, table: u32, index: u64) -> pointer;26// Returns an index for Wasm's `table.grow` instruction for `funcref`s.27table_grow_func_ref(vmctx: vmctx, table: u32, delta: u64, init: pointer) -> pointer;28// Returns an index for Wasm's `table.fill` instruction for `funcref`s.29table_fill_func_ref(vmctx: vmctx, table: u32, dst: u64, val: pointer, len: u64) -> bool;30// Returns an index for wasm's `memory.atomic.notify` instruction.31#[cfg(feature = "threads")]32memory_atomic_notify(vmctx: vmctx, memory: u32, addr: u64, count: u32) -> u64;33// Returns an index for wasm's `memory.atomic.wait32` instruction.34#[cfg(feature = "threads")]35memory_atomic_wait32(vmctx: vmctx, memory: u32, addr: u64, expected: u32, timeout: u64) -> u64;36// Returns an index for wasm's `memory.atomic.wait64` instruction.37#[cfg(feature = "threads")]38memory_atomic_wait64(vmctx: vmctx, memory: u32, addr: u64, expected: u64, timeout: u64) -> u64;39// Invoked when fuel has run out while executing a function.40out_of_gas(vmctx: vmctx) -> bool;41// Invoked when we reach a new epoch.42#[cfg(target_has_atomic = "64")]43new_epoch(vmctx: vmctx) -> u64;44// Invoked before malloc returns.45#[cfg(feature = "wmemcheck")]46check_malloc(vmctx: vmctx, addr: u32, len: u32) -> bool;47// Invoked before the free returns.48#[cfg(feature = "wmemcheck")]49check_free(vmctx: vmctx, addr: u32) -> bool;50// Invoked before a load is executed.51#[cfg(feature = "wmemcheck")]52check_load(vmctx: vmctx, num_bytes: u32, addr: u32, offset: u32) -> bool;53// Invoked before a store is executed.54#[cfg(feature = "wmemcheck")]55check_store(vmctx: vmctx, num_bytes: u32, addr: u32, offset: u32) -> bool;56// Invoked after malloc is called.57#[cfg(feature = "wmemcheck")]58malloc_start(vmctx: vmctx);59// Invoked after free is called.60#[cfg(feature = "wmemcheck")]61free_start(vmctx: vmctx);62// Invoked when wasm stack pointer is updated.63#[cfg(feature = "wmemcheck")]64update_stack_pointer(vmctx: vmctx, value: u32);65// Invoked before memory.grow is called.66#[cfg(feature = "wmemcheck")]67update_mem_size(vmctx: vmctx, num_bytes: u32);6869// Drop a non-stack GC reference (eg an overwritten table entry)70// once it will no longer be used again. (Note: `val` is not of type71// `reference` because it needn't appear in any stack maps, as it72// must not be live after this call.)73#[cfg(feature = "gc-drc")]74drop_gc_ref(vmctx: vmctx, val: u32);7576// Grow the GC heap by `bytes_needed` bytes.77//78// Traps if growing the GC heap fails.79#[cfg(feature = "gc-null")]80grow_gc_heap(vmctx: vmctx, bytes_needed: u64) -> bool;8182// Allocate a new, uninitialized GC object and return a reference to83// it.84#[cfg(feature = "gc-drc")]85gc_alloc_raw(86vmctx: vmctx,87kind: u32,88module_interned_type_index: u32,89size: u32,90align: u3291) -> u32;9293// Intern a `funcref` into the GC heap, returning its94// `FuncRefTableId`.95//96// This libcall may not GC.97#[cfg(feature = "gc")]98intern_func_ref_for_gc_heap(99vmctx: vmctx,100func_ref: pointer101) -> u64;102103// Get the raw `VMFuncRef` pointer associated with a104// `FuncRefTableId` from an earlier `intern_func_ref_for_gc_heap`105// call.106//107// This libcall may not GC.108//109// Passes in the `ModuleInternedTypeIndex` of the funcref's expected110// type, or `ModuleInternedTypeIndex::reserved_value()` if we are111// getting the function reference as an untyped `funcref` rather112// than a typed `(ref $ty)`.113//114// TODO: We will want to eventually expose the table directly to115// Wasm code, so that it doesn't need to make a libcall to go from116// id to `VMFuncRef`. That will be a little tricky: it will also117// require updating the pointer to the slab in the `VMContext` (or118// `VMStoreContext` or wherever we put it) when the slab is119// resized.120#[cfg(feature = "gc")]121get_interned_func_ref(122vmctx: vmctx,123func_ref_id: u32,124module_interned_type_index: u32125) -> pointer;126127// Builtin implementation of the `array.new_data` instruction.128#[cfg(feature = "gc")]129array_new_data(130vmctx: vmctx,131array_interned_type_index: u32,132data_index: u32,133data_offset: u32,134len: u32135) -> u32;136137// Builtin implementation of the `array.new_elem` instruction.138#[cfg(feature = "gc")]139array_new_elem(140vmctx: vmctx,141array_interned_type_index: u32,142elem_index: u32,143elem_offset: u32,144len: u32145) -> u32;146147// Builtin implementation of the `array.copy` instruction.148#[cfg(feature = "gc")]149array_copy(150vmctx: vmctx,151dst_array: u32,152dst_index: u32,153src_array: u32,154src_index: u32,155len: u32156) -> bool;157158// Builtin implementation of the `array.init_data` instruction.159#[cfg(feature = "gc")]160array_init_data(161vmctx: vmctx,162array_interned_type_index: u32,163array: u32,164dst_index: u32,165data_index: u32,166data_offset: u32,167len: u32168) -> bool;169170// Builtin implementation of the `array.init_elem` instruction.171#[cfg(feature = "gc")]172array_init_elem(173vmctx: vmctx,174array_interned_type_index: u32,175array: u32,176dst: u32,177elem_index: u32,178src: u32,179len: u32180) -> bool;181182// Returns whether `actual_engine_type` is a subtype of183// `expected_engine_type`.184#[cfg(feature = "gc")]185is_subtype(186vmctx: vmctx,187actual_engine_type: u32,188expected_engine_type: u32189) -> u32;190191// Returns an index for Wasm's `table.grow` instruction for GC references.192#[cfg(feature = "gc")]193table_grow_gc_ref(vmctx: vmctx, table: u32, delta: u64, init: u32) -> pointer;194195// Returns an index for Wasm's `table.fill` instruction for GC references.196#[cfg(feature = "gc")]197table_fill_gc_ref(vmctx: vmctx, table: u32, dst: u64, val: u32, len: u64) -> bool;198199// Wasm floating-point routines for when the CPU instructions aren't available.200ceil_f32(vmctx: vmctx, x: f32) -> f32;201ceil_f64(vmctx: vmctx, x: f64) -> f64;202floor_f32(vmctx: vmctx, x: f32) -> f32;203floor_f64(vmctx: vmctx, x: f64) -> f64;204trunc_f32(vmctx: vmctx, x: f32) -> f32;205trunc_f64(vmctx: vmctx, x: f64) -> f64;206nearest_f32(vmctx: vmctx, x: f32) -> f32;207nearest_f64(vmctx: vmctx, x: f64) -> f64;208i8x16_swizzle(vmctx: vmctx, a: i8x16, b: i8x16) -> i8x16;209i8x16_shuffle(vmctx: vmctx, a: i8x16, b: i8x16, c: i8x16) -> i8x16;210fma_f32x4(vmctx: vmctx, x: f32x4, y: f32x4, z: f32x4) -> f32x4;211fma_f64x2(vmctx: vmctx, x: f64x2, y: f64x2, z: f64x2) -> f64x2;212213// Raises an unconditional trap with the specified code.214//215// This is used when signals-based-traps are disabled for backends216// when an illegal instruction can't be executed for example.217trap(vmctx: vmctx, code: u8);218219// Raises an unconditional trap where the trap information must have220// been previously filled in.221raise(vmctx: vmctx);222223// Creates a new continuation from a funcref.224#[cfg(feature = "stack-switching")]225cont_new(vmctx: vmctx, r: pointer, param_count: u32, result_count: u32) -> pointer;226227// Returns an index for Wasm's `table.grow` instruction228// for `contobj`s. Note that the initial229// Option<VMContObj> (i.e., the value to fill the new230// slots with) is split into two arguments: The underlying231// continuation reference and the revision count. To232// denote the continuation being `None`, `init_contref`233// may be 0.234#[cfg(feature = "stack-switching")]235table_grow_cont_obj(vmctx: vmctx, table: u32, delta: u64, init_contref: pointer, init_revision: size) -> pointer;236237// `value_contref` and `value_revision` together encode238// the Option<VMContObj>, as in previous libcall.239#[cfg(feature = "stack-switching")]240table_fill_cont_obj(vmctx: vmctx, table: u32, dst: u64, value_contref: pointer, value_revision: size, len: u64) -> bool;241242// Return the instance ID for a given vmctx.243#[cfg(feature = "gc")]244get_instance_id(vmctx: vmctx) -> u32;245246// Throw an exception.247#[cfg(feature = "gc")]248throw_ref(vmctx: vmctx, exnref: u32) -> bool;249}250};251}252253/// Helper macro to define a builtin type such as `BuiltinFunctionIndex` and254/// `ComponentBuiltinFunctionIndex` using the iterator macro, e.g.255/// `foreach_builtin_function`, as the way to generate accessor methods.256macro_rules! declare_builtin_index {257(258$(#[$attr:meta])*259pub struct $index_name:ident : $for_each_builtin:ident ;260) => {261$(#[$attr])*262#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]263pub struct $index_name(u32);264265impl $index_name {266/// Create a new builtin from its raw index267pub const fn from_u32(i: u32) -> Self {268assert!(i < Self::len());269Self(i)270}271272/// Return the index as an u32 number.273pub const fn index(&self) -> u32 {274self.0275}276277$for_each_builtin!(declare_builtin_index_constructors);278}279};280}281282/// Helper macro used by the above macro.283macro_rules! declare_builtin_index_constructors {284(285$(286$( #[$attr:meta] )*287$name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;288)*289) => {290declare_builtin_index_constructors!(291@indices;2920;293$( $( #[$attr] )* $name; )*294);295296/// Returns a symbol name for this builtin.297pub fn name(&self) -> &'static str {298$(299if *self == Self::$name() {300return stringify!($name);301}302)*303unreachable!()304}305};306307// Base case: no more indices to declare, so define the total number of308// function indices.309(310@indices;311$len:expr;312) => {313/// Returns the total number of builtin functions.314pub const fn len() -> u32 {315$len316}317};318319// Recursive case: declare the next index, and then keep declaring the rest of320// the indices.321(322@indices;323$index:expr;324$( #[$this_attr:meta] )*325$this_name:ident;326$(327$( #[$rest_attr:meta] )*328$rest_name:ident;329)*330) => {331#[expect(missing_docs, reason = "macro-generated")]332pub const fn $this_name() -> Self {333Self($index)334}335336declare_builtin_index_constructors!(337@indices;338($index + 1);339$( $( #[$rest_attr] )* $rest_name; )*340);341}342}343344// Define `struct BuiltinFunctionIndex`345declare_builtin_index! {346/// An index type for builtin functions.347pub struct BuiltinFunctionIndex : foreach_builtin_function;348}349350/// Return value of [`BuiltinFunctionIndex::trap_sentinel`].351pub enum TrapSentinel {352/// A falsy or zero value indicates a trap.353Falsy,354/// The value `-2` indicates a trap (used for growth-related builtins).355NegativeTwo,356/// The value `-1` indicates a trap .357NegativeOne,358/// Any negative value indicates a trap.359Negative,360}361362impl BuiltinFunctionIndex {363/// Describes the return value of this builtin and what represents a trap.364///365/// Libcalls don't raise traps themselves and instead delegate to compilers366/// to do so. This means that some return values of libcalls indicate a trap367/// is happening and this is represented with sentinel values. This function368/// returns the description of the sentinel value which indicates a trap, if369/// any. If `None` is returned from this function then this builtin cannot370/// generate a trap.371#[allow(unreachable_code, unused_macro_rules, reason = "macro-generated code")]372pub fn trap_sentinel(&self) -> Option<TrapSentinel> {373macro_rules! trap_sentinel {374(375$(376$( #[$attr:meta] )*377$name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;378)*379) => {{380$(381$(#[$attr])*382if *self == BuiltinFunctionIndex::$name() {383let mut _ret = None;384$(_ret = Some(trap_sentinel!(@get $name $result));)?385return _ret;386}387)*388389None390}};391392// Growth-related functions return -2 as a sentinel.393(@get memory_grow pointer) => (TrapSentinel::NegativeTwo);394(@get table_grow_func_ref pointer) => (TrapSentinel::NegativeTwo);395(@get table_grow_gc_ref pointer) => (TrapSentinel::NegativeTwo);396(@get table_grow_cont_obj pointer) => (TrapSentinel::NegativeTwo);397398// Atomics-related functions return a negative value indicating trap399// indicate a trap.400(@get memory_atomic_notify u64) => (TrapSentinel::Negative);401(@get memory_atomic_wait32 u64) => (TrapSentinel::Negative);402(@get memory_atomic_wait64 u64) => (TrapSentinel::Negative);403404// GC returns an optional GC ref, encoded as a `u64` with a negative405// value indicating a trap.406(@get gc u64) => (TrapSentinel::Negative);407408// GC allocation functions return a u32 which is zero to indicate a409// trap.410(@get gc_alloc_raw u32) => (TrapSentinel::Falsy);411(@get array_new_data u32) => (TrapSentinel::Falsy);412(@get array_new_elem u32) => (TrapSentinel::Falsy);413414// The final epoch represents a trap415(@get new_epoch u64) => (TrapSentinel::NegativeOne);416417// These libcalls can't trap418(@get ref_func pointer) => (return None);419(@get table_get_lazy_init_func_ref pointer) => (return None);420(@get get_interned_func_ref pointer) => (return None);421(@get intern_func_ref_for_gc_heap u64) => (return None);422(@get is_subtype u32) => (return None);423(@get ceil_f32 f32) => (return None);424(@get ceil_f64 f64) => (return None);425(@get floor_f32 f32) => (return None);426(@get floor_f64 f64) => (return None);427(@get trunc_f32 f32) => (return None);428(@get trunc_f64 f64) => (return None);429(@get nearest_f32 f32) => (return None);430(@get nearest_f64 f64) => (return None);431(@get i8x16_swizzle i8x16) => (return None);432(@get i8x16_shuffle i8x16) => (return None);433(@get fma_f32x4 f32x4) => (return None);434(@get fma_f64x2 f64x2) => (return None);435436(@get cont_new pointer) => (TrapSentinel::Negative);437438(@get get_instance_id u32) => (return None);439440// Bool-returning functions use `false` as an indicator of a trap.441(@get $name:ident bool) => (TrapSentinel::Falsy);442443(@get $name:ident $ret:ident) => (444compile_error!(concat!("no trap sentinel registered for ", stringify!($name)))445)446}447448foreach_builtin_function!(trap_sentinel)449}450}451452453