Repository: bytecodealliance/wasmtime
Pull request #6939 (2023-08-31 11:28:45)

cranelift-interpreter: Fix SIMD shifts and rotates
👋 Hey,

This PR fixes the `ishl`/`{s,u}shr`/`rot{r,l}` instructions for SIMD in the interpreter. The main issue is that while the LHS is a vector the RHS is a scalar and by default we perform the operations lanewise. The solution is to splat the RHS into a similar sized vector and perform the normal operation individually.

This PR also enables these operations in fuzzgen. As well as `ineg` (fixed in #6844) and `bitselect` with floats (which was working previously)


Patch:
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 15e784433..216adce67 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -525,6 +525,8 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::Cls, &[I32], &[I32]),
                 (Opcode::Cls, &[I64], &[I64]),
                 (Opcode::Cls, &[I128], &[I128]),
+                // TODO
+                (Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4897
                 // https://github.com/bytecodealliance/wasmtime/issues/4899
                 (
@@ -589,6 +591,15 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                     &[],
                     &[I8X16 | I16X8 | I32X4 | I64X2 | F32X4 | F64X2]
                 ),
+                // TODO
+                (
+                    Opcode::Sshr | Opcode::Ushr | Opcode::Ishl,
+                    &[I8X16 | I16X8 | I32X4 | I64X2, I128]
+                ),
+                (
+                    Opcode::Rotr | Opcode::Rotl,
+                    &[I8X16 | I16X8 | I32X4 | I64X2, _]
+                ),
             )
         }
 
@@ -647,6 +658,17 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 // https://github.com/bytecodealliance/wasmtime/issues/6104
                 (Opcode::Bitcast, &[I128], &[_]),
                 (Opcode::Bitcast, &[_], &[I128]),
+                // TODO
+                (
+                    Opcode::Sshr | Opcode::Ushr | Opcode::Ishl,
+                    &[I8X16 | I16X8 | I32X4 | I64X2, I128]
+                ),
+                (
+                    Opcode::Rotr | Opcode::Rotl,
+                    &[I8X16 | I16X8 | I32X4 | I64X2, _]
+                ),
+                // TODO
+                (Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
             )
         }
 
@@ -689,6 +711,8 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 // https://github.com/bytecodealliance/wasmtime/issues/6104
                 (Opcode::Bitcast, &[I128], &[_]),
                 (Opcode::Bitcast, &[_], &[I128]),
+                // TODO
+                (Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
             )
         }
 
@@ -737,8 +761,9 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::Bitcast, &[I128], &[_]),
                 (Opcode::Bitcast, &[_], &[I128]),
                 // TODO
-                (Opcode::SelectSpectreGuard, &[_, _, _], &[F32]),
-                (Opcode::SelectSpectreGuard, &[_, _, _], &[F64]),
+                (Opcode::SelectSpectreGuard, &[_, _, _], &[F32 | F64]),
+                // TODO
+                (Opcode::Bitselect, &[_, _, _], &[F32 | F64]),
             )
         }
 
@@ -896,10 +921,6 @@ static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| {
                 (Opcode::ScalarToVector),
                 (Opcode::X86Pmaddubsw),
                 (Opcode::X86Cvtt2dq),
-                (Opcode::Bitselect, &[F32, F32, F32], &[F32]),
-                (Opcode::Bitselect, &[F64, F64, F64], &[F64]),
-                (Opcode::Bitselect, &[F32X4, F32X4, F32X4], &[F32X4]),
-                (Opcode::Bitselect, &[F64X2, F64X2, F64X2], &[F64X2]),
                 (Opcode::VanyTrue, &[F32X4], &[I8]),
                 (Opcode::VanyTrue, &[F64X2], &[I8]),
                 (Opcode::VhighBits, &[F32X4], &[I8]),
@@ -952,10 +973,6 @@ static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| {
                 (Opcode::VhighBits, &[I64X2], &[I64X2]),
                 (Opcode::VhighBits, &[F32X4], &[I64X2]),
                 (Opcode::VhighBits, &[F64X2], &[I64X2]),
-                (Opcode::Ineg, &[I8X16], &[I8X16]),
-                (Opcode::Ineg, &[I16X8], &[I16X8]),
-                (Opcode::Ineg, &[I32X4], &[I32X4]),
-                (Opcode::Ineg, &[I64X2], &[I64X2]),
                 (Opcode::Umulhi, &[I128, I128], &[I128]),
                 (Opcode::Smulhi, &[I128, I128], &[I128]),
                 // https://github.com/bytecodealliance/wasmtime/issues/6073
@@ -966,106 +983,6 @@ static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| {
                 (Opcode::Isplit, &[I64], &[I32, I32]),
                 (Opcode::Isplit, &[I32], &[I16, I16]),
                 (Opcode::Isplit, &[I16], &[I8, I8]),
-                (Opcode::Rotl, &[I8X16, I8], &[I8X16]),
-                (Opcode::Rotl, &[I8X16, I16], &[I8X16]),
-                (Opcode::Rotl, &[I8X16, I32], &[I8X16]),
-                (Opcode::Rotl, &[I8X16, I64], &[I8X16]),
-                (Opcode::Rotl, &[I8X16, I128], &[I8X16]),
-                (Opcode::Rotl, &[I16X8, I8], &[I16X8]),
-                (Opcode::Rotl, &[I16X8, I16], &[I16X8]),
-                (Opcode::Rotl, &[I16X8, I32], &[I16X8]),
-                (Opcode::Rotl, &[I16X8, I64], &[I16X8]),
-                (Opcode::Rotl, &[I16X8, I128], &[I16X8]),
-                (Opcode::Rotl, &[I32X4, I8], &[I32X4]),
-                (Opcode::Rotl, &[I32X4, I16], &[I32X4]),
-                (Opcode::Rotl, &[I32X4, I32], &[I32X4]),
-                (Opcode::Rotl, &[I32X4, I64], &[I32X4]),
-                (Opcode::Rotl, &[I32X4, I128], &[I32X4]),
-                (Opcode::Rotl, &[I64X2, I8], &[I64X2]),
-                (Opcode::Rotl, &[I64X2, I16], &[I64X2]),
-                (Opcode::Rotl, &[I64X2, I32], &[I64X2]),
-                (Opcode::Rotl, &[I64X2, I64], &[I64X2]),
-                (Opcode::Rotl, &[I64X2, I128], &[I64X2]),
-                (Opcode::Rotr, &[I8X16, I8], &[I8X16]),
-                (Opcode::Rotr, &[I8X16, I16], &[I8X16]),
-                (Opcode::Rotr, &[I8X16, I32], &[I8X16]),
-                (Opcode::Rotr, &[I8X16, I64], &[I8X16]),
-                (Opcode::Rotr, &[I8X16, I128], &[I8X16]),
-                (Opcode::Rotr, &[I16X8, I8], &[I16X8]),
-                (Opcode::Rotr, &[I16X8, I16], &[I16X8]),
-                (Opcode::Rotr, &[I16X8, I32], &[I16X8]),
-                (Opcode::Rotr, &[I16X8, I64], &[I16X8]),
-                (Opcode::Rotr, &[I16X8, I128], &[I16X8]),
-                (Opcode::Rotr, &[I32X4, I8], &[I32X4]),
-                (Opcode::Rotr, &[I32X4, I16], &[I32X4]),
-                (Opcode::Rotr, &[I32X4, I32], &[I32X4]),
-                (Opcode::Rotr, &[I32X4, I64], &[I32X4]),
-                (Opcode::Rotr, &[I32X4, I128], &[I32X4]),
-                (Opcode::Rotr, &[I64X2, I8], &[I64X2]),
-                (Opcode::Rotr, &[I64X2, I16], &[I64X2]),
-                (Opcode::Rotr, &[I64X2, I32], &[I64X2]),
-                (Opcode::Rotr, &[I64X2, I64], &[I64X2]),
-                (Opcode::Rotr, &[I64X2, I128], &[I64X2]),
-                (Opcode::Ishl, &[I8X16, I8], &[I8X16]),
-                (Opcode::Ishl, &[I8X16, I16], &[I8X16]),
-                (Opcode::Ishl, &[I8X16, I32], &[I8X16]),
-                (Opcode::Ishl, &[I8X16, I64], &[I8X16]),
-                (Opcode::Ishl, &[I8X16, I128], &[I8X16]),
-                (Opcode::Ishl, &[I16X8, I8], &[I16X8]),
-                (Opcode::Ishl, &[I16X8, I16], &[I16X8]),
-                (Opcode::Ishl, &[I16X8, I32], &[I16X8]),
-                (Opcode::Ishl, &[I16X8, I64], &[I16X8]),
-                (Opcode::Ishl, &[I16X8, I128], &[I16X8]),
-                (Opcode::Ishl, &[I32X4, I8], &[I32X4]),
-                (Opcode::Ishl, &[I32X4, I16], &[I32X4]),
-                (Opcode::Ishl, &[I32X4, I32], &[I32X4]),
-                (Opcode::Ishl, &[I32X4, I64], &[I32X4]),
-                (Opcode::Ishl, &[I32X4, I128], &[I32X4]),
-                (Opcode::Ishl, &[I64X2, I8], &[I64X2]),
-                (Opcode::Ishl, &[I64X2, I16], &[I64X2]),
-                (Opcode::Ishl, &[I64X2, I32], &[I64X2]),
-                (Opcode::Ishl, &[I64X2, I64], &[I64X2]),
-                (Opcode::Ishl, &[I64X2, I128], &[I64X2]),
-                (Opcode::Ushr, &[I8X16, I8], &[I8X16]),
-                (Opcode::Ushr, &[I8X16, I16], &[I8X16]),
-                (Opcode::Ushr, &[I8X16, I32], &[I8X16]),
-                (Opcode::Ushr, &[I8X16, I64], &[I8X16]),
-                (Opcode::Ushr, &[I8X16, I128], &[I8X16]),
-                (Opcode::Ushr, &[I16X8, I8], &[I16X8]),
-                (Opcode::Ushr, &[I16X8, I16], &[I16X8]),
-                (Opcode::Ushr, &[I16X8, I32], &[I16X8]),
-                (Opcode::Ushr, &[I16X8, I64], &[I16X8]),
-                (Opcode::Ushr, &[I16X8, I128], &[I16X8]),
-                (Opcode::Ushr, &[I32X4, I8], &[I32X4]),
-                (Opcode::Ushr, &[I32X4, I16], &[I32X4]),
-                (Opcode::Ushr, &[I32X4, I32], &[I32X4]),
-                (Opcode::Ushr, &[I32X4, I64], &[I32X4]),
-                (Opcode::Ushr, &[I32X4, I128], &[I32X4]),
-                (Opcode::Ushr, &[I64X2, I8], &[I64X2]),
-                (Opcode::Ushr, &[I64X2, I16], &[I64X2]),
-                (Opcode::Ushr, &[I64X2, I32], &[I64X2]),
-                (Opcode::Ushr, &[I64X2, I64], &[I64X2]),
-                (Opcode::Ushr, &[I64X2, I128], &[I64X2]),
-                (Opcode::Sshr, &[I8X16, I8], &[I8X16]),
-                (Opcode::Sshr, &[I8X16, I16], &[I8X16]),
-                (Opcode::Sshr, &[I8X16, I32], &[I8X16]),
-                (Opcode::Sshr, &[I8X16, I64], &[I8X16]),
-                (Opcode::Sshr, &[I8X16, I128], &[I8X16]),
-                (Opcode::Sshr, &[I16X8, I8], &[I16X8]),
-                (Opcode::Sshr, &[I16X8, I16], &[I16X8]),
-                (Opcode::Sshr, &[I16X8, I32], &[I16X8]),
-                (Opcode::Sshr, &[I16X8, I64], &[I16X8]),
-                (Opcode::Sshr, &[I16X8, I128], &[I16X8]),
-                (Opcode::Sshr, &[I32X4, I8], &[I32X4]),
-                (Opcode::Sshr, &[I32X4, I16], &[I32X4]),
-                (Opcode::Sshr, &[I32X4, I32], &[I32X4]),
-                (Opcode::Sshr, &[I32X4, I64], &[I32X4]),
-                (Opcode::Sshr, &[I32X4, I128], &[I32X4]),
-                (Opcode::Sshr, &[I64X2, I8], &[I64X2]),
-                (Opcode::Sshr, &[I64X2, I16], &[I64X2]),
-                (Opcode::Sshr, &[I64X2, I32], &[I64X2]),
-                (Opcode::Sshr, &[I64X2, I64], &[I64X2]),
-                (Opcode::Sshr, &[I64X2, I128], &[I64X2]),
                 (Opcode::Fmin, &[F32X4, F32X4], &[F32X4]),
                 (Opcode::Fmin, &[F64X2, F64X2], &[F64X2]),
                 (Opcode::Fmax, &[F32X4, F32X4], &[F32X4]),
diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs
index f13d2c918..a8ae06f90 100644
--- a/cranelift/interpreter/src/step.rs
+++ b/cranelift/interpreter/src/step.rs
@@ -795,16 +795,16 @@ where
         Opcode::BandImm => binary(DataValueExt::and, arg(0), imm_as_ctrl_ty()?)?,
         Opcode::BorImm => binary(DataValueExt::or, arg(0), imm_as_ctrl_ty()?)?,
         Opcode::BxorImm => binary(DataValueExt::xor, arg(0), imm_as_ctrl_ty()?)?,
-        Opcode::Rotl => binary(DataValueExt::rotl, arg(0), arg(1))?,
-        Opcode::Rotr => binary(DataValueExt::rotr, arg(0), arg(1))?,
-        Opcode::RotlImm => binary(DataValueExt::rotl, arg(0), imm_as_ctrl_ty()?)?,
-        Opcode::RotrImm => binary(DataValueExt::rotr, arg(0), imm_as_ctrl_ty()?)?,
-        Opcode::Ishl => binary(DataValueExt::shl, arg(0), arg(1))?,
-        Opcode::Ushr => binary(DataValueExt::ushr, arg(0), arg(1))?,
-        Opcode::Sshr => binary(DataValueExt::sshr, arg(0), arg(1))?,
-        Opcode::IshlImm => binary(DataValueExt::shl, arg(0), imm_as_ctrl_ty()?)?,
-        Opcode::UshrImm => binary(DataValueExt::ushr, arg(0), imm_as_ctrl_ty()?)?,
-        Opcode::SshrImm => binary(DataValueExt::sshr, arg(0), imm_as_ctrl_ty()?)?,
+        Opcode::Rotl => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
+        Opcode::Rotr => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
+        Opcode::RotlImm => binary(DataValueExt::rotl, arg(0), shift_amt(ctrl_ty, imm())?)?,
+        Opcode::RotrImm => binary(DataValueExt::rotr, arg(0), shift_amt(ctrl_ty, imm())?)?,
+        Opcode::Ishl => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
+        Opcode::Ushr => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
+        Opcode::Sshr => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, arg(1))?)?,
+        Opcode::IshlImm => binary(DataValueExt::shl, arg(0), shift_amt(ctrl_ty, imm())?)?,
+        Opcode::UshrImm => binary(DataValueExt::ushr, arg(0), shift_amt(ctrl_ty, imm())?)?,
+        Opcode::SshrImm => binary(DataValueExt::sshr, arg(0), shift_amt(ctrl_ty, imm())?)?,
         Opcode::Bitrev => unary(DataValueExt::reverse_bits, arg(0))?,
         Opcode::Bswap => unary(DataValueExt::swap_bytes, arg(0))?,
         Opcode::Clz => unary(DataValueExt::leading_zeros, arg(0))?,
@@ -994,13 +994,7 @@ where
             }
             assign(DataValueExt::vector(new, types::I8X16)?)
         }
