diff --git a/src/common/slice.rs b/src/common/slice.rs index 0f1620ce6..bb82a64c2 100644 --- a/src/common/slice.rs +++ b/src/common/slice.rs @@ -1,7 +1,7 @@ use std::ops::{Index, IndexMut}; use std::ptr::NonNull; use std::marker::PhantomData; -use cranelift_entity::EntityRef; +use crate::runtime::types::MapIndex; #[derive(Copy, Clone, Debug)] #[repr(transparent)] @@ -109,19 +109,32 @@ impl<'a, T> From<&'a [T]> for BoundedSlice { #[derive(Debug)] #[repr(transparent)] -pub struct IndexedSlice { +pub struct IndexedSlice<'a, T, I> { ptr: NonNull, _phantom: PhantomData, } -impl IndexedSlice { - pub fn new(ptr: *mut T) -> Self { +impl<'a, T: 'a, I> IndexedSlice +where + I: MapIndex, +{ + pub(crate) fn new(ptr: *mut T) -> Self { Self { ptr: NonNull::new(ptr).unwrap(), _phantom: PhantomData, } } + pub unsafe fn get(&self, index: I) -> &T { + let ptr = self.as_ptr(); + &*ptr.add(index.index()) + } + + pub unsafe fn get_mut(&mut self, index: I) -> &mut T { + let ptr = self.as_mut_ptr(); + &mut *ptr.add(index.index()) + } + pub fn as_ptr(&self) -> *const T { self.ptr.as_ptr() } @@ -129,19 +142,4 @@ impl IndexedSlice { pub fn as_mut_ptr(&mut self) -> *mut T { self.ptr.as_ptr() } -} - -impl Index for IndexedSlice { - type Output = T; - fn index(&self, index: I) -> &T { - let ptr = self.as_ptr(); - unsafe { &*ptr.add(index.index()) } - } -} - -impl IndexMut for IndexedSlice { - fn index_mut(&mut self, index: I) -> &mut T { - let ptr = self.as_mut_ptr(); - unsafe { &mut *ptr.add(index.index()) } - } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 064912838..c63b82413 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,3 +34,4 @@ pub mod sighandler; mod spectests; pub mod update; pub mod webassembly; +pub mod runtime; diff --git a/src/runtime/backend.rs b/src/runtime/backend.rs new file mode 100644 index 000000000..5922ac5a4 --- /dev/null +++ b/src/runtime/backend.rs @@ -0,0 +1,15 @@ +use crate::runtime::module::Module; +use crate::runtime::types::FuncIndex; +use crate::runtime::{ + vm, + module::Module, + types::FuncIndex, +}; + +pub trait Compiler { + fn compile(wasm: &[u8]) -> Box; +} + +pub trait FuncResolver { + pub fn resolve(&self, index: FuncIndex) -> *const vm::Func; +} \ No newline at end of file diff --git a/src/webassembly/backing.rs b/src/runtime/backing.rs similarity index 67% rename from src/webassembly/backing.rs rename to src/runtime/backing.rs index 8082a538f..d03a29f04 100644 --- a/src/webassembly/backing.rs +++ b/src/runtime/backing.rs @@ -1,13 +1,20 @@ use super::vm; -use super::module::Module; +use crate::module::Module; use super::table::{TableBacking, TableScheme}; use super::memory::LinearMemory; use super::instance::{InstanceOptions, InstanceABI}; -use super::ImportObject; -use cranelift_entity::EntityRef; +use std::mem; + +use crate::runtime::{ + vm, + module::Module, + table::TableBacking, + types::{Val, GlobalInit}, +}; + #[derive(Debug)] -pub struct Backing { +pub struct LocalBacking { memories: Box<[LinearMemory]>, tables: Box<[TableBacking]>, @@ -16,47 +23,48 @@ pub struct Backing { vm_globals: Box<[vm::LocalGlobal]>, } -impl Backing { - pub fn new(module: &Module, options: &InstanceOptions, imports: &ImportObject) -> Self { - let memories = Backing::generate_memories(module, options); - let tables = Backing::generate_tables(module, options); +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); + let globals = Self::generate_globals(module); - Backing { + Self { memories, tables, - vm_memories: Backing::finalize_memories(module, &memories, options), - vm_tables: Backing::finalize_tables(module, &tables, options, imports), - vm_globals: Backing::generate_globals(module), + vm_memories: Self::finalize_memories(module, &mut memories[..], options), + vm_tables: Self::finalize_tables(module, &mut tables[..], options), + vm_globals: Self::finalize_globals(module, imports, globals), } } fn generate_memories(module: &Module, options: &InstanceOptions) -> Box<[LinearMemory]> { - let memories = Vec::with_capacity(module.info.memories.len()); + let mut memories = Vec::with_capacity(module.memories.len()); - for mem in &module.info.memories { - let memory = mem.entity; + for (_, mem) in &module.memories { // If we use emscripten, we set a fixed initial and maximum debug!( "Instance - init memory ({}, {:?})", - memory.minimum, memory.maximum + memory.min, memory.max ); - let memory = if options.abi == InstanceABI::Emscripten { - // We use MAX_PAGES, so at the end the result is: - // (initial * LinearMemory::PAGE_SIZE) == LinearMemory::DEFAULT_HEAP_SIZE - // However, it should be: (initial * LinearMemory::PAGE_SIZE) == 16777216 - LinearMemory::new(LinearMemory::MAX_PAGES, None) - } else { - LinearMemory::new(memory.minimum, memory.maximum.map(|m| m as u32)) - }; + // let memory = if options.abi == InstanceABI::Emscripten { + // // We use MAX_PAGES, so at the end the result is: + // // (initial * LinearMemory::PAGE_SIZE) == LinearMemory::DEFAULT_HEAP_SIZE + // // However, it should be: (initial * LinearMemory::PAGE_SIZE) == 16777216 + // LinearMemory::new(LinearMemory::MAX_PAGES, None) + // } else { + // LinearMemory::new(memory.minimum, memory.maximum.map(|m| m as u32)) + // }; + let memory = LinearMemory::new(mem); memories.push(memory); } memories.into_boxed_slice() } - fn finalize_memories(module: &Module, memories: &[LinearMemory], options: &InstanceOptions) -> Box<[vm::LocalMemory]> { - for init in &module.info.data_initializers { + fn finalize_memories(module: &Module, memories: &mut [LinearMemory], options: &InstanceOptions) -> Box<[vm::LocalMemory]> { + for init in &module.data_initializers { debug_assert!(init.base.is_none(), "globalvar base not supported yet"); let offset = init.offset; let mem: &mut LinearMemory = &mut memories[init.memory_index.index()]; @@ -76,35 +84,44 @@ impl Backing { debug!("emscripten::finish setup memory"); } - memories.iter().map(|mem| mem.into_vm_memory()).collect::>().into_boxed_slice() + memories.iter_mut().map(|mem| mem.into_vm_memory()).collect::>().into_boxed_slice() } fn generate_tables(module: &Module, options: &InstanceOptions) -> Box<[TableBacking]> { - let mut tables = Vec::with_capacity(module.info.tables.len()); + let mut tables = Vec::with_capacity(module.tables.len()); - for table in &module.info.tables { - let scheme = TableScheme::from_table(table.entity); - let table_backing = TableBacking::new(&scheme); - tables.push(table_backing); + for table in &module.tables { + let table_backing = TableBacking::new(table); + tables.push(table_backing); } tables.into_boxed_slice() } - fn finalize_tables(module: &Module, tables: &[TableBacking], options: &InstanceOptions, imports: &ImportObject) -> Box<[vm::LocalTable]> { + fn finalize_tables(module: &Module, tables: &[TableBacking], options: &InstanceOptions) -> Box<[vm::LocalTable]> { tables.iter().map(|table| table.into_vm_table()).collect::>().into_boxed_slice() } fn generate_globals(module: &Module) -> Box<[vm::LocalGlobal]> { - let mut globals = Vec::with_capacity(module.info.globals.len()); - - for global in module.info.globals.iter().map(|mem| mem.entity) { - - } + let mut globals = vec![vm::LocalGlobal::null(); module.globals.len()]; globals.into_boxed_slice() } + fn finalize_globals(module: &Module, imports: &ImportBacking, globals: Box<[vm::LocalGlobal]>) -> Box<[vm::LocalGlobal]> { + for (to, from) in globals.iter_mut().zip(module.globals.iter()) { + *to = match from.init { + GlobalInit::Val(Val::I32(x)) => x as u64, + GlobalInit::Val(Val::I64(x)) => x as u64, + GlobalInit::Val(Val::F32(x)) => x as u64, + GlobalInit::Val(Val::F64(x)) => x, + GlobalInit::GetGlobal(index) => unsafe { (*imports.globals[index.index()].global).data }, + }; + } + + globals + } + // fn generate_tables(module: &Module, _options: &InstanceOptions) -> (Box<[TableBacking]>, Box<[vm::LocalTable]>) { // let mut tables = Vec::new(); // // Reserve space for tables @@ -174,7 +191,7 @@ impl Backing { } #[derive(Debug)] -pub struct ImportsBacking { +pub struct ImportBacking { functions: Box<[vm::ImportedFunc]>, memories: Box<[vm::ImportedMemory]>, tables: Box<[vm::ImportedTable]>, diff --git a/src/runtime/instance.rs b/src/runtime/instance.rs new file mode 100644 index 000000000..29216bffb --- /dev/null +++ b/src/runtime/instance.rs @@ -0,0 +1,26 @@ +use crate::runtime::{ + vm, + backing::{LocalBacking, ImportBacking}, +}; +use std::sync::Arc; + +pub struct Instance { + pub vmctx: vm::Ctx, + + pub finalized_funcs: Box<[*const vm::Func]>, + + pub backing: LocalBacking, + pub imports: ImportBacking, + + pub module: Arc, +} + +impl Instance { + pub fn new(module: Arc) -> Box { + + Box::new(Instance { + vmctx, + finalized_funcs + }) + } +} \ No newline at end of file diff --git a/src/webassembly/memory.rs b/src/runtime/memory.rs similarity index 57% rename from src/webassembly/memory.rs rename to src/runtime/memory.rs index 207dd9953..39f9ebd1d 100644 --- a/src/webassembly/memory.rs +++ b/src/runtime/memory.rs @@ -8,15 +8,18 @@ use std::ops::{Deref, DerefMut}; use std::slice; use crate::common::mmap::Mmap; -use super::vm::LocalMemory; +use crate::runtime::{ + vm::LocalMemory, + types::{Memory, Map, FuncIndex}, +}; /// A linear memory instance. #[derive(Debug)] pub struct LinearMemory { - // The mmap allocation + /// The actual memory allocation. mmap: Mmap, - // current number of wasm pages + /// The current number of wasm pages. current: u32, // The maximum size the WebAssembly Memory is allowed to grow @@ -25,17 +28,20 @@ pub struct LinearMemory { // front. However, the engine may ignore or clamp this reservation // request. In general, most WebAssembly modules shouldn't need // to set a maximum. - maximum: Option, + max: Option, // The size of the extra guard pages after the end. // Is used to optimize loads and stores with constant offsets. offset_guard_size: usize, + + /// Requires exception catching to handle out-of-bounds accesses. + requires_signal_catch: bool, } /// It holds the raw bytes of memory accessed by a WebAssembly Instance impl LinearMemory { - pub const PAGE_SIZE: u32 = 65536; - pub const MAX_PAGES: u32 = 65536; + pub const PAGE_SIZE: u32 = 65_536; + pub const MAX_PAGES: u32 = 65_536; pub const DEFAULT_HEAP_SIZE: usize = 1 << 32; // 4 GiB pub const DEFAULT_GUARD_SIZE: usize = 1 << 31; // 2 GiB pub const DEFAULT_SIZE: usize = Self::DEFAULT_HEAP_SIZE + Self::DEFAULT_GUARD_SIZE; // 6 GiB @@ -43,46 +49,41 @@ 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 <= Self::MAX_PAGES); - assert!(maximum.is_none() || maximum.unwrap() <= Self::MAX_PAGES); - debug!( - "Instantiate LinearMemory(initial={:?}, maximum={:?})", - initial, maximum - ); + pub fn new(mem: &Memory) -> Self { + assert!(mem.min <= Self::MAX_PAGES); + assert!(mem.max.is_none() || mem.max.unwrap() <= Self::MAX_PAGES); + debug!("Instantiate LinearMemory(mem: {:?})", mem); - let mut mmap = Mmap::with_size(Self::DEFAULT_SIZE).expect("Can't create mmap"); + let (mmap_size, initial_pages, offset_guard_size, requires_signal_catch) = if mem.is_static_heap() { + (Self::DEFAULT_SIZE, mem.min, Self::DEFAULT_GUARD_SIZE, true) + // This is a static heap + } else { + // this is a dynamic heap + assert!(!mem.shared, "shared memories must have a maximum size."); - let base = mmap.as_mut_ptr(); + (mem.min as usize * Self::PAGE_SIZE as usize, mem.min, 0, false) + } + + let mut mmap = Mmap::with_size(mmap_size).unwrap(); // map initial pages as readwrite since the inital mmap is mapped as not accessible. - if initial != 0 { + if initial_pages != 0 { unsafe { region::protect( - base, - initial as usize * Self::PAGE_SIZE as usize, + mmap.as_mut_ptr(), + initial_pages as usize * Self::PAGE_SIZE as usize, region::Protection::ReadWrite, ) } - .expect("unable to make memory inaccessible"); + .expect("unable to make memory accessible"); } - debug!("LinearMemory instantiated"); - debug!( - " - usable: {:#x}..{:#x}", - base as usize, - (base as usize) + LinearMemory::DEFAULT_HEAP_SIZE - ); - debug!( - " - guard: {:#x}..{:#x}", - (base as usize) + LinearMemory::DEFAULT_HEAP_SIZE, - (base as usize) + LinearMemory::DEFAULT_SIZE - ); Self { mmap, - current: initial, - offset_guard_size: LinearMemory::DEFAULT_GUARD_SIZE, - maximum, + current: initial_pages, + max: mem.max, + offset_guard_size, + requires_signal_catch, } } @@ -116,8 +117,60 @@ impl LinearMemory { /// /// Returns `None` if memory can't be grown by the specified amount /// of pages. - pub fn grow(&mut self, add_pages: u32) -> Option { - debug!("grow_memory called!"); + pub fn grow_dynamic(&mut self, add_pages: u32) -> Option { + debug!("grow_memory_dynamic called!"); + assert!(self.max.is_none()); + if add_pages == 0 { + return Some(self.current as _); + } + + let prev_pages = self.current; + + 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; + } + // Wasm linear memories are never allowed to grow beyond what is + // indexable. If the memory has no maximum, enforce the greatest + // limit here. + } else if new_pages >= Self::MAX_PAGES { + return None; + } + + let prev_bytes = (prev_pages * Self::PAGE_SIZE) as usize; + let new_bytes = (new_pages * Self::PAGE_SIZE) as usize; + + if new_bytes > self.mmap.len() - self.offset_guard_size { + let mmap_size = new_bytes.checked_add(self.offset_guard_size)?; + let mut new_mmap = Mmap::with_size(request_bytes).ok()?; + + unsafe { + region::protect( + new_mmap.as_mut_ptr(), + new_bytes, + region::Protection::ReadWrite, + ).ok()?; + } + + let copy_size = self.mmap.len() - self.offset_guard_size; + new_mmap.as_mut_slice()[..copy_size].copy_from_slice(self.mmap.as_slice()[..copy_size]); + + self.mmap = new_mmap; + } + + self.current = new_pages; + + Some(prev_pages as i32) + } + + pub fn grow_static(&mut self, add_pages: u32) -> Option { + debug!("grow_memory_static called!"); + assert!(self.max.is_some()); if add_pages == 0 { return Some(self.current as _); } @@ -143,40 +196,13 @@ impl LinearMemory { let prev_bytes = (prev_pages * Self::PAGE_SIZE) as usize; let new_bytes = (new_pages * Self::PAGE_SIZE) as usize; - // if new_bytes > self.mmap.len() - self.offset_guard_size { unsafe { region::protect( self.mmap.as_ptr().add(prev_bytes) as _, new_bytes - prev_bytes, region::Protection::ReadWrite, - ) + ).ok()?; } - .expect("unable to make memory inaccessible"); - // }; - // if new_bytes > self.mmap.len() - self.offset_guard_size { - // // If we have no maximum, this is a "dynamic" heap, and it's allowed to move. - // assert!(self.maximum.is_none()); - // let guard_bytes = self.offset_guard_size; - // let request_bytes = new_bytes.checked_add(guard_bytes)?; - - // let mut new_mmap = Mmap::with_size(request_bytes).ok()?; - - // // Make the offset-guard pages inaccessible. - // unsafe { - // region::protect( - // new_mmap.as_ptr().add(new_bytes), - // guard_bytes, - // region::Protection::Read | region::Protection::Write, - // // region::Protection::None, - // ) - // } - // .expect("unable to make memory inaccessible"); - - // let copy_len = self.mmap.len() - self.offset_guard_size; - // new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&self.mmap.as_slice()[..copy_len]); - - // self.mmap = new_mmap; - // } self.current = new_pages; @@ -213,3 +239,8 @@ 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 new file mode 100644 index 000000000..d2b3f998d --- /dev/null +++ b/src/runtime/mod.rs @@ -0,0 +1,9 @@ + +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 diff --git a/src/runtime/module.rs b/src/runtime/module.rs new file mode 100644 index 000000000..deb87a83c --- /dev/null +++ b/src/runtime/module.rs @@ -0,0 +1,52 @@ +use crate::runtime::types::{ + Map, + FuncIndex, MemoryIndex, TableIndex, GlobalIndex, + Memory, Globals, GlobalDesc, Func, Table, +}; +use crate::runtime::backend::FuncResolver; + +/// This is used to instantiate a new webassembly module. +#[derive(Debug)] +pub struct Module { + pub functions: Box, + pub memories: Map, + pub globals: Map, + pub tables: Map, + + pub imported_functions: Map<(ImportName, Func), FuncIndex>, + pub imported_memories: Map<(ImportName, Memory), MemoryIndex>, + pub imported_tables: Map<(ImportName, Table), TableIndex>, + pub imported_globals: Map<(ImportName, GlobalDesc), GlobalIndex>, + + pub exported: Vec<(ItemName, Export)>, + + pub data_initializers: Vec, + pub start_func: FuncIndex, + + pub signatures: Map, +} + +pub type ModuleName = Vec; +pub type ItemName = Vec; +pub type ImportName = (ModuleName, ItemName); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Export { + Func(FuncIndex), + Memory(MemoryIndex), + Global(GlobalIndex), + Table(TableIndex), +} + +/// A data initializer for linear memory. +#[derive(Debug)] +pub struct DataInitializer { + /// The index of the memory to initialize. + pub memory_index: MemoryIndex, + /// Optionally a globalvalue base to initialize at. + pub base: Option, + /// A constant offset to initialize at. + pub offset: usize, + /// The initialization data. + pub data: Vec, +} \ No newline at end of file diff --git a/src/runtime/table.rs b/src/runtime/table.rs new file mode 100644 index 000000000..3c72290ab --- /dev/null +++ b/src/runtime/table.rs @@ -0,0 +1,38 @@ +use super::vm; +use crate::runtime::types::{ElementType, Table}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum TableElements { + /// This is intended to be a caller-checked Anyfunc. + Anyfunc(Box<[vm::Anyfunc]>), +} + +#[derive(Debug)] +pub struct TableBacking { + pub elements: TableElements, + pub max: Option, +} + +impl TableBacking { + pub fn new(table: &Table) -> Self { + match table.ty { + ElementType::Anyfunc => { + Self { + elements: TableElements::Anyfunc(vec![vm::Anyfunc::null(); table.min].into_boxed_slice()), + max: table.max, + } + } + } + } + + pub fn into_vm_table(&mut self) -> vm::LocalTable { + match self.elements { + TableElements::Anyfunc(funcs) => { + vm::LocalTable { + base: funcs.as_mut_ptr() as *mut u8, + current_elements: funcs.len(), + } + }, + } + } +} \ No newline at end of file diff --git a/src/runtime/types.rs b/src/runtime/types.rs new file mode 100644 index 000000000..ad63c70d6 --- /dev/null +++ b/src/runtime/types.rs @@ -0,0 +1,270 @@ +use std::marker::PhantomData; +use std::{slice, iter}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Type { + /// The `i32` type. + I32, + /// The `i64` type. + I64, + /// The `f32` type. + F32, + /// The `f64` type. + F64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Val { + /// The `i32` type. + I32(i32), + /// The `i64` type. + I64(i64), + /// The `f32` type. + F32(u32), + /// The `f64` type. + F64(u64), +} + +impl From for Val { + fn from(n: i32) -> Self { + Self::I32(n) + } +} + +impl From for Val { + fn from(n: i64) -> Self { + Self::I64(n) + } +} + +impl From for Val { + fn from(n: f32) -> Self { + Self::F32(n.to_bits()) + } +} + +impl From for Val { + fn from(n: f64) -> Self { + Self::I64(n.to_bits()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ElementType { + /// Any wasm function. + Anyfunc, +} + +#[derive(Debug, Clone, Copy)] +pub struct Table { + /// Type of data stored in this table. + pub ty: ElementType, + /// The minimum number of elements that must be stored in this table. + pub min: u32, + /// The maximum number of elements in this table. + pub max: Option, +} + +/// A global value initializer. +/// Overtime, this will be able to represent more and more +/// complex expressions. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GlobalInit { + Val(Val), + GetGlobal(GlobalIndex), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct GlobalDesc { + pub mutable: bool, + pub ty: Type, +} + +/// A wasm global. +#[derive(Debug, Clone, Copy)] +pub struct Global { + pub desc: GlobalDesc, + pub init: GlobalInit, +} + +/// A wasm memory. +#[derive(Debug, Clone, Copy)] +pub struct Memory { + /// The minimum number of allowed pages. + pub min: u32, + /// The maximum number of allowed pages. + pub max: Option, + /// This memory can be shared between wasm threads. + pub shared: bool, +} + +impl Memory { + pub fn is_static_heap(&self) -> bool { + self.max.is_some() + } +} + +/// A wasm func. +#[derive(Debug)] +pub struct Func { + pub params: Vec, + pub returns: Vec, +} + +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 { + 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) + } +} + +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, T, I> 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, T, I> 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 { + Iter::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) -> Self::Item { + 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::Iter<'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) -> Self::Item { + self.enumerated.next().map(|i, v| (I::new(i), v)) + } +} + +macro_rules! define_map_index { + ($ty:ident) => { + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + pub struct $ty (u32); + impl MapIndex for $ty { + fn new(index: usize) -> Self { + $ty (index as _) + } + + fn index(&self) -> usize { + self.0 as usize + } + } + }; + ($($ty:ident),*) => { + $( + define_map_index!($ty); + )* + }; +} + +define_map_index![ + FuncIndex, MemoryIndex, GlobalIndex, TableIndex, + SignatureIndex, +]; \ No newline at end of file diff --git a/src/webassembly/vm.rs b/src/runtime/vm.rs similarity index 72% rename from src/webassembly/vm.rs rename to src/runtime/vm.rs index 1f25c103c..0494ed87e 100644 --- a/src/webassembly/vm.rs +++ b/src/runtime/vm.rs @@ -1,40 +1,38 @@ use std::{ptr, mem}; -use crate::common::slice::IndexedSlice; -use cranelift_wasm::{ - TableIndex, MemoryIndex, GlobalIndex, FuncIndex, - DefinedTableIndex, DefinedMemoryIndex, DefinedGlobalIndex, +use crate::runtime::types::{ + MemoryIndex, TableIndex, GlobalIndex, FuncIndex, SignatureIndex, }; #[derive(Debug)] #[repr(C)] -pub struct VmCtx { +pub struct Ctx<'a> { /// A pointer to an array of locally-defined memories, indexed by `DefinedMemoryIndex`. - pub(in crate::webassembly) memories: IndexedSlice, + pub memories: *mut LocalMemory, /// A pointer to an array of locally-defined tables, indexed by `DefinedTableIndex`. - pub(in crate::webassembly) tables: IndexedSlice, + pub tables: *mut LocalTable, /// A pointer to an array of locally-defined globals, indexed by `DefinedGlobalIndex`. - pub(in crate::webassembly) globals: IndexedSlice, + pub globals: *mut LocalGlobal, /// A pointer to an array of imported memories, indexed by `MemoryIndex, - pub(in crate::webassembly) imported_memories: IndexedSlice, + pub imported_memories: *mut ImportedMemory, /// A pointer to an array of imported tables, indexed by `TableIndex`. - pub(in crate::webassembly) imported_tables: IndexedSlice, + pub imported_tables: *mut ImportedTable, /// A pointer to an array of imported globals, indexed by `GlobalIndex`. - pub(in crate::webassembly) imported_globals: IndexedSlice, + pub imported_globals: *mut ImportedGlobal, /// A pointer to an array of imported functions, indexed by `FuncIndex`. - pub(in crate::webassembly) imported_funcs: IndexedSlice, + pub imported_funcs: *mut ImportedFunc, /// Signature identifiers for signature-checked indirect calls. - pub(in crate::webassembly) sig_ids: IndexedSlice, + pub sig_ids: *mut SigId, } -impl VmCtx { +impl Ctx { pub fn new( memories: *mut LocalMemory, tables: *mut LocalTable, @@ -46,14 +44,14 @@ impl VmCtx { sig_ids: *mut SigId, ) -> Self { Self { - memories: IndexedSlice::new(memories), - tables: IndexedSlice::new(tables), - globals: IndexedSlice::new(globals), - imported_memories: IndexedSlice::new(imported_memories), - imported_tables: IndexedSlice::new(imported_tables), - imported_globals: IndexedSlice::new(imported_globals), - imported_funcs: IndexedSlice::new(imported_funcs), - sig_ids: IndexedSlice::new(sig_ids), + memories, + tables, + globals, + imported_memories, + imported_tables, + imported_globals, + imported_funcs, + sig_ids, } } @@ -100,7 +98,7 @@ pub enum Func {} #[repr(C)] pub struct ImportedFunc { pub func: *const Func, - pub vmctx: *mut VmCtx, + pub vmctx: *mut Ctx, } impl ImportedFunc { @@ -139,7 +137,7 @@ pub struct ImportedTable { /// A pointer to the table definition. pub table: *mut LocalTable, /// A pointer to the vmcontext that owns this table definition. - pub vmctx: *mut VmCtx, + pub vmctx: *mut Ctx, } impl ImportedTable { @@ -156,9 +154,9 @@ impl ImportedTable { #[derive(Debug, Clone)] #[repr(C)] pub struct LocalMemory { - /// Pointer to the bottom of linear memory. + /// Pointer to the bottom of this linear memory. pub base: *mut u8, - /// Current logical size of this linear memory in bytes. + /// Current size of this linear memory in bytes. pub size: usize, } @@ -189,13 +187,19 @@ impl ImportedMemory { #[derive(Debug, Clone)] #[repr(C)] pub struct LocalGlobal { - pub data: [u8; 8], + pub data: u64, } impl LocalGlobal { pub fn offset_data() -> u8 { 0 * (mem::size_of::() as u8) } + + pub fn null() -> Self { + Self { + data: 0, + } + } } #[derive(Debug, Clone)] @@ -217,12 +221,12 @@ pub struct SigId(u32); /// Caller-checked anyfunc #[derive(Debug, Clone)] #[repr(C)] -pub struct CCAnyfunc { +pub struct Anyfunc { pub func_data: ImportedFunc, pub sig_id: SigId, } -impl CCAnyfunc { +impl Anyfunc { pub fn null() -> Self { Self { func_data: ImportedFunc { @@ -249,7 +253,7 @@ impl CCAnyfunc { #[cfg(test)] mod vm_offset_tests { use super::{ - VmCtx, + Ctx, ImportedFunc, LocalTable, ImportedTable, @@ -257,49 +261,49 @@ mod vm_offset_tests { ImportedMemory, LocalGlobal, ImportedGlobal, - CCAnyfunc, + Anyfunc, }; #[test] fn vmctx() { assert_eq!( - VmCtx::offset_memories() as usize, - offset_of!(VmCtx => memories).get_byte_offset(), + Ctx::offset_memories() as usize, + offset_of!(Ctx => memories).get_byte_offset(), ); assert_eq!( - VmCtx::offset_tables() as usize, - offset_of!(VmCtx => tables).get_byte_offset(), + Ctx::offset_tables() as usize, + offset_of!(Ctx => tables).get_byte_offset(), ); assert_eq!( - VmCtx::offset_globals() as usize, - offset_of!(VmCtx => globals).get_byte_offset(), + Ctx::offset_globals() as usize, + offset_of!(Ctx => globals).get_byte_offset(), ); assert_eq!( - VmCtx::offset_imported_memories() as usize, - offset_of!(VmCtx => imported_memories).get_byte_offset(), + Ctx::offset_imported_memories() as usize, + offset_of!(Ctx => imported_memories).get_byte_offset(), ); assert_eq!( - VmCtx::offset_imported_tables() as usize, - offset_of!(VmCtx => imported_tables).get_byte_offset(), + Ctx::offset_imported_tables() as usize, + offset_of!(Ctx => imported_tables).get_byte_offset(), ); assert_eq!( - VmCtx::offset_imported_globals() as usize, - offset_of!(VmCtx => imported_globals).get_byte_offset(), + Ctx::offset_imported_globals() as usize, + offset_of!(Ctx => imported_globals).get_byte_offset(), ); assert_eq!( - VmCtx::offset_imported_funcs() as usize, - offset_of!(VmCtx => imported_funcs).get_byte_offset(), + Ctx::offset_imported_funcs() as usize, + offset_of!(Ctx => imported_funcs).get_byte_offset(), ); assert_eq!( - VmCtx::offset_sig_ids() as usize, - offset_of!(VmCtx => sig_ids).get_byte_offset(), + Ctx::offset_sig_ids() as usize, + offset_of!(Ctx => sig_ids).get_byte_offset(), ); } @@ -382,18 +386,18 @@ mod vm_offset_tests { #[test] fn cc_anyfunc() { assert_eq!( - CCAnyfunc::offset_func() as usize, - offset_of!(CCAnyfunc => func_data: ImportedFunc => func).get_byte_offset(), + Anyfunc::offset_func() as usize, + offset_of!(Anyfunc => func_data: ImportedFunc => func).get_byte_offset(), ); assert_eq!( - CCAnyfunc::offset_vmctx() as usize, - offset_of!(CCAnyfunc => func_data: ImportedFunc => vmctx).get_byte_offset(), + Anyfunc::offset_vmctx() as usize, + offset_of!(Anyfunc => func_data: ImportedFunc => vmctx).get_byte_offset(), ); assert_eq!( - CCAnyfunc::offset_sig_id() as usize, - offset_of!(CCAnyfunc => sig_id).get_byte_offset(), + Anyfunc::offset_sig_id() as usize, + offset_of!(Anyfunc => sig_id).get_byte_offset(), ); } } \ No newline at end of file diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index ce22443b8..fee7d6577 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -30,7 +30,7 @@ use super::memory::LinearMemory; use super::module::{Export, ImportableExportable, Module}; use super::relocation::{Reloc, RelocSink, RelocationType}; use super::vm; -use super::backing::{Backing, ImportsBacking}; +use super::backing::{LocalBacking, ImportsBacking}; type TablesSlice = UncheckedSlice>; // TODO: this should be `type MemoriesSlice = UncheckedSlice>;`, but that crashes for some reason. @@ -96,13 +96,14 @@ pub enum InstanceABI { #[derive(Debug)] #[repr(C)] pub struct Instance { + pub vmctx: vm::Ctx, // C-like pointers to data (heaps, globals, tables) pub data_pointers: DataPointers, /// Webassembly functions finalized_funcs: Box<[*const vm::Func]>, - backing: Backing, + backing: LocalBacking, imports: ImportsBacking, @@ -113,6 +114,14 @@ pub struct Instance { pub emscripten_data: Option, } +impl Instance { + /// Shortcut for converting from a `vm::Ctx` pointer to a reference to the `Instance`. + /// This works because of the `vm::Ctx` is the first field of the `Instance`. + pub unsafe fn from_vmctx<'a>(ctx: *mut vm::Ctx) -> &'a mut Instance { + &mut *(ctx as *mut Instance) + } +} + /// Contains pointers to data (heaps, globals, tables) needed /// by Cranelift. /// NOTE: Rearranging the fields will break the memory arrangement model diff --git a/src/webassembly/table.rs b/src/webassembly/table.rs deleted file mode 100644 index f56cbab0b..000000000 --- a/src/webassembly/table.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::vm::{CCAnyfunc, LocalTable}; -use cranelift_wasm::{ - Table as ClifTable, - TableElementType, -}; - -pub struct TableScheme { - table: ClifTable, - info: TableInfo, -} - -impl TableScheme { - pub fn from_table(table: ClifTable) -> Self { - Self { - table, - info: match table.ty { - TableElementType::Func => TableInfo::CallerChecks, - TableElementType::Val(_) => unimplemented!(), - }, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum TableInfo { - CallerChecks, -} - -#[derive(Debug)] -pub struct TableBacking { - pub elements: Box<[CCAnyfunc]>, - pub max: Option, -} - -impl TableBacking { - pub fn new(scheme: &TableScheme) -> Self { - match (scheme.table.ty, scheme.info) { - (TableElementType::Func, TableInfo::CallerChecks) => { - TableBacking { - elements: vec![CCAnyfunc::null(); scheme.table.minimum as usize].into(), - max: scheme.table.maximum, - } - }, - (TableElementType::Val(_), _) => unimplemented!(), - } - } - - pub fn into_vm_table(&mut self) -> LocalTable { - LocalTable { - base: self.elements.as_mut_ptr() as *mut u8, - current_elements: self.elements.len(), - } - } -} \ No newline at end of file