Repository: bytecodealliance/wasmtime
Pull request #7444 (2023-11-01 15:54:46)

add `reset_adapter_state` export to adapter
This is useful as the last step of component pre-initialization with e.g. [component-init](https://github.com/dicej/component-init), in which case we want the adapter to forget about any open handles it has (and force it to re-request the stdio handles next time they're needed) since it will be talking to a brand new host at runtime.



Patch:
diff --git a/cranelift/codegen/src/opts/shifts.isle b/cranelift/codegen/src/opts/shifts.isle
index 8dcf153d1..1b33d89d8 100644
--- a/cranelift/codegen/src/opts/shifts.isle
+++ b/cranelift/codegen/src/opts/shifts.isle
@@ -102,3 +102,45 @@
 (rule (simplify (ineg ty (ushr ty x sconst @ (iconst ty (u64_from_imm64 shift_amt)))))
       (if-let $true (u64_eq shift_amt (u64_sub (ty_bits ty) 1)))
       (sshr ty x sconst))
+
+;; Shifts and rotates allow a different type for the shift amount, so we
+;; can remove any extend/reduce operations on the shift amount.
+;;
+;; (op x (ireduce y)) == (op x y)
+;; (op x (uextend y)) == (op x y)
+;; (op x (sextend y)) == (op x y)
+;;
+;; where `op` is one of ishl, ushr, sshr, rotl, rotr
+;;
+;; TODO: This rule is restricted to <=64 bits for ireduce since the x86
+;; backend doesn't support SIMD shifts with 128-bit shift amounts.
+
+(rule (simplify (ishl ty x (ireduce _ y @ (value_type (fits_in_64 _))))) (ishl ty x y))
+(rule (simplify (ishl ty x (uextend _ y))) (ishl ty x y))
+(rule (simplify (ishl ty x (sextend _ y))) (ishl ty x y))
+(rule (simplify (ushr ty x (ireduce _ y @ (value_type (fits_in_64 _))))) (ushr ty x y))
+(rule (simplify (ushr ty x (uextend _ y))) (ushr ty x y))
+(rule (simplify (ushr ty x (sextend _ y))) (ushr ty x y))
+(rule (simplify (sshr ty x (ireduce _ y @ (value_type (fits_in_64 _))))) (sshr ty x y))
+(rule (simplify (sshr ty x (uextend _ y))) (sshr ty x y))
+(rule (simplify (sshr ty x (sextend _ y))) (sshr ty x y))
+(rule (simplify (rotr ty x (ireduce _ y @ (value_type (fits_in_64 _))))) (rotr ty x y))
+(rule (simplify (rotr ty x (uextend _ y))) (rotr ty x y))
+(rule (simplify (rotr ty x (sextend _ y))) (rotr ty x y))
+(rule (simplify (rotl ty x (ireduce _ y @ (value_type (fits_in_64 _))))) (rotl ty x y))
+(rule (simplify (rotl ty x (uextend _ y))) (rotl ty x y))
+(rule (simplify (rotl ty x (sextend _ y))) (rotl ty x y))
+
+;; Remove iconcat from the shift amount input. This is correct even if the
+;; the iconcat is i8 type, since it can represent the largest shift amount
+;; for i128 types.
+;;
+;; (op x (iconcat y1 y2)) == (op x y1)
+;;
+;; where `op` is one of ishl, ushr, sshr, rotl, rotr
+
+(rule (simplify (ishl ty x (iconcat _ y _))) (ishl ty x y))
+(rule (simplify (ushr ty x (iconcat _ y _))) (ushr ty x y))
+(rule (simplify (sshr ty x (iconcat _ y _))) (sshr ty x y))
+(rule (simplify (rotr ty x (iconcat _ y _))) (rotr ty x y))
+(rule (simplify (rotl ty x (iconcat _ y _))) (rotl ty x y))
diff --git a/crates/runtime/src/mpk/pkru.rs b/crates/runtime/src/mpk/pkru.rs
index 99dee74fa..9e36dd35a 100644
--- a/crates/runtime/src/mpk/pkru.rs
+++ b/crates/runtime/src/mpk/pkru.rs
@@ -56,6 +56,11 @@ pub fn write(pkru: u32) {
 /// Check the `ECX.PKU` flag (bit 3) of the `07h` `CPUID` leaf; see the
 /// Intel Software Development Manual, vol 3a, section 2.7.
 pub fn has_cpuid_bit_set() -> bool {
+    // TODO: disable MPK support until the following issue is resolved:
+    // https://github.com/bytecodealliance/wasmtime/issues/7445
+    if true {
+        return false;
+    }
     let result = unsafe { std::arch::x86_64::__cpuid(0x07) };
     (result.ecx & 0b100) != 0
 }
diff --git a/crates/wasi-common/cap-std-sync/src/stdio.rs b/crates/wasi-common/cap-std-sync/src/stdio.rs
index b2723d076..af4d52faa 100644
--- a/crates/wasi-common/cap-std-sync/src/stdio.rs
+++ b/crates/wasi-common/cap-std-sync/src/stdio.rs
@@ -127,7 +127,12 @@ macro_rules! wasi_file_write_impl {
                 Ok(FdFlags::APPEND)
             }
             async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result<u64, Error> {
-                let n = self.0.lock().write_vectored(bufs)?;
+                let mut io = self.0.lock();
+                let n = io.write_vectored(bufs)?;
+                // On a successful write additionally flush out the bytes to
+                // handle stdio buffering done by libstd since WASI interfaces
+                // here aren't buffered.
+                io.flush()?;
                 Ok(n.try_into().map_err(|_| {
                     Error::range().context("converting write_vectored total length")
                 })?)
diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs
index dc41a30c6..e962ff47c 100644
--- a/crates/wasi-http/src/types.rs
+++ b/crates/wasi-http/src/types.rs
@@ -289,7 +289,7 @@ pub struct HostIncomingResponse {
 }
 
 pub struct HostOutgoingResponse {
-    pub status: u16,
+    pub status: http::StatusCode,
     pub headers: FieldMap,
     pub body: Option<HyperOutgoingBody>,
 }
diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs
index 2f7612af1..bce0384ab 100644
--- a/crates/wasi-http/src/types_impl.rs
+++ b/crates/wasi-http/src/types_impl.rs
@@ -632,13 +632,12 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostIncomingBody for T {
 impl<T: WasiHttpView> crate::bindings::http::types::HostOutgoingResponse for T {
     fn new(
         &mut self,
-        status: StatusCode,
         headers: Resource<Headers>,
     ) -> wasmtime::Result<Resource<HostOutgoingResponse>> {
         let fields = move_fields(self.table(), headers)?;
 
         let id = self.table().push(HostOutgoingResponse {
-            status,
+            status: http::StatusCode::OK,
             headers: fields,
             body: None,
         })?;
@@ -669,16 +668,26 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostOutgoingResponse for T {
         &mut self,
         id: Resource<HostOutgoingResponse>,
     ) -> wasmtime::Result<types::StatusCode> {
-        Ok(self.table().get(&id)?.status)
+        Ok(self.table().get(&id)?.status.into())
     }
 
     fn set_status_code(
         &mut self,
         id: Resource<HostOutgoingResponse>,
         status: types::StatusCode,
-    ) -> wasmtime::Result<()> {
-        self.table().get_mut(&id)?.status = status;
-        Ok(())
+    ) -> wasmtime::Result<Result<(), Error>> {
+        let resp = self.table().get_mut(&id)?;
+
+        match http::StatusCode::from_u16(status) {
+            Ok(status) => resp.status = status,
+            Err(_) => {
+                return Ok(Err(Error::UnexpectedError(
+                    "Invalid status code".to_string(),
+                )))
+            }
+        };
+
+        Ok(Ok(()))
     }
 
     fn headers(
diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit
index 9a0c8956b..81166b011 100644
--- a/crates/wasi-http/wit/deps/http/types.wit
+++ b/crates/wasi-http/wit/deps/http/types.wit
@@ -351,16 +351,18 @@ interface types {
   /// Represents an outgoing HTTP Response.
   resource outgoing-response {
 
-    /// Construct an `outgoing-response`.
+    /// Construct an `outgoing-response`, with a default `status-code` of `200`.
+    /// If a different `status-code` is needed, it must be set via the
+    /// `set-status-code` method.
     ///
-    /// * `status-code` is the HTTP Status Code for the Response.
     /// * `headers` is the HTTP Headers for the Response.
-    constructor(status-code: status-code, headers: headers);
+    constructor(headers: headers);
 
     /// Get the HTTP Status Code for the Response.
     status-code: func() -> status-code;
+
     /// Set the HTTP Status Code for the Response.
-    set-status-code: func(status-code: status-code);
+    set-status-code: func(status-code: status-code) -> result<_, error>;
 
     /// Get the headers associated with the Request.
     ///
diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs
index 66d56da23..e9f5c6f0f 100644
--- a/crates/wasi-preview1-component-adapter/src/lib.rs
+++ b/crates/wasi-preview1-component-adapter/src/lib.rs
@@ -94,6 +94,14 @@ impl<T, E> TrappingUnwrap<T> for Result<T, E> {
     }
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn reset_adapter_state() {
+    let state = get_state_ptr();
+    if !state.is_null() {
+        State::init(state)
+    }
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn cabi_import_realloc(
     old_ptr: *mut u8,
@@ -2385,8 +2393,8 @@ enum AllocationState {
 
 #[allow(improper_ctypes)]
 extern "C" {
-    fn get_state_ptr() -> *const State;
-    fn set_state_ptr(state: *const State);
+    fn get_state_ptr() -> *mut State;
+    fn set_state_ptr(state: *mut State);
     fn get_allocation_state() -> AllocationState;
     fn set_allocation_state(state: AllocationState);
 }
@@ -2415,7 +2423,7 @@ impl State {
     }
 
     #[cold]
-    fn new() -> &'static State {
+    fn new() -> *mut State {
         #[link(wasm_import_module = "__main_module__")]
         extern "C" {
             fn cabi_realloc(
@@ -2445,31 +2453,37 @@ impl State {
         unsafe { set_allocation_state(AllocationState::StateAllocated) };
 
         unsafe {
-            ret.write(State {
-                magic1: MAGIC,
-                magic2: MAGIC,
-                import_alloc: ImportAlloc::new(),
-                descriptors: RefCell::new(None),
-                path_buf: UnsafeCell::new(MaybeUninit::uninit()),
-                long_lived_arena: BumpArena::new(),
-                args: Cell::new(None),
-                env_vars: Cell::new(None),
-                dirent_cache: DirentCache {
-                    stream: Cell::new(None),
-                    for_fd: Cell::new(0),
-                    cookie: Cell::new(wasi::DIRCOOKIE_START),
-                    cached_dirent: Cell::new(wasi::Dirent {
-                        d_next: 0,
-                        d_ino: 0,
-                        d_type: FILETYPE_UNKNOWN,
-                        d_namlen: 0,
-                    }),
-                    path_data: UnsafeCell::new(MaybeUninit::uninit()),
-                },
-                dotdot: [UnsafeCell::new(b'.'), UnsafeCell::new(b'.')],
-            });
-            &*ret
+            Self::init(ret);
         }
+
+        ret
+    }
+
+    #[cold]
+    unsafe fn init(state: *mut State) {
+        state.write(State {
+            magic1: MAGIC,
+            magic2: MAGIC,
+            import_alloc: ImportAlloc::new(),
+            descriptors: RefCell::new(None),
+            path_buf: UnsafeCell::new(MaybeUninit::uninit()),
+            long_lived_arena: BumpArena::new(),
+            args: Cell::new(None),
+            env_vars: Cell::new(None),
+            dirent_cache: DirentCache {
+                stream: Cell::new(None),
+                for_fd: Cell::new(0),
+                cookie: Cell::new(wasi::DIRCOOKIE_START),
+                cached_dirent: Cell::new(wasi::Dirent {
+                    d_next: 0,
+                    d_ino: 0,
+                    d_type: FILETYPE_UNKNOWN,
+                    d_namlen: 0,
+                }),
+                path_data: UnsafeCell::new(MaybeUninit::uninit()),
+            },
+            dotdot: [UnsafeCell::new(b'.'), UnsafeCell::new(b'.')],
+        });
     }
 
     /// Accessor for the descriptors member that ensures it is properly initialized
diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit
index 9a0c8956b..81166b011 100644
--- a/crates/wasi/wit/deps/http/types.wit
+++ b/crates/wasi/wit/deps/http/types.wit
@@ -351,16 +351,18 @@ interface types {
   /// Represents an outgoing HTTP Response.
   resource outgoing-response {
 
-    /// Construct an `outgoing-response`.
+    /// Construct an `outgoing-response`, with a default `status-code` of `200`.
+    /// If a different `status-code` is needed, it must be set via the
+    /// `set-status-code` method.
     ///
-    /// * `status-code` is the HTTP Status Code for the Response.
     /// * `headers` is the HTTP Headers for the Response.
-    constructor(status-code: status-code, headers: headers);
+    constructor(headers: headers);
 
     /// Get the HTTP Status Code for the Response.
     status-code: func() -> status-code;
+
     /// Set the HTTP Status Code for the Response.
-    set-status-code: func(status-code: status-code);
+    set-status-code: func(status-code: status-code) -> result<_, error>;
 
     /// Get the headers associated with the Request.
     ///
diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs
index e44bdc4fc..98f7723c6 100644
--- a/winch/codegen/src/isa/x64/asm.rs
+++ b/winch/codegen/src/isa/x64/asm.rs
@@ -773,7 +773,10 @@ impl Assembler {
     }
 
     pub fn popcnt(&mut self, src: Reg, size: OperandSize) {
-        assert!(self.isa_flags.has_popcnt(), "Requires has_popcnt flag");
+        assert!(
+            self.isa_flags.has_popcnt() && self.isa_flags.has_sse42(),
+            "Requires has_popcnt and has_sse42 flags"
+        );
         self.emit(Inst::UnaryRmR {
             size: size.into(),
             op: args::UnaryRmROpcode::Popcnt,
diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs
index f28ab3196..e534b040a 100644
--- a/winch/codegen/src/isa/x64/masm.rs
+++ b/winch/codegen/src/isa/x64/masm.rs
@@ -799,7 +799,7 @@ impl Masm for MacroAssembler {
 
     fn popcnt(&mut self, context: &mut CodeGenContext, size: OperandSize) {
         let src = context.pop_to_reg(self, None);
-        if self.flags.has_popcnt() {
+        if self.flags.has_popcnt() && self.flags.has_sse42() {
             self.asm.popcnt(src.into(), size);
             context.stack.push(src.into());
         } else {


Test patch:
diff --git a/cranelift/filetests/filetests/egraph/shifts.clif b/cranelift/filetests/filetests/egraph/shifts.clif
index b8028c50e..7852ada12 100644
--- a/cranelift/filetests/filetests/egraph/shifts.clif
+++ b/cranelift/filetests/filetests/egraph/shifts.clif
@@ -315,3 +315,212 @@ block0(v0: i64):
     ; check: v8 = uextend.i64 v7
     ; check: return v8
 }
+
+function %ishl_amt_type_ireduce(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = ireduce.i8 v1
+    v3 = ishl.i8 v0, v2
+    return v3
+}
+
+; check: v4 = ishl v0, v1
+; check: return v4
+
+function %ishl_amt_type_sextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = sextend.i32 v1
+    v3 = ishl.i8 v0, v2
+    return v3
+}
+
+; check: v4 = ishl v0, v1
+; check: return v4
+
+function %ishl_amt_type_uextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = uextend.i32 v1
+    v3 = ishl.i8 v0, v2
+    return v3
+}
+
+; check: v4 = ishl v0, v1
+; check: return v4
+
+
+function %ushr_amt_type_ireduce(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = ireduce.i8 v1
+    v3 = ushr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = ushr v0, v1
+; check: return v4
+
+function %ushr_amt_type_sextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = sextend.i32 v1
+    v3 = ushr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = ushr v0, v1
+; check: return v4
+
+function %ushr_amt_type_uextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = uextend.i32 v1
+    v3 = ushr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = ushr v0, v1
+; check: return v4
+
+
+function %sshr_amt_type_ireduce(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = ireduce.i8 v1
+    v3 = sshr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = sshr v0, v1
+; check: return v4
+
+function %sshr_amt_type_sextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = sextend.i32 v1
+    v3 = sshr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = sshr v0, v1
+; check: return v4
+
+function %sshr_amt_type_uextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = uextend.i32 v1
+    v3 = sshr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = sshr v0, v1
+; check: return v4
+
+
+function %rotr_amt_type_ireduce(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = ireduce.i8 v1
+    v3 = rotr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = rotr v0, v1
+; check: return v4
+
+function %rotr_amt_type_sextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = sextend.i32 v1
+    v3 = rotr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = rotr v0, v1
+; check: return v4
+
+function %rotr_amt_type_uextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = uextend.i32 v1
+    v3 = rotr.i8 v0, v2
+    return v3
+}
+
+; check: v4 = rotr v0, v1
+; check: return v4
+
+
+function %rotl_amt_type_ireduce(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = ireduce.i8 v1
+    v3 = rotl.i8 v0, v2
+    return v3
+}
+
+; check: v4 = rotl v0, v1
+; check: return v4
+
+function %rotl_amt_type_sextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = sextend.i32 v1
+    v3 = rotl.i8 v0, v2
+    return v3
+}
+
+; check: v4 = rotl v0, v1
+; check: return v4
+
+function %rotl_amt_type_uextend(i8, i16) -> i8 {
+block0(v0: i8, v1: i16):
+    v2 = uextend.i32 v1
+    v3 = rotl.i8 v0, v2
+    return v3
+}
+
+; check: v4 = rotl v0, v1
+; check: return v4
+
+
+function %ishl_amt_type_iconcat(i8, i16, i16) -> i8 {
+block0(v0: i8, v1: i16, v2: i16):
+    v3 = iconcat.i16 v1, v2
+    v4 = ishl.i8 v0, v3
+    return v4
+}
+
+; check: v5 = ishl v0, v1
+; check: return v5
+
+
+function %ushr_amt_type_iconcat(i8, i16, i16) -> i8 {
+block0(v0: i8, v1: i16, v2: i16):
+    v3 = iconcat.i16 v1, v2
+    v4 = ushr.i8 v0, v3
+    return v4
+}
+
+; check: v5 = ushr v0, v1
+; check: return v5
+
+
+function %sshr_amt_type_iconcat(i8, i16, i16) -> i8 {
+block0(v0: i8, v1: i16, v2: i16):
+    v3 = iconcat.i16 v1, v2
+    v4 = sshr.i8 v0, v3
+    return v4
+}
+
+; check: v5 = sshr v0, v1
+; check: return v5
+
+
+function %rotr_amt_type_iconcat(i8, i16, i16) -> i8 {
+block0(v0: i8, v1: i16, v2: i16):
+    v3 = iconcat.i16 v1, v2
+    v4 = rotr.i8 v0, v3
+    return v4
+}
+
+; check: v5 = rotr v0, v1
+; check: return v5
+
+
+function %rotl_amt_type_iconcat(i8, i16, i16) -> i8 {
+block0(v0: i8, v1: i16, v2: i16):
+    v3 = iconcat.i16 v1, v2
+    v4 = rotl.i8 v0, v3
+    return v4
+}
+
+; check: v5 = rotl v0, v1
+; check: return v5
diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs
index 1bcdc783d..2170bdbbc 100644
--- a/crates/test-programs/src/bin/api_proxy.rs
+++ b/crates/test-programs/src/bin/api_proxy.rs
@@ -17,7 +17,7 @@ struct T;
 impl bindings::exports::wasi::http::incoming_handler::Guest for T {
     fn handle(_request: IncomingRequest, outparam: ResponseOutparam) {
         let hdrs = bindings::wasi::http::types::Headers::new();
-        let resp = bindings::wasi::http::types::OutgoingResponse::new(200, hdrs);
+        let resp = bindings::wasi::http::types::OutgoingResponse::new(hdrs);
         let body = resp.body().expect("outgoing response");
 
         bindings::wasi::http::types::ResponseOutparam::set(outparam, Ok(resp));
diff --git a/crates/test-programs/src/bin/api_proxy_streaming.rs b/crates/test-programs/src/bin/api_proxy_streaming.rs
index 5f1c0560c..730bef938 100644
--- a/crates/test-programs/src/bin/api_proxy_streaming.rs
+++ b/crates/test-programs/src/bin/api_proxy_streaming.rs
@@ -53,7 +53,6 @@ async fn handle_request(request: IncomingRequest, response_out: ResponseOutparam
             let mut results = stream::iter(results).buffer_unordered(MAX_CONCURRENCY);
 
             let response = OutgoingResponse::new(
-                200,
                 Fields::from_list(&[("content-type".to_string(), b"text/plain".to_vec())]).unwrap(),
             );
 
@@ -79,7 +78,6 @@ async fn handle_request(request: IncomingRequest, response_out: ResponseOutparam
             // Echo the request body without buffering it.
 
             let response = OutgoingResponse::new(
-                200,
                 Fields::from_list(
                     &headers
                         .into_iter()
@@ -129,7 +127,6 @@ async fn handle_request(request: IncomingRequest, response_out: ResponseOutparam
                         );
 
                         let response = OutgoingResponse::new(
-                            200,
                             Fields::from_list(
                                 &headers
                                     .into_iter()
@@ -242,7 +239,10 @@ fn method_not_allowed(response_out: ResponseOutparam) {
 }
 
 fn respond(status: u16, response_out: ResponseOutparam) {
-    let response = OutgoingResponse::new(status, Fields::new());
+    let response = OutgoingResponse::new(Fields::new());
+    response
+        .set_status_code(status)
+        .expect("setting status code");
 
     let body = response.body().expect("response should be writable");
 
diff --git a/crates/test-programs/src/bin/cli_stdio_write_flushes.rs b/crates/test-programs/src/bin/cli_stdio_write_flushes.rs
new file mode 100644
index 000000000..64d0ccdc6
--- /dev/null
+++ b/crates/test-programs/src/bin/cli_stdio_write_flushes.rs
@@ -0,0 +1,10 @@
+use std::io::Write;
+
+fn main() {
+    print!("> ");
+    std::io::stdout().flush().unwrap();
+
+    let mut s = String::new();
+    std::io::stdin().read_line(&mut s).unwrap();
+    assert!(s.is_empty());
+}
diff --git a/crates/test-programs/src/bin/http_outbound_request_response_build.rs b/crates/test-programs/src/bin/http_outbound_request_response_build.rs
index 8a50ff6fa..c2360d299 100644
--- a/crates/test-programs/src/bin/http_outbound_request_response_build.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_response_build.rs
@@ -27,7 +27,7 @@ fn main() {
             "application/text".to_string().into_bytes(),
         )])
         .unwrap();
-        let response = http_types::OutgoingResponse::new(200, headers);
+        let response = http_types::OutgoingResponse::new(headers);
         let outgoing_body = response.body().unwrap();
         let response_body = outgoing_body.write().unwrap();
         response_body
diff --git a/tests/all/cli_tests.rs b/tests/all/cli_tests.rs
index a3113e2da..ff336feb8 100644
--- a/tests/all/cli_tests.rs
+++ b/tests/all/cli_tests.rs
@@ -1198,7 +1198,7 @@ fn float_args() -> Result<()> {
 mod test_programs {
     use super::{get_wasmtime_command, run_wasmtime};
     use anyhow::Result;
-    use std::io::Write;
+    use std::io::{Read, Write};
     use std::process::Stdio;
     use test_programs_artifacts::*;
 
@@ -1449,4 +1449,40 @@ mod test_programs {
         assert!(output.status.success());
         Ok(())
     }
+
+    // Test to ensure that prints in the guest aren't buffered on the host by
+    // accident. The test here will print something without a newline and then
+    // wait for input on stdin, and the test here is to ensure that the
+    // character shows up here even as the guest is waiting on input via stdin.
+    #[test]
+    fn cli_stdio_write_flushes() -> Result<()> {
+        fn run(args: &[&str]) -> Result<()> {
+            println!("running {args:?}");
+            let mut child = get_wasmtime_command()?
+                .args(args)
+                .stdin(Stdio::piped())
+                .stdout(Stdio::piped())
+                .spawn()?;
+            let mut stdout = child.stdout.take().unwrap();
+            let mut buf = [0; 10];
+            match stdout.read(&mut buf) {
+                Ok(2) => assert_eq!(&buf[..2], b"> "),
+                e => panic!("unexpected read result {e:?}"),
+            }
+            drop(stdout);
+            drop(child.stdin.take().unwrap());
+            let status = child.wait()?;
+            assert!(status.success());
+            Ok(())
+        }
+
+        run(&["run", "-Spreview2=n", CLI_STDIO_WRITE_FLUSHES])?;
+        run(&["run", "-Spreview2=y", CLI_STDIO_WRITE_FLUSHES])?;
+        run(&[
+            "run",
+            "-Wcomponent-model",
+            CLI_STDIO_WRITE_FLUSHES_COMPONENT,
+        ])?;
+        Ok(())
+    }
 }
diff --git a/winch/filetests/filetests/x64/i32_popcnt/no_sse42.wat b/winch/filetests/filetests/x64/i32_popcnt/no_sse42.wat
new file mode 100644
index 000000000..dc3567e84
--- /dev/null
+++ b/winch/filetests/filetests/x64/i32_popcnt/no_sse42.wat
@@ -0,0 +1,33 @@
+;;! target = "x86_64"
+;;! flags = ["has_popcnt"]
+
+(module
+    (func (result i32)
+      i32.const 3
+      i32.popcnt
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 b803000000           	mov	eax, 3
+;;   11:	 89c1                 	mov	ecx, eax
+;;   13:	 c1e801               	shr	eax, 1
+;;   16:	 81e055555555         	and	eax, 0x55555555
+;;   1c:	 29c1                 	sub	ecx, eax
+;;   1e:	 89c8                 	mov	eax, ecx
+;;   20:	 41bb33333333         	mov	r11d, 0x33333333
+;;   26:	 4421d8               	and	eax, r11d
+;;   29:	 c1e902               	shr	ecx, 2
+;;   2c:	 4421d9               	and	ecx, r11d
+;;   2f:	 01c1                 	add	ecx, eax
+;;   31:	 89c8                 	mov	eax, ecx
+;;   33:	 c1e804               	shr	eax, 4
+;;   36:	 01c8                 	add	eax, ecx
+;;   38:	 81e00f0f0f0f         	and	eax, 0xf0f0f0f
+;;   3e:	 69c001010101         	imul	eax, eax, 0x1010101
+;;   44:	 c1e818               	shr	eax, 0x18
+;;   47:	 4883c408             	add	rsp, 8
+;;   4b:	 5d                   	pop	rbp
+;;   4c:	 c3                   	ret	
diff --git a/winch/filetests/filetests/x64/i64_popcnt/no_sse42.wat b/winch/filetests/filetests/x64/i64_popcnt/no_sse42.wat
new file mode 100644
index 000000000..7430a14ff
--- /dev/null
+++ b/winch/filetests/filetests/x64/i64_popcnt/no_sse42.wat
@@ -0,0 +1,40 @@
+;;! target = "x86_64"
+;;! flags = ["has_popcnt"]
+
+(module
+    (func (result i64)
+      i64.const 3
+      i64.popcnt
+    )
+)
+;;    0:	 55                   	push	rbp
+;;    1:	 4889e5               	mov	rbp, rsp
+;;    4:	 4883ec08             	sub	rsp, 8
+;;    8:	 4c893424             	mov	qword ptr [rsp], r14
+;;    c:	 48c7c003000000       	mov	rax, 3
+;;   13:	 4889c1               	mov	rcx, rax
+;;   16:	 48c1e801             	shr	rax, 1
+;;   1a:	 49bb5555555555555555 	
+;; 				movabs	r11, 0x5555555555555555
+;;   24:	 4c21d8               	and	rax, r11
+;;   27:	 4829c1               	sub	rcx, rax
+;;   2a:	 4889c8               	mov	rax, rcx
+;;   2d:	 49bb3333333333333333 	
+;; 				movabs	r11, 0x3333333333333333
+;;   37:	 4c21d8               	and	rax, r11
+;;   3a:	 48c1e902             	shr	rcx, 2
+;;   3e:	 4c21d9               	and	rcx, r11
+;;   41:	 4801c1               	add	rcx, rax
+;;   44:	 4889c8               	mov	rax, rcx
+;;   47:	 48c1e804             	shr	rax, 4
+;;   4b:	 4801c8               	add	rax, rcx
+;;   4e:	 49bb0f0f0f0f0f0f0f0f 	
+;; 				movabs	r11, 0xf0f0f0f0f0f0f0f
+;;   58:	 4c21d8               	and	rax, r11
+;;   5b:	 49bb0101010101010101 	
+;; 				movabs	r11, 0x101010101010101
+;;   65:	 490fafc3             	imul	rax, r11
+;;   69:	 48c1e838             	shr	rax, 0x38
+;;   6d:	 4883c408             	add	rsp, 8
+;;   71:	 5d                   	pop	rbp
+;;   72:	 c3                   	ret