diff --git a/lib/clif-backend/src/codegen.rs b/lib/clif-backend/src/codegen.rs index 22f522e19..a68518512 100644 --- a/lib/clif-backend/src/codegen.rs +++ b/lib/clif-backend/src/codegen.rs @@ -25,9 +25,9 @@ use wasmer_runtime::{ types::{ ElementType as WasmerElementType, FuncIndex as WasmerFuncIndex, FuncSig as WasmerSignature, Global as WasmerGlobal, GlobalDesc as WasmerGlobalDesc, GlobalIndex as WasmerGlobalIndex, - Initializer as WasmerInitializer, Map, MapIndex, Memory as WasmerMemory, - MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable, - TableIndex as WasmerTableIndex, Type as WasmerType, + GlobalInit as WasmerGlobalInit, Initializer as WasmerInitializer, Map, + Memory as WasmerMemory, MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, + Table as WasmerTable, TableIndex as WasmerTableIndex, Type as WasmerType, TypedIndex, }, vm::{self, Ctx as WasmerVMContext}, }; @@ -142,14 +142,14 @@ pub mod converter { // TODO: WasmerGlobal does not support `Import` as Global values. let init = match global.initializer { - I32Const(val) => Const(val.into()), - I64Const(val) => Const(val.into()), - F32Const(val) => Const(f32::from_bits(val).into()), - F64Const(val) => Const(f64::from_bits(val).into()), - GlobalInit::GetGlobal(index) => { - WasmerInitializer::GetGlobal(WasmerGlobalIndex::new(index.index())) - }, - GlobalInit::Import => WasmerInitializer::Import + I32Const(val) => WasmerGlobalInit::Init(Const(val.into())), + I64Const(val) => WasmerGlobalInit::Init(Const(val.into())), + F32Const(val) => WasmerGlobalInit::Init(Const(f32::from_bits(val).into())), + F64Const(val) => WasmerGlobalInit::Init(Const(f64::from_bits(val).into())), + GlobalInit::GetGlobal(index) => WasmerGlobalInit::Init(WasmerInitializer::GetGlobal( + WasmerGlobalIndex::new(index.index()), + )), + GlobalInit::Import => WasmerGlobalInit::Import, }; WasmerGlobal { desc, init } @@ -173,6 +173,7 @@ pub mod converter { /// Converts a Cranelift table to a Wasmer table. pub fn convert_memory(memory: Memory) -> WasmerMemory { + println!("codegen memory: {:?}", memory); WasmerMemory { shared: memory.shared, min: memory.minimum, @@ -350,29 +351,71 @@ impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { /// by `index`. /// /// The index space covers both imported and locally declared globals. - fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { + fn make_global( + &mut self, + func: &mut ir::Function, + global_index: GlobalIndex, + ) -> GlobalVariable { // Create VMContext value. let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); - let ptr_size = self.pointer_bytes(); - let globals_offset = WasmerVMContext::offset_globals(); - // Load value at (vmctx + globals_offset), i.e. the address at Ctx.globals. - let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load { - base: vmctx, - offset: Offset32::new(globals_offset as i32), - global_type: self.pointer_type(), - readonly: false, - }); + if global_index.index() < self.module.imported_globals.len() { + // imported global - // *Ctx.globals -> [ u8, u8, .. ] - // Based on the index provided, we need to know the offset into globals array - let offset = index.index() * ptr_size as usize; + let imported_globals_base_addr = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: (vm::Ctx::offset_imported_globals() as i32).into(), + global_type: self.pointer_type(), + readonly: true, + }); - // Create global variable based on the data above. - GlobalVariable::Memory { - gv: globals_base_addr, - offset: (offset as i32).into(), - ty: self.module.get_global(index).ty, + let offset = 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(), + global_type: self.pointer_type(), + }); + + let local_global_addr = func.create_global_value(ir::GlobalValueData::Load { + base: imported_global_addr, + offset: (vm::ImportedGlobal::offset_global() as i32).into(), + global_type: self.pointer_type(), + readonly: true, + }); + + GlobalVariable::Memory { + gv: local_global_addr, + offset: (vm::LocalGlobal::offset_data() as i32).into(), + ty: self.module.get_global(global_index).ty, + } + } else { + // locally defined global + + let globals_base_addr = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: (vm::Ctx::offset_globals() as i32).into(), + global_type: self.pointer_type(), + readonly: true, + }); + + // *Ctx.globals -> [ u8, u8, .. ] + // Based on the index provided, we need to know the offset into globals array + let offset = (global_index.index() - self.module.imported_globals.len()) + * vm::LocalGlobal::size() as usize; + + let local_global_addr = func.create_global_value(ir::GlobalValueData::IAddImm { + base: globals_base_addr, + offset: (offset as i64).into(), + global_type: self.pointer_type(), + }); + + // Create global variable based on the data above. + GlobalVariable::Memory { + gv: local_global_addr, + offset: (vm::LocalGlobal::offset_data() as i32).into(), + ty: self.module.get_global(global_index).ty, + } } } @@ -870,13 +913,18 @@ impl<'data> ModuleEnvironment<'data> for CraneliftModule { elements: Vec, ) { // Convert Cranelift GlobalIndex to wamser GlobalIndex - let base = base.map(|index| WasmerGlobalIndex::new(index.index())); + // let base = base.map(|index| WasmerGlobalIndex::new(index.index())); + let base = match base { + Some(global_index) => { + WasmerInitializer::GetGlobal(WasmerGlobalIndex::new(global_index.index())) + } + None => WasmerInitializer::Const((offset as i32).into()), + }; // Add table initializer to list of table initializers self.table_initializers.push(TableInitializer { table_index: WasmerTableIndex::new(table_index.index()), base, - offset, elements: elements .iter() .map(|index| WasmerFuncIndex::new(index.index())) diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index c053c0b81..5763163ae 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -2,6 +2,7 @@ pub mod codegen; mod libcalls; mod relocation; mod resolver; +// mod module; use cranelift_codegen::{ isa, diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs new file mode 100644 index 000000000..7d7f616ff --- /dev/null +++ b/lib/clif-backend/src/module.rs @@ -0,0 +1,20 @@ +use wasmer_runtime::{ + backend::SigRegistry, + memory::LinearMemory, + module::{ + DataInitializer, ExportIndex, ImportName, ModuleInner, TableInitializer, + }, + types::{ + ElementType, FuncIndex, FuncSig, + Global, GlobalDesc, GlobalIndex, + Initializer, Map, TypedIndex, Memory, + MemoryIndex as WasmerMemoryIndex, SigIndex as WasmerSignatureIndex, Table as WasmerTable, + TableIndex as WasmerTableIndex, Type as WasmerType, GlobalInit as WasmerGlobalInit, + }, + vm::{self, Ctx as WasmerVMContext}, +}; + +/// This is a wasmer module. +pub struct Module { + +} \ No newline at end of file diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 68fde0bb8..b9560677c 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -7,15 +7,15 @@ use std::ptr::{write_unaligned, NonNull}; use wasmer_runtime::{ self, backend::{self, Mmap, Protect}, - types::{FuncIndex, Map, MapIndex}, + types::{LocalFuncIndex, Map, TypedIndex}, vm, vmcalls, }; #[allow(dead_code)] pub struct FuncResolverBuilder { resolver: FuncResolver, - relocations: Map>, - trap_sinks: Map, + relocations: Map>, + trap_sinks: Map, } impl FuncResolverBuilder { @@ -158,15 +158,13 @@ impl FuncResolverBuilder { /// Resolves a function index to a function address. pub struct FuncResolver { num_imported_funcs: usize, - map: Map, + map: Map, memory: Mmap, } impl FuncResolver { - fn lookup(&self, index: FuncIndex) -> Option> { - let offset = *self - .map - .get(FuncIndex::new(index.index() - self.num_imported_funcs))?; + fn lookup(&self, local_func_index: LocalFuncIndex) -> Option> { + let offset = *self.map.get(local_func_index)?; let ptr = unsafe { self.memory.as_ptr().add(offset) }; NonNull::new(ptr).map(|nonnull| nonnull.cast()) diff --git a/lib/runtime/build/spectests.rs b/lib/runtime/build/spectests.rs index e9cd1930f..054bf6d2b 100644 --- a/lib/runtime/build/spectests.rs +++ b/lib/runtime/build/spectests.rs @@ -88,8 +88,8 @@ static IMPORT_MODULE: &str = r#" (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 anyfunc) - (memory $memory (export "memory") 1) + (table $table (export "table") 10 20 anyfunc) + (memory $memory (export "memory") 1 2) (global $global_i32 (export "global_i32") i32 (i32.const 666))) "#; @@ -350,6 +350,7 @@ fn test_module_{}() {{ format!( "fn create_module_{}() -> Instance {{ let module_str = \"{}\"; + println!(\"{{}}\", module_str); 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 generate_imports()).expect(\"WASM can't be instantiated\") diff --git a/lib/runtime/src/backend.rs b/lib/runtime/src/backend.rs index 1acac213c..5b7951bc2 100644 --- a/lib/runtime/src/backend.rs +++ b/lib/runtime/src/backend.rs @@ -1,4 +1,4 @@ -use crate::{module::ModuleInner, types::FuncIndex, vm}; +use crate::{module::ModuleInner, types::LocalFuncIndex, vm}; use std::ptr::NonNull; pub use crate::mmap::{Mmap, Protect}; @@ -10,5 +10,9 @@ pub trait Compiler { } pub trait FuncResolver { - fn get(&self, module: &ModuleInner, index: FuncIndex) -> Option>; + fn get( + &self, + module: &ModuleInner, + local_func_index: LocalFuncIndex, + ) -> Option>; } diff --git a/lib/runtime/src/backing.rs b/lib/runtime/src/backing.rs index b0c1efd22..ad4949569 100644 --- a/lib/runtime/src/backing.rs +++ b/lib/runtime/src/backing.rs @@ -3,28 +3,34 @@ use crate::{ import::Imports, memory::LinearMemory, module::{ImportName, ModuleInner}, + structures::{BoxedMap, Map, SliceMap, TypedIndex}, table::{TableBacking, TableElements}, - types::{Initializer, MapIndex, Value}, + types::{ + ElementType, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, + ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex, LocalOrImport, + LocalTableIndex, Type, Value, + }, vm, }; +use std::slice; #[derive(Debug)] pub struct LocalBacking { - pub(crate) memories: Box<[LinearMemory]>, - pub(crate) tables: Box<[TableBacking]>, + pub(crate) memories: BoxedMap, + pub(crate) tables: BoxedMap, - pub(crate) vm_memories: Box<[vm::LocalMemory]>, - pub(crate) vm_tables: Box<[vm::LocalTable]>, - pub(crate) vm_globals: Box<[vm::LocalGlobal]>, + pub(crate) vm_memories: BoxedMap, + pub(crate) vm_tables: BoxedMap, + pub(crate) vm_globals: BoxedMap, } impl LocalBacking { - pub fn memory(&mut self, index: u32) -> &mut LinearMemory { - &mut self.memories[index as usize] + pub fn memory(&mut self, local_memory_index: LocalMemoryIndex) -> &mut LinearMemory { + &mut self.memories[local_memory_index] } - pub fn table(&mut self, index: u32) -> &mut TableBacking { - &mut self.tables[index as usize] + pub fn table(&mut self, local_table_index: LocalTableIndex) -> &mut TableBacking { + &mut self.tables[local_table_index] } } @@ -34,9 +40,9 @@ impl LocalBacking { let mut tables = Self::generate_tables(module); let globals = Self::generate_globals(module); - let vm_memories = Self::finalize_memories(module, &mut memories[..]); - let vm_tables = Self::finalize_tables(module, imports, &mut tables[..], vmctx); - let vm_globals = Self::finalize_globals(module, imports, globals); + let vm_memories = Self::finalize_memories(module, imports, &mut memories); + let vm_tables = Self::finalize_tables(module, imports, &mut tables, vmctx); + let vm_globals = Self::finalize_globals(module, globals); Self { memories, @@ -48,8 +54,8 @@ impl LocalBacking { } } - fn generate_memories(module: &ModuleInner) -> Box<[LinearMemory]> { - let mut memories = Vec::with_capacity(module.memories.len()); + fn generate_memories(module: &ModuleInner) -> BoxedMap { + let mut memories = Map::with_capacity(module.memories.len()); for (_, mem) in &module.memories { // If we use emscripten, we set a fixed initial and maximum @@ -66,75 +72,155 @@ impl LocalBacking { memories.push(memory); } - memories.into_boxed_slice() + memories.into_boxed_map() } fn finalize_memories( module: &ModuleInner, - memories: &mut [LinearMemory], - ) -> Box<[vm::LocalMemory]> { - for init in &module.data_initializers { + imports: &ImportBacking, + memories: &mut SliceMap, + ) -> BoxedMap { + // For each init that has some data... + for init in module + .data_initializers + .iter() + .filter(|init| init.data.len() > 0) + { assert!(init.base.is_none(), "global base not supported yet"); - assert!(init.offset + init.data.len() <= memories[init.memory_index.index()].size()); - let offset = init.offset; - let mem: &mut LinearMemory = &mut memories[init.memory_index.index()]; - // let end_of_init = offset + init.data.len(); - // if end_of_init > mem.current_size() { - // let grow_pages = (end_of_init / LinearMemory::PAGE_SIZE as usize) + 1; - // mem.grow(grow_pages as u32) - // .expect("failed to grow memory for data initializers"); - // } - let to_init = &mut mem[offset..offset + init.data.len()]; - to_init.copy_from_slice(&init.data); + + match init.memory_index.local_or_import(module) { + LocalOrImport::Local(local_memory_index) => { + let memory_desc = &module.memories[local_memory_index]; + let data_top = init.offset + init.data.len(); + assert!(memory_desc.min as usize >= data_top); + let mem: &mut LinearMemory = &mut memories[local_memory_index]; + + let to_init = &mut mem[init.offset..init.offset + init.data.len()]; + to_init.copy_from_slice(&init.data); + } + LocalOrImport::Import(imported_memory_index) => { + let _ = imported_memory_index; + let _ = imports; + unimplemented!() + } + } } memories .iter_mut() - .map(|mem| mem.into_vm_memory()) - .collect::>() - .into_boxed_slice() + .map(|(_, mem)| mem.into_vm_memory()) + .collect::>() + .into_boxed_map() } - fn generate_tables(module: &ModuleInner) -> Box<[TableBacking]> { - let mut tables = Vec::with_capacity(module.tables.len()); + fn generate_tables(module: &ModuleInner) -> BoxedMap { + let mut tables = Map::with_capacity(module.tables.len()); for (_, table) in &module.tables { let table_backing = TableBacking::new(table); tables.push(table_backing); } - tables.into_boxed_slice() + tables.into_boxed_map() } fn finalize_tables( module: &ModuleInner, imports: &ImportBacking, - tables: &mut [TableBacking], + tables: &mut SliceMap, vmctx: *mut vm::Ctx, - ) -> Box<[vm::LocalTable]> { + ) -> BoxedMap { for init in &module.table_initializers { - assert!(init.base.is_none(), "global base not supported yet"); - let table = &mut tables[init.table_index.index()]; - match table.elements { - TableElements::Anyfunc(ref mut elements) => { - 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); + let init_base = match init.base { + Initializer::Const(Value::I32(offset)) => offset as u32, + Initializer::Const(_) => panic!("a const initializer must be the i32 type"), + Initializer::GetGlobal(imported_global_index) => { + if module.imported_globals[imported_global_index].1.desc.ty == Type::I32 { + unsafe { (*imports.globals[imported_global_index].global).data as u32 } + } else { + panic!("unsupported global type for initialzer") + } + } + } as usize; - let func_data = if module.is_imported_function(func_index) { - imports.functions[func_index.index()].clone() - } else { - vm::ImportedFunc { - func: module - .func_resolver - .get(module, func_index) - .unwrap() - .as_ptr(), - vmctx, + 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) => { + 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); + + let func_data = match func_index.local_or_import(module) { + LocalOrImport::Local(local_func_index) => vm::ImportedFunc { + func: module + .func_resolver + .get(module, local_func_index) + .unwrap() + .as_ptr(), + vmctx, + }, + LocalOrImport::Import(imported_func_index) => { + imports.functions[imported_func_index].clone() + } + }; + + elements[init_base + i] = vm::Anyfunc { func_data, sig_id }; } - }; + } + } + } + LocalOrImport::Import(imported_table_index) => { + let imported_table = &imports.tables[imported_table_index]; - elements[init.offset + i] = vm::Anyfunc { func_data, sig_id }; + 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 => { + 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); + + let func_data = match func_index.local_or_import(module) { + LocalOrImport::Local(local_func_index) => vm::ImportedFunc { + func: module + .func_resolver + .get(module, local_func_index) + .unwrap() + .as_ptr(), + vmctx, + }, + LocalOrImport::Import(imported_func_index) => { + imports.functions[imported_func_index].clone() + } + }; + + imported_local_table_slice[init_base + i] = + vm::Anyfunc { func_data, sig_id }; + } + } } } } @@ -142,34 +228,29 @@ impl LocalBacking { tables .iter_mut() - .map(|table| table.into_vm_table()) - .collect::>() - .into_boxed_slice() + .map(|(_, table)| table.into_vm_table()) + .collect::>() + .into_boxed_map() } - fn generate_globals(module: &ModuleInner) -> Box<[vm::LocalGlobal]> { - let globals = vec![vm::LocalGlobal::null(); module.globals.len()]; + fn generate_globals(module: &ModuleInner) -> BoxedMap { + let mut globals = Map::with_capacity(module.globals.len()); - globals.into_boxed_slice() + globals.resize(module.globals.len(), vm::LocalGlobal::null()); + + globals.into_boxed_map() } fn finalize_globals( module: &ModuleInner, - imports: &ImportBacking, - mut globals: Box<[vm::LocalGlobal]>, - ) -> Box<[vm::LocalGlobal]> { - for (to, (global_index, from)) in globals.iter_mut().zip(module.globals.into_iter()) { + mut globals: BoxedMap, + ) -> BoxedMap { + for ((_, to), (_, from)) in globals.iter_mut().zip(module.globals.into_iter()) { to.data = match from.init { - Initializer::Const(Value::I32(x)) => x as u64, - Initializer::Const(Value::I64(x)) => x as u64, - Initializer::Const(Value::F32(x)) => x.to_bits() as u64, - Initializer::Const(Value::F64(x)) => x.to_bits(), - Initializer::GetGlobal(index) => unsafe { - (*imports.globals[index.index()].global).data - }, - Initializer::Import => unsafe { - (*imports.globals[global_index.index()].global).data - }, + Value::I32(x) => x as u64, + Value::I64(x) => x as u64, + Value::F32(x) => x.to_bits() as u64, + Value::F64(x) => x.to_bits(), }; } @@ -179,10 +260,10 @@ impl LocalBacking { #[derive(Debug)] pub struct ImportBacking { - pub functions: Box<[vm::ImportedFunc]>, - pub memories: Box<[vm::ImportedMemory]>, - pub tables: Box<[vm::ImportedTable]>, - pub globals: Box<[vm::ImportedGlobal]>, + pub functions: BoxedMap, + pub memories: BoxedMap, + pub tables: BoxedMap, + pub globals: BoxedMap, } impl ImportBacking { @@ -200,12 +281,56 @@ impl ImportBacking { } } +fn import_functions( + module: &ModuleInner, + imports: &mut Imports, + vmctx: *mut vm::Ctx, +) -> Result, String> { + let mut functions = Map::with_capacity(module.imported_functions.len()); + for (index, ImportName { namespace, name }) in &module.imported_functions { + let sig_index = module.func_assoc[index.convert_up(module)]; + let expected_sig = module.sig_registry.lookup_func_sig(sig_index); + let import = imports + .get_namespace(namespace) + .and_then(|namespace| namespace.get_export(name)); + match import { + Some(Export::Function { + func, + ctx, + signature, + }) => { + if expected_sig == &signature { + functions.push(vm::ImportedFunc { + func: func.inner(), + vmctx: match ctx { + Context::External(ctx) => ctx, + Context::Internal => vmctx, + }, + }); + } else { + return Err(format!( + "unexpected signature for {:?}:{:?}", + namespace, name + )); + } + } + Some(_) => { + return Err(format!("incorrect import type for {}:{}", namespace, name)); + } + None => { + return Err(format!("import not found: {}:{}", namespace, name)); + } + } + } + Ok(functions.into_boxed_map()) +} + fn import_memories( module: &ModuleInner, imports: &mut Imports, vmctx: *mut vm::Ctx, -) -> Result, String> { - let mut memories = Vec::with_capacity(module.imported_memories.len()); +) -> Result, String> { + let mut memories = Map::with_capacity(module.imported_memories.len()); for (_index, (ImportName { namespace, name }, expected_memory_desc)) in &module.imported_memories { @@ -241,15 +366,15 @@ fn import_memories( } } } - Ok(memories.into_boxed_slice()) + Ok(memories.into_boxed_map()) } fn import_tables( module: &ModuleInner, imports: &mut Imports, vmctx: *mut vm::Ctx, -) -> Result, String> { - let mut tables = Vec::with_capacity(module.imported_tables.len()); +) -> Result, String> { + let mut tables = Map::with_capacity(module.imported_tables.len()); for (_index, (ImportName { namespace, name }, expected_table_desc)) in &module.imported_tables { let table_import = imports .get_namespace(namespace) @@ -283,65 +408,21 @@ fn import_tables( } } } - Ok(tables.into_boxed_slice()) -} - -fn import_functions( - module: &ModuleInner, - imports: &mut Imports, - vmctx: *mut vm::Ctx, -) -> Result, String> { - let mut functions = Vec::with_capacity(module.imported_functions.len()); - for (index, ImportName { namespace, name }) in &module.imported_functions { - let sig_index = module.func_assoc[index]; - let expected_sig = module.sig_registry.lookup_func_sig(sig_index); - let import = imports - .get_namespace(namespace) - .and_then(|namespace| namespace.get_export(name)); - match import { - Some(Export::Function { - func, - ctx, - signature, - }) => { - if expected_sig == &signature { - functions.push(vm::ImportedFunc { - func: func.inner(), - vmctx: match ctx { - Context::External(ctx) => ctx, - Context::Internal => vmctx, - }, - }); - } else { - return Err(format!( - "unexpected signature for {:?}:{:?}", - namespace, name - )); - } - } - Some(_) => { - return Err(format!("incorrect import type for {}:{}", namespace, name)); - } - None => { - return Err(format!("import not found: {}:{}", namespace, name)); - } - } - } - Ok(functions.into_boxed_slice()) + Ok(tables.into_boxed_map()) } fn import_globals( module: &ModuleInner, imports: &mut Imports, -) -> Result, String> { - let mut globals = Vec::with_capacity(module.imported_globals.len()); - for (_, (ImportName { namespace, name }, global_desc)) in &module.imported_globals { +) -> Result, String> { + let mut globals = Map::with_capacity(module.imported_globals.len()); + for (_, (ImportName { namespace, name }, imported_global)) in &module.imported_globals { let import = imports .get_namespace(namespace) .and_then(|namespace| namespace.get_export(name)); match import { Some(Export::Global { local, global }) => { - if &global == global_desc { + if global == imported_global.desc { globals.push(vm::ImportedGlobal { global: local.inner(), }); @@ -360,5 +441,5 @@ fn import_globals( } } } - Ok(globals.into_boxed_slice()) + Ok(globals.into_boxed_map()) } diff --git a/lib/runtime/src/instance.rs b/lib/runtime/src/instance.rs index c4c139ce3..6f578fc9e 100644 --- a/lib/runtime/src/instance.rs +++ b/lib/runtime/src/instance.rs @@ -7,7 +7,7 @@ use crate::{ import::{Imports, Namespace}, module::{ExportIndex, Module, ModuleInner}, types::{ - FuncIndex, FuncSig, GlobalDesc, GlobalIndex, MapIndex, Memory, MemoryIndex, Table, + FuncIndex, FuncSig, GlobalDesc, GlobalIndex, LocalOrImport, Memory, MemoryIndex, Table, TableIndex, Type, Value, }, vm, @@ -202,22 +202,23 @@ impl InstanceInner { .get(func_index) .expect("broken invariant, incorrect func index"); - let (func_ptr, ctx) = if module.is_imported_function(func_index) { - let imported_func = &self.import_backing.functions[func_index.index()]; - ( - imported_func.func as *const _, - Context::External(imported_func.vmctx), - ) - } else { - ( + let (func_ptr, ctx) = match func_index.local_or_import(module) { + LocalOrImport::Local(local_func_index) => ( module .func_resolver - .get(&module, func_index) + .get(&module, local_func_index) .expect("broken invariant, func resolver not synced with module.exports") .cast() .as_ptr() as *const _, Context::Internal, - ) + ), + LocalOrImport::Import(imported_func_index) => { + let imported_func = &self.import_backing.functions[imported_func_index]; + ( + imported_func.func as *const _, + Context::External(imported_func.vmctx), + ) + } }; let signature = module.sig_registry.lookup_func_sig(sig_index).clone(); @@ -230,28 +231,31 @@ impl InstanceInner { module: &ModuleInner, mem_index: MemoryIndex, ) -> (MemoryPointer, Context, Memory) { - if module.is_imported_memory(mem_index) { - let &(_, mem) = &module - .imported_memories - .get(mem_index) - .expect("missing imported memory index"); - let vm::ImportedMemory { memory, vmctx } = - &self.import_backing.memories[mem_index.index()]; - ( - unsafe { MemoryPointer::new(*memory) }, - Context::External(*vmctx), - *mem, - ) - } else { - let vm_mem = &mut self.backing.memories[mem_index.index() as usize]; - ( - unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) }, - Context::Internal, - *module - .memories - .get(mem_index) - .expect("broken invariant, memories"), - ) + match mem_index.local_or_import(module) { + LocalOrImport::Local(local_mem_index) => { + let vm_mem = &mut self.backing.memories[local_mem_index]; + ( + unsafe { MemoryPointer::new(&mut vm_mem.into_vm_memory()) }, + Context::Internal, + *module + .memories + .get(local_mem_index) + .expect("broken invariant, memories"), + ) + } + LocalOrImport::Import(imported_mem_index) => { + let &(_, mem) = &module + .imported_memories + .get(imported_mem_index) + .expect("missing imported memory index"); + let vm::ImportedMemory { memory, vmctx } = + &self.import_backing.memories[imported_mem_index]; + ( + unsafe { MemoryPointer::new(*memory) }, + Context::External(*vmctx), + *mem, + ) + } } } @@ -260,23 +264,27 @@ impl InstanceInner { module: &ModuleInner, global_index: GlobalIndex, ) -> (GlobalPointer, GlobalDesc) { - if module.is_imported_global(global_index) { - let &(_, desc) = &module - .imported_globals - .get(global_index) - .expect("missing imported global index"); - let vm::ImportedGlobal { global } = &self.import_backing.globals[global_index.index()]; - (unsafe { GlobalPointer::new(*global) }, *desc) - } else { - let vm_global = &mut self.backing.vm_globals[global_index.index() as usize]; - ( - unsafe { GlobalPointer::new(vm_global) }, - module - .globals - .get(global_index) - .expect("broken invariant, globals") - .desc, - ) + match global_index.local_or_import(module) { + LocalOrImport::Local(local_global_index) => { + let vm_global = &mut self.backing.vm_globals[local_global_index]; + ( + unsafe { GlobalPointer::new(vm_global) }, + module + .globals + .get(local_global_index) + .expect("broken invariant, globals") + .desc, + ) + } + LocalOrImport::Import(imported_global_index) => { + let &(_, imported_global) = &module + .imported_globals + .get(imported_global_index) + .expect("missing imported global index"); + let vm::ImportedGlobal { global } = + &self.import_backing.globals[imported_global_index]; + (unsafe { GlobalPointer::new(*global) }, imported_global.desc) + } } } @@ -285,28 +293,31 @@ impl InstanceInner { module: &ModuleInner, table_index: TableIndex, ) -> (TablePointer, Context, Table) { - if module.is_imported_table(table_index) { - let &(_, tab) = &module - .imported_tables - .get(table_index) - .expect("missing imported table index"); - let vm::ImportedTable { table, vmctx } = - &self.import_backing.tables[table_index.index()]; - ( - unsafe { TablePointer::new(*table) }, - Context::External(*vmctx), - *tab, - ) - } else { - let vm_table = &mut self.backing.tables[table_index.index() as usize]; - ( - unsafe { TablePointer::new(&mut vm_table.into_vm_table()) }, - Context::Internal, - *module - .tables - .get(table_index) - .expect("broken invariant, tables"), - ) + match table_index.local_or_import(module) { + LocalOrImport::Local(local_table_index) => { + let vm_table = &mut self.backing.tables[local_table_index]; + ( + unsafe { TablePointer::new(&mut vm_table.into_vm_table()) }, + Context::Internal, + *module + .tables + .get(local_table_index) + .expect("broken invariant, tables"), + ) + } + LocalOrImport::Import(imported_table_index) => { + let &(_, tab) = &module + .imported_tables + .get(imported_table_index) + .expect("missing imported table index"); + let vm::ImportedTable { table, vmctx } = + &self.import_backing.tables[imported_table_index]; + ( + unsafe { TablePointer::new(*table) }, + Context::External(*vmctx), + *tab, + ) + } } } } diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index d3bb1b63b..7a48de2dd 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -16,6 +16,7 @@ pub mod module; mod recovery; mod sig_registry; mod sighandler; +pub mod structures; pub mod table; pub mod types; pub mod vm; diff --git a/lib/runtime/src/memory.rs b/lib/runtime/src/memory.rs index 9719b44b5..19d1c5034 100644 --- a/lib/runtime/src/memory.rs +++ b/lib/runtime/src/memory.rs @@ -93,7 +93,7 @@ impl LinearMemory { self.mmap.as_ptr() } - /// Returns a number of allocated wasm pages. + /// Returns the size in bytes pub(crate) fn size(&self) -> usize { self.current as usize * Self::PAGE_SIZE as usize } diff --git a/lib/runtime/src/module.rs b/lib/runtime/src/module.rs index bed4acbb0..e23eccfc2 100644 --- a/lib/runtime/src/module.rs +++ b/lib/runtime/src/module.rs @@ -2,9 +2,11 @@ use crate::{ backend::FuncResolver, import::Imports, sig_registry::SigRegistry, + structures::Map, types::{ - FuncIndex, Global, GlobalDesc, GlobalIndex, Map, MapIndex, Memory, MemoryIndex, SigIndex, - Table, TableIndex, + FuncIndex, Global, GlobalIndex, ImportedFuncIndex, ImportedGlobal, ImportedGlobalIndex, + ImportedMemoryIndex, ImportedTableIndex, Initializer, LocalGlobalIndex, LocalMemoryIndex, + LocalTableIndex, Memory, MemoryIndex, SigIndex, Table, TableIndex, }, Instance, }; @@ -15,14 +17,16 @@ use std::rc::Rc; #[doc(hidden)] pub struct ModuleInner { pub func_resolver: Box, - pub memories: Map, - pub globals: Map, - pub tables: Map, + // This are strictly local and the typsystem ensures that. + pub memories: Map, + pub globals: Map, + pub tables: Map, - pub imported_functions: Map, - pub imported_memories: Map, - pub imported_tables: Map, - pub imported_globals: Map, + // These are strictly imported and the typesystem ensures that. + pub imported_functions: Map, + pub imported_memories: Map, + pub imported_tables: Map, + pub imported_globals: Map, pub exports: HashMap, @@ -47,23 +51,7 @@ impl Module { } } -impl ModuleInner { - pub(crate) fn is_imported_function(&self, func_index: FuncIndex) -> bool { - func_index.index() < self.imported_functions.len() - } - - pub(crate) fn is_imported_memory(&self, memory_index: MemoryIndex) -> bool { - memory_index.index() < self.imported_memories.len() - } - - pub(crate) fn is_imported_table(&self, table_index: TableIndex) -> bool { - table_index.index() < self.imported_tables.len() - } - - pub(crate) fn is_imported_global(&self, global_index: GlobalIndex) -> bool { - global_index.index() < self.imported_globals.len() - } -} +impl ModuleInner {} #[doc(hidden)] #[derive(Debug, Clone)] @@ -90,7 +78,7 @@ pub enum ExportIndex { } /// A data initializer for linear memory. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DataInitializer { /// The index of the memory to initialize. pub memory_index: MemoryIndex, @@ -103,14 +91,12 @@ pub struct DataInitializer { } /// A WebAssembly table initializer. -#[derive(Clone, Debug)] +#[derive(Debug, Clone)] pub struct TableInitializer { /// The index of a table to initialize. pub table_index: TableIndex, - /// Optionally, a global variable giving a base index. - pub base: Option, - /// The offset to add to the base. - pub offset: usize, + /// Either a constant offset or a `get_global` + pub base: Initializer, /// The values to write into the table elements. pub elements: Vec, } diff --git a/lib/runtime/src/sig_registry.rs b/lib/runtime/src/sig_registry.rs index 9442ab5c1..a97d08508 100644 --- a/lib/runtime/src/sig_registry.rs +++ b/lib/runtime/src/sig_registry.rs @@ -1,4 +1,7 @@ -use crate::types::{FuncSig, Map, SigIndex}; +use crate::{ + structures::Map, + types::{FuncSig, SigIndex}, +}; use hashbrown::HashMap; #[derive(Debug)] diff --git a/lib/runtime/src/structures/boxed.rs b/lib/runtime/src/structures/boxed.rs new file mode 100644 index 000000000..710dc70a6 --- /dev/null +++ b/lib/runtime/src/structures/boxed.rs @@ -0,0 +1,46 @@ +use super::{SliceMap, TypedIndex}; +use std::{ + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, +}; + +#[derive(Debug, Clone)] +pub struct BoxedMap +where + K: TypedIndex, +{ + elems: Box<[V]>, + _marker: PhantomData, +} + +impl BoxedMap +where + K: TypedIndex, +{ + pub(in crate::structures) fn new(elems: Box<[V]>) -> Self { + Self { + elems, + _marker: PhantomData, + } + } +} + +impl Deref for BoxedMap +where + K: TypedIndex, +{ + type Target = SliceMap; + fn deref(&self) -> &SliceMap { + unsafe { mem::transmute::<&[V], _>(&*self.elems) } + } +} + +impl DerefMut for BoxedMap +where + K: TypedIndex, +{ + fn deref_mut(&mut self) -> &mut SliceMap { + unsafe { mem::transmute::<&mut [V], _>(&mut *self.elems) } + } +} diff --git a/lib/runtime/src/structures/map.rs b/lib/runtime/src/structures/map.rs new file mode 100644 index 000000000..8c9b8cc02 --- /dev/null +++ b/lib/runtime/src/structures/map.rs @@ -0,0 +1,174 @@ +use super::{BoxedMap, SliceMap, TypedIndex}; +use std::{ + iter::{self, Extend, FromIterator}, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + slice, +}; + +/// Dense item map +#[derive(Debug, Clone)] +pub struct Map +where + K: TypedIndex, +{ + elems: Vec, + _marker: PhantomData, +} + +impl Map +where + K: TypedIndex, +{ + pub fn new() -> Self { + Self { + elems: Vec::new(), + _marker: PhantomData, + } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + elems: Vec::with_capacity(capacity), + _marker: PhantomData, + } + } + + pub fn len(&self) -> usize { + self.elems.len() + } + + pub fn push(&mut self, value: V) -> K { + let len = self.len(); + self.elems.push(value); + K::new(len) + } + + pub fn reserve_exact(&mut self, size: usize) { + self.elems.reserve_exact(size); + } + + pub fn into_boxed_map(self) -> BoxedMap { + BoxedMap::new(self.elems.into_boxed_slice()) + } +} + +impl Map +where + K: TypedIndex, + V: Clone, +{ + pub fn resize(&mut self, new_len: usize, value: V) { + self.elems.resize(new_len, value); + } +} + +impl Extend for Map +where + K: TypedIndex, +{ + fn extend>(&mut self, iter: I) { + self.elems.extend(iter); + } +} + +impl FromIterator for Map +where + K: TypedIndex, +{ + fn from_iter>(iter: I) -> Self { + let elems: Vec = iter.into_iter().collect(); + Self { + elems, + _marker: PhantomData, + } + } +} + +impl Deref for Map +where + K: TypedIndex, +{ + type Target = SliceMap; + fn deref(&self) -> &SliceMap { + unsafe { mem::transmute::<&[V], _>(self.elems.as_slice()) } + } +} + +impl DerefMut for Map +where + K: TypedIndex, +{ + fn deref_mut(&mut self) -> &mut SliceMap { + unsafe { mem::transmute::<&mut [V], _>(self.elems.as_mut_slice()) } + } +} + +impl<'a, K, V> IntoIterator for &'a Map +where + K: TypedIndex, +{ + type Item = (K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + Iter::new(self.elems.iter()) + } +} + +impl<'a, K, V> IntoIterator for &'a mut Map +where + K: TypedIndex, +{ + type Item = (K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> Self::IntoIter { + IterMut::new(self.elems.iter_mut()) + } +} + +pub struct Iter<'a, K: TypedIndex, V: 'a> { + enumerated: iter::Enumerate>, + _marker: PhantomData, +} + +impl<'a, K: TypedIndex, V: 'a> Iter<'a, K, V> { + pub(in crate::structures) fn new(iter: slice::Iter<'a, V>) -> Self { + Self { + enumerated: iter.enumerate(), + _marker: PhantomData, + } + } +} + +impl<'a, K: TypedIndex, V: 'a> Iterator for Iter<'a, K, V> { + type Item = (K, &'a V); + + fn next(&mut self) -> Option { + self.enumerated.next().map(|(i, v)| (K::new(i), v)) + } +} + +pub struct IterMut<'a, K: TypedIndex, V: 'a> { + enumerated: iter::Enumerate>, + _marker: PhantomData, +} + +impl<'a, K: TypedIndex, V: 'a> IterMut<'a, K, V> { + pub(in crate::structures) fn new(iter: slice::IterMut<'a, V>) -> Self { + Self { + enumerated: iter.enumerate(), + _marker: PhantomData, + } + } +} + +impl<'a, K: TypedIndex, V: 'a> Iterator for IterMut<'a, K, V> { + type Item = (K, &'a mut V); + + fn next(&mut self) -> Option { + self.enumerated.next().map(|(i, v)| (K::new(i), v)) + } +} diff --git a/lib/runtime/src/structures/mod.rs b/lib/runtime/src/structures/mod.rs new file mode 100644 index 000000000..614584540 --- /dev/null +++ b/lib/runtime/src/structures/mod.rs @@ -0,0 +1,12 @@ +mod boxed; +mod map; +mod slice; + +pub use self::boxed::BoxedMap; +pub use self::map::{Iter, IterMut, Map}; +pub use self::slice::SliceMap; + +pub trait TypedIndex { + fn new(index: usize) -> Self; + fn index(&self) -> usize; +} diff --git a/lib/runtime/src/structures/mono_vec.rs b/lib/runtime/src/structures/mono_vec.rs new file mode 100644 index 000000000..35279654f --- /dev/null +++ b/lib/runtime/src/structures/mono_vec.rs @@ -0,0 +1,86 @@ + +#[derive(Debug, Clone)] +enum MonoVecInner { + None, + Inline(T), + Heap(Vec), +} + +/// A type that can hold zero items, +/// one item, or many items. +#[derive(Debug, Clone)] +pub struct MonoVec { + inner: MonoVecInner, +} + +impl MonoVec { + pub fn new() -> Self { + Self { + inner: MonoVecInner::None, + } + } + + pub fn new_inline(item: T) -> Self { + Self { + inner: MonoVecInner::Inline(item), + } + } + + pub fn with_capacity(capacity: usize) -> Self { + match capacity { + 0 | 1 => Self::new(), + _ => Self { + inner: MonoVecInner::Heap(Vec::with_capacity(capacity)), + }, + } + } + + pub fn push(&mut self, item: T) { + let uninit = unsafe { mem::uninitialized() }; + let prev = mem::replace(&mut self.inner, uninit); + let next = match prev { + MonoVecInner::None => MonoVecInner::Inline(item), + MonoVecInner::Inline(previous_item) => MonoVecInner::Heap(vec![previous_item, item]), + MonoVecInner::Heap(mut v) => { + v.push(item); + MonoVecInner::Heap(v) + } + }; + let uninit = mem::replace(&mut self.inner, next); + mem::forget(uninit); + } + + pub fn pop(&mut self) -> Option { + match self.inner { + MonoVecInner::None => None, + MonoVecInner::Inline(ref mut item) => { + let uninit = unsafe { mem::uninitialized() }; + let item = mem::replace(item, uninit); + let uninit = mem::replace(&mut self.inner, MonoVecInner::None); + mem::forget(uninit); + Some(item) + } + MonoVecInner::Heap(ref mut v) => v.pop(), + } + } + + pub fn as_slice(&self) -> &[T] { + match self.inner { + MonoVecInner::None => unsafe { + slice::from_raw_parts(mem::align_of::() as *const T, 0) + }, + MonoVecInner::Inline(ref item) => slice::from_ref(item), + MonoVecInner::Heap(ref v) => &v[..], + } + } + + pub fn as_slice_mut(&mut self) -> &mut [T] { + match self.inner { + MonoVecInner::None => unsafe { + slice::from_raw_parts_mut(mem::align_of::() as *mut T, 0) + }, + MonoVecInner::Inline(ref mut item) => slice::from_mut(item), + MonoVecInner::Heap(ref mut v) => &mut v[..], + } + } +} diff --git a/lib/runtime/src/structures/slice.rs b/lib/runtime/src/structures/slice.rs new file mode 100644 index 000000000..be2227e57 --- /dev/null +++ b/lib/runtime/src/structures/slice.rs @@ -0,0 +1,68 @@ +use super::{Iter, IterMut, TypedIndex}; +use std::{ + marker::PhantomData, + ops::{Index, IndexMut}, +}; + +/// This is a dynamically-sized slice +/// that can only be indexed by the +/// correct index type. +pub struct SliceMap +where + K: TypedIndex, +{ + _marker: PhantomData, + slice: [V], +} + +impl SliceMap +where + K: TypedIndex, +{ + pub fn get(&self, index: K) -> Option<&V> { + self.slice.get(index.index()) + } + + pub fn get_mut(&mut self, index: K) -> Option<&mut V> { + self.slice.get_mut(index.index()) + } + + pub fn len(&self) -> usize { + self.slice.len() + } + + pub fn iter(&self) -> Iter { + Iter::new(self.slice.iter()) + } + + pub fn iter_mut(&mut self) -> IterMut { + IterMut::new(self.slice.iter_mut()) + } + + pub fn as_ptr(&self) -> *const V { + self as *const SliceMap as *const V + } + + pub fn as_mut_ptr(&mut self) -> *mut V { + self as *mut SliceMap as *mut V + } +} + +impl Index for SliceMap +where + K: TypedIndex, +{ + type Output = V; + fn index(&self, index: K) -> &V { + &self.slice[index.index()] + } +} + +impl IndexMut for SliceMap +where + K: TypedIndex, +{ + fn index_mut(&mut self, index: K) -> &mut V { + &mut self.slice[index.index()] + } +} diff --git a/lib/runtime/src/types.rs b/lib/runtime/src/types.rs index d0834721a..75e663180 100644 --- a/lib/runtime/src/types.rs +++ b/lib/runtime/src/types.rs @@ -1,9 +1,4 @@ -use std::marker::PhantomData; -use std::{ - iter, mem, - ops::{Index, IndexMut}, - slice, -}; +use crate::{module::ModuleInner, structures::TypedIndex}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Type { @@ -82,21 +77,22 @@ pub struct Table { impl Table { pub(crate) fn fits_in_imported(&self, imported: &Table) -> bool { - self.max == imported.max && self.min <= imported.min + // TODO: We should define implementation limits. + let imported_max = imported.max.unwrap_or(u32::max_value()); + let self_max = self.max.unwrap_or(u32::max_value()); + self.ty == imported.ty && imported_max <= self_max && self.min <= imported.min } } -/// A global value initializer. -/// Overtime, this will be able to represent more and more +/// A const value initializer. +/// Over time, this will be able to represent more and more /// complex expressions. #[derive(Debug, Clone, PartialEq)] pub enum Initializer { /// Corresponds to a `const.*` instruction. Const(Value), /// Corresponds to a `get_global` instruction. - GetGlobal(GlobalIndex), - /// Initialized externally - Import, + GetGlobal(ImportedGlobalIndex), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -105,11 +101,23 @@ pub struct GlobalDesc { pub ty: Type, } +#[derive(Debug, Clone, PartialEq)] +pub enum ImportedGlobalInit { + GetGlobal(ImportedGlobalIndex), + Import, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ImportedGlobal { + pub desc: GlobalDesc, + pub init: ImportedGlobalInit, +} + /// A wasm global. #[derive(Debug, Clone)] pub struct Global { pub desc: GlobalDesc, - pub init: Initializer, + pub init: Value, } /// A wasm memory. @@ -129,11 +137,15 @@ impl Memory { } pub(crate) fn fits_in_imported(&self, imported: &Memory) -> bool { - self.shared == imported.shared && self.max == imported.max && self.min <= imported.min + let imported_max = imported.max.unwrap_or(65_536); + let self_max = self.max.unwrap_or(65_536); + + self.shared == imported.shared && imported_max <= self_max && self.min <= imported.min } } -/// A wasm func. +/// The signature of a function that is either implemented +/// in a wasm module or exposed to wasm by the host. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FuncSig { pub params: Vec, @@ -151,158 +163,17 @@ impl FuncSig { } } -pub trait MapIndex { - fn new(index: usize) -> Self; - fn index(&self) -> usize; -} - -/// Dense item map -#[derive(Debug, Clone)] -pub struct Map -where - I: MapIndex, -{ - elems: Vec, - _marker: PhantomData, -} - -impl Map -where - I: MapIndex, -{ - pub fn new() -> Self { - Self { - elems: Vec::new(), - _marker: PhantomData, - } - } - - pub fn with_capacity(capacity: usize) -> Self { - Self { - elems: Vec::with_capacity(capacity), - _marker: PhantomData, - } - } - - pub fn get(&self, index: I) -> Option<&T> { - self.elems.get(index.index()) - } - - pub fn len(&self) -> usize { - self.elems.len() - } - - pub fn push(&mut self, value: T) -> I { - let len = self.len(); - self.elems.push(value); - I::new(len) - } - - pub fn as_ptr(&self) -> *const T { - self.elems.as_ptr() - } - - pub fn reserve_exact(&mut self, size: usize) { - self.elems.reserve_exact(size); - } - - pub fn iter(&self) -> Iter { - Iter::new(self.elems.iter()) - } -} - -impl Index for Map -where - I: MapIndex, -{ - type Output = T; - fn index(&self, index: I) -> &T { - &self.elems[index.index()] - } -} - -impl IndexMut for Map -where - I: MapIndex, -{ - fn index_mut(&mut self, index: I) -> &mut T { - &mut self.elems[index.index()] - } -} - -impl<'a, I, T> IntoIterator for &'a Map -where - I: MapIndex, -{ - type Item = (I, &'a T); - type IntoIter = Iter<'a, T, I>; - - fn into_iter(self) -> Self::IntoIter { - Iter::new(self.elems.iter()) - } -} - -impl<'a, I, T> IntoIterator for &'a mut Map -where - I: MapIndex, -{ - type Item = (I, &'a mut T); - type IntoIter = IterMut<'a, T, I>; - - fn into_iter(self) -> Self::IntoIter { - IterMut::new(self.elems.iter_mut()) - } -} - -pub struct Iter<'a, T: 'a, I: MapIndex> { - enumerated: iter::Enumerate>, - _marker: PhantomData, -} - -impl<'a, T: 'a, I: MapIndex> Iter<'a, T, I> { - fn new(iter: slice::Iter<'a, T>) -> Self { - Self { - enumerated: iter.enumerate(), - _marker: PhantomData, - } - } -} - -impl<'a, T: 'a, I: MapIndex> Iterator for Iter<'a, T, I> { - type Item = (I, &'a T); - - fn next(&mut self) -> Option { - self.enumerated.next().map(|(i, v)| (I::new(i), v)) - } -} - -pub struct IterMut<'a, T: 'a, I: MapIndex> { - enumerated: iter::Enumerate>, - _marker: PhantomData, -} - -impl<'a, T: 'a, I: MapIndex> IterMut<'a, T, I> { - fn new(iter: slice::IterMut<'a, T>) -> Self { - Self { - enumerated: iter.enumerate(), - _marker: PhantomData, - } - } -} - -impl<'a, T: 'a, I: MapIndex> Iterator for IterMut<'a, T, I> { - type Item = (I, &'a mut T); - - fn next(&mut self) -> Option { - self.enumerated.next().map(|(i, v)| (I::new(i), v)) - } +pub trait LocalImport { + type Local: TypedIndex; + type Import: TypedIndex; } +#[rustfmt::skip] macro_rules! define_map_index { ($ty:ident) => { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct $ty (u32); - impl MapIndex for $ty { + impl TypedIndex for $ty { fn new(index: usize) -> Self { $ty (index as _) } @@ -312,20 +183,72 @@ macro_rules! define_map_index { } } }; - ($($ty:ident,)*) => { + ($($normal_ty:ident,)* | local: $($local_ty:ident,)* | imported: $($imported_ty:ident,)*) => { $( - define_map_index!($ty); + define_map_index!($normal_ty); + define_map_index!($local_ty); + define_map_index!($imported_ty); + + impl LocalImport for $normal_ty { + type Local = $local_ty; + type Import = $imported_ty; + } )* }; } -define_map_index![FuncIndex, MemoryIndex, TableIndex, SigIndex,]; +#[rustfmt::skip] +define_map_index![ + FuncIndex, MemoryIndex, TableIndex, GlobalIndex, + | local: LocalFuncIndex, LocalMemoryIndex, LocalTableIndex, LocalGlobalIndex, + | imported: ImportedFuncIndex, ImportedMemoryIndex, ImportedTableIndex, ImportedGlobalIndex, +]; + +#[rustfmt::skip] +macro_rules! define_local_or_import { + ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => { + impl $ty { + pub fn local_or_import(self, module: &ModuleInner) -> LocalOrImport<$ty> { + if self.index() < module.$imports.len() { + LocalOrImport::Import(::Import::new(self.index())) + } else { + LocalOrImport::Local(::Local::new(self.index() - module.$imports.len())) + } + } + } + + impl $local_ty { + pub fn convert_up(self, module: &ModuleInner) -> $ty { + $ty ((self.index() + module.$imports.len()) as u32) + } + } + + impl $imported_ty { + pub fn convert_up(self, _module: &ModuleInner) -> $ty { + $ty (self.index() as u32) + } + } + }; + ($(($ty:ident | ($local_ty:ident, $imported_ty:ident): $imports:ident),)*) => { + $( + define_local_or_import!($ty, $local_ty, $imported_ty, $imports); + )* + }; +} + +#[rustfmt::skip] +define_local_or_import![ + (FuncIndex | (LocalFuncIndex, ImportedFuncIndex): imported_functions), + (MemoryIndex | (LocalMemoryIndex, ImportedMemoryIndex): imported_memories), + (TableIndex | (LocalTableIndex, ImportedTableIndex): imported_tables), + (GlobalIndex | (LocalGlobalIndex, ImportedGlobalIndex): imported_globals), +]; #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct GlobalIndex(u32); -impl MapIndex for GlobalIndex { +pub struct SigIndex(u32); +impl TypedIndex for SigIndex { fn new(index: usize) -> Self { - GlobalIndex(index as _) + SigIndex(index as _) } fn index(&self) -> usize { @@ -333,88 +256,10 @@ impl MapIndex for GlobalIndex { } } -#[derive(Debug, Clone)] -enum MonoVecInner { - None, - Inline(T), - Heap(Vec), -} - -/// A type that can hold zero items, -/// one item, or many items. -#[derive(Debug, Clone)] -pub struct MonoVec { - inner: MonoVecInner, -} - -impl MonoVec { - pub fn new() -> Self { - Self { - inner: MonoVecInner::None, - } - } - - pub fn new_inline(item: T) -> Self { - Self { - inner: MonoVecInner::Inline(item), - } - } - - pub fn with_capacity(capacity: usize) -> Self { - match capacity { - 0 | 1 => Self::new(), - _ => Self { - inner: MonoVecInner::Heap(Vec::with_capacity(capacity)), - }, - } - } - - pub fn push(&mut self, item: T) { - let uninit = unsafe { mem::uninitialized() }; - let prev = mem::replace(&mut self.inner, uninit); - let next = match prev { - MonoVecInner::None => MonoVecInner::Inline(item), - MonoVecInner::Inline(previous_item) => MonoVecInner::Heap(vec![previous_item, item]), - MonoVecInner::Heap(mut v) => { - v.push(item); - MonoVecInner::Heap(v) - } - }; - let uninit = mem::replace(&mut self.inner, next); - mem::forget(uninit); - } - - pub fn pop(&mut self) -> Option { - match self.inner { - MonoVecInner::None => None, - MonoVecInner::Inline(ref mut item) => { - let uninit = unsafe { mem::uninitialized() }; - let item = mem::replace(item, uninit); - let uninit = mem::replace(&mut self.inner, MonoVecInner::None); - mem::forget(uninit); - Some(item) - } - MonoVecInner::Heap(ref mut v) => v.pop(), - } - } - - pub fn as_slice(&self) -> &[T] { - match self.inner { - MonoVecInner::None => unsafe { - slice::from_raw_parts(mem::align_of::() as *const T, 0) - }, - MonoVecInner::Inline(ref item) => slice::from_ref(item), - MonoVecInner::Heap(ref v) => &v[..], - } - } - - pub fn as_slice_mut(&mut self) -> &mut [T] { - match self.inner { - MonoVecInner::None => unsafe { - slice::from_raw_parts_mut(mem::align_of::() as *mut T, 0) - }, - MonoVecInner::Inline(ref mut item) => slice::from_mut(item), - MonoVecInner::Heap(ref mut v) => &mut v[..], - } - } +pub enum LocalOrImport +where + T: LocalImport, +{ + Local(T::Local), + Import(T::Import), } diff --git a/lib/runtime/src/vm.rs b/lib/runtime/src/vm.rs index 290b0b7dd..3f3ccb6ad 100644 --- a/lib/runtime/src/vm.rs +++ b/lib/runtime/src/vm.rs @@ -201,6 +201,10 @@ impl LocalGlobal { pub fn null() -> Self { Self { data: 0 } } + + pub fn size() -> u8 { + mem::size_of::() as u8 + } } #[derive(Debug, Clone)] @@ -213,6 +217,10 @@ impl ImportedGlobal { pub fn offset_global() -> u8 { 0 * (mem::size_of::() as u8) } + + pub fn size() -> u8 { + mem::size_of::() as u8 + } } #[derive(Debug, Clone, Copy)] diff --git a/lib/runtime/src/vmcalls.rs b/lib/runtime/src/vmcalls.rs index 838356d97..13efbb39d 100644 --- a/lib/runtime/src/vmcalls.rs +++ b/lib/runtime/src/vmcalls.rs @@ -1,7 +1,7 @@ -use crate::{memory::LinearMemory, vm}; +use crate::{memory::LinearMemory, structures::TypedIndex, types::LocalMemoryIndex, vm}; pub unsafe extern "C" fn memory_grow_static( - memory_index: u32, + memory_index: LocalMemoryIndex, by_pages: u32, ctx: *mut vm::Ctx, ) -> i32 { @@ -10,7 +10,7 @@ pub unsafe extern "C" fn memory_grow_static( .grow_static(by_pages) { // Store the new size back into the vmctx. - (*(*ctx).memories.add(memory_index as usize)).size = + (*(*ctx).memories.add(memory_index.index())).size = (old as usize + by_pages as usize) * LinearMemory::PAGE_SIZE as usize; old } else { @@ -18,12 +18,12 @@ pub unsafe extern "C" fn memory_grow_static( } } -pub unsafe extern "C" fn memory_size(memory_index: u32, ctx: *mut vm::Ctx) -> u32 { +pub unsafe extern "C" fn 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( - memory_index: u32, + memory_index: LocalMemoryIndex, by_pages: u32, ctx: *mut vm::Ctx, ) -> i32 { @@ -32,7 +32,7 @@ pub unsafe extern "C" fn memory_grow_dynamic( .grow_dynamic(by_pages) { // Store the new size back into the vmctx. - (*(*ctx).memories.add(memory_index as usize)).size = + (*(*ctx).memories.add(memory_index.index())).size = (old as usize + by_pages as usize) * LinearMemory::PAGE_SIZE as usize; old } else {