diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b18fbcf0a958..0562a7f9dad3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -570,6 +570,27 @@ jobs: GH_TOKEN: ${{ github.token }} + build-wasmtime-target-wasm32: + name: Build wasmtime-target-wasm32 + needs: determine + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: rustup update stable && rustup default stable + - run: rustup target add wasm32-wasi wasm32-unknown-unknown + - run: cargo build -p wasmtime --target wasm32-wasi --no-default-features --features cranelift,all-arch + env: + VERSION: ${{ github.sha }} + + # common logic to cancel the entire run if this job fails + - run: gh run cancel ${{ github.run_id }} + if: failure() && github.event_name != 'pull_request' + env: + GH_TOKEN: ${{ github.token }} + + bench: needs: determine if: needs.determine.outputs.run-full diff --git a/cranelift/codegen/build.rs b/cranelift/codegen/build.rs index 0377711cd23f..d5c5f7a57469 100644 --- a/cranelift/codegen/build.rs +++ b/cranelift/codegen/build.rs @@ -28,16 +28,19 @@ fn main() { let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"); let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set"); + let all_arch = env::var("CARGO_FEATURE_ALL_ARCH").is_ok(); + let mut isas = meta::isa::Isa::all() .iter() .cloned() .filter(|isa| { let env_key = format!("CARGO_FEATURE_{}", isa.to_string().to_uppercase()); - env::var(env_key).is_ok() + all_arch || env::var(env_key).is_ok() }) .collect::>(); - let host_isa = env::var("CARGO_FEATURE_HOST_ARCH").is_ok(); + // Don't require host isa if under 'all-arch' feature. + let host_isa = env::var("CARGO_FEATURE_HOST_ARCH").is_ok() && !all_arch; if isas.is_empty() || host_isa { // Try to match native target. diff --git a/cranelift/native/src/lib.rs b/cranelift/native/src/lib.rs index f1f9f27d8b6c..cd2973fa4a5f 100644 --- a/cranelift/native/src/lib.rs +++ b/cranelift/native/src/lib.rs @@ -144,6 +144,10 @@ 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); } + + // On all other architectures (e.g. wasm32) we won't infer any native flags, + // but still need to use the `isa_builder` to avoid compiler warnings. + let _ = isa_builder; Ok(()) } diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml index 60f6662ef802..ecc21c4cc0c0 100644 --- a/crates/c-api/Cargo.toml +++ b/crates/c-api/Cargo.toml @@ -22,7 +22,7 @@ doctest = false env_logger = { workspace = true, optional = true } anyhow = { workspace = true } once_cell = { workspace = true } -wasmtime = { workspace = true, features = ['cranelift'] } +wasmtime = { workspace = true, features = ['cranelift', 'runtime'] } wasmtime-c-api-macros = { path = "macros" } log = { workspace = true } tracing = { workspace = true } 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 354e29e163bd..cde0d4e711f5 100644 --- a/crates/cranelift/Cargo.toml +++ b/crates/cranelift/Cargo.toml @@ -34,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/explorer/Cargo.toml b/crates/explorer/Cargo.toml index 3ecd9e9eea15..dcda32acc279 100644 --- a/crates/explorer/Cargo.toml +++ b/crates/explorer/Cargo.toml @@ -19,4 +19,4 @@ serde_derive = { workspace = true } serde_json = { workspace = true } target-lexicon = { workspace = true } wasmprinter = { workspace = true } -wasmtime = { workspace = true, features = ["cranelift"] } +wasmtime = { workspace = true, features = ["cranelift", "runtime"] } diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 33fc29cc050e..28559f932eea 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -69,6 +69,7 @@ exit = [] preview2 = [ 'wasmtime/component-model', 'wasmtime/async', + 'wasmtime/runtime', 'dep:thiserror', 'dep:tracing', 'dep:cap-std', diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 25d96a9f02da..617c6679119d 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -21,7 +21,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,7 @@ default = [ 'addr2line', 'coredump', 'debug-builtins', + 'runtime', ] # An on-by-default feature enabling runtime compilation of WebAssembly modules @@ -132,18 +133,16 @@ async = [ "wasmtime-runtime/async", "dep:async-trait", "wasmtime-component-macro?/async", + "runtime", ] # 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", -] +all-arch = ["wasmtime-cranelift?/all-arch", "wasmtime-winch?/all-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 @@ -152,21 +151,24 @@ 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. demangle = ["wasmtime-environ/demangle"] # Enable support for generating core dumps on traps. -coredump = ["dep:wasm-encoder"] +coredump = ["dep:wasm-encoder", "runtime"] # 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"] + +# Enable support for executing compiled Wasm modules. +runtime = ["dep:wasmtime-runtime"] diff --git a/crates/wasmtime/src/compiler.rs b/crates/wasmtime/src/compile.rs similarity index 76% rename from crates/wasmtime/src/compiler.rs rename to crates/wasmtime/src/compile.rs index 5b8d43fd6eea..752c91924761 100644 --- a/crates/wasmtime/src/compiler.rs +++ b/crates/wasmtime/src/compile.rs @@ -22,16 +22,203 @@ //! functions. It is up to the caller to serialize the relevant parts of the //! `Artifacts` into the ELF file. -use crate::Engine; -use anyhow::Result; -use std::collections::{btree_map, BTreeMap, BTreeSet}; -use std::{any::Any, collections::HashMap}; +use std::{ + any::Any, + collections::{btree_map, BTreeMap, BTreeSet, HashMap}, + mem, +}; + +use anyhow::{Context, Result}; +use object::{ + write::{Object, StandardSegment}, + SectionKind, +}; +#[cfg(feature = "component-model")] +use wasmtime_environ::component::Translator; use wasmtime_environ::{ - CompiledFunctionInfo, CompiledModuleInfo, Compiler, DefinedFuncIndex, FuncIndex, - FunctionBodyData, ModuleInternedTypeIndex, ModuleTranslation, ModuleType, ModuleTypesBuilder, - PrimaryMap, StaticModuleIndex, WasmFunctionInfo, + obj, CompiledFunctionInfo, CompiledModuleInfo, Compiler, DefinedFuncIndex, FinishedObject, + FuncIndex, FunctionBodyData, ModuleEnvironment, ModuleInternedTypeIndex, ModuleTranslation, + ModuleType, ModuleTypes, ModuleTypesBuilder, ObjectKind, PrimaryMap, StaticModuleIndex, + WasmFunctionInfo, }; +#[cfg(feature = "component-model")] +use crate::component_artifacts::{CompiledComponentInfo, ComponentArtifacts}; +use crate::{Engine, Metadata, ModuleVersionStrategy, 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 compile_inputs = CompileInputs::for_module(&types, &translation, functions); + let unlinked_compile_outputs = compile_inputs.compile(engine)?; + let types = types.finish(); + 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 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 (types, ty) = types.finish( + &compilation_artifacts.modules, + component + .component + .import_types + .iter() + .map(|(_, (name, ty))| (name.clone(), *ty)), + component + .component + .exports + .iter() + .map(|(name, ty)| (name.clone(), ty)), + ); + + 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, + ty, + types, + static_modules: compilation_artifacts.modules, + }; + object.serialize_info(&artifacts); + + let result = T::finish_object(object)?; + Ok((result, artifacts)) +} + +/// 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(crate) 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); +} + type CompileInput<'a> = Box Result + Send + 'a>; /// A sortable, comparable key for a compilation output. @@ -165,7 +352,7 @@ struct CompileOutput { /// The collection of things we need to compile for a Wasm module or component. #[derive(Default)] -pub struct CompileInputs<'a> { +struct CompileInputs<'a> { inputs: Vec>, } @@ -175,7 +362,7 @@ impl<'a> CompileInputs<'a> { } /// Create the `CompileInputs` for a core Wasm module. - pub fn for_module( + fn for_module( types: &'a ModuleTypesBuilder, translation: &'a ModuleTranslation<'a>, functions: PrimaryMap>, @@ -190,7 +377,7 @@ impl<'a> CompileInputs<'a> { /// Create a `CompileInputs` for a component. #[cfg(feature = "component-model")] - pub fn for_component( + fn for_component( types: &'a wasmtime_environ::component::ComponentTypesBuilder, component: &'a wasmtime_environ::component::ComponentTranslation, module_translations: impl IntoIterator< @@ -339,7 +526,7 @@ impl<'a> CompileInputs<'a> { /// Compile these `CompileInput`s (maybe in parallel) and return the /// resulting `UnlinkedCompileOutput`s. - pub fn compile(self, engine: &Engine) -> Result { + fn compile(self, engine: &Engine) -> Result { let compiler = engine.compiler(); // Compile each individual input in parallel. @@ -370,7 +557,7 @@ impl<'a> CompileInputs<'a> { } #[derive(Default)] -pub struct UnlinkedCompileOutputs { +struct UnlinkedCompileOutputs { // A map from kind to `CompileOutput`. outputs: BTreeMap>, } @@ -378,7 +565,7 @@ pub struct UnlinkedCompileOutputs { impl UnlinkedCompileOutputs { /// Flatten all our functions into a single list and remember each of their /// indices within it. - pub fn pre_link(self) -> (Vec<(String, Box)>, FunctionIndices) { + fn pre_link(self) -> (Vec<(String, Box)>, FunctionIndices) { // The order the functions end up within `compiled_funcs` is the order // that they will be laid out in the ELF file, so try and group hot and // cold functions together as best we can. However, because we bucket by @@ -432,7 +619,7 @@ impl UnlinkedCompileOutputs { } #[derive(Default)] -pub struct FunctionIndices { +struct FunctionIndices { // A reverse map from an index in `compiled_funcs` to the // `StaticModuleIndex` for that function. compiled_func_index_to_module: HashMap, @@ -447,7 +634,7 @@ pub struct FunctionIndices { impl FunctionIndices { /// Link the compiled functions together, resolving relocations, and append /// them to the given ELF file. - pub fn link_and_append_code<'a>( + fn link_and_append_code<'a>( mut self, mut obj: object::write::Object<'static>, engine: &'a Engine, @@ -662,21 +849,21 @@ impl FunctionIndices { /// The artifacts necessary for finding and calling Wasm functions at runtime, /// to be serialized into an ELF file. #[derive(Default)] -pub struct Artifacts { - pub modules: PrimaryMap, +struct Artifacts { + modules: PrimaryMap, #[cfg(feature = "component-model")] - pub trampolines: PrimaryMap< + trampolines: PrimaryMap< wasmtime_environ::component::TrampolineIndex, wasmtime_environ::component::AllCallFunc, >, #[cfg(feature = "component-model")] - pub resource_drop_wasm_to_native_trampoline: Option, + resource_drop_wasm_to_native_trampoline: Option, } impl Artifacts { /// Assuming this compilation was for a single core Wasm module, get the /// resulting `CompiledModuleInfo`. - pub fn unwrap_as_module_info(self) -> CompiledModuleInfo { + fn unwrap_as_module_info(self) -> CompiledModuleInfo { assert_eq!(self.modules.len(), 1); #[cfg(feature = "component-model")] assert!(self.trampolines.is_empty()); diff --git a/crates/wasmtime/src/component_artifacts.rs b/crates/wasmtime/src/component_artifacts.rs new file mode 100644 index 000000000000..124674abfd14 --- /dev/null +++ b/crates/wasmtime/src/component_artifacts.rs @@ -0,0 +1,38 @@ +#![cfg(feature = "component-model")] + +use serde_derive::{Deserialize, Serialize}; +use wasmtime_environ::{ + component::{AllCallFunc, ComponentTypes, TrampolineIndex, TypeComponentIndex}, + CompiledModuleInfo, FunctionLoc, PrimaryMap, StaticModuleIndex, +}; + +#[derive(Serialize, Deserialize)] +pub(crate) struct ComponentArtifacts { + pub(crate) ty: TypeComponentIndex, + 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 221c45470753..75a1c616fe43 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1,6 +1,3 @@ -use crate::memory::MemoryCreator; -use crate::profiling_agent::{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_agent::{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 @@ -1108,6 +1116,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 @@ -1607,6 +1616,7 @@ impl Config { Ok(()) } + #[cfg(feature = "runtime")] pub(crate) fn build_allocator(&self) -> Result> { #[cfg(feature = "async")] let stack_size = self.async_stack_size; @@ -1639,6 +1649,7 @@ impl Config { } } + #[cfg(feature = "runtime")] pub(crate) fn build_profiler(&self) -> Result> { Ok(match self.profiling_strategy { ProfilingStrategy::PerfMap => profiling_agent::new_perfmap()?, @@ -1650,25 +1661,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)?; } @@ -1813,6 +1822,15 @@ impl Config { } } +/// If building without the runtime feature we can't determine the page size of +/// the platform where the execution will happen so just keep the original +/// values. +#[cfg(not(feature = "runtime"))] +fn round_up_to_pages(val: u64) -> u64 { + val +} + +#[cfg(feature = "runtime")] fn round_up_to_pages(val: u64) -> u64 { let page_size = wasmtime_runtime::page_size() as u64; debug_assert!(page_size.is_power_of_two()); diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 0cc5204e1a14..436ef58f1870 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -1,20 +1,19 @@ -use crate::{profiling_agent::ProfilingAgent, type_registry::TypeRegistry, 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_agent::ProfilingAgent, runtime::type_registry::TypeRegistry}; -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,33 @@ 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: TypeRegistry, - 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: TypeRegistry, + 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 Default for Engine { + fn default() -> Engine { + Engine::new(&Config::default()).unwrap() + } } impl Engine { @@ -72,38 +82,190 @@ 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 = TypeRegistry::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: TypeRegistry::new(), epoch: AtomicU64::new(0), + #[cfg(feature = "runtime")] unique_id_allocator: CompiledModuleIdAllocator::new(), + #[cfg(feature = "runtime")] compatible_with_native_host: OnceCell::new(), + config, }), }) } + /// Returns the configuration settings that this engine is using. + #[inline] + pub fn config(&self) -> &Config { + &self.inner.config + } + + pub(crate) fn run_maybe_parallel< + A: Send, + B: Send, + E: Send, + F: Fn(A) -> Result + Send + Sync, + >( + &self, + input: Vec, + f: F, + ) -> Result, E> { + if self.config().parallel_compilation { + #[cfg(feature = "parallel-compilation")] + return input + .into_par_iter() + .map(|a| f(a)) + .collect::, E>>(); + } + + // In case the parallel-compilation feature is disabled or the parallel_compilation config + // was turned off dynamically fallback to the non-parallel version. + input + .into_iter() + .map(|a| f(a)) + .collect::, E>>() + } + + /// Take a weak reference to this engine. + pub fn weak(&self) -> EngineWeak { + EngineWeak { + inner: Arc::downgrade(&self.inner), + } + } +} + +cfg_if::cfg_if! { + if #[cfg(feature = "runtime")] { + use anyhow::Context; + use std::path::Path; + use std::sync::atomic::Ordering; + #[cfg(feature = "cache")] + use wasmtime_cache::CacheConfig; + use wasmtime_environ::ObjectKind; + use wasmtime_runtime::MmapVec; + + use crate::CodeMemory; + + mod serialization; + } +} + +cfg_if::cfg_if! { + if #[cfg(any(feature = "cranelift", feature = "winch"))] { + use object::write::{Object, StandardSegment}; + use object::SectionKind; + use wasmtime_environ::obj; + + use crate::append_compiler_info; + } +} + +#[cfg(any(feature = "cranelift", feature = "winch"))] +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`](crate::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::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::build_component_artifacts::>(self, &bytes)?; + Ok(v) + } +} + +/// Return value from the [`Engine::detect_precompiled`] API. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg(feature = "runtime")] +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(feature = "runtime")] +impl Engine { /// Eagerly initialize thread-local functionality shared by all [`Engine`]s. /// /// Wasmtime's implementation on some platforms may involve per-thread @@ -125,17 +287,6 @@ impl Engine { 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() } @@ -181,9 +332,10 @@ impl Engine { /// to the other relevant methods. /// /// When performing `increment_epoch` in a separate thread, consider using - /// [`Engine::weak`] to hold an [`EngineWeak`] and performing - /// [`EngineWeak::upgrade`] on each tick, so that the epoch ticking thread - /// does not keep an [`Engine`] alive longer than any of its consumers. + /// [`Engine::weak`] to hold an [`EngineWeak`](crate::EngineWeak) and + /// performing [`EngineWeak::upgrade`](crate::EngineWeak::upgrade) on each + /// tick, so that the epoch ticking thread does not keep an [`Engine`] alive + /// longer than any of its consumers. /// /// ## Signal Safety /// @@ -198,50 +350,6 @@ impl Engine { &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`] @@ -254,32 +362,6 @@ impl Engine { crate::module::HashedEngineCompileEnv(self) } - pub(crate) fn run_maybe_parallel< - A: Send, - B: Send, - E: Send, - F: Fn(A) -> Result + Send + Sync, - >( - &self, - input: Vec, - f: F, - ) -> Result, E> { - if self.config().parallel_compilation { - #[cfg(feature = "parallel-compilation")] - return input - .into_par_iter() - .map(|a| f(a)) - .collect::, E>>(); - } - - // In case the parallel-compilation feature is disabled or the parallel_compilation config - // was turned off dynamically fallback to the non-parallel version. - input - .into_iter() - .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 @@ -581,26 +663,6 @@ impl Engine { )) } - #[cfg(any(feature = "cranelift", feature = "winch"))] - pub(crate) fn append_compiler_info(&self, obj: &mut Object<'_>) { - serialization::append_compiler_info(self, obj); - } - - #[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); - } - /// Loads a `CodeMemory` from the specified in-memory slice, copying it to a /// uniquely owned mmap. /// @@ -659,28 +721,6 @@ impl Engine { pub fn detect_precompiled_file(&self, path: impl AsRef) -> Result> { serialization::detect_precompiled_file(path) } - - /// Take a weak reference to this engine. - pub fn weak(&self) -> EngineWeak { - EngineWeak { - inner: Arc::downgrade(&self.inner), - } - } -} - -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, } /// A weak reference to an [`Engine`]. @@ -697,17 +737,282 @@ impl EngineWeak { } } +#[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, +} + +#[cfg(any(feature = "cranelift", feature = "winch"))] +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, + component_model_nested_names, + + // 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); + assert!(!component_model_nested_names); + + 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, + }, + } + } +} +// 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, +} + #[cfg(test)] -mod tests { +mod test { + use super::*; + + use crate::{Config, Engine, Module, ModuleVersionStrategy, OptLevel}; + + use anyhow::Result; + use tempfile::TempDir; + use std::{ collections::hash_map::DefaultHasher, hash::{Hash, Hasher}, }; - use crate::{Config, Engine, Module, ModuleVersionStrategy, OptLevel}; + #[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'", + ), + } - use anyhow::Result; - use tempfile::TempDir; + 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(()) + } + + #[test] + fn engine_weak_upgrades() { + let engine = Engine::default(); + let weak = engine.weak(); + weak.upgrade() + .expect("engine is still alive, so weak reference can upgrade"); + drop(engine); + assert!( + weak.upgrade().is_none(), + "engine was dropped, so weak reference cannot upgrade" + ); + } #[test] #[cfg_attr(miri, ignore)] @@ -813,17 +1118,4 @@ mod tests { Ok(()) } - - #[test] - fn engine_weak_upgrades() { - let engine = Engine::default(); - let weak = engine.weak(); - weak.upgrade() - .expect("engine is still alive, so weak reference can upgrade"); - drop(engine); - assert!( - weak.upgrade().is_none(), - "engine was dropped, so weak reference cannot upgrade" - ); - } } diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 6e891d549dcf..00641595086d 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/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,93 +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, - component_model_nested_names, - - // 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); - assert!(!component_model_nested_names); - - 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)?; @@ -487,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/lib.rs b/crates/wasmtime/src/lib.rs index 0173f61b484f..a5e9d0edc7fe 100644 --- a/crates/wasmtime/src/lib.rs +++ b/crates/wasmtime/src/lib.rs @@ -410,72 +410,24 @@ // and will prevent the doc build from failing. #![cfg_attr(feature = "default", deny(rustdoc::broken_intra_doc_links))] -#[macro_use] -mod func; +#[cfg(feature = "runtime")] +mod runtime; +#[cfg(feature = "runtime")] +pub use runtime::*; #[cfg(any(feature = "cranelift", feature = "winch"))] -mod compiler; +mod compile; +#[cfg(any(feature = "cranelift", feature = "winch"))] +use compile::*; -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 profiling_agent; -mod r#ref; -mod resources; -mod store; -mod trampoline; -mod trap; -mod type_registry; -mod types; -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, ModuleExport}; -#[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::*; - -#[cfg(feature = "coredump")] -mod coredump; -#[cfg(feature = "coredump")] -pub use crate::coredump::*; /// A convenience wrapper for `Result`. /// @@ -484,49 +436,9 @@ 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(miri)] { - // no extensions on miri - } else 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::(); - _assert::(); - _assert::<(Func, TypedFunc<(), ()>, Global, Table, Memory)>(); - _assert::(); - _assert::(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::>(); - _assert::(); - _assert::>(); - _assert::>(); +fn _assert_send_and_sync() {} - #[cfg(feature = "async")] - fn _call_async(s: &mut Store<()>, f: Func) { - _assert_send(f.call_async(&mut *s, &[], &mut [])) - } - #[cfg(feature = "async")] - fn _typed_call_async(s: &mut Store<()>, f: TypedFunc<(), ()>) { - _assert_send(f.call_async(&mut *s, ())) - } - #[cfg(feature = "async")] - fn _instantiate_async(s: &mut Store<()>, m: &Module) { - _assert_send(Instance::new_async(s, m, &[])) - } +fn _assertions_lib() { + _assert_send_and_sync::(); + _assert_send_and_sync::(); } diff --git a/crates/wasmtime/src/profiling_agent.rs b/crates/wasmtime/src/profiling_agent.rs index 71d02e59a704..316f243627ad 100644 --- a/crates/wasmtime/src/profiling_agent.rs +++ b/crates/wasmtime/src/profiling_agent.rs @@ -1,8 +1,6 @@ #[allow(unused)] use anyhow::{bail, Result}; -use crate::CodeMemory; - cfg_if::cfg_if! { if #[cfg(all(feature = "profiling", target_os = "linux"))] { mod jitdump; @@ -50,10 +48,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, }; @@ -103,5 +101,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..c92187e81304 --- /dev/null +++ b/crates/wasmtime/src/runtime.rs @@ -0,0 +1,107 @@ +#[macro_use] +pub(crate) mod func; + +pub(crate) mod code; +pub(crate) mod code_memory; +pub(crate) mod debug; +pub(crate) mod externals; +pub(crate) mod instance; +pub(crate) mod instantiate; +pub(crate) mod limits; +pub(crate) mod linker; +pub(crate) mod memory; +pub(crate) mod module; +pub(crate) mod profiling; +pub(crate) mod r#ref; +pub(crate) mod resources; +pub(crate) mod store; +pub(crate) mod trampoline; +pub(crate) mod trap; +pub(crate) mod type_registry; +pub(crate) mod types; +pub(crate) mod v128; +pub(crate) mod values; + +#[cfg(feature = "component-model")] +pub mod component; + +#[cfg(feature = "async")] +pub(crate) mod stack; + +#[cfg(feature = "coredump")] +mod coredump; + +cfg_if::cfg_if! { + if #[cfg(miri)] { + // no extensions on miri + } else if #[cfg(unix)] { + pub mod unix; + } else if #[cfg(windows)] { + pub mod windows; + } else { + // ... unknown os! + } +} + +pub use code_memory::CodeMemory; +pub use externals::*; +pub use func::*; +pub use instance::{Instance, InstancePre}; +pub use instantiate::CompiledModule; +pub use limits::*; +pub use linker::*; +pub use memory::*; +pub use module::{Module, ModuleExport}; +pub use r#ref::ExternRef; +pub use resources::*; +#[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 = "profiling")] +pub use profiling::GuestProfiler; + +#[cfg(feature = "async")] +pub use stack::*; + +#[cfg(feature = "coredump")] +pub use coredump::*; + +fn _assertions_runtime() { + use crate::_assert_send_and_sync; + + #[cfg(feature = "async")] + fn _assert_send(_t: T) {} + + _assert_send_and_sync::>(); + _assert_send_and_sync::(); + _assert_send_and_sync::<(Func, TypedFunc<(), ()>, Global, Table, Memory)>(); + _assert_send_and_sync::(); + _assert_send_and_sync::>(); + _assert_send_and_sync::>(); + _assert_send_and_sync::>(); + _assert_send_and_sync::>(); + _assert_send_and_sync::(); + _assert_send_and_sync::>(); + _assert_send_and_sync::>(); + _assert_send_and_sync::>(); + + #[cfg(feature = "async")] + fn _call_async(s: &mut Store<()>, f: Func) { + _assert_send(f.call_async(&mut *s, &[], &mut [])) + } + #[cfg(feature = "async")] + fn _typed_call_async(s: &mut Store<()>, f: TypedFunc<(), ()>) { + _assert_send(f.call_async(&mut *s, ())) + } + #[cfg(feature = "async")] + fn _instantiate_async(s: &mut Store<()>, m: &Module) { + _assert_send(Instance::new_async(s, m, &[])) + } +} 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 07508f044cd9..fb75914c5fc3 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, - type_registry::TypeCollection, Engine, Module, ResourcesRequired, + code::CodeObject, + code_memory::CodeMemory, + component_artifacts::{CompiledComponentInfo, ComponentArtifacts}, + instantiate::MmapVecWrapper, + type_registry::TypeCollection, + 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, TypeComponentIndex, VMComponentOffsets, -}; -use wasmtime_environ::{ - CompiledModuleInfo, FunctionLoc, HostPtr, ObjectKind, PrimaryMap, ScopeVec, + TrampolineIndex, TypeComponentIndex, 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. @@ -49,43 +49,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 { - ty: TypeComponentIndex, - info: CompiledComponentInfo, - types: ComponentTypes, - static_modules: PrimaryMap, -} - impl Component { /// Compiles a new WebAssembly component from the in-memory wasm image /// provided. @@ -133,6 +102,9 @@ 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 wasmtime_runtime::MmapVec; + + use crate::build_component_artifacts; use crate::module::HashedEngineCompileEnv; engine @@ -151,8 +123,8 @@ impl Component { // Cache miss, compute the actual artifacts |(engine, wasm)| -> Result<_> { - let (mmap, artifacts) = Component::build_artifacts(engine.0, wasm)?; - let code = publish_mmap(mmap)?; + let (mmap, artifacts) = build_component_artifacts::(engine.0, wasm)?; + let code = publish_mmap(mmap.0)?; Ok((code, Some(artifacts))) }, @@ -168,9 +140,9 @@ impl Component { }, )?; } else { - let (mmap, artifacts) = Component::build_artifacts(engine, binary)?; + let (mmap, artifacts) = build_component_artifacts::(engine, binary)?; let artifacts = Some(artifacts); - let code = publish_mmap(mmap)?; + let code = publish_mmap(mmap.0)?; } }; @@ -208,86 +180,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 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 (types, ty) = types.finish( - &compilation_artifacts.modules, - component - .component - .import_types - .iter() - .map(|(_, (name, ty))| (name.clone(), *ty)), - component - .component - .exports - .iter() - .map(|(name, ty)| (name.clone(), ty)), - ); - - 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, - ty, - 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/resource_table.rs b/crates/wasmtime/src/runtime/component/resource_table.rs similarity index 100% rename from crates/wasmtime/src/component/resource_table.rs rename to crates/wasmtime/src/runtime/component/resource_table.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/coredump.rs b/crates/wasmtime/src/runtime/coredump.rs similarity index 100% rename from crates/wasmtime/src/coredump.rs rename to crates/wasmtime/src/runtime/coredump.rs diff --git a/crates/wasmtime/src/debug.rs b/crates/wasmtime/src/runtime/debug.rs similarity index 100% rename from crates/wasmtime/src/debug.rs rename to crates/wasmtime/src/runtime/debug.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 b3c169a2168d..ee9d364076c9 100644 --- a/crates/wasmtime/src/instantiate.rs +++ b/crates/wasmtime/src/runtime/instantiate.rs @@ -84,7 +84,7 @@ impl CompiledModule { let reg = wasmtime_runtime::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)?; @@ -364,7 +364,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/limits.rs b/crates/wasmtime/src/runtime/limits.rs similarity index 100% rename from crates/wasmtime/src/limits.rs rename to crates/wasmtime/src/runtime/limits.rs 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 4e8f45b54c81..172f611253fc 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, EntityIndex, HostPtr, - ModuleEnvironment, ModuleTypes, ObjectKind, VMOffsets, + CompiledModuleInfo, DefinedFuncIndex, DefinedMemoryIndex, EntityIndex, 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::{build_artifacts, 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)) }, @@ -335,8 +337,8 @@ impl Module { }, )?; } else { - let (mmap, info_and_types) = Module::build_artifacts(engine, binary)?; - let code = publish_mmap(mmap)?; + let (mmap, info_and_types) = build_artifacts::(engine, binary)?; + let code = publish_mmap(mmap.0)?; } }; @@ -383,75 +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::compiler::CompileInputs; - use crate::instantiate::finish_object; - - 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 compile_inputs = CompileInputs::for_module(&types, &translation, functions); - let unlinked_compile_outputs = compile_inputs.compile(engine)?; - let types = types.finish(); - 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/profiling.rs b/crates/wasmtime/src/runtime/profiling.rs similarity index 97% rename from crates/wasmtime/src/profiling.rs rename to crates/wasmtime/src/runtime/profiling.rs index 3d4ef8c39b2d..4dfedfa36ec4 100644 --- a/crates/wasmtime/src/profiling.rs +++ b/crates/wasmtime/src/runtime/profiling.rs @@ -2,9 +2,9 @@ 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; @@ -72,6 +72,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, @@ -81,6 +82,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. @@ -183,6 +185,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); 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/resources.rs b/crates/wasmtime/src/runtime/resources.rs similarity index 100% rename from crates/wasmtime/src/resources.rs rename to crates/wasmtime/src/runtime/resources.rs diff --git a/crates/wasmtime/src/runtime/signatures.rs b/crates/wasmtime/src/runtime/signatures.rs new file mode 100644 index 000000000000..6ce6b0150423 --- /dev/null +++ b/crates/wasmtime/src/runtime/signatures.rs @@ -0,0 +1,216 @@ +//! Implement a registry of function signatures, for fast indirect call +//! signature checking. + +use std::{ + collections::{hash_map::Entry, HashMap}, + sync::RwLock, +}; +use std::{convert::TryFrom, sync::Arc}; +use wasmtime_environ::{ModuleTypes, PrimaryMap, SignatureIndex, WasmFuncType}; +use wasmtime_runtime::VMSharedSignatureIndex; + +/// Represents a collection of shared signatures. +/// +/// This is used to register shared signatures with a shared signature registry. +/// +/// The collection will unregister any contained signatures with the registry +/// when dropped. +#[derive(Debug)] +pub struct SignatureCollection { + registry: Arc>, + signatures: PrimaryMap, + reverse_signatures: HashMap, +} + +impl SignatureCollection { + /// Creates a signature collection for a module given the module's signatures. + pub fn new_for_module(registry: &SignatureRegistry, types: &ModuleTypes) -> Self { + let signatures = registry.0.write().unwrap().register_for_module(types); + let reverse_signatures = signatures.iter().map(|(k, v)| (*v, k)).collect(); + + Self { + registry: registry.0.clone(), + signatures, + reverse_signatures, + } + } + + /// Treats the signature collection as a map from a module signature index to + /// registered shared signature indexes. + /// + /// This is used for looking up module shared signature indexes during module + /// instantiation. + pub fn as_module_map(&self) -> &PrimaryMap { + &self.signatures + } + + /// Gets the shared signature index given a module signature index. + #[inline] + pub fn shared_signature(&self, index: SignatureIndex) -> Option { + self.signatures.get(index).copied() + } + + /// Get the module-local signature index for the given shared signature index. + pub fn local_signature(&self, index: VMSharedSignatureIndex) -> Option { + self.reverse_signatures.get(&index).copied() + } +} + +impl Drop for SignatureCollection { + fn drop(&mut self) { + if !self.signatures.is_empty() { + self.registry.write().unwrap().unregister_signatures(self); + } + } +} + +#[derive(Debug)] +struct RegistryEntry { + references: usize, + ty: WasmFuncType, +} + +#[derive(Debug, Default)] +struct SignatureRegistryInner { + map: HashMap, + entries: Vec>, + free: Vec, +} + +impl SignatureRegistryInner { + fn register_for_module( + &mut self, + types: &ModuleTypes, + ) -> PrimaryMap { + let mut sigs = PrimaryMap::default(); + for (idx, ty) in types.wasm_signatures() { + let b = sigs.push(self.register(ty)); + assert_eq!(idx, b); + } + sigs + } + + fn register(&mut self, ty: &WasmFuncType) -> VMSharedSignatureIndex { + let len = self.map.len(); + + let index = match self.map.entry(ty.clone()) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let (index, entry) = match self.free.pop() { + Some(index) => (index, &mut self.entries[index.bits() as usize]), + None => { + // Keep `index_map` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX) + // is reserved for VMSharedSignatureIndex::default(). + assert!( + len < std::u32::MAX as usize, + "Invariant check: index_map.len() < std::u32::MAX" + ); + debug_assert_eq!(len, self.entries.len()); + + let index = VMSharedSignatureIndex::new(u32::try_from(len).unwrap()); + self.entries.push(None); + + (index, self.entries.last_mut().unwrap()) + } + }; + + // The entry should be missing for one just allocated or + // taken from the free list + assert!(entry.is_none()); + + *entry = Some(RegistryEntry { + references: 0, + ty: ty.clone(), + }); + + *e.insert(index) + } + }; + + self.entries[index.bits() as usize] + .as_mut() + .unwrap() + .references += 1; + + index + } + + fn unregister_signatures(&mut self, collection: &SignatureCollection) { + for (_, index) in collection.signatures.iter() { + self.unregister_entry(*index, 1); + } + } + + fn unregister_entry(&mut self, index: VMSharedSignatureIndex, count: usize) { + let removed = { + let entry = self.entries[index.bits() as usize].as_mut().unwrap(); + + debug_assert!(entry.references >= count); + entry.references -= count; + + if entry.references == 0 { + self.map.remove(&entry.ty); + self.free.push(index); + true + } else { + false + } + }; + + if removed { + self.entries[index.bits() as usize] = None; + } + } +} + +// `SignatureRegistryInner` implements `Drop` in debug builds to assert that +// all signatures have been unregistered for the registry. +#[cfg(debug_assertions)] +impl Drop for SignatureRegistryInner { + fn drop(&mut self) { + assert!( + self.map.is_empty() && self.free.len() == self.entries.len(), + "signature registry not empty" + ); + } +} + +/// Implements a shared signature registry. +/// +/// WebAssembly requires that the caller and callee signatures in an indirect +/// call must match. To implement this efficiently, keep a registry of all +/// signatures, shared by all instances, so that call sites can just do an +/// index comparison. +#[derive(Debug)] +pub struct SignatureRegistry(Arc>); + +impl SignatureRegistry { + /// Creates a new shared signature registry. + pub fn new() -> Self { + Self(Arc::new(RwLock::new(SignatureRegistryInner::default()))) + } + + /// Looks up a function type from a shared signature index. + pub fn lookup_type(&self, index: VMSharedSignatureIndex) -> Option { + self.0 + .read() + .unwrap() + .entries + .get(index.bits() as usize) + .and_then(|e| e.as_ref().map(|e| &e.ty).cloned()) + } + + /// Registers a single function with the collection. + /// + /// Returns the shared signature index for the function. + pub fn register(&self, ty: &WasmFuncType) -> VMSharedSignatureIndex { + self.0.write().unwrap().register(ty) + } + + /// Registers a single function with the collection. + /// + /// Returns the shared signature index for the function. + pub unsafe fn unregister(&self, sig: VMSharedSignatureIndex) { + self.0.write().unwrap().unregister_entry(sig, 1) + } +} diff --git a/crates/wasmtime/src/stack.rs b/crates/wasmtime/src/runtime/stack.rs similarity index 100% rename from crates/wasmtime/src/stack.rs rename to crates/wasmtime/src/runtime/stack.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 9c102c6bc550..ab90d5dcf731 100644 --- a/crates/wasmtime/src/trampoline/func.rs +++ b/crates/wasmtime/src/runtime/trampoline/func.rs @@ -104,7 +104,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 99% rename from crates/wasmtime/src/trap.rs rename to crates/wasmtime/src/runtime/trap.rs index 360298959ad5..8eae96abc5c8 100644 --- a/crates/wasmtime/src/trap.rs +++ b/crates/wasmtime/src/runtime/trap.rs @@ -1,5 +1,5 @@ #[cfg(feature = "coredump")] -use crate::coredump::WasmCoreDump; +use super::coredump::WasmCoreDump; use crate::store::StoreOpaque; use crate::{AsContext, Module}; use anyhow::Error; diff --git a/crates/wasmtime/src/type_registry.rs b/crates/wasmtime/src/runtime/type_registry.rs similarity index 100% rename from crates/wasmtime/src/type_registry.rs rename to crates/wasmtime/src/runtime/type_registry.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/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/wast/Cargo.toml b/crates/wast/Cargo.toml index 8e319b4de454..b326466d6f49 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -14,7 +14,7 @@ workspace = true [dependencies] anyhow = { workspace = true } -wasmtime = { workspace = true, features = ['cranelift', 'wat'] } +wasmtime = { workspace = true, features = ['cranelift', 'wat', 'runtime'] } wast = { workspace = true } log = { workspace = true } 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 9053b2cd66a2..1a4a967f02dc 100644 --- a/src/commands/settings.rs +++ b/src/commands/settings.rs @@ -107,7 +107,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)?;