diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index 707e2cd7b7e1..716d48b7e995 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -654,6 +654,22 @@ impl<'a> TrampolineCompiler<'a> { |_, _| {}, ); } + Trampoline::SyncToSyncEnterCall => { + self.translate_libcall( + host::sync_to_sync_enter_call, + TrapSentinel::NegativeOne, + WasmArgs::InRegisters, + |_, _| {}, + ); + } + Trampoline::SyncToSyncExitCall => { + self.translate_libcall( + host::sync_to_sync_exit_call, + TrapSentinel::Falsy, + WasmArgs::InRegisters, + |_, _| {}, + ); + } Trampoline::PrepareCall { memory } => { self.translate_libcall( host::prepare_call, @@ -665,7 +681,7 @@ impl<'a> TrampolineCompiler<'a> { }, ); } - Trampoline::SyncStartCall { callback } => { + Trampoline::SyncToAsyncStartCall { callback } => { let pointer_type = self.isa.pointer_type(); let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( &WasmFuncType::new( @@ -677,7 +693,7 @@ impl<'a> TrampolineCompiler<'a> { ); let values_vec_len = self.builder.ins().iconst(pointer_type, i64::from(len)); self.translate_libcall( - host::sync_start, + host::sync_to_async_start, HostResult::MultiValue { ptr: Some(values_vec_ptr), len: Some(values_vec_len), @@ -691,12 +707,12 @@ impl<'a> TrampolineCompiler<'a> { }, ); } - Trampoline::AsyncStartCall { + Trampoline::AsyncToAnyStartCall { callback, post_return, } => { self.translate_libcall( - host::async_start, + host::async_to_any_start, TrapSentinel::NegativeOne, WasmArgs::InRegisters, |me, params| { diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 378096f1a6d3..f1726b6f0fe2 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -60,6 +60,7 @@ component-model = [ "dep:semver", "wasmparser/component-model", ] +component-model-async = ['component-model'] demangle = ['std', 'dep:rustc-demangle', 'dep:cpp_demangle'] gc = [] gc-drc = ["gc"] @@ -74,9 +75,4 @@ compile = [ stack-switching = [] threads = ['std'] wmemcheck = ['std'] -std = [ - 'anyhow/std', - 'object/std', - 'wasmparser/std', - 'indexmap/std', -] +std = ['anyhow/std', 'object/std', 'wasmparser/std', 'indexmap/std'] diff --git a/crates/environ/src/component.rs b/crates/environ/src/component.rs index 9b97c8b17960..2e17c4977971 100644 --- a/crates/environ/src/component.rs +++ b/crates/environ/src/component.rs @@ -99,6 +99,18 @@ macro_rules! foreach_builtin_component_function { resource_enter_call(vmctx: vmctx); resource_exit_call(vmctx: vmctx) -> bool; + // Returns an `Option<(u32,u32)>` where `None` is "no thread to restore" + // and `Some((task,thread))` is "restore this thread after the sync call". + // The option is encoded as a 64-bit integer where the low bit is Some/None, + // bits 1-32 are the thread ID and bits 33-64 are the task ID. + #[cfg(feature = "component-model-async")] + sync_to_sync_enter_call( + vmctx: vmctx, + caller_instance: u32, + callee_instance: u32) -> u64; + #[cfg(feature = "component-model-async")] + sync_to_sync_exit_call(vmctx: vmctx, callee_instance: u32, old_thread: u64) -> bool; + #[cfg(feature = "component-model-async")] backpressure_modify(vmctx: vmctx, caller_instance: u32, increment: u8) -> bool; #[cfg(feature = "component-model-async")] @@ -137,9 +149,9 @@ macro_rules! foreach_builtin_component_function { storage_len: size ) -> bool; #[cfg(feature = "component-model-async")] - sync_start(vmctx: vmctx, callback: ptr_u8, storage: ptr_u8, storage_len: size, callee: ptr_u8, param_count: u32) -> bool; + sync_to_async_start(vmctx: vmctx, callback: ptr_u8, storage: ptr_u8, storage_len: size, callee: ptr_u8, param_count: u32) -> bool; #[cfg(feature = "component-model-async")] - async_start(vmctx: vmctx, callback: ptr_u8, post_return: ptr_u8, callee: ptr_u8, param_count: u32, result_count: u32, flags: u32) -> u64; + async_to_any_start(vmctx: vmctx, callback: ptr_u8, post_return: ptr_u8, callee: ptr_u8, param_count: u32, result_count: u32, flags: u32) -> u64; #[cfg(feature = "component-model-async")] future_new(vmctx: vmctx, caller_instance: u32, ty: u32) -> u64; #[cfg(feature = "component-model-async")] diff --git a/crates/environ/src/component/dfg.rs b/crates/environ/src/component/dfg.rs index 7e1e89a64e3d..b4de882b5203 100644 --- a/crates/environ/src/component/dfg.rs +++ b/crates/environ/src/component/dfg.rs @@ -229,10 +229,15 @@ id! { /// Same as `info::InstantiateModule` #[expect(missing_docs, reason = "tedious to document variants")] pub enum Instance { - Static(StaticModuleIndex, Box<[CoreDef]>), + Static( + StaticModuleIndex, + Box<[CoreDef]>, + RuntimeComponentInstanceIndex, + ), Import( RuntimeImportIndex, IndexMap>, + RuntimeComponentInstanceIndex, ), } @@ -276,7 +281,7 @@ pub enum CoreDef { /// During translation into `info::CoreDef` this variant is erased and /// replaced by `info::CoreDef::Export` since adapters are always /// represented as the exports of a core wasm instance. - Adapter(AdapterId), + Adapter(AdapterId, RuntimeComponentInstanceIndex), } impl From> for CoreDef @@ -462,13 +467,15 @@ pub enum Trampoline { ResourceTransferBorrow, ResourceEnterCall, ResourceExitCall, + SyncToSyncEnterCall, + SyncToSyncExitCall, PrepareCall { memory: Option, }, - SyncStartCall { + SyncToAsyncStartCall { callback: Option, }, - AsyncStartCall { + AsyncToAnyStartCall { callback: Option, post_return: Option, }, @@ -733,11 +740,12 @@ impl LinearizeDfg<'_> { fn instantiate(&mut self, instance: InstanceId, args: &Instance) { log::trace!("creating instance {instance:?}"); let instantiation = match args { - Instance::Static(index, args) => InstantiateModule::Static( + Instance::Static(index, args, component_instance) => InstantiateModule::Static( *index, args.iter().map(|def| self.core_def(def)).collect(), + *component_instance, ), - Instance::Import(index, args) => InstantiateModule::Import( + Instance::Import(index, args, component_instance) => InstantiateModule::Import( *index, args.iter() .map(|(module, values)| { @@ -748,6 +756,7 @@ impl LinearizeDfg<'_> { (module.clone(), values) }) .collect(), + *component_instance, ), }; let index = RuntimeInstanceIndex::new(self.runtime_instances.len()); @@ -895,7 +904,7 @@ impl LinearizeDfg<'_> { match def { CoreDef::Export(e) => info::CoreDef::Export(self.core_export(e)), CoreDef::InstanceFlags(i) => info::CoreDef::InstanceFlags(*i), - CoreDef::Adapter(id) => info::CoreDef::Export(self.adapter(*id)), + CoreDef::Adapter(id, instance) => info::CoreDef::Export(self.adapter(*id, *instance)), CoreDef::Trampoline(index) => info::CoreDef::Trampoline(self.trampoline(*index)), CoreDef::UnsafeIntrinsic(ty, i) => { let index = usize::try_from(i.index()).unwrap(); @@ -1139,16 +1148,20 @@ impl LinearizeDfg<'_> { Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow, Trampoline::ResourceEnterCall => info::Trampoline::ResourceEnterCall, Trampoline::ResourceExitCall => info::Trampoline::ResourceExitCall, + Trampoline::SyncToSyncEnterCall => info::Trampoline::SyncToSyncEnterCall, + Trampoline::SyncToSyncExitCall => info::Trampoline::SyncToSyncExitCall, Trampoline::PrepareCall { memory } => info::Trampoline::PrepareCall { memory: memory.map(|v| self.runtime_memory(v)), }, - Trampoline::SyncStartCall { callback } => info::Trampoline::SyncStartCall { - callback: callback.map(|v| self.runtime_callback(v)), - }, - Trampoline::AsyncStartCall { + Trampoline::SyncToAsyncStartCall { callback } => { + info::Trampoline::SyncToAsyncStartCall { + callback: callback.map(|v| self.runtime_callback(v)), + } + } + Trampoline::AsyncToAnyStartCall { callback, post_return, - } => info::Trampoline::AsyncStartCall { + } => info::Trampoline::AsyncToAnyStartCall { callback: callback.map(|v| self.runtime_callback(v)), post_return: post_return.map(|v| self.runtime_post_return(v)), }, @@ -1218,13 +1231,17 @@ impl LinearizeDfg<'_> { } } - fn adapter(&mut self, adapter: AdapterId) -> info::CoreExport { + fn adapter( + &mut self, + adapter: AdapterId, + instance: RuntimeComponentInstanceIndex, + ) -> info::CoreExport { let (adapter_module, entity_index) = self.dfg.adapter_partitionings[adapter]; // Instantiates the adapter module if it hasn't already been // instantiated or otherwise returns the index that the module was // already instantiated at. - let instance = self.adapter_module(adapter_module); + let instance = self.adapter_module(adapter_module, instance); // This adapter is always an export of the instance. info::CoreExport { @@ -1233,7 +1250,11 @@ impl LinearizeDfg<'_> { } } - fn adapter_module(&mut self, adapter_module: AdapterModuleId) -> RuntimeInstanceIndex { + fn adapter_module( + &mut self, + adapter_module: AdapterModuleId, + instance: RuntimeComponentInstanceIndex, + ) -> RuntimeInstanceIndex { self.intern( RuntimeInstance::Adapter(adapter_module), |me| &mut me.runtime_instances, @@ -1241,7 +1262,7 @@ impl LinearizeDfg<'_> { log::debug!("instantiating {adapter_module:?}"); let (module_index, args) = &me.dfg.adapter_modules[adapter_module]; let args = args.iter().map(|arg| me.core_def(arg)).collect(); - let instantiate = InstantiateModule::Static(*module_index, args); + let instantiate = InstantiateModule::Static(*module_index, args, instance); GlobalInitializer::InstantiateModule(instantiate) }, |_, init| init, diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index 5805e91b44c7..8099c52df4a1 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -356,7 +356,11 @@ pub enum InstantiateModule { /// order of imports required is statically known and can be pre-calculated /// to avoid string lookups related to names at runtime, represented by the /// flat list of arguments here. - Static(StaticModuleIndex, Box<[CoreDef]>), + Static( + StaticModuleIndex, + Box<[CoreDef]>, + RuntimeComponentInstanceIndex, + ), /// An imported module is being instantiated. /// @@ -366,6 +370,7 @@ pub enum InstantiateModule { Import( RuntimeImportIndex, IndexMap>, + RuntimeComponentInstanceIndex, ), } @@ -1051,6 +1056,13 @@ pub enum Trampoline { /// Same as `ResourceEnterCall` except for when exiting a call. ResourceExitCall, + /// An intrinsic used by FACT-generated modules which indicates that a call + /// is being entered and thread/resource-related metadata needs to be configured. + SyncToSyncEnterCall, + + /// Same as `SyncToSyncEnterCall` except for when exiting a call. + SyncToSyncExitCall, + /// An intrinsic used by FACT-generated modules to prepare a call involving /// an async-lowered import and/or an async-lifted export. PrepareCall { @@ -1062,7 +1074,7 @@ pub enum Trampoline { /// An intrinsic used by FACT-generated modules to start a call involving a /// sync-lowered import and async-lifted export. - SyncStartCall { + SyncToAsyncStartCall { /// The callee's callback function, if any. callback: Option, }, @@ -1070,11 +1082,11 @@ pub enum Trampoline { /// An intrinsic used by FACT-generated modules to start a call involving /// an async-lowered import function. /// - /// Note that `AsyncPrepareCall` and `AsyncStartCall` could theoretically be + /// Note that `PrepareCall` and `AsyncToAnyStartCall` could theoretically be /// combined into a single `AsyncCall` intrinsic, but we separate them to /// allow the FACT-generated module to optionally call the callee directly /// without an intermediate host stack frame. - AsyncStartCall { + AsyncToAnyStartCall { /// The callee's callback, if any. callback: Option, /// The callee's post-return function, if any. @@ -1232,9 +1244,11 @@ impl Trampoline { ResourceTransferBorrow => format!("component-resource-transfer-borrow"), ResourceEnterCall => format!("component-resource-enter-call"), ResourceExitCall => format!("component-resource-exit-call"), + SyncToSyncEnterCall => format!("component-sync-to-sync-enter-call"), + SyncToSyncExitCall => format!("component-sync-to-sync-exit-call"), PrepareCall { .. } => format!("component-prepare-call"), - SyncStartCall { .. } => format!("component-sync-start-call"), - AsyncStartCall { .. } => format!("component-async-start-call"), + SyncToAsyncStartCall { .. } => format!("component-sync-to-async-start-call"), + AsyncToAnyStartCall { .. } => format!("component-async-to-any-start-call"), FutureTransfer => format!("future-transfer"), StreamTransfer => format!("stream-transfer"), ErrorContextTransfer => format!("error-context-transfer"), diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index ed0a953ad715..f01f8aa1d217 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -556,7 +556,7 @@ impl<'a, 'data> Translator<'a, 'data> { for init in &translation.component.initializers { match init { GlobalInitializer::InstantiateModule(instantiation) => match instantiation { - InstantiateModule::Static(module, args) => { + InstantiateModule::Static(module, args, _) => { instantiations[*module].join(AbstractInstantiations::One(&*args)); instance_to_module.push(Some(*module).into()); } diff --git a/crates/environ/src/component/translate/adapt.rs b/crates/environ/src/component/translate/adapt.rs index 76a75813de9e..138b63807737 100644 --- a/crates/environ/src/component/translate/adapt.rs +++ b/crates/environ/src/component/translate/adapt.rs @@ -325,18 +325,20 @@ fn fact_import_to_core_def( } fact::Import::ResourceEnterCall => simple_intrinsic(dfg::Trampoline::ResourceEnterCall), fact::Import::ResourceExitCall => simple_intrinsic(dfg::Trampoline::ResourceExitCall), + fact::Import::SyncToSyncEnterCall => simple_intrinsic(dfg::Trampoline::SyncToSyncEnterCall), + fact::Import::SyncToSyncExitCall => simple_intrinsic(dfg::Trampoline::SyncToSyncExitCall), fact::Import::PrepareCall { memory } => simple_intrinsic(dfg::Trampoline::PrepareCall { memory: memory.as_ref().map(|v| dfg.memories.push(unwrap_memory(v))), }), - fact::Import::SyncStartCall { callback } => { - simple_intrinsic(dfg::Trampoline::SyncStartCall { + fact::Import::SyncToAsyncStartCall { callback } => { + simple_intrinsic(dfg::Trampoline::SyncToAsyncStartCall { callback: callback.clone().map(|v| dfg.callbacks.push(v)), }) } - fact::Import::AsyncStartCall { + fact::Import::AsyncToAnyStartCall { callback, post_return, - } => simple_intrinsic(dfg::Trampoline::AsyncStartCall { + } => simple_intrinsic(dfg::Trampoline::AsyncToAnyStartCall { callback: callback.clone().map(|v| dfg.callbacks.push(v)), post_return: post_return.clone().map(|v| dfg.post_returns.push(v)), }), @@ -431,7 +433,7 @@ impl PartitionAdapterModules { fn core_def(&mut self, dfg: &dfg::ComponentDfg, def: &dfg::CoreDef) { match def { dfg::CoreDef::Export(e) => self.core_export(dfg, e), - dfg::CoreDef::Adapter(id) => { + dfg::CoreDef::Adapter(id, _) => { // If this adapter is already defined then we can safely depend // on it with no consequences. if self.defined_items.contains(&Def::Adapter(*id)) { @@ -483,12 +485,12 @@ impl PartitionAdapterModules { // then the instances own arguments are recursively visited to find // transitive dependencies on adapters. match &dfg.instances[instance] { - dfg::Instance::Static(_, args) => { + dfg::Instance::Static(_, args, _) => { for arg in args.iter() { self.core_def(dfg, arg); } } - dfg::Instance::Import(_, args) => { + dfg::Instance::Import(_, args, _) => { for (_, values) in args { for (_, def) in values { self.core_def(dfg, def); diff --git a/crates/environ/src/component/translate/inline.rs b/crates/environ/src/component/translate/inline.rs index badbfd8e8d0e..25c025ce67d9 100644 --- a/crates/environ/src/component/translate/inline.rs +++ b/crates/environ/src/component/translate/inline.rs @@ -609,7 +609,7 @@ impl<'a> Inliner<'a> { lower_options: options_lower, func: func.clone(), }); - dfg::CoreDef::Adapter(adapter_idx) + dfg::CoreDef::Adapter(adapter_idx, frame.instance) } ComponentFuncDef::UnsafeIntrinsic(intrinsic) => { @@ -1214,7 +1214,7 @@ impl<'a> Inliner<'a> { } ( InstanceModule::Static(*idx), - dfg::Instance::Static(*idx, defs.into()), + dfg::Instance::Static(*idx, defs.into(), frame.instance), ) } ModuleDef::Import(path, ty) => { @@ -1230,7 +1230,7 @@ impl<'a> Inliner<'a> { let index = self.runtime_import(path); ( InstanceModule::Import(*ty), - dfg::Instance::Import(index, defs), + dfg::Instance::Import(index, defs, frame.instance), ) } }; diff --git a/crates/environ/src/fact.rs b/crates/environ/src/fact.rs index 3fa31386df20..ba30de99052f 100644 --- a/crates/environ/src/fact.rs +++ b/crates/environ/src/fact.rs @@ -81,8 +81,12 @@ pub struct Module<'a> { imported_resource_enter_call: Option, imported_resource_exit_call: Option, + /// Cached versions of imported trampolines for sync->sync guest function calls. + imported_sync_to_sync_enter_call: Option, + imported_sync_to_sync_exit_call: Option, + // Cached versions of imported trampolines for working with the async ABI. - imported_async_start_calls: HashMap<(Option, Option), FuncIndex>, + imported_async_to_any_start_calls: HashMap<(Option, Option), FuncIndex>, // Cached versions of imported trampolines for working with `stream`s, // `future`s, and `error-context`s. @@ -258,7 +262,9 @@ impl<'a> Module<'a> { imported_resource_transfer_borrow: None, imported_resource_enter_call: None, imported_resource_exit_call: None, - imported_async_start_calls: HashMap::new(), + imported_sync_to_sync_enter_call: None, + imported_sync_to_sync_exit_call: None, + imported_async_to_any_start_calls: HashMap::new(), imported_future_transfer: None, imported_stream_transfer: None, imported_error_context_transfer: None, @@ -572,7 +578,7 @@ impl<'a> Module<'a> { /// we've kept them separate to allow a future optimization where the caller /// calls the callee directly rather than using `sync-start` to have the host /// do it. - fn import_sync_start_call( + fn import_sync_to_async_start_call( &mut self, suffix: &str, callback: Option, @@ -586,7 +592,7 @@ impl<'a> Module<'a> { &format!("[start-call]{suffix}"), EntityType::Function(ty), ); - let import = Import::SyncStartCall { + let import = Import::SyncToAsyncStartCall { callback: callback .map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()), }; @@ -602,7 +608,7 @@ impl<'a> Module<'a> { /// we've kept them separate to allow a future optimization where the caller /// calls the callee directly rather than using `async-start` to have the /// host do it. - fn import_async_start_call( + fn import_async_to_any_start_call( &mut self, suffix: &str, callback: Option, @@ -613,7 +619,7 @@ impl<'a> Module<'a> { &format!("[start-call]{suffix}"), &[ValType::FUNCREF, ValType::I32, ValType::I32, ValType::I32], &[ValType::I32], - Import::AsyncStartCall { + Import::AsyncToAnyStartCall { callback: callback .map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()), post_return: post_return.map(|post_return| { @@ -625,13 +631,13 @@ impl<'a> Module<'a> { }), }, |me| { - me.imported_async_start_calls + me.imported_async_to_any_start_calls .get(&(callback, post_return)) .copied() }, |me, v| { assert!( - me.imported_async_start_calls + me.imported_async_to_any_start_calls .insert((callback, post_return), v) .is_none() ) @@ -716,6 +722,28 @@ impl<'a> Module<'a> { ) } + fn import_sync_to_sync_enter_call(&mut self) -> FuncIndex { + self.import_simple( + "sync", + "enter-call", + &[ValType::I32, ValType::I32], + &[ValType::I64], + Import::SyncToSyncEnterCall, + |me| &mut me.imported_sync_to_sync_enter_call, + ) + } + + fn import_sync_to_sync_exit_call(&mut self) -> FuncIndex { + self.import_simple( + "sync", + "exit-call", + &[ValType::I32, ValType::I64], + &[], + Import::SyncToSyncExitCall, + |me| &mut me.imported_sync_to_sync_exit_call, + ) + } + fn import_check_blocking(&mut self) -> FuncIndex { self.import_simple( "async", @@ -853,6 +881,11 @@ pub enum Import { /// Tears down a previous entry and handles checking borrow-related /// metadata. ResourceExitCall, + /// Sets up thread and resource borrow state when a sync->sync call starts. + SyncToSyncEnterCall, + /// Tears down a previous entry and handles checking borrow-related + /// metadata for sync->sync calls. + SyncToSyncExitCall, /// An intrinsic used by FACT-generated modules to begin a call involving /// an async-lowered import and/or an async-lifted export. PrepareCall { @@ -863,13 +896,13 @@ pub enum Import { }, /// An intrinsic used by FACT-generated modules to complete a call involving /// a sync-lowered import and async-lifted export. - SyncStartCall { + SyncToAsyncStartCall { /// The callee's callback function, if any. callback: Option, }, /// An intrinsic used by FACT-generated modules to complete a call involving /// an async-lowered import function. - AsyncStartCall { + AsyncToAnyStartCall { /// The callee's callback function, if any. callback: Option, diff --git a/crates/environ/src/fact/signature.rs b/crates/environ/src/fact/signature.rs index c565bd973750..34475be72a33 100644 --- a/crates/environ/src/fact/signature.rs +++ b/crates/environ/src/fact/signature.rs @@ -58,7 +58,7 @@ impl ComponentTypesBuilder { /// Note that this function uses multi-value return to return up to /// `MAX_FLAT_PARAMS` _results_ via the stack, allowing the host to pass /// them directly to the callee with no additional effort. - pub(super) fn async_start_signature( + pub(super) fn async_to_any_start_signature( &self, lower: &AdapterOptions, lift: &AdapterOptions, diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index e334876ef1f4..e87e9f9fbf6a 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -124,10 +124,10 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { // // This allows the host to delay copying the parameters until the callee // signals readiness by clearing its backpressure flag. - let async_start_adapter = |module: &mut Module| { + let async_to_any_start_adapter = |module: &mut Module| { let sig = module .types - .async_start_signature(&adapter.lower, &adapter.lift); + .async_to_any_start_signature(&adapter.lower, &adapter.lift); let ty = module.core_types.function(&sig.params, &sig.results); let result = module.funcs.push(Function::new( Some(format!("[async-start]{}", adapter.name)), @@ -135,7 +135,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { )); Compiler::new(module, result, sig.params.len() as u32, false) - .compile_async_start_adapter(adapter, &sig); + .compile_async_to_any_start_adapter(adapter, &sig); result }; @@ -187,7 +187,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { // switch events), when the callee has produced a result, it will // call `async-return` via the `task.return` intrinsic, at which // point a `STATUS_RETURNED` event will be delivered to the caller. - let start = async_start_adapter(module); + let start = async_to_any_start_adapter(module); let return_ = async_return_adapter(module); let (compiler, lower_sig, lift_sig) = compiler(module, adapter); compiler.compile_async_to_async_adapter( @@ -211,7 +211,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { // Similarly, the host will also temporarily store the results that // the callee provides to `async-return` until it is ready to resume // the caller. - let start = async_start_adapter(module); + let start = async_to_any_start_adapter(module); let return_ = async_return_adapter(module); let (compiler, lower_sig, lift_sig) = compiler(module, adapter); compiler.compile_sync_to_async_adapter( @@ -243,7 +243,7 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { // linear memory and deliver a `STATUS_RETURNED` event to the // caller. let lift_sig = module.types.signature(&adapter.lift); - let start = async_start_adapter(module); + let start = async_to_any_start_adapter(module); let return_ = async_return_adapter(module); let (compiler, lower_sig, ..) = compiler(module, adapter); compiler.compile_async_to_sync_adapter( @@ -468,9 +468,11 @@ impl<'a, 'b> Compiler<'a, 'b> { param_count: i32, lower_sig: &Signature, ) { - let start_call = - self.module - .import_async_start_call(&adapter.name, adapter.lift.options.callback, None); + let start_call = self.module.import_async_to_any_start_call( + &adapter.name, + adapter.lift.options.callback, + None, + ); self.call_prepare(adapter, start, return_, lower_sig, false); @@ -602,7 +604,7 @@ impl<'a, 'b> Compiler<'a, 'b> { lift_param_count: i32, lower_sig: &Signature, ) { - let start_call = self.module.import_sync_start_call( + let start_call = self.module.import_sync_to_async_start_call( &adapter.name, adapter.lift.options.callback, &lower_sig.results, @@ -648,9 +650,11 @@ impl<'a, 'b> Compiler<'a, 'b> { result_count: i32, lower_sig: &Signature, ) { - let start_call = - self.module - .import_async_start_call(&adapter.name, None, adapter.lift.post_return); + let start_call = self.module.import_async_to_any_start_call( + &adapter.name, + None, + adapter.lift.post_return, + ); self.call_prepare(adapter, start, return_, lower_sig, false); @@ -676,7 +680,7 @@ impl<'a, 'b> Compiler<'a, 'b> { /// /// This allows the host to delay copying the parameters until the callee /// signals readiness by clearing its backpressure flag. - fn compile_async_start_adapter(mut self, adapter: &AdapterData, sig: &Signature) { + fn compile_async_to_any_start_adapter(mut self, adapter: &AdapterData, sig: &Signature) { let param_locals = sig .params .iter() @@ -728,8 +732,7 @@ impl<'a, 'b> Compiler<'a, 'b> { /// sync-lifted export. /// /// Unlike calls involving async-lowered imports or async-lifted exports, - /// this adapter need not involve host built-ins except possibly for - /// resource bookkeeping. + /// this adapter requires host built-ins only for entry and exit bookkeeping. fn compile_sync_to_sync_adapter( mut self, adapter: &AdapterData, @@ -758,6 +761,22 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(Call(check_blocking.as_u32())); } + // If we have async support, we need to call sync_to_sync_enter_call to set up + // necessary threading context. + let old_thread = if cfg!(feature = "component-model-async") { + let enter = self.module.import_sync_to_sync_enter_call(); + self.instruction(I32Const( + i32::try_from(adapter.lower.instance.as_u32()).unwrap(), + )); + self.instruction(I32Const( + i32::try_from(adapter.lift.instance.as_u32()).unwrap(), + )); + self.instruction(Call(enter.as_u32())); + Some(self.local_set_new_tmp(ValType::I64)) + } else { + None + }; + if self.emit_resource_call { let enter = self.module.import_resource_enter_call(); self.instruction(Call(enter.as_u32())); @@ -826,6 +845,18 @@ impl<'a, 'b> Compiler<'a, 'b> { self.free_temp_local(tmp); } + // Similar to how we handled function entry, we select between intrinsics + // to call based on whether we have async support. + if cfg!(feature = "component-model-async") { + let old_thread = old_thread.unwrap(); + let exit = self.module.import_sync_to_sync_exit_call(); + self.instruction(I32Const( + i32::try_from(adapter.lift.instance.as_u32()).unwrap(), + )); + self.instruction(LocalGet(old_thread.idx)); + self.instruction(Call(exit.as_u32())); + self.free_temp_local(old_thread); + } if self.emit_resource_call { let exit = self.module.import_resource_exit_call(); self.instruction(Call(exit.as_u32())); diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml index 87636c934328..8394518c3279 100644 --- a/crates/wasmtime/Cargo.toml +++ b/crates/wasmtime/Cargo.toml @@ -22,7 +22,9 @@ wasmtime-jit-debug = { workspace = true, optional = true } wasmtime-jit-icache-coherence = { workspace = true, optional = true } wasmtime-cache = { workspace = true, optional = true } wasmtime-fiber = { workspace = true, optional = true } -wasmtime-cranelift = { workspace = true, optional = true, features = ['pulley'] } +wasmtime-cranelift = { workspace = true, optional = true, features = [ + 'pulley', +] } wasmtime-unwinder = { workspace = true, optional = true } wasmtime-winch = { workspace = true, optional = true } wasmtime-component-macro = { workspace = true, optional = true } @@ -202,22 +204,22 @@ cache = ["dep:wasmtime-cache", "std"] # Enables support for "async stores" as well as defining host functions as # `async fn` and calling functions asynchronously. -async = [ - "dep:wasmtime-fiber", - "wasmtime-component-macro?/async", - "runtime", -] +async = ["dep:wasmtime-fiber", "wasmtime-component-macro?/async", "runtime"] # Enables support for the pooling instance allocation strategy pooling-allocator = [ "runtime", - "std", # not ported to no_std yet + "std", # not ported to no_std yet ] # 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", "pulley"] +all-arch = [ + "wasmtime-cranelift?/all-arch", + "wasmtime-winch?/all-arch", + "pulley", +] # Enables in-progress support for the component model. Note that this feature is # in-progress, buggy, and incomplete. This is primarily here for internal @@ -252,9 +254,7 @@ coredump = ["dep:wasm-encoder", "runtime", "std"] # Export some symbols from the final binary to assist in debugging # Cranelift-generated code with native debuggers like GDB and LLDB. -debug-builtins = [ - "wasmtime-jit-debug/gdb_jit_int", -] +debug-builtins = ["wasmtime-jit-debug/gdb_jit_int"] # Enable support for executing compiled Wasm modules. runtime = [ @@ -287,11 +287,7 @@ runtime = [ # # You can additionally configure which GC implementations are enabled via the # `gc-drc` and `gc-null` features. -gc = [ - "wasmtime-environ/gc", - "wasmtime-cranelift?/gc", - "wasmtime-winch?/gc", -] +gc = ["wasmtime-environ/gc", "wasmtime-cranelift?/gc", "wasmtime-winch?/gc"] # Enable the deferred reference counting garbage collector. gc-drc = [ @@ -310,11 +306,7 @@ gc-null = [ ] # Enable runtime support for the WebAssembly threads proposal. -threads = [ - "wasmtime-cranelift?/threads", - "wasmtime-winch?/threads", - "std", -] +threads = ["wasmtime-cranelift?/threads", "wasmtime-winch?/threads", "std"] stack-switching = [ "runtime", @@ -406,22 +398,17 @@ component-model-async = [ "async", "component-model", "std", + "wasmtime-environ/component-model-async", "wasmtime-component-macro?/component-model-async", "dep:futures", "futures/std", ] # Enables support for `stream` interop with the `bytes` crate. -component-model-async-bytes = [ - "component-model-async", - "dep:bytes", -] +component-model-async-bytes = ["component-model-async", "dep:bytes"] # Enables support for guest debugging. -debug = [ - 'runtime', - 'async' -] +debug = ['runtime', 'async'] # Enables support for defining compile-time builtins. compile-time-builtins = ['dep:wasm-compose', 'dep:tempfile'] diff --git a/crates/wasmtime/src/runtime/component/component.rs b/crates/wasmtime/src/runtime/component/component.rs index c4d581272363..18b54f0667b1 100644 --- a/crates/wasmtime/src/runtime/component/component.rs +++ b/crates/wasmtime/src/runtime/component/component.rs @@ -654,11 +654,11 @@ impl Component { for init in &self.env_component().initializers { match init { GlobalInitializer::InstantiateModule(inst) => match inst { - InstantiateModule::Static(index, _) => { + InstantiateModule::Static(index, _, _) => { let module = self.static_module(*index); resources.add(&module.resources_required()); } - InstantiateModule::Import(_, _) => { + InstantiateModule::Import(_, _, _) => { // We can't statically determine the resources required // to instantiate this component. return None; diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index 2429aa82bcdd..d9b30cf18732 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -1659,7 +1659,7 @@ impl Instance { bail!(Trap::NoAsyncResult); } match &task.caller { - Caller::Host { .. } => { + Caller::AsyncHost { .. } => { if task.ready_to_delete() { Waitable::Guest(guest_thread.task) .delete_from(store.concurrent_state_mut())?; @@ -1669,6 +1669,8 @@ impl Instance { task.exited = true; task.callback = None; } + // Must have async support enabled to reach here + Caller::SyncHost => unreachable!(), } None } @@ -2048,7 +2050,7 @@ impl Instance { let task = state.get_mut(guest_thread.task)?; match &task.caller { - Caller::Host { .. } => { + Caller::AsyncHost { .. } => { if task.ready_to_delete() { Waitable::Guest(guest_thread.task).delete_from(state)?; } @@ -2056,6 +2058,8 @@ impl Instance { Caller::Guest { .. } => { task.exited = true; } + // Must have async support enabled to reach here + Caller::SyncHost => unreachable!(), } Ok(None) @@ -2145,7 +2149,7 @@ impl Instance { let old_thread = state.guest_thread.take(); let new_task = GuestTask::new( state, - Box::new(move |store, dst| { + Some(Box::new(move |store, dst| { let mut store = token.as_context_mut(store); assert!(dst.len() <= MAX_FLAT_PARAMS); // The `+ 1` here accounts for the return pointer, if any: @@ -2173,7 +2177,7 @@ impl Instance { // SAFETY: `start` is a valid `*mut VMFuncRef` from // `wasmtime-cranelift`-generated fused adapter code. Based on // how it was constructed (see - // `wasmtime_environ::fact::trampoline::Compiler::compile_async_start_adapter` + // `wasmtime_environ::fact::trampoline::Compiler::compile_async_to_any_start_adapter` // for details) we know it takes count parameters and returns // `dst.len()` results. unsafe { @@ -2195,8 +2199,8 @@ impl Instance { }), )?; Ok(()) - }), - LiftResult { + })), + Some(LiftResult { lift: Box::new(move |store, src| { // SAFETY: See comment in closure passed as `lower_params` // parameter above. @@ -2238,7 +2242,7 @@ impl Instance { ty: task_return_type, memory: NonNull::new(memory).map(SendSyncPtr::new), string_encoding: StringEncoding::from_u8(string_encoding).unwrap(), - }, + }), Caller::Guest { thread: old_thread.unwrap(), instance: caller_instance, @@ -2757,14 +2761,7 @@ impl Instance { .get_mut(guest_task)? .call_post_return_automatically() { - let (calls, host_table, _, instance) = - store.component_resource_state_with_instance(self); - ResourceTables { - calls, - host_table: Some(host_table), - guest: Some(instance.guest_tables()), - } - .exit_call()?; + self.get_resource_tables(store).exit_call()?; } else { // As of this writing, the only scenario where `call_post_return_automatically` // would be false for a `GuestTask` is for host-to-guest calls using @@ -2783,13 +2780,18 @@ impl Instance { let state = store.concurrent_state_mut(); let task = state.get_mut(guest_task)?; - if let Caller::Host { tx, .. } = &mut task.caller { - if let Some(tx) = tx.take() { - _ = tx.send(result); + match &mut task.caller { + Caller::AsyncHost { tx, .. } => { + if let Some(tx) = tx.take() { + _ = tx.send(result); + } } - } else { - task.result = Some(result); - Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?; + Caller::Guest { .. } => { + task.result = Some(result); + Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?; + } + // Must have async support enabled to reach here + Caller::SyncHost => unreachable!(), } Ok(()) @@ -3442,6 +3444,91 @@ impl Instance { self.id().get(store).check_may_leave(caller)?; store.concurrent_state_mut().context_set(slot, value) } + + pub(crate) fn sync_to_sync_enter_call( + self, + store: &mut StoreOpaque, + caller_instance: Option, + callee_instance: RuntimeComponentInstanceIndex, + ) -> Result> { + let state = store.concurrent_state_mut(); + let caller = if caller_instance.is_some() { + if state.guest_thread.is_none() { + bail!("No existing guest thread for guest->guest sync call"); + } + Caller::Guest { + thread: state.guest_thread.unwrap(), + instance: caller_instance.unwrap(), + } + } else { + Caller::SyncHost + }; + let task = GuestTask::new(state, None, None, caller, None, callee_instance, false)?; + + let task = state.push(task)?; + let thread = state.push(GuestThread::new_implicit(task))?; + state.get_mut(task)?.threads.insert(thread); + let guest_thread = QualifiedThreadId { task, thread }; + store + .concurrent_state_mut() + .get_mut(guest_thread.thread)? + .state = GuestThreadState::Running; + + self.add_guest_thread_to_instance_table(guest_thread.thread, store, callee_instance)?; + let old_thread = store + .concurrent_state_mut() + .guest_thread + .replace(guest_thread); + log::trace!( + "entering sync call: replaced {old_thread:?} with {guest_thread:?} as current thread", + ); + if let Some(old_thread) = old_thread { + store + .concurrent_state_mut() + .get_mut(old_thread.task)? + .subtasks + .insert(guest_thread.task); + } + + Ok(old_thread.map(|thread| (thread.task.rep(), thread.thread.rep()))) + } + + pub(crate) fn sync_to_sync_exit_call( + self, + store: &mut StoreOpaque, + callee_instance: RuntimeComponentInstanceIndex, + old_thread: Option<(u32, u32)>, + ) -> Result<()> { + let state = store.concurrent_state_mut(); + let guest_thread = state.guest_thread.unwrap(); + self.cleanup_thread(store, guest_thread, callee_instance)?; + if store + .concurrent_state_mut() + .get_mut(guest_thread.task)? + .ready_to_delete() + { + Waitable::Guest(guest_thread.task).delete_from(store.concurrent_state_mut())?; + } + + let old_thread = old_thread.map(|(task, thread)| QualifiedThreadId { + task: TableId::new(task), + thread: TableId::new(thread), + }); + store.concurrent_state_mut().guest_thread = old_thread; + log::trace!( + "exiting sync call: replaced {guest_thread:?} with {old_thread:?} as current thread", + ); + Ok(()) + } + + fn get_resource_tables<'a>(self, store: &'a mut StoreOpaque) -> ResourceTables<'a> { + let (calls, host_table, _, instance) = store.component_resource_state_with_instance(self); + ResourceTables { + calls, + host_table: Some(host_table), + guest: Some(instance.guest_tables()), + } + } } /// Trait representing component model ABI async intrinsics and fused adapter @@ -3475,7 +3562,7 @@ pub trait VMComponentAsyncStore { /// A helper function for fused adapter modules involving calls where the /// caller is sync-lowered but the callee is async-lifted. - unsafe fn sync_start( + unsafe fn sync_to_async_start( &mut self, instance: Instance, callback: *mut VMFuncRef, @@ -3487,7 +3574,7 @@ pub trait VMComponentAsyncStore { /// A helper function for fused adapter modules involving calls where the /// caller is async-lowered. - unsafe fn async_start( + unsafe fn async_to_any_start( &mut self, instance: Instance, callback: *mut VMFuncRef, @@ -3666,7 +3753,7 @@ impl VMComponentAsyncStore for StoreInner { } } - unsafe fn sync_start( + unsafe fn sync_to_async_start( &mut self, instance: Instance, callback: *mut VMFuncRef, @@ -3694,7 +3781,7 @@ impl VMComponentAsyncStore for StoreInner { } } - unsafe fn async_start( + unsafe fn async_to_any_start( &mut self, instance: Instance, callback: *mut VMFuncRef, @@ -3974,8 +4061,8 @@ type CallbackFn = Box< /// Represents the caller of a given guest task. enum Caller { - /// The host called the guest task. - Host { + /// The host called the guest task asynchronously. + AsyncHost { /// If present, may be used to deliver the result. tx: Option>, /// Channel to notify once all subtasks spawned by this caller have @@ -3991,6 +4078,8 @@ enum Caller { /// If true, call `post-return` function (if any) automatically. call_post_return_automatically: bool, }, + /// The host called the guest task synchronously. + SyncHost, /// Another guest thread called the guest task Guest { /// The id of the caller @@ -4230,8 +4319,8 @@ impl GuestTask { } fn new( state: &mut ConcurrentState, - lower_params: RawLower, - lift_result: LiftResult, + lower_params: Option, + lift_result: Option, caller: Caller, callback: Option, component_instance: RuntimeComponentInstanceIndex, @@ -4239,8 +4328,8 @@ impl GuestTask { ) -> Result { let sync_call_set = state.push(WaitableSet::default())?; let host_future_state = match &caller { - Caller::Guest { .. } => HostFutureState::NotApplicable, - Caller::Host { + Caller::Guest { .. } | Caller::SyncHost => HostFutureState::NotApplicable, + Caller::AsyncHost { host_future_present, .. } => { @@ -4253,8 +4342,8 @@ impl GuestTask { }; Ok(Self { common: WaitableCommon::default(), - lower_params: Some(lower_params), - lift_result: Some(lift_result), + lower_params, + lift_result, result: None, callback, caller, @@ -4313,9 +4402,9 @@ impl GuestTask { }; } } - Caller::Host { exit_tx, .. } => { + Caller::AsyncHost { exit_tx, .. } => { for subtask in &self.subtasks { - state.get_mut(*subtask)?.caller = Caller::Host { + state.get_mut(*subtask)?.caller = Caller::AsyncHost { tx: None, // Clone `exit_tx` to ensure that it is only dropped // once all transitive subtasks of the host call have @@ -4326,10 +4415,15 @@ impl GuestTask { }; } } + Caller::SyncHost => { + for subtask in &self.subtasks { + state.get_mut(*subtask)?.caller = Caller::SyncHost; + } + } } for subtask in self.subtasks { - if state.get_mut(subtask)?.exited { + if state.get_mut(subtask)?.exited && state.get_mut(subtask)?.ready_to_delete() { Waitable::Guest(subtask).delete_from(state)?; } } @@ -4341,7 +4435,7 @@ impl GuestTask { matches!( self.caller, Caller::Guest { .. } - | Caller::Host { + | Caller::AsyncHost { call_post_return_automatically: true, .. } @@ -4790,7 +4884,7 @@ impl ConcurrentState { // constant time check. loop { let next_thread = match &self.get_mut(guest_task).unwrap().caller { - Caller::Host { .. } => break true, + Caller::AsyncHost { .. } => break true, Caller::Guest { thread, instance } => { if *instance == guest_instance { break false; @@ -4798,6 +4892,8 @@ impl ConcurrentState { *thread } } + // Must have async support enabled to reach here + Caller::SyncHost => unreachable!(), }; guest_task = next_thread.task; } @@ -5080,18 +5176,18 @@ pub(crate) fn prepare_call( let mut task = GuestTask::new( state, - Box::new(for_any_lower(move |store, params| { + Some(Box::new(for_any_lower(move |store, params| { lower_params(handle, token.as_context_mut(store), params) - })), - LiftResult { + }))), + Some(LiftResult { lift: Box::new(for_any_lift(move |store, result| { lift_result(handle, store, result) })), ty: task_return_type, memory, string_encoding, - }, - Caller::Host { + }), + Caller::AsyncHost { tx: Some(tx), exit_tx: Arc::new(exit_tx), host_future_present, diff --git a/crates/wasmtime/src/runtime/component/func.rs b/crates/wasmtime/src/runtime/component/func.rs index 366ffd7165d0..e086f4c0fe32 100644 --- a/crates/wasmtime/src/runtime/component/func.rs +++ b/crates/wasmtime/src/runtime/component/func.rs @@ -240,7 +240,25 @@ impl Func { !store.0.async_support(), "must use `call_async` when async support is enabled on the config" ); - self.call_impl(&mut store.as_context_mut(), params, results) + + // If the async feature is enabled, we need to call sync_to_sync_enter/exit_call + // to track concurrent state. + #[cfg(feature = "component-model-async")] + { + let callee_instance = self.abi_info(store.0).3.instance; + let old_thread = + self.instance + .sync_to_sync_enter_call(store.0, None, callee_instance)?; + + self.call_impl(&mut store.as_context_mut(), params, results)?; + + self.instance + .sync_to_sync_exit_call(store.0, callee_instance, old_thread)?; + } + #[cfg(not(feature = "component-model-async"))] + self.call_impl(&mut store.as_context_mut(), params, results)?; + + Ok(()) } /// Exactly like [`Self::call`] except for use on async stores. diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index dceddd3285b5..e2c1ff5d4903 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -162,6 +162,28 @@ where !store.as_context().async_support(), "must use `call_async` when async support is enabled on the config" ); + // If the async feature is enabled, we need to call sync_to_sync_enter/exit_call + // to track concurrent state. + #[cfg(feature = "component-model-async")] + { + let mut store = store; + let callee_instance = self.func.abi_info(store.as_context_mut().0).3.instance; + let old_thread = self.func.instance.sync_to_sync_enter_call( + store.as_context_mut().0, + None, + callee_instance, + )?; + + let ret = self.call_impl(store.as_context_mut(), params)?; + + self.func.instance.sync_to_sync_exit_call( + store.as_context_mut().0, + callee_instance, + old_thread, + )?; + Ok(ret) + } + #[cfg(not(feature = "component-model-async"))] self.call_impl(store, params) } diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs index a803e6040c93..4cdcb4c586f6 100644 --- a/crates/wasmtime/src/runtime/component/instance.rs +++ b/crates/wasmtime/src/runtime/component/instance.rs @@ -832,10 +832,12 @@ impl<'a> Instantiator<'a> { match initializer { GlobalInitializer::InstantiateModule(m) => { let module; + #[cfg(feature = "component-model-async")] + let instance = Instance::from_wasmtime(store.0, self.id); let imports = match m { // Since upvars are statically know we know that the // `args` list is already in the right order. - InstantiateModule::Static(idx, args) => { + InstantiateModule::Static(idx, args, _) => { module = self.component.static_module(*idx); self.build_imports(store.0, module, args.iter()) } @@ -848,7 +850,7 @@ impl<'a> Instantiator<'a> { // FIXME: see the note in `ExportItem::Name` handling // above for how we ideally shouldn't do string lookup // here. - InstantiateModule::Import(idx, args) => { + InstantiateModule::Import(idx, args, _) => { module = match &self.imports[*idx] { RuntimeImport::Module(m) => m, _ => unreachable!(), @@ -860,19 +862,41 @@ impl<'a> Instantiator<'a> { } }; - // Note that the unsafety here should be ok because the - // validity of the component means that type-checks have - // already been performed. This means that the unsafety due - // to imports having the wrong type should not happen here. + // If async is enabled, we need to call sync_to_sync_enter/exit_call + // to track concurrent state. // - // Also note we are calling new_started_impl because we have - // already checked for asyncness and are running on a fiber - // if required. - - let i = unsafe { - crate::Instance::new_started(store, module, imports.as_ref()).await? - }; - self.instance_mut(store.0).push_instance_id(i.id()); + // Note that the unsafety in the call to new_started + // should be ok because the validity of the component means + // that type-checks have already been performed. This means + // that the unsafety due to imports having the wrong type should + // not happen here. + // + // Also note we have already checked for asyncness and are + // running on a fiber if required. + #[cfg(feature = "component-model-async")] + { + let runtime_instance = match m { + InstantiateModule::Static(_, _, i) => *i, + InstantiateModule::Import(_, _, i) => *i, + }; + let old_thread = + instance.sync_to_sync_enter_call(store.0, None, runtime_instance)?; + + let i = unsafe { + crate::Instance::new_started(store, module, imports.as_ref()).await? + }; + + instance.sync_to_sync_exit_call(store.0, runtime_instance, old_thread)?; + + self.instance_mut(store.0).push_instance_id(i.id()); + } + #[cfg(not(feature = "component-model-async"))] + { + let i = unsafe { + crate::Instance::new_started(store, module, imports.as_ref()).await? + }; + self.instance_mut(store.0).push_instance_id(i.id()); + } } GlobalInitializer::LowerImport { import, index } => { diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index 02f21b5833e8..f67c2189629a 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -665,6 +665,59 @@ fn resource_exit_call(store: &mut dyn VMStore, instance: Instance) -> Result<()> instance.resource_exit_call(store) } +#[cfg(feature = "component-model-async")] +struct SyncToSyncEnterCallRet(Option<(u32, u32)>); + +#[cfg(feature = "component-model-async")] +unsafe impl HostResultHasUnwindSentinel for SyncToSyncEnterCallRet { + type Abi = u64; + const SENTINEL: u64 = u64::MAX; + fn into_abi(self) -> u64 { + match self.0 { + Some((task, thread)) => (u64::from(task) << 32) | (u64::from(thread) << 1) | 1, + None => 0, + } + } +} + +#[cfg(feature = "component-model-async")] +fn sync_to_sync_enter_call( + store: &mut dyn VMStore, + instance: Instance, + caller_instance: u32, + callee_instance: u32, +) -> Result { + instance + .sync_to_sync_enter_call( + store, + Some(RuntimeComponentInstanceIndex::from_u32(caller_instance)), + RuntimeComponentInstanceIndex::from_u32(callee_instance), + ) + .map(SyncToSyncEnterCallRet) +} + +#[cfg(feature = "component-model-async")] +fn sync_to_sync_exit_call( + store: &mut dyn VMStore, + instance: Instance, + callee_instance: u32, + old_thread: u64, +) -> Result<()> { + let old_thread = if old_thread & 1 == 1 { + Some(( + (old_thread >> 32) as u32, + ((old_thread & 0xffffffff) >> 1) as u32, + )) + } else { + None + }; + instance.sync_to_sync_exit_call( + store, + RuntimeComponentInstanceIndex::from_u32(callee_instance), + old_thread, + ) +} + fn trap(_store: &mut dyn VMStore, _instance: Instance, code: u8) -> Result<()> { Err(wasmtime_environ::Trap::from_u8(code).unwrap().into()) } @@ -876,7 +929,7 @@ unsafe fn prepare_call( } #[cfg(feature = "component-model-async")] -unsafe fn sync_start( +unsafe fn sync_to_async_start( store: &mut dyn VMStore, instance: Instance, callback: *mut u8, @@ -886,7 +939,7 @@ unsafe fn sync_start( param_count: u32, ) -> Result<()> { unsafe { - store.component_async_store().sync_start( + store.component_async_store().sync_to_async_start( instance, callback.cast::(), callee.cast::(), @@ -898,7 +951,7 @@ unsafe fn sync_start( } #[cfg(feature = "component-model-async")] -unsafe fn async_start( +unsafe fn async_to_any_start( store: &mut dyn VMStore, instance: Instance, callback: *mut u8, @@ -909,7 +962,7 @@ unsafe fn async_start( flags: u32, ) -> Result { unsafe { - store.component_async_store().async_start( + store.component_async_store().async_to_any_start( instance, callback.cast::(), post_return.cast::(), diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs index 06fca8d5fe28..ad7bdf2b125a 100644 --- a/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs +++ b/crates/wasmtime/src/runtime/vm/instance/allocator/pooling.rs @@ -569,12 +569,12 @@ unsafe impl InstanceAllocator for PoolingInstanceAllocator { use wasmtime_environ::component::GlobalInitializer::*; use wasmtime_environ::component::InstantiateModule; match init { - InstantiateModule(InstantiateModule::Import(_, _)) => { + InstantiateModule(InstantiateModule::Import(_, _, _)) => { num_core_instances += 1; // Can't statically account for the total vmctx size, number // of memories, and number of tables in this component. } - InstantiateModule(InstantiateModule::Static(static_module_index, _)) => { + InstantiateModule(InstantiateModule::Static(static_module_index, _, _)) => { let module = get_module(*static_module_index); let offsets = VMOffsets::new(HostPtr, &module); self.validate_module(module, &offsets)?; diff --git a/src/commands/wast.rs b/src/commands/wast.rs index 00329ce9d5a0..2ce093c36c68 100644 --- a/src/commands/wast.rs +++ b/src/commands/wast.rs @@ -48,6 +48,8 @@ impl WastCommand { let async_ = optional_flag_with_default(self.async_, true); let mut config = self.common.config(None)?; config.async_support(async_); + config.wasm_component_model_async(true); + config.wasm_component_model_threading(true); config.shared_memory(true); let engine = Engine::new(&config)?; let mut wast_context = WastContext::new( diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs index 68f339576100..515aed54f5b1 100644 --- a/tests/all/component_model/resources.rs +++ b/tests/all/component_model/resources.rs @@ -233,7 +233,7 @@ fn mismatch_intrinsics() -> Result<()> { let ctor = i.get_typed_func::<(u32,), (ResourceAny,)>(&mut store, "ctor")?; assert_eq!( ctor.call(&mut store, (100,)).unwrap_err().to_string(), - "handle index 1 used with the wrong type, expected guest-defined \ + "handle index 2 used with the wrong type, expected guest-defined \ resource but found a different guest-defined resource", ); @@ -1412,8 +1412,8 @@ fn guest_different_host_same() -> Result<()> { (func (export "f") (param i32 i32) ;; different types, but everything goes into the same ;; handle index namespace - (if (i32.ne (local.get 0) (i32.const 1)) (then (unreachable))) - (if (i32.ne (local.get 1) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get 0) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get 1) (i32.const 3)) (then (unreachable))) ;; host should end up getting the same resource (call $f (local.get 0) (local.get 1)) diff --git a/tests/disas/component-model/direct-adapter-calls-inlining.wat b/tests/disas/component-model/direct-adapter-calls-inlining.wat index dab7c47b37a8..5193cf71d026 100644 --- a/tests/disas/component-model/direct-adapter-calls-inlining.wat +++ b/tests/disas/component-model/direct-adapter-calls-inlining.wat @@ -63,15 +63,17 @@ ;; gv5 = load.i64 notrap aligned readonly gv4+8 ;; gv6 = load.i64 notrap aligned gv5+16 ;; gv7 = vmctx -;; gv8 = load.i64 notrap aligned readonly can_move gv7+96 -;; gv9 = load.i64 notrap aligned readonly can_move gv7+72 +;; gv8 = load.i64 notrap aligned readonly can_move gv7+144 +;; gv9 = load.i64 notrap aligned readonly can_move gv7+120 ;; gv10 = vmctx ;; gv11 = load.i64 notrap aligned readonly gv10+8 ;; gv12 = load.i64 notrap aligned gv11+16 ;; sig0 = (i64 vmctx, i64, i32) -> i32 tail -;; sig1 = (i64 vmctx, i64, i32) -> i32 tail +;; sig1 = (i64 vmctx, i64, i32, i32) -> i64 tail +;; sig2 = (i64 vmctx, i64, i32) -> i32 tail +;; sig3 = (i64 vmctx, i64, i32, i64) tail ;; fn0 = colocated u2:0 sig0 -;; fn1 = colocated u0:0 sig1 +;; fn1 = colocated u0:0 sig2 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64): @@ -79,38 +81,43 @@ ;; ;; block2: ;; @00ee v5 = load.i64 notrap aligned readonly can_move v0+64 -;; v12 = load.i64 notrap aligned readonly can_move v5+96 -;; v13 = load.i32 notrap aligned table v12 -;; v14 = iconst.i32 1 -;; v15 = band v13, v14 ; v14 = 1 -;; v11 = iconst.i32 0 -;; v17 = icmp eq v15, v11 ; v11 = 0 -;; v18 = uextend.i32 v17 -;; trapnz v18, user11 +;; v13 = load.i64 notrap aligned readonly can_move v5+144 +;; v14 = load.i32 notrap aligned table v13 +;; v15 = iconst.i32 1 +;; v16 = band v14, v15 ; v15 = 1 +;; v12 = iconst.i32 0 +;; v18 = icmp eq v16, v12 ; v12 = 0 +;; v19 = uextend.i32 v18 +;; trapnz v19, user11 ;; jump block5 ;; ;; block5: -;; v19 = load.i64 notrap aligned readonly can_move v5+72 -;; v20 = load.i32 notrap aligned table v19 -;; v21 = iconst.i32 2 -;; v22 = band v20, v21 ; v21 = 2 +;; v20 = load.i64 notrap aligned readonly can_move v5+120 +;; v21 = load.i32 notrap aligned table v20 +;; v22 = iconst.i32 2 +;; v23 = band v21, v22 ; v22 = 2 ;; v79 = iconst.i32 0 -;; v80 = icmp eq v22, v79 ; v79 = 0 -;; v25 = uextend.i32 v80 -;; trapnz v25, user11 +;; v80 = icmp eq v23, v79 ; v79 = 0 +;; v26 = uextend.i32 v80 +;; trapnz v26, user11 ;; jump block7 ;; ;; block7: -;; v27 = load.i32 notrap aligned table v19 -;; v28 = iconst.i32 -3 -;; v29 = band v27, v28 ; v28 = -3 -;; store notrap aligned table v29, v19 -;; v60 = iconst.i32 -4 -;; v66 = band v27, v60 ; v60 = -4 -;; store notrap aligned table v66, v19 -;; v81 = iconst.i32 1 -;; v82 = bor v29, v81 ; v81 = 1 -;; store notrap aligned table v82, v19 +;; v28 = load.i32 notrap aligned table v20 +;; v29 = iconst.i32 -3 +;; v30 = band v28, v29 ; v29 = -3 +;; store notrap aligned table v30, v20 +;; v35 = load.i64 notrap aligned readonly can_move v5+72 +;; v34 = load.i64 notrap aligned readonly can_move v5+88 +;; v81 = iconst.i32 2 +;; v82 = iconst.i32 1 +;; v36 = call_indirect sig1, v35(v34, v5, v81, v82) ; v81 = 2, v82 = 1 +;; v38 = load.i32 notrap aligned table v20 +;; v39 = iconst.i32 -2 +;; v40 = band v38, v39 ; v39 = -2 +;; store notrap aligned table v40, v20 +;; v83 = bor v38, v82 ; v82 = 1 +;; store notrap aligned table v83, v20 ;; jump block8 ;; ;; block8: @@ -120,17 +127,20 @@ ;; jump block10 ;; ;; block10: -;; v45 = load.i32 notrap aligned table v12 -;; v33 = iconst.i32 -2 -;; v47 = band v45, v33 ; v33 = -2 -;; store notrap aligned table v47, v12 -;; v83 = iconst.i32 1 -;; v84 = bor v45, v83 ; v83 = 1 -;; store notrap aligned table v84, v12 -;; v55 = load.i32 notrap aligned table v19 -;; v85 = iconst.i32 2 -;; v86 = bor v55, v85 ; v85 = 2 -;; store notrap aligned table v86, v19 +;; v51 = load.i32 notrap aligned table v13 +;; v84 = iconst.i32 -2 +;; v85 = band v51, v84 ; v84 = -2 +;; store notrap aligned table v85, v13 +;; v86 = iconst.i32 1 +;; v87 = bor v51, v86 ; v86 = 1 +;; store notrap aligned table v87, v13 +;; v61 = load.i32 notrap aligned table v20 +;; v88 = iconst.i32 2 +;; v89 = bor v61, v88 ; v88 = 2 +;; store notrap aligned table v89, v20 +;; v67 = load.i64 notrap aligned readonly can_move v5+96 +;; v66 = load.i64 notrap aligned readonly can_move v5+112 +;; call_indirect sig3, v67(v66, v5, v86, v36) ; v86 = 1 ;; jump block3 ;; ;; block3: diff --git a/tests/disas/component-model/direct-adapter-calls-x64.wat b/tests/disas/component-model/direct-adapter-calls-x64.wat index c2062cbbe4d3..8c3b7c03a6ea 100644 --- a/tests/disas/component-model/direct-adapter-calls-x64.wat +++ b/tests/disas/component-model/direct-adapter-calls-x64.wat @@ -80,51 +80,75 @@ ;; retq ;; 4f: ud2 ;; -;; wasm[2]::function[1]: +;; wasm[2]::function[3]: ;; pushq %rbp ;; movq %rsp, %rbp ;; movq 8(%rdi), %r10 ;; movq 0x10(%r10), %r10 -;; addq $0x20, %r10 +;; addq $0x50, %r10 ;; cmpq %rsp, %r10 -;; ja 0xfe -;; 79: subq $0x10, %rsp -;; movq %rbx, (%rsp) -;; movq %r14, 8(%rsp) -;; movq 0x60(%rdi), %rbx -;; movl (%rbx), %r9d -;; testl $1, %r9d -;; je 0x100 -;; 9a: movq 0x48(%rdi), %r14 -;; movq %rdi, %r11 -;; movl (%r14), %esi -;; testl $2, %esi -;; je 0x102 -;; b0: movl (%r14), %ecx -;; movq %rcx, %rax -;; andl $0xfffffffd, %eax -;; movl %eax, (%r14) -;; andl $0xfffffffc, %ecx -;; movl %ecx, (%r14) -;; orl $1, %eax -;; movl %eax, (%r14) -;; movq %r11, %r10 -;; movq 0x40(%r10), %rdi -;; movq %r10, %rsi +;; ja 0x169 +;; 79: subq $0x40, %rsp +;; movq %rbx, 0x10(%rsp) +;; movq %r12, 0x18(%rsp) +;; movq %r13, 0x20(%rsp) +;; movq %r14, 0x28(%rsp) +;; movq %r15, 0x30(%rsp) +;; movq %rdi, %rbx +;; movq %rdx, %r13 +;; movq 0x90(%rdi), %r12 +;; movl (%r12), %ecx +;; testl $1, %ecx +;; je 0x16b +;; b3: movq %rbx, %rdi +;; movq 0x78(%rdi), %r15 +;; movl (%r15), %r9d +;; testl $2, %r9d +;; je 0x16d +;; ca: andl $0xfffffffd, (%r15) +;; movq %rbx, %rdi +;; movq 0x48(%rdi), %r8 +;; movq 0x58(%rdi), %rdi +;; movl $2, %edx +;; movl $1, %r14d +;; movq %r14, %rcx +;; movq %rbx, %rsi +;; callq *%r8 +;; movq %rax, (%rsp) +;; movl (%r15), %r11d +;; movq %r11, %rdi +;; andl $0xfffffffe, %edi +;; movl %edi, (%r15) +;; orl $1, %r11d +;; movl %r11d, (%r15) +;; movq 0x40(%rbx), %rdi +;; movq %r13, %rdx +;; movq %rbx, %rsi ;; callq 0 -;; movl (%rbx), %edx -;; movq %rdx, %r9 -;; andl $0xfffffffe, %r9d -;; movl %r9d, (%rbx) -;; orl $1, %edx -;; movl %edx, (%rbx) -;; orl $2, (%r14) -;; movq (%rsp), %rbx -;; movq 8(%rsp), %r14 -;; addq $0x10, %rsp +;; movq %rax, %r13 +;; movl (%r12), %edi +;; movq %rdi, %rcx +;; andl $0xfffffffe, %ecx +;; movl %ecx, (%r12) +;; orl $1, %edi +;; movl %edi, (%r12) +;; orl $2, (%r15) +;; movq 0x60(%rbx), %r9 +;; movq 0x70(%rbx), %rdi +;; movq (%rsp), %rcx +;; movq %r14, %rdx +;; movq %rbx, %rsi +;; callq *%r9 +;; movq %r13, %rax +;; movq 0x10(%rsp), %rbx +;; movq 0x18(%rsp), %r12 +;; movq 0x20(%rsp), %r13 +;; movq 0x28(%rsp), %r14 +;; movq 0x30(%rsp), %r15 +;; addq $0x40, %rsp ;; movq %rbp, %rsp ;; popq %rbp ;; retq -;; fe: ud2 -;; 100: ud2 -;; 102: ud2 +;; 169: ud2 +;; 16b: ud2 +;; 16d: ud2 diff --git a/tests/disas/component-model/direct-adapter-calls.wat b/tests/disas/component-model/direct-adapter-calls.wat index c342cfd0feb8..5c04247ed102 100644 --- a/tests/disas/component-model/direct-adapter-calls.wat +++ b/tests/disas/component-model/direct-adapter-calls.wat @@ -96,59 +96,67 @@ ;; gv1 = load.i64 notrap aligned readonly gv0+8 ;; gv2 = load.i64 notrap aligned gv1+16 ;; gv3 = vmctx -;; gv4 = load.i64 notrap aligned readonly can_move gv3+96 -;; gv5 = load.i64 notrap aligned readonly can_move gv3+72 -;; sig0 = (i64 vmctx, i64, i32) -> i32 tail -;; fn0 = colocated u0:0 sig0 +;; gv4 = load.i64 notrap aligned readonly can_move gv3+144 +;; gv5 = load.i64 notrap aligned readonly can_move gv3+120 +;; sig0 = (i64 vmctx, i64, i32, i32) -> i64 tail +;; sig1 = (i64 vmctx, i64, i32) -> i32 tail +;; sig2 = (i64 vmctx, i64, i32, i64) tail +;; fn0 = colocated u0:0 sig1 ;; stack_limit = gv2 ;; ;; block0(v0: i64, v1: i64, v2: i32): -;; @0064 v5 = load.i64 notrap aligned readonly can_move v0+96 -;; @0064 v6 = load.i32 notrap aligned table v5 -;; @0066 v7 = iconst.i32 1 -;; @0068 v8 = band v6, v7 ; v7 = 1 -;; @0062 v4 = iconst.i32 0 -;; @0069 v9 = icmp eq v8, v4 ; v4 = 0 -;; @0069 v10 = uextend.i32 v9 -;; @006c trapnz v10, user11 -;; @006a jump block3 +;; @0094 v6 = load.i64 notrap aligned readonly can_move v0+144 +;; @0094 v7 = load.i32 notrap aligned table v6 +;; @0096 v8 = iconst.i32 1 +;; @0098 v9 = band v7, v8 ; v8 = 1 +;; @0092 v5 = iconst.i32 0 +;; @0099 v10 = icmp eq v9, v5 ; v5 = 0 +;; @0099 v11 = uextend.i32 v10 +;; @009c trapnz v11, user11 +;; @009a jump block3 ;; ;; block3: -;; @006e v11 = load.i64 notrap aligned readonly can_move v0+72 -;; @006e v12 = load.i32 notrap aligned table v11 -;; @0070 v13 = iconst.i32 2 -;; @0072 v14 = band v12, v13 ; v13 = 2 -;; v79 = iconst.i32 0 -;; v80 = icmp eq v14, v79 ; v79 = 0 -;; @0073 v16 = uextend.i32 v80 -;; @0076 trapnz v16, user11 -;; @0074 jump block5 +;; @009e v12 = load.i64 notrap aligned readonly can_move v0+120 +;; @009e v13 = load.i32 notrap aligned table v12 +;; @00a0 v14 = iconst.i32 2 +;; @00a2 v15 = band v13, v14 ; v14 = 2 +;; v81 = iconst.i32 0 +;; v82 = icmp eq v15, v81 ; v81 = 0 +;; @00a3 v17 = uextend.i32 v82 +;; @00a6 trapnz v17, user11 +;; @00a4 jump block5 ;; ;; block5: -;; @0078 v18 = load.i32 notrap aligned table v11 -;; @007a v19 = iconst.i32 -3 -;; @007c v20 = band v18, v19 ; v19 = -3 -;; @007d store notrap aligned table v20, v11 -;; v67 = iconst.i32 -4 -;; v73 = band v18, v67 ; v67 = -4 -;; @0084 store notrap aligned table v73, v11 -;; v81 = iconst.i32 1 -;; v82 = bor v20, v81 ; v81 = 1 -;; @008d store notrap aligned table v82, v11 -;; @008f v33 = load.i64 notrap aligned readonly can_move v0+64 -;; @008f v34 = call fn0(v33, v0, v2) -;; @0093 v36 = load.i32 notrap aligned table v5 -;; @0081 v24 = iconst.i32 -2 -;; @0097 v38 = band v36, v24 ; v24 = -2 -;; @0098 store notrap aligned table v38, v5 -;; v83 = bor v36, v81 ; v81 = 1 -;; @00a1 store notrap aligned table v83, v5 -;; @00a3 v46 = load.i32 notrap aligned table v11 -;; v84 = iconst.i32 2 -;; v85 = bor v46, v84 ; v84 = 2 -;; @00a8 store notrap aligned table v85, v11 -;; @00aa jump block1 +;; @00a8 v19 = load.i32 notrap aligned table v12 +;; @00aa v20 = iconst.i32 -3 +;; @00ac v21 = band v19, v20 ; v20 = -3 +;; @00ad store notrap aligned table v21, v12 +;; @00b3 v27 = load.i64 notrap aligned readonly can_move v0+72 +;; @00b3 v26 = load.i64 notrap aligned readonly can_move v0+88 +;; v83 = iconst.i32 2 +;; v84 = iconst.i32 1 +;; @00b3 v28 = call_indirect sig0, v27(v26, v0, v83, v84) ; v83 = 2, v84 = 1 +;; @00b7 v30 = load.i32 notrap aligned table v12 +;; @00b9 v31 = iconst.i32 -2 +;; @00bb v32 = band v30, v31 ; v31 = -2 +;; @00bc store notrap aligned table v32, v12 +;; v85 = bor v30, v84 ; v84 = 1 +;; @00c5 store notrap aligned table v85, v12 +;; @00c7 v40 = load.i64 notrap aligned readonly can_move v0+64 +;; @00c7 v41 = call fn0(v40, v0, v2) +;; @00cb v43 = load.i32 notrap aligned table v6 +;; @00cf v45 = band v43, v31 ; v31 = -2 +;; @00d0 store notrap aligned table v45, v6 +;; v86 = bor v43, v84 ; v84 = 1 +;; @00d9 store notrap aligned table v86, v6 +;; @00db v53 = load.i32 notrap aligned table v12 +;; v87 = bor v53, v83 ; v83 = 2 +;; @00e0 store notrap aligned table v87, v12 +;; @00e6 v60 = load.i64 notrap aligned readonly can_move v0+96 +;; @00e6 v59 = load.i64 notrap aligned readonly can_move v0+112 +;; @00e6 call_indirect sig2, v60(v59, v0, v84, v28) ; v84 = 1 +;; @00e8 jump block1 ;; ;; block1: -;; @00aa return v34 +;; @00e8 return v41 ;; } diff --git a/tests/disas/component-model/enum.wat b/tests/disas/component-model/enum.wat index e1ad99c9cceb..29a1042786b2 100644 --- a/tests/disas/component-model/enum.wat +++ b/tests/disas/component-model/enum.wat @@ -31,73 +31,3 @@ (with "f" (func $i1 "f")))) ) -;; function u2:0(i64 vmctx, i64) -> i32 tail { -;; gv0 = vmctx -;; gv1 = load.i64 notrap aligned readonly gv0+8 -;; gv2 = load.i64 notrap aligned gv1+16 -;; gv3 = vmctx -;; gv4 = load.i64 notrap aligned readonly can_move gv3+96 -;; gv5 = load.i64 notrap aligned readonly can_move gv3+72 -;; sig0 = (i64 vmctx, i64) -> i32 tail -;; fn0 = colocated u0:0 sig0 -;; stack_limit = gv2 -;; -;; block0(v0: i64, v1: i64): -;; @0063 v4 = load.i64 notrap aligned readonly can_move v0+96 -;; @0063 v5 = load.i32 notrap aligned table v4 -;; @0065 v6 = iconst.i32 1 -;; @0067 v7 = band v5, v6 ; v6 = 1 -;; @0061 v3 = iconst.i32 0 -;; @0068 v8 = icmp eq v7, v3 ; v3 = 0 -;; @0068 v9 = uextend.i32 v8 -;; @006b trapnz v9, user11 -;; @0069 jump block3 -;; -;; block3: -;; @006d v10 = load.i64 notrap aligned readonly can_move v0+72 -;; @006d v11 = load.i32 notrap aligned table v10 -;; @006f v12 = iconst.i32 2 -;; @0071 v13 = band v11, v12 ; v12 = 2 -;; v79 = iconst.i32 0 -;; v80 = icmp eq v13, v79 ; v79 = 0 -;; @0072 v15 = uextend.i32 v80 -;; @0075 trapnz v15, user11 -;; @0073 jump block5 -;; -;; block5: -;; @0077 v17 = load.i32 notrap aligned table v10 -;; @0079 v18 = iconst.i32 -3 -;; @007b v19 = band v17, v18 ; v18 = -3 -;; @007c store notrap aligned table v19, v10 -;; v69 = iconst.i32 -4 -;; v75 = band v17, v69 ; v69 = -4 -;; @0083 store notrap aligned table v75, v10 -;; v81 = iconst.i32 1 -;; v82 = bor v19, v81 ; v81 = 1 -;; @008a store notrap aligned table v82, v10 -;; @008c v32 = load.i64 notrap aligned readonly can_move v0+64 -;; @008c v33 = call fn0(v32, v0) -;; @0090 v35 = load.i32 notrap aligned table v4 -;; @0080 v23 = iconst.i32 -2 -;; @0094 v37 = band v35, v23 ; v23 = -2 -;; @0095 store notrap aligned table v37, v4 -;; @009b v39 = iconst.i32 3 -;; @009d v40 = icmp ugt v33, v39 ; v39 = 3 -;; @009d v41 = uextend.i32 v40 -;; @00a0 trapnz v41, user11 -;; @009e jump block7 -;; -;; block7: -;; @00a4 v43 = load.i32 notrap aligned table v4 -;; v83 = iconst.i32 1 -;; v84 = bor v43, v83 ; v83 = 1 -;; @00a9 store notrap aligned table v84, v4 -;; @00ab v48 = load.i32 notrap aligned table v10 -;; v85 = iconst.i32 2 -;; v86 = bor v48, v85 ; v85 = 2 -;; @00b0 store notrap aligned table v86, v10 -;; @00b2 jump block1 -;; -;; block1: -;; @00b2 return v33 -;; } diff --git a/tests/misc_testsuite/component-model/resources.wast b/tests/misc_testsuite/component-model/resources.wast index f019c8b87145..3ebc4aedcf90 100644 --- a/tests/misc_testsuite/component-model/resources.wast +++ b/tests/misc_testsuite/component-model/resources.wast @@ -16,7 +16,7 @@ (local $r i32) (local.set $r (call $new (i32.const 100))) - (if (i32.ne (local.get $r) (i32.const 1)) (then (unreachable))) + (if (i32.ne (local.get $r) (i32.const 2)) (then (unreachable))) (if (i32.ne (call $rep (local.get $r)) (i32.const 100)) (then (unreachable))) (call $drop (local.get $r)) @@ -97,13 +97,13 @@ ;; resources assigned sequentially (local.set $r1 (call $new (i32.const 100))) - (if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable))) + (if (i32.ne (local.get $r1) (i32.const 2)) (then (unreachable))) (local.set $r2 (call $new (i32.const 200))) - (if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 3)) (then (unreachable))) (local.set $r3 (call $new (i32.const 300))) - (if (i32.ne (local.get $r3) (i32.const 3)) (then (unreachable))) + (if (i32.ne (local.get $r3) (i32.const 4)) (then (unreachable))) ;; representations all look good (if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (then (unreachable))) @@ -115,7 +115,7 @@ (local.set $r2 (call $new (i32.const 400))) ;; should have reused index 1 - (if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 3)) (then (unreachable))) ;; representations all look good (if (i32.ne (call $rep (local.get $r1)) (i32.const 100)) (then (unreachable))) @@ -137,13 +137,13 @@ (if (i32.ne (call $rep (local.get $r3)) (i32.const 700)) (then (unreachable))) ;; indices should be lifo - (if (i32.ne (local.get $r1) (i32.const 3)) (then (unreachable))) - (if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable))) - (if (i32.ne (local.get $r3) (i32.const 1)) (then (unreachable))) + (if (i32.ne (local.get $r1) (i32.const 4)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 3)) (then (unreachable))) + (if (i32.ne (local.get $r3) (i32.const 2)) (then (unreachable))) ;; bump one more time (local.set $r4 (call $new (i32.const 800))) - (if (i32.ne (local.get $r4) (i32.const 4)) (then (unreachable))) + (if (i32.ne (local.get $r4) (i32.const 5)) (then (unreachable))) ;; deallocate everything (call $drop (local.get $r1)) @@ -243,13 +243,13 @@ (local.set $r2 (call $ctor (i32.const 200))) ;; assert r1/r2 are sequential - (if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable))) - (if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get $r1) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 3)) (then (unreachable))) ;; reallocate r1 and it should be reassigned the same index (call $drop (local.get $r1)) (local.set $r1 (call $ctor (i32.const 300))) - (if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable))) + (if (i32.ne (local.get $r1) (i32.const 2)) (then (unreachable))) ;; internal values should match (call $assert (local.get $r1) (i32.const 300)) @@ -445,7 +445,7 @@ (import "" "ctor" (func $ctor (param i32) (result i32))) (func $start - (if (i32.ne (call $ctor (i32.const 100)) (i32.const 1)) (then (unreachable))) + (if (i32.ne (call $ctor (i32.const 100)) (i32.const 2)) (then (unreachable))) ) (start $start) ) @@ -619,12 +619,12 @@ (call $drop2 (call $new2 (i32.const 104))) ;; should be referencing the same namespace - (if (i32.ne (call $new1 (i32.const 105)) (i32.const 1)) (then (unreachable))) - (if (i32.ne (call $new2 (i32.const 105)) (i32.const 2)) (then (unreachable))) + (if (i32.ne (call $new1 (i32.const 105)) (i32.const 2)) (then (unreachable))) + (if (i32.ne (call $new2 (i32.const 105)) (i32.const 3)) (then (unreachable))) ;; use different drops out of order - (call $drop2 (i32.const 1)) - (call $drop1 (i32.const 2)) + (call $drop2 (i32.const 2)) + (call $drop1 (i32.const 3)) ) (start $start) @@ -702,10 +702,10 @@ (local.set $r1 (call $new1 (i32.const 100))) (local.set $r2 (call $new2 (i32.const 200))) - ;; indexes start at 1 and while they have distinct types they should be + ;; indexes start at 2 and while they have distinct types they should be ;; within the same table. - (if (i32.ne (local.get $r1) (i32.const 1)) (then (unreachable))) - (if (i32.ne (local.get $r2) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get $r1) (i32.const 2)) (then (unreachable))) + (if (i32.ne (local.get $r2) (i32.const 3)) (then (unreachable))) ;; nothing should be dropped yet (if (i32.ne (call $drops) (i32.const 0)) (then (unreachable))) @@ -869,7 +869,7 @@ ;; table should be empty at this point, so a fresh allocation should get ;; index 0 - (if (i32.ne (call $ctor (i32.const 600)) (i32.const 1)) (then (unreachable))) + (if (i32.ne (call $ctor (i32.const 600)) (i32.const 2)) (then (unreachable))) ) (start $start)