From 644395712a81c8f3fd56b6beafe9d3e09dfa9501 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Fri, 28 Jun 2024 01:10:54 +0200 Subject: feat: compact value stack (#21) Not fully done, but ready for the `next` branch now. This changes how values are represented internally, making this pr particularly large, as this touches most parts of the codebase. The parser should now also be ready for multithreaded compilation to speed this up a bit. --------- Signed-off-by: Henry Gressmann --- CHANGELOG.md | 9 + Cargo.lock | 28 +- crates/cli/Cargo.toml | 2 +- crates/parser/Cargo.toml | 2 +- crates/parser/src/conversion.rs | 48 +- crates/parser/src/lib.rs | 1 + crates/parser/src/module.rs | 11 +- crates/parser/src/visit.rs | 217 +++--- crates/tinywasm/Cargo.toml | 12 +- crates/tinywasm/src/boxvec.rs | 148 ---- crates/tinywasm/src/func.rs | 13 +- crates/tinywasm/src/lib.rs | 1 - crates/tinywasm/src/runtime/interpreter/macros.rs | 131 ---- crates/tinywasm/src/runtime/interpreter/mod.rs | 768 ++++++++++----------- .../src/runtime/interpreter/num_helpers.rs | 163 +++++ crates/tinywasm/src/runtime/interpreter/traits.rs | 119 ---- crates/tinywasm/src/runtime/mod.rs | 16 +- crates/tinywasm/src/runtime/raw.rs | 123 ---- crates/tinywasm/src/runtime/raw_simd.rs | 27 - crates/tinywasm/src/runtime/stack/block_stack.rs | 15 +- crates/tinywasm/src/runtime/stack/call_stack.rs | 81 ++- crates/tinywasm/src/runtime/stack/mod.rs | 3 +- crates/tinywasm/src/runtime/stack/value_stack.rs | 291 +++----- crates/tinywasm/src/runtime/stack/values.rs | 420 +++++++++++ crates/tinywasm/src/store/global.rs | 8 +- crates/tinywasm/src/store/mod.rs | 38 +- crates/tinywasm/tests/generated/2.0.csv | 2 +- crates/types/src/instructions.rs | 66 +- crates/types/src/lib.rs | 11 +- 29 files changed, 1421 insertions(+), 1353 deletions(-) delete mode 100644 crates/tinywasm/src/boxvec.rs delete mode 100644 crates/tinywasm/src/runtime/interpreter/macros.rs create mode 100644 crates/tinywasm/src/runtime/interpreter/num_helpers.rs delete mode 100644 crates/tinywasm/src/runtime/interpreter/traits.rs delete mode 100644 crates/tinywasm/src/runtime/raw.rs delete mode 100644 crates/tinywasm/src/runtime/raw_simd.rs create mode 100644 crates/tinywasm/src/runtime/stack/values.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f6539a1..da8359c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [UNRELEASED] + +### Changed + +- Improved support for WebAssembly 2.0 features +- Simplify and optimize the interpreter loop +- Use a seperate stack and locals for 32, 64 and 128 bit values and references (#21) +- Updated to latest wasmparser version + ## [0.7.0] - 2024-05-15 **All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.6.0...v0.7.0 diff --git a/Cargo.lock b/Cargo.lock index d432d76..7a7412a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,9 +295,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" +checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" [[package]] name = "cfg-if" @@ -768,9 +768,9 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum-iterator" @@ -1999,7 +1999,7 @@ dependencies = [ "tinywasm-parser", "tinywasm-types", "wasm-testsuite", - "wast 211.0.1", + "wast 212.0.0", ] [[package]] @@ -2011,7 +2011,7 @@ dependencies = [ "log", "pretty_env_logger", "tinywasm", - "wast 211.0.1", + "wast 212.0.0", ] [[package]] @@ -2020,7 +2020,7 @@ version = "0.7.0" dependencies = [ "log", "tinywasm-types", - "wasmparser 0.211.1", + "wasmparser 0.212.0", ] [[package]] @@ -2304,9 +2304,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.211.1" +version = "0.212.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e7d931a1120ef357f32b74547646b6fa68ea25e377772b72874b131a9ed70d4" +checksum = "501940df4418b8929eb6d52f1aade1fdd15a5b86c92453cb696e3c906bd3fc33" dependencies = [ "leb128", ] @@ -2550,9 +2550,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.211.1" +version = "0.212.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3189cc8a91f547390e2f043ca3b3e3fe0892f7d581767fd4e4b7f3dc3fe8e561" +checksum = "8d28bc49ba1e5c5b61ffa7a2eace10820443c4b7d1c0b144109261d14570fdf8" dependencies = [ "ahash 0.8.11", "bitflags 2.6.0", @@ -2584,15 +2584,15 @@ dependencies = [ [[package]] name = "wast" -version = "211.0.1" +version = "212.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b25506dd82d00da6b14a87436b3d52b1d264083fa79cdb72a0d1b04a8595ccaa" +checksum = "4606a05fb0aae5d11dd7d8280a640d88a63ee019360ba9be552da3d294b8d1f5" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.211.1", + "wasm-encoder 0.212.0", ] [[package]] diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 60207a6..bccf998 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -20,7 +20,7 @@ argh="0.1" color-eyre={version="0.6", default-features=false} log="0.4" pretty_env_logger="0.5" -wast={version="211.0", optional=true} +wast={version="212.0", optional=true} [features] default=["wat"] diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 5b28112..89d2b71 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -9,7 +9,7 @@ repository.workspace=true rust-version.workspace=true [dependencies] -wasmparser={version="0.211", default-features=false, features=["validate"]} +wasmparser={version="0.212", default-features=false, features=["validate"]} log={version="0.4", optional=true} tinywasm-types={version="0.7.0", path="../types", default-features=false} diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 59c023e..f18b570 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -2,7 +2,7 @@ use crate::Result; 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}; +use wasmparser::{FuncValidator, FuncValidatorAllocations, OperatorsReader, ValidatorResources}; pub(crate) fn convert_module_elements<'a, T: IntoIterator>>>( elements: T, @@ -168,27 +168,45 @@ pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result, - validator: &mut FuncValidator, -) -> Result { + mut validator: FuncValidator, +) -> Result<(Code, FuncValidatorAllocations)> { let locals_reader = func.get_locals_reader()?; let count = locals_reader.get_count(); let pos = locals_reader.original_position(); - 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)); + // maps a local's address to the index in the type's locals array + let mut local_addr_map = Vec::with_capacity(count as usize); + let mut local_counts = LocalCounts::default(); + + for (i, local) in locals_reader.into_iter().enumerate() { + let local = local?; + validator.define_locals(pos + i, local.0, local.1)?; + } + + for i in 0..validator.len_locals() { + match validator.get_local_type(i) { + Some(wasmparser::ValType::I32) | Some(wasmparser::ValType::F32) => { + local_addr_map.push(local_counts.local_32); + local_counts.local_32 += 1; + } + Some(wasmparser::ValType::I64) | Some(wasmparser::ValType::F64) => { + local_addr_map.push(local_counts.local_64); + local_counts.local_64 += 1; } + Some(wasmparser::ValType::V128) => { + local_addr_map.push(local_counts.local_128); + local_counts.local_128 += 1; + } + Some(wasmparser::ValType::Ref(_)) => { + local_addr_map.push(local_counts.local_ref); + local_counts.local_ref += 1; + } + None => return Err(crate::ParseError::UnsupportedOperator("Unknown local type".to_string())), } - locals.into_boxed_slice() - }; + } - let body = process_operators_and_validate(validator, func)?; - Ok((body, locals)) + let (body, allocations) = process_operators_and_validate(validator, func, local_addr_map)?; + Ok(((body, local_counts), allocations)) } pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result { diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 51743c7..0e8f976 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -62,6 +62,7 @@ impl Parser { component_model: false, component_model_nested_names: false, component_model_values: false, + component_model_more_flags: false, exceptions: false, extended_const: false, gc: false, diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 294782c..3ee619f 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -3,12 +3,12 @@ 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, TinyWasmModule, ValType, + Data, Element, Export, FuncType, Global, Import, Instruction, LocalCounts, MemoryType, TableType, TinyWasmModule, WasmFunction, }; use wasmparser::{FuncValidatorAllocations, Payload, Validator}; -pub(crate) type Code = (Box<[Instruction]>, Box<[ValType]>); +pub(crate) type Code = (Box<[Instruction]>, LocalCounts); #[derive(Default)] pub(crate) struct ModuleReader { @@ -135,9 +135,10 @@ impl ModuleReader { CodeSectionEntry(function) => { debug!("Found code section entry"); let v = validator.code_section_entry(&function)?; - let mut func_validator = v.into_validator(self.func_validator_allocations.take().unwrap_or_default()); - self.code.push(conversion::convert_module_code(function, &mut func_validator)?); - self.func_validator_allocations = Some(func_validator.into_allocations()); + let func_validator = v.into_validator(self.func_validator_allocations.take().unwrap_or_default()); + let (code, allocations) = conversion::convert_module_code(function, func_validator)?; + self.code.push(code); + self.func_validator_allocations = Some(allocations); } ImportSection(reader) => { if !self.imports.is_empty() { diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index f22e05b..4e2fece 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -4,45 +4,43 @@ use crate::conversion::{convert_heaptype, convert_valtype}; use alloc::string::ToString; use alloc::{boxed::Box, vec::Vec}; use tinywasm_types::{Instruction, MemoryArg}; -use wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources}; +use wasmparser::{FuncValidator, FuncValidatorAllocations, FunctionBody, VisitOperator, WasmModuleResources}; -struct ValidateThenVisit<'a, T, U>(T, &'a mut U); +struct ValidateThenVisit<'a, R: WasmModuleResources>(usize, &'a mut FunctionBuilder); 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()),*)?)?; - self.1.$visit($($($arg),*)?); + self.1.$visit($($($arg.clone()),*)?); + self.1.validator_visitor(self.0).$visit($($($arg),*)?)?; Ok(()) } )*}; } -impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U> -where - T: VisitOperator<'a, Output = wasmparser::Result<()>>, - U: VisitOperator<'a, Output = ()>, -{ +impl<'a, R: WasmModuleResources> VisitOperator<'a> for ValidateThenVisit<'_, R> { type Output = Result<()>; wasmparser::for_each_operator!(validate_then_visit); } pub(crate) fn process_operators_and_validate( - validator: &mut FuncValidator, + validator: FuncValidator, body: FunctionBody<'_>, -) -> Result> { + local_addr_map: Vec, +) -> Result<(Box<[Instruction]>, FuncValidatorAllocations)> { let mut reader = body.get_operators_reader()?; let remaining = reader.get_binary_reader().bytes_remaining(); - let mut builder = FunctionBuilder::new(remaining); + let mut builder = FunctionBuilder::new(remaining, validator, local_addr_map); + while !reader.eof() { - let validate = validator.visitor(reader.original_position()); - reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))??; + reader.visit_operator(&mut ValidateThenVisit(reader.original_position(), &mut builder))??; } - validator.finish(reader.original_position())?; + + builder.validator_finish(reader.original_position())?; if !builder.errors.is_empty() { return Err(builder.errors.remove(0)); } - Ok(builder.instructions.into_boxed_slice()) + Ok((builder.instructions.into_boxed_slice(), builder.validator.into_allocations())) } macro_rules! define_operands { @@ -77,15 +75,32 @@ macro_rules! define_mem_operands { )*}; } -pub(crate) struct FunctionBuilder { +pub(crate) struct FunctionBuilder { + validator: FuncValidator, instructions: Vec, label_ptrs: Vec, + local_addr_map: Vec, errors: Vec, } -impl FunctionBuilder { - pub(crate) fn new(instr_capacity: usize) -> Self { +impl FunctionBuilder { + pub(crate) fn validator_visitor( + &mut self, + offset: usize, + ) -> impl VisitOperator<'_, Output = Result<(), wasmparser::BinaryReaderError>> { + self.validator.visitor(offset) + } + + pub(crate) fn validator_finish(&mut self, offset: usize) -> Result<(), wasmparser::BinaryReaderError> { + self.validator.finish(offset) + } +} + +impl FunctionBuilder { + pub(crate) fn new(instr_capacity: usize, validator: FuncValidator, local_addr_map: Vec) -> Self { Self { + validator, + local_addr_map, instructions: Vec::with_capacity(instr_capacity), label_ptrs: Vec::with_capacity(256), errors: Vec::new(), @@ -115,7 +130,7 @@ macro_rules! impl_visit_operator { }; } -impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { +impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuilder { type Output = (); wasmparser::for_each_operator!(impl_visit_operator); @@ -123,7 +138,6 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_br, Instruction::Br, u32, visit_br_if, Instruction::BrIf, u32, 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_call, Instruction::Call, u32, @@ -161,8 +175,6 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_unreachable, Instruction::Unreachable, visit_nop, Instruction::Nop, visit_return, Instruction::Return, - visit_drop, Instruction::Drop, - visit_select, Instruction::Select(None), visit_i32_eqz, Instruction::I32Eqz, visit_i32_eq, Instruction::I32Eq, visit_i32_ne, Instruction::I32Ne, @@ -305,94 +317,101 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U } - fn visit_i32_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - 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.instructions.push(i32store); + fn visit_global_set(&mut self, global_index: u32) -> Self::Output { + match self.validator.get_operand_type(0) { + Some(Some(t)) => self.instructions.push(match convert_valtype(&t) { + tinywasm_types::ValType::I32 => Instruction::GlobalSet32(global_index), + tinywasm_types::ValType::F32 => Instruction::GlobalSet32(global_index), + tinywasm_types::ValType::I64 => Instruction::GlobalSet64(global_index), + tinywasm_types::ValType::F64 => Instruction::GlobalSet64(global_index), + tinywasm_types::ValType::V128 => Instruction::GlobalSet128(global_index), + tinywasm_types::ValType::RefExtern => Instruction::GlobalSetRef(global_index), + tinywasm_types::ValType::RefFunc => Instruction::GlobalSetRef(global_index), + }), + _ => self.visit_unreachable(), } + } - match self.instructions[self.instructions.len() - 2..] { - [_, Instruction::LocalGet2(a, b)] => { - self.instructions.pop(); - self.instructions.push(Instruction::I32StoreLocal { - local_a: a, - local_b: b, - offset: arg.offset as u32, - mem_addr: arg.mem_addr as u8, - }) - } - [Instruction::LocalGet(a), Instruction::I32Const(b)] => { - self.instructions.pop(); - self.instructions.pop(); - self.instructions.push(Instruction::I32ConstStoreLocal { - local: a, - const_i32: b, - offset: arg.offset as u32, - mem_addr: arg.mem_addr as u8, - }) - } - _ => self.instructions.push(i32store), + fn visit_drop(&mut self) -> Self::Output { + match self.validator.get_operand_type(0) { + Some(Some(t)) => self.instructions.push(match convert_valtype(&t) { + tinywasm_types::ValType::I32 => Instruction::Drop32, + tinywasm_types::ValType::F32 => Instruction::Drop32, + tinywasm_types::ValType::I64 => Instruction::Drop64, + tinywasm_types::ValType::F64 => Instruction::Drop64, + tinywasm_types::ValType::V128 => Instruction::Drop128, + tinywasm_types::ValType::RefExtern => Instruction::DropRef, + tinywasm_types::ValType::RefFunc => Instruction::DropRef, + }), + _ => self.visit_unreachable(), + } + } + fn visit_select(&mut self) -> Self::Output { + match self.validator.get_operand_type(1) { + Some(Some(t)) => self.visit_typed_select(t), + _ => self.visit_unreachable(), } } + fn visit_i32_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output { + let arg = MemoryArg { offset: memarg.offset, mem_addr: memarg.memory }; + let i32store = Instruction::I32Store { offset: arg.offset, mem_addr: arg.mem_addr }; + self.instructions.push(i32store) + } fn visit_local_get(&mut self, idx: u32) -> Self::Output { - let Some(instruction) = self.instructions.last_mut() else { - return self.instructions.push(Instruction::LocalGet(idx)); - }; - - 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), - _ => self.instructions.push(Instruction::LocalGet(idx)), - }; + let resolved_idx = self.local_addr_map[idx as usize]; + match self.validator.get_local_type(idx) { + Some(t) => self.instructions.push(match convert_valtype(&t) { + tinywasm_types::ValType::I32 => Instruction::LocalGet32(resolved_idx), + tinywasm_types::ValType::F32 => Instruction::LocalGet32(resolved_idx), + tinywasm_types::ValType::I64 => Instruction::LocalGet64(resolved_idx), + tinywasm_types::ValType::F64 => Instruction::LocalGet64(resolved_idx), + tinywasm_types::ValType::V128 => Instruction::LocalGet128(resolved_idx), + tinywasm_types::ValType::RefExtern => Instruction::LocalGetRef(resolved_idx), + tinywasm_types::ValType::RefFunc => Instruction::LocalGetRef(resolved_idx), + }), + _ => self.visit_unreachable(), + } } fn visit_local_set(&mut self, idx: u32) -> Self::Output { - let Some(instruction) = self.instructions.last_mut() else { - return self.instructions.push(Instruction::LocalSet(idx)); - }; - match instruction { - Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), - _ => self.instructions.push(Instruction::LocalSet(idx)), - }; + let resolved_idx = self.local_addr_map[idx as usize]; + match self.validator.get_operand_type(0) { + Some(Some(t)) => self.instructions.push(match convert_valtype(&t) { + tinywasm_types::ValType::I32 => Instruction::LocalSet32(resolved_idx), + tinywasm_types::ValType::F32 => Instruction::LocalSet32(resolved_idx), + tinywasm_types::ValType::I64 => Instruction::LocalSet64(resolved_idx), + tinywasm_types::ValType::F64 => Instruction::LocalSet64(resolved_idx), + tinywasm_types::ValType::V128 => Instruction::LocalSet128(resolved_idx), + tinywasm_types::ValType::RefExtern => Instruction::LocalSetRef(resolved_idx), + tinywasm_types::ValType::RefFunc => Instruction::LocalSetRef(resolved_idx), + }), + _ => self.visit_unreachable(), + } } fn visit_local_tee(&mut self, idx: u32) -> Self::Output { - self.instructions.push(Instruction::LocalTee(idx)) + let resolved_idx = self.local_addr_map[idx as usize]; + match self.validator.get_operand_type(0) { + Some(Some(t)) => self.instructions.push(match convert_valtype(&t) { + tinywasm_types::ValType::I32 => Instruction::LocalTee32(resolved_idx), + tinywasm_types::ValType::F32 => Instruction::LocalTee32(resolved_idx), + tinywasm_types::ValType::I64 => Instruction::LocalTee64(resolved_idx), + tinywasm_types::ValType::F64 => Instruction::LocalTee64(resolved_idx), + tinywasm_types::ValType::V128 => Instruction::LocalTee128(resolved_idx), + tinywasm_types::ValType::RefExtern => Instruction::LocalTeeRef(resolved_idx), + tinywasm_types::ValType::RefFunc => Instruction::LocalTeeRef(resolved_idx), + }), + _ => self.visit_unreachable(), + } } fn visit_i64_rotl(&mut self) -> Self::Output { - let Some([Instruction::I64Xor, Instruction::I64Const(a)]) = self.instructions.last_chunk::<2>() else { - return self.instructions.push(Instruction::I64Rotl); - }; - let a = *a; - self.instructions.pop(); - self.instructions.pop(); - self.instructions.push(Instruction::I64XorConstRotl(a)) + self.instructions.push(Instruction::I64Rotl) } fn visit_i32_add(&mut self) -> Self::Output { - let Some(last) = self.instructions.last_chunk::<2>() else { - return self.instructions.push(Instruction::I32Add); - }; - - match *last { - [Instruction::LocalGet(a), Instruction::I32Const(b)] => { - self.instructions.pop(); - self.instructions.pop(); - self.instructions.push(Instruction::I32LocalGetConstAdd(a, b)) - } - [Instruction::LocalGet2(a, b), Instruction::I32Const(c)] => { - self.instructions.pop(); - self.instructions.pop(); - self.instructions.push(Instruction::LocalGet(a)); - self.instructions.push(Instruction::I32LocalGetConstAdd(b, c)) - } - _ => self.instructions.push(Instruction::I32Add), - } + self.instructions.push(Instruction::I32Add) } fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output { @@ -474,7 +493,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { .targets() .map(|t| t.map(Instruction::BrLabel)) .collect::, wasmparser::BinaryReaderError>>() - .expect("BrTable targets are invalid, this should have been caught by the validator"); + .expect("visit_br_table: 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)); } @@ -518,7 +537,15 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output { - self.instructions.push(Instruction::Select(Some(convert_valtype(&ty)))) + self.instructions.push(match convert_valtype(&ty) { + tinywasm_types::ValType::I32 => Instruction::Select32, + tinywasm_types::ValType::F32 => Instruction::Select32, + tinywasm_types::ValType::I64 => Instruction::Select64, + tinywasm_types::ValType::F64 => Instruction::Select64, + tinywasm_types::ValType::V128 => Instruction::Select128, + tinywasm_types::ValType::RefExtern => Instruction::SelectRef, + tinywasm_types::ValType::RefFunc => Instruction::SelectRef, + }) } define_primitive_operands! { diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index 491522c..d82d850 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -19,9 +19,19 @@ tinywasm-parser={version="0.7.0", path="../parser", default-features=false, opti tinywasm-types={version="0.7.0", path="../types", default-features=false} libm={version="0.2", default-features=false} +# maybe? +# arrayvec={version="0.7"} instead of the custom implementation +# bumpalo={version="3.16"} +# wide= for simd +# vec1= might be useful? fast .last() and .first() access +# https://github.com/lumol-org/soa-derive could be useful for the memory layout of Stacks + +#https://alic.dev/blog/dense-enums +# https://docs.rs/tagged-pointer/latest/tagged_pointer/ + [dev-dependencies] wasm-testsuite={path="../wasm-testsuite"} -wast={version="211.0"} +wast={version="212.0"} owo-colors={version="4.0"} eyre={version="0.6"} serde_json={version="1.0"} diff --git a/crates/tinywasm/src/boxvec.rs b/crates/tinywasm/src/boxvec.rs deleted file mode 100644 index 9c6732d..0000000 --- a/crates/tinywasm/src/boxvec.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::unlikely; -use alloc::{borrow::Cow, boxed::Box, vec::Vec}; -use core::ops::RangeBounds; - -// A Vec-like type that doesn't deallocate memory when popping elements. -#[derive(Debug)] -pub(crate) struct BoxVec { - pub(crate) data: Box<[T]>, - pub(crate) end: usize, -} - -impl BoxVec { - #[inline(always)] - pub(crate) fn with_capacity(capacity: usize) -> Self { - 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)] - pub(crate) fn push(&mut self, value: T) { - assert!(self.end <= self.data.len(), "stack overflow"); - self.data[self.end] = value; - self.end += 1; - } - - #[inline(always)] - pub(crate) fn pop(&mut self) -> Option { - assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)"); - if unlikely(self.end == 0) { - None - } else { - self.end -= 1; - Some(self.data[self.end]) - } - } - - #[inline(always)] - pub(crate) fn len(&self) -> usize { - self.end - } - - #[inline(always)] - pub(crate) fn extend_from_slice(&mut self, values: &[T]) { - let new_end = self.end + values.len(); - assert!(new_end <= self.data.len(), "stack overflow"); - self.data[self.end..new_end].copy_from_slice(values); - self.end = new_end; - } - - #[inline(always)] - pub(crate) fn last_mut(&mut self) -> Option<&mut T> { - assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)"); - if unlikely(self.end == 0) { - None - } else { - Some(&mut self.data[self.end - 1]) - } - } - - #[inline(always)] - pub(crate) fn last(&self) -> Option<&T> { - assert!(self.end <= self.data.len(), "invalid stack state (should be impossible)"); - if unlikely(self.end == 0) { - None - } else { - Some(&self.data[self.end - 1]) - } - } - - #[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 extend(&mut self, iter: impl Iterator) { - let (lower, _) = iter.size_hint(); - let upper = lower; - let new_end = self.end + upper; - assert!(new_end <= self.data.len(), "stack overflow"); - for (i, value) in iter.enumerate() { - self.data[self.end + i] = value; - } - self.end = new_end; - } - - #[inline(always)] - pub(crate) fn drain(&mut self, range: impl RangeBounds) -> Cow<'_, [T]> { - let start = match range.start_bound() { - core::ops::Bound::Included(&start) => start, - core::ops::Bound::Excluded(&start) => start + 1, - core::ops::Bound::Unbounded => 0, - }; - let end = match range.end_bound() { - core::ops::Bound::Included(&end) => end + 1, - core::ops::Bound::Excluded(&end) => end, - core::ops::Bound::Unbounded => self.end, - }; - - assert!(start <= end); - assert!(end <= self.end); - - if end == self.end { - self.end = start; - return Cow::Borrowed(&self.data[start..end]); - } - - let drain = self.data[start..end].to_vec(); - self.data.copy_within(end..self.end, start); - self.end -= end - start; - Cow::Owned(drain) - } -} - -impl core::ops::Index for BoxVec { - type Output = T; - - #[inline(always)] - fn index(&self, index: usize) -> &T { - &self.data[index] - } -} - -impl core::ops::Index> for BoxVec { - type Output = [T]; - - #[inline(always)] - fn index(&self, index: core::ops::Range) -> &[T] { - &self.data[index] - } -} - -impl core::ops::IndexMut for BoxVec { - #[inline(always)] - fn index_mut(&mut self, index: usize) -> &mut T { - &mut self.data[index] - } -} diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index a6e16ad..650f9df 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,5 +1,5 @@ use crate::runtime::{CallFrame, Stack}; -use crate::{log, runtime::RawWasmValue, unlikely, Function}; +use crate::{log, unlikely, Function}; use crate::{Error, FuncContext, Result, Store}; use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; use tinywasm_types::{FuncType, ModuleInstanceAddr, ValType, WasmValue}; @@ -59,8 +59,7 @@ impl FuncHandle { }; // 6. Let f be the dummy frame - let call_frame_params = params.iter().map(|v| RawWasmValue::from(*v)).collect::>(); - let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, &call_frame_params, 0); + let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, params, 0); // 7. Push the frame f to the call stack // & 8. Push the values to the stack (Not needed since the call frame owns the values) @@ -71,16 +70,16 @@ impl FuncHandle { runtime.exec(store, &mut stack)?; // Once the function returns: - let result_m = func_ty.results.len(); + // let result_m = func_ty.results.len(); // 1. Assert: m values are on the top of the stack (Ensured by validation) - assert!(stack.values.len() >= result_m); + // assert!(stack.values.len() >= result_m); // 2. Pop m values from the stack - let res = stack.values.last_n(result_m)?; + let res = stack.values.pop_results(&func_ty.results)?; // The values are returned as the results of the invocation. - Ok(res.iter().zip(func_ty.results.iter()).map(|(v, ty)| v.attach_type(*ty)).collect()) + Ok(res) } } diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 0ab61f8..9c48ee0 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -100,7 +100,6 @@ pub use module::Module; pub use reference::*; pub use store::*; -mod boxvec; mod func; mod imports; mod instance; diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs deleted file mode 100644 index b2042c2..0000000 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Break to a block at the given index (relative to the current frame) -// If there is no block at the given index, return or call the parent function -// -// This is a bit hard to see from the spec, but it's vaild to use breaks to return -// from a function, so we need to check if the label stack is empty -macro_rules! break_to { - ($break_to_relative:expr, $self:expr) => { - if $self.cf.break_to($break_to_relative, &mut $self.stack.values, &mut $self.stack.blocks).is_none() { - return $self.exec_return(); - } - }; -} - -/// 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. -/// Rust sadly doesn't have wrapping casts for floats yet, maybe never. -/// Alternatively, https://crates.io/crates/az could be used for this but -/// it's not worth the dependency. -#[rustfmt::skip] -macro_rules! float_min_max { - (f32, i32) => {(-2147483904.0_f32, 2147483648.0_f32)}; - (f64, i32) => {(-2147483649.0_f64, 2147483648.0_f64)}; - (f32, u32) => {(-1.0_f32, 4294967296.0_f32)}; // 2^32 - (f64, u32) => {(-1.0_f64, 4294967296.0_f64)}; // 2^32 - (f32, i64) => {(-9223373136366403584.0_f32, 9223372036854775808.0_f32)}; // 2^63 + 2^40 | 2^63 - (f64, i64) => {(-9223372036854777856.0_f64, 9223372036854775808.0_f64)}; // 2^63 + 2^40 | 2^63 - (f32, u64) => {(-1.0_f32, 18446744073709551616.0_f32)}; // 2^64 - (f64, u64) => {(-1.0_f64, 18446744073709551616.0_f64)}; // 2^64 - // other conversions are not allowed - ($from:ty, $to:ty) => {compile_error!("invalid float conversion")}; -} - -/// Convert a value on the stack -macro_rules! conv { - ($from:ty, $to:ty, $self:expr) => { - $self.stack.values.replace_top(|v| (<$from>::from(v) as $to).into())? - }; -} - -/// Convert a value on the stack with error checking -macro_rules! checked_conv_float { - // Direct conversion with error checking (two types) - ($from:tt, $to:tt, $self:expr) => { - checked_conv_float!($from, $to, $to, $self) - }; - // Conversion with an intermediate unsigned type and error checking (three types) - ($from:tt, $intermediate:tt, $to:tt, $self:expr) => { - $self.stack.values.replace_top_trap(|v| { - let (min, max) = float_min_max!($from, $intermediate); - let a: $from = v.into(); - if unlikely(a.is_nan()) { - return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); - } - if unlikely(a <= min || a >= max) { - return Err(Error::Trap(crate::Trap::IntegerOverflow)); - } - Ok((a as $intermediate as $to).into()) - })? - }; -} - -/// Compare two values on the stack -macro_rules! comp { - ($op:tt, $to:ty, $self:ident) => { - $self.stack.values.calculate(|a, b| { - ((<$to>::from(a) $op <$to>::from(b)) as i32).into() - })? - }; -} - -/// Compare a value on the stack to zero -macro_rules! comp_zero { - ($op:tt, $ty:ty, $self:expr) => { - $self.stack.values.replace_top(|v| ((<$ty>::from(v) $op 0) as i32).into())? - }; -} - -/// Apply an arithmetic method to two values on the stack -macro_rules! arithmetic { - ($op:ident, $to:ty, $self:expr) => { - $self.stack.values.calculate(|a, b| { - (<$to>::from(a).$op(<$to>::from(b)) as $to).into() - })? - }; - - // also allow operators such as +, - - ($op:tt, $ty:ty, $self:expr) => { - $self.stack.values.calculate(|a, b| { - ((<$ty>::from(a) $op <$ty>::from(b)) as $ty).into() - })? - }; -} - -/// Apply an arithmetic method to a single value on the stack -macro_rules! arithmetic_single { - ($op:ident, $ty:ty, $self:expr) => { - arithmetic_single!($op, $ty, $ty, $self) - }; - - ($op:ident, $from:ty, $to:ty, $self:expr) => { - $self.stack.values.replace_top(|v| (<$from>::from(v).$op() as $to).into())? - }; -} - -/// Apply an arithmetic operation to two values on the stack with error checking -macro_rules! checked_int_arithmetic { - ($op:ident, $to:ty, $self:expr) => { - $self.stack.values.calculate_trap(|a, b| { - let a: $to = a.into(); - let b: $to = b.into(); - - if unlikely(b == 0) { - return Err(Error::Trap(crate::Trap::DivisionByZero)); - } - - let result = a.$op(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow))?; - Ok((result).into()) - })? - }; -} - -pub(super) use arithmetic; -pub(super) use arithmetic_single; -pub(super) use break_to; -pub(super) use checked_conv_float; -pub(super) use checked_int_arithmetic; -pub(super) use comp; -pub(super) use comp_zero; -pub(super) use conv; -pub(super) use float_min_max; diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index be3f60a..8028e87 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -1,15 +1,4 @@ -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::stack::{BlockFrame, BlockType}; -use super::{InterpreterRuntime, RawWasmValue, Stack}; -use crate::runtime::CallFrame; -use crate::{cold, unlikely, Error, FuncContext, MemLoadable, MemStorable, ModuleInstance, Result, Store, Trap}; - -mod macros; -mod traits; -use {macros::*, traits::*}; +mod num_helpers; #[cfg(not(feature = "std"))] mod no_std_floats; @@ -18,6 +7,16 @@ mod no_std_floats; #[allow(unused_imports)] use no_std_floats::NoStdFloatExt; +use alloc::{format, rc::Rc, string::ToString}; +use core::ops::ControlFlow; +use num_helpers::*; +use tinywasm_types::*; + +use super::stack::{values::StackHeight, BlockFrame, BlockType}; +use super::{values::*, InterpreterRuntime, Stack}; +use crate::runtime::CallFrame; +use crate::*; + impl InterpreterRuntime { pub(crate) fn exec(&self, store: &mut Store, stack: &mut Stack) -> Result<()> { Executor::new(store, stack)?.run_to_completion() @@ -56,10 +55,18 @@ impl<'store, 'stack> Executor<'store, 'stack> { Nop => self.exec_noop(), Unreachable => self.exec_unreachable()?, - Drop => self.exec_drop()?, - Select(_valtype) => self.exec_select()?, - Call(v) => self.exec_call_direct(*v)?, - CallIndirect(ty, table) => self.exec_call_indirect(*ty, *table)?, + Drop32 => self.stack.values.drop::()?, + Drop64 => self.stack.values.drop::()?, + Drop128 => self.stack.values.drop::()?, + DropRef => self.stack.values.drop::()?, + + Select32 => self.stack.values.select::()?, + Select64 => self.stack.values.select::()?, + Select128 => self.stack.values.select::()?, + SelectRef => self.stack.values.select::()?, + + Call(v) => return self.exec_call_direct(*v), + CallIndirect(ty, table) => return self.exec_call_indirect(*ty, *table), If(args, el, end) => self.exec_if((*args).into(), *el, *end)?, Else(end_offset) => self.exec_else(*end_offset)?, @@ -72,18 +79,41 @@ impl<'store, 'stack> Executor<'store, 'stack> { Return => return self.exec_return(), EndBlockFrame => self.exec_end_block()?, - LocalGet(local_index) => self.exec_local_get(*local_index), - LocalSet(local_index) => self.exec_local_set(*local_index)?, - LocalTee(local_index) => self.exec_local_tee(*local_index)?, + LocalGet32(local_index) => { + self.cf.locals.get::(*local_index).map(|v| self.stack.values.push(v))? + } + LocalGet64(local_index) => { + self.cf.locals.get::(*local_index).map(|v| self.stack.values.push(v))? + } + LocalGet128(local_index) => { + self.cf.locals.get::(*local_index).map(|v| self.stack.values.push(v))? + } + LocalGetRef(local_index) => { + self.cf.locals.get::(*local_index).map(|v| self.stack.values.push(v))? + } + + LocalSet32(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::()?)?, + LocalSet64(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::()?)?, + LocalSet128(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::()?)?, + LocalSetRef(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::()?)?, + + LocalTee32(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::()?)?, + LocalTee64(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::()?)?, + LocalTee128(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::()?)?, + LocalTeeRef(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::()?)?, + GlobalGet(global_index) => self.exec_global_get(*global_index)?, - GlobalSet(global_index) => self.exec_global_set(*global_index)?, - - I32Const(val) => self.exec_const(*val), - I64Const(val) => self.exec_const(*val), - F32Const(val) => self.exec_const(*val), - F64Const(val) => self.exec_const(*val), - RefFunc(func_idx) => self.exec_const(*func_idx), - RefNull(_) => self.exec_const(-1i64), + GlobalSet32(global_index) => self.exec_global_set::(*global_index)?, + GlobalSet64(global_index) => self.exec_global_set::(*global_index)?, + GlobalSet128(global_index) => self.exec_global_set::(*global_index)?, + GlobalSetRef(global_index) => self.exec_global_set::(*global_index)?, + + I32Const(val) => self.stack.values.push(*val), + I64Const(val) => self.stack.values.push(*val), + F32Const(val) => self.stack.values.push::(val.to_bits() as i32), + F64Const(val) => self.stack.values.push(val.to_bits() as i64), + RefFunc(func_idx) => self.stack.values.push(Some(*func_idx)), // do we need to resolve the function index? + RefNull(_) => self.stack.values.push(None), RefIsNull => self.exec_ref_is_null()?, MemorySize(addr) => self.exec_memory_size(*addr)?, @@ -97,15 +127,42 @@ 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 } => 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)?, + I32Store { mem_addr, offset } => { + let v = self.stack.values.pop::()?; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + I64Store { mem_addr, offset } => { + let v = self.stack.values.pop::()?; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + F32Store { mem_addr, offset } => { + let v = self.stack.values.pop::()?; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + F64Store { mem_addr, offset } => { + let v = self.stack.values.pop::()?; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + I32Store8 { mem_addr, offset } => { + let v = self.stack.values.pop::()? as i8; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + I32Store16 { mem_addr, offset } => { + let v = self.stack.values.pop::()? as i16; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + I64Store8 { mem_addr, offset } => { + let v = self.stack.values.pop::()? as i8; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + I64Store16 { mem_addr, offset } => { + let v = self.stack.values.pop::()? as i16; + self.exec_mem_store::(v, *mem_addr, *offset)? + } + I64Store32 { mem_addr, offset } => { + let v = self.stack.values.pop::()? as i32; + self.exec_mem_store::(v, *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)?, @@ -122,145 +179,184 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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), - - I32Eq => comp!(==, i32, self), - I64Eq => comp!(==, i64, self), - F32Eq => comp!(==, f32, self), - F64Eq => comp!(==, f64, self), - - I32Ne => comp!(!=, i32, self), - I64Ne => comp!(!=, i64, self), - F32Ne => comp!(!=, f32, self), - F64Ne => comp!(!=, f64, self), - - I32LtS => comp!(<, i32, self), - I64LtS => comp!(<, i64, self), - I32LtU => comp!(<, u32, self), - I64LtU => comp!(<, u64, self), - F32Lt => comp!(<, f32, self), - F64Lt => comp!(<, f64, self), - - I32LeS => comp!(<=, i32, self), - I64LeS => comp!(<=, i64, self), - I32LeU => comp!(<=, u32, self), - I64LeU => comp!(<=, u64, self), - F32Le => comp!(<=, f32, self), - F64Le => comp!(<=, f64, self), - - I32GeS => comp!(>=, i32, self), - I64GeS => comp!(>=, i64, self), - I32GeU => comp!(>=, u32, self), - I64GeU => comp!(>=, u64, self), - F32Ge => comp!(>=, f32, self), - F64Ge => comp!(>=, f64, self), - - I32GtS => comp!(>, i32, self), - I64GtS => comp!(>, i64, self), - I32GtU => comp!(>, u32, self), - I64GtU => comp!(>, u64, self), - F32Gt => comp!(>, f32, self), - F64Gt => comp!(>, f64, self), - - I64Add => arithmetic!(wrapping_add, i64, self), - I32Add => arithmetic!(wrapping_add, i32, self), - F32Add => arithmetic!(+, f32, self), - F64Add => arithmetic!(+, f64, self), - - I32Sub => arithmetic!(wrapping_sub, i32, self), - I64Sub => arithmetic!(wrapping_sub, i64, self), - F32Sub => arithmetic!(-, f32, self), - F64Sub => arithmetic!(-, f64, self), - - F32Div => arithmetic!(/, f32, self), - F64Div => arithmetic!(/, f64, self), - - I32Mul => arithmetic!(wrapping_mul, i32, self), - I64Mul => arithmetic!(wrapping_mul, i64, self), - F32Mul => arithmetic!(*, f32, self), - F64Mul => arithmetic!(*, f64, self), + I64Eqz => self.stack.values.replace_top::(|v| Ok((v == 0) as i32))?, + I32Eqz => self.stack.values.replace_top::(|v| Ok((v == 0) as i32))?, + I32Eq => self.stack.values.calculate::(|a, b| Ok((a == b) as i32))?, + I64Eq => self.stack.values.calculate::(|a, b| Ok((a == b) as i32))?, + F32Eq => self.stack.values.calculate::(|a, b| Ok((a == b) as i32))?, + F64Eq => self.stack.values.calculate::(|a, b| Ok((a == b) as i32))?, + + I32Ne => self.stack.values.calculate::(|a, b| Ok((a != b) as i32))?, + I64Ne => self.stack.values.calculate::(|a, b| Ok((a != b) as i32))?, + F32Ne => self.stack.values.calculate::(|a, b| Ok((a != b) as i32))?, + F64Ne => self.stack.values.calculate::(|a, b| Ok((a != b) as i32))?, + + I32LtS => self.stack.values.calculate::(|a, b| Ok((a < b) as i32))?, + I64LtS => self.stack.values.calculate::(|a, b| Ok((a < b) as i32))?, + I32LtU => self.stack.values.calculate::(|a, b| Ok((a < b) as i32))?, + I64LtU => self.stack.values.calculate::(|a, b| Ok((a < b) as i32))?, + F32Lt => self.stack.values.calculate::(|a, b| Ok((a < b) as i32))?, + F64Lt => self.stack.values.calculate::(|a, b| Ok((a < b) as i32))?, + + I32LeS => self.stack.values.calculate::(|a, b| Ok((a <= b) as i32))?, + I64LeS => self.stack.values.calculate::(|a, b| Ok((a <= b) as i32))?, + I32LeU => self.stack.values.calculate::(|a, b| Ok((a <= b) as i32))?, + I64LeU => self.stack.values.calculate::(|a, b| Ok((a <= b) as i32))?, + F32Le => self.stack.values.calculate::(|a, b| Ok((a <= b) as i32))?, + F64Le => self.stack.values.calculate::(|a, b| Ok((a <= b) as i32))?, + + I32GeS => self.stack.values.calculate::(|a, b| Ok((a >= b) as i32))?, + I64GeS => self.stack.values.calculate::(|a, b| Ok((a >= b) as i32))?, + I32GeU => self.stack.values.calculate::(|a, b| Ok((a >= b) as i32))?, + I64GeU => self.stack.values.calculate::(|a, b| Ok((a >= b) as i32))?, + F32Ge => self.stack.values.calculate::(|a, b| Ok((a >= b) as i32))?, + F64Ge => self.stack.values.calculate::(|a, b| Ok((a >= b) as i32))?, + + I32GtS => self.stack.values.calculate::(|a, b| Ok((a > b) as i32))?, + I64GtS => self.stack.values.calculate::(|a, b| Ok((a > b) as i32))?, + I32GtU => self.stack.values.calculate::(|a, b| Ok((a > b) as i32))?, + I64GtU => self.stack.values.calculate::(|a, b| Ok((a > b) as i32))?, + F32Gt => self.stack.values.calculate::(|a, b| Ok((a > b) as i32))?, + F64Gt => self.stack.values.calculate::(|a, b| Ok((a > b) as i32))?, + + I32Add => self.stack.values.calculate::(|a, b| Ok(a.wrapping_add(b)))?, + I64Add => self.stack.values.calculate::(|a, b| Ok(a.wrapping_add(b)))?, + F32Add => self.stack.values.calculate::(|a, b| Ok(a + b))?, + F64Add => self.stack.values.calculate::(|a, b| Ok(a + b))?, + + I32Sub => self.stack.values.calculate::(|a, b| Ok(a.wrapping_sub(b)))?, + I64Sub => self.stack.values.calculate::(|a, b| Ok(a.wrapping_sub(b)))?, + F32Sub => self.stack.values.calculate::(|a, b| Ok(a - b))?, + F64Sub => self.stack.values.calculate::(|a, b| Ok(a - b))?, + + F32Div => self.stack.values.calculate::(|a, b| Ok(a / b))?, + F64Div => self.stack.values.calculate::(|a, b| Ok(a / b))?, + + I32Mul => self.stack.values.calculate::(|a, b| Ok(a.wrapping_mul(b)))?, + I64Mul => self.stack.values.calculate::(|a, b| Ok(a.wrapping_mul(b)))?, + F32Mul => self.stack.values.calculate::(|a, b| Ok(a * b))?, + F64Mul => self.stack.values.calculate::(|a, b| Ok(a * b))?, // these can trap - I32DivS => checked_int_arithmetic!(checked_div, i32, self), - I64DivS => checked_int_arithmetic!(checked_div, i64, self), - I32DivU => checked_int_arithmetic!(checked_div, u32, self), - I64DivU => checked_int_arithmetic!(checked_div, u64, self), - - I32RemS => checked_int_arithmetic!(checked_wrapping_rem, i32, self), - I64RemS => checked_int_arithmetic!(checked_wrapping_rem, i64, self), - I32RemU => checked_int_arithmetic!(checked_wrapping_rem, u32, self), - I64RemU => checked_int_arithmetic!(checked_wrapping_rem, u64, self), - - I32And => arithmetic!(bitand, i32, self), - I64And => arithmetic!(bitand, i64, self), - I32Or => arithmetic!(bitor, i32, self), - I64Or => arithmetic!(bitor, i64, self), - I32Xor => arithmetic!(bitxor, i32, self), - I64Xor => arithmetic!(bitxor, i64, self), - I32Shl => arithmetic!(wasm_shl, i32, self), - I64Shl => arithmetic!(wasm_shl, i64, self), - I32ShrS => arithmetic!(wasm_shr, i32, self), - I64ShrS => arithmetic!(wasm_shr, i64, self), - I32ShrU => arithmetic!(wasm_shr, u32, self), - I64ShrU => arithmetic!(wasm_shr, u64, self), - I32Rotl => arithmetic!(wasm_rotl, i32, self), - I64Rotl => arithmetic!(wasm_rotl, i64, self), - I32Rotr => arithmetic!(wasm_rotr, i32, self), - I64Rotr => arithmetic!(wasm_rotr, i64, self), - - I32Clz => arithmetic_single!(leading_zeros, i32, self), - I64Clz => arithmetic_single!(leading_zeros, i64, self), - I32Ctz => arithmetic_single!(trailing_zeros, i32, self), - I64Ctz => arithmetic_single!(trailing_zeros, i64, self), - I32Popcnt => arithmetic_single!(count_ones, i32, self), - I64Popcnt => arithmetic_single!(count_ones, i64, self), - - F32ConvertI32S => conv!(i32, f32, self), - F32ConvertI64S => conv!(i64, f32, self), - F64ConvertI32S => conv!(i32, f64, self), - F64ConvertI64S => conv!(i64, f64, self), - F32ConvertI32U => conv!(u32, f32, self), - F32ConvertI64U => conv!(u64, f32, self), - F64ConvertI32U => conv!(u32, f64, self), - F64ConvertI64U => conv!(u64, f64, self), - I32Extend8S => conv!(i8, i32, self), - I32Extend16S => conv!(i16, i32, self), - I64Extend8S => conv!(i8, i64, self), - I64Extend16S => conv!(i16, i64, self), - I64Extend32S => conv!(i32, i64, self), - I64ExtendI32U => conv!(u32, i64, self), - I64ExtendI32S => conv!(i32, i64, self), - I32WrapI64 => conv!(i64, i32, self), - - F32DemoteF64 => conv!(f64, f32, self), - F64PromoteF32 => conv!(f32, f64, self), - - F32Abs => arithmetic_single!(abs, f32, self), - F64Abs => arithmetic_single!(abs, f64, self), - F32Neg => arithmetic_single!(neg, f32, self), - F64Neg => arithmetic_single!(neg, f64, self), - F32Ceil => arithmetic_single!(ceil, f32, self), - F64Ceil => arithmetic_single!(ceil, f64, self), - F32Floor => arithmetic_single!(floor, f32, self), - F64Floor => arithmetic_single!(floor, f64, self), - F32Trunc => arithmetic_single!(trunc, f32, self), - F64Trunc => arithmetic_single!(trunc, f64, self), - F32Nearest => arithmetic_single!(tw_nearest, f32, self), - F64Nearest => arithmetic_single!(tw_nearest, f64, self), - F32Sqrt => arithmetic_single!(sqrt, f32, self), - F64Sqrt => arithmetic_single!(sqrt, f64, self), - F32Min => arithmetic!(tw_minimum, f32, self), - F64Min => arithmetic!(tw_minimum, f64, self), - F32Max => arithmetic!(tw_maximum, f32, self), - F64Max => arithmetic!(tw_maximum, f64, self), - F32Copysign => arithmetic!(copysign, f32, self), - F64Copysign => arithmetic!(copysign, f64, self), + I32DivS => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + I64DivS => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + I32DivU => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + I64DivU => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + + I32RemS => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + I64RemS => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + I32RemU => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + I64RemU => self.stack.values.calculate::(|a, b| { + if unlikely(b == 0) { + return Err(Error::Trap(Trap::DivisionByZero)); + } + a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) + })?, + + I32And => self.stack.values.calculate::(|a, b| Ok(a & b))?, + I64And => self.stack.values.calculate::(|a, b| Ok(a & b))?, + I32Or => self.stack.values.calculate::(|a, b| Ok(a | b))?, + I64Or => self.stack.values.calculate::(|a, b| Ok(a | b))?, + I32Xor => self.stack.values.calculate::(|a, b| Ok(a ^ b))?, + I64Xor => self.stack.values.calculate::(|a, b| Ok(a ^ b))?, + I32Shl => self.stack.values.calculate::(|a, b| Ok(a.wasm_shl(b)))?, + I64Shl => self.stack.values.calculate::(|a, b| Ok(a.wasm_shl(b)))?, + I32ShrS => self.stack.values.calculate::(|a, b| Ok(a.wasm_shr(b)))?, + I64ShrS => self.stack.values.calculate::(|a, b| Ok(a.wasm_shr(b)))?, + I32ShrU => self.stack.values.calculate::(|a, b| Ok(a.wasm_shr(b)))?, + I64ShrU => self.stack.values.calculate::(|a, b| Ok(a.wasm_shr(b)))?, + I32Rotl => self.stack.values.calculate::(|a, b| Ok(a.wasm_rotl(b)))?, + I64Rotl => self.stack.values.calculate::(|a, b| Ok(a.wasm_rotl(b)))?, + I32Rotr => self.stack.values.calculate::(|a, b| Ok(a.wasm_rotr(b)))?, + I64Rotr => self.stack.values.calculate::(|a, b| Ok(a.wasm_rotr(b)))?, + + I32Clz => self.stack.values.replace_top::(|v| Ok(v.leading_zeros() as i32))?, + I64Clz => self.stack.values.replace_top::(|v| Ok(v.leading_zeros() as i64))?, + I32Ctz => self.stack.values.replace_top::(|v| Ok(v.trailing_zeros() as i32))?, + I64Ctz => self.stack.values.replace_top::(|v| Ok(v.trailing_zeros() as i64))?, + I32Popcnt => self.stack.values.replace_top::(|v| Ok(v.count_ones() as i32))?, + I64Popcnt => self.stack.values.replace_top::(|v| Ok(v.count_ones() as i64))?, + + F32ConvertI32S => self.stack.values.replace_top::(|v| Ok(v as f32))?, + F32ConvertI64S => self.stack.values.replace_top::(|v| Ok(v as f32))?, + F64ConvertI32S => self.stack.values.replace_top::(|v| Ok(v as f64))?, + F64ConvertI64S => self.stack.values.replace_top::(|v| Ok(v as f64))?, + F32ConvertI32U => self.stack.values.replace_top::(|v| Ok(v as f32))?, + F32ConvertI64U => self.stack.values.replace_top::(|v| Ok(v as f32))?, + F64ConvertI32U => self.stack.values.replace_top::(|v| Ok(v as f64))?, + F64ConvertI64U => self.stack.values.replace_top::(|v| Ok(v as f64))?, + + I32Extend8S => self.stack.values.replace_top::(|v| Ok((v as i8) as i32))?, + I32Extend16S => self.stack.values.replace_top::(|v| Ok((v as i16) as i32))?, + I64Extend8S => self.stack.values.replace_top::(|v| Ok((v as i8) as i64))?, + I64Extend16S => self.stack.values.replace_top::(|v| Ok((v as i16) as i64))?, + I64Extend32S => self.stack.values.replace_top::(|v| Ok((v as i32) as i64))?, + I64ExtendI32U => self.stack.values.replace_top::(|v| Ok(v as i64))?, + I64ExtendI32S => self.stack.values.replace_top::(|v| Ok(v as i64))?, + I32WrapI64 => self.stack.values.replace_top::(|v| Ok(v as i32))?, + + F32DemoteF64 => self.stack.values.replace_top::(|v| Ok(v as f32))?, + F64PromoteF32 => self.stack.values.replace_top::(|v| Ok(v as f64))?, + + F32Abs => self.stack.values.replace_top::(|v| Ok(v.abs()))?, + F64Abs => self.stack.values.replace_top::(|v| Ok(v.abs()))?, + F32Neg => self.stack.values.replace_top::(|v| Ok(-v))?, + F64Neg => self.stack.values.replace_top::(|v| Ok(-v))?, + F32Ceil => self.stack.values.replace_top::(|v| Ok(v.ceil()))?, + F64Ceil => self.stack.values.replace_top::(|v| Ok(v.ceil()))?, + F32Floor => self.stack.values.replace_top::(|v| Ok(v.floor()))?, + F64Floor => self.stack.values.replace_top::(|v| Ok(v.floor()))?, + F32Trunc => self.stack.values.replace_top::(|v| Ok(v.trunc()))?, + F64Trunc => self.stack.values.replace_top::(|v| Ok(v.trunc()))?, + F32Nearest => self.stack.values.replace_top::(|v| Ok(v.tw_nearest()))?, + F64Nearest => self.stack.values.replace_top::(|v| Ok(v.tw_nearest()))?, + F32Sqrt => self.stack.values.replace_top::(|v| Ok(v.sqrt()))?, + F64Sqrt => self.stack.values.replace_top::(|v| Ok(v.sqrt()))?, + F32Min => self.stack.values.calculate::(|a, b| Ok(a.tw_minimum(b)))?, + F64Min => self.stack.values.calculate::(|a, b| Ok(a.tw_minimum(b)))?, + F32Max => self.stack.values.calculate::(|a, b| Ok(a.tw_maximum(b)))?, + F64Max => self.stack.values.calculate::(|a, b| Ok(a.tw_maximum(b)))?, + F32Copysign => self.stack.values.calculate::(|a, b| Ok(a.copysign(b)))?, + F64Copysign => self.stack.values.calculate::(|a, b| Ok(a.copysign(b)))?, // no-op instructions since types are erased at runtime I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {} - // unsigned versions of these are a bit broken atm I32TruncF32S => checked_conv_float!(f32, i32, self), I32TruncF64S => checked_conv_float!(f64, i32, self), I32TruncF32U => checked_conv_float!(f32, u32, i32, self), @@ -277,31 +373,30 @@ impl<'store, 'stack> Executor<'store, 'stack> { TableGrow(table_idx) => self.exec_table_grow(*table_idx)?, TableFill(table_idx) => self.exec_table_fill(*table_idx)?, - I32TruncSatF32S => arithmetic_single!(trunc, f32, i32, self), - I32TruncSatF32U => arithmetic_single!(trunc, f32, u32, self), - I32TruncSatF64S => arithmetic_single!(trunc, f64, i32, self), - I32TruncSatF64U => arithmetic_single!(trunc, f64, u32, self), - I64TruncSatF32S => arithmetic_single!(trunc, f32, i64, self), - I64TruncSatF32U => arithmetic_single!(trunc, f32, u64, self), - I64TruncSatF64S => arithmetic_single!(trunc, f64, i64, self), - I64TruncSatF64U => arithmetic_single!(trunc, f64, u64, self), - + I32TruncSatF32S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i32))?, + I32TruncSatF32U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u32))?, + I32TruncSatF64S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i32))?, + I32TruncSatF64U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u32))?, + I64TruncSatF32S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i64))?, + I64TruncSatF32U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u64))?, + I64TruncSatF64S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i64))?, + I64TruncSatF64U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u64))?, // custom instructions - LocalGet2(a, b) => self.exec_local_get2(*a, *b), - LocalGet3(a, b, c) => self.exec_local_get3(*a, *b, *c), - LocalTeeGet(a, b) => self.exec_local_tee_get(*a, *b)?, - LocalGetSet(a, b) => self.exec_local_get_set(*a, *b), - I64XorConstRotl(rotate_by) => self.exec_i64_xor_const_rotl(*rotate_by)?, - I32LocalGetConstAdd(local, val) => self.exec_i32_local_get_const_add(*local, *val), - I32ConstStoreLocal { local, const_i32, offset, mem_addr } => { - self.exec_i32_const_store_local(*local, *const_i32, *offset, *mem_addr)? - } - I32StoreLocal { local_a, local_b, offset, mem_addr } => { - self.exec_i32_store_local(*local_a, *local_b, *offset, *mem_addr)? - } + // LocalGet2(a, b) => self.exec_local_get2(*a, *b), + // LocalGet3(a, b, c) => self.exec_local_get3(*a, *b, *c), + // LocalTeeGet(a, b) => self.exec_local_tee_get(*a, *b)?, + // LocalGetSet(a, b) => self.exec_local_get_set(*a, *b), + // I64XorConstRotl(rotate_by) => self.exec_i64_xor_const_rotl(*rotate_by)?, + // I32LocalGetConstAdd(local, val) => self.exec_i32_local_get_const_add(*local, *val), + // I32ConstStoreLocal { local, const_i32, offset, mem_addr } => { + // self.exec_i32_const_store_local(*local, *const_i32, *offset, *mem_addr)? + // } + // I32StoreLocal { local_a, local_b, offset, mem_addr } => { + // self.exec_i32_store_local(*local_a, *local_b, *offset, *mem_addr)? + // } }; - self.cf.instr_ptr += 1; + self.cf.incr_instr_ptr(); Ok(ControlFlow::Continue(())) } @@ -311,30 +406,16 @@ impl<'store, 'stack> Executor<'store, 'stack> { Err(Error::Trap(Trap::Unreachable)) } - 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()?; - - // 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 + fn exec_call(&mut self, wasm_func: Rc, owner: ModuleInstanceAddr) -> Result> { + let params = self.stack.values.pop_many_raw(&wasm_func.ty.params)?; + let new_call_frame = + CallFrame::new_raw(wasm_func, owner, params.into_iter().rev(), self.stack.blocks.len() as u32); + self.cf.incr_instr_ptr(); // 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); - self.cf.instr_ptr -= 1; - Ok(()) + Ok(ControlFlow::Continue(())) } - fn exec_call_direct(&mut self, v: u32) -> Result<()> { + 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, @@ -342,17 +423,19 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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); - return Ok(()); + self.stack.values.extend_from_wasmvalues(&res); + self.cf.incr_instr_ptr(); + 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<()> { + 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_idx: u32 = self.stack.values.pop::()? as u32; let table = table.borrow(); assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref"); table @@ -377,8 +460,9 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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); - return Ok(()); + self.stack.values.extend_from_wasmvalues(&res); + self.cf.incr_instr_ptr(); + return Ok(ControlFlow::Continue(())); } }; @@ -392,7 +476,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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 { + if self.stack.values.pop::()? != 0 { self.enter_block(self.cf.instr_ptr(), end_offset, BlockType::If, args); return Ok(()); } @@ -414,67 +498,37 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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) - } - }; - - self.stack.blocks.push(BlockFrame { - instr_ptr, - end_instr_offset, - stack_ptr: self.stack.values.len() as u32 - params as u32, - results, - params, - ty, - }); + let (params, results) = match args { + BlockArgs::Empty => (StackHeight::default(), StackHeight::default()), + BlockArgs::Type(t) => (StackHeight::default(), t.into()), + BlockArgs::FuncType(t) => { + let ty = self.module.func_ty(t); + ((&*ty.params).into(), (&*ty.results).into()) + } }; - #[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, - }); - }; + self.stack.blocks.push(BlockFrame { + instr_ptr, + end_instr_offset, + stack_ptr: self.stack.values.height(), + results, + params, + ty, + }); } fn exec_br(&mut self, to: u32) -> Result> { - break_to!(to, self); + if self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() { + return self.exec_return(); + } + self.cf.incr_instr_ptr(); Ok(ControlFlow::Continue(())) } fn exec_br_if(&mut self, to: u32) -> Result> { - let val: i32 = self.stack.values.pop()?.into(); - if val != 0 { - break_to!(to, self); + if self.stack.values.pop::()? != 0 + && self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() + { + return self.exec_return(); } self.cf.incr_instr_ptr(); Ok(ControlFlow::Continue(())) @@ -486,11 +540,15 @@ impl<'store, 'stack> Executor<'store, 'stack> { return Err(Error::Other(format!("br_table out of bounds: {} >= {}", end, self.cf.instructions().len()))); } - 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), + let idx = self.stack.values.pop::()?; + let to = match self.cf.instructions()[start..end].get(idx as usize) { + None => default, + Some(Instruction::BrLabel(to)) => *to, _ => return Err(Error::Other("br_table with invalid label".to_string())), + }; + + if self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() { + return self.exec_return(); } self.cf.incr_instr_ptr(); @@ -512,56 +570,46 @@ impl<'store, 'stack> Executor<'store, 'stack> { } 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); + self.stack.values.truncate_keep(&block.stack_ptr, &block.results); Ok(()) } - 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)?)?); + self.stack.values.push_dyn(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()?) - } - - fn exec_const(&mut self, val: impl Into) { - self.stack.values.push(val.into()); + fn exec_global_set(&mut self, global_index: u32) -> Result<()> + where + TinyWasmValue: From, + { + self.store.set_global_val(self.module.resolve_global_addr(global_index)?, self.stack.values.pop::()?.into()) } fn exec_ref_is_null(&mut self) -> Result<()> { - self.stack.values.replace_top(|val| ((i32::from(val) == -1) as i32).into()) + let is_null = self.stack.values.pop::()?.is_none() as i32; + self.stack.values.push::(is_null); + Ok(()) } fn exec_memory_size(&mut self, addr: u32) -> Result<()> { let mem = self.store.get_mem(self.module.resolve_mem_addr(addr)?)?; - self.stack.values.push((mem.borrow().page_count() as i32).into()); + self.stack.values.push::(mem.borrow().page_count() as i32); Ok(()) } fn exec_memory_grow(&mut self, addr: u32) -> Result<()> { 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(), - }; + let pages_delta = self.stack.values.pop::()?; + self.stack.values.push::(match mem.grow(pages_delta) { + Some(_) => prev_size, + None => -1, + }); Ok(()) } 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(); + let size = self.stack.values.pop::()?; + let src = self.stack.values.pop::()?; + let dst = self.stack.values.pop::()?; if from == to { let mut mem_from = self.store.get_mem(self.module.resolve_mem_addr(from)?)?.borrow_mut(); @@ -576,18 +624,18 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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(); + let size = self.stack.values.pop::()?; + let val = self.stack.values.pop::()?; + let dst = 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 + let size = self.stack.values.pop::()?; // n + let offset = self.stack.values.pop::()?; // s + let dst = self.stack.values.pop::()?; // 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)?)?; @@ -617,9 +665,9 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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(); + let size: i32 = self.stack.values.pop::()?; + let src: i32 = self.stack.values.pop::()?; + let dst: i32 = self.stack.values.pop::()?; if from == to { let mut table_from = self.store.get_table(self.module.resolve_table_addr(from)?)?.borrow_mut(); @@ -634,14 +682,14 @@ impl<'store, 'stack> Executor<'store, 'stack> { Ok(()) } - fn exec_mem_load, const LOAD_SIZE: usize, TARGET: Into>( + fn exec_mem_load, const LOAD_SIZE: usize, TARGET: InternalValue>( &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 val = self.stack.values.pop::()? as u64; let Some(Ok(addr)) = offset.checked_add(val).map(|a| a.try_into()) else { cold(); return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { @@ -650,42 +698,40 @@ impl<'store, 'stack> Executor<'store, 'stack> { max: mem.borrow().max_pages(), })); }; - let val = mem.borrow().load_as::(addr)?; - self.stack.values.push(cast(val).into()); + self.stack.values.push(cast(val)); Ok(()) } - fn exec_mem_store + MemStorable, const N: usize>( + fn exec_mem_store, const N: usize>( &mut self, + val: T, 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(); + let addr = self.stack.values.pop::()? as u64; mem.borrow_mut().store((offset + addr) as usize, val.len(), &val)?; Ok(()) } 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()); + let idx: i32 = self.stack.values.pop::()?; + let v = table.borrow().get_wasm_val(idx as u32)?; + self.stack.values.push_dyn(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(); + let val = self.stack.values.pop::()?; + let idx = self.stack.values.pop::()? as u32; table.borrow_mut().set(idx, val.into())?; - 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()); + self.stack.values.push_dyn(table.borrow().size().into()); Ok(()) } fn exec_table_init(&mut self, elem_index: u32, table_index: u32) -> Result<()> { @@ -694,9 +740,9 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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 + let size: i32 = self.stack.values.pop::()?; // n + let offset: i32 = self.stack.values.pop::()?; // s + let dst: i32 = self.stack.values.pop::()?; // 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()); @@ -721,12 +767,12 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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(); + let n = self.stack.values.pop::()?; + let val = self.stack.values.pop::()?; match table.borrow_mut().grow(n, val.into()) { - Ok(_) => self.stack.values.push(sz.into()), - Err(_) => self.stack.values.push((-1_i32).into()), + Ok(_) => self.stack.values.push_dyn(sz.into()), + Err(_) => self.stack.values.push_dyn((-1_i32).into()), } Ok(()) @@ -734,9 +780,9 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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(); + let n = self.stack.values.pop::()?; + let val = self.stack.values.pop::()?; + let i = self.stack.values.pop::()?; if unlikely(i + n > table.borrow().size()) { return Err(Error::Trap(Trap::TableOutOfBounds { @@ -753,50 +799,4 @@ impl<'store, 'stack> Executor<'store, 'stack> { table.borrow_mut().fill(self.module.func_addrs(), i as usize, n as usize, val.into())?; Ok(()) } - - // custom instructions - fn exec_i32_const_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_mem_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_store_local(&mut self, local_a: u32, local_b: u32, offset: u32, mem_addr: u8) -> Result<()> { - let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr as u32)?)?; - let addr: u64 = self.cf.get_local(local_a).into(); - let val: i32 = self.cf.get_local(local_b).into(); - let val = val.to_mem_bytes(); - 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/interpreter/num_helpers.rs b/crates/tinywasm/src/runtime/interpreter/num_helpers.rs new file mode 100644 index 0000000..d0402bc --- /dev/null +++ b/crates/tinywasm/src/runtime/interpreter/num_helpers.rs @@ -0,0 +1,163 @@ +pub(crate) trait CheckedWrappingRem +where + Self: Sized, +{ + fn checked_wrapping_rem(self, rhs: Self) -> Option; +} + +/// 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. +/// Rust sadly doesn't have wrapping casts for floats yet, maybe never. +/// Alternatively, https://crates.io/crates/az could be used for this but +/// it's not worth the dependency. +#[rustfmt::skip] +macro_rules! float_min_max { + (f32, i32) => {(-2147483904.0_f32, 2147483648.0_f32)}; + (f64, i32) => {(-2147483649.0_f64, 2147483648.0_f64)}; + (f32, u32) => {(-1.0_f32, 4294967296.0_f32)}; // 2^32 + (f64, u32) => {(-1.0_f64, 4294967296.0_f64)}; // 2^32 + (f32, i64) => {(-9223373136366403584.0_f32, 9223372036854775808.0_f32)}; // 2^63 + 2^40 | 2^63 + (f64, i64) => {(-9223372036854777856.0_f64, 9223372036854775808.0_f64)}; // 2^63 + 2^40 | 2^63 + (f32, u64) => {(-1.0_f32, 18446744073709551616.0_f32)}; // 2^64 + (f64, u64) => {(-1.0_f64, 18446744073709551616.0_f64)}; // 2^64 + // other conversions are not allowed + ($from:ty, $to:ty) => {compile_error!("invalid float conversion")}; +} + +/// Convert a value on the stack with error checking +macro_rules! checked_conv_float { + // Direct conversion with error checking (two types) + ($from:tt, $to:tt, $self:expr) => { + checked_conv_float!($from, $to, $to, $self) + }; + // Conversion with an intermediate unsigned type and error checking (three types) + ($from:tt, $intermediate:tt, $to:tt, $self:expr) => { + $self.stack.values.replace_top::<$from, $to>(|v| { + let (min, max) = float_min_max!($from, $intermediate); + if unlikely(v.is_nan()) { + return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); + } + if unlikely(v <= min || v >= max) { + return Err(Error::Trap(crate::Trap::IntegerOverflow)); + } + Ok((v as $intermediate as $to).into()) + })? + }; +} + +pub(crate) use checked_conv_float; +pub(crate) use float_min_max; + +pub(crate) trait TinywasmFloatExt { + fn tw_minimum(self, other: Self) -> Self; + fn tw_maximum(self, other: Self) -> Self; + fn tw_nearest(self) -> Self; +} + +#[cfg(not(feature = "std"))] +use super::no_std_floats::NoStdFloatExt; + +macro_rules! impl_wasm_float_ops { + ($($t:ty)*) => ($( + impl TinywasmFloatExt for $t { + // https://webassembly.github.io/spec/core/exec/numerics.html#op-fnearest + fn tw_nearest(self) -> Self { + match self { + x if x.is_nan() => x, // preserve NaN + x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros + x if (0.0..=0.5).contains(&x) => 0.0, + x if (-0.5..0.0).contains(&x) => -0.0, + x => { + // Handle normal and halfway cases + let rounded = x.round(); + let diff = (x - rounded).abs(); + if diff != 0.5 || rounded % 2.0 == 0.0 { + return rounded + } + + rounded - x.signum() // Make even + } + } + } + + // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmin + // Based on f32::minimum (which is not yet stable) + #[inline] + fn tw_minimum(self, other: Self) -> Self { + match self.partial_cmp(&other) { + Some(core::cmp::Ordering::Less) => self, + Some(core::cmp::Ordering::Greater) => other, + Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { self } else { other }, + None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + } + } + + // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmax + // Based on f32::maximum (which is not yet stable) + #[inline] + fn tw_maximum(self, other: Self) -> Self { + match self.partial_cmp(&other) { + Some(core::cmp::Ordering::Greater) => self, + Some(core::cmp::Ordering::Less) => other, + Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { other } else { self }, + None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + } + } + } + )*) +} + +impl_wasm_float_ops! { f32 f64 } + +pub(crate) trait WasmIntOps { + fn wasm_shl(self, rhs: Self) -> Self; + fn wasm_shr(self, rhs: Self) -> Self; + fn wasm_rotl(self, rhs: Self) -> Self; + fn wasm_rotr(self, rhs: Self) -> Self; +} + +macro_rules! impl_wrapping_self_sh { + ($($t:ty)*) => ($( + impl WasmIntOps for $t { + #[inline] + fn wasm_shl(self, rhs: Self) -> Self { + self.wrapping_shl(rhs as u32) + } + + #[inline] + fn wasm_shr(self, rhs: Self) -> Self { + self.wrapping_shr(rhs as u32) + } + + #[inline] + fn wasm_rotl(self, rhs: Self) -> Self { + self.rotate_left(rhs as u32) + } + + #[inline] + fn wasm_rotr(self, rhs: Self) -> Self { + self.rotate_right(rhs as u32) + } + } + )*) +} + +impl_wrapping_self_sh! { i32 i64 u32 u64 } + +macro_rules! impl_checked_wrapping_rem { + ($($t:ty)*) => ($( + impl CheckedWrappingRem for $t { + #[inline] + fn checked_wrapping_rem(self, rhs: Self) -> Option { + if rhs == 0 { + None + } else { + Some(self.wrapping_rem(rhs)) + } + } + } + )*) +} + +impl_checked_wrapping_rem! { i32 i64 u32 u64 } diff --git a/crates/tinywasm/src/runtime/interpreter/traits.rs b/crates/tinywasm/src/runtime/interpreter/traits.rs deleted file mode 100644 index 7aeb3b7..0000000 --- a/crates/tinywasm/src/runtime/interpreter/traits.rs +++ /dev/null @@ -1,119 +0,0 @@ -pub(crate) trait CheckedWrappingRem -where - Self: Sized, -{ - fn checked_wrapping_rem(self, rhs: Self) -> Option; -} - -pub(crate) trait TinywasmFloatExt { - fn tw_minimum(self, other: Self) -> Self; - fn tw_maximum(self, other: Self) -> Self; - fn tw_nearest(self) -> Self; -} - -#[cfg(not(feature = "std"))] -use super::no_std_floats::NoStdFloatExt; - -macro_rules! impl_wasm_float_ops { - ($($t:ty)*) => ($( - impl TinywasmFloatExt for $t { - // https://webassembly.github.io/spec/core/exec/numerics.html#op-fnearest - fn tw_nearest(self) -> Self { - match self { - x if x.is_nan() => x, // preserve NaN - x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros - x if (0.0..=0.5).contains(&x) => 0.0, - x if (-0.5..0.0).contains(&x) => -0.0, - x => { - // Handle normal and halfway cases - let rounded = x.round(); - let diff = (x - rounded).abs(); - if diff != 0.5 || rounded % 2.0 == 0.0 { - return rounded - } - - rounded - x.signum() // Make even - } - } - } - - // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmin - // Based on f32::minimum (which is not yet stable) - #[inline] - fn tw_minimum(self, other: Self) -> Self { - match self.partial_cmp(&other) { - Some(core::cmp::Ordering::Less) => self, - Some(core::cmp::Ordering::Greater) => other, - Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { self } else { other }, - None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - } - } - - // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmax - // Based on f32::maximum (which is not yet stable) - #[inline] - fn tw_maximum(self, other: Self) -> Self { - match self.partial_cmp(&other) { - Some(core::cmp::Ordering::Greater) => self, - Some(core::cmp::Ordering::Less) => other, - Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { other } else { self }, - None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - } - } - } - )*) -} - -impl_wasm_float_ops! { f32 f64 } - -pub(crate) trait WasmIntOps { - fn wasm_shl(self, rhs: Self) -> Self; - fn wasm_shr(self, rhs: Self) -> Self; - fn wasm_rotl(self, rhs: Self) -> Self; - fn wasm_rotr(self, rhs: Self) -> Self; -} - -macro_rules! impl_wrapping_self_sh { - ($($t:ty)*) => ($( - impl WasmIntOps for $t { - #[inline] - fn wasm_shl(self, rhs: Self) -> Self { - self.wrapping_shl(rhs as u32) - } - - #[inline] - fn wasm_shr(self, rhs: Self) -> Self { - self.wrapping_shr(rhs as u32) - } - - #[inline] - fn wasm_rotl(self, rhs: Self) -> Self { - self.rotate_left(rhs as u32) - } - - #[inline] - fn wasm_rotr(self, rhs: Self) -> Self { - self.rotate_right(rhs as u32) - } - } - )*) -} - -impl_wrapping_self_sh! { i32 i64 u32 u64 } - -macro_rules! impl_checked_wrapping_rem { - ($($t:ty)*) => ($( - impl CheckedWrappingRem for $t { - #[inline] - fn checked_wrapping_rem(self, rhs: Self) -> Option { - if rhs == 0 { - None - } else { - Some(self.wrapping_rem(rhs)) - } - } - } - )*) -} - -impl_checked_wrapping_rem! { i32 i64 u32 u64 } diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/runtime/mod.rs index dc4816b..fca58cc 100644 --- a/crates/tinywasm/src/runtime/mod.rs +++ b/crates/tinywasm/src/runtime/mod.rs @@ -1,17 +1,11 @@ mod interpreter; -mod stack; +pub(crate) mod stack; -mod raw; +#[doc(hidden)] +pub use stack::values; +pub use stack::values::*; -#[cfg(all(not(feature = "nightly"), feature = "simd"))] -compile_error!("`simd` feature requires nightly"); - -#[cfg(feature = "simd")] -mod raw_simd; - -pub use raw::RawWasmValue; -pub(crate) use stack::CallFrame; -pub(crate) use stack::Stack; +pub(crate) use stack::{CallFrame, Stack}; /// The main TinyWasm runtime. /// diff --git a/crates/tinywasm/src/runtime/raw.rs b/crates/tinywasm/src/runtime/raw.rs deleted file mode 100644 index a186fd7..0000000 --- a/crates/tinywasm/src/runtime/raw.rs +++ /dev/null @@ -1,123 +0,0 @@ -use core::fmt::Debug; -use tinywasm_types::{ValType, WasmValue}; - -/// A raw wasm value. -/// -/// This is the internal representation of all wasm values -/// -/// See [`WasmValue`] for the public representation. -#[derive(Clone, Copy, Default, PartialEq, Eq)] -pub struct RawWasmValue(pub [u8; 8]); - -impl Debug for RawWasmValue { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "RawWasmValue({})", 0) - } -} - -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(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)) - } - ValType::RefFunc => { - self.as_reference().map(WasmValue::RefFunc).unwrap_or(WasmValue::RefNull(ValType::RefFunc)) - } - } - } - - #[inline] - pub(crate) fn as_reference(&self) -> Option { - match i64::from(*self) { - v if v < 0 => None, - addr => Some(addr as u32), - } - } -} - -impl From for RawWasmValue { - #[inline] - fn from(v: WasmValue) -> Self { - match v { - WasmValue::I32(i) => Self::from(i), - WasmValue::I64(i) => Self::from(i), - WasmValue::F32(i) => Self::from(i), - WasmValue::F64(i) => Self::from(i), - WasmValue::V128(_) => panic!("RawWasmValue cannot be converted to V128"), - WasmValue::RefExtern(v) => Self::from(v as i64), - WasmValue::RefFunc(v) => Self::from(v as i64), - WasmValue::RefNull(_) => Self::from(-1i64), - } - } -} - -macro_rules! impl_from_raw_wasm_value { - ($type:ty, $to_raw:expr, $from_raw:expr) => { - // Implement From<$type> for RawWasmValue - impl From<$type> for RawWasmValue { - #[inline] - fn from(value: $type) -> Self { - #[allow(clippy::redundant_closure_call)] - Self(u64::to_ne_bytes($to_raw(value))) - } - } - - // Implement From for $type - impl From for $type { - #[inline] - fn from(value: RawWasmValue) -> Self { - #[allow(clippy::redundant_closure_call)] - $from_raw(value.0) - } - } - }; -} - -// This all looks like a lot of extra steps, but the compiler will optimize it all away. -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!(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() -)); -impl_from_raw_wasm_value!(f64, f64::to_bits, |x: [u8; 8]| f64::from_bits(u64::from_ne_bytes( - x[0..8].try_into().unwrap() -))); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_raw_wasm_value() { - macro_rules! test_macro { - ($( $ty:ty => $val:expr ),*) => { - $( - let raw: RawWasmValue = $val.into(); - let val: $ty = raw.into(); - assert_eq!(val, $val); - )* - }; - } - - test_macro! { - i32 => 0, i64 => 0, u8 => 0, u16 => 0, u32 => 0, u64 => 0, i8 => 0, i16 => 0, f32 => 0.0, f64 => 0.0, - i32 => i32::MIN, i64 => i64::MIN, u8 => u8::MIN, u16 => u16::MIN, u32 => u32::MIN, u64 => u64::MIN, i8 => i8::MIN, i16 => i16::MIN, f32 => f32::MIN, f64 => f64::MIN, - i32 => i32::MAX, i64 => i64::MAX, u8 => u8::MAX, u16 => u16::MAX, u32 => u32::MAX, u64 => u64::MAX, i8 => i8::MAX, i16 => i16::MAX, f32 => f32::MAX, f64 => f64::MAX - } - } -} diff --git a/crates/tinywasm/src/runtime/raw_simd.rs b/crates/tinywasm/src/runtime/raw_simd.rs deleted file mode 100644 index ba7dd62..0000000 --- a/crates/tinywasm/src/runtime/raw_simd.rs +++ /dev/null @@ -1,27 +0,0 @@ -use core::{fmt::Debug, simd::Simd}; - -/// A large raw wasm value, used for 128-bit values. -/// -/// This is the internal representation of vector values. -/// -/// See [`WasmValue`] for the public representation. -#[derive(Clone, Copy, Default, PartialEq, Eq)] -pub struct RawSimdWasmValue(Simd); // wasm has up to 16 8 bit lanes - -impl Debug for RawSimdWasmValue { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "LargeRawWasmValue({})", 0) - } -} - -impl From for RawSimdWasmValue { - fn from(value: u128) -> Self { - Self(value.to_le_bytes().into()) - } -} - -impl From for u128 { - fn from(value: RawSimdWasmValue) -> Self { - u128::from_le_bytes(value.0.into()) - } -} diff --git a/crates/tinywasm/src/runtime/stack/block_stack.rs b/crates/tinywasm/src/runtime/stack/block_stack.rs index c01c9fb..cef536a 100644 --- a/crates/tinywasm/src/runtime/stack/block_stack.rs +++ b/crates/tinywasm/src/runtime/stack/block_stack.rs @@ -1,6 +1,8 @@ use crate::{cold, unlikely, Error, Result}; use alloc::vec::Vec; +use super::values::{StackHeight, StackLocation}; + #[derive(Debug)] pub(crate) struct BlockStack(Vec); @@ -57,16 +59,9 @@ pub(crate) struct BlockFrame { pub(crate) instr_ptr: usize, // position of the instruction pointer when the block was entered pub(crate) end_instr_offset: u32, // position of the end instruction of the block - pub(crate) stack_ptr: u32, // position of the stack pointer when the block was entered - pub(crate) results: u8, - pub(crate) params: u8, - - #[cfg(feature = "simd")] - pub(crate) simd_stack_ptr: u16, // position of the large stack pointer when the block was entered - #[cfg(feature = "simd")] - pub(crate) simd_results: u8, - #[cfg(feature = "simd")] - pub(crate) simd_params: u8, + pub(crate) stack_ptr: StackLocation, // stack pointer when the block was entered + pub(crate) results: StackHeight, + pub(crate) params: StackHeight, pub(crate) ty: BlockType, } diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index 30957be..8e9dbe7 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::values::{InternalValue, TinyWasmValue, Value128, Value32, Value64, ValueRef}; use super::BlockType; -use crate::runtime::RawWasmValue; use crate::unlikely; use crate::{Result, Trap}; use alloc::boxed::Box; use alloc::{rc::Rc, vec, vec::Vec}; -use tinywasm_types::{Instruction, LocalAddr, ModuleInstanceAddr, WasmFunction}; +use tinywasm_types::{Instruction, LocalAddr, ModuleInstanceAddr, WasmFunction, WasmValue}; pub(crate) const MAX_CALL_STACK_SIZE: usize = 1024; @@ -41,7 +41,25 @@ pub(crate) struct CallFrame { pub(crate) block_ptr: u32, pub(crate) func_instance: Rc, pub(crate) module_addr: ModuleInstanceAddr, - pub(crate) locals: Box<[RawWasmValue]>, + pub(crate) locals: Locals, +} + +#[derive(Debug)] +pub(crate) struct Locals { + pub(crate) locals_32: Box<[Value32]>, + pub(crate) locals_64: Box<[Value64]>, + pub(crate) locals_128: Box<[Value128]>, + pub(crate) locals_ref: Box<[ValueRef]>, +} + +impl Locals { + pub(crate) fn get(&self, local_index: LocalAddr) -> Result { + T::local_get(self, local_index) + } + + pub(crate) fn set(&mut self, local_index: LocalAddr, value: T) -> Result<()> { + T::local_set(self, local_index, value) + } } impl CallFrame { @@ -96,7 +114,7 @@ impl CallFrame { self.instr_ptr = break_to.instr_ptr; // We also want to push the params to the stack - values.break_to_params(break_to); + values.truncate_keep(&break_to.stack_ptr, &break_to.params); // check if we're breaking to the loop if break_to_relative != 0 { @@ -109,7 +127,7 @@ impl CallFrame { BlockType::Block | BlockType::If | BlockType::Else => { // this is a block, so we want to jump to the next instruction after the block ends // We also want to push the block's results to the stack - values.break_to_results(break_to); + values.truncate_keep(&break_to.stack_ptr, &break_to.results); // (the inst_ptr will be incremented by 1 before the next instruction is executed) self.instr_ptr = break_to.instr_ptr + break_to.end_instr_offset as usize; @@ -126,29 +144,48 @@ impl CallFrame { pub(crate) fn new( wasm_func_inst: Rc, owner: ModuleInstanceAddr, - params: &[RawWasmValue], + params: &[WasmValue], block_ptr: u32, ) -> Self { - let locals = { - let total_size = wasm_func_inst.locals.len() + params.len(); - let mut locals = Vec::new(); - locals.reserve_exact(total_size); - locals.extend(params); - locals.resize_with(total_size, RawWasmValue::default); - locals.into_boxed_slice() - }; - - Self { instr_ptr: 0, func_instance: wasm_func_inst, module_addr: owner, block_ptr, locals } + Self::new_raw(wasm_func_inst, owner, params.iter().map(|v| v.into()), block_ptr) } #[inline(always)] - pub(crate) fn set_local(&mut self, local_index: LocalAddr, value: RawWasmValue) { - self.locals[local_index as usize] = value; - } + pub(crate) fn new_raw( + wasm_func_inst: Rc, + owner: ModuleInstanceAddr, + params: impl ExactSizeIterator, + block_ptr: u32, + ) -> Self { + let locals = { + let mut locals_32 = Vec::new(); + let mut locals_64 = Vec::new(); + let mut locals_128 = Vec::new(); + let mut locals_ref = Vec::new(); + + for p in params { + match p { + TinyWasmValue::Value32(v) => locals_32.push(v), + TinyWasmValue::Value64(v) => locals_64.push(v), + TinyWasmValue::Value128(v) => locals_128.push(v), + TinyWasmValue::ValueRef(v) => locals_ref.push(v), + } + } - #[inline(always)] - pub(crate) fn get_local(&self, local_index: LocalAddr) -> RawWasmValue { - self.locals[local_index as usize] + locals_32.resize_with(wasm_func_inst.locals.local_32 as usize, Default::default); + locals_64.resize_with(wasm_func_inst.locals.local_64 as usize, Default::default); + locals_128.resize_with(wasm_func_inst.locals.local_128 as usize, Default::default); + locals_ref.resize_with(wasm_func_inst.locals.local_ref as usize, Default::default); + + Locals { + locals_32: locals_32.into_boxed_slice(), + locals_64: locals_64.into_boxed_slice(), + locals_128: locals_128.into_boxed_slice(), + locals_ref: locals_ref.into_boxed_slice(), + } + }; + + Self { instr_ptr: 0, func_instance: wasm_func_inst, module_addr: owner, block_ptr, locals } } #[inline(always)] diff --git a/crates/tinywasm/src/runtime/stack/mod.rs b/crates/tinywasm/src/runtime/stack/mod.rs index 06510ab..3902bd2 100644 --- a/crates/tinywasm/src/runtime/stack/mod.rs +++ b/crates/tinywasm/src/runtime/stack/mod.rs @@ -1,6 +1,7 @@ mod block_stack; mod call_stack; mod value_stack; +pub mod values; pub(crate) use block_stack::{BlockFrame, BlockStack, BlockType}; pub(crate) use call_stack::{CallFrame, CallStack}; @@ -16,6 +17,6 @@ pub(crate) struct Stack { impl Stack { pub(crate) fn new(call_frame: CallFrame) -> Self { - Self { values: ValueStack::default(), blocks: BlockStack::default(), call_stack: CallStack::new(call_frame) } + Self { values: ValueStack::new(), blocks: BlockStack::default(), call_stack: CallStack::new(call_frame) } } } diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index 159e366..40376f1 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -1,257 +1,174 @@ -use crate::{boxvec::BoxVec, cold, runtime::RawWasmValue, unlikely, Error, Result}; use alloc::vec::Vec; use tinywasm_types::{ValType, WasmValue}; -use super::BlockFrame; - -pub(crate) const VALUE_STACK_SIZE: usize = 1024 * 128; - -#[cfg(feature = "simd")] -pub(crate) const SIMD_VALUE_STACK_SIZE: usize = 1024 * 32; - -#[cfg(feature = "simd")] -use crate::runtime::raw_simd::RawSimdWasmValue; +use super::values::*; +use crate::Result; +pub(crate) const STACK_32_SIZE: usize = 1024 * 128; +pub(crate) const STACK_64_SIZE: usize = 1024 * 128; +pub(crate) const STACK_128_SIZE: usize = 1024 * 128; +pub(crate) const STACK_REF_SIZE: usize = 1024; #[derive(Debug)] pub(crate) struct ValueStack { - pub(crate) stack: BoxVec, - - #[cfg(feature = "simd")] - simd_stack: BoxVec, + pub(crate) stack_32: Vec, + pub(crate) stack_64: Vec, + pub(crate) stack_128: Vec, + pub(crate) stack_ref: Vec, } -impl Default for ValueStack { - fn default() -> Self { +impl ValueStack { + pub(crate) fn new() -> Self { Self { - stack: BoxVec::with_capacity(VALUE_STACK_SIZE), - - #[cfg(feature = "simd")] - simd_stack: BoxVec::with_capacity(SIMD_VALUE_STACK_SIZE), + stack_32: Vec::with_capacity(STACK_32_SIZE), + stack_64: Vec::with_capacity(STACK_64_SIZE), + stack_128: Vec::with_capacity(STACK_128_SIZE), + stack_ref: Vec::with_capacity(STACK_REF_SIZE), } } -} - -impl ValueStack { - #[inline] - pub(crate) fn extend_from_typed(&mut self, values: &[WasmValue]) { - #[cfg(not(feature = "simd"))] - self.stack.extend(values.iter().map(|v| RawWasmValue::from(*v))); - #[cfg(feature = "simd")] - { - values.iter().for_each(|v| match v { - WasmValue::V128(v) => self.simd_stack.push(RawSimdWasmValue::from(*v)), - v => self.stack.push(RawWasmValue::from(*v)), - }); + pub(crate) fn height(&self) -> StackLocation { + StackLocation { + s32: self.stack_32.len() as u32, + s64: self.stack_64.len() as u32, + s128: self.stack_128.len() as u32, + sref: self.stack_ref.len() as u32, } } - #[inline(always)] - pub(crate) fn replace_top(&mut self, func: fn(RawWasmValue) -> RawWasmValue) -> Result<()> { - let v = self.last_mut()?; - *v = func(*v); - Ok(()) - } - - #[inline(always)] - pub(crate) fn replace_top_trap(&mut self, func: fn(RawWasmValue) -> Result) -> Result<()> { - let v = self.last_mut()?; - *v = func(*v)?; - Ok(()) - } - - #[inline(always)] - pub(crate) fn calculate(&mut self, func: fn(RawWasmValue, RawWasmValue) -> RawWasmValue) -> Result<()> { - let v2 = self.pop()?; - let v1 = self.last_mut()?; - *v1 = func(*v1, v2); - Ok(()) - } - - #[inline(always)] - pub(crate) fn calculate_trap( - &mut self, - func: fn(RawWasmValue, RawWasmValue) -> Result, - ) -> Result<()> { - let v2 = self.pop()?; - let v1 = self.last_mut()?; - *v1 = func(*v1, v2)?; - Ok(()) + pub(crate) fn peek(&self) -> Result { + T::stack_peek(self) } - #[inline(always)] - pub(crate) fn len(&self) -> usize { - self.stack.len() + pub(crate) fn pop(&mut self) -> Result { + T::stack_pop(self) } - #[cfg(feature = "simd")] - #[inline(always)] - pub(crate) fn simd_len(&self) -> usize { - self.simd_stack.len() + pub(crate) fn push(&mut self, value: T) { + T::stack_push(self, value) } - #[inline] - pub(crate) fn truncate_keep(&mut self, n: u32, end_keep: u32) { - truncate_keep(&mut self.stack, n, end_keep); + pub(crate) fn drop(&mut self) -> Result<()> { + T::stack_pop(self).map(|_| ()) } - #[cfg(feature = "simd")] - #[inline] - pub(crate) fn truncate_keep_simd(&mut self, n: u16, end_keep: u32) { - truncate_keep(&mut self.simd_stack, n as u32, end_keep); + pub(crate) fn select(&mut self) -> Result<()> { + let cond: i32 = self.pop()?; + let val2: T = self.pop()?; + if cond == 0 { + self.drop::()?; + self.push(val2); + } + Ok(()) } - #[inline(always)] - pub(crate) fn push(&mut self, value: RawWasmValue) { - self.stack.push(value); + pub(crate) fn calculate(&mut self, func: fn(T, T) -> Result) -> Result<()> { + let v2 = T::stack_pop(self)?; + let v1 = T::stack_pop(self)?; + U::stack_push(self, func(v1, v2)?); + Ok(()) } - #[inline(always)] - pub(crate) fn extend_from_slice(&mut self, values: &[RawWasmValue]) { - self.stack.extend_from_slice(values); + pub(crate) fn replace_top(&mut self, func: fn(T) -> Result) -> Result<()> { + let v1 = T::stack_pop(self)?; + U::stack_push(self, func(v1)?); + Ok(()) } - #[inline] - pub(crate) fn last_mut(&mut self) -> Result<&mut RawWasmValue> { - match self.stack.last_mut() { - Some(v) => Ok(v), - None => { - cold(); // cold in here instead of the stack makes a huge performance difference - Err(Error::ValueStackUnderflow) - } + pub(crate) fn pop_dyn(&mut self, val_type: ValType) -> Result { + match val_type { + ValType::I32 => self.pop().map(TinyWasmValue::Value32), + ValType::I64 => self.pop().map(TinyWasmValue::Value64), + ValType::V128 => self.pop().map(TinyWasmValue::Value128), + ValType::RefExtern => self.pop().map(TinyWasmValue::ValueRef), + ValType::RefFunc => self.pop().map(TinyWasmValue::ValueRef), + ValType::F32 => self.pop().map(TinyWasmValue::Value32), + ValType::F64 => self.pop().map(TinyWasmValue::Value64), } } - #[inline] - pub(crate) fn last(&self) -> Result<&RawWasmValue> { - match self.stack.last() { - Some(v) => Ok(v), - None => { - cold(); // cold in here instead of the stack makes a huge performance difference - Err(Error::ValueStackUnderflow) - } - } + pub(crate) fn pop_params(&mut self, val_types: &[ValType]) -> Result> { + val_types.iter().map(|val_type| self.pop_wasmvalue(*val_type)).collect::>>() } - #[inline(always)] - pub(crate) fn pop(&mut self) -> Result { - match self.stack.pop() { - Some(v) => Ok(v), - None => { - cold(); // cold in here instead of the stack makes a huge performance difference - Err(Error::ValueStackUnderflow) - } - } + pub(crate) fn pop_results(&mut self, val_types: &[ValType]) -> Result> { + val_types.iter().rev().map(|val_type| self.pop_wasmvalue(*val_type)).collect::>>().map(|mut v| { + v.reverse(); + v + }) } - #[inline] - pub(crate) fn pop_params(&mut self, types: &[ValType]) -> Result> { - #[cfg(not(feature = "simd"))] - return Ok(self.pop_n(types.len())?.iter().zip(types.iter()).map(|(v, ty)| v.attach_type(*ty)).collect()); - - #[cfg(feature = "simd")] - { - let mut values = Vec::with_capacity(types.len()); - for ty in types { - match ty { - ValType::V128 => values.push(WasmValue::V128(self.simd_stack.pop().unwrap().into())), - ty => values.push(self.pop()?.attach_type(*ty)), - } - } - Ok(values) + pub(crate) fn pop_many_raw(&mut self, val_types: &[ValType]) -> Result> { + let mut values = Vec::with_capacity(val_types.len()); + for val_type in val_types.iter() { + values.push(self.pop_dyn(*val_type)?); } + Ok(values) } - #[inline] - pub(crate) fn break_to_results(&mut self, bf: &BlockFrame) { - let end = self.stack.len() - bf.results as usize; - self.stack.drain(bf.stack_ptr as usize..end); - - #[cfg(feature = "simd")] - let end = self.simd_stack.len() - bf.simd_results as usize; - #[cfg(feature = "simd")] - self.simd_stack.drain(bf.simd_stack_ptr as usize..end); + pub(crate) fn truncate_keep(&mut self, to: &StackLocation, keep: &StackHeight) { + truncate_keep(&mut self.stack_32, to.s32, keep.s32); + truncate_keep(&mut self.stack_64, to.s64, keep.s64); + truncate_keep(&mut self.stack_128, to.s128, keep.s128); + truncate_keep(&mut self.stack_ref, to.sref, keep.sref); } - #[inline] - pub(crate) fn break_to_params(&mut self, bf: &BlockFrame) { - let end = self.stack.len() - bf.params as usize; - self.stack.drain(bf.stack_ptr as usize..end); - - #[cfg(feature = "simd")] - let end = self.simd_stack.len() - bf.simd_params as usize; - #[cfg(feature = "simd")] - self.simd_stack.drain(bf.simd_stack_ptr as usize..end); + pub(crate) fn push_dyn(&mut self, value: TinyWasmValue) { + match value { + TinyWasmValue::Value32(v) => self.stack_32.push(v), + TinyWasmValue::Value64(v) => self.stack_64.push(v), + TinyWasmValue::Value128(v) => self.stack_128.push(v), + TinyWasmValue::ValueRef(v) => self.stack_ref.push(v), + } } - #[inline] - pub(crate) fn last_n(&self, n: usize) -> Result<&[RawWasmValue]> { - let len = self.stack.len(); - if unlikely(len < n) { - return Err(Error::ValueStackUnderflow); + pub(crate) fn pop_wasmvalue(&mut self, val_type: ValType) -> Result { + match val_type { + ValType::I32 => self.pop().map(WasmValue::I32), + ValType::I64 => self.pop().map(WasmValue::I64), + ValType::V128 => self.pop().map(WasmValue::V128), + ValType::F32 => self.pop().map(WasmValue::F32), + ValType::F64 => self.pop().map(WasmValue::F64), + ValType::RefExtern => self.pop().map(|v| match v { + Some(v) => WasmValue::RefExtern(v), + None => WasmValue::RefNull(ValType::RefExtern), + }), + ValType::RefFunc => self.pop().map(|v| match v { + Some(v) => WasmValue::RefFunc(v), + None => WasmValue::RefNull(ValType::RefFunc), + }), } - Ok(&self.stack[len - n..len]) } - #[inline] - 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) - } + pub(crate) fn extend_from_wasmvalues(&mut self, values: &[WasmValue]) { + for value in values.iter() { + self.push_dyn(value.into()) } } } -#[inline(always)] -fn truncate_keep(data: &mut BoxVec, n: u32, end_keep: u32) { +fn truncate_keep(data: &mut Vec, n: u32, end_keep: u32) { let total_to_keep = n + end_keep; let len = data.len() as u32; - assert!(len >= total_to_keep, "RawWasmValueotal to keep should be less than or equal to self.top"); + crate::log::error!("truncate_keep: len: {}, total_to_keep: {}, end_keep: {}", len, total_to_keep, end_keep); - if len <= total_to_keep { + if len <= n { return; // No need to truncate if the current size is already less than or equal to total_to_keep } - let items_to_remove = len - total_to_keep; - let remove_start_index = (len - items_to_remove - end_keep) as usize; - let remove_end_index = (len - end_keep) as usize; - data.drain(remove_start_index..remove_end_index); + data.drain((n as usize)..(len - end_keep) as usize); } #[cfg(test)] mod tests { use super::*; - #[test] - fn test_value_stack() { - let mut stack = ValueStack::default(); - stack.push(1.into()); - stack.push(2.into()); - stack.push(3.into()); - assert_eq!(stack.len(), 3); - assert_eq!(i32::from(stack.pop().unwrap()), 3); - assert_eq!(stack.len(), 2); - assert_eq!(i32::from(stack.pop().unwrap()), 2); - assert_eq!(stack.len(), 1); - assert_eq!(i32::from(stack.pop().unwrap()), 1); - assert_eq!(stack.len(), 0); - } - #[test] fn test_truncate_keep() { macro_rules! test_macro { ($( $n:expr, $end_keep:expr, $expected:expr ),*) => { $( - let mut stack = ValueStack::default(); - stack.push(1.into()); - stack.push(2.into()); - stack.push(3.into()); - stack.push(4.into()); - stack.push(5.into()); - stack.truncate_keep($n, $end_keep); + let mut stack = alloc::vec![1,2,3,4,5]; + truncate_keep(&mut stack, $n, $end_keep); assert_eq!(stack.len(), $expected); )* }; diff --git a/crates/tinywasm/src/runtime/stack/values.rs b/crates/tinywasm/src/runtime/stack/values.rs new file mode 100644 index 0000000..606e13a --- /dev/null +++ b/crates/tinywasm/src/runtime/stack/values.rs @@ -0,0 +1,420 @@ +#![allow(missing_docs)] +use tinywasm_types::{ValType, WasmValue}; + +use crate::{Error, Result}; + +use super::{call_stack::Locals, ValueStack}; + +pub type Value32 = u32; +pub type Value64 = u64; +pub type Value128 = u128; +pub type ValueRef = Option; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct StackLocation { + pub(crate) s32: u32, + pub(crate) s64: u32, + pub(crate) s128: u32, + pub(crate) sref: u32, +} + +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct StackHeight { + pub(crate) s32: u32, + pub(crate) s64: u32, + pub(crate) s128: u32, + pub(crate) sref: u32, +} + +impl From for StackHeight { + fn from(value: ValType) -> Self { + match value { + ValType::I32 | ValType::F32 => Self { s32: 1, ..Default::default() }, + ValType::I64 | ValType::F64 => Self { s64: 1, ..Default::default() }, + ValType::V128 => Self { s128: 1, ..Default::default() }, + ValType::RefExtern | ValType::RefFunc => Self { sref: 1, ..Default::default() }, + } + } +} + +impl From<&[ValType]> for StackHeight { + fn from(value: &[ValType]) -> Self { + let mut s32 = 0; + let mut s64 = 0; + let mut s128 = 0; + let mut sref = 0; + for val_type in value.iter() { + match val_type { + ValType::I32 | ValType::F32 => s32 += 1, + ValType::I64 | ValType::F64 => s64 += 1, + ValType::V128 => s128 += 1, + ValType::RefExtern | ValType::RefFunc => sref += 1, + } + } + Self { s32, s64, s128, sref } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum TinyWasmValue { + Value32(Value32), + Value64(Value64), + Value128(Value128), + ValueRef(ValueRef), +} + +impl TinyWasmValue { + pub fn unwrap_32(&self) -> Value32 { + match self { + TinyWasmValue::Value32(v) => *v, + _ => unreachable!("Expected Value32"), + } + } + + pub fn unwrap_64(&self) -> Value64 { + match self { + TinyWasmValue::Value64(v) => *v, + _ => unreachable!("Expected Value64"), + } + } + + pub fn unwrap_128(&self) -> Value128 { + match self { + TinyWasmValue::Value128(v) => *v, + _ => unreachable!("Expected Value128"), + } + } + + pub fn unwrap_ref(&self) -> ValueRef { + match self { + TinyWasmValue::ValueRef(v) => *v, + _ => unreachable!("Expected ValueRef"), + } + } + + pub fn attach_type(&self, ty: ValType) -> WasmValue { + match ty { + ValType::I32 => WasmValue::I32(self.unwrap_32() as i32), + ValType::I64 => WasmValue::I64(self.unwrap_64() as i64), + ValType::F32 => WasmValue::F32(f32::from_bits(self.unwrap_32())), + ValType::F64 => WasmValue::F64(f64::from_bits(self.unwrap_64())), + ValType::V128 => WasmValue::V128(self.unwrap_128()), + ValType::RefExtern => match self.unwrap_ref() { + Some(v) => WasmValue::RefExtern(v), + None => WasmValue::RefNull(ValType::RefExtern), + }, + ValType::RefFunc => match self.unwrap_ref() { + Some(v) => WasmValue::RefFunc(v), + None => WasmValue::RefNull(ValType::RefFunc), + }, + } + } +} + +impl Default for TinyWasmValue { + fn default() -> Self { + TinyWasmValue::Value32(0) + } +} + +impl From for TinyWasmValue { + fn from(value: WasmValue) -> Self { + match value { + WasmValue::I32(v) => TinyWasmValue::Value32(v as u32), + WasmValue::I64(v) => TinyWasmValue::Value64(v as u64), + WasmValue::V128(v) => TinyWasmValue::Value128(v), + WasmValue::F32(v) => TinyWasmValue::Value32(v.to_bits()), + WasmValue::F64(v) => TinyWasmValue::Value64(v.to_bits()), + WasmValue::RefFunc(v) => TinyWasmValue::ValueRef(Some(v)), + WasmValue::RefExtern(v) => TinyWasmValue::ValueRef(Some(v)), + WasmValue::RefNull(_) => TinyWasmValue::ValueRef(None), + } + } +} + +impl From<&WasmValue> for TinyWasmValue { + fn from(value: &WasmValue) -> Self { + match value { + WasmValue::I32(v) => TinyWasmValue::Value32(*v as u32), + WasmValue::I64(v) => TinyWasmValue::Value64(*v as u64), + WasmValue::V128(v) => TinyWasmValue::Value128(*v), + WasmValue::F32(v) => TinyWasmValue::Value32(v.to_bits()), + WasmValue::F64(v) => TinyWasmValue::Value64(v.to_bits()), + WasmValue::RefFunc(v) => TinyWasmValue::ValueRef(Some(*v)), + WasmValue::RefExtern(v) => TinyWasmValue::ValueRef(Some(*v)), + WasmValue::RefNull(_) => TinyWasmValue::ValueRef(None), + } + } +} + +impl From for TinyWasmValue { + fn from(value: f32) -> Self { + TinyWasmValue::Value32(value.to_bits()) + } +} + +impl From for TinyWasmValue { + fn from(value: f64) -> Self { + TinyWasmValue::Value64(value.to_bits()) + } +} + +impl From for TinyWasmValue { + fn from(value: i32) -> Self { + TinyWasmValue::Value32(value as u32) + } +} + +impl From for TinyWasmValue { + fn from(value: u32) -> Self { + TinyWasmValue::Value32(value) + } +} + +impl From for TinyWasmValue { + fn from(value: i64) -> Self { + TinyWasmValue::Value64(value as u64) + } +} + +impl From for TinyWasmValue { + fn from(value: u64) -> Self { + TinyWasmValue::Value64(value) + } +} + +impl From for TinyWasmValue { + fn from(value: Value128) -> Self { + TinyWasmValue::Value128(value) + } +} + +impl From for TinyWasmValue { + fn from(value: ValueRef) -> Self { + TinyWasmValue::ValueRef(value) + } +} + +// TODO: this can be made a bit more maintainable by using a macro + +mod sealed { + #[allow(unreachable_pub)] + pub trait Sealed {} +} + +impl sealed::Sealed for i32 {} +impl sealed::Sealed for f32 {} +impl sealed::Sealed for i64 {} +impl sealed::Sealed for u64 {} +impl sealed::Sealed for f64 {} +impl sealed::Sealed for u32 {} +impl sealed::Sealed for Value128 {} +impl sealed::Sealed for ValueRef {} + +pub(crate) trait InternalValue: sealed::Sealed { + fn stack_push(stack: &mut ValueStack, value: Self); + fn stack_pop(stack: &mut ValueStack) -> Result + where + Self: Sized; + fn stack_peek(stack: &ValueStack) -> Result + where + Self: Sized; + + fn local_get(locals: &Locals, index: u32) -> Result + where + Self: Sized; + + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()>; +} + +impl InternalValue for i32 { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_32.push(value as u32); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_32.pop().ok_or(Error::ValueStackUnderflow).map(|v| v as i32) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_32.last().ok_or(Error::ValueStackUnderflow).map(|v| *v as i32) + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(locals.locals_32[index as usize] as i32) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_32[index as usize] = value as u32; + Ok(()) + } +} + +impl InternalValue for f32 { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_32.push(value.to_bits()); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_32.pop().ok_or(Error::ValueStackUnderflow).map(f32::from_bits) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_32.last().ok_or(Error::ValueStackUnderflow).map(|v| f32::from_bits(*v)) + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(f32::from_bits(locals.locals_32[index as usize])) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_32[index as usize] = value.to_bits(); + Ok(()) + } +} + +impl InternalValue for i64 { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_64.push(value as u64); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_64.pop().ok_or(Error::ValueStackUnderflow).map(|v| v as i64) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_64.last().ok_or(Error::ValueStackUnderflow).map(|v| *v as i64) + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(locals.locals_64[index as usize] as i64) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_64[index as usize] = value as u64; + Ok(()) + } +} + +impl InternalValue for u64 { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_64.push(value); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_64.pop().ok_or(Error::ValueStackUnderflow) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_64.last().ok_or(Error::ValueStackUnderflow).copied() + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(locals.locals_64[index as usize]) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_64[index as usize] = value; + Ok(()) + } +} + +impl InternalValue for f64 { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_64.push(value.to_bits()); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_64.pop().ok_or(Error::ValueStackUnderflow).map(f64::from_bits) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_64.last().ok_or(Error::ValueStackUnderflow).map(|v| f64::from_bits(*v)) + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(f64::from_bits(locals.locals_64[index as usize])) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_64[index as usize] = value.to_bits(); + Ok(()) + } +} + +impl InternalValue for u32 { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_32.push(value); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_32.pop().ok_or(Error::ValueStackUnderflow) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_32.last().ok_or(Error::ValueStackUnderflow).copied() + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(locals.locals_32[index as usize]) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_32[index as usize] = value; + Ok(()) + } +} + +impl InternalValue for Value128 { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_128.push(value); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_128.pop().ok_or(Error::ValueStackUnderflow) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_128.last().ok_or(Error::ValueStackUnderflow).copied() + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(locals.locals_128[index as usize]) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_128[index as usize] = value; + Ok(()) + } +} + +impl InternalValue for ValueRef { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.stack_ref.push(value); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result { + stack.stack_ref.pop().ok_or(Error::ValueStackUnderflow) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result { + stack.stack_ref.last().ok_or(Error::ValueStackUnderflow).copied() + } + #[inline] + fn local_get(locals: &Locals, index: u32) -> Result { + Ok(locals.locals_ref[index as usize]) + } + #[inline] + fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { + locals.locals_ref[index as usize] = value; + Ok(()) + } +} diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs index 6cc778c..a18e43d 100644 --- a/crates/tinywasm/src/store/global.rs +++ b/crates/tinywasm/src/store/global.rs @@ -3,20 +3,20 @@ use core::cell::Cell; use alloc::{format, string::ToString}; use tinywasm_types::*; -use crate::{runtime::RawWasmValue, unlikely, Error, Result}; +use crate::{runtime::TinyWasmValue, unlikely, Error, Result}; /// A WebAssembly Global Instance /// /// See #[derive(Debug)] pub(crate) struct GlobalInstance { - pub(crate) value: Cell, + pub(crate) value: Cell, pub(crate) ty: GlobalType, pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances } impl GlobalInstance { - pub(crate) fn new(ty: GlobalType, value: RawWasmValue, owner: ModuleInstanceAddr) -> Self { + pub(crate) fn new(ty: GlobalType, value: TinyWasmValue, owner: ModuleInstanceAddr) -> Self { Self { ty, value: value.into(), _owner: owner } } @@ -50,7 +50,7 @@ mod tests { #[test] fn test_global_instance_get_set() { let global_type = GlobalType { ty: ValType::I32, mutable: true }; - let initial_value = RawWasmValue::from(10i32); + let initial_value = TinyWasmValue::from(10i32); let owner = 0; let mut global_instance = GlobalInstance::new(global_type, initial_value, owner); diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index f72dcab..9ebd82e 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; -use crate::runtime::{self, InterpreterRuntime, RawWasmValue}; +use crate::runtime::{self, InterpreterRuntime, TinyWasmValue}; use crate::{Error, Function, ModuleInstance, Result, Trap}; mod data; @@ -160,8 +160,8 @@ impl Store { } /// Get the global at the actual index in the store - #[inline(always)] - pub fn get_global_val(&self, addr: MemAddr) -> Result { + #[doc(hidden)] + pub fn get_global_val(&self, addr: MemAddr) -> Result { self.data .globals .get(addr as usize) @@ -170,8 +170,8 @@ impl Store { } /// Set the global at the actual index in the store - #[inline(always)] - pub(crate) fn set_global_val(&mut self, addr: MemAddr, value: RawWasmValue) -> Result<()> { + #[doc(hidden)] + pub fn set_global_val(&mut self, addr: MemAddr, value: TinyWasmValue) -> Result<()> { let global = self.data.globals.get(addr as usize).ok_or_else(|| Self::not_found_error("global")); global.map(|global| global.value.set(value)) } @@ -251,13 +251,7 @@ impl Store { let addr = globals.get(*addr as usize).copied().ok_or_else(|| { Error::Other(format!("global {} not found. This should have been caught by the validator", addr)) })?; - let val: i64 = self.data.globals[addr as usize].value.get().into(); - - // check if the global is actually a null reference - match val < 0 { - true => None, - false => Some(val as u32), - } + self.data.globals[addr as usize].value.get().unwrap_ref() } _ => return Err(Error::UnsupportedFeature(format!("const expression other than ref: {:?}", item))), }; @@ -368,7 +362,7 @@ impl Store { Ok((data_addrs.into_boxed_slice(), None)) } - pub(crate) fn add_global(&mut self, ty: GlobalType, value: RawWasmValue, idx: ModuleInstanceAddr) -> Result { + pub(crate) fn add_global(&mut self, ty: GlobalType, value: TinyWasmValue, idx: ModuleInstanceAddr) -> Result { self.data.globals.push(GlobalInstance::new(ty, value, idx)); Ok(self.data.globals.len() as Addr - 1) } @@ -396,7 +390,7 @@ impl Store { use tinywasm_types::ConstInstruction::*; let val = match const_instr { I32Const(i) => *i, - GlobalGet(addr) => i32::from(self.data.globals[*addr as usize].value.get()), + GlobalGet(addr) => self.data.globals[*addr as usize].value.get().unwrap_32() as i32, _ => return Err(Error::Other("expected i32".to_string())), }; Ok(val) @@ -408,13 +402,13 @@ impl Store { const_instr: &tinywasm_types::ConstInstruction, module_global_addrs: &[Addr], module_func_addrs: &[FuncAddr], - ) -> Result { + ) -> Result { use tinywasm_types::ConstInstruction::*; let val = match const_instr { - F32Const(f) => RawWasmValue::from(*f), - F64Const(f) => RawWasmValue::from(*f), - I32Const(i) => RawWasmValue::from(*i), - I64Const(i) => RawWasmValue::from(*i), + F32Const(f) => (*f).into(), + F64Const(f) => (*f).into(), + I32Const(i) => (*i).into(), + I64Const(i) => (*i).into(), GlobalGet(addr) => { let addr = module_global_addrs.get(*addr as usize).ok_or_else(|| { Error::Other(format!("global {} not found. This should have been caught by the validator", addr)) @@ -424,10 +418,10 @@ impl Store { self.data.globals.get(*addr as usize).expect("global not found. This should be unreachable"); global.value.get() } - RefNull(t) => RawWasmValue::from(t.default_value()), - RefFunc(idx) => RawWasmValue::from(*module_func_addrs.get(*idx as usize).ok_or_else(|| { + RefNull(t) => t.default_value().into(), + RefFunc(idx) => TinyWasmValue::ValueRef(Some(*module_func_addrs.get(*idx as usize).ok_or_else(|| { Error::Other(format!("function {} not found. This should have been caught by the validator", idx)) - })?), + })?)), }; Ok(val) } diff --git a/crates/tinywasm/tests/generated/2.0.csv b/crates/tinywasm/tests/generated/2.0.csv index 3ad4978..353c879 100644 --- a/crates/tinywasm/tests/generated/2.0.csv +++ b/crates/tinywasm/tests/generated/2.0.csv @@ -3,4 +3,4 @@ 0.4.1,27551,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.5.0,27551,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.6.1,27572,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] -0.7.0,27871,48,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":104,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":112,"failed":5},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref_func.wast","passed":17,"failed":0},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":50,"failed":0},{"name":"table_init.wast","passed":737,"failed":43},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.7.0,27856,63,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":104,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":112,"failed":5},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":88,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"obsolete-keywords.wast","passed":11,"failed":0},{"name":"ref_func.wast","passed":6,"failed":11},{"name":"ref_is_null.wast","passed":16,"failed":0},{"name":"ref_null.wast","passed":3,"failed":0},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1728,"failed":0},{"name":"table_fill.wast","passed":45,"failed":0},{"name":"table_get.wast","passed":16,"failed":0},{"name":"table_grow.wast","passed":46,"failed":4},{"name":"table_init.wast","passed":737,"failed":43},{"name":"table_set.wast","passed":26,"failed":0},{"name":"table_size.wast","passed":39,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index d67ba83..d19326d 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -87,21 +87,20 @@ pub enum ConstInstruction { #[rustfmt::skip] pub enum Instruction { // > Custom Instructions - BrLabel(LabelAddr), - // LocalGet + I32Const + I32Add - I32LocalGetConstAdd(LocalAddr, i32), - // LocalGet + I32Const + I32Store - I32ConstStoreLocal { local: LocalAddr, const_i32: i32, offset: u32, mem_addr: u8 }, - // LocalGet + LocalGet + I32Store - I32StoreLocal { local_a: LocalAddr, local_b: LocalAddr, offset: u32, mem_addr: u8 }, - // I64Xor + I64Const + I64RotL - // Commonly used by a few crypto libraries - I64XorConstRotl(i64), - // LocalTee + LocalGet - LocalTeeGet(LocalAddr, LocalAddr), - LocalGet2(LocalAddr, LocalAddr), - LocalGet3(LocalAddr, LocalAddr, LocalAddr), - LocalGetSet(LocalAddr, LocalAddr), + // // LocalGet + I32Const + I32Add + // I32LocalGetConstAdd(LocalAddr, i32), + // // LocalGet + I32Const + I32Store + // I32ConstStoreLocal { local: LocalAddr, const_i32: i32, offset: u32, mem_addr: u8 }, + // // LocalGet + LocalGet + I32Store + // I32StoreLocal { local_a: LocalAddr, local_b: LocalAddr, offset: u32, mem_addr: u8 }, + // // I64Xor + I64Const + I64RotL + // // Commonly used by a few crypto libraries + // I64XorConstRotl(i64), + // // LocalTee + LocalGet + // LocalTeeGet(LocalAddr, LocalAddr), + // LocalGet2(LocalAddr, LocalAddr), + // LocalGet3(LocalAddr, LocalAddr, LocalAddr), + // LocalGetSet(LocalAddr, LocalAddr), // > Control Instructions // See @@ -115,22 +114,45 @@ pub enum Instruction { Br(LabelAddr), BrIf(LabelAddr), BrTable(BrTableDefault, BrTableLen), // has to be followed by multiple BrLabel instructions + BrLabel(LabelAddr), Return, Call(FuncAddr), CallIndirect(TypeAddr, TableAddr), - + // > Parametric Instructions // See - Drop, - Select(Option), + Drop32, + Drop64, + Drop128, + DropRef, + + Select32, + Select64, + Select128, + SelectRef, // > Variable Instructions // See - LocalGet(LocalAddr), - LocalSet(LocalAddr), - LocalTee(LocalAddr), + LocalGet32(LocalAddr), + LocalGet64(LocalAddr), + LocalGet128(LocalAddr), + LocalGetRef(LocalAddr), + + LocalSet32(LocalAddr), + LocalSet64(LocalAddr), + LocalSet128(LocalAddr), + LocalSetRef(LocalAddr), + + LocalTee32(LocalAddr), + LocalTee64(LocalAddr), + LocalTee128(LocalAddr), + LocalTeeRef(LocalAddr), + GlobalGet(GlobalAddr), - GlobalSet(GlobalAddr), + GlobalSet32(GlobalAddr), + GlobalSet64(GlobalAddr), + GlobalSet128(GlobalAddr), + GlobalSetRef(GlobalAddr), // > Memory Instructions I32Load { offset: u64, mem_addr: MemAddr }, diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index d447efb..785b13b 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -172,11 +172,20 @@ pub struct FuncType { pub results: Box<[ValType]>, } +#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] +pub struct LocalCounts { + pub local_32: u32, + pub local_64: u32, + pub local_128: u32, + pub local_ref: u32, +} + #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct WasmFunction { pub instructions: Box<[Instruction]>, - pub locals: Box<[ValType]>, + pub locals: LocalCounts, pub ty: FuncType, } -- cgit v1.3.1