Repository: bytecodealliance/wasmtime
Pull request #5977 (2023-03-10 03:28:20)

aarch64: Add specialized `shuffle` lowerings
This is the equivalent of https://github.com/bytecodealliance/wasmtime/pull/5930 but for AArch64. I went through various instructions I saw for AArch64 and added corresponding `shuffle` lowerings where appropriate. These lowerings cover all the lowerings I found in the meshoptimizer repository plus a few more based on various instructions I found while perusing ARM's documentation. Like with x86_64 I've tried to make sure there's a runtest and a precise-output test for each lowering, even if some of them probably overlap with the x86_64 runtests.

I'll note that many of these lowerings probably won't end up getting used by "portable" wasm binaries since some of the shifts here are pretty specific to AArch64 and don't have efficient 1/2 instruction lowerings on x86_64. That being said these are useful to any sort of hypothetical Cranelift-as-an-AArch64-backend-compiler such as rustc_cranelift_codegen since this broadens the spectrum of instructions supported by Cranelift's AArch64 backend.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 94d046acc..53af2084f 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",
diff --git a/Cargo.toml b/Cargo.toml
index 908bbb084..8b22fbfb3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -169,10 +169,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"
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/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..1d91d0898 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -2123,9 +2123,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 +2345,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 +2476,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/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 4cee85ed5..66a0d942a 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -1925,6 +1925,12 @@
 (rule (normalize_cmp_value $I64  r _) r)
 (rule (normalize_cmp_value $I128 r _) r)
 
+(decl normalize_fcvt_from_int (ValueRegs Type ExtendOp) ValueRegs)
+(rule 2 (normalize_fcvt_from_int r (fits_in_16 ty) op)
+  (extend r op ty $I64))
+(rule 1 (normalize_fcvt_from_int r _ _)
+  r)
+
 ;; Convert a truthy value, possibly of more than one register (an
 ;; I128), to one register. If narrower than 64 bits, must have already
 ;; been masked (e.g. by `normalize_cmp_value`).
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index 0a15865e7..163d0cb63 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -802,12 +802,12 @@
 ;;;;;  Rules for `fcvt_from_sint`;;;;;;;;;
 (rule
   (lower (has_type to (fcvt_from_sint v @ (value_type from))))
-  (fpu_rr (int_convert_2_float_op from $true to) to v))
+  (fpu_rr (int_convert_2_float_op from $true to) to (normalize_fcvt_from_int v from (ExtendOp.Signed))))
 
 ;;;;;  Rules for `fcvt_from_uint`;;;;;;;;;
 (rule
   (lower (has_type to (fcvt_from_uint v @ (value_type from))))
-  (fpu_rr (int_convert_2_float_op from $false to) to v))
+  (fpu_rr (int_convert_2_float_op from $false to) to (normalize_fcvt_from_int v from (ExtendOp.Zero))))
 
 ;;;;;  Rules for `symbol_value`;;;;;;;;;
 (rule
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/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/jit/src/compiled_blob.rs b/cranelift/jit/src/compiled_blob.rs
index 7c60454e7..652f4bd97 100644
--- a/cranelift/jit/src/compiled_blob.rs
+++ b/cranelift/jit/src/compiled_blob.rs
@@ -120,10 +120,18 @@ impl CompiledBlob {
                     // See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses
                     // for a better explanation of the following code.
                     //
-                    // Unlike the regular symbol relocations, here both "sub-relocations" point
-                    // to the same address.
+                    // Unlike the regular symbol relocations, here both "sub-relocations" point to the same address.
+                    //
+                    // `pcrel` is a signed value (+/- 2GiB range), when splitting it into two parts, we need to
+                    // ensure that `hi20` is close enough to `pcrel` to be able to add `lo12` to it and still
+                    // get a valid address.
+                    //
+                    // `lo12` is also a signed offset (+/- 2KiB range) relative to the `hi20` value.
+                    //
+                    // `hi20` should also be shifted right to be the "true" value. But we also need it
+                    // left shifted for the `lo12` calculation and it also matches the instruction encoding.
                     let hi20 = pcrel.wrapping_add(0x800) & 0xFFFFF000;
-                    let lo12 = (pcrel - hi20) & 0xFFF;
+                    let lo12 = pcrel.wrapping_sub(hi20) & 0xFFF;
 
                     unsafe {
                         // Do a R_RISCV_PCREL_HI20 on the `auipc`
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/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/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/fcvt-small.clif b/cranelift/filetests/filetests/isa/riscv64/fcvt-small.clif
index eb1be5612..e821e120f 100644
--- a/cranelift/filetests/filetests/isa/riscv64/fcvt-small.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/fcvt-small.clif
@@ -10,12 +10,14 @@ block0(v0: i8):
 
 ; VCode:
 ; block0:
-;   fcvt.s.lu fa0,a0
+;   andi t2,a0,255
+;   fcvt.s.lu fa0,t2
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.s.lu fa0, a0
+;   andi t2, a0, 0xff
+;   fcvt.s.lu fa0, t2
 ;   ret
 
 function u0:0(i8) -> f64 {
@@ -26,12 +28,14 @@ block0(v0: i8):
 
 ; VCode:
 ; block0:
-;   fcvt.d.lu fa0,a0
+;   andi t2,a0,255
+;   fcvt.d.lu fa0,t2
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.d.lu fa0, a0
+;   andi t2, a0, 0xff
+;   fcvt.d.lu fa0, t2
 ;   ret
 
 function u0:0(i16) -> f32 {
@@ -42,12 +46,16 @@ block0(v0: i16):
 
 ; VCode:
 ; block0:
-;   fcvt.s.lu fa0,a0
+;   slli t2,a0,48
+;   srli a1,t2,48
+;   fcvt.s.lu fa0,a1
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.s.lu fa0, a0
+;   slli t2, a0, 0x30
+;   srli a1, t2, 0x30
+;   fcvt.s.lu fa0, a1
 ;   ret
 
 function u0:0(i16) -> f64 {
@@ -58,12 +66,16 @@ block0(v0: i16):
 
 ; VCode:
 ; block0:
-;   fcvt.d.lu fa0,a0
+;   slli t2,a0,48
+;   srli a1,t2,48
+;   fcvt.d.lu fa0,a1
 ;   ret
 ; 
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.d.lu fa0, a0
+;   slli t2, a0, 0x30
+;   srli a1, t2, 0x30
+;   fcvt.d.lu fa0, a1
 ;   ret
 
 function u0:0(f32) -> i8 {
diff --git a/cranelift/filetests/filetests/runtests/issue5952.clif b/cranelift/filetests/filetests/runtests/issue5952.clif
new file mode 100644
index 000000000..64a4a8f39
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/issue5952.clif
@@ -0,0 +1,14 @@
+test interpret
+test run
+target aarch64
+target x86_64
+target s390x
+target riscv64
+
+function %a(i16 uext) -> f32 {
+block0(v0: i16):
+    v1 = fcvt_from_sint.f32 v0
+    return v1
+}
+
+; run: %a(-12800) == -0x1.900000p13
\ No newline at end of file
diff --git a/cranelift/filetests/filetests/runtests/simd-shuffle.clif b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
index 60b515628..6056fb3dd 100644
--- a/cranelift/filetests/filetests/runtests/simd-shuffle.clif
+++ b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
@@ -258,3 +258,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/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