diff --git a/Cargo.lock b/Cargo.lock index cc13d333d..40b84be10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,20 +84,18 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0" dependencies = [ - "cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-entity 0.22.0", ] [[package]] name = "cranelift-codegen" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0" dependencies = [ - "cranelift-bforest 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-codegen-meta 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-bforest 0.22.0", + "cranelift-codegen-meta 0.22.0", + "cranelift-entity 0.22.0", "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -106,45 +104,42 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0" [[package]] name = "cranelift-entity" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0" [[package]] name = "cranelift-frontend" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0" dependencies = [ - "cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.22.0", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-native" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0" dependencies = [ - "cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-cpuid 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.22.0", + "raw-cpuid 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cranelift-wasm" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0" dependencies = [ - "cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-frontend 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.22.0", + "cranelift-entity 0.22.0", + "cranelift-frontend 0.22.0", "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -306,7 +301,7 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -424,6 +419,11 @@ dependencies = [ "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "spin" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "strsim" version = "0.7.0" @@ -578,16 +578,17 @@ dependencies = [ name = "wasmer" version = "0.1.0" dependencies = [ - "cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-native 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cranelift-wasm 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cranelift-codegen 0.22.0", + "cranelift-entity 0.22.0", + "cranelift-native 0.22.0", + "cranelift-wasm 0.22.0", "docopt 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "region 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "target-lexicon 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -597,7 +598,7 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.17.3" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -635,13 +636,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" "checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" -"checksum cranelift-bforest 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e96851b525021dd220259b9f29bf79d83f65b49e4f12b786d545aa929e4cad2" -"checksum cranelift-codegen 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16f418f1d1e6221812a7d35cff5b9a572dc978c002e33792134bbd50c07cacca" -"checksum cranelift-codegen-meta 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1da3daa0109e7a0b7b322cea666cc223fb6a0d5170e83d23b3d5d2deaddca5f3" -"checksum cranelift-entity 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27412f153f2b517125dea9247ee8859a9ea3923d44384d54420e64fab9314752" -"checksum cranelift-frontend 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03c44cc7006b375e60e0c7edb6fc81abfbf20158374c03f5d0da981b373860a3" -"checksum cranelift-native 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d87fdf63094bef798edbca95a05d7c396c14858e02dee1ef5481c8c4271c8" -"checksum cranelift-wasm 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a9d3454bf60ee6c3d1f54d6cf9ed82cfc1a2e7efb9ec1b16666bf2987c88bfa" "checksum docopt 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c92df70dfaaabecc14b409fd79f55ba0f247780529db1d73bfa601e1d3ac0" "checksum errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2a071601ed01b988f896ab14b95e67335d1eeb50190932a1320f7fe3cadc84e" "checksum errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" @@ -661,7 +655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" "checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" -"checksum raw-cpuid 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fe3c460bd35fdb75644e94ab498372bdf29a4849367ce7ba74cf358edce590c4" +"checksum raw-cpuid 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41219962ecab392f1e68db9e7ebd972800d4045a128cc23462b384e8c312cde1" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2069749032ea3ec200ca51e4a31df41759190a88edca0d2d86ee8bedf7073341" @@ -676,6 +670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" "checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" "checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" +"checksum spin 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "37b5646825922b96b5d7d676b5bb3458a54498e96ed7b0ce09dc43a07038fea4" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum structopt 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ca85f2c9a5a1e2d5ac686fc0be48e40f8ad803f5bbe31f692ff71eb2dd8aad45" "checksum structopt-derive 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "1383e5db585da799a5c4acc496c5c868e18bf82e658c00c75cc91038fa26b55f" @@ -695,7 +690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum wabt 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0768faf932f2898c0a7545edee648fefa716906a2e143009addf4959d2335a75" "checksum wabt-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4aaa9a8fa0d698315da5611f3e113a1e688fbb8d6fa0dd9510dfa023f665e9dc" -"checksum wasmparser 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc6511bad33610d7798f80d48e10495a6eb4f1e39306b0bb340b2de0816c4ca" +"checksum wasmparser 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e760a7904c2a4ed9d4a522c1a5625e36356fb4ff5fe6f6ddb8eb3d6a6817b9c" "checksum wasmparser 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a75e0c3fe9a4d4fd91901348a5be05ba4791e29dda89e8596bfe87900ba7edc" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" diff --git a/Cargo.toml b/Cargo.toml index 0c9c49ea4..0d73659cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,14 @@ repository = "https://github.com/wapmio/wasmer" publish = false [dependencies] -cranelift-codegen = "0.20.0" -cranelift-entity = "0.20.1" -cranelift-wasm = "0.20.1" -cranelift-native = "0.20.0" +# cranelift-native = "0.20.0" +cranelift-native = { path = "cranelift/lib/native" } +# cranelift-codegen = "0.20.0" +cranelift-codegen = { path = "cranelift/lib/codegen" } +# cranelift-entity = "0.20.1" +cranelift-entity = { path = "cranelift/lib/entity" } +# cranelift-wasm = "0.20.1" +cranelift-wasm = { path = "cranelift/lib/wasm" } docopt = "1.0.0" serde = "1.0.55" serde_derive = "1.0.55" @@ -21,4 +25,6 @@ wabt = "0.6.0" wasmparser = "0.20.0" region = "0.3.0" memmap = "0.6.2" -target-lexicon = "0.0.3" +spin = "0.4.9" +# target-lexicon = "0.0.3" +target-lexicon = { version = "0.0.3", default-features = false } diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 000000000..913812d43 --- /dev/null +++ b/src/common/mod.rs @@ -0,0 +1 @@ +pub mod slice; diff --git a/src/common/slice.rs b/src/common/slice.rs new file mode 100644 index 000000000..85434e813 --- /dev/null +++ b/src/common/slice.rs @@ -0,0 +1,101 @@ +use core::ptr::NonNull; +use core::ops::{Index, IndexMut}; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct UncheckedSlice { + ptr: NonNull, +} + +impl UncheckedSlice { + #[inline] + unsafe fn get_unchecked(&self, index: usize) -> &T { + let ptr = self.ptr.as_ptr(); + &*ptr.add(index) + } + + #[inline] + unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { + let ptr = self.ptr.as_ptr(); + &mut*(ptr.add(index) as *mut _) + } + + pub unsafe fn dangling() -> UncheckedSlice { + UncheckedSlice { + ptr: NonNull::dangling(), + } + } + + pub fn as_ptr(&self) -> *const T { + self.ptr.as_ptr() + } + + pub fn as_mut_ptr(&mut self) -> *mut T { + self.ptr.as_ptr() + } +} + +impl<'a, T> From<&'a [T]> for UncheckedSlice { + fn from(slice: &[T]) -> UncheckedSlice { + let ptr: NonNull<[T]> = slice.into(); + UncheckedSlice { + ptr: ptr.cast(), + } + } +} + +pub struct BoundedSlice { + data: UncheckedSlice, + len: usize, +} + +impl BoundedSlice { + pub fn get(&self, index: usize) -> Option<&T> { + if index < self.len { + unsafe { + Some(self.data.get_unchecked(index)) + } + } else { + None + } + } + + pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { + if index < self.len { + unsafe { + Some(self.data.get_unchecked_mut(index)) + } + } else { + None + } + } + + #[inline] + pub fn len(&self) -> usize { + self.len + } +} + +impl Index for BoundedSlice { + type Output = T; + fn index(&self, index: usize) -> &T { + self.get(index) + .expect(&format!("index: {} was out of bounds.", index)) + } +} + +impl IndexMut for BoundedSlice { + fn index_mut(&mut self, index: usize) -> &mut T { + self.get_mut(index) + .expect(&format!("index: {} was out of bounds.", index)) + } +} + +impl<'a, T> From<&'a [T]> for BoundedSlice { + fn from(slice: &[T]) -> BoundedSlice { + BoundedSlice { + data: slice.into(), + len: slice.len(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 043724955..3e06ddd48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ extern crate cranelift_wasm; extern crate cranelift_entity; #[macro_use] extern crate target_lexicon; +extern crate spin; use std::path::PathBuf; use std::fs::File; @@ -22,6 +23,7 @@ use wabt::wat2wasm; pub mod webassembly; pub mod spec; +pub mod common; /// The options for the wasmer Command Line Interface diff --git a/src/webassembly/env.rs b/src/webassembly/env.rs index fb504639c..c6fa581ef 100644 --- a/src/webassembly/env.rs +++ b/src/webassembly/env.rs @@ -78,7 +78,7 @@ pub struct ModuleInfo { /// WebAssembly table initializers. // Should be Vec // instead of Vec> ?? - pub table_elements: Vec>, + pub table_elements: Vec, /// The base of tables. pub tables_base: Option, @@ -214,14 +214,14 @@ impl ModuleInstance { } /// The `FuncEnvironment` implementation for use by the `ModuleInstance`. -pub struct FuncEnvironment<'dummy_environment> { - pub mod_info: &'dummy_environment ModuleInfo, +pub struct FuncEnvironment<'environment> { + pub mod_info: &'environment ModuleInfo, return_mode: ReturnMode, } -impl<'dummy_environment> FuncEnvironment<'dummy_environment> { - pub fn new(mod_info: &'dummy_environment ModuleInfo, return_mode: ReturnMode) -> Self { +impl<'environment> FuncEnvironment<'environment> { + pub fn new(mod_info: &'environment ModuleInfo, return_mode: ReturnMode) -> Self { Self { mod_info, return_mode, @@ -255,7 +255,7 @@ impl<'dummy_environment> FuncEnvironment<'dummy_environment> { } } -impl<'dummy_environment> FuncEnvironmentTrait for FuncEnvironment<'dummy_environment> { +impl<'environment> FuncEnvironmentTrait for FuncEnvironment<'environment> { fn triple(&self) -> &Triple { &self.mod_info.triple } @@ -280,6 +280,7 @@ impl<'dummy_environment> FuncEnvironmentTrait for FuncEnvironment<'dummy_environ } fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap { + // OLD // Create a static heap whose base address is stored at `vmctx+0`. let addr = func.create_global_value(ir::GlobalValueData::VMContext); let gv = func.create_global_value(ir::GlobalValueData::Load { @@ -297,6 +298,49 @@ impl<'dummy_environment> FuncEnvironmentTrait for FuncEnvironment<'dummy_environ }, index_type: I32, }) + // use memory::WasmMemory; + // if index == 0 { + // let heap_base = self.main_memory_base.unwrap_or_else(|| { + // let new_base = func.create_global_value(ir::GlobalValueData::VMContext { + // offset: 0.into(), + // }); + // self.main_memory_base = Some(new_base); + // new_base + // }); + + // func.create_heap(ir::HeapData { + // base: heap_base, + // min_size: 0.into(), + // guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(), + // style: ir::HeapStyle::Static { + // bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(), + // }, + // }) + // } else { + // let memory_base = self.memory_base.unwrap_or_else(|| { + // let memories_offset = self.ptr_size() as i32 * -2; + // let new_base = func.create_global_value(ir::GlobalValueData::VMContext { + // offset: memories_offset.into(), + // }); + // self.memory_base = Some(new_base); + // new_base + // }); + + // let memory_offset = (index - 1) * self.ptr_size(); + // let heap_base = func.create_global_value(ir::GlobalValueData::Deref { + // base: memory_base, + // offset: (memory_offset as i32).into(), + // }); + + // func.create_heap(ir::HeapData { + // base: heap_base, + // min_size: 0.into(), + // guard_size: (WasmMemory::DEFAULT_GUARD_SIZE as i64).into(), + // style: ir::HeapStyle::Static { + // bound: (WasmMemory::DEFAULT_HEAP_SIZE as i64).into(), + // }, + // }) + // } } fn make_table(&mut self, func: &mut ir::Function, table_index: TableIndex) -> ir::Table { @@ -558,12 +602,12 @@ impl<'data> ModuleEnvironment<'data> for ModuleInstance { ) { // NEW debug_assert!(base.is_none(), "global-value offsets not supported yet"); - self.info.table_elements.push(Exportable::new(TableElements { + self.info.table_elements.push(TableElements { table_index, base, offset, elements, - })); + }); } fn declare_memory(&mut self, memory: Memory) { diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index c094adfc4..df6fe2aeb 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -1,232 +1,165 @@ -//! An `Instance` contains all the runtime state used by execution of a wasm -//! module. -use cranelift_codegen::ir; -use cranelift_wasm::GlobalIndex; -use std::mem::transmute; -use std::ptr; -use std::any::Any; -use cranelift_codegen::ir::{AbiParam, types}; +//! An 'Instance' contains all the runtime state used by execution of a wasm module +use cranelift_wasm::{GlobalInit, FuncIndex}; +use super::env::ModuleInstance; +use super::env::{DataInitializer, Exportable}; +use cranelift_entity::EntityRef; use super::memory::LinearMemory; -use super::module::{DataInitializer, Module, Export, TableElements}; -use super::compilation::Compilation; -use super::execute::make_vmctx; +use std::marker::PhantomData; +use std::{slice, mem}; +use std::sync::Arc; -/// An Instance of a WebAssemby module. -#[derive(Debug)] -pub struct Instance { - // pub module: Box, +use spin::RwLock; +use super::super::common::slice::{BoundedSlice, UncheckedSlice}; - // pub compilation: Box, - - /// WebAssembly table data. - pub tables: Vec>, - - /// WebAssembly linear memory data. - pub memories: Vec, - - /// WebAssembly global variable data. - pub globals: Vec, +pub fn get_function_addr(base: *const (), functions: &[usize], func_index: &FuncIndex) -> *const () { + let offset = functions[func_index.index()]; + (base as usize + offset) as _ } +/// Zero-sized, non-instantiable type. +pub enum VmCtx {} + +impl VmCtx { + pub fn data(&self) -> &VmCtxData { + let heap_ptr = self as *const _ as *const VmCtxData; + unsafe { + &*heap_ptr.sub(1) + } + } + + /// This is safe because the offset is 32 bits and thus + /// cannot extend out of the guarded wasm memory. + pub fn fastpath_offset_ptr(&self, offset: u32) -> *const T { + let heap_ptr = self as *const _ as *const u8; + unsafe { + heap_ptr.add(offset as usize) as *const T + } + } +} + +#[repr(C)] +pub struct VmCtxData<'a> { + pub user_data: UserData, + globals: UncheckedSlice, + memories: UncheckedSlice>, + tables: UncheckedSlice>, + phantom: PhantomData<&'a ()>, +} + +#[repr(C)] +pub struct UserData { + // pub process: Dispatch, + pub instance: Instance, +} + + +/// An Instance of a WebAssembly module #[derive(Debug)] -pub enum InvokeResult { - VOID, - I32(i32), - I64(i64), - F32(f32), - F64(f64), +pub struct Instance { + /// WebAssembly table data + pub tables: Arc>>>, + + /// WebAssembly linear memory data + pub memories: Arc>, + + /// WebAssembly global variable data + pub globals: Vec, } impl Instance { /// Create a new `Instance`. - pub fn new( - module: &Module, - compilation: &Compilation, - data_initializers: &[DataInitializer], - ) -> Self { - let mut result = Self { - // module: Box::new(module), - // compilation: Box::new(compilation), - tables: Vec::new(), - memories: Vec::new(), - globals: Vec::new(), - }; - // println!("Instance::instantiate tables"); - result.instantiate_tables(module, compilation, &module.table_elements); - // println!("Instance::instantiate memories"); - result.instantiate_memories(module, data_initializers); - // println!("Instance::instantiate globals"); - result.instantiate_globals(module); - result - } + pub fn new(module: &ModuleInstance, data_initializers: &[DataInitializer], code_base: *const (), functions: &[usize]) -> Instance { + let mut tables: Vec> = Vec::new(); + let mut memories: Vec = Vec::new(); + let mut globals: Vec = Vec::new(); - /// Allocate memory in `self` for just the tables of the current module. - fn instantiate_tables( - &mut self, - module: &Module, - compilation: &Compilation, - table_initializers: &[TableElements], - ) { - debug_assert!(self.tables.is_empty()); - self.tables.reserve_exact(module.tables.len()); - for table in &module.tables { - let len = table.size; - let mut v = Vec::with_capacity(len); - v.resize(len, 0); - self.tables.push(v); - } - for init in table_initializers { - debug_assert!(init.base.is_none(), "globalvar base not supported yet"); - let to_init = - &mut self.tables[init.table_index][init.offset..init.offset + init.elements.len()]; - for (i, func_idx) in init.elements.iter().enumerate() { - let code_buf = &compilation.functions[module.defined_func_index(*func_idx).expect( - "table element initializer with imported function not supported yet", - )]; - to_init[i] = code_buf.as_ptr() as usize; + // instantiate_tables + { + tables.reserve_exact(module.info.tables.len()); + for table in &module.info.tables { + let len = table.entity.size; + let mut v = Vec::with_capacity(len); + v.resize(len, 0); + tables.push(v); } - } - } + // instantiate tables + for table_element in &module.info.table_elements { + assert!(table_element.base.is_none(), "globalvalue base not supported yet."); + let base = 0; - /// Allocate memory in `instance` for just the memories of the current module. - fn instantiate_memories(&mut self, module: &Module, data_initializers: &[DataInitializer]) { - debug_assert!(self.memories.is_empty()); - // Allocate the underlying memory and initialize it to all zeros. - // println!("instantiate_memories::reserve exact"); - self.memories.reserve_exact(module.memories.len()); - // println!("instantiate_memories::loop"); - for memory in &module.memories { - // println!("instantiate_memories::new linear memory: {}", memory.pages_count); - // We do this so at least there is one page - let pages_count = if (memory.pages_count as u32) > 0 { - memory.pages_count as u32 - } - else { - 1 - }; - let v = LinearMemory::new(pages_count, memory.maximum.map(|m| m as u32)); - self.memories.push(v); - } - for init in data_initializers { - // println!("instantiate_memories::initialize data"); - debug_assert!(init.base.is_none(), "globalvar base not supported yet"); - let mem_mut = self.memories[init.memory_index].as_mut(); - let to_init = &mut mem_mut[init.offset..init.offset + init.data.len()]; - to_init.copy_from_slice(init.data); - } - } + let table = &mut tables[table_element.table_index]; + for (i, func_index) in table_element.elements.iter().enumerate() { + // since the table just contains functions in the MVP + // we get the address of the specified function indexes + // to populate the table. - /// Allocate memory in `instance` for just the globals of the current module, - /// without any initializers applied yet. - fn instantiate_globals(&mut self, module: &Module) { - debug_assert!(self.globals.is_empty()); - // Allocate the underlying memory and initialize it to all zeros. - let globals_data_size = module.globals.len() * 8; - self.globals.resize(globals_data_size, 0); - } + // let func_index = *elem_index - module.info.imported_funcs.len() as u32; - /// Returns a mutable reference to a linear memory under the specified index. - pub fn memory_mut(&mut self, memory_index: usize) -> &mut LinearMemory { - self.memories - .get_mut(memory_index) - .unwrap_or_else(|| panic!("no memory for index {}", memory_index)) - } - - /// Returns a slice of the contents of allocated linear memory. - pub fn inspect_memory(&self, memory_index: usize, address: usize, len: usize) -> &[u8] { - &self - .memories - .get(memory_index) - .unwrap_or_else(|| panic!("no memory for index {}", memory_index)) - .as_ref()[address..address + len] - } - - /// Shows the value of a global variable. - pub fn inspect_global(&self, global_index: GlobalIndex, ty: ir::Type) -> &[u8] { - let offset = global_index * 8; - let len = ty.bytes() as usize; - &self.globals[offset..offset + len] - } - - - pub fn execute_fn( - &mut self, - module: &Module, - compilation: &Compilation, - func_name: String, - ) -> Result { - // println!("execute"); - // println!("TABLES: {:?}", self.tables); - // println!("MEMORIES: {:?}", self.memories); - // println!("GLOBALS: {:?}", self.globals); - - let export_func = module.exports.get(&func_name); - let func_index = match export_func { - Some(&Export::Function(index)) => index, - _ => panic!("No func name") - }; - - let code_buf = &compilation.functions[module - .defined_func_index(func_index) - .expect("imported start functions not supported yet")]; - - let sig_index = module.functions[func_index]; - let imported_sig = &module.signatures[sig_index]; - - // println!("FUNCTION CODE BUF={:?}", imported_sig); - - // Collect all memory base addresses and Vec. - let mut mem_base_addrs = self - .memories - .iter_mut() - .map(LinearMemory::base_addr) - .collect::>(); - let vmctx = make_vmctx(self, &mut mem_base_addrs); - - // unsafe { - // func = transmute::<_, fn(*const *mut u8) -> Box>(code_buf.as_ptr()); - // } - // ret = ; - match imported_sig.returns.len() { - 0 => unsafe { - let func = transmute::<_, fn(*const *mut u8)>(code_buf.as_ptr()); - func(vmctx.as_ptr()); - Ok(InvokeResult::VOID) - }, - 1 => { - let value_type = imported_sig.returns[0].value_type; - match value_type { - types::I32 => unsafe { - let func = transmute::<_, fn(*const *mut u8) -> i32>(code_buf.as_ptr()); - Ok(InvokeResult::I32(func(vmctx.as_ptr()))) - }, - types::I64 => unsafe { - let func = transmute::<_, fn(*const *mut u8) -> i64>(code_buf.as_ptr()); - Ok(InvokeResult::I64(func(vmctx.as_ptr()))) - }, - types::F32 => unsafe { - let func = transmute::<_, fn(*const *mut u8) -> f32>(code_buf.as_ptr()); - Ok(InvokeResult::F32(func(vmctx.as_ptr()))) - }, - types::F64 => unsafe { - let func = transmute::<_, fn(*const *mut u8) -> f64>(code_buf.as_ptr()); - Ok(InvokeResult::F64(func(vmctx.as_ptr()))) - }, - _ => panic!("Invalid signature") + let func_addr = get_function_addr(code_base, functions, *&func_index); + table[base + table_element.offset + i] = func_addr as _; } - }, - _ => panic!("Only one-returnf functions are supported for now") + } + }; + + // instantiate_memories + { + // Allocate the underlying memory and initialize it to all zeros. + memories.reserve_exact(module.info.memories.len()); + for memory in &module.info.memories { + let memory = memory.entity; + let v = LinearMemory::new(memory.pages_count as u32, memory.maximum.map(|m| m as u32)); + memories.push(v); + } + for init in data_initializers { + debug_assert!(init.base.is_none(), "globalvar base not supported yet"); + let mem_mut = memories[init.memory_index].as_mut(); + let to_init = &mut mem_mut[init.offset..init.offset + init.data.len()]; + to_init.copy_from_slice(&init.data); + } + }; + + // instantiate_globals + { + let globals_count = module.info.globals.len(); + // Allocate the underlying memory and initialize it to zeros + let globals_data_size = globals_count * 8; + globals.resize(globals_data_size, 0); + + // cast the globals slice to a slice of i64. + let globals_data = unsafe { slice::from_raw_parts_mut(globals.as_mut_ptr() as *mut i64, globals_count) }; + for (i, global) in module.info.globals.iter().enumerate() { + let value: i64 = match global.entity.initializer { + GlobalInit::I32Const(n) => n as _, + GlobalInit::I64Const(n) => n, + GlobalInit::F32Const(f) => unsafe { mem::transmute(f as f64) }, + GlobalInit::F64Const(f) => unsafe { mem::transmute(f) }, + _ => unimplemented!(), + }; + + globals_data[i] = value; + } + }; + + Instance { + tables: Arc::new(tables.into_iter().map(|table| RwLock::new(table)).collect()), + memories: Arc::new(memories.into_iter().collect()), + globals: globals, } - - // println!("TABLES: {:?}", self.tables); - // println!("MEMORIES: {:?}", self.memories); - // println!("{:?}", module.exports); - // println!("execute end"); - - - } + pub fn memories(&self) -> Arc> { + self.memories.clone() + } +} + +impl Clone for Instance { + fn clone(&self) -> Instance { + Instance { + tables: Arc::clone(&self.tables), + memories: Arc::clone(&self.memories), + globals: self.globals.clone(), + } + } } diff --git a/src/webassembly/memory.rs b/src/webassembly/memory.rs index f5dcf4278..4830b9386 100644 --- a/src/webassembly/memory.rs +++ b/src/webassembly/memory.rs @@ -1,158 +1,113 @@ -use errno; -use libc; -use region; -use std::mem; -use std::ptr; +use memmap; +use std::fmt; -/// Round `size` up to the nearest multiple of `page_size`. -fn round_up_to_page_size(size: usize, page_size: usize) -> usize { - (size + (page_size - 1)) & !(page_size - 1) +const PAGE_SIZE: u32 = 65536; +const MAX_PAGES: u32 = 65536; + +/// A linear memory instance. +/// +/// This linear memory has a stable base address and at the same time allows +/// for dynamical growing. +pub struct LinearMemory { + mmap: memmap::MmapMut, + current: u32, + maximum: Option, } -/// A simple struct consisting of a pointer and length. -struct PtrLen { - ptr: *mut u8, - len: usize, -} +impl LinearMemory { + /// Create a new linear memory instance with specified initial and maximum number of pages. + /// + /// `maximum` cannot be set to more than `65536` pages. + pub fn new(initial: u32, maximum: Option) -> Self { + assert!(initial <= MAX_PAGES); + assert!(maximum.is_none() || maximum.unwrap() <= MAX_PAGES); -impl PtrLen { - /// Create a new empty `PtrLen`. - fn new() -> Self { - Self { - ptr: ptr::null_mut(), - len: 0, - } - } - - /// Create a new `PtrLen` pointing to at least `size` bytes of memory, - /// suitably sized and aligned for memory protection. - #[cfg(not(target_os = "windows"))] - fn with_size(size: usize) -> Result { - let page_size = region::page::size(); - let alloc_size = round_up_to_page_size(size, page_size); - unsafe { - let mut ptr: *mut libc::c_void = mem::uninitialized(); - let err = libc::posix_memalign(&mut ptr, page_size, alloc_size); - if err == 0 { - Ok(Self { - ptr: ptr as *mut u8, - len: alloc_size, - }) - } else { - Err(errno::Errno(err).to_string()) - } - } - } - - #[cfg(target_os = "windows")] - fn with_size(size: usize) -> Result { - use winapi::um::memoryapi::VirtualAlloc; - use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE}; - - let page_size = region::page::size(); - - // VirtualAlloc always rounds up to the next multiple of the page size - let ptr = unsafe { - VirtualAlloc( - ptr::null_mut(), - size, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE, - ) + let len = PAGE_SIZE * match maximum { + Some(val) => val, + None => initial, }; - if !ptr.is_null() { - Ok(Self { - ptr: ptr as *mut u8, - len: round_up_to_page_size(size, page_size), - }) - } else { - Err(errno::errno().to_string()) - } - } -} - -/// JIT memory manager. This manages pages of suitably aligned and -/// accessible memory. -pub struct Memory { - allocations: Vec, - executable: usize, - current: PtrLen, - position: usize, -} - -impl Memory { - pub fn new() -> Self { + let mmap = memmap::MmapMut::map_anon(len as usize).unwrap(); Self { - allocations: Vec::new(), - executable: 0, - current: PtrLen::new(), - position: 0, + mmap, + current: initial, + maximum, } } - fn finish_current(&mut self) { - self.allocations - .push(mem::replace(&mut self.current, PtrLen::new())); - self.position = 0; + /// Returns an base address of this linear memory. + pub fn base_addr(&mut self) -> *mut u8 { + self.mmap.as_mut_ptr() } - /// TODO: Use a proper error type. - pub fn allocate(&mut self, size: usize) -> Result<*mut u8, String> { - if size <= self.current.len - self.position { - // TODO: Ensure overflow is not possible. - let ptr = unsafe { self.current.ptr.offset(self.position as isize) }; - self.position += size; - return Ok(ptr); - } - - self.finish_current(); - - // TODO: Allocate more at a time. - self.current = PtrLen::with_size(size)?; - self.position = size; - Ok(self.current.ptr) + /// Returns a number of allocated wasm pages. + pub fn current_size(&self) -> u32 { + self.current } - /// Set all memory allocated in this `Memory` up to now as readable and executable. - pub fn set_readable_and_executable(&mut self) { - self.finish_current(); - - for &PtrLen { ptr, len } in &self.allocations[self.executable..] { - if len != 0 { - unsafe { - region::protect(ptr, len, region::Protection::ReadExecute) - .expect("unable to make memory readable+executable"); - } + /// Grow memory by the specified amount of pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of pages. + pub fn grow(&mut self, add_pages: u32) -> Option { + let new_pages = match self.current.checked_add(add_pages) { + Some(new_pages) => new_pages, + None => return None, + }; + if let Some(val) = self.maximum { + if new_pages > val { + return None; + } + } else { + // Wasm linear memories are never allowed to grow beyond what is + // indexable. If the memory has no maximum, enforce the greatest + // limit here. + if new_pages >= 65536 { + return None; } } - } - /// Set all memory allocated in this `Memory` up to now as readonly. - pub fn set_readonly(&mut self) { - self.finish_current(); + let prev_pages = self.current; + let new_bytes = (new_pages * PAGE_SIZE) as usize; - for &PtrLen { ptr, len } in &self.allocations[self.executable..] { - if len != 0 { - unsafe { - region::protect(ptr, len, region::Protection::Read) - .expect("unable to make memory readonly"); - } - } + if self.mmap.len() < new_bytes { + // If we have no maximum, this is a "dynamic" heap, and it's allowed + // to move. + assert!(self.maximum.is_none()); + let mut new_mmap = memmap::MmapMut::map_anon(new_bytes).unwrap(); + new_mmap.copy_from_slice(&self.mmap); + self.mmap = new_mmap; } + + self.current = new_pages; + + // Ensure that newly allocated area is zeroed. + let new_start_offset = (prev_pages * PAGE_SIZE) as usize; + let new_end_offset = (new_pages * PAGE_SIZE) as usize; + for i in new_start_offset..new_end_offset { + assert!(self.mmap[i] == 0); + } + + Some(prev_pages) } } -// TODO: Implement Drop to unprotect and deallocate the memory? - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_round_up_to_page_size() { - assert_eq!(round_up_to_page_size(0, 4096), 0); - assert_eq!(round_up_to_page_size(1, 4096), 4096); - assert_eq!(round_up_to_page_size(4096, 4096), 4096); - assert_eq!(round_up_to_page_size(4097, 4096), 8192); +impl fmt::Debug for LinearMemory { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("LinearMemory") + .field("current", &self.current) + .field("maximum", &self.maximum) + .finish() + } +} + +impl AsRef<[u8]> for LinearMemory { + fn as_ref(&self) -> &[u8] { + &self.mmap + } +} + +impl AsMut<[u8]> for LinearMemory { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.mmap } } diff --git a/src/webassembly/mod.rs b/src/webassembly/mod.rs index ecb0ae814..e52ecb1b2 100644 --- a/src/webassembly/mod.rs +++ b/src/webassembly/mod.rs @@ -7,6 +7,8 @@ pub mod errors; // pub mod execute; pub mod utils; pub mod env; +pub mod memory; +pub mod instance; use std::str::FromStr; use std::time::{Duration, Instant}; @@ -25,6 +27,7 @@ use cranelift_codegen::verifier; use cranelift_wasm::{translate_module, ReturnMode}; pub use self::env::ModuleInstance; +pub use self::instance::Instance; // pub use self::compilation::{compile_module, Compilation}; // pub use self::environ::{ModuleEnvironment}; @@ -68,22 +71,22 @@ pub struct ImportObject { pub fn instantiate(buffer_source: Vec, import_object: Option) -> Result { let flags = Flags::new(settings::builder()); let return_mode = ReturnMode::NormalReturns; - let mut dummy_environ = + let mut environ = ModuleInstance::with_triple_flags(triple!("riscv64"), flags.clone(), return_mode); - translate_module(&buffer_source, &mut dummy_environ).map_err(|e| ErrorKind::CompileError(e.to_string()))?; + translate_module(&buffer_source, &mut environ).map_err(|e| ErrorKind::CompileError(e.to_string()))?; - let isa = isa::lookup(dummy_environ.info.triple) + let isa = isa::lookup(environ.info.triple) .unwrap() - .finish(dummy_environ.info.flags); + .finish(environ.info.flags); - for func in dummy_environ.info.function_bodies.values() { + for func in environ.info.function_bodies.values() { verifier::verify_function(func, &*isa) .map_err(|errors| panic!(pretty_verifier_error(func, Some(&*isa), None, errors))) .unwrap(); }; unimplemented!() - // Ok(dummy_environ) + // Ok(environ) // let now = Instant::now(); // let isa = construct_isa(); // println!("instantiate::init {:?}", now.elapsed());