From 9d7c1924f1d0e9f79e31da4b84d9da12c3d37945 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Wed, 29 May 2024 01:22:31 +0200 Subject: chore: improve parser, clean up interpreter loop Signed-off-by: Henry Gressmann --- crates/parser/src/conversion.rs | 28 +- crates/parser/src/lib.rs | 45 +- crates/parser/src/module.rs | 46 +- crates/parser/src/visit.rs | 264 +++----- crates/tinywasm/src/boxvec.rs | 20 +- crates/tinywasm/src/instance.rs | 2 +- crates/tinywasm/src/runtime/interpreter/macros.rs | 64 -- crates/tinywasm/src/runtime/interpreter/mod.rs | 769 ++++++++++------------ crates/tinywasm/src/runtime/raw.rs | 40 +- crates/tinywasm/src/runtime/stack/call_stack.rs | 4 +- crates/tinywasm/src/runtime/stack/value_stack.rs | 13 +- 11 files changed, 570 insertions(+), 725 deletions(-) (limited to 'crates') 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 "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::>().into_boxed_slice(); let results = ty.results().iter().map(convert_valtype).collect::>().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 { let ops = ops.into_iter().collect::>>()?; // 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 for TinyWasmModule { type Error = ParseError; fn try_from(reader: ModuleReader) -> Result { - 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::>(); - - 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 { + 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::>(); + + 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; + type Output = Result<()>; wasmparser::for_each_operator!(validate_then_visit); } -pub(crate) fn process_operators( - validator: Option<&mut FuncValidator>, +pub(crate) fn process_operators_and_validate( + validator: &mut FuncValidator, body: FunctionBody<'_>, ) -> Result> { 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, label_ptrs: Vec, + errors: Vec, } 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 { impl BoxVec { #[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)] @@ -66,6 +69,19 @@ impl BoxVec { } } + #[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) -> Cow<'_, [T]> { let start = match range.start_bound() { 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::(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 { - 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 { 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), - - 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), + I32Store { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + I64Store { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + F32Store { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + F64Store { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + I32Store8 { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + I32Store16 { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + I64Store8 { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + I64Store16 { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + I64Store32 { mem_addr, offset } => self.exec_mem_store::(*mem_addr, *offset)?, + + I32Load { mem_addr, offset } => self.exec_mem_load::(|v| v, *mem_addr, *offset)?, + I64Load { mem_addr, offset } => self.exec_mem_load::(|v| v, *mem_addr, *offset)?, + F32Load { mem_addr, offset } => self.exec_mem_load::(|v| v, *mem_addr, *offset)?, + F64Load { mem_addr, offset } => self.exec_mem_load::(|v| v, *mem_addr, *offset)?, + I32Load8S { mem_addr, offset } => self.exec_mem_load::(|v| v as i32, *mem_addr, *offset)?, + I32Load8U { mem_addr, offset } => self.exec_mem_load::(|v| v as i32, *mem_addr, *offset)?, + I32Load16S { mem_addr, offset } => self.exec_mem_load::(|v| v as i32, *mem_addr, *offset)?, + I32Load16U { mem_addr, offset } => self.exec_mem_load::(|v| v as i32, *mem_addr, *offset)?, + I64Load8S { mem_addr, offset } => self.exec_mem_load::(|v| v as i64, *mem_addr, *offset)?, + I64Load8U { mem_addr, offset } => self.exec_mem_load::(|v| v as i64, *mem_addr, *offset)?, + I64Load16S { mem_addr, offset } => self.exec_mem_load::(|v| v as i64, *mem_addr, *offset)?, + I64Load16U { mem_addr, offset } => self.exec_mem_load::(|v| v as i64, *mem_addr, *offset)?, + I64Load32S { mem_addr, offset } => self.exec_mem_load::(|v| v as i64, *mem_addr, *offset)?, + I64Load32U { mem_addr, offset } => self.exec_mem_load::(|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, owner: ModuleInstanceAddr) -> Result> { + 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> { + 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> { + // 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> { + // 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> { break_to!(to, self); self.cf.instr_ptr += 1; Ok(ControlFlow::Continue(())) } - - #[inline(always)] fn exec_br_if(&mut self, to: u32) -> Result> { 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> { 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> { let old = self.cf.block_ptr; match self.stack.call_stack.pop() { @@ -395,137 +514,194 @@ 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)) + 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_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()) + fn exec_local_get(&mut self, local_index: u32) { + self.stack.values.push(self.cf.get_local(local_index)); + } + fn exec_local_set(&mut self, local_index: u32) -> Result<()> { + self.stack.values.pop().map(|val| self.cf.set_local(local_index, val)) + } + fn exec_local_tee(&mut self, local_index: u32) -> Result<()> { + self.stack.values.last().map(|val| self.cf.set_local(local_index, *val)) + } + 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(()) + } + 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_const(&mut self, val: impl Into) { 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(()) + 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_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_memory_size(&mut self, addr: u32, byte: u8) -> Result<()> { + if unlikely(byte != 0) { + return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); + } - #[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(); + let mem = self.store.get_mem(self.module.resolve_mem_addr(addr))?; + self.stack.values.push((mem.borrow().page_count() as i32).into()); Ok(()) } + 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())); + } - #[inline(always)] - fn exec_local_get(&mut self, local_index: u32) { - self.stack.values.push(self.cf.get_local(local_index)); - } + let mut mem = self.store.get_mem(self.module.resolve_mem_addr(addr))?.borrow_mut(); + let prev_size = mem.page_count() as i32; + let pages_delta = self.stack.values.last_mut()?; + *pages_delta = match mem.grow(i32::from(*pages_delta)) { + Some(_) => prev_size.into(), + None => (-1).into(), + }; - #[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)]); + Ok(()) } - #[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)]); - } + 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(); + let dst: i32 = self.stack.values.pop()?.into(); - #[inline(always)] - fn exec_local_get_set(&mut self, a: u32, b: u32) { - self.cf.set_local(b, self.cf.get_local(a)) + if from == to { + let mut mem_from = self.store.get_mem(self.module.resolve_mem_addr(from))?.borrow_mut(); + // copy within the same memory + mem_from.copy_within(dst as usize, src as usize, size as usize)?; + } else { + // copy between two memories + let mem_from = self.store.get_mem(self.module.resolve_mem_addr(from))?.borrow(); + let mut mem_to = self.store.get_mem(self.module.resolve_mem_addr(to))?.borrow_mut(); + mem_to.copy_from_slice(dst as usize, mem_from.load(src as usize, size as usize)?)?; + } + Ok(()) } + 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(); + let dst: i32 = self.stack.values.pop()?.into(); - #[inline(always)] - fn exec_local_set(&mut self, local_index: u32) -> Result<()> { - self.cf.set_local(local_index, self.stack.values.pop()?); + let mem = self.store.get_mem(self.module.resolve_mem_addr(addr))?; + mem.borrow_mut().fill(dst as usize, size as usize, val as u8)?; Ok(()) } + 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 + let dst: i32 = self.stack.values.pop()?.into(); // d - #[inline(always)] - fn exec_local_tee(&mut self, local_index: u32) -> Result<()> { - self.cf.set_local(local_index, *self.stack.values.last()?); + let data = self.store.get_data(self.module.resolve_data_addr(data_index))?; + let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_index))?; + + let data_len = data.data.as_ref().map(|d| d.len()).unwrap_or(0); + + if unlikely(((size + offset) as usize > data_len) || ((dst + size) as usize > mem.borrow().len())) { + return Err(Trap::MemoryOutOfBounds { offset: offset as usize, len: size as usize, max: data_len }.into()); + } + + if size == 0 { + return Ok(()); + } + + let data = match &data.data { + Some(data) => data, + None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()), + }; + + mem.borrow_mut().store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)])?; Ok(()) } + fn exec_data_drop(&mut self, data_index: u32) -> Result<()> { + self.store.get_data_mut(self.module.resolve_data_addr(data_index)).map(|d| d.drop()) + } + fn exec_elem_drop(&mut self, elem_index: u32) -> Result<()> { + self.store.get_elem_mut(self.module.resolve_elem_addr(elem_index)).map(|e| e.drop()) + } + 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(); + let dst: i32 = self.stack.values.pop()?.into(); - #[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), - }); + if from == to { + let mut table_from = self.store.get_table(self.module.resolve_table_addr(from))?.borrow_mut(); + // copy within the same memory + table_from.copy_within(dst as usize, src as usize, size as usize)?; + } else { + // copy between two memories + let table_from = self.store.get_table(self.module.resolve_table_addr(from))?.borrow(); + let mut table_to = self.store.get_table(self.module.resolve_table_addr(to))?.borrow_mut(); + table_to.copy_from_slice(dst as usize, table_from.load(src as usize, size as usize)?)?; + } 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))?); + fn exec_mem_load, const LOAD_SIZE: usize, TARGET: Into>( + &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::(addr)?; + self.stack.values.push(cast(val).into()); 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()?) + fn exec_mem_store + ToMemBytes, 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_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 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(()) } - - #[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 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())?; 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)?; + let table = self.store.get_table(self.module.resolve_table_addr(table_index))?; 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 = 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); @@ -554,8 +730,6 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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))?; @@ -571,8 +745,6 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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))?; @@ -581,12 +753,11 @@ impl<'store, 'stack> Executor<'store, 'stack> { let i: i32 = self.stack.values.pop()?.into(); if unlikely(i + n > table.borrow().size()) { - return Err(Trap::TableOutOfBounds { + return Err(Error::Trap(Trap::TableOutOfBounds { offset: i as usize, len: n as usize, max: table.borrow().size() as usize, - } - .into()); + })); } if n == 0 { @@ -597,286 +768,42 @@ impl<'store, 'stack> Executor<'store, 'stack> { Ok(()) } - #[inline(always)] - fn exec_drop(&mut self) -> Result<()> { - self.stack.values.pop()?; - Ok(()) - } - - #[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(()) - } - - #[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)?; - 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())); - } - - let mut mem = self.store.get_mem(self.module.resolve_mem_addr(addr))?.borrow_mut(); - let prev_size = mem.page_count() as i32; - let pages_delta = self.stack.values.last_mut()?; - *pages_delta = match mem.grow(i32::from(*pages_delta)) { - Some(_) => prev_size.into(), - None => (-1).into(), - }; + // 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(()) } - - #[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(); - let dst: i32 = self.stack.values.pop()?.into(); - - if from == to { - let mut mem_from = self.store.get_mem(self.module.resolve_mem_addr(from))?.borrow_mut(); - // copy within the same memory - mem_from.copy_within(dst as usize, src as usize, size as usize)?; - } else { - // copy between two memories - let mem_from = self.store.get_mem(self.module.resolve_mem_addr(from))?.borrow(); - let mut mem_to = self.store.get_mem(self.module.resolve_mem_addr(to))?.borrow_mut(); - mem_to.copy_from_slice(dst as usize, mem_from.load(src as usize, size as usize)?)?; - } - 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()); } - - #[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(); - let dst: i32 = self.stack.values.pop()?.into(); - - let mem = self.store.get_mem(self.module.resolve_mem_addr(addr))?; - mem.borrow_mut().fill(dst as usize, size as usize, val as u8)?; + 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(()) } - - #[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 - let dst: i32 = self.stack.values.pop()?.into(); // d - - let data = self.store.get_data(self.module.resolve_data_addr(data_index))?; - let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_index))?; - - let data_len = data.data.as_ref().map(|d| d.len()).unwrap_or(0); - - if unlikely(((size + offset) as usize > data_len) || ((dst + size) as usize > mem.borrow().len())) { - return Err(Trap::MemoryOutOfBounds { offset: offset as usize, len: size as usize, max: data_len }.into()); - } - - if size == 0 { - return Ok(()); - } - - let data = match &data.data { - Some(data) => data, - None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()), - }; - - mem.borrow_mut().store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)])?; - 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)]); } - - #[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(()) + 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_elem_drop(&mut self, elem_index: u32) -> Result<()> { - self.store.get_elem_mut(self.module.resolve_elem_addr(elem_index))?.drop(); - Ok(()) + 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_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(); - let dst: i32 = self.stack.values.pop()?.into(); - - if from == to { - let mut table_from = self.store.get_table(self.module.resolve_table_addr(from))?.borrow_mut(); - // copy within the same memory - table_from.copy_within(dst as usize, src as usize, size as usize)?; - } else { - // copy between two memories - let table_from = self.store.get_table(self.module.resolve_table_addr(from))?.borrow(); - let mut table_to = self.store.get_table(self.module.resolve_table_addr(to))?.borrow_mut(); - table_to.copy_from_slice(dst as usize, table_from.load(src as usize, size as usize)?)?; - } + 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_call(&mut self, wasm_func: Rc, owner: ModuleInstanceAddr) -> Result> { - 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(())) - } - - #[inline(always)] - fn exec_call_direct(&mut self, v: u32) -> Result> { - 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) - } - - #[inline(always)] - fn exec_call_indirect(&mut self, type_addr: u32, table_addr: u32) -> Result> { - // 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 })? - }; - - 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 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()) - } - - #[inline(always)] - fn exec_if(&mut self, args: BlockArgs, else_offset: u32, end_offset: u32) -> Result> { - // 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(())) - } - - #[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) - } - }; - - 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 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) - } - }; - - 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, - }); - }; - } } 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 { + 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> { - 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)..)) } } -- cgit v1.3.1