From c06c65e7c61ffe722fd58e73f4b9a8403e3d23c3 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Mon, 24 Dec 2018 23:05:04 -0500 Subject: [PATCH] Add preliminary support for imports --- Cargo.lock | 11 +++++ Cargo.toml | 1 + src/runtime/backend.rs | 5 ++- src/runtime/backing.rs | 85 ++++++++++++++++++++++++++++--------- src/runtime/instance.rs | 53 +++++++++++++++++++---- src/runtime/memory.rs | 5 --- src/runtime/mod.rs | 25 ++++++++--- src/runtime/module.rs | 6 +-- src/runtime/sig_registry.rs | 5 +++ src/runtime/types.rs | 15 ++++++- src/runtime/vm.rs | 60 ++++++++++++-------------- 11 files changed, 191 insertions(+), 80 deletions(-) create mode 100644 src/runtime/sig_registry.rs diff --git a/Cargo.lock b/Cargo.lock index 1ead438a6..d1a9ba87e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -329,6 +329,15 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hashbrown" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indicatif" version = "0.10.3" @@ -894,6 +903,7 @@ dependencies = [ "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "indicatif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.44 (git+https://github.com/rust-lang/libc)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -978,6 +988,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum hashbrown 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "64b7d419d0622ae02fe5da6b9a5e1964b610a65bb37923b976aeebb6dbb8f86e" "checksum indicatif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "40ecd1e2ee08e6c255ce890f5a99d17000850e664e7acf119fb03b25b0575bfe" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" diff --git a/Cargo.toml b/Cargo.toml index 53f251800..513bfb44c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ byteorder = "1" indicatif = "0.10" console = "0.7.1" field-offset = "0.1.1" +hashbrown = "0.1" [build-dependencies] wabt = "0.7.2" diff --git a/src/runtime/backend.rs b/src/runtime/backend.rs index 5922ac5a4..c48f41415 100644 --- a/src/runtime/backend.rs +++ b/src/runtime/backend.rs @@ -5,11 +5,12 @@ use crate::runtime::{ module::Module, types::FuncIndex, }; +use std::sync::Arc; pub trait Compiler { - fn compile(wasm: &[u8]) -> Box; + fn compile(&self, wasm: &[u8]) -> Result, String>; } pub trait FuncResolver { - pub fn resolve(&self, index: FuncIndex) -> *const vm::Func; + pub fn resolve(&self, index: FuncIndex) -> Option<*const vm::Func>; } \ No newline at end of file diff --git a/src/runtime/backing.rs b/src/runtime/backing.rs index d03a29f04..dae840c6a 100644 --- a/src/runtime/backing.rs +++ b/src/runtime/backing.rs @@ -1,16 +1,12 @@ -use super::vm; -use crate::module::Module; -use super::table::{TableBacking, TableScheme}; -use super::memory::LinearMemory; -use super::instance::{InstanceOptions, InstanceABI}; -use std::mem; - use crate::runtime::{ vm, module::Module, table::TableBacking, types::{Val, GlobalInit}, + memory::LinearMemory, + instance::{Imports, Import}, }; +use std::{ptr, mem}; #[derive(Debug)] @@ -24,9 +20,9 @@ pub struct LocalBacking { } impl LocalBacking { - pub fn new(module: &Module, imports: &ImportBacking, options: &InstanceOptions) -> Self { - let mut memories = Self::generate_memories(module, options); - let mut tables = Self::generate_tables(module, options); + pub fn new(module: &Module, imports: &ImportBacking) -> Self { + let mut memories = Self::generate_memories(module); + let mut tables = Self::generate_tables(module); let globals = Self::generate_globals(module); Self { @@ -39,7 +35,7 @@ impl LocalBacking { } } - fn generate_memories(module: &Module, options: &InstanceOptions) -> Box<[LinearMemory]> { + fn generate_memories(module: &Module) -> Box<[LinearMemory]> { let mut memories = Vec::with_capacity(module.memories.len()); for (_, mem) in &module.memories { @@ -63,7 +59,7 @@ impl LocalBacking { memories.into_boxed_slice() } - fn finalize_memories(module: &Module, memories: &mut [LinearMemory], options: &InstanceOptions) -> Box<[vm::LocalMemory]> { + fn finalize_memories(module: &Module, memories: &mut [LinearMemory]) -> Box<[vm::LocalMemory]> { for init in &module.data_initializers { debug_assert!(init.base.is_none(), "globalvar base not supported yet"); let offset = init.offset; @@ -78,16 +74,10 @@ impl LocalBacking { to_init.copy_from_slice(&init.data); } - if options.abi == InstanceABI::Emscripten { - debug!("emscripten::setup memory"); - crate::apis::emscripten::emscripten_set_up_memory(&mut memories[0]); - debug!("emscripten::finish setup memory"); - } - memories.iter_mut().map(|mem| mem.into_vm_memory()).collect::>().into_boxed_slice() } - fn generate_tables(module: &Module, options: &InstanceOptions) -> Box<[TableBacking]> { + fn generate_tables(module: &Module) -> Box<[TableBacking]> { let mut tables = Vec::with_capacity(module.tables.len()); for table in &module.tables { @@ -98,7 +88,7 @@ impl LocalBacking { tables.into_boxed_slice() } - fn finalize_tables(module: &Module, tables: &[TableBacking], options: &InstanceOptions) -> Box<[vm::LocalTable]> { + fn finalize_tables(module: &Module, tables: &[TableBacking]) -> Box<[vm::LocalTable]> { tables.iter().map(|table| table.into_vm_table()).collect::>().into_boxed_slice() } @@ -196,4 +186,59 @@ pub struct ImportBacking { memories: Box<[vm::ImportedMemory]>, tables: Box<[vm::ImportedTable]>, globals: Box<[vm::ImportedGlobal]>, +} + +impl ImportBacking { + pub fn new(module: &Module, imports: &Imports) -> Result { + assert!(module.imported_memories.len() == 0, "imported memories not yet supported"); + assert!(module.imported_tables.len() == 0, "imported tables not yet supported"); + + let mut functions = Vec::with_capacity(module.imported_functions.len()); + for (index, (mod_name, item_name)) in &module.imported_functions { + let expected_sig = module.signatures[index]; + let import = imports.get(mod_name, item_name); + if let Import::Func(func, signature) = import { + if expected_sig == signature { + functions.push(vm::ImportedFunc { + func, + vmctx: ptr::null_mut(), + }); + } else { + return Err(format!("unexpected signature for {}:{}", mod_name, item_name)); + } + } else { + return Err(format!("incorrect type for {}:{}", mod_name, item_name)); + } + } + + let mut globals = Vec::with_capacity(module.imported_globals.len()); + for (_, ((mod_name, item_name), global_desc)) in &module.imported_globals { + let import = imports.get(mod_name, item_name); + if let Import::Global(val) = import { + if val.ty() == global_desc.ty { + globals.push(vm::ImportedGlobal { + global: vm::LocalGlobal { + data: match val { + Val::I32(n) => n as u64, + Val::I64(n) => n as u64, + Val::F32(n) => n as u64, + Val::F64(n) => n, + }, + }, + }); + } else { + return Err(format!("unexpected global type for {}:{}", mod_name, item_name)); + } + } else { + return Err(format!("incorrect type for {}:{}", mod_name, item_name)); + } + } + + Ok(ImportBacking { + functions: functions.into_boxed_slice(), + memories: vec![].into_boxed_slice(), + tables: vec![].into_boxed_slice(), + globals: globals.into_boxed_slice(), + }) + } } \ No newline at end of file diff --git a/src/runtime/instance.rs b/src/runtime/instance.rs index 29216bffb..aa6bb2ac2 100644 --- a/src/runtime/instance.rs +++ b/src/runtime/instance.rs @@ -1,26 +1,63 @@ use crate::runtime::{ vm, backing::{LocalBacking, ImportBacking}, + module::{ModuleName, ItemName}, + types::{Val, Memory, Table, Global, FuncSig}, + table::TableBacking, + memory::LinearMemory, }; use std::sync::Arc; +use hashbrown::{HashMap, Entry}; pub struct Instance { pub vmctx: vm::Ctx, - pub finalized_funcs: Box<[*const vm::Func]>, - - pub backing: LocalBacking, - pub imports: ImportBacking, + backing: LocalBacking, + imports: ImportBacking, pub module: Arc, } impl Instance { - pub fn new(module: Arc) -> Box { + pub fn new(module: Arc, imports: &Imports) -> Result, String> { + let mut import_backing = ImportBacking::new(&*module, imports)?; + let mut backing = LocalBacking::new(&*module, &import_backing); + + let vmctx = vm::Ctx::new(&mut backing, &mut imports); - Box::new(Instance { + Ok(Box::new(Instance { vmctx, - finalized_funcs - }) + backing, + import_backing, + module, + })) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Import { + Func(*const vm::Func, FuncSig), + Table(Arc, Table), + Memory(Arc, Memory), + Global(Val), +} + +pub struct Imports { + map: HashMap>, +} + +impl Imports { + pub fn new() -> Self { + Self { + map: HashMap::new(), + } + } + + pub fn add(&mut self, module: ModuleName, name: ItemName, import: Import) { + self.map.entry(module).or_insert(HashMap::new()).insert(name, import) + } + + pub fn get(&self, module: ModuleName, name: ItemName) -> Option<&Import> { + self.map.get().and_then(|m| m.get(name)) } } \ No newline at end of file diff --git a/src/runtime/memory.rs b/src/runtime/memory.rs index 39f9ebd1d..8ccc0a51a 100644 --- a/src/runtime/memory.rs +++ b/src/runtime/memory.rs @@ -238,9 +238,4 @@ impl DerefMut for LinearMemory { ) } } -} - -fn round_up_to_page_size(size: usize) -> usize { - let page_size = region::page::size(); - (size + (page_size - 1)) & !(page_size - 1) } \ No newline at end of file diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index d2b3f998d..4b47b0e4e 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,9 +1,20 @@ pub mod vm; -pub mod backing; -pub mod types; -pub mod memory; -pub mod backend; -pub mod module; -pub mod instance; -pub mod table; \ No newline at end of file +mod backing; +mod types; +mod memory; +mod backend; +mod module; +mod instance; +mod table; +mod sig_registry; + +pub use backend::Compiler; +pub use instance::{Instance, Imports, Import}; +pub use module::{ModuleName, ItemName, Module}; + +/// Compile a webassembly module using the provided compiler and linked with the provided imports. +pub fn compile(compiler: &dyn Compiler, wasm: &[u8], imports: &Imports) -> Result, String> { + let module = compiler.compile(wasm)?; + Instance::new(module, imports) +} \ No newline at end of file diff --git a/src/runtime/module.rs b/src/runtime/module.rs index deb87a83c..110a7e6ed 100644 --- a/src/runtime/module.rs +++ b/src/runtime/module.rs @@ -1,7 +1,7 @@ use crate::runtime::types::{ Map, FuncIndex, MemoryIndex, TableIndex, GlobalIndex, - Memory, Globals, GlobalDesc, Func, Table, + Memory, Globals, GlobalDesc, FuncSig, Table, }; use crate::runtime::backend::FuncResolver; @@ -13,7 +13,7 @@ pub struct Module { pub globals: Map, pub tables: Map, - pub imported_functions: Map<(ImportName, Func), FuncIndex>, + pub imported_functions: Map, pub imported_memories: Map<(ImportName, Memory), MemoryIndex>, pub imported_tables: Map<(ImportName, Table), TableIndex>, pub imported_globals: Map<(ImportName, GlobalDesc), GlobalIndex>, @@ -23,7 +23,7 @@ pub struct Module { pub data_initializers: Vec, pub start_func: FuncIndex, - pub signatures: Map, + pub signatures: Map, } pub type ModuleName = Vec; diff --git a/src/runtime/sig_registry.rs b/src/runtime/sig_registry.rs new file mode 100644 index 000000000..cf9256ab1 --- /dev/null +++ b/src/runtime/sig_registry.rs @@ -0,0 +1,5 @@ + + +pub struct SignatureRegistry { + +} \ No newline at end of file diff --git a/src/runtime/types.rs b/src/runtime/types.rs index ad63c70d6..9fe58bd2a 100644 --- a/src/runtime/types.rs +++ b/src/runtime/types.rs @@ -25,6 +25,17 @@ pub enum Val { F64(u64), } +impl Val { + pub fn ty(&self) -> Type { + match self { + Val::I32(_) => Type::I32, + Val::I64(_) => Type::I64, + Val::F32(_) => Type::F32, + Val::F64(_) => Type::F64, + } + } +} + impl From for Val { fn from(n: i32) -> Self { Self::I32(n) @@ -105,8 +116,8 @@ impl Memory { } /// A wasm func. -#[derive(Debug)] -pub struct Func { +#[derive(Debug, PartialEq, Eq)] +pub struct FuncSig { pub params: Vec, pub returns: Vec, } diff --git a/src/runtime/vm.rs b/src/runtime/vm.rs index 0494ed87e..56100e183 100644 --- a/src/runtime/vm.rs +++ b/src/runtime/vm.rs @@ -1,19 +1,22 @@ use std::{ptr, mem}; -use crate::runtime::types::{ - MemoryIndex, TableIndex, GlobalIndex, FuncIndex, - SignatureIndex, +use crate::runtime::{ + types::{ + MemoryIndex, TableIndex, GlobalIndex, FuncIndex, + SignatureIndex, + }, + backing::{LocalBacking, ImportBacking}, }; #[derive(Debug)] #[repr(C)] pub struct Ctx<'a> { - /// A pointer to an array of locally-defined memories, indexed by `DefinedMemoryIndex`. + /// A pointer to an array of locally-defined memories, indexed by `MemoryIndex`. pub memories: *mut LocalMemory, - /// A pointer to an array of locally-defined tables, indexed by `DefinedTableIndex`. + /// A pointer to an array of locally-defined tables, indexed by `TableIndex`. pub tables: *mut LocalTable, - /// A pointer to an array of locally-defined globals, indexed by `DefinedGlobalIndex`. + /// A pointer to an array of locally-defined globals, indexed by `GlobalIndex`. pub globals: *mut LocalGlobal, /// A pointer to an array of imported memories, indexed by `MemoryIndex, @@ -34,23 +37,19 @@ pub struct Ctx<'a> { impl Ctx { pub fn new( - memories: *mut LocalMemory, - tables: *mut LocalTable, - globals: *mut LocalGlobal, - imported_memories: *mut ImportedMemory, - imported_tables: *mut ImportedTable, - imported_globals: *mut ImportedGlobal, - imported_funcs: *mut ImportedFunc, + local_backing: &mut LocalBacking, + import_backing: &mut ImportBacking, sig_ids: *mut SigId, ) -> Self { Self { - memories, - tables, - globals, - imported_memories, - imported_tables, - imported_globals, - imported_funcs, + memories: local_backing.vm_memories.as_mut_ptr(), + tables: local_backing.vm_tables.as_mut_ptr(), + globals: local_backing.vm_globals.as_mut_ptr(), + + imported_memories: import_backing.memories.as_mut_ptr(), + imported_tables: import_backing.tables.as_mut_ptr(), + imported_funcs: import_backing.functions.as_mut_ptr(), + sig_ids, } } @@ -98,17 +97,12 @@ pub enum Func {} #[repr(C)] pub struct ImportedFunc { pub func: *const Func, - pub vmctx: *mut Ctx, } impl ImportedFunc { pub fn offset_func() -> u8 { 0 * (mem::size_of::() as u8) } - - pub fn offset_vmctx() -> u8 { - 1 * (mem::size_of::() as u8) - } } /// Definition of a table used by the VM. (obviously) @@ -205,11 +199,11 @@ impl LocalGlobal { #[derive(Debug, Clone)] #[repr(C)] pub struct ImportedGlobal { - pub global: *mut LocalGlobal, + pub global: LocalGlobal, } impl ImportedGlobal { - pub fn offset_global() -> u8 { + pub fn offset_data() -> u8 { 0 * (mem::size_of::() as u8) } } @@ -314,10 +308,10 @@ mod vm_offset_tests { offset_of!(ImportedFunc => func).get_byte_offset(), ); - assert_eq!( - ImportedFunc::offset_vmctx() as usize, - offset_of!(ImportedFunc => vmctx).get_byte_offset(), - ); + // assert_eq!( + // ImportedFunc::offset_vmctx() as usize, + // offset_of!(ImportedFunc => vmctx).get_byte_offset(), + // ); } #[test] @@ -378,8 +372,8 @@ mod vm_offset_tests { #[test] fn imported_global() { assert_eq!( - ImportedGlobal::offset_global() as usize, - offset_of!(ImportedGlobal => global).get_byte_offset(), + ImportedGlobal::offset_data() as usize, + offset_of!(ImportedGlobal => global: LocalGlobal => data).get_byte_offset(), ); }