-        Opcode::Splat => {
-            let mut new_vector = SimdVec::new();
-            for _ in 0..ctrl_ty.lane_count() {
-                new_vector.push(arg(0));
-            }
-            assign(vectorizelanes(&new_vector, ctrl_ty)?)
-        }
+        Opcode::Splat => assign(splat(ctrl_ty, arg(0))?),
         Opcode::Insertlane => {
             let idx = imm().into_int_unsigned()? as usize;
             let mut vector = extractlanes(&arg(0), ctrl_ty)?;
@@ -1575,3 +1569,17 @@ fn bitselect(c: DataValue, x: DataValue, y: DataValue) -> ValueResult<DataValue>
     let mask_y = DataValueExt::and(DataValueExt::not(c)?, y)?;
     DataValueExt::or(mask_x, mask_y)
 }
+
+fn splat(ty: Type, val: DataValue) -> ValueResult<DataValue> {
+    let mut new_vector = SimdVec::new();
+    for _ in 0..ty.lane_count() {
+        new_vector.push(val.clone());
+    }
+    vectorizelanes(&new_vector, ty)
+}
+
+// Prepares the shift amount for a shift/rotate operation.
+// The shift amount must be the same type and have the same number of lanes as the vector.
+fn shift_amt(ty: Type, val: DataValue) -> ValueResult<DataValue> {
+    splat(ty, val.convert(ValueConversionKind::Exact(ty.lane_type()))?)
+}
diff --git a/crates/component-macro/src/bindgen.rs b/crates/component-macro/src/bindgen.rs
index 3dfd7f846..a87616e55 100644
--- a/crates/component-macro/src/bindgen.rs
+++ b/crates/component-macro/src/bindgen.rs
@@ -1,10 +1,11 @@
 use proc_macro2::{Span, TokenStream};
 use std::collections::HashMap;
