diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-06-30 22:19:25 +0200 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-06-30 22:19:25 +0200 |
| commit | 4425733bdfb516ca2530e6199c2c95ece91b627d (patch) | |
| tree | 2b50626d449c5b4f2295ac90f93d28df1600a901 | |
| parent | 9c85fa9c3a2066ce851af53d7019e6a25a45af3b (diff) | |
chore: Memory and Data Instances are no longer reference counted
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
| -rw-r--r-- | .cargo/config.toml | 5 | ||||
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | crates/tinywasm/benches/fibonacci.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/imports.rs | 13 | ||||
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 20 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/executor.rs | 130 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/call_stack.rs | 33 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/value_stack.rs | 46 | ||||
| -rw-r--r-- | crates/tinywasm/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/reference.rs | 5 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory.rs | 18 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/mod.rs | 114 | ||||
| -rw-r--r-- | examples/wasm-rust.rs | 22 |
13 files changed, 266 insertions, 145 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml index af2bffe..24cd2f4 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,8 +5,3 @@ test-mvp="test --package tinywasm --test test-mvp --release -- --enable " test-2="test --package tinywasm --test test-two --release -- --enable " test-wast="test --package tinywasm --test test-wast -- --enable " test-wast-release="test --package tinywasm --test test-wast --release -- --enable " - -# enable for linux perf -[target.x86_64-unknown-linux-gnu] -linker="/usr/bin/clang" -rustflags=["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"] diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f6e985..c88a55e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use a seperate stack and locals for 32, 64 and 128 bit values and references (#21) - Updated to latest `wasmparser` version - Removed benchmarks comparing TinyWasm to other WebAssembly runtimes to reduce build dependencies +- Memory and Data Instances are no longer reference counted ## [0.7.0] - 2024-05-15 diff --git a/crates/tinywasm/benches/fibonacci.rs b/crates/tinywasm/benches/fibonacci.rs index e5c7184..973423c 100644 --- a/crates/tinywasm/benches/fibonacci.rs +++ b/crates/tinywasm/benches/fibonacci.rs @@ -43,7 +43,7 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function("fibonacci_to_twasm", |b| b.iter(|| fibonacci_to_twasm(module.clone()))); c.bench_function("fibonacci_from_twasm", |b| b.iter(|| fibonacci_from_twasm(twasm.clone()))); c.bench_function("fibonacci_iterative_60", |b| b.iter(|| fibonacci_run(module.clone(), false, 60))); - c.bench_function("fibonacci_recursive_60", |b| b.iter(|| fibonacci_run(module.clone(), true, 60))); + c.bench_function("fibonacci_recursive_26", |b| b.iter(|| fibonacci_run(module.clone(), true, 26))); } criterion_group!(benches, criterion_benchmark); diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index c6cd0c2..82ae8c8 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -6,7 +6,7 @@ use alloc::vec::Vec; use core::fmt::Debug; use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple}; -use crate::{log, LinkingError, Result}; +use crate::{log, LinkingError, MemoryRef, MemoryRefMut, Result}; use tinywasm_types::*; /// The internal representation of a function @@ -72,12 +72,12 @@ impl FuncContext<'_> { } /// Get a reference to an exported memory - pub fn exported_memory(&mut self, name: &str) -> Result<crate::MemoryRef<'_>> { + pub fn exported_memory(&mut self, name: &str) -> Result<MemoryRef<'_>> { self.module().exported_memory(self.store, name) } /// Get a reference to an exported memory - pub fn exported_memory_mut(&mut self, name: &str) -> Result<crate::MemoryRefMut<'_>> { + pub fn exported_memory_mut(&mut self, name: &str) -> Result<MemoryRefMut<'_>> { self.module().exported_memory_mut(self.store, name) } } @@ -394,15 +394,12 @@ impl Imports { } (ExternVal::Table(table_addr), ImportKind::Table(ty)) => { let table = store.get_table(table_addr)?; - Self::compare_table_types(import, &table.borrow().kind, ty)?; + Self::compare_table_types(import, &table.kind, ty)?; imports.tables.push(table_addr); } (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => { let mem = store.get_mem(memory_addr)?; - let (size, kind) = { - let mem = mem.borrow(); - (mem.page_count(), mem.kind) - }; + let (size, kind) = { (mem.page_count(), mem.kind) }; Self::compare_memory_types(import, &kind, ty, Some(size))?; imports.memories.push(memory_addr); } diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 7250cf0..74a1598 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -133,37 +133,37 @@ impl ModuleInstance { } // resolve a function address to the global store address - #[inline(always)] + #[inline] pub(crate) fn resolve_func_addr(&self, addr: FuncAddr) -> Result<FuncAddr> { self.0.func_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("function")).copied() } // resolve a table address to the global store address - #[inline(always)] + #[inline] pub(crate) fn resolve_table_addr(&self, addr: TableAddr) -> Result<TableAddr> { self.0.table_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("table")).copied() } // resolve a memory address to the global store address - #[inline(always)] + #[inline] pub(crate) fn resolve_mem_addr(&self, addr: MemAddr) -> Result<MemAddr> { self.0.mem_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("mem")).copied() } // resolve a data address to the global store address - #[inline(always)] + #[inline] pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> Result<DataAddr> { self.0.data_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("data")).copied() } // resolve a memory address to the global store address - #[inline(always)] + #[inline] pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> Result<ElemAddr> { self.0.elem_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("elem")).copied() } // resolve a global address to the global store address - #[inline(always)] + #[inline] pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> Result<GlobalAddr> { self.0.global_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("global")).copied() } @@ -216,15 +216,15 @@ impl ModuleInstance { } /// Get a memory by address - pub fn memory<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result<MemoryRef<'a>> { + pub fn memory<'a>(&self, store: &'a Store, addr: MemAddr) -> Result<MemoryRef<'a>> { let mem = store.get_mem(self.resolve_mem_addr(addr)?)?; - Ok(MemoryRef(mem.borrow())) + Ok(MemoryRef(mem)) } /// Get a memory by address (mutable) pub fn memory_mut<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result<MemoryRefMut<'a>> { - let mem = store.get_mem(self.resolve_mem_addr(addr)?)?; - Ok(MemoryRefMut(mem.borrow_mut())) + let mem = store.get_mem_mut(self.resolve_mem_addr(addr)?)?; + Ok(MemoryRefMut(mem)) } /// Get the start function of the module diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 7f724f3..353f778 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -86,18 +86,10 @@ impl<'store, 'stack> Executor<'store, 'stack> { Return => return self.exec_return(), EndBlockFrame => self.exec_end_block()?, - LocalGet32(local_index) => { - self.cf.locals.get::<Value32>(*local_index).map(|v| self.stack.values.push(v))? - } - LocalGet64(local_index) => { - self.cf.locals.get::<Value64>(*local_index).map(|v| self.stack.values.push(v))? - } - LocalGet128(local_index) => { - self.cf.locals.get::<Value128>(*local_index).map(|v| self.stack.values.push(v))? - } - LocalGetRef(local_index) => { - self.cf.locals.get::<ValueRef>(*local_index).map(|v| self.stack.values.push(v))? - } + LocalGet32(local_index) => self.exec_local_get::<Value32>(*local_index)?, + LocalGet64(local_index) => self.exec_local_get::<Value64>(*local_index)?, + LocalGet128(local_index) => self.exec_local_get::<Value128>(*local_index)?, + LocalGetRef(local_index) => self.exec_local_get::<ValueRef>(*local_index)?, LocalSet32(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value32>()?)?, LocalSet64(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value64>()?)?, @@ -414,9 +406,15 @@ impl<'store, 'stack> Executor<'store, 'stack> { } fn exec_call(&mut self, wasm_func: Rc<WasmFunction>, owner: ModuleInstanceAddr) -> Result<ControlFlow<()>> { - let params = self.stack.values.pop_many_raw(&wasm_func.ty.params)?; - let new_call_frame = - CallFrame::new_raw(wasm_func, owner, params.into_iter().rev(), self.stack.blocks.len() as u32); + let locals = match self.stack.values.pop_locals(&wasm_func.ty.params, wasm_func.locals) { + Ok(locals) => locals, + Err(e) => { + cold(); + return Err(e); + } + }; + + let new_call_frame = CallFrame::new_raw(wasm_func, owner, locals, self.stack.blocks.len() as u32); self.cf.incr_instr_ptr(); // skip the call instruction self.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?; self.module.swap_with(self.cf.module_addr(), self.store); @@ -443,7 +441,6 @@ impl<'store, 'stack> Executor<'store, 'stack> { let func_ref = { let table = self.store.get_table(self.module.resolve_table_addr(table_addr)?)?; let table_idx: u32 = self.stack.values.pop::<i32>()? as u32; - let table = table.borrow(); assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref"); table .get(table_idx) @@ -586,6 +583,11 @@ impl<'store, 'stack> Executor<'store, 'stack> { self.stack.values.truncate_keep(&block.stack_ptr, &block.results); Ok(()) } + fn exec_local_get<T: InternalValue>(&mut self, local_index: u16) -> Result<()> { + let v = self.cf.locals.get::<T>(local_index)?; + self.stack.values.push(v); + Ok(()) + } fn exec_global_get(&mut self, global_index: u32) -> Result<()> { self.stack.values.push_dyn(self.store.get_global_val(self.module.resolve_global_addr(global_index)?)?); @@ -605,11 +607,11 @@ impl<'store, 'stack> Executor<'store, 'stack> { fn exec_memory_size(&mut self, addr: u32) -> Result<()> { let mem = self.store.get_mem(self.module.resolve_mem_addr(addr)?)?; - self.stack.values.push::<i32>(mem.borrow().page_count() as i32); + self.stack.values.push::<i32>(mem.page_count() as i32); Ok(()) } fn exec_memory_grow(&mut self, addr: u32) -> Result<()> { - let mut mem = self.store.get_mem(self.module.resolve_mem_addr(addr)?)?.borrow_mut(); + let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(addr)?)?; let prev_size = mem.page_count() as i32; let pages_delta = self.stack.values.pop::<i32>()?; self.stack.values.push::<i32>(match mem.grow(pages_delta) { @@ -625,13 +627,13 @@ impl<'store, 'stack> Executor<'store, 'stack> { let dst = self.stack.values.pop::<i32>()?; if from == to { - let mut mem_from = self.store.get_mem(self.module.resolve_mem_addr(from)?)?.borrow_mut(); + let mem_from = self.store.get_mem_mut(self.module.resolve_mem_addr(from)?)?; // copy within the same memory mem_from.copy_within(dst as usize, src as usize, size as usize)?; } else { // copy between two memories - let mem_from = self.store.get_mem(self.module.resolve_mem_addr(from)?)?.borrow(); - let mut mem_to = self.store.get_mem(self.module.resolve_mem_addr(to)?)?.borrow_mut(); + let (mem_from, mem_to) = + self.store.get_mems_mut(self.module.resolve_mem_addr(from)?, self.module.resolve_mem_addr(to)?)?; mem_to.copy_from_slice(dst as usize, mem_from.load(src as usize, size as usize)?)?; } Ok(()) @@ -641,8 +643,8 @@ impl<'store, 'stack> Executor<'store, 'stack> { let val = self.stack.values.pop::<i32>()?; let dst = self.stack.values.pop::<i32>()?; - let mem = self.store.get_mem(self.module.resolve_mem_addr(addr)?)?; - mem.borrow_mut().fill(dst as usize, size as usize, val as u8)?; + let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(addr)?)?; + mem.fill(dst as usize, size as usize, val as u8)?; Ok(()) } fn exec_memory_init(&mut self, data_index: u32, mem_index: u32) -> Result<()> { @@ -650,12 +652,23 @@ impl<'store, 'stack> Executor<'store, 'stack> { let offset = self.stack.values.pop::<i32>()?; // s let dst = self.stack.values.pop::<i32>()?; // d - let data = self.store.get_data(self.module.resolve_data_addr(data_index)?)?; - let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_index)?)?; + let data = self + .store + .data + .datas + .get(self.module.resolve_data_addr(data_index)? as usize) + .ok_or_else(|| Error::Other("data not found".to_string()))?; + + let mem = self + .store + .data + .memories + .get_mut(self.module.resolve_mem_addr(mem_index)? as usize) + .ok_or_else(|| Error::Other("memory not found".to_string()))?; let data_len = data.data.as_ref().map(|d| d.len()).unwrap_or(0); - if unlikely(((size + offset) as usize > data_len) || ((dst + size) as usize > mem.borrow().len())) { + if unlikely(((size + offset) as usize > data_len) || ((dst + size) as usize > mem.len())) { return Err(Trap::MemoryOutOfBounds { offset: offset as usize, len: size as usize, max: data_len }.into()); } @@ -668,7 +681,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()), }; - mem.borrow_mut().store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)])?; + mem.store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)])?; Ok(()) } fn exec_data_drop(&mut self, data_index: u32) -> Result<()> { @@ -683,13 +696,17 @@ impl<'store, 'stack> Executor<'store, 'stack> { let dst: i32 = self.stack.values.pop::<i32>()?; if from == to { - let mut table_from = self.store.get_table(self.module.resolve_table_addr(from)?)?.borrow_mut(); // copy within the same memory - table_from.copy_within(dst as usize, src as usize, size as usize)?; + self.store.get_table_mut(self.module.resolve_table_addr(from)?)?.copy_within( + dst as usize, + src as usize, + size as usize, + )?; } else { // copy between two memories - let table_from = self.store.get_table(self.module.resolve_table_addr(from)?)?.borrow(); - let mut table_to = self.store.get_table(self.module.resolve_table_addr(to)?)?.borrow_mut(); + let (table_from, table_to) = self + .store + .get_tables_mut(self.module.resolve_table_addr(from)?, self.module.resolve_table_addr(to)?)?; table_to.copy_from_slice(dst as usize, table_from.load(src as usize, size as usize)?)?; } Ok(()) @@ -702,16 +719,17 @@ impl<'store, 'stack> Executor<'store, 'stack> { offset: u64, ) -> Result<()> { let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr)?)?; + let val = self.stack.values.pop::<i32>()? as u64; let Some(Ok(addr)) = offset.checked_add(val).map(|a| a.try_into()) else { cold(); return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: offset as usize, len: LOAD_SIZE, - max: mem.borrow().max_pages(), + max: mem.max_pages(), })); }; - let val = mem.borrow().load_as::<LOAD_SIZE, LOAD>(addr)?; + let val = mem.load_as::<LOAD_SIZE, LOAD>(addr)?; self.stack.values.push(cast(val)); Ok(()) } @@ -721,37 +739,49 @@ impl<'store, 'stack> Executor<'store, 'stack> { mem_addr: tinywasm_types::MemAddr, offset: u64, ) -> Result<()> { - let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr)?)?; + let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(mem_addr)?)?; let val = val.to_mem_bytes(); let addr = self.stack.values.pop::<i32>()? as u64; - mem.borrow_mut().store((offset + addr) as usize, val.len(), &val)?; + mem.store((offset + addr) as usize, val.len(), &val)?; Ok(()) } fn exec_table_get(&mut self, table_index: u32) -> Result<()> { let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; let idx: i32 = self.stack.values.pop::<i32>()?; - let v = table.borrow().get_wasm_val(idx as u32)?; + let v = table.get_wasm_val(idx as u32)?; self.stack.values.push_dyn(v.into()); Ok(()) } fn exec_table_set(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; + let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)?)?; let val = self.stack.values.pop::<ValueRef>()?; let idx = self.stack.values.pop::<i32>()? as u32; - table.borrow_mut().set(idx, val.into())?; + table.set(idx, val.into())?; Ok(()) } fn exec_table_size(&mut self, table_index: u32) -> Result<()> { let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; - self.stack.values.push_dyn(table.borrow().size().into()); + self.stack.values.push_dyn(table.size().into()); Ok(()) } fn exec_table_init(&mut self, elem_index: u32, table_index: u32) -> Result<()> { - let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; - let table_len = table.borrow().size(); - let elem = self.store.get_elem(self.module.resolve_elem_addr(elem_index)?)?; + let elem = self + .store + .data + .elements + .get(self.module.resolve_elem_addr(elem_index)? as usize) + .ok_or_else(|| Error::Other("element not found".to_string()))?; + + let table = self + .store + .data + .tables + .get_mut(self.module.resolve_table_addr(table_index)? as usize) + .ok_or_else(|| Error::Other("table not found".to_string()))?; + let elem_len = elem.items.as_ref().map(|items| items.len()).unwrap_or(0); + let table_len = table.size(); let size: i32 = self.stack.values.pop::<i32>()?; // n let offset: i32 = self.stack.values.pop::<i32>()?; // s @@ -773,17 +803,17 @@ impl<'store, 'stack> Executor<'store, 'stack> { return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into()); }; - table.borrow_mut().init(self.module.func_addrs(), dst, &items[offset as usize..(offset + size) as usize])?; + table.init(self.module.func_addrs(), dst, &items[offset as usize..(offset + size) as usize])?; Ok(()) } fn exec_table_grow(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; - let sz = table.borrow().size(); + let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)?)?; + let sz = table.size(); let n = self.stack.values.pop::<i32>()?; let val = self.stack.values.pop::<ValueRef>()?; - match table.borrow_mut().grow(n, val.into()) { + match table.grow(n, val.into()) { Ok(_) => self.stack.values.push_dyn(sz.into()), Err(_) => self.stack.values.push_dyn((-1_i32).into()), } @@ -791,17 +821,17 @@ impl<'store, 'stack> Executor<'store, 'stack> { Ok(()) } fn exec_table_fill(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; + let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)?)?; let n = self.stack.values.pop::<i32>()?; let val = self.stack.values.pop::<ValueRef>()?; let i = self.stack.values.pop::<i32>()?; - if unlikely(i + n > table.borrow().size()) { + if unlikely(i + n > table.size()) { return Err(Error::Trap(Trap::TableOutOfBounds { offset: i as usize, len: n as usize, - max: table.borrow().size() as usize, + max: table.size() as usize, })); } @@ -809,7 +839,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { return Ok(()); } - table.borrow_mut().fill(self.module.func_addrs(), i as usize, n as usize, val.into())?; + table.fill(self.module.func_addrs(), i as usize, n as usize, val.into())?; Ok(()) } } diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs index 18eac4f..a7c6a74 100644 --- a/crates/tinywasm/src/interpreter/stack/call_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs @@ -53,6 +53,8 @@ pub(crate) struct Locals { } impl Locals { + // TODO: locals get_set + pub(crate) fn get<T: InternalValue>(&self, local_index: LocalAddr) -> Result<T> { T::local_get(self, local_index) } @@ -90,10 +92,7 @@ impl CallFrame { #[inline(always)] pub(crate) fn fetch_instr(&self) -> &Instruction { - match self.func_instance.instructions.get(self.instr_ptr) { - Some(instr) => instr, - None => unreachable!("Instruction pointer out of bounds"), - } + &self.func_instance.instructions[self.instr_ptr] } /// Break to a block at the given index (relative to the current frame) @@ -148,24 +147,18 @@ impl CallFrame { params: &[WasmValue], block_ptr: u32, ) -> Self { - Self::new_raw(wasm_func_inst, owner, params.iter().map(|v| v.into()), block_ptr) - } - - #[inline(always)] - pub(crate) fn new_raw( - wasm_func_inst: Rc<WasmFunction>, - owner: ModuleInstanceAddr, - params: impl ExactSizeIterator<Item = TinyWasmValue>, - block_ptr: u32, - ) -> Self { let locals = { let mut locals_32 = Vec::new(); + locals_32.reserve_exact(wasm_func_inst.locals.local_32 as usize); let mut locals_64 = Vec::new(); + locals_64.reserve_exact(wasm_func_inst.locals.local_64 as usize); let mut locals_128 = Vec::new(); + locals_128.reserve_exact(wasm_func_inst.locals.local_128 as usize); let mut locals_ref = Vec::new(); + locals_ref.reserve_exact(wasm_func_inst.locals.local_ref as usize); for p in params { - match p { + match p.into() { TinyWasmValue::Value32(v) => locals_32.push(v), TinyWasmValue::Value64(v) => locals_64.push(v), TinyWasmValue::Value128(v) => locals_128.push(v), @@ -189,6 +182,16 @@ impl CallFrame { Self { instr_ptr: 0, func_instance: wasm_func_inst, module_addr: owner, block_ptr, locals } } + #[inline] + pub(crate) fn new_raw( + wasm_func_inst: Rc<WasmFunction>, + owner: ModuleInstanceAddr, + locals: Locals, + block_ptr: u32, + ) -> Self { + Self { instr_ptr: 0, func_instance: wasm_func_inst, module_addr: owner, block_ptr, locals } + } + #[inline(always)] pub(crate) fn instructions(&self) -> &[Instruction] { &self.func_instance.instructions diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index 525d061..02d35c1 100644 --- a/crates/tinywasm/src/interpreter/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -1,7 +1,9 @@ use alloc::vec::Vec; -use tinywasm_types::{ValType, WasmValue}; +use tinywasm_types::{LocalCounts, ValType, WasmValue}; use crate::{interpreter::values::*, Result}; + +use super::Locals; pub(crate) const STACK_32_SIZE: usize = 1024 * 128; pub(crate) const STACK_64_SIZE: usize = 1024 * 128; pub(crate) const STACK_128_SIZE: usize = 1024 * 128; @@ -34,14 +36,17 @@ impl ValueStack { } } + #[inline] pub(crate) fn peek<T: InternalValue>(&self) -> Result<T> { T::stack_peek(self) } + #[inline] pub(crate) fn pop<T: InternalValue>(&mut self) -> Result<T> { T::stack_pop(self) } + #[inline] pub(crate) fn push<T: InternalValue>(&mut self, value: T) { T::stack_push(self, value) } @@ -62,7 +67,6 @@ impl ValueStack { } // TODO: this needs to re-introduce the top replacement optimization - #[inline(always)] pub(crate) fn calculate<T: InternalValue, U: InternalValue>(&mut self, func: fn(T, T) -> Result<U>) -> Result<()> { let v2 = T::stack_pop(self)?; let v1 = T::stack_pop(self)?; @@ -100,12 +104,40 @@ impl ValueStack { }) } - pub(crate) fn pop_many_raw(&mut self, val_types: &[ValType]) -> Result<Vec<TinyWasmValue>> { - let mut values = Vec::with_capacity(val_types.len()); - for val_type in val_types.iter() { - values.push(self.pop_dyn(*val_type)?); + // TODO: a lot of optimization potential here + pub(crate) fn pop_locals(&mut self, val_types: &[ValType], lc: LocalCounts) -> Result<Locals> { + let mut locals_32 = Vec::new(); + locals_32.reserve_exact(lc.local_32 as usize); + let mut locals_64 = Vec::new(); + locals_64.reserve_exact(lc.local_64 as usize); + let mut locals_128 = Vec::new(); + locals_128.reserve_exact(lc.local_128 as usize); + let mut locals_ref = Vec::new(); + locals_ref.reserve_exact(lc.local_ref as usize); + + for ty in val_types { + match self.pop_dyn(*ty)? { + TinyWasmValue::Value32(v) => locals_32.push(v), + TinyWasmValue::Value64(v) => locals_64.push(v), + TinyWasmValue::Value128(v) => locals_128.push(v), + TinyWasmValue::ValueRef(v) => locals_ref.push(v), + } } - Ok(values) + locals_32.reverse(); + locals_32.resize_with(lc.local_32 as usize, Default::default); + locals_64.reverse(); + locals_64.resize_with(lc.local_64 as usize, Default::default); + locals_128.reverse(); + locals_128.resize_with(lc.local_128 as usize, Default::default); + locals_ref.reverse(); + locals_ref.resize_with(lc.local_ref as usize, Default::default); + + Ok(Locals { + locals_32: locals_32.into_boxed_slice(), + locals_64: locals_64.into_boxed_slice(), + locals_128: locals_128.into_boxed_slice(), + locals_ref: locals_ref.into_boxed_slice(), + }) } pub(crate) fn truncate_keep(&mut self, to: &StackLocation, keep: &StackHeight) { diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index cf6f585..1bb257d 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -5,7 +5,7 @@ ))] #![allow(unexpected_cfgs, clippy::reserve_after_initialization)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -#![cfg_attr(feature = "nightly", feature(error_in_core, portable_simd))] +#![cfg_attr(feature = "nightly", feature(portable_simd))] #![forbid(unsafe_code)] //! A tiny WebAssembly Runtime written in Rust diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index 870de48..60ed61a 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -1,4 +1,3 @@ -use core::cell::{Ref, RefMut}; use core::ffi::CStr; use alloc::ffi::CString; @@ -11,11 +10,11 @@ use crate::{MemoryInstance, Result}; /// A reference to a memory instance #[derive(Debug)] -pub struct MemoryRef<'a>(pub(crate) Ref<'a, MemoryInstance>); +pub struct MemoryRef<'a>(pub(crate) &'a MemoryInstance); /// A borrowed reference to a memory instance #[derive(Debug)] -pub struct MemoryRefMut<'a>(pub(crate) RefMut<'a, MemoryInstance>); +pub struct MemoryRefMut<'a>(pub(crate) &'a mut MemoryInstance); impl<'a> MemoryRefLoad for MemoryRef<'a> { /// Load a slice of memory diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 9d6cdaf..eb2bccf 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -2,7 +2,7 @@ use alloc::vec; use alloc::vec::Vec; use tinywasm_types::{MemoryType, ModuleInstanceAddr}; -use crate::{log, Error, Result}; +use crate::{cold, log, Error, Result}; const PAGE_SIZE: usize = 65536; const MAX_PAGES: usize = 65536; @@ -45,13 +45,14 @@ impl MemoryInstance { pub(crate) fn store(&mut self, addr: usize, len: usize, data: &[u8]) -> Result<()> { let Some(end) = addr.checked_add(len) else { + cold(); return Err(self.trap_oob(addr, data.len())); }; if end > self.data.len() || end < addr { + cold(); return Err(self.trap_oob(addr, data.len())); } - self.data[addr..end].copy_from_slice(data); Ok(()) } @@ -62,10 +63,12 @@ impl MemoryInstance { pub(crate) fn load(&self, addr: usize, len: usize) -> Result<&[u8]> { let Some(end) = addr.checked_add(len) else { + cold(); return Err(self.trap_oob(addr, len)); }; if end > self.data.len() || end < addr { + cold(); return Err(self.trap_oob(addr, len)); } @@ -75,16 +78,25 @@ impl MemoryInstance { // this is a workaround since we can't use generic const expressions yet (https://github.com/rust-lang/rust/issues/76560) pub(crate) fn load_as<const SIZE: usize, T: MemLoadable<SIZE>>(&self, addr: usize) -> Result<T> { let Some(end) = addr.checked_add(SIZE) else { + cold(); return Err(self.trap_oob(addr, SIZE)); }; if end > self.data.len() { + cold(); return Err(self.trap_oob(addr, SIZE)); } Ok(T::from_le_bytes(match self.data[addr..end].try_into() { Ok(bytes) => bytes, - Err(_) => unreachable!("checked bounds above"), + Err(_) => { + cold(); + return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { + offset: addr, + len: SIZE, + max: self.data.len(), + })); + } })) } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index b427497..7fc8460 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -1,10 +1,9 @@ use alloc::{boxed::Box, format, string::ToString, vec::Vec}; -use core::cell::RefCell; use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; use crate::interpreter::{self, InterpreterRuntime, TinyWasmValue}; -use crate::{Error, Function, ModuleInstance, Result, Trap}; +use crate::{cold, Error, Function, ModuleInstance, Result, Trap}; mod data; mod element; @@ -84,8 +83,8 @@ impl Default for Store { /// See <https://webassembly.github.io/spec/core/exec/runtime.html#store> pub(crate) struct StoreData { pub(crate) funcs: Vec<FunctionInstance>, - pub(crate) tables: Vec<RefCell<TableInstance>>, - pub(crate) memories: Vec<RefCell<MemoryInstance>>, + pub(crate) tables: Vec<TableInstance>, + pub(crate) memories: Vec<MemoryInstance>, pub(crate) globals: Vec<GlobalInstance>, pub(crate) elements: Vec<ElementInstance>, pub(crate) datas: Vec<DataInstance>, @@ -112,49 +111,93 @@ impl Store { } /// Get the function at the actual index in the store - #[inline(always)] + #[inline] pub(crate) fn get_func(&self, addr: FuncAddr) -> Result<&FunctionInstance> { self.data.funcs.get(addr as usize).ok_or_else(|| Self::not_found_error("function")) } /// Get the memory at the actual index in the store - #[inline(always)] - pub(crate) fn get_mem(&self, addr: MemAddr) -> Result<&RefCell<MemoryInstance>> { - self.data.memories.get(addr as usize).ok_or_else(|| Self::not_found_error("memory")) + #[inline] + pub(crate) fn get_mem(&self, addr: MemAddr) -> Result<&MemoryInstance> { + match self.data.memories.get(addr as usize) { + Some(mem) => Ok(mem), + None => { + cold(); + Err(Self::not_found_error("memory")) + } + } + } + + /// Get the memory at the actual index in the store + #[inline] + pub(crate) fn get_mem_mut(&mut self, addr: MemAddr) -> Result<&mut MemoryInstance> { + match self.data.memories.get_mut(addr as usize) { + Some(mem) => Ok(mem), + None => { + cold(); + Err(Self::not_found_error("memory")) + } + } + } + + /// Get the memory at the actual index in the store + #[inline] + pub(crate) fn get_mems_mut( + &mut self, + addr: MemAddr, + addr2: MemAddr, + ) -> Result<(&mut MemoryInstance, &mut MemoryInstance)> { + match get_pair_mut(&mut self.data.memories, addr as usize, addr2 as usize) { + Some(mems) => Ok(mems), + None => { + cold(); + Err(Self::not_found_error("memory")) + } + } } /// Get the table at the actual index in the store - #[inline(always)] - pub(crate) fn get_table(&self, addr: TableAddr) -> Result<&RefCell<TableInstance>> { + #[inline] + pub(crate) fn get_table(&self, addr: TableAddr) -> Result<&TableInstance> { self.data.tables.get(addr as usize).ok_or_else(|| Self::not_found_error("table")) } - /// Get the data at the actual index in the store - #[inline(always)] - pub(crate) fn get_data(&self, addr: DataAddr) -> Result<&DataInstance> { - self.data.datas.get(addr as usize).ok_or_else(|| Self::not_found_error("data")) + /// Get the table at the actual index in the store + #[inline] + pub(crate) fn get_table_mut(&mut self, addr: TableAddr) -> Result<&mut TableInstance> { + self.data.tables.get_mut(addr as usize).ok_or_else(|| Self::not_found_error("table")) + } + + /// Get two mutable tables at the actual index in the store + #[inline] + pub(crate) fn get_tables_mut( + &mut self, + addr: TableAddr, + addr2: TableAddr, + ) -> Result<(&mut TableInstance, &mut TableInstance)> { + match get_pair_mut(&mut self.data.tables, addr as usize, addr2 as usize) { + Some(tables) => Ok(tables), + None => { + cold(); + Err(Self::not_found_error("table")) + } + } } /// Get the data at the actual index in the store - #[inline(always)] + #[inline] pub(crate) fn get_data_mut(&mut self, addr: DataAddr) -> Result<&mut DataInstance> { self.data.datas.get_mut(addr as usize).ok_or_else(|| Self::not_found_error("data")) } /// Get the element at the actual index in the store - #[inline(always)] - pub(crate) fn get_elem(&self, addr: ElemAddr) -> Result<&ElementInstance> { - self.data.elements.get(addr as usize).ok_or_else(|| Self::not_found_error("element")) - } - - /// Get the element at the actual index in the store - #[inline(always)] + #[inline] pub(crate) fn get_elem_mut(&mut self, addr: ElemAddr) -> Result<&mut ElementInstance> { self.data.elements.get_mut(addr as usize).ok_or_else(|| Self::not_found_error("element")) } /// Get the global at the actual index in the store - #[inline(always)] + #[inline] pub(crate) fn get_global(&self, addr: GlobalAddr) -> Result<&GlobalInstance> { self.data.globals.get(addr as usize).ok_or_else(|| Self::not_found_error("global")) } @@ -195,7 +238,7 @@ impl Store { let table_count = self.data.tables.len(); let mut table_addrs = Vec::with_capacity(table_count); for (i, table) in tables.into_iter().enumerate() { - self.data.tables.push(RefCell::new(TableInstance::new(table, idx))); + self.data.tables.push(TableInstance::new(table, idx)); table_addrs.push((i + table_count) as TableAddr); } Ok(table_addrs) @@ -209,7 +252,7 @@ impl Store { if let MemoryArch::I64 = mem.arch { return Err(Error::UnsupportedFeature("64-bit memories".to_string())); } - self.data.memories.push(RefCell::new(MemoryInstance::new(mem, idx))); + self.data.memories.push(MemoryInstance::new(mem, idx)); mem_addrs.push((i + mem_count) as MemAddr); } Ok(mem_addrs) @@ -302,7 +345,7 @@ impl Store { // This isn't mentioned in the spec, but the "unofficial" testsuite has a test for it: // https://github.com/WebAssembly/testsuite/blob/5a1a590603d81f40ef471abba70a90a9ae5f4627/linking.wast#L264-L276 // I have NO IDEA why this is allowed, but it is. - if let Err(Error::Trap(trap)) = table.borrow_mut().init_raw(offset, &init) { + if let Err(Error::Trap(trap)) = table.init_raw(offset, &init) { return Ok((elem_addrs.into_boxed_slice(), Some(trap))); } @@ -345,7 +388,7 @@ impl Store { return Err(Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))); }; - match mem.borrow_mut().store(offset as usize, data.data.len(), &data.data) { + match mem.store(offset as usize, data.data.len(), &data.data) { Ok(()) => None, Err(Error::Trap(trap)) => return Ok((data_addrs.into_boxed_slice(), Some(trap))), Err(e) => return Err(e), @@ -368,7 +411,7 @@ impl Store { } pub(crate) fn add_table(&mut self, table: TableType, idx: ModuleInstanceAddr) -> Result<TableAddr> { - self.data.tables.push(RefCell::new(TableInstance::new(table, idx))); + self.data.tables.push(TableInstance::new(table, idx)); Ok(self.data.tables.len() as TableAddr - 1) } @@ -376,7 +419,7 @@ impl Store { if let MemoryArch::I64 = mem.arch { return Err(Error::UnsupportedFeature("64-bit memories".to_string())); } - self.data.memories.push(RefCell::new(MemoryInstance::new(mem, idx))); + self.data.memories.push(MemoryInstance::new(mem, idx)); Ok(self.data.memories.len() as MemAddr - 1) } @@ -426,3 +469,16 @@ impl Store { Ok(val) } } + +// remove this when the `get_many_mut` function is stabilized +fn get_pair_mut<T>(slice: &mut [T], i: usize, j: usize) -> Option<(&mut T, &mut T)> { + let (first, second) = (core::cmp::min(i, j), core::cmp::max(i, j)); + if i == j || second >= slice.len() { + return None; + } + let (_, tmp) = slice.split_at_mut(first); + let (x, rest) = tmp.split_at_mut(1); + let (_, y) = rest.split_at_mut(second - first - 1); + let pair = if i < j { (&mut x[0], &mut y[0]) } else { (&mut y[0], &mut x[0]) }; + Some(pair) +} diff --git a/examples/wasm-rust.rs b/examples/wasm-rust.rs index 79fb8fb..5d32cd2 100644 --- a/examples/wasm-rust.rs +++ b/examples/wasm-rust.rs @@ -1,3 +1,5 @@ +use std::hint::black_box; + use eyre::{eyre, Result}; use tinywasm::{Extern, FuncContext, Imports, MemoryStringExt, Module, Store}; @@ -66,19 +68,13 @@ fn tinywasm() -> Result<()> { let mut store = Store::default(); let mut imports = Imports::new(); - imports.define( - "env", - "printi32", - Extern::typed_func(|_: FuncContext<'_>, x: i32| { - println!("{}", x); - Ok(()) - }), - )?; - let instance = module.instantiate(&mut store, Some(imports))?; + imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _x: i32| Ok(())))?; + let instance = module.instantiate(&mut store, Some(black_box(imports)))?; let hello = instance.exported_func::<(), ()>(&store, "hello")?; - hello.call(&mut store, ())?; - + hello.call(&mut store, black_box(()))?; + hello.call(&mut store, black_box(()))?; + hello.call(&mut store, black_box(()))?; Ok(()) } @@ -133,7 +129,7 @@ fn printi32() -> Result<()> { } fn fibonacci() -> Result<()> { - let module = Module::parse_file("./examples/rust/out/fibonacci.wasm")?; + let module = Module::parse_file("./examples/rust/out/fibonacci.opt.wasm")?; let mut store = Store::default(); let instance = module.instantiate(&mut store, None)?; @@ -146,7 +142,7 @@ fn fibonacci() -> Result<()> { } fn argon2id() -> Result<()> { - let module = Module::parse_file("./examples/rust/out/argon2id.wasm")?; + let module = Module::parse_file("./examples/rust/out/argon2id.opt.wasm")?; let mut store = Store::default(); let instance = module.instantiate(&mut store, None)?; |
