Add importable memories and dynamic memories

This commit is contained in:
Lachlan Sneff
2019-01-25 15:28:54 -08:00
parent a20627964c
commit e4686e67c4
26 changed files with 992 additions and 694 deletions

View File

@ -0,0 +1,105 @@
use crate::{
memory::{WASM_MAX_PAGES, WASM_PAGE_SIZE},
sys,
types::MemoryDesc,
vm,
};
pub const DYNAMIC_GUARD_SIZE: usize = 4096;
pub struct DynamicMemory {
memory: sys::Memory,
current: u32,
max: Option<u32>,
}
impl DynamicMemory {
pub(super) fn new(desc: MemoryDesc, local: &mut vm::LocalMemory) -> Option<Box<Self>> {
let memory = {
let mut memory =
sys::Memory::with_size((desc.min as usize * WASM_PAGE_SIZE) + DYNAMIC_GUARD_SIZE)
.ok()?;
if desc.min != 0 {
unsafe {
memory
.protect(
0..(desc.min as usize * WASM_PAGE_SIZE),
sys::Protect::ReadWrite,
)
.ok()?;
}
}
memory
};
let mut storage = Box::new(DynamicMemory {
memory,
current: desc.min,
max: desc.max,
});
let storage_ptr: *mut DynamicMemory = &mut *storage;
local.base = storage.memory.as_ptr();
local.bound = desc.min as usize * WASM_PAGE_SIZE;
local.memory = storage_ptr as *mut ();
println!("local: {:?}", local);
Some(storage)
}
pub fn current(&self) -> u32 {
self.current
}
pub fn grow(&mut self, delta: u32, local: &mut vm::LocalMemory) -> Option<u32> {
if delta == 0 {
return Some(self.current);
}
let new_pages = self.current.checked_add(delta)?;
if let Some(max) = self.max {
if new_pages > max {
return None;
}
}
if new_pages as usize > WASM_MAX_PAGES {
return None;
}
let mut new_memory =
sys::Memory::with_size((new_pages as usize * WASM_PAGE_SIZE) + DYNAMIC_GUARD_SIZE)
.ok()?;
unsafe {
new_memory
.protect(
0..(new_pages as usize * WASM_PAGE_SIZE),
sys::Protect::ReadWrite,
)
.ok()?;
new_memory.as_slice_mut()[..self.current as usize * WASM_PAGE_SIZE]
.copy_from_slice(&self.memory.as_slice()[..self.current as usize * WASM_PAGE_SIZE]);
}
self.memory = new_memory; //The old memory gets dropped.
local.base = self.memory.as_ptr();
local.bound = new_pages as usize * WASM_PAGE_SIZE;
let old_pages = self.current;
self.current = new_pages;
Some(old_pages)
}
pub fn as_slice(&self) -> &[u8] {
unsafe { &self.memory.as_slice()[0..self.current as usize * WASM_PAGE_SIZE] }
}
pub fn as_slice_mut(&mut self) -> &mut [u8] {
unsafe { &mut self.memory.as_slice_mut()[0..self.current as usize * WASM_PAGE_SIZE] }
}
}

View File

@ -0,0 +1,156 @@
use crate::{
export::Export,
import::IsExport,
memory::dynamic::DYNAMIC_GUARD_SIZE,
memory::static_::{SAFE_STATIC_GUARD_SIZE, SAFE_STATIC_HEAP_SIZE},
types::MemoryDesc,
vm,
};
use std::{cell::UnsafeCell, fmt, ptr, rc::Rc};
pub use self::dynamic::DynamicMemory;
pub use self::static_::{SharedStaticMemory, StaticMemory};
mod dynamic;
mod static_;
pub const WASM_PAGE_SIZE: usize = 65_536;
pub const WASM_MAX_PAGES: usize = 65_536;
pub struct Memory {
desc: MemoryDesc,
storage: Rc<UnsafeCell<(MemoryStorage, Box<vm::LocalMemory>)>>,
}
impl Memory {
pub fn new(desc: MemoryDesc) -> Option<Self> {
let mut vm_local_memory = Box::new(vm::LocalMemory {
base: ptr::null_mut(),
bound: 0,
memory: ptr::null_mut(),
});
let memory_storage = match desc.memory_type() {
MemoryType::Dynamic => {
MemoryStorage::Dynamic(DynamicMemory::new(desc, &mut vm_local_memory)?)
}
MemoryType::Static => {
MemoryStorage::Static(StaticMemory::new(desc, &mut vm_local_memory)?)
}
MemoryType::SharedStatic => unimplemented!(),
};
Some(Memory {
desc,
storage: Rc::new(UnsafeCell::new((memory_storage, vm_local_memory))),
})
}
pub fn description(&self) -> MemoryDesc {
self.desc
}
pub fn grow(&mut self, delta: u32) -> Option<u32> {
match unsafe { &mut *self.storage.get() } {
(MemoryStorage::Dynamic(dynamic_memory), local) => dynamic_memory.grow(delta, local),
(MemoryStorage::Static(static_memory), local) => static_memory.grow(delta, local),
(MemoryStorage::SharedStatic(_), _) => unimplemented!(),
}
}
/// This returns the number of pages in the memory.
pub fn current_pages(&self) -> u32 {
match unsafe { &*self.storage.get() } {
(MemoryStorage::Dynamic(dynamic_memory), _) => dynamic_memory.current(),
(MemoryStorage::Static(static_memory), _) => static_memory.current(),
(MemoryStorage::SharedStatic(_), _) => unimplemented!(),
}
}
pub fn as_slice(&self) -> &[u8] {
match unsafe { &*self.storage.get() } {
(MemoryStorage::Dynamic(dynamic_memory), _) => dynamic_memory.as_slice(),
(MemoryStorage::Static(static_memory), _) => static_memory.as_slice(),
(MemoryStorage::SharedStatic(_), _) => panic!("cannot slice a shared memory"),
}
}
pub fn as_slice_mut(&mut self) -> &mut [u8] {
match unsafe { &mut *self.storage.get() } {
(MemoryStorage::Dynamic(dynamic_memory), _) => dynamic_memory.as_slice_mut(),
(MemoryStorage::Static(static_memory), _) => static_memory.as_slice_mut(),
(MemoryStorage::SharedStatic(_), _) => panic!("cannot slice a shared memory"),
}
}
pub(crate) fn vm_local_memory(&mut self) -> *mut vm::LocalMemory {
&mut *unsafe { &mut *self.storage.get() }.1
}
}
impl IsExport for Memory {
fn to_export(&mut self) -> Export {
Export::Memory(self.clone())
}
}
impl Clone for Memory {
fn clone(&self) -> Self {
Self {
desc: self.desc,
storage: Rc::clone(&self.storage),
}
}
}
pub enum MemoryStorage {
Dynamic(Box<DynamicMemory>),
Static(Box<StaticMemory>),
SharedStatic(Box<SharedStaticMemory>),
}
impl MemoryStorage {
pub fn to_type(&self) -> MemoryType {
match self {
MemoryStorage::Dynamic(_) => MemoryType::Dynamic,
MemoryStorage::Static(_) => MemoryType::Static,
MemoryStorage::SharedStatic(_) => MemoryType::SharedStatic,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryType {
Dynamic,
Static,
SharedStatic,
}
impl MemoryType {
#[doc(hidden)]
pub fn guard_size(self) -> u64 {
match self {
MemoryType::Dynamic => DYNAMIC_GUARD_SIZE as u64,
MemoryType::Static => SAFE_STATIC_GUARD_SIZE as u64,
MemoryType::SharedStatic => SAFE_STATIC_GUARD_SIZE as u64,
}
}
#[doc(hidden)]
pub fn bounds(self) -> Option<u64> {
match self {
MemoryType::Dynamic => None,
MemoryType::Static => Some(SAFE_STATIC_HEAP_SIZE as u64),
MemoryType::SharedStatic => Some(SAFE_STATIC_HEAP_SIZE as u64),
}
}
}
impl fmt::Debug for Memory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Memory")
.field("desc", &self.desc)
.field("size", &(self.current_pages() as usize * WASM_PAGE_SIZE))
.finish()
}
}

View File

@ -0,0 +1,10 @@
#[doc(hidden)]
pub const SAFE_STATIC_HEAP_SIZE: usize = 1 << 32; // 4 GiB
#[doc(hidden)]
pub const SAFE_STATIC_GUARD_SIZE: usize = 1 << 31; // 2 GiB
mod shared;
mod unshared;
pub use self::shared::SharedStaticMemory;
pub use self::unshared::StaticMemory;

View File

@ -0,0 +1,11 @@
use crate::sys;
use parking_lot::Mutex;
use std::sync::atomic::AtomicUsize;
// Remove this attribute once this is used.
#[allow(dead_code)]
pub struct SharedStaticMemory {
memory: sys::Memory,
current: AtomicUsize,
lock: Mutex<()>,
}

View File

@ -0,0 +1,99 @@
use crate::{
memory::{
static_::{SAFE_STATIC_GUARD_SIZE, SAFE_STATIC_HEAP_SIZE},
WASM_MAX_PAGES, WASM_PAGE_SIZE,
},
sys,
types::MemoryDesc,
vm,
};
pub struct StaticMemory {
memory: sys::Memory,
current: u32,
max: Option<u32>,
}
impl StaticMemory {
pub(in crate::memory) fn new(
desc: MemoryDesc,
local: &mut vm::LocalMemory,
) -> Option<Box<Self>> {
let memory = {
let mut memory =
sys::Memory::with_size(SAFE_STATIC_HEAP_SIZE + SAFE_STATIC_GUARD_SIZE).ok()?;
if desc.min != 0 {
unsafe {
memory
.protect(
0..(desc.min as usize * WASM_PAGE_SIZE),
sys::Protect::ReadWrite,
)
.ok()?;
}
}
memory
};
let mut storage = Box::new(StaticMemory {
memory,
current: desc.min,
max: desc.max,
});
let storage_ptr: *mut StaticMemory = &mut *storage;
local.base = storage.memory.as_ptr();
local.bound = desc.min as usize * WASM_PAGE_SIZE;
local.memory = storage_ptr as *mut ();
Some(storage)
}
pub fn current(&self) -> u32 {
self.current
}
pub fn grow(&mut self, delta: u32, local: &mut vm::LocalMemory) -> Option<u32> {
if delta == 0 {
return Some(self.current);
}
let new_pages = self.current.checked_add(delta)?;
if let Some(max) = self.max {
if new_pages > max {
return None;
}
}
if new_pages as usize > WASM_MAX_PAGES {
return None;
}
unsafe {
self.memory
.protect(
self.current as usize * WASM_PAGE_SIZE..new_pages as usize * WASM_PAGE_SIZE,
sys::Protect::ReadWrite,
)
.ok()?;
}
local.bound = new_pages as usize * WASM_PAGE_SIZE;
let old_pages = self.current;
self.current = new_pages;
Some(old_pages)
}
pub fn as_slice(&self) -> &[u8] {
unsafe { &self.memory.as_slice()[0..self.current as usize * WASM_PAGE_SIZE] }
}
pub fn as_slice_mut(&mut self) -> &mut [u8] {
unsafe { &mut self.memory.as_slice_mut()[0..self.current as usize * WASM_PAGE_SIZE] }
}
}