+use std::collections::HashSet;
 use std::path::{Path, PathBuf};
 use syn::parse::{Error, Parse, ParseStream, Result};
 use syn::punctuated::Punctuated;
 use syn::{braced, token, Ident, Token};
-use wasmtime_wit_bindgen::{Opts, Ownership, TrappableError};
+use wasmtime_wit_bindgen::{AsyncConfig, Opts, Ownership, TrappableError};
 use wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId};
 
 pub struct Config {
@@ -15,7 +16,7 @@ pub struct Config {
 }
 
 pub fn expand(input: &Config) -> Result<TokenStream> {
-    if !cfg!(feature = "async") && input.opts.async_ {
+    if !cfg!(feature = "async") && input.opts.async_.maybe_async() {
         return Err(Error::new(
             Span::call_site(),
             "cannot enable async bindings unless `async` crate feature is active",
@@ -45,6 +46,7 @@ impl Parse for Config {
         let mut world = None;
         let mut inline = None;
         let mut path = None;
+        let mut async_configured = false;
 
         if input.peek(token::Brace) {
             let content;
@@ -71,7 +73,13 @@ impl Parse for Config {
                         inline = Some(s.value());
                     }
                     Opt::Tracing(val) => opts.tracing = val,
-                    Opt::Async(val) => opts.async_ = val,
+                    Opt::Async(val, span) => {
+                        if async_configured {
+                            return Err(Error::new(span, "cannot specify second async config"));
+                        }
+                        async_configured = true;
+                        opts.async_ = val;
+                    }
                     Opt::TrappableErrorType(val) => opts.trappable_error_type = val,
                     Opt::Ownership(val) => opts.ownership = val,
                     Opt::Interfaces(s) => {
@@ -171,6 +179,8 @@ mod kw {
     syn::custom_keyword!(ownership);
     syn::custom_keyword!(interfaces);
     syn::custom_keyword!(with);
+    syn::custom_keyword!(except_imports);
+    syn::custom_keyword!(only_imports);
 }
 
 enum Opt {
@@ -178,7 +188,7 @@ enum Opt {
     Path(syn::LitStr),
     Inline(syn::LitStr),
     Tracing(bool),
-    Async(bool),
+    Async(AsyncConfig, Span),
     TrappableErrorType(Vec<TrappableError>),
     Ownership(Ownership),
     Interfaces(syn::LitStr),
@@ -205,9 +215,43 @@ impl Parse for Opt {
             input.parse::<Token![:]>()?;
             Ok(Opt::Tracing(input.parse::<syn::LitBool>()?.value))
         } else if l.peek(Token![async]) {
-            input.parse::<Token![async]>()?;
+            let span = input.parse::<Token![async]>()?.span;
             input.parse::<Token![:]>()?;
-            Ok(Opt::Async(input.parse::<syn::LitBool>()?.value))
+            if input.peek(syn::LitBool) {
+                match input.parse::<syn::LitBool>()?.value {
+                    true => Ok(Opt::Async(AsyncConfig::All, span)),
+                    false => Ok(Opt::Async(AsyncConfig::None, span)),
+                }
+            } else {
+                let contents;
+                syn::braced!(contents in input);
+
+                let l = contents.lookahead1();
+                let ctor: fn(HashSet<String>) -> AsyncConfig = if l.peek(kw::except_imports) {
+                    contents.parse::<kw::except_imports>()?;
+                    contents.parse::<Token![:]>()?;
+                    AsyncConfig::AllExceptImports
+                } else if l.peek(kw::only_imports) {
+                    contents.parse::<kw::only_imports>()?;
+                    contents.parse::<Token![:]>()?;
+                    AsyncConfig::OnlyImports
+                } else {
+                    return Err(l.error());
+                };
+
+                let list;
+                syn::bracketed!(list in contents);
+                let fields: Punctuated<syn::LitStr, Token![,]> =
+                    list.parse_terminated(Parse::parse, Token![,])?;
+
+                if contents.peek(Token![,]) {
+                    contents.parse::<Token![,]>()?;
+                }
+                Ok(Opt::Async(
+                    ctor(fields.iter().map(|s| s.value()).collect()),
+                    span,
+                ))
+            }
         } else if l.peek(kw::ownership) {
             input.parse::<kw::ownership>()?;
             input.parse::<Token![:]>()?;
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index 8a08b0108..d15490420 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -255,9 +255,7 @@ fn compile_module(
         Ok(module) => Some(module),
         Err(_) if !known_valid => None,
         Err(e) => {
-            if let generators::InstanceAllocationStrategy::Pooling { .. } =
-                &config.wasmtime.strategy
-            {
+            if let generators::InstanceAllocationStrategy::Pooling(c) = &config.wasmtime.strategy {
                 // When using the pooling allocator, accept failures to compile
                 // when arbitrary table element limits have been exceeded as
                 // there is currently no way to constrain the generated module
@@ -275,6 +273,23 @@ fn compile_module(
                 if string.contains("instance allocation for this module requires") {
                     return None;
                 }
+
+                // If the pooling allocator is more restrictive on the number of
+                // tables and memories than we allowed wasm-smith to generate
+                // then allow compilation errors along those lines.
+                if c.max_tables_per_module < (config.module_config.config.max_tables as u32)
+                    && string.contains("defined tables count")
+                    && string.contains("exceeds the per-instance limit")
+                {
+                    return None;
+                }
+
+                if c.max_memories_per_module < (config.module_config.config.max_memories as u32)
+                    && string.contains("defined memories count")
+                    && string.contains("exceeds the per-instance limit")
+                {
+                    return None;
+                }
             }
 
             panic!("failed to compile module: {:?}", e);
@@ -329,7 +344,7 @@ pub fn instantiate_with_dummy(store: &mut Store<StoreLimits>, module: &Module) -
     }
 
     // Also allow failures to instantiate as a result of hitting pooling limits.
-    if string.contains("maximum concurrent instance limit")
+    if string.contains("maximum concurrent core instance limit")
         || string.contains("maximum concurrent memory limit")
         || string.contains("maximum concurrent table limit")
     {
diff --git a/crates/jit/src/unwind/miri.rs b/crates/jit/src/unwind/miri.rs
index 8e7113f7b..4b4527e66 100644
--- a/crates/jit/src/unwind/miri.rs
+++ b/crates/jit/src/unwind/miri.rs
@@ -3,7 +3,7 @@ use anyhow::Result;
 pub struct UnwindRegistration {}
 
 impl UnwindRegistration {
-    pub const SECTION_NAME: &str = ".eh_frame";
+    pub const SECTION_NAME: &'static str = ".eh_frame";
 
     pub unsafe fn new(
         _base_address: *const u8,
diff --git a/crates/jit/src/unwind/systemv.rs b/crates/jit/src/unwind/systemv.rs
index 66ba66f2c..2233e4dcb 100644
--- a/crates/jit/src/unwind/systemv.rs
+++ b/crates/jit/src/unwind/systemv.rs
@@ -14,7 +14,7 @@ extern "C" {
 }
 
 impl UnwindRegistration {
-    pub const SECTION_NAME: &str = ".eh_frame";
+    pub const SECTION_NAME: &'static str = ".eh_frame";
 
     /// Registers precompiled unwinding information with the system.
     ///
diff --git a/crates/jit/src/unwind/winx64.rs b/crates/jit/src/unwind/winx64.rs
index f0f663077..6468b87c3 100644
--- a/crates/jit/src/unwind/winx64.rs
+++ b/crates/jit/src/unwind/winx64.rs
@@ -10,7 +10,7 @@ pub struct UnwindRegistration {
 }
 
 impl UnwindRegistration {
-    pub const SECTION_NAME: &str = ".pdata";
+    pub const SECTION_NAME: &'static str = ".pdata";
 
     pub unsafe fn new(
         base_address: *const u8,
diff --git a/crates/wasmtime/src/component/mod.rs b/crates/wasmtime/src/component/mod.rs
index 4129975de..618f829b2 100644
--- a/crates/wasmtime/src/component/mod.rs
+++ b/crates/wasmtime/src/component/mod.rs
@@ -281,6 +281,22 @@ pub(crate) use self::store::ComponentStoreData;
 ///     // This option defaults to `false`.
 ///     async: true,
 ///
+///     // Alternative mode of async configuration where this still implies
+///     // async instantiation happens, for example, but more control is
+///     // provided over which imports are async and which aren't.
+///     //
+///     // Note that in this mode all exports are still async.
+///     async: {
+///         // All imports are async except for functions with these names
+///         except_imports: ["foo", "bar"],
+///
+///         // All imports are synchronous except for functions with these names
+///         //
+///         // Note that this key cannot be specified with `except_imports`,
+///         // only one or the other is accepted.
+///         only_imports: ["foo", "bar"],
+///     },
+///
 ///     // This can be used to translate WIT return values of the form
 ///     // `result<T, error-type>` into `Result<T, RustErrorType>` in Rust.
 ///     // The `RustErrorType` structure will have an automatically generated
@@ -293,6 +309,20 @@ pub(crate) use self::store::ComponentStoreData;
 ///         interface::ErrorType: RustErrorType,
 ///     },
 ///
+///     // All generated bindgen types are "owned" meaning types like `String`
+///     // are used instead of `&str`, for example. This is the default and
+///     // ensures that the same type used in both imports and exports uses the
+///     // same generated type.
+///     ownership: Owning,
+///
+///     // Alternative to `Owning` above where borrowed types attempt to be used
+///     // instead. The `duplicate_if_necessary` configures whether duplicate
+///     // Rust types will be generated for the same WIT type if necessary, for
+///     // example when a type is used both as an import and an export.
+///     ownership: Borrowing {
+///         duplicate_if_necessary: true
+///     },
+///
 ///     // Restrict the code generated to what's needed for the interface
 ///     // imports in the inlined WIT document fragment.
 ///     interfaces: "
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index 44431ba2e..21ae6367b 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -3,7 +3,7 @@ use crate::types::{TypeInfo, Types};
 use anyhow::{anyhow, bail, Context};
 use heck::*;
 use indexmap::IndexMap;
-use std::collections::{BTreeMap, HashMap};
+use std::collections::{BTreeMap, HashMap, HashSet};
 use std::fmt::Write as _;
 use std::io::{Read, Write};
 use std::mem;
@@ -94,7 +94,7 @@ pub struct Opts {
     pub tracing: bool,
 
     /// Whether or not to use async rust functions and traits.
-    pub async_: bool,
+    pub async_: AsyncConfig,
 
     /// A list of "trappable errors" which are used to replace the `E` in
     /// `result<T, E>` found in WIT.
@@ -123,6 +123,42 @@ pub struct TrappableError {
     pub rust_type_name: String,
 }
 
+#[derive(Default, Debug, Clone)]
+pub enum AsyncConfig {
+    /// No functions are `async`.
+    #[default]
+    None,
+    /// All generated functions should be `async`.
+    All,
+    /// These imported functions should not be async, but everything else is.
+    AllExceptImports(HashSet<String>),
+    /// These functions are the only imports that are async, all other imports
+    /// are sync.
+    ///
+    /// Note that all exports are still async in this situation.
+    OnlyImports(HashSet<String>),
+}
+
+impl AsyncConfig {
+    pub fn is_import_async(&self, f: &str) -> bool {
+        match self {
+            AsyncConfig::None => false,
+            AsyncConfig::All => true,
+            AsyncConfig::AllExceptImports(set) => !set.contains(f),
+            AsyncConfig::OnlyImports(set) => set.contains(f),
+        }
+    }
+
+    pub fn maybe_async(&self) -> bool {
+        match self {
+            AsyncConfig::None => false,
+            AsyncConfig::All | AsyncConfig::AllExceptImports(_) | AsyncConfig::OnlyImports(_) => {
+                true
+            }
+        }
+    }
+}
+
 impl Opts {
     pub fn generate(&self, resolve: &Resolve, world: WorldId) -> String {
         let mut r = Wasmtime::default();
@@ -412,7 +448,7 @@ impl Wasmtime {
         }
         self.src.push_str("}\n");
 
-        let (async_, async__, send, await_) = if self.opts.async_ {
+        let (async_, async__, send, await_) = if self.opts.async_.maybe_async() {
             ("async", "_async", ":Send", ".await")
         } else {
             ("", "", "", "")
@@ -577,7 +613,7 @@ impl Wasmtime {
         }
 
         let world_camel = to_rust_upper_camel_case(&resolve.worlds[world].name);
-        if self.opts.async_ {
+        if self.opts.async_.maybe_async() {
             uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
         }
         uwrite!(self.src, "pub trait {world_camel}Imports");
@@ -646,7 +682,7 @@ impl Wasmtime {
             self.src.push_str(&name);
         }
 
-        let maybe_send = if self.opts.async_ {
+        let maybe_send = if self.opts.async_.maybe_async() {
             " + Send, T: Send"
         } else {
             ""
@@ -854,7 +890,7 @@ impl<'a> InterfaceGenerator<'a> {
         self.rustdoc(docs);
         uwriteln!(self.src, "pub enum {camel} {{}}");
 
-        if self.gen.opts.async_ {
+        if self.gen.opts.async_.maybe_async() {
             uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
         }
 
@@ -1375,7 +1411,7 @@ impl<'a> InterfaceGenerator<'a> {
         let iface = &self.resolve.interfaces[id];
         let owner = TypeOwner::Interface(id);
 
-        if self.gen.opts.async_ {
+        if self.gen.opts.async_.maybe_async() {
             uwriteln!(self.src, "#[wasmtime::component::__internal::async_trait]")
         }
         // Generate the `pub trait` which represents the host functionality for
@@ -1400,7 +1436,7 @@ impl<'a> InterfaceGenerator<'a> {
         }
         uwriteln!(self.src, "}}");
 
-        let where_clause = if self.gen.opts.async_ {
+        let where_clause = if self.gen.opts.async_.maybe_async() {
             "T: Send, U: Host + Send".to_string()
         } else {
             "U: Host".to_string()
@@ -1443,7 +1479,7 @@ impl<'a> InterfaceGenerator<'a> {
         uwrite!(
             self.src,
             "{linker}.{}(\"{}\", ",
-            if self.gen.opts.async_ {
+            if self.gen.opts.async_.is_import_async(&func.name) {
                 "func_wrap_async"
             } else {
                 "func_wrap"
@@ -1472,7 +1508,7 @@ impl<'a> InterfaceGenerator<'a> {
             self.src.push_str(", ");
         }
         self.src.push_str(") |");
-        if self.gen.opts.async_ {
+        if self.gen.opts.async_.is_import_async(&func.name) {
             self.src.push_str(" Box::new(async move { \n");
         } else {
             self.src.push_str(" { \n");
@@ -1541,7 +1577,7 @@ impl<'a> InterfaceGenerator<'a> {
         for (i, _) in func.params.iter().enumerate() {
             uwrite!(self.src, "arg{},", i);
         }
-        if self.gen.opts.async_ {
+        if self.gen.opts.async_.is_import_async(&func.name) {
             uwrite!(self.src, ").await;\n");
         } else {
             uwrite!(self.src, ");\n");
@@ -1571,7 +1607,7 @@ impl<'a> InterfaceGenerator<'a> {
             uwrite!(self.src, "r\n");
         }
 
-        if self.gen.opts.async_ {
+        if self.gen.opts.async_.is_import_async(&func.name) {
             // Need to close Box::new and async block
             self.src.push_str("})");
         } else {
@@ -1582,7 +1618,7 @@ impl<'a> InterfaceGenerator<'a> {
     fn generate_function_trait_sig(&mut self, func: &Function) {
         self.rustdoc(&func.docs);
 
-        if self.gen.opts.async_ {
+        if self.gen.opts.async_.is_import_async(&func.name) {
             self.push_str("async ");
         }
         self.push_str("fn ");
@@ -1658,7 +1694,11 @@ impl<'a> InterfaceGenerator<'a> {
         ns: Option<&WorldKey>,
         func: &Function,
     ) {
-        let (async_, async__, await_) = if self.gen.opts.async_ {
+        // Exports must be async if anything could be async, it's just imports
+        // that get to be optionally async/sync.
+        let is_async = self.gen.opts.async_.maybe_async();
+
+        let (async_, async__, await_) = if is_async {
             ("async", "_async", ".await")
         } else {
             ("", "", "")
@@ -1681,7 +1721,7 @@ impl<'a> InterfaceGenerator<'a> {
         self.src.push_str(") -> wasmtime::Result<");
         self.print_result_ty(&func.results, TypeMode::Owned);
 
-        if self.gen.opts.async_ {
+        if is_async {
             self.src
                 .push_str("> where <S as wasmtime::AsContext>::Data: Send {\n");
         } else {
diff --git a/fuzz/fuzz_targets/instantiate-many.rs b/fuzz/fuzz_targets/instantiate-many.rs
index 0a8fd9c92..48f1248a1 100644
--- a/fuzz/fuzz_targets/instantiate-many.rs
+++ b/fuzz/fuzz_targets/instantiate-many.rs
@@ -12,7 +12,7 @@ const MAX_MODULES: usize = 5;
 fuzz_target!(|data: &[u8]| {
     // errors in `run` have to do with not enough input in `data`, which we
     // ignore here since it doesn't affect how we'd like to fuzz.
-    drop(execute_one(data));
+    let _ = execute_one(data);
 });
 
 fn execute_one(data: &[u8]) -> Result<()> {


Test patch:
diff --git a/cranelift/filetests/filetests/runtests/simd-ishl.clif b/cranelift/filetests/filetests/runtests/simd-ishl.clif
index 524bc2745..648d8f9fe 100644
--- a/cranelift/filetests/filetests/runtests/simd-ishl.clif
+++ b/cranelift/filetests/filetests/runtests/simd-ishl.clif
@@ -1,3 +1,4 @@
+test interpret
 test run
 target aarch64
 target s390x
diff --git a/cranelift/filetests/filetests/runtests/simd-sshr.clif b/cranelift/filetests/filetests/runtests/simd-sshr.clif
index a5fc47821..2df402270 100644
--- a/cranelift/filetests/filetests/runtests/simd-sshr.clif
+++ b/cranelift/filetests/filetests/runtests/simd-sshr.clif
@@ -1,3 +1,4 @@
+test interpret
 test run
 target aarch64
 target s390x
@@ -70,7 +71,7 @@ block0(v0: i8x16):
     v2 = sshr v0, v1
     return v2
 }
-; run: %i8x16_shl_const([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0xe0 0 0 0 0 0 0 0 0]
+; run: %i8x16_sshr_const([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0xe0 0 0 0 0 0 0 0 0]
 
 function %i16x8_sshr_const(i16x8) -> i16x8 {
 block0(v0: i16x8):
diff --git a/cranelift/filetests/filetests/runtests/simd-ushr.clif b/cranelift/filetests/filetests/runtests/simd-ushr.clif
index 5b188523e..5d8cf88be 100644
--- a/cranelift/filetests/filetests/runtests/simd-ushr.clif
+++ b/cranelift/filetests/filetests/runtests/simd-ushr.clif
@@ -1,3 +1,4 @@
+test interpret
 test run
 target aarch64
 target s390x
@@ -54,7 +55,7 @@ block0(v0: i8x16):
     v2 = ushr v0, v1
     return v2
 }
-; run: %i8x16_shl_const([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0x20 0 0 0 0 0 0 0 0]
+; run: %i8x16_ushr_const([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0x20 0 0 0 0 0 0 0 0]
 
 function %i16x8_ushr_const(i16x8) -> i16x8 {
 block0(v0: i16x8):
diff --git a/tests/all/component_model/bindgen.rs b/tests/all/component_model/bindgen.rs
index dbe65a3dd..f0160dc92 100644
--- a/tests/all/component_model/bindgen.rs
+++ b/tests/all/component_model/bindgen.rs
@@ -316,3 +316,97 @@ mod resources_at_interface_level {
         Ok(())
     }
 }
+
+mod async_config {
+    use super::*;
+
+    wasmtime::component::bindgen!({
+        inline: "
+            package foo:foo
+
+            world t1 {
+                import x: func()
+                import y: func()
+                export z: func()
+            }
+        ",
+        async: true,
+    });
+
+    struct T;
+
+    #[async_trait::async_trait]
+    impl T1Imports for T {
+        async fn x(&mut self) -> Result<()> {
+            Ok(())
+        }
+
+        async fn y(&mut self) -> Result<()> {
+            Ok(())
+        }
+    }
+
+    async fn _test_t1(t1: &T1, store: &mut Store<()>) {
+        let _ = t1.call_z(&mut *store).await;
+    }
+
+    wasmtime::component::bindgen!({
+        inline: "
+            package foo:foo
+
+            world t2 {
+                import x: func()
+                import y: func()
+                export z: func()
+            }
+        ",
+        async: {
+            except_imports: ["x"],
+        },
+    });
+
+    #[async_trait::async_trait]
+    impl T2Imports for T {
+        fn x(&mut self) -> Result<()> {
+            Ok(())
+        }
+
+        async fn y(&mut self) -> Result<()> {
+            Ok(())
+        }
+    }
+
+    async fn _test_t2(t2: &T2, store: &mut Store<()>) {
+        let _ = t2.call_z(&mut *store).await;
+    }
+
+    wasmtime::component::bindgen!({
+        inline: "
+            package foo:foo
+
+            world t3 {
+                import x: func()
+                import y: func()
+                export z: func()
+            }
+        ",
+        async: {
+            only_imports: ["x"],
+        },
+    });
+
+    #[async_trait::async_trait]
+    impl T3Imports for T {
+        async fn x(&mut self) -> Result<()> {
+            Ok(())
+        }
+
+        fn y(&mut self) -> Result<()> {
+            Ok(())
+        }
+    }
+
+    async fn _test_t3(t3: &T3, store: &mut Store<()>) {
+        let _ = t3.call_z(&mut *store).await;
+    }
+}