├── .gitignore ├── components ├── conformance-tests │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ ├── build.rs │ └── Cargo.lock ├── wasm-opt │ ├── tests │ │ ├── hello_world.wasm │ │ ├── ink_example_multisig.wasm │ │ ├── command_tests.rs │ │ ├── api_tests.rs │ │ └── base_tests.rs │ ├── README.md │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ ├── profiles.rs │ │ ├── fake_command.rs │ │ ├── features.rs │ │ ├── builder.rs │ │ ├── lib.rs │ │ ├── base.rs │ │ ├── api.rs │ │ ├── integration.rs │ │ ├── run.rs │ │ └── passes.rs ├── hello-world │ ├── src │ │ └── lib.rs │ └── Cargo.toml ├── wasm-opt-sys │ ├── README.md │ ├── Cargo.toml │ ├── src │ │ ├── lib.rs │ │ └── wasm-opt-main-shim.cpp │ └── build.rs └── wasm-opt-cxx-sys │ ├── README.md │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── lib.rs │ └── shims.h ├── .gitmodules ├── Cargo.toml ├── .circleci └── config.yml ├── LICENSE-MIT ├── publish.sh ├── CHANGELOG.md ├── .github └── workflows │ └── ci.yml ├── README.md ├── Cargo.lock └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *~ -------------------------------------------------------------------------------- /components/conformance-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "binaryen"] 2 | path = binaryen 3 | url = https://github.com/WebAssembly/binaryen.git 4 | -------------------------------------------------------------------------------- /components/wasm-opt/tests/hello_world.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brson/wasm-opt-rs/HEAD/components/wasm-opt/tests/hello_world.wasm -------------------------------------------------------------------------------- /components/wasm-opt/tests/ink_example_multisig.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brson/wasm-opt-rs/HEAD/components/wasm-opt/tests/ink_example_multisig.wasm -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "components/*", 4 | ] 5 | exclude = [ 6 | "components/hello-world", # not compatible with Rust 1.48 7 | "components/conformance-tests", 8 | ] 9 | -------------------------------------------------------------------------------- /components/hello-world/src/lib.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::prelude::*; 2 | 3 | #[wasm_bindgen] 4 | extern "C" { 5 | pub fn alert(s: &str); 6 | } 7 | 8 | #[wasm_bindgen] 9 | pub fn greet() { 10 | alert("Hello, world!"); 11 | } 12 | -------------------------------------------------------------------------------- /components/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version = "0.0.0" 4 | edition = "2018" 5 | description = "wasm-pack sample" 6 | license = "MIT / Apache-2.0" 7 | publish = false 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | wasm-bindgen = "0.2" 14 | -------------------------------------------------------------------------------- /components/wasm-opt-sys/README.md: -------------------------------------------------------------------------------- 1 | # Rust bindings for Binaryen's `wasm-opt` 2 | 3 | `wasm-opt` is a component of the [Binaryen] toolkit 4 | that optimizes [WebAssembly] modules. It is written 5 | in C++. 6 | 7 | [Binaryen]: https://github.com/WebAssembly/binaryen 8 | [WebAssembly]: https://webassembly.org/ 9 | -------------------------------------------------------------------------------- /components/wasm-opt/README.md: -------------------------------------------------------------------------------- 1 | # Rust bindings for Binaryen's `wasm-opt` 2 | 3 | `wasm-opt` is a component of the [Binaryen] toolkit 4 | that optimizes [WebAssembly] modules. It is written 5 | in C++. 6 | 7 | [Binaryen]: https://github.com/WebAssembly/binaryen 8 | [WebAssembly]: https://webassembly.org/ 9 | -------------------------------------------------------------------------------- /components/wasm-opt-cxx-sys/README.md: -------------------------------------------------------------------------------- 1 | # Rust bindings for Binaryen's `wasm-opt` 2 | 3 | `wasm-opt` is a component of the [Binaryen] toolkit 4 | that optimizes [WebAssembly] modules. It is written 5 | in C++. 6 | 7 | [Binaryen]: https://github.com/WebAssembly/binaryen 8 | [WebAssembly]: https://webassembly.org/ 9 | -------------------------------------------------------------------------------- /components/conformance-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "conformance-tests" 3 | version = "0.116.0" 4 | description = "Tests against binaryen" 5 | license = "MIT / Apache-2.0" 6 | edition = "2018" 7 | publish = false 8 | 9 | [build-dependencies] 10 | anyhow = "1.0.58" 11 | 12 | [dev-dependencies] 13 | anyhow = "1.0.58" 14 | wasm-opt = { path = "../wasm-opt", version = "0.116.0", default-features = false } 15 | tempfile = "3.3.0" 16 | 17 | [features] 18 | default = ["dwarf"] 19 | dwarf = ["wasm-opt/dwarf"] 20 | -------------------------------------------------------------------------------- /components/wasm-opt-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-opt-sys" 3 | version = "0.116.0" 4 | description = "Native wasm-opt build" 5 | license = "MIT / Apache-2.0" 6 | edition = "2018" 7 | documentation = "https://docs.rs/wasm-opt-sys" 8 | repository = "https://github.com/brson/wasm-opt-rs" 9 | readme = "README.md" 10 | links = "binaryen" 11 | 12 | [build-dependencies] 13 | anyhow = "1.0.58" 14 | cc = { version = "1.0.73", features = ["parallel"] } 15 | cxx-build = "1.0.79" 16 | 17 | [dependencies] 18 | cxx = "1.0.79" 19 | 20 | [features] 21 | default = ["dwarf"] 22 | dwarf = [] -------------------------------------------------------------------------------- /components/wasm-opt-cxx-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-opt-cxx-sys" 3 | version = "0.116.0" 4 | description = "wasm-opt bindings via cxx" 5 | license = "MIT / Apache-2.0" 6 | edition = "2018" 7 | documentation = "https://docs.rs/wasm-opt-cxx-sys" 8 | repository = "https://github.com/brson/wasm-opt-rs" 9 | readme = "README.md" 10 | 11 | [dependencies] 12 | anyhow = "1.0.58" 13 | cxx = "1.0.79" 14 | wasm-opt-sys = { path = "../wasm-opt-sys", version = "0.116.0", default-features = false } 15 | 16 | [build-dependencies] 17 | anyhow = "1.0.58" 18 | cxx-build = { version = "1.0.79", features = ["parallel"] } 19 | -------------------------------------------------------------------------------- /components/wasm-opt-cxx-sys/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> anyhow::Result<()> { 2 | let mut builder = cxx_build::bridge("src/lib.rs"); 3 | 4 | { 5 | let target_env = std::env::var("CARGO_CFG_TARGET_ENV")?; 6 | 7 | let flags: &[_] = if target_env != "msvc" { 8 | &["-std=c++17", "-Wno-unused-parameter", "-DBUILD_LLVM_DWARF"] 9 | } else { 10 | &["/std:c++17", "/DBUILD_LLVM_DWARF"] 11 | }; 12 | 13 | for flag in flags { 14 | builder.flag(flag); 15 | } 16 | } 17 | 18 | builder.include("src").compile("wasm-opt-cxx"); 19 | 20 | Ok(()) 21 | } 22 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | rust: circleci/rust@1.6.0 5 | 6 | jobs: 7 | build-and-tests: 8 | machine: 9 | image: ubuntu-2004:current 10 | resource_class: arm.large 11 | steps: 12 | - checkout 13 | - run: git submodule update --init --recursive 14 | - rust/install 15 | - run: 16 | name: Version information 17 | command: rustc --version; cargo --version; rustup --version 18 | - rust/test 19 | # - run: cargo test --manifest-path components/conformance-tests/Cargo.toml 20 | 21 | workflows: 22 | wasm-opt-rs-workflow: 23 | jobs: 24 | - build-and-tests 25 | -------------------------------------------------------------------------------- /components/wasm-opt-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Native build of `wasm-opt`. 2 | //! 3 | //! This crate builds the C++ code for `wasm-opt` but 4 | //! does not provide any Rust bindings. 5 | //! 6 | //! Rust bindings can be found in [`wasm-opt-cxx-sys`], 7 | //! but most users will probably want the high level APIs in the [`wasm-opt`] crate. 8 | //! 9 | //! [`wasm-opt-cxx-sys`]: https://docs.rs/wasm-opt-cxx-sys 10 | //! [`wasm-opt`]: https://docs.rs/wasm-opt 11 | 12 | /// Just here so that cxx-build becomes willing to manage the set of include 13 | /// directories from this crate for downstream crates to include from. Perhaps 14 | /// cxx-build should stop making it necessary to put this. 15 | #[cxx::bridge] 16 | mod dummy {} 17 | -------------------------------------------------------------------------------- /components/wasm-opt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-opt" 3 | version = "0.116.1" 4 | description = "wasm-opt bindings" 5 | license = "MIT / Apache-2.0" 6 | edition = "2018" 7 | documentation = "https://docs.rs/wasm-opt" 8 | repository = "https://github.com/brson/wasm-opt-rs" 9 | categories = ["wasm"] 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | anyhow = "1.0.58" 14 | libc = "0.2.126" 15 | wasm-opt-sys = { path = "../wasm-opt-sys", version = "0.116.0", default-features = false } 16 | wasm-opt-cxx-sys = { path = "../wasm-opt-cxx-sys", version = "0.116.0" } 17 | strum = "0.24" 18 | strum_macros = "0.24" 19 | thiserror = "1.0.32" 20 | tempfile = "3.3.0" 21 | 22 | [features] 23 | default = ["dwarf"] 24 | dwarf = ["wasm-opt-sys/dwarf"] -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /components/wasm-opt-sys/src/wasm-opt-main-shim.cpp: -------------------------------------------------------------------------------- 1 | #include // _Exit 2 | #include // runtime_error 3 | #include 4 | 5 | extern "C" int wasm_opt_main_actual(int argc, const char* argv[]); 6 | 7 | // A wrapper for the C++ `main` function that catches exceptions. 8 | // 9 | // This is needed because we have asked the `Fatal` type to throw instead of 10 | // exit, for use as a library. But for use as a `bin` we still need to handle 11 | // those exceptions in a similar way to the "real" `wasm-opt` bin. 12 | // 13 | // Since the bin does not use `cxx` it doesn't get baked-in exception handling, 14 | // so we do that here, and the bin calls this main function. 15 | // 16 | // This design is also influenced by the need to maintain binary compatibility 17 | // between `wasm-opt` crate 0.110.0 and 0.110.1. We might otherwise use `cxx` 18 | // for the exception handling. 19 | extern "C" int wasm_opt_main(int argc, const char* argv[]) { 20 | try { 21 | return wasm_opt_main_actual(argc, argv); 22 | } catch (const std::exception &e) { 23 | std::cerr << e.what() << std::endl; 24 | // See comments in `Fatal` about `_Exit` and static destructors. 25 | _Exit(EXIT_FAILURE); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /components/wasm-opt/src/main.rs: -------------------------------------------------------------------------------- 1 | // Establish linking with wasm_opt_sys, which contains no Rust code. 2 | extern crate wasm_opt_sys; 3 | 4 | fn main() -> anyhow::Result<()> { 5 | wasm_opt_main() 6 | } 7 | 8 | mod c { 9 | use libc::{c_char, c_int}; 10 | 11 | extern "C" { 12 | pub fn wasm_opt_main(argc: c_int, argv: *const *const c_char) -> c_int; 13 | } 14 | } 15 | 16 | pub fn wasm_opt_main() -> anyhow::Result<()> { 17 | use libc::{c_char, c_int}; 18 | use std::ffi::OsString; 19 | #[cfg(unix)] 20 | use std::os::unix::ffi::OsStrExt; 21 | 22 | let args: Vec = std::env::args_os().collect(); 23 | 24 | #[cfg(unix)] 25 | let c_args: Result, _> = args 26 | .into_iter() 27 | .map(|s| std::ffi::CString::new(s.as_bytes())) 28 | .collect(); 29 | 30 | #[cfg(windows)] 31 | let c_args: Result, _> = args 32 | .into_iter() 33 | .map(|s| std::ffi::CString::new(s.to_str().expect("utf8").as_bytes())) 34 | .collect(); 35 | 36 | let c_args = c_args?; 37 | let c_ptrs: Vec<*const c_char> = c_args.iter().map(|s| s.as_ptr() as *const c_char).collect(); 38 | 39 | let argc = c_ptrs.len() as c_int; 40 | let argv = c_ptrs.as_ptr(); 41 | 42 | let c_return; 43 | unsafe { 44 | c_return = c::wasm_opt_main(argc, argv); 45 | } 46 | 47 | drop(c_ptrs); 48 | drop(c_args); 49 | 50 | std::process::exit(c_return) 51 | } 52 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # replacing version numbers: 4 | # perl -p -i -e "s/0\.110\.0/0\.110\.1/g" components/*/*toml 5 | # 6 | # REMEMBER TO do a dry-run build with the RUST_DEPLOY_VERSION 7 | # REMEMBER TO WIPE THE binaryen SUBDIR 8 | # REMEMBER TO TAG THE RELEASE 9 | 10 | set -x -e 11 | 12 | # The rust min version we (and cxx) "support" can no longer resolve the crate 13 | # graph correctly (it picks a once_cell that requires rust 2021), and various 14 | # other crates' latest versions no longer work with 1.48. 15 | # We publish with the first version of Rust that can publish and verify the crates. 16 | RUST_MIN_VERSION=1.48.0 17 | RUST_DEPLOY_VERSION=1.66.0 18 | 19 | # This is just to make sure we've got the compiler. 20 | rustc +$RUST_DEPLOY_VERSION --version 21 | 22 | rm -rf ./components/wasm-opt-sys/binaryen 23 | 24 | cp -r ./binaryen ./components/wasm-opt-sys/ 25 | 26 | # Make sure we don't publish this recursive submodule. 27 | # Not needed by our build. 28 | rm -rf ./components/wasm-opt-sys/binaryen/third_party/googletest 29 | 30 | # Don't publish the large Binaryen test suite. 31 | rm -r ./components/wasm-opt-sys/binaryen/test 32 | 33 | # cargo +$RUST_DEPLOY_VERSION publish --manifest-path ./components/wasm-opt-sys/Cargo.toml --dry-run 34 | # cargo +$RUST_DEPLOY_VERSION publish --manifest-path ./components/wasm-opt-cxx-sys/Cargo.toml --dry-run 35 | # cargo +$RUST_DEPLOY_VERSION publish --manifest-path ./components/wasm-opt/Cargo.toml --dry-run 36 | 37 | 38 | # --allow-dirty lets the binaryen source be shipped in its copied location 39 | 40 | cargo +$RUST_DEPLOY_VERSION publish --manifest-path ./components/wasm-opt-sys/Cargo.toml --allow-dirty 41 | echo waiting 42 | sleep 10 43 | cargo +$RUST_DEPLOY_VERSION publish --manifest-path ./components/wasm-opt-cxx-sys/Cargo.toml 44 | echo waiting 45 | sleep 10 46 | cargo +$RUST_DEPLOY_VERSION publish --manifest-path ./components/wasm-opt/Cargo.toml 47 | -------------------------------------------------------------------------------- /components/conformance-tests/build.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{bail, Result}; 2 | use std::path::PathBuf; 3 | use std::process::Command; 4 | 5 | fn main() -> Result<()> { 6 | if cfg!(windows) { 7 | panic!("this doesn't work on windows yet"); 8 | } 9 | 10 | build_binaryen_wasm_opt()?; 11 | build_rust_wasm_opt()?; 12 | 13 | Ok(()) 14 | } 15 | 16 | struct Dirs { 17 | workspace: PathBuf, 18 | binaryen_src: PathBuf, 19 | binaryen_build: PathBuf, 20 | } 21 | 22 | fn get_dirs() -> Result { 23 | let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?; 24 | let manifest_dir = PathBuf::from(manifest_dir); 25 | let workspace = manifest_dir.join("../.."); 26 | let binaryen_src = workspace.join("binaryen"); 27 | let out_dir = std::env::var("OUT_DIR")?; 28 | let out_dir = PathBuf::from(out_dir); 29 | let binaryen_build = out_dir.join("binaryen-test-build"); 30 | 31 | Ok(Dirs { 32 | workspace, 33 | binaryen_src, 34 | binaryen_build, 35 | }) 36 | } 37 | 38 | fn build_binaryen_wasm_opt() -> Result<()> { 39 | let dirs = get_dirs()?; 40 | 41 | std::fs::create_dir_all(&dirs.binaryen_build)?; 42 | 43 | let cmake_status = Command::new("cmake") 44 | .current_dir(&dirs.binaryen_build) 45 | .arg(&dirs.binaryen_src) 46 | .arg("-DBUILD_TESTS=OFF") 47 | .status()?; 48 | 49 | if !cmake_status.success() { 50 | bail!("cmake failed"); 51 | } 52 | 53 | let make_status = Command::new("make") 54 | .current_dir(&dirs.binaryen_build) 55 | .status()?; 56 | 57 | if !make_status.success() { 58 | bail!("make failed"); 59 | } 60 | 61 | Ok(()) 62 | } 63 | 64 | fn build_rust_wasm_opt() -> Result<()> { 65 | let dirs = get_dirs()?; 66 | 67 | let mut cmd = Command::new("cargo"); 68 | cmd.current_dir(dirs.workspace) 69 | .args(["build", "-p", "wasm-opt", "--release"]); 70 | 71 | #[cfg(feature = "dwarf")] 72 | cmd.args(["--features", "dwarf"]); 73 | 74 | let cargo_status = cmd.status()?; 75 | 76 | if !cargo_status.success() { 77 | bail!("cargo failed"); 78 | } 79 | 80 | Ok(()) 81 | } 82 | -------------------------------------------------------------------------------- /components/wasm-opt/src/profiles.rs: -------------------------------------------------------------------------------- 1 | use crate::api::{OptimizationOptions, OptimizeLevel, ShrinkLevel}; 2 | 3 | #[derive(Eq, PartialEq, Debug)] 4 | pub struct Profile { 5 | optimize_level: OptimizeLevel, 6 | shrink_level: ShrinkLevel, 7 | add_default_passes: bool, 8 | } 9 | 10 | impl Profile { 11 | pub fn optimize_for_size() -> Profile { 12 | Profile { 13 | optimize_level: OptimizeLevel::Level2, 14 | shrink_level: ShrinkLevel::Level1, 15 | add_default_passes: true, 16 | } 17 | } 18 | 19 | pub fn optimize_for_size_aggressively() -> Profile { 20 | Profile { 21 | optimize_level: OptimizeLevel::Level2, 22 | shrink_level: ShrinkLevel::Level2, 23 | add_default_passes: true, 24 | } 25 | } 26 | 27 | pub fn opt_level_0() -> Profile { 28 | Profile { 29 | optimize_level: OptimizeLevel::Level0, 30 | shrink_level: ShrinkLevel::Level0, 31 | add_default_passes: false, 32 | } 33 | } 34 | 35 | pub fn opt_level_1() -> Profile { 36 | Profile { 37 | optimize_level: OptimizeLevel::Level1, 38 | shrink_level: ShrinkLevel::Level0, 39 | add_default_passes: true, 40 | } 41 | } 42 | 43 | pub fn opt_level_2() -> Profile { 44 | Profile { 45 | optimize_level: OptimizeLevel::Level2, 46 | shrink_level: ShrinkLevel::Level0, 47 | add_default_passes: true, 48 | } 49 | } 50 | 51 | pub fn opt_level_3() -> Profile { 52 | Profile { 53 | optimize_level: OptimizeLevel::Level3, 54 | shrink_level: ShrinkLevel::Level0, 55 | add_default_passes: true, 56 | } 57 | } 58 | 59 | pub fn opt_level_4() -> Profile { 60 | Profile { 61 | optimize_level: OptimizeLevel::Level4, 62 | shrink_level: ShrinkLevel::Level0, 63 | add_default_passes: true, 64 | } 65 | } 66 | 67 | pub fn into_opts(self) -> OptimizationOptions { 68 | let mut opts = OptimizationOptions::new_empty(); 69 | self.apply_to_opts(&mut opts); 70 | opts 71 | } 72 | 73 | pub fn apply_to_opts(self, opts: &mut OptimizationOptions) { 74 | opts.passopts.optimize_level = self.optimize_level; 75 | opts.passopts.shrink_level = self.shrink_level; 76 | opts.passes.add_default_passes = self.add_default_passes; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.116.1 2 | 3 | - [Fixed build on wasm32-wasmi](https://github.com/brson/wasm-opt-rs/pull/165). 4 | 5 | ## 0.116.0 6 | 7 | - The "dwarf" feature is enabled by default. 8 | This feature activates DWARF-related passes in Binaryen. 9 | It builds C++ code from the LLVM project. 10 | Disable it to avoid linkage errors with LLVM. 11 | - [Binaryen changelog for 116](https://github.com/WebAssembly/binaryen/blob/main/CHANGELOG.md#v116). 12 | - [Binaryen changelog for 115](https://github.com/WebAssembly/binaryen/blob/main/CHANGELOG.md#v115). 13 | 14 | ## 0.114.2 15 | 16 | - Added the "dwarf" cargo feature, disabled by default. 17 | - [Fixed link-time regression in 0.114.1](https://github.com/brson/wasm-opt-rs/issues/154) 18 | 19 | 0.114.1 added missing DWARF passes. Unfortunately these passes, taken from 20 | LLVM code, cause duplicate symbol linker errors when linked into a program 21 | that links to LLVM. For now we have put the compilation of these passes under 22 | the "dwarf" flag and made them non-default. In a future release "dwarf" will 23 | be a default feature. Version 0.114.1 has been yanked. 24 | 25 | ## 0.114.1 26 | 27 | - [Compiled missing DWARF passes](https://github.com/brson/wasm-opt-rs/pull/151). 28 | 29 | ## 0.114.0 30 | 31 | - Upgraded to Binaryen 114. 32 | 33 | ## 0.113.0 34 | 35 | - Upgraded to Binaryen 113. 36 | 37 | ## 0.112.0 38 | 39 | - Upgraded to Binaryen 112. 40 | - [Fixed the displayed version number in `wasm-opt` bin](https://github.com/brson/wasm-opt-rs/pull/133) 41 | 42 | ## 0.111.0 43 | 44 | - Upgraded to Binaryen 111. 45 | - [Fixed bugs in feature selection via the API](https://github.com/brson/wasm-opt-rs/issues/123). 46 | - Binaryen now enables the `SignExt` and `MutableGlobals` features by default, 47 | which are also enabled in the LLVM backend. 48 | In the future Binaryen will align its default feature selection with the LLVM backend. 49 | To get the same feature selection as Binaryen 110, call 50 | 51 | ```rust 52 | opts.mvp_features_only() 53 | ``` 54 | - The `TypedFunctionReferences` feature has been removed. The CLI still accepts 55 | `--enable-typed-function-references` and `--disabled-type-function-references` 56 | as no-ops. The `integration` module does not accept these command line arguments. 57 | 58 | ## 0.110.2 59 | 60 | - [Backported Binaryen patch to remove empty memories sections from output](https://github.com/brson/wasm-opt-rs/pull/111). 61 | 62 | ## 0.110.1 63 | 64 | - [Removed Binaryen test suite from published source](https://github.com/brson/wasm-opt-rs/issues/98). 65 | - [Removed duplicate Binaryen source from `wasm-opt-cxx-sys`](https://github.com/brson/wasm-opt-rs/pull/90). 66 | - [Fixed exception handling in bin](https://github.com/brson/wasm-opt-rs/issues/89). 67 | 68 | ## 0.110.0 69 | 70 | - Initial release 71 | -------------------------------------------------------------------------------- /components/wasm-opt/tests/command_tests.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsString; 2 | use std::fs::{self, File}; 3 | use std::io::BufWriter; 4 | use std::io::Write; 5 | use std::path::PathBuf; 6 | use tempfile::Builder; 7 | use wasm_opt::integration::Command; 8 | use wasm_opt::*; 9 | 10 | static WASM_FILE: &[u8] = include_bytes!("hello_world.wasm"); 11 | static MULTISIG_WASM: &[u8] = include_bytes!("ink_example_multisig.wasm"); 12 | 13 | #[test] 14 | fn pass_arg_works() -> anyhow::Result<()> { 15 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 16 | let inpath = temp_dir.path().join("infile.wasm"); 17 | 18 | let infile = File::create(&inpath)?; 19 | 20 | let mut buf_writer = BufWriter::new(&infile); 21 | buf_writer.write_all(MULTISIG_WASM)?; 22 | 23 | let outfile = temp_dir.path().join("outfile.wasm"); 24 | 25 | let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?; 26 | let manifest_dir = PathBuf::from(manifest_dir); 27 | let workspace = manifest_dir.join("../.."); 28 | let rust_wasm_opt_dir = workspace.join("target/release/wasm-opt"); 29 | 30 | let mut cmd = Command::new(rust_wasm_opt_dir); 31 | cmd.arg(&inpath); 32 | cmd.arg("--output"); 33 | cmd.arg(outfile.to_str().expect("PathBuf")); 34 | 35 | cmd.arg("--extract-function"); 36 | cmd.arg("--pass-arg"); 37 | cmd.arg("extract-function@rust_begin_unwind"); 38 | 39 | integration::run_from_command_args(cmd)?; 40 | 41 | let infile_reader = fs::read(inpath)?; 42 | let outfile_reader = fs::read(outfile)?; 43 | 44 | assert!(infile_reader.len() >= outfile_reader.len()); 45 | 46 | Ok(()) 47 | } 48 | 49 | #[test] 50 | fn pass_arg_unsupported_works() -> anyhow::Result<()> { 51 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 52 | let inpath = temp_dir.path().join("infile.wasm"); 53 | 54 | let infile = File::create(&inpath)?; 55 | 56 | let mut buf_writer = BufWriter::new(&infile); 57 | buf_writer.write_all(WASM_FILE)?; 58 | 59 | let outfile = temp_dir.path().join("outfile.wasm"); 60 | 61 | let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?; 62 | let manifest_dir = PathBuf::from(manifest_dir); 63 | let workspace = manifest_dir.join("../.."); 64 | let rust_wasm_opt_dir = workspace.join("target/release/wasm-opt"); 65 | 66 | let mut cmd = Command::new(rust_wasm_opt_dir); 67 | cmd.arg(&inpath); 68 | cmd.arg("--output"); 69 | cmd.arg(outfile.to_str().expect("PathBuf")); 70 | 71 | cmd.arg("-p"); 72 | cmd.arg("--whatever"); 73 | cmd.arg("--no-validation"); 74 | 75 | let res = integration::run_from_command_args(cmd); 76 | 77 | assert!(res.is_err()); 78 | 79 | if let Some(err) = res.err() { 80 | match err { 81 | integration::Error::Unsupported { args } => { 82 | if args.contains(&OsString::from("--no-validation")) { 83 | panic!(); 84 | } 85 | 86 | if !(args.contains(&OsString::from("-p")) 87 | && args.contains(&OsString::from("--whatever"))) 88 | { 89 | panic!(); 90 | } 91 | } 92 | _ => panic!(), 93 | } 94 | } 95 | 96 | Ok(()) 97 | } 98 | -------------------------------------------------------------------------------- /components/wasm-opt/src/fake_command.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{OsStr, OsString}; 2 | use std::io::Result; 3 | use std::path::Path; 4 | use std::process::Command as StdCommand; 5 | use std::process::{Child, ExitStatus, Output, Stdio}; 6 | 7 | /// Like [`std::process::Command`] but args are iterable in old versions of Rust. 8 | pub struct Command { 9 | inner: StdCommand, 10 | cached_args: Vec, 11 | } 12 | 13 | impl Command { 14 | pub fn new>(program: S) -> Command { 15 | Command { 16 | inner: StdCommand::new(program), 17 | cached_args: vec![], 18 | } 19 | } 20 | 21 | pub fn arg>(&mut self, arg: S) -> &mut Command { 22 | self.cached_args.push(OsString::from(arg.as_ref())); 23 | self.inner.arg(arg); 24 | self 25 | } 26 | 27 | pub fn args(&mut self, args: I) -> &mut Command 28 | where 29 | I: IntoIterator, 30 | S: AsRef, 31 | { 32 | let args: Vec<_> = args.into_iter().collect(); 33 | self.cached_args 34 | .extend(args.iter().map(|arg| OsString::from(arg))); 35 | self.inner.args(args); 36 | self 37 | } 38 | 39 | pub fn env(&mut self, key: K, val: V) -> &mut Command 40 | where 41 | K: AsRef, 42 | V: AsRef, 43 | { 44 | self.inner.env(key, val); 45 | self 46 | } 47 | 48 | pub fn envs(&mut self, vars: I) -> &mut Command 49 | where 50 | I: IntoIterator, 51 | K: AsRef, 52 | V: AsRef, 53 | { 54 | self.inner.envs(vars); 55 | self 56 | } 57 | 58 | pub fn env_remove>(&mut self, key: K) -> &mut Command { 59 | self.inner.env_remove(key); 60 | self 61 | } 62 | 63 | pub fn env_clear(&mut self) -> &mut Command { 64 | self.inner.env_clear(); 65 | self 66 | } 67 | 68 | pub fn current_dir>(&mut self, dir: P) -> &mut Command { 69 | self.inner.current_dir(dir); 70 | self 71 | } 72 | 73 | pub fn stdin>(&mut self, cfg: T) -> &mut Command { 74 | self.inner.stdin(cfg); 75 | self 76 | } 77 | 78 | pub fn stdout>(&mut self, cfg: T) -> &mut Command { 79 | self.inner.stdout(cfg); 80 | self 81 | } 82 | 83 | pub fn stderr>(&mut self, cfg: T) -> &mut Command { 84 | self.inner.stderr(cfg); 85 | self 86 | } 87 | 88 | pub fn spawn(&mut self) -> Result { 89 | self.inner.spawn() 90 | } 91 | 92 | pub fn output(&mut self) -> Result { 93 | self.inner.output() 94 | } 95 | 96 | pub fn status(&mut self) -> Result { 97 | self.inner.status() 98 | } 99 | 100 | pub fn get_args(&self) -> CommandArgs<'_> { 101 | CommandArgs { 102 | inner: self 103 | .cached_args 104 | .iter() 105 | .map(&|arg: &OsString| arg.as_os_str()), 106 | } 107 | } 108 | } 109 | 110 | pub struct CommandArgs<'cmd> { 111 | inner: std::iter::Map< 112 | std::slice::Iter<'cmd, OsString>, 113 | &'cmd dyn for<'r> Fn(&'r OsString) -> &'r OsStr, 114 | >, // omg 115 | } 116 | 117 | impl<'cmd> Iterator for CommandArgs<'cmd> { 118 | type Item = &'cmd OsStr; 119 | 120 | fn next(&mut self) -> Option<&'cmd OsStr> { 121 | self.inner.next() 122 | } 123 | } 124 | 125 | impl std::fmt::Debug for Command { 126 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 127 | self.inner.fmt(f) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /components/wasm-opt/src/features.rs: -------------------------------------------------------------------------------- 1 | use strum_macros::EnumString; 2 | 3 | /// Optional wasm features. 4 | /// 5 | /// The [`Feature::Mvp`] feature represents the original spec. 6 | /// Other features are post-MVP, 7 | /// some specified and implemented in all engines, 8 | /// some specified but not implemented, some experimental. 9 | /// 10 | /// See [the WebAssembly roadmap][rm] for an indication of which features can be 11 | /// used where. 12 | /// 13 | /// [rm]: https://webassembly.org/roadmap/ 14 | #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, EnumString)] 15 | pub enum Feature { 16 | /// None. 17 | #[strum(disabled)] 18 | None, 19 | /// Atomics. 20 | /// 21 | /// [Specification](https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md). 22 | #[strum(serialize = "threads")] 23 | Atomics, 24 | /// Import and export of mutable globals. 25 | /// 26 | /// [Specification](https://github.com/WebAssembly/mutable-global/blob/master/proposals/mutable-global/Overview.md). 27 | #[strum(serialize = "mutable-globals")] 28 | MutableGlobals, 29 | #[strum(serialize = "nontrapping-float-to-int")] 30 | TruncSat, 31 | /// Fixed-width SIMD. 32 | /// 33 | /// [Specification](https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md). 34 | #[strum(serialize = "simd")] 35 | Simd, 36 | /// Bulk memory operations. 37 | /// 38 | /// [Specification](https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md). 39 | #[strum(serialize = "bulk-memory")] 40 | BulkMemory, 41 | /// Sign extension operations. 42 | /// 43 | /// [Specification](https://github.com/WebAssembly/spec/blob/master/proposals/sign-extension-ops/Overview.md). 44 | #[strum(serialize = "sign-ext")] 45 | SignExt, 46 | /// Exception handling. 47 | /// 48 | /// [Specification](https://github.com/WebAssembly/exception-handling/blob/master/proposals/exception-handling/Exceptions.md). 49 | #[strum(serialize = "exception-handling")] 50 | ExceptionHandling, 51 | /// Tail calls. 52 | /// 53 | /// [Specification](https://github.com/WebAssembly/tail-call/blob/master/proposals/tail-call/Overview.md). 54 | #[strum(serialize = "tail-call")] 55 | TailCall, 56 | /// Reference types. 57 | /// 58 | /// [Specification](https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md). 59 | #[strum(serialize = "reference-types")] 60 | ReferenceTypes, 61 | /// Multi-value. 62 | /// 63 | /// [Specification](https://github.com/WebAssembly/spec/blob/master/proposals/multi-value/Overview.md) 64 | #[strum(serialize = "multivalue")] 65 | Multivalue, 66 | #[strum(serialize = "gc")] 67 | Gc, 68 | /// Large memory. 69 | /// 70 | /// [Specification](https://github.com/WebAssembly/memory64/blob/main/proposals/memory64/Overview.md). 71 | #[strum(serialize = "memory64")] 72 | Memory64, 73 | /// Relaxed SIMD. 74 | /// 75 | /// [Specification](https://github.com/WebAssembly/relaxed-simd/tree/main/proposals/relaxed-simd). 76 | #[strum(serialize = "relaxed-simd")] 77 | RelaxedSimd, 78 | /// Extended constant expressions. 79 | /// 80 | /// [Specification](https://github.com/WebAssembly/relaxed-simd/tree/main/proposals/relaxed-simd). 81 | #[strum(serialize = "extended-const")] 82 | ExtendedConst, 83 | #[strum(serialize = "strings")] 84 | Strings, 85 | /// Multiple memory. 86 | /// 87 | /// [Specification](https://github.com/WebAssembly/multi-memory/blob/master/proposals/multi-memory/Overview.md). 88 | #[strum(serialize = "multi-memory")] 89 | MultiMemory, 90 | /// The original WebAssembly specification. 91 | /// 92 | /// It has the same value as `None`. 93 | #[strum(disabled)] 94 | Mvp, 95 | /// The default feature set. 96 | /// 97 | /// Includes [`Feature::SignExt`] and [`Feature::MutableGlobals`]. 98 | #[strum(disabled)] 99 | Default, 100 | /// All features. 101 | #[strum(disabled)] 102 | All, 103 | } 104 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "master", "ci" ] 6 | pull_request: 7 | branches: [ "master", "ci" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUSTFLAGS: -D warnings 12 | 13 | jobs: 14 | build: 15 | strategy: 16 | matrix: 17 | os: [macos-11, windows-2019, ubuntu-20.04] 18 | rust: [stable, 1.48.0] 19 | c-compiler: [''] 20 | cxx-compiler: [''] 21 | target: [''] 22 | include: 23 | - os: ubuntu-20.04 24 | rust: nightly 25 | - os: ubuntu-20.04 26 | rust: beta 27 | - os: ubuntu-20.04 28 | target: i686-unknown-linux-gnu 29 | rust: stable 30 | - os: ubuntu-20.04 31 | rust: stable 32 | - os: windows-2019 33 | target: i686-pc-windows-msvc 34 | rust: stable 35 | - os: windows-2019 36 | rust: stable 37 | - os: ubuntu-20.04 38 | rust: stable 39 | - os: ubuntu-20.04 40 | c-compiler: clang 41 | cxx-compiler: clang++ 42 | rust: stable 43 | - os: ubuntu-20.04 44 | c-compiler: gcc-7 45 | cxx-compiler: g++-7 46 | rust: stable 47 | - os: ubuntu-20.04 48 | c-compiler: clang-7 49 | cxx-compiler: clang++-7 50 | rust: stable 51 | 52 | runs-on: ${{ matrix.os }} 53 | 54 | steps: 55 | - if: matrix.c-compiler == 'clang' 56 | name: Install clang 57 | run: | 58 | sudo apt-get update 59 | sudo apt-get install clang 60 | 61 | - if: matrix.c-compiler == 'clang-7' 62 | name: Install clang-7 63 | run: | 64 | sudo apt-get update 65 | sudo apt-get install clang-7 66 | 67 | - if: matrix.c-compiler == 'gcc-7' 68 | name: Install gcc-7 69 | run: | 70 | sudo apt-get update 71 | sudo apt-get install gcc-7 g++-7 72 | 73 | - uses: dtolnay/rust-toolchain@v1 74 | with: 75 | toolchain: ${{ matrix.rust }} 76 | - uses: actions/checkout@v3 77 | - name: Checkout submodule 78 | run: git submodule update --init --recursive 79 | 80 | - if: matrix.target != '' 81 | run: rustup target add ${{matrix.target}} 82 | 83 | - if: ${{ matrix.target != '' && matrix.os == 'ubuntu-20.04' }} 84 | name: Install cross tools 85 | run: | 86 | sudo apt-get update 87 | sudo apt-get install gcc-multilib g++-multilib 88 | 89 | - if: ${{ matrix.target == '' && matrix.c-compiler == '' }} 90 | name: Build / test 91 | run: | 92 | rustc --version 93 | cargo build --verbose 94 | cargo test -- --nocapture 95 | - if: ${{ matrix.target == '' && matrix.c-compiler != '' }} 96 | name: Build / test with alt compiler 97 | env: 98 | CC: ${{matrix.c-compiler}} 99 | CXX: ${{matrix.cxx-compiler}} 100 | run: | 101 | $CC --version 102 | $CXX --version 103 | rustc --version 104 | cargo build --verbose 105 | cargo test -- --nocapture 106 | - if: matrix.target != '' 107 | name: Build / test 32-bit 108 | run: | 109 | rustc --version 110 | cargo build --verbose --target=${{matrix.target}} 111 | cargo test --target=${{matrix.target}} -- --nocapture 112 | 113 | conformance-tests: 114 | name: Conformance Tests 115 | runs-on: ubuntu-latest 116 | steps: 117 | - uses: actions/checkout@v3 118 | - name: Checkout submodule 119 | run: git submodule update --init --recursive 120 | - run: cargo test --manifest-path components/conformance-tests/Cargo.toml 121 | 122 | non-dwarf: 123 | name: Without dwarf feature 124 | runs-on: ubuntu-latest 125 | steps: 126 | - uses: actions/checkout@v3 127 | - name: Checkout submodule 128 | run: git submodule update --init --recursive 129 | - run: cargo test --no-default-features 130 | - run: cargo test --no-default-features --manifest-path components/conformance-tests/Cargo.toml 131 | 132 | fmt: 133 | name: Cargo fmt 134 | runs-on: ubuntu-latest 135 | steps: 136 | - uses: actions/checkout@v3 137 | - run: cargo fmt --check 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust bindings for Binaryen's `wasm-opt` 2 | 3 | [github](https://github.com/brson/wasm-opt-rs) 4 | [crates.io](https://crates.io/crates/wasm-opt) 5 | [docs.rs](https://docs.rs/wasm-opt) 6 | [build status](https://github.com/brson/wasm-opt-rs/actions?query=branch%3Amaster) 7 | 8 | `wasm-opt` is a component of the [Binaryen] toolkit 9 | that optimizes [WebAssembly] modules. It is written 10 | in C++. 11 | 12 | [Binaryen]: https://github.com/WebAssembly/binaryen 13 | [WebAssembly]: https://webassembly.org/ 14 | 15 | This project provides a Rust crate that builds `wasm-opt` and: 16 | 17 | 1) makes its command-line interface installable via `cargo install`, 18 | 2) provides an API to access it programmatically. 19 | 20 | 21 | 22 | 23 | ## Installing the binary 24 | 25 | ``` 26 | cargo install wasm-opt --locked 27 | ``` 28 | 29 | It should behave exactly the same as `wasm-opt` installed from other sources. 30 | 31 | 32 | 33 | 34 | ## Using the library 35 | 36 | See the [API documentation][api]. 37 | 38 | [api]: https://docs.rs/wasm-opt 39 | 40 | 41 | 42 | 43 | ## Building from source 44 | 45 | ``` 46 | git clone https://github.com/brson/wasm-opt-rs 47 | cd wasm-opt-rs 48 | git submodule update --init --recursive 49 | cargo build && cargo test 50 | ``` 51 | 52 | 53 | 54 | 55 | ## Toolchain requirements 56 | 57 | Requires Rust 1.48+ and a C++ compiler with C++17 support. 58 | It does not require CMake or other C++ build tools. 59 | 60 | These are the earliest C++ compiler versions known to work: 61 | 62 | - gcc 7 63 | - clang 7 64 | - Visual Studio 2019 65 | 66 | 67 | 68 | 69 | ## Limitations 70 | 71 | - The `wasm-opt-sys` crate takes a non-negligible amount of time to build. It 72 | also does not do any incremental recompilation, so if the build is invalidated 73 | it will rebuild the C++ code from scratch. The lack of incremental 74 | recompilation is a limitation self-imposed by not using cmake or other 75 | external build system. 76 | - `wasm-opt` on Windows does not support extended unicode paths (probably 77 | anything non-ASCII). This is a [limitation of 78 | binaryen](https://github.com/brson/wasm-opt-rs/issues/40) and not a regression 79 | of the bindings. It may or may not be fixed in the future. The APIs will 80 | return an error if this occurs. 81 | - `cargo tarpaulin` (code coverage) [segfaults running any `wasm-opt` 82 | crates](https://github.com/brson/wasm-opt-rs/issues/59), reason unknown. This 83 | behavior could infect other crates that link to `wasm-opt`. If you use 84 | tarpaulin, you might verify it continues to work. 85 | 86 | 87 | 88 | 89 | ## Versioning 90 | 91 | Binaryen uses a single monotonically-increasing number for versions. 92 | This crate uses the semver minor version to track the Binaryen version, 93 | so e.g. the `wasm-opt` crate version `0.110.0` corresponds to Binaryen 110. 94 | Point releases are used for bugfixes. 95 | 96 | Since minor version bumps of crates earlier than `1.0.0` are considered breaking, 97 | users need to explicitly upgrade versions of `wasm-opt` to get new Binaryen releases, 98 | and upgrades may have breaking API changes, 99 | though we don't anticipate significant changes. 100 | 101 | 102 | 103 | 104 | ## Thanks 105 | 106 | This project was created thanks to a [grant from the Web3 Foundation](https://github.com/w3f/Grants-Program/pull/1070). 107 | 108 | 109 | 110 | 111 | ## License 112 | 113 | Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) 114 | or [MIT license](LICENSE-MIT) at your option. 115 | 116 | Unless you explicitly state otherwise, any contribution intentionally submitted 117 | for inclusion in this project by you, as defined in the Apache-2.0 license, 118 | shall be dual licensed as above, without any additional terms or conditions. 119 | 120 | Binaryen itself, code from which is compiled and linked by this project, 121 | is licensed under the terms of the Apache License, Version 2.0. 122 | -------------------------------------------------------------------------------- /components/wasm-opt/tests/api_tests.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use strum::IntoEnumIterator; 3 | use wasm_opt::base::pass_registry; 4 | use wasm_opt::base::InliningOptions as BaseInliningOptions; 5 | use wasm_opt::base::PassOptions as BasePassOptions; 6 | use wasm_opt::base::{ 7 | check_inlining_options_defaults, check_pass_options_defaults, check_pass_options_defaults_os, 8 | pass_registry::is_pass_hidden, 9 | }; 10 | use wasm_opt::*; 11 | 12 | use std::error::Error; 13 | use std::fs::File; 14 | use std::io::BufWriter; 15 | use std::io::Write; 16 | use tempfile::Builder; 17 | 18 | static GARBAGE_FILE: &[u8] = include_bytes!("garbage_file.wat"); 19 | 20 | #[test] 21 | fn all_passes_correct() -> anyhow::Result<()> { 22 | let mut passes_via_base_rs = HashSet::::new(); 23 | pass_registry::get_registered_names() 24 | .iter() 25 | .for_each(|name| { 26 | if !is_pass_hidden(name) { 27 | passes_via_base_rs.insert(name.to_string()); 28 | } 29 | }); 30 | 31 | let mut passes_via_enum = HashSet::::new(); 32 | 33 | Pass::iter().for_each(|item| { 34 | passes_via_enum.insert(item.name().to_string()); 35 | }); 36 | 37 | let diff: Vec<_> = passes_via_base_rs.difference(&passes_via_enum).collect(); 38 | 39 | println!("diff: {:?}", diff); 40 | 41 | assert_eq!(passes_via_base_rs, passes_via_enum); 42 | 43 | Ok(()) 44 | } 45 | 46 | #[test] 47 | fn test_inlining_options_defaults() -> anyhow::Result<()> { 48 | let inlining_defaults = InliningOptions::default(); 49 | let mut inlining = BaseInliningOptions::new(); 50 | inlining.set_always_inline_max_size(inlining_defaults.always_inline_max_size); 51 | inlining.set_one_caller_inline_max_size(inlining_defaults.one_caller_inline_max_size); 52 | inlining.set_flexible_inline_max_size(inlining_defaults.flexible_inline_max_size); 53 | inlining.set_allow_functions_with_loops(inlining_defaults.allow_functions_with_loops); 54 | inlining.set_partial_inlining_ifs(inlining_defaults.partial_inlining_ifs); 55 | 56 | assert_eq!(check_inlining_options_defaults(inlining), true); 57 | 58 | Ok(()) 59 | } 60 | 61 | #[test] 62 | fn test_pass_options_defaults() -> anyhow::Result<()> { 63 | let pass_options_defaults = PassOptions::default(); 64 | let mut pass_options = BasePassOptions::new(); 65 | pass_options.set_validate(pass_options_defaults.validate); 66 | pass_options.set_validate_globally(pass_options_defaults.validate_globally); 67 | pass_options.set_optimize_level(pass_options_defaults.optimize_level as i32); 68 | pass_options.set_shrink_level(pass_options_defaults.shrink_level as i32); 69 | pass_options.set_traps_never_happen(pass_options_defaults.traps_never_happen); 70 | pass_options.set_low_memory_unused(pass_options_defaults.low_memory_unused); 71 | pass_options.set_fast_math(pass_options_defaults.fast_math); 72 | pass_options.set_zero_filled_memory(pass_options_defaults.zero_filled_memory); 73 | pass_options.set_debug_info(pass_options_defaults.debug_info); 74 | 75 | assert_eq!(check_pass_options_defaults(pass_options), true); 76 | 77 | Ok(()) 78 | } 79 | 80 | #[test] 81 | fn test_optimization_options_os() -> anyhow::Result<()> { 82 | let opts = OptimizationOptions::new_optimize_for_size(); 83 | 84 | let mut pass_options = BasePassOptions::new(); 85 | pass_options.set_validate(opts.passopts.validate); 86 | pass_options.set_validate_globally(opts.passopts.validate_globally); 87 | pass_options.set_optimize_level(opts.passopts.optimize_level as i32); 88 | pass_options.set_shrink_level(opts.passopts.shrink_level as i32); 89 | pass_options.set_traps_never_happen(opts.passopts.traps_never_happen); 90 | pass_options.set_low_memory_unused(opts.passopts.low_memory_unused); 91 | pass_options.set_fast_math(opts.passopts.fast_math); 92 | pass_options.set_zero_filled_memory(opts.passopts.zero_filled_memory); 93 | pass_options.set_debug_info(opts.passopts.debug_info); 94 | 95 | assert_eq!(check_pass_options_defaults_os(pass_options), true); 96 | 97 | Ok(()) 98 | } 99 | 100 | #[test] 101 | fn optimization_read_module_error_works() -> anyhow::Result<()> { 102 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 103 | let inpath = temp_dir.path().join("infile.wasm"); 104 | 105 | let file = File::create(&inpath)?; 106 | 107 | let mut buf_writer = BufWriter::new(&file); 108 | buf_writer.write_all(GARBAGE_FILE)?; 109 | 110 | let outpath = temp_dir.path().join("outfile.wasm"); 111 | 112 | let opts = OptimizationOptions::new_optimize_for_size(); 113 | let res = opts.run(inpath, outpath); 114 | 115 | assert!(res.err().unwrap().source().is_some()); 116 | 117 | Ok(()) 118 | } 119 | -------------------------------------------------------------------------------- /components/wasm-opt/src/builder.rs: -------------------------------------------------------------------------------- 1 | //! A builder API for `OptimizationOptions`. 2 | 3 | use crate::api::*; 4 | 5 | /// Builder methods. 6 | impl OptimizationOptions { 7 | /// Sets [`ReaderOptions::file_type`]. 8 | pub fn reader_file_type(&mut self, value: FileType) -> &mut Self { 9 | self.reader.file_type = value; 10 | self 11 | } 12 | 13 | /// Sets [`WriterOptions::file_type`]. 14 | pub fn writer_file_type(&mut self, value: FileType) -> &mut Self { 15 | self.writer.file_type = value; 16 | self 17 | } 18 | 19 | /// Sets [`OptimizationOptions::converge`]. 20 | pub fn set_converge(&mut self) -> &mut Self { 21 | self.converge = true; 22 | self 23 | } 24 | 25 | /// Sets [`InliningOptions::always_inline_max_size`]. 26 | pub fn always_inline_max_size(&mut self, value: u32) -> &mut Self { 27 | self.inlining.always_inline_max_size = value; 28 | self 29 | } 30 | 31 | /// Sets [`InliningOptions::one_caller_inline_max_size`]. 32 | pub fn one_caller_inline_max_size(&mut self, value: u32) -> &mut Self { 33 | self.inlining.one_caller_inline_max_size = value; 34 | self 35 | } 36 | 37 | /// Sets [`InliningOptions::flexible_inline_max_size`]. 38 | pub fn flexible_inline_max_size(&mut self, value: u32) -> &mut Self { 39 | self.inlining.flexible_inline_max_size = value; 40 | self 41 | } 42 | 43 | /// Sets [`InliningOptions::allow_functions_with_loops`]. 44 | pub fn allow_functions_with_loops(&mut self, value: bool) -> &mut Self { 45 | self.inlining.allow_functions_with_loops = value; 46 | self 47 | } 48 | 49 | /// Sets [`InliningOptions::partial_inlining_ifs`]. 50 | pub fn partial_inlining_ifs(&mut self, value: u32) -> &mut Self { 51 | self.inlining.partial_inlining_ifs = value; 52 | self 53 | } 54 | 55 | /// Sets [`PassOptions::validate`]. 56 | pub fn validate(&mut self, value: bool) -> &mut Self { 57 | self.passopts.validate = value; 58 | self 59 | } 60 | 61 | /// Sets [`PassOptions::validate_globally`]. 62 | pub fn validate_globally(&mut self, value: bool) -> &mut Self { 63 | self.passopts.validate_globally = value; 64 | self 65 | } 66 | 67 | /// Sets [`PassOptions::optimize_level`]. 68 | pub fn optimize_level(&mut self, value: OptimizeLevel) -> &mut Self { 69 | self.passopts.optimize_level = value; 70 | self 71 | } 72 | 73 | /// Sets [`PassOptions::shrink_level`]. 74 | pub fn shrink_level(&mut self, value: ShrinkLevel) -> &mut Self { 75 | self.passopts.shrink_level = value; 76 | self 77 | } 78 | 79 | /// Sets [`PassOptions::traps_never_happen`]. 80 | pub fn traps_never_happen(&mut self, value: bool) -> &mut Self { 81 | self.passopts.traps_never_happen = value; 82 | self 83 | } 84 | 85 | /// Sets [`PassOptions::low_memory_unused`]. 86 | pub fn low_memory_unused(&mut self, value: bool) -> &mut Self { 87 | self.passopts.low_memory_unused = value; 88 | self 89 | } 90 | 91 | /// Sets [`PassOptions::fast_math`]. 92 | pub fn fast_math(&mut self, value: bool) -> &mut Self { 93 | self.passopts.fast_math = value; 94 | self 95 | } 96 | 97 | /// Sets [`PassOptions::zero_filled_memory`]. 98 | pub fn zero_filled_memory(&mut self, value: bool) -> &mut Self { 99 | self.passopts.zero_filled_memory = value; 100 | self 101 | } 102 | 103 | /// Sets [`PassOptions::debug_info`]. 104 | pub fn debug_info(&mut self, value: bool) -> &mut Self { 105 | self.passopts.debug_info = value; 106 | self 107 | } 108 | 109 | /// Adds a pass argument to [`PassOptions::arguments`]. 110 | pub fn set_pass_arg(&mut self, key: &str, value: &str) -> &mut Self { 111 | self.passopts 112 | .arguments 113 | .insert(key.to_string(), value.to_string()); 114 | self 115 | } 116 | 117 | /// Sets [`Passes::add_default_passes`]. 118 | pub fn add_default_passes(&mut self, value: bool) -> &mut Self { 119 | self.passes.add_default_passes = value; 120 | self 121 | } 122 | 123 | /// Adds a pass to [`Passes::more_passes`]. 124 | pub fn add_pass(&mut self, value: Pass) -> &mut Self { 125 | self.passes.more_passes.push(value); 126 | self 127 | } 128 | 129 | /// Sets the baseline feature set to [`FeatureBaseline::MvpOnly`]. 130 | pub fn mvp_features_only(&mut self) -> &mut Self { 131 | self.features.baseline = FeatureBaseline::MvpOnly; 132 | self 133 | } 134 | 135 | /// Sets the baseline feature set to [`FeatureBaseline::All`]. 136 | pub fn all_features(&mut self) -> &mut Self { 137 | self.features.baseline = FeatureBaseline::All; 138 | self 139 | } 140 | 141 | /// Enables a feature. 142 | /// 143 | /// This adds the feature to [`Features::enabled`], and is equivalent to the 144 | /// `--enable-{feature}` command line arguments. 145 | pub fn enable_feature(&mut self, feature: Feature) -> &mut Self { 146 | self.features.enabled.insert(feature); 147 | self 148 | } 149 | 150 | /// Disables a feature. 151 | /// 152 | /// This adds the feature to [`Features::disabled`], and is equivalent to 153 | /// the `--disable-{feature}` command line arguments. 154 | pub fn disable_feature(&mut self, feature: Feature) -> &mut Self { 155 | self.features.disabled.insert(feature); 156 | self 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /components/wasm-opt-cxx-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Direct bindings to `wasm-opt`. 2 | //! 3 | //! These are bindings to `wasm-opt`, 4 | //! as built by the [`wasm-opt-sys`] crate. 5 | //! The bindings are created by the [`cxx`] crate, 6 | //! and all go through a custom C++ shim layer 7 | //! that provides a `cxx`-compatible C++ API. 8 | //! 9 | //! Most users will not want to use this crate directly, 10 | //! but instead the [`wasm-opt`] crate. 11 | //! 12 | //! [`wasm-opt-sys`]: https://docs.rs/wasm-opt-sys 13 | //! [`cxx`]: https://docs.rs/cxx 14 | //! [`wasm-opt`]: https://docs.rs/wasm-opt 15 | //! 16 | //! The version of `cxx` used by these bindings is 17 | //! reexported here. 18 | 19 | pub use cxx; 20 | 21 | // Establish linking with wasm_opt_sys, which contains no Rust code. 22 | extern crate wasm_opt_sys; 23 | 24 | #[cxx::bridge(namespace = "Colors")] 25 | pub mod colors { 26 | unsafe extern "C++" { 27 | include!("shims.h"); 28 | 29 | fn setEnabled(enabled: bool); 30 | } 31 | } 32 | 33 | #[cxx::bridge(namespace = "wasm_shims")] 34 | pub mod wasm { 35 | unsafe extern "C++" { 36 | include!("shims.h"); 37 | } 38 | 39 | unsafe extern "C++" { 40 | type Module; 41 | 42 | fn newModule() -> UniquePtr; 43 | 44 | fn validateWasm(wasm: Pin<&mut Module>) -> bool; 45 | } 46 | 47 | unsafe extern "C++" { 48 | type ModuleReader; 49 | 50 | fn newModuleReader() -> UniquePtr; 51 | 52 | fn setDebugInfo(self: Pin<&mut Self>, debug: bool); 53 | 54 | fn setDwarf(self: Pin<&mut Self>, dwarf: bool); 55 | 56 | fn readText( 57 | self: Pin<&mut Self>, 58 | filename: Pin<&mut CxxString>, 59 | wasm: Pin<&mut Module>, 60 | ) -> Result<()>; 61 | 62 | fn readBinary( 63 | self: Pin<&mut Self>, 64 | filename: Pin<&mut CxxString>, 65 | wasm: Pin<&mut Module>, 66 | sourceMapFilename: Pin<&mut CxxString>, 67 | ) -> Result<()>; 68 | 69 | fn read( 70 | self: Pin<&mut Self>, 71 | filename: Pin<&mut CxxString>, 72 | wasm: Pin<&mut Module>, 73 | sourceMapFilename: Pin<&mut CxxString>, 74 | ) -> Result<()>; 75 | } 76 | 77 | unsafe extern "C++" { 78 | type ModuleWriter; 79 | 80 | fn newModuleWriter() -> UniquePtr; 81 | 82 | fn setDebugInfo(self: Pin<&mut Self>, debug: bool); 83 | 84 | fn setSourceMapFilename(self: Pin<&mut Self>, source_map_filename: Pin<&mut CxxString>); 85 | 86 | fn setSourceMapUrl(self: Pin<&mut Self>, source_map_url: Pin<&mut CxxString>); 87 | 88 | fn writeText( 89 | self: Pin<&mut Self>, 90 | wasm: Pin<&mut Module>, 91 | filename: Pin<&mut CxxString>, 92 | ) -> Result<()>; 93 | 94 | fn writeBinary( 95 | self: Pin<&mut Self>, 96 | wasm: Pin<&mut Module>, 97 | filename: Pin<&mut CxxString>, 98 | ) -> Result<()>; 99 | } 100 | 101 | unsafe extern "C++" { 102 | fn getRegisteredNames() -> UniquePtr>; 103 | 104 | fn getPassDescription(name: Pin<&mut CxxString>) -> UniquePtr; 105 | 106 | fn isPassHidden(name: Pin<&mut CxxString>) -> bool; 107 | } 108 | 109 | unsafe extern "C++" { 110 | type InliningOptions; 111 | 112 | fn newInliningOptions() -> UniquePtr; 113 | 114 | fn setAlwaysInlineMaxSize(self: Pin<&mut Self>, size: u32); 115 | 116 | fn setOneCallerInlineMaxSize(self: Pin<&mut Self>, size: u32); 117 | 118 | fn setFlexibleInlineMaxSize(self: Pin<&mut Self>, size: u32); 119 | 120 | fn setAllowFunctionsWithLoops(self: Pin<&mut Self>, allow: bool); 121 | 122 | fn setPartialInliningIfs(self: Pin<&mut Self>, number: u32); 123 | } 124 | 125 | unsafe extern "C++" { 126 | type PassOptions; 127 | 128 | fn newPassOptions() -> UniquePtr; 129 | 130 | fn setValidate(self: Pin<&mut Self>, validate: bool); 131 | 132 | fn setValidateGlobally(self: Pin<&mut Self>, validate: bool); 133 | 134 | fn setOptimizeLevel(self: Pin<&mut Self>, level: i32); 135 | 136 | fn setShrinkLevel(self: Pin<&mut Self>, level: i32); 137 | 138 | fn setInliningOptions(self: Pin<&mut Self>, inlining: UniquePtr); 139 | 140 | fn setTrapsNeverHappen(self: Pin<&mut Self>, ignoreTraps: bool); 141 | 142 | fn setLowMemoryUnused(self: Pin<&mut Self>, memoryUnused: bool); 143 | 144 | fn setFastMath(self: Pin<&mut Self>, fastMath: bool); 145 | 146 | fn setZeroFilledMemory(self: Pin<&mut Self>, zeroFilledMemory: bool); 147 | 148 | fn setDebugInfo(self: Pin<&mut Self>, debugInfo: bool); 149 | 150 | fn setArguments(self: Pin<&mut Self>, key: Pin<&mut CxxString>, value: Pin<&mut CxxString>); 151 | } 152 | 153 | unsafe extern "C++" { 154 | type WasmFeatureSet; 155 | 156 | fn newFeatureSet() -> UniquePtr; 157 | 158 | fn setMVP(self: Pin<&mut Self>); 159 | 160 | fn setAll(self: Pin<&mut Self>); 161 | 162 | fn set(self: Pin<&mut Self>, feature: u32, val: bool); 163 | 164 | fn has(self: &Self, features: &WasmFeatureSet) -> bool; 165 | 166 | fn as_int(self: &Self) -> u32; 167 | 168 | fn getFeatureArray() -> UniquePtr>; 169 | 170 | fn applyFeatures( 171 | wasm: Pin<&mut Module>, 172 | enabled_features: UniquePtr, 173 | disabled_features: UniquePtr, 174 | ); 175 | } 176 | 177 | unsafe extern "C++" { 178 | type PassRunner<'wasm>; 179 | 180 | fn newPassRunner<'wasm>(wasm: Pin<&'wasm mut Module>) -> UniquePtr>; 181 | 182 | fn newPassRunnerWithOptions<'wasm>( 183 | wasm: Pin<&'wasm mut Module>, 184 | options: UniquePtr, 185 | ) -> UniquePtr>; 186 | 187 | fn add(self: Pin<&mut Self>, pass_name: Pin<&mut CxxString>); 188 | 189 | fn addDefaultOptimizationPasses(self: Pin<&mut Self>); 190 | 191 | fn run(self: Pin<&mut Self>); 192 | 193 | fn passRemovesDebugInfo(name: Pin<&mut CxxString>) -> bool; 194 | } 195 | 196 | unsafe extern "C++" { 197 | fn checkInliningOptionsDefaults(inlining_options: UniquePtr) -> bool; 198 | 199 | fn checkPassOptionsDefaults(pass_options: UniquePtr) -> bool; 200 | 201 | fn checkPassOptionsDefaultsOs(pass_options: UniquePtr) -> bool; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /components/wasm-opt/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Rust bindings to the `wasm-opt` WebAssembly optimizer. 2 | //! 3 | //! `wasm-opt` is a component of the [Binaryen] toolkit 4 | //! that optimizes [WebAssembly] modules. It is written 5 | //! in C++. 6 | //! 7 | //! [Binaryen]: https://github.com/WebAssembly/binaryen 8 | //! [WebAssembly]: https://webassembly.org/ 9 | //! 10 | //! This project provides a Rust crate that builds `wasm-opt` and: 11 | //! 12 | //! 1) makes its command-line interface installable via `cargo install`, 13 | //! 2) provides an API to access it programmatically. 14 | //! 15 | //! 16 | //! ## Installing the binary 17 | //! 18 | //! ```text 19 | //! cargo install wasm-opt --locked 20 | //! ``` 21 | //! 22 | //! It should behave exactly the same as `wasm-opt` installed from other sources. 23 | //! 24 | //! 25 | //! ## Using the library 26 | //! 27 | //! The crate provides an [`OptimizationOptions`] type that 28 | //! follows the builder pattern, with options that closely 29 | //! mirror the command line options of `wasm-opt`. Once built, 30 | //! call [`OptimizationOptions::run`] to load, optimize, and write 31 | //! the optimized module. 32 | //! 33 | //! ```no_run 34 | //! use wasm_opt::OptimizationOptions; 35 | //! 36 | //! let infile = "hello_world.wasm"; 37 | //! let outfile = "hello_world_optimized.wasm"; 38 | //! 39 | //! OptimizationOptions::new_optimize_for_size() 40 | //! .run(infile, outfile)?; 41 | //! 42 | //! # Ok::<(), anyhow::Error>(()) 43 | //! ``` 44 | //! 45 | //! There are constructors for all the typical optimization profiles: 46 | //! 47 | //! - [`OptimizationOptions::new_optimize_for_size`] · `-Os` or `-O` 48 | //! - [`OptimizationOptions::new_optimize_for_size_aggressively`] · `-Oz` 49 | //! - [`OptimizationOptions::new_opt_level_0`] · `-O0`, or no `-O*` argument. 50 | //! - [`OptimizationOptions::new_opt_level_1`] · `-O1` 51 | //! - [`OptimizationOptions::new_opt_level_2`] · `-O2` 52 | //! - [`OptimizationOptions::new_opt_level_3`] · `-O3` 53 | //! - [`OptimizationOptions::new_opt_level_4`] · `-O4` 54 | //! 55 | //! By default, the `run` method will read either binary `wasm` or text `wat` files, 56 | //! inspecting the first few bytes for the binary header and choosing as appropriate, 57 | //! and it will write a binary `wasm` file. 58 | //! This behavior can be changed with [`OptimizationOptions::reader_file_type`] 59 | //! and [`OptimizationOptions::writer_file_type`]. 60 | //! 61 | //! 62 | //! ## Enabling and disabling WASM features 63 | //! 64 | //! The WebAssembly specification has [optional features](https://webassembly.org/roadmap/), 65 | //! represeted by the [`Feature`] enum. 66 | //! The `Feature` variants link to the relevant specifications of each feature when known. 67 | //! `wasm-opt` can be configured with support for them individually using the 68 | //! [`OptimizationOptions::enable_feature`] and [`OptimizationOptions::disable_feature`] 69 | //! methods. 70 | //! 71 | //! By default Binaryen (and this crate) enables these common features by default: 72 | //! 73 | //! - [`Feature::SignExt`] 74 | //! - [`Feature::MutableGlobals`]. 75 | //! 76 | //! The original WebAssembly specification with no additional features is known 77 | //! as the _MVP_ specification. __To enable only the MVP features call 78 | //! [`OptimizationOptions::mvp_features_only`]__. 79 | //! 80 | //! After resetting to MVP features, additional calls to `enable_feature` will 81 | //! add features to the MVP feature set. 82 | //! 83 | //! 84 | //! ## Customizing passes 85 | //! 86 | //! All Binaryen optimization passes are represented in the [`Pass`] 87 | //! enum, and can be added to `OptimizationOptions` via [`OptimizationOptions::add_pass`]. 88 | //! These are added after the default set of passes, which are 89 | //! enabled by most `OptimizationOptions` constructors. The default passes 90 | //! can be disabled either with the [`OptimizationOptions::new_opt_level_0`] constructor, 91 | //! or by calling [`OptimizationOptions::add_default_passes`] 92 | //! with a `false` argument. 93 | //! 94 | //! ```no_run 95 | //! use wasm_opt::{OptimizationOptions, Pass}; 96 | //! 97 | //! let infile = "hello_world.wasm"; 98 | //! let outfile = "hello_world_optimized.wasm"; 99 | //! 100 | //! // Just run the inliner. 101 | //! OptimizationOptions::new_opt_level_0() 102 | //! .add_pass(Pass::InliningOptimizing) 103 | //! .run(infile, outfile)?; 104 | //! 105 | //! # Ok::<(), anyhow::Error>(()) 106 | //! ``` 107 | //! 108 | //! Note that while this crate exposes all Binaryen passes 109 | //! some may not make sense to actually use — Binaryen 110 | //! is a command-line oriented tool, and some passes are 111 | //! for debug purposes or print directly to the console. 112 | //! 113 | //! 114 | //! ## Integrating with existing tooling 115 | //! 116 | //! For ease of integration with tools that already use `wasm-opt` via CLI, this 117 | //! crate provides the [`integration`] module, which presents an API that is 118 | //! compatible with `std`s `Command`. This allows client code to use mostly the 119 | //! same code path for executing the `wasm-opt` CLI, and the crate-based API. 120 | //! 121 | //! 122 | //! ## Cargo features 123 | //! 124 | //! Enabled by default, the `dwarf` feature enables passes related to DWARF 125 | //! debug info. When enabled, this crate includes C++ code from the LLVM project. 126 | //! This can cause duplicate symbol linkage errors when _also_ linking to LLVM. 127 | //! When disabled, this code is not built, so can link successfully to LLVM, 128 | //! but the Binaryen DWARF passes will do nothing. 129 | 130 | // Most of the API surface is exported here. 131 | // 132 | // Many public methods are defined in other non-pub modules. 133 | pub use api::*; 134 | 135 | // Returned by the `run` method. 136 | pub use run::OptimizationError; 137 | 138 | // Easy integration with tools that already use `wasm-opt` via CLI. 139 | pub mod integration; 140 | 141 | // The "base" API. 142 | // 143 | // This API hides the `cxx` types, 144 | // but otherwise sticks closely to the Binaryen API. 145 | // 146 | // This is hidden because we don't need to commit to these low-level APIs, 147 | // but want to keep testing them from the `tests` folder. 148 | #[doc(hidden)] 149 | pub mod base; 150 | 151 | // Types and constructors used in the API. 152 | mod api; 153 | 154 | // A builder interface for `OptimizationOptions`. 155 | mod builder; 156 | 157 | // The list of optimization passes. 158 | mod passes; 159 | 160 | // Definitions of -O1, -O2, etc. 161 | mod profiles; 162 | 163 | // The list of wasm features. 164 | mod features; 165 | 166 | // The `run` method that re-implements the logic from `wasm-opt.cpp` 167 | // on top of `OptimizationOptions`. 168 | mod run; 169 | 170 | // A thin wrapper around `std::process::Command` that provides the unstable 171 | // `get_args` method. 172 | mod fake_command; 173 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.66" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "cc" 19 | version = "1.0.77" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4" 22 | dependencies = [ 23 | "jobserver", 24 | ] 25 | 26 | [[package]] 27 | name = "cfg-if" 28 | version = "1.0.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 31 | 32 | [[package]] 33 | name = "codespan-reporting" 34 | version = "0.11.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 37 | dependencies = [ 38 | "termcolor", 39 | "unicode-width", 40 | ] 41 | 42 | [[package]] 43 | name = "cxx" 44 | version = "1.0.83" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf" 47 | dependencies = [ 48 | "cc", 49 | "cxxbridge-flags", 50 | "cxxbridge-macro", 51 | "link-cplusplus", 52 | ] 53 | 54 | [[package]] 55 | name = "cxx-build" 56 | version = "1.0.83" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39" 59 | dependencies = [ 60 | "cc", 61 | "codespan-reporting", 62 | "once_cell", 63 | "proc-macro2", 64 | "quote", 65 | "scratch", 66 | "syn", 67 | ] 68 | 69 | [[package]] 70 | name = "cxxbridge-flags" 71 | version = "1.0.83" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12" 74 | 75 | [[package]] 76 | name = "cxxbridge-macro" 77 | version = "1.0.83" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" 80 | dependencies = [ 81 | "proc-macro2", 82 | "quote", 83 | "syn", 84 | ] 85 | 86 | [[package]] 87 | name = "fastrand" 88 | version = "1.8.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 91 | dependencies = [ 92 | "instant", 93 | ] 94 | 95 | [[package]] 96 | name = "heck" 97 | version = "0.4.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 100 | 101 | [[package]] 102 | name = "instant" 103 | version = "0.1.12" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 106 | dependencies = [ 107 | "cfg-if", 108 | ] 109 | 110 | [[package]] 111 | name = "jobserver" 112 | version = "0.1.25" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" 115 | dependencies = [ 116 | "libc", 117 | ] 118 | 119 | [[package]] 120 | name = "libc" 121 | version = "0.2.138" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" 124 | 125 | [[package]] 126 | name = "link-cplusplus" 127 | version = "1.0.7" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" 130 | dependencies = [ 131 | "cc", 132 | ] 133 | 134 | [[package]] 135 | name = "once_cell" 136 | version = "1.13.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" 139 | 140 | [[package]] 141 | name = "proc-macro2" 142 | version = "1.0.60" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 145 | dependencies = [ 146 | "unicode-ident", 147 | ] 148 | 149 | [[package]] 150 | name = "quote" 151 | version = "1.0.21" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 154 | dependencies = [ 155 | "proc-macro2", 156 | ] 157 | 158 | [[package]] 159 | name = "redox_syscall" 160 | version = "0.2.16" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 163 | dependencies = [ 164 | "bitflags", 165 | ] 166 | 167 | [[package]] 168 | name = "remove_dir_all" 169 | version = "0.5.3" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 172 | dependencies = [ 173 | "winapi", 174 | ] 175 | 176 | [[package]] 177 | name = "rustversion" 178 | version = "1.0.9" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 181 | 182 | [[package]] 183 | name = "scratch" 184 | version = "1.0.2" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" 187 | 188 | [[package]] 189 | name = "strum" 190 | version = "0.24.1" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 193 | 194 | [[package]] 195 | name = "strum_macros" 196 | version = "0.24.3" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 199 | dependencies = [ 200 | "heck", 201 | "proc-macro2", 202 | "quote", 203 | "rustversion", 204 | "syn", 205 | ] 206 | 207 | [[package]] 208 | name = "syn" 209 | version = "1.0.105" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" 212 | dependencies = [ 213 | "proc-macro2", 214 | "quote", 215 | "unicode-ident", 216 | ] 217 | 218 | [[package]] 219 | name = "tempfile" 220 | version = "3.3.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 223 | dependencies = [ 224 | "cfg-if", 225 | "fastrand", 226 | "libc", 227 | "redox_syscall", 228 | "remove_dir_all", 229 | "winapi", 230 | ] 231 | 232 | [[package]] 233 | name = "termcolor" 234 | version = "1.1.3" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 237 | dependencies = [ 238 | "winapi-util", 239 | ] 240 | 241 | [[package]] 242 | name = "thiserror" 243 | version = "1.0.37" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 246 | dependencies = [ 247 | "thiserror-impl", 248 | ] 249 | 250 | [[package]] 251 | name = "thiserror-impl" 252 | version = "1.0.37" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 255 | dependencies = [ 256 | "proc-macro2", 257 | "quote", 258 | "syn", 259 | ] 260 | 261 | [[package]] 262 | name = "unicode-ident" 263 | version = "1.0.5" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 266 | 267 | [[package]] 268 | name = "unicode-width" 269 | version = "0.1.10" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 272 | 273 | [[package]] 274 | name = "wasm-opt" 275 | version = "0.116.1" 276 | dependencies = [ 277 | "anyhow", 278 | "libc", 279 | "strum", 280 | "strum_macros", 281 | "tempfile", 282 | "thiserror", 283 | "wasm-opt-cxx-sys", 284 | "wasm-opt-sys", 285 | ] 286 | 287 | [[package]] 288 | name = "wasm-opt-cxx-sys" 289 | version = "0.116.0" 290 | dependencies = [ 291 | "anyhow", 292 | "cxx", 293 | "cxx-build", 294 | "wasm-opt-sys", 295 | ] 296 | 297 | [[package]] 298 | name = "wasm-opt-sys" 299 | version = "0.116.0" 300 | dependencies = [ 301 | "anyhow", 302 | "cc", 303 | "cxx", 304 | "cxx-build", 305 | ] 306 | 307 | [[package]] 308 | name = "winapi" 309 | version = "0.3.9" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 312 | dependencies = [ 313 | "winapi-i686-pc-windows-gnu", 314 | "winapi-x86_64-pc-windows-gnu", 315 | ] 316 | 317 | [[package]] 318 | name = "winapi-i686-pc-windows-gnu" 319 | version = "0.4.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 322 | 323 | [[package]] 324 | name = "winapi-util" 325 | version = "0.1.5" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 328 | dependencies = [ 329 | "winapi", 330 | ] 331 | 332 | [[package]] 333 | name = "winapi-x86_64-pc-windows-gnu" 334 | version = "0.4.0" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 337 | -------------------------------------------------------------------------------- /components/conformance-tests/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anyhow" 7 | version = "1.0.65" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" 10 | 11 | [[package]] 12 | name = "bitflags" 13 | version = "1.3.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 16 | 17 | [[package]] 18 | name = "cc" 19 | version = "1.0.73" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 22 | dependencies = [ 23 | "jobserver", 24 | ] 25 | 26 | [[package]] 27 | name = "cfg-if" 28 | version = "1.0.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 31 | 32 | [[package]] 33 | name = "codespan-reporting" 34 | version = "0.11.1" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 37 | dependencies = [ 38 | "termcolor", 39 | "unicode-width", 40 | ] 41 | 42 | [[package]] 43 | name = "conformance-tests" 44 | version = "0.116.0" 45 | dependencies = [ 46 | "anyhow", 47 | "tempfile", 48 | "wasm-opt", 49 | ] 50 | 51 | [[package]] 52 | name = "cxx" 53 | version = "1.0.80" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" 56 | dependencies = [ 57 | "cc", 58 | "cxxbridge-flags", 59 | "cxxbridge-macro", 60 | "link-cplusplus", 61 | ] 62 | 63 | [[package]] 64 | name = "cxx-build" 65 | version = "1.0.80" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" 68 | dependencies = [ 69 | "cc", 70 | "codespan-reporting", 71 | "once_cell", 72 | "proc-macro2", 73 | "quote", 74 | "scratch", 75 | "syn", 76 | ] 77 | 78 | [[package]] 79 | name = "cxxbridge-flags" 80 | version = "1.0.80" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" 83 | 84 | [[package]] 85 | name = "cxxbridge-macro" 86 | version = "1.0.80" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" 89 | dependencies = [ 90 | "proc-macro2", 91 | "quote", 92 | "syn", 93 | ] 94 | 95 | [[package]] 96 | name = "fastrand" 97 | version = "1.8.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 100 | dependencies = [ 101 | "instant", 102 | ] 103 | 104 | [[package]] 105 | name = "heck" 106 | version = "0.4.0" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 109 | 110 | [[package]] 111 | name = "instant" 112 | version = "0.1.12" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 115 | dependencies = [ 116 | "cfg-if", 117 | ] 118 | 119 | [[package]] 120 | name = "jobserver" 121 | version = "0.1.24" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" 124 | dependencies = [ 125 | "libc", 126 | ] 127 | 128 | [[package]] 129 | name = "libc" 130 | version = "0.2.132" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" 133 | 134 | [[package]] 135 | name = "link-cplusplus" 136 | version = "1.0.7" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" 139 | dependencies = [ 140 | "cc", 141 | ] 142 | 143 | [[package]] 144 | name = "once_cell" 145 | version = "1.14.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" 148 | 149 | [[package]] 150 | name = "proc-macro2" 151 | version = "1.0.60" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" 154 | dependencies = [ 155 | "unicode-ident", 156 | ] 157 | 158 | [[package]] 159 | name = "quote" 160 | version = "1.0.21" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 163 | dependencies = [ 164 | "proc-macro2", 165 | ] 166 | 167 | [[package]] 168 | name = "redox_syscall" 169 | version = "0.2.16" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 172 | dependencies = [ 173 | "bitflags", 174 | ] 175 | 176 | [[package]] 177 | name = "remove_dir_all" 178 | version = "0.5.3" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 181 | dependencies = [ 182 | "winapi", 183 | ] 184 | 185 | [[package]] 186 | name = "rustversion" 187 | version = "1.0.9" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" 190 | 191 | [[package]] 192 | name = "scratch" 193 | version = "1.0.2" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" 196 | 197 | [[package]] 198 | name = "strum" 199 | version = "0.24.1" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" 202 | 203 | [[package]] 204 | name = "strum_macros" 205 | version = "0.24.3" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" 208 | dependencies = [ 209 | "heck", 210 | "proc-macro2", 211 | "quote", 212 | "rustversion", 213 | "syn", 214 | ] 215 | 216 | [[package]] 217 | name = "syn" 218 | version = "1.0.99" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" 221 | dependencies = [ 222 | "proc-macro2", 223 | "quote", 224 | "unicode-ident", 225 | ] 226 | 227 | [[package]] 228 | name = "tempfile" 229 | version = "3.3.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 232 | dependencies = [ 233 | "cfg-if", 234 | "fastrand", 235 | "libc", 236 | "redox_syscall", 237 | "remove_dir_all", 238 | "winapi", 239 | ] 240 | 241 | [[package]] 242 | name = "termcolor" 243 | version = "1.1.3" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 246 | dependencies = [ 247 | "winapi-util", 248 | ] 249 | 250 | [[package]] 251 | name = "thiserror" 252 | version = "1.0.35" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" 255 | dependencies = [ 256 | "thiserror-impl", 257 | ] 258 | 259 | [[package]] 260 | name = "thiserror-impl" 261 | version = "1.0.35" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" 264 | dependencies = [ 265 | "proc-macro2", 266 | "quote", 267 | "syn", 268 | ] 269 | 270 | [[package]] 271 | name = "unicode-ident" 272 | version = "1.0.4" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" 275 | 276 | [[package]] 277 | name = "unicode-width" 278 | version = "0.1.10" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 281 | 282 | [[package]] 283 | name = "wasm-opt" 284 | version = "0.116.1" 285 | dependencies = [ 286 | "anyhow", 287 | "libc", 288 | "strum", 289 | "strum_macros", 290 | "tempfile", 291 | "thiserror", 292 | "wasm-opt-cxx-sys", 293 | "wasm-opt-sys", 294 | ] 295 | 296 | [[package]] 297 | name = "wasm-opt-cxx-sys" 298 | version = "0.116.0" 299 | dependencies = [ 300 | "anyhow", 301 | "cxx", 302 | "cxx-build", 303 | "wasm-opt-sys", 304 | ] 305 | 306 | [[package]] 307 | name = "wasm-opt-sys" 308 | version = "0.116.0" 309 | dependencies = [ 310 | "anyhow", 311 | "cc", 312 | "cxx", 313 | "cxx-build", 314 | ] 315 | 316 | [[package]] 317 | name = "winapi" 318 | version = "0.3.9" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 321 | dependencies = [ 322 | "winapi-i686-pc-windows-gnu", 323 | "winapi-x86_64-pc-windows-gnu", 324 | ] 325 | 326 | [[package]] 327 | name = "winapi-i686-pc-windows-gnu" 328 | version = "0.4.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 331 | 332 | [[package]] 333 | name = "winapi-util" 334 | version = "0.1.5" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 337 | dependencies = [ 338 | "winapi", 339 | ] 340 | 341 | [[package]] 342 | name = "winapi-x86_64-pc-windows-gnu" 343 | version = "0.4.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 346 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /components/wasm-opt/src/base.rs: -------------------------------------------------------------------------------- 1 | use wasm_opt_cxx_sys as wocxx; 2 | use wocxx::cxx::let_cxx_string; 3 | use wocxx::{colors, cxx, wasm}; 4 | 5 | use std::path::Path; 6 | use strum_macros::EnumIter; 7 | 8 | #[cfg(unix)] 9 | use std::os::unix::ffi::OsStrExt; 10 | 11 | pub struct Module(cxx::UniquePtr); 12 | 13 | impl Module { 14 | pub fn new() -> Module { 15 | Module(wasm::newModule()) 16 | } 17 | 18 | pub fn apply_features(&mut self, enabled_features: FeatureSet, disabled_features: FeatureSet) { 19 | wasm::applyFeatures(self.0.pin_mut(), enabled_features.0, disabled_features.0); 20 | } 21 | } 22 | 23 | pub struct ModuleReader(cxx::UniquePtr); 24 | 25 | impl ModuleReader { 26 | pub fn new() -> ModuleReader { 27 | ModuleReader(wasm::newModuleReader()) 28 | } 29 | 30 | pub fn set_debug_info(&mut self, debug: bool) { 31 | let this = self.0.pin_mut(); 32 | this.setDebugInfo(debug); 33 | } 34 | 35 | pub fn set_dwarf(&mut self, dwarf: bool) { 36 | let this = self.0.pin_mut(); 37 | this.setDwarf(dwarf); 38 | } 39 | 40 | pub fn read_text(&mut self, path: &Path, wasm: &mut Module) -> Result<(), cxx::Exception> { 41 | let path = convert_path_to_u8(path)?; 42 | let_cxx_string!(path = path); 43 | 44 | let this = self.0.pin_mut(); 45 | this.readText(path, wasm.0.pin_mut()) 46 | } 47 | 48 | pub fn read_binary( 49 | &mut self, 50 | path: &Path, 51 | wasm: &mut Module, 52 | source_map_filename: Option<&Path>, 53 | ) -> Result<(), cxx::Exception> { 54 | let path = convert_path_to_u8(path)?; 55 | let_cxx_string!(path = path); 56 | 57 | let source_map_filename = source_map_filename.unwrap_or(&Path::new("")); 58 | let source_map_filename = convert_path_to_u8(source_map_filename)?; 59 | let_cxx_string!(source_map_filename = source_map_filename); 60 | 61 | let this = self.0.pin_mut(); 62 | this.readBinary(path, wasm.0.pin_mut(), source_map_filename) 63 | } 64 | 65 | pub fn read( 66 | &mut self, 67 | path: &Path, 68 | wasm: &mut Module, 69 | source_map_filename: Option<&Path>, 70 | ) -> Result<(), cxx::Exception> { 71 | let path = convert_path_to_u8(path)?; 72 | let_cxx_string!(path = path); 73 | 74 | let source_map_filename = source_map_filename.unwrap_or(&Path::new("")); 75 | let source_map_filename = convert_path_to_u8(source_map_filename)?; 76 | let_cxx_string!(source_map_filename = source_map_filename); 77 | 78 | let this = self.0.pin_mut(); 79 | this.read(path, wasm.0.pin_mut(), source_map_filename) 80 | } 81 | } 82 | 83 | pub struct ModuleWriter(cxx::UniquePtr); 84 | 85 | impl ModuleWriter { 86 | pub fn new() -> ModuleWriter { 87 | ModuleWriter(wasm::newModuleWriter()) 88 | } 89 | 90 | pub fn set_debug_info(&mut self, debug: bool) { 91 | let this = self.0.pin_mut(); 92 | this.setDebugInfo(debug); 93 | } 94 | 95 | pub fn set_source_map_filename( 96 | &mut self, 97 | source_map_filename: &Path, 98 | ) -> Result<(), cxx::Exception> { 99 | let source_map_filename = convert_path_to_u8(source_map_filename)?; 100 | let_cxx_string!(source_map_filename = source_map_filename); 101 | 102 | let this = self.0.pin_mut(); 103 | this.setSourceMapFilename(source_map_filename); 104 | 105 | Ok(()) 106 | } 107 | 108 | pub fn set_source_map_url(&mut self, source_map_url: &str) { 109 | let_cxx_string!(source_map_url = source_map_url); 110 | 111 | let this = self.0.pin_mut(); 112 | this.setSourceMapUrl(source_map_url); 113 | } 114 | 115 | pub fn write_text(&mut self, wasm: &mut Module, path: &Path) -> Result<(), cxx::Exception> { 116 | colors::setEnabled(false); 117 | 118 | let path = convert_path_to_u8(path)?; 119 | let_cxx_string!(path = path); 120 | 121 | let this = self.0.pin_mut(); 122 | this.writeText(wasm.0.pin_mut(), path) 123 | } 124 | 125 | pub fn write_binary(&mut self, wasm: &mut Module, path: &Path) -> Result<(), cxx::Exception> { 126 | let path = convert_path_to_u8(path)?; 127 | let_cxx_string!(path = path); 128 | 129 | let this = self.0.pin_mut(); 130 | this.writeBinary(wasm.0.pin_mut(), path) 131 | } 132 | } 133 | 134 | pub mod pass_registry { 135 | use wasm_opt_cxx_sys as wocxx; 136 | use wocxx::cxx::let_cxx_string; 137 | use wocxx::wasm; 138 | 139 | pub fn get_registered_names() -> Vec { 140 | let names = wasm::getRegisteredNames(); 141 | 142 | let name_vec: Vec = names 143 | .iter() 144 | .map(|name| name.to_string_lossy().into_owned()) 145 | .collect(); 146 | 147 | name_vec 148 | } 149 | 150 | /// Aborts if `name` is invalid. 151 | pub fn get_pass_description(name: &str) -> String { 152 | let_cxx_string!(name = name); 153 | 154 | let description = wasm::getPassDescription(name); 155 | let description = description.as_ref().expect("non-null"); 156 | 157 | description.to_str().expect("utf8").to_string() 158 | } 159 | 160 | /// Aborts if `name` is invalid. 161 | pub fn is_pass_hidden(name: &str) -> bool { 162 | let_cxx_string!(name = name); 163 | 164 | wasm::isPassHidden(name) 165 | } 166 | } 167 | 168 | pub struct InliningOptions(cxx::UniquePtr); 169 | 170 | impl InliningOptions { 171 | pub fn new() -> InliningOptions { 172 | InliningOptions(wasm::newInliningOptions()) 173 | } 174 | 175 | pub fn set_always_inline_max_size(&mut self, size: u32) { 176 | let this = self.0.pin_mut(); 177 | this.setAlwaysInlineMaxSize(size); 178 | } 179 | 180 | pub fn set_one_caller_inline_max_size(&mut self, size: u32) { 181 | let this = self.0.pin_mut(); 182 | this.setOneCallerInlineMaxSize(size); 183 | } 184 | 185 | pub fn set_flexible_inline_max_size(&mut self, size: u32) { 186 | let this = self.0.pin_mut(); 187 | this.setFlexibleInlineMaxSize(size); 188 | } 189 | 190 | pub fn set_allow_functions_with_loops(&mut self, allow: bool) { 191 | let this = self.0.pin_mut(); 192 | this.setAllowFunctionsWithLoops(allow); 193 | } 194 | 195 | pub fn set_partial_inlining_ifs(&mut self, number: u32) { 196 | let this = self.0.pin_mut(); 197 | this.setPartialInliningIfs(number); 198 | } 199 | } 200 | 201 | pub struct PassOptions(cxx::UniquePtr); 202 | 203 | impl PassOptions { 204 | pub fn new() -> PassOptions { 205 | PassOptions(wasm::newPassOptions()) 206 | } 207 | 208 | pub fn set_validate(&mut self, validate: bool) { 209 | let this = self.0.pin_mut(); 210 | this.setValidate(validate); 211 | } 212 | 213 | pub fn set_validate_globally(&mut self, validate: bool) { 214 | let this = self.0.pin_mut(); 215 | this.setValidateGlobally(validate); 216 | } 217 | 218 | pub fn set_optimize_level(&mut self, level: i32) { 219 | let this = self.0.pin_mut(); 220 | this.setOptimizeLevel(level); 221 | } 222 | 223 | pub fn set_shrink_level(&mut self, level: i32) { 224 | let this = self.0.pin_mut(); 225 | this.setShrinkLevel(level); 226 | } 227 | 228 | pub fn set_inlining_options(&mut self, inlining: InliningOptions) { 229 | let this = self.0.pin_mut(); 230 | this.setInliningOptions(inlining.0); 231 | } 232 | 233 | pub fn set_traps_never_happen(&mut self, ignore_traps: bool) { 234 | let this = self.0.pin_mut(); 235 | this.setTrapsNeverHappen(ignore_traps); 236 | } 237 | 238 | pub fn set_low_memory_unused(&mut self, memory_unused: bool) { 239 | let this = self.0.pin_mut(); 240 | this.setLowMemoryUnused(memory_unused); 241 | } 242 | 243 | pub fn set_fast_math(&mut self, fast_math: bool) { 244 | let this = self.0.pin_mut(); 245 | this.setFastMath(fast_math); 246 | } 247 | 248 | pub fn set_zero_filled_memory(&mut self, zero_filled_memory: bool) { 249 | let this = self.0.pin_mut(); 250 | this.setZeroFilledMemory(zero_filled_memory); 251 | } 252 | 253 | pub fn set_debug_info(&mut self, debug_info: bool) { 254 | let this = self.0.pin_mut(); 255 | this.setDebugInfo(debug_info); 256 | } 257 | 258 | pub fn set_arguments(&mut self, key: &str, value: &str) { 259 | let_cxx_string!(key = key); 260 | let_cxx_string!(value = value); 261 | 262 | let this = self.0.pin_mut(); 263 | this.setArguments(key, value); 264 | } 265 | } 266 | 267 | pub struct FeatureSet(cxx::UniquePtr); 268 | 269 | impl FeatureSet { 270 | pub fn new() -> FeatureSet { 271 | FeatureSet(wasm::newFeatureSet()) 272 | } 273 | 274 | pub fn set_mvp(&mut self) { 275 | let this = self.0.pin_mut(); 276 | this.setMVP(); 277 | } 278 | 279 | pub fn set_all(&mut self) { 280 | let this = self.0.pin_mut(); 281 | this.setAll(); 282 | } 283 | 284 | pub fn set(&mut self, feature: Feature, val: bool) { 285 | let this = self.0.pin_mut(); 286 | this.set(feature as u32, val); 287 | } 288 | 289 | pub fn has(&self, features: &FeatureSet) -> bool { 290 | //let this = self.0.pin(); 291 | //let other = features.0.pin(); 292 | self.0.has(&*features.0) 293 | } 294 | 295 | pub fn as_int(&self) -> u32 { 296 | self.0.as_int() 297 | } 298 | } 299 | 300 | pub fn get_feature_array() -> Vec { 301 | let f = wasm::getFeatureArray(); 302 | 303 | let feature_vec: Vec = f.iter().map(|f| *f).collect(); 304 | 305 | feature_vec 306 | } 307 | 308 | #[derive(Copy, Clone, Debug, EnumIter)] 309 | pub enum Feature { 310 | None = 0, 311 | Atomics = 1 << 0, 312 | MutableGlobals = 1 << 1, 313 | TruncSat = 1 << 2, 314 | Simd = 1 << 3, 315 | BulkMemory = 1 << 4, 316 | SignExt = 1 << 5, 317 | ExceptionHandling = 1 << 6, 318 | TailCall = 1 << 7, 319 | ReferenceTypes = 1 << 8, 320 | Multivalue = 1 << 9, 321 | Gc = 1 << 10, 322 | Memory64 = 1 << 11, 323 | RelaxedSimd = 1 << 12, 324 | ExtendedConst = 1 << 13, 325 | Strings = 1 << 14, 326 | MultiMemory = 1 << 15, 327 | // MVP has the same value as None. 328 | // Mvp = 0, 329 | Default = 1 << 5 | 1 << 1, // SignExt | MutableGlobals, 330 | // GCNNLocals are opt-in: merely asking for "All" does not apply them. To 331 | // get all possible values use AllPossible. See setAll() below for more 332 | // details. 333 | All = (1 << 16) - 1, 334 | } 335 | 336 | pub struct PassRunner<'wasm>(cxx::UniquePtr>); 337 | 338 | impl<'wasm> PassRunner<'wasm> { 339 | pub fn new(wasm: &'wasm mut Module) -> PassRunner<'wasm> { 340 | let wasm = wasm.0.pin_mut(); 341 | PassRunner(wasm::newPassRunner(wasm)) 342 | } 343 | 344 | pub fn new_with_options(wasm: &'wasm mut Module, options: PassOptions) -> PassRunner<'wasm> { 345 | let wasm = wasm.0.pin_mut(); 346 | PassRunner(wasm::newPassRunnerWithOptions(wasm, options.0)) 347 | } 348 | 349 | pub fn add(&mut self, pass_name: &str) { 350 | let_cxx_string!(pass_name = pass_name); 351 | 352 | let this = self.0.pin_mut(); 353 | this.add(pass_name); 354 | } 355 | 356 | pub fn add_default_optimization_passes(&mut self) { 357 | let this = self.0.pin_mut(); 358 | this.addDefaultOptimizationPasses(); 359 | } 360 | 361 | pub fn run(&mut self) { 362 | let this = self.0.pin_mut(); 363 | this.run(); 364 | } 365 | 366 | pub fn pass_removes_debug_info(name: &str) -> bool { 367 | let_cxx_string!(name = name); 368 | 369 | wasm::passRemovesDebugInfo(name) 370 | } 371 | } 372 | 373 | pub fn validate_wasm(wasm: &mut Module) -> bool { 374 | wasm::validateWasm(wasm.0.pin_mut()) 375 | } 376 | 377 | pub fn check_inlining_options_defaults(inlining_options: InliningOptions) -> bool { 378 | wasm::checkInliningOptionsDefaults(inlining_options.0) 379 | } 380 | 381 | pub fn check_pass_options_defaults(pass_options: PassOptions) -> bool { 382 | wasm::checkPassOptionsDefaults(pass_options.0) 383 | } 384 | 385 | pub fn check_pass_options_defaults_os(pass_options: PassOptions) -> bool { 386 | wasm::checkPassOptionsDefaultsOs(pass_options.0) 387 | } 388 | 389 | // FIXME binaryen unicode path handling is broken on windows 390 | fn convert_path_to_u8(path: &Path) -> Result<&[u8], cxx::Exception> { 391 | #[cfg(unix)] 392 | let path = path.as_os_str().as_bytes(); 393 | 394 | #[cfg(not(unix))] 395 | let path = path.to_str().expect("utf8").as_bytes(); 396 | 397 | Ok(path) 398 | } 399 | -------------------------------------------------------------------------------- /components/wasm-opt-cxx-sys/src/shims.h: -------------------------------------------------------------------------------- 1 | #ifndef wasmopt_shims_h 2 | #define wasmopt_shims_h 3 | 4 | #include "pass.h" 5 | #include "wasm-io.h" 6 | #include "support/colors.h" 7 | #include "wasm-validator.h" 8 | #include "wasm-features.h" 9 | 10 | #include // runtime_error 11 | #include // unique_ptr 12 | 13 | namespace rust::behavior { 14 | template 15 | static void trycatch(Try&& func, Fail&& fail) noexcept try { 16 | func(); 17 | } catch (const std::exception& e) { 18 | fail(e.what()); 19 | } catch (const wasm::ParseException& e) { 20 | Colors::setEnabled(false); 21 | std::ostringstream buf; 22 | e.dump(buf); 23 | fail(buf.str()); 24 | } catch (const wasm::MapParseException& e) { 25 | Colors::setEnabled(false); 26 | std::ostringstream buf; 27 | e.dump(buf); 28 | fail(buf.str()); 29 | } 30 | } 31 | 32 | namespace wasm_shims { 33 | typedef wasm::Module Module; 34 | 35 | std::unique_ptr newModule() { 36 | return std::make_unique(); 37 | } 38 | 39 | bool validateWasm(wasm::Module& wasm) { 40 | wasm::WasmValidator v; 41 | 42 | return v.validate(wasm); 43 | } 44 | } 45 | 46 | namespace wasm_shims { 47 | struct ModuleReader { 48 | wasm::ModuleReader inner; 49 | 50 | void setDebugInfo(bool debug) { 51 | inner.setDebugInfo(debug); 52 | } 53 | 54 | void setDwarf(bool dwarf) { 55 | inner.setDWARF(dwarf); 56 | } 57 | 58 | void readText(std::string& filename, Module& wasm) { 59 | inner.readText(std::move(filename), wasm); 60 | } 61 | 62 | void readBinary(std::string& filename, 63 | Module& wasm, 64 | std::string& sourceMapFilename) { 65 | inner.readBinary(std::move(filename), 66 | wasm, 67 | std::move(sourceMapFilename)); 68 | } 69 | 70 | void read(std::string& filename, 71 | Module& wasm, 72 | std::string& sourceMapFilename) { 73 | inner.read(std::move(filename), 74 | wasm, 75 | std::move(sourceMapFilename)); 76 | } 77 | }; 78 | 79 | std::unique_ptr newModuleReader() { 80 | return std::make_unique(); 81 | } 82 | } 83 | 84 | namespace wasm_shims { 85 | struct ModuleWriter { 86 | wasm::ModuleWriter inner; 87 | 88 | void setDebugInfo(bool debug) { 89 | inner.setDebugInfo(debug); 90 | } 91 | 92 | void setSourceMapFilename(std::string& source_map_filename) { 93 | inner.setSourceMapFilename(std::move(source_map_filename)); 94 | } 95 | 96 | void setSourceMapUrl(std::string& source_map_url) { 97 | inner.setSourceMapUrl(std::move(source_map_url)); 98 | } 99 | 100 | void writeText(Module& wasm, 101 | std::string& filename) { 102 | inner.writeText(wasm, std::move(filename)); 103 | } 104 | 105 | void writeBinary(Module& wasm, 106 | std::string& filename) { 107 | inner.writeBinary(wasm, std::move(filename)); 108 | } 109 | }; 110 | 111 | std::unique_ptr newModuleWriter() { 112 | return std::make_unique(); 113 | } 114 | } 115 | 116 | namespace wasm_shims { 117 | std::unique_ptr> getRegisteredNames() { 118 | auto r = wasm::PassRegistry::get(); 119 | return std::make_unique>(r->getRegisteredNames()); 120 | } 121 | 122 | std::unique_ptr getPassDescription(std::string& name) { 123 | auto r = wasm::PassRegistry::get(); 124 | return std::make_unique(r->getPassDescription(std::move(name))); 125 | } 126 | 127 | bool isPassHidden(std::string& name) { 128 | auto r = wasm::PassRegistry::get(); 129 | return r->isPassHidden(std::move(name)); 130 | } 131 | } 132 | 133 | namespace wasm_shims { 134 | struct InliningOptions { 135 | wasm::InliningOptions inner; 136 | 137 | void setAlwaysInlineMaxSize(uint32_t size) { 138 | inner.alwaysInlineMaxSize = size; 139 | } 140 | 141 | void setOneCallerInlineMaxSize(uint32_t size) { 142 | inner.oneCallerInlineMaxSize = size; 143 | } 144 | 145 | void setFlexibleInlineMaxSize(uint32_t size) { 146 | inner.flexibleInlineMaxSize = size; 147 | } 148 | 149 | void setAllowFunctionsWithLoops(bool allow) { 150 | inner.allowFunctionsWithLoops = allow; 151 | } 152 | 153 | void setPartialInliningIfs(uint32_t number) { 154 | inner.partialInliningIfs = number; 155 | } 156 | }; 157 | 158 | std::unique_ptr newInliningOptions() { 159 | return std::make_unique(); 160 | } 161 | } 162 | 163 | namespace wasm_shims { 164 | struct PassOptions { 165 | wasm::PassOptions inner; 166 | 167 | void setValidate(bool validate) { 168 | inner.validate = validate; 169 | } 170 | 171 | void setValidateGlobally(bool validate) { 172 | inner.validateGlobally = validate; 173 | } 174 | 175 | void setOptimizeLevel(int32_t level) { 176 | inner.optimizeLevel = level; 177 | } 178 | 179 | void setShrinkLevel(int32_t level) { 180 | inner.shrinkLevel = level; 181 | } 182 | 183 | void setInliningOptions(std::unique_ptr inlining) { 184 | inner.inlining = inlining->inner; 185 | } 186 | 187 | void setTrapsNeverHappen(bool ignoreTraps) { 188 | inner.trapsNeverHappen = ignoreTraps; 189 | } 190 | 191 | void setLowMemoryUnused(bool memoryUnused) { 192 | inner.lowMemoryUnused = memoryUnused; 193 | } 194 | 195 | void setFastMath(bool fastMath) { 196 | inner.fastMath = fastMath; 197 | } 198 | 199 | void setZeroFilledMemory(bool zeroFilledMemory) { 200 | inner.zeroFilledMemory = zeroFilledMemory; 201 | } 202 | 203 | void setDebugInfo(bool debugInfo) { 204 | inner.debugInfo = debugInfo; 205 | } 206 | 207 | void setArguments(std::string& key, std::string& value) { 208 | inner.arguments[std::move(key)] = std::move(value); 209 | } 210 | }; 211 | 212 | std::unique_ptr newPassOptions() { 213 | return std::make_unique(); 214 | } 215 | } 216 | 217 | namespace wasm_shims { 218 | struct WasmFeatureSet { 219 | wasm::FeatureSet inner; 220 | 221 | void setMVP() { 222 | inner.setMVP(); 223 | } 224 | 225 | void setAll() { 226 | inner.setAll(); 227 | } 228 | 229 | void set(uint32_t feature, bool val) { 230 | inner.set(feature, val); 231 | } 232 | 233 | bool has(const WasmFeatureSet& features) const { 234 | return inner.has(features.inner); 235 | } 236 | 237 | uint32_t as_int() const { 238 | return inner; 239 | } 240 | }; 241 | 242 | std::unique_ptr newFeatureSet() { 243 | return std::make_unique(); 244 | } 245 | 246 | std::unique_ptr> getFeatureArray() { 247 | std::vector f; 248 | 249 | f.push_back(wasm::FeatureSet::Feature::None); 250 | f.push_back(wasm::FeatureSet::Feature::Atomics); 251 | f.push_back(wasm::FeatureSet::Feature::MutableGlobals); 252 | f.push_back(wasm::FeatureSet::Feature::TruncSat); 253 | f.push_back(wasm::FeatureSet::Feature::SIMD); 254 | f.push_back(wasm::FeatureSet::Feature::BulkMemory); 255 | f.push_back(wasm::FeatureSet::Feature::SignExt); 256 | f.push_back(wasm::FeatureSet::Feature::ExceptionHandling); 257 | f.push_back(wasm::FeatureSet::Feature::TailCall); 258 | f.push_back(wasm::FeatureSet::Feature::ReferenceTypes); 259 | f.push_back(wasm::FeatureSet::Feature::Multivalue); 260 | f.push_back(wasm::FeatureSet::Feature::GC); 261 | f.push_back(wasm::FeatureSet::Feature::Memory64); 262 | f.push_back(wasm::FeatureSet::Feature::RelaxedSIMD); 263 | f.push_back(wasm::FeatureSet::Feature::ExtendedConst); 264 | f.push_back(wasm::FeatureSet::Feature::Strings); 265 | f.push_back(wasm::FeatureSet::Feature::MultiMemory); 266 | // This is not part of the Rust API because it has the same value as None. 267 | // f.push_back(wasm::FeatureSet::Feature::MVP); 268 | f.push_back(wasm::FeatureSet::Feature::Default); 269 | f.push_back(wasm::FeatureSet::Feature::All); 270 | 271 | return std::make_unique>(f); 272 | } 273 | 274 | void applyFeatures(wasm::Module& wasm, std::unique_ptr enabledFeatures, std::unique_ptr disabledFeatures) { 275 | wasm.features.enable(enabledFeatures->inner); 276 | wasm.features.disable(disabledFeatures->inner); 277 | } 278 | } 279 | 280 | namespace wasm_shims { 281 | struct PassRunner { 282 | wasm::PassRunner inner; 283 | 284 | PassRunner(Module* wasm) : inner(wasm::PassRunner(wasm)) {} 285 | PassRunner(Module* wasm, PassOptions options) : inner(wasm::PassRunner(wasm, options.inner)) {} 286 | 287 | void add(std::string& passName) { 288 | inner.add(std::move(passName)); 289 | } 290 | 291 | void addDefaultOptimizationPasses() { 292 | inner.addDefaultOptimizationPasses(); 293 | } 294 | 295 | void run() { 296 | inner.run(); 297 | } 298 | }; 299 | 300 | std::unique_ptr newPassRunner(Module& wasm) { 301 | return std::make_unique(&wasm); 302 | } 303 | 304 | std::unique_ptr newPassRunnerWithOptions(Module& wasm, std::unique_ptr options) { 305 | return std::make_unique(&wasm, *options); 306 | } 307 | 308 | bool passRemovesDebugInfo(std::string& name) { 309 | return wasm::PassRunner::passRemovesDebugInfo(std::move(name)); 310 | } 311 | } 312 | 313 | namespace wasm_shims { 314 | bool checkInliningOptionsDefaults(std::unique_ptr inlining) { 315 | auto inliningOptionsDefaults = wasm_shims::newInliningOptions(); 316 | 317 | // The size assertion will fail when `InliningOptions` fields change, 318 | // which indicates the current test need to be updated. 319 | assert(sizeof(inliningOptionsDefaults->inner) == 20); 320 | 321 | bool isEqual = (inlining->inner.alwaysInlineMaxSize == inliningOptionsDefaults->inner.alwaysInlineMaxSize) 322 | && (inlining->inner.oneCallerInlineMaxSize == inliningOptionsDefaults->inner.oneCallerInlineMaxSize) 323 | && (inlining->inner.flexibleInlineMaxSize == inliningOptionsDefaults->inner.flexibleInlineMaxSize) 324 | && (inlining->inner.allowFunctionsWithLoops == inliningOptionsDefaults->inner.allowFunctionsWithLoops) 325 | && (inlining->inner.partialInliningIfs == inliningOptionsDefaults->inner.partialInliningIfs); 326 | 327 | return isEqual; 328 | } 329 | 330 | bool checkPassOptions(std::unique_ptr passOptions, wasm::PassOptions passOptionsDefaults) { 331 | 332 | // The size assertion will fail when `PassOptions` or `InliningOptions` fields change. 333 | // We need to update the test when the size assertion failed. 334 | // assert(sizeof(passOptionsDefaults) == 64); // Unix 335 | // assert(sizeof(passOptionsDefaults) == 56); // Windows 336 | // assert(sizeof(passOptionsDefaults) == 88); // Ubuntu 337 | // std::cout << " -------- size of optimizationsOptions " << sizeof(passOptionsDefaults); 338 | 339 | bool isEqual = (passOptions->inner.debug == passOptionsDefaults.debug) 340 | && (passOptions->inner.validate == passOptionsDefaults.validate) 341 | && (passOptions->inner.validateGlobally == passOptionsDefaults.validateGlobally) 342 | && (passOptions->inner.optimizeLevel == passOptionsDefaults.optimizeLevel) 343 | && (passOptions->inner.shrinkLevel == passOptionsDefaults.shrinkLevel) 344 | && (passOptions->inner.trapsNeverHappen == passOptionsDefaults.trapsNeverHappen) 345 | && (passOptions->inner.lowMemoryUnused == passOptionsDefaults.lowMemoryUnused) 346 | && (passOptions->inner.fastMath == passOptionsDefaults.fastMath) 347 | && (passOptions->inner.zeroFilledMemory == passOptionsDefaults.zeroFilledMemory) 348 | && (passOptions->inner.debugInfo == passOptionsDefaults.debugInfo) 349 | // inlining fields comparison 350 | && (passOptions->inner.inlining.alwaysInlineMaxSize == passOptionsDefaults.inlining.alwaysInlineMaxSize) 351 | && (passOptions->inner.inlining.oneCallerInlineMaxSize == passOptionsDefaults.inlining.oneCallerInlineMaxSize) 352 | && (passOptions->inner.inlining.flexibleInlineMaxSize == passOptionsDefaults.inlining.flexibleInlineMaxSize) 353 | && (passOptions->inner.inlining.allowFunctionsWithLoops == passOptionsDefaults.inlining.allowFunctionsWithLoops) 354 | && (passOptions->inner.inlining.partialInliningIfs == passOptionsDefaults.inlining.partialInliningIfs); 355 | 356 | return isEqual; 357 | } 358 | 359 | bool checkPassOptionsDefaults(std::unique_ptr passOptions) { 360 | auto passOptionsDefaults = wasm::PassOptions::getWithoutOptimization(); 361 | 362 | return checkPassOptions(std::move(passOptions), passOptionsDefaults); 363 | } 364 | 365 | bool checkPassOptionsDefaultsOs(std::unique_ptr passOptions) { 366 | auto passOptionsDefaults = wasm::PassOptions::getWithDefaultOptimizationOptions(); 367 | 368 | return checkPassOptions(std::move(passOptions), passOptionsDefaults); 369 | } 370 | } 371 | 372 | #endif // wasmopt_shims_h 373 | 374 | -------------------------------------------------------------------------------- /components/wasm-opt/src/api.rs: -------------------------------------------------------------------------------- 1 | pub use crate::features::Feature; 2 | pub use crate::passes::Pass; 3 | use crate::profiles::Profile; 4 | use std::collections::{HashMap, HashSet}; 5 | 6 | /// Optimization options and optimization builder. 7 | /// 8 | /// This type declares all supported Binaryen options. 9 | /// It can be modified directly or by its [builder-pattern] methods. 10 | /// 11 | /// Call [`OptimizationOptions::run`] to perform the optimizations. 12 | /// 13 | /// [builder-pattern]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html 14 | #[derive(Clone, Debug)] 15 | pub struct OptimizationOptions { 16 | /// Options for reading the unoptimized wasm module. 17 | pub reader: ReaderOptions, 18 | /// Options for writing the optimized wasm module. 19 | pub writer: WriterOptions, 20 | /// Options related to inlining. 21 | pub inlining: InliningOptions, 22 | /// Options that affect how optimization passes behave. 23 | pub passopts: PassOptions, 24 | /// The set of optimization passes to apply. 25 | pub passes: Passes, 26 | /// The set of wasm-features. 27 | pub features: Features, 28 | /// Run passes to convergence, continuing while binary size decreases. 29 | pub converge: bool, 30 | } 31 | 32 | /// Options for reading the unoptimized wasm module. 33 | #[derive(Copy, Clone, Debug)] 34 | pub struct ReaderOptions { 35 | /// The module format: wasm, wat, or either. 36 | /// 37 | /// The default value is [`FileType::Any`]. 38 | /// 39 | /// When this is `FileType::Any` the first bytes 40 | /// of the module will be inspected to determine whether 41 | /// the module is in binary or text format, 42 | /// then read as appropriate. 43 | pub file_type: FileType, 44 | } 45 | 46 | /// Options for writing the optimized wasm module. 47 | #[derive(Clone, Debug)] 48 | pub struct WriterOptions { 49 | /// The module format: wasm, wat, or either. 50 | /// 51 | /// The default value is [`FileType::Wasm`]. 52 | /// 53 | /// Note that when this is [`FileType::Any`] the following logic applies: 54 | /// 55 | /// If [`ReaderOptions::file_type`] is [`FileType::Wat`], 56 | /// write a wat file, otherwise write a wasm file. 57 | pub file_type: FileType, 58 | } 59 | 60 | /// Module format used by [`ReaderOptions`] and [`WriterOptions`]. 61 | #[derive(Copy, Clone, Debug)] 62 | pub enum FileType { 63 | /// A binary wasm module. 64 | Wasm, 65 | /// A text wasm module in wat format. 66 | Wat, 67 | /// Either a binary or text module. 68 | /// 69 | /// See the documentation for [`ReaderOptions`] and [`WriterOptions`] 70 | /// for an explanation of how this is interpreted. 71 | Any, 72 | } 73 | 74 | /// Options related to inlining. 75 | #[derive(Copy, Clone, Debug)] 76 | pub struct InliningOptions { 77 | /// Function size at which we always inline. 78 | /// 79 | /// Default: `2`. 80 | pub always_inline_max_size: u32, 81 | /// Function size which we inline when there is only one caller. 82 | /// 83 | /// Default: `u32::MAX`. 84 | pub one_caller_inline_max_size: u32, 85 | /// Function size above which we generally never inline. 86 | /// 87 | /// Default: `20`. 88 | pub flexible_inline_max_size: u32, 89 | /// Functions with loops are usually not inlined. 90 | /// 91 | /// Default: `false`. 92 | pub allow_functions_with_loops: bool, 93 | /// The number of `if`s to allow partial inlining of their conditions. 94 | /// 95 | /// Default: `0`. 96 | pub partial_inlining_ifs: u32, 97 | } 98 | 99 | /// Options that affect how optimization passes behave. 100 | /// 101 | /// The Binaryen source code has more extensive documentation of these options 102 | /// than is reproduced here. 103 | #[derive(Clone, Debug)] 104 | pub struct PassOptions { 105 | /// Validate both the unoptimized module and the optimized module. 106 | /// 107 | /// Default: `true`. 108 | pub validate: bool, 109 | /// Validate globally, not just locally. 110 | /// 111 | /// Default: `true`. 112 | pub validate_globally: bool, 113 | /// The amount of optimization to apply. 114 | /// 115 | /// The default depends on how [`OptimizationOptions`] is constructed. 116 | pub optimize_level: OptimizeLevel, 117 | /// The amount of effort to put into reducing module size. 118 | /// 119 | /// The default depends on how [`OptimizationOptions`] is constructed. 120 | pub shrink_level: ShrinkLevel, 121 | /// Assume traps never happen at runtime. 122 | /// 123 | /// Default: `false`. 124 | pub traps_never_happen: bool, 125 | /// Assume that the low 1K of memory is not valid for application use. 126 | /// 127 | /// Default: `false`. 128 | pub low_memory_unused: bool, 129 | /// Do faster floating point math by breaking official IEEE semantics. 130 | /// 131 | /// Default: `false`. 132 | pub fast_math: bool, 133 | /// Assume imported memory is zeroed. 134 | /// 135 | /// Default: `false`. 136 | pub zero_filled_memory: bool, 137 | /// Preserve debug info. 138 | /// 139 | /// Default: `false`. 140 | pub debug_info: bool, 141 | /// Additional pass-specific arguments. 142 | pub arguments: HashMap, 143 | } 144 | 145 | /// The amount of optimization to apply. 146 | /// 147 | /// This is interpreted differently by different passes. 148 | /// 149 | /// See the documentation of various [`OptimizationOptions`] 150 | /// constructors for a general description of how these behave. 151 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 152 | pub enum OptimizeLevel { 153 | Level0 = 0, 154 | Level1 = 1, 155 | Level2 = 2, 156 | Level3 = 3, 157 | Level4 = 4, 158 | } 159 | 160 | /// The amount of effort to put into reducing module size. 161 | /// 162 | /// This is interpreted differently by different passes. 163 | /// 164 | /// See the documentation of various [`OptimizationOptions`] 165 | /// constructors for a general description of how these behave. 166 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 167 | pub enum ShrinkLevel { 168 | Level0 = 0, 169 | Level1 = 1, 170 | Level2 = 2, 171 | } 172 | 173 | /// The set of optimization passes to apply. 174 | #[derive(Clone, Debug)] 175 | pub struct Passes { 176 | /// Apply the default set of optimization passes. 177 | pub add_default_passes: bool, 178 | /// Additional passes to apply. 179 | pub more_passes: Vec, 180 | } 181 | 182 | /// Which wasm [`Feature`]s to enable and disable. 183 | /// 184 | /// The baseline features are applied first, then 185 | /// enabled and disabled features are applied. 186 | #[derive(Clone, Debug, Default)] 187 | pub struct Features { 188 | pub baseline: FeatureBaseline, 189 | pub enabled: HashSet, 190 | pub disabled: HashSet, 191 | } 192 | 193 | /// The set of features to apply before applying custom features. 194 | #[derive(Clone, Debug)] 195 | pub enum FeatureBaseline { 196 | /// The default Binaryen feature set. 197 | /// 198 | /// Enables [`Feature::Default`]. 199 | /// Disables [`Feature::None`]. 200 | Default, 201 | /// Only allow WebAssembly MVP features. 202 | /// 203 | /// Enables [`Feature::Mvp`]. 204 | /// Disables [`Feature::All`]. 205 | MvpOnly, 206 | /// Allow all features. 207 | /// 208 | /// Enables [`Feature::All`]. 209 | /// Disables [Feature::Mvp`]. 210 | All, 211 | } 212 | 213 | /// Constructors. 214 | impl OptimizationOptions { 215 | pub(crate) fn new_empty() -> Self { 216 | OptimizationOptions { 217 | reader: ReaderOptions::default(), 218 | writer: WriterOptions::default(), 219 | inlining: InliningOptions::default(), 220 | passopts: PassOptions::default(), 221 | passes: Passes::default(), 222 | features: Features::default(), 223 | converge: false, 224 | } 225 | } 226 | 227 | /// Optimize for size. 228 | /// 229 | /// This corresponds to the `-Os` argument to `wasm-opt`, 230 | /// and also the `-O` argument to `wasm-opt`. 231 | /// 232 | /// It applies 233 | /// - [`Passes::add_default_passes`], 234 | /// - [`OptimizeLevel::Level2`], 235 | /// - [`ShrinkLevel::Level1`]. 236 | pub fn new_optimize_for_size() -> Self { 237 | Profile::optimize_for_size().into_opts() 238 | } 239 | 240 | /// Optimize for size, but even more. 241 | /// 242 | /// It applies 243 | /// - [`Passes::add_default_passes`], 244 | /// - [`OptimizeLevel::Level2`], 245 | /// - [`ShrinkLevel::Level2`]. 246 | /// 247 | /// This corresponds to the `-Oz` argument to `wasm-opt`. 248 | pub fn new_optimize_for_size_aggressively() -> Self { 249 | Profile::optimize_for_size_aggressively().into_opts() 250 | } 251 | 252 | /// Do not optimize. 253 | /// 254 | /// It applies 255 | /// - [`OptimizeLevel::Level0`], 256 | /// - [`ShrinkLevel::Level0`]. 257 | /// 258 | /// It adds no default passes. 259 | /// 260 | /// This corresponds to the `-O0` argument to `wasm-opt`, 261 | /// and also to calling `wasm-opt` with no `-O*` optional at all. 262 | pub fn new_opt_level_0() -> Self { 263 | Profile::opt_level_0().into_opts() 264 | } 265 | 266 | /// Apply basic optimizations. 267 | /// 268 | /// Useful for fast iteration. 269 | /// 270 | /// It applies 271 | /// - [`Passes::add_default_passes`], 272 | /// - [`OptimizeLevel::Level1`], 273 | /// - [`ShrinkLevel::Level0`]. 274 | /// 275 | /// This corresponds to the `-O1` argument to `wasm-opt`. 276 | pub fn new_opt_level_1() -> Self { 277 | Profile::opt_level_1().into_opts() 278 | } 279 | 280 | /// Apply most optimizations. 281 | /// 282 | /// This level of optimization is appropriate for most applications. 283 | /// Higher optimization levels will not necessarily yield better performance, 284 | /// but will take longer to optimize. 285 | /// 286 | /// It applies 287 | /// - [`Passes::add_default_passes`], 288 | /// - [`OptimizeLevel::Level2`], 289 | /// - [`ShrinkLevel::Level0`]. 290 | /// 291 | /// This corresponds to the `-O2` argument to `wasm-opt`. 292 | pub fn new_opt_level_2() -> Self { 293 | Profile::opt_level_2().into_opts() 294 | } 295 | 296 | /// Apply slower optimizations. 297 | /// 298 | /// Spends potentially a lot of time on optimizations. 299 | /// 300 | /// It applies 301 | /// - [`Passes::add_default_passes`], 302 | /// - [`OptimizeLevel::Level3`], 303 | /// - [`ShrinkLevel::Level0`]. 304 | /// 305 | /// This corresponds to the `-O3` argument to `wasm-opt`. 306 | pub fn new_opt_level_3() -> Self { 307 | Profile::opt_level_3().into_opts() 308 | } 309 | 310 | /// Apply the most aggressive optimizations. 311 | /// 312 | /// Flattens the IR, which can take a lot of time and memory, 313 | /// but may be useful on nested / complex / less-optimized input. 314 | /// 315 | /// It applies 316 | /// - [`Passes::add_default_passes`], 317 | /// - [`OptimizeLevel::Level4`], 318 | /// - [`ShrinkLevel::Level0`]. 319 | /// 320 | /// This corresponds to the `-O4` argument to `wasm-opt`. 321 | pub fn new_opt_level_4() -> Self { 322 | Profile::opt_level_4().into_opts() 323 | } 324 | } 325 | 326 | impl Default for ReaderOptions { 327 | fn default() -> ReaderOptions { 328 | ReaderOptions { 329 | file_type: FileType::Any, 330 | } 331 | } 332 | } 333 | 334 | impl Default for WriterOptions { 335 | fn default() -> WriterOptions { 336 | WriterOptions { 337 | file_type: FileType::Wasm, 338 | } 339 | } 340 | } 341 | 342 | impl Default for InliningOptions { 343 | fn default() -> InliningOptions { 344 | InliningOptions { 345 | always_inline_max_size: 2, 346 | one_caller_inline_max_size: u32::MAX, 347 | flexible_inline_max_size: 20, 348 | allow_functions_with_loops: false, 349 | partial_inlining_ifs: 0, 350 | } 351 | } 352 | } 353 | 354 | impl Default for PassOptions { 355 | fn default() -> PassOptions { 356 | PassOptions { 357 | validate: true, 358 | validate_globally: true, 359 | optimize_level: OptimizeLevel::default(), 360 | shrink_level: ShrinkLevel::default(), 361 | traps_never_happen: false, 362 | low_memory_unused: false, 363 | fast_math: false, 364 | zero_filled_memory: false, 365 | debug_info: false, 366 | arguments: HashMap::::new(), 367 | } 368 | } 369 | } 370 | 371 | impl Default for OptimizeLevel { 372 | fn default() -> OptimizeLevel { 373 | OptimizeLevel::Level0 374 | } 375 | } 376 | 377 | impl Default for ShrinkLevel { 378 | fn default() -> ShrinkLevel { 379 | ShrinkLevel::Level0 380 | } 381 | } 382 | 383 | impl Default for Passes { 384 | fn default() -> Passes { 385 | Passes { 386 | add_default_passes: true, 387 | more_passes: vec![], 388 | } 389 | } 390 | } 391 | 392 | impl Default for FeatureBaseline { 393 | fn default() -> FeatureBaseline { 394 | FeatureBaseline::Default 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /components/wasm-opt/src/integration.rs: -------------------------------------------------------------------------------- 1 | //! Easy integration with tools that already use `wasm-opt` via CLI. 2 | //! 3 | //! The [`run_from_command_args`] function interprets the arguments to 4 | //! a [`Command`], typically used for executing a subprocess, to construct 5 | //! an [`OptimizationOptions`], then runs the optimizer. 6 | //! 7 | //! Note that integrators must used the provided `Command` type, _not_ 8 | //! `std::process::Command`. The provided type is a thin wrapper around the 9 | //! standard type that is needed for backwards compatibility with older versions 10 | //! of Rust. 11 | 12 | use crate::api::{Feature, FileType, OptimizationOptions, OptimizeLevel, Pass, ShrinkLevel}; 13 | use crate::profiles::Profile; 14 | use crate::run::OptimizationError; 15 | use std::ffi::{OsStr, OsString}; 16 | use std::iter::Iterator; 17 | use std::num::ParseIntError; 18 | use std::path::PathBuf; 19 | use std::result::Result; 20 | use std::str::FromStr; 21 | use strum::IntoEnumIterator; 22 | use thiserror::Error; 23 | 24 | pub use crate::fake_command::Command; 25 | 26 | /// Interpret a pre-built [`Command`] as an [`OptimizationOptions`], 27 | /// then call [`OptimizationOptions::run`] on it. 28 | /// 29 | /// This function is meant for easy integration with tools that already 30 | /// call `wasm-opt` via the command line, allowing them to use either 31 | /// the command-line tool or the integrated API from a single `Command` builder. 32 | /// New programs that just need to optimize wasm should use `OptimizationOptions` directly. 33 | /// 34 | /// This function is provided on a best-effort basis to support programs 35 | /// trying to integrate with the crate. 36 | /// In general, it should support any command line options that are also supported 37 | /// by the `OptimizationOptions` API, 38 | /// but it may not parse — and in some cases may not interpret — 39 | /// those commands in exactly the same way. 40 | /// It is meant to make it _possible_ to produce a single command-line that works 41 | /// with both the CLI and the API, 42 | /// not to reproduce the behavior of the CLI perfectly. 43 | /// 44 | /// The `-o` argument is required, followed by a path — 45 | /// the `wasm-opt` tool writes the optimized module to stdout by default, 46 | /// but this library is not currently capable of that. 47 | /// `-o` specifies a file in which to write the module. 48 | /// If `-o` is not provided, [`Error::OutputFileRequired`] is returned. 49 | /// 50 | /// Only the arguments to `command` are interpreted; 51 | /// environment variables and other settings are ignored. 52 | /// 53 | /// # Errors 54 | /// 55 | /// - Returns [`Error::Unsupported`] if any argument is not understood. 56 | /// - Returns [`Error::OutputFileRequired`] if the `-o` argument and subsequent path 57 | /// are not provided. 58 | pub fn run_from_command_args(command: Command) -> Result<(), Error> { 59 | let parsed = parse_command_args(command)?; 60 | 61 | parsed.opts.run_with_sourcemaps( 62 | parsed.input_file, 63 | parsed.input_sourcemap, 64 | parsed.output_file, 65 | parsed.output_sourcemap, 66 | parsed.sourcemap_url, 67 | )?; 68 | 69 | Ok(()) 70 | } 71 | 72 | /// An error resulting from [`run_from_command_args`]. 73 | #[derive(Error, Debug)] 74 | pub enum Error { 75 | /// No input file specified. 76 | #[error("An input file is required")] 77 | InputFileRequired, 78 | /// No output file specified. 79 | #[error("The `-o` option to `wasm-opt` is required")] 80 | OutputFileRequired, 81 | /// Expected another argument. 82 | #[error("The `wasm-opt` argument list ended while expecting another argument")] 83 | UnexpectedEndOfArgs, 84 | /// Expected to parse unicode. 85 | #[error("Argument must be unicode: {arg:?}")] 86 | NeedUnicode { arg: OsString }, 87 | /// Expected to parse a number. 88 | #[error("Argument must be a number: {arg:?}")] 89 | NeedNumber { 90 | arg: OsString, 91 | #[source] 92 | source: ParseIntError, 93 | }, 94 | /// Unsupported or unrecognized command-line option. 95 | #[error("Unsupported `wasm-opt` command-line arguments: {args:?}")] 96 | Unsupported { args: Vec }, 97 | /// An error occurred while executing [`OptimizationOptions::run`]. 98 | #[error("Error while optimization wasm modules")] 99 | ExecutionError( 100 | #[from] 101 | #[source] 102 | OptimizationError, 103 | ), 104 | } 105 | 106 | struct ParsedCliArgs { 107 | opts: OptimizationOptions, 108 | input_file: PathBuf, 109 | input_sourcemap: Option, 110 | output_file: PathBuf, 111 | output_sourcemap: Option, 112 | sourcemap_url: Option, 113 | } 114 | 115 | #[rustfmt::skip] 116 | fn parse_command_args(command: Command) -> Result { 117 | let mut opts = OptimizationOptions::new_opt_level_0(); 118 | 119 | let mut args = command.get_args(); 120 | 121 | let mut input_file: Option = None; 122 | let mut input_sourcemap: Option = None; 123 | let mut output_file: Option = None; 124 | let mut output_sourcemap: Option = None; 125 | let mut sourcemap_url: Option = None; 126 | 127 | let mut unsupported: Vec = vec![]; 128 | 129 | while let Some(arg) = args.next() { 130 | let arg = if let Some(arg) = arg.to_str() { 131 | arg 132 | } else { 133 | // Not unicode. Might still be the infile. 134 | parse_infile_path(arg, &mut input_file, &mut unsupported); 135 | continue; 136 | }; 137 | 138 | // Keep these cases in order they are listed in the original cpp files. 139 | match arg { 140 | /* from wasm-opt.cpp */ 141 | 142 | "--output" | "-o" => { 143 | parse_path_into(&mut args, &mut output_file, &mut unsupported)?; 144 | } 145 | "--emit-text" | "-S" => { 146 | opts.writer_file_type(FileType::Wat); 147 | } 148 | "--converge" | "-c" => { 149 | opts.set_converge(); 150 | } 151 | "--input-source-map" | "-ism" => { 152 | parse_path_into(&mut args, &mut input_sourcemap, &mut unsupported)?; 153 | } 154 | "--output-source-map" | "-osm" => { 155 | parse_path_into(&mut args, &mut output_sourcemap, &mut unsupported)?; 156 | } 157 | "--output-source-map-url" | "-osu" => { 158 | sourcemap_url = Some(parse_unicode(&mut args)?); 159 | } 160 | 161 | /* from optimization-options.h */ 162 | 163 | "-O" => { 164 | Profile::optimize_for_size().apply_to_opts(&mut opts); 165 | } 166 | "-O0" => { 167 | Profile::opt_level_0().apply_to_opts(&mut opts); 168 | } 169 | "-O1" => { 170 | Profile::opt_level_1().apply_to_opts(&mut opts); 171 | } 172 | "-O2" => { 173 | Profile::opt_level_2().apply_to_opts(&mut opts); 174 | } 175 | "-O3" => { 176 | Profile::opt_level_3().apply_to_opts(&mut opts); 177 | } 178 | "-O4" => { 179 | Profile::opt_level_4().apply_to_opts(&mut opts); 180 | } 181 | "-Os" => { 182 | Profile::optimize_for_size().apply_to_opts(&mut opts); 183 | } 184 | "-Oz" => { 185 | Profile::optimize_for_size_aggressively().apply_to_opts(&mut opts); 186 | } 187 | "--optimize-level" | "-ol" => { 188 | match parse_unicode(&mut args)?.as_str() { 189 | "0" => { opts.optimize_level(OptimizeLevel::Level0); }, 190 | "1" => { opts.optimize_level(OptimizeLevel::Level1); }, 191 | "2" => { opts.optimize_level(OptimizeLevel::Level2); }, 192 | "3" => { opts.optimize_level(OptimizeLevel::Level3); }, 193 | "4" => { opts.optimize_level(OptimizeLevel::Level4); }, 194 | arg => { 195 | unsupported.push(OsString::from(arg)); 196 | } 197 | } 198 | } 199 | "--shrink-level" | "-s" => { 200 | match parse_unicode(&mut args)?.as_str() { 201 | "0" => { opts.shrink_level(ShrinkLevel::Level0); }, 202 | "1" => { opts.shrink_level(ShrinkLevel::Level1); }, 203 | "2" => { opts.shrink_level(ShrinkLevel::Level2); }, 204 | arg => { 205 | unsupported.push(OsString::from(arg)); 206 | } 207 | } 208 | } 209 | "--debuginfo" | "-g" => { 210 | opts.debug_info(true); 211 | } 212 | "--always-inline-max-function-size" | "-aimfs" => { 213 | opts.always_inline_max_size(parse_u32(&mut args)?); 214 | } 215 | "--flexible-inline-max-function-size" | "-fimfs" => { 216 | opts.flexible_inline_max_size(parse_u32(&mut args)?); 217 | } 218 | "--one-caller-inline-max-function-size" | "-ocifms" => { 219 | opts.one_caller_inline_max_size(parse_u32(&mut args)?); 220 | } 221 | "--inline-functions-with-loops" | "-ifwl" => { 222 | opts.allow_functions_with_loops(true); 223 | } 224 | "--partial-inlining-ifs" | "-pii" => { 225 | opts.partial_inlining_ifs(parse_u32(&mut args)?); 226 | } 227 | "--traps-never-happen" | "-tnh" => { 228 | opts.traps_never_happen(true); 229 | } 230 | "--low-memory-unused" | "-lmu" => { 231 | opts.low_memory_unused(true); 232 | } 233 | "--fast-math" | "-ffm" => { 234 | opts.fast_math(true); 235 | } 236 | "--zero-filled-memory" | "-uim" => { 237 | opts.zero_filled_memory(true); 238 | } 239 | 240 | /* from tool-options.h */ 241 | 242 | "--mvp-features" | "-mvp" => { 243 | opts.mvp_features_only(); 244 | } 245 | "--all-features" | "-all" => { 246 | opts.all_features(); 247 | } 248 | "--quiet" | "-q" => { 249 | /* pass */ 250 | } 251 | "--no-validation" | "-n" => { 252 | opts.validate(false); 253 | } 254 | "--pass-arg" | "-pa" => { 255 | let args = parse_unicode(&mut args)?; 256 | if args.contains("@") { 257 | let args: Vec<&str> = args.split("@").collect(); 258 | opts.set_pass_arg(args[0], args[1]); 259 | } else { 260 | opts.set_pass_arg(&args, "1"); 261 | } 262 | } 263 | 264 | /* fallthrough */ 265 | 266 | _ => { 267 | // todo parse pass names w/ pass args (--pass-name=value). 268 | 269 | if arg.starts_with("--enable-") { 270 | let feature = &arg[9..]; 271 | if let Ok(feature) = Feature::from_str(feature) { 272 | opts.enable_feature(feature); 273 | } else { 274 | unsupported.push(OsString::from(arg)); 275 | } 276 | } else if arg.starts_with("--disable-") { 277 | let feature = &arg[10..]; 278 | if let Ok(feature) = Feature::from_str(feature) { 279 | opts.disable_feature(feature); 280 | } else { 281 | unsupported.push(OsString::from(arg)); 282 | } 283 | } else { 284 | let mut is_pass = false; 285 | for pass in Pass::iter() { 286 | if is_pass_argument(arg, &pass) { 287 | opts.add_pass(pass); 288 | is_pass = true; 289 | } 290 | } 291 | 292 | if !is_pass { 293 | if arg.starts_with("-") && arg.len() > 1 { 294 | // Reject args that look like flags that we don't support. 295 | unsupported.push(OsString::from(arg)); 296 | } else { 297 | parse_infile_path(OsStr::new(arg), &mut input_file, &mut unsupported); 298 | } 299 | } 300 | } 301 | } 302 | } 303 | } 304 | 305 | let input_file = if let Some(input_file) = input_file { 306 | input_file 307 | } else { 308 | return Err(Error::InputFileRequired); 309 | }; 310 | let output_file = if let Some(output_file) = output_file { 311 | output_file 312 | } else { 313 | return Err(Error::OutputFileRequired); 314 | }; 315 | 316 | if unsupported.len() > 0 { 317 | return Err(Error::Unsupported { 318 | args: unsupported, 319 | }); 320 | } 321 | 322 | Ok(ParsedCliArgs { 323 | opts, 324 | input_file, 325 | input_sourcemap, 326 | output_file, 327 | output_sourcemap, 328 | sourcemap_url, 329 | }) 330 | } 331 | 332 | fn is_pass_argument(arg: &str, pass: &Pass) -> bool { 333 | let pass_name = pass.name(); 334 | arg.starts_with("--") && arg.contains(pass_name) && arg.len() == 2 + pass_name.len() 335 | } 336 | 337 | fn parse_infile_path( 338 | arg: &OsStr, 339 | maybe_input_file: &mut Option, 340 | unsupported: &mut Vec, 341 | ) { 342 | parse_path_into(&mut Some(arg).into_iter(), maybe_input_file, unsupported).expect("impossible") 343 | } 344 | 345 | fn parse_path_into<'item>( 346 | args: &mut impl Iterator, 347 | maybe_path: &mut Option, 348 | unsupported: &mut Vec, 349 | ) -> Result<(), Error> { 350 | if let Some(arg) = args.next() { 351 | if maybe_path.is_none() { 352 | *maybe_path = Some(PathBuf::from(arg)); 353 | } else { 354 | unsupported.push(OsString::from(arg)); 355 | } 356 | 357 | Ok(()) 358 | } else { 359 | Err(Error::UnexpectedEndOfArgs) 360 | } 361 | } 362 | 363 | fn parse_unicode<'item>(args: &mut impl Iterator) -> Result { 364 | if let Some(arg) = args.next() { 365 | if let Some(arg) = arg.to_str() { 366 | Ok(arg.to_owned()) 367 | } else { 368 | Err(Error::NeedUnicode { 369 | arg: arg.to_owned(), 370 | }) 371 | } 372 | } else { 373 | Err(Error::UnexpectedEndOfArgs) 374 | } 375 | } 376 | 377 | fn parse_u32<'item>(args: &mut impl Iterator) -> Result { 378 | let arg = parse_unicode(args)?; 379 | let number: u32 = arg.parse().map_err(|e| Error::NeedNumber { 380 | arg: OsString::from(arg), 381 | source: e, 382 | })?; 383 | Ok(number) 384 | } 385 | -------------------------------------------------------------------------------- /components/wasm-opt/tests/base_tests.rs: -------------------------------------------------------------------------------- 1 | use strum::IntoEnumIterator; 2 | use wasm_opt::base::*; 3 | 4 | use std::fs::{self, File}; 5 | use std::io::BufWriter; 6 | use std::io::Write; 7 | use tempfile::Builder; 8 | 9 | static WAT_FILE: &[u8] = include_bytes!("hello_world.wat"); 10 | static WASM_FILE: &[u8] = include_bytes!("hello_world.wasm"); 11 | static GARBAGE_FILE: &[u8] = include_bytes!("garbage_file.wat"); 12 | static MULTISIG_WASM: &[u8] = include_bytes!("ink_example_multisig.wasm"); 13 | 14 | #[test] 15 | #[ignore] 16 | fn read_write_from_unicode_works() -> anyhow::Result<()> { 17 | let temp_dir = Builder::new().prefix("unicode-α℗$∞ℳ-").tempdir()?; 18 | let path = temp_dir.path().join("hello_world.wasm"); 19 | 20 | let temp_file = File::create(&path)?; 21 | let mut buf_writer = BufWriter::new(&temp_file); 22 | buf_writer.write_all(WASM_FILE)?; 23 | 24 | let mut m = Module::new(); 25 | let mut reader = ModuleReader::new(); 26 | reader.read_binary(&path, &mut m, None)?; 27 | 28 | let mut writer = ModuleWriter::new(); 29 | let new_file = temp_dir.path().join("hello_world_by_module_writer.wasm"); 30 | writer.write_binary(&mut m, &new_file)?; 31 | 32 | let mut another_m = Module::new(); 33 | let mut another_reader = ModuleReader::new(); 34 | another_reader.read_binary(&new_file, &mut another_m, None)?; 35 | 36 | let mut another_writer = ModuleWriter::new(); 37 | let another_new_file = temp_dir 38 | .path() 39 | .join("hello_world_by_another_module_writer.wasm"); 40 | another_writer.write_binary(&mut another_m, &another_new_file)?; 41 | 42 | let new_file_reader = fs::read(&new_file)?; 43 | let another_new_file_reader = fs::read(&another_new_file)?; 44 | 45 | assert_eq!(new_file_reader, another_new_file_reader); 46 | 47 | Ok(()) 48 | } 49 | 50 | #[test] 51 | fn read_write_text_works() -> anyhow::Result<()> { 52 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 53 | let path = temp_dir.path().join("hello_world.wat"); 54 | 55 | let temp_file = File::create(&path)?; 56 | let mut buf_writer = BufWriter::new(&temp_file); 57 | buf_writer.write_all(WAT_FILE)?; 58 | 59 | let mut m = Module::new(); 60 | let mut reader = ModuleReader::new(); 61 | reader.read_text(&path, &mut m)?; 62 | 63 | let mut writer = ModuleWriter::new(); 64 | let new_file = temp_dir.path().join("hello_world_by_module_writer.wat"); 65 | writer.write_text(&mut m, &new_file)?; 66 | 67 | let mut another_m = Module::new(); 68 | let mut another_reader = ModuleReader::new(); 69 | another_reader.read_text(&new_file, &mut another_m)?; 70 | 71 | let mut another_writer = ModuleWriter::new(); 72 | let another_new_file = temp_dir 73 | .path() 74 | .join("hello_world_by_another_module_writer.wat"); 75 | another_writer.write_text(&mut another_m, &another_new_file)?; 76 | 77 | let new_file_reader = fs::read(&new_file)?; 78 | let another_new_file_reader = fs::read(&another_new_file)?; 79 | 80 | assert_eq!(new_file_reader, another_new_file_reader); 81 | 82 | Ok(()) 83 | } 84 | 85 | #[test] 86 | fn read_write_binary_works() -> anyhow::Result<()> { 87 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 88 | let path = temp_dir.path().join("hello_world.wasm"); 89 | 90 | let temp_file = File::create(&path)?; 91 | let mut buf_writer = BufWriter::new(&temp_file); 92 | buf_writer.write_all(WASM_FILE)?; 93 | 94 | let mut m = Module::new(); 95 | let mut reader = ModuleReader::new(); 96 | reader.read_binary(&path, &mut m, None)?; 97 | 98 | let mut writer = ModuleWriter::new(); 99 | let new_file = temp_dir.path().join("hello_world_by_module_writer.wasm"); 100 | writer.write_binary(&mut m, &new_file)?; 101 | 102 | let mut another_m = Module::new(); 103 | let mut another_reader = ModuleReader::new(); 104 | another_reader.read_binary(&new_file, &mut another_m, None)?; 105 | 106 | let mut another_writer = ModuleWriter::new(); 107 | let another_new_file = temp_dir 108 | .path() 109 | .join("hello_world_by_another_module_writer.wasm"); 110 | another_writer.write_binary(&mut another_m, &another_new_file)?; 111 | 112 | let new_file_reader = fs::read(&new_file)?; 113 | let another_new_file_reader = fs::read(&another_new_file)?; 114 | 115 | assert_eq!(new_file_reader, another_new_file_reader); 116 | 117 | Ok(()) 118 | } 119 | 120 | #[test] 121 | fn module_read_garbage_error_works() -> anyhow::Result<()> { 122 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 123 | let path = temp_dir.path().join("hello_world_bad.wat"); 124 | 125 | let temp_file = File::create(&path)?; 126 | let mut buf_writer = BufWriter::new(&temp_file); 127 | buf_writer.write_all(GARBAGE_FILE)?; 128 | 129 | let mut m = Module::new(); 130 | let mut reader = ModuleReader::new(); 131 | let res = reader.read_text(&path, &mut m); 132 | match res { 133 | Ok(()) => {} 134 | Err(_) => println!("Module read_text failed"), 135 | } 136 | 137 | let mut m = Module::new(); 138 | let mut reader = ModuleReader::new(); 139 | let res = reader.read_binary(&path, &mut m, None); 140 | match res { 141 | Ok(()) => {} 142 | Err(_) => println!("Module read_binary failed"), 143 | } 144 | 145 | let mut m = Module::new(); 146 | let mut reader = ModuleReader::new(); 147 | let res = reader.read(&path, &mut m, None); 148 | match res { 149 | Ok(()) => {} 150 | Err(_) => println!("Module read failed"), 151 | } 152 | 153 | Ok(()) 154 | } 155 | 156 | #[test] 157 | fn map_parse_exception_works() -> anyhow::Result<()> { 158 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 159 | let path = temp_dir.path().join("hello_world.wasm"); 160 | 161 | let temp_file = File::create(&path)?; 162 | let mut buf_writer = BufWriter::new(&temp_file); 163 | buf_writer.write_all(WASM_FILE)?; 164 | 165 | let mut m = Module::new(); 166 | let mut reader = ModuleReader::new(); 167 | 168 | let source_map_path = temp_dir.path().join("bad_source_map_path"); 169 | let res = reader.read_binary(&path, &mut m, Some(&source_map_path)); 170 | match res { 171 | Ok(()) => panic!(), 172 | Err(e) => println!("Module read_binary failed: {}", e), 173 | } 174 | 175 | let res = reader.read(&path, &mut m, Some(&source_map_path)); 176 | match res { 177 | Ok(()) => panic!(), 178 | Err(e) => println!("Module read failed: {}", e), 179 | } 180 | 181 | Ok(()) 182 | } 183 | 184 | #[test] 185 | fn pass_runner_works() -> anyhow::Result<()> { 186 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 187 | let path = temp_dir.path().join("hello_world.wasm"); 188 | 189 | let temp_file = File::create(&path)?; 190 | let mut buf_writer = BufWriter::new(&temp_file); 191 | buf_writer.write_all(WASM_FILE)?; 192 | 193 | // Module without optimization 194 | let mut m = Module::new(); 195 | let mut reader = ModuleReader::new(); 196 | reader.read_binary(&path, &mut m, None)?; 197 | 198 | let mut writer = ModuleWriter::new(); 199 | let new_file = temp_dir.path().join("hello_world_by_module_writer.wasm"); 200 | writer.write_binary(&mut m, &new_file)?; 201 | 202 | // Module with optimization 203 | let mut another_m = Module::new(); 204 | let mut another_reader = ModuleReader::new(); 205 | another_reader.read_binary(&new_file, &mut another_m, None)?; 206 | 207 | let mut pass_runner = PassRunner::new(&mut another_m); 208 | pass_runner.add_default_optimization_passes(); 209 | pass_runner.run(); 210 | drop(pass_runner); 211 | 212 | let mut another_writer = ModuleWriter::new(); 213 | let another_new_file = temp_dir 214 | .path() 215 | .join("hello_world_by_another_module_writer.wasm"); 216 | another_writer.write_binary(&mut another_m, &another_new_file)?; 217 | 218 | let new_file_reader = fs::read(&new_file)?; 219 | let another_new_file_reader = fs::read(&another_new_file)?; 220 | 221 | assert!(new_file_reader.len() > another_new_file_reader.len()); 222 | 223 | Ok(()) 224 | } 225 | 226 | #[test] 227 | fn pass_options_works() -> anyhow::Result<()> { 228 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 229 | let path = temp_dir.path().join("hello_world.wasm"); 230 | 231 | let temp_file = File::create(&path)?; 232 | let mut buf_writer = BufWriter::new(&temp_file); 233 | buf_writer.write_all(WASM_FILE)?; 234 | 235 | // Module without optimization 236 | let mut m = Module::new(); 237 | let mut reader = ModuleReader::new(); 238 | reader.read_binary(&path, &mut m, None)?; 239 | 240 | let mut writer = ModuleWriter::new(); 241 | let new_file = temp_dir.path().join("hello_world_by_module_writer.wasm"); 242 | writer.write_binary(&mut m, &new_file)?; 243 | 244 | // Module with optimization: default 245 | let mut m_0 = Module::new(); 246 | let mut reader_0 = ModuleReader::new(); 247 | reader_0.read_binary(&new_file, &mut m_0, None)?; 248 | 249 | let mut pass_options = PassOptions::new(); 250 | pass_options.set_optimize_level(2); 251 | pass_options.set_shrink_level(1); 252 | 253 | let mut pass_runner = PassRunner::new_with_options(&mut m_0, pass_options); 254 | pass_runner.add_default_optimization_passes(); 255 | pass_runner.run(); 256 | drop(pass_runner); 257 | 258 | let mut writer_0 = ModuleWriter::new(); 259 | let file_0 = temp_dir.path().join("hello_world_by_module_writer_0.wasm"); 260 | writer_0.write_binary(&mut m_0, &file_0)?; 261 | 262 | let new_file_reader = fs::read(&new_file)?; 263 | let file_reader_0 = fs::read(&file_0)?; 264 | 265 | println!("new_file: {}", new_file_reader.len()); 266 | println!("file_0: {}", file_reader_0.len()); 267 | 268 | assert!(new_file_reader.len() > file_reader_0.len()); 269 | 270 | // Module with optimization: more optimized settings 271 | let mut m_1 = Module::new(); 272 | let mut reader_1 = ModuleReader::new(); 273 | reader_1.read_binary(&new_file, &mut m_1, None)?; 274 | 275 | let mut pass_options = PassOptions::new(); 276 | pass_options.set_optimize_level(5); 277 | pass_options.set_shrink_level(5); 278 | 279 | let mut pass_runner = PassRunner::new_with_options(&mut m_1, pass_options); 280 | pass_runner.add_default_optimization_passes(); 281 | pass_runner.run(); 282 | drop(pass_runner); 283 | 284 | let mut writer_1 = ModuleWriter::new(); 285 | let file_1 = temp_dir.path().join("hello_world_by_module_writer_1.wasm"); 286 | writer_1.write_binary(&mut m_1, &file_1)?; 287 | 288 | let file_reader_1 = fs::read(&file_1)?; 289 | println!("file_1: {}", file_reader_1.len()); 290 | 291 | assert!(file_reader_0.len() > file_reader_1.len()); 292 | 293 | // Module with optimization: ridiculous settings 294 | let mut m_2 = Module::new(); 295 | let mut reader_2 = ModuleReader::new(); 296 | reader_2.read_binary(&new_file, &mut m_2, None)?; 297 | 298 | let mut pass_options = PassOptions::new(); 299 | 300 | pass_options.set_optimize_level(2_000_000_000); 301 | pass_options.set_shrink_level(2_000_000_000); 302 | 303 | let mut pass_runner = PassRunner::new_with_options(&mut m_2, pass_options); 304 | pass_runner.add_default_optimization_passes(); 305 | pass_runner.run(); 306 | drop(pass_runner); 307 | 308 | let mut writer_2 = ModuleWriter::new(); 309 | let file_2 = temp_dir.path().join("hello_world_by_module_writer_2.wasm"); 310 | writer_2.write_binary(&mut m_2, &file_2)?; 311 | 312 | let file_reader_2 = fs::read(&file_2)?; 313 | println!("file_2: {}", file_reader_2.len()); 314 | 315 | assert!(file_reader_1.len() >= file_reader_2.len()); 316 | 317 | Ok(()) 318 | } 319 | 320 | #[test] 321 | fn pass_runner_add_works() -> anyhow::Result<()> { 322 | let temp_dir = Builder::new().prefix("wasm_opt_tests").tempdir()?; 323 | let path = temp_dir.path().join("ink_example_multisig.wasm"); 324 | 325 | let temp_file = File::create(&path)?; 326 | let mut buf_writer = BufWriter::new(&temp_file); 327 | buf_writer.write_all(MULTISIG_WASM)?; 328 | 329 | // Module without optimization 330 | let mut m = Module::new(); 331 | let mut reader = ModuleReader::new(); 332 | reader.read_binary(&path, &mut m, None)?; 333 | 334 | let mut writer = ModuleWriter::new(); 335 | let new_file = temp_dir 336 | .path() 337 | .join("ink_example_multisig_by_module_writer.wasm"); 338 | writer.write_binary(&mut m, &new_file)?; 339 | 340 | // Module with optimization 341 | let mut another_m = Module::new(); 342 | let mut another_reader = ModuleReader::new(); 343 | another_reader.read_binary(&new_file, &mut another_m, None)?; 344 | 345 | let mut pass_runner = PassRunner::new(&mut another_m); 346 | pass_runner.add("duplicate-function-elimination"); 347 | pass_runner.run(); 348 | drop(pass_runner); 349 | 350 | let mut another_writer = ModuleWriter::new(); 351 | let another_new_file = temp_dir 352 | .path() 353 | .join("ink_example_multisig_by_another_module_writer.wasm"); 354 | another_writer.write_binary(&mut another_m, &another_new_file)?; 355 | 356 | let new_file_reader = fs::read(&new_file)?; 357 | let another_new_file_reader = fs::read(&another_new_file)?; 358 | 359 | println!("file_1: {}", new_file_reader.len()); 360 | println!("file_2: {}", another_new_file_reader.len()); 361 | assert!(new_file_reader.len() > another_new_file_reader.len()); 362 | 363 | Ok(()) 364 | } 365 | 366 | #[test] 367 | fn get_pass_description_valid_name_works() -> anyhow::Result<()> { 368 | let pass_description = pass_registry::get_pass_description("limit-segments"); 369 | 370 | assert_eq!( 371 | pass_description, 372 | "attempt to merge segments to fit within web limits" 373 | ); 374 | 375 | Ok(()) 376 | } 377 | 378 | #[test] 379 | fn is_pass_hidden_works() -> anyhow::Result<()> { 380 | let is_pass_hidden = pass_registry::is_pass_hidden("limit-segments"); 381 | 382 | assert_eq!(is_pass_hidden, false); 383 | 384 | Ok(()) 385 | } 386 | 387 | #[test] 388 | fn read_file_not_exists() -> anyhow::Result<()> { 389 | let temp_dir = Builder::new().prefix("wasm-opt").tempdir()?; 390 | let path = temp_dir.path().join("not-a-file.wasm"); 391 | 392 | let mut m = Module::new(); 393 | let mut reader = ModuleReader::new(); 394 | let r = reader.read_binary(&path, &mut m, None); 395 | 396 | assert!(r.is_err()); 397 | 398 | Ok(()) 399 | } 400 | 401 | #[test] 402 | fn write_file_path_not_exists() -> anyhow::Result<()> { 403 | let temp_dir = Builder::new().prefix("wasm-opt").tempdir()?; 404 | let path = temp_dir.path().join("hello_world.wat"); 405 | 406 | let temp_file = File::create(&path)?; 407 | let mut buf_writer = BufWriter::new(&temp_file); 408 | buf_writer.write_all(WAT_FILE)?; 409 | 410 | let mut m = Module::new(); 411 | let mut reader = ModuleReader::new(); 412 | reader.read_text(&path, &mut m)?; 413 | 414 | let mut writer = ModuleWriter::new(); 415 | let new_file = temp_dir 416 | .path() 417 | .join("badpath") 418 | .join("hello_world_by_module_writer.wat"); 419 | let r = writer.write_text(&mut m, &new_file); 420 | 421 | assert!(r.is_err()); 422 | 423 | Ok(()) 424 | } 425 | 426 | #[test] 427 | fn all_features_correct() -> anyhow::Result<()> { 428 | let features_via_shims = get_feature_array(); 429 | let mut features_via_base = Vec::::new(); 430 | 431 | Feature::iter().for_each(|f| { 432 | features_via_base.push(f as u32); 433 | }); 434 | 435 | assert_eq!(features_via_shims, features_via_base); 436 | Ok(()) 437 | } 438 | -------------------------------------------------------------------------------- /components/wasm-opt/src/run.rs: -------------------------------------------------------------------------------- 1 | use crate::api::*; 2 | use crate::base::{ 3 | validate_wasm, Feature as BaseFeature, FeatureSet as BaseFeatureSet, 4 | InliningOptions as BaseInliningOptions, Module, ModuleReader, ModuleWriter, 5 | PassOptions as BasePassOptions, PassRunner, 6 | }; 7 | use std::fs; 8 | use std::path::Path; 9 | use thiserror::Error; 10 | 11 | /// An error resulting from the [`OptimizationOptions::run`] method. 12 | #[derive(Error, Debug)] 13 | pub enum OptimizationError { 14 | /// The input module did not validate. 15 | #[error("Failed to validate wasm: error validating input")] 16 | ValidateWasmInput, 17 | /// The output module did not validate. 18 | #[error("Failed to validate wasm: error after opts")] 19 | ValidateWasmOutput, 20 | /// An error occurred while reading the input module. 21 | #[error("Failed to read module")] 22 | Read { 23 | #[source] 24 | source: Box, 25 | }, 26 | /// An error occurred while writing the output module. 27 | #[error("Failed to write module")] 28 | Write { 29 | #[source] 30 | source: Box, 31 | }, 32 | /// The input file path represents stdin to Binaryen, 33 | /// but the API does not support reading stdin. 34 | #[error("Refusing to read from stdin")] 35 | InvalidStdinPath, 36 | } 37 | 38 | /// Execution. 39 | impl OptimizationOptions { 40 | /// Run the Binaryen wasm optimizer. 41 | /// 42 | /// This loads a module from a file, 43 | /// runs optimization passes, 44 | /// and writes the module back to a file. 45 | /// 46 | /// To supply sourcemaps for the input module, 47 | /// and preserve them for the output module, 48 | /// use [`OptimizationOptions::run_with_sourcemaps`]. 49 | /// 50 | /// # Errors 51 | /// 52 | /// Returns error on I/O failure, or if the input fails to parse. 53 | /// If [`PassOptions::validate`] is true, it returns an error 54 | /// if the input module fails to validate, or if the optimized 55 | /// module fails to validate. 56 | /// 57 | /// The Rust API does not support reading a module on stdin, as the CLI 58 | /// does. If `infile` is empty or "-", 59 | /// [`OptimizationError::InvalidStdinPath`] is returned. 60 | pub fn run( 61 | &self, 62 | infile: impl AsRef, 63 | outfile: impl AsRef, 64 | ) -> Result<(), OptimizationError> { 65 | self.run_with_sourcemaps(infile, None::<&str>, outfile, None::<&str>, None::<&str>) 66 | } 67 | 68 | /// Run the Binaryen wasm optimizer. 69 | /// 70 | /// This loads a module from a file, 71 | /// runs optimization passes, 72 | /// and writes the module back to a file. 73 | /// 74 | /// The sourcemap arguments are optional, and only have effect 75 | /// when reading or writing binary `wasm` files. When using 76 | /// text `wat` files the respective sourcemap argument is ignored. 77 | /// 78 | /// # Errors 79 | /// 80 | /// Returns error on I/O failure, or if the input fails to parse. 81 | /// If [`PassOptions::validate`] is true, it returns an error 82 | /// if the input module fails to validate, or if the optimized 83 | /// module fails to validate. 84 | /// 85 | /// The Rust API does not support reading a module on stdin, as the CLI 86 | /// does. If `infile` is empty or "-", 87 | /// [`OptimizationError::InvalidStdinPath`] is returned. 88 | pub fn run_with_sourcemaps( 89 | &self, 90 | infile: impl AsRef, 91 | infile_sourcemap: Option>, 92 | outfile: impl AsRef, 93 | outfile_sourcemap: Option>, 94 | sourcemap_url: Option>, 95 | ) -> Result<(), OptimizationError> { 96 | let infile: &Path = infile.as_ref(); 97 | let infile_sourcemap: Option<&Path> = infile_sourcemap.as_ref().map(AsRef::as_ref); 98 | let outfile: &Path = outfile.as_ref(); 99 | let outfile_sourcemap: Option<&Path> = outfile_sourcemap.as_ref().map(AsRef::as_ref); 100 | let sourcemap_url: Option<&str> = sourcemap_url.as_ref().map(AsRef::as_ref); 101 | 102 | if infile.as_os_str().is_empty() || infile == Path::new("-") { 103 | return Err(OptimizationError::InvalidStdinPath); 104 | } 105 | 106 | let mut m = Module::new(); 107 | self.apply_features(&mut m); 108 | 109 | { 110 | let mut reader = ModuleReader::new(); 111 | 112 | let set_dwarf = 113 | self.passopts.debug_info && !will_remove_debug_info(&self.passes.more_passes); 114 | reader.set_dwarf(set_dwarf); 115 | 116 | match self.reader.file_type { 117 | FileType::Wasm => reader.read_binary(infile, &mut m, infile_sourcemap), 118 | FileType::Wat => reader.read_text(infile, &mut m), 119 | FileType::Any => reader.read(infile, &mut m, infile_sourcemap), 120 | } 121 | .map_err(|e| OptimizationError::Read { 122 | source: Box::from(e), 123 | })?; 124 | } 125 | 126 | { 127 | if self.passopts.validate && !validate_wasm(&mut m) { 128 | return Err(OptimizationError::ValidateWasmInput); 129 | } 130 | 131 | self.create_and_run_pass_runner(&mut m); 132 | 133 | if self.converge { 134 | self.run_until_convergence(&mut m) 135 | .map_err(|e| OptimizationError::Write { 136 | source: Box::from(e), 137 | })?; 138 | } 139 | 140 | if self.passopts.validate && !validate_wasm(&mut m) { 141 | return Err(OptimizationError::ValidateWasmOutput); 142 | } 143 | } 144 | 145 | { 146 | let mut writer = ModuleWriter::new(); 147 | writer.set_debug_info(self.passopts.debug_info); 148 | 149 | if let Some(filename) = outfile_sourcemap { 150 | writer 151 | .set_source_map_filename(filename) 152 | .map_err(|e| OptimizationError::Write { 153 | source: Box::from(e), 154 | })?; 155 | } 156 | 157 | if let Some(url) = sourcemap_url { 158 | writer.set_source_map_url(url); 159 | } 160 | 161 | match self.writer.file_type { 162 | FileType::Wasm => writer.write_binary(&mut m, outfile), 163 | FileType::Wat => writer.write_text(&mut m, outfile), 164 | FileType::Any => match self.reader.file_type { 165 | FileType::Any | FileType::Wasm => writer.write_binary(&mut m, outfile), 166 | FileType::Wat => writer.write_text(&mut m, outfile), 167 | }, 168 | } 169 | .map_err(|e| OptimizationError::Write { 170 | source: Box::from(e), 171 | })?; 172 | } 173 | 174 | Ok(()) 175 | } 176 | 177 | fn create_and_run_pass_runner(&self, m: &mut Module) { 178 | let passopts = self.translate_pass_options(); 179 | 180 | let mut pass_runner = PassRunner::new_with_options(m, passopts); 181 | 182 | if self.passes.add_default_passes { 183 | pass_runner.add_default_optimization_passes(); 184 | } 185 | 186 | self.passes 187 | .more_passes 188 | .iter() 189 | .for_each(|pass| pass_runner.add(pass.name())); 190 | 191 | pass_runner.run(); 192 | } 193 | 194 | fn run_until_convergence(&self, m: &mut Module) -> anyhow::Result<()> { 195 | let mut last_size = Self::get_module_size(m)?; 196 | let mut current_size; 197 | 198 | loop { 199 | self.create_and_run_pass_runner(m); 200 | 201 | current_size = Self::get_module_size(m)?; 202 | 203 | if current_size >= last_size { 204 | break; 205 | } 206 | 207 | last_size = current_size; 208 | } 209 | 210 | Ok(()) 211 | } 212 | 213 | fn get_module_size(m: &mut Module) -> anyhow::Result { 214 | let tempdir = tempfile::tempdir()?; 215 | let temp_outfile = tempdir.path().join("wasm_opt_temp_outfile.wasm"); 216 | 217 | let mut writer = ModuleWriter::new(); 218 | writer.write_binary(m, &temp_outfile)?; 219 | 220 | let file_size = fs::read(&temp_outfile)?.len(); 221 | 222 | Ok(file_size) 223 | } 224 | 225 | fn apply_features(&self, m: &mut Module) { 226 | let (enabled_features, disabled_features) = convert_feature_sets(&self.features); 227 | 228 | m.apply_features(enabled_features, disabled_features); 229 | } 230 | 231 | fn translate_pass_options(&self) -> BasePassOptions { 232 | let mut opts = BasePassOptions::new(); 233 | 234 | opts.set_validate(self.passopts.validate); 235 | opts.set_validate_globally(self.passopts.validate_globally); 236 | opts.set_optimize_level(self.passopts.optimize_level as i32); 237 | opts.set_shrink_level(self.passopts.shrink_level as i32); 238 | opts.set_traps_never_happen(self.passopts.traps_never_happen); 239 | opts.set_low_memory_unused(self.passopts.low_memory_unused); 240 | opts.set_fast_math(self.passopts.fast_math); 241 | opts.set_zero_filled_memory(self.passopts.zero_filled_memory); 242 | opts.set_debug_info(self.passopts.debug_info); 243 | 244 | self.passopts 245 | .arguments 246 | .iter() 247 | .for_each(|(key, value)| opts.set_arguments(key, value)); 248 | 249 | let mut inlining = BaseInliningOptions::new(); 250 | inlining.set_always_inline_max_size(self.inlining.always_inline_max_size); 251 | inlining.set_one_caller_inline_max_size(self.inlining.one_caller_inline_max_size); 252 | inlining.set_flexible_inline_max_size(self.inlining.flexible_inline_max_size); 253 | inlining.set_allow_functions_with_loops(self.inlining.allow_functions_with_loops); 254 | inlining.set_partial_inlining_ifs(self.inlining.partial_inlining_ifs); 255 | 256 | opts.set_inlining_options(inlining); 257 | 258 | opts 259 | } 260 | } 261 | 262 | fn will_remove_debug_info(passes: &[Pass]) -> bool { 263 | passes 264 | .iter() 265 | .any(|pass| PassRunner::pass_removes_debug_info(pass.name()) == true) 266 | } 267 | 268 | fn convert_feature_sets(features: &Features) -> (BaseFeatureSet, BaseFeatureSet) { 269 | let mut feature_set_enabled = BaseFeatureSet::new(); 270 | let mut feature_set_disabled = BaseFeatureSet::new(); 271 | 272 | match features.baseline { 273 | FeatureBaseline::Default => { 274 | feature_set_enabled.set(BaseFeature::Default, true); 275 | } 276 | FeatureBaseline::MvpOnly => { 277 | feature_set_enabled.set_mvp(); 278 | feature_set_disabled.set_all(); 279 | } 280 | FeatureBaseline::All => { 281 | feature_set_enabled.set_all(); 282 | feature_set_disabled.set_mvp(); 283 | } 284 | } 285 | 286 | features.enabled.iter().for_each(|f| { 287 | let feature = convert_feature(f); 288 | feature_set_enabled.set(feature, true); 289 | feature_set_disabled.set(feature, false); 290 | }); 291 | 292 | features.disabled.iter().for_each(|f| { 293 | let feature = convert_feature(f); 294 | feature_set_enabled.set(feature, false); 295 | feature_set_disabled.set(feature, true); 296 | }); 297 | 298 | (feature_set_enabled, feature_set_disabled) 299 | } 300 | 301 | fn convert_feature(feature: &Feature) -> BaseFeature { 302 | match feature { 303 | Feature::None => BaseFeature::None, 304 | Feature::Atomics => BaseFeature::Atomics, 305 | Feature::MutableGlobals => BaseFeature::MutableGlobals, 306 | Feature::TruncSat => BaseFeature::TruncSat, 307 | Feature::Simd => BaseFeature::Simd, 308 | Feature::BulkMemory => BaseFeature::BulkMemory, 309 | Feature::SignExt => BaseFeature::SignExt, 310 | Feature::ExceptionHandling => BaseFeature::ExceptionHandling, 311 | Feature::TailCall => BaseFeature::TailCall, 312 | Feature::ReferenceTypes => BaseFeature::ReferenceTypes, 313 | Feature::Multivalue => BaseFeature::Multivalue, 314 | Feature::Gc => BaseFeature::Gc, 315 | Feature::Memory64 => BaseFeature::Memory64, 316 | Feature::RelaxedSimd => BaseFeature::RelaxedSimd, 317 | Feature::ExtendedConst => BaseFeature::ExtendedConst, 318 | Feature::Strings => BaseFeature::Strings, 319 | Feature::MultiMemory => BaseFeature::MultiMemory, 320 | Feature::Mvp => BaseFeature::None, 321 | Feature::Default => BaseFeature::Default, 322 | Feature::All => BaseFeature::All, 323 | } 324 | } 325 | 326 | #[cfg(test)] 327 | mod test { 328 | use super::*; 329 | 330 | fn has(set: &BaseFeatureSet, feature: BaseFeature) -> bool { 331 | let mut new_set = BaseFeatureSet::new(); 332 | new_set.set(feature, true); 333 | set.has(&new_set) 334 | } 335 | 336 | #[test] 337 | fn test_features_default() { 338 | let features = Features::default(); 339 | let (enabled, disabled) = convert_feature_sets(&features); 340 | 341 | assert_eq!(enabled.as_int(), BaseFeature::Default as u32); 342 | assert_eq!(disabled.as_int(), BaseFeature::None as u32); 343 | 344 | assert!(has(&enabled, BaseFeature::SignExt)); 345 | assert!(!has(&disabled, BaseFeature::SignExt)); 346 | assert!(has(&enabled, BaseFeature::MutableGlobals)); 347 | assert!(!has(&disabled, BaseFeature::MutableGlobals)); 348 | } 349 | 350 | #[test] 351 | fn test_features_remove_defaults() { 352 | let mut opts = OptimizationOptions::new_optimize_for_size(); 353 | opts.disable_feature(Feature::SignExt) 354 | .disable_feature(Feature::MutableGlobals); 355 | let (enabled, disabled) = convert_feature_sets(&opts.features); 356 | 357 | assert!(!has(&enabled, BaseFeature::SignExt)); 358 | assert!(has(&disabled, BaseFeature::SignExt)); 359 | assert!(!has(&enabled, BaseFeature::MutableGlobals)); 360 | assert!(has(&disabled, BaseFeature::MutableGlobals)); 361 | } 362 | 363 | #[test] 364 | fn test_features_mvp_and_enable() { 365 | let mut opts = OptimizationOptions::new_optimize_for_size(); 366 | opts.mvp_features_only(); 367 | 368 | let (enabled, disabled) = convert_feature_sets(&opts.features); 369 | 370 | assert!(has(&enabled, BaseFeature::None)); 371 | assert!(has(&disabled, BaseFeature::All)); 372 | 373 | assert!(!has(&enabled, BaseFeature::Gc)); 374 | assert!(has(&disabled, BaseFeature::Gc)); 375 | 376 | opts.enable_feature(Feature::Gc); 377 | 378 | let (enabled, disabled) = convert_feature_sets(&opts.features); 379 | 380 | assert!(has(&enabled, BaseFeature::Gc)); 381 | assert!(!has(&disabled, BaseFeature::Gc)); 382 | 383 | // Other features still disabled 384 | 385 | assert!(!has(&enabled, BaseFeature::Atomics)); 386 | assert!(has(&disabled, BaseFeature::Atomics)); 387 | } 388 | 389 | #[test] 390 | fn test_features_all_and_disable() { 391 | let mut opts = OptimizationOptions::new_optimize_for_size(); 392 | opts.all_features(); 393 | 394 | let (enabled, disabled) = convert_feature_sets(&opts.features); 395 | 396 | assert!(has(&enabled, BaseFeature::All)); 397 | assert!(has(&disabled, BaseFeature::None)); 398 | 399 | assert!(has(&enabled, BaseFeature::Gc)); 400 | assert!(!has(&disabled, BaseFeature::Gc)); 401 | 402 | opts.disable_feature(Feature::Gc); 403 | 404 | let (enabled, disabled) = convert_feature_sets(&opts.features); 405 | 406 | assert!(!has(&enabled, BaseFeature::Gc)); 407 | assert!(has(&disabled, BaseFeature::Gc)); 408 | 409 | // Other features still enabled 410 | 411 | assert!(has(&enabled, BaseFeature::Atomics)); 412 | assert!(!has(&disabled, BaseFeature::Atomics)); 413 | } 414 | } 415 | -------------------------------------------------------------------------------- /components/wasm-opt-sys/build.rs: -------------------------------------------------------------------------------- 1 | use cxx_build::CFG; 2 | use std::fmt::Write as FmtWrite; 3 | use std::fs::{self, File}; 4 | use std::io::{BufRead, BufReader, BufWriter, Write}; 5 | use std::path::{Path, PathBuf}; 6 | 7 | fn main() -> anyhow::Result<()> { 8 | check_cxx17_support()?; 9 | 10 | let output_dir = std::env::var("OUT_DIR")?; 11 | let output_dir = Path::new(&output_dir); 12 | 13 | let binaryen_dir = get_binaryen_dir()?; 14 | 15 | let src_dir = binaryen_dir.join("src"); 16 | let src_files = get_src_files(&src_dir)?; 17 | 18 | #[cfg(feature = "dwarf")] 19 | let llvm_dir = binaryen_dir.join("third_party/llvm-project"); 20 | #[cfg(feature = "dwarf")] 21 | let llvm_files = get_llvm_files(&llvm_dir)?; 22 | 23 | let tools_dir = src_dir.join("tools"); 24 | let wasm_opt_src = tools_dir.join("wasm-opt.cpp"); 25 | let wasm_opt_src = get_converted_wasm_opt_cpp(&wasm_opt_src)?; 26 | 27 | let wasm_intrinsics_src = get_converted_wasm_intrinsics_cpp(&src_dir)?; 28 | 29 | let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?; 30 | let manifest_dir = Path::new(&manifest_dir); 31 | let wasm_opt_main_shim = manifest_dir.join("src/wasm-opt-main-shim.cpp"); 32 | 33 | create_config_header()?; 34 | 35 | // Set up cxx's include path so that wasm-opt-cxx-sys's C++ header can 36 | // include from these same dirs. 37 | CFG.exported_header_dirs.push(&src_dir); 38 | CFG.exported_header_dirs.push(&tools_dir); 39 | CFG.exported_header_dirs.push(&output_dir); 40 | 41 | #[cfg(feature = "dwarf")] 42 | { 43 | let llvm_include = llvm_dir.join("include"); 44 | CFG.exported_header_dirs.push(&llvm_include); 45 | } 46 | 47 | let mut builder = cxx_build::bridge("src/lib.rs"); 48 | 49 | { 50 | let target_env = std::env::var("CARGO_CFG_TARGET_ENV")?; 51 | 52 | let flags: &[_] = if target_env != "msvc" { 53 | &[ 54 | "-std=c++17", 55 | "-w", 56 | "-Wno-unused-parameter", 57 | "-DTHROW_ON_FATAL", 58 | #[cfg(feature = "dwarf")] 59 | "-DBUILD_LLVM_DWARF", 60 | "-DNDEBUG", 61 | ] 62 | } else { 63 | &[ 64 | "/std:c++17", 65 | "/w", 66 | "/DTHROW_ON_FATAL", 67 | #[cfg(feature = "dwarf")] 68 | "/DBUILD_LLVM_DWARF", 69 | "/DNDEBUG", 70 | ] 71 | }; 72 | 73 | for flag in flags { 74 | builder.flag(flag); 75 | } 76 | } 77 | 78 | builder 79 | .file(wasm_opt_main_shim) 80 | .files(src_files) 81 | .file(wasm_opt_src) 82 | .file(wasm_intrinsics_src); 83 | 84 | #[cfg(feature = "dwarf")] 85 | builder.files(&llvm_files); 86 | 87 | builder.compile("wasm-opt-cc"); 88 | 89 | Ok(()) 90 | } 91 | 92 | /// Finds the binaryen source directory. 93 | /// 94 | /// During development this will be at the workspace level submodule, 95 | /// but as packaged, will be a subdirectory of the manifest directory. 96 | /// 97 | /// The packaged subdirectories are put in place by `publish.sh`. 98 | /// 99 | /// The packaged source is pre-processed to remove Binaryen's large test suite. 100 | fn get_binaryen_dir() -> anyhow::Result { 101 | let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?; 102 | let manifest_dir = Path::new(&manifest_dir); 103 | let binaryen_packaged_dir = manifest_dir.join("binaryen"); 104 | let binaryen_submodule_dir = manifest_dir.join("../../binaryen"); 105 | 106 | match ( 107 | binaryen_packaged_dir.is_dir(), 108 | binaryen_submodule_dir.is_dir(), 109 | ) { 110 | (true, _) => Ok(binaryen_packaged_dir), 111 | (_, true) => Ok(binaryen_submodule_dir), 112 | (false, false) => anyhow::bail!( 113 | "binaryen source directory doesn't exist (maybe `git submodule update --init`?)" 114 | ), 115 | } 116 | } 117 | 118 | /// Replaces the `main` declaration with a C ABI and a different name. 119 | /// 120 | /// It can be called from Rust and doesn't clash with Rust's `main`. 121 | fn get_converted_wasm_opt_cpp(src_dir: &Path) -> anyhow::Result { 122 | let wasm_opt_file = File::open(src_dir)?; 123 | let reader = BufReader::new(wasm_opt_file); 124 | 125 | let output_dir = std::env::var("OUT_DIR")?; 126 | let output_dir = Path::new(&output_dir); 127 | 128 | let temp_file_dir = output_dir.join("wasm_opt.cpp.temp"); 129 | let temp_file = File::create(&temp_file_dir)?; 130 | 131 | let mut writer = BufWriter::new(temp_file); 132 | for line in reader.lines() { 133 | let mut line = line?; 134 | 135 | if line.contains("int main") { 136 | line = line.replace("int main", "extern \"C\" int wasm_opt_main_actual"); 137 | } 138 | 139 | writer.write_all(line.as_bytes())?; 140 | writer.write_all(b"\n")?; 141 | } 142 | 143 | let output_wasm_opt_file = output_dir.join("wasm-opt.cpp"); 144 | fs::rename(&temp_file_dir, &output_wasm_opt_file)?; 145 | 146 | Ok(output_wasm_opt_file) 147 | } 148 | 149 | fn get_src_files(src_dir: &Path) -> anyhow::Result> { 150 | let analysis_dir = src_dir.join("analysis"); 151 | let analysis_files = ["cfg.cpp"]; 152 | let analysis_files = analysis_files.iter().map(|f| analysis_dir.join(f)); 153 | 154 | let wasm_dir = src_dir.join("wasm"); 155 | let wasm_files = [ 156 | "literal.cpp", 157 | "parsing.cpp", 158 | "wasm-binary.cpp", 159 | "wasm-debug.cpp", 160 | "wasm-emscripten.cpp", 161 | "wasm-interpreter.cpp", 162 | "wasm-ir-builder.cpp", 163 | "wasm-io.cpp", 164 | "wasm-stack.cpp", 165 | "wasm-s-parser.cpp", 166 | "wasm-type.cpp", 167 | "wasm-validator.cpp", 168 | "wasm.cpp", 169 | "wat-lexer.cpp", 170 | "wat-parser.cpp", 171 | ]; 172 | let wasm_files = wasm_files.iter().map(|f| wasm_dir.join(f)); 173 | 174 | let support_dir = src_dir.join("support"); 175 | let support_files = [ 176 | "bits.cpp", 177 | "colors.cpp", 178 | "command-line.cpp", 179 | "debug.cpp", 180 | "dfa_minimization.cpp", 181 | "file.cpp", 182 | "safe_integer.cpp", 183 | "threads.cpp", 184 | "utilities.cpp", 185 | "istring.cpp", 186 | ]; 187 | let support_files = support_files.iter().map(|f| support_dir.join(f)); 188 | 189 | let ir_dir = src_dir.join("ir"); 190 | let ir_files = [ 191 | "drop.cpp", 192 | "eh-utils.cpp", 193 | "ExpressionManipulator.cpp", 194 | "ExpressionAnalyzer.cpp", 195 | "export-utils.cpp", 196 | "LocalGraph.cpp", 197 | "LocalStructuralDominance.cpp", 198 | "lubs.cpp", 199 | "memory-utils.cpp", 200 | "module-utils.cpp", 201 | "names.cpp", 202 | "possible-contents.cpp", 203 | "properties.cpp", 204 | "ReFinalize.cpp", 205 | "stack-utils.cpp", 206 | "table-utils.cpp", 207 | "type-updating.cpp", 208 | ]; 209 | let ir_files = ir_files.iter().map(|f| ir_dir.join(f)); 210 | 211 | let passes_dir = src_dir.join("passes"); 212 | let passes_files = get_files_from_dir(&passes_dir)?; 213 | 214 | let fuzzing_dir = src_dir.join("tools/fuzzing"); 215 | let fuzzing_files = ["fuzzing.cpp", "random.cpp", "heap-types.cpp"]; 216 | let fuzzing_files = fuzzing_files.iter().map(|f| fuzzing_dir.join(f)); 217 | 218 | let asmjs_dir = src_dir.join("asmjs"); 219 | let asmjs_files = ["asm_v_wasm.cpp", "shared-constants.cpp"]; 220 | let asmjs_files = asmjs_files.iter().map(|f| asmjs_dir.join(f)); 221 | 222 | let cfg_dir = src_dir.join("cfg"); 223 | let cfg_files = ["Relooper.cpp"]; 224 | let cfg_files = cfg_files.iter().map(|f| cfg_dir.join(f)); 225 | 226 | let file_intrinsics = disambiguate_file(&ir_dir.join("intrinsics.cpp"), "intrinsics-ir.cpp")?; 227 | 228 | let src_files: Vec<_> = None 229 | .into_iter() 230 | .chain(analysis_files) 231 | .chain(wasm_files) 232 | .chain(support_files) 233 | .chain(ir_files) 234 | .chain(passes_files) 235 | .chain(fuzzing_files) 236 | .chain(asmjs_files) 237 | .chain(cfg_files) 238 | .chain(Some(file_intrinsics).into_iter()) 239 | .collect(); 240 | 241 | Ok(src_files) 242 | } 243 | 244 | fn get_files_from_dir(src_dir: &Path) -> anyhow::Result + '_> { 245 | let files = fs::read_dir(src_dir)? 246 | .map(|f| f.expect("error reading dir")) 247 | .filter(|f| f.file_name().into_string().expect("UTF8").ends_with(".cpp")) 248 | .map(move |f| src_dir.join(f.path())); 249 | 250 | Ok(files) 251 | } 252 | 253 | fn disambiguate_file(input_file: &Path, new_file_name: &str) -> anyhow::Result { 254 | let output_dir = std::env::var("OUT_DIR")?; 255 | let output_dir = Path::new(&output_dir); 256 | let output_file = output_dir.join(new_file_name); 257 | 258 | fs::copy(input_file, &output_file)?; 259 | 260 | Ok(output_file) 261 | } 262 | 263 | /// Pre-process the WasmIntrinsics.cpp.in file and return a path to the processed file. 264 | /// 265 | /// This file needs to be injected with the contents of wasm-intrinsics.wat, 266 | /// replacing `@WASM_INTRINSICS_SIZE@` with the size of the wat + 1, 267 | /// and `@WASM_INTRINSICS_EMBED@` with the hex-encoded contents of the wat, 268 | /// appended with `0x00`. 269 | /// 270 | /// The extra byte is presumably a null terminator. 271 | fn get_converted_wasm_intrinsics_cpp(src_dir: &Path) -> anyhow::Result { 272 | let src_passes_dir = src_dir.join("passes"); 273 | 274 | let output_dir = std::env::var("OUT_DIR")?; 275 | let output_dir = Path::new(&output_dir); 276 | 277 | let wasm_intrinsics_cpp_in_file = src_passes_dir.join("WasmIntrinsics.cpp.in"); 278 | let wasm_intrinsics_cpp_out_file = output_dir.join("WasmIntrinsics.cpp"); 279 | 280 | let (wasm_intrinsics_wat_hex, wasm_intrinsics_wat_bytes) = 281 | load_wasm_intrinsics_wat(&src_passes_dir)?; 282 | 283 | configure_file( 284 | &wasm_intrinsics_cpp_in_file, 285 | &wasm_intrinsics_cpp_out_file, 286 | &[ 287 | ( 288 | "WASM_INTRINSICS_SIZE", 289 | format!("{}", wasm_intrinsics_wat_bytes), 290 | ), 291 | ("WASM_INTRINSICS_EMBED", wasm_intrinsics_wat_hex), 292 | ], 293 | )?; 294 | 295 | Ok(wasm_intrinsics_cpp_out_file) 296 | } 297 | 298 | fn load_wasm_intrinsics_wat(passes_dir: &Path) -> anyhow::Result<(String, usize)> { 299 | let wasm_intrinsics_wat = passes_dir.join("wasm-intrinsics.wat"); 300 | let wat_contents = std::fs::read_to_string(&wasm_intrinsics_wat)?; 301 | 302 | let mut buffer = String::with_capacity(wat_contents.len() * 5 /* 0xNN, */ + 4 /* null */); 303 | 304 | for byte in wat_contents.bytes() { 305 | write!(buffer, "0x{:02x},", byte)?; 306 | } 307 | write!(buffer, "0x00")?; 308 | 309 | Ok((buffer, wat_contents.len() + 1)) 310 | } 311 | 312 | /// A rough implementation of CMake's `configure_file` directive. 313 | /// 314 | /// Consume `src_file` and output `dst_file`. 315 | /// 316 | /// `replacements` is a list of key-value pairs from variable name 317 | /// to a textual substitute for that variable. 318 | /// 319 | /// Any variables in the source file, surrounded by `@`, e.g. 320 | /// `@WASM_INTRINSICS_SIZE@`, will be replaced with the specified value. The 321 | /// variable as specified in the `replacements` list does not include the `@` 322 | /// symbols. 323 | /// 324 | /// re: 325 | fn configure_file( 326 | src_file: &Path, 327 | dst_file: &Path, 328 | replacements: &[(&str, String)], 329 | ) -> anyhow::Result<()> { 330 | let mut src = std::fs::read_to_string(src_file)?; 331 | 332 | for (var, txt) in replacements { 333 | let var = format!("@{}@", var); 334 | src = src.replace(&var, txt); 335 | } 336 | 337 | std::fs::write(dst_file, src)?; 338 | 339 | Ok(()) 340 | } 341 | 342 | fn create_config_header() -> anyhow::Result<()> { 343 | let output_dir = std::env::var("OUT_DIR")?; 344 | let output_dir = Path::new(&output_dir); 345 | let config_file = output_dir.join("config.h"); 346 | 347 | let config_text = "#define PROJECT_VERSION \"116 (version_116)\""; 348 | 349 | fs::write(&config_file, config_text)?; 350 | 351 | Ok(()) 352 | } 353 | 354 | fn check_cxx17_support() -> anyhow::Result<()> { 355 | let mut builder = cc::Build::new(); 356 | builder.cpp(true); 357 | 358 | let target_env = std::env::var("CARGO_CFG_TARGET_ENV")?; 359 | let cxx17_flag = if target_env != "msvc" { 360 | "-std=c++17" 361 | } else { 362 | "/std:c++17" 363 | }; 364 | 365 | if !builder.is_flag_supported(cxx17_flag)? { 366 | return Err(anyhow::anyhow!( 367 | "C++ compiler does not support `{}` flag", 368 | cxx17_flag 369 | )); 370 | } 371 | 372 | Ok(()) 373 | } 374 | 375 | #[cfg(feature = "dwarf")] 376 | fn get_llvm_files(llvm_dir: &Path) -> anyhow::Result<[PathBuf; 63]> { 377 | let llvm_dwarf = disambiguate_file(&llvm_dir.join("Dwarf.cpp"), "LLVMDwarf.cpp")?; 378 | let llvm_debug = disambiguate_file(&llvm_dir.join("Debug.cpp"), "LLVMDebug.cpp")?; 379 | // Array taken from 380 | // https://github.com/WebAssembly/binaryen/blob/616c08bc1ec604a822d354cbb0353b7994cec72d/third_party/llvm-project/CMakeLists.txt 381 | Ok([ 382 | llvm_dir.join("Binary.cpp"), 383 | llvm_dir.join("ConvertUTF.cpp"), 384 | llvm_dir.join("DataExtractor.cpp"), 385 | llvm_dir.join("DJB.cpp"), 386 | llvm_debug, 387 | llvm_dir.join("dwarf2yaml.cpp"), 388 | llvm_dir.join("DWARFAbbreviationDeclaration.cpp"), 389 | llvm_dir.join("DWARFAcceleratorTable.cpp"), 390 | llvm_dir.join("DWARFAddressRange.cpp"), 391 | llvm_dir.join("DWARFCompileUnit.cpp"), 392 | llvm_dir.join("DWARFContext.cpp"), 393 | llvm_dir.join("DWARFDataExtractor.cpp"), 394 | llvm_dir.join("DWARFDebugAbbrev.cpp"), 395 | llvm_dir.join("DWARFDebugAddr.cpp"), 396 | llvm_dir.join("DWARFDebugAranges.cpp"), 397 | llvm_dir.join("DWARFDebugArangeSet.cpp"), 398 | llvm_dir.join("DWARFDebugFrame.cpp"), 399 | llvm_dir.join("DWARFDebugInfoEntry.cpp"), 400 | llvm_dir.join("DWARFDebugLine.cpp"), 401 | llvm_dir.join("DWARFDebugLoc.cpp"), 402 | llvm_dir.join("DWARFDebugMacro.cpp"), 403 | llvm_dir.join("DWARFDebugPubTable.cpp"), 404 | llvm_dir.join("DWARFDebugRangeList.cpp"), 405 | llvm_dir.join("DWARFDebugRnglists.cpp"), 406 | llvm_dir.join("DWARFDie.cpp"), 407 | llvm_dir.join("DWARFEmitter.cpp"), 408 | llvm_dir.join("DWARFExpression.cpp"), 409 | llvm_dir.join("DWARFFormValue.cpp"), 410 | llvm_dir.join("DWARFGdbIndex.cpp"), 411 | llvm_dir.join("DWARFListTable.cpp"), 412 | llvm_dir.join("DWARFTypeUnit.cpp"), 413 | llvm_dir.join("DWARFUnit.cpp"), 414 | llvm_dir.join("DWARFUnitIndex.cpp"), 415 | llvm_dir.join("DWARFVerifier.cpp"), 416 | llvm_dir.join("DWARFVisitor.cpp"), 417 | llvm_dir.join("DWARFYAML.cpp"), 418 | llvm_dir.join("Error.cpp"), 419 | llvm_dir.join("ErrorHandling.cpp"), 420 | llvm_dir.join("FormatVariadic.cpp"), 421 | llvm_dir.join("Hashing.cpp"), 422 | llvm_dir.join("LEB128.cpp"), 423 | llvm_dir.join("LineIterator.cpp"), 424 | llvm_dir.join("MCRegisterInfo.cpp"), 425 | llvm_dir.join("MD5.cpp"), 426 | llvm_dir.join("MemoryBuffer.cpp"), 427 | llvm_dir.join("NativeFormatting.cpp"), 428 | llvm_dir.join("ObjectFile.cpp"), 429 | llvm_dir.join("obj2yaml_Error.cpp"), 430 | llvm_dir.join("Optional.cpp"), 431 | llvm_dir.join("Path.cpp"), 432 | llvm_dir.join("raw_ostream.cpp"), 433 | llvm_dir.join("ScopedPrinter.cpp"), 434 | llvm_dir.join("SmallVector.cpp"), 435 | llvm_dir.join("SourceMgr.cpp"), 436 | llvm_dir.join("StringMap.cpp"), 437 | llvm_dir.join("StringRef.cpp"), 438 | llvm_dir.join("SymbolicFile.cpp"), 439 | llvm_dir.join("Twine.cpp"), 440 | llvm_dir.join("UnicodeCaseFold.cpp"), 441 | llvm_dir.join("WithColor.cpp"), 442 | llvm_dir.join("YAMLParser.cpp"), 443 | llvm_dir.join("YAMLTraits.cpp"), 444 | llvm_dwarf, 445 | ]) 446 | } 447 | -------------------------------------------------------------------------------- /components/wasm-opt/src/passes.rs: -------------------------------------------------------------------------------- 1 | use crate::base::pass_registry; 2 | use strum_macros::EnumIter; 3 | 4 | /// A Binaryen optimization pass. 5 | /// 6 | /// These have the same names as given on the command line to 7 | /// `wasm-opt`, but with Rust capitalization conventions. 8 | // Keep these in the same order as PassRegistry::registerPasses 9 | #[non_exhaustive] 10 | #[derive(Clone, Debug, EnumIter)] 11 | pub enum Pass { 12 | /// Lower unaligned loads and stores to smaller aligned ones. 13 | AlignmentLowering, 14 | /// Async/await style transform, allowing pausing and resuming. 15 | Asyncify, 16 | /// Tries to avoid reinterpret operations via more loads. 17 | AvoidReinterprets, 18 | /// Removes arguments to calls in an lto-like manner. 19 | Dae, 20 | /// Removes arguments to calls in an lto-like manner, and optimizes where removed. 21 | DaeOptimizing, 22 | /// Refine and merge abstract (never-created) types. 23 | AbstractTypeRefining, 24 | /// Reduce # of locals by coalescing. 25 | CoalesceLocals, 26 | /// Reduce # of locals by coalescing and learning. 27 | CoalesceLocalsLearning, 28 | /// Push code forward, potentially making it not always execute. 29 | CodePushing, 30 | /// Fold code, merging duplicates. 31 | CodeFolding, 32 | /// Hoist repeated constants to a local. 33 | ConstHoisting, 34 | /// Propagate constant struct field values. 35 | Cfp, 36 | /// Removes unreachable code. 37 | Dce, 38 | /// Forces all loads and stores to have alignment 1. 39 | Dealign, 40 | /// Instrument the wasm to convert NaNs into 0 at runtime. 41 | DeNan, 42 | /// Turns indirect calls into direct ones. 43 | Directize, 44 | /// Discards global effect info. 45 | DiscardGlobalEffects, 46 | /// Optimizes using the DataFlow SSA IR. 47 | Dfo, 48 | /// Dump DWARF debug info sections from the read binary. 49 | DwarfDump, 50 | /// Removes duplicate imports. 51 | DuplicateImportElimination, 52 | /// Removes duplicate functions. 53 | DuplicateFunctionElimination, 54 | /// Emit the target features section in the output. 55 | EmitTargetFeatures, 56 | /// Leaves just one function (useful for debugging). 57 | ExtractFunction, 58 | /// Leaves just one function selected by index. 59 | ExtractFunctionIndex, 60 | /// Flattens out code, removing nesting. 61 | Flatten, 62 | /// Emulates function pointer casts, allowing incorrect indirect calls to (sometimes) work. 63 | FpCastEmu, 64 | /// Reports function metrics. 65 | FuncMetrics, 66 | /// Generate dynCall fuctions used by emscripten ABI. 67 | GenerateDyncalls, 68 | /// Generate dynCall functions used by emscripten ABI, but only for functions with i64 in their signature (which cannot be invoked via the wasm table without JavaScript BigInt support). 69 | GenerateI64Dyncalls, 70 | /// Generate global effect info (helps later passes). 71 | GenerateGlobalEffects, 72 | /// Generate Stack IR. 73 | GenerateStackIr, 74 | /// Refine the types of globals. 75 | GlobalRefining, 76 | /// Globally optimize GC types. 77 | Gto, 78 | /// Globally optimize struct values. 79 | Gsi, 80 | /// Grand unified flow analyses. 81 | /// 82 | /// Optimize the entire program using information about what content can actually appear in each location. 83 | Gufa, 84 | /// GUFA plus add casts for all inferences. 85 | GufaCastAll, 86 | /// Gufa plus local optimizations in functions we modified. 87 | GufaOptimizing, 88 | /// Apply more specific subtypes to type fields where possible. 89 | TypeRefining, 90 | /// Replace GC allocations with locals. 91 | Heap2Local, 92 | /// Inline __original_main into main. 93 | InlineMain, 94 | /// Inline functions (you probably want inlining-optimizing). 95 | Inlining, 96 | /// Inline functions and optimizes where we inlined. 97 | InliningOptimizing, 98 | /// Lower away binaryen intrinsics. 99 | IntrinsicLowering, 100 | /// Wrap imports and exports for JavaScript promise integration. 101 | Jspi, 102 | /// Legalizes i64 types on the import/export boundary. 103 | LegalizeJsInterface, 104 | /// Legalizes i64 types on the import/export boundary in a minimal manner, only on things only JS will call. 105 | LegalizeJsInterfaceMinimally, 106 | /// Common subexpression elimination inside basic blocks. 107 | LocalCse, 108 | /// Apply more specific subtypes to locals where possible. 109 | LocalSubtyping, 110 | /// Instrument the build with logging of where execution goes. 111 | LogExecution, 112 | /// Lower all uses of i64s to use i32s instead. 113 | I64ToI32Lowering, 114 | /// Instrument the build with code to intercept all loads and stores. 115 | InstrumentLocals, 116 | /// Instrument the build with code to intercept all loads and stores. 117 | InstrumentMemory, 118 | /// Loop invariant code motion. 119 | Licm, 120 | /// Attempt to merge segments to fit within web limits. 121 | LimitSegments, 122 | /// Lower loads and stores to a 64-bit memory to instead use a 32-bit one. 123 | Memory64Lowering, 124 | /// Packs memory into separate segments, skipping zeros. 125 | MemoryPacking, 126 | /// Merges blocks to their parents. 127 | MergeBlocks, 128 | /// Merges similar functions when benefical. 129 | MergeSimilarFunctions, 130 | /// Merges locals when beneficial. 131 | MergeLocals, 132 | /// Reports metrics. 133 | Metrics, 134 | /// Minifies import names (only those, and not export names), and emits a mapping to the minified ones. 135 | MinifyImports, 136 | /// Minifies both import and export names, and emits a mapping to the minified ones. 137 | MinifyImportsAndExports, 138 | /// Minifies both import and export names, and emits a mapping to the minified ones, and minifies the modules as well. 139 | MinifyImportsAndExportsAndModules, 140 | /// Apply the assumption that asyncify imports always unwind, and we never rewind. 141 | ModAsyncifyAlwaysAndOnlyUnwind, 142 | /// Apply the assumption that asyncify never unwinds. 143 | ModAsyncifyNeverUnwind, 144 | /// Creates specialized versions of functions. 145 | Monomorphize, 146 | /// Creates specialized versions of functions (even if unhelpful). 147 | MonomorphizeAlways, 148 | /// Combines multiple memories into a single memory. 149 | MultiMemoryLowering, 150 | /// Combines multiple memories into a single memory, trapping if the read or write is larger than the length of the memory's data. 151 | MultiMemoryLoweringWithBoundsChecks, 152 | /// Name list. 153 | Nm, 154 | /// (Re)name all heap types. 155 | NameTypes, 156 | /// Reduces calls to code that only runs once. 157 | OnceReduction, 158 | /// Optimizes added constants into load/store offsets. 159 | OptimizeAddedConstants, 160 | /// Optimizes added constants into load/store offsets, propagating them across locals too. 161 | OptimizeAddedConstantsPropagate, 162 | /// Eliminate and reuse casts. 163 | OptimizeCasts, 164 | /// Optimizes instruction combinations. 165 | OptimizeInstructions, 166 | /// Optimize Stack IR. 167 | OptimizeStackIr, 168 | /// Pick load signs based on their uses. 169 | PickLoadSigns, 170 | /// Tranform Binaryen IR into Poppy IR. 171 | Poppify, 172 | /// Miscellaneous optimizations for Emscripten-generated code. 173 | PostEmscripten, 174 | /// Early optimize of the instruction combinations for js. 175 | OptimizeForJs, 176 | /// Computes compile-time evaluatable expressions. 177 | Precompute, 178 | /// Computes compile-time evaluatable expressions and propagates. 179 | PrecomputePropagate, 180 | /// Print in s-expression format. 181 | Print, 182 | /// Print in minified s-expression format. 183 | PrintMinified, 184 | /// Print options for enabled features. 185 | PrintFeatures, 186 | /// Print in full s-expression format. 187 | PrintFull, 188 | /// Print call graph. 189 | PrintCallGraph, 190 | /// Print a map of function indexes to names. 191 | PrintFunctionMap, 192 | /// (Alias for print-function-map). 193 | Symbolmap, 194 | /// Print out Stack IR (useful for internal debugging). 195 | PrintStackIr, 196 | /// Removes operations incompatible with js. 197 | RemoveNonJsOps, 198 | /// Removes imports and replaces them with nops. 199 | RemoveImports, 200 | /// Removes memory segments. 201 | RemoveMemory, 202 | /// Removes breaks from locations that are not needed. 203 | RemoveUnusedBrs, 204 | /// Removes unused module elements. 205 | RemoveUnusedModuleElements, 206 | /// Removes unused module elements that are not functions. 207 | RemoveUnusedNonfunctionModuleElements, 208 | /// Removes names from locations that are never branched to. 209 | RemoveUnusedNames, 210 | /// Remove unused private GC types. 211 | RemoveUnusedTypes, 212 | /// Sorts functions by name (useful for debugging). 213 | ReorderFunctionsByName, 214 | /// Sorts functions by access frequency. 215 | ReorderFunctions, 216 | /// Sorts globals by access frequency. 217 | ReorderGlobals, 218 | /// Sorts locals by access frequency. 219 | RecorderLocals, 220 | /// Re-optimize control flow using the relooper algorithm. 221 | Rereloop, 222 | /// Remove redundant local.sets. 223 | Rse, 224 | /// Write the module to binary, then read it. 225 | Roundtrip, 226 | /// Instrument loads and stores to check for invalid behavior. 227 | SafeHeap, 228 | /// Sets specified globals to specified values. 229 | SetGlobals, 230 | /// Remove params from function signature types where possible. 231 | SignaturePruning, 232 | /// Apply more specific subtypes to signature types where possible. 233 | SignatureRefining, 234 | /// Lower sign-ext operations to wasm mvp. 235 | SignextLowering, 236 | /// Miscellaneous globals-related optimizations. 237 | SimplifyGlobals, 238 | /// Miscellaneous globals-related optimizations, and optimizes where we replaced global.gets with constants. 239 | SimplifyGlobalsOptimizing, 240 | /// Miscellaneous locals-related optimizations. 241 | SimplifyLocals, 242 | /// Miscellaneous locals-related optimizations (no nesting at all; preserves flatness). 243 | SimplifyLocalsNonesting, 244 | /// Miscellaneous locals-related optimizations (no tees). 245 | SimplifyLocalsNotee, 246 | /// Miscellaneous locals-related optimizations (no structure). 247 | SimplifyLocalsNostructure, 248 | /// Miscellaneous locals-related optimizations (no tees or structure). 249 | SimplifyLocalsNoteeNostructure, 250 | /// Emit Souper IR in text form. 251 | Souperify, 252 | /// Emit Souper IR in text form (single-use nodes only). 253 | SouperifySingleUse, 254 | /// Spill pointers to the C stack (useful for Boehm-style GC). 255 | SpillPointers, 256 | /// Stub out unsupported JS operations. 257 | StubUnsupportedJs, 258 | /// Ssa-ify variables so that they have a single assignment. 259 | Ssa, 260 | /// Ssa-ify variables so that they have a single assignment, ignoring merges. 261 | SsaNomerge, 262 | /// Deprecated; same as strip-debug. 263 | Strip, 264 | /// Enforce limits on llvm's __stack_pointer global. 265 | StackCheck, 266 | /// Strip debug info (including the names section). 267 | StripDebug, 268 | /// Strip dwarf debug info. 269 | StripDwarf, 270 | /// Strip the wasm producers section. 271 | StripProducers, 272 | /// Strip EH instructions. 273 | StripEh, 274 | /// Strip the wasm target features section. 275 | StripTargetFeatuers, 276 | /// Replace trapping operations with clamping semantics. 277 | TrapModeClamp, 278 | /// Replace trapping operations with js semantics. 279 | TrapModeJs, 280 | /// Merge types to their supertypes where possible. 281 | TypeMerging, 282 | /// Create new nominal types to help other optimizations. 283 | TypeSsa, 284 | /// Removes local.tees, replacing them with sets and gets. 285 | Untee, 286 | /// Removes obviously unneeded code. 287 | Vacuum, 288 | } 289 | 290 | impl Pass { 291 | /// Returns the name of the pass. 292 | /// 293 | /// This is the same name used by Binaryen to identify the pass on the command line. 294 | pub fn name(&self) -> &'static str { 295 | use Pass::*; 296 | match self { 297 | AlignmentLowering => "alignment-lowering", 298 | Asyncify => "asyncify", 299 | AvoidReinterprets => "avoid-reinterprets", 300 | Dae => "dae", 301 | DaeOptimizing => "dae-optimizing", 302 | AbstractTypeRefining => "abstract-type-refining", 303 | CoalesceLocals => "coalesce-locals", 304 | CoalesceLocalsLearning => "coalesce-locals-learning", 305 | CodePushing => "code-pushing", 306 | CodeFolding => "code-folding", 307 | ConstHoisting => "const-hoisting", 308 | Cfp => "cfp", 309 | Dce => "dce", 310 | Dealign => "dealign", 311 | DeNan => "denan", 312 | DiscardGlobalEffects => "discard-global-effects", 313 | Directize => "directize", 314 | Dfo => "dfo", 315 | DwarfDump => "dwarfdump", 316 | DuplicateImportElimination => "duplicate-import-elimination", 317 | DuplicateFunctionElimination => "duplicate-function-elimination", 318 | EmitTargetFeatures => "emit-target-features", 319 | ExtractFunction => "extract-function", 320 | ExtractFunctionIndex => "extract-function-index", 321 | Flatten => "flatten", 322 | FpCastEmu => "fpcast-emu", 323 | FuncMetrics => "func-metrics", 324 | GenerateDyncalls => "generate-dyncalls", 325 | GenerateI64Dyncalls => "generate-i64-dyncalls", 326 | GenerateGlobalEffects => "generate-global-effects", 327 | GenerateStackIr => "generate-stack-ir", 328 | GlobalRefining => "global-refining", 329 | Gto => "gto", 330 | Gsi => "gsi", 331 | Gufa => "gufa", 332 | GufaCastAll => "gufa-cast-all", 333 | GufaOptimizing => "gufa-optimizing", 334 | TypeRefining => "type-refining", 335 | Heap2Local => "heap2local", 336 | InlineMain => "inline-main", 337 | Inlining => "inlining", 338 | InliningOptimizing => "inlining-optimizing", 339 | IntrinsicLowering => "intrinsic-lowering", 340 | Jspi => "jspi", 341 | LegalizeJsInterface => "legalize-js-interface", 342 | LegalizeJsInterfaceMinimally => "legalize-js-interface-minimally", 343 | LocalCse => "local-cse", 344 | LocalSubtyping => "local-subtyping", 345 | LogExecution => "log-execution", 346 | I64ToI32Lowering => "i64-to-i32-lowering", 347 | InstrumentLocals => "instrument-locals", 348 | InstrumentMemory => "instrument-memory", 349 | Licm => "licm", 350 | LimitSegments => "limit-segments", 351 | Memory64Lowering => "memory64-lowering", 352 | MemoryPacking => "memory-packing", 353 | MergeBlocks => "merge-blocks", 354 | MergeSimilarFunctions => "merge-similar-functions", 355 | MergeLocals => "merge-locals", 356 | Metrics => "metrics", 357 | MinifyImports => "minify-imports", 358 | MinifyImportsAndExports => "minify-imports-and-exports", 359 | MinifyImportsAndExportsAndModules => "minify-imports-and-exports-and-modules", 360 | ModAsyncifyAlwaysAndOnlyUnwind => "mod-asyncify-always-and-only-unwind", 361 | ModAsyncifyNeverUnwind => "mod-asyncify-never-unwind", 362 | Monomorphize => "monomorphize", 363 | MonomorphizeAlways => "monomorphize-always", 364 | MultiMemoryLowering => "multi-memory-lowering", 365 | MultiMemoryLoweringWithBoundsChecks => "multi-memory-lowering-with-bounds-checks", 366 | Nm => "nm", 367 | NameTypes => "name-types", 368 | OnceReduction => "once-reduction", 369 | OptimizeAddedConstants => "optimize-added-constants", 370 | OptimizeAddedConstantsPropagate => "optimize-added-constants-propagate", 371 | OptimizeCasts => "optimize-casts", 372 | OptimizeInstructions => "optimize-instructions", 373 | OptimizeStackIr => "optimize-stack-ir", 374 | PickLoadSigns => "pick-load-signs", 375 | Poppify => "poppify", 376 | PostEmscripten => "post-emscripten", 377 | OptimizeForJs => "optimize-for-js", 378 | Precompute => "precompute", 379 | PrecomputePropagate => "precompute-propagate", 380 | Print => "print", 381 | PrintMinified => "print-minified", 382 | PrintFeatures => "print-features", 383 | PrintFull => "print-full", 384 | PrintCallGraph => "print-call-graph", 385 | PrintFunctionMap => "print-function-map", 386 | Symbolmap => "symbolmap", 387 | PrintStackIr => "print-stack-ir", 388 | RemoveNonJsOps => "remove-non-js-ops", 389 | RemoveImports => "remove-imports", 390 | RemoveMemory => "remove-memory", 391 | RemoveUnusedBrs => "remove-unused-brs", 392 | RemoveUnusedModuleElements => "remove-unused-module-elements", 393 | RemoveUnusedNonfunctionModuleElements => "remove-unused-nonfunction-module-elements", 394 | RemoveUnusedNames => "remove-unused-names", 395 | RemoveUnusedTypes => "remove-unused-types", 396 | ReorderFunctionsByName => "reorder-functions-by-name", 397 | ReorderFunctions => "reorder-functions", 398 | ReorderGlobals => "reorder-globals", 399 | RecorderLocals => "reorder-locals", 400 | Rereloop => "rereloop", 401 | Rse => "rse", 402 | Roundtrip => "roundtrip", 403 | SafeHeap => "safe-heap", 404 | SetGlobals => "set-globals", 405 | SignaturePruning => "signature-pruning", 406 | SignatureRefining => "signature-refining", 407 | SignextLowering => "signext-lowering", 408 | SimplifyGlobals => "simplify-globals", 409 | SimplifyGlobalsOptimizing => "simplify-globals-optimizing", 410 | SimplifyLocals => "simplify-locals", 411 | SimplifyLocalsNonesting => "simplify-locals-nonesting", 412 | SimplifyLocalsNotee => "simplify-locals-notee", 413 | SimplifyLocalsNostructure => "simplify-locals-nostructure", 414 | SimplifyLocalsNoteeNostructure => "simplify-locals-notee-nostructure", 415 | Souperify => "souperify", 416 | SouperifySingleUse => "souperify-single-use", 417 | SpillPointers => "spill-pointers", 418 | StubUnsupportedJs => "stub-unsupported-js", 419 | Ssa => "ssa", 420 | SsaNomerge => "ssa-nomerge", 421 | Strip => "strip", 422 | StackCheck => "stack-check", 423 | StripDebug => "strip-debug", 424 | StripDwarf => "strip-dwarf", 425 | StripProducers => "strip-producers", 426 | StripEh => "strip-eh", 427 | StripTargetFeatuers => "strip-target-features", 428 | TrapModeClamp => "trap-mode-clamp", 429 | TrapModeJs => "trap-mode-js", 430 | TypeMerging => "type-merging", 431 | TypeSsa => "type-ssa", 432 | Untee => "untee", 433 | Vacuum => "vacuum", 434 | } 435 | } 436 | 437 | /// Get Binaryen's description of the pass. 438 | pub fn description(&self) -> String { 439 | // NB: This will abort if the name is invalid 440 | pass_registry::get_pass_description(self.name()) 441 | } 442 | } 443 | --------------------------------------------------------------------------------