Add initial support for the multi-memory proposal (#2263)
This commit adds initial (gated) support for the multi-memory wasm proposal. This was actually quite easy since almost all of wasmtime already expected multi-memory to be implemented one day. The only real substantive change is the `memory.copy` intrinsic changes, which now accounts for the source/destination memories possibly being different.
This commit is contained in:
@@ -224,26 +224,6 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
||||
(sig, BuiltinFunctionIndex::elem_drop())
|
||||
}
|
||||
|
||||
fn get_memory_copy_func(
|
||||
&mut self,
|
||||
func: &mut Function,
|
||||
memory_index: MemoryIndex,
|
||||
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
|
||||
if let Some(defined_memory_index) = self.module.defined_memory_index(memory_index) {
|
||||
(
|
||||
self.builtin_function_signatures.defined_memory_copy(func),
|
||||
defined_memory_index.index(),
|
||||
BuiltinFunctionIndex::defined_memory_copy(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self.builtin_function_signatures.imported_memory_copy(func),
|
||||
memory_index.index(),
|
||||
BuiltinFunctionIndex::imported_memory_copy(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_memory_fill_func(
|
||||
&mut self,
|
||||
func: &mut Function,
|
||||
@@ -1199,23 +1179,25 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
fn translate_memory_copy(
|
||||
&mut self,
|
||||
mut pos: FuncCursor,
|
||||
memory_index: MemoryIndex,
|
||||
_heap: ir::Heap,
|
||||
src_index: MemoryIndex,
|
||||
_src_heap: ir::Heap,
|
||||
dst_index: MemoryIndex,
|
||||
_dst_heap: ir::Heap,
|
||||
dst: ir::Value,
|
||||
src: ir::Value,
|
||||
len: ir::Value,
|
||||
) -> WasmResult<()> {
|
||||
let (func_sig, memory_index, func_idx) =
|
||||
self.get_memory_copy_func(&mut pos.func, memory_index);
|
||||
let src_index = pos.ins().iconst(I32, i64::from(src_index.as_u32()));
|
||||
let dst_index = pos.ins().iconst(I32, i64::from(dst_index.as_u32()));
|
||||
|
||||
let memory_index_arg = pos.ins().iconst(I32, memory_index as i64);
|
||||
|
||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||
let (vmctx, func_addr) = self
|
||||
.translate_load_builtin_function_address(&mut pos, BuiltinFunctionIndex::memory_copy());
|
||||
|
||||
let func_sig = self.builtin_function_signatures.memory_copy(&mut pos.func);
|
||||
pos.ins().call_indirect(
|
||||
func_sig,
|
||||
func_addr,
|
||||
&[vmctx, memory_index_arg, dst, src, len],
|
||||
&[vmctx, dst_index, dst, src_index, src, len],
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -18,10 +18,8 @@ macro_rules! foreach_builtin_function {
|
||||
table_init(vmctx, i32, i32, i32, i32, i32) -> ();
|
||||
/// Returns an index for wasm's `elem.drop`.
|
||||
elem_drop(vmctx, i32) -> ();
|
||||
/// Returns an index for wasm's `memory.copy` for locally defined memories.
|
||||
defined_memory_copy(vmctx, i32, i32, i32, i32) -> ();
|
||||
/// Returns an index for wasm's `memory.copy` for imported memories.
|
||||
imported_memory_copy(vmctx, i32, i32, i32, i32) -> ();
|
||||
/// Returns an index for wasm's `memory.copy`
|
||||
memory_copy(vmctx, i32, i32, i32, i32, i32) -> ();
|
||||
/// Returns an index for wasm's `memory.fill` for locally defined memories.
|
||||
memory_fill(vmctx, i32, i32, i32, i32) -> ();
|
||||
/// Returns an index for wasm's `memory.fill` for imported memories.
|
||||
|
||||
@@ -604,29 +604,31 @@ impl Instance {
|
||||
// dropping a non-passive element is a no-op (not a trap).
|
||||
}
|
||||
|
||||
/// Do a `memory.copy` for a locally defined memory.
|
||||
/// Do a `memory.copy`
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `Trap` error when the source or destination ranges are out of
|
||||
/// bounds.
|
||||
pub(crate) fn defined_memory_copy(
|
||||
pub(crate) fn memory_copy(
|
||||
&self,
|
||||
memory_index: DefinedMemoryIndex,
|
||||
dst_index: MemoryIndex,
|
||||
dst: u32,
|
||||
src_index: MemoryIndex,
|
||||
src: u32,
|
||||
len: u32,
|
||||
) -> Result<(), Trap> {
|
||||
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
|
||||
|
||||
let memory = self.memory(memory_index);
|
||||
let src_mem = self.get_memory(src_index);
|
||||
let dst_mem = self.get_memory(dst_index);
|
||||
|
||||
if src
|
||||
.checked_add(len)
|
||||
.map_or(true, |n| n as usize > memory.current_length)
|
||||
.map_or(true, |n| n as usize > src_mem.current_length)
|
||||
|| dst
|
||||
.checked_add(len)
|
||||
.map_or(true, |m| m as usize > memory.current_length)
|
||||
.map_or(true, |m| m as usize > dst_mem.current_length)
|
||||
{
|
||||
return Err(Trap::wasm(ir::TrapCode::HeapOutOfBounds));
|
||||
}
|
||||
@@ -637,31 +639,14 @@ impl Instance {
|
||||
// Bounds and casts are checked above, by this point we know that
|
||||
// everything is safe.
|
||||
unsafe {
|
||||
let dst = memory.base.add(dst);
|
||||
let src = memory.base.add(src);
|
||||
let dst = dst_mem.base.add(dst);
|
||||
let src = src_mem.base.add(src);
|
||||
ptr::copy(src, dst, len as usize);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform a `memory.copy` on an imported memory.
|
||||
pub(crate) fn imported_memory_copy(
|
||||
&self,
|
||||
memory_index: MemoryIndex,
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
) -> Result<(), Trap> {
|
||||
let import = self.imported_memory(memory_index);
|
||||
unsafe {
|
||||
let foreign_instance = (&*import.vmctx).instance();
|
||||
let foreign_memory = &*import.from;
|
||||
let foreign_index = foreign_instance.memory_index(foreign_memory);
|
||||
foreign_instance.defined_memory_copy(foreign_index, dst, src, len)
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform the `memory.fill` operation on a locally defined memory.
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@@ -351,35 +351,19 @@ pub unsafe extern "C" fn wasmtime_elem_drop(vmctx: *mut VMContext, elem_index: u
|
||||
}
|
||||
|
||||
/// Implementation of `memory.copy` for locally defined memories.
|
||||
pub unsafe extern "C" fn wasmtime_defined_memory_copy(
|
||||
pub unsafe extern "C" fn wasmtime_memory_copy(
|
||||
vmctx: *mut VMContext,
|
||||
memory_index: u32,
|
||||
dst_index: u32,
|
||||
dst: u32,
|
||||
src_index: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
) {
|
||||
let result = {
|
||||
let memory_index = DefinedMemoryIndex::from_u32(memory_index);
|
||||
let src_index = MemoryIndex::from_u32(src_index);
|
||||
let dst_index = MemoryIndex::from_u32(dst_index);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.defined_memory_copy(memory_index, dst, src, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `memory.copy` for imported memories.
|
||||
pub unsafe extern "C" fn wasmtime_imported_memory_copy(
|
||||
vmctx: *mut VMContext,
|
||||
memory_index: u32,
|
||||
dst: u32,
|
||||
src: u32,
|
||||
len: u32,
|
||||
) {
|
||||
let result = {
|
||||
let memory_index = MemoryIndex::from_u32(memory_index);
|
||||
let instance = (&mut *vmctx).instance();
|
||||
instance.imported_memory_copy(memory_index, dst, src, len)
|
||||
instance.memory_copy(dst_index, dst, src_index, src, len)
|
||||
};
|
||||
if let Err(trap) = result {
|
||||
raise_lib_trap(trap);
|
||||
|
||||
@@ -577,10 +577,7 @@ impl VMBuiltinFunctionsArray {
|
||||
wasmtime_table_grow as usize;
|
||||
ptrs[BuiltinFunctionIndex::table_init().index() as usize] = wasmtime_table_init as usize;
|
||||
ptrs[BuiltinFunctionIndex::elem_drop().index() as usize] = wasmtime_elem_drop as usize;
|
||||
ptrs[BuiltinFunctionIndex::defined_memory_copy().index() as usize] =
|
||||
wasmtime_defined_memory_copy as usize;
|
||||
ptrs[BuiltinFunctionIndex::imported_memory_copy().index() as usize] =
|
||||
wasmtime_imported_memory_copy as usize;
|
||||
ptrs[BuiltinFunctionIndex::memory_copy().index() as usize] = wasmtime_memory_copy as usize;
|
||||
ptrs[BuiltinFunctionIndex::memory_fill().index() as usize] = wasmtime_memory_fill as usize;
|
||||
ptrs[BuiltinFunctionIndex::imported_memory_fill().index() as usize] =
|
||||
wasmtime_imported_memory_fill as usize;
|
||||
|
||||
@@ -238,7 +238,7 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether the WebAssembly multi-value proposal will
|
||||
/// Configures whether the WebAssembly multi-value [proposal] will
|
||||
/// be enabled for compilation.
|
||||
///
|
||||
/// This feature gates functions and blocks returning multiple values in a
|
||||
@@ -252,6 +252,20 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether the WebAssembly multi-memory [proposal] will
|
||||
/// be enabled for compilation.
|
||||
///
|
||||
/// This feature gates modules having more than one linear memory
|
||||
/// declaration or import.
|
||||
///
|
||||
/// This is `false` by default.
|
||||
///
|
||||
/// [proposal]: https://github.com/webassembly/multi-memory
|
||||
pub fn wasm_multi_memory(&mut self, enable: bool) -> &mut Self {
|
||||
self.features.multi_memory = enable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures which compilation strategy will be used for wasm modules.
|
||||
///
|
||||
/// This method can be used to configure which compiler is used for wasm
|
||||
|
||||
Reference in New Issue
Block a user