Path: blob/main/crates/environ/src/compile/mod.rs
3089 views
//! A `Compilation` contains the compiled function bodies for a WebAssembly1//! module.23use crate::error::Result;4use crate::prelude::*;5use crate::{6DefinedFuncIndex, FlagValue, FuncKey, FunctionLoc, ObjectKind, PrimaryMap, StaticModuleIndex,7TripleExt, Tunables, WasmError, WasmFuncType, obj,8};9use object::write::{Object, SymbolId};10use object::{Architecture, BinaryFormat, FileFlags};11use std::any::Any;12use std::borrow::Cow;13use std::fmt;14use std::path;15use std::sync::Arc;1617mod address_map;18mod frame_table;19mod module_artifacts;20mod module_environ;21mod module_types;22mod stack_maps;23mod trap_encoding;2425pub use self::address_map::*;26pub use self::frame_table::*;27pub use self::module_artifacts::*;28pub use self::module_environ::*;29pub use self::module_types::*;30pub use self::stack_maps::*;31pub use self::trap_encoding::*;3233/// An error while compiling WebAssembly to machine code.34#[derive(Debug)]35pub enum CompileError {36/// A wasm translation error occurred.37Wasm(WasmError),3839/// A compilation error occurred.40Codegen(String),4142/// A compilation error occurred.43DebugInfoNotSupported,44}4546impl fmt::Display for CompileError {47fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {48match self {49CompileError::Wasm(_) => write!(f, "WebAssembly translation error"),50CompileError::Codegen(s) => write!(f, "Compilation error: {s}"),51CompileError::DebugInfoNotSupported => {52write!(f, "Debug info is not supported with this configuration")53}54}55}56}5758impl From<WasmError> for CompileError {59fn from(err: WasmError) -> CompileError {60CompileError::Wasm(err)61}62}6364impl core::error::Error for CompileError {65fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {66match self {67CompileError::Wasm(e) => Some(e),68_ => None,69}70}71}7273/// Implementation of an incremental compilation's key/value cache store.74///75/// In theory, this could just be Cranelift's `CacheKvStore` trait, but it is not as we want to76/// make sure that wasmtime isn't too tied to Cranelift internals (and as a matter of fact, we77/// can't depend on the Cranelift trait here).78pub trait CacheStore: Send + Sync + std::fmt::Debug {79/// Try to retrieve an arbitrary cache key entry, and returns a reference to bytes that were80/// inserted via `Self::insert` before.81fn get(&self, key: &[u8]) -> Option<Cow<'_, [u8]>>;8283/// Given an arbitrary key and bytes, stores them in the cache.84///85/// Returns false when insertion in the cache failed.86fn insert(&self, key: &[u8], value: Vec<u8>) -> bool;87}8889/// Abstract trait representing the ability to create a `Compiler` below.90///91/// This is used in Wasmtime to separate compiler implementations, currently92/// mostly used to separate Cranelift from Wasmtime itself.93pub trait CompilerBuilder: Send + Sync + fmt::Debug {94/// Sets the target of compilation to the target specified.95fn target(&mut self, target: target_lexicon::Triple) -> Result<()>;9697/// Enables clif output in the directory specified.98fn clif_dir(&mut self, _path: &path::Path) -> Result<()> {99bail!("clif output not supported");100}101102/// Returns the currently configured target triple that compilation will103/// produce artifacts for.104fn triple(&self) -> &target_lexicon::Triple;105106/// Compiler-specific method to configure various settings in the compiler107/// itself.108///109/// This is expected to be defined per-compiler. Compilers should return110/// errors for unknown names/values.111fn set(&mut self, name: &str, val: &str) -> Result<()>;112113/// Compiler-specific method for configuring settings.114///115/// Same as [`CompilerBuilder::set`] except for enabling boolean flags.116/// Currently cranelift uses this to sometimes enable a family of settings.117fn enable(&mut self, name: &str) -> Result<()>;118119/// Returns a list of all possible settings that can be configured with120/// [`CompilerBuilder::set`] and [`CompilerBuilder::enable`].121fn settings(&self) -> Vec<Setting>;122123/// Enables Cranelift's incremental compilation cache, using the given `CacheStore`124/// implementation.125///126/// This will return an error if the compiler does not support incremental compilation.127fn enable_incremental_compilation(&mut self, cache_store: Arc<dyn CacheStore>) -> Result<()>;128129/// Set the tunables for this compiler.130fn set_tunables(&mut self, tunables: Tunables) -> Result<()>;131132/// Get the tunables used by this compiler.133fn tunables(&self) -> Option<&Tunables>;134135/// Builds a new [`Compiler`] object from this configuration.136fn build(&self) -> Result<Box<dyn Compiler>>;137138/// Enables or disables wmemcheck during runtime according to the wmemcheck CLI flag.139fn wmemcheck(&mut self, _enable: bool) {}140}141142/// Description of compiler settings returned by [`CompilerBuilder::settings`].143#[derive(Clone, Copy, Debug)]144pub struct Setting {145/// The name of the setting.146pub name: &'static str,147/// The description of the setting.148pub description: &'static str,149/// The kind of the setting.150pub kind: SettingKind,151/// The supported values of the setting (for enum values).152pub values: Option<&'static [&'static str]>,153}154155/// Different kinds of [`Setting`] values that can be configured in a156/// [`CompilerBuilder`]157#[derive(Clone, Copy, Debug)]158pub enum SettingKind {159/// The setting is an enumeration, meaning it's one of a set of values.160Enum,161/// The setting is a number.162Num,163/// The setting is a boolean.164Bool,165/// The setting is a preset.166Preset,167}168169/// The result of compiling a single function body.170pub struct CompiledFunctionBody {171/// The code. This is whatever type the `Compiler` implementation wants it172/// to be, we just shepherd it around.173pub code: Box<dyn Any + Send + Sync>,174/// Whether the compiled function needs a GC heap to run; that is, whether175/// it reads a struct field, allocates, an array, or etc...176pub needs_gc_heap: bool,177}178179/// An implementation of a compiler which can compile WebAssembly functions to180/// machine code and perform other miscellaneous tasks needed by the JIT runtime.181///182/// The diagram below depicts typical usage of this trait:183///184/// ```text185/// +------+186/// | Wasm |187/// +------+188/// |189/// |190/// Compiler::compile_function()191/// |192/// |193/// V194/// +----------------------+195/// | CompiledFunctionBody |196/// +----------------------+197/// | |198/// | |199/// | When200/// | Compiler::inlining_compiler()201/// | is some202/// | |203/// When |204/// Compiler::inlining_compiler() |-----------------.205/// is none | |206/// | | |207/// | Optionally call |208/// | InliningCompiler::inline() |209/// | | |210/// | | |211/// | |-----------------'212/// | |213/// | |214/// | V215/// | InliningCompiler::finish_compiling()216/// | |217/// | |218/// |------------------'219/// |220/// |221/// Compiler::append_code()222/// |223/// |224/// V225/// +--------+226/// | Object |227/// +--------+228/// ```229pub trait Compiler: Send + Sync {230/// Get this compiler's inliner.231///232/// Consumers of this trait **must** check for when when this method returns233/// `Some(_)`, and **must** call `InliningCompiler::finish_compiling` on all234/// `CompiledFunctionBody`s produced by this compiler in that case before235/// passing the the compiled functions to `Compiler::append_code`, even if236/// the consumer does not actually intend to do any inlining. This allows237/// implementations of the trait to only translate to an internal238/// representation in `Compiler::compile_*` methods so that they can then239/// perform inlining afterwards if the consumer desires, and then finally240/// proceed with compilng that internal representation to native code in241/// `InliningCompiler::finish_compiling`.242fn inlining_compiler(&self) -> Option<&dyn InliningCompiler>;243244/// Compiles the function `index` within `translation`.245///246/// The body of the function is available in `data` and configuration247/// values are also passed in via `tunables`. Type information in248/// `translation` is all relative to `types`.249fn compile_function(250&self,251translation: &ModuleTranslation<'_>,252key: FuncKey,253data: FunctionBodyData<'_>,254types: &ModuleTypesBuilder,255symbol: &str,256) -> Result<CompiledFunctionBody, CompileError>;257258/// Compile a trampoline for an array-call host function caller calling the259/// `index`th Wasm function.260///261/// The trampoline should save the necessary state to record the262/// host-to-Wasm transition (e.g. registers used for fast stack walking).263fn compile_array_to_wasm_trampoline(264&self,265translation: &ModuleTranslation<'_>,266types: &ModuleTypesBuilder,267key: FuncKey,268symbol: &str,269) -> Result<CompiledFunctionBody, CompileError>;270271/// Compile a trampoline for a Wasm caller calling a array callee with the272/// given signature.273///274/// The trampoline should save the necessary state to record the275/// Wasm-to-host transition (e.g. registers used for fast stack walking).276fn compile_wasm_to_array_trampoline(277&self,278wasm_func_ty: &WasmFuncType,279key: FuncKey,280symbol: &str,281) -> Result<CompiledFunctionBody, CompileError>;282283/// Creates a trampoline that can be used to call Wasmtime's implementation284/// of the builtin function specified by `index`.285///286/// The trampoline created can technically have any ABI but currently has287/// the native ABI. This will then perform all the necessary duties of an288/// exit trampoline from wasm and then perform the actual dispatch to the289/// builtin function. Builtin functions in Wasmtime are stored in an array290/// in all `VMContext` pointers, so the call to the host is an indirect291/// call.292fn compile_wasm_to_builtin(293&self,294key: FuncKey,295symbol: &str,296) -> Result<CompiledFunctionBody, CompileError>;297298/// Returns the list of relocations required for a function from one of the299/// previous `compile_*` functions above.300fn compiled_function_relocation_targets<'a>(301&'a self,302func: &'a dyn Any,303) -> Box<dyn Iterator<Item = FuncKey> + 'a>;304305/// Appends a list of compiled functions to an in-memory object.306///307/// This function will receive the same `Box<dyn Any>` produced as part of308/// compilation from functions like `compile_function`,309/// `compile_host_to_wasm_trampoline`, and other component-related shims.310/// Internally this will take all of these functions and add information to311/// the object such as:312///313/// * Compiled code in a `.text` section314/// * Unwind information in Wasmtime-specific sections315/// * Relocations, if necessary, for the text section316///317/// Each function is accompanied with its desired symbol name and the return318/// value of this function is the symbol for each function as well as where319/// each function was placed within the object.320///321/// The `resolve_reloc` argument is intended to resolving relocations322/// between function, chiefly resolving intra-module calls within one core323/// wasm module. The closure here takes two arguments:324///325/// 1. First, the index within `funcs` that is being resolved,326///327/// 2. and next the `RelocationTarget` which is the relocation target to328/// resolve.329///330/// The return value is an index within `funcs` that the relocation points331/// to.332fn append_code(333&self,334obj: &mut Object<'static>,335funcs: &[(String, FuncKey, Box<dyn Any + Send + Sync>)],336resolve_reloc: &dyn Fn(usize, FuncKey) -> usize,337) -> Result<Vec<(SymbolId, FunctionLoc)>>;338339/// Creates a new `Object` file which is used to build the results of a340/// compilation into.341///342/// The returned object file will have an appropriate343/// architecture/endianness for `self.triple()`, but at this time it is344/// always an ELF file, regardless of target platform.345fn object(&self, kind: ObjectKind) -> Result<Object<'static>> {346use target_lexicon::Architecture::*;347348let triple = self.triple();349let (arch, flags) = match triple.architecture {350X86_32(_) => (Architecture::I386, 0),351X86_64 => (Architecture::X86_64, 0),352Arm(_) => (Architecture::Arm, 0),353Aarch64(_) => (Architecture::Aarch64, 0),354S390x => (Architecture::S390x, 0),355Riscv64(_) => (Architecture::Riscv64, 0),356// XXX: the `object` crate won't successfully build an object357// with relocations and such if it doesn't know the358// architecture, so just pretend we are riscv64. Yolo!359//360// Also note that we add some flags to `e_flags` in the object file361// to indicate that it's pulley, not actually riscv64. This is used362// by `wasmtime objdump` for example.363Pulley32 | Pulley32be => (Architecture::Riscv64, obj::EF_WASMTIME_PULLEY32),364Pulley64 | Pulley64be => (Architecture::Riscv64, obj::EF_WASMTIME_PULLEY64),365architecture => {366bail!("target architecture {architecture:?} is unsupported");367}368};369let mut obj = Object::new(370BinaryFormat::Elf,371arch,372match triple.endianness().unwrap() {373target_lexicon::Endianness::Little => object::Endianness::Little,374target_lexicon::Endianness::Big => object::Endianness::Big,375},376);377obj.flags = FileFlags::Elf {378os_abi: obj::ELFOSABI_WASMTIME,379e_flags: flags380| match kind {381ObjectKind::Module => obj::EF_WASMTIME_MODULE,382ObjectKind::Component => obj::EF_WASMTIME_COMPONENT,383},384abi_version: 0,385};386Ok(obj)387}388389/// Returns the target triple that this compiler is compiling for.390fn triple(&self) -> &target_lexicon::Triple;391392/// Returns the alignment necessary to align values to the page size of the393/// compilation target. Note that this may be an upper-bound where the394/// alignment is larger than necessary for some platforms since it may395/// depend on the platform's runtime configuration.396fn page_size_align(&self) -> u64 {397// Conservatively assume the max-of-all-supported-hosts for pulley398// and round up to 64k.399if self.triple().is_pulley() {400return 0x10000;401}402403use target_lexicon::*;404match (self.triple().operating_system, self.triple().architecture) {405(406OperatingSystem::MacOSX { .. }407| OperatingSystem::Darwin(_)408| OperatingSystem::IOS(_)409| OperatingSystem::TvOS(_),410Architecture::Aarch64(..),411) => 0x4000,412// 64 KB is the maximal page size (i.e. memory translation granule size)413// supported by the architecture and is used on some platforms.414(_, Architecture::Aarch64(..)) => 0x10000,415_ => 0x1000,416}417}418419/// Returns a list of configured settings for this compiler.420fn flags(&self) -> Vec<(&'static str, FlagValue<'static>)>;421422/// Same as [`Compiler::flags`], but ISA-specific (a cranelift-ism)423fn isa_flags(&self) -> Vec<(&'static str, FlagValue<'static>)>;424425/// Get a flag indicating whether branch protection is enabled.426fn is_branch_protection_enabled(&self) -> bool;427428/// Returns a suitable compiler usable for component-related compilations.429///430/// Note that the `ComponentCompiler` trait can also be implemented for431/// `Self` in which case this function would simply return `self`.432#[cfg(feature = "component-model")]433fn component_compiler(&self) -> &dyn crate::component::ComponentCompiler;434435/// Appends generated DWARF sections to the `obj` specified.436///437/// The `translations` track all compiled functions and `get_func` can be438/// used to acquire the metadata for a particular function within a module.439fn append_dwarf<'a>(440&self,441obj: &mut Object<'_>,442translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,443get_func: &'a dyn Fn(444StaticModuleIndex,445DefinedFuncIndex,446) -> (SymbolId, &'a (dyn Any + Send + Sync)),447dwarf_package_bytes: Option<&'a [u8]>,448tunables: &'a Tunables,449) -> Result<()>;450451/// Creates a new System V Common Information Entry for the ISA.452///453/// Returns `None` if the ISA does not support System V unwind information.454fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {455// By default, an ISA cannot create a System V CIE.456None457}458}459460/// An inlining compiler.461pub trait InliningCompiler: Sync + Send {462/// Enumerate the function calls that the given `func` makes.463fn calls(&self, func: &CompiledFunctionBody, calls: &mut IndexSet<FuncKey>) -> Result<()>;464465/// Get the abstract size of the given function, for the purposes of466/// inlining heuristics.467fn size(&self, func: &CompiledFunctionBody) -> u32;468469/// Process this function for inlining.470///471/// Implementations should call `get_callee` for each of their direct472/// function call sites and if `get_callee` returns `Some(_)`, they should473/// inline the given function body into that call site.474fn inline<'a>(475&self,476func: &mut CompiledFunctionBody,477get_callee: &'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>,478) -> Result<()>;479480/// Finish compiling the given function.481///482/// This method **must** be called before passing the483/// `CompiledFunctionBody`'s contents to `Compiler::append_code`, even if no484/// inlining was performed.485fn finish_compiling(486&self,487func: &mut CompiledFunctionBody,488input: Option<wasmparser::FunctionBody<'_>>,489symbol: &str,490) -> Result<()>;491}492493494