summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--benchmarks/benches/selfhosted.rs10
-rw-r--r--crates/parser/src/conversion.rs28
-rw-r--r--crates/parser/src/lib.rs45
-rw-r--r--crates/parser/src/module.rs46
-rw-r--r--crates/parser/src/visit.rs264
-rw-r--r--crates/tinywasm/src/boxvec.rs20
-rw-r--r--crates/tinywasm/src/instance.rs2
-rw-r--r--crates/tinywasm/src/runtime/interpreter/macros.rs64
-rw-r--r--crates/tinywasm/src/runtime/interpreter/mod.rs739
-rw-r--r--crates/tinywasm/src/runtime/raw.rs40
-rw-r--r--crates/tinywasm/src/runtime/stack/call_stack.rs4
-rw-r--r--crates/tinywasm/src/runtime/stack/value_stack.rs13
12 files changed, 560 insertions, 715 deletions
diff --git a/benchmarks/benches/selfhosted.rs b/benchmarks/benches/selfhosted.rs
index 4241eea..441923b 100644
--- a/benchmarks/benches/selfhosted.rs
+++ b/benchmarks/benches/selfhosted.rs
@@ -60,11 +60,11 @@ fn criterion_benchmark(c: &mut Criterion) {
}
{
- let mut group = c.benchmark_group("selfhosted");
- group.bench_function("native", |b| b.iter(run_native));
- group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(TINYWASM)));
- group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM)));
- group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM)));
+ // let mut group = c.benchmark_group("selfhosted");
+ // group.bench_function("native", |b| b.iter(run_native));
+ // group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(TINYWASM)));
+ // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM)));
+ // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM)));
}
}
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index bc082f0..e8dd9a8 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -1,5 +1,5 @@
use crate::Result;
-use crate::{module::Code, visit::process_operators};
+use crate::{module::Code, visit::process_operators_and_validate};
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
use tinywasm_types::*;
use wasmparser::{FuncValidator, OperatorsReader, ValidatorResources};
@@ -174,17 +174,20 @@ pub(crate) fn convert_module_code(
let count = locals_reader.get_count();
let pos = locals_reader.original_position();
- let mut locals = Vec::with_capacity(count as usize);
- for (i, local) in locals_reader.into_iter().enumerate() {
- let local = local?;
- validator.define_locals(pos + i, local.0, local.1)?;
- for _ in 0..local.0 {
- locals.push(convert_valtype(&local.1));
+ let locals = {
+ let mut locals = Vec::new();
+ locals.reserve_exact(count as usize);
+ for (i, local) in locals_reader.into_iter().enumerate() {
+ let local = local?;
+ validator.define_locals(pos + i, local.0, local.1)?;
+ for _ in 0..local.0 {
+ locals.push(convert_valtype(&local.1));
+ }
}
- }
+ locals.into_boxed_slice()
+ };
- let body = process_operators(Some(validator), func)?;
- let locals = locals.into_boxed_slice();
+ let body = process_operators_and_validate(validator, func)?;
Ok((body, locals))
}
@@ -196,6 +199,7 @@ pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType>
"Expected exactly one type in the type section".to_string(),
));
}
+
let ty = types.next().unwrap().unwrap_func();
let params = ty.params().iter().map(convert_valtype).collect::<Vec<ValType>>().into_boxed_slice();
let results = ty.results().iter().map(convert_valtype).collect::<Vec<ValType>>().into_boxed_slice();
@@ -230,10 +234,6 @@ pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType {
}
}
-pub(crate) fn convert_memarg(memarg: wasmparser::MemArg) -> MemoryArg {
- MemoryArg { offset: memarg.offset, mem_addr: memarg.memory }
-}
-
pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result<ConstInstruction> {
let ops = ops.into_iter().collect::<wasmparser::Result<Vec<_>>>()?;
// In practice, the len can never be something other than 2,
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index dd10f4d..4ee9bb5 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -31,10 +31,9 @@ mod conversion;
mod error;
mod module;
mod visit;
-use alloc::{string::ToString, vec::Vec};
+use alloc::vec::Vec;
pub use error::*;
use module::ModuleReader;
-use tinywasm_types::WasmFunction;
use wasmparser::{Validator, WasmFeaturesInflated};
pub use tinywasm_types::TinyWasmModule;
@@ -93,7 +92,7 @@ impl Parser {
return Err(ParseError::EndNotReached);
}
- reader.try_into()
+ reader.to_module()
}
#[cfg(feature = "std")]
@@ -133,7 +132,7 @@ impl Parser {
reader.process_payload(payload, &mut validator)?;
buffer.drain(..consumed);
if eof || reader.end_reached {
- return reader.try_into();
+ return reader.to_module();
}
}
};
@@ -145,42 +144,6 @@ impl TryFrom<ModuleReader> for TinyWasmModule {
type Error = ParseError;
fn try_from(reader: ModuleReader) -> Result<Self> {
- if !reader.end_reached {
- return Err(ParseError::EndNotReached);
- }
-
- let code_type_addrs = reader.code_type_addrs;
- let local_function_count = reader.code.len();
-
- if code_type_addrs.len() != local_function_count {
- return Err(ParseError::Other("Code and code type address count mismatch".to_string()));
- }
-
- let funcs = reader
- .code
- .into_iter()
- .zip(code_type_addrs)
- .map(|((instructions, locals), ty_idx)| WasmFunction {
- instructions,
- locals,
- ty: reader.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(),
- })
- .collect::<Vec<_>>();
-
- let globals = reader.globals;
- let table_types = reader.table_types;
-
- Ok(TinyWasmModule {
- funcs: funcs.into_boxed_slice(),
- func_types: reader.func_types.into_boxed_slice(),
- globals: globals.into_boxed_slice(),
- table_types: table_types.into_boxed_slice(),
- imports: reader.imports.into_boxed_slice(),
- start_func: reader.start_func,
- data: reader.data.into_boxed_slice(),
- exports: reader.exports.into_boxed_slice(),
- elements: reader.elements.into_boxed_slice(),
- memory_types: reader.memory_types.into_boxed_slice(),
- })
+ reader.to_module()
}
}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index 1cd5ed5..a4bdba4 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -1,7 +1,11 @@
use crate::log::debug;
use crate::{conversion, ParseError, Result};
+use alloc::string::ToString;
use alloc::{boxed::Box, format, vec::Vec};
-use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, ValType};
+use tinywasm_types::{
+ Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, TinyWasmModule, ValType,
+ WasmFunction,
+};
use wasmparser::{FuncValidatorAllocations, Payload, Validator};
pub(crate) type Code = (Box<[Instruction]>, Box<[ValType]>);
@@ -173,4 +177,44 @@ impl ModuleReader {
Ok(())
}
+
+ #[inline]
+ pub(crate) fn to_module(self) -> Result<TinyWasmModule> {
+ if !self.end_reached {
+ return Err(ParseError::EndNotReached);
+ }
+
+ let local_function_count = self.code.len();
+
+ if self.code_type_addrs.len() != local_function_count {
+ return Err(ParseError::Other("Code and code type address count mismatch".to_string()));
+ }
+
+ let funcs = self
+ .code
+ .into_iter()
+ .zip(self.code_type_addrs)
+ .map(|((instructions, locals), ty_idx)| WasmFunction {
+ instructions,
+ locals,
+ ty: self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(),
+ })
+ .collect::<Vec<_>>();
+
+ let globals = self.globals;
+ let table_types = self.table_types;
+
+ Ok(TinyWasmModule {
+ funcs: funcs.into_boxed_slice(),
+ func_types: self.func_types.into_boxed_slice(),
+ globals: globals.into_boxed_slice(),
+ table_types: table_types.into_boxed_slice(),
+ imports: self.imports.into_boxed_slice(),
+ start_func: self.start_func,
+ data: self.data.into_boxed_slice(),
+ exports: self.exports.into_boxed_slice(),
+ elements: self.elements.into_boxed_slice(),
+ memory_types: self.memory_types.into_boxed_slice(),
+ })
+ }
}
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index 6002996..bddd01d 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -1,122 +1,99 @@
use crate::{conversion::convert_blocktype, Result};
-use crate::conversion::{convert_heaptype, convert_memarg, convert_valtype};
+use crate::conversion::{convert_heaptype, convert_valtype};
use alloc::string::ToString;
-use alloc::{boxed::Box, format, vec::Vec};
-use tinywasm_types::Instruction;
+use alloc::{boxed::Box, vec::Vec};
+use tinywasm_types::{Instruction, MemoryArg};
use wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources};
struct ValidateThenVisit<'a, T, U>(T, &'a mut U);
macro_rules! validate_then_visit {
- ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
- $(
- fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
- self.0.$visit($($($arg.clone()),*)?)?;
- Ok(self.1.$visit($($($arg),*)?))
- }
- )*
- };
+ ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {$(
+ fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
+ self.0.$visit($($($arg.clone()),*)?)?;
+ self.1.$visit($($($arg),*)?);
+ Ok(())
+ }
+ )*};
}
impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U>
where
T: VisitOperator<'a, Output = wasmparser::Result<()>>,
- U: VisitOperator<'a>,
+ U: VisitOperator<'a, Output = ()>,
{
- type Output = Result<U::Output>;
+ type Output = Result<()>;
wasmparser::for_each_operator!(validate_then_visit);
}
-pub(crate) fn process_operators<R: WasmModuleResources>(
- validator: Option<&mut FuncValidator<R>>,
+pub(crate) fn process_operators_and_validate<R: WasmModuleResources>(
+ validator: &mut FuncValidator<R>,
body: FunctionBody<'_>,
) -> Result<Box<[Instruction]>> {
let mut reader = body.get_operators_reader()?;
let remaining = reader.get_binary_reader().bytes_remaining();
let mut builder = FunctionBuilder::new(remaining);
- if let Some(validator) = validator {
- while !reader.eof() {
- let validate = validator.visitor(reader.original_position());
- reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))???;
- }
- validator.finish(reader.original_position())?;
- } else {
- while !reader.eof() {
- reader.visit_operator(&mut builder)??;
- }
+ while !reader.eof() {
+ let validate = validator.visitor(reader.original_position());
+ reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))??;
+ }
+ validator.finish(reader.original_position())?;
+ if !builder.errors.is_empty() {
+ return Err(builder.errors.remove(0));
}
Ok(builder.instructions.into_boxed_slice())
}
macro_rules! define_operands {
- ($($name:ident, $instr:expr),*) => {
- $(
- #[inline(always)]
- fn $name(&mut self) -> Self::Output {
- self.instructions.push($instr);
- Ok(())
- }
- )*
- };
+ ($($name:ident, $instr:expr),*) => {$(
+ fn $name(&mut self) -> Self::Output {
+ self.instructions.push($instr);
+ }
+ )*};
}
macro_rules! define_primitive_operands {
- ($($name:ident, $instr:expr, $ty:ty),*) => {
- $(
- #[inline(always)]
- fn $name(&mut self, arg: $ty) -> Self::Output {
- self.instructions.push($instr(arg));
- Ok(())
- }
- )*
- };
- ($($name:ident, $instr:expr, $ty:ty, $ty2:ty),*) => {
- $(
- #[inline(always)]
- fn $name(&mut self, arg: $ty, arg2: $ty) -> Self::Output {
- self.instructions.push($instr(arg, arg2));
- Ok(())
- }
- )*
- };
+ ($($name:ident, $instr:expr, $ty:ty),*) => {$(
+ fn $name(&mut self, arg: $ty) -> Self::Output {
+ self.instructions.push($instr(arg));
+ }
+ )*};
+ ($($name:ident, $instr:expr, $ty:ty, $ty2:ty),*) => {$(
+ fn $name(&mut self, arg: $ty, arg2: $ty2) -> Self::Output {
+ self.instructions.push($instr(arg, arg2));
+ }
+ )*};
}
macro_rules! define_mem_operands {
- ($($name:ident, $instr:ident),*) => {
- $(
- #[inline(always)]
- fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
- let arg = convert_memarg(memarg);
- self.instructions.push(Instruction::$instr {
- offset: arg.offset,
- mem_addr: arg.mem_addr,
- });
- Ok(())
- }
- )*
- };
+ ($($name:ident, $instr:ident),*) => {$(
+ fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+ self.instructions.push(Instruction::$instr {
+ offset: memarg.offset,
+ mem_addr: memarg.memory,
+ });
+ }
+ )*};
}
pub(crate) struct FunctionBuilder {
instructions: Vec<Instruction>,
label_ptrs: Vec<usize>,
+ errors: Vec<crate::ParseError>,
}
impl FunctionBuilder {
pub(crate) fn new(instr_capacity: usize) -> Self {
- Self { instructions: Vec::with_capacity(instr_capacity / 4), label_ptrs: Vec::with_capacity(256) }
- }
-
- #[cold]
- fn unsupported(&self, name: &str) -> Result<()> {
- Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", name)))
+ Self {
+ instructions: Vec::with_capacity(instr_capacity),
+ label_ptrs: Vec::with_capacity(256),
+ errors: Vec::new(),
+ }
}
- #[inline(always)]
- fn visit(&mut self, op: Instruction) -> Result<()> {
- self.instructions.push(op);
- Ok(())
+ fn unsupported(&mut self, name: &str) {
+ self.errors.push(crate::ParseError::UnsupportedOperator(name.to_string()));
}
}
@@ -132,14 +109,14 @@ macro_rules! impl_visit_operator {
(@@bulk_memory $($rest:tt)* ) => {};
(@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident) => {
#[cold]
- fn $visit(&mut self $($(,$arg: $argty)*)?) -> Result<()>{
+ fn $visit(&mut self $($(,$arg: $argty)*)?) {
self.unsupported(stringify!($visit))
}
};
}
impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
- type Output = Result<()>;
+ type Output = ();
wasmparser::for_each_operator!(impl_visit_operator);
define_primitive_operands! {
@@ -148,7 +125,15 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
visit_global_get, Instruction::GlobalGet, u32,
visit_global_set, Instruction::GlobalSet, u32,
visit_i32_const, Instruction::I32Const, i32,
- visit_i64_const, Instruction::I64Const, i64
+ visit_i64_const, Instruction::I64Const, i64,
+ visit_call, Instruction::Call, u32,
+ visit_local_set, Instruction::LocalSet, u32,
+ visit_local_tee, Instruction::LocalTee, u32
+ }
+
+ define_primitive_operands! {
+ visit_memory_size, Instruction::MemorySize, u32, u8,
+ visit_memory_grow, Instruction::MemoryGrow, u32, u8
}
define_mem_operands! {
@@ -325,116 +310,95 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U
}
- #[inline(always)]
fn visit_i32_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
- let arg = convert_memarg(memarg);
+ let arg = MemoryArg { offset: memarg.offset, mem_addr: memarg.memory };
let i32store = Instruction::I32Store { offset: arg.offset, mem_addr: arg.mem_addr };
if self.instructions.len() < 3 || arg.mem_addr > 0xFF || arg.offset > 0xFFFF_FFFF {
- return self.visit(i32store);
+ return self.instructions.push(i32store);
}
match self.instructions[self.instructions.len() - 2..] {
[Instruction::LocalGet(a), Instruction::I32Const(b)] => {
self.instructions.pop();
self.instructions.pop();
- self.visit(Instruction::I32StoreLocal {
+ self.instructions.push(Instruction::I32StoreLocal {
local: a,
const_i32: b,
offset: arg.offset as u32,
mem_addr: arg.mem_addr as u8,
})
}
- _ => self.visit(i32store),
+ _ => self.instructions.push(i32store),
}
}
- #[inline(always)]
fn visit_local_get(&mut self, idx: u32) -> Self::Output {
- let Some(instruction) = self.instructions.last_mut() else {
- return self.visit(Instruction::LocalGet(idx));
- };
-
+ if self.instructions.is_empty() {
+ return self.instructions.push(Instruction::LocalGet(idx));
+ }
+ let instruction = self.instructions.last_mut().unwrap();
match instruction {
Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx),
Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx),
Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx),
- _ => return self.visit(Instruction::LocalGet(idx)),
+ _ => self.instructions.push(Instruction::LocalGet(idx)),
};
-
- Ok(())
}
- #[inline(always)]
- fn visit_local_set(&mut self, idx: u32) -> Self::Output {
- self.visit(Instruction::LocalSet(idx))
- }
-
- #[inline(always)]
- fn visit_local_tee(&mut self, idx: u32) -> Self::Output {
- self.visit(Instruction::LocalTee(idx))
- }
-
- #[inline(always)]
fn visit_i64_rotl(&mut self) -> Self::Output {
if self.instructions.len() < 2 {
- return self.visit(Instruction::I64Rotl);
+ return self.instructions.push(Instruction::I64Rotl);
}
match self.instructions[self.instructions.len() - 2..] {
[Instruction::I64Xor, Instruction::I64Const(a)] => {
self.instructions.pop();
self.instructions.pop();
- self.visit(Instruction::I64XorConstRotl(a))
+ self.instructions.push(Instruction::I64XorConstRotl(a))
}
- _ => self.visit(Instruction::I64Rotl),
+ _ => self.instructions.push(Instruction::I64Rotl),
}
}
- #[inline(always)]
fn visit_i32_add(&mut self) -> Self::Output {
if self.instructions.len() < 2 {
- return self.visit(Instruction::I32Add);
+ return self.instructions.push(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.instructions.push(Instruction::I32LocalGetConstAdd(a, b))
}
- _ => self.visit(Instruction::I32Add),
+ _ => self.instructions.push(Instruction::I32Add),
}
}
- #[inline(always)]
fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output {
self.label_ptrs.push(self.instructions.len());
- self.visit(Instruction::Block(convert_blocktype(blockty), 0))
+ self.instructions.push(Instruction::Block(convert_blocktype(blockty), 0))
}
- #[inline(always)]
fn visit_loop(&mut self, ty: wasmparser::BlockType) -> Self::Output {
self.label_ptrs.push(self.instructions.len());
- self.visit(Instruction::Loop(convert_blocktype(ty), 0))
+ self.instructions.push(Instruction::Loop(convert_blocktype(ty), 0))
}
- #[inline(always)]
fn visit_if(&mut self, ty: wasmparser::BlockType) -> Self::Output {
self.label_ptrs.push(self.instructions.len());
- self.visit(Instruction::If(convert_blocktype(ty).into(), 0, 0))
+ self.instructions.push(Instruction::If(convert_blocktype(ty).into(), 0, 0))
}
- #[inline(always)]
fn visit_else(&mut self) -> Self::Output {
self.label_ptrs.push(self.instructions.len());
- self.visit(Instruction::Else(0))
+ self.instructions.push(Instruction::Else(0))
}
- #[inline(always)]
fn visit_end(&mut self) -> Self::Output {
let Some(label_pointer) = self.label_ptrs.pop() else {
- return self.visit(Instruction::Return);
+ return self.instructions.push(Instruction::Return);
};
let current_instr_ptr = self.instructions.len();
@@ -444,19 +408,22 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
.try_into()
.expect("else_instr_end_offset is too large, tinywasm does not support if blocks that large");
- #[cold]
- fn error() -> crate::ParseError {
- crate::ParseError::UnsupportedOperator(
- "Expected to end an if block, but the last label was not an if".to_string(),
- )
- }
-
// since we're ending an else block, we need to end the if block as well
- let if_label_pointer = self.label_ptrs.pop().ok_or_else(error)?;
+ let Some(if_label_pointer) = self.label_ptrs.pop() else {
+ self.errors.push(crate::ParseError::UnsupportedOperator(
+ "Expected to end an if block, but there was no if block to end".to_string(),
+ ));
+
+ return;
+ };
let if_instruction = &mut self.instructions[if_label_pointer];
let Instruction::If(_, else_offset, end_offset) = if_instruction else {
- return Err(error());
+ self.errors.push(crate::ParseError::UnsupportedOperator(
+ "Expected to end an if block, but the last label was not an if".to_string(),
+ ));
+
+ return;
};
*else_offset = (label_pointer - if_label_pointer)
@@ -479,10 +446,9 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
}
};
- self.visit(Instruction::EndBlockFrame)
+ self.instructions.push(Instruction::EndBlockFrame)
}
- #[inline(always)]
fn visit_br_table(&mut self, targets: wasmparser::BrTable<'_>) -> Self::Output {
let def = targets.default();
let instrs = targets
@@ -492,37 +458,18 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
.expect("BrTable targets are invalid, this should have been caught by the validator");
self.instructions.extend(([Instruction::BrTable(def, instrs.len() as u32)].into_iter()).chain(instrs));
- Ok(())
- }
-
- #[inline(always)]
- fn visit_call(&mut self, idx: u32) -> Self::Output {
- self.visit(Instruction::Call(idx))
}
- #[inline(always)]
fn visit_call_indirect(&mut self, ty: u32, table: u32, _table_byte: u8) -> Self::Output {
- self.visit(Instruction::CallIndirect(ty, table))
+ self.instructions.push(Instruction::CallIndirect(ty, table))
}
- #[inline(always)]
- fn visit_memory_size(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
- self.visit(Instruction::MemorySize(mem, mem_byte))
- }
-
- #[inline(always)]
- fn visit_memory_grow(&mut self, mem: u32, mem_byte: u8) -> Self::Output {
- self.visit(Instruction::MemoryGrow(mem, mem_byte))
- }
-
- #[inline(always)]
fn visit_f32_const(&mut self, val: wasmparser::Ieee32) -> Self::Output {
- self.visit(Instruction::F32Const(f32::from_bits(val.bits())))
+ self.instructions.push(Instruction::F32Const(f32::from_bits(val.bits())))
}
- #[inline(always)]
fn visit_f64_const(&mut self, val: wasmparser::Ieee64) -> Self::Output {
- self.visit(Instruction::F64Const(f64::from_bits(val.bits())))
+ self.instructions.push(Instruction::F64Const(f64::from_bits(val.bits())))
}
// Bulk Memory Operations
@@ -538,26 +485,21 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
visit_elem_drop, Instruction::ElemDrop, u32
}
- #[inline(always)]
fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output {
- self.visit(Instruction::TableCopy { from: src_table, to: dst_table })
+ self.instructions.push(Instruction::TableCopy { from: src_table, to: dst_table })
}
// Reference Types
-
- #[inline(always)]
fn visit_ref_null(&mut self, ty: wasmparser::HeapType) -> Self::Output {
- self.visit(Instruction::RefNull(convert_heaptype(ty)))
+ self.instructions.push(Instruction::RefNull(convert_heaptype(ty)))
}
- #[inline(always)]
fn visit_ref_is_null(&mut self) -> Self::Output {
- self.visit(Instruction::RefIsNull)
+ self.instructions.push(Instruction::RefIsNull)
}
- #[inline(always)]
fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output {
- self.visit(Instruction::Select(Some(convert_valtype(&ty))))
+ self.instructions.push(Instruction::Select(Some(convert_valtype(&ty))))
}
define_primitive_operands! {
diff --git a/crates/tinywasm/src/boxvec.rs b/crates/tinywasm/src/boxvec.rs
index bf7cd8b..17d3740 100644
--- a/crates/tinywasm/src/boxvec.rs
+++ b/crates/tinywasm/src/boxvec.rs
@@ -1,5 +1,5 @@
use crate::unlikely;
-use alloc::{borrow::Cow, boxed::Box, vec};
+use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use core::ops::RangeBounds;
// A Vec-like type that doesn't deallocate memory when popping elements.
@@ -12,7 +12,10 @@ pub(crate) struct BoxVec<T> {
impl<T: Copy + Default> BoxVec<T> {
#[inline(always)]
pub(crate) fn with_capacity(capacity: usize) -> Self {
- Self { data: vec![T::default(); capacity].into_boxed_slice(), end: 0 }
+ let mut data = Vec::new();
+ data.reserve_exact(capacity);
+ data.resize_with(capacity, T::default);
+ Self { data: data.into_boxed_slice(), end: 0 }
}
#[inline(always)]
@@ -67,6 +70,19 @@ impl<T: Copy + Default> BoxVec<T> {
}
#[inline(always)]
+ pub(crate) fn pop_n(&mut self, n: usize) -> Option<&[T]> {
+ assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)");
+ if unlikely(self.end < n) {
+ None
+ } else {
+ let start = self.end - n;
+ let end = self.end;
+ self.end = start;
+ Some(&self.data[start..end])
+ }
+ }
+
+ #[inline(always)]
pub(crate) fn drain(&mut self, range: impl RangeBounds<usize>) -> Cow<'_, [T]> {
let start = match range.start_bound() {
core::ops::Bound::Included(&start) => start,
diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs
index f96fcd8..abc2819 100644
--- a/crates/tinywasm/src/instance.rs
+++ b/crates/tinywasm/src/instance.rs
@@ -125,7 +125,7 @@ impl ModuleInstance {
#[inline]
pub(crate) fn func_ty(&self, addr: FuncAddr) -> &FuncType {
- self.0.types.get(addr as usize).expect("No func type for func, this is a bug")
+ &self.0.types[addr as usize]
}
#[inline]
diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs
index aee3e30..aca2252 100644
--- a/crates/tinywasm/src/runtime/interpreter/macros.rs
+++ b/crates/tinywasm/src/runtime/interpreter/macros.rs
@@ -17,68 +17,6 @@ macro_rules! break_to {
}};
}
-/// Load a value from memory
-macro_rules! mem_load {
- ($type:ty, $arg:expr, $self:expr) => {{
- mem_load!($type, $type, $arg, $self)
- }};
-
- ($load_type:ty, $target_type:ty, $arg:expr, $self:expr) => {{
- #[inline(always)]
- fn mem_load_inner(
- store: &Store,
- module: &crate::ModuleInstance,
- stack: &mut crate::runtime::Stack,
- mem_addr: tinywasm_types::MemAddr,
- offset: u64,
- ) -> Result<()> {
- let mem = store.get_mem(module.resolve_mem_addr(mem_addr))?;
- let Some(Ok(addr)) = offset.checked_add(stack.values.pop()?.into()).map(|a| a.try_into()) else {
- return Err(Error::Trap(crate::Trap::MemoryOutOfBounds {
- offset: offset as usize,
- len: core::mem::size_of::<$load_type>(),
- max: mem.borrow().max_pages(),
- }));
- };
-
- const LEN: usize = core::mem::size_of::<$load_type>();
- let val = mem.borrow().load_as::<LEN, $load_type>(addr)?;
- stack.values.push((val as $target_type).into());
- Ok(())
- }
-
- let (mem_addr, offset) = $arg;
- mem_load_inner($self.store, &$self.module, $self.stack, *mem_addr, *offset)?;
- }};
-}
-
-/// Store a value to memory
-macro_rules! mem_store {
- ($type:ty, $arg:expr, $self:expr) => {{
- mem_store!($type, $type, $arg, $self)
- }};
-
- ($store_type:ty, $target_type:ty, $arg:expr, $self:expr) => {{
- #[inline(always)]
- fn mem_store_inner(
- store: &Store,
- module: &crate::ModuleInstance,
- stack: &mut crate::runtime::Stack,
- mem_addr: tinywasm_types::MemAddr,
- offset: u64,
- ) -> Result<()> {
- let mem = store.get_mem(module.resolve_mem_addr(mem_addr))?;
- 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)?;
- Ok(())
- }
-
- mem_store_inner($self.store, &$self.module, $self.stack, *$arg.0, *$arg.1)?;
- }};
-}
-
/// Doing the actual conversion from float to int is a bit tricky, because
/// we need to check for overflow. This macro generates the min/max values
/// for a specific conversion, which are then used in the actual conversion.
@@ -200,5 +138,3 @@ pub(super) use comp;
pub(super) use comp_zero;
pub(super) use conv;
pub(super) use float_min_max;
-pub(super) use mem_load;
-pub(super) use mem_store;
diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs
index 7a7dbca..6163f1b 100644
--- a/crates/tinywasm/src/runtime/interpreter/mod.rs
+++ b/crates/tinywasm/src/runtime/interpreter/mod.rs
@@ -1,14 +1,12 @@
-use alloc::format;
-use alloc::rc::Rc;
-use alloc::string::ToString;
+use alloc::{format, rc::Rc, string::ToString};
use core::ops::{BitAnd, BitOr, BitXor, ControlFlow, Neg};
use tinywasm_types::{BlockArgs, ElementKind, Instruction, ModuleInstanceAddr, ValType, WasmFunction};
+use super::raw::ToMemBytes;
use super::stack::{BlockFrame, BlockType};
use super::{InterpreterRuntime, RawWasmValue, Stack};
use crate::runtime::CallFrame;
-use crate::{cold, unlikely};
-use crate::{Error, FuncContext, ModuleInstance, Result, Store, Trap};
+use crate::{cold, unlikely, Error, FuncContext, MemLoadable, ModuleInstance, Result, Store, Trap};
mod macros;
mod traits;
@@ -35,18 +33,6 @@ struct Executor<'store, 'stack> {
module: ModuleInstance,
}
-impl Iterator for Executor<'_, '_> {
- type Item = Result<()>;
-
- fn next(&mut self) -> Option<Self::Item> {
- match self.exec_next() {
- Ok(ControlFlow::Continue(())) => Some(Ok(())),
- Ok(ControlFlow::Break(())) => None,
- Err(e) => Some(Err(e)),
- }
- }
-}
-
impl<'store, 'stack> Executor<'store, 'stack> {
pub(crate) fn new(store: &'store mut Store, stack: &'stack mut Stack) -> Result<Self> {
let current_frame = stack.call_stack.pop().ok_or_else(|| Error::CallStackUnderflow)?;
@@ -72,7 +58,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
Drop => self.exec_drop()?,
Select(_valtype) => self.exec_select()?,
-
Call(v) => return self.exec_call_direct(*v),
CallIndirect(ty, table) => return self.exec_call_indirect(*ty, *table),
@@ -111,30 +96,30 @@ impl<'store, 'stack> Executor<'store, 'stack> {
ElemDrop(elem_index) => self.exec_elem_drop(*elem_index)?,
TableCopy { from, to } => self.exec_table_copy(*from, *to)?,
- I32Store { mem_addr, offset } => mem_store!(i32, (mem_addr, offset), self),
- I64Store { mem_addr, offset } => mem_store!(i64, (mem_addr, offset), self),
- F32Store { mem_addr, offset } => mem_store!(f32, (mem_addr, offset), self),
- F64Store { mem_addr, offset } => mem_store!(f64, (mem_addr, offset), self),
- I32Store8 { mem_addr, offset } => mem_store!(i8, i32, (mem_addr, offset), self),
- I32Store16 { mem_addr, offset } => mem_store!(i16, i32, (mem_addr, offset), self),
- I64Store8 { mem_addr, offset } => mem_store!(i8, i64, (mem_addr, offset), self),
- I64Store16 { mem_addr, offset } => mem_store!(i16, i64, (mem_addr, offset), self),
- I64Store32 { mem_addr, offset } => mem_store!(i32, i64, (mem_addr, offset), self),
+ I32Store { mem_addr, offset } => self.exec_mem_store::<i32, 4>(*mem_addr, *offset)?,
+ I64Store { mem_addr, offset } => self.exec_mem_store::<i64, 8>(*mem_addr, *offset)?,
+ F32Store { mem_addr, offset } => self.exec_mem_store::<f32, 4>(*mem_addr, *offset)?,
+ F64Store { mem_addr, offset } => self.exec_mem_store::<f64, 8>(*mem_addr, *offset)?,
+ I32Store8 { mem_addr, offset } => self.exec_mem_store::<i8, 1>(*mem_addr, *offset)?,
+ I32Store16 { mem_addr, offset } => self.exec_mem_store::<i16, 2>(*mem_addr, *offset)?,
+ I64Store8 { mem_addr, offset } => self.exec_mem_store::<i8, 1>(*mem_addr, *offset)?,
+ I64Store16 { mem_addr, offset } => self.exec_mem_store::<i16, 2>(*mem_addr, *offset)?,
+ I64Store32 { mem_addr, offset } => self.exec_mem_store::<i32, 4>(*mem_addr, *offset)?,
- I32Load { mem_addr, offset } => mem_load!(i32, (mem_addr, offset), self),
- I64Load { mem_addr, offset } => mem_load!(i64, (mem_addr, offset), self),
- F32Load { mem_addr, offset } => mem_load!(f32, (mem_addr, offset), self),
- F64Load { mem_addr, offset } => mem_load!(f64, (mem_addr, offset), self),
- I32Load8S { mem_addr, offset } => mem_load!(i8, i32, (mem_addr, offset), self),
- I32Load8U { mem_addr, offset } => mem_load!(u8, i32, (mem_addr, offset), self),
- I32Load16S { mem_addr, offset } => mem_load!(i16, i32, (mem_addr, offset), self),
- I32Load16U { mem_addr, offset } => mem_load!(u16, i32, (mem_addr, offset), self),
- I64Load8S { mem_addr, offset } => mem_load!(i8, i64, (mem_addr, offset), self),
- I64Load8U { mem_addr, offset } => mem_load!(u8, i64, (mem_addr, offset), self),
- I64Load16S { mem_addr, offset } => mem_load!(i16, i64, (mem_addr, offset), self),
- I64Load16U { mem_addr, offset } => mem_load!(u16, i64, (mem_addr, offset), self),
- I64Load32S { mem_addr, offset } => mem_load!(i32, i64, (mem_addr, offset), self),
- I64Load32U { mem_addr, offset } => mem_load!(u32, i64, (mem_addr, offset), self),
+ I32Load { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(|v| v, *mem_addr, *offset)?,
+ I64Load { mem_addr, offset } => self.exec_mem_load::<i64, 8, _>(|v| v, *mem_addr, *offset)?,
+ F32Load { mem_addr, offset } => self.exec_mem_load::<f32, 4, _>(|v| v, *mem_addr, *offset)?,
+ F64Load { mem_addr, offset } => self.exec_mem_load::<f64, 8, _>(|v| v, *mem_addr, *offset)?,
+ I32Load8S { mem_addr, offset } => self.exec_mem_load::<i8, 1, _>(|v| v as i32, *mem_addr, *offset)?,
+ I32Load8U { mem_addr, offset } => self.exec_mem_load::<u8, 1, _>(|v| v as i32, *mem_addr, *offset)?,
+ I32Load16S { mem_addr, offset } => self.exec_mem_load::<i16, 2, _>(|v| v as i32, *mem_addr, *offset)?,
+ I32Load16U { mem_addr, offset } => self.exec_mem_load::<u16, 2, _>(|v| v as i32, *mem_addr, *offset)?,
+ I64Load8S { mem_addr, offset } => self.exec_mem_load::<i8, 1, _>(|v| v as i64, *mem_addr, *offset)?,
+ I64Load8U { mem_addr, offset } => self.exec_mem_load::<u8, 1, _>(|v| v as i64, *mem_addr, *offset)?,
+ I64Load16S { mem_addr, offset } => self.exec_mem_load::<i16, 2, _>(|v| v as i64, *mem_addr, *offset)?,
+ I64Load16U { mem_addr, offset } => self.exec_mem_load::<u16, 2, _>(|v| v as i64, *mem_addr, *offset)?,
+ I64Load32S { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(|v| v as i64, *mem_addr, *offset)?,
+ I64Load32U { mem_addr, offset } => self.exec_mem_load::<u32, 4, _>(|v| v as i64, *mem_addr, *offset)?,
I64Eqz => comp_zero!(==, i64, self),
I32Eqz => comp_zero!(==, i32, self),
@@ -310,6 +295,7 @@ impl<'store, 'stack> Executor<'store, 'stack> {
I32StoreLocal { local, const_i32, offset, mem_addr } => {
self.exec_i32_store_local(*local, *const_i32, *offset, *mem_addr)?
}
+
i => {
cold();
return Err(Error::UnsupportedFeature(format!("unimplemented instruction: {:?}", i)));
@@ -320,37 +306,175 @@ impl<'store, 'stack> Executor<'store, 'stack> {
Ok(ControlFlow::Continue(()))
}
- #[inline(always)]
- fn exec_end_block(&mut self) -> Result<()> {
- let block = self.stack.blocks.pop()?;
- self.stack.values.truncate_keep(block.stack_ptr, block.results as u32);
+ fn exec_noop(&self) {}
+ #[cold]
+ fn exec_unreachable(&self) -> Result<()> {
+ Err(Error::Trap(Trap::Unreachable))
+ }
- #[cfg(feature = "simd")]
- self.stack.values.truncate_keep_simd(block.simd_stack_ptr, block.simd_results as u32);
+ fn exec_drop(&mut self) -> Result<()> {
+ self.stack.values.pop()?;
Ok(())
}
+ fn exec_select(&mut self) -> Result<()> {
+ let cond: i32 = self.stack.values.pop()?.into();
+ let val2 = self.stack.values.pop()?;
- #[inline(always)]
- fn exec_else(&mut self, end_offset: u32) -> Result<()> {
- let block = self.stack.blocks.pop()?;
+ // if cond != 0, we already have the right value on the stack
+ if cond == 0 {
+ *self.stack.values.last_mut()? = val2;
+ }
+ Ok(())
+ }
+ fn exec_call(&mut self, wasm_func: Rc<WasmFunction>, owner: ModuleInstanceAddr) -> Result<ControlFlow<()>> {
+ let params = self.stack.values.pop_n(wasm_func.ty.params.len())?;
+ let new_call_frame = CallFrame::new(wasm_func, owner, params, self.stack.blocks.len() as u32);
+ self.cf.instr_ptr += 1; // skip the call instruction
+ self.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?;
+ self.module.swap_with(self.cf.module_addr, self.store);
+ Ok(ControlFlow::Continue(()))
+ }
+ fn exec_call_direct(&mut self, v: u32) -> Result<ControlFlow<()>> {
+ let func_inst = self.store.get_func(self.module.resolve_func_addr(v))?;
+ let wasm_func = match &func_inst.func {
+ crate::Function::Wasm(wasm_func) => wasm_func,
+ crate::Function::Host(host_func) => {
+ let func = &host_func.clone();
+ let params = self.stack.values.pop_params(&host_func.ty.params)?;
+ let res = (func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, &params)?;
+ self.stack.values.extend_from_typed(&res);
+ self.cf.instr_ptr += 1;
+ return Ok(ControlFlow::Continue(()));
+ }
+ };
+ self.exec_call(wasm_func.clone(), func_inst.owner)
+ }
+ fn exec_call_indirect(&mut self, type_addr: u32, table_addr: u32) -> Result<ControlFlow<()>> {
+ // verify that the table is of the right type, this should be validated by the parser already
+ let func_ref = {
+ let table = self.store.get_table(self.module.resolve_table_addr(table_addr))?;
+ let table_idx: u32 = self.stack.values.pop()?.into();
+ let table = table.borrow();
+ assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref");
+ table
+ .get(table_idx)
+ .map_err(|_| Error::Trap(Trap::UndefinedElement { index: table_idx as usize }))?
+ .addr()
+ .ok_or(Trap::UninitializedElement { index: table_idx as usize })?
+ };
- self.stack.values.truncate_keep(block.stack_ptr, block.results as u32);
+ let func_inst = self.store.get_func(func_ref)?;
+ let call_ty = self.module.func_ty(type_addr);
+ let wasm_func = match &func_inst.func {
+ crate::Function::Wasm(f) => f,
+ crate::Function::Host(host_func) => {
+ if unlikely(host_func.ty != *call_ty) {
+ return Err(Error::Trap(Trap::IndirectCallTypeMismatch {
+ actual: host_func.ty.clone(),
+ expected: call_ty.clone(),
+ }));
+ }
- #[cfg(feature = "simd")]
- self.stack.values.truncate_keep_simd(block.simd_stack_ptr, block.simd_results as u32);
+ let host_func = host_func.clone();
+ let params = self.stack.values.pop_params(&host_func.ty.params)?;
+ let res = (host_func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, &params)?;
+ self.stack.values.extend_from_typed(&res);
+ self.cf.instr_ptr += 1;
+ return Ok(ControlFlow::Continue(()));
+ }
+ };
+ if wasm_func.ty == *call_ty {
+ return self.exec_call(wasm_func.clone(), func_inst.owner);
+ }
+
+ cold();
+ Err(Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }.into())
+ }
+
+ fn exec_if(&mut self, args: BlockArgs, else_offset: u32, end_offset: u32) -> Result<ControlFlow<()>> {
+ // truthy value is on the top of the stack, so enter the then block
+ if i32::from(self.stack.values.pop()?) != 0 {
+ self.enter_block(self.cf.instr_ptr, end_offset, BlockType::If, args);
+ self.cf.instr_ptr += 1;
+ return Ok(ControlFlow::Continue(()));
+ }
+
+ // falsy value is on the top of the stack
+ if else_offset == 0 {
+ self.cf.instr_ptr += end_offset as usize + 1;
+ return Ok(ControlFlow::Continue(()));
+ }
+
+ let old = self.cf.instr_ptr;
+ self.cf.instr_ptr += else_offset as usize;
+ self.enter_block(old + else_offset as usize, end_offset - else_offset, BlockType::Else, args);
+ self.cf.instr_ptr += 1;
+ Ok(ControlFlow::Continue(()))
+ }
+ fn exec_else(&mut self, end_offset: u32) -> Result<()> {
+ self.exec_end_block()?;
self.cf.instr_ptr += end_offset as usize;
Ok(())
}
+ fn enter_block(&mut self, instr_ptr: usize, end_instr_offset: u32, ty: BlockType, args: BlockArgs) {
+ #[cfg(not(feature = "simd"))]
+ {
+ let (params, results) = match args {
+ BlockArgs::Empty => (0, 0),
+ BlockArgs::Type(_) => (0, 1),
+ BlockArgs::FuncType(t) => {
+ let ty = self.module.func_ty(t);
+ (ty.params.len() as u8, ty.results.len() as u8)
+ }
+ };
- #[inline(always)]
+ self.stack.blocks.push(BlockFrame {
+ instr_ptr,
+ end_instr_offset,
+ stack_ptr: self.stack.values.len() as u32 - params as u32,
+ results,
+ params,
+ ty,
+ });
+ };
+
+ #[cfg(feature = "simd")]
+ {
+ let (params, results, simd_params, simd_results) = match args {
+ BlockArgs::Empty => (0, 0, 0, 0),
+ BlockArgs::Type(t) => match t {
+ ValType::V128 => (0, 0, 0, 1),
+ _ => (0, 1, 0, 0),
+ },
+ BlockArgs::FuncType(t) => {
+ let ty = self.module.func_ty(t);
+ let simd_params = ty.params.iter().filter(|t| t.is_simd()).count() as u8;
+ let simd_results = ty.results.iter().filter(|t| t.is_simd()).count() as u8;
+ let params = ty.params.len() as u8 - simd_params;
+ let results = ty.results.len() as u8 - simd_results;
+ (params, results, simd_params, simd_results)
+ }
+ };
+
+ self.stack.blocks.push(BlockFrame {
+ instr_ptr,
+ end_instr_offset,
+ stack_ptr: self.stack.values.len() as u32 - params as u32,
+ simd_stack_ptr: self.stack.values.simd_len() as u16 - simd_params as u16,
+ results,
+ simd_params,
+ simd_results,
+ params,
+ ty,
+ });
+ };
+ }
fn exec_br(&mut self, to: u32) -> Result<ControlFlow<()>> {
break_to!(to, self);
self.cf.instr_ptr += 1;
Ok(ControlFlow::Continue(()))
}
-
- #[inline(always)]
fn exec_br_if(&mut self, to: u32) -> Result<ControlFlow<()>> {
let val: i32 = self.stack.values.pop()?.into();
if val != 0 {
@@ -359,8 +483,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
self.cf.instr_ptr += 1;
Ok(ControlFlow::Continue(()))
}
-
- #[inline(always)]
fn exec_brtable(&mut self, default: u32, len: u32) -> Result<ControlFlow<()>> {
let start = self.cf.instr_ptr + 1;
let end = start + len as usize;
@@ -369,7 +491,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
}
let idx: i32 = self.stack.values.pop()?.into();
-
match self.cf.instructions()[start..end].get(idx as usize) {
None => break_to!(default, self),
Some(Instruction::BrLabel(to)) => break_to!(*to, self),
@@ -379,8 +500,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
self.cf.instr_ptr += 1;
Ok(ControlFlow::Continue(()))
}
-
- #[inline(always)]
fn exec_return(&mut self) -> Result<ControlFlow<()>> {
let old = self.cf.block_ptr;
match self.stack.call_stack.pop() {
@@ -395,238 +514,47 @@ impl<'store, 'stack> Executor<'store, 'stack> {
self.module.swap_with(self.cf.module_addr, self.store);
Ok(ControlFlow::Continue(()))
}
-
- #[inline(always)]
- #[cold]
- fn exec_unreachable(&self) -> Result<()> {
- Err(Error::Trap(Trap::Unreachable))
- }
-
- #[inline(always)]
- fn exec_noop(&self) {}
-
- #[inline(always)]
- fn exec_ref_is_null(&mut self) -> Result<()> {
- self.stack.values.replace_top(|val| ((i32::from(val) == -1) as i32).into())
- }
-
- #[inline(always)]
- fn exec_const(&mut self, val: impl Into<RawWasmValue>) {
- self.stack.values.push(val.into());
- }
-
- #[inline(always)]
- fn exec_i32_store_local(&mut self, local: u32, const_i32: i32, offset: u32, mem_addr: u8) -> Result<()> {
- let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr as u32))?;
- let val = const_i32.to_le_bytes();
- let addr: u64 = self.cf.get_local(local).into();
- mem.borrow_mut().store((offset as u64 + addr) as usize, val.len(), &val)?;
- Ok(())
- }
-
- #[inline(always)]
- fn exec_i32_local_get_const_add(&mut self, local: u32, val: i32) {
- let local: i32 = self.cf.get_local(local).into();
- self.stack.values.push((local + val).into());
- }
-
- #[inline(always)]
- fn exec_i64_xor_const_rotl(&mut self, rotate_by: i64) -> Result<()> {
- let val: i64 = self.stack.values.pop()?.into();
- let res = self.stack.values.last_mut()?;
- let mask: i64 = (*res).into();
- *res = (val ^ mask).rotate_left(rotate_by as u32).into();
+ fn exec_end_block(&mut self) -> Result<()> {
+ let block = self.stack.blocks.pop()?;
+ #[cfg(feature = "simd")]
+ self.stack.values.truncate_keep_simd(block.simd_stack_ptr, block.simd_results as u32);
+ self.stack.values.truncate_keep(block.stack_ptr, block.results as u32);
Ok(())
}
- #[inline(always)]
fn exec_local_get(&mut self, local_index: u32) {
self.stack.values.push(self.cf.get_local(local_index));
}
-
- #[inline(always)]
- fn exec_local_get2(&mut self, a: u32, b: u32) {
- self.stack.values.extend_from_slice(&[self.cf.get_local(a), self.cf.get_local(b)]);
- }
-
- #[inline(always)]
- fn exec_local_get3(&mut self, a: u32, b: u32, c: u32) {
- self.stack.values.extend_from_slice(&[self.cf.get_local(a), self.cf.get_local(b), self.cf.get_local(c)]);
- }
-
- #[inline(always)]
- fn exec_local_get_set(&mut self, a: u32, b: u32) {
- self.cf.set_local(b, self.cf.get_local(a))
- }
-
- #[inline(always)]
fn exec_local_set(&mut self, local_index: u32) -> Result<()> {
- self.cf.set_local(local_index, self.stack.values.pop()?);
- Ok(())
+ self.stack.values.pop().map(|val| self.cf.set_local(local_index, val))
}
-
- #[inline(always)]
fn exec_local_tee(&mut self, local_index: u32) -> Result<()> {
- self.cf.set_local(local_index, *self.stack.values.last()?);
- Ok(())
+ self.stack.values.last().map(|val| self.cf.set_local(local_index, *val))
}
-
- #[inline(always)]
- fn exec_local_tee_get(&mut self, a: u32, b: u32) -> Result<()> {
- let last = self.stack.values.last()?;
- self.cf.set_local(a, *last);
- self.stack.values.push(match a == b {
- true => *last,
- false => self.cf.get_local(b),
- });
- Ok(())
- }
-
- #[inline(always)]
fn exec_global_get(&mut self, global_index: u32) -> Result<()> {
self.stack.values.push(self.store.get_global_val(self.module.resolve_global_addr(global_index))?);
Ok(())
}
-
- #[inline(always)]
fn exec_global_set(&mut self, global_index: u32) -> Result<()> {
self.store.set_global_val(self.module.resolve_global_addr(global_index), self.stack.values.pop()?)
}
- #[inline(always)]
- fn exec_table_get(&mut self, table_index: u32) -> Result<()> {
- let table_idx = self.module.resolve_table_addr(table_index);
- let table = self.store.get_table(table_idx)?;
- let idx: u32 = self.stack.values.pop()?.into();
- let v = table.borrow().get_wasm_val(idx)?;
- self.stack.values.push(v.into());
- Ok(())
- }
-
- #[inline(always)]
- fn exec_table_set(&mut self, table_index: u32) -> Result<()> {
- let table_idx = self.module.resolve_table_addr(table_index);
- let table = self.store.get_table(table_idx)?;
- let val = self.stack.values.pop()?.as_reference();
- let idx = self.stack.values.pop()?.into();
- table.borrow_mut().set(idx, val.into())?;
-
- Ok(())
- }
-
- #[inline(always)]
- fn exec_table_size(&mut self, table_index: u32) -> Result<()> {
- let table_idx = self.module.resolve_table_addr(table_index);
- let table = self.store.get_table(table_idx)?;
- self.stack.values.push(table.borrow().size().into());
- Ok(())
- }
-
- #[inline(always)]
- fn exec_table_init(&mut self, elem_index: u32, table_index: u32) -> Result<()> {
- let table_idx = self.module.resolve_table_addr(table_index);
- let table = self.store.get_table(table_idx)?;
- let table_len = table.borrow().size();
- let elem = self.store.get_elem(self.module.resolve_elem_addr(elem_index))?;
- let elem_len = elem.items.as_ref().map(|items| items.len()).unwrap_or(0);
-
- let size: i32 = self.stack.values.pop()?.into(); // n
- let offset: i32 = self.stack.values.pop()?.into(); // s
- let dst: i32 = self.stack.values.pop()?.into(); // d
-
- if unlikely(((size + offset) as usize > elem_len) || ((dst + size) > table_len)) {
- return Err(Trap::TableOutOfBounds { offset: offset as usize, len: size as usize, max: elem_len }.into());
- }
-
- if size == 0 {
- return Ok(());
- }
-
- // TODO, not sure how to handle passive elements, but this makes the test pass
- if let ElementKind::Passive = elem.kind {
- return Ok(());
- }
-
- let Some(items) = elem.items.as_ref() else {
- return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into());
- };
-
- table.borrow_mut().init(self.module.func_addrs(), dst, &items[offset as usize..(offset + size) as usize])?;
- Ok(())
- }
-
- #[inline(always)]
- // todo: this is just a placeholder, need to check the spec
- fn exec_table_grow(&mut self, table_index: u32) -> Result<()> {
- let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
- let sz = table.borrow().size();
-
- let n: i32 = self.stack.values.pop()?.into();
- let val = self.stack.values.pop()?.as_reference();
-
- match table.borrow_mut().grow(n, val.into()) {
- Ok(_) => self.stack.values.push(sz.into()),
- Err(_) => self.stack.values.push((-1_i32).into()),
- }
-
- Ok(())
- }
-
- #[inline(always)]
- fn exec_table_fill(&mut self, table_index: u32) -> Result<()> {
- let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
-
- let n: i32 = self.stack.values.pop()?.into();
- let val = self.stack.values.pop()?.as_reference();
- let i: i32 = self.stack.values.pop()?.into();
-
- if unlikely(i + n > table.borrow().size()) {
- return Err(Trap::TableOutOfBounds {
- offset: i as usize,
- len: n as usize,
- max: table.borrow().size() as usize,
- }
- .into());
- }
-
- if n == 0 {
- return Ok(());
- }
-
- table.borrow_mut().fill(self.module.func_addrs(), i as usize, n as usize, val.into())?;
- Ok(())
- }
-
- #[inline(always)]
- fn exec_drop(&mut self) -> Result<()> {
- self.stack.values.pop()?;
- Ok(())
+ fn exec_const(&mut self, val: impl Into<RawWasmValue>) {
+ self.stack.values.push(val.into());
}
-
- #[inline(always)]
- fn exec_select(&mut self) -> Result<()> {
- let cond: i32 = self.stack.values.pop()?.into();
- let val2 = self.stack.values.pop()?;
- // if cond != 0, we already have the right value on the stack
- if cond == 0 {
- *self.stack.values.last_mut()? = val2;
- }
- Ok(())
+ fn exec_ref_is_null(&mut self) -> Result<()> {
+ self.stack.values.replace_top(|val| ((i32::from(val) == -1) as i32).into())
}
- #[inline(always)]
fn exec_memory_size(&mut self, addr: u32, byte: u8) -> Result<()> {
if unlikely(byte != 0) {
return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string()));
}
- let mem_idx = self.module.resolve_mem_addr(addr);
- let mem = self.store.get_mem(mem_idx)?;
+ let mem = self.store.get_mem(self.module.resolve_mem_addr(addr))?;
self.stack.values.push((mem.borrow().page_count() as i32).into());
Ok(())
}
-
- #[inline(always)]
fn exec_memory_grow(&mut self, addr: u32, byte: u8) -> Result<()> {
if unlikely(byte != 0) {
return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string()));
@@ -643,7 +571,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
Ok(())
}
- #[inline(always)]
fn exec_memory_copy(&mut self, from: u32, to: u32) -> Result<()> {
let size: i32 = self.stack.values.pop()?.into();
let src: i32 = self.stack.values.pop()?.into();
@@ -661,8 +588,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
}
Ok(())
}
-
- #[inline(always)]
fn exec_memory_fill(&mut self, addr: u32) -> Result<()> {
let size: i32 = self.stack.values.pop()?.into();
let val: i32 = self.stack.values.pop()?.into();
@@ -672,8 +597,6 @@ impl<'store, 'stack> Executor<'store, 'stack> {
mem.borrow_mut().fill(dst as usize, size as usize, val as u8)?;
Ok(())
}
-
- #[inline(always)]
fn exec_memory_init(&mut self, data_index: u32, mem_index: u32) -> Result<()> {
let size: i32 = self.stack.values.pop()?.into(); // n
let offset: i32 = self.stack.values.pop()?.into(); // s
@@ -700,20 +623,12 @@ impl<'store, 'stack> Executor<'store, 'stack> {
mem.borrow_mut().store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)])?;
Ok(())
}
-
- #[inline(always)]
fn exec_data_drop(&mut self, data_index: u32) -> Result<()> {
- self.store.get_data_mut(self.module.resolve_data_addr(data_index))?.drop();
- Ok(())
+ self.store.get_data_mut(self.module.resolve_data_addr(data_index)).map(|d| d.drop())
}
-
- #[inline(always)]
fn exec_elem_drop(&mut self, elem_index: u32) -> Result<()> {
- self.store.get_elem_mut(self.module.resolve_elem_addr(elem_index))?.drop();
- Ok(())
+ self.store.get_elem_mut(self.module.resolve_elem_addr(elem_index)).map(|e| e.drop())
}
-
- #[inline(always)]
fn exec_table_copy(&mut self, from: u32, to: u32) -> Result<()> {
let size: i32 = self.stack.values.pop()?.into();
let src: i32 = self.stack.values.pop()?.into();
@@ -732,151 +647,163 @@ impl<'store, 'stack> Executor<'store, 'stack> {
Ok(())
}
- #[inline(always)]
- fn exec_call(&mut self, wasm_func: Rc<WasmFunction>, owner: ModuleInstanceAddr) -> Result<ControlFlow<()>> {
- let params = self.stack.values.pop_n_rev(wasm_func.ty.params.len())?;
- let new_call_frame = CallFrame::new(wasm_func, owner, &params, self.stack.blocks.len() as u32);
- self.cf.instr_ptr += 1; // skip the call instruction
- self.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?;
- self.module.swap_with(self.cf.module_addr, self.store);
- Ok(ControlFlow::Continue(()))
+ fn exec_mem_load<LOAD: MemLoadable<LOAD_SIZE>, const LOAD_SIZE: usize, TARGET: Into<RawWasmValue>>(
+ &mut self,
+ cast: fn(LOAD) -> TARGET,
+ mem_addr: tinywasm_types::MemAddr,
+ offset: u64,
+ ) -> Result<()> {
+ let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr))?;
+ let val: u64 = self.stack.values.pop()?.into();
+ let Some(Ok(addr)) = offset.checked_add(val).map(|a| a.try_into()) else {
+ return Err(Error::Trap(crate::Trap::MemoryOutOfBounds {
+ offset: offset as usize,
+ len: LOAD_SIZE,
+ max: mem.borrow().max_pages(),
+ }));
+ };
+
+ let val = mem.borrow().load_as::<LOAD_SIZE, LOAD>(addr)?;
+ self.stack.values.push(cast(val).into());
+ Ok(())
+ }
+ fn exec_mem_store<T: From<RawWasmValue> + ToMemBytes<N>, const N: usize>(
+ &mut self,
+ mem_addr: tinywasm_types::MemAddr,
+ offset: u64,
+ ) -> Result<()> {
+ let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr))?;
+ let val: T = self.stack.values.pop()?.into();
+ let val = val.to_mem_bytes();
+ let addr: u64 = self.stack.values.pop()?.into();
+ mem.borrow_mut().store((offset + addr) as usize, val.len(), &val)?;
+ Ok(())
}
- #[inline(always)]
- fn exec_call_direct(&mut self, v: u32) -> Result<ControlFlow<()>> {
- let func_inst = self.store.get_func(self.module.resolve_func_addr(v))?;
- let wasm_func = match &func_inst.func {
- crate::Function::Wasm(wasm_func) => wasm_func,
- crate::Function::Host(host_func) => {
- let func = &host_func.clone();
- let params = self.stack.values.pop_params(&host_func.ty.params)?;
- let res = (func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, &params)?;
- self.stack.values.extend_from_typed(&res);
- self.cf.instr_ptr += 1;
- return Ok(ControlFlow::Continue(()));
- }
- };
- self.exec_call(wasm_func.clone(), func_inst.owner)
+ fn exec_table_get(&mut self, table_index: u32) -> Result<()> {
+ let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
+ let idx: u32 = self.stack.values.pop()?.into();
+ let v = table.borrow().get_wasm_val(idx)?;
+ self.stack.values.push(v.into());
+ Ok(())
}
+ fn exec_table_set(&mut self, table_index: u32) -> Result<()> {
+ let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
+ let val = self.stack.values.pop()?.as_reference();
+ let idx = self.stack.values.pop()?.into();
+ table.borrow_mut().set(idx, val.into())?;
- #[inline(always)]
- fn exec_call_indirect(&mut self, type_addr: u32, table_addr: u32) -> Result<ControlFlow<()>> {
- // verify that the table is of the right type, this should be validated by the parser already
- let func_ref = {
- let table = self.store.get_table(self.module.resolve_table_addr(table_addr))?;
- let table_idx: u32 = self.stack.values.pop()?.into();
- let table = table.borrow();
- assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref");
- table
- .get(table_idx)
- .map_err(|_| Error::Trap(Trap::UndefinedElement { index: table_idx as usize }))?
- .addr()
- .ok_or(Trap::UninitializedElement { index: table_idx as usize })?
- };
+ Ok(())
+ }
+ fn exec_table_size(&mut self, table_index: u32) -> Result<()> {
+ let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
+ self.stack.values.push(table.borrow().size().into());
+ Ok(())
+ }
+ fn exec_table_init(&mut self, elem_index: u32, table_index: u32) -> Result<()> {
+ let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
+ let table_len = table.borrow().size();
+ let elem = self.store.get_elem(self.module.resolve_elem_addr(elem_index))?;
+ let elem_len = elem.items.as_ref().map(|items| items.len()).unwrap_or(0);
- let func_inst = self.store.get_func(func_ref)?;
- let call_ty = self.module.func_ty(type_addr);
- let wasm_func = match &func_inst.func {
- crate::Function::Wasm(f) => f,
- crate::Function::Host(host_func) => {
- if unlikely(host_func.ty != *call_ty) {
- return Err(Error::Trap(Trap::IndirectCallTypeMismatch {
- actual: host_func.ty.clone(),
- expected: call_ty.clone(),
- }));
- }
+ let size: i32 = self.stack.values.pop()?.into(); // n
+ let offset: i32 = self.stack.values.pop()?.into(); // s
+ let dst: i32 = self.stack.values.pop()?.into(); // d
- let host_func = host_func.clone();
- let params = self.stack.values.pop_params(&host_func.ty.params)?;
- let res = (host_func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, &params)?;
- self.stack.values.extend_from_typed(&res);
- self.cf.instr_ptr += 1;
- return Ok(ControlFlow::Continue(()));
- }
- };
+ if unlikely(((size + offset) as usize > elem_len) || ((dst + size) > table_len)) {
+ return Err(Trap::TableOutOfBounds { offset: offset as usize, len: size as usize, max: elem_len }.into());
+ }
- if wasm_func.ty == *call_ty {
- return self.exec_call(wasm_func.clone(), func_inst.owner);
+ if size == 0 {
+ return Ok(());
}
- cold();
- Err(Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }.into())
+ // TODO, not sure how to handle passive elements, but this makes the test pass
+ if let ElementKind::Passive = elem.kind {
+ return Ok(());
+ }
+
+ let Some(items) = elem.items.as_ref() else {
+ return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into());
+ };
+
+ table.borrow_mut().init(self.module.func_addrs(), dst, &items[offset as usize..(offset + size) as usize])?;
+ Ok(())
}
+ // todo: this is just a placeholder, need to check the spec
+ fn exec_table_grow(&mut self, table_index: u32) -> Result<()> {
+ let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
+ let sz = table.borrow().size();
- #[inline(always)]
- fn exec_if(&mut self, args: BlockArgs, else_offset: u32, end_offset: u32) -> Result<ControlFlow<()>> {
- // truthy value is on the top of the stack, so enter the then block
- if i32::from(self.stack.values.pop()?) != 0 {
- self.enter_block(self.cf.instr_ptr, end_offset, BlockType::If, args);
- self.cf.instr_ptr += 1;
- return Ok(ControlFlow::Continue(()));
- }
+ let n: i32 = self.stack.values.pop()?.into();
+ let val = self.stack.values.pop()?.as_reference();
- // falsy value is on the top of the stack
- if else_offset == 0 {
- self.cf.instr_ptr += end_offset as usize + 1;
- return Ok(ControlFlow::Continue(()));
+ match table.borrow_mut().grow(n, val.into()) {
+ Ok(_) => self.stack.values.push(sz.into()),
+ Err(_) => self.stack.values.push((-1_i32).into()),
}
- let old = self.cf.instr_ptr;
- self.cf.instr_ptr += else_offset as usize;
- self.enter_block(old + else_offset as usize, end_offset - else_offset, BlockType::Else, args);
- self.cf.instr_ptr += 1;
- Ok(ControlFlow::Continue(()))
+ Ok(())
}
+ fn exec_table_fill(&mut self, table_index: u32) -> Result<()> {
+ let table = self.store.get_table(self.module.resolve_table_addr(table_index))?;
- #[inline(always)]
- fn enter_block(&mut self, instr_ptr: usize, end_instr_offset: u32, ty: BlockType, args: BlockArgs) {
- #[cfg(not(feature = "simd"))]
- {
- let (params, results) = match args {
- BlockArgs::Empty => (0, 0),
- BlockArgs::Type(_) => (0, 1),
- BlockArgs::FuncType(t) => {
- let ty = self.module.func_ty(t);
- (ty.params.len() as u8, ty.results.len() as u8)
- }
- };
+ let n: i32 = self.stack.values.pop()?.into();
+ let val = self.stack.values.pop()?.as_reference();
+ let i: i32 = self.stack.values.pop()?.into();
- self.stack.blocks.push(BlockFrame {
- instr_ptr,
- end_instr_offset,
- stack_ptr: self.stack.values.len() as u32 - params as u32,
- results,
- params,
- ty,
- });
- };
+ if unlikely(i + n > table.borrow().size()) {
+ return Err(Error::Trap(Trap::TableOutOfBounds {
+ offset: i as usize,
+ len: n as usize,
+ max: table.borrow().size() as usize,
+ }));
+ }
- #[cfg(feature = "simd")]
- {
- let (params, results, simd_params, simd_results) = match args {
- BlockArgs::Empty => (0, 0, 0, 0),
- BlockArgs::Type(t) => match t {
- ValType::V128 => (0, 0, 0, 1),
- _ => (0, 1, 0, 0),
- },
- BlockArgs::FuncType(t) => {
- let ty = self.module.func_ty(t);
- let simd_params = ty.params.iter().filter(|t| t.is_simd()).count() as u8;
- let params = ty.params.len() as u8 - simd_params;
- let simd_results = ty.results.iter().filter(|t| t.is_simd()).count() as u8;
- let results = ty.results.len() as u8 - simd_results;
- (params, results, simd_params, simd_results)
- }
- };
+ if n == 0 {
+ return Ok(());
+ }
- self.stack.blocks.push(BlockFrame {
- instr_ptr,
- end_instr_offset,
- stack_ptr: self.stack.values.len() as u32 - params as u32,
- simd_stack_ptr: self.stack.values.simd_len() as u16 - simd_params as u16,
- results,
- simd_params,
- simd_results,
- params,
- ty,
- });
- };
+ table.borrow_mut().fill(self.module.func_addrs(), i as usize, n as usize, val.into())?;
+ Ok(())
+ }
+
+ // custom instructions
+
+ fn exec_i32_store_local(&mut self, local: u32, const_i32: i32, offset: u32, mem_addr: u8) -> Result<()> {
+ let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr as u32))?;
+ let val = const_i32.to_le_bytes();
+ let addr: u64 = self.cf.get_local(local).into();
+ mem.borrow_mut().store((offset as u64 + addr) as usize, val.len(), &val)?;
+ Ok(())
+ }
+ fn exec_i32_local_get_const_add(&mut self, local: u32, val: i32) {
+ let local: i32 = self.cf.get_local(local).into();
+ self.stack.values.push((local + val).into());
+ }
+ fn exec_i64_xor_const_rotl(&mut self, rotate_by: i64) -> Result<()> {
+ let val: i64 = self.stack.values.pop()?.into();
+ let res = self.stack.values.last_mut()?;
+ let mask: i64 = (*res).into();
+ *res = (val ^ mask).rotate_left(rotate_by as u32).into();
+ Ok(())
+ }
+ fn exec_local_get2(&mut self, a: u32, b: u32) {
+ self.stack.values.extend_from_slice(&[self.cf.get_local(a), self.cf.get_local(b)]);
+ }
+ fn exec_local_get3(&mut self, a: u32, b: u32, c: u32) {
+ self.stack.values.extend_from_slice(&[self.cf.get_local(a), self.cf.get_local(b), self.cf.get_local(c)]);
+ }
+ fn exec_local_get_set(&mut self, a: u32, b: u32) {
+ self.cf.set_local(b, self.cf.get_local(a))
+ }
+ fn exec_local_tee_get(&mut self, a: u32, b: u32) -> Result<()> {
+ let last = self.stack.values.last()?;
+ self.cf.set_local(a, *last);
+ self.stack.values.push(match a == b {
+ true => *last,
+ false => self.cf.get_local(b),
+ });
+ Ok(())
}
}
diff --git a/crates/tinywasm/src/runtime/raw.rs b/crates/tinywasm/src/runtime/raw.rs
index a62643e..877dcb1 100644
--- a/crates/tinywasm/src/runtime/raw.rs
+++ b/crates/tinywasm/src/runtime/raw.rs
@@ -7,7 +7,7 @@ use tinywasm_types::{ValType, WasmValue};
///
/// See [`WasmValue`] for the public representation.
#[derive(Clone, Copy, Default, PartialEq, Eq)]
-pub struct RawWasmValue([u8; 8]);
+pub struct RawWasmValue(pub [u8; 8]);
impl Debug for RawWasmValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
@@ -15,21 +15,34 @@ impl Debug for RawWasmValue {
}
}
-impl RawWasmValue {
- #[inline(always)]
- /// Get the raw value
- pub fn raw_value(&self) -> [u8; 8] {
- self.0
- }
+pub(crate) trait ToMemBytes<const N: usize> {
+ fn to_mem_bytes(self) -> [u8; N];
+}
+macro_rules! impl_to_mem_bytes {
+ ($( $ty:ty, $n:expr ),*) => {
+ $(
+ impl ToMemBytes<$n> for $ty {
+ #[inline]
+ fn to_mem_bytes(self) -> [u8; $n] {
+ self.to_ne_bytes()
+ }
+ }
+ )*
+ };
+}
+
+impl_to_mem_bytes! {u8, 1, u16, 2, u32, 4, u64, 8, i8, 1, i16, 2, i32, 4, i64, 8, f32, 4, f64, 8}
+
+impl RawWasmValue {
#[inline]
/// Attach a type to the raw value (does not support simd values)
pub fn attach_type(self, ty: ValType) -> WasmValue {
match ty {
ValType::I32 => WasmValue::I32(self.into()),
ValType::I64 => WasmValue::I64(self.into()),
- ValType::F32 => WasmValue::F32(f32::from_bits(self.into())),
- ValType::F64 => WasmValue::F64(f64::from_bits(self.into())),
+ ValType::F32 => WasmValue::F32(self.into()),
+ ValType::F64 => WasmValue::F64(self.into()),
ValType::V128 => panic!("RawWasmValue cannot be converted to V128"),
ValType::RefExtern => {
self.as_reference().map(WasmValue::RefExtern).unwrap_or(WasmValue::RefNull(ValType::RefExtern))
@@ -88,14 +101,15 @@ macro_rules! impl_from_raw_wasm_value {
}
// This all looks like a lot of extra steps, but the compiler will optimize it all away.
-impl_from_raw_wasm_value!(i32, |x| x as u64, |x: [u8; 8]| i32::from_ne_bytes(x[0..4].try_into().unwrap()));
-impl_from_raw_wasm_value!(i64, |x| x as u64, |x: [u8; 8]| i64::from_ne_bytes(x[0..8].try_into().unwrap()));
-impl_from_raw_wasm_value!(u8, |x| x as u64, |x: [u8; 8]| u8::from_ne_bytes(x[0..1].try_into().unwrap()));
+impl_from_raw_wasm_value!(i16, |x| x as u64, |x: [u8; 8]| i16::from_ne_bytes(x[0..2].try_into().unwrap()));
impl_from_raw_wasm_value!(u16, |x| x as u64, |x: [u8; 8]| u16::from_ne_bytes(x[0..2].try_into().unwrap()));
+impl_from_raw_wasm_value!(i32, |x| x as u64, |x: [u8; 8]| i32::from_ne_bytes(x[0..4].try_into().unwrap()));
impl_from_raw_wasm_value!(u32, |x| x as u64, |x: [u8; 8]| u32::from_ne_bytes(x[0..4].try_into().unwrap()));
+impl_from_raw_wasm_value!(i64, |x| x as u64, |x: [u8; 8]| i64::from_ne_bytes(x[0..8].try_into().unwrap()));
impl_from_raw_wasm_value!(u64, |x| x, |x: [u8; 8]| u64::from_ne_bytes(x[0..8].try_into().unwrap()));
impl_from_raw_wasm_value!(i8, |x| x as u64, |x: [u8; 8]| i8::from_ne_bytes(x[0..1].try_into().unwrap()));
-impl_from_raw_wasm_value!(i16, |x| x as u64, |x: [u8; 8]| i16::from_ne_bytes(x[0..2].try_into().unwrap()));
+impl_from_raw_wasm_value!(u8, |x| x as u64, |x: [u8; 8]| u8::from_ne_bytes(x[0..1].try_into().unwrap()));
+
impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as u64, |x: [u8; 8]| f32::from_ne_bytes(
x[0..4].try_into().unwrap()
));
diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs
index 4bf678e..08b80c1 100644
--- a/crates/tinywasm/src/runtime/stack/call_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/call_stack.rs
@@ -1,11 +1,11 @@
+use super::BlockType;
use crate::runtime::RawWasmValue;
use crate::unlikely;
use crate::{Result, Trap};
+
use alloc::{boxed::Box, rc::Rc, vec::Vec};
use tinywasm_types::{Instruction, LocalAddr, ModuleInstanceAddr, WasmFunction};
-use super::BlockType;
-
const CALL_STACK_SIZE: usize = 1024;
#[derive(Debug)]
diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs
index 49710af..3668670 100644
--- a/crates/tinywasm/src/runtime/stack/value_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/value_stack.rs
@@ -1,5 +1,5 @@
use crate::{boxvec::BoxVec, cold, runtime::RawWasmValue, unlikely, Error, Result};
-use alloc::{borrow::Cow, vec::Vec};
+use alloc::vec::Vec;
use tinywasm_types::{ValType, WasmValue};
use super::BlockFrame;
@@ -202,11 +202,14 @@ impl ValueStack {
}
#[inline]
- pub(crate) fn pop_n_rev(&mut self, n: usize) -> Result<Cow<'_, [RawWasmValue]>> {
- if unlikely(self.stack.len() < n) {
- return Err(Error::ValueStackUnderflow);
+ pub(crate) fn pop_n(&mut self, n: usize) -> Result<&[RawWasmValue]> {
+ match self.stack.pop_n(n) {
+ Some(v) => Ok(v),
+ None => {
+ cold();
+ Err(Error::ValueStackUnderflow)
+ }
}
- Ok(self.stack.drain((self.stack.len() - n)..))
}
}