summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry Gressmann <mail@henrygressmann.de>2024-02-27 17:04:23 +0100
committerGitHub <noreply@github.com>2024-02-27 17:04:23 +0100
commitab2ae9b445268d503bde3dbb7a91886c55c19144 (patch)
treeb2644fc05d37b320035a2997867de61f804f5b6c
parent43e6d23ae8806c813dd5fa0663c17c1772ebf5a8 (diff)
parent417bb70c0f8c8f8781f675481e5336affd209183 (diff)
feat: rework instructions (#4)
-rw-r--r--BENCHMARKS.md10
-rw-r--r--Cargo.lock46
-rw-r--r--README.md2
-rw-r--r--crates/benchmarks/benches/fibonacci.rs6
-rw-r--r--crates/parser/Cargo.toml2
-rw-r--r--crates/parser/src/conversion.rs2
-rw-r--r--crates/parser/src/lib.rs1
-rw-r--r--crates/parser/src/visit.rs246
-rw-r--r--crates/tinywasm/src/func.rs73
-rw-r--r--crates/tinywasm/src/imports.rs1
-rw-r--r--crates/tinywasm/src/reference.rs10
-rw-r--r--crates/tinywasm/src/runtime/interpreter/macros.rs106
-rw-r--r--crates/tinywasm/src/runtime/interpreter/mod.rs235
-rw-r--r--crates/tinywasm/src/runtime/interpreter/no_std_floats.rs97
-rw-r--r--crates/tinywasm/src/runtime/interpreter/traits.rs44
-rw-r--r--crates/tinywasm/src/runtime/stack/call_stack.rs12
-rw-r--r--crates/tinywasm/src/runtime/stack/value_stack.rs29
-rw-r--r--crates/tinywasm/src/runtime/value.rs78
-rw-r--r--crates/tinywasm/src/store/memory.rs14
-rw-r--r--crates/tinywasm/src/store/mod.rs99
-rw-r--r--crates/tinywasm/src/store/table.rs4
-rw-r--r--crates/tinywasm/tests/generated/2.0.csv2
-rw-r--r--crates/types/src/archive.rs2
-rw-r--r--crates/types/src/instructions.rs166
-rw-r--r--crates/types/src/value.rs31
-rw-r--r--examples/rust/analyze.py36
26 files changed, 745 insertions, 609 deletions
diff --git a/BENCHMARKS.md b/BENCHMARKS.md
index 5189b55..2cded9d 100644
--- a/BENCHMARKS.md
+++ b/BENCHMARKS.md
@@ -30,12 +30,12 @@ All runtimes are compiled with the following settings:
| Benchmark | Native | TinyWasm\* | Wasmi | Wasmer (Single Pass) |
| ------------ | -------- | ---------- | --------- | -------------------- |
-| `fib` | \*\* | ` 43.81µs` | `48.60µs` | ` 43.97µs` |
-| `fib-rec` | `0.26ms` | ` 20.99ms` | ` 4.64ms` | ` 0.50ms` |
-| `argon2id` | `0.53ms` | `107.77ms` | `47.76ms` | ` 4.49ms` |
-| `selfhosted` | `0.06ms` | ` 2.88ms` | ` 6.20ms` | `359.33ms` |
+| `fib` | \*\* | ` 43.60µs` | `48.27µs` | ` 44.99µs` |
+| `fib-rec` | `0.27ms` | ` 21.13ms` | ` 4.63ms` | ` 0.47ms` |
+| `argon2id` | `0.53ms` | ` 99.16ms` | `45.00ms` | ` 4.59ms` |
+| `selfhosted` | `0.05ms` | ` 1.84ms` | ` 6.51ms` | `446.48ms` |
-_\* converting WASM to TinyWasm bytecode is not included. I takes ~7ms to convert `tinywasm.wasm` to TinyWasm bytecode._
+_\* converting WASM to TinyWasm bytecode is not included. I takes ~5.7ms to convert `tinywasm.wasm` to TinyWasm bytecode._
_\*\* essentially instant as it gets computed at compile time._
### Fib
diff --git a/Cargo.lock b/Cargo.lock
index a6fdbbd..835692a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -96,7 +96,7 @@ dependencies = [
"argh_shared",
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
[[package]]
@@ -700,7 +700,7 @@ dependencies = [
"ident_case",
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
[[package]]
@@ -711,7 +711,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
[[package]]
@@ -867,7 +867,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
[[package]]
@@ -1084,9 +1084,9 @@ dependencies = [
[[package]]
name = "half"
-version = "2.3.1"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872"
+checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e"
dependencies = [
"cfg-if",
"crunchy",
@@ -1752,9 +1752,9 @@ dependencies = [
[[package]]
name = "rust-embed"
-version = "8.2.0"
+version = "8.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f"
+checksum = "fb78f46d0066053d16d4ca7b898e9343bc3530f71c61d5ad84cd404ada068745"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
@@ -1763,22 +1763,22 @@ dependencies = [
[[package]]
name = "rust-embed-impl"
-version = "8.2.0"
+version = "8.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16"
+checksum = "b91ac2a3c6c0520a3fb3dd89321177c3c692937c4eb21893378219da10c44fc8"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
- "syn 2.0.50",
+ "syn 2.0.51",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
-version = "8.2.0"
+version = "8.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665"
+checksum = "86f69089032567ffff4eada41c573fc43ff466c7db7c5688b2e7969584345581"
dependencies = [
"globset",
"sha2",
@@ -1867,7 +1867,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
[[package]]
@@ -1966,9 +1966,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.50"
+version = "2.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
+checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
dependencies = [
"proc-macro2",
"quote",
@@ -2013,7 +2013,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
[[package]]
@@ -2120,9 +2120,9 @@ dependencies = [
[[package]]
name = "tinywasm-wasmparser"
-version = "0.200.2"
+version = "0.200.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9fce1b3563499af272f7e88c8b0357e740e62c2bcf59f134992698d35af96da"
+checksum = "365709f885b90cad71c71120414f99255e74d9d03f232618d9d1c6eb5db0ea99"
dependencies = [
"ahash 0.8.9",
"bitflags 2.4.2",
@@ -2150,7 +2150,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
[[package]]
@@ -2289,7 +2289,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
"wasm-bindgen-shared",
]
@@ -2311,7 +2311,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2859,5 +2859,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.50",
+ "syn 2.0.51",
]
diff --git a/README.md b/README.md
index c07fadc..c2abbf3 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
## Why TinyWasm?
-- **Tiny**: TinyWasm is designed to be as small as possible without significantly compromising performance or functionality.
+- **Tiny**: TinyWasm is designed to be as small as possible without significantly compromising performance or functionality (< 6000 lines of code).
- **Portable**: TinyWasm runs on any platform that Rust can target, including WebAssembly itself, with minimal external dependencies.
- **Lightweight**: TinyWasm is easy to integrate and has a low call overhead, making it suitable for scripting and embedding.
diff --git a/crates/benchmarks/benches/fibonacci.rs b/crates/benchmarks/benches/fibonacci.rs
index 38bbde9..8a4dab2 100644
--- a/crates/benchmarks/benches/fibonacci.rs
+++ b/crates/benchmarks/benches/fibonacci.rs
@@ -17,10 +17,10 @@ fn run_wasmi(wasm: &[u8], iterations: i32, name: &str) {
fn run_wasmer(wasm: &[u8], iterations: i32, name: &str) {
use wasmer::*;
- let engine: Engine = wasmer::Singlepass::default().into();
- let mut store = Store::default();
+ let compiler = wasmer::Singlepass::default();
+ let mut store = Store::new(compiler);
let import_object = imports! {};
- let module = wasmer::Module::from_binary(&engine, wasm).expect("wasmer::Module::from_binary");
+ let module = wasmer::Module::from_binary(&store, wasm).expect("wasmer::Module::from_binary");
let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new");
let fib = instance.exports.get_typed_function::<i32, i32>(&store, name).expect("get_function");
fib.call(&mut store, iterations).expect("call");
diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml
index 73eec99..3be0bb1 100644
--- a/crates/parser/Cargo.toml
+++ b/crates/parser/Cargo.toml
@@ -9,7 +9,7 @@ repository.workspace=true
[dependencies]
# fork of wasmparser with no_std support, see https://github.com/bytecodealliance/wasmtime/issues/3495
-wasmparser={version="0.200.2", package="tinywasm-wasmparser", default-features=false}
+wasmparser={version="0.200.3", package="tinywasm-wasmparser", default-features=false}
log={version="0.4", optional=true}
tinywasm-types={version="0.4.0", path="../types", default-features=false}
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index 53cceb6..c13d08f 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -226,7 +226,7 @@ pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType {
}
pub(crate) fn convert_memarg(memarg: wasmparser::MemArg) -> MemoryArg {
- MemoryArg { offset: memarg.offset, align: memarg.align, align_max: memarg.max_align, mem_addr: memarg.memory }
+ MemoryArg { offset: memarg.offset, mem_addr: memarg.memory }
}
pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result<ConstInstruction> {
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 5de4b03..7beb5f8 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -7,7 +7,6 @@
#![forbid(unsafe_code)]
#![cfg_attr(not(feature = "std"), feature(error_in_core))]
//! See [`tinywasm`](https://docs.rs/tinywasm) for documentation.
-#![recursion_limit = "1028"]
mod std;
extern crate alloc;
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index 15024f1..cc9f0e2 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -3,7 +3,7 @@ use crate::{conversion::convert_blocktype, Result};
use crate::conversion::{convert_heaptype, convert_memarg, convert_valtype};
use alloc::string::ToString;
use alloc::{boxed::Box, format, vec::Vec};
-use tinywasm_types::Instruction;
+use tinywasm_types::{BlockArgsPacked, Instruction};
use wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources};
struct ValidateThenVisit<'a, T, U>(T, &'a mut U);
@@ -71,49 +71,31 @@ macro_rules! define_primitive_operands {
}
)*
};
+ ($($name:ident, $instr:expr, $ty:ty, $ty2:ty),*) => {
+ $(
+ fn $name(&mut self, arg: $ty, arg2: $ty) -> Self::Output {
+ self.instructions.push($instr(arg, arg2));
+ Ok(())
+ }
+ )*
+ };
}
macro_rules! define_mem_operands {
- ($($name:ident, $instr:expr),*) => {
+ ($($name:ident, $instr:ident),*) => {
$(
fn $name(&mut self, mem_arg: wasmparser::MemArg) -> Self::Output {
- self.instructions.push($instr(
- convert_memarg(mem_arg)
- ));
+ let arg = convert_memarg(mem_arg);
+ self.instructions.push(Instruction::$instr {
+ offset: arg.offset,
+ mem_addr: arg.mem_addr,
+ });
Ok(())
}
)*
};
}
-macro_rules! impl_visit_operator {
- ( @mvp $($rest:tt)* ) => {
- impl_visit_operator!(@@skipped $($rest)*);
- };
- ( @sign_extension $($rest:tt)* ) => {
- impl_visit_operator!(@@skipped $($rest)*);
- };
- ( @saturating_float_to_int $($rest:tt)* ) => {
- impl_visit_operator!(@@skipped $($rest)*);
- };
- ( @bulk_memory $($rest:tt)* ) => {
- impl_visit_operator!(@@skipped $($rest)*);
- };
- ( @reference_types $($rest:tt)* ) => {
- impl_visit_operator!(@@skipped $($rest)*);
- };
- ( @@skipped $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
- impl_visit_operator!($($rest)*);
- };
- ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => {
- fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output {
- self.unsupported(stringify!($op))
- }
- impl_visit_operator!($($rest)*);
- };
- () => {};
-}
-
pub(crate) struct FunctionBuilder {
instructions: Vec<Instruction>,
label_ptrs: Vec<usize>,
@@ -139,12 +121,13 @@ impl FunctionBuilder {
impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
type Output = Result<()>;
+ fn visit_default(&mut self, op: &str) -> Self::Output {
+ self.unsupported(op)
+ }
+
define_primitive_operands! {
visit_br, Instruction::Br, u32,
visit_br_if, Instruction::BrIf, u32,
- visit_local_get, Instruction::LocalGet, u32,
- visit_local_set, Instruction::LocalSet, u32,
- visit_local_tee, Instruction::LocalTee, u32,
visit_global_get, Instruction::GlobalGet, u32,
visit_global_set, Instruction::GlobalSet, u32,
visit_i32_const, Instruction::I32Const, i32,
@@ -152,29 +135,29 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
}
define_mem_operands! {
- visit_i32_load, Instruction::I32Load,
- visit_i64_load, Instruction::I64Load,
- visit_f32_load, Instruction::F32Load,
- visit_f64_load, Instruction::F64Load,
- visit_i32_load8_s, Instruction::I32Load8S,
- visit_i32_load8_u, Instruction::I32Load8U,
- visit_i32_load16_s, Instruction::I32Load16S,
- visit_i32_load16_u, Instruction::I32Load16U,
- visit_i64_load8_s, Instruction::I64Load8S,
- visit_i64_load8_u, Instruction::I64Load8U,
- visit_i64_load16_s, Instruction::I64Load16S,
- visit_i64_load16_u, Instruction::I64Load16U,
- visit_i64_load32_s, Instruction::I64Load32S,
- visit_i64_load32_u, Instruction::I64Load32U,
- visit_i32_store, Instruction::I32Store,
- visit_i64_store, Instruction::I64Store,
- visit_f32_store, Instruction::F32Store,
- visit_f64_store, Instruction::F64Store,
- visit_i32_store8, Instruction::I32Store8,
- visit_i32_store16, Instruction::I32Store16,
- visit_i64_store8, Instruction::I64Store8,
- visit_i64_store16, Instruction::I64Store16,
- visit_i64_store32, Instruction::I64Store32
+ visit_i32_load, I32Load,
+ visit_i64_load, I64Load,
+ visit_f32_load, F32Load,
+ visit_f64_load, F64Load,
+ visit_i32_load8_s, I32Load8S,
+ visit_i32_load8_u, I32Load8U,
+ visit_i32_load16_s, I32Load16S,
+ visit_i32_load16_u, I32Load16U,
+ visit_i64_load8_s, I64Load8S,
+ visit_i64_load8_u, I64Load8U,
+ visit_i64_load16_s, I64Load16S,
+ visit_i64_load16_u, I64Load16U,
+ visit_i64_load32_s, I64Load32S,
+ visit_i64_load32_u, I64Load32U,
+ visit_i32_store, I32Store,
+ visit_i64_store, I64Store,
+ visit_f32_store, F32Store,
+ visit_f64_store, F64Store,
+ visit_i32_store8, I32Store8,
+ visit_i32_store16, I32Store16,
+ visit_i64_store8, I64Store8,
+ visit_i64_store16, I64Store16,
+ visit_i64_store32, I64Store32
}
define_operands! {
@@ -220,7 +203,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
visit_i32_clz, Instruction::I32Clz,
visit_i32_ctz, Instruction::I32Ctz,
visit_i32_popcnt, Instruction::I32Popcnt,
- visit_i32_add, Instruction::I32Add,
+ // visit_i32_add, Instruction::I32Add, custom implementation
visit_i32_sub, Instruction::I32Sub,
visit_i32_mul, Instruction::I32Mul,
visit_i32_div_s, Instruction::I32DivS,
@@ -251,7 +234,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
visit_i64_shl, Instruction::I64Shl,
visit_i64_shr_s, Instruction::I64ShrS,
visit_i64_shr_u, Instruction::I64ShrU,
- visit_i64_rotl, Instruction::I64Rotl,
+ // visit_i64_rotl, Instruction::I64Rotl, custom implementation
visit_i64_rotr, Instruction::I64Rotr,
visit_f32_abs, Instruction::F32Abs,
visit_f32_neg, Instruction::F32Neg,
@@ -325,6 +308,69 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U
}
+ fn visit_local_get(&mut self, idx: u32) -> Self::Output {
+ if let Some(instruction) = self.instructions.last_mut() {
+ match instruction {
+ Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx),
+ Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx),
+ Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx),
+ _ => return self.visit(Instruction::LocalGet(idx)),
+ };
+ Ok(())
+ } else {
+ self.visit(Instruction::LocalGet(idx))
+ }
+ }
+
+ fn visit_local_set(&mut self, idx: u32) -> Self::Output {
+ self.visit(Instruction::LocalSet(idx))
+ // if let Some(instruction) = self.instructions.last_mut() {
+ // match instruction {
+ // // Needs more testing, seems to make performance worse
+ // // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx),
+ // _ => return self.visit(Instruction::LocalSet(idx)),
+ // };
+ // // Ok(())
+ // } else {
+ // self.visit(Instruction::LocalSet(idx))
+ // }
+ }
+
+ fn visit_local_tee(&mut self, idx: u32) -> Self::Output {
+ self.visit(Instruction::LocalTee(idx))
+ }
+
+ fn visit_i64_rotl(&mut self) -> Self::Output {
+ if self.instructions.len() < 2 {
+ return self.visit(Instruction::I64Rotl);
+ }
+
+ match self.instructions[self.instructions.len() - 2..] {
+ [Instruction::I64Xor, Instruction::I64Const(a)] => {
+ self.instructions.pop();
+ self.instructions.pop();
+ self.visit(Instruction::I64XorConstRotl(a))
+ }
+ _ => self.visit(Instruction::I64Rotl),
+ }
+ }
+
+ fn visit_i32_add(&mut self) -> Self::Output {
+ self.visit(Instruction::I32Add)
+ // if self.instructions.len() < 2 {
+ // return self.visit(Instruction::I32Add);
+ // }
+
+ // match self.instructions[self.instructions.len() - 2..] {
+ // // [Instruction::LocalGet(a), Instruction::I32Const(b)] => {
+ // // self.instructions.pop();
+ // // self.instructions.pop();
+ // // self.visit(Instruction::I32LocalGetConstAdd(a, b))
+ // // }
+ // _ => self.visit(Instruction::I32Add),
+ // }
+ }
+
fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output {
self.label_ptrs.push(self.instructions.len());
self.visit(Instruction::Block(convert_blocktype(blockty), 0))
@@ -337,7 +383,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
fn visit_if(&mut self, ty: wasmparser::BlockType) -> Self::Output {
self.label_ptrs.push(self.instructions.len());
- self.visit(Instruction::If(convert_blocktype(ty), None, 0))
+ self.visit(Instruction::If(BlockArgsPacked::new(convert_blocktype(ty)), 0, 0))
}
fn visit_else(&mut self) -> Self::Output {
@@ -355,7 +401,9 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
match self.instructions[label_pointer] {
Instruction::Else(ref mut else_instr_end_offset) => {
- *else_instr_end_offset = current_instr_ptr - label_pointer;
+ *else_instr_end_offset = (current_instr_ptr - label_pointer)
+ .try_into()
+ .expect("else_instr_end_offset is too large, tinywasm does not support if blocks that large");
#[cold]
fn error() -> crate::ParseError {
@@ -372,13 +420,20 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
return Err(error());
};
- *else_offset = Some(label_pointer - if_label_pointer);
- *end_offset = current_instr_ptr - if_label_pointer;
+ *else_offset = (label_pointer - if_label_pointer)
+ .try_into()
+ .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large");
+
+ *end_offset = (current_instr_ptr - if_label_pointer)
+ .try_into()
+ .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large");
}
Instruction::Block(_, ref mut end_offset)
| Instruction::Loop(_, ref mut end_offset)
| Instruction::If(_, _, ref mut end_offset) => {
- *end_offset = current_instr_ptr - label_pointer;
+ *end_offset = (current_instr_ptr - label_pointer)
+ .try_into()
+ .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large");
}
_ => {
return Err(crate::ParseError::UnsupportedOperator(
@@ -398,7 +453,9 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
.collect::<Result<Vec<Instruction>, wasmparser::BinaryReaderError>>()
.expect("BrTable targets are invalid, this should have been caught by the validator");
- self.instructions.extend(IntoIterator::into_iter([Instruction::BrTable(def, instrs.len())]).chain(instrs));
+ self.instructions
+ .extend(IntoIterator::into_iter([Instruction::BrTable(def, instrs.len() as u32)]).chain(instrs));
+
Ok(())
}
@@ -428,24 +485,14 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
// Bulk Memory Operations
- fn visit_memory_init(&mut self, data_index: u32, mem: u32) -> Self::Output {
- self.visit(Instruction::MemoryInit(data_index, mem))
- }
-
- fn visit_data_drop(&mut self, data_index: u32) -> Self::Output {
- self.visit(Instruction::DataDrop(data_index))
- }
-
- fn visit_memory_copy(&mut self, dst_mem: u32, src_mem: u32) -> Self::Output {
- self.visit(Instruction::MemoryCopy(dst_mem, src_mem))
- }
-
- fn visit_memory_fill(&mut self, mem: u32) -> Self::Output {
- self.visit(Instruction::MemoryFill(mem))
+ define_primitive_operands! {
+ visit_memory_init, Instruction::MemoryInit, u32, u32,
+ visit_memory_copy, Instruction::MemoryCopy, u32, u32,
+ visit_table_init, Instruction::TableInit, u32, u32
}
-
- fn visit_table_init(&mut self, elem_index: u32, table: u32) -> Self::Output {
- self.visit(Instruction::TableInit(elem_index, table))
+ define_primitive_operands! {
+ visit_memory_fill, Instruction::MemoryFill, u32,
+ visit_data_drop, Instruction::DataDrop, u32
}
fn visit_elem_drop(&mut self, _elem_index: u32) -> Self::Output {
@@ -466,33 +513,16 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
self.visit(Instruction::RefIsNull)
}
- fn visit_ref_func(&mut self, idx: u32) -> Self::Output {
- self.visit(Instruction::RefFunc(idx))
- }
-
fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output {
self.visit(Instruction::Select(Some(convert_valtype(&ty))))
}
- fn visit_table_fill(&mut self, table: u32) -> Self::Output {
- self.visit(Instruction::TableFill(table))
- }
-
- fn visit_table_get(&mut self, table: u32) -> Self::Output {
- self.visit(Instruction::TableGet(table))
- }
-
- fn visit_table_set(&mut self, table: u32) -> Self::Output {
- self.visit(Instruction::TableSet(table))
- }
-
- fn visit_table_grow(&mut self, table: u32) -> Self::Output {
- self.visit(Instruction::TableGrow(table))
- }
-
- fn visit_table_size(&mut self, table: u32) -> Self::Output {
- self.visit(Instruction::TableSize(table))
+ define_primitive_operands! {
+ visit_ref_func, Instruction::RefFunc, u32,
+ visit_table_fill, Instruction::TableFill, u32,
+ visit_table_get, Instruction::TableGet, u32,
+ visit_table_set, Instruction::TableSet, u32,
+ visit_table_grow, Instruction::TableGrow, u32,
+ visit_table_size, Instruction::TableSize, u32
}
-
- wasmparser::for_each_operator!(impl_visit_operator);
}
diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs
index 757cb85..d7f7ca1 100644
--- a/crates/tinywasm/src/func.rs
+++ b/crates/tinywasm/src/func.rs
@@ -22,6 +22,9 @@ impl FuncHandle {
/// See <https://webassembly.github.io/spec/core/exec/modules.html#invocation>
#[inline]
pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result<Vec<WasmValue>> {
+ // Comments are ordered by the steps in the spec
+ // In this implementation, some steps are combined and ordered differently for performance reasons
+
// 3. Let func_ty be the function type
let func_ty = &self.ty;
@@ -35,7 +38,7 @@ impl FuncHandle {
}
// 5. For each value type and the corresponding value, check if types match
- if !unlikely(func_ty.params.iter().zip(params).enumerate().all(|(i, (ty, param))| {
+ if !(func_ty.params.iter().zip(params).enumerate().all(|(i, (ty, param))| {
if ty != &param.val_type() {
log::error!("param type mismatch at index {}: expected {:?}, got {:?}", i, ty, param);
false
@@ -57,8 +60,8 @@ impl FuncHandle {
};
// 6. Let f be the dummy frame
- let call_frame =
- CallFrame::new(wasm_func.clone(), func_inst.owner, params.iter().map(|v| RawWasmValue::from(*v)), 0);
+ let call_frame_params = params.iter().map(|v| RawWasmValue::from(*v));
+ let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, call_frame_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)
@@ -113,6 +116,7 @@ impl<P: IntoWasmValueTuple, R: FromWasmValueTuple> FuncHandleTyped<P, R> {
R::from_wasm_value_tuple(&result)
}
}
+
macro_rules! impl_into_wasm_value_tuple {
($($T:ident),*) => {
impl<$($T),*> IntoWasmValueTuple for ($($T,)*)
@@ -140,21 +144,6 @@ macro_rules! impl_into_wasm_value_tuple_single {
};
}
-impl_into_wasm_value_tuple_single!(i32);
-impl_into_wasm_value_tuple_single!(i64);
-impl_into_wasm_value_tuple_single!(f32);
-impl_into_wasm_value_tuple_single!(f64);
-
-impl_into_wasm_value_tuple!();
-impl_into_wasm_value_tuple!(T1);
-impl_into_wasm_value_tuple!(T1, T2);
-impl_into_wasm_value_tuple!(T1, T2, T3);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7);
-impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
-
macro_rules! impl_from_wasm_value_tuple {
($($T:ident),*) => {
impl<$($T),*> FromWasmValueTuple for ($($T,)*)
@@ -200,21 +189,6 @@ macro_rules! impl_from_wasm_value_tuple_single {
};
}
-impl_from_wasm_value_tuple_single!(i32);
-impl_from_wasm_value_tuple_single!(i64);
-impl_from_wasm_value_tuple_single!(f32);
-impl_from_wasm_value_tuple_single!(f64);
-
-impl_from_wasm_value_tuple!();
-impl_from_wasm_value_tuple!(T1);
-impl_from_wasm_value_tuple!(T1, T2);
-impl_from_wasm_value_tuple!(T1, T2, T3);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7);
-impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
-
pub trait ValTypesFromTuple {
fn val_types() -> Box<[ValType]>;
}
@@ -268,19 +242,42 @@ impl ValTypesFromTuple for () {
}
}
-impl<T1> ValTypesFromTuple for T1
-where
- T1: ToValType,
-{
+impl<T: ToValType> ValTypesFromTuple for T {
#[inline]
fn val_types() -> Box<[ValType]> {
- Box::new([T1::to_val_type()])
+ Box::new([T::to_val_type()])
}
}
+impl_from_wasm_value_tuple_single!(i32);
+impl_from_wasm_value_tuple_single!(i64);
+impl_from_wasm_value_tuple_single!(f32);
+impl_from_wasm_value_tuple_single!(f64);
+
+impl_into_wasm_value_tuple_single!(i32);
+impl_into_wasm_value_tuple_single!(i64);
+impl_into_wasm_value_tuple_single!(f32);
+impl_into_wasm_value_tuple_single!(f64);
+
impl_val_types_from_tuple!(T1);
impl_val_types_from_tuple!(T1, T2);
impl_val_types_from_tuple!(T1, T2, T3);
impl_val_types_from_tuple!(T1, T2, T3, T4);
impl_val_types_from_tuple!(T1, T2, T3, T4, T5);
impl_val_types_from_tuple!(T1, T2, T3, T4, T5, T6);
+
+impl_from_wasm_value_tuple!();
+impl_from_wasm_value_tuple!(T1);
+impl_from_wasm_value_tuple!(T1, T2);
+impl_from_wasm_value_tuple!(T1, T2, T3);
+impl_from_wasm_value_tuple!(T1, T2, T3, T4);
+impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5);
+impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
+
+impl_into_wasm_value_tuple!();
+impl_into_wasm_value_tuple!(T1);
+impl_into_wasm_value_tuple!(T1, T2);
+impl_into_wasm_value_tuple!(T1, T2, T3);
+impl_into_wasm_value_tuple!(T1, T2, T3, T4);
+impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5);
+impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6);
diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs
index 522a82d..1c5ca98 100644
--- a/crates/tinywasm/src/imports.rs
+++ b/crates/tinywasm/src/imports.rs
@@ -157,7 +157,6 @@ impl Extern {
};
let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() };
-
Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty })))
}
diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs
index 4c6d703..6713a42 100644
--- a/crates/tinywasm/src/reference.rs
+++ b/crates/tinywasm/src/reference.rs
@@ -26,21 +26,21 @@ pub struct MemoryRefMut<'a> {
impl<'a> MemoryRefLoad for MemoryRef<'a> {
/// Load a slice of memory
fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.instance.load(offset, 0, len)
+ self.instance.load(offset, len)
}
}
impl<'a> MemoryRefLoad for MemoryRefMut<'a> {
/// Load a slice of memory
fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.instance.load(offset, 0, len)
+ self.instance.load(offset, len)
}
}
impl MemoryRef<'_> {
/// Load a slice of memory
pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.instance.load(offset, 0, len)
+ self.instance.load(offset, len)
}
/// Load a slice of memory as a vector
@@ -52,7 +52,7 @@ impl MemoryRef<'_> {
impl MemoryRefMut<'_> {
/// Load a slice of memory
pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> {
- self.instance.load(offset, 0, len)
+ self.instance.load(offset, len)
}
/// Load a slice of memory as a vector
@@ -82,7 +82,7 @@ impl MemoryRefMut<'_> {
/// Store a slice of memory
pub fn store(&mut self, offset: usize, len: usize, data: &[u8]) -> Result<()> {
- self.instance.store(offset, 0, data, len)
+ self.instance.store(offset, len, data)
}
}
diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs
index 824666d..30f34fc 100644
--- a/crates/tinywasm/src/runtime/interpreter/macros.rs
+++ b/crates/tinywasm/src/runtime/interpreter/macros.rs
@@ -12,10 +12,9 @@
macro_rules! break_to {
($cf:ident, $stack:ident, $break_to_relative:ident) => {{
if $cf.break_to(*$break_to_relative, &mut $stack.values, &mut $stack.blocks).is_none() {
- if $stack.call_stack.is_empty() {
- return Ok(ExecResult::Return);
- } else {
- return Ok(ExecResult::Call);
+ match $stack.call_stack.is_empty() {
+ true => return Ok(ExecResult::Return),
+ false => return Ok(ExecResult::Call),
}
}
}};
@@ -23,20 +22,22 @@ macro_rules! break_to {
/// Load a value from memory
macro_rules! mem_load {
- ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{
+ ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{
mem_load!($type, $type, $arg, $stack, $store, $module)
}};
- ($load_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{
- let mem_idx = $module.resolve_mem_addr($arg.mem_addr);
+ ($load_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{
+ let (mem_addr, offset) = $arg;
+
+ let mem_idx = $module.resolve_mem_addr(*mem_addr);
let mem = $store.get_mem(mem_idx as usize)?;
let mem_ref = mem.borrow_mut();
- let addr = $stack.values.pop()?.raw_value();
- let addr = $arg.offset.checked_add(addr).ok_or_else(|| {
+ let addr: u64 = $stack.values.pop()?.into();
+ let addr = offset.checked_add(addr).ok_or_else(|| {
cold();
Error::Trap(crate::Trap::MemoryOutOfBounds {
- offset: $arg.offset as usize,
+ offset: *offset as usize,
len: core::mem::size_of::<$load_type>(),
max: mem_ref.max_pages(),
})
@@ -45,38 +46,31 @@ macro_rules! mem_load {
let addr: usize = addr.try_into().ok().ok_or_else(|| {
cold();
Error::Trap(crate::Trap::MemoryOutOfBounds {
- offset: $arg.offset as usize,
+ offset: *offset as usize,
len: core::mem::size_of::<$load_type>(),
max: mem_ref.max_pages(),
})
})?;
const LEN: usize = core::mem::size_of::<$load_type>();
- let val = mem_ref.load_as::<LEN, $load_type>(addr, $arg.align as usize)?;
+ let val = mem_ref.load_as::<LEN, $load_type>(addr)?;
$stack.values.push((val as $target_type).into());
}};
}
/// Store a value to memory
macro_rules! mem_store {
- ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{
- log::debug!("mem_store!({}, {:?})", stringify!($type), $arg);
-
+ ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{
mem_store!($type, $type, $arg, $stack, $store, $module)
}};
- ($store_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{
- // likewise, there could be a lot of performance improvements here
- let mem_idx = $module.resolve_mem_addr($arg.mem_addr);
- let mem = $store.get_mem(mem_idx as usize)?;
-
- let val = $stack.values.pop_t::<$store_type>()?;
- let addr = $stack.values.pop()?.raw_value();
-
- let val = val as $store_type;
+ ($store_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{
+ let (mem_addr, offset) = $arg;
+ let mem = $store.get_mem($module.resolve_mem_addr(*mem_addr) as usize)?;
+ let val: $store_type = $stack.values.pop()?.into();
let val = val.to_le_bytes();
-
- mem.borrow_mut().store(($arg.offset + addr) as usize, $arg.align as usize, &val, val.len())?;
+ let addr: u64 = $stack.values.pop()?.into();
+ mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?;
}};
}
@@ -102,13 +96,11 @@ macro_rules! float_min_max {
/// Convert a value on the stack
macro_rules! conv {
- ($from:ty, $intermediate:ty, $to:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$from>()? as $intermediate;
- $stack.values.push((a as $to).into());
- }};
($from:ty, $to:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$from>()?;
- $stack.values.push((a as $to).into());
+ $stack.values.replace_top(|v| {
+ let a: $from = v.into();
+ (a as $to).into()
+ });
}};
}
@@ -137,13 +129,9 @@ macro_rules! checked_conv_float {
/// Compare two values on the stack
macro_rules! comp {
- ($op:tt, $ty:ty, $stack:ident) => {{
- comp!($op, $ty, $ty, $stack)
- }};
-
- ($op:tt, $intermediate:ty, $to:ty, $stack:ident) => {{
- let b = $stack.values.pop_t::<$intermediate>()? as $to;
- let a = $stack.values.pop_t::<$intermediate>()? as $to;
+ ($op:tt, $to:ty, $stack:ident) => {{
+ let b: $to = $stack.values.pop()?.into();
+ let a: $to = $stack.values.pop()?.into();
$stack.values.push(((a $op b) as i32).into());
}};
}
@@ -151,62 +139,52 @@ macro_rules! comp {
/// Compare a value on the stack to zero
macro_rules! comp_zero {
($op:tt, $ty:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$ty>()?;
+ let a: $ty = $stack.values.pop()?.into();
$stack.values.push(((a $op 0) as i32).into());
}};
}
/// Apply an arithmetic method to two values on the stack
macro_rules! arithmetic {
- ($op:ident, $ty:ty, $stack:ident) => {
- arithmetic!($op, $ty, $ty, $stack)
- };
+ ($op:ident, $to:ty, $stack:ident) => {{
+ let b: $to = $stack.values.pop()?.into();
+ let a: $to = $stack.values.pop()?.into();
+ $stack.values.push((a.$op(b) as $to).into());
+ }};
// also allow operators such as +, -
($op:tt, $ty:ty, $stack:ident) => {{
- let b: $ty = $stack.values.pop_t()?;
- let a: $ty = $stack.values.pop_t()?;
+ let b: $ty = $stack.values.pop()?.into();
+ let a: $ty = $stack.values.pop()?.into();
$stack.values.push((a $op b).into());
}};
-
- ($op:ident, $intermediate:ty, $to:ty, $stack:ident) => {{
- let b = $stack.values.pop_t::<$to>()? as $intermediate;
- let a = $stack.values.pop_t::<$to>()? as $intermediate;
- $stack.values.push((a.$op(b) as $to).into());
- }};
}
/// Apply an arithmetic method to a single value on the stack
macro_rules! arithmetic_single {
($op:ident, $ty:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$ty>()?;
+ let a: $ty = $stack.values.pop()?.into();
$stack.values.push((a.$op() as $ty).into());
}};
($op:ident, $from:ty, $to:ty, $stack:ident) => {{
- let a = $stack.values.pop_t::<$from>()?;
+ let a: $from = $stack.values.pop()?.into();
$stack.values.push((a.$op() as $to).into());
}};
}
/// Apply an arithmetic operation to two values on the stack with error checking
macro_rules! checked_int_arithmetic {
- // Direct conversion with error checking (two types)
- ($from:tt, $to:tt, $stack:ident) => {{
- checked_int_arithmetic!($from, $to, $to, $stack)
- }};
-
- ($op:ident, $from:ty, $to:ty, $stack:ident) => {{
- let b = $stack.values.pop_t::<$from>()? as $to;
- let a = $stack.values.pop_t::<$from>()? as $to;
+ ($op:ident, $to:ty, $stack:ident) => {{
+ let b: $to = $stack.values.pop()?.into();
+ let a: $to = $stack.values.pop()?.into();
- if b == 0 {
+ if unlikely(b == 0) {
return Err(Error::Trap(crate::Trap::DivisionByZero));
}
let result = a.$op(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow))?;
- // Cast back to original type if different
- $stack.values.push((result as $from).into());
+ $stack.values.push((result).into());
}};
}
diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs
index b06152d..007acaa 100644
--- a/crates/tinywasm/src/runtime/interpreter/mod.rs
+++ b/crates/tinywasm/src/runtime/interpreter/mod.rs
@@ -79,17 +79,17 @@ enum ExecResult {
#[inline(always)]
fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &ModuleInstance) -> Result<ExecResult> {
let instrs = &cf.func_instance.0.instructions;
+
if unlikely(cf.instr_ptr >= instrs.len() || instrs.is_empty()) {
- cold();
log::error!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len());
return Err(Error::Other(format!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len())));
}
// A match statement is probably the fastest way to do this without
- // unreasonable complexity
+ // unreasonable complexity. This *should* be optimized to a jump table.
// See https://pliniker.github.io/post/dispatchers/
use tinywasm_types::Instruction::*;
- match &instrs[cf.instr_ptr] {
+ match cf.current_instruction() {
Nop => { /* do nothing */ }
Unreachable => {
cold();
@@ -144,14 +144,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
let table_idx = stack.values.pop_t::<u32>()?;
// verify that the table is of the right type, this should be validated by the parser already
- assert!(table.borrow().kind.element_type == ValType::RefFunc, "table is not of type funcref");
-
let func_ref = {
- table
- .borrow()
- .get(table_idx as usize)?
- .addr()
- .ok_or(Trap::UninitializedElement { index: table_idx as usize })?
+ let table = table.borrow();
+ assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref");
+ table.get(table_idx as usize)?.addr().ok_or(Trap::UninitializedElement { index: table_idx as usize })?
};
let func_inst = store.get_func(func_ref as usize)?.clone();
@@ -202,10 +198,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
cf.enter_block(
BlockFrame::new(
cf.instr_ptr,
- cf.instr_ptr + *end_offset,
+ cf.instr_ptr + *end_offset as usize,
stack.values.len(),
BlockType::If,
- args,
+ &args.unpack(),
module,
),
&mut stack.values,
@@ -215,19 +211,19 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
}
// falsy value is on the top of the stack
- if let Some(else_offset) = else_offset {
+ if *else_offset != 0 {
let label = BlockFrame::new(
- cf.instr_ptr + *else_offset,
- cf.instr_ptr + *end_offset,
+ cf.instr_ptr + *else_offset as usize,
+ cf.instr_ptr + *end_offset as usize,
stack.values.len(),
BlockType::Else,
- args,
+ &args.unpack(),
module,
);
- cf.instr_ptr += *else_offset;
+ cf.instr_ptr += *else_offset as usize;
cf.enter_block(label, &mut stack.values, &mut stack.blocks);
} else {
- cf.instr_ptr += *end_offset;
+ cf.instr_ptr += *end_offset as usize;
}
}
@@ -235,7 +231,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
cf.enter_block(
BlockFrame::new(
cf.instr_ptr,
- cf.instr_ptr + *end_offset,
+ cf.instr_ptr + *end_offset as usize,
stack.values.len(),
BlockType::Loop,
args,
@@ -250,8 +246,8 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
cf.enter_block(
BlockFrame::new(
cf.instr_ptr,
- cf.instr_ptr + *end_offset,
- stack.values.len(), // - params,
+ cf.instr_ptr + *end_offset as usize,
+ stack.values.len(),
BlockType::Block,
args,
module,
@@ -262,7 +258,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
}
BrTable(default, len) => {
- let instr = instrs[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len]
+ let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len as usize]
.iter()
.map(|i| match i {
BrLabel(l) => Ok(*l),
@@ -273,7 +269,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
})
.collect::<Result<Vec<_>>>()?;
- if unlikely(instr.len() != *len) {
+ if unlikely(instr.len() != *len as usize) {
panic!(
"Expected {} BrLabel instructions, got {}, this should have been validated by the parser",
len,
@@ -319,7 +315,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
let res_count = block.results;
stack.values.truncate_keep(block.stack_ptr, res_count);
- cf.instr_ptr += *end_offset;
+ cf.instr_ptr += *end_offset as usize;
}
EndBlockFrame => {
@@ -335,16 +331,11 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
LocalGet(local_index) => stack.values.push(cf.get_local(*local_index as usize)),
LocalSet(local_index) => cf.set_local(*local_index as usize, stack.values.pop()?),
LocalTee(local_index) => {
- let last_val = match stack.values.last() {
- Ok(val) => val,
- Err(_) => {
- log::error!("index: {}", local_index);
- log::error!("stack: {:?}", stack.values);
-
- panic!();
- }
- };
- cf.set_local(*local_index as usize, *last_val)
+ let local = stack.values.last();
+ cf.set_local(
+ *local_index as usize,
+ *local.expect("localtee: stack is empty. this should have been validated by the parser"),
+ );
}
GlobalGet(global_index) => {
@@ -364,8 +355,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
F64Const(val) => stack.values.push((*val).into()),
MemorySize(addr, byte) => {
- if *byte != 0 {
- cold();
+ if unlikely(*byte != 0) {
return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string()));
}
@@ -375,8 +365,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
}
MemoryGrow(addr, byte) => {
- if *byte != 0 {
- cold();
+ if unlikely(*byte != 0) {
return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string()));
}
@@ -397,9 +386,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
// Bulk memory operations
MemoryCopy(from, to) => {
- let size = stack.values.pop_t::<i32>()?;
- let src = stack.values.pop_t::<i32>()?;
- let dst = stack.values.pop_t::<i32>()?;
+ let size: i32 = stack.values.pop()?.into();
+ let src: i32 = stack.values.pop()?.into();
+ let dst: i32 = stack.values.pop()?.into();
let mem = store.get_mem(module.resolve_mem_addr(*from) as usize)?;
let mut mem = mem.borrow_mut();
@@ -411,14 +400,14 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
// copy between two memories
let mem2 = store.get_mem(module.resolve_mem_addr(*to) as usize)?;
let mut mem2 = mem2.borrow_mut();
- mem2.copy_from_slice(dst as usize, mem.load(src as usize, 0, size as usize)?)?;
+ mem2.copy_from_slice(dst as usize, mem.load(src as usize, size as usize)?)?;
}
}
MemoryFill(addr) => {
- let size = stack.values.pop_t::<i32>()?;
- let val = stack.values.pop_t::<i32>()?;
- let dst = stack.values.pop_t::<i32>()?;
+ let size: i32 = stack.values.pop()?.into();
+ let val: i32 = stack.values.pop()?.into();
+ let dst: i32 = stack.values.pop()?.into();
let mem = store.get_mem(module.resolve_mem_addr(*addr) as usize)?;
let mut mem = mem.borrow_mut();
@@ -430,26 +419,20 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
let offset = stack.values.pop_t::<i32>()? as usize;
let dst = stack.values.pop_t::<i32>()? as usize;
- let data_idx = module.resolve_data_addr(*data_index);
- let Some(ref data) = store.get_data(data_idx as usize)?.data else {
- cold();
- return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into());
+ let data = match &store.get_data(module.resolve_data_addr(*data_index) as usize)?.data {
+ Some(data) => data,
+ None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()),
};
- let mem_idx = module.resolve_mem_addr(*mem_index);
- let mem = store.get_mem(mem_idx as usize)?;
-
- let data_len = data.len();
- if offset + size > data_len {
- cold();
- return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data_len }.into());
+ if unlikely(offset + size > data.len()) {
+ return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data.len() }.into());
}
+ let mem = store.get_mem(module.resolve_mem_addr(*mem_index) as usize)?;
let mut mem = mem.borrow_mut();
- let data = &data[offset..(offset + size)];
// mem.store checks bounds
- mem.store(dst, 0, data, size)?;
+ mem.store(dst, size, &data[offset..(offset + size)])?;
}
DataDrop(data_index) => {
@@ -458,30 +441,30 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
data.drop();
}
- I32Store(arg) => mem_store!(i32, arg, stack, store, module),
- I64Store(arg) => mem_store!(i64, arg, stack, store, module),
- F32Store(arg) => mem_store!(f32, arg, stack, store, module),
- F64Store(arg) => mem_store!(f64, arg, stack, store, module),
- I32Store8(arg) => mem_store!(i8, i32, arg, stack, store, module),
- I32Store16(arg) => mem_store!(i16, i32, arg, stack, store, module),
- I64Store8(arg) => mem_store!(i8, i64, arg, stack, store, module),
- I64Store16(arg) => mem_store!(i16, i64, arg, stack, store, module),
- I64Store32(arg) => mem_store!(i32, i64, arg, stack, store, module),
+ I32Store { mem_addr, offset } => mem_store!(i32, (mem_addr, offset), stack, store, module),
+ I64Store { mem_addr, offset } => mem_store!(i64, (mem_addr, offset), stack, store, module),
+ F32Store { mem_addr, offset } => mem_store!(f32, (mem_addr, offset), stack, store, module),
+ F64Store { mem_addr, offset } => mem_store!(f64, (mem_addr, offset), stack, store, module),
+ I32Store8 { mem_addr, offset } => mem_store!(i8, i32, (mem_addr, offset), stack, store, module),
+ I32Store16 { mem_addr, offset } => mem_store!(i16, i32, (mem_addr, offset), stack, store, module),
+ I64Store8 { mem_addr, offset } => mem_store!(i8, i64, (mem_addr, offset), stack, store, module),
+ I64Store16 { mem_addr, offset } => mem_store!(i16, i64, (mem_addr, offset), stack, store, module),
+ I64Store32 { mem_addr, offset } => mem_store!(i32, i64, (mem_addr, offset), stack, store, module),
- I32Load(arg) => mem_load!(i32, arg, stack, store, module),
- I64Load(arg) => mem_load!(i64, arg, stack, store, module),
- F32Load(arg) => mem_load!(f32, arg, stack, store, module),
- F64Load(arg) => mem_load!(f64, arg, stack, store, module),
- I32Load8S(arg) => mem_load!(i8, i32, arg, stack, store, module),
- I32Load8U(arg) => mem_load!(u8, i32, arg, stack, store, module),
- I32Load16S(arg) => mem_load!(i16, i32, arg, stack, store, module),
- I32Load16U(arg) => mem_load!(u16, i32, arg, stack, store, module),
- I64Load8S(arg) => mem_load!(i8, i64, arg, stack, store, module),
- I64Load8U(arg) => mem_load!(u8, i64, arg, stack, store, module),
- I64Load16S(arg) => mem_load!(i16, i64, arg, stack, store, module),
- I64Load16U(arg) => mem_load!(u16, i64, arg, stack, store, module),
- I64Load32S(arg) => mem_load!(i32, i64, arg, stack, store, module),
- I64Load32U(arg) => mem_load!(u32, i64, arg, stack, store, module),
+ I32Load { mem_addr, offset } => mem_load!(i32, (mem_addr, offset), stack, store, module),
+ I64Load { mem_addr, offset } => mem_load!(i64, (mem_addr, offset), stack, store, module),
+ F32Load { mem_addr, offset } => mem_load!(f32, (mem_addr, offset), stack, store, module),
+ F64Load { mem_addr, offset } => mem_load!(f64, (mem_addr, offset), stack, store, module),
+ I32Load8S { mem_addr, offset } => mem_load!(i8, i32, (mem_addr, offset), stack, store, module),
+ I32Load8U { mem_addr, offset } => mem_load!(u8, i32, (mem_addr, offset), stack, store, module),
+ I32Load16S { mem_addr, offset } => mem_load!(i16, i32, (mem_addr, offset), stack, store, module),
+ I32Load16U { mem_addr, offset } => mem_load!(u16, i32, (mem_addr, offset), stack, store, module),
+ I64Load8S { mem_addr, offset } => mem_load!(i8, i64, (mem_addr, offset), stack, store, module),
+ I64Load8U { mem_addr, offset } => mem_load!(u8, i64, (mem_addr, offset), stack, store, module),
+ I64Load16S { mem_addr, offset } => mem_load!(i16, i64, (mem_addr, offset), stack, store, module),
+ I64Load16U { mem_addr, offset } => mem_load!(u16, i64, (mem_addr, offset), stack, store, module),
+ I64Load32S { mem_addr, offset } => mem_load!(i32, i64, (mem_addr, offset), stack, store, module),
+ I64Load32U { mem_addr, offset } => mem_load!(u32, i64, (mem_addr, offset), stack, store, module),
I64Eqz => comp_zero!(==, i64, stack),
I32Eqz => comp_zero!(==, i32, stack),
@@ -498,29 +481,29 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
I32LtS => comp!(<, i32, stack),
I64LtS => comp!(<, i64, stack),
- I32LtU => comp!(<, i32, u32, stack),
- I64LtU => comp!(<, i64, u64, stack),
+ I32LtU => comp!(<, u32, stack),
+ I64LtU => comp!(<, u64, stack),
F32Lt => comp!(<, f32, stack),
F64Lt => comp!(<, f64, stack),
I32LeS => comp!(<=, i32, stack),
I64LeS => comp!(<=, i64, stack),
- I32LeU => comp!(<=, i32, u32, stack),
- I64LeU => comp!(<=, i64, u64, stack),
+ I32LeU => comp!(<=, u32, stack),
+ I64LeU => comp!(<=, u64, stack),
F32Le => comp!(<=, f32, stack),
F64Le => comp!(<=, f64, stack),
I32GeS => comp!(>=, i32, stack),
I64GeS => comp!(>=, i64, stack),
- I32GeU => comp!(>=, i32, u32, stack),
- I64GeU => comp!(>=, i64, u64, stack),
+ I32GeU => comp!(>=, u32, stack),
+ I64GeU => comp!(>=, u64, stack),
F32Ge => comp!(>=, f32, stack),
F64Ge => comp!(>=, f64, stack),
I32GtS => comp!(>, i32, stack),
I64GtS => comp!(>, i64, stack),
- I32GtU => comp!(>, i32, u32, stack),
- I64GtU => comp!(>, i64, u64, stack),
+ I32GtU => comp!(>, u32, stack),
+ I64GtU => comp!(>, u64, stack),
F32Gt => comp!(>, f32, stack),
F64Gt => comp!(>, f64, stack),
@@ -545,13 +528,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
// these can trap
I32DivS => checked_int_arithmetic!(checked_div, i32, stack),
I64DivS => checked_int_arithmetic!(checked_div, i64, stack),
- I32DivU => checked_int_arithmetic!(checked_div, i32, u32, stack),
- I64DivU => checked_int_arithmetic!(checked_div, i64, u64, stack),
+ I32DivU => checked_int_arithmetic!(checked_div, u32, stack),
+ I64DivU => checked_int_arithmetic!(checked_div, u64, stack),
I32RemS => checked_int_arithmetic!(checked_wrapping_rem, i32, stack),
I64RemS => checked_int_arithmetic!(checked_wrapping_rem, i64, stack),
- I32RemU => checked_int_arithmetic!(checked_wrapping_rem, i32, u32, stack),
- I64RemU => checked_int_arithmetic!(checked_wrapping_rem, i64, u64, stack),
+ I32RemU => checked_int_arithmetic!(checked_wrapping_rem, u32, stack),
+ I64RemU => checked_int_arithmetic!(checked_wrapping_rem, u64, stack),
I32And => arithmetic!(bitand, i32, stack),
I64And => arithmetic!(bitand, i64, stack),
@@ -563,8 +546,8 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
I64Shl => arithmetic!(wasm_shl, i64, stack),
I32ShrS => arithmetic!(wasm_shr, i32, stack),
I64ShrS => arithmetic!(wasm_shr, i64, stack),
- I32ShrU => arithmetic!(wasm_shr, u32, i32, stack),
- I64ShrU => arithmetic!(wasm_shr, u64, i64, stack),
+ I32ShrU => arithmetic!(wasm_shr, u32, stack),
+ I64ShrU => arithmetic!(wasm_shr, u64, stack),
I32Rotl => arithmetic!(wasm_rotl, i32, stack),
I64Rotl => arithmetic!(wasm_rotl, i64, stack),
I32Rotr => arithmetic!(wasm_rotr, i32, stack),
@@ -581,16 +564,16 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
F32ConvertI64S => conv!(i64, f32, stack),
F64ConvertI32S => conv!(i32, f64, stack),
F64ConvertI64S => conv!(i64, f64, stack),
- F32ConvertI32U => conv!(i32, u32, f32, stack),
- F32ConvertI64U => conv!(i64, u64, f32, stack),
- F64ConvertI32U => conv!(i32, u32, f64, stack),
- F64ConvertI64U => conv!(i64, u64, f64, stack),
- I32Extend8S => conv!(i32, i8, i32, stack),
- I32Extend16S => conv!(i32, i16, i32, stack),
- I64Extend8S => conv!(i64, i8, i64, stack),
- I64Extend16S => conv!(i64, i16, i64, stack),
- I64Extend32S => conv!(i64, i32, i64, stack),
- I64ExtendI32U => conv!(i32, u32, i64, stack),
+ F32ConvertI32U => conv!(u32, f32, stack),
+ F32ConvertI64U => conv!(u64, f32, stack),
+ F64ConvertI32U => conv!(u32, f64, stack),
+ F64ConvertI64U => conv!(u64, f64, stack),
+ I32Extend8S => conv!(i8, i32, stack),
+ I32Extend16S => conv!(i16, i32, stack),
+ I64Extend8S => conv!(i8, i64, stack),
+ I64Extend16S => conv!(i16, i64, stack),
+ I64Extend32S => conv!(i32, i64, stack),
+ I64ExtendI32U => conv!(u32, i64, stack),
I64ExtendI32S => conv!(i32, i64, stack),
I32WrapI64 => conv!(i64, i32, stack),
@@ -680,6 +663,48 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
I64TruncSatF64S => arithmetic_single!(trunc, f64, i64, stack),
I64TruncSatF64U => arithmetic_single!(trunc, f64, u64, stack),
+ // custom instructions
+ LocalGet2(a, b) => {
+ stack.values.extend_from_slice(&[cf.get_local(*a as usize), cf.get_local(*b as usize)]);
+ }
+ LocalGet3(a, b, c) => {
+ stack.values.extend_from_slice(&[
+ cf.get_local(*a as usize),
+ cf.get_local(*b as usize),
+ cf.get_local(*c as usize),
+ ]);
+ }
+ // LocalGet4(a, b, c, d) => {
+ // stack.values.extend_from_slice(&[
+ // cf.get_local(*a as usize),
+ // cf.get_local(*b as usize),
+ // cf.get_local(*c as usize),
+ // cf.get_local(*d as usize),
+ // ]);
+ // }
+ LocalTeeGet(a, b) => {
+ #[inline]
+ fn local_tee_get(cf: &mut CallFrame, stack: &mut Stack, a: u32, b: u32) -> Result<()> {
+ let last = *stack
+ .values
+ .last()
+ .expect("localtee: stack is empty. this should have been validated by the parser");
+ cf.set_local(a as usize, last);
+ stack.values.push(cf.get_local(b as usize));
+ Ok(())
+ }
+ local_tee_get(cf, stack, *a, *b)?;
+ }
+ LocalGetSet(a, b) => {
+ let a = cf.get_local(*a as usize);
+ cf.set_local(*b as usize, a);
+ }
+ I64XorConstRotl(rotate_by) => {
+ let val = stack.values.pop_t::<i64>()?;
+ let mask = stack.values.pop_t::<i64>()?;
+ let res = val ^ mask;
+ stack.values.push(res.rotate_left(*rotate_by as u32).into());
+ }
i => {
cold();
log::error!("unimplemented instruction: {:?}", i);
diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
index 91c74b5..5b9471e 100644
--- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
+++ b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs
@@ -9,85 +9,26 @@ pub(super) trait NoStdFloatExt {
fn copysign(self, other: Self) -> Self;
}
+#[rustfmt::skip]
impl NoStdFloatExt for f64 {
- #[inline]
- fn round(self) -> Self {
- libm::round(self)
- }
-
- #[inline]
- fn abs(self) -> Self {
- libm::fabs(self)
- }
-
- #[inline]
- fn signum(self) -> Self {
- libm::copysign(1.0, self)
- }
-
- #[inline]
- fn ceil(self) -> Self {
- libm::ceil(self)
- }
-
- #[inline]
- fn floor(self) -> Self {
- libm::floor(self)
- }
-
- #[inline]
- fn trunc(self) -> Self {
- libm::trunc(self)
- }
-
- #[inline]
- fn sqrt(self) -> Self {
- libm::sqrt(self)
- }
-
- #[inline]
- fn copysign(self, other: Self) -> Self {
- libm::copysign(self, other)
- }
+ #[inline] fn round(self) -> Self { libm::round(self) }
+ #[inline] fn abs(self) -> Self { libm::fabs(self) }
+ #[inline] fn signum(self) -> Self { libm::copysign(1.0, self) }
+ #[inline] fn ceil(self) -> Self { libm::ceil(self) }
+ #[inline] fn floor(self) -> Self { libm::floor(self) }
+ #[inline] fn trunc(self) -> Self { libm::trunc(self) }
+ #[inline] fn sqrt(self) -> Self { libm::sqrt(self) }
+ #[inline] fn copysign(self, other: Self) -> Self { libm::copysign(self, other) }
}
-impl NoStdFloatExt for f32 {
- #[inline]
- fn round(self) -> Self {
- libm::roundf(self)
- }
- #[inline]
- fn abs(self) -> Self {
- libm::fabsf(self)
- }
-
- #[inline]
- fn signum(self) -> Self {
- libm::copysignf(1.0, self)
- }
-
- #[inline]
- fn ceil(self) -> Self {
- libm::ceilf(self)
- }
-
- #[inline]
- fn floor(self) -> Self {
- libm::floorf(self)
- }
-
- #[inline]
- fn trunc(self) -> Self {
- libm::truncf(self)
- }
-
- #[inline]
- fn sqrt(self) -> Self {
- libm::sqrtf(self)
- }
-
- #[inline]
- fn copysign(self, other: Self) -> Self {
- libm::copysignf(self, other)
- }
+#[rustfmt::skip]
+impl NoStdFloatExt for f32 {
+ #[inline] fn round(self) -> Self { libm::roundf(self) }
+ #[inline] fn abs(self) -> Self { libm::fabsf(self) }
+ #[inline] fn signum(self) -> Self { libm::copysignf(1.0, self) }
+ #[inline] fn ceil(self) -> Self { libm::ceilf(self) }
+ #[inline] fn floor(self) -> Self { libm::floorf(self) }
+ #[inline] fn trunc(self) -> Self { libm::truncf(self) }
+ #[inline] fn sqrt(self) -> Self { libm::sqrtf(self) }
+ #[inline] fn copysign(self, other: Self) -> Self { libm::copysignf(self, other) }
}
diff --git a/crates/tinywasm/src/runtime/interpreter/traits.rs b/crates/tinywasm/src/runtime/interpreter/traits.rs
index 523265b..7aeb3b7 100644
--- a/crates/tinywasm/src/runtime/interpreter/traits.rs
+++ b/crates/tinywasm/src/runtime/interpreter/traits.rs
@@ -24,23 +24,15 @@ macro_rules! impl_wasm_float_ops {
x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros
x if (0.0..=0.5).contains(&x) => 0.0,
x if (-0.5..0.0).contains(&x) => -0.0,
- // x => x.round(),
x => {
// Handle normal and halfway cases
let rounded = x.round();
let diff = (x - rounded).abs();
-
- if diff == 0.5 {
- // Halfway case: round to even
- if rounded % 2.0 == 0.0 {
- rounded // Already even
- } else {
- rounded - x.signum() // Make even
- }
- } else {
- // Normal case
- rounded
+ if diff != 0.5 || rounded % 2.0 == 0.0 {
+ return rounded
}
+
+ rounded - x.signum() // Make even
}
}
}
@@ -49,15 +41,11 @@ macro_rules! impl_wasm_float_ops {
// Based on f32::minimum (which is not yet stable)
#[inline]
fn tw_minimum(self, other: Self) -> Self {
- if self < other {
- self
- } else if other < self {
- other
- } else if self == other {
- if self.is_sign_negative() && other.is_sign_positive() { self } else { other }
- } else {
- // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
- self + other
+ match self.partial_cmp(&other) {
+ Some(core::cmp::Ordering::Less) => self,
+ Some(core::cmp::Ordering::Greater) => other,
+ Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { self } else { other },
+ None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
}
}
@@ -65,15 +53,11 @@ macro_rules! impl_wasm_float_ops {
// Based on f32::maximum (which is not yet stable)
#[inline]
fn tw_maximum(self, other: Self) -> Self {
- if self > other {
- self
- } else if other > self {
- other
- } else if self == other {
- if self.is_sign_negative() && other.is_sign_positive() { other } else { self }
- } else {
- // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
- self + other
+ match self.partial_cmp(&other) {
+ Some(core::cmp::Ordering::Greater) => self,
+ Some(core::cmp::Ordering::Less) => other,
+ Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { other } else { self },
+ None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting.
}
}
}
diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs
index 1c441a8..12270d1 100644
--- a/crates/tinywasm/src/runtime/stack/call_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/call_stack.rs
@@ -1,5 +1,5 @@
use alloc::{boxed::Box, rc::Rc, vec::Vec};
-use tinywasm_types::{ModuleInstanceAddr, WasmFunction};
+use tinywasm_types::{Instruction, ModuleInstanceAddr, WasmFunction};
use crate::runtime::{BlockType, RawWasmValue};
use crate::unlikely;
@@ -142,4 +142,14 @@ impl CallFrame {
pub(crate) fn get_local(&self, local_index: usize) -> RawWasmValue {
self.locals[local_index]
}
+
+ #[inline(always)]
+ pub(crate) fn instructions(&self) -> &[Instruction] {
+ &self.func_instance.0.instructions
+ }
+
+ #[inline(always)]
+ pub(crate) fn current_instruction(&self) -> &Instruction {
+ &self.func_instance.0.instructions[self.instr_ptr]
+ }
}
diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs
index 9649177..5903228 100644
--- a/crates/tinywasm/src/runtime/stack/value_stack.rs
+++ b/crates/tinywasm/src/runtime/stack/value_stack.rs
@@ -25,11 +25,22 @@ impl ValueStack {
#[inline]
pub(crate) fn extend_from_typed(&mut self, values: &[WasmValue]) {
- if values.is_empty() {
+ self.stack.extend(values.iter().map(|v| RawWasmValue::from(*v)));
+ }
+
+ #[inline]
+ pub(crate) fn extend_from_slice(&mut self, values: &[RawWasmValue]) {
+ self.stack.extend_from_slice(values);
+ }
+
+ #[inline]
+ pub(crate) fn replace_top(&mut self, func: impl FnOnce(RawWasmValue) -> RawWasmValue) {
+ let len = self.stack.len();
+ if unlikely(len == 0) {
return;
}
-
- self.stack.extend(values.iter().map(|v| RawWasmValue::from(*v)));
+ let top = self.stack[len - 1];
+ self.stack[len - 1] = func(top);
}
#[inline]
@@ -52,20 +63,14 @@ impl ValueStack {
self.stack.drain(remove_start_index..remove_end_index);
}
- #[inline]
+ #[inline(always)]
pub(crate) fn push(&mut self, value: RawWasmValue) {
self.stack.push(value);
}
#[inline]
- pub(crate) fn last(&self) -> Result<&RawWasmValue> {
- match self.stack.last() {
- Some(v) => Ok(v),
- None => {
- cold();
- Err(Error::ValueStackUnderflow)
- }
- }
+ pub(crate) fn last(&self) -> Option<&RawWasmValue> {
+ self.stack.last()
}
#[inline]
diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs
index bc78adb..4835eab 100644
--- a/crates/tinywasm/src/runtime/value.rs
+++ b/crates/tinywasm/src/runtime/value.rs
@@ -7,40 +7,43 @@ use tinywasm_types::{ValType, WasmValue};
///
/// See [`WasmValue`] for the public representation.
#[derive(Clone, Copy, Default, PartialEq, Eq)]
-#[repr(transparent)]
-pub struct RawWasmValue(u64);
+// pub struct RawWasmValue([u8; 16]);
+pub struct RawWasmValue([u8; 8]);
impl Debug for RawWasmValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- write!(f, "RawWasmValue({})", self.0 as i64) // cast to i64 so at least negative numbers for i32 and i64 are printed correctly
+ write!(f, "RawWasmValue({})", 0)
}
}
impl RawWasmValue {
#[inline(always)]
- pub fn raw_value(&self) -> u64 {
+ pub fn raw_value(&self) -> [u8; 8] {
self.0
}
#[inline]
pub fn attach_type(self, ty: ValType) -> WasmValue {
match ty {
- ValType::I32 => WasmValue::I32(self.0 as i32),
- ValType::I64 => WasmValue::I64(self.0 as i64),
- ValType::F32 => WasmValue::F32(f32::from_bits(self.0 as u32)),
- ValType::F64 => WasmValue::F64(f64::from_bits(self.0)),
+ ValType::I32 => WasmValue::I32(self.into()),
+ ValType::I64 => WasmValue::I64(self.into()),
+ ValType::F32 => WasmValue::F32(f32::from_bits(self.into())),
+ ValType::F64 => WasmValue::F64(f64::from_bits(self.into())),
+ // ValType::V128 => WasmValue::V128(self.into()),
ValType::RefExtern => {
- if self.0 == -1i64 as u64 {
+ let val: i64 = self.into();
+ if val < 0 {
WasmValue::RefNull(ValType::RefExtern)
} else {
- WasmValue::RefExtern(self.0 as u32)
+ WasmValue::RefExtern(val as u32)
}
}
ValType::RefFunc => {
- if self.0 == -1i64 as u64 {
+ let val: i64 = self.into();
+ if val < 0 {
WasmValue::RefNull(ValType::RefFunc)
} else {
- WasmValue::RefFunc(self.0 as u32)
+ WasmValue::RefFunc(val as u32)
}
}
}
@@ -51,13 +54,14 @@ impl From<WasmValue> for RawWasmValue {
#[inline]
fn from(v: WasmValue) -> Self {
match v {
- WasmValue::I32(i) => Self(i as u64),
- WasmValue::I64(i) => Self(i as u64),
- WasmValue::F32(i) => Self(i.to_bits() as u64),
- WasmValue::F64(i) => Self(i.to_bits()),
- WasmValue::RefExtern(v) => Self(v as i64 as u64),
- WasmValue::RefFunc(v) => Self(v as i64 as u64),
- WasmValue::RefNull(_) => Self(-1i64 as u64),
+ 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(i) => Self::from(i),
+ WasmValue::RefExtern(v) => Self::from(v as i64),
+ WasmValue::RefFunc(v) => Self::from(v as i64),
+ WasmValue::RefNull(_) => Self::from(-1i64),
}
}
}
@@ -68,8 +72,8 @@ macro_rules! impl_from_raw_wasm_value {
impl From<$type> for RawWasmValue {
#[inline]
fn from(value: $type) -> Self {
- #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :)
- Self($to_raw(value))
+ #[allow(clippy::redundant_closure_call)]
+ Self(u64::to_ne_bytes($to_raw(value)))
}
}
@@ -77,20 +81,32 @@ macro_rules! impl_from_raw_wasm_value {
impl From<RawWasmValue> for $type {
#[inline]
fn from(value: RawWasmValue) -> Self {
- #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :)
+ #[allow(clippy::redundant_closure_call)]
$from_raw(value.0)
}
}
};
}
-impl_from_raw_wasm_value!(i32, |x| x as u64, |x| x as i32);
-impl_from_raw_wasm_value!(i64, |x| x as u64, |x| x as i64);
-impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as u64, |x| f32::from_bits(x as u32));
-impl_from_raw_wasm_value!(f64, f64::to_bits, f64::from_bits);
+type RawValue = u64;
+type RawValueRep = [u8; 8];
-// used for memory load/store
-impl_from_raw_wasm_value!(i8, |x| x as u64, |x| x as i8);
-impl_from_raw_wasm_value!(i16, |x| x as u64, |x| x as i16);
-impl_from_raw_wasm_value!(u32, |x| x as u64, |x| x as u32);
-impl_from_raw_wasm_value!(u64, |x| x, |x| x);
+// This all looks like a lot of extra steps, but the compiler will optimize it all away.
+// The `u128` is used to make the conversion easier to write.
+impl_from_raw_wasm_value!(i32, |x| x as RawValue, |x: RawValueRep| i32::from_ne_bytes(x[0..4].try_into().unwrap()));
+impl_from_raw_wasm_value!(i64, |x| x as RawValue, |x: RawValueRep| i64::from_ne_bytes(x[0..8].try_into().unwrap()));
+impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as RawValue, |x: RawValueRep| f32::from_bits(u32::from_ne_bytes(
+ x[0..4].try_into().unwrap()
+)));
+impl_from_raw_wasm_value!(f64, |x| f64::to_bits(x) as RawValue, |x: RawValueRep| f64::from_bits(u64::from_ne_bytes(
+ x[0..8].try_into().unwrap()
+)));
+
+impl_from_raw_wasm_value!(u8, |x| x as RawValue, |x: RawValueRep| u8::from_ne_bytes(x[0..1].try_into().unwrap()));
+impl_from_raw_wasm_value!(u16, |x| x as RawValue, |x: RawValueRep| u16::from_ne_bytes(x[0..2].try_into().unwrap()));
+impl_from_raw_wasm_value!(u32, |x| x as RawValue, |x: RawValueRep| u32::from_ne_bytes(x[0..4].try_into().unwrap()));
+impl_from_raw_wasm_value!(u64, |x| x as RawValue, |x: RawValueRep| u64::from_ne_bytes(x[0..8].try_into().unwrap()));
+// impl_from_raw_wasm_value!(u128, |x| x, |x: RawValueRep| RawValue::from_ne_bytes(x));
+
+impl_from_raw_wasm_value!(i8, |x| x as RawValue, |x: RawValueRep| i8::from_ne_bytes(x[0..1].try_into().unwrap()));
+impl_from_raw_wasm_value!(i16, |x| x as RawValue, |x: RawValueRep| i16::from_ne_bytes(x[0..2].try_into().unwrap()));
diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs
index cbaed8d..ea820da 100644
--- a/crates/tinywasm/src/store/memory.rs
+++ b/crates/tinywasm/src/store/memory.rs
@@ -2,7 +2,7 @@ use alloc::vec;
use alloc::vec::Vec;
use tinywasm_types::{MemoryType, ModuleInstanceAddr};
-use crate::{Error, Result};
+use crate::{log, Error, Result};
const PAGE_SIZE: usize = 65536;
const MAX_PAGES: usize = 65536;
@@ -37,7 +37,7 @@ impl MemoryInstance {
Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() })
}
- pub(crate) fn store(&mut self, addr: usize, _align: usize, data: &[u8], len: usize) -> Result<()> {
+ pub(crate) fn store(&mut self, addr: usize, len: usize, data: &[u8]) -> Result<()> {
let Some(end) = addr.checked_add(len) else {
return Err(self.trap_oob(addr, data.len()));
};
@@ -67,7 +67,7 @@ impl MemoryInstance {
self.kind.page_count_max.unwrap_or(MAX_PAGES as u64) as usize
}
- pub(crate) fn load(&self, addr: usize, _align: usize, len: usize) -> Result<&[u8]> {
+ pub(crate) fn load(&self, addr: usize, len: usize) -> Result<&[u8]> {
let Some(end) = addr.checked_add(len) else {
return Err(self.trap_oob(addr, len));
};
@@ -80,7 +80,7 @@ impl MemoryInstance {
}
// this is a workaround since we can't use generic const expressions yet (https://github.com/rust-lang/rust/issues/76560)
- pub(crate) fn load_as<const SIZE: usize, T: MemLoadable<SIZE>>(&self, addr: usize, _align: usize) -> Result<T> {
+ pub(crate) fn load_as<const SIZE: usize, T: MemLoadable<SIZE>>(&self, addr: usize) -> Result<T> {
let Some(end) = addr.checked_add(SIZE) else {
return Err(self.trap_oob(addr, SIZE));
};
@@ -223,8 +223,8 @@ mod memory_instance_tests {
fn test_memory_store_and_load() {
let mut memory = create_test_memory();
let data_to_store = [1, 2, 3, 4];
- assert!(memory.store(0, 0, &data_to_store, data_to_store.len()).is_ok());
- let loaded_data = memory.load(0, 0, data_to_store.len()).unwrap();
+ assert!(memory.store(0, data_to_store.len(), &data_to_store).is_ok());
+ let loaded_data = memory.load(0, data_to_store.len()).unwrap();
assert_eq!(loaded_data, &data_to_store);
}
@@ -232,7 +232,7 @@ mod memory_instance_tests {
fn test_memory_store_out_of_bounds() {
let mut memory = create_test_memory();
let data_to_store = [1, 2, 3, 4];
- assert!(memory.store(memory.data.len(), 0, &data_to_store, data_to_store.len()).is_err());
+ assert!(memory.store(memory.data.len(), data_to_store.len(), &data_to_store).is_err());
}
#[test]
diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs
index ce02dd7..f4e0df9 100644
--- a/crates/tinywasm/src/store/mod.rs
+++ b/crates/tinywasm/src/store/mod.rs
@@ -172,11 +172,8 @@ impl Store {
/// Set the global at the actual index in the store
#[inline]
pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> {
- self.data
- .globals
- .get(addr)
- .ok_or_else(|| Self::not_found_error("global"))
- .map(|global| global.borrow_mut().value = value)
+ let global = self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global"));
+ global.map(|global| global.borrow_mut().value = value)
}
}
@@ -190,12 +187,10 @@ impl Store {
) -> Result<Vec<FuncAddr>> {
let func_count = self.data.funcs.len();
let mut func_addrs = Vec::with_capacity(func_count);
-
for (i, func) in funcs.into_iter().enumerate() {
self.data.funcs.push(FunctionInstance::new_wasm(func.wasm_function, idx));
func_addrs.push((i + func_count) as FuncAddr);
}
-
Ok(func_addrs)
}
@@ -264,10 +259,9 @@ impl Store {
let val = i64::from(global.borrow().value);
// check if the global is actually a null reference
- if val < 0 {
- None
- } else {
- Some(val as u32)
+ match val < 0 {
+ true => None,
+ false => Some(val as u32),
}
}
_ => return Err(Error::UnsupportedFeature(format!("const expression other than ref: {:?}", item))),
@@ -300,10 +294,7 @@ impl Store {
ElementKind::Passive => Some(init),
// this one is not available to the runtime but needs to be initialized to declare references
- ElementKind::Declared => {
- // a. Execute the instruction elm.drop i
- None
- }
+ ElementKind::Declared => None, // a. Execute the instruction elm.drop i
// this one is active, so we need to initialize it (essentially a `table.init` instruction)
ElementKind::Active { offset, table } => {
@@ -313,17 +304,17 @@ impl Store {
.copied()
.ok_or_else(|| Error::Other(format!("table {} not found for element {}", table, i)))?;
- if let Some(table) = self.data.tables.get_mut(table_addr as usize) {
- // In wasm 2.0, it's possible to call a function that hasn't been instantiated yet,
- // when using a partially initialized active element segments.
- // This isn't mentioned in the spec, but the "unofficial" testsuite has a test for it:
- // https://github.com/WebAssembly/testsuite/blob/5a1a590603d81f40ef471abba70a90a9ae5f4627/linking.wast#L264-L276
- // I have NO IDEA why this is allowed, but it is.
- if let Err(Error::Trap(trap)) = table.borrow_mut().init_raw(offset, &init) {
- return Ok((elem_addrs.into_boxed_slice(), Some(trap)));
- }
- } else {
+ let Some(table) = self.data.tables.get_mut(table_addr as usize) else {
return Err(Error::Other(format!("table {} not found for element {}", table, i)));
+ };
+
+ // In wasm 2.0, it's possible to call a function that hasn't been instantiated yet,
+ // when using a partially initialized active element segments.
+ // This isn't mentioned in the spec, but the "unofficial" testsuite has a test for it:
+ // https://github.com/WebAssembly/testsuite/blob/5a1a590603d81f40ef471abba70a90a9ae5f4627/linking.wast#L264-L276
+ // I have NO IDEA why this is allowed, but it is.
+ if let Err(Error::Trap(trap)) = table.borrow_mut().init_raw(offset, &init) {
+ return Ok((elem_addrs.into_boxed_slice(), Some(trap)));
}
// f. Execute the instruction elm.drop i
@@ -349,36 +340,30 @@ impl Store {
let data_count = self.data.datas.len();
let mut data_addrs = Vec::with_capacity(data_count);
for (i, data) in datas.into_iter().enumerate() {
- let data_val =
- match data.kind {
- tinywasm_types::DataKind::Active { mem: mem_addr, offset } => {
- // a. Assert: memidx == 0
- if mem_addr != 0 {
- return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string()));
- }
-
- let mem_addr = mem_addrs.get(mem_addr as usize).copied().ok_or_else(|| {
- Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))
- })?;
-
- let offset = self.eval_i32_const(&offset)?;
+ let data_val = match data.kind {
+ tinywasm_types::DataKind::Active { mem: mem_addr, offset } => {
+ // a. Assert: memidx == 0
+ if mem_addr != 0 {
+ return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string()));
+ }
- let mem = self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| {
- Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))
- })?;
+ let Some(mem_addr) = mem_addrs.get(mem_addr as usize) else {
+ return Err(Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)));
+ };
- // See comment for active element sections in the function above why we need to do this here
- if let Err(Error::Trap(trap)) =
- mem.borrow_mut().store(offset as usize, 0, &data.data, data.data.len())
- {
- return Ok((data_addrs.into_boxed_slice(), Some(trap)));
- }
+ let offset = self.eval_i32_const(&offset)?;
+ let Some(mem) = self.data.memories.get_mut(*mem_addr as usize) else {
+ return Err(Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)));
+ };
- // drop the data
- None
+ match mem.borrow_mut().store(offset as usize, data.data.len(), &data.data) {
+ Ok(()) => None,
+ Err(Error::Trap(trap)) => return Ok((data_addrs.into_boxed_slice(), Some(trap))),
+ Err(e) => return Err(e),
}
- tinywasm_types::DataKind::Passive => Some(data.data.to_vec()),
- };
+ }
+ tinywasm_types::DataKind::Passive => Some(data.data.to_vec()),
+ };
self.data.datas.push(DataInstance::new(data_val, idx));
data_addrs.push((i + data_count) as Addr);
@@ -417,10 +402,8 @@ impl Store {
let val = match const_instr {
I32Const(i) => *i,
GlobalGet(addr) => {
- let addr = *addr as usize;
- let global = self.data.globals[addr].clone();
- let val = global.borrow().value;
- i32::from(val)
+ let global = self.data.globals[*addr as usize].borrow();
+ i32::from(global.value)
}
_ => return Err(Error::Other("expected i32".to_string())),
};
@@ -441,17 +424,17 @@ impl Store {
I32Const(i) => RawWasmValue::from(*i),
I64Const(i) => RawWasmValue::from(*i),
GlobalGet(addr) => {
- let addr = module_global_addrs.get(*addr as usize).copied().ok_or_else(|| {
+ 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))
})?;
let global =
- self.data.globals.get(addr as usize).expect("global not found. This should be unreachable");
+ self.data.globals.get(*addr as usize).expect("global not found. This should be unreachable");
global.borrow().value
}
RefNull(t) => RawWasmValue::from(t.default_value()),
- RefFunc(idx) => RawWasmValue::from(module_func_addrs.get(*idx as usize).copied().ok_or_else(|| {
+ RefFunc(idx) => RawWasmValue::from(*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))
})?),
};
diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs
index 1b31999..d9dd9ad 100644
--- a/crates/tinywasm/src/store/table.rs
+++ b/crates/tinywasm/src/store/table.rs
@@ -1,4 +1,4 @@
-use crate::log;
+use crate::{log, unlikely};
use crate::{Error, Result, Trap};
use alloc::{vec, vec::Vec};
use tinywasm_types::*;
@@ -40,7 +40,7 @@ impl TableInstance {
pub(crate) fn grow_to_fit(&mut self, new_size: usize) -> Result<()> {
if new_size > self.elements.len() {
- if new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize {
+ if unlikely(new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize) {
return Err(crate::Trap::TableOutOfBounds { offset: new_size, len: 1, max: self.elements.len() }.into());
}
diff --git a/crates/tinywasm/tests/generated/2.0.csv b/crates/tinywasm/tests/generated/2.0.csv
index 495ea38..40ef2ae 100644
--- a/crates/tinywasm/tests/generated/2.0.csv
+++ b/crates/tinywasm/tests/generated/2.0.csv
@@ -1,3 +1,3 @@
0.3.0,26722,1161,[{"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":8,"failed":109},{"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":1},{"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":171,"failed":12},{"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":3928,"failed":522},{"name":"memory_fill.wast","passed":64,"failed":36},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":177,"failed":63},{"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":594,"failed":186},{"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.4.0,27549,334,[{"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":183,"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":720,"failed":60},{"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.4.1,27552,334,[{"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":720,"failed":60},{"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.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}]
diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs
index bbd2206..273d6ed 100644
--- a/crates/types/src/archive.rs
+++ b/crates/types/src/archive.rs
@@ -7,10 +7,8 @@ use rkyv::{
Deserialize,
};
-// 16 bytes
const TWASM_MAGIC_PREFIX: &[u8; 4] = b"TWAS";
const TWASM_VERSION: &[u8; 2] = b"01";
-
#[rustfmt::skip]
const TWASM_MAGIC: [u8; 16] = [ TWASM_MAGIC_PREFIX[0], TWASM_MAGIC_PREFIX[1], TWASM_MAGIC_PREFIX[2], TWASM_MAGIC_PREFIX[3], TWASM_VERSION[0], TWASM_VERSION[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs
index fc12b54..9923116 100644
--- a/crates/types/src/instructions.rs
+++ b/crates/types/src/instructions.rs
@@ -9,20 +9,50 @@ pub enum BlockArgs {
FuncType(u32),
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))]
+/// A packed representation of BlockArgs
+/// This is needed to keep the size of the Instruction enum small.
+/// Sadly, using #[repr(u8)] on BlockArgs itself is not possible because of the FuncType variant.
+pub struct BlockArgsPacked([u8; 5]); // Modifying this directly can cause runtime errors, but no UB
+impl BlockArgsPacked {
+ pub fn new(args: BlockArgs) -> Self {
+ let mut packed = [0; 5];
+ match args {
+ BlockArgs::Empty => packed[0] = 0,
+ BlockArgs::Type(t) => {
+ packed[0] = 1;
+ packed[1] = t.to_byte();
+ }
+ BlockArgs::FuncType(t) => {
+ packed[0] = 2;
+ packed[1..].copy_from_slice(&t.to_le_bytes());
+ }
+ }
+ Self(packed)
+ }
+ pub fn unpack(&self) -> BlockArgs {
+ match self.0[0] {
+ 0 => BlockArgs::Empty,
+ 1 => BlockArgs::Type(ValType::from_byte(self.0[1]).unwrap()),
+ 2 => BlockArgs::FuncType(u32::from_le_bytes(self.0[1..].try_into().unwrap())),
+ _ => unreachable!(),
+ }
+ }
+}
+
/// Represents a memory immediate in a WebAssembly memory instruction.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))]
pub struct MemoryArg {
pub offset: u64,
pub mem_addr: MemAddr,
- pub align: u8,
- pub align_max: u8,
}
type BrTableDefault = u32;
-type BrTableLen = usize;
-type EndOffset = usize;
-type ElseOffset = usize;
+type BrTableLen = u32;
+type EndOffset = u32;
+type ElseOffset = u32;
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))]
@@ -48,19 +78,47 @@ pub enum ConstInstruction {
/// This makes it easier to implement the label stack iteratively.
///
/// See <https://webassembly.github.io/spec/core/binary/instructions.html>
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))]
+// should be kept as small as possible (16 bytes max)
pub enum Instruction {
// Custom Instructions
BrLabel(LabelAddr),
+ // Not implemented yet
+ // LocalGet + I32Const + I32Add
+ // One of the most common patterns in the Rust compiler output
+ // I32LocalGetConstAdd(LocalAddr, i32),
+
+ // Not implemented yet
+ // LocalGet + I32Const + I32Store => I32LocalGetConstStore + I32Const
+ // Also common, helps us skip the stack entirely.
+ // Has to be followed by an I32Const instruction
+ // I32StoreLocal { local: LocalAddr, offset: i32, mem_addr: MemAddr },
+
+ // 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),
+
+ // Not implemented yet
+ // I32AddConst(i32),
+ // I32SubConst(i32),
+ // I64AddConst(i64),
+ // I64SubConst(i64),
+
// Control Instructions
// See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions>
Unreachable,
Nop,
Block(BlockArgs, EndOffset),
Loop(BlockArgs, EndOffset),
- If(BlockArgs, Option<ElseOffset>, EndOffset),
+ If(BlockArgsPacked, ElseOffset, EndOffset), // If else offset is 0 if there is no else block
Else(EndOffset),
EndBlockFrame,
EndFunc,
@@ -85,29 +143,29 @@ pub enum Instruction {
GlobalSet(GlobalAddr),
// Memory Instructions
- I32Load(MemoryArg),
- I64Load(MemoryArg),
- F32Load(MemoryArg),
- F64Load(MemoryArg),
- I32Load8S(MemoryArg),
- I32Load8U(MemoryArg),
- I32Load16S(MemoryArg),
- I32Load16U(MemoryArg),
- I64Load8S(MemoryArg),
- I64Load8U(MemoryArg),
- I64Load16S(MemoryArg),
- I64Load16U(MemoryArg),
- I64Load32S(MemoryArg),
- I64Load32U(MemoryArg),
- I32Store(MemoryArg),
- I64Store(MemoryArg),
- F32Store(MemoryArg),
- F64Store(MemoryArg),
- I32Store8(MemoryArg),
- I32Store16(MemoryArg),
- I64Store8(MemoryArg),
- I64Store16(MemoryArg),
- I64Store32(MemoryArg),
+ I32Load { offset: u64, mem_addr: MemAddr },
+ I64Load { offset: u64, mem_addr: MemAddr },
+ F32Load { offset: u64, mem_addr: MemAddr },
+ F64Load { offset: u64, mem_addr: MemAddr },
+ I32Load8S { offset: u64, mem_addr: MemAddr },
+ I32Load8U { offset: u64, mem_addr: MemAddr },
+ I32Load16S { offset: u64, mem_addr: MemAddr },
+ I32Load16U { offset: u64, mem_addr: MemAddr },
+ I64Load8S { offset: u64, mem_addr: MemAddr },
+ I64Load8U { offset: u64, mem_addr: MemAddr },
+ I64Load16S { offset: u64, mem_addr: MemAddr },
+ I64Load16U { offset: u64, mem_addr: MemAddr },
+ I64Load32S { offset: u64, mem_addr: MemAddr },
+ I64Load32U { offset: u64, mem_addr: MemAddr },
+ I32Store { offset: u64, mem_addr: MemAddr },
+ I64Store { offset: u64, mem_addr: MemAddr },
+ F32Store { offset: u64, mem_addr: MemAddr },
+ F64Store { offset: u64, mem_addr: MemAddr },
+ I32Store8 { offset: u64, mem_addr: MemAddr },
+ I32Store16 { offset: u64, mem_addr: MemAddr },
+ I64Store8 { offset: u64, mem_addr: MemAddr },
+ I64Store16 { offset: u64, mem_addr: MemAddr },
+ I64Store32 { offset: u64, mem_addr: MemAddr },
MemorySize(MemAddr, u8),
MemoryGrow(MemAddr, u8),
@@ -276,3 +334,51 @@ pub enum Instruction {
MemoryFill(MemAddr),
DataDrop(DataAddr),
}
+
+#[cfg(test)]
+mod test_blockargs_packed {
+ use super::*;
+
+ #[test]
+ fn test_empty() {
+ let args = BlockArgs::Empty;
+ let packed = BlockArgsPacked::new(args);
+ assert_eq!(packed.unpack(), BlockArgs::Empty);
+ }
+
+ #[test]
+ fn test_val_type_i32() {
+ let args = BlockArgs::Type(ValType::I32);
+ let packed = BlockArgsPacked::new(args);
+ assert_eq!(packed.unpack(), BlockArgs::Type(ValType::I32));
+ }
+
+ #[test]
+ fn test_val_type_i64() {
+ let args = BlockArgs::Type(ValType::I64);
+ let packed = BlockArgsPacked::new(args);
+ assert_eq!(packed.unpack(), BlockArgs::Type(ValType::I64));
+ }
+
+ #[test]
+ fn test_val_type_f32() {
+ let args = BlockArgs::Type(ValType::F32);
+ let packed = BlockArgsPacked::new(args);
+ assert_eq!(packed.unpack(), BlockArgs::Type(ValType::F32));
+ }
+
+ #[test]
+ fn test_val_type_f64() {
+ let args = BlockArgs::Type(ValType::F64);
+ let packed = BlockArgsPacked::new(args);
+ assert_eq!(packed.unpack(), BlockArgs::Type(ValType::F64));
+ }
+
+ #[test]
+ fn test_func_type() {
+ let func_type = 123; // Use an arbitrary u32 value
+ let args = BlockArgs::FuncType(func_type);
+ let packed = BlockArgsPacked::new(args);
+ assert_eq!(packed.unpack(), BlockArgs::FuncType(func_type));
+ }
+}
diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs
index 24fed3b..bcd43e5 100644
--- a/crates/types/src/value.rs
+++ b/crates/types/src/value.rs
@@ -16,7 +16,8 @@ pub enum WasmValue {
F32(f32),
/// A 64-bit float.
F64(f64),
-
+ // /// A 128-bit vector
+ // V128(u128),
RefExtern(ExternAddr),
RefFunc(FuncAddr),
RefNull(ValType),
@@ -47,6 +48,7 @@ impl WasmValue {
ValType::I64 => Self::I64(0),
ValType::F32 => Self::F32(0.0),
ValType::F64 => Self::F64(0.0),
+ // ValType::V128 => Self::V128(0),
ValType::RefFunc => Self::RefNull(ValType::RefFunc),
ValType::RefExtern => Self::RefNull(ValType::RefExtern),
}
@@ -89,6 +91,7 @@ impl Debug for WasmValue {
WasmValue::I64(i) => write!(f, "i64({})", i),
WasmValue::F32(i) => write!(f, "f32({})", i),
WasmValue::F64(i) => write!(f, "f64({})", i),
+ // WasmValue::V128(i) => write!(f, "v128.half({:?})", i),
WasmValue::RefExtern(addr) => write!(f, "ref.extern({:?})", addr),
WasmValue::RefFunc(addr) => write!(f, "ref.func({:?})", addr),
WasmValue::RefNull(ty) => write!(f, "ref.null({:?})", ty),
@@ -105,6 +108,7 @@ impl WasmValue {
Self::I64(_) => ValType::I64,
Self::F32(_) => ValType::F32,
Self::F64(_) => ValType::F64,
+ // Self::V128(_) => ValType::V128,
Self::RefExtern(_) => ValType::RefExtern,
Self::RefFunc(_) => ValType::RefFunc,
Self::RefNull(ty) => *ty,
@@ -124,6 +128,8 @@ pub enum ValType {
F32,
/// A 64-bit float.
F64,
+ /// A 128-bit vector
+ // V128,
/// A reference to a function.
RefFunc,
/// A reference to an external value.
@@ -135,6 +141,29 @@ impl ValType {
pub fn default_value(&self) -> WasmValue {
WasmValue::default_for(*self)
}
+
+ pub(crate) fn to_byte(self) -> u8 {
+ match self {
+ ValType::I32 => 0x7F,
+ ValType::I64 => 0x7E,
+ ValType::F32 => 0x7D,
+ ValType::F64 => 0x7C,
+ ValType::RefFunc => 0x70,
+ ValType::RefExtern => 0x6F,
+ }
+ }
+
+ pub(crate) fn from_byte(byte: u8) -> Option<Self> {
+ match byte {
+ 0x7F => Some(ValType::I32),
+ 0x7E => Some(ValType::I64),
+ 0x7D => Some(ValType::F32),
+ 0x7C => Some(ValType::F64),
+ 0x70 => Some(ValType::RefFunc),
+ 0x6F => Some(ValType::RefExtern),
+ _ => None,
+ }
+ }
}
macro_rules! impl_conversion_for_wasmvalue {
diff --git a/examples/rust/analyze.py b/examples/rust/analyze.py
new file mode 100644
index 0000000..a450a1a
--- /dev/null
+++ b/examples/rust/analyze.py
@@ -0,0 +1,36 @@
+import re
+import sys
+from collections import Counter
+
+seq_len = 5
+
+# Check if a file path was provided
+if len(sys.argv) < 2:
+ print("Usage: python script.py path/to/yourfile.wat")
+ sys.exit(1)
+
+# The first command line argument is the file path
+file_path = sys.argv[1]
+
+# Regex to match WASM operators, adjust as necessary
+operator_pattern = re.compile(r'\b[a-z0-9_]+\.[a-z0-9_]+\b')
+
+# Read the file
+with open(file_path, 'r') as file:
+ content = file.read()
+
+# Find all operators
+operators = operator_pattern.findall(content)
+
+# Generate sequences of three consecutive operators
+sequences = [' '.join(operators[i:i+seq_len]) for i in range(len(operators) - 2)]
+
+# Count occurrences of each sequence
+sequence_counts = Counter(sequences)
+
+# Sort sequences by their count, this time in ascending order for reverse display
+sorted_sequences = sorted(sequence_counts.items(), key=lambda x: x[1])
+
+# Print the sequences, now from least common to most common
+for sequence, count in sorted_sequences:
+ print(f"{sequence}: {count}")