Path: blob/main/crates/environ/src/compile/mod.rs
1693 views
//! A `Compilation` contains the compiled function bodies for a WebAssembly1//! module.23use crate::prelude::*;4use crate::{5DefinedFuncIndex, FlagValue, FunctionLoc, ObjectKind, PrimaryMap, StaticModuleIndex, TripleExt,6WasmError, WasmFuncType,7};8use crate::{Tunables, obj};9use anyhow::Result;10use object::write::{Object, SymbolId};11use object::{Architecture, BinaryFormat, FileFlags};12use std::any::Any;13use std::borrow::Cow;14use std::fmt;15use std::path;16use std::sync::Arc;1718mod address_map;19mod key;20mod module_artifacts;21mod module_environ;22mod module_types;23mod stack_maps;24mod trap_encoding;2526pub use self::address_map::*;27pub use self::key::*;28pub use self::module_artifacts::*;29pub use self::module_environ::*;30pub use self::module_types::*;31pub use self::stack_maps::*;32pub use self::trap_encoding::*;3334/// An error while compiling WebAssembly to machine code.35#[derive(Debug)]36pub enum CompileError {37/// A wasm translation error occurred.38Wasm(WasmError),3940/// A compilation error occurred.41Codegen(String),4243/// A compilation error occurred.44DebugInfoNotSupported,45}4647impl fmt::Display for CompileError {48fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {49match self {50CompileError::Wasm(_) => write!(f, "WebAssembly translation error"),51CompileError::Codegen(s) => write!(f, "Compilation error: {s}"),52CompileError::DebugInfoNotSupported => {53write!(f, "Debug info is not supported with this configuration")54}55}56}57}5859impl From<WasmError> for CompileError {60fn from(err: WasmError) -> CompileError {61CompileError::Wasm(err)62}63}6465impl core::error::Error for CompileError {66fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {67match self {68CompileError::Wasm(e) => Some(e),69_ => None,70}71}72}7374/// Implementation of an incremental compilation's key/value cache store.75///76/// In theory, this could just be Cranelift's `CacheKvStore` trait, but it is not as we want to77/// make sure that wasmtime isn't too tied to Cranelift internals (and as a matter of fact, we78/// can't depend on the Cranelift trait here).79pub trait CacheStore: Send + Sync + std::fmt::Debug {80/// Try to retrieve an arbitrary cache key entry, and returns a reference to bytes that were81/// inserted via `Self::insert` before.82fn get(&self, key: &[u8]) -> Option<Cow<'_, [u8]>>;8384/// Given an arbitrary key and bytes, stores them in the cache.85///86/// Returns false when insertion in the cache failed.87fn insert(&self, key: &[u8], value: Vec<u8>) -> bool;88}8990/// Abstract trait representing the ability to create a `Compiler` below.91///92/// This is used in Wasmtime to separate compiler implementations, currently93/// mostly used to separate Cranelift from Wasmtime itself.94pub trait CompilerBuilder: Send + Sync + fmt::Debug {95/// Sets the target of compilation to the target specified.96fn target(&mut self, target: target_lexicon::Triple) -> Result<()>;9798/// Enables clif output in the directory specified.99fn clif_dir(&mut self, _path: &path::Path) -> Result<()> {100anyhow::bail!("clif output not supported");101}102103/// Returns the currently configured target triple that compilation will104/// produce artifacts for.105fn triple(&self) -> &target_lexicon::Triple;106107/// Compiler-specific method to configure various settings in the compiler108/// itself.109///110/// This is expected to be defined per-compiler. Compilers should return111/// errors for unknown names/values.112fn set(&mut self, name: &str, val: &str) -> Result<()>;113114/// Compiler-specific method for configuring settings.115///116/// Same as [`CompilerBuilder::set`] except for enabling boolean flags.117/// Currently cranelift uses this to sometimes enable a family of settings.118fn enable(&mut self, name: &str) -> Result<()>;119120/// Returns a list of all possible settings that can be configured with121/// [`CompilerBuilder::set`] and [`CompilerBuilder::enable`].122fn settings(&self) -> Vec<Setting>;123124/// Enables Cranelift's incremental compilation cache, using the given `CacheStore`125/// implementation.126///127/// This will return an error if the compiler does not support incremental compilation.128fn enable_incremental_compilation(&mut self, cache_store: Arc<dyn CacheStore>) -> Result<()>;129130/// Set the tunables for this compiler.131fn set_tunables(&mut self, tunables: Tunables) -> Result<()>;132133/// Builds a new [`Compiler`] object from this configuration.134fn build(&self) -> Result<Box<dyn Compiler>>;135136/// Enables or disables wmemcheck during runtime according to the wmemcheck CLI flag.137fn wmemcheck(&mut self, _enable: bool) {}138}139140/// Description of compiler settings returned by [`CompilerBuilder::settings`].141#[derive(Clone, Copy, Debug)]142pub struct Setting {143/// The name of the setting.144pub name: &'static str,145/// The description of the setting.146pub description: &'static str,147/// The kind of the setting.148pub kind: SettingKind,149/// The supported values of the setting (for enum values).150pub values: Option<&'static [&'static str]>,151}152153/// Different kinds of [`Setting`] values that can be configured in a154/// [`CompilerBuilder`]155#[derive(Clone, Copy, Debug)]156pub enum SettingKind {157/// The setting is an enumeration, meaning it's one of a set of values.158Enum,159/// The setting is a number.160Num,161/// The setting is a boolean.162Bool,163/// The setting is a preset.164Preset,165}166167/// The result of compiling a single function body.168pub struct CompiledFunctionBody {169/// The code. This is whatever type the `Compiler` implementation wants it170/// to be, we just shepherd it around.171pub code: Box<dyn Any + Send + Sync>,172/// Whether the compiled function needs a GC heap to run; that is, whether173/// it reads a struct field, allocates, an array, or etc...174pub needs_gc_heap: bool,175}176177/// An implementation of a compiler which can compile WebAssembly functions to178/// machine code and perform other miscellaneous tasks needed by the JIT runtime.179///180/// The diagram below depicts typical usage of this trait:181///182/// ```text183/// +------+184/// | Wasm |185/// +------+186/// |187/// |188/// Compiler::compile_function()189/// |190/// |191/// V192/// +----------------------+193/// | CompiledFunctionBody |194/// +----------------------+195/// | |196/// | |197/// | When198/// | Compiler::inlining_compiler()199/// | is some200/// | |201/// When |202/// Compiler::inlining_compiler() |-----------------.203/// is none | |204/// | | |205/// | Optionally call |206/// | InliningCompiler::inline() |207/// | | |208/// | | |209/// | |-----------------'210/// | |211/// | |212/// | V213/// | InliningCompiler::finish_compiling()214/// | |215/// | |216/// |------------------'217/// |218/// |219/// Compiler::append_code()220/// |221/// |222/// V223/// +--------+224/// | Object |225/// +--------+226/// ```227pub trait Compiler: Send + Sync {228/// Get this compiler's inliner.229///230/// Consumers of this trait **must** check for when when this method returns231/// `Some(_)`, and **must** call `InliningCompiler::finish_compiling` on all232/// `CompiledFunctionBody`s produced by this compiler in that case before233/// passing the the compiled functions to `Compiler::append_code`, even if234/// the consumer does not actually intend to do any inlining. This allows235/// implementations of the trait to only translate to an internal236/// representation in `Compiler::compile_*` methods so that they can then237/// perform inlining afterwards if the consumer desires, and then finally238/// proceed with compilng that internal representation to native code in239/// `InliningCompiler::finish_compiling`.240fn inlining_compiler(&self) -> Option<&dyn InliningCompiler>;241242/// Compiles the function `index` within `translation`.243///244/// The body of the function is available in `data` and configuration245/// values are also passed in via `tunables`. Type information in246/// `translation` is all relative to `types`.247fn compile_function(248&self,249translation: &ModuleTranslation<'_>,250key: FuncKey,251data: FunctionBodyData<'_>,252types: &ModuleTypesBuilder,253symbol: &str,254) -> Result<CompiledFunctionBody, CompileError>;255256/// Compile a trampoline for an array-call host function caller calling the257/// `index`th Wasm function.258///259/// The trampoline should save the necessary state to record the260/// host-to-Wasm transition (e.g. registers used for fast stack walking).261fn compile_array_to_wasm_trampoline(262&self,263translation: &ModuleTranslation<'_>,264types: &ModuleTypesBuilder,265key: FuncKey,266symbol: &str,267) -> Result<CompiledFunctionBody, CompileError>;268269/// Compile a trampoline for a Wasm caller calling a array callee with the270/// given signature.271///272/// The trampoline should save the necessary state to record the273/// Wasm-to-host transition (e.g. registers used for fast stack walking).274fn compile_wasm_to_array_trampoline(275&self,276wasm_func_ty: &WasmFuncType,277key: FuncKey,278symbol: &str,279) -> Result<CompiledFunctionBody, CompileError>;280281/// Creates a trampoline that can be used to call Wasmtime's implementation282/// of the builtin function specified by `index`.283///284/// The trampoline created can technically have any ABI but currently has285/// the native ABI. This will then perform all the necessary duties of an286/// exit trampoline from wasm and then perform the actual dispatch to the287/// builtin function. Builtin functions in Wasmtime are stored in an array288/// in all `VMContext` pointers, so the call to the host is an indirect289/// call.290fn compile_wasm_to_builtin(291&self,292key: FuncKey,293symbol: &str,294) -> Result<CompiledFunctionBody, CompileError>;295296/// Returns the list of relocations required for a function from one of the297/// previous `compile_*` functions above.298fn compiled_function_relocation_targets<'a>(299&'a self,300func: &'a dyn Any,301) -> Box<dyn Iterator<Item = FuncKey> + 'a>;302303/// Appends a list of compiled functions to an in-memory object.304///305/// This function will receive the same `Box<dyn Any>` produced as part of306/// compilation from functions like `compile_function`,307/// `compile_host_to_wasm_trampoline`, and other component-related shims.308/// Internally this will take all of these functions and add information to309/// the object such as:310///311/// * Compiled code in a `.text` section312/// * Unwind information in Wasmtime-specific sections313/// * Relocations, if necessary, for the text section314///315/// Each function is accompanied with its desired symbol name and the return316/// value of this function is the symbol for each function as well as where317/// each function was placed within the object.318///319/// The `resolve_reloc` argument is intended to resolving relocations320/// between function, chiefly resolving intra-module calls within one core321/// wasm module. The closure here takes two arguments:322///323/// 1. First, the index within `funcs` that is being resolved,324///325/// 2. and next the `RelocationTarget` which is the relocation target to326/// resolve.327///328/// The return value is an index within `funcs` that the relocation points329/// to.330fn append_code(331&self,332obj: &mut Object<'static>,333funcs: &[(String, Box<dyn Any + Send + Sync>)],334resolve_reloc: &dyn Fn(usize, FuncKey) -> usize,335) -> Result<Vec<(SymbolId, FunctionLoc)>>;336337/// Creates a new `Object` file which is used to build the results of a338/// compilation into.339///340/// The returned object file will have an appropriate341/// architecture/endianness for `self.triple()`, but at this time it is342/// always an ELF file, regardless of target platform.343fn object(&self, kind: ObjectKind) -> Result<Object<'static>> {344use target_lexicon::Architecture::*;345346let triple = self.triple();347let (arch, flags) = match triple.architecture {348X86_32(_) => (Architecture::I386, 0),349X86_64 => (Architecture::X86_64, 0),350Arm(_) => (Architecture::Arm, 0),351Aarch64(_) => (Architecture::Aarch64, 0),352S390x => (Architecture::S390x, 0),353Riscv64(_) => (Architecture::Riscv64, 0),354// XXX: the `object` crate won't successfully build an object355// with relocations and such if it doesn't know the356// architecture, so just pretend we are riscv64. Yolo!357//358// Also note that we add some flags to `e_flags` in the object file359// to indicate that it's pulley, not actually riscv64. This is used360// by `wasmtime objdump` for example.361Pulley32 | Pulley32be => (Architecture::Riscv64, obj::EF_WASMTIME_PULLEY32),362Pulley64 | Pulley64be => (Architecture::Riscv64, obj::EF_WASMTIME_PULLEY64),363architecture => {364anyhow::bail!("target architecture {:?} is unsupported", architecture,);365}366};367let mut obj = Object::new(368BinaryFormat::Elf,369arch,370match triple.endianness().unwrap() {371target_lexicon::Endianness::Little => object::Endianness::Little,372target_lexicon::Endianness::Big => object::Endianness::Big,373},374);375obj.flags = FileFlags::Elf {376os_abi: obj::ELFOSABI_WASMTIME,377e_flags: flags378| match kind {379ObjectKind::Module => obj::EF_WASMTIME_MODULE,380ObjectKind::Component => obj::EF_WASMTIME_COMPONENT,381},382abi_version: 0,383};384Ok(obj)385}386387/// Returns the target triple that this compiler is compiling for.388fn triple(&self) -> &target_lexicon::Triple;389390/// Returns the alignment necessary to align values to the page size of the391/// compilation target. Note that this may be an upper-bound where the392/// alignment is larger than necessary for some platforms since it may393/// depend on the platform's runtime configuration.394fn page_size_align(&self) -> u64 {395// Conservatively assume the max-of-all-supported-hosts for pulley396// and round up to 64k.397if self.triple().is_pulley() {398return 0x10000;399}400401use target_lexicon::*;402match (self.triple().operating_system, self.triple().architecture) {403(404OperatingSystem::MacOSX { .. }405| OperatingSystem::Darwin(_)406| OperatingSystem::IOS(_)407| OperatingSystem::TvOS(_),408Architecture::Aarch64(..),409) => 0x4000,410// 64 KB is the maximal page size (i.e. memory translation granule size)411// supported by the architecture and is used on some platforms.412(_, Architecture::Aarch64(..)) => 0x10000,413_ => 0x1000,414}415}416417/// Returns a list of configured settings for this compiler.418fn flags(&self) -> Vec<(&'static str, FlagValue<'static>)>;419420/// Same as [`Compiler::flags`], but ISA-specific (a cranelift-ism)421fn isa_flags(&self) -> Vec<(&'static str, FlagValue<'static>)>;422423/// Get a flag indicating whether branch protection is enabled.424fn is_branch_protection_enabled(&self) -> bool;425426/// Returns a suitable compiler usable for component-related compilations.427///428/// Note that the `ComponentCompiler` trait can also be implemented for429/// `Self` in which case this function would simply return `self`.430#[cfg(feature = "component-model")]431fn component_compiler(&self) -> &dyn crate::component::ComponentCompiler;432433/// Appends generated DWARF sections to the `obj` specified.434///435/// The `translations` track all compiled functions and `get_func` can be436/// used to acquire the metadata for a particular function within a module.437fn append_dwarf<'a>(438&self,439obj: &mut Object<'_>,440translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,441get_func: &'a dyn Fn(442StaticModuleIndex,443DefinedFuncIndex,444) -> (SymbolId, &'a (dyn Any + Send + Sync)),445dwarf_package_bytes: Option<&'a [u8]>,446tunables: &'a Tunables,447) -> Result<()>;448449/// Creates a new System V Common Information Entry for the ISA.450///451/// Returns `None` if the ISA does not support System V unwind information.452fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {453// By default, an ISA cannot create a System V CIE.454None455}456}457458/// An inlining compiler.459pub trait InliningCompiler: Sync + Send {460/// Enumerate the function calls that the given `func` makes.461fn calls(&self, func: &CompiledFunctionBody, calls: &mut IndexSet<FuncKey>) -> Result<()>;462463/// Get the abstract size of the given function, for the purposes of464/// inlining heuristics.465fn size(&self, func: &CompiledFunctionBody) -> u32;466467/// Process this function for inlining.468///469/// Implementations should call `get_callee` for each of their direct470/// function call sites and if `get_callee` returns `Some(_)`, they should471/// inline the given function body into that call site.472fn inline<'a>(473&self,474func: &mut CompiledFunctionBody,475get_callee: &'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>,476) -> Result<()>;477478/// Finish compiling the given function.479///480/// This method **must** be called before passing the481/// `CompiledFunctionBody`'s contents to `Compiler::append_code`, even if no482/// inlining was performed.483fn finish_compiling(484&self,485func: &mut CompiledFunctionBody,486input: Option<wasmparser::FunctionBody<'_>>,487symbol: &str,488) -> Result<()>;489}490491492