diff --git a/.cargo/config.toml b/.cargo/config.toml index df52e1c..be912c8 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -7,3 +7,8 @@ test-wast="test --package tinywasm --test test-wast -- --enable " test-wast-release="test --package tinywasm --test test-wast --release -- --enable " generate-charts="run --package scripts --bin generate-charts --release" benchmark="bench -p benchmarks --bench" + +# # enable for linux perf +# [target.x86_64-unknown-linux-gnu] +# linker="/usr/bin/clang" +# rustflags=["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"] diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 3a85e19..50df853 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -1,7 +1,7 @@ # Benchmark results All benchmarks are run on a Ryzen 7 5800X with 32GB of RAM on Linux 6.6. -WebAssembly files are optimized using [wasm-opt](https://github.com/WebAssembly/binaryen), +WebAssembly files are optimized using [wasm-opt](https://github.com/WebAssembly/binaryen) (with the `--O3` flag) and the benchmark code is available in the `crates/benchmarks` folder. These are mainly preliminary benchmarks, and I will be rewriting the benchmarks to be more accurate and to test more features in the future. @@ -20,27 +20,29 @@ All WebAssembly files are compiled with the following settings: All runtimes are compiled with the following settings: -- `unsafe` features are enabled. - `opt-level` is set to 3, `lto` is set to `thin`, `codegen-units` is set to 1. +- No CPU-specific optimizations are used as AVX2 can reduce performance by more than 50% on some CPUs. +- Default runtime settings are used ## Versions -- `tinywasm`: `0.4.1` +- `tinywasm`: `0.7.0` - `wasmi`: `0.31.2` -- `wasmer`: `4.2.5` +- `wasmer`: `4.3.0` ## Results -| Benchmark | Native | TinyWasm\* | Wasmi | Wasmer (Single Pass) | -| ------------ | -------- | ---------- | --------- | -------------------- | -| `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` | +> Results include the time it takes to parse/compile the WebAssembly file and execute the function. -_\* Uses tinywasm's internal module format instead of `wasm`. It takes ~5.7ms to parse and validate `tinywasm.wasm`._ +| Benchmark | Native | TinyWasm | Wasmi | Wasmer (Single Pass) | +| ------------ | -------- | ---------- | --------- | -------------------- | +| `fib` | `6.33µs` | ` 19.18µs` | `18.26µs` | ` 51.20µs` | +| `fib-rec` | `0.27ms` | ` 16.09ms` | ` 5.08ms` | ` 0.47ms` | +| `argon2id` | `0.50ms` | ` 89.52ms` | `45.31ms` | ` 4.74ms` | +| `selfhosted` | `0.05ms` | ` 7.93ms` | ` 7.54ms` | `512.45ms` | -_\*\* essentially instant as it gets computed at compile time._ +> Note that parsing is still pretty slow, especially for the `selfhosted` benchmark, taking up `~6ms` for TinyWasm. +> This can be improved by using the `archive` feature, which pre-parses the WebAssembly file into tinywasm's custom bytecode format. ### Fib diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c51b74..f6539a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ 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). +## [0.7.0] - 2024-05-15 + +**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.6.0...v0.7.0 + +### Changed + +- Remove all unsafe code +- Refactor interpreter loop +- Optimize Call-frames +- Remove unnecessary reference counter data from store + +## [0.6.1] - 2024-05-10 + +**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.6.0...v0.6.1 + +### Changed + +- Switched back to the original `wasmparser` crate, which recently added support for `no_std` +- Performance improvements +- Updated dependencies + ## [0.6.0] - 2024-03-27 **All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.5.0...v0.6.0 diff --git a/Cargo.lock b/Cargo.lock index 61a20c8..0703613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "const-random", "once_cell", "version_check", "zerocopy", @@ -71,11 +70,60 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "argh" @@ -96,7 +144,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", ] [[package]] @@ -128,9 +176,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -147,6 +195,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64ct" version = "1.6.0" @@ -219,9 +273,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecheck" @@ -284,6 +338,18 @@ name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +dependencies = [ + "serde", +] + +[[package]] +name = "bytesize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +dependencies = [ + "serde", +] [[package]] name = "cast" @@ -293,9 +359,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.90" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg-if" @@ -305,16 +371,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -341,33 +407,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half", + "half 2.4.1", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.61", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "color-eyre" @@ -403,30 +484,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "const-cstr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" - -[[package]] -name = "const-random" -version = "0.1.18" +name = "colorchoice" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] -name = "const-random-macro" -version = "0.1.16" +name = "const-cstr" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] +checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" [[package]] name = "core-foundation" @@ -680,14 +747,38 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + [[package]] name = "darling" version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.8", + "darling_macro 0.20.8", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", ] [[package]] @@ -700,7 +791,18 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", ] [[package]] @@ -709,9 +811,9 @@ version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ - "darling_core", + "darling_core 0.20.8", "quote", - "syn 2.0.55", + "syn 2.0.61", ] [[package]] @@ -721,7 +823,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -738,6 +840,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -779,11 +912,20 @@ dependencies = [ "libloading", ] +[[package]] +name = "document-features" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] + [[package]] name = "downcast-rs" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dwrote" @@ -797,6 +939,12 @@ dependencies = [ "wio", ] +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "dynasm" version = "1.2.3" @@ -825,9 +973,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "enum-iterator" @@ -864,10 +1012,10 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ - "darling", + "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", ] [[package]] @@ -889,6 +1037,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "eyre" version = "0.6.12" @@ -905,6 +1063,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "fdeflate" version = "0.3.4" @@ -914,11 +1078,23 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -976,11 +1152,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "freetype" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efc8599a3078adf8edeb86c71e9f8fa7d88af5ca31e806a867756081f90f5d83" +checksum = "5a440748e063798e4893ceb877151e84acef9bea9a8c6800645cf3f1b3a7806e" dependencies = [ "freetype-sys", "libc", @@ -988,9 +1173,9 @@ dependencies = [ [[package]] name = "freetype-sys" -version = "0.19.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66ee28c39a43d89fbed8b4798fb4ba56722cfd2b5af81f9326c27614ba88ecd5" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" dependencies = [ "cc", "libc", @@ -1024,13 +1209,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1075,9 +1262,15 @@ dependencies = [ [[package]] name = "half" -version = "2.4.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -1094,19 +1287,31 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "humantime" version = "2.1.0" @@ -1142,6 +1347,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.24.9" @@ -1170,6 +1385,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -1179,7 +1395,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", + "serde", ] [[package]] @@ -1199,6 +1416,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.10.5" @@ -1243,9 +1466,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" @@ -1254,7 +1477,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -1265,20 +1488,31 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.5.0", "libc", - "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 = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1310,9 +1544,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -1334,9 +1568,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -1359,9 +1593,9 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1401,15 +1635,15 @@ checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1425,9 +1659,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathfinder_geometry" @@ -1447,11 +1681,17 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pkg-config" @@ -1554,9 +1794,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1583,9 +1823,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1631,11 +1871,20 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -1679,9 +1928,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "region" @@ -1754,7 +2003,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.55", + "syn 2.0.61", "walkdir", ] @@ -1771,9 +2020,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" @@ -1784,11 +2033,24 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1799,6 +2061,31 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schemars" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6e7ed6919cb46507fb01ff1654309219f62b4d603822501b0b80d42f6f21ef" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "schemars_derive" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185f2b7aa7e02d418e453790dde16890256bbd2bcd04b7dc5348811052b53f49" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.61", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1821,21 +2108,24 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "self_cell" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "serde" -version = "1.0.197" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -1851,28 +2141,71 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half 1.8.3", + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.2.6", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1939,6 +2272,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.5.0" @@ -1958,9 +2297,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.55" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -1973,12 +2312,35 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -1990,22 +2352,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", ] [[package]] @@ -2018,15 +2380,6 @@ 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" @@ -2054,7 +2407,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tinywasm" -version = "0.5.0" +version = "0.7.0" dependencies = [ "eyre", "libm", @@ -2066,28 +2419,28 @@ dependencies = [ "tinywasm-parser", "tinywasm-types", "wasm-testsuite", - "wast 202.0.0", + "wast 207.0.0", ] [[package]] name = "tinywasm-cli" -version = "0.5.0" +version = "0.7.0" dependencies = [ "argh", "color-eyre", "log", "pretty_env_logger", "tinywasm", - "wast 202.0.0", + "wast 207.0.0", ] [[package]] name = "tinywasm-parser" -version = "0.5.0" +version = "0.7.0" dependencies = [ "log", "tinywasm-types", - "tinywasm-wasmparser", + "wasmparser 0.207.0", ] [[package]] @@ -2102,7 +2455,7 @@ dependencies = [ [[package]] name = "tinywasm-types" -version = "0.5.0" +version = "0.7.0" dependencies = [ "bytecheck 0.7.0", "log", @@ -2110,16 +2463,62 @@ dependencies = [ ] [[package]] -name = "tinywasm-wasmparser" -version = "0.202.0" +name = "toml" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b02b9566a3c8b98f29dd9821d0584de1ad0290cdc9132296f66fbf4015001bb1" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.12", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "ahash 0.8.11", - "bitflags 2.5.0", - "hashbrown 0.14.3", "indexmap 2.2.6", - "semver", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.8", ] [[package]] @@ -2141,7 +2540,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", ] [[package]] @@ -2187,17 +2586,56 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" @@ -2254,7 +2692,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", "wasm-bindgen-shared", ] @@ -2276,7 +2714,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2298,25 +2736,25 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.202.0" +version = "0.207.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a" +checksum = "d996306fb3aeaee0d9157adbe2f670df0236caf19f6728b221e92d0f27b3fe17" dependencies = [ "leb128", ] [[package]] name = "wasm-testsuite" -version = "0.2.2" +version = "0.4.0" dependencies = [ "rust-embed", ] [[package]] name = "wasmer" -version = "4.2.6" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c15724dc25d1ee57962334aea8e41ade2675e5ea2ac6b8d42da6051b0face66" +checksum = "6d6beae0c56cd5c26fe29aa613c6637bde6747a782ec3e3ed362c2dda615e701" dependencies = [ "bytes", "cfg-if", @@ -2344,9 +2782,9 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "4.2.6" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a7f3b3a96f8d844c25e2c032af9572306dd63fa93dc17bcca4c5458ac569bd" +checksum = "df65b299475df71947607b24528e5a34e0fc42ad84350c242e591cbf74a6bc37" dependencies = [ "backtrace", "bytes", @@ -2365,15 +2803,16 @@ dependencies = [ "thiserror", "wasmer-types", "wasmer-vm", - "wasmparser", + "wasmparser 0.121.2", "winapi", + "xxhash-rust", ] [[package]] name = "wasmer-compiler-cranelift" -version = "4.2.6" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "102e2c5bacac69495c4025767e2fa26797ffb27f242dccb7cf57d9cefd944386" +checksum = "42867bde8e7bda9419c9b08a20eb58ed8e493fea5ba3cb920f602df826cb7795" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -2390,9 +2829,9 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "4.2.6" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2071db9b993508dac72d12f7a9372e0c095fbdc173e0009c4b75886bed4a855e" +checksum = "8ffcce77a325738b1b64e1ec7e141b62b0706ecd7cfbf70227aedc9a8c9c1bd6" dependencies = [ "byteorder", "dynasm", @@ -2407,11 +2846,33 @@ dependencies = [ "wasmer-types", ] +[[package]] +name = "wasmer-config" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a0f70c177b1c5062cfe0f5308c3317751796fef9403c22a0cd7b4cacd4ccd8" +dependencies = [ + "anyhow", + "bytesize", + "derive_builder", + "hex", + "indexmap 2.2.6", + "schemars", + "semver", + "serde", + "serde_cbor", + "serde_json", + "serde_yaml", + "thiserror", + "toml 0.8.12", + "url", +] + [[package]] name = "wasmer-derive" -version = "4.2.6" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea737fa08f95d6abc4459f42a70a9833e8974b814e74971d77ef473814f4d4c" +checksum = "231826965de8fe7bfba02b3b8adac3304ca8b7fea92dc6129e8330e020aa6b45" dependencies = [ "proc-macro-error", "proc-macro2", @@ -2421,25 +2882,30 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.2.6" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0689110e291b0f07fc665f2824e5ff81df120848e8a9acfbf1a9bf7990773f9" +checksum = "9782e1a5a28ae2c5165cdfc1aa5ce2aa89b20f745ae3f3a3974f6500849cc31a" dependencies = [ "bytecheck 0.6.12", "enum-iterator", "enumset", + "getrandom", + "hex", "indexmap 1.9.3", "more-asserts", "rkyv", + "sha2", "target-lexicon", "thiserror", + "webc", + "xxhash-rust", ] [[package]] name = "wasmer-vm" -version = "4.2.6" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd41f822a1ac4242d478754e8ceba2806a00ea5072803622e1fe91e8e28b2a1" +checksum = "9f143d07733ac0832f42c7acb1b0abf22f00e38505eb605951f06af382970f80" dependencies = [ "backtrace", "cc", @@ -2505,11 +2971,24 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.207.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19bb9f8ab07616da582ef8adb24c54f1424c7ec876720b7da9db8ec0626c92c" +dependencies = [ + "ahash 0.8.11", + "bitflags 2.5.0", + "hashbrown 0.14.5", + "indexmap 2.2.6", + "semver", +] + [[package]] name = "wasmparser-nostd" -version = "0.100.1" +version = "0.100.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" dependencies = [ "indexmap-nostd", ] @@ -2528,15 +3007,15 @@ dependencies = [ [[package]] name = "wast" -version = "202.0.0" +version = "207.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbcb11204515c953c9b42ede0a46a1c5e17f82af05c4fae201a8efff1b0f4fe" +checksum = "0e40be9fd494bfa501309487d2dc0b3f229be6842464ecbdc54eac2679c84c93" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.202.0", + "wasm-encoder 0.207.0", ] [[package]] @@ -2558,6 +3037,36 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webc" +version = "6.0.0-alpha8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf53893f8df356f1305446c1bc59c4082cb592f39ffcae0a2f10bd8ed100bb9" +dependencies = [ + "anyhow", + "base64", + "bytes", + "cfg-if", + "clap", + "document-features", + "flate2", + "indexmap 1.9.3", + "libc", + "once_cell", + "semver", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "shared-buffer", + "tar", + "tempfile", + "thiserror", + "toml 0.7.8", + "url", + "wasmer-config", +] + [[package]] name = "weezl" version = "0.1.8" @@ -2582,11 +3091,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2601,7 +3110,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets", ] [[package]] @@ -2623,50 +3132,30 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -2676,15 +3165,9 @@ checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -2694,15 +3177,15 @@ checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_gnu" -version = "0.52.4" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -2712,15 +3195,9 @@ checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" [[package]] name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -2730,27 +3207,15 @@ checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2760,15 +3225,27 @@ checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +dependencies = [ + "memchr", +] [[package]] name = "wio" @@ -2788,6 +3265,23 @@ dependencies = [ "tap", ] +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + [[package]] name = "yeslogic-fontconfig-sys" version = "3.2.0" @@ -2802,20 +3296,20 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.61", ] diff --git a/Cargo.toml b/Cargo.toml index bb3275c..58ded7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ panic="abort" inherits="release" [workspace.package] -version="0.5.0" +version="0.7.0" edition="2021" license="MIT OR Apache-2.0" authors=["Henry Gressmann "] @@ -28,8 +28,8 @@ test=false [dev-dependencies] color-eyre="0.6" -tinywasm={path="crates/tinywasm", features=["unsafe"]} -wat={version="1.0"} +tinywasm={path="crates/tinywasm"} +wat={version="1"} pretty_env_logger="0.5" [profile.bench] diff --git a/README.md b/README.md index 38000f4..bf36393 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

TinyWasm

- A tiny WebAssembly Runtime written in Rust + A tiny WebAssembly Runtime written in safe Rust
@@ -12,9 +12,9 @@ ## Why TinyWasm? -- **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 other WebAssembly Runtimes, with minimal external dependencies. -- **Lightweight**: TinyWasm is easy to integrate and has a low call overhead, making it suitable for scripting and embedding. +- **Tiny**: TinyWasm is designed to be as small as possible without significantly compromising performance or functionality (< 4000 LLOC). +- **Portable**: TinyWasm runs on any platform that Rust can target, including `no_std`, with minimal external dependencies. +- **Safe**: No unsafe code is used in the runtime (`rkyv` which uses unsafe code can be used for serialization, but it is optional). ## Status @@ -65,8 +65,6 @@ $ tinywasm-cli --help 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. With all these features disabled, TinyWasm only depends on `core`, `alloc` ,and `libm` and can be used in `no_std` environments. Since `libm` is not as performant as the compiler's math intrinsics, it is recommended to use the `std` feature if possible (at least [for now](https://github.com/rust-lang/rfcs/issues/2505)), especially on wasm32 targets. diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index a374713..e137a92 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -5,10 +5,10 @@ edition.workspace=true [dependencies] criterion={version="0.5", features=["html_reports"]} -tinywasm={path="../crates/tinywasm", features=["unsafe"]} -wat={version="1.0"} +tinywasm={path="../crates/tinywasm"} +wat={version="1"} wasmi={version="0.31", features=["std"]} -wasmer={version="4.2", features=["cranelift", "singlepass"]} +wasmer={version="4.3", features=["cranelift", "singlepass"]} argon2={version="0.5"} [[bench]] diff --git a/benchmarks/benches/argon2id.rs b/benchmarks/benches/argon2id.rs index 3046dee..0cf5af4 100644 --- a/benchmarks/benches/argon2id.rs +++ b/benchmarks/benches/argon2id.rs @@ -1,9 +1,8 @@ mod util; use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use util::wasm_to_twasm; -fn run_tinywasm(twasm: &[u8], params: (i32, i32, i32), name: &str) { - let (mut store, instance) = util::tinywasm(twasm); +fn run_tinywasm(wasm: &[u8], params: (i32, i32, i32), name: &str) { + let (mut store, instance) = util::tinywasm(wasm); let argon2 = instance.exported_func::<(i32, i32, i32), i32>(&store, name).expect("exported_func"); argon2.call(&mut store, params).expect("call"); } @@ -38,7 +37,6 @@ fn run_native(params: (i32, i32, i32)) { 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); let mut group = c.benchmark_group("argon2id"); @@ -46,7 +44,7 @@ fn criterion_benchmark(c: &mut Criterion) { group.sample_size(10); 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("tinywasm", |b| b.iter(|| run_tinywasm(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"))); } diff --git a/benchmarks/benches/fibonacci.rs b/benchmarks/benches/fibonacci.rs index b391285..15c09ac 100644 --- a/benchmarks/benches/fibonacci.rs +++ b/benchmarks/benches/fibonacci.rs @@ -1,9 +1,8 @@ mod util; use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use util::wasm_to_twasm; -fn run_tinywasm(twasm: &[u8], iterations: i32, name: &str) { - let (mut store, instance) = util::tinywasm(twasm); +fn run_tinywasm(wasm: &[u8], iterations: i32, name: &str) { + let (mut store, instance) = util::tinywasm(wasm); let fib = instance.exported_func::(&store, name).expect("exported_func"); fib.call(&mut store, iterations).expect("call"); } @@ -47,12 +46,10 @@ fn run_native_recursive(n: i32) -> i32 { const FIBONACCI: &[u8] = include_bytes!("../../examples/rust/out/fibonacci.wasm"); fn criterion_benchmark(c: &mut Criterion) { - let twasm = wasm_to_twasm(FIBONACCI); - { 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("tinywasm", |b| b.iter(|| run_tinywasm(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"))); } @@ -61,7 +58,7 @@ fn criterion_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("fibonacci-recursive"); 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("tinywasm", |b| b.iter(|| run_tinywasm(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/benchmarks/benches/selfhosted.rs b/benchmarks/benches/selfhosted.rs index 02d44ac..4241eea 100644 --- a/benchmarks/benches/selfhosted.rs +++ b/benchmarks/benches/selfhosted.rs @@ -1,6 +1,5 @@ mod util; -use crate::util::twasm_to_module; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; fn run_native() { use tinywasm::*; @@ -15,7 +14,7 @@ fn run_native() { fn run_tinywasm(twasm: &[u8]) { use tinywasm::*; - let module = twasm_to_module(twasm); + let module = Module::parse_bytes(twasm).expect("Module::parse_bytes"); let mut store = Store::default(); let mut imports = Imports::default(); imports.define("env", "printi32", Extern::typed_func(|_: FuncContext<'_>, _: i32| Ok(()))).expect("define"); @@ -55,14 +54,15 @@ 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::parse_wasm(TINYWASM))); + group.bench_function("tinywasm", |b| { + b.iter(|| tinywasm::Module::parse_bytes(black_box(TINYWASM)).expect("parse")) + }); } { - 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("tinywasm", |b| b.iter(|| run_tinywasm(TINYWASM))); group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } diff --git a/benchmarks/benches/util/mod.rs b/benchmarks/benches/util/mod.rs index f75ce66..2961046 100644 --- a/benchmarks/benches/util/mod.rs +++ b/benchmarks/benches/util/mod.rs @@ -1,26 +1,8 @@ #![allow(dead_code)] -use tinywasm::{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"); - res.serialize_twasm().to_vec() -} - -#[inline] -pub fn twasm_to_module(twasm: &[u8]) -> tinywasm::Module { - unsafe { TinyWasmModule::from_twasm_unchecked(twasm) }.into() -} - -pub fn tinywasm(twasm: &[u8]) -> (tinywasm::Store, tinywasm::ModuleInstance) { +pub fn tinywasm(wasm: &[u8]) -> (tinywasm::Store, tinywasm::ModuleInstance) { use tinywasm::*; - let module = twasm_to_module(twasm); + let module = Module::parse_bytes(wasm).expect("Module::parse_bytes"); let mut store = Store::default(); let imports = Imports::default(); let instance = ModuleInstance::instantiate(&mut store, module, Some(imports)).expect("instantiate"); diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 7d54ced..4aa9cd0 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,12 +14,12 @@ path="src/bin.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tinywasm={version="0.5.0", path="../tinywasm", features=["std", "parser"]} +tinywasm={version="0.7.0", path="../tinywasm", features=["std", "parser"]} argh="0.1" color-eyre={version="0.6", default-features=false} log="0.4" pretty_env_logger="0.5" -wast={version="202.0", optional=true} +wast={version="207.0", optional=true} [features] default=["wat"] diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index 6ba7461..206a9ed 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -8,12 +8,11 @@ authors.workspace=true repository.workspace=true [dependencies] -# fork of wasmparser with no_std support, see https://github.com/bytecodealliance/wasmtime/issues/3495 -wasmparser={version="0.202.0", package="tinywasm-wasmparser", default-features=false} +wasmparser={version="0.207", default-features=false, features=["validate"]} log={version="0.4", optional=true} -tinywasm-types={version="0.5.0", path="../types", default-features=false} +tinywasm-types={version="0.7.0", path="../types", default-features=false} [features] default=["std", "logging"] logging=["log"] -std=["tinywasm-types/std"] +std=["tinywasm-types/std", "wasmparser/std"] diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index ccdc308..001d7cc 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -7,8 +7,7 @@ use wasmparser::{FuncValidator, OperatorsReader, ValidatorResources}; pub(crate) fn convert_module_elements<'a, T: IntoIterator>>>( elements: T, ) -> Result> { - let elements = elements.into_iter().map(|element| convert_module_element(element?)).collect::>>()?; - Ok(elements) + elements.into_iter().map(|element| convert_module_element(element?)).collect::>>() } pub(crate) fn convert_module_element(element: wasmparser::Element<'_>) -> Result { @@ -47,8 +46,7 @@ pub(crate) fn convert_module_element(element: wasmparser::Element<'_>) -> Result pub(crate) fn convert_module_data_sections<'a, T: IntoIterator>>>( data_sections: T, ) -> Result> { - let data_sections = data_sections.into_iter().map(|data| convert_module_data(data?)).collect::>>()?; - Ok(data_sections) + data_sections.into_iter().map(|data| convert_module_data(data?)).collect::>>() } pub(crate) fn convert_module_data(data: wasmparser::Data<'_>) -> Result { @@ -68,8 +66,7 @@ pub(crate) fn convert_module_data(data: wasmparser::Data<'_>) -> Result>>>( imports: T, ) -> Result> { - let imports = imports.into_iter().map(|import| convert_module_import(import?)).collect::>>()?; - Ok(imports) + imports.into_iter().map(|import| convert_module_import(import?)).collect::>>() } pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result { @@ -80,8 +77,15 @@ pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result ImportKind::Function(ty), wasmparser::TypeRef::Table(ty) => ImportKind::Table(TableType { element_type: convert_reftype(&ty.element_type), - size_initial: ty.initial, - size_max: ty.maximum, + size_initial: ty.initial.try_into().map_err(|_| { + crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", ty.initial)) + })?, + size_max: match ty.maximum { + Some(max) => Some(max.try_into().map_err(|_| { + crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {}", max)) + })?), + None => None, + }, }), wasmparser::TypeRef::Memory(ty) => ImportKind::Memory(convert_module_memory(ty)?), wasmparser::TypeRef::Global(ty) => { @@ -97,10 +101,7 @@ pub(crate) fn convert_module_import(import: wasmparser::Import<'_>) -> Result>>( memory_types: T, ) -> Result> { - let memory_type = - memory_types.into_iter().map(|memory| convert_module_memory(memory?)).collect::>>()?; - - Ok(memory_type) + memory_types.into_iter().map(|memory| convert_module_memory(memory?)).collect::>>() } pub(crate) fn convert_module_memory(memory: wasmparser::MemoryType) -> Result { @@ -117,17 +118,27 @@ pub(crate) fn convert_module_memory(memory: wasmparser::MemoryType) -> Result>>>( table_types: T, ) -> Result> { - let table_type = table_types.into_iter().map(|table| convert_module_table(table?)).collect::>>()?; - Ok(table_type) + table_types.into_iter().map(|table| convert_module_table(table?)).collect::>>() } 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 }) + let size_initial = table.ty.initial.try_into().map_err(|_| { + crate::ParseError::UnsupportedOperator(format!("Table size initial is too large: {}", table.ty.initial)) + })?; + + let size_max = match table.ty.maximum { + Some(max) => Some( + max.try_into() + .map_err(|_| crate::ParseError::UnsupportedOperator(format!("Table size max is too large: {}", max)))?, + ), + None => None, + }; + + Ok(TableType { element_type: convert_reftype(&table.ty.element_type), size_initial, size_max }) } -pub(crate) fn convert_module_globals<'a, T: IntoIterator>>>( - globals: T, +pub(crate) fn convert_module_globals( + globals: wasmparser::SectionLimited<'_, wasmparser::Global<'_>>, ) -> Result> { let globals = globals .into_iter() @@ -135,7 +146,6 @@ pub(crate) fn convert_module_globals<'a, T: IntoIterator>>()?; @@ -158,7 +168,7 @@ pub(crate) fn convert_module_export(export: wasmparser::Export<'_>) -> Result, - mut validator: FuncValidator, + validator: &mut FuncValidator, ) -> Result { let locals_reader = func.get_locals_reader()?; let count = locals_reader.get_count(); @@ -173,7 +183,7 @@ pub(crate) fn convert_module_code( } } - let body = process_operators(Some(&mut validator), &func)?; + let body = process_operators(Some(validator), func)?; let locals = locals.into_boxed_slice(); Ok((body, locals)) } @@ -187,12 +197,8 @@ pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result )); } let ty = types.next().unwrap().unwrap_func(); - - let params = - ty.params().iter().map(|p| Ok(convert_valtype(p))).collect::>>()?.into_boxed_slice(); - - let results = - ty.results().iter().map(|p| Ok(convert_valtype(p))).collect::>>()?.into_boxed_slice(); + let params = ty.params().iter().map(convert_valtype).collect::>().into_boxed_slice(); + let results = ty.results().iter().map(convert_valtype).collect::>().into_boxed_slice(); Ok(FuncType { params, results }) } @@ -214,14 +220,13 @@ pub(crate) fn convert_reftype(reftype: &wasmparser::RefType) -> ValType { } pub(crate) fn convert_valtype(valtype: &wasmparser::ValType) -> ValType { - use wasmparser::ValType::*; match valtype { - I32 => ValType::I32, - I64 => ValType::I64, - F32 => ValType::F32, - F64 => ValType::F64, - Ref(r) => convert_reftype(r), - V128 => unimplemented!("128-bit values are not supported yet"), + wasmparser::ValType::I32 => ValType::I32, + wasmparser::ValType::I64 => ValType::I64, + wasmparser::ValType::F32 => ValType::F32, + wasmparser::ValType::F64 => ValType::F64, + wasmparser::ValType::Ref(r) => convert_reftype(r), + wasmparser::ValType::V128 => unimplemented!("128-bit values are not supported yet"), } } @@ -236,18 +241,15 @@ pub(crate) fn process_const_operators(ops: OperatorsReader<'_>) -> Result= 2); assert!(matches!(ops[ops.len() - 1], wasmparser::Operator::End)); - process_const_operator(ops[ops.len() - 2].clone()) -} -pub(crate) fn process_const_operator(op: wasmparser::Operator<'_>) -> Result { - match op { - 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)), + match &ops[ops.len() - 2] { + 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)), wasmparser::Operator::F32Const { value } => Ok(ConstInstruction::F32Const(f32::from_bits(value.bits()))), wasmparser::Operator::F64Const { value } => Ok(ConstInstruction::F64Const(f64::from_bits(value.bits()))), - wasmparser::Operator::GlobalGet { global_index } => Ok(ConstInstruction::GlobalGet(global_index)), + wasmparser::Operator::GlobalGet { global_index } => Ok(ConstInstruction::GlobalGet(*global_index)), op => Err(crate::ParseError::UnsupportedOperator(format!("Unsupported const instruction: {:?}", op))), } } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 5fb3c48..dd10f4d 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -35,7 +35,7 @@ use alloc::{string::ToString, vec::Vec}; pub use error::*; use module::ModuleReader; use tinywasm_types::WasmFunction; -use wasmparser::{Validator, WasmFeatures}; +use wasmparser::{Validator, WasmFeaturesInflated}; pub use tinywasm_types::TinyWasmModule; @@ -50,7 +50,7 @@ impl Parser { } fn create_validator(&self) -> Validator { - let features = WasmFeatures { + let features = WasmFeaturesInflated { bulk_memory: true, floats: true, multi_value: true, @@ -73,8 +73,10 @@ impl Parser { tail_call: false, threads: false, multi_memory: false, // should be working mostly + custom_page_sizes: false, + shared_everything_threads: false, }; - Validator::new_with_features(features) + Validator::new_with_features(features.into()) } /// Parse a [`TinyWasmModule`] from bytes diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 8414c17..1cd5ed5 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -2,12 +2,14 @@ use crate::log::debug; use crate::{conversion, ParseError, Result}; use alloc::{boxed::Box, format, vec::Vec}; use tinywasm_types::{Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, ValType}; -use wasmparser::{Payload, Validator}; +use wasmparser::{FuncValidatorAllocations, Payload, Validator}; pub(crate) type Code = (Box<[Instruction]>, Box<[ValType]>); #[derive(Default)] pub(crate) struct ModuleReader { + func_validator_allocations: Option, + pub(crate) version: Option, pub(crate) start_func: Option, pub(crate) func_types: Vec, @@ -129,8 +131,9 @@ impl ModuleReader { CodeSectionEntry(function) => { debug!("Found code section entry"); let v = validator.code_section_entry(&function)?; - let func_validator = v.into_validator(Default::default()); - self.code.push(conversion::convert_module_code(function, func_validator)?); + let mut func_validator = v.into_validator(self.func_validator_allocations.take().unwrap_or_default()); + self.code.push(conversion::convert_module_code(function, &mut func_validator)?); + self.func_validator_allocations = Some(func_validator.into_allocations()); } ImportSection(reader) => { if !self.imports.is_empty() { diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index c5743b8..332380c 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::{BlockArgsPacked, Instruction}; +use tinywasm_types::Instruction; use wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources}; struct ValidateThenVisit<'a, T, U>(T, &'a mut U); @@ -29,12 +29,11 @@ where pub(crate) fn process_operators( validator: Option<&mut FuncValidator>, - body: &FunctionBody<'_>, + body: FunctionBody<'_>, ) -> Result> { let mut reader = body.get_operators_reader()?; let remaining = reader.get_binary_reader().bytes_remaining(); let mut builder = FunctionBuilder::new(remaining); - if let Some(validator) = validator { while !reader.eof() { let validate = validator.visitor(reader.original_position()); @@ -53,6 +52,7 @@ pub(crate) fn process_operators( macro_rules! define_operands { ($($name:ident, $instr:expr),*) => { $( + #[inline(always)] fn $name(&mut self) -> Self::Output { self.instructions.push($instr); Ok(()) @@ -64,6 +64,7 @@ macro_rules! define_operands { macro_rules! define_primitive_operands { ($($name:ident, $instr:expr, $ty:ty),*) => { $( + #[inline(always)] fn $name(&mut self, arg: $ty) -> Self::Output { self.instructions.push($instr(arg)); Ok(()) @@ -72,6 +73,7 @@ macro_rules! define_primitive_operands { }; ($($name:ident, $instr:expr, $ty:ty, $ty2:ty),*) => { $( + #[inline(always)] fn $name(&mut self, arg: $ty, arg2: $ty) -> Self::Output { self.instructions.push($instr(arg, arg2)); Ok(()) @@ -83,6 +85,7 @@ macro_rules! define_primitive_operands { macro_rules! define_mem_operands { ($($name:ident, $instr:ident),*) => { $( + #[inline(always)] fn $name(&mut self, mem_arg: wasmparser::MemArg) -> Self::Output { let arg = convert_memarg(mem_arg); self.instructions.push(Instruction::$instr { @@ -102,7 +105,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(256) } + Self { instructions: Vec::with_capacity(instr_capacity / 4), label_ptrs: Vec::with_capacity(256) } } #[cold] @@ -110,19 +113,34 @@ impl FunctionBuilder { Err(crate::ParseError::UnsupportedOperator(format!("Unsupported instruction: {:?}", name))) } - #[inline] + #[inline(always)] fn visit(&mut self, op: Instruction) -> Result<()> { self.instructions.push(op); Ok(()) } } +macro_rules! impl_visit_operator { + ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { + $(impl_visit_operator!(@@$proposal $op $({ $($arg: $argty),* })? => $visit);)* + }; + + (@@mvp $($rest:tt)* ) => {}; + (@@reference_types $($rest:tt)* ) => {}; + (@@sign_extension $($rest:tt)* ) => {}; + (@@saturating_float_to_int $($rest:tt)* ) => {}; + (@@bulk_memory $($rest:tt)* ) => {}; + (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident) => { + #[cold] + fn $visit(&mut self $($(,$arg: $argty)*)?) -> Result<()>{ + self.unsupported(stringify!($visit)) + } + }; +} + impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { type Output = Result<()>; - - fn visit_default(&mut self, op: &str) -> Self::Output { - self.unsupported(op) - } + wasmparser::for_each_operator!(impl_visit_operator); define_primitive_operands! { visit_br, Instruction::Br, u32, @@ -148,7 +166,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_i64_load16_u, I64Load16U, visit_i64_load32_s, I64Load32S, visit_i64_load32_u, I64Load32U, - visit_i32_store, I32Store, + // visit_i32_store, I32Store, custom implementation visit_i64_store, I64Store, visit_f32_store, F32Store, visit_f64_store, F64Store, @@ -307,38 +325,57 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U } - fn visit_local_get(&mut self, idx: u32) -> Self::Output { - if let Some(instruction) = self.instructions.last_mut() { - match instruction { - Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), - Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), - Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), - _ => return self.visit(Instruction::LocalGet(idx)), - }; - Ok(()) - } else { - self.visit(Instruction::LocalGet(idx)) + #[inline(always)] + fn visit_i32_store(&mut self, memarg: wasmparser::MemArg) -> Self::Output { + let arg = convert_memarg(memarg); + let i32store = Instruction::I32Store { offset: arg.offset, mem_addr: arg.mem_addr }; + + if self.instructions.len() < 3 || arg.mem_addr > 0xFF || arg.offset > 0xFFFF_FFFF { + return self.visit(i32store); + } + + match self.instructions[self.instructions.len() - 2..] { + [Instruction::LocalGet(a), Instruction::I32Const(b)] => { + self.instructions.pop(); + self.instructions.pop(); + self.visit(Instruction::I32StoreLocal { + local: a, + const_i32: b, + offset: arg.offset as u32, + mem_addr: arg.mem_addr as u8, + }) + } + _ => self.visit(i32store), } } + #[inline(always)] + fn visit_local_get(&mut self, idx: u32) -> Self::Output { + let Some(instruction) = self.instructions.last_mut() else { + return self.visit(Instruction::LocalGet(idx)); + }; + + match instruction { + Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), + Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), + Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), + _ => return self.visit(Instruction::LocalGet(idx)), + }; + + Ok(()) + } + + #[inline(always)] fn visit_local_set(&mut self, idx: u32) -> Self::Output { self.visit(Instruction::LocalSet(idx)) - // if let Some(instruction) = self.instructions.last_mut() { - // match instruction { - // // Needs more testing, seems to make performance worse - // // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), - // _ => return self.visit(Instruction::LocalSet(idx)), - // }; - // // Ok(()) - // } else { - // self.visit(Instruction::LocalSet(idx)) - // } } + #[inline(always)] fn visit_local_tee(&mut self, idx: u32) -> Self::Output { self.visit(Instruction::LocalTee(idx)) } + #[inline(always)] fn visit_i64_rotl(&mut self) -> Self::Output { if self.instructions.len() < 2 { return self.visit(Instruction::I64Rotl); @@ -354,51 +391,55 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } } + #[inline(always)] fn visit_i32_add(&mut self) -> Self::Output { - self.visit(Instruction::I32Add) - // if self.instructions.len() < 2 { - // return 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), + } } + #[inline(always)] 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)) } + #[inline(always)] 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)) } + #[inline(always)] fn visit_if(&mut self, ty: wasmparser::BlockType) -> Self::Output { self.label_ptrs.push(self.instructions.len()); - self.visit(Instruction::If(BlockArgsPacked::new(convert_blocktype(ty)), 0, 0)) + self.visit(Instruction::If(convert_blocktype(ty).into(), 0, 0)) } + #[inline(always)] fn visit_else(&mut self) -> Self::Output { self.label_ptrs.push(self.instructions.len()); self.visit(Instruction::Else(0)) } + #[inline(always)] fn visit_end(&mut self) -> Self::Output { let Some(label_pointer) = self.label_ptrs.pop() else { return self.visit(Instruction::Return); }; let current_instr_ptr = self.instructions.len(); - - match self.instructions[label_pointer] { - Instruction::Else(ref mut else_instr_end_offset) => { + match self.instructions.get_mut(label_pointer) { + Some(Instruction::Else(else_instr_end_offset)) => { *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"); @@ -414,7 +455,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { 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 { + let Instruction::If(_, else_offset, end_offset) = if_instruction else { return Err(error()); }; @@ -426,23 +467,22 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { .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) => { + Some(Instruction::Block(_, end_offset)) + | Some(Instruction::Loop(_, end_offset)) + | Some(Instruction::If(_, _, end_offset)) => { *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( - "Expected to end a block, but the last label was not a block".to_string(), - )) + unreachable!("Expected to end a block, but the last label was not a block") } }; self.visit(Instruction::EndBlockFrame) } + #[inline(always)] fn visit_br_table(&mut self, targets: wasmparser::BrTable<'_>) -> Self::Output { let def = targets.default(); let instrs = targets @@ -451,32 +491,36 @@ 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() as u32)]).chain(instrs)); - + self.instructions.extend(([Instruction::BrTable(def, instrs.len() as u32)].into_iter()).chain(instrs)); Ok(()) } + #[inline(always)] fn visit_call(&mut self, idx: u32) -> Self::Output { self.visit(Instruction::Call(idx)) } + #[inline(always)] fn visit_call_indirect(&mut self, ty: u32, table: u32, _table_byte: u8) -> Self::Output { self.visit(Instruction::CallIndirect(ty, table)) } + #[inline(always)] fn visit_memory_size(&mut self, mem: u32, mem_byte: u8) -> Self::Output { self.visit(Instruction::MemorySize(mem, mem_byte)) } + #[inline(always)] fn visit_memory_grow(&mut self, mem: u32, mem_byte: u8) -> Self::Output { self.visit(Instruction::MemoryGrow(mem, mem_byte)) } + #[inline(always)] fn visit_f32_const(&mut self, val: wasmparser::Ieee32) -> Self::Output { self.visit(Instruction::F32Const(f32::from_bits(val.bits()))) } + #[inline(always)] fn visit_f64_const(&mut self, val: wasmparser::Ieee64) -> Self::Output { self.visit(Instruction::F64Const(f64::from_bits(val.bits()))) } @@ -493,24 +537,29 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { visit_data_drop, Instruction::DataDrop, u32 } + #[inline(always)] fn visit_elem_drop(&mut self, _elem_index: u32) -> Self::Output { self.unsupported("elem_drop") } + #[inline(always)] 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 + #[inline(always)] fn visit_ref_null(&mut self, ty: wasmparser::HeapType) -> Self::Output { self.visit(Instruction::RefNull(convert_heaptype(ty))) } + #[inline(always)] fn visit_ref_is_null(&mut self) -> Self::Output { self.visit(Instruction::RefIsNull) } + #[inline(always)] fn visit_typed_select(&mut self, ty: wasmparser::ValType) -> Self::Output { self.visit(Instruction::Select(Some(convert_valtype(&ty)))) } diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index 0af1d0c..ca6b723 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -14,13 +14,13 @@ path="src/lib.rs" [dependencies] _log={version="0.4", optional=true, package="log"} -tinywasm-parser={version="0.5.0", path="../parser", default-features=false, optional=true} -tinywasm-types={version="0.5.0", path="../types", default-features=false} +tinywasm-parser={version="0.7.0", path="../parser", default-features=false, optional=true} +tinywasm-types={version="0.7.0", path="../types", default-features=false} libm={version="0.2", default-features=false} [dev-dependencies] wasm-testsuite={path="../wasm-testsuite"} -wast={version="202.0"} +wast={version="207.0"} owo-colors={version="4.0"} eyre={version="0.6"} serde_json={version="1.0"} @@ -32,8 +32,8 @@ default=["std", "parser", "logging", "archive"] logging=["_log", "tinywasm-parser?/logging", "tinywasm-types/logging"] std=["tinywasm-parser?/std", "tinywasm-types/std"] parser=["tinywasm-parser"] -unsafe=["tinywasm-types/unsafe"] archive=["tinywasm-types/archive"] +nightly=[] [[test]] name="test-mvp" diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index d7f7ca1..95b7cc0 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -49,7 +49,7 @@ impl FuncHandle { return Err(Error::Other("Type mismatch".into())); } - let func_inst = store.get_func(self.addr as usize)?; + let func_inst = store.get_func(self.addr)?; let wasm_func = match &func_inst.func { Function::Host(host_func) => { let func = &host_func.clone().func; diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 67ac360..f24ed73 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -15,7 +15,7 @@ pub enum Function { /// A host function Host(Rc), - /// A function defined in WebAssembly + /// A pointer to a WebAssembly function Wasm(Rc), } @@ -226,11 +226,8 @@ pub struct Imports { } pub(crate) enum ResolvedExtern { - // already in the store - Store(S), - - // needs to be added to the store, provided value - Extern(V), + Store(S), // already in the store + Extern(V), // needs to be added to the store, provided value } pub(crate) struct ResolvedImports { @@ -391,17 +388,17 @@ impl Imports { match (val, &import.kind) { (ExternVal::Global(global_addr), ImportKind::Global(ty)) => { - let global = store.get_global(global_addr as usize)?; - Self::compare_types(import, &global.borrow().ty, ty)?; + let global = store.get_global(global_addr)?; + Self::compare_types(import, &global.ty, ty)?; imports.globals.push(global_addr); } (ExternVal::Table(table_addr), ImportKind::Table(ty)) => { - let table = store.get_table(table_addr as usize)?; + let table = store.get_table(table_addr)?; Self::compare_table_types(import, &table.borrow().kind, ty)?; imports.tables.push(table_addr); } (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => { - let mem = store.get_mem(memory_addr as usize)?; + let mem = store.get_mem(memory_addr)?; let (size, kind) = { let mem = mem.borrow(); (mem.page_count(), mem.kind) @@ -410,7 +407,7 @@ impl Imports { imports.memories.push(memory_addr); } (ExternVal::Func(func_addr), ImportKind::Function(ty)) => { - let func = store.get_func(func_addr as usize)?; + let func = store.get_func(func_addr)?; let import_func_type = module .data .func_types diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 3fc4fe0..8402302 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -2,7 +2,7 @@ use alloc::{boxed::Box, format, rc::Rc, string::ToString}; use tinywasm_types::*; use crate::func::{FromWasmValueTuple, IntoWasmValueTuple}; -use crate::{log, Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store}; +use crate::{Error, FuncHandle, FuncHandleTyped, Imports, MemoryRef, MemoryRefMut, Module, Result, Store}; /// An instanciated WebAssembly module /// @@ -61,13 +61,9 @@ impl ModuleInstance { // don't need to create a auxiliary frame etc. let idx = store.next_module_instance_idx(); - log::info!("Instantiating module at index {}", idx); - let imports = imports.unwrap_or_default(); - - let mut addrs = imports.link(store, &module, idx)?; + let mut addrs = imports.unwrap_or_default().link(store, &module, idx)?; let data = module.data; - // TODO: check if the compiler correctly optimizes this to prevent wasted allocations addrs.funcs.extend(store.init_funcs(data.funcs.into(), idx)?); addrs.tables.extend(store.init_tables(data.table_types.into(), idx)?); addrs.memories.extend(store.init_memories(data.memory_types.into(), idx)?); @@ -94,7 +90,7 @@ impl ModuleInstance { }; let instance = ModuleInstance::new(instance); - store.add_instance(instance.clone())?; + store.add_instance(instance.clone()); if let Some(trap) = elem_trapped { return Err(trap.into()); @@ -110,15 +106,14 @@ impl ModuleInstance { /// Get a export by name pub fn export_addr(&self, name: &str) -> Option { let exports = self.0.exports.iter().find(|e| e.name == name.into())?; - let kind = exports.kind.clone(); - let addr = match kind { + let addr = match exports.kind { ExternalKind::Func => self.0.func_addrs.get(exports.index as usize)?, ExternalKind::Table => self.0.table_addrs.get(exports.index as usize)?, ExternalKind::Memory => self.0.mem_addrs.get(exports.index as usize)?, ExternalKind::Global => self.0.global_addrs.get(exports.index as usize)?, }; - Some(ExternVal::new(kind, *addr)) + Some(ExternVal::new(exports.kind, *addr)) } #[inline] @@ -137,37 +132,37 @@ impl ModuleInstance { } // resolve a function address to the global store address - #[inline] + #[inline(always)] 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") + self.0.func_addrs[addr as usize] } // resolve a table address to the global store address - #[inline] + #[inline(always)] 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") + self.0.table_addrs[addr as usize] } // resolve a memory address to the global store address - #[inline] + #[inline(always)] 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") + self.0.mem_addrs[addr as usize] } // resolve a data address to the global store address - #[inline] + #[inline(always)] 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") + self.0.data_addrs[addr as usize] } // resolve a memory address to the global store address - #[inline] + #[inline(always)] 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") + self.0.elem_addrs[addr as usize] } // resolve a global address to the global store address - #[inline] + #[inline(always)] pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> GlobalAddr { self.0.global_addrs[addr as usize] } @@ -183,7 +178,7 @@ impl ModuleInstance { return Err(Error::Other(format!("Export is not a function: {}", name))); }; - let func_inst = store.get_func(func_addr as usize)?; + let func_inst = store.get_func(func_addr)?; let ty = func_inst.func.ty(); Ok(FuncHandle { addr: func_addr, module_addr: self.id(), name: Some(name.to_string()), ty: ty.clone() }) @@ -205,8 +200,8 @@ impl ModuleInstance { let ExternVal::Memory(mem_addr) = export else { return Err(Error::Other(format!("Export is not a memory: {}", name))); }; - let mem = self.memory(store, mem_addr)?; - Ok(mem) + + self.memory(store, mem_addr) } /// Get an exported memory by name @@ -215,21 +210,19 @@ impl ModuleInstance { let ExternVal::Memory(mem_addr) = export else { return Err(Error::Other(format!("Export is not a memory: {}", name))); }; - let mem = self.memory_mut(store, mem_addr)?; - Ok(mem) + + self.memory_mut(store, mem_addr) } /// Get a memory by address pub fn memory<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result> { - let addr = self.resolve_mem_addr(addr); - let mem = store.get_mem(addr as usize)?; + let mem = store.get_mem(self.resolve_mem_addr(addr))?; Ok(MemoryRef { instance: mem.borrow() }) } /// Get a memory by address (mutable) pub fn memory_mut<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result> { - let addr = self.resolve_mem_addr(addr); - let mem = store.get_mem(addr as usize)?; + let mem = store.get_mem(self.resolve_mem_addr(addr))?; Ok(MemoryRefMut { instance: mem.borrow_mut() }) } @@ -257,7 +250,7 @@ impl ModuleInstance { }; let func_addr = self.0.func_addrs.get(func_index as usize).expect("No func addr for start func, this is a bug"); - let func_inst = store.get_func(*func_addr as usize)?; + let func_inst = store.get_func(*func_addr)?; let ty = func_inst.func.ty(); Ok(Some(FuncHandle { module_addr: self.id(), addr: *func_addr, ty: ty.clone(), name: None })) diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index e2d57fc..2e9fece 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -3,9 +3,10 @@ no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables)) ))] +#![allow(unexpected_cfgs, clippy::reserve_after_initialization)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] #![cfg_attr(nightly, feature(error_in_core))] -#![cfg_attr(not(feature = "unsafe"), deny(unsafe_code))] +#![forbid(unsafe_code)] //! A tiny WebAssembly Runtime written in Rust //! @@ -22,8 +23,6 @@ //! 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 //! //! 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 @@ -93,15 +92,13 @@ pub(crate) mod log { } mod error; -pub use { - error::*, - func::{FuncHandle, FuncHandleTyped}, - imports::*, - instance::ModuleInstance, - module::Module, - reference::*, - store::*, -}; +pub use error::*; +pub use func::{FuncHandle, FuncHandleTyped}; +pub use imports::*; +pub use instance::ModuleInstance; +pub use module::Module; +pub use reference::*; +pub use store::*; mod func; mod imports; diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index 6713a42..f3acc49 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -2,7 +2,6 @@ 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; @@ -142,9 +141,9 @@ impl MemoryStringExt for MemoryRef<'_> {} impl MemoryStringExt for MemoryRefMut<'_> {} /// A reference to a global instance -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct GlobalRef { - pub(crate) instance: Rc>, + pub(crate) instance: RefCell, } impl GlobalRef { diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index 30f34fc..8a092c0 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -10,12 +10,13 @@ // 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) => {{ + ($cf:ident, $stack:ident, $module:ident, $store:ident, $break_to_relative:ident) => {{ if $cf.break_to(*$break_to_relative, &mut $stack.values, &mut $stack.blocks).is_none() { - match $stack.call_stack.is_empty() { - true => return Ok(ExecResult::Return), - false => return Ok(ExecResult::Call), + if $stack.call_stack.is_empty() { + return Ok(()); } + + call!($cf, $stack, $module, $store) } }}; } @@ -27,34 +28,35 @@ macro_rules! mem_load { }}; ($load_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ - let (mem_addr, offset) = $arg; + #[inline(always)] + fn mem_load_inner( + store: &Store, + module: &crate::ModuleInstance, + stack: &mut crate::runtime::Stack, + mem_addr: tinywasm_types::MemAddr, + offset: u64, + ) -> Result<()> { + let mem = store.get_mem(module.resolve_mem_addr(mem_addr))?; + let addr: usize = match offset.checked_add(stack.values.pop()?.into()).map(|a| a.try_into()) { + Some(Ok(a)) => a, + _ => { + cold(); + return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { + offset: offset as usize, + len: core::mem::size_of::<$load_type>(), + max: mem.borrow().max_pages(), + })); + } + }; + + const LEN: usize = core::mem::size_of::<$load_type>(); + let val = mem.borrow().load_as::(addr)?; + stack.values.push((val as $target_type).into()); + Ok(()) + } - 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 = offset.checked_add(addr).ok_or_else(|| { - cold(); - Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: *offset as usize, - len: core::mem::size_of::<$load_type>(), - max: mem_ref.max_pages(), - }) - })?; - - let addr: usize = addr.try_into().ok().ok_or_else(|| { - cold(); - Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: *offset as usize, - len: core::mem::size_of::<$load_type>(), - max: mem_ref.max_pages(), - }) - })?; - - const LEN: usize = core::mem::size_of::<$load_type>(); - let val = mem_ref.load_as::(addr)?; - $stack.values.push((val as $target_type).into()); + let (mem_addr, offset) = $arg; + mem_load_inner($store, &$module, $stack, *mem_addr, *offset)?; }}; } @@ -65,12 +67,24 @@ macro_rules! mem_store { }}; ($store_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ + #[inline(always)] + fn mem_store_inner( + store: &Store, + module: &crate::ModuleInstance, + stack: &mut crate::runtime::Stack, + mem_addr: tinywasm_types::MemAddr, + offset: u64, + ) -> Result<()> { + let mem = store.get_mem(module.resolve_mem_addr(mem_addr))?; + 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)?; + Ok(()) + } + let (mem_addr, offset) = $arg; - let mem = $store.get_mem($module.resolve_mem_addr(*mem_addr) as usize)?; - let val: $store_type = $stack.values.pop()?.into(); - let val = val.to_le_bytes(); - let addr: u64 = $stack.values.pop()?.into(); - mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?; + mem_store_inner($store, &$module, $stack, *mem_addr, *offset)?; }}; } @@ -79,8 +93,8 @@ macro_rules! mem_store { /// for a specific conversion, which are then used in the actual conversion. /// Rust sadly doesn't have wrapping casts for floats yet, maybe never. /// Alternatively, https://crates.io/crates/az could be used for this but -/// it's not worth the dependency. -#[rustfmt::skip] +/// 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)}; @@ -96,20 +110,17 @@ macro_rules! float_min_max { /// Convert a value on the stack macro_rules! conv { - ($from:ty, $to:ty, $stack:ident) => {{ - $stack.values.replace_top(|v| { - let a: $from = v.into(); - (a as $to).into() - }); - }}; + ($from:ty, $to:ty, $stack:ident) => { + $stack.values.replace_top(|v| (<$from>::from(v) as $to).into())? + }; } /// Convert a value on the stack with error checking macro_rules! checked_conv_float { // Direct conversion with error checking (two types) - ($from:tt, $to:tt, $stack:ident) => {{ + ($from:tt, $to:tt, $stack:ident) => { checked_conv_float!($from, $to, $to, $stack) - }}; + }; // Conversion with an intermediate unsigned type and error checking (three types) ($from:tt, $intermediate:tt, $to:tt, $stack:ident) => {{ let (min, max) = float_min_max!($from, $intermediate); @@ -129,68 +140,96 @@ macro_rules! checked_conv_float { /// Compare two values on the stack macro_rules! comp { - ($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()); - }}; + ($op:tt, $to:ty, $stack:ident) => { + $stack.values.calculate(|a, b| { + ((<$to>::from(a) $op <$to>::from(b)) as i32).into() + })? + }; } /// Compare a value on the stack to zero macro_rules! comp_zero { - ($op:tt, $ty:ty, $stack:ident) => {{ - let a: $ty = $stack.values.pop()?.into(); - $stack.values.push(((a $op 0) as i32).into()); - }}; + ($op:tt, $ty:ty, $stack:ident) => { + $stack.values.replace_top(|v| { + ((<$ty>::from(v) $op 0) as i32).into() + })? + }; } /// Apply an arithmetic method to two values on the stack macro_rules! arithmetic { - ($op:ident, $to:ty, $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()); - }}; + ($op:ident, $to:ty, $stack:ident) => { + $stack.values.calculate(|a, b| { + (<$to>::from(a).$op(<$to>::from(b)) as $to).into() + })? + }; // also allow operators such as +, - - ($op:tt, $ty:ty, $stack:ident) => {{ - let b: $ty = $stack.values.pop()?.into(); - let a: $ty = $stack.values.pop()?.into(); - $stack.values.push((a $op b).into()); - }}; + ($op:tt, $ty:ty, $stack:ident) => { + $stack.values.calculate(|a, b| { + ((<$ty>::from(a) $op <$ty>::from(b)) as $ty).into() + })? + }; } /// Apply an arithmetic method to a single value on the stack macro_rules! arithmetic_single { - ($op:ident, $ty:ty, $stack:ident) => {{ - let a: $ty = $stack.values.pop()?.into(); - $stack.values.push((a.$op() as $ty).into()); - }}; + ($op:ident, $ty:ty, $stack:ident) => { + arithmetic_single!($op, $ty, $ty, $stack) + }; - ($op:ident, $from:ty, $to:ty, $stack:ident) => {{ - let a: $from = $stack.values.pop()?.into(); - $stack.values.push((a.$op() as $to).into()); - }}; + ($op:ident, $from:ty, $to:ty, $stack:ident) => { + $stack.values.replace_top(|v| (<$from>::from(v).$op() as $to).into())? + }; } /// Apply an arithmetic operation to two values on the stack with error checking macro_rules! checked_int_arithmetic { - ($op:ident, $to:ty, $stack:ident) => {{ - let b: $to = $stack.values.pop()?.into(); - let a: $to = $stack.values.pop()?.into(); + ($op:ident, $to:ty, $stack:ident) => { + $stack.values.calculate_trap(|a, b| { + let a: $to = a.into(); + let b: $to = b.into(); + + if unlikely(b == 0) { + return Err(Error::Trap(crate::Trap::DivisionByZero)); + } + + let result = a.$op(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow))?; + Ok((result).into()) + })? + }; +} - if unlikely(b == 0) { - return Err(Error::Trap(crate::Trap::DivisionByZero)); +macro_rules! call { + ($cf:expr, $stack:expr, $module:expr, $store:expr) => {{ + let old = $cf.block_ptr; + $cf = $stack.call_stack.pop()?; + + if old > $cf.block_ptr { + $stack.blocks.truncate(old); + } + + if $cf.module_addr != $module.id() { + $module.swap_with($cf.module_addr, $store); } - let result = a.$op(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow))?; - $stack.values.push((result).into()); + continue; }}; } +macro_rules! skip { + ($code:expr) => { + match $code { + Ok(_) => continue, + Err(e) => return Err(e), + } + }; +} + pub(super) use arithmetic; pub(super) use arithmetic_single; pub(super) use break_to; +pub(super) use call; pub(super) use checked_conv_float; pub(super) use checked_int_arithmetic; pub(super) use comp; @@ -199,3 +238,4 @@ pub(super) use conv; pub(super) use float_min_max; pub(super) use mem_load; pub(super) use mem_store; +pub(super) use skip; diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 2f0328b..d01faeb 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -1,12 +1,12 @@ use alloc::format; -use alloc::{string::ToString, vec::Vec}; +use alloc::string::ToString; use core::ops::{BitAnd, BitOr, BitXor, Neg}; -use tinywasm_types::{ElementKind, ValType}; +use tinywasm_types::{BlockArgs, ElementKind, ValType}; -use super::{InterpreterRuntime, Stack}; +use super::{InterpreterRuntime, RawWasmValue, Stack}; use crate::runtime::{BlockFrame, BlockType, CallFrame}; -use crate::{cold, log, unlikely}; -use crate::{Error, FuncContext, ModuleInstance, Result, Store, Trap}; +use crate::{cold, unlikely, ModuleInstance}; +use crate::{Error, FuncContext, Result, Store, Trap}; mod macros; mod traits; @@ -20,681 +20,756 @@ mod no_std_floats; use no_std_floats::NoStdFloatExt; impl InterpreterRuntime { - // #[inline(always)] // a small 2-3% performance improvement in some cases pub(crate) fn exec(&self, store: &mut Store, stack: &mut Stack) -> Result<()> { - // The current call frame, gets updated inside of exec_one let mut cf = stack.call_stack.pop()?; - - // The function to execute, gets updated from ExecResult::Call - let mut current_module = store.get_module_instance_raw(cf.func_instance.1); + let mut module = store.get_module_instance_raw(cf.module_addr); loop { - 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); + use tinywasm_types::Instruction::*; + match cf.fetch_instr() { + Nop => cold(), + Unreachable => self.exec_unreachable()?, + Drop => stack.values.pop().map(|_| ())?, + Select(_valtype) => self.exec_select(stack)?, + + Call(v) => skip!(self.exec_call(*v, store, stack, &mut cf, &mut module)), + CallIndirect(ty, table) => { + skip!(self.exec_call_indirect(*ty, *table, store, stack, &mut cf, &mut module)) + } + If(args, el, end) => skip!(self.exec_if((*args).into(), *el, *end, stack, &mut cf, &mut module)), + Loop(args, end) => self.enter_block(stack, cf.instr_ptr, *end, BlockType::Loop, args, &module), + Block(args, end) => self.enter_block(stack, cf.instr_ptr, *end, BlockType::Block, args, &module), + + Br(v) => break_to!(cf, stack, module, store, v), + BrIf(v) => { + if i32::from(stack.values.pop()?) != 0 { + break_to!(cf, stack, module, store, v); + } + } + BrTable(default, len) => { + let start = cf.instr_ptr + 1; + let end = start + *len as usize; + if end > cf.instructions().len() { + return Err(Error::Other(format!( + "br_table out of bounds: {} >= {}", + end, + cf.instructions().len() + ))); } - // 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); + let idx: i32 = stack.values.pop()?.into(); + match cf.instructions()[start..end].get(idx as usize) { + None => break_to!(cf, stack, module, store, default), + Some(BrLabel(to)) => break_to!(cf, stack, module, store, to), + _ => return Err(Error::Other("br_table with invalid label".to_string())), } } - // return from the function - Ok(ExecResult::Return) => return Ok(()), - - // continue to the next instruction and increment the instruction pointer - Ok(ExecResult::Ok) => cf.instr_ptr += 1, - - // trap the program - 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); + Return => match stack.call_stack.is_empty() { + true => return Ok(()), + false => call!(cf, stack, module, store), + }, + + // We're essentially using else as a EndBlockFrame instruction for if blocks + Else(end_offset) => self.exec_else(stack, *end_offset, &mut cf)?, + + // remove the label from the label stack + EndBlockFrame => self.exec_end_block(stack)?, + + LocalGet(local_index) => self.exec_local_get(*local_index, stack, &cf), + LocalSet(local_index) => self.exec_local_set(*local_index, stack, &mut cf)?, + LocalTee(local_index) => self.exec_local_tee(*local_index, stack, &mut cf)?, + + GlobalGet(global_index) => self.exec_global_get(*global_index, stack, store, &module)?, + GlobalSet(global_index) => self.exec_global_set(*global_index, stack, store, &module)?, + + I32Const(val) => self.exec_const(*val, stack), + I64Const(val) => self.exec_const(*val, stack), + F32Const(val) => self.exec_const(*val, stack), + F64Const(val) => self.exec_const(*val, stack), + + MemorySize(addr, byte) => self.exec_memory_size(*addr, *byte, stack, store, &module)?, + MemoryGrow(addr, byte) => self.exec_memory_grow(*addr, *byte, stack, store, &module)?, + + // Bulk memory operations + MemoryCopy(from, to) => self.exec_memory_copy(*from, *to, stack, store, &module)?, + MemoryFill(addr) => self.exec_memory_fill(*addr, stack, store, &module)?, + MemoryInit(data_idx, mem_idx) => self.exec_memory_init(*data_idx, *mem_idx, stack, store, &module)?, + DataDrop(data_index) => store.get_data_mut(module.resolve_data_addr(*data_index))?.drop(), + + 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), + + I32Eq => comp!(==, i32, stack), + I64Eq => comp!(==, i64, stack), + F32Eq => comp!(==, f32, stack), + F64Eq => comp!(==, f64, stack), + + I32Ne => comp!(!=, i32, stack), + I64Ne => comp!(!=, i64, stack), + F32Ne => comp!(!=, f32, stack), + F64Ne => comp!(!=, f64, stack), + + I32LtS => comp!(<, i32, stack), + I64LtS => comp!(<, i64, 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!(<=, u32, stack), + I64LeU => comp!(<=, u64, stack), + F32Le => comp!(<=, f32, stack), + F64Le => comp!(<=, f64, stack), + + I32GeS => comp!(>=, i32, stack), + I64GeS => comp!(>=, i64, 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!(>, u32, stack), + I64GtU => comp!(>, u64, stack), + F32Gt => comp!(>, f32, stack), + F64Gt => comp!(>, f64, stack), + + I64Add => arithmetic!(wrapping_add, i64, stack), + I32Add => arithmetic!(wrapping_add, i32, stack), + F32Add => arithmetic!(+, f32, stack), + F64Add => arithmetic!(+, f64, stack), + + I32Sub => arithmetic!(wrapping_sub, i32, stack), + I64Sub => arithmetic!(wrapping_sub, i64, stack), + F32Sub => arithmetic!(-, f32, stack), + F64Sub => arithmetic!(-, f64, stack), + + F32Div => arithmetic!(/, f32, stack), + F64Div => arithmetic!(/, f64, stack), + + I32Mul => arithmetic!(wrapping_mul, i32, stack), + I64Mul => arithmetic!(wrapping_mul, i64, stack), + F32Mul => arithmetic!(*, f32, stack), + F64Mul => arithmetic!(*, f64, stack), + + // 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, 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, u32, stack), + I64RemU => checked_int_arithmetic!(checked_wrapping_rem, u64, stack), + + I32And => arithmetic!(bitand, i32, stack), + I64And => arithmetic!(bitand, i64, stack), + I32Or => arithmetic!(bitor, i32, stack), + I64Or => arithmetic!(bitor, i64, stack), + I32Xor => arithmetic!(bitxor, i32, stack), + I64Xor => arithmetic!(bitxor, i64, stack), + I32Shl => arithmetic!(wasm_shl, i32, stack), + I64Shl => arithmetic!(wasm_shl, i64, stack), + I32ShrS => arithmetic!(wasm_shr, i32, stack), + I64ShrS => arithmetic!(wasm_shr, 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), + I64Rotr => arithmetic!(wasm_rotr, i64, stack), + + I32Clz => arithmetic_single!(leading_zeros, i32, stack), + I64Clz => arithmetic_single!(leading_zeros, i64, stack), + I32Ctz => arithmetic_single!(trailing_zeros, i32, stack), + I64Ctz => arithmetic_single!(trailing_zeros, i64, stack), + I32Popcnt => arithmetic_single!(count_ones, i32, stack), + I64Popcnt => arithmetic_single!(count_ones, i64, stack), + + F32ConvertI32S => conv!(i32, f32, stack), + F32ConvertI64S => conv!(i64, f32, stack), + F64ConvertI32S => conv!(i32, f64, stack), + F64ConvertI64S => conv!(i64, f64, 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), + + F32DemoteF64 => conv!(f64, f32, stack), + F64PromoteF32 => conv!(f32, f64, stack), + + F32Abs => arithmetic_single!(abs, f32, stack), + F64Abs => arithmetic_single!(abs, f64, stack), + F32Neg => arithmetic_single!(neg, f32, stack), + F64Neg => arithmetic_single!(neg, f64, stack), + F32Ceil => arithmetic_single!(ceil, f32, stack), + F64Ceil => arithmetic_single!(ceil, f64, stack), + F32Floor => arithmetic_single!(floor, f32, stack), + F64Floor => arithmetic_single!(floor, f64, stack), + F32Trunc => arithmetic_single!(trunc, f32, stack), + F64Trunc => arithmetic_single!(trunc, 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!(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 => {} + + // unsigned versions of these are a bit broken atm + I32TruncF32S => checked_conv_float!(f32, i32, stack), + I32TruncF64S => checked_conv_float!(f64, i32, stack), + I32TruncF32U => checked_conv_float!(f32, u32, i32, stack), + I32TruncF64U => checked_conv_float!(f64, u32, i32, stack), + I64TruncF32S => checked_conv_float!(f32, i64, stack), + I64TruncF64S => checked_conv_float!(f64, i64, stack), + I64TruncF32U => checked_conv_float!(f32, u64, i64, stack), + I64TruncF64U => checked_conv_float!(f64, u64, i64, stack), + + TableGet(table_idx) => self.exec_table_get(*table_idx, stack, store, &module)?, + TableSet(table_idx) => self.exec_table_set(*table_idx, stack, store, &module)?, + TableSize(table_idx) => self.exec_table_size(*table_idx, stack, store, &module)?, + TableInit(table_idx, elem_idx) => self.exec_table_init(*elem_idx, *table_idx, store, &module)?, + + I32TruncSatF32S => arithmetic_single!(trunc, f32, i32, stack), + I32TruncSatF32U => arithmetic_single!(trunc, f32, u32, stack), + I32TruncSatF64S => arithmetic_single!(trunc, f64, i32, stack), + I32TruncSatF64U => arithmetic_single!(trunc, f64, u32, stack), + I64TruncSatF32S => arithmetic_single!(trunc, f32, i64, stack), + I64TruncSatF32U => arithmetic_single!(trunc, f32, u64, stack), + I64TruncSatF64S => arithmetic_single!(trunc, f64, i64, stack), + I64TruncSatF64U => arithmetic_single!(trunc, f64, u64, stack), + + // custom instructions + LocalGet2(a, b) => self.exec_local_get2(*a, *b, stack, &cf), + LocalGet3(a, b, c) => self.exec_local_get3(*a, *b, *c, stack, &cf), + LocalTeeGet(a, b) => self.exec_local_tee_get(*a, *b, stack, &mut cf), + LocalGetSet(a, b) => self.exec_local_get_set(*a, *b, &mut cf), + I64XorConstRotl(rotate_by) => self.exec_i64_xor_const_rotl(*rotate_by, stack)?, + I32LocalGetConstAdd(local, val) => self.exec_i32_local_get_const_add(*local, *val, stack, &cf), + I32StoreLocal { local, const_i32: consti32, offset, mem_addr } => { + self.exec_i32_store_local(*local, *consti32, *offset, *mem_addr, &cf, store, &module)? } - } + i => { + cold(); + return Err(Error::UnsupportedFeature(format!("unimplemented instruction: {:?}", i))); + } + }; + + cf.instr_ptr += 1; } } -} -enum ExecResult { - Ok, - Return, - 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?) -// 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 as usize >= instrs.len() || instrs.is_empty()) { - 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()))); + #[inline(always)] + fn exec_end_block(&self, stack: &mut Stack) -> Result<()> { + let block = stack.blocks.pop()?; + stack.values.truncate_keep(block.stack_ptr, block.results as u32); + Ok(()) } - // A match statement is probably the fastest way to do this without - // 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() { - Nop => { /* do nothing */ } - Unreachable => { - cold(); - return Err(crate::Trap::Unreachable.into()); - } - Drop => stack.values.pop().map(|_| ())?, - - Select( - _valtype, // due to validation, we know that the type of the values on the stack are correct - ) => { - // due to validation, we know that the type of the values on the stack - let cond: i32 = stack.values.pop()?.into(); - let val2 = stack.values.pop()?; - - // if cond != 0, we already have the right value on the stack - if cond == 0 { - let _ = stack.values.pop()?; - stack.values.push(val2); - } - } + #[inline(always)] + fn exec_else(&self, stack: &mut Stack, end_offset: u32, cf: &mut CallFrame) -> Result<()> { + let block = stack.blocks.pop()?; + stack.values.truncate_keep(block.stack_ptr, block.results as u32); + cf.instr_ptr += end_offset as usize; + Ok(()) + } - Call(v) => { - // prepare the call frame - 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 { - crate::Function::Wasm(wasm_func) => wasm_func.clone(), - crate::Function::Host(host_func) => { - let func = &host_func.func; - let params = stack.values.pop_params(&host_func.ty.params)?; - let res = (func)(FuncContext { store, module_addr: module.id() }, ¶ms)?; - stack.values.extend_from_typed(&res); - return Ok(ExecResult::Ok); - } - }; + #[inline(always)] + #[cold] + fn exec_unreachable(&self) -> Result<()> { + Err(Error::Trap(Trap::Unreachable)) + } - let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?; - let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len() as u32); + #[inline(always)] + fn exec_const(&self, val: impl Into, stack: &mut Stack) { + stack.values.push(val.into()); + } - // push the call frame - cf.instr_ptr += 1; // skip the call instruction - stack.call_stack.push(cf.clone())?; - stack.call_stack.push(call_frame)?; + #[inline(always)] + fn exec_i32_store_local( + &self, + local: u32, + const_i32: i32, + offset: u32, + mem_addr: u8, + cf: &CallFrame, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + let mem_addr = module.resolve_mem_addr(mem_addr as u32); + let mem = store.get_mem(mem_addr)?; + let val = const_i32.to_le_bytes(); + let addr: u64 = cf.get_local(local).into(); + mem.borrow_mut().store((offset as u64 + addr) as usize, val.len(), &val)?; + Ok(()) + } - // call the function - return Ok(ExecResult::Call); - } + #[inline(always)] + fn exec_i32_local_get_const_add(&self, local: u32, val: i32, stack: &mut Stack, cf: &CallFrame) { + let local: i32 = cf.get_local(local).into(); + stack.values.push((local + val).into()); + } - CallIndirect(type_addr, table_addr) => { - let table = store.get_table(module.resolve_table_addr(*table_addr) as usize)?; - let table_idx = stack.values.pop_t::()?; + #[inline(always)] + fn exec_i64_xor_const_rotl(&self, rotate_by: i64, stack: &mut Stack) -> Result<()> { + let val: i64 = stack.values.pop()?.into(); + let res = stack.values.last_mut()?; + let mask: i64 = (*res).into(); + *res = (val ^ mask).rotate_left(rotate_by as u32).into(); + Ok(()) + } - // verify that the table is of the right type, this should be validated by the parser already - let func_ref = { - 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 })? - }; + #[inline(always)] + fn exec_local_get(&self, local_index: u32, stack: &mut Stack, cf: &CallFrame) { + stack.values.push(cf.get_local(local_index)); + } - let func_inst = store.get_func(func_ref as usize)?.clone(); - let call_ty = module.func_ty(*type_addr); - - let wasm_func = match func_inst.func { - crate::Function::Wasm(ref f) => f.clone(), - crate::Function::Host(host_func) => { - if unlikely(host_func.ty != *call_ty) { - log::error!("indirect call type mismatch: {:?} != {:?}", host_func.ty, call_ty); - return Err(Trap::IndirectCallTypeMismatch { - actual: host_func.ty.clone(), - expected: call_ty.clone(), - } - .into()); - } + #[inline(always)] + fn exec_local_get2(&self, a: u32, b: u32, stack: &mut Stack, cf: &CallFrame) { + stack.values.push(cf.get_local(a)); + stack.values.push(cf.get_local(b)); + } - let host_func = host_func.clone(); - let params = stack.values.pop_params(&host_func.ty.params)?; - let res = (host_func.func)(FuncContext { store, module_addr: module.id() }, ¶ms)?; - stack.values.extend_from_typed(&res); - return Ok(ExecResult::Ok); - } - }; + #[inline(always)] + fn exec_local_get3(&self, a: u32, b: u32, c: u32, stack: &mut Stack, cf: &CallFrame) { + stack.values.push(cf.get_local(a)); + stack.values.push(cf.get_local(b)); + stack.values.push(cf.get_local(c)); + } - if unlikely(wasm_func.ty != *call_ty) { - log::error!("indirect call type mismatch: {:?} != {:?}", wasm_func.ty, call_ty); - return Err( - Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }.into() - ); - } + #[inline(always)] + fn exec_local_get_set(&self, a: u32, b: u32, cf: &mut CallFrame) { + cf.set_local(b, cf.get_local(a)) + } - let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?; - let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len() as u32); + #[inline(always)] + fn exec_local_set(&self, local_index: u32, stack: &mut Stack, cf: &mut CallFrame) -> Result<()> { + cf.set_local(local_index, stack.values.pop()?); + Ok(()) + } - // push the call frame - cf.instr_ptr += 1; // skip the call instruction - stack.call_stack.push(cf.clone())?; - stack.call_stack.push(call_frame)?; + #[inline(always)] + fn exec_local_tee(&self, local_index: u32, stack: &mut Stack, cf: &mut CallFrame) -> Result<()> { + cf.set_local(local_index, *stack.values.last()?); + Ok(()) + } - // call the function - return Ok(ExecResult::Call); - } + #[inline(always)] + fn exec_local_tee_get(&self, a: u32, b: u32, stack: &mut Stack, cf: &mut CallFrame) { + let last = + stack.values.last().expect("localtee: stack is empty. this should have been validated by the parser"); + cf.set_local(a, *last); + stack.values.push(match a == b { + true => *last, + false => cf.get_local(b), + }); + } - 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_block( - BlockFrame::new( - cf.instr_ptr, - cf.instr_ptr + *end_offset, - stack.values.len() as u32, - BlockType::If, - &args.unpack(), - module, - ), - &mut stack.values, - &mut stack.blocks, - ); - return Ok(ExecResult::Ok); - } + #[inline(always)] + fn exec_global_get( + &self, + global_index: u32, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + let global = store.get_global_val(module.resolve_global_addr(global_index))?; + stack.values.push(global); + Ok(()) + } - // falsy value is on the top of the stack - if *else_offset != 0 { - let label = BlockFrame::new( - cf.instr_ptr + *else_offset, - cf.instr_ptr + *end_offset, - stack.values.len() as u32, - BlockType::Else, - &args.unpack(), - module, - ); - cf.instr_ptr += *else_offset; - cf.enter_block(label, &mut stack.values, &mut stack.blocks); - } else { - cf.instr_ptr += *end_offset; - } - } + #[inline(always)] + fn exec_global_set( + &self, + global_index: u32, + stack: &mut Stack, + store: &mut Store, + module: &ModuleInstance, + ) -> Result<()> { + let idx = module.resolve_global_addr(global_index); + store.set_global_val(idx, stack.values.pop()?)?; + Ok(()) + } - Loop(args, end_offset) => { - cf.enter_block( - BlockFrame::new( - cf.instr_ptr, - cf.instr_ptr + *end_offset, - stack.values.len() as u32, - BlockType::Loop, - args, - module, - ), - &mut stack.values, - &mut stack.blocks, - ); - } + #[inline(always)] + fn exec_table_get( + &self, + table_index: u32, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + let table_idx = module.resolve_table_addr(table_index); + let table = store.get_table(table_idx)?; + let idx: u32 = stack.values.pop()?.into(); + let v = table.borrow().get_wasm_val(idx)?; + stack.values.push(v.into()); + Ok(()) + } - Block(args, end_offset) => { - cf.enter_block( - BlockFrame::new( - cf.instr_ptr, - cf.instr_ptr + *end_offset, - stack.values.len() as u32, - BlockType::Block, - args, - module, - ), - &mut stack.values, - &mut stack.blocks, - ); - } + #[inline(always)] + fn exec_table_set( + &self, + table_index: u32, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + let table_idx = module.resolve_table_addr(table_index); + let table = store.get_table(table_idx)?; + let val = stack.values.pop()?.into(); + let idx = stack.values.pop()?.into(); + table.borrow_mut().set(idx, val)?; + Ok(()) + } - BrTable(default, len) => { - let start = cf.instr_ptr + 1; - let end = cf.instr_ptr + 1 + *len; - let instr = cf.instructions()[start as usize..end as usize] - .iter() - .map(|i| match i { - BrLabel(l) => Ok(*l), - _ => { - cold(); - panic!("Expected BrLabel, this should have been validated by the parser") - } - }) - .collect::>>()?; - - if unlikely(instr.len() != *len as usize) { - panic!( - "Expected {} BrLabel instructions, got {}, this should have been validated by the parser", - len, - instr.len() - ); - } + #[inline(always)] + fn exec_table_size( + &self, + table_index: u32, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + let table_idx = module.resolve_table_addr(table_index); + let table = store.get_table(table_idx)?; + stack.values.push(table.borrow().size().into()); + Ok(()) + } - let idx = stack.values.pop_t::()? as usize; - let to = instr.get(idx).unwrap_or(default); - break_to!(cf, stack, to); - } + #[inline(always)] + fn exec_table_init(&self, elem_index: u32, table_index: u32, store: &Store, module: &ModuleInstance) -> Result<()> { + let table_idx = module.resolve_table_addr(table_index); + let table = store.get_table(table_idx)?; + let elem = store.get_elem(module.resolve_elem_addr(elem_index))?; - Br(v) => break_to!(cf, stack, v), - BrIf(v) => { - if stack.values.pop_t::()? != 0 { - break_to!(cf, stack, v); - } + if let ElementKind::Passive = elem.kind { + return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into()); } - Return => match stack.call_stack.is_empty() { - true => return Ok(ExecResult::Return), - false => return Ok(ExecResult::Call), - }, + let Some(items) = elem.items.as_ref() else { + return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into()); + }; - // We're essentially using else as a EndBlockFrame instruction for if blocks - Else(end_offset) => { - let block = - stack.blocks.pop().expect("else: no label to end, this should have been validated by the parser"); + table.borrow_mut().init(module.func_addrs(), 0, items)?; + Ok(()) + } - stack.values.truncate_keep(block.stack_ptr, block.results as u32); - cf.instr_ptr += *end_offset; + #[inline(always)] + fn exec_select(&self, stack: &mut Stack) -> Result<()> { + let cond: i32 = stack.values.pop()?.into(); + let val2 = stack.values.pop()?; + // if cond != 0, we already have the right value on the stack + if cond == 0 { + *stack.values.last_mut()? = val2; } + Ok(()) + } - // remove the label from the label stack - EndBlockFrame => { - let block = stack - .blocks - .pop() - .expect("end blockframe: no label to end, this should have been validated by the parser"); - - stack.values.truncate_keep(block.stack_ptr, block.results as u32); + #[inline(always)] + fn exec_memory_size( + &self, + addr: u32, + byte: u8, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + if unlikely(byte != 0) { + return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); } - 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().expect("localtee: stack is empty. this should have been validated by the parser"), - ), - - GlobalGet(global_index) => { - let idx = module.resolve_global_addr(*global_index); - let global = store.get_global_val(idx as usize)?; - stack.values.push(global); - } + let mem_idx = module.resolve_mem_addr(addr); + let mem = store.get_mem(mem_idx)?; + stack.values.push((mem.borrow().page_count() as i32).into()); + Ok(()) + } - GlobalSet(global_index) => { - let idx = module.resolve_global_addr(*global_index); - store.set_global_val(idx as usize, stack.values.pop()?)?; + #[inline(always)] + fn exec_memory_grow( + &self, + addr: u32, + byte: u8, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + if unlikely(byte != 0) { + return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string())); } - 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()), + let mut mem = store.get_mem(module.resolve_mem_addr(addr))?.borrow_mut(); + let prev_size = mem.page_count() as i32; + let pages_delta = stack.values.last_mut()?; + *pages_delta = match mem.grow(i32::from(*pages_delta)) { + Some(_) => prev_size.into(), + None => (-1).into(), + }; - MemorySize(addr, byte) => { - if unlikely(*byte != 0) { - return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); - } + Ok(()) + } - 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()); + #[inline(always)] + fn exec_memory_copy( + &self, + from: u32, + to: u32, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + let size: i32 = stack.values.pop()?.into(); + let src: i32 = stack.values.pop()?.into(); + let dst: i32 = stack.values.pop()?.into(); + + if from == to { + let mut mem_from = store.get_mem(module.resolve_mem_addr(from))?.borrow_mut(); + // copy within the same memory + mem_from.copy_within(dst as usize, src as usize, size as usize)?; + } else { + // copy between two memories + let mem_from = store.get_mem(module.resolve_mem_addr(from))?.borrow(); + let mut mem_to = store.get_mem(module.resolve_mem_addr(to))?.borrow_mut(); + mem_to.copy_from_slice(dst as usize, mem_from.load(src as usize, size as usize)?)?; } + Ok(()) + } - MemoryGrow(addr, byte) => { - if unlikely(*byte != 0) { - return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string())); - } - - let mem_idx = module.resolve_mem_addr(*addr); - let mem = store.get_mem(mem_idx as usize)?; + #[inline(always)] + fn exec_memory_fill(&self, addr: u32, stack: &mut Stack, store: &Store, module: &ModuleInstance) -> Result<()> { + let size: i32 = stack.values.pop()?.into(); + let val: i32 = stack.values.pop()?.into(); + let dst: i32 = stack.values.pop()?.into(); - let (res, prev_size) = { - let mut mem = mem.borrow_mut(); - let prev_size = mem.page_count() as i32; - (mem.grow(stack.values.pop_t::()?), prev_size) - }; + let mem = store.get_mem(module.resolve_mem_addr(addr))?; + mem.borrow_mut().fill(dst as usize, size as usize, val as u8)?; + Ok(()) + } - match res { - Some(_) => stack.values.push(prev_size.into()), - None => stack.values.push((-1).into()), - } + #[inline(always)] + fn exec_memory_init( + &self, + data_index: u32, + mem_index: u32, + stack: &mut Stack, + store: &Store, + module: &ModuleInstance, + ) -> Result<()> { + let size = i32::from(stack.values.pop()?) as usize; + let offset = i32::from(stack.values.pop()?) as usize; + let dst = i32::from(stack.values.pop()?) as usize; + + let data = match &store.get_data(module.resolve_data_addr(data_index))?.data { + Some(data) => data, + None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()), + }; + + if unlikely(offset + size > data.len()) { + return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data.len() }.into()); } - // Bulk memory operations - MemoryCopy(from, to) => { - 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(); - - if from == to { - // copy within the same memory - 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 mut mem2 = mem2.borrow_mut(); - mem2.copy_from_slice(dst as usize, mem.load(src as usize, size as usize)?)?; + let mem = store.get_mem(module.resolve_mem_addr(mem_index))?; + mem.borrow_mut().store(dst, size, &data[offset..(offset + size)])?; + Ok(()) + } + + #[inline(always)] + fn exec_call( + &self, + v: u32, + store: &mut Store, + stack: &mut Stack, + cf: &mut CallFrame, + module: &mut ModuleInstance, + ) -> Result<()> { + let func_inst = store.get_func(module.resolve_func_addr(v))?; + let wasm_func = match &func_inst.func { + crate::Function::Wasm(wasm_func) => wasm_func, + crate::Function::Host(host_func) => { + let func = &host_func.clone(); + let params = stack.values.pop_params(&host_func.ty.params)?; + let res = (func.func)(FuncContext { store, module_addr: module.id() }, ¶ms)?; + stack.values.extend_from_typed(&res); + cf.instr_ptr += 1; + return Ok(()); } - } + }; - MemoryFill(addr) => { - let size: i32 = stack.values.pop()?.into(); - let val: i32 = stack.values.pop()?.into(); - let dst: i32 = stack.values.pop()?.into(); + let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?; + let new_call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, params, stack.blocks.len() as u32); - 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)?; + cf.instr_ptr += 1; // skip the call instruction + stack.call_stack.push(core::mem::replace(cf, new_call_frame))?; + if cf.module_addr != module.id() { + module.swap_with(cf.module_addr, store); } + Ok(()) + } - MemoryInit(data_index, mem_index) => { - let size = stack.values.pop_t::()? as usize; - let offset = stack.values.pop_t::()? as usize; - let dst = stack.values.pop_t::()? as usize; + #[inline(always)] + fn exec_call_indirect( + &self, + type_addr: u32, + table_addr: u32, + store: &mut Store, + stack: &mut Stack, + cf: &mut CallFrame, + module: &mut ModuleInstance, + ) -> Result<()> { + let table = store.get_table(module.resolve_table_addr(table_addr))?; + let table_idx: u32 = stack.values.pop()?.into(); + + // verify that the table is of the right type, this should be validated by the parser already + let func_ref = { + let table = table.borrow(); + assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref"); + table.get(table_idx)?.addr().ok_or(Trap::UninitializedElement { index: table_idx as usize })? + }; + + let func_inst = store.get_func(func_ref)?.clone(); + let call_ty = module.func_ty(type_addr); + + let wasm_func = match func_inst.func { + crate::Function::Wasm(ref f) => f, + crate::Function::Host(host_func) => { + if unlikely(host_func.ty != *call_ty) { + return Err(Trap::IndirectCallTypeMismatch { + actual: host_func.ty.clone(), + expected: call_ty.clone(), + } + .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 host_func = host_func.clone(); + let params = stack.values.pop_params(&host_func.ty.params)?; + let res = (host_func.func)(FuncContext { store, module_addr: module.id() }, ¶ms)?; + stack.values.extend_from_typed(&res); - if unlikely(offset + size > data.len()) { - return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data.len() }.into()); + cf.instr_ptr += 1; + return Ok(()); } + }; - let mem = store.get_mem(module.resolve_mem_addr(*mem_index) as usize)?; - let mut mem = mem.borrow_mut(); - - // mem.store checks bounds - mem.store(dst, size, &data[offset..(offset + size)])?; + if unlikely(wasm_func.ty != *call_ty) { + return Err( + Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }.into() + ); } - DataDrop(data_index) => { - let data_idx = module.resolve_data_addr(*data_index); - let data = store.get_data_mut(data_idx as usize)?; - data.drop(); - } + let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?; + let new_call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, params, stack.blocks.len() as u32); - 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), - - I32Eq => comp!(==, i32, stack), - I64Eq => comp!(==, i64, stack), - F32Eq => comp!(==, f32, stack), - F64Eq => comp!(==, f64, stack), - - I32Ne => comp!(!=, i32, stack), - I64Ne => comp!(!=, i64, stack), - F32Ne => comp!(!=, f32, stack), - F64Ne => comp!(!=, f64, stack), - - I32LtS => comp!(<, i32, stack), - I64LtS => comp!(<, i64, 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!(<=, u32, stack), - I64LeU => comp!(<=, u64, stack), - F32Le => comp!(<=, f32, stack), - F64Le => comp!(<=, f64, stack), - - I32GeS => comp!(>=, i32, stack), - I64GeS => comp!(>=, i64, 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!(>, u32, stack), - I64GtU => comp!(>, u64, stack), - F32Gt => comp!(>, f32, stack), - F64Gt => comp!(>, f64, stack), - - I64Add => arithmetic!(wrapping_add, i64, stack), - I32Add => arithmetic!(wrapping_add, i32, stack), - F32Add => arithmetic!(+, f32, stack), - F64Add => arithmetic!(+, f64, stack), - - I32Sub => arithmetic!(wrapping_sub, i32, stack), - I64Sub => arithmetic!(wrapping_sub, i64, stack), - F32Sub => arithmetic!(-, f32, stack), - F64Sub => arithmetic!(-, f64, stack), - - F32Div => arithmetic!(/, f32, stack), - F64Div => arithmetic!(/, f64, stack), - - I32Mul => arithmetic!(wrapping_mul, i32, stack), - I64Mul => arithmetic!(wrapping_mul, i64, stack), - F32Mul => arithmetic!(*, f32, stack), - F64Mul => arithmetic!(*, f64, stack), - - // 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, 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, u32, stack), - I64RemU => checked_int_arithmetic!(checked_wrapping_rem, u64, stack), - - I32And => arithmetic!(bitand, i32, stack), - I64And => arithmetic!(bitand, i64, stack), - I32Or => arithmetic!(bitor, i32, stack), - I64Or => arithmetic!(bitor, i64, stack), - I32Xor => arithmetic!(bitxor, i32, stack), - I64Xor => arithmetic!(bitxor, i64, stack), - I32Shl => arithmetic!(wasm_shl, i32, stack), - I64Shl => arithmetic!(wasm_shl, i64, stack), - I32ShrS => arithmetic!(wasm_shr, i32, stack), - I64ShrS => arithmetic!(wasm_shr, 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), - I64Rotr => arithmetic!(wasm_rotr, i64, stack), - - I32Clz => arithmetic_single!(leading_zeros, i32, stack), - I64Clz => arithmetic_single!(leading_zeros, i64, stack), - I32Ctz => arithmetic_single!(trailing_zeros, i32, stack), - I64Ctz => arithmetic_single!(trailing_zeros, i64, stack), - I32Popcnt => arithmetic_single!(count_ones, i32, stack), - I64Popcnt => arithmetic_single!(count_ones, i64, stack), - - F32ConvertI32S => conv!(i32, f32, stack), - F32ConvertI64S => conv!(i64, f32, stack), - F64ConvertI32S => conv!(i32, f64, stack), - F64ConvertI64S => conv!(i64, f64, 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), - - F32DemoteF64 => conv!(f64, f32, stack), - F64PromoteF32 => conv!(f32, f64, stack), - - F32Abs => arithmetic_single!(abs, f32, stack), - F64Abs => arithmetic_single!(abs, f64, stack), - F32Neg => arithmetic_single!(neg, f32, stack), - F64Neg => arithmetic_single!(neg, f64, stack), - F32Ceil => arithmetic_single!(ceil, f32, stack), - F64Ceil => arithmetic_single!(ceil, f64, stack), - F32Floor => arithmetic_single!(floor, f32, stack), - F64Floor => arithmetic_single!(floor, f64, stack), - F32Trunc => arithmetic_single!(trunc, f32, stack), - F64Trunc => arithmetic_single!(trunc, 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!(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 => {} - - // unsigned versions of these are a bit broken atm - I32TruncF32S => checked_conv_float!(f32, i32, stack), - I32TruncF64S => checked_conv_float!(f64, i32, stack), - I32TruncF32U => checked_conv_float!(f32, u32, i32, stack), - I32TruncF64U => checked_conv_float!(f64, u32, i32, stack), - I64TruncF32S => checked_conv_float!(f32, i64, stack), - I64TruncF64S => checked_conv_float!(f64, i64, stack), - I64TruncF32U => checked_conv_float!(f32, u64, i64, stack), - I64TruncF64U => checked_conv_float!(f64, u64, i64, stack), - - TableGet(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)?; - stack.values.push(v.into()); + cf.instr_ptr += 1; // skip the call instruction + stack.call_stack.push(core::mem::replace(cf, new_call_frame))?; + if cf.module_addr != module.id() { + module.swap_with(cf.module_addr, store); } + Ok(()) + } - TableSet(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; - table.borrow_mut().set(idx, val)?; + #[inline(always)] + fn exec_if( + &self, + args: BlockArgs, + else_offset: u32, + end_offset: u32, + stack: &mut Stack, + cf: &mut CallFrame, + module: &mut ModuleInstance, + ) -> Result<()> { + // truthy value is on the top of the stack, so enter the then block + if i32::from(stack.values.pop()?) != 0 { + self.enter_block(stack, cf.instr_ptr, end_offset, BlockType::If, &args, module); + cf.instr_ptr += 1; + return Ok(()); } - TableSize(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()); + // falsy value is on the top of the stack + if else_offset == 0 { + cf.instr_ptr += end_offset as usize + 1; + return Ok(()); } - TableInit(table_index, elem_index) => { - let table_idx = module.resolve_table_addr(*table_index); - let table = store.get_table(table_idx as usize)?; + let old = cf.instr_ptr; + cf.instr_ptr += else_offset as usize; - let elem_idx = module.resolve_elem_addr(*elem_index); - let elem = store.get_elem(elem_idx as usize)?; + self.enter_block(stack, old + else_offset as usize, end_offset - else_offset, BlockType::Else, &args, module); - if let ElementKind::Passive = elem.kind { - return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into()); - } - - let Some(items) = elem.items.as_ref() else { - return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into()); - }; - - table.borrow_mut().init(module.func_addrs(), 0, items)?; - } + cf.instr_ptr += 1; + Ok(()) + } - I32TruncSatF32S => arithmetic_single!(trunc, f32, i32, stack), - I32TruncSatF32U => arithmetic_single!(trunc, f32, u32, stack), - I32TruncSatF64S => arithmetic_single!(trunc, f64, i32, stack), - I32TruncSatF64U => arithmetic_single!(trunc, f64, u32, stack), - I64TruncSatF32S => arithmetic_single!(trunc, f32, i64, stack), - I64TruncSatF32U => arithmetic_single!(trunc, f32, u64, stack), - I64TruncSatF64S => arithmetic_single!(trunc, f64, i64, stack), - I64TruncSatF64U => arithmetic_single!(trunc, f64, u64, stack), - - // custom instructions - LocalGet2(a, b) => { - stack.values.extend_from_slice(&[cf.get_local(*a as usize), cf.get_local(*b as usize)]); - } - LocalGet3(a, b, c) => { - stack.values.extend_from_slice(&[ - cf.get_local(*a as usize), - cf.get_local(*b as usize), - cf.get_local(*c as usize), - ]); - } - // LocalGet4(a, b, c, d) => { - // stack.values.extend_from_slice(&[ - // cf.get_local(*a as usize), - // cf.get_local(*b as usize), - // cf.get_local(*c as usize), - // cf.get_local(*d as usize), - // ]); - // } - LocalTeeGet(a, b) => { - #[inline] - fn local_tee_get(cf: &mut CallFrame, stack: &mut Stack, a: u32, b: u32) -> Result<()> { - let last = *stack - .values - .last() - .expect("localtee: stack is empty. this should have been validated by the parser"); - cf.set_local(a as usize, last); - stack.values.push(cf.get_local(b as usize)); - Ok(()) + #[inline(always)] + fn enter_block( + &self, + stack: &mut super::Stack, + instr_ptr: usize, + end_instr_offset: u32, + ty: BlockType, + args: &BlockArgs, + module: &ModuleInstance, + ) { + let (params, results) = match args { + BlockArgs::Empty => (0, 0), + BlockArgs::Type(_) => (0, 1), + BlockArgs::FuncType(t) => { + let ty = module.func_ty(*t); + (ty.params.len() as u8, ty.results.len() as u8) } - local_tee_get(cf, stack, *a, *b)?; - } - LocalGetSet(a, b) => { - let a = cf.get_local(*a as usize); - cf.set_local(*b as usize, a); - } - I64XorConstRotl(rotate_by) => { - let val = stack.values.pop_t::()?; - 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); - return Err(Error::UnsupportedFeature(alloc::format!("unimplemented instruction: {:?}", i))); - } - }; - - Ok(ExecResult::Ok) + }; + + stack.blocks.push(BlockFrame { + instr_ptr, + end_instr_offset, + stack_ptr: stack.values.len() as u32 - params as u32, + results, + params, + ty, + }); + } } diff --git a/crates/tinywasm/src/runtime/stack.rs b/crates/tinywasm/src/runtime/stack.rs index 3db3c4b..a64b234 100644 --- a/crates/tinywasm/src/runtime/stack.rs +++ b/crates/tinywasm/src/runtime/stack.rs @@ -2,7 +2,7 @@ mod block_stack; mod call_stack; mod value_stack; -use self::{call_stack::CallStack, value_stack::ValueStack}; +pub(crate) use self::{call_stack::CallStack, value_stack::ValueStack}; pub(crate) use block_stack::{BlockFrame, BlockStack, BlockType}; pub(crate) use call_stack::CallFrame; @@ -16,6 +16,6 @@ pub struct Stack { impl Stack { pub(crate) fn new(call_frame: CallFrame) -> Self { - Self { values: ValueStack::default(), blocks: BlockStack::default(), call_stack: CallStack::new(call_frame) } + Self { values: ValueStack::default(), blocks: BlockStack::new(), call_stack: CallStack::new(call_frame) } } } diff --git a/crates/tinywasm/src/runtime/stack/block_stack.rs b/crates/tinywasm/src/runtime/stack/block_stack.rs index 719dc00..9a823fd 100644 --- a/crates/tinywasm/src/runtime/stack/block_stack.rs +++ b/crates/tinywasm/src/runtime/stack/block_stack.rs @@ -1,17 +1,20 @@ -use crate::{unlikely, Error, ModuleInstance, Result}; +use crate::{cold, unlikely, Error, Result}; use alloc::vec::Vec; -use tinywasm_types::BlockArgs; -#[derive(Debug, Clone, Default)] -pub(crate) struct BlockStack(Vec); // TODO: maybe Box<[LabelFrame]> by analyzing the label count when parsing the module? +#[derive(Debug, Clone)] +pub(crate) struct BlockStack(Vec); impl BlockStack { - #[inline] + pub(crate) fn new() -> Self { + Self(Vec::with_capacity(128)) + } + + #[inline(always)] pub(crate) fn len(&self) -> usize { self.0.len() } - #[inline] + #[inline(always)] pub(crate) fn push(&mut self, block: BlockFrame) { self.0.push(block); } @@ -29,55 +32,36 @@ impl BlockStack { Some(&self.0[self.0.len() - index as usize - 1]) } - #[inline] + #[inline(always)] pub(crate) fn pop(&mut self) -> Result { - self.0.pop().ok_or(Error::BlockStackUnderflow) + match self.0.pop() { + Some(frame) => Ok(frame), + None => { + cold(); + Err(Error::BlockStackUnderflow) + } + } } /// keep the top `len` blocks and discard the rest - #[inline] + #[inline(always)] pub(crate) fn truncate(&mut self, len: u32) { self.0.truncate(len as usize); } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub(crate) struct BlockFrame { - // position of the instruction pointer when the block was entered - pub(crate) instr_ptr: u32, - // position of the end instruction of the block - pub(crate) end_instr_ptr: u32, - - // position of the stack pointer when the block was entered - pub(crate) stack_ptr: u32, + pub(crate) instr_ptr: usize, // position of the instruction pointer when the block was entered + pub(crate) end_instr_offset: u32, // position of the end instruction of the block + pub(crate) stack_ptr: u32, // position of the stack pointer when the block was entered pub(crate) results: u8, pub(crate) params: u8, pub(crate) ty: BlockType, } -impl BlockFrame { - #[inline] - pub(crate) fn new( - instr_ptr: u32, - end_instr_ptr: u32, - stack_ptr: u32, - ty: BlockType, - args: &BlockArgs, - module: &ModuleInstance, - ) -> Self { - let (params, results) = match args { - BlockArgs::Empty => (0, 0), - BlockArgs::Type(_) => (0, 1), - BlockArgs::FuncType(t) => { - let ty = module.func_ty(*t); - (ty.params.len() as u8, ty.results.len() as u8) - } - }; - - Self { instr_ptr, end_instr_ptr, stack_ptr, results, params, ty } - } -} +impl BlockFrame {} #[derive(Debug, Copy, Clone)] #[allow(dead_code)] diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index c242b74..5dc754f 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -1,14 +1,10 @@ -use alloc::{boxed::Box, rc::Rc, vec::Vec}; -use tinywasm_types::{Instruction, ModuleInstanceAddr, WasmFunction}; - use crate::runtime::{BlockType, RawWasmValue}; -use crate::unlikely; +use crate::{cold, unlikely}; use crate::{Error, Result, Trap}; +use alloc::{boxed::Box, rc::Rc, vec::Vec}; +use tinywasm_types::{Instruction, LocalAddr, ModuleInstanceAddr, WasmFunction}; -use super::BlockFrame; - -const CALL_STACK_SIZE: usize = 128; -const CALL_STACK_MAX_SIZE: usize = 1024; +const CALL_STACK_SIZE: usize = 1024; #[derive(Debug)] pub(crate) struct CallStack { @@ -18,9 +14,10 @@ pub(crate) struct CallStack { impl CallStack { #[inline] pub(crate) fn new(initial_frame: CallFrame) -> Self { - let mut stack = Self { stack: Vec::with_capacity(CALL_STACK_SIZE) }; - stack.push(initial_frame).unwrap(); - stack + let mut stack = Vec::new(); + stack.reserve_exact(CALL_STACK_SIZE); + stack.push(initial_frame); + Self { stack } } #[inline] @@ -28,17 +25,20 @@ impl CallStack { self.stack.is_empty() } - #[inline] + #[inline(always)] pub(crate) fn pop(&mut self) -> Result { match self.stack.pop() { Some(frame) => Ok(frame), - None => Err(Error::CallStackUnderflow), + None => { + cold(); + Err(Error::CallStackUnderflow) + } } } - #[inline] + #[inline(always)] pub(crate) fn push(&mut self, call_frame: CallFrame) -> Result<()> { - if unlikely(self.stack.len() >= CALL_STACK_MAX_SIZE) { + if unlikely(self.stack.len() >= self.stack.capacity()) { return Err(Trap::CallStackOverflow.into()); } self.stack.push(call_frame); @@ -48,27 +48,23 @@ impl CallStack { #[derive(Debug, Clone)] pub(crate) struct CallFrame { - pub(crate) instr_ptr: u32, + pub(crate) instr_ptr: usize, pub(crate) block_ptr: u32, - pub(crate) func_instance: (Rc, ModuleInstanceAddr), + pub(crate) func_instance: Rc, + pub(crate) module_addr: ModuleInstanceAddr, 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_block( - &mut self, - block_frame: BlockFrame, - values: &mut super::ValueStack, - blocks: &mut super::BlockStack, - ) { - if block_frame.params > 0 { - let start = (block_frame.stack_ptr - block_frame.params as u32) as usize; - let end = block_frame.stack_ptr as usize; - values.extend_from_within(start..end); + #[inline(always)] + pub(crate) fn fetch_instr(&self) -> &Instruction { + match self.func_instance.instructions.get(self.instr_ptr) { + Some(instr) => instr, + None => { + cold(); + panic!("Instruction pointer out of bounds"); + } } - - blocks.push(block_frame); } /// Break to a block at the given index (relative to the current frame) @@ -105,7 +101,7 @@ impl CallFrame { 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; + self.instr_ptr = break_to.instr_ptr + break_to.end_instr_offset as usize; // we also want to trim the label stack, including the block blocks.truncate(blocks.len() as u32 - (break_to_relative + 1)); @@ -115,8 +111,7 @@ impl CallFrame { Some(()) } - // TODO: perf: a lot of time is spent here - #[inline(always)] // about 10% faster with this + #[inline(always)] pub(crate) fn new( wasm_func_inst: Rc, owner: ModuleInstanceAddr, @@ -124,34 +119,29 @@ impl CallFrame { block_ptr: u32, ) -> Self { let locals = { - let local_types = &wasm_func_inst.locals; - let total_size = local_types.len() + params.len(); - let mut locals = Vec::with_capacity(total_size); + let total_size = wasm_func_inst.locals.len() + params.len(); + let mut locals = Vec::new(); + locals.reserve_exact(total_size); locals.extend(params); locals.resize_with(total_size, RawWasmValue::default); locals.into_boxed_slice() }; - Self { instr_ptr: 0, func_instance: (wasm_func_inst, owner), locals, block_ptr } + Self { instr_ptr: 0, func_instance: wasm_func_inst, module_addr: owner, locals, block_ptr } } - #[inline] - pub(crate) fn set_local(&mut self, local_index: usize, value: RawWasmValue) { - self.locals[local_index] = value; - } - - #[inline] - pub(crate) fn get_local(&self, local_index: usize) -> RawWasmValue { - self.locals[local_index] + #[inline(always)] + pub(crate) fn set_local(&mut self, local_index: LocalAddr, value: RawWasmValue) { + self.locals[local_index as usize] = value; } #[inline(always)] - pub(crate) fn instructions(&self) -> &[Instruction] { - &self.func_instance.0.instructions + pub(crate) fn get_local(&self, local_index: LocalAddr) -> RawWasmValue { + self.locals[local_index as usize] } #[inline(always)] - pub(crate) fn current_instruction(&self) -> &Instruction { - &self.func_instance.0.instructions[self.instr_ptr as usize] + pub(crate) fn instructions(&self) -> &[Instruction] { + &self.func_instance.instructions } } diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index 2fa03e9..1573a5d 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -1,11 +1,8 @@ -use core::ops::Range; - use crate::{cold, runtime::RawWasmValue, unlikely, Error, Result}; 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; +pub(crate) const MIN_VALUE_STACK_SIZE: usize = 1024 * 128; #[derive(Debug)] pub(crate) struct ValueStack { @@ -19,36 +16,43 @@ impl Default for ValueStack { } impl ValueStack { - #[inline] - pub(crate) fn extend_from_within(&mut self, range: Range) { - self.stack.extend_from_within(range); - } - #[inline] pub(crate) fn extend_from_typed(&mut self, values: &[WasmValue]) { 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(always)] + pub(crate) fn replace_top(&mut self, func: fn(RawWasmValue) -> RawWasmValue) -> Result<()> { + let v = self.last_mut()?; + *v = func(*v); + Ok(()) } - #[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(always)] + pub(crate) fn calculate(&mut self, func: fn(RawWasmValue, RawWasmValue) -> RawWasmValue) -> Result<()> { + let v2 = self.pop()?; + let v1 = self.last_mut()?; + *v1 = func(*v1, v2); + Ok(()) } - #[inline] + #[inline(always)] + pub(crate) fn calculate_trap( + &mut self, + func: fn(RawWasmValue, RawWasmValue) -> Result, + ) -> Result<()> { + let v2 = self.pop()?; + let v1 = self.last_mut()?; + *v1 = func(*v1, v2)?; + Ok(()) + } + + #[inline(always)] pub(crate) fn len(&self) -> usize { self.stack.len() } + #[inline] pub(crate) fn truncate_keep(&mut self, n: u32, end_keep: u32) { let total_to_keep = n + end_keep; let len = self.stack.len() as u32; @@ -70,8 +74,8 @@ impl ValueStack { } #[inline] - pub(crate) fn last(&self) -> Result<&RawWasmValue> { - match self.stack.last() { + pub(crate) fn last_mut(&mut self) -> Result<&mut RawWasmValue> { + match self.stack.last_mut() { Some(v) => Ok(v), None => { cold(); @@ -81,9 +85,9 @@ impl ValueStack { } #[inline] - pub(crate) fn pop_t>(&mut self) -> Result { - match self.stack.pop() { - Some(v) => Ok(v.into()), + pub(crate) fn last(&self) -> Result<&RawWasmValue> { + match self.stack.last() { + Some(v) => Ok(v), None => { cold(); Err(Error::ValueStackUnderflow) @@ -91,7 +95,7 @@ impl ValueStack { } } - #[inline] + #[inline(always)] pub(crate) fn pop(&mut self) -> Result { match self.stack.pop() { Some(v) => Ok(v), @@ -104,8 +108,7 @@ impl ValueStack { #[inline] pub(crate) fn pop_params(&mut self, types: &[ValType]) -> Result> { - let res = self.pop_n_rev(types.len())?.zip(types.iter()).map(|(v, ty)| v.attach_type(*ty)).collect(); - Ok(res) + Ok(self.pop_n_rev(types.len())?.zip(types.iter()).map(|(v, ty)| v.attach_type(*ty)).collect()) } #[inline] @@ -126,12 +129,10 @@ impl ValueStack { #[inline] pub(crate) fn pop_n_rev(&mut self, n: usize) -> Result> { - let len = self.stack.len(); - if unlikely(len < n) { + if unlikely(self.stack.len() < n) { return Err(Error::ValueStackUnderflow); } - let res = self.stack.drain((len - n)..); - Ok(res) + Ok(self.stack.drain((self.stack.len() - n)..)) } } @@ -146,11 +147,38 @@ mod tests { stack.push(2.into()); stack.push(3.into()); assert_eq!(stack.len(), 3); - assert_eq!(stack.pop_t::().unwrap(), 3); + assert_eq!(i32::from(stack.pop().unwrap()), 3); assert_eq!(stack.len(), 2); - assert_eq!(stack.pop_t::().unwrap(), 2); + assert_eq!(i32::from(stack.pop().unwrap()), 2); assert_eq!(stack.len(), 1); - assert_eq!(stack.pop_t::().unwrap(), 1); + assert_eq!(i32::from(stack.pop().unwrap()), 1); assert_eq!(stack.len(), 0); } + + #[test] + fn test_truncate_keep() { + macro_rules! test_macro { + ($( $n:expr, $end_keep:expr, $expected:expr ),*) => { + $( + let mut stack = ValueStack::default(); + stack.push(1.into()); + stack.push(2.into()); + stack.push(3.into()); + stack.push(4.into()); + stack.push(5.into()); + stack.truncate_keep($n, $end_keep); + assert_eq!(stack.len(), $expected); + )* + }; + } + + test_macro! { + 0, 0, 0, + 1, 0, 1, + 0, 1, 1, + 1, 1, 2, + 2, 1, 3, + 2, 2, 4 + } + } } diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs index 2381657..e5769bf 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)] -// pub struct RawWasmValue([u8; 16]); pub struct RawWasmValue([u8; 8]); impl Debug for RawWasmValue { @@ -29,23 +28,14 @@ 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::RefExtern => { - let val: i64 = self.into(); - if val < 0 { - WasmValue::RefNull(ValType::RefExtern) - } else { - WasmValue::RefExtern(val as u32) - } - } - ValType::RefFunc => { - let val: i64 = self.into(); - if val < 0 { - WasmValue::RefNull(ValType::RefFunc) - } else { - WasmValue::RefFunc(val as u32) - } - } + ValType::RefExtern => match i64::from(self) { + v if v < 0 => WasmValue::RefNull(ValType::RefExtern), + addr => WasmValue::RefExtern(addr as u32), + }, + ValType::RefFunc => match i64::from(self) { + v if v < 0 => WasmValue::RefNull(ValType::RefFunc), + addr => WasmValue::RefFunc(addr as u32), + }, } } } @@ -58,7 +48,6 @@ 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::RefExtern(v) => Self::from(v as i64), WasmValue::RefFunc(v) => Self::from(v as i64), WasmValue::RefNull(_) => Self::from(-1i64), @@ -88,24 +77,42 @@ macro_rules! impl_from_raw_wasm_value { }; } -type RawValue = u64; -type RawValueRep = [u8; 8]; - // This all looks like a lot of extra steps, but the compiler will optimize it all away. -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( +impl_from_raw_wasm_value!(i32, |x| x as u64, |x: [u8; 8]| i32::from_ne_bytes(x[0..4].try_into().unwrap())); +impl_from_raw_wasm_value!(i64, |x| x as u64, |x: [u8; 8]| i64::from_ne_bytes(x[0..8].try_into().unwrap())); +impl_from_raw_wasm_value!(u8, |x| x as u64, |x: [u8; 8]| u8::from_ne_bytes(x[0..1].try_into().unwrap())); +impl_from_raw_wasm_value!(u16, |x| x as u64, |x: [u8; 8]| u16::from_ne_bytes(x[0..2].try_into().unwrap())); +impl_from_raw_wasm_value!(u32, |x| x as u64, |x: [u8; 8]| u32::from_ne_bytes(x[0..4].try_into().unwrap())); +impl_from_raw_wasm_value!(u64, |x| x, |x: [u8; 8]| u64::from_ne_bytes(x[0..8].try_into().unwrap())); +impl_from_raw_wasm_value!(i8, |x| x as u64, |x: [u8; 8]| i8::from_ne_bytes(x[0..1].try_into().unwrap())); +impl_from_raw_wasm_value!(i16, |x| x as u64, |x: [u8; 8]| i16::from_ne_bytes(x[0..2].try_into().unwrap())); +impl_from_raw_wasm_value!(f32, |x| f32::to_bits(x) as u64, |x: [u8; 8]| f32::from_ne_bytes( x[0..4].try_into().unwrap() -))); -impl_from_raw_wasm_value!(f64, |x| f64::to_bits(x) as RawValue, |x: RawValueRep| f64::from_bits(u64::from_ne_bytes( +)); +impl_from_raw_wasm_value!(f64, f64::to_bits, |x: [u8; 8]| f64::from_bits(u64::from_ne_bytes( x[0..8].try_into().unwrap() ))); -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)); +#[cfg(test)] +mod tests { + use super::*; -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())); + #[test] + fn test_raw_wasm_value() { + macro_rules! test_macro { + ($( $ty:ty => $val:expr ),*) => { + $( + let raw: RawWasmValue = $val.into(); + let val: $ty = raw.into(); + assert_eq!(val, $val); + )* + }; + } + + test_macro! { + i32 => 0, i64 => 0, u8 => 0, u16 => 0, u32 => 0, u64 => 0, i8 => 0, i16 => 0, f32 => 0.0, f64 => 0.0, + i32 => i32::MIN, i64 => i64::MIN, u8 => u8::MIN, u16 => u16::MIN, u32 => u32::MIN, u64 => u64::MIN, i8 => i8::MIN, i16 => i16::MIN, f32 => f32::MIN, f64 => f64::MIN, + i32 => i32::MAX, i64 => i64::MAX, u8 => u8::MAX, u16 => u16::MAX, u32 => u32::MAX, u64 => u64::MAX, i8 => i8::MAX, i16 => i16::MAX, f32 => f32::MAX, f64 => f64::MAX + } + } +} diff --git a/crates/tinywasm/src/store/data.rs b/crates/tinywasm/src/store/data.rs index efbb858..935e761 100644 --- a/crates/tinywasm/src/store/data.rs +++ b/crates/tinywasm/src/store/data.rs @@ -15,13 +15,7 @@ impl DataInstance { Self { data, _owner: owner } } - pub(crate) fn drop(&mut self) -> Option<()> { - match self.data { - None => None, - Some(_) => { - let _ = self.data.take(); - Some(()) - } - } + pub(crate) fn drop(&mut self) { + self.data.is_some().then(|| self.data.take()); } } diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs index cbba1a2..6cc778c 100644 --- a/crates/tinywasm/src/store/global.rs +++ b/crates/tinywasm/src/store/global.rs @@ -1,3 +1,5 @@ +use core::cell::Cell; + use alloc::{format, string::ToString}; use tinywasm_types::*; @@ -8,19 +10,19 @@ use crate::{runtime::RawWasmValue, unlikely, Error, Result}; /// See #[derive(Debug)] pub(crate) struct GlobalInstance { - pub(crate) value: RawWasmValue, + pub(crate) value: Cell, pub(crate) ty: GlobalType, pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances } impl GlobalInstance { pub(crate) fn new(ty: GlobalType, value: RawWasmValue, owner: ModuleInstanceAddr) -> Self { - Self { ty, value, _owner: owner } + Self { ty, value: value.into(), _owner: owner } } #[inline] pub(crate) fn get(&self) -> WasmValue { - self.value.attach_type(self.ty.ty) + self.value.get().attach_type(self.ty.ty) } pub(crate) fn set(&mut self, val: WasmValue) -> Result<()> { @@ -36,7 +38,7 @@ impl GlobalInstance { return Err(Error::Other("global is immutable".to_string())); } - self.value = val.into(); + self.value.set(val.into()); Ok(()) } } diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 00bcc97..5ef4366 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -32,6 +32,7 @@ impl MemoryInstance { } } + #[inline(never)] #[cold] fn trap_oob(&self, addr: usize, len: usize) -> Error { Error::Trap(crate::Trap::MemoryOutOfBounds { offset: addr, len, max: self.data.len() }) @@ -46,20 +47,7 @@ impl MemoryInstance { return Err(self.trap_oob(addr, data.len())); } - // WebAssembly doesn't require alignment for stores - #[cfg(not(feature = "unsafe"))] self.data[addr..end].copy_from_slice(data); - - #[cfg(feature = "unsafe")] - // SAFETY: we checked that `end` is in bounds above, this is the same as `copy_from_slice` - // src must is for reads of count * size_of::() bytes. - // dst must is for writes of count * size_of::() bytes. - // Both src and dst are properly aligned. - // The region of memory beginning at src does not overlap with the region of memory beginning at dst with the same size. - unsafe { - core::ptr::copy_nonoverlapping(data.as_ptr(), self.data[addr..end].as_mut_ptr(), len); - } - Ok(()) } @@ -88,14 +76,10 @@ impl MemoryInstance { 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")] - // 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) }; + let val = T::from_le_bytes(match self.data[addr..end].try_into() { + Ok(bytes) => bytes, + Err(_) => unreachable!("checked bounds above"), + }); Ok(val) } @@ -168,37 +152,20 @@ impl MemoryInstance { } } -#[allow(unsafe_code)] /// A trait for types that can be loaded from memory -/// -/// # Safety -/// Only implemented for primitive types, unsafe to not allow it for other types. -/// Only actually unsafe to implement if the `unsafe` feature is enabled since there might be -/// UB for loading things things like packed structs -pub(crate) unsafe trait MemLoadable: Sized + Copy { +pub(crate) 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] + impl MemLoadable<$size> for $type { + #[inline(always)] 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 1cdcff3..8c6e698 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -1,4 +1,4 @@ -use alloc::{boxed::Box, format, rc::Rc, string::ToString, vec::Vec}; +use alloc::{boxed::Box, format, string::ToString, vec::Vec}; use core::cell::RefCell; use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; @@ -31,7 +31,6 @@ static STORE_ID: AtomicUsize = AtomicUsize::new(0); pub struct Store { id: usize, module_instances: Vec, - module_instance_count: usize, pub(crate) data: StoreData, pub(crate) runtime: Runtime, @@ -74,14 +73,7 @@ impl PartialEq for Store { impl Default for Store { fn default() -> Self { let id = STORE_ID.fetch_add(1, Ordering::Relaxed); - - Self { - id, - module_instances: Vec::new(), - module_instance_count: 0, - data: StoreData::default(), - runtime: Runtime::Default, - } + Self { id, module_instances: Vec::new(), data: StoreData::default(), runtime: Runtime::Default } } } @@ -92,9 +84,9 @@ impl Default for Store { /// See pub(crate) struct StoreData { pub(crate) funcs: Vec, - pub(crate) tables: Vec>>, - pub(crate) memories: Vec>>, - pub(crate) globals: Vec>>, + pub(crate) tables: Vec>, + pub(crate) memories: Vec>, + pub(crate) globals: Vec, pub(crate) elements: Vec, pub(crate) datas: Vec, } @@ -106,14 +98,12 @@ impl Store { } pub(crate) fn next_module_instance_idx(&self) -> ModuleInstanceAddr { - self.module_instance_count as ModuleInstanceAddr + self.module_instances.len() as ModuleInstanceAddr } - pub(crate) fn add_instance(&mut self, instance: ModuleInstance) -> Result<()> { - assert!(instance.id() == self.module_instance_count as ModuleInstanceAddr); + pub(crate) fn add_instance(&mut self, instance: ModuleInstance) { + assert!(instance.id() == self.module_instances.len() as ModuleInstanceAddr); self.module_instances.push(instance); - self.module_instance_count += 1; - Ok(()) } #[cold] @@ -123,57 +113,61 @@ impl Store { /// 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")) + pub(crate) fn get_func(&self, addr: FuncAddr) -> Result<&FunctionInstance> { + self.data.funcs.get(addr as usize).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")) + pub(crate) fn get_mem(&self, addr: MemAddr) -> Result<&RefCell> { + self.data.memories.get(addr as usize).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")) + pub(crate) fn get_table(&self, addr: TableAddr) -> Result<&RefCell> { + self.data.tables.get(addr as usize).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")) + pub(crate) fn get_data(&self, addr: DataAddr) -> Result<&DataInstance> { + self.data.datas.get(addr as usize).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")) + pub(crate) fn get_data_mut(&mut self, addr: DataAddr) -> Result<&mut DataInstance> { + self.data.datas.get_mut(addr as usize).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")) + pub(crate) fn get_elem(&self, addr: ElemAddr) -> Result<&ElementInstance> { + self.data.elements.get(addr as usize).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")) + pub(crate) fn get_global(&self, addr: GlobalAddr) -> Result<&GlobalInstance> { + self.data.globals.get(addr as usize).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) + pub fn get_global_val(&self, addr: MemAddr) -> Result { + self.data + .globals + .get(addr as usize) + .ok_or_else(|| Self::not_found_error("global")) + .map(|global| global.value.get()) } /// Set the global at the actual index in the store #[inline] - pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> { - let global = self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")); - global.map(|global| global.borrow_mut().value = value) + pub(crate) fn set_global_val(&mut self, addr: MemAddr, value: RawWasmValue) -> Result<()> { + let global = self.data.globals.get(addr as usize).ok_or_else(|| Self::not_found_error("global")); + global.map(|global| global.value.set(value)) } } @@ -195,7 +189,7 @@ impl Store { let table_count = self.data.tables.len(); let mut table_addrs = Vec::with_capacity(table_count); for (i, table) in tables.into_iter().enumerate() { - self.data.tables.push(Rc::new(RefCell::new(TableInstance::new(table, idx)))); + self.data.tables.push(RefCell::new(TableInstance::new(table, idx))); table_addrs.push((i + table_count) as TableAddr); } Ok(table_addrs) @@ -209,7 +203,7 @@ impl Store { if let MemoryArch::I64 = mem.arch { return Err(Error::UnsupportedFeature("64-bit memories".to_string())); } - self.data.memories.push(Rc::new(RefCell::new(MemoryInstance::new(mem, idx)))); + self.data.memories.push(RefCell::new(MemoryInstance::new(mem, idx))); mem_addrs.push((i + mem_count) as MemAddr); } Ok(mem_addrs) @@ -228,11 +222,11 @@ impl Store { let mut global_addrs = imported_globals; for (i, global) in new_globals.iter().enumerate() { - self.data.globals.push(Rc::new(RefCell::new(GlobalInstance::new( + self.data.globals.push(GlobalInstance::new( global.ty, self.eval_const(&global.init, &global_addrs, func_addrs)?, idx, - )))); + )); global_addrs.push((i + global_count) as Addr); } @@ -251,8 +245,7 @@ impl Store { let addr = globals.get(*addr as usize).copied().ok_or_else(|| { Error::Other(format!("global {} not found. This should have been caught by the validator", addr)) })?; - let global = self.data.globals[addr as usize].clone(); - let val = i64::from(global.borrow().value); + let val: i64 = self.data.globals[addr as usize].value.get().into(); // check if the global is actually a null reference match val < 0 { @@ -370,12 +363,12 @@ impl Store { } pub(crate) fn add_global(&mut self, ty: GlobalType, value: RawWasmValue, idx: ModuleInstanceAddr) -> Result { - self.data.globals.push(Rc::new(RefCell::new(GlobalInstance::new(ty, value, idx)))); + self.data.globals.push(GlobalInstance::new(ty, value, idx)); Ok(self.data.globals.len() as Addr - 1) } pub(crate) fn add_table(&mut self, table: TableType, idx: ModuleInstanceAddr) -> Result { - self.data.tables.push(Rc::new(RefCell::new(TableInstance::new(table, idx)))); + self.data.tables.push(RefCell::new(TableInstance::new(table, idx))); Ok(self.data.tables.len() as TableAddr - 1) } @@ -383,7 +376,7 @@ impl Store { if let MemoryArch::I64 = mem.arch { return Err(Error::UnsupportedFeature("64-bit memories".to_string())); } - self.data.memories.push(Rc::new(RefCell::new(MemoryInstance::new(mem, idx)))); + self.data.memories.push(RefCell::new(MemoryInstance::new(mem, idx))); Ok(self.data.memories.len() as MemAddr - 1) } @@ -397,10 +390,7 @@ impl Store { use tinywasm_types::ConstInstruction::*; let val = match const_instr { I32Const(i) => *i, - GlobalGet(addr) => { - let global = self.data.globals[*addr as usize].borrow(); - i32::from(global.value) - } + GlobalGet(addr) => i32::from(self.data.globals[*addr as usize].value.get()), _ => return Err(Error::Other("expected i32".to_string())), }; Ok(val) @@ -426,8 +416,7 @@ impl Store { let global = self.data.globals.get(*addr as usize).expect("global not found. This should be unreachable"); - - global.borrow().value + global.value.get() } RefNull(t) => RawWasmValue::from(t.default_value()), RefFunc(idx) => RawWasmValue::from(*module_func_addrs.get(*idx as usize).ok_or_else(|| { diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index 52c35f6..a094a14 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -20,7 +20,7 @@ impl TableInstance { Self { elements: vec![TableElement::Uninitialized; kind.size_initial as usize], kind, _owner: owner } } - pub(crate) fn get_wasm_val(&self, addr: usize) -> Result { + pub(crate) fn get_wasm_val(&self, addr: TableAddr) -> Result { let val = self.get(addr)?.addr(); Ok(match self.kind.element_type { @@ -30,12 +30,13 @@ impl TableInstance { }) } - pub(crate) fn get(&self, addr: usize) -> Result<&TableElement> { - self.elements.get(addr).ok_or_else(|| Error::Trap(Trap::UndefinedElement { index: addr })) + pub(crate) fn get(&self, addr: TableAddr) -> Result<&TableElement> { + self.elements.get(addr as usize).ok_or_else(|| Error::Trap(Trap::UndefinedElement { index: addr as usize })) } - pub(crate) fn set(&mut self, table_idx: usize, value: Addr) -> Result<()> { - self.grow_to_fit(table_idx + 1).map(|_| self.elements[table_idx] = TableElement::Initialized(value)) + pub(crate) fn set(&mut self, table_idx: TableAddr, value: Addr) -> Result<()> { + self.grow_to_fit(table_idx as usize + 1) + .map(|_| self.elements[table_idx as usize] = TableElement::Initialized(value)) } pub(crate) fn grow_to_fit(&mut self, new_size: usize) -> Result<()> { diff --git a/crates/tinywasm/tests/generated/2.0.csv b/crates/tinywasm/tests/generated/2.0.csv index c97b58e..11dd840 100644 --- a/crates/tinywasm/tests/generated/2.0.csv +++ b/crates/tinywasm/tests/generated/2.0.csv @@ -2,3 +2,4 @@ 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,27551,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] 0.5.0,27551,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":156,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":99,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":163,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] +0.6.1,27572,335,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"bulk.wast","passed":75,"failed":42},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_copy.wast","passed":4450,"failed":0},{"name":"memory_fill.wast","passed":100,"failed":0},{"name":"memory_grow.wast","passed":96,"failed":0},{"name":"memory_init.wast","passed":240,"failed":0},{"name":"memory_redundancy.wast","passed":8,"failed":0},{"name":"memory_size.wast","passed":42,"failed":0},{"name":"memory_trap.wast","passed":182,"failed":0},{"name":"names.wast","passed":486,"failed":0},{"name":"nop.wast","passed":88,"failed":0},{"name":"ref_func.wast","passed":9,"failed":8},{"name":"ref_is_null.wast","passed":4,"failed":12},{"name":"ref_null.wast","passed":1,"failed":2},{"name":"return.wast","passed":84,"failed":0},{"name":"select.wast","passed":148,"failed":0},{"name":"skip-stack-guard-page.wast","passed":11,"failed":0},{"name":"stack.wast","passed":7,"failed":0},{"name":"start.wast","passed":20,"failed":0},{"name":"store.wast","passed":68,"failed":0},{"name":"switch.wast","passed":28,"failed":0},{"name":"table-sub.wast","passed":2,"failed":0},{"name":"table.wast","passed":19,"failed":0},{"name":"table_copy.wast","passed":1613,"failed":115},{"name":"table_fill.wast","passed":23,"failed":22},{"name":"table_get.wast","passed":10,"failed":6},{"name":"table_grow.wast","passed":21,"failed":29},{"name":"table_init.wast","passed":719,"failed":61},{"name":"table_set.wast","passed":19,"failed":7},{"name":"table_size.wast","passed":8,"failed":31},{"name":"token.wast","passed":58,"failed":0},{"name":"traps.wast","passed":36,"failed":0},{"name":"type.wast","passed":3,"failed":0},{"name":"unreachable.wast","passed":64,"failed":0},{"name":"unreached-invalid.wast","passed":118,"failed":0},{"name":"unreached-valid.wast","passed":7,"failed":0},{"name":"unwind.wast","passed":50,"failed":0},{"name":"utf8-custom-section-id.wast","passed":176,"failed":0},{"name":"utf8-import-field.wast","passed":176,"failed":0},{"name":"utf8-import-module.wast","passed":176,"failed":0},{"name":"utf8-invalid-encoding.wast","passed":176,"failed":0}] diff --git a/crates/tinywasm/tests/generated/mvp.csv b/crates/tinywasm/tests/generated/mvp.csv index 90dfa04..6cf7fea 100644 --- a/crates/tinywasm/tests/generated/mvp.csv +++ b/crates/tinywasm/tests/generated/mvp.csv @@ -6,3 +6,5 @@ 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}] 0.5.0,20272,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":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_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.6.0,20278,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_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.6.1,20278,0,[{"name":"address.wast","passed":260,"failed":0},{"name":"align.wast","passed":162,"failed":0},{"name":"binary-leb128.wast","passed":91,"failed":0},{"name":"binary.wast","passed":112,"failed":0},{"name":"block.wast","passed":223,"failed":0},{"name":"br.wast","passed":97,"failed":0},{"name":"br_if.wast","passed":118,"failed":0},{"name":"br_table.wast","passed":174,"failed":0},{"name":"call.wast","passed":91,"failed":0},{"name":"call_indirect.wast","passed":170,"failed":0},{"name":"comments.wast","passed":8,"failed":0},{"name":"const.wast","passed":778,"failed":0},{"name":"conversions.wast","passed":619,"failed":0},{"name":"custom.wast","passed":11,"failed":0},{"name":"data.wast","passed":61,"failed":0},{"name":"elem.wast","passed":98,"failed":0},{"name":"endianness.wast","passed":69,"failed":0},{"name":"exports.wast","passed":96,"failed":0},{"name":"f32.wast","passed":2514,"failed":0},{"name":"f32_bitwise.wast","passed":364,"failed":0},{"name":"f32_cmp.wast","passed":2407,"failed":0},{"name":"f64.wast","passed":2514,"failed":0},{"name":"f64_bitwise.wast","passed":364,"failed":0},{"name":"f64_cmp.wast","passed":2407,"failed":0},{"name":"fac.wast","passed":8,"failed":0},{"name":"float_exprs.wast","passed":900,"failed":0},{"name":"float_literals.wast","passed":179,"failed":0},{"name":"float_memory.wast","passed":90,"failed":0},{"name":"float_misc.wast","passed":441,"failed":0},{"name":"forward.wast","passed":5,"failed":0},{"name":"func.wast","passed":172,"failed":0},{"name":"func_ptrs.wast","passed":36,"failed":0},{"name":"global.wast","passed":110,"failed":0},{"name":"i32.wast","passed":460,"failed":0},{"name":"i64.wast","passed":416,"failed":0},{"name":"if.wast","passed":241,"failed":0},{"name":"imports.wast","passed":186,"failed":0},{"name":"inline-module.wast","passed":1,"failed":0},{"name":"int_exprs.wast","passed":108,"failed":0},{"name":"int_literals.wast","passed":51,"failed":0},{"name":"labels.wast","passed":29,"failed":0},{"name":"left-to-right.wast","passed":96,"failed":0},{"name":"linking.wast","passed":132,"failed":0},{"name":"load.wast","passed":97,"failed":0},{"name":"local_get.wast","passed":36,"failed":0},{"name":"local_set.wast","passed":53,"failed":0},{"name":"local_tee.wast","passed":97,"failed":0},{"name":"loop.wast","passed":120,"failed":0},{"name":"memory.wast","passed":79,"failed":0},{"name":"memory_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/run.rs b/crates/tinywasm/tests/testsuite/run.rs index 2b2bbc4..1de633f 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -291,7 +291,7 @@ impl TestSuite { )?; return Ok(()); } - wast::WastExecute::Get { module: _, global: _ } => { + wast::WastExecute::Get { module: _, global: _, .. } => { panic!("get not supported"); } wast::WastExecute::Invoke(invoke) => invoke, @@ -395,7 +395,7 @@ impl TestSuite { let invoke = match match exec { wast::WastExecute::Wat(_) => Err(eyre!("wat not supported")), - wast::WastExecute::Get { module: module_id, global } => { + wast::WastExecute::Get { module: module_id, global, .. } => { let module = registered_modules.get(module_id, &store); let Some(module) = module else { test_group.add_result( @@ -408,7 +408,7 @@ impl TestSuite { let module_global = match match module.export_addr(global) { Some(ExternVal::Global(addr)) => { - store.get_global_val(addr as usize).map_err(|_| eyre!("failed to get global")) + store.get_global_val(addr).map_err(|_| eyre!("failed to get global")) } _ => Err(eyre!("no module to get global from")), } { diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index a45c59f..7b3ff1e 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -155,7 +155,7 @@ trait FloatToken { } } } -impl FloatToken for wast::token::Float32 { +impl FloatToken for wast::token::F32 { fn bits(&self) -> Bits { Bits::U32(self.bits) } @@ -168,7 +168,7 @@ impl FloatToken for wast::token::Float32 { WasmValue::F32(f32::NAN) } } -impl FloatToken for wast::token::Float64 { +impl FloatToken for wast::token::F64 { fn bits(&self) -> Bits { Bits::U64(self.bits) } diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 76e5679..33271d5 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -13,8 +13,7 @@ rkyv={version="0.7", optional=true, default-features=false, features=["size_32", bytecheck={version="0.7", optional=true} [features] -default=["std", "logging", "archive", "unsafe"] +default=["std", "logging", "archive"] std=["rkyv?/std"] archive=["dep:rkyv", "dep:bytecheck"] logging=["dep:log"] -unsafe=[] diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index 9bf095b..52146e7 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -65,18 +65,6 @@ impl TinyWasmModule { Ok(root.deserialize(&mut rkyv::Infallible).unwrap()) } - #[cfg(feature = "unsafe")] - #[allow(unsafe_code)] - /// Creates a TinyWasmModule from a slice of bytes. - /// - /// # Safety - /// This function is only safe to call if the bytes have been created by - /// a trusted source. Otherwise, it may cause undefined behavior. - pub unsafe fn from_twasm_unchecked(wasm: &[u8]) -> Self { - let len = validate_magic(wasm).unwrap(); - rkyv::archived_root::(&wasm[len..]).deserialize(&mut rkyv::Infallible).unwrap() - } - /// Serializes the TinyWasmModule into a vector of bytes. /// AlignedVec can be deferenced as a slice of bytes and /// implements io::Write when the `std` feature is enabled. @@ -101,14 +89,4 @@ mod tests { let wasm2 = TinyWasmModule::from_twasm(&twasm).unwrap(); assert_eq!(wasm, wasm2); } - - #[cfg(feature = "unsafe")] - #[test] - fn test_serialize_unchecked() { - let wasm = TinyWasmModule::default(); - let twasm = wasm.serialize_twasm(); - #[allow(unsafe_code)] - let wasm2 = unsafe { TinyWasmModule::from_twasm_unchecked(&twasm) }; - assert_eq!(wasm, wasm2); - } } diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 402cc9a..6290bb4 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -15,8 +15,9 @@ pub enum 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 { + +impl From for BlockArgsPacked { + fn from(args: BlockArgs) -> Self { let mut packed = [0; 5]; match args { BlockArgs::Empty => packed[0] = 0, @@ -31,11 +32,14 @@ impl BlockArgsPacked { } Self(packed) } - pub fn unpack(&self) -> BlockArgs { - match self.0[0] { +} + +impl From for BlockArgs { + fn from(packed: BlockArgsPacked) -> Self { + match packed.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())), + 1 => BlockArgs::Type(ValType::from_byte(packed.0[1]).unwrap()), + 2 => BlockArgs::FuncType(u32::from_le_bytes(packed.0[1..].try_into().unwrap())), _ => unreachable!(), } } @@ -80,38 +84,28 @@ pub enum ConstInstruction { #[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) +#[rustfmt::skip] +#[non_exhaustive] pub enum Instruction { - // Custom Instructions + // > Custom Instructions BrLabel(LabelAddr), - - // Not implemented yet // LocalGet + I32Const + I32Add // One of the most common patterns in the Rust compiler output - // I32LocalGetConstAdd(LocalAddr, i32), - - // Not implemented yet + I32LocalGetConstAdd(LocalAddr, i32), // LocalGet + I32Const + I32Store => I32LocalGetConstStore + I32Const // Also common, helps us skip the stack entirely. // Has to be followed by an I32Const instruction - // I32StoreLocal { local: LocalAddr, offset: i32, mem_addr: MemAddr }, - + I32StoreLocal { local: LocalAddr, const_i32: i32, offset: u32, mem_addr: u8 }, // I64Xor + I64Const + I64RotL // Commonly used by a few crypto libraries I64XorConstRotl(i64), - // LocalTee + LocalGet LocalTeeGet(LocalAddr, LocalAddr), LocalGet2(LocalAddr, LocalAddr), LocalGet3(LocalAddr, LocalAddr, LocalAddr), LocalGetSet(LocalAddr, LocalAddr), - // Not implemented yet - // I32AddConst(i32), - // I32SubConst(i32), - // I64AddConst(i64), - // I64SubConst(i64), - - // Control Instructions + // > Control Instructions // See Unreachable, Nop, @@ -127,12 +121,12 @@ pub enum Instruction { Call(FuncAddr), CallIndirect(TypeAddr, TableAddr), - // Parametric Instructions + // > Parametric Instructions // See Drop, Select(Option), - // Variable Instructions + // > Variable Instructions // See LocalGet(LocalAddr), LocalSet(LocalAddr), @@ -140,7 +134,7 @@ pub enum Instruction { GlobalGet(GlobalAddr), GlobalSet(GlobalAddr), - // Memory Instructions + // > Memory Instructions I32Load { offset: u64, mem_addr: MemAddr }, I64Load { offset: u64, mem_addr: MemAddr }, F32Load { offset: u64, mem_addr: MemAddr }, @@ -167,157 +161,43 @@ pub enum Instruction { MemorySize(MemAddr, u8), MemoryGrow(MemAddr, u8), - // Constants + // > Constants I32Const(i32), I64Const(i64), F32Const(f32), F64Const(f64), - // Reference Types + // > Reference Types RefNull(ValType), RefFunc(FuncAddr), RefIsNull, - // Numeric Instructions + // > Numeric Instructions // See - I32Eqz, - I32Eq, - I32Ne, - I32LtS, - I32LtU, - I32GtS, - I32GtU, - I32LeS, - I32LeU, - I32GeS, - I32GeU, - I64Eqz, - I64Eq, - I64Ne, - I64LtS, - I64LtU, - I64GtS, - I64GtU, - I64LeS, - I64LeU, - I64GeS, - I64GeU, - F32Eq, - F32Ne, - F32Lt, - F32Gt, - F32Le, - F32Ge, - F64Eq, - F64Ne, - F64Lt, - F64Gt, - F64Le, - F64Ge, - I32Clz, - I32Ctz, - I32Popcnt, - I32Add, - I32Sub, - I32Mul, - I32DivS, - I32DivU, - I32RemS, - I32RemU, - I32And, - I32Or, - I32Xor, - I32Shl, - I32ShrS, - I32ShrU, - I32Rotl, - I32Rotr, - I64Clz, - I64Ctz, - I64Popcnt, - I64Add, - I64Sub, - I64Mul, - I64DivS, - I64DivU, - I64RemS, - I64RemU, - I64And, - I64Or, - I64Xor, - I64Shl, - I64ShrS, - I64ShrU, - I64Rotl, - I64Rotr, - F32Abs, - F32Neg, - F32Ceil, - F32Floor, - F32Trunc, - F32Nearest, - F32Sqrt, - F32Add, - F32Sub, - F32Mul, - F32Div, - F32Min, - F32Max, - F32Copysign, - F64Abs, - F64Neg, - F64Ceil, - F64Floor, - F64Trunc, - F64Nearest, - F64Sqrt, - F64Add, - F64Sub, - F64Mul, - F64Div, - F64Min, - F64Max, - F64Copysign, - I32WrapI64, - I32TruncF32S, - I32TruncF32U, - I32TruncF64S, - I32TruncF64U, - I32Extend8S, - I32Extend16S, - I64Extend8S, - I64Extend16S, - I64Extend32S, - I64ExtendI32S, - I64ExtendI32U, - I64TruncF32S, - I64TruncF32U, - I64TruncF64S, - I64TruncF64U, - F32ConvertI32S, - F32ConvertI32U, - F32ConvertI64S, - F32ConvertI64U, - F32DemoteF64, - F64ConvertI32S, - F64ConvertI32U, - F64ConvertI64S, - F64ConvertI64U, - F64PromoteF32, - I32ReinterpretF32, - I64ReinterpretF64, - F32ReinterpretI32, - F64ReinterpretI64, - I32TruncSatF32S, - I32TruncSatF32U, - I32TruncSatF64S, - I32TruncSatF64U, - I64TruncSatF32S, - I64TruncSatF32U, - I64TruncSatF64S, - I64TruncSatF64U, - - // Table Instructions + I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU, + I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU, + // Comparisons + F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge, + F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge, + I32Clz, I32Ctz, I32Popcnt, I32Add, I32Sub, I32Mul, I32DivS, I32DivU, I32RemS, I32RemU, + I64Clz, I64Ctz, I64Popcnt, I64Add, I64Sub, I64Mul, I64DivS, I64DivU, I64RemS, I64RemU, + // Bitwise + I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr, + I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr, + // Floating Point + F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt, F32Add, F32Sub, F32Mul, F32Div, F32Min, F32Max, F32Copysign, + F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, F64Nearest, F64Sqrt, F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign, + I32WrapI64, I32TruncF32S, I32TruncF32U, I32TruncF64S, I32TruncF64U, I32Extend8S, I32Extend16S, + I64Extend8S, I64Extend16S, I64Extend32S, I64ExtendI32S, I64ExtendI32U, I64TruncF32S, I64TruncF32U, I64TruncF64S, I64TruncF64U, + F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64, + F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32, + // Reinterpretations (noops at runtime) + I32ReinterpretF32, I64ReinterpretF64, F32ReinterpretI32, F64ReinterpretI64, + // Saturating Float-to-Int Conversions + I32TruncSatF32S, I32TruncSatF32U, I32TruncSatF64S, I32TruncSatF64U, + I64TruncSatF32S, I64TruncSatF32U, I64TruncSatF64S, I64TruncSatF64U, + + // > Table Instructions TableInit(TableAddr, ElemAddr), TableGet(TableAddr), TableSet(TableAddr), @@ -326,7 +206,7 @@ pub enum Instruction { TableSize(TableAddr), TableFill(TableAddr), - // Bulk Memory Instructions + // > Bulk Memory Instructions MemoryInit(MemAddr, DataAddr), MemoryCopy(MemAddr, MemAddr), MemoryFill(MemAddr), @@ -339,44 +219,37 @@ mod test_blockargs_packed { #[test] fn test_empty() { - let args = BlockArgs::Empty; - let packed = BlockArgsPacked::new(args); - assert_eq!(packed.unpack(), BlockArgs::Empty); + let packed: BlockArgsPacked = BlockArgs::Empty.into(); + assert_eq!(BlockArgs::from(packed), 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)); + let packed: BlockArgsPacked = BlockArgs::Type(ValType::I32).into(); + assert_eq!(BlockArgs::from(packed), 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)); + let packed: BlockArgsPacked = BlockArgs::Type(ValType::I64).into(); + assert_eq!(BlockArgs::from(packed), 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)); + let packed: BlockArgsPacked = BlockArgs::Type(ValType::F32).into(); + assert_eq!(BlockArgs::from(packed), 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)); + let packed: BlockArgsPacked = BlockArgs::Type(ValType::F64).into(); + assert_eq!(BlockArgs::from(packed), 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)); + let packed: BlockArgsPacked = BlockArgs::FuncType(0x12345678).into(); + assert_eq!(BlockArgs::from(packed), BlockArgs::FuncType(0x12345678)); } } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index c5c8071..5e38bfc 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -4,8 +4,7 @@ ))] #![warn(missing_debug_implementations, rust_2018_idioms, unreachable_pub)] #![no_std] -#![cfg_attr(not(feature = "unsafe"), forbid(unsafe_code))] -#![cfg_attr(feature = "unsafe", deny(unused_unsafe))] +#![forbid(unsafe_code)] //! Types used by [`tinywasm`](https://docs.rs/tinywasm) and [`tinywasm_parser`](https://docs.rs/tinywasm_parser). @@ -95,7 +94,7 @@ pub struct TinyWasmModule { /// A WebAssembly External Kind. /// /// See -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub enum ExternalKind { /// A WebAssembly Function. diff --git a/crates/wasm-testsuite/Cargo.toml b/crates/wasm-testsuite/Cargo.toml index 2e2fc21..5a59ecd 100644 --- a/crates/wasm-testsuite/Cargo.toml +++ b/crates/wasm-testsuite/Cargo.toml @@ -1,6 +1,6 @@ [package] name="wasm-testsuite" -version="0.2.2" +version="0.4.0" description="Mirror of the WebAssembly core testsuite for use in testing WebAssembly implementations" license="Apache-2.0" readme="README.md" diff --git a/crates/wasm-testsuite/data b/crates/wasm-testsuite/data index 16a839d..9df2c8a 160000 --- a/crates/wasm-testsuite/data +++ b/crates/wasm-testsuite/data @@ -1 +1 @@ -Subproject commit 16a839d5601c283541a84572b47637f035b51437 +Subproject commit 9df2c8a23c4d2f889c2c1a62e5fb9b744579efc5 diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml index f3f475e..f88fde4 100644 --- a/examples/rust/Cargo.toml +++ b/examples/rust/Cargo.toml @@ -10,7 +10,7 @@ forced-target="wasm32-unknown-unknown" edition="2021" [dependencies] -tinywasm={path="../../crates/tinywasm", features=["parser", "std", "unsafe"]} +tinywasm={path="../../crates/tinywasm", features=["parser", "std", "nightly"]} argon2={version="0.5"} [[bin]] diff --git a/examples/rust/analyze.py b/examples/rust/analyze.py index a450a1a..a813c1c 100644 --- a/examples/rust/analyze.py +++ b/examples/rust/analyze.py @@ -2,28 +2,27 @@ 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") +if len(sys.argv) < 3: + print("Usage: python script.py sequence_length path/to/yourfile.wat") sys.exit(1) # The first command line argument is the file path -file_path = sys.argv[1] +seq_len = int(sys.argv[1]) +file_path = sys.argv[2] # Regex to match WASM operators, adjust as necessary -operator_pattern = re.compile(r'\b[a-z0-9_]+\.[a-z0-9_]+\b') +operator_pattern = re.compile(r"\b[a-z0-9_]+\.[a-z0-9_]+\b") # Read the file -with open(file_path, 'r') as 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)] +sequences = [" ".join(operators[i : i + seq_len]) for i in range(len(operators) - 2)] # Count occurrences of each sequence sequence_counts = Counter(sequences) diff --git a/examples/rust/build.sh b/examples/rust/build.sh index e6d3b0d..9c1f77d 100755 --- a/examples/rust/build.sh +++ b/examples/rust/build.sh @@ -15,7 +15,7 @@ for bin in "${bins[@]}"; do RUSTFLAGS="-C target-feature=$features -C panic=abort" cargo build --target wasm32-unknown-unknown --package rust-wasm-examples --profile=wasm --bin "$bin" cp "$out_dir/$bin.wasm" "$dest_dir/" - wasm-opt "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wasm" -Oz --enable-bulk-memory --enable-multivalue --enable-reference-types --enable-mutable-globals + wasm-opt "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wasm" -O3 --enable-bulk-memory --enable-reference-types --enable-mutable-globals if [[ ! " ${exclude_wat[@]} " =~ " $bin " ]]; then wasm2wat "$dest_dir/$bin.wasm" -o "$dest_dir/$bin.wat" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 7d876a7..a84b93a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel="nightly-2024-03-11" +channel="nightly-2024-04-15"