Repository: bytecodealliance/wasmtime
Pull request #5978 (2023-03-10 13:45:44)

cranelift-entity: more efficient `EntitySet` implementation
As discussed on https://github.com/bytecodealliance/wasmtime/issues/5804, this PR replaces `u8` elements in `EntitySet` with `usize` elements.

I didn't add doc-tests. If I did so, I needed to add a piece of code like below to doc-tests for every method, which looks redundant to me. I think there's no need to remove the existing tests.
Let me know if something in my opinion is wrong.

```rust
// `EntityRef` impl for testing.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct E(u32);

impl EntityRef for E {
    fn new(i: usize) -> Self {
        E(i as u32)
    }
    fn index(self) -> usize {
        self.0 as usize
    }
}

let mut m = EntitySet::new();
...
```

------

I realized that, in `wasmtime/cranelift/entity`, `cargo test` fails on some tests below.

```
running 52 tests
...omitted...

failures:

---- tests::basic_entity::cannot_construct_from_reserved_u32 stdout ----
note: test did not panic as expected
---- tests::basic_entity::cannot_construct_from_reserved_usize stdout ----
note: test did not panic as expected
---- tests::other_entity::cannot_construct_from_reserved_u32 stdout ----
note: test did not panic as expected
---- tests::other_entity::cannot_construct_from_reserved_usize stdout ----
note: test did not panic as expected
---- tests::prefix_entity::cannot_construct_from_reserved_u32 stdout ----
note: test did not panic as expected
---- tests::prefix_entity::cannot_construct_from_reserved_usize stdout ----
note: test did not panic as expected

failures:
    tests::basic_entity::cannot_construct_from_reserved_u32
    tests::basic_entity::cannot_construct_from_reserved_usize
    tests::other_entity::cannot_construct_from_reserved_u32
    tests::other_entity::cannot_construct_from_reserved_usize
    tests::prefix_entity::cannot_construct_from_reserved_u32
    tests::prefix_entity::cannot_construct_from_reserved_usize

test result: FAILED. 46 passed; 6 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```

Is this an expected behavior?


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 94d046acc..f61028eb3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3298,9 +3298,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-mutate"
-version = "0.2.21"
+version = "0.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92ada47eda4f13d5dd83b77af7751dd44f1ab8120c01f2fca5b78b49080cf82c"
+checksum = "956f9fd2f785491bdb243ae608c53fd6bb9956f9e174c1fc5a77354fce396698"
 dependencies = [
  "egg",
  "log",
@@ -3383,9 +3383,9 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.2.53"
+version = "0.2.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa44d546e4e4479f2e91035fa497c0a05cffbf22413ad05bf0b06a789b9118f"
+checksum = "2dc17ae63836d010a2bf001c26a5fedbb9a05e5f71117fb63e0ab878bfbe1ca3"
 dependencies = [
  "anyhow",
  "wasmparser",
@@ -3533,6 +3533,7 @@ dependencies = [
  "wasmtime-component-util",
  "wasmtime-cranelift",
  "wasmtime-environ",
+ "wasmtime-explorer",
  "wasmtime-runtime",
  "wasmtime-wasi",
  "wasmtime-wasi-crypto",
@@ -3647,6 +3648,19 @@ dependencies = [
  "wat",
 ]
 
+[[package]]
+name = "wasmtime-explorer"
+version = "8.0.0"
+dependencies = [
+ "anyhow",
+ "capstone",
+ "serde",
+ "serde_json",
+ "target-lexicon",
+ "wasmprinter",
+ "wasmtime",
+]
+
 [[package]]
 name = "wasmtime-fiber"
 version = "8.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index 908bbb084..f2634cffc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,6 +27,7 @@ wasmtime-cache = { workspace = true }
 wasmtime-cli-flags = { workspace = true }
 wasmtime-cranelift = { workspace = true }
 wasmtime-environ = { workspace = true }
+wasmtime-explorer = { workspace = true }
 wasmtime-wast = { workspace = true }
 wasmtime-wasi = { workspace = true, features = ["exit"] }
 wasmtime-wasi-crypto = { workspace = true, optional = true }
@@ -39,8 +40,8 @@ humantime = "2.0.0"
 once_cell = { workspace = true }
 listenfd = "1.0.0"
 wat = { workspace = true }
-serde = "1.0.94"
-serde_json = "1.0.26"
+serde = { workspace = true }
+serde_json = { workspace = true }
 wasmparser = { workspace = true }
 wasm-coredump-builder = { version = "0.1.11" }
 
@@ -70,8 +71,8 @@ component-macro-test = { path = "crates/misc/component-macro-test" }
 component-test-util = { workspace = true }
 bstr = "0.2.17"
 libc = "0.2.60"
-serde = "1.0"
-serde_json = "1.0"
+serde = { workspace = true }
+serde_json = { workspace = true }
 
 [target.'cfg(windows)'.dev-dependencies]
 windows-sys = { workspace = true, features = ["Win32_System_Memory"] }
@@ -120,6 +121,7 @@ wasmtime-cli-flags = { path = "crates/cli-flags", version = "=8.0.0" }
 wasmtime-cranelift = { path = "crates/cranelift", version = "=8.0.0" }
 wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=8.0.0" }
 wasmtime-environ = { path = "crates/environ", version = "=8.0.0" }
+wasmtime-explorer = { path = "crates/explorer", version = "=8.0.0" }
 wasmtime-fiber = { path = "crates/fiber", version = "=8.0.0" }
 wasmtime-types = { path = "crates/types", version = "8.0.0" }
 wasmtime-jit = { path = "crates/jit", version = "=8.0.0" }
@@ -169,10 +171,10 @@ anyhow = "1.0.22"
 wasmparser = "0.102.0"
 wat = "1.0.61"
 wast = "55.0.0"
-wasmprinter = "0.2.53"
+wasmprinter = "0.2.54"
 wasm-encoder = "0.25.0"
 wasm-smith = "0.12.5"
-wasm-mutate = "0.2.21"
+wasm-mutate = "0.2.22"
 wit-parser = "0.6.4"
 windows-sys = "0.45.0"
 env_logger = "0.9"
@@ -196,6 +198,7 @@ heck = "0.4"
 similar = "2.1.0"
 toml = "0.5.9"
 serde = "1.0.94"
+serde_json = "1.0.80"
 glob = "0.3.0"
 
 [features]
diff --git a/build.rs b/build.rs
index b735f076b..564133438 100644
--- a/build.rs
+++ b/build.rs
@@ -30,12 +30,6 @@ fn main() -> anyhow::Result<()> {
             test_directory_module(out, "tests/misc_testsuite/threads", strategy)?;
             test_directory_module(out, "tests/misc_testsuite/memory64", strategy)?;
             test_directory_module(out, "tests/misc_testsuite/component-model", strategy)?;
-
-            // NB: these are copied from upstream and updated to wasmtime's
-            // current version of `wast`. This local copy should go away when
-            // all of Wasmtime's tooling is updated and the upstream
-            // `testsuite` module is additionally updated.
-            test_directory_module(out, "tests/misc_testsuite/relaxed-simd", strategy)?;
             Ok(())
         })?;
 
@@ -51,6 +45,11 @@ fn main() -> anyhow::Result<()> {
                     strategy,
                 )?;
                 test_directory_module(out, "tests/spec_testsuite/proposals/threads", strategy)?;
+                test_directory_module(
+                    out,
+                    "tests/spec_testsuite/proposals/relaxed-simd",
+                    strategy,
+                )?;
             } else {
                 println!(
                     "cargo:warning=The spec testsuite is disabled. To enable, run `git submodule \
@@ -180,6 +179,12 @@ fn write_testsuite_tests(
 /// Ignore tests that aren't supported yet.
 fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
     assert_eq!(strategy, "Cranelift");
+
+    // This is an empty file right now which the `wast` crate doesn't parse
+    if testname.contains("memory_copy1") {
+        return true;
+    }
+
     match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
         "s390x" => {
             // FIXME: These tests fail under qemu due to a qemu bug.
diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs
index f36790f26..3ab1e0b1a 100755
--- a/cranelift/codegen/meta/src/shared/instructions.rs
+++ b/cranelift/codegen/meta/src/shared/instructions.rs
@@ -1422,7 +1422,7 @@ pub(crate) fn define(
         Shuffle two vectors using the given immediate bytes. For each of the 16 bytes of the
         immediate, a value i of 0-15 selects the i-th element of the first vector and a value i of
         16-31 selects the (i-16)th element of the second vector. Immediate values outside of the
-        0-31 range place a 0 in the resulting vector lane.
+        0-31 range are not valid.
         "#,
             &formats.shuffle,
         )
diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs
index 94c5722fd..bd8ae3b64 100644
--- a/cranelift/codegen/src/context.rs
+++ b/cranelift/codegen/src/context.rs
@@ -213,7 +213,6 @@ impl Context {
     ///
     /// Returns information about the function's code and read-only data.
     pub fn compile(&mut self, isa: &dyn TargetIsa) -> CompileResult<&CompiledCode> {
-        let _tt = timing::compile();
         let stencil = self.compile_stencil(isa).map_err(|error| CompileError {
             inner: error,
             func: &self.func,
@@ -379,6 +378,8 @@ impl Context {
 
     /// Run optimizations via the egraph infrastructure.
     pub fn egraph_pass(&mut self) -> CodegenResult<()> {
+        let _tt = timing::egraph();
+
         trace!(
             "About to optimize with egraph phase:\n{}",
             self.func.display()
diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle
index 53b0e65ed..f035789c1 100644
--- a/cranelift/codegen/src/isa/aarch64/inst.isle
+++ b/cranelift/codegen/src/isa/aarch64/inst.isle
@@ -553,7 +553,8 @@
        (VecDupFromFpu
         (rd WritableReg)
         (rn Reg)
-        (size VectorSize))
+        (size VectorSize)
+        (lane u8))
 
        ;; Duplicate FP immediate to vector.
        (VecDupFPImm
@@ -1390,8 +1391,18 @@
     (Addp)
     ;; Zip vectors (primary) [meaning, high halves]
     (Zip1)
+    ;; Zip vectors (secondary)
+    (Zip2)
     ;; Signed saturating rounding doubling multiply returning high half
     (Sqrdmulh)
+    ;; Unzip vectors (primary)
+    (Uzp1)
+    ;; Unzip vectors (secondary)
+    (Uzp2)
+    ;; Transpose vectors (primary)
+    (Trn1)
+    ;; Transpose vectors (secondary)
+    (Trn2)
 ))
 
 ;; A Vector ALU operation which modifies a source register.
@@ -1420,6 +1431,10 @@
     (Fneg)
     ;; Floating-point square root
     (Fsqrt)
+    ;; Reverse elements in 16-bit lanes
+    (Rev16)
+    ;; Reverse elements in 32-bit lanes
+    (Rev32)
     ;; Reverse elements in 64-bit doublewords
     (Rev64)
     ;; Floating-point convert to signed integer, rounding toward zero
@@ -1887,10 +1902,10 @@
         dst))
 
 ;; Helper for emitting `MInst.VecDupFromFpu` instructions.
-(decl vec_dup_from_fpu (Reg VectorSize) Reg)
-(rule (vec_dup_from_fpu src size)
+(decl vec_dup_from_fpu (Reg VectorSize u8) Reg)
+(rule (vec_dup_from_fpu src size lane)
       (let ((dst WritableReg (temp_writable_reg $I8X16))
-            (_ Unit (emit (MInst.VecDupFromFpu dst src size))))
+            (_ Unit (emit (MInst.VecDupFromFpu dst src size lane))))
         dst))
 
 ;; Helper for emitting `MInst.AluRRImm12` instructions.
@@ -2386,6 +2401,14 @@
 (decl neg (Reg VectorSize) Reg)
 (rule (neg x size) (vec_misc (VecMisc2.Neg) x size))
 
+;; Helper for generating `rev16` instructions.
+(decl rev16 (Reg VectorSize) Reg)
+(rule (rev16 x size) (vec_misc (VecMisc2.Rev16) x size))
+
+;; Helper for generating `rev32` instructions.
+(decl rev32 (Reg VectorSize) Reg)
+(rule (rev32 x size) (vec_misc (VecMisc2.Rev32) x size))
+
 ;; Helper for generating `rev64` instructions.
 (decl rev64 (Reg VectorSize) Reg)
 (rule (rev64 x size) (vec_misc (VecMisc2.Rev64) x size))
@@ -3767,3 +3790,27 @@
        (emit_side_effect (with_flags_side_effect
             (cmp (OperandSize.Size32) ridx jt_size)
             (jt_sequence ridx jt_info)))))
+
+;; Helper for emitting the `uzp1` instruction
+(decl vec_uzp1 (Reg Reg VectorSize) Reg)
+(rule (vec_uzp1 rn rm size) (vec_rrr (VecALUOp.Uzp1) rn rm size))
+
+;; Helper for emitting the `uzp2` instruction
+(decl vec_uzp2 (Reg Reg VectorSize) Reg)
+(rule (vec_uzp2 rn rm size) (vec_rrr (VecALUOp.Uzp2) rn rm size))
+
+;; Helper for emitting the `zip1` instruction
+(decl vec_zip1 (Reg Reg VectorSize) Reg)
+(rule (vec_zip1 rn rm size) (vec_rrr (VecALUOp.Zip1) rn rm size))
+
+;; Helper for emitting the `zip2` instruction
+(decl vec_zip2 (Reg Reg VectorSize) Reg)
+(rule (vec_zip2 rn rm size) (vec_rrr (VecALUOp.Zip2) rn rm size))
+
+;; Helper for emitting the `trn1` instruction
+(decl vec_trn1 (Reg Reg VectorSize) Reg)
+(rule (vec_trn1 rn rm size) (vec_rrr (VecALUOp.Trn1) rn rm size))
+
+;; Helper for emitting the `trn2` instruction
+(decl vec_trn2 (Reg Reg VectorSize) Reg)
+(rule (vec_trn2 rn rm size) (vec_rrr (VecALUOp.Trn2) rn rm size))
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index 72b1e1176..2332ff19b 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -1977,8 +1977,20 @@ impl MachInstEmit for Inst {
                         );
                         (0b1, 0b11111, enc_size)
                     }
+                    VecMisc2::Rev16 => {
+                        debug_assert_eq!(size, VectorSize::Size8x16);
+                        (0b0, 0b00001, enc_size)
+                    }
+                    VecMisc2::Rev32 => {
+                        debug_assert!(size == VectorSize::Size8x16 || size == VectorSize::Size16x8);
+                        (0b1, 0b00000, enc_size)
+                    }
                     VecMisc2::Rev64 => {
-                        debug_assert_ne!(VectorSize::Size64x2, size);
+                        debug_assert!(
+                            size == VectorSize::Size8x16
+                                || size == VectorSize::Size16x8
+                                || size == VectorSize::Size32x4
+                        );
                         (0b0, 0b00000, enc_size)
                     }
                     VecMisc2::Fcvtzs => {
@@ -2493,13 +2505,27 @@ impl MachInstEmit for Inst {
                         | machreg_to_vec(rd.to_reg()),
                 );
             }
-            &Inst::VecDupFromFpu { rd, rn, size } => {
+            &Inst::VecDupFromFpu { rd, rn, size, lane } => {
                 let rd = allocs.next_writable(rd);
                 let rn = allocs.next(rn);
                 let q = size.is_128bits() as u32;
                 let imm5 = match size.lane_size() {
-                    ScalarSize::Size32 => 0b00100,
-                    ScalarSize::Size64 => 0b01000,
+                    ScalarSize::Size8 => {
+                        assert!(lane < 16);
+                        0b00001 | (u32::from(lane) << 1)
+                    }
+                    ScalarSize::Size16 => {
+                        assert!(lane < 8);
+                        0b00010 | (u32::from(lane) << 2)
+                    }
+                    ScalarSize::Size32 => {
+                        assert!(lane < 4);
+                        0b00100 | (u32::from(lane) << 3)
+                    }
+                    ScalarSize::Size64 => {
+                        assert!(lane < 2);
+                        0b01000 | (u32::from(lane) << 4)
+                    }
                     _ => unimplemented!(),
                 };
                 sink.put4(
@@ -2870,6 +2896,7 @@ impl MachInstEmit for Inst {
                     VecALUOp::Fmul => (0b001_01110_00_1, 0b110111),
                     VecALUOp::Addp => (0b000_01110_00_1 | enc_size << 1, 0b101111),
                     VecALUOp::Zip1 => (0b01001110_00_0 | enc_size << 1, 0b001110),
+                    VecALUOp::Zip2 => (0b01001110_00_0 | enc_size << 1, 0b011110),
                     VecALUOp::Sqrdmulh => {
                         debug_assert!(
                             size.lane_size() == ScalarSize::Size16
@@ -2878,6 +2905,10 @@ impl MachInstEmit for Inst {
 
                         (0b001_01110_00_1 | enc_size << 1, 0b101101)
                     }
+                    VecALUOp::Uzp1 => (0b01001110_00_0 | enc_size << 1, 0b000110),
+                    VecALUOp::Uzp2 => (0b01001110_00_0 | enc_size << 1, 0b010110),
+                    VecALUOp::Trn1 => (0b01001110_00_0 | enc_size << 1, 0b001010),
+                    VecALUOp::Trn2 => (0b01001110_00_0 | enc_size << 1, 0b011010),
                 };
                 let top11 = if is_float {
                     top11 | size.enc_float_size() << 1
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index 779782907..6e8e602de 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -416,22 +416,30 @@ impl Inst {
                 size
             }]
         } else if let Some(imm) = widen_32_bit_pattern(pattern, lane_size) {
-            let tmp = alloc_tmp(types::I64X2);
-            let mut insts = smallvec![Inst::VecDupImm {
-                rd: tmp,
-                imm,
-                invert: false,
-                size: VectorSize::Size64x2,
-            }];
+            let mut insts = smallvec![];
 
             // TODO: Implement support for 64-bit scalar MOVI; we zero-extend the
             // lower 64 bits instead.
             if !size.is_128bits() {
+                let tmp = alloc_tmp(types::I64X2);
+                insts.push(Inst::VecDupImm {
+                    rd: tmp,
+                    imm,
+                    invert: false,
+                    size: VectorSize::Size64x2,
+                });
                 insts.push(Inst::FpuExtend {
                     rd,
                     rn: tmp.to_reg(),
                     size: ScalarSize::Size64,
                 });
+            } else {
+                insts.push(Inst::VecDupImm {
+                    rd,
+                    imm,
+                    invert: false,
+                    size: VectorSize::Size64x2,
+                });
             }
 
             insts
@@ -2123,9 +2131,9 @@ impl Inst {
                 let rn = pretty_print_ireg(rn, size.operand_size(), allocs);
                 format!("dup {}, {}", rd, rn)
             }
-            &Inst::VecDupFromFpu { rd, rn, size } => {
+            &Inst::VecDupFromFpu { rd, rn, size, lane } => {
                 let rd = pretty_print_vreg_vector(rd.to_reg(), size, allocs);
-                let rn = pretty_print_vreg_element(rn, 0, size.lane_size(), allocs);
+                let rn = pretty_print_vreg_element(rn, lane.into(), size.lane_size(), allocs);
                 format!("dup {}, {}", rd, rn)
             }
             &Inst::VecDupFPImm { rd, imm, size } => {
@@ -2345,7 +2353,12 @@ impl Inst {
                     VecALUOp::Fmul => ("fmul", size),
                     VecALUOp::Addp => ("addp", size),
                     VecALUOp::Zip1 => ("zip1", size),
+                    VecALUOp::Zip2 => ("zip2", size),
                     VecALUOp::Sqrdmulh => ("sqrdmulh", size),
+                    VecALUOp::Uzp1 => ("uzp1", size),
+                    VecALUOp::Uzp2 => ("uzp2", size),
+                    VecALUOp::Trn1 => ("trn1", size),
+                    VecALUOp::Trn2 => ("trn2", size),
                 };
                 let rd = pretty_print_vreg_vector(rd.to_reg(), size, allocs);
                 let rn = pretty_print_vreg_vector(rn, size, allocs);
@@ -2471,6 +2484,8 @@ impl Inst {
                     VecMisc2::Fabs => ("fabs", size, ""),
                     VecMisc2::Fneg => ("fneg", size, ""),
                     VecMisc2::Fsqrt => ("fsqrt", size, ""),
+                    VecMisc2::Rev16 => ("rev16", size, ""),
+                    VecMisc2::Rev32 => ("rev32", size, ""),
                     VecMisc2::Rev64 => ("rev64", size, ""),
                     VecMisc2::Fcvtzs => ("fcvtzs", size, ""),
                     VecMisc2::Fcvtzu => ("fcvtzu", size, ""),
diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle
index edb112447..f3a3db703 100644
--- a/cranelift/codegen/src/isa/aarch64/lower.isle
+++ b/cranelift/codegen/src/isa/aarch64/lower.isle
@@ -118,6 +118,118 @@
 
 ;;;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+;; When a single element of one vector is broadcast to all the destination
+;; lanes then the `dup` instruction can be used for this operation. Note that
+;; for now this only matches lane selection from the first vector `a`, but
+;; if necessary in the future rules can be added to select from `b` as well.
+(rule 6 (lower (shuffle a b (shuffle_dup8_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size8x16) n))
+(rule 5 (lower (shuffle a b (shuffle_dup16_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size16x8) n))
+(rule 4 (lower (shuffle a b (shuffle_dup32_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size32x4) n))
+(rule 3 (lower (shuffle a b (shuffle_dup64_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size64x2) n))
+
+;; If the `Immediate` specified to the extractor looks like a duplication of the
+;; `n`th lane of the first vector of size K-byte lanes, then each extractor
+;; returns the `n` value as a `u8` to be used as part of a `vec_dup_from_fpu`
+;; instruction. Note that there's a different extractor for each bit-width of
+;; lane.
+(decl shuffle_dup8_from_imm (u8) Immediate)
+(extern extractor shuffle_dup8_from_imm shuffle_dup8_from_imm)
+(decl shuffle_dup16_from_imm (u8) Immediate)
+(extern extractor shuffle_dup16_from_imm shuffle_dup16_from_imm)
+(decl shuffle_dup32_from_imm (u8) Immediate)
+(extern extractor shuffle_dup32_from_imm shuffle_dup32_from_imm)
+(decl shuffle_dup64_from_imm (u8) Immediate)
+(extern extractor shuffle_dup64_from_imm shuffle_dup64_from_imm)
+
+;; When the shuffle looks like "concatenate `a` and `b` and shift right by n*8
+;; bytes", that's an `ext` instruction.
+(rule 2 (lower (shuffle a b (vec_extract_imm4_from_immediate n)))
+        (vec_extract a b n))
+
+;; Attempts to extract `n` from the specified shuffle `Immediate` where each
+;; byte of the `Immediate` is a consecutive sequence starting from `n`. This
+;; value of `n` is used as part of the `vec_extract` instruction which extracts
+;; consecutive bytes from two vectors into one final vector, offset by `n`
+;; bytes.
+(decl vec_extract_imm4_from_immediate (u8) Immediate)
+(extern extractor vec_extract_imm4_from_immediate vec_extract_imm4_from_immediate)
+
+;; Rules for the `uzp1` and `uzp2` instructions which gather even-numbered lanes
+;; or odd-numbered lanes
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1e1c_1a18_1614_1210_0e0c_0a08_0604_0200)))
+      (vec_uzp1 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1d_1b19_1715_1311_0f0d_0b09_0705_0301)))
+      (vec_uzp2 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1d1c_1918_1514_1110_0d0c_0908_0504_0100)))
+      (vec_uzp1 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_1b1a_1716_1312_0f0e_0b0a_0706_0302)))
+      (vec_uzp2 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1b1a1918_13121110_0b0a0908_03020100)))
+      (vec_uzp1 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_17161514_0f0e0d0c_07060504)))
+      (vec_uzp2 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1716151413121110_0706050403020100)))
+      (vec_uzp1 a b (VectorSize.Size64x2)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908)))
+      (vec_uzp2 a b (VectorSize.Size64x2)))
+
+;; Rules for the `zip1` and `zip2` instructions which interleave lanes in the
+;; low or high halves of the two input vectors.
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000)))
+      (vec_zip1 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808)))
+      (vec_zip2 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100)))
+      (vec_zip1 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908)))
+      (vec_zip2 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x17161514_07060504_13121110_03020100)))
+      (vec_zip1 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908)))
+      (vec_zip2 a b (VectorSize.Size32x4)))
+;; Note that zip1/zip2 for i64x2 vectors is omitted since it's already covered
+;; by the i64x2 cases of uzp1/uzp2 above where both zip and uzp have the same
+;; semantics for 64-bit lanes.
+
+;; Rules for the `trn1` and `trn2` instructions which interleave odd or even
+;; lanes in the two input vectors.
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1e0e_1c0c_1a0a_1808_1606_1404_1202_1000)))
+      (vec_trn1 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f0f_1d0d_1b0b_1909_1707_1505_1303_1101)))
+      (vec_trn2 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1d1c_0d0c_1918_0908_1514_0504_1110_0100)))
+      (vec_trn1 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1b1a_0b0a_1716_0706_1312_0302)))
+      (vec_trn2 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1b1a1918_0b0a0908_13121110_03020100)))
+      (vec_trn1 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_17161514_07060504)))
+      (vec_trn2 a b (VectorSize.Size32x4)))
+;; Note that trn1/trn2 for i64x2 vectors is omitted since it's already covered
+;; by the i64x2 cases of uzp1/uzp2 above where both trn and uzp have the same
+;; semantics for 64-bit lanes.
+
+;; Rules for the `rev{16,32,64}` instructions where reversals happen at either
+;; the byte level, the 16-bit level, or 32-bit level. Note that all of these
+;; patterns only match reversals in the first operand, but they can
+;; theoretically be extended if necessary to reversals in the second operand.
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0e0f_0c0d_0a0b_0809_0607_0405_0203_0001)))
+      (rev16 a (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0c0d0e0f_08090a0b_04050607_00010203)))
+      (rev32 a (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0d0c0f0e_09080b0a_05040706_01000302)))
+      (rev32 a (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x08090a0b0c0d0e0f_0001020304050607)))
+      (rev64 a (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x09080b0a0d0c0f0e_0100030205040706)))
+      (rev64 a (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0b0a09080f0e0d0c_0302010007060504)))
+      (rev64 a (VectorSize.Size32x4)))
+
 (rule (lower (has_type ty (shuffle rn rn2 (u128_from_immediate mask))))
       (let ((mask_reg Reg (constant_f128 mask)))
        (vec_tbl2 rn rn2 mask_reg ty)))
