summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry Gressmann <mail@henrygressmann.de>2024-06-30 22:19:25 +0200
committerHenry Gressmann <mail@henrygressmann.de>2024-06-30 22:19:25 +0200
commit4425733bdfb516ca2530e6199c2c95ece91b627d (patch)
tree2b50626d449c5b4f2295ac90f93d28df1600a901
parent9c85fa9c3a2066ce851af53d7019e6a25a45af3b (diff)
chore: Memory and Data Instances are no longer reference counted
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
-rw-r--r--.cargo/config.toml5
-rw-r--r--CHANGELOG.md1
-rw-r--r--crates/tinywasm/benches/fibonacci.rs2
-rw-r--r--crates/tinywasm/src/imports.rs13
-rw-r--r--crates/tinywasm/src/instance.rs20
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs130
-rw-r--r--crates/tinywasm/src/interpreter/stack/call_stack.rs33
-rw-r--r--crates/tinywasm/src/interpreter/stack/value_stack.rs46
-rw-r--r--crates/tinywasm/src/lib.rs2
-rw-r--r--crates/tinywasm/src/reference.rs5
-rw-r--r--crates/tinywasm/src/store/memory.rs18
-rw-r--r--crates/tinywasm/src/store/mod.rs114
-rw-r--r--examples/wasm-rust.rs22
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)?;