Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding "optimization-passes" option #216

Merged
merged 18 commits into from
Mar 18, 2021
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
48 changes: 35 additions & 13 deletions src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ use crate::{
crate_metadata::CrateMetadata,
maybe_println, util, validate_wasm,
workspace::{ManifestPath, Profile, Workspace},
BuildArtifacts, BuildResult, OptimizationResult, UnstableFlags, UnstableOptions, Verbosity,
VerbosityFlags,
BuildArtifacts, BuildResult, OptimizationFlags, OptimizationPasses, OptimizationResult,
UnstableFlags, UnstableOptions, Verbosity, VerbosityFlags,
};
use anyhow::{Context, Result};
use colored::Colorize;
Expand Down Expand Up @@ -66,6 +66,8 @@ pub struct BuildCommand {
verbosity: VerbosityFlags,
#[structopt(flatten)]
unstable_options: UnstableOptions,
#[structopt(flatten)]
optimization_passes: OptimizationFlags,
}

impl BuildCommand {
Expand All @@ -74,12 +76,15 @@ impl BuildCommand {
let unstable_flags: UnstableFlags =
TryFrom::<&UnstableOptions>::try_from(&self.unstable_options)?;
let verbosity = TryFrom::<&VerbosityFlags>::try_from(&self.verbosity)?;
let optimization_passes =
TryFrom::<&OptimizationFlags>::try_from(&self.optimization_passes)?;
execute(
&manifest_path,
verbosity,
true,
self.build_artifact,
unstable_flags,
optimization_passes,
)
}
}
Expand All @@ -102,12 +107,14 @@ impl CheckCommand {
let unstable_flags: UnstableFlags =
TryFrom::<&UnstableOptions>::try_from(&self.unstable_options)?;
let verbosity: Verbosity = TryFrom::<&VerbosityFlags>::try_from(&self.verbosity)?;
let optimization_passes = OptimizationPasses::Zero;
execute(
&manifest_path,
verbosity,
false,
BuildArtifacts::CheckOnly,
unstable_flags,
optimization_passes,
)
}
}
Expand Down Expand Up @@ -268,14 +275,16 @@ fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> {
///
/// The intention is to reduce the size of bloated wasm binaries as a result of missing
/// optimizations (or bugs?) between Rust and Wasm.
fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<OptimizationResult> {
fn optimize_wasm(
crate_metadata: &CrateMetadata,
optimization_passes: OptimizationPasses,
) -> Result<OptimizationResult> {
let mut dest_optimized = crate_metadata.dest_wasm.clone();
dest_optimized.set_file_name(format!("{}-opt.wasm", crate_metadata.package_name));

let _ = do_optimization(
crate_metadata.dest_wasm.as_os_str(),
&dest_optimized.as_os_str(),
3,
optimization_passes,
)?;

let original_size = metadata(&crate_metadata.dest_wasm)?.len() as f64 / 1000.0;
Expand All @@ -299,17 +308,17 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result<OptimizationResult> {
fn do_optimization(
dest_wasm: &OsStr,
dest_optimized: &OsStr,
optimization_level: u32,
optimization_level: OptimizationPasses,
) -> Result<()> {
let mut dest_wasm_file = File::open(dest_wasm)?;
let mut dest_wasm_file_content = Vec::new();
dest_wasm_file.read_to_end(&mut dest_wasm_file_content)?;

let codegen_config = binaryen::CodegenConfig {
// number of optimization passes (spends potentially a lot of time optimizing)
optimization_level,
// Number of optimization passes (spends potentially a lot of time optimizing)
optimization_level: optimization_level.to_passes(),
// the default
shrink_level: 1,
shrink_level: optimization_level.to_shrink(),
// the default
debug_info: false,
};
Expand All @@ -334,7 +343,7 @@ fn do_optimization(
fn do_optimization(
dest_wasm: &OsStr,
dest_optimized: &OsStr,
optimization_level: u32,
optimization_level: OptimizationPasses,
) -> Result<()> {
// check `wasm-opt` is installed
if which::which("wasm-opt").is_err() {
Expand All @@ -349,7 +358,7 @@ fn do_optimization(

let output = Command::new("wasm-opt")
.arg(dest_wasm)
.arg(format!("-O{}", optimization_level))
.arg(format!("-O{}", optimization_level.to_str()))
.arg("-o")
.arg(dest_optimized)
// the memory in our module is imported, `wasm-opt` needs to be told that
Expand Down Expand Up @@ -381,6 +390,7 @@ fn execute(
optimize_contract: bool,
build_artifact: BuildArtifacts,
unstable_flags: UnstableFlags,
optimization_passes: OptimizationPasses,
) -> Result<BuildResult> {
if build_artifact == BuildArtifacts::CodeOnly || build_artifact == BuildArtifacts::CheckOnly {
let crate_metadata = CrateMetadata::collect(manifest_path)?;
Expand All @@ -390,6 +400,7 @@ fn execute(
optimize_contract,
build_artifact,
unstable_flags,
optimization_passes,
)?;
let res = BuildResult {
dest_wasm: maybe_dest_wasm,
Expand All @@ -403,7 +414,13 @@ fn execute(
return Ok(res);
}

let res = super::metadata::execute(&manifest_path, verbosity, build_artifact, unstable_flags)?;
let res = super::metadata::execute(
&manifest_path,
verbosity,
build_artifact,
unstable_flags,
optimization_passes,
)?;
Ok(res)
}

Expand All @@ -422,6 +439,7 @@ pub(crate) fn execute_with_crate_metadata(
optimize_contract: bool,
build_artifact: BuildArtifacts,
unstable_flags: UnstableFlags,
optimization_passes: OptimizationPasses,
) -> Result<(Option<PathBuf>, Option<OptimizationResult>)> {
maybe_println!(
verbosity,
Expand All @@ -446,7 +464,7 @@ pub(crate) fn execute_with_crate_metadata(
format!("[3/{}]", build_artifact.steps()).bold(),
"Optimizing wasm file".bright_green().bold()
);
let optimization_result = optimize_wasm(&crate_metadata)?;
let optimization_result = optimize_wasm(&crate_metadata, optimization_passes)?;
Ok((
Some(crate_metadata.dest_wasm.clone()),
Some(optimization_result),
Expand All @@ -464,12 +482,14 @@ mod tests_ci_only {
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let manifest_path =
ManifestPath::new(&path.join("new_project").join("Cargo.toml")).unwrap();
let optimization_passes = 3;
let res = super::execute(
&manifest_path,
None,
true,
BuildArtifacts::All,
UnstableFlags::default(),
optimization_passes,
)
.expect("build failed");

Expand All @@ -495,6 +515,7 @@ mod tests_ci_only {
cmd::new::execute("new_project", Some(path)).expect("new project creation failed");
let project_dir = path.join("new_project");
let manifest_path = ManifestPath::new(&project_dir.join("Cargo.toml")).unwrap();
let optimization_passes = 3;

// when
super::execute(
Expand All @@ -503,6 +524,7 @@ mod tests_ci_only {
true,
BuildArtifacts::CheckOnly,
UnstableFlags::default(),
optimization_passes,
)
.expect("build failed");

Expand Down
6 changes: 5 additions & 1 deletion src/cmd/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
crate_metadata::CrateMetadata,
maybe_println, util,
workspace::{ManifestPath, Workspace},
BuildArtifacts, BuildResult, OptimizationResult, UnstableFlags, Verbosity,
BuildArtifacts, BuildResult, OptimizationPasses, OptimizationResult, UnstableFlags, Verbosity,
};

use anyhow::Result;
Expand All @@ -40,6 +40,7 @@ struct GenerateMetadataCommand {
verbosity: Verbosity,
build_artifact: BuildArtifacts,
unstable_options: UnstableFlags,
optimization_passes: OptimizationPasses,
}

/// Result of generating the extended contract project metadata
Expand Down Expand Up @@ -239,6 +240,7 @@ impl GenerateMetadataCommand {
true, // for the hash we always use the optimized version of the contract
self.build_artifact,
self.unstable_options.clone(),
self.optimization_passes,
)?;

let wasm = fs::read(&self.crate_metadata.dest_wasm)?;
Expand All @@ -265,13 +267,15 @@ pub(crate) fn execute(
verbosity: Verbosity,
build_artifact: BuildArtifacts,
unstable_options: UnstableFlags,
optimization_passes: OptimizationPasses,
) -> Result<BuildResult> {
let crate_metadata = CrateMetadata::collect(manifest_path)?;
let res = GenerateMetadataCommand {
crate_metadata,
verbosity,
build_artifact,
unstable_options,
optimization_passes,
}
.exec()?;
Ok(res)
Expand Down
94 changes: 94 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,100 @@ impl ExtrinsicOpts {
}
}

#[derive(Clone, Debug, StructOpt)]
pub struct OptimizationFlags {
/// number of optimization passes, passed as an argument to wasm-opt
///
/// - `0`: execute no optimization passes
///
/// - `1`: execute 1 optimization pass (quick & useful opts, useful for iteration builds)
///
/// - `2`, execute 2 optimization passes (most opts, generally gets most perf)
///
/// - `3`, execute 3 optimization passes (spends potentially a lot of time optimizing)
///
/// - `4`, execute 4 optimization passes (also flatten the IR, which can take a lot more time and memory
/// but is useful on more nested / complex / less-optimized input)
///
/// - `s`, execute default optimization passes, focusing on code size
///
/// - `z`, execute default optimization passes, super-focusing on code size
///
/// -
#[structopt(long = "optimization-passes", default_value = "3")]
optimization_passes: String,
}

#[derive(Clone, Copy)]
pub enum OptimizationPasses {
Zero,
One,
Two,
Three,
Four,
S,
Z,
}

impl TryFrom<&OptimizationFlags> for OptimizationPasses {
type Error = Error;

fn try_from(value: &OptimizationFlags) -> Result<Self, Self::Error> {
match value.optimization_passes.to_lowercase().as_str() {
"0" => Ok(OptimizationPasses::Zero),
"1" => Ok(OptimizationPasses::One),
"2" => Ok(OptimizationPasses::Two),
"3" => Ok(OptimizationPasses::Three),
"4" => Ok(OptimizationPasses::Four),
"s" => Ok(OptimizationPasses::S),
"z" => Ok(OptimizationPasses::Z),
_ => anyhow::bail!(
"Unknown optimization passes option {}",
value.optimization_passes
),
}
}
}

impl OptimizationPasses {
/// Returns the string representation of `OptimizationPasses`
pub(crate) fn to_str(&self) -> &str {
match self {
OptimizationPasses::Zero => "0",
OptimizationPasses::One => "1",
OptimizationPasses::Two => "2",
OptimizationPasses::Three => "3",
OptimizationPasses::Four => "4",
OptimizationPasses::S => "s",
OptimizationPasses::Z => "z",
}
}

/// Returns the number of optimization passes to do
#[cfg(feature = "binaryen-as-dependency")]
pub(crate) fn to_passes(&self) -> u32 {
trace-andreason marked this conversation as resolved.
Show resolved Hide resolved
match self {
OptimizationPasses::Zero => 0,
OptimizationPasses::One => 1,
OptimizationPasses::Two => 2,
OptimizationPasses::Three => 3,
OptimizationPasses::Four => 4,
_ => 3, // Default to three for shrink settings
}
}

/// Returns amount of shrinkage to do
#[cfg(feature = "binaryen-as-dependency")]
pub(crate) fn to_shrink(&self) -> u32 {
trace-andreason marked this conversation as resolved.
Show resolved Hide resolved
match self {
OptimizationPasses::Zero => 0,
OptimizationPasses::S => 1,
OptimizationPasses::Z => 2,
_ => 1,
}
}
}

#[derive(Clone, Debug, StructOpt)]
pub struct VerbosityFlags {
/// No output printed to stdout
Expand Down