diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-05-29 01:22:31 +0200 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-05-29 01:22:31 +0200 |
| commit | 9d7c1924f1d0e9f79e31da4b84d9da12c3d37945 (patch) | |
| tree | f3a98df3fdb4a87a1ecd321cb96aa8255b95d615 | |
| parent | dce86f8f9ad919fa2dc1db043209fe1b1c9f08de (diff) | |
chore: improve parser, clean up interpreter loop
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
| -rw-r--r-- | benchmarks/benches/selfhosted.rs | 10 | ||||
| -rw-r--r-- | crates/parser/src/conversion.rs | 28 | ||||
| -rw-r--r-- | crates/parser/src/lib.rs | 45 | ||||
| -rw-r--r-- | crates/parser/src/module.rs | 46 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 264 | ||||
| -rw-r--r-- | crates/tinywasm/src/boxvec.rs | 20 | ||||
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/macros.rs | 64 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/mod.rs | 739 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/raw.rs | 40 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/call_stack.rs | 4 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/value_stack.rs | 13 |
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() }, ¶ms)?; + 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() }, ¶ms)?; + 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, ¶ms, 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() }, ¶ms)?; - 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() }, ¶ms)?; - 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)..)) } } |
