diff --git a/Cargo.lock b/Cargo.lock index d9a79fa..ab1785d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,6 +75,27 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +[[package]] +name = "binaryen" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783bea139d75b6a565b13fab54d12ec4d58724a9458598ad7283d578f4f8777a" +dependencies = [ + "binaryen-sys", +] + +[[package]] +name = "binaryen-sys" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86e9636d01b92f2df45dce29c35a9e3724687c1055bb4472fb4b829cc4a5a561" +dependencies = [ + "cc", + "cmake", + "heck 0.3.3", + "regex", +] + [[package]] name = "binread" version = "2.2.0" @@ -184,6 +205,12 @@ dependencies = [ "syn", ] +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "cfg-if" version = "1.0.0" @@ -229,6 +256,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "cmake" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +dependencies = [ + "cc", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -359,6 +395,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" + [[package]] name = "hashbrown" version = "0.12.1" @@ -395,6 +437,12 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "humansize" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" + [[package]] name = "ic-types" version = "0.3.0" @@ -416,8 +464,11 @@ version = "0.1.2" dependencies = [ "anyhow", "assert_cmd", + "binaryen", "candid", "clap", + "humansize", + "wabt", "walrus", ] @@ -446,6 +497,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" + [[package]] name = "lalrpop" version = "0.19.8" @@ -817,6 +874,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" +[[package]] +name = "ryu" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + [[package]] name = "scopeguard" version = "1.1.0" @@ -852,6 +915,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1019,6 +1093,29 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wabt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bef93d5e6c81a293bccf107cf43aa47239382f455ba14869d36695d8963b9c" +dependencies = [ + "serde", + "serde_derive", + "serde_json", + "wabt-sys", +] + +[[package]] +name = "wabt-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a4e043159f63e16986e713e9b5e1c06043df4848565bf672e27c523864c7791" +dependencies = [ + "cc", + "cmake", + "glob", +] + [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 347f0bb..8b863a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,9 +25,14 @@ candid = "0.7" anyhow = { version = "1.0.34", optional = true } clap = { version = "3.0.14", features = ["derive", "cargo"], optional = true } +binaryen = { version = "0.12.0", optional = true } +humansize = { version = "1.1.0", optional = true } +wabt = { version = "0.10.0", optional = true } + [features] default = ["exe"] exe = ["anyhow", "clap"] +optimize = ["binaryen", "wabt", "humansize"] [dev-dependencies] assert_cmd = "1.0.0" diff --git a/src/lib.rs b/src/lib.rs index d48d258..02c1762 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,5 +2,7 @@ pub mod info; pub mod instrumentation; pub mod limit_resource; pub mod metadata; +#[cfg(feature = "optimize")] +pub mod optimize; pub mod shrink; pub mod utils; diff --git a/src/optimize.rs b/src/optimize.rs new file mode 100644 index 0000000..5be6b36 --- /dev/null +++ b/src/optimize.rs @@ -0,0 +1,64 @@ +//! A legacy implementation of WASM module optimization for IC +//! +//! https://crates.io/crates/ic-cdk-optimizer +//! +//! Mainly to be used as a lib in dfx. +//! Will be unified with shrink + +use binaryen::{CodegenConfig, Module}; +use humansize::{file_size_opts, FileSize}; +use wabt::{wasm2wat, wat2wasm}; + +pub type PassResult = Result, Box>; + +pub fn optimize(content: &[u8]) -> PassResult { + let original_wasm_size = content.len(); + eprintln!( + "Original: {:>8}", + original_wasm_size + .file_size(file_size_opts::BINARY) + .unwrap() + ); + + let mut wasm_back = content; + + // strip sections + eprintln!("Stripping Unused Data Segments..."); + let wat = wasm2wat(&wasm_back)?; + let wasm_new = wat2wasm(wat)?; + wasm_back = compare_wasm(wasm_back, &wasm_new); + + // binaryen + eprintln!("Executing a binaryen optimization..."); + let mut module = Module::read(wasm_back) + .map_err(|_| String::from("Could not load module for binaryen..."))?; + module.optimize(&CodegenConfig { + debug_info: false, + optimization_level: 2, + shrink_level: 2, + }); + let wasm_new = module.write(); + wasm_back = compare_wasm(wasm_back, &wasm_new); + + eprintln!( + "\nFinal Size: {} ({:3.1}% smaller)", + wasm_back.len().file_size(file_size_opts::BINARY).unwrap(), + (1.0 - ((wasm_back.len() as f64) / (original_wasm_size as f64))) * 100.0 + ); + + Ok(wasm_back.to_vec()) +} + +fn compare_wasm<'a>(wasm_back: &'a [u8], wasm_new: &'a [u8]) -> &'a [u8] { + if wasm_new.len() < wasm_back.len() { + eprintln!( + " Size: {:>8} ({:3.1}% smaller)", + wasm_back.len().file_size(file_size_opts::BINARY).unwrap(), + (1.0 - ((wasm_new.len() as f64) / (wasm_back.len() as f64))) * 100.0 + ); + wasm_new + } else { + eprintln!("Pass did not result in smaller WASM... Skipping."); + wasm_back + } +}