diff --git a/CHANGELOG.md b/CHANGELOG.md index e63ff2b..8c47dea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for the custom memory page sizes proposal ([#22](https://github.com/explodingcamera/tinywasm/pull/22) by [@danielstuart14](https://github.com/danielstuart14)) - Support for the `tail_call` proposal +- Support for the `memory64` proposal +- Groundwork for the `simd` proposal +- New `canonicalize_nans` feature flag to enable canonicalizing NaN values in the `f32`, `f64`, and `v128` types ### Breaking Changes diff --git a/Cargo.lock b/Cargo.lock index acdc41b..d1cbed1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,18 +114,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.36" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstyle", "clap_lex", @@ -268,15 +268,15 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "hermit-abi" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "humantime" @@ -353,15 +353,15 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libm" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8" [[package]] name = "log" @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -552,9 +552,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -664,9 +664,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.229.0" +version = "0.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2" +checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" dependencies = [ "leb128fmt", "wasmparser", @@ -674,9 +674,9 @@ dependencies = [ [[package]] name = "wasm-testsuite" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "735333508bfc70a3cc7a7ed86b3cd53e4a775cbf78511eaec8ac4926ed3a0bf7" +checksum = "77cf162d75d8af40f1e44215c84edd30f895538741c29d1c890249665b310d94" dependencies = [ "include_dir", "wast", @@ -684,9 +684,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.229.0" +version = "0.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c" +checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" dependencies = [ "bitflags", "indexmap", @@ -695,9 +695,9 @@ dependencies = [ [[package]] name = "wast" -version = "229.0.0" +version = "230.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63fcaff613c12225696bb163f79ca38ffb40e9300eff0ff4b8aa8b2f7eadf0d9" +checksum = "b8edac03c5fa691551531533928443faf3dc61a44f814a235c7ec5d17b7b34f1" dependencies = [ "bumpalo", "leb128fmt", @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.229.0" +version = "1.230.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4189bad08b70455a9e9e67dc126d2dcf91fac143a80f1046747a5dde6d4c33e0" +checksum = "0d77d62229e38db83eac32bacb5f61ebb952366ab0dae90cf2b3c07a65eea894" dependencies = [ "wast", ] diff --git a/Cargo.toml b/Cargo.toml index 1f645cf..135dbd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ default-members=[".", "crates/tinywasm", "crates/types", "crates/parser"] resolver="2" [workspace.dependencies] -wast="229" -wat="1.229" -wasmparser={version="0.229", default-features=false} +wast="230" +wat="1.230" +wasmparser={version="0.230", default-features=false} eyre="0.6" log="0.4" pretty_env_logger="0.5" @@ -19,6 +19,8 @@ edition="2024" license="MIT OR Apache-2.0" authors=["Henry Gressmann "] repository="https://github.com/explodingcamera/tinywasm" +categories=["wasm", "no-std"] +keywords=["tinywasm"] [package] name="tinywasm-root" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index d5e64a8..097a7c4 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -7,6 +7,8 @@ license.workspace=true authors.workspace=true repository.workspace=true rust-version.workspace=true +keywords.workspace=true +categories=["wasm"] [[bin]] name="tinywasm-cli" diff --git a/crates/cli/src/bin.rs b/crates/cli/src/bin.rs index 8c0fe93..d00848e 100644 --- a/crates/cli/src/bin.rs +++ b/crates/cli/src/bin.rs @@ -82,7 +82,7 @@ fn main() -> Result<()> { match args.nested { TinyWasmSubcommand::Run(Run { wasm_file, engine, args, func }) => { - debug!("args: {:?}", args); + debug!("args: {args:?}"); let path = cwd.join(wasm_file.clone()); let module = match wasm_file.ends_with(".wat") { diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 7bd9cad..3cbeee1 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -7,6 +7,8 @@ license.workspace=true authors.workspace=true repository.workspace=true rust-version.workspace=true +keywords.workspace=true +categories.workspace=true [dependencies] wasmparser={workspace=true, features=["validate", "features", "simd"]} diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index aa33995..f0196b6 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -83,6 +83,8 @@ impl Parser { cm_nested_names: false, cm_values: false, cm_error_context: false, + cm_fixed_size_list: false, + cm_gc: false, }; Validator::new_with_features(features.into()) } diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 0e17ad9..1651bf0 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -30,7 +30,7 @@ pub(crate) struct ModuleReader { } impl ModuleReader { - pub(crate) fn new() -> ModuleReader { + pub(crate) fn new() -> Self { Self::default() } @@ -125,7 +125,7 @@ impl ModuleReader { self.code_type_addrs = reader.into_iter().map(|f| Ok(f?)).collect::>>()?; } CodeSectionStart { count, range, .. } => { - debug!("Found code section ({} functions)", count); + debug!("Found code section ({count} functions)"); if !self.code.is_empty() { return Err(ParseError::DuplicateSection("Code section".into())); } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 10005c0..4ae2cc1 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -370,17 +370,13 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild let if_instruction = &mut self.instructions[if_label_pointer]; - let (else_offset, end_offset) = match if_instruction { - Instruction::If(else_offset, end_offset) - | Instruction::IfWithFuncType(_, else_offset, end_offset) - | Instruction::IfWithType(_, else_offset, end_offset) => (else_offset, end_offset), - _ => { - self.errors.push(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - )); - - return; - } + let (Instruction::If(else_offset, end_offset) + | Instruction::IfWithFuncType(_, else_offset, end_offset) + | Instruction::IfWithType(_, else_offset, end_offset)) = if_instruction + else { + return self.errors.push(crate::ParseError::UnsupportedOperator( + "Expected to end an if block, but the last label was not an if".to_string(), + )); }; *else_offset = (label_pointer - if_label_pointer) @@ -453,6 +449,21 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild self.instructions.push(Instruction::RefIsNull); } + fn visit_typed_select_multi(&mut self, _tys: Vec) -> Self::Output { + self.errors.push(crate::ParseError::UnsupportedOperator( + "Typed select with multiple types is not supported".to_string(), + )); + + // self.instructions.extend(tys.into_iter().map(|ty| match ty { + // wasmparser::ValType::I32 => Instruction::Select32, + // wasmparser::ValType::F32 => Instruction::Select32, + // wasmparser::ValType::I64 => Instruction::Select64, + // wasmparser::ValType::F64 => Instruction::Select64, + // wasmparser::ValType::V128 => Instruction::Select128, + // wasmparser::ValType::Ref(_) => Instruction::SelectRef, + // })); + } + fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output { self.instructions.push(match ty { wasmparser::ValType::I32 => Instruction::Select32, diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index 3281476..19e75ff 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -7,6 +7,8 @@ license.workspace=true authors.workspace=true repository.workspace=true rust-version.workspace=true +keywords.workspace=true +categories.workspace=true readme="../../README.md" [lib] @@ -20,7 +22,7 @@ tinywasm-types={version="0.9.0-alpha.0", path="../types", default-features=false libm={version="0.2", default-features=false} [dev-dependencies] -wasm-testsuite={version="0.5.3"} +wasm-testsuite={version="0.5.4"} indexmap="2.7" wast={workspace=true} wat={workspace=true} @@ -32,12 +34,20 @@ serde_json={version="1.0"} serde={version="1.0", features=["derive"]} [features] -default=["std", "parser", "logging", "archive"] +default=["std", "parser", "logging", "archive", "canonicalize_nans", "__simd"] + logging=["log", "tinywasm-parser?/logging", "tinywasm-types/logging"] std=["tinywasm-parser?/std", "tinywasm-types/std"] + +# support for parsing WebAssembly parser=["dep:tinywasm-parser"] + +# support for "archiving" tinywasm bytecode archive=["tinywasm-types/archive"] +# canonicalize all NaN values to a single representation +canonicalize_nans=[] + # enable simd support (unstable / unfinished) __simd=[] diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 9637521..4b3a969 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -51,6 +51,7 @@ pub enum Error { #[derive(Debug)] /// Errors that can occur when linking a WebAssembly module +#[non_exhaustive] pub enum LinkingError { /// An unknown import was encountered UnknownImport { @@ -83,6 +84,7 @@ impl LinkingError { /// A WebAssembly trap /// /// See +#[non_exhaustive] pub enum Trap { /// An unreachable instruction was executed Unreachable, @@ -211,9 +213,9 @@ impl Display for Error { impl Display for LinkingError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - Self::UnknownImport { module, name } => write!(f, "unknown import: {}.{}", module, name), + Self::UnknownImport { module, name } => write!(f, "unknown import: {module}.{name}"), Self::IncompatibleImportType { module, name } => { - write!(f, "incompatible import type: {}.{}", module, name) + write!(f, "incompatible import type: {module}.{name}") } } } diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 2eeabca..5e5eb25 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,6 +1,6 @@ use crate::interpreter::stack::{CallFrame, Stack}; +use crate::{log, unlikely, Function}; use crate::{Error, FuncContext, Result, Store}; -use crate::{Function, log, unlikely}; use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; use tinywasm_types::{ExternRef, FuncRef, FuncType, ModuleInstanceAddr, ValType, WasmValue}; @@ -38,11 +38,11 @@ impl FuncHandle { // 5. For each value type and the corresponding value, check if types match 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 - } else { + if ty == ¶m.val_type() { true + } else { + log::error!("param type mismatch at index {_i}: expected {ty:?}, got {param:?}"); + false } })) { return Err(Error::Other("Type mismatch".into())); diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 12876c8..90f4971 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -170,7 +170,7 @@ impl Extern { let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result> { let args = P::from_wasm_value_tuple(args)?; let result = func(ctx, args)?; - Ok(result.into_wasm_value_tuple().to_vec()) + Ok(result.into_wasm_value_tuple()) }; let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() }; @@ -263,7 +263,7 @@ impl ResolvedImports { impl Imports { /// Create a new empty import set pub fn new() -> Self { - Imports { values: BTreeMap::new(), modules: BTreeMap::new() } + Self { values: BTreeMap::new(), modules: BTreeMap::new() } } /// Merge two import sets diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index eebf5ff..75c12d6 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -12,7 +12,7 @@ use crate::{Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut #[derive(Debug, Clone)] pub struct ModuleInstance(pub(crate) Rc); -#[allow(dead_code)] +#[expect(dead_code)] #[derive(Debug)] pub(crate) struct ModuleInstanceInner { pub(crate) failed_to_instantiate: bool, @@ -90,7 +90,7 @@ impl ModuleInstance { exports: module.0.exports, }; - let instance = ModuleInstance::new(instance); + let instance = Self::new(instance); store.add_instance(instance.clone()); match (elem_trapped, data_trapped) { @@ -192,7 +192,7 @@ impl ModuleInstance { pub fn exported_memory<'a>(&self, store: &'a mut Store, name: &str) -> Result> { let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {name}")))?; let ExternVal::Memory(mem_addr) = export else { - return Err(Error::Other(format!("Export is not a memory: {}", name))); + return Err(Error::Other(format!("Export is not a memory: {name}"))); }; self.memory(store, mem_addr) @@ -202,7 +202,7 @@ impl ModuleInstance { pub fn exported_memory_mut<'a>(&self, store: &'a mut Store, name: &str) -> Result> { let export = self.export_addr(name).ok_or_else(|| Error::Other(format!("Export not found: {name}")))?; let ExternVal::Memory(mem_addr) = export else { - return Err(Error::Other(format!("Export is not a memory: {}", name))); + return Err(Error::Other(format!("Export is not a memory: {name}"))); }; self.memory_mut(store, mem_addr) diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 4bf4f5d..10e9ddd 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -142,16 +142,16 @@ impl<'store, 'stack> Executor<'store, 'stack> { I64Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, F32Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, F64Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, - I32Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i32)?, - I32Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i32)?, - I32Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i32)?, - I32Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i32)?, - I64Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i64)?, - I64Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i64)?, - I64Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i64)?, - I64Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i64)?, - I64Load32S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i64)?, - I64Load32U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v as i64)?, + I32Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I32Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I32Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I32Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I64Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load32S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load32U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, I64Eqz => self.stack.values.replace_top::(|v| Ok(i32::from(v == 0))).to_cf()?, I32Eqz => self.stack.values.replace_top_same::(|v| Ok(i32::from(v == 0))).to_cf()?, @@ -238,32 +238,32 @@ impl<'store, 'stack> Executor<'store, 'stack> { I64Rotr => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_rotr(b))).to_cf()?, I32Clz => self.stack.values.replace_top_same::(|v| Ok(v.leading_zeros() as i32)).to_cf()?, - I64Clz => self.stack.values.replace_top_same::(|v| Ok(v.leading_zeros() as i64)).to_cf()?, + I64Clz => self.stack.values.replace_top_same::(|v| Ok(i64::from(v.leading_zeros()))).to_cf()?, I32Ctz => self.stack.values.replace_top_same::(|v| Ok(v.trailing_zeros() as i32)).to_cf()?, - I64Ctz => self.stack.values.replace_top_same::(|v| Ok(v.trailing_zeros() as i64)).to_cf()?, + I64Ctz => self.stack.values.replace_top_same::(|v| Ok(i64::from(v.trailing_zeros()))).to_cf()?, I32Popcnt => self.stack.values.replace_top_same::(|v| Ok(v.count_ones() as i32)).to_cf()?, - I64Popcnt => self.stack.values.replace_top_same::(|v| Ok(v.count_ones() as i64)).to_cf()?, + I64Popcnt => self.stack.values.replace_top_same::(|v| Ok(i64::from(v.count_ones()))).to_cf()?, F32ConvertI32S => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, F32ConvertI64S => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F64ConvertI32S => self.stack.values.replace_top::(|v| Ok(v as f64)).to_cf()?, + F64ConvertI32S => self.stack.values.replace_top::(|v| Ok(f64::from(v))).to_cf()?, F64ConvertI64S => self.stack.values.replace_top::(|v| Ok(v as f64)).to_cf()?, F32ConvertI32U => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, F32ConvertI64U => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F64ConvertI32U => self.stack.values.replace_top::(|v| Ok(v as f64)).to_cf()?, + F64ConvertI32U => self.stack.values.replace_top::(|v| Ok(f64::from(v))).to_cf()?, F64ConvertI64U => self.stack.values.replace_top::(|v| Ok(v as f64)).to_cf()?, - I32Extend8S => self.stack.values.replace_top_same::(|v| Ok((v as i8) as i32)).to_cf()?, - I32Extend16S => self.stack.values.replace_top_same::(|v| Ok((v as i16) as i32)).to_cf()?, - I64Extend8S => self.stack.values.replace_top_same::(|v| Ok((v as i8) as i64)).to_cf()?, - I64Extend16S => self.stack.values.replace_top_same::(|v| Ok((v as i16) as i64)).to_cf()?, - I64Extend32S => self.stack.values.replace_top_same::(|v| Ok((v as i32) as i64)).to_cf()?, - I64ExtendI32U => self.stack.values.replace_top::(|v| Ok(v as i64)).to_cf()?, - I64ExtendI32S => self.stack.values.replace_top::(|v| Ok(v as i64)).to_cf()?, + I32Extend8S => self.stack.values.replace_top_same::(|v| Ok(i32::from(v as i8))).to_cf()?, + I32Extend16S => self.stack.values.replace_top_same::(|v| Ok(i32::from(v as i16))).to_cf()?, + I64Extend8S => self.stack.values.replace_top_same::(|v| Ok(i64::from(v as i8))).to_cf()?, + I64Extend16S => self.stack.values.replace_top_same::(|v| Ok(i64::from(v as i16))).to_cf()?, + I64Extend32S => self.stack.values.replace_top_same::(|v| Ok(i64::from(v as i32))).to_cf()?, + I64ExtendI32U => self.stack.values.replace_top::(|v| Ok(i64::from(v))).to_cf()?, + I64ExtendI32S => self.stack.values.replace_top::(|v| Ok(i64::from(v))).to_cf()?, I32WrapI64 => self.stack.values.replace_top::(|v| Ok(v as i32)).to_cf()?, F32DemoteF64 => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F64PromoteF32 => self.stack.values.replace_top::(|v| Ok(v as f64)).to_cf()?, + F64PromoteF32 => self.stack.values.replace_top::(|v| Ok(f64::from(v))).to_cf()?, F32Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, F64Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, @@ -324,7 +324,19 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[cfg(feature = "__simd")] V128Bitselect => self.stack.values.calculate_same_3::(|v1, v2, c| Ok((v1 & c) | (v2 & !c))).to_cf()?, #[cfg(feature = "__simd")] V128AnyTrue => self.stack.values.replace_top::(|v| Ok((v.reduce_or() != 0) as i32)).to_cf()?, #[cfg(feature = "__simd")] I8x16Swizzle => self.stack.values.calculate_same::(|a, s| Ok(a.swizzle_dyn(s))).to_cf()?, + #[cfg(feature = "__simd")] V128Load(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| v)?, + #[cfg(feature = "__simd")] V128Load8x8S(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load8x8U(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load16x4S(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load16x4U(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load32x2S(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load32x2U(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load8Splat(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load16Splat(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load32Splat(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Load64Splat(_arg) => unimplemented!(), + #[cfg(feature = "__simd")] V128Store(arg) => self.exec_mem_store::(arg.mem_addr(), arg.offset(), |v| v)?, #[cfg(feature = "__simd")] V128Store8Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, @@ -358,6 +370,13 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[cfg(feature = "__simd")] V128Load32Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, #[cfg(feature = "__simd")] V128Load64Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, + #[cfg(feature = "__simd")] I8x16ReplaceLane(_lane) => unimplemented!(), + #[cfg(feature = "__simd")] I16x8ReplaceLane(_lane) => unimplemented!(), + #[cfg(feature = "__simd")] I32x4ReplaceLane(_lane) => unimplemented!(), + #[cfg(feature = "__simd")] I64x2ReplaceLane(_lane) => unimplemented!(), + #[cfg(feature = "__simd")] F32x4ReplaceLane(_lane) => unimplemented!(), + #[cfg(feature = "__simd")] F64x2ReplaceLane(_lane) => unimplemented!(), + #[cfg(feature = "__simd")] I8x16Splat => self.stack.values.replace_top::(|v| Ok(Simd::::splat(v as i8))).to_cf()?, #[cfg(feature = "__simd")] I16x8Splat => self.stack.values.replace_top::(|v| Ok(Simd::::splat(v as i16))).to_cf()?, #[cfg(feature = "__simd")] I32x4Splat => self.stack.values.replace_top::(|v| Ok(Simd::::splat(v))).to_cf()?, @@ -368,12 +387,14 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[cfg(feature = "__simd")] I8x16Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] I16x8Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] I32x4Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, + #[cfg(feature = "__simd")] I64x2Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] F32x4Eq => self.stack.values.calculate::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] F64x2Eq => self.stack.values.calculate::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] I8x16Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] I16x8Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] I32x4Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, + #[cfg(feature = "__simd")] I64x2Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] F32x4Ne => self.stack.values.calculate::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, #[cfg(feature = "__simd")] F64x2Ne => self.stack.values.calculate::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, @@ -500,6 +521,9 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[cfg(feature = "__simd")] I8x16SubSatU => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_sub(b))).to_cf()?, #[cfg(feature = "__simd")] I16x8SubSatU => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_sub(b))).to_cf()?, + #[cfg(feature = "__simd")] I8x16AvgrU => unimplemented!(), + #[cfg(feature = "__simd")] I16x8AvgrU => unimplemented!(), + #[cfg(feature = "__simd")] I16x8ExtAddPairwiseI8x16S => unimplemented!(), #[cfg(feature = "__simd")] I16x8ExtAddPairwiseI8x16U => unimplemented!(), #[cfg(feature = "__simd")] I32x4ExtAddPairwiseI16x8S => unimplemented!(), @@ -532,6 +556,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[cfg(feature = "__simd")] I64x2ExtendHighI32x4U => unimplemented!(), #[cfg(feature = "__simd")] I8x16Popcnt => self.stack.values.replace_top::(|v| Ok(v.count_ones())).to_cf()?, + #[cfg(feature = "__simd")] I8x16Shuffle(_idx) => unimplemented!(), #[cfg(feature = "__simd")] I16x8Q15MulrSatS => self.stack.values.calculate_same::(|a, b| { @@ -575,6 +600,8 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[cfg(feature = "__simd")] F64x2Floor => self.stack.values.replace_top_same::(|v| Ok(v.floor())).to_cf()?, #[cfg(feature = "__simd")] F32x4Trunc => self.stack.values.replace_top_same::(|v| Ok(v.trunc())).to_cf()?, #[cfg(feature = "__simd")] F64x2Trunc => self.stack.values.replace_top_same::(|v| Ok(v.trunc())).to_cf()?, + #[cfg(feature = "__simd")] F32x4Nearest => self.stack.values.replace_top_same::(|v| Ok(v.round())).to_cf()?, + #[cfg(feature = "__simd")] F64x2Nearest => self.stack.values.replace_top_same::(|v| Ok(v.round())).to_cf()?, #[cfg(feature = "__simd")] F32x4Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, #[cfg(feature = "__simd")] F64x2Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, #[cfg(feature = "__simd")] F32x4Neg => self.stack.values.replace_top_same::(|v| Ok(-v)).to_cf()?, @@ -664,15 +691,37 @@ impl<'store, 'stack> Executor<'store, 'stack> { // not correct #[cfg(feature = "__simd")] I32x4TruncSatF32x4S => self.stack.values.replace_top::(|v| Ok(v.trunc())).to_cf()?, #[cfg(feature = "__simd")] I32x4TruncSatF32x4U => self.stack.values.replace_top::(|v| Ok(v.trunc())).to_cf()?, - #[cfg(feature = "__simd")] F32x4ConvertI32x4S => {}, - #[cfg(feature = "__simd")] F32x4ConvertI32x4U => {}, - #[cfg(feature = "__simd")] F64x2ConvertLowI32x4S => {}, - #[cfg(feature = "__simd")] F64x2ConvertLowI32x4U => {}, - #[cfg(feature = "__simd")] F32x4DemoteF64x2Zero => {}, - #[cfg(feature = "__simd")] F64x2PromoteLowF32x4 => {}, + #[cfg(feature = "__simd")] F32x4ConvertI32x4S => unimplemented!(), + #[cfg(feature = "__simd")] F32x4ConvertI32x4U => unimplemented!(), + #[cfg(feature = "__simd")] F64x2ConvertLowI32x4S => unimplemented!(), + #[cfg(feature = "__simd")] F64x2ConvertLowI32x4U => unimplemented!(), + #[cfg(feature = "__simd")] F32x4DemoteF64x2Zero => unimplemented!(), + #[cfg(feature = "__simd")] F64x2PromoteLowF32x4 => unimplemented!(), #[cfg(feature = "__simd")] I32x4TruncSatF64x2SZero => unimplemented!(), #[cfg(feature = "__simd")] I32x4TruncSatF64x2UZero => unimplemented!(), + #[cfg(feature = "__simd")] I8x16RelaxedSwizzle => unimplemented!(), + #[cfg(feature = "__simd")] I32x4RelaxedTruncF32x4S => unimplemented!(), + #[cfg(feature = "__simd")] I32x4RelaxedTruncF32x4U => unimplemented!(), + #[cfg(feature = "__simd")] I32x4RelaxedTruncF64x2SZero => unimplemented!(), + #[cfg(feature = "__simd")] I32x4RelaxedTruncF64x2UZero => unimplemented!(), + #[cfg(feature = "__simd")] F32x4RelaxedMadd => unimplemented!(), + #[cfg(feature = "__simd")] F32x4RelaxedNmadd => unimplemented!(), + #[cfg(feature = "__simd")] F64x2RelaxedMadd => unimplemented!(), + #[cfg(feature = "__simd")] F64x2RelaxedNmadd => unimplemented!(), + #[cfg(feature = "__simd")] I8x16RelaxedLaneselect => unimplemented!(), + #[cfg(feature = "__simd")] I16x8RelaxedLaneselect => unimplemented!(), + #[cfg(feature = "__simd")] I32x4RelaxedLaneselect => unimplemented!(), + #[cfg(feature = "__simd")] I64x2RelaxedLaneselect => unimplemented!(), + #[cfg(feature = "__simd")] F32x4RelaxedMin => unimplemented!(), + #[cfg(feature = "__simd")] F32x4RelaxedMax => unimplemented!(), + #[cfg(feature = "__simd")] F64x2RelaxedMin => unimplemented!(), + #[cfg(feature = "__simd")] F64x2RelaxedMax => unimplemented!(), + #[cfg(feature = "__simd")] I16x8RelaxedQ15mulrS => unimplemented!(), + #[cfg(feature = "__simd")] I16x8RelaxedDotI8x16I7x16S => unimplemented!(), + #[cfg(feature = "__simd")] I32x4RelaxedDotI8x16I7x16AddS => unimplemented!(), + + #[allow(unreachable_patterns)] i => return ControlFlow::Break(Some(Error::UnsupportedFeature(format!("unimplemented opcode: {i:?}")))), }; @@ -690,18 +739,17 @@ impl<'store, 'stack> Executor<'store, 'stack> { wasm_func: Rc, owner: ModuleInstanceAddr, ) -> ControlFlow> { - if !IS_RETURN_CALL { - let locals = self.stack.values.pop_locals(wasm_func.params, wasm_func.locals); + let locals = self.stack.values.pop_locals(wasm_func.params, wasm_func.locals); + + if IS_RETURN_CALL { + self.cf.reuse_for(wasm_func, locals, self.stack.blocks.len() as u32, owner); + } else { let new_call_frame = CallFrame::new_raw(wasm_func, owner, locals, self.stack.blocks.len() as u32); self.cf.incr_instr_ptr(); // skip the call instruction self.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?; - self.module.swap_with(self.cf.module_addr(), self.store); - } else { - let locals = self.stack.values.pop_locals(wasm_func.params, wasm_func.locals); - self.cf.reuse_for(wasm_func, locals, self.stack.blocks.len() as u32, owner); - self.module.swap_with(self.cf.module_addr(), self.store); } + self.module.swap_with(self.cf.module_addr(), self.store); ControlFlow::Continue(()) } fn exec_call_host(&mut self, host_func: Rc) -> ControlFlow> { @@ -880,7 +928,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { self.stack.values.push(val); } fn exec_ref_is_null(&mut self) { - let is_null = self.stack.values.pop::().is_none() as i32; + let is_null = i32::from(self.stack.values.pop::().is_none()); self.stack.values.push::(is_null); } @@ -898,7 +946,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { let pages_delta = match mem.is_64bit() { true => self.stack.values.pop::(), - false => self.stack.values.pop::() as i64, + false => i64::from(self.stack.values.pop::()), }; match ( @@ -1035,7 +1083,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { let addr = match mem.is_64bit() { true => self.stack.values.pop::() as u64, - false => self.stack.values.pop::() as u32 as u64, + false => u64::from(self.stack.values.pop::() as u32), }; let Some(Ok(addr)) = offset.checked_add(addr).map(|a| a.try_into()) else { @@ -1086,7 +1134,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { let addr = match mem.is_64bit() { true => self.stack.values.pop::() as u64, - false => self.stack.values.pop::() as u32 as u64, + false => u64::from(self.stack.values.pop::() as u32), }; if let Err(e) = mem.store((offset + addr) as usize, val.len(), &val) { @@ -1152,7 +1200,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into()); }; - table.init(dst as i64, &items[offset as usize..(offset + size) as usize]) + table.init(i64::from(dst), &items[offset as usize..(offset + size) as usize]) } fn exec_table_grow(&mut self, table_index: u32) -> Result<()> { let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)); @@ -1162,7 +1210,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { let val = self.stack.values.pop::(); match table.grow(n, val.into()) { - Ok(_) => self.stack.values.push(sz), + Ok(()) => self.stack.values.push(sz), Err(_) => self.stack.values.push(-1_i32), } diff --git a/crates/tinywasm/src/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs index 678c32e..7e8a461 100644 --- a/crates/tinywasm/src/interpreter/num_helpers.rs +++ b/crates/tinywasm/src/interpreter/num_helpers.rs @@ -72,9 +72,12 @@ macro_rules! impl_wasm_float_ops { ($($t:ty)*) => ($( impl TinywasmFloatExt for $t { // https://webassembly.github.io/spec/core/exec/numerics.html#op-fnearest + #[inline] fn tw_nearest(self) -> Self { match self { - // x if x.is_nan() => x, // preserve NaN + #[cfg(not(feature = "canonicalize_nans"))] + x if x.is_nan() => x, // preserve NaN + #[cfg(feature = "canonicalize_nans")] x if x.is_nan() => Self::NAN, // Do not preserve NaN x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros x if (0.0..=0.5).contains(&x) => 0.0, @@ -100,7 +103,9 @@ macro_rules! impl_wasm_float_ops { 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. + #[cfg(not(feature = "canonicalize_nans"))] + None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + #[cfg(feature = "canonicalize_nans")] None => Self::NAN, // Do not preserve NaN } } @@ -113,7 +118,9 @@ macro_rules! impl_wasm_float_ops { 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. + #[cfg(not(feature = "canonicalize_nans"))] + None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. + #[cfg(feature = "canonicalize_nans")] None => Self::NAN, // Do not preserve NaN } } @@ -184,8 +191,15 @@ macro_rules! impl_checked_wrapping_rem { impl_checked_wrapping_rem! { i32 i64 u32 u64 } -#[cfg(feature = "__simd")] +#[cfg(all(feature = "__simd", not(feature = "canonicalize_nans")))] +#[inline] +pub(crate) fn canonicalize_f32x4(x: core::simd::f32x4) -> core::simd::f32x4 { + x // No need to do anything, as we are not replacing NaNs +} + +#[cfg(all(feature = "__simd", feature = "canonicalize_nans"))] /// replace all NaNs in a f32x4 with f32::NAN +#[inline] pub(crate) fn canonicalize_f32x4(x: core::simd::f32x4) -> core::simd::f32x4 { use core::simd::{Simd, num::SimdFloat}; let nan = Simd::splat(f32::NAN); @@ -193,8 +207,15 @@ pub(crate) fn canonicalize_f32x4(x: core::simd::f32x4) -> core::simd::f32x4 { mask.select(nan, x) } +#[cfg(all(feature = "__simd", not(feature = "canonicalize_nans")))] +#[inline] +pub(crate) fn canonicalize_f64x2(x: core::simd::f64x2) -> core::simd::f64x2 { + x // No need to do anything, as we are not replacing NaNs +} + #[cfg(feature = "__simd")] /// replace all NaNs in a f64x2 with f64::NAN +#[inline] pub(crate) fn canonicalize_f64x2(x: core::simd::f64x2) -> core::simd::f64x2 { use core::simd::{Simd, num::SimdFloat}; let nan = Simd::splat(f64::NAN); diff --git a/crates/tinywasm/src/interpreter/values.rs b/crates/tinywasm/src/interpreter/values.rs index 779fc60..957b4bb 100644 --- a/crates/tinywasm/src/interpreter/values.rs +++ b/crates/tinywasm/src/interpreter/values.rs @@ -12,7 +12,7 @@ pub(crate) type Value128 = core::simd::u8x16; #[cfg(not(feature = "__simd"))] pub(crate) type Value128 = i128; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] /// A untyped WebAssembly value pub enum TinyWasmValue { /// A 32-bit value @@ -74,7 +74,7 @@ impl TinyWasmValue { /// Asserts that the value is a 32-bit value and returns it (panics if the value is the wrong size) pub fn unwrap_32(&self) -> Value32 { match self { - TinyWasmValue::Value32(v) => *v, + Self::Value32(v) => *v, _ => unreachable!("Expected Value32"), } } @@ -82,7 +82,7 @@ impl TinyWasmValue { /// Asserts that the value is a 64-bit value and returns it (panics if the value is the wrong size) pub fn unwrap_64(&self) -> Value64 { match self { - TinyWasmValue::Value64(v) => *v, + Self::Value64(v) => *v, _ => unreachable!("Expected Value64"), } } @@ -90,7 +90,7 @@ impl TinyWasmValue { /// Asserts that the value is a 128-bit value and returns it (panics if the value is the wrong size) pub fn unwrap_128(&self) -> Value128 { match self { - TinyWasmValue::Value128(v) => *v, + Self::Value128(v) => *v, _ => unreachable!("Expected Value128"), } } @@ -98,7 +98,7 @@ impl TinyWasmValue { /// Asserts that the value is a reference value and returns it (panics if the value is the wrong size) pub fn unwrap_ref(&self) -> ValueRef { match self { - TinyWasmValue::ValueRef(v) => *v, + Self::ValueRef(v) => *v, _ => unreachable!("Expected ValueRef"), } } @@ -125,15 +125,15 @@ impl TinyWasmValue { impl From<&WasmValue> for TinyWasmValue { fn from(value: &WasmValue) -> Self { match value { - WasmValue::I32(v) => TinyWasmValue::Value32(*v as u32), - WasmValue::I64(v) => TinyWasmValue::Value64(*v as u64), - WasmValue::F32(v) => TinyWasmValue::Value32(v.to_bits()), - WasmValue::F64(v) => TinyWasmValue::Value64(v.to_bits()), - WasmValue::RefExtern(v) => TinyWasmValue::ValueRef(v.addr()), - WasmValue::RefFunc(v) => TinyWasmValue::ValueRef(v.addr()), + WasmValue::I32(v) => Self::Value32(*v as u32), + WasmValue::I64(v) => Self::Value64(*v as u64), + WasmValue::F32(v) => Self::Value32(v.to_bits()), + WasmValue::F64(v) => Self::Value64(v.to_bits()), + WasmValue::RefExtern(v) => Self::ValueRef(v.addr()), + WasmValue::RefFunc(v) => Self::ValueRef(v.addr()), #[cfg(not(feature = "__simd"))] - WasmValue::V128(v) => TinyWasmValue::Value128(*v), + WasmValue::V128(v) => Self::Value128(*v), #[cfg(feature = "__simd")] WasmValue::V128(v) => TinyWasmValue::Value128(v.to_le_bytes().into()), @@ -143,12 +143,12 @@ impl From<&WasmValue> for TinyWasmValue { impl From for TinyWasmValue { fn from(value: WasmValue) -> Self { - TinyWasmValue::from(&value) + Self::from(&value) } } mod sealed { - #[allow(unreachable_pub)] + #[expect(unreachable_pub)] pub trait Sealed {} } @@ -160,7 +160,6 @@ pub(crate) trait InternalValue: sealed::Sealed + Into { fn stack_calculate(stack: &mut ValueStack, func: impl FnOnce(Self, Self) -> Result) -> Result<()> where Self: Sized; - #[allow(dead_code)] fn stack_calculate3(stack: &mut ValueStack, func: impl FnOnce(Self, Self, Self) -> Result) -> Result<()> where Self: Sized; @@ -266,8 +265,8 @@ macro_rules! impl_internalvalue { impl_internalvalue! { Value32, stack_32, locals_32, u32, u32, |v| v, |v| v Value64, stack_64, locals_64, u64, u64, |v| v, |v| v - Value32, stack_32, locals_32, u32, i32, |v| v as u32, |v: u32| v as i32 - Value64, stack_64, locals_64, u64, i64, |v| v as u64, |v| v as i64 + Value32, stack_32, locals_32, u32, i32, |v: i32| u32::from_ne_bytes(v.to_ne_bytes()), |v: u32| i32::from_ne_bytes(v.to_ne_bytes()) + Value64, stack_64, locals_64, u64, i64, |v: i64| u64::from_ne_bytes(v.to_ne_bytes()), |v: u64| i64::from_ne_bytes(v.to_ne_bytes()) Value32, stack_32, locals_32, u32, f32, f32::to_bits, f32::from_bits Value64, stack_64, locals_64, u64, f64, f64::to_bits, f64::from_bits ValueRef, stack_ref, locals_ref, ValueRef, ValueRef, |v| v, |v| v diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 9cb6c0a..b44a15e 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -75,7 +75,7 @@ extern crate alloc; // log for logging (optional). #[cfg(feature = "logging")] -#[allow(clippy::single_component_path_imports)] +#[expect(clippy::single_component_path_imports)] use log; // noop fallback if logging is disabled. diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index e32a2f3..790c313 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -1,8 +1,7 @@ use core::ffi::CStr; -use alloc::ffi::CString; use alloc::string::{String, ToString}; -use alloc::vec::Vec; +use alloc::{ffi::CString, format, vec::Vec}; use crate::{MemoryInstance, Result}; @@ -92,19 +91,19 @@ pub trait MemoryStringExt: MemoryRefLoad { /// Load a C-style string from memory fn load_cstr(&self, offset: usize, len: usize) -> Result<&CStr> { let bytes = self.load(offset, len)?; - CStr::from_bytes_with_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string())) + CStr::from_bytes_with_nul(bytes).map_err(|e| crate::Error::Other(format!("Invalid C-style string: {e}"))) } /// Load a C-style string from memory, stopping at the first nul byte fn load_cstr_until_nul(&self, offset: usize, max_len: usize) -> Result<&CStr> { let bytes = self.load(offset, max_len)?; - CStr::from_bytes_until_nul(bytes).map_err(|_| crate::Error::Other("Invalid C-style string".to_string())) + CStr::from_bytes_until_nul(bytes).map_err(|e| crate::Error::Other(format!("Invalid C-style string: {e}"))) } /// Load a UTF-8 string from memory fn load_string(&self, offset: usize, len: usize) -> Result { let bytes = self.load(offset, len)?; - String::from_utf8(bytes.to_vec()).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string())) + String::from_utf8(bytes.to_vec()).map_err(|e| crate::Error::Other(format!("Invalid UTF-8 string: {e}"))) } /// Load a C-style string from memory diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index d53654e..634fdfe 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -288,7 +288,9 @@ impl Store { })?; self.data.globals[addr as usize].value.get().unwrap_ref() } - _ => return Err(Error::UnsupportedFeature(format!("const expression other than ref: {item:?}"))), + ElementItem::Expr(item) => { + return Err(Error::UnsupportedFeature(format!("const expression other than ref: {item:?}"))); + } }; Ok(res) @@ -418,10 +420,10 @@ impl Store { /// Evaluate a constant expression that's either a i32 or a i64 as a global or a const instruction pub(crate) fn eval_size_const(&self, const_instr: tinywasm_types::ConstInstruction) -> Result { Ok(match const_instr { - ConstInstruction::I32Const(i) => i as i64, + ConstInstruction::I32Const(i) => i64::from(i), ConstInstruction::I64Const(i) => i, ConstInstruction::GlobalGet(addr) => match self.data.globals[addr as usize].value.get() { - TinyWasmValue::Value32(i) => i as i64, + TinyWasmValue::Value32(i) => i64::from(i), TinyWasmValue::Value64(i) => i as i64, o => return Err(Error::Other(format!("expected i32 or i64, got {o:?}"))), }, diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index f83de9b..bc4429d 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -159,8 +159,8 @@ pub(crate) enum TableElement { impl From> for TableElement { fn from(addr: Option) -> Self { match addr { - None => TableElement::Uninitialized, - Some(addr) => TableElement::Initialized(addr), + None => Self::Uninitialized, + Some(addr) => Self::Initialized(addr), } } } @@ -168,15 +168,15 @@ impl From> for TableElement { impl TableElement { pub(crate) fn addr(&self) -> Option { match self { - TableElement::Uninitialized => None, - TableElement::Initialized(addr) => Some(*addr), + Self::Uninitialized => None, + Self::Initialized(addr) => Some(*addr), } } pub(crate) fn map(self, f: impl FnOnce(Addr) -> Addr) -> Self { match self { - TableElement::Uninitialized => TableElement::Uninitialized, - TableElement::Initialized(addr) => TableElement::Initialized(f(addr)), + Self::Uninitialized => Self::Uninitialized, + Self::Initialized(addr) => Self::Initialized(f(addr)), } } } @@ -250,8 +250,7 @@ mod tests { let elem = table_instance.get(i); assert!( elem.is_ok() && matches!(elem.unwrap(), &TableElement::Initialized(_)), - "Element not initialized correctly at index {}", - i + "Element not initialized correctly at index {i}" ); } } diff --git a/crates/tinywasm/tests/host_func_signature_check.rs b/crates/tinywasm/tests/host_func_signature_check.rs index 698ad61..9389435 100644 --- a/crates/tinywasm/tests/host_func_signature_check.rs +++ b/crates/tinywasm/tests/host_func_signature_check.rs @@ -147,7 +147,7 @@ fn proxy_module(func_ty: &FuncType) -> Module { let params_text = join_surround(params, "param"); let params_gets: String = params.iter().enumerate().fold(String::new(), |mut acc, (num, _)| { - let _ = writeln!(acc, "(local.get {num})", num = num); + let _ = writeln!(acc, "(local.get {num})"); acc }); diff --git a/crates/tinywasm/tests/testsuite/mod.rs b/crates/tinywasm/tests/testsuite/mod.rs index c1ba46e..140b063 100644 --- a/crates/tinywasm/tests/testsuite/mod.rs +++ b/crates/tinywasm/tests/testsuite/mod.rs @@ -41,7 +41,7 @@ impl TestSuite { pub fn print_errors(&self) { for (group_name, group) in &self.0 { let tests = &group.tests; - for (test_name, test) in tests.iter() { + for (test_name, test) in tests { if let Err(e) = &test.result { eprintln!( "{} {} failed: {:?}", diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index d09efdb..c46a276 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -31,7 +31,7 @@ impl ModuleRegistry { } } fn register(&mut self, name: String, addr: ModuleInstanceAddr) { - log::debug!("registering module: {}", name); + log::debug!("registering module: {name}"); self.modules.insert(name.clone(), addr); self.last_module = Some(addr); @@ -99,22 +99,22 @@ impl TestSuite { }); let print_i32 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: i32| { - log::debug!("print_i32: {}", arg); + log::debug!("print_i32: {arg}"); Ok(()) }); let print_i64 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: i64| { - log::debug!("print_i64: {}", arg); + log::debug!("print_i64: {arg}"); Ok(()) }); let print_f32 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: f32| { - log::debug!("print_f32: {}", arg); + log::debug!("print_f32: {arg}"); Ok(()) }); let print_f64 = Extern::typed_func(|_ctx: tinywasm::FuncContext, arg: f64| { - log::debug!("print_f64: {}", arg); + log::debug!("print_f64: {arg}"); Ok(()) }); @@ -148,7 +148,7 @@ impl TestSuite { .define("spectest", "print_f64_f64", print_f64_f64)?; for (name, addr) in modules { - log::debug!("registering module: {}", name); + log::debug!("registering module: {name}"); imports.link_module(name, *addr)?; } @@ -158,7 +158,7 @@ impl TestSuite { pub fn run_files<'a>(&mut self, tests: impl IntoIterator>) -> Result<()> { tests.into_iter().for_each(|group| { let name = group.name(); - println!("running group: {}", name); + println!("running group: {name}"); if self.1.contains(&name.to_string()) { info!("skipping group: {name}"); self.test_group(&format!("{name} (skipped)"), name); @@ -217,7 +217,7 @@ impl TestSuite { .map_err(|e| eyre!("failed to parse wat module: {:?}", try_downcast_panic(e))); match &result { - Err(err) => debug!("failed to parse module: {:?}", err), + Err(err) => debug!("failed to parse module: {err:?}"), Ok((name, module)) => module_registry.update_last_module(module.id(), name.clone()), }; @@ -402,7 +402,7 @@ impl TestSuite { let args = convert_wastargs(invoke.args)?; let module = module_registry.get_idx(invoke.module); exec_fn_instance(module, &mut store, invoke.name, &args).map_err(|e| { - error!("failed to execute function: {:?}", e); + error!("failed to execute function: {e:?}"); e })?; Ok(()) @@ -413,7 +413,7 @@ impl TestSuite { } AssertReturn { span, exec, results } => { - info!("AssertReturn: {:?}", exec); + info!("AssertReturn: {exec:?}"); let expected = match convert_wastret(results.into_iter()) { Err(err) => { test_group.add_result( @@ -489,11 +489,11 @@ impl TestSuite { let invoke_name = invoke.name; let res: Result, _> = catch_unwind_silent(|| { - debug!("invoke: {:?}", invoke); + debug!("invoke: {invoke:?}"); let args = convert_wastargs(invoke.args)?; let module = module_registry.get_idx(invoke.module); let outcomes = exec_fn_instance(module, &mut store, invoke.name, &args).map_err(|e| { - error!("failed to execute function: {:?}", e); + error!("failed to execute function: {e:?}"); e })?; diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index 5e8558a..59013ad 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -74,7 +74,7 @@ pub fn encode_quote_wat(module: QuoteWat) -> (Option, Vec) { }; (module.id.map(|id| id.name().to_string()), wat.encode().expect("failed to encode module")) } - _ => unimplemented!("Not supported"), + QuoteWat::QuoteComponent(..) => unimplemented!("components are not supported"), } } @@ -96,7 +96,7 @@ fn wastarg2tinywasmvalue(arg: wast::WastArg) -> Result WasmValue::F32(f32::from_bits(f.bits)), F64(f) => WasmValue::F64(f64::from_bits(f.bits)), @@ -113,7 +113,7 @@ fn wastarg2tinywasmvalue(arg: wast::WastArg) -> Result bail!("unsupported arg type: refnull: {:?}", t), }, - v => bail!("unsupported arg type: {:?}", v), + RefHost(v) => bail!("unsupported arg type: RefHost"), }) } diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index fda8a86..b986c2a 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -7,6 +7,8 @@ license.workspace=true authors.workspace=true repository.workspace=true rust-version.workspace=true +keywords.workspace=true +categories.workspace=true [dependencies] log={workspace=true, optional=true} diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index cbc22ad..7d52049 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -34,10 +34,10 @@ pub enum TwasmError { impl Display for TwasmError { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { match self { - TwasmError::InvalidMagic => write!(f, "Invalid twasm: invalid magic number"), - TwasmError::InvalidVersion => write!(f, "Invalid twasm: invalid version"), - TwasmError::InvalidPadding => write!(f, "Invalid twasm: invalid padding"), - TwasmError::InvalidArchive(e) => write!(f, "Invalid twasm: {}", e), + Self::InvalidMagic => write!(f, "Invalid twasm: invalid magic number"), + Self::InvalidVersion => write!(f, "Invalid twasm: invalid version"), + Self::InvalidPadding => write!(f, "Invalid twasm: invalid padding"), + Self::InvalidArchive(e) => write!(f, "Invalid twasm: {e}"), } } } @@ -49,7 +49,7 @@ impl core::error::Error for TwasmError {} impl TinyWasmModule { /// Creates a `TinyWasmModule` from a slice of bytes. - pub fn from_twasm(wasm: &[u8]) -> Result { + pub fn from_twasm(wasm: &[u8]) -> Result { let len = validate_magic(wasm)?; postcard::from_bytes(&wasm[len..]).map_err(TwasmError::InvalidArchive) diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 9db5ed5..aa57dfa 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -2,7 +2,7 @@ use super::{FuncAddr, GlobalAddr, LabelAddr, LocalAddr, TableAddr, TypeAddr, Val use crate::{ConstIdx, DataAddr, ElemAddr, ExternAddr, MemAddr}; /// Represents a memory immediate in a WebAssembly memory instruction. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct MemoryArg([u8; 12]); diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 470c631..c34b705 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -119,7 +119,7 @@ pub struct TinyWasmModule { /// A WebAssembly External Kind. /// /// See -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub enum ExternalKind { /// A WebAssembly Function. @@ -191,14 +191,14 @@ impl ExternVal { /// The type of a WebAssembly Function. /// /// See -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct FuncType { pub params: Box<[ValType]>, pub results: Box<[ValType]>, } -#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct ValueCounts { pub c32: u32, @@ -207,7 +207,7 @@ pub struct ValueCounts { pub cref: u32, } -#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct ValueCountsSmall { pub c32: u16, @@ -218,7 +218,7 @@ pub struct ValueCountsSmall { impl<'a, T: IntoIterator> From for ValueCounts { fn from(types: T) -> Self { - let mut counts = ValueCounts::default(); + let mut counts = Self::default(); for ty in types { match ty { ValType::I32 | ValType::F32 => counts.c32 += 1, @@ -233,7 +233,7 @@ impl<'a, T: IntoIterator> From for ValueCounts { impl<'a, T: IntoIterator> From for ValueCountsSmall { fn from(types: T) -> Self { - let mut counts = ValueCountsSmall::default(); + let mut counts = Self::default(); for ty in types { match ty { ValType::I32 | ValType::F32 => counts.c32 += 1, @@ -256,14 +256,14 @@ pub struct WasmFunction { pub ty: FuncType, } -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct WasmFunctionData { pub v128_constants: Box<[i128]>, } /// A WebAssembly Module Export -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct Export { /// The name of the export. @@ -281,14 +281,14 @@ pub struct Global { pub init: ConstInstruction, } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct GlobalType { pub mutable: bool, pub ty: ValType, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct TableType { pub element_type: ValType, @@ -307,7 +307,7 @@ impl TableType { } /// Represents a memory's type. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct MemoryType { arch: MemoryArch, @@ -353,7 +353,7 @@ pub enum MemoryArch { I64, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct Import { pub module: Box, @@ -361,7 +361,7 @@ pub struct Import { pub kind: ImportKind, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub enum ImportKind { Function(TypeAddr), diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index a9498cc..22bb4eb 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -23,25 +23,25 @@ pub enum WasmValue { RefFunc(FuncRef), } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct ExternRef(Option); -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct FuncRef(Option); impl Debug for ExternRef { - fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self.0 { - Some(addr) => write!(f, "extern({:?})", addr), + Some(addr) => write!(f, "extern({addr:?})"), None => write!(f, "extern(null)"), } } } impl Debug for FuncRef { - fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self.0 { - Some(addr) => write!(f, "func({:?})", addr), + Some(addr) => write!(f, "func({addr:?})"), None => write!(f, "func(null)"), } } @@ -114,7 +114,7 @@ impl WasmValue { Self::F64(i) => ConstInstruction::F64Const(*i), Self::V128(i) => ConstInstruction::V128Const(*i), Self::RefFunc(i) => ConstInstruction::RefFunc(i.addr()), - _ => unimplemented!("no const_instr for {:?}", self), + Self::RefExtern(_) => unimplemented!("no const_instr for RefExtern"), } } @@ -220,15 +220,15 @@ impl WasmValue { fn cold() {} impl Debug for WasmValue { - fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - WasmValue::I32(i) => write!(f, "i32({i})"), - 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({i:?})"), - WasmValue::RefExtern(i) => write!(f, "ref({i:?})"), - WasmValue::RefFunc(i) => write!(f, "func({i:?})"), + Self::I32(i) => write!(f, "i32({i})"), + Self::I64(i) => write!(f, "i64({i})"), + Self::F32(i) => write!(f, "f32({i})"), + Self::F64(i) => write!(f, "f64({i})"), + Self::V128(i) => write!(f, "v128({i:?})"), + Self::RefExtern(i) => write!(f, "ref({i:?})"), + Self::RefFunc(i) => write!(f, "func({i:?})"), } } } @@ -278,7 +278,7 @@ impl ValType { #[doc(hidden)] #[inline] pub fn is_simd(&self) -> bool { - matches!(self, ValType::V128) + matches!(self, Self::V128) } }