Implement data.drop and memory.init and get the rest of the bulk memory spec tests passing (#1264)

* Enable the already-passing `bulk-memoryoperations/imports.wast` test

* Implement support for the `memory.init` instruction and passive data

This adds support for passive data segments and the `memory.init` instruction
from the bulk memory operations proposal. Passive data segments are stored on
the Wasm module and then `memory.init` instructions copy their contents into
memory.

* Implement the `data.drop` instruction

This allows wasm modules to deallocate passive data segments that it doesn't
need anymore. We keep track of which segments have not been dropped on an
`Instance` and when dropping them, remove the entry from the instance's hash
map. The module always needs all of the segments for new instantiations.

* Enable final bulk memory operations spec test

This requires special casing an expected error message for an `assert_trap`,
since the expected error message contains the index of an uninitialized table
element, but our trap implementation doesn't save that diagnostic information
and shepherd it out.
This commit is contained in:
Nick Fitzgerald
2020-03-10 07:30:11 -07:00
committed by GitHub
parent 11510ec426
commit 674a6208d8
10 changed files with 237 additions and 42 deletions

View File

@@ -72,9 +72,17 @@ impl BuiltinFunctionIndex {
pub const fn get_imported_memory_fill_index() -> Self {
Self(10)
}
/// Returns an index for wasm's `memory.init` instruction.
pub const fn get_memory_init_index() -> Self {
Self(11)
}
/// Returns an index for wasm's `data.drop` instruction.
pub const fn get_data_drop_index() -> Self {
Self(12)
}
/// Returns the total number of builtin functions.
pub const fn builtin_functions_total_number() -> u32 {
11
13
}
/// Return the index as an u32 number.
@@ -120,6 +128,12 @@ pub struct FuncEnvironment<'module_environment> {
/// (it's the same for both local and imported memories).
memory_fill_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `memory.init`.
memory_init_sig: Option<ir::SigRef>,
/// The external function signature for implementing wasm's `data.drop`.
data_drop_sig: Option<ir::SigRef>,
/// Offsets to struct fields accessed by JIT code.
offsets: VMOffsets,
}
@@ -140,6 +154,8 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
elem_drop_sig: None,
memory_copy_sig: None,
memory_fill_sig: None,
memory_init_sig: None,
data_drop_sig: None,
offsets: VMOffsets::new(target_config.pointer_bytes(), module),
}
}
@@ -423,6 +439,58 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
}
}
fn get_memory_init_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.memory_init_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Memory index.
AbiParam::new(I32),
// Data index.
AbiParam::new(I32),
// Destination address.
AbiParam::new(I32),
// Source index within the data segment.
AbiParam::new(I32),
// Length.
AbiParam::new(I32),
// Source location.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.memory_init_sig = Some(sig);
sig
}
fn get_memory_init_func(&mut self, func: &mut Function) -> (ir::SigRef, BuiltinFunctionIndex) {
let sig = self.get_memory_init_sig(func);
(sig, BuiltinFunctionIndex::get_memory_init_index())
}
fn get_data_drop_sig(&mut self, func: &mut Function) -> ir::SigRef {
let sig = self.data_drop_sig.unwrap_or_else(|| {
func.import_signature(Signature {
params: vec![
AbiParam::special(self.pointer_type(), ArgumentPurpose::VMContext),
// Data index.
AbiParam::new(I32),
],
returns: vec![],
call_conv: self.target_config.default_call_conv,
})
});
self.data_drop_sig = Some(sig);
sig
}
fn get_data_drop_func(&mut self, func: &mut Function) -> (ir::SigRef, BuiltinFunctionIndex) {
let sig = self.get_data_drop_sig(func);
(sig, BuiltinFunctionIndex::get_data_drop_index())
}
/// Translates load of builtin function and returns a pair of values `vmctx`
/// and address of the loaded function.
fn translate_load_builtin_function_address(
@@ -1076,23 +1144,47 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
fn translate_memory_init(
&mut self,
_pos: FuncCursor,
_index: MemoryIndex,
mut pos: FuncCursor,
memory_index: MemoryIndex,
_heap: ir::Heap,
_seg_index: u32,
_dst: ir::Value,
_src: ir::Value,
_len: ir::Value,
seg_index: u32,
dst: ir::Value,
src: ir::Value,
len: ir::Value,
) -> WasmResult<()> {
Err(WasmError::Unsupported(
"bulk memory: `memory.init`".to_string(),
))
let (func_sig, func_idx) = self.get_memory_init_func(&mut pos.func);
let memory_index_arg = pos.ins().iconst(I32, memory_index.index() as i64);
let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
let src_loc = pos.srcloc();
let src_loc_arg = pos.ins().iconst(I32, src_loc.bits() as i64);
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
pos.ins().call_indirect(
func_sig,
func_addr,
&[
vmctx,
memory_index_arg,
seg_index_arg,
dst,
src,
len,
src_loc_arg,
],
);
Ok(())
}
fn translate_data_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> {
Err(WasmError::Unsupported(
"bulk memory: `data.drop`".to_string(),
))
fn translate_data_drop(&mut self, mut pos: FuncCursor, seg_index: u32) -> WasmResult<()> {
let (func_sig, func_idx) = self.get_data_drop_func(&mut pos.func);
let seg_index_arg = pos.ins().iconst(I32, seg_index as i64);
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
pos.ins()
.call_indirect(func_sig, func_addr, &[vmctx, seg_index_arg]);
Ok(())
}
fn translate_table_size(