summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/parser/src/conversion.rs4
-rw-r--r--crates/tinywasm/src/func.rs6
-rw-r--r--crates/tinywasm/src/imports.rs80
-rw-r--r--crates/tinywasm/src/instance.rs272
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs154
-rw-r--r--crates/tinywasm/src/interpreter/stack/call_stack.rs31
-rw-r--r--crates/tinywasm/src/interpreter/stack/value_stack.rs36
-rw-r--r--crates/tinywasm/src/store/mod.rs252
-rw-r--r--crates/types/src/lib.rs25
9 files changed, 411 insertions, 449 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index 08f3c83..f7fcb34 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -241,8 +241,8 @@ pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result<Box<[C
// In practice, the len can never be something other than 2,
// but we'll keep this here since it's part of the spec
// Invalid modules will be rejected by the validator anyway (there are also tests for this in the testsuite)
- assert!(ops.len() >= 2);
- assert!(matches!(ops[ops.len() - 1], wasmparser::Operator::End));
+ debug_assert!(ops.len() >= 2);
+ debug_assert!(matches!(ops[ops.len() - 1], wasmparser::Operator::End));
let mut out = Vec::with_capacity(ops.len().saturating_sub(1));
for op in ops.iter().take(ops.len() - 1) {
diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs
index d79951a..e0929e4 100644
--- a/crates/tinywasm/src/func.rs
+++ b/crates/tinywasm/src/func.rs
@@ -25,8 +25,7 @@ impl Function {
store.value_stack.clear();
store.value_stack.extend_from_wasmvalues(params)?;
let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?;
- let stack_offset = wasm_func.func.locals;
- let callframe = CallFrame::new(self.addr, wasm_func.owner, locals_base, stack_offset);
+ let callframe = CallFrame::new(self.addr, locals_base, wasm_func.func.locals);
// Execute until completion and then collect result values from the stack.
InterpreterRuntime::exec(store, callframe)?;
@@ -56,8 +55,7 @@ impl Function {
store.value_stack.clear();
store.value_stack.extend_from_wasmvalues(params)?;
let locals_base = store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?;
- let stack_offset = wasm_func.func.locals;
- let callframe = CallFrame::new(self.addr, wasm_func.owner, locals_base, stack_offset);
+ let callframe = CallFrame::new(self.addr, locals_base, wasm_func.func.locals);
Ok(FuncExecution {
store,
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index fff2a8f..ba6076e 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -98,10 +98,7 @@ impl From<&Import> for ExternName {
#[derive(Default, Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Imports {
- globals: BTreeMap<ExternName, Global>,
- tables: BTreeMap<ExternName, Table>,
- memories: BTreeMap<ExternName, Memory>,
- function_handles: BTreeMap<ExternName, Function>,
+ externs: BTreeMap<ExternName, Extern>,
modules: BTreeMap<String, crate::ModuleInstance>,
}
@@ -112,30 +109,15 @@ pub(crate) struct ResolvedImports {
pub(crate) funcs: Vec<FuncAddr>,
}
-impl ResolvedImports {
- pub(crate) const fn new() -> Self {
- Self { globals: Vec::new(), tables: Vec::new(), memories: Vec::new(), funcs: Vec::new() }
- }
-}
-
impl Imports {
/// Create a new empty import set
pub const fn new() -> Self {
- Self {
- globals: BTreeMap::new(),
- tables: BTreeMap::new(),
- memories: BTreeMap::new(),
- function_handles: BTreeMap::new(),
- modules: BTreeMap::new(),
- }
+ Self { externs: BTreeMap::new(), modules: BTreeMap::new() }
}
/// Merge two import sets
pub fn merge(mut self, other: Self) -> Self {
- self.globals.extend(other.globals);
- self.tables.extend(other.tables);
- self.memories.extend(other.memories);
- self.function_handles.extend(other.function_handles);
+ self.externs.extend(other.externs);
self.modules.extend(other.modules);
self
}
@@ -151,38 +133,13 @@ impl Imports {
/// Define an import value.
pub fn define(&mut self, module: &str, name: &str, value: impl Into<Extern>) -> &mut Self {
let name = ExternName { module: module.to_string(), name: name.to_string() };
- match value.into() {
- Extern::Global(v) => {
- self.globals.insert(name, v);
- }
- Extern::Table(v) => {
- self.tables.insert(name, v);
- }
- Extern::Memory(v) => {
- self.memories.insert(name, v);
- }
- Extern::Function(v) => {
- self.function_handles.insert(name, v);
- }
- }
+ self.externs.insert(name, value.into());
self
}
pub(crate) fn take_defined(&self, import: &Import) -> Option<Extern> {
let name = ExternName::from(import);
- if let Some(v) = self.globals.get(&name) {
- return Some(Extern::Global(*v));
- }
- if let Some(v) = self.tables.get(&name) {
- return Some(Extern::Table(*v));
- }
- if let Some(v) = self.memories.get(&name) {
- return Some(Extern::Memory(*v));
- }
- if let Some(v) = self.function_handles.get(&name) {
- return Some(Extern::Function(v.clone()));
- }
- None
+ self.externs.get(&name).cloned()
}
#[cfg(not(feature = "debug"))]
@@ -242,13 +199,21 @@ impl Imports {
Ok(())
}
- pub(crate) fn link(
- self,
- store: &mut crate::Store,
- module: &Module,
- _idx: ModuleInstanceAddr,
- ) -> Result<ResolvedImports> {
- let mut imports = ResolvedImports::new();
+ pub(crate) fn link(&self, store: &mut crate::Store, module: &Module) -> Result<ResolvedImports> {
+ let (global_count, table_count, mem_count, func_count) =
+ module.imports.iter().fold((0, 0, 0, 0), |(g, t, m, f), import| match import.kind {
+ ImportKind::Global(_) => (g + 1, t, m, f),
+ ImportKind::Table(_) => (g, t + 1, m, f),
+ ImportKind::Memory(_) => (g, t, m + 1, f),
+ ImportKind::Function(_) => (g, t, m, f + 1),
+ });
+
+ let mut imports = ResolvedImports {
+ globals: Vec::with_capacity(global_count),
+ tables: Vec::with_capacity(table_count),
+ memories: Vec::with_capacity(mem_count),
+ funcs: Vec::with_capacity(func_count),
+ };
for import in &*module.imports {
if let Some(defined) = self.take_defined(import) {
@@ -299,9 +264,8 @@ impl Imports {
let Some(instance) = self.modules.get(&name.module) else {
return Err(LinkingError::unknown_import(import).into());
};
- if instance.0.store_id != store.id() {
- return Err(crate::Error::InvalidStore);
- }
+ instance.validate_store(store)?;
+
let val = instance.export_addr(&import.name).ok_or_else(|| LinkingError::unknown_import(import))?;
{
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index 627604e..f5125ba 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -1,3 +1,5 @@
+use core::hint::cold_path;
+
use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::{format, rc::Rc};
@@ -25,132 +27,121 @@ pub enum ExternItem {
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#module-instances>
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
-pub struct ModuleInstance(pub(crate) Rc<ModuleInstanceInner>);
+pub struct ModuleInstance(Rc<ModuleInstanceInner>);
#[cfg_attr(feature = "debug", derive(Debug))]
-pub(crate) struct ModuleInstanceInner {
- pub(crate) store_id: usize,
- pub(crate) idx: ModuleInstanceAddr,
- pub(crate) types: Arc<[Arc<FuncType>]>,
- pub(crate) func_addrs: Box<[FuncAddr]>,
- pub(crate) table_addrs: Box<[TableAddr]>,
- pub(crate) mem_addrs: Box<[MemAddr]>,
- pub(crate) global_addrs: Box<[GlobalAddr]>,
- pub(crate) elem_addrs: Box<[ElemAddr]>,
- pub(crate) data_addrs: Box<[DataAddr]>,
- pub(crate) func_start: Option<FuncAddr>,
- pub(crate) exports: Arc<[Export]>,
+struct ModuleInstanceInner {
+ store_id: usize,
+ idx: ModuleInstanceAddr,
+ types: Arc<[Arc<FuncType>]>,
+ func_addrs: Box<[FuncAddr]>,
+ table_addrs: Box<[TableAddr]>,
+ mem_addrs: Box<[MemAddr]>,
+ global_addrs: Box<[GlobalAddr]>,
+ elem_addrs: Box<[ElemAddr]>,
+ data_addrs: Box<[DataAddr]>,
+ func_start: Option<FuncAddr>,
+ exports: Arc<[Export]>,
}
-// impl ModuleInstance {
-// #[cfg(feature = "parser")]
-// /// Parse a module from bytes. Requires `parser` feature.
-// pub fn from_wasm_bytes(wasm: &[u8]) -> Result<Self> {
-// let data = tinywasm_parser::Parser::new().parse_module_bytes(wasm)?;
-// Ok(data.into())
-// }
-
-// #[cfg(all(feature = "parser", feature = "std"))]
-// /// Parse a module from a file. Requires `parser` and `std` features.
-// pub fn from_wasm_file(path: impl AsRef<crate::std::path::Path> + Clone) -> Result<Self> {
-// let data = tinywasm_parser::Parser::new().parse_module_file(path)?;
-// Ok(data.into())
-// }
-
-// #[cfg(all(feature = "parser", feature = "std"))]
-// /// Parse a module from a stream. Requires `parser` and `std` features.
-// pub fn from_wasm_stream(stream: impl crate::std::io::Read) -> Result<Self> {
-// let data = tinywasm_parser::Parser::new().parse_module_stream(stream)?;
-// Ok(data.into())
-// }
-
-// /// Instantiate the module in the given store
-// ///
-// /// Runs the start function if it exists
-// ///
-// /// If you want to run the start function yourself, use `ModuleInstance::instantiate`
-// ///
-// /// See <https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation>
-// pub fn instantiate(self, store: &mut Store, imports: Option<Imports>) -> Result<ModuleInstance> {
-// let instance = ModuleInstance::instantiate(store, self, imports)?;
-// let _ = instance.start(store)?;
-// Ok(instance)
-// }
-// }
+impl ModuleInstance {
+ #[inline]
+ pub(crate) fn idx(&self) -> ModuleInstanceAddr {
+ self.0.idx
+ }
-impl ModuleInstanceInner {
#[inline]
pub(crate) fn func_ty(&self, addr: FuncAddr) -> &Arc<FuncType> {
- match self.types.get(addr as usize) {
+ match self.0.types.get(addr as usize) {
Some(ty) => ty,
- None => unreachable!("invalid function address: {addr}"),
+ None => {
+ cold_path();
+ unreachable!("invalid function address: {addr}")
+ }
}
}
#[inline]
pub(crate) fn func_addrs(&self) -> &[FuncAddr] {
- &self.func_addrs
+ &self.0.func_addrs
}
// resolve a function address to the global store address
#[inline]
pub(crate) fn resolve_func_addr(&self, addr: FuncAddr) -> FuncAddr {
- match self.func_addrs.get(addr as usize) {
+ match self.0.func_addrs.get(addr as usize) {
Some(addr) => *addr,
- None => unreachable!("invalid function address: {addr}"),
+ None => {
+ cold_path();
+ unreachable!("invalid function address: {addr}")
+ }
}
}
// resolve a table address to the global store address
#[inline]
pub(crate) fn resolve_table_addr(&self, addr: TableAddr) -> TableAddr {
- match self.table_addrs.get(addr as usize) {
+ match self.0.table_addrs.get(addr as usize) {
Some(addr) => *addr,
- None => unreachable!("invalid table address: {addr}"),
+ None => {
+ cold_path();
+ unreachable!("invalid table address: {addr}")
+ }
}
}
// resolve a memory address to the global store address
#[inline]
pub(crate) fn resolve_mem_addr(&self, addr: MemAddr) -> MemAddr {
- match self.mem_addrs.get(addr as usize) {
+ match self.0.mem_addrs.get(addr as usize) {
Some(addr) => *addr,
- None => unreachable!("invalid memory address: {addr}"),
+ None => {
+ cold_path();
+ unreachable!("invalid memory address: {addr}")
+ }
}
}
// resolve a data address to the global store address
#[inline]
pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> DataAddr {
- match self.data_addrs.get(addr as usize) {
+ match self.0.data_addrs.get(addr as usize) {
Some(addr) => *addr,
- None => unreachable!("invalid data address: {addr}"),
+ None => {
+ cold_path();
+ unreachable!("invalid data address: {addr}")
+ }
}
}
- // resolve a memory address to the global store address
+ // resolve an element address to the global store address
#[inline]
pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> ElemAddr {
- match self.elem_addrs.get(addr as usize) {
+ match self.0.elem_addrs.get(addr as usize) {
Some(addr) => *addr,
- None => unreachable!("invalid element address: {addr}"),
+ None => {
+ cold_path();
+ unreachable!("invalid element address: {addr}")
+ }
}
}
// resolve a global address to the global store address
#[inline]
pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> GlobalAddr {
- match self.global_addrs.get(addr as usize) {
+ match self.0.global_addrs.get(addr as usize) {
Some(addr) => *addr,
- None => unreachable!("invalid global address: {addr}"),
+ None => {
+ cold_path();
+ unreachable!("invalid global address: {addr}")
+ }
}
}
-}
-impl ModuleInstance {
#[inline]
- fn validate_store(&self, store: &Store) -> Result<()> {
+ pub(crate) fn validate_store(&self, store: &Store) -> Result<()> {
if self.0.store_id != store.id() {
+ cold_path();
return Err(Error::InvalidStore);
}
Ok(())
@@ -175,20 +166,21 @@ impl ModuleInstance {
/// See <https://webassembly.github.io/spec/core/exec/modules.html#exec-instantiation>
pub fn instantiate_no_start(store: &mut Store, module: &Module, imports: Option<Imports>) -> Result<Self> {
let idx = store.next_module_instance_idx();
- let mut addrs = imports.unwrap_or_default().link(store, module, idx)?;
+ let mut addrs = imports.unwrap_or_default().link(store, module)?;
addrs.funcs.extend(store.init_funcs(&module.funcs, idx));
- addrs.tables.extend(store.init_tables(&module.table_types, idx));
+ addrs.tables.extend(store.init_tables(&module.table_types));
match module.local_memory_allocation {
LocalMemoryAllocation::Skip => {}
- LocalMemoryAllocation::Lazy => addrs.memories.extend(store.init_lazy_memories(&module.memory_types, idx)?),
- LocalMemoryAllocation::Eager => addrs.memories.extend(store.init_memories(&module.memory_types, idx)?),
+ LocalMemoryAllocation::Lazy => addrs.memories.extend(store.init_lazy_memories(&module.memory_types)?),
+ LocalMemoryAllocation::Eager => addrs.memories.extend(store.init_memories(&module.memory_types)?),
}
- let global_addrs = store.init_globals(addrs.globals, &module.globals, &addrs.funcs, idx)?;
+
+ store.init_globals(&mut addrs.globals, &module.globals, &addrs.funcs)?;
let (elem_addrs, elem_trapped) =
- store.init_elements(&addrs.tables, &addrs.funcs, &global_addrs, &module.elements, idx)?;
+ store.init_elements(&addrs.tables, &addrs.funcs, &addrs.globals, &module.elements)?;
let (data_addrs, data_trapped) =
- store.init_data(&addrs.memories, &global_addrs, &addrs.funcs, &module.data, idx)?;
+ store.init_data(&addrs.memories, &addrs.globals, &addrs.funcs, &module.data)?;
let instance = ModuleInstanceInner {
store_id: store.id(),
@@ -197,25 +189,25 @@ impl ModuleInstance {
func_addrs: addrs.funcs.into_boxed_slice(),
table_addrs: addrs.tables.into_boxed_slice(),
mem_addrs: addrs.memories.into_boxed_slice(),
- global_addrs: global_addrs.into_boxed_slice(),
+ global_addrs: addrs.globals.into_boxed_slice(),
elem_addrs,
data_addrs,
func_start: module.start_func,
exports: module.exports.clone(),
};
- let instance = Rc::new(instance);
+ let instance = ModuleInstance(Rc::new(instance));
store.add_instance(instance.clone());
match (elem_trapped, data_trapped) {
(Some(trap), _) | (_, Some(trap)) => Err(trap.into()),
- _ => Ok(ModuleInstance(instance)),
+ _ => Ok(instance),
}
}
/// Get a export by name
pub fn export_addr(&self, name: &str) -> Option<ExternVal> {
- let exports = self.0.exports.iter().find(|e| e.name == name.into())?;
+ let exports = self.0.exports.iter().find(|e| *e.name == *name)?;
let addr = match exports.kind {
ExternalKind::Func => self.0.func_addrs.get(exports.index as usize)?,
ExternalKind::Table => self.0.table_addrs.get(exports.index as usize)?,
@@ -228,53 +220,28 @@ impl ModuleInstance {
/// Returns an iterator over all exported extern values for this instance.
pub fn exports(&self) -> impl Iterator<Item = (&str, ExternItem)> + '_ {
self.0.exports.iter().map(move |export| {
- let name = export.name.as_ref();
let item = match export.kind {
ExternalKind::Func => {
- let idx = export.index as usize;
- let func_addr = *self
- .0
- .func_addrs
- .get(idx)
- .unwrap_or_else(|| unreachable!("invalid function export index: {}", export.index));
- let ty = self.0.func_ty(export.index).clone();
+ let func_addr = self.resolve_func_addr(export.index);
ExternItem::Func(Function {
item: crate::StoreItem::new(self.0.store_id, func_addr),
module_addr: self.id(),
addr: func_addr,
- ty,
+ ty: self.func_ty(export.index).clone(),
})
}
ExternalKind::Table => {
- let idx = export.index as usize;
- let table_addr = *self
- .0
- .table_addrs
- .get(idx)
- .unwrap_or_else(|| unreachable!("invalid table export index: {}", export.index));
- ExternItem::Table(Table::from_store_addr(self.0.store_id, table_addr))
+ ExternItem::Table(Table::from_store_addr(self.0.store_id, self.resolve_table_addr(export.index)))
}
ExternalKind::Memory => {
- let idx = export.index as usize;
- let mem_addr = *self
- .0
- .mem_addrs
- .get(idx)
- .unwrap_or_else(|| unreachable!("invalid memory export index: {}", export.index));
- ExternItem::Memory(Memory::from_store_addr(self.0.store_id, mem_addr))
+ ExternItem::Memory(Memory::from_store_addr(self.0.store_id, self.resolve_mem_addr(export.index)))
}
ExternalKind::Global => {
- let idx = export.index as usize;
- let global_addr = *self
- .0
- .global_addrs
- .get(idx)
- .unwrap_or_else(|| unreachable!("invalid global export index: {}", export.index));
- ExternItem::Global(Global::from_store_addr(self.0.store_id, global_addr))
+ ExternItem::Global(Global::from_store_addr(self.0.store_id, self.resolve_global_addr(export.index)))
}
};
- (name, item)
+ (export.name.as_ref(), item)
})
}
@@ -292,25 +259,19 @@ impl ModuleInstance {
/// Get any exported extern value by name.
pub fn extern_item(&self, name: &str) -> Result<ExternItem> {
match self.require_export(name)? {
- ExternVal::Func(func_addr) => {
- let export = self
- .0
- .exports
- .iter()
- .find(|e| e.name == name.into())
- .ok_or_else(|| Error::Other(format!("Export not found: {name}")))?;
+ ExternVal::Func(addr) => {
+ let export = self.0.exports.iter().find(|e| e.name == name.into());
+ let export = export.ok_or_else(|| Error::Other(format!("Export not found: {name}")))?;
Ok(ExternItem::Func(Function {
- item: crate::StoreItem::new(self.0.store_id, func_addr),
+ item: crate::StoreItem::new(self.0.store_id, addr),
module_addr: self.id(),
- addr: func_addr,
- ty: self.0.func_ty(export.index).clone(),
+ addr,
+ ty: self.func_ty(export.index).clone(),
}))
}
- ExternVal::Memory(mem_addr) => Ok(ExternItem::Memory(Memory::from_store_addr(self.0.store_id, mem_addr))),
- ExternVal::Table(table_addr) => Ok(ExternItem::Table(Table::from_store_addr(self.0.store_id, table_addr))),
- ExternVal::Global(global_addr) => {
- Ok(ExternItem::Global(Global::from_store_addr(self.0.store_id, global_addr)))
- }
+ ExternVal::Memory(addr) => Ok(ExternItem::Memory(Memory::from_store_addr(self.0.store_id, addr))),
+ ExternVal::Table(addr) => Ok(ExternItem::Table(Table::from_store_addr(self.0.store_id, addr))),
+ ExternVal::Global(addr) => Ok(ExternItem::Global(Global::from_store_addr(self.0.store_id, addr))),
}
}
@@ -318,17 +279,19 @@ impl ModuleInstance {
pub fn func_untyped(&self, store: &Store, name: &str) -> Result<Function> {
self.validate_store(store)?;
- let export = self.require_export(name)?;
- let ExternVal::Func(func_addr) = export else {
- return Err(Error::Other(format!("Export is not a function: {name}")));
+ let func_addr = match self.require_export(name)? {
+ ExternVal::Func(func_addr) => func_addr,
+ _ => {
+ cold_path();
+ return Err(Error::Other(format!("Export is not a function: {name}")));
+ }
};
- let ty = store.state.get_func(func_addr).ty();
Ok(Function {
item: crate::StoreItem::new(self.0.store_id, func_addr),
addr: func_addr,
module_addr: self.id(),
- ty: ty.clone(),
+ ty: store.state.get_func(func_addr).ty().clone(),
})
}
@@ -384,13 +347,16 @@ impl ModuleInstance {
func: &Function,
func_name: &str,
) -> Result<()> {
- let expected = FuncType::new(&P::wasm_types(), &R::wasm_types());
- if *func.ty != expected {
+ if *func.ty.params() != *P::wasm_types() || *func.ty.results() != *R::wasm_types() {
+ cold_path();
+
#[cfg(feature = "debug")]
return Err(Error::Other(format!(
- "function type mismatch for {func_name}: expected {expected:?}, actual {:?}",
+ "function type mismatch for {func_name}: expected {:?}, actual {:?}",
+ FuncType::new(&P::wasm_types(), &R::wasm_types()),
func.ty
)));
+
#[cfg(not(feature = "debug"))]
return Err(Error::Other(format!("function type mismatch for {func_name}")));
}
@@ -400,10 +366,10 @@ impl ModuleInstance {
/// Get a memory export by name.
pub fn memory(&self, name: &str) -> Result<Memory> {
- let ExternVal::Memory(mem_addr) = self.require_export(name)? else {
- return Err(Error::Other(format!("Export is not a memory: {name}")));
- };
- Ok(Memory::from_store_addr(self.0.store_id, mem_addr))
+ match self.require_export(name)? {
+ ExternVal::Memory(mem_addr) => Ok(Memory::from_store_addr(self.0.store_id, mem_addr)),
+ _ => Err(Error::Other(format!("Export is not a memory: {name}"))),
+ }
}
/// Get a memory by its module-local index.
@@ -420,11 +386,10 @@ impl ModuleInstance {
/// Get a table export by name.
pub fn table(&self, name: &str) -> Result<Table> {
- let export = self.require_export(name)?;
- let ExternVal::Table(table_addr) = export else {
- return Err(Error::Other(format!("Export is not a table: {name}")));
- };
- Ok(Table::from_store_addr(self.0.store_id, table_addr))
+ match self.require_export(name)? {
+ ExternVal::Table(table_addr) => Ok(Table::from_store_addr(self.0.store_id, table_addr)),
+ _ => Err(Error::Other(format!("Export is not a table: {name}"))),
+ }
}
/// Get a table by its module-local index.
@@ -446,12 +411,10 @@ impl ModuleInstance {
/// Get a global export by name.
pub fn global(&self, name: &str) -> Result<Global> {
- let export = self.require_export(name)?;
- let ExternVal::Global(global_addr) = export else {
- return Err(Error::Other(format!("Export is not a global: {name}")));
- };
-
- Ok(Global::from_store_addr(self.0.store_id, global_addr))
+ match self.require_export(name)? {
+ ExternVal::Global(global_addr) => Ok(Global::from_store_addr(self.0.store_id, global_addr)),
+ _ => Err(Error::Other(format!("Export is not a global: {name}"))),
+ }
}
/// Set the value of a mutable global export by name.
@@ -492,13 +455,12 @@ impl ModuleInstance {
}
};
- let func_addr = self.0.resolve_func_addr(func_index);
- let ty = store.state.get_func(func_addr).ty();
+ let func_addr = self.resolve_func_addr(func_index);
Ok(Some(Function {
item: crate::StoreItem::new(self.0.store_id, func_addr),
module_addr: self.id(),
addr: func_addr,
- ty: ty.clone(),
+ ty: store.state.get_func(func_addr).ty().clone(),
}))
}
@@ -508,9 +470,9 @@ impl ModuleInstance {
///
/// See <https://webassembly.github.io/spec/core/syntax/modules.html#syntax-start>
pub fn start(&self, store: &mut Store) -> Result<Option<()>> {
- let Some(func) = self.start_func(store)? else {
- return Ok(None);
- };
- func.call(store, &[]).map(|_| Some(()))
+ match self.start_func(store)? {
+ Some(func) => func.call(store, &[]).map(|_| Some(())),
+ None => Ok(None),
+ }
}
}
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index 204618a..4cacea6 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -16,7 +16,6 @@ use super::num_helpers::*;
use super::values::*;
use crate::engine::FuelPolicy;
use crate::func::{FuncContext, HostFunction};
-use crate::instance::ModuleInstanceInner;
use crate::interpreter::Value128;
use crate::*;
@@ -25,15 +24,15 @@ const FUEL_COST_CALL_TOTAL: u32 = 5;
pub(crate) struct Executor<'store, const BUDGETED: bool> {
cf: CallFrame,
func: Arc<WasmFunction>,
- module: Rc<ModuleInstanceInner>,
+ module: ModuleInstance,
store: &'store mut Store,
}
impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
pub(crate) fn new(store: &'store mut Store, cf: CallFrame) -> Self {
- let module = store.get_module_instance_raw(cf.module_addr).clone();
- let func = store.state.get_wasm_func(cf.func_addr).clone();
- Self { module, store, cf, func }
+ let wasm_func = store.state.get_wasm_func(cf.func_addr);
+ let module = store.get_module_instance_internal(wasm_func.owner);
+ Self { module, cf, func: wasm_func.func.clone(), store }
}
#[inline(always)]
@@ -138,11 +137,11 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
Select64 => self.store.value_stack.select::<Value64>()?,
Select128 => self.store.value_stack.select::<Value128>()?,
SelectMulti(counts) => self.store.value_stack.select_multi(*counts),
- Call(v) => { self.exec_call_direct::<false>(*v)?; return Ok(None); }
- CallSelf => { self.exec_call_self::<false>()?; return Ok(None); }
+ Call(v) => { self.exec_call_direct(*v)?; return Ok(None); }
+ CallSelf => { self.exec_call_self()?; return Ok(None); }
CallIndirect(ty, table) => { self.exec_call_indirect::<false>(*ty, *table)?; return Ok(None); }
- ReturnCall(v) => { self.exec_call_direct::<true>(*v)?; return Ok(None); }
- ReturnCallSelf => { self.exec_call_self::<true>()?; return Ok(None); }
+ ReturnCall(v) => { self.exec_return_call_direct(*v)?; return Ok(None); }
+ ReturnCallSelf => { self.exec_return_call_self()?; return Ok(None); }
ReturnCallIndirect(ty, table) => { self.exec_call_indirect::<true>(*ty, *table)?; return Ok(None); }
Jump(ip) => { self.exec_jump(*ip); return Ok(None); }
JumpIfZero(ip) => if self.exec_jump_if_zero(*ip) { return Ok(None) },
@@ -769,7 +768,6 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
self.jump_if(cmp_i64(lhs, rhs, op), target_ip)
}
- #[inline(always)]
fn exec_branch_table(&mut self, default_ip: u32, start: u32, len: u32) {
let idx = self.store.value_stack.pop::<i32>();
let target_ip = if idx >= 0 && (idx as u32) < len {
@@ -781,121 +779,119 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
self.cf.instr_ptr = target_ip;
}
- fn exec_call<const IS_RETURN_CALL: bool>(
- &mut self,
- wasm_func: WasmFunctionInstance,
- func_addr: FuncAddr,
- ) -> Result<(), Trap> {
+ fn exec_call(&mut self, wasm_func: WasmFunctionInstance, func_addr: FuncAddr) -> Result<(), Trap> {
if !Arc::ptr_eq(&self.func, &wasm_func.func) {
self.func = wasm_func.func.clone();
}
- if IS_RETURN_CALL {
- self.store.value_stack.truncate_keep_counts(self.cf.locals_base, wasm_func.func.params);
- } else if self.store.call_stack.is_at_limit() {
+ let Ok(locals_base) = self.store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)
+ else {
cold_path();
return Err(Trap::CallStackOverflow);
- }
-
- let locals_base = match self.store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals) {
- Ok(base) => base,
- Err(err) => {
- cold_path();
- if IS_RETURN_CALL {
- return Err(err);
- }
- return Err(Trap::CallStackOverflow);
- }
};
- let new_call_frame = CallFrame::new(func_addr, wasm_func.owner, locals_base, wasm_func.func.locals);
+ self.store.call_stack.push(self.cf)?;
+ self.cf = CallFrame::new(func_addr, locals_base, wasm_func.func.locals);
+ if wasm_func.owner != self.module.idx() {
+ self.module = self.store.get_module_instance_internal(wasm_func.owner);
+ }
+
+ Ok(())
+ }
- if !IS_RETURN_CALL {
- self.cf.incr_instr_ptr(); // skip the call instruction
- self.store.call_stack.push(self.cf)?;
+ fn exec_return_call(&mut self, wasm_func: WasmFunctionInstance, func_addr: FuncAddr) -> Result<(), Trap> {
+ if !Arc::ptr_eq(&self.func, &wasm_func.func) {
+ self.func = wasm_func.func.clone();
}
- self.cf = new_call_frame;
- if self.cf.module_addr != self.module.idx {
- self.module = self.store.get_module_instance_raw(self.cf.module_addr).clone();
+ self.store.value_stack.truncate_keep_counts(self.cf.locals_base, wasm_func.func.params);
+ let locals_base = self.store.value_stack.enter_locals(&wasm_func.func.params, &wasm_func.func.locals)?;
+ self.cf = CallFrame::new(func_addr, locals_base, wasm_func.func.locals);
+ if wasm_func.owner != self.module.idx() {
+ self.module = self.store.get_module_instance_internal(wasm_func.owner);
}
Ok(())
}
+
fn exec_call_host(&mut self, host_func: Rc<HostFunction>) -> Result<(), Trap> {
let params = self.store.value_stack.pop_types(host_func.ty.params()).collect::<Box<_>>();
- let res = match host_func.call(FuncContext { store: self.store, module_addr: self.module.idx }, &params) {
+ let res = match host_func.call(FuncContext { store: self.store, module_addr: self.module.idx() }, &params) {
Ok(res) => res,
Err(err) => {
cold_path();
return Err(Trap::HostFunction(Box::new(err)));
}
};
+
self.store.value_stack.extend_from_wasmvalues(&res)?;
self.cf.incr_instr_ptr();
Ok(())
}
- fn exec_call_direct<const IS_RETURN_CALL: bool>(&mut self, v: u32) -> Result<(), Trap> {
+
+ fn exec_call_direct(&mut self, v: u32) -> Result<(), Trap> {
+ self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
+ let addr = self.module.resolve_func_addr(v);
+ match self.store.state.get_func(addr) {
+ crate::FunctionInstance::Wasm(wasm_func) => self.exec_call(wasm_func.clone(), addr),
+ crate::FunctionInstance::Host(host_func) => self.exec_call_host(host_func.clone()),
+ }
+ }
+
+ fn exec_return_call_direct(&mut self, v: u32) -> Result<(), Trap> {
self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
let addr = self.module.resolve_func_addr(v);
match self.store.state.get_func(addr) {
- crate::FunctionInstance::Wasm(wasm_func) => self.exec_call::<IS_RETURN_CALL>(wasm_func.clone(), addr),
+ crate::FunctionInstance::Wasm(wasm_func) => self.exec_return_call(wasm_func.clone(), addr),
crate::FunctionInstance::Host(host_func) => self.exec_call_host(host_func.clone()),
}
}
- fn exec_call_self<const IS_RETURN_CALL: bool>(&mut self) -> Result<(), Trap> {
+ fn exec_call_self(&mut self) -> Result<(), Trap> {
self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
- let params = self.func.params;
- let locals = self.func.locals;
- if IS_RETURN_CALL {
- self.store.value_stack.truncate_keep_counts(self.cf.locals_base, params);
- } else if self.store.call_stack.is_at_limit() {
+ self.store.call_stack.push(self.cf)?;
+ let Ok(locals_base) = self.store.value_stack.enter_locals(&self.func.params, &self.func.locals) else {
cold_path();
return Err(Trap::CallStackOverflow);
- }
+ };
+ self.cf = CallFrame::new(self.cf.func_addr, locals_base, self.func.locals);
+
+ Ok(())
+ }
+
+ fn exec_return_call_self(&mut self) -> Result<(), Trap> {
+ self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
- let locals_base = match self.store.value_stack.enter_locals(&params, &locals) {
+ self.store.value_stack.truncate_keep_counts(self.cf.locals_base, self.func.params);
+ let locals_base = match self.store.value_stack.enter_locals(&self.func.params, &self.func.locals) {
Ok(base) => base,
Err(err) => {
cold_path();
- if IS_RETURN_CALL {
- return Err(err);
- }
- return Err(Trap::CallStackOverflow);
+ return Err(err);
}
};
- let new_call_frame = CallFrame::new(self.cf.func_addr, self.cf.module_addr, locals_base, locals);
- if !IS_RETURN_CALL {
- self.cf.incr_instr_ptr();
- self.store.call_stack.push(self.cf)?;
- }
- self.cf = new_call_frame;
+ self.cf = CallFrame::new(self.cf.func_addr, locals_base, self.func.locals);
Ok(())
}
fn exec_call_indirect<const IS_RETURN_CALL: bool>(&mut self, type_addr: u32, table_addr: u32) -> Result<(), Trap> {
self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
+
// verify that the table is of the right type, this should be validated by the parser already
- let func_ref = {
- let table_idx: u32 = self.store.value_stack.pop::<i32>() as u32;
- let table = self.store.state.get_table(self.module.resolve_table_addr(table_addr));
- assert!(table.kind.element_type == WasmType::RefFunc, "table is not of type funcref");
+ let table_idx: u32 = self.store.value_stack.pop::<i32>() as u32;
+ let table = self.store.state.get_table(self.module.resolve_table_addr(table_addr));
+ debug_assert!(table.kind.element_type == WasmType::RefFunc, "table is not of type funcref");
- let Ok(table) = table.get(table_idx) else {
- cold_path();
- return Err(Trap::UndefinedElement { index: table_idx as usize });
- };
+ let Ok(table) = table.get(table_idx) else {
+ cold_path();
+ return Err(Trap::UndefinedElement { index: table_idx as usize });
+ };
- match table.addr() {
- Some(addr) => addr,
- None => {
- cold_path();
- return Err(Trap::UninitializedElement { index: table_idx as usize });
- }
- }
+ let Some(func_ref) = table.addr() else {
+ cold_path();
+ return Err(Trap::UninitializedElement { index: table_idx as usize });
};
let call_ty = self.module.func_ty(type_addr);
@@ -909,7 +905,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
});
}
- self.exec_call::<IS_RETURN_CALL>(wasm_func.clone(), func_ref)
+ match IS_RETURN_CALL {
+ true => self.exec_return_call(wasm_func.clone(), func_ref),
+ false => self.exec_call(wasm_func.clone(), func_ref),
+ }
}
crate::FunctionInstance::Host(host_func) => {
if host_func.ty != *call_ty {
@@ -931,12 +930,13 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
let Some(cf) = self.store.call_stack.pop() else { return true };
if cf.func_addr != self.cf.func_addr {
- self.func = self.store.state.get_wasm_func(cf.func_addr).clone();
-
- if cf.module_addr != self.module.idx {
- self.module = self.store.get_module_instance_raw(cf.module_addr).clone();
+ let wasm_func = self.store.state.get_wasm_func(cf.func_addr);
+ self.func = wasm_func.func.clone();
+ if wasm_func.owner != self.module.idx() {
+ self.module = self.store.get_module_instance_internal(wasm_func.owner);
}
}
+
self.cf = cf;
false
}
diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs
index 0592e31..03cf5fe 100644
--- a/crates/tinywasm/src/interpreter/stack/call_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs
@@ -2,7 +2,7 @@ use crate::{Result, Trap};
use core::hint::cold_path;
use alloc::vec::Vec;
-use tinywasm_types::{FuncAddr, ModuleInstanceAddr, ValueCounts};
+use tinywasm_types::{FuncAddr, ValueCounts};
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct CallStack {
@@ -27,13 +27,9 @@ impl CallStack {
}
#[inline(always)]
- pub(crate) fn is_at_limit(&self) -> bool {
- self.stack.len() == self.max_size
- }
-
- #[inline(always)]
- pub(crate) fn push(&mut self, call_frame: CallFrame) -> Result<(), Trap> {
+ pub(crate) fn push(&mut self, mut call_frame: CallFrame) -> Result<(), Trap> {
self.ensure_capacity_for(self.stack.len() + 1)?;
+ call_frame.incr_instr_ptr();
self.stack.push(call_frame);
Ok(())
}
@@ -50,13 +46,10 @@ impl CallStack {
}
let target_capacity = required_len.max(self.stack.capacity().max(1).saturating_mul(2)).min(self.max_size);
- match self.stack.try_reserve(target_capacity.saturating_sub(self.stack.len())) {
- Ok(()) => {}
- Err(_) => {
- cold_path();
- return Err(Trap::CallStackOverflow);
- }
- }
+ let Ok(()) = self.stack.try_reserve(target_capacity.saturating_sub(self.stack.len())) else {
+ cold_path();
+ return Err(Trap::CallStackOverflow);
+ };
Ok(())
}
}
@@ -65,7 +58,6 @@ impl CallStack {
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct CallFrame {
pub(crate) instr_ptr: u32,
- pub(crate) module_addr: ModuleInstanceAddr,
pub(crate) func_addr: FuncAddr,
pub(crate) locals_base: StackBase,
pub(crate) stack_offset: ValueCounts,
@@ -80,13 +72,8 @@ pub(crate) struct StackBase {
}
impl CallFrame {
- pub(crate) fn new(
- func_addr: FuncAddr,
- module_addr: ModuleInstanceAddr,
- locals_base: StackBase,
- stack_offset: ValueCounts,
- ) -> Self {
- Self { instr_ptr: 0, func_addr, module_addr, locals_base, stack_offset }
+ pub(crate) fn new(func_addr: FuncAddr, locals_base: StackBase, stack_offset: ValueCounts) -> Self {
+ Self { instr_ptr: 0, func_addr, locals_base, stack_offset }
}
#[inline]
diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs
index 5ef1e10..80ea044 100644
--- a/crates/tinywasm/src/interpreter/stack/value_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs
@@ -38,11 +38,6 @@ impl<T: Copy + Default> Stack<T> {
}
#[inline(always)]
- pub(crate) fn truncate(&mut self, len: usize) {
- self.data.truncate(len);
- }
-
- #[inline(always)]
pub(crate) fn push(&mut self, value: T) -> Result<(), Trap> {
self.ensure_capacity_for(self.data.len() + 1)?;
self.data.push(value);
@@ -97,10 +92,13 @@ impl<T: Copy + Default> Stack<T> {
return;
}
- let keep = (len - n).min(end_keep);
- if keep > 0 {
- self.data.copy_within(len - keep..len, n);
+ if end_keep == 0 {
+ self.data.truncate(n);
+ return;
}
+
+ let keep = (len - n).min(end_keep);
+ self.data.copy_within(len - keep..len, n);
self.data.truncate(n + keep);
}
@@ -111,8 +109,8 @@ impl<T: Copy + Default> Stack<T> {
let start = self.data.len() - param_count;
let end = start + local_count;
self.ensure_capacity_for(end)?;
-
self.data.resize(end, T::default());
+
Ok(start as u32)
}
@@ -231,25 +229,9 @@ impl ValueStack {
}
pub(crate) fn enter_locals(&mut self, params: &ValueCounts, locals: &ValueCounts) -> Result<StackBase, Trap> {
- let len32 = self.stack_32.len();
- let len64 = self.stack_64.len();
-
let locals_base32 = self.stack_32.enter_locals(params.c32 as usize, locals.c32 as usize)?;
- let locals_base64 = match self.stack_64.enter_locals(params.c64 as usize, locals.c64 as usize) {
- Ok(base) => base,
- Err(err) => {
- self.stack_32.truncate(len32);
- return Err(err);
- }
- };
- let locals_base128 = match self.stack_128.enter_locals(params.c128 as usize, locals.c128 as usize) {
- Ok(base) => base,
- Err(err) => {
- self.stack_32.truncate(len32);
- self.stack_64.truncate(len64);
- return Err(err);
- }
- };
+ let locals_base64 = self.stack_64.enter_locals(params.c64 as usize, locals.c64 as usize)?;
+ let locals_base128 = self.stack_128.enter_locals(params.c128 as usize, locals.c128 as usize)?;
Ok(StackBase { s32: locals_base32, s64: locals_base64, s128: locals_base128 })
}
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index 773f4cb..7e70062 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -1,10 +1,9 @@
-use alloc::rc::Rc;
use alloc::sync::Arc;
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
+use core::hint::cold_path;
use core::sync::atomic::{AtomicUsize, Ordering};
use tinywasm_types::*;
-use crate::instance::ModuleInstanceInner;
use crate::interpreter::stack::{CallStack, ValueStack};
use crate::interpreter::{TinyWasmValue, ValueRef};
use crate::{Engine, Error, ModuleInstance, Result, Trap};
@@ -34,7 +33,7 @@ static STORE_ID: AtomicUsize = AtomicUsize::new(0);
/// See <https://webassembly.github.io/spec/core/exec/runtime.html#store>
pub struct Store {
id: usize,
- module_instances: Vec<Rc<ModuleInstanceInner>>,
+ module_instances: Vec<ModuleInstance>,
pub(crate) engine: Engine,
pub(crate) execution_fuel: u32,
@@ -71,14 +70,17 @@ impl Store {
/// Get a module instance by the internal id
pub fn get_module_instance(&self, addr: ModuleInstanceAddr) -> Option<ModuleInstance> {
- Some(ModuleInstance(self.module_instances.get(addr as usize)?.clone()))
+ self.module_instances.get(addr as usize).cloned()
}
#[inline]
- pub(crate) fn get_module_instance_raw(&self, addr: ModuleInstanceAddr) -> &Rc<ModuleInstanceInner> {
+ pub(crate) fn get_module_instance_internal(&self, addr: ModuleInstanceAddr) -> ModuleInstance {
match self.module_instances.get(addr as usize) {
- Some(instance) => instance,
- None => unreachable!("module instance {addr} not found. This should be unreachable"),
+ Some(instance) => instance.clone(),
+ None => {
+ cold_path();
+ unreachable!("module instance {addr} not found. This should be unreachable")
+ }
}
}
}
@@ -114,20 +116,21 @@ impl State {
pub(crate) fn get_func(&self, addr: FuncAddr) -> &FunctionInstance {
match self.funcs.get(addr as usize) {
Some(func) => func,
- None => unreachable!("function {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("function {addr} not found. This should be unreachable")
+ }
}
}
/// Get a wasm function at the actual index in the store, panicking if it's a host function (which should be guaranteed by the validator)
- pub(crate) fn get_wasm_func(&self, addr: FuncAddr) -> &Arc<WasmFunction> {
+ pub(crate) fn get_wasm_func(&self, addr: FuncAddr) -> &WasmFunctionInstance {
match self.funcs.get(addr as usize) {
- Some(func) => match func {
- FunctionInstance::Wasm(wasm_func) => &wasm_func.func,
- FunctionInstance::Host(_) => unreachable!(
- "expected a wasm function at address {addr}, but found a host function. This should be unreachable"
- ),
- },
- None => unreachable!("function {addr} not found. This should be unreachable"),
+ Some(FunctionInstance::Wasm(wasm_func)) => wasm_func,
+ _ => {
+ cold_path();
+ unreachable!("function {addr} not found. This should be unreachable")
+ }
}
}
@@ -135,7 +138,10 @@ impl State {
pub(crate) fn get_mem(&self, addr: MemAddr) -> &MemoryInstance {
match self.memories.get(addr as usize) {
Some(mem) => mem,
- None => unreachable!("memory {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("memory {addr} not found. This should be unreachable")
+ }
}
}
@@ -143,7 +149,10 @@ impl State {
pub(crate) fn get_mem_mut(&mut self, addr: MemAddr) -> &mut MemoryInstance {
match self.memories.get_mut(addr as usize) {
Some(mem) => mem,
- None => unreachable!("memory {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("memory {addr} not found. This should be unreachable")
+ }
}
}
@@ -151,7 +160,10 @@ impl State {
pub(crate) fn get_mems_mut(&mut self, addr: MemAddr, addr2: MemAddr) -> (&mut MemoryInstance, &mut MemoryInstance) {
match self.memories.get_disjoint_mut([addr as usize, addr2 as usize]) {
Ok([mem_a, mem_b]) => (mem_a, mem_b),
- Err(_) => unreachable!("memory {addr} or {addr2} not found. This should be unreachable"),
+ Err(_) => {
+ cold_path();
+ unreachable!("memory {addr} or {addr2} not found. This should be unreachable")
+ }
}
}
@@ -159,7 +171,10 @@ impl State {
pub(crate) fn get_table(&self, addr: TableAddr) -> &TableInstance {
match self.tables.get(addr as usize) {
Some(table) => table,
- None => unreachable!("table {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("table {addr} not found. This should be unreachable")
+ }
}
}
@@ -167,7 +182,10 @@ impl State {
pub(crate) fn get_table_mut(&mut self, addr: TableAddr) -> &mut TableInstance {
match self.tables.get_mut(addr as usize) {
Some(table) => table,
- None => unreachable!("table {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("table {addr} not found. This should be unreachable")
+ }
}
}
@@ -179,7 +197,10 @@ impl State {
) -> (&mut TableInstance, &mut TableInstance) {
match self.tables.get_disjoint_mut([addr as usize, addr2 as usize]) {
Ok([table_a, table_b]) => (table_a, table_b),
- Err(_) => unreachable!("table {addr} or {addr2} not found. This should be unreachable"),
+ Err(_) => {
+ cold_path();
+ unreachable!("table {addr} or {addr2} not found. This should be unreachable")
+ }
}
}
@@ -187,7 +208,10 @@ impl State {
pub(crate) fn get_data_mut(&mut self, addr: DataAddr) -> &mut DataInstance {
match self.data.get_mut(addr as usize) {
Some(data) => data,
- None => unreachable!("data {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("data {addr} not found. This should be unreachable")
+ }
}
}
@@ -195,7 +219,10 @@ impl State {
pub(crate) fn get_elem_mut(&mut self, addr: ElemAddr) -> &mut ElementInstance {
match self.elements.get_mut(addr as usize) {
Some(elem) => elem,
- None => unreachable!("element {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("element {addr} not found. This should be unreachable")
+ }
}
}
@@ -203,7 +230,10 @@ impl State {
pub(crate) fn get_global(&self, addr: GlobalAddr) -> &GlobalInstance {
match self.globals.get(addr as usize) {
Some(global) => global,
- None => unreachable!("global {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("global {addr} not found. This should be unreachable")
+ }
}
}
@@ -211,7 +241,10 @@ impl State {
pub(crate) fn get_global_mut(&mut self, addr: GlobalAddr) -> &mut GlobalInstance {
match self.globals.get_mut(addr as usize) {
Some(global) => global,
- None => unreachable!("global {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("global {addr} not found. This should be unreachable")
+ }
}
}
@@ -219,7 +252,10 @@ impl State {
pub(crate) fn get_global_val(&self, addr: MemAddr) -> TinyWasmValue {
match self.globals.get(addr as usize) {
Some(global) => global.value.get(),
- None => unreachable!("global {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("global {addr} not found. This should be unreachable")
+ }
}
}
@@ -227,7 +263,10 @@ impl State {
pub(crate) fn set_global_val(&mut self, addr: MemAddr, value: TinyWasmValue) {
match self.globals.get_mut(addr as usize) {
Some(global) => global.value.set(value),
- None => unreachable!("global {addr} not found. This should be unreachable"),
+ None => {
+ cold_path();
+ unreachable!("global {addr} not found. This should be unreachable")
+ }
}
}
}
@@ -242,8 +281,8 @@ impl Store {
self.module_instances.len() as ModuleInstanceAddr
}
- pub(crate) fn add_instance(&mut self, instance: Rc<ModuleInstanceInner>) {
- assert!(instance.idx == self.module_instances.len() as ModuleInstanceAddr);
+ pub(crate) fn add_instance(&mut self, instance: ModuleInstance) {
+ debug_assert!(instance.idx() == self.module_instances.len() as ModuleInstanceAddr);
self.module_instances.push(instance);
}
@@ -263,78 +302,83 @@ impl Store {
// Linking related functions
impl Store {
/// Add functions to the store, returning their addresses in the store
- pub(crate) fn init_funcs(&mut self, funcs: &[Arc<WasmFunction>], idx: ModuleInstanceAddr) -> Vec<FuncAddr> {
- let func_count = self.state.funcs.len();
- let mut func_addrs = Vec::with_capacity(func_count);
- for (i, func) in funcs.iter().enumerate() {
- self.state.funcs.push(FunctionInstance::new_wasm(func.clone(), idx));
- func_addrs.push((i + func_count) as FuncAddr);
- }
- func_addrs
+ pub(crate) fn init_funcs(
+ &mut self,
+ funcs: &[Arc<WasmFunction>],
+ idx: ModuleInstanceAddr,
+ ) -> impl ExactSizeIterator<Item = FuncAddr> {
+ let start = self.state.funcs.len() as FuncAddr;
+ self.state.funcs.extend(funcs.iter().map(|func| FunctionInstance::new_wasm(func.clone(), idx)));
+ start..start + funcs.len() as FuncAddr
}
/// Add tables to the store, returning their addresses in the store
- pub(crate) fn init_tables(&mut self, tables: &[TableType], _idx: ModuleInstanceAddr) -> Vec<TableAddr> {
- let table_count = self.state.tables.len();
- let mut table_addrs = Vec::with_capacity(table_count);
- for (i, table) in tables.iter().enumerate() {
- self.state.tables.push(TableInstance::new(table.clone()));
- table_addrs.push((i + table_count) as TableAddr);
- }
- table_addrs
+ pub(crate) fn init_tables(&mut self, tables: &[TableType]) -> impl ExactSizeIterator<Item = TableAddr> {
+ let start = self.state.tables.len() as TableAddr;
+ self.state.tables.extend(tables.iter().map(|table| TableInstance::new(table.clone())));
+ start..start + tables.len() as TableAddr
}
/// Add memories to the store, returning their addresses in the store
- pub(crate) fn init_memories(&mut self, memories: &[MemoryType], _idx: ModuleInstanceAddr) -> Result<Vec<MemAddr>> {
- let mem_count = self.state.memories.len();
- let mut mem_addrs = Vec::with_capacity(mem_count);
- for (i, mem) in memories.iter().enumerate() {
- self.state.memories.push(MemoryInstance::new(*mem, &self.engine.config().memory_backend)?);
- mem_addrs.push((i + mem_count) as MemAddr);
+ pub(crate) fn init_memories(&mut self, memories: &[MemoryType]) -> 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(MemoryInstance::new(mem, &self.engine.config().memory_backend)?);
}
- Ok(mem_addrs)
+ Ok(start..start + memories.len() as MemAddr)
}
pub(crate) fn init_lazy_memories(
&mut self,
memories: &[MemoryType],
- _idx: ModuleInstanceAddr,
- ) -> Result<Vec<MemAddr>> {
- let mem_count = self.state.memories.len();
- let mut mem_addrs = Vec::with_capacity(mem_count);
- for (i, mem) in memories.iter().enumerate() {
- self.state.memories.push(MemoryInstance::new_lazy(*mem, &self.engine.config().memory_backend)?);
- mem_addrs.push((i + mem_count) as MemAddr);
+ ) -> 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(MemoryInstance::new_lazy(mem, &self.engine.config().memory_backend)?);
}
- Ok(mem_addrs)
+ Ok(start..start + memories.len() as MemAddr)
}
/// Add globals to the store, returning their addresses in the store
pub(crate) fn init_globals(
&mut self,
- mut imported_globals: Vec<GlobalAddr>,
+ out: &mut Vec<Addr>,
new_globals: &[Global],
func_addrs: &[FuncAddr],
- _idx: ModuleInstanceAddr,
- ) -> Result<Vec<Addr>> {
- let global_count = self.state.globals.len();
- imported_globals.reserve_exact(new_globals.len());
- let mut global_addrs = imported_globals;
+ ) -> Result<()> {
+ let start = self.state.globals.len() as Addr;
+ out.reserve_exact(new_globals.len());
+ self.state.globals.reserve_exact(new_globals.len());
for (i, global) in new_globals.iter().enumerate() {
- let value = self.eval_const(&global.init, &global_addrs, func_addrs)?;
+ let value = match self.eval_const(&global.init, out, func_addrs) {
+ Ok(val) => val,
+ Err(e) => {
+ cold_path();
+ return Err(e);
+ }
+ };
+
self.state.globals.push(GlobalInstance::new(global.ty, value));
- global_addrs.push((i + global_count) as Addr);
+ out.push(start + i as Addr);
}
- Ok(global_addrs)
+ Ok(())
}
fn elem_addr(&self, item: &ElementItem, globals: &[Addr], funcs: &[FuncAddr]) -> Result<Option<u32>> {
let res = match item {
- ElementItem::Func(addr) => Some(funcs.get(*addr as usize).copied().ok_or_else(|| {
- Error::Other(format!("function {addr} not found. This should have been caught by the validator"))
- })?),
+ ElementItem::Func(addr) => match funcs.get(*addr as usize) {
+ Some(func_addr) => Some(*func_addr),
+ None => {
+ cold_path();
+ return Err(Error::Other(format!(
+ "function {addr} not found. This should have been caught by the validator"
+ )));
+ }
+ },
ElementItem::Expr(expr) => self.eval_ref_const(expr, globals, funcs)?,
};
@@ -349,7 +393,6 @@ impl Store {
func_addrs: &[FuncAddr],
global_addrs: &[Addr],
elements: &[Element],
- _idx: ModuleInstanceAddr,
) -> Result<(Box<[Addr]>, Option<Trap>)> {
let elem_count = self.state.elements.len();
let mut elem_addrs = Vec::with_capacity(elem_count);
@@ -408,7 +451,6 @@ impl Store {
global_addrs: &[Addr],
func_addrs: &[FuncAddr],
data: &[Data],
- _idx: ModuleInstanceAddr,
) -> Result<(Box<[Addr]>, Option<Trap>)> {
let data_count = self.state.data.len();
let mut data_addrs = Vec::with_capacity(data_count);
@@ -479,21 +521,31 @@ impl Store {
use tinywasm_types::ConstInstruction::*;
let resolve_global = |idx: u32| -> Result<TinyWasmValue> {
- let addr = module_global_addrs.get(idx as usize).ok_or_else(|| {
- Error::Other(format!("global {idx} not found. This should have been caught by the validator"))
- })?;
- let global = self
- .state
- .globals
- .get(*addr as usize)
- .ok_or_else(|| Error::Other(format!("global {addr} not found")))?;
+ let Some(addr) = module_global_addrs.get(idx as usize) else {
+ cold_path();
+ return Err(Error::Other(format!(
+ "global {idx} not found. This should have been caught by the validator"
+ )));
+ };
+
+ let Some(global) = self.state.globals.get(*addr as usize) else {
+ cold_path();
+ return Err(Error::Other(format!("global {addr} not found")));
+ };
+
Ok(global.value.get())
};
let resolve_func = |idx: u32| -> Result<u32> {
- module_func_addrs.get(idx as usize).copied().ok_or_else(|| {
- Error::Other(format!("function {idx} not found. This should have been caught by the validator"))
- })
+ match module_func_addrs.get(idx as usize).copied() {
+ Some(func_addr) => Ok(func_addr),
+ None => {
+ cold_path();
+ Err(Error::Other(format!(
+ "function {idx} not found. This should have been caught by the validator"
+ )))
+ }
+ }
};
if const_instrs.len() == 1 {
@@ -507,8 +559,12 @@ impl Store {
RefFunc(None) => TinyWasmValue::ValueRef(ValueRef::NULL),
RefExtern(None) => TinyWasmValue::ValueRef(ValueRef::NULL),
RefFunc(Some(idx)) => TinyWasmValue::ValueRef(ValueRef::from_addr(Some(resolve_func(*idx)?))),
- _ => return Err(Error::Other("unsupported const instruction".to_string())),
+ _ => {
+ cold_path();
+ return Err(Error::Other("unsupported const instruction".to_string()));
+ }
};
+
return Ok(val);
}
@@ -526,12 +582,14 @@ impl Store {
stack.push(TinyWasmValue::ValueRef(ValueRef::from_addr(Some(resolve_func(*idx)?))))
}
RefExtern(Some(_)) => {
+ cold_path();
return Err(Error::Other("ref.extern constants are not supported in init expressions".to_string()));
}
I32Add | I32Sub | I32Mul => {
let rhs = stack.pop().ok_or_else(|| Error::Other("const stack underflow".to_string()))?;
let lhs = stack.pop().ok_or_else(|| Error::Other("const stack underflow".to_string()))?;
let (TinyWasmValue::Value32(lhs), TinyWasmValue::Value32(rhs)) = (lhs, rhs) else {
+ cold_path();
return Err(Error::Other("type mismatch in const i32 op".to_string()));
};
let lhs = lhs as i32;
@@ -545,26 +603,36 @@ impl Store {
stack.push(TinyWasmValue::Value32(out as u32));
}
I64Add | I64Sub | I64Mul => {
- let rhs = stack.pop().ok_or_else(|| Error::Other("const stack underflow".to_string()))?;
- let lhs = stack.pop().ok_or_else(|| Error::Other("const stack underflow".to_string()))?;
- let (TinyWasmValue::Value64(lhs), TinyWasmValue::Value64(rhs)) = (lhs, rhs) else {
+ let rhs = stack.pop();
+ let lhs = stack.pop();
+ let (Some(TinyWasmValue::Value64(lhs)), Some(TinyWasmValue::Value64(rhs))) = (lhs, rhs) else {
+ cold_path();
return Err(Error::Other("type mismatch in const i64 op".to_string()));
};
+
let lhs = lhs as i64;
let rhs = rhs as i64;
let out = match instr {
I64Add => lhs.wrapping_add(rhs),
I64Sub => lhs.wrapping_sub(rhs),
I64Mul => lhs.wrapping_mul(rhs),
- _ => unreachable!(),
+ _ => {
+ cold_path();
+ unreachable!("invalid const instruction in i64 op")
+ }
};
stack.push(TinyWasmValue::Value64(out as u64));
}
}
}
- let value = stack.pop().ok_or_else(|| Error::Other("empty const expression".to_string()))?;
+ let Some(value) = stack.pop() else {
+ cold_path();
+ return Err(Error::Other("empty const expression".to_string()));
+ };
+
if !stack.is_empty() {
+ cold_path();
return Err(Error::Other("const expression did not reduce to single value".to_string()));
}
Ok(value)
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 6196beb..3eb14ba 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -93,7 +93,7 @@ pub struct ModuleInner {
/// Optimized and validated WebAssembly functions
///
/// Contains data from to the `code`, `func`, and `type` sections of the original WebAssembly module.
- pub funcs: Arc<[Arc<WasmFunction>]>,
+ pub funcs: Box<[Arc<WasmFunction>]>,
/// A vector of type definitions, indexed by `TypeAddr`
///
@@ -108,32 +108,32 @@ pub struct ModuleInner {
/// Global components of the WebAssembly module.
///
/// Corresponds to the `global` section of the original WebAssembly module.
- pub globals: Arc<[Global]>,
+ pub globals: Box<[Global]>,
/// Table components of the WebAssembly module used to initialize tables.
///
/// Corresponds to the `table` section of the original WebAssembly module.
- pub table_types: Arc<[TableType]>,
+ pub table_types: Box<[TableType]>,
/// Memory components of the WebAssembly module used to initialize memories.
///
/// Corresponds to the `memory` section of the original WebAssembly module.
- pub memory_types: Arc<[MemoryType]>,
+ pub memory_types: Box<[MemoryType]>,
/// Imports of the WebAssembly module.
///
/// Corresponds to the `import` section of the original WebAssembly module.
- pub imports: Arc<[Import]>,
+ pub imports: Box<[Import]>,
/// Data segments of the WebAssembly module.
///
/// Corresponds to the `data` section of the original WebAssembly module.
- pub data: Arc<[Data]>,
+ pub data: Box<[Data]>,
/// Element segments of the WebAssembly module.
///
/// Corresponds to the `elem` section of the original WebAssembly module.
- pub elements: Arc<[Element]>,
+ pub elements: Box<[Element]>,
/// How instantiation should prepare the module's local memories.
pub local_memory_allocation: LocalMemoryAllocation,
@@ -397,15 +397,16 @@ impl ValueCounts {
impl<'a> FromIterator<&'a WasmType> for ValueCounts {
#[inline]
fn from_iter<I: IntoIterator<Item = &'a WasmType>>(iter: I) -> Self {
- iter.into_iter().fold(Self::default(), |mut counts, ty| {
+ let mut counts = Self::default();
+
+ for ty in iter {
match ty {
- WasmType::I32 | WasmType::F32 => counts.c32 += 1,
+ WasmType::I32 | WasmType::F32 | WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1,
WasmType::I64 | WasmType::F64 => counts.c64 += 1,
WasmType::V128 => counts.c128 += 1,
- WasmType::RefExtern | WasmType::RefFunc => counts.c32 += 1,
}
- counts
- })
+ }
+ counts
}
}