Skip to main content

substrate_wasmtime_runtime/
libcalls.rs

1//! Runtime library calls.
2//!
3//! Note that Wasm compilers may sometimes perform these inline rather than
4//! calling them, particularly when CPUs have special instructions which compute
5//! them directly.
6//!
7//! These functions are called by compiled Wasm code, and therefore must take
8//! certain care about some things:
9//!
10//! * They must always be `pub extern "C"` and should only contain basic, raw
11//!   i32/i64/f32/f64/pointer parameters that are safe to pass across the system
12//!   ABI!
13//!
14//! * If any nested function propagates an `Err(trap)` out to the library
15//!   function frame, we need to raise it. This involves some nasty and quite
16//!   unsafe code under the covers! Notable, after raising the trap, drops
17//!   **will not** be run for local variables! This can lead to things like
18//!   leaking `InstanceHandle`s which leads to never deallocating JIT code,
19//!   instances, and modules! Therefore, always use nested blocks to ensure
20//!   drops run before raising a trap:
21//!
22//!   ```ignore
23//!   pub extern "C" fn my_lib_function(...) {
24//!       let result = {
25//!           // Do everything in here so drops run at the end of the block.
26//!           ...
27//!       };
28//!       if let Err(trap) = result {
29//!           // Now we can safely raise the trap without leaking!
30//!           raise_lib_trap(trap);
31//!       }
32//!   }
33//!   ```
34
35use crate::table::Table;
36use crate::traphandlers::raise_lib_trap;
37use crate::vmcontext::VMContext;
38use wasmtime_environ::wasm::{DataIndex, DefinedMemoryIndex, ElemIndex, MemoryIndex, TableIndex};
39
40/// Implementation of f32.ceil
41pub extern "C" fn wasmtime_f32_ceil(x: f32) -> f32 {
42    x.ceil()
43}
44
45/// Implementation of f32.floor
46pub extern "C" fn wasmtime_f32_floor(x: f32) -> f32 {
47    x.floor()
48}
49
50/// Implementation of f32.trunc
51pub extern "C" fn wasmtime_f32_trunc(x: f32) -> f32 {
52    x.trunc()
53}
54
55/// Implementation of f32.nearest
56#[allow(clippy::float_arithmetic, clippy::float_cmp)]
57pub extern "C" fn wasmtime_f32_nearest(x: f32) -> f32 {
58    // Rust doesn't have a nearest function, so do it manually.
59    if x == 0.0 {
60        // Preserve the sign of zero.
61        x
62    } else {
63        // Nearest is either ceil or floor depending on which is nearest or even.
64        let u = x.ceil();
65        let d = x.floor();
66        let um = (x - u).abs();
67        let dm = (x - d).abs();
68        if um < dm
69            || (um == dm && {
70                let h = u / 2.;
71                h.floor() == h
72            })
73        {
74            u
75        } else {
76            d
77        }
78    }
79}
80
81/// Implementation of f64.ceil
82pub extern "C" fn wasmtime_f64_ceil(x: f64) -> f64 {
83    x.ceil()
84}
85
86/// Implementation of f64.floor
87pub extern "C" fn wasmtime_f64_floor(x: f64) -> f64 {
88    x.floor()
89}
90
91/// Implementation of f64.trunc
92pub extern "C" fn wasmtime_f64_trunc(x: f64) -> f64 {
93    x.trunc()
94}
95
96/// Implementation of f64.nearest
97#[allow(clippy::float_arithmetic, clippy::float_cmp)]
98pub extern "C" fn wasmtime_f64_nearest(x: f64) -> f64 {
99    // Rust doesn't have a nearest function, so do it manually.
100    if x == 0.0 {
101        // Preserve the sign of zero.
102        x
103    } else {
104        // Nearest is either ceil or floor depending on which is nearest or even.
105        let u = x.ceil();
106        let d = x.floor();
107        let um = (x - u).abs();
108        let dm = (x - d).abs();
109        if um < dm
110            || (um == dm && {
111                let h = u / 2.;
112                h.floor() == h
113            })
114        {
115            u
116        } else {
117            d
118        }
119    }
120}
121
122/// Implementation of memory.grow for locally-defined 32-bit memories.
123pub unsafe extern "C" fn wasmtime_memory32_grow(
124    vmctx: *mut VMContext,
125    delta: u32,
126    memory_index: u32,
127) -> u32 {
128    let instance = (&mut *vmctx).instance();
129    let memory_index = DefinedMemoryIndex::from_u32(memory_index);
130
131    instance
132        .memory_grow(memory_index, delta)
133        .unwrap_or(u32::max_value())
134}
135
136/// Implementation of memory.grow for imported 32-bit memories.
137pub unsafe extern "C" fn wasmtime_imported_memory32_grow(
138    vmctx: *mut VMContext,
139    delta: u32,
140    memory_index: u32,
141) -> u32 {
142    let instance = (&mut *vmctx).instance();
143    let memory_index = MemoryIndex::from_u32(memory_index);
144
145    instance
146        .imported_memory_grow(memory_index, delta)
147        .unwrap_or(u32::max_value())
148}
149
150/// Implementation of memory.size for locally-defined 32-bit memories.
151pub unsafe extern "C" fn wasmtime_memory32_size(vmctx: *mut VMContext, memory_index: u32) -> u32 {
152    let instance = (&mut *vmctx).instance();
153    let memory_index = DefinedMemoryIndex::from_u32(memory_index);
154
155    instance.memory_size(memory_index)
156}
157
158/// Implementation of memory.size for imported 32-bit memories.
159pub unsafe extern "C" fn wasmtime_imported_memory32_size(
160    vmctx: *mut VMContext,
161    memory_index: u32,
162) -> u32 {
163    let instance = (&mut *vmctx).instance();
164    let memory_index = MemoryIndex::from_u32(memory_index);
165
166    instance.imported_memory_size(memory_index)
167}
168
169/// Implementation of `table.copy`.
170pub unsafe extern "C" fn wasmtime_table_copy(
171    vmctx: *mut VMContext,
172    dst_table_index: u32,
173    src_table_index: u32,
174    dst: u32,
175    src: u32,
176    len: u32,
177) {
178    let result = {
179        let dst_table_index = TableIndex::from_u32(dst_table_index);
180        let src_table_index = TableIndex::from_u32(src_table_index);
181        let instance = (&mut *vmctx).instance();
182        let dst_table = instance.get_table(dst_table_index);
183        let src_table = instance.get_table(src_table_index);
184        Table::copy(dst_table, src_table, dst, src, len)
185    };
186    if let Err(trap) = result {
187        raise_lib_trap(trap);
188    }
189}
190
191/// Implementation of `table.init`.
192pub unsafe extern "C" fn wasmtime_table_init(
193    vmctx: *mut VMContext,
194    table_index: u32,
195    elem_index: u32,
196    dst: u32,
197    src: u32,
198    len: u32,
199) {
200    let result = {
201        let table_index = TableIndex::from_u32(table_index);
202        let elem_index = ElemIndex::from_u32(elem_index);
203        let instance = (&mut *vmctx).instance();
204        instance.table_init(table_index, elem_index, dst, src, len)
205    };
206    if let Err(trap) = result {
207        raise_lib_trap(trap);
208    }
209}
210
211/// Implementation of `elem.drop`.
212pub unsafe extern "C" fn wasmtime_elem_drop(vmctx: *mut VMContext, elem_index: u32) {
213    let elem_index = ElemIndex::from_u32(elem_index);
214    let instance = (&mut *vmctx).instance();
215    instance.elem_drop(elem_index);
216}
217
218/// Implementation of `memory.copy` for locally defined memories.
219pub unsafe extern "C" fn wasmtime_defined_memory_copy(
220    vmctx: *mut VMContext,
221    memory_index: u32,
222    dst: u32,
223    src: u32,
224    len: u32,
225) {
226    let result = {
227        let memory_index = DefinedMemoryIndex::from_u32(memory_index);
228        let instance = (&mut *vmctx).instance();
229        instance.defined_memory_copy(memory_index, dst, src, len)
230    };
231    if let Err(trap) = result {
232        raise_lib_trap(trap);
233    }
234}
235
236/// Implementation of `memory.copy` for imported memories.
237pub unsafe extern "C" fn wasmtime_imported_memory_copy(
238    vmctx: *mut VMContext,
239    memory_index: u32,
240    dst: u32,
241    src: u32,
242    len: u32,
243) {
244    let result = {
245        let memory_index = MemoryIndex::from_u32(memory_index);
246        let instance = (&mut *vmctx).instance();
247        instance.imported_memory_copy(memory_index, dst, src, len)
248    };
249    if let Err(trap) = result {
250        raise_lib_trap(trap);
251    }
252}
253
254/// Implementation of `memory.fill` for locally defined memories.
255pub unsafe extern "C" fn wasmtime_memory_fill(
256    vmctx: *mut VMContext,
257    memory_index: u32,
258    dst: u32,
259    val: u32,
260    len: u32,
261) {
262    let result = {
263        let memory_index = DefinedMemoryIndex::from_u32(memory_index);
264        let instance = (&mut *vmctx).instance();
265        instance.defined_memory_fill(memory_index, dst, val, len)
266    };
267    if let Err(trap) = result {
268        raise_lib_trap(trap);
269    }
270}
271
272/// Implementation of `memory.fill` for imported memories.
273pub unsafe extern "C" fn wasmtime_imported_memory_fill(
274    vmctx: *mut VMContext,
275    memory_index: u32,
276    dst: u32,
277    val: u32,
278    len: u32,
279) {
280    let result = {
281        let memory_index = MemoryIndex::from_u32(memory_index);
282        let instance = (&mut *vmctx).instance();
283        instance.imported_memory_fill(memory_index, dst, val, len)
284    };
285    if let Err(trap) = result {
286        raise_lib_trap(trap);
287    }
288}
289
290/// Implementation of `memory.init`.
291pub unsafe extern "C" fn wasmtime_memory_init(
292    vmctx: *mut VMContext,
293    memory_index: u32,
294    data_index: u32,
295    dst: u32,
296    src: u32,
297    len: u32,
298) {
299    let result = {
300        let memory_index = MemoryIndex::from_u32(memory_index);
301        let data_index = DataIndex::from_u32(data_index);
302        let instance = (&mut *vmctx).instance();
303        instance.memory_init(memory_index, data_index, dst, src, len)
304    };
305    if let Err(trap) = result {
306        raise_lib_trap(trap);
307    }
308}
309
310/// Implementation of `data.drop`.
311pub unsafe extern "C" fn wasmtime_data_drop(vmctx: *mut VMContext, data_index: u32) {
312    let data_index = DataIndex::from_u32(data_index);
313    let instance = (&mut *vmctx).instance();
314    instance.data_drop(data_index)
315}