Repository: bytecodealliance/wasmtime
Pull request #5971 (2023-03-09 16:30:02)

fuzzgen: Add SIMD instructions supported by the interpreter
👋 Hey,

This PR adds all SIMD instructions currently supported by the interpreter to the fuzzer. There is a surprising amount of missing coverage even with all of these ones (i.e. lots of work to do in the interpreter)!

I've also had to disable aligned loads/stores for > 8 bytes. It's really hard to do this without support from cranelift, and I think its probably best to go add support for that before re-enabling it here.

I gave this a bunch of fuzzing time, and it has stopped crashing on all arches. S390X was a little bit harder since I was only able to test it on QEMU and its quite a bit slower than native hardware though it ran about 12hours on 8 cores without crashing, so ¯\\\_(ツ)\_\/¯. 


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

cc @fitzgen

<details>
This issue or pull request has been labeled: "cranelift", "fuzzing"

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

* fitzgen: fuzzing

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

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

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 94d046acc..f61028eb3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3298,9 +3298,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-mutate"
-version = "0.2.21"
+version = "0.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92ada47eda4f13d5dd83b77af7751dd44f1ab8120c01f2fca5b78b49080cf82c"
+checksum = "956f9fd2f785491bdb243ae608c53fd6bb9956f9e174c1fc5a77354fce396698"
 dependencies = [
  "egg",
  "log",
@@ -3383,9 +3383,9 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.2.53"
+version = "0.2.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa44d546e4e4479f2e91035fa497c0a05cffbf22413ad05bf0b06a789b9118f"
+checksum = "2dc17ae63836d010a2bf001c26a5fedbb9a05e5f71117fb63e0ab878bfbe1ca3"
 dependencies = [
  "anyhow",
  "wasmparser",
@@ -3533,6 +3533,7 @@ dependencies = [
  "wasmtime-component-util",
  "wasmtime-cranelift",
  "wasmtime-environ",
+ "wasmtime-explorer",
  "wasmtime-runtime",
  "wasmtime-wasi",
  "wasmtime-wasi-crypto",
@@ -3647,6 +3648,19 @@ dependencies = [
  "wat",
 ]
 
+[[package]]
+name = "wasmtime-explorer"
+version = "8.0.0"
+dependencies = [
+ "anyhow",
+ "capstone",
+ "serde",
+ "serde_json",
+ "target-lexicon",
+ "wasmprinter",
+ "wasmtime",
+]
+
 [[package]]
 name = "wasmtime-fiber"
 version = "8.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index 908bbb084..f2634cffc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,6 +27,7 @@ wasmtime-cache = { workspace = true }
 wasmtime-cli-flags = { workspace = true }
 wasmtime-cranelift = { workspace = true }
 wasmtime-environ = { workspace = true }
+wasmtime-explorer = { workspace = true }
 wasmtime-wast = { workspace = true }
 wasmtime-wasi = { workspace = true, features = ["exit"] }
 wasmtime-wasi-crypto = { workspace = true, optional = true }
@@ -39,8 +40,8 @@ humantime = "2.0.0"
 once_cell = { workspace = true }
 listenfd = "1.0.0"
 wat = { workspace = true }
-serde = "1.0.94"
-serde_json = "1.0.26"
+serde = { workspace = true }
+serde_json = { workspace = true }
 wasmparser = { workspace = true }
 wasm-coredump-builder = { version = "0.1.11" }
 
@@ -70,8 +71,8 @@ component-macro-test = { path = "crates/misc/component-macro-test" }
 component-test-util = { workspace = true }
 bstr = "0.2.17"
 libc = "0.2.60"
-serde = "1.0"
-serde_json = "1.0"
+serde = { workspace = true }
+serde_json = { workspace = true }
 
 [target.'cfg(windows)'.dev-dependencies]
 windows-sys = { workspace = true, features = ["Win32_System_Memory"] }
@@ -120,6 +121,7 @@ wasmtime-cli-flags = { path = "crates/cli-flags", version = "=8.0.0" }
 wasmtime-cranelift = { path = "crates/cranelift", version = "=8.0.0" }
 wasmtime-cranelift-shared = { path = "crates/cranelift-shared", version = "=8.0.0" }
 wasmtime-environ = { path = "crates/environ", version = "=8.0.0" }
+wasmtime-explorer = { path = "crates/explorer", version = "=8.0.0" }
 wasmtime-fiber = { path = "crates/fiber", version = "=8.0.0" }
 wasmtime-types = { path = "crates/types", version = "8.0.0" }
 wasmtime-jit = { path = "crates/jit", version = "=8.0.0" }
@@ -169,10 +171,10 @@ anyhow = "1.0.22"
 wasmparser = "0.102.0"
 wat = "1.0.61"
 wast = "55.0.0"
-wasmprinter = "0.2.53"
+wasmprinter = "0.2.54"
 wasm-encoder = "0.25.0"
 wasm-smith = "0.12.5"
-wasm-mutate = "0.2.21"
+wasm-mutate = "0.2.22"
 wit-parser = "0.6.4"
 windows-sys = "0.45.0"
 env_logger = "0.9"
@@ -196,6 +198,7 @@ heck = "0.4"
 similar = "2.1.0"
 toml = "0.5.9"
 serde = "1.0.94"
+serde_json = "1.0.80"
 glob = "0.3.0"
 
 [features]
diff --git a/build.rs b/build.rs
index b735f076b..564133438 100644
--- a/build.rs
+++ b/build.rs
@@ -30,12 +30,6 @@ fn main() -> anyhow::Result<()> {
             test_directory_module(out, "tests/misc_testsuite/threads", strategy)?;
             test_directory_module(out, "tests/misc_testsuite/memory64", strategy)?;
             test_directory_module(out, "tests/misc_testsuite/component-model", strategy)?;
-
-            // NB: these are copied from upstream and updated to wasmtime's
-            // current version of `wast`. This local copy should go away when
-            // all of Wasmtime's tooling is updated and the upstream
-            // `testsuite` module is additionally updated.
-            test_directory_module(out, "tests/misc_testsuite/relaxed-simd", strategy)?;
             Ok(())
         })?;
 
@@ -51,6 +45,11 @@ fn main() -> anyhow::Result<()> {
                     strategy,
                 )?;
                 test_directory_module(out, "tests/spec_testsuite/proposals/threads", strategy)?;
+                test_directory_module(
+                    out,
+                    "tests/spec_testsuite/proposals/relaxed-simd",
+                    strategy,
+                )?;
             } else {
                 println!(
                     "cargo:warning=The spec testsuite is disabled. To enable, run `git submodule \
@@ -180,6 +179,12 @@ fn write_testsuite_tests(
 /// Ignore tests that aren't supported yet.
 fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
     assert_eq!(strategy, "Cranelift");
+
+    // This is an empty file right now which the `wast` crate doesn't parse
+    if testname.contains("memory_copy1") {
+        return true;
+    }
+
     match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_str() {
         "s390x" => {
             // FIXME: These tests fail under qemu due to a qemu bug.
diff --git a/cranelift/codegen/meta/src/cdsl/instructions.rs b/cranelift/codegen/meta/src/cdsl/instructions.rs
index 1f8e36c4f..34961f503 100644
--- a/cranelift/codegen/meta/src/cdsl/instructions.rs
+++ b/cranelift/codegen/meta/src/cdsl/instructions.rs
@@ -159,15 +159,15 @@ impl InstructionBuilder {
         }
     }
 
-    pub fn operands_in(mut self, operands: Vec<&Operand>) -> Self {
+    pub fn operands_in(mut self, operands: Vec<Operand>) -> Self {
         assert!(self.operands_in.is_none());
-        self.operands_in = Some(operands.iter().map(|x| (*x).clone()).collect());
+        self.operands_in = Some(operands);
         self
     }
 
-    pub fn operands_out(mut self, operands: Vec<&Operand>) -> Self {
+    pub fn operands_out(mut self, operands: Vec<Operand>) -> Self {
         assert!(self.operands_out.is_none());
-        self.operands_out = Some(operands.iter().map(|x| (*x).clone()).collect());
+        self.operands_out = Some(operands);
         self
     }
 
diff --git a/cranelift/codegen/meta/src/shared/instructions.rs b/cranelift/codegen/meta/src/shared/instructions.rs
index 372407965..f36790f26 100755
--- a/cranelift/codegen/meta/src/shared/instructions.rs
+++ b/cranelift/codegen/meta/src/shared/instructions.rs
@@ -17,9 +17,6 @@ fn define_control_flow(
     imm: &Immediates,
     entities: &EntityRefs,
 ) {
-    let block_call = &Operand::new("block_call", &entities.block_call)
-        .with_doc("Destination basic block, with its arguments provided");
-
     ig.push(
         Inst::new(
             "jump",
@@ -32,7 +29,8 @@ fn define_control_flow(
         "#,
             &formats.jump,
         )
-        .operands_in(vec![block_call])
+        .operands_in(vec![Operand::new("block_call", &entities.block_call)
+            .with_doc("Destination basic block, with its arguments provided")])
         .branches(),
     );
 
@@ -42,25 +40,23 @@ fn define_control_flow(
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
 
-    {
-        let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-        let block_then = &Operand::new("block_then", &entities.block_then).with_doc("Then block");
-        let block_else = &Operand::new("block_else", &entities.block_else).with_doc("Else block");
-
-        ig.push(
-            Inst::new(
-                "brif",
-                r#"
+    ig.push(
+        Inst::new(
+            "brif",
+            r#"
         Conditional branch when cond is non-zero.
 
         Take the ``then`` branch when ``c != 0``, and the ``else`` branch otherwise.
         "#,
-                &formats.brif,
-            )
-            .operands_in(vec![c, block_then, block_else])
-            .branches(),
-        );
-    }
+            &formats.brif,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("block_then", &entities.block_then).with_doc("Then block"),
+            Operand::new("block_else", &entities.block_else).with_doc("Else block"),
+        ])
+        .branches(),
+    );
 
     {
         let _i32 = &TypeVar::new(
@@ -68,8 +64,6 @@ fn define_control_flow(
             "A 32 bit scalar integer type",
             TypeSetBuilder::new().ints(32..32).build(),
         );
-        let x = &Operand::new("x", _i32).with_doc("i32 index into jump table");
-        let JT = &Operand::new("JT", &entities.jump_table);
 
         ig.push(
             Inst::new(
@@ -93,7 +87,10 @@ fn define_control_flow(
         "#,
                 &formats.branch_table,
             )
-            .operands_in(vec![x, JT])
+            .operands_in(vec![
+                Operand::new("x", _i32).with_doc("i32 index into jump table"),
+                Operand::new("JT", &entities.jump_table),
+            ])
             .branches(),
         );
     }
@@ -108,8 +105,8 @@ fn define_control_flow(
         Inst::new(
             "debugtrap",
             r#"
-    Encodes an assembly debug trap.
-    "#,
+        Encodes an assembly debug trap.
+        "#,
             &formats.nullary,
         )
         .other_side_effects()
@@ -117,81 +114,84 @@ fn define_control_flow(
         .can_store(),
     );
 
-    {
-        let code = &Operand::new("code", &imm.trapcode);
-        ig.push(
-            Inst::new(
-                "trap",
-                r#"
+    ig.push(
+        Inst::new(
+            "trap",
+            r#"
         Terminate execution unconditionally.
         "#,
-                &formats.trap,
-            )
-            .operands_in(vec![code])
-            .can_trap()
-            .terminates_block(),
-        );
+            &formats.trap,
+        )
+        .operands_in(vec![Operand::new("code", &imm.trapcode)])
+        .can_trap()
+        .terminates_block(),
+    );
 
-        let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-        ig.push(
-            Inst::new(
-                "trapz",
-                r#"
+    ig.push(
+        Inst::new(
+            "trapz",
+            r#"
         Trap when zero.
 
         if ``c`` is non-zero, execution continues at the following instruction.
         "#,
-                &formats.cond_trap,
-            )
-            .operands_in(vec![c, code])
-            .can_trap(),
-        );
+            &formats.cond_trap,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .can_trap(),
+    );
 
-        ig.push(
-            Inst::new(
-                "resumable_trap",
-                r#"
+    ig.push(
+        Inst::new(
+            "resumable_trap",
+            r#"
         A resumable trap.
 
         This instruction allows non-conditional traps to be used as non-terminal instructions.
         "#,
-                &formats.trap,
-            )
-            .operands_in(vec![code])
-            .can_trap(),
-        );
+            &formats.trap,
+        )
+        .operands_in(vec![Operand::new("code", &imm.trapcode)])
+        .can_trap(),
+    );
 
-        let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-        ig.push(
-            Inst::new(
-                "trapnz",
-                r#"
+    ig.push(
+        Inst::new(
+            "trapnz",
+            r#"
         Trap when non-zero.
 
         If ``c`` is zero, execution continues at the following instruction.
         "#,
-                &formats.cond_trap,
-            )
-            .operands_in(vec![c, code])
-            .can_trap(),
-        );
+            &formats.cond_trap,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .can_trap(),
+    );
 
-        ig.push(
-            Inst::new(
-                "resumable_trapnz",
-                r#"
+    ig.push(
+        Inst::new(
+            "resumable_trapnz",
+            r#"
         A resumable trap to be called when the passed condition is non-zero.
 
         If ``c`` is zero, execution continues at the following instruction.
         "#,
-                &formats.cond_trap,
-            )
-            .operands_in(vec![c, code])
-            .can_trap(),
-        );
-    }
+            &formats.cond_trap,
+        )
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .can_trap(),
+    );
 
-    let rvals = &Operand::new("rvals", &entities.varargs).with_doc("return values");
     ig.push(
         Inst::new(
             "return",
@@ -204,14 +204,12 @@ fn define_control_flow(
         "#,
             &formats.multiary,
         )
-        .operands_in(vec![rvals])
+        .operands_in(vec![
+            Operand::new("rvals", &entities.varargs).with_doc("return values")
+        ])
         .returns(),
     );
 
-    let FN = &Operand::new("FN", &entities.func_ref)
-        .with_doc("function to call, declared by `function`");
-    let args = &Operand::new("args", &entities.varargs).with_doc("call arguments");
-    let rvals = &Operand::new("rvals", &entities.varargs).with_doc("return values");
     ig.push(
         Inst::new(
             "call",
@@ -223,15 +221,17 @@ fn define_control_flow(
         "#,
             &formats.call,
         )
-        .operands_in(vec![FN, args])
-        .operands_out(vec![rvals])
+        .operands_in(vec![
+            Operand::new("FN", &entities.func_ref)
+                .with_doc("function to call, declared by `function`"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
+        .operands_out(vec![
+            Operand::new("rvals", &entities.varargs).with_doc("return values")
+        ])
         .call(),
     );
 
-    let SIG = &Operand::new("SIG", &entities.sig_ref).with_doc("function signature");
-    let callee = &Operand::new("callee", iAddr).with_doc("address of function to call");
-    let args = &Operand::new("args", &entities.varargs).with_doc("call arguments");
-    let rvals = &Operand::new("rvals", &entities.varargs).with_doc("return values");
     ig.push(
         Inst::new(
             "call_indirect",
@@ -248,8 +248,14 @@ fn define_control_flow(
         "#,
             &formats.call_indirect,
         )
-        .operands_in(vec![SIG, callee, args])
-        .operands_out(vec![rvals])
+        .operands_in(vec![
+            Operand::new("SIG", &entities.sig_ref).with_doc("function signature"),
+            Operand::new("callee", iAddr).with_doc("address of function to call"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
+        .operands_out(vec![
+            Operand::new("rvals", &entities.varargs).with_doc("return values")
+        ])
         .call(),
     );
 
@@ -268,7 +274,11 @@ fn define_control_flow(
         "#,
             &formats.call,
         )
-        .operands_in(vec![FN, args])
+        .operands_in(vec![
+            Operand::new("FN", &entities.func_ref)
+                .with_doc("function to call, declared by `function`"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
         .returns()
         .call(),
     );
@@ -293,14 +303,15 @@ fn define_control_flow(
         "#,
             &formats.call_indirect,
         )
-        .operands_in(vec![SIG, callee, args])
+        .operands_in(vec![
+            Operand::new("SIG", &entities.sig_ref).with_doc("function signature"),
+            Operand::new("callee", iAddr).with_doc("address of function to call"),
+            Operand::new("args", &entities.varargs).with_doc("call arguments"),
+        ])
         .returns()
         .call(),
     );
 
-    let FN = &Operand::new("FN", &entities.func_ref)
-        .with_doc("function to call, declared by `function`");
-    let addr = &Operand::new("addr", iAddr);
     ig.push(
         Inst::new(
             "func_addr",
@@ -315,8 +326,9 @@ fn define_control_flow(
         "#,
             &formats.func_addr,
         )
-        .operands_in(vec![FN])
-        .operands_out(vec![addr]),
+        .operands_in(vec![Operand::new("FN", &entities.func_ref)
+            .with_doc("function to call, declared by `function`")])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 }
 
@@ -339,9 +351,6 @@ fn define_simd_lane_access(
             .build(),
     );
 
-    let x = &Operand::new("x", &TxN.lane_of()).with_doc("Value to splat to all lanes");
-    let a = &Operand::new("a", TxN);
-
     ig.push(
         Inst::new(
             "splat",
@@ -352,8 +361,10 @@ fn define_simd_lane_access(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", &TxN.lane_of()).with_doc("Value to splat to all lanes")
+        ])
+        .operands_out(vec![Operand::new("a", TxN)]),
     );
 
     let I8x16 = &TypeVar::new(
@@ -365,9 +376,6 @@ fn define_simd_lane_access(
             .includes_scalars(false)
             .build(),
     );
-    let x = &Operand::new("x", I8x16).with_doc("Vector to modify by re-arranging lanes");
-    let y = &Operand::new("y", I8x16).with_doc("Mask for re-arranging lanes");
-    let a = &Operand::new("a", I8x16);
 
     ig.push(
         Inst::new(
@@ -382,8 +390,11 @@ fn define_simd_lane_access(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I8x16).with_doc("Vector to modify by re-arranging lanes"),
+            Operand::new("y", I8x16).with_doc("Mask for re-arranging lanes"),
+        ])
+        .operands_out(vec![Operand::new("a", I8x16)]),
     );
 
     ig.push(
@@ -403,15 +414,13 @@ fn define_simd_lane_access(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I8x16).with_doc("Vector to modify by re-arranging lanes"),
+            Operand::new("y", I8x16).with_doc("Mask for re-arranging lanes"),
+        ])
+        .operands_out(vec![Operand::new("a", I8x16)]),
     );
 
-    let x = &Operand::new("x", TxN).with_doc("The vector to modify");
-    let y = &Operand::new("y", &TxN.lane_of()).with_doc("New lane value");
-    let Idx = &Operand::new("Idx", &imm.uimm8).with_doc("Lane index");
-    let a = &Operand::new("a", TxN);
-
     ig.push(
         Inst::new(
             "insertlane",
@@ -423,13 +432,14 @@ fn define_simd_lane_access(
         "#,
             &formats.ternary_imm8,
         )
-        .operands_in(vec![x, y, Idx])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", TxN).with_doc("The vector to modify"),
+            Operand::new("y", &TxN.lane_of()).with_doc("New lane value"),
+            Operand::new("Idx", &imm.uimm8).with_doc("Lane index"),
+        ])
+        .operands_out(vec![Operand::new("a", TxN)]),
     );
 
-    let x = &Operand::new("x", TxN);
-    let a = &Operand::new("a", &TxN.lane_of());
-
     ig.push(
         Inst::new(
             "extractlane",
@@ -443,8 +453,11 @@ fn define_simd_lane_access(
         "#,
             &formats.binary_imm8,
         )
-        .operands_in(vec![x, Idx])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", TxN),
+            Operand::new("Idx", &imm.uimm8).with_doc("Lane index"),
+        ])
+        .operands_out(vec![Operand::new("a", &TxN.lane_of())]),
     );
 }
 
@@ -464,10 +477,6 @@ fn define_simd_arithmetic(
             .build(),
     );
 
-    let a = &Operand::new("a", Int);
-    let x = &Operand::new("x", Int);
-    let y = &Operand::new("y", Int);
-
     ig.push(
         Inst::new(
             "smin",
@@ -476,8 +485,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -488,8 +497,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -500,8 +509,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -512,8 +521,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     let IxN = &TypeVar::new(
@@ -526,10 +535,6 @@ fn define_simd_arithmetic(
             .build(),
     );
 
-    let a = &Operand::new("a", IxN);
-    let x = &Operand::new("x", IxN);
-    let y = &Operand::new("y", IxN);
-
     ig.push(
         Inst::new(
             "avg_round",
@@ -540,8 +545,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -556,8 +561,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -574,8 +579,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -590,8 +595,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 
     ig.push(
@@ -606,8 +611,8 @@ fn define_simd_arithmetic(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", IxN), Operand::new("y", IxN)])
+        .operands_out(vec![Operand::new("a", IxN)]),
     );
 }
 
@@ -716,16 +721,6 @@ pub(crate) fn define(
 
     let MemTo = &TypeVar::copy_from(Mem, "MemTo".to_string());
 
-    let addr = &Operand::new("addr", iAddr);
-
-    let SS = &Operand::new("SS", &entities.stack_slot);
-    let DSS = &Operand::new("DSS", &entities.dynamic_stack_slot);
-    let Offset = &Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address");
-    let x = &Operand::new("x", Mem).with_doc("Value to be stored");
-    let a = &Operand::new("a", Mem).with_doc("Value loaded");
-    let p = &Operand::new("p", iAddr);
-    let MemFlags = &Operand::new("MemFlags", &imm.memflags);
-
     ig.push(
         Inst::new(
             "load",
@@ -737,8 +732,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -753,7 +752,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", Mem).with_doc("Value to be stored"),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -762,8 +766,6 @@ pub(crate) fn define(
         "An integer type with more than 8 bits",
         TypeSetBuilder::new().ints(16..64).build(),
     );
-    let x = &Operand::new("x", iExt8);
-    let a = &Operand::new("a", iExt8);
 
     ig.push(
         Inst::new(
@@ -775,8 +777,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt8)])
         .can_load(),
     );
 
@@ -790,8 +796,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt8)])
         .can_load(),
     );
 
@@ -805,7 +815,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", iExt8),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -814,8 +829,6 @@ pub(crate) fn define(
         "An integer type with more than 16 bits",
         TypeSetBuilder::new().ints(32..64).build(),
     );
-    let x = &Operand::new("x", iExt16);
-    let a = &Operand::new("a", iExt16);
 
     ig.push(
         Inst::new(
@@ -827,8 +840,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt16)])
         .can_load(),
     );
 
@@ -842,8 +859,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt16)])
         .can_load(),
     );
 
@@ -857,7 +878,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", iExt16),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -866,8 +892,6 @@ pub(crate) fn define(
         "An integer type with more than 32 bits",
         TypeSetBuilder::new().ints(64..64).build(),
     );
-    let x = &Operand::new("x", iExt32);
-    let a = &Operand::new("a", iExt32);
 
     ig.push(
         Inst::new(
@@ -879,8 +903,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt32)])
         .can_load(),
     );
 
@@ -894,8 +922,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", iExt32)])
         .can_load(),
     );
 
@@ -909,7 +941,12 @@ pub(crate) fn define(
         "#,
             &formats.store,
         )
-        .operands_in(vec![MemFlags, x, p, Offset])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", iExt32),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
         .can_store(),
     );
 
@@ -922,7 +959,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", I16x8).with_doc("Value loaded");
 
     ig.push(
         Inst::new(
@@ -933,8 +969,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I16x8).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -947,8 +987,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I16x8).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -961,7 +1005,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", I32x4).with_doc("Value loaded");
 
     ig.push(
         Inst::new(
@@ -972,8 +1015,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I32x4).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -986,8 +1033,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I32x4).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1000,7 +1051,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", I64x2).with_doc("Value loaded");
 
     ig.push(
         Inst::new(
@@ -1011,8 +1061,12 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I64x2).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1025,16 +1079,15 @@ pub(crate) fn define(
         "#,
             &formats.load,
         )
-        .operands_in(vec![MemFlags, p, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from base address"),
+        ])
+        .operands_out(vec![Operand::new("a", I64x2).with_doc("Value loaded")])
         .can_load(),
     );
 
-    let x = &Operand::new("x", Mem).with_doc("Value to be stored");
-    let a = &Operand::new("a", Mem).with_doc("Value loaded");
-    let Offset =
-        &Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot");
-
     ig.push(
         Inst::new(
             "stack_load",
@@ -1050,8 +1103,11 @@ pub(crate) fn define(
         "#,
             &formats.stack_load,
         )
-        .operands_in(vec![SS, Offset])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("SS", &entities.stack_slot),
+            Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot"),
+        ])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1070,7 +1126,11 @@ pub(crate) fn define(
         "#,
             &formats.stack_store,
         )
-        .operands_in(vec![x, SS, Offset])
+        .operands_in(vec![
+            Operand::new("x", Mem).with_doc("Value to be stored"),
+            Operand::new("SS", &entities.stack_slot),
+            Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot"),
+        ])
         .can_store(),
     );
 
@@ -1086,8 +1146,11 @@ pub(crate) fn define(
         "#,
             &formats.stack_load,
         )
-        .operands_in(vec![SS, Offset])
-        .operands_out(vec![addr]),
+        .operands_in(vec![
+            Operand::new("SS", &entities.stack_slot),
+            Operand::new("Offset", &imm.offset32).with_doc("In-bounds offset into stack slot"),
+        ])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1101,8 +1164,8 @@ pub(crate) fn define(
         "#,
             &formats.dynamic_stack_load,
         )
-        .operands_in(vec![DSS])
-        .operands_out(vec![a])
+        .operands_in(vec![Operand::new("DSS", &entities.dynamic_stack_slot)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")])
         .can_load(),
     );
 
@@ -1117,11 +1180,13 @@ pub(crate) fn define(
         "#,
             &formats.dynamic_stack_store,
         )
-        .operands_in(vec![x, DSS])
+        .operands_in(vec![
+            Operand::new("x", Mem).with_doc("Value to be stored"),
+            Operand::new("DSS", &entities.dynamic_stack_slot),
+        ])
         .can_store(),
     );
 
-    let GV = &Operand::new("GV", &entities.global_value);
     ig.push(
         Inst::new(
             "dynamic_stack_addr",
@@ -1132,8 +1197,8 @@ pub(crate) fn define(
         "#,
             &formats.dynamic_stack_load,
         )
-        .operands_in(vec![DSS])
-        .operands_out(vec![addr]),
+        .operands_in(vec![Operand::new("DSS", &entities.dynamic_stack_slot)])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1144,8 +1209,8 @@ pub(crate) fn define(
         "#,
             &formats.unary_global_value,
         )
-        .operands_in(vec![GV])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("GV", &entities.global_value)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")]),
     );
 
     ig.push(
@@ -1156,8 +1221,8 @@ pub(crate) fn define(
         "#,
             &formats.unary_global_value,
         )
-        .operands_in(vec![GV])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("GV", &entities.global_value)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")]),
     );
 
     ig.push(
@@ -1168,8 +1233,8 @@ pub(crate) fn define(
         "#,
             &formats.unary_global_value,
         )
-        .operands_in(vec![GV])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("GV", &entities.global_value)])
+        .operands_out(vec![Operand::new("a", Mem).with_doc("Value loaded")]),
     );
 
     // Note this instruction is marked as having other side-effects, so GVN won't try to hoist it,
@@ -1186,7 +1251,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr])
+        .operands_out(vec![Operand::new("addr", iAddr)])
         .other_side_effects(),
     );
 
@@ -1198,7 +1263,7 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![addr])
+        .operands_in(vec![Operand::new("addr", iAddr)])
         .other_side_effects(),
     );
 
@@ -1212,7 +1277,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr]),
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1223,7 +1288,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr]),
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     ig.push(
@@ -1236,7 +1301,7 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![addr]),
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
     let TableOffset = &TypeVar::new(
@@ -1244,10 +1309,6 @@ pub(crate) fn define(
         "An unsigned table offset",
         TypeSetBuilder::new().ints(32..64).build(),
     );
-    let T = &Operand::new("T", &entities.table);
-    let p = &Operand::new("p", TableOffset);
-    let Offset =
-        &Operand::new("Offset", &imm.offset32).with_doc("Byte offset from element address");
 
     ig.push(
         Inst::new(
@@ -1267,13 +1328,14 @@ pub(crate) fn define(
         "#,
             &formats.table_addr,
         )
-        .operands_in(vec![T, p, Offset])
-        .operands_out(vec![addr]),
+        .operands_in(vec![
+            Operand::new("T", &entities.table),
+            Operand::new("p", TableOffset),
+            Operand::new("Offset", &imm.offset32).with_doc("Byte offset from element address"),
+        ])
+        .operands_out(vec![Operand::new("addr", iAddr)]),
     );
 
-    let N = &Operand::new("N", &imm.imm64);
-    let a = &Operand::new("a", NarrowInt).with_doc("A constant integer scalar or vector value");
-
     ig.push(
         Inst::new(
             "iconst",
@@ -1285,13 +1347,12 @@ pub(crate) fn define(
         "#,
             &formats.unary_imm,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.imm64)])
+        .operands_out(vec![
+            Operand::new("a", NarrowInt).with_doc("A constant integer scalar or vector value")
+        ]),
     );
 
-    let N = &Operand::new("N", &imm.ieee32);
-    let a = &Operand::new("a", f32_).with_doc("A constant f32 scalar value");
-
     ig.push(
         Inst::new(
             "f32const",
@@ -1302,13 +1363,12 @@ pub(crate) fn define(
         "#,
             &formats.unary_ieee32,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.ieee32)])
+        .operands_out(vec![
+            Operand::new("a", f32_).with_doc("A constant f32 scalar value")
+        ]),
     );
 
-    let N = &Operand::new("N", &imm.ieee64);
-    let a = &Operand::new("a", f64_).with_doc("A constant f64 scalar value");
-
     ig.push(
         Inst::new(
             "f64const",
@@ -1319,14 +1379,12 @@ pub(crate) fn define(
         "#,
             &formats.unary_ieee64,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.ieee64)])
+        .operands_out(vec![
+            Operand::new("a", f64_).with_doc("A constant f64 scalar value")
+        ]),
     );
 
-    let N = &Operand::new("N", &imm.pool_constant)
-        .with_doc("The 16 immediate bytes of a 128-bit vector");
-    let a = &Operand::new("a", TxN).with_doc("A constant vector value");
-
     ig.push(
         Inst::new(
             "vconst",
@@ -1337,12 +1395,13 @@ pub(crate) fn define(
         "#,
             &formats.unary_const,
         )
-        .operands_in(vec![N])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("N", &imm.pool_constant)
+            .with_doc("The 16 immediate bytes of a 128-bit vector")])
+        .operands_out(vec![
+            Operand::new("a", TxN).with_doc("A constant vector value")
+        ]),
     );
 
-    let mask = &Operand::new("mask", &imm.uimm128)
-        .with_doc("The 16 immediate bytes used for selecting the elements to shuffle");
     let Tx16 = &TypeVar::new(
         "Tx16",
         "A SIMD vector with exactly 16 lanes of 8-bit values; eventually this may support other \
@@ -1353,8 +1412,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let a = &Operand::new("a", Tx16).with_doc("A vector value");
-    let b = &Operand::new("b", Tx16).with_doc("A vector value");
 
     ig.push(
         Inst::new(
@@ -1369,12 +1426,15 @@ pub(crate) fn define(
         "#,
             &formats.shuffle,
         )
-        .operands_in(vec![a, b, mask])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("a", Tx16).with_doc("A vector value"),
+            Operand::new("b", Tx16).with_doc("A vector value"),
+            Operand::new("mask", &imm.uimm128)
+                .with_doc("The 16 immediate bytes used for selecting the elements to shuffle"),
+        ])
+        .operands_out(vec![Operand::new("a", Tx16).with_doc("A vector value")]),
     );
 