@@ -1840,7 +1952,7 @@
       (vec_dup x (vector_size ty)))
 
 (rule -2 (lower (has_type ty (splat x @ (value_type (ty_scalar_float _)))))
-      (vec_dup_from_fpu x (vector_size ty)))
+      (vec_dup_from_fpu x (vector_size ty) 0))
 
 (rule (lower (has_type ty (splat (f32const (u64_from_ieee32 n)))))
       (splat_const n (vector_size ty)))
diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
index 57e187142..707243d2a 100644
--- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
@@ -742,4 +742,47 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
             );
         }
     }
+
+    fn vec_extract_imm4_from_immediate(&mut self, imm: Immediate) -> Option<u8> {
+        let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
+
+        if bytes.windows(2).all(|a| a[0] + 1 == a[1]) && bytes[0] < 16 {
+            Some(bytes[0])
+        } else {
+            None
+        }
+    }
+
+    fn shuffle_dup8_from_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
+        if bytes.iter().all(|b| *b == bytes[0]) && bytes[0] < 16 {
+            Some(bytes[0])
+        } else {
+            None
+        }
+    }
+    fn shuffle_dup16_from_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        if a == b && b == c && c == d && d == e && e == f && f == g && g == h && a < 8 {
+            Some(a)
+        } else {
+            None
+        }
+    }
+    fn shuffle_dup32_from_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        if a == b && b == c && c == d && a < 4 {
+            Some(a)
+        } else {
+            None
+        }
+    }
+    fn shuffle_dup64_from_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b) = self.shuffle64_from_imm(imm)?;
+        if a == b && a < 2 {
+            Some(a)
+        } else {
+            None
+        }
+    }
 }
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index 163d0cb63..fd69eb4bd 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -186,14 +186,14 @@
     (alu_rrr (AluOPRRR.RemU) x y)))
 
 ;;;; Rules for `and` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(rule -1 (lower (has_type (fits_in_64 ty) (band x y)))
+(rule -1 (lower (has_type (fits_in_64 (ty_int ty)) (band x y)))
   (alu_rrr (AluOPRRR.And) x y))
 
 ;; Special cases for when one operand is an immediate that fits in 12 bits.
-(rule 2 (lower (has_type (fits_in_64 ty) (band x (imm12_from_value y))))
+(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (band x (imm12_from_value y))))
   (alu_rr_imm12 (AluOPRRI.Andi) x y))
 
