diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs index 0ffabeac9be2..ae1ff5a8c5d0 100644 --- a/crates/fuzzing/src/generators/config.rs +++ b/crates/fuzzing/src/generators/config.rs @@ -139,6 +139,7 @@ impl Config { extended_const, wide_arithmetic, component_model_more_flags, + component_model_async, simd, hogs_memory: _, @@ -151,6 +152,7 @@ impl Config { self.module_config.function_references_enabled = function_references.or(gc).unwrap_or(false); self.module_config.component_model_more_flags = component_model_more_flags.unwrap_or(false); + self.module_config.component_model_async = component_model_async.unwrap_or(false); // Enable/disable proposals that wasm-smith has knobs for which will be // read when creating `wasmtime::Config`. @@ -266,6 +268,7 @@ impl Config { .wasm_wide_arithmetic(self.module_config.config.wide_arithmetic_enabled) .wasm_extended_const(self.module_config.config.extended_const_enabled) .wasm_component_model_more_flags(self.module_config.component_model_more_flags) + .wasm_component_model_async(self.module_config.component_model_async) .native_unwind_info(cfg!(target_os = "windows") || self.wasmtime.native_unwind_info) .cranelift_nan_canonicalization(self.wasmtime.canonicalize_nans) .cranelift_opt_level(self.wasmtime.opt_level.to_wasmtime()) diff --git a/crates/fuzzing/src/generators/module.rs b/crates/fuzzing/src/generators/module.rs index 283edaf1d2b8..9b8715636399 100644 --- a/crates/fuzzing/src/generators/module.rs +++ b/crates/fuzzing/src/generators/module.rs @@ -16,6 +16,7 @@ pub struct ModuleConfig { // config-to-`wasmtime::Config` translation. pub function_references_enabled: bool, pub component_model_more_flags: bool, + pub component_model_async: bool, } impl<'a> Arbitrary<'a> for ModuleConfig { @@ -62,6 +63,7 @@ impl<'a> Arbitrary<'a> for ModuleConfig { Ok(ModuleConfig { component_model_more_flags: false, + component_model_async: false, function_references_enabled: config.gc_enabled, config, }) diff --git a/crates/misc/component-test-util/Cargo.toml b/crates/misc/component-test-util/Cargo.toml index b5dfefddcb50..dc1ce92d029f 100644 --- a/crates/misc/component-test-util/Cargo.toml +++ b/crates/misc/component-test-util/Cargo.toml @@ -11,7 +11,7 @@ publish = false env_logger = { workspace = true } anyhow = { workspace = true } arbitrary = { workspace = true, features = ["derive"] } -wasmtime = { workspace = true, features = ["component-model", "async"] } +wasmtime = { workspace = true, features = ["component-model", "async", "component-model-async"] } wasmtime-environ = { workspace = true } wasmtime-wast-util = { path = '../../wast-util' } target-lexicon = { workspace = true } diff --git a/crates/misc/component-test-util/src/lib.rs b/crates/misc/component-test-util/src/lib.rs index 07d492b298df..2a6e72efb5e6 100644 --- a/crates/misc/component-test-util/src/lib.rs +++ b/crates/misc/component-test-util/src/lib.rs @@ -166,6 +166,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wasmtime_wast_util:: extended_const, wide_arithmetic, component_model_more_flags, + component_model_async, nan_canonicalization, simd, @@ -184,6 +185,7 @@ pub fn apply_test_config(config: &mut Config, test_config: &wasmtime_wast_util:: let extended_const = extended_const.unwrap_or(false); let wide_arithmetic = wide_arithmetic.unwrap_or(false); let component_model_more_flags = component_model_more_flags.unwrap_or(false); + let component_model_async = component_model_async.unwrap_or(false); let nan_canonicalization = nan_canonicalization.unwrap_or(false); let relaxed_simd = relaxed_simd.unwrap_or(false); @@ -210,5 +212,6 @@ pub fn apply_test_config(config: &mut Config, test_config: &wasmtime_wast_util:: .wasm_extended_const(extended_const) .wasm_wide_arithmetic(wide_arithmetic) .wasm_component_model_more_flags(component_model_more_flags) + .wasm_component_model_async(component_model_async) .cranelift_nan_canonicalization(nan_canonicalization); } diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 0212a071f531..6d6b1f9d4dae 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1111,6 +1111,19 @@ impl Config { self } + /// Configures whether components support the async ABI [proposal] for + /// lifting and lowering functions, as well as `stream`, `future`, and + /// `error-context` types. + /// + /// Please note that Wasmtime's support for this feature is _very_ incomplete. + /// + /// [proposal]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md + #[cfg(feature = "component-model-async")] + pub fn wasm_component_model_async(&mut self, enable: bool) -> &mut Self { + self.wasm_feature(WasmFeatures::COMPONENT_MODEL_ASYNC, enable); + self + } + /// Configures which compilation strategy will be used for wasm modules. /// /// This method can be used to configure which compiler is used for wasm diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs index 6281c123aa4f..8051970917c6 100644 --- a/crates/wasmtime/src/engine/serialization.rs +++ b/crates/wasmtime/src/engine/serialization.rs @@ -202,6 +202,7 @@ struct WasmFeatures { custom_page_sizes: bool, component_model_more_flags: bool, component_model_multiple_returns: bool, + component_model_async: bool, gc_types: bool, wide_arithmetic: bool, } @@ -253,7 +254,6 @@ impl Metadata<'_> { assert!(!shared_everything_threads); assert!(!legacy_exceptions); assert!(!stack_switching); - assert!(!component_model_async); Metadata { target: engine.compiler().triple().to_string(), @@ -278,6 +278,7 @@ impl Metadata<'_> { custom_page_sizes, component_model_more_flags, component_model_multiple_returns, + component_model_async, gc_types, wide_arithmetic, }, @@ -488,6 +489,7 @@ impl Metadata<'_> { custom_page_sizes, component_model_more_flags, component_model_multiple_returns, + component_model_async, gc_types, wide_arithmetic, } = self.features; @@ -574,6 +576,11 @@ impl Metadata<'_> { other.contains(F::COMPONENT_MODEL_MULTIPLE_RETURNS), "WebAssembly component model support for multiple returns", )?; + Self::check_bool( + component_model_async, + other.contains(F::COMPONENT_MODEL_ASYNC), + "WebAssembly component model support for async lifts/lowers, futures, streams, and errors", + )?; Self::check_cfg_bool( cfg!(feature = "gc"), "gc", diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs index 5584cf68b753..611822aa6e87 100644 --- a/crates/wasmtime/src/runtime/component/instance.rs +++ b/crates/wasmtime/src/runtime/component/instance.rs @@ -608,8 +608,7 @@ impl<'a> Instantiator<'a> { } GlobalInitializer::ExtractCallback(callback) => { - _ = callback; - todo!() + self.extract_callback(store.0, callback) } GlobalInitializer::ExtractPostReturn(post_return) => { @@ -659,6 +658,16 @@ impl<'a> Instantiator<'a> { self.data.state.set_runtime_realloc(realloc.index, func_ref); } + fn extract_callback(&mut self, store: &mut StoreOpaque, callback: &ExtractCallback) { + let func_ref = match self.data.lookup_def(store, &callback.def) { + crate::runtime::vm::Export::Function(f) => f.func_ref, + _ => unreachable!(), + }; + self.data + .state + .set_runtime_callback(callback.index, func_ref); + } + fn extract_post_return(&mut self, store: &mut StoreOpaque, post_return: &ExtractPostReturn) { let func_ref = match self.data.lookup_def(store, &post_return.def) { crate::runtime::vm::Export::Function(f) => f.func_ref, diff --git a/crates/wasmtime/src/runtime/vm/component.rs b/crates/wasmtime/src/runtime/vm/component.rs index 853b77bf7ddf..766959c46804 100644 --- a/crates/wasmtime/src/runtime/vm/component.rs +++ b/crates/wasmtime/src/runtime/vm/component.rs @@ -375,6 +375,16 @@ impl ComponentInstance { } } + /// Same as `set_runtime_memory` but for async callback function pointers. + pub fn set_runtime_callback(&mut self, idx: RuntimeCallbackIndex, ptr: NonNull) { + unsafe { + let storage = + self.vmctx_plus_offset_mut::>(self.offsets.runtime_callback(idx)); + debug_assert!((*storage).as_ptr() as usize == INVALID_PTR); + *storage = ptr.into(); + } + } + /// Same as `set_runtime_memory` but for post-return function pointers. pub fn set_runtime_post_return( &mut self, @@ -493,6 +503,11 @@ impl ComponentInstance { let offset = self.offsets.runtime_realloc(i); *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; } + for i in 0..self.offsets.num_runtime_callbacks { + let i = RuntimeCallbackIndex::from_u32(i); + let offset = self.offsets.runtime_callback(i); + *self.vmctx_plus_offset_mut(offset) = INVALID_PTR; + } for i in 0..self.offsets.num_runtime_post_returns { let i = RuntimePostReturnIndex::from_u32(i); let offset = self.offsets.runtime_post_return(i); @@ -734,6 +749,11 @@ impl OwnedComponentInstance { unsafe { self.instance_mut().set_runtime_realloc(idx, ptr) } } + /// See `ComponentInstance::set_runtime_callback` + pub fn set_runtime_callback(&mut self, idx: RuntimeCallbackIndex, ptr: NonNull) { + unsafe { self.instance_mut().set_runtime_callback(idx, ptr) } + } + /// See `ComponentInstance::set_runtime_post_return` pub fn set_runtime_post_return( &mut self, diff --git a/crates/wast-util/src/lib.rs b/crates/wast-util/src/lib.rs index 08cd8fb55761..6760128f1cdd 100644 --- a/crates/wast-util/src/lib.rs +++ b/crates/wast-util/src/lib.rs @@ -185,6 +185,7 @@ macro_rules! foreach_config_option { hogs_memory nan_canonicalization component_model_more_flags + component_model_async simd gc_types } diff --git a/crates/wast/Cargo.toml b/crates/wast/Cargo.toml index 599cfae1cdd1..dbe3f4d224b9 100644 --- a/crates/wast/Cargo.toml +++ b/crates/wast/Cargo.toml @@ -20,4 +20,4 @@ wast = { workspace = true } log = { workspace = true } [features] -component-model = ['wasmtime/component-model'] +component-model = ['wasmtime/component-model', 'wasmtime/component-model-async'] diff --git a/tests/misc_testsuite/component-model-async/lift.wast b/tests/misc_testsuite/component-model-async/lift.wast new file mode 100644 index 000000000000..f90c65672c96 --- /dev/null +++ b/tests/misc_testsuite/component-model-async/lift.wast @@ -0,0 +1,26 @@ +;;! component_model_async = true + +;; async lift; no callback +(component + (core module $m + (func (export "foo") (param i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "foo") (param "p1" u32) (result u32) + (canon lift (core func $i "foo") async) + ) +) + +;; async lift; with callback +(component + (core module $m + (func (export "callback") (param i32 i32 i32 i32) (result i32) unreachable) + (func (export "foo") (param i32) (result i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "foo") (param "p1" u32) (result u32) + (canon lift (core func $i "foo") async (callback (func $i "callback"))) + ) +)