-    let a = &Operand::new("a", Ref).with_doc("A constant reference null value");
-
     ig.push(
         Inst::new(
             "null",
@@ -1385,7 +1445,9 @@ pub(crate) fn define(
         "#,
             &formats.nullary,
         )
-        .operands_out(vec![a]),
+        .operands_out(vec![
+            Operand::new("a", Ref).with_doc("A constant reference null value")
+        ]),
     );
 
     ig.push(Inst::new(
@@ -1398,11 +1460,6 @@ pub(crate) fn define(
         &formats.nullary,
     ));
 
-    let c = &Operand::new("c", ScalarTruthy).with_doc("Controlling value to test");
-    let x = &Operand::new("x", Any).with_doc("Value to use when `c` is true");
-    let y = &Operand::new("y", Any).with_doc("Value to use when `c` is false");
-    let a = &Operand::new("a", Any);
-
     ig.push(
         Inst::new(
             "select",
@@ -1414,8 +1471,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)]),
     );
 
     ig.push(
@@ -1441,15 +1502,18 @@ pub(crate) fn define(
             "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("c", ScalarTruthy).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)])
         .other_side_effects()
         // We can de-duplicate spectre selects since the side effect is
         // idempotent.
         .side_effects_idempotent(),
     );
 
-    let c = &Operand::new("c", Any).with_doc("Controlling value to test");
     ig.push(
         Inst::new(
             "bitselect",
@@ -1462,8 +1526,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("c", Any).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)]),
     );
 
     ig.push(
@@ -1480,13 +1548,14 @@ pub(crate) fn define(
             "#,
             &formats.ternary,
         )
-        .operands_in(vec![c, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("c", Any).with_doc("Controlling value to test"),
+            Operand::new("x", Any).with_doc("Value to use when `c` is true"),
+            Operand::new("y", Any).with_doc("Value to use when `c` is false"),
+        ])
+        .operands_out(vec![Operand::new("a", Any)]),
     );
 
-    let a = &Operand::new("a", TxN);
-    let s = &Operand::new("s", i8);
-
     ig.push(
         Inst::new(
             "vany_true",
@@ -1497,8 +1566,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![s]),
+        .operands_in(vec![Operand::new("a", TxN)])
+        .operands_out(vec![Operand::new("s", i8)]),
     );
 
     ig.push(
@@ -1511,13 +1580,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![s]),
+        .operands_in(vec![Operand::new("a", TxN)])
+        .operands_out(vec![Operand::new("s", i8)]),
     );
 
-    let a = &Operand::new("a", TxN);
-    let x = &Operand::new("x", Int);
-
     ig.push(
         Inst::new(
             "vhigh_bits",
@@ -1529,15 +1595,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![x]),
+        .operands_in(vec![Operand::new("a", TxN)])
+        .operands_out(vec![Operand::new("x", Int)]),
     );
 
