diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs index 58df015e97a1..607339050cc7 100644 --- a/crates/cranelift/src/builder.rs +++ b/crates/cranelift/src/builder.rs @@ -21,6 +21,7 @@ struct Builder { linkopts: LinkOptions, cache_store: Option>, clif_dir: Option, + opt_clif_dir: Option, wmemcheck: bool, } @@ -44,6 +45,7 @@ pub fn builder(triple: Option) -> Result> { linkopts: LinkOptions::default(), cache_store: None, clif_dir: None, + opt_clif_dir: None, wmemcheck: false, })) } @@ -58,6 +60,11 @@ impl CompilerBuilder for Builder { Ok(()) } + fn opt_clif_dir(&mut self, path: &path::Path) -> Result<()> { + self.opt_clif_dir = Some(path.to_path_buf()); + Ok(()) + } + fn target(&mut self, target: target_lexicon::Triple) -> Result<()> { self.inner.target(target)?; Ok(()) @@ -97,6 +104,7 @@ impl CompilerBuilder for Builder { self.cache_store.clone(), self.linkopts.clone(), self.clif_dir.clone(), + self.opt_clif_dir.clone(), self.wmemcheck, ))) } diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index a49d019b7ab3..baf6c8abcc7e 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -1,3 +1,4 @@ +use crate::compiler::path::PathBuf; use crate::debug::DwarfSectionRelocTarget; use crate::func_environ::FuncEnvironment; use crate::DEBUG_ASSERT_TRAP_CODE; @@ -69,6 +70,7 @@ pub struct Compiler { linkopts: LinkOptions, cache_store: Option>, clif_dir: Option, + opt_clif_dir: Option, wmemcheck: bool, } @@ -107,6 +109,7 @@ impl Compiler { cache_store: Option>, linkopts: LinkOptions, clif_dir: Option, + opt_clif_dir: Option, wmemcheck: bool, ) -> Compiler { Compiler { @@ -116,6 +119,7 @@ impl Compiler { linkopts, cache_store, clif_dir, + opt_clif_dir, wmemcheck, } } @@ -228,7 +232,11 @@ impl wasmtime_environ::Compiler for Compiler { write!(output, "{}", context.func.display()).unwrap(); } - let (info, func) = compiler.finish_with_info(Some((&body, &self.tunables)))?; + let (info, func) = compiler.finish_with_info( + Some((&body, &self.tunables)), + &self.opt_clif_dir, + func_index.as_u32(), + )?; let timing = cranelift_codegen::timing::take_current(); log::debug!("{:?} translated in {:?}", func_index, timing.total()); @@ -799,7 +807,7 @@ impl FunctionCompiler<'_> { } fn finish(self) -> Result { - let (info, func) = self.finish_with_info(None)?; + let (info, func) = self.finish_with_info(None, &None, 0)?; assert!(info.stack_maps.is_empty()); Ok(func) } @@ -807,6 +815,8 @@ impl FunctionCompiler<'_> { fn finish_with_info( mut self, body_and_tunables: Option<(&FunctionBody<'_>, &Tunables)>, + opt_clif_dir: &Option, + func_index: u32, ) -> Result<(WasmFunctionInfo, CompiledFunction), CompileError> { let context = &mut self.cx.codegen_context; let isa = &*self.compiler.isa; @@ -814,6 +824,17 @@ impl FunctionCompiler<'_> { compile_maybe_cached(context, isa, self.cx.incremental_cache_ctx.as_mut())?; let mut compiled_code = context.take_compiled_code().unwrap(); + if let Some(path) = opt_clif_dir { + use std::io::Write; + + let mut path = path.to_path_buf(); + path.push(format!("wasm_func_{func_index}")); + path.set_extension("clif"); + + let mut output = std::fs::File::create(path).unwrap(); + write!(output, "{}", context.func.display()).unwrap(); + } + // Give wasm functions, user defined code, a "preferred" alignment // instead of the minimum alignment as this can help perf in niche // situations. diff --git a/crates/environ/src/compile/mod.rs b/crates/environ/src/compile/mod.rs index 0af013a9f11b..8ad7f4cdc88b 100644 --- a/crates/environ/src/compile/mod.rs +++ b/crates/environ/src/compile/mod.rs @@ -111,6 +111,11 @@ pub trait CompilerBuilder: Send + Sync + fmt::Debug { anyhow::bail!("clif output not supported"); } + /// Enables optimized clif output in the directory specified. + fn opt_clif_dir(&mut self, _path: &path::Path) -> Result<()> { + anyhow::bail!("optimized clif output not supported"); + } + /// Returns the currently configured target triple that compilation will /// produce artifacts for. fn triple(&self) -> &target_lexicon::Triple; diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index a25cfd35ced5..2fd0b890b79a 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -163,6 +163,7 @@ struct CompilerConfig { #[cfg(all(feature = "incremental-cache", feature = "cranelift"))] cache_store: Option>, clif_dir: Option, + opt_clif_dir: Option, wmemcheck: bool, } @@ -177,6 +178,7 @@ impl CompilerConfig { #[cfg(all(feature = "incremental-cache", feature = "cranelift"))] cache_store: None, clif_dir: None, + opt_clif_dir: None, wmemcheck: false, } } @@ -1897,6 +1899,10 @@ impl Config { compiler.clif_dir(path)?; } + if let Some(path) = &self.compiler_config.opt_clif_dir { + compiler.opt_clif_dir(path)?; + } + // If probestack is enabled for a target, Wasmtime will always use the // inline strategy which doesn't require us to define a `__probestack` // function or similar. @@ -1999,6 +2005,13 @@ impl Config { self } + /// Enables optimized clif output when compiling a WebAssembly module. + #[cfg(any(feature = "cranelift", feature = "winch"))] + pub fn emit_opt_clif(&mut self, path: &Path) -> &mut Self { + self.compiler_config.opt_clif_dir = Some(path.to_path_buf()); + self + } + /// Configures whether, when on macOS, Mach ports are used for exception /// handling instead of traditional Unix-based signal handling. /// diff --git a/src/commands/compile.rs b/src/commands/compile.rs index 791f44de3863..d0ba9b981e12 100644 --- a/src/commands/compile.rs +++ b/src/commands/compile.rs @@ -51,6 +51,10 @@ pub struct CompileCommand { #[arg(long = "emit-clif", value_name = "PATH")] pub emit_clif: Option, + /// The directory path to write optimized clif files into, one clif file per wasm function. + #[arg(long = "emit-opt-clif", value_name = "PATH")] + pub emit_opt_clif: Option, + /// The path of the WebAssembly to compile #[arg(index = 1, value_name = "MODULE")] pub module: PathBuf, @@ -78,6 +82,21 @@ impl CompileCommand { config.emit_clif(&path); } + if let Some(path) = self.emit_opt_clif { + if !path.exists() { + std::fs::create_dir(&path)?; + } + + if !path.is_dir() { + bail!( + "the path passed for '--emit-opt-clif' ({}) must be a directory", + path.display() + ); + } + + config.emit_opt_clif(&path); + } + let engine = Engine::new(&config)?; if self.module.file_name().is_none() {