From c69d8327c805c3f904feb3da425eafbd73e68d9f Mon Sep 17 00:00:00 2001 From: Nikolay Volf Date: Mon, 11 Sep 2017 18:37:26 +0300 Subject: [PATCH 01/11] Update Cargo.toml --- build/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/Cargo.toml b/build/Cargo.toml index 9ac7979..69643a8 100644 --- a/build/Cargo.toml +++ b/build/Cargo.toml @@ -7,7 +7,7 @@ authors = ["NikVolf "] glob = "0.2" wasm-utils = { path = "../" } clap = "2.24" -parity-wasm = "0.12" +parity-wasm = "0.14" [[bin]] -name = "wasm-build" \ No newline at end of file +name = "wasm-build" From 203cc4fea8427ad2457f95e6fd99645fe0d893ec Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 15 Sep 2017 22:00:35 +0300 Subject: [PATCH 02/11] rename wasm-opt binary to wasm-prune --- Cargo.toml | 4 ++-- opt/.gitignore | 2 -- opt/Cargo.toml | 9 --------- opt/src/main.rs | 44 -------------------------------------------- 4 files changed, 2 insertions(+), 57 deletions(-) delete mode 100644 opt/.gitignore delete mode 100644 opt/Cargo.toml delete mode 100644 opt/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 2b37e54..b4944ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ glob = "0.2" [lib] [[bin]] -name = "wasm-opt" -path = "opt/src/main.rs" +name = "wasm-prune" +path = "prune/src/main.rs" [[bin]] name = "wasm-ext" diff --git a/opt/.gitignore b/opt/.gitignore deleted file mode 100644 index f2f9e58..0000000 --- a/opt/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock \ No newline at end of file diff --git a/opt/Cargo.toml b/opt/Cargo.toml deleted file mode 100644 index 5270544..0000000 --- a/opt/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "wasm-opt" -version = "0.1.0" -authors = ["NikVolf "] - -[dependencies] -parity-wasm = "0.12" -wasm-utils = { path = "../" } -clap = "2.24" diff --git a/opt/src/main.rs b/opt/src/main.rs deleted file mode 100644 index 6175ad9..0000000 --- a/opt/src/main.rs +++ /dev/null @@ -1,44 +0,0 @@ -extern crate parity_wasm; -extern crate wasm_utils; -extern crate clap; - -use clap::{App, Arg}; - -fn main() { - wasm_utils::init_log(); - - let matches = App::new("wasm-opt") - .arg(Arg::with_name("input") - .index(1) - .required(true) - .help("Input WASM file")) - .arg(Arg::with_name("output") - .index(2) - .required(true) - .help("Output WASM file")) - .arg(Arg::with_name("exports") - .long("exports") - .short("e") - .takes_value(true) - .value_name("functions") - .help("Comma-separated list of exported functions to keep. Default: _call")) - .get_matches(); - - let exports = matches - .value_of("exports") - .unwrap_or("_call") - .split(',') - .collect(); - - let input = matches.value_of("input").expect("is required; qed"); - let output = matches.value_of("output").expect("is required; qed"); - - let mut module = parity_wasm::deserialize_file(&input).unwrap(); - - // Invoke optimizer - // Contract is supposed to have only these functions as public api - // All other symbols not usable by this list is optimized away - wasm_utils::optimize(&mut module, exports).expect("Optimizer to finish without errors"); - - parity_wasm::serialize_to_file(&output, module).unwrap(); -} From 5095880843b70220036b88772622224a66117d38 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 15 Sep 2017 22:01:12 +0300 Subject: [PATCH 03/11] add prune actually --- prune/.gitignore | 2 ++ prune/Cargo.toml | 9 +++++++++ prune/src/main.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 prune/.gitignore create mode 100644 prune/Cargo.toml create mode 100644 prune/src/main.rs diff --git a/prune/.gitignore b/prune/.gitignore new file mode 100644 index 0000000..f2f9e58 --- /dev/null +++ b/prune/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock \ No newline at end of file diff --git a/prune/Cargo.toml b/prune/Cargo.toml new file mode 100644 index 0000000..5270544 --- /dev/null +++ b/prune/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wasm-opt" +version = "0.1.0" +authors = ["NikVolf "] + +[dependencies] +parity-wasm = "0.12" +wasm-utils = { path = "../" } +clap = "2.24" diff --git a/prune/src/main.rs b/prune/src/main.rs new file mode 100644 index 0000000..6175ad9 --- /dev/null +++ b/prune/src/main.rs @@ -0,0 +1,44 @@ +extern crate parity_wasm; +extern crate wasm_utils; +extern crate clap; + +use clap::{App, Arg}; + +fn main() { + wasm_utils::init_log(); + + let matches = App::new("wasm-opt") + .arg(Arg::with_name("input") + .index(1) + .required(true) + .help("Input WASM file")) + .arg(Arg::with_name("output") + .index(2) + .required(true) + .help("Output WASM file")) + .arg(Arg::with_name("exports") + .long("exports") + .short("e") + .takes_value(true) + .value_name("functions") + .help("Comma-separated list of exported functions to keep. Default: _call")) + .get_matches(); + + let exports = matches + .value_of("exports") + .unwrap_or("_call") + .split(',') + .collect(); + + let input = matches.value_of("input").expect("is required; qed"); + let output = matches.value_of("output").expect("is required; qed"); + + let mut module = parity_wasm::deserialize_file(&input).unwrap(); + + // Invoke optimizer + // Contract is supposed to have only these functions as public api + // All other symbols not usable by this list is optimized away + wasm_utils::optimize(&mut module, exports).expect("Optimizer to finish without errors"); + + parity_wasm::serialize_to_file(&output, module).unwrap(); +} From 087399f6b59c9db4a7d1900f5ec85b8e9a2af473 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Fri, 15 Sep 2017 22:03:43 +0300 Subject: [PATCH 04/11] update also readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 636d48a..5d6c575 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ Easiest way to use is to install via `cargo install`: cargo install --git https://github.com/paritytech/wasm-utils wasm-build ``` -## Symbols optimizer (wasm-opt) +## Symbols pruning (wasm-prune) ``` -cargo run --release --bin wasm-opt -- +cargo run --release --bin wasm-prune -- ``` This will optimize WASM symbols tree to leave only those elements that are used by contract `_call` function entry. From 31673aa31d57cecee09ac370fa646e5794543014 Mon Sep 17 00:00:00 2001 From: fro Date: Tue, 19 Sep 2017 23:58:20 +0300 Subject: [PATCH 05/11] non-determinism checker initial impl wip --- Cargo.toml | 2 +- non_determinism_checker/.gitignore | 2 + non_determinism_checker/Cargo.toml | 9 +++ non_determinism_checker/src/main.rs | 26 +++++++ src/lib.rs | 4 +- src/non_determinism_checker.rs | 101 ++++++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 non_determinism_checker/.gitignore create mode 100644 non_determinism_checker/Cargo.toml create mode 100644 non_determinism_checker/src/main.rs create mode 100644 src/non_determinism_checker.rs diff --git a/Cargo.toml b/Cargo.toml index b4944ee..544e857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,4 +31,4 @@ path = "pack/src/main.rs" [[bin]] name = "wasm-build" -path = "build/src/main.rs" \ No newline at end of file +path = "build/src/main.rs" diff --git a/non_determinism_checker/.gitignore b/non_determinism_checker/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/non_determinism_checker/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/non_determinism_checker/Cargo.toml b/non_determinism_checker/Cargo.toml new file mode 100644 index 0000000..57ed39b --- /dev/null +++ b/non_determinism_checker/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wasm-pack" +version = "0.1.0" +authors = ["NikVolf "] + +[dependencies] +parity-wasm = "0.14" +wasm-utils = { path = "../" } +clap = "2.24" diff --git a/non_determinism_checker/src/main.rs b/non_determinism_checker/src/main.rs new file mode 100644 index 0000000..eee51bd --- /dev/null +++ b/non_determinism_checker/src/main.rs @@ -0,0 +1,26 @@ +extern crate parity_wasm; +extern crate wasm_utils; + +use std::env; + + +fn main() { + + wasm_utils::init_log(); + + let args = env::args().collect::>(); + if args.len() != 2 { + println!("Usage: {} input_file.wasm", args[0]); + return; + } + + // Loading module + let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); + + if wasm_utils::have_non_determinism(module) { + println!("Yes"); + } else { + println!("No"); + } + +} diff --git a/src/lib.rs b/src/lib.rs index ea075b2..af09f6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,9 +11,11 @@ mod symbols; mod logger; mod ext; mod pack; +mod non_determinism_checker; pub use optimizer::{optimize, Error as OptimizerError}; pub use gas::inject_gas_counter; pub use logger::init_log; pub use ext::externalize; -pub use pack::pack_instance; \ No newline at end of file +pub use pack::pack_instance; +pub use non_determinism_checker::have_non_determinism; diff --git a/src/non_determinism_checker.rs b/src/non_determinism_checker.rs new file mode 100644 index 0000000..e5bd10c --- /dev/null +++ b/src/non_determinism_checker.rs @@ -0,0 +1,101 @@ +use parity_wasm::{elements}; +use parity_wasm::elements::Section; +use parity_wasm::elements::Opcode::*; + +// TODO: +// fn check_code (FuncBodybody) -> bool { + +// } + + +pub fn have_non_determinism(module: elements::Module) -> bool { + + for section in module.sections() { + match *section { + Section::Code(ref cs) => { + for body in cs.bodies() { + for opcode in body.code().elements() { + match *opcode { + F32Abs | + F32Neg | + F32Ceil | + F32Floor | + F32Trunc | + F32Nearest | + F32Sqrt | + F32Add | + F32Sub | + F32Mul | + F32Div | + F32Min | + F32Max | + F32Copysign | + F64Abs | + F64Neg | + F64Ceil | + F64Floor | + F64Trunc | + F64Nearest | + F64Sqrt | + F64Add | + F64Sub | + F64Mul | + F64Div | + F64Min | + F64Max | + F64Copysign | + I32TruncSF32 | + I32TruncUF32 | + I32TruncSF64 | + I32TruncUF64 | + I64TruncSF32 | + I64TruncUF32 | + I64TruncSF64 | + I64TruncUF64 | + F32ConvertSI32 | + F32ConvertUI32 | + F32ConvertSI64 | + F32ConvertUI64 | + F32DemoteF64 | + F64ConvertSI32 | + F64ConvertUI32 | + F64ConvertSI64 | + F64ConvertUI64 | + F64PromoteF32 | + I32ReinterpretF32 | + I64ReinterpretF64 | + F32ReinterpretI32 | + F64ReinterpretI64 | + F32Eq | + F32Ne | + F32Lt | + F32Gt | + F32Le | + F32Ge | + F64Eq | + F64Ne | + F64Lt | + F64Gt | + F64Le | + F64Ge + => { + return true + }, + _ => continue + } + } + }; + return false; + }, + _ => continue + } + } + false +} + +#[cfg(test)] +mod tests { + use parity_wasm::{builder, elements}; + use super::*; + +} From 768d4633a137ddde37cb6f849c37d1e7dc0b2ce5 Mon Sep 17 00:00:00 2001 From: fro Date: Wed, 20 Sep 2017 12:52:33 +0300 Subject: [PATCH 06/11] check_opcodes func implemented --- non_determinism_checker/Cargo.toml | 2 +- src/non_determinism_checker.rs | 155 +++++++++++++++-------------- 2 files changed, 79 insertions(+), 78 deletions(-) diff --git a/non_determinism_checker/Cargo.toml b/non_determinism_checker/Cargo.toml index 57ed39b..20a8b9f 100644 --- a/non_determinism_checker/Cargo.toml +++ b/non_determinism_checker/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wasm-pack" +name = "non_determinism_checker" version = "0.1.0" authors = ["NikVolf "] diff --git a/src/non_determinism_checker.rs b/src/non_determinism_checker.rs index e5bd10c..13777e4 100644 --- a/src/non_determinism_checker.rs +++ b/src/non_determinism_checker.rs @@ -1,11 +1,78 @@ use parity_wasm::{elements}; -use parity_wasm::elements::Section; +use parity_wasm::elements::{ Section, Opcodes }; use parity_wasm::elements::Opcode::*; -// TODO: -// fn check_code (FuncBodybody) -> bool { - -// } +fn check_opcodes (opcodes: &Opcodes) -> bool { + for opcode in opcodes.elements() { + match *opcode { + F32Abs | + F32Neg | + F32Ceil | + F32Floor | + F32Trunc | + F32Nearest | + F32Sqrt | + F32Add | + F32Sub | + F32Mul | + F32Div | + F32Min | + F32Max | + F32Copysign | + F64Abs | + F64Neg | + F64Ceil | + F64Floor | + F64Trunc | + F64Nearest | + F64Sqrt | + F64Add | + F64Sub | + F64Mul | + F64Div | + F64Min | + F64Max | + F64Copysign | + I32TruncSF32 | + I32TruncUF32 | + I32TruncSF64 | + I32TruncUF64 | + I64TruncSF32 | + I64TruncUF32 | + I64TruncSF64 | + I64TruncUF64 | + F32ConvertSI32 | + F32ConvertUI32 | + F32ConvertSI64 | + F32ConvertUI64 | + F32DemoteF64 | + F64ConvertSI32 | + F64ConvertUI32 | + F64ConvertSI64 | + F64ConvertUI64 | + F64PromoteF32 | + I32ReinterpretF32 | + I64ReinterpretF64 | + F32ReinterpretI32 | + F64ReinterpretI64 | + F32Eq | + F32Ne | + F32Lt | + F32Gt | + F32Le | + F32Ge | + F64Eq | + F64Ne | + F64Lt | + F64Gt | + F64Le | + F64Ge + => return true, + _ => continue + } + } + false +} pub fn have_non_determinism(module: elements::Module) -> bool { @@ -14,82 +81,16 @@ pub fn have_non_determinism(module: elements::Module) -> bool { match *section { Section::Code(ref cs) => { for body in cs.bodies() { - for opcode in body.code().elements() { - match *opcode { - F32Abs | - F32Neg | - F32Ceil | - F32Floor | - F32Trunc | - F32Nearest | - F32Sqrt | - F32Add | - F32Sub | - F32Mul | - F32Div | - F32Min | - F32Max | - F32Copysign | - F64Abs | - F64Neg | - F64Ceil | - F64Floor | - F64Trunc | - F64Nearest | - F64Sqrt | - F64Add | - F64Sub | - F64Mul | - F64Div | - F64Min | - F64Max | - F64Copysign | - I32TruncSF32 | - I32TruncUF32 | - I32TruncSF64 | - I32TruncUF64 | - I64TruncSF32 | - I64TruncUF32 | - I64TruncSF64 | - I64TruncUF64 | - F32ConvertSI32 | - F32ConvertUI32 | - F32ConvertSI64 | - F32ConvertUI64 | - F32DemoteF64 | - F64ConvertSI32 | - F64ConvertUI32 | - F64ConvertSI64 | - F64ConvertUI64 | - F64PromoteF32 | - I32ReinterpretF32 | - I64ReinterpretF64 | - F32ReinterpretI32 | - F64ReinterpretI64 | - F32Eq | - F32Ne | - F32Lt | - F32Gt | - F32Le | - F32Ge | - F64Eq | - F64Ne | - F64Lt | - F64Gt | - F64Le | - F64Ge - => { - return true - }, - _ => continue + if check_opcodes(body.code()) { + return true; + } else { + continue } } - }; - return false; - }, + }, _ => continue + } } - } false } From 6aa896e9fe7b9114c4d2a7bbc0ac6455316d6da5 Mon Sep 17 00:00:00 2001 From: fro Date: Wed, 20 Sep 2017 19:16:00 +0300 Subject: [PATCH 07/11] rename non_determinism_checker -> indeterminism_check tests added --- Cargo.toml | 4 + .../.gitignore | 0 .../Cargo.toml | 2 +- .../src/main.rs | 6 +- src/indeterminism_check.rs | 152 ++++++++++++++++++ src/lib.rs | 4 +- src/non_determinism_checker.rs | 102 ------------ 7 files changed, 162 insertions(+), 108 deletions(-) rename {non_determinism_checker => indeterminism_check}/.gitignore (100%) rename {non_determinism_checker => indeterminism_check}/Cargo.toml (82%) rename {non_determinism_checker => indeterminism_check}/src/main.rs (75%) create mode 100644 src/indeterminism_check.rs delete mode 100644 src/non_determinism_checker.rs diff --git a/Cargo.toml b/Cargo.toml index 544e857..95cc25f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,7 @@ path = "pack/src/main.rs" [[bin]] name = "wasm-build" path = "build/src/main.rs" + +[[bin]] +name = "indeterminism-check" +path = "indeterminism_check/src/main.rs" diff --git a/non_determinism_checker/.gitignore b/indeterminism_check/.gitignore similarity index 100% rename from non_determinism_checker/.gitignore rename to indeterminism_check/.gitignore diff --git a/non_determinism_checker/Cargo.toml b/indeterminism_check/Cargo.toml similarity index 82% rename from non_determinism_checker/Cargo.toml rename to indeterminism_check/Cargo.toml index 20a8b9f..b85adfd 100644 --- a/non_determinism_checker/Cargo.toml +++ b/indeterminism_check/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "non_determinism_checker" +name = "indeterminism_check" version = "0.1.0" authors = ["NikVolf "] diff --git a/non_determinism_checker/src/main.rs b/indeterminism_check/src/main.rs similarity index 75% rename from non_determinism_checker/src/main.rs rename to indeterminism_check/src/main.rs index eee51bd..44eb125 100644 --- a/non_determinism_checker/src/main.rs +++ b/indeterminism_check/src/main.rs @@ -17,10 +17,10 @@ fn main() { // Loading module let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - if wasm_utils::have_non_determinism(module) { - println!("Yes"); + if wasm_utils::have_indeterminism(module) { + println!("Non-determinism found"); } else { - println!("No"); + println!("Non-determinism not found"); } } diff --git a/src/indeterminism_check.rs b/src/indeterminism_check.rs new file mode 100644 index 0000000..e041edf --- /dev/null +++ b/src/indeterminism_check.rs @@ -0,0 +1,152 @@ +use parity_wasm::{elements}; +use parity_wasm::elements::{ Section, Opcode }; +use parity_wasm::elements::Opcode::*; + +fn check_opcodes (opcodes: &[Opcode]) -> bool { + for opcode in opcodes { + match *opcode { + F32Abs | + F32Neg | + F32Ceil | + F32Floor | + F32Trunc | + F32Nearest | + F32Sqrt | + F32Add | + F32Sub | + F32Mul | + F32Div | + F32Min | + F32Max | + F32Copysign | + F64Abs | + F64Neg | + F64Ceil | + F64Floor | + F64Trunc | + F64Nearest | + F64Sqrt | + F64Add | + F64Sub | + F64Mul | + F64Div | + F64Min | + F64Max | + F64Copysign | + I32TruncSF32 | + I32TruncUF32 | + I32TruncSF64 | + I32TruncUF64 | + I64TruncSF32 | + I64TruncUF32 | + I64TruncSF64 | + I64TruncUF64 | + F32ConvertSI32 | + F32ConvertUI32 | + F32ConvertSI64 | + F32ConvertUI64 | + F32DemoteF64 | + F64ConvertSI32 | + F64ConvertUI32 | + F64ConvertSI64 | + F64ConvertUI64 | + F64PromoteF32 | + I32ReinterpretF32 | + I64ReinterpretF64 | + F32ReinterpretI32 | + F64ReinterpretI64 | + F32Eq | + F32Ne | + F32Lt | + F32Gt | + F32Le | + F32Ge | + F64Eq | + F64Ne | + F64Lt | + F64Gt | + F64Le | + F64Ge + => return true, + _ => continue + } + } + false +} + + + + +pub fn have_indeterminism(module: elements::Module) -> bool { + for section in module.sections() { + match *section { + Section::Code(ref cs) => { + for body in cs.bodies() { + if check_opcodes(body.code().elements()) { + return true; + } + } + }, + Section::Global(ref global) => { + for entry in global.entries() { + if check_opcodes(entry.init_expr().code()) { + return true; + } + } + }, + Section::Element(ref element) => { + for entry in element.entries() { + if check_opcodes(entry.offset().code()) { + return true; + } + } + } + _ => continue + } + } + false +} + +#[cfg(test)] +mod tests { + use parity_wasm::{builder, elements}; + use super::*; + + #[test] + fn indeterminism_found() { + let module = builder::module() + .function().signature().return_type().f32().build() + .body() + .with_opcodes(elements::Opcodes::new( + vec![ + elements::Opcode::F32Const(1), // unrelated to this test matter + elements::Opcode::F32Const(1), // unrelated to this test matter + elements::Opcode::F32Add, + elements::Opcode::End + ] + )) + .build() + .build() + .build(); + assert_eq!(true, have_indeterminism(module)); + } + + #[test] + fn indeterminism_not() { + let module = builder::module() + .function().signature().return_type().f32().build() + .body() + .with_opcodes(elements::Opcodes::new( + vec![ + elements::Opcode::I32Const(1), + elements::Opcode::I32Const(1), + elements::Opcode::I32Add, + elements::Opcode::End + ] + )) + .build() + .build() + .build(); + assert_eq!(false, have_indeterminism(module)); + } +} diff --git a/src/lib.rs b/src/lib.rs index af09f6c..57d8aaa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,11 +11,11 @@ mod symbols; mod logger; mod ext; mod pack; -mod non_determinism_checker; +mod indeterminism_check; pub use optimizer::{optimize, Error as OptimizerError}; pub use gas::inject_gas_counter; pub use logger::init_log; pub use ext::externalize; pub use pack::pack_instance; -pub use non_determinism_checker::have_non_determinism; +pub use indeterminism_check::have_indeterminism; diff --git a/src/non_determinism_checker.rs b/src/non_determinism_checker.rs deleted file mode 100644 index 13777e4..0000000 --- a/src/non_determinism_checker.rs +++ /dev/null @@ -1,102 +0,0 @@ -use parity_wasm::{elements}; -use parity_wasm::elements::{ Section, Opcodes }; -use parity_wasm::elements::Opcode::*; - -fn check_opcodes (opcodes: &Opcodes) -> bool { - for opcode in opcodes.elements() { - match *opcode { - F32Abs | - F32Neg | - F32Ceil | - F32Floor | - F32Trunc | - F32Nearest | - F32Sqrt | - F32Add | - F32Sub | - F32Mul | - F32Div | - F32Min | - F32Max | - F32Copysign | - F64Abs | - F64Neg | - F64Ceil | - F64Floor | - F64Trunc | - F64Nearest | - F64Sqrt | - F64Add | - F64Sub | - F64Mul | - F64Div | - F64Min | - F64Max | - F64Copysign | - I32TruncSF32 | - I32TruncUF32 | - I32TruncSF64 | - I32TruncUF64 | - I64TruncSF32 | - I64TruncUF32 | - I64TruncSF64 | - I64TruncUF64 | - F32ConvertSI32 | - F32ConvertUI32 | - F32ConvertSI64 | - F32ConvertUI64 | - F32DemoteF64 | - F64ConvertSI32 | - F64ConvertUI32 | - F64ConvertSI64 | - F64ConvertUI64 | - F64PromoteF32 | - I32ReinterpretF32 | - I64ReinterpretF64 | - F32ReinterpretI32 | - F64ReinterpretI64 | - F32Eq | - F32Ne | - F32Lt | - F32Gt | - F32Le | - F32Ge | - F64Eq | - F64Ne | - F64Lt | - F64Gt | - F64Le | - F64Ge - => return true, - _ => continue - } - } - false -} - - -pub fn have_non_determinism(module: elements::Module) -> bool { - - for section in module.sections() { - match *section { - Section::Code(ref cs) => { - for body in cs.bodies() { - if check_opcodes(body.code()) { - return true; - } else { - continue - } - } - }, - _ => continue - } - } - false -} - -#[cfg(test)] -mod tests { - use parity_wasm::{builder, elements}; - use super::*; - -} From da265f3671b285b7e52d162fbdf54965ad9c448b Mon Sep 17 00:00:00 2001 From: fro Date: Thu, 21 Sep 2017 12:44:08 +0300 Subject: [PATCH 08/11] remame indeterminism --- Cargo.toml | 4 +-- .../.gitignore | 0 .../Cargo.toml | 2 +- .../src/main.rs | 6 ++--- src/lib.rs | 4 +-- ...inism_check.rs => nondeterminism_check.rs} | 26 +++++++++---------- 6 files changed, 21 insertions(+), 21 deletions(-) rename {indeterminism_check => nondeterminism_check}/.gitignore (100%) rename {indeterminism_check => nondeterminism_check}/Cargo.toml (83%) rename {indeterminism_check => nondeterminism_check}/src/main.rs (74%) rename src/{indeterminism_check.rs => nondeterminism_check.rs} (81%) diff --git a/Cargo.toml b/Cargo.toml index 95cc25f..f0b8f18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,5 +34,5 @@ name = "wasm-build" path = "build/src/main.rs" [[bin]] -name = "indeterminism-check" -path = "indeterminism_check/src/main.rs" +name = "nondeterminism-check" +path = "nondeterminism_check/src/main.rs" diff --git a/indeterminism_check/.gitignore b/nondeterminism_check/.gitignore similarity index 100% rename from indeterminism_check/.gitignore rename to nondeterminism_check/.gitignore diff --git a/indeterminism_check/Cargo.toml b/nondeterminism_check/Cargo.toml similarity index 83% rename from indeterminism_check/Cargo.toml rename to nondeterminism_check/Cargo.toml index b85adfd..e5ccfc9 100644 --- a/indeterminism_check/Cargo.toml +++ b/nondeterminism_check/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "indeterminism_check" +name = "nondeterminism_check" version = "0.1.0" authors = ["NikVolf "] diff --git a/indeterminism_check/src/main.rs b/nondeterminism_check/src/main.rs similarity index 74% rename from indeterminism_check/src/main.rs rename to nondeterminism_check/src/main.rs index 44eb125..c699938 100644 --- a/indeterminism_check/src/main.rs +++ b/nondeterminism_check/src/main.rs @@ -17,10 +17,10 @@ fn main() { // Loading module let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - if wasm_utils::have_indeterminism(module) { - println!("Non-determinism found"); + if wasm_utils::is_deterministic(module) { + println!("Module is deterministic"); } else { - println!("Non-determinism not found"); + println!("Module is not deterministic"); } } diff --git a/src/lib.rs b/src/lib.rs index 57d8aaa..c565594 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,11 +11,11 @@ mod symbols; mod logger; mod ext; mod pack; -mod indeterminism_check; +mod nondeterminism_check; pub use optimizer::{optimize, Error as OptimizerError}; pub use gas::inject_gas_counter; pub use logger::init_log; pub use ext::externalize; pub use pack::pack_instance; -pub use indeterminism_check::have_indeterminism; +pub use nondeterminism_check::is_deterministic; diff --git a/src/indeterminism_check.rs b/src/nondeterminism_check.rs similarity index 81% rename from src/indeterminism_check.rs rename to src/nondeterminism_check.rs index e041edf..411eeda 100644 --- a/src/indeterminism_check.rs +++ b/src/nondeterminism_check.rs @@ -2,7 +2,7 @@ use parity_wasm::{elements}; use parity_wasm::elements::{ Section, Opcode }; use parity_wasm::elements::Opcode::*; -fn check_opcodes (opcodes: &[Opcode]) -> bool { +fn have_nondeterministic_opcodes (opcodes: &[Opcode]) -> bool { for opcode in opcodes { match *opcode { F32Abs | @@ -77,34 +77,34 @@ fn check_opcodes (opcodes: &[Opcode]) -> bool { -pub fn have_indeterminism(module: elements::Module) -> bool { +pub fn is_deterministic(module: elements::Module) -> bool { for section in module.sections() { match *section { Section::Code(ref cs) => { for body in cs.bodies() { - if check_opcodes(body.code().elements()) { - return true; + if have_nondeterministic_opcodes(body.code().elements()) { + return false; } } }, Section::Global(ref global) => { for entry in global.entries() { - if check_opcodes(entry.init_expr().code()) { - return true; + if have_nondeterministic_opcodes(entry.init_expr().code()) { + return false; } } }, Section::Element(ref element) => { for entry in element.entries() { - if check_opcodes(entry.offset().code()) { - return true; + if have_nondeterministic_opcodes(entry.offset().code()) { + return false; } } } _ => continue } } - false + true } #[cfg(test)] @@ -113,7 +113,7 @@ mod tests { use super::*; #[test] - fn indeterminism_found() { + fn nondeterminism_found() { let module = builder::module() .function().signature().return_type().f32().build() .body() @@ -128,11 +128,11 @@ mod tests { .build() .build() .build(); - assert_eq!(true, have_indeterminism(module)); + assert_eq!(false, is_deterministic(module)); } #[test] - fn indeterminism_not() { + fn nondeterminism_not() { let module = builder::module() .function().signature().return_type().f32().build() .body() @@ -147,6 +147,6 @@ mod tests { .build() .build() .build(); - assert_eq!(false, have_indeterminism(module)); + assert_eq!(true, is_deterministic(module)); } } From 93647ec3df5147106c225bb945c02f0e0e49e893 Mon Sep 17 00:00:00 2001 From: fro Date: Thu, 21 Sep 2017 12:44:50 +0300 Subject: [PATCH 09/11] unnecessary checks for init_expr removed ( http://webassembly.org/docs/modules/#initializer-expression ) --- src/nondeterminism_check.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/nondeterminism_check.rs b/src/nondeterminism_check.rs index 411eeda..11e0fd1 100644 --- a/src/nondeterminism_check.rs +++ b/src/nondeterminism_check.rs @@ -87,20 +87,6 @@ pub fn is_deterministic(module: elements::Module) -> bool { } } }, - Section::Global(ref global) => { - for entry in global.entries() { - if have_nondeterministic_opcodes(entry.init_expr().code()) { - return false; - } - } - }, - Section::Element(ref element) => { - for entry in element.entries() { - if have_nondeterministic_opcodes(entry.offset().code()) { - return false; - } - } - } _ => continue } } From 6e5c2282bfd4d573e690395d25357ec6de5b22d2 Mon Sep 17 00:00:00 2001 From: fro Date: Fri, 22 Sep 2017 19:20:58 +0300 Subject: [PATCH 10/11] take module as ref in is_deterministic --- nondeterminism_check/src/main.rs | 2 +- src/nondeterminism_check.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nondeterminism_check/src/main.rs b/nondeterminism_check/src/main.rs index c699938..fca40af 100644 --- a/nondeterminism_check/src/main.rs +++ b/nondeterminism_check/src/main.rs @@ -17,7 +17,7 @@ fn main() { // Loading module let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - if wasm_utils::is_deterministic(module) { + if wasm_utils::is_deterministic(&module) { println!("Module is deterministic"); } else { println!("Module is not deterministic"); diff --git a/src/nondeterminism_check.rs b/src/nondeterminism_check.rs index 11e0fd1..b8cc7ca 100644 --- a/src/nondeterminism_check.rs +++ b/src/nondeterminism_check.rs @@ -77,7 +77,7 @@ fn have_nondeterministic_opcodes (opcodes: &[Opcode]) -> bool { -pub fn is_deterministic(module: elements::Module) -> bool { +pub fn is_deterministic(module: &elements::Module) -> bool { for section in module.sections() { match *section { Section::Code(ref cs) => { @@ -114,7 +114,7 @@ mod tests { .build() .build() .build(); - assert_eq!(false, is_deterministic(module)); + assert_eq!(false, is_deterministic(&module)); } #[test] @@ -133,6 +133,6 @@ mod tests { .build() .build() .build(); - assert_eq!(true, is_deterministic(module)); + assert_eq!(true, is_deterministic(&module)); } } From 9be2a5bf316b06a5b6c07e6e44dc06dac5b2e752 Mon Sep 17 00:00:00 2001 From: fro Date: Mon, 25 Sep 2017 20:14:46 +0300 Subject: [PATCH 11/11] runtime type injection implemented #10 --- Cargo.toml | 1 + build/src/main.rs | 20 ++++++++++++++++- src/lib.rs | 3 +++ src/nondeterminism_check.rs | 2 +- src/runtime_type.rs | 43 +++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/runtime_type.rs diff --git a/Cargo.toml b/Cargo.toml index f0b8f18..c5b07aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ env_logger = "0.4" lazy_static = "0.2" clap = "2.24" glob = "0.2" +byteorder = "1" [lib] diff --git a/build/src/main.rs b/build/src/main.rs index 9cf3a1e..71b23b9 100644 --- a/build/src/main.rs +++ b/build/src/main.rs @@ -76,6 +76,14 @@ fn main() { .arg(Arg::with_name("skip_alloc") .help("Skip allocator externalizer step producing final wasm") .long("skip-externalize")) + .arg(Arg::with_name("runtime_type") + .help("Injects RUNTIME_TYPE global export") + .takes_value(true) + .long("runtime-type")) + .arg(Arg::with_name("runtime_version") + .help("Injects RUNTIME_VERSION global export") + .takes_value(true) + .long("runtime-version")) .get_matches(); let target_dir = matches.value_of("target").expect("is required; qed"); @@ -98,5 +106,15 @@ fn main() { wasm_utils::optimize(&mut module, vec!["_call", "setTempRet0"]).expect("Optimizer to finish without errors"); } + if let Some(runtime_type) = matches.value_of("runtime_type") { + let runtime_type: &[u8] = runtime_type.as_bytes(); + if runtime_type.len() != 4 { + panic!("--runtime-type should be equal to 4 bytes"); + } + let runtime_version: u32 = matches.value_of("runtime_version").unwrap_or("1").parse() + .expect("--runtime-version should be a positive integer"); + module = wasm_utils::inject_runtime_type(module, &runtime_type, runtime_version); + } + parity_wasm::serialize_to_file(&path, module).unwrap(); -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index c565594..12d0a9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ extern crate parity_wasm; extern crate env_logger; +extern crate byteorder; #[macro_use] extern crate log; #[macro_use] extern crate lazy_static; @@ -12,6 +13,7 @@ mod logger; mod ext; mod pack; mod nondeterminism_check; +mod runtime_type; pub use optimizer::{optimize, Error as OptimizerError}; pub use gas::inject_gas_counter; @@ -19,3 +21,4 @@ pub use logger::init_log; pub use ext::externalize; pub use pack::pack_instance; pub use nondeterminism_check::is_deterministic; +pub use runtime_type::inject_runtime_type; diff --git a/src/nondeterminism_check.rs b/src/nondeterminism_check.rs index b8cc7ca..eae6f1a 100644 --- a/src/nondeterminism_check.rs +++ b/src/nondeterminism_check.rs @@ -120,7 +120,7 @@ mod tests { #[test] fn nondeterminism_not() { let module = builder::module() - .function().signature().return_type().f32().build() + .function().signature().return_type().i32().build() .body() .with_opcodes(elements::Opcodes::new( vec![ diff --git a/src/runtime_type.rs b/src/runtime_type.rs new file mode 100644 index 0000000..24443ef --- /dev/null +++ b/src/runtime_type.rs @@ -0,0 +1,43 @@ +use parity_wasm::{elements, builder}; +use self::elements::{ Module, GlobalEntry, External, ExportEntry, GlobalType, ValueType, InitExpr, Opcode, Internal }; +use byteorder::{ LittleEndian, ByteOrder }; + +pub fn inject_runtime_type(module: Module, runtime_type: &[u8], runtime_version: u32) -> Module { + let runtime_type: u32 = LittleEndian::read_u32(&runtime_type); + let globals_count: u32 = match module.global_section() { + Some(ref section) => section.entries().len() as u32, + None => 0 + }; + let imported_globals_count: u32 = match module.import_section() { + Some(ref section) => section.entries().iter().filter(|e| match *e.external() { + External::Global(ref _a) => true, + _ => false + }).count() as u32, + None => 0 + }; + let total_globals_count: u32 = globals_count + imported_globals_count; + + builder::from_module(module) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(runtime_type as i32), Opcode::End]))) + .with_export(ExportEntry::new("RUNTIME_TYPE".into(), Internal::Global(total_globals_count))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(runtime_version as i32), Opcode::End]))) + .with_export(ExportEntry::new("RUNTIME_VERSION".into(), Internal::Global(total_globals_count + 1))) + .build() +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn it_injects() { + let mut module = builder::module() + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(42 as i32)]))) + .build(); + module = inject_runtime_type(module, b"emcc", 1); + let global_section = module.global_section().expect("Global section expected"); + assert_eq!(3, global_section.entries().len()); + let export_section = module.export_section().expect("Export section expected"); + assert!(export_section.entries().iter().find(|e| e.field() == "RUNTIME_TYPE" ).is_some()); + assert!(export_section.entries().iter().find(|e| e.field() == "RUNTIME_VERSION" ).is_some()); + } +}