From f0ef00292ee645814529491186c6a3ab0660dc26 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 25 Apr 2017 17:06:41 +0300 Subject: [PATCH] symbols optimizer initial --- gas/Cargo.lock | 2 +- gas/Cargo.toml | 2 +- opt/.gitignore | 1 + opt/Cargo.lock | 23 ++++++++++ opt/Cargo.toml | 7 +++ opt/src/main.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ runner/build.sh | 2 +- 7 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 opt/.gitignore create mode 100644 opt/Cargo.lock create mode 100644 opt/Cargo.toml create mode 100644 opt/src/main.rs diff --git a/gas/Cargo.lock b/gas/Cargo.lock index dc3dffb..30d6c9c 100644 --- a/gas/Cargo.lock +++ b/gas/Cargo.lock @@ -1,5 +1,5 @@ [root] -name = "gas" +name = "wasm-gas" version = "0.1.0" dependencies = [ "parity-wasm 0.3.0 (git+https://github.com/nikvolf/parity-wasm)", diff --git a/gas/Cargo.toml b/gas/Cargo.toml index 9f0e103..9a6e489 100644 --- a/gas/Cargo.toml +++ b/gas/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "gas" +name = "wasm-gas" version = "0.1.0" authors = ["NikVolf "] diff --git a/opt/.gitignore b/opt/.gitignore new file mode 100644 index 0000000..1de5659 --- /dev/null +++ b/opt/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/opt/Cargo.lock b/opt/Cargo.lock new file mode 100644 index 0000000..a14c57b --- /dev/null +++ b/opt/Cargo.lock @@ -0,0 +1,23 @@ +[root] +name = "wasm-opt" +version = "0.1.0" +dependencies = [ + "parity-wasm 0.3.1 (git+https://github.com/nikvolf/parity-wasm)", +] + +[[package]] +name = "byteorder" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "parity-wasm" +version = "0.3.1" +source = "git+https://github.com/nikvolf/parity-wasm#825169d34ea6d9c3909fe762e36ccc3ba5ae98cf" +dependencies = [ + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" +"checksum parity-wasm 0.3.1 (git+https://github.com/nikvolf/parity-wasm)" = "" diff --git a/opt/Cargo.toml b/opt/Cargo.toml new file mode 100644 index 0000000..fc49439 --- /dev/null +++ b/opt/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wasm-opt" +version = "0.1.0" +authors = ["NikVolf "] + +[dependencies] +parity-wasm = { git="https://github.com/nikvolf/parity-wasm" } \ No newline at end of file diff --git a/opt/src/main.rs b/opt/src/main.rs new file mode 100644 index 0000000..c66cd9b --- /dev/null +++ b/opt/src/main.rs @@ -0,0 +1,119 @@ +extern crate parity_wasm; + +use std::env; +use std::collections::HashSet; +use parity_wasm::elements; + +#[derive(PartialEq, Eq, Hash, Copy, Clone)] +enum Symbol { + Import(usize), + Global(usize), + Function(usize), + Export(usize), +} + +fn resolve_function(module: &elements::Module, index: u32) -> Symbol { + let imports_len = module + .import_section() + .expect("Functions section to exist") + .entries() + .iter() + .map(|e| match e.external() { + &elements::External::Function(_) => 1, + _ => 0, + }) + .sum(); + + if index < imports_len { + Symbol::Import(index as usize) + } else { + Symbol::Function(index as usize - imports_len as usize) + } +} + +fn resolve_global(module: &elements::Module, index: u32) -> Symbol { + let imports_len = module + .import_section() + .expect("Functions section to exist") + .entries() + .iter() + .map(|e| match e.external() { + &elements::External::Global(_) => 1, + _ => 0, + }) + .sum(); + + if index < imports_len { + Symbol::Import(index as usize) + } else { + Symbol::Global(index as usize - imports_len as usize) + } +} + +fn expand_symbols(module: &elements::Module, set: &mut HashSet) { + use Symbol::*; + + // symbols that were already processed + let mut stop: HashSet = HashSet::new(); + let mut fringe = set.iter().cloned().collect::>(); + loop { + let next = match fringe.pop() { + Some(s) => s, + _ => { break; } + }; + + if stop.contains(&next) { + continue; + } + + match next { + Export(idx) => { + let entry = &module.export_section().expect("Export section to exist").entries()[idx]; + match entry.internal() { + &elements::Internal::Function(func_idx) => { fringe.push(resolve_function(module, func_idx)); }, + &elements::Internal::Global(global_idx) => { fringe.push(resolve_global(module, global_idx)); }, + _ => {} + } + }, + _ => {} + } + + stop.insert(next); + } +} + +fn main() { + + let args = env::args().collect::>(); + if args.len() != 3 { + println!("Usage: {} input_file.wasm output_file.wasm", args[0]); + return; + } + + // Loading module + let mut module = parity_wasm::deserialize_file(&args[1]).unwrap(); + + // WebAssembly exports optimizer + // Motivation: emscripten compiler backend compiles in many unused exports + // which in turn compile in unused imports and leaves unused functions + + // List of exports that are actually used in the managed code + let used_exports = vec!["_call", "_malloc", "_free"]; + + // Algo starts from the top, listing all items that should stay + let mut stay = HashSet::new(); + for (index, entry) in module.export_section().expect("Export section to exist").entries().iter().enumerate() { + if used_exports.iter().find(|e| **e == entry.field()).is_some() { + stay.insert(Symbol::Export(index)); + } + } + + // Call function which will traverse the list recursively, filling stay with all symbols + // that are already used by those which already there + expand_symbols(&mut module, &mut stay); + + // Finally, delete all items one by one, updating reference indices in the process + // (todo: initial naive impementation can be optimized to avoid multiple passes) + + parity_wasm::serialize_to_file(&args[2], module).unwrap(); +} diff --git a/runner/build.sh b/runner/build.sh index f27428e..7ff02e5 100755 --- a/runner/build.sh +++ b/runner/build.sh @@ -19,6 +19,6 @@ then echo "No gas utility, compile it in /gas folder with" echo "cargo build --release" else - ./../gas/target/release/gas ./out/contract.wasm ./out/contract.wasm + cargo run --manifest-path=./../gas/Cargo.toml --release -- ./out/contract.wasm ./out/contract.wasm # echo "Removed gasification" fi