diff --git a/Cargo.lock b/Cargo.lock index c07af94cbb..05dcdf25ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -624,7 +624,7 @@ dependencies = [ "tokio", "wasi-http-draft", "wasm-compose", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmtime", "wasmtime-wasi", ] @@ -3897,7 +3897,7 @@ name = "verify-component-adapter" version = "34.0.0" dependencies = [ "anyhow", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wat", ] @@ -4078,9 +4078,9 @@ checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wasm-compose" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3efe9b91ee8d09c6696e448d756a207c290deb6af2e92df6acfd8bbde3bb7f70" +checksum = "d529e2655cd5722a1d21754386461502bf0b3d7a36657b3f16554a416ce4d401" dependencies = [ "anyhow", "heck 0.4.1", @@ -4093,51 +4093,51 @@ dependencies = [ "serde_yaml", "smallvec", "wasm-encoder", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wat", ] [[package]] name = "wasm-encoder" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4349d0943718e6e434b51b9639e876293093dca4b96384fb136ab5bd5ce6660" +checksum = "a447e61e38d1226b57e4628edadff36d16760be24a343712ba236b5106c95156" dependencies = [ "leb128fmt", - "wasmparser 0.230.0", + "wasmparser 0.232.0", ] [[package]] name = "wasm-metadata" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a52e010df5494f4289ccc68ce0c2a8c17555225a5e55cc41b98f5ea28d0844b" +checksum = "71aba7991cf9922a097b2dcc4c36ba5bc207339bdcd3a515530fc2555f01e8eb" dependencies = [ "anyhow", "indexmap 2.7.0", "wasm-encoder", - "wasmparser 0.230.0", + "wasmparser 0.232.0", ] [[package]] name = "wasm-mutate" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c9228682f308eb59db56835e1113ac2951d92ad92cd7bdcd9ab3cee607ed4f" +checksum = "79bd4bdbd0388f94172ab752a07c43f790af003148127ca04840ca1bbc83f651" dependencies = [ "egg", "log", "rand", "thiserror 1.0.65", "wasm-encoder", - "wasmparser 0.230.0", + "wasmparser 0.232.0", ] [[package]] name = "wasm-smith" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ac6b9a65a2552ce3279294e877cc4b408d6d2b771445feb215f24aff3093d6f" +checksum = "70b3cbebe831a2319a70e6e971eb1aa92ab8f04e8c0b067040c0407e3882c688" dependencies = [ "anyhow", "arbitrary", @@ -4158,9 +4158,9 @@ dependencies = [ [[package]] name = "wasm-wave" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66809a0fb9e6555af54d3a388296b0859263d7ff520e4dad94218a301e9f44bd" +checksum = "9a66ccf53b27238b3e8a8a61db21edd500b637414b9948654f17f01b4ff2b82f" dependencies = [ "indexmap 2.7.0", "logos", @@ -4224,9 +4224,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" +checksum = "917739b33bb1eb0e9a49bcd2637a351931be4578d0cc4d37b908d7a797784fbb" dependencies = [ "bitflags 2.6.0", "hashbrown 0.15.2", @@ -4237,13 +4237,13 @@ dependencies = [ [[package]] name = "wasmprinter" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dc8e9a1e48f4b2247b006b3a9b0a02ba62a2e52cfcfd4bc4c70785a6104fc32" +checksum = "3be346255070ba58f65b24826bc94643dca065e797db7abb83e90965d086eba3" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.230.0", + "wasmparser 0.232.0", ] [[package]] @@ -4293,7 +4293,7 @@ dependencies = [ "wasi-common", "wasm-encoder", "wasm-wave", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -4439,7 +4439,7 @@ dependencies = [ "walkdir", "wasi-common", "wasm-encoder", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmtime", "wasmtime-cache", "wasmtime-cli-flags", @@ -4457,7 +4457,7 @@ dependencies = [ "wasmtime-wasi-threads", "wasmtime-wasi-tls", "wasmtime-wast", - "wast 230.0.0", + "wast 232.0.0", "wat", "windows-sys 0.59.0", "wit-component", @@ -4521,7 +4521,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -4548,7 +4548,7 @@ dependencies = [ "smallvec", "target-lexicon", "wasm-encoder", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmprinter", "wasmtime-component-util", "wat", @@ -4561,7 +4561,7 @@ dependencies = [ "arbitrary", "env_logger 0.11.5", "libfuzzer-sys", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmprinter", "wasmtime-environ", "wasmtime-test-util", @@ -4621,7 +4621,7 @@ dependencies = [ "rand", "smallvec", "target-lexicon", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmtime", "wasmtime-fuzzing", "wasmtime-test-util", @@ -4647,7 +4647,7 @@ dependencies = [ "wasm-smith", "wasm-spec-interpreter", "wasmi", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmprinter", "wasmtime", "wasmtime-cli-flags", @@ -4876,7 +4876,7 @@ dependencies = [ "log", "tokio", "wasmtime", - "wast 230.0.0", + "wast 232.0.0", ] [[package]] @@ -4888,7 +4888,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -4919,9 +4919,9 @@ dependencies = [ [[package]] name = "wast" -version = "230.0.0" +version = "232.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8edac03c5fa691551531533928443faf3dc61a44f814a235c7ec5d17b7b34f1" +checksum = "1b5a22bfb0c309f5cf4b0cfa4fae77801e52570e88bff1344c1b7673a9977954" dependencies = [ "bumpalo", "leb128fmt", @@ -4932,11 +4932,11 @@ dependencies = [ [[package]] name = "wat" -version = "1.230.0" +version = "1.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d77d62229e38db83eac32bacb5f61ebb952366ab0dae90cf2b3c07a65eea894" +checksum = "5e2054d3da4289c8634a6ed0dd177e066518f45772c521b6959f5d6109f4c210" dependencies = [ - "wast 230.0.0", + "wast 232.0.0", ] [[package]] @@ -5066,7 +5066,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror 2.0.12", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wasmtime-cranelift", "wasmtime-environ", ] @@ -5224,8 +5224,7 @@ dependencies = [ [[package]] name = "wit-bindgen" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa5b79cd8cb4b27a9be3619090c03cbb87fe7b1c6de254b4c9b4477188828af8" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "wit-bindgen-rt 0.42.1", "wit-bindgen-rust-macro", @@ -5234,8 +5233,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35e550f614e16db196e051d22b0d4c94dd6f52c90cb1016240f71b9db332631" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "anyhow", "heck 0.5.0", @@ -5263,8 +5261,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051105bab12bc78e161f8dfb3596e772dd6a01ebf9c4840988e00347e744966a" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "bitflags 2.6.0", "futures", @@ -5274,8 +5271,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb1e0a91fc85f4ef70e0b81cd86c2b49539d3cd14766fd82396184aadf8cb7d7" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "anyhow", "heck 0.5.0", @@ -5290,8 +5286,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce69f52c5737705881d5da5a1dd06f47f8098d094a8d65a3e44292942edb571f" +source = "git+https://github.com/bytecodealliance/wit-bindgen#98772fbf15abc0c1ea6b9d8eb444990d2e0b3d80" dependencies = [ "anyhow", "prettyplease", @@ -5304,9 +5299,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b607b15ead6d0e87f5d1613b4f18c04d4e80ceeada5ffa608d8360e6909881df" +checksum = "5d1d7a3444f5039b4461a66dc085d749a832e518a86e8c7498d6fdd9df776ed0" dependencies = [ "anyhow", "bitflags 2.6.0", @@ -5317,15 +5312,15 @@ dependencies = [ "serde_json", "wasm-encoder", "wasm-metadata", - "wasmparser 0.230.0", + "wasmparser 0.232.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.230.0" +version = "0.232.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "679fde5556495f98079a8e6b9ef8c887f731addaffa3d48194075c1dd5cd611b" +checksum = "00a0e031533e3f9082057b09b346f76d3af12a714feccbe8d32f926254319d86" dependencies = [ "anyhow", "id-arena", @@ -5336,7 +5331,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.230.0", + "wasmparser 0.232.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 97e6dae890..248e591a54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -305,17 +305,17 @@ wit-bindgen-rt = { version = "0.42.1", default-features = false } wit-bindgen-rust-macro = { version = "0.42.1", default-features = false } # wasm-tools family: -wasmparser = { version = "0.230.0", default-features = false, features = ['simd'] } -wat = "1.230.0" -wast = "230.0.0" -wasmprinter = "0.230.0" -wasm-encoder = "0.230.0" -wasm-smith = "0.230.0" -wasm-mutate = "0.230.0" -wit-parser = "0.230.0" -wit-component = "0.230.0" -wasm-wave = "0.230.0" -wasm-compose = "0.230.0" +wasmparser = { version = "0.232.0", default-features = false, features = ['simd'] } +wat = "1.232.0" +wast = "232.0.0" +wasmprinter = "0.232.0" +wasm-encoder = "0.232.0" +wasm-smith = "0.232.0" +wasm-mutate = "0.232.0" +wit-parser = "0.232.0" +wit-component = "0.232.0" +wasm-wave = "0.232.0" +wasm-compose = "0.232.0" # Non-Bytecode Alliance maintained dependencies: # -------------------------- @@ -606,9 +606,9 @@ lto = true # wasm-wave = { git = "https://github.com/bytecodealliance/wasm-tools" } # wasm-compose = { git = "https://github.com/bytecodealliance/wasm-tools" } # wasm-metadata = { git = "https://github.com/bytecodealliance/wasm-tools" } -# wit-bindgen = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'fix-a-tpyo' } -# wit-bindgen-rt = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'fix-a-tpyo' } -# wit-bindgen-rust-macro = { git = "https://github.com/alexcrichton/wit-bindgen", branch = 'fix-a-tpyo' } +wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen" } +wit-bindgen-rt = { git = "https://github.com/bytecodealliance/wit-bindgen" } +wit-bindgen-rust-macro = { git = "https://github.com/bytecodealliance/wit-bindgen" } # wasmparser = { path = '../wasm-tools/crates/wasmparser' } # wat = { path = '../wasm-tools/crates/wat' } diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index f394a1b97c..6f9a01d370 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -6,7 +6,7 @@ use cranelift_codegen::ir::condcodes::IntCC; use cranelift_codegen::ir::{self, InstBuilder, MemFlags, Value}; use cranelift_codegen::isa::{CallConv, TargetIsa}; use cranelift_frontend::FunctionBuilder; -use wasmtime_environ::fact::SYNC_PREPARE_FIXED_PARAMS; +use wasmtime_environ::fact::PREPARE_CALL_FIXED_PARAMS; use wasmtime_environ::{CompiledFunctionBody, component::*}; use wasmtime_environ::{ HostCall, ModuleInternedTypeIndex, PtrSize, TrapSentinel, Tunables, WasmFuncType, WasmValType, @@ -255,9 +255,8 @@ impl<'a> TrampolineCompiler<'a> { me.raise_if_host_trapped(rets.pop().unwrap()); }) } - Trampoline::SyncPrepareCall { memory } => self.translate_sync_prepare(*memory), + Trampoline::PrepareCall { memory } => self.translate_prepare(*memory), Trampoline::SyncStartCall { callback } => self.translate_sync_start(*callback), - Trampoline::AsyncPrepareCall { memory } => self.translate_async_prepare(*memory), Trampoline::AsyncStartCall { callback, post_return, @@ -471,7 +470,7 @@ impl<'a> TrampolineCompiler<'a> { ); } - fn translate_sync_prepare(&mut self, memory: Option) { + fn translate_prepare(&mut self, memory: Option) { match self.abi { Abi::Wasm => {} @@ -489,8 +488,8 @@ impl<'a> TrampolineCompiler<'a> { let pointer_type = self.isa.pointer_type(); let wasm_func_ty = &self.types[self.signature].unwrap_func(); - let param_offset = SYNC_PREPARE_FIXED_PARAMS.len(); - let spill_offset = param_offset + 2; + let param_offset = PREPARE_CALL_FIXED_PARAMS.len(); + let spill_offset = param_offset + 2; // skip caller/callee vmctx let (values_vec_ptr, len) = self.compiler.allocate_stack_array_and_spill_args( &WasmFuncType::new( @@ -517,7 +516,7 @@ impl<'a> TrampolineCompiler<'a> { self.translate_intrinsic_libcall( vmctx, - host::sync_prepare, + host::prepare_call, &callee_args, TrapSentinel::Falsy, ); @@ -572,34 +571,6 @@ impl<'a> TrampolineCompiler<'a> { self.builder.ins().return_(&results); } - fn translate_async_prepare(&mut self, memory: Option) { - match self.abi { - Abi::Wasm => {} - - Abi::Array => { - // This code can only be called from (FACT-generated) Wasm, so - // we don't need to support the array ABI. - self.builder.ins().trap(TRAP_INTERNAL_ASSERT); - return; - } - } - - let args = self.builder.func.dfg.block_params(self.block0).to_vec(); - let vmctx = args[0]; - - let mut callee_args = vec![vmctx, self.load_optional_memory(vmctx, memory)]; - - // remaining parameters - callee_args.extend(args[2..].iter().copied()); - - self.translate_intrinsic_libcall( - vmctx, - host::async_prepare, - &callee_args, - TrapSentinel::Falsy, - ); - } - fn translate_async_start( &mut self, callback: Option, diff --git a/crates/environ/src/component.rs b/crates/environ/src/component.rs index 1f607d78ff..a4036b5b57 100644 --- a/crates/environ/src/component.rs +++ b/crates/environ/src/component.rs @@ -32,11 +32,26 @@ /// for transferring parameters. pub const MAX_FLAT_PARAMS: usize = 16; +/// Similar to `MAX_FLAT_PARAMS`, but used for async-lowered imports instead of +/// sync ones. +pub const MAX_FLAT_ASYNC_PARAMS: usize = 4; + /// Canonical ABI-defined constant for the maximum number of "flat" results. /// This number of results are returned directly from wasm and otherwise results /// are transferred through memory. pub const MAX_FLAT_RESULTS: usize = 1; +/// Sentinel value in `result_count_or_max_if_async` as part of the +/// `prepare_call` libcall which indicates that preparation is being done for an +/// async function that produces no result, aka there is no return pointer. +pub const PREPARE_ASYNC_NO_RESULT: u32 = u32::MAX; + +/// Sentinel value in `result_count_or_max_if_async` as part of the +/// `prepare_call` libcall which indicates that preparation is being done for an +/// async function that produces at least one result, aka there is a return +/// pointer. +pub const PREPARE_ASYNC_WITH_RESULT: u32 = u32::MAX - 1; + mod artifacts; mod info; mod names; @@ -106,12 +121,22 @@ macro_rules! foreach_builtin_component_function { #[cfg(feature = "component-model-async")] subtask_cancel(vmctx: vmctx, caller_instance: u32, async_: u8, task_id: u32) -> u64; #[cfg(feature = "component-model-async")] - sync_prepare(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, callee_instance: u32, task_return_type: u32, string_encoding: u32, result_count: u32, storage: ptr_u8, storage_len: size) -> bool; + prepare_call( + vmctx: vmctx, + memory: ptr_u8, + start: ptr_u8, + return_: ptr_u8, + caller_instance: u32, + callee_instance: u32, + task_return_type: u32, + string_encoding: u32, + result_count_or_max_if_async: u32, + storage: ptr_u8, + torage_len: size + ) -> bool; #[cfg(feature = "component-model-async")] sync_start(vmctx: vmctx, callback: ptr_u8, callee: ptr_u8, param_count: u32, storage: ptr_u8, storage_len: size) -> bool; #[cfg(feature = "component-model-async")] - async_prepare(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, callee_instance: u32, task_return_type: u32, string_encoding: u32, params: u32, results: 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; #[cfg(feature = "component-model-async")] future_new(vmctx: vmctx, ty: u32) -> u64; diff --git a/crates/environ/src/component/dfg.rs b/crates/environ/src/component/dfg.rs index 22b0f3af52..5f6f7c7d29 100644 --- a/crates/environ/src/component/dfg.rs +++ b/crates/environ/src/component/dfg.rs @@ -383,15 +383,12 @@ pub enum Trampoline { ResourceTransferBorrow, ResourceEnterCall, ResourceExitCall, - SyncPrepareCall { + PrepareCall { memory: Option, }, SyncStartCall { callback: Option, }, - AsyncPrepareCall { - memory: Option, - }, AsyncStartCall { callback: Option, post_return: Option, @@ -895,15 +892,12 @@ impl LinearizeDfg<'_> { Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow, Trampoline::ResourceEnterCall => info::Trampoline::ResourceEnterCall, Trampoline::ResourceExitCall => info::Trampoline::ResourceExitCall, - Trampoline::SyncPrepareCall { memory } => info::Trampoline::SyncPrepareCall { + 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::AsyncPrepareCall { memory } => info::Trampoline::AsyncPrepareCall { - memory: memory.map(|v| self.runtime_memory(v)), - }, Trampoline::AsyncStartCall { callback, post_return, diff --git a/crates/environ/src/component/info.rs b/crates/environ/src/component/info.rs index b65a01f1df..7a71a641aa 100644 --- a/crates/environ/src/component/info.rs +++ b/crates/environ/src/component/info.rs @@ -957,8 +957,8 @@ pub enum Trampoline { ResourceExitCall, /// An intrinsic used by FACT-generated modules to prepare a call involving - /// a sync-lowered import and async-lifted export. - SyncPrepareCall { + /// an async-lowered import and/or an async-lifted export. + PrepareCall { /// The memory used to verify that the memory specified for the /// `task.return` that is called at runtime matches the one specified in /// the lifted export. @@ -972,15 +972,6 @@ pub enum Trampoline { callback: Option, }, - /// An intrinsic used by FACT-generated modules to prepare a call involving - /// an async-lowered import function. - AsyncPrepareCall { - /// The memory used to verify that the memory specified for the - /// `task.return` that is called at runtime (if any) matches the one - /// specified in the lifted export. - memory: Option, - }, - /// An intrinsic used by FACT-generated modules to start a call involving /// an async-lowered import function. /// @@ -1086,9 +1077,8 @@ impl Trampoline { ResourceTransferBorrow => format!("component-resource-transfer-borrow"), ResourceEnterCall => format!("component-resource-enter-call"), ResourceExitCall => format!("component-resource-exit-call"), - SyncPrepareCall { .. } => format!("component-sync-prepare-call"), + PrepareCall { .. } => format!("component-prepare-call"), SyncStartCall { .. } => format!("component-sync-start-call"), - AsyncPrepareCall { .. } => format!("component-async-prepare-call"), AsyncStartCall { .. } => format!("component-async-start-call"), FutureTransfer => format!("future-transfer"), StreamTransfer => format!("stream-transfer"), diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index 60c333f12e..24ec3265ac 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -1251,6 +1251,7 @@ impl<'a, 'data> Translator<'a, 'data> { ret.callback = Some(idx); } wasmparser::CanonicalOption::CoreType(_) => todo!(), + wasmparser::CanonicalOption::Gc => todo!(), } } return ret; diff --git a/crates/environ/src/component/translate/adapt.rs b/crates/environ/src/component/translate/adapt.rs index 157de10910..ded8d63325 100644 --- a/crates/environ/src/component/translate/adapt.rs +++ b/crates/environ/src/component/translate/adapt.rs @@ -305,21 +305,14 @@ fn fact_import_to_core_def( } fact::Import::ResourceEnterCall => simple_intrinsic(dfg::Trampoline::ResourceEnterCall), fact::Import::ResourceExitCall => simple_intrinsic(dfg::Trampoline::ResourceExitCall), - fact::Import::SyncPrepareCall { memory } => { - simple_intrinsic(dfg::Trampoline::SyncPrepareCall { - memory: memory.as_ref().map(|v| dfg.memories.push(unwrap_memory(v))), - }) - } + 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 { callback: callback.clone().map(|v| dfg.callbacks.push(v)), }) } - fact::Import::AsyncPrepareCall { memory } => { - simple_intrinsic(dfg::Trampoline::AsyncPrepareCall { - memory: memory.as_ref().map(|v| dfg.memories.push(unwrap_memory(v))), - }) - } fact::Import::AsyncStartCall { callback, post_return, diff --git a/crates/environ/src/fact.rs b/crates/environ/src/fact.rs index a1d48ce610..62847cf9a4 100644 --- a/crates/environ/src/fact.rs +++ b/crates/environ/src/fact.rs @@ -43,19 +43,19 @@ mod traps; /// callee is an async-lifted export. pub const START_FLAG_ASYNC_CALLEE: i32 = 1 << 0; -/// Fixed parameter types for the `sync-prepare` built-in function. +/// Fixed parameter types for the `prepare_call` built-in function. /// -/// Note that `sync-prepare` also takes a variable number of parameters in +/// Note that `prepare_call` also takes a variable number of parameters in /// addition to these, determined by the signature of the function for which /// we're generating an adapter. -pub static SYNC_PREPARE_FIXED_PARAMS: &[ValType] = &[ - ValType::FUNCREF, - ValType::FUNCREF, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, +pub static PREPARE_CALL_FIXED_PARAMS: &[ValType] = &[ + ValType::FUNCREF, // start + ValType::FUNCREF, // return + ValType::I32, // caller_instance + ValType::I32, // callee_instance + ValType::I32, // task_return_type + ValType::I32, // string_encoding + ValType::I32, // result_count_or_max_if_async ]; /// Representation of an adapter module. @@ -88,7 +88,6 @@ pub struct Module<'a> { imported_resource_exit_call: Option, // Cached versions of imported trampolines for working with the async ABI. - imported_async_prepare_calls: HashMap, FuncIndex>, imported_async_start_calls: HashMap<(Option, Option), FuncIndex>, // Cached versions of imported trampolines for working with `stream`s, @@ -224,7 +223,6 @@ impl<'a> Module<'a> { imported_resource_transfer_borrow: None, imported_resource_enter_call: None, imported_resource_exit_call: None, - imported_async_prepare_calls: HashMap::new(), imported_async_start_calls: HashMap::new(), imported_future_transfer: None, imported_stream_transfer: None, @@ -485,14 +483,14 @@ impl<'a> Module<'a> { /// the parameters, the adapter must use this function to set up the subtask /// and stash the parameters as part of that subtask until any backpressure /// has cleared. - fn import_sync_prepare_call( + fn import_prepare_call( &mut self, suffix: &str, params: &[ValType], memory: Option, ) -> FuncIndex { let ty = self.core_types.function( - &SYNC_PREPARE_FIXED_PARAMS + &PREPARE_CALL_FIXED_PARAMS .iter() .copied() .chain(params.iter().copied()) @@ -504,7 +502,7 @@ impl<'a> Module<'a> { &format!("[prepare-call]{suffix}"), EntityType::Function(ty), ); - let import = Import::SyncPrepareCall { + let import = Import::PrepareCall { memory: memory.map(|v| self.imported_memories[v].clone()), }; self.imports.push(import); @@ -544,35 +542,6 @@ impl<'a> Module<'a> { self.imported_funcs.push(None) } - /// Import a host built-in function to set up a subtask for an async-lowered - /// import call to an async- or sync-lifted export. - fn import_async_prepare_call( - &mut self, - suffix: &str, - memory: Option, - ) -> FuncIndex { - self.import_simple_get_and_set( - "async", - &format!("[prepare-call]{suffix}"), - &[ - ValType::FUNCREF, - ValType::FUNCREF, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ValType::I32, - ], - &[], - Import::AsyncPrepareCall { - memory: memory.map(|v| self.imported_memories[v].clone()), - }, - |me| me.imported_async_prepare_calls.get(&memory).copied(), - |me, v| assert!(me.imported_async_prepare_calls.insert(memory, v).is_none()), - ) - } - /// Import a host built-in function to start a subtask for an async-lowered /// import call to an async- or sync-lifted export. /// @@ -821,9 +790,9 @@ pub enum Import { /// Tears down a previous entry and handles checking borrow-related /// metadata. ResourceExitCall, - /// An intrinsic used by FACT-generated modules to begin a call involving a - /// sync-lowered import and async-lifted export. - SyncPrepareCall { + /// An intrinsic used by FACT-generated modules to begin a call involving + /// an async-lowered import and/or an async-lifted export. + PrepareCall { /// The memory used to verify that the memory specified for the /// `task.return` that is called at runtime (if any) matches the one /// specified in the lifted export. @@ -835,14 +804,6 @@ pub enum Import { /// The callee's callback function, if any. callback: Option, }, - /// An intrinsic used by FACT-generated modules to begin a call involving an - /// async-lowered import function. - AsyncPrepareCall { - /// The memory used to verify that the memory specified for the - /// `task.return` that is called at runtime (if any) matches the one - /// specified in the lifted export. - memory: Option, - }, /// An intrinsic used by FACT-generated modules to complete a call involving /// an async-lowered import function. AsyncStartCall { diff --git a/crates/environ/src/fact/signature.rs b/crates/environ/src/fact/signature.rs index c62e254045..5d93b0007b 100644 --- a/crates/environ/src/fact/signature.rs +++ b/crates/environ/src/fact/signature.rs @@ -1,6 +1,8 @@ //! Size, align, and flattening information about component model types. -use crate::component::{ComponentTypesBuilder, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS}; +use crate::component::{ + ComponentTypesBuilder, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, +}; use crate::fact::{AdapterOptions, Context, Options}; use crate::prelude::*; use wasm_encoder::ValType; @@ -26,77 +28,78 @@ impl ComponentTypesBuilder { let ty = &self[options.ty]; let ptr_ty = options.options.ptr(); - // The async lower ABI is always `(param i32 i32) (result i32)` (for - // wasm32, anyway), regardless of the component-level signature. - // - // The first param is a pointer to linear memory where the parameters have - // been stored by the caller, the second param is a pointer to linear - // memory where the results should be stored by the callee, and the - // result is a status code optionally ORed with a subtask ID. - if let (Context::Lower, true) = (&context, options.options.async_) { - return Signature { - params: vec![ptr_ty; 2], - results: vec![ValType::I32], - }; - } + let max_flat_params = match (&context, options.options.async_) { + // Async imports have a lower limit on their flat parameters than + // normal functions, so account for that here. + (Context::Lower, true) => MAX_FLAT_ASYNC_PARAMS, + _ => MAX_FLAT_PARAMS, + }; // If we're lifting async or sync, or if we're lowering sync, we can - // pass up to `MAX_FLAT_PARAMS` via the stack. + // pass up to `max_flat_params` via the stack. let mut params = match self.flatten_types( &options.options, - MAX_FLAT_PARAMS, + max_flat_params, self[ty.params].types.iter().copied(), ) { Some(list) => list, - None => { - vec![ptr_ty] - } + None => vec![ptr_ty], }; - // If we're lifting async with a callback, the result is an `i32` status - // code, optionally ORed with a guest task identifier, and the result - // will be returned via `task.return`. - // - // If we're lifting async without a callback, then there's no need to return - // anything here since the result will be returned via `task.return` and the - // guest will use `task.wait` rather than return a status code in order to suspend - // itself, if necessary. - if options.options.async_ { - return Signature { - params, - results: if options.options.callback.is_some() { + let results = match (&context, options.options.async_) { + // Async lowered functions store their results in a pointer passed + // as a function argument, so if results are present then add + // another pointer to the list of parameters. + // + // The actual ABI results are then a status code, so account for + // that here. + (Context::Lower, true) => { + if !self[ty.results].types.is_empty() { + params.push(ptr_ty); + } + vec![ValType::I32] + } + + // Async lifted functions transmit results through `task.return` + // meaning there's no handling in the ABI of results. If this is a + // callback-based functions then the export returns a status code, + // otherwise for stackful functions there is no return value. + (Context::Lift, true) => { + if options.options.callback.is_some() { vec![ptr_ty] } else { Vec::new() - }, - }; - } + } + } - // If we've reached this point, we're either lifting or lowering sync, - // in which case the guest will return up to `MAX_FLAT_RESULTS` via the - // stack or spill to linear memory otherwise. - let results = match self.flatten_types( - &options.options, - MAX_FLAT_RESULTS, - self[ty.results].types.iter().copied(), - ) { - Some(list) => list, - None => { - match context { - // For a lifted function too-many-results gets translated to a - // returned pointer where results are read from. The callee - // allocates space here. - Context::Lift => vec![ptr_ty], - // For a lowered function too-many-results becomes a return - // pointer which is passed as the last argument. The caller - // allocates space here. - Context::Lower => { - params.push(ptr_ty); - Vec::new() + // If we've reached this point, we're either lifting or lowering + // sync, in which case the guest will return up to + // `MAX_FLAT_RESULTS` via the stack or spill to linear memory + // otherwise. + (_, false) => match self.flatten_types( + &options.options, + MAX_FLAT_RESULTS, + self[ty.results].types.iter().copied(), + ) { + Some(list) => list, + None => { + match context { + // For a lifted function too-many-results gets translated to a + // returned pointer where results are read from. The callee + // allocates space here. + Context::Lift => vec![ptr_ty], + // For a lowered function too-many-results becomes a return + // pointer which is passed as the last argument. The caller + // allocates space here. + Context::Lower => { + params.push(ptr_ty); + Vec::new() + } } } - } + }, }; + Signature { params, results } } @@ -117,19 +120,18 @@ impl ComponentTypesBuilder { ) -> Signature { let lower_ty = &self[lower.ty]; let lower_ptr_ty = lower.options.ptr(); - let params = if lower.options.async_ { - vec![lower_ptr_ty] + let max_flat_params = if lower.options.async_ { + MAX_FLAT_ASYNC_PARAMS } else { - match self.flatten_types( - &lower.options, - MAX_FLAT_PARAMS, - self[lower_ty.params].types.iter().copied(), - ) { - Some(list) => list, - None => { - vec![lower_ptr_ty] - } - } + MAX_FLAT_PARAMS + }; + let params = match self.flatten_types( + &lower.options, + max_flat_params, + self[lower_ty.params].types.iter().copied(), + ) { + Some(list) => list, + None => vec![lower_ptr_ty], }; let lift_ty = &self[lift.ty]; @@ -158,13 +160,11 @@ impl ComponentTypesBuilder { options: &Options, tys: impl IntoIterator, ) -> Option> { - if options.async_ { - // When lowering an async function, we always spill parameters to - // linear memory. - None - } else { - self.flatten_types(options, MAX_FLAT_RESULTS, tys) - } + // Async functions "use the stack" for zero return values, meaning + // nothing is actually passed, but otherwise if anything is returned + // it's always through memory. + let max = if options.async_ { 0 } else { MAX_FLAT_RESULTS }; + self.flatten_types(options, max, tys) } pub(super) fn flatten_lifting_types( @@ -214,15 +214,18 @@ impl ComponentTypesBuilder { }; let lower_ty = &self[lower.ty]; + let lower_result_tys = &self[lower_ty.results]; let results = if lower.options.async_ { // Add return pointer - params.push(lift_ptr_ty); + if !lower_result_tys.types.is_empty() { + params.push(lift_ptr_ty); + } Vec::new() } else { match self.flatten_types( &lower.options, MAX_FLAT_RESULTS, - self[lower_ty.results].types.iter().copied(), + lower_result_tys.types.iter().copied(), ) { Some(list) => list, None => { diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index 87f0c138a9..a284cbaa59 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -17,10 +17,11 @@ use crate::component::{ CanonicalAbiInfo, ComponentTypesBuilder, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, FixedEncoding as FE, - FlatType, InterfaceType, MAX_FLAT_PARAMS, StringEncoding, Transcode, - TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFlagsIndex, TypeFutureTableIndex, - TypeListIndex, TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, - TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, VariantInfo, + FlatType, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT, + PREPARE_ASYNC_WITH_RESULT, StringEncoding, Transcode, TypeComponentLocalErrorContextTableIndex, + TypeEnumIndex, TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, + TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex, + TypeVariantIndex, VariantInfo, }; use crate::fact::signature::Signature; use crate::fact::transcode::Transcoder; @@ -31,6 +32,7 @@ use crate::fact::{ }; use crate::prelude::*; use crate::{FuncIndex, GlobalIndex}; +use cranelift_entity::Signed; use std::collections::HashMap; use std::mem; use std::ops::Range; @@ -186,12 +188,13 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { // point a `STATUS_RETURNED` event will be delivered to the caller. let start = async_start_adapter(module); let return_ = async_return_adapter(module); - let (compiler, _, lift_sig) = compiler(module, adapter); + let (compiler, lower_sig, lift_sig) = compiler(module, adapter); compiler.compile_async_to_async_adapter( adapter, start, return_, i32::try_from(lift_sig.params.len()).unwrap(), + &lower_sig, ); } (false, true) => { @@ -241,13 +244,14 @@ pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) { let lift_sig = module.types.signature(&adapter.lift, Context::Lift); let start = async_start_adapter(module); let return_ = async_return_adapter(module); - let (compiler, ..) = compiler(module, adapter); + let (compiler, lower_sig, ..) = compiler(module, adapter); compiler.compile_async_to_sync_adapter( adapter, start, return_, i32::try_from(lift_sig.params.len()).unwrap(), i32::try_from(lift_sig.results.len()).unwrap(), + &lower_sig, ); } } @@ -418,40 +422,13 @@ impl<'a, 'b> Compiler<'a, 'b> { start: FunctionId, return_: FunctionId, param_count: i32, + lower_sig: &Signature, ) { - let prepare = self - .module - .import_async_prepare_call(&adapter.name, adapter.lift.options.memory); let start_call = self.module .import_async_start_call(&adapter.name, adapter.lift.options.callback, None); - self.flush_code(); - self.module.funcs[self.result] - .body - .push(Body::RefFunc(start)); - self.module.funcs[self.result] - .body - .push(Body::RefFunc(return_)); - 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(I32Const( - i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(), - )); - self.instruction(I32Const(i32::from( - adapter.lift.options.string_encoding as u8, - ))); - // Async-lowered imports pass params and receive results via linear - // memory, and those pointers are in the the first and second params to - // this adapter. We pass them on to the host so it can store them in - // the subtask for later use. - self.instruction(LocalGet(0)); - self.instruction(LocalGet(1)); - self.instruction(Call(prepare.as_u32())); + self.call_prepare(adapter, start, return_, lower_sig, false); // TODO: As an optimization, consider checking the backpressure flag on // the callee instance and, if it's unset _and_ the callee uses a @@ -478,33 +455,31 @@ impl<'a, 'b> Compiler<'a, 'b> { self.finish() } - /// Compile an adapter function supporting a sync-lowered import to an - /// async-lifted export. + /// Invokes the `prepare_call` builtin with the provided parameters for this + /// adapter. /// - /// This uses a pair of `sync-prepare` and `sync-start` built-in functions - /// to set up and start a subtask, respectively. `sync-prepare` accepts - /// `start` and `return_` functions which copy the parameters and results, - /// respectively; the host will call the former when the callee has cleared - /// its backpressure flag and the latter when the callee has called - /// `task.return`. - fn compile_sync_to_async_adapter( - mut self, + /// This is part of a async lower and/or async lift adapter. This is not + /// used for a sync->sync function call. This is done to create the task on + /// the host side of the runtime and such. This will notably invoke a + /// Cranelift builtin which will spill all wasm-level parameters to the + /// stack to handle variadic signatures. + /// + /// Note that the `prepare_sync` parameter here configures the + /// `result_count_or_max_if_async` parameter to indicate whether this is a + /// sync or async prepare. + fn call_prepare( + &mut self, adapter: &AdapterData, start: FunctionId, return_: FunctionId, - lift_param_count: i32, lower_sig: &Signature, + prepare_sync: bool, ) { - let prepare = self.module.import_sync_prepare_call( + let prepare = self.module.import_prepare_call( &adapter.name, &lower_sig.params, adapter.lift.options.memory, ); - let start_call = self.module.import_sync_start_call( - &adapter.name, - adapter.lift.options.callback, - &lower_sig.results, - ); self.flush_code(); self.module.funcs[self.result] @@ -525,28 +500,63 @@ impl<'a, 'b> Compiler<'a, 'b> { self.instruction(I32Const(i32::from( adapter.lift.options.string_encoding as u8, ))); - self.instruction(I32Const( - i32::try_from( - self.types - .flatten_types( - &adapter.lower.options, - usize::MAX, - self.types[self.types[adapter.lower.ty].results] - .types - .iter() - .copied(), - ) - .map(|v| v.len()) - .unwrap_or(usize::try_from(i32::MAX).unwrap()), - ) - .unwrap(), - )); + // flag this as a preparation for either an async call or sync call, + // depending on `prepare_sync` + let result_types = &self.types[self.types[adapter.lower.ty].results].types; + if prepare_sync { + self.instruction(I32Const( + i32::try_from( + self.types + .flatten_types( + &adapter.lower.options, + usize::MAX, + result_types.iter().copied(), + ) + .map(|v| v.len()) + .unwrap_or(usize::try_from(i32::MAX).unwrap()), + ) + .unwrap(), + )); + } else { + if result_types.len() > 0 { + self.instruction(I32Const(PREPARE_ASYNC_WITH_RESULT.signed())); + } else { + self.instruction(I32Const(PREPARE_ASYNC_NO_RESULT.signed())); + } + } + + // forward all our own arguments on to the host stub for index in 0..lower_sig.params.len() { self.instruction(LocalGet(u32::try_from(index).unwrap())); } - self.instruction(Call(prepare.as_u32())); + } + + /// Compile an adapter function supporting a sync-lowered import to an + /// async-lifted export. + /// + /// This uses a pair of `sync-prepare` and `sync-start` built-in functions + /// to set up and start a subtask, respectively. `sync-prepare` accepts + /// `start` and `return_` functions which copy the parameters and results, + /// respectively; the host will call the former when the callee has cleared + /// its backpressure flag and the latter when the callee has called + /// `task.return`. + fn compile_sync_to_async_adapter( + mut self, + adapter: &AdapterData, + start: FunctionId, + return_: FunctionId, + lift_param_count: i32, + lower_sig: &Signature, + ) { + let start_call = self.module.import_sync_start_call( + &adapter.name, + adapter.lift.options.callback, + &lower_sig.results, + ); + + self.call_prepare(adapter, start, return_, lower_sig, true); // TODO: As an optimization, consider checking the backpressure flag on // the callee instance and, if it's unset _and_ the callee uses a @@ -584,36 +594,13 @@ impl<'a, 'b> Compiler<'a, 'b> { return_: FunctionId, param_count: i32, result_count: i32, + lower_sig: &Signature, ) { - let prepare = self - .module - .import_async_prepare_call(&adapter.name, adapter.lift.options.memory); let start_call = self.module .import_async_start_call(&adapter.name, None, adapter.lift.post_return); - self.flush_code(); - self.module.funcs[self.result] - .body - .push(Body::RefFunc(start)); - self.module.funcs[self.result] - .body - .push(Body::RefFunc(return_)); - 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(I32Const( - i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(), - )); - self.instruction(I32Const(i32::from( - adapter.lift.options.string_encoding as u8, - ))); - self.instruction(LocalGet(0)); - self.instruction(LocalGet(1)); - self.instruction(Call(prepare.as_u32())); + self.call_prepare(adapter, start, return_, lower_sig, false); // We export this function so we can pass a funcref to the host. // @@ -809,12 +796,17 @@ impl<'a, 'b> Compiler<'a, 'b> { // TODO: handle subtyping assert_eq!(src_tys.len(), dst_tys.len()); - let src_flat = if adapter.lower.options.async_ { - None + // Async lowered functions have a smaller limit on flat parameters, but + // their destination, a lifted function, does not have a different limit + // than sync functions. + let max_flat_params = if adapter.lower.options.async_ { + MAX_FLAT_ASYNC_PARAMS } else { - self.types - .flatten_types(lower_opts, MAX_FLAT_PARAMS, src_tys.iter().copied()) + MAX_FLAT_PARAMS }; + let src_flat = + self.types + .flatten_types(lower_opts, max_flat_params, src_tys.iter().copied()); let dst_flat = self.types .flatten_types(lift_opts, MAX_FLAT_PARAMS, dst_tys.iter().copied()); diff --git a/crates/misc/component-async-tests/http/src/lib.rs b/crates/misc/component-async-tests/http/src/lib.rs index 8ffefb2895..4c7f99c52e 100644 --- a/crates/misc/component-async-tests/http/src/lib.rs +++ b/crates/misc/component-async-tests/http/src/lib.rs @@ -249,19 +249,13 @@ impl wasi::http::types::HostBodyConcurrent for WasiHt async fn finish( accessor: &mut Accessor, this: Resource, - ) -> wasmtime::Result>> { - let trailers = accessor.with(|mut store| { + ) -> wasmtime::Result>>> { + let trailers = accessor.with(|mut store| -> wasmtime::Result<_> { let trailers = store.get().table().delete(this)?.trailers; - Ok::, anyhow::Error>(match trailers { - Some(t) => t, - None => { - let instance = store.instance(); - instance.future(&mut store)?.1 - } - }) + Ok(trailers) })?; - Ok(trailers.into()) + Ok(trailers.map(|t| t.into())) } } diff --git a/crates/misc/component-async-tests/tests/scenario/proxy.rs b/crates/misc/component-async-tests/tests/scenario/proxy.rs index 5bb56bf031..cb4a0b3c2a 100644 --- a/crates/misc/component-async-tests/tests/scenario/proxy.rs +++ b/crates/misc/component-async-tests/tests/scenario/proxy.rs @@ -158,7 +158,8 @@ async fn test_http_echo(component: &[u8], use_compression: bool) -> Result<()> { let trailers = vec![("fizz".into(), b"buzz".into())]; - let (request_trailers_tx, request_trailers_rx) = instance.future(&mut store)?; + let (request_trailers_tx, request_trailers_rx) = + instance.future(|| unreachable!(), &mut store)?; let request_trailers = IoView::table(store.data_mut()).push(Fields(trailers.clone()))?; diff --git a/crates/misc/component-async-tests/tests/scenario/streams.rs b/crates/misc/component-async-tests/tests/scenario/streams.rs index 567f6510db..b98454a536 100644 --- a/crates/misc/component-async-tests/tests/scenario/streams.rs +++ b/crates/misc/component-async-tests/tests/scenario/streams.rs @@ -65,24 +65,24 @@ pub async fn async_watch_streams() -> Result<()> { instance.run(&mut store, rx.watch_writer().0).await?; // Test watching and then dropping the read end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; let watch = tx.watch_reader().0; drop(rx); instance.run(&mut store, watch).await?; // Test dropping and then watching the read end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; drop(rx); instance.run(&mut store, tx.watch_reader().0).await?; // Test watching and then dropping the write end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; let watch = rx.watch_writer().0; drop(tx); instance.run(&mut store, watch).await?; // Test dropping and then watching the write end of a future. - let (tx, rx) = instance.future::(&mut store)?; + let (tx, rx) = instance.future::(|| 42, &mut store)?; drop(tx); instance.run(&mut store, rx.watch_writer().0).await?; @@ -242,8 +242,8 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { // Next, test futures host->host { - let (tx, rx) = instance.future(&mut store)?; - let (tx_ignored, rx_ignored) = instance.future(&mut store)?; + let (tx, rx) = instance.future(|| unreachable!(), &mut store)?; + let (tx_ignored, rx_ignored) = instance.future(|| 42u8, &mut store)?; let mut futures = FuturesUnordered::new(); futures.push(tx.write(value).map(FutureEvent::Write).boxed()); @@ -340,8 +340,8 @@ pub async fn test_closed_streams(watch: bool) -> Result<()> { // Next, test futures host->guest { - let (tx, rx) = instance.future(&mut store)?; - let (tx_ignored, rx_ignored) = instance.future(&mut store)?; + let (tx, rx) = instance.future(|| unreachable!(), &mut store)?; + let (tx_ignored, rx_ignored) = instance.future(|| unreachable!(), &mut store)?; let mut futures = FuturesUnordered::new(); futures.push( diff --git a/crates/misc/component-async-tests/tests/scenario/transmit.rs b/crates/misc/component-async-tests/tests/scenario/transmit.rs index 0e4c3b273b..41794d5aa0 100644 --- a/crates/misc/component-async-tests/tests/scenario/transmit.rs +++ b/crates/misc/component-async-tests/tests/scenario/transmit.rs @@ -345,8 +345,8 @@ async fn test_transmit_with(component: &[u8]) -> R let (control_tx, control_rx) = instance.stream::<_, _, Option<_>, _, _>(&mut store)?; let (caller_stream_tx, caller_stream_rx) = instance.stream::<_, _, Option<_>, _, _>(&mut store)?; - let (caller_future1_tx, caller_future1_rx) = instance.future(&mut store)?; - let (_caller_future2_tx, caller_future2_rx) = instance.future(&mut store)?; + let (caller_future1_tx, caller_future1_rx) = instance.future(|| unreachable!(), &mut store)?; + let (_caller_future2_tx, caller_future2_rx) = instance.future(|| unreachable!(), &mut store)?; let mut futures = FuturesUnordered::< Pin>> + Send + 'static>>, diff --git a/crates/misc/component-async-tests/wit/deps/http/types.wit b/crates/misc/component-async-tests/wit/deps/http/types.wit index 1b648878bc..08d4049769 100644 --- a/crates/misc/component-async-tests/wit/deps/http/types.wit +++ b/crates/misc/component-async-tests/wit/deps/http/types.wit @@ -266,7 +266,7 @@ interface types { /// Takes ownership of `body`, and returns a `trailers`. This function will /// trap if a `stream` child is still alive. - finish: static func(this: body) -> future; + finish: static func(this: body) -> option>; } /// Represents an HTTP Request. diff --git a/crates/test-programs/src/bin/async_cancel_callee.rs b/crates/test-programs/src/bin/async_cancel_callee.rs index fd5eb8f962..1379676862 100644 --- a/crates/test-programs/src/bin/async_cancel_callee.rs +++ b/crates/test-programs/src/bin/async_cancel_callee.rs @@ -6,7 +6,6 @@ mod bindings { } use { - std::ptr, test_programs::async_::{ CALLBACK_CODE_EXIT, CALLBACK_CODE_WAIT, EVENT_CANCELLED, EVENT_NONE, EVENT_SUBTASK, STATUS_RETURN_CANCELLED, STATUS_RETURNED, STATUS_STARTED, context_get, context_set, @@ -42,10 +41,10 @@ unsafe fn sleep_millis(_: u64) { #[link(wasm_import_module = "local:local/sleep")] unsafe extern "C" { #[link_name = "[async-lower][async]sleep-millis"] - fn sleep_millis_async(_: *mut u8, _: *mut u8) -> u32; + fn sleep_millis_async(ms: u64) -> u32; } #[cfg(not(target_arch = "wasm32"))] -unsafe fn sleep_millis_async(_: *mut u8, _: *mut u8) -> u32 { +unsafe fn sleep_millis_async(_ms: u64) -> u32 { unreachable!() } @@ -74,13 +73,11 @@ enum State { set: u32, waitable: u32, params: SleepParams, - param_ptr: *mut u64, }, S2 { set: u32, waitable: u32, params: SleepParams, - param_ptr: *mut u64, }, } @@ -131,9 +128,7 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( State::S0(params) => { assert_eq!(event0, EVENT_NONE); - let param_ptr = Box::into_raw(Box::new(params.time_in_millis)); - - let status = sleep_millis_async(param_ptr.cast(), ptr::null_mut()); + let status = sleep_millis_async(params.time_in_millis); let waitable = status >> 4; let status = status & 0xF; @@ -147,7 +142,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params: *params, - param_ptr, }; CALLBACK_CODE_WAIT | (set << 4) @@ -157,7 +151,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params, - param_ptr, } => { assert_eq!(event0, EVENT_CANCELLED); @@ -194,9 +187,7 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( CALLBACK_CODE_EXIT } else { - **param_ptr = params.on_cancel_delay_millis; - - let status = sleep_millis_async(param_ptr.cast(), ptr::null_mut()); + let status = sleep_millis_async(params.on_cancel_delay_millis); let waitable = status >> 4; let status = status & 0xF; @@ -211,7 +202,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params: *params, - param_ptr: *param_ptr, }; CALLBACK_CODE_WAIT | (set << 4) @@ -222,7 +212,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( set, waitable, params, - param_ptr, } => { assert_eq!(event0, EVENT_SUBTASK); assert_eq!(event1, *waitable); @@ -237,7 +226,6 @@ unsafe extern "C" fn callback_sleep_with_options_sleep_millis( waitable_join(*waitable, 0); subtask_drop(*waitable); waitable_set_drop(*set); - drop(Box::from_raw(*param_ptr)); match params.on_cancel { ON_CANCEL_TASK_RETURN => task_return_sleep_millis(), diff --git a/crates/test-programs/src/bin/async_cancel_caller.rs b/crates/test-programs/src/bin/async_cancel_caller.rs index 38bcead301..5f013839c2 100644 --- a/crates/test-programs/src/bin/async_cancel_caller.rs +++ b/crates/test-programs/src/bin/async_cancel_caller.rs @@ -5,14 +5,11 @@ mod bindings { }); } -use { - std::ptr, - test_programs::async_::{ - BLOCKED, CALLBACK_CODE_EXIT, CALLBACK_CODE_WAIT, EVENT_NONE, EVENT_SUBTASK, - STATUS_RETURN_CANCELLED, STATUS_RETURNED, STATUS_START_CANCELLED, STATUS_STARTED, - STATUS_STARTING, context_get, context_set, subtask_cancel, subtask_cancel_async, - subtask_drop, waitable_join, waitable_set_drop, waitable_set_new, - }, +use test_programs::async_::{ + BLOCKED, CALLBACK_CODE_EXIT, CALLBACK_CODE_WAIT, EVENT_NONE, EVENT_SUBTASK, + STATUS_RETURN_CANCELLED, STATUS_RETURNED, STATUS_START_CANCELLED, STATUS_STARTED, + STATUS_STARTING, context_get, context_set, subtask_cancel, subtask_cancel_async, subtask_drop, + waitable_join, waitable_set_drop, waitable_set_new, }; #[cfg(target_arch = "wasm32")] @@ -42,10 +39,10 @@ mod sleep { #[link(wasm_import_module = "local:local/sleep")] unsafe extern "C" { #[link_name = "[async-lower][async]sleep-millis"] - pub fn sleep_millis(_: *mut u8, _: *mut u8) -> u32; + pub fn sleep_millis(_: u64) -> u32; } #[cfg(not(target_arch = "wasm32"))] - pub unsafe fn sleep_millis(_: *mut u8, _: *mut u8) -> u32 { + pub unsafe fn sleep_millis(_: u64) -> u32 { unreachable!() } } @@ -55,10 +52,10 @@ mod sleep_with_options { #[link(wasm_import_module = "local:local/sleep-with-options")] unsafe extern "C" { #[link_name = "[async-lower][async]sleep-millis"] - pub fn sleep_millis(_: *mut u8, _: *mut u8) -> u32; + pub fn sleep_millis(_: *mut u8) -> u32; } #[cfg(not(target_arch = "wasm32"))] - pub unsafe fn sleep_millis(_: *mut u8, _: *mut u8) -> u32 { + pub unsafe fn sleep_millis(_: *mut u8) -> u32 { unreachable!() } } @@ -107,7 +104,6 @@ enum State { set: u32, waitable: u32, params: *mut SleepParams, - params2: *mut u64, }, } @@ -141,7 +137,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 mode: *mode, })); - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -167,7 +163,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 set_backpressure(false); - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -193,7 +189,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (*params).on_cancel_delay_millis = 10; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -237,7 +233,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).on_cancel = ON_CANCEL_TASK_RETURN; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -288,7 +284,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).on_cancel = ON_CANCEL_TASK_CANCEL; (**params).synchronous_delay = true; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -337,9 +333,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 // `STATUS_RETURNED` when complete since the callee cannot // actually be cancelled. - let params2 = Box::into_raw(Box::new(10u64)); - - let status = sleep::sleep_millis(params2.cast(), ptr::null_mut()); + let status = sleep::sleep_millis(10); let waitable = status >> 4; let status = status & 0xF; @@ -358,7 +352,6 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 set, waitable, params: *params, - params2, }; CALLBACK_CODE_WAIT | (set << 4) @@ -368,7 +361,6 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 set, waitable, params, - params2, } => { assert_eq!(event0, EVENT_SUBTASK); assert_eq!(event1, *waitable); @@ -377,7 +369,6 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 waitable_join(*waitable, 0); subtask_drop(*waitable); waitable_set_drop(*set); - drop(Box::from_raw(*params2)); // Next, call and cancel `sleep_with_options::sleep_millis` with // a non-zero cancel delay, and specify that the callee should @@ -387,7 +378,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).synchronous_delay = true; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; @@ -406,7 +397,7 @@ unsafe extern "C" fn callback_run(event0: u32, event1: u32, event2: u32) -> u32 (**params).synchronous_delay = false; - let status = sleep_with_options::sleep_millis(params.cast(), ptr::null_mut()); + let status = sleep_with_options::sleep_millis(params.cast()); let waitable = status >> 4; let status = status & 0xF; diff --git a/crates/test-programs/src/bin/async_closed_streams.rs b/crates/test-programs/src/bin/async_closed_streams.rs index 931a94ad5e..cf90cf4a96 100644 --- a/crates/test-programs/src/bin/async_closed_streams.rs +++ b/crates/test-programs/src/bin/async_closed_streams.rs @@ -24,7 +24,7 @@ impl Guest for Component { } async fn read_future(rx: FutureReader, expected: u8, _rx_ignored: FutureReader) { - assert_eq!(rx.await.unwrap(), expected); + assert_eq!(rx.await, expected); } } diff --git a/crates/test-programs/src/bin/async_http_echo.rs b/crates/test-programs/src/bin/async_http_echo.rs index d9f9a28d80..094ecb0031 100644 --- a/crates/test-programs/src/bin/async_http_echo.rs +++ b/crates/test-programs/src/bin/async_http_echo.rs @@ -30,7 +30,7 @@ impl Handler for Component { } else { // ...but we do it the more difficult, less efficient way here to exercise various component model // features (e.g. `future`s, `stream`s, and post-return asynchronous execution): - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (mut pipe_tx, pipe_rx) = wit_stream::new(); async_support::spawn(async move { @@ -51,8 +51,8 @@ impl Handler for Component { drop(pipe_tx); - if let Some(trailers) = Body::finish(body).await { - trailers_tx.write(trailers).await.unwrap(); + if let Some(trailers) = Body::finish(body) { + trailers_tx.write(trailers.await).await.unwrap(); } }); diff --git a/crates/test-programs/src/bin/async_http_middleware.rs b/crates/test-programs/src/bin/async_http_middleware.rs index fde01550ca..17036d8af6 100644 --- a/crates/test-programs/src/bin/async_http_middleware.rs +++ b/crates/test-programs/src/bin/async_http_middleware.rs @@ -60,7 +60,7 @@ impl Handler for Component { let body = if content_deflated { // Next, spawn a task to pipe and decode the original request body and trailers into a new request // we'll create below. This will run concurrently with any code in the imported `wasi:http/handler`. - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (mut pipe_tx, pipe_rx) = wit_stream::new(); async_support::spawn(async move { @@ -84,8 +84,8 @@ impl Handler for Component { drop(pipe_tx); } - if let Some(trailers) = Body::finish(body).await { - trailers_tx.write(trailers).await.unwrap(); + if let Some(trailers) = Body::finish(body) { + trailers_tx.write(trailers.await).await.unwrap(); } }); @@ -120,7 +120,7 @@ impl Handler for Component { // Spawn another task; this one is to pipe and encode the original response body and trailers into a // new response we'll create below. This will run concurrently with the caller's code (i.e. it won't // necessarily complete before we return a value). - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (mut pipe_tx, pipe_rx) = wit_stream::new(); async_support::spawn(async move { @@ -144,8 +144,8 @@ impl Handler for Component { drop(pipe_tx); } - if let Some(trailers) = Body::finish(body).await { - trailers_tx.write(trailers).await.unwrap(); + if let Some(trailers) = Body::finish(body) { + trailers_tx.write(trailers.await).await.unwrap(); } }); diff --git a/crates/test-programs/src/bin/async_poll_stackless.rs b/crates/test-programs/src/bin/async_poll_stackless.rs index 6277783e74..7ad3908a3e 100644 --- a/crates/test-programs/src/bin/async_poll_stackless.rs +++ b/crates/test-programs/src/bin/async_poll_stackless.rs @@ -37,9 +37,9 @@ fn async_when_ready() -> u32 { #[link(wasm_import_module = "local:local/ready")] unsafe extern "C" { #[link_name = "[async-lower][async]when-ready"] - fn call_when_ready(_: *mut u8, _: *mut u8) -> u32; + fn call_when_ready() -> u32; } - unsafe { call_when_ready(std::ptr::null_mut(), std::ptr::null_mut()) } + unsafe { call_when_ready() } } } diff --git a/crates/test-programs/src/bin/async_poll_synchronous.rs b/crates/test-programs/src/bin/async_poll_synchronous.rs index 6f1689ed18..962b5e7ba7 100644 --- a/crates/test-programs/src/bin/async_poll_synchronous.rs +++ b/crates/test-programs/src/bin/async_poll_synchronous.rs @@ -28,9 +28,9 @@ fn async_when_ready() -> u32 { #[link(wasm_import_module = "local:local/ready")] unsafe extern "C" { #[link_name = "[async-lower][async]when-ready"] - fn call_when_ready(_: *mut u8, _: *mut u8) -> u32; + fn call_when_ready() -> u32; } - unsafe { call_when_ready(std::ptr::null_mut(), std::ptr::null_mut()) } + unsafe { call_when_ready() } } } diff --git a/crates/test-programs/src/bin/async_round_trip_stackful.rs b/crates/test-programs/src/bin/async_round_trip_stackful.rs index 991ad6e065..381ef68142 100644 --- a/crates/test-programs/src/bin/async_round_trip_stackful.rs +++ b/crates/test-programs/src/bin/async_round_trip_stackful.rs @@ -43,10 +43,10 @@ unsafe extern "C" fn task_return_foo(_ptr: *mut u8, _len: usize) { #[link(wasm_import_module = "local:local/baz")] unsafe extern "C" { #[link_name = "[async-lower][async]foo"] - fn import_foo(params: *mut u8, results: *mut u8) -> u32; + fn import_foo(ptr: *mut u8, len: usize, results: *mut u8) -> u32; } #[cfg(not(target_arch = "wasm32"))] -unsafe extern "C" fn import_foo(_params: *mut u8, _results: *mut u8) -> u32 { +unsafe extern "C" fn import_foo(_ptr: *mut u8, _len: usize, _results: *mut u8) -> u32 { unreachable!() } @@ -65,13 +65,9 @@ unsafe extern "C" fn export_foo(ptr: *mut u8, len: usize) { let layout = Layout::from_size_align(8, 4).unwrap(); - let params = alloc::alloc(layout); - *params.cast::<*mut u8>() = s.as_ptr().cast_mut(); - *params.add(4).cast::() = s.len(); - let results = alloc::alloc(layout); - let result = import_foo(params, results); + let result = import_foo(s.as_ptr().cast_mut(), s.len(), results); let mut status = result & 0xf; let call = result >> 4; let set = waitable_set_new(); @@ -93,7 +89,6 @@ unsafe extern "C" fn export_foo(ptr: *mut u8, len: usize) { } } } - alloc::dealloc(params, layout); let len = *results.add(4).cast::(); let s = format!( diff --git a/crates/test-programs/src/bin/async_transmit_callee.rs b/crates/test-programs/src/bin/async_transmit_callee.rs index 9d884583db..533c3d5045 100644 --- a/crates/test-programs/src/bin/async_transmit_callee.rs +++ b/crates/test-programs/src/bin/async_transmit_callee.rs @@ -31,8 +31,8 @@ impl Guest for Component { FutureReader, ) { let (mut callee_stream_tx, callee_stream_rx) = wit_stream::new(); - let (callee_future_tx1, callee_future_rx1) = wit_future::new(); - let (callee_future_tx2, callee_future_rx2) = wit_future::new(); + let (callee_future_tx1, callee_future_rx1) = wit_future::new(|| todo!()); + let (callee_future_tx2, callee_future_rx2) = wit_future::new(|| String::new()); async_support::spawn(async move { let mut caller_future_rx1 = Some(caller_future_rx1); @@ -50,10 +50,7 @@ impl Guest for Component { ); } Control::ReadFuture(value) => { - assert_eq!( - caller_future_rx1.take().unwrap().into_future().await, - Some(value) - ); + assert_eq!(caller_future_rx1.take().unwrap().into_future().await, value); } Control::WriteStream(value) => { assert!(callee_stream_tx.write_one(value).await.is_none()); diff --git a/crates/test-programs/src/bin/async_transmit_caller.rs b/crates/test-programs/src/bin/async_transmit_caller.rs index dfa03642e5..596e4a7247 100644 --- a/crates/test-programs/src/bin/async_transmit_caller.rs +++ b/crates/test-programs/src/bin/async_transmit_caller.rs @@ -29,8 +29,8 @@ impl Guest for Component { async fn run() { let (mut control_tx, control_rx) = wit_stream::new(); let (mut caller_stream_tx, caller_stream_rx) = wit_stream::new(); - let (mut caller_future_tx1, caller_future_rx1) = wit_future::new(); - let (caller_future_tx2, caller_future_rx2) = wit_future::new(); + let (mut caller_future_tx1, caller_future_rx1) = wit_future::new(|| todo!()); + let (caller_future_tx2, caller_future_rx2) = wit_future::new(|| String::new()); let (mut callee_stream_rx, mut callee_future_rx1, callee_future_rx2) = transmit::exchange( control_rx, @@ -195,7 +195,7 @@ impl Guest for Component { .await .is_none() ); - assert_eq!(callee_future_rx1.into_future().await, Some("b".into())); + assert_eq!(callee_future_rx1.into_future().await, "b"); // Start writing a value to the stream, but drop the stream without telling the peer to read. let send = Box::pin(caller_stream_tx.write_one("d".into())); diff --git a/crates/test-programs/src/bin/p3_api_proxy.rs b/crates/test-programs/src/bin/p3_api_proxy.rs index 420b0e404a..8661e02a3f 100644 --- a/crates/test-programs/src/bin/p3_api_proxy.rs +++ b/crates/test-programs/src/bin/p3_api_proxy.rs @@ -34,7 +34,7 @@ impl test_programs::p3::proxy::exports::wasi::http::handler::Guest for T { let hdrs = Headers::new(); let (mut contents_tx, contents_rx) = wit_stream::new(); - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (resp, transmit) = Response::new(hdrs, Some(contents_rx), trailers_rx); spawn(async { join!( @@ -47,12 +47,7 @@ impl test_programs::p3::proxy::exports::wasi::http::handler::Guest for T { .await .expect("failed to write trailers"); }, - async { - transmit - .await - .expect("failed to transmit response") - .unwrap() - } + async { transmit.await.unwrap() } ); }); Ok(resp) diff --git a/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs b/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs index 9023ec2bfe..94f1adeb8f 100644 --- a/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs +++ b/crates/test-programs/src/bin/p3_filesystem_file_read_write.rs @@ -36,7 +36,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { ); let (data_rx, data_fut) = file.read_via_stream(0); let contents = data_rx.collect().await; - data_fut.await.unwrap().unwrap(); + data_fut.await.unwrap(); assert_eq!( String::from_utf8_lossy(&contents), "\0\0\0\0\0Hello, World!" @@ -45,7 +45,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { // Test that file read streams behave like other read streams. let (data_rx, data_fut) = file.read_via_stream(5); let contents = data_rx.collect().await; - data_fut.await.unwrap().unwrap(); + data_fut.await.unwrap(); assert_eq!(String::from_utf8_lossy(&contents), "Hello, World!"); dir.unlink_file_at(filename.to_string()).await.unwrap(); diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs b/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs index 6c2234aa7f..a8f8178eee 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_content_length.rs @@ -17,7 +17,7 @@ fn make_request() -> ( FutureReader>, ) { let (contents_tx, contents_rx) = wit_stream::new(); - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| todo!()); let (request, transmit) = Request::new( Headers::from_list(&[("Content-Length".to_string(), b"11".to_vec())]).unwrap(), Some(contents_rx), @@ -67,9 +67,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { }); let res = handle.unwrap(); drop(res); - transmit - .expect("transmit sender dropped") - .expect("failed to transmit request"); + transmit.expect("failed to transmit request"); } { @@ -94,9 +92,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { }); let res = handle.unwrap(); drop(res); - let err = transmit - .expect("transmit sender dropped") - .expect_err("request transmission should have failed"); + let err = transmit.expect_err("request transmission should have failed"); assert!( matches!(err, ErrorCode::HttpRequestBodySize(Some(3))), "unexpected error: {err:#?}" @@ -122,9 +118,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { }); let res = handle.unwrap(); drop(res); - let err = transmit - .expect("transmit sender dropped") - .expect_err("request transmission should have failed"); + let err = transmit.expect_err("request transmission should have failed"); assert!( matches!(err, ErrorCode::HttpRequestBodySize(Some(18))), "unexpected error: {err:#?}" diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs b/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs index 10447d2fc8..2004d2edfe 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_invalid_header.rs @@ -63,7 +63,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { Err(HeaderError::InvalidSyntax) )); - let (_, rx) = wit_future::new(); + let (_, rx) = wit_future::new(|| Ok(None)); let (req, _) = Request::new(hdrs, None, rx, None); let hdrs = req.headers(); diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs b/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs index 4d00350cd8..d56e3e393d 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_missing_path_and_query.rs @@ -9,7 +9,7 @@ test_programs::p3::export!(Component); impl test_programs::p3::exports::wasi::cli::run::Guest for Component { async fn run() -> Result<(), ()> { let fields = Fields::new(); - let (_, rx) = wit_future::new(); + let (_, rx) = wit_future::new(|| Ok(None)); let (req, _) = Request::new(fields, None, rx, None); req.set_method(&Method::Get).unwrap(); req.set_scheme(Some(&Scheme::Https)).unwrap(); diff --git a/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs b/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs index 71598e7a09..1667a8f9ba 100644 --- a/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs +++ b/crates/test-programs/src/bin/p3_http_outbound_request_response_build.rs @@ -15,7 +15,7 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { )]) .unwrap(); let (mut contents_tx, contents_rx) = wit_stream::new(); - let (_, trailers_rx) = wit_future::new(); + let (_, trailers_rx) = wit_future::new(|| Ok(None)); let (request, _) = Request::new(headers, Some(contents_rx), trailers_rx, None); request.set_method(&Method::Get).expect("setting method"); @@ -35,14 +35,14 @@ impl test_programs::p3::exports::wasi::cli::run::Guest for Component { )]) .unwrap(); let (mut contents_tx, contents_rx) = wit_stream::new(); - let (_, trailers_rx) = wit_future::new(); + let (_, trailers_rx) = wit_future::new(|| Ok(None)); let _ = Response::new(headers, Some(contents_rx), trailers_rx); let remaining = contents_tx.write_all(b"response-body".to_vec()).await; assert!(remaining.is_empty()); } { - let (_, trailers_rx) = wit_future::new(); + let (_, trailers_rx) = wit_future::new(|| Ok(None)); let (req, _) = Request::new(Fields::new(), None, trailers_rx, None); assert!( diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs b/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs index b09eeb77b5..bc2d717d3d 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs @@ -47,7 +47,7 @@ async fn test_tcp_sample_application(family: IpAddressFamily, bind_address: IpSo // Check that we sent and received our message! assert_eq!(data, first_message); // Not guaranteed to work but should work in practice. - fut.await.unwrap().unwrap() + fut.await.unwrap() }, ); @@ -76,7 +76,7 @@ async fn test_tcp_sample_application(family: IpAddressFamily, bind_address: IpSo // Check that we sent and received our message! assert_eq!(data, second_message); // Not guaranteed to work but should work in practice. - fut.await.unwrap().unwrap() + fut.await.unwrap() } ); } diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs index b2e100e575..6541f4a91d 100644 --- a/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs +++ b/crates/test-programs/src/bin/p3_sockets_tcp_streams.rs @@ -23,7 +23,7 @@ async fn test_tcp_input_stream_should_be_closed_by_remote_shutdown(family: IpAdd // Wait for the shutdown signal to reach the client: assert!(client_rx.next().await.is_none()); - assert_eq!(client_fut.await, Some(Ok(()))); + assert_eq!(client_fut.await, Ok(())); }) .await; } @@ -51,7 +51,7 @@ async fn test_tcp_input_stream_should_be_closed_by_local_shutdown(family: IpAddr // Shut down socket locally: drop(client_rx); // Wait for the shutdown signal to reach the client: - assert_eq!(client_fut.await, Some(Ok(()))); + assert_eq!(client_fut.await, Ok(())); }).await; } @@ -104,7 +104,7 @@ async fn test_tcp_shutdown_should_not_lose_data(family: IpAddressFamily) { outgoing_data, incoming_data, "Received data should match the sent data" ); - server_fut.await.unwrap().unwrap() + server_fut.await.unwrap() }, ); }) diff --git a/crates/test-programs/src/p3/http.rs b/crates/test-programs/src/p3/http.rs index fbbb707406..026889417a 100644 --- a/crates/test-programs/src/p3/http.rs +++ b/crates/test-programs/src/p3/http.rs @@ -71,7 +71,7 @@ pub async fn request( .map_err(|_err| anyhow!("failed to set between_bytes_timeout"))?; let (mut contents_tx, contents_rx) = wit_stream::new(); - let (trailers_tx, trailers_rx) = wit_future::new(); + let (trailers_tx, trailers_rx) = wit_future::new(|| Ok(None)); let (request, transmit) = types::Request::new(headers, Some(contents_rx), trailers_rx, Some(options)); @@ -89,12 +89,7 @@ pub async fn request( .map_err(|()| anyhow!("failed to set path_with_query"))?; let (transmit, handle) = join!( - async { - transmit - .await - .context("transmit sender dropped")? - .context("failed to transmit request") - }, + async { transmit.await.context("failed to transmit request") }, async { let response = handler::handle(request).await?; let status = response.status_code(); @@ -112,10 +107,7 @@ pub async fn request( }, async { let body = body_rx.collect().await; - let trailers = trailers_rx - .await - .context("trailers sender dropped")? - .context("failed to read body")?; + let trailers = trailers_rx.await.context("failed to read body")?; let trailers = trailers.map(|trailers| trailers.entries()); anyhow::Ok(Response { status, diff --git a/crates/wasi-http/Cargo.toml b/crates/wasi-http/Cargo.toml index 3721b887a3..53cb3ab167 100644 --- a/crates/wasi-http/Cargo.toml +++ b/crates/wasi-http/Cargo.toml @@ -37,7 +37,7 @@ webpki-roots = { workspace = true } test-programs-artifacts = { workspace = true } test-log = { workspace = true } tracing-subscriber = { workspace = true } -wasmtime = { workspace = true, features = ['cranelift'] } +wasmtime = { workspace = true, features = ['cranelift', 'default'] } tokio = { workspace = true, features = ['macros'] } futures = { workspace = true, default-features = false, features = ['alloc'] } sha2 = "0.10.2" diff --git a/crates/wasi-http/src/p3/host/types.rs b/crates/wasi-http/src/p3/host/types.rs index cc06580883..c644d993ae 100644 --- a/crates/wasi-http/src/p3/host/types.rs +++ b/crates/wasi-http/src/p3/host/types.rs @@ -749,7 +749,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| Ok(()), &mut view) .context("failed to create future")?; let contents = contents.map(|contents| { contents @@ -793,7 +793,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(&mut view) + .future(|| Ok(None), &mut view) .context("failed to create future")?; let mut binding = view.get(); let Request { body, .. } = get_request_mut(binding.table(), &req)?; @@ -1075,7 +1075,7 @@ where store.with(|mut view| { let instance = view.instance(); let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| Ok(()), &mut view) .context("failed to create future")?; let contents = contents.map(|contents| { contents @@ -1110,7 +1110,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (trailers_tx, trailers_rx) = instance - .future(&mut view) + .future(|| Ok(None), &mut view) .context("failed to create future")?; let mut binding = view.get(); let Response { body, .. } = get_response_mut(binding.table(), &res)?; diff --git a/crates/wasi/src/p3/filesystem/host.rs b/crates/wasi/src/p3/filesystem/host.rs index 71538864c7..5798ce012a 100644 --- a/crates/wasi/src/p3/filesystem/host.rs +++ b/crates/wasi/src/p3/filesystem/host.rs @@ -64,7 +64,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let fd = get_descriptor(binding.table(), &fd)?; @@ -321,7 +321,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let fd = get_descriptor(binding.table(), &fd)?; diff --git a/crates/wasi/src/p3/mod.rs b/crates/wasi/src/p3/mod.rs index 7ba7db3852..1bf86f20ea 100644 --- a/crates/wasi/src/p3/mod.rs +++ b/crates/wasi/src/p3/mod.rs @@ -152,7 +152,7 @@ where } } -pub struct IoTask { +pub struct IoTask { pub data: StreamWriter>, pub result: FutureWriter>, pub rx: mpsc::Receiver, E>>, diff --git a/crates/wasi/src/p3/sockets/host/types/tcp.rs b/crates/wasi/src/p3/sockets/host/types/tcp.rs index aa98a0a8ea..aa7dabfcd4 100644 --- a/crates/wasi/src/p3/sockets/host/types/tcp.rs +++ b/crates/wasi/src/p3/sockets/host/types/tcp.rs @@ -443,7 +443,7 @@ where .stream::<_, _, Vec<_>, _, _>(&mut view) .context("failed to create stream")?; let (res_tx, res_rx) = instance - .future(&mut view) + .future(|| unreachable!(), &mut view) .context("failed to create future")?; let mut binding = view.get(); let sock = get_socket(binding.table(), &socket)?; diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index ebbf395eb0..16ffb0777a 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -96,7 +96,8 @@ use { wasmtime_environ::{ PrimaryMap, component::{ - MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, RuntimeComponentInstanceIndex, StringEncoding, + MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT, + RuntimeComponentInstanceIndex, StringEncoding, TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex, TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex, }, @@ -700,7 +701,10 @@ enum WaitableState { /// guest->guest call orchestrated by a fused adapter. enum CallerInfo { /// Metadata for a call to an async-lowered import - Async { params: u32, results: u32 }, + Async { + params: Vec, + has_result: bool, + }, /// Metadata for a call to an sync-lowered import Sync { params: Vec, @@ -1543,7 +1547,15 @@ impl ComponentInstance { } let result_info = match &caller_info { - CallerInfo::Async { results, .. } => ResultInfo::Heap { results: *results }, + CallerInfo::Async { + has_result: true, + params, + } => ResultInfo::Heap { + results: params.last().unwrap().get_u32(), + }, + CallerInfo::Async { + has_result: false, .. + } => ResultInfo::Stack { result_count: 0 }, CallerInfo::Sync { result_count, params, @@ -1571,16 +1583,22 @@ impl ComponentInstance { assert!(dst.len() <= MAX_FLAT_PARAMS); let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS]; let count = match caller_info { - CallerInfo::Async { params, .. } => { - src[0] = MaybeUninit::new(ValRaw::u32(params)); - 1 + // Async callers, if they have a result, use the last + // parameter as a return pointer so chop that off if + // relevant here. + CallerInfo::Async { params, has_result } => { + let params = ¶ms[..params.len() - usize::from(has_result)]; + for (param, src) in params.iter().zip(&mut src) { + src.write(*param); + } + params.len() } + + // Sync callers forward everything directly. CallerInfo::Sync { params, .. } => { - // SAFETY: Transmuting from `&[T]` to - // `&[MaybeUninit]` should be sound for any `T`. - src[..params.len()].copy_from_slice(unsafe { - mem::transmute::<&[ValRaw], &[MaybeUninit]>(¶ms) - }); + for (param, src) in params.iter().zip(&mut src) { + src.write(*param); + } params.len() } }; @@ -3438,8 +3456,11 @@ impl Instance { /// pointers used for async calls). 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_prepare( + /// one of the caller or callee is async. + /// + /// This helper is not used when the caller and callee both use the sync + /// ABI, only when at least one is async is this used. + unsafe fn prepare_call( &mut self, instance: &mut ComponentInstance, memory: *mut VMMemoryDefinition, @@ -3466,22 +3487,6 @@ pub trait VMComponentAsyncStore { storage_len: usize, ) -> Result<()>; - /// A helper function for fused adapter modules involving calls where the - /// caller is async-lowered. - unsafe fn async_prepare( - &mut self, - instance: &mut ComponentInstance, - memory: *mut VMMemoryDefinition, - start: *mut VMFuncRef, - return_: *mut VMFuncRef, - caller_instance: RuntimeComponentInstanceIndex, - callee_instance: RuntimeComponentInstanceIndex, - task_return_type: TypeTupleIndex, - string_encoding: u8, - params: u32, - results: u32, - ) -> Result<()>; - /// A helper function for fused adapter modules involving calls where the /// caller is async-lowered. unsafe fn async_start( @@ -3596,7 +3601,7 @@ pub trait VMComponentAsyncStore { /// SAFETY: See trait docs. impl VMComponentAsyncStore for StoreInner { - unsafe fn sync_prepare( + unsafe fn prepare_call( &mut self, instance: &mut ComponentInstance, memory: *mut VMMemoryDefinition, @@ -3606,10 +3611,15 @@ impl VMComponentAsyncStore for StoreInner { callee_instance: RuntimeComponentInstanceIndex, task_return_type: TypeTupleIndex, string_encoding: u8, - result_count: u32, + result_count_or_max_if_async: u32, storage: *mut ValRaw, storage_len: usize, ) -> Result<()> { + // SAFETY: The `wasmtime_cranelift`-generated code that calls + // this method will have ensured that `storage` is a valid + // pointer containing at least `storage_len` items. + let params = unsafe { std::slice::from_raw_parts(storage, storage_len) }.to_vec(); + instance.prepare_call( StoreContextMut(self), start, @@ -3619,12 +3629,19 @@ impl VMComponentAsyncStore for StoreInner { task_return_type, memory, string_encoding, - CallerInfo::Sync { - // SAFETY: The `wasmtime_cranelift`-generated code that calls - // this method will have ensured that `storage` is a valid - // pointer containing at least `storage_len` items. - params: unsafe { std::slice::from_raw_parts(storage, storage_len) }.to_vec(), - result_count, + match result_count_or_max_if_async { + PREPARE_ASYNC_NO_RESULT => CallerInfo::Async { + params, + has_result: false, + }, + PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async { + params, + has_result: true, + }, + result_count => CallerInfo::Sync { + params, + result_count, + }, }, ) } @@ -3655,32 +3672,6 @@ impl VMComponentAsyncStore for StoreInner { .map(drop) } - unsafe fn async_prepare( - &mut self, - instance: &mut ComponentInstance, - memory: *mut VMMemoryDefinition, - start: *mut VMFuncRef, - return_: *mut VMFuncRef, - caller_instance: RuntimeComponentInstanceIndex, - callee_instance: RuntimeComponentInstanceIndex, - task_return_type: TypeTupleIndex, - string_encoding: u8, - params: u32, - results: u32, - ) -> Result<()> { - instance.prepare_call( - StoreContextMut(self), - start, - return_, - caller_instance, - callee_instance, - task_return_type, - memory, - string_encoding, - CallerInfo::Async { params, results }, - ) - } - unsafe fn async_start( &mut self, instance: &mut ComponentInstance, diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index dab1e0c6a4..78f3deb553 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -381,7 +381,7 @@ enum WriteEvent { tx: oneshot::Sender>, }, /// Close the write end of the stream or future. - Close, + Close(Option B + Send + Sync>>), /// Watch the read (i.e. opposite) end of this stream or future, dropping /// the specified sender when it is closed. Watch { tx: oneshot::Sender<()> }, @@ -488,17 +488,20 @@ fn watch( } /// Represents the writable end of a Component Model `future`. -pub struct FutureWriter { +pub struct FutureWriter { + default: Option T>, instance: SendSyncPtr, tx: Option>>>, } impl FutureWriter { fn new( + default: fn() -> T, tx: Option>>>, instance: &mut ComponentInstance, ) -> Self { Self { + default: Some(default), instance: SendSyncPtr::new(instance.into()), tx, } @@ -525,6 +528,7 @@ impl FutureWriter { tx, }, ); + self.default = None; let instance = self.instance; super::checked( instance, @@ -563,7 +567,13 @@ impl FutureWriter { impl Drop for FutureWriter { fn drop(&mut self) { if let Some(mut tx) = self.tx.take() { - send(&mut tx, WriteEvent::Close); + send( + &mut tx, + WriteEvent::Close(self.default.take().map(|v| { + Box::new(move || Some(v())) + as Box Option + Send + Sync + 'static> + })), + ); } } } @@ -960,7 +970,7 @@ impl StreamWriter { impl Drop for StreamWriter { fn drop(&mut self) { if let Some(mut tx) = self.tx.take() { - send(&mut tx, WriteEvent::Close); + send(&mut tx, WriteEvent::Close(None)); } } } @@ -1517,12 +1527,24 @@ enum Reader<'a> { impl Instance { /// Create a new Component Model `future` as pair of writable and readable ends, /// the latter of which may be passed to guest code. + /// + /// The `default` parameter will be used if the returned `FutureWriter` is + /// dropped before `FutureWriter::write` is called. Since the write end of + /// a Component Model `future` must be written to before it is closed, and + /// since Rust does not currently provide a way to statically enforce that + /// (e.g. linear typing), we use this mechanism to ensure a value is always + /// written prior to closing. + /// + /// If there's no plausible default value, and you're sure + /// `FutureWriter::write` will be called, you can consider passing `|| + /// unreachable!()` as the `default` parameter. pub fn future< T: func::Lower + func::Lift + Send + Sync + 'static, U: 'static, S: AsContextMut, >( &self, + default: fn() -> T, mut store: S, ) -> Result<(FutureWriter, FutureReader)> { store @@ -1532,6 +1554,7 @@ impl Instance { Ok(( FutureWriter::new( + default, Some(instance.start_write_event_loop::<_, _, U>( store.as_context_mut(), write.rep(), @@ -1674,9 +1697,21 @@ impl ComponentInstance { ) })? } - WriteEvent::Close => super::with_local_instance(|_, instance| { - instance.host_close_writer(rep, kind) - })?, + WriteEvent::Close(default) => { + super::with_local_instance(|store, instance| { + if let Some(default) = default { + instance.host_write::<_, _, U>( + token.as_context_mut(store), + rep, + default(), + PostWrite::Continue, + oneshot::channel().0, + kind, + )?; + } + instance.host_close_writer(rep, kind) + })? + } WriteEvent::Watch { tx } => super::with_local_instance(|_, instance| { let state = instance.get_mut(TableId::::new(rep))?; if !matches!(&state.read, ReadState::Closed) { @@ -1740,7 +1775,18 @@ impl ComponentInstance { })?, ReadEvent::Watch { tx } => super::with_local_instance(|_, instance| { let state = instance.get_mut(TableId::::new(rep))?; - if !matches!(&state.write, WriteState::Closed) { + if !matches!( + &state.write, + WriteState::Closed + | WriteState::GuestReady { + post_write: PostWrite::Close, + .. + } + | WriteState::HostReady { + post_write: PostWrite::Close, + .. + } + ) { state.writer_watcher = Some(tx); } Ok::<_, anyhow::Error>(()) diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index e6a2aaaf55..7340adef77 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -20,7 +20,7 @@ use core::pin::Pin; use core::ptr::NonNull; use wasmtime_environ::component::{ CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, - RuntimeComponentInstanceIndex, StringEncoding, TypeFuncIndex, + RuntimeComponentInstanceIndex, StringEncoding, TypeFuncIndex, TypeTuple, }; pub struct HostFunc { @@ -269,24 +269,6 @@ where Params: Lift + Send + Sync + 'static, Return: Lower + Send + Sync + 'static, { - /// Representation of arguments to this function when a return pointer is in - /// use, namely the argument list is followed by a single value which is the - /// return pointer. - #[repr(C)] - struct ReturnPointer { - args: T, - retptr: ValRaw, - } - - /// Representation of arguments to this function when the return value is - /// returned directly, namely the arguments and return value all start from - /// the beginning (aka this is a `union`, not a `struct`). - #[repr(C)] - union ReturnStack { - args: T, - ret: U, - } - let options = Options::new( store.0.store_opaque().id(), NonNull::new(memory), @@ -310,21 +292,37 @@ where if async_ { #[cfg(feature = "component-model-async")] { - let paramptr = storage[0].assume_init(); - let retptr = storage[1].assume_init(); + let mut storage = Storage::<'_, Params, u32>::new_async::(storage); + // Lift the parameters, either from flat storage or from linear + // memory. let params = { let lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, types, instance); lift.enter_call(); - let ptr = validate_inbounds::(lift.memory(), ¶mptr)?; - Params::load(lift, param_tys, &lift.memory()[ptr..][..Params::SIZE32])? + storage.lift_params(lift, param_tys)? }; - let future = closure(store.as_context_mut(), instance, params); + // Load the return pointer, if present. + let retptr = match storage.async_retptr() { + Some(ptr) => { + let mut lower = + LowerContext::new(store.as_context_mut(), &options, &types, instance); + validate_inbounds::(lower.as_slice_mut(), ptr)? + } + // If there's no return pointer then `Return` should have an + // empty flat representation. In this situation pretend the + // return pointer was 0 so we have something to shepherd along + // into the closure below. + None => { + assert_eq!(Return::flatten_count(), 0); + 0 + } + }; + let future = closure(store.as_context_mut(), instance, params); let instance_ptr = SendSyncPtr::new(NonNull::new(instance).unwrap()); - let task = instance.first_poll(store, future, caller_instance, { + let task = instance.first_poll(store.as_context_mut(), future, caller_instance, { let types = types.clone(); move |mut store: StoreContextMut, instance: &mut ComponentInstance, @@ -333,8 +331,7 @@ where flags.set_may_leave(false); let mut lower = LowerContext::new(store, &options, &types, instance_ptr.as_ptr()); - let ptr = validate_inbounds::(lower.as_slice_mut(), &retptr)?; - ret.store(&mut lower, result_tys, ptr)?; + ret.store(&mut lower, result_tys, retptr)?; flags.set_may_leave(true); lower.exit_call()?; Ok(()) @@ -348,7 +345,8 @@ where Status::Returned.pack(None) }; - storage[0] = MaybeUninit::new(ValRaw::i32(status as i32)); + let mut lower = LowerContext::new(store, &options, &types, instance_ptr.as_ptr()); + storage.lower_results(&mut lower, InterfaceType::U32, status)?; } #[cfg(not(feature = "component-model-async"))] { @@ -358,29 +356,7 @@ where ); } } else { - // There's a 2x2 matrix of whether parameters and results are stored on the - // stack or on the heap. Each of the 4 branches here have a different - // representation of the storage of arguments/returns. - // - // Also note that while four branches are listed here only one is taken for - // any particular `Params` and `Return` combination. This should be - // trivially DCE'd by LLVM. Perhaps one day with enough const programming in - // Rust we can make monomorphizations of this function codegen only one - // branch, but today is not that day. - let mut storage: Storage<'_, Params, Return> = if Params::flatten_count() <= MAX_FLAT_PARAMS - { - if Return::flatten_count() <= MAX_FLAT_RESULTS { - Storage::Direct(slice_to_storage_mut(storage)) - } else { - Storage::ResultsIndirect(slice_to_storage_mut(storage).assume_init_ref()) - } - } else { - if Return::flatten_count() <= MAX_FLAT_RESULTS { - Storage::ParamsIndirect(slice_to_storage_mut(storage)) - } else { - Storage::Indirect(slice_to_storage_mut(storage).assume_init_ref()) - } - }; + let mut storage = Storage::<'_, Params, Return>::new_sync(storage); let mut lift = LiftContext::new(store.0.store_opaque_mut(), &options, types, instance); lift.enter_call(); let params = storage.lift_params(&mut lift, param_tys)?; @@ -401,11 +377,138 @@ where return Ok(()); + /// Type-level representation of the matrix of possibilities of how + /// WebAssembly parameters and results are handled in the canonical ABI. + /// + /// Wasmtime's ABI here always works with `&mut [MaybeUninit]` as the + /// base representation of params/results. Parameters are passed + /// sequentially and results are returned by overwriting the parameters. + /// That means both params/results start from index 0. + /// + /// The type-level representation here involves working with the typed + /// `P::Lower` and `R::Lower` values which is a type-level representation of + /// a lowered value. All lowered values are in essence a sequence of + /// `ValRaw` values one after the other to fit within this original array + /// that is the basis of Wasmtime's ABI. + /// + /// The various combinations here are cryptic, but only used in this file. + /// This in theory cuts down on the verbosity below, but an explanation of + /// the various acronyms here are: + /// + /// * Pd - params direct - means that parameters are passed directly in + /// their flat representation via `P::Lower`. + /// + /// * Pi - params indirect - means that parameters are passed indirectly in + /// linear memory and the argument here is `ValRaw` to store the pointer. + /// + /// * Rd - results direct - means that results are returned directly in + /// their flat representation via `R::Lower`. Note that this is always + /// represented as `MaybeUninit` as well because the return + /// values may point to uninitialized memory if there were no parameters + /// for example. + /// + /// * Ri - results indirect - means that results are returned indirectly in + /// linear memory through the pointer specified. Note that this is + /// specified as a `ValRaw` to represent the argument that's being given + /// to the host from WebAssembly. + /// + /// * Ar - async results - means that the parameters to this call + /// additionally include an async result pointer. Async results are always + /// transmitted via a pointer so this is always a `ValRaw`. + /// + /// Internally this type makes liberal use of `Union` and `Pair` helpers + /// below which are simple `#[repr(C)]` wrappers around a pair of types that + /// are a union or a pair. + /// + /// Note that for any combination of `P` and `R` this `enum` is actually + /// pointless as a single variant will be used. In theory we should be able + /// to monomorphize based on `P` and `R` to a specific type. This + /// monomorphization depends on conditionals like `flatten_count() <= N`, + /// however, and I don't know how to encode that in Rust easily. In lieu of + /// that we assume LLVM will figure things out and boil away the actual enum + /// and runtime dispatch. enum Storage<'a, P: ComponentType, R: ComponentType> { - Direct(&'a mut MaybeUninit>), - ParamsIndirect(&'a mut MaybeUninit>), - ResultsIndirect(&'a ReturnPointer), - Indirect(&'a ReturnPointer), + /// Params: direct, Results: direct + /// + /// The lowered representation of params/results are overlaid on top of + /// each other. + PdRd(&'a mut Union>), + + /// Params: direct, Results: indirect + /// + /// The return pointer comes after the params so this is sequentially + /// laid out with one after the other. + PdRi(&'a Pair), + + /// Params: indirect, Results: direct + /// + /// Here the return values are overlaid on top of the pointer parameter. + PiRd(&'a mut Union>), + + /// Params: indirect, Results: indirect + /// + /// Here the two parameters are laid out sequentially one after the + /// other. + PiRi(&'a Pair), + + /// Params: direct + async result, Results: direct + /// + /// This is like `PdRd` except that the parameters additionally include + /// a pointer for where to store the result. + #[cfg(feature = "component-model-async")] + PdArRd(&'a mut Union, MaybeUninit>), + + /// Params: indirect + async result, Results: direct + /// + /// This is like `PiRd` except that the parameters additionally include + /// a pointer for where to store the result. + #[cfg(feature = "component-model-async")] + PiArRd(&'a mut Union, MaybeUninit>), + } + + // Helper structure used above in `Storage` to represent two consecutive + // values. + #[repr(C)] + #[derive(Copy, Clone)] + struct Pair { + a: T, + b: U, + } + + // Helper structure used above in `Storage` to represent two values overlaid + // on each other. + #[repr(C)] + union Union { + a: T, + b: U, + } + + /// Representation of where parameters are lifted from. + enum Src<'a, T> { + /// Parameters are directly lifted from `T`, which is under the hood a + /// sequence of `ValRaw`. This is `P::Lower` for example. + Direct(&'a T), + + /// Parameters are loaded from linear memory, and this is the wasm + /// parameter representing the pointer into linear memory to load from. + Indirect(&'a ValRaw), + } + + /// Dual of [`Src`], where to store results. + enum Dst<'a, T> { + /// Results are stored directly in this pointer. + /// + /// Note that this is a mutable pointer but it's specifically + /// `MaybeUninit` as trampolines do not initialize it. The `T` here will + /// be `R::Lower` for example. + Direct(&'a mut MaybeUninit), + + /// Results are stored in linear memory, and this value is the wasm + /// parameter given which represents the pointer into linear memory. + /// + /// Note that this is not mutable as the parameter is not mutated, but + /// memory will be mutated. + Indirect(&'a ValRaw), } impl Storage<'_, P, R> @@ -413,39 +516,158 @@ where P: ComponentType + Lift, R: ComponentType + Lower, { - unsafe fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result

{ - match self { - Storage::Direct(storage) => P::lift(cx, ty, &storage.assume_init_ref().args), - Storage::ResultsIndirect(storage) => P::lift(cx, ty, &storage.args), - Storage::ParamsIndirect(storage) => { - let ptr = validate_inbounds::

(cx.memory(), &storage.assume_init_ref().args)?; - P::load(cx, ty, &cx.memory()[ptr..][..P::SIZE32]) + /// Classifies a new `Storage` suitable for use with sync functions. + /// + /// There's a 2x2 matrix of whether parameters and results are stored on the + /// stack or on the heap. Each of the 4 branches here have a different + /// representation of the storage of arguments/returns. + /// + /// Also note that while four branches are listed here only one is taken for + /// any particular `Params` and `Return` combination. This should be + /// trivially DCE'd by LLVM. Perhaps one day with enough const programming in + /// Rust we can make monomorphizations of this function codegen only one + /// branch, but today is not that day. + /// + /// # Safety + /// + /// Requires that the `storage` provided does indeed match an wasm + /// function with the signature of `P` and `R` as params/results. + unsafe fn new_sync(storage: &mut [MaybeUninit]) -> Storage<'_, P, R> { + // SAFETY: this `unsafe` is due to the `slice_to_storage_*` helpers + // used which view the slice provided as a different type. This + // safety should be upheld by the contract of the `ComponentType` + // trait and its `Lower` type parameter meaning they're valid to + // view as a sequence of `ValRaw` types. Additionally the + // `ComponentType` trait ensures that the matching of the runtime + // length of `storage` should match the actual size of `P::Lower` + // and `R::Lower` or such as needed. + unsafe { + if P::flatten_count() <= MAX_FLAT_PARAMS { + if R::flatten_count() <= MAX_FLAT_RESULTS { + Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref()) + } + } else { + if R::flatten_count() <= MAX_FLAT_RESULTS { + Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref()) + } } - Storage::Indirect(storage) => { - let ptr = validate_inbounds::

(cx.memory(), &storage.args)?; + } + } + + fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result

{ + match self.lift_src() { + Src::Direct(storage) => P::lift(cx, ty, storage), + Src::Indirect(ptr) => { + let ptr = validate_inbounds::

(cx.memory(), ptr)?; P::load(cx, ty, &cx.memory()[ptr..][..P::SIZE32]) } } } - unsafe fn lower_results( + fn lift_src(&self) -> Src<'_, P::Lower> { + match self { + // SAFETY: these `unsafe` blocks are due to accessing union + // fields. The safety here relies on the contract of the + // `ComponentType` trait which should ensure that the types + // projected onto a list of wasm parameters are indeed correct. + // That means that the projections here, if the types are + // correct, all line up to initialized memory that's well-typed + // to access. + Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) }, + Storage::PdRi(storage) => Src::Direct(&storage.a), + #[cfg(feature = "component-model-async")] + Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) }, + Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) }, + Storage::PiRi(storage) => Src::Indirect(&storage.a), + #[cfg(feature = "component-model-async")] + Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) }, + } + } + + fn lower_results( &mut self, cx: &mut LowerContext<'_, T>, ty: InterfaceType, ret: R, ) -> Result<()> { - match self { - Storage::Direct(storage) => ret.lower(cx, ty, map_maybe_uninit!(storage.ret)), - Storage::ParamsIndirect(storage) => { - ret.lower(cx, ty, map_maybe_uninit!(storage.ret)) - } - Storage::ResultsIndirect(storage) => { - let ptr = validate_inbounds::(cx.as_slice_mut(), &storage.retptr)?; + match self.lower_dst() { + Dst::Direct(storage) => ret.lower(cx, ty, storage), + Dst::Indirect(ptr) => { + let ptr = validate_inbounds::(cx.as_slice_mut(), ptr)?; ret.store(cx, ty, ptr) } - Storage::Indirect(storage) => { - let ptr = validate_inbounds::(cx.as_slice_mut(), &storage.retptr)?; - ret.store(cx, ty, ptr) + } + } + + fn lower_dst(&mut self) -> Dst<'_, R::Lower> { + match self { + // SAFETY: these unsafe blocks are due to accessing fields of a + // `union` which is not safe in Rust. The returned value is + // `MaybeUninit` in all cases, however, which should + // safely model how `union` memory is possibly uninitialized. + // Additionally `R::Lower` has the `unsafe` contract that all + // its bit patterns must be sound, which additionally should + // help make this safe. + Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + #[cfg(feature = "component-model-async")] + Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + #[cfg(feature = "component-model-async")] + Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) }, + Storage::PdRi(storage) => Dst::Indirect(&storage.b), + Storage::PiRi(storage) => Dst::Indirect(&storage.b), + } + } + + #[cfg(feature = "component-model-async")] + fn async_retptr(&self) -> Option<&ValRaw> { + match self { + // SAFETY: like above these are `unsafe` due to accessing a + // `union` field. This should be safe via the construction of + // `Storage` which should correctly determine whether or not an + // async return pointer is provided and classify the args/rets + // appropriately. + Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) }, + Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) }, + Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None, + } + } + } + + #[cfg(feature = "component-model-async")] + impl

Storage<'_, P, u32> + where + P: ComponentType + Lift, + { + /// Classifies a new `Storage` suitable for use with async functions. + /// + /// # Safety + /// + /// Requires that the `storage` provided does indeed match an `async` + /// wasm function with the signature of `P` and `R` as params/results. + unsafe fn new_async(storage: &mut [MaybeUninit]) -> Storage<'_, P, u32> + where + R: ComponentType + Lower, + { + // SAFETY: see `Storage::new` for discussion on why this should be + // safe given the unsafe contract of the `ComponentType` trait. + unsafe { + if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS { + if R::flatten_count() == 0 { + Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut()) + } + } else { + if R::flatten_count() == 0 { + Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut()) + } else { + Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut()) + } } } } @@ -529,9 +751,6 @@ where bail!("cannot leave component instance"); } - let args; - let ret_index; - let func_ty = &types[ty]; let param_tys = &types[func_ty.params]; let result_tys = &types[func_ty.results]; @@ -539,25 +758,28 @@ where if async_ { #[cfg(feature = "component-model-async")] { - let paramptr = storage[0].assume_init(); - let retptr = storage[1].assume_init(); - - let params = { + let mut params = Vec::new(); + let ret_index = { let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, types, instance); lift.enter_call(); - let mut offset = - validate_inbounds_dynamic(¶m_tys.abi, lift.memory(), ¶mptr)?; - param_tys - .types - .iter() - .map(|ty| { - let abi = types.canonical_abi(ty); - let size = usize::try_from(abi.size32).unwrap(); - let memory = &lift.memory()[abi.next_field32_size(&mut offset)..][..size]; - Val::load(&mut lift, *ty, memory) - }) - .collect::>>()? + + dynamic_params_load( + &mut lift, + types, + storage, + param_tys, + &mut params, + wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS, + )? + }; + let retptr = if result_tys.types.len() == 0 { + 0 + } else { + let retptr = storage[ret_index].assume_init(); + let mut lower = + LowerContext::new(store.as_context_mut(), &options, &types, instance); + validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)? }; let future = closure( @@ -584,11 +806,7 @@ where let mut lower = LowerContext::new(store, &options, &types, instance_ptr.as_ptr()); - let mut ptr = validate_inbounds_dynamic( - &result_tys.abi, - lower.as_slice_mut(), - &retptr, - )?; + let mut ptr = retptr; for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { let offset = types.canonical_abi(ty).next_field32_size(&mut ptr); val.store(&mut lower, *ty, offset)?; @@ -621,35 +839,15 @@ where } else { let mut cx = LiftContext::new(store.0.store_opaque_mut(), &options, types, instance); cx.enter_call(); - if let Some(param_count) = param_tys.abi.flat_count(MAX_FLAT_PARAMS) { - // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable - let mut iter = - mem::transmute::<&[MaybeUninit], &[ValRaw]>(&storage[..param_count]).iter(); - args = param_tys - .types - .iter() - .map(|ty| Val::lift(&mut cx, *ty, &mut iter)) - .collect::>>()?; - ret_index = param_count; - assert!(iter.next().is_none()); - } else { - let mut offset = validate_inbounds_dynamic( - ¶m_tys.abi, - cx.memory(), - storage[0].assume_init_ref(), - )?; - args = param_tys - .types - .iter() - .map(|ty| { - let abi = types.canonical_abi(ty); - let size = usize::try_from(abi.size32).unwrap(); - let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size]; - Val::load(&mut cx, *ty, memory) - }) - .collect::>>()?; - ret_index = 1; - }; + let mut args = Vec::new(); + let ret_index = dynamic_params_load( + &mut cx, + types, + storage, + param_tys, + &mut args, + MAX_FLAT_PARAMS, + )?; let future = closure( store.as_context_mut(), @@ -690,6 +888,46 @@ where return Ok(()); } +/// Loads the parameters for a dynamic host function call into `params` +/// +/// Returns the number of flat `storage` values consumed. +/// +/// # Safety +/// +/// Requires that `param_tys` matches the type signature of the `storage` that +/// was passed in. +unsafe fn dynamic_params_load( + cx: &mut LiftContext<'_>, + types: &ComponentTypes, + storage: &[MaybeUninit], + param_tys: &TypeTuple, + params: &mut Vec, + max_flat_params: usize, +) -> Result { + if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) { + // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable + let storage = + unsafe { mem::transmute::<&[MaybeUninit], &[ValRaw]>(&storage[..param_count]) }; + let mut iter = storage.iter(); + for ty in param_tys.types.iter() { + params.push(Val::lift(cx, *ty, &mut iter)?); + } + assert!(iter.next().is_none()); + Ok(param_count) + } else { + let mut offset = validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), unsafe { + storage[0].assume_init_ref() + })?; + for ty in param_tys.types.iter() { + let abi = types.canonical_abi(ty); + let size = usize::try_from(abi.size32).unwrap(); + let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size]; + params.push(Val::load(cx, *ty, memory)?); + } + Ok(1) + } +} + pub(crate) fn validate_inbounds_dynamic( abi: &CanonicalAbiInfo, memory: &[u8], diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index 2e393f6c4d..6777d9df40 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -753,7 +753,7 @@ unsafe fn subtask_cancel( } #[cfg(feature = "component-model-async")] -unsafe fn sync_prepare( +unsafe fn prepare_call( vmctx: NonNull, memory: *mut u8, start: *mut u8, @@ -762,12 +762,12 @@ unsafe fn sync_prepare( callee_instance: u32, task_return_type: u32, string_encoding: u32, - result_count: u32, + result_count_or_max_if_async: u32, storage: *mut u8, storage_len: usize, ) -> Result<()> { ComponentInstance::from_vmctx(vmctx, |store, instance| { - store.component_async_store().sync_prepare( + store.component_async_store().prepare_call( instance, memory.cast::(), start.cast::(), @@ -776,7 +776,7 @@ unsafe fn sync_prepare( wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(callee_instance), wasmtime_environ::component::TypeTupleIndex::from_u32(task_return_type), u8::try_from(string_encoding).unwrap(), - result_count, + result_count_or_max_if_async, storage.cast::(), storage_len, ) @@ -804,35 +804,6 @@ unsafe fn sync_start( }) } -#[cfg(feature = "component-model-async")] -unsafe fn async_prepare( - vmctx: NonNull, - memory: *mut u8, - start: *mut u8, - return_: *mut u8, - caller_instance: u32, - callee_instance: u32, - task_return_type: u32, - string_encoding: u32, - params: u32, - results: u32, -) -> Result<()> { - ComponentInstance::from_vmctx(vmctx, |store, instance| { - store.component_async_store().async_prepare( - instance, - memory.cast::(), - start.cast::(), - return_.cast::(), - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(caller_instance), - wasmtime_environ::component::RuntimeComponentInstanceIndex::from_u32(callee_instance), - wasmtime_environ::component::TypeTupleIndex::from_u32(task_return_type), - u8::try_from(string_encoding).unwrap(), - params, - results, - ) - }) -} - #[cfg(feature = "component-model-async")] unsafe fn async_start( vmctx: NonNull, diff --git a/src/commands/objdump.rs b/src/commands/objdump.rs index dd5ae311bf..cd9b5aa9c4 100644 --- a/src/commands/objdump.rs +++ b/src/commands/objdump.rs @@ -152,7 +152,10 @@ impl ObjdumpCommand { Func::Builtin } else if name.contains("]::function[") { Func::Wasm - } else if name.contains("trampoline") { + } else if name.contains("trampoline") + || name.ends_with("_array_call") + || name.ends_with("_wasm_call") + { Func::Trampoline } else if name.contains("libcall") || name.starts_with("component") { Func::Libcall diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs index f7d60ebdca..ddd96932c6 100644 --- a/tests/all/component_model/bindgen.rs +++ b/tests/all/component_model/bindgen.rs @@ -249,11 +249,9 @@ mod one_import_concurrent { ) (core instance $libc-instance (instantiate $libc)) (core module $m - (import "" "foo" (func $foo (param i32 i32) (result i32))) + (import "" "foo" (func $foo (param) (result i32))) (import "" "task.return" (func $task-return)) (func (export "bar") (result i32) - i32.const 0 - i32.const 0 call $foo drop call $task-return diff --git a/tests/all/component_model/import.rs b/tests/all/component_model/import.rs index 0b57ed6947..660cdf5976 100644 --- a/tests/all/component_model/import.rs +++ b/tests/all/component_model/import.rs @@ -505,10 +505,8 @@ async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> { (local $results i32) block - (local.set $params (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) - (i32.store offset=0 (local.get $params) (i32.const 1)) (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) - (call $f1 (local.get $params) (local.get $results)) + (call $f1 (i32.const 1) (local.get $results)) drop (i32.load offset=0 (local.get $results)) i32.const 2 @@ -530,10 +528,8 @@ async fn test_stack_and_heap_args_and_rets(concurrent: bool) -> Result<()> { end block - (local.set $params (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 4))) - (i32.store offset=0 (local.get $params) (i32.const 8)) (local.set $results (call $realloc (i32.const 0) (i32.const 0) (i32.const 4) (i32.const 8))) - (call $f3 (local.get $params) (local.get $results)) + (call $f3 (i32.const 8) (local.get $results)) drop (call $validate_string_ret (local.get $results)) end diff --git a/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast b/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast index dc340c3bc4..52c8e1209d 100644 --- a/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast +++ b/tests/misc_testsuite/component-model-async/backpressure-deadlock.wast @@ -48,7 +48,7 @@ (core func $waitable-set.wait (canon waitable-set.wait (memory $libc "mem"))) (core module $m - (import "" "f" (func $f (param i32 i32) (result i32))) + (import "" "f" (func $f (result i32))) (import "" "turn-on-backpressure" (func $turn-on-backpressure)) (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable.join" (func $waitable.join (param i32 i32))) @@ -59,7 +59,7 @@ (local $set i32) call $turn-on-backpressure - (local.set $status (call $f (i32.const 0) (i32.const 0))) + (local.set $status (call $f)) ;; low 4 bits should be "STARTING == 0" (i32.ne diff --git a/tests/misc_testsuite/component-model-async/drop-subtask.wast b/tests/misc_testsuite/component-model-async/drop-subtask.wast index b8b5765847..3fb3d099f7 100644 --- a/tests/misc_testsuite/component-model-async/drop-subtask.wast +++ b/tests/misc_testsuite/component-model-async/drop-subtask.wast @@ -69,14 +69,14 @@ (import "" "waitable.join" (func $waitable.join (param i32 i32))) (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) - (import "" "loop" (func $loop (param i32 i32) (result i32))) + (import "" "loop" (func $loop (result i32))) (import "" "return" (func $return)) (func $drop-after-return (export "drop-after-return") (result i32) (local $ret i32) (local $ws i32) (local $subtask i32) ;; start 'loop' - (local.set $ret (call $loop (i32.const 0xdead) (i32.const 0xbeef))) + (local.set $ret (call $loop)) (if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf))) (then unreachable)) (local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4))) diff --git a/tests/misc_testsuite/component-model-async/drop-waitable-set.wast b/tests/misc_testsuite/component-model-async/drop-waitable-set.wast index d2da838dbe..ea78cb30f0 100644 --- a/tests/misc_testsuite/component-model-async/drop-waitable-set.wast +++ b/tests/misc_testsuite/component-model-async/drop-waitable-set.wast @@ -18,7 +18,7 @@ (global $ws (mut i32) (i32.const 0)) (func $start (global.set $ws (call $waitable-set.new))) (start $start) - + (func $wait-on-set (export "wait-on-set") (result i32) ;; wait on $ws (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4))) @@ -57,14 +57,14 @@ (core instance $memory (instantiate $Memory)) (core module $Core (import "" "mem" (memory 1)) - (import "" "wait-on-set" (func $wait-on-set (param i32 i32) (result i32))) + (import "" "wait-on-set" (func $wait-on-set (result i32))) (import "" "drop-while-waiting" (func $drop-while-waiting)) (func $run (export "run") (result i32) (local $ret i32) ;; start an async call to 'wait-on-set' which blocks, waiting on a ;; waitable-set. - (local.set $ret (call $wait-on-set (i32.const 0xdeadbeef) (i32.const 0xdeadbeef))) + (local.set $ret (call $wait-on-set)) (if (i32.ne (i32.const 0x11) (local.get $ret)) (then unreachable)) diff --git a/tests/misc_testsuite/component-model-async/empty-wait.wast b/tests/misc_testsuite/component-model-async/empty-wait.wast index 83a53f4b30..c9d5cae496 100644 --- a/tests/misc_testsuite/component-model-async/empty-wait.wast +++ b/tests/misc_testsuite/component-model-async/empty-wait.wast @@ -136,8 +136,8 @@ (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) (import "" "subtask.drop" (func $subtask.drop (param i32))) - (import "" "blocker" (func $blocker (param i32 i32) (result i32))) - (import "" "unblocker" (func $unblocker (param i32 i32) (result i32))) + (import "" "blocker" (func $blocker (param i32) (result i32))) + (import "" "unblocker" (func $unblocker (param i32) (result i32))) (global $ws (mut i32) (i32.const 0)) (func $start (global.set $ws (call $waitable-set.new))) @@ -150,7 +150,7 @@ ;; call 'blocker'; it should block (local.set $retp1 (i32.const 4)) - (local.set $ret (call $blocker (i32.const 0xdeadbeef) (local.get $retp1))) + (local.set $ret (call $blocker (local.get $retp1))) (if (i32.ne (i32.const 1 (; STARTED ;)) (i32.and (local.get $ret) (i32.const 0xf))) (then unreachable)) (local.set $subtask (i32.shr_u (local.get $ret) (i32.const 4))) @@ -159,7 +159,7 @@ ;; call 'unblocker' to unblock 'blocker'; it should complete eagerly (local.set $retp2 (i32.const 8)) - (local.set $ret (call $unblocker (i32.const 0xbeefdead) (local.get $retp2))) + (local.set $ret (call $unblocker (local.get $retp2))) (if (i32.ne (i32.const 2 (; RETURNED ;)) (local.get $ret)) (then unreachable)) (if (i32.ne (i32.const 43) (i32.load (local.get $retp2))) diff --git a/tests/misc_testsuite/component-model-async/fused.wast b/tests/misc_testsuite/component-model-async/fused.wast index 56b9b12c7d..00e8a8d9d3 100644 --- a/tests/misc_testsuite/component-model-async/fused.wast +++ b/tests/misc_testsuite/component-model-async/fused.wast @@ -31,8 +31,7 @@ (import "" "foo" (func $foo (param i32 i32) (result i32))) (func (export "run") block - (i32.store offset=0 (i32.const 1200) (i32.const 42)) - (call $foo (i32.const 1200) (i32.const 1204)) + (call $foo (i32.const 42) (i32.const 1204)) (i32.eq (i32.load offset=0 (i32.const 1204)) (i32.const 42)) br_if 0 unreachable @@ -84,8 +83,7 @@ (import "" "foo" (func $foo (param i32 i32) (result i32))) (func (export "run") block - (i32.store offset=0 (i32.const 1200) (i32.const 42)) - (call $foo (i32.const 1200) (i32.const 1204)) + (call $foo (i32.const 42) (i32.const 1204)) (i32.eq (i32.load offset=0 (i32.const 1204)) (i32.const 42)) br_if 0 unreachable @@ -130,8 +128,7 @@ (import "" "foo" (func $foo (param i32 i32) (result i32))) (func (export "run") block - (i32.store offset=0 (i32.const 1200) (i32.const 42)) - (call $foo (i32.const 1200) (i32.const 1204)) + (call $foo (i32.const 42) (i32.const 1204)) (i32.eq (i32.load offset=0 (i32.const 1204)) (i32.const 42)) br_if 0 unreachable diff --git a/tests/misc_testsuite/component-model-async/partial-stream-copies.wast b/tests/misc_testsuite/component-model-async/partial-stream-copies.wast index a847c96241..c63d7921ff 100644 --- a/tests/misc_testsuite/component-model-async/partial-stream-copies.wast +++ b/tests/misc_testsuite/component-model-async/partial-stream-copies.wast @@ -153,7 +153,7 @@ (import "" "transform" (func $transform (param i32 i32) (result i32))) (func $run (export "run") (result i32) - (local $ret i32) (local $ret64 i64) (local $paramp i32) (local $retp i32) + (local $ret i32) (local $ret64 i64) (local $retp i32) (local $insr i32) (local $insw i32) (local $outsr i32) (local $subtask i32) (local $ws i32) @@ -168,10 +168,8 @@ (then unreachable)) ;; call 'transform' which will return a readable stream $outsr eagerly - (local.set $paramp (i32.const 4)) (local.set $retp (i32.const 8)) - (i32.store (local.get $paramp) (local.get $insr)) - (local.set $ret (call $transform (local.get $paramp) (local.get $retp))) + (local.set $ret (call $transform (local.get $insr) (local.get $retp))) (if (i32.ne (i32.const 2 (; RETURNED=2 | (0<<4) ;)) (local.get $ret)) (then unreachable)) (local.set $outsr (i32.load (local.get $retp))) @@ -193,7 +191,7 @@ (local.set $ret (call $stream.write (local.get $insw) (i32.const 16) (i32.const 12))) (if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret)) (then unreachable)) - + ;; wait for transform to read our write and close all the streams (local.set $ws (call $waitable-set.new)) (call $waitable.join (local.get $insw) (local.get $ws)) diff --git a/tests/misc_testsuite/component-model-async/wait-forever2.wast b/tests/misc_testsuite/component-model-async/wait-forever2.wast index 80b29e3885..e6df132f05 100644 --- a/tests/misc_testsuite/component-model-async/wait-forever2.wast +++ b/tests/misc_testsuite/component-model-async/wait-forever2.wast @@ -42,12 +42,12 @@ (import "" "waitable.join" (func $waitable.join (param i32 i32))) (import "" "waitable-set.new" (func $waitable-set.new (result i32))) (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32))) - (import "" "f" (func $f (param i32 i32) (result i32))) + (import "" "f" (func $f (param i32) (result i32))) (func (export "g") (result i32) (local $ws i32) (local $ret i32) (local $subtaski i32) (local.set $ws (call $waitable-set.new)) - (local.set $ret (call $f (i32.const 0) (i32.const 0))) + (local.set $ret (call $f (i32.const 0))) (local.set $subtaski (i32.shr_u (local.get $ret) (i32.const 4))) (call $waitable.join (local.get $subtaski) (local.get $ws)) (call $waitable-set.wait (local.get $ws) (i32.const 0))