-(rule 1 (lower (has_type (fits_in_64 ty) (band (imm12_from_value x) y)))
+(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (band (imm12_from_value x) y)))
   (alu_rr_imm12 (AluOPRRI.Andi) y x))
 
 (rule (lower (has_type $I128 (band x y)))
@@ -201,6 +201,7 @@
 
 (rule (lower (has_type $F32 (band x y)))
   (lower_float_binary (AluOPRRR.And) x y $F32))
+
 (rule (lower (has_type $F64 (band x y)))
   (lower_float_binary (AluOPRRR.And) x y $F64))
 
@@ -208,18 +209,21 @@
 ;; by Cranelift's `band_not` instruction that is legalized into the simpler
 ;; forms early on.
 
-(rule 3 (lower (has_type (fits_in_64 ty) (band x (bnot y))))
+(rule 3 (lower (has_type (fits_in_64 (ty_int ty)) (band x (bnot y))))
   (if-let $true (has_zbb))
   (gen_andn x y))
-(rule 4 (lower (has_type (fits_in_64 ty) (band (bnot y) x)))
+
+(rule 4 (lower (has_type (fits_in_64 (ty_int ty)) (band (bnot y) x)))
   (if-let $true (has_zbb))
   (gen_andn x y))
+
 (rule 5 (lower (has_type $I128 (band x (bnot y))))
   (if-let $true (has_zbb))
   (let
     ((low Reg (gen_andn (value_regs_get x 0) (value_regs_get y 0)))
       (high Reg (gen_andn (value_regs_get x 1) (value_regs_get y 1))))
     (value_regs low high)))
+
 (rule 6 (lower (has_type $I128 (band (bnot y) x)))
   (if-let $true (has_zbb))
   (let
@@ -229,19 +233,22 @@
 
 
 ;;;; Rules for `or` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(rule -1 (lower (has_type (fits_in_64 ty) (bor x y)))
+(rule -1 (lower (has_type (fits_in_64 (ty_int ty)) (bor x y)))
   (alu_rrr (AluOPRRR.Or) x y))
 
 ;; Special cases for when one operand is an immediate that fits in 12 bits.
-(rule 2 (lower (has_type (fits_in_64 ty) (bor x (imm12_from_value y))))
+(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (bor x (imm12_from_value y))))
   (alu_rr_imm12 (AluOPRRI.Ori) x y))
 
-(rule 1 (lower (has_type (fits_in_64 ty) (bor (imm12_from_value x) y)))
+(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bor (imm12_from_value x) y)))
   (alu_rr_imm12 (AluOPRRI.Ori) y x))
+
 (rule (lower (has_type $I128 (bor x y)))
   (lower_b128_binary (AluOPRRR.Or) x y))
+
 (rule (lower (has_type $F32 (bor x y)))
   (lower_float_binary (AluOPRRR.Or) x y $F32))
+
 (rule (lower (has_type $F64 (bor x y)))
   (lower_float_binary (AluOPRRR.Or) x y $F64))
 
@@ -249,10 +256,11 @@
 ;; by Cranelift's `bor_not` instruction that is legalized into the simpler
 ;; forms early on.
 
-(rule 3 (lower (has_type (fits_in_64 ty) (bor x (bnot y))))
+(rule 3 (lower (has_type (fits_in_64 (ty_int ty)) (bor x (bnot y))))
   (if-let $true (has_zbb))
   (gen_orn x y))
-(rule 4 (lower (has_type (fits_in_64 ty) (bor (bnot y) x)))
+
+(rule 4 (lower (has_type (fits_in_64 (ty_int ty)) (bor (bnot y) x)))
   (if-let $true (has_zbb))
   (gen_orn x y))
 
@@ -262,6 +270,7 @@
     ((low Reg (gen_orn (value_regs_get x 0) (value_regs_get y 0)))
       (high Reg (gen_orn (value_regs_get x 1) (value_regs_get y 1))))
     (value_regs low high)))
+
 (rule 6 (lower (has_type $I128 (bor (bnot y) x)))
   (if-let $true (has_zbb))
   (let
@@ -271,40 +280,43 @@
 
 
 ;;;; Rules for `xor` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(rule -1 (lower (has_type (fits_in_64 ty) (bxor x y)))
+(rule -1 (lower (has_type (fits_in_64 (ty_int ty)) (bxor x y)))
   (alu_rrr (AluOPRRR.Xor) x y))
 
 ;; Special cases for when one operand is an immediate that fits in 12 bits.
-(rule 2 (lower (has_type (fits_in_64 ty) (bxor x (imm12_from_value y))))
+(rule 2 (lower (has_type (fits_in_64 (ty_int ty)) (bxor x (imm12_from_value y))))
   (alu_rr_imm12 (AluOPRRI.Xori) x y))
 
-(rule 1 (lower (has_type (fits_in_64 ty) (bxor (imm12_from_value x) y)))
+(rule 1 (lower (has_type (fits_in_64 (ty_int ty)) (bxor (imm12_from_value x) y)))
   (alu_rr_imm12 (AluOPRRI.Xori) y x))
+
 (rule (lower (has_type $I128 (bxor x y)))
   (lower_b128_binary (AluOPRRR.Xor) x y))
+
 (rule (lower (has_type $F32 (bxor x y)))
   (lower_float_binary (AluOPRRR.Xor) x y $F32))
+
 (rule (lower (has_type $F64 (bxor x y)))
   (lower_float_binary (AluOPRRR.Xor) x y $F64))
 
 
 ;;;; Rules for `bnot` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(rule -1 (lower (has_type fits_in_64 (bnot x)))
+(rule -1 (lower (has_type (fits_in_64 (ty_int ty)) (bnot x)))
   (alu_rr_imm12 (AluOPRRI.Xori) x (imm_from_neg_bits -1)))
 
 (rule (lower (has_type $I128 (bnot x)))
   (bnot_128 x))
+
 (rule
   (lower (has_type $F32 (bnot x)))
-  (lower_float_bnot x $F32)
-)
+  (lower_float_bnot x $F32))
+
 (rule
   (lower (has_type $F64 (bnot x)))
-  (lower_float_bnot x $F64)
-)
+  (lower_float_bnot x $F64))
 
 ;;;; Rules for `bit_reverse` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(rule (lower (has_type ty (bitrev x)))
+(rule (lower (has_type (fits_in_64 (ty_int ty)) (bitrev x)))
   (lower_bit_reverse x ty))
 
 (rule 1 (lower (has_type $I128 (bitrev x)))
diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs
index d68e3317c..7305b98fb 100644
--- a/cranelift/codegen/src/isa/s390x/inst/mod.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs
@@ -261,10 +261,10 @@ impl Inst {
                 _ => InstructionSet::Base,
             },
             Inst::FpuRound { op, .. } => match op {
-                FpuRoundOp::ToSInt32 | FpuRoundOp::FromSInt32 => InstructionSet::MIE2,
-                FpuRoundOp::ToUInt32 | FpuRoundOp::FromUInt32 => InstructionSet::MIE2,
-                FpuRoundOp::ToSInt32x4 | FpuRoundOp::FromSInt32x4 => InstructionSet::MIE2,
-                FpuRoundOp::ToUInt32x4 | FpuRoundOp::FromUInt32x4 => InstructionSet::MIE2,
+                FpuRoundOp::ToSInt32 | FpuRoundOp::FromSInt32 => InstructionSet::VXRS_EXT2,
+                FpuRoundOp::ToUInt32 | FpuRoundOp::FromUInt32 => InstructionSet::VXRS_EXT2,
+                FpuRoundOp::ToSInt32x4 | FpuRoundOp::FromSInt32x4 => InstructionSet::VXRS_EXT2,
+                FpuRoundOp::ToUInt32x4 | FpuRoundOp::FromUInt32x4 => InstructionSet::VXRS_EXT2,
                 _ => InstructionSet::Base,
             },
 
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 396f9fd4a..c1c71690d 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -30,7 +30,7 @@ use args::*;
 // `Inst` is defined inside ISLE as `MInst`. We publicly re-export it here.
 pub use super::lower::isle::generated_code::MInst as Inst;
 
-// Out-of-line data for calls, to keep the size of `Inst` downn.
+/// Out-of-line data for calls, to keep the size of `Inst` down.
 #[derive(Clone, Debug)]
 pub struct CallInfo {
     /// Register uses of this call.
diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs
index 79f79c7e2..dd84f4395 100644
--- a/cranelift/codegen/src/isa/x64/mod.rs
+++ b/cranelift/codegen/src/isa/x64/mod.rs
@@ -1,6 +1,6 @@
 //! X86_64-bit Instruction Set Architecture.
 
-pub use self::inst::{args, EmitInfo, EmitState, Inst};
+pub use self::inst::{args, CallInfo, EmitInfo, EmitState, Inst};
 
 use super::{OwnedTargetIsa, TargetIsa};
 use crate::dominator_tree::DominatorTree;
diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs
index 3eb0db7ea..9b6adcab9 100644
--- a/cranelift/codegen/src/machinst/isle.rs
+++ b/cranelift/codegen/src/machinst/isle.rs
@@ -586,6 +586,17 @@ macro_rules! isle_lower_prelude_methods {
             self.lower_ctx.gen_return(rets);
         }
 
+        /// Same as `shuffle32_from_imm`, but for 64-bit lane shuffles.
+        fn shuffle64_from_imm(&mut self, imm: Immediate) -> Option<(u8, u8)> {
+            use crate::machinst::isle::shuffle_imm_as_le_lane_idx;
+
+            let bytes = self.lower_ctx.get_immediate_data(imm).as_slice();
+            Some((
+                shuffle_imm_as_le_lane_idx(8, &bytes[0..8])?,
+                shuffle_imm_as_le_lane_idx(8, &bytes[8..16])?,
+            ))
+        }
+
         /// Attempts to interpret the shuffle immediate `imm` as a shuffle of
         /// 32-bit lanes, returning four integers, each of which is less than 8,
         /// which represents a permutation of 32-bit lanes as specified by
diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle
index a7e59d590..a0498fae4 100644
--- a/cranelift/codegen/src/prelude_lower.isle
+++ b/cranelift/codegen/src/prelude_lower.isle
@@ -597,6 +597,8 @@
 ;; returned will be in the range of 0 to (256/N)-1, inclusive, and index the
 ;; N-bit chunks of two concatenated 128-bit vectors starting from the
 ;; least-significant bits.
+(decl shuffle64_from_imm (u8 u8) Immediate)
+(extern extractor shuffle64_from_imm shuffle64_from_imm)
 (decl shuffle32_from_imm (u8 u8 u8 u8) Immediate)
 (extern extractor shuffle32_from_imm shuffle32_from_imm)
 (decl shuffle16_from_imm (u8 u8 u8 u8 u8 u8 u8 u8) Immediate)
diff --git a/cranelift/codegen/src/timing.rs b/cranelift/codegen/src/timing.rs
index 65412c5df..50a0b66ad 100644
--- a/cranelift/codegen/src/timing.rs
+++ b/cranelift/codegen/src/timing.rs
@@ -3,8 +3,11 @@
 //! This modules provides facilities for timing the execution of individual compilation passes.
 
 use core::fmt;
-
-pub use self::details::{add_to_current, take_current, PassTimes, TimingToken};
+use std::any::Any;
+use std::boxed::Box;
+use std::cell::{Cell, RefCell};
+use std::mem;
+use std::time::{Duration, Instant};
 
 // Each pass that can be timed is predefined with the `define_passes!` macro. Each pass has a
 // snake_case name and a plain text description used when printing out the timing report.
@@ -16,22 +19,26 @@ pub use self::details::{add_to_current, take_current, PassTimes, TimingToken};
 // - A const array of pass descriptions.
 // - A public function per pass used to start the timing of that pass.
 macro_rules! define_passes {
-    { $enum:ident, $num_passes:ident, $descriptions:ident;
-      $($pass:ident: $desc:expr,)+
-    } => {
+    ($($pass:ident: $desc:expr,)+) => {
+        /// A single profiled pass.
         #[allow(non_camel_case_types)]
         #[derive(Clone, Copy, Debug, PartialEq, Eq)]
-        enum $enum { $($pass,)+ None}
+        pub enum Pass {
+            $(#[doc=$desc] $pass,)+
+            /// No active pass.
+            None,
+        }
 
-        const $num_passes: usize = $enum::None as usize;
+        /// The amount of profiled passes.
+        pub const NUM_PASSES: usize = Pass::None as usize;
 
-        const $descriptions: [&str; $num_passes] = [ $($desc),+ ];
+        const DESCRIPTIONS: [&str; NUM_PASSES] = [ $($desc),+ ];
 
         $(
             #[doc=$desc]
             #[must_use]
-            pub fn $pass() -> TimingToken {
-                details::start_pass($enum::$pass)
+            pub fn $pass() -> Box<dyn Any> {
+                start_pass(Pass::$pass)
             }
         )+
     }
@@ -39,8 +46,6 @@ macro_rules! define_passes {
 
 // Pass definitions.
 define_passes! {
-    Pass, NUM_PASSES, DESCRIPTIONS;
-
     // All these are used in other crates but defined here so they appear in the unified
     // `PassTimes` output.
     process_file: "Processing test file",
@@ -58,6 +63,7 @@ define_passes! {
     loop_analysis: "Loop analysis",
     preopt: "Pre-legalization rewriting",
     dce: "Dead code elimination",
+    egraph: "Egraph based optimizations",
     gvn: "Global value numbering",
     licm: "Loop invariant code motion",
     unreachable_code: "Remove unreachable blocks",
@@ -75,179 +81,181 @@ define_passes! {
 }
 
 impl Pass {
-    pub fn idx(self) -> usize {
+    fn idx(self) -> usize {
         self as usize
     }
+
+    /// Description of the pass.
+    pub fn description(self) -> &'static str {
+        match DESCRIPTIONS.get(self.idx()) {
+            Some(s) => s,
+            None => "<no pass>",
+        }
+    }
 }
 
 impl fmt::Display for Pass {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match DESCRIPTIONS.get(self.idx()) {
-            Some(s) => f.write_str(s),
-            None => f.write_str("<no pass>"),
-        }
+        f.write_str(self.description())
     }
 }
 
-/// Implementation details.
-///
-/// This whole module can be gated on a `cfg` feature to provide a dummy implementation for
-/// performance-sensitive builds or restricted environments. The dummy implementation must provide
-/// `TimingToken` and `PassTimes` types and `take_current`, `add_to_current`, and `start_pass` funcs
-#[cfg(feature = "std")]
-mod details {
-    use super::{Pass, DESCRIPTIONS, NUM_PASSES};
-    use std::cell::{Cell, RefCell};
-    use std::fmt;
-    use std::mem;
-    use std::time::{Duration, Instant};
-
-    /// A timing token is responsible for timing the currently running pass. Timing starts when it
-    /// is created and ends when it is dropped.
+/// A profiler.
+pub trait Profiler {
+    /// Start a profiling pass.
+    ///
+    /// Will return a token which when dropped indicates the end of the pass.
     ///
     /// Multiple passes can be active at the same time, but they must be started and stopped in a
     /// LIFO fashion.
-    pub struct TimingToken {
-        /// Start time for this pass.
-        start: Instant,
+    fn start_pass(&self, pass: Pass) -> Box<dyn Any>;
+}
 
-        // Pass being timed by this token.
-        pass: Pass,
+// Information about passes in a single thread.
+thread_local! {
+    static PROFILER: RefCell<Box<dyn Profiler>> = RefCell::new(Box::new(DefaultProfiler));
+}
 
-        // The previously active pass which will be restored when this token is dropped.
-        prev: Pass,
-    }
+/// Set the profiler for the current thread.
+///
+/// Returns the old profiler.
+pub fn set_thread_profiler(new_profiler: Box<dyn Profiler>) -> Box<dyn Profiler> {
+    PROFILER.with(|profiler| std::mem::replace(&mut *profiler.borrow_mut(), new_profiler))
+}
+
+/// Start timing `pass` as a child of the currently running pass, if any.
+///
+/// This function is called by the publicly exposed pass functions.
+fn start_pass(pass: Pass) -> Box<dyn Any> {
+    PROFILER.with(|profiler| profiler.borrow().start_pass(pass))
+}
+
+/// A timing token is responsible for timing the currently running pass. Timing starts when it
+/// is created and ends when it is dropped.
+///
+/// Multiple passes can be active at the same time, but they must be started and stopped in a
+/// LIFO fashion.
+struct DefaultTimingToken {
+    /// Start time for this pass.
+    start: Instant,
 
-    /// Accumulated timing information for a single pass.
-    #[derive(Default, Copy, Clone)]
-    struct PassTime {
-        /// Total time spent running this pass including children.
-        total: Duration,
+    // Pass being timed by this token.
+    pass: Pass,
 
-        /// Time spent running in child passes.
-        child: Duration,
+    // The previously active pass which will be restored when this token is dropped.
+    prev: Pass,
+}
+
+/// Accumulated timing information for a single pass.
+#[derive(Default, Copy, Clone)]
+struct PassTime {
+    /// Total time spent running this pass including children.
+    total: Duration,
+
+    /// Time spent running in child passes.
+    child: Duration,
+}
+
+/// Accumulated timing for all passes.
+pub struct PassTimes {
+    pass: [PassTime; NUM_PASSES],
+}
+
+impl PassTimes {
+    /// Add `other` to the timings of this `PassTimes`.
+    pub fn add(&mut self, other: &Self) {
+        for (a, b) in self.pass.iter_mut().zip(&other.pass[..]) {
+            a.total += b.total;
+            a.child += b.child;
+        }
     }
 
-    /// Accumulated timing for all passes.
-    pub struct PassTimes {
-        pass: [PassTime; NUM_PASSES],
+    /// Returns the total amount of time taken by all the passes measured.
+    pub fn total(&self) -> Duration {
+        self.pass.iter().map(|p| p.total - p.child).sum()
     }
+}
 
-    impl PassTimes {
-        /// Returns the total amount of time taken by all the passes measured.
-        pub fn total(&self) -> Duration {
-            self.pass.iter().map(|p| p.total - p.child).sum()
+impl Default for PassTimes {
+    fn default() -> Self {
+        Self {
+            pass: [Default::default(); NUM_PASSES],
         }
     }
+}
 
-    impl Default for PassTimes {
-        fn default() -> Self {
-            Self {
-                pass: [Default::default(); NUM_PASSES],
+impl fmt::Display for PassTimes {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        writeln!(f, "======== ========  ==================================")?;
+        writeln!(f, "   Total     Self  Pass")?;
+        writeln!(f, "-------- --------  ----------------------------------")?;
+        for (time, desc) in self.pass.iter().zip(&DESCRIPTIONS[..]) {
+            // Omit passes that haven't run.
+            if time.total == Duration::default() {
+                continue;
             }
-        }
-    }
 
-    impl fmt::Display for PassTimes {
-        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            writeln!(f, "======== ========  ==================================")?;
-            writeln!(f, "   Total     Self  Pass")?;
-            writeln!(f, "-------- --------  ----------------------------------")?;
-            for (time, desc) in self.pass.iter().zip(&DESCRIPTIONS[..]) {
-                // Omit passes that haven't run.
-                if time.total == Duration::default() {
-                    continue;
-                }
-
-                // Write a duration as secs.millis, trailing space.
-                fn fmtdur(mut dur: Duration, f: &mut fmt::Formatter) -> fmt::Result {
-                    // Round to nearest ms by adding 500us.
-                    dur += Duration::new(0, 500_000);
-                    let ms = dur.subsec_millis();
-                    write!(f, "{:4}.{:03} ", dur.as_secs(), ms)
-                }
-
-                fmtdur(time.total, f)?;
-                if let Some(s) = time.total.checked_sub(time.child) {
-                    fmtdur(s, f)?;
-                }
-                writeln!(f, " {}", desc)?;
+            // Write a duration as secs.millis, trailing space.
+            fn fmtdur(mut dur: Duration, f: &mut fmt::Formatter) -> fmt::Result {
+                // Round to nearest ms by adding 500us.
+                dur += Duration::new(0, 500_000);
+                let ms = dur.subsec_millis();
+                write!(f, "{:4}.{:03} ", dur.as_secs(), ms)
             }
-            writeln!(f, "======== ========  ==================================")
+
+            fmtdur(time.total, f)?;
+            if let Some(s) = time.total.checked_sub(time.child) {
+                fmtdur(s, f)?;
+            }
+            writeln!(f, " {}", desc)?;
         }
+        writeln!(f, "======== ========  ==================================")
     }
+}
 
-    // Information about passes in a single thread.
-    thread_local! {
-        static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
-        static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
-    }
+// Information about passes in a single thread.
+thread_local! {
+    static CURRENT_PASS: Cell<Pass> = const { Cell::new(Pass::None) };
+    static PASS_TIME: RefCell<PassTimes> = RefCell::new(Default::default());
+}
 
-    /// Start timing `pass` as a child of the currently running pass, if any.
-    ///
-    /// This function is called by the publicly exposed pass functions.
-    pub(super) fn start_pass(pass: Pass) -> TimingToken {
+/// The default profiler. You can get the results using [`take_current`].
+pub struct DefaultProfiler;
+
+impl Profiler for DefaultProfiler {
+    fn start_pass(&self, pass: Pass) -> Box<dyn Any> {
         let prev = CURRENT_PASS.with(|p| p.replace(pass));
         log::debug!("timing: Starting {}, (during {})", pass, prev);
-        TimingToken {
+        Box::new(DefaultTimingToken {
             start: Instant::now(),
             pass,
             prev,
-        }
-    }
-
-    /// Dropping a timing token indicated the end of the pass.
-    impl Drop for TimingToken {
-        fn drop(&mut self) {
-            let duration = self.start.elapsed();
-            log::debug!("timing: Ending {}", self.pass);
-            let old_cur = CURRENT_PASS.with(|p| p.replace(self.prev));
-            debug_assert_eq!(self.pass, old_cur, "Timing tokens dropped out of order");
-            PASS_TIME.with(|rc| {
-                let mut table = rc.borrow_mut();
-                table.pass[self.pass.idx()].total += duration;
-                if let Some(parent) = table.pass.get_mut(self.prev.idx()) {
-                    parent.child += duration;
-                }
-            })
-        }
-    }
-
-    /// Take the current accumulated pass timings and reset the timings for the current thread.
-    pub fn take_current() -> PassTimes {
-        PASS_TIME.with(|rc| mem::replace(&mut *rc.borrow_mut(), Default::default()))
+        })
     }
+}
 
-    /// Add `timings` to the accumulated timings for the current thread.
-    pub fn add_to_current(times: &PassTimes) {
+/// Dropping a timing token indicated the end of the pass.
+impl Drop for DefaultTimingToken {
+    fn drop(&mut self) {
+        let duration = self.start.elapsed();
+        log::debug!("timing: Ending {}", self.pass);
+        let old_cur = CURRENT_PASS.with(|p| p.replace(self.prev));
+        debug_assert_eq!(self.pass, old_cur, "Timing tokens dropped out of order");
         PASS_TIME.with(|rc| {
-            for (a, b) in rc.borrow_mut().pass.iter_mut().zip(&times.pass[..]) {
-                a.total += b.total;
-                a.child += b.child;
+            let mut table = rc.borrow_mut();
+            table.pass[self.pass.idx()].total += duration;
+            if let Some(parent) = table.pass.get_mut(self.prev.idx()) {
+                parent.child += duration;
             }
         })
     }
 }
 
-/// Dummy `debug` implementation
-#[cfg(not(feature = "std"))]
-mod details {
-    use super::Pass;
-    /// Dummy `TimingToken`
-    pub struct TimingToken;
-    /// Dummy `PassTimes`
-    pub struct PassTimes;
-    /// Returns dummy `PassTimes`
-    pub fn take_current() -> PassTimes {
-        PassTimes
-    }
-    /// does nothing
-    pub fn add_to_current(_times: PassTimes) {}
-
-    /// does nothing
-    pub(super) fn start_pass(_pass: Pass) -> TimingToken {
-        TimingToken
-    }
+/// Take the current accumulated pass timings and reset the timings for the current thread.
+///
+/// Only applies when [`DefaultProfiler`] is used.
+pub fn take_current() -> PassTimes {
+    PASS_TIME.with(|rc| mem::take(&mut *rc.borrow_mut()))
 }
 
 #[cfg(test)]
diff --git a/cranelift/codegen/src/verifier/mod.rs b/cranelift/codegen/src/verifier/mod.rs
index 4c46ac225..cd8d76771 100644
--- a/cranelift/codegen/src/verifier/mod.rs
+++ b/cranelift/codegen/src/verifier/mod.rs
@@ -1685,6 +1685,28 @@ impl<'a> Verifier<'a> {
                     Ok(())
                 }
             }
+            ir::InstructionData::Shuffle {
+                opcode: ir::instructions::Opcode::Shuffle,
+                imm,
+                ..
+            } => {
+                let imm = self.func.dfg.immediates.get(imm).unwrap().as_slice();
+                if imm.len() != 16 {
+                    errors.fatal((
+                        inst,
+                        self.context(inst),
+                        format!("the shuffle immediate wasn't 16-bytes long"),
+                    ))
+                } else if let Some(i) = imm.iter().find(|i| **i >= 32) {
+                    errors.fatal((
+                        inst,
+                        self.context(inst),
+                        format!("shuffle immediate index {i} is larger than the maximum 31"),
+                    ))
+                } else {
+                    Ok(())
+                }
+            }
             _ => Ok(()),
         }
     }
diff --git a/cranelift/entity/src/set.rs b/cranelift/entity/src/set.rs
index 4b64d79f4..a5c8b7b29 100644
--- a/cranelift/entity/src/set.rs
+++ b/cranelift/entity/src/set.rs
@@ -5,6 +5,9 @@ use crate::EntityRef;
 use alloc::vec::Vec;
 use core::marker::PhantomData;
 
+// How many bits are used to represent a single element in `EntitySet`.
+const BITS: usize = core::mem::size_of::<usize>() * 8;
+
 /// A set of `K` for densely indexed entity references.
 ///
 /// The `EntitySet` data structure uses the dense index space to implement a set with a bitvector.
@@ -14,7 +17,7 @@ pub struct EntitySet<K>
 where
     K: EntityRef,
 {
-    elems: Vec<u8>,
+    elems: Vec<usize>,
     len: usize,
     unused: PhantomData<K>,
 }
@@ -42,7 +45,7 @@ where
     /// Creates a new empty set with the specified capacity.
     pub fn with_capacity(capacity: usize) -> Self {
         Self {
-            elems: Vec::with_capacity((capacity + 7) / 8),
+            elems: Vec::with_capacity((capacity + (BITS - 1)) / BITS),
             ..Self::new()
         }
     }
@@ -51,7 +54,7 @@ where
     pub fn contains(&self, k: K) -> bool {
         let index = k.index();
         if index < self.len {
-            (self.elems[index / 8] & (1 << (index % 8))) != 0
+            (self.elems[index / BITS] & (1 << (index % BITS))) != 0
         } else {
             false
         }
@@ -71,11 +74,11 @@ where
     /// `clear`ed or created with `new`.
     pub fn cardinality(&self) -> usize {
         let mut n: usize = 0;
-        for byte_ix in 0..self.len / 8 {
-            n += self.elems[byte_ix].count_ones() as usize;
+        for idx in 0..self.len / BITS {
+            n += self.elems[idx].count_ones() as usize;
         }
-        for bit_ix in (self.len / 8) * 8..self.len {
-            if (self.elems[bit_ix / 8] & (1 << (bit_ix % 8))) != 0 {
+        for bit_ix in (self.len / BITS) * BITS..self.len {
+            if (self.elems[bit_ix / BITS] & (1 << (bit_ix % BITS))) != 0 {
                 n += 1;
             }
         }
@@ -95,7 +98,7 @@ where
 
     /// Resize the set to have `n` entries by adding default entries as needed.
     pub fn resize(&mut self, n: usize) {
-        self.elems.resize((n + 7) / 8, 0);
+        self.elems.resize((n + (BITS - 1)) / BITS, 0);
         self.len = n
     }
 
@@ -106,7 +109,7 @@ where
             self.resize(index + 1)
         }
         let result = !self.contains(k);
-        self.elems[index / 8] |= 1 << (index % 8);
+        self.elems[index / BITS] |= 1 << (index % BITS);
         result
     }
 
@@ -118,7 +121,7 @@ where
 
         // Clear the last known entity in the list.
         let last_index = self.len - 1;
-        self.elems[last_index / 8] &= !(1 << (last_index % 8));
+        self.elems[last_index / BITS] &= !(1 << (last_index % BITS));
 
         // Set the length to the next last stored entity or zero if we pop'ed
         // the last entity.
@@ -127,12 +130,14 @@ where
             .iter()
             .enumerate()
             .rev()
-            .find(|(_, &byte)| byte != 0)
-            // Map `i` from byte index to bit level index.
-            // `(i + 1) * 8` = Last bit in byte.
-            // `last - byte.leading_zeros()` = last set bit in byte.
+            .find(|(_, &elem)| elem != 0)
+            // Map `i` from `elem` index to bit level index.
+            // `(i + 1) * BITS` = Last bit in `elem`.
+            // `last - elem.leading_zeros()` = last set bit in `elem`.
             // `as usize` won't ever truncate as the potential range is `0..=8`.
-            .map_or(0, |(i, byte)| ((i + 1) * 8) - byte.leading_zeros() as usize);
+            .map_or(0, |(i, elem)| {
+                ((i + 1) * BITS) - elem.leading_zeros() as usize
+            });
 
         Some(K::new(last_index))
     }
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 744b923ca..ff7ae0b88 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -6,9 +6,10 @@ use cranelift::codegen::data_value::DataValue;
 use cranelift::codegen::ir::immediates::Offset32;
 use cranelift::codegen::ir::instructions::InstructionFormat;
 use cranelift::codegen::ir::stackslot::StackSize;
+
 use cranelift::codegen::ir::{
-    types::*, AtomicRmwOp, Block, ExternalName, FuncRef, Function, LibCall, Opcode, Signature,
-    StackSlot, Type, UserExternalName, UserFuncName, Value,
+    types::*, AtomicRmwOp, Block, ConstantData, ExternalName, FuncRef, Function, LibCall, Opcode,
+    Signature, StackSlot, Type, UserExternalName, UserFuncName, Value,
 };
 use cranelift::codegen::isa::CallConv;
 use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Switch, Variable};
@@ -36,10 +37,10 @@ fn insert_opcode(
         vals.push(val);
     }
 
-    // For pretty much every instruction the control type is the return type
-    // except for Iconcat and Isplit which are *special* and the control type
-    // is the input type.
-    let ctrl_type = if opcode == Opcode::Iconcat || opcode == Opcode::Isplit {
+    // Some opcodes require us to look at their input arguments to determine the
+    // controlling type. This is not the general case, but we can neatly check this
+    // using `requires_typevar_operand`.
+    let ctrl_type = if opcode.constraints().requires_typevar_operand() {
         args.first()
     } else {
         rets.first()
@@ -139,26 +140,28 @@ fn insert_cmp(
     let res = if opcode == Opcode::Fcmp {
         let cc = *fgen.u.choose(FloatCC::all())?;
 
-        // Some FloatCC's are not implemented on AArch64, see:
-        // https://github.com/bytecodealliance/wasmtime/issues/4850
         // We filter out condition codes that aren't supported by the target at
         // this point after randomly choosing one, instead of randomly choosing a
         // supported one, to avoid invalidating the corpus when these get implemented.
-        if matches!(fgen.target_triple.architecture, Architecture::Aarch64(_))
-            && ![
-                FloatCC::Ordered,
-                FloatCC::Unordered,
-                FloatCC::Equal,
-                FloatCC::NotEqual,
-                FloatCC::LessThan,
-                FloatCC::LessThanOrEqual,
-                FloatCC::GreaterThan,
-                FloatCC::GreaterThanOrEqual,
-            ]
-            .contains(&cc)
-        {
-            return Err(arbitrary::Error::IncorrectFormat.into());
+        let unimplemented_cc = match (fgen.target_triple.architecture, cc) {
+            // Some FloatCC's are not implemented on AArch64, see:
+            // https://github.com/bytecodealliance/wasmtime/issues/4850
+            (Architecture::Aarch64(_), FloatCC::OrderedNotEqual) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrEqual) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrLessThan) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrLessThanOrEqual) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThan) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThanOrEqual) => true,
+
+            // These are not implemented on x86_64, for vectors.
+            (Architecture::X86_64, FloatCC::UnorderedOrEqual | FloatCC::OrderedNotEqual) => {
+                args[0].is_vector()
+            }
+            _ => false,
         };
+        if unimplemented_cc {
+            return Err(arbitrary::Error::IncorrectFormat.into());
+        }
 
         builder.ins().fcmp(cc, lhs, rhs)
     } else {
@@ -323,6 +326,68 @@ fn insert_atomic_cas(
     Ok(())
 }
 
+fn insert_shuffle(
+    fgen: &mut FunctionGenerator,
+    builder: &mut FunctionBuilder,
+    opcode: Opcode,
+    _: &'static [Type],
+    rets: &'static [Type],
+) -> Result<()> {
+    let ctrl_type = *rets.first().unwrap();
+
+    let lhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?);
+    let rhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?);
+
+    let mask = {
+        let mut lanes = [0u8; 16];
+        for lane in lanes.iter_mut() {
+            *lane = fgen.u.int_in_range(0..=31)?;
+        }
+        let lanes = ConstantData::from(lanes.as_ref());
+        builder.func.dfg.immediates.push(lanes)
+    };
+
+    // This function is called for any `InstructionFormat::Shuffle`. Which today is just
+    // `shuffle`, but lets assert that, just to be sure we don't accidentally insert
+    // something else.
+    assert_eq!(opcode, Opcode::Shuffle);
+    let res = builder.ins().shuffle(lhs, rhs, mask);
+
+    let target_var = fgen.get_variable_of_type(ctrl_type)?;
+    builder.def_var(target_var, res);
+
+    Ok(())
+}
+
+fn insert_ins_ext_lane(
+    fgen: &mut FunctionGenerator,
+    builder: &mut FunctionBuilder,
+    opcode: Opcode,
+    args: &'static [Type],
+    rets: &'static [Type],
+) -> Result<()> {
+    let vector_type = *args.first().unwrap();
+    let ret_type = *rets.first().unwrap();
+
+    let lhs = builder.use_var(fgen.get_variable_of_type(vector_type)?);
+    let max_lane = (vector_type.lane_count() as u8) - 1;
+    let lane = fgen.u.int_in_range(0..=max_lane)?;
+
+    let res = match opcode {
+        Opcode::Insertlane => {
+            let rhs = builder.use_var(fgen.get_variable_of_type(args[1])?);
+            builder.ins().insertlane(lhs, rhs, lane)
+        }
+        Opcode::Extractlane => builder.ins().extractlane(lhs, lane),
+        _ => todo!(),
+    };
+
+    let target_var = fgen.get_variable_of_type(ret_type)?;
+    builder.def_var(target_var, res);
+
+    Ok(())
+}
+
 type OpcodeInserter = fn(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
@@ -433,6 +498,8 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToUint, &[F64], &[I8]),
                 (Opcode::FcvtToUint, &[F64], &[I16]),
                 (Opcode::FcvtToUint, &[F64], &[I128]),
+                (Opcode::FcvtToUint, &[F32X4], &[I32X4]),
+                (Opcode::FcvtToUint, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4897
                 // https://github.com/bytecodealliance/wasmtime/issues/4899
                 (Opcode::FcvtToUintSat, &[F32], &[I8]),
@@ -441,6 +508,7 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToUintSat, &[F64], &[I8]),
                 (Opcode::FcvtToUintSat, &[F64], &[I16]),
                 (Opcode::FcvtToUintSat, &[F64], &[I128]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4897
                 // https://github.com/bytecodealliance/wasmtime/issues/4899
                 (Opcode::FcvtToSint, &[F32], &[I8]),
@@ -449,6 +517,8 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToSint, &[F64], &[I8]),
                 (Opcode::FcvtToSint, &[F64], &[I16]),
                 (Opcode::FcvtToSint, &[F64], &[I128]),
+                (Opcode::FcvtToSint, &[F32X4], &[I32X4]),
+                (Opcode::FcvtToSint, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4897
                 // https://github.com/bytecodealliance/wasmtime/issues/4899
                 (Opcode::FcvtToSintSat, &[F32], &[I8]),
@@ -457,12 +527,73 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToSintSat, &[F64], &[I8]),
                 (Opcode::FcvtToSintSat, &[F64], &[I16]),
                 (Opcode::FcvtToSintSat, &[F64], &[I128]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4900
                 (Opcode::FcvtFromUint, &[I128], &[F32]),
                 (Opcode::FcvtFromUint, &[I128], &[F64]),
+                // This has a lowering, but only when preceded by `uwiden_low`.
+                (Opcode::FcvtFromUint, &[I64X2], &[F64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4900
                 (Opcode::FcvtFromSint, &[I128], &[F32]),
                 (Opcode::FcvtFromSint, &[I128], &[F64]),
+                (Opcode::FcvtFromSint, &[I64X2], &[F64X2]),
+                (Opcode::Bmask, &[I8X16]),
+                (Opcode::Bmask, &[I16X8]),
+                (Opcode::Bmask, &[I32X4]),
+                (Opcode::Bmask, &[I64X2]),
+                (Opcode::Umulhi, &[I8X16, I8X16]),
+                (Opcode::Umulhi, &[I16X8, I16X8]),
+                (Opcode::Umulhi, &[I32X4, I32X4]),
+                (Opcode::Umulhi, &[I64X2, I64X2]),
+                (Opcode::Smulhi, &[I8X16, I8X16]),
+                (Opcode::Smulhi, &[I16X8, I16X8]),
+                (Opcode::Smulhi, &[I32X4, I32X4]),
+                (Opcode::Smulhi, &[I64X2, I64X2]),
+                (Opcode::UaddSat, &[I32X4, I32X4]),
+                (Opcode::UaddSat, &[I64X2, I64X2]),
+                (Opcode::SaddSat, &[I32X4, I32X4]),
+                (Opcode::SaddSat, &[I64X2, I64X2]),
+                (Opcode::UsubSat, &[I32X4, I32X4]),
+                (Opcode::UsubSat, &[I64X2, I64X2]),
+                (Opcode::SsubSat, &[I32X4, I32X4]),
+                (Opcode::SsubSat, &[I64X2, I64X2]),
+                (Opcode::Fcopysign, &[F32X4, F32X4]),
+                (Opcode::Fcopysign, &[F64X2, F64X2]),
+                (Opcode::Popcnt, &[I8X16]),
+                (Opcode::Popcnt, &[I16X8]),
+                (Opcode::Popcnt, &[I32X4]),
+                (Opcode::Popcnt, &[I64X2]),
+                (Opcode::Umax, &[I64X2, I64X2]),
+                (Opcode::Smax, &[I64X2, I64X2]),
+                (Opcode::Umin, &[I64X2, I64X2]),
+                (Opcode::Smin, &[I64X2, I64X2]),
+                (Opcode::Bitcast, &[I128], &[_]),
+                (Opcode::Bitcast, &[_], &[I128]),
+                (Opcode::Uunarrow),
+                (Opcode::Snarrow, &[I64X2, I64X2]),
+                (Opcode::Unarrow, &[I64X2, I64X2]),
+                (Opcode::SqmulRoundSat, &[I32X4, I32X4]),
+                // This Icmp is not implemented: #5529
+                (Opcode::Icmp, &[I64X2, I64X2]),
+                // IaddPairwise is implemented, but only for some types, and with some preceding ops.
+                (Opcode::IaddPairwise),
+                // Nothing wrong with this select. But we have an isle rule that can optimize it
+                // into a `min`/`max` instructions, which we don't have implemented yet.
+                (Opcode::Select, &[_, I128, I128]),
+                // These stack accesses can cause segfaults if they are merged into an SSE instruction.
+                // See: #5922
+                (Opcode::StackStore, &[I8X16], &[]),
+                (Opcode::StackStore, &[I16X8], &[]),
+                (Opcode::StackStore, &[I32X4], &[]),
+                (Opcode::StackStore, &[I64X2], &[]),
+                (Opcode::StackStore, &[F32X4], &[]),
+                (Opcode::StackStore, &[F64X2], &[]),
+                (Opcode::StackLoad, &[], &[I8X16]),
+                (Opcode::StackLoad, &[], &[I16X8]),
+                (Opcode::StackLoad, &[], &[I32X4]),
+                (Opcode::StackLoad, &[], &[I64X2]),
+                (Opcode::StackLoad, &[], &[F32X4]),
+                (Opcode::StackLoad, &[], &[F64X2]),
             )
         }
 
@@ -528,6 +659,24 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 // https://github.com/bytecodealliance/wasmtime/issues/4933
                 (Opcode::FcvtFromSint, &[I128], &[F32]),
                 (Opcode::FcvtFromSint, &[I128], &[F64]),
+                (Opcode::Bmask, &[I8X16]),
+                (Opcode::Bmask, &[I16X8]),
+                (Opcode::Bmask, &[I32X4]),
+                (Opcode::Bmask, &[I64X2]),
+                (Opcode::Umulhi, &[I8X16, I8X16]),
+                (Opcode::Umulhi, &[I16X8, I16X8]),
+                (Opcode::Umulhi, &[I32X4, I32X4]),
+                (Opcode::Umulhi, &[I64X2, I64X2]),
+                (Opcode::Smulhi, &[I8X16, I8X16]),
+                (Opcode::Smulhi, &[I16X8, I16X8]),
+                (Opcode::Smulhi, &[I32X4, I32X4]),
+                (Opcode::Smulhi, &[I64X2, I64X2]),
+                (Opcode::Popcnt, &[I16X8]),
+                (Opcode::Popcnt, &[I32X4]),
+                (Opcode::Popcnt, &[I64X2]),
+                // Nothing wrong with this select. But we have an isle rule that can optimize it
+                // into a `min`/`max` instructions, which we don't have implemented yet.
+                (Opcode::Select, &[I8, I128, I128]),
             )
         }
 
@@ -564,6 +713,12 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtFromUint, &[I128], &[F64]),
                 (Opcode::FcvtFromSint, &[I128], &[F32]),
                 (Opcode::FcvtFromSint, &[I128], &[F64]),
+                (Opcode::Bmask, &[I8X16]),
+                (Opcode::Bmask, &[I16X8]),
+                (Opcode::Bmask, &[I32X4]),
+                (Opcode::Bmask, &[I64X2]),
+                (Opcode::SsubSat, &[I64X2, I64X2]),
+                (Opcode::SaddSat, &[I64X2, I64X2]),
             )
         }
 
@@ -687,12 +842,32 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::IaddCout, &[I32, I32], &[I32, I8]),
     (Opcode::IaddCout, &[I64, I64], &[I64, I8]),
     (Opcode::IaddCout, &[I128, I128], &[I128, I8]),
+    // UaddSat
+    (Opcode::UaddSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::UaddSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::UaddSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::UaddSat, &[I64X2, I64X2], &[I64X2]),
+    // SaddSat
+    (Opcode::SaddSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::SaddSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::SaddSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::SaddSat, &[I64X2, I64X2], &[I64X2]),
     // Isub
     (Opcode::Isub, &[I8, I8], &[I8]),
     (Opcode::Isub, &[I16, I16], &[I16]),
     (Opcode::Isub, &[I32, I32], &[I32]),
     (Opcode::Isub, &[I64, I64], &[I64]),
     (Opcode::Isub, &[I128, I128], &[I128]),
+    // UsubSat
+    (Opcode::UsubSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::UsubSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::UsubSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::UsubSat, &[I64X2, I64X2], &[I64X2]),
+    // SsubSat
+    (Opcode::SsubSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::SsubSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::SsubSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::SsubSat, &[I64X2, I64X2], &[I64X2]),
     // Imul
     (Opcode::Imul, &[I8, I8], &[I8]),
     (Opcode::Imul, &[I16, I16], &[I16]),
@@ -704,11 +879,19 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Smulhi, &[I16, I16], &[I16]),
     (Opcode::Smulhi, &[I32, I32], &[I32]),
     (Opcode::Smulhi, &[I64, I64], &[I64]),
+    (Opcode::Smulhi, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Smulhi, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Smulhi, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Smulhi, &[I64X2, I64X2], &[I64X2]),
     // Umulhi
     (Opcode::Umulhi, &[I8, I8], &[I8]),
     (Opcode::Umulhi, &[I16, I16], &[I16]),
     (Opcode::Umulhi, &[I32, I32], &[I32]),
     (Opcode::Umulhi, &[I64, I64], &[I64]),
+    (Opcode::Umulhi, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Umulhi, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Umulhi, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Umulhi, &[I64X2, I64X2], &[I64X2]),
     // Udiv
     (Opcode::Udiv, &[I8, I8], &[I8]),
     (Opcode::Udiv, &[I16, I16], &[I16]),
@@ -755,24 +938,40 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Smin, &[I32, I32], &[I32]),
     (Opcode::Smin, &[I64, I64], &[I64]),
     (Opcode::Smin, &[I128, I128], &[I128]),
+    (Opcode::Smin, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Smin, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Smin, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Smin, &[I64X2, I64X2], &[I64X2]),
     // Umin
     (Opcode::Umin, &[I8, I8], &[I8]),
     (Opcode::Umin, &[I16, I16], &[I16]),
     (Opcode::Umin, &[I32, I32], &[I32]),
     (Opcode::Umin, &[I64, I64], &[I64]),
     (Opcode::Umin, &[I128, I128], &[I128]),
+    (Opcode::Umin, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Umin, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Umin, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Umin, &[I64X2, I64X2], &[I64X2]),
     // Smax
     (Opcode::Smax, &[I8, I8], &[I8]),
     (Opcode::Smax, &[I16, I16], &[I16]),
     (Opcode::Smax, &[I32, I32], &[I32]),
     (Opcode::Smax, &[I64, I64], &[I64]),
     (Opcode::Smax, &[I128, I128], &[I128]),
+    (Opcode::Smax, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Smax, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Smax, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Smax, &[I64X2, I64X2], &[I64X2]),
     // Umax
     (Opcode::Umax, &[I8, I8], &[I8]),
     (Opcode::Umax, &[I16, I16], &[I16]),
     (Opcode::Umax, &[I32, I32], &[I32]),
     (Opcode::Umax, &[I64, I64], &[I64]),
     (Opcode::Umax, &[I128, I128], &[I128]),
+    (Opcode::Umax, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Umax, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Umax, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Umax, &[I64X2, I64X2], &[I64X2]),
     // Rotr
     (Opcode::Rotr, &[I8, I8], &[I8]),
     (Opcode::Rotr, &[I8, I16], &[I8]),
@@ -1026,6 +1225,10 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Popcnt, &[I32], &[I32]),
     (Opcode::Popcnt, &[I64], &[I64]),
     (Opcode::Popcnt, &[I128], &[I128]),
+    (Opcode::Popcnt, &[I8X16], &[I8X16]),
+    (Opcode::Popcnt, &[I16X8], &[I16X8]),
+    (Opcode::Popcnt, &[I32X4], &[I32X4]),
+    (Opcode::Popcnt, &[I64X2], &[I64X2]),
     // Bmask
     (Opcode::Bmask, &[I8], &[I8]),
     (Opcode::Bmask, &[I16], &[I8]),
@@ -1052,6 +1255,10 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Bmask, &[I32], &[I128]),
     (Opcode::Bmask, &[I64], &[I128]),
     (Opcode::Bmask, &[I128], &[I128]),
+    (Opcode::Bmask, &[I8X16], &[I8X16]),
+    (Opcode::Bmask, &[I16X8], &[I16X8]),
+    (Opcode::Bmask, &[I32X4], &[I32X4]),
+    (Opcode::Bmask, &[I64X2], &[I64X2]),
     // Bswap
     (Opcode::Bswap, &[I16], &[I16]),
     (Opcode::Bswap, &[I32], &[I32]),
@@ -1142,6 +1349,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     // Fcopysign
     (Opcode::Fcopysign, &[F32, F32], &[F32]),
     (Opcode::Fcopysign, &[F64, F64], &[F64]),
+    (Opcode::Fcopysign, &[F32X4, F32X4], &[F32X4]),
+    (Opcode::Fcopysign, &[F64X2, F64X2], &[F64X2]),
     // Fma
     (Opcode::Fma, &[F32, F32, F32], &[F32]),
     (Opcode::Fma, &[F64, F64, F64], &[F64]),
@@ -1192,6 +1401,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtToUintSat, &[F64], &[I32]),
     (Opcode::FcvtToUintSat, &[F64], &[I64]),
     (Opcode::FcvtToUintSat, &[F64], &[I128]),
+    (Opcode::FcvtToUintSat, &[F32X4], &[I32X4]),
+    (Opcode::FcvtToUintSat, &[F64X2], &[I64X2]),
     // FcvtToSint
     (Opcode::FcvtToSint, &[F32], &[I8]),
     (Opcode::FcvtToSint, &[F32], &[I16]),
@@ -1214,6 +1425,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtToSintSat, &[F64], &[I32]),
     (Opcode::FcvtToSintSat, &[F64], &[I64]),
     (Opcode::FcvtToSintSat, &[F64], &[I128]),
+    (Opcode::FcvtToSintSat, &[F32X4], &[I32X4]),
+    (Opcode::FcvtToSintSat, &[F64X2], &[I64X2]),
     // FcvtFromUint
     (Opcode::FcvtFromUint, &[I8], &[F32]),
     (Opcode::FcvtFromUint, &[I16], &[F32]),
@@ -1225,6 +1438,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtFromUint, &[I32], &[F64]),
     (Opcode::FcvtFromUint, &[I64], &[F64]),
     (Opcode::FcvtFromUint, &[I128], &[F64]),
+    (Opcode::FcvtFromUint, &[I32X4], &[F32X4]),
+    (Opcode::FcvtFromUint, &[I64X2], &[F64X2]),
     // FcvtFromSint
     (Opcode::FcvtFromSint, &[I8], &[F32]),
     (Opcode::FcvtFromSint, &[I16], &[F32]),
@@ -1236,15 +1451,29 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtFromSint, &[I32], &[F64]),
     (Opcode::FcvtFromSint, &[I64], &[F64]),
     (Opcode::FcvtFromSint, &[I128], &[F64]),
+    (Opcode::FcvtFromSint, &[I32X4], &[F32X4]),
+    (Opcode::FcvtFromSint, &[I64X2], &[F64X2]),
+    // FcvtLowFromSint
+    (Opcode::FcvtLowFromSint, &[I32X4], &[F64X2]),
+    // FvpromoteLow
+    (Opcode::FvpromoteLow, &[F32X4], &[F64X2]),
+    // Fvdemote
+    (Opcode::Fvdemote, &[F64X2], &[F32X4]),
     // Fcmp
     (Opcode::Fcmp, &[F32, F32], &[I8]),
     (Opcode::Fcmp, &[F64, F64], &[I8]),
+    (Opcode::Fcmp, &[F32X4, F32X4], &[I32X4]),
+    (Opcode::Fcmp, &[F64X2, F64X2], &[I64X2]),
     // Icmp
     (Opcode::Icmp, &[I8, I8], &[I8]),
     (Opcode::Icmp, &[I16, I16], &[I8]),
     (Opcode::Icmp, &[I32, I32], &[I8]),
     (Opcode::Icmp, &[I64, I64], &[I8]),
     (Opcode::Icmp, &[I128, I128], &[I8]),
+    (Opcode::Icmp, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Icmp, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Icmp, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Icmp, &[I64X2, I64X2], &[I64X2]),
     // Fence
     (Opcode::Fence, &[], &[]),
     // Stack Access
@@ -1253,11 +1482,27 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::StackStore, &[I32], &[]),
     (Opcode::StackStore, &[I64], &[]),
     (Opcode::StackStore, &[I128], &[]),
+    (Opcode::StackStore, &[F32], &[]),
+    (Opcode::StackStore, &[F64], &[]),
+    (Opcode::StackStore, &[I8X16], &[]),
+    (Opcode::StackStore, &[I16X8], &[]),
+    (Opcode::StackStore, &[I32X4], &[]),
+    (Opcode::StackStore, &[I64X2], &[]),
+    (Opcode::StackStore, &[F32X4], &[]),
+    (Opcode::StackStore, &[F64X2], &[]),
     (Opcode::StackLoad, &[], &[I8]),
     (Opcode::StackLoad, &[], &[I16]),
     (Opcode::StackLoad, &[], &[I32]),
     (Opcode::StackLoad, &[], &[I64]),
     (Opcode::StackLoad, &[], &[I128]),
+    (Opcode::StackLoad, &[], &[F32]),
+    (Opcode::StackLoad, &[], &[F64]),
+    (Opcode::StackLoad, &[], &[I8X16]),
+    (Opcode::StackLoad, &[], &[I16X8]),
+    (Opcode::StackLoad, &[], &[I32X4]),
+    (Opcode::StackLoad, &[], &[I64X2]),
+    (Opcode::StackLoad, &[], &[F32X4]),
+    (Opcode::StackLoad, &[], &[F64X2]),
     // Loads
     (Opcode::Load, &[], &[I8]),
     (Opcode::Load, &[], &[I16]),
@@ -1266,6 +1511,12 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Load, &[], &[I128]),
     (Opcode::Load, &[], &[F32]),
     (Opcode::Load, &[], &[F64]),
+    (Opcode::Load, &[], &[I8X16]),
+    (Opcode::Load, &[], &[I16X8]),
+    (Opcode::Load, &[], &[I32X4]),
+    (Opcode::Load, &[], &[I64X2]),
+    (Opcode::Load, &[], &[F32X4]),
+    (Opcode::Load, &[], &[F64X2]),
     // Special Loads
     (Opcode::Uload8, &[], &[I16]),
     (Opcode::Uload8, &[], &[I32]),
@@ -1299,6 +1550,12 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Store, &[I128], &[]),
     (Opcode::Store, &[F32], &[]),
     (Opcode::Store, &[F64], &[]),
+    (Opcode::Store, &[I8X16], &[]),
+    (Opcode::Store, &[I16X8], &[]),
+    (Opcode::Store, &[I32X4], &[]),
+    (Opcode::Store, &[I64X2], &[]),
+    (Opcode::Store, &[F32X4], &[]),
+    (Opcode::Store, &[F64X2], &[]),
     // Special Stores
     (Opcode::Istore8, &[I16], &[]),
     (Opcode::Istore8, &[I32], &[]),
@@ -1326,6 +1583,76 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Bitcast, &[I32], &[F32]),
     (Opcode::Bitcast, &[F64], &[I64]),
     (Opcode::Bitcast, &[I64], &[F64]),
+    // Shuffle
+    (Opcode::Shuffle, &[I8X16, I8X16, I8X16], &[I8X16]),
+    // Swizzle
+    (Opcode::Swizzle, &[I8X16, I8X16], &[I8X16]),
+    // Splat
+    (Opcode::Splat, &[I8], &[I8X16]),
+    (Opcode::Splat, &[I16], &[I16X8]),
+    (Opcode::Splat, &[I32], &[I32X4]),
+    (Opcode::Splat, &[I64], &[I64X2]),
+    (Opcode::Splat, &[F32], &[F32X4]),
+    (Opcode::Splat, &[F64], &[F64X2]),
+    // Insert Lane
+    (Opcode::Insertlane, &[I8X16, I8], &[I8X16]),
+    (Opcode::Insertlane, &[I16X8, I16], &[I16X8]),
+    (Opcode::Insertlane, &[I32X4, I32], &[I32X4]),
+    (Opcode::Insertlane, &[I64X2, I64], &[I64X2]),
+    (Opcode::Insertlane, &[F32X4, F32], &[F32X4]),
+    (Opcode::Insertlane, &[F64X2, F64], &[F64X2]),
+    // Extract Lane
+    (Opcode::Extractlane, &[I8X16], &[I8]),
+    (Opcode::Extractlane, &[I16X8], &[I16]),
+    (Opcode::Extractlane, &[I32X4], &[I32]),
+    (Opcode::Extractlane, &[I64X2], &[I64]),
+    (Opcode::Extractlane, &[F32X4], &[F32]),
+    (Opcode::Extractlane, &[F64X2], &[F64]),
+    // Snarrow
+    (Opcode::Snarrow, &[I64X2, I64X2], &[I32X4]),
+    (Opcode::Snarrow, &[I32X4, I32X4], &[I16X8]),
+    (Opcode::Snarrow, &[I16X8, I16X8], &[I8X16]),
+    // Unarrow
+    (Opcode::Unarrow, &[I64X2, I64X2], &[I32X4]),
+    (Opcode::Unarrow, &[I32X4, I32X4], &[I16X8]),
+    (Opcode::Unarrow, &[I16X8, I16X8], &[I8X16]),
+    // Uunarrow
+    (Opcode::Uunarrow, &[I64X2, I64X2], &[I32X4]),
+    (Opcode::Uunarrow, &[I32X4, I32X4], &[I16X8]),
+    (Opcode::Uunarrow, &[I16X8, I16X8], &[I8X16]),
+    // VhighBits
+    (Opcode::VhighBits, &[I8X16], &[I8]),
+    (Opcode::VhighBits, &[I16X8], &[I8]),
+    (Opcode::VhighBits, &[I32X4], &[I8]),
+    (Opcode::VhighBits, &[I64X2], &[I8]),
+    // VanyTrue
+    (Opcode::VanyTrue, &[I8X16, I8X16, I8X16], &[I8]),
+    (Opcode::VanyTrue, &[I16X8, I16X8, I16X8], &[I8]),
+    (Opcode::VanyTrue, &[I32X4, I32X4, I32X4], &[I8]),
+    (Opcode::VanyTrue, &[I64X2, I64X2, I64X2], &[I8]),
+    // SwidenLow
+    (Opcode::SwidenLow, &[I8X16], &[I16X8]),
+    (Opcode::SwidenLow, &[I16X8], &[I32X4]),
+    (Opcode::SwidenLow, &[I32X4], &[I64X2]),
+    // SwidenHigh
+    (Opcode::SwidenHigh, &[I8X16], &[I16X8]),
+    (Opcode::SwidenHigh, &[I16X8], &[I32X4]),
+    (Opcode::SwidenHigh, &[I32X4], &[I64X2]),
+    // UwidenLow
+    (Opcode::UwidenLow, &[I8X16], &[I16X8]),
+    (Opcode::UwidenLow, &[I16X8], &[I32X4]),
+    (Opcode::UwidenLow, &[I32X4], &[I64X2]),
+    // UwidenHigh
+    (Opcode::UwidenHigh, &[I8X16], &[I16X8]),
+    (Opcode::UwidenHigh, &[I16X8], &[I32X4]),
+    (Opcode::UwidenHigh, &[I32X4], &[I64X2]),
+    // SqmulRoundSat
+    (Opcode::SqmulRoundSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::SqmulRoundSat, &[I32X4, I32X4], &[I32X4]),
+    // IaddPairwise
+    (Opcode::IaddPairwise, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::IaddPairwise, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::IaddPairwise, &[I32X4, I32X4], &[I32X4]),
     // Integer Consts
     (Opcode::Iconst, &[], &[I8]),
     (Opcode::Iconst, &[], &[I16]),
@@ -1334,6 +1661,13 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     // Float Consts
     (Opcode::F32const, &[], &[F32]),
     (Opcode::F64const, &[], &[F64]),
+    // Vector Consts
+    (Opcode::Vconst, &[], &[I8X16]),
+    (Opcode::Vconst, &[], &[I16X8]),
+    (Opcode::Vconst, &[], &[I32X4]),
+    (Opcode::Vconst, &[], &[I64X2]),
+    (Opcode::Vconst, &[], &[F32X4]),
+    (Opcode::Vconst, &[], &[F64X2]),
     // Call
     (Opcode::Call, &[], &[]),
 ];
@@ -1344,7 +1678,7 @@ fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
         InstructionFormat::AtomicRmw => insert_atomic_rmw,
         InstructionFormat::Binary => insert_opcode,
         InstructionFormat::BinaryImm64 => todo!(),
-        InstructionFormat::BinaryImm8 => todo!(),
+        InstructionFormat::BinaryImm8 => insert_ins_ext_lane,
         InstructionFormat::Call => insert_call,
         InstructionFormat::CallIndirect => insert_call,
         InstructionFormat::CondTrap => todo!(),
@@ -1358,14 +1692,14 @@ fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
         InstructionFormat::Load => insert_load_store,
         InstructionFormat::LoadNoOffset => insert_load_store,
         InstructionFormat::NullAry => insert_opcode,
-        InstructionFormat::Shuffle => todo!(),
+        InstructionFormat::Shuffle => insert_shuffle,
         InstructionFormat::StackLoad => insert_stack_load,
         InstructionFormat::StackStore => insert_stack_store,
         InstructionFormat::Store => insert_load_store,
         InstructionFormat::StoreNoOffset => insert_load_store,
         InstructionFormat::TableAddr => todo!(),
         InstructionFormat::Ternary => insert_opcode,
-        InstructionFormat::TernaryImm8 => todo!(),
+        InstructionFormat::TernaryImm8 => insert_ins_ext_lane,
         InstructionFormat::Trap => todo!(),
         InstructionFormat::Unary => insert_opcode,
         InstructionFormat::UnaryConst => insert_const,
@@ -1561,6 +1895,12 @@ where
         );
         let aligned = if is_atomic && requires_aligned_atomics {
             true
+        } else if min_size > 8 {
+            // TODO: We currently can't guarantee that a stack_slot will be aligned on a 16 byte
+            // boundary. We don't have a way to specify alignment when creating stack slots, and
+            // cranelift only guarantees 8 byte alignment between stack slots.
+            // See: https://github.com/bytecodealliance/wasmtime/issues/5922#issuecomment-1457926624
+            false
         } else {
             bool::arbitrary(self.u)?
         };
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 3488096ac..cb9f0ef82 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -196,6 +196,11 @@ pub struct CommonOptions {
     #[clap(long, value_name = "SIZE")]
     pub dynamic_memory_guard_size: Option<u64>,
 
+    /// Bytes to reserve at the end of linear memory for growth for dynamic
+    /// memories.
+    #[clap(long, value_name = "SIZE")]
+    pub dynamic_memory_reserved_for_growth: Option<u64>,
+
     /// Enable Cranelift's internal debug verifier (expensive)
     #[clap(long)]
     pub enable_cranelift_debug_verifier: bool,
@@ -323,6 +328,9 @@ impl CommonOptions {
         if let Some(size) = self.dynamic_memory_guard_size {
             config.dynamic_memory_guard_size(size);
         }
+        if let Some(size) = self.dynamic_memory_reserved_for_growth {
+            config.dynamic_memory_reserved_for_growth(size);
+        }
 
         // If fuel has been configured, set the `consume fuel` flag on the config.
         if self.fuel.is_some() {
diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs
index d9c9d8c95..aed73950e 100644
--- a/crates/cranelift/src/lib.rs
+++ b/crates/cranelift/src/lib.rs
@@ -135,7 +135,7 @@ fn indirect_signature(isa: &dyn TargetIsa, wasm: &WasmFuncType) -> ir::Signature
     return sig;
 }
 
-/// Returns the cranelift fucntion signature of the function specified.
+/// Returns the cranelift function signature of the function specified.
 ///
 /// Note that this will determine the calling convention for the function, and
 /// namely includes an optimization where functions never exported from a module
diff --git a/crates/explorer/Cargo.toml b/crates/explorer/Cargo.toml
new file mode 100644
index 000000000..59eff18c9
--- /dev/null
+++ b/crates/explorer/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "wasmtime-explorer"
+authors.workspace = true
+description = "Compiler explorer for Wasmtime and Cranelift"
+documentation = "https://docs.rs/wasmtime-explorer/"
+edition.workspace = true
+license = "Apache-2.0 WITH LLVM-exception"
+repository = "https://github.com/bytecodealliance/wasmtime"
+version.workspace = true
+
+[dependencies]
+anyhow = { workspace = true }
+capstone = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+target-lexicon = { workspace = true }
+wasmprinter = { workspace = true }
+wasmtime = { workspace = true, features = ["cranelift"] }
diff --git a/crates/explorer/src/.eslintrc.yml b/crates/explorer/src/.eslintrc.yml
new file mode 100644
index 000000000..6bd1f3477
--- /dev/null
+++ b/crates/explorer/src/.eslintrc.yml
@@ -0,0 +1,8 @@
+root: true
+
+env:
+  browser: true
+  es2022: true
+
+extends:
+  - "eslint:recommended"
diff --git a/crates/explorer/src/index.css b/crates/explorer/src/index.css
new file mode 100644
index 000000000..ba23a1fbe
--- /dev/null
+++ b/crates/explorer/src/index.css
@@ -0,0 +1,26 @@
+* {
+    margin: 0;
+    padding: 0;
+}
+
+.hbox {
+    display: flex;
+    flex-direction: row;
+}
+
+html, body {
+    width: 100%;
+    height: 100%;
+}
+
+#wat {
+    width: 50%;
+    height: 100%;
+    overflow: scroll;
+}
+
+#asm {
+    width: 50%;
+    height: 100%;
+    overflow: scroll;
+}
diff --git a/crates/explorer/src/index.js b/crates/explorer/src/index.js
new file mode 100644
index 000000000..afcfac9e5
--- /dev/null
+++ b/crates/explorer/src/index.js
@@ -0,0 +1,238 @@
+/*** State *********************************************************************/
+
+class State {
+  constructor(wat, asm) {
+    this.wat = wat;
+    this.asm = asm;
+  }
+}
+
+const state = window.STATE = new State(window.WAT, window.ASM);
+
+/*** Hues for Offsets **********************************************************/
+
+const hues = [
+  80,
+  160,
+  240,
+  320,
+  40,
+  120,
+  200,
+  280,
+  20,
+  100,
+  180,
+  260,
+  340,
+  60,
+  140,
+  220,
+  300,
+];
+
+const nextHue = (function () {
+  let i = 0;
+  return () => {
+    return hues[++i % hues.length];
+  };
+}());
+
+// NB: don't just assign hues based on something simple like `hues[offset %
+// hues.length]` since that can suffer from bias due to certain alignments
+// happening more or less frequently.
+const offsetToHue = new Map();
+
+// Get the hue for the given offset, or assign it a new one if it doesn't have
+// one already.
+const hueForOffset = offset => {
+  if (offsetToHue.has(offset)) {
+    return offsetToHue.get(offset);
+  } else {
+    let hue = nextHue();
+    offsetToHue.set(offset, hue);
+    return hue;
+  }
+};
+
+// Get the hue for the given offset, only if the offset has already been
+// assigned a hue.
+const existingHueForOffset = offset => {
+  return offsetToHue.get(offset);
+};
+
+// Get WAT chunk elements by Wasm offset.
+const watByOffset = new Map();
+
+// Get asm instruction elements by Wasm offset.
+const asmByOffset = new Map();
+
+// Get all (WAT chunk or asm instruction) elements by offset.
+const anyByOffset = new Map();
+
+const addWatElem = (offset, elem) => {
+  if (!watByOffset.has(offset)) {
+    watByOffset.set(offset, []);
+  }
+  watByOffset.get(offset).push(elem);
+
+  if (!anyByOffset.has(offset)) {
+    anyByOffset.set(offset, []);
+  }
+  anyByOffset.get(offset).push(elem);
+};
+
+const addAsmElem = (offset, elem) => {
+  if (!asmByOffset.has(offset)) {
+    asmByOffset.set(offset, []);
+  }
+  asmByOffset.get(offset).push(elem);
+
+  if (!anyByOffset.has(offset)) {
+    anyByOffset.set(offset, []);
+  }
+  anyByOffset.get(offset).push(elem);
+};
+
+/*** Event Handlers ************************************************************/
+
+const watElem = document.getElementById("wat");
+watElem.addEventListener("click", event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  if (!asmByOffset.get(offset)) {
+    return;
+  }
+
+  const firstAsmElem = asmByOffset.get(offset)[0];
+  firstAsmElem.scrollIntoView({
+    behavior: "smooth",
+    block: "center",
+    inline: "nearest",
+  });
+}, { passive: true });
+
+const asmElem = document.getElementById("asm");
+asmElem.addEventListener("click", event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  if (!watByOffset.get(offset)) {
+    return;
+  }
+
+  const firstWatElem = watByOffset.get(offset)[0];
+  firstWatElem.scrollIntoView({
+    behavior: "smooth",
+    block: "center",
+    inline: "nearest",
+  });
+}, { passive: true });
+
+const onMouseEnter = event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  const hue = hueForOffset(offset);
+  for (const elem of anyByOffset.get(offset)) {
+    elem.style.backgroundColor = `hsl(${hue} 75% 80%)`;
+  }
+};
+
+const onMouseLeave = event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  const hue = hueForOffset(offset);
+  for (const elem of anyByOffset.get(offset)) {
+    elem.style.backgroundColor = `hsl(${hue} 50% 95%)`;
+  }
+};
+
+/*** Rendering *****************************************************************/
+
+const repeat = (s, n) => {
+  return s.repeat(n >= 0 ? n : 0);
+};
+
+const renderAddress = addr => {
+  let hex = addr.toString(16);
+  return repeat("0", 8 - hex.length) + hex;
+};
+
+const renderBytes = bytes => {
+  let s = "";
+  for (let i = 0; i < bytes.length; i++) {
+    if (i != 0) {
+      s += " ";
+    }
+    const hexByte = bytes[i].toString(16);
+    s += hexByte.length == 2 ? hexByte : "0" + hexByte;
+  }
+  return s + repeat(" ", 30 - s.length);
+};
+
+const renderInst = (mnemonic, operands) => {
+  if (operands.length == 0) {
+    return mnemonic;
+  } else {
+    return mnemonic + " " + operands;
+  }
+};
+
+// Render the ASM.
+
+let nthFunc = 0;
+for (const func of state.asm.functions) {
+  const funcElem = document.createElement("div");
+
+  const funcHeader = document.createElement("h3");
+  funcHeader.textContent = `Defined Function ${nthFunc}`;
+  funcElem.appendChild(funcHeader);
+
+  const bodyElem = document.createElement("pre");
+  for (const inst of func.instructions) {
+    const instElem = document.createElement("span");
+    instElem.textContent = `${renderAddress(inst.address)}    ${renderBytes(inst.bytes)}    ${renderInst(inst.mnemonic, inst.operands)}\n`;
+    if (inst.wasm_offset != null) {
+      instElem.setAttribute("data-wasm-offset", inst.wasm_offset);
+      const hue = hueForOffset(inst.wasm_offset);
+      instElem.style.backgroundColor = `hsl(${hue} 50% 90%)`;
+      instElem.addEventListener("mouseenter", onMouseEnter);
+      instElem.addEventListener("mouseleave", onMouseLeave);
+      addAsmElem(inst.wasm_offset, instElem);
+    }
+    bodyElem.appendChild(instElem);
+  }
+  funcElem.appendChild(bodyElem);
+
+  asmElem.appendChild(funcElem);
+  nthFunc++;
+}
+
+// Render the WAT.
+
+for (const chunk of state.wat.chunks) {
+  const chunkElem = document.createElement("span");
+  if (chunk.wasm_offset != null) {
+    chunkElem.dataset.wasmOffset = chunk.wasm_offset;
+    const hue = existingHueForOffset(chunk.wasm_offset);
+    if (hue) {
+      chunkElem.style.backgroundColor = `hsl(${hue} 50% 95%)`;
+      chunkElem.addEventListener("mouseenter", onMouseEnter);
+      chunkElem.addEventListener("mouseleave", onMouseLeave);
+      addWatElem(chunk.wasm_offset, chunkElem);
+    }
+  }
+  chunkElem.textContent = chunk.wat;
+  watElem.appendChild(chunkElem);
+}
diff --git a/crates/explorer/src/lib.rs b/crates/explorer/src/lib.rs
new file mode 100644
index 000000000..5dab3e847
--- /dev/null
+++ b/crates/explorer/src/lib.rs
@@ -0,0 +1,189 @@
+use anyhow::Result;
+use capstone::arch::BuildsCapstone;
+use serde::Serialize;
+use std::{io::Write, str::FromStr};
+
+pub fn generate(
+    config: &wasmtime::Config,
+    target: Option<&str>,
+    wasm: &[u8],
+    dest: &mut dyn Write,
+) -> Result<()> {
+    let target = match target {
+        None => target_lexicon::Triple::host(),
+        Some(target) => target_lexicon::Triple::from_str(target)?,
+    };
+
+    let wat = annotate_wat(wasm)?;
+    let wat_json = serde_json::to_string(&wat)?;
+    let asm = annotate_asm(config, &target, wasm)?;
+    let asm_json = serde_json::to_string(&asm)?;
+
+    let index_css = include_str!("./index.css");
+    let index_js = include_str!("./index.js");
+
+    write!(
+        dest,
+        r#"
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Wasmtime Compiler Explorer</title>
+    <style>
+      {index_css}
+    </style>
+  </head>
+  <body class="hbox">
+    <pre id="wat"></pre>
+    <div id="asm"></div>
+    <script>
+      window.WAT = {wat_json};
+      window.ASM = {asm_json};
+    </script>
+    <script>
+      {index_js}
+    </script>
+  </body>
+</html>
+        "#
+    )?;
+    Ok(())
+}
+
+#[derive(Serialize, Clone, Copy, Debug)]
+struct WasmOffset(u32);
+
+#[derive(Serialize, Debug)]
+struct AnnotatedWat {
+    chunks: Vec<AnnotatedWatChunk>,
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedWatChunk {
+    wasm_offset: Option<WasmOffset>,
+    wat: String,
+}
+
+fn annotate_wat(wasm: &[u8]) -> Result<AnnotatedWat> {
+    let mut printer = wasmprinter::Printer::new();
+    let chunks = printer
+        .offsets_and_lines(wasm)?
+        .map(|(offset, wat)| AnnotatedWatChunk {
+            wasm_offset: offset.map(|o| WasmOffset(u32::try_from(o).unwrap())),
+            wat: wat.to_string(),
+        })
+        .collect();
+    Ok(AnnotatedWat { chunks })
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedAsm {
+    functions: Vec<AnnotatedFunction>,
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedFunction {
+    instructions: Vec<AnnotatedInstruction>,
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedInstruction {
+    wasm_offset: Option<WasmOffset>,
+    address: u32,
+    bytes: Vec<u8>,
+    mnemonic: Option<String>,
+    operands: Option<String>,
+}
+
+fn annotate_asm(
+    config: &wasmtime::Config,
+    target: &target_lexicon::Triple,
+    wasm: &[u8],
+) -> Result<AnnotatedAsm> {
+    let engine = wasmtime::Engine::new(config)?;
+    let module = wasmtime::Module::new(&engine, wasm)?;
+
+    let text = module.text();
+    let address_map: Vec<_> = module
+        .address_map()
+        .ok_or_else(|| anyhow::anyhow!("address maps must be enabled in the config"))?
+        .collect();
+
+    let mut address_map_iter = address_map.into_iter().peekable();
+    let mut current_entry = address_map_iter.next();
+    let mut wasm_offset_for_address = |start: usize, address: u32| -> Option<WasmOffset> {
+        // Consume any entries that happened before the current function for the
+        // first instruction.
+        while current_entry.map_or(false, |cur| cur.0 < start) {
+            current_entry = address_map_iter.next();
+        }
+
+        // Next advance the address map up to the current `address` specified,
+        // including it.
+        while address_map_iter.peek().map_or(false, |next_entry| {
+            u32::try_from(next_entry.0).unwrap() <= address
+        }) {
+            current_entry = address_map_iter.next();
+        }
+        current_entry.and_then(|entry| entry.1.map(WasmOffset))
+    };
+
+    let functions = module
+        .function_locations()
+        .into_iter()
+        .map(|(start, len)| {
+            let body = &text[start..][..len];
+
+            let mut cs = match target.architecture {
+                target_lexicon::Architecture::Aarch64(_) => capstone::Capstone::new()
+                    .arm64()
+                    .mode(capstone::arch::arm64::ArchMode::Arm)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                target_lexicon::Architecture::Riscv64(_) => capstone::Capstone::new()
+                    .riscv()
+                    .mode(capstone::arch::riscv::ArchMode::RiscV64)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                target_lexicon::Architecture::S390x => capstone::Capstone::new()
+                    .sysz()
+                    .mode(capstone::arch::sysz::ArchMode::Default)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                target_lexicon::Architecture::X86_64 => capstone::Capstone::new()
+                    .x86()
+                    .mode(capstone::arch::x86::ArchMode::Mode64)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                _ => anyhow::bail!("Unsupported target: {target}"),
+            };
+
+            // This tells capstone to skip over anything that looks like data,
+            // such as inline constant pools and things like that. This also
+            // additionally is required to skip over trapping instructions on
+            // AArch64.
+            cs.set_skipdata(true).unwrap();
+
+            let instructions = cs
+                .disasm_all(body, start as u64)
+                .map_err(|e| anyhow::anyhow!("{e}"))?;
+            let instructions = instructions
+                .iter()
+                .map(|inst| {
+                    let address = u32::try_from(inst.address()).unwrap();
+                    let wasm_offset = wasm_offset_for_address(start, address);
+                    Ok(AnnotatedInstruction {
+                        wasm_offset,
+                        address,
+                        bytes: inst.bytes().to_vec(),
+                        mnemonic: inst.mnemonic().map(ToString::to_string),
+                        operands: inst.op_str().map(ToString::to_string),
+                    })
+                })
+                .collect::<Result<Vec<_>>>()?;
+            Ok(AnnotatedFunction { instructions })
+        })
+        .collect::<Result<Vec<_>>>()?;
+
+    Ok(AnnotatedAsm { functions })
+}
diff --git a/scripts/publish.rs b/scripts/publish.rs
index 964f75bb0..73a229a54 100644
--- a/scripts/publish.rs
+++ b/scripts/publish.rs
@@ -70,6 +70,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
     "wasmtime-wasi-threads",
     "wasmtime-wast",
     "wasmtime-cli-flags",
+    "wasmtime-explorer",
     "wasmtime-cli",
 ];
 
diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs
index f4b908ef0..65a4f69d3 100644
--- a/src/bin/wasmtime.rs
+++ b/src/bin/wasmtime.rs
@@ -6,7 +6,7 @@
 use anyhow::Result;
 use clap::{ErrorKind, Parser};
 use wasmtime_cli::commands::{
-    CompileCommand, ConfigCommand, RunCommand, SettingsCommand, WastCommand,
+    CompileCommand, ConfigCommand, ExploreCommand, RunCommand, SettingsCommand, WastCommand,
 };
 
 /// Wasmtime WebAssembly Runtime
@@ -35,6 +35,8 @@ enum Wasmtime {
     Config(ConfigCommand),
     /// Compiles a WebAssembly module.
     Compile(CompileCommand),
+    /// Explore the compilation of a WebAssembly module to native code.
+    Explore(ExploreCommand),
     /// Runs a WebAssembly module
     Run(RunCommand),
     /// Displays available Cranelift settings for a target.
@@ -49,6 +51,7 @@ impl Wasmtime {
         match self {
             Self::Config(c) => c.execute(),
             Self::Compile(c) => c.execute(),
+            Self::Explore(c) => c.execute(),
             Self::Run(c) => c.execute(),
             Self::Settings(c) => c.execute(),
             Self::Wast(c) => c.execute(),
diff --git a/src/commands.rs b/src/commands.rs
index be3a9624e..764f01e93 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -2,8 +2,9 @@
 
 mod compile;
 mod config;
+mod explore;
 mod run;
 mod settings;
 mod wast;
 
-pub use self::{compile::*, config::*, run::*, settings::*, wast::*};
+pub use self::{compile::*, config::*, explore::*, run::*, settings::*, wast::*};
diff --git a/src/commands/explore.rs b/src/commands/explore.rs
new file mode 100644
index 000000000..46c2bd79e
--- /dev/null
+++ b/src/commands/explore.rs
@@ -0,0 +1,51 @@
+//! The module that implements the `wasmtime explore` command.
+
+use anyhow::{Context, Result};
+use clap::Parser;
+use std::path::PathBuf;
+use wasmtime_cli_flags::CommonOptions;
+
+/// Explore the compilation of a WebAssembly module to native code.
+#[derive(Parser)]
+#[clap(name = "explore")]
+pub struct ExploreCommand {
+    #[clap(flatten)]
+    common: CommonOptions,
+
+    /// The target triple; default is the host triple
+    #[clap(long, value_name = "TARGET")]
+    target: Option<String>,
+
+    /// The path of the WebAssembly module to compile
+    #[clap(required = true, value_name = "MODULE")]
+    module: PathBuf,
+
+    /// The path of the explorer output (derived from the MODULE name if none
+    /// provided)
+    #[clap(short, long)]
+    output: Option<PathBuf>,
+}
+
+impl ExploreCommand {
+    /// Executes the command.
+    pub fn execute(&self) -> Result<()> {
+        self.common.init_logging();
+
+        let config = self.common.config(self.target.as_deref())?;
+
+        let wasm = std::fs::read(&self.module)
+            .with_context(|| format!("failed to read Wasm module: {}", self.module.display()))?;
+
+        let output = self
+            .output
+            .clone()
+            .unwrap_or_else(|| self.module.with_extension("explore.html"));
+        let output_file = std::fs::File::create(&output)
+            .with_context(|| format!("failed to create file: {}", output.display()))?;
+        let mut output_file = std::io::BufWriter::new(output_file);
+
+        wasmtime_explorer::generate(&config, self.target.as_deref(), &wasm, &mut output_file)?;
+        println!("Exploration written to {}", output.display());
+        Ok(())
+    }
+}
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 32a1210ab..17ebd39fe 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1,6 +1,27 @@
 
 # cargo-vet audits file
 
+[[wildcard-audits.bumpalo]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+user-id = 696
+start = "2019-03-16"
+end = "2024-03-10"
+
+[[wildcard-audits.wasm-mutate]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+user-id = 696
+start = "2022-02-17"
+end = "2024-03-10"
+
+[[wildcard-audits.wasmprinter]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+user-id = 696
+start = "2021-04-28"
+end = "2024-03-10"
+
 [[audits.addr2line]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 4f333db00..c4bc9af4d 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1,6 +1,20 @@
 
 # cargo-vet imports lock
 
+[[publisher.wasm-mutate]]
+version = "0.2.22"
+when = "2023-03-10"
+user-id = 696
+user-login = "fitzgen"
+user-name = "Nick Fitzgerald"
+
+[[publisher.wasmprinter]]
+version = "0.2.54"
+when = "2023-03-10"
+user-id = 696
+user-login = "fitzgen"
+user-name = "Nick Fitzgerald"
+
 [audits.chromeos.criteria.crypto-safe]
 description = """
 All crypto algorithms in this crate have been reviewed by a relevant expert.


Test patch:
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
index 0670a3fae..e06221836 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
@@ -2657,6 +2657,7 @@ fn test_aarch64_binemit() {
             rd: writable_vreg(14),
             rn: vreg(19),
             size: VectorSize::Size32x4,
+            lane: 0,
         },
         "6E06044E",
         "dup v14.4s, v19.s[0]",
@@ -2666,6 +2667,7 @@ fn test_aarch64_binemit() {
             rd: writable_vreg(18),
             rn: vreg(10),
             size: VectorSize::Size64x2,
+            lane: 0,
         },
         "5205084E",
         "dup v18.2d, v10.d[0]",
diff --git a/cranelift/filetests/filetests/isa/aarch64/issue-5985.clif b/cranelift/filetests/filetests/isa/aarch64/issue-5985.clif
new file mode 100644
index 000000000..7c14c4774
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/aarch64/issue-5985.clif
@@ -0,0 +1,20 @@
+test compile precise-output
+target aarch64
+
+function %a() -> f32x4 system_v {
+block0:
+    v16 = f32const 0x1.fffe00p-126
+    v25 = splat.f32x4 v16
+    return v25
+}
+
+; VCode:
+; block0:
+;   movi v0.2d, #72056494543077120
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   movi v0.2d, #0xffff0000ffff00
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/aarch64/shuffle.clif b/cranelift/filetests/filetests/isa/aarch64/shuffle.clif
new file mode 100644
index 000000000..c52959b88
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/aarch64/shuffle.clif
@@ -0,0 +1,747 @@
+test compile precise-output
+set unwind_info=false
+target aarch64
+
+function %shuffle_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [3 0 31 26 4 6 12 11 23 13 24 4 2 15 17 5]
+    return v2
+}
+
+; VCode:
+; block0:
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, pc+8 ; b 20 ; data.f128 0x05110f0204180d170b0c06041a1f0003
+;   tbl v0.16b, { v30.16b, v31.16b }, v3.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, #0x10
+;   b #0x20
+;   adc w3, w0, wzr
+;   add w4, w16, w12, lsl #1
+;   orr z23.b, p3/m, z23.b, z8.b
+;   mov z2.b, p1/z, #0x78
+;   tbl v0.16b, {v30.16b, v31.16b}, v3.16b
+;   ret
+
+function %aarch64_uzp1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30]
+    return v2
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_uzp2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_uzp1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 4 5 8 9 12 13 16 17 20 21 24 25 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_uzp2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 6 7 10 11 14 15 18 19 22 23 26 27 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_uzp1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_uzp2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 12 13 14 15 20 21 22 23 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_uzp1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_uzp2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %punpcklbw(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 16 1 17 2 18 3 19 4 20 5 21 6 22 7 23]
+    return v2
+}
+
+; VCode:
+; block0:
+;   zip1 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip1 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %punpckhbw(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [8 24 9 25 10 26 11 27 12 28 13 29 14 30 15 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   zip2 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip2 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %punpcklwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 2 3 18 19 4 5 20 21 6 7 22 23]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip1 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip1 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %punpckhwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 24 25 10 11 26 27 12 13 28 29 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip2 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip2 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %punpckldq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 4 5 6 7 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip1 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip1 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %punpckhdq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 24 25 26 27 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip2 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip2 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %punpcklqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %punpckhqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_trn1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 16 2 18 4 20 6 22 8 24 10 26 12 28 14 30]
+    return v2
+}
+
+; VCode:
+; block0:
+;   trn1 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn1 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_trn2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 17 3 19 5 21 7 23 9 25 11 27 13 29 15 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   trn2 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn2 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_trn1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 4 5 20 21 8 9 24 25 12 13 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn1 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn1 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_trn2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 18 19 6 7 22 23 10 11 26 27 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn2 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn2 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_trn1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 8 9 10 11 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn1 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn1 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_trn2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 20 21 22 23 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn2 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn2 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_trn1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_trn2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_ext_0(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #0
+;   ret
+
+function %aarch64_ext_1(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #1
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #1
+;   ret
+
+function %aarch64_ext_5(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #5
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #5
+;   ret
+
+function %aarch64_ext_11(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #11
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #0xb
+;   ret
+
+function %aarch64_ext_16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, pc+8 ; b 20 ; data.f128 0x1f1e1d1c1b1a19181716151413121110
+;   tbl v0.16b, { v30.16b, v31.16b }, v3.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, #0x10
+;   b #0x20
+;   sbfiz w16, w8, #0xe, #5
+;   b #0xfffffffffc585464
+;   madd w24, w8, w26, w6
+;   fmadd s28, s8, s30, s7
+;   tbl v0.16b, {v30.16b, v31.16b}, v3.16b
+;   ret
+
+function %aarch64_dup_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5]
+    return v2
+}
+
+; VCode:
+; block0:
+;   dup v0.16b, v0.b[5]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.16b, v0.b[5]
+;   ret
+
+function %aarch64_dup_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 12 13 12 13 12 13 12 13 12 13 12 13 12 13]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   dup v0.8h, v0.h[6]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.8h, v0.h[6]
+;   ret
+
+function %aarch64_dup_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 14 15 12 13 14 15 12 13 14 15 12 13 14 15]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   dup v0.4s, v0.s[3]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.4s, v0.s[3]
+;   ret
+
+function %aarch64_dup_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   dup v0.2d, v0.d[0]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.2d, v0.d[0]
+;   ret
+
+function %aarch64_rev16(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [1 0 3 2 5 4 7 6 9 8 11 10 13 12 15 14]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev16 v0.16b, v0.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev16 v0.16b, v0.16b
+;   ret
+
+function %aarch64_rev32_bytes(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev32 v0.16b, v0.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev32 v0.16b, v0.16b
+;   ret
+
+function %aarch64_rev32_words(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 0 1 6 7 4 5 10 11 8 9 14 15 12 13]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev32 v0.8h, v0.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev32 v0.8h, v0.8h
+;   ret
+
+function %aarch64_rev64_bytes(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev64 v0.16b, v0.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev64 v0.16b, v0.16b
+;   ret
+
+function %aarch64_rev64_words(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev64 v0.8h, v0.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev64 v0.8h, v0.8h
+;   ret
+
+function %aarch64_rev64_doublewords(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 0 1 2 3 12 13 14 15 8 9 10 11]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev64 v0.4s, v0.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev64 v0.4s, v0.4s
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/bitops-float.clif b/cranelift/filetests/filetests/isa/riscv64/bitops-float.clif
new file mode 100644
index 000000000..ffd36afd0
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/bitops-float.clif
@@ -0,0 +1,79 @@
+test compile precise-output
+set unwind_info=false
+target riscv64 has_zbb
+
+;; This is a regression test for a bug in the RISC-V backend where
+;; When enabling `Zbb` the backend would try to use one of the
+;; integer instructions (`orn`) to implement a float operation
+;; causing a regalloc panic.
+
+function %or_not_optimization_float() -> i32 system_v {
+block0:
+    v0 = iconst.i32 0
+    v1 = f32const 0.0
+    v2 = bnot v1 
+    v3 = bor v2, v2
+    br_table v0, block1(v3), [block1(v1)]
+
+block1(v4: f32):
+    return v0
+}
+
+; VCode:
+; block0:
+;   li a1,0
+;   fmv.w.x ft9,a1
+;   li t1,0
+;   fmv.w.x fa6,t1
+;   fmv.x.w a1,fa6
+;   not a3,a1
+;   fmv.w.x ft1,a3
+;   fmv.x.w t1,ft1
+;   fmv.x.w a0,ft1
+;   or a2,t1,a0
+;   fmv.w.x fa2,a2
+;   li t2,0
+;   br_table t2,[MachLabel(1),MachLabel(2)]##tmp1=a1,tmp2=a2
+; block1:
+;   j label3
+; block2:
+;   fmv.d fa2,ft9
+;   j label3
+; block3:
+;   li a0,0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   mv a1, zero
+;   fmv.w.x ft9, a1
+;   mv t1, zero
+;   fmv.w.x fa6, t1
+;   fmv.x.w a1, fa6
+;   not a3, a1
+;   fmv.w.x ft1, a3
+;   fmv.x.w t1, ft1
+;   fmv.x.w a0, ft1
+;   or a2, t1, a0
+;   fmv.w.x fa2, a2
+;   mv t2, zero
+;   slli t6, t2, 0x20
+;   srli t6, t6, 0x20
+;   addi a2, zero, 1
+;   bltu t6, a2, 0xc
+;   auipc a2, 0
+;   jalr zero, a2, 0x28
+;   auipc a1, 0
+;   slli a2, t6, 3
+;   add a1, a1, a2
+;   jalr zero, a1, 0x10
+;   auipc a2, 0
+;   jalr zero, a2, 0xc
+; block1: ; offset 0x60
+;   j 8
+; block2: ; offset 0x64
+;   fmv.d fa2, ft9
+; block3: ; offset 0x68
+;   mv a0, zero
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/s390x/vec-permute-le-lane.clif b/cranelift/filetests/filetests/isa/s390x/vec-permute-le-lane.clif
index 968886d5a..f657ef47e 100644
--- a/cranelift/filetests/filetests/isa/s390x/vec-permute-le-lane.clif
+++ b/cranelift/filetests/filetests/isa/s390x/vec-permute-le-lane.clif
@@ -70,37 +70,6 @@ block0(v0: i8x16, v1: i8x16):
 ;   vperm %v24, %v24, %v25, %v3
 ;   br %r14
 
-function %shuffle_2(i8x16, i8x16) -> i8x16 wasmtime_system_v {
-block0(v0: i8x16, v1: i8x16):
-    v2 = shuffle v0, v1, [0 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47]
-    return v2
-}
-
-; VCode:
-; block0:
-;   vgbm %v3, 1
-;   bras %r1, 20 ; data.u128 0x8080808080808080808080808080800f ; vl %v5, 0(%r1)
-;   vperm %v7, %v24, %v25, %v5
-;   vn %v24, %v3, %v7
-;   br %r14
-; 
-; Disassembled:
-; block0: ; offset 0x0
-;   vgbm %v3, 1
-;   bras %r1, 0x1a
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x0f
-;   vl %v5, 0(%r1)
-;   vperm %v7, %v24, %v25, %v5
-;   vn %v24, %v3, %v7
-;   br %r14
-
 function %shuffle_vmrhg_xy(i8x16, i8x16) -> i8x16 wasmtime_system_v {
 block0(v0: i8x16, v1: i8x16):
     v2 = shuffle v0, v1, [24 25 26 27 28 29 30 31 8 9 10 11 12 13 14 15]
diff --git a/cranelift/filetests/filetests/isa/s390x/vec-permute.clif b/cranelift/filetests/filetests/isa/s390x/vec-permute.clif
index 3e8295884..e8ac72764 100644
--- a/cranelift/filetests/filetests/isa/s390x/vec-permute.clif
+++ b/cranelift/filetests/filetests/isa/s390x/vec-permute.clif
@@ -68,37 +68,6 @@ block0(v0: i8x16, v1: i8x16):
 ;   vperm %v24, %v24, %v25, %v3
 ;   br %r14
 
-function %shuffle_2(i8x16, i8x16) -> i8x16 {
-block0(v0: i8x16, v1: i8x16):
-    v2 = shuffle v0, v1, [0 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47]
-    return v2
-}
-
-; VCode:
-; block0:
-;   vgbm %v3, 32768
-;   bras %r1, 20 ; data.u128 0x00808080808080808080808080808080 ; vl %v5, 0(%r1)
-;   vperm %v7, %v24, %v25, %v5
-;   vn %v24, %v3, %v7
-;   br %r14
-; 
-; Disassembled:
-; block0: ; offset 0x0
-;   vgbm %v3, 0x8000
-;   bras %r1, 0x1a
-;   .byte 0x00, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   .byte 0x80, 0x80
-;   vl %v5, 0(%r1)
-;   vperm %v7, %v24, %v25, %v5
-;   vn %v24, %v3, %v7
-;   br %r14
-
 function %shuffle_vmrhg_xy(i8x16, i8x16) -> i8x16 {
 block0(v0: i8x16, v1: i8x16):
     v2 = shuffle v0, v1, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
diff --git a/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif b/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif
index 6f6301049..f0b4eaff2 100644
--- a/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif
+++ b/cranelift/filetests/filetests/isa/x64/shuffle-avx512.clif
@@ -42,55 +42,6 @@ block0(v0: i8x16, v1: i8x16):
 ;   addb %al, (%rax)
 ;   addb %al, (%rax)
 
-function %shuffle_out_of_bounds(i8x16, i8x16) -> i8x16 {
-block0(v0: i8x16, v1: i8x16):
-    ;; pick zero for the first lane, the rest use first lane of v0
-    ;; This should introduce two constants, one for the permutation and one to
-    ;; mask the non-zero values for lanes 1-15
-    v2 = shuffle v0, v1, 0x80000000000000000000000000000000
-    return v2
-}
-
-; VCode:
-;   pushq   %rbp
-;   movq    %rsp, %rbp
-; block0:
-;   movdqa  %xmm0, %xmm6
-;   movdqu  const(0), %xmm0
-;   movdqa  %xmm6, %xmm7
-;   vpermi2b %xmm1, %xmm7, %xmm0, %xmm0
-;   andps   %xmm0, const(1), %xmm0
-;   movq    %rbp, %rsp
-;   popq    %rbp
-;   ret
-; 
-; Disassembled:
-; block0: ; offset 0x0
-;   pushq %rbp
-;   movq %rsp, %rbp
-; block1: ; offset 0x4
-;   movdqa %xmm0, %xmm6
-;   movdqu 0x20(%rip), %xmm0
-;   movdqa %xmm6, %xmm7
-;   vpermi2b %xmm1, %xmm7, %xmm0
-;   andps 0x1f(%rip), %xmm0
-;   movq %rbp, %rsp
-;   popq %rbp
-;   retq
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, (%rax)
-;   addb %al, -1(%rax)
-
 function %f3(i8x16, i8x16) -> i8x16 {
 block0(v0: i8x16, v1: i8x16):
     v2 = shuffle v0, v1, [3 0 31 26 4 6 12 11 23 13 24 4 2 15 17 5]
diff --git a/cranelift/filetests/filetests/runtests/simd-shuffle.clif b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
index 60b515628..f236a0205 100644
--- a/cranelift/filetests/filetests/runtests/simd-shuffle.clif
+++ b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
@@ -14,13 +14,6 @@ block0(v0: i8x16, v1: i8x16):
 }
 ; run: %shuffle_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [4 1 32 27 5 7 13 12 24 14 25 5 3 16 18 6]
 
-function %shuffle_zeros(i8x16, i8x16) -> i8x16 {
-block0(v0: i8x16, v1: i8x16):
-    v2 = shuffle v0, v1, [3 0 32 255 4 6 12 11 23 13 24 4 2 97 17 5]
-    return v2
-}
-; run: %shuffle_zeros([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [4 1 0 0 5 7 13 12 24 14 25 5 3 0 18 6]
-
 function %shuffle1(i8x16) -> i8x16 {
 block0(v0: i8x16):
     v1 = shuffle v0, v0, [8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
@@ -258,3 +251,295 @@ block0(v0: i8x16, v1: i8x16):
     return v2
 }
 ; run: %shuffle_all_zeros([5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1], [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]) == [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5]
+
+function %aarch64_uzp1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30]
+    return v2
+}
+; run: %aarch64_uzp1_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31]
+
+function %aarch64_uzp2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31]
+    return v2
+}
+; run: %aarch64_uzp2_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32]
+
+function %aarch64_uzp1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 4 5 8 9 12 13 16 17 20 21 24 25 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 3 5 7 9 11 13 15]
+
+function %aarch64_uzp2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 6 7 10 11 14 15 18 19 22 23 26 27 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_uzp2_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [2 4 6 8 10 12 14 16]
+
+function %aarch64_uzp1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i32x4([1 2 3 4], [5 6 7 8]) == [1 3 5 7]
+
+function %aarch64_uzp2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 12 13 14 15 20 21 22 23 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_uzp2_i32x4([1 2 3 4], [5 6 7 8]) == [2 4 6 8]
+
+function %aarch64_uzp1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i64x2([1 2], [3 4]) == [1 3]
+
+function %aarch64_uzp2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i64x2([1 2], [3 4]) == [2 4]
+
+function %aarch64_trn1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 16 2 18 4 20 6 22 8 24 10 26 12 28 14 30]
+    return v2
+}
+; run: %aarch64_trn1_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [1 17 3 19 5 21 7 23 9 25 11 27 13 29 15 31]
+
+function %aarch64_trn2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 17 3 19 5 21 7 23 9 25 11 27 13 29 15 31]
+    return v2
+}
+; run: %aarch64_trn2_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [2 18 4 20 6 22 8 24 10 26 12 28 14 30 16 32]
+
+function %aarch64_trn1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 4 5 20 21 8 9 24 25 12 13 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_trn1_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 9 3 11 5 13 7 15]
+
+function %aarch64_trn2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 18 19 6 7 22 23 10 11 26 27 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_trn2_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [2 10 4 12 6 14 8 16]
+
+function %aarch64_trn1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 8 9 10 11 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_trn1_i32x4([1 2 3 4], [5 6 7 8]) == [1 5 3 7]
+
+function %aarch64_trn2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 20 21 22 23 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_trn2_i32x4([1 2 3 4], [5 6 7 8]) == [2 6 4 8]
+
+function %aarch64_trn1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_trn1_i64x2([1 2], [3 4]) == [1 3]
+
+function %aarch64_trn2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_trn2_i64x2([1 2], [3 4]) == [2 4]
+
+function %aarch64_ext_0(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
+    return v2
+}
+; run: %aarch64_ext_0([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
+
+function %aarch64_ext_1(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
+    return v2
+}
+; run: %aarch64_ext_1([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17]
+
+function %aarch64_ext_5(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
+    return v2
+}
+; run: %aarch64_ext_5([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21]
+
+function %aarch64_ext_11(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]
+    return v2
+}
+; run: %aarch64_ext_11([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27]
+
+function %aarch64_ext_16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31]
+    return v2
+}
+; run: %aarch64_ext_16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]
+
+function %aarch64_dup_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5]
+    return v2
+}
+; run: %aarch64_dup_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6]
+
+function %aarch64_dup_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 12 13 12 13 12 13 12 13 12 13 12 13 12 13]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_dup_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [7 7 7 7 7 7 7 7]
+
+function %aarch64_dup_i32x4_1(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 8 9 10 11 8 9 10 11 8 9 10 11]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_dup_i32x4_1([1 2 3 4], [5 6 7 8]) == [3 3 3 3]
+
+function %aarch64_dup_i32x4_2(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 16 17 18 19 16 17 18 19 16 17 18 19]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_dup_i32x4_2([1 2 3 4], [5 6 7 8]) == [5 5 5 5]
+
+function %aarch64_dup_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_dup_i64x2([1 2], [5 6]) == [1 1]
+
+function %aarch64_rev16(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [1 0 3 2 5 4 7 6 9 8 11 10 13 12 15 14]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_rev16([0x1 0x2 0x3 0x4 0x100 0x200 0x300 0x400], [0 0 0 0 0 0 0 0]) == [0x100 0x200 0x300 0x400 0x1 0x2 0x3 0x4]
+
+function %aarch64_rev32_bytes(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_rev32_bytes([1 2 3 4], [0 0 0 0]) == [0x1000000 0x2000000 0x3000000 0x4000000]
+; run: %aarch64_rev32_bytes([0xff000000 0x00ff0000 0x0000ff00 0x000000ff], [0 0 0 0]) == [0xff 0xff00 0xff0000 0xff000000]
+
+function %aarch64_rev32_words(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 0 1 6 7 4 5 10 11 8 9 14 15 12 13]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_rev32_words([1 2 3 4], [0 0 0 0]) == [0x10000 0x20000 0x30000 0x40000]
+; run: %aarch64_rev32_words([0xff000000 0x00ff0000 0x0000ff00 0x000000ff], [0 0 0 0]) == [0xff00 0xff 0xff000000 0xff0000]
+
+function %aarch64_rev64_bytes(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_rev64_bytes([0x0102030405060708 0x0807060504030201], [0 0]) == [0x0807060504030201 0x0102030405060708]
+
+function %aarch64_rev64_words(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_rev64_words([0x0102030405060708 0x0807060504030201], [0 0]) == [0x0708050603040102 0x0201040306050807]
+
+function %aarch64_rev64_doublewords(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 0 1 2 3 12 13 14 15 8 9 10 11]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_rev64_words([0x0102030405060708 0x0807060504030201], [0 0]) == [0x0506070801020304 0x0403020108070605]
diff --git a/cranelift/filetests/filetests/verifier/type_check.clif b/cranelift/filetests/filetests/verifier/type_check.clif
index 0c6710b61..ea5ed07ed 100644
--- a/cranelift/filetests/filetests/verifier/type_check.clif
+++ b/cranelift/filetests/filetests/verifier/type_check.clif
@@ -123,3 +123,9 @@ block0:
     v1 = ireduce.i64 v0 ; error: input i32 must be larger than output i64
     return
 }
+
+function %bad_shuffle(i8x16, i8x16) {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 32] ; error: shuffle immediate index 32 is larger than the maximum 31
+    return
+}
diff --git a/cranelift/filetests/src/concurrent.rs b/cranelift/filetests/src/concurrent.rs
index a3d7543f8..c85f50fa5 100644
--- a/cranelift/filetests/src/concurrent.rs
+++ b/cranelift/filetests/src/concurrent.rs
@@ -82,14 +82,16 @@ impl ConcurrentRunner {
 
     /// Join all the worker threads.
     /// Transfer pass timings from the worker threads to the current thread.
-    pub fn join(&mut self) {
+    pub fn join(&mut self) -> timing::PassTimes {
         assert!(self.request_tx.is_none(), "must shutdown before join");
+        let mut pass_times = timing::PassTimes::default();
         for h in self.handles.drain(..) {
             match h.join() {
-                Ok(t) => timing::add_to_current(&t),
+                Ok(t) => pass_times.add(&t),
                 Err(e) => println!("worker panicked: {:?}", e),
             }
         }
+        pass_times
     }
 
     /// Add a new job to the queues.
diff --git a/cranelift/filetests/src/lib.rs b/cranelift/filetests/src/lib.rs
index 7fb7d44b3..c82e45280 100644
--- a/cranelift/filetests/src/lib.rs
+++ b/cranelift/filetests/src/lib.rs
@@ -24,10 +24,8 @@
 
 pub use crate::function_runner::TestFileCompiler;
 use crate::runner::TestRunner;
-use cranelift_codegen::timing;
 use cranelift_reader::TestCommand;
 use std::path::Path;
-use std::time;
 
 mod concurrent;
 pub mod function_runner;
@@ -63,7 +61,7 @@ mod test_wasm;
 /// Directories are scanned recursively for test cases ending in `.clif`. These test cases are
 /// executed on background threads.
 ///
-pub fn run(verbose: bool, report_times: bool, files: &[String]) -> anyhow::Result<time::Duration> {
+pub fn run(verbose: bool, report_times: bool, files: &[String]) -> anyhow::Result<()> {
     let mut runner = TestRunner::new(verbose, report_times);
 
     for path in files.iter().map(Path::new) {
@@ -89,8 +87,8 @@ pub fn run_passes(
     passes: &[String],
     target: &str,
     file: &str,
-) -> anyhow::Result<time::Duration> {
-    let mut runner = TestRunner::new(verbose, /* report_times */ false);
+) -> anyhow::Result<()> {
+    let mut runner = TestRunner::new(verbose, report_times);
 
     let path = Path::new(file);
     if path == Path::new("-") || path.is_file() {
@@ -99,11 +97,8 @@ pub fn run_passes(
         runner.push_dir(path);
     }
 
-    let result = runner.run_passes(passes, target);
-    if report_times {
-        println!("{}", timing::take_current());
-    }
-    result
+    runner.start_threads();
+    runner.run_passes(passes, target)
 }
 
 /// Create a new subcommand trait object to match `parsed.command`.
diff --git a/cranelift/filetests/src/runner.rs b/cranelift/filetests/src/runner.rs
index 5b86f020e..a1517b4dc 100644
--- a/cranelift/filetests/src/runner.rs
+++ b/cranelift/filetests/src/runner.rs
@@ -5,7 +5,6 @@
 
 use crate::concurrent::{ConcurrentRunner, Reply};
 use crate::runone;
-use cranelift_codegen::timing;
 use std::error::Error;
 use std::ffi::OsStr;
 use std::fmt::{self, Display};
@@ -298,9 +297,9 @@ impl TestRunner {
                     None => break,
                 }
             }
-            conc.join();
+            let pass_times = conc.join();
             if self.report_times {
-                println!("{}", timing::take_current());
+                println!("{}", pass_times);
             }
         }
     }
@@ -355,8 +354,7 @@ impl TestRunner {
     }
 
     /// Scan pushed directories for tests and run them.
-    pub fn run(&mut self) -> anyhow::Result<time::Duration> {
-        let started = time::Instant::now();
+    pub fn run(&mut self) -> anyhow::Result<()> {
         self.scan_dirs(IsPass::NotPass);
         self.schedule_jobs();
         self.report_slow_tests();
@@ -364,26 +362,22 @@ impl TestRunner {
 
         println!("{} tests", self.tests.len());
         match self.errors {
-            0 => Ok(started.elapsed()),
+            0 => Ok(()),
             1 => anyhow::bail!("1 failure"),
             n => anyhow::bail!("{} failures", n),
         }
     }
 
     /// Scan pushed directories for tests and run specified passes from commandline on them.
-    pub fn run_passes(
-        &mut self,
-        passes: &[String],
-        target: &str,
-    ) -> anyhow::Result<time::Duration> {
-        let started = time::Instant::now();
+    pub fn run_passes(&mut self, passes: &[String], target: &str) -> anyhow::Result<()> {
         self.scan_dirs(IsPass::Pass);
         self.schedule_pass_job(passes, target);
         self.report_slow_tests();
+        self.drain_threads();
 
         println!("{} tests", self.tests.len());
         match self.errors {
-            0 => Ok(started.elapsed()),
+            0 => Ok(()),
             1 => anyhow::bail!("1 failure"),
             n => anyhow::bail!("{} failures", n),
         }
diff --git a/tests/all/wast.rs b/tests/all/wast.rs
index 5da3e4cbd..260e3bc00 100644
--- a/tests/all/wast.rs
+++ b/tests/all/wast.rs
@@ -85,6 +85,8 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
             cfg.static_memory_maximum_size(0);
         }
         cfg.dynamic_memory_reserved_for_growth(0);
+        cfg.static_memory_guard_size(0);
+        cfg.dynamic_memory_guard_size(0);
     }
 
     let _pooling_lock = if pooling {
@@ -101,7 +103,7 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
         // fails to grow, the values here will need to be adjusted.
         let mut pool = PoolingAllocationConfig::default();
         pool.instance_count(450)
-            .instance_memories(2)
+            .instance_memories(if multi_memory { 9 } else { 1 })
             .instance_tables(4)
             .instance_memory_pages(805);
         cfg.allocation_strategy(InstanceAllocationStrategy::Pooling(pool));
diff --git a/tests/misc_testsuite/relaxed-simd/i16x8_relaxed_q15mulr_s.wast b/tests/misc_testsuite/relaxed-simd/i16x8_relaxed_q15mulr_s.wast
deleted file mode 100644
index 265d99160..000000000
--- a/tests/misc_testsuite/relaxed-simd/i16x8_relaxed_q15mulr_s.wast
+++ /dev/null
@@ -1,26 +0,0 @@
-;; Tests for i16x8.relaxed_q15mulr_s.
-(module
-    (func (export "i16x8.relaxed_q15mulr_s") (param v128 v128) (result v128) (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1)))
-
-    (func (export "i16x8.relaxed_q15mulr_s_cmp") (param v128 v128) (result v128)
-          (i16x8.eq
-            (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1))
-            (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1))))
-)
-
-;; INT16_MIN = -32768
-(assert_return (invoke "i16x8.relaxed_q15mulr_s"
-                       (v128.const i16x8 -32768 -32767 32767 0 0 0 0 0)
-                       (v128.const i16x8 -32768 -32768 32767 0 0 0 0 0))
-               ;; overflows, return either INT16_MIN or INT16_MAX
-               (either (v128.const i16x8 -32768 32767 32766 0 0 0 0 0)
-                       (v128.const i16x8 32767 32767 32766 0 0 0 0 0)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "i16x8.relaxed_q15mulr_s_cmp"
-                       (v128.const i16x8 -32768 -32767 32767 0 0 0 0 0)
-                       (v128.const i16x8 -32768 -32768 32767 0 0 0 0 0))
-               ;; overflows, return either INT16_MIN or INT16_MAX
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
diff --git a/tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast b/tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast
deleted file mode 100644
index 889542c6a..000000000
--- a/tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast
+++ /dev/null
@@ -1,123 +0,0 @@
-;; Tests for i32x4.relaxed_trunc_f32x4_s, i32x4.relaxed_trunc_f32x4_u, i32x4.relaxed_trunc_f64x2_s_zero, and i32x4.relaxed_trunc_f64x2_u_zero.
-
-(module
-    (func (export "i32x4.relaxed_trunc_f32x4_s") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_s (local.get 0)))
-    (func (export "i32x4.relaxed_trunc_f32x4_u") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_u (local.get 0)))
-    (func (export "i32x4.relaxed_trunc_f64x2_s_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0)))
-    (func (export "i32x4.relaxed_trunc_f64x2_u_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0)))
-
-    (func (export "i32x4.relaxed_trunc_f32x4_s_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f32x4_s (local.get 0))
-            (i32x4.relaxed_trunc_f32x4_s (local.get 0))))
-    (func (export "i32x4.relaxed_trunc_f32x4_u_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f32x4_u (local.get 0))
-            (i32x4.relaxed_trunc_f32x4_u (local.get 0))))
-    (func (export "i32x4.relaxed_trunc_f64x2_s_zero_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))
-            (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))))
-    (func (export "i32x4.relaxed_trunc_f64x2_u_zero_cmp") (param v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))
-            (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))))
-)
-
-;; Test some edge cases around min/max to ensure that the instruction either
-;; saturates correctly or returns INT_MIN.
-;;
-;; Note, though, that INT_MAX itself is not tested. The value for INT_MAX is
-;; 2147483647 but that is not representable in a `f32` since it requires 31 bits
-;; when a f32 has only 24 bits available. This means that the closest integers
-;; to INT_MAX which can be represented are 2147483520 and 2147483648, meaning
-;; that the INT_MAX test case cannot be tested.
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
-                       ;;                INT32_MIN     <INT32_MIN        >INT32_MAX
-                       (v128.const f32x4 -2147483648.0 -2147483904.0 2.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (either (v128.const i32x4 -2147483648 -2147483648 2 2147483647)
-                       (v128.const i32x4 -2147483648 -2147483648 2 -2147483648)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or INT32_MIN
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
-                       ;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
-                       (v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (either (v128.const i32x4 0 0 4294967040 0xffffffff)
-                       (v128.const i32x4 0 0xffffffff 4294967040 0xffffffff)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or UINT32_MAX
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
-                       (v128.const f64x2 -2147483904.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (either (v128.const i32x4 -2147483648 2147483647 0 0)
-                       (v128.const i32x4 -2147483648 -2147483648 0 0)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
-                       (v128.const f64x2 nan -nan))
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0x80000000 0x80000000 0 0)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
-                       (v128.const f64x2 -1.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (either (v128.const i32x4 0 0xffffffff 0 0)
-                       (v128.const i32x4 0xffffffff 0xffffffff 0 0)))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
-                       (v128.const f64x2 nan -nan))
-               (either (v128.const i32x4 0 0 0 0)
-                       (v128.const i32x4 0 0 0xffffffff 0xffffffff)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
-                       ;; INT32_MIN <INT32_MIN INT32_MAX >INT32_MAX
-                       (v128.const f32x4 -2147483648.0 -2147483904.0 2147483647.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or INT32_MIN
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
-                       ;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
-                       (v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
-                       (v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
-               ;; nans -> 0 or UINT32_MAX
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
-                       (v128.const f64x2 -2147483904.0 2147483904.0))
-               ;; out of range -> saturate or INT32_MIN
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
-                       (v128.const f64x2 nan -nan))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
-                       (v128.const f64x2 -1.0 4294967296.0))
-               ;; out of range -> saturate or UINT32_MAX
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
-                       (v128.const f64x2 nan -nan))
-               (v128.const i32x4 -1 -1 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast b/tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast
deleted file mode 100644
index 1b20668d3..000000000
--- a/tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast
+++ /dev/null
@@ -1,44 +0,0 @@
-;; Tests for relaxed i8x16 swizzle.
-
-(module
-    (func (export "i8x16.relaxed_swizzle") (param v128 v128) (result v128) (i8x16.relaxed_swizzle (local.get 0) (local.get 1)))
-
-    (func (export "i8x16.relaxed_swizzle_cmp") (param v128 v128) (result v128)
-          (i8x16.eq
-            (i8x16.relaxed_swizzle (local.get 0) (local.get 1))
-            (i8x16.relaxed_swizzle (local.get 0) (local.get 1))))
-)
-
-(assert_return (invoke "i8x16.relaxed_swizzle"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))
-               (either (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
-
-;; out of range, returns 0 or modulo 15 if < 128
-(assert_return (invoke "i8x16.relaxed_swizzle"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31))
-               (either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
-
-;; out of range, returns 0 if >= 128
-(assert_return (invoke "i8x16.relaxed_swizzle"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255))
-               (either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-;; out of range, returns 0 or modulo 15 if < 128
-(assert_return (invoke "i8x16.relaxed_swizzle_cmp"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31))
-               (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
-
-;; out of range, returns 0 if >= 128
-(assert_return (invoke "i8x16.relaxed_swizzle_cmp"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255))
-               (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast b/tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast
deleted file mode 100644
index 41dee0afc..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast
+++ /dev/null
@@ -1,106 +0,0 @@
-;; Tests for relaxed dot products.
-
-(module
-    (func (export "i16x8.relaxed_dot_i8x16_i7x16_s") (param v128 v128) (result v128) (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1)))
-    (func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s") (param v128 v128 v128) (result v128) (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2)))
-
-    (func (export "i16x8.relaxed_dot_i8x16_i7x16_s_cmp") (param v128 v128) (result v128)
-          (i16x8.eq
-            (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1))
-            (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1))))
-    (func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp") (param v128 v128 v128) (result v128)
-          (i16x8.eq
-            (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2))
-            (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2))))
-)
-
-;; Simple values to ensure things are functional.
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))
-               (v128.const i16x8 1 13 41 85 145 221 313 421))
-
-;; Test max and min i8 values;
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
-                       (v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i16x8 -32512 32258 0 0 0 0 0 0))
-
-;; signed * unsigned   : -128 *  129 * 2 = -33,024 saturated to -32,768
-;; signed * signed     : -128 * -127 * 2 =  32,512
-;; unsigned * unsigned :  128 *  129 * 2 =  33,024
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
-                       (v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
-               (either
-                 (v128.const i16x8 -32768 0 0 0 0 0 0 0)
-                 (v128.const i16x8  32512 0 0 0 0 0 0 0)
-                 (v128.const i16x8  33024 0 0 0 0 0 0 0)))
-
-;; Simple values to ensure things are functional.
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i32x4 0 1 2 3))
-               ;; intermediate result is [14, 126, 366, 734]
-               (v128.const i32x4 14 127 368 737))
-
-;; Test max and min i8 values;
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
-                       (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               ;; intermediate result is [-65024, 64516, 0, 0]
-               (v128.const i32x4 -65023 64518 3 4))
-
-;; signed * unsigned   : -128 *  129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI
-;; signed * unsigned with intermediate saturation :
-;;   (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW)
-;;   -32768 + -32768 = -65536 (+ 1)
-;; signed * signed     : -128 * -127 * 4 =  65,024 (+ 1)
-;; unsigned * unsigned :  128 *  129 * 2 =  66,048 (+ 1)
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
-                       (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               (either
-                 (v128.const i32x4 -66047 2 3 4)
-                 (v128.const i32x4 -65535 2 3 4)
-                 (v128.const i32x4  65025 2 3 4)
-                 (v128.const i32x4  66049 2 3 4)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-;; Test max and min i8 values;
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp"
-                       (v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
-;; Test max and min i8 values;
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp"
-                       (v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               ;; intermediate result is [-65024, 64516, 0, 0]
-               (v128.const i32x4 -1 -1 -1 -1))
-
-;; signed * unsigned   : -128 *  129 * 2 = -33,024 saturated to -32,768
-;; signed * signed     : -128 * -127 * 2 =  32,512
-;; unsigned * unsigned :  128 *  129 * 2 =  33,024
-(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp"
-                       (v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
-;; signed * unsigned   : -128 *  129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI
-;; signed * unsigned with intermediate saturation :
-;;   (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW)
-;;   -32768 + -32768 = -65536 (+ 1)
-;; signed * signed     : -128 * -127 * 4 =  65,024 (+ 1)
-;; unsigned * unsigned :  128 *  129 * 2 =  66,048 (+ 1)
-(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp"
-                       (v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0)
-                       (v128.const i32x4 1 2 3 4))
-               (v128.const i32x4 -1 -1 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast b/tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast
deleted file mode 100644
index 4ea6eb818..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast
+++ /dev/null
@@ -1,92 +0,0 @@
-;; Tests for i8x16.relaxed_laneselect, i16x8.relaxed_laneselect, i32x4.relaxed_laneselect, and i64x2.relaxed_laneselect.
-
-(module
-    (func (export "i8x16.relaxed_laneselect") (param v128 v128 v128) (result v128) (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "i16x8.relaxed_laneselect") (param v128 v128 v128) (result v128) (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "i32x4.relaxed_laneselect") (param v128 v128 v128) (result v128) (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "i64x2.relaxed_laneselect") (param v128 v128 v128) (result v128) (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
-
-    (func (export "i8x16.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i8x16.eq
-            (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "i16x8.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i16x8.eq
-            (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "i32x4.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i32x4.eq
-            (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "i64x2.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
-          (i64x2.eq
-            (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
-            (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
-)
-
-(assert_return (invoke "i8x16.relaxed_laneselect"
-                       (v128.const i8x16 0    1  0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16   17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31)
-                       (v128.const i8x16 0xff 0  0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0))
-               (either (v128.const i8x16 0    17 0x14 0x32 20 21 22 23 24 25 26 27 28 29 30 31)
-                       (v128.const i8x16 0    17 0x12 0x34 20 21 22 23 24 25 26 27 28 29 30 31)))
-
-(assert_return (invoke "i16x8.relaxed_laneselect"
-                       (v128.const i16x8 0      1 0x1234 0x1234 4 5 6 7)
-                       (v128.const i16x8 8      9 0x5678 0x5678 12 13 14 15)
-                       (v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0))
-               (either (v128.const i16x8 0      9 0x1278 0x5634 12 13 14 15)
-                       (v128.const i16x8 0      9 0x1234 0x5678 12 13 14 15)))
-
-(assert_return (invoke "i32x4.relaxed_laneselect"
-                       (v128.const i32x4 0          1 0x12341234 0x12341234)
-                       (v128.const i32x4 4          5 0x56785678 0x56785678)
-                       (v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff))
-               (either (v128.const i32x4 0          5 0x12345678 0x56781234)
-                       (v128.const i32x4 0          5 0x12341234 0x56785678)))
-
-(assert_return (invoke "i64x2.relaxed_laneselect"
-                       (v128.const i64x2 0                  1)
-                       (v128.const i64x2 2                  3)
-                       (v128.const i64x2 0xffffffffffffffff 0))
-               (either (v128.const i64x2 0                  3)
-                       (v128.const i64x2 0                  3)))
-
-(assert_return (invoke "i64x2.relaxed_laneselect"
-                       (v128.const i64x2 0x1234123412341234 0x1234123412341234)
-                       (v128.const i64x2 0x5678567856785678 0x5678567856785678)
-                       (v128.const i64x2 0xffffffff00000000 0x00000000ffffffff))
-               (either (v128.const i64x2 0x1234123456785678 0x5678567812341234)
-                       (v128.const i64x2 0x1234123412341234 0x5678567856785678)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "i8x16.relaxed_laneselect_cmp"
-                       (v128.const i8x16 0    1  0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15)
-                       (v128.const i8x16 16   17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31)
-                       (v128.const i8x16 0xff 0  0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0))
-               (v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
-
-(assert_return (invoke "i16x8.relaxed_laneselect_cmp"
-                       (v128.const i16x8 0      1 0x1234 0x1234 4 5 6 7)
-                       (v128.const i16x8 8      9 0x5678 0x5678 12 13 14 15)
-                       (v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0))
-               (v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
-
-(assert_return (invoke "i32x4.relaxed_laneselect_cmp"
-                       (v128.const i32x4 0          1 0x12341234 0x12341234)
-                       (v128.const i32x4 4          5 0x56785678 0x56785678)
-                       (v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "i64x2.relaxed_laneselect_cmp"
-                       (v128.const i64x2 0                  1)
-                       (v128.const i64x2 2                  3)
-                       (v128.const i64x2 0xffffffffffffffff 0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "i64x2.relaxed_laneselect_cmp"
-                       (v128.const i64x2 0x1234123412341234 0x1234123412341234)
-                       (v128.const i64x2 0x5678567856785678 0x5678567856785678)
-                       (v128.const i64x2 0xffffffff00000000 0x00000000ffffffff))
-               (v128.const i64x2 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast b/tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast
deleted file mode 100644
index 0e0e0c2bf..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast
+++ /dev/null
@@ -1,190 +0,0 @@
-;; Tests for f32x4.relaxed_madd, f32x4.relaxed_nmadd, f64x2.relaxed_madd, and f64x2.relaxed_nmadd.
-
-(module
-    (func (export "f32x4.relaxed_madd") (param v128 v128 v128) (result v128) (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "f32x4.relaxed_nmadd") (param v128 v128 v128) (result v128) (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "f64x2.relaxed_nmadd") (param v128 v128 v128) (result v128) (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))
-    (func (export "f64x2.relaxed_madd") (param v128 v128 v128) (result v128) (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))
-
-    (func (export "f32x4.relaxed_madd_cmp") (param v128 v128 v128) (result v128)
-          (f32x4.eq
-            (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2))
-            (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "f32x4.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128)
-          (f32x4.eq
-            (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))
-            (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "f64x2.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128)
-          (f64x2.eq
-            (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))
-            (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))))
-    (func (export "f64x2.relaxed_madd_cmp") (param v128 v128 v128) (result v128)
-          (f64x2.eq
-            (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2))
-            (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2))))
-)
-
-
-;; FLT_MAX == 0x1.fffffep+127
-;; FLT_MAX * 2 - FLT_MAX ==
-;;   FLT_MAX (if fma)
-;;   0       (if no fma)
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f32x4.relaxed_madd"
-                       (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 )
-                       (v128.const f32x4 2.0 2.0 2.0 2.0)
-                       (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127))
-               (either (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)
-                       (v128.const f32x4 inf inf inf inf)))
-
-;; Special values for float:
-;; x            = 0x1.000004p+0 (1 + 2^-22)
-;; y            = 0x1.0002p+0   (1 + 2^-15)
-;; z            = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0)
-;;              = -0x1.000204p+0
-;; x.y          = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = (0x1p-37) 2^-37
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f32x4.relaxed_madd"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
-                       (v128.const f32x4 0 0 0 0)))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd"
-                       (v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
-                       (v128.const f32x4 0 0 0 0)))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
-                       (v128.const f32x4 0 0 0 0)))
-
-;; DBL_MAX = 0x1.fffffffffffffp+1023
-;; DLB_MAX * 2 - DLB_MAX ==
-;;   DLB_MAX (if fma)
-;;   0       (if no fma)
-;; form https://www.vinc17.net/software/fma-tests.c
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f64x2.relaxed_madd"
-                       (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
-                       (v128.const f64x2 2.0 2.0)
-                       (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023))
-               (either (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
-                       (v128.const f64x2 inf inf)))
-
-;; Special values for double:
-;; x            = 0x1.00000004p+0 (1 + 2^-30)
-;; y            = 0x1.000002p+0   (1 + 2^-23)
-;; z            = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0)
-;;              = -0x1.00000204p+0
-;; x.y          = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = 0x1p-53
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f64x2.relaxed_madd"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (either (v128.const f64x2 0x1p-53 0x1p-53)
-                       (v128.const f64x2 0 0)))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd"
-                       (v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (either (v128.const f64x2 0x1p-53 0x1p-53)
-                       (v128.const f64x2 0 0)))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (either (v128.const f64x2 0x1p-53 0x1p-53)
-                       (v128.const f64x2 0 0)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-;; FLT_MAX == 0x1.fffffep+127
-;; FLT_MAX * 2 - FLT_MAX ==
-;;   FLT_MAX (if fma)
-;;   0       (if no fma)
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f32x4.relaxed_madd_cmp"
-                       (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 )
-                       (v128.const f32x4 2.0 2.0 2.0 2.0)
-                       (v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-;; Special values for float:
-;; x            = 0x1.000004p+0 (1 + 2^-22)
-;; y            = 0x1.0002p+0   (1 + 2^-15)
-;; z            = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0)
-;;              = -0x1.000204p+0
-;; x.y          = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = (0x1p-37) 2^-37
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f32x4.relaxed_madd_cmp"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (v128.const i32x4 -1 -1 -1 -1))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd_cmp"
-                       (v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0)
-                       (v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (v128.const i32x4 -1 -1 -1 -1))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f32x4.relaxed_nmadd_cmp"
-                       (v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
-                       (v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0)
-                       (v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-;; DBL_MAX = 0x1.fffffffffffffp+1023
-;; DLB_MAX * 2 - DLB_MAX ==
-;;   DLB_MAX (if fma)
-;;   0       (if no fma)
-;; form https://www.vinc17.net/software/fma-tests.c
-;; from https://www.vinc17.net/software/fma-tests.c
-(assert_return (invoke "f64x2.relaxed_madd_cmp"
-                       (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
-                       (v128.const f64x2 2.0 2.0)
-                       (v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023))
-               (v128.const i64x2 -1 -1))
-
-;; Special values for double:
-;; x            = 0x1.00000004p+0 (1 + 2^-30)
-;; y            = 0x1.000002p+0   (1 + 2^-23)
-;; z            = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0)
-;;              = -0x1.00000204p+0
-;; x.y          = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit)
-;; x.y+z        = 0 (2 roundings)
-;; fma(x, y, z) = 0x1p-53
-;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
-(assert_return (invoke "f64x2.relaxed_madd_cmp"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (v128.const i64x2 -1 -1))
-;; fnma tests with negated x, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd_cmp"
-                       (v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0)
-                       (v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (v128.const i64x2 -1 -1))
-;; fnma tests with negated y, same answers are expected.
-(assert_return (invoke "f64x2.relaxed_nmadd_cmp"
-                       (v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
-                       (v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0)
-                       (v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
-               (v128.const i64x2 -1 -1))
diff --git a/tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast b/tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast
deleted file mode 100644
index d8a04ba4d..000000000
--- a/tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast
+++ /dev/null
@@ -1,183 +0,0 @@
-;; Tests for f32x4.min, f32x4.max, f64x2.min, and f64x2.max.
-
-(module
-    (func (export "f32x4.relaxed_min") (param v128 v128) (result v128) (f32x4.relaxed_min (local.get 0) (local.get 1)))
-    (func (export "f32x4.relaxed_max") (param v128 v128) (result v128) (f32x4.relaxed_max (local.get 0) (local.get 1)))
-    (func (export "f64x2.relaxed_min") (param v128 v128) (result v128) (f64x2.relaxed_min (local.get 0) (local.get 1)))
-    (func (export "f64x2.relaxed_max") (param v128 v128) (result v128) (f64x2.relaxed_max (local.get 0) (local.get 1)))
-
-    (func (export "f32x4.relaxed_min_cmp") (param v128 v128) (result v128)
-          (i32x4.eq
-            (f32x4.relaxed_min (local.get 0) (local.get 1))
-            (f32x4.relaxed_min (local.get 0) (local.get 1))))
-    (func (export "f32x4.relaxed_max_cmp") (param v128 v128) (result v128)
-          (i32x4.eq
-            (f32x4.relaxed_max (local.get 0) (local.get 1))
-            (f32x4.relaxed_max (local.get 0) (local.get 1))))
-    (func (export "f64x2.relaxed_min_cmp") (param v128 v128) (result v128)
-          (i64x2.eq
-            (f64x2.relaxed_min (local.get 0) (local.get 1))
-            (f64x2.relaxed_min (local.get 0) (local.get 1))))
-    (func (export "f64x2.relaxed_max_cmp") (param v128 v128) (result v128)
-          (i64x2.eq
-            (f64x2.relaxed_max (local.get 0) (local.get 1))
-            (f64x2.relaxed_max (local.get 0) (local.get 1))))
-)
-
-(assert_return (invoke "f32x4.relaxed_min"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)
-                       (v128.const f32x4 nan:canonical nan:canonical 0 0)
-                       (v128.const f32x4 0 0 nan:canonical nan:canonical)
-                       (v128.const f32x4 0 0 0 0)))
-
-(assert_return (invoke "f32x4.relaxed_min"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (either (v128.const f32x4 -0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 -0.0 +0.0 -0.0)))
-
-(assert_return (invoke "f32x4.relaxed_max"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)
-                       (v128.const f32x4 nan:canonical nan:canonical 0 0)
-                       (v128.const f32x4 0 0 nan:canonical nan:canonical)
-                       (v128.const f32x4 0 0 0 0)))
-
-(assert_return (invoke "f32x4.relaxed_max"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (either (v128.const f32x4 +0.0 +0.0 +0.0 -0.0)
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 -0.0 +0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (either (v128.const f64x2 -0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0)
-                       (v128.const f64x2 -0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_min"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (either (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (either (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 nan:canonical nan:canonical)
-                       (v128.const f64x2 0 0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (either (v128.const f64x2 +0.0 +0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0)
-                       (v128.const f64x2 -0.0 -0.0)))
-
-(assert_return (invoke "f64x2.relaxed_max"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (either (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0)))
-
-;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
-
-(assert_return (invoke "f32x4.relaxed_min_cmp"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f32x4.relaxed_min_cmp"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f32x4.relaxed_max_cmp"
-                       (v128.const f32x4 -nan nan 0 0)
-                       (v128.const f32x4 0 0 -nan nan))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f32x4.relaxed_max_cmp"
-                       (v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
-                       (v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
-               (v128.const i32x4 -1 -1 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_min_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 -nan nan)
-                       (v128.const f64x2 0 0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 0 0)
-                       (v128.const f64x2 -nan nan))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 -0.0 +0.0))
-               (v128.const i64x2 -1 -1))
-
-(assert_return (invoke "f64x2.relaxed_max_cmp"
-                       (v128.const f64x2 +0.0 -0.0)
-                       (v128.const f64x2 +0.0 -0.0))
-               (v128.const i64x2 -1 -1))
diff --git a/tests/spec_testsuite b/tests/spec_testsuite
index 4f77306bb..7ef86ddee 160000
--- a/tests/spec_testsuite
+++ b/tests/spec_testsuite
@@ -1,1 +1,1 @@
-Subproject commit 4f77306bb63151631d84f58dedf67958eb9911b9
+Subproject commit 7ef86ddeed81458f9031a49a40b3a3f99c1c6a8a