-    let a = &Operand::new("a", &Int.as_bool());
-    let Cond = &Operand::new("Cond", &imm.intcc);
-    let x = &Operand::new("x", Int);
-    let y = &Operand::new("y", Int);
-
     ig.push(
         Inst::new(
             "icmp",
@@ -1561,14 +1622,14 @@ pub(crate) fn define(
         "#,
             &formats.int_compare,
         )
-        .operands_in(vec![Cond, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("Cond", &imm.intcc),
+            Operand::new("x", Int),
+            Operand::new("y", Int),
+        ])
+        .operands_out(vec![Operand::new("a", &Int.as_bool())]),
     );
 
-    let a = &Operand::new("a", i8);
-    let x = &Operand::new("x", iB);
-    let Y = &Operand::new("Y", &imm.imm64);
-
     ig.push(
         Inst::new(
             "icmp_imm",
@@ -1583,14 +1644,14 @@ pub(crate) fn define(
         "#,
             &formats.int_compare_imm,
         )
-        .operands_in(vec![Cond, x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("Cond", &imm.intcc),
+            Operand::new("x", iB),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", i8)]),
     );
 
-    let a = &Operand::new("a", Int);
-    let x = &Operand::new("x", Int);
-    let y = &Operand::new("y", Int);
-
     ig.push(
         Inst::new(
             "iadd",
@@ -1602,8 +1663,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1617,8 +1678,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1629,8 +1690,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1641,8 +1702,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1658,8 +1719,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1673,8 +1734,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -1688,8 +1749,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int), Operand::new("y", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     let I16or32 = &TypeVar::new(
@@ -1698,10 +1759,6 @@ pub(crate) fn define(
         TypeSetBuilder::new().ints(16..32).simd_lanes(4..8).build(),
     );
 
-    let qx = &Operand::new("x", I16or32);
-    let qy = &Operand::new("y", I16or32);
-    let qa = &Operand::new("a", I16or32);
-
     ig.push(
         Inst::new(
             "sqmul_round_sat",
@@ -1714,8 +1771,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![qx, qy])
-        .operands_out(vec![qa]),
+        .operands_in(vec![Operand::new("x", I16or32), Operand::new("y", I16or32)])
+        .operands_out(vec![Operand::new("a", I16or32)]),
     );
 
     ig.push(
@@ -1730,89 +1787,79 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![qx, qy])
-        .operands_out(vec![qa]),
+        .operands_in(vec![Operand::new("x", I16or32), Operand::new("y", I16or32)])
+        .operands_out(vec![Operand::new("a", I16or32)]),
     );
 
-    {
-        // Integer division and remainder are scalar-only; most
-        // hardware does not directly support vector integer division.
+    // Integer division and remainder are scalar-only; most
+    // hardware does not directly support vector integer division.
 
-        let x = &Operand::new("x", iB);
-        let y = &Operand::new("y", iB);
-        let a = &Operand::new("a", iB);
-
-        ig.push(
-            Inst::new(
-                "udiv",
-                r#"
-            Unsigned integer division: `a := \lfloor {x \over y} \rfloor`.
-
-            This operation traps if the divisor is zero.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
+    ig.push(
+        Inst::new(
+            "udiv",
+            r#"
+        Unsigned integer division: `a := \lfloor {x \over y} \rfloor`.
 
-        ig.push(
-            Inst::new(
-                "sdiv",
-                r#"
-            Signed integer division rounded toward zero: `a := sign(xy)
-            \lfloor {|x| \over |y|}\rfloor`.
+        This operation traps if the divisor is zero.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
-            This operation traps if the divisor is zero, or if the result is not
-            representable in `B` bits two's complement. This only happens
-            when `x = -2^{B-1}, y = -1`.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
+    ig.push(
+        Inst::new(
+            "sdiv",
+            r#"
+        Signed integer division rounded toward zero: `a := sign(xy)
+        \lfloor {|x| \over |y|}\rfloor`.
 
-        ig.push(
-            Inst::new(
-                "urem",
-                r#"
-            Unsigned integer remainder.
+        This operation traps if the divisor is zero, or if the result is not
+        representable in `B` bits two's complement. This only happens
+        when `x = -2^{B-1}, y = -1`.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
-            This operation traps if the divisor is zero.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
+    ig.push(
+        Inst::new(
+            "urem",
+            r#"
+        Unsigned integer remainder.
 
-        ig.push(
-            Inst::new(
-                "srem",
-                r#"
-            Signed integer remainder. The result has the sign of the dividend.
+        This operation traps if the divisor is zero.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
-            This operation traps if the divisor is zero.
-            "#,
-                &formats.binary,
-            )
-            .operands_in(vec![x, y])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
-    }
+    ig.push(
+        Inst::new(
+            "srem",
+            r#"
+        Signed integer remainder. The result has the sign of the dividend.
 
-    let a = &Operand::new("a", iB);
-    let x = &Operand::new("x", iB);
-    let Y = &Operand::new("Y", &imm.imm64);
+        This operation traps if the divisor is zero.
+        "#,
+            &formats.binary,
+        )
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![Operand::new("a", iB)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
     ig.push(
         Inst::new(
@@ -1827,8 +1874,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1844,8 +1891,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1860,8 +1907,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1878,8 +1925,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1894,8 +1941,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1910,8 +1957,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1931,19 +1978,10 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
-    let a = &Operand::new("a", iB);
-    let x = &Operand::new("x", iB);
-    let y = &Operand::new("y", iB);
-
-    let c_in = &Operand::new("c_in", i8).with_doc("Input carry flag");
-    let c_out = &Operand::new("c_out", i8).with_doc("Output carry flag");
-    let b_in = &Operand::new("b_in", i8).with_doc("Input borrow flag");
-    let b_out = &Operand::new("b_out", i8).with_doc("Output borrow flag");
-
     ig.push(
         Inst::new(
             "iadd_cin",
@@ -1961,8 +1999,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, c_in])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("c_in", i8).with_doc("Input carry flag"),
+        ])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -1983,8 +2025,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a, c_out]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("c_out", i8).with_doc("Output carry flag"),
+        ]),
     );
 
     ig.push(
@@ -2005,38 +2050,42 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, c_in])
-        .operands_out(vec![a, c_out]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("c_in", i8).with_doc("Input carry flag"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("c_out", i8).with_doc("Output carry flag"),
+        ]),
     );
 
-    {
-        let code = &Operand::new("code", &imm.trapcode);
-
-        let i32_64 = &TypeVar::new(
-            "i32_64",
-            "A 32 or 64-bit scalar integer type",
-            TypeSetBuilder::new().ints(32..64).build(),
-        );
+    let i32_64 = &TypeVar::new(
+        "i32_64",
+        "A 32 or 64-bit scalar integer type",
+        TypeSetBuilder::new().ints(32..64).build(),
+    );
 
-        let a = &Operand::new("a", i32_64);
-        let x = &Operand::new("x", i32_64);
-        let y = &Operand::new("y", i32_64);
-        ig.push(
-            Inst::new(
-                "uadd_overflow_trap",
-                r#"
-            Unsigned addition of x and y, trapping if the result overflows.
+    ig.push(
+        Inst::new(
+            "uadd_overflow_trap",
+            r#"
+        Unsigned addition of x and y, trapping if the result overflows.
 
-            Accepts 32 or 64-bit integers, and does not support vector types.
-            "#,
-                &formats.int_add_trap,
-            )
-            .operands_in(vec![x, y, code])
-            .operands_out(vec![a])
-            .can_trap()
-            .side_effects_idempotent(),
-        );
-    }
+        Accepts 32 or 64-bit integers, and does not support vector types.
+        "#,
+            &formats.int_add_trap,
+        )
+        .operands_in(vec![
+            Operand::new("x", i32_64),
+            Operand::new("y", i32_64),
+            Operand::new("code", &imm.trapcode),
+        ])
+        .operands_out(vec![Operand::new("a", i32_64)])
+        .can_trap()
+        .side_effects_idempotent(),
+    );
 
     ig.push(
         Inst::new(
@@ -2055,8 +2104,12 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, b_in])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("b_in", i8).with_doc("Input borrow flag"),
+        ])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2077,8 +2130,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a, b_out]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("y", iB)])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("b_out", i8).with_doc("Output borrow flag"),
+        ]),
     );
 
     ig.push(
@@ -2099,8 +2155,15 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, b_in])
-        .operands_out(vec![a, b_out]),
+        .operands_in(vec![
+            Operand::new("x", iB),
+            Operand::new("y", iB),
+            Operand::new("b_in", i8).with_doc("Input borrow flag"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", iB),
+            Operand::new("b_out", i8).with_doc("Output borrow flag"),
+        ]),
     );
 
     let bits = &TypeVar::new(
@@ -2113,9 +2176,6 @@ pub(crate) fn define(
             .includes_scalars(true)
             .build(),
     );
-    let x = &Operand::new("x", bits);
-    let y = &Operand::new("y", bits);
-    let a = &Operand::new("a", bits);
 
     ig.push(
         Inst::new(
@@ -2125,8 +2185,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2137,8 +2197,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2149,8 +2209,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2161,8 +2221,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2175,8 +2235,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2189,8 +2249,8 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
     ig.push(
@@ -2203,14 +2263,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", bits), Operand::new("y", bits)])
+        .operands_out(vec![Operand::new("a", bits)]),
     );
 
-    let x = &Operand::new("x", iB);
-    let Y = &Operand::new("Y", &imm.imm64);
-    let a = &Operand::new("a", iB);
-
     ig.push(
         Inst::new(
             "band_imm",
@@ -2224,8 +2280,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2241,8 +2297,8 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2258,15 +2314,10 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB), Operand::new("Y", &imm.imm64)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
-    let x = &Operand::new("x", Int).with_doc("Scalar or vector value to shift");
-    let y = &Operand::new("y", iB).with_doc("Number of bits to shift");
-    let Y = &Operand::new("Y", &imm.imm64);
-    let a = &Operand::new("a", Int);
-
     ig.push(
         Inst::new(
             "rotl",
@@ -2277,8 +2328,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2291,8 +2345,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2305,8 +2362,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2319,8 +2379,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2341,8 +2404,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2364,8 +2430,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2380,8 +2449,11 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("y", iB).with_doc("Number of bits to shift"),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2394,8 +2466,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2408,8 +2483,11 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     ig.push(
@@ -2422,13 +2500,13 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm64,
         )
-        .operands_in(vec![x, Y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Int).with_doc("Scalar or vector value to shift"),
+            Operand::new("Y", &imm.imm64),
+        ])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
-    let x = &Operand::new("x", iB);
-    let a = &Operand::new("a", iB);
-
     ig.push(
         Inst::new(
             "bitrev",
@@ -2439,8 +2517,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2455,8 +2533,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2471,8 +2549,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
     ig.push(
@@ -2487,13 +2565,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iB)])
+        .operands_out(vec![Operand::new("a", iB)]),
     );
 
-    let x = &Operand::new("x", iSwappable);
-    let a = &Operand::new("a", iSwappable);
-
     ig.push(
         Inst::new(
             "bswap",
@@ -2504,13 +2579,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", iSwappable)])
+        .operands_out(vec![Operand::new("a", iSwappable)]),
     );
 
-    let x = &Operand::new("x", Int);
-    let a = &Operand::new("a", Int);
-
     ig.push(
         Inst::new(
             "popcnt",
@@ -2521,8 +2593,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", Int)]),
     );
 
     let Float = &TypeVar::new(
@@ -2534,10 +2606,6 @@ pub(crate) fn define(
             .dynamic_simd_lanes(Interval::All)
             .build(),
     );
-    let Cond = &Operand::new("Cond", &imm.floatcc);
-    let x = &Operand::new("x", Float);
-    let y = &Operand::new("y", Float);
-    let a = &Operand::new("a", &Float.as_bool());
 
     ig.push(
         Inst::new(
@@ -2611,15 +2679,14 @@ pub(crate) fn define(
         "#,
             &formats.float_compare,
         )
-        .operands_in(vec![Cond, x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("Cond", &imm.floatcc),
+            Operand::new("x", Float),
+            Operand::new("y", Float),
+        ])
+        .operands_out(vec![Operand::new("a", &Float.as_bool())]),
     );
 
-    let x = &Operand::new("x", Float);
-    let y = &Operand::new("y", Float);
-    let z = &Operand::new("z", Float);
-    let a = &Operand::new("a", Float).with_doc("Result of applying operator to each lane");
-
     ig.push(
         Inst::new(
             "fadd",
@@ -2628,8 +2695,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2640,8 +2709,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2652,8 +2723,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2668,8 +2741,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2680,8 +2755,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
     ig.push(
@@ -2695,12 +2772,16 @@ pub(crate) fn define(
         "#,
             &formats.ternary,
         )
-        .operands_in(vec![x, y, z])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", Float),
+            Operand::new("y", Float),
+            Operand::new("z", Float),
+        ])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("Result of applying operator to each lane")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` with its sign bit inverted");
-
     ig.push(
         Inst::new(
             "fneg",
@@ -2711,12 +2792,12 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` with its sign bit inverted")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` with its sign bit cleared");
-
     ig.push(
         Inst::new(
             "fabs",
@@ -2727,12 +2808,12 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` with its sign bit cleared")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` with its sign bit changed to that of ``y``");
-
     ig.push(
         Inst::new(
             "fcopysign",
@@ -2744,12 +2825,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` with its sign bit changed to that of ``y``")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("The smaller of ``x`` and ``y``");
-
     ig.push(
         Inst::new(
             "fmin",
@@ -2763,8 +2844,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The smaller of ``x`` and ``y``")
+        ]),
     );
 
     ig.push(
@@ -2779,12 +2862,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The smaller of ``x`` and ``y``")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("The larger of ``x`` and ``y``");
-
     ig.push(
         Inst::new(
             "fmax",
@@ -2798,8 +2881,10 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The larger of ``x`` and ``y``")
+        ]),
     );
 
     ig.push(
@@ -2814,12 +2899,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float), Operand::new("y", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("The larger of ``x`` and ``y``")
+        ]),
     );
 
-    let a = &Operand::new("a", Float).with_doc("``x`` rounded to integral value");
-
     ig.push(
         Inst::new(
             "ceil",
@@ -2828,8 +2913,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
     ig.push(
@@ -2840,8 +2927,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
     ig.push(
@@ -2852,8 +2941,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
     ig.push(
@@ -2865,13 +2956,12 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![
+            Operand::new("a", Float).with_doc("``x`` rounded to integral value")
+        ]),
     );
 
-    let a = &Operand::new("a", i8);
-    let x = &Operand::new("x", Ref);
-
     ig.push(
         Inst::new(
             "is_null",
@@ -2883,13 +2973,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Ref)])
+        .operands_out(vec![Operand::new("a", i8)]),
     );
 
-    let a = &Operand::new("a", i8);
-    let x = &Operand::new("x", Ref);
-
     ig.push(
         Inst::new(
             "is_invalid",
@@ -2901,14 +2988,10 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Ref)])
+        .operands_out(vec![Operand::new("a", i8)]),
     );
 
-    let x = &Operand::new("x", Mem);
-    let a = &Operand::new("a", MemTo).with_doc("Bits of `x` reinterpreted");
-    let MemFlags = &Operand::new("MemFlags", &imm.memflags);
-
     ig.push(
         Inst::new(
             "bitcast",
@@ -2926,13 +3009,15 @@ pub(crate) fn define(
         "#,
             &formats.load_no_offset,
         )
-        .operands_in(vec![MemFlags, x])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", Mem),
+        ])
+        .operands_out(vec![
+            Operand::new("a", MemTo).with_doc("Bits of `x` reinterpreted")
+        ]),
     );
 
-    let a = &Operand::new("a", TxN).with_doc("A vector value");
-    let s = &Operand::new("s", &TxN.lane_of()).with_doc("A scalar value");
-
     ig.push(
         Inst::new(
             "scalar_to_vector",
@@ -2942,8 +3027,10 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![s])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("s", &TxN.lane_of()).with_doc("A scalar value")
+        ])
+        .operands_out(vec![Operand::new("a", TxN).with_doc("A vector value")]),
     );
 
     let Truthy = &TypeVar::new(
@@ -2956,8 +3043,6 @@ pub(crate) fn define(
         "An integer type",
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
-    let x = &Operand::new("x", Truthy);
-    let a = &Operand::new("a", IntTo);
 
     ig.push(
         Inst::new(
@@ -2969,8 +3054,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Truthy)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     let Int = &TypeVar::new(
@@ -2984,8 +3069,6 @@ pub(crate) fn define(
         "A smaller integer type",
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
-    let x = &Operand::new("x", Int);
-    let a = &Operand::new("a", IntTo);
 
     ig.push(
         Inst::new(
@@ -2998,8 +3081,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     let I16or32or64xN = &TypeVar::new(
@@ -3013,10 +3096,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", I16or32or64xN);
-    let y = &Operand::new("y", I16or32or64xN);
-    let a = &Operand::new("a", &I16or32or64xN.split_lanes());
-
     ig.push(
         Inst::new(
             "snarrow",
@@ -3030,8 +3109,11 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I16or32or64xN),
+            Operand::new("y", I16or32or64xN),
+        ])
+        .operands_out(vec![Operand::new("a", &I16or32or64xN.split_lanes())]),
     );
 
     ig.push(
@@ -3050,8 +3132,11 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I16or32or64xN),
+            Operand::new("y", I16or32or64xN),
+        ])
+        .operands_out(vec![Operand::new("a", &I16or32or64xN.split_lanes())]),
     );
 
     ig.push(
@@ -3069,8 +3154,8 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I16or32or64xN), Operand::new("y", I16or32or64xN)])
+        .operands_out(vec![Operand::new("a", &I16or32or64xN.split_lanes())]),
     );
 
     let I8or16or32xN = &TypeVar::new(
@@ -3084,9 +3169,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", I8or16or32xN);
-    let a = &Operand::new("a", &I8or16or32xN.merge_lanes());
-
     ig.push(
         Inst::new(
             "swiden_low",
@@ -3097,8 +3179,8 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
     ig.push(
@@ -3111,8 +3193,8 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
     ig.push(
@@ -3125,8 +3207,8 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
     ig.push(
@@ -3139,14 +3221,10 @@ pub(crate) fn define(
             "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8or16or32xN)])
+        .operands_out(vec![Operand::new("a", &I8or16or32xN.merge_lanes())]),
     );
 
-    let x = &Operand::new("x", I8or16or32xN);
-    let y = &Operand::new("y", I8or16or32xN);
-    let a = &Operand::new("a", I8or16or32xN);
-
     ig.push(
         Inst::new(
             "iadd_pairwise",
@@ -3160,8 +3238,11 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", I8or16or32xN),
+            Operand::new("y", I8or16or32xN),
+        ])
+        .operands_out(vec![Operand::new("a", I8or16or32xN)]),
     );
 
     let I8x16 = &TypeVar::new(
@@ -3173,9 +3254,6 @@ pub(crate) fn define(
             .includes_scalars(false)
             .build(),
     );
-    let x = &Operand::new("x", I8x16);
-    let y = &Operand::new("y", I8x16);
-    let a = &Operand::new("a", I16x8);
 
     ig.push(
         Inst::new(
@@ -3190,8 +3268,8 @@ pub(crate) fn define(
             "#,
             &formats.binary,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", I8x16), Operand::new("y", I8x16)])
+        .operands_out(vec![Operand::new("a", I16x8)]),
     );
 
     {
@@ -3201,9 +3279,6 @@ pub(crate) fn define(
             TypeSetBuilder::new().ints(Interval::All).build(),
         );
 
-        let x = &Operand::new("x", Int);
-        let a = &Operand::new("a", IntTo);
-
         ig.push(
             Inst::new(
                 "uextend",
@@ -3220,8 +3295,8 @@ pub(crate) fn define(
         "#,
                 &formats.unary,
             )
-            .operands_in(vec![x])
-            .operands_out(vec![a]),
+            .operands_in(vec![Operand::new("x", Int)])
+            .operands_out(vec![Operand::new("a", IntTo)]),
         );
 
         ig.push(
@@ -3240,8 +3315,8 @@ pub(crate) fn define(
         "#,
                 &formats.unary,
             )
-            .operands_in(vec![x])
-            .operands_out(vec![a]),
+            .operands_in(vec![Operand::new("x", Int)])
+            .operands_out(vec![Operand::new("a", IntTo)]),
         );
     }
 
@@ -3254,9 +3329,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", Float);
-    let a = &Operand::new("a", FloatTo);
-
     ig.push(
         Inst::new(
             "fpromote",
@@ -3274,8 +3346,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     ig.push(
@@ -3295,8 +3367,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     let F64x2 = &TypeVar::new(
@@ -3318,9 +3390,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", F64x2);
-    let a = &Operand::new("a", F32x4);
-
     ig.push(
         Inst::new(
             "fvdemote",
@@ -3340,8 +3409,8 @@ pub(crate) fn define(
                 "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", F64x2)])
+        .operands_out(vec![Operand::new("a", F32x4)]),
     );
 
     ig.push(
@@ -3358,8 +3427,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![a])
-        .operands_out(vec![x]),
+        .operands_in(vec![Operand::new("a", F32x4)])
+        .operands_out(vec![Operand::new("x", F64x2)]),
     );
 
     let FloatScalar = &TypeVar::new(
@@ -3372,8 +3441,6 @@ pub(crate) fn define(
         "An scalar only integer type",
         TypeSetBuilder::new().ints(Interval::All).build(),
     );
-    let x = &Operand::new("x", FloatScalar);
-    let a = &Operand::new("a", IntTo);
 
     ig.push(
         Inst::new(
@@ -3388,8 +3455,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a])
+        .operands_in(vec![Operand::new("x", FloatScalar)])
+        .operands_out(vec![Operand::new("a", IntTo)])
         .can_trap()
         .side_effects_idempotent(),
     );
@@ -3407,8 +3474,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a])
+        .operands_in(vec![Operand::new("x", FloatScalar)])
+        .operands_out(vec![Operand::new("a", IntTo)])
         .can_trap()
         .side_effects_idempotent(),
     );
@@ -3422,9 +3489,6 @@ pub(crate) fn define(
             .build(),
     );
 
-    let x = &Operand::new("x", Float);
-    let a = &Operand::new("a", IntTo);
-
     ig.push(
         Inst::new(
             "fcvt_to_uint_sat",
@@ -3435,8 +3499,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     ig.push(
@@ -3448,8 +3512,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     ig.push(
@@ -3462,8 +3526,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Float)])
+        .operands_out(vec![Operand::new("a", IntTo)]),
     );
 
     let Int = &TypeVar::new(
@@ -3474,8 +3538,6 @@ pub(crate) fn define(
             .simd_lanes(Interval::All)
             .build(),
     );
-    let x = &Operand::new("x", Int);
-    let a = &Operand::new("a", FloatTo);
 
     ig.push(
         Inst::new(
@@ -3490,8 +3552,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     ig.push(
@@ -3507,8 +3569,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     ig.push(
@@ -3527,8 +3589,8 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![a]),
+        .operands_in(vec![Operand::new("x", Int)])
+        .operands_out(vec![Operand::new("a", FloatTo)]),
     );
 
     let WideInt = &TypeVar::new(
@@ -3539,9 +3601,6 @@ pub(crate) fn define(
             .simd_lanes(Interval::All)
             .build(),
     );
-    let x = &Operand::new("x", WideInt);
-    let lo = &Operand::new("lo", &WideInt.half_width()).with_doc("The low bits of `x`");
-    let hi = &Operand::new("hi", &WideInt.half_width()).with_doc("The high bits of `x`");
 
     ig.push(
         Inst::new(
@@ -3557,15 +3616,13 @@ pub(crate) fn define(
         "#,
             &formats.unary,
         )
-        .operands_in(vec![x])
-        .operands_out(vec![lo, hi]),
+        .operands_in(vec![Operand::new("x", WideInt)])
+        .operands_out(vec![
+            Operand::new("lo", &WideInt.half_width()).with_doc("The low bits of `x`"),
+            Operand::new("hi", &WideInt.half_width()).with_doc("The high bits of `x`"),
+        ]),
     );
 
-    let lo = &Operand::new("lo", NarrowInt);
-    let hi = &Operand::new("hi", NarrowInt);
-    let a = &Operand::new("a", &NarrowInt.double_width())
-        .with_doc("The concatenation of `lo` and `hi`");
-
     ig.push(
         Inst::new(
             "iconcat",
@@ -3578,8 +3635,12 @@ pub(crate) fn define(
         "#,
             &formats.binary,
         )
-        .operands_in(vec![lo, hi])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("lo", NarrowInt),
+            Operand::new("hi", NarrowInt),
+        ])
+        .operands_out(vec![Operand::new("a", &NarrowInt.double_width())
+            .with_doc("The concatenation of `lo` and `hi`")]),
     );
 
     // Instructions relating to atomic memory accesses and fences
@@ -3588,12 +3649,6 @@ pub(crate) fn define(
         "Any type that can be stored in memory, which can be used in an atomic operation",
         TypeSetBuilder::new().ints(8..64).build(),
     );
-    let x = &Operand::new("x", AtomicMem).with_doc("Value to be atomically stored");
-    let a = &Operand::new("a", AtomicMem).with_doc("Value atomically loaded");
-    let e = &Operand::new("e", AtomicMem).with_doc("Expected value in CAS");
-    let p = &Operand::new("p", iAddr);
-    let MemFlags = &Operand::new("MemFlags", &imm.memflags);
-    let AtomicRmwOp = &Operand::new("AtomicRmwOp", &imm.atomic_rmw_op);
 
     ig.push(
         Inst::new(
@@ -3607,8 +3662,15 @@ pub(crate) fn define(
         "#,
             &formats.atomic_rmw,
         )
-        .operands_in(vec![MemFlags, AtomicRmwOp, p, x])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("AtomicRmwOp", &imm.atomic_rmw_op),
+            Operand::new("p", iAddr),
+            Operand::new("x", AtomicMem).with_doc("Value to be atomically stored"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", AtomicMem).with_doc("Value atomically loaded")
+        ])
         .can_load()
         .can_store()
         .other_side_effects(),
@@ -3629,8 +3691,15 @@ pub(crate) fn define(
         "#,
             &formats.atomic_cas,
         )
-        .operands_in(vec![MemFlags, p, e, x])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+            Operand::new("e", AtomicMem).with_doc("Expected value in CAS"),
+            Operand::new("x", AtomicMem).with_doc("Value to be atomically stored"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", AtomicMem).with_doc("Value atomically loaded")
+        ])
         .can_load()
         .can_store()
         .other_side_effects(),
@@ -3649,8 +3718,13 @@ pub(crate) fn define(
         "#,
             &formats.load_no_offset,
         )
-        .operands_in(vec![MemFlags, p])
-        .operands_out(vec![a])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("p", iAddr),
+        ])
+        .operands_out(vec![
+            Operand::new("a", AtomicMem).with_doc("Value atomically loaded")
+        ])
         .can_load()
         .other_side_effects(),
     );
@@ -3668,7 +3742,11 @@ pub(crate) fn define(
         "#,
             &formats.store_no_offset,
         )
-        .operands_in(vec![MemFlags, x, p])
+        .operands_in(vec![
+            Operand::new("MemFlags", &imm.memflags),
+            Operand::new("x", AtomicMem).with_doc("Value to be atomically stored"),
+            Operand::new("p", iAddr),
+        ])
         .can_store()
         .other_side_effects(),
     );
@@ -3695,9 +3773,6 @@ pub(crate) fn define(
             .dynamic_simd_lanes(Interval::All)
             .build(),
     );
-    let x = &Operand::new("x", TxN).with_doc("The dynamic vector to extract from");
-    let y = &Operand::new("y", &imm.uimm8).with_doc("128-bit vector index");
-    let a = &Operand::new("a", &TxN.dynamic_to_vector()).with_doc("New fixed vector");
 
     ig.push(
         Inst::new(
@@ -3707,7 +3782,12 @@ pub(crate) fn define(
         "#,
             &formats.binary_imm8,
         )
-        .operands_in(vec![x, y])
-        .operands_out(vec![a]),
+        .operands_in(vec![
+            Operand::new("x", TxN).with_doc("The dynamic vector to extract from"),
+            Operand::new("y", &imm.uimm8).with_doc("128-bit vector index"),
+        ])
+        .operands_out(vec![
+            Operand::new("a", &TxN.dynamic_to_vector()).with_doc("New fixed vector")
+        ]),
     );
 }
diff --git a/cranelift/codegen/src/context.rs b/cranelift/codegen/src/context.rs
index 94c5722fd..bd8ae3b64 100644
--- a/cranelift/codegen/src/context.rs
+++ b/cranelift/codegen/src/context.rs
@@ -213,7 +213,6 @@ impl Context {
     ///
     /// Returns information about the function's code and read-only data.
     pub fn compile(&mut self, isa: &dyn TargetIsa) -> CompileResult<&CompiledCode> {
-        let _tt = timing::compile();
         let stencil = self.compile_stencil(isa).map_err(|error| CompileError {
             inner: error,
             func: &self.func,
@@ -379,6 +378,8 @@ impl Context {
 
     /// Run optimizations via the egraph infrastructure.
     pub fn egraph_pass(&mut self) -> CodegenResult<()> {
+        let _tt = timing::egraph();
+
         trace!(
             "About to optimize with egraph phase:\n{}",
             self.func.display()
diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle
index 53b0e65ed..f035789c1 100644
--- a/cranelift/codegen/src/isa/aarch64/inst.isle
+++ b/cranelift/codegen/src/isa/aarch64/inst.isle
@@ -553,7 +553,8 @@
        (VecDupFromFpu
         (rd WritableReg)
         (rn Reg)
-        (size VectorSize))
+        (size VectorSize)
+        (lane u8))
 
        ;; Duplicate FP immediate to vector.
        (VecDupFPImm
@@ -1390,8 +1391,18 @@
     (Addp)
     ;; Zip vectors (primary) [meaning, high halves]
     (Zip1)
+    ;; Zip vectors (secondary)
+    (Zip2)
     ;; Signed saturating rounding doubling multiply returning high half
     (Sqrdmulh)
+    ;; Unzip vectors (primary)
+    (Uzp1)
+    ;; Unzip vectors (secondary)
+    (Uzp2)
+    ;; Transpose vectors (primary)
+    (Trn1)
+    ;; Transpose vectors (secondary)
+    (Trn2)
 ))
 
 ;; A Vector ALU operation which modifies a source register.
@@ -1420,6 +1431,10 @@
     (Fneg)
     ;; Floating-point square root
     (Fsqrt)
+    ;; Reverse elements in 16-bit lanes
+    (Rev16)
+    ;; Reverse elements in 32-bit lanes
+    (Rev32)
     ;; Reverse elements in 64-bit doublewords
     (Rev64)
     ;; Floating-point convert to signed integer, rounding toward zero
@@ -1887,10 +1902,10 @@
         dst))
 
 ;; Helper for emitting `MInst.VecDupFromFpu` instructions.
-(decl vec_dup_from_fpu (Reg VectorSize) Reg)
-(rule (vec_dup_from_fpu src size)
+(decl vec_dup_from_fpu (Reg VectorSize u8) Reg)
+(rule (vec_dup_from_fpu src size lane)
       (let ((dst WritableReg (temp_writable_reg $I8X16))
-            (_ Unit (emit (MInst.VecDupFromFpu dst src size))))
+            (_ Unit (emit (MInst.VecDupFromFpu dst src size lane))))
         dst))
 
 ;; Helper for emitting `MInst.AluRRImm12` instructions.
@@ -2386,6 +2401,14 @@
 (decl neg (Reg VectorSize) Reg)
 (rule (neg x size) (vec_misc (VecMisc2.Neg) x size))
 
+;; Helper for generating `rev16` instructions.
+(decl rev16 (Reg VectorSize) Reg)
+(rule (rev16 x size) (vec_misc (VecMisc2.Rev16) x size))
+
+;; Helper for generating `rev32` instructions.
+(decl rev32 (Reg VectorSize) Reg)
+(rule (rev32 x size) (vec_misc (VecMisc2.Rev32) x size))
+
 ;; Helper for generating `rev64` instructions.
 (decl rev64 (Reg VectorSize) Reg)
 (rule (rev64 x size) (vec_misc (VecMisc2.Rev64) x size))
@@ -3767,3 +3790,27 @@
        (emit_side_effect (with_flags_side_effect
             (cmp (OperandSize.Size32) ridx jt_size)
             (jt_sequence ridx jt_info)))))
+
+;; Helper for emitting the `uzp1` instruction
+(decl vec_uzp1 (Reg Reg VectorSize) Reg)
+(rule (vec_uzp1 rn rm size) (vec_rrr (VecALUOp.Uzp1) rn rm size))
+
+;; Helper for emitting the `uzp2` instruction
+(decl vec_uzp2 (Reg Reg VectorSize) Reg)
+(rule (vec_uzp2 rn rm size) (vec_rrr (VecALUOp.Uzp2) rn rm size))
+
+;; Helper for emitting the `zip1` instruction
+(decl vec_zip1 (Reg Reg VectorSize) Reg)
+(rule (vec_zip1 rn rm size) (vec_rrr (VecALUOp.Zip1) rn rm size))
+
+;; Helper for emitting the `zip2` instruction
+(decl vec_zip2 (Reg Reg VectorSize) Reg)
+(rule (vec_zip2 rn rm size) (vec_rrr (VecALUOp.Zip2) rn rm size))
+
+;; Helper for emitting the `trn1` instruction
+(decl vec_trn1 (Reg Reg VectorSize) Reg)
+(rule (vec_trn1 rn rm size) (vec_rrr (VecALUOp.Trn1) rn rm size))
+
+;; Helper for emitting the `trn2` instruction
+(decl vec_trn2 (Reg Reg VectorSize) Reg)
+(rule (vec_trn2 rn rm size) (vec_rrr (VecALUOp.Trn2) rn rm size))
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index 72b1e1176..2332ff19b 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -1977,8 +1977,20 @@ impl MachInstEmit for Inst {
                         );
                         (0b1, 0b11111, enc_size)
                     }
+                    VecMisc2::Rev16 => {
+                        debug_assert_eq!(size, VectorSize::Size8x16);
+                        (0b0, 0b00001, enc_size)
+                    }
+                    VecMisc2::Rev32 => {
+                        debug_assert!(size == VectorSize::Size8x16 || size == VectorSize::Size16x8);
+                        (0b1, 0b00000, enc_size)
+                    }
                     VecMisc2::Rev64 => {
-                        debug_assert_ne!(VectorSize::Size64x2, size);
+                        debug_assert!(
+                            size == VectorSize::Size8x16
+                                || size == VectorSize::Size16x8
+                                || size == VectorSize::Size32x4
+                        );
                         (0b0, 0b00000, enc_size)
                     }
                     VecMisc2::Fcvtzs => {
@@ -2493,13 +2505,27 @@ impl MachInstEmit for Inst {
                         | machreg_to_vec(rd.to_reg()),
                 );
             }
-            &Inst::VecDupFromFpu { rd, rn, size } => {
+            &Inst::VecDupFromFpu { rd, rn, size, lane } => {
                 let rd = allocs.next_writable(rd);
                 let rn = allocs.next(rn);
                 let q = size.is_128bits() as u32;
                 let imm5 = match size.lane_size() {
-                    ScalarSize::Size32 => 0b00100,
-                    ScalarSize::Size64 => 0b01000,
+                    ScalarSize::Size8 => {
+                        assert!(lane < 16);
+                        0b00001 | (u32::from(lane) << 1)
+                    }
+                    ScalarSize::Size16 => {
+                        assert!(lane < 8);
+                        0b00010 | (u32::from(lane) << 2)
+                    }
+                    ScalarSize::Size32 => {
+                        assert!(lane < 4);
+                        0b00100 | (u32::from(lane) << 3)
+                    }
+                    ScalarSize::Size64 => {
+                        assert!(lane < 2);
+                        0b01000 | (u32::from(lane) << 4)
+                    }
                     _ => unimplemented!(),
                 };
                 sink.put4(
@@ -2870,6 +2896,7 @@ impl MachInstEmit for Inst {
                     VecALUOp::Fmul => (0b001_01110_00_1, 0b110111),
                     VecALUOp::Addp => (0b000_01110_00_1 | enc_size << 1, 0b101111),
                     VecALUOp::Zip1 => (0b01001110_00_0 | enc_size << 1, 0b001110),
+                    VecALUOp::Zip2 => (0b01001110_00_0 | enc_size << 1, 0b011110),
                     VecALUOp::Sqrdmulh => {
                         debug_assert!(
                             size.lane_size() == ScalarSize::Size16
@@ -2878,6 +2905,10 @@ impl MachInstEmit for Inst {
 
                         (0b001_01110_00_1 | enc_size << 1, 0b101101)
                     }
+                    VecALUOp::Uzp1 => (0b01001110_00_0 | enc_size << 1, 0b000110),
+                    VecALUOp::Uzp2 => (0b01001110_00_0 | enc_size << 1, 0b010110),
+                    VecALUOp::Trn1 => (0b01001110_00_0 | enc_size << 1, 0b001010),
+                    VecALUOp::Trn2 => (0b01001110_00_0 | enc_size << 1, 0b011010),
                 };
                 let top11 = if is_float {
                     top11 | size.enc_float_size() << 1
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index 779782907..6e8e602de 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -416,22 +416,30 @@ impl Inst {
                 size
             }]
         } else if let Some(imm) = widen_32_bit_pattern(pattern, lane_size) {
-            let tmp = alloc_tmp(types::I64X2);
-            let mut insts = smallvec![Inst::VecDupImm {
-                rd: tmp,
-                imm,
-                invert: false,
-                size: VectorSize::Size64x2,
-            }];
+            let mut insts = smallvec![];
 
             // TODO: Implement support for 64-bit scalar MOVI; we zero-extend the
             // lower 64 bits instead.
             if !size.is_128bits() {
+                let tmp = alloc_tmp(types::I64X2);
+                insts.push(Inst::VecDupImm {
+                    rd: tmp,
+                    imm,
+                    invert: false,
+                    size: VectorSize::Size64x2,
+                });
                 insts.push(Inst::FpuExtend {
                     rd,
                     rn: tmp.to_reg(),
                     size: ScalarSize::Size64,
                 });
+            } else {
+                insts.push(Inst::VecDupImm {
+                    rd,
+                    imm,
+                    invert: false,
+                    size: VectorSize::Size64x2,
+                });
             }
 
             insts
@@ -2123,9 +2131,9 @@ impl Inst {
                 let rn = pretty_print_ireg(rn, size.operand_size(), allocs);
                 format!("dup {}, {}", rd, rn)
             }
-            &Inst::VecDupFromFpu { rd, rn, size } => {
+            &Inst::VecDupFromFpu { rd, rn, size, lane } => {
                 let rd = pretty_print_vreg_vector(rd.to_reg(), size, allocs);
-                let rn = pretty_print_vreg_element(rn, 0, size.lane_size(), allocs);
+                let rn = pretty_print_vreg_element(rn, lane.into(), size.lane_size(), allocs);
                 format!("dup {}, {}", rd, rn)
             }
             &Inst::VecDupFPImm { rd, imm, size } => {
@@ -2345,7 +2353,12 @@ impl Inst {
                     VecALUOp::Fmul => ("fmul", size),
                     VecALUOp::Addp => ("addp", size),
                     VecALUOp::Zip1 => ("zip1", size),
+                    VecALUOp::Zip2 => ("zip2", size),
                     VecALUOp::Sqrdmulh => ("sqrdmulh", size),
+                    VecALUOp::Uzp1 => ("uzp1", size),
+                    VecALUOp::Uzp2 => ("uzp2", size),
+                    VecALUOp::Trn1 => ("trn1", size),
+                    VecALUOp::Trn2 => ("trn2", size),
                 };
                 let rd = pretty_print_vreg_vector(rd.to_reg(), size, allocs);
                 let rn = pretty_print_vreg_vector(rn, size, allocs);
@@ -2471,6 +2484,8 @@ impl Inst {
                     VecMisc2::Fabs => ("fabs", size, ""),
                     VecMisc2::Fneg => ("fneg", size, ""),
                     VecMisc2::Fsqrt => ("fsqrt", size, ""),
+                    VecMisc2::Rev16 => ("rev16", size, ""),
+                    VecMisc2::Rev32 => ("rev32", size, ""),
                     VecMisc2::Rev64 => ("rev64", size, ""),
                     VecMisc2::Fcvtzs => ("fcvtzs", size, ""),
                     VecMisc2::Fcvtzu => ("fcvtzu", size, ""),
diff --git a/cranelift/codegen/src/isa/aarch64/lower.isle b/cranelift/codegen/src/isa/aarch64/lower.isle
index edb112447..f3a3db703 100644
--- a/cranelift/codegen/src/isa/aarch64/lower.isle
+++ b/cranelift/codegen/src/isa/aarch64/lower.isle
@@ -118,6 +118,118 @@
 
 ;;;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+;; When a single element of one vector is broadcast to all the destination
+;; lanes then the `dup` instruction can be used for this operation. Note that
+;; for now this only matches lane selection from the first vector `a`, but
+;; if necessary in the future rules can be added to select from `b` as well.
+(rule 6 (lower (shuffle a b (shuffle_dup8_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size8x16) n))
+(rule 5 (lower (shuffle a b (shuffle_dup16_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size16x8) n))
+(rule 4 (lower (shuffle a b (shuffle_dup32_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size32x4) n))
+(rule 3 (lower (shuffle a b (shuffle_dup64_from_imm n)))
+        (vec_dup_from_fpu a (VectorSize.Size64x2) n))
+
+;; If the `Immediate` specified to the extractor looks like a duplication of the
+;; `n`th lane of the first vector of size K-byte lanes, then each extractor
+;; returns the `n` value as a `u8` to be used as part of a `vec_dup_from_fpu`
+;; instruction. Note that there's a different extractor for each bit-width of
+;; lane.
+(decl shuffle_dup8_from_imm (u8) Immediate)
+(extern extractor shuffle_dup8_from_imm shuffle_dup8_from_imm)
+(decl shuffle_dup16_from_imm (u8) Immediate)
+(extern extractor shuffle_dup16_from_imm shuffle_dup16_from_imm)
+(decl shuffle_dup32_from_imm (u8) Immediate)
+(extern extractor shuffle_dup32_from_imm shuffle_dup32_from_imm)
+(decl shuffle_dup64_from_imm (u8) Immediate)
+(extern extractor shuffle_dup64_from_imm shuffle_dup64_from_imm)
+
+;; When the shuffle looks like "concatenate `a` and `b` and shift right by n*8
+;; bytes", that's an `ext` instruction.
+(rule 2 (lower (shuffle a b (vec_extract_imm4_from_immediate n)))
+        (vec_extract a b n))
+
+;; Attempts to extract `n` from the specified shuffle `Immediate` where each
+;; byte of the `Immediate` is a consecutive sequence starting from `n`. This
+;; value of `n` is used as part of the `vec_extract` instruction which extracts
+;; consecutive bytes from two vectors into one final vector, offset by `n`
+;; bytes.
+(decl vec_extract_imm4_from_immediate (u8) Immediate)
+(extern extractor vec_extract_imm4_from_immediate vec_extract_imm4_from_immediate)
+
+;; Rules for the `uzp1` and `uzp2` instructions which gather even-numbered lanes
+;; or odd-numbered lanes
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1e1c_1a18_1614_1210_0e0c_0a08_0604_0200)))
+      (vec_uzp1 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1d_1b19_1715_1311_0f0d_0b09_0705_0301)))
+      (vec_uzp2 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1d1c_1918_1514_1110_0d0c_0908_0504_0100)))
+      (vec_uzp1 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_1b1a_1716_1312_0f0e_0b0a_0706_0302)))
+      (vec_uzp2 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1b1a1918_13121110_0b0a0908_03020100)))
+      (vec_uzp1 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_17161514_0f0e0d0c_07060504)))
+      (vec_uzp2 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1716151413121110_0706050403020100)))
+      (vec_uzp1 a b (VectorSize.Size64x2)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908)))
+      (vec_uzp2 a b (VectorSize.Size64x2)))
+
+;; Rules for the `zip1` and `zip2` instructions which interleave lanes in the
+;; low or high halves of the two input vectors.
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000)))
+      (vec_zip1 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808)))
+      (vec_zip2 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100)))
+      (vec_zip1 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908)))
+      (vec_zip2 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x17161514_07060504_13121110_03020100)))
+      (vec_zip1 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908)))
+      (vec_zip2 a b (VectorSize.Size32x4)))
+;; Note that zip1/zip2 for i64x2 vectors is omitted since it's already covered
+;; by the i64x2 cases of uzp1/uzp2 above where both zip and uzp have the same
+;; semantics for 64-bit lanes.
+
+;; Rules for the `trn1` and `trn2` instructions which interleave odd or even
+;; lanes in the two input vectors.
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1e0e_1c0c_1a0a_1808_1606_1404_1202_1000)))
+      (vec_trn1 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f0f_1d0d_1b0b_1909_1707_1505_1303_1101)))
+      (vec_trn2 a b (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1d1c_0d0c_1918_0908_1514_0504_1110_0100)))
+      (vec_trn1 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1b1a_0b0a_1716_0706_1312_0302)))
+      (vec_trn2 a b (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1b1a1918_0b0a0908_13121110_03020100)))
+      (vec_trn1 a b (VectorSize.Size32x4)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_17161514_07060504)))
+      (vec_trn2 a b (VectorSize.Size32x4)))
+;; Note that trn1/trn2 for i64x2 vectors is omitted since it's already covered
+;; by the i64x2 cases of uzp1/uzp2 above where both trn and uzp have the same
+;; semantics for 64-bit lanes.
+
+;; Rules for the `rev{16,32,64}` instructions where reversals happen at either
+;; the byte level, the 16-bit level, or 32-bit level. Note that all of these
+;; patterns only match reversals in the first operand, but they can
+;; theoretically be extended if necessary to reversals in the second operand.
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0e0f_0c0d_0a0b_0809_0607_0405_0203_0001)))
+      (rev16 a (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0c0d0e0f_08090a0b_04050607_00010203)))
+      (rev32 a (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0d0c0f0e_09080b0a_05040706_01000302)))
+      (rev32 a (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x08090a0b0c0d0e0f_0001020304050607)))
+      (rev64 a (VectorSize.Size8x16)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x09080b0a0d0c0f0e_0100030205040706)))
+      (rev64 a (VectorSize.Size16x8)))
+(rule 1 (lower (shuffle a b (u128_from_immediate 0x0b0a09080f0e0d0c_0302010007060504)))
+      (rev64 a (VectorSize.Size32x4)))
+
 (rule (lower (has_type ty (shuffle rn rn2 (u128_from_immediate mask))))
       (let ((mask_reg Reg (constant_f128 mask)))
        (vec_tbl2 rn rn2 mask_reg ty)))
@@ -1840,7 +1952,7 @@
       (vec_dup x (vector_size ty)))
 
 (rule -2 (lower (has_type ty (splat x @ (value_type (ty_scalar_float _)))))
-      (vec_dup_from_fpu x (vector_size ty)))
+      (vec_dup_from_fpu x (vector_size ty) 0))
 
 (rule (lower (has_type ty (splat (f32const (u64_from_ieee32 n)))))
       (splat_const n (vector_size ty)))
diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
index 69796c4bc..707243d2a 100644
--- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
@@ -513,10 +513,14 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
     }
 
     fn amode(&mut self, ty: Type, addr: Value, offset: u32) -> AMode {
+        let addr_ty = self.value_type(addr);
+        assert!(addr_ty == I64 || addr_ty == R64);
         lower_address(self.lower_ctx, ty, addr, offset as i32)
     }
 
     fn pair_amode(&mut self, addr: Value, offset: u32) -> PairAMode {
+        let addr_ty = self.value_type(addr);
+        assert!(addr_ty == I64 || addr_ty == R64);
         lower_pair_address(self.lower_ctx, addr, offset as i32)
     }
 
@@ -738,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 b08d6558f..163d0cb63 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -697,63 +697,63 @@
 
 ;;;;;  Rules for `uload8`;;;;;;;;;
 (rule
-  (lower (uload8 flags p offset))
+  (lower (uload8 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $false 8) flags $I64))
 ;;;;;  Rules for `sload8`;;;;;;;;;
 (rule
-  (lower (sload8 flags p offset))
+  (lower (sload8 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $true 8) flags $I64))
 ;;;;;  Rules for `uload16`;;;;;;;;;
 (rule
-  (lower (uload16 flags p offset))
+  (lower (uload16 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $false 16) flags $I64))
 
 ;;;;;  Rules for `iload16`;;;;;;;;;
 (rule
-  (lower (sload16 flags p offset))
+  (lower (sload16 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $true 16) flags $I64))
 
 ;;;;;  Rules for `uload32`;;;;;;;;;
 (rule
-  (lower (uload32 flags p offset))
+  (lower (uload32 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $false 32) flags $I64))
 
 ;;;;;  Rules for `iload16`;;;;;;;;;
 (rule
-  (lower (sload32 flags p offset))
+  (lower (sload32 flags p @ (value_type (ty_addr64 _)) offset))
   (gen_load p offset (int_load_op $true 32) flags $I64))
 
 (rule
-  (lower (has_type ty (load flags p offset)))
+  (lower (has_type ty (load flags p @ (value_type (ty_addr64 _)) offset)))
   (gen_load p offset (load_op ty) flags ty)
 )
 ;;;; for I128
 (rule 1
-  (lower (has_type $I128 (load flags p offset)))
+  (lower (has_type $I128 (load flags p @ (value_type (ty_addr64 _)) offset)))
   (gen_load_128 p offset flags))
 
 ;;;;;  Rules for `istore8`;;;;;;;;;
 (rule
-  (lower (istore8 flags x p offset))
+  (lower (istore8 flags x p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (StoreOP.Sb) flags x))
 ;;;;;  Rules for `istore16`;;;;;;;;;
 (rule
-  (lower (istore16 flags x p offset))
+  (lower (istore16 flags x p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (StoreOP.Sh) flags x))
 
 ;;;;;  Rules for `istore32`;;;;;;;;;
 (rule
-  (lower (istore32 flags x p offset))
+  (lower (istore32 flags x p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (StoreOP.Sw) flags x))
 
 ;;;;;  Rules for `store`;;;;;;;;;
 (rule
-  (lower (store flags x @ (value_type ty) p offset))
+  (lower (store flags x @ (value_type ty) p @ (value_type (ty_addr64 _)) offset))
   (gen_store p offset (store_op ty) flags x))
 
 ;;; special for I128
 (rule 1
-  (lower (store flags x @ (value_type $I128 ) p offset))
+  (lower (store flags x @ (value_type $I128 ) p @ (value_type (ty_addr64 _)) offset))
   (gen_store_128 p offset flags x))
 
 (decl gen_icmp (IntCC ValueRegs ValueRegs Type) Reg)
@@ -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.isle b/cranelift/codegen/src/isa/s390x/inst.isle
index 815513f04..ffc5f7350 100644
--- a/cranelift/codegen/src/isa/s390x/inst.isle
+++ b/cranelift/codegen/src/isa/s390x/inst.isle
@@ -1811,10 +1811,10 @@
 
 (decl lower_address (MemFlags Value Offset32) MemArg)
 
-(rule (lower_address flags addr (i64_from_offset offset))
+(rule (lower_address flags addr @ (value_type (ty_addr64 _)) (i64_from_offset offset))
       (memarg_reg_plus_off addr offset 0 flags))
 
-(rule 1 (lower_address flags (iadd x y) (i64_from_offset 0))
+(rule 1 (lower_address flags (has_type (ty_addr64 _) (iadd x y)) (i64_from_offset 0))
       (memarg_reg_plus_reg x y 0 flags))
 
 (rule 1 (lower_address flags
@@ -1828,10 +1828,10 @@
 
 (decl lower_address_bias (MemFlags Value Offset32 u8) MemArg)
 
-(rule (lower_address_bias flags addr (i64_from_offset offset) bias)
+(rule (lower_address_bias flags addr @ (value_type $I64) (i64_from_offset offset) bias)
       (memarg_reg_plus_off addr offset bias flags))
 
-(rule 1 (lower_address_bias flags (iadd x y) (i64_from_offset 0) bias)
+(rule 1 (lower_address_bias flags (has_type $I64 (iadd x y)) (i64_from_offset 0) bias)
       (memarg_reg_plus_reg x y bias flags))
 
 
diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs
index d68e3317c..7305b98fb 100644
--- a/cranelift/codegen/src/isa/s390x/inst/mod.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs
@@ -261,10 +261,10 @@ impl Inst {
                 _ => InstructionSet::Base,
             },
             Inst::FpuRound { op, .. } => match op {
-                FpuRoundOp::ToSInt32 | FpuRoundOp::FromSInt32 => InstructionSet::MIE2,
-                FpuRoundOp::ToUInt32 | FpuRoundOp::FromUInt32 => InstructionSet::MIE2,
-                FpuRoundOp::ToSInt32x4 | FpuRoundOp::FromSInt32x4 => InstructionSet::MIE2,
-                FpuRoundOp::ToUInt32x4 | FpuRoundOp::FromUInt32x4 => InstructionSet::MIE2,
+                FpuRoundOp::ToSInt32 | FpuRoundOp::FromSInt32 => InstructionSet::VXRS_EXT2,
+                FpuRoundOp::ToUInt32 | FpuRoundOp::FromUInt32 => InstructionSet::VXRS_EXT2,
+                FpuRoundOp::ToSInt32x4 | FpuRoundOp::FromSInt32x4 => InstructionSet::VXRS_EXT2,
+                FpuRoundOp::ToUInt32x4 | FpuRoundOp::FromUInt32x4 => InstructionSet::VXRS_EXT2,
                 _ => InstructionSet::Base,
             },
 
diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle
index 5293d7071..99cfeda06 100644
--- a/cranelift/codegen/src/isa/x64/inst.isle
+++ b/cranelift/codegen/src/isa/x64/inst.isle
@@ -227,13 +227,6 @@
                         (src2 XmmMem)
                         (dst WritableXmm))
 
-       ;; XMM (scalar or vector) production of a constant value by operating
-       ;; on a register with itself.
-       ;;
-       ;; Used to produce all zeros with xor or all one with a comparison.
-       (XmmConstOp (op SseOpcode)
-                   (dst WritableXmm))
-
        ;; XMM (scalar or vector) blend op. The mask is used to blend between
        ;; src1 and src2. This differs from a use of `XmmRmR` as the mask is
        ;; implicitly in register xmm0; this special case exists to allow us to
@@ -294,6 +287,12 @@
                           (dst WritableXmm)
                           (imm u8))
 
+       ;; XMM (scalar or vector) unary op (from xmm to reg/mem) using the
+       ;; VEX prefix
+       (XmmMovRMVex (op AvxOpcode)
+                    (src Reg)
+                    (dst SyntheticAmode))
+
        ;; XMM (scalar or vector) binary op that relies on the EVEX
        ;; prefix. Takes two inputs.
        (XmmRmREvex (op Avx512Opcode)
@@ -863,6 +862,12 @@
             Xorpd
             Phaddw
             Phaddd
+            Punpckhdq
+            Punpckldq
+            Punpckhqdq
+            Punpcklqdq
+            Pshuflw
+            Pshufhw
           ))
 
 (type CmpOpcode extern
@@ -1114,7 +1119,7 @@
         (Amode.ImmRegRegShift off sum index shift flags)))
 
 ;; Finally, define the toplevel `to_amode`.
-(rule (to_amode flags base offset)
+(rule (to_amode flags base @ (value_type (ty_addr64 _)) offset)
       (amode_finalize (amode_add (amode_initial flags offset) base)))
 
 ;; If an amode has no registers at all and only offsets (a constant
@@ -1347,6 +1352,18 @@
             Vcvttps2dq
             Vphaddw
             Vphaddd
+            Vpunpckhdq
+            Vpunpckldq
+            Vpunpckhqdq
+            Vpunpcklqdq
+            Vpshuflw
+            Vpshufhw
+            Vpshufd
+            Vmovss
+            Vmovsd
+            Vmovups
+            Vmovupd
+            Vmovdqu
           ))
 
 (type Avx512Opcode extern
@@ -1714,21 +1731,27 @@
 (decl sinkable_load (SinkableLoad) Value)
 (extern extractor sinkable_load sinkable_load)
 
-;; Sink a `SinkableLoad` into a `RegMemImm.Mem`.
+;; Sink a `SinkableLoad` into a `SyntheticAmode`.
 ;;
 ;; This is a side-effectful operation that notifies the context that the
 ;; instruction that produced the `SinkableImm` has been sunk into another
 ;; instruction, and no longer needs to be lowered.
-(decl sink_load (SinkableLoad) RegMem)
+(decl sink_load (SinkableLoad) SyntheticAmode)
 (extern constructor sink_load sink_load)
 
 (decl sink_load_to_gpr_mem_imm (SinkableLoad) GprMemImm)
 (rule (sink_load_to_gpr_mem_imm load)
-      (gpr_mem_imm_new (sink_load load)))
+      (gpr_mem_imm_new load))
 
 (decl sink_load_to_xmm_mem (SinkableLoad) XmmMem)
 (rule (sink_load_to_xmm_mem load)
-      (reg_mem_to_xmm_mem (sink_load load)))
+      (reg_mem_to_xmm_mem load))
+
+(decl sink_load_to_reg_mem (SinkableLoad) RegMem)
+(rule (sink_load_to_reg_mem load) (RegMem.Mem load))
+
+(decl sink_load_to_reg_mem_imm (SinkableLoad) RegMemImm)
+(rule (sink_load_to_reg_mem_imm load) (RegMemImm.Mem load))
 
 ;;;; Helpers for Sign/Zero Extending ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -1787,28 +1810,13 @@
 (rule (vec_int_type (multi_lane 32 4)) $I32X4)
 (rule (vec_int_type (multi_lane 64 2)) $I64X2)
 
-;; Determine the appropriate operation for xor-ing vectors of the specified type
-(decl sse_xor_op (Type) SseOpcode)
-(rule 1 (sse_xor_op $F32X4) (SseOpcode.Xorps))
-(rule 1 (sse_xor_op $F64X2) (SseOpcode.Xorpd))
-(rule 1 (sse_xor_op $F32) (SseOpcode.Xorps))
-(rule 1 (sse_xor_op $F64) (SseOpcode.Xorpd))
-
-;; Priority 0 because multi_lane overlaps with the previous two explicit type
-;; patterns.
-(rule 0 (sse_xor_op (multi_lane _bits _lanes)) (SseOpcode.Pxor))
-
-(decl avx_xor_op (Type) AvxOpcode)
-(rule 1 (avx_xor_op $F32X4) (AvxOpcode.Vxorps))
-(rule 1 (avx_xor_op $F64X2) (AvxOpcode.Vxorpd))
-(rule 0 (avx_xor_op (multi_lane _bits _lanes)) (AvxOpcode.Vpxor))
-
 ;; Performs an xor operation of the two operands specified.
-(decl sse_xor (Type Xmm XmmMem) Xmm)
-(rule 0 (sse_xor ty x y) (xmm_rm_r (sse_xor_op ty) x y))
-(rule 1 (sse_xor ty @ (multi_lane _ _) x y)
-        (if-let $true (has_avx))
-        (xmm_rmir_vex (avx_xor_op ty) x y))
+(decl x64_xor_vector (Type Xmm XmmMem) Xmm)
+(rule 1 (x64_xor_vector $F32 x y) (x64_xorps x y))
+(rule 1 (x64_xor_vector $F64 x y) (x64_xorpd x y))
+(rule 1 (x64_xor_vector $F32X4 x y) (x64_xorps x y))
+(rule 1 (x64_xor_vector $F64X2 x y) (x64_xorpd x y))
+(rule 0 (x64_xor_vector (multi_lane _ _) x y) (x64_pxor x y))
 
 ;; Generates a register value which has an all-ones pattern.
 ;;
@@ -1821,9 +1829,8 @@
 ;; we're guaranteeed that everything is equal to itself.
 (decl vector_all_ones () Xmm)
 (rule (vector_all_ones)
-      (let ((r WritableXmm (temp_writable_xmm))
-            (_ Unit (emit (MInst.XmmConstOp (SseOpcode.Pcmpeqd) r))))
-        r))
+      (let ((tmp Xmm (xmm_uninit_value)))
+        (x64_pcmpeqd tmp tmp)))
 
 ;; Helper for creating XmmUninitializedValue instructions.
 (decl xmm_uninit_value () Xmm)
@@ -1879,19 +1886,19 @@
         dst))
 
 (rule 2 (x64_load $F32 addr _ext_kind)
-      (xmm_unary_rm_r_unaligned (SseOpcode.Movss) addr))
+      (x64_movss_load addr))
 
 (rule 2 (x64_load $F64 addr _ext_kind)
-      (xmm_unary_rm_r_unaligned (SseOpcode.Movsd) addr))
+      (x64_movsd_load addr))
 
 (rule 2 (x64_load $F32X4 addr _ext_kind)
-      (xmm_unary_rm_r_unaligned (SseOpcode.Movups) addr))
+      (x64_movups_load addr))
 
 (rule 2 (x64_load $F64X2 addr _ext_kind)
-      (xmm_unary_rm_r_unaligned (SseOpcode.Movupd) addr))
+      (x64_movupd_load addr))
 
 (rule 0 (x64_load (multi_lane _bits _lanes) addr _ext_kind)
-      (xmm_unary_rm_r_unaligned (SseOpcode.Movdqu) addr))
+      (x64_movdqu_load addr))
 
 (decl x64_mov (Amode) Reg)
 (rule (x64_mov addr)
@@ -1911,29 +1918,79 @@
             (_ Unit (emit (MInst.MovsxRmR mode src dst))))
         dst))
 
-(decl x64_movss_load (XmmMem) Xmm)
+(decl x64_movss_load (SyntheticAmode) Xmm)
 (rule (x64_movss_load from)
       (xmm_unary_rm_r_unaligned (SseOpcode.Movss) from))
+(rule 1 (x64_movss_load from)
+        (if-let $true (has_avx))
+        (xmm_unary_rm_r_vex (AvxOpcode.Vmovss) from))
 
-(decl x64_movsd_load (XmmMem) Xmm)
+(decl x64_movss_store (SyntheticAmode Xmm) SideEffectNoResult)
+(rule (x64_movss_store addr data)
+      (xmm_movrm (SseOpcode.Movss) addr data))
+(rule 1 (x64_movss_store addr data)
+        (if-let $true (has_avx))
+        (xmm_movrm_vex (AvxOpcode.Vmovss) addr data))
+
+(decl x64_movsd_load (SyntheticAmode) Xmm)
 (rule (x64_movsd_load from)
       (xmm_unary_rm_r_unaligned (SseOpcode.Movsd) from))
+(rule 1 (x64_movsd_load from)
+        (if-let $true (has_avx))
+        (xmm_unary_rm_r_vex (AvxOpcode.Vmovsd) from))
+
+(decl x64_movsd_store (SyntheticAmode Xmm) SideEffectNoResult)
+(rule (x64_movsd_store addr data)
+      (xmm_movrm (SseOpcode.Movsd) addr data))
+(rule 1 (x64_movsd_store addr data)
+        (if-let $true (has_avx))
+        (xmm_movrm_vex (AvxOpcode.Vmovsd) addr data))
 
-(decl x64_movups (XmmMem) Xmm)
-(rule (x64_movups from)
+(decl x64_movups_load (SyntheticAmode) Xmm)
+(rule (x64_movups_load from)
       (xmm_unary_rm_r_unaligned (SseOpcode.Movups) from))
+(rule 1 (x64_movups_load from)
+        (if-let $true (has_avx))
+        (xmm_unary_rm_r_vex (AvxOpcode.Vmovups) from))
+
+(decl x64_movups_store (SyntheticAmode Xmm) SideEffectNoResult)
+(rule (x64_movups_store addr data)
+      (xmm_movrm (SseOpcode.Movups) addr data))
+(rule 1 (x64_movups_store addr data)
+        (if-let $true (has_avx))
+        (xmm_movrm_vex (AvxOpcode.Vmovups) addr data))
 
-(decl x64_movupd (XmmMem) Xmm)
-(rule (x64_movupd from)
+(decl x64_movupd_load (SyntheticAmode) Xmm)
+(rule (x64_movupd_load from)
       (xmm_unary_rm_r_unaligned (SseOpcode.Movupd) from))
+(rule 1 (x64_movupd_load from)
+        (if-let $true (has_avx))
+        (xmm_unary_rm_r_vex (AvxOpcode.Vmovupd) from))
+
+(decl x64_movupd_store (SyntheticAmode Xmm) SideEffectNoResult)
+(rule (x64_movupd_store addr data)
+      (xmm_movrm (SseOpcode.Movupd) addr data))
+(rule 1 (x64_movupd_store addr data)
+        (if-let $true (has_avx))
+        (xmm_movrm_vex (AvxOpcode.Vmovupd) addr data))
 
 (decl x64_movd (Xmm) Gpr)
 (rule (x64_movd from)
       (xmm_to_gpr (SseOpcode.Movd) from (OperandSize.Size32)))
 
-(decl x64_movdqu (XmmMem) Xmm)
-(rule (x64_movdqu from)
+(decl x64_movdqu_load (XmmMem) Xmm)
+(rule (x64_movdqu_load from)
       (xmm_unary_rm_r_unaligned (SseOpcode.Movdqu) from))
+(rule 1 (x64_movdqu_load from)
+        (if-let $true (has_avx))
+        (xmm_unary_rm_r_vex (AvxOpcode.Vmovdqu) from))
+
+(decl x64_movdqu_store (SyntheticAmode Xmm) SideEffectNoResult)
+(rule (x64_movdqu_store addr data)
+      (xmm_movrm (SseOpcode.Movdqu) addr data))
+(rule 1 (x64_movdqu_store addr data)
+        (if-let $true (has_avx))
+        (xmm_movrm_vex (AvxOpcode.Vmovdqu) addr data))
 
 (decl x64_pmovsxbw (XmmMem) Xmm)
 (rule (x64_pmovsxbw from)
@@ -1982,10 +2039,14 @@
       (let ((size OperandSize (raw_operand_size_of_type ty)))
         (SideEffectNoResult.Inst (MInst.MovRM size data addr))))
 
-(decl x64_xmm_movrm (SseOpcode SyntheticAmode Xmm) SideEffectNoResult)
-(rule (x64_xmm_movrm op addr data)
+(decl xmm_movrm (SseOpcode SyntheticAmode Xmm) SideEffectNoResult)
+(rule (xmm_movrm op addr data)
       (SideEffectNoResult.Inst (MInst.XmmMovRM op data addr)))
 
+(decl xmm_movrm_vex (AvxOpcode SyntheticAmode Xmm) SideEffectNoResult)
+(rule (xmm_movrm_vex op addr data)
+      (SideEffectNoResult.Inst (MInst.XmmMovRMVex op data addr)))
+
 ;; Load a constant into an XMM register.
 (decl x64_xmm_load_const (Type VCodeConstant) Xmm)
 (rule (x64_xmm_load_const ty const)
@@ -2180,26 +2241,19 @@
       (xmm_to_reg (xmm_zero ty)))
 
 ;; Special case for `f32` zero immediates
-(rule 2 (imm ty @ $F32 (u64_zero))
-      (let ((wr WritableXmm (temp_writable_xmm))
-            (_ Unit (emit (MInst.XmmConstOp (SseOpcode.Xorps) wr))))
-        (xmm_to_reg wr)))
+(rule 2 (imm ty @ $F32 (u64_zero)) (xmm_zero ty))
 
 ;; TODO: use cmpeqps for all 1s
 
 ;; Special case for `f64` zero immediates to use `xorpd`.
-(rule 2 (imm ty @ $F64 (u64_zero))
-      (let ((wr WritableXmm (temp_writable_xmm))
-            (_ Unit (emit (MInst.XmmConstOp (SseOpcode.Xorpd) wr))))
-        (xmm_to_reg wr)))
+(rule 2 (imm ty @ $F64 (u64_zero)) (xmm_zero ty))
 
 ;; TODO: use cmpeqpd for all 1s
 
 (decl xmm_zero (Type) Xmm)
 (rule (xmm_zero ty)
-      (let ((wr WritableXmm (temp_writable_xmm))
-            (_ Unit (emit (MInst.XmmConstOp (sse_xor_op ty) wr))))
-        wr))
+      (let ((tmp Xmm (xmm_uninit_value)))
+        (x64_xor_vector ty tmp tmp)))
 
 ;; Helper for creating `MInst.ShiftR` instructions.
 (decl shift_r (Type ShiftKind Gpr Imm8Gpr) Gpr)
@@ -2729,6 +2783,38 @@
       (if-let $true (has_avx))
       (xmm_rmir_vex (AvxOpcode.Vpunpcklwd) src1 src2))
 
+;; Helper for creating `punpckldq` instructions.
+(decl x64_punpckldq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpckldq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpckldq) src1 src2))
+(rule 1 (x64_punpckldq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpckldq) src1 src2))
+
+;; Helper for creating `punpckhdq` instructions.
+(decl x64_punpckhdq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpckhdq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpckhdq) src1 src2))
+(rule 1 (x64_punpckhdq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpckhdq) src1 src2))
+
+;; Helper for creating `punpcklqdq` instructions.
+(decl x64_punpcklqdq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpcklqdq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpcklqdq) src1 src2))
+(rule 1 (x64_punpcklqdq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpcklqdq) src1 src2))
+
+;; Helper for creating `punpckhqdq` instructions.
+(decl x64_punpckhqdq (Xmm XmmMem) Xmm)
+(rule 0 (x64_punpckhqdq src1 src2)
+      (xmm_rm_r (SseOpcode.Punpckhqdq) src1 src2))
+(rule 1 (x64_punpckhqdq src1 src2)
+      (if-let $true (has_avx))
+      (xmm_rmir_vex (AvxOpcode.Vpunpckhqdq) src1 src2))
+
 ;; Helper for creating `unpcklps` instructions.
 (decl x64_unpcklps (Xmm XmmMem) Xmm)
 (rule 0 (x64_unpcklps src1 src2)
@@ -2947,10 +3033,20 @@
       (if-let $true (has_avx))
       (xmm_rmr_blend_vex (AvxOpcode.Vpblendvb) src1 src2 mask))
 
-;; Helper for creating `movsd` instructions.
-(decl x64_movsd_regmove (Xmm XmmMem) Xmm)
+;; Helper for creating a `movsd` instruction which creates a new vector
+;; register where the upper 64-bits are from the first operand and the low
+;; 64-bits are from the second operand.
+;;
+;; Note that the second argument here is specifically `Xmm` instead of `XmmMem`
+;; because there is no encoding of a 3-operand form of `movsd` and otherwise
+;; when used as a load instruction it wipes out the entire destination register
+;; which defeats the purpose of this being a 2-operand instruction.
+(decl x64_movsd_regmove (Xmm Xmm) Xmm)
 (rule (x64_movsd_regmove src1 src2)
       (xmm_rm_r_unaligned (SseOpcode.Movsd) src1 src2))
+(rule 1 (x64_movsd_regmove src1 src2)
+        (if-let $true (has_avx))
+        (xmm_rmir_vex (AvxOpcode.Vmovsd) src1 src2))
 
 ;; Helper for creating `movlhps` instructions.
 (decl x64_movlhps (Xmm XmmMem) Xmm)
@@ -3275,6 +3371,9 @@
 (decl x64_pshufd (XmmMem u8) Xmm)
 (rule (x64_pshufd src imm)
       (xmm_unary_rm_r_imm (SseOpcode.Pshufd) src imm))
+(rule 1 (x64_pshufd src imm)
+      (if-let $true (has_avx))
+      (xmm_unary_rm_r_imm_vex (AvxOpcode.Vpshufd) src imm))
 
 ;; Helper for creating `pshufb` instructions.
 (decl x64_pshufb (Xmm XmmMem) Xmm)
@@ -3284,6 +3383,22 @@
       (if-let $true (has_avx))
       (xmm_rmir_vex (AvxOpcode.Vpshufb) src1 src2))
 
+;; Helper for creating `pshuflw` instructions.
+(decl x64_pshuflw (XmmMem u8) Xmm)
+(rule (x64_pshuflw src imm)
+      (xmm_unary_rm_r_imm (SseOpcode.Pshuflw) src imm))
+(rule 1 (x64_pshuflw src imm)
+      (if-let $true (has_avx))
+      (xmm_unary_rm_r_imm_vex (AvxOpcode.Vpshuflw) src imm))
+
+;; Helper for creating `pshufhw` instructions.
+(decl x64_pshufhw (XmmMem u8) Xmm)
+(rule (x64_pshufhw src imm)
+      (xmm_unary_rm_r_imm (SseOpcode.Pshufhw) src imm))
+(rule 1 (x64_pshufhw src imm)
+      (if-let $true (has_avx))
+      (xmm_unary_rm_r_imm_vex (AvxOpcode.Vpshufhw) src imm))
+
 ;; Helper for creating `shufps` instructions.
 (decl x64_shufps (Xmm XmmMem u8) Xmm)
 (rule 0 (x64_shufps src1 src2 byte)
@@ -4502,9 +4617,11 @@
 (convert IntCC CC intcc_to_cc)
 (convert AtomicRmwOp MachAtomicRmwOp atomic_rmw_op_to_mach_atomic_rmw_op)
 
-(convert SinkableLoad RegMem sink_load)
+(convert SinkableLoad RegMem sink_load_to_reg_mem)
+(convert SinkableLoad RegMemImm sink_load_to_reg_mem_imm)
 (convert SinkableLoad GprMemImm sink_load_to_gpr_mem_imm)
 (convert SinkableLoad XmmMem sink_load_to_xmm_mem)
+(convert SinkableLoad SyntheticAmode sink_load)
 
 (decl reg_to_xmm_mem (Reg) XmmMem)
 (rule (reg_to_xmm_mem r)
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index ca6e40ce5..6ae024c01 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -1117,6 +1117,12 @@ pub enum SseOpcode {
     Xorpd,
     Phaddw,
     Phaddd,
+    Punpckhdq,
+    Punpckldq,
+    Punpckhqdq,
+    Punpcklqdq,
+    Pshuflw,
+    Pshufhw,
 }
 
 impl SseOpcode {
@@ -1256,7 +1262,13 @@ impl SseOpcode {
             | SseOpcode::Subpd
             | SseOpcode::Subsd
             | SseOpcode::Ucomisd
-            | SseOpcode::Xorpd => SSE2,
+            | SseOpcode::Xorpd
+            | SseOpcode::Punpckldq
+            | SseOpcode::Punpckhdq
+            | SseOpcode::Punpcklqdq
+            | SseOpcode::Punpckhqdq
+            | SseOpcode::Pshuflw
+            | SseOpcode::Pshufhw => SSE2,
 
             SseOpcode::Pabsb
             | SseOpcode::Pabsw
@@ -1501,6 +1513,12 @@ impl fmt::Debug for SseOpcode {
             SseOpcode::Xorpd => "xorpd",
             SseOpcode::Phaddw => "phaddw",
             SseOpcode::Phaddd => "phaddd",
+            SseOpcode::Punpckldq => "punpckldq",
+            SseOpcode::Punpckhdq => "punpckhdq",
+            SseOpcode::Punpcklqdq => "punpcklqdq",
+            SseOpcode::Punpckhqdq => "punpckhqdq",
+            SseOpcode::Pshuflw => "pshuflw",
+            SseOpcode::Pshufhw => "pshufhw",
         };
         write!(fmt, "{}", name)
     }
@@ -1669,7 +1687,19 @@ impl AvxOpcode {
             | AvxOpcode::Vcvttpd2dq
             | AvxOpcode::Vcvttps2dq
             | AvxOpcode::Vphaddw
-            | AvxOpcode::Vphaddd => {
+            | AvxOpcode::Vphaddd
+            | AvxOpcode::Vpunpckldq
+            | AvxOpcode::Vpunpckhdq
+            | AvxOpcode::Vpunpcklqdq
+            | AvxOpcode::Vpunpckhqdq
+            | AvxOpcode::Vpshuflw
+            | AvxOpcode::Vpshufhw
+            | AvxOpcode::Vpshufd
+            | AvxOpcode::Vmovss
+            | AvxOpcode::Vmovsd
+            | AvxOpcode::Vmovups
+            | AvxOpcode::Vmovupd
+            | AvxOpcode::Vmovdqu => {
                 smallvec![InstructionSet::AVX]
             }
         }
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index b92e1ecd9..9fb2c1599 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -1789,6 +1789,8 @@ pub(crate) fn emit(
                 SseOpcode::Roundpd => (LegacyPrefixes::_66, 0x0F3A09, 3),
                 SseOpcode::Roundsd => (LegacyPrefixes::_66, 0x0F3A0B, 3),
                 SseOpcode::Pshufd => (LegacyPrefixes::_66, 0x0F70, 2),
+                SseOpcode::Pshuflw => (LegacyPrefixes::_F2, 0x0F70, 2),
+                SseOpcode::Pshufhw => (LegacyPrefixes::_F3, 0x0F70, 2),
                 _ => unimplemented!("Opcode {:?} not implemented", op),
             };
             match src {
@@ -1946,6 +1948,10 @@ pub(crate) fn emit(
                 SseOpcode::Punpckhwd => (LegacyPrefixes::_66, 0x0F69, 2),
                 SseOpcode::Punpcklbw => (LegacyPrefixes::_66, 0x0F60, 2),
                 SseOpcode::Punpcklwd => (LegacyPrefixes::_66, 0x0F61, 2),
+                SseOpcode::Punpckldq => (LegacyPrefixes::_66, 0x0F62, 2),
+                SseOpcode::Punpcklqdq => (LegacyPrefixes::_66, 0x0F6C, 2),
+                SseOpcode::Punpckhdq => (LegacyPrefixes::_66, 0x0F6A, 2),
+                SseOpcode::Punpckhqdq => (LegacyPrefixes::_66, 0x0F6D, 2),
                 SseOpcode::Pxor => (LegacyPrefixes::_66, 0x0FEF, 2),
                 SseOpcode::Subps => (LegacyPrefixes::None, 0x0F5C, 2),
                 SseOpcode::Subpd => (LegacyPrefixes::_66, 0x0F5C, 2),
@@ -1970,22 +1976,6 @@ pub(crate) fn emit(
             }
         }
 
-        Inst::XmmConstOp { op, dst } => {
-            let dst = allocs.next(dst.to_reg().to_reg());
-            emit(
-                &Inst::XmmRmR {
-                    op: *op,
-                    dst: Writable::from_reg(Xmm::new(dst).unwrap()),
-                    src1: Xmm::new(dst).unwrap(),
-                    src2: Xmm::new(dst).unwrap().into(),
-                },
-                allocs,
-                sink,
-                info,
-                state,
-            );
-        }
-
         Inst::XmmRmRBlend {
             op,
             src1,
@@ -2171,6 +2161,11 @@ pub(crate) fn emit(
                 AvxOpcode::Vmaxsd => (LP::_F2, OM::_0F, 0x5F),
                 AvxOpcode::Vphaddw => (LP::_66, OM::_0F38, 0x01),
                 AvxOpcode::Vphaddd => (LP::_66, OM::_0F38, 0x02),
+                AvxOpcode::Vpunpckldq => (LP::_66, OM::_0F, 0x62),
+                AvxOpcode::Vpunpckhdq => (LP::_66, OM::_0F, 0x6A),
+                AvxOpcode::Vpunpcklqdq => (LP::_66, OM::_0F, 0x6C),
+                AvxOpcode::Vpunpckhqdq => (LP::_66, OM::_0F, 0x6D),
+                AvxOpcode::Vmovsd => (LP::_F2, OM::_0F, 0x10),
                 _ => panic!("unexpected rmir vex opcode {op:?}"),
             };
             VexInstruction::new()
@@ -2375,6 +2370,23 @@ pub(crate) fn emit(
                 AvxOpcode::Vcvtps2pd => (LegacyPrefixes::None, OpcodeMap::_0F, 0x5A),
                 AvxOpcode::Vcvttpd2dq => (LegacyPrefixes::_66, OpcodeMap::_0F, 0xE6),
                 AvxOpcode::Vcvttps2dq => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x5B),
+                AvxOpcode::Vmovdqu => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x6F),
+                AvxOpcode::Vmovups => (LegacyPrefixes::None, OpcodeMap::_0F, 0x10),
+                AvxOpcode::Vmovupd => (LegacyPrefixes::_66, OpcodeMap::_0F, 0x10),
+
+                // Note that for `vmov{s,d}` the `inst.isle` rules should
+                // statically ensure that only `Amode` operands are used here.
+                // Otherwise the other encodings of `vmovss` are more like
+                // 2-operand instructions which this unary encoding does not
+                // have.
+                AvxOpcode::Vmovss => match &src {
+                    RegisterOrAmode::Amode(_) => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x10),
+                    _ => unreachable!(),
+                },
+                AvxOpcode::Vmovsd => match &src {
+                    RegisterOrAmode::Amode(_) => (LegacyPrefixes::_F2, OpcodeMap::_0F, 0x10),
+                    _ => unreachable!(),
+                },
                 _ => panic!("unexpected rmr_imm_vex opcode {op:?}"),
             };
 
@@ -2400,6 +2412,9 @@ pub(crate) fn emit(
             let (prefix, map, opcode) = match op {
                 AvxOpcode::Vroundps => (LegacyPrefixes::_66, OpcodeMap::_0F3A, 0x08),
                 AvxOpcode::Vroundpd => (LegacyPrefixes::_66, OpcodeMap::_0F3A, 0x09),
+                AvxOpcode::Vpshuflw => (LegacyPrefixes::_F2, OpcodeMap::_0F, 0x70),
+                AvxOpcode::Vpshufhw => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x70),
+                AvxOpcode::Vpshufd => (LegacyPrefixes::_66, OpcodeMap::_0F, 0x70),
                 _ => panic!("unexpected rmr_imm_vex opcode {op:?}"),
             };
 
@@ -2414,6 +2429,28 @@ pub(crate) fn emit(
                 .encode(sink);
         }
 
+        Inst::XmmMovRMVex { op, src, dst } => {
+            let src = allocs.next(*src);
+            let dst = dst.with_allocs(allocs).finalize(state, sink);
+
+            let (prefix, map, opcode) = match op {
+                AvxOpcode::Vmovdqu => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x7F),
+                AvxOpcode::Vmovss => (LegacyPrefixes::_F3, OpcodeMap::_0F, 0x11),
+                AvxOpcode::Vmovsd => (LegacyPrefixes::_F2, OpcodeMap::_0F, 0x11),
+                AvxOpcode::Vmovups => (LegacyPrefixes::None, OpcodeMap::_0F, 0x11),
+                AvxOpcode::Vmovupd => (LegacyPrefixes::_66, OpcodeMap::_0F, 0x11),
+                _ => unimplemented!("Opcode {:?} not implemented", op),
+            };
+            VexInstruction::new()
+                .length(VexVectorLength::V128)
+                .prefix(prefix)
+                .map(map)
+                .opcode(opcode)
+                .rm(dst)
+                .reg(src.to_real_reg().unwrap().hw_enc())
+                .encode(sink);
+        }
+
         Inst::XmmRmREvex {
             op,
             src1,
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index b0002c59f..396f9fd4a 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -140,8 +140,7 @@ impl Inst {
             | Inst::XmmToGprImm { op, .. }
             | Inst::XmmUnaryRmRImm { op, .. }
             | Inst::XmmUnaryRmRUnaligned { op, .. }
-            | Inst::XmmUnaryRmR { op, .. }
-            | Inst::XmmConstOp { op, .. } => smallvec![op.available_from()],
+            | Inst::XmmUnaryRmR { op, .. } => smallvec![op.available_from()],
 
             Inst::XmmUnaryRmREvex { op, .. }
             | Inst::XmmRmREvex { op, .. }
@@ -153,7 +152,8 @@ impl Inst {
             | Inst::XmmRmRBlendVex { op, .. }
             | Inst::XmmVexPinsr { op, .. }
             | Inst::XmmUnaryRmRVex { op, .. }
-            | Inst::XmmUnaryRmRImmVex { op, .. } => op.available_from(),
+            | Inst::XmmUnaryRmRImmVex { op, .. }
+            | Inst::XmmMovRMVex { op, .. } => op.available_from(),
         }
     }
 }
@@ -938,6 +938,12 @@ impl PrettyPrint for Inst {
                 format!("{} {}, {}", ljustify(op.to_string()), src, dst)
             }
 
+            Inst::XmmMovRMVex { op, src, dst, .. } => {
+                let src = pretty_print_reg(*src, 8, allocs);
+                let dst = dst.pretty_print(8, allocs);
+                format!("{} {}, {}", ljustify(op.to_string()), src, dst)
+            }
+
             Inst::XmmRmR {
                 op,
                 src1,
@@ -964,11 +970,6 @@ impl PrettyPrint for Inst {
                 format!("{} {}, {}, {}", ljustify(op.to_string()), src1, src2, dst)
             }
 
-            Inst::XmmConstOp { op, dst } => {
-                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8, allocs);
-                format!("{} {dst}, {dst}, {dst}", ljustify(op.to_string()))
-            }
-
             Inst::XmmRmRBlend {
                 op,
                 src1,
@@ -2019,9 +2020,6 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
-        Inst::XmmConstOp { dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
-        }
         Inst::XmmUninitializedValue { dst } => collector.reg_def(dst.to_writable_reg()),
         Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => {
             collector.reg_use(rhs.to_reg());
@@ -2035,7 +2033,7 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             collector.reg_reuse_def(dst.to_writable_reg(), 0); // Reuse RHS.
             src2.get_operands(collector);
         }
-        Inst::XmmMovRM { src, dst, .. } => {
+        Inst::XmmMovRM { src, dst, .. } | Inst::XmmMovRMVex { src, dst, .. } => {
             collector.reg_use(*src);
             dst.get_operands(collector);
         }
diff --git a/cranelift/codegen/src/isa/x64/lower.isle b/cranelift/codegen/src/isa/x64/lower.isle
index ac27aae4d..56d2dc0f2 100644
--- a/cranelift/codegen/src/isa/x64/lower.isle
+++ b/cranelift/codegen/src/isa/x64/lower.isle
@@ -337,12 +337,12 @@
 ;; f32 and f64
 
 (rule 5 (lower (has_type (ty_scalar_float ty) (bxor x y)))
-      (sse_xor ty x y))
+      (x64_xor_vector ty x y))
 
 ;; SSE.
 
 (rule 6 (lower (has_type ty @ (multi_lane _bits _lanes) (bxor x y)))
-      (sse_xor ty x y))
+      (x64_xor_vector ty x y))
 
 ;; `{i,b}128`.
 
@@ -1171,12 +1171,12 @@
 ;; f32 and f64
 
 (rule -3 (lower (has_type (ty_scalar_float ty) (bnot x)))
-      (sse_xor ty x (vector_all_ones)))
+      (x64_xor_vector ty x (vector_all_ones)))
 
 ;; Special case for vector-types where bit-negation is an xor against an
 ;; all-one value
 (rule -1 (lower (has_type ty @ (multi_lane _bits _lanes) (bnot x)))
-      (sse_xor ty x (vector_all_ones)))
+      (x64_xor_vector ty x (vector_all_ones)))
 
 ;;;; Rules for `bitselect` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -1267,20 +1267,10 @@
 ;; Here the `movsd` instruction is used specifically to specialize moving
 ;; into the fist lane where unlike above cases we're not using the lane
 ;; immediate as an immediate to the instruction itself.
-;;
-;; Note, though, the `movsd` has different behavior with respect to the second
-;; lane of the f64x2 depending on whether the RegMem operand is a register or
-;; memory. When loading from a register `movsd` preserves the upper bits, but
-;; when loading from memory it zeros the upper bits. We specifically want to
-;; preserve the upper bits so if a `RegMem.Mem` is passed in we need to emit
-;; two `movsd` instructions. The first `movsd` (used as `xmm_unary_rm_r`) will
-;; load from memory into a temp register and then the second `movsd` (modeled
-;; internally as `xmm_rm_r` will merge the temp register into our `vec`
-;; register.
-(rule 1 (vec_insert_lane $F64X2 vec (RegMem.Reg val) 0)
+(rule (vec_insert_lane $F64X2 vec (RegMem.Reg val) 0)
       (x64_movsd_regmove vec val))
-(rule (vec_insert_lane $F64X2 vec mem 0)
-      (x64_movsd_regmove vec (x64_movsd_load mem)))
+(rule (vec_insert_lane $F64X2 vec (RegMem.Mem val) 0)
+      (x64_movsd_regmove vec (x64_movsd_load val)))
 
 ;; f64x2.replace_lane 1
 ;;
@@ -1288,7 +1278,7 @@
 ;; into the second lane where unlike above cases we're not using the lane
 ;; immediate as an immediate to the instruction itself.
 (rule (vec_insert_lane $F64X2 vec val 1)
-      (x64_movlhps vec (reg_mem_to_xmm_mem val)))
+      (x64_movlhps vec val))
 
 ;;;; Rules for `smin`, `smax`, `umin`, `umax` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -2557,11 +2547,11 @@
 (rule (lower (has_type $F64 (load flags address offset)))
       (x64_movsd_load (to_amode flags address offset)))
 (rule (lower (has_type $F32X4 (load flags address offset)))
-      (x64_movups (to_amode flags address offset)))
+      (x64_movups_load (to_amode flags address offset)))
 (rule (lower (has_type $F64X2 (load flags address offset)))
-      (x64_movupd (to_amode flags address offset)))
+      (x64_movupd_load (to_amode flags address offset)))
 (rule -2 (lower (has_type (ty_vec128 ty) (load flags address offset)))
-      (x64_movdqu (to_amode flags address offset)))
+      (x64_movdqu_load (to_amode flags address offset)))
 
 ;; We can load an I128 by doing two 64-bit loads.
 (rule -3 (lower (has_type $I128
@@ -2614,7 +2604,7 @@
                     address
                     offset))
       (side_effect
-       (x64_xmm_movrm (SseOpcode.Movss) (to_amode flags address offset) value)))
+       (x64_movss_store (to_amode flags address offset) value)))
 
 ;; F64 stores of values in XMM registers.
 (rule 1 (lower (store flags
@@ -2622,7 +2612,7 @@
                     address
                     offset))
       (side_effect
-       (x64_xmm_movrm (SseOpcode.Movsd) (to_amode flags address offset) value)))
+       (x64_movsd_store (to_amode flags address offset) value)))
 
 ;; Stores of F32X4 vectors.
 (rule 1 (lower (store flags
@@ -2630,7 +2620,7 @@
                     address
                     offset))
       (side_effect
-       (x64_xmm_movrm (SseOpcode.Movups) (to_amode flags address offset) value)))
+       (x64_movups_store (to_amode flags address offset) value)))
 
 ;; Stores of F64X2 vectors.
 (rule 1 (lower (store flags
@@ -2638,7 +2628,7 @@
                     address
                     offset))
       (side_effect
-       (x64_xmm_movrm (SseOpcode.Movupd) (to_amode flags address offset) value)))
+       (x64_movupd_store (to_amode flags address offset) value)))
 
 ;; Stores of all other 128-bit vector types with integer lanes.
 (rule -1 (lower (store flags
@@ -2646,7 +2636,7 @@
                     address
                     offset))
       (side_effect
-       (x64_xmm_movrm (SseOpcode.Movdqu) (to_amode flags address offset) value)))
+       (x64_movdqu_store (to_amode flags address offset) value)))
 
 ;; Stores of I128 values: store the two 64-bit halves separately.
 (rule 0 (lower (store flags
@@ -2663,6 +2653,24 @@
         (x64_movrm $I64 addr_lo value_lo)
         (x64_movrm $I64 addr_hi value_hi)))))
 
+;; Slightly optimize the extraction of the first lane from a vector which is
+;; stored in memory. In the case the first lane specifically is selected the
+;; standard `movss` and `movsd` instructions can be used as-if we're storing a
+;; f32 or f64 despite the source perhaps being an integer vector since the
+;; result of the instruction is the same.
+(rule 2 (lower (store flags
+                    (has_type (ty_32 _) (extractlane value (u8_from_uimm8 0)))
+                    address
+                    offset))
+      (side_effect
+       (x64_movss_store (to_amode flags address offset) value)))
+(rule 3 (lower (store flags
+                    (has_type (ty_64 _) (extractlane value (u8_from_uimm8 0)))
+                    address
+                    offset))
+      (side_effect
+       (x64_movsd_store (to_amode flags address offset) value)))
+
 ;; Rules for `load*` + ALU op + `store*` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Add mem, reg
@@ -2675,7 +2683,7 @@
                               src2))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_add_mem ty (to_amode flags addr offset) src2))))
 
@@ -2689,7 +2697,7 @@
                                (load flags addr offset))))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_add_mem ty (to_amode flags addr offset) src2))))
 
@@ -2703,7 +2711,7 @@
                               src2))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_sub_mem ty (to_amode flags addr offset) src2))))
 
@@ -2717,7 +2725,7 @@
                               src2))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_and_mem ty (to_amode flags addr offset) src2))))
 
@@ -2731,7 +2739,7 @@
                                (load flags addr offset))))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_and_mem ty (to_amode flags addr offset) src2))))
 
@@ -2745,7 +2753,7 @@
                               src2))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_or_mem ty (to_amode flags addr offset) src2))))
 
@@ -2759,7 +2767,7 @@
                                (load flags addr offset))))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_or_mem ty (to_amode flags addr offset) src2))))
 
@@ -2773,7 +2781,7 @@
                               src2))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_xor_mem ty (to_amode flags addr offset) src2))))
 
@@ -2787,7 +2795,7 @@
                                (load flags addr offset))))
               addr
               offset))
-      (let ((_ RegMemImm (sink_load sink)))
+      (let ((_ RegMemImm sink))
         (side_effect
          (x64_xor_mem ty (to_amode flags addr offset) src2))))
 
@@ -3529,16 +3537,98 @@
 
 ;; Rules for `shuffle` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;; Special case for the `punpckhbw` instruction which interleaves the upper
-;; lanes of the two input registers.
-(rule 4 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808)))
+;; Special case the `pshuf{l,h}w` instruction which shuffles four 16-bit
+;; integers within one value, preserving the other four 16-bit integers in that
+;; value (either the high or low half). The complicated logic is in the
+;; extractors here implemented in Rust and note that there's two cases for each
+;; instruction here to match when either the first or second shuffle operand is
+;; used.
+(rule 12 (lower (shuffle x y (pshuflw_lhs_imm imm)))
+      (x64_pshuflw x imm))
+(rule 11 (lower (shuffle x y (pshuflw_rhs_imm imm)))
+      (x64_pshuflw y imm))
+(rule 10 (lower (shuffle x y (pshufhw_lhs_imm imm)))
+      (x64_pshufhw x imm))
+(rule 9 (lower (shuffle x y (pshufhw_rhs_imm imm)))
+      (x64_pshufhw y imm))
+
+(decl pshuflw_lhs_imm (u8) Immediate)
+(extern extractor pshuflw_lhs_imm pshuflw_lhs_imm)
+(decl pshuflw_rhs_imm (u8) Immediate)
+(extern extractor pshuflw_rhs_imm pshuflw_rhs_imm)
+(decl pshufhw_lhs_imm (u8) Immediate)
+(extern extractor pshufhw_lhs_imm pshufhw_lhs_imm)
+(decl pshufhw_rhs_imm (u8) Immediate)
+(extern extractor pshufhw_rhs_imm pshufhw_rhs_imm)
+
+;; Special case for the `pshufd` instruction which will permute 32-bit values
+;; within a single register. This is only applicable if the `imm` specified
+;; selects 32-bit values from either `x` or `y`, but not both. This means
+;; there's one rule for selecting from `x` and another rule for selecting from
+;; `y`.
+(rule 8 (lower (shuffle x y (pshufd_lhs_imm imm)))
+      (x64_pshufd x imm))
+(rule 7 (lower (shuffle x y (pshufd_rhs_imm imm)))
+      (x64_pshufd y imm))
+
+(decl pshufd_lhs_imm (u8) Immediate)
+(extern extractor pshufd_lhs_imm pshufd_lhs_imm)
+(decl pshufd_rhs_imm (u8) Immediate)
+(extern extractor pshufd_rhs_imm pshufd_rhs_imm)
+
+;; Special case for i8-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f0f_1e0e_1d0d_1c0c_1b0b_1a0a_1909_1808)))
       (x64_punpckhbw a b))
-
-;; Special case for the `punpcklbw` instruction which interleaves the lower
-;; lanes of the two input registers.
-(rule 4 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000)))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1707_1606_1505_1404_1303_1202_1101_1000)))
       (x64_punpcklbw a b))
 
+;; Special case for i16-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e_0f0e_1d1c_0d0c_1b1a_0b0a_1918_0908)))
+      (x64_punpckhwd a b))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1716_0706_1514_0504_1312_0302_1110_0100)))
+      (x64_punpcklwd a b))
+
+;; Special case for i32-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c_0f0e0d0c_1b1a1918_0b0a0908)))
+      (x64_punpckhdq a b))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x17161514_07060504_13121110_03020100)))
+      (x64_punpckldq a b))
+
+;; Special case for i64-level interleaving of upper/low bytes.
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1f1e1d1c1b1a1918_0f0e0d0c0b0a0908)))
+      (x64_punpckhqdq a b))
+(rule 6 (lower (shuffle a b (u128_from_immediate 0x1716151413121110_0706050403020100)))
+      (x64_punpcklqdq a b))
+
+;; If the vector shift mask is all 0s then that means the first byte of the
+;; first operand is broadcast to all bytes. Falling through would load an
+;; all-zeros constant from a rip-relative location but it should be slightly
+;; more efficient to execute the `pshufb` here-and-now with an xor'd-to-be-zero
+;; register.
+(rule 6 (lower (shuffle a _ (u128_from_immediate 0)))
+      (x64_pshufb a (xmm_zero $I8X16)))
+
+;; Special case for the `shufps` instruction which will select two 32-bit values
+;; from the first operand and two 32-bit values from the second operand. Note
+;; that there is a second case here as well for when the operands can be
+;; swapped.
+;;
+;; Note that the priority of this instruction is currently lower than the above
+;; special cases since `shufps` handles many of them and for now it's
+;; hypothesized that the dedicated instructions are better than `shufps`.
+;; Someone with more knowledge about x86 timings should perhaps reorder the
+;; rules here eventually though.
+(rule 5 (lower (shuffle x y (shufps_imm imm)))
+      (x64_shufps x y imm))
+(rule 4 (lower (shuffle x y (shufps_rev_imm imm)))
+      (x64_shufps y x imm))
+
+(decl shufps_imm(u8) Immediate)
+(extern extractor shufps_imm shufps_imm)
+(decl shufps_rev_imm(u8) Immediate)
+(extern extractor shufps_rev_imm shufps_rev_imm)
+
+
 ;; If `lhs` and `rhs` are the same we can use a single PSHUFB to shuffle the XMM
 ;; register. We statically build `constructed_mask` to zero out any unknown lane
 ;; indices (may not be completely necessary: verification could fail incorrect
diff --git a/cranelift/codegen/src/isa/x64/lower/isle.rs b/cranelift/codegen/src/isa/x64/lower/isle.rs
index 61be54a00..815e40e35 100644
--- a/cranelift/codegen/src/isa/x64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/x64/lower/isle.rs
@@ -151,7 +151,9 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
         }
 
         if let Some(load) = self.sinkable_load(val) {
-            return self.sink_load(&load);
+            return RegMem::Mem {
+                addr: self.sink_load(&load),
+            };
         }
 
         RegMem::reg(self.put_in_reg(val))
@@ -277,12 +279,10 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
         None
     }
 
-    fn sink_load(&mut self, load: &SinkableLoad) -> RegMem {
+    fn sink_load(&mut self, load: &SinkableLoad) -> SyntheticAmode {
         self.lower_ctx.sink_inst(load.inst);
         let addr = lower_to_amode(self.lower_ctx, load.addr_input, load.offset);
-        RegMem::Mem {
-            addr: SyntheticAmode::Real(addr),
-        }
+        SyntheticAmode::Real(addr)
     }
 
     #[inline]
@@ -999,6 +999,124 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
             },
         }
     }
+
+    fn pshufd_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshufd_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        // When selecting from the right-hand-side, subtract these all by 4
+        // which will bail out if anything is less than 4. Afterwards the check
+        // is the same as `pshufd_lhs_imm` above.
+        let a = a.checked_sub(4)?;
+        let b = b.checked_sub(4)?;
+        let c = c.checked_sub(4)?;
+        let d = d.checked_sub(4)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn shufps_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // The `shufps` instruction selects the first two elements from the
+        // first vector and the second two elements from the second vector, so
+        // offset the third/fourth selectors by 4 and then make sure everything
+        // fits in 32-bits.
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        let c = c.checked_sub(4)?;
+        let d = d.checked_sub(4)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn shufps_rev_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // This is almost the same as `shufps_imm` except the elements that are
+        // subtracted are reversed. This handles the case that `shufps`
+        // instruction can be emitted if the order of the operands are swapped.
+        let (a, b, c, d) = self.shuffle32_from_imm(imm)?;
+        let a = a.checked_sub(4)?;
+        let b = b.checked_sub(4)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshuflw_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // Similar to `shufps` except this operates over 16-bit values so four
+        // of them must be fixed and the other four must be in-range to encode
+        // in the immediate.
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 && [e, f, g, h] == [4, 5, 6, 7] {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshuflw_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        let a = a.checked_sub(8)?;
+        let b = b.checked_sub(8)?;
+        let c = c.checked_sub(8)?;
+        let d = d.checked_sub(8)?;
+        let e = e.checked_sub(8)?;
+        let f = f.checked_sub(8)?;
+        let g = g.checked_sub(8)?;
+        let h = h.checked_sub(8)?;
+        if a < 4 && b < 4 && c < 4 && d < 4 && [e, f, g, h] == [4, 5, 6, 7] {
+            Some(a | (b << 2) | (c << 4) | (d << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshufhw_lhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // Similar to `pshuflw` except that the first four operands must be
+        // fixed and the second four are offset by an extra 4 and tested to
+        // make sure they're all in the range [4, 8).
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        let e = e.checked_sub(4)?;
+        let f = f.checked_sub(4)?;
+        let g = g.checked_sub(4)?;
+        let h = h.checked_sub(4)?;
+        if e < 4 && f < 4 && g < 4 && h < 4 && [a, b, c, d] == [0, 1, 2, 3] {
+            Some(e | (f << 2) | (g << 4) | (h << 6))
+        } else {
+            None
+        }
+    }
+
+    fn pshufhw_rhs_imm(&mut self, imm: Immediate) -> Option<u8> {
+        // Note that everything here is offset by at least 8 and the upper
+        // bits are offset by 12 to test they're in the range of [12, 16).
+        let (a, b, c, d, e, f, g, h) = self.shuffle16_from_imm(imm)?;
+        let a = a.checked_sub(8)?;
+        let b = b.checked_sub(8)?;
+        let c = c.checked_sub(8)?;
+        let d = d.checked_sub(8)?;
+        let e = e.checked_sub(12)?;
+        let f = f.checked_sub(12)?;
+        let g = g.checked_sub(12)?;
+        let h = h.checked_sub(12)?;
+        if e < 4 && f < 4 && g < 4 && h < 4 && [a, b, c, d] == [0, 1, 2, 3] {
+            Some(e | (f << 2) | (g << 4) | (h << 6))
+        } else {
+            None
+        }
+    }
 }
 
 impl IsleContext<'_, '_, MInst, X64Backend> {
diff --git a/cranelift/codegen/src/isle_prelude.rs b/cranelift/codegen/src/isle_prelude.rs
index 435955fa2..aa3714f14 100644
--- a/cranelift/codegen/src/isle_prelude.rs
+++ b/cranelift/codegen/src/isle_prelude.rs
@@ -412,6 +412,14 @@ macro_rules! isle_common_prelude_methods {
             }
         }
 
+        #[inline]
+        fn ty_addr64(&mut self, ty: Type) -> Option<Type> {
+            match ty {
+                I64 | R64 => Some(ty),
+                _ => None,
+            }
+        }
+
         #[inline]
         fn u64_from_imm64(&mut self, imm: Imm64) -> u64 {
             imm.bits() as u64
diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs
index 70bcb7d12..9b6adcab9 100644
--- a/cranelift/codegen/src/machinst/isle.rs
+++ b/cranelift/codegen/src/machinst/isle.rs
@@ -585,9 +585,97 @@ macro_rules! isle_lower_prelude_methods {
                 .collect();
             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
+        /// `imm`.
+        ///
+        /// For example the shuffle immediate
+        ///
+        /// `0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27`
+        ///
+        /// would return `Some((0, 2, 4, 6))`.
+        fn shuffle32_from_imm(&mut self, imm: Immediate) -> Option<(u8, u8, 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(4, &bytes[0..4])?,
+                shuffle_imm_as_le_lane_idx(4, &bytes[4..8])?,
+                shuffle_imm_as_le_lane_idx(4, &bytes[8..12])?,
+                shuffle_imm_as_le_lane_idx(4, &bytes[12..16])?,
+            ))
+        }
+
+        /// Same as `shuffle32_from_imm`, but for 16-bit lane shuffles.
+        fn shuffle16_from_imm(
+            &mut self,
+            imm: Immediate,
+        ) -> Option<(u8, u8, u8, u8, u8, u8, 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(2, &bytes[0..2])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[2..4])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[4..6])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[6..8])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[8..10])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[10..12])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[12..14])?,
+                shuffle_imm_as_le_lane_idx(2, &bytes[14..16])?,
+            ))
+        }
     };
 }
 
+/// Returns the `size`-byte lane referred to by the shuffle immediate specified
+/// in `bytes`.
+///
+/// This helper is used by `shuffleNN_from_imm` above and is used to interpret a
+/// byte-based shuffle as a higher-level shuffle of bigger lanes. This will see
+/// if the `bytes` specified, which must have `size` length, specifies a lane in
+/// vectors aligned to a `size`-byte boundary.
+///
+/// Returns `None` if `bytes` doesn't specify a `size`-byte lane aligned
+/// appropriately, or returns `Some(n)` where `n` is the index of the lane being
+/// shuffled.
+pub fn shuffle_imm_as_le_lane_idx(size: u8, bytes: &[u8]) -> Option<u8> {
+    assert_eq!(bytes.len(), usize::from(size));
+
+    // The first index in `bytes` must be aligned to a `size` boundary for the
+    // bytes to be a valid specifier for a lane of `size` bytes.
+    if bytes[0] % size != 0 {
+        return None;
+    }
+
+    // Afterwards the bytes must all be one larger than the prior to specify a
+    // contiguous sequence of bytes that's being shuffled. Basically `bytes`
+    // must refer to the entire `size`-byte lane, in little-endian order.
+    for i in 0..size - 1 {
+        let idx = usize::from(i);
+        if bytes[idx] + 1 != bytes[idx + 1] {
+            return None;
+        }
+    }
+
+    // All of the `bytes` are in-order, meaning that this is a valid shuffle
+    // immediate to specify a lane of `size` bytes. The index, when viewed as
+    // `size`-byte immediates, will be the first byte divided by the byte size.
+    Some(bytes[0] / size)
+}
+
 /// Helpers specifically for machines that use ABICaller.
 #[macro_export]
 #[doc(hidden)]
diff --git a/cranelift/codegen/src/prelude.isle b/cranelift/codegen/src/prelude.isle
index 89fc8e981..fb22e29c0 100644
--- a/cranelift/codegen/src/prelude.isle
+++ b/cranelift/codegen/src/prelude.isle
@@ -347,6 +347,10 @@
 (decl ty_vec128_int (Type) Type)
 (extern extractor ty_vec128_int ty_vec128_int)
 
+;; An extractor that only matches types that can be a 64-bit address.
+(decl ty_addr64 (Type) Type)
+(extern extractor ty_addr64 ty_addr64)
+
 ;; A pure constructor that matches everything except vectors with size 32X2.
 (decl pure partial not_vec32x2 (Type) Type)
 (extern constructor not_vec32x2 not_vec32x2)
diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle
index 51e15cb2a..a0498fae4 100644
--- a/cranelift/codegen/src/prelude_lower.isle
+++ b/cranelift/codegen/src/prelude_lower.isle
@@ -592,6 +592,18 @@
 (decl u64_from_constant (u64) Constant)
 (extern extractor u64_from_constant u64_from_constant)
 
+;; Extracts lane indices, represented as u8's, if the immediate for a
+;; `shuffle` instruction represents shuffling N-bit values. The u8 values
+;; 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)
+(extern extractor shuffle16_from_imm shuffle16_from_imm)
+
 ;;;; Helpers for generating returns ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; Extractor to check for the special case that a `WritableValueRegs`
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/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 744b923ca..527677466 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -6,9 +6,10 @@ use cranelift::codegen::data_value::DataValue;
 use cranelift::codegen::ir::immediates::Offset32;
 use cranelift::codegen::ir::instructions::InstructionFormat;
 use cranelift::codegen::ir::stackslot::StackSize;
+
 use cranelift::codegen::ir::{
-    types::*, AtomicRmwOp, Block, ExternalName, FuncRef, Function, LibCall, Opcode, Signature,
-    StackSlot, Type, UserExternalName, UserFuncName, Value,
+    types::*, AtomicRmwOp, Block, ConstantData, ExternalName, FuncRef, Function, LibCall, Opcode,
+    Signature, StackSlot, Type, UserExternalName, UserFuncName, Value,
 };
 use cranelift::codegen::isa::CallConv;
 use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Switch, Variable};
@@ -36,10 +37,10 @@ fn insert_opcode(
         vals.push(val);
     }
 
-    // For pretty much every instruction the control type is the return type
-    // except for Iconcat and Isplit which are *special* and the control type
-    // is the input type.
-    let ctrl_type = if opcode == Opcode::Iconcat || opcode == Opcode::Isplit {
+    // Some opcodes require us to look at their input arguments to determine the
+    // controlling type. This is not the general case, but we can neatly check this
+    // using `requires_typevar_operand`.
+    let ctrl_type = if opcode.constraints().requires_typevar_operand() {
         args.first()
     } else {
         rets.first()
@@ -139,26 +140,28 @@ fn insert_cmp(
     let res = if opcode == Opcode::Fcmp {
         let cc = *fgen.u.choose(FloatCC::all())?;
 
-        // Some FloatCC's are not implemented on AArch64, see:
-        // https://github.com/bytecodealliance/wasmtime/issues/4850
         // We filter out condition codes that aren't supported by the target at
         // this point after randomly choosing one, instead of randomly choosing a
         // supported one, to avoid invalidating the corpus when these get implemented.
-        if matches!(fgen.target_triple.architecture, Architecture::Aarch64(_))
-            && ![
-                FloatCC::Ordered,
-                FloatCC::Unordered,
-                FloatCC::Equal,
-                FloatCC::NotEqual,
-                FloatCC::LessThan,
-                FloatCC::LessThanOrEqual,
-                FloatCC::GreaterThan,
-                FloatCC::GreaterThanOrEqual,
-            ]
-            .contains(&cc)
-        {
-            return Err(arbitrary::Error::IncorrectFormat.into());
+        let unimplemented_cc = match (fgen.target_triple.architecture, cc) {
+            // Some FloatCC's are not implemented on AArch64, see:
+            // https://github.com/bytecodealliance/wasmtime/issues/4850
+            (Architecture::Aarch64(_), FloatCC::OrderedNotEqual) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrEqual) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrLessThan) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrLessThanOrEqual) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThan) => true,
+            (Architecture::Aarch64(_), FloatCC::UnorderedOrGreaterThanOrEqual) => true,
+
+            // These are not implemented on x86_64, for vectors.
+            (Architecture::X86_64, FloatCC::UnorderedOrEqual | FloatCC::OrderedNotEqual) => {
+                args[0].is_vector()
+            }
+            _ => false,
         };
+        if unimplemented_cc {
+            return Err(arbitrary::Error::IncorrectFormat.into());
+        }
 
         builder.ins().fcmp(cc, lhs, rhs)
     } else {
@@ -323,6 +326,65 @@ fn insert_atomic_cas(
     Ok(())
 }
 
+fn insert_shuffle(
+    fgen: &mut FunctionGenerator,
+    builder: &mut FunctionBuilder,
+    opcode: Opcode,
+    _: &'static [Type],
+    rets: &'static [Type],
+) -> Result<()> {
+    let ctrl_type = *rets.first().unwrap();
+
+    let lhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?);
+    let rhs = builder.use_var(fgen.get_variable_of_type(ctrl_type)?);
+
+    let mask = {
+        let lanes = fgen.u.arbitrary::<[u8; 16]>()?;
+        let lanes = ConstantData::from(lanes.as_ref());
+        builder.func.dfg.immediates.push(lanes)
+    };
+
+    // This function is called for any `InstructionFormat::Shuffle`. Which today is just
+    // `shuffle`, but lets assert that, just to be sure we don't accidentally insert
+    // something else.
+    assert_eq!(opcode, Opcode::Shuffle);
+    let res = builder.ins().shuffle(lhs, rhs, mask);
+
+    let target_var = fgen.get_variable_of_type(ctrl_type)?;
+    builder.def_var(target_var, res);
+
+    Ok(())
+}
+
+fn insert_ins_ext_lane(
+    fgen: &mut FunctionGenerator,
+    builder: &mut FunctionBuilder,
+    opcode: Opcode,
+    args: &'static [Type],
+    rets: &'static [Type],
+) -> Result<()> {
+    let vector_type = *args.first().unwrap();
+    let ret_type = *rets.first().unwrap();
+
+    let lhs = builder.use_var(fgen.get_variable_of_type(vector_type)?);
+    let max_lane = (vector_type.lane_count() as u8) - 1;
+    let lane = fgen.u.int_in_range(0..=max_lane)?;
+
+    let res = match opcode {
+        Opcode::Insertlane => {
+            let rhs = builder.use_var(fgen.get_variable_of_type(args[1])?);
+            builder.ins().insertlane(lhs, rhs, lane)
+        }
+        Opcode::Extractlane => builder.ins().extractlane(lhs, lane),
+        _ => todo!(),
+    };
+
+    let target_var = fgen.get_variable_of_type(ret_type)?;
+    builder.def_var(target_var, res);
+
+    Ok(())
+}
+
 type OpcodeInserter = fn(
     fgen: &mut FunctionGenerator,
     builder: &mut FunctionBuilder,
@@ -433,6 +495,8 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToUint, &[F64], &[I8]),
                 (Opcode::FcvtToUint, &[F64], &[I16]),
                 (Opcode::FcvtToUint, &[F64], &[I128]),
+                (Opcode::FcvtToUint, &[F32X4], &[I32X4]),
+                (Opcode::FcvtToUint, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4897
                 // https://github.com/bytecodealliance/wasmtime/issues/4899
                 (Opcode::FcvtToUintSat, &[F32], &[I8]),
@@ -441,6 +505,7 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToUintSat, &[F64], &[I8]),
                 (Opcode::FcvtToUintSat, &[F64], &[I16]),
                 (Opcode::FcvtToUintSat, &[F64], &[I128]),
+                (Opcode::FcvtToUintSat, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4897
                 // https://github.com/bytecodealliance/wasmtime/issues/4899
                 (Opcode::FcvtToSint, &[F32], &[I8]),
@@ -449,6 +514,8 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToSint, &[F64], &[I8]),
                 (Opcode::FcvtToSint, &[F64], &[I16]),
                 (Opcode::FcvtToSint, &[F64], &[I128]),
+                (Opcode::FcvtToSint, &[F32X4], &[I32X4]),
+                (Opcode::FcvtToSint, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4897
                 // https://github.com/bytecodealliance/wasmtime/issues/4899
                 (Opcode::FcvtToSintSat, &[F32], &[I8]),
@@ -457,12 +524,73 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtToSintSat, &[F64], &[I8]),
                 (Opcode::FcvtToSintSat, &[F64], &[I16]),
                 (Opcode::FcvtToSintSat, &[F64], &[I128]),
+                (Opcode::FcvtToSintSat, &[F64X2], &[I64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4900
                 (Opcode::FcvtFromUint, &[I128], &[F32]),
                 (Opcode::FcvtFromUint, &[I128], &[F64]),
+                // This has a lowering, but only when preceded by `uwiden_low`.
+                (Opcode::FcvtFromUint, &[I64X2], &[F64X2]),
                 // https://github.com/bytecodealliance/wasmtime/issues/4900
                 (Opcode::FcvtFromSint, &[I128], &[F32]),
                 (Opcode::FcvtFromSint, &[I128], &[F64]),
+                (Opcode::FcvtFromSint, &[I64X2], &[F64X2]),
+                (Opcode::Bmask, &[I8X16]),
+                (Opcode::Bmask, &[I16X8]),
+                (Opcode::Bmask, &[I32X4]),
+                (Opcode::Bmask, &[I64X2]),
+                (Opcode::Umulhi, &[I8X16, I8X16]),
+                (Opcode::Umulhi, &[I16X8, I16X8]),
+                (Opcode::Umulhi, &[I32X4, I32X4]),
+                (Opcode::Umulhi, &[I64X2, I64X2]),
+                (Opcode::Smulhi, &[I8X16, I8X16]),
+                (Opcode::Smulhi, &[I16X8, I16X8]),
+                (Opcode::Smulhi, &[I32X4, I32X4]),
+                (Opcode::Smulhi, &[I64X2, I64X2]),
+                (Opcode::UaddSat, &[I32X4, I32X4]),
+                (Opcode::UaddSat, &[I64X2, I64X2]),
+                (Opcode::SaddSat, &[I32X4, I32X4]),
+                (Opcode::SaddSat, &[I64X2, I64X2]),
+                (Opcode::UsubSat, &[I32X4, I32X4]),
+                (Opcode::UsubSat, &[I64X2, I64X2]),
+                (Opcode::SsubSat, &[I32X4, I32X4]),
+                (Opcode::SsubSat, &[I64X2, I64X2]),
+                (Opcode::Fcopysign, &[F32X4, F32X4]),
+                (Opcode::Fcopysign, &[F64X2, F64X2]),
+                (Opcode::Popcnt, &[I8X16]),
+                (Opcode::Popcnt, &[I16X8]),
+                (Opcode::Popcnt, &[I32X4]),
+                (Opcode::Popcnt, &[I64X2]),
+                (Opcode::Umax, &[I64X2, I64X2]),
+                (Opcode::Smax, &[I64X2, I64X2]),
+                (Opcode::Umin, &[I64X2, I64X2]),
+                (Opcode::Smin, &[I64X2, I64X2]),
+                (Opcode::Bitcast, &[I128], &[_]),
+                (Opcode::Bitcast, &[_], &[I128]),
+                (Opcode::Uunarrow),
+                (Opcode::Snarrow, &[I64X2, I64X2]),
+                (Opcode::Unarrow, &[I64X2, I64X2]),
+                (Opcode::SqmulRoundSat, &[I32X4, I32X4]),
+                // This Icmp is not implemented: #5529
+                (Opcode::Icmp, &[I64X2, I64X2]),
+                // IaddPairwise is implemented, but only for some types, and with some preceding ops.
+                (Opcode::IaddPairwise),
+                // Nothing wrong with this select. But we have an isle rule that can optimize it
+                // into a `min`/`max` instructions, which we don't have implemented yet.
+                (Opcode::Select, &[_, I128, I128]),
+                // These stack accesses can cause segfaults if they are merged into an SSE instruction.
+                // See: #5922
+                (Opcode::StackStore, &[I8X16], &[]),
+                (Opcode::StackStore, &[I16X8], &[]),
+                (Opcode::StackStore, &[I32X4], &[]),
+                (Opcode::StackStore, &[I64X2], &[]),
+                (Opcode::StackStore, &[F32X4], &[]),
+                (Opcode::StackStore, &[F64X2], &[]),
+                (Opcode::StackLoad, &[], &[I8X16]),
+                (Opcode::StackLoad, &[], &[I16X8]),
+                (Opcode::StackLoad, &[], &[I32X4]),
+                (Opcode::StackLoad, &[], &[I64X2]),
+                (Opcode::StackLoad, &[], &[F32X4]),
+                (Opcode::StackLoad, &[], &[F64X2]),
             )
         }
 
@@ -528,6 +656,24 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 // https://github.com/bytecodealliance/wasmtime/issues/4933
                 (Opcode::FcvtFromSint, &[I128], &[F32]),
                 (Opcode::FcvtFromSint, &[I128], &[F64]),
+                (Opcode::Bmask, &[I8X16]),
+                (Opcode::Bmask, &[I16X8]),
+                (Opcode::Bmask, &[I32X4]),
+                (Opcode::Bmask, &[I64X2]),
+                (Opcode::Umulhi, &[I8X16, I8X16]),
+                (Opcode::Umulhi, &[I16X8, I16X8]),
+                (Opcode::Umulhi, &[I32X4, I32X4]),
+                (Opcode::Umulhi, &[I64X2, I64X2]),
+                (Opcode::Smulhi, &[I8X16, I8X16]),
+                (Opcode::Smulhi, &[I16X8, I16X8]),
+                (Opcode::Smulhi, &[I32X4, I32X4]),
+                (Opcode::Smulhi, &[I64X2, I64X2]),
+                (Opcode::Popcnt, &[I16X8]),
+                (Opcode::Popcnt, &[I32X4]),
+                (Opcode::Popcnt, &[I64X2]),
+                // Nothing wrong with this select. But we have an isle rule that can optimize it
+                // into a `min`/`max` instructions, which we don't have implemented yet.
+                (Opcode::Select, &[I8, I128, I128]),
             )
         }
 
@@ -564,6 +710,12 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (Opcode::FcvtFromUint, &[I128], &[F64]),
                 (Opcode::FcvtFromSint, &[I128], &[F32]),
                 (Opcode::FcvtFromSint, &[I128], &[F64]),
+                (Opcode::Bmask, &[I8X16]),
+                (Opcode::Bmask, &[I16X8]),
+                (Opcode::Bmask, &[I32X4]),
+                (Opcode::Bmask, &[I64X2]),
+                (Opcode::SsubSat, &[I64X2, I64X2]),
+                (Opcode::SaddSat, &[I64X2, I64X2]),
             )
         }
 
@@ -687,12 +839,32 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::IaddCout, &[I32, I32], &[I32, I8]),
     (Opcode::IaddCout, &[I64, I64], &[I64, I8]),
     (Opcode::IaddCout, &[I128, I128], &[I128, I8]),
+    // UaddSat
+    (Opcode::UaddSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::UaddSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::UaddSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::UaddSat, &[I64X2, I64X2], &[I64X2]),
+    // SaddSat
+    (Opcode::SaddSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::SaddSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::SaddSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::SaddSat, &[I64X2, I64X2], &[I64X2]),
     // Isub
     (Opcode::Isub, &[I8, I8], &[I8]),
     (Opcode::Isub, &[I16, I16], &[I16]),
     (Opcode::Isub, &[I32, I32], &[I32]),
     (Opcode::Isub, &[I64, I64], &[I64]),
     (Opcode::Isub, &[I128, I128], &[I128]),
+    // UsubSat
+    (Opcode::UsubSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::UsubSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::UsubSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::UsubSat, &[I64X2, I64X2], &[I64X2]),
+    // SsubSat
+    (Opcode::SsubSat, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::SsubSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::SsubSat, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::SsubSat, &[I64X2, I64X2], &[I64X2]),
     // Imul
     (Opcode::Imul, &[I8, I8], &[I8]),
     (Opcode::Imul, &[I16, I16], &[I16]),
@@ -704,11 +876,19 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Smulhi, &[I16, I16], &[I16]),
     (Opcode::Smulhi, &[I32, I32], &[I32]),
     (Opcode::Smulhi, &[I64, I64], &[I64]),
+    (Opcode::Smulhi, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Smulhi, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Smulhi, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Smulhi, &[I64X2, I64X2], &[I64X2]),
     // Umulhi
     (Opcode::Umulhi, &[I8, I8], &[I8]),
     (Opcode::Umulhi, &[I16, I16], &[I16]),
     (Opcode::Umulhi, &[I32, I32], &[I32]),
     (Opcode::Umulhi, &[I64, I64], &[I64]),
+    (Opcode::Umulhi, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Umulhi, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Umulhi, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Umulhi, &[I64X2, I64X2], &[I64X2]),
     // Udiv
     (Opcode::Udiv, &[I8, I8], &[I8]),
     (Opcode::Udiv, &[I16, I16], &[I16]),
@@ -755,24 +935,40 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Smin, &[I32, I32], &[I32]),
     (Opcode::Smin, &[I64, I64], &[I64]),
     (Opcode::Smin, &[I128, I128], &[I128]),
+    (Opcode::Smin, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Smin, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Smin, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Smin, &[I64X2, I64X2], &[I64X2]),
     // Umin
     (Opcode::Umin, &[I8, I8], &[I8]),
     (Opcode::Umin, &[I16, I16], &[I16]),
     (Opcode::Umin, &[I32, I32], &[I32]),
     (Opcode::Umin, &[I64, I64], &[I64]),
     (Opcode::Umin, &[I128, I128], &[I128]),
+    (Opcode::Umin, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Umin, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Umin, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Umin, &[I64X2, I64X2], &[I64X2]),
     // Smax
     (Opcode::Smax, &[I8, I8], &[I8]),
     (Opcode::Smax, &[I16, I16], &[I16]),
     (Opcode::Smax, &[I32, I32], &[I32]),
     (Opcode::Smax, &[I64, I64], &[I64]),
     (Opcode::Smax, &[I128, I128], &[I128]),
+    (Opcode::Smax, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Smax, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Smax, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Smax, &[I64X2, I64X2], &[I64X2]),
     // Umax
     (Opcode::Umax, &[I8, I8], &[I8]),
     (Opcode::Umax, &[I16, I16], &[I16]),
     (Opcode::Umax, &[I32, I32], &[I32]),
     (Opcode::Umax, &[I64, I64], &[I64]),
     (Opcode::Umax, &[I128, I128], &[I128]),
+    (Opcode::Umax, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Umax, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Umax, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Umax, &[I64X2, I64X2], &[I64X2]),
     // Rotr
     (Opcode::Rotr, &[I8, I8], &[I8]),
     (Opcode::Rotr, &[I8, I16], &[I8]),
@@ -1026,6 +1222,10 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Popcnt, &[I32], &[I32]),
     (Opcode::Popcnt, &[I64], &[I64]),
     (Opcode::Popcnt, &[I128], &[I128]),
+    (Opcode::Popcnt, &[I8X16], &[I8X16]),
+    (Opcode::Popcnt, &[I16X8], &[I16X8]),
+    (Opcode::Popcnt, &[I32X4], &[I32X4]),
+    (Opcode::Popcnt, &[I64X2], &[I64X2]),
     // Bmask
     (Opcode::Bmask, &[I8], &[I8]),
     (Opcode::Bmask, &[I16], &[I8]),
@@ -1052,6 +1252,10 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Bmask, &[I32], &[I128]),
     (Opcode::Bmask, &[I64], &[I128]),
     (Opcode::Bmask, &[I128], &[I128]),
+    (Opcode::Bmask, &[I8X16], &[I8X16]),
+    (Opcode::Bmask, &[I16X8], &[I16X8]),
+    (Opcode::Bmask, &[I32X4], &[I32X4]),
+    (Opcode::Bmask, &[I64X2], &[I64X2]),
     // Bswap
     (Opcode::Bswap, &[I16], &[I16]),
     (Opcode::Bswap, &[I32], &[I32]),
@@ -1142,6 +1346,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     // Fcopysign
     (Opcode::Fcopysign, &[F32, F32], &[F32]),
     (Opcode::Fcopysign, &[F64, F64], &[F64]),
+    (Opcode::Fcopysign, &[F32X4, F32X4], &[F32X4]),
+    (Opcode::Fcopysign, &[F64X2, F64X2], &[F64X2]),
     // Fma
     (Opcode::Fma, &[F32, F32, F32], &[F32]),
     (Opcode::Fma, &[F64, F64, F64], &[F64]),
@@ -1192,6 +1398,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtToUintSat, &[F64], &[I32]),
     (Opcode::FcvtToUintSat, &[F64], &[I64]),
     (Opcode::FcvtToUintSat, &[F64], &[I128]),
+    (Opcode::FcvtToUintSat, &[F32X4], &[I32X4]),
+    (Opcode::FcvtToUintSat, &[F64X2], &[I64X2]),
     // FcvtToSint
     (Opcode::FcvtToSint, &[F32], &[I8]),
     (Opcode::FcvtToSint, &[F32], &[I16]),
@@ -1214,6 +1422,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtToSintSat, &[F64], &[I32]),
     (Opcode::FcvtToSintSat, &[F64], &[I64]),
     (Opcode::FcvtToSintSat, &[F64], &[I128]),
+    (Opcode::FcvtToSintSat, &[F32X4], &[I32X4]),
+    (Opcode::FcvtToSintSat, &[F64X2], &[I64X2]),
     // FcvtFromUint
     (Opcode::FcvtFromUint, &[I8], &[F32]),
     (Opcode::FcvtFromUint, &[I16], &[F32]),
@@ -1225,6 +1435,8 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtFromUint, &[I32], &[F64]),
     (Opcode::FcvtFromUint, &[I64], &[F64]),
     (Opcode::FcvtFromUint, &[I128], &[F64]),
+    (Opcode::FcvtFromUint, &[I32X4], &[F32X4]),
+    (Opcode::FcvtFromUint, &[I64X2], &[F64X2]),
     // FcvtFromSint
     (Opcode::FcvtFromSint, &[I8], &[F32]),
     (Opcode::FcvtFromSint, &[I16], &[F32]),
@@ -1236,15 +1448,29 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::FcvtFromSint, &[I32], &[F64]),
     (Opcode::FcvtFromSint, &[I64], &[F64]),
     (Opcode::FcvtFromSint, &[I128], &[F64]),
+    (Opcode::FcvtFromSint, &[I32X4], &[F32X4]),
+    (Opcode::FcvtFromSint, &[I64X2], &[F64X2]),
+    // FcvtLowFromSint
+    (Opcode::FcvtLowFromSint, &[I32X4], &[F64X2]),
+    // FvpromoteLow
+    (Opcode::FvpromoteLow, &[F32X4], &[F64X2]),
+    // Fvdemote
+    (Opcode::Fvdemote, &[F64X2], &[F32X4]),
     // Fcmp
     (Opcode::Fcmp, &[F32, F32], &[I8]),
     (Opcode::Fcmp, &[F64, F64], &[I8]),
+    (Opcode::Fcmp, &[F32X4, F32X4], &[I32X4]),
+    (Opcode::Fcmp, &[F64X2, F64X2], &[I64X2]),
     // Icmp
     (Opcode::Icmp, &[I8, I8], &[I8]),
     (Opcode::Icmp, &[I16, I16], &[I8]),
     (Opcode::Icmp, &[I32, I32], &[I8]),
     (Opcode::Icmp, &[I64, I64], &[I8]),
     (Opcode::Icmp, &[I128, I128], &[I8]),
+    (Opcode::Icmp, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::Icmp, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::Icmp, &[I32X4, I32X4], &[I32X4]),
+    (Opcode::Icmp, &[I64X2, I64X2], &[I64X2]),
     // Fence
     (Opcode::Fence, &[], &[]),
     // Stack Access
@@ -1253,11 +1479,27 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::StackStore, &[I32], &[]),
     (Opcode::StackStore, &[I64], &[]),
     (Opcode::StackStore, &[I128], &[]),
+    (Opcode::StackStore, &[F32], &[]),
+    (Opcode::StackStore, &[F64], &[]),
+    (Opcode::StackStore, &[I8X16], &[]),
+    (Opcode::StackStore, &[I16X8], &[]),
+    (Opcode::StackStore, &[I32X4], &[]),
+    (Opcode::StackStore, &[I64X2], &[]),
+    (Opcode::StackStore, &[F32X4], &[]),
+    (Opcode::StackStore, &[F64X2], &[]),
     (Opcode::StackLoad, &[], &[I8]),
     (Opcode::StackLoad, &[], &[I16]),
     (Opcode::StackLoad, &[], &[I32]),
     (Opcode::StackLoad, &[], &[I64]),
     (Opcode::StackLoad, &[], &[I128]),
+    (Opcode::StackLoad, &[], &[F32]),
+    (Opcode::StackLoad, &[], &[F64]),
+    (Opcode::StackLoad, &[], &[I8X16]),
+    (Opcode::StackLoad, &[], &[I16X8]),
+    (Opcode::StackLoad, &[], &[I32X4]),
+    (Opcode::StackLoad, &[], &[I64X2]),
+    (Opcode::StackLoad, &[], &[F32X4]),
+    (Opcode::StackLoad, &[], &[F64X2]),
     // Loads
     (Opcode::Load, &[], &[I8]),
     (Opcode::Load, &[], &[I16]),
@@ -1266,6 +1508,12 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Load, &[], &[I128]),
     (Opcode::Load, &[], &[F32]),
     (Opcode::Load, &[], &[F64]),
+    (Opcode::Load, &[], &[I8X16]),
+    (Opcode::Load, &[], &[I16X8]),
+    (Opcode::Load, &[], &[I32X4]),
+    (Opcode::Load, &[], &[I64X2]),
+    (Opcode::Load, &[], &[F32X4]),
+    (Opcode::Load, &[], &[F64X2]),
     // Special Loads
     (Opcode::Uload8, &[], &[I16]),
     (Opcode::Uload8, &[], &[I32]),
@@ -1299,6 +1547,12 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Store, &[I128], &[]),
     (Opcode::Store, &[F32], &[]),
     (Opcode::Store, &[F64], &[]),
+    (Opcode::Store, &[I8X16], &[]),
+    (Opcode::Store, &[I16X8], &[]),
+    (Opcode::Store, &[I32X4], &[]),
+    (Opcode::Store, &[I64X2], &[]),
+    (Opcode::Store, &[F32X4], &[]),
+    (Opcode::Store, &[F64X2], &[]),
     // Special Stores
     (Opcode::Istore8, &[I16], &[]),
     (Opcode::Istore8, &[I32], &[]),
@@ -1326,6 +1580,76 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     (Opcode::Bitcast, &[I32], &[F32]),
     (Opcode::Bitcast, &[F64], &[I64]),
     (Opcode::Bitcast, &[I64], &[F64]),
+    // Shuffle
+    (Opcode::Shuffle, &[I8X16, I8X16, I8X16], &[I8X16]),
+    // Swizzle
+    (Opcode::Swizzle, &[I8X16, I8X16], &[I8X16]),
+    // Splat
+    (Opcode::Splat, &[I8], &[I8X16]),
+    (Opcode::Splat, &[I16], &[I16X8]),
+    (Opcode::Splat, &[I32], &[I32X4]),
+    (Opcode::Splat, &[I64], &[I64X2]),
+    (Opcode::Splat, &[F32], &[F32X4]),
+    (Opcode::Splat, &[F64], &[F64X2]),
+    // Insert Lane
+    (Opcode::Insertlane, &[I8X16, I8], &[I8X16]),
+    (Opcode::Insertlane, &[I16X8, I16], &[I16X8]),
+    (Opcode::Insertlane, &[I32X4, I32], &[I32X4]),
+    (Opcode::Insertlane, &[I64X2, I64], &[I64X2]),
+    (Opcode::Insertlane, &[F32X4, F32], &[F32X4]),
+    (Opcode::Insertlane, &[F64X2, F64], &[F64X2]),
+    // Extract Lane
+    (Opcode::Extractlane, &[I8X16], &[I8]),
+    (Opcode::Extractlane, &[I16X8], &[I16]),
+    (Opcode::Extractlane, &[I32X4], &[I32]),
+    (Opcode::Extractlane, &[I64X2], &[I64]),
+    (Opcode::Extractlane, &[F32X4], &[F32]),
+    (Opcode::Extractlane, &[F64X2], &[F64]),
+    // Snarrow
+    (Opcode::Snarrow, &[I64X2, I64X2], &[I32X4]),
+    (Opcode::Snarrow, &[I32X4, I32X4], &[I16X8]),
+    (Opcode::Snarrow, &[I16X8, I16X8], &[I8X16]),
+    // Unarrow
+    (Opcode::Unarrow, &[I64X2, I64X2], &[I32X4]),
+    (Opcode::Unarrow, &[I32X4, I32X4], &[I16X8]),
+    (Opcode::Unarrow, &[I16X8, I16X8], &[I8X16]),
+    // Uunarrow
+    (Opcode::Uunarrow, &[I64X2, I64X2], &[I32X4]),
+    (Opcode::Uunarrow, &[I32X4, I32X4], &[I16X8]),
+    (Opcode::Uunarrow, &[I16X8, I16X8], &[I8X16]),
+    // VhighBits
+    (Opcode::VhighBits, &[I8X16], &[I8]),
+    (Opcode::VhighBits, &[I16X8], &[I8]),
+    (Opcode::VhighBits, &[I32X4], &[I8]),
+    (Opcode::VhighBits, &[I64X2], &[I8]),
+    // VanyTrue
+    (Opcode::VanyTrue, &[I8X16, I8X16, I8X16], &[I8]),
+    (Opcode::VanyTrue, &[I16X8, I16X8, I16X8], &[I8]),
+    (Opcode::VanyTrue, &[I32X4, I32X4, I32X4], &[I8]),
+    (Opcode::VanyTrue, &[I64X2, I64X2, I64X2], &[I8]),
+    // SwidenLow
+    (Opcode::SwidenLow, &[I8X16], &[I16X8]),
+    (Opcode::SwidenLow, &[I16X8], &[I32X4]),
+    (Opcode::SwidenLow, &[I32X4], &[I64X2]),
+    // SwidenHigh
+    (Opcode::SwidenHigh, &[I8X16], &[I16X8]),
+    (Opcode::SwidenHigh, &[I16X8], &[I32X4]),
+    (Opcode::SwidenHigh, &[I32X4], &[I64X2]),
+    // UwidenLow
+    (Opcode::UwidenLow, &[I8X16], &[I16X8]),
+    (Opcode::UwidenLow, &[I16X8], &[I32X4]),
+    (Opcode::UwidenLow, &[I32X4], &[I64X2]),
+    // UwidenHigh
+    (Opcode::UwidenHigh, &[I8X16], &[I16X8]),
+    (Opcode::UwidenHigh, &[I16X8], &[I32X4]),
+    (Opcode::UwidenHigh, &[I32X4], &[I64X2]),
+    // SqmulRoundSat
+    (Opcode::SqmulRoundSat, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::SqmulRoundSat, &[I32X4, I32X4], &[I32X4]),
+    // IaddPairwise
+    (Opcode::IaddPairwise, &[I8X16, I8X16], &[I8X16]),
+    (Opcode::IaddPairwise, &[I16X8, I16X8], &[I16X8]),
+    (Opcode::IaddPairwise, &[I32X4, I32X4], &[I32X4]),
     // Integer Consts
     (Opcode::Iconst, &[], &[I8]),
     (Opcode::Iconst, &[], &[I16]),
@@ -1334,6 +1658,13 @@ const OPCODE_SIGNATURES: &[OpcodeSignature] = &[
     // Float Consts
     (Opcode::F32const, &[], &[F32]),
     (Opcode::F64const, &[], &[F64]),
+    // Vector Consts
+    (Opcode::Vconst, &[], &[I8X16]),
+    (Opcode::Vconst, &[], &[I16X8]),
+    (Opcode::Vconst, &[], &[I32X4]),
+    (Opcode::Vconst, &[], &[I64X2]),
+    (Opcode::Vconst, &[], &[F32X4]),
+    (Opcode::Vconst, &[], &[F64X2]),
     // Call
     (Opcode::Call, &[], &[]),
 ];
@@ -1344,7 +1675,7 @@ fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
         InstructionFormat::AtomicRmw => insert_atomic_rmw,
         InstructionFormat::Binary => insert_opcode,
         InstructionFormat::BinaryImm64 => todo!(),
-        InstructionFormat::BinaryImm8 => todo!(),
+        InstructionFormat::BinaryImm8 => insert_ins_ext_lane,
         InstructionFormat::Call => insert_call,
         InstructionFormat::CallIndirect => insert_call,
         InstructionFormat::CondTrap => todo!(),
@@ -1358,14 +1689,14 @@ fn inserter_for_format(fmt: InstructionFormat) -> OpcodeInserter {
         InstructionFormat::Load => insert_load_store,
         InstructionFormat::LoadNoOffset => insert_load_store,
         InstructionFormat::NullAry => insert_opcode,
-        InstructionFormat::Shuffle => todo!(),
+        InstructionFormat::Shuffle => insert_shuffle,
         InstructionFormat::StackLoad => insert_stack_load,
         InstructionFormat::StackStore => insert_stack_store,
         InstructionFormat::Store => insert_load_store,
         InstructionFormat::StoreNoOffset => insert_load_store,
         InstructionFormat::TableAddr => todo!(),
         InstructionFormat::Ternary => insert_opcode,
-        InstructionFormat::TernaryImm8 => todo!(),
+        InstructionFormat::TernaryImm8 => insert_ins_ext_lane,
         InstructionFormat::Trap => todo!(),
         InstructionFormat::Unary => insert_opcode,
         InstructionFormat::UnaryConst => insert_const,
@@ -1561,6 +1892,12 @@ where
         );
         let aligned = if is_atomic && requires_aligned_atomics {
             true
+        } else if min_size > 8 {
+            // TODO: We currently can't guarantee that a stack_slot will be aligned on a 16 byte
+            // boundary. We don't have a way to specify alignment when creating stack slots, and
+            // cranelift only guarantees 8 byte alignment between stack slots.
+            // See: https://github.com/bytecodealliance/wasmtime/issues/5922#issuecomment-1457926624
+            false
         } else {
             bool::arbitrary(self.u)?
         };
diff --git a/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/crates/environ/src/address_map.rs b/crates/environ/src/address_map.rs
index 7a219543f..c55250113 100644
--- a/crates/environ/src/address_map.rs
+++ b/crates/environ/src/address_map.rs
@@ -116,6 +116,23 @@ impl AddressMapSection {
     }
 }
 
+/// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets
+/// and the slice of associated file positions for each offset.
+fn parse_address_map(
+    section: &[u8],
+) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> {
+    let mut section = Bytes(section);
+    // NB: this matches the encoding written by `append_to` above.
+    let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
+    let count = usize::try_from(count.get(LittleEndian)).ok()?;
+    let (offsets, section) =
+        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
+    let (positions, section) =
+        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
+    debug_assert!(section.is_empty());
+    Some((offsets, positions))
+}
+
 /// Lookup an `offset` within an encoded address map section, returning the
 /// original `FilePos` that corresponds to the offset, if found.
 ///
@@ -127,15 +144,7 @@ impl AddressMapSection {
 /// section of the pc that is being looked up. If `offset` is out of range or
 /// doesn't correspond to anything in this file then `None` is returned.
 pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
-    let mut section = Bytes(section);
-    // NB: this matches the encoding written by `append_to` above.
-    let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
-    let count = usize::try_from(count.get(LittleEndian)).ok()?;
-    let (offsets, section) =
-        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
-    let (positions, section) =
-        object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?;
-    debug_assert!(section.is_empty());
+    let (offsets, positions) = parse_address_map(section)?;
 
     // First perform a binary search on the `offsets` array. This is a sorted
     // array of offsets within the text section, which is conveniently what our
@@ -160,3 +169,24 @@ pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> {
     let pos = positions.get(index)?;
     Some(FilePos(pos.get(LittleEndian)))
 }
+
+/// Iterate over the address map contained in the given address map section.
+///
+/// This function takes a `section` as its first argument which must have been
+/// created with `AddressMapSection` above. This is intended to be the raw
+/// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact.
+///
+/// The yielded offsets are relative to the start of the text section for this
+/// map's code object.
+pub fn iterate_address_map<'a>(
+    section: &'a [u8],
+) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> {
+    let (offsets, positions) = parse_address_map(section)?;
+
+    Some(
+        offsets
+            .iter()
+            .map(|o| o.get(LittleEndian))
+            .zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))),
+    )
+}
diff --git a/crates/explorer/Cargo.toml b/crates/explorer/Cargo.toml
new file mode 100644
index 000000000..59eff18c9
--- /dev/null
+++ b/crates/explorer/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "wasmtime-explorer"
+authors.workspace = true
+description = "Compiler explorer for Wasmtime and Cranelift"
+documentation = "https://docs.rs/wasmtime-explorer/"
+edition.workspace = true
+license = "Apache-2.0 WITH LLVM-exception"
+repository = "https://github.com/bytecodealliance/wasmtime"
+version.workspace = true
+
+[dependencies]
+anyhow = { workspace = true }
+capstone = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+target-lexicon = { workspace = true }
+wasmprinter = { workspace = true }
+wasmtime = { workspace = true, features = ["cranelift"] }
diff --git a/crates/explorer/src/.eslintrc.yml b/crates/explorer/src/.eslintrc.yml
new file mode 100644
index 000000000..6bd1f3477
--- /dev/null
+++ b/crates/explorer/src/.eslintrc.yml
@@ -0,0 +1,8 @@
+root: true
+
+env:
+  browser: true
+  es2022: true
+
+extends:
+  - "eslint:recommended"
diff --git a/crates/explorer/src/index.css b/crates/explorer/src/index.css
new file mode 100644
index 000000000..ba23a1fbe
--- /dev/null
+++ b/crates/explorer/src/index.css
@@ -0,0 +1,26 @@
+* {
+    margin: 0;
+    padding: 0;
+}
+
+.hbox {
+    display: flex;
+    flex-direction: row;
+}
+
+html, body {
+    width: 100%;
+    height: 100%;
+}
+
+#wat {
+    width: 50%;
+    height: 100%;
+    overflow: scroll;
+}
+
+#asm {
+    width: 50%;
+    height: 100%;
+    overflow: scroll;
+}
diff --git a/crates/explorer/src/index.js b/crates/explorer/src/index.js
new file mode 100644
index 000000000..afcfac9e5
--- /dev/null
+++ b/crates/explorer/src/index.js
@@ -0,0 +1,238 @@
+/*** State *********************************************************************/
+
+class State {
+  constructor(wat, asm) {
+    this.wat = wat;
+    this.asm = asm;
+  }
+}
+
+const state = window.STATE = new State(window.WAT, window.ASM);
+
+/*** Hues for Offsets **********************************************************/
+
+const hues = [
+  80,
+  160,
+  240,
+  320,
+  40,
+  120,
+  200,
+  280,
+  20,
+  100,
+  180,
+  260,
+  340,
+  60,
+  140,
+  220,
+  300,
+];
+
+const nextHue = (function () {
+  let i = 0;
+  return () => {
+    return hues[++i % hues.length];
+  };
+}());
+
+// NB: don't just assign hues based on something simple like `hues[offset %
+// hues.length]` since that can suffer from bias due to certain alignments
+// happening more or less frequently.
+const offsetToHue = new Map();
+
+// Get the hue for the given offset, or assign it a new one if it doesn't have
+// one already.
+const hueForOffset = offset => {
+  if (offsetToHue.has(offset)) {
+    return offsetToHue.get(offset);
+  } else {
+    let hue = nextHue();
+    offsetToHue.set(offset, hue);
+    return hue;
+  }
+};
+
+// Get the hue for the given offset, only if the offset has already been
+// assigned a hue.
+const existingHueForOffset = offset => {
+  return offsetToHue.get(offset);
+};
+
+// Get WAT chunk elements by Wasm offset.
+const watByOffset = new Map();
+
+// Get asm instruction elements by Wasm offset.
+const asmByOffset = new Map();
+
+// Get all (WAT chunk or asm instruction) elements by offset.
+const anyByOffset = new Map();
+
+const addWatElem = (offset, elem) => {
+  if (!watByOffset.has(offset)) {
+    watByOffset.set(offset, []);
+  }
+  watByOffset.get(offset).push(elem);
+
+  if (!anyByOffset.has(offset)) {
+    anyByOffset.set(offset, []);
+  }
+  anyByOffset.get(offset).push(elem);
+};
+
+const addAsmElem = (offset, elem) => {
+  if (!asmByOffset.has(offset)) {
+    asmByOffset.set(offset, []);
+  }
+  asmByOffset.get(offset).push(elem);
+
+  if (!anyByOffset.has(offset)) {
+    anyByOffset.set(offset, []);
+  }
+  anyByOffset.get(offset).push(elem);
+};
+
+/*** Event Handlers ************************************************************/
+
+const watElem = document.getElementById("wat");
+watElem.addEventListener("click", event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  if (!asmByOffset.get(offset)) {
+    return;
+  }
+
+  const firstAsmElem = asmByOffset.get(offset)[0];
+  firstAsmElem.scrollIntoView({
+    behavior: "smooth",
+    block: "center",
+    inline: "nearest",
+  });
+}, { passive: true });
+
+const asmElem = document.getElementById("asm");
+asmElem.addEventListener("click", event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  if (!watByOffset.get(offset)) {
+    return;
+  }
+
+  const firstWatElem = watByOffset.get(offset)[0];
+  firstWatElem.scrollIntoView({
+    behavior: "smooth",
+    block: "center",
+    inline: "nearest",
+  });
+}, { passive: true });
+
+const onMouseEnter = event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  const hue = hueForOffset(offset);
+  for (const elem of anyByOffset.get(offset)) {
+    elem.style.backgroundColor = `hsl(${hue} 75% 80%)`;
+  }
+};
+
+const onMouseLeave = event => {
+  if (event.target.dataset.wasmOffset == null) {
+    return;
+  }
+
+  const offset = parseInt(event.target.dataset.wasmOffset);
+  const hue = hueForOffset(offset);
+  for (const elem of anyByOffset.get(offset)) {
+    elem.style.backgroundColor = `hsl(${hue} 50% 95%)`;
+  }
+};
+
+/*** Rendering *****************************************************************/
+
+const repeat = (s, n) => {
+  return s.repeat(n >= 0 ? n : 0);
+};
+
+const renderAddress = addr => {
+  let hex = addr.toString(16);
+  return repeat("0", 8 - hex.length) + hex;
+};
+
+const renderBytes = bytes => {
+  let s = "";
+  for (let i = 0; i < bytes.length; i++) {
+    if (i != 0) {
+      s += " ";
+    }
+    const hexByte = bytes[i].toString(16);
+    s += hexByte.length == 2 ? hexByte : "0" + hexByte;
+  }
+  return s + repeat(" ", 30 - s.length);
+};
+
+const renderInst = (mnemonic, operands) => {
+  if (operands.length == 0) {
+    return mnemonic;
+  } else {
+    return mnemonic + " " + operands;
+  }
+};
+
+// Render the ASM.
+
+let nthFunc = 0;
+for (const func of state.asm.functions) {
+  const funcElem = document.createElement("div");
+
+  const funcHeader = document.createElement("h3");
+  funcHeader.textContent = `Defined Function ${nthFunc}`;
+  funcElem.appendChild(funcHeader);
+
+  const bodyElem = document.createElement("pre");
+  for (const inst of func.instructions) {
+    const instElem = document.createElement("span");
+    instElem.textContent = `${renderAddress(inst.address)}    ${renderBytes(inst.bytes)}    ${renderInst(inst.mnemonic, inst.operands)}\n`;
+    if (inst.wasm_offset != null) {
+      instElem.setAttribute("data-wasm-offset", inst.wasm_offset);
+      const hue = hueForOffset(inst.wasm_offset);
+      instElem.style.backgroundColor = `hsl(${hue} 50% 90%)`;
+      instElem.addEventListener("mouseenter", onMouseEnter);
+      instElem.addEventListener("mouseleave", onMouseLeave);
+      addAsmElem(inst.wasm_offset, instElem);
+    }
+    bodyElem.appendChild(instElem);
+  }
+  funcElem.appendChild(bodyElem);
+
+  asmElem.appendChild(funcElem);
+  nthFunc++;
+}
+
+// Render the WAT.
+
+for (const chunk of state.wat.chunks) {
+  const chunkElem = document.createElement("span");
+  if (chunk.wasm_offset != null) {
+    chunkElem.dataset.wasmOffset = chunk.wasm_offset;
+    const hue = existingHueForOffset(chunk.wasm_offset);
+    if (hue) {
+      chunkElem.style.backgroundColor = `hsl(${hue} 50% 95%)`;
+      chunkElem.addEventListener("mouseenter", onMouseEnter);
+      chunkElem.addEventListener("mouseleave", onMouseLeave);
+      addWatElem(chunk.wasm_offset, chunkElem);
+    }
+  }
+  chunkElem.textContent = chunk.wat;
+  watElem.appendChild(chunkElem);
+}
diff --git a/crates/explorer/src/lib.rs b/crates/explorer/src/lib.rs
new file mode 100644
index 000000000..5dab3e847
--- /dev/null
+++ b/crates/explorer/src/lib.rs
@@ -0,0 +1,189 @@
+use anyhow::Result;
+use capstone::arch::BuildsCapstone;
+use serde::Serialize;
+use std::{io::Write, str::FromStr};
+
+pub fn generate(
+    config: &wasmtime::Config,
+    target: Option<&str>,
+    wasm: &[u8],
+    dest: &mut dyn Write,
+) -> Result<()> {
+    let target = match target {
+        None => target_lexicon::Triple::host(),
+        Some(target) => target_lexicon::Triple::from_str(target)?,
+    };
+
+    let wat = annotate_wat(wasm)?;
+    let wat_json = serde_json::to_string(&wat)?;
+    let asm = annotate_asm(config, &target, wasm)?;
+    let asm_json = serde_json::to_string(&asm)?;
+
+    let index_css = include_str!("./index.css");
+    let index_js = include_str!("./index.js");
+
+    write!(
+        dest,
+        r#"
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Wasmtime Compiler Explorer</title>
+    <style>
+      {index_css}
+    </style>
+  </head>
+  <body class="hbox">
+    <pre id="wat"></pre>
+    <div id="asm"></div>
+    <script>
+      window.WAT = {wat_json};
+      window.ASM = {asm_json};
+    </script>
+    <script>
+      {index_js}
+    </script>
+  </body>
+</html>
+        "#
+    )?;
+    Ok(())
+}
+
+#[derive(Serialize, Clone, Copy, Debug)]
+struct WasmOffset(u32);
+
+#[derive(Serialize, Debug)]
+struct AnnotatedWat {
+    chunks: Vec<AnnotatedWatChunk>,
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedWatChunk {
+    wasm_offset: Option<WasmOffset>,
+    wat: String,
+}
+
+fn annotate_wat(wasm: &[u8]) -> Result<AnnotatedWat> {
+    let mut printer = wasmprinter::Printer::new();
+    let chunks = printer
+        .offsets_and_lines(wasm)?
+        .map(|(offset, wat)| AnnotatedWatChunk {
+            wasm_offset: offset.map(|o| WasmOffset(u32::try_from(o).unwrap())),
+            wat: wat.to_string(),
+        })
+        .collect();
+    Ok(AnnotatedWat { chunks })
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedAsm {
+    functions: Vec<AnnotatedFunction>,
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedFunction {
+    instructions: Vec<AnnotatedInstruction>,
+}
+
+#[derive(Serialize, Debug)]
+struct AnnotatedInstruction {
+    wasm_offset: Option<WasmOffset>,
+    address: u32,
+    bytes: Vec<u8>,
+    mnemonic: Option<String>,
+    operands: Option<String>,
+}
+
+fn annotate_asm(
+    config: &wasmtime::Config,
+    target: &target_lexicon::Triple,
+    wasm: &[u8],
+) -> Result<AnnotatedAsm> {
+    let engine = wasmtime::Engine::new(config)?;
+    let module = wasmtime::Module::new(&engine, wasm)?;
+
+    let text = module.text();
+    let address_map: Vec<_> = module
+        .address_map()
+        .ok_or_else(|| anyhow::anyhow!("address maps must be enabled in the config"))?
+        .collect();
+
+    let mut address_map_iter = address_map.into_iter().peekable();
+    let mut current_entry = address_map_iter.next();
+    let mut wasm_offset_for_address = |start: usize, address: u32| -> Option<WasmOffset> {
+        // Consume any entries that happened before the current function for the
+        // first instruction.
+        while current_entry.map_or(false, |cur| cur.0 < start) {
+            current_entry = address_map_iter.next();
+        }
+
+        // Next advance the address map up to the current `address` specified,
+        // including it.
+        while address_map_iter.peek().map_or(false, |next_entry| {
+            u32::try_from(next_entry.0).unwrap() <= address
+        }) {
+            current_entry = address_map_iter.next();
+        }
+        current_entry.and_then(|entry| entry.1.map(WasmOffset))
+    };
+
+    let functions = module
+        .function_locations()
+        .into_iter()
+        .map(|(start, len)| {
+            let body = &text[start..][..len];
+
+            let mut cs = match target.architecture {
+                target_lexicon::Architecture::Aarch64(_) => capstone::Capstone::new()
+                    .arm64()
+                    .mode(capstone::arch::arm64::ArchMode::Arm)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                target_lexicon::Architecture::Riscv64(_) => capstone::Capstone::new()
+                    .riscv()
+                    .mode(capstone::arch::riscv::ArchMode::RiscV64)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                target_lexicon::Architecture::S390x => capstone::Capstone::new()
+                    .sysz()
+                    .mode(capstone::arch::sysz::ArchMode::Default)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                target_lexicon::Architecture::X86_64 => capstone::Capstone::new()
+                    .x86()
+                    .mode(capstone::arch::x86::ArchMode::Mode64)
+                    .build()
+                    .map_err(|e| anyhow::anyhow!("{e}"))?,
+                _ => anyhow::bail!("Unsupported target: {target}"),
+            };
+
+            // This tells capstone to skip over anything that looks like data,
+            // such as inline constant pools and things like that. This also
+            // additionally is required to skip over trapping instructions on
+            // AArch64.
+            cs.set_skipdata(true).unwrap();
+
+            let instructions = cs
+                .disasm_all(body, start as u64)
+                .map_err(|e| anyhow::anyhow!("{e}"))?;
+            let instructions = instructions
+                .iter()
+                .map(|inst| {
+                    let address = u32::try_from(inst.address()).unwrap();
+                    let wasm_offset = wasm_offset_for_address(start, address);
+                    Ok(AnnotatedInstruction {
+                        wasm_offset,
+                        address,
+                        bytes: inst.bytes().to_vec(),
+                        mnemonic: inst.mnemonic().map(ToString::to_string),
+                        operands: inst.op_str().map(ToString::to_string),
+                    })
+                })
+                .collect::<Result<Vec<_>>>()?;
+            Ok(AnnotatedFunction { instructions })
+        })
+        .collect::<Result<Vec<_>>>()?;
+
+    Ok(AnnotatedAsm { functions })
+}
diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs
index d52832195..1da66f547 100644
--- a/crates/wasmtime/src/module.rs
+++ b/crates/wasmtime/src/module.rs
@@ -1042,6 +1042,73 @@ impl Module {
         self.inner.memory_images()?;
         Ok(())
     }
+
+    /// Get the map from `.text` section offsets to Wasm binary offsets for this
+    /// module.
+    ///
+    /// Each entry is a (`.text` section offset, Wasm binary offset) pair.
+    ///
+    /// Entries are yielded in order of `.text` section offset.
+    ///
+    /// Some entries are missing a Wasm binary offset. This is for code that is
+    /// not associated with any single location in the Wasm binary, or for when
+    /// source information was optimized away.
+    ///
+    /// Not every module has an address map, since address map generation can be
+    /// turned off on `Config`.
+    ///
+    /// There is not an entry for every `.text` section offset. Every offset
+    /// after an entry's offset, but before the next entry's offset, is
+    /// considered to map to the same Wasm binary offset as the original
+    /// entry. For example, the address map will not contain the following
+    /// sequnce of entries:
+    ///
+    /// ```ignore
+    /// [
+    ///     // ...
+    ///     (10, Some(42)),
+    ///     (11, Some(42)),
+    ///     (12, Some(42)),
+    ///     (13, Some(43)),
+    ///     // ...
+    /// ]
+    /// ```
+    ///
+    /// Instead, it will drop the entries for offsets `11` and `12` since they
+    /// are the same as the entry for offset `10`:
+    ///
+    /// ```ignore
+    /// [
+    ///     // ...
+    ///     (10, Some(42)),
+    ///     (13, Some(43)),
+    ///     // ...
+    /// ]
+    /// ```
+    pub fn address_map<'a>(&'a self) -> Option<impl Iterator<Item = (usize, Option<u32>)> + 'a> {
+        Some(
+            wasmtime_environ::iterate_address_map(
+                self.code_object().code_memory().address_map_data(),
+            )?
+            .map(|(offset, file_pos)| (offset as usize, file_pos.file_offset())),
+        )
+    }
+
+    /// Get this module's code object's `.text` section, containing its compiled
+    /// executable code.
+    pub fn text(&self) -> &[u8] {
+        self.code_object().code_memory().text()
+    }
+
+    /// Get the locations of functions in this module's `.text` section.
+    ///
+    /// Each function's locartion is a (`.text` section offset, length) pair.
+    pub fn function_locations<'a>(&'a self) -> impl ExactSizeIterator<Item = (usize, usize)> + 'a {
+        self.compiled_module().finished_functions().map(|(f, _)| {
+            let loc = self.compiled_module().func_loc(f);
+            (loc.start as usize, loc.length as usize)
+        })
+    }
 }
 
 impl ModuleInner {
diff --git a/scripts/publish.rs b/scripts/publish.rs
index 964f75bb0..73a229a54 100644
--- a/scripts/publish.rs
+++ b/scripts/publish.rs
@@ -70,6 +70,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
     "wasmtime-wasi-threads",
     "wasmtime-wast",
     "wasmtime-cli-flags",
+    "wasmtime-explorer",
     "wasmtime-cli",
 ];
 
diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs
index f4b908ef0..65a4f69d3 100644
--- a/src/bin/wasmtime.rs
+++ b/src/bin/wasmtime.rs
@@ -6,7 +6,7 @@
 use anyhow::Result;
 use clap::{ErrorKind, Parser};
 use wasmtime_cli::commands::{
-    CompileCommand, ConfigCommand, RunCommand, SettingsCommand, WastCommand,
+    CompileCommand, ConfigCommand, ExploreCommand, RunCommand, SettingsCommand, WastCommand,
 };
 
 /// Wasmtime WebAssembly Runtime
@@ -35,6 +35,8 @@ enum Wasmtime {
     Config(ConfigCommand),
     /// Compiles a WebAssembly module.
     Compile(CompileCommand),
+    /// Explore the compilation of a WebAssembly module to native code.
+    Explore(ExploreCommand),
     /// Runs a WebAssembly module
     Run(RunCommand),
     /// Displays available Cranelift settings for a target.
@@ -49,6 +51,7 @@ impl Wasmtime {
         match self {
             Self::Config(c) => c.execute(),
             Self::Compile(c) => c.execute(),
+            Self::Explore(c) => c.execute(),
             Self::Run(c) => c.execute(),
             Self::Settings(c) => c.execute(),
             Self::Wast(c) => c.execute(),
diff --git a/src/commands.rs b/src/commands.rs
index be3a9624e..764f01e93 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -2,8 +2,9 @@
 
 mod compile;
 mod config;
+mod explore;
 mod run;
 mod settings;
 mod wast;
 
-pub use self::{compile::*, config::*, run::*, settings::*, wast::*};
+pub use self::{compile::*, config::*, explore::*, run::*, settings::*, wast::*};
diff --git a/src/commands/explore.rs b/src/commands/explore.rs
new file mode 100644
index 000000000..46c2bd79e
--- /dev/null
+++ b/src/commands/explore.rs
@@ -0,0 +1,51 @@
+//! The module that implements the `wasmtime explore` command.
+
+use anyhow::{Context, Result};
+use clap::Parser;
+use std::path::PathBuf;
+use wasmtime_cli_flags::CommonOptions;
+
+/// Explore the compilation of a WebAssembly module to native code.
+#[derive(Parser)]
+#[clap(name = "explore")]
+pub struct ExploreCommand {
+    #[clap(flatten)]
+    common: CommonOptions,
+
+    /// The target triple; default is the host triple
+    #[clap(long, value_name = "TARGET")]
+    target: Option<String>,
+
+    /// The path of the WebAssembly module to compile
+    #[clap(required = true, value_name = "MODULE")]
+    module: PathBuf,
+
+    /// The path of the explorer output (derived from the MODULE name if none
+    /// provided)
+    #[clap(short, long)]
+    output: Option<PathBuf>,
+}
+
+impl ExploreCommand {
+    /// Executes the command.
+    pub fn execute(&self) -> Result<()> {
+        self.common.init_logging();
+
+        let config = self.common.config(self.target.as_deref())?;
+
+        let wasm = std::fs::read(&self.module)
+            .with_context(|| format!("failed to read Wasm module: {}", self.module.display()))?;
+
+        let output = self
+            .output
+            .clone()
+            .unwrap_or_else(|| self.module.with_extension("explore.html"));
+        let output_file = std::fs::File::create(&output)
+            .with_context(|| format!("failed to create file: {}", output.display()))?;
+        let mut output_file = std::io::BufWriter::new(output_file);
+
+        wasmtime_explorer::generate(&config, self.target.as_deref(), &wasm, &mut output_file)?;
+        println!("Exploration written to {}", output.display());
+        Ok(())
+    }
+}
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 32a1210ab..17ebd39fe 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1,6 +1,27 @@
 
 # cargo-vet audits file
 
+[[wildcard-audits.bumpalo]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+user-id = 696
+start = "2019-03-16"
+end = "2024-03-10"
+
+[[wildcard-audits.wasm-mutate]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+user-id = 696
+start = "2022-02-17"
+end = "2024-03-10"
+
+[[wildcard-audits.wasmprinter]]
+who = "Nick Fitzgerald <fitzgen@gmail.com>"
+criteria = "safe-to-deploy"
+user-id = 696
+start = "2021-04-28"
+end = "2024-03-10"
+
 [[audits.addr2line]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 4f333db00..c4bc9af4d 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1,6 +1,20 @@
 
 # cargo-vet imports lock
 
+[[publisher.wasm-mutate]]
+version = "0.2.22"
+when = "2023-03-10"
+user-id = 696
+user-login = "fitzgen"
+user-name = "Nick Fitzgerald"
+
+[[publisher.wasmprinter]]
+version = "0.2.54"
+when = "2023-03-10"
+user-id = 696
+user-login = "fitzgen"
+user-name = "Nick Fitzgerald"
+
 [audits.chromeos.criteria.crypto-safe]
 description = """
 All crypto algorithms in this crate have been reviewed by a relevant expert.


Test patch:
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
index 0670a3fae..e06221836 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
@@ -2657,6 +2657,7 @@ fn test_aarch64_binemit() {
             rd: writable_vreg(14),
             rn: vreg(19),
             size: VectorSize::Size32x4,
+            lane: 0,
         },
         "6E06044E",
         "dup v14.4s, v19.s[0]",
@@ -2666,6 +2667,7 @@ fn test_aarch64_binemit() {
             rd: writable_vreg(18),
             rn: vreg(10),
             size: VectorSize::Size64x2,
+            lane: 0,
         },
         "5205084E",
         "dup v18.2d, v10.d[0]",
diff --git a/cranelift/filetests/filetests/isa/aarch64/issue-5985.clif b/cranelift/filetests/filetests/isa/aarch64/issue-5985.clif
new file mode 100644
index 000000000..7c14c4774
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/aarch64/issue-5985.clif
@@ -0,0 +1,20 @@
+test compile precise-output
+target aarch64
+
+function %a() -> f32x4 system_v {
+block0:
+    v16 = f32const 0x1.fffe00p-126
+    v25 = splat.f32x4 v16
+    return v25
+}
+
+; VCode:
+; block0:
+;   movi v0.2d, #72056494543077120
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   movi v0.2d, #0xffff0000ffff00
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/aarch64/shuffle.clif b/cranelift/filetests/filetests/isa/aarch64/shuffle.clif
new file mode 100644
index 000000000..c52959b88
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/aarch64/shuffle.clif
@@ -0,0 +1,747 @@
+test compile precise-output
+set unwind_info=false
+target aarch64
+
+function %shuffle_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [3 0 31 26 4 6 12 11 23 13 24 4 2 15 17 5]
+    return v2
+}
+
+; VCode:
+; block0:
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, pc+8 ; b 20 ; data.f128 0x05110f0204180d170b0c06041a1f0003
+;   tbl v0.16b, { v30.16b, v31.16b }, v3.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, #0x10
+;   b #0x20
+;   adc w3, w0, wzr
+;   add w4, w16, w12, lsl #1
+;   orr z23.b, p3/m, z23.b, z8.b
+;   mov z2.b, p1/z, #0x78
+;   tbl v0.16b, {v30.16b, v31.16b}, v3.16b
+;   ret
+
+function %aarch64_uzp1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30]
+    return v2
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_uzp2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_uzp1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 4 5 8 9 12 13 16 17 20 21 24 25 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_uzp2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 6 7 10 11 14 15 18 19 22 23 26 27 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_uzp1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_uzp2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 12 13 14 15 20 21 22 23 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_uzp1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_uzp2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %punpcklbw(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 16 1 17 2 18 3 19 4 20 5 21 6 22 7 23]
+    return v2
+}
+
+; VCode:
+; block0:
+;   zip1 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip1 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %punpckhbw(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [8 24 9 25 10 26 11 27 12 28 13 29 14 30 15 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   zip2 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip2 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %punpcklwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 2 3 18 19 4 5 20 21 6 7 22 23]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip1 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip1 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %punpckhwd(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 24 25 10 11 26 27 12 13 28 29 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip2 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip2 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %punpckldq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 4 5 6 7 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip1 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip1 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %punpckhdq(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 24 25 26 27 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   zip2 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   zip2 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %punpcklqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %punpckhqdq(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_trn1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 16 2 18 4 20 6 22 8 24 10 26 12 28 14 30]
+    return v2
+}
+
+; VCode:
+; block0:
+;   trn1 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn1 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_trn2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 17 3 19 5 21 7 23 9 25 11 27 13 29 15 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   trn2 v0.16b, v0.16b, v1.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn2 v0.16b, v0.16b, v1.16b
+;   ret
+
+function %aarch64_trn1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 4 5 20 21 8 9 24 25 12 13 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn1 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn1 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_trn2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 18 19 6 7 22 23 10 11 26 27 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn2 v0.8h, v0.8h, v1.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn2 v0.8h, v0.8h, v1.8h
+;   ret
+
+function %aarch64_trn1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 8 9 10 11 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn1 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn1 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_trn2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 20 21 22 23 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   trn2 v0.4s, v0.4s, v1.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   trn2 v0.4s, v0.4s, v1.4s
+;   ret
+
+function %aarch64_trn1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp1 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_trn2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   uzp2 v0.2d, v0.2d, v1.2d
+;   ret
+
+function %aarch64_ext_0(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #0
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #0
+;   ret
+
+function %aarch64_ext_1(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #1
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #1
+;   ret
+
+function %aarch64_ext_5(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #5
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #5
+;   ret
+
+function %aarch64_ext_11(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]
+    return v2
+}
+
+; VCode:
+; block0:
+;   ext v0.16b, v0.16b, v1.16b, #11
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   ext v0.16b, v0.16b, v1.16b, #0xb
+;   ret
+
+function %aarch64_ext_16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31]
+    return v2
+}
+
+; VCode:
+; block0:
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, pc+8 ; b 20 ; data.f128 0x1f1e1d1c1b1a19181716151413121110
+;   tbl v0.16b, { v30.16b, v31.16b }, v3.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   mov v30.16b, v0.16b
+;   mov v31.16b, v1.16b
+;   ldr q3, #0x10
+;   b #0x20
+;   sbfiz w16, w8, #0xe, #5
+;   b #0xfffffffffc585464
+;   madd w24, w8, w26, w6
+;   fmadd s28, s8, s30, s7
+;   tbl v0.16b, {v30.16b, v31.16b}, v3.16b
+;   ret
+
+function %aarch64_dup_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5]
+    return v2
+}
+
+; VCode:
+; block0:
+;   dup v0.16b, v0.b[5]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.16b, v0.b[5]
+;   ret
+
+function %aarch64_dup_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 12 13 12 13 12 13 12 13 12 13 12 13 12 13]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   dup v0.8h, v0.h[6]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.8h, v0.h[6]
+;   ret
+
+function %aarch64_dup_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 14 15 12 13 14 15 12 13 14 15 12 13 14 15]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   dup v0.4s, v0.s[3]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.4s, v0.s[3]
+;   ret
+
+function %aarch64_dup_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   dup v0.2d, v0.d[0]
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   dup v0.2d, v0.d[0]
+;   ret
+
+function %aarch64_rev16(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [1 0 3 2 5 4 7 6 9 8 11 10 13 12 15 14]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev16 v0.16b, v0.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev16 v0.16b, v0.16b
+;   ret
+
+function %aarch64_rev32_bytes(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev32 v0.16b, v0.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev32 v0.16b, v0.16b
+;   ret
+
+function %aarch64_rev32_words(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 0 1 6 7 4 5 10 11 8 9 14 15 12 13]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev32 v0.8h, v0.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev32 v0.8h, v0.8h
+;   ret
+
+function %aarch64_rev64_bytes(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev64 v0.16b, v0.16b
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev64 v0.16b, v0.16b
+;   ret
+
+function %aarch64_rev64_words(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev64 v0.8h, v0.8h
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev64 v0.8h, v0.8h
+;   ret
+
+function %aarch64_rev64_doublewords(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 0 1 2 3 12 13 14 15 8 9 10 11]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+
+; VCode:
+; block0:
+;   rev64 v0.4s, v0.4s
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   rev64 v0.4s, v0.4s
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/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/isa/x64/extractlane.clif b/cranelift/filetests/filetests/isa/x64/extractlane.clif
index 1cbdfbf7d..bf6ce4cc9 100644
--- a/cranelift/filetests/filetests/isa/x64/extractlane.clif
+++ b/cranelift/filetests/filetests/isa/x64/extractlane.clif
@@ -151,3 +151,107 @@ block0(v0: f64x2):
 ;   popq %rbp
 ;   retq
 
+function %extract_i32x4_lane0_to_memory(i32x4, i64) {
+block0(v0: i32x4, v1: i64):
+  v2 = extractlane v0, 0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movss   %xmm0, 0(%rdi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movss %xmm0, (%rdi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %extract_f32x4_lane0_to_memory(f32x4, i64) {
+block0(v0: f32x4, v1: i64):
+  v2 = extractlane v0, 0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movss   %xmm0, 0(%rdi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movss %xmm0, (%rdi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %extract_i64x2_lane0_to_memory(i64x2, i64) {
+block0(v0: i64x2, v1: i64):
+  v2 = extractlane v0, 0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movsd   %xmm0, 0(%rdi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movsd %xmm0, (%rdi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %extract_f64x2_lane0_to_memory(f64x2, i64) {
+block0(v0: f64x2, v1: i64):
+  v2 = extractlane v0, 0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movsd   %xmm0, 0(%rdi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movsd %xmm0, (%rdi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/fabs.clif b/cranelift/filetests/filetests/isa/x64/fabs.clif
index 574d34cc1..c915d9c59 100644
--- a/cranelift/filetests/filetests/isa/x64/fabs.clif
+++ b/cranelift/filetests/filetests/isa/x64/fabs.clif
@@ -69,9 +69,10 @@ block0(v0: f32x4):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pcmpeqd %xmm3, %xmm3, %xmm3
-;   psrld   %xmm3, $1, %xmm3
-;   andps   %xmm0, %xmm3, %xmm0
+;   uninit  %xmm4
+;   pcmpeqd %xmm4, %xmm4, %xmm4
+;   psrld   %xmm4, $1, %xmm4
+;   andps   %xmm0, %xmm4, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -81,9 +82,9 @@ block0(v0: f32x4):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pcmpeqd %xmm3, %xmm3
-;   psrld $1, %xmm3
-;   andps %xmm3, %xmm0
+;   pcmpeqd %xmm4, %xmm4
+;   psrld $1, %xmm4
+;   andps %xmm4, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -98,9 +99,10 @@ block0(v0: f64x2):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pcmpeqd %xmm3, %xmm3, %xmm3
-;   psrlq   %xmm3, $1, %xmm3
-;   andpd   %xmm0, %xmm3, %xmm0
+;   uninit  %xmm4
+;   pcmpeqd %xmm4, %xmm4, %xmm4
+;   psrlq   %xmm4, $1, %xmm4
+;   andpd   %xmm0, %xmm4, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -110,9 +112,9 @@ block0(v0: f64x2):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pcmpeqd %xmm3, %xmm3
-;   psrlq $1, %xmm3
-;   andpd %xmm3, %xmm0
+;   pcmpeqd %xmm4, %xmm4
+;   psrlq $1, %xmm4
+;   andpd %xmm4, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
diff --git a/cranelift/filetests/filetests/isa/x64/fcvt.clif b/cranelift/filetests/filetests/isa/x64/fcvt.clif
index aee96700a..1a4ab0cc1 100644
--- a/cranelift/filetests/filetests/isa/x64/fcvt.clif
+++ b/cranelift/filetests/filetests/isa/x64/fcvt.clif
@@ -1032,20 +1032,22 @@ block0(v0: f32x4):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   xorps   %xmm5, %xmm5, %xmm5
-;   movdqa  %xmm0, %xmm9
-;   maxps   %xmm9, %xmm5, %xmm9
-;   pcmpeqd %xmm5, %xmm5, %xmm5
-;   psrld   %xmm5, $1, %xmm5
-;   cvtdq2ps %xmm5, %xmm13
-;   cvttps2dq %xmm9, %xmm12
-;   subps   %xmm9, %xmm13, %xmm9
-;   cmpps   $2, %xmm13, %xmm9, %xmm13
-;   cvttps2dq %xmm9, %xmm0
-;   pxor    %xmm0, %xmm13, %xmm0
-;   pxor    %xmm6, %xmm6, %xmm6
-;   pmaxsd  %xmm0, %xmm6, %xmm0
-;   paddd   %xmm0, %xmm12, %xmm0
+;   uninit  %xmm6
+;   xorps   %xmm6, %xmm6, %xmm6
+;   movdqa  %xmm0, %xmm11
+;   maxps   %xmm11, %xmm6, %xmm11
+;   pcmpeqd %xmm6, %xmm6, %xmm6
+;   psrld   %xmm6, $1, %xmm6
+;   cvtdq2ps %xmm6, %xmm15
+;   cvttps2dq %xmm11, %xmm14
+;   subps   %xmm11, %xmm15, %xmm11
+;   cmpps   $2, %xmm15, %xmm11, %xmm15
+;   cvttps2dq %xmm11, %xmm0
+;   pxor    %xmm0, %xmm15, %xmm0
+;   uninit  %xmm9
+;   pxor    %xmm9, %xmm9, %xmm9
+;   pmaxsd  %xmm0, %xmm9, %xmm0
+;   paddd   %xmm0, %xmm14, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -1055,20 +1057,20 @@ block0(v0: f32x4):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   xorps %xmm5, %xmm5
-;   movdqa %xmm0, %xmm9
-;   maxps %xmm5, %xmm9
-;   pcmpeqd %xmm5, %xmm5
-;   psrld $1, %xmm5
-;   cvtdq2ps %xmm5, %xmm13
-;   cvttps2dq %xmm9, %xmm12
-;   subps %xmm13, %xmm9
-;   cmpleps %xmm9, %xmm13
-;   cvttps2dq %xmm9, %xmm0
-;   pxor %xmm13, %xmm0
-;   pxor %xmm6, %xmm6
-;   pmaxsd %xmm6, %xmm0
-;   paddd %xmm12, %xmm0
+;   xorps %xmm6, %xmm6
+;   movdqa %xmm0, %xmm11
+;   maxps %xmm6, %xmm11
+;   pcmpeqd %xmm6, %xmm6
+;   psrld $1, %xmm6
+;   cvtdq2ps %xmm6, %xmm15
+;   cvttps2dq %xmm11, %xmm14
+;   subps %xmm15, %xmm11
+;   cmpleps %xmm11, %xmm15
+;   cvttps2dq %xmm11, %xmm0
+;   pxor %xmm15, %xmm0
+;   pxor %xmm9, %xmm9
+;   pmaxsd %xmm9, %xmm0
+;   paddd %xmm14, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
diff --git a/cranelift/filetests/filetests/isa/x64/float-avx.clif b/cranelift/filetests/filetests/isa/x64/float-avx.clif
index 6776bb529..4e29340a3 100644
--- a/cranelift/filetests/filetests/isa/x64/float-avx.clif
+++ b/cranelift/filetests/filetests/isa/x64/float-avx.clif
@@ -589,3 +589,59 @@ block0(v0: f64x2):
 ;   addb %al, (%rax)
 ;   sarb $0xff, %bh
 
+function %load_and_store_f32(i64, i64) {
+block0(v0: i64, v1: i64):
+  v2 = load.f32 v0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovss  0(%rdi), %xmm3
+;   vmovss  %xmm3, 0(%rsi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovss (%rdi), %xmm3 ; trap: heap_oob
+;   vmovss %xmm3, (%rsi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %load_and_store_f64(i64, i64) {
+block0(v0: i64, v1: i64):
+  v2 = load.f64 v0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovsd  0(%rdi), %xmm3
+;   vmovsd  %xmm3, 0(%rsi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovsd (%rdi), %xmm3 ; trap: heap_oob
+;   vmovsd %xmm3, (%rsi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/fneg.clif b/cranelift/filetests/filetests/isa/x64/fneg.clif
index d990c31c1..4d0b2535e 100644
--- a/cranelift/filetests/filetests/isa/x64/fneg.clif
+++ b/cranelift/filetests/filetests/isa/x64/fneg.clif
@@ -69,9 +69,10 @@ block0(v0: f32x4):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pcmpeqd %xmm3, %xmm3, %xmm3
-;   pslld   %xmm3, $31, %xmm3
-;   xorps   %xmm0, %xmm3, %xmm0
+;   uninit  %xmm4
+;   pcmpeqd %xmm4, %xmm4, %xmm4
+;   pslld   %xmm4, $31, %xmm4
+;   xorps   %xmm0, %xmm4, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -81,9 +82,9 @@ block0(v0: f32x4):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pcmpeqd %xmm3, %xmm3
-;   pslld $0x1f, %xmm3
-;   xorps %xmm3, %xmm0
+;   pcmpeqd %xmm4, %xmm4
+;   pslld $0x1f, %xmm4
+;   xorps %xmm4, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -98,9 +99,10 @@ block0(v0: f64x2):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pcmpeqd %xmm3, %xmm3, %xmm3
-;   psllq   %xmm3, $63, %xmm3
-;   xorpd   %xmm0, %xmm3, %xmm0
+;   uninit  %xmm4
+;   pcmpeqd %xmm4, %xmm4, %xmm4
+;   psllq   %xmm4, $63, %xmm4
+;   xorpd   %xmm0, %xmm4, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -110,9 +112,9 @@ block0(v0: f64x2):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pcmpeqd %xmm3, %xmm3
-;   psllq $0x3f, %xmm3
-;   xorpd %xmm3, %xmm0
+;   pcmpeqd %xmm4, %xmm4
+;   psllq $0x3f, %xmm4
+;   xorpd %xmm4, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
diff --git a/cranelift/filetests/filetests/isa/x64/insertlane.clif b/cranelift/filetests/filetests/isa/x64/insertlane.clif
new file mode 100644
index 000000000..7f2569d02
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/insertlane.clif
@@ -0,0 +1,82 @@
+test compile precise-output
+set enable_simd
+target x86_64 has_avx
+
+function %insertlane_f64x2_zero(f64x2, f64) -> f64x2 {
+block0(v0: f64x2, v1: f64):
+  v2 = insertlane v0, v1, 0
+  return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovsd  %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovsd %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %insertlane_f64x2_one(f64x2, f64) -> f64x2 {
+block0(v0: f64x2, v1: f64):
+  v2 = insertlane v0, v1, 1
+  return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovlhps %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovlhps %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %insertlane_f64x2_zero_with_load(f64x2, i64) -> f64x2 {
+block0(v0: f64x2, v1: i64):
+  v2 = load.f64 v1
+  v3 = insertlane v0, v2, 0
+  return v3
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovsd  0(%rdi), %xmm3
+;   vmovsd  %xmm0, %xmm3, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovsd (%rdi), %xmm3 ; trap: heap_oob
+;   vmovsd %xmm3, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/shuffle-avx.clif b/cranelift/filetests/filetests/isa/x64/shuffle-avx.clif
new file mode 100644
index 000000000..30cf9721e
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/shuffle-avx.clif
@@ -0,0 +1,116 @@
+test compile precise-output
+set enable_simd
+target x86_64 has_avx
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpckldq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpckldq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpckhdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpckhdq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpcklqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpcklqdq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vpunpckhqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vpunpckhqdq %xmm1, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/shuffle.clif b/cranelift/filetests/filetests/isa/x64/shuffle.clif
index 529b95cc5..ce1d1b484 100644
--- a/cranelift/filetests/filetests/isa/x64/shuffle.clif
+++ b/cranelift/filetests/filetests/isa/x64/shuffle.clif
@@ -52,3 +52,595 @@ block0(v0: i8x16, v1: i8x16):
 ;   popq %rbp
 ;   retq
 
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpcklwd %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpcklwd %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckhwd %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckhwd %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufd_0022(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 0 1 2 3 8 9 10 11 8 9 10 11]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufd  $160, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufd $0xa0, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufd_3120(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 4 5 6 7 8 9 10 11 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufd  $39, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufd $0x27, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufd_7546(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [28 29 30 31 20 21 22 23 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufd  $135, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufd $0x87, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %not_single_pshufd(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 12 13 14 15 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   shufps  $78, %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   shufps $0x4e, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckldq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckldq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckhdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckhdq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpcklqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpcklqdq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+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:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   punpckhqdq %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   punpckhqdq %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %shufps_3277(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 8 9 10 11 28 29 30 31 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   shufps  $251, %xmm0, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   shufps $0xfb, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %shufps_6500(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [24 25 26 27 20 21 22 23 0 1 2 3 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movdqa  %xmm0, %xmm4
+;   movdqa  %xmm1, %xmm0
+;   shufps  $6, %xmm0, %xmm4, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movdqa %xmm0, %xmm4
+;   movdqa %xmm1, %xmm0
+;   shufps $6, %xmm4, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $27, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0x1b, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 6 7 4 5 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $187, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0xbb, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 20 21 18 19 16 17 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $27, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0x1b, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshuflw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 18 19 22 23 18 19 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshuflw $119, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshuflw $0x77, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $27, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x1b, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 10 11 14 15 10 11]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $119, %xmm0, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x77, %xmm0, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 28 29 26 27 24 25]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $27, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x1b, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %pshufhw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 26 27 30 31 26 27]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   pshufhw $119, %xmm1, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pshufhw $0x77, %xmm1, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %shuffle_all_zeros(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
+    return v2
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   uninit  %xmm4
+;   pxor    %xmm4, %xmm4, %xmm4
+;   pshufb  %xmm0, %xmm4, %xmm0
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   pxor %xmm4, %xmm4
+;   pshufb %xmm4, %xmm0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif
index fdd58032e..87c1b7710 100644
--- a/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif
+++ b/cranelift/filetests/filetests/isa/x64/simd-arith-avx.clif
@@ -610,8 +610,8 @@ block0(v0: i32x4, v1: i32x4):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pshufd  $250, %xmm0, %xmm3
-;   pshufd  $250, %xmm1, %xmm5
+;   vpshufd $250, %xmm0, %xmm3
+;   vpshufd $250, %xmm1, %xmm5
 ;   vpmuldq %xmm3, %xmm5, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
@@ -622,8 +622,8 @@ block0(v0: i32x4, v1: i32x4):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pshufd $0xfa, %xmm0, %xmm3
-;   pshufd $0xfa, %xmm1, %xmm5
+;   vpshufd $0xfa, %xmm0, %xmm3
+;   vpshufd $0xfa, %xmm1, %xmm5
 ;   vpmuldq %xmm5, %xmm3, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
@@ -641,8 +641,8 @@ block0(v0: i32x4, v1: i32x4):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pshufd  $80, %xmm0, %xmm3
-;   pshufd  $80, %xmm1, %xmm5
+;   vpshufd $80, %xmm0, %xmm3
+;   vpshufd $80, %xmm1, %xmm5
 ;   vpmuludq %xmm3, %xmm5, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
@@ -653,8 +653,8 @@ block0(v0: i32x4, v1: i32x4):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pshufd $0x50, %xmm0, %xmm3
-;   pshufd $0x50, %xmm1, %xmm5
+;   vpshufd $0x50, %xmm0, %xmm3
+;   vpshufd $0x50, %xmm1, %xmm5
 ;   vpmuludq %xmm5, %xmm3, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
@@ -1233,7 +1233,7 @@ block0(v0: i8x16):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   movdqu  const(0), %xmm2
+;   vmovdqu const(0), %xmm2
 ;   vpmaddubsw %xmm2, %xmm0, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
@@ -1244,7 +1244,7 @@ block0(v0: i8x16):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movdqu 0x14(%rip), %xmm2
+;   vmovdqu 0x14(%rip), %xmm2
 ;   vpmaddubsw %xmm0, %xmm2, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
@@ -1317,8 +1317,9 @@ block0(v0: i8):
 ; block0:
 ;   uninit  %xmm2
 ;   vpinsrb $0 %xmm2, %rdi, %xmm4
-;   pxor    %xmm6, %xmm6, %xmm6
-;   vpshufb %xmm4, %xmm6, %xmm0
+;   uninit  %xmm6
+;   vpxor   %xmm6, %xmm6, %xmm8
+;   vpshufb %xmm4, %xmm8, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -1329,8 +1330,8 @@ block0(v0: i8):
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
 ;   vpinsrb $0, %edi, %xmm2, %xmm4
-;   pxor %xmm6, %xmm6
-;   vpshufb %xmm6, %xmm4, %xmm0
+;   vpxor %xmm6, %xmm6, %xmm8
+;   vpshufb %xmm8, %xmm4, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -1347,12 +1348,13 @@ block0(v0: f64x2):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   xorpd   %xmm2, %xmm2, %xmm2
-;   vmaxpd  %xmm0, %xmm2, %xmm4
-;   vminpd  %xmm4, const(0), %xmm6
-;   vroundpd $3, %xmm6, %xmm8
-;   vaddpd  %xmm8, const(1), %xmm10
-;   vshufps $136 %xmm10, %xmm2, %xmm0
+;   uninit  %xmm2
+;   vxorpd  %xmm2, %xmm2, %xmm4
+;   vmaxpd  %xmm0, %xmm4, %xmm6
+;   vminpd  %xmm6, const(0), %xmm8
+;   vroundpd $3, %xmm8, %xmm10
+;   vaddpd  %xmm10, const(1), %xmm12
+;   vshufps $136 %xmm12, %xmm4, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -1362,12 +1364,12 @@ block0(v0: f64x2):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   xorpd %xmm2, %xmm2
-;   vmaxpd %xmm2, %xmm0, %xmm4
-;   vminpd 0x1c(%rip), %xmm4, %xmm6
-;   vroundpd $3, %xmm6, %xmm8
-;   vaddpd 0x1e(%rip), %xmm8, %xmm10
-;   vshufps $0x88, %xmm2, %xmm10, %xmm0
+;   vxorpd %xmm2, %xmm2, %xmm4
+;   vmaxpd %xmm4, %xmm0, %xmm6
+;   vminpd 0x1c(%rip), %xmm6, %xmm8
+;   vroundpd $3, %xmm8, %xmm10
+;   vaddpd 0x1e(%rip), %xmm10, %xmm12
+;   vshufps $0x88, %xmm4, %xmm12, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -1392,7 +1394,7 @@ block0(v0: i8x16, v1: i32):
 ;   vpsllw  %xmm0, %xmm5, %xmm7
 ;   lea     const(0), %rsi
 ;   shlq    $4, %r10, %r10
-;   movdqu  0(%rsi,%r10,1), %xmm13
+;   vmovdqu 0(%rsi,%r10,1), %xmm13
 ;   vpand   %xmm7, %xmm13, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
@@ -1409,7 +1411,7 @@ block0(v0: i8x16, v1: i32):
 ;   vpsllw %xmm5, %xmm0, %xmm7
 ;   leaq 0x15(%rip), %rsi
 ;   shlq $4, %r10
-;   movdqu (%rsi, %r10), %xmm13
+;   vmovdqu (%rsi, %r10), %xmm13
 ;   vpand %xmm13, %xmm7, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
@@ -1427,7 +1429,7 @@ block0(v0: i8x16):
 ;   movq    %rsp, %rbp
 ; block0:
 ;   vpsllw  %xmm0, $1, %xmm2
-;   movdqu  const(0), %xmm4
+;   vmovdqu const(0), %xmm4
 ;   vpand   %xmm2, %xmm4, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
@@ -1439,7 +1441,7 @@ block0(v0: i8x16):
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
 ;   vpsllw $1, %xmm0, %xmm2
-;   movdqu 0xf(%rip), %xmm4
+;   vmovdqu 0xf(%rip), %xmm4
 ;   vpand %xmm4, %xmm2, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
diff --git a/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif
index 4cf435295..7da79a0cc 100644
--- a/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif
+++ b/cranelift/filetests/filetests/isa/x64/simd-bitwise-avx.clif
@@ -187,9 +187,10 @@ block0(v0: f32x4):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pcmpeqd %xmm2, %xmm2, %xmm2
-;   vpsrld  %xmm2, $1, %xmm4
-;   vandps  %xmm0, %xmm4, %xmm0
+;   uninit  %xmm2
+;   vpcmpeqd %xmm2, %xmm2, %xmm4
+;   vpsrld  %xmm4, $1, %xmm6
+;   vandps  %xmm0, %xmm6, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -199,9 +200,9 @@ block0(v0: f32x4):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pcmpeqd %xmm2, %xmm2
-;   vpsrld $1, %xmm2, %xmm4
-;   vandps %xmm4, %xmm0, %xmm0
+;   vpcmpeqd %xmm2, %xmm2, %xmm4
+;   vpsrld $1, %xmm4, %xmm6
+;   vandps %xmm6, %xmm0, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
diff --git a/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif b/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif
index 80e7fa248..9dd8be2b1 100644
--- a/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif
+++ b/cranelift/filetests/filetests/isa/x64/simd-comparison-legalize.clif
@@ -13,8 +13,9 @@ block0(v0: i32x4, v1: i32x4):
 ;   movq    %rsp, %rbp
 ; block0:
 ;   pcmpeqd %xmm0, %xmm1, %xmm0
-;   pcmpeqd %xmm5, %xmm5, %xmm5
-;   pxor    %xmm0, %xmm5, %xmm0
+;   uninit  %xmm6
+;   pcmpeqd %xmm6, %xmm6, %xmm6
+;   pxor    %xmm0, %xmm6, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -25,8 +26,8 @@ block0(v0: i32x4, v1: i32x4):
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
 ;   pcmpeqd %xmm1, %xmm0
-;   pcmpeqd %xmm5, %xmm5
-;   pxor %xmm5, %xmm0
+;   pcmpeqd %xmm6, %xmm6
+;   pxor %xmm6, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -43,8 +44,9 @@ block0(v0: i32x4, v1: i32x4):
 ; block0:
 ;   pmaxud  %xmm0, %xmm1, %xmm0
 ;   pcmpeqd %xmm0, %xmm1, %xmm0
-;   pcmpeqd %xmm7, %xmm7, %xmm7
-;   pxor    %xmm0, %xmm7, %xmm0
+;   uninit  %xmm8
+;   pcmpeqd %xmm8, %xmm8, %xmm8
+;   pxor    %xmm0, %xmm8, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -56,8 +58,8 @@ block0(v0: i32x4, v1: i32x4):
 ; block1: ; offset 0x4
 ;   pmaxud %xmm1, %xmm0
 ;   pcmpeqd %xmm1, %xmm0
-;   pcmpeqd %xmm7, %xmm7
-;   pxor %xmm7, %xmm0
+;   pcmpeqd %xmm8, %xmm8
+;   pxor %xmm8, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
diff --git a/cranelift/filetests/filetests/isa/x64/simd-lane-access-compile.clif b/cranelift/filetests/filetests/isa/x64/simd-lane-access-compile.clif
index f414054ed..f2ce65fec 100644
--- a/cranelift/filetests/filetests/isa/x64/simd-lane-access-compile.clif
+++ b/cranelift/filetests/filetests/isa/x64/simd-lane-access-compile.clif
@@ -172,8 +172,9 @@ block0(v0: i8):
 ; block0:
 ;   uninit  %xmm0
 ;   pinsrb  $0, %xmm0, %rdi, %xmm0
-;   pxor    %xmm6, %xmm6, %xmm6
-;   pshufb  %xmm0, %xmm6, %xmm0
+;   uninit  %xmm7
+;   pxor    %xmm7, %xmm7, %xmm7
+;   pshufb  %xmm0, %xmm7, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -184,8 +185,8 @@ block0(v0: i8):
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
 ;   pinsrb $0, %edi, %xmm0
-;   pxor %xmm6, %xmm6
-;   pshufb %xmm6, %xmm0
+;   pxor %xmm7, %xmm7
+;   pshufb %xmm7, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
diff --git a/cranelift/filetests/filetests/isa/x64/simd-load-avx.clif b/cranelift/filetests/filetests/isa/x64/simd-load-avx.clif
index df4f25a99..6f9856e66 100644
--- a/cranelift/filetests/filetests/isa/x64/simd-load-avx.clif
+++ b/cranelift/filetests/filetests/isa/x64/simd-load-avx.clif
@@ -152,3 +152,87 @@ block0(v0: i64):
 ;   popq %rbp
 ;   retq
 
+function %load_store_i8x16(i64, i64) {
+block0(v0: i64, v1: i64):
+  v2 = load.i8x16 v0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovdqu 0(%rdi), %xmm3
+;   vmovdqu %xmm3, 0(%rsi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovdqu (%rdi), %xmm3 ; trap: heap_oob
+;   vmovdqu %xmm3, (%rsi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %load_store_f32x4(i64, i64) {
+block0(v0: i64, v1: i64):
+  v2 = load.f32x4 v0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovups 0(%rdi), %xmm3
+;   vmovups %xmm3, 0(%rsi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovups (%rdi), %xmm3 ; trap: heap_oob
+;   vmovups %xmm3, (%rsi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
+function %load_store_f64x2(i64, i64) {
+block0(v0: i64, v1: i64):
+  v2 = load.f64x2 v0
+  store v2, v1
+  return
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   vmovupd 0(%rdi), %xmm3
+;   vmovupd %xmm3, 0(%rsi)
+;   movq    %rbp, %rsp
+;   popq    %rbp
+;   ret
+; 
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   vmovupd (%rdi), %xmm3 ; trap: heap_oob
+;   vmovupd %xmm3, (%rsi) ; trap: heap_oob
+;   movq %rbp, %rsp
+;   popq %rbp
+;   retq
+
diff --git a/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif b/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif
index a4098176d..60ad9a419 100644
--- a/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif
+++ b/cranelift/filetests/filetests/isa/x64/simd-logical-compile.clif
@@ -12,8 +12,9 @@ block0(v0: i32x4):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pcmpeqd %xmm2, %xmm2, %xmm2
-;   pxor    %xmm0, %xmm2, %xmm0
+;   uninit  %xmm3
+;   pcmpeqd %xmm3, %xmm3, %xmm3
+;   pxor    %xmm0, %xmm3, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -23,8 +24,8 @@ block0(v0: i32x4):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pcmpeqd %xmm2, %xmm2
-;   pxor %xmm2, %xmm0
+;   pcmpeqd %xmm3, %xmm3
+;   pxor %xmm3, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
@@ -66,10 +67,11 @@ block0(v0: i64x2):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   pxor    %xmm2, %xmm2, %xmm2
-;   movdqa  %xmm0, %xmm4
-;   pcmpeqq %xmm4, %xmm2, %xmm4
-;   ptest   %xmm4, %xmm4
+;   uninit  %xmm3
+;   pxor    %xmm3, %xmm3, %xmm3
+;   movdqa  %xmm0, %xmm6
+;   pcmpeqq %xmm6, %xmm3, %xmm6
+;   ptest   %xmm6, %xmm6
 ;   setz    %al
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
@@ -80,10 +82,10 @@ block0(v0: i64x2):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   pxor %xmm2, %xmm2
-;   movdqa %xmm0, %xmm4
-;   pcmpeqq %xmm2, %xmm4
-;   ptest %xmm4, %xmm4
+;   pxor %xmm3, %xmm3
+;   movdqa %xmm0, %xmm6
+;   pcmpeqq %xmm3, %xmm6
+;   ptest %xmm6, %xmm6
 ;   sete %al
 ;   movq %rbp, %rsp
 ;   popq %rbp
diff --git a/cranelift/filetests/filetests/isa/x64/uunarrow.clif b/cranelift/filetests/filetests/isa/x64/uunarrow.clif
index 643b32f8e..7bae44a65 100644
--- a/cranelift/filetests/filetests/isa/x64/uunarrow.clif
+++ b/cranelift/filetests/filetests/isa/x64/uunarrow.clif
@@ -13,13 +13,14 @@ block0(v0: f64x2):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   xorpd   %xmm2, %xmm2, %xmm2
-;   movdqa  %xmm0, %xmm5
-;   maxpd   %xmm5, %xmm2, %xmm5
-;   minpd   %xmm5, const(0), %xmm5
-;   roundpd $3, %xmm5, %xmm0
+;   uninit  %xmm3
+;   xorpd   %xmm3, %xmm3, %xmm3
+;   movdqa  %xmm0, %xmm7
+;   maxpd   %xmm7, %xmm3, %xmm7
+;   minpd   %xmm7, const(0), %xmm7
+;   roundpd $3, %xmm7, %xmm0
 ;   addpd   %xmm0, const(1), %xmm0
-;   shufps  $136, %xmm0, %xmm2, %xmm0
+;   shufps  $136, %xmm0, %xmm3, %xmm0
 ;   movq    %rbp, %rsp
 ;   popq    %rbp
 ;   ret
@@ -29,13 +30,13 @@ block0(v0: f64x2):
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   xorpd %xmm2, %xmm2
-;   movdqa %xmm0, %xmm5
-;   maxpd %xmm2, %xmm5
-;   minpd 0x18(%rip), %xmm5
-;   roundpd $3, %xmm5, %xmm0
+;   xorpd %xmm3, %xmm3
+;   movdqa %xmm0, %xmm7
+;   maxpd %xmm3, %xmm7
+;   minpd 0x18(%rip), %xmm7
+;   roundpd $3, %xmm7, %xmm0
 ;   addpd 0x1a(%rip), %xmm0
-;   shufps $0x88, %xmm2, %xmm0
+;   shufps $0x88, %xmm3, %xmm0
 ;   movq %rbp, %rsp
 ;   popq %rbp
 ;   retq
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-insertlane.clif b/cranelift/filetests/filetests/runtests/simd-insertlane.clif
index e0965d832..6e0182b1c 100644
--- a/cranelift/filetests/filetests/runtests/simd-insertlane.clif
+++ b/cranelift/filetests/filetests/runtests/simd-insertlane.clif
@@ -4,6 +4,7 @@ target aarch64
 target s390x
 set enable_simd
 target x86_64 has_sse3 has_ssse3 has_sse41
+target x86_64 has_sse3 has_ssse3 has_sse41 has_avx
 
 function %insertlane_15(i8x16, i8) -> i8x16 {
 block0(v0: i8x16, v1: i8):
@@ -32,3 +33,17 @@ block0(v0: i64x2, v1: i64):
     return v2
 }
 ; run: %insertlane_0([1 1], 5000000000) == [5000000000 1]
+
+function %insertlane_0_in_f64x2(f64x2, f64) -> f64x2 {
+block0(v0: f64x2, v1: f64):
+    v2 = insertlane v0, v1, 0
+    return v2
+}
+; run: %insertlane_0_in_f64x2([0x1.0 0x2.0], 0x3.0) == [0x3.0 0x2.0]
+
+function %insertlane_1_in_f64x2(f64x2, f64) -> f64x2 {
+block0(v0: f64x2, v1: f64):
+    v2 = insertlane v0, v1, 1
+    return v2
+}
+; run: %insertlane_1_in_f64x2([0x1.0 0x2.0], 0x3.0) == [0x1.0 0x3.0]
diff --git a/cranelift/filetests/filetests/runtests/simd-shuffle.clif b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
index 621eebda6..6056fb3dd 100644
--- a/cranelift/filetests/filetests/runtests/simd-shuffle.clif
+++ b/cranelift/filetests/filetests/runtests/simd-shuffle.clif
@@ -1,9 +1,10 @@
-test interpret
+;; test interpret ;; FIXME(#5915)
 test run
 target aarch64
 target s390x
 set enable_simd
 target x86_64 has_sse3 has_ssse3 has_sse41
+target x86_64 has_sse3 has_ssse3 has_sse41 has_avx
 target x86_64 has_sse3 has_ssse3 has_sse41 has_avx512vl has_avx512vbmi
 
 function %shuffle_i8x16(i8x16, i8x16) -> i8x16 {
@@ -26,3 +27,526 @@ block0(v0: i8x16):
     return v1
 }
 ; run: %shuffle1([0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]) == [8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7]
+
+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
+}
+; run: %punpcklbw([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 2 18 3 19 4 20 5 21 6 22 7 23 8 24]
+
+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
+}
+; run: %punpckhbw([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]) == [9 25 10 26 11 27 12 28 13 29 14 30 15 31 16 32]
+
+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
+}
+; run: %punpcklwd([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 9 2 10 3 11 4 12]
+
+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
+}
+; run: %punpckhwd([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [5 13 6 14 7 15 8 16]
+
+function %pshufd_0022(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 0 1 2 3 8 9 10 11 8 9 10 11]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [1 1 3 3]
+
+function %pshufd_3120(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 4 5 6 7 8 9 10 11 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [4 2 3 1]
+
+function %pshufd_7546(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [28 29 30 31 20 21 22 23 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [8 6 5 7]
+
+function %not_pshufd(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 12 13 14 15 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %pshufd_0022([1 2 3 4], [5 6 7 8]) == [3 4 5 6]
+
+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
+}
+; run: %punpckldq([1 2 3 4], [5 6 7 8]) == [1 5 2 6]
+
+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
+}
+; run: %punpckldq([1 2 3 4], [5 6 7 8]) == [3 7 4 8]
+
+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
+}
+; run: %punpcklqdq([1 2], [5 6]) == [1 5]
+
+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
+}
+; run: %punpckhqdq([1 2], [5 6]) == [2 6]
+
+function %shufps_0145(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 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %shufps_0145([1 2 3 4], [5 6 7 8]) == [1 2 5 6]
+
+function %shufps_3277(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 8 9 10 11 28 29 30 31 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %shufps_0145([1 2 3 4], [5 6 7 8]) == [4 3 8 8]
+
+function %shufps_6500(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [24 25 26 27 20 21 22 23 0 1 2 3 0 1 2 3]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %shufps_0145([1 2 3 4], [5 6 7 8]) == [7 6 1 1]
+
+function %pshuflw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [4 3 2 1 5 6 7 8]
+
+function %pshuflw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 6 7 4 5 8 9 10 11 12 13 14 15]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [4 3 4 3 5 6 7 8]
+
+function %pshuflw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 20 21 18 19 16 17 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_rhs_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [12 11 10 9 13 14 15 16]
+
+function %pshuflw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [22 23 18 19 22 23 18 19 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshuflw_rhs_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [12 10 12 10 13 14 15 16]
+
+function %pshufhw_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 2 3 4 8 7 6 5]
+
+function %pshufhw_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 14 15 10 11 14 15 10 11]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 2 3 4 8 6 8 6]
+
+function %pshufhw_rhs_3210(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 28 29 26 27 24 25]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_rhs_3210([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [9 10 11 12 16 15 14 13]
+
+function %pshufhw_rhs_3131(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 20 21 22 23 30 31 26 27 30 31 26 27]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %pshufhw_rhs_3131([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [9 10 11 12 16 14 16 14]
+
+function %shuffle_all_zeros(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
+    return v2
+}
+; run: %shuffle_all_zeros([5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1], [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]) == [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5]
+
+function %aarch64_uzp1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30]
+    return v2
+}
+; run: %aarch64_uzp1_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31]
+
+function %aarch64_uzp2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31]
+    return v2
+}
+; run: %aarch64_uzp2_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32]
+
+function %aarch64_uzp1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 4 5 8 9 12 13 16 17 20 21 24 25 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 3 5 7 9 11 13 15]
+
+function %aarch64_uzp2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 6 7 10 11 14 15 18 19 22 23 26 27 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_uzp2_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [2 4 6 8 10 12 14 16]
+
+function %aarch64_uzp1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 8 9 10 11 16 17 18 19 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i32x4([1 2 3 4], [5 6 7 8]) == [1 3 5 7]
+
+function %aarch64_uzp2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 12 13 14 15 20 21 22 23 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_uzp2_i32x4([1 2 3 4], [5 6 7 8]) == [2 4 6 8]
+
+function %aarch64_uzp1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i64x2([1 2], [3 4]) == [1 3]
+
+function %aarch64_uzp2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_uzp1_i64x2([1 2], [3 4]) == [2 4]
+
+function %aarch64_trn1_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 16 2 18 4 20 6 22 8 24 10 26 12 28 14 30]
+    return v2
+}
+; run: %aarch64_trn1_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [1 17 3 19 5 21 7 23 9 25 11 27 13 29 15 31]
+
+function %aarch64_trn2_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 17 3 19 5 21 7 23 9 25 11 27 13 29 15 31]
+    return v2
+}
+; run: %aarch64_trn2_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [2 18 4 20 6 22 8 24 10 26 12 28 14 30 16 32]
+
+function %aarch64_trn1_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 16 17 4 5 20 21 8 9 24 25 12 13 28 29]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_trn1_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [1 9 3 11 5 13 7 15]
+
+function %aarch64_trn2_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 18 19 6 7 22 23 10 11 26 27 14 15 30 31]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_trn2_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [2 10 4 12 6 14 8 16]
+
+function %aarch64_trn1_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 16 17 18 19 8 9 10 11 24 25 26 27]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_trn1_i32x4([1 2 3 4], [5 6 7 8]) == [1 5 3 7]
+
+function %aarch64_trn2_i32x4(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 20 21 22 23 12 13 14 15 28 29 30 31]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_trn2_i32x4([1 2 3 4], [5 6 7 8]) == [2 6 4 8]
+
+function %aarch64_trn1_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_trn1_i64x2([1 2], [3 4]) == [1 3]
+
+function %aarch64_trn2_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_trn2_i64x2([1 2], [3 4]) == [2 4]
+
+function %aarch64_ext_0(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
+    return v2
+}
+; run: %aarch64_ext_0([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
+
+function %aarch64_ext_1(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
+    return v2
+}
+; run: %aarch64_ext_1([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17]
+
+function %aarch64_ext_5(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
+    return v2
+}
+; run: %aarch64_ext_5([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21]
+
+function %aarch64_ext_11(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]
+    return v2
+}
+; run: %aarch64_ext_11([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27]
+
+function %aarch64_ext_16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31]
+    return v2
+}
+; run: %aarch64_ext_16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]
+
+function %aarch64_dup_i8x16(i8x16, i8x16) -> i8x16 {
+block0(v0: i8x16, v1: i8x16):
+    v2 = shuffle v0, v1, [5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5]
+    return v2
+}
+; run: %aarch64_dup_i8x16([1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16], [17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32]) == [6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6]
+
+function %aarch64_dup_i16x8(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [12 13 12 13 12 13 12 13 12 13 12 13 12 13 12 13]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_dup_i16x8([1 2 3 4 5 6 7 8], [9 10 11 12 13 14 15 16]) == [7 7 7 7 7 7 7 7]
+
+function %aarch64_dup_i32x4_1(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [8 9 10 11 8 9 10 11 8 9 10 11 8 9 10 11]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_dup_i32x4_1([1 2 3 4], [5 6 7 8]) == [3 3 3 3]
+
+function %aarch64_dup_i32x4_2(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [16 17 18 19 16 17 18 19 16 17 18 19 16 17 18 19]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_dup_i32x4_2([1 2 3 4], [5 6 7 8]) == [5 5 5 5]
+
+function %aarch64_dup_i64x2(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_dup_i64x2([1 2], [5 6]) == [1 1]
+
+function %aarch64_rev16(i16x8, i16x8) -> i16x8 {
+block0(v0: i16x8, v1: i16x8):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [1 0 3 2 5 4 7 6 9 8 11 10 13 12 15 14]
+    v5 = bitcast.i16x8 little v4
+    return v5
+}
+; run: %aarch64_rev16([0x1 0x2 0x3 0x4 0x100 0x200 0x300 0x400], [0 0 0 0 0 0 0 0]) == [0x100 0x200 0x300 0x400 0x1 0x2 0x3 0x4]
+
+function %aarch64_rev32_bytes(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_rev32_bytes([1 2 3 4], [0 0 0 0]) == [0x1000000 0x2000000 0x3000000 0x4000000]
+; run: %aarch64_rev32_bytes([0xff000000 0x00ff0000 0x0000ff00 0x000000ff], [0 0 0 0]) == [0xff 0xff00 0xff0000 0xff000000]
+
+function %aarch64_rev32_words(i32x4, i32x4) -> i32x4 {
+block0(v0: i32x4, v1: i32x4):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [2 3 0 1 6 7 4 5 10 11 8 9 14 15 12 13]
+    v5 = bitcast.i32x4 little v4
+    return v5
+}
+; run: %aarch64_rev32_words([1 2 3 4], [0 0 0 0]) == [0x10000 0x20000 0x30000 0x40000]
+; run: %aarch64_rev32_words([0xff000000 0x00ff0000 0x0000ff00 0x000000ff], [0 0 0 0]) == [0xff00 0xff 0xff000000 0xff0000]
+
+function %aarch64_rev64_bytes(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [7 6 5 4 3 2 1 0 15 14 13 12 11 10 9 8]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_rev64_bytes([0x0102030405060708 0x0807060504030201], [0 0]) == [0x0807060504030201 0x0102030405060708]
+
+function %aarch64_rev64_words(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [6 7 4 5 2 3 0 1 14 15 12 13 10 11 8 9]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_rev64_words([0x0102030405060708 0x0807060504030201], [0 0]) == [0x0708050603040102 0x0201040306050807]
+
+function %aarch64_rev64_doublewords(i64x2, i64x2) -> i64x2 {
+block0(v0: i64x2, v1: i64x2):
+    v2 = bitcast.i8x16 little v0
+    v3 = bitcast.i8x16 little v1
+    v4 = shuffle v2, v3, [4 5 6 7 0 1 2 3 12 13 14 15 8 9 10 11]
+    v5 = bitcast.i64x2 little v4
+    return v5
+}
+; run: %aarch64_rev64_words([0x0102030405060708 0x0807060504030201], [0 0]) == [0x0506070801020304 0x0403020108070605]
diff --git a/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat b/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat
index f3ae7c735..8d7eca8c0 100644
--- a/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat
+++ b/cranelift/filetests/filetests/wasm/x64-relaxed-simd-deterministic.wat
@@ -63,19 +63,21 @@
 ;;   movq    %rsp, %rbp
 ;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
 ;; block0:
-;;   xorps   %xmm3, %xmm3, %xmm3
-;;   vmaxps  %xmm0, %xmm3, %xmm5
-;;   vpcmpeqd %xmm3, %xmm3, %xmm7
-;;   vpsrld  %xmm7, $1, %xmm9
-;;   vcvtdq2ps %xmm9, %xmm11
-;;   vcvttps2dq %xmm5, %xmm13
-;;   vsubps  %xmm5, %xmm11, %xmm15
-;;   vcmpps  $2 %xmm11, %xmm15, %xmm1
-;;   vcvttps2dq %xmm15, %xmm3
-;;   vpxor   %xmm3, %xmm1, %xmm5
-;;   pxor    %xmm7, %xmm7, %xmm7
-;;   vpmaxsd %xmm5, %xmm7, %xmm9
-;;   vpaddd  %xmm9, %xmm13, %xmm0
+;;   uninit  %xmm3
+;;   vxorps  %xmm3, %xmm3, %xmm5
+;;   vmaxps  %xmm0, %xmm5, %xmm7
+;;   vpcmpeqd %xmm5, %xmm5, %xmm9
+;;   vpsrld  %xmm9, $1, %xmm11
+;;   vcvtdq2ps %xmm11, %xmm13
+;;   vcvttps2dq %xmm7, %xmm15
+;;   vsubps  %xmm7, %xmm13, %xmm1
+;;   vcmpps  $2 %xmm13, %xmm1, %xmm3
+;;   vcvttps2dq %xmm1, %xmm5
+;;   vpxor   %xmm5, %xmm3, %xmm7
+;;   uninit  %xmm9
+;;   vpxor   %xmm9, %xmm9, %xmm11
+;;   vpmaxsd %xmm7, %xmm11, %xmm13
+;;   vpaddd  %xmm13, %xmm15, %xmm0
 ;;   jmp     label1
 ;; block1:
 ;;   movq    %rbp, %rsp
@@ -104,12 +106,13 @@
 ;;   movq    %rsp, %rbp
 ;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
 ;; block0:
-;;   xorpd   %xmm3, %xmm3, %xmm3
-;;   vmaxpd  %xmm0, %xmm3, %xmm5
-;;   vminpd  %xmm5, const(0), %xmm7
-;;   vroundpd $3, %xmm7, %xmm9
-;;   vaddpd  %xmm9, const(1), %xmm11
-;;   vshufps $136 %xmm11, %xmm3, %xmm0
+;;   uninit  %xmm3
+;;   vxorpd  %xmm3, %xmm3, %xmm5
+;;   vmaxpd  %xmm0, %xmm5, %xmm7
+;;   vminpd  %xmm7, const(0), %xmm9
+;;   vroundpd $3, %xmm9, %xmm11
+;;   vaddpd  %xmm11, const(1), %xmm13
+;;   vshufps $136 %xmm13, %xmm5, %xmm0
 ;;   jmp     label1
 ;; block1:
 ;;   movq    %rbp, %rsp
diff --git a/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat b/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat
index 43586fcb2..cef3910e4 100644
--- a/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat
+++ b/cranelift/filetests/filetests/wasm/x64-relaxed-simd.wat
@@ -55,20 +55,22 @@
 ;;   movq    %rsp, %rbp
 ;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
 ;; block0:
-;;   xorps   %xmm6, %xmm6, %xmm6
-;;   movdqa  %xmm0, %xmm10
-;;   maxps   %xmm10, %xmm6, %xmm10
-;;   pcmpeqd %xmm6, %xmm6, %xmm6
-;;   psrld   %xmm6, $1, %xmm6
-;;   cvtdq2ps %xmm6, %xmm14
-;;   cvttps2dq %xmm10, %xmm13
-;;   subps   %xmm10, %xmm14, %xmm10
-;;   cmpps   $2, %xmm14, %xmm10, %xmm14
-;;   cvttps2dq %xmm10, %xmm0
-;;   pxor    %xmm0, %xmm14, %xmm0
-;;   pxor    %xmm7, %xmm7, %xmm7
-;;   pmaxsd  %xmm0, %xmm7, %xmm0
-;;   paddd   %xmm0, %xmm13, %xmm0
+;;   uninit  %xmm7
+;;   xorps   %xmm7, %xmm7, %xmm7
+;;   movdqa  %xmm0, %xmm12
+;;   maxps   %xmm12, %xmm7, %xmm12
+;;   pcmpeqd %xmm7, %xmm7, %xmm7
+;;   psrld   %xmm7, $1, %xmm7
+;;   cvtdq2ps %xmm7, %xmm1
+;;   cvttps2dq %xmm12, %xmm15
+;;   subps   %xmm12, %xmm1, %xmm12
+;;   cmpps   $2, %xmm1, %xmm12, %xmm1
+;;   cvttps2dq %xmm12, %xmm0
+;;   pxor    %xmm0, %xmm1, %xmm0
+;;   uninit  %xmm10
+;;   pxor    %xmm10, %xmm10, %xmm10
+;;   pmaxsd  %xmm0, %xmm10, %xmm0
+;;   paddd   %xmm0, %xmm15, %xmm0
 ;;   jmp     label1
 ;; block1:
 ;;   movq    %rbp, %rsp
@@ -94,13 +96,14 @@
 ;;   movq    %rsp, %rbp
 ;;   unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
 ;; block0:
-;;   xorpd   %xmm3, %xmm3, %xmm3
-;;   movdqa  %xmm0, %xmm6
-;;   maxpd   %xmm6, %xmm3, %xmm6
-;;   minpd   %xmm6, const(0), %xmm6
-;;   roundpd $3, %xmm6, %xmm0
+;;   uninit  %xmm4
+;;   xorpd   %xmm4, %xmm4, %xmm4
+;;   movdqa  %xmm0, %xmm8
+;;   maxpd   %xmm8, %xmm4, %xmm8
+;;   minpd   %xmm8, const(0), %xmm8
+;;   roundpd $3, %xmm8, %xmm0
 ;;   addpd   %xmm0, const(1), %xmm0
-;;   shufps  $136, %xmm0, %xmm3, %xmm0
+;;   shufps  $136, %xmm0, %xmm4, %xmm0
 ;;   jmp     label1
 ;; block1:
 ;;   movq    %rbp, %rsp
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