summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/benchmarks/benches/argon2id.rs6
-rw-r--r--crates/parser/src/visit.rs46
-rw-r--r--crates/tinywasm/src/func.rs73
-rw-r--r--crates/tinywasm/src/imports.rs1
-rw-r--r--crates/tinywasm/src/runtime/interpreter/macros.rs79
-rw-r--r--crates/tinywasm/src/runtime/interpreter/mod.rs115
-rw-r--r--crates/tinywasm/src/runtime/interpreter/no_std_floats.rs97
-rw-r--r--crates/tinywasm/src/runtime/interpreter/traits.rs44
-rw-r--r--crates/tinywasm/src/runtime/stack/call_stack.rs2
-rw-r--r--crates/tinywasm/src/runtime/stack/value_stack.rs12
-rw-r--r--crates/tinywasm/src/store/mod.rs79
-rw-r--r--crates/tinywasm/src/store/table.rs4
-rw-r--r--crates/types/src/instructions.rs2
-rw-r--r--crates/types/src/value.rs2
14 files changed, 220 insertions, 342 deletions
diff --git a/crates/benchmarks/benches/argon2id.rs b/crates/benchmarks/benches/argon2id.rs
index 7c1ffc5..a503687 100644
--- a/crates/benchmarks/benches/argon2id.rs
+++ b/crates/benchmarks/benches/argon2id.rs
@@ -45,10 +45,10 @@ fn criterion_benchmark(c: &mut Criterion) {
group.measurement_time(std::time::Duration::from_secs(7));
group.sample_size(10);
- group.bench_function("native", |b| b.iter(|| run_native(black_box(params))));
+ // group.bench_function("native", |b| b.iter(|| run_native(black_box(params))));
group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(params), "argon2id")));
- group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id")));
- group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id")));
+ // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id")));
+ // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id")));
}
criterion_group!(
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index edebc97..88b6ba5 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -340,16 +340,17 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
}
fn visit_local_set(&mut self, idx: u32) -> Self::Output {
- if let Some(instruction) = self.instructions.last_mut() {
- match instruction {
- // Needs more testing, seems to make performance worse
- // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx),
- _ => return self.visit(Instruction::LocalSet(idx)),
- };
- // Ok(())
- } else {
- self.visit(Instruction::LocalSet(idx))
- }
+ self.visit(Instruction::LocalSet(idx))
+ // if let Some(instruction) = self.instructions.last_mut() {
+ // match instruction {
+ // // Needs more testing, seems to make performance worse
+ // // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx),
+ // _ => return self.visit(Instruction::LocalSet(idx)),
+ // };
+ // // Ok(())
+ // } else {
+ // self.visit(Instruction::LocalSet(idx))
+ // }
}
fn visit_local_tee(&mut self, idx: u32) -> Self::Output {
@@ -372,18 +373,19 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
}
fn visit_i32_add(&mut self) -> Self::Output {
- if self.instructions.len() < 2 {
- return self.visit(Instruction::I32Add);
- }
+ self.visit(Instruction::I32Add)
+ // if self.instructions.len() < 2 {
+ // return self.visit(Instruction::I32Add);
+ // }
- match self.instructions[self.instructions.len() - 2..] {
- // [Instruction::LocalGet(a), Instruction::I32Const(b)] => {
- // self.instructions.pop();
- // self.instructions.pop();
- // self.visit(Instruction::I32LocalGetConstAdd(a, b))
- // }
- _ => self.visit(Instruction::I32Add),
- }
+ // match self.instructions[self.instructions.len() - 2..] {
+ // // [Instruction::LocalGet(a), Instruction::I32Const(b)] => {
+ // // self.instructions.pop();
+ // // self.instructions.pop();
+ // // self.visit(Instruction::I32LocalGetConstAdd(a, b))
+ // // }
+ // _ => self.visit(Instruction::I32Add),
+ // }
}
fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output {
@@ -416,7 +418,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
match self.instructions[label_pointer] {
Instruction::Else(ref mut else_instr_end_offset) => {
- *else_instr_end_offset = (current_instr_ptr - label_pointer as usize)
+ *else_instr_end_offset = (current_instr_ptr - label_pointer)
.try_into()
.expect("else_instr_end_offset is too large, tinywasm does not support if blocks that large");
diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs
index 757cb85..d7f7ca1 100644
--- a/crates/tinywasm/src/func.rs
+++ b/crates/tinywasm/src/func.rs
@@ -22,6 +22,9 @@ impl FuncHandle {
/// See <https://webassembly.github.io/spec/core/exec/modules.html#invocation>
#[inline]
pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result<Vec<WasmValue>> {
+ // Comments are ordered by the steps in the spec
+ // In this implementation, some steps are combined and ordered differently for performance reasons
+
// 3. Let func_ty be the function type
let func_ty = &self.ty;
@@ -35,7 +38,7 @@ impl FuncHandle {
}
// 5. For each value type and the corresponding value, check if types match
- if !unlikely(func_ty.params.iter().zip(params).enumerate().all(|(i, (ty, param))| {
+ if !(func_ty.params.iter().zip(params).enumerate().all(|(i, (ty, param))| {
if ty != &param.val_type() {
log::error!("param type mismatch at index {}: expected {:?}, got {:?}", i, ty, param);
false
@@ -57,8 +60,8 @@ impl FuncHandle {
};
// 6. Let f be the dummy frame
- let call_frame =
- CallFrame::new(wasm_func.clone(), func_inst.owner, params.iter().map(|v| RawWasmValue::from(*v)), 0);
+ let call_frame_params = params.iter().map(|v| RawWasmValue::from(*v));
+ let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, call_frame_params, 0);
// 7. Push the frame f to the call stack
// & 8. Push the values to the stack (Not needed since the call frame owns the values)
@@ -113,6 +116,7 @@ impl<P: IntoWasmValueTuple, R: FromWasmValueTuple> FuncHandleTyped<P, R> {
R::from_wasm_value_tuple(&result)
}
}
+
macro_rules! impl_into_wasm_value_tuple {
($($T:ident),*) => {
impl<$($T),*> IntoWasmValueTuple for ($($T,)*)
@@ -140,21 +144,6 @@ macro_rules! impl_into_wasm_value_tuple_single {
};
}
-impl_into_wasm_value_tuple_single!(i32);
-impl_into_wasm_value_tuple_single!(i64);
-impl_into_wasm_value_tuple_single!(f32);
-impl_into_wasm_value_tuple_single!(f64);
-
-impl_into_wasm_value_tuple!();
-impl_into_wasm_value_tuple!(T1);
-impl_into_wasm_value_tuple!(T1, T2);
-impl_into_wasm_value_tuple!(T1, T2, T3);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
-
macro_rules! impl_from_wasm_value_tuple {
($($T:ident),*) => {
impl<$($T),*> FromWasmValueTuple for ($($T,)*)
@@ -200,21 +189,6 @@ macro_rules! impl_from_wasm_value_tuple_single {
};
}
-impl_from_wasm_value_tuple_single!(i32);
-impl_from_wasm_value_tuple_single!(i64);
-impl_from_wasm_value_tuple_single!(f32);
-impl_from_wasm_value_tuple_single!(f64);
-
-impl_from_wasm_value_tuple!();
-impl_from_wasm_value_tuple!(T1);
-impl_from_wasm_value_tuple!(T1, T2);
-impl_from_wasm_value_tuple!(T1, T2, T3);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
-
pub trait ValTypesFromTuple {
fn val_types() -> Box<[ValType]>;
}
@@ -268,19 +242,42 @@ impl ValTypesFromTuple for () {
}
}
-impl<T1> ValTypesFromTuple for T1
-where
- T1: ToValType,
-{
+impl<T: ToValType> ValTypesFromTuple for T {
#[inline]
fn val_types() -> Box<[ValType]> {
- Box::new([T1::to_val_type()])
+ Box::new([T::to_val_type()])
}
}
+impl_from_wasm_value_tuple_single!(i32);
+impl_from_wasm_value_tuple_single!(i64);
+impl_from_wasm_value_tuple_single!(f32);
+impl_from_wasm_value_tuple_single!(f64);
+
+impl_into_wasm_value_tuple_single!(i32);
+impl_into_wasm_value_tuple_single!(i64);
+impl_into_wasm_value_tuple_single!(f32);
+impl_into_wasm_value_tuple_single!(f64);
+
impl_val_types_from_tuple!(T1);
impl_val_types_from_tuple!(T1, T2);
impl_val_types_from_tuple!(T1, T2, T3);
impl_val_types_from_tuple!(T1, T2, T3, T4);
impl_val_types_from_tuple!(T1, T2, T3, T4, T5);
impl_val_types_from_tuple!(T1, T2, T3, T4, T5, T6);
+
+impl_from_wasm_value_tuple!();
+impl_from_wasm_value_tuple!(T1);
+impl_from_wasm_value_tuple!(T1, T2);
+impl_from_wasm_value_tuple!(T1, T2, T3);
+impl_from_wasm_value_tuple!(T1, T2, T3, T4);
+impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5);
+impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
+
+impl_into_wasm_value_tuple!();
+impl_into_wasm_value_tuple!(T1);
+impl_into_wasm_value_tuple!(T1, T2);
+impl_into_wasm_value_tuple!(T1, T2, T3);
+impl_into_wasm_value_tuple!(T1, T2, T3, T4);
+impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5);
+impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index 522a82d..1c5ca98 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -157,7 +157,6 @@ impl Extern {
};
let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() };
-
Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty })))
}
diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs
index 9227ceb..30f34fc 100644
--- a/crates/tinywasm/src/runtime/interpreter/macros.rs
+++ b/crates/tinywasm/src/runtime/interpreter/macros.rs
@@ -12,10 +12,9 @@
macro_rules! break_to {
($cf:ident, $stack:ident, $break_to_relative:ident) => {{
if $cf.break_to(*$break_to_relative, &mut $stack.values, &mut $stack.blocks).is_none() {
- if $stack.call_stack.is_empty() {
- return Ok(ExecResult::Return);
- } else {
- return Ok(ExecResult::Call);
+ match $stack.call_stack.is_empty() {
+ true => return Ok(ExecResult::Return),
+ false => return Ok(ExecResult::Call),
}
}
}};
@@ -62,21 +61,15 @@ macro_rules! mem_load {
/// Store a value to memory
macro_rules! mem_store {
($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{
- log::debug!("mem_store!({}, {:?})", stringify!($type), $arg);
mem_store!($type, $type, $arg, $stack, $store, $module)
}};
($store_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{
let (mem_addr, offset) = $arg;
- let mem_idx = $module.resolve_mem_addr(*mem_addr);
- let mem = $store.get_mem(mem_idx as usize)?;
-
- let val = $stack.values.pop_t::<$store_type>()?;
- let addr: u64 = $stack.values.pop()?.into();
-
- let val = val as $store_type;
+ let mem = $store.get_mem($module.resolve_mem_addr(*mem_addr) as usize)?;
+ let val: $store_type = $stack.values.pop()?.into();
let val = val.to_le_bytes();
-
+ let addr: u64 = $stack.values.pop()?.into();
mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?;
}};
}
@@ -103,13 +96,11 @@ macro_rules! float_min_max {
/// Convert a value on the stack
macro_rules! conv {
- ($from:ty, $intermediate:ty, $to:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$from>()? as $intermediate;
- $stack.values.push((a as $to).into());
- }};
($from:ty, $to:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$from>()?;
- $stack.values.push((a as $to).into());
+ $stack.values.replace_top(|v| {
+ let a: $from = v.into();
+ (a as $to).into()
+ });
}};
}
@@ -138,13 +129,9 @@ macro_rules! checked_conv_float {
/// Compare two values on the stack
macro_rules! comp {
- ($op:tt, $ty:ty, $stack:ident) => {{
- comp!($op, $ty, $ty, $stack)
- }};
-
- ($op:tt, $intermediate:ty, $to:ty, $stack:ident) => {{
- let b = $stack.values.pop_t::<$intermediate>()? as $to;
- let a = $stack.values.pop_t::<$intermediate>()? as $to;
+ ($op:tt, $to:ty, $stack:ident) => {{
+ let b: $to = $stack.values.pop()?.into();
+ let a: $to = $stack.values.pop()?.into();
$stack.values.push(((a $op b) as i32).into());
}};
}
@@ -152,62 +139,52 @@ macro_rules! comp {
/// Compare a value on the stack to zero
macro_rules! comp_zero {
($op:tt, $ty:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$ty>()?;
+ let a: $ty = $stack.values.pop()?.into();
$stack.values.push(((a $op 0) as i32).into());
}};
}
/// Apply an arithmetic method to two values on the stack
macro_rules! arithmetic {
- ($op:ident, $ty:ty, $stack:ident) => {
- arithmetic!($op, $ty, $ty, $stack)
- };
+ ($op:ident, $to:ty, $stack:ident) => {{
+ let b: $to = $stack.values.pop()?.into();
+ let a: $to = $stack.values.pop()?.into();
+ $stack.values.push((a.$op(b) as $to).into());
+ }};
// also allow operators such as +, -
($op:tt, $ty:ty, $stack:ident) => {{
- let b: $ty = $stack.values.pop_t()?;
- let a: $ty = $stack.values.pop_t()?;
+ let b: $ty = $stack.values.pop()?.into();
+ let a: $ty = $stack.values.pop()?.into();
$stack.values.push((a $op b).into());
}};
-
- ($op:ident, $intermediate:ty, $to:ty, $stack:ident) => {{
- let b = $stack.values.pop_t::<$to>()? as $intermediate;
- let a = $stack.values.pop_t::<$to>()? as $intermediate;
- $stack.values.push((a.$op(b) as $to).into());
- }};
}
/// Apply an arithmetic method to a single value on the stack
macro_rules! arithmetic_single {
($op:ident, $ty:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$ty>()?;
+ let a: $ty = $stack.values.pop()?.into();
$stack.values.push((a.$op() as $ty).into());
}};
($op:ident, $from:ty, $to:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$from>()?;
+ let a: $from = $stack.values.pop()?.into();
$stack.values.push((a.$op() as $to).into());
}};
}
/// Apply an arithmetic operation to two values on the stack with error checking
macro_rules! checked_int_arithmetic {
- // Direct conversion with error checking (two types)
- ($from:tt, $to:tt, $stack:ident) => {{
- checked_int_arithmetic!($from, $to, $to, $stack)
- }};
-
- ($op:ident, $from:ty, $to:ty, $stack:ident) => {{
- let b = $stack.values.pop_t::<$from>()? as $to;
- let a = $stack.values.pop_t::<$from>()? as $to;
+ ($op:ident, $to:ty, $stack:ident) => {{
+ let b: $to = $stack.values.pop()?.into();
+ let a: $to = $stack.values.pop()?.into();
- if b == 0 {
+ if unlikely(b == 0) {
return Err(Error::Trap(crate::Trap::DivisionByZero));
}
let result = a.$op(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow))?;
- // Cast back to original type if different
- $stack.values.push((result as $from).into());
+ $stack.values.push((result).into());
}};
}
diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs
index bd07051..007acaa 100644
--- a/crates/tinywasm/src/runtime/interpreter/mod.rs
+++ b/crates/tinywasm/src/runtime/interpreter/mod.rs
@@ -79,14 +79,14 @@ enum ExecResult {
#[inline(always)]
fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &ModuleInstance) -> Result<ExecResult> {
let instrs = &cf.func_instance.0.instructions;
+
if unlikely(cf.instr_ptr >= instrs.len() || instrs.is_empty()) {
- cold();
log::error!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len());
return Err(Error::Other(format!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len())));
}
// A match statement is probably the fastest way to do this without
- // unreasonable complexity
+ // unreasonable complexity. This *should* be optimized to a jump table.
// See https://pliniker.github.io/post/dispatchers/
use tinywasm_types::Instruction::*;
match cf.current_instruction() {
@@ -144,14 +144,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
let table_idx = stack.values.pop_t::<u32>()?;
// verify that the table is of the right type, this should be validated by the parser already
- assert!(table.borrow().kind.element_type == ValType::RefFunc, "table is not of type funcref");
-
let func_ref = {
- table
- .borrow()
- .get(table_idx as usize)?
- .addr()
- .ok_or(Trap::UninitializedElement { index: table_idx as usize })?
+ let table = table.borrow();
+ assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref");
+ table.get(table_idx as usize)?.addr().ok_or(Trap::UninitializedElement { index: table_idx as usize })?
};
let func_inst = store.get_func(func_ref as usize)?.clone();
@@ -238,7 +234,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
cf.instr_ptr + *end_offset as usize,
stack.values.len(),
BlockType::Loop,
- &args,
+ args,
module,
),
&mut stack.values,
@@ -251,9 +247,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
BlockFrame::new(
cf.instr_ptr,
cf.instr_ptr + *end_offset as usize,
- stack.values.len(), // - params,
+ stack.values.len(),
BlockType::Block,
- &args,
+ args,
module,
),
&mut stack.values,
@@ -282,7 +278,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
}
let idx = stack.values.pop_t::<i32>()? as usize;
- let to = instr.get(idx).unwrap_or(&default);
+ let to = instr.get(idx).unwrap_or(default);
break_to!(cf, stack, to);
}
@@ -335,13 +331,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
LocalGet(local_index) => stack.values.push(cf.get_local(*local_index as usize)),
LocalSet(local_index) => cf.set_local(*local_index as usize, stack.values.pop()?),
LocalTee(local_index) => {
+ let local = stack.values.last();
cf.set_local(
*local_index as usize,
- stack
- .values
- .last()
- .expect("localtee: stack is empty. this should have been validated by the parser")
- .clone(),
+ *local.expect("localtee: stack is empty. this should have been validated by the parser"),
);
}
@@ -362,8 +355,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
F64Const(val) => stack.values.push((*val).into()),
MemorySize(addr, byte) => {
- if *byte != 0 {
- cold();
+ if unlikely(*byte != 0) {
return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string()));
}
@@ -373,8 +365,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
}
MemoryGrow(addr, byte) => {
- if *byte != 0 {
- cold();
+ if unlikely(*byte != 0) {
return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string()));
}
@@ -395,9 +386,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
// Bulk memory operations
MemoryCopy(from, to) => {
- let size = stack.values.pop_t::<i32>()?;
- let src = stack.values.pop_t::<i32>()?;
- let dst = stack.values.pop_t::<i32>()?;
+ let size: i32 = stack.values.pop()?.into();
+ let src: i32 = stack.values.pop()?.into();
+ let dst: i32 = stack.values.pop()?.into();
let mem = store.get_mem(module.resolve_mem_addr(*from) as usize)?;
let mut mem = mem.borrow_mut();
@@ -414,9 +405,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
}
MemoryFill(addr) => {
- let size = stack.values.pop_t::<i32>()?;
- let val = stack.values.pop_t::<i32>()?;
- let dst = stack.values.pop_t::<i32>()?;
+ let size: i32 = stack.values.pop()?.into();
+ let val: i32 = stack.values.pop()?.into();
+ let dst: i32 = stack.values.pop()?.into();
let mem = store.get_mem(module.resolve_mem_addr(*addr) as usize)?;
let mut mem = mem.borrow_mut();
@@ -428,26 +419,20 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
let offset = stack.values.pop_t::<i32>()? as usize;
let dst = stack.values.pop_t::<i32>()? as usize;
- let data_idx = module.resolve_data_addr(*data_index);
- let Some(ref data) = store.get_data(data_idx as usize)?.data else {
- cold();
- return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into());
+ let data = match &store.get_data(module.resolve_data_addr(*data_index) as usize)?.data {
+ Some(data) => data,
+ None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()),
};
- let mem_idx = module.resolve_mem_addr(*mem_index);
- let mem = store.get_mem(mem_idx as usize)?;
-
- let data_len = data.len();
- if offset + size > data_len {
- cold();
- return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data_len }.into());
+ if unlikely(offset + size > data.len()) {
+ return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data.len() }.into());
}
+ let mem = store.get_mem(module.resolve_mem_addr(*mem_index) as usize)?;
let mut mem = mem.borrow_mut();
- let data = &data[offset..(offset + size)];
// mem.store checks bounds
- mem.store(dst, size, data)?;
+ mem.store(dst, size, &data[offset..(offset + size)])?;
}
DataDrop(data_index) => {
@@ -496,29 +481,29 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
I32LtS => comp!(<, i32, stack),
I64LtS => comp!(<, i64, stack),
- I32LtU => comp!(<, i32, u32, stack),
- I64LtU => comp!(<, i64, u64, stack),
+ I32LtU => comp!(<, u32, stack),
+ I64LtU => comp!(<, u64, stack),
F32Lt => comp!(<, f32, stack),
F64Lt => comp!(<, f64, stack),
I32LeS => comp!(<=, i32, stack),
I64LeS => comp!(<=, i64, stack),
- I32LeU => comp!(<=, i32, u32, stack),
- I64LeU => comp!(<=, i64, u64, stack),
+ I32LeU => comp!(<=, u32, stack),
+ I64LeU => comp!(<=, u64, stack),
F32Le => comp!(<=, f32, stack),
F64Le => comp!(<=, f64, stack),
I32GeS => comp!(>=, i32, stack),
I64GeS => comp!(>=, i64, stack),
- I32GeU => comp!(>=, i32, u32, stack),
- I64GeU => comp!(>=, i64, u64, stack),
+ I32GeU => comp!(>=, u32, stack),
+ I64GeU => comp!(>=, u64, stack),
F32Ge => comp!(>=, f32, stack),
F64Ge => comp!(>=, f64, stack),
I32GtS => comp!(>, i32, stack),
I64GtS => comp!(>, i64, stack),
- I32GtU => comp!(>, i32, u32, stack),
- I64GtU => comp!(>, i64, u64, stack),
+ I32GtU => comp!(>, u32, stack),
+ I64GtU => comp!(>, u64, stack),
F32Gt => comp!(>, f32, stack),
F64Gt => comp!(>, f64, stack),
@@ -543,13 +528,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
// these can trap
I32DivS => checked_int_arithmetic!(checked_div, i32, stack),
I64DivS => checked_int_arithmetic!(checked_div, i64, stack),
- I32DivU => checked_int_arithmetic!(checked_div, i32, u32, stack),
- I64DivU => checked_int_arithmetic!(checked_div, i64, u64, stack),
+ I32DivU => checked_int_arithmetic!(checked_div, u32, stack),
+ I64DivU => checked_int_arithmetic!(checked_div, u64, stack),
I32RemS => checked_int_arithmetic!(checked_wrapping_rem, i32, stack),
I64RemS => checked_int_arithmetic!(checked_wrapping_rem, i64, stack),
- I32RemU => checked_int_arithmetic!(checked_wrapping_rem, i32, u32, stack),
- I64RemU => checked_int_arithmetic!(checked_wrapping_rem, i64, u64, stack),
+ I32RemU => checked_int_arithmetic!(checked_wrapping_rem, u32, stack),
+ I64RemU => checked_int_arithmetic!(checked_wrapping_rem, u64, stack),
I32And => arithmetic!(bitand, i32, stack),
I64And => arithmetic!(bitand, i64, stack),
@@ -561,8 +546,8 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
I64Shl => arithmetic!(wasm_shl, i64, stack),
I32ShrS => arithmetic!(wasm_shr, i32, stack),
I64ShrS => arithmetic!(wasm_shr, i64, stack),
- I32ShrU => arithmetic!(wasm_shr, u32, i32, stack),
- I64ShrU => arithmetic!(wasm_shr, u64, i64, stack),
+ I32ShrU => arithmetic!(wasm_shr, u32, stack),
+ I64ShrU => arithmetic!(wasm_shr, u64, stack),
I32Rotl => arithmetic!(wasm_rotl, i32, stack),
I64Rotl => arithmetic!(wasm_rotl, i64, stack),
I32Rotr => arithmetic!(wasm_rotr, i32, stack),
@@ -579,16 +564,16 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
F32ConvertI64S => conv!(i64, f32, stack),
F64ConvertI32S => conv!(i32, f64, stack),
F64ConvertI64S => conv!(i64, f64, stack),
- F32ConvertI32U => conv!(i32, u32, f32, stack),
- F32ConvertI64U => conv!(i64, u64, f32, stack),
- F64ConvertI32U => conv!(i32, u32, f64, stack),
- F64ConvertI64U => conv!(i64, u64, f64, stack),
- I32Extend8S => conv!(i32, i8, i32, stack),
- I32Extend16S => conv!(i32, i16, i32, stack),
- I64Extend8S => conv!(i64, i8, i64, stack),
- I64Extend16S => conv!(i64, i16, i64, stack),
- I64Extend32S => conv!(i64, i32, i64, stack),
- I64ExtendI32U => conv!(i32, u32, i64, stack),
+ F32ConvertI32U => conv!(u32, f32, stack),
+ F32ConvertI64U => conv!(u64, f32, stack),
+ F64ConvertI32U => conv!(u32, f64, stack),
+ F64ConvertI64U => conv!(u64, f64, stack),
+ I32Extend8S => conv!(i8, i32, stack),
+ I32Extend16S => conv!(i16, i32, stack),
+ I64Extend8S => conv!(i8, i64, stack),
+ I64Extend16S => conv!(i16, i64, stack),
+ I64Extend32S => conv!(i32, i64, stack),
+ I64ExtendI32U => conv!(u32, i64, stack),
I64ExtendI32S => conv!(i32, i64, stack),
I32WrapI64 => conv!(i64, i32, stack),
diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
index 91c74b5..5b9471e 100644
--- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
+++ b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
@@ -9,85 +9,26 @@ pub(super) trait NoStdFloatExt {
fn copysign(self, other: Self) -> Self;
}
+#[rustfmt::skip]
impl NoStdFloatExt for f64 {
- #[inline]
- fn round(self) -> Self {
- libm::round(self)
- }
-
- #[inline]
- fn abs(self) -> Self {
- libm::fabs(self)
- }
-
- #[inline]
- fn signum(self) -> Self {
- libm::copysign(1.0, self)
- }
-
- #[inline]
- fn ceil(self) -> Self {
- libm::ceil(self)
- }
-
- #[inline]
- fn floor(self) -> Self {
- libm::floor(self)
- }
-
- #[inline]
- fn trunc(self) -> Self {
- libm::trunc(self)
- }
-
- #[inline]
- fn sqrt(self) -> Self {
- libm::sqrt(self)
- }
-
- #[inline]
- fn copysign(self, other: Self) -> Self {
- libm::copysign(self, other)
- }
+ #[inline] fn round(self) -> Self { libm::round(self) }
+ #[inline] fn abs(self) -> Self { libm::fabs(self) }
+ #[inline] fn signum(self) -> Self { libm::copysign(1.0, self) }
+ #[inline] fn ceil(self) -> Self { libm::ceil(self) }
+ #[inline] fn floor(self) -> Self { libm::floor(self) }
+ #[inline] fn trunc(self) -> Self { libm::trunc(self) }
+ #[inline] fn sqrt(self) -> Self { libm::sqrt(self) }
+ #[inline] fn copysign(self, other: Self) -> Self { libm::copysign(self, other) }
}
-impl NoStdFloatExt for f32 {
- #[inline]
- fn round(self) -> Self {
- libm::roundf(self)
- }
- #[inline]
- fn abs(self) -> Self {
- libm::fabsf(self)
- }
-
- #[inline]
- fn signum(self) -> Self {
- libm::copysignf(1.0, self)
- }
-
- #[inline]
- fn ceil(self) -> Self {
- libm::ceilf(self)
- }
-
- #[inline]
- fn floor(self) -> Self {
- libm::floorf(self)
- }
-
- #[inline]
- fn trunc(self) -> Self {
- libm::truncf(self)
- }
-
- #[inline]
- fn sqrt(self) -> Self {
- libm::sqrtf(self)
- }
-
- #[inline]
- fn copysign(self, other: Self) -> Self {
- libm::copysignf(self, other)
- }
+#[rustfmt::skip]
+impl NoStdFloatExt for f32 {
+ #[inline] fn round(self) -> Self { libm::roundf(self) }
+ #[inline] fn abs(self) -> Self { libm::fabsf(self) }
+ #[inline] fn signum(self) -> Self { libm::copysignf(1.0, self) }
+ #[inline] fn ceil(self) -> Self { libm::ceilf(self) }
+ #[inline] fn floor(self) -> Self { libm::floorf(self) }
+ #[inline] fn trunc(self) -> Self { libm::truncf(self) }
+ #[inline] fn sqrt(self) -> Self { libm::sqrtf(self) }
+ #[inline] fn copysign(self, other: Self) -> Self { libm::copysignf(self, other) }
}
diff --git a/crates/tinywasm/src/runtime/interpreter/traits.rs b/crates/tinywasm/src/runtime/interpreter/traits.rs
index 523265b..7aeb3b7 100644
--- a/crates/tinywasm/src/runtime/interpreter/traits.rs
+++ b/crates/tinywasm/src/runtime/interpreter/traits.rs
@@ -24,23 +24,15 @@ macro_rules! impl_wasm_float_ops {
x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros
x if (0.0..=0.5).contains(&x) => 0.0,
x if (-0.5..0.0).contains(&x) => -0.0,
- // x => x.round(),
x => {
// Handle normal and halfway cases
let rounded = x.round();
let diff = (x - rounded).abs();
-
- if diff == 0.5 {
- // Halfway case: round to even
- if rounded % 2.0 == 0.0 {
- rounded // Already even
- } else {
- rounded - x.signum() // Make even
- }
- } else {
- // Normal case
- rounded
+ if diff != 0.5 || rounded % 2.0 == 0.0 {
+ return rounded
}
+
+ rounded - x.signum() // Make even
}
}
}
@@ -49,15 +41,11 @@ macro_rules! impl_wasm_float_ops {
// Based on f32::minimum (which is not yet stable)
#[inline]
fn tw_minimum(self, other: Self) -> Self {
- if self < other {
- self
- } else if other < self {
- other
- } else if self == other {
- if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
- } else {
- // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
- self + other
+ match self.partial_cmp(&other) {
+ Some(core::cmp::Ordering::Less) => self,
+ Some(core::cmp::Ordering::Greater) => other,
+ Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { self } else { other },
+ None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
}
}
@@ -65,15 +53,11 @@ macro_rules! impl_wasm_float_ops {
// Based on f32::maximum (which is not yet stable)
#[inline]
fn tw_maximum(self, other: Self) -> Self {
- if self > other {
- self
- } else if other > self {
- other
- } else if self == other {
- if self.is_sign_negative() && other.is_sign_positive() { other } else { self }
- } else {
- // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
- self + other
+ match self.partial_cmp(&other) {
+ Some(core::cmp::Ordering::Greater) => self,
+ Some(core::cmp::Ordering::Less) => other,
+ Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { other } else { self },
+ None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
}
}
}
diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs
index 14ad050..12270d1 100644
--- a/crates/tinywasm/src/runtime/stack/call_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/call_stack.rs
@@ -143,7 +143,7 @@ impl CallFrame {
self.locals[local_index]
}
- #[inline]
+ #[inline(always)]
pub(crate) fn instructions(&self) -> &[Instruction] {
&self.func_instance.0.instructions
}
diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs
index c6d7918..5903228 100644
--- a/crates/tinywasm/src/runtime/stack/value_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/value_stack.rs
@@ -34,6 +34,16 @@ impl ValueStack {
}
#[inline]
+ pub(crate) fn replace_top(&mut self, func: impl FnOnce(RawWasmValue) -> RawWasmValue) {
+ let len = self.stack.len();
+ if unlikely(len == 0) {
+ return;
+ }
+ let top = self.stack[len - 1];
+ self.stack[len - 1] = func(top);
+ }
+
+ #[inline]
pub(crate) fn len(&self) -> usize {
self.stack.len()
}
@@ -53,7 +63,7 @@ impl ValueStack {
self.stack.drain(remove_start_index..remove_end_index);
}
- #[inline]
+ #[inline(always)]
pub(crate) fn push(&mut self, value: RawWasmValue) {
self.stack.push(value);
}
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index a3d99fe..f4e0df9 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -172,11 +172,8 @@ impl Store {
/// Set the global at the actual index in the store
#[inline]
pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> {
- self.data
- .globals
- .get(addr)
- .ok_or_else(|| Self::not_found_error("global"))
- .map(|global| global.borrow_mut().value = value)
+ let global = self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global"));
+ global.map(|global| global.borrow_mut().value = value)
}
}
@@ -190,12 +187,10 @@ impl Store {
) -> Result<Vec<FuncAddr>> {
let func_count = self.data.funcs.len();
let mut func_addrs = Vec::with_capacity(func_count);
-
for (i, func) in funcs.into_iter().enumerate() {
self.data.funcs.push(FunctionInstance::new_wasm(func.wasm_function, idx));
func_addrs.push((i + func_count) as FuncAddr);
}
-
Ok(func_addrs)
}
@@ -264,10 +259,9 @@ impl Store {
let val = i64::from(global.borrow().value);
// check if the global is actually a null reference
- if val < 0 {
- None
- } else {
- Some(val as u32)
+ match val < 0 {
+ true => None,
+ false => Some(val as u32),
}
}
_ => return Err(Error::UnsupportedFeature(format!("const expression other than ref: {:?}", item))),
@@ -300,10 +294,7 @@ impl Store {
ElementKind::Passive => Some(init),
// this one is not available to the runtime but needs to be initialized to declare references
- ElementKind::Declared => {
- // a. Execute the instruction elm.drop i
- None
- }
+ ElementKind::Declared => None, // a. Execute the instruction elm.drop i
// this one is active, so we need to initialize it (essentially a `table.init` instruction)
ElementKind::Active { offset, table } => {
@@ -313,17 +304,17 @@ impl Store {
.copied()
.ok_or_else(|| Error::Other(format!("table {} not found for element {}", table, i)))?;
- if let Some(table) = self.data.tables.get_mut(table_addr as usize) {
- // In wasm 2.0, it's possible to call a function that hasn't been instantiated yet,
- // when using a partially initialized active element segments.
- // 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) {
- return Ok((elem_addrs.into_boxed_slice(), Some(trap)));
- }
- } else {
+ let Some(table) = self.data.tables.get_mut(table_addr as usize) else {
return Err(Error::Other(format!("table {} not found for element {}", table, i)));
+ };
+
+ // In wasm 2.0, it's possible to call a function that hasn't been instantiated yet,
+ // when using a partially initialized active element segments.
+ // 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) {
+ return Ok((elem_addrs.into_boxed_slice(), Some(trap)));
}
// f. Execute the instruction elm.drop i
@@ -356,26 +347,20 @@ impl Store {
return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string()));
}
- let mem_addr = mem_addrs
- .get(mem_addr as usize)
- .copied()
- .ok_or_else(|| Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)))?;
+ let Some(mem_addr) = mem_addrs.get(mem_addr as usize) else {
+ return Err(Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)));
+ };
let offset = self.eval_i32_const(&offset)?;
+ let Some(mem) = self.data.memories.get_mut(*mem_addr as usize) else {
+ return Err(Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)));
+ };
- let mem =
- self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| {
- Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))
- })?;
-
- // See comment for active element sections in the function above why we need to do this here
- if let Err(Error::Trap(trap)) = mem.borrow_mut().store(offset as usize, data.data.len(), &data.data)
- {
- return Ok((data_addrs.into_boxed_slice(), Some(trap)));
+ match mem.borrow_mut().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),
}
-
- // drop the data
- None
}
tinywasm_types::DataKind::Passive => Some(data.data.to_vec()),
};
@@ -417,10 +402,8 @@ impl Store {
let val = match const_instr {
I32Const(i) => *i,
GlobalGet(addr) => {
- let addr = *addr as usize;
- let global = self.data.globals[addr].clone();
- let val = global.borrow().value;
- i32::from(val)
+ let global = self.data.globals[*addr as usize].borrow();
+ i32::from(global.value)
}
_ => return Err(Error::Other("expected i32".to_string())),
};
@@ -441,17 +424,17 @@ impl Store {
I32Const(i) => RawWasmValue::from(*i),
I64Const(i) => RawWasmValue::from(*i),
GlobalGet(addr) => {
- let addr = module_global_addrs.get(*addr as usize).copied().ok_or_else(|| {
+ let addr = module_global_addrs.get(*addr as usize).ok_or_else(|| {
Error::Other(format!("global {} not found. This should have been caught by the validator", addr))
})?;
let global =
- self.data.globals.get(addr as usize).expect("global not found. This should be unreachable");
+ self.data.globals.get(*addr as usize).expect("global not found. This should be unreachable");
global.borrow().value
}
RefNull(t) => RawWasmValue::from(t.default_value()),
- RefFunc(idx) => RawWasmValue::from(module_func_addrs.get(*idx as usize).copied().ok_or_else(|| {
+ RefFunc(idx) => RawWasmValue::from(*module_func_addrs.get(*idx as usize).ok_or_else(|| {
Error::Other(format!("function {} not found. This should have been caught by the validator", idx))
})?),
};
diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs
index 1b31999..d9dd9ad 100644
--- a/crates/tinywasm/src/store/table.rs
+++ b/crates/tinywasm/src/store/table.rs
@@ -1,4 +1,4 @@
-use crate::log;
+use crate::{log, unlikely};
use crate::{Error, Result, Trap};
use alloc::{vec, vec::Vec};
use tinywasm_types::*;
@@ -40,7 +40,7 @@ impl TableInstance {
pub(crate) fn grow_to_fit(&mut self, new_size: usize) -> Result<()> {
if new_size > self.elements.len() {
- if new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize {
+ if unlikely(new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize) {
return Err(crate::Trap::TableOutOfBounds { offset: new_size, len: 1, max: self.elements.len() }.into());
}
diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs
index 537b8d7..9923116 100644
--- a/crates/types/src/instructions.rs
+++ b/crates/types/src/instructions.rs
@@ -94,7 +94,7 @@ pub enum Instruction {
// LocalGet + I32Const + I32Store => I32LocalGetConstStore + I32Const
// Also common, helps us skip the stack entirely.
// Has to be followed by an I32Const instruction
- // I32LocalGetConstStore { local: LocalAddr, offset: i32, mem_addr: MemAddr }, // I32Store + LocalGet + I32Const
+ // I32StoreLocal { local: LocalAddr, offset: i32, mem_addr: MemAddr },
// I64Xor + I64Const + I64RotL
// Commonly used by a few crypto libraries
diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs
index df062ce..bcd43e5 100644
--- a/crates/types/src/value.rs
+++ b/crates/types/src/value.rs
@@ -142,7 +142,7 @@ impl ValType {
WasmValue::default_for(*self)
}
- pub(crate) fn to_byte(&self) -> u8 {
+ pub(crate) fn to_byte(self) -> u8 {
match self {
ValType::I32 => 0x7F,
ValType::I64 => 0x7E,