From fccbf74a6bb9dbf111d56af3de7a3eb0a19293de Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Tue, 19 Dec 2023 13:57:17 +0000 Subject: [PATCH] add runtime feature --- cranelift/native/src/lib.rs | 1 + crates/cranelift-shared/src/isa_builder.rs | 9 +- crates/cranelift/Cargo.toml | 6 +- crates/cranelift/src/builder.rs | 9 +- crates/wasmtime/Cargo.toml | 16 +- crates/wasmtime/src/compile.rs | 295 ++++++ crates/wasmtime/src/component_artifacts.rs | 37 + crates/wasmtime/src/config.rs | 39 +- crates/wasmtime/src/engine.rs | 858 +++++------------- crates/wasmtime/src/lib.rs | 126 ++- crates/wasmtime/src/profiling.rs | 17 +- crates/wasmtime/src/runtime.rs | 34 + crates/wasmtime/src/{ => runtime}/code.rs | 0 .../wasmtime/src/{ => runtime}/code_memory.rs | 0 .../src/{ => runtime}/component/component.rs | 119 +-- .../src/{ => runtime}/component/func.rs | 0 .../src/{ => runtime}/component/func/host.rs | 0 .../{ => runtime}/component/func/options.rs | 0 .../src/{ => runtime}/component/func/typed.rs | 0 .../src/{ => runtime}/component/instance.rs | 0 .../src/{ => runtime}/component/linker.rs | 0 .../src/{ => runtime}/component/matching.rs | 0 .../src/{ => runtime}/component/mod.rs | 0 .../src/{ => runtime}/component/resources.rs | 0 .../src/{ => runtime}/component/storage.rs | 0 .../src/{ => runtime}/component/store.rs | 0 .../src/{ => runtime}/component/types.rs | 0 .../src/{ => runtime}/component/values.rs | 0 .../wasmtime/src/{ => runtime}/externals.rs | 0 .../src/{ => runtime}/externals/global.rs | 0 .../src/{ => runtime}/externals/table.rs | 0 crates/wasmtime/src/{ => runtime}/func.rs | 0 .../wasmtime/src/{ => runtime}/func/typed.rs | 0 crates/wasmtime/src/{ => runtime}/instance.rs | 0 .../wasmtime/src/{ => runtime}/instantiate.rs | 4 +- crates/wasmtime/src/{ => runtime}/linker.rs | 0 crates/wasmtime/src/{ => runtime}/memory.rs | 0 crates/wasmtime/src/{ => runtime}/module.rs | 80 +- .../src/{ => runtime}/module/registry.rs | 0 crates/wasmtime/src/{ => runtime}/ref.rs | 0 crates/wasmtime/src/runtime/runtime_engine.rs | 596 ++++++++++++ .../runtime_engine}/serialization.rs | 296 +----- .../wasmtime/src/{ => runtime}/signatures.rs | 0 crates/wasmtime/src/{ => runtime}/store.rs | 0 .../src/{ => runtime}/store/context.rs | 0 .../wasmtime/src/{ => runtime}/store/data.rs | 0 .../src/{ => runtime}/store/func_refs.rs | 0 .../wasmtime/src/{ => runtime}/trampoline.rs | 0 .../src/{ => runtime}/trampoline/func.rs | 4 +- .../src/{ => runtime}/trampoline/global.rs | 0 .../src/{ => runtime}/trampoline/memory.rs | 0 .../src/{ => runtime}/trampoline/table.rs | 0 crates/wasmtime/src/{ => runtime}/trap.rs | 0 crates/wasmtime/src/{ => runtime}/types.rs | 0 .../src/{ => runtime}/types/matching.rs | 0 crates/wasmtime/src/{ => runtime}/unix.rs | 0 crates/wasmtime/src/{ => runtime}/unwind.rs | 0 .../wasmtime/src/{ => runtime}/unwind/miri.rs | 0 .../src/{ => runtime}/unwind/systemv.rs | 0 .../src/{ => runtime}/unwind/winx64.rs | 0 crates/wasmtime/src/{ => runtime}/v128.rs | 0 crates/wasmtime/src/{ => runtime}/values.rs | 0 crates/wasmtime/src/{ => runtime}/windows.rs | 0 crates/winch/src/builder.rs | 9 +- src/commands/settings.rs | 2 +- 65 files changed, 1329 insertions(+), 1228 deletions(-) create mode 100644 crates/wasmtime/src/compile.rs create mode 100644 crates/wasmtime/src/component_artifacts.rs create mode 100644 crates/wasmtime/src/runtime.rs rename crates/wasmtime/src/{ => runtime}/code.rs (100%) rename crates/wasmtime/src/{ => runtime}/code_memory.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/component.rs (77%) rename crates/wasmtime/src/{ => runtime}/component/func.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/func/host.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/func/options.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/func/typed.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/instance.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/linker.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/matching.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/mod.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/resources.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/storage.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/store.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/types.rs (100%) rename crates/wasmtime/src/{ => runtime}/component/values.rs (100%) rename crates/wasmtime/src/{ => runtime}/externals.rs (100%) rename crates/wasmtime/src/{ => runtime}/externals/global.rs (100%) rename crates/wasmtime/src/{ => runtime}/externals/table.rs (100%) rename crates/wasmtime/src/{ => runtime}/func.rs (100%) rename crates/wasmtime/src/{ => runtime}/func/typed.rs (100%) rename crates/wasmtime/src/{ => runtime}/instance.rs (100%) rename crates/wasmtime/src/{ => runtime}/instantiate.rs (99%) rename crates/wasmtime/src/{ => runtime}/linker.rs (100%) rename crates/wasmtime/src/{ => runtime}/memory.rs (100%) rename crates/wasmtime/src/{ => runtime}/module.rs (93%) rename crates/wasmtime/src/{ => runtime}/module/registry.rs (100%) rename crates/wasmtime/src/{ => runtime}/ref.rs (100%) create mode 100644 crates/wasmtime/src/runtime/runtime_engine.rs rename crates/wasmtime/src/{engine => runtime/runtime_engine}/serialization.rs (59%) rename crates/wasmtime/src/{ => runtime}/signatures.rs (100%) rename crates/wasmtime/src/{ => runtime}/store.rs (100%) rename crates/wasmtime/src/{ => runtime}/store/context.rs (100%) rename crates/wasmtime/src/{ => runtime}/store/data.rs (100%) rename crates/wasmtime/src/{ => runtime}/store/func_refs.rs (100%) rename crates/wasmtime/src/{ => runtime}/trampoline.rs (100%) rename crates/wasmtime/src/{ => runtime}/trampoline/func.rs (98%) rename crates/wasmtime/src/{ => runtime}/trampoline/global.rs (100%) rename crates/wasmtime/src/{ => runtime}/trampoline/memory.rs (100%) rename crates/wasmtime/src/{ => runtime}/trampoline/table.rs (100%) rename crates/wasmtime/src/{ => runtime}/trap.rs (100%) rename crates/wasmtime/src/{ => runtime}/types.rs (100%) rename crates/wasmtime/src/{ => runtime}/types/matching.rs (100%) rename crates/wasmtime/src/{ => runtime}/unix.rs (100%) rename crates/wasmtime/src/{ => runtime}/unwind.rs (100%) rename crates/wasmtime/src/{ => runtime}/unwind/miri.rs (100%) rename crates/wasmtime/src/{ => runtime}/unwind/systemv.rs (100%) rename crates/wasmtime/src/{ => runtime}/unwind/winx64.rs (100%) rename crates/wasmtime/src/{ => runtime}/v128.rs (100%) rename crates/wasmtime/src/{ => runtime}/values.rs (100%) rename crates/wasmtime/src/{ => runtime}/windows.rs (100%) diff --git a/cranelift/native/src/lib.rs b/cranelift/native/src/lib.rs index 42d20a326191..510b6c57ab51 100644 --- a/cranelift/native/src/lib.rs +++ b/cranelift/native/src/lib.rs @@ -150,6 +150,7 @@ pub fn infer_native_flags(isa_builder: &mut dyn Configurable) -> Result<(), &'st // the cpuinfo interface, so we can't rely on it being present for now. let _ = riscv::cpuinfo_detect(isa_builder); } + let _ = isa_builder; Ok(()) } diff --git a/crates/cranelift-shared/src/isa_builder.rs b/crates/cranelift-shared/src/isa_builder.rs index ad16bb8e8591..94c241be22fe 100644 --- a/crates/cranelift-shared/src/isa_builder.rs +++ b/crates/cranelift-shared/src/isa_builder.rs @@ -20,7 +20,7 @@ pub struct IsaBuilder { impl IsaBuilder { /// Create a new ISA builder with the given lookup function. - pub fn new(lookup: fn(Triple) -> Result>) -> Self { + pub fn new(triple: Option, lookup: fn(Triple) -> Result>) -> Result { let mut flags = settings::builder(); // We don't use probestack as a stack limit mechanism @@ -28,14 +28,15 @@ impl IsaBuilder { .set("enable_probestack", "false") .expect("should be valid flag"); - let mut isa_flags = lookup(Triple::host()).expect("host machine is not a supported target"); + let triple = triple.unwrap_or_else(Triple::host); + let mut isa_flags = lookup(triple)?; cranelift_native::infer_native_flags(&mut isa_flags).unwrap(); - Self { + Ok(Self { shared_flags: flags, inner: isa_flags, lookup, - } + }) } pub fn triple(&self) -> &target_lexicon::Triple { diff --git a/crates/cranelift/Cargo.toml b/crates/cranelift/Cargo.toml index 1ef91e7f5e7d..e7323284f077 100644 --- a/crates/cranelift/Cargo.toml +++ b/crates/cranelift/Cargo.toml @@ -15,7 +15,10 @@ anyhow = { workspace = true } log = { workspace = true } wasmtime-environ = { workspace = true } cranelift-wasm = { workspace = true } -cranelift-codegen = { workspace = true, features = ["default"] } +cranelift-codegen = { workspace = true, default-features = false, features = [ + "std", + "unwind", +] } cranelift-frontend = { workspace = true } cranelift-entity = { workspace = true } cranelift-native = { workspace = true } @@ -31,6 +34,7 @@ wasmtime-versioned-export-macros = { workspace = true } [features] all-arch = ["cranelift-codegen/all-arch"] +host-arch = ["cranelift-codegen/host-arch"] component-model = ["wasmtime-environ/component-model"] incremental-cache = ["cranelift-codegen/incremental-cache"] wmemcheck = [] diff --git a/crates/cranelift/src/builder.rs b/crates/cranelift/src/builder.rs index b2c86107cb7c..026960dd4097 100644 --- a/crates/cranelift/src/builder.rs +++ b/crates/cranelift/src/builder.rs @@ -11,6 +11,7 @@ use cranelift_codegen::{ use std::fmt; use std::path; use std::sync::Arc; +use target_lexicon::Triple; use wasmtime_cranelift_shared::isa_builder::IsaBuilder; use wasmtime_environ::{CacheStore, CompilerBuilder, Setting, Tunables}; @@ -36,15 +37,15 @@ pub struct LinkOptions { pub force_jump_veneers: bool, } -pub fn builder() -> Box { - Box::new(Builder { +pub fn builder(triple: Option) -> Result> { + Ok(Box::new(Builder { tunables: Tunables::default(), - inner: IsaBuilder::new(|triple| isa::lookup(triple).map_err(|e| e.into())), + inner: IsaBuilder::new(triple, |triple| isa::lookup(triple).map_err(|e| e.into()))?, linkopts: LinkOptions::default(), cache_store: None, clif_dir: None, wmemcheck: false, - }) + })) } impl CompilerBuilder for Builder { diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 655959cc16f3..e6c18dcfefb2 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -18,7 +18,7 @@ rustdoc-args = ["--cfg", "nightlydoc"] features = ["component-model"] [dependencies] -wasmtime-runtime = { workspace = true } +wasmtime-runtime = { workspace = true, optional = true } wasmtime-environ = { workspace = true } wasmtime-jit-debug = { workspace = true, features = [ "perf_jitdump", @@ -96,6 +96,8 @@ default = [ 'addr2line', 'coredump', 'debug-builtins', + 'runtime', + 'host-arch', ] # An on-by-default feature enabling runtime compilation of WebAssembly modules @@ -135,13 +137,15 @@ async = [ ] # Enables support for the pooling instance allocation strategy -pooling-allocator = ["wasmtime-runtime/pooling-allocator"] +pooling-allocator = ["runtime", "wasmtime-runtime/pooling-allocator"] # Enables support for all architectures in Cranelift, allowing # cross-compilation using the `wasmtime` crate's API, notably the # `Engine::precompile_module` function. all-arch = ["wasmtime-cranelift?/all-arch", "wasmtime-winch?/all-arch"] +host-arch = ["wasmtime-cranelift?/host-arch"] + # Enables in-progress support for the component model. Note that this feature is # in-progress, buggy, and incomplete. This is primarily here for internal # testing purposes. @@ -149,13 +153,13 @@ component-model = [ "wasmtime-environ/component-model", "wasmtime-cranelift?/component-model", "wasmtime-winch?/component-model", - "wasmtime-runtime/component-model", + "wasmtime-runtime?/component-model", "dep:wasmtime-component-macro", "dep:wasmtime-component-util", "dep:encoding_rs", ] -wmemcheck = ["wasmtime-runtime/wmemcheck", "wasmtime-cranelift?/wmemcheck"] +wmemcheck = ["wasmtime-runtime?/wmemcheck", "wasmtime-cranelift?/wmemcheck"] # Enables support for demangling WebAssembly function names at runtime in # errors such as backtraces. @@ -166,4 +170,6 @@ coredump = ["dep:wasm-encoder"] # Export some symbols from the final binary to assist in debugging # Cranelift-generated code with native debuggers like GDB and LLDB. -debug-builtins = ["wasmtime-runtime/debug-builtins"] +debug-builtins = ["wasmtime-runtime?/debug-builtins"] + +runtime = ["dep:wasmtime-runtime"] diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs new file mode 100644 index 000000000000..e5a843514871 --- /dev/null +++ b/crates/wasmtime/src/compile.rs @@ -0,0 +1,295 @@ +use std::mem; + +use anyhow::{Context, Result}; +use object::write::{Object, StandardSegment}; +use object::SectionKind; +#[cfg(feature = "component-model")] +use wasmtime_environ::component::Translator; +use wasmtime_environ::{ + obj, CompiledModuleInfo, FinishedObject, ModuleEnvironment, ModuleTypes, ObjectKind, +}; + +use crate::compiler::CompileInputs; +#[cfg(feature = "component-model")] +use crate::component_artifacts::{CompiledComponentInfo, ComponentArtifacts}; +use crate::{Engine, Metadata, ModuleVersionStrategy, WasmFeatures, VERSION}; + +/// Converts an input binary-encoded WebAssembly module to compilation +/// artifacts and type information. +/// +/// This is where compilation actually happens of WebAssembly modules and +/// translation/parsing/validation of the binary input occurs. The binary +/// artifact represented in the `MmapVec` returned here is an in-memory ELF +/// file in an owned area of virtual linear memory where permissions (such +/// as the executable bit) can be applied. +/// +/// Additionally compilation returns an `Option` here which is always +/// `Some`, notably compiled metadata about the module in addition to the +/// type information found within. +pub(crate) fn build_artifacts( + engine: &Engine, + wasm: &[u8], +) -> Result<(T, Option<(CompiledModuleInfo, ModuleTypes)>)> { + let tunables = &engine.config().tunables; + + // First a `ModuleEnvironment` is created which records type information + // about the wasm module. This is where the WebAssembly is parsed and + // validated. Afterwards `types` will have all the type information for + // this module. + let mut validator = wasmparser::Validator::new_with_features(engine.config().features.clone()); + let parser = wasmparser::Parser::new(0); + let mut types = Default::default(); + let mut translation = ModuleEnvironment::new(tunables, &mut validator, &mut types) + .translate(parser, wasm) + .context("failed to parse WebAssembly module")?; + let functions = mem::take(&mut translation.function_body_inputs); + let types = types.finish(); + + let compile_inputs = CompileInputs::for_module(&types, &translation, functions); + let unlinked_compile_outputs = compile_inputs.compile(engine)?; + let (compiled_funcs, function_indices) = unlinked_compile_outputs.pre_link(); + + // Emplace all compiled functions into the object file with any other + // sections associated with code as well. + let mut object = engine.compiler().object(ObjectKind::Module)?; + // Insert `Engine` and type-level information into the compiled + // artifact so if this module is deserialized later it contains all + // information necessary. + // + // Note that `append_compiler_info` and `append_types` here in theory + // can both be skipped if this module will never get serialized. + // They're only used during deserialization and not during runtime for + // the module itself. Currently there's no need for that, however, so + // it's left as an exercise for later. + engine.append_compiler_info(&mut object); + engine.append_bti(&mut object); + + let (mut object, compilation_artifacts) = function_indices.link_and_append_code( + object, + engine, + compiled_funcs, + std::iter::once(translation).collect(), + )?; + + let info = compilation_artifacts.unwrap_as_module_info(); + object.serialize_info(&(&info, &types)); + let result = T::finish_object(object)?; + + Ok((result, Some((info, types)))) +} + +/// Performs the compilation phase for a component, translating and +/// validating the provided wasm binary to machine code. +/// +/// This method will compile all nested core wasm binaries in addition to +/// any necessary extra functions required for operation with components. +/// The output artifact here is the serialized object file contained within +/// an owned mmap along with metadata about the compilation itself. +#[cfg(feature = "component-model")] +pub(crate) fn build_component_artifacts( + engine: &Engine, + binary: &[u8], +) -> Result<(T, ComponentArtifacts)> { + use wasmtime_environ::ScopeVec; + + let tunables = &engine.config().tunables; + let compiler = engine.compiler(); + + let scope = ScopeVec::new(); + let mut validator = wasmparser::Validator::new_with_features(engine.config().features.clone()); + let mut types = Default::default(); + let (component, mut module_translations) = + Translator::new(tunables, &mut validator, &mut types, &scope) + .translate(binary) + .context("failed to parse WebAssembly module")?; + let types = types.finish(); + + let compile_inputs = CompileInputs::for_component( + &types, + &component, + module_translations.iter_mut().map(|(i, translation)| { + let functions = mem::take(&mut translation.function_body_inputs); + (i, &*translation, functions) + }), + ); + let unlinked_compile_outputs = compile_inputs.compile(&engine)?; + let (compiled_funcs, function_indices) = unlinked_compile_outputs.pre_link(); + + let mut object = compiler.object(ObjectKind::Component)?; + engine.append_compiler_info(&mut object); + engine.append_bti(&mut object); + + let (mut object, compilation_artifacts) = function_indices.link_and_append_code( + object, + engine, + compiled_funcs, + module_translations, + )?; + + let info = CompiledComponentInfo { + component: component.component, + trampolines: compilation_artifacts.trampolines, + resource_drop_wasm_to_native_trampoline: compilation_artifacts + .resource_drop_wasm_to_native_trampoline, + }; + let artifacts = ComponentArtifacts { + info, + types, + static_modules: compilation_artifacts.modules, + }; + object.serialize_info(&artifacts); + + let result = T::finish_object(object)?; + Ok((result, artifacts)) +} + +impl Engine { + pub(crate) fn compiler(&self) -> &dyn wasmtime_environ::Compiler { + &*self.inner.compiler + } + + pub(crate) fn append_compiler_info(&self, obj: &mut Object<'_>) { + append_compiler_info(self, obj); + } + + pub(crate) fn append_bti(&self, obj: &mut Object<'_>) { + let section = obj.add_section( + obj.segment_name(StandardSegment::Data).to_vec(), + obj::ELF_WASM_BTI.as_bytes().to_vec(), + SectionKind::ReadOnlyData, + ); + let contents = if self.compiler().is_branch_protection_enabled() { + 1 + } else { + 0 + }; + obj.append_section_data(section, &[contents], 1); + } + + /// Ahead-of-time (AOT) compiles a WebAssembly module. + /// + /// The `bytes` provided must be in one of two formats: + /// + /// * A [binary-encoded][binary] WebAssembly module. This is always supported. + /// * A [text-encoded][text] instance of the WebAssembly text format. + /// This is only supported when the `wat` feature of this crate is enabled. + /// If this is supplied then the text format will be parsed before validation. + /// Note that the `wat` feature is enabled by default. + /// + /// This method may be used to compile a module for use with a different target + /// host. The output of this method may be used with + /// [`Module::deserialize`](crate::Module::deserialize) on hosts compatible + /// with the [`Config`] associated with this [`Engine`]. + /// + /// The output of this method is safe to send to another host machine for later + /// execution. As the output is already a compiled module, translation and code + /// generation will be skipped and this will improve the performance of constructing + /// a [`Module`](crate::Module) from the output of this method. + /// + /// [binary]: https://webassembly.github.io/spec/core/binary/index.html + /// [text]: https://webassembly.github.io/spec/core/text/index.html + #[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] + pub fn precompile_module(&self, bytes: &[u8]) -> Result> { + #[cfg(feature = "wat")] + let bytes = wat::parse_bytes(&bytes)?; + let (v, _) = crate::compile::build_artifacts::>(self, &bytes)?; + Ok(v) + } + + /// Same as [`Engine::precompile_module`] except for a + /// [`Component`](crate::component::Component) + #[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] + #[cfg(feature = "component-model")] + #[cfg_attr(nightlydoc, doc(cfg(feature = "component-model")))] + pub fn precompile_component(&self, bytes: &[u8]) -> Result> { + #[cfg(feature = "wat")] + let bytes = wat::parse_bytes(&bytes)?; + let (v, _) = crate::compile::build_component_artifacts::>(self, &bytes)?; + Ok(v) + } +} + +/// Produces a blob of bytes by serializing the `engine`'s configuration data to +/// be checked, perhaps in a different process, with the `check_compatible` +/// method below. +/// +/// The blob of bytes is inserted into the object file specified to become part +/// of the final compiled artifact. +pub fn append_compiler_info(engine: &Engine, obj: &mut Object<'_>) { + let section = obj.add_section( + obj.segment_name(StandardSegment::Data).to_vec(), + obj::ELF_WASM_ENGINE.as_bytes().to_vec(), + SectionKind::ReadOnlyData, + ); + let mut data = Vec::new(); + data.push(VERSION); + let version = match &engine.config().module_version { + ModuleVersionStrategy::WasmtimeVersion => env!("CARGO_PKG_VERSION"), + ModuleVersionStrategy::Custom(c) => c, + ModuleVersionStrategy::None => "", + }; + // This precondition is checked in Config::module_version: + assert!( + version.len() < 256, + "package version must be less than 256 bytes" + ); + data.push(version.len() as u8); + data.extend_from_slice(version.as_bytes()); + bincode::serialize_into(&mut data, &Metadata::new(engine)).unwrap(); + obj.set_section_data(section, data, 1); +} + +impl Metadata<'_> { + pub(crate) fn new(engine: &Engine) -> Metadata<'static> { + let wasmparser::WasmFeatures { + reference_types, + multi_value, + bulk_memory, + component_model, + simd, + threads, + tail_call, + multi_memory, + exceptions, + memory64, + relaxed_simd, + extended_const, + memory_control, + function_references, + gc, + component_model_values, + + // Always on; we don't currently have knobs for these. + mutable_global: _, + saturating_float_to_int: _, + sign_extension: _, + floats: _, + } = engine.config().features; + + assert!(!memory_control); + assert!(!gc); + assert!(!component_model_values); + + Metadata { + target: engine.compiler().triple().to_string(), + shared_flags: engine.compiler().flags(), + isa_flags: engine.compiler().isa_flags(), + tunables: engine.config().tunables.clone(), + features: WasmFeatures { + reference_types, + multi_value, + bulk_memory, + component_model, + simd, + threads, + tail_call, + multi_memory, + exceptions, + memory64, + relaxed_simd, + extended_const, + function_references, + }, + } + } +} diff --git a/crates/wasmtime/src/component_artifacts.rs b/crates/wasmtime/src/component_artifacts.rs new file mode 100644 index 000000000000..4151cb033600 --- /dev/null +++ b/crates/wasmtime/src/component_artifacts.rs @@ -0,0 +1,37 @@ +#![cfg(feature = "component-model")] + +use serde_derive::{Deserialize, Serialize}; +use wasmtime_environ::{ + component::{AllCallFunc, ComponentTypes, TrampolineIndex}, + CompiledModuleInfo, FunctionLoc, PrimaryMap, StaticModuleIndex, +}; + +#[derive(Serialize, Deserialize)] +pub(crate) struct ComponentArtifacts { + pub(crate) info: CompiledComponentInfo, + pub(crate) types: ComponentTypes, + pub(crate) static_modules: PrimaryMap, +} + +#[derive(Serialize, Deserialize)] +pub(crate) struct CompiledComponentInfo { + /// Type information calculated during translation about this component. + pub(crate) component: wasmtime_environ::component::Component, + + /// Where lowered function trampolines are located within the `text` + /// section of `code_memory`. + /// + /// These are the + /// + /// 1. Wasm-call, + /// 2. array-call, and + /// 3. native-call + /// + /// function pointers that end up in a `VMFuncRef` for each + /// lowering. + pub(crate) trampolines: PrimaryMap>, + + /// The location of the wasm-to-native trampoline for the `resource.drop` + /// intrinsic. + pub(crate) resource_drop_wasm_to_native_trampoline: Option, +} diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 4afcce98fb88..8ab21ccccbd4 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1,6 +1,3 @@ -use crate::memory::MemoryCreator; -use crate::profiling::{self, ProfilingAgent}; -use crate::trampoline::MemoryCreatorProxy; use anyhow::{bail, ensure, Result}; use serde_derive::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; @@ -14,7 +11,15 @@ use wasmparser::WasmFeatures; #[cfg(feature = "cache")] use wasmtime_cache::CacheConfig; use wasmtime_environ::Tunables; -use wasmtime_runtime::{mpk, InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator}; + +#[cfg(feature = "runtime")] +use crate::memory::MemoryCreator; +#[cfg(feature = "runtime")] +use crate::profiling::{self, ProfilingAgent}; +#[cfg(feature = "runtime")] +use crate::trampoline::MemoryCreatorProxy; +#[cfg(feature = "runtime")] +use wasmtime_runtime::{InstanceAllocator, OnDemandInstanceAllocator, RuntimeMemoryCreator}; #[cfg(feature = "async")] use crate::stack::{StackCreator, StackCreatorProxy}; @@ -22,7 +27,8 @@ use crate::stack::{StackCreator, StackCreatorProxy}; use wasmtime_fiber::RuntimeFiberStackCreator; pub use wasmtime_environ::CacheStore; -pub use wasmtime_runtime::MpkEnabled; +#[cfg(feature = "pooling-allocator")] +pub use wasmtime_runtime::{mpk, MpkEnabled}; /// Represents the module instance allocation strategy to use. #[derive(Clone)] @@ -101,6 +107,7 @@ pub struct Config { pub(crate) tunables: Tunables, #[cfg(feature = "cache")] pub(crate) cache_config: CacheConfig, + #[cfg(feature = "runtime")] pub(crate) mem_creator: Option>, pub(crate) allocation_strategy: InstanceAllocationStrategy, pub(crate) max_wasm_stack: usize, @@ -189,6 +196,7 @@ impl Config { #[cfg(feature = "cache")] cache_config: CacheConfig::new_cache_disabled(), profiling_strategy: ProfilingStrategy::None, + #[cfg(feature = "runtime")] mem_creator: None, allocation_strategy: InstanceAllocationStrategy::OnDemand, // 512k of stack -- note that this is chosen currently to not be too @@ -1102,6 +1110,7 @@ impl Config { /// /// Custom memory creators are used when creating host `Memory` objects or when /// creating instance linear memories for the on-demand instance allocation strategy. + #[cfg(feature = "runtime")] pub fn with_host_memory(&mut self, mem_creator: Arc) -> &mut Self { self.mem_creator = Some(Arc::new(MemoryCreatorProxy(mem_creator))); self @@ -1601,6 +1610,7 @@ impl Config { Ok(()) } + #[cfg(feature = "runtime")] pub(crate) fn build_allocator(&self) -> Result> { #[cfg(feature = "async")] let stack_size = self.async_stack_size; @@ -1633,6 +1643,7 @@ impl Config { } } + #[cfg(feature = "runtime")] pub(crate) fn build_profiler(&self) -> Result> { Ok(match self.profiling_strategy { ProfilingStrategy::PerfMap => profiling::new_perfmap()?, @@ -1644,25 +1655,23 @@ impl Config { #[cfg(any(feature = "cranelift", feature = "winch"))] pub(crate) fn build_compiler(mut self) -> Result<(Self, Box)> { + let target = self.compiler_config.target.clone(); + let mut compiler = match self.compiler_config.strategy { #[cfg(feature = "cranelift")] - Strategy::Auto => wasmtime_cranelift::builder(), + Strategy::Auto => wasmtime_cranelift::builder(target)?, #[cfg(all(feature = "winch", not(feature = "cranelift")))] - Strategy::Auto => wasmtime_winch::builder(), + Strategy::Auto => wasmtime_winch::builder(target)?, #[cfg(feature = "cranelift")] - Strategy::Cranelift => wasmtime_cranelift::builder(), + Strategy::Cranelift => wasmtime_cranelift::builder(target)?, #[cfg(not(feature = "cranelift"))] Strategy::Cranelift => bail!("cranelift support not compiled in"), #[cfg(feature = "winch")] - Strategy::Winch => wasmtime_winch::builder(), + Strategy::Winch => wasmtime_winch::builder(target)?, #[cfg(not(feature = "winch"))] Strategy::Winch => bail!("winch support not compiled in"), }; - if let Some(target) = &self.compiler_config.target { - compiler.target(target.clone())?; - } - if let Some(path) = &self.compiler_config.clif_dir { compiler.clif_dir(path)?; } @@ -1808,7 +1817,11 @@ impl Config { } fn round_up_to_pages(val: u64) -> u64 { + #[cfg(feature = "runtime")] let page_size = wasmtime_runtime::page_size() as u64; + // TODOABK: put this in the config? + #[cfg(not(feature = "runtime"))] + let page_size = 4096 as u64; debug_assert!(page_size.is_power_of_two()); val.checked_add(page_size - 1) .map(|val| val & !(page_size - 1)) diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 82bb0dd38816..823aea433ef3 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -1,20 +1,19 @@ -use crate::{profiling::ProfilingAgent, signatures::SignatureRegistry, CodeMemory, Config}; -use anyhow::{Context, Result}; -use object::write::{Object, StandardSegment}; -use object::SectionKind; +use std::sync::{atomic::AtomicU64, Arc}; + +use anyhow::Result; use once_cell::sync::OnceCell; #[cfg(feature = "parallel-compilation")] use rayon::prelude::*; -use std::path::Path; -use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::Arc; -#[cfg(feature = "cache")] -use wasmtime_cache::CacheConfig; -use wasmtime_environ::obj; -use wasmtime_environ::{FlagValue, ObjectKind}; -use wasmtime_runtime::{CompiledModuleIdAllocator, InstanceAllocator, MmapVec}; +use serde_derive::{Deserialize, Serialize}; +use wasmtime_environ::{FlagValue, Tunables}; +#[cfg(feature = "runtime")] +use wasmtime_runtime::{CompiledModuleIdAllocator, InstanceAllocator}; + +use crate::Config; +#[cfg(feature = "runtime")] +use crate::{profiling::ProfilingAgent, runtime::signatures::SignatureRegistry}; -mod serialization; +pub(crate) const VERSION: u8 = 0; /// An `Engine` which is a global context for compilation and management of wasm /// modules. @@ -40,22 +39,27 @@ mod serialization; /// default settings. #[derive(Clone)] pub struct Engine { - inner: Arc, + pub(crate) inner: Arc, } -struct EngineInner { - config: Config, +pub(crate) struct EngineInner { + pub(crate) config: Config, #[cfg(any(feature = "cranelift", feature = "winch"))] - compiler: Box, - allocator: Box, - profiler: Box, - signatures: SignatureRegistry, - epoch: AtomicU64, - unique_id_allocator: CompiledModuleIdAllocator, - - // One-time check of whether the compiler's settings, if present, are - // compatible with the native host. - compatible_with_native_host: OnceCell>, + pub(crate) compiler: Box, + #[cfg(feature = "runtime")] + pub(crate) allocator: Box, + #[cfg(feature = "runtime")] + pub(crate) profiler: Box, + #[cfg(feature = "runtime")] + pub(crate) signatures: SignatureRegistry, + pub(crate) epoch: AtomicU64, + #[cfg(feature = "runtime")] + pub(crate) unique_id_allocator: CompiledModuleIdAllocator, + + /// One-time check of whether the compiler's settings, if present, are + /// compatible with the native host. + #[cfg(feature = "runtime")] + pub(crate) compatible_with_native_host: OnceCell>, } impl Engine { @@ -72,183 +76,51 @@ impl Engine { /// to `true`, but explicitly disable these two compiler settings /// will cause errors. pub fn new(config: &Config) -> Result { - // Ensure that wasmtime_runtime's signal handlers are configured. This - // is the per-program initialization required for handling traps, such - // as configuring signals, vectored exception handlers, etc. - wasmtime_runtime::init_traps(crate::module::is_wasm_trap_pc, config.macos_use_mach_ports); - #[cfg(feature = "debug-builtins")] - wasmtime_runtime::debug_builtins::ensure_exported(); - - let registry = SignatureRegistry::new(); + #[cfg(feature = "runtime")] + { + // Ensure that wasmtime_runtime's signal handlers are configured. This + // is the per-program initialization required for handling traps, such + // as configuring signals, vectored exception handlers, etc. + wasmtime_runtime::init_traps( + crate::module::is_wasm_trap_pc, + config.macos_use_mach_ports, + ); + #[cfg(feature = "debug-builtins")] + wasmtime_runtime::debug_builtins::ensure_exported(); + } + let config = config.clone(); config.validate()?; #[cfg(any(feature = "cranelift", feature = "winch"))] let (config, compiler) = config.build_compiler()?; - let allocator = config.build_allocator()?; - let profiler = config.build_profiler()?; - Ok(Engine { inner: Arc::new(EngineInner { #[cfg(any(feature = "cranelift", feature = "winch"))] compiler, - config, - allocator, - profiler, - signatures: registry, + #[cfg(feature = "runtime")] + allocator: config.build_allocator()?, + #[cfg(feature = "runtime")] + profiler: config.build_profiler()?, + #[cfg(feature = "runtime")] + signatures: SignatureRegistry::new(), epoch: AtomicU64::new(0), + #[cfg(feature = "runtime")] unique_id_allocator: CompiledModuleIdAllocator::new(), + #[cfg(feature = "runtime")] compatible_with_native_host: OnceCell::new(), + config, }), }) } - /// Eagerly initialize thread-local functionality shared by all [`Engine`]s. - /// - /// Wasmtime's implementation on some platforms may involve per-thread - /// setup that needs to happen whenever WebAssembly is invoked. This setup - /// can take on the order of a few hundred microseconds, whereas the - /// overhead of calling WebAssembly is otherwise on the order of a few - /// nanoseconds. This setup cost is paid once per-OS-thread. If your - /// application is sensitive to the latencies of WebAssembly function - /// calls, even those that happen first on a thread, then this function - /// can be used to improve the consistency of each call into WebAssembly - /// by explicitly frontloading the cost of the one-time setup per-thread. - /// - /// Note that this function is not required to be called in any embedding. - /// Wasmtime will automatically initialize thread-local-state as necessary - /// on calls into WebAssembly. This is provided for use cases where the - /// latency of WebAssembly calls are extra-important, which is not - /// necessarily true of all embeddings. - pub fn tls_eager_initialize() { - wasmtime_runtime::tls_eager_initialize(); - } - /// Returns the configuration settings that this engine is using. #[inline] pub fn config(&self) -> &Config { &self.inner.config } - #[cfg(any(feature = "cranelift", feature = "winch"))] - pub(crate) fn compiler(&self) -> &dyn wasmtime_environ::Compiler { - &*self.inner.compiler - } - - pub(crate) fn allocator(&self) -> &dyn InstanceAllocator { - self.inner.allocator.as_ref() - } - - pub(crate) fn profiler(&self) -> &dyn ProfilingAgent { - self.inner.profiler.as_ref() - } - - #[cfg(feature = "cache")] - pub(crate) fn cache_config(&self) -> &CacheConfig { - &self.config().cache_config - } - - /// Returns whether the engine `a` and `b` refer to the same configuration. - pub fn same(a: &Engine, b: &Engine) -> bool { - Arc::ptr_eq(&a.inner, &b.inner) - } - - pub(crate) fn signatures(&self) -> &SignatureRegistry { - &self.inner.signatures - } - - pub(crate) fn epoch_counter(&self) -> &AtomicU64 { - &self.inner.epoch - } - - pub(crate) fn current_epoch(&self) -> u64 { - self.epoch_counter().load(Ordering::Relaxed) - } - - /// Increments the epoch. - /// - /// When using epoch-based interruption, currently-executing Wasm - /// code within this engine will trap or yield "soon" when the - /// epoch deadline is reached or exceeded. (The configuration, and - /// the deadline, are set on the `Store`.) The intent of the - /// design is for this method to be called by the embedder at some - /// regular cadence, for example by a thread that wakes up at some - /// interval, or by a signal handler. - /// - /// See [`Config::epoch_interruption`](crate::Config::epoch_interruption) - /// for an introduction to epoch-based interruption and pointers - /// to the other relevant methods. - /// - /// ## Signal Safety - /// - /// This method is signal-safe: it does not make any syscalls, and - /// performs only an atomic increment to the epoch value in - /// memory. - pub fn increment_epoch(&self) { - self.inner.epoch.fetch_add(1, Ordering::Relaxed); - } - - pub(crate) fn unique_id_allocator(&self) -> &CompiledModuleIdAllocator { - &self.inner.unique_id_allocator - } - - /// Ahead-of-time (AOT) compiles a WebAssembly module. - /// - /// The `bytes` provided must be in one of two formats: - /// - /// * A [binary-encoded][binary] WebAssembly module. This is always supported. - /// * A [text-encoded][text] instance of the WebAssembly text format. - /// This is only supported when the `wat` feature of this crate is enabled. - /// If this is supplied then the text format will be parsed before validation. - /// Note that the `wat` feature is enabled by default. - /// - /// This method may be used to compile a module for use with a different target - /// host. The output of this method may be used with - /// [`Module::deserialize`](crate::Module::deserialize) on hosts compatible - /// with the [`Config`] associated with this [`Engine`]. - /// - /// The output of this method is safe to send to another host machine for later - /// execution. As the output is already a compiled module, translation and code - /// generation will be skipped and this will improve the performance of constructing - /// a [`Module`](crate::Module) from the output of this method. - /// - /// [binary]: https://webassembly.github.io/spec/core/binary/index.html - /// [text]: https://webassembly.github.io/spec/core/text/index.html - #[cfg(any(feature = "cranelift", feature = "winch"))] - #[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] - pub fn precompile_module(&self, bytes: &[u8]) -> Result> { - #[cfg(feature = "wat")] - let bytes = wat::parse_bytes(&bytes)?; - let (mmap, _) = crate::Module::build_artifacts(self, &bytes)?; - Ok(mmap.to_vec()) - } - - /// Same as [`Engine::precompile_module`] except for a - /// [`Component`](crate::component::Component) - #[cfg(any(feature = "cranelift", feature = "winch"))] - #[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] - #[cfg(feature = "component-model")] - #[cfg_attr(nightlydoc, doc(cfg(feature = "component-model")))] - pub fn precompile_component(&self, bytes: &[u8]) -> Result> { - #[cfg(feature = "wat")] - let bytes = wat::parse_bytes(&bytes)?; - let (mmap, _) = crate::component::Component::build_artifacts(self, &bytes)?; - Ok(mmap.to_vec()) - } - - /// Returns a [`std::hash::Hash`] that can be used to check precompiled WebAssembly compatibility. - /// - /// The outputs of [`Engine::precompile_module`] and [`Engine::precompile_component`] - /// are compatible with a different [`Engine`] instance only if the two engines use - /// compatible [`Config`]s. If this Hash matches between two [`Engine`]s then binaries - /// from one are guaranteed to deserialize in the other. - #[cfg(any(feature = "cranelift", feature = "winch"))] - #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs - pub fn precompile_compatibility_hash(&self) -> impl std::hash::Hash + '_ { - crate::module::HashedEngineCompileEnv(self) - } - pub(crate) fn run_maybe_parallel< A: Send, B: Send, @@ -274,516 +146,202 @@ impl Engine { .map(|a| f(a)) .collect::, E>>() } +} - /// Executes `f1` and `f2` in parallel if parallel compilation is enabled at - /// both runtime and compile time, otherwise runs them synchronously. - #[allow(dead_code)] // only used for the component-model feature right now - pub(crate) fn join_maybe_parallel( - &self, - f1: impl FnOnce() -> T + Send, - f2: impl FnOnce() -> U + Send, - ) -> (T, U) - where - T: Send, - U: Send, - { - if self.config().parallel_compilation { - #[cfg(feature = "parallel-compilation")] - return rayon::join(f1, f2); - } - (f1(), f2()) - } - - /// Returns the target triple which this engine is compiling code for - /// and/or running code for. - pub(crate) fn target(&self) -> target_lexicon::Triple { - // If a compiler is configured, use that target. - #[cfg(any(feature = "cranelift", feature = "winch"))] - return self.compiler().triple().clone(); +#[derive(Serialize, Deserialize)] +pub(crate) struct Metadata<'a> { + pub(crate) target: String, + #[serde(borrow)] + pub(crate) shared_flags: Vec<(&'a str, FlagValue<'a>)>, + #[serde(borrow)] + pub(crate) isa_flags: Vec<(&'a str, FlagValue<'a>)>, + pub(crate) tunables: Tunables, + pub(crate) features: WasmFeatures, +} - // ... otherwise it's the native target - #[cfg(not(any(feature = "cranelift", feature = "winch")))] - return target_lexicon::Triple::host(); - } +// This exists because `wasmparser::WasmFeatures` isn't serializable +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub(crate) struct WasmFeatures { + pub(crate) reference_types: bool, + pub(crate) multi_value: bool, + pub(crate) bulk_memory: bool, + pub(crate) component_model: bool, + pub(crate) simd: bool, + pub(crate) tail_call: bool, + pub(crate) threads: bool, + pub(crate) multi_memory: bool, + pub(crate) exceptions: bool, + pub(crate) memory64: bool, + pub(crate) relaxed_simd: bool, + pub(crate) extended_const: bool, + pub(crate) function_references: bool, +} - /// Verify that this engine's configuration is compatible with loading - /// modules onto the native host platform. - /// - /// This method is used as part of `Module::new` to ensure that this - /// engine can indeed load modules for the configured compiler (if any). - /// Note that if cranelift is disabled this trivially returns `Ok` because - /// loaded serialized modules are checked separately. - pub(crate) fn check_compatible_with_native_host(&self) -> Result<()> { - self.inner - .compatible_with_native_host - .get_or_init(|| self._check_compatible_with_native_host()) - .clone() - .map_err(anyhow::Error::msg) - } +#[cfg(test)] +mod test { + use super::*; + use crate::Config; - fn _check_compatible_with_native_host(&self) -> Result<(), String> { - #[cfg(any(feature = "cranelift", feature = "winch"))] - { - let compiler = self.compiler(); - - // Check to see that the config's target matches the host - let target = compiler.triple(); - if *target != target_lexicon::Triple::host() { - return Err(format!( - "target '{}' specified in the configuration does not match the host", - target - )); - } - - // Also double-check all compiler settings - for (key, value) in compiler.flags().iter() { - self.check_compatible_with_shared_flag(key, value)?; - } - for (key, value) in compiler.isa_flags().iter() { - self.check_compatible_with_isa_flag(key, value)?; - } + #[test] + fn test_architecture_mismatch() -> Result<()> { + let engine = Engine::default(); + let mut metadata = Metadata::new(&engine); + metadata.target = "unknown-generic-linux".to_string(); + + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert_eq!( + e.to_string(), + "Module was compiled for architecture 'unknown'", + ), } - Ok(()) - } - /// Checks to see whether the "shared flag", something enabled for - /// individual compilers, is compatible with the native host platform. - /// - /// This is used both when validating an engine's compilation settings are - /// compatible with the host as well as when deserializing modules from - /// disk to ensure they're compatible with the current host. - /// - /// Note that most of the settings here are not configured by users that - /// often. While theoretically possible via `Config` methods the more - /// interesting flags are the ISA ones below. Typically the values here - /// represent global configuration for wasm features. Settings here - /// currently rely on the compiler informing us of all settings, including - /// those disabled. Settings then fall in a few buckets: - /// - /// * Some settings must be enabled, such as `preserve_frame_pointers`. - /// * Some settings must have a particular value, such as - /// `libcall_call_conv`. - /// * Some settings do not matter as to their value, such as `opt_level`. - pub(crate) fn check_compatible_with_shared_flag( - &self, - flag: &str, - value: &FlagValue, - ) -> Result<(), String> { - let target = self.target(); - let ok = match flag { - // These settings must all have be enabled, since their value - // can affect the way the generated code performs or behaves at - // runtime. - "libcall_call_conv" => *value == FlagValue::Enum("isa_default".into()), - "preserve_frame_pointers" => *value == FlagValue::Bool(true), - "enable_probestack" => *value == FlagValue::Bool(crate::config::probestack_supported(target.architecture)), - "probestack_strategy" => *value == FlagValue::Enum("inline".into()), - - // Features wasmtime doesn't use should all be disabled, since - // otherwise if they are enabled it could change the behavior of - // generated code. - "enable_llvm_abi_extensions" => *value == FlagValue::Bool(false), - "enable_pinned_reg" => *value == FlagValue::Bool(false), - "use_colocated_libcalls" => *value == FlagValue::Bool(false), - "use_pinned_reg_as_heap_base" => *value == FlagValue::Bool(false), - - // If reference types are enabled this must be enabled, otherwise - // this setting can have any value. - "enable_safepoints" => { - if self.config().features.reference_types { - *value == FlagValue::Bool(true) - } else { - return Ok(()) - } - } - - // Windows requires unwind info as part of its ABI. - "unwind_info" => { - if target.operating_system == target_lexicon::OperatingSystem::Windows { - *value == FlagValue::Bool(true) - } else { - return Ok(()) - } - } - - // These settings don't affect the interface or functionality of - // the module itself, so their configuration values shouldn't - // matter. - "enable_heap_access_spectre_mitigation" - | "enable_table_access_spectre_mitigation" - | "enable_nan_canonicalization" - | "enable_jump_tables" - | "enable_float" - | "enable_verifier" - | "enable_pcc" - | "regalloc_checker" - | "regalloc_verbose_logs" - | "is_pic" - | "bb_padding_log2_minus_one" - | "machine_code_cfg_info" - | "tls_model" // wasmtime doesn't use tls right now - | "opt_level" // opt level doesn't change semantics - | "enable_alias_analysis" // alias analysis-based opts don't change semantics - | "probestack_func_adjusts_sp" // probestack above asserted disabled - | "probestack_size_log2" // probestack above asserted disabled - | "regalloc" // shouldn't change semantics - | "enable_incremental_compilation_cache_checks" // shouldn't change semantics - | "enable_atomics" => return Ok(()), - - // Everything else is unknown and needs to be added somewhere to - // this list if encountered. - _ => { - return Err(format!("unknown shared setting {:?} configured to {:?}", flag, value)) - } - }; - - if !ok { - return Err(format!( - "setting {:?} is configured to {:?} which is not supported", - flag, value, - )); - } Ok(()) } - /// Same as `check_compatible_with_native_host` except used for ISA-specific - /// flags. This is used to test whether a configured ISA flag is indeed - /// available on the host platform itself. - pub(crate) fn check_compatible_with_isa_flag( - &self, - flag: &str, - value: &FlagValue, - ) -> Result<(), String> { - match value { - // ISA flags are used for things like CPU features, so if they're - // disabled then it's compatible with the native host. - FlagValue::Bool(false) => return Ok(()), - - // Fall through below where we test at runtime that features are - // available. - FlagValue::Bool(true) => {} - - // Only `bool` values are supported right now, other settings would - // need more support here. - _ => { - return Err(format!( - "isa-specific feature {:?} configured to unknown value {:?}", - flag, value - )) - } - } - - #[allow(unused_assignments)] - let mut enabled = None; - - #[cfg(target_arch = "aarch64")] - { - enabled = match flag { - "has_lse" => Some(std::arch::is_aarch64_feature_detected!("lse")), - // No effect on its own, but in order to simplify the code on a - // platform without pointer authentication support we fail if - // "has_pauth" is enabled, but "sign_return_address" is not. - "has_pauth" => Some(std::arch::is_aarch64_feature_detected!("paca")), - // No effect on its own. - "sign_return_address_all" => Some(true), - // The pointer authentication instructions act as a `NOP` when - // unsupported (but keep in mind "has_pauth" as well), so it is - // safe to enable them. - "sign_return_address" => Some(true), - // No effect on its own. - "sign_return_address_with_bkey" => Some(true), - // The `BTI` instruction acts as a `NOP` when unsupported, so it - // is safe to enable it. - "use_bti" => Some(true), - // fall through to the very bottom to indicate that support is - // not enabled to test whether this feature is enabled on the - // host. - _ => None, - }; - } - - // There is no is_s390x_feature_detected macro yet, so for now - // we use getauxval from the libc crate directly. - #[cfg(all(target_arch = "s390x", target_os = "linux"))] - { - let v = unsafe { libc::getauxval(libc::AT_HWCAP) }; - const HWCAP_S390X_VXRS_EXT2: libc::c_ulong = 32768; - - enabled = match flag { - // There is no separate HWCAP bit for mie2, so assume - // that any machine with vxrs_ext2 also has mie2. - "has_vxrs_ext2" | "has_mie2" => Some((v & HWCAP_S390X_VXRS_EXT2) != 0), - // fall through to the very bottom to indicate that support is - // not enabled to test whether this feature is enabled on the - // host. - _ => None, - } - } - - #[cfg(target_arch = "riscv64")] - { - enabled = match flag { - // make sure `test_isa_flags_mismatch` test pass. - "not_a_flag" => None, - // due to `is_riscv64_feature_detected` is not stable. - // we cannot use it. - _ => Some(true), - } - } + #[test] + fn test_os_mismatch() -> Result<()> { + let engine = Engine::default(); + let mut metadata = Metadata::new(&engine); - #[cfg(target_arch = "x86_64")] - { - enabled = match flag { - "has_sse3" => Some(std::is_x86_feature_detected!("sse3")), - "has_ssse3" => Some(std::is_x86_feature_detected!("ssse3")), - "has_sse41" => Some(std::is_x86_feature_detected!("sse4.1")), - "has_sse42" => Some(std::is_x86_feature_detected!("sse4.2")), - "has_popcnt" => Some(std::is_x86_feature_detected!("popcnt")), - "has_avx" => Some(std::is_x86_feature_detected!("avx")), - "has_avx2" => Some(std::is_x86_feature_detected!("avx2")), - "has_fma" => Some(std::is_x86_feature_detected!("fma")), - "has_bmi1" => Some(std::is_x86_feature_detected!("bmi1")), - "has_bmi2" => Some(std::is_x86_feature_detected!("bmi2")), - "has_avx512bitalg" => Some(std::is_x86_feature_detected!("avx512bitalg")), - "has_avx512dq" => Some(std::is_x86_feature_detected!("avx512dq")), - "has_avx512f" => Some(std::is_x86_feature_detected!("avx512f")), - "has_avx512vl" => Some(std::is_x86_feature_detected!("avx512vl")), - "has_avx512vbmi" => Some(std::is_x86_feature_detected!("avx512vbmi")), - "has_lzcnt" => Some(std::is_x86_feature_detected!("lzcnt")), - - // fall through to the very bottom to indicate that support is - // not enabled to test whether this feature is enabled on the - // host. - _ => None, - }; - } + metadata.target = format!( + "{}-generic-unknown", + target_lexicon::Triple::host().architecture + ); - match enabled { - Some(true) => return Ok(()), - Some(false) => { - return Err(format!( - "compilation setting {:?} is enabled, but not available on the host", - flag - )) - } - // fall through - None => {} + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert_eq!( + e.to_string(), + "Module was compiled for operating system 'unknown'", + ), } - Err(format!( - "cannot test if target-specific flag {:?} is available at runtime", - flag - )) + Ok(()) } - #[cfg(any(feature = "cranelift", feature = "winch"))] - pub(crate) fn append_compiler_info(&self, obj: &mut Object<'_>) { - serialization::append_compiler_info(self, obj); - } + #[test] + fn test_cranelift_flags_mismatch() -> Result<()> { + let engine = Engine::default(); + let mut metadata = Metadata::new(&engine); + + metadata + .shared_flags + .push(("preserve_frame_pointers", FlagValue::Bool(false))); + + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert!(format!("{:?}", e).starts_with( + "\ +compilation settings of module incompatible with native host + +Caused by: + setting \"preserve_frame_pointers\" is configured to Bool(false) which is not supported" + )), + } - #[cfg(any(feature = "cranelift", feature = "winch"))] - pub(crate) fn append_bti(&self, obj: &mut Object<'_>) { - let section = obj.add_section( - obj.segment_name(StandardSegment::Data).to_vec(), - obj::ELF_WASM_BTI.as_bytes().to_vec(), - SectionKind::ReadOnlyData, - ); - let contents = if self.compiler().is_branch_protection_enabled() { - 1 - } else { - 0 - }; - obj.append_section_data(section, &[contents], 1); + Ok(()) } - /// Loads a `CodeMemory` from the specified in-memory slice, copying it to a - /// uniquely owned mmap. - /// - /// The `expected` marker here is whether the bytes are expected to be a - /// precompiled module or a component. - pub(crate) fn load_code_bytes( - &self, - bytes: &[u8], - expected: ObjectKind, - ) -> Result> { - self.load_code(MmapVec::from_slice(bytes)?, expected) - } + #[test] + fn test_isa_flags_mismatch() -> Result<()> { + let engine = Engine::default(); + let mut metadata = Metadata::new(&engine); + + metadata + .isa_flags + .push(("not_a_flag", FlagValue::Bool(true))); + + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert!(format!("{:?}", e).starts_with( + "\ +compilation settings of module incompatible with native host + +Caused by: + cannot test if target-specific flag \"not_a_flag\" is available at runtime", + )), + } - /// Like `load_code_bytes`, but creates a mmap from a file on disk. - pub(crate) fn load_code_file( - &self, - path: &Path, - expected: ObjectKind, - ) -> Result> { - self.load_code( - MmapVec::from_file(path).with_context(|| { - format!("failed to create file mapping for: {}", path.display()) - })?, - expected, - ) + Ok(()) } - pub(crate) fn load_code(&self, mmap: MmapVec, expected: ObjectKind) -> Result> { - serialization::check_compatible(self, &mmap, expected)?; - let mut code = CodeMemory::new(mmap)?; - code.publish()?; - Ok(Arc::new(code)) - } + #[test] + #[cfg_attr(miri, ignore)] + fn test_tunables_int_mismatch() -> Result<()> { + let engine = Engine::default(); + let mut metadata = Metadata::new(&engine); - /// Detects whether the bytes provided are a precompiled object produced by - /// Wasmtime. - /// - /// This function will inspect the header of `bytes` to determine if it - /// looks like a precompiled core wasm module or a precompiled component. - /// This does not validate the full structure or guarantee that - /// deserialization will succeed, instead it helps higher-levels of the - /// stack make a decision about what to do next when presented with the - /// `bytes` as an input module. - /// - /// If the `bytes` looks like a precompiled object previously produced by - /// [`Module::serialize`](crate::Module::serialize), - /// [`Component::serialize`](crate::component::Component::serialize), - /// [`Engine::precompile_module`], or [`Engine::precompile_component`], then - /// this will return `Some(...)` indicating so. Otherwise `None` is - /// returned. - pub fn detect_precompiled(&self, bytes: &[u8]) -> Option { - serialization::detect_precompiled_bytes(bytes) - } + metadata.tunables.static_memory_offset_guard_size = 0; - /// Like [`Engine::detect_precompiled`], but performs the detection on a file. - pub fn detect_precompiled_file(&self, path: impl AsRef) -> Result> { - serialization::detect_precompiled_file(path) - } -} + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert_eq!(e.to_string(), "Module was compiled with a static memory guard size of '0' but '2147483648' is expected for the host"), + } -impl Default for Engine { - fn default() -> Engine { - Engine::new(&Config::default()).unwrap() + Ok(()) } -} - -/// Return value from the [`Engine::detect_precompiled`] API. -#[derive(PartialEq, Eq, Copy, Clone, Debug)] -pub enum Precompiled { - /// The input bytes look like a precompiled core wasm module. - Module, - /// The input bytes look like a precompiled wasm component. - Component, -} -#[cfg(test)] -mod tests { - use std::{ - collections::hash_map::DefaultHasher, - hash::{Hash, Hasher}, - }; + #[test] + fn test_tunables_bool_mismatch() -> Result<()> { + let mut config = Config::new(); + config.epoch_interruption(true); + + let engine = Engine::new(&config)?; + let mut metadata = Metadata::new(&engine); + metadata.tunables.epoch_interruption = false; + + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert_eq!( + e.to_string(), + "Module was compiled without epoch interruption but it is enabled for the host" + ), + } - use crate::{Config, Engine, Module, ModuleVersionStrategy, OptLevel}; + let mut config = Config::new(); + config.epoch_interruption(false); - use anyhow::Result; - use tempfile::TempDir; + let engine = Engine::new(&config)?; + let mut metadata = Metadata::new(&engine); + metadata.tunables.epoch_interruption = true; - #[test] - #[cfg_attr(miri, ignore)] - fn cache_accounts_for_opt_level() -> Result<()> { - let td = TempDir::new()?; - let config_path = td.path().join("config.toml"); - std::fs::write( - &config_path, - &format!( - " - [cache] - enabled = true - directory = '{}' - ", - td.path().join("cache").display() + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert_eq!( + e.to_string(), + "Module was compiled with epoch interruption but it is not enabled for the host" ), - )?; - let mut cfg = Config::new(); - cfg.cranelift_opt_level(OptLevel::None) - .cache_config_load(&config_path)?; - let engine = Engine::new(&cfg)?; - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 0); - assert_eq!(engine.config().cache_config.cache_misses(), 1); - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 1); - assert_eq!(engine.config().cache_config.cache_misses(), 1); - - let mut cfg = Config::new(); - cfg.cranelift_opt_level(OptLevel::Speed) - .cache_config_load(&config_path)?; - let engine = Engine::new(&cfg)?; - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 0); - assert_eq!(engine.config().cache_config.cache_misses(), 1); - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 1); - assert_eq!(engine.config().cache_config.cache_misses(), 1); - - let mut cfg = Config::new(); - cfg.cranelift_opt_level(OptLevel::SpeedAndSize) - .cache_config_load(&config_path)?; - let engine = Engine::new(&cfg)?; - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 0); - assert_eq!(engine.config().cache_config.cache_misses(), 1); - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 1); - assert_eq!(engine.config().cache_config.cache_misses(), 1); - - let mut cfg = Config::new(); - cfg.debug_info(true).cache_config_load(&config_path)?; - let engine = Engine::new(&cfg)?; - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 0); - assert_eq!(engine.config().cache_config.cache_misses(), 1); - Module::new(&engine, "(module (func))")?; - assert_eq!(engine.config().cache_config.cache_hits(), 1); - assert_eq!(engine.config().cache_config.cache_misses(), 1); + } Ok(()) } #[test] - fn precompile_compatibility_key_accounts_for_opt_level() { - fn hash_for_config(cfg: &Config) -> u64 { - let engine = Engine::new(cfg).expect("Config should be valid"); - let mut hasher = DefaultHasher::new(); - engine.precompile_compatibility_hash().hash(&mut hasher); - hasher.finish() - } - let mut cfg = Config::new(); - cfg.cranelift_opt_level(OptLevel::None); - let opt_none_hash = hash_for_config(&cfg); - cfg.cranelift_opt_level(OptLevel::Speed); - let opt_speed_hash = hash_for_config(&cfg); - assert_ne!(opt_none_hash, opt_speed_hash) - } + fn test_feature_mismatch() -> Result<()> { + let mut config = Config::new(); + config.wasm_threads(true); - #[test] - fn precompile_compatibility_key_accounts_for_module_version_strategy() -> Result<()> { - fn hash_for_config(cfg: &Config) -> u64 { - let engine = Engine::new(cfg).expect("Config should be valid"); - let mut hasher = DefaultHasher::new(); - engine.precompile_compatibility_hash().hash(&mut hasher); - hasher.finish() + let engine = Engine::new(&config)?; + let mut metadata = Metadata::new(&engine); + metadata.features.threads = false; + + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert_eq!(e.to_string(), "Module was compiled without WebAssembly threads support but it is enabled for the host"), } - let mut cfg_custom_version = Config::new(); - cfg_custom_version.module_version(ModuleVersionStrategy::Custom("1.0.1111".to_string()))?; - let custom_version_hash = hash_for_config(&cfg_custom_version); - let mut cfg_default_version = Config::new(); - cfg_default_version.module_version(ModuleVersionStrategy::WasmtimeVersion)?; - let default_version_hash = hash_for_config(&cfg_default_version); + let mut config = Config::new(); + config.wasm_threads(false); - let mut cfg_none_version = Config::new(); - cfg_none_version.module_version(ModuleVersionStrategy::None)?; - let none_version_hash = hash_for_config(&cfg_none_version); + let engine = Engine::new(&config)?; + let mut metadata = Metadata::new(&engine); + metadata.features.threads = true; - assert_ne!(custom_version_hash, default_version_hash); - assert_ne!(custom_version_hash, none_version_hash); - assert_ne!(default_version_hash, none_version_hash); + match metadata.check_compatible(&engine) { + Ok(_) => unreachable!(), + Err(e) => assert_eq!(e.to_string(), "Module was compiled with WebAssembly threads support but it is not enabled for the host"), + } Ok(()) } diff --git a/crates/wasmtime/src/lib.rs b/crates/wasmtime/src/lib.rs index a6be8b8c1f48..51508c72e047 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -403,65 +403,72 @@ // most features enabled. This will present warnings in stripped-down doc builds // and will prevent the doc build from failing. #![cfg_attr(feature = "default", deny(rustdoc::broken_intra_doc_links))] +// TODOABK: Figure out why this is needed for the map_maybe_uninit macro +#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)] -#[macro_use] -mod func; +cfg_if::cfg_if! { + if #[cfg(feature = "runtime")] { + mod runtime; + pub(crate) use runtime::*; + + pub use code_memory::CodeMemory; + pub use instance::{Instance, InstancePre}; + pub use instantiate::CompiledModule; + pub use runtime_engine::*; + pub use externals::*; + pub use func::*; + pub use linker::*; + pub use memory::*; + pub use module::Module; + pub use r#ref::ExternRef; + #[cfg(feature = "async")] + pub use store::CallHookHandler; + pub use store::{ + AsContext, AsContextMut, CallHook, Store, StoreContext, StoreContextMut, UpdateDeadline, + }; + pub use trap::*; + pub use types::*; + pub use v128::V128; + pub use values::*; + + #[cfg(feature = "component-model")] + pub use runtime::component; + cfg_if::cfg_if! { + if #[cfg(unix)] { + pub use runtime::unix; + } else if #[cfg(windows)] { + pub use runtime::windows; + } else { + // ... unknown os! + } + } + } +} + +#[cfg(any(feature = "cranelift", feature = "winch"))] +mod compile; #[cfg(any(feature = "cranelift", feature = "winch"))] mod compiler; -mod code; -mod code_memory; +#[cfg(feature = "component-model")] +mod component_artifacts; mod config; mod debug; mod engine; -mod externals; -mod instance; -mod instantiate; mod limits; -mod linker; -mod memory; -mod module; -#[cfg(feature = "profiling")] mod profiling; -mod r#ref; mod resources; -mod signatures; -mod store; -mod trampoline; -mod trap; -mod types; -mod unwind; -mod v128; -mod values; #[cfg(feature = "async")] mod stack; -pub use crate::code_memory::CodeMemory; pub use crate::config::*; pub use crate::engine::*; -pub use crate::externals::*; -pub use crate::func::*; -pub use crate::instance::{Instance, InstancePre}; -pub use crate::instantiate::CompiledModule; pub use crate::limits::*; -pub use crate::linker::*; -pub use crate::memory::*; -pub use crate::module::Module; #[cfg(feature = "profiling")] pub use crate::profiling::GuestProfiler; -pub use crate::r#ref::ExternRef; pub use crate::resources::*; -#[cfg(feature = "async")] -pub use crate::store::CallHookHandler; -pub use crate::store::{ - AsContext, AsContextMut, CallHook, Store, StoreContext, StoreContextMut, UpdateDeadline, -}; -pub use crate::trap::*; -pub use crate::types::*; -pub use crate::v128::V128; -pub use crate::values::*; #[cfg(feature = "async")] pub use crate::stack::*; @@ -478,36 +485,27 @@ pub use crate::coredump::*; /// This type alias is identical to `anyhow::Result`. pub use anyhow::{Error, Result}; -#[cfg(feature = "component-model")] -pub mod component; - -cfg_if::cfg_if! { - if #[cfg(unix)] { - pub mod unix; - } else if #[cfg(windows)] { - pub mod windows; - } else { - // ... unknown os! - } -} - fn _assert_send_sync() { fn _assert() {} fn _assert_send(_t: T) {} - _assert::(); + + #[cfg(feature = "runtime")] + { + _assert::>(); + _assert::(); + _assert::(); + _assert::<(Func, TypedFunc<(), ()>, Global, Table, Memory)>(); + _assert::(); + _assert::>(); + _assert::>(); + _assert::>(); + _assert::>(); + _assert::(); + _assert::>(); + _assert::>(); + _assert::>(); + } _assert::(); - _assert::<(Func, TypedFunc<(), ()>, Global, Table, Memory)>(); - _assert::(); - _assert::(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::(); - _assert::>(); - _assert::>(); #[cfg(feature = "async")] fn _call_async(s: &mut Store<()>, f: Func) { diff --git a/crates/wasmtime/src/profiling.rs b/crates/wasmtime/src/profiling.rs index 189a2c43df7c..533e8a688fd0 100644 --- a/crates/wasmtime/src/profiling.rs +++ b/crates/wasmtime/src/profiling.rs @@ -1,15 +1,17 @@ -use crate::{code_memory::CodeMemory, instantiate::CompiledModule, AsContext, Module}; +#[cfg(feature = "runtime")] +use crate::{instantiate::CompiledModule, AsContext, Module}; #[allow(unused_imports)] use anyhow::bail; use anyhow::Result; -use fxprof_processed_profile::debugid::DebugId; +#[cfg(feature = "profiling")] use fxprof_processed_profile::{ - CategoryHandle, CpuDelta, Frame, FrameFlags, FrameInfo, LibraryInfo, Profile, + debugid::DebugId, CategoryHandle, CpuDelta, Frame, FrameFlags, FrameInfo, LibraryInfo, Profile, ReferenceTimestamp, Symbol, SymbolTable, Timestamp, }; use std::ops::Range; use std::sync::Arc; use std::time::{Duration, Instant}; +#[cfg(feature = "runtime")] use wasmtime_runtime::Backtrace; // TODO: collect more data @@ -71,6 +73,7 @@ use wasmtime_runtime::Backtrace; /// where they don't already have the WebAssembly module binary available this /// could theoretically lead to an undesirable information disclosure. So you /// should only include user-provided modules in profiles. +#[cfg(feature = "profiling")] #[derive(Debug)] pub struct GuestProfiler { profile: Profile, @@ -80,6 +83,7 @@ pub struct GuestProfiler { start: Instant, } +#[cfg(feature = "profiling")] impl GuestProfiler { /// Begin profiling a new guest. When this function is called, the current /// wall-clock time is recorded as the start time for the guest. @@ -182,6 +186,7 @@ impl GuestProfiler { } } +#[cfg(feature = "profiling")] fn module_symbols(name: String, compiled: &CompiledModule) -> Option { let symbols = Vec::from_iter(compiled.finished_functions().map(|(defined_idx, _)| { let loc = compiled.func_loc(defined_idx); @@ -259,10 +264,10 @@ cfg_if::cfg_if! { pub trait ProfilingAgent: Send + Sync + 'static { fn register_function(&self, name: &str, addr: *const u8, size: usize); - fn register_module(&self, code: &CodeMemory, custom_name: &dyn Fn(usize) -> Option) { + fn register_module(&self, code: &[u8], custom_name: &dyn Fn(usize) -> Option) { use object::{File, Object as _, ObjectSection, ObjectSymbol, SectionKind, SymbolKind}; - let image = match File::parse(&code.mmap()[..]) { + let image = match File::parse(code) { Ok(image) => image, Err(_) => return, }; @@ -312,5 +317,5 @@ struct NullProfilerAgent; impl ProfilingAgent for NullProfilerAgent { fn register_function(&self, _name: &str, _addr: *const u8, _size: usize) {} - fn register_module(&self, _code: &CodeMemory, _custom_name: &dyn Fn(usize) -> Option) {} + fn register_module(&self, _code: &[u8], _custom_name: &dyn Fn(usize) -> Option) {} } diff --git a/crates/wasmtime/src/runtime.rs b/crates/wasmtime/src/runtime.rs new file mode 100644 index 000000000000..26e8a1f2e1c2 --- /dev/null +++ b/crates/wasmtime/src/runtime.rs @@ -0,0 +1,34 @@ +#[macro_use] +pub(crate) mod func; + +pub(crate) mod code; +pub(crate) mod code_memory; +pub(crate) mod externals; +pub(crate) mod instance; +pub(crate) mod instantiate; +pub(crate) mod linker; +pub(crate) mod memory; +pub(crate) mod module; +pub(crate) mod r#ref; +pub(crate) mod runtime_engine; +pub(crate) mod signatures; +pub(crate) mod store; +pub(crate) mod trampoline; +pub(crate) mod trap; +pub(crate) mod types; +pub(crate) mod unwind; +pub(crate) mod v128; +pub(crate) mod values; + +cfg_if::cfg_if! { + if #[cfg(unix)] { + pub mod unix; + } else if #[cfg(windows)] { + pub mod windows; + } else { + // ... unknown os! + } +} + +#[cfg(feature = "component-model")] +pub mod component; diff --git a/crates/wasmtime/src/code.rs b/crates/wasmtime/src/runtime/code.rs similarity index 100% rename from crates/wasmtime/src/code.rs rename to crates/wasmtime/src/runtime/code.rs diff --git a/crates/wasmtime/src/code_memory.rs b/crates/wasmtime/src/runtime/code_memory.rs similarity index 100% rename from crates/wasmtime/src/code_memory.rs rename to crates/wasmtime/src/runtime/code_memory.rs diff --git a/crates/wasmtime/src/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs similarity index 77% rename from crates/wasmtime/src/component/component.rs rename to crates/wasmtime/src/runtime/component/component.rs index 8c55ba4cff87..4cab012522e0 100644 --- a/crates/wasmtime/src/component/component.rs +++ b/crates/wasmtime/src/runtime/component/component.rs @@ -1,9 +1,12 @@ use crate::{ - code::CodeObject, code_memory::CodeMemory, instantiate::finish_object, - signatures::SignatureCollection, Engine, Module, ResourcesRequired, + code::CodeObject, + code_memory::CodeMemory, + component_artifacts::{CompiledComponentInfo, ComponentArtifacts}, + instantiate::MmapVecWrapper, + signatures::SignatureCollection, + Engine, Module, ResourcesRequired, }; use anyhow::{bail, Context, Result}; -use serde_derive::{Deserialize, Serialize}; use std::fs; use std::mem; use std::path::Path; @@ -11,15 +14,12 @@ use std::ptr::NonNull; use std::sync::Arc; use wasmtime_environ::component::{ AllCallFunc, ComponentTypes, GlobalInitializer, InstantiateModule, StaticModuleIndex, - TrampolineIndex, Translator, VMComponentOffsets, -}; -use wasmtime_environ::{ - CompiledModuleInfo, FunctionLoc, HostPtr, ObjectKind, PrimaryMap, ScopeVec, + TrampolineIndex, VMComponentOffsets, }; +use wasmtime_environ::{FunctionLoc, HostPtr, ObjectKind, PrimaryMap}; use wasmtime_runtime::component::ComponentRuntimeInfo; use wasmtime_runtime::{ - MmapVec, VMArrayCallFunction, VMFuncRef, VMFunctionBody, VMNativeCallFunction, - VMWasmCallFunction, + VMArrayCallFunction, VMFuncRef, VMFunctionBody, VMNativeCallFunction, VMWasmCallFunction, }; /// A compiled WebAssembly Component. @@ -46,42 +46,12 @@ struct ComponentInner { info: CompiledComponentInfo, } -#[derive(Serialize, Deserialize)] -struct CompiledComponentInfo { - /// Type information calculated during translation about this component. - component: wasmtime_environ::component::Component, - - /// Where lowered function trampolines are located within the `text` - /// section of `code_memory`. - /// - /// These are the - /// - /// 1. Wasm-call, - /// 2. array-call, and - /// 3. native-call - /// - /// function pointers that end up in a `VMFuncRef` for each - /// lowering. - trampolines: PrimaryMap>, - - /// The location of the wasm-to-native trampoline for the `resource.drop` - /// intrinsic. - resource_drop_wasm_to_native_trampoline: Option, -} - pub(crate) struct AllCallFuncPointers { pub wasm_call: NonNull, pub array_call: VMArrayCallFunction, pub native_call: NonNull, } -#[derive(Serialize, Deserialize)] -pub(crate) struct ComponentArtifacts { - info: CompiledComponentInfo, - types: ComponentTypes, - static_modules: PrimaryMap, -} - impl Component { /// Compiles a new WebAssembly component from the in-memory wasm image /// provided. @@ -129,12 +99,13 @@ impl Component { #[cfg(any(feature = "cranelift", feature = "winch"))] #[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result { + use crate::compile::build_component_artifacts; engine .check_compatible_with_native_host() .context("compilation settings are not compatible with the native host")?; - let (mmap, artifacts) = Component::build_artifacts(engine, binary)?; - let mut code_memory = CodeMemory::new(mmap)?; + let (mmap, artifacts) = build_component_artifacts::(engine, binary)?; + let mut code_memory = CodeMemory::new(mmap.0)?; code_memory.publish()?; Component::from_parts(engine, Arc::new(code_memory), Some(artifacts)) } @@ -164,72 +135,6 @@ impl Component { Component::from_parts(engine, code, None) } - /// Performs the compilation phase for a component, translating and - /// validating the provided wasm binary to machine code. - /// - /// This method will compile all nested core wasm binaries in addition to - /// any necessary extra functions required for operation with components. - /// The output artifact here is the serialized object file contained within - /// an owned mmap along with metadata about the compilation itself. - #[cfg(any(feature = "cranelift", feature = "winch"))] - pub(crate) fn build_artifacts( - engine: &Engine, - binary: &[u8], - ) -> Result<(MmapVec, ComponentArtifacts)> { - use crate::compiler::CompileInputs; - - let tunables = &engine.config().tunables; - let compiler = engine.compiler(); - - let scope = ScopeVec::new(); - let mut validator = - wasmparser::Validator::new_with_features(engine.config().features.clone()); - let mut types = Default::default(); - let (component, mut module_translations) = - Translator::new(tunables, &mut validator, &mut types, &scope) - .translate(binary) - .context("failed to parse WebAssembly module")?; - let types = types.finish(); - - let compile_inputs = CompileInputs::for_component( - &types, - &component, - module_translations.iter_mut().map(|(i, translation)| { - let functions = mem::take(&mut translation.function_body_inputs); - (i, &*translation, functions) - }), - ); - let unlinked_compile_outputs = compile_inputs.compile(&engine)?; - let (compiled_funcs, function_indices) = unlinked_compile_outputs.pre_link(); - - let mut object = compiler.object(ObjectKind::Component)?; - engine.append_compiler_info(&mut object); - engine.append_bti(&mut object); - - let (mut object, compilation_artifacts) = function_indices.link_and_append_code( - object, - engine, - compiled_funcs, - module_translations, - )?; - - let info = CompiledComponentInfo { - component: component.component, - trampolines: compilation_artifacts.trampolines, - resource_drop_wasm_to_native_trampoline: compilation_artifacts - .resource_drop_wasm_to_native_trampoline, - }; - let artifacts = ComponentArtifacts { - info, - types, - static_modules: compilation_artifacts.modules, - }; - object.serialize_info(&artifacts); - - let mmap = finish_object(object)?; - Ok((mmap, artifacts)) - } - /// Final assembly step for a component from its in-memory representation. /// /// If the `artifacts` are specified as `None` here then they will be diff --git a/crates/wasmtime/src/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs similarity index 100% rename from crates/wasmtime/src/component/func.rs rename to crates/wasmtime/src/runtime/component/func.rs diff --git a/crates/wasmtime/src/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs similarity index 100% rename from crates/wasmtime/src/component/func/host.rs rename to crates/wasmtime/src/runtime/component/func/host.rs diff --git a/crates/wasmtime/src/component/func/options.rs b/crates/wasmtime/src/runtime/component/func/options.rs similarity index 100% rename from crates/wasmtime/src/component/func/options.rs rename to crates/wasmtime/src/runtime/component/func/options.rs diff --git a/crates/wasmtime/src/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs similarity index 100% rename from crates/wasmtime/src/component/func/typed.rs rename to crates/wasmtime/src/runtime/component/func/typed.rs diff --git a/crates/wasmtime/src/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs similarity index 100% rename from crates/wasmtime/src/component/instance.rs rename to crates/wasmtime/src/runtime/component/instance.rs diff --git a/crates/wasmtime/src/component/linker.rs b/crates/wasmtime/src/runtime/component/linker.rs similarity index 100% rename from crates/wasmtime/src/component/linker.rs rename to crates/wasmtime/src/runtime/component/linker.rs diff --git a/crates/wasmtime/src/component/matching.rs b/crates/wasmtime/src/runtime/component/matching.rs similarity index 100% rename from crates/wasmtime/src/component/matching.rs rename to crates/wasmtime/src/runtime/component/matching.rs diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs similarity index 100% rename from crates/wasmtime/src/component/mod.rs rename to crates/wasmtime/src/runtime/component/mod.rs diff --git a/crates/wasmtime/src/component/resources.rs b/crates/wasmtime/src/runtime/component/resources.rs similarity index 100% rename from crates/wasmtime/src/component/resources.rs rename to crates/wasmtime/src/runtime/component/resources.rs diff --git a/crates/wasmtime/src/component/storage.rs b/crates/wasmtime/src/runtime/component/storage.rs similarity index 100% rename from crates/wasmtime/src/component/storage.rs rename to crates/wasmtime/src/runtime/component/storage.rs diff --git a/crates/wasmtime/src/component/store.rs b/crates/wasmtime/src/runtime/component/store.rs similarity index 100% rename from crates/wasmtime/src/component/store.rs rename to crates/wasmtime/src/runtime/component/store.rs diff --git a/crates/wasmtime/src/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs similarity index 100% rename from crates/wasmtime/src/component/types.rs rename to crates/wasmtime/src/runtime/component/types.rs diff --git a/crates/wasmtime/src/component/values.rs b/crates/wasmtime/src/runtime/component/values.rs similarity index 100% rename from crates/wasmtime/src/component/values.rs rename to crates/wasmtime/src/runtime/component/values.rs diff --git a/crates/wasmtime/src/externals.rs b/crates/wasmtime/src/runtime/externals.rs similarity index 100% rename from crates/wasmtime/src/externals.rs rename to crates/wasmtime/src/runtime/externals.rs diff --git a/crates/wasmtime/src/externals/global.rs b/crates/wasmtime/src/runtime/externals/global.rs similarity index 100% rename from crates/wasmtime/src/externals/global.rs rename to crates/wasmtime/src/runtime/externals/global.rs diff --git a/crates/wasmtime/src/externals/table.rs b/crates/wasmtime/src/runtime/externals/table.rs similarity index 100% rename from crates/wasmtime/src/externals/table.rs rename to crates/wasmtime/src/runtime/externals/table.rs diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/runtime/func.rs similarity index 100% rename from crates/wasmtime/src/func.rs rename to crates/wasmtime/src/runtime/func.rs diff --git a/crates/wasmtime/src/func/typed.rs b/crates/wasmtime/src/runtime/func/typed.rs similarity index 100% rename from crates/wasmtime/src/func/typed.rs rename to crates/wasmtime/src/runtime/func/typed.rs diff --git a/crates/wasmtime/src/instance.rs b/crates/wasmtime/src/runtime/instance.rs similarity index 100% rename from crates/wasmtime/src/instance.rs rename to crates/wasmtime/src/runtime/instance.rs diff --git a/crates/wasmtime/src/instantiate.rs b/crates/wasmtime/src/runtime/instantiate.rs similarity index 99% rename from crates/wasmtime/src/instantiate.rs rename to crates/wasmtime/src/runtime/instantiate.rs index 2f7e27c32f73..c215185f41f8 100644 --- a/crates/wasmtime/src/instantiate.rs +++ b/crates/wasmtime/src/runtime/instantiate.rs @@ -78,7 +78,7 @@ impl CompiledModule { let reg = GdbJitImageRegistration::register(bytes); self.dbg_jit_registration = Some(reg); } - profiler.register_module(&self.code_memory, &|addr| { + profiler.register_module(&self.code_memory.mmap()[..], &|addr| { let (idx, _) = self.func_by_text_offset(addr)?; let idx = self.module.func_index(idx); let name = self.func_name(idx)?; @@ -357,7 +357,7 @@ pub fn finish_object(obj: ObjectBuilder<'_>) -> Result { Ok(::finish_object(obj)?.0) } -struct MmapVecWrapper(pub MmapVec); +pub(crate) struct MmapVecWrapper(pub MmapVec); impl FinishedObject for MmapVecWrapper { fn finish_object(obj: ObjectBuilder<'_>) -> Result { diff --git a/crates/wasmtime/src/linker.rs b/crates/wasmtime/src/runtime/linker.rs similarity index 100% rename from crates/wasmtime/src/linker.rs rename to crates/wasmtime/src/runtime/linker.rs diff --git a/crates/wasmtime/src/memory.rs b/crates/wasmtime/src/runtime/memory.rs similarity index 100% rename from crates/wasmtime/src/memory.rs rename to crates/wasmtime/src/runtime/memory.rs diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/runtime/module.rs similarity index 93% rename from crates/wasmtime/src/module.rs rename to crates/wasmtime/src/runtime/module.rs index 519ea84f66fa..69e37b27ed39 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/runtime/module.rs @@ -17,8 +17,8 @@ use std::ptr::NonNull; use std::sync::Arc; use wasmparser::{Parser, ValidPayload, Validator}; use wasmtime_environ::{ - CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, HostPtr, ModuleEnvironment, - ModuleTypes, ObjectKind, VMOffsets, + CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, HostPtr, ModuleTypes, ObjectKind, + VMOffsets, }; use wasmtime_runtime::{ CompiledModuleId, MemoryImage, MmapVec, ModuleMemoryImages, VMArrayCallFunction, @@ -302,6 +302,8 @@ impl Module { #[cfg(any(feature = "cranelift", feature = "winch"))] #[cfg_attr(nightlydoc, doc(cfg(any(feature = "cranelift", feature = "winch"))))] pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result { + use crate::{compile::build_artifacts, runtime::instantiate::MmapVecWrapper}; + engine .check_compatible_with_native_host() .context("compilation settings are not compatible with the native host")?; @@ -318,8 +320,8 @@ impl Module { // Cache miss, compute the actual artifacts |(engine, wasm)| -> Result<_> { - let (mmap, info) = Module::build_artifacts(engine.0, wasm)?; - let code = publish_mmap(mmap)?; + let (mmap, info) = build_artifacts::(engine.0, wasm)?; + let code = publish_mmap(mmap.0)?; Ok((code, info)) }, @@ -383,76 +385,6 @@ impl Module { Module::new(engine, &*mmap) } - /// Converts an input binary-encoded WebAssembly module to compilation - /// artifacts and type information. - /// - /// This is where compilation actually happens of WebAssembly modules and - /// translation/parsing/validation of the binary input occurs. The binary - /// artifact represented in the `MmapVec` returned here is an in-memory ELF - /// file in an owned area of virtual linear memory where permissions (such - /// as the executable bit) can be applied. - /// - /// Additionally compilation returns an `Option` here which is always - /// `Some`, notably compiled metadata about the module in addition to the - /// type information found within. - #[cfg(any(feature = "cranelift", feature = "winch"))] - pub(crate) fn build_artifacts( - engine: &Engine, - wasm: &[u8], - ) -> Result<(MmapVec, Option<(CompiledModuleInfo, ModuleTypes)>)> { - use crate::instantiate::finish_object; - - use crate::compiler::CompileInputs; - - let tunables = &engine.config().tunables; - - // First a `ModuleEnvironment` is created which records type information - // about the wasm module. This is where the WebAssembly is parsed and - // validated. Afterwards `types` will have all the type information for - // this module. - let mut validator = - wasmparser::Validator::new_with_features(engine.config().features.clone()); - let parser = wasmparser::Parser::new(0); - let mut types = Default::default(); - let mut translation = ModuleEnvironment::new(tunables, &mut validator, &mut types) - .translate(parser, wasm) - .context("failed to parse WebAssembly module")?; - let functions = mem::take(&mut translation.function_body_inputs); - let types = types.finish(); - - let compile_inputs = CompileInputs::for_module(&types, &translation, functions); - let unlinked_compile_outputs = compile_inputs.compile(engine)?; - let (compiled_funcs, function_indices) = unlinked_compile_outputs.pre_link(); - - // Emplace all compiled functions into the object file with any other - // sections associated with code as well. - let mut object = engine.compiler().object(ObjectKind::Module)?; - // Insert `Engine` and type-level information into the compiled - // artifact so if this module is deserialized later it contains all - // information necessary. - // - // Note that `append_compiler_info` and `append_types` here in theory - // can both be skipped if this module will never get serialized. - // They're only used during deserialization and not during runtime for - // the module itself. Currently there's no need for that, however, so - // it's left as an exercise for later. - engine.append_compiler_info(&mut object); - engine.append_bti(&mut object); - - let (mut object, compilation_artifacts) = function_indices.link_and_append_code( - object, - engine, - compiled_funcs, - std::iter::once(translation).collect(), - )?; - - let info = compilation_artifacts.unwrap_as_module_info(); - object.serialize_info(&(&info, &types)); - let mmap = finish_object(object)?; - - Ok((mmap, Some((info, types)))) - } - /// Deserializes an in-memory compiled module previously created with /// [`Module::serialize`] or [`Engine::precompile_module`]. /// diff --git a/crates/wasmtime/src/module/registry.rs b/crates/wasmtime/src/runtime/module/registry.rs similarity index 100% rename from crates/wasmtime/src/module/registry.rs rename to crates/wasmtime/src/runtime/module/registry.rs diff --git a/crates/wasmtime/src/ref.rs b/crates/wasmtime/src/runtime/ref.rs similarity index 100% rename from crates/wasmtime/src/ref.rs rename to crates/wasmtime/src/runtime/ref.rs diff --git a/crates/wasmtime/src/runtime/runtime_engine.rs b/crates/wasmtime/src/runtime/runtime_engine.rs new file mode 100644 index 000000000000..7bf619ab4ccd --- /dev/null +++ b/crates/wasmtime/src/runtime/runtime_engine.rs @@ -0,0 +1,596 @@ +use crate::{profiling::ProfilingAgent, signatures::SignatureRegistry, CodeMemory, Config, Engine}; +use anyhow::{Context, Result}; +use std::path::Path; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; +#[cfg(feature = "cache")] +use wasmtime_cache::CacheConfig; +use wasmtime_environ::{FlagValue, ObjectKind}; +use wasmtime_runtime::{CompiledModuleIdAllocator, InstanceAllocator, MmapVec}; + +mod serialization; + +impl Engine { + /// Eagerly initialize thread-local functionality shared by all [`Engine`]s. + /// + /// Wasmtime's implementation on some platforms may involve per-thread + /// setup that needs to happen whenever WebAssembly is invoked. This setup + /// can take on the order of a few hundred microseconds, whereas the + /// overhead of calling WebAssembly is otherwise on the order of a few + /// nanoseconds. This setup cost is paid once per-OS-thread. If your + /// application is sensitive to the latencies of WebAssembly function + /// calls, even those that happen first on a thread, then this function + /// can be used to improve the consistency of each call into WebAssembly + /// by explicitly frontloading the cost of the one-time setup per-thread. + /// + /// Note that this function is not required to be called in any embedding. + /// Wasmtime will automatically initialize thread-local-state as necessary + /// on calls into WebAssembly. This is provided for use cases where the + /// latency of WebAssembly calls are extra-important, which is not + /// necessarily true of all embeddings. + pub fn tls_eager_initialize() { + wasmtime_runtime::tls_eager_initialize(); + } + + pub(crate) fn allocator(&self) -> &dyn InstanceAllocator { + self.inner.allocator.as_ref() + } + + pub(crate) fn profiler(&self) -> &dyn ProfilingAgent { + self.inner.profiler.as_ref() + } + + #[cfg(feature = "cache")] + pub(crate) fn cache_config(&self) -> &CacheConfig { + &self.config().cache_config + } + + /// Returns whether the engine `a` and `b` refer to the same configuration. + pub fn same(a: &Engine, b: &Engine) -> bool { + Arc::ptr_eq(&a.inner, &b.inner) + } + + pub(crate) fn signatures(&self) -> &SignatureRegistry { + &self.inner.signatures + } + + pub(crate) fn epoch_counter(&self) -> &AtomicU64 { + &self.inner.epoch + } + + pub(crate) fn current_epoch(&self) -> u64 { + self.epoch_counter().load(Ordering::Relaxed) + } + + /// Increments the epoch. + /// + /// When using epoch-based interruption, currently-executing Wasm + /// code within this engine will trap or yield "soon" when the + /// epoch deadline is reached or exceeded. (The configuration, and + /// the deadline, are set on the `Store`.) The intent of the + /// design is for this method to be called by the embedder at some + /// regular cadence, for example by a thread that wakes up at some + /// interval, or by a signal handler. + /// + /// See [`Config::epoch_interruption`](crate::Config::epoch_interruption) + /// for an introduction to epoch-based interruption and pointers + /// to the other relevant methods. + /// + /// ## Signal Safety + /// + /// This method is signal-safe: it does not make any syscalls, and + /// performs only an atomic increment to the epoch value in + /// memory. + pub fn increment_epoch(&self) { + self.inner.epoch.fetch_add(1, Ordering::Relaxed); + } + + pub(crate) fn unique_id_allocator(&self) -> &CompiledModuleIdAllocator { + &self.inner.unique_id_allocator + } + + /// Returns a [`std::hash::Hash`] that can be used to check precompiled WebAssembly compatibility. + /// + /// The outputs of [`Engine::precompile_module`] and [`Engine::precompile_component`] + /// are compatible with a different [`Engine`] instance only if the two engines use + /// compatible [`Config`]s. If this Hash matches between two [`Engine`]s then binaries + /// from one are guaranteed to deserialize in the other. + #[cfg(any(feature = "cranelift", feature = "winch"))] + #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs + pub fn precompile_compatibility_hash(&self) -> impl std::hash::Hash + '_ { + crate::module::HashedEngineCompileEnv(self) + } + + /// Executes `f1` and `f2` in parallel if parallel compilation is enabled at + /// both runtime and compile time, otherwise runs them synchronously. + #[allow(dead_code)] // only used for the component-model feature right now + pub(crate) fn join_maybe_parallel( + &self, + f1: impl FnOnce() -> T + Send, + f2: impl FnOnce() -> U + Send, + ) -> (T, U) + where + T: Send, + U: Send, + { + if self.config().parallel_compilation { + #[cfg(feature = "parallel-compilation")] + return rayon::join(f1, f2); + } + (f1(), f2()) + } + + /// Returns the target triple which this engine is compiling code for + /// and/or running code for. + pub(crate) fn target(&self) -> target_lexicon::Triple { + // If a compiler is configured, use that target. + #[cfg(any(feature = "cranelift", feature = "winch"))] + return self.compiler().triple().clone(); + + // ... otherwise it's the native target + #[cfg(not(any(feature = "cranelift", feature = "winch")))] + return target_lexicon::Triple::host(); + } + + /// Verify that this engine's configuration is compatible with loading + /// modules onto the native host platform. + /// + /// This method is used as part of `Module::new` to ensure that this + /// engine can indeed load modules for the configured compiler (if any). + /// Note that if cranelift is disabled this trivially returns `Ok` because + /// loaded serialized modules are checked separately. + pub(crate) fn check_compatible_with_native_host(&self) -> Result<()> { + self.inner + .compatible_with_native_host + .get_or_init(|| self._check_compatible_with_native_host()) + .clone() + .map_err(anyhow::Error::msg) + } + + fn _check_compatible_with_native_host(&self) -> Result<(), String> { + #[cfg(any(feature = "cranelift", feature = "winch"))] + { + let compiler = self.compiler(); + + // Check to see that the config's target matches the host + let target = compiler.triple(); + if *target != target_lexicon::Triple::host() { + return Err(format!( + "target '{}' specified in the configuration does not match the host", + target + )); + } + + // Also double-check all compiler settings + for (key, value) in compiler.flags().iter() { + self.check_compatible_with_shared_flag(key, value)?; + } + for (key, value) in compiler.isa_flags().iter() { + self.check_compatible_with_isa_flag(key, value)?; + } + } + Ok(()) + } + + /// Checks to see whether the "shared flag", something enabled for + /// individual compilers, is compatible with the native host platform. + /// + /// This is used both when validating an engine's compilation settings are + /// compatible with the host as well as when deserializing modules from + /// disk to ensure they're compatible with the current host. + /// + /// Note that most of the settings here are not configured by users that + /// often. While theoretically possible via `Config` methods the more + /// interesting flags are the ISA ones below. Typically the values here + /// represent global configuration for wasm features. Settings here + /// currently rely on the compiler informing us of all settings, including + /// those disabled. Settings then fall in a few buckets: + /// + /// * Some settings must be enabled, such as `preserve_frame_pointers`. + /// * Some settings must have a particular value, such as + /// `libcall_call_conv`. + /// * Some settings do not matter as to their value, such as `opt_level`. + pub(crate) fn check_compatible_with_shared_flag( + &self, + flag: &str, + value: &FlagValue, + ) -> Result<(), String> { + let target = self.target(); + let ok = match flag { + // These settings must all have be enabled, since their value + // can affect the way the generated code performs or behaves at + // runtime. + "libcall_call_conv" => *value == FlagValue::Enum("isa_default".into()), + "preserve_frame_pointers" => *value == FlagValue::Bool(true), + "enable_probestack" => *value == FlagValue::Bool(crate::config::probestack_supported(target.architecture)), + "probestack_strategy" => *value == FlagValue::Enum("inline".into()), + + // Features wasmtime doesn't use should all be disabled, since + // otherwise if they are enabled it could change the behavior of + // generated code. + "enable_llvm_abi_extensions" => *value == FlagValue::Bool(false), + "enable_pinned_reg" => *value == FlagValue::Bool(false), + "use_colocated_libcalls" => *value == FlagValue::Bool(false), + "use_pinned_reg_as_heap_base" => *value == FlagValue::Bool(false), + + // If reference types are enabled this must be enabled, otherwise + // this setting can have any value. + "enable_safepoints" => { + if self.config().features.reference_types { + *value == FlagValue::Bool(true) + } else { + return Ok(()) + } + } + + // Windows requires unwind info as part of its ABI. + "unwind_info" => { + if target.operating_system == target_lexicon::OperatingSystem::Windows { + *value == FlagValue::Bool(true) + } else { + return Ok(()) + } + } + + // These settings don't affect the interface or functionality of + // the module itself, so their configuration values shouldn't + // matter. + "enable_heap_access_spectre_mitigation" + | "enable_table_access_spectre_mitigation" + | "enable_nan_canonicalization" + | "enable_jump_tables" + | "enable_float" + | "enable_verifier" + | "enable_pcc" + | "regalloc_checker" + | "regalloc_verbose_logs" + | "is_pic" + | "bb_padding_log2_minus_one" + | "machine_code_cfg_info" + | "tls_model" // wasmtime doesn't use tls right now + | "opt_level" // opt level doesn't change semantics + | "enable_alias_analysis" // alias analysis-based opts don't change semantics + | "probestack_func_adjusts_sp" // probestack above asserted disabled + | "probestack_size_log2" // probestack above asserted disabled + | "regalloc" // shouldn't change semantics + | "enable_incremental_compilation_cache_checks" // shouldn't change semantics + | "enable_atomics" => return Ok(()), + + // Everything else is unknown and needs to be added somewhere to + // this list if encountered. + _ => { + return Err(format!("unknown shared setting {:?} configured to {:?}", flag, value)) + } + }; + + if !ok { + return Err(format!( + "setting {:?} is configured to {:?} which is not supported", + flag, value, + )); + } + Ok(()) + } + + /// Same as `check_compatible_with_native_host` except used for ISA-specific + /// flags. This is used to test whether a configured ISA flag is indeed + /// available on the host platform itself. + pub(crate) fn check_compatible_with_isa_flag( + &self, + flag: &str, + value: &FlagValue, + ) -> Result<(), String> { + match value { + // ISA flags are used for things like CPU features, so if they're + // disabled then it's compatible with the native host. + FlagValue::Bool(false) => return Ok(()), + + // Fall through below where we test at runtime that features are + // available. + FlagValue::Bool(true) => {} + + // Only `bool` values are supported right now, other settings would + // need more support here. + _ => { + return Err(format!( + "isa-specific feature {:?} configured to unknown value {:?}", + flag, value + )) + } + } + + #[allow(unused_assignments)] + let mut enabled = None; + + #[cfg(target_arch = "aarch64")] + { + enabled = match flag { + "has_lse" => Some(std::arch::is_aarch64_feature_detected!("lse")), + // No effect on its own, but in order to simplify the code on a + // platform without pointer authentication support we fail if + // "has_pauth" is enabled, but "sign_return_address" is not. + "has_pauth" => Some(std::arch::is_aarch64_feature_detected!("paca")), + // No effect on its own. + "sign_return_address_all" => Some(true), + // The pointer authentication instructions act as a `NOP` when + // unsupported (but keep in mind "has_pauth" as well), so it is + // safe to enable them. + "sign_return_address" => Some(true), + // No effect on its own. + "sign_return_address_with_bkey" => Some(true), + // The `BTI` instruction acts as a `NOP` when unsupported, so it + // is safe to enable it. + "use_bti" => Some(true), + // fall through to the very bottom to indicate that support is + // not enabled to test whether this feature is enabled on the + // host. + _ => None, + }; + } + + // There is no is_s390x_feature_detected macro yet, so for now + // we use getauxval from the libc crate directly. + #[cfg(all(target_arch = "s390x", target_os = "linux"))] + { + let v = unsafe { libc::getauxval(libc::AT_HWCAP) }; + const HWCAP_S390X_VXRS_EXT2: libc::c_ulong = 32768; + + enabled = match flag { + // There is no separate HWCAP bit for mie2, so assume + // that any machine with vxrs_ext2 also has mie2. + "has_vxrs_ext2" | "has_mie2" => Some((v & HWCAP_S390X_VXRS_EXT2) != 0), + // fall through to the very bottom to indicate that support is + // not enabled to test whether this feature is enabled on the + // host. + _ => None, + } + } + + #[cfg(target_arch = "riscv64")] + { + enabled = match flag { + // make sure `test_isa_flags_mismatch` test pass. + "not_a_flag" => None, + // due to `is_riscv64_feature_detected` is not stable. + // we cannot use it. + _ => Some(true), + } + } + + #[cfg(target_arch = "x86_64")] + { + enabled = match flag { + "has_sse3" => Some(std::is_x86_feature_detected!("sse3")), + "has_ssse3" => Some(std::is_x86_feature_detected!("ssse3")), + "has_sse41" => Some(std::is_x86_feature_detected!("sse4.1")), + "has_sse42" => Some(std::is_x86_feature_detected!("sse4.2")), + "has_popcnt" => Some(std::is_x86_feature_detected!("popcnt")), + "has_avx" => Some(std::is_x86_feature_detected!("avx")), + "has_avx2" => Some(std::is_x86_feature_detected!("avx2")), + "has_fma" => Some(std::is_x86_feature_detected!("fma")), + "has_bmi1" => Some(std::is_x86_feature_detected!("bmi1")), + "has_bmi2" => Some(std::is_x86_feature_detected!("bmi2")), + "has_avx512bitalg" => Some(std::is_x86_feature_detected!("avx512bitalg")), + "has_avx512dq" => Some(std::is_x86_feature_detected!("avx512dq")), + "has_avx512f" => Some(std::is_x86_feature_detected!("avx512f")), + "has_avx512vl" => Some(std::is_x86_feature_detected!("avx512vl")), + "has_avx512vbmi" => Some(std::is_x86_feature_detected!("avx512vbmi")), + "has_lzcnt" => Some(std::is_x86_feature_detected!("lzcnt")), + + // fall through to the very bottom to indicate that support is + // not enabled to test whether this feature is enabled on the + // host. + _ => None, + }; + } + + match enabled { + Some(true) => return Ok(()), + Some(false) => { + return Err(format!( + "compilation setting {:?} is enabled, but not available on the host", + flag + )) + } + // fall through + None => {} + } + + Err(format!( + "cannot test if target-specific flag {:?} is available at runtime", + flag + )) + } + + /// Loads a `CodeMemory` from the specified in-memory slice, copying it to a + /// uniquely owned mmap. + /// + /// The `expected` marker here is whether the bytes are expected to be a + /// precompiled module or a component. + pub(crate) fn load_code_bytes( + &self, + bytes: &[u8], + expected: ObjectKind, + ) -> Result> { + self.load_code(MmapVec::from_slice(bytes)?, expected) + } + + /// Like `load_code_bytes`, but creates a mmap from a file on disk. + pub(crate) fn load_code_file( + &self, + path: &Path, + expected: ObjectKind, + ) -> Result> { + self.load_code( + MmapVec::from_file(path).with_context(|| { + format!("failed to create file mapping for: {}", path.display()) + })?, + expected, + ) + } + + pub(crate) fn load_code(&self, mmap: MmapVec, expected: ObjectKind) -> Result> { + serialization::check_compatible(self, &mmap, expected)?; + let mut code = CodeMemory::new(mmap)?; + code.publish()?; + Ok(Arc::new(code)) + } + + /// Detects whether the bytes provided are a precompiled object produced by + /// Wasmtime. + /// + /// This function will inspect the header of `bytes` to determine if it + /// looks like a precompiled core wasm module or a precompiled component. + /// This does not validate the full structure or guarantee that + /// deserialization will succeed, instead it helps higher-levels of the + /// stack make a decision about what to do next when presented with the + /// `bytes` as an input module. + /// + /// If the `bytes` looks like a precompiled object previously produced by + /// [`Module::serialize`](crate::Module::serialize), + /// [`Component::serialize`](crate::component::Component::serialize), + /// [`Engine::precompile_module`], or [`Engine::precompile_component`], then + /// this will return `Some(...)` indicating so. Otherwise `None` is + /// returned. + pub fn detect_precompiled(&self, bytes: &[u8]) -> Option { + serialization::detect_precompiled_bytes(bytes) + } + + /// Like [`Engine::detect_precompiled`], but performs the detection on a file. + pub fn detect_precompiled_file(&self, path: impl AsRef) -> Result> { + serialization::detect_precompiled_file(path) + } +} + +impl Default for Engine { + fn default() -> Engine { + Engine::new(&Config::default()).unwrap() + } +} + +/// Return value from the [`Engine::detect_precompiled`] API. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub enum Precompiled { + /// The input bytes look like a precompiled core wasm module. + Module, + /// The input bytes look like a precompiled wasm component. + Component, +} + +#[cfg(test)] +mod tests { + use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + }; + + use crate::{Config, Engine, Module, ModuleVersionStrategy, OptLevel}; + + use anyhow::Result; + use tempfile::TempDir; + + #[test] + #[cfg_attr(miri, ignore)] + fn cache_accounts_for_opt_level() -> Result<()> { + let td = TempDir::new()?; + let config_path = td.path().join("config.toml"); + std::fs::write( + &config_path, + &format!( + " + [cache] + enabled = true + directory = '{}' + ", + td.path().join("cache").display() + ), + )?; + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::None) + .cache_config_load(&config_path)?; + let engine = Engine::new(&cfg)?; + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::Speed) + .cache_config_load(&config_path)?; + let engine = Engine::new(&cfg)?; + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::SpeedAndSize) + .cache_config_load(&config_path)?; + let engine = Engine::new(&cfg)?; + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + + let mut cfg = Config::new(); + cfg.debug_info(true).cache_config_load(&config_path)?; + let engine = Engine::new(&cfg)?; + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 0); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + Module::new(&engine, "(module (func))")?; + assert_eq!(engine.config().cache_config.cache_hits(), 1); + assert_eq!(engine.config().cache_config.cache_misses(), 1); + + Ok(()) + } + + #[test] + fn precompile_compatibility_key_accounts_for_opt_level() { + fn hash_for_config(cfg: &Config) -> u64 { + let engine = Engine::new(cfg).expect("Config should be valid"); + let mut hasher = DefaultHasher::new(); + engine.precompile_compatibility_hash().hash(&mut hasher); + hasher.finish() + } + let mut cfg = Config::new(); + cfg.cranelift_opt_level(OptLevel::None); + let opt_none_hash = hash_for_config(&cfg); + cfg.cranelift_opt_level(OptLevel::Speed); + let opt_speed_hash = hash_for_config(&cfg); + assert_ne!(opt_none_hash, opt_speed_hash) + } + + #[test] + fn precompile_compatibility_key_accounts_for_module_version_strategy() -> Result<()> { + fn hash_for_config(cfg: &Config) -> u64 { + let engine = Engine::new(cfg).expect("Config should be valid"); + let mut hasher = DefaultHasher::new(); + engine.precompile_compatibility_hash().hash(&mut hasher); + hasher.finish() + } + let mut cfg_custom_version = Config::new(); + cfg_custom_version.module_version(ModuleVersionStrategy::Custom("1.0.1111".to_string()))?; + let custom_version_hash = hash_for_config(&cfg_custom_version); + + let mut cfg_default_version = Config::new(); + cfg_default_version.module_version(ModuleVersionStrategy::WasmtimeVersion)?; + let default_version_hash = hash_for_config(&cfg_default_version); + + let mut cfg_none_version = Config::new(); + cfg_none_version.module_version(ModuleVersionStrategy::None)?; + let none_version_hash = hash_for_config(&cfg_none_version); + + assert_ne!(custom_version_hash, default_version_hash); + assert_ne!(custom_version_hash, none_version_hash); + assert_ne!(default_version_hash, none_version_hash); + + Ok(()) + } +} diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/runtime/runtime_engine/serialization.rs similarity index 59% rename from crates/wasmtime/src/engine/serialization.rs rename to crates/wasmtime/src/runtime/runtime_engine/serialization.rs index d6d0f4291ffb..00641595086d 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/runtime/runtime_engine/serialization.rs @@ -21,49 +21,14 @@ //! other random ELF files, as well as provide better error messages for //! using wasmtime artifacts across versions. -use crate::{Engine, ModuleVersionStrategy, Precompiled}; +use crate::{Engine, Metadata, ModuleVersionStrategy, Precompiled, WasmFeatures, VERSION}; use anyhow::{anyhow, bail, Context, Result}; -use object::write::{Object, StandardSegment}; -use object::{File, FileFlags, Object as _, ObjectSection, SectionKind}; -use serde_derive::{Deserialize, Serialize}; +use object::{File, FileFlags, Object as _, ObjectSection}; use std::str::FromStr; use wasmtime_environ::obj; -use wasmtime_environ::{FlagValue, ObjectKind, Tunables}; +use wasmtime_environ::{ObjectKind, Tunables}; use wasmtime_runtime::MmapVec; -const VERSION: u8 = 0; - -/// Produces a blob of bytes by serializing the `engine`'s configuration data to -/// be checked, perhaps in a different process, with the `check_compatible` -/// method below. -/// -/// The blob of bytes is inserted into the object file specified to become part -/// of the final compiled artifact. -#[cfg(any(feature = "cranelift", feature = "winch"))] -pub fn append_compiler_info(engine: &Engine, obj: &mut Object<'_>) { - let section = obj.add_section( - obj.segment_name(StandardSegment::Data).to_vec(), - obj::ELF_WASM_ENGINE.as_bytes().to_vec(), - SectionKind::ReadOnlyData, - ); - let mut data = Vec::new(); - data.push(VERSION); - let version = match &engine.config().module_version { - ModuleVersionStrategy::WasmtimeVersion => env!("CARGO_PKG_VERSION"), - ModuleVersionStrategy::Custom(c) => c, - ModuleVersionStrategy::None => "", - }; - // This precondition is checked in Config::module_version: - assert!( - version.len() < 256, - "package version must be less than 256 bytes" - ); - data.push(version.len() as u8); - data.extend_from_slice(version.as_bytes()); - bincode::serialize_into(&mut data, &Metadata::new(engine)).unwrap(); - obj.set_section_data(section, data, 1); -} - /// Verifies that the serialized engine in `mmap` is compatible with the /// `engine` provided. /// @@ -172,91 +137,8 @@ pub fn detect_precompiled_file(path: impl AsRef) -> Result { - target: String, - #[serde(borrow)] - shared_flags: Vec<(&'a str, FlagValue<'a>)>, - #[serde(borrow)] - isa_flags: Vec<(&'a str, FlagValue<'a>)>, - tunables: Tunables, - features: WasmFeatures, -} - -// This exists because `wasmparser::WasmFeatures` isn't serializable -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -struct WasmFeatures { - reference_types: bool, - multi_value: bool, - bulk_memory: bool, - component_model: bool, - simd: bool, - tail_call: bool, - threads: bool, - multi_memory: bool, - exceptions: bool, - memory64: bool, - relaxed_simd: bool, - extended_const: bool, - function_references: bool, -} - impl Metadata<'_> { - #[cfg(any(feature = "cranelift", feature = "winch"))] - fn new(engine: &Engine) -> Metadata<'static> { - let wasmparser::WasmFeatures { - reference_types, - multi_value, - bulk_memory, - component_model, - simd, - threads, - tail_call, - multi_memory, - exceptions, - memory64, - relaxed_simd, - extended_const, - memory_control, - function_references, - gc, - component_model_values, - - // Always on; we don't currently have knobs for these. - mutable_global: _, - saturating_float_to_int: _, - sign_extension: _, - floats: _, - } = engine.config().features; - - assert!(!memory_control); - assert!(!gc); - assert!(!component_model_values); - - Metadata { - target: engine.compiler().triple().to_string(), - shared_flags: engine.compiler().flags(), - isa_flags: engine.compiler().isa_flags(), - tunables: engine.config().tunables.clone(), - features: WasmFeatures { - reference_types, - multi_value, - bulk_memory, - component_model, - simd, - threads, - tail_call, - multi_memory, - exceptions, - memory64, - relaxed_simd, - extended_const, - function_references, - }, - } - } - - fn check_compatible(mut self, engine: &Engine) -> Result<()> { + pub(crate) fn check_compatible(mut self, engine: &Engine) -> Result<()> { self.check_triple(engine)?; self.check_shared_flags(engine)?; self.check_isa_flags(engine)?; @@ -485,173 +367,3 @@ impl Metadata<'_> { Ok(()) } } - -#[cfg(test)] -mod test { - use super::*; - use crate::Config; - - #[test] - fn test_architecture_mismatch() -> Result<()> { - let engine = Engine::default(); - let mut metadata = Metadata::new(&engine); - metadata.target = "unknown-generic-linux".to_string(); - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert_eq!( - e.to_string(), - "Module was compiled for architecture 'unknown'", - ), - } - - Ok(()) - } - - #[test] - fn test_os_mismatch() -> Result<()> { - let engine = Engine::default(); - let mut metadata = Metadata::new(&engine); - - metadata.target = format!( - "{}-generic-unknown", - target_lexicon::Triple::host().architecture - ); - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert_eq!( - e.to_string(), - "Module was compiled for operating system 'unknown'", - ), - } - - Ok(()) - } - - #[test] - fn test_cranelift_flags_mismatch() -> Result<()> { - let engine = Engine::default(); - let mut metadata = Metadata::new(&engine); - - metadata - .shared_flags - .push(("preserve_frame_pointers", FlagValue::Bool(false))); - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert!(format!("{:?}", e).starts_with( - "\ -compilation settings of module incompatible with native host - -Caused by: - setting \"preserve_frame_pointers\" is configured to Bool(false) which is not supported" - )), - } - - Ok(()) - } - - #[test] - fn test_isa_flags_mismatch() -> Result<()> { - let engine = Engine::default(); - let mut metadata = Metadata::new(&engine); - - metadata - .isa_flags - .push(("not_a_flag", FlagValue::Bool(true))); - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert!(format!("{:?}", e).starts_with( - "\ -compilation settings of module incompatible with native host - -Caused by: - cannot test if target-specific flag \"not_a_flag\" is available at runtime", - )), - } - - Ok(()) - } - - #[test] - #[cfg_attr(miri, ignore)] - fn test_tunables_int_mismatch() -> Result<()> { - let engine = Engine::default(); - let mut metadata = Metadata::new(&engine); - - metadata.tunables.static_memory_offset_guard_size = 0; - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert_eq!(e.to_string(), "Module was compiled with a static memory guard size of '0' but '2147483648' is expected for the host"), - } - - Ok(()) - } - - #[test] - fn test_tunables_bool_mismatch() -> Result<()> { - let mut config = Config::new(); - config.epoch_interruption(true); - - let engine = Engine::new(&config)?; - let mut metadata = Metadata::new(&engine); - metadata.tunables.epoch_interruption = false; - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert_eq!( - e.to_string(), - "Module was compiled without epoch interruption but it is enabled for the host" - ), - } - - let mut config = Config::new(); - config.epoch_interruption(false); - - let engine = Engine::new(&config)?; - let mut metadata = Metadata::new(&engine); - metadata.tunables.epoch_interruption = true; - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert_eq!( - e.to_string(), - "Module was compiled with epoch interruption but it is not enabled for the host" - ), - } - - Ok(()) - } - - #[test] - fn test_feature_mismatch() -> Result<()> { - let mut config = Config::new(); - config.wasm_threads(true); - - let engine = Engine::new(&config)?; - let mut metadata = Metadata::new(&engine); - metadata.features.threads = false; - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert_eq!(e.to_string(), "Module was compiled without WebAssembly threads support but it is enabled for the host"), - } - - let mut config = Config::new(); - config.wasm_threads(false); - - let engine = Engine::new(&config)?; - let mut metadata = Metadata::new(&engine); - metadata.features.threads = true; - - match metadata.check_compatible(&engine) { - Ok(_) => unreachable!(), - Err(e) => assert_eq!(e.to_string(), "Module was compiled with WebAssembly threads support but it is not enabled for the host"), - } - - Ok(()) - } -} diff --git a/crates/wasmtime/src/signatures.rs b/crates/wasmtime/src/runtime/signatures.rs similarity index 100% rename from crates/wasmtime/src/signatures.rs rename to crates/wasmtime/src/runtime/signatures.rs diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/runtime/store.rs similarity index 100% rename from crates/wasmtime/src/store.rs rename to crates/wasmtime/src/runtime/store.rs diff --git a/crates/wasmtime/src/store/context.rs b/crates/wasmtime/src/runtime/store/context.rs similarity index 100% rename from crates/wasmtime/src/store/context.rs rename to crates/wasmtime/src/runtime/store/context.rs diff --git a/crates/wasmtime/src/store/data.rs b/crates/wasmtime/src/runtime/store/data.rs similarity index 100% rename from crates/wasmtime/src/store/data.rs rename to crates/wasmtime/src/runtime/store/data.rs diff --git a/crates/wasmtime/src/store/func_refs.rs b/crates/wasmtime/src/runtime/store/func_refs.rs similarity index 100% rename from crates/wasmtime/src/store/func_refs.rs rename to crates/wasmtime/src/runtime/store/func_refs.rs diff --git a/crates/wasmtime/src/trampoline.rs b/crates/wasmtime/src/runtime/trampoline.rs similarity index 100% rename from crates/wasmtime/src/trampoline.rs rename to crates/wasmtime/src/runtime/trampoline.rs diff --git a/crates/wasmtime/src/trampoline/func.rs b/crates/wasmtime/src/runtime/trampoline/func.rs similarity index 98% rename from crates/wasmtime/src/trampoline/func.rs rename to crates/wasmtime/src/runtime/trampoline/func.rs index fdc77222b249..4fc6902c5571 100644 --- a/crates/wasmtime/src/trampoline/func.rs +++ b/crates/wasmtime/src/runtime/trampoline/func.rs @@ -105,7 +105,9 @@ where let mut code_memory = CodeMemory::new(obj)?; code_memory.publish()?; - engine.profiler().register_module(&code_memory, &|_| None); + engine + .profiler() + .register_module(&code_memory.mmap()[..], &|_| None); // Extract the host/wasm trampolines from the results of compilation since // we know their start/length. diff --git a/crates/wasmtime/src/trampoline/global.rs b/crates/wasmtime/src/runtime/trampoline/global.rs similarity index 100% rename from crates/wasmtime/src/trampoline/global.rs rename to crates/wasmtime/src/runtime/trampoline/global.rs diff --git a/crates/wasmtime/src/trampoline/memory.rs b/crates/wasmtime/src/runtime/trampoline/memory.rs similarity index 100% rename from crates/wasmtime/src/trampoline/memory.rs rename to crates/wasmtime/src/runtime/trampoline/memory.rs diff --git a/crates/wasmtime/src/trampoline/table.rs b/crates/wasmtime/src/runtime/trampoline/table.rs similarity index 100% rename from crates/wasmtime/src/trampoline/table.rs rename to crates/wasmtime/src/runtime/trampoline/table.rs diff --git a/crates/wasmtime/src/trap.rs b/crates/wasmtime/src/runtime/trap.rs similarity index 100% rename from crates/wasmtime/src/trap.rs rename to crates/wasmtime/src/runtime/trap.rs diff --git a/crates/wasmtime/src/types.rs b/crates/wasmtime/src/runtime/types.rs similarity index 100% rename from crates/wasmtime/src/types.rs rename to crates/wasmtime/src/runtime/types.rs diff --git a/crates/wasmtime/src/types/matching.rs b/crates/wasmtime/src/runtime/types/matching.rs similarity index 100% rename from crates/wasmtime/src/types/matching.rs rename to crates/wasmtime/src/runtime/types/matching.rs diff --git a/crates/wasmtime/src/unix.rs b/crates/wasmtime/src/runtime/unix.rs similarity index 100% rename from crates/wasmtime/src/unix.rs rename to crates/wasmtime/src/runtime/unix.rs diff --git a/crates/wasmtime/src/unwind.rs b/crates/wasmtime/src/runtime/unwind.rs similarity index 100% rename from crates/wasmtime/src/unwind.rs rename to crates/wasmtime/src/runtime/unwind.rs diff --git a/crates/wasmtime/src/unwind/miri.rs b/crates/wasmtime/src/runtime/unwind/miri.rs similarity index 100% rename from crates/wasmtime/src/unwind/miri.rs rename to crates/wasmtime/src/runtime/unwind/miri.rs diff --git a/crates/wasmtime/src/unwind/systemv.rs b/crates/wasmtime/src/runtime/unwind/systemv.rs similarity index 100% rename from crates/wasmtime/src/unwind/systemv.rs rename to crates/wasmtime/src/runtime/unwind/systemv.rs diff --git a/crates/wasmtime/src/unwind/winx64.rs b/crates/wasmtime/src/runtime/unwind/winx64.rs similarity index 100% rename from crates/wasmtime/src/unwind/winx64.rs rename to crates/wasmtime/src/runtime/unwind/winx64.rs diff --git a/crates/wasmtime/src/v128.rs b/crates/wasmtime/src/runtime/v128.rs similarity index 100% rename from crates/wasmtime/src/v128.rs rename to crates/wasmtime/src/runtime/v128.rs diff --git a/crates/wasmtime/src/values.rs b/crates/wasmtime/src/runtime/values.rs similarity index 100% rename from crates/wasmtime/src/values.rs rename to crates/wasmtime/src/runtime/values.rs diff --git a/crates/wasmtime/src/windows.rs b/crates/wasmtime/src/runtime/windows.rs similarity index 100% rename from crates/wasmtime/src/windows.rs rename to crates/wasmtime/src/runtime/windows.rs diff --git a/crates/winch/src/builder.rs b/crates/winch/src/builder.rs index d9d534cc7a74..2f9e5010928b 100644 --- a/crates/winch/src/builder.rs +++ b/crates/winch/src/builder.rs @@ -1,6 +1,7 @@ use crate::compiler::Compiler; use anyhow::{bail, Result}; use std::sync::Arc; +use target_lexicon::Triple; use wasmtime_cranelift_shared::isa_builder::IsaBuilder; use wasmtime_environ::{CompilerBuilder, Setting}; use winch_codegen::{isa, TargetIsa}; @@ -10,10 +11,10 @@ struct Builder { inner: IsaBuilder>>, } -pub fn builder() -> Box { - Box::new(Builder { - inner: IsaBuilder::new(|triple| isa::lookup(triple).map_err(|e| e.into())), - }) +pub fn builder(triple: Option) -> Result> { + Ok(Box::new(Builder { + inner: IsaBuilder::new(triple, |triple| isa::lookup(triple).map_err(|e| e.into()))?, + })) } impl CompilerBuilder for Builder { diff --git a/src/commands/settings.rs b/src/commands/settings.rs index 600a23454649..0dfc1721dbb1 100644 --- a/src/commands/settings.rs +++ b/src/commands/settings.rs @@ -108,7 +108,7 @@ impl SettingsCommand { /// Executes the command. pub fn execute(self) -> Result<()> { // Gather settings from the cranelift compiler builder - let mut builder = wasmtime_cranelift::builder(); + let mut builder = wasmtime_cranelift::builder(None)?; if let Some(target) = &self.target { let target = target_lexicon::Triple::from_str(target).map_err(|e| anyhow!(e))?; builder.target(target)?;