Get most spectests passing

This commit is contained in:
Lachlan Sneff
2019-01-17 13:09:05 -08:00
parent c74eed8a06
commit 1dbbaa30b6
10 changed files with 316 additions and 101 deletions

View File

@ -4,7 +4,6 @@ use cranelift_codegen::{
ir::{self, InstBuilder},
isa,
};
use cranelift_entity::EntityRef;
use cranelift_wasm::{self, FuncEnvironment, ModuleEnvironment};
use wasmer_runtime::{
memory::LinearMemory,
@ -105,7 +104,6 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
});
let offset = imported_global_index.index() * vm::ImportedGlobal::size() as usize;
let imported_global_addr = func.create_global_value(ir::GlobalValueData::IAddImm {
base: imported_globals_base_addr,
offset: (offset as i64).into(),
@ -282,7 +280,7 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
LocalOrImport::Import(imported_table_index) => {
let imported_tables_base = func.create_global_value(ir::GlobalValueData::Load {
base: vmctx,
offset: (vm::Ctx::offset_imported_memories() as i32).into(),
offset: (vm::Ctx::offset_imported_tables() as i32).into(),
global_type: ptr_type,
readonly: true,
});
@ -525,9 +523,9 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
fn translate_memory_grow(
&mut self,
mut pos: FuncCursor,
index: cranelift_wasm::MemoryIndex,
clif_mem_index: cranelift_wasm::MemoryIndex,
_heap: ir::Heap,
val: ir::Value,
by_value: ir::Value,
) -> cranelift_wasm::WasmResult<ir::Value> {
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
@ -539,26 +537,42 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let grow_mem_func = pos.func.import_function(ir::ExtFuncData {
// `ir::ExternalName` for static_grow_memory`
name: ir::ExternalName::user(1, 0),
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (name, mem_index) = match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => {
(
// local_static_memory_grow
ir::ExternalName::user(1, 0),
local_mem_index.index(),
)
}
LocalOrImport::Import(imported_mem_index) => {
(
// imported_static_memory_grow
ir::ExternalName::user(1, 2),
imported_mem_index.index(),
)
}
};
let mem_grow_func = pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
});
// Create a memory index value.
let memory_index = pos.ins().iconst(ir::types::I32, index.index() as i64);
let const_mem_index = pos.ins().iconst(ir::types::I32, mem_index as i64);
// Create a VMContext value.
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
// Insert call instructions for `grow_memory`.
let call_inst = pos.ins().call(grow_mem_func, &[memory_index, val, vmctx]);
let call_inst = pos
.ins()
.call(mem_grow_func, &[const_mem_index, by_value, vmctx]);
// Return value.
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
@ -570,9 +584,40 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
fn translate_memory_size(
&mut self,
mut pos: FuncCursor,
index: cranelift_wasm::MemoryIndex,
clif_mem_index: cranelift_wasm::MemoryIndex,
_heap: ir::Heap,
) -> cranelift_wasm::WasmResult<ir::Value> {
// let signature = pos.func.import_signature(ir::Signature {
// call_conv: self.target_config().default_call_conv,
// params: vec![
// ir::AbiParam::new(ir::types::I32),
// ir::AbiParam::special(self.pointer_type(), ir::ArgumentPurpose::VMContext),
// ],
// returns: vec![ir::AbiParam::new(ir::types::I32)],
// });
// let size_mem_func = pos.func.import_function(ir::ExtFuncData {
// // `ir::ExternalName` for static_grow_memory`
// name: ir::ExternalName::user(1, 1),
// signature,
// colocated: false,
// });
// // Create a memory index value.
// let memory_index = pos.ins().iconst(ir::types::I32, index.index() as i64);
// // Create a VMContext value.
// let vmctx = pos
// .func
// .special_param(ir::ArgumentPurpose::VMContext)
// .expect("missing vmctx parameter");
// // Insert call instructions for `grow_memory`.
// let call_inst = pos.ins().call(size_mem_func, &[memory_index, vmctx]);
// // Return value.
// Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
let signature = pos.func.import_signature(ir::Signature {
call_conv: self.target_config().default_call_conv,
params: vec![
@ -582,26 +627,39 @@ impl<'env, 'module, 'isa> FuncEnvironment for FuncEnv<'env, 'module, 'isa> {
returns: vec![ir::AbiParam::new(ir::types::I32)],
});
let size_mem_func = pos.func.import_function(ir::ExtFuncData {
// `ir::ExternalName` for static_grow_memory`
name: ir::ExternalName::user(1, 1),
let mem_index: MemoryIndex = Converter(clif_mem_index).into();
let (name, mem_index) = match mem_index.local_or_import(self.env.module) {
LocalOrImport::Local(local_mem_index) => {
(
// local_static_memory_size
ir::ExternalName::user(1, 1),
local_mem_index.index(),
)
}
LocalOrImport::Import(imported_mem_index) => {
(
// imported_static_memory_size
ir::ExternalName::user(1, 3),
imported_mem_index.index(),
)
}
};
let mem_grow_func = pos.func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
});
// Create a memory index value.
let memory_index = pos.ins().iconst(ir::types::I32, index.index() as i64);
// Create a VMContext value.
let const_mem_index = pos.ins().iconst(ir::types::I32, mem_index as i64);
let vmctx = pos
.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("missing vmctx parameter");
// Insert call instructions for `grow_memory`.
let call_inst = pos.ins().call(size_mem_func, &[memory_index, vmctx]);
let call_inst = pos.ins().call(mem_grow_func, &[const_mem_index, vmctx]);
// Return value.
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
}
}

View File

@ -20,14 +20,21 @@ pub struct Relocation {
pub target: RelocationType,
}
#[derive(Debug, Clone, Copy)]
pub enum VmCall {
LocalStaticMemoryGrow,
LocalStaticMemorySize,
ImportedStaticMemoryGrow,
ImportedStaticMemorySize,
}
/// Specify the type of relocation
#[derive(Debug, Clone)]
pub enum RelocationType {
Normal(LocalFuncIndex),
Intrinsic(String),
LibCall(LibCall),
StaticGrowMemory,
StaticCurrentMemory,
VmCall(VmCall),
}
/// Implementation of a relocation sink that just saves all the information for later
@ -69,11 +76,13 @@ impl binemit::RelocSink for RelocSink {
namespace: 1,
index,
} => {
let target = match index {
0 => RelocationType::StaticGrowMemory,
1 => RelocationType::StaticCurrentMemory,
let target = RelocationType::VmCall(match index {
0 => VmCall::LocalStaticMemoryGrow,
1 => VmCall::LocalStaticMemorySize,
2 => VmCall::ImportedStaticMemoryGrow,
3 => VmCall::ImportedStaticMemorySize,
_ => unimplemented!(),
};
});
self.func_relocs.push(Relocation {
reloc,
offset,

View File

@ -1,5 +1,5 @@
use crate::libcalls;
use crate::relocation::{Reloc, RelocSink, Relocation, RelocationType, TrapSink};
use crate::relocation::{Reloc, RelocSink, Relocation, RelocationType, TrapSink, VmCall};
use byteorder::{ByteOrder, LittleEndian};
use cranelift_codegen::{ir, isa, Context};
use std::mem;
@ -87,8 +87,6 @@ impl FuncResolverBuilder {
// called in this way.
self.resolver.lookup(local_func_index).unwrap().as_ptr() as isize
}
RelocationType::StaticCurrentMemory => vmcalls::memory_size as isize,
RelocationType::StaticGrowMemory => vmcalls::memory_grow_static as isize,
RelocationType::LibCall(libcall) => match libcall {
ir::LibCall::CeilF32 => libcalls::ceilf32 as isize,
ir::LibCall::FloorF32 => libcalls::floorf32 as isize,
@ -106,6 +104,16 @@ impl FuncResolverBuilder {
RelocationType::Intrinsic(ref name) => {
panic!("unexpected intrinsic {}", name);
}
RelocationType::VmCall(vmcall) => match vmcall {
VmCall::LocalStaticMemoryGrow => vmcalls::local_static_memory_grow as _,
VmCall::LocalStaticMemorySize => vmcalls::local_static_memory_size as _,
VmCall::ImportedStaticMemoryGrow => {
vmcalls::imported_static_memory_grow as _
}
VmCall::ImportedStaticMemorySize => {
vmcalls::imported_static_memory_size as _
}
},
};
// We need the address of the current function

View File

@ -4,7 +4,7 @@ use wasmer_runtime::{import::Imports, Instance};
fn main() {
let mut instance = create_module_1();
let result = instance.call("type-i64", &[]);
let result = instance.call("get-0-ref", &[]);
println!("result: {:?}", result);
}
@ -18,21 +18,44 @@ fn main() {
// }
fn create_module_1() -> Instance {
let module_str = "(module
(type (;0;) (func (result i64)))
(func (;0;) (type 0) (result i64)
i64.const 356)
(func (;1;) (type 0) (result i64)
i32.const 0
call_indirect (type 0))
(table (;0;) 2 anyfunc)
(export \"type-i64\" (func 1))
(elem (;0;) (i32.const 0) 0 1))
";
let module_str = r#"(module
(type (;0;) (func (result i32)))
(import "spectest" "global_i32" (global (;0;) i32))
(func (;0;) (type 0) (result i32)
get_global 0)
(func (;1;) (type 0) (result i32)
get_global 1)
(global (;1;) i32 (get_global 0))
(export "get-0" (func 0))
(export "get-0-ref" (func 1)))
"#;
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
let module = wasmer_runtime::compile(&wasm_binary[..], &CraneliftCompiler::new())
.expect("WASM can't be compiled");
module
.instantiate(&mut Imports::new())
.instantiate(&mut generate_imports())
.expect("WASM can't be instantiated")
}
static IMPORT_MODULE: &str = r#"
(module
(type $t0 (func (param i32)))
(type $t1 (func))
(func $print_i32 (export "print_i32") (type $t0) (param $lhs i32))
(func $print (export "print") (type $t1))
(table $table (export "table") 10 20 anyfunc)
(memory $memory (export "memory") 1 2)
(global $global_i32 (export "global_i32") i32 (i32.const 666)))
"#;
pub fn generate_imports() -> Imports {
let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed");
let module = wasmer_runtime::compile(&wasm_binary[..], &CraneliftCompiler::new())
.expect("WASM can't be compiled");
let instance = module
.instantiate(&mut Imports::new())
.expect("WASM can't be instantiated");
let mut imports = Imports::new();
imports.register("spectest", instance);
imports
}

View File

@ -12,7 +12,7 @@ use crate::{
},
vm,
};
use std::slice;
use std::{mem, slice};
#[derive(Debug)]
pub struct LocalBacking {
@ -106,20 +106,27 @@ impl LocalBacking {
assert!((memory_desc.min * LinearMemory::PAGE_SIZE) as usize >= data_top);
let mem: &mut LinearMemory = &mut memories[local_memory_index];
let to_init = &mut mem[init_base..init_base + init.data.len()];
to_init.copy_from_slice(&init.data);
let mem_init_view = &mut mem[init_base..init_base + init.data.len()];
mem_init_view.copy_from_slice(&init.data);
}
LocalOrImport::Import(imported_memory_index) => {
let _ = imported_memory_index;
let _ = imports;
unimplemented!()
let vm_imported_memory = imports.imported_memory(imported_memory_index);
unsafe {
let local_memory = &(*vm_imported_memory.memory);
let memory_slice =
slice::from_raw_parts_mut(local_memory.base, local_memory.size);
let mem_init_view =
&mut memory_slice[init_base..init_base + init.data.len()];
mem_init_view.copy_from_slice(&init.data);
}
}
}
}
memories
.iter_mut()
.map(|(_, mem)| mem.into_vm_memory())
.map(|(index, mem)| mem.into_vm_memory(index))
.collect::<Map<_, _>>()
.into_boxed_map()
}
@ -154,24 +161,17 @@ impl LocalBacking {
}
} as usize;
assert!(
init_base + init.elements.len()
<= match init.table_index.local_or_import(module) {
LocalOrImport::Local(local_table_index) => {
module.tables[local_table_index].min
}
LocalOrImport::Import(imported_table_index) => {
let (_, table_desc) = module.imported_tables[imported_table_index];
table_desc.min
}
} as usize
);
match init.table_index.local_or_import(module) {
LocalOrImport::Local(local_table_index) => {
let table = &mut tables[local_table_index];
match table.elements {
TableElements::Anyfunc(ref mut elements) => {
if elements.len() < init_base + init.elements.len() {
// Grow the table if it's too small.
elements
.resize(init_base + init.elements.len(), vm::Anyfunc::null());
}
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.func_assoc[func_index];
let sig_id = vm::SigId(sig_index.index() as u32);
@ -196,20 +196,33 @@ impl LocalBacking {
}
}
LocalOrImport::Import(imported_table_index) => {
let imported_table = &imports.tables[imported_table_index];
let imported_local_table_slice = unsafe {
let imported_local_table = (*imported_table).table;
slice::from_raw_parts_mut(
(*imported_local_table).base as *mut vm::Anyfunc,
(*imported_local_table).current_elements,
)
};
let (_, table_description) = module.imported_tables[imported_table_index];
match table_description.ty {
ElementType::Anyfunc => {
let imported_table = &imports.tables[imported_table_index];
let imported_local_table = (*imported_table).table;
let mut elements = unsafe {
Vec::from_raw_parts(
(*imported_local_table).base as *mut vm::Anyfunc,
(*imported_local_table).current_elements,
(*imported_local_table).capacity,
)
};
if elements.len() < init_base + init.elements.len() {
// Grow the table if it's too small.
elements
.resize(init_base + init.elements.len(), vm::Anyfunc::null());
// Since the vector may have changed location after reallocating,
// we must fix the base, current_elements, and capacity fields.
unsafe {
(*imported_local_table).base = elements.as_mut_ptr() as *mut u8;
(*imported_local_table).current_elements = elements.len();
(*imported_local_table).capacity = elements.capacity();
}
}
for (i, &func_index) in init.elements.iter().enumerate() {
let sig_index = module.func_assoc[func_index];
let sig_id = vm::SigId(sig_index.index() as u32);
@ -228,9 +241,13 @@ impl LocalBacking {
}
};
imported_local_table_slice[init_base + i] =
vm::Anyfunc { func_data, sig_id };
elements[init_base + i] = vm::Anyfunc { func_data, sig_id };
}
// println!("imported elements: {:#?}", elements);
// THIS IS EXTREMELY IMPORTANT.
mem::forget(elements);
}
}
}
@ -277,10 +294,10 @@ impl LocalBacking {
#[derive(Debug)]
pub struct ImportBacking {
pub functions: BoxedMap<ImportedFuncIndex, vm::ImportedFunc>,
pub memories: BoxedMap<ImportedMemoryIndex, vm::ImportedMemory>,
pub tables: BoxedMap<ImportedTableIndex, vm::ImportedTable>,
pub globals: BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>,
pub(crate) functions: BoxedMap<ImportedFuncIndex, vm::ImportedFunc>,
pub(crate) memories: BoxedMap<ImportedMemoryIndex, vm::ImportedMemory>,
pub(crate) tables: BoxedMap<ImportedTableIndex, vm::ImportedTable>,
pub(crate) globals: BoxedMap<ImportedGlobalIndex, vm::ImportedGlobal>,
}
impl ImportBacking {
@ -296,6 +313,10 @@ impl ImportBacking {
globals: import_globals(module, imports)?,
})
}
pub fn imported_memory(&self, memory_index: ImportedMemoryIndex) -> vm::ImportedMemory {
self.memories[memory_index].clone()
}
}
fn import_functions(

View File

@ -235,7 +235,7 @@ impl InstanceInner {
LocalOrImport::Local(local_mem_index) => {
let vm_mem = &mut self.backing.memories[local_mem_index];
(
unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) },
unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory(local_mem_index)) },
Context::Internal,
*module
.memories

View File

@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut};
use crate::{
mmap::{Mmap, Protect},
types::Memory,
types::{LocalMemoryIndex, Memory},
vm,
};
@ -107,10 +107,11 @@ impl LinearMemory {
self.max.unwrap_or(Self::MAX_PAGES)
}
pub(crate) fn into_vm_memory(&mut self) -> vm::LocalMemory {
pub(crate) fn into_vm_memory(&mut self, index: LocalMemoryIndex) -> vm::LocalMemory {
vm::LocalMemory {
base: self.base(),
size: self.size(),
index,
}
}

View File

@ -4,7 +4,7 @@ use crate::types::{ElementType, Table};
#[derive(Debug, Clone)]
pub enum TableElements {
/// This is intended to be a caller-checked Anyfunc.
Anyfunc(Box<[vm::Anyfunc]>),
Anyfunc(Vec<vm::Anyfunc>),
}
#[derive(Debug)]
@ -16,12 +16,20 @@ pub struct TableBacking {
impl TableBacking {
pub fn new(table: &Table) -> Self {
match table.ty {
ElementType::Anyfunc => Self {
elements: TableElements::Anyfunc(
vec![vm::Anyfunc::null(); table.min as usize].into_boxed_slice(),
),
ElementType::Anyfunc => {
let initial_table_backing_len = match table.max {
Some(max) => max,
None => table.min,
} as usize;
Self {
elements: TableElements::Anyfunc(vec![
vm::Anyfunc::null();
initial_table_backing_len
]),
max: table.max,
},
}
}
}
}
@ -30,6 +38,7 @@ impl TableBacking {
TableElements::Anyfunc(ref mut funcs) => vm::LocalTable {
base: funcs.as_mut_ptr() as *mut u8,
current_elements: funcs.len(),
capacity: funcs.capacity(),
},
}
}

View File

@ -1,5 +1,5 @@
use crate::backing::ImportBacking;
pub use crate::backing::LocalBacking;
pub use crate::backing::{ImportBacking, LocalBacking};
use crate::types::LocalMemoryIndex;
use std::ffi::c_void;
use std::{mem, ptr};
@ -29,10 +29,11 @@ pub struct Ctx {
/// The local backing of the instance that created this vmctx.
pub local_backing: *mut LocalBacking,
/// The import backing of the parent instance.
pub import_backing: *mut ImportBacking,
/// Host data
pub data: *mut c_void,
/// Host data finalizer
pub data_finalizer: Option<extern "C" fn(data: *mut c_void)>,
}
@ -61,6 +62,8 @@ impl Ctx {
imported_funcs: import_backing.functions.as_mut_ptr(),
local_backing,
import_backing,
data: ptr::null_mut(),
data_finalizer: None,
}
@ -83,6 +86,8 @@ impl Ctx {
imported_funcs: import_backing.functions.as_mut_ptr(),
local_backing,
import_backing,
data,
data_finalizer,
}
@ -156,6 +161,8 @@ pub struct LocalTable {
pub base: *mut u8,
/// Number of elements in the table (NOT necessarily the size of the table in bytes!).
pub current_elements: usize,
/// The number of elements that can fit into the memory allocated for this table.
pub capacity: usize,
}
impl LocalTable {
@ -203,6 +210,8 @@ pub struct LocalMemory {
pub base: *mut u8,
/// Current size of this linear memory in bytes.
pub size: usize,
/// The local memory index.
pub index: LocalMemoryIndex,
}
impl LocalMemory {

View File

@ -1,6 +1,15 @@
use crate::{memory::LinearMemory, structures::TypedIndex, types::LocalMemoryIndex, vm};
use crate::{
memory::LinearMemory,
structures::TypedIndex,
types::{ImportedMemoryIndex, LocalMemoryIndex, LocalTableIndex},
vm,
};
pub unsafe extern "C" fn memory_grow_static(
// +*****************************+
// | LOCAL MEMORIES |
// +****************************+
pub unsafe extern "C" fn local_static_memory_grow(
memory_index: LocalMemoryIndex,
by_pages: u32,
ctx: *mut vm::Ctx,
@ -18,11 +27,14 @@ pub unsafe extern "C" fn memory_grow_static(
}
}
pub unsafe extern "C" fn memory_size(memory_index: LocalMemoryIndex, ctx: *mut vm::Ctx) -> u32 {
pub unsafe extern "C" fn local_static_memory_size(
memory_index: LocalMemoryIndex,
ctx: *mut vm::Ctx,
) -> u32 {
(*(*ctx).local_backing).memory(memory_index).pages()
}
pub unsafe extern "C" fn memory_grow_dynamic(
pub unsafe extern "C" fn local_dynamic_memory_grow(
memory_index: LocalMemoryIndex,
by_pages: u32,
ctx: *mut vm::Ctx,
@ -39,3 +51,68 @@ pub unsafe extern "C" fn memory_grow_dynamic(
-1
}
}
// +*****************************+
// | IMPORTED MEMORIES |
// +****************************+
pub unsafe extern "C" fn imported_static_memory_grow(
imported_mem_index: ImportedMemoryIndex,
by_pages: u32,
caller_ctx: *mut vm::Ctx,
) -> i32 {
let import_backing = &*(*caller_ctx).import_backing;
let vm_imported_mem = import_backing.imported_memory(imported_mem_index);
// We can assume that the memory here is local to the callee ctx.
let local_mem_index = (*vm_imported_mem.memory).index;
if let Some(old) = (*(*vm_imported_mem.vmctx).local_backing)
.memory(local_mem_index)
.grow_dynamic(by_pages)
{
// Store the new size back into the vmctx.
(*(*vm_imported_mem.vmctx)
.memories
.add(local_mem_index.index()))
.size = (old as usize + by_pages as usize) * LinearMemory::PAGE_SIZE as usize;
old
} else {
-1
}
}
pub unsafe extern "C" fn imported_static_memory_size(
imported_memory_index: ImportedMemoryIndex,
caller_ctx: *mut vm::Ctx,
) -> u32 {
let import_backing = &*(*caller_ctx).import_backing;
let vm_imported_mem = import_backing.imported_memory(imported_memory_index);
// We can assume that the memory here is local to the callee ctx.
let local_mem_index = (*vm_imported_mem.memory).index;
(*(*vm_imported_mem.vmctx).local_backing)
.memory(local_mem_index)
.pages()
}
// +*****************************+
// | LOCAL TABLES |
// +****************************+
pub unsafe extern "C" fn local_table_grow(
table_index: LocalTableIndex,
by_elems: u32,
ctx: *mut vm::Ctx,
) -> i32 {
let _ = table_index;
let _ = by_elems;
let _ = ctx;
unimplemented!()
}
pub unsafe extern "C" fn local_table_size(table_index: LocalTableIndex, ctx: *mut vm::Ctx) -> u32 {
let _ = table_index;
let _ = ctx;
unimplemented!()
}