From 6850e8bc669171c658696655f4f31996d6e934af Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Wed, 31 Jan 2024 00:03:42 +0100 Subject: [PATCH 01/24] chore: overall code cleanup Signed-off-by: Henry Gressmann --- .cargo/config.toml | 1 + BENCHMARKS.md | 6 +- Cargo.lock | 16 +++- Cargo.toml | 27 ++---- crates/benchmarks/Cargo.toml | 24 +++++ .../benchmarks/benches}/argon2id.rs | 6 +- .../benchmarks/benches}/fibonacci.rs | 14 +-- .../benchmarks/benches}/selfhosted.rs | 11 ++- .../benchmarks/benches}/util/mod.rs | 2 +- crates/parser/src/conversion.rs | 13 ++- crates/parser/src/error.rs | 26 +++++- crates/parser/src/lib.rs | 13 ++- crates/parser/src/module.rs | 66 ++++---------- crates/parser/src/std.rs | 2 +- crates/tinywasm/src/error.rs | 17 ++-- crates/tinywasm/src/func.rs | 6 +- crates/tinywasm/src/imports.rs | 23 ++--- crates/tinywasm/src/instance.rs | 6 +- crates/tinywasm/src/lib.rs | 35 ++++---- crates/tinywasm/src/module.rs | 3 +- crates/tinywasm/src/reference.rs | 17 ++-- .../src/runtime/interpreter/macros.rs | 82 +++++++++-------- .../tinywasm/src/runtime/interpreter/mod.rs | 60 +++++-------- .../src/runtime/interpreter/no_std_floats.rs | 6 +- .../src/runtime/interpreter/traits.rs | 18 ++-- crates/tinywasm/src/runtime/mod.rs | 3 +- crates/tinywasm/src/runtime/stack/blocks.rs | 2 + .../tinywasm/src/runtime/stack/call_stack.rs | 10 +-- .../tinywasm/src/runtime/stack/value_stack.rs | 1 - crates/tinywasm/src/runtime/value.rs | 7 +- crates/tinywasm/src/store/function.rs | 7 ++ crates/tinywasm/src/store/global.rs | 7 +- crates/tinywasm/src/store/memory.rs | 90 +++++++------------ crates/tinywasm/src/store/mod.rs | 48 +++++----- crates/tinywasm/src/store/table.rs | 1 - crates/tinywasm/tests/generated/mvp.csv | 1 + crates/tinywasm/tests/testsuite/indexmap.rs | 1 + crates/tinywasm/tests/testsuite/mod.rs | 15 +--- crates/tinywasm/tests/testsuite/run.rs | 36 +------- crates/tinywasm/tests/testsuite/util.rs | 25 ++++++ crates/types/src/instructions.rs | 15 ++-- crates/types/src/lib.rs | 73 ++++++--------- crates/types/src/value.rs | 3 +- examples/archive.rs | 2 +- 44 files changed, 377 insertions(+), 470 deletions(-) create mode 100644 crates/benchmarks/Cargo.toml rename {benches => crates/benchmarks/benches}/argon2id.rs (88%) rename {benches => crates/benchmarks/benches}/fibonacci.rs (80%) rename {benches => crates/benchmarks/benches}/selfhosted.rs (90%) rename {benches => crates/benchmarks/benches}/util/mod.rs (95%) diff --git a/.cargo/config.toml b/.cargo/config.toml index 807bc56..bfe56dd 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -6,3 +6,4 @@ test-2="test --package tinywasm --test test-two --release -- --enable " test-wast="test --package tinywasm --test test-wast -- --enable " test-wast-release="test --package tinywasm --test test-wast --release -- --enable " generate-charts="test --package tinywasm --test generate-charts -- --enable " +benchmark="bench -p benchmarks --bench" diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 78f05d7..12edd59 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -2,7 +2,7 @@ All benchmarks are run on a Ryzen 7 5800X with 32GB of RAM, running Linux 6.6. WebAssembly files are optimized using [wasm-opt](https://github.com/WebAssembly/binaryen), -and the benchmark code is available in the `benches` folder. +and the benchmark code is available in the `crates/benchmarks` folder. These are mainly preliminary benchmarks, and I will be adding more in the future that are also looking into memory usage and other metrics. @@ -67,7 +67,7 @@ After profiling and fixing some low-hanging fruits, I found the biggest bottlene Benchmarks are run using [Criterion.rs](https://github.com/bheisler/criterion.rs). To run a benchmark, use the following command: ```sh -$ cargo bench --bench +$ cargo benchmark ``` # Profiling @@ -75,7 +75,7 @@ $ cargo bench --bench To profile a benchmark, use the following command: ```sh -$ cargo flamegraph --bench -- --bench +$ cargo flamegraph -p benchmarks --bench -- --bench ``` This will generate a flamegraph in `flamegraph.svg` and a `perf.data` file. diff --git a/Cargo.lock b/Cargo.lock index e147db6..68ea040 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,6 +140,18 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "benchmarks" +version = "0.0.0" +dependencies = [ + "argon2", + "criterion", + "tinywasm", + "wasmer", + "wasmi", + "wat", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -2063,12 +2075,8 @@ dependencies = [ name = "tinywasm-root" version = "0.0.0" dependencies = [ - "argon2", "color-eyre", - "criterion", "tinywasm", - "wasmer", - "wasmi", "wat", ] diff --git a/Cargo.toml b/Cargo.toml index 49b73a5..b565c4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members=["crates/*"] +default-members=[".", "crates/tinywasm", "crates/types", "crates/parser"] resolver="2" [profile.wasm] @@ -25,31 +26,13 @@ edition="2021" name="wasm-rust" test=false -[[bench]] -name="selfhosted" -harness=false - -[[bench]] -name="fibonacci" -harness=false - - -[[bench]] -name="argon2id" -harness=false +[dev-dependencies] +color-eyre="0.6" +tinywasm={path="crates/tinywasm", features=["unsafe"]} +wat={version="1.0"} [profile.bench] opt-level=3 lto="thin" codegen-units=1 debug=true - -[dev-dependencies] -color-eyre="0.6" -criterion={version="0.5", features=["html_reports"]} - -tinywasm={path="crates/tinywasm", features=["unsafe"]} -wat={version="1.0"} -wasmi={version="0.31", features=["std"]} -wasmer={version="4.2", features=["cranelift", "singlepass"]} -argon2={version="0.5"} diff --git a/crates/benchmarks/Cargo.toml b/crates/benchmarks/Cargo.toml new file mode 100644 index 0000000..b9225c1 --- /dev/null +++ b/crates/benchmarks/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name="benchmarks" +publish=false +edition.workspace=true + +[dependencies] +criterion={version="0.5", features=["html_reports"]} +tinywasm={path="../../crates/tinywasm", features=["unsafe"]} +wat={version="1.0"} +wasmi={version="0.31", features=["std"]} +wasmer={version="4.2", features=["cranelift", "singlepass"]} +argon2={version="0.5"} + +[[bench]] +name="selfhosted" +harness=false + +[[bench]] +name="fibonacci" +harness=false + +[[bench]] +name="argon2id" +harness=false diff --git a/benches/argon2id.rs b/crates/benchmarks/benches/argon2id.rs similarity index 88% rename from benches/argon2id.rs rename to crates/benchmarks/benches/argon2id.rs index 4504812..7c1ffc5 100644 --- a/benches/argon2id.rs +++ b/crates/benchmarks/benches/argon2id.rs @@ -36,7 +36,7 @@ fn run_native(params: (i32, i32, i32)) { run_native(params.0, params.1, params.2) } -const ARGON2ID: &[u8] = include_bytes!("../examples/rust/out/argon2id.wasm"); +const ARGON2ID: &[u8] = include_bytes!("../../../examples/rust/out/argon2id.wasm"); fn criterion_benchmark(c: &mut Criterion) { let twasm = wasm_to_twasm(ARGON2ID); let params = (1000, 2, 1); @@ -47,8 +47,8 @@ fn criterion_benchmark(c: &mut Criterion) { group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(params), "argon2id"))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(&ARGON2ID, black_box(params), "argon2id"))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(&ARGON2ID, black_box(params), "argon2id"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); } criterion_group!( diff --git a/benches/fibonacci.rs b/crates/benchmarks/benches/fibonacci.rs similarity index 80% rename from benches/fibonacci.rs rename to crates/benchmarks/benches/fibonacci.rs index 1d1134a..38bbde9 100644 --- a/benches/fibonacci.rs +++ b/crates/benchmarks/benches/fibonacci.rs @@ -20,9 +20,9 @@ fn run_wasmer(wasm: &[u8], iterations: i32, name: &str) { let engine: Engine = wasmer::Singlepass::default().into(); let mut store = Store::default(); let import_object = imports! {}; - let module = wasmer::Module::from_binary(&engine, &wasm).expect("wasmer::Module::from_binary"); + let module = wasmer::Module::from_binary(&engine, 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::(&mut store, name).expect("get_function"); + let fib = instance.exports.get_typed_function::(&store, name).expect("get_function"); fib.call(&mut store, iterations).expect("call"); } @@ -45,7 +45,7 @@ fn run_native_recursive(n: i32) -> i32 { run_native_recursive(n - 1) + run_native_recursive(n - 2) } -const FIBONACCI: &[u8] = include_bytes!("../examples/rust/out/fibonacci.wasm"); +const FIBONACCI: &[u8] = include_bytes!("../../../examples/rust/out/fibonacci.wasm"); fn criterion_benchmark(c: &mut Criterion) { let twasm = wasm_to_twasm(FIBONACCI); @@ -53,8 +53,8 @@ fn criterion_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("fibonacci"); group.bench_function("native", |b| b.iter(|| run_native(black_box(60)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(60), "fibonacci"))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(&FIBONACCI, black_box(60), "fibonacci"))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(&FIBONACCI, black_box(60), "fibonacci"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(60), "fibonacci"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(60), "fibonacci"))); } { @@ -62,8 +62,8 @@ fn criterion_benchmark(c: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(5)); group.bench_function("native", |b| b.iter(|| run_native_recursive(black_box(26)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(26), "fibonacci_recursive"))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(&FIBONACCI, black_box(26), "fibonacci_recursive"))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(&FIBONACCI, black_box(26), "fibonacci_recursive"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(FIBONACCI, black_box(26), "fibonacci_recursive"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(FIBONACCI, black_box(26), "fibonacci_recursive"))); } } diff --git a/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs similarity index 90% rename from benches/selfhosted.rs rename to crates/benchmarks/benches/selfhosted.rs index 57146e7..b022fd1 100644 --- a/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -1,11 +1,10 @@ mod util; -use criterion::{criterion_group, criterion_main, Criterion}; - use crate::util::twasm_to_module; +use criterion::{criterion_group, criterion_main, Criterion}; fn run_native() { use tinywasm::*; - let module = tinywasm::Module::parse_bytes(include_bytes!("../examples/rust/out/print.wasm")).expect("parse"); + let module = tinywasm::Module::parse_bytes(include_bytes!("../../../examples/rust/out/print.wasm")).expect("parse"); let mut store = Store::default(); let mut imports = Imports::default(); imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(()))).expect("define"); @@ -46,18 +45,18 @@ fn run_wasmer(wasm: &[u8]) { "printi32" => Function::new_typed(&mut store, |_: i32| {}), }, }; - let module = wasmer::Module::from_binary(&engine, &wasm).expect("wasmer::Module::from_binary"); + let module = wasmer::Module::from_binary(&engine, wasm).expect("wasmer::Module::from_binary"); let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); let hello = instance.exports.get_function("hello").expect("get_function"); hello.call(&mut store, &[]).expect("call"); } -const TINYWASM: &[u8] = include_bytes!("../examples/rust/out/tinywasm.wasm"); +const TINYWASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm"); fn criterion_benchmark(c: &mut Criterion) { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); - group.bench_function("native", |b| b.iter(|| run_native())); + group.bench_function("native", |b| b.iter(run_native)); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); diff --git a/benches/util/mod.rs b/crates/benchmarks/benches/util/mod.rs similarity index 95% rename from benches/util/mod.rs rename to crates/benchmarks/benches/util/mod.rs index 03e2075..d6594b9 100644 --- a/benches/util/mod.rs +++ b/crates/benchmarks/benches/util/mod.rs @@ -10,7 +10,7 @@ pub fn wasm_to_twasm(wasm: &[u8]) -> Vec { #[inline] pub fn twasm_to_module(twasm: &[u8]) -> tinywasm::Module { - unsafe { TinyWasmModule::from_twasm_unchecked(&twasm) }.into() + unsafe { TinyWasmModule::from_twasm_unchecked(twasm) }.into() } pub fn tinywasm(twasm: &[u8]) -> (tinywasm::Store, tinywasm::ModuleInstance) { diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 7c10d62..38a2ada 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -131,7 +131,7 @@ pub(crate) fn convert_module_globals<'a, T: IntoIterator Result { +pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result { let kind = match export.kind { wasmparser::ExternalKind::Func => ExternalKind::Func, wasmparser::ExternalKind::Table => ExternalKind::Table, @@ -146,7 +146,7 @@ pub(crate) fn convert_module_export(export: wasmparser::Export) -> Result, mut validator: FuncValidator, ) -> Result { let locals_reader = func.get_locals_reader()?; @@ -205,18 +205,17 @@ 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 } } -pub(crate) fn process_const_operators(ops: OperatorsReader) -> Result { +pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result { let ops = ops.into_iter().collect::>>()?; // In practice, the len can never be something other than 2, // but we'll keep this here since it's part of the spec // Invalid modules will be rejected by the validator anyway (there are also tests for this in the testsuite) assert!(ops.len() >= 2); assert!(matches!(ops[ops.len() - 1], wasmparser::Operator::End)); - process_const_operator(ops[ops.len() - 2].clone()) } -pub fn process_const_operator(op: wasmparser::Operator) -> Result { +pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result { match op { wasmparser::Operator::RefNull { ty } => Ok(ConstInstruction::RefNull(convert_valtype(&ty))), wasmparser::Operator::RefFunc { function_index } => Ok(ConstInstruction::RefFunc(function_index)), @@ -229,7 +228,7 @@ pub fn process_const_operator(op: wasmparser::Operator) -> Result( +pub(crate) fn process_operators<'a>( mut offset: usize, ops: impl Iterator, wasmparser::BinaryReaderError>>, mut validator: FuncValidator, @@ -515,7 +514,6 @@ pub fn process_operators<'a>( return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", op))); } }; - instructions.push(res); } @@ -524,6 +522,5 @@ pub fn process_operators<'a>( } validator.finish(offset)?; - Ok(instructions.into_boxed_slice()) } diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs index 35bad28..76d806d 100644 --- a/crates/parser/src/error.rs +++ b/crates/parser/src/error.rs @@ -6,15 +6,35 @@ use wasmparser::Encoding; #[derive(Debug)] /// Errors that can occur when parsing a WebAssembly module pub enum ParseError { + /// An invalid type was encountered InvalidType, + /// An unsupported section was encountered UnsupportedSection(String), + /// A duplicate section was encountered DuplicateSection(String), + /// An empty section was encountered EmptySection(String), + /// An unsupported operator was encountered UnsupportedOperator(String), - ParseError { message: String, offset: usize }, + /// An error occurred while parsing the module + ParseError { + /// The error message + message: String, + /// The offset in the module where the error occurred + offset: usize, + }, + /// An invalid encoding was encountered InvalidEncoding(Encoding), - InvalidLocalCount { expected: u32, actual: u32 }, + /// An invalid local count was encountered + InvalidLocalCount { + /// The expected local count + expected: u32, + /// The actual local count + actual: u32, + }, + /// The end of the module was not reached EndNotReached, + /// An unknown error occurred Other(String), } @@ -48,4 +68,4 @@ impl From for ParseError { } } -pub type Result = core::result::Result; +pub(crate) type Result = core::result::Result; diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index c608232..8cc34db 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,6 +1,12 @@ #![no_std] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables)) +))] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] #![forbid(unsafe_code)] #![cfg_attr(not(feature = "std"), feature(error_in_core))] +//! See [`tinywasm`](https://docs.rs/tinywasm) for documentation. mod std; extern crate alloc; @@ -30,14 +36,17 @@ use wasmparser::Validator; pub use tinywasm_types::TinyWasmModule; -#[derive(Default)] +/// A WebAssembly parser +#[derive(Default, Debug)] pub struct Parser {} impl Parser { + /// Create a new parser instance pub fn new() -> Self { Self {} } + /// Parse a [`TinyWasmModule`] from bytes pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result { let wasm = wasm.as_ref(); let mut validator = Validator::new(); @@ -55,6 +64,7 @@ impl Parser { } #[cfg(feature = "std")] + /// Parse a [`TinyWasmModule`] from a file. Requires `std` feature. pub fn parse_module_file(&self, path: impl AsRef + Clone) -> Result { use alloc::format; let f = crate::std::fs::File::open(path.clone()) @@ -65,6 +75,7 @@ impl Parser { } #[cfg(feature = "std")] + /// Parse a [`TinyWasmModule`] from a stream. Requires `std` feature. pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result { use alloc::format; diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index f5c01ac..a18d343 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -6,58 +6,34 @@ use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instructio use wasmparser::{Payload, Validator}; #[derive(Debug, Clone)] -pub struct CodeSection { - pub locals: Box<[ValType]>, - pub body: Box<[Instruction]>, +pub(crate) struct CodeSection { + pub(crate) locals: Box<[ValType]>, + pub(crate) body: Box<[Instruction]>, } #[derive(Default)] -pub struct ModuleReader { - pub version: Option, - pub start_func: Option, - - pub func_types: Vec, - - // map from local function index to type index - pub code_type_addrs: Vec, - - pub exports: Vec, - pub code: Vec, - pub globals: Vec, - pub table_types: Vec, - pub memory_types: Vec, - pub imports: Vec, - pub data: Vec, - pub elements: Vec, - - // pub element_section: Option>, - pub end_reached: bool, -} - -impl Debug for ModuleReader { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("ModuleReader") - .field("version", &self.version) - .field("func_types", &self.func_types) - .field("func_addrs", &self.code_type_addrs) - .field("code", &self.code) - .field("exports", &self.exports) - .field("globals", &self.globals) - .field("table_types", &self.table_types) - .field("memory_types", &self.memory_types) - .field("import_section", &self.imports) - // .field("element_section", &self.element_section) - // .field("data_section", &self.data_section) - .finish() - } +pub(crate) struct ModuleReader { + pub(crate) version: Option, + pub(crate) start_func: Option, + pub(crate) func_types: Vec, + pub(crate) code_type_addrs: Vec, + pub(crate) exports: Vec, + pub(crate) code: Vec, + pub(crate) globals: Vec, + pub(crate) table_types: Vec, + pub(crate) memory_types: Vec, + pub(crate) imports: Vec, + pub(crate) data: Vec, + pub(crate) elements: Vec, + pub(crate) end_reached: bool, } impl ModuleReader { - pub fn new() -> ModuleReader { + pub(crate) fn new() -> ModuleReader { Self::default() } - pub fn process_payload(&mut self, payload: Payload, validator: &mut Validator) -> Result<()> { + pub(crate) fn process_payload(&mut self, payload: Payload<'_>, validator: &mut Validator) -> Result<()> { use wasmparser::Payload::*; match payload { @@ -191,10 +167,6 @@ impl ModuleReader { debug!("Found custom section"); debug!("Skipping custom section: {:?}", _reader.name()); } - // TagSection(tag) => { - // debug!("Found tag section"); - // validator.tag_section(&tag)?; - // } UnknownSection { .. } => return Err(ParseError::UnsupportedSection("Unknown section".into())), section => return Err(ParseError::UnsupportedSection(format!("Unsupported section: {:?}", section))), }; diff --git a/crates/parser/src/std.rs b/crates/parser/src/std.rs index 67152be..16a7058 100644 --- a/crates/parser/src/std.rs +++ b/crates/parser/src/std.rs @@ -2,4 +2,4 @@ extern crate std; #[cfg(feature = "std")] -pub use std::*; +pub(crate) use std::*; diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 4f68eaa..6b8119f 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -8,14 +8,6 @@ pub use tinywasm_parser::ParseError; /// Errors that can occur for TinyWasm operations #[derive(Debug)] pub enum Error { - #[cfg(feature = "std")] - /// An I/O error occurred - Io(crate::std::io::Error), - - #[cfg(feature = "parser")] - /// A parsing error occurred - ParseError(ParseError), - /// A WebAssembly trap occurred Trap(Trap), @@ -45,6 +37,14 @@ pub enum Error { /// The store is not the one that the module instance was instantiated in InvalidStore, + + #[cfg(feature = "std")] + /// An I/O error occurred + Io(crate::std::io::Error), + + #[cfg(feature = "parser")] + /// A parsing error occurred + ParseError(ParseError), } #[derive(Debug)] @@ -57,6 +57,7 @@ pub enum LinkingError { /// The import name name: String, }, + /// A mismatched import type was encountered IncompatibleImportType { /// The module name diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 2088494..faf7931 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -2,10 +2,8 @@ use crate::{log, runtime::RawWasmValue, unlikely, Function}; use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; use tinywasm_types::{FuncType, ModuleInstanceAddr, ValType, WasmValue}; -use crate::{ - runtime::{CallFrame, Stack}, - Error, FuncContext, Result, Store, -}; +use crate::runtime::{CallFrame, Stack}; +use crate::{Error, FuncContext, Result, Store}; #[derive(Debug)] /// A function handle diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index e273838..522a82d 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -1,18 +1,12 @@ -#![allow(dead_code)] - +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use core::fmt::Debug; -use crate::{ - func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple}, - log, LinkingError, Result, -}; -use alloc::{ - boxed::Box, - collections::BTreeMap, - rc::Rc, - string::{String, ToString}, - vec::Vec, -}; +use crate::func::{FromWasmValueTuple, IntoWasmValueTuple, ValTypesFromTuple}; +use crate::{log, LinkingError, Result}; use tinywasm_types::*; /// The internal representation of a function @@ -167,7 +161,8 @@ impl Extern { Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty }))) } - pub(crate) fn kind(&self) -> ExternalKind { + /// Get the kind of the external value + pub fn kind(&self) -> ExternalKind { match self { Self::Global { .. } => ExternalKind::Global, Self::Table { .. } => ExternalKind::Table, diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index cb12f1b..75bd20d 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -1,10 +1,8 @@ use alloc::{boxed::Box, format, rc::Rc, string::ToString}; use tinywasm_types::*; -use crate::{ - func::{FromWasmValueTuple, IntoWasmValueTuple}, - log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store, -}; +use crate::func::{FromWasmValueTuple, IntoWasmValueTuple}; +use crate::{log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store}; /// An instanciated WebAssembly module /// diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 79b111c..77ecb1e 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -81,34 +81,29 @@ use log; pub(crate) mod log { macro_rules! debug ( ($($tt:tt)*) => {{}} ); macro_rules! info ( ($($tt:tt)*) => {{}} ); - macro_rules! trace ( ($($tt:tt)*) => {{}} ); macro_rules! error ( ($($tt:tt)*) => {{}} ); pub(crate) use debug; pub(crate) use error; pub(crate) use info; - pub(crate) use trace; } mod error; -pub use error::*; - -mod store; -pub use store::*; - -mod module; -pub use module::Module; - -mod instance; -pub use instance::ModuleInstance; - -mod reference; -pub use reference::*; +pub use { + error::*, + func::{FuncHandle, FuncHandleTyped}, + imports::*, + instance::ModuleInstance, + module::Module, + reference::*, + store::*, +}; mod func; -pub use func::{FuncHandle, FuncHandleTyped}; - mod imports; -pub use imports::*; +mod instance; +mod module; +mod reference; +mod store; /// Runtime for executing WebAssembly modules. pub mod runtime; @@ -130,7 +125,7 @@ pub(crate) fn cold() {} pub(crate) fn unlikely(b: bool) -> bool { if b { - cold(); - } + cold() + }; b } diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs index 5a2c4f7..08e76da 100644 --- a/crates/tinywasm/src/module.rs +++ b/crates/tinywasm/src/module.rs @@ -1,6 +1,5 @@ -use tinywasm_types::TinyWasmModule; - use crate::{Imports, ModuleInstance, Result, Store}; +use tinywasm_types::TinyWasmModule; #[derive(Debug)] /// A WebAssembly Module diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index a34e30b..4c6d703 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -1,15 +1,12 @@ -use core::{ - cell::{Ref, RefCell, RefMut}, - ffi::CStr, -}; +use core::cell::{Ref, RefCell, RefMut}; +use core::ffi::CStr; + +use alloc::ffi::CString; +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; use crate::{GlobalInstance, MemoryInstance, Result}; -use alloc::{ - ffi::CString, - rc::Rc, - string::{String, ToString}, - vec::Vec, -}; use tinywasm_types::WasmValue; // This module essentially contains the public APIs to interact with the data stored in the store diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index a769b12..9333a4c 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -4,6 +4,23 @@ //! In some basic tests this generated better assembly than using generic functions, even when inlined. //! (Something to revisit in the future) +// Break to a block at the given index (relative to the current frame) +// If there is no block at the given index, return or call the parent function +// +// This is a bit hard to see from the spec, but it's vaild to use breaks to return +// from a function, so we need to check if the label stack is empty +macro_rules! break_to { + ($cf:ident, $stack:ident, $break_to_relative:ident) => {{ + if $cf.break_to(*$break_to_relative, &mut $stack.values).is_none() { + if $stack.call_stack.is_empty() { + return Ok(ExecResult::Return); + } else { + return Ok(ExecResult::Call); + } + } + }}; +} + /// Load a value from memory macro_rules! mem_load { ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ @@ -69,45 +86,28 @@ macro_rules! mem_store { /// Rust sadly doesn't have wrapping casts for floats yet, maybe never. /// Alternatively, https://crates.io/crates/az could be used for this but /// it's not worth the dependency. +#[rustfmt::skip] macro_rules! float_min_max { - (f32, i32) => { - (-2147483904.0_f32, 2147483648.0_f32) - }; - (f64, i32) => { - (-2147483649.0_f64, 2147483648.0_f64) - }; - (f32, u32) => { - (-1.0_f32, 4294967296.0_f32) // 2^32 - }; - (f64, u32) => { - (-1.0_f64, 4294967296.0_f64) // 2^32 - }; - (f32, i64) => { - (-9223373136366403584.0_f32, 9223372036854775808.0_f32) // 2^63 + 2^40 | 2^63 - }; - (f64, i64) => { - (-9223372036854777856.0_f64, 9223372036854775808.0_f64) // 2^63 + 2^40 | 2^63 - }; - (f32, u64) => { - (-1.0_f32, 18446744073709551616.0_f32) // 2^64 - }; - (f64, u64) => { - (-1.0_f64, 18446744073709551616.0_f64) // 2^64 - }; + (f32, i32) => {(-2147483904.0_f32, 2147483648.0_f32)}; + (f64, i32) => {(-2147483649.0_f64, 2147483648.0_f64)}; + (f32, u32) => {(-1.0_f32, 4294967296.0_f32)}; // 2^32 + (f64, u32) => {(-1.0_f64, 4294967296.0_f64)}; // 2^32 + (f32, i64) => {(-9223373136366403584.0_f32, 9223372036854775808.0_f32)}; // 2^63 + 2^40 | 2^63 + (f64, i64) => {(-9223372036854777856.0_f64, 9223372036854775808.0_f64)}; // 2^63 + 2^40 | 2^63 + (f32, u64) => {(-1.0_f32, 18446744073709551616.0_f32)}; // 2^64 + (f64, u64) => {(-1.0_f64, 18446744073709551616.0_f64)}; // 2^64 // other conversions are not allowed - ($from:ty, $to:ty) => { - compile_error!("invalid float conversion"); - }; + ($from:ty, $to:ty) => {compile_error!("invalid float conversion")}; } /// Convert a value on the stack macro_rules! conv { ($from:ty, $intermediate:ty, $to:ty, $stack:ident) => {{ - let a: $from = $stack.values.pop()?.into(); - $stack.values.push((a as $intermediate as $to).into()); + let a = $stack.values.pop_t::<$from>()? as $intermediate; + $stack.values.push((a as $to).into()); }}; ($from:ty, $to:ty, $stack:ident) => {{ - let a: $from = $stack.values.pop()?.into(); + let a = $stack.values.pop_t::<$from>()?; $stack.values.push((a as $to).into()); }}; } @@ -123,11 +123,11 @@ macro_rules! checked_conv_float { let (min, max) = float_min_max!($from, $intermediate); let a: $from = $stack.values.pop()?.into(); - if a.is_nan() { + if unlikely(a.is_nan()) { return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); } - if a <= min || a >= max { + if unlikely(a <= min || a >= max) { return Err(Error::Trap(crate::Trap::IntegerOverflow)); } @@ -158,9 +158,9 @@ macro_rules! comp_zero { /// Apply an arithmetic method to two values on the stack macro_rules! arithmetic { - ($op:ident, $ty:ty, $stack:ident) => {{ + ($op:ident, $ty:ty, $stack:ident) => { arithmetic!($op, $ty, $ty, $stack) - }}; + }; // also allow operators such as +, - ($op:tt, $ty:ty, $stack:ident) => {{ @@ -172,23 +172,20 @@ macro_rules! arithmetic { ($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; - let result = a.$op(b); - $stack.values.push((result as $to).into()); + $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: $ty = $stack.values.pop()?.into(); - let result = a.$op(); - $stack.values.push((result as $ty).into()); + let a = $stack.values.pop_t::<$ty>()?; + $stack.values.push((a.$op() as $ty).into()); }}; ($op:ident, $from:ty, $to:ty, $stack:ident) => {{ - let a: $from = $stack.values.pop()?.into(); - let result = a.$op(); - $stack.values.push((result as $to).into()); + let a = $stack.values.pop_t::<$from>()?; + $stack.values.push((a.$op() as $to).into()); }}; } @@ -215,6 +212,7 @@ macro_rules! checked_int_arithmetic { pub(super) use arithmetic; pub(super) use arithmetic_single; +pub(super) use break_to; pub(super) use checked_conv_float; pub(super) use checked_int_arithmetic; pub(super) use comp; diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 79abbf3..c8d8791 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -1,26 +1,23 @@ -use super::{InterpreterRuntime, Stack}; -use crate::{cold, log, unlikely}; -use crate::{ - runtime::{BlockType, CallFrame, LabelFrame}, - Error, FuncContext, ModuleInstance, Result, Store, Trap, -}; use alloc::format; use alloc::{string::ToString, vec::Vec}; use core::ops::{BitAnd, BitOr, BitXor, Neg}; use tinywasm_types::{ElementKind, ValType}; +use super::{InterpreterRuntime, Stack}; +use crate::runtime::{BlockType, CallFrame, LabelFrame}; +use crate::{cold, log, unlikely}; +use crate::{Error, FuncContext, ModuleInstance, Result, Store, Trap}; + +mod macros; +mod traits; +use {macros::*, traits::*}; + #[cfg(not(feature = "std"))] mod no_std_floats; #[cfg(not(feature = "std"))] #[allow(unused_imports)] -use no_std_floats::FExt; - -mod macros; -mod traits; - -use macros::*; -use traits::*; +use no_std_floats::NoStdFloatExt; impl InterpreterRuntime { // #[inline(always)] // a small 2-3% performance improvement in some cases @@ -67,23 +64,6 @@ enum ExecResult { Trap(crate::Trap), } -// Break to a block at the given index (relative to the current frame) -// If there is no block at the given index, return or call the parent function -// -// This is a bit hard to see from the spec, but it's vaild to use breaks to return -// from a function, so we need to check if the label stack is empty -macro_rules! break_to { - ($cf:ident, $stack:ident, $break_to_relative:ident) => {{ - if $cf.break_to(*$break_to_relative, &mut $stack.values).is_none() { - if $stack.call_stack.is_empty() { - return Ok(ExecResult::Return); - } else { - return Ok(ExecResult::Call); - } - } - }}; -} - /// Run a single step of the interpreter /// A seperate function is used so later, we can more easily implement /// a step-by-step debugger (using generators once they're stable?) @@ -96,6 +76,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M 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 + // See https://pliniker.github.io/post/dispatchers/ use tinywasm_types::Instruction::*; match &instrs[cf.instr_ptr] { Nop => { /* do nothing */ } @@ -600,22 +583,19 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M F64Floor => arithmetic_single!(floor, f64, stack), F32Trunc => arithmetic_single!(trunc, f32, stack), F64Trunc => arithmetic_single!(trunc, f64, stack), - F32Nearest => arithmetic_single!(wasm_nearest, f32, stack), - F64Nearest => arithmetic_single!(wasm_nearest, f64, stack), + F32Nearest => arithmetic_single!(tw_nearest, f32, stack), + F64Nearest => arithmetic_single!(tw_nearest, f64, stack), F32Sqrt => arithmetic_single!(sqrt, f32, stack), F64Sqrt => arithmetic_single!(sqrt, f64, stack), - F32Min => arithmetic!(wasm_min, f32, stack), - F64Min => arithmetic!(wasm_min, f64, stack), - F32Max => arithmetic!(wasm_max, f32, stack), - F64Max => arithmetic!(wasm_max, f64, stack), + F32Min => arithmetic!(tw_minimum, f32, stack), + F64Min => arithmetic!(tw_minimum, f64, stack), + F32Max => arithmetic!(tw_maximum, f32, stack), + F64Max => arithmetic!(tw_maximum, f64, stack), F32Copysign => arithmetic!(copysign, f32, stack), F64Copysign => arithmetic!(copysign, f64, stack), // no-op instructions since types are erased at runtime - I32ReinterpretF32 => {} - I64ReinterpretF64 => {} - F32ReinterpretI32 => {} - F64ReinterpretI64 => {} + I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {} // unsigned versions of these are a bit broken atm I32TruncF32S => checked_conv_float!(f32, i32, stack), diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs index 5620249..91c74b5 100644 --- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs +++ b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs @@ -1,4 +1,4 @@ -pub(super) trait FExt { +pub(super) trait NoStdFloatExt { fn round(self) -> Self; fn abs(self) -> Self; fn signum(self) -> Self; @@ -9,7 +9,7 @@ pub(super) trait FExt { fn copysign(self, other: Self) -> Self; } -impl FExt for f64 { +impl NoStdFloatExt for f64 { #[inline] fn round(self) -> Self { libm::round(self) @@ -50,7 +50,7 @@ impl FExt for f64 { libm::copysign(self, other) } } -impl FExt for f32 { +impl NoStdFloatExt for f32 { #[inline] fn round(self) -> Self { libm::roundf(self) diff --git a/crates/tinywasm/src/runtime/interpreter/traits.rs b/crates/tinywasm/src/runtime/interpreter/traits.rs index 06a97e3..523265b 100644 --- a/crates/tinywasm/src/runtime/interpreter/traits.rs +++ b/crates/tinywasm/src/runtime/interpreter/traits.rs @@ -5,20 +5,20 @@ where fn checked_wrapping_rem(self, rhs: Self) -> Option; } -pub(crate) trait WasmFloatOps { - fn wasm_min(self, other: Self) -> Self; - fn wasm_max(self, other: Self) -> Self; - fn wasm_nearest(self) -> Self; +pub(crate) trait TinywasmFloatExt { + fn tw_minimum(self, other: Self) -> Self; + fn tw_maximum(self, other: Self) -> Self; + fn tw_nearest(self) -> Self; } #[cfg(not(feature = "std"))] -use super::no_std_floats::FExt; +use super::no_std_floats::NoStdFloatExt; macro_rules! impl_wasm_float_ops { ($($t:ty)*) => ($( - impl WasmFloatOps for $t { + impl TinywasmFloatExt for $t { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fnearest - fn wasm_nearest(self) -> Self { + fn tw_nearest(self) -> Self { match self { x if x.is_nan() => x, // preserve NaN x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros @@ -48,7 +48,7 @@ macro_rules! impl_wasm_float_ops { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmin // Based on f32::minimum (which is not yet stable) #[inline] - fn wasm_min(self, other: Self) -> Self { + fn tw_minimum(self, other: Self) -> Self { if self < other { self } else if other < self { @@ -64,7 +64,7 @@ macro_rules! impl_wasm_float_ops { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fmax // Based on f32::maximum (which is not yet stable) #[inline] - fn wasm_max(self, other: Self) -> Self { + fn tw_maximum(self, other: Self) -> Self { if self > other { self } else if other > self { diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/runtime/mod.rs index 3b9a57c..8c22ce0 100644 --- a/crates/tinywasm/src/runtime/mod.rs +++ b/crates/tinywasm/src/runtime/mod.rs @@ -2,11 +2,10 @@ mod interpreter; mod stack; mod value; +use crate::Result; pub use stack::*; pub(crate) use value::RawWasmValue; -use crate::Result; - #[allow(rustdoc::private_intra_doc_links)] /// A WebAssembly runtime. /// diff --git a/crates/tinywasm/src/runtime/stack/blocks.rs b/crates/tinywasm/src/runtime/stack/blocks.rs index f302e59..c04632c 100644 --- a/crates/tinywasm/src/runtime/stack/blocks.rs +++ b/crates/tinywasm/src/runtime/stack/blocks.rs @@ -7,11 +7,13 @@ use crate::{unlikely, ModuleInstance}; pub(crate) struct Labels(Vec); // TODO: maybe Box<[LabelFrame]> by analyzing the lable count when parsing the module? impl Labels { + #[inline] pub(crate) fn new() -> Self { // this is somehow a lot faster than Vec::with_capacity(128) or even using Default::default() in the benchmarks Self(Vec::new()) } + #[inline] pub(crate) fn len(&self) -> usize { self.0.len() } diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index dcbfcac..a902833 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -1,14 +1,11 @@ -use crate::unlikely; -use crate::{ - runtime::{BlockType, RawWasmValue}, - Error, Result, Trap, -}; use alloc::{boxed::Box, rc::Rc, vec::Vec}; use tinywasm_types::{ModuleInstanceAddr, WasmFunction}; use super::{blocks::Labels, LabelFrame}; +use crate::runtime::{BlockType, RawWasmValue}; +use crate::unlikely; +use crate::{Error, Result, Trap}; -// minimum call stack size const CALL_STACK_SIZE: usize = 128; const CALL_STACK_MAX_SIZE: usize = 1024; @@ -51,7 +48,6 @@ impl CallStack { #[derive(Debug, Clone)] pub(crate) struct CallFrame { pub(crate) instr_ptr: usize, - // pub(crate) module: ModuleInstanceAddr, pub(crate) func_instance: (Rc, ModuleInstanceAddr), pub(crate) labels: Labels, pub(crate) locals: Box<[RawWasmValue]>, diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index 9b8f82d..cc35bc2 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -5,7 +5,6 @@ use alloc::vec::Vec; use tinywasm_types::{ValType, WasmValue}; pub(crate) const MIN_VALUE_STACK_SIZE: usize = 1024; -// pub(crate) const MAX_VALUE_STACK_SIZE: usize = 1024 * 1024; #[derive(Debug)] pub(crate) struct ValueStack { diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs index 5341361..bc78adb 100644 --- a/crates/tinywasm/src/runtime/value.rs +++ b/crates/tinywasm/src/runtime/value.rs @@ -1,5 +1,4 @@ use core::fmt::Debug; - use tinywasm_types::{ValType, WasmValue}; /// A raw wasm value. @@ -23,6 +22,7 @@ impl RawWasmValue { self.0 } + #[inline] pub fn attach_type(self, ty: ValType) -> WasmValue { match ty { ValType::I32 => WasmValue::I32(self.0 as i32), @@ -48,6 +48,7 @@ impl RawWasmValue { } impl From for RawWasmValue { + #[inline] fn from(v: WasmValue) -> Self { match v { WasmValue::I32(i) => Self(i as u64), @@ -65,6 +66,7 @@ macro_rules! impl_from_raw_wasm_value { ($type:ty, $to_raw:expr, $from_raw:expr) => { // Implement From<$type> for RawWasmValue impl From<$type> for RawWasmValue { + #[inline] fn from(value: $type) -> Self { #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :) Self($to_raw(value)) @@ -73,6 +75,7 @@ macro_rules! impl_from_raw_wasm_value { // Implement From for $type impl From for $type { + #[inline] fn from(value: RawWasmValue) -> Self { #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :) $from_raw(value.0) @@ -86,7 +89,7 @@ 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); -// convenience impls (not actually part of the spec) +// 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); diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs index 7508d00..ef370c2 100644 --- a/crates/tinywasm/src/store/function.rs +++ b/crates/tinywasm/src/store/function.rs @@ -1,4 +1,5 @@ use crate::Function; +use alloc::rc::Rc; use tinywasm_types::*; #[derive(Debug, Clone)] @@ -9,3 +10,9 @@ pub(crate) struct FunctionInstance { pub(crate) func: Function, pub(crate) owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions } + +impl FunctionInstance { + pub(crate) fn new_wasm(func: WasmFunction, owner: ModuleInstanceAddr) -> Self { + Self { func: Function::Wasm(Rc::new(func)), owner } + } +} diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs index 298a31e..1bcbad7 100644 --- a/crates/tinywasm/src/store/global.rs +++ b/crates/tinywasm/src/store/global.rs @@ -1,7 +1,7 @@ use alloc::{format, string::ToString}; use tinywasm_types::*; -use crate::{runtime::RawWasmValue, Error, Result}; +use crate::{runtime::RawWasmValue, unlikely, Error, Result}; /// A WebAssembly Global Instance /// @@ -18,12 +18,13 @@ impl GlobalInstance { Self { ty, value, _owner: owner } } + #[inline] pub(crate) fn get(&self) -> WasmValue { self.value.attach_type(self.ty.ty) } pub(crate) fn set(&mut self, val: WasmValue) -> Result<()> { - if val.val_type() != self.ty.ty { + if unlikely(val.val_type() != self.ty.ty) { return Err(Error::Other(format!( "global type mismatch: expected {:?}, got {:?}", self.ty.ty, @@ -31,7 +32,7 @@ impl GlobalInstance { ))); } - if !self.ty.mutable { + if unlikely(!self.ty.mutable) { return Err(Error::Other("global is immutable".to_string())); } diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 9b527d3..64e16c6 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -2,11 +2,11 @@ use alloc::vec; use alloc::vec::Vec; use tinywasm_types::{MemoryType, ModuleInstanceAddr}; -use crate::{cold, unlikely, Error, Result}; +use crate::{Error, Result}; -pub(crate) const PAGE_SIZE: usize = 65536; -pub(crate) const MAX_PAGES: usize = 65536; -pub(crate) const MAX_SIZE: u64 = PAGE_SIZE as u64 * MAX_PAGES as u64; +const PAGE_SIZE: usize = 65536; +const MAX_PAGES: usize = 65536; +const MAX_SIZE: u64 = PAGE_SIZE as u64 * MAX_PAGES as u64; /// A WebAssembly Memory Instance /// @@ -32,22 +32,18 @@ impl MemoryInstance { } } + #[cold] + fn trap_oob(&self, addr: usize, len: usize) -> Error { + 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<()> { let Some(end) = addr.checked_add(len) else { - cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: addr, - len: data.len(), - max: self.data.len(), - })); + return Err(self.trap_oob(addr, data.len())); }; - if unlikely(end > self.data.len() || end < addr) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: addr, - len: data.len(), - max: self.data.len(), - })); + if end > self.data.len() || end < addr { + return Err(self.trap_oob(addr, data.len())); } // WebAssembly doesn't require alignment for stores @@ -73,12 +69,11 @@ impl MemoryInstance { pub(crate) fn load(&self, addr: usize, _align: usize, len: usize) -> Result<&[u8]> { let Some(end) = addr.checked_add(len) else { - cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() })); + return Err(self.trap_oob(addr, len)); }; - if unlikely(end > self.data.len() || end < addr) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() })); + if end > self.data.len() || end < addr { + return Err(self.trap_oob(addr, len)); } Ok(&self.data[addr..end]) @@ -87,23 +82,21 @@ 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>(&self, addr: usize, _align: usize) -> Result { let Some(end) = addr.checked_add(SIZE) else { - cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len: SIZE, max: self.max_pages() })); + return Err(self.trap_oob(addr, SIZE)); }; - if unlikely(end > self.data.len()) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len: SIZE, max: self.data.len() })); + if end > self.data.len() { + return Err(self.trap_oob(addr, SIZE)); } + #[cfg(not(feature = "unsafe"))] + let val = T::from_le_bytes(self.data[addr..end].try_into().expect("slice size mismatch")); + #[cfg(feature = "unsafe")] - // WebAssembly doesn't require alignment for loads // SAFETY: we checked that `end` is in bounds above. All types that implement `Into` are valid // to load from unaligned addresses. let val = unsafe { core::ptr::read_unaligned(self.data[addr..end].as_ptr() as *const T) }; - #[cfg(not(feature = "unsafe"))] - let val = T::from_le_bytes(self.data[addr..end].try_into().expect("slice size mismatch")); - Ok(val) } @@ -112,11 +105,9 @@ impl MemoryInstance { } pub(crate) fn fill(&mut self, addr: usize, len: usize, val: u8) -> Result<()> { - let end = addr - .checked_add(len) - .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() }))?; - if unlikely(end > self.data.len()) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() })); + let end = addr.checked_add(len).ok_or_else(|| self.trap_oob(addr, len))?; + if end > self.data.len() { + return Err(self.trap_oob(addr, len)); } self.data[addr..end].fill(val); @@ -124,15 +115,9 @@ impl MemoryInstance { } pub(crate) fn copy_from_slice(&mut self, dst: usize, src: &[u8]) -> Result<()> { - let end = dst.checked_add(src.len()).ok_or_else(|| { - Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len: src.len(), max: self.data.len() }) - })?; - if unlikely(end > self.data.len()) { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: dst, - len: src.len(), - max: self.data.len(), - })); + let end = dst.checked_add(src.len()).ok_or_else(|| self.trap_oob(dst, src.len()))?; + if end > self.data.len() { + return Err(self.trap_oob(dst, src.len())); } self.data[dst..end].copy_from_slice(src); @@ -141,19 +126,15 @@ impl MemoryInstance { pub(crate) fn copy_within(&mut self, dst: usize, src: usize, len: usize) -> Result<()> { // Calculate the end of the source slice - let src_end = src - .checked_add(len) - .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: src, len, max: self.data.len() }))?; + let src_end = src.checked_add(len).ok_or_else(|| self.trap_oob(src, len))?; if src_end > self.data.len() { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: src, len, max: self.data.len() })); + return Err(self.trap_oob(src, len)); } // Calculate the end of the destination slice - let dst_end = dst - .checked_add(len) - .ok_or_else(|| Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len, max: self.data.len() }))?; + let dst_end = dst.checked_add(len).ok_or_else(|| self.trap_oob(dst, len))?; if dst_end > self.data.len() { - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { offset: dst, len, max: self.data.len() })); + return Err(self.trap_oob(dst, len)); } // Perform the copy @@ -170,7 +151,6 @@ impl MemoryInstance { } if new_pages as usize > self.max_pages() { - log::info!("memory size out of bounds: {}", new_pages); return None; } @@ -182,12 +162,8 @@ impl MemoryInstance { // Zero initialize the new pages self.data.resize(new_size, 0); self.page_count = new_pages as usize; - - log::debug!("memory was {} pages", current_pages); - log::debug!("memory grown by {} pages", pages_delta); - log::debug!("memory grown to {} pages", self.page_count); - - Some(current_pages.try_into().expect("memory size out of bounds, this should have been caught earlier")) + debug_assert!(current_pages <= i32::MAX as usize, "page count should never be greater than i32::MAX"); + Some(current_pages as i32) } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index c8c5d8a..ad24480 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -1,15 +1,10 @@ -use crate::log; use alloc::{boxed::Box, format, rc::Rc, string::ToString, vec::Vec}; -use core::{ - cell::RefCell, - sync::atomic::{AtomicUsize, Ordering}, -}; +use core::cell::RefCell; +use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; -use crate::{ - runtime::{self, InterpreterRuntime, RawWasmValue}, - Error, Function, ModuleInstance, Result, Trap, -}; +use crate::runtime::{self, InterpreterRuntime, RawWasmValue}; +use crate::{Error, Function, ModuleInstance, Result, Trap}; mod data; mod element; @@ -17,6 +12,7 @@ mod function; mod global; mod memory; mod table; + pub(crate) use {data::*, element::*, function::*, global::*, memory::*, table::*}; // global store id counter @@ -130,7 +126,7 @@ impl Store { let mut func_addrs = Vec::with_capacity(func_count); for (i, func) in funcs.into_iter().enumerate() { - self.data.funcs.push(FunctionInstance { func: Function::Wasm(Rc::new(func.wasm_function)), owner: idx }); + self.data.funcs.push(FunctionInstance::new_wasm(func.wasm_function, idx)); func_addrs.push((i + func_count) as FuncAddr); } @@ -156,9 +152,7 @@ impl Store { if let MemoryArch::I64 = mem.arch { return Err(Error::UnsupportedFeature("64-bit memories".to_string())); } - log::info!("adding memory: {:?}", mem); self.data.memories.push(Rc::new(RefCell::new(MemoryInstance::new(mem, idx)))); - mem_addrs.push((i + mem_count) as MemAddr); } Ok(mem_addrs) @@ -235,8 +229,6 @@ impl Store { .map(|item| Ok(TableElement::from(self.elem_addr(item, global_addrs, func_addrs)?))) .collect::>>()?; - log::error!("element kind: {:?}", element.kind); - let items = match element.kind { // doesn't need to be initialized, can be initialized lazily using the `table.init` instruction ElementKind::Passive => Some(init), @@ -400,55 +392,57 @@ impl Store { Ok(val) } + #[cold] + fn not_found_error(name: &str) -> Error { + Error::Other(format!("{} not found", name)) + } + /// Get the function at the actual index in the store pub(crate) fn get_func(&self, addr: usize) -> Result<&FunctionInstance> { - self.data.funcs.get(addr).ok_or_else(|| Error::Other(format!("function {} not found", addr))) + self.data.funcs.get(addr).ok_or_else(|| Self::not_found_error("function")) } /// Get the memory at the actual index in the store pub(crate) fn get_mem(&self, addr: usize) -> Result<&Rc>> { - self.data.memories.get(addr).ok_or_else(|| Error::Other(format!("memory {} not found", addr))) + self.data.memories.get(addr).ok_or_else(|| Self::not_found_error("memory")) } /// Get the table at the actual index in the store pub(crate) fn get_table(&self, addr: usize) -> Result<&Rc>> { - self.data.tables.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + self.data.tables.get(addr).ok_or_else(|| Self::not_found_error("table")) } /// Get the data at the actual index in the store pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> { - self.data.datas.get(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + self.data.datas.get(addr).ok_or_else(|| Self::not_found_error("data")) } /// Get the data at the actual index in the store pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> { - self.data.datas.get_mut(addr).ok_or_else(|| Error::Other(format!("table {} not found", addr))) + self.data.datas.get_mut(addr).ok_or_else(|| Self::not_found_error("data")) } /// Get the element at the actual index in the store pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> { - self.data.elements.get(addr).ok_or_else(|| Error::Other(format!("element {} not found", addr))) + self.data.elements.get(addr).ok_or_else(|| Self::not_found_error("element")) } /// Get the global at the actual index in the store pub(crate) fn get_global(&self, addr: usize) -> Result<&Rc>> { - self.data.globals.get(addr).ok_or_else(|| Error::Other(format!("global {} not found", addr))) + self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")) } /// Get the global at the actual index in the store pub fn get_global_val(&self, addr: usize) -> Result { - self.data - .globals - .get(addr) - .ok_or_else(|| Error::Other(format!("global {} not found", addr))) - .map(|global| global.borrow().value) + self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")).map(|global| global.borrow().value) } + /// Set the global at the actual index in the store pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> { self.data .globals .get(addr) - .ok_or_else(|| Error::Other(format!("global {} not found", addr))) + .ok_or_else(|| Self::not_found_error("global")) .map(|global| global.borrow_mut().value = value) } } diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index ea520b8..1b31999 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -82,7 +82,6 @@ impl TableInstance { // Initialize the table with the given elements (resolves function references) pub(crate) fn init(&mut self, func_addrs: &[u32], offset: i32, init: &[TableElement]) -> Result<()> { let init = init.iter().map(|item| item.map(|addr| self.resolve_func_ref(func_addrs, addr))).collect::>(); - self.init_raw(offset, &init) } } diff --git a/crates/tinywasm/tests/generated/mvp.csv b/crates/tinywasm/tests/generated/mvp.csv index 23c080d..f9fda83 100644 --- a/crates/tinywasm/tests/generated/mvp.csv +++ b/crates/tinywasm/tests/generated/mvp.csv @@ -3,3 +3,4 @@ 0.1.0,17630,2598,[{"name":"address.wast","passed":5,"failed":255},{"name":"align.wast","passed":108,"failed":48},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":110,"failed":2},{"name":"block.wast","passed":193,"failed":30},{"name":"br.wast","passed":84,"failed":13},{"name":"br_if.wast","passed":90,"failed":28},{"name":"br_table.wast","passed":25,"failed":149},{"name":"call.wast","passed":29,"failed":62},{"name":"call_indirect.wast","passed":36,"failed":134},{"name":"comments.wast","passed":7,"failed":1},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":371,"failed":248},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":47,"failed":14},{"name":"elem.wast","passed":50,"failed":49},{"name":"endianness.wast","passed":1,"failed":68},{"name":"exports.wast","passed":92,"failed":4},{"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":2,"failed":6},{"name":"float_exprs.wast","passed":761,"failed":139},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":6,"failed":84},{"name":"float_misc.wast","passed":437,"failed":4},{"name":"forward.wast","passed":1,"failed":4},{"name":"func.wast","passed":124,"failed":48},{"name":"func_ptrs.wast","passed":10,"failed":26},{"name":"global.wast","passed":51,"failed":59},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":120,"failed":121},{"name":"imports.wast","passed":74,"failed":109},{"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":14,"failed":15},{"name":"left-to-right.wast","passed":1,"failed":95},{"name":"linking.wast","passed":21,"failed":111},{"name":"load.wast","passed":60,"failed":37},{"name":"local_get.wast","passed":32,"failed":4},{"name":"local_set.wast","passed":50,"failed":3},{"name":"local_tee.wast","passed":68,"failed":29},{"name":"loop.wast","passed":93,"failed":27},{"name":"memory.wast","passed":34,"failed":45},{"name":"memory_grow.wast","passed":12,"failed":84},{"name":"memory_redundancy.wast","passed":1,"failed":7},{"name":"memory_size.wast","passed":6,"failed":36},{"name":"memory_trap.wast","passed":2,"failed":180},{"name":"names.wast","passed":485,"failed":1},{"name":"nop.wast","passed":46,"failed":42},{"name":"return.wast","passed":73,"failed":11},{"name":"select.wast","passed":86,"failed":62},{"name":"skip-stack-guard-page.wast","passed":1,"failed":10},{"name":"stack.wast","passed":2,"failed":5},{"name":"start.wast","passed":9,"failed":11},{"name":"store.wast","passed":59,"failed":9},{"name":"switch.wast","passed":2,"failed":26},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":22,"failed":14},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":50,"failed":14},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unwind.wast","passed":35,"failed":15},{"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.2.0,19344,884,[{"name":"address.wast","passed":181,"failed":79},{"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":220,"failed":3},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":171,"failed":3},{"name":"call.wast","passed":73,"failed":18},{"name":"call_indirect.wast","passed":50,"failed":120},{"name":"comments.wast","passed":7,"failed":1},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":439,"failed":180},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":47,"failed":14},{"name":"elem.wast","passed":56,"failed":43},{"name":"endianness.wast","passed":29,"failed":40},{"name":"exports.wast","passed":92,"failed":4},{"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":6,"failed":2},{"name":"float_exprs.wast","passed":890,"failed":10},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":78,"failed":12},{"name":"float_misc.wast","passed":437,"failed":4},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":168,"failed":4},{"name":"func_ptrs.wast","passed":10,"failed":26},{"name":"global.wast","passed":103,"failed":7},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":231,"failed":10},{"name":"imports.wast","passed":80,"failed":103},{"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":26,"failed":3},{"name":"left-to-right.wast","passed":92,"failed":4},{"name":"linking.wast","passed":29,"failed":103},{"name":"load.wast","passed":93,"failed":4},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":93,"failed":4},{"name":"loop.wast","passed":116,"failed":4},{"name":"memory.wast","passed":78,"failed":1},{"name":"memory_grow.wast","passed":91,"failed":5},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":35,"failed":7},{"name":"memory_trap.wast","passed":180,"failed":2},{"name":"names.wast","passed":485,"failed":1},{"name":"nop.wast","passed":78,"failed":10},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":114,"failed":34},{"name":"skip-stack-guard-page.wast","passed":1,"failed":10},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":11,"failed":9},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"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.3.0,20254,0,[{"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":"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_grow.wast","passed":96,"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":"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.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.4.0,20254,0,[{"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":"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_grow.wast","passed":96,"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":"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.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/testsuite/indexmap.rs b/crates/tinywasm/tests/testsuite/indexmap.rs index 1642ce2..3e751c4 100644 --- a/crates/tinywasm/tests/testsuite/indexmap.rs +++ b/crates/tinywasm/tests/testsuite/indexmap.rs @@ -1,3 +1,4 @@ +/// A naive implementation of an index map for use in the test suite pub struct IndexMap { map: std::collections::HashMap, keys: Vec, diff --git a/crates/tinywasm/tests/testsuite/mod.rs b/crates/tinywasm/tests/testsuite/mod.rs index 2019c04..35f0607 100644 --- a/crates/tinywasm/tests/testsuite/mod.rs +++ b/crates/tinywasm/tests/testsuite/mod.rs @@ -128,23 +128,10 @@ impl Debug for TestSuite { writeln!(f, "{}", link(group_name, &group.file, None).bold().underline())?; writeln!(f, " Tests Passed: {}", group_passed.to_string().green())?; + if group_failed != 0 { writeln!(f, " Tests Failed: {}", group_failed.to_string().red())?; } - - // for (test_name, test) in &group.tests { - // write!(f, " {}: ", test_name.bold())?; - // match &test.result { - // Ok(()) => { - // writeln!(f, "{}", "Passed".green())?; - // } - // Err(e) => { - // writeln!(f, "{}", "Failed".red())?; - // // writeln!(f, "Error: {:?}", e)?; - // } - // } - // writeln!(f, " Span: {:?}", test.span)?; - // } } writeln!(f, "\n{}", "Total Test Summary:".bold().underline())?; diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index 125a9d6..d4dafb1 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -1,3 +1,4 @@ +/// Here be dragons (this file is in need of a big refactor) use crate::testsuite::util::*; use std::{borrow::Cow, collections::HashMap}; @@ -6,7 +7,7 @@ use eyre::{eyre, Result}; use log::{debug, error, info}; use tinywasm::{Extern, Imports, ModuleInstance}; use tinywasm_types::{ExternVal, MemoryType, ModuleInstanceAddr, TableType, ValType, WasmValue}; -use wast::{lexer::Lexer, parser::ParseBuffer, QuoteWat, Wast}; +use wast::{lexer::Lexer, parser::ParseBuffer, Wast}; #[derive(Default)] struct RegisteredModules { @@ -195,31 +196,7 @@ impl TestSuite { Wat(module) => { debug!("got wat module"); let result = catch_unwind_silent(|| { - let (name, bytes) = match module { - QuoteWat::QuoteModule(_, quoted_wat) => { - let wat = quoted_wat - .iter() - .map(|(_, s)| std::str::from_utf8(s).expect("failed to convert wast to utf8")) - .collect::>() - .join("\n"); - - let lexer = Lexer::new(&wat); - let buf = ParseBuffer::new_with_lexer(lexer).expect("failed to create parse buffer"); - let mut wat_data = wast::parser::parse::(&buf).expect("failed to parse wat"); - (None, wat_data.encode().expect("failed to encode module")) - } - QuoteWat::Wat(mut wat) => { - let wast::Wat::Module(ref module) = wat else { - unimplemented!("Not supported"); - }; - ( - module.id.map(|id| id.name().to_string()), - wat.encode().expect("failed to encode module"), - ) - } - _ => unimplemented!("Not supported"), - }; - + let (name, bytes) = encode_quote_wat(module); let m = parse_module_bytes(&bytes).expect("failed to parse module bytes"); let module_instance = tinywasm::Module::from(m) @@ -488,10 +465,6 @@ impl TestSuite { e })?; - debug!("outcomes: {:?}", outcomes); - - debug!("expected: {:?}", expected); - if outcomes.len() != expected.len() { return Err(eyre!( "span: {:?} expected {} results, got {}", @@ -501,8 +474,6 @@ impl TestSuite { )); } - log::debug!("outcomes: {:?}", outcomes); - outcomes.iter().zip(expected).enumerate().try_for_each(|(i, (outcome, exp))| { (outcome.eq_loose(&exp)) .then_some(()) @@ -511,7 +482,6 @@ impl TestSuite { }); let res = res.map_err(|e| eyre!("test panicked: {:?}", try_downcast_panic(e))).and_then(|r| r); - test_group.add_result(&format!("AssertReturn({}-{})", invoke_name, i), span.linecol_in(wast), res); } _ => test_group.add_result( diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index 1b91e1e..a45c59f 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -2,6 +2,7 @@ use std::panic::{self, AssertUnwindSafe}; use eyre::{eyre, Result}; use tinywasm_types::{ModuleInstanceAddr, TinyWasmModule, ValType, WasmValue}; +use wast::QuoteWat; pub fn try_downcast_panic(panic: Box) -> String { let info = panic.downcast_ref::().or(None).map(|p| p.to_string()).clone(); @@ -53,6 +54,30 @@ pub fn catch_unwind_silent R, R>(f: F) -> std::thread::Result result } +pub fn encode_quote_wat(module: QuoteWat) -> (Option, Vec) { + match module { + QuoteWat::QuoteModule(_, quoted_wat) => { + let wat = quoted_wat + .iter() + .map(|(_, s)| std::str::from_utf8(s).expect("failed to convert wast to utf8")) + .collect::>() + .join("\n"); + + let lexer = wast::lexer::Lexer::new(&wat); + let buf = wast::parser::ParseBuffer::new_with_lexer(lexer).expect("failed to create parse buffer"); + let mut wat_data = wast::parser::parse::(&buf).expect("failed to parse wat"); + (None, wat_data.encode().expect("failed to encode module")) + } + QuoteWat::Wat(mut wat) => { + let wast::Wat::Module(ref module) = wat else { + unimplemented!("Not supported"); + }; + (module.id.map(|id| id.name().to_string()), wat.encode().expect("failed to encode module")) + } + _ => unimplemented!("Not supported"), + } +} + pub fn parse_module_bytes(bytes: &[u8]) -> Result { let parser = tinywasm_parser::Parser::new(); Ok(parser.parse_module_bytes(bytes)?) diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 0e2eafe..b5a68a4 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -1,10 +1,8 @@ -use crate::{DataAddr, ElemAddr, MemAddr}; - use super::{FuncAddr, GlobalAddr, LabelAddr, LocalAddr, TableAddr, TypeAddr, ValType}; +use crate::{DataAddr, ElemAddr, MemAddr}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum BlockArgs { Empty, Type(ValType), @@ -13,8 +11,7 @@ pub enum BlockArgs { /// 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))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct MemoryArg { pub mem_addr: MemAddr, pub align: u8, @@ -28,8 +25,7 @@ type EndOffset = usize; type ElseOffset = usize; #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ConstInstruction { I32Const(i32), I64Const(i64), @@ -53,8 +49,7 @@ pub enum ConstInstruction { /// /// See #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum Instruction { // Custom Instructions BrLabel(LabelAddr), diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 205ec5a..d2da7d7 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -39,16 +39,17 @@ pub mod archive; /// TinyWasmModules are validated before being created, so they are guaranteed to be valid (as long as they were created by TinyWasm). /// This means you should not trust a TinyWasmModule created by a third party to be valid. #[derive(Debug, Clone, Default, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct TinyWasmModule { /// The version of the WebAssembly module. pub version: Option, + /// The start function of the WebAssembly module. pub start_func: Option, /// The functions of the WebAssembly module. pub funcs: Box<[TypedWasmFunction]>, + /// The types of the WebAssembly module. pub func_types: Box<[FuncType]>, @@ -78,8 +79,7 @@ pub struct TinyWasmModule { /// /// See #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ExternalKind { /// A WebAssembly Function. Func, @@ -97,6 +97,8 @@ pub enum ExternalKind { /// /// See pub type Addr = u32; + +// aliases for clarity pub type FuncAddr = Addr; pub type TableAddr = Addr; pub type MemAddr = Addr; @@ -104,6 +106,7 @@ pub type GlobalAddr = Addr; pub type ElemAddr = Addr; pub type DataAddr = Addr; pub type ExternAddr = Addr; + // additional internal addresses pub type TypeAddr = Addr; pub type LocalAddr = Addr; @@ -146,25 +149,15 @@ impl ExternVal { /// The type of a WebAssembly Function. /// /// See -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[derive(Debug, Clone, PartialEq, Default)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct FuncType { pub params: Box<[ValType]>, pub results: Box<[ValType]>, } -impl FuncType { - /// Get the number of parameters of a function type. - #[inline] - pub fn empty() -> Self { - Self { params: Box::new([]), results: Box::new([]) } - } -} - #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct WasmFunction { pub instructions: Box<[Instruction]>, pub locals: Box<[ValType]>, @@ -172,8 +165,7 @@ pub struct WasmFunction { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct TypedWasmFunction { pub type_addr: u32, pub wasm_function: WasmFunction, @@ -181,8 +173,7 @@ pub struct TypedWasmFunction { /// A WebAssembly Module Export #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Export { /// The name of the export. pub name: Box, @@ -193,24 +184,21 @@ pub struct Export { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Global { pub ty: GlobalType, pub init: ConstInstruction, } #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct GlobalType { pub mutable: bool, pub ty: ValType, } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct TableType { pub element_type: ValType, pub size_initial: u32, @@ -227,12 +215,9 @@ impl TableType { } } -#[derive(Debug, Clone, PartialEq)] - /// Represents a memory's type. -#[derive(Copy)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct MemoryType { pub arch: MemoryArch, pub page_count_initial: u64, @@ -246,16 +231,14 @@ impl MemoryType { } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum MemoryArch { I32, I64, } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Import { pub module: Box, pub name: Box, @@ -263,8 +246,7 @@ pub struct Import { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ImportKind { Function(TypeAddr), Table(TableType), @@ -285,8 +267,7 @@ impl From<&ImportKind> for ExternalKind { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Data { pub data: Box<[u8]>, pub range: Range, @@ -294,16 +275,14 @@ pub struct Data { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum DataKind { Active { mem: MemAddr, offset: ConstInstruction }, Passive, } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct Element { pub kind: ElementKind, pub items: Box<[ElementItem]>, @@ -312,8 +291,7 @@ pub struct Element { } #[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ElementKind { Passive, Active { table: TableAddr, offset: ConstInstruction }, @@ -321,8 +299,7 @@ pub enum ElementKind { } #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ElementItem { Func(FuncAddr), Expr(ConstInstruction), diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index e46092b..24fed3b 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -114,8 +114,7 @@ impl WasmValue { /// Type of a WebAssembly value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))] -#[cfg_attr(feature = "archive", archive(check_bytes))] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ValType { /// A 32-bit integer. I32, diff --git a/examples/archive.rs b/examples/archive.rs index 7c93205..4dd714a 100644 --- a/examples/archive.rs +++ b/examples/archive.rs @@ -12,7 +12,7 @@ const WASM: &str = r#" fn main() -> Result<()> { let wasm = wat::parse_str(WASM).expect("failed to parse wat"); - let module = Parser::default().parse_module_bytes(&wasm)?; + let module = Parser::default().parse_module_bytes(wasm)?; let twasm = module.serialize_twasm(); // now, you could e.g. write twasm to a file called `add.twasm` From f6dba823b785cb05a024d52f787b81bd56d6b3e4 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Thu, 1 Feb 2024 01:36:12 +0100 Subject: [PATCH 02/24] perf: small performance improvements Signed-off-by: Henry Gressmann --- BENCHMARKS.md | 6 +- crates/tinywasm/src/instance.rs | 20 ++- .../src/runtime/interpreter/macros.rs | 3 +- .../tinywasm/src/runtime/interpreter/mod.rs | 22 ++-- crates/tinywasm/src/store/memory.rs | 3 + crates/tinywasm/src/store/mod.rs | 120 ++++++++++-------- 6 files changed, 104 insertions(+), 70 deletions(-) diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 12edd59..450b751 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -33,7 +33,7 @@ All runtimes are compiled with the following settings: | `fib` | 6ns | 44.76µs | 48.96µs | 52µs | | `fib-rec` | 284ns | 25.565ms | 5.11ms | 0.50ms | | `argon2id` | 0.52ms | 110.08ms | 44.408ms | 4.76ms | -| `selfhosted` | 45µs | 2.18ms | 4.25ms | 258.87ms | +| `selfhosted` | 45µs | 2.08ms | 4.25ms | 258.87ms | ### Fib @@ -49,7 +49,7 @@ TinyWasm is a lot slower here, but that's because there's currently no way to re This benchmark runs the Argon2id hashing algorithm, with 2 iterations, 1KB of memory, and 1 parallel lane. I had to decrease the memory usage from the default to 1KB, because especially the interpreters were struggling to finish in a reasonable amount of time. -This is where `simd` instructions would be really useful, and it also highlights some of the issues with the current implementation of TinyWasm's Value Stack and Memory Instances. +This is where `simd` instructions would be really useful, and it also highlights some of the issues with the current implementation of TinyWasm's Value Stack and Memory Instances. These spend a lot of time on `Vec` operations, so they might be a good place to start experimenting with Arena Allocation. ### Selfhosted @@ -62,6 +62,8 @@ Wasmer also offers a pre-parsed module format, so keep in mind that this number After profiling and fixing some low-hanging fruits, I found the biggest bottleneck to be Vector operations, especially for the Value Stack, and having shared access to Memory Instances using RefCell. These are the two areas I will be focusing on improving in the future, trying out Arena Allocation and other data structures to improve performance. Additionally, typed FuncHandles have a significant overhead over the untyped ones, so I will be looking into improving that as well. Still, I'm quite happy with the results, especially considering the use of standard Rust data structures. +Something that made a much bigger difference than I expected was to give compiler hints about cold paths, and to force inlining of some functions. This made the benchmarks 30%+ faster in some cases. A lot of places in the codebase have comments about what optimizations have been done. + # Running benchmarks Benchmarks are run using [Criterion.rs](https://github.com/bheisler/criterion.rs). To run a benchmark, use the following command: diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 75bd20d..6bfe99c 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -36,15 +36,18 @@ pub(crate) struct ModuleInstanceInner { impl ModuleInstance { // drop the module instance reference and swap it with another one + #[inline] pub(crate) fn swap(&mut self, other: Self) { self.0 = other.0; } + #[inline] pub(crate) fn swap_with(&mut self, other_addr: ModuleInstanceAddr, store: &mut Store) { self.swap(store.get_module_instance_raw(other_addr)) } /// Get the module instance's address + #[inline] pub fn id(&self) -> ModuleInstanceAddr { self.0.idx } @@ -118,44 +121,53 @@ impl ModuleInstance { Some(ExternVal::new(kind, *addr)) } - pub(crate) fn func_addrs(&self) -> &[FuncAddr] { - &self.0.func_addrs - } - + #[inline] pub(crate) fn new(inner: ModuleInstanceInner) -> Self { Self(Rc::new(inner)) } + #[inline] pub(crate) fn func_ty(&self, addr: FuncAddr) -> &FuncType { self.0.types.get(addr as usize).expect("No func type for func, this is a bug") } + #[inline] + pub(crate) fn func_addrs(&self) -> &[FuncAddr] { + &self.0.func_addrs + } + // resolve a function address to the global store address + #[inline] pub(crate) fn resolve_func_addr(&self, addr: FuncAddr) -> FuncAddr { *self.0.func_addrs.get(addr as usize).expect("No func addr for func, this is a bug") } // resolve a table address to the global store address + #[inline] pub(crate) fn resolve_table_addr(&self, addr: TableAddr) -> TableAddr { *self.0.table_addrs.get(addr as usize).expect("No table addr for table, this is a bug") } // resolve a memory address to the global store address + #[inline] pub(crate) fn resolve_mem_addr(&self, addr: MemAddr) -> MemAddr { *self.0.mem_addrs.get(addr as usize).expect("No mem addr for mem, this is a bug") } // resolve a data address to the global store address + #[inline] pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> MemAddr { *self.0.data_addrs.get(addr as usize).expect("No data addr for data, this is a bug") } // resolve a memory address to the global store address + #[inline] pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> ElemAddr { *self.0.elem_addrs.get(addr as usize).expect("No elem addr for elem, this is a bug") } // resolve a global address to the global store address + #[inline] pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> GlobalAddr { self.0.global_addrs[addr as usize] } diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index 9333a4c..d6c0553 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -28,13 +28,13 @@ macro_rules! mem_load { }}; ($load_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ - // TODO: 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 mem_ref = mem.borrow_mut(); let addr = $stack.values.pop()?.raw_value(); let addr = $arg.offset.checked_add(addr).ok_or_else(|| { + cold(); Error::Trap(crate::Trap::MemoryOutOfBounds { offset: $arg.offset as usize, len: core::mem::size_of::<$load_type>(), @@ -43,6 +43,7 @@ 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, len: core::mem::size_of::<$load_type>(), diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index c8d8791..7e34ac1 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -29,28 +29,31 @@ impl InterpreterRuntime { let mut current_module = store.get_module_instance_raw(cf.func_instance.1); loop { - match exec_one(&mut cf, stack, store, ¤t_module)? { + match exec_one(&mut cf, stack, store, ¤t_module) { // Continue execution at the new top of the call stack - ExecResult::Call => { + Ok(ExecResult::Call) => { cf = stack.call_stack.pop()?; + + // keeping the pointer seperate from the call frame is about 2% faster + // than storing it in the call frame if cf.func_instance.1 != current_module.id() { current_module.swap_with(cf.func_instance.1, store); } } // return from the function - ExecResult::Return => return Ok(()), + Ok(ExecResult::Return) => return Ok(()), // continue to the next instruction and increment the instruction pointer - ExecResult::Ok => cf.instr_ptr += 1, + Ok(ExecResult::Ok) => cf.instr_ptr += 1, // trap the program - ExecResult::Trap(trap) => { + Err(error) => { cf.instr_ptr += 1; // push the call frame back onto the stack so that it can be resumed // if the trap can be handled stack.call_stack.push(cf)?; - return Err(Error::Trap(trap)); + return Err(error); } } } @@ -61,13 +64,14 @@ enum ExecResult { Ok, Return, Call, - Trap(crate::Trap), } /// Run a single step of the interpreter /// A seperate function is used so later, we can more easily implement /// a step-by-step debugger (using generators once they're stable?) -#[inline(always)] // this improves performance by more than 20% in some cases +// we want this be always part of the loop, rust just doesn't inline it as its too big +// this can be a 30%+ performance difference in some cases +#[inline(always)] fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &ModuleInstance) -> Result { let instrs = &cf.func_instance.0.instructions; if unlikely(cf.instr_ptr >= instrs.len() || instrs.is_empty()) { @@ -84,7 +88,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M Nop => { /* do nothing */ } Unreachable => { cold(); - return Ok(ExecResult::Trap(crate::Trap::Unreachable)); + return Err(crate::Trap::Unreachable.into()); } // we don't need to include the call frame here because it's already on the stack Drop => stack.values.pop().map(|_| ())?, diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 64e16c6..588d995 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -100,6 +100,7 @@ impl MemoryInstance { Ok(val) } + #[inline] pub(crate) fn page_count(&self) -> usize { self.page_count } @@ -186,10 +187,12 @@ macro_rules! impl_mem_loadable_for_primitive { $( #[allow(unsafe_code)] unsafe impl MemLoadable<$size> for $type { + #[inline] fn from_le_bytes(bytes: [u8; $size]) -> Self { <$type>::from_le_bytes(bytes) } + #[inline] fn from_be_bytes(bytes: [u8; $size]) -> Self { <$type>::from_be_bytes(bytes) } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index ad24480..ce02dd7 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -116,6 +116,72 @@ impl Store { Ok(()) } + #[cold] + fn not_found_error(name: &str) -> Error { + Error::Other(format!("{} not found", name)) + } + + /// Get the function at the actual index in the store + #[inline] + pub(crate) fn get_func(&self, addr: usize) -> Result<&FunctionInstance> { + self.data.funcs.get(addr).ok_or_else(|| Self::not_found_error("function")) + } + + /// Get the memory at the actual index in the store + #[inline] + pub(crate) fn get_mem(&self, addr: usize) -> Result<&Rc>> { + self.data.memories.get(addr).ok_or_else(|| Self::not_found_error("memory")) + } + + /// Get the table at the actual index in the store + #[inline] + pub(crate) fn get_table(&self, addr: usize) -> Result<&Rc>> { + self.data.tables.get(addr).ok_or_else(|| Self::not_found_error("table")) + } + + /// Get the data at the actual index in the store + #[inline] + pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> { + self.data.datas.get(addr).ok_or_else(|| Self::not_found_error("data")) + } + + /// Get the data at the actual index in the store + #[inline] + pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> { + self.data.datas.get_mut(addr).ok_or_else(|| Self::not_found_error("data")) + } + + /// Get the element at the actual index in the store + #[inline] + pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> { + self.data.elements.get(addr).ok_or_else(|| Self::not_found_error("element")) + } + + /// Get the global at the actual index in the store + #[inline] + pub(crate) fn get_global(&self, addr: usize) -> Result<&Rc>> { + self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")) + } + + /// Get the global at the actual index in the store + #[inline] + pub fn get_global_val(&self, addr: usize) -> Result { + self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")).map(|global| global.borrow().value) + } + + /// 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) + } +} + +// Linking related functions +impl Store { /// Add functions to the store, returning their addresses in the store pub(crate) fn init_funcs( &mut self, @@ -391,58 +457,4 @@ impl Store { }; Ok(val) } - - #[cold] - fn not_found_error(name: &str) -> Error { - Error::Other(format!("{} not found", name)) - } - - /// Get the function at the actual index in the store - pub(crate) fn get_func(&self, addr: usize) -> Result<&FunctionInstance> { - self.data.funcs.get(addr).ok_or_else(|| Self::not_found_error("function")) - } - - /// Get the memory at the actual index in the store - pub(crate) fn get_mem(&self, addr: usize) -> Result<&Rc>> { - self.data.memories.get(addr).ok_or_else(|| Self::not_found_error("memory")) - } - - /// Get the table at the actual index in the store - pub(crate) fn get_table(&self, addr: usize) -> Result<&Rc>> { - self.data.tables.get(addr).ok_or_else(|| Self::not_found_error("table")) - } - - /// Get the data at the actual index in the store - pub(crate) fn get_data(&self, addr: usize) -> Result<&DataInstance> { - self.data.datas.get(addr).ok_or_else(|| Self::not_found_error("data")) - } - - /// Get the data at the actual index in the store - pub(crate) fn get_data_mut(&mut self, addr: usize) -> Result<&mut DataInstance> { - self.data.datas.get_mut(addr).ok_or_else(|| Self::not_found_error("data")) - } - - /// Get the element at the actual index in the store - pub(crate) fn get_elem(&self, addr: usize) -> Result<&ElementInstance> { - self.data.elements.get(addr).ok_or_else(|| Self::not_found_error("element")) - } - - /// Get the global at the actual index in the store - pub(crate) fn get_global(&self, addr: usize) -> Result<&Rc>> { - self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")) - } - - /// Get the global at the actual index in the store - pub fn get_global_val(&self, addr: usize) -> Result { - self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")).map(|global| global.borrow().value) - } - - /// Set the global at the actual index in the store - 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) - } } From 3f5db9ae793015b279c38e64b7c1fb5a7d5fd01c Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sun, 4 Feb 2024 00:05:28 +0100 Subject: [PATCH 03/24] docs: update readme Signed-off-by: Henry Gressmann --- CONTRIBUTING.md | 6 ++++++ Cargo.lock | 32 ++++++++++++++++---------------- README.md | 6 +++--- crates/tinywasm/src/lib.rs | 27 ++++++++++++++++----------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c00a34..bd91491 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,12 @@ - **`cargo test-mvp`**\ Run the WebAssembly MVP (1.0) test suite. Be sure to cloned this repo with `--recursive` or initialize the submodules with `git submodule update --init --recursive` +- **`cargo test-2`**\ + Run the full WebAssembly test suite (2.0) + +- **`cargo benchmark `**\ + Run a single benchmark. e.g. `cargo benchmark argon2id` + - **`cargo test-wast `**\ Run a single WAST test file. e.g. `cargo test-wast ./examples/wast/i32.wast` diff --git a/Cargo.lock b/Cargo.lock index 68ea040..459995f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,9 +652,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.4" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da01daa5f6d41c91358398e8db4dde38e292378da1f28300b59ef4732b879454" +checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" dependencies = [ "darling_core", "darling_macro", @@ -662,9 +662,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.4" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44f6238b948a3c6c3073cdf53bb0c2d5e024ee27e0f35bfe9d556a12395808a" +checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" dependencies = [ "fnv", "ident_case", @@ -675,9 +675,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.4" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d88bd93979b1feb760a6b5c531ac5ba06bd63e74894c377af02faee07b9cd" +checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" dependencies = [ "darling_core", "quote", @@ -865,9 +865,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -1095,9 +1095,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1223,9 +1223,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -1320,9 +1320,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -1770,9 +1770,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", diff --git a/README.md b/README.md index da4d738..c07fadc 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,14 @@ ## Why TinyWasm? - **Tiny**: TinyWasm is designed to be as small as possible without significantly compromising performance or functionality. -- **Portable**: TinyWasm runs on any platform that LLVM supports, including WebAssembly itself, with minimal external dependencies. +- **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. ## Status -As of version `0.3.0`, TinyWasm successfully passes all the WebAssembly 1.0 tests in the [WebAssembly Test Suite](https://github.com/WebAssembly/testsuite). Work on the 2.0 tests is ongoing. This achievement ensures that TinyWasm can run most WebAssembly programs, including versions of TinyWasm itself compiled to WebAssembly (see [examples/wasm-rust.rs](./examples/wasm-rust.rs)). The results of the testsuite are available [here](https://github.com/explodingcamera/tinywasm/tree/main/crates/tinywasm/tests/generated). +As of version `0.3.0`, TinyWasm successfully passes all the WebAssembly 1.0 tests in the [WebAssembly Test Suite](https://github.com/WebAssembly/testsuite). Work on the 2.0 tests is ongoing. This enables TinyWasm to run most WebAssembly programs, including versions of TinyWasm itself compiled to WebAssembly (see [examples/wasm-rust.rs](./examples/wasm-rust.rs)). The results of the testsuites are available [here](https://github.com/explodingcamera/tinywasm/tree/main/crates/tinywasm/tests/generated). -The API is still unstable and may change at any time, so don't use it in production _yet_. Note that TinyWasm isn't primarily designed for high performance; its focus lies more on simplicity, size, and portability. More details on its performance aspects can be found in [BENCHMARKS.md](./BENCHMARKS.md). +The API is still unstable and may change at any time, so you probably don't want to use it in production _yet_. Note that TinyWasm isn't primarily designed for high performance; its focus lies more on simplicity, size, and portability. More details on its performance aspects can be found in [BENCHMARKS.md](./BENCHMARKS.md). ## Supported Proposals diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 77ecb1e..583a68d 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -10,20 +10,25 @@ //! A tiny WebAssembly Runtime written in Rust //! //! TinyWasm provides a minimal WebAssembly runtime for executing WebAssembly modules. -//! It currently supports a subset of the WebAssembly MVP specification and is intended -//! to be useful for embedded systems and other environments where a full-featured -//! runtime is not required. +//! It currently supports all features of the WebAssembly MVP specification and is +//! designed to be easy to use and integrate in other projects. //! //! ## Features -//! - `std` (default): Enables the use of `std` and `std::io` for parsing from files and streams. -//! - `logging` (default): Enables logging via the `log` crate. -//! - `parser` (default): Enables the `tinywasm_parser` crate for parsing WebAssembly modules. +//!- **`std`**\ +//! Enables the use of `std` and `std::io` for parsing from files and streams. This is enabled by default. +//!- **`logging`**\ +//! Enables logging using the `log` crate. This is enabled by default. +//!- **`parser`**\ +//! Enables the `tinywasm-parser` crate. This is enabled by default. +//!- **`archive`**\ +//! Enables pre-parsing of archives. This is enabled by default. +//!- **`unsafe`**\ +//! Uses `unsafe` code to improve performance, particularly in Memory access //! -//! ## No-std support -//! TinyWasm supports `no_std` environments by disabling the `std` feature and registering -//! a custom allocator. This removes support for parsing from files and streams, -//! but otherwise the API is the same. -//! Additionally, to have proper error types, you currently need a `nightly` compiler to have the error trait in core. +//! With all these features disabled, TinyWasm only depends on `core`, `alloc` and `libm`. +//! By disabling `std`, you can use TinyWasm in `no_std` environments. This requires +//! a custom allocator and removes support for parsing from files and streams, but otherwise the API is the same. +//! Additionally, to have proper error types in `no_std`, you currently need a `nightly` compiler to use the unstable error trait in `core`. //! //! ## Getting Started //! The easiest way to get started is to use the [`Module::parse_bytes`] function to load a From 2d80aae778bb1f436c5777aeca1ca0d465e19062 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sun, 4 Feb 2024 00:05:57 +0100 Subject: [PATCH 04/24] Release 0.4.1 tinywasm@0.4.1 tinywasm-parser@0.4.1 tinywasm-types@0.4.1 Generated by cargo-workspaces --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 459995f..36dcdcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2034,7 +2034,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tinywasm" -version = "0.4.0" +version = "0.4.1" dependencies = [ "eyre", "libm", @@ -2052,7 +2052,7 @@ dependencies = [ [[package]] name = "tinywasm-cli" -version = "0.4.0" +version = "0.4.1" dependencies = [ "argh", "color-eyre", @@ -2064,7 +2064,7 @@ dependencies = [ [[package]] name = "tinywasm-parser" -version = "0.4.0" +version = "0.4.1" dependencies = [ "log", "tinywasm-types", @@ -2082,7 +2082,7 @@ dependencies = [ [[package]] name = "tinywasm-types" -version = "0.4.0" +version = "0.4.1" dependencies = [ "bytecheck 0.7.0", "log", diff --git a/Cargo.toml b/Cargo.toml index b565c4d..bbe7e57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ panic="abort" inherits="release" [workspace.package] -version="0.4.0" +version="0.4.1" edition="2021" license="MIT OR Apache-2.0" authors=["Henry Gressmann "] From 3d1a621dc52727629a798249c27beba2466719ea Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 13 Feb 2024 00:26:40 +0100 Subject: [PATCH 05/24] chore: update dependencies Signed-off-by: Henry Gressmann --- Cargo.lock | 164 +++++++++++++++---------------------- crates/cli/Cargo.toml | 2 +- crates/tinywasm/Cargo.toml | 2 +- 3 files changed, 68 insertions(+), 100 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36dcdcf..732fcd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -60,9 +60,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "argh" @@ -212,11 +212,11 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytecheck" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ - "bytecheck_derive 0.6.11", + "bytecheck_derive 0.6.12", "ptr_meta", "simdutf8", ] @@ -234,9 +234,9 @@ dependencies = [ [[package]] name = "bytecheck_derive" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ "proc-macro2", "quote", @@ -256,9 +256,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.14.1" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" [[package]] name = "byteorder" @@ -280,12 +280,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "9b918671670962b48bc23753aef0c51d072dca6f52f01f800854ada6ddb7f7d3" [[package]] name = "cfg-if" @@ -295,9 +292,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -336,18 +333,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstyle", "clap_lex", @@ -355,9 +352,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "color-eyre" @@ -557,9 +554,9 @@ checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -795,9 +792,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "enum-iterator" @@ -853,16 +850,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "eyre" version = "0.6.12" @@ -1083,9 +1070,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" [[package]] name = "humantime" @@ -1170,12 +1157,12 @@ checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", - "rustix", + "libc", "windows-sys 0.52.0", ] @@ -1202,9 +1189,9 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -1254,12 +1241,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - [[package]] name = "lock_api" version = "0.4.11" @@ -1336,9 +1317,9 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -1681,21 +1662,21 @@ dependencies = [ [[package]] name = "rend" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" dependencies = [ - "bytecheck 0.6.11", + "bytecheck 0.6.12", ] [[package]] name = "rkyv" -version = "0.7.43" +version = "0.7.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" dependencies = [ "bitvec", - "bytecheck 0.6.11", + "bytecheck 0.6.12", "bytes", "hashbrown 0.12.3", "indexmap", @@ -1709,9 +1690,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.43" +version = "0.7.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" dependencies = [ "proc-macro2", "quote", @@ -1768,19 +1749,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" -dependencies = [ - "bitflags 2.4.2", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "ryu" version = "1.0.16" @@ -1979,18 +1947,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -2047,7 +2015,7 @@ dependencies = [ "tinywasm-parser", "tinywasm-types", "wasm-testsuite", - "wast 70.0.2", + "wast 71.0.1", ] [[package]] @@ -2059,7 +2027,7 @@ dependencies = [ "log", "pretty_env_logger", "tinywasm", - "wast 70.0.2", + "wast 71.0.1", ] [[package]] @@ -2228,9 +2196,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2238,9 +2206,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", @@ -2253,9 +2221,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2263,9 +2231,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", @@ -2276,9 +2244,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "wasm-encoder" @@ -2291,9 +2259,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.41.0" +version = "0.41.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09bca7d6388637d27fb5edbeab11f56bfabcef8743c55ae34370e1e5030a071" +checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" dependencies = [ "leb128", ] @@ -2417,7 +2385,7 @@ version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0caf1c87937b52aba8e9f920a278e1beda282f7439612c0b48f51a58e7a87bab" dependencies = [ - "bytecheck 0.6.11", + "bytecheck 0.6.12", "enum-iterator", "enumset", "indexmap", @@ -2519,15 +2487,15 @@ dependencies = [ [[package]] name = "wast" -version = "70.0.2" +version = "71.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d5061300042ff5065123dae1e27d00c03f567d34a2937c8472255148a216dc" +checksum = "647c3ac4354da32688537e8fc4d2fe6c578df51896298cb64727d98088a1fd26" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.41.0", + "wasm-encoder 0.41.2", ] [[package]] @@ -2541,9 +2509,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 11914ef..e1e9412 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,7 +19,7 @@ argh="0.1" color-eyre={version="0.6", default-features=false} log="0.4" pretty_env_logger="0.5" -wast={version="70.0", optional=true} +wast={version="71.0", optional=true} [features] default=["wat"] diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index ad58b9e..a71b7da 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -20,7 +20,7 @@ libm={version="0.2", default-features=false} [dev-dependencies] wasm-testsuite={path="../wasm-testsuite"} -wast={version="70.0"} +wast={version="71.0"} owo-colors={version="4.0"} eyre={version="0.6"} serde_json={version="1.0"} From 6cf5448611bdf5956127f1fe6840891c50a6f3ae Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 13 Feb 2024 01:18:18 +0100 Subject: [PATCH 06/24] fix: broken dependency on latest nightly Signed-off-by: Henry Gressmann --- Cargo.lock | 3 +-- Cargo.toml | 4 ++++ crates/tinywasm/src/store/memory.rs | 3 +++ rust-toolchain.toml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 732fcd2..dfd58ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1400,8 +1400,7 @@ dependencies = [ [[package]] name = "pathfinder_simd" version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0444332826c70dc47be74a7c6a5fc44e23a7905ad6858d4162b658320455ef93" +source = "git+https://github.com/servo/pathfinder?rev=e4fcda0d5259d0acf902aee6de7d2501f2bd6629#e4fcda0d5259d0acf902aee6de7d2501f2bd6629" dependencies = [ "rustc_version", ] diff --git a/Cargo.toml b/Cargo.toml index bbe7e57..7e85d93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,7 @@ opt-level=3 lto="thin" codegen-units=1 debug=true + +[patch.crates-io] +# https://github.com/servo/pathfinder/pull/548 +pathfinder_simd={git="https://github.com/servo/pathfinder", rev="e4fcda0d5259d0acf902aee6de7d2501f2bd6629"} diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 588d995..cbaed8d 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -177,14 +177,17 @@ impl MemoryInstance { /// UB for loading things things like packed structs pub(crate) unsafe trait MemLoadable: Sized + Copy { /// Load a value from memory + #[allow(unused)] fn from_le_bytes(bytes: [u8; T]) -> Self; /// Load a value from memory + #[allow(unused)] fn from_be_bytes(bytes: [u8; T]) -> Self; } macro_rules! impl_mem_loadable_for_primitive { ($($type:ty, $size:expr),*) => { $( + #[allow(unused)] #[allow(unsafe_code)] unsafe impl MemLoadable<$size> for $type { #[inline] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c046a09..6c22ba5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel="nightly" +channel="nightly-2024-02-11" From 1313cc071f45f5a0ce97637d130d4334a5e9bdfd Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Thu, 22 Feb 2024 20:18:11 +0100 Subject: [PATCH 07/24] chore: update wasm testsuite, fix broken tests Signed-off-by: Henry Gressmann --- Cargo.lock | 162 +++++++++--------- crates/cli/Cargo.toml | 2 +- crates/tinywasm/Cargo.toml | 2 +- crates/tinywasm/tests/generated/2.0.csv | 3 +- crates/tinywasm/tests/generated/mvp.csv | 1 + .../tinywasm/tests/generated/progress-2.0.svg | 49 +++--- .../tinywasm/tests/generated/progress-mvp.svg | 36 ++-- crates/tinywasm/tests/testsuite/run.rs | 4 +- crates/wasm-testsuite/data | 2 +- 9 files changed, 133 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dfd58ac..8508a3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,7 +83,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytecheck" @@ -280,9 +280,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b918671670962b48bc23753aef0c51d072dca6f52f01f800854ada6ddb7f7d3" +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" [[package]] name = "cfg-if" @@ -301,7 +301,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -333,18 +333,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstyle", "clap_lex", @@ -649,9 +649,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" +checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" dependencies = [ "darling_core", "darling_macro", @@ -659,26 +659,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" +checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "darling_macro" -version = "0.20.5" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" +checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -834,7 +834,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1070,9 +1070,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hermit-abi" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "humantime" @@ -1419,9 +1419,9 @@ checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" @@ -1471,9 +1471,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.11" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -1718,7 +1718,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.48", + "syn 2.0.50", "walkdir", ] @@ -1750,9 +1750,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -1783,15 +1783,15 @@ checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -1809,20 +1809,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -1914,9 +1914,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -1931,9 +1931,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "termcolor" @@ -1961,14 +1961,14 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -2014,7 +2014,7 @@ dependencies = [ "tinywasm-parser", "tinywasm-types", "wasm-testsuite", - "wast 71.0.1", + "wast 200.0.0", ] [[package]] @@ -2026,7 +2026,7 @@ dependencies = [ "log", "pretty_env_logger", "tinywasm", - "wast 71.0.1", + "wast 200.0.0", ] [[package]] @@ -2075,7 +2075,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -2135,9 +2135,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -2214,7 +2214,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", "wasm-bindgen-shared", ] @@ -2236,7 +2236,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2258,9 +2258,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.41.2" +version = "0.200.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "972f97a5d8318f908dded23594188a90bcd09365986b1163e66d70170e5287ae" +checksum = "b9e3fb0c8fbddd78aa6095b850dfeedbc7506cf5f81e633f69cf8f2333ab84b9" dependencies = [ "leb128", ] @@ -2486,15 +2486,15 @@ dependencies = [ [[package]] name = "wast" -version = "71.0.1" +version = "200.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "647c3ac4354da32688537e8fc4d2fe6c578df51896298cb64727d98088a1fd26" +checksum = "d1810d14e6b03ebb8fb05eef4009ad5749c989b65197d83bce7de7172ed91366" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.41.2", + "wasm-encoder 0.200.0", ] [[package]] @@ -2559,7 +2559,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -2590,7 +2590,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -2610,17 +2610,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -2631,9 +2631,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -2649,9 +2649,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -2667,9 +2667,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -2685,9 +2685,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -2703,9 +2703,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -2715,9 +2715,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -2733,9 +2733,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "wio" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index e1e9412..78d0b90 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -19,7 +19,7 @@ argh="0.1" color-eyre={version="0.6", default-features=false} log="0.4" pretty_env_logger="0.5" -wast={version="71.0", optional=true} +wast={version="200.0", optional=true} [features] default=["wat"] diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index a71b7da..01502bb 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -20,7 +20,7 @@ libm={version="0.2", default-features=false} [dev-dependencies] wasm-testsuite={path="../wasm-testsuite"} -wast={version="71.0"} +wast={version="200.0"} owo-colors={version="4.0"} eyre={version="0.6"} serde_json={version="1.0"} diff --git a/crates/tinywasm/tests/generated/2.0.csv b/crates/tinywasm/tests/generated/2.0.csv index fbf18ae..495ea38 100644 --- a/crates/tinywasm/tests/generated/2.0.csv +++ b/crates/tinywasm/tests/generated/2.0.csv @@ -1,2 +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-alpha.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.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}] diff --git a/crates/tinywasm/tests/generated/mvp.csv b/crates/tinywasm/tests/generated/mvp.csv index f9fda83..2e151d5 100644 --- a/crates/tinywasm/tests/generated/mvp.csv +++ b/crates/tinywasm/tests/generated/mvp.csv @@ -4,3 +4,4 @@ 0.2.0,19344,884,[{"name":"address.wast","passed":181,"failed":79},{"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":220,"failed":3},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":171,"failed":3},{"name":"call.wast","passed":73,"failed":18},{"name":"call_indirect.wast","passed":50,"failed":120},{"name":"comments.wast","passed":7,"failed":1},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":439,"failed":180},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":47,"failed":14},{"name":"elem.wast","passed":56,"failed":43},{"name":"endianness.wast","passed":29,"failed":40},{"name":"exports.wast","passed":92,"failed":4},{"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":6,"failed":2},{"name":"float_exprs.wast","passed":890,"failed":10},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":78,"failed":12},{"name":"float_misc.wast","passed":437,"failed":4},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":168,"failed":4},{"name":"func_ptrs.wast","passed":10,"failed":26},{"name":"global.wast","passed":103,"failed":7},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":231,"failed":10},{"name":"imports.wast","passed":80,"failed":103},{"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":26,"failed":3},{"name":"left-to-right.wast","passed":92,"failed":4},{"name":"linking.wast","passed":29,"failed":103},{"name":"load.wast","passed":93,"failed":4},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":93,"failed":4},{"name":"loop.wast","passed":116,"failed":4},{"name":"memory.wast","passed":78,"failed":1},{"name":"memory_grow.wast","passed":91,"failed":5},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":35,"failed":7},{"name":"memory_trap.wast","passed":180,"failed":2},{"name":"names.wast","passed":485,"failed":1},{"name":"nop.wast","passed":78,"failed":10},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":114,"failed":34},{"name":"skip-stack-guard-page.wast","passed":1,"failed":10},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":11,"failed":9},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"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.3.0,20254,0,[{"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":"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_grow.wast","passed":96,"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":"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.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.4.0,20254,0,[{"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":"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_grow.wast","passed":96,"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":"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.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.4.1,20257,0,[{"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":"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_grow.wast","passed":96,"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":"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.wast","passed":19,"failed":0},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/progress-2.0.svg b/crates/tinywasm/tests/generated/progress-2.0.svg index c869fcb..6424367 100644 --- a/crates/tinywasm/tests/generated/progress-2.0.svg +++ b/crates/tinywasm/tests/generated/progress-2.0.svg @@ -10,45 +10,50 @@ Tests Passed TinyWasm Version - - - - - + + + + + 0 - + 5000 - - + + 10000 - - + + 15000 - - + + 20000 - - + + 25000 - + - + v0.3.0 (26722) - - -v0.4.0-alpha.0 (27464) + + +v0.4.0 (27549) - - - + + +v0.4.1 (27552) + + + + + diff --git a/crates/tinywasm/tests/generated/progress-mvp.svg b/crates/tinywasm/tests/generated/progress-mvp.svg index b8a3e35..2a26dd5 100644 --- a/crates/tinywasm/tests/generated/progress-mvp.svg +++ b/crates/tinywasm/tests/generated/progress-mvp.svg @@ -36,29 +36,27 @@ TinyWasm Version - + v0.0.4 (9258) - - -v0.0.5 (11135) - - - + + v0.1.0 (17630) - - -v0.2.0 (19344) - - - + + v0.3.0 (20254) - - - - - - + + +v0.4.1 (20257) + + + + + + + + + diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index d4dafb1..84efc3a 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -126,8 +126,8 @@ impl TestSuite { .define("spectest", "table", table)? .define("spectest", "global_i32", Extern::global(WasmValue::I32(666), false))? .define("spectest", "global_i64", Extern::global(WasmValue::I64(666), false))? - .define("spectest", "global_f32", Extern::global(WasmValue::F32(666.0), false))? - .define("spectest", "global_f64", Extern::global(WasmValue::F64(666.0), false))? + .define("spectest", "global_f32", Extern::global(WasmValue::F32(666.6), false))? + .define("spectest", "global_f64", Extern::global(WasmValue::F64(666.6), false))? .define("spectest", "print", print)? .define("spectest", "print_i32", print_i32)? .define("spectest", "print_i64", print_i64)? diff --git a/crates/wasm-testsuite/data b/crates/wasm-testsuite/data index dc27dad..5a1a590 160000 --- a/crates/wasm-testsuite/data +++ b/crates/wasm-testsuite/data @@ -1 +1 @@ -Subproject commit dc27dad3e34e466bdbfea32fe3c73f5e31f88560 +Subproject commit 5a1a590603d81f40ef471abba70a90a9ae5f4627 From dea93272b80a7ae658318ce7af21f00cacae054d Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sat, 24 Feb 2024 14:35:20 +0100 Subject: [PATCH 08/24] pref: improve parser performance Signed-off-by: Henry Gressmann --- Cargo.lock | 20 +++++------ crates/benchmarks/benches/selfhosted.rs | 18 ++++++---- crates/parser/src/conversion.rs | 47 ++++++++++++++++++------- examples/rust/README.md | 2 ++ examples/rust/rust-toolchain.toml | 2 ++ 5 files changed, 61 insertions(+), 28 deletions(-) create mode 100644 examples/rust/rust-toolchain.toml diff --git a/Cargo.lock b/Cargo.lock index 8508a3a..fbfe381 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,9 +280,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" +checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" [[package]] name = "cfg-if" @@ -649,9 +649,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ "darling_core", "darling_macro", @@ -659,9 +659,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", @@ -672,9 +672,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", @@ -1121,9 +1121,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index b022fd1..985577c 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -53,13 +53,19 @@ fn run_wasmer(wasm: &[u8]) { const TINYWASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm"); fn criterion_benchmark(c: &mut Criterion) { - let twasm = util::wasm_to_twasm(TINYWASM); + { + let mut group = c.benchmark_group("selfhosted-parse"); + group.bench_function("tinywasm", |b| b.iter(|| util::wasm_to_twasm(TINYWASM))); + } - let mut group = c.benchmark_group("selfhosted"); - group.bench_function("native", |b| b.iter(run_native)); - group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + { + let twasm = util::wasm_to_twasm(TINYWASM); + let mut group = c.benchmark_group("selfhosted"); + // group.bench_function("native", |b| b.iter(run_native)); + group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); + // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + } } criterion_group!( diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 38a2ada..cd8840a 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -163,7 +163,7 @@ pub(crate) fn convert_module_code( } let body_reader = func.get_operators_reader()?; - let body = process_operators(body_reader.original_position(), body_reader.into_iter(), validator)?; + let body = process_operators(body_reader, validator)?; Ok(CodeSection { locals: locals.into_boxed_slice(), body }) } @@ -228,28 +228,47 @@ pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result( - mut offset: usize, - ops: impl Iterator, wasmparser::BinaryReaderError>>, +pub(crate) fn process_operators( + ops: OperatorsReader<'_>, mut validator: FuncValidator, ) -> Result> { - let mut instructions = Vec::new(); - let mut labels_ptrs = Vec::new(); // indexes into the instructions array + let mut instructions = Vec::with_capacity(1024); + let mut labels_ptrs = Vec::with_capacity(32); + // indexes into the instructions array + let mut offset = ops.original_position(); for op in ops { - log::debug!("op: {:?}", op); + let op = match op { + Ok(op) => op, + Err(e) => { + cold(); + log::error!("Error while processing operators: {:?}", e); + return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string())); + } + }; - let op = op?; - validator.op(offset, &op)?; + match validator.op(offset, &op) { + Ok(_) => (), + Err(e) => { + cold(); + log::error!("Error while processing operators: {:?}", e); + return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string())); + } + } offset += 1; use wasmparser::Operator::*; let res = match op { BrTable { targets } => { let def = targets.default(); - let targets = targets.targets().collect::, wasmparser::BinaryReaderError>>()?; - instructions.push(Instruction::BrTable(def, targets.len())); - instructions.extend(targets.into_iter().map(Instruction::BrLabel)); + + let instrs = targets + .targets() + .map(|t| t.map(Instruction::BrLabel)) + .collect::, wasmparser::BinaryReaderError>>()?; + + instructions.push(Instruction::BrTable(def, instrs.len())); + instructions.extend(instrs); continue; } Unreachable => Instruction::Unreachable, @@ -510,6 +529,7 @@ pub(crate) fn process_operators<'a>( TableSize { table } => Instruction::TableSize(table), TableFill { table } => Instruction::TableFill(table), op => { + cold(); log::error!("Unsupported instruction: {:?}", op); return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", op))); } @@ -524,3 +544,6 @@ pub(crate) fn process_operators<'a>( validator.finish(offset)?; Ok(instructions.into_boxed_slice()) } + +#[cold] +fn cold() {} diff --git a/examples/rust/README.md b/examples/rust/README.md index 1b6be2f..c24054c 100644 --- a/examples/rust/README.md +++ b/examples/rust/README.md @@ -2,3 +2,5 @@ This is a seperate crate that generates WebAssembly from Rust code. It is used by the `wasm-rust` example. + +Requires the `wasm32-unknown-unknown` target to be installed. diff --git a/examples/rust/rust-toolchain.toml b/examples/rust/rust-toolchain.toml new file mode 100644 index 0000000..6c22ba5 --- /dev/null +++ b/examples/rust/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel="nightly-2024-02-11" From 7f99f964f74f7e77203bd505e69411af6dcae040 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sat, 24 Feb 2024 14:50:52 +0100 Subject: [PATCH 09/24] docs: update benchmarks Signed-off-by: Henry Gressmann --- BENCHMARKS.md | 22 +++++++++++++--------- crates/benchmarks/benches/selfhosted.rs | 6 +++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 450b751..5090043 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -22,18 +22,22 @@ All runtimes are compiled with the following settings: ## Versions -- `tinywasm`: `0.4.0` -- `wasmi`: `0.31.0` -- `wasmer`: `4.2.0` +- `tinywasm`: `0.4.1` +- `wasmi`: `0.31.2` +- `wasmer`: `4.2.5` ## Results -| Benchmark | Native | TinyWasm | Wasmi | Wasmer (Single Pass) | -| ------------ | ------ | -------- | -------- | -------------------- | -| `fib` | 6ns | 44.76µs | 48.96µs | 52µs | -| `fib-rec` | 284ns | 25.565ms | 5.11ms | 0.50ms | -| `argon2id` | 0.52ms | 110.08ms | 44.408ms | 4.76ms | -| `selfhosted` | 45µs | 2.08ms | 4.25ms | 258.87ms | +| Benchmark | Native | TinyWasm\* | Wasmi | Wasmer (Single Pass) | +| ------------ | -------- | ---------- | --------- | -------------------- | +| `fib` | \*\* | ` 44.11µs` | `49.46µs` | ` 50.65µs` | +| `fib-rec` | `0.26ms` | ` 24.91ms` | ` 4.62ms` | ` 0.49ms` | +| `argon2id` | `0.53ms` | `109.38ms` | `45.85ms` | ` 4.82ms` | +| `selfhosted` | `0.05ms` | ` 2.07ms` | ` 4.26ms` | `260.32ms` | + +_\* converting WASM to TinyWasm bytecode is not included. 7.2.ms is the time it takes to convert `tinywasm.wasm` to TinyWasm bytecode._ + +_\*\* essentially instant as it gets computed at compile time._ ### Fib diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 985577c..542343e 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -61,10 +61,10 @@ fn criterion_benchmark(c: &mut Criterion) { { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); - // group.bench_function("native", |b| b.iter(run_native)); + group.bench_function("native", |b| b.iter(run_native)); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); - // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); - // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } } From bf30b77a5ce9dd4921c2488b86d146d3d5d7ce38 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sat, 24 Feb 2024 19:17:46 +0100 Subject: [PATCH 10/24] perf: reduce block overhead (#3) --- BENCHMARKS.md | 6 +- Cargo.lock | 5 +- Cargo.toml | 1 + crates/parser/src/conversion.rs | 2 - crates/tinywasm/src/error.rs | 16 ++--- crates/tinywasm/src/func.rs | 2 +- crates/tinywasm/src/instance.rs | 2 +- .../src/runtime/interpreter/macros.rs | 3 +- .../tinywasm/src/runtime/interpreter/mod.rs | 60 ++++++++++++------- crates/tinywasm/src/runtime/stack.rs | 7 ++- .../stack/{blocks.rs => block_stack.rs} | 31 ++++------ .../tinywasm/src/runtime/stack/call_stack.rs | 40 ++++++++----- .../tinywasm/src/runtime/stack/value_stack.rs | 10 ++-- examples/wasm-rust.rs | 2 + 14 files changed, 108 insertions(+), 79 deletions(-) rename crates/tinywasm/src/runtime/stack/{blocks.rs => block_stack.rs} (72%) diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 5090043..673545e 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -31,11 +31,11 @@ All runtimes are compiled with the following settings: | Benchmark | Native | TinyWasm\* | Wasmi | Wasmer (Single Pass) | | ------------ | -------- | ---------- | --------- | -------------------- | | `fib` | \*\* | ` 44.11µs` | `49.46µs` | ` 50.65µs` | -| `fib-rec` | `0.26ms` | ` 24.91ms` | ` 4.62ms` | ` 0.49ms` | +| `fib-rec` | `0.26ms` | ` 20.99ms` | ` 4.64ms` | ` 0.50ms` | | `argon2id` | `0.53ms` | `109.38ms` | `45.85ms` | ` 4.82ms` | -| `selfhosted` | `0.05ms` | ` 2.07ms` | ` 4.26ms` | `260.32ms` | +| `selfhosted` | `0.05ms` | ` 1.97ms` | ` 4.26ms` | `260.32ms` | -_\* converting WASM to TinyWasm bytecode is not included. 7.2.ms is the time it takes to convert `tinywasm.wasm` to TinyWasm bytecode._ +_\* converting WASM to TinyWasm bytecode is not included. I takes ~7ms to convert `tinywasm.wasm` to TinyWasm bytecode._ _\*\* essentially instant as it gets computed at compile time._ diff --git a/Cargo.lock b/Cargo.lock index fbfe381..bf9afe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1070,9 +1070,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" [[package]] name = "humantime" @@ -2043,6 +2043,7 @@ name = "tinywasm-root" version = "0.0.0" dependencies = [ "color-eyre", + "pretty_env_logger", "tinywasm", "wat", ] diff --git a/Cargo.toml b/Cargo.toml index 7e85d93..68c0b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ test=false color-eyre="0.6" tinywasm={path="crates/tinywasm", features=["unsafe"]} wat={version="1.0"} +pretty_env_logger="0.5" [profile.bench] opt-level=3 diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index cd8840a..68567f3 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -291,8 +291,6 @@ pub(crate) fn process_operators( } End => { if let Some(label_pointer) = labels_ptrs.pop() { - log::debug!("ending block: {:?}", instructions[label_pointer]); - let current_instr_ptr = instructions.len(); // last_label_pointer is Some if we're ending a block diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 6b8119f..85ffdf6 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -24,17 +24,17 @@ pub enum Error { FuncDidNotReturn, /// The stack is empty - StackUnderflow, + ValueStackUnderflow, /// The label stack is empty - LabelStackUnderflow, + BlockStackUnderflow, + + /// The call stack is empty + CallStackUnderflow, /// An invalid label type was encountered InvalidLabelType, - /// The call stack is empty - CallStackEmpty, - /// The store is not the one that the module instance was instantiated in InvalidStore, @@ -189,13 +189,13 @@ impl Display for Error { Self::Trap(trap) => write!(f, "trap: {}", trap), Self::Linker(err) => write!(f, "linking error: {}", err), - Self::CallStackEmpty => write!(f, "call stack empty"), + Self::CallStackUnderflow => write!(f, "call stack empty"), Self::InvalidLabelType => write!(f, "invalid label type"), Self::Other(message) => write!(f, "unknown error: {}", message), Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {}", feature), Self::FuncDidNotReturn => write!(f, "function did not return"), - Self::LabelStackUnderflow => write!(f, "label stack underflow"), - Self::StackUnderflow => write!(f, "stack underflow"), + Self::BlockStackUnderflow => write!(f, "label stack underflow"), + Self::ValueStackUnderflow => write!(f, "value stack underflow"), Self::InvalidStore => write!(f, "invalid store"), } } diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index faf7931..757cb85 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -58,7 +58,7 @@ 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))); + CallFrame::new(wasm_func.clone(), func_inst.owner, params.iter().map(|v| RawWasmValue::from(*v)), 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) diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 6bfe99c..3fc4fe0 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -61,7 +61,7 @@ impl ModuleInstance { // don't need to create a auxiliary frame etc. let idx = store.next_module_instance_idx(); - log::error!("Instantiating module at index {}", idx); + log::info!("Instantiating module at index {}", idx); let imports = imports.unwrap_or_default(); let mut addrs = imports.link(store, &module, idx)?; diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index d6c0553..824666d 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -11,7 +11,7 @@ // from a function, so we need to check if the label stack is empty macro_rules! break_to { ($cf:ident, $stack:ident, $break_to_relative:ident) => {{ - if $cf.break_to(*$break_to_relative, &mut $stack.values).is_none() { + 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 { @@ -53,7 +53,6 @@ macro_rules! mem_load { const LEN: usize = core::mem::size_of::<$load_type>(); let val = mem_ref.load_as::(addr, $arg.align as usize)?; - // let loaded_value = mem_ref.load_as::<$load_type>(addr, $arg.align as usize)?; $stack.values.push((val as $target_type).into()); }}; } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 7e34ac1..78f6ed2 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -4,7 +4,7 @@ use core::ops::{BitAnd, BitOr, BitXor, Neg}; use tinywasm_types::{ElementKind, ValType}; use super::{InterpreterRuntime, Stack}; -use crate::runtime::{BlockType, CallFrame, LabelFrame}; +use crate::runtime::{BlockFrame, BlockType, CallFrame}; use crate::{cold, log, unlikely}; use crate::{Error, FuncContext, ModuleInstance, Result, Store, Trap}; @@ -32,8 +32,13 @@ impl InterpreterRuntime { match exec_one(&mut cf, stack, store, ¤t_module) { // Continue execution at the new top of the call stack Ok(ExecResult::Call) => { + let old = cf.block_ptr; cf = stack.call_stack.pop()?; + if old > cf.block_ptr { + stack.blocks.truncate(old); + } + // keeping the pointer seperate from the call frame is about 2% faster // than storing it in the call frame if cf.func_instance.1 != current_module.id() { @@ -123,7 +128,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }; let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?; - let call_frame = CallFrame::new(wasm_func, func_inst.owner, params); + let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len()); // push the call frame cf.instr_ptr += 1; // skip the call instruction @@ -180,7 +185,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?; - let call_frame = CallFrame::new(wasm_func, func_inst.owner, params); + let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len()); // push the call frame cf.instr_ptr += 1; // skip the call instruction @@ -194,8 +199,8 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M If(args, else_offset, end_offset) => { // truthy value is on the top of the stack, so enter the then block if stack.values.pop_t::()? != 0 { - cf.enter_label( - LabelFrame::new( + cf.enter_block( + BlockFrame::new( cf.instr_ptr, cf.instr_ptr + *end_offset, stack.values.len(), // - params, @@ -204,13 +209,14 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M module, ), &mut stack.values, + &mut stack.blocks, ); return Ok(ExecResult::Ok); } // falsy value is on the top of the stack if let Some(else_offset) = else_offset { - let label = LabelFrame::new( + let label = BlockFrame::new( cf.instr_ptr + *else_offset, cf.instr_ptr + *end_offset, stack.values.len(), // - params, @@ -219,15 +225,15 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M module, ); cf.instr_ptr += *else_offset; - cf.enter_label(label, &mut stack.values); + cf.enter_block(label, &mut stack.values, &mut stack.blocks); } else { cf.instr_ptr += *end_offset; } } Loop(args, end_offset) => { - cf.enter_label( - LabelFrame::new( + cf.enter_block( + BlockFrame::new( cf.instr_ptr, cf.instr_ptr + *end_offset, stack.values.len(), // - params, @@ -236,12 +242,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M module, ), &mut stack.values, + &mut stack.blocks, ); } Block(args, end_offset) => { - cf.enter_label( - LabelFrame::new( + cf.enter_block( + BlockFrame::new( cf.instr_ptr, cf.instr_ptr + *end_offset, stack.values.len(), // - params, @@ -250,6 +257,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M module, ), &mut stack.values, + &mut stack.blocks, ); } @@ -291,10 +299,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }, EndFunc => { - assert!( - cf.labels.len() == 0, - "endfunc: block frames not empty, this should have been validated by the parser" - ); + if stack.blocks.len() != cf.block_ptr { + cold(); + panic!("endfunc: block frames not empty, this should have been validated by the parser"); + } match stack.call_stack.is_empty() { true => return Ok(ExecResult::Return), @@ -304,7 +312,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // We're essentially using else as a EndBlockFrame instruction for if blocks Else(end_offset) => { - let Some(block) = cf.labels.pop() else { + let Some(block) = stack.blocks.pop() else { cold(); panic!("else: no label to end, this should have been validated by the parser"); }; @@ -316,16 +324,28 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M EndBlockFrame => { // remove the label from the label stack - let Some(block) = cf.labels.pop() else { + let Some(block) = stack.blocks.pop() else { cold(); - panic!("end: no label to end, this should have been validated by the parser"); + panic!("end blockframe: no label to end, this should have been validated by the parser"); }; - stack.values.truncate_keep(block.stack_ptr, block.results) + + stack.values.truncate_keep(block.stack_ptr, block.results); } 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) => cf.set_local(*local_index as usize, *stack.values.last()?), + 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) + } GlobalGet(global_index) => { let idx = module.resolve_global_addr(*global_index); diff --git a/crates/tinywasm/src/runtime/stack.rs b/crates/tinywasm/src/runtime/stack.rs index 31c41f0..3db3c4b 100644 --- a/crates/tinywasm/src/runtime/stack.rs +++ b/crates/tinywasm/src/runtime/stack.rs @@ -1,20 +1,21 @@ -mod blocks; +mod block_stack; mod call_stack; mod value_stack; use self::{call_stack::CallStack, value_stack::ValueStack}; -pub(crate) use blocks::{BlockType, LabelFrame}; +pub(crate) use block_stack::{BlockFrame, BlockStack, BlockType}; pub(crate) use call_stack::CallFrame; /// A WebAssembly Stack #[derive(Debug)] pub struct Stack { pub(crate) values: ValueStack, + pub(crate) blocks: BlockStack, pub(crate) call_stack: CallStack, } impl Stack { pub(crate) fn new(call_frame: CallFrame) -> Self { - Self { values: ValueStack::default(), call_stack: CallStack::new(call_frame) } + Self { values: ValueStack::default(), blocks: BlockStack::default(), call_stack: CallStack::new(call_frame) } } } diff --git a/crates/tinywasm/src/runtime/stack/blocks.rs b/crates/tinywasm/src/runtime/stack/block_stack.rs similarity index 72% rename from crates/tinywasm/src/runtime/stack/blocks.rs rename to crates/tinywasm/src/runtime/stack/block_stack.rs index c04632c..cf7a0d4 100644 --- a/crates/tinywasm/src/runtime/stack/blocks.rs +++ b/crates/tinywasm/src/runtime/stack/block_stack.rs @@ -1,33 +1,28 @@ +use crate::{unlikely, ModuleInstance}; use alloc::vec::Vec; use tinywasm_types::BlockArgs; -use crate::{unlikely, ModuleInstance}; - -#[derive(Debug, Clone)] -pub(crate) struct Labels(Vec); // TODO: maybe Box<[LabelFrame]> by analyzing the lable count when parsing the module? - -impl Labels { - #[inline] - pub(crate) fn new() -> Self { - // this is somehow a lot faster than Vec::with_capacity(128) or even using Default::default() in the benchmarks - Self(Vec::new()) - } +#[derive(Debug, Clone, Default)] +pub(crate) struct BlockStack(Vec); // TODO: maybe Box<[LabelFrame]> by analyzing the lable count when parsing the module? +impl BlockStack { #[inline] pub(crate) fn len(&self) -> usize { self.0.len() } #[inline] - pub(crate) fn push(&mut self, label: LabelFrame) { - self.0.push(label); + pub(crate) fn push(&mut self, block: BlockFrame) { + self.0.push(block); } #[inline] /// get the label at the given index, where 0 is the top of the stack - pub(crate) fn get_relative_to_top(&self, index: usize) -> Option<&LabelFrame> { + pub(crate) fn get_relative_to(&self, index: usize, offset: usize) -> Option<&BlockFrame> { + let len = self.0.len() - offset; + // the vast majority of wasm functions don't use break to return - if unlikely(index >= self.0.len()) { + if unlikely(index >= len) { return None; } @@ -35,7 +30,7 @@ impl Labels { } #[inline] - pub(crate) fn pop(&mut self) -> Option { + pub(crate) fn pop(&mut self) -> Option { self.0.pop() } @@ -47,7 +42,7 @@ impl Labels { } #[derive(Debug, Clone)] -pub(crate) struct LabelFrame { +pub(crate) struct BlockFrame { // position of the instruction pointer when the block was entered pub(crate) instr_ptr: usize, // position of the end instruction of the block @@ -60,7 +55,7 @@ pub(crate) struct LabelFrame { pub(crate) ty: BlockType, } -impl LabelFrame { +impl BlockFrame { #[inline] pub(crate) fn new( instr_ptr: usize, diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index a902833..1c441a8 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -1,11 +1,12 @@ use alloc::{boxed::Box, rc::Rc, vec::Vec}; use tinywasm_types::{ModuleInstanceAddr, WasmFunction}; -use super::{blocks::Labels, LabelFrame}; use crate::runtime::{BlockType, RawWasmValue}; use crate::unlikely; use crate::{Error, Result, Trap}; +use super::BlockFrame; + const CALL_STACK_SIZE: usize = 128; const CALL_STACK_MAX_SIZE: usize = 1024; @@ -31,7 +32,7 @@ impl CallStack { pub(crate) fn pop(&mut self) -> Result { match self.stack.pop() { Some(frame) => Ok(frame), - None => Err(Error::CallStackEmpty), + None => Err(Error::CallStackUnderflow), } } @@ -48,25 +49,35 @@ impl CallStack { #[derive(Debug, Clone)] pub(crate) struct CallFrame { pub(crate) instr_ptr: usize, + pub(crate) block_ptr: usize, pub(crate) func_instance: (Rc, ModuleInstanceAddr), - pub(crate) labels: Labels, pub(crate) locals: Box<[RawWasmValue]>, } impl CallFrame { /// Push a new label to the label stack and ensure the stack has the correct values - pub(crate) fn enter_label(&mut self, label_frame: LabelFrame, stack: &mut super::ValueStack) { - if label_frame.params > 0 { - stack.extend_from_within((label_frame.stack_ptr - label_frame.params)..label_frame.stack_ptr); + pub(crate) fn enter_block( + &mut self, + block_frame: BlockFrame, + values: &mut super::ValueStack, + blocks: &mut super::BlockStack, + ) { + if block_frame.params > 0 { + values.extend_from_within((block_frame.stack_ptr - block_frame.params)..block_frame.stack_ptr); } - self.labels.push(label_frame); + blocks.push(block_frame); } /// Break to a block at the given index (relative to the current frame) /// Returns `None` if there is no block at the given index (e.g. if we need to return, this is handled by the caller) - pub(crate) fn break_to(&mut self, break_to_relative: u32, value_stack: &mut super::ValueStack) -> Option<()> { - let break_to = self.labels.get_relative_to_top(break_to_relative as usize)?; + pub(crate) fn break_to( + &mut self, + break_to_relative: u32, + values: &mut super::ValueStack, + blocks: &mut super::BlockStack, + ) -> Option<()> { + let break_to = blocks.get_relative_to(break_to_relative as usize, self.block_ptr)?; // instr_ptr points to the label instruction, but the next step // will increment it by 1 since we're changing the "current" instr_ptr @@ -76,12 +87,12 @@ impl CallFrame { self.instr_ptr = break_to.instr_ptr; // We also want to push the params to the stack - value_stack.break_to(break_to.stack_ptr, break_to.params); + values.break_to(break_to.stack_ptr, break_to.params); // check if we're breaking to the loop if break_to_relative != 0 { // we also want to trim the label stack to the loop (but not including the loop) - self.labels.truncate(self.labels.len() - break_to_relative as usize); + blocks.truncate(blocks.len() - break_to_relative as usize); return Some(()); } } @@ -89,13 +100,13 @@ impl CallFrame { BlockType::Block | BlockType::If | BlockType::Else => { // this is a block, so we want to jump to the next instruction after the block ends // We also want to push the block's results to the stack - value_stack.break_to(break_to.stack_ptr, break_to.results); + values.break_to(break_to.stack_ptr, break_to.results); // (the inst_ptr will be incremented by 1 before the next instruction is executed) self.instr_ptr = break_to.end_instr_ptr; // we also want to trim the label stack, including the block - self.labels.truncate(self.labels.len() - (break_to_relative as usize + 1)); + blocks.truncate(blocks.len() - (break_to_relative as usize + 1)); } } @@ -108,6 +119,7 @@ impl CallFrame { wasm_func_inst: Rc, owner: ModuleInstanceAddr, params: impl Iterator + ExactSizeIterator, + block_ptr: usize, ) -> Self { let locals = { let local_types = &wasm_func_inst.locals; @@ -118,7 +130,7 @@ impl CallFrame { locals.into_boxed_slice() }; - Self { instr_ptr: 0, func_instance: (wasm_func_inst, owner), locals, labels: Labels::new() } + Self { instr_ptr: 0, func_instance: (wasm_func_inst, owner), locals, block_ptr } } #[inline] diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index cc35bc2..9649177 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -63,7 +63,7 @@ impl ValueStack { Some(v) => Ok(v), None => { cold(); - Err(Error::StackUnderflow) + Err(Error::ValueStackUnderflow) } } } @@ -74,7 +74,7 @@ impl ValueStack { Some(v) => Ok(v.into()), None => { cold(); - Err(Error::StackUnderflow) + Err(Error::ValueStackUnderflow) } } } @@ -85,7 +85,7 @@ impl ValueStack { Some(v) => Ok(v), None => { cold(); - Err(Error::StackUnderflow) + Err(Error::ValueStackUnderflow) } } } @@ -105,7 +105,7 @@ impl ValueStack { pub(crate) fn last_n(&self, n: usize) -> Result<&[RawWasmValue]> { let len = self.stack.len(); if unlikely(len < n) { - return Err(Error::StackUnderflow); + return Err(Error::ValueStackUnderflow); } Ok(&self.stack[len - n..len]) } @@ -114,7 +114,7 @@ impl ValueStack { pub(crate) fn pop_n_rev(&mut self, n: usize) -> Result> { let len = self.stack.len(); if unlikely(len < n) { - return Err(Error::StackUnderflow); + return Err(Error::ValueStackUnderflow); } let res = self.stack.drain((len - n)..); Ok(res) diff --git a/examples/wasm-rust.rs b/examples/wasm-rust.rs index b57a1da..cff38f5 100644 --- a/examples/wasm-rust.rs +++ b/examples/wasm-rust.rs @@ -18,6 +18,8 @@ use tinywasm::{Extern, FuncContext, Imports, MemoryStringExt, Module, Store}; /// https://github.com/WebAssembly/binaryen /// fn main() -> Result<()> { + pretty_env_logger::init(); + let args = std::env::args().collect::>(); if args.len() < 2 { println!("Usage: cargo run --example wasm-rust "); From a0368bc39ac620b867a105ca0c9bc12824abcd9d Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sat, 24 Feb 2024 22:16:10 +0100 Subject: [PATCH 11/24] feat: switch to own fork of wasmparser Signed-off-by: Henry Gressmann --- Cargo.lock | 116 ++++++++++++++++-- crates/parser/Cargo.toml | 2 +- crates/parser/README.md | 4 +- crates/parser/src/conversion.rs | 76 ++++++++---- crates/parser/src/lib.rs | 34 ++++- .../tinywasm/src/runtime/interpreter/mod.rs | 6 +- 6 files changed, 197 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf9afe3..4fddf4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,19 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +dependencies = [ + "cfg-if", + "const-random", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.2" @@ -196,9 +209,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -395,6 +408,26 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" +[[package]] +name = "const-random" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -523,7 +556,7 @@ dependencies = [ "cranelift-entity", "fxhash", "hashbrown 0.12.3", - "indexmap", + "indexmap 1.9.3", "log", "smallvec", ] @@ -850,6 +883,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "eyre" version = "0.6.12" @@ -1020,7 +1059,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] @@ -1059,7 +1098,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.8", ] [[package]] @@ -1067,6 +1106,9 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash 0.8.9", +] [[package]] name = "hermit-abi" @@ -1149,6 +1191,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + [[package]] name = "indexmap-nostd" version = "0.4.0" @@ -1678,7 +1730,7 @@ dependencies = [ "bytecheck 0.6.12", "bytes", "hashbrown 0.12.3", - "indexmap", + "indexmap 1.9.3", "ptr_meta", "rend", "rkyv_derive", @@ -1974,6 +2026,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -2035,7 +2096,7 @@ version = "0.4.1" dependencies = [ "log", "tinywasm-types", - "wasmparser-nostd", + "tinywasm-wasmparser", ] [[package]] @@ -2057,6 +2118,19 @@ dependencies = [ "rkyv", ] +[[package]] +name = "tinywasm-wasmparser" +version = "0.200.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fce1b3563499af272f7e88c8b0357e740e62c2bcf59f134992698d35af96da" +dependencies = [ + "ahash 0.8.9", + "bitflags 2.4.2", + "hashbrown 0.14.3", + "indexmap 2.2.3", + "semver", +] + [[package]] name = "tracing" version = "0.1.40" @@ -2282,7 +2356,7 @@ dependencies = [ "bytes", "cfg-if", "derivative", - "indexmap", + "indexmap 1.9.3", "js-sys", "more-asserts", "rustc-demangle", @@ -2388,7 +2462,7 @@ dependencies = [ "bytecheck 0.6.12", "enum-iterator", "enumset", - "indexmap", + "indexmap 1.9.3", "more-asserts", "rkyv", "target-lexicon", @@ -2410,7 +2484,7 @@ dependencies = [ "derivative", "enum-iterator", "fnv", - "indexmap", + "indexmap 1.9.3", "lazy_static", "libc", "mach", @@ -2460,7 +2534,7 @@ version = "0.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" dependencies = [ - "indexmap", + "indexmap 1.9.3", "url", ] @@ -2767,3 +2841,23 @@ dependencies = [ "once_cell", "pkg-config", ] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 0fee8e1..73eec99 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.100", package="wasmparser-nostd", default-features=false} +wasmparser={version="0.200.2", 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/README.md b/crates/parser/README.md index 8ac7a30..563cd6b 100644 --- a/crates/parser/README.md +++ b/crates/parser/README.md @@ -1,7 +1,7 @@ # `tinywasm-parser` -This crate provides a parser that can parse WebAssembly modules into a TinyWasm module. It is based on -[`wasmparser_nostd`](https://crates.io/crates/wasmparser_nostd) and used by [`tinywasm`](https://crates.io/crates/tinywasm). +This crate provides a parser that can parse WebAssembly modules into a TinyWasm module. +It uses [my fork](https://crates.io/crates/tinywasm-wasmparser) of the [`wasmparser`](https://crates.io/crates/wasmparser) crate that has been modified to be compatible with `no_std` environments. ## Features diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 68567f3..f81b9e7 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -15,26 +15,34 @@ pub(crate) fn convert_module_elements<'a, T: IntoIterator) -> Result { let kind = match element.kind { wasmparser::ElementKind::Active { table_index, offset_expr } => tinywasm_types::ElementKind::Active { - table: table_index, + table: table_index.unwrap_or(0), offset: process_const_operators(offset_expr.get_operators_reader())?, }, wasmparser::ElementKind::Passive => tinywasm_types::ElementKind::Passive, wasmparser::ElementKind::Declared => tinywasm_types::ElementKind::Declared, }; - let items = match element.items { + match element.items { wasmparser::ElementItems::Functions(funcs) => { - funcs.into_iter().map(|func| Ok(ElementItem::Func(func?))).collect::>>()?.into_boxed_slice() + let items = funcs + .into_iter() + .map(|func| Ok(ElementItem::Func(func?))) + .collect::>>()? + .into_boxed_slice(); + + Ok(tinywasm_types::Element { kind, items, ty: ValType::RefFunc, range: element.range }) } - wasmparser::ElementItems::Expressions(exprs) => exprs - .into_iter() - .map(|expr| Ok(ElementItem::Expr(process_const_operators(expr?.get_operators_reader())?))) - .collect::>>()? - .into_boxed_slice(), - }; + wasmparser::ElementItems::Expressions(ty, exprs) => { + let items = exprs + .into_iter() + .map(|expr| Ok(ElementItem::Expr(process_const_operators(expr?.get_operators_reader())?))) + .collect::>>()? + .into_boxed_slice(); - Ok(tinywasm_types::Element { kind, items, ty: convert_valtype(&element.ty), range: element.range }) + Ok(tinywasm_types::Element { kind, items, ty: convert_reftype(&ty), range: element.range }) + } + } } pub(crate) fn convert_module_data_sections<'a, T: IntoIterator>>>( @@ -71,7 +79,11 @@ pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result ImportKind::Function(ty), - wasmparser::TypeRef::Table(ty) => ImportKind::Table(convert_module_table(ty)?), + wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType { + element_type: convert_reftype(&ty.element_type), + size_initial: ty.initial, + size_max: ty.maximum, + }), wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)?), wasmparser::TypeRef::Global(ty) => { ImportKind::Global(GlobalType { mutable: ty.mutable, ty: convert_valtype(&ty.content_type) }) @@ -103,16 +115,16 @@ pub(crate) fn convert_module_memory(memory: wasmparser::MemoryType) -> Result>>( +pub(crate) fn convert_module_tables<'a, T: IntoIterator>>>( table_types: T, ) -> Result> { let table_type = table_types.into_iter().map(|table| convert_module_table(table?)).collect::>>()?; Ok(table_type) } -pub(crate) fn convert_module_table(table: wasmparser::TableType) -> Result { - let ty = convert_valtype(&table.element_type); - Ok(TableType { element_type: ty, size_initial: table.initial, size_max: table.maximum }) +pub(crate) fn convert_module_table(table: wasmparser::Table<'_>) -> Result { + let ty = convert_reftype(&table.ty.element_type); + Ok(TableType { element_type: ty, size_initial: table.ty.initial, size_max: table.ty.maximum }) } pub(crate) fn convert_module_globals<'a, T: IntoIterator>>>( @@ -168,8 +180,15 @@ pub(crate) fn convert_module_code( Ok(CodeSection { locals: locals.into_boxed_slice(), body }) } -pub(crate) fn convert_module_type(ty: wasmparser::Type) -> Result { - let wasmparser::Type::Func(ty) = ty; +pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result { + let mut types = ty.types(); + + if types.len() != 1 { + return Err(crate::ParseError::UnsupportedOperator( + "Expected exactly one type in the type section".to_string(), + )); + } + let ty = types.next().unwrap().unwrap_func(); let params = ty.params().iter().map(|p| Ok(convert_valtype(p))).collect::>>()?.into_boxed_slice(); @@ -188,6 +207,14 @@ pub(crate) fn convert_blocktype(blocktype: wasmparser::BlockType) -> BlockArgs { } } +pub(crate) fn convert_reftype(reftype: &wasmparser::RefType) -> ValType { + match reftype { + _ if reftype.is_func_ref() => ValType::RefFunc, + _ if reftype.is_extern_ref() => ValType::RefExtern, + _ => unimplemented!("Unsupported reference type: {:?}", reftype), + } +} + pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType { use wasmparser::ValType::*; match valtype { @@ -195,9 +222,8 @@ pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType { I64 => ValType::I64, F32 => ValType::F32, F64 => ValType::F64, + Ref(r) => convert_reftype(r), V128 => unimplemented!("128-bit values are not supported yet"), - FuncRef => ValType::RefFunc, - ExternRef => ValType::RefExtern, } } @@ -217,7 +243,7 @@ pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result) -> Result { match op { - wasmparser::Operator::RefNull { ty } => Ok(ConstInstruction::RefNull(convert_valtype(&ty))), + wasmparser::Operator::RefNull { hty } => Ok(ConstInstruction::RefNull(convert_heaptype(hty))), wasmparser::Operator::RefFunc { function_index } => Ok(ConstInstruction::RefFunc(function_index)), wasmparser::Operator::I32Const { value } => Ok(ConstInstruction::I32Const(value)), wasmparser::Operator::I64Const { value } => Ok(ConstInstruction::I64Const(value)), @@ -228,6 +254,14 @@ pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result ValType { + match heap { + wasmparser::HeapType::Func => ValType::RefFunc, + wasmparser::HeapType::Extern => ValType::RefExtern, + _ => unimplemented!("Unsupported heap type: {:?}", heap), + } +} + pub(crate) fn process_operators( ops: OperatorsReader<'_>, mut validator: FuncValidator, @@ -357,7 +391,7 @@ pub(crate) fn process_operators( I64Const { value } => Instruction::I64Const(value), F32Const { value } => Instruction::F32Const(f32::from_bits(value.bits())), F64Const { value } => Instruction::F64Const(f64::from_bits(value.bits())), - RefNull { ty } => Instruction::RefNull(convert_valtype(&ty)), + RefNull { hty } => Instruction::RefNull(convert_heaptype(hty)), RefIsNull => Instruction::RefIsNull, RefFunc { function_index } => Instruction::RefFunc(function_index), I32Load { memarg } => Instruction::I32Load(convert_memarg(memarg)), diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 8cc34db..72b3f81 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -32,7 +32,7 @@ use alloc::{string::ToString, vec::Vec}; pub use error::*; use module::ModuleReader; use tinywasm_types::{TypedWasmFunction, WasmFunction}; -use wasmparser::Validator; +use wasmparser::{Validator, WasmFeatures}; pub use tinywasm_types::TinyWasmModule; @@ -46,10 +46,38 @@ impl Parser { Self {} } + fn create_validator(&self) -> Validator { + let features = WasmFeatures { + bulk_memory: true, + floats: true, + function_references: true, + multi_value: true, + mutable_global: true, + reference_types: true, + sign_extension: true, + saturating_float_to_int: true, + + component_model: false, + component_model_nested_names: false, + component_model_values: false, + exceptions: false, + extended_const: false, + gc: false, + memory64: false, + memory_control: false, + relaxed_simd: false, + simd: false, + tail_call: false, + threads: false, + multi_memory: false, // should be working mostly + }; + Validator::new_with_features(features) + } + /// Parse a [`TinyWasmModule`] from bytes pub fn parse_module_bytes(&self, wasm: impl AsRef<[u8]>) -> Result { let wasm = wasm.as_ref(); - let mut validator = Validator::new(); + let mut validator = self.create_validator(); let mut reader = ModuleReader::new(); for payload in wasmparser::Parser::new(0).parse_all(wasm) { @@ -79,7 +107,7 @@ impl Parser { pub fn parse_module_stream(&self, mut stream: impl std::io::Read) -> Result { use alloc::format; - let mut validator = Validator::new(); + let mut validator = self.create_validator(); let mut reader = ModuleReader::new(); let mut buffer = Vec::new(); let mut parser = wasmparser::Parser::new(0); diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 78f6ed2..b06152d 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -203,7 +203,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M BlockFrame::new( cf.instr_ptr, cf.instr_ptr + *end_offset, - stack.values.len(), // - params, + stack.values.len(), BlockType::If, args, module, @@ -219,7 +219,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let label = BlockFrame::new( cf.instr_ptr + *else_offset, cf.instr_ptr + *end_offset, - stack.values.len(), // - params, + stack.values.len(), BlockType::Else, args, module, @@ -236,7 +236,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M BlockFrame::new( cf.instr_ptr, cf.instr_ptr + *end_offset, - stack.values.len(), // - params, + stack.values.len(), BlockType::Loop, args, module, From 062515c39154b29bb0406b207e3c67bae399a449 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sun, 25 Feb 2024 17:51:41 +0100 Subject: [PATCH 12/24] feat: new parser architecture Signed-off-by: Henry Gressmann --- BENCHMARKS.md | 7 +- crates/benchmarks/benches/selfhosted.rs | 2 +- crates/benchmarks/benches/util/mod.rs | 5 + crates/parser/src/conversion.rs | 336 +--------------- crates/parser/src/lib.rs | 8 +- crates/parser/src/module.rs | 27 +- crates/parser/src/visit.rs | 487 ++++++++++++++++++++++++ crates/types/src/instructions.rs | 2 +- 8 files changed, 521 insertions(+), 353 deletions(-) create mode 100644 crates/parser/src/visit.rs diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 673545e..5189b55 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -30,13 +30,12 @@ All runtimes are compiled with the following settings: | Benchmark | Native | TinyWasm\* | Wasmi | Wasmer (Single Pass) | | ------------ | -------- | ---------- | --------- | -------------------- | -| `fib` | \*\* | ` 44.11µs` | `49.46µs` | ` 50.65µs` | +| `fib` | \*\* | ` 43.81µs` | `48.60µs` | ` 43.97µs` | | `fib-rec` | `0.26ms` | ` 20.99ms` | ` 4.64ms` | ` 0.50ms` | -| `argon2id` | `0.53ms` | `109.38ms` | `45.85ms` | ` 4.82ms` | -| `selfhosted` | `0.05ms` | ` 1.97ms` | ` 4.26ms` | `260.32ms` | +| `argon2id` | `0.53ms` | `107.77ms` | `47.76ms` | ` 4.49ms` | +| `selfhosted` | `0.06ms` | ` 2.88ms` | ` 6.20ms` | `359.33ms` | _\* converting WASM to TinyWasm bytecode is not included. I takes ~7ms to convert `tinywasm.wasm` to TinyWasm bytecode._ - _\*\* essentially instant as it gets computed at compile time._ ### Fib diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 542343e..94dfdce 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -55,7 +55,7 @@ const TINYWASM: &[u8] = include_bytes!("../../../examples/rust/out/tinywasm.wasm fn criterion_benchmark(c: &mut Criterion) { { let mut group = c.benchmark_group("selfhosted-parse"); - group.bench_function("tinywasm", |b| b.iter(|| util::wasm_to_twasm(TINYWASM))); + group.bench_function("tinywasm", |b| b.iter(|| util::parse_wasm(TINYWASM))); } { diff --git a/crates/benchmarks/benches/util/mod.rs b/crates/benchmarks/benches/util/mod.rs index d6594b9..0df2a52 100644 --- a/crates/benchmarks/benches/util/mod.rs +++ b/crates/benchmarks/benches/util/mod.rs @@ -2,6 +2,11 @@ use tinywasm::{self, parser::Parser, types::TinyWasmModule}; +pub fn parse_wasm(wasm: &[u8]) -> TinyWasmModule { + let parser = Parser::new(); + parser.parse_module_bytes(wasm).expect("parse_module_bytes") +} + pub fn wasm_to_twasm(wasm: &[u8]) -> Vec { let parser = Parser::new(); let res = parser.parse_module_bytes(wasm).expect("parse_module_bytes"); diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index f81b9e7..03b5f82 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -1,10 +1,9 @@ -use crate::log; +use crate::visit::process_operators; +use crate::Result; use alloc::{boxed::Box, format, string::ToString, vec::Vec}; use tinywasm_types::*; use wasmparser::{FuncValidator, OperatorsReader, ValidatorResources}; -use crate::{module::CodeSection, Result}; - pub(crate) fn convert_module_elements<'a, T: IntoIterator>>>( elements: T, ) -> Result> { @@ -160,12 +159,12 @@ pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result, mut validator: FuncValidator, -) -> Result { +) -> Result<(Box<[Instruction]>, Box<[ValType]>)> { let locals_reader = func.get_locals_reader()?; let count = locals_reader.get_count(); let pos = locals_reader.original_position(); - let mut locals = Vec::with_capacity(count as usize); + let mut locals = Vec::with_capacity(count as usize); for (i, local) in locals_reader.into_iter().enumerate() { let local = local?; validator.define_locals(pos + i, local.0, local.1)?; @@ -174,10 +173,9 @@ pub(crate) fn convert_module_code( } } - let body_reader = func.get_operators_reader()?; - let body = process_operators(body_reader, validator)?; - - Ok(CodeSection { locals: locals.into_boxed_slice(), body }) + let body = process_operators(&mut validator, &func)?; + let locals = locals.into_boxed_slice(); + Ok((body, locals)) } pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result { @@ -254,328 +252,10 @@ pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result ValType { +pub(crate) fn convert_heaptype(heap: wasmparser::HeapType) -> ValType { match heap { wasmparser::HeapType::Func => ValType::RefFunc, wasmparser::HeapType::Extern => ValType::RefExtern, _ => unimplemented!("Unsupported heap type: {:?}", heap), } } - -pub(crate) fn process_operators( - ops: OperatorsReader<'_>, - mut validator: FuncValidator, -) -> Result> { - let mut instructions = Vec::with_capacity(1024); - let mut labels_ptrs = Vec::with_capacity(32); - // indexes into the instructions array - let mut offset = ops.original_position(); - - for op in ops { - let op = match op { - Ok(op) => op, - Err(e) => { - cold(); - log::error!("Error while processing operators: {:?}", e); - return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string())); - } - }; - - match validator.op(offset, &op) { - Ok(_) => (), - Err(e) => { - cold(); - log::error!("Error while processing operators: {:?}", e); - return Err(crate::ParseError::UnsupportedOperator("Error while processing operators".to_string())); - } - } - offset += 1; - - use wasmparser::Operator::*; - let res = match op { - BrTable { targets } => { - let def = targets.default(); - - let instrs = targets - .targets() - .map(|t| t.map(Instruction::BrLabel)) - .collect::, wasmparser::BinaryReaderError>>()?; - - instructions.push(Instruction::BrTable(def, instrs.len())); - instructions.extend(instrs); - continue; - } - Unreachable => Instruction::Unreachable, - Nop => Instruction::Nop, - Block { blockty } => { - labels_ptrs.push(instructions.len()); - Instruction::Block(convert_blocktype(blockty), 0) - } - Loop { blockty } => { - labels_ptrs.push(instructions.len()); - Instruction::Loop(convert_blocktype(blockty), 0) - } - If { blockty } => { - labels_ptrs.push(instructions.len()); - Instruction::If(convert_blocktype(blockty), None, 0) - } - Else => { - labels_ptrs.push(instructions.len()); - Instruction::Else(0) - } - End => { - if let Some(label_pointer) = labels_ptrs.pop() { - let current_instr_ptr = instructions.len(); - - // last_label_pointer is Some if we're ending a block - match instructions[label_pointer] { - Instruction::Else(ref mut else_instr_end_offset) => { - *else_instr_end_offset = current_instr_ptr - label_pointer; - - // since we're ending an else block, we need to end the if block as well - let if_label_pointer = labels_ptrs.pop().ok_or(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - ))?; - - let if_instruction = &mut instructions[if_label_pointer]; - let Instruction::If(_, ref mut else_offset, ref mut end_offset) = if_instruction else { - return Err(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - )); - }; - - *else_offset = Some(label_pointer - if_label_pointer); - *end_offset = current_instr_ptr - if_label_pointer; - } - 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; - } - _ => { - return Err(crate::ParseError::UnsupportedOperator( - "Expected to end a block, but the last label was not a block".to_string(), - )) - } - } - - Instruction::EndBlockFrame - } else { - // last_label_pointer is None if we're ending the function - Instruction::EndFunc - } - } - - Br { relative_depth } => Instruction::Br(relative_depth), - BrIf { relative_depth } => Instruction::BrIf(relative_depth), - Return => Instruction::Return, - Call { function_index } => Instruction::Call(function_index), - CallIndirect { type_index, table_index, .. } => Instruction::CallIndirect(type_index, table_index), - Drop => Instruction::Drop, - Select => Instruction::Select(None), - TypedSelect { ty } => Instruction::Select(Some(convert_valtype(&ty))), - LocalGet { local_index } => Instruction::LocalGet(local_index), - LocalSet { local_index } => Instruction::LocalSet(local_index), - LocalTee { local_index } => Instruction::LocalTee(local_index), - GlobalGet { global_index } => Instruction::GlobalGet(global_index), - GlobalSet { global_index } => Instruction::GlobalSet(global_index), - MemorySize { mem, mem_byte } => Instruction::MemorySize(mem, mem_byte), - MemoryGrow { mem, mem_byte } => Instruction::MemoryGrow(mem, mem_byte), - - MemoryCopy { dst_mem, src_mem } => Instruction::MemoryCopy(src_mem, dst_mem), - MemoryFill { mem } => Instruction::MemoryFill(mem), - MemoryInit { data_index, mem } => Instruction::MemoryInit(data_index, mem), - DataDrop { data_index } => Instruction::DataDrop(data_index), - - I32Const { value } => Instruction::I32Const(value), - I64Const { value } => Instruction::I64Const(value), - F32Const { value } => Instruction::F32Const(f32::from_bits(value.bits())), - F64Const { value } => Instruction::F64Const(f64::from_bits(value.bits())), - RefNull { hty } => Instruction::RefNull(convert_heaptype(hty)), - RefIsNull => Instruction::RefIsNull, - RefFunc { function_index } => Instruction::RefFunc(function_index), - I32Load { memarg } => Instruction::I32Load(convert_memarg(memarg)), - I64Load { memarg } => Instruction::I64Load(convert_memarg(memarg)), - F32Load { memarg } => Instruction::F32Load(convert_memarg(memarg)), - F64Load { memarg } => Instruction::F64Load(convert_memarg(memarg)), - I32Load8S { memarg } => Instruction::I32Load8S(convert_memarg(memarg)), - I32Load8U { memarg } => Instruction::I32Load8U(convert_memarg(memarg)), - I32Load16S { memarg } => Instruction::I32Load16S(convert_memarg(memarg)), - I32Load16U { memarg } => Instruction::I32Load16U(convert_memarg(memarg)), - I64Load8S { memarg } => Instruction::I64Load8S(convert_memarg(memarg)), - I64Load8U { memarg } => Instruction::I64Load8U(convert_memarg(memarg)), - I64Load16S { memarg } => Instruction::I64Load16S(convert_memarg(memarg)), - I64Load16U { memarg } => Instruction::I64Load16U(convert_memarg(memarg)), - I64Load32S { memarg } => Instruction::I64Load32S(convert_memarg(memarg)), - I64Load32U { memarg } => Instruction::I64Load32U(convert_memarg(memarg)), - I32Store { memarg } => Instruction::I32Store(convert_memarg(memarg)), - I64Store { memarg } => Instruction::I64Store(convert_memarg(memarg)), - F32Store { memarg } => Instruction::F32Store(convert_memarg(memarg)), - F64Store { memarg } => Instruction::F64Store(convert_memarg(memarg)), - I32Store8 { memarg } => Instruction::I32Store8(convert_memarg(memarg)), - I32Store16 { memarg } => Instruction::I32Store16(convert_memarg(memarg)), - I64Store8 { memarg } => Instruction::I64Store8(convert_memarg(memarg)), - I64Store16 { memarg } => Instruction::I64Store16(convert_memarg(memarg)), - I64Store32 { memarg } => Instruction::I64Store32(convert_memarg(memarg)), - I32Eqz => Instruction::I32Eqz, - I32Eq => Instruction::I32Eq, - I32Ne => Instruction::I32Ne, - I32LtS => Instruction::I32LtS, - I32LtU => Instruction::I32LtU, - I32GtS => Instruction::I32GtS, - I32GtU => Instruction::I32GtU, - I32LeS => Instruction::I32LeS, - I32LeU => Instruction::I32LeU, - I32GeS => Instruction::I32GeS, - I32GeU => Instruction::I32GeU, - I64Eqz => Instruction::I64Eqz, - I64Eq => Instruction::I64Eq, - I64Ne => Instruction::I64Ne, - I64LtS => Instruction::I64LtS, - I64LtU => Instruction::I64LtU, - I64GtS => Instruction::I64GtS, - I64GtU => Instruction::I64GtU, - I64LeS => Instruction::I64LeS, - I64LeU => Instruction::I64LeU, - I64GeS => Instruction::I64GeS, - I64GeU => Instruction::I64GeU, - F32Eq => Instruction::F32Eq, - F32Ne => Instruction::F32Ne, - F32Lt => Instruction::F32Lt, - F32Gt => Instruction::F32Gt, - F32Le => Instruction::F32Le, - F32Ge => Instruction::F32Ge, - F64Eq => Instruction::F64Eq, - F64Ne => Instruction::F64Ne, - F64Lt => Instruction::F64Lt, - F64Gt => Instruction::F64Gt, - F64Le => Instruction::F64Le, - F64Ge => Instruction::F64Ge, - I32Clz => Instruction::I32Clz, - I32Ctz => Instruction::I32Ctz, - I32Popcnt => Instruction::I32Popcnt, - I32Add => Instruction::I32Add, - I32Sub => Instruction::I32Sub, - I32Mul => Instruction::I32Mul, - I32DivS => Instruction::I32DivS, - I32DivU => Instruction::I32DivU, - I32RemS => Instruction::I32RemS, - I32RemU => Instruction::I32RemU, - I32And => Instruction::I32And, - I32Or => Instruction::I32Or, - I32Xor => Instruction::I32Xor, - I32Shl => Instruction::I32Shl, - I32ShrS => Instruction::I32ShrS, - I32ShrU => Instruction::I32ShrU, - I32Rotl => Instruction::I32Rotl, - I32Rotr => Instruction::I32Rotr, - I64Clz => Instruction::I64Clz, - I64Ctz => Instruction::I64Ctz, - I64Popcnt => Instruction::I64Popcnt, - I64Add => Instruction::I64Add, - I64Sub => Instruction::I64Sub, - I64Mul => Instruction::I64Mul, - I64DivS => Instruction::I64DivS, - I64DivU => Instruction::I64DivU, - I64RemS => Instruction::I64RemS, - I64RemU => Instruction::I64RemU, - I64And => Instruction::I64And, - I64Or => Instruction::I64Or, - I64Xor => Instruction::I64Xor, - I64Shl => Instruction::I64Shl, - I64ShrS => Instruction::I64ShrS, - I64ShrU => Instruction::I64ShrU, - I64Rotl => Instruction::I64Rotl, - I64Rotr => Instruction::I64Rotr, - F32Abs => Instruction::F32Abs, - F32Neg => Instruction::F32Neg, - F32Ceil => Instruction::F32Ceil, - F32Floor => Instruction::F32Floor, - F32Trunc => Instruction::F32Trunc, - F32Nearest => Instruction::F32Nearest, - F32Sqrt => Instruction::F32Sqrt, - F32Add => Instruction::F32Add, - F32Sub => Instruction::F32Sub, - F32Mul => Instruction::F32Mul, - F32Div => Instruction::F32Div, - F32Min => Instruction::F32Min, - F32Max => Instruction::F32Max, - F32Copysign => Instruction::F32Copysign, - F64Abs => Instruction::F64Abs, - F64Neg => Instruction::F64Neg, - F64Ceil => Instruction::F64Ceil, - F64Floor => Instruction::F64Floor, - F64Trunc => Instruction::F64Trunc, - F64Nearest => Instruction::F64Nearest, - F64Sqrt => Instruction::F64Sqrt, - F64Add => Instruction::F64Add, - F64Sub => Instruction::F64Sub, - F64Mul => Instruction::F64Mul, - F64Div => Instruction::F64Div, - F64Min => Instruction::F64Min, - F64Max => Instruction::F64Max, - F64Copysign => Instruction::F64Copysign, - I32WrapI64 => Instruction::I32WrapI64, - I32TruncF32S => Instruction::I32TruncF32S, - I32TruncF32U => Instruction::I32TruncF32U, - I32TruncF64S => Instruction::I32TruncF64S, - I32TruncF64U => Instruction::I32TruncF64U, - I64Extend8S => Instruction::I64Extend8S, - I64Extend16S => Instruction::I64Extend16S, - I64Extend32S => Instruction::I64Extend32S, - I64ExtendI32S => Instruction::I64ExtendI32S, - I64ExtendI32U => Instruction::I64ExtendI32U, - I32Extend8S => Instruction::I32Extend8S, - I32Extend16S => Instruction::I32Extend16S, - I64TruncF32S => Instruction::I64TruncF32S, - I64TruncF32U => Instruction::I64TruncF32U, - I64TruncF64S => Instruction::I64TruncF64S, - I64TruncF64U => Instruction::I64TruncF64U, - F32ConvertI32S => Instruction::F32ConvertI32S, - F32ConvertI32U => Instruction::F32ConvertI32U, - F32ConvertI64S => Instruction::F32ConvertI64S, - F32ConvertI64U => Instruction::F32ConvertI64U, - F32DemoteF64 => Instruction::F32DemoteF64, - F64ConvertI32S => Instruction::F64ConvertI32S, - F64ConvertI32U => Instruction::F64ConvertI32U, - F64ConvertI64S => Instruction::F64ConvertI64S, - F64ConvertI64U => Instruction::F64ConvertI64U, - F64PromoteF32 => Instruction::F64PromoteF32, - I32ReinterpretF32 => Instruction::I32ReinterpretF32, - I64ReinterpretF64 => Instruction::I64ReinterpretF64, - F32ReinterpretI32 => Instruction::F32ReinterpretI32, - F64ReinterpretI64 => Instruction::F64ReinterpretI64, - I32TruncSatF32S => Instruction::I32TruncSatF32S, - I32TruncSatF32U => Instruction::I32TruncSatF32U, - I32TruncSatF64S => Instruction::I32TruncSatF64S, - I32TruncSatF64U => Instruction::I32TruncSatF64U, - I64TruncSatF32S => Instruction::I64TruncSatF32S, - I64TruncSatF32U => Instruction::I64TruncSatF32U, - I64TruncSatF64S => Instruction::I64TruncSatF64S, - I64TruncSatF64U => Instruction::I64TruncSatF64U, - TableGet { table } => Instruction::TableGet(table), - TableSet { table } => Instruction::TableSet(table), - TableInit { table, elem_index } => Instruction::TableInit(table, elem_index), - TableCopy { src_table, dst_table } => Instruction::TableCopy { from: src_table, to: dst_table }, - TableGrow { table } => Instruction::TableGrow(table), - TableSize { table } => Instruction::TableSize(table), - TableFill { table } => Instruction::TableFill(table), - op => { - cold(); - log::error!("Unsupported instruction: {:?}", op); - return Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", op))); - } - }; - instructions.push(res); - } - - if !labels_ptrs.is_empty() { - panic!("last_label_pointer should be None after processing all instructions: {:?}", labels_ptrs); - } - - validator.finish(offset)?; - Ok(instructions.into_boxed_slice()) -} - -#[cold] -fn cold() {} diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 72b3f81..dd4b931 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -7,6 +7,7 @@ #![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; @@ -28,6 +29,7 @@ mod log { mod conversion; mod error; mod module; +mod visit; use alloc::{string::ToString, vec::Vec}; pub use error::*; use module::ModuleReader; @@ -155,11 +157,11 @@ impl TryFrom for TinyWasmModule { .code .into_iter() .zip(code_type_addrs) - .map(|(f, ty_idx)| TypedWasmFunction { + .map(|((instructions, locals), ty_idx)| TypedWasmFunction { type_addr: ty_idx, wasm_function: WasmFunction { - instructions: f.body, - locals: f.locals, + instructions, + locals, ty: reader.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(), }, }) diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index a18d343..813a65d 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -1,16 +1,9 @@ use crate::log::debug; use crate::{conversion, ParseError, Result}; use alloc::{boxed::Box, format, vec::Vec}; -use core::fmt::Debug; use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, ValType}; use wasmparser::{Payload, Validator}; -#[derive(Debug, Clone)] -pub(crate) struct CodeSection { - pub(crate) locals: Box<[ValType]>, - pub(crate) body: Box<[Instruction]>, -} - #[derive(Default)] pub(crate) struct ModuleReader { pub(crate) version: Option, @@ -18,7 +11,7 @@ pub(crate) struct ModuleReader { pub(crate) func_types: Vec, pub(crate) code_type_addrs: Vec, pub(crate) exports: Vec, - pub(crate) code: Vec, + pub(crate) code: Vec<(Box<[Instruction]>, Box<[ValType]>)>, pub(crate) globals: Vec, pub(crate) table_types: Vec, pub(crate) memory_types: Vec, @@ -66,15 +59,7 @@ impl ModuleReader { .map(|t| conversion::convert_module_type(t?)) .collect::>>()?; } - FunctionSection(reader) => { - if !self.code_type_addrs.is_empty() { - return Err(ParseError::DuplicateSection("Function section".into())); - } - debug!("Found function section"); - validator.function_section(&reader)?; - self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::>>()?; - } GlobalSection(reader) => { if !self.globals.is_empty() { return Err(ParseError::DuplicateSection("Global section".into())); @@ -122,11 +107,21 @@ impl ModuleReader { } validator.data_count_section(count, &range)?; } + FunctionSection(reader) => { + if !self.code_type_addrs.is_empty() { + return Err(ParseError::DuplicateSection("Function section".into())); + } + + debug!("Found function section"); + validator.function_section(&reader)?; + self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::>>()?; + } CodeSectionStart { count, range, .. } => { debug!("Found code section ({} functions)", count); if !self.code.is_empty() { return Err(ParseError::DuplicateSection("Code section".into())); } + self.code.reserve(count as usize); validator.code_section_start(count, &range)?; } CodeSectionEntry(function) => { diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs new file mode 100644 index 0000000..b42e462 --- /dev/null +++ b/crates/parser/src/visit.rs @@ -0,0 +1,487 @@ +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 wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources}; + +struct ValidateThenVisit<'a, T, U>(T, &'a mut U); +macro_rules! validate_then_visit { + ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $( + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { + self.0.$visit($($($arg.clone()),*)?)?; + Ok(self.1.$visit($($($arg),*)?)) + } + )* + }; +} + +impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U> +where + T: VisitOperator<'a, Output = wasmparser::Result<()>>, + U: VisitOperator<'a>, +{ + type Output = Result; + + wasmparser::for_each_operator!(validate_then_visit); +} + +pub(crate) fn process_operators( + validator: &mut FuncValidator, + body: &FunctionBody<'_>, +) -> Result> { + let mut reader = body.get_operators_reader()?; + let mut builder = FunctionBuilder::new(1024); + + while !reader.eof() { + let validate = validator.visitor(reader.original_position()); + reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))???; + } + + validator.finish(reader.original_position())?; + Ok(builder.instructions.into_boxed_slice()) +} + +macro_rules! define_operands { + ($($name:ident, $instr:expr),*) => { + $( + fn $name(&mut self) -> Self::Output { + self.instructions.push($instr); + Ok(()) + } + )* + }; +} + +macro_rules! define_primitive_operands { + ($($name:ident, $instr:expr, $ty:ty),*) => { + $( + fn $name(&mut self, arg: $ty) -> Self::Output { + self.instructions.push($instr(arg)); + Ok(()) + } + )* + }; +} + +macro_rules! define_mem_operands { + ($($name:ident, $instr:expr),*) => { + $( + fn $name(&mut self, mem_arg: wasmparser::MemArg) -> Self::Output { + self.instructions.push($instr( + convert_memarg(mem_arg) + )); + 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, + label_ptrs: Vec, +} + +impl FunctionBuilder { + pub(crate) fn new(instr_capacity: usize) -> Self { + Self { instructions: Vec::with_capacity(instr_capacity), label_ptrs: Vec::with_capacity(64) } + } + + #[cold] + fn unsupported(&self, name: &str) -> Result<()> { + Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", name))) + } + + #[inline] + fn visit(&mut self, op: Instruction) -> Result<()> { + Ok(self.instructions.push(op)) + } +} + +impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { + type Output = Result<()>; + + 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, + visit_i64_const, Instruction::I64Const, i64 + } + + 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 + } + + define_operands! { + visit_unreachable, Instruction::Unreachable, + visit_nop, Instruction::Nop, + visit_return, Instruction::Return, + visit_drop, Instruction::Drop, + visit_select, Instruction::Select(None), + visit_i32_eqz, Instruction::I32Eqz, + visit_i32_eq, Instruction::I32Eq, + visit_i32_ne, Instruction::I32Ne, + visit_i32_lt_s, Instruction::I32LtS, + visit_i32_lt_u, Instruction::I32LtU, + visit_i32_gt_s, Instruction::I32GtS, + visit_i32_gt_u, Instruction::I32GtU, + visit_i32_le_s, Instruction::I32LeS, + visit_i32_le_u, Instruction::I32LeU, + visit_i32_ge_s, Instruction::I32GeS, + visit_i32_ge_u, Instruction::I32GeU, + visit_i64_eqz, Instruction::I64Eqz, + visit_i64_eq, Instruction::I64Eq, + visit_i64_ne, Instruction::I64Ne, + visit_i64_lt_s, Instruction::I64LtS, + visit_i64_lt_u, Instruction::I64LtU, + visit_i64_gt_s, Instruction::I64GtS, + visit_i64_gt_u, Instruction::I64GtU, + visit_i64_le_s, Instruction::I64LeS, + visit_i64_le_u, Instruction::I64LeU, + visit_i64_ge_s, Instruction::I64GeS, + visit_i64_ge_u, Instruction::I64GeU, + visit_f32_eq, Instruction::F32Eq, + visit_f32_ne, Instruction::F32Ne, + visit_f32_lt, Instruction::F32Lt, + visit_f32_gt, Instruction::F32Gt, + visit_f32_le, Instruction::F32Le, + visit_f32_ge, Instruction::F32Ge, + visit_f64_eq, Instruction::F64Eq, + visit_f64_ne, Instruction::F64Ne, + visit_f64_lt, Instruction::F64Lt, + visit_f64_gt, Instruction::F64Gt, + visit_f64_le, Instruction::F64Le, + visit_f64_ge, Instruction::F64Ge, + visit_i32_clz, Instruction::I32Clz, + visit_i32_ctz, Instruction::I32Ctz, + visit_i32_popcnt, Instruction::I32Popcnt, + visit_i32_add, Instruction::I32Add, + visit_i32_sub, Instruction::I32Sub, + visit_i32_mul, Instruction::I32Mul, + visit_i32_div_s, Instruction::I32DivS, + visit_i32_div_u, Instruction::I32DivU, + visit_i32_rem_s, Instruction::I32RemS, + visit_i32_rem_u, Instruction::I32RemU, + visit_i32_and, Instruction::I32And, + visit_i32_or, Instruction::I32Or, + visit_i32_xor, Instruction::I32Xor, + visit_i32_shl, Instruction::I32Shl, + visit_i32_shr_s, Instruction::I32ShrS, + visit_i32_shr_u, Instruction::I32ShrU, + visit_i32_rotl, Instruction::I32Rotl, + visit_i32_rotr, Instruction::I32Rotr, + visit_i64_clz, Instruction::I64Clz, + visit_i64_ctz, Instruction::I64Ctz, + visit_i64_popcnt, Instruction::I64Popcnt, + visit_i64_add, Instruction::I64Add, + visit_i64_sub, Instruction::I64Sub, + visit_i64_mul, Instruction::I64Mul, + visit_i64_div_s, Instruction::I64DivS, + visit_i64_div_u, Instruction::I64DivU, + visit_i64_rem_s, Instruction::I64RemS, + visit_i64_rem_u, Instruction::I64RemU, + visit_i64_and, Instruction::I64And, + visit_i64_or, Instruction::I64Or, + visit_i64_xor, Instruction::I64Xor, + visit_i64_shl, Instruction::I64Shl, + visit_i64_shr_s, Instruction::I64ShrS, + visit_i64_shr_u, Instruction::I64ShrU, + visit_i64_rotl, Instruction::I64Rotl, + visit_i64_rotr, Instruction::I64Rotr, + visit_f32_abs, Instruction::F32Abs, + visit_f32_neg, Instruction::F32Neg, + visit_f32_ceil, Instruction::F32Ceil, + visit_f32_floor, Instruction::F32Floor, + visit_f32_trunc, Instruction::F32Trunc, + visit_f32_nearest, Instruction::F32Nearest, + visit_f32_sqrt, Instruction::F32Sqrt, + visit_f32_add, Instruction::F32Add, + visit_f32_sub, Instruction::F32Sub, + visit_f32_mul, Instruction::F32Mul, + visit_f32_div, Instruction::F32Div, + visit_f32_min, Instruction::F32Min, + visit_f32_max, Instruction::F32Max, + visit_f32_copysign, Instruction::F32Copysign, + visit_f64_abs, Instruction::F64Abs, + visit_f64_neg, Instruction::F64Neg, + visit_f64_ceil, Instruction::F64Ceil, + visit_f64_floor, Instruction::F64Floor, + visit_f64_trunc, Instruction::F64Trunc, + visit_f64_nearest, Instruction::F64Nearest, + visit_f64_sqrt, Instruction::F64Sqrt, + visit_f64_add, Instruction::F64Add, + visit_f64_sub, Instruction::F64Sub, + visit_f64_mul, Instruction::F64Mul, + visit_f64_div, Instruction::F64Div, + visit_f64_min, Instruction::F64Min, + visit_f64_max, Instruction::F64Max, + visit_f64_copysign, Instruction::F64Copysign, + visit_i32_wrap_i64, Instruction::I32WrapI64, + visit_i32_trunc_f32_s, Instruction::I32TruncF32S, + visit_i32_trunc_f32_u, Instruction::I32TruncF32U, + visit_i32_trunc_f64_s, Instruction::I32TruncF64S, + visit_i32_trunc_f64_u, Instruction::I32TruncF64U, + visit_i64_extend_i32_s, Instruction::I64ExtendI32S, + visit_i64_extend_i32_u, Instruction::I64ExtendI32U, + visit_i64_trunc_f32_s, Instruction::I64TruncF32S, + visit_i64_trunc_f32_u, Instruction::I64TruncF32U, + visit_i64_trunc_f64_s, Instruction::I64TruncF64S, + visit_i64_trunc_f64_u, Instruction::I64TruncF64U, + visit_f32_convert_i32_s, Instruction::F32ConvertI32S, + visit_f32_convert_i32_u, Instruction::F32ConvertI32U, + visit_f32_convert_i64_s, Instruction::F32ConvertI64S, + visit_f32_convert_i64_u, Instruction::F32ConvertI64U, + visit_f32_demote_f64, Instruction::F32DemoteF64, + visit_f64_convert_i32_s, Instruction::F64ConvertI32S, + visit_f64_convert_i32_u, Instruction::F64ConvertI32U, + visit_f64_convert_i64_s, Instruction::F64ConvertI64S, + visit_f64_convert_i64_u, Instruction::F64ConvertI64U, + visit_f64_promote_f32, Instruction::F64PromoteF32, + visit_i32_reinterpret_f32, Instruction::I32ReinterpretF32, + visit_i64_reinterpret_f64, Instruction::I64ReinterpretF64, + visit_f32_reinterpret_i32, Instruction::F32ReinterpretI32, + visit_f64_reinterpret_i64, Instruction::F64ReinterpretI64, + + // sign_extension + visit_i32_extend8_s, Instruction::I32Extend8S, + visit_i32_extend16_s, Instruction::I32Extend16S, + visit_i64_extend8_s, Instruction::I64Extend8S, + visit_i64_extend16_s, Instruction::I64Extend16S, + visit_i64_extend32_s, Instruction::I64Extend32S, + + // Non-trapping Float-to-int Conversions + visit_i32_trunc_sat_f32_s, Instruction::I32TruncSatF32S, + visit_i32_trunc_sat_f32_u, Instruction::I32TruncSatF32U, + visit_i32_trunc_sat_f64_s, Instruction::I32TruncSatF64S, + visit_i32_trunc_sat_f64_u, Instruction::I32TruncSatF64U, + visit_i64_trunc_sat_f32_s, Instruction::I64TruncSatF32S, + visit_i64_trunc_sat_f32_u, Instruction::I64TruncSatF32U, + visit_i64_trunc_sat_f64_s, Instruction::I64TruncSatF64S, + visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U + } + + 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)) + } + + fn visit_loop(&mut self, ty: wasmparser::BlockType) -> Self::Output { + self.label_ptrs.push(self.instructions.len()); + self.visit(Instruction::Loop(convert_blocktype(ty), 0)) + } + + 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)) + } + + fn visit_else(&mut self) -> Self::Output { + self.label_ptrs.push(self.instructions.len()); + self.visit(Instruction::Else(0)) + } + + fn visit_end(&mut self) -> Self::Output { + let Some(label_pointer) = self.label_ptrs.pop() else { + return self.visit(Instruction::EndFunc); + }; + + let current_instr_ptr = self.instructions.len(); + + match self.instructions[label_pointer] { + Instruction::Else(ref mut else_instr_end_offset) => { + *else_instr_end_offset = current_instr_ptr - label_pointer; + + // since we're ending an else block, we need to end the if block as well + let if_label_pointer = self.label_ptrs.pop().ok_or(crate::ParseError::UnsupportedOperator( + "Expected to end an if block, but the last label was not an if".to_string(), + ))?; + + let if_instruction = &mut self.instructions[if_label_pointer]; + let Instruction::If(_, ref mut else_offset, ref mut end_offset) = if_instruction else { + return Err(crate::ParseError::UnsupportedOperator( + "Expected to end an if block, but the last label was not an if".to_string(), + )); + }; + + *else_offset = Some(label_pointer - if_label_pointer); + *end_offset = current_instr_ptr - if_label_pointer; + } + 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; + } + _ => { + return Err(crate::ParseError::UnsupportedOperator( + "Expected to end a block, but the last label was not a block".to_string(), + )) + } + }; + + self.visit(Instruction::EndBlockFrame) + } + + fn visit_br_table(&mut self, targets: wasmparser::BrTable<'_>) -> Self::Output { + let def = targets.default(); + let instrs = targets + .targets() + .map(|t| t.map(Instruction::BrLabel)) + .collect::, 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.into_iter())); + Ok(()) + } + + fn visit_call(&mut self, idx: u32) -> Self::Output { + self.visit(Instruction::Call(idx)) + } + + fn visit_call_indirect(&mut self, ty: u32, table: u32, _table_byte: u8) -> Self::Output { + self.visit(Instruction::CallIndirect(ty, table)) + } + + fn visit_memory_size(&mut self, mem: u32, mem_byte: u8) -> Self::Output { + self.visit(Instruction::MemorySize(mem, mem_byte)) + } + + fn visit_memory_grow(&mut self, mem: u32, mem_byte: u8) -> Self::Output { + self.visit(Instruction::MemoryGrow(mem, mem_byte)) + } + + fn visit_f32_const(&mut self, val: wasmparser::Ieee32) -> Self::Output { + self.visit(Instruction::F32Const(f32::from_bits(val.bits()))) + } + + fn visit_f64_const(&mut self, val: wasmparser::Ieee64) -> Self::Output { + self.visit(Instruction::F64Const(f64::from_bits(val.bits()))) + } + + // 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)) + } + + fn visit_table_init(&mut self, elem_index: u32, table: u32) -> Self::Output { + self.visit(Instruction::TableInit(elem_index, table)) + } + + fn visit_elem_drop(&mut self, _elem_index: u32) -> Self::Output { + self.unsupported("elem_drop") + } + + fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output { + self.visit(Instruction::TableCopy { from: src_table, to: dst_table }) + } + + // Reference Types + + fn visit_ref_null(&mut self, ty: wasmparser::HeapType) -> Self::Output { + self.visit(Instruction::RefNull(convert_heaptype(ty))) + } + + fn visit_ref_is_null(&mut self) -> Self::Output { + 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)) + } + + wasmparser::for_each_operator!(impl_visit_operator); +} diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index b5a68a4..fc12b54 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -13,10 +13,10 @@ pub enum BlockArgs { #[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, - pub offset: u64, } type BrTableDefault = u32; From 43e6d23ae8806c813dd5fa0663c17c1772ebf5a8 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Sun, 25 Feb 2024 19:36:13 +0100 Subject: [PATCH 13/24] chore: improve new parser arch Signed-off-by: Henry Gressmann --- Cargo.lock | 4 +-- crates/parser/src/conversion.rs | 6 ++--- crates/parser/src/lib.rs | 2 +- crates/parser/src/module.rs | 4 ++- crates/parser/src/visit.rs | 45 ++++++++++++++++++++------------- 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4fddf4d..a6fdbbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,9 +293,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" [[package]] name = "cfg-if" diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 03b5f82..53cceb6 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -1,5 +1,5 @@ -use crate::visit::process_operators; use crate::Result; +use crate::{module::Code, visit::process_operators}; use alloc::{boxed::Box, format, string::ToString, vec::Vec}; use tinywasm_types::*; use wasmparser::{FuncValidator, OperatorsReader, ValidatorResources}; @@ -159,7 +159,7 @@ pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result, mut validator: FuncValidator, -) -> Result<(Box<[Instruction]>, Box<[ValType]>)> { +) -> Result { let locals_reader = func.get_locals_reader()?; let count = locals_reader.get_count(); let pos = locals_reader.original_position(); @@ -173,7 +173,7 @@ pub(crate) fn convert_module_code( } } - let body = process_operators(&mut validator, &func)?; + let body = process_operators(Some(&mut validator), &func)?; let locals = locals.into_boxed_slice(); Ok((body, locals)) } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index dd4b931..5de4b03 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -52,13 +52,13 @@ impl Parser { let features = WasmFeatures { bulk_memory: true, floats: true, - function_references: true, multi_value: true, mutable_global: true, reference_types: true, sign_extension: true, saturating_float_to_int: true, + function_references: false, component_model: false, component_model_nested_names: false, component_model_values: false, diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 813a65d..8414c17 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -4,6 +4,8 @@ use alloc::{boxed::Box, format, vec::Vec}; use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, ValType}; use wasmparser::{Payload, Validator}; +pub(crate) type Code = (Box<[Instruction]>, Box<[ValType]>); + #[derive(Default)] pub(crate) struct ModuleReader { pub(crate) version: Option, @@ -11,7 +13,7 @@ pub(crate) struct ModuleReader { pub(crate) func_types: Vec, pub(crate) code_type_addrs: Vec, pub(crate) exports: Vec, - pub(crate) code: Vec<(Box<[Instruction]>, Box<[ValType]>)>, + pub(crate) code: Vec, pub(crate) globals: Vec, pub(crate) table_types: Vec, pub(crate) memory_types: Vec, diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index b42e462..15024f1 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -10,6 +10,7 @@ struct ValidateThenVisit<'a, T, U>(T, &'a mut U); macro_rules! validate_then_visit { ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( + #[inline] fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { self.0.$visit($($($arg.clone()),*)?)?; Ok(self.1.$visit($($($arg),*)?)) @@ -24,23 +25,29 @@ where U: VisitOperator<'a>, { type Output = Result; - wasmparser::for_each_operator!(validate_then_visit); } pub(crate) fn process_operators( - validator: &mut FuncValidator, + validator: Option<&mut FuncValidator>, body: &FunctionBody<'_>, ) -> Result> { let mut reader = body.get_operators_reader()?; - let mut builder = FunctionBuilder::new(1024); + let remaining = reader.get_binary_reader().bytes_remaining(); + let mut builder = FunctionBuilder::new(remaining); - while !reader.eof() { - let validate = validator.visitor(reader.original_position()); - reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))???; + if let Some(validator) = validator { + while !reader.eof() { + let validate = validator.visitor(reader.original_position()); + reader.visit_operator(&mut ValidateThenVisit(validate, &mut builder))???; + } + validator.finish(reader.original_position())?; + } else { + while !reader.eof() { + reader.visit_operator(&mut builder)??; + } } - validator.finish(reader.original_position())?; Ok(builder.instructions.into_boxed_slice()) } @@ -114,7 +121,7 @@ pub(crate) struct FunctionBuilder { impl FunctionBuilder { pub(crate) fn new(instr_capacity: usize) -> Self { - Self { instructions: Vec::with_capacity(instr_capacity), label_ptrs: Vec::with_capacity(64) } + Self { instructions: Vec::with_capacity(instr_capacity), label_ptrs: Vec::with_capacity(128) } } #[cold] @@ -124,7 +131,8 @@ impl FunctionBuilder { #[inline] fn visit(&mut self, op: Instruction) -> Result<()> { - Ok(self.instructions.push(op)) + self.instructions.push(op); + Ok(()) } } @@ -337,6 +345,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { self.visit(Instruction::Else(0)) } + #[inline] fn visit_end(&mut self) -> Self::Output { let Some(label_pointer) = self.label_ptrs.pop() else { return self.visit(Instruction::EndFunc); @@ -348,16 +357,19 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { Instruction::Else(ref mut else_instr_end_offset) => { *else_instr_end_offset = current_instr_ptr - label_pointer; + #[cold] + fn error() -> crate::ParseError { + crate::ParseError::UnsupportedOperator( + "Expected to end an if block, but the last label was not an if".to_string(), + ) + } + // since we're ending an else block, we need to end the if block as well - let if_label_pointer = self.label_ptrs.pop().ok_or(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - ))?; + let if_label_pointer = self.label_ptrs.pop().ok_or_else(error)?; let if_instruction = &mut self.instructions[if_label_pointer]; let Instruction::If(_, ref mut else_offset, ref mut end_offset) = if_instruction else { - return Err(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - )); + return Err(error()); }; *else_offset = Some(label_pointer - if_label_pointer); @@ -386,8 +398,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { .collect::, 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.into_iter())); + self.instructions.extend(IntoIterator::into_iter([Instruction::BrTable(def, instrs.len())]).chain(instrs)); Ok(()) } From 0fa1bca956229fa49509524aa7edd087439ea39a Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Mon, 26 Feb 2024 15:30:42 +0100 Subject: [PATCH 14/24] chore: preperations for simd support Signed-off-by: Henry Gressmann --- .../src/runtime/interpreter/macros.rs | 4 +- crates/tinywasm/src/runtime/value.rs | 69 +++++++++++-------- crates/tinywasm/tests/generated/2.0.csv | 2 +- crates/types/src/value.rs | 7 ++ 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index 824666d..18330a4 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -32,7 +32,7 @@ macro_rules! mem_load { let mem = $store.get_mem(mem_idx as usize)?; let mem_ref = mem.borrow_mut(); - let addr = $stack.values.pop()?.raw_value(); + let addr: u64 = $stack.values.pop()?.into(); let addr = $arg.offset.checked_add(addr).ok_or_else(|| { cold(); Error::Trap(crate::Trap::MemoryOutOfBounds { @@ -71,7 +71,7 @@ macro_rules! mem_store { 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 addr: u64 = $stack.values.pop()?.into(); let val = val as $store_type; let val = val.to_le_bytes(); diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs index bc78adb..4e1b746 100644 --- a/crates/tinywasm/src/runtime/value.rs +++ b/crates/tinywasm/src/runtime/value.rs @@ -8,39 +8,42 @@ 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]); 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; 16] { 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 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), } } } @@ -69,7 +73,7 @@ macro_rules! impl_from_raw_wasm_value { #[inline] fn from(value: $type) -> Self { #[allow(clippy::redundant_closure_call)] // the comiler will figure it out :) - Self($to_raw(value)) + Self(u128::to_ne_bytes($to_raw(value))) } } @@ -84,13 +88,22 @@ macro_rules! impl_from_raw_wasm_value { }; } -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); +// This all looks like a lot of extra steps, but the compiler will optimize it all away. +// The u128 just makes it a bit easier to write. +impl_from_raw_wasm_value!(i32, |x| x as u128, |x: [u8; 16]| i32::from_ne_bytes(x[0..4].try_into().unwrap())); +impl_from_raw_wasm_value!(i64, |x| x as u128, |x: [u8; 16]| i64::from_ne_bytes(x[0..8].try_into().unwrap())); +impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as u128, |x: [u8; 16]| 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 u128, |x: [u8; 16]| f64::from_bits(u64::from_ne_bytes( + x[0..8].try_into().unwrap() +))); -// 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); +impl_from_raw_wasm_value!(u8, |x| x as u128, |x: [u8; 16]| u8::from_ne_bytes(x[0..1].try_into().unwrap())); +impl_from_raw_wasm_value!(u16, |x| x as u128, |x: [u8; 16]| u16::from_ne_bytes(x[0..2].try_into().unwrap())); +impl_from_raw_wasm_value!(u32, |x| x as u128, |x: [u8; 16]| u32::from_ne_bytes(x[0..4].try_into().unwrap())); +impl_from_raw_wasm_value!(u64, |x| x as u128, |x: [u8; 16]| u64::from_ne_bytes(x[0..8].try_into().unwrap())); +impl_from_raw_wasm_value!(u128, |x| x, |x: [u8; 16]| u128::from_ne_bytes(x.try_into().unwrap())); + +impl_from_raw_wasm_value!(i8, |x| x as u128, |x: [u8; 16]| i8::from_ne_bytes(x[0..1].try_into().unwrap())); +impl_from_raw_wasm_value!(i16, |x| x as u128, |x: [u8; 16]| i16::from_ne_bytes(x[0..2].try_into().unwrap())); 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/value.rs b/crates/types/src/value.rs index 24fed3b..3c02e55 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -16,6 +16,8 @@ pub enum WasmValue { F32(f32), /// A 64-bit float. F64(f64), + /// A half of a 128-bit vector. Allways used in pairs. + V128(u128), RefExtern(ExternAddr), RefFunc(FuncAddr), @@ -47,6 +49,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 +92,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 +109,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 +129,8 @@ pub enum ValType { F32, /// A 64-bit float. F64, + /// A half of a 128-bit vector. Allways used in pairs. + V128, /// A reference to a function. RefFunc, /// A reference to an external value. From 5e573f0765570e0aaf6c9415b652b25d5bddebd9 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 00:17:09 +0100 Subject: [PATCH 15/24] no more simd Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/fibonacci.rs | 6 +- crates/parser/src/visit.rs | 69 +++++++++- .../src/runtime/interpreter/macros.rs | 2 +- .../tinywasm/src/runtime/interpreter/mod.rs | 129 +++++++++++------- .../tinywasm/src/runtime/stack/call_stack.rs | 12 +- .../tinywasm/src/runtime/stack/value_stack.rs | 10 +- crates/tinywasm/src/runtime/value.rs | 42 +++--- crates/types/src/instructions.rs | 26 ++++ crates/types/src/value.rs | 15 +- examples/rust/analyze.py | 36 +++++ 10 files changed, 250 insertions(+), 97 deletions(-) create mode 100644 examples/rust/analyze.py 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::(&store, name).expect("get_function"); fib.call(&mut store, iterations).expect("call"); diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 15024f1..9038567 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -142,9 +142,6 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { 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, @@ -220,7 +217,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 +248,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 +322,68 @@ 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::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, 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 { + // LocalGetSet + if let Some(instruction) = self.instructions.last_mut() { + match instruction { + // 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 { + 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)) diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index 18330a4..1da8758 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -11,7 +11,7 @@ // from a function, so we need to check if the label stack is empty 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 $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 { diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index b06152d..57e5f34 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -89,7 +89,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // unreasonable complexity // 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(); @@ -113,7 +113,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M Call(v) => { // prepare the call frame - let func_idx = module.resolve_func_addr(*v); + let func_idx = module.resolve_func_addr(v); let func_inst = store.get_func(func_idx as usize)?.clone(); let wasm_func = match &func_inst.func { @@ -140,7 +140,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } CallIndirect(type_addr, table_addr) => { - let table = store.get_table(module.resolve_table_addr(*table_addr) as usize)?; + let table = store.get_table(module.resolve_table_addr(table_addr) as usize)?; let table_idx = stack.values.pop_t::()?; // verify that the table is of the right type, this should be validated by the parser already @@ -155,7 +155,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }; let func_inst = store.get_func(func_ref as usize)?.clone(); - let call_ty = module.func_ty(*type_addr); + let call_ty = module.func_ty(type_addr); let wasm_func = match func_inst.func { crate::Function::Wasm(ref f) => f.clone(), @@ -202,10 +202,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, stack.values.len(), BlockType::If, - args, + &args, module, ), &mut stack.values, @@ -217,17 +217,17 @@ 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 { let label = BlockFrame::new( - cf.instr_ptr + *else_offset, - cf.instr_ptr + *end_offset, + cf.instr_ptr + else_offset, + cf.instr_ptr + end_offset, stack.values.len(), BlockType::Else, - args, + &args, module, ); - cf.instr_ptr += *else_offset; + cf.instr_ptr += else_offset; cf.enter_block(label, &mut stack.values, &mut stack.blocks); } else { - cf.instr_ptr += *end_offset; + cf.instr_ptr += end_offset; } } @@ -235,10 +235,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, stack.values.len(), BlockType::Loop, - args, + &args, module, ), &mut stack.values, @@ -250,10 +250,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, stack.values.len(), // - params, BlockType::Block, - args, + &args, module, ), &mut stack.values, @@ -262,7 +262,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] .iter() .map(|i| match i { BrLabel(l) => Ok(*l), @@ -273,7 +273,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }) .collect::>>()?; - if unlikely(instr.len() != *len) { + if unlikely(instr.len() != len) { panic!( "Expected {} BrLabel instructions, got {}, this should have been validated by the parser", len, @@ -282,7 +282,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } let idx = stack.values.pop_t::()? as usize; - let to = instr.get(idx).unwrap_or(default); + let to = *instr.get(idx).unwrap_or(&default); break_to!(cf, stack, to); } @@ -319,7 +319,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; } EndBlockFrame => { @@ -332,55 +332,53 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M stack.values.truncate_keep(block.stack_ptr, block.results); } - 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()?), + 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) + cf.set_local( + local_index as usize, + stack + .values + .last() + .expect("localtee: stack is empty. this should have been validated by the parser") + .clone(), + ); } GlobalGet(global_index) => { - let idx = module.resolve_global_addr(*global_index); + let idx = module.resolve_global_addr(global_index); let global = store.get_global_val(idx as usize)?; stack.values.push(global); } GlobalSet(global_index) => { - let idx = module.resolve_global_addr(*global_index); + let idx = module.resolve_global_addr(global_index); store.set_global_val(idx as usize, stack.values.pop()?)?; } - I32Const(val) => stack.values.push((*val).into()), - I64Const(val) => stack.values.push((*val).into()), - F32Const(val) => stack.values.push((*val).into()), - F64Const(val) => stack.values.push((*val).into()), + I32Const(val) => stack.values.push((val).into()), + I64Const(val) => stack.values.push((val).into()), + F32Const(val) => stack.values.push((val).into()), + F64Const(val) => stack.values.push((val).into()), MemorySize(addr, byte) => { - if *byte != 0 { + if byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(*addr); + let mem_idx = module.resolve_mem_addr(addr); let mem = store.get_mem(mem_idx as usize)?; stack.values.push((mem.borrow().page_count() as i32).into()); } MemoryGrow(addr, byte) => { - if *byte != 0 { + if byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(*addr); + let mem_idx = module.resolve_mem_addr(addr); let mem = store.get_mem(mem_idx as usize)?; let (res, prev_size) = { @@ -401,7 +399,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let src = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - let mem = store.get_mem(module.resolve_mem_addr(*from) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(from) as usize)?; let mut mem = mem.borrow_mut(); if from == to { @@ -409,7 +407,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M mem.copy_within(dst as usize, src as usize, size as usize)?; } else { // copy between two memories - let mem2 = store.get_mem(module.resolve_mem_addr(*to) as usize)?; + 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)?)?; } @@ -420,7 +418,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let val = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - let mem = store.get_mem(module.resolve_mem_addr(*addr) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(addr) as usize)?; let mut mem = mem.borrow_mut(); mem.fill(dst as usize, size as usize, val as u8)?; } @@ -430,13 +428,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let offset = stack.values.pop_t::()? as usize; let dst = stack.values.pop_t::()? as usize; - let data_idx = module.resolve_data_addr(*data_index); + 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 mem_idx = module.resolve_mem_addr(*mem_index); + let mem_idx = module.resolve_mem_addr(mem_index); let mem = store.get_mem(mem_idx as usize)?; let data_len = data.len(); @@ -453,7 +451,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } DataDrop(data_index) => { - let data_idx = module.resolve_data_addr(*data_index); + let data_idx = module.resolve_data_addr(data_index); let data = store.get_data_mut(data_idx as usize)?; data.drop(); } @@ -632,7 +630,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M I64TruncF64U => checked_conv_float!(f64, u64, i64, stack), TableGet(table_index) => { - let table_idx = module.resolve_table_addr(*table_index); + let table_idx = module.resolve_table_addr(table_index); let table = store.get_table(table_idx as usize)?; let idx = stack.values.pop_t::()? as usize; let v = table.borrow().get_wasm_val(idx)?; @@ -640,7 +638,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSet(table_index) => { - let table_idx = module.resolve_table_addr(*table_index); + let table_idx = module.resolve_table_addr(table_index); let table = store.get_table(table_idx as usize)?; let val = stack.values.pop_t::()?; let idx = stack.values.pop_t::()? as usize; @@ -648,16 +646,16 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSize(table_index) => { - let table_idx = module.resolve_table_addr(*table_index); + let table_idx = module.resolve_table_addr(table_index); let table = store.get_table(table_idx as usize)?; stack.values.push(table.borrow().size().into()); } TableInit(table_index, elem_index) => { - let table_idx = module.resolve_table_addr(*table_index); + let table_idx = module.resolve_table_addr(table_index); let table = store.get_table(table_idx as usize)?; - let elem_idx = module.resolve_elem_addr(*elem_index); + let elem_idx = module.resolve_elem_addr(elem_index); let elem = store.get_elem(elem_idx as usize)?; if let ElementKind::Passive = elem.kind { @@ -680,6 +678,33 @@ 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.push(cf.get_local(a as usize)); + stack.values.push(cf.get_local(b as usize)); + } + LocalGet3(a, b, c) => { + stack.values.push(cf.get_local(a as usize)); + stack.values.push(cf.get_local(b as usize)); + stack.values.push(cf.get_local(c as usize)); + } + LocalGet4(a, b, c, d) => { + stack.values.push(cf.get_local(a as usize)); + stack.values.push(cf.get_local(b as usize)); + stack.values.push(cf.get_local(c as usize)); + stack.values.push(cf.get_local(d as usize)); + } + LocalTeeGet(a, b) => { + 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)); + } + + // LocalTeeGet + // LocalGetSet + // I64XorConstRotl + // I32LocalGetConstAdd i => { cold(); log::error!("unimplemented instruction: {:?}", i); diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index 1c441a8..dd80bb7 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] + 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..be5b53b 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -58,14 +58,8 @@ impl ValueStack { } #[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 4e1b746..56fdf60 100644 --- a/crates/tinywasm/src/runtime/value.rs +++ b/crates/tinywasm/src/runtime/value.rs @@ -8,7 +8,8 @@ use tinywasm_types::{ValType, WasmValue}; /// See [`WasmValue`] for the public representation. #[derive(Clone, Copy, Default, PartialEq, Eq)] #[repr(transparent)] -pub struct RawWasmValue([u8; 16]); +// 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 { @@ -18,7 +19,7 @@ impl Debug for RawWasmValue { impl RawWasmValue { #[inline(always)] - pub fn raw_value(&self) -> [u8; 16] { + pub fn raw_value(&self) -> [u8; 8] { self.0 } @@ -29,7 +30,7 @@ impl RawWasmValue { 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::V128 => WasmValue::V128(self.into()), ValType::RefExtern => { let val: i64 = self.into(); if val < 0 { @@ -58,7 +59,7 @@ impl From for RawWasmValue { 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::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), @@ -72,8 +73,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(u128::to_ne_bytes($to_raw(value))) + #[allow(clippy::redundant_closure_call)] + Self(u64::to_ne_bytes($to_raw(value))) } } @@ -81,29 +82,32 @@ macro_rules! impl_from_raw_wasm_value { impl From 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) } } }; } +type RawValue = u64; +type RawValueRep = [u8; 8]; + // This all looks like a lot of extra steps, but the compiler will optimize it all away. -// The u128 just makes it a bit easier to write. -impl_from_raw_wasm_value!(i32, |x| x as u128, |x: [u8; 16]| i32::from_ne_bytes(x[0..4].try_into().unwrap())); -impl_from_raw_wasm_value!(i64, |x| x as u128, |x: [u8; 16]| i64::from_ne_bytes(x[0..8].try_into().unwrap())); -impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as u128, |x: [u8; 16]| f32::from_bits(u32::from_ne_bytes( +// 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 u128, |x: [u8; 16]| f64::from_bits(u64::from_ne_bytes( +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 u128, |x: [u8; 16]| u8::from_ne_bytes(x[0..1].try_into().unwrap())); -impl_from_raw_wasm_value!(u16, |x| x as u128, |x: [u8; 16]| u16::from_ne_bytes(x[0..2].try_into().unwrap())); -impl_from_raw_wasm_value!(u32, |x| x as u128, |x: [u8; 16]| u32::from_ne_bytes(x[0..4].try_into().unwrap())); -impl_from_raw_wasm_value!(u64, |x| x as u128, |x: [u8; 16]| u64::from_ne_bytes(x[0..8].try_into().unwrap())); -impl_from_raw_wasm_value!(u128, |x| x, |x: [u8; 16]| u128::from_ne_bytes(x.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 u128, |x: [u8; 16]| i8::from_ne_bytes(x[0..1].try_into().unwrap())); -impl_from_raw_wasm_value!(i16, |x| x as u128, |x: [u8; 16]| i16::from_ne_bytes(x[0..2].try_into().unwrap())); +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/types/src/instructions.rs b/crates/types/src/instructions.rs index fc12b54..dd941b3 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -54,6 +54,32 @@ pub enum Instruction { // Custom Instructions BrLabel(LabelAddr), + //== Not implemented yet, to be determined + + // LocalGet + I32Const + I32Add + // One of the most common patterns in the Rust compiler output + I32LocalGetConstAdd(LocalAddr, i32), + + // LocalGet + I32Const + I32Store + // Also common, helps us skip the stack entirely + I32LocalGetConstStore(LocalAddr, i32, MemoryArg), // I32Store + LocalGet + I32Const + + // I64Xor + I64Const + I64RotL + // Commonly used by a few crypto libraries + I64XorConstRotl(i64), + + // LocalTee + LocalGet + LocalTeeGet(LocalAddr, LocalAddr), + LocalGet2(LocalAddr, LocalAddr), + LocalGet3(LocalAddr, LocalAddr, LocalAddr), + LocalGet4(LocalAddr, LocalAddr, LocalAddr, LocalAddr), + LocalGetSet(LocalAddr, LocalAddr), + + I32AddConst(i32), + I32SubConst(i32), + I64AddConst(i64), + I64SubConst(i64), + // Control Instructions // See Unreachable, diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 3c02e55..8fd72fb 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -16,9 +16,8 @@ pub enum WasmValue { F32(f32), /// A 64-bit float. F64(f64), - /// A half of a 128-bit vector. Allways used in pairs. - V128(u128), - + // /// A 128-bit vector + // V128(u128), RefExtern(ExternAddr), RefFunc(FuncAddr), RefNull(ValType), @@ -49,7 +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::V128 => Self::V128(0), ValType::RefFunc => Self::RefNull(ValType::RefFunc), ValType::RefExtern => Self::RefNull(ValType::RefExtern), } @@ -92,7 +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::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), @@ -109,7 +108,7 @@ impl WasmValue { Self::I64(_) => ValType::I64, Self::F32(_) => ValType::F32, Self::F64(_) => ValType::F64, - Self::V128(_) => ValType::V128, + // Self::V128(_) => ValType::V128, Self::RefExtern(_) => ValType::RefExtern, Self::RefFunc(_) => ValType::RefFunc, Self::RefNull(ty) => *ty, @@ -129,8 +128,8 @@ pub enum ValType { F32, /// A 64-bit float. F64, - /// A half of a 128-bit vector. Allways used in pairs. - V128, + /// A 128-bit vector + // V128, /// A reference to a function. RefFunc, /// A reference to an external value. 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}") From e883003b7436708e91d0971b1a8e5f74c8166261 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 01:04:34 +0100 Subject: [PATCH 16/24] some new instructuons Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/selfhosted.rs | 6 ++-- crates/parser/src/visit.rs | 35 +++++++++---------- .../tinywasm/src/runtime/interpreter/mod.rs | 33 ++++++++++------- .../tinywasm/src/runtime/stack/value_stack.rs | 9 ++--- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 94dfdce..1396fd1 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -61,10 +61,10 @@ fn criterion_benchmark(c: &mut Criterion) { { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); - group.bench_function("native", |b| b.iter(run_native)); + // group.bench_function("native", |b| b.iter(run_native)); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 9038567..f994e7e 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -325,10 +325,10 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { 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::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), - // Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), + Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), + Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), + Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), + Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), _ => return self.visit(Instruction::LocalGet(idx)), }; Ok(()) @@ -338,16 +338,15 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_local_set(&mut self, idx: u32) -> Self::Output { - // LocalGetSet - if let Some(instruction) = self.instructions.last_mut() { - match instruction { - // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), - _ => return self.visit(Instruction::LocalSet(idx)), - }; - Ok(()) - } else { - self.visit(Instruction::LocalSet(idx)) + if self.instructions.len() < 1 { + return self.visit(Instruction::I64Rotl); } + + // LocalGetSet + match self.instructions[self.instructions.len() - 1..] { + // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), + _ => return self.visit(Instruction::LocalSet(idx)), + }; } fn visit_local_tee(&mut self, idx: u32) -> Self::Output { @@ -360,11 +359,11 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } match self.instructions[self.instructions.len() - 2..] { - // [Instruction::I64Xor, Instruction::I64Const(a)] => { - // self.instructions.pop(); - // self.instructions.pop(); - // self.visit(Instruction::I64XorConstRotl(a)) - // } + [Instruction::I64Xor, Instruction::I64Const(a)] => { + self.instructions.pop(); + self.instructions.pop(); + self.visit(Instruction::I64XorConstRotl(a)) + } _ => self.visit(Instruction::I64Rotl), } } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 57e5f34..91d1859 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -680,19 +680,22 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // custom instructions LocalGet2(a, b) => { - stack.values.push(cf.get_local(a as usize)); - stack.values.push(cf.get_local(b as usize)); + stack.values.extend_from_slice(&[cf.get_local(a as usize), cf.get_local(b as usize)]); } LocalGet3(a, b, c) => { - stack.values.push(cf.get_local(a as usize)); - stack.values.push(cf.get_local(b as usize)); - stack.values.push(cf.get_local(c as usize)); + 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.push(cf.get_local(a as usize)); - stack.values.push(cf.get_local(b as usize)); - stack.values.push(cf.get_local(c as usize)); - stack.values.push(cf.get_local(d as usize)); + 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) => { let last = @@ -701,10 +704,14 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M stack.values.push(cf.get_local(b as usize)); } - // LocalTeeGet - // LocalGetSet - // I64XorConstRotl - // I32LocalGetConstAdd + // I64Xor + I64Const + I64RotL + I64XorConstRotl(rotate_by) => { + let val = stack.values.pop_t::()?; + let mask = stack.values.pop_t::()?; + 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/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index be5b53b..c6d7918 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -25,13 +25,14 @@ impl ValueStack { #[inline] pub(crate) fn extend_from_typed(&mut self, values: &[WasmValue]) { - if values.is_empty() { - return; - } - 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 len(&self) -> usize { self.stack.len() From 83a768d77ac57f5d8e8884173765e0efc80831f4 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 01:50:54 +0100 Subject: [PATCH 17/24] reduce instruction enum size Signed-off-by: Henry Gressmann --- crates/parser/src/conversion.rs | 2 +- crates/parser/src/visit.rs | 29 ++++++------ crates/tinywasm/src/reference.rs | 10 ++-- .../src/runtime/interpreter/macros.rs | 4 +- .../tinywasm/src/runtime/interpreter/mod.rs | 29 +++++++----- crates/tinywasm/src/runtime/value.rs | 1 - crates/tinywasm/src/store/memory.rs | 12 ++--- crates/tinywasm/src/store/mod.rs | 46 +++++++++---------- crates/types/src/instructions.rs | 12 ++--- 9 files changed, 76 insertions(+), 69 deletions(-) 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 { diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index f994e7e..3a10a93 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -338,15 +338,16 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_local_set(&mut self, idx: u32) -> Self::Output { - if self.instructions.len() < 1 { - return self.visit(Instruction::I64Rotl); + 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)) } - - // LocalGetSet - match self.instructions[self.instructions.len() - 1..] { - // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), - _ => return self.visit(Instruction::LocalSet(idx)), - }; } fn visit_local_tee(&mut self, idx: u32) -> Self::Output { @@ -413,7 +414,7 @@ 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 as usize) as u32; #[cold] fn error() -> crate::ParseError { @@ -430,13 +431,13 @@ 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 = Some((label_pointer - if_label_pointer) as u32); + *end_offset = (current_instr_ptr - if_label_pointer) as u32; } 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) as u32; } _ => { return Err(crate::ParseError::UnsupportedOperator( @@ -456,7 +457,9 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { .collect::, 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(()) } 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 1da8758..a13531b 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -52,7 +52,7 @@ macro_rules! mem_load { })?; const LEN: usize = core::mem::size_of::<$load_type>(); - let val = mem_ref.load_as::(addr, $arg.align as usize)?; + let val = mem_ref.load_as::(addr)?; $stack.values.push((val as $target_type).into()); }}; } @@ -76,7 +76,7 @@ macro_rules! mem_store { let val = val as $store_type; let val = val.to_le_bytes(); - mem.borrow_mut().store(($arg.offset + addr) as usize, $arg.align as usize, &val, val.len())?; + mem.borrow_mut().store(($arg.offset + addr) as usize, val.len(), &val)?; }}; } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 91d1859..45e10b7 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -202,7 +202,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::If, &args, @@ -217,17 +217,17 @@ 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 { 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, 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 +235,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,7 +250,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(), // - params, BlockType::Block, &args, @@ -262,7 +262,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } BrTable(default, len) => { - let instr = cf.instructions()[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 +273,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }) .collect::>>()?; - 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 +319,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 => { @@ -409,7 +409,7 @@ 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)?)?; } } @@ -447,7 +447,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let data = &data[offset..(offset + size)]; // mem.store checks bounds - mem.store(dst, 0, data, size)?; + mem.store(dst, size, data)?; } DataDrop(data_index) => { @@ -704,6 +704,11 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M stack.values.push(cf.get_local(b as usize)); } + LocalGetSet(a, b) => { + let a = cf.get_local(a as usize); + cf.set_local(b as usize, a); + } + // I64Xor + I64Const + I64RotL I64XorConstRotl(rotate_by) => { let val = stack.values.pop_t::()?; diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs index 56fdf60..4835eab 100644 --- a/crates/tinywasm/src/runtime/value.rs +++ b/crates/tinywasm/src/runtime/value.rs @@ -7,7 +7,6 @@ use tinywasm_types::{ValType, WasmValue}; /// /// See [`WasmValue`] for the public representation. #[derive(Clone, Copy, Default, PartialEq, Eq)] -#[repr(transparent)] // pub struct RawWasmValue([u8; 16]); pub struct RawWasmValue([u8; 8]); diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index cbaed8d..1c8acce 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -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>(&self, addr: usize, _align: usize) -> Result { + pub(crate) fn load_as>(&self, addr: usize) -> Result { 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..a3d99fe 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -349,36 +349,36 @@ 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 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 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 offset = self.eval_i32_const(&offset)?; - let mem = self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| { + 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)) })?; - // 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))); - } - - // drop the data - None + // 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, data.data.len(), &data.data) + { + return Ok((data_addrs.into_boxed_slice(), Some(trap))); } - tinywasm_types::DataKind::Passive => Some(data.data.to_vec()), - }; + + // drop the data + None + } + 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); diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index dd941b3..a19b4d2 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -15,14 +15,14 @@ pub enum BlockArgs { pub struct MemoryArg { pub offset: u64, pub mem_addr: MemAddr, - pub align: u8, - pub align_max: u8, + // 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))] @@ -62,7 +62,7 @@ pub enum Instruction { // LocalGet + I32Const + I32Store // Also common, helps us skip the stack entirely - I32LocalGetConstStore(LocalAddr, i32, MemoryArg), // I32Store + LocalGet + I32Const + // I32LocalGetConstStore(LocalAddr, i32, MemoryArg), // I32Store + LocalGet + I32Const // I64Xor + I64Const + I64RotL // Commonly used by a few crypto libraries From 4faaf5bb222947a95b854d733a3cbe3bf588b597 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 14:35:40 +0100 Subject: [PATCH 18/24] reduce instruction enum size even more Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/selfhosted.rs | 6 +- crates/parser/src/visit.rs | 79 ++++---- .../src/runtime/interpreter/macros.rs | 27 +-- .../tinywasm/src/runtime/interpreter/mod.rs | 179 +++++++++--------- .../tinywasm/src/runtime/stack/call_stack.rs | 4 +- crates/types/src/archive.rs | 2 - crates/types/src/instructions.rs | 156 +++++++++++---- crates/types/src/value.rs | 23 +++ 8 files changed, 296 insertions(+), 180 deletions(-) diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 1396fd1..94dfdce 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -61,10 +61,10 @@ fn criterion_benchmark(c: &mut Criterion) { { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); - // group.bench_function("native", |b| b.iter(run_native)); + group.bench_function("native", |b| b.iter(run_native)); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); - // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); - // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 3a10a93..edebc97 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); @@ -74,12 +74,14 @@ macro_rules! define_primitive_operands { } 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(()) } )* @@ -149,29 +151,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! { @@ -327,7 +329,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { match instruction { Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), - Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), + // Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), _ => return self.visit(Instruction::LocalGet(idx)), }; @@ -396,7 +398,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 { @@ -414,7 +416,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 as usize) as u32; + *else_instr_end_offset = (current_instr_ptr - label_pointer as usize) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support if blocks that large"); #[cold] fn error() -> crate::ParseError { @@ -431,13 +435,20 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { return Err(error()); }; - *else_offset = Some((label_pointer - if_label_pointer) as u32); - *end_offset = (current_instr_ptr - if_label_pointer) as u32; + *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) as u32; + *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( diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index a13531b..9227ceb 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -11,7 +11,7 @@ // from a function, so we need to check if the label stack is empty 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 $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 { @@ -23,20 +23,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: u64 = $stack.values.pop()?.into(); - let addr = $arg.offset.checked_add(addr).ok_or_else(|| { + 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,7 +47,7 @@ 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(), }) @@ -59,15 +61,14 @@ macro_rules! mem_load { /// Store a value to memory macro_rules! mem_store { - ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ + ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ log::debug!("mem_store!({}, {:?})", stringify!($type), $arg); - 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); + ($store_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 val = $stack.values.pop_t::<$store_type>()?; @@ -76,7 +77,7 @@ macro_rules! mem_store { let val = val as $store_type; let val = val.to_le_bytes(); - mem.borrow_mut().store(($arg.offset + addr) as usize, val.len(), &val)?; + mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?; }}; } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 45e10b7..bd07051 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -113,7 +113,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M Call(v) => { // prepare the call frame - let func_idx = module.resolve_func_addr(v); + let func_idx = module.resolve_func_addr(*v); let func_inst = store.get_func(func_idx as usize)?.clone(); let wasm_func = match &func_inst.func { @@ -140,7 +140,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } CallIndirect(type_addr, table_addr) => { - let table = store.get_table(module.resolve_table_addr(table_addr) as usize)?; + let table = store.get_table(module.resolve_table_addr(*table_addr) as usize)?; let table_idx = stack.values.pop_t::()?; // verify that the table is of the right type, this should be validated by the parser already @@ -155,7 +155,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }; let func_inst = store.get_func(func_ref as usize)?.clone(); - let call_ty = module.func_ty(type_addr); + let call_ty = module.func_ty(*type_addr); let wasm_func = match func_inst.func { crate::Function::Wasm(ref f) => f.clone(), @@ -202,10 +202,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 as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::If, - &args, + &args.unpack(), module, ), &mut stack.values, @@ -215,19 +215,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 as usize, - cf.instr_ptr + end_offset as usize, + 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 as usize; + cf.instr_ptr += *else_offset as usize; cf.enter_block(label, &mut stack.values, &mut stack.blocks); } else { - cf.instr_ptr += end_offset as usize; + cf.instr_ptr += *end_offset as usize; } } @@ -235,7 +235,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 as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::Loop, &args, @@ -250,7 +250,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 as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), // - params, BlockType::Block, &args, @@ -262,7 +262,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } BrTable(default, len) => { - let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + len as usize] + 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 +273,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }) .collect::>>()?; - if unlikely(instr.len() != len as usize) { + if unlikely(instr.len() != *len as usize) { panic!( "Expected {} BrLabel instructions, got {}, this should have been validated by the parser", len, @@ -282,7 +282,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } let idx = stack.values.pop_t::()? as usize; - let to = *instr.get(idx).unwrap_or(&default); + let to = instr.get(idx).unwrap_or(&default); break_to!(cf, stack, to); } @@ -319,7 +319,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 as usize; + cf.instr_ptr += *end_offset as usize; } EndBlockFrame => { @@ -332,11 +332,11 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M stack.values.truncate_keep(block.stack_ptr, block.results); } - 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()?), + 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) => { cf.set_local( - local_index as usize, + *local_index as usize, stack .values .last() @@ -346,39 +346,39 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } GlobalGet(global_index) => { - let idx = module.resolve_global_addr(global_index); + let idx = module.resolve_global_addr(*global_index); let global = store.get_global_val(idx as usize)?; stack.values.push(global); } GlobalSet(global_index) => { - let idx = module.resolve_global_addr(global_index); + let idx = module.resolve_global_addr(*global_index); store.set_global_val(idx as usize, stack.values.pop()?)?; } - I32Const(val) => stack.values.push((val).into()), - I64Const(val) => stack.values.push((val).into()), - F32Const(val) => stack.values.push((val).into()), - F64Const(val) => stack.values.push((val).into()), + I32Const(val) => stack.values.push((*val).into()), + I64Const(val) => stack.values.push((*val).into()), + F32Const(val) => stack.values.push((*val).into()), + F64Const(val) => stack.values.push((*val).into()), MemorySize(addr, byte) => { - if byte != 0 { + if *byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(addr); + let mem_idx = module.resolve_mem_addr(*addr); let mem = store.get_mem(mem_idx as usize)?; stack.values.push((mem.borrow().page_count() as i32).into()); } MemoryGrow(addr, byte) => { - if byte != 0 { + if *byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(addr); + let mem_idx = module.resolve_mem_addr(*addr); let mem = store.get_mem(mem_idx as usize)?; let (res, prev_size) = { @@ -399,7 +399,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let src = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - let mem = store.get_mem(module.resolve_mem_addr(from) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(*from) as usize)?; let mut mem = mem.borrow_mut(); if from == to { @@ -407,7 +407,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M mem.copy_within(dst as usize, src as usize, size as usize)?; } else { // copy between two memories - let mem2 = store.get_mem(module.resolve_mem_addr(to) as usize)?; + 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, size as usize)?)?; } @@ -418,7 +418,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let val = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - let mem = store.get_mem(module.resolve_mem_addr(addr) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(*addr) as usize)?; let mut mem = mem.borrow_mut(); mem.fill(dst as usize, size as usize, val as u8)?; } @@ -428,13 +428,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let offset = stack.values.pop_t::()? as usize; let dst = stack.values.pop_t::()? as usize; - let data_idx = module.resolve_data_addr(data_index); + 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 mem_idx = module.resolve_mem_addr(mem_index); + let mem_idx = module.resolve_mem_addr(*mem_index); let mem = store.get_mem(mem_idx as usize)?; let data_len = data.len(); @@ -451,35 +451,35 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } DataDrop(data_index) => { - let data_idx = module.resolve_data_addr(data_index); + let data_idx = module.resolve_data_addr(*data_index); let data = store.get_data_mut(data_idx as usize)?; 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), - - 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), + 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 { 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), @@ -630,7 +630,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M I64TruncF64U => checked_conv_float!(f64, u64, i64, stack), TableGet(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; let idx = stack.values.pop_t::()? as usize; let v = table.borrow().get_wasm_val(idx)?; @@ -638,7 +638,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSet(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; let val = stack.values.pop_t::()?; let idx = stack.values.pop_t::()? as usize; @@ -646,16 +646,16 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSize(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; stack.values.push(table.borrow().size().into()); } TableInit(table_index, elem_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; - let elem_idx = module.resolve_elem_addr(elem_index); + let elem_idx = module.resolve_elem_addr(*elem_index); let elem = store.get_elem(elem_idx as usize)?; if let ElementKind::Passive = elem.kind { @@ -680,43 +680,46 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // custom instructions LocalGet2(a, b) => { - stack.values.extend_from_slice(&[cf.get_local(a as usize), cf.get_local(b as usize)]); + 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), + 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) => { - 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)); + #[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); + let a = cf.get_local(*a as usize); + cf.set_local(*b as usize, a); } - - // I64Xor + I64Const + I64RotL I64XorConstRotl(rotate_by) => { let val = stack.values.pop_t::()?; let mask = stack.values.pop_t::()?; let res = val ^ mask; - stack.values.push(res.rotate_left(rotate_by as u32).into()); + 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/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index dd80bb7..14ad050 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -149,7 +149,7 @@ impl CallFrame { } #[inline(always)] - pub(crate) fn current_instruction(&self) -> Instruction { - self.func_instance.0.instructions[self.instr_ptr] + pub(crate) fn current_instruction(&self) -> &Instruction { + &self.func_instance.0.instructions[self.instr_ptr] } } 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 a19b4d2..537b8d7 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -9,14 +9,44 @@ 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; @@ -48,21 +78,23 @@ pub enum ConstInstruction { /// This makes it easier to implement the label stack iteratively. /// /// See -#[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, to be determined - + // Not implemented yet // LocalGet + I32Const + I32Add // One of the most common patterns in the Rust compiler output - I32LocalGetConstAdd(LocalAddr, i32), + // I32LocalGetConstAdd(LocalAddr, i32), - // LocalGet + I32Const + I32Store - // Also common, helps us skip the stack entirely - // I32LocalGetConstStore(LocalAddr, i32, MemoryArg), // I32Store + LocalGet + I32Const + // Not implemented yet + // LocalGet + I32Const + I32Store => I32LocalGetConstStore + I32Const + // Also common, helps us skip the stack entirely. + // Has to be followed by an I32Const instruction + // I32LocalGetConstStore { local: LocalAddr, offset: i32, mem_addr: MemAddr }, // I32Store + LocalGet + I32Const // I64Xor + I64Const + I64RotL // Commonly used by a few crypto libraries @@ -72,13 +104,13 @@ pub enum Instruction { LocalTeeGet(LocalAddr, LocalAddr), LocalGet2(LocalAddr, LocalAddr), LocalGet3(LocalAddr, LocalAddr, LocalAddr), - LocalGet4(LocalAddr, LocalAddr, LocalAddr, LocalAddr), LocalGetSet(LocalAddr, LocalAddr), - I32AddConst(i32), - I32SubConst(i32), - I64AddConst(i64), - I64SubConst(i64), + // Not implemented yet + // I32AddConst(i32), + // I32SubConst(i32), + // I64AddConst(i64), + // I64SubConst(i64), // Control Instructions // See @@ -86,7 +118,7 @@ pub enum Instruction { Nop, Block(BlockArgs, EndOffset), Loop(BlockArgs, EndOffset), - If(BlockArgs, Option, EndOffset), + If(BlockArgsPacked, ElseOffset, EndOffset), // If else offset is 0 if there is no else block Else(EndOffset), EndBlockFrame, EndFunc, @@ -111,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), @@ -302,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 8fd72fb..df062ce 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -141,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 { + 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 { From d2db1f7b3803c42619d91b85a66c12ace296bb21 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 16:08:14 +0100 Subject: [PATCH 19/24] chore: overall code cleanup Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/argon2id.rs | 6 +- crates/parser/src/visit.rs | 46 +++---- crates/tinywasm/src/func.rs | 73 ++++++----- crates/tinywasm/src/imports.rs | 1 - .../src/runtime/interpreter/macros.rs | 79 +++++------- .../tinywasm/src/runtime/interpreter/mod.rs | 115 ++++++++---------- .../src/runtime/interpreter/no_std_floats.rs | 97 +++------------ .../src/runtime/interpreter/traits.rs | 44 +++---- .../tinywasm/src/runtime/stack/call_stack.rs | 2 +- .../tinywasm/src/runtime/stack/value_stack.rs | 12 +- crates/tinywasm/src/store/mod.rs | 81 +++++------- crates/tinywasm/src/store/table.rs | 4 +- crates/types/src/instructions.rs | 2 +- crates/types/src/value.rs | 2 +- 14 files changed, 221 insertions(+), 343 deletions(-) diff --git a/crates/benchmarks/benches/argon2id.rs b/crates/benchmarks/benches/argon2id.rs index 7c1ffc5..a503687 100644 --- a/crates/benchmarks/benches/argon2id.rs +++ b/crates/benchmarks/benches/argon2id.rs @@ -45,10 +45,10 @@ fn criterion_benchmark(c: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(7)); group.sample_size(10); - group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); + // group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(params), "argon2id"))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); + // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); + // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); } criterion_group!( diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index edebc97..88b6ba5 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -340,16 +340,17 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_local_set(&mut self, idx: u32) -> Self::Output { - 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)) - } + 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 { @@ -372,18 +373,19 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_i32_add(&mut self) -> Self::Output { - if self.instructions.len() < 2 { - return self.visit(Instruction::I32Add); - } + 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), - } + // 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 { @@ -416,7 +418,7 @@ 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 as usize) + *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"); 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 #[inline] pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result> { + // 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 != ¶m.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 FuncHandleTyped { 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 ValTypesFromTuple for T1 -where - T1: ToValType, -{ +impl 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/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index 9227ceb..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), } } }}; @@ -62,21 +61,15 @@ macro_rules! mem_load { /// Store a value to memory macro_rules! mem_store { ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ - log::debug!("mem_store!({}, {:?})", stringify!($type), $arg); mem_store!($type, $type, $arg, $stack, $store, $module) }}; ($store_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 val = $stack.values.pop_t::<$store_type>()?; - let addr: u64 = $stack.values.pop()?.into(); - - let val = val as $store_type; + 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(); - + let addr: u64 = $stack.values.pop()?.into(); mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?; }}; } @@ -103,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() + }); }}; } @@ -138,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()); }}; } @@ -152,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 bd07051..007acaa 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -79,14 +79,14 @@ enum ExecResult { #[inline(always)] fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &ModuleInstance) -> Result { 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 cf.current_instruction() { @@ -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::()?; // 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(); @@ -238,7 +234,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::Loop, - &args, + args, module, ), &mut stack.values, @@ -251,9 +247,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M BlockFrame::new( cf.instr_ptr, cf.instr_ptr + *end_offset as usize, - stack.values.len(), // - params, + stack.values.len(), BlockType::Block, - &args, + args, module, ), &mut stack.values, @@ -282,7 +278,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } let idx = stack.values.pop_t::()? as usize; - let to = instr.get(idx).unwrap_or(&default); + let to = instr.get(idx).unwrap_or(default); break_to!(cf, stack, to); } @@ -335,13 +331,10 @@ 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 local = stack.values.last(); cf.set_local( *local_index as usize, - stack - .values - .last() - .expect("localtee: stack is empty. this should have been validated by the parser") - .clone(), + *local.expect("localtee: stack is empty. this should have been validated by the parser"), ); } @@ -362,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())); } @@ -373,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())); } @@ -395,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::()?; - let src = stack.values.pop_t::()?; - let dst = stack.values.pop_t::()?; + 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(); @@ -414,9 +405,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } MemoryFill(addr) => { - let size = stack.values.pop_t::()?; - let val = stack.values.pop_t::()?; - let dst = stack.values.pop_t::()?; + 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(); @@ -428,26 +419,20 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let offset = stack.values.pop_t::()? as usize; let dst = stack.values.pop_t::()? 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, size, data)?; + mem.store(dst, size, &data[offset..(offset + size)])?; } DataDrop(data_index) => { @@ -496,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), @@ -543,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), @@ -561,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), @@ -579,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), 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 14ad050..12270d1 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -143,7 +143,7 @@ impl CallFrame { self.locals[local_index] } - #[inline] + #[inline(always)] pub(crate) fn instructions(&self) -> &[Instruction] { &self.func_instance.0.instructions } diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index c6d7918..5903228 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -33,6 +33,16 @@ impl ValueStack { 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; + } + let top = self.stack[len - 1]; + self.stack[len - 1] = func(top); + } + #[inline] pub(crate) fn len(&self) -> usize { self.stack.len() @@ -53,7 +63,7 @@ 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); } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index a3d99fe..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> { 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 @@ -356,26 +347,20 @@ impl Store { 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 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))); + }; let offset = self.eval_i32_const(&offset)?; - - 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)) - })?; - - // 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, data.data.len(), &data.data) - { - return Ok((data_addrs.into_boxed_slice(), Some(trap))); + 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))); + }; + + 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), } - - // drop the data - None } tinywasm_types::DataKind::Passive => Some(data.data.to_vec()), }; @@ -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/types/src/instructions.rs b/crates/types/src/instructions.rs index 537b8d7..9923116 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -94,7 +94,7 @@ pub enum Instruction { // LocalGet + I32Const + I32Store => I32LocalGetConstStore + I32Const // Also common, helps us skip the stack entirely. // Has to be followed by an I32Const instruction - // I32LocalGetConstStore { local: LocalAddr, offset: i32, mem_addr: MemAddr }, // I32Store + LocalGet + I32Const + // I32StoreLocal { local: LocalAddr, offset: i32, mem_addr: MemAddr }, // I64Xor + I64Const + I64RotL // Commonly used by a few crypto libraries diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index df062ce..bcd43e5 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -142,7 +142,7 @@ impl ValType { WasmValue::default_for(*self) } - pub(crate) fn to_byte(&self) -> u8 { + pub(crate) fn to_byte(self) -> u8 { match self { ValType::I32 => 0x7F, ValType::I64 => 0x7E, From cfe8b708a82dd42b65d3ed4bef7577ed8737cd22 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 16:29:18 +0100 Subject: [PATCH 20/24] chore: remove recursive macro from parser Signed-off-by: Henry Gressmann --- Cargo.lock | 4 +- crates/parser/Cargo.toml | 2 +- crates/parser/src/lib.rs | 1 - crates/parser/src/visit.rs | 96 +++++++++++--------------------------- 4 files changed, 29 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a6fdbbd..8edaba3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", 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/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 88b6ba5..cc9f0e2 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -71,6 +71,14 @@ 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 { @@ -88,34 +96,6 @@ macro_rules! define_mem_operands { }; } -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, label_ptrs: Vec, @@ -141,6 +121,10 @@ 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, @@ -329,7 +313,6 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { match instruction { Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), - // Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), _ => return self.visit(Instruction::LocalGet(idx)), }; @@ -502,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 { @@ -540,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); } From ef13c584654e243dfd4c1c9d3979a386c297dfd3 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 16:42:18 +0100 Subject: [PATCH 21/24] chore: update benchmark results Signed-off-by: Henry Gressmann --- BENCHMARKS.md | 10 +++++----- crates/benchmarks/benches/argon2id.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 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/crates/benchmarks/benches/argon2id.rs b/crates/benchmarks/benches/argon2id.rs index a503687..7c1ffc5 100644 --- a/crates/benchmarks/benches/argon2id.rs +++ b/crates/benchmarks/benches/argon2id.rs @@ -45,10 +45,10 @@ fn criterion_benchmark(c: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(7)); group.sample_size(10); - // group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); + group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(params), "argon2id"))); - // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); - // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); } criterion_group!( From 417bb70c0f8c8f8781f675481e5336affd209183 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 17:00:29 +0100 Subject: [PATCH 22/24] chore: update readme Signed-off-by: Henry Gressmann --- Cargo.lock | 42 ++++++++++++++--------------- README.md | 2 +- crates/tinywasm/src/store/memory.rs | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8edaba3..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]] @@ -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/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 1c8acce..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; From 1f5d583470a30fdfc3f8327f624a613191c7cf95 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Fri, 1 Mar 2024 16:06:54 +0100 Subject: [PATCH 23/24] chore: prepare release Signed-off-by: Henry Gressmann --- CHANGELOG.md | 76 ++++++++++++++++++++++++++++++++++++++++ crates/cli/README.md | 1 + crates/parser/Cargo.toml | 1 + 3 files changed, 78 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f33ee83 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,76 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.4.0...v0.5.0 + +### Added + +- Added this `CHANGELOG.md` file to the project +- Added merged instructions for improved performance and reduced bytecode size + +### Changed + +- Now using a custom `wasmparser` fork +- Switched to a visitor pattern for parsing WebAssembly modules +- Reduced the overhead of control flow instructions +- Reduced the size of bytecode instructions +- Fixed issues on the latest nightly Rust compiler +- Simpliefied a lot of the internal macros + +### Removed + +- Removed duplicate internal code + +## [0.4.0] - 2024-02-04 [(commits)](https://github.com/explodingcamera/tinywasm/compare/v0.3.0...v0.4.0) + +**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.3.0...v0.4.0 + +### Added + +- Added benchmarks for comparison with other WebAssembly runtimes +- Added support for pre-processing WebAssembly modules into tinywasm bytecode +- Improved examples and documentation +- Implemented the bulk memory operations proposal + +### Changed + +- Overall performance improvements + +## [0.3.0] - 2024-01-26 + +**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.2.0...v0.3.0 + +- Better trap handling +- Implement linker +- Element instantiation +- Table Operations +- FuncRefs +- Typesafe host functions +- Host function context +- Spec compliance improvements +- Wasm 2.0 testsuite +- Usage examples +- End-to-end tests +- Lots of bug fixes +- Full `no_std` support + +## [0.3.0] - 2024-01-11 + +**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.1.0...v0.2.0 + +- Support for br_table +- Memory trapping improvments +- Implicit function lable scopes +- else Instructions +- All Memory instructions +- Imports +- Basic linking +- Globals +- Fix function addr resolution +- Reference Instructions diff --git a/crates/cli/README.md b/crates/cli/README.md index 1f7a1bb..70b2cff 100644 --- a/crates/cli/README.md +++ b/crates/cli/README.md @@ -1,6 +1,7 @@ # `tinywasm-cli` The `tinywasm-cli` crate contains the command line interface for the `tinywasm` project. See [`tinywasm`](https://crates.io/crates/tinywasm) for more information. +It is recommended to use the library directly instead of the CLI. ## Usage diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 3be0bb1..3c5717d 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -17,3 +17,4 @@ tinywasm-types={version="0.4.0", path="../types", default-features=false} default=["std", "logging"] logging=["log"] std=["tinywasm-types/std"] + \ No newline at end of file From d2b6c53b5d0134a609f6d3dfd8225d5e9d2ed3c4 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Fri, 1 Mar 2024 16:07:53 +0100 Subject: [PATCH 24/24] Release 0.5.0 tinywasm@0.5.0 tinywasm-cli@0.5.0 tinywasm-parser@0.5.0 tinywasm-types@0.5.0 wasm-testsuite@0.2.2 Generated by cargo-workspaces --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- crates/cli/Cargo.toml | 2 +- crates/parser/Cargo.toml | 4 ++-- crates/tinywasm/Cargo.toml | 4 ++-- crates/wasm-testsuite/Cargo.toml | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 835692a..902dd10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2062,7 +2062,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tinywasm" -version = "0.4.1" +version = "0.5.0" dependencies = [ "eyre", "libm", @@ -2080,7 +2080,7 @@ dependencies = [ [[package]] name = "tinywasm-cli" -version = "0.4.1" +version = "0.5.0" dependencies = [ "argh", "color-eyre", @@ -2092,7 +2092,7 @@ dependencies = [ [[package]] name = "tinywasm-parser" -version = "0.4.1" +version = "0.5.0" dependencies = [ "log", "tinywasm-types", @@ -2111,7 +2111,7 @@ dependencies = [ [[package]] name = "tinywasm-types" -version = "0.4.1" +version = "0.5.0" dependencies = [ "bytecheck 0.7.0", "log", @@ -2342,7 +2342,7 @@ dependencies = [ [[package]] name = "wasm-testsuite" -version = "0.2.1" +version = "0.2.2" dependencies = [ "rust-embed", ] diff --git a/Cargo.toml b/Cargo.toml index 68c0b5a..41b7af0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ panic="abort" inherits="release" [workspace.package] -version="0.4.1" +version="0.5.0" edition="2021" license="MIT OR Apache-2.0" authors=["Henry Gressmann "] diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 78d0b90..f6105c8 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,7 +14,7 @@ path="src/bin.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tinywasm={version="0.4.0", path="../tinywasm", features=["std", "parser"]} +tinywasm={version="0.5.0", path="../tinywasm", features=["std", "parser"]} argh="0.1" color-eyre={version="0.6", default-features=false} log="0.4" diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 3c5717d..b21c0ac 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -11,10 +11,10 @@ repository.workspace=true # fork of wasmparser with no_std support, see https://github.com/bytecodealliance/wasmtime/issues/3495 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} +tinywasm-types={version="0.5.0", path="../types", default-features=false} [features] default=["std", "logging"] logging=["log"] std=["tinywasm-types/std"] - \ No newline at end of file + diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index 01502bb..406a82d 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -14,8 +14,8 @@ path="src/lib.rs" [dependencies] log={version="0.4", optional=true} -tinywasm-parser={version="0.4.0", path="../parser", default-features=false, optional=true} -tinywasm-types={version="0.4.0", path="../types", default-features=false} +tinywasm-parser={version="0.5.0", path="../parser", default-features=false, optional=true} +tinywasm-types={version="0.5.0", path="../types", default-features=false} libm={version="0.2", default-features=false} [dev-dependencies] diff --git a/crates/wasm-testsuite/Cargo.toml b/crates/wasm-testsuite/Cargo.toml index 04f478c..97c1f58 100644 --- a/crates/wasm-testsuite/Cargo.toml +++ b/crates/wasm-testsuite/Cargo.toml @@ -1,6 +1,6 @@ [package] name="wasm-testsuite" -version="0.2.1" +version="0.2.2" description="Mirror of the WebAssembly core testsuite for use in testing WebAssembly implementations" license="Apache-2.0" readme="README.md"