Repository: bytecodealliance/wasmtime
Pull request #8695 (2024-05-27 11:09:12)

riscv64: Initial support for the ZiCond Extension
👋 Hey,

This PR adds support for the ZiCond: Integer Conditional Operations (Chapter 12 of the [Unprivileged RISC-V ISA Manual](https://github.com/riscv/riscv-isa-manual/releases/tag/riscv-isa-release-221bd85-2024-05-14)). 

This extension adds two instructions that either zero a register, or move one of the arguments, depending on a condition register.

* `czero.eqz rd, rs1, rs2` - Moves zero to a register rd, if the condition rs2 is equal to zero, otherwise moves rs1 to rd.
* `czero.nez rd, rs1, rs2` - Moves zero to a register rd, if the condition rs2 is nonzero, otherwise moves rs1 to rd

It doesn't include a full conditional move of two registers, but that is implemented by combining both of the instructions above.

The ISA manual also suggests some interesting instruction sequences for conditional add/sub/and/or/etc... that only contain two instructions.

This PR only implements the base cases. The direct lowerings of the instructions, as well as the full conditional move of two registers. I'm planning on implementing the other sequences at a later date.

github-actions[bot]: #### Subscribe to Label Action

cc @cfallin, @fitzgen

<details>
This issue or pull request has been labeled: "cranelift", "cranelift:area:riscv64", "cranelift:meta", "isle"

Thus the following users have been cc'd because of the following labels:

* cfallin: isle
* fitzgen: isle

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 49aaa7f4b..9d23f7c15 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -853,7 +853,7 @@ dependencies = [
  "serde_derive",
  "smallvec",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmtime-types",
  "wat",
 ]
@@ -2763,7 +2763,7 @@ dependencies = [
  "cargo_metadata",
  "heck 0.4.0",
  "wasmtime",
- "wit-component",
+ "wit-component 0.209.1",
 ]
 
 [[package]]
@@ -3093,7 +3093,7 @@ name = "verify-component-adapter"
 version = "22.0.0"
 dependencies = [
  "anyhow",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wat",
 ]
 
@@ -3186,7 +3186,7 @@ dependencies = [
  "byte-array-literals",
  "object 0.33.0",
  "wasi",
- "wasm-encoder",
+ "wasm-encoder 0.209.1",
  "wit-bindgen-rust-macro",
 ]
 
@@ -3253,6 +3253,15 @@ dependencies = [
  "leb128",
 ]
 
+[[package]]
+name = "wasm-encoder"
+version = "0.209.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4a05336882dae732ce6bd48b7e11fe597293cb72c13da4f35d7d5f8d53b2a7"
+dependencies = [
+ "leb128",
+]
+
 [[package]]
 name = "wasm-metadata"
 version = "0.208.1"
@@ -3265,36 +3274,52 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "spdx",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.208.1",
+ "wasmparser 0.208.1",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.209.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d32029ce424f6d3c2b39b4419fb45a0e2d84fb0751e0c0a32b7ce8bd5d97f46"
+dependencies = [
+ "anyhow",
+ "indexmap 2.2.6",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "spdx",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
 ]
 
 [[package]]
 name = "wasm-mutate"
-version = "0.208.1"
+version = "0.209.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d279475f0f5e68af5ad6eebf0377f777cadcd0c15208f64121522e44dbe45ec9"
+checksum = "f58dddabebff47e1a4f22fbe7fbb769ab699cd9efe7552f50ce5d0881f006884"
 dependencies = [
  "egg",
  "log",
  "rand",
  "thiserror",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
 ]
 
 [[package]]
 name = "wasm-smith"
-version = "0.208.1"
+version = "0.209.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c94a8e7d3a1c3b34ad60d41421c857105aac67e00ae9854247a4a668694f6e9"
+checksum = "38482bb6ce309f7b5b8a168209d0e5c1df8643f1026fc21aaa196058f16d42e8"
 dependencies = [
  "anyhow",
  "arbitrary",
  "flagset",
  "indexmap 2.2.6",
  "leb128",
- "wasm-encoder",
+ "wasm-encoder 0.209.1",
 ]
 
 [[package]]
@@ -3342,6 +3367,19 @@ name = "wasmparser"
 version = "0.208.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd921789c9dcc495f589cb37d200155dee65b4a4beeb853323b5e24e0a5f9c58"
+dependencies = [
+ "ahash",
+ "bitflags 2.4.1",
+ "hashbrown 0.14.3",
+ "indexmap 2.2.6",
+ "semver",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.209.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07035cc9a9b41e62d3bb3a3815a66ab87c993c06fe1cf6b2a3f2a18499d937db"
 dependencies = [
  "ahash",
  "bitflags 2.4.1",
@@ -3362,12 +3400,12 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.208.1"
+version = "0.209.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0700bdace4821e6c694617938500ae9999946df464bb13219c16570f8b6f202f"
+checksum = "ceca8ae6eaa8c7c87b33c25c53bdf299f8c2a764aee1179402ff7652ef3a6859"
 dependencies = [
  "anyhow",
- "wasmparser",
+ "wasmparser 0.209.1",
 ]
 
 [[package]]
@@ -3410,8 +3448,8 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "wasi-common",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
  "wasmtime-asm-macros",
  "wasmtime-cache",
  "wasmtime-component-macro",
@@ -3553,7 +3591,7 @@ dependencies = [
  "tracing",
  "walkdir",
  "wasi-common",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmtime",
  "wasmtime-cache",
  "wasmtime-cli-flags",
@@ -3561,15 +3599,16 @@ dependencies = [
  "wasmtime-cranelift",
  "wasmtime-environ",
  "wasmtime-explorer",
+ "wasmtime-test-macros",
  "wasmtime-wasi",
  "wasmtime-wasi-http",
  "wasmtime-wasi-nn",
  "wasmtime-wasi-threads",
  "wasmtime-wast",
- "wast 208.0.1",
+ "wast 209.0.1",
  "wat",
  "windows-sys 0.52.0",
- "wit-component",
+ "wit-component 0.209.1",
 ]
 
 [[package]]
@@ -3602,7 +3641,7 @@ dependencies = [
  "wasmtime",
  "wasmtime-component-util",
  "wasmtime-wit-bindgen",
- "wit-parser",
+ "wit-parser 0.209.1",
 ]
 
 [[package]]
@@ -3626,7 +3665,7 @@ dependencies = [
  "object 0.33.0",
  "target-lexicon",
  "thiserror",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmtime-environ",
  "wasmtime-versioned-export-macros",
 ]
@@ -3649,8 +3688,8 @@ dependencies = [
  "serde",
  "serde_derive",
  "target-lexicon",
- "wasm-encoder",
- "wasmparser",
+ "wasm-encoder 0.209.1",
+ "wasmparser 0.209.1",
  "wasmprinter",
  "wasmtime-component-util",
  "wasmtime-types",
@@ -3665,7 +3704,7 @@ dependencies = [
  "component-fuzz-util",
  "env_logger",
  "libfuzzer-sys",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmprinter",
  "wasmtime-environ",
  "wat",
@@ -3723,7 +3762,7 @@ dependencies = [
  "rand",
  "smallvec",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmtime",
  "wasmtime-fuzzing",
 ]
@@ -3744,12 +3783,12 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "v8",
- "wasm-encoder",
+ "wasm-encoder 0.209.1",
  "wasm-mutate",
  "wasm-smith",
  "wasm-spec-interpreter",
  "wasmi",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmprinter",
  "wasmtime",
  "wasmtime-wast",
@@ -3780,6 +3819,16 @@ dependencies = [
 name = "wasmtime-slab"
 version = "22.0.0"
 
+[[package]]
+name = "wasmtime-test-macros"
+version = "0.0.0"
+dependencies = [
+ "anyhow",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.60",
+]
+
 [[package]]
 name = "wasmtime-types"
 version = "22.0.0"
@@ -3788,7 +3837,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "smallvec",
- "wasmparser",
+ "wasmparser 0.209.1",
 ]
 
 [[package]]
@@ -3895,7 +3944,7 @@ dependencies = [
  "anyhow",
  "log",
  "wasmtime",
- "wast 208.0.1",
+ "wast 209.0.1",
 ]
 
 [[package]]
@@ -3907,7 +3956,7 @@ dependencies = [
  "gimli",
  "object 0.33.0",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmtime-cranelift",
  "wasmtime-environ",
  "winch-codegen",
@@ -3920,7 +3969,7 @@ dependencies = [
  "anyhow",
  "heck 0.4.0",
  "indexmap 2.2.6",
- "wit-parser",
+ "wit-parser 0.209.1",
 ]
 
 [[package]]
@@ -3938,24 +3987,24 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "208.0.1"
+version = "209.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc00b3f023b4e2ccd2e054e240294263db52ae962892e6523e550783c83a67f1"
+checksum = "8fffef2ff6147e4d12e972765fd75332c6a11c722571d4ab7a780d81ffc8f0a4"
 dependencies = [
  "bumpalo",
  "leb128",
  "memchr",
  "unicode-width",
- "wasm-encoder",
+ "wasm-encoder 0.209.1",
 ]
 
 [[package]]
 name = "wat"
-version = "1.208.1"
+version = "1.209.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58ed38e59176550214c025ea2bd0eeefd8e86b92d0af6698d5ba95020ec2e07b"
+checksum = "42203ec0271d113f8eb1f77ebc624886530cecb35915a7f63a497131f16e4d24"
 dependencies = [
- "wast 208.0.1",
+ "wast 209.0.1",
 ]
 
 [[package]]
@@ -4075,7 +4124,7 @@ dependencies = [
  "regalloc2",
  "smallvec",
  "target-lexicon",
- "wasmparser",
+ "wasmparser 0.209.1",
  "wasmtime-cranelift",
  "wasmtime-environ",
 ]
@@ -4268,7 +4317,7 @@ checksum = "7076a12e69af6e1f6093bd16657d7ae61c30cfd3c5f62321046eb863b17ab1e2"
 dependencies = [
  "anyhow",
  "heck 0.5.0",
- "wit-parser",
+ "wit-parser 0.208.1",
 ]
 
 [[package]]
@@ -4289,9 +4338,9 @@ dependencies = [
  "anyhow",
  "heck 0.5.0",
  "indexmap 2.2.6",
- "wasm-metadata",
+ "wasm-metadata 0.208.1",
  "wit-bindgen-core",
- "wit-component",
+ "wit-component 0.208.1",
 ]
 
 [[package]]
@@ -4321,10 +4370,29 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder",
- "wasm-metadata",
- "wasmparser",
- "wit-parser",
+ "wasm-encoder 0.208.1",
+ "wasm-metadata 0.208.1",
+ "wasmparser 0.208.1",
+ "wit-parser 0.208.1",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.209.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25a2bb5b039f9cb03425e1d5a6e54b441ca4ca1b1d4fa6a0924db67a55168f99"
+dependencies = [
+ "anyhow",
+ "bitflags 2.4.1",
+ "indexmap 2.2.6",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder 0.209.1",
+ "wasm-metadata 0.209.1",
+ "wasmparser 0.209.1",
+ "wit-parser 0.209.1",
 ]
 
 [[package]]
@@ -4342,7 +4410,25 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser",
+ "wasmparser 0.208.1",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.209.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e79b9e3c0b6bb589dec46317e645851e0db2734c44e2be5e251b03ff4a51269"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap 2.2.6",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser 0.209.1",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 7dccf676f..ef48f5fc0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -115,6 +115,7 @@ similar = { workspace = true }
 libtest-mimic = "0.7.0"
 capstone = { workspace = true }
 object = { workspace = true, features = ['std'] }
+wasmtime-test-macros = { path = "crates/test-macros" }
 
 [target.'cfg(windows)'.dev-dependencies]
 windows-sys = { workspace = true, features = ["Win32_System_Memory"] }
@@ -165,6 +166,7 @@ unused_extern_crates = 'warn'
 trivial_numeric_casts = 'warn'
 unstable_features = 'warn'
 unused_import_braces = 'warn'
+unused-lifetimes = 'warn'
 
 [workspace.lints.clippy]
 # The default set of lints in Clippy is viewed as "too noisy" right now so
@@ -250,15 +252,15 @@ wit-bindgen = { version = "0.25.0", default-features = false }
 wit-bindgen-rust-macro = { version = "0.25.0", default-features = false }
 
 # wasm-tools family:
-wasmparser = { version = "0.208.1", default-features = false }
-wat = "1.208.1"
-wast = "208.0.1"
-wasmprinter = "0.208.1"
-wasm-encoder = "0.208.1"
-wasm-smith = "0.208.1"
-wasm-mutate = "0.208.1"
-wit-parser = "0.208.1"
-wit-component = "0.208.1"
+wasmparser = { version = "0.209.1", default-features = false }
+wat = "1.209.1"
+wast = "209.0.1"
+wasmprinter = "0.209.1"
+wasm-encoder = "0.209.1"
+wasm-smith = "0.209.1"
+wasm-mutate = "0.209.1"
+wit-parser = "0.209.1"
+wit-component = "0.209.1"
 
 # Non-Bytecode Alliance maintained dependencies:
 # --------------------------
diff --git a/benches/instantiation.rs b/benches/instantiation.rs
index b1d5e9c91..938159985 100644
--- a/benches/instantiation.rs
+++ b/benches/instantiation.rs
@@ -20,7 +20,7 @@ fn instantiate(pre: &InstancePre<WasiCtx>, engine: &Engine) -> Result<()> {
     Ok(())
 }
 
-fn benchmark_name<'a>(strategy: &InstanceAllocationStrategy) -> &'static str {
+fn benchmark_name(strategy: &InstanceAllocationStrategy) -> &'static str {
     match strategy {
         InstanceAllocationStrategy::OnDemand => "default",
         InstanceAllocationStrategy::Pooling { .. } => "pooling",
diff --git a/cranelift/codegen/meta/src/isa/riscv64.rs b/cranelift/codegen/meta/src/isa/riscv64.rs
index 7c1caff9b..d11fbf951 100644
--- a/cranelift/codegen/meta/src/isa/riscv64.rs
+++ b/cranelift/codegen/meta/src/isa/riscv64.rs
@@ -126,6 +126,12 @@ pub(crate) fn define() -> TargetIsa {
         "Zbs: Single-bit instructions",
         false,
     );
+    let _has_zicond = setting.add_bool(
+        "has_zicond",
+        "has extension zicond?",
+        "ZiCond: Integer Conditional Operations",
+        false,
+    );
 
     let has_zicsr = setting.add_bool(
         "has_zicsr",
diff --git a/cranelift/codegen/src/ir/extfunc.rs b/cranelift/codegen/src/ir/extfunc.rs
index cafdb11dc..c4721eb13 100644
--- a/cranelift/codegen/src/ir/extfunc.rs
+++ b/cranelift/codegen/src/ir/extfunc.rs
@@ -250,12 +250,6 @@ pub enum ArgumentPurpose {
     /// This is a pointer to a context struct containing details about the current sandbox. It is
     /// used as a base pointer for `vmctx` global values.
     VMContext,
-
-    /// A stack limit pointer.
-    ///
-    /// This is a pointer to a stack limit. It is used to check the current stack pointer
-    /// against. Can only appear once in a signature.
-    StackLimit,
 }
 
 impl fmt::Display for ArgumentPurpose {
@@ -265,7 +259,6 @@ impl fmt::Display for ArgumentPurpose {
             Self::StructArgument(size) => return write!(f, "sarg({})", size),
             Self::StructReturn => "sret",
             Self::VMContext => "vmctx",
-            Self::StackLimit => "stack_limit",
         })
     }
 }
@@ -277,7 +270,6 @@ impl FromStr for ArgumentPurpose {
             "normal" => Ok(Self::Normal),
             "sret" => Ok(Self::StructReturn),
             "vmctx" => Ok(Self::VMContext),
-            "stack_limit" => Ok(Self::StackLimit),
             _ if s.starts_with("sarg(") => {
                 if !s.ends_with(")") {
                     return Err(());
@@ -374,7 +366,6 @@ mod tests {
             (ArgumentPurpose::Normal, "normal"),
             (ArgumentPurpose::StructReturn, "sret"),
             (ArgumentPurpose::VMContext, "vmctx"),
-            (ArgumentPurpose::StackLimit, "stack_limit"),
             (ArgumentPurpose::StructArgument(42), "sarg(42)"),
         ];
         for &(e, n) in &all_purpose {
diff --git a/cranelift/codegen/src/ir/types.rs b/cranelift/codegen/src/ir/types.rs
index c17628f99..928a28526 100644
--- a/cranelift/codegen/src/ir/types.rs
+++ b/cranelift/codegen/src/ir/types.rs
@@ -158,8 +158,6 @@ impl Type {
 
     /// Get a type with the same number of lanes as this type, but with the lanes replaced by
     /// integers of the same size.
-    ///
-    /// Scalar types follow this same rule, but `b1` is converted into `i8`
     pub fn as_int(self) -> Self {
         self.replace_lanes(match self.lane_type() {
             I8 => I8,
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index a3133d77d..922e4ebfc 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -166,9 +166,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
 
             if matches!(
                 param.purpose,
-                ir::ArgumentPurpose::StructArgument(_)
-                    | ir::ArgumentPurpose::StructReturn
-                    | ir::ArgumentPurpose::StackLimit
+                ir::ArgumentPurpose::StructArgument(_) | ir::ArgumentPurpose::StructReturn
             ) {
                 assert!(
                     call_conv != isa::CallConv::Tail,
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index bb9166287..e065a1b1d 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -553,6 +553,10 @@
   (Pack)
   (Packw)
   (Packh)
+
+  ;; ZiCond: Integer Conditional Operations
+  (CzeroEqz)
+  (CzeroNez)
 ))
 
 
@@ -1047,6 +1051,9 @@
 (decl pure has_zbs () bool)
 (extern constructor has_zbs has_zbs)
 
+(decl pure has_zicond () bool)
+(extern constructor has_zicond has_zicond)
+
 
 ;;;; Instruction Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -1783,6 +1790,26 @@
 (rule (rv_packw rs1 rs2)
   (alu_rrr (AluOPRRR.Packw) rs1 rs2))
 
+;; `ZiCond` Extension Instructions
+
+;; Helper for emitting the `czero.eqz` ("Conditional zero, if condition is equal to zero") instruction.
+;; RS1 is the data source
+;; RS2 is the condition
+;;
+;; rd = (rs2 == 0) ? 0 : rs1
+(decl rv_czero_eqz (XReg XReg) XReg)
+(rule (rv_czero_eqz rs1 rs2)
+  (alu_rrr (AluOPRRR.CzeroEqz) rs1 rs2))
+
+;; Helper for emitting the `czero.nez` ("Conditional zero, if condition is nonzero") instruction.
+;; RS1 is the data source
+;; RS2 is the condition
+;;
+;; rd = (rs2 != 0) ? 0 : rs1
+(decl rv_czero_nez (XReg XReg) XReg)
+(rule (rv_czero_nez rs1 rs2)
+  (alu_rrr (AluOPRRR.CzeroNez) rs1 rs2))
+
 
 ;; `Zicsr` Extension Instructions
 
@@ -2560,26 +2587,68 @@
 
 (decl gen_select_xreg (IntegerCompare XReg XReg) XReg)
 
-(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
+(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
   (if-let (IntCC.UnsignedLessThan) (intcc_without_eq cc))
   (if-let $true (has_zbb))
   (rv_minu x y))
 
-(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
+(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
   (if-let (IntCC.SignedLessThan) (intcc_without_eq cc))
   (if-let $true (has_zbb))
   (rv_min x y))
 
-(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
+(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
   (if-let (IntCC.UnsignedGreaterThan) (intcc_without_eq cc))
   (if-let $true (has_zbb))
   (rv_maxu x y))
 
-(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
+(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
   (if-let (IntCC.SignedGreaterThan) (intcc_without_eq cc))
   (if-let $true (has_zbb))
   (rv_max x y))
 
+;; Rotate Zero Reg to the right. This allows us to write fewer rules
+;; below when matching the zero register
+;;
+;; Additionally prevent this rule from recursing infinitely by only
+;; matching when one of the inputs is the zero register, but not both.
+
+(rule 5 (gen_select_xreg (int_compare_decompose cc a @ (zero_reg) b @ (non_zero_reg)) x y)
+  (if-let $true (has_zicond))
+  (gen_select_xreg (int_compare (intcc_swap_args cc) b a) x y))
+
+(rule 4 (gen_select_xreg c @ (int_compare_decompose cc a b) x @ (zero_reg) y @ (non_zero_reg))
+  (if-let $true (has_zicond))
+  (gen_select_xreg (int_compare (intcc_complement cc) a b) y x))
+
+(rule 3 (gen_select_xreg (int_compare_decompose (IntCC.Equal) c (zero_reg)) x (zero_reg))
+  (if-let $true (has_zicond))
+  (rv_czero_nez x c))
+
+(rule 3 (gen_select_xreg (int_compare_decompose (IntCC.NotEqual) c (zero_reg)) x (zero_reg))
+  (if-let $true (has_zicond))
+  (rv_czero_eqz x c))
+
+(rule 2 (gen_select_xreg (int_compare_decompose (IntCC.Equal) c (zero_reg)) x y)
+  (if-let $true (has_zicond))
+  (rv_or
+    (rv_czero_nez x c)
+    (rv_czero_eqz y c)))
+
+(rule 2 (gen_select_xreg (int_compare_decompose (IntCC.NotEqual) c (zero_reg)) x y)
+  (if-let $true (has_zicond))
+  (rv_or
+    (rv_czero_eqz x c)
+    (rv_czero_nez y c)))
+
+;; It is still beneficial to emit the full compare instruction, and then the 3 instruction
+;; select using zicond, so do that here as a last resort.
+(rule 1 (gen_select_xreg compare x y)
+  (if-let $true (has_zicond))
+  (gen_select_xreg (cmp_nez (lower_int_compare compare)) x y))
+
+;; In the base case we emit a conditional branch and a few moves.
+
 (rule 0 (gen_select_xreg c x y)
   (let
     ((dst WritableReg (temp_writable_xreg))
@@ -2815,6 +2884,10 @@
 ;; Generates a bitcast instruction.
 ;; Args are: src, src_ty, dst_ty
 (decl gen_bitcast (Reg Type Type) Reg)
+(rule 5 (gen_bitcast r (ty_scalar_float src_ty) (ty_vec_fits_in_register _)) (rv_vfmv_sf r src_ty))
+(rule 4 (gen_bitcast r (ty_int_ref_scalar_64 src_ty) (ty_vec_fits_in_register _)) (rv_vmv_sx r src_ty))
+(rule 3 (gen_bitcast r (ty_vec_fits_in_register _) (ty_scalar_float dst_ty)) (rv_vfmv_fs r dst_ty))
+(rule 2 (gen_bitcast r (ty_vec_fits_in_register _) (ty_int_ref_scalar_64 dst_ty)) (rv_vmv_xs r dst_ty))
 (rule 1 (gen_bitcast r $F32 $I32) (rv_fmvxw r))
 (rule 1 (gen_bitcast r $F64 $I64) (rv_fmvxd r))
 (rule 1 (gen_bitcast r $I32 $F32) (rv_fmvwx r))
@@ -2952,6 +3025,10 @@
 (decl sp_reg () PReg)
 (extern constructor sp_reg sp_reg)
 
+;; Extractor that matches all registers, except the zero register
+(decl non_zero_reg () XReg)
+(extern extractor non_zero_reg is_non_zero_reg)
+
 ;; Helper for creating the zero register.
 (decl zero_reg () XReg)
 (extern constructor zero_reg zero_reg)
diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs
index 2f6ed9fb0..51e3c69f9 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/args.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs
@@ -804,6 +804,8 @@ impl AluOPRRR {
             Self::Pack => "pack",
             Self::Packw => "packw",
             Self::Packh => "packh",
+            Self::CzeroEqz => "czero.eqz",
+            Self::CzeroNez => "czero.nez",
         }
     }
 
@@ -874,6 +876,10 @@ impl AluOPRRR {
             AluOPRRR::Pack => 0b100,
             AluOPRRR::Packw => 0b100,
             AluOPRRR::Packh => 0b111,
+
+            // ZiCond
+            AluOPRRR::CzeroEqz => 0b101,
+            AluOPRRR::CzeroNez => 0b111,
         }
     }
 
@@ -935,7 +941,9 @@ impl AluOPRRR {
             | AluOPRRR::Sh1add
             | AluOPRRR::Sh2add
             | AluOPRRR::Sh3add
-            | AluOPRRR::Xnor => 0b0110011,
+            | AluOPRRR::Xnor
+            | AluOPRRR::CzeroEqz
+            | AluOPRRR::CzeroNez => 0b0110011,
 
             AluOPRRR::Rolw
             | AluOPRRR::Rorw
@@ -1011,6 +1019,10 @@ impl AluOPRRR {
             AluOPRRR::Pack => 0b0000100,
             AluOPRRR::Packw => 0b0000100,
             AluOPRRR::Packh => 0b0000100,
+
+            // ZiCond
+            AluOPRRR::CzeroEqz => 0b0000111,
+            AluOPRRR::CzeroNez => 0b0000111,
         }
     }
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst/vector.rs b/cranelift/codegen/src/isa/riscv64/inst/vector.rs
index 3b0bdad97..6b374298b 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/vector.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/vector.rs
@@ -268,13 +268,16 @@ impl VecAluOpRRRR {
             VecAluOpRRRR::VfnmaccVV | VecAluOpRRRR::VfnmaccVF => 0b101101,
             VecAluOpRRRR::VfmsacVV | VecAluOpRRRR::VfmsacVF => 0b101110,
             VecAluOpRRRR::VfnmsacVV | VecAluOpRRRR::VfnmsacVF => 0b101111,
+            VecAluOpRRRR::Vslide1upVX => 0b001110,
         }
     }
 
     pub fn category(&self) -> VecOpCategory {
         match self {
             VecAluOpRRRR::VmaccVV | VecAluOpRRRR::VnmsacVV => VecOpCategory::OPMVV,
-            VecAluOpRRRR::VmaccVX | VecAluOpRRRR::VnmsacVX => VecOpCategory::OPMVX,
+            VecAluOpRRRR::VmaccVX | VecAluOpRRRR::VnmsacVX | VecAluOpRRRR::Vslide1upVX => {
+                VecOpCategory::OPMVX
+            }
             VecAluOpRRRR::VfmaccVV
             | VecAluOpRRRR::VfnmaccVV
             | VecAluOpRRRR::VfmsacVV
@@ -299,7 +302,10 @@ impl VecAluOpRRRR {
 
 impl VecInstOverlapInfo for VecAluOpRRRR {
     fn forbids_src_dst_overlaps(&self) -> bool {
-        false
+        match self {
+            VecAluOpRRRR::Vslide1upVX => true,
+            _ => false,
+        }
     }
 }
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst_vector.isle b/cranelift/codegen/src/isa/riscv64/inst_vector.isle
index b35c1c79b..eccbc372f 100644
--- a/cranelift/codegen/src/isa/riscv64/inst_vector.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst_vector.isle
@@ -229,6 +229,7 @@
   (VfnmaccVF)
   (VfmsacVF)
   (VfnmsacVF)
+  (Vslide1upVX)
 ))
 
 ;; Register-Imm ALU Ops
@@ -1095,6 +1096,13 @@
 (rule (rv_vslideup_vvi vd vs2 imm mask vstate)
   (vec_alu_rrr_uimm5 (VecAluOpRRRImm5.VslideupVI) vd vs2 imm mask vstate))
 
+;; Helper for emitting the `vslide1up.vx` instruction.
+;;
+;; # vd[0]=x[rs1], vd[i+1] = vs2[i]
+(decl rv_vslide1up_vx (VReg VReg XReg VecOpMasking VState) VReg)
+(rule (rv_vslide1up_vx vd vs2 rs1 mask vstate)
+  (vec_alu_rrrr (VecAluOpRRRR.Vslide1upVX) vd vs2 rs1 mask vstate))
+
 ;; Helper for emitting the `vmv.x.s` instruction.
 ;; This instruction copies the first element of the source vector to the destination X register.
 ;; Masked versions of this instruction are not supported.
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index a0932f34e..6dc99e3c8 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -1826,16 +1826,27 @@
   (rv_seqz (rv_addi v (imm12_const 1))))
 
 ;;;;;  Rules for `select`;;;;;;;;;
-(rule 0 (lower (has_type (ty_int_ref_scalar_64 _) (select c x y)))
+
+;; Manually matching (iconst 0) here is a bit of a hack. We can't do that as part
+;; of the iconst rule because that runs into regalloc issues. gen_select_xreg
+;; has some optimizations based on the use of the zero register so we have to
+;; manually match it here.
+(rule 5 (lower (has_type (ty_int_ref_scalar_64 _) (select c (i64_from_iconst 0) y)))
+  (gen_select_xreg (is_nonzero_cmp c) (zero_reg) y))
+
+(rule 4 (lower (has_type (ty_int_ref_scalar_64 _) (select c x (i64_from_iconst 0))))
+  (gen_select_xreg (is_nonzero_cmp c) x (zero_reg)))
+
+(rule 3 (lower (has_type (ty_int_ref_scalar_64 _) (select c x y)))
   (gen_select_xreg (is_nonzero_cmp c) x y))
 
-(rule 1 (lower (has_type $I128 (select c x y)))
+(rule 2 (lower (has_type $I128 (select c x y)))
   (gen_select_regs (is_nonzero_cmp c) x y))
 
-(rule 2 (lower (has_type (ty_vec_fits_in_register _) (select c x y)))
+(rule 1 (lower (has_type (ty_vec_fits_in_register _) (select c x y)))
   (gen_select_vreg (is_nonzero_cmp c) x y))
 
-(rule 3 (lower (has_type (ty_scalar_float _) (select c x y)))
+(rule 0 (lower (has_type (ty_scalar_float _) (select c x y)))
   (gen_select_freg (is_nonzero_cmp c) x y))
 
 ;;;;;  Rules for `bitselect`;;;;;;;;;
@@ -2436,8 +2447,24 @@
       (elf_tls_get_addr name))
 
 ;;;;;  Rules for `bitcast`;;;;;;;;;
-(rule
-   (lower (has_type out_ty (bitcast _ v @ (value_type in_ty))))
+
+;; These rules should probably be handled in `gen_bitcast`, but it's convenient to have that return
+;; a single register, instead of a `ValueRegs`
+(rule 2 (lower (has_type $I128 (bitcast _ v @ (value_type (ty_vec_fits_in_register _)))))
+    (value_regs
+      (gen_extractlane $I64X2 v 0)
+      (gen_extractlane $I64X2 v 1)))
+
+;; Move the high half into a vector register, and then use vslide1up to move it up and
+;; insert the lower half in one instruction.
+(rule 1 (lower (has_type (ty_vec_fits_in_register _) (bitcast _ v @ (value_type $I128))))
+    (let ((lo XReg (value_regs_get v 0))
+          (hi XReg (value_regs_get v 1))
+          (vstate VState (vstate_from_type $I64X2))
+          (vec VReg (rv_vmv_sx hi vstate)))
+      (rv_vslide1up_vx vec vec lo (unmasked) vstate)))
+
+(rule 0 (lower (has_type out_ty (bitcast _ v @ (value_type in_ty))))
    (gen_bitcast v in_ty out_ty))
 
 ;;;;;  Rules for `ceil`;;;;;;;;;
diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
index 87f15ba4f..743220d0d 100644
--- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
@@ -300,6 +300,13 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
     fn zero_reg(&mut self) -> XReg {
         XReg::new(zero_reg()).unwrap()
     }
+    fn is_non_zero_reg(&mut self, reg: XReg) -> Option<()> {
+        if reg != self.zero_reg() {
+            Some(())
+        } else {
+            None
+        }
+    }
     fn is_zero_reg(&mut self, reg: XReg) -> Option<()> {
         if reg == self.zero_reg() {
             Some(())
@@ -399,6 +406,10 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
         self.backend.isa_flags.has_zbs()
     }
 
+    fn has_zicond(&mut self) -> bool {
+        self.backend.isa_flags.has_zicond()
+    }
+
     fn gen_reg_offset_amode(&mut self, base: Reg, offset: i64) -> AMode {
         AMode::RegOffset(base, offset)
     }
diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs
index 75d6b8fb4..713fcec25 100644
--- a/cranelift/codegen/src/machinst/abi.rs
+++ b/cranelift/codegen/src/machinst/abi.rs
@@ -1087,7 +1087,7 @@ fn checked_round_up(val: u32, mask: u32) -> Option<u32> {
 
 impl<M: ABIMachineSpec> Callee<M> {
     /// Create a new body ABI instance.
-    pub fn new<'a>(
+    pub fn new(
         f: &ir::Function,
         isa: &dyn TargetIsa,
         isa_flags: &M::F,
@@ -1164,13 +1164,9 @@ impl<M: ABIMachineSpec> Callee<M> {
         // stack limit. This can either be specified as a special-purpose
         // argument or as a global value which often calculates the stack limit
         // from the arguments.
-        let stack_limit =
-            get_special_purpose_param_register(f, sigs, sig, ir::ArgumentPurpose::StackLimit)
-                .map(|reg| (reg, smallvec![]))
-                .or_else(|| {
-                    f.stack_limit
-                        .map(|gv| gen_stack_limit::<M>(f, sigs, sig, gv))
-                });
+        let stack_limit = f
+            .stack_limit
+            .map(|gv| gen_stack_limit::<M>(f, sigs, sig, gv));
 
         let tail_args_size = sigs[sig].sized_stack_arg_space;
 
diff --git a/cranelift/codegen/src/prelude_opt.isle b/cranelift/codegen/src/prelude_opt.isle
index 8bd6370eb..e0cd14860 100644
--- a/cranelift/codegen/src/prelude_opt.isle
+++ b/cranelift/codegen/src/prelude_opt.isle
@@ -74,7 +74,7 @@
 ;; When constructing, the rule will fail if the value cannot be represented in
 ;; the target type.  If it fits, it'll be masked accordingly in the constant.
 (decl iconst_s (Type i64) Value)
-(extractor (iconst_s ty c) (sextend_maybe ty (inst_data_tupled (iconst_sextend_etor _inner_ty c))))
+(extractor (iconst_s ty c) (inst_data_tupled (iconst_sextend_etor ty c)))
 (rule 0 (iconst_s ty c)
 	(if-let c_masked (u64_and (i64_as_u64 c) (ty_umax ty)))
 	(if-let c_reextended (i64_sextend_u64 ty c_masked))
@@ -89,7 +89,7 @@
 ;; When constructing, the rule will fail if the value cannot be represented in
 ;; the target type.
 (decl iconst_u (Type u64) Value)
-(extractor (iconst_u ty c) (uextend_maybe ty (iconst _inner_ty (u64_from_imm64 c))))
+(extractor (iconst_u ty c) (iconst ty (u64_from_imm64 c)))
 (rule 0 (iconst_u ty c)
 	(if-let $true (u64_le c (ty_umax ty)))
     (iconst ty (imm64 c)))
diff --git a/cranelift/codegen/src/write.rs b/cranelift/codegen/src/write.rs
index e80081625..68864a9d4 100644
--- a/cranelift/codegen/src/write.rs
+++ b/cranelift/codegen/src/write.rs
@@ -218,7 +218,7 @@ fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result {
 ///
 ///    block1:
 ///    block1(v1: i32):
-///    block10(v4: f64, v5: b1):
+///    block10(v4: f64, v5: i8):
 ///
 pub fn write_block_header(
     w: &mut dyn Write,
diff --git a/cranelift/docs/compare-llvm.md b/cranelift/docs/compare-llvm.md
index bc9e2060f..d1fe5272e 100644
--- a/cranelift/docs/compare-llvm.md
+++ b/cranelift/docs/compare-llvm.md
@@ -155,9 +155,9 @@ can hold.
 - Cranelift has no aggregate types. LLVM has named and anonymous struct types as
   well as array types.
 
-Cranelift has multiple boolean types, whereas LLVM simply uses `i1`. The sized
-Cranelift boolean types are used to represent SIMD vector masks like `b32x4`
-where each lane is either all 0 or all 1 bits.
+Cranelift uses integer-typed values of `0` or `1` for booleans, whereas LLVM
+simply uses `i1`. The sized Cranelift integer types are used to represent SIMD
+vector masks like `i32x4` where each lane is either all 0 or all 1 bits.
 
 Cranelift instructions and function calls can return multiple result values. LLVM
 instead models this by returning a single value of an aggregate type.
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 2ec2d1799..31d3cea93 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -764,9 +764,6 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                     &[I128],
                     &[F32 | F64]
                 ),
-                // https://github.com/bytecodealliance/wasmtime/issues/6104
-                (Opcode::Bitcast, &[I128], &[_]),
-                (Opcode::Bitcast, &[_], &[I128]),
                 // TODO
                 (
                     Opcode::SelectSpectreGuard,
diff --git a/cranelift/reader/src/lexer.rs b/cranelift/reader/src/lexer.rs
index 2899e008e..cd6d2ed23 100644
--- a/cranelift/reader/src/lexer.rs
+++ b/cranelift/reader/src/lexer.rs
@@ -32,7 +32,7 @@ pub enum Token<'a> {
     Arrow,                 // '->'
     Float(&'a str),        // Floating point immediate
     Integer(&'a str),      // Integer immediate
-    Type(types::Type),     // i32, f32, b32x4, ...
+    Type(types::Type),     // i32, f32, i32x4, ...
     DynamicType(u32),      // dt5
     Value(Value),          // v12, v7
     Block(Block),          // block3
diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs
index 5947802b0..1ad9809bc 100644
--- a/cranelift/reader/src/parser.rs
+++ b/cranelift/reader/src/parser.rs
@@ -179,7 +179,7 @@ pub fn parse_test<'a>(text: &'a str, options: ParseOptions<'a>) -> ParseResult<T
 ///    or `print`
 ///  - `Ok(Some(command))` if the comment is intended as a `RunCommand` and can be parsed to one
 ///  - `Err` otherwise.
-pub fn parse_run_command<'a>(text: &str, signature: &Signature) -> ParseResult<Option<RunCommand>> {
+pub fn parse_run_command(text: &str, signature: &Signature) -> ParseResult<Option<RunCommand>> {
     let _tt = timing::parse_text();
     // We remove leading spaces and semi-colons for convenience here instead of at the call sites
     // since this function will be attempting to parse a RunCommand from a CLIF comment.
@@ -2043,14 +2043,19 @@ impl<'a> Parser<'a> {
         Ok(())
     }
 
-    // Parse parenthesized list of block parameters. Returns a vector of (u32, Type) pairs with the
-    // value numbers of the defined values and the defined types.
+    // Parse parenthesized list of block parameters.
     //
-    // block-params ::= * "(" block-param { "," block-param } ")"
+    // block-params ::= * "(" ( block-param { "," block-param } )? ")"
     fn parse_block_params(&mut self, ctx: &mut Context, block: Block) -> ParseResult<()> {
-        // block-params ::= * "(" block-param { "," block-param } ")"
+        // block-params ::= * "(" ( block-param { "," block-param } )? ")"
         self.match_token(Token::LPar, "expected '(' before block parameters")?;
 
+        // block-params ::= "(" * ")"
+        if self.token() == Some(Token::RPar) {
+            self.consume();
+            return Ok(());
+        }
+
         // block-params ::= "(" * block-param { "," block-param } ")"
         self.parse_block_param(ctx, block)?;
 
diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt
index a8b5098ec..1235c61a0 100644
--- a/crates/c-api/CMakeLists.txt
+++ b/crates/c-api/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.12)
 project(wasmtime C)
 
 set(WASMTIME_USER_CARGO_BUILD_OPTIONS "" CACHE STRING "Additional cargo flags (such as --features) to apply to the build command")
diff --git a/crates/cache/src/lib.rs b/crates/cache/src/lib.rs
index aabaded44..7b146fc9c 100644
--- a/crates/cache/src/lib.rs
+++ b/crates/cache/src/lib.rs
@@ -27,7 +27,7 @@ struct Sha256Hasher(Sha256);
 
 impl<'config> ModuleCacheEntry<'config> {
     /// Create the cache entry.
-    pub fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self {
+    pub fn new(compiler_name: &str, cache_config: &'config CacheConfig) -> Self {
         if cache_config.enabled() {
             Self(Some(ModuleCacheEntryInner::new(
                 compiler_name,
@@ -108,7 +108,7 @@ impl<'config> ModuleCacheEntry<'config> {
 }
 
 impl<'config> ModuleCacheEntryInner<'config> {
-    fn new<'data>(compiler_name: &str, cache_config: &'config CacheConfig) -> Self {
+    fn new(compiler_name: &str, cache_config: &'config CacheConfig) -> Self {
         // If debug assertions are enabled then assume that we're some sort of
         // local build. We don't want local builds to stomp over caches between
         // builds, so just use a separate cache directory based on the mtime of
diff --git a/crates/cranelift/src/debug/transform/mod.rs b/crates/cranelift/src/debug/transform/mod.rs
index 788550d0e..0695c3f3a 100644
--- a/crates/cranelift/src/debug/transform/mod.rs
+++ b/crates/cranelift/src/debug/transform/mod.rs
@@ -132,7 +132,7 @@ fn read_dwarf_package_from_bytes<'data>(
     }
 }
 
-pub fn transform_dwarf<'data>(
+pub fn transform_dwarf(
     isa: &dyn TargetIsa,
     di: &DebugInfoData,
     funcs: &CompiledFunctionsMetadata,
diff --git a/crates/cranelift/src/debug/write_debuginfo.rs b/crates/cranelift/src/debug/write_debuginfo.rs
index 35b9fed72..e5099a704 100644
--- a/crates/cranelift/src/debug/write_debuginfo.rs
+++ b/crates/cranelift/src/debug/write_debuginfo.rs
@@ -140,7 +140,7 @@ impl Writer for WriterRelocate {
     }
 }
 
-fn create_frame_table<'a>(
+fn create_frame_table(
     isa: &dyn TargetIsa,
     funcs: &CompiledFunctionsMetadata,
 ) -> Option<FrameTable> {
@@ -174,7 +174,7 @@ fn create_frame_table<'a>(
     Some(table)
 }
 
-pub fn emit_dwarf<'a>(
+pub fn emit_dwarf(
     isa: &dyn TargetIsa,
     debuginfo_data: &DebugInfoData,
     funcs: &CompiledFunctionsMetadata,
diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml
index 8ca3e98d6..5cef16b18 100644
--- a/crates/environ/Cargo.toml
+++ b/crates/environ/Cargo.toml
@@ -13,6 +13,9 @@ edition.workspace = true
 [lints]
 workspace = true
 
+[package.metadata.docs.rs]
+all-features = true
+
 [dependencies]
 anyhow = { workspace = true }
 postcard = { workspace = true }
diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs
index d88cd2c3a..75152c399 100644
--- a/crates/fuzzing/src/oracles/dummy.rs
+++ b/crates/fuzzing/src/oracles/dummy.rs
@@ -4,7 +4,7 @@ use anyhow::ensure;
 use wasmtime::*;
 
 /// Create a set of dummy functions/globals/etc for the given imports.
-pub fn dummy_linker<'module, T>(store: &mut Store<T>, module: &Module) -> Result<Linker<T>> {
+pub fn dummy_linker<T>(store: &mut Store<T>, module: &Module) -> Result<Linker<T>> {
     let mut linker = Linker::new(store.engine());
     linker.allow_shadowing(true);
     for import in module.imports() {
diff --git a/crates/wasi-nn/src/witx.rs b/crates/wasi-nn/src/witx.rs
index d2f85d25f..ffcaccac4 100644
--- a/crates/wasi-nn/src/witx.rs
+++ b/crates/wasi-nn/src/witx.rs
@@ -35,7 +35,7 @@ mod gen {
     }
 
     /// Convert the host errors to their WITX-generated type.
-    impl<'a> types::UserErrorConversion for WasiNnCtx {
+    impl types::UserErrorConversion for WasiNnCtx {
         fn nn_errno_from_wasi_nn_error(
             &mut self,
             e: WasiNnError,
@@ -51,8 +51,8 @@ mod gen {
 }
 
 /// Wire up the WITX-generated trait to the `wasi-nn` host state.
-impl<'a> gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx {
-    fn load<'b>(
+impl gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx {
+    fn load(
         &mut self,
         builders: &gen::types::GraphBuilderArray<'_>,
         encoding: gen::types::GraphEncoding,
@@ -128,7 +128,7 @@ impl<'a> gen::wasi_ephemeral_nn::WasiEphemeralNn for WasiNnCtx {
         }
     }
 
-    fn get_output<'b>(
+    fn get_output(
         &mut self,
         exec_context_id: gen::types::GraphExecutionContext,
         index: u32,
diff --git a/crates/wasmtime/Cargo.toml b/crates/wasmtime/Cargo.toml
index 8aad132e5..5e00f9a63 100644
--- a/crates/wasmtime/Cargo.toml
+++ b/crates/wasmtime/Cargo.toml
@@ -14,10 +14,7 @@ rust-version.workspace = true
 workspace = true
 
 [package.metadata.docs.rs]
-# Docs.rs will use the `component-model` feature for documentation;
-# so this feature also passed in to the `cargo doc` invocation in CI.
-# See .github/workflows/main.yml
-features = ["component-model"]
+all-features = true
 
 [dependencies]
 wasmtime-asm-macros = { workspace = true, optional = true }
diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs
index 0436a7e76..e352ac6ad 100644
--- a/crates/wasmtime/src/compile.rs
+++ b/crates/wasmtime/src/compile.rs
@@ -122,7 +122,7 @@ pub(crate) fn build_artifacts<T: FinishedObject>(
 /// The output artifact here is the serialized object file contained within
 /// an owned mmap along with metadata about the compilation itself.
 #[cfg(feature = "component-model")]
-pub(crate) fn build_component_artifacts<'a, T: FinishedObject>(
+pub(crate) fn build_component_artifacts<T: FinishedObject>(
     engine: &Engine,
     binary: &[u8],
     _dwarf_package: Option<&[u8]>,
diff --git a/crates/wasmtime/src/runtime/func/typed.rs b/crates/wasmtime/src/runtime/func/typed.rs
index a802d941e..c72e5414b 100644
--- a/crates/wasmtime/src/runtime/func/typed.rs
+++ b/crates/wasmtime/src/runtime/func/typed.rs
@@ -533,7 +533,7 @@ unsafe impl WasmTy for Func {
     }
 
     #[inline]
-    fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
+    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
         store.store_data().contains(self.0)
     }
 
@@ -571,7 +571,7 @@ unsafe impl WasmTy for Option<Func> {
     }
 
     #[inline]
-    fn compatible_with_store<'a>(&self, store: &StoreOpaque) -> bool {
+    fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
         if let Some(f) = self {
             store.store_data().contains(f.0)
         } else {
diff --git a/crates/wasmtime/src/runtime/vm/traphandlers.rs b/crates/wasmtime/src/runtime/vm/traphandlers.rs
index 25e48560c..e68ff075f 100644
--- a/crates/wasmtime/src/runtime/vm/traphandlers.rs
+++ b/crates/wasmtime/src/runtime/vm/traphandlers.rs
@@ -227,7 +227,7 @@ pub(crate) enum TrapTest {
 /// returning them as a `Result`.
 ///
 /// Highly unsafe since `closure` won't have any dtors run.
-pub unsafe fn catch_traps<'a, F>(
+pub unsafe fn catch_traps<F>(
     signal_handler: Option<*const SignalHandler<'static>>,
     capture_backtrace: bool,
     capture_coredump: bool,
diff --git a/crates/wit-bindgen/src/lib.rs b/crates/wit-bindgen/src/lib.rs
index 1a570e107..23b9e8c4b 100644
--- a/crates/wit-bindgen/src/lib.rs
+++ b/crates/wit-bindgen/src/lib.rs
@@ -315,13 +315,13 @@ impl Wasmtime {
 
         let world = &resolve.worlds[id];
         for (name, import) in world.imports.iter() {
-            if !self.opts.only_interfaces || matches!(import, WorldItem::Interface(_)) {
+            if !self.opts.only_interfaces || matches!(import, WorldItem::Interface { .. }) {
                 self.import(resolve, id, name, import);
             }
         }
 
         for (name, export) in world.exports.iter() {
-            if !self.opts.only_interfaces || matches!(export, WorldItem::Interface(_)) {
+            if !self.opts.only_interfaces || matches!(export, WorldItem::Interface { .. }) {
                 self.export(resolve, name, export);
             }
         }
@@ -349,7 +349,7 @@ impl Wasmtime {
                     add_to_linker,
                 });
             }
-            WorldItem::Interface(id) => {
+            WorldItem::Interface { id, .. } => {
                 gen.gen.interface_last_seen_as_import.insert(*id, true);
                 if gen.gen.name_interface(resolve, *id, name, false) {
                     return;
@@ -412,7 +412,7 @@ impl Wasmtime {
                 )
             }
             WorldItem::Type(_) => unreachable!(),
-            WorldItem::Interface(id) => {
+            WorldItem::Interface { id, .. } => {
                 gen.gen.interface_last_seen_as_import.insert(*id, false);
                 gen.gen.name_interface(resolve, *id, name, true);
                 gen.current_interface = Some((*id, name, true));
diff --git a/crates/wit-bindgen/src/types.rs b/crates/wit-bindgen/src/types.rs
index b41360495..c45d3d80f 100644
--- a/crates/wit-bindgen/src/types.rs
+++ b/crates/wit-bindgen/src/types.rs
@@ -57,7 +57,7 @@ impl Types {
         {
             match item {
                 WorldItem::Function(f) => self.type_info_func(resolve, f, import),
-                WorldItem::Interface(id) => {
+                WorldItem::Interface { id, .. } => {
                     let iface = &resolve.interfaces[*id];
 
                     for (_, t) in iface.types.iter() {
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 736a1f7d7..15c1483f4 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1537,6 +1537,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-encoder]]
+version = "0.209.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-metadata]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1585,6 +1591,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-metadata]]
+version = "0.209.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-mutate]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1705,6 +1717,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmparser]]
+version = "0.209.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmprinter]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1753,6 +1771,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmprinter]]
+version = "0.209.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -2215,6 +2239,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wast]]
+version = "209.0.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wat]]
 version = "1.201.0"
 when = "2024-02-27"
@@ -2263,6 +2293,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wat]]
+version = "1.209.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -2635,6 +2671,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-component]]
+version = "0.209.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-parser]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -2683,6 +2725,12 @@ when = "2024-05-20"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-parser]]
+version = "0.209.1"
+when = "2024-05-29"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.witx]]
 version = "0.9.1"
 when = "2021-06-22"


Test patch:
diff --git a/ci/build-test-matrix.js b/ci/build-test-matrix.js
index 9bc77f219..18692260e 100644
--- a/ci/build-test-matrix.js
+++ b/ci/build-test-matrix.js
@@ -116,7 +116,7 @@ const FULL_MATRIX = [
     "target": "riscv64gc-unknown-linux-gnu",
     "gcc_package": "gcc-riscv64-linux-gnu",
     "gcc": "riscv64-linux-gnu-gcc",
-    "qemu": "qemu-riscv64 -cpu rv64,v=true,vlen=256,vext_spec=v1.0,Zfa=true,zba=true,zbb=true,zbc=true,zbs=true,zbkb=true,zcb=true -L /usr/riscv64-linux-gnu",
+    "qemu": "qemu-riscv64 -cpu rv64,v=true,vlen=256,vext_spec=v1.0,Zfa=true,zba=true,zbb=true,zbc=true,zbs=true,zbkb=true,zcb=true,x-zicond=true -L /usr/riscv64-linux-gnu",
     "qemu_target": "riscv64-linux-user",
     "name": "Test Linux riscv64",
     "filter": "linux-riscv64",
diff --git a/cranelift/filetests/filetests/egraph/i128-opts.clif b/cranelift/filetests/filetests/egraph/i128-opts.clif
index c84fc23bb..86a04b164 100644
--- a/cranelift/filetests/filetests/egraph/i128-opts.clif
+++ b/cranelift/filetests/filetests/egraph/i128-opts.clif
@@ -136,22 +136,22 @@ block0:
     ; check: return v4
 }
 
-function %mul_2u_i128(i128) -> i128 {
-block0(v0: i128):
-    v1 = iconst.i16 2
-    v2 = uextend.i128 v1
-    v3 = imul v0, v2
-    return v3
-    ; check: v4 = iadd v0, v0
-    ; check: return v4
+function %slt() -> i8 {
+block0:
+    v0 = iconst.i64 0
+    v1 = uextend.i128 v0
+    v2 = icmp slt v1, v1
+    return v2
+    ; check: v3 = iconst.i8 0
+    ; check: return
 }
 
-function %mul_neg1_i128(i128) -> i128 {
-block0(v0: i128):
-    v1 = iconst.i16 -1
-    v2 = sextend.i128 v1
-    v3 = imul v0, v2
-    return v3
-    ; check: v4 = ineg v0
-    ; check: return v4
+function %ult() -> i8 {
+block0:
+    v0 = iconst.i64 0
+    v1 = uextend.i128 v0
+    v2 = icmp ult v1, v1
+    return v2
+    ; check: v3 = iconst.i8 0
+    ; check: return
 }
diff --git a/cranelift/filetests/filetests/isa/aarch64/stack-limit.clif b/cranelift/filetests/filetests/isa/aarch64/stack-limit.clif
index 6672d37d5..49d4963f6 100644
--- a/cranelift/filetests/filetests/isa/aarch64/stack-limit.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/stack-limit.clif
@@ -15,19 +15,6 @@ block0:
 ; block0: ; offset 0x0
 ;   ret
 
-function %stack_limit_leaf_zero(i64 stack_limit) {
-block0(v0: i64):
-    return
-}
-
-; VCode:
-; block0:
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   ret
-
 function %stack_limit_gv_leaf_zero(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
@@ -45,42 +32,6 @@ block0(v0: i64):
 ; block0: ; offset 0x0
 ;   ret
 
-function %stack_limit_call_zero(i64 stack_limit) {
-    fn0 = %foo()
-block0(v0: i64):
-    call fn0()
-    return
-}
-
-; VCode:
-;   stp fp, lr, [sp, #-16]!
-;   mov fp, sp
-;   add x16, x0, #16
-;   subs xzr, sp, x16, UXTX
-;   b.lo #trap=stk_ovf
-; block0:
-;   load_ext_name x0, TestCase(%foo)+0
-;   blr x0
-;   ldp fp, lr, [sp], #16
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   stp x29, x30, [sp, #-0x10]!
-;   mov x29, sp
-;   add x16, x0, #0x10
-;   cmp sp, x16
-;   b.lo #0x30
-; block1: ; offset 0x14
-;   ldr x0, #0x1c
-;   b #0x24
-;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %foo 0
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   blr x0
-;   ldp x29, x30, [sp], #0x10
-;   ret
-;   .byte 0x1f, 0xc1, 0x00, 0x00 ; trap: stk_ovf
-
 function %stack_limit_gv_call_zero(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
@@ -125,87 +76,6 @@ block0(v0: i64):
 ;   ret
 ;   .byte 0x1f, 0xc1, 0x00, 0x00 ; trap: stk_ovf
 
-function %stack_limit(i64 stack_limit) {
-    ss0 = explicit_slot 168
-block0(v0: i64):
-    return
-}
-
-; VCode:
-;   stp fp, lr, [sp, #-16]!
-;   mov fp, sp
-;   add x16, x0, #176
-;   subs xzr, sp, x16, UXTX
-;   b.lo #trap=stk_ovf
-;   sub sp, sp, #176
-; block0:
-;   add sp, sp, #176
-;   ldp fp, lr, [sp], #16
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   stp x29, x30, [sp, #-0x10]!
-;   mov x29, sp
-;   add x16, x0, #0xb0
-;   cmp sp, x16
-;   b.lo #0x24
-;   sub sp, sp, #0xb0
-; block1: ; offset 0x18
-;   add sp, sp, #0xb0
-;   ldp x29, x30, [sp], #0x10
-;   ret
-;   .byte 0x1f, 0xc1, 0x00, 0x00 ; trap: stk_ovf
-
-function %huge_stack_limit(i64 stack_limit) {
-    ss0 = explicit_slot 400000
-block0(v0: i64):
-    return
-}
-
-; VCode:
-;   stp fp, lr, [sp, #-16]!
-;   mov fp, sp
-;   subs xzr, sp, x0, UXTX
-;   b.lo #trap=stk_ovf
-;   movz w17, #6784
-;   movk w17, w17, #6, LSL #16
-;   add x16, x0, x17, UXTX
-;   subs xzr, sp, x16, UXTX
-;   b.lo #trap=stk_ovf
-;   movz w16, #6784
-;   movk w16, w16, #6, LSL #16
-;   sub sp, sp, x16, UXTX
-; block0:
-;   movz w16, #6784
-;   movk w16, w16, #6, LSL #16
-;   add sp, sp, x16, UXTX
-;   ldp fp, lr, [sp], #16
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   stp x29, x30, [sp, #-0x10]!
-;   mov x29, sp
-;   cmp sp, x0
-;   b.lo #0x44
-;   mov w17, #0x1a80
-;   movk w17, #6, lsl #16
-;   add x16, x0, x17, uxtx
-;   cmp sp, x16
-;   b.lo #0x48
-;   mov w16, #0x1a80
-;   movk w16, #6, lsl #16
-;   sub sp, sp, x16
-; block1: ; offset 0x30
-;   mov w16, #0x1a80
-;   movk w16, #6, lsl #16
-;   add sp, sp, x16
-;   ldp x29, x30, [sp], #0x10
-;   ret
-;   .byte 0x1f, 0xc1, 0x00, 0x00 ; trap: stk_ovf
-;   .byte 0x1f, 0xc1, 0x00, 0x00 ; trap: stk_ovf
-
 function %limit_preamble(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
diff --git a/cranelift/filetests/filetests/isa/riscv64/bitcast-scalar-vector.clif b/cranelift/filetests/filetests/isa/riscv64/bitcast-scalar-vector.clif
new file mode 100644
index 000000000..5658fbe5a
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/bitcast-scalar-vector.clif
@@ -0,0 +1,236 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_v
+
+function %bitcast_vec_to_i128(i64x2) -> i128 {
+block0(v0: i64x2):
+    v1 = bitcast.i128 little v0
+    return v1
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v8,-16(incoming_arg) #avl=16, #vtype=(e8, m1, ta, ma)
+;   vmv.x.s a0,v8 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vslidedown.vi v12,v8,1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmv.x.s a1,v12 #avl=2, #vtype=(e64, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   addi t6, sp, 0x10
+;   .byte 0x07, 0x84, 0x0f, 0x02
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0x25, 0x80, 0x42
+;   .byte 0x57, 0xb6, 0x80, 0x3e
+;   .byte 0xd7, 0x25, 0xc0, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %bitcast_i128_to_vec(i128) -> i64x2 {
+block0(v0: i128):
+    v1 = bitcast.i64x2 little v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.s.x v12,a1 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vmv1r.v v14,v12
+;   vslide1up.vx v14,v12,a0 #avl=2, #vtype=(e64, m1, ta, ma)
+;   vse8.v v14,0(a2) #avl=16, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0x70, 0x81, 0xcd
+;   .byte 0x57, 0xe6, 0x05, 0x42
+;   .byte 0x57, 0x37, 0xc0, 0x9e
+;   .byte 0x57, 0x67, 0xc5, 0x3a
+;   .byte 0x57, 0x70, 0x08, 0xcc
+;   .byte 0x27, 0x07, 0x06, 0x02
+;   ret
+
+function %bitcast_vec_to_i64(i32x2) -> i64 {
+block0(v0: i32x2):
+    v1 = bitcast.i64 little v0
+    return v1
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v8,-16(incoming_arg) #avl=8, #vtype=(e8, m1, ta, ma)
+;   vmv.x.s a0,v8 #avl=1, #vtype=(e64, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x04, 0xcc
+;   addi t6, sp, 0x10
+;   .byte 0x07, 0x84, 0x0f, 0x02
+;   .byte 0x57, 0xf0, 0x80, 0xcd
+;   .byte 0x57, 0x25, 0x80, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %bitcast_i64_to_vec(i64) -> i32x2 {
+block0(v0: i64):
+    v1 = bitcast.i32x2 little v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vmv.s.x v11,a0 #avl=1, #vtype=(e64, m1, ta, ma)
+;   vse8.v v11,0(a1) #avl=8, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0xf0, 0x80, 0xcd
+;   .byte 0xd7, 0x65, 0x05, 0x42
+;   .byte 0x57, 0x70, 0x04, 0xcc
+;   .byte 0xa7, 0x85, 0x05, 0x02
+;   ret
+
+function %bitcast_vec_to_f64(i32x2) -> f64 {
+block0(v0: i32x2):
+    v1 = bitcast.f64 little v0
+    return v1
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v8,-16(incoming_arg) #avl=8, #vtype=(e8, m1, ta, ma)
+;   vfmv.f.s fa0,v8 #avl=1, #vtype=(e64, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x04, 0xcc
+;   addi t6, sp, 0x10
+;   .byte 0x07, 0x84, 0x0f, 0x02
+;   .byte 0x57, 0xf0, 0x80, 0xcd
+;   .byte 0x57, 0x15, 0x80, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %bitcast_f64_to_vec(f64) -> i32x2 {
+block0(v0: f64):
+    v1 = bitcast.i32x2 little v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vfmv.s.f v11,fa0 #avl=1, #vtype=(e64, m1, ta, ma)
+;   vse8.v v11,0(a0) #avl=8, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0xf0, 0x80, 0xcd
+;   .byte 0xd7, 0x55, 0x05, 0x42
+;   .byte 0x57, 0x70, 0x04, 0xcc
+;   .byte 0xa7, 0x05, 0x05, 0x02
+;   ret
+
+function %bitcast_i16x2_to_f32(i16x2) -> f32 {
+block0(v0: i16x2):
+    v1 = bitcast.f32 little v0
+    return v1
+}
+
+; VCode:
+;   addi sp,sp,-16
+;   sd ra,8(sp)
+;   sd fp,0(sp)
+;   mv fp,sp
+; block0:
+;   vle8.v v8,-16(incoming_arg) #avl=4, #vtype=(e8, m1, ta, ma)
+;   vfmv.f.s fa0,v8 #avl=1, #vtype=(e32, m1, ta, ma)
+;   ld ra,8(sp)
+;   ld fp,0(sp)
+;   addi sp,sp,16
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   addi sp, sp, -0x10
+;   sd ra, 8(sp)
+;   sd s0, 0(sp)
+;   mv s0, sp
+; block1: ; offset 0x10
+;   .byte 0x57, 0x70, 0x02, 0xcc
+;   addi t6, sp, 0x10
+;   .byte 0x07, 0x84, 0x0f, 0x02
+;   .byte 0x57, 0xf0, 0x00, 0xcd
+;   .byte 0x57, 0x15, 0x80, 0x42
+;   ld ra, 8(sp)
+;   ld s0, 0(sp)
+;   addi sp, sp, 0x10
+;   ret
+
+function %bitcast_f32_to_i16x2(f32) -> i16x2 {
+block0(v0: f32):
+    v1 = bitcast.i16x2 little v0
+    return v1
+}
+
+; VCode:
+; block0:
+;   vfmv.s.f v11,fa0 #avl=1, #vtype=(e32, m1, ta, ma)
+;   vse8.v v11,0(a0) #avl=4, #vtype=(e8, m1, ta, ma)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x57, 0xf0, 0x00, 0xcd
+;   .byte 0xd7, 0x55, 0x05, 0x42
+;   .byte 0x57, 0x70, 0x02, 0xcc
+;   .byte 0xa7, 0x05, 0x05, 0x02
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif b/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif
index 7a11f1876..fe934fc7c 100644
--- a/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/stack-limit.clif
@@ -16,19 +16,6 @@ block0:
 ; block0: ; offset 0x0
 ;   ret
 
-function %stack_limit_leaf_zero(i64 stack_limit) {
-block0(v0: i64):
-    return
-}
-
-; VCode:
-; block0:
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   ret
-
 function %stack_limit_gv_leaf_zero(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
@@ -46,49 +33,6 @@ block0(v0: i64):
 ; block0: ; offset 0x0
 ;   ret
 
-function %stack_limit_call_zero(i64 stack_limit) {
-    fn0 = %foo()
-block0(v0: i64):
-    call fn0()
-    return
-}
-
-; VCode:
-;   addi sp,sp,-16
-;   sd ra,8(sp)
-;   sd fp,0(sp)
-;   mv fp,sp
-;   addi t6,a0,16
-;   trap_if stk_ovf##(sp ult t6)
-; block0:
-;   load_sym a0,%foo+0
-;   callind a0
-;   ld ra,8(sp)
-;   ld fp,0(sp)
-;   addi sp,sp,16
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   addi sp, sp, -0x10
-;   sd ra, 8(sp)
-;   sd s0, 0(sp)
-;   mv s0, sp
-;   addi t6, a0, 0x10
-;   bgeu sp, t6, 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: stk_ovf
-; block1: ; offset 0x1c
-;   auipc a0, 0
-;   ld a0, 0xc(a0)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %foo 0
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   jalr a0
-;   ld ra, 8(sp)
-;   ld s0, 0(sp)
-;   addi sp, sp, 0x10
-;   ret
-
 function %stack_limit_gv_call_zero(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
@@ -140,104 +84,6 @@ block0(v0: i64):
 ;   addi sp, sp, 0x10
 ;   ret
 
-function %stack_limit(i64 stack_limit) {
-    ss0 = explicit_slot 168
-block0(v0: i64):
-    return
-}
-
-; VCode:
-;   addi sp,sp,-16
-;   sd ra,8(sp)
-;   sd fp,0(sp)
-;   mv fp,sp
-;   addi t6,a0,176
-;   trap_if stk_ovf##(sp ult t6)
-;   addi sp,sp,-176
-; block0:
-;   addi sp,sp,176
-;   ld ra,8(sp)
-;   ld fp,0(sp)
-;   addi sp,sp,16
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   addi sp, sp, -0x10
-;   sd ra, 8(sp)
-;   sd s0, 0(sp)
-;   mv s0, sp
-;   addi t6, a0, 0xb0
-;   bgeu sp, t6, 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: stk_ovf
-;   addi sp, sp, -0xb0
-; block1: ; offset 0x20
-;   addi sp, sp, 0xb0
-;   ld ra, 8(sp)
-;   ld s0, 0(sp)
-;   addi sp, sp, 0x10
-;   ret
-
-function %huge_stack_limit(i64 stack_limit) {
-    ss0 = explicit_slot 400000
-block0(v0: i64):
-    return
-}
-
-; VCode:
-;   addi sp,sp,-16
-;   sd ra,8(sp)
-;   sd fp,0(sp)
-;   mv fp,sp
-;   trap_if stk_ovf##(sp ult a0)
-;   lui t5,98
-;   addi t5,t5,-1408
-;   add t6,t5,a0
-;   trap_if stk_ovf##(sp ult t6)
-;   lui a0,98
-;   addi a0,a0,-1408
-;   call %Probestack
-;   lui t6,-98
-;   addi t6,t6,1408
-;   add sp,sp,t6
-; block0:
-;   lui t6,98
-;   addi t6,t6,-1408
-;   add sp,sp,t6
-;   ld ra,8(sp)
-;   ld fp,0(sp)
-;   addi sp,sp,16
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   addi sp, sp, -0x10
-;   sd ra, 8(sp)
-;   sd s0, 0(sp)
-;   mv s0, sp
-;   bgeu sp, a0, 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: stk_ovf
-;   lui t5, 0x62
-;   addi t5, t5, -0x580
-;   add t6, t5, a0
-;   bgeu sp, t6, 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: stk_ovf
-;   lui a0, 0x62
-;   addi a0, a0, -0x580
-;   auipc ra, 0 ; reloc_external RiscvCallPlt %Probestack 0
-;   jalr ra
-;   lui t6, 0xfff9e
-;   addi t6, t6, 0x580
-;   add sp, sp, t6
-; block1: ; offset 0x48
-;   lui t6, 0x62
-;   addi t6, t6, -0x580
-;   add sp, sp, t6
-;   ld ra, 8(sp)
-;   ld s0, 0(sp)
-;   addi sp, sp, 0x10
-;   ret
-
 function %limit_preamble(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
diff --git a/cranelift/filetests/filetests/isa/riscv64/zicond.clif b/cranelift/filetests/filetests/isa/riscv64/zicond.clif
new file mode 100644
index 000000000..00b0c37dd
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/zicond.clif
@@ -0,0 +1,185 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_zicond
+
+
+function %select_zero(i64, i64) -> i64 {
+block0(v0: i64, v1: i64):
+  v2 = iconst.i64 0
+  v3 = select.i64 v0, v1, v2
+  return v3
+}
+
+; VCode:
+; block0:
+;   czero.eqz a0,a1,a0
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x33, 0xd5, 0xa5, 0x0e
+;   ret
+
+function %select_zero_icmp_neq(i64, i64) -> i64 {
+block0(v0: i64, v1: i64):
+  v2 = iconst.i64 0
+  v3 = icmp.i64 ne v0, v2
+  v4 = select.i64 v3, v1, v2
+  return v4
+}
+
+; VCode:
+; block0:
+;   czero.eqz a0,a1,a0
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x33, 0xd5, 0xa5, 0x0e
+;   ret
+
+function %select_zero_icmp_neq_reverse(i64, i64) -> i64 {
+block0(v0: i64, v1: i64):
+  v2 = iconst.i64 0
+  v3 = icmp.i64 ne v2, v0
+  v4 = select.i64 v3, v2, v1
+  return v4
+}
+
+; VCode:
+; block0:
+;   czero.nez a0,a1,a0
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x33, 0xf5, 0xa5, 0x0e
+;   ret
+
+function %select_zero_icmp_eqz(i64, i64) -> i64 {
+block0(v0: i64, v1: i64):
+  v2 = iconst.i64 0
+  v3 = icmp.i64 eq v0, v2
+  v4 = select.i64 v3, v1, v2
+  return v4
+}
+
+; VCode:
+; block0:
+;   czero.nez a0,a1,a0
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x33, 0xf5, 0xa5, 0x0e
+;   ret
+
+
+function %select_zero_icmp_eqz_reverse(i64, i64) -> i64 {
+block0(v0: i64, v1: i64):
+  v2 = iconst.i64 0
+  v3 = icmp.i64 eq v2, v0
+  v4 = select.i64 v3, v2, v1
+  return v4
+}
+
+; VCode:
+; block0:
+;   czero.eqz a0,a1,a0
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x33, 0xd5, 0xa5, 0x0e
+;   ret
+
+
+function %select_icmp_eq_zero(i64, i64, i64) -> i64 {
+block0(v0: i64, v1: i64, v2: i64):
+  v3 = iconst.i64 0
+  v4 = icmp.i64 eq v0, v3
+  v5 = select.i64 v4, v1, v2
+  return v5
+}
+
+; VCode:
+; block0:
+;   czero.nez a4,a1,a0
+;   czero.eqz a0,a2,a0
+;   or a0,a4,a0
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x33, 0xf7, 0xa5, 0x0e
+;   .byte 0x33, 0x55, 0xa6, 0x0e
+;   or a0, a4, a0
+;   ret
+
+function %select_icmp_ne_zero(i64, i64, i64) -> i64 {
+block0(v0: i64, v1: i64, v2: i64):
+  v3 = iconst.i64 0
+  v4 = icmp.i64 ne v0, v3
+  v5 = select.i64 v4, v1, v2
+  return v5
+}
+
+; VCode:
+; block0:
+;   czero.eqz a4,a1,a0
+;   czero.nez a0,a2,a0
+;   or a0,a4,a0
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   .byte 0x33, 0xd7, 0xa5, 0x0e
+;   .byte 0x33, 0x75, 0xa6, 0x0e
+;   or a0, a4, a0
+;   ret
+
+
+function %select_icmp_sle(i64, i64, i64, i64) -> i64 {
+block0(v0: i64, v1: i64, v2: i64, v3: i64):
+  v4 = icmp.i64 slt v0, v1
+  v5 = select.i64 v4, v2, v3
+  return v5
+}
+
+; VCode:
+; block0:
+;   slt a5,a0,a1
+;   czero.eqz a1,a2,a5
+;   czero.nez a3,a3,a5
+;   or a0,a1,a3
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   slt a5, a0, a1
+;   .byte 0xb3, 0x55, 0xf6, 0x0e
+;   .byte 0xb3, 0xf6, 0xf6, 0x0e
+;   or a0, a1, a3
+;   ret
+
+;; This can trigger a stack overflow with if the rules don't prevent
+;; this situation.
+function %check_rule_stack_overflow(i64, i64, i64) -> i64 {
+block0(v0: i64, v1: i64, v2: i64):
+  v3 = iconst.i64 0
+  v4 = icmp.i64 ne v3, v3
+  v5 = select.i64 v4, v3, v3
+  return v5
+}
+
+; VCode:
+; block0:
+;   li a1,0
+;   czero.nez a0,a1,zero
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   mv a1, zero
+;   .byte 0x33, 0xf5, 0x05, 0x0e
+;   ret
diff --git a/cranelift/filetests/filetests/isa/s390x/stack-limit.clif b/cranelift/filetests/filetests/isa/s390x/stack-limit.clif
index b21a40ae4..7b31db18f 100644
--- a/cranelift/filetests/filetests/isa/s390x/stack-limit.clif
+++ b/cranelift/filetests/filetests/isa/s390x/stack-limit.clif
@@ -14,19 +14,6 @@ block0:
 ; block0: ; offset 0x0
 ;   br %r14
 
-function %stack_limit_leaf_zero(i64 stack_limit) {
-block0(v0: i64):
-    return
-}
-
-; VCode:
-; block0:
-;   br %r14
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   br %r14
-
 function %stack_limit_gv_leaf_zero(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
@@ -44,41 +31,6 @@ block0(v0: i64):
 ; block0: ; offset 0x0
 ;   br %r14
 
-function %stack_limit_call_zero(i64 stack_limit) {
-    fn0 = %foo()
-block0(v0: i64):
-    call fn0()
-    return
-}
-
-; VCode:
-;   la %r1, 160(%r2)
-;   clgrtle %r15, %r1
-;   stmg %r14, %r15, 112(%r15)
-;   aghi %r15, -160
-; block0:
-;   bras %r1, 12 ; data %foo + 0 ; lg %r2, 0(%r1)
-;   basr %r14, %r2
-;   lmg %r14, %r15, 272(%r15)
-;   br %r14
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   la %r1, 0xa0(%r2)
-;   clgrtle %r15, %r1 ; trap: stk_ovf
-;   stmg %r14, %r15, 0x70(%r15)
-;   aghi %r15, -0xa0
-; block1: ; offset 0x12
-;   bras %r1, 0x1e
-;   .byte 0x00, 0x00 ; reloc_external Abs8 %foo 0
-;   .byte 0x00, 0x00
-;   .byte 0x00, 0x00
-;   .byte 0x00, 0x00
-;   lg %r2, 0(%r1)
-;   basr %r14, %r2
-;   lmg %r14, %r15, 0x110(%r15)
-;   br %r14
-
 function %stack_limit_gv_call_zero(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
@@ -122,81 +74,6 @@ block0(v0: i64):
 ;   lmg %r14, %r15, 0x110(%r15)
 ;   br %r14
 
-function %stack_limit(i64 stack_limit) {
-    ss0 = explicit_slot 168
-block0(v0: i64):
-    return
-}
-
-; VCode:
-;   la %r1, 168(%r2)
-;   clgrtle %r15, %r1
-;   aghi %r15, -168
-; block0:
-;   aghi %r15, 168
-;   br %r14
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   la %r1, 0xa8(%r2)
-;   clgrtle %r15, %r1 ; trap: stk_ovf
-;   aghi %r15, -0xa8
-; block1: ; offset 0xc
-;   aghi %r15, 0xa8
-;   br %r14
-
-function %large_stack_limit(i64 stack_limit) {
-    ss0 = explicit_slot 400000
-block0(v0: i64):
-    return
-}
-
-; VCode:
-;   clgrtle %r15, %r2
-;   lay %r1, 400000(%r2)
-;   clgrtle %r15, %r1
-;   agfi %r15, -400000
-; block0:
-;   agfi %r15, 400000
-;   br %r14
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   clgrtle %r15, %r2 ; trap: stk_ovf
-;   lay %r1, 0x61a80(%r2)
-;   clgrtle %r15, %r1 ; trap: stk_ovf
-;   agfi %r15, -0x61a80
-; block1: ; offset 0x14
-;   agfi %r15, 0x61a80
-;   br %r14
-
-function %huge_stack_limit(i64 stack_limit) {
-    ss0 = explicit_slot 4000000
-block0(v0: i64):
-    return
-}
-
-; VCode:
-;   clgrtle %r15, %r2
-;   lgr %r1, %r2
-;   algfi %r1, 4000000
-;   clgrtle %r15, %r1
-;   agfi %r15, -4000000
-; block0:
-;   agfi %r15, 4000000
-;   br %r14
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   clgrtle %r15, %r2 ; trap: stk_ovf
-;   lgr %r1, %r2
-;   algfi %r1, 0x3d0900
-;   clgrtle %r15, %r1 ; trap: stk_ovf
-;   agfi %r15, -0x3d0900
-; block1: ; offset 0x18
-;   agfi %r15, 0x3d0900
-;   br %r14
-
 function %limit_preamble(i64 vmctx) {
     gv0 = vmctx
     gv1 = load.i64 notrap aligned gv0
diff --git a/cranelift/filetests/filetests/parser/call.clif b/cranelift/filetests/filetests/parser/call.clif
index 1334b07af..927e441ff 100644
--- a/cranelift/filetests/filetests/parser/call.clif
+++ b/cranelift/filetests/filetests/parser/call.clif
@@ -84,11 +84,11 @@ block0:
 ; check: return
 
 ; Special purpose function arguments
-function %special1(i32 sret, i32 stack_limit) -> i32 vmctx {
-block0(v1: i32, v2: i32):
+function %special1(i32 sret) -> i32 vmctx {
+block0(v1: i32):
     return v1
 }
-; check: function %special1(i32 sret, i32 stack_limit) -> i32 vmctx fast {
-; check: block0(v1: i32, v2: i32):
+; check: function %special1(i32 sret) -> i32 vmctx fast {
+; check: block0(v1: i32):
 ; check:     return v1
 ; check: }
diff --git a/cranelift/filetests/filetests/parser/tiny.clif b/cranelift/filetests/filetests/parser/tiny.clif
index 8906141cb..900e3ff70 100644
--- a/cranelift/filetests/filetests/parser/tiny.clif
+++ b/cranelift/filetests/filetests/parser/tiny.clif
@@ -10,6 +10,16 @@ block0:
 ; nextln:     trap user0
 ; nextln: }
 
+; The same small function, but with empty parens on the first block.
+function %minimal_empty_parens() {
+block0():
+    trap user0
+}
+; sameln: function %minimal_empty_parens() fast {
+; nextln: block0:
+; nextln:     trap user0
+; nextln: }
+
 ; Create and use values.
 ; Polymorphic instructions with type suffix.
 function %ivalues() {
diff --git a/cranelift/filetests/filetests/runtests/select.clif b/cranelift/filetests/filetests/runtests/select.clif
index 7740093e2..e53e240e2 100644
--- a/cranelift/filetests/filetests/runtests/select.clif
+++ b/cranelift/filetests/filetests/runtests/select.clif
@@ -4,6 +4,7 @@ target aarch64
 target s390x
 target x86_64
 target riscv64
+target riscv64 has_zicond
 target riscv64 has_c has_zcb
 
 function %select_eq_f32(f32, f32) -> i32 {
@@ -139,3 +140,43 @@ block0(v0: i32):
 ; run: %select_overflow_i32(2) == 1
 ; run: %select_overflow_i32(1) == 0
 ; run: %select_overflow_i32(98) == 1
+
+function %select_zero(i64, i64) -> i64 {
+block0(v0: i64, v1: i64):
+  v2 = iconst.i64 0
+  v3 = select.i64 v0, v1, v2
+  return v3
+}
+; run: %select_zero(0, 42) == 0
+; run: %select_zero(42, 42) == 42
+
+function %select_zero_icmp_neq_zero(i64, i64) -> i64 {
+block0(v0: i64, v1: i64):
+  v2 = iconst.i64 0
+  v3 = icmp.i64 ne v0, v2
+  v4 = select.i64 v3, v1, v2
+  return v4
+}
+; run: %select_zero_icmp_neq_zero(0, 42) == 0
+; run: %select_zero_icmp_neq_zero(42, 42) == 42
+
+function %select_icmp_eq_zero(i64, i64, i64) -> i64 {
+block0(v0: i64, v1: i64, v2: i64):
+  v3 = iconst.i64 0
+  v4 = icmp.i64 eq v0, v3
+  v5 = select.i64 v4, v1, v2
+  return v5
+}
+; run: %select_icmp_eq_zero(0, 42, 35) == 42
+; run: %select_icmp_eq_zero(42, 42, 35) == 35
+
+function %select_icmp_ne_zero(i64, i64, i64) -> i64 {
+block0(v0: i64, v1: i64, v2: i64):
+  v3 = iconst.i64 0
+  v4 = icmp.i64 ne v0, v3
+  v5 = select.i64 v4, v1, v2
+  return v5
+}
+; run: %select_icmp_ne_zero(0, 42, 35) == 35
+; run: %select_icmp_ne_zero(42, 42, 35) == 42
+
diff --git a/cranelift/filetests/filetests/runtests/simd-bitcast-64bit.clif b/cranelift/filetests/filetests/runtests/simd-bitcast-64bit.clif
new file mode 100644
index 000000000..e58678025
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/simd-bitcast-64bit.clif
@@ -0,0 +1,36 @@
+test run
+target riscv64 has_v
+target riscv64 has_v has_c has_zcb
+
+function %bitcast_i32x2_to_i64(i32x2) -> i64 {
+block0(v0: i32x2):
+    v1 = bitcast.i64 little v0
+    return v1
+}
+; run: %bitcast_i32x2_to_i64([0xBEEF 0xC0FFEE]) == 0x00c0ffee_0000beef
+; run: %bitcast_i32x2_to_i64([-1 127]) == 0x0000007f_ffffffff
+
+
+function %bitcast_i64_to_i32x2(i64) -> i32x2 {
+block0(v0: i64):
+    v1 = bitcast.i32x2 little v0
+    return v1
+}
+; run: %bitcast_i64_to_i32x2(0x00c0ffee_0000beef) == [0xBEEF 0xC0FFEE]
+; run: %bitcast_i64_to_i32x2(0x0000007f_ffffffff) == [-1 127]
+
+function %bitcast_i32x2_to_f64(i32x2) -> f64 {
+block0(v0: i32x2):
+    v1 = bitcast.f64 little v0
+    return v1
+}
+; run: %bitcast_i32x2_to_f64([0xBEEF 0xC0FFEE]) == 0x1.0ffee0000beefp-1011
+; run: %bitcast_i32x2_to_f64([-1 127]) == 0x0.0007fffffffffp-1022
+
+function %bitcast_f64_to_i32x2(f64) -> i32x2 {
+block0(v0: f64):
+    v1 = bitcast.i32x2 little v0
+    return v1
+}
+; run: %bitcast_f64_to_i32x2(0x1.0ffee0000beefp-1011) == [0xBEEF 0xC0FFEE]
+; run: %bitcast_f64_to_i32x2(0x0.0007fffffffffp-1022) == [-1 127]
diff --git a/cranelift/filetests/filetests/runtests/simd-bitcast-i128.clif b/cranelift/filetests/filetests/runtests/simd-bitcast-i128.clif
new file mode 100644
index 000000000..08f1d63a0
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/simd-bitcast-i128.clif
@@ -0,0 +1,20 @@
+test run
+target riscv64 has_v
+target riscv64 has_v has_c has_zcb
+
+function %bitcast_i64x2_to_i128(i64x2) -> i128 {
+block0(v0: i64x2):
+    v1 = bitcast.i128 little v0
+    return v1
+}
+; run: %bitcast_i64x2_to_i128([0xBEEF 0xC0FFEE]) == 0x0000000000c0ffee_000000000000beef
+; run: %bitcast_i64x2_to_i128([-1 127]) == 0x000000000000007f_ffffffffffffffff
+
+
+function %bitcast_i128_to_i64x2(i128) -> i64x2 {
+block0(v0: i128):
+    v1 = bitcast.i64x2 little v0
+    return v1
+}
+; run: %bitcast_i128_to_i64x2(0x0000000000c0ffee_000000000000beef) == [0xBEEF 0xC0FFEE]
+; run: %bitcast_i128_to_i64x2(0x000000000000007f_ffffffffffffffff) == [-1 127]
diff --git a/crates/test-macros/Cargo.toml b/crates/test-macros/Cargo.toml
new file mode 100644
index 000000000..c4871a359
--- /dev/null
+++ b/crates/test-macros/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+description = "Macros for testing Wasmtime"
+name = "wasmtime-test-macros"
+license = "Apache-2.0 WITH LLVM-exception"
+version = "0.0.0"
+authors.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+publish = false
+
+[lib]
+proc-macro = true
+test = false
+doctest = false
+
+[lints]
+workspace = true
+
+[dependencies]
+quote = "1.0"
+proc-macro2 = "1.0"
+syn = { workspace = true, features = ["full"] }
+anyhow = { workspace = true }
diff --git a/crates/test-macros/src/lib.rs b/crates/test-macros/src/lib.rs
new file mode 100644
index 000000000..cd2a5a069
--- /dev/null
+++ b/crates/test-macros/src/lib.rs
@@ -0,0 +1,178 @@
+//! Wasmtime test macro.
+//!
+//! This macro is a helper to define tests that exercise multiple configuration
+//! combinations for Wasmtime. Currently, only compiler strategies are
+//! supported.
+//!
+//! Usage
+//!
+//! #[wasmtime_test(strategies(Cranelift, Winch))]
+//! fn my_test(config: &mut Config) -> Result<()> {
+//!    Ok(())
+//! }
+use proc_macro::TokenStream;
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{
+    braced,
+    parse::{Parse, ParseStream},
+    parse_macro_input, token, Attribute, Ident, Result, ReturnType, Signature, Visibility,
+};
+
+/// Test configuration.
+struct TestConfig {
+    /// Supported compiler strategies.
+    strategies: Vec<(String, Ident)>,
+}
+
+impl TestConfig {
+    /// Validate the test configuration.
+    /// Only the number of strategies is validated, as this avoid expansions of
+    /// empty strategies or more strategies than supported.
+    ///
+    /// The supported strategies are validated inline when parsing.
+    fn validate(&self) -> anyhow::Result<()> {
+        if self.strategies.len() > 2 {
+            Err(anyhow::anyhow!("Expected at most 2 strategies"))
+        } else if self.strategies.len() == 0 {
+            Err(anyhow::anyhow!("Expected at least 1 strategy"))
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl Default for TestConfig {
+    fn default() -> Self {
+        Self { strategies: vec![] }
+    }
+}
+
+/// A generic function body represented as a braced [`TokenStream`].
+struct Block {
+    brace: token::Brace,
+    rest: proc_macro2::TokenStream,
+}
+
+impl Parse for Block {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let content;
+        Ok(Self {
+            brace: braced!(content in input),
+            rest: content.parse()?,
+        })
+    }
+}
+
+impl ToTokens for Block {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        self.brace.surround(tokens, |tokens| {
+            tokens.append_all(self.rest.clone());
+        });
+    }
+}
+
+/// Custom function parser.
+/// Parses the function's attributes, visibility and signature, leaving the
+/// block as an opaque [`TokenStream`].
+struct Fn {
+    attrs: Vec<Attribute>,
+    visibility: Visibility,
+    sig: Signature,
+    body: Block,
+}
+
+impl Parse for Fn {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let attrs = input.call(Attribute::parse_outer)?;
+        let visibility: Visibility = input.parse()?;
+        let sig: Signature = input.parse()?;
+        let body: Block = input.parse()?;
+
+        Ok(Self {
+            attrs,
+            visibility,
+            sig,
+            body,
+        })
+    }
+}
+
+impl ToTokens for Fn {
+    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+        for attr in &self.attrs {
+            attr.to_tokens(tokens);
+        }
+        self.visibility.to_tokens(tokens);
+        self.sig.to_tokens(tokens);
+        self.body.to_tokens(tokens);
+    }
+}
+
+#[proc_macro_attribute]
+pub fn wasmtime_test(attrs: TokenStream, item: TokenStream) -> TokenStream {
+    let mut test_config = TestConfig::default();
+
+    let config_parser = syn::meta::parser(|meta| {
+        if meta.path.is_ident("strategies") {
+            meta.parse_nested_meta(|meta| {
+                if meta.path.is_ident("Winch") || meta.path.is_ident("Cranelift") {
+                    let id = meta.path.require_ident()?.clone();
+                    test_config.strategies.push((id.to_string(), id));
+                    Ok(())
+                } else {
+                    Err(meta.error("Unknown strategy"))
+                }
+            })?;
+
+            test_config.validate().map_err(|e| meta.error(e))
+        } else {
+            Err(meta.error("Unsupported attributes"))
+        }
+    });
+
+    parse_macro_input!(attrs with config_parser);
+
+    match expand(&test_config, parse_macro_input!(item as Fn)) {
+        Ok(tok) => tok,
+        Err(e) => e.into_compile_error().into(),
+    }
+}
+
+fn expand(test_config: &TestConfig, func: Fn) -> Result<TokenStream> {
+    let mut tests = vec![quote! { #func }];
+    let attrs = &func.attrs;
+
+    for (strategy_name, ident) in &test_config.strategies {
+        // Winch currently only offers support for x64.
+        let target = if strategy_name == "Winch" {
+            quote! { #[cfg(target_arch = "x86_64")] }
+        } else {
+            quote! {}
+        };
+        let func_name = &func.sig.ident;
+        let ret = match &func.sig.output {
+            ReturnType::Default => quote! { () },
+            ReturnType::Type(_, ty) => quote! { -> #ty },
+        };
+        let test_name = Ident::new(
+            &format!("{}_{}", strategy_name.to_lowercase(), func_name),
+            func_name.span(),
+        );
+        let tok = quote! {
+            #[test]
+            #target
+            #(#attrs)*
+            fn #test_name() #ret {
+                let mut config = Config::new();
+                config.strategy(Strategy::#ident);
+                #func_name(&mut config)
+            }
+        };
+
+        tests.push(tok);
+    }
+    Ok(quote! {
+        #(#tests)*
+    }
+    .into())
+}
diff --git a/tests/all/func.rs b/tests/all/func.rs
index f44e8532f..70fd71834 100644
--- a/tests/all/func.rs
+++ b/tests/all/func.rs
@@ -3,10 +3,11 @@ use std::sync::atomic::Ordering;
 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
 use std::sync::Arc;
 use wasmtime::*;
+use wasmtime_test_macros::wasmtime_test;
 
-#[test]
+#[wasmtime_test(strategies(Cranelift, Winch))]
 #[cfg_attr(miri, ignore)]
-fn call_wasm_to_wasm() -> Result<()> {
+fn call_wasm_to_wasm(config: &mut Config) -> Result<()> {
     let wasm = wat::parse_str(
         r#"
           (module
@@ -21,7 +22,8 @@ fn call_wasm_to_wasm() -> Result<()> {
           )
         "#,
     )?;
-    let mut store = Store::<()>::default();
+    let engine = Engine::new(&config)?;
+    let mut store = Store::<()>::new(&engine, ());
     let module = Module::new(store.engine(), &wasm)?;
     let instance = Instance::new(&mut store, &module, &[])?;
     let func = instance