From ed0b5373261270ef713ca429e10db3e82fdb2d01 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Tue, 12 Mar 2024 18:51:10 -0400 Subject: [PATCH] Use the wasmtime-cranelift for winch component trampolines (#8082) * Use wasmtime-cranelift compiler for winch trampolines * Plumb the winch calling convention through Tunables * Guard setting the winch_callable tunable * Allow the trampolines field to be unused when component-model is disabled --- Cargo.lock | 1 + .../cranelift-shared/src/compiled_function.rs | 18 +++++++------ crates/cranelift-shared/src/obj.rs | 4 +-- crates/cranelift/src/compiler.rs | 18 ++++++------- crates/cranelift/src/lib.rs | 9 +++++++ crates/environ/src/tunables.rs | 4 +++ crates/wasmtime/src/config.rs | 10 +++++++ crates/wasmtime/src/engine/serialization.rs | 6 +++++ crates/winch/Cargo.toml | 6 ++++- crates/winch/src/builder.rs | 27 ++++++++++++------- crates/winch/src/compiler.rs | 17 +++++++----- 11 files changed, 84 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1af5217a0193..31eb0a18946f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3803,6 +3803,7 @@ dependencies = [ "object 0.33.0", "target-lexicon", "wasmparser", + "wasmtime-cranelift", "wasmtime-cranelift-shared", "wasmtime-environ", "winch-codegen", diff --git a/crates/cranelift-shared/src/compiled_function.rs b/crates/cranelift-shared/src/compiled_function.rs index 949c276c3b13..4f481458bead 100644 --- a/crates/cranelift-shared/src/compiled_function.rs +++ b/crates/cranelift-shared/src/compiled_function.rs @@ -1,3 +1,5 @@ +use std::any::Any; + use crate::{mach_reloc_to_reloc, mach_trap_to_trap, Relocation}; use cranelift_codegen::{ ir, ir::UserExternalNameRef, isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo, Final, @@ -59,11 +61,11 @@ pub struct CompiledFunctionMetadata { } /// Compiled function: machine code body, jump table offsets, and unwind information. -pub struct CompiledFunction { +pub struct CompiledFunction { /// The machine code buffer for this function. pub buffer: MachBufferFinalized, /// The environment for the compiled function. - env: E, + env: Box, /// The alignment for the compiled function. pub alignment: u32, /// The metadata for the compiled function, including unwind information @@ -71,17 +73,17 @@ pub struct CompiledFunction { metadata: CompiledFunctionMetadata, } -impl CompiledFunction -where - E: CompiledFuncEnv, -{ +impl CompiledFunction { /// Creates a [CompiledFunction] from a [cranelift_codegen::MachBufferFinalized] /// This function uses the information in the machine buffer to derive the traps and relocations /// fields. The compiled function metadata is loaded with the default values. - pub fn new(buffer: MachBufferFinalized, env: E, alignment: u32) -> Self { + pub fn new(buffer: MachBufferFinalized, env: E, alignment: u32) -> Self + where + E: Any + CompiledFuncEnv + Send + 'static, + { Self { buffer, - env, + env: Box::new(env), alignment, metadata: Default::default(), } diff --git a/crates/cranelift-shared/src/obj.rs b/crates/cranelift-shared/src/obj.rs index 8f1519bd6dad..8bce2b0120dc 100644 --- a/crates/cranelift-shared/src/obj.rs +++ b/crates/cranelift-shared/src/obj.rs @@ -13,7 +13,7 @@ //! function body, the imported wasm function do not. The trampolines symbol //! names have format "_trampoline_N", where N is `SignatureIndex`. -use crate::{CompiledFuncEnv, CompiledFunction, RelocationTarget}; +use crate::{CompiledFunction, RelocationTarget}; use anyhow::Result; use cranelift_codegen::binemit::Reloc; use cranelift_codegen::ir::LibCall; @@ -107,7 +107,7 @@ impl<'a> ModuleTextBuilder<'a> { pub fn append_func( &mut self, name: &str, - compiled_func: &'a CompiledFunction, + compiled_func: &'a CompiledFunction, resolve_reloc_target: impl Fn(FuncIndex) -> usize, ) -> (SymbolId, Range) { let body = compiled_func.buffer.data(); diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index ceb8d2ecf0bc..00d9c96d0cc3 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -65,7 +65,7 @@ impl Default for CompilerContext { /// A compiler that compiles a WebAssembly module with Compiler, translating /// the Wasm to Compiler IR, optimizing it and then translating to assembly. -pub(crate) struct Compiler { +pub struct Compiler { tunables: Tunables, contexts: Mutex>, isa: OwnedTargetIsa, @@ -104,7 +104,7 @@ impl Drop for Compiler { } impl Compiler { - pub(crate) fn new( + pub fn new( tunables: Tunables, isa: OwnedTargetIsa, cache_store: Option>, @@ -460,9 +460,7 @@ impl wasmtime_environ::Compiler for Compiler { let mut ret = Vec::with_capacity(funcs.len()); for (i, (sym, func)) in funcs.iter().enumerate() { - let func = func - .downcast_ref::>() - .unwrap(); + let func = func.downcast_ref::().unwrap(); let (sym, range) = builder.append_func(&sym, func, |idx| resolve_reloc(i, idx)); if self.tunables.generate_address_map { let addr = func.address_map(); @@ -575,7 +573,7 @@ impl wasmtime_environ::Compiler for Compiler { let functions_info = funcs .iter() .map(|(_, (_, func))| { - let f: &CompiledFunction = func.downcast_ref().unwrap(); + let f = func.downcast_ref::().unwrap(); f.metadata() }) .collect(); @@ -725,7 +723,7 @@ impl Compiler { &self, ty: &WasmFuncType, host_fn: usize, - ) -> Result, CompileError> { + ) -> Result { let isa = &*self.isa; let pointer_type = isa.pointer_type(); let native_call_sig = native_call_signature(isa, ty); @@ -788,7 +786,7 @@ impl Compiler { &self, ty: &WasmFuncType, host_fn: usize, - ) -> Result, CompileError> { + ) -> Result { let isa = &*self.isa; let pointer_type = isa.pointer_type(); let wasm_call_sig = wasm_call_signature(isa, ty, &self.tunables); @@ -1012,7 +1010,7 @@ impl FunctionCompiler<'_> { (builder, block0) } - fn finish(self) -> Result, CompileError> { + fn finish(self) -> Result { let (info, func) = self.finish_with_info(None)?; assert!(info.stack_maps.is_empty()); Ok(func) @@ -1021,7 +1019,7 @@ impl FunctionCompiler<'_> { fn finish_with_info( mut self, body_and_tunables: Option<(&FunctionBody<'_>, &Tunables)>, - ) -> Result<(WasmFunctionInfo, CompiledFunction), CompileError> { + ) -> Result<(WasmFunctionInfo, CompiledFunction), CompileError> { let context = &mut self.cx.codegen_context; let isa = &*self.compiler.isa; let (_, _code_buf) = diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs index a1ee5a8bafc5..848dd6fd33e5 100644 --- a/crates/cranelift/src/lib.rs +++ b/crates/cranelift/src/lib.rs @@ -153,6 +153,15 @@ fn wasm_call_signature( CallConv::Tail } + // The winch calling convention is only implemented for x64 and aarch64 + arch if tunables.tail_callable => { + assert!( + matches!(arch, Architecture::X86_64 | Architecture::Aarch64(_)), + "https://github.com/bytecodealliance/wasmtime/issues/6530" + ); + CallConv::Winch + } + // On s390x the "wasmtime" calling convention is used to give vectors // little-endian lane order at the ABI layer which should reduce the // need for conversion when operating on vector function arguments. By diff --git a/crates/environ/src/tunables.rs b/crates/environ/src/tunables.rs index 7328345ee765..fc932212f493 100644 --- a/crates/environ/src/tunables.rs +++ b/crates/environ/src/tunables.rs @@ -56,6 +56,9 @@ pub struct Tunables { /// Whether or not Wasm functions can be tail-called or not. pub tail_callable: bool, + + /// Whether or not Wasm functions target the winch abi. + pub winch_callable: bool, } impl Tunables { @@ -106,6 +109,7 @@ impl Tunables { debug_adapter_modules: false, relaxed_simd_deterministic: false, tail_callable: false, + winch_callable: false, } } diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 35e9fe4ba787..bef3e7061901 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1719,6 +1719,16 @@ impl Config { tail_callable } + // If we're going to compile with winch, we must use the winch calling convention. + #[cfg(any(feature = "cranelift", feature = "winch"))] + { + tunables.winch_callable = match self.compiler_config.strategy { + Strategy::Auto => !cfg!(feature = "cranelift") && cfg!(feature = "winch"), + Strategy::Cranelift => false, + Strategy::Winch => true, + }; + } + if tunables.static_memory_offset_guard_size < tunables.dynamic_memory_offset_guard_size { bail!("static memory guard size cannot be smaller than dynamic memory guard size"); } diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 8c854f2e74ad..b2d5915b406e 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -341,6 +341,7 @@ impl Metadata<'_> { guard_before_linear_memory, relaxed_simd_deterministic, tail_callable, + winch_callable, // This doesn't affect compilation, it's just a runtime setting. dynamic_memory_growth_reserve: _, @@ -402,6 +403,11 @@ impl Metadata<'_> { "relaxed simd deterministic semantics", )?; Self::check_bool(tail_callable, other.tail_callable, "WebAssembly tail calls")?; + Self::check_bool( + winch_callable, + other.winch_callable, + "Winch calling convention", + )?; Ok(()) } diff --git a/crates/winch/Cargo.toml b/crates/winch/Cargo.toml index 2231a538fce7..21f7e86379a1 100644 --- a/crates/winch/Cargo.toml +++ b/crates/winch/Cargo.toml @@ -17,10 +17,14 @@ wasmtime-environ = { workspace = true } anyhow = { workspace = true } object = { workspace = true } cranelift-codegen = { workspace = true } +wasmtime-cranelift = { workspace = true } wasmtime-cranelift-shared = { workspace = true } wasmparser = { workspace = true } gimli = { workspace = true } [features] -component-model = ["wasmtime-environ/component-model"] +component-model = [ + "wasmtime-environ/component-model", + "wasmtime-cranelift/component-model", +] all-arch = ["winch-codegen/all-arch"] diff --git a/crates/winch/src/builder.rs b/crates/winch/src/builder.rs index 2f9e5010928b..8fb8961f85f9 100644 --- a/crates/winch/src/builder.rs +++ b/crates/winch/src/builder.rs @@ -9,12 +9,15 @@ use winch_codegen::{isa, TargetIsa}; /// Compiler builder. struct Builder { inner: IsaBuilder>>, + cranelift: Box, } pub fn builder(triple: Option) -> Result> { - Ok(Box::new(Builder { - inner: IsaBuilder::new(triple, |triple| isa::lookup(triple).map_err(|e| e.into()))?, - })) + let inner = IsaBuilder::new(triple.clone(), |triple| { + isa::lookup(triple).map_err(|e| e.into()) + })?; + let cranelift = wasmtime_cranelift::builder(triple)?; + Ok(Box::new(Builder { inner, cranelift })) } impl CompilerBuilder for Builder { @@ -23,16 +26,21 @@ impl CompilerBuilder for Builder { } fn target(&mut self, target: target_lexicon::Triple) -> Result<()> { - self.inner.target(target)?; + self.inner.target(target.clone())?; + self.cranelift.target(target)?; Ok(()) } fn set(&mut self, name: &str, value: &str) -> Result<()> { - self.inner.set(name, value) + self.inner.set(name, value)?; + self.cranelift.set(name, value)?; + Ok(()) } fn enable(&mut self, name: &str) -> Result<()> { - self.inner.enable(name) + self.inner.enable(name)?; + self.cranelift.enable(name)?; + Ok(()) } fn settings(&self) -> Vec { @@ -40,14 +48,15 @@ impl CompilerBuilder for Builder { } fn set_tunables(&mut self, tunables: wasmtime_environ::Tunables) -> Result<()> { - let _ = tunables; + assert!(tunables.winch_callable); + self.cranelift.set_tunables(tunables)?; Ok(()) } fn build(&self) -> Result> { let isa = self.inner.build()?; - - Ok(Box::new(Compiler::new(isa))) + let cranelift = self.cranelift.build()?; + Ok(Box::new(Compiler::new(isa, cranelift))) } fn enable_incremental_compilation( diff --git a/crates/winch/src/compiler.rs b/crates/winch/src/compiler.rs index c58318eb8003..a9d52de34708 100644 --- a/crates/winch/src/compiler.rs +++ b/crates/winch/src/compiler.rs @@ -25,6 +25,12 @@ struct CompilationContext { pub(crate) struct Compiler { isa: Box, + + /// The trampoline compiler is only used for the component model currently, but will soon be + /// used for all winch trampolines. For now, mark it as unused to handle the situation where + /// the component-model feature is disabled. + #[allow(unused)] + trampolines: Box, contexts: Mutex>, } @@ -40,9 +46,10 @@ impl wasmtime_cranelift_shared::CompiledFuncEnv for CompiledFuncEnv { } impl Compiler { - pub fn new(isa: Box) -> Self { + pub fn new(isa: Box, trampolines: Box) -> Self { Self { isa, + trampolines, contexts: Mutex::new(Vec::new()), } } @@ -68,7 +75,7 @@ impl Compiler { /// Emit unwind info into the [`CompiledFunction`]. fn emit_unwind_info( &self, - compiled_function: &mut CompiledFunction, + compiled_function: &mut CompiledFunction, ) -> Result<(), CompileError> { let kind = match self.isa.triple().operating_system { target_lexicon::OperatingSystem::Windows => UnwindInfoKind::Windows, @@ -217,9 +224,7 @@ impl wasmtime_environ::Compiler for Compiler { let mut ret = Vec::with_capacity(funcs.len()); for (i, (sym, func)) in funcs.iter().enumerate() { - let func = func - .downcast_ref::>() - .unwrap(); + let func = func.downcast_ref::().unwrap(); let (sym, range) = builder.append_func(&sym, func, |idx| resolve_reloc(i, idx)); traps.push(range.clone(), &func.traps().collect::>()); @@ -265,7 +270,7 @@ impl wasmtime_environ::Compiler for Compiler { #[cfg(feature = "component-model")] fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler { - todo!() + self.trampolines.component_compiler() } fn append_dwarf(