diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 5 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory/lazy.rs | 178 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/mod.rs | 13 | ||||
| -rw-r--r-- | crates/tinywasm/tests/memory_backends.rs | 9 |
4 files changed, 84 insertions, 121 deletions
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index b509d0f..a8cf14a 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -174,7 +174,10 @@ impl ModuleInstance { addrs.funcs.extend(store.init_funcs(&module.funcs, idx)); addrs.tables.extend(store.init_tables(&module.table_types)?); match module.local_memory_allocation { - LocalMemoryAllocation::Skip => {} + LocalMemoryAllocation::Skip => { + #[cfg(feature = "guest-debug")] + addrs.memories.extend(store.init_memories(&module.memory_types, MemoryInstance::new_lazy)?); + } LocalMemoryAllocation::Lazy => { addrs.memories.extend(store.init_memories(&module.memory_types, MemoryInstance::new_lazy)?) } diff --git a/crates/tinywasm/src/store/memory/lazy.rs b/crates/tinywasm/src/store/memory/lazy.rs index 4b0fe56..2bcce09 100644 --- a/crates/tinywasm/src/store/memory/lazy.rs +++ b/crates/tinywasm/src/store/memory/lazy.rs @@ -1,6 +1,4 @@ use alloc::boxed::Box; -use alloc::vec::Vec; -use core::cell::RefCell; use core::hint::cold_path; use tinywasm_types::MemoryType; @@ -9,19 +7,19 @@ use crate::{Error, MemoryBackend, Result}; use super::LinearMemory; -/// A linear memory wrapper that materializes its inner backend on first access. +/// A linear memory wrapper that allocates its backend on the first mutation. /// -/// If the wrapped backend fails to create the inner memory during first access, -/// this wrapper will panic. +/// Before materialization, the memory is represented by its logical length and +/// reads return the zeroes required by WebAssembly semantics. pub struct LazyLinearMemory { ty: MemoryType, initial_len: usize, backend: MemoryBackend, - inner: RefCell<Option<Box<dyn LinearMemory>>>, + inner: Option<Box<dyn LinearMemory>>, } impl LazyLinearMemory { - /// Creates a lazy memory for `ty` using `backend` for the eventual materialized storage. + /// Creates a lazy memory for `ty` using `backend` for eventual storage. pub fn try_new(ty: MemoryType, backend: MemoryBackend) -> Result<Self> { let initial_len = usize::try_from(ty.initial_size()) .map_err(|_| Error::UnsupportedFeature("memory size exceeds the host address space"))?; @@ -29,147 +27,99 @@ impl LazyLinearMemory { } pub(crate) fn new_with_initial_len(ty: MemoryType, initial_len: usize, backend: MemoryBackend) -> Self { - Self { ty, initial_len, backend, inner: RefCell::new(None) } + Self { ty, initial_len, backend, inner: None } } - fn with_inner<R>(&self, f: impl FnOnce(&dyn LinearMemory) -> R) -> R { - self.ensure_materialized(); - let inner = self.inner.borrow(); - f(inner.as_deref().expect("lazy memory should be materialized")) - } - - fn try_with_inner<R>(&self, f: impl FnOnce(&dyn LinearMemory) -> R) -> core::result::Result<R, crate::Trap> { - self.try_ensure_materialized()?; - let inner = self.inner.borrow(); - Ok(f(inner.as_deref().expect("lazy memory should be materialized"))) - } - - fn with_inner_mut<R>(&self, f: impl FnOnce(&mut dyn LinearMemory) -> R) -> R { - self.ensure_materialized(); - let mut inner = self.inner.borrow_mut(); - f(inner.as_deref_mut().expect("lazy memory should be materialized")) - } - - fn try_with_inner_mut<R>( - &self, - f: impl FnOnce(&mut dyn LinearMemory) -> R, - ) -> core::result::Result<R, crate::Trap> { - self.try_ensure_materialized()?; - let mut inner = self.inner.borrow_mut(); - Ok(f(inner.as_deref_mut().expect("lazy memory should be materialized"))) - } - - fn ensure_materialized(&self) { - if self.inner.borrow().is_some() { - return; + fn materialize(&mut self) -> &mut dyn LinearMemory { + if self.inner.is_none() { + self.inner = + Some(self.backend.create(self.ty, self.initial_len).expect("lazy memory materialization failed").0); } - - let storage = self.backend.create(self.ty, self.initial_len).expect("lazy memory materialization failed"); - *self.inner.borrow_mut() = Some(storage.0); + self.inner.as_deref_mut().expect("lazy memory should be materialized") } - fn try_ensure_materialized(&self) -> core::result::Result<(), crate::Trap> { - if self.inner.borrow().is_some() { - return Ok(()); + fn try_materialize(&mut self) -> core::result::Result<&mut dyn LinearMemory, crate::Trap> { + if self.inner.is_none() { + let storage = match self.backend.create(self.ty, self.initial_len) { + Ok(storage) => storage, + Err(Error::Trap(trap)) => { + cold_path(); + return Err(trap); + } + Err(err) => panic!("lazy memory materialization failed: {err}"), + }; + self.inner = Some(storage.0); } - - let storage = match self.backend.create(self.ty, self.initial_len) { - Ok(storage) => storage, - Err(Error::Trap(trap)) => { - cold_path(); - return Err(trap); - } - Err(err) => panic!("lazy memory materialization failed: {err}"), - }; - *self.inner.borrow_mut() = Some(storage.0); - Ok(()) + Ok(self.inner.as_deref_mut().expect("lazy memory should be materialized")) } } impl LinearMemory for LazyLinearMemory { fn len(&self) -> usize { - self.with_inner(|inner| inner.len()) + self.inner.as_deref().map_or(self.initial_len, LinearMemory::len) } fn grow_to(&mut self, new_len: usize) -> Result<(), crate::Trap> { - self.try_with_inner_mut(|inner| inner.grow_to(new_len))? + self.try_materialize()?.grow_to(new_len) } fn read(&self, addr: usize, dst: &mut [u8]) -> usize { - self.with_inner(|inner| inner.read(addr, dst)) + if let Some(inner) = self.inner.as_deref() { + return inner.read(addr, dst); + } + if addr >= self.initial_len { + return 0; + } + let read_len = dst.len().min(self.initial_len - addr); + dst[..read_len].fill(0); + read_len } fn write(&mut self, addr: usize, src: &[u8]) -> usize { - self.with_inner_mut(|inner| inner.write(addr, src)) + if src.is_empty() || addr >= self.len() { + return 0; + } + self.materialize().write(addr, src) } fn write_all(&mut self, addr: usize, src: &[u8]) -> Option<()> { - self.with_inner_mut(|inner| inner.write_all(addr, src)) + let end = addr.checked_add(src.len())?; + if end > self.len() { + return None; + } + if src.is_empty() { + return Some(()); + } + self.materialize().write_all(addr, src) } fn fill(&mut self, addr: usize, len: usize, val: u8) -> Option<()> { - self.with_inner_mut(|inner| inner.fill(addr, len, val)) + let end = addr.checked_add(len)?; + if end > self.len() { + return None; + } + if len == 0 || val == 0 && self.inner.is_none() { + return Some(()); + } + self.materialize().fill(addr, len, val) } fn copy_within(&mut self, dst: usize, src: usize, len: usize) -> Option<()> { - self.with_inner_mut(|inner| inner.copy_within(dst, src, len)) - } - - fn read_exact(&self, addr: usize, dst: &mut [u8]) -> Option<()> { - self.with_inner(|inner| inner.read_exact(addr, dst)) - } - - fn read_vec(&self, addr: usize, len: usize) -> Option<Vec<u8>> { - self.with_inner(|inner| inner.read_vec(addr, len)) - } - - fn read_8(&self, base: u64, offset: u64) -> core::result::Result<u8, crate::Trap> { - self.try_with_inner(|inner| inner.read_8(base, offset))? - } - - fn read_16(&self, base: u64, offset: u64) -> core::result::Result<[u8; 2], crate::Trap> { - self.try_with_inner(|inner| inner.read_16(base, offset))? - } - - fn read_32(&self, base: u64, offset: u64) -> core::result::Result<[u8; 4], crate::Trap> { - self.try_with_inner(|inner| inner.read_32(base, offset))? - } - - fn read_64(&self, base: u64, offset: u64) -> core::result::Result<[u8; 8], crate::Trap> { - self.try_with_inner(|inner| inner.read_64(base, offset))? - } - - fn read_128(&self, base: u64, offset: u64) -> core::result::Result<[u8; 16], crate::Trap> { - self.try_with_inner(|inner| inner.read_128(base, offset))? - } - - fn write_8(&mut self, base: u64, offset: u64, byte: u8) -> core::result::Result<(), crate::Trap> { - self.try_with_inner_mut(|inner| inner.write_8(base, offset, byte))? - } - - fn write_16(&mut self, base: u64, offset: u64, bytes: [u8; 2]) -> core::result::Result<(), crate::Trap> { - self.try_with_inner_mut(|inner| inner.write_16(base, offset, bytes))? - } - - fn write_32(&mut self, base: u64, offset: u64, bytes: [u8; 4]) -> core::result::Result<(), crate::Trap> { - self.try_with_inner_mut(|inner| inner.write_32(base, offset, bytes))? - } - - fn write_64(&mut self, base: u64, offset: u64, bytes: [u8; 8]) -> core::result::Result<(), crate::Trap> { - self.try_with_inner_mut(|inner| inner.write_64(base, offset, bytes))? - } - - fn write_128(&mut self, base: u64, offset: u64, bytes: [u8; 16]) -> core::result::Result<(), crate::Trap> { - self.try_with_inner_mut(|inner| inner.write_128(base, offset, bytes))? + let src_end = src.checked_add(len)?; + let dst_end = dst.checked_add(len)?; + if src_end > self.len() || dst_end > self.len() { + return None; + } + if self.inner.is_none() || len == 0 || dst == src { + return Some(()); + } + self.materialize().copy_within(dst, src, len) } } #[cfg(feature = "debug")] impl core::fmt::Debug for LazyLinearMemory { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("LazyLinearMemory") - .field("ty", &self.ty) - .field("materialized", &self.inner.borrow().is_some()) - .finish() + f.debug_struct("LazyLinearMemory").field("ty", &self.ty).field("materialized", &self.inner.is_some()).finish() } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 2d34432..875980a 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -281,12 +281,17 @@ impl Store { pub(crate) fn init_memories( &mut self, memories: &[MemoryType], - init: fn(MemoryType, &MemoryBackend) -> Result<MemoryInstance>, + init: impl Fn(MemoryType, &MemoryBackend) -> Result<MemoryInstance>, ) -> Result<impl ExactSizeIterator<Item = MemAddr>> { let start = self.state.memories.len() as MemAddr; - self.state.memories.reserve_exact(memories.len()); - for &mem in memories { - self.state.memories.push(init(mem, &self.engine.config().memory_backend)?); + for mem in memories { + self.state.memories.push(match init(*mem, &self.engine.config().memory_backend) { + Ok(mem) => mem, + Err(e) => { + cold_path(); + return Err(e); + } + }); } Ok(start..start + memories.len() as MemAddr) } diff --git a/crates/tinywasm/tests/memory_backends.rs b/crates/tinywasm/tests/memory_backends.rs index b965c9f..c231bc1 100644 --- a/crates/tinywasm/tests/memory_backends.rs +++ b/crates/tinywasm/tests/memory_backends.rs @@ -129,8 +129,8 @@ fn exported_local_memory_is_not_eagerly_allocated() -> Result<()> { } #[test] -fn exported_local_memory_materializes_on_first_method_call() -> Result<()> { - let (store, instance, created) = instantiate_exported_memory_with_counting_backend( +fn exported_local_memory_reads_zeroes_without_materializing() -> Result<()> { + let (mut store, instance, created) = instantiate_exported_memory_with_counting_backend( r#" (module (memory (export "memory") 1) @@ -141,7 +141,12 @@ fn exported_local_memory_materializes_on_first_method_call() -> Result<()> { let memory = instance.memory("memory")?; assert_eq!(created.load(Ordering::Relaxed), 0); assert_eq!(memory.len(&store)?, 65536); + assert_eq!(memory.read_vec(&store, 65534, 2)?, &[0, 0]); + assert_eq!(created.load(Ordering::Relaxed), 0); + + memory.copy_from_slice(&mut store, 65534, &[1, 2])?; assert_eq!(created.load(Ordering::Relaxed), 1); + assert_eq!(memory.read_vec(&store, 65534, 2)?, &[1, 2]); Ok(()) } |
