Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ jobs:
# Explicitly move into the libchisel directory to properly build with features
# FIXME: make this work in workspace
cd libchisel && cargo build --release --features wabt && cd ..
cd libchisel && cargo build --release --features binaryen && cd ..
- run:
name: Test
command: |
cargo test
# Explicitly move into the libchisel directory to properly build with features
# FIXME: make this work in workspace
cd libchisel && cargo test --features wabt && cd ..
cd libchisel && cargo test --features binaryen && cd ..
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
members = [
"chisel",
"libchisel"
]
]
1 change: 1 addition & 0 deletions chisel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ serde_yaml = "0.8.7"
[features]
default = []
wabt = [ "libchisel/wabt" ]
binaryen = ["libchisel/binaryen"]
12 changes: 12 additions & 0 deletions chisel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ use libchisel::{
snip::*, trimexports::*, trimstartfunc::*, verifyexports::*, verifyimports::*,
};

#[cfg(feature = "binaryen")]
use libchisel::binaryenopt::*;

use clap::{App, Arg, ArgMatches, SubCommand};
use libchisel::*;
use parity_wasm::elements::{deserialize_buffer, serialize_to_file, Module};
Expand Down Expand Up @@ -269,6 +272,15 @@ fn execute_module(context: &ModuleContext, module: &mut Module) -> bool {
is_translator = true;
translate_module(module, &DropSection::NamesSection)
}
#[cfg(feature = "binaryen")]
"binaryenopt" => {
is_translator = true;
if let Ok(chisel) = BinaryenOptimiser::with_preset(&preset) {
translate_module(module, &chisel)
} else {
Err("binaryenopt: Invalid preset")
}
}
_ => Err("Module Not Found"),
};

Expand Down
1 change: 1 addition & 0 deletions libchisel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ keywords = ["webassembly", "wasm", "blockchain", "ethereum"]
edition = "2018"

[dependencies]
binaryen = { version = "0.8", optional = true }
parity-wasm = "^0.40.1"
rustc-hex = "1.0"
failure = "0.1.5"
Expand Down
142 changes: 142 additions & 0 deletions libchisel/src/binaryenopt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use super::{ChiselModule, ModuleError, ModuleKind, ModulePreset, ModuleTranslator};
use crate::utils::SerializationHelpers;
use parity_wasm::elements::*;

// FIXME: change level names
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps something renaming the enum itself would be clean enough. Something like BinaryenOptimizationLevel::O1, etc. would be more clear, I think.

pub enum BinaryenOptimiser {
O0, // Baseline aka no changes
O1,
O2,
O3,
O4,
Os,
Oz,
}

impl<'a> ChiselModule<'a> for BinaryenOptimiser {
type ObjectReference = &'a dyn ModuleTranslator;

fn id(&'a self) -> String {
"binaryenopt".to_string()
}

fn kind(&'a self) -> ModuleKind {
ModuleKind::Translator
}

fn as_abstract(&'a self) -> Self::ObjectReference {
self as Self::ObjectReference
}
}

impl ModulePreset for BinaryenOptimiser {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"O0" => Ok(BinaryenOptimiser::O0),
"O1" => Ok(BinaryenOptimiser::O1),
"O2" => Ok(BinaryenOptimiser::O2),
"O3" => Ok(BinaryenOptimiser::O3),
"O4" => Ok(BinaryenOptimiser::O4),
"Os" => Ok(BinaryenOptimiser::Os),
"Oz" => Ok(BinaryenOptimiser::Oz),
_ => Err(()),
}
}
}

impl ModuleTranslator for BinaryenOptimiser {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Err(ModuleError::NotSupported)
}

fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let has_names_section = module.has_names_section();

// FIXME: could just move this into `BinaryenOptimiser`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, would be much cleaner.

let config = match &self {
BinaryenOptimiser::O0 => binaryen::CodegenConfig {
optimization_level: 0,
shrink_level: 0,
debug_info: has_names_section,
},
BinaryenOptimiser::O1 => binaryen::CodegenConfig {
optimization_level: 1,
shrink_level: 0,
debug_info: has_names_section,
},
BinaryenOptimiser::O2 => binaryen::CodegenConfig {
optimization_level: 2,
shrink_level: 0,
debug_info: has_names_section,
},
BinaryenOptimiser::O3 => binaryen::CodegenConfig {
optimization_level: 3,
shrink_level: 0,
debug_info: has_names_section,
},
BinaryenOptimiser::O4 => binaryen::CodegenConfig {
optimization_level: 4,
shrink_level: 0,
debug_info: has_names_section,
},
BinaryenOptimiser::Os => binaryen::CodegenConfig {
optimization_level: 2,
shrink_level: 1,
debug_info: has_names_section,
},
BinaryenOptimiser::Oz => binaryen::CodegenConfig {
optimization_level: 2,
shrink_level: 2,
debug_info: has_names_section,
},
};

let serialized = module.clone().to_vec()?;
let output = binaryen_optimiser(&serialized, &config)?;
let output = Module::from_slice(&output)?;
Ok(Some(output))
}
}

fn binaryen_optimiser(
input: &[u8],
config: &binaryen::CodegenConfig,
) -> Result<Vec<u8>, ModuleError> {
match binaryen::Module::read(&input) {
Ok(module) => {
// NOTE: this is a global setting...
binaryen::set_global_codegen_config(&config);
module.optimize();
Ok(module.write())
}
Err(_) => Err(ModuleError::Custom(
"Failed to deserialise binary with binaryen".to_string(),
)),
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn smoke_test_o0() {
let input: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x08, 0x01, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];

let expected: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x05, 0x01, 0x03, 0x00, 0x01, 0x0b,
];

let module = Module::from_slice(&input).unwrap();
let translator = BinaryenOptimiser::with_preset("O0").unwrap();
let result = translator.translate(&module).unwrap().unwrap();
let serialized = result.to_vec().unwrap();
assert_eq!(expected, serialized);
}
}
4 changes: 4 additions & 0 deletions libchisel/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#[cfg(feature = "binaryen")]
extern crate binaryen;
extern crate parity_wasm;
extern crate rustc_hex;

use parity_wasm::elements::Module;

pub mod imports;

#[cfg(feature = "binaryen")]
pub mod binaryenopt;
pub mod checkfloat;
pub mod checkstartfunc;
pub mod deployer;
Expand Down