Repository: bytecodealliance/wasmtime
Pull request #8085 (2024-03-12 08:01:25)

Fix `connect_timeout` for `wasi-http`
@elliottt in https://github.com/bytecodealliance/wasmtime/issues/7356 mentions that `connect_timeout`, `first_byte_timeout`, and `between_bytes_timeout` should be tested.

This PR adds a test for `connect_timeout` and ensures that `TcpStream::connect` adheres to the timeout. Without this fix, setting `connect_timeout` has no effect on hosts where `TcpStream::connect` tries to connect.

Note that the variable `connect_timeout` is also used a bit later for the handshake:

https://github.com/bytecodealliance/wasmtime/blob/8dd2ae9e2c57b3fd32f91fb8f0ec1543854f4c7c/crates/wasi-http/src/types.rs#L207-L213

I'm not sure whether using this variable twice is correct.



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index ff529f58a..1af5217a0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4201,29 +4201,36 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e6058f3ef320faece2d48ab105a278e94bac47df03b04af69c205173e0afcc"
+checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27"
 dependencies = [
  "bitflags 2.4.1",
+ "wit-bindgen-rt",
  "wit-bindgen-rust-macro",
 ]
 
 [[package]]
 name = "wit-bindgen-core"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11a8abb3fed5dee9be41aef6609fe2bc4a9d10f82af24fe9b843e885d51ef638"
+checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4"
 dependencies = [
  "anyhow",
  "wit-parser",
 ]
 
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64"
+
 [[package]]
 name = "wit-bindgen-rust"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c63da73f9dc6b68c6aea864b3e277ddb056ec49bd3e41b2b76bc2165d769a9"
+checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3"
 dependencies = [
  "anyhow",
  "heck",
@@ -4235,9 +4242,9 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen-rust-macro"
-version = "0.20.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2adddfe43ab23342715b15225a32b29010f8af91ad9cab7a51a46c5006aea08e"
+checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b"
 dependencies = [
  "anyhow",
  "proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index d6972080e..c7431f823 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -228,7 +228,7 @@ io-lifetimes = { version = "2.0.3", default-features = false }
 io-extras = "0.18.1"
 rustix = "0.38.31"
 # wit-bindgen:
-wit-bindgen = { version = "0.20.0", default-features = false }
+wit-bindgen = { version = "0.22.0", default-features = false }
 
 # wasm-tools family:
 wasmparser = "0.201.0"
diff --git a/RELEASES.md b/RELEASES.md
index 93af3bd3f..30e58bfdc 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -16,8 +16,98 @@ Unreleased.
 
 ### Added
 
+* C API bindings for the sampling-based profiler in Wasmtime have been added.
+  [#7854](https://github.com/bytecodealliance/wasmtime/pull/7854)
+
+* Add the WasiP1Ctx to ease the use of `wasmtime-wasi` in place of `wasi-common`
+  [#8053](https://github.com/bytecodealliance/wasmtime/pull/8053)
+
+* The Winch compiler backend is now feature-complete for x64. Note that minor
+  issues and fuzz bugs are still being addressed, but now's a good time to test
+  if you're interested.
+  [#7894](https://github.com/bytecodealliance/wasmtime/pull/7894)
+  [#7909](https://github.com/bytecodealliance/wasmtime/pull/7909)
+  [#7927](https://github.com/bytecodealliance/wasmtime/pull/7927)
+  [#7932](https://github.com/bytecodealliance/wasmtime/pull/7932)
+  [#7949](https://github.com/bytecodealliance/wasmtime/pull/7949)
+  [#7974](https://github.com/bytecodealliance/wasmtime/pull/7974)
+  [#8001](https://github.com/bytecodealliance/wasmtime/pull/8001)
+
+* The typed function references proposal to WebAssembly is now fully
+  implemented.
+  [#7943](https://github.com/bytecodealliance/wasmtime/pull/7943)
+
+* The `component::Linker` type is now "semver aware" in that it enables loading
+  components referring to past or future versions of interfaces so long as the
+  types are compatible.
+  [#7994](https://github.com/bytecodealliance/wasmtime/pull/7994)
+
+* Wasmtime can now be built for "custom platforms" which is intended for
+  bare-metal builds.
+  [#7995](https://github.com/bytecodealliance/wasmtime/pull/7995)
+
+* The `wasmtime-wasi-nn` crate now has a backend based on WinML.
+  [#7807](https://github.com/bytecodealliance/wasmtime/pull/7807)
+
+* The `wasmtime` CLI now has flags for configuring limits of the pooling
+  allocator.
+  [#8027](https://github.com/bytecodealliance/wasmtime/pull/8027)
+
+
 ### Changed
 
+* The `wasmtime serve` command no longer binds its port with `SO_REUSEADDR`
+  meaning that if it is invoked twice only one will succeed.
+  [#7863](https://github.com/bytecodealliance/wasmtime/pull/7863)
+
+* The sampling-based profiler in Wasmtime now takes an explicit argument of
+  the time delta between samples.
+  [#7873](https://github.com/bytecodealliance/wasmtime/pull/7873)
+
+* Many accessors for type information now require an `&Engine` argument to be
+  specified in preparation for an implementation of wasm GC.
+  [#7892](https://github.com/bytecodealliance/wasmtime/pull/7892)
+
+* The `host` header is now forbidden in wasi-http.
+  [#7905](https://github.com/bytecodealliance/wasmtime/pull/7905)
+
+* Stronger type-checks are now performed for host-owned resources when
+  interacting with the component model to help catch mistakes earlier.
+  [#7902](https://github.com/bytecodealliance/wasmtime/pull/7902)
+
+* Demangling Rust and C/C++ symbols in WebAssembly modules is now done by
+  default in the C API.
+  [#7962](https://github.com/bytecodealliance/wasmtime/pull/7962)
+
+* Preview2-based APIs are now located at the root of the `wasmtime_wasi` crate
+  instead of a submodule.
+  [#7933](https://github.com/bytecodealliance/wasmtime/pull/7933)
+
+* Components now reserve index 0 for handle tables to match the component model
+  specification.
+  [#7661](https://github.com/bytecodealliance/wasmtime/pull/7661)
+
+* Support for `externref` has been moved behind a new `gc` Cargo feature. This
+  will also gate support for wasm gc in the future.
+  [#7975](https://github.com/bytecodealliance/wasmtime/pull/7975)
+
+* Require the `WASMTIME_WASI_CONFIG_PREOPEN_SOCKET_ALLOW` environment variable
+  to bet set to allow the use of `wasi_config_preopen_socket` via the c api, as
+  it will be deprecated in `20.0.0`.
+  [#8053](https://github.com/bytecodealliance/wasmtime/pull/8053)
+
+### Fixed
+
+* WIT interface names that are Rust keywords now correctly generate bindings.
+  [#7790](https://github.com/bytecodealliance/wasmtime/pull/7790)
+
+* PKRU state is now restored across await points.
+  [#7789](https://github.com/bytecodealliance/wasmtime/pull/7789)
+
+* Wasmtime now correctly supports `global.get` in all constant expressions
+  within a module.
+  [#7996](https://github.com/bytecodealliance/wasmtime/pull/7996)
+
 --------------------------------------------------------------------------------
 
 ## 18.0.2
diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle
index f07251c8b..c79a5d70a 100644
--- a/cranelift/codegen/src/isa/x64/inst.isle
+++ b/cranelift/codegen/src/isa/x64/inst.isle
@@ -966,6 +966,7 @@
             Ucomiss
             Ucomisd
             Unpcklps
+            Unpcklpd
             Unpckhps
             Xorps
             Xorpd
@@ -1279,6 +1280,7 @@
             Vpunpckhwd
             Vpunpcklwd
             Vunpcklps
+            Vunpcklpd
             Vunpckhps
             Vandnps
             Vandnpd
@@ -3296,6 +3298,14 @@
       (if-let $true (use_avx))
       (xmm_rmir_vex (AvxOpcode.Vunpcklps) src1 src2))
 
+;; Helper for creating `unpcklpd` instructions.
+(decl x64_unpcklpd (Xmm XmmMem) Xmm)
+(rule 0 (x64_unpcklpd src1 src2)
+      (xmm_rm_r (SseOpcode.Unpcklpd) src1 src2))
+(rule 1 (x64_unpcklpd src1 src2)
+      (if-let $true (use_avx))
+      (xmm_rmir_vex (AvxOpcode.Vunpcklpd) src1 src2))
+
 ;; Helper for creating `unpckhps` instructions.
 (decl x64_unpckhps (Xmm XmmMem) Xmm)
 (rule 0 (x64_unpckhps src1 src2)
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index 8d8182691..85c70b6b3 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -1151,6 +1151,7 @@ pub enum SseOpcode {
     Ucomiss,
     Ucomisd,
     Unpcklps,
+    Unpcklpd,
     Unpckhps,
     Xorps,
     Xorpd,
@@ -1309,7 +1310,8 @@ impl SseOpcode {
             | SseOpcode::Punpcklqdq
             | SseOpcode::Punpckhqdq
             | SseOpcode::Pshuflw
-            | SseOpcode::Pshufhw => SSE2,
+            | SseOpcode::Pshufhw
+            | SseOpcode::Unpcklpd => SSE2,
 
             SseOpcode::Pabsb
             | SseOpcode::Pabsw
@@ -1566,6 +1568,7 @@ impl fmt::Debug for SseOpcode {
             SseOpcode::Pshufhw => "pshufhw",
             SseOpcode::Pblendw => "pblendw",
             SseOpcode::Movddup => "movddup",
+            SseOpcode::Unpcklpd => "unpcklpd",
         };
         write!(fmt, "{}", name)
     }
@@ -1767,7 +1770,8 @@ impl AvxOpcode {
             | AvxOpcode::Vsqrtss
             | AvxOpcode::Vsqrtsd
             | AvxOpcode::Vroundss
-            | AvxOpcode::Vroundsd => {
+            | AvxOpcode::Vroundsd
+            | AvxOpcode::Vunpcklpd => {
                 smallvec![InstructionSet::AVX]
             }
 
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index 9b17aad4e..3f947dae2 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -2226,6 +2226,7 @@ pub(crate) fn emit(
                 SseOpcode::Cvtsd2ss => (LegacyPrefixes::_F2, 0x0F5A, 2),
                 SseOpcode::Sqrtss => (LegacyPrefixes::_F3, 0x0F51, 2),
                 SseOpcode::Sqrtsd => (LegacyPrefixes::_F2, 0x0F51, 2),
+                SseOpcode::Unpcklpd => (LegacyPrefixes::_66, 0x0F14, 2),
                 _ => unimplemented!("Opcode {:?} not implemented", op),
             };
 
@@ -2451,6 +2452,7 @@ pub(crate) fn emit(
                 AvxOpcode::Vcvtsd2ss => (LP::_F2, OM::_0F, 0x5A),
                 AvxOpcode::Vsqrtss => (LP::_F3, OM::_0F, 0x51),
                 AvxOpcode::Vsqrtsd => (LP::_F2, OM::_0F, 0x51),
+                AvxOpcode::Vunpcklpd => (LP::_66, OM::_0F, 0x14),
                 _ => panic!("unexpected rmir vex opcode {op:?}"),
             };
             VexInstruction::new()
diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle
index d872b8152..9bb01775e 100644
--- a/cranelift/codegen/src/isa/x64/lower.isle
+++ b/cranelift/codegen/src/isa/x64/lower.isle
@@ -3403,6 +3403,18 @@
 (rule 0 (lower (fcvt_from_sint a @ (value_type $I32X4)))
       (x64_cvtdq2ps a))
 
+;; Base case: decompose the i64x2 input into two scalar registers and convert
+;; each of those into a float. Afterwards re-pack the two results into the final
+;; destination.
+(rule 0 (lower (fcvt_from_sint a @ (value_type $I64X2)))
+      (let (
+          (a Xmm a)
+          (zero Xmm (xmm_zero $F64X2))
+          (f0 Xmm (x64_cvtsi2sd $I64 zero (x64_movq_to_gpr a)))
+          (f1 Xmm (x64_cvtsi2sd $I64 zero (x64_movq_to_gpr (x64_pshufd a 0b11_10_11_10))))
+        )
+        (x64_unpcklpd f0 f1)))
+
 (rule 1 (lower (has_type $F64X2 (fcvt_from_sint (swiden_low a @ (value_type $I32X4)))))
       (x64_cvtdq2pd a))
 
diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs
index f17a6119a..1d35f7466 100644
--- a/cranelift/wasm/src/environ/dummy.rs
+++ b/cranelift/wasm/src/environ/dummy.rs
@@ -594,20 +594,30 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
         &mut self,
         builder: &mut FunctionBuilder,
         _table_index: TableIndex,
-        _table: ir::Table,
-        _index: ir::Value,
+        table: ir::Table,
+        index: ir::Value,
     ) -> WasmResult<ir::Value> {
-        Ok(builder.ins().null(self.reference_type()))
+        let pointer_type = self.pointer_type();
+        let table_entry_addr = builder.ins().table_addr(pointer_type, table, index, 0);
+        let flags = ir::MemFlags::trusted().with_table();
+        let value = builder
+            .ins()
+            .load(self.reference_type(), flags, table_entry_addr, 0);
+        Ok(value)
     }
 
     fn translate_table_set(
         &mut self,
-        _builder: &mut FunctionBuilder,
+        builder: &mut FunctionBuilder,
         _table_index: TableIndex,
-        _table: ir::Table,
-        _value: ir::Value,
-        _index: ir::Value,
+        table: ir::Table,
+        value: ir::Value,
+        index: ir::Value,
     ) -> WasmResult<()> {
+        let pointer_type = self.pointer_type();
+        let table_entry_addr = builder.ins().table_addr(pointer_type, table, index, 0);
+        let flags = ir::MemFlags::trusted().with_table();
+        builder.ins().store(flags, value, table_entry_addr, 0);
         Ok(())
     }
 
diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/http_impl.rs
index 7c3f7507e..7f4990853 100644
--- a/crates/wasi-http/src/http_impl.rs
+++ b/crates/wasi-http/src/http_impl.rs
@@ -22,15 +22,15 @@ impl<T: WasiHttpView> outgoing_handler::Host for T {
 
         let connect_timeout = opts
             .and_then(|opts| opts.connect_timeout)
-            .unwrap_or(std::time::Duration::from_millis(600 * 1000));
+            .unwrap_or(std::time::Duration::from_secs(600));
 
         let first_byte_timeout = opts
             .and_then(|opts| opts.first_byte_timeout)
-            .unwrap_or(std::time::Duration::from_millis(600 * 1000));
+            .unwrap_or(std::time::Duration::from_secs(600));
 
         let between_bytes_timeout = opts
             .and_then(|opts| opts.between_bytes_timeout)
-            .unwrap_or(std::time::Duration::from_millis(600 * 1000));
+            .unwrap_or(std::time::Duration::from_secs(600));
 
         let req = self.table().delete(request_id)?;
         let mut builder = hyper::Request::builder();
diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs
index 7a78d6f64..c7c8aec44 100644
--- a/crates/wasi-http/src/lib.rs
+++ b/crates/wasi-http/src/lib.rs
@@ -18,10 +18,11 @@ pub mod bindings {
         tracing: true,
         async: false,
         with: {
-            "wasi:io/error": wasmtime_wasi::bindings::io::error,
-            "wasi:io/streams": wasmtime_wasi::bindings::io::streams,
-            "wasi:io/poll": wasmtime_wasi::bindings::io::poll,
+            // Upstream package dependencies
+            "wasi:io": wasmtime_wasi::bindings::io,
 
+            // Configure all WIT http resources to be defined types in this
+            // crate to use the `ResourceTable` helper methods.
             "wasi:http/types/outgoing-body": super::body::HostOutgoingBody,
             "wasi:http/types/future-incoming-response": super::types::HostFutureIncomingResponse,
             "wasi:http/types/outgoing-response": super::types::HostOutgoingResponse,
diff --git a/crates/wasi-http/src/proxy.rs b/crates/wasi-http/src/proxy.rs
index 9e4e6b29d..baf13d4d8 100644
--- a/crates/wasi-http/src/proxy.rs
+++ b/crates/wasi-http/src/proxy.rs
@@ -5,18 +5,8 @@ wasmtime::component::bindgen!({
     tracing: true,
     async: true,
     with: {
-        "wasi:cli/stderr": wasmtime_wasi::bindings::cli::stderr,
-        "wasi:cli/stdin": wasmtime_wasi::bindings::cli::stdin,
-        "wasi:cli/stdout": wasmtime_wasi::bindings::cli::stdout,
-        "wasi:clocks/monotonic-clock": wasmtime_wasi::bindings::clocks::monotonic_clock,
-        "wasi:clocks/timezone": wasmtime_wasi::bindings::clocks::timezone,
-        "wasi:clocks/wall-clock": wasmtime_wasi::bindings::clocks::wall_clock,
-        "wasi:http/incoming-handler": bindings::http::incoming_handler,
-        "wasi:http/outgoing-handler": bindings::http::outgoing_handler,
-        "wasi:http/types": bindings::http::types,
-        "wasi:io/streams": wasmtime_wasi::bindings::io::streams,
-        "wasi:io/poll": wasmtime_wasi::bindings::io::poll,
-        "wasi:random/random": wasmtime_wasi::bindings::random::random,
+        "wasi:http": bindings::http,
+        "wasi": wasmtime_wasi::bindings,
     },
 });
 
@@ -56,18 +46,9 @@ pub mod sync {
         tracing: true,
         async: false,
         with: {
-            "wasi:cli/stderr": wasmtime_wasi::bindings::cli::stderr,
-            "wasi:cli/stdin": wasmtime_wasi::bindings::cli::stdin,
-            "wasi:cli/stdout": wasmtime_wasi::bindings::cli::stdout,
-            "wasi:clocks/monotonic-clock": wasmtime_wasi::bindings::clocks::monotonic_clock,
-            "wasi:clocks/timezone": wasmtime_wasi::bindings::clocks::timezone,
-            "wasi:clocks/wall-clock": wasmtime_wasi::bindings::clocks::wall_clock,
-            "wasi:http/incoming-handler": bindings::http::incoming_handler,
-            "wasi:http/outgoing-handler": bindings::http::outgoing_handler,
-            "wasi:http/types": bindings::http::types,
-            "wasi:io/streams": wasmtime_wasi::bindings::io::streams,
-            "wasi:poll/poll": wasmtime_wasi::bindings::poll::poll,
-            "wasi:random/random": wasmtime_wasi::bindings::random::random,
+            "wasi:http": bindings::http, // http is in this crate
+            "wasi:io": wasmtime_wasi::bindings::sync_io, // io is sync
+            "wasi": wasmtime_wasi::bindings, // everything else
         },
     });
 
diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs
index 6966ed08b..839af33f1 100644
--- a/crates/wasi-http/src/types.rs
+++ b/crates/wasi-http/src/types.rs
@@ -148,8 +148,9 @@ async fn handler(
     mut request: http::Request<HyperOutgoingBody>,
     between_bytes_timeout: Duration,
 ) -> Result<IncomingResponseInternal, types::ErrorCode> {
-    let tcp_stream = TcpStream::connect(authority.clone())
+    let tcp_stream = timeout(connect_timeout, TcpStream::connect(authority.clone()))
         .await
+        .map_err(|_| types::ErrorCode::ConnectionTimeout)?
         .map_err(|e| match e.kind() {
             std::io::ErrorKind::AddrNotAvailable => {
                 dns_error("address not available".to_string(), 0)
diff --git a/crates/wasi/src/bindings.rs b/crates/wasi/src/bindings.rs
index 034aaa92d..a313326da 100644
--- a/crates/wasi/src/bindings.rs
+++ b/crates/wasi/src/bindings.rs
@@ -19,7 +19,13 @@ pub mod sync_io {
                 "wasi:filesystem/types/error-code" => FsError,
             },
             with: {
-                "wasi:clocks/wall-clock": crate::bindings::clocks::wall_clock,
+                // This interface comes from the outer module, as it's
+                // sync/async agnostic.
+                "wasi:clocks": crate::bindings::clocks,
+
+                // Configure the resource types of the bound interfaces here
+                // to be the same as the async versions of the resources, that
+                // way everything has the same type.
                 "wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor,
                 "wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream,
                 "wasi:io/poll/pollable": super::super::io::poll::Pollable,
@@ -100,6 +106,9 @@ wasmtime::component::bindgen!({
         "wasi:sockets/network/error-code" => crate::SocketError,
     },
     with: {
+        // Configure all resources to be concrete types defined in this crate,
+        // so that way we get to use nice typed helper methods with
+        // `ResourceTable`.
         "wasi:sockets/network/network": super::network::Network,
         "wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket,
         "wasi:sockets/udp/udp-socket": super::udp::UdpSocket,
diff --git a/crates/wasi/src/command.rs b/crates/wasi/src/command.rs
index 2638ca103..57ccd7be6 100644
--- a/crates/wasi/src/command.rs
+++ b/crates/wasi/src/command.rs
@@ -4,26 +4,7 @@ wasmtime::component::bindgen!({
     world: "wasi:cli/command",
     tracing: true,
     async: true,
-    with: {
-       "wasi:filesystem/types": crate::bindings::filesystem::types,
-       "wasi:filesystem/preopens": crate::bindings::filesystem::preopens,
-       "wasi:sockets/tcp": crate::bindings::sockets::tcp,
-       "wasi:clocks/monotonic_clock": crate::bindings::clocks::monotonic_clock,
-       "wasi:io/poll": crate::bindings::io::poll,
-       "wasi:io/streams": crate::bindings::io::streams,
-       "wasi:clocks/wall_clock": crate::bindings::clocks::wall_clock,
-       "wasi:random/random": crate::bindings::random::random,
-       "wasi:cli/environment": crate::bindings::cli::environment,
-       "wasi:cli/exit": crate::bindings::cli::exit,
-       "wasi:cli/stdin": crate::bindings::cli::stdin,
-       "wasi:cli/stdout": crate::bindings::cli::stdout,
-       "wasi:cli/stderr": crate::bindings::cli::stderr,
-       "wasi:cli/terminal-input": crate::bindings::cli::terminal_input,
-       "wasi:cli/terminal-output": crate::bindings::cli::terminal_output,
-       "wasi:cli/terminal-stdin": crate::bindings::cli::terminal_stdin,
-       "wasi:cli/terminal-stdout": crate::bindings::cli::terminal_stdout,
-       "wasi:cli/terminal-stderr": crate::bindings::cli::terminal_stderr,
-    },
+    with: { "wasi": crate::bindings },
 });
 
 pub fn add_to_linker<T: WasiView>(l: &mut wasmtime::component::Linker<T>) -> anyhow::Result<()> {
@@ -65,25 +46,14 @@ pub mod sync {
         tracing: true,
         async: false,
         with: {
-           "wasi:filesystem/types": crate::bindings::sync_io::filesystem::types,
-           "wasi:filesystem/preopens": crate::bindings::filesystem::preopens,
-           "wasi:sockets/tcp": crate::bindings::sockets::tcp,
-           "wasi:sockets/udp": crate::bindings::sockets::udp,
-           "wasi:clocks/monotonic_clock": crate::bindings::clocks::monotonic_clock,
-           "wasi:io/poll": crate::bindings::sync_io::io::poll,
-           "wasi:io/streams": crate::bindings::sync_io::io::streams,
-           "wasi:clocks/wall_clock": crate::bindings::clocks::wall_clock,
-           "wasi:random/random": crate::bindings::random::random,
-           "wasi:cli/environment": crate::bindings::cli::environment,
-           "wasi:cli/exit": crate::bindings::cli::exit,
-           "wasi:cli/stdin": crate::bindings::cli::stdin,
-           "wasi:cli/stdout": crate::bindings::cli::stdout,
-           "wasi:cli/stderr": crate::bindings::cli::stderr,
-           "wasi:cli/terminal-input": crate::bindings::cli::terminal_input,
-           "wasi:cli/terminal-output": crate::bindings::cli::terminal_output,
-           "wasi:cli/terminal-stdin": crate::bindings::cli::terminal_stdin,
-           "wasi:cli/terminal-stdout": crate::bindings::cli::terminal_stdout,
-           "wasi:cli/terminal-stderr": crate::bindings::cli::terminal_stderr,
+            // Map interfaces with synchronous funtions to their synchronous
+            // counterparts...
+            "wasi:filesystem": crate::bindings::sync_io::filesystem,
+            "wasi:io": crate::bindings::sync_io::io,
+
+            // ... and everything else is not-async and so goes through the
+            // top-level bindings.
+            "wasi": crate::bindings
         },
     });
 
diff --git a/crates/wasmtime/src/runtime/component/mod.rs b/crates/wasmtime/src/runtime/component/mod.rs
index 7247b93a1..b9f81350c 100644
--- a/crates/wasmtime/src/runtime/component/mod.rs
+++ b/crates/wasmtime/src/runtime/component/mod.rs
@@ -414,7 +414,25 @@ pub(crate) use self::store::ComponentStoreData;
 ///     // with an invocation of this macro. Resources need to be mapped to a
 ///     // Rust type name.
 ///     with: {
-///         "wasi:random/random": some::other::wasi::random::random,
+///         // This can be used to indicate that entire interfaces have
+///         // bindings generated elsewhere with a path pointing to the
+///         // bindinges-generated module.
+///         "wasi:random/random": wasmtime_wasi::bindings::random::random,
+///
+///         // Similarly entire packages can also be specified.
+///         "wasi:cli": wasmtime_wasi::bindings::cli,
+///
+///         // Or, if applicable, entire namespaces can additionally be mapped.
+///         "wasi": wasmtime_wasi::bindings,
+///
+///         // Versions are supported if multiple versions are in play:
+///         "wasi:http/types@0.2.0": wasmtime_wasi_http::bindings::http::types,
+///         "wasi:http@0.2.0": wasmtime_wasi_http::bindings::http,
+///
+///         // The `with` key can also be used to specify the `T` used in
+///         // import bindings of `Resource<T>`. This can be done to configure
+///         // which typed resource shows up in generated bindings and can be
+///         // useful when working with the typed methods of `ResourceTable`.
 ///         "wasi:filesystem/types/descriptor": MyDescriptorType,
 ///     },
 /// });
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index 7042eb7f0..2f92db841 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -48,7 +48,6 @@ struct Wasmtime {
     types: Types,
     sizes: SizeAlign,
     interface_names: HashMap<InterfaceId, InterfaceName>,
-    with_name_counter: usize,
     interface_last_seen_as_import: HashMap<InterfaceId, bool>,
     trappable_errors: IndexMap<TypeId, String>,
 }
@@ -188,13 +187,9 @@ impl Wasmtime {
                 path.push(to_rust_ident(iface.name.as_ref().unwrap()));
             }
         }
-        let entry = if let Some(remapped_path) = self.lookup_replacement(resolve, name, None) {
-            let remapped_path = remapped_path.to_string();
-            let name = format!("__with_name{}", self.with_name_counter);
-            self.with_name_counter += 1;
-            uwriteln!(self.src, "use {remapped_path} as {name};");
+        let entry = if let Some(name_at_root) = self.lookup_replacement(resolve, name, None) {
             InterfaceName::Remapped {
-                name_at_root: name,
+                name_at_root,
                 local_path: path,
             }
         } else {
@@ -263,6 +258,18 @@ impl Wasmtime {
             assert!(prev.is_none());
         }
 
+        // Convert all entries in `with` as relative to the root of where the
+        // macro itself is invoked. This emits a `pub use` to bring the name
+        // into scope under an "anonymous name" which then replaces the `with`
+        // map entry.
+        let mut with = self.opts.with.iter_mut().collect::<Vec<_>>();
+        with.sort();
+        for (i, (_k, v)) in with.into_iter().enumerate() {
+            let name = format!("__with_name{i}");
+            uwriteln!(self.src, "#[doc(hidden)]\npub use {v} as {name};");
+            *v = name;
+        }
+
         let world = &resolve.worlds[id];
         for (name, import) in world.imports.iter() {
             if !self.opts.only_interfaces || matches!(import, WorldItem::Interface(_)) {
@@ -666,42 +673,144 @@ impl Wasmtime {
         }
     }
 
+    /// Attempts to find the `key`, possibly with the resource projection
+    /// `item`, within the `with` map provided to bindings configuration.
     fn lookup_replacement(
         &self,
         resolve: &Resolve,
         key: &WorldKey,
         item: Option<&str>,
-    ) -> Option<&str> {
-        // First try to lookup the exact name of the interface as specified
-        // via `name_world_key`.
-        let name = resolve.name_world_key(key);
-        let candidate1 = match item {
-            Some(item) => format!("{name}/{item}"),
-            None => name.clone(),
+    ) -> Option<String> {
+        struct Name<'a> {
+            prefix: Prefix,
+            item: Option<&'a str>,
+        }
+
+        #[derive(Copy, Clone)]
+        enum Prefix {
+            Namespace(PackageId),
+            UnversionedPackage(PackageId),
+            VersionedPackage(PackageId),
+            UnversionedInterface(InterfaceId),
+            VersionedInterface(InterfaceId),
+        }
+
+        let prefix = match key {
+            WorldKey::Interface(id) => Prefix::VersionedInterface(*id),
+
+            // Non-interface-keyed names don't get the lookup logic below,
+            // they're relatively uncommon so only lookup the precise key here.
+            WorldKey::Name(key) => {
+                let to_lookup = match item {
+                    Some(item) => format!("{key}/{item}"),
+                    None => key.to_string(),
+                };
+                return self.opts.with.get(&to_lookup).cloned();
+            }
         };
-        if let Some(ret) = self.opts.with.get(&candidate1) {
-            return Some(ret);
-        }
 
-        // .. if the above failed allow omitting the `@...` version information
-        // and see if there's a `with` key for that.
+        // Here names are iteratively attempted as `key` + `item` is "walked to
+        // its root" and each attempt is consulted in `self.opts.with`. This
+        // loop will start at the leaf, the most specific path, and then walk to
+        // the root, popping items, trying to find a result.
         //
-        // NB: this means that in a scenario where there's packages with the
-        // same name/namespace where one has a version and one doesn't there's
-        // no way to use `with` to specify just one and not the other. That
-        // should be ok for now, but this should ideally detect a situation like
-        // that and omit this fallback in such a situation.
-        let version_start = name.find('@')?;
-        let name = &name[..version_start];
-        let candidate2 = match item {
-            Some(item) => format!("{name}/{item}"),
-            None => name.to_string(),
-        };
-        if let Some(ret) = self.opts.with.get(&candidate2) {
-            return Some(ret);
+        // Each time a name is "popped" the projection from the next path is
+        // pushed onto `projection`. This means that if we actually find a match
+        // then `projection` is a collection of namespaces that results in the
+        // final replacement name.
+        let mut name = Name { prefix, item };
+        let mut projection = Vec::new();
+        loop {
+            let lookup = name.lookup_key(resolve);
+            if let Some(renamed) = self.opts.with.get(&lookup) {
+                projection.push(renamed.clone());
+                projection.reverse();
+                return Some(projection.join("::"));
+            }
+            if !name.pop(resolve, &mut projection) {
+                return None;
+            }
+        }
+
+        impl<'a> Name<'a> {
+            fn lookup_key(&self, resolve: &Resolve) -> String {
+                let mut s = self.prefix.lookup_key(resolve);
+                if let Some(item) = self.item {
+                    s.push_str("/");
+                    s.push_str(item);
+                }
+                s
+            }
+
+            fn pop(&mut self, resolve: &'a Resolve, projection: &mut Vec<String>) -> bool {
+                match (self.item, self.prefix) {
+                    // If this is a versioned resource name, try the unversioned
+                    // resource name next.
+                    (Some(_), Prefix::VersionedInterface(id)) => {
+                        self.prefix = Prefix::UnversionedInterface(id);
+                        true
+                    }
+                    // If this is an unversioned resource name then time to
+                    // ignore the resource itself and move on to the next most
+                    // specific item, versioned interface names.
+                    (Some(item), Prefix::UnversionedInterface(id)) => {
+                        self.prefix = Prefix::VersionedInterface(id);
+                        self.item = None;
+                        projection.push(item.to_upper_camel_case());
+                        true
+                    }
+                    (Some(_), _) => unreachable!(),
+                    (None, _) => self.prefix.pop(resolve, projection),
+                }
+            }
         }
 
-        None
+        impl Prefix {
+            fn lookup_key(&self, resolve: &Resolve) -> String {
+                match *self {
+                    Prefix::Namespace(id) => resolve.packages[id].name.namespace.clone(),
+                    Prefix::UnversionedPackage(id) => {
+                        let mut name = resolve.packages[id].name.clone();
+                        name.version = None;
+                        name.to_string()
+                    }
+                    Prefix::VersionedPackage(id) => resolve.packages[id].name.to_string(),
+                    Prefix::UnversionedInterface(id) => {
+                        let id = resolve.id_of(id).unwrap();
+                        match id.find('@') {
+                            Some(i) => id[..i].to_string(),
+                            None => id,
+                        }
+                    }
+                    Prefix::VersionedInterface(id) => resolve.id_of(id).unwrap(),
+                }
+            }
+
+            fn pop(&mut self, resolve: &Resolve, projection: &mut Vec<String>) -> bool {
+                *self = match *self {
+                    // try the unversioned interface next
+                    Prefix::VersionedInterface(id) => Prefix::UnversionedInterface(id),
+                    // try this interface's versioned package next
+                    Prefix::UnversionedInterface(id) => {
+                        let iface = &resolve.interfaces[id];
+                        let name = iface.name.as_ref().unwrap();
+                        projection.push(to_rust_ident(name));
+                        Prefix::VersionedPackage(iface.package.unwrap())
+                    }
+                    // try the unversioned package next
+                    Prefix::VersionedPackage(id) => Prefix::UnversionedPackage(id),
+                    // try this package's namespace next
+                    Prefix::UnversionedPackage(id) => {
+                        let name = &resolve.packages[id].name;
+                        projection.push(to_rust_ident(&name.name));
+                        Prefix::Namespace(id)
+                    }
+                    // nothing left to try any more
+                    Prefix::Namespace(_) => return false,
+                };
+                true
+            }
+        }
     }
 }
 
@@ -960,7 +1069,7 @@ impl<'a> InterfaceGenerator<'a> {
 
             let replacement = match self.current_interface {
                 Some((_, key, _)) => self.gen.lookup_replacement(self.resolve, key, Some(name)),
-                None => self.gen.opts.with.get(name).map(|s| s.as_str()),
+                None => self.gen.opts.with.get(name).cloned(),
             };
             match replacement {
                 Some(path) => {
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 5f6db2f7b..f37438171 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -3739,6 +3739,12 @@ user-id = 73222 # wasmtime-publish
 start = "2024-02-23"
 end = "2025-02-23"
 
+[[trusted.wit-bindgen-rt]]
+criteria = "safe-to-deploy"
+user-id = 73222 # wasmtime-publish
+start = "2024-03-04"
+end = "2025-03-11"
+
 [[trusted.wit-bindgen-rust]]
 criteria = "safe-to-deploy"
 user-id = 73222 # wasmtime-publish
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 3a2bea9a3..622fa6e93 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1886,6 +1886,12 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-core]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1911,6 +1917,18 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-core]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
+[[publisher.wit-bindgen-rt]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1936,6 +1954,12 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust-macro]]
 version = "0.17.0"
 when = "2024-02-05"
@@ -1961,6 +1985,12 @@ when = "2024-02-29"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust-macro]]
+version = "0.22.0"
+when = "2024-03-11"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-component]]
 version = "0.20.1"
 when = "2024-01-29"
diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs
index 05bc71116..978d16561 100644
--- a/winch/codegen/src/codegen/env.rs
+++ b/winch/codegen/src/codegen/env.rs
@@ -205,9 +205,10 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
     {
         let types = self.translation.get_types();
         let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
-        let ty = self.convert_func_type(ty);
         let import = self.translation.module.is_imported_function(idx);
         let val = || {
+            let converter = TypeConverter::new(self.translation, self.types);
+            let ty = converter.convert_func_type(&ty);
             let sig = wasm_sig::<A>(&ty);
             CalleeInfo { sig, index: idx }
         };
@@ -225,7 +226,7 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
         match ty {
             Empty => BlockSig::new(control::BlockType::void()),
             Type(ty) => {
-                let ty = self.convert_valtype(ty);
+                let ty = TypeConverter::new(self.translation, self.types).convert_valtype(ty);
                 BlockSig::new(control::BlockType::single(ty))
             }
             FuncType(idx) => {
@@ -351,7 +352,14 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
     }
 }
 
-impl<P: PtrSize> TypeConvert for FuncEnv<'_, '_, '_, P> {
+/// A wrapper struct over a reference to a [ModuleTranslation] and
+/// [ModuleTypesBuilder].
+pub(crate) struct TypeConverter<'a, 'data: 'a> {
+    translation: &'a ModuleTranslation<'data>,
+    types: &'a ModuleTypesBuilder,
+}
+
+impl TypeConvert for TypeConverter<'_, '_> {
     fn lookup_heap_type(&self, idx: wasmparser::UnpackedIndex) -> WasmHeapType {
         wasmtime_environ::WasmparserTypeConverter {
             module: &self.translation.module,
@@ -361,6 +369,12 @@ impl<P: PtrSize> TypeConvert for FuncEnv<'_, '_, '_, P> {
     }
 }
 
+impl<'a, 'data> TypeConverter<'a, 'data> {
+    pub fn new(translation: &'a ModuleTranslation<'data>, types: &'a ModuleTypesBuilder) -> Self {
+        Self { translation, types }
+    }
+}
+
 fn heap_style_and_offset_guard_size(plan: &MemoryPlan) -> (HeapStyle, u64) {
     match plan {
         MemoryPlan {
diff --git a/winch/codegen/src/isa/aarch64/mod.rs b/winch/codegen/src/isa/aarch64/mod.rs
index 4e7db5ac0..52ef3e125 100644
--- a/winch/codegen/src/isa/aarch64/mod.rs
+++ b/winch/codegen/src/isa/aarch64/mod.rs
@@ -1,7 +1,7 @@
 use self::regs::{ALL_GPR, MAX_FPR, MAX_GPR, NON_ALLOCATABLE_GPR};
 use crate::{
     abi::{wasm_sig, ABI},
-    codegen::{CodeGen, CodeGenContext, FuncEnv},
+    codegen::{CodeGen, CodeGenContext, FuncEnv, TypeConverter},
     frame::{DefinedLocals, Frame},
     isa::{Builder, TargetIsa},
     masm::MacroAssembler,
@@ -106,7 +106,9 @@ impl TargetIsa for Aarch64 {
             self,
             abi::Aarch64ABI::ptr_type(),
         );
-        let defined_locals = DefinedLocals::new::<abi::Aarch64ABI>(&env, &mut body, validator)?;
+        let type_converter = TypeConverter::new(env.translation, env.types);
+        let defined_locals =
+            DefinedLocals::new::<abi::Aarch64ABI>(&type_converter, &mut body, validator)?;
         let frame = Frame::new::<abi::Aarch64ABI>(&abi_sig, &defined_locals)?;
         let gpr = RegBitSet::int(
             ALL_GPR.into(),
diff --git a/winch/codegen/src/isa/x64/mod.rs b/winch/codegen/src/isa/x64/mod.rs
index b420711bb..20578b0bd 100644
--- a/winch/codegen/src/isa/x64/mod.rs
+++ b/winch/codegen/src/isa/x64/mod.rs
@@ -1,6 +1,6 @@
 use crate::{
     abi::{wasm_sig, ABI},
-    codegen::{BuiltinFunctions, CodeGen, CodeGenContext, FuncEnv},
+    codegen::{BuiltinFunctions, CodeGen, CodeGenContext, FuncEnv, TypeConverter},
 };
 
 use crate::frame::{DefinedLocals, Frame};
@@ -116,7 +116,9 @@ impl TargetIsa for X64 {
             self,
             abi::X64ABI::ptr_type(),
         );
-        let defined_locals = DefinedLocals::new::<abi::X64ABI>(&env, &mut body, validator)?;
+        let type_converter = TypeConverter::new(env.translation, env.types);
+        let defined_locals =
+            DefinedLocals::new::<abi::X64ABI>(&type_converter, &mut body, validator)?;
         let frame = Frame::new::<abi::X64ABI>(&abi_sig, &defined_locals)?;
         let gpr = RegBitSet::int(
             ALL_GPR.into(),


Test patch:
diff --git a/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif b/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif
index 5d72e1090..61f7f0ec3 100644
--- a/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif
+++ b/cranelift/filetests/filetests/isa/x64/fcvt-avx.clif
@@ -155,3 +155,41 @@ block0(v0: i64x2):
 ;   addb %al, (%rax)
 ;   addb %al, (%rax)
 
+function %i64x2_to_f64x2(i64x2) -> f64x2 {
+block0(v0: i64x2):
+  v1 = fcvt_from_sint.f64x2 v0
+  return v1
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   uninit  %xmm2
+;   vxorpd  %xmm2, %xmm2, %xmm4
+;   vmovq   %xmm0, %r9
+;   vcvtsi2sd %xmm4, %r9, %xmm1
+;   vpshufd $238, %xmm0, %xmm2
+;   vmovq   %xmm2, %rcx
+;   vcvtsi2sd %xmm4, %rcx, %xmm6
+;   vunpcklpd %xmm1, %xmm6, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vxorpd %xmm2, %xmm2, %xmm4
+;   vmovq %xmm0, %r9
+;   vcvtsi2sdq %r9, %xmm4, %xmm1
+;   vpshufd $0xee, %xmm0, %xmm2
+;   vmovq %xmm2, %rcx
+;   vcvtsi2sdq %rcx, %xmm4, %xmm6
+;   vunpcklpd %xmm6, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/fcvt.clif b/cranelift/filetests/filetests/isa/x64/fcvt.clif
index 882d253e3..ae506aa09 100644
--- a/cranelift/filetests/filetests/isa/x64/fcvt.clif
+++ b/cranelift/filetests/filetests/isa/x64/fcvt.clif
@@ -1196,3 +1196,45 @@ block0(v0: i64x2):
 ;   addb %al, (%rax)
 ;   addb %al, (%rax)
 
+function %i64x2_to_f64x2(i64x2) -> f64x2 {
+block0(v0: i64x2):
+  v1 = fcvt_from_sint.f64x2 v0
+  return v1
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   uninit  %xmm1
+;   xorpd   %xmm1, %xmm1, %xmm1
+;   movdqa  %xmm0, %xmm6
+;   movq    %xmm6, %r9
+;   movdqa  %xmm1, %xmm0
+;   cvtsi2sd %xmm0, %r9, %xmm0
+;   pshufd  $238, %xmm6, %xmm2
+;   movq    %xmm2, %rcx
+;   cvtsi2sd %xmm1, %rcx, %xmm1
+;   unpcklpd %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   xorpd %xmm1, %xmm1
+;   movdqa %xmm0, %xmm6
+;   movq %xmm6, %r9
+;   movdqa %xmm1, %xmm0
+;   cvtsi2sdq %r9, %xmm0
+;   pshufd $0xee, %xmm6, %xmm2
+;   movq %xmm2, %rcx
+;   cvtsi2sdq %rcx, %xmm1
+;   unpcklpd %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/runtests/simd-fcvt-from-sint.clif b/cranelift/filetests/filetests/runtests/simd-fcvt-from-sint.clif
index f7e2e6571..d5f86eb2d 100644
--- a/cranelift/filetests/filetests/runtests/simd-fcvt-from-sint.clif
+++ b/cranelift/filetests/filetests/runtests/simd-fcvt-from-sint.clif
@@ -9,10 +9,18 @@ target x86_64 sse42 has_avx
 target riscv64 has_v
 target riscv64 has_v has_c has_zcb
 
-function %fcvt_from_sint(i32x4) -> f32x4 {
+function %fcvt_from_sint32(i32x4) -> f32x4 {
 block0(v0: i32x4):
     v1 = fcvt_from_sint.f32x4 v0
     return v1
 }
-; run: %fcvt_from_sint([-1 0 1 123456789]) == [-0x1.0 0.0 0x1.0 0x75bcd18.0]
+; run: %fcvt_from_sint32([-1 0 1 123456789]) == [-0x1.0 0.0 0x1.0 0x75bcd18.0]
 ; Note that 123456789 rounds to 123456792.0, an error of 3
+
+function %fcvt_from_sint64(i64x2) -> f64x2 {
+block0(v0: i64x2):
+    v1 = fcvt_from_sint.f64x2 v0
+    return v1
+}
+; run: %fcvt_from_sint64([-1 0]) == [-0x1.0 0.0]
+; run: %fcvt_from_sint64([1 123456789]) == [0x1.0 0x75bcd15.0]
diff --git a/cranelift/filetests/filetests/wasm/table-get.wat b/cranelift/filetests/filetests/wasm/table-get.wat
new file mode 100644
index 000000000..20abed729
--- /dev/null
+++ b/cranelift/filetests/filetests/wasm/table-get.wat
@@ -0,0 +1,81 @@
+;;! target = "x86_64"
+;;! optimize = true
+
+;; Test basic code generation for table WebAssembly instructions.
+;; Use optimization but with opt-level "none" to legalize away table_addr instructions.
+
+(module
+  (table (export "table") 1 externref)
+  (func (export "table.get.const") (result externref)
+    i32.const 0
+    table.get 0)
+  (func (export "table.get.var") (param i32) (result externref)
+    local.get 0
+    table.get 0))
+
+;; function u0:0(i64 vmctx) -> r64 fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0
+;;     gv2 = load.i32 notrap aligned readonly gv0
+;;     table0 = dynamic gv1, min 0, bound gv2, element_size 16, index_type i32
+;;
+;;                                 block0(v0: i64):
+;;                                     v13 -> v0
+;;                                     v14 -> v0
+;; @0051                               v2 = iconst.i32 0
+;; @0053                               v5 = load.i32 notrap aligned readonly v13
+;; @0053                               v6 = icmp uge v2, v5  ; v2 = 0
+;; @0053                               brif v6, block2, block3
+;;
+;;                                 block2 cold:
+;; @0053                               trap table_oob
+;;
+;;                                 block3:
+;; @0053                               v7 = uextend.i64 v2  ; v2 = 0
+;; @0053                               v8 = load.i64 notrap aligned readonly v14
+;;                                     v15 = iconst.i64 4
+;; @0053                               v9 = ishl v7, v15  ; v15 = 4
+;; @0053                               v10 = iadd v8, v9
+;; @0053                               v11 = icmp.i32 uge v2, v5  ; v2 = 0
+;; @0053                               v12 = select_spectre_guard v11, v8, v10
+;;                                     v3 -> v12
+;; @0053                               v4 = load.r64 notrap aligned table v3
+;;                                     v1 -> v4
+;; @0055                               jump block1
+;;
+;;                                 block1:
+;; @0055                               return v1
+;; }
+;;
+;; function u0:1(i32, i64 vmctx) -> r64 fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0
+;;     gv2 = load.i32 notrap aligned readonly gv0
+;;     table0 = dynamic gv1, min 0, bound gv2, element_size 16, index_type i32
+;;
+;;                                 block0(v0: i32, v1: i64):
+;;                                     v13 -> v1
+;;                                     v14 -> v1
+;; @005a                               v5 = load.i32 notrap aligned readonly v13
+;; @005a                               v6 = icmp uge v0, v5
+;; @005a                               brif v6, block2, block3
+;;
+;;                                 block2 cold:
+;; @005a                               trap table_oob
+;;
+;;                                 block3:
+;; @005a                               v7 = uextend.i64 v0
+;; @005a                               v8 = load.i64 notrap aligned readonly v14
+;;                                     v15 = iconst.i64 4
+;; @005a                               v9 = ishl v7, v15  ; v15 = 4
+;; @005a                               v10 = iadd v8, v9
+;; @005a                               v11 = icmp.i32 uge v0, v5
+;; @005a                               v12 = select_spectre_guard v11, v8, v10
+;;                                     v3 -> v12
+;; @005a                               v4 = load.r64 notrap aligned table v3
+;;                                     v2 -> v4
+;; @005c                               jump block1
+;;
+;;                                 block1:
+;; @005c                               return v2
+;; }
diff --git a/cranelift/filetests/filetests/wasm/table-set.wat b/cranelift/filetests/filetests/wasm/table-set.wat
new file mode 100644
index 000000000..650dfc04a
--- /dev/null
+++ b/cranelift/filetests/filetests/wasm/table-set.wat
@@ -0,0 +1,81 @@
+;;! target = "x86_64"
+;;! optimize = true
+
+;; Test basic code generation for table WebAssembly instructions.
+;; Use optimization but with opt-level "none" to legalize away table_addr instructions.
+
+(module
+  (table (export "table") 1 externref)
+  (func (export "table.set.const") (param externref)
+    i32.const 0
+    local.get 0
+    table.set 0)
+  (func (export "table.set.var") (param i32 externref)
+    local.get 0
+    local.get 1
+    table.set 0))
+
+;; function u0:0(r64, i64 vmctx) fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0
+;;     gv2 = load.i32 notrap aligned readonly gv0
+;;     table0 = dynamic gv1, min 0, bound gv2, element_size 16, index_type i32
+;;
+;;                                 block0(v0: r64, v1: i64):
+;;                                     v12 -> v1
+;;                                     v13 -> v1
+;; @0051                               v2 = iconst.i32 0
+;; @0055                               v4 = load.i32 notrap aligned readonly v12
+;; @0055                               v5 = icmp uge v2, v4  ; v2 = 0
+;; @0055                               brif v5, block2, block3
+;;
+;;                                 block2 cold:
+;; @0055                               trap table_oob
+;;
+;;                                 block3:
+;; @0055                               v6 = uextend.i64 v2  ; v2 = 0
+;; @0055                               v7 = load.i64 notrap aligned readonly v13
+;;                                     v14 = iconst.i64 4
+;; @0055                               v8 = ishl v6, v14  ; v14 = 4
+;; @0055                               v9 = iadd v7, v8
+;; @0055                               v10 = icmp.i32 uge v2, v4  ; v2 = 0
+;; @0055                               v11 = select_spectre_guard v10, v7, v9
+;;                                     v3 -> v11
+;; @0055                               store.r64 notrap aligned table v0, v3
+;; @0057                               jump block1
+;;
+;;                                 block1:
+;; @0057                               return
+;; }
+;;
+;; function u0:1(i32, r64, i64 vmctx) fast {
+;;     gv0 = vmctx
+;;     gv1 = load.i64 notrap aligned readonly gv0
+;;     gv2 = load.i32 notrap aligned readonly gv0
+;;     table0 = dynamic gv1, min 0, bound gv2, element_size 16, index_type i32
+;;
+;;                                 block0(v0: i32, v1: r64, v2: i64):
+;;                                     v12 -> v2
+;;                                     v13 -> v2
+;; @005e                               v4 = load.i32 notrap aligned readonly v12
+;; @005e                               v5 = icmp uge v0, v4
+;; @005e                               brif v5, block2, block3
+;;
+;;                                 block2 cold:
+;; @005e                               trap table_oob
+;;
+;;                                 block3:
+;; @005e                               v6 = uextend.i64 v0
+;; @005e                               v7 = load.i64 notrap aligned readonly v13
+;;                                     v14 = iconst.i64 4
+;; @005e                               v8 = ishl v6, v14  ; v14 = 4
+;; @005e                               v9 = iadd v7, v8
+;; @005e                               v10 = icmp.i32 uge v0, v4
+;; @005e                               v11 = select_spectre_guard v10, v7, v9
+;;                                     v3 -> v11
+;; @005e                               store.r64 notrap aligned table v1, v3
+;; @0060                               jump block1
+;;
+;;                                 block1:
+;; @0060                               return
+;; }
diff --git a/crates/component-macro/tests/codegen.rs b/crates/component-macro/tests/codegen.rs
index c7fb0f51a..da2f0550d 100644
--- a/crates/component-macro/tests/codegen.rs
+++ b/crates/component-macro/tests/codegen.rs
@@ -162,3 +162,154 @@ mod interface_name_with_rust_keyword {
         "
     });
 }
+
+mod with_works_with_hierarchy {
+    mod bindings {
+        wasmtime::component::bindgen!({
+            inline: "
+                package foo:foo;
+
+                interface a {
+                    record t {
+                        x: u32,
+                    }
+                    x: func() -> t;
+                }
+
+                interface b {
+                    use a.{t};
+                    x: func() -> t;
+                }
+
+                interface c {
+                    use b.{t};
+                    x: func() -> t;
+                }
+
+                world foo {
+                    import c;
+                }
+            "
+        });
+    }
+
+    mod with_just_one_interface {
+        wasmtime::component::bindgen!({
+            inline: "
+                package foo:foo;
+
+                interface a {
+                    record t {
+                        x: u32,
+                    }
+                    x: func() -> t;
+                }
+
+                interface b {
+                    use a.{t};
+                    x: func() -> t;
+                }
+
+                interface c {
+                    use b.{t};
+                    x: func() -> t;
+                }
+
+                world foo {
+                    use c.{t};
+
+                    import x: func() -> t;
+                }
+            ",
+            with: { "foo:foo/a": super::bindings::foo::foo::a }
+        });
+
+        struct X;
+
+        impl FooImports for X {
+            fn x(&mut self) -> wasmtime::Result<super::bindings::foo::foo::a::T> {
+                loop {}
+            }
+        }
+    }
+
+    mod with_whole_package {
+        wasmtime::component::bindgen!({
+            inline: "
+                package foo:foo;
+
+                interface a {
+                    record t {
+                        x: u32,
+                    }
+                    x: func() -> t;
+                }
+
+                interface b {
+                    use a.{t};
+                    x: func() -> t;
+                }
+
+                interface c {
+                    use b.{t};
+                    x: func() -> t;
+                }
+
+                world foo {
+                    use c.{t};
+
+                    import x: func() -> t;
+                }
+            ",
+            with: { "foo:foo": super::bindings::foo::foo }
+        });
+
+        struct X;
+
+        impl FooImports for X {
+            fn x(&mut self) -> wasmtime::Result<super::bindings::foo::foo::a::T> {
+                loop {}
+            }
+        }
+    }
+
+    mod with_whole_namespace {
+        wasmtime::component::bindgen!({
+            inline: "
+                package foo:foo;
+
+                interface a {
+                    record t {
+                        x: u32,
+                    }
+                    x: func() -> t;
+                }
+
+                interface b {
+                    use a.{t};
+                    x: func() -> t;
+                }
+
+                interface c {
+                    use b.{t};
+                    x: func() -> t;
+                }
+
+                world foo {
+                    use c.{t};
+
+                    import x: func() -> t;
+                }
+            ",
+            with: { "foo": super::bindings::foo }
+        });
+
+        struct X;
+
+        impl FooImports for X {
+            fn x(&mut self) -> wasmtime::Result<super::bindings::foo::foo::a::T> {
+                loop {}
+            }
+        }
+    }
+}
diff --git a/crates/test-programs/src/bin/http_outbound_request_get.rs b/crates/test-programs/src/bin/http_outbound_request_get.rs
index 91a7bc719..ab12935c2 100644
--- a/crates/test-programs/src/bin/http_outbound_request_get.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_get.rs
@@ -10,6 +10,9 @@ fn main() {
         "/get?some=arg&goes=here",
         None,
         None,
+        None,
+        None,
+        None,
     )
     .context("/get")
     .unwrap();
diff --git a/crates/test-programs/src/bin/http_outbound_request_invalid_dnsname.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_dnsname.rs
index 0b6deddef..a566b873c 100644
--- a/crates/test-programs/src/bin/http_outbound_request_invalid_dnsname.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_invalid_dnsname.rs
@@ -8,6 +8,9 @@ fn main() {
         "/",
         None,
         None,
+        None,
+        None,
+        None,
     );
 
     let e = res.unwrap_err();
diff --git a/crates/test-programs/src/bin/http_outbound_request_invalid_port.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_port.rs
index e49690ce0..9d22c6746 100644
--- a/crates/test-programs/src/bin/http_outbound_request_invalid_port.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_invalid_port.rs
@@ -8,6 +8,9 @@ fn main() {
         "/",
         None,
         None,
+        None,
+        None,
+        None,
     );
 
     assert!(res.is_err());
diff --git a/crates/test-programs/src/bin/http_outbound_request_invalid_version.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_version.rs
index 568026859..e2009324a 100644
--- a/crates/test-programs/src/bin/http_outbound_request_invalid_version.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_invalid_version.rs
@@ -2,8 +2,17 @@ use test_programs::wasi::http::types::{ErrorCode, Method, Scheme};
 
 fn main() {
     let addr = std::env::var("HTTP_SERVER").unwrap();
-    let res =
-        test_programs::http::request(Method::Connect, Scheme::Http, &addr, "/", None, Some(&[]));
+    let res = test_programs::http::request(
+        Method::Connect,
+        Scheme::Http,
+        &addr,
+        "/",
+        None,
+        Some(&[]),
+        None,
+        None,
+        None,
+    );
 
     assert!(matches!(
         res.unwrap_err()
diff --git a/crates/test-programs/src/bin/http_outbound_request_large_post.rs b/crates/test-programs/src/bin/http_outbound_request_large_post.rs
index 111cba139..4e0749369 100644
--- a/crates/test-programs/src/bin/http_outbound_request_large_post.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_large_post.rs
@@ -15,6 +15,9 @@ fn main() {
         "/post",
         Some(&buffer),
         None,
+        None,
+        None,
+        None,
     )
     .context("/post large")
     .unwrap();
diff --git a/crates/test-programs/src/bin/http_outbound_request_post.rs b/crates/test-programs/src/bin/http_outbound_request_post.rs
index 89f8b6ba8..1a499a953 100644
--- a/crates/test-programs/src/bin/http_outbound_request_post.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_post.rs
@@ -10,6 +10,9 @@ fn main() {
         "/post",
         Some(b"{\"foo\": \"bar\"}"),
         None,
+        None,
+        None,
+        None,
     )
     .context("/post")
     .unwrap();
diff --git a/crates/test-programs/src/bin/http_outbound_request_put.rs b/crates/test-programs/src/bin/http_outbound_request_put.rs
index 5184464e2..dd3644b51 100644
--- a/crates/test-programs/src/bin/http_outbound_request_put.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_put.rs
@@ -3,10 +3,19 @@ use test_programs::wasi::http::types::{Method, Scheme};
 
 fn main() {
     let addr = std::env::var("HTTP_SERVER").unwrap();
-    let res =
-        test_programs::http::request(Method::Put, Scheme::Http, &addr, "/put", Some(&[]), None)
-            .context("/put")
-            .unwrap();
+    let res = test_programs::http::request(
+        Method::Put,
+        Scheme::Http,
+        &addr,
+        "/put",
+        Some(&[]),
+        None,
+        None,
+        None,
+        None,
+    )
+    .context("/put")
+    .unwrap();
 
     println!("/put: {res:?}");
     assert_eq!(res.status, 200);
diff --git a/crates/test-programs/src/bin/http_outbound_request_timeout.rs b/crates/test-programs/src/bin/http_outbound_request_timeout.rs
new file mode 100644
index 000000000..198288e48
--- /dev/null
+++ b/crates/test-programs/src/bin/http_outbound_request_timeout.rs
@@ -0,0 +1,32 @@
+use anyhow::Context;
+use std::net::SocketAddr;
+use std::time::Duration;
+use test_programs::wasi::http::types::{ErrorCode, Method, Scheme};
+
+fn main() {
+    // This address inside the TEST-NET-3 address block is expected to time out.
+    let addr = SocketAddr::from(([203, 0, 113, 12], 80)).to_string();
+    let timeout = Duration::from_millis(200);
+    let connect_timeout: Option<u64> = Some(timeout.as_nanos() as u64);
+    let res = test_programs::http::request(
+        Method::Get,
+        Scheme::Http,
+        &addr,
+        "/get?some=arg&goes=here",
+        None,
+        None,
+        connect_timeout,
+        None,
+        None,
+    )
+    .context("/get");
+
+    assert!(res.is_err());
+    assert!(
+        matches!(
+            res.unwrap_err().downcast_ref::<ErrorCode>(),
+            Some(ErrorCode::ConnectionTimeout)
+        ),
+        "expected connection timeout"
+    );
+}
diff --git a/crates/test-programs/src/bin/http_outbound_request_unknown_method.rs b/crates/test-programs/src/bin/http_outbound_request_unknown_method.rs
index 50d5751f5..d6fd61c9d 100644
--- a/crates/test-programs/src/bin/http_outbound_request_unknown_method.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_unknown_method.rs
@@ -8,6 +8,9 @@ fn main() {
         "/",
         None,
         None,
+        None,
+        None,
+        None,
     );
 
     // This error arises from input validation in the `set_method` function on `OutgoingRequest`.
diff --git a/crates/test-programs/src/bin/http_outbound_request_unsupported_scheme.rs b/crates/test-programs/src/bin/http_outbound_request_unsupported_scheme.rs
index 84ce1a5ba..a596f2dc9 100644
--- a/crates/test-programs/src/bin/http_outbound_request_unsupported_scheme.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_unsupported_scheme.rs
@@ -8,6 +8,9 @@ fn main() {
         "/",
         None,
         None,
+        None,
+        None,
+        None,
     );
 
     assert!(matches!(
diff --git a/crates/test-programs/src/http.rs b/crates/test-programs/src/http.rs
index 93ccb8243..cde88cb62 100644
--- a/crates/test-programs/src/http.rs
+++ b/crates/test-programs/src/http.rs
@@ -37,6 +37,9 @@ pub fn request(
     path_with_query: &str,
     body: Option<&[u8]>,
     additional_headers: Option<&[(String, Vec<u8>)]>,
+    connect_timeout: Option<u64>,
+    first_by_timeout: Option<u64>,
+    between_bytes_timeout: Option<u64>,
 ) -> Result<Response> {
     fn header_val(v: &str) -> Vec<u8> {
         v.to_string().into_bytes()
@@ -108,7 +111,19 @@ pub fn request(
         };
     }
 
-    let future_response = outgoing_handler::handle(request, None)?;
+    let options = http_types::RequestOptions::new();
+    options
+        .set_connect_timeout(connect_timeout)
+        .map_err(|()| anyhow!("failed to set connect_timeout"))?;
+    options
+        .set_first_byte_timeout(first_by_timeout)
+        .map_err(|()| anyhow!("failed to set first_byte_timeout"))?;
+    options
+        .set_between_bytes_timeout(between_bytes_timeout)
+        .map_err(|()| anyhow!("failed to set between_bytes_timeout"))?;
+    let options = Some(options);
+
+    let future_response = outgoing_handler::handle(request, options)?;
 
     http_types::OutgoingBody::finish(outgoing_body, None)?;
 
diff --git a/crates/wasi-http/tests/all/async_.rs b/crates/wasi-http/tests/all/async_.rs
index d79bf445c..7d6ac8572 100644
--- a/crates/wasi-http/tests/all/async_.rs
+++ b/crates/wasi-http/tests/all/async_.rs
@@ -26,6 +26,12 @@ async fn http_outbound_request_get() -> Result<()> {
     run(HTTP_OUTBOUND_REQUEST_GET_COMPONENT, &server).await
 }
 
+#[test_log::test(tokio::test(flavor = "multi_thread"))]
+async fn http_outbound_request_timeout() -> Result<()> {
+    let server = Server::http1()?;
+    run(HTTP_OUTBOUND_REQUEST_TIMEOUT_COMPONENT, &server).await
+}
+
 #[test_log::test(tokio::test(flavor = "multi_thread"))]
 async fn http_outbound_request_post() -> Result<()> {
     let server = Server::http1()?;
diff --git a/crates/wasi-http/tests/all/sync.rs b/crates/wasi-http/tests/all/sync.rs
index 6560927c3..7b61ed9b7 100644
--- a/crates/wasi-http/tests/all/sync.rs
+++ b/crates/wasi-http/tests/all/sync.rs
@@ -24,6 +24,12 @@ fn http_outbound_request_get() -> Result<()> {
     run(HTTP_OUTBOUND_REQUEST_GET_COMPONENT, &server)
 }
 
+#[test_log::test]
+fn http_outbound_request_timeout() -> Result<()> {
+    let server = Server::http1()?;
+    run(HTTP_OUTBOUND_REQUEST_TIMEOUT_COMPONENT, &server)
+}
+
 #[test_log::test]
 fn http_outbound_request_post() -> Result<()> {
     let server = Server::http1()?;
diff --git a/crates/wasi/tests/all/api.rs b/crates/wasi/tests/all/api.rs
index 2fc594697..80c93b8d7 100644
--- a/crates/wasi/tests/all/api.rs
+++ b/crates/wasi/tests/all/api.rs
@@ -125,21 +125,7 @@ fn api_proxy_streaming() {}
 wasmtime::component::bindgen!({
     world: "test-reactor",
     async: true,
-    with: {
-       "wasi:io/streams": wasmtime_wasi::bindings::io::streams,
-       "wasi:filesystem/types": wasmtime_wasi::bindings::filesystem::types,
-       "wasi:filesystem/preopens": wasmtime_wasi::bindings::filesystem::preopens,
-       "wasi:cli/environment": wasmtime_wasi::bindings::cli::environment,
-       "wasi:cli/exit": wasmtime_wasi::bindings::cli::exit,
-       "wasi:cli/stdin": wasmtime_wasi::bindings::cli::stdin,
-       "wasi:cli/stdout": wasmtime_wasi::bindings::cli::stdout,
-       "wasi:cli/stderr": wasmtime_wasi::bindings::cli::stderr,
-       "wasi:cli/terminal_input": wasmtime_wasi::bindings::cli::terminal_input,
-       "wasi:cli/terminal_output": wasmtime_wasi::bindings::cli::terminal_output,
-       "wasi:cli/terminal_stdin": wasmtime_wasi::bindings::cli::terminal_stdin,
-       "wasi:cli/terminal_stdout": wasmtime_wasi::bindings::cli::terminal_stdout,
-       "wasi:cli/terminal_stderr": wasmtime_wasi::bindings::cli::terminal_stderr,
-    },
+    with: { "wasi": wasmtime_wasi::bindings },
     ownership: Borrowing {
         duplicate_if_necessary: false
     }