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" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy