summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md9
-rw-r--r--Cargo.lock28
-rw-r--r--crates/cli/Cargo.toml2
-rw-r--r--crates/parser/Cargo.toml2
-rw-r--r--crates/parser/src/conversion.rs48
-rw-r--r--crates/parser/src/lib.rs1
-rw-r--r--crates/parser/src/module.rs11
-rw-r--r--crates/parser/src/visit.rs217
-rw-r--r--crates/tinywasm/Cargo.toml12
-rw-r--r--crates/tinywasm/src/boxvec.rs148
-rw-r--r--crates/tinywasm/src/func.rs13
-rw-r--r--crates/tinywasm/src/lib.rs1
-rw-r--r--crates/tinywasm/src/runtime/interpreter/macros.rs131
-rw-r--r--crates/tinywasm/src/runtime/interpreter/mod.rs736
-rw-r--r--crates/tinywasm/src/runtime/interpreter/num_helpers.rs (renamed from crates/tinywasm/src/runtime/interpreter/traits.rs)44
-rw-r--r--crates/tinywasm/src/runtime/mod.rs16
-rw-r--r--crates/tinywasm/src/runtime/raw.rs123
-rw-r--r--crates/tinywasm/src/runtime/raw_simd.rs27
-rw-r--r--crates/tinywasm/src/runtime/stack/block_stack.rs15
-rw-r--r--crates/tinywasm/src/runtime/stack/call_stack.rs81
-rw-r--r--crates/tinywasm/src/runtime/stack/mod.rs3
-rw-r--r--crates/tinywasm/src/runtime/stack/value_stack.rs291
-rw-r--r--crates/tinywasm/src/runtime/stack/values.rs420
-rw-r--r--crates/tinywasm/src/store/global.rs8
-rw-r--r--crates/tinywasm/src/store/mod.rs38
-rw-r--r--crates/tinywasm/tests/generated/2.0.csv2
-rw-r--r--crates/types/src/instructions.rs66
-rw-r--r--crates/types/src/lib.rs11
28 files changed, 1286 insertions, 1218 deletions
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<Item = wasmparser::Result<wasmparser::Element<'a>>>>(
elements: T,
@@ -168,27 +168,45 @@ pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result<Ex
pub(crate) fn convert_module_code(
func: wasmparser::FunctionBody<'_>,
- validator: &mut FuncValidator<ValidatorResources>,
-) -> Result<Code> {
+ mut validator: FuncValidator<ValidatorResources>,
+) -> 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<FuncType> {
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<R>);
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<R: WasmModuleResources>(
- validator: &mut FuncValidator<R>,
+ validator: FuncValidator<R>,
body: FunctionBody<'_>,
-) -> Result<Box<[Instruction]>> {
+ local_addr_map: Vec<u32>,
+) -> 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<R: WasmModuleResources> {
+ validator: FuncValidator<R>,
instructions: Vec<Instruction>,
label_ptrs: Vec<usize>,
+ local_addr_map: Vec<u32>,
errors: Vec<crate::ParseError>,
}
-impl FunctionBuilder {
- pub(crate) fn new(instr_capacity: usize) -> Self {
+impl<R: WasmModuleResources> FunctionBuilder<R> {
+ 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<R: WasmModuleResources> FunctionBuilder<R> {
+ pub(crate) fn new(instr_capacity: usize, validator: FuncValidator<R>, local_addr_map: Vec<u32>) -> 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<R> {
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::<Result<Vec<Instruction>, 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<T> {
- pub(crate) data: Box<[T]>,
- pub(crate) end: usize,
-}
-
-impl<T: Copy + Default> BoxVec<T> {
- #[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<T> {
- 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<Item = T>) {
- 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<usize>) -> 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<T> core::ops::Index<usize> for BoxVec<T> {
- type Output = T;
-
- #[inline(always)]
- fn index(&self, index: usize) -> &T {
- &self.data[index]
- }
-}
-
-impl<T> core::ops::Index<core::ops::Range<usize>> for BoxVec<T> {
- type Output = [T];
-
- #[inline(always)]
- fn index(&self, index: core::ops::Range<usize>) -> &[T] {
- &self.data[index]
- }
-}
-
-impl<T> core::ops::IndexMut<usize> for BoxVec<T> {
- #[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::<Vec<_>>();
- 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::<Value32>()?,
+ Drop64 => self.stack.values.drop::<Value64>()?,
+ Drop128 => self.stack.values.drop::<Value128>()?,
+ DropRef => self.stack.values.drop::<ValueRef>()?,
+
+ Select32 => self.stack.values.select::<Value32>()?,
+ Select64 => self.stack.values.select::<Value64>()?,
+ Select128 => self.stack.values.select::<Value128>()?,
+ SelectRef => self.stack.values.select::<ValueRef>()?,
+
+ 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::<Value32>(*local_index).map(|v| self.stack.values.push(v))?
+ }
+ LocalGet64(local_index) => {
+ self.cf.locals.get::<Value64>(*local_index).map(|v| self.stack.values.push(v))?
+ }
+ LocalGet128(local_index) => {
+ self.cf.locals.get::<Value128>(*local_index).map(|v| self.stack.values.push(v))?
+ }
+ LocalGetRef(local_index) => {
+ self.cf.locals.get::<ValueRef>(*local_index).map(|v| self.stack.values.push(v))?
+ }
+
+ LocalSet32(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value32>()?)?,
+ LocalSet64(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value64>()?)?,
+ LocalSet128(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value128>()?)?,
+ LocalSetRef(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<ValueRef>()?)?,
+
+ LocalTee32(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<Value32>()?)?,
+ LocalTee64(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<Value64>()?)?,
+ LocalTee128(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<Value128>()?)?,
+ LocalTeeRef(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<ValueRef>()?)?,
+
GlobalGet(global_index) => self.exec_global_get(*global_index)?,
- GlobalSet(global_index) => self.exec_global_set(*global_index)?,
+ GlobalSet32(global_index) => self.exec_global_set::<Value32>(*global_index)?,
+ GlobalSet64(global_index) => self.exec_global_set::<Value64>(*global_index)?,
+ GlobalSet128(global_index) => self.exec_global_set::<Value128>(*global_index)?,
+ GlobalSetRef(global_index) => self.exec_global_set::<ValueRef>(*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),
+ I32Const(val) => self.stack.values.push(*val),
+ I64Const(val) => self.stack.values.push(*val),
+ F32Const(val) => self.stack.values.push::<i32>(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::<i32, 4>(*mem_addr, *offset)?,
- I64Store { mem_addr, offset } => self.exec_mem_store::<i64, 8>(*mem_addr, *offset)?,
- F32Store { mem_addr, offset } => self.exec_mem_store::<f32, 4>(*mem_addr, *offset)?,
- F64Store { mem_addr, offset } => self.exec_mem_store::<f64, 8>(*mem_addr, *offset)?,
- I32Store8 { mem_addr, offset } => self.exec_mem_store::<i8, 1>(*mem_addr, *offset)?,
- I32Store16 { mem_addr, offset } => self.exec_mem_store::<i16, 2>(*mem_addr, *offset)?,
- I64Store8 { mem_addr, offset } => self.exec_mem_store::<i8, 1>(*mem_addr, *offset)?,
- I64Store16 { mem_addr, offset } => self.exec_mem_store::<i16, 2>(*mem_addr, *offset)?,
- I64Store32 { mem_addr, offset } => self.exec_mem_store::<i32, 4>(*mem_addr, *offset)?,
+ I32Store { mem_addr, offset } => {
+ let v = self.stack.values.pop::<i32>()?;
+ self.exec_mem_store::<i32, 4>(v, *mem_addr, *offset)?
+ }
+ I64Store { mem_addr, offset } => {
+ let v = self.stack.values.pop::<i64>()?;
+ self.exec_mem_store::<i64, 8>(v, *mem_addr, *offset)?
+ }
+ F32Store { mem_addr, offset } => {
+ let v = self.stack.values.pop::<f32>()?;
+ self.exec_mem_store::<f32, 4>(v, *mem_addr, *offset)?
+ }
+ F64Store { mem_addr, offset } => {
+ let v = self.stack.values.pop::<f64>()?;
+ self.exec_mem_store::<f64, 8>(v, *mem_addr, *offset)?
+ }
+ I32Store8 { mem_addr, offset } => {
+ let v = self.stack.values.pop::<i32>()? as i8;
+ self.exec_mem_store::<i8, 1>(v, *mem_addr, *offset)?
+ }
+ I32Store16 { mem_addr, offset } => {
+ let v = self.stack.values.pop::<i32>()? as i16;
+ self.exec_mem_store::<i16, 2>(v, *mem_addr, *offset)?
+ }
+ I64Store8 { mem_addr, offset } => {
+ let v = self.stack.values.pop::<i64>()? as i8;
+ self.exec_mem_store::<i8, 1>(v, *mem_addr, *offset)?
+ }
+ I64Store16 { mem_addr, offset } => {
+ let v = self.stack.values.pop::<i64>()? as i16;
+ self.exec_mem_store::<i16, 2>(v, *mem_addr, *offset)?
+ }
+ I64Store32 { mem_addr, offset } => {
+ let v = self.stack.values.pop::<i64>()? as i32;
+ self.exec_mem_store::<i32, 4>(v, *mem_addr, *offset)?
+ }
I32Load { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(|v| v, *mem_addr, *offset)?,
I64Load { mem_addr, offset } => self.exec_mem_load::<i64, 8, _>(|v| v, *mem_addr, *offset)?,
@@ -122,145 +179,184 @@ impl<'store, 'stack> Executor<'store, 'stack> {
I64Load32S { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(|v| v as i64, *mem_addr, *offset)?,
I64Load32U { mem_addr, offset } => self.exec_mem_load::<u32, 4, _>(|v| v as i64, *mem_addr, *offset)?,
- I64Eqz => comp_zero!(==, i64, self),
- I32Eqz => comp_zero!(==, i32, self),
+ I64Eqz => self.stack.values.replace_top::<i64, _>(|v| Ok((v == 0) as i32))?,
+ I32Eqz => self.stack.values.replace_top::<i32, _>(|v| Ok((v == 0) as i32))?,
+ I32Eq => self.stack.values.calculate::<i32, _>(|a, b| Ok((a == b) as i32))?,
+ I64Eq => self.stack.values.calculate::<i64, _>(|a, b| Ok((a == b) as i32))?,
+ F32Eq => self.stack.values.calculate::<f32, _>(|a, b| Ok((a == b) as i32))?,
+ F64Eq => self.stack.values.calculate::<f64, _>(|a, b| Ok((a == b) as i32))?,
- I32Eq => comp!(==, i32, self),
- I64Eq => comp!(==, i64, self),
- F32Eq => comp!(==, f32, self),
- F64Eq => comp!(==, f64, self),
+ I32Ne => self.stack.values.calculate::<i32, _>(|a, b| Ok((a != b) as i32))?,
+ I64Ne => self.stack.values.calculate::<i64, _>(|a, b| Ok((a != b) as i32))?,
+ F32Ne => self.stack.values.calculate::<f32, _>(|a, b| Ok((a != b) as i32))?,
+ F64Ne => self.stack.values.calculate::<f64, _>(|a, b| Ok((a != b) as i32))?,
- I32Ne => comp!(!=, i32, self),
- I64Ne => comp!(!=, i64, self),
- F32Ne => comp!(!=, f32, self),
- F64Ne => comp!(!=, f64, self),
+ I32LtS => self.stack.values.calculate::<i32, _>(|a, b| Ok((a < b) as i32))?,
+ I64LtS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a < b) as i32))?,
+ I32LtU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a < b) as i32))?,
+ I64LtU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a < b) as i32))?,
+ F32Lt => self.stack.values.calculate::<f32, _>(|a, b| Ok((a < b) as i32))?,
+ F64Lt => self.stack.values.calculate::<f64, _>(|a, b| Ok((a < b) as i32))?,
- 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 => self.stack.values.calculate::<i32, _>(|a, b| Ok((a <= b) as i32))?,
+ I64LeS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a <= b) as i32))?,
+ I32LeU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a <= b) as i32))?,
+ I64LeU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a <= b) as i32))?,
+ F32Le => self.stack.values.calculate::<f32, _>(|a, b| Ok((a <= b) as i32))?,
+ F64Le => self.stack.values.calculate::<f64, _>(|a, b| Ok((a <= b) as i32))?,
- 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 => self.stack.values.calculate::<i32, _>(|a, b| Ok((a >= b) as i32))?,
+ I64GeS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a >= b) as i32))?,
+ I32GeU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a >= b) as i32))?,
+ I64GeU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a >= b) as i32))?,
+ F32Ge => self.stack.values.calculate::<f32, _>(|a, b| Ok((a >= b) as i32))?,
+ F64Ge => self.stack.values.calculate::<f64, _>(|a, b| Ok((a >= b) as i32))?,
- 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 => self.stack.values.calculate::<i32, _>(|a, b| Ok((a > b) as i32))?,
+ I64GtS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a > b) as i32))?,
+ I32GtU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a > b) as i32))?,
+ I64GtU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a > b) as i32))?,
+ F32Gt => self.stack.values.calculate::<f32, _>(|a, b| Ok((a > b) as i32))?,
+ F64Gt => self.stack.values.calculate::<f64, _>(|a, b| Ok((a > b) as i32))?,
- I32GtS => comp!(>, i32, self),
- I64GtS => comp!(>, i64, self),
- I32GtU => comp!(>, u32, self),
- I64GtU => comp!(>, u64, self),
- F32Gt => comp!(>, f32, self),
- F64Gt => comp!(>, f64, self),
+ I32Add => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wrapping_add(b)))?,
+ I64Add => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wrapping_add(b)))?,
+ F32Add => self.stack.values.calculate::<f32, _>(|a, b| Ok(a + b))?,
+ F64Add => self.stack.values.calculate::<f64, _>(|a, b| Ok(a + b))?,
- I64Add => arithmetic!(wrapping_add, i64, self),
- I32Add => arithmetic!(wrapping_add, i32, self),
- F32Add => arithmetic!(+, f32, self),
- F64Add => arithmetic!(+, f64, self),
+ I32Sub => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wrapping_sub(b)))?,
+ I64Sub => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wrapping_sub(b)))?,
+ F32Sub => self.stack.values.calculate::<f32, _>(|a, b| Ok(a - b))?,
+ F64Sub => self.stack.values.calculate::<f64, _>(|a, b| Ok(a - b))?,
- I32Sub => arithmetic!(wrapping_sub, i32, self),
- I64Sub => arithmetic!(wrapping_sub, i64, self),
- F32Sub => arithmetic!(-, f32, self),
- F64Sub => arithmetic!(-, f64, self),
+ F32Div => self.stack.values.calculate::<f32, _>(|a, b| Ok(a / b))?,
+ F64Div => self.stack.values.calculate::<f64, _>(|a, b| Ok(a / b))?,
- 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),
+ I32Mul => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wrapping_mul(b)))?,
+ I64Mul => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wrapping_mul(b)))?,
+ F32Mul => self.stack.values.calculate::<f32, _>(|a, b| Ok(a * b))?,
+ F64Mul => self.stack.values.calculate::<f64, _>(|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),
+ I32DivS => self.stack.values.calculate::<i32, _>(|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::<i64, _>(|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::<u32, _>(|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::<u64, _>(|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::<i32, _>(|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::<i64, _>(|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::<u32, _>(|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::<u64, _>(|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))
+ })?,
- 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 => self.stack.values.calculate::<i32, _>(|a, b| Ok(a & b))?,
+ I64And => self.stack.values.calculate::<i64, _>(|a, b| Ok(a & b))?,
+ I32Or => self.stack.values.calculate::<i32, _>(|a, b| Ok(a | b))?,
+ I64Or => self.stack.values.calculate::<i64, _>(|a, b| Ok(a | b))?,
+ I32Xor => self.stack.values.calculate::<i32, _>(|a, b| Ok(a ^ b))?,
+ I64Xor => self.stack.values.calculate::<i64, _>(|a, b| Ok(a ^ b))?,
+ I32Shl => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_shl(b)))?,
+ I64Shl => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_shl(b)))?,
+ I32ShrS => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_shr(b)))?,
+ I64ShrS => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_shr(b)))?,
+ I32ShrU => self.stack.values.calculate::<u32, _>(|a, b| Ok(a.wasm_shr(b)))?,
+ I64ShrU => self.stack.values.calculate::<u64, _>(|a, b| Ok(a.wasm_shr(b)))?,
+ I32Rotl => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_rotl(b)))?,
+ I64Rotl => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_rotl(b)))?,
+ I32Rotr => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_rotr(b)))?,
+ I64Rotr => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_rotr(b)))?,
- 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 => self.stack.values.replace_top::<i32, _>(|v| Ok(v.leading_zeros() as i32))?,
+ I64Clz => self.stack.values.replace_top::<i64, _>(|v| Ok(v.leading_zeros() as i64))?,
+ I32Ctz => self.stack.values.replace_top::<i32, _>(|v| Ok(v.trailing_zeros() as i32))?,
+ I64Ctz => self.stack.values.replace_top::<i64, _>(|v| Ok(v.trailing_zeros() as i64))?,
+ I32Popcnt => self.stack.values.replace_top::<i32, _>(|v| Ok(v.count_ones() as i32))?,
+ I64Popcnt => self.stack.values.replace_top::<i64, _>(|v| Ok(v.count_ones() as i64))?,
- 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 => self.stack.values.replace_top::<i32, _>(|v| Ok(v as f32))?,
+ F32ConvertI64S => self.stack.values.replace_top::<i64, _>(|v| Ok(v as f32))?,
+ F64ConvertI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as f64))?,
+ F64ConvertI64S => self.stack.values.replace_top::<i64, _>(|v| Ok(v as f64))?,
+ F32ConvertI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as f32))?,
+ F32ConvertI64U => self.stack.values.replace_top::<u64, _>(|v| Ok(v as f32))?,
+ F64ConvertI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as f64))?,
+ F64ConvertI64U => self.stack.values.replace_top::<u64, _>(|v| Ok(v as f64))?,
- 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),
+ I32Extend8S => self.stack.values.replace_top::<i32, _>(|v| Ok((v as i8) as i32))?,
+ I32Extend16S => self.stack.values.replace_top::<i32, _>(|v| Ok((v as i16) as i32))?,
+ I64Extend8S => self.stack.values.replace_top::<i64, _>(|v| Ok((v as i8) as i64))?,
+ I64Extend16S => self.stack.values.replace_top::<i64, _>(|v| Ok((v as i16) as i64))?,
+ I64Extend32S => self.stack.values.replace_top::<i64, _>(|v| Ok((v as i32) as i64))?,
+ I64ExtendI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as i64))?,
+ I64ExtendI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as i64))?,
+ I32WrapI64 => self.stack.values.replace_top::<i64, _>(|v| Ok(v as i32))?,
- F32DemoteF64 => conv!(f64, f32, self),
- F64PromoteF32 => conv!(f32, f64, self),
+ F32DemoteF64 => self.stack.values.replace_top::<f64, _>(|v| Ok(v as f32))?,
+ F64PromoteF32 => self.stack.values.replace_top::<f32, _>(|v| Ok(v as f64))?,
- 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),
+ F32Abs => self.stack.values.replace_top::<f32, _>(|v| Ok(v.abs()))?,
+ F64Abs => self.stack.values.replace_top::<f64, _>(|v| Ok(v.abs()))?,
+ F32Neg => self.stack.values.replace_top::<f32, _>(|v| Ok(-v))?,
+ F64Neg => self.stack.values.replace_top::<f64, _>(|v| Ok(-v))?,
+ F32Ceil => self.stack.values.replace_top::<f32, _>(|v| Ok(v.ceil()))?,
+ F64Ceil => self.stack.values.replace_top::<f64, _>(|v| Ok(v.ceil()))?,
+ F32Floor => self.stack.values.replace_top::<f32, _>(|v| Ok(v.floor()))?,
+ F64Floor => self.stack.values.replace_top::<f64, _>(|v| Ok(v.floor()))?,
+ F32Trunc => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc()))?,
+ F64Trunc => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc()))?,
+ F32Nearest => self.stack.values.replace_top::<f32, _>(|v| Ok(v.tw_nearest()))?,
+ F64Nearest => self.stack.values.replace_top::<f64, _>(|v| Ok(v.tw_nearest()))?,
+ F32Sqrt => self.stack.values.replace_top::<f32, _>(|v| Ok(v.sqrt()))?,
+ F64Sqrt => self.stack.values.replace_top::<f64, _>(|v| Ok(v.sqrt()))?,
+ F32Min => self.stack.values.calculate::<f32, _>(|a, b| Ok(a.tw_minimum(b)))?,
+ F64Min => self.stack.values.calculate::<f64, _>(|a, b| Ok(a.tw_minimum(b)))?,
+ F32Max => self.stack.values.calculate::<f32, _>(|a, b| Ok(a.tw_maximum(b)))?,
+ F64Max => self.stack.values.calculate::<f64, _>(|a, b| Ok(a.tw_maximum(b)))?,
+ F32Copysign => self.stack.values.calculate::<f32, _>(|a, b| Ok(a.copysign(b)))?,
+ F64Copysign => self.stack.values.calculate::<f64, _>(|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::<f32, _>(|v| Ok(v.trunc() as i32))?,
+ I32TruncSatF32U => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as u32))?,
+ I32TruncSatF64S => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as i32))?,
+ I32TruncSatF64U => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as u32))?,
+ I64TruncSatF32S => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as i64))?,
+ I64TruncSatF32U => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as u64))?,
+ I64TruncSatF64S => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as i64))?,
+ I64TruncSatF64U => self.stack.values.replace_top::<f64, _>(|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<WasmFunction>, 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<WasmFunction>, owner: ModuleInstanceAddr) -> Result<ControlFlow<()>> {
+ 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<ControlFlow<()>> {
let func_inst = self.store.get_func(self.module.resolve_func_addr(v)?)?;
let wasm_func = match &func_inst.func {
crate::Function::Wasm(wasm_func) => wasm_func,
@@ -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() }, &params)?;
- 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<ControlFlow<()>> {
// verify that the table is of the right type, this should be validated by the parser already
let func_ref = {
let table = self.store.get_table(self.module.resolve_table_addr(table_addr)?)?;
- let table_idx: u32 = self.stack.values.pop()?.into();
+ let table_idx: u32 = self.stack.values.pop::<i32>()? 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() }, &params)?;
- 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::<i32>()? != 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<ControlFlow<()>> {
- 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<ControlFlow<()>> {
- let val: i32 = self.stack.values.pop()?.into();
- if val != 0 {
- break_to!(to, self);
+ if self.stack.values.pop::<i32>()? != 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::<i32>()?;
+ 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<RawWasmValue>) {
- self.stack.values.push(val.into());
+ fn exec_global_set<T: InternalValue>(&mut self, global_index: u32) -> Result<()>
+ where
+ TinyWasmValue: From<T>,
+ {
+ self.store.set_global_val(self.module.resolve_global_addr(global_index)?, self.stack.values.pop::<T>()?.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::<ValueRef>()?.is_none() as i32;
+ self.stack.values.push::<i32>(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::<i32>(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::<i32>()?;
+ self.stack.values.push::<i32>(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::<i32>()?;
+ let src = self.stack.values.pop::<i32>()?;
+ let dst = self.stack.values.pop::<i32>()?;
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::<i32>()?;
+ let val = self.stack.values.pop::<i32>()?;
+ let dst = self.stack.values.pop::<i32>()?;
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::<i32>()?; // n
+ let offset = self.stack.values.pop::<i32>()?; // s
+ let dst = self.stack.values.pop::<i32>()?; // 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::<i32>()?;
+ let src: i32 = self.stack.values.pop::<i32>()?;
+ let dst: i32 = self.stack.values.pop::<i32>()?;
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<LOAD: MemLoadable<LOAD_SIZE>, const LOAD_SIZE: usize, TARGET: Into<RawWasmValue>>(
+ fn exec_mem_load<LOAD: MemLoadable<LOAD_SIZE>, 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::<i32>()? 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::<LOAD_SIZE, LOAD>(addr)?;
- self.stack.values.push(cast(val).into());
+ self.stack.values.push(cast(val));
Ok(())
}
- fn exec_mem_store<T: From<RawWasmValue> + MemStorable<N>, const N: usize>(
+ fn exec_mem_store<T: MemStorable<N>, 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::<i32>()? 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::<i32>()?;
+ 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::<ValueRef>()?;
+ let idx = self.stack.values.pop::<i32>()? 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::<i32>()?; // n
+ let offset: i32 = self.stack.values.pop::<i32>()?; // s
+ let dst: i32 = self.stack.values.pop::<i32>()?; // 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::<i32>()?;
+ let val = self.stack.values.pop::<ValueRef>()?;
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::<i32>()?;
+ let val = self.stack.values.pop::<ValueRef>()?;
+ let i = self.stack.values.pop::<i32>()?;
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/traits.rs b/crates/tinywasm/src/runtime/interpreter/num_helpers.rs
index 7aeb3b7..d0402bc 100644
--- a/crates/tinywasm/src/runtime/interpreter/traits.rs
+++ b/crates/tinywasm/src/runtime/interpreter/num_helpers.rs
@@ -5,6 +5,50 @@ where
fn checked_wrapping_rem(self, rhs: Self) -> Option<Self>;
}
+/// 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;
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<u32> {
- match i64::from(*self) {
- v if v < 0 => None,
- addr => Some(addr as u32),
- }
- }
-}
-
-impl From<WasmValue> 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<RawWasmValue> for $type
- impl From<RawWasmValue> 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<u8, 16>); // 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<u128> for RawSimdWasmValue {
- fn from(value: u128) -> Self {
- Self(value.to_le_bytes().into())
- }
-}
-
-impl From<RawSimdWasmValue> 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<BlockFrame>);
@@ -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<WasmFunction>,
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<T: InternalValue>(&self, local_index: LocalAddr) -> Result<T> {
+ T::local_get(self, local_index)
+ }
+
+ pub(crate) fn set<T: InternalValue>(&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<WasmFunction>,
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<WasmFunction>,
+ owner: ModuleInstanceAddr,
+ params: impl ExactSizeIterator<Item = TinyWasmValue>,
+ 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();
- #[inline(always)]
- pub(crate) fn get_local(&self, local_index: LocalAddr) -> RawWasmValue {
- self.locals[local_index as usize]
+ 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),
+ }
+ }
+
+ 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,224 +1,161 @@
-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<RawWasmValue>,
-
- #[cfg(feature = "simd")]
- simd_stack: BoxVec<RawSimdWasmValue>,
+ pub(crate) stack_32: Vec<Value32>,
+ pub(crate) stack_64: Vec<Value64>,
+ pub(crate) stack_128: Vec<Value128>,
+ pub(crate) stack_ref: Vec<ValueRef>,
}
-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<RawWasmValue>) -> 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<RawWasmValue>,
- ) -> Result<()> {
- let v2 = self.pop()?;
- let v1 = self.last_mut()?;
- *v1 = func(*v1, v2)?;
- Ok(())
+ pub(crate) fn peek<T: InternalValue>(&self) -> Result<T> {
+ T::stack_peek(self)
}
- #[inline(always)]
- pub(crate) fn len(&self) -> usize {
- self.stack.len()
+ pub(crate) fn pop<T: InternalValue>(&mut self) -> Result<T> {
+ T::stack_pop(self)
}
- #[cfg(feature = "simd")]
- #[inline(always)]
- pub(crate) fn simd_len(&self) -> usize {
- self.simd_stack.len()
+ pub(crate) fn push<T: InternalValue>(&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<T: InternalValue>(&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<T: InternalValue>(&mut self) -> Result<()> {
+ let cond: i32 = self.pop()?;
+ let val2: T = self.pop()?;
+ if cond == 0 {
+ self.drop::<T>()?;
+ self.push(val2);
+ }
+ Ok(())
}
- #[inline(always)]
- pub(crate) fn push(&mut self, value: RawWasmValue) {
- self.stack.push(value);
+ pub(crate) fn calculate<T: InternalValue, U: InternalValue>(&mut self, func: fn(T, T) -> Result<U>) -> 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<T: InternalValue, U: InternalValue>(&mut self, func: fn(T) -> Result<U>) -> 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<TinyWasmValue> {
+ 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<Vec<WasmValue>> {
+ val_types.iter().map(|val_type| self.pop_wasmvalue(*val_type)).collect::<Result<Vec<_>>>()
}
- #[inline(always)]
- pub(crate) fn pop(&mut self) -> Result<RawWasmValue> {
- 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<Vec<WasmValue>> {
+ val_types.iter().rev().map(|val_type| self.pop_wasmvalue(*val_type)).collect::<Result<Vec<_>>>().map(|mut v| {
+ v.reverse();
+ v
+ })
}
- #[inline]
- pub(crate) fn pop_params(&mut self, types: &[ValType]) -> Result<Vec<WasmValue>> {
- #[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<Vec<TinyWasmValue>> {
+ 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<WasmValue> {
+ 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<T: Copy + Default>(data: &mut BoxVec<T>, n: u32, end_keep: u32) {
+fn truncate_keep<T: Copy + Default>(data: &mut Vec<T>, 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)]
@@ -226,32 +163,12 @@ 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<u32>;
+
+#[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<ValType> 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<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<&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<f32> for TinyWasmValue {
+ fn from(value: f32) -> Self {
+ TinyWasmValue::Value32(value.to_bits())
+ }
+}
+
+impl From<f64> for TinyWasmValue {
+ fn from(value: f64) -> Self {
+ TinyWasmValue::Value64(value.to_bits())
+ }
+}
+
+impl From<i32> for TinyWasmValue {
+ fn from(value: i32) -> Self {
+ TinyWasmValue::Value32(value as u32)
+ }
+}
+
+impl From<u32> for TinyWasmValue {
+ fn from(value: u32) -> Self {
+ TinyWasmValue::Value32(value)
+ }
+}
+
+impl From<i64> for TinyWasmValue {
+ fn from(value: i64) -> Self {
+ TinyWasmValue::Value64(value as u64)
+ }
+}
+
+impl From<u64> for TinyWasmValue {
+ fn from(value: u64) -> Self {
+ TinyWasmValue::Value64(value)
+ }
+}
+
+impl From<Value128> for TinyWasmValue {
+ fn from(value: Value128) -> Self {
+ TinyWasmValue::Value128(value)
+ }
+}
+
+impl From<ValueRef> 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<Self>
+ where
+ Self: Sized;
+ fn stack_peek(stack: &ValueStack) -> Result<Self>
+ where
+ Self: Sized;
+
+ fn local_get(locals: &Locals, index: u32) -> Result<Self>
+ 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<Self> {
+ stack.stack_32.pop().ok_or(Error::ValueStackUnderflow).map(|v| v as i32)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_32.last().ok_or(Error::ValueStackUnderflow).map(|v| *v as i32)
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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<Self> {
+ stack.stack_32.pop().ok_or(Error::ValueStackUnderflow).map(f32::from_bits)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_32.last().ok_or(Error::ValueStackUnderflow).map(|v| f32::from_bits(*v))
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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<Self> {
+ stack.stack_64.pop().ok_or(Error::ValueStackUnderflow).map(|v| v as i64)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_64.last().ok_or(Error::ValueStackUnderflow).map(|v| *v as i64)
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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<Self> {
+ stack.stack_64.pop().ok_or(Error::ValueStackUnderflow)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_64.last().ok_or(Error::ValueStackUnderflow).copied()
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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<Self> {
+ stack.stack_64.pop().ok_or(Error::ValueStackUnderflow).map(f64::from_bits)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_64.last().ok_or(Error::ValueStackUnderflow).map(|v| f64::from_bits(*v))
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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<Self> {
+ stack.stack_32.pop().ok_or(Error::ValueStackUnderflow)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_32.last().ok_or(Error::ValueStackUnderflow).copied()
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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<Self> {
+ stack.stack_128.pop().ok_or(Error::ValueStackUnderflow)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_128.last().ok_or(Error::ValueStackUnderflow).copied()
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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<Self> {
+ stack.stack_ref.pop().ok_or(Error::ValueStackUnderflow)
+ }
+ #[inline]
+ fn stack_peek(stack: &ValueStack) -> Result<Self> {
+ stack.stack_ref.last().ok_or(Error::ValueStackUnderflow).copied()
+ }
+ #[inline]
+ fn local_get(locals: &Locals, index: u32) -> Result<Self> {
+ 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 <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances>
#[derive(Debug)]
pub(crate) struct GlobalInstance {
- pub(crate) value: Cell<RawWasmValue>,
+ pub(crate) value: Cell<TinyWasmValue>,
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<RawWasmValue> {
+ #[doc(hidden)]
+ pub fn get_global_val(&self, addr: MemAddr) -> Result<TinyWasmValue> {
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<Addr> {
+ pub(crate) fn add_global(&mut self, ty: GlobalType, value: TinyWasmValue, idx: ModuleInstanceAddr) -> Result<Addr> {
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<RawWasmValue> {
+ ) -> Result<TinyWasmValue> {
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 <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions>
@@ -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 <https://webassembly.github.io/spec/core/binary/instructions.html#parametric-instructions>
- Drop,
- Select(Option<ValType>),
+ Drop32,
+ Drop64,
+ Drop128,
+ DropRef,
+
+ Select32,
+ Select64,
+ Select128,
+ SelectRef,
// > Variable Instructions
// See <https://webassembly.github.io/spec/core/binary/instructions.html#variable-instructions>
- 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,
}