Repository: bytecodealliance/wasmtime
Pull request #9982 (2025-01-10 20:44:45)

Improve some logging in the differential fuzzer
Clearly show "lhs" and "rhs" more often and then also swap the order of the arguments in `assert_error_match` to match the "lhs" and "rhs" terminology of the original execution.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 71e8a5a35..3ce594f53 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1406,12 +1406,13 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.3.27"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
 dependencies = [
  "futures-channel",
  "futures-core",
+ "futures-executor",
  "futures-io",
  "futures-sink",
  "futures-task",
@@ -1420,9 +1421,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.27"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -1434,11 +1435,33 @@ version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
 
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
 [[package]]
 name = "futures-io"
-version = "0.3.28"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
 
 [[package]]
 name = "futures-sink"
@@ -1458,11 +1481,16 @@ version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
+ "futures-channel",
  "futures-core",
+ "futures-io",
+ "futures-macro",
  "futures-sink",
  "futures-task",
+ "memchr",
  "pin-project-lite",
  "pin-utils",
+ "slab",
 ]
 
 [[package]]
@@ -3209,7 +3237,7 @@ dependencies = [
  "cargo_metadata",
  "heck 0.5.0",
  "wasmtime",
- "wit-component 0.223.0",
+ "wit-component",
 ]
 
 [[package]]
@@ -3618,7 +3646,7 @@ name = "verify-component-adapter"
 version = "30.0.0"
 dependencies = [
  "anyhow",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wat",
 ]
 
@@ -3710,7 +3738,7 @@ dependencies = [
  "byte-array-literals",
  "object",
  "wasi",
- "wasm-encoder 0.223.0",
+ "wasm-encoder",
  "wit-bindgen-rust-macro",
 ]
 
@@ -3769,16 +3797,6 @@ version = "0.2.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
 
-[[package]]
-name = "wasm-encoder"
-version = "0.220.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7"
-dependencies = [
- "leb128",
- "wasmparser 0.220.0",
-]
-
 [[package]]
 name = "wasm-encoder"
 version = "0.223.0"
@@ -3786,23 +3804,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464"
 dependencies = [
  "leb128",
- "wasmparser 0.223.0",
-]
-
-[[package]]
-name = "wasm-metadata"
-version = "0.220.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f3e5f5920c5abfc45573c89b07b38efdaae1515ef86f83dad12d60e50ecd62b"
-dependencies = [
- "anyhow",
- "indexmap 2.7.0",
- "serde",
- "serde_derive",
- "serde_json",
- "spdx",
- "wasm-encoder 0.220.0",
- "wasmparser 0.220.0",
+ "wasmparser",
 ]
 
 [[package]]
@@ -3818,8 +3820,8 @@ dependencies = [
  "serde_json",
  "spdx",
  "url",
- "wasm-encoder 0.223.0",
- "wasmparser 0.223.0",
+ "wasm-encoder",
+ "wasmparser",
 ]
 
 [[package]]
@@ -3832,8 +3834,8 @@ dependencies = [
  "log",
  "rand",
  "thiserror",
- "wasm-encoder 0.223.0",
- "wasmparser 0.223.0",
+ "wasm-encoder",
+ "wasmparser",
 ]
 
 [[package]]
@@ -3847,7 +3849,7 @@ dependencies = [
  "flagset",
  "indexmap 2.7.0",
  "leb128",
- "wasm-encoder 0.223.0",
+ "wasm-encoder",
 ]
 
 [[package]]
@@ -3867,7 +3869,7 @@ dependencies = [
  "indexmap 2.7.0",
  "logos",
  "thiserror",
- "wit-parser 0.223.0",
+ "wit-parser",
 ]
 
 [[package]]
@@ -3914,19 +3916,6 @@ dependencies = [
  "wasmi_core",
 ]
 
-[[package]]
-name = "wasmparser"
-version = "0.220.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b"
-dependencies = [
- "ahash",
- "bitflags 2.6.0",
- "hashbrown 0.14.3",
- "indexmap 2.7.0",
- "semver",
-]
-
 [[package]]
 name = "wasmparser"
 version = "0.223.0"
@@ -3957,7 +3946,7 @@ checksum = "9235722b8cdb6c1c6daa537d4be4e230e76ce3ce0e4ba991956a1c6aed50305a"
 dependencies = [
  "anyhow",
  "termcolor",
- "wasmparser 0.223.0",
+ "wasmparser",
 ]
 
 [[package]]
@@ -4002,9 +3991,9 @@ dependencies = [
  "tempfile",
  "trait-variant",
  "wasi-common",
- "wasm-encoder 0.223.0",
+ "wasm-encoder",
  "wasm-wave",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wasmtime-asm-macros",
  "wasmtime-cache",
  "wasmtime-component-macro",
@@ -4147,8 +4136,8 @@ dependencies = [
  "trait-variant",
  "walkdir",
  "wasi-common",
- "wasm-encoder 0.223.0",
- "wasmparser 0.223.0",
+ "wasm-encoder",
+ "wasmparser",
  "wasmtime",
  "wasmtime-cache",
  "wasmtime-cli-flags",
@@ -4168,7 +4157,7 @@ dependencies = [
  "wast 223.0.0",
  "wat",
  "windows-sys 0.59.0",
- "wit-component 0.223.0",
+ "wit-component",
 ]
 
 [[package]]
@@ -4201,7 +4190,7 @@ dependencies = [
  "wasmtime",
  "wasmtime-component-util",
  "wasmtime-wit-bindgen",
- "wit-parser 0.223.0",
+ "wit-parser",
 ]
 
 [[package]]
@@ -4227,7 +4216,7 @@ dependencies = [
  "smallvec",
  "target-lexicon",
  "thiserror",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wasmtime-environ",
  "wasmtime-versioned-export-macros",
 ]
@@ -4253,8 +4242,8 @@ dependencies = [
  "serde_derive",
  "smallvec",
  "target-lexicon",
- "wasm-encoder 0.223.0",
- "wasmparser 0.223.0",
+ "wasm-encoder",
+ "wasmparser",
  "wasmprinter",
  "wasmtime-component-util",
  "wat",
@@ -4268,7 +4257,7 @@ dependencies = [
  "component-fuzz-util",
  "env_logger 0.11.5",
  "libfuzzer-sys",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wasmprinter",
  "wasmtime-environ",
  "wat",
@@ -4327,7 +4316,7 @@ dependencies = [
  "rand",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wasmtime",
  "wasmtime-fuzzing",
 ]
@@ -4348,12 +4337,12 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "v8",
- "wasm-encoder 0.223.0",
+ "wasm-encoder",
  "wasm-mutate",
  "wasm-smith",
  "wasm-spec-interpreter",
  "wasmi",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wasmprinter",
  "wasmtime",
  "wasmtime-wast",
@@ -4555,7 +4544,7 @@ dependencies = [
  "gimli",
  "object",
  "target-lexicon",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wasmtime-cranelift",
  "wasmtime-environ",
  "winch-codegen",
@@ -4568,7 +4557,7 @@ dependencies = [
  "anyhow",
  "heck 0.5.0",
  "indexmap 2.7.0",
- "wit-parser 0.223.0",
+ "wit-parser",
 ]
 
 [[package]]
@@ -4594,7 +4583,7 @@ dependencies = [
  "leb128",
  "memchr",
  "unicode-width 0.2.0",
- "wasm-encoder 0.223.0",
+ "wasm-encoder",
 ]
 
 [[package]]
@@ -4745,7 +4734,7 @@ dependencies = [
  "smallvec",
  "target-lexicon",
  "thiserror",
- "wasmparser 0.223.0",
+ "wasmparser",
  "wasmtime-cranelift",
  "wasmtime-environ",
 ]
@@ -4968,9 +4957,9 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen"
-version = "0.35.0"
+version = "0.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c966692b6d8c4bb3c1aee3313e0930f44457a5763cfffb666f814506124e4691"
+checksum = "9219694564701fa935754f1552ce299154fc74948d6d148134ce55f3504c8bf1"
 dependencies = [
  "wit-bindgen-rt",
  "wit-bindgen-rust-macro",
@@ -4978,45 +4967,47 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen-core"
-version = "0.35.0"
+version = "0.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19857cff2a480fece56ea43f9199322ee5014688a3539ebf8d29ae62d75a3a1f"
+checksum = "8ba105733ba146c94e067793fb46505265ea8720eb14ceae65b10797c7728a65"
 dependencies = [
  "anyhow",
  "heck 0.5.0",
- "wit-parser 0.220.0",
+ "wit-parser",
 ]
 
 [[package]]
 name = "wit-bindgen-rt"
-version = "0.35.0"
+version = "0.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "605d5562e971a823cf5a550a9d69cb7144e9b4969a810043127175517a4e1865"
+checksum = "fc801b991c56492f87ab3086e786468f75c285a4d73017ab0ebc2fa1aed5d82c"
 dependencies = [
  "bitflags 2.6.0",
+ "futures",
+ "once_cell",
 ]
 
 [[package]]
 name = "wit-bindgen-rust"
-version = "0.35.0"
+version = "0.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d894e599c161d71acb6a78e8ec8a609a09c2bb227de50829f5afbd03b844a69"
+checksum = "257e0d217bc06635837d751447c39e77b9901752e052288ff6fe0fdb17850bc5"
 dependencies = [
  "anyhow",
  "heck 0.5.0",
  "indexmap 2.7.0",
  "prettyplease",
  "syn 2.0.90",
- "wasm-metadata 0.220.0",
+ "wasm-metadata",
  "wit-bindgen-core",
- "wit-component 0.220.0",
+ "wit-component",
 ]
 
 [[package]]
 name = "wit-bindgen-rust-macro"
-version = "0.35.0"
+version = "0.37.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a723cf943bba3bf34f437eb101e5a78180971e25dfb0085d80dbe4e73afb2854"
+checksum = "8ac98caa9302234687b8e67ce7dfcf31ae5238523f166b93c23988fd0d4e0594"
 dependencies = [
  "anyhow",
  "prettyplease",
@@ -5027,25 +5018,6 @@ dependencies = [
  "wit-bindgen-rust",
 ]
 
-[[package]]
-name = "wit-component"
-version = "0.220.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73ccedf54cc65f287da268d64d2bf4f7530d2cfb2296ffbe3ad5f65567e4cf53"
-dependencies = [
- "anyhow",
- "bitflags 2.6.0",
- "indexmap 2.7.0",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "wasm-encoder 0.220.0",
- "wasm-metadata 0.220.0",
- "wasmparser 0.220.0",
- "wit-parser 0.220.0",
-]
-
 [[package]]
 name = "wit-component"
 version = "0.223.0"
@@ -5059,28 +5031,10 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.223.0",
- "wasm-metadata 0.223.0",
- "wasmparser 0.223.0",
- "wit-parser 0.223.0",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.220.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b7117ce3adc0b4354b46dc1cf3190b00b333e65243d244c613ffcc58bdec84d"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap 2.7.0",
- "log",
- "semver",
- "serde",
- "serde_derive",
- "serde_json",
- "unicode-xid",
- "wasmparser 0.220.0",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
 ]
 
 [[package]]
@@ -5098,7 +5052,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.223.0",
+ "wasmparser",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 7f6a4e78d..5da2cb346 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -293,8 +293,8 @@ io-lifetimes = { version = "2.0.3", default-features = false }
 io-extras = "0.18.1"
 rustix = "0.38.43"
 # wit-bindgen:
-wit-bindgen = { version = "0.35.0", default-features = false }
-wit-bindgen-rust-macro = { version = "0.35.0", default-features = false }
+wit-bindgen = { version = "0.37.0", default-features = false }
+wit-bindgen-rust-macro = { version = "0.37.0", default-features = false }
 
 # wasm-tools family:
 wasmparser = { version = "0.223.0", default-features = false, features = ['simd'] }
@@ -422,6 +422,7 @@ default = [
   "gc-drc",
   "gc-null",
   "winch",
+  "pulley",
 
   # Enable some nice features of clap by default, but they come at a binary size
   # cost, so allow disabling this through disabling of our own `default`
@@ -436,7 +437,6 @@ default = [
 # These features are off-by-default but may optionally be enabled.
 all-arch = ["wasmtime/all-arch"]
 winch = ["wasmtime/winch"]
-pulley = ["wasmtime/pulley"]
 wmemcheck = ["wasmtime/wmemcheck"]
 trace-log = ["wasmtime/trace-log"]
 memory-protection-keys = ["wasmtime-cli-flags/memory-protection-keys"]
@@ -476,6 +476,7 @@ gc = ["wasmtime-cli-flags/gc", "wasmtime/gc"]
 gc-drc = ["gc", "wasmtime/gc-drc", "wasmtime-cli-flags/gc-drc"]
 gc-null = ["gc", "wasmtime/gc-null", "wasmtime-cli-flags/gc-null"]
 signals-based-traps = ["wasmtime/signals-based-traps", "wasmtime-cli-flags/signals-based-traps"]
+pulley = ["wasmtime-cli-flags/pulley"]
 
 # CLI subcommands for the `wasmtime` executable. See `wasmtime $cmd --help`
 # for more information on each subcommand.
diff --git a/cranelift/codegen/src/isa/pulley_shared/abi.rs b/cranelift/codegen/src/isa/pulley_shared/abi.rs
index 6efbd3927..e41db7e55 100644
--- a/cranelift/codegen/src/isa/pulley_shared/abi.rs
+++ b/cranelift/codegen/src/isa/pulley_shared/abi.rs
@@ -454,16 +454,7 @@ where
                 // `info.dest.args` to be handled differently during register
                 // allocation.
                 let mut args = SmallVec::new();
-                if cfg!(debug_assertions) {
-                    let xargs = info
-                        .uses
-                        .iter()
-                        .filter_map(|a| XReg::new(a.preg))
-                        .collect::<Vec<_>>();
-                    for window in xargs.windows(2) {
-                        assert!(window[0] < window[1]);
-                    }
-                }
+                info.uses.sort_by_key(|arg| arg.preg);
                 info.uses.retain(|arg| {
                     if arg.preg != x0() && arg.preg != x1() && arg.preg != x2() && arg.preg != x3()
                     {
diff --git a/cranelift/codegen/src/isa/pulley_shared/inst.isle b/cranelift/codegen/src/isa/pulley_shared/inst.isle
index ffb72c24a..565c34714 100644
--- a/cranelift/codegen/src/isa/pulley_shared/inst.isle
+++ b/cranelift/codegen/src/isa/pulley_shared/inst.isle
@@ -368,6 +368,9 @@
 ;; Lower a constant into a register.
 (decl imm (Type u64) Reg)
 
+(rule 4 (imm (ty_int _) 0) (pulley_xzero))
+(rule 5 (imm (ty_int _) 1) (pulley_xone))
+
 ;; If a value can fit into 8 bits, then prioritize that.
 (rule 3 (imm (ty_int _) x)
       (if-let y (i8_try_from_u64 x))
diff --git a/crates/cli-flags/Cargo.toml b/crates/cli-flags/Cargo.toml
index f42efcdc0..c91b8cec2 100644
--- a/crates/cli-flags/Cargo.toml
+++ b/crates/cli-flags/Cargo.toml
@@ -36,3 +36,4 @@ gc-null = ["gc", "wasmtime/gc-null"]
 threads = ["wasmtime/threads"]
 memory-protection-keys = ["wasmtime/memory-protection-keys"]
 signals-based-traps = ["wasmtime/signals-based-traps"]
+pulley = ["wasmtime/pulley"]
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index 3592aebd3..35c030f67 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -429,13 +429,13 @@ pub fn differential(
         // execution by returning success.
         Ok(None) => return Ok(true),
     };
-    log::debug!(" -> results on {}: {:?}", lhs.name(), &lhs_results);
+    log::debug!(" -> lhs results on {}: {:?}", lhs.name(), &lhs_results);
 
     let rhs_results = rhs
         .evaluate(name, args, result_tys)
         // wasmtime should be able to invoke any signature, so unwrap this result
         .map(|results| results.unwrap());
-    log::debug!(" -> results on {}: {:?}", rhs.name(), &rhs_results);
+    log::debug!(" -> rhs results on {}: {:?}", rhs.name(), &rhs_results);
 
     // If Wasmtime hit its OOM condition, which is possible since it's set
     // somewhat low while fuzzing, then don't return an error but return
@@ -507,7 +507,7 @@ impl<T, U> DiffEqResult<T, U> {
             // Both sides failed. Check that the trap and state at the time of
             // failure is the same, when possible.
             (Err(lhs), Err(rhs)) => {
-                let err = match rhs.downcast::<Trap>() {
+                let rhs = match rhs.downcast::<Trap>() {
                     Ok(trap) => trap,
 
                     // For general, unknown errors, we can't rely on this being
@@ -527,16 +527,16 @@ impl<T, U> DiffEqResult<T, U> {
                 let poisoned =
                     // Allocations being too large for the GC are
                     // implementation-defined.
-                    err == Trap::AllocationTooLarge
+                    rhs == Trap::AllocationTooLarge
                     // Stack size, and therefore when overflow happens, is
                     // implementation-defined.
-                    || err == Trap::StackOverflow
+                    || rhs == Trap::StackOverflow
                     || lhs_engine.is_stack_overflow(&lhs);
                 if poisoned {
                     return DiffEqResult::Poisoned;
                 }
 
-                lhs_engine.assert_error_match(&err, &lhs);
+                lhs_engine.assert_error_match(&lhs, &rhs);
                 DiffEqResult::Failed
             }
             // A real bug is found if only one side fails.
diff --git a/crates/fuzzing/src/oracles/diff_spec.rs b/crates/fuzzing/src/oracles/diff_spec.rs
index 23a502555..09c590ac5 100644
--- a/crates/fuzzing/src/oracles/diff_spec.rs
+++ b/crates/fuzzing/src/oracles/diff_spec.rs
@@ -44,7 +44,7 @@ impl DiffEngine for SpecInterpreter {
         Ok(Box::new(SpecInstance { instance }))
     }
 
-    fn assert_error_match(&self, trap: &Trap, err: &Error) {
+    fn assert_error_match(&self, err: &Error, trap: &Trap) {
         // TODO: implement this for the spec interpreter
         let _ = (trap, err);
     }
diff --git a/crates/fuzzing/src/oracles/diff_v8.rs b/crates/fuzzing/src/oracles/diff_v8.rs
index fee5fb555..f2fa5603d 100644
--- a/crates/fuzzing/src/oracles/diff_v8.rs
+++ b/crates/fuzzing/src/oracles/diff_v8.rs
@@ -78,7 +78,7 @@ impl DiffEngine for V8Engine {
         }))
     }
 
-    fn assert_error_match(&self, wasmtime: &Trap, err: &Error) {
+    fn assert_error_match(&self, err: &Error, wasmtime: &Trap) {
         let v8 = err.to_string();
         let wasmtime_msg = wasmtime.to_string();
         let verify_wasmtime = |msg: &str| {
diff --git a/crates/fuzzing/src/oracles/diff_wasmi.rs b/crates/fuzzing/src/oracles/diff_wasmi.rs
index 8715c6a93..49219f922 100644
--- a/crates/fuzzing/src/oracles/diff_wasmi.rs
+++ b/crates/fuzzing/src/oracles/diff_wasmi.rs
@@ -78,10 +78,10 @@ impl DiffEngine for WasmiEngine {
         Ok(Box::new(WasmiInstance { store, instance }))
     }
 
-    fn assert_error_match(&self, trap: &Trap, err: &Error) {
-        match self.trap_code(err) {
-            Some(code) => assert_eq!(wasmi_to_wasmtime_trap_code(code), *trap),
-            None => panic!("unexpected wasmi error {err:?}"),
+    fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {
+        match self.trap_code(lhs) {
+            Some(code) => assert_eq!(wasmi_to_wasmtime_trap_code(code), *rhs),
+            None => panic!("unexpected wasmi error {lhs:?}"),
         }
     }
 
diff --git a/crates/fuzzing/src/oracles/diff_wasmtime.rs b/crates/fuzzing/src/oracles/diff_wasmtime.rs
index 51293e6da..291035973 100644
--- a/crates/fuzzing/src/oracles/diff_wasmtime.rs
+++ b/crates/fuzzing/src/oracles/diff_wasmtime.rs
@@ -52,11 +52,11 @@ impl DiffEngine for WasmtimeEngine {
         Ok(Box::new(instance))
     }
 
-    fn assert_error_match(&self, trap: &Trap, err: &Error) {
-        let trap2 = err
+    fn assert_error_match(&self, lhs: &Error, rhs: &Trap) {
+        let lhs = lhs
             .downcast_ref::<Trap>()
-            .expect(&format!("not a trap: {err:?}"));
-        assert_eq!(trap, trap2, "{trap}\nis not equal to\n{trap2}");
+            .expect(&format!("not a trap: {lhs:?}"));
+        assert_eq!(lhs, rhs, "{lhs}\nis not equal to\n{rhs}");
     }
 
     fn is_stack_overflow(&self, err: &Error) -> bool {
diff --git a/crates/fuzzing/src/oracles/engine.rs b/crates/fuzzing/src/oracles/engine.rs
index 887f8382b..342c43b92 100644
--- a/crates/fuzzing/src/oracles/engine.rs
+++ b/crates/fuzzing/src/oracles/engine.rs
@@ -50,7 +50,7 @@ pub trait DiffEngine {
 
     /// Tests that the wasmtime-originating `trap` matches the error this engine
     /// generated.
-    fn assert_error_match(&self, trap: &Trap, err: &Error);
+    fn assert_error_match(&self, err: &Error, trap: &Trap);
 
     /// Returns whether the error specified from this engine might be stack
     /// overflow.
diff --git a/crates/wasi/src/runtime.rs b/crates/wasi/src/runtime.rs
index 8115f8daa..762e4045e 100644
--- a/crates/wasi/src/runtime.rs
+++ b/crates/wasi/src/runtime.rs
@@ -4,7 +4,7 @@
 //! wasmtime-wasi requires a tokio executor in a way that is [deeply tied to
 //! its
 //! design](https://github.com/bytecodealliance/wasmtime/issues/7973#issuecomment-1960513214).
-//! When used from a sychrnonous wasmtime context, this module provides the
+//! When used from a synchronous wasmtime context, this module provides the
 //! wrapper function [`in_tokio`] used throughout the shim implementations of
 //! synchronous component binding `Host` traits in terms of the async ones.
 //!
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 03a807484..f3a9087d2 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -1972,7 +1972,6 @@ impl Config {
             Some(Strategy::Winch) => {
                 let mut unsupported = WasmFeatures::GC
                     | WasmFeatures::FUNCTION_REFERENCES
-                    | WasmFeatures::THREADS
                     | WasmFeatures::RELAXED_SIMD
                     | WasmFeatures::TAIL_CALL
                     | WasmFeatures::GC_TYPES;
@@ -1985,6 +1984,7 @@ impl Config {
                         // winch on aarch64 but this helps gate most spec tests
                         // by default which otherwise currently cause panics.
                         unsupported |= WasmFeatures::REFERENCE_TYPES;
+                        unsupported |= WasmFeatures::THREADS
                     }
 
                     // Winch doesn't support other non-x64 architectures at this
diff --git a/crates/wast-util/src/lib.rs b/crates/wast-util/src/lib.rs
index 2dcc8e153..a72a05b2a 100644
--- a/crates/wast-util/src/lib.rs
+++ b/crates/wast-util/src/lib.rs
@@ -286,7 +286,6 @@ impl Compiler {
             // Cranelift has quite yet.
             Compiler::Winch => {
                 if config.gc()
-                    || config.threads()
                     || config.tail_call()
                     || config.function_references()
                     || config.gc()
@@ -457,7 +456,6 @@ impl WastTest {
                 "misc_testsuite/simd/spillslot-size-fuzzbug.wast",
                 "misc_testsuite/simd/unaligned-load.wast",
                 "multi-memory/simd_memory-multi.wast",
-                "spec_testsuite/simd_align.wast",
                 "spec_testsuite/simd_bit_shift.wast",
                 "spec_testsuite/simd_bitwise.wast",
                 "spec_testsuite/simd_boolean.wast",
@@ -511,11 +509,32 @@ impl WastTest {
                 "spec_testsuite/simd_store32_lane.wast",
                 "spec_testsuite/simd_store64_lane.wast",
                 "spec_testsuite/simd_store8_lane.wast",
+                // thread related failures
+                "proposals/threads/atomic.wast",
+                "misc_testsuite/threads/MP_wait.wast",
+                "misc_testsuite/threads/load-store-alignment.wast",
+                "misc_testsuite/threads/MP_atomic.wast",
+                "misc_testsuite/threads/SB_atomic.wast",
+                "misc_testsuite/threads/wait_notify.wast",
+                "misc_testsuite/threads/LB_atomic.wast",
+                "misc_testsuite/threads/atomics_wait_address.wast",
+                "misc_testsuite/threads/atomics_notify.wast",
+                "misc_testsuite/threads/load-store-alignment.wast",
             ];
 
             if unsupported.iter().any(|part| self.path.ends_with(part)) {
                 return true;
             }
+
+            // SIMD on Winch requires AVX instructions.
+            #[cfg(target_arch = "x86_64")]
+            if !(std::is_x86_feature_detected!("avx") && std::is_x86_feature_detected!("avx2")) {
+                let unsupported = ["spec_testsuite/simd_align.wast"];
+
+                if unsupported.iter().any(|part| self.path.ends_with(part)) {
+                    return true;
+                }
+            }
         }
 
         for part in self.path.iter() {
diff --git a/pulley/src/interp.rs b/pulley/src/interp.rs
index 173f337ca..dbad29f7d 100644
--- a/pulley/src/interp.rs
+++ b/pulley/src/interp.rs
@@ -1398,6 +1398,16 @@ impl OpVisitor for Interpreter<'_> {
         ControlFlow::Continue(())
     }
 
+    fn xzero(&mut self, dst: XReg) -> ControlFlow<Done> {
+        self.state[dst].set_i64(0);
+        ControlFlow::Continue(())
+    }
+
+    fn xone(&mut self, dst: XReg) -> ControlFlow<Done> {
+        self.state[dst].set_i64(1);
+        ControlFlow::Continue(())
+    }
+
     fn xconst16(&mut self, dst: XReg, imm: i16) -> ControlFlow<Done> {
         self.state[dst].set_i64(i64::from(imm));
         ControlFlow::Continue(())
diff --git a/pulley/src/lib.rs b/pulley/src/lib.rs
index dc36eaafc..39acfe455 100644
--- a/pulley/src/lib.rs
+++ b/pulley/src/lib.rs
@@ -247,6 +247,10 @@ macro_rules! for_each_op {
             /// Move between `x` registers.
             xmov = Xmov { dst: XReg, src: XReg };
 
+            /// Set `dst = 0`
+            xzero = Xzero { dst: XReg };
+            /// Set `dst = 1`
+            xone = Xone { dst: XReg };
             /// Set `dst = sign_extend(imm8)`.
             xconst8 = Xconst8 { dst: XReg, imm: i8 };
             /// Set `dst = sign_extend(imm16)`.
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 66aa2bc3f..c74e98133 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1751,12 +1751,22 @@ criteria = "safe-to-deploy"
 version = "0.3.27"
 notes = "There are no definitions in this crate, just exports of definitions from child crates."
 
+[[audits.futures]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-deploy"
+version = "0.3.31"
+
 [[audits.futures-channel]]
 who = "Pat Hickey <phickey@fastly.com>"
 criteria = "safe-to-deploy"
 version = "0.3.27"
 notes = "build.rs is just detecting the target and setting cfg. unsafety is for implementing a concurrency primitives using atomics and unsafecell, and is not obviously incorrect (this is the sort of thing I wouldn't certify as correct without formal methods)"
 
+[[audits.futures-channel]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-deploy"
+version = "0.3.31"
+
 [[audits.futures-core]]
 who = "Pat Hickey <phickey@fastly.com>"
 criteria = "safe-to-deploy"
@@ -1774,11 +1784,26 @@ criteria = "safe-to-deploy"
 version = "0.3.27"
 notes = "Unsafe used to implement the unpark mutex, which is well commented and not obviously incorrect. Like with futures-channel I wouldn't be able to certify it as correct without formal methods."
 
+[[audits.futures-executor]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-deploy"
+version = "0.3.31"
+
 [[audits.futures-io]]
 who = "Pat Hickey <phickey@fastly.com>"
 criteria = "safe-to-deploy"
 version = "0.3.27"
 
+[[audits.futures-io]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-deploy"
+version = "0.3.31"
+
+[[audits.futures-macro]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-deploy"
+version = "0.3.31"
+
 [[audits.futures-sink]]
 who = "Pat Hickey <phickey@fastly.com>"
 criteria = "safe-to-deploy"
@@ -3894,6 +3919,31 @@ criteria = "safe-to-deploy"
 delta = "0.35.0 -> 0.35.1"
 notes = "Just a dependency version bump"
 
+[[audits.wit-bindgen]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-run"
+version = "0.37.0"
+
+[[audits.wit-bindgen-core]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-run"
+version = "0.37.0"
+
+[[audits.wit-bindgen-rt]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-run"
+version = "0.37.0"
+
+[[audits.wit-bindgen-rust]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-run"
+version = "0.37.0"
+
+[[audits.wit-bindgen-rust-macro]]
+who = "Joel Dice <joel.dice@gmail.com>"
+criteria = "safe-to-run"
+version = "0.37.0"
+
 [[audits.wit-parser]]
 who = "Alex Crichton <alex@alexcrichton.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index d8de8656c..108c13d4e 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1786,32 +1786,32 @@ user-login = "sunfishcode"
 user-name = "Dan Gohman"
 
 [[publisher.wit-bindgen]]
-version = "0.35.0"
-when = "2024-11-13"
+version = "0.37.0"
+when = "2025-01-10"
 user-id = 73222
 user-login = "wasmtime-publish"
 
 [[publisher.wit-bindgen-core]]
-version = "0.35.0"
-when = "2024-11-13"
+version = "0.37.0"
+when = "2025-01-10"
 user-id = 73222
 user-login = "wasmtime-publish"
 
 [[publisher.wit-bindgen-rt]]
-version = "0.35.0"
-when = "2024-11-13"
+version = "0.37.0"
+when = "2025-01-10"
 user-id = 73222
 user-login = "wasmtime-publish"
 
 [[publisher.wit-bindgen-rust]]
-version = "0.35.0"
-when = "2024-11-13"
+version = "0.37.0"
+when = "2025-01-10"
 user-id = 73222
 user-login = "wasmtime-publish"
 
 [[publisher.wit-bindgen-rust-macro]]
-version = "0.35.0"
-when = "2024-11-13"
+version = "0.37.0"
+when = "2025-01-10"
 user-id = 73222
 user-login = "wasmtime-publish"
 
diff --git a/winch/codegen/src/abi/mod.rs b/winch/codegen/src/abi/mod.rs
index d46144489..1a3eee578 100644
--- a/winch/codegen/src/abi/mod.rs
+++ b/winch/codegen/src/abi/mod.rs
@@ -48,6 +48,7 @@
 use crate::codegen::ptr_type_from_ptr_size;
 use crate::isa::{reg::Reg, CallingConvention};
 use crate::masm::SPOffset;
+use anyhow::Result;
 use smallvec::SmallVec;
 use std::collections::HashSet;
 use std::ops::{Add, BitAnd, Not, Sub};
@@ -86,7 +87,7 @@ pub(crate) use scratch;
 pub(crate) use vmctx;
 
 /// Constructs an [ABISig] using Winch's ABI.
-pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> ABISig {
+pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> Result<ABISig> {
     // 6 is used semi-arbitrarily here, we can modify as we see fit.
     let mut params: SmallVec<[WasmValType; 6]> = SmallVec::new();
     params.extend_from_slice(&vmctx_types::<A>());
@@ -116,7 +117,7 @@ pub(crate) trait ABI {
     /// Construct the ABI-specific signature from a WebAssembly
     /// function type.
     #[cfg(test)]
-    fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> ABISig {
+    fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> Result<ABISig> {
         Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv)
     }
 
@@ -125,10 +126,10 @@ pub(crate) trait ABI {
         params: &[WasmValType],
         returns: &[WasmValType],
         call_conv: &CallingConvention,
-    ) -> ABISig;
+    ) -> Result<ABISig>;
 
     /// Construct [`ABIResults`] from a slice of [`WasmType`].
-    fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> ABIResults;
+    fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults>;
 
     /// Returns the number of bits in a word.
     fn word_bits() -> u8;
@@ -315,24 +316,30 @@ impl ABIResults {
     /// representation, according to the calling convention. In the case of
     /// results, one result is stored in registers and the rest at particular
     /// offsets in the stack.
-    pub fn from<F>(returns: &[WasmValType], call_conv: &CallingConvention, mut map: F) -> Self
+    pub fn from<F>(
+        returns: &[WasmValType],
+        call_conv: &CallingConvention,
+        mut map: F,
+    ) -> Result<Self>
     where
-        F: FnMut(&WasmValType, u32) -> (ABIOperand, u32),
+        F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
     {
         if returns.len() == 0 {
-            return Self::default();
+            return Ok(Self::default());
         }
 
         type FoldTuple = (SmallVec<[ABIOperand; 6]>, HashSet<Reg>, u32);
-
-        let fold_impl = |(mut operands, mut regs, stack_bytes): FoldTuple, arg| {
-            let (operand, bytes) = map(arg, stack_bytes);
-            if operand.is_reg() {
-                regs.insert(operand.unwrap_reg());
-            }
-            operands.push(operand);
-            (operands, regs, bytes)
-        };
+        type FoldTupleResult = Result<FoldTuple>;
+
+        let fold_impl =
+            |(mut operands, mut regs, stack_bytes): FoldTuple, arg| -> FoldTupleResult {
+                let (operand, bytes) = map(arg, stack_bytes)?;
+                if operand.is_reg() {
+                    regs.insert(operand.unwrap_reg());
+                }
+                operands.push(operand);
+                Ok((operands, regs, bytes))
+            };
 
         // When dealing with multiple results, Winch's calling convention stores the
         // last return value in a register rather than the first one. In that
@@ -342,15 +349,15 @@ impl ABIResults {
         // * Spilled memory values always precede register values
         // * Spilled values are stored from oldest to newest, matching their
         //   respective locations on the machine stack.
-        let (mut operands, regs, bytes): FoldTuple = if call_conv.is_default() {
+        let (mut operands, regs, bytes) = if call_conv.is_default() {
             returns
                 .iter()
                 .rev()
-                .fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)
+                .try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
         } else {
             returns
                 .iter()
-                .fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)
+                .try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
         };
 
         // Similar to above, we reverse the result of the operands calculation
@@ -359,11 +366,11 @@ impl ABIResults {
             operands.reverse();
         }
 
-        Self::new(ABIOperands {
+        Ok(Self::new(ABIOperands {
             inner: operands,
             regs,
             bytes,
-        })
+        }))
     }
 
     /// Create a new [`ABIResults`] from [`ABIOperands`].
@@ -464,12 +471,12 @@ impl ABIParams {
         initial_bytes: u32,
         needs_stack_results: bool,
         mut map: F,
-    ) -> Self
+    ) -> Result<Self>
     where
-        F: FnMut(&WasmValType, u32) -> (ABIOperand, u32),
+        F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
     {
         if params.len() == 0 && !needs_stack_results {
-            return Self::with_bytes(initial_bytes);
+            return Ok(Self::with_bytes(initial_bytes));
         }
 
         let register_capacity = params.len().min(6);
@@ -480,7 +487,7 @@ impl ABIParams {
         let ptr_type = ptr_type_from_ptr_size(<A as ABI>::word_bytes());
         // Handle stack results by specifying an extra, implicit first argument.
         let stack_results = if needs_stack_results {
-            let (operand, bytes) = map(&ptr_type, stack_bytes);
+            let (operand, bytes) = map(&ptr_type, stack_bytes)?;
             if operand.is_reg() {
                 regs.insert(operand.unwrap_reg());
             }
@@ -491,7 +498,7 @@ impl ABIParams {
         };
 
         for arg in params.iter() {
-            let (operand, bytes) = map(arg, stack_bytes);
+            let (operand, bytes) = map(arg, stack_bytes)?;
             if operand.is_reg() {
                 regs.insert(operand.unwrap_reg());
             }
@@ -505,14 +512,14 @@ impl ABIParams {
             operands.push(operand);
         }
 
-        Self {
+        Ok(Self {
             operands: ABIOperands {
                 inner: operands,
                 regs,
                 bytes: stack_bytes,
             },
             has_retptr: needs_stack_results,
-        }
+        })
     }
 
     /// Creates new [`ABIParams`], with the specified amount of stack bytes.
diff --git a/winch/codegen/src/codegen/builtin.rs b/winch/codegen/src/codegen/builtin.rs
index 96268ccfd..fee8a93f1 100644
--- a/winch/codegen/src/codegen/builtin.rs
+++ b/winch/codegen/src/codegen/builtin.rs
@@ -5,6 +5,7 @@ use crate::{
     codegen::env::ptr_type_from_ptr_size,
     CallingConvention,
 };
+use anyhow::Result;
 use cranelift_codegen::ir::LibCall;
 use std::sync::Arc;
 use wasmtime_environ::{BuiltinFunctionIndex, PtrSize, VMOffsets, WasmValType};
@@ -154,109 +155,109 @@ macro_rules! declare_function_sig {
                 WasmValType::I32
             }
 
-            fn over_f64<A: ABI>(&self) -> ABISig {
+            fn over_f64<A: ABI>(&self) -> Result<ABISig> {
                 A::sig_from(&[self.f64()], &[self.f64()], &self.host_call_conv)
             }
 
-            fn over_f32<A: ABI>(&self) -> ABISig {
+            fn over_f32<A: ABI>(&self) -> Result<ABISig> {
                 A::sig_from(&[self.f64()], &[self.f64()], &self.host_call_conv)
             }
 
-            pub(crate) fn ceil_f32<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn ceil_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.ceil_f32.is_none() {
-                    let sig = self.over_f32::<A>();
+                    let sig = self.over_f32::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::CeilF32) });
                     self.ceil_f32 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.ceil_f32.as_ref().unwrap().clone()
+                Ok(self.ceil_f32.as_ref().unwrap().clone())
             }
 
-            pub(crate) fn ceil_f64<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn ceil_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.ceil_f64.is_none() {
-                    let sig = self.over_f64::<A>();
+                    let sig = self.over_f64::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::CeilF64) });
                     self.ceil_f64 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.ceil_f64.as_ref().unwrap().clone()
+                Ok(self.ceil_f64.as_ref().unwrap().clone())
             }
 
-            pub(crate) fn floor_f32<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn floor_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.floor_f32.is_none() {
-                    let sig = self.over_f32::<A>();
+                    let sig = self.over_f32::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::FloorF32) });
                     self.floor_f32 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.floor_f32.as_ref().unwrap().clone()
+                Ok(self.floor_f32.as_ref().unwrap().clone())
             }
 
-            pub(crate) fn floor_f64<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn floor_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.floor_f64.is_none() {
-                    let sig = self.over_f64::<A>();
+                    let sig = self.over_f64::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::FloorF64) });
                     self.floor_f64 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.floor_f64.as_ref().unwrap().clone()
+                Ok(self.floor_f64.as_ref().unwrap().clone())
             }
 
-            pub(crate) fn trunc_f32<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn trunc_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.trunc_f32.is_none() {
-                    let sig = self.over_f32::<A>();
+                    let sig = self.over_f32::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::TruncF32) });
                     self.trunc_f32 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.trunc_f32.as_ref().unwrap().clone()
+                Ok(self.trunc_f32.as_ref().unwrap().clone())
             }
 
-            pub(crate) fn trunc_f64<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn trunc_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.trunc_f64.is_none() {
-                    let sig = self.over_f64::<A>();
+                    let sig = self.over_f64::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::TruncF64) });
                     self.trunc_f64 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.trunc_f64.as_ref().unwrap().clone()
+                Ok(self.trunc_f64.as_ref().unwrap().clone())
             }
 
-            pub(crate) fn nearest_f32<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn nearest_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.nearest_f32.is_none() {
-                    let sig = self.over_f32::<A>();
+                    let sig = self.over_f32::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::NearestF32) });
                     self.nearest_f32 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.nearest_f32.as_ref().unwrap().clone()
+                Ok(self.nearest_f32.as_ref().unwrap().clone())
             }
 
-            pub(crate) fn nearest_f64<A: ABI>(&mut self) -> BuiltinFunction {
+            pub(crate) fn nearest_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
                 if self.nearest_f64.is_none() {
-                    let sig = self.over_f64::<A>();
+                    let sig = self.over_f64::<A>()?;
                     let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::NearestF64) });
                     self.nearest_f64 = Some(BuiltinFunction {
                         inner,
                     });
                 }
-                self.nearest_f64.as_ref().unwrap().clone()
+                Ok(self.nearest_f64.as_ref().unwrap().clone())
             }
 
             $(
                 $( #[ $attr ] )*
-                pub(crate) fn $name<A: ABI, P: PtrSize>(&mut self) -> BuiltinFunction {
+                pub(crate) fn $name<A: ABI, P: PtrSize>(&mut self) -> Result<BuiltinFunction> {
                     if self.$name.is_none() {
                         let params = vec![ $(self.$param() ),* ];
                         let result = vec![ $(self.$result() )?];
-                        let sig = A::sig_from(&params, &result, &self.wasm_call_conv);
+                        let sig = A::sig_from(&params, &result, &self.wasm_call_conv)?;
                         let index = BuiltinFunctionIndex::$name();
                         let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::builtin(index) });
                         self.$name = Some(BuiltinFunction {
@@ -264,7 +265,7 @@ macro_rules! declare_function_sig {
                         });
                     }
 
-                    self.$name.as_ref().unwrap().clone()
+                    Ok(self.$name.as_ref().unwrap().clone())
                 }
              )*
         }
diff --git a/winch/codegen/src/codegen/call.rs b/winch/codegen/src/codegen/call.rs
index d27ef919d..f4dec97a9 100644
--- a/winch/codegen/src/codegen/call.rs
+++ b/winch/codegen/src/codegen/call.rs
@@ -91,7 +91,7 @@ impl FnCall {
     ) -> Result<()> {
         let (kind, callee_context) = Self::lower(env, context.vmoffsets, &callee, context, masm)?;
 
-        let sig = env.callee_sig::<M::ABI>(&callee);
+        let sig = env.callee_sig::<M::ABI>(&callee)?;
         context.spill(masm)?;
         let ret_area = Self::make_ret_area(&sig, masm)?;
         let arg_stack_space = sig.params_stack_size();
@@ -142,11 +142,11 @@ impl FnCall {
         match callee {
             Callee::Builtin(b) => Ok(Self::lower_builtin(env, b)),
             Callee::FuncRef(_) => {
-                Self::lower_funcref(env.callee_sig::<M::ABI>(callee), ptr, context, masm)
+                Self::lower_funcref(env.callee_sig::<M::ABI>(callee)?, ptr, context, masm)
             }
             Callee::Local(i) => Ok(Self::lower_local(env, *i)),
             Callee::Import(i) => {
-                let sig = env.callee_sig::<M::ABI>(callee);
+                let sig = env.callee_sig::<M::ABI>(callee)?;
                 Self::lower_import(*i, sig, context, masm, vmoffsets)
             }
         }
diff --git a/winch/codegen/src/codegen/control.rs b/winch/codegen/src/codegen/control.rs
index 6d93ab35e..99eec7b73 100644
--- a/winch/codegen/src/codegen/control.rs
+++ b/winch/codegen/src/codegen/control.rs
@@ -63,19 +63,19 @@ impl BlockSig {
 
     /// Return the ABI representation of the results of the block.
     /// This method will lazily initialize the results if not present.
-    pub fn results<M>(&mut self) -> &mut ABIResults
+    pub fn results<M>(&mut self) -> Result<&mut ABIResults>
     where
         M: MacroAssembler,
     {
         if self.ty.is_sig() {
             return match &mut self.ty {
-                BlockType::ABISig(sig) => &mut sig.results,
+                BlockType::ABISig(sig) => Ok(&mut sig.results),
                 _ => unreachable!(),
             };
         }
 
         if self.results.is_some() {
-            return self.results.as_mut().unwrap();
+            return Ok(self.results.as_mut().unwrap());
         }
 
         let results = match &self.ty {
@@ -89,19 +89,19 @@ impl BlockSig {
             BlockType::ABISig(_) => unreachable!(),
         };
 
-        self.results = Some(results);
-        self.results.as_mut().unwrap()
+        self.results = Some(results?);
+        Ok(self.results.as_mut().unwrap())
     }
 
     /// Construct an ABI result representation of the params of the block.
     /// This is needed for loops and for handling cases in which params flow as
     /// the block's results, i.e. in the presence of an empty then or else.
-    pub fn params<M>(&mut self) -> &mut ABIResults
+    pub fn params<M>(&mut self) -> Result<&mut ABIResults>
     where
         M: MacroAssembler,
     {
         if self.params.is_some() {
-            return self.params.as_mut().unwrap();
+            return Ok(self.params.as_mut().unwrap());
         }
 
         let params_as_results = match &self.ty {
@@ -118,8 +118,8 @@ impl BlockSig {
             BlockType::ABISig(_) => unreachable!(),
         };
 
-        self.params = Some(params_as_results);
-        self.params.as_mut().unwrap()
+        self.params = Some(params_as_results?);
+        Ok(self.params.as_mut().unwrap())
     }
 
     /// Returns the signature param count.
@@ -304,9 +304,9 @@ impl ControlStackFrame {
         self.calculate_stack_state(context, masm)?;
         // If the block has stack results, immediately resolve the return area
         // base.
-        if self.results::<M>().on_stack() {
+        if self.results::<M>()?.on_stack() {
             let results_base = self.stack_state().target_offset;
-            self.results::<M>().set_ret_area(RetArea::sp(results_base));
+            self.results::<M>()?.set_ret_area(RetArea::sp(results_base));
         }
 
         if self.is_if() || self.is_loop() {
@@ -324,13 +324,13 @@ impl ControlStackFrame {
             //   )
             //)
             let base_offset = self.stack_state().base_offset;
-            if self.params::<M>().on_stack() {
-                let offset = base_offset.as_u32() + self.params::<M>().size();
-                self.params::<M>()
+            if self.params::<M>()?.on_stack() {
+                let offset = base_offset.as_u32() + self.params::<M>()?.size();
+                self.params::<M>()?
                     .set_ret_area(RetArea::sp(SPOffset::from_u32(offset)));
             }
             Self::top_abi_results_impl(
-                self.params::<M>(),
+                self.params::<M>()?,
                 context,
                 masm,
                 |params: &ABIResults, _, _| Ok(params.ret_area().copied()),
@@ -361,7 +361,7 @@ impl ControlStackFrame {
             context.stack.len() >= param_count,
             CodeGenError::missing_values_in_stack()
         );
-        let results_size = self.results::<M>().size();
+        let results_size = self.results::<M>()?.size();
 
         // Save any live registers and locals.
         context.spill(masm)?;
@@ -445,7 +445,7 @@ impl ControlStackFrame {
                 // branch params, we exclude any result registers from being
                 // used as the branch test.
                 let top = context.without::<Result<TypedReg>, _, _>(
-                    self.params::<M>().regs(),
+                    self.params::<M>()?.regs(),
                     masm,
                     |cx, masm| cx.pop_to_reg(masm, None),
                 )??;
@@ -520,8 +520,8 @@ impl ControlStackFrame {
                 // resets the stack pointer so that it matches the expectations
                 // of the else branch: the stack pointer is expected to be at
                 // the base stack pointer, plus the params stack size in bytes.
-                let params_size = sig.params::<M>().size();
-                context.push_abi_results::<M, _>(sig.params::<M>(), masm, |params, _, _| {
+                let params_size = sig.params::<M>()?.size();
+                context.push_abi_results::<M, _>(sig.params::<M>()?, masm, |params, _, _| {
                     params.ret_area().copied()
                 })?;
                 masm.reset_stack_pointer(SPOffset::from_u32(
@@ -635,7 +635,7 @@ impl ControlStackFrame {
 
     /// Returns [`crate::abi::ABIResults`] of the control stack frame
     /// block.
-    pub fn results<M>(&mut self) -> &mut ABIResults
+    pub fn results<M>(&mut self) -> Result<&mut ABIResults>
     where
         M: MacroAssembler,
     {
@@ -648,7 +648,7 @@ impl ControlStackFrame {
     }
 
     /// Returns the block params interpreted as [crate::abi::ABIResults].
-    pub fn params<M>(&mut self) -> &mut ABIResults
+    pub fn params<M>(&mut self) -> Result<&mut ABIResults>
     where
         M: MacroAssembler,
     {
@@ -686,7 +686,7 @@ impl ControlStackFrame {
         M: MacroAssembler,
         F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Result<Option<RetArea>>,
     {
-        Self::pop_abi_results_impl(self.results::<M>(), context, masm, calculate_ret_area)
+        Self::pop_abi_results_impl(self.results::<M>()?, context, masm, calculate_ret_area)
     }
 
     /// Shared implementation for poppping the ABI results.
@@ -753,7 +753,7 @@ impl ControlStackFrame {
     where
         M: MacroAssembler,
     {
-        context.push_abi_results(self.results::<M>(), masm, |results, _, _| {
+        context.push_abi_results(self.results::<M>()?, masm, |results, _, _| {
             results.ret_area().copied()
         })
     }
@@ -772,7 +772,7 @@ impl ControlStackFrame {
         M: MacroAssembler,
         F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Result<Option<RetArea>>,
     {
-        Self::top_abi_results_impl::<M, _>(self.results::<M>(), context, masm, calculate_ret_area)
+        Self::top_abi_results_impl::<M, _>(self.results::<M>()?, context, masm, calculate_ret_area)
     }
 
     /// Internal implementation of [Self::top_abi_results].
diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs
index edae51d47..c20302553 100644
--- a/winch/codegen/src/codegen/env.rs
+++ b/winch/codegen/src/codegen/env.rs
@@ -16,6 +16,8 @@ use wasmtime_environ::{
     TypeIndex, VMOffsets, WasmHeapType, WasmValType,
 };
 
+use anyhow::Result;
+
 #[derive(Debug, Clone, Copy)]
 pub struct GlobalData {
     /// The offset of the global.
@@ -54,7 +56,7 @@ pub struct HeapData {
     pub offset: u32,
     /// The offset to the current length field.
     pub current_length_offset: u32,
-    /// If the WebAssembly memory is imported, this field contains the offset to locate the
+    /// If the WebAssembly memory is imported or shared, this field contains the offset to locate the
     /// base of the heap.
     pub import_from: Option<u32>,
     /// The memory type this heap is associated with.
@@ -247,22 +249,33 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
     }
 
     /// Resolve a `HeapData` from a [MemoryIndex].
-    // TODO: (@saulecabrera)
-    // Handle shared memories when implementing support for Wasm Threads.
     pub fn resolve_heap(&mut self, index: MemoryIndex) -> HeapData {
+        let mem = self.translation.module.memories[index];
+        let is_shared = mem.shared;
         match self.resolved_heaps.entry(index) {
             Occupied(entry) => *entry.get(),
             Vacant(entry) => {
                 let (import_from, base_offset, current_length_offset) =
                     match self.translation.module.defined_memory_index(index) {
                         Some(defined) => {
-                            let owned = self.translation.module.owned_memory_index(defined);
-                            (
-                                None,
-                                self.vmoffsets.vmctx_vmmemory_definition_base(owned),
-                                self.vmoffsets
-                                    .vmctx_vmmemory_definition_current_length(owned),
-                            )
+                            if is_shared {
+                                (
+                                    Some(self.vmoffsets.vmctx_vmmemory_pointer(defined)),
+                                    self.vmoffsets.ptr.vmmemory_definition_base().into(),
+                                    self.vmoffsets
+                                        .ptr
+                                        .vmmemory_definition_current_length()
+                                        .into(),
+                                )
+                            } else {
+                                let owned = self.translation.module.owned_memory_index(defined);
+                                (
+                                    None,
+                                    self.vmoffsets.vmctx_vmmemory_definition_base(owned),
+                                    self.vmoffsets
+                                        .vmctx_vmmemory_definition_current_length(owned),
+                                )
+                            }
                         }
                         None => (
                             Some(self.vmoffsets.vmctx_vmmemory_import_from(index)),
@@ -302,32 +315,37 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
         self.table_access_spectre_mitigation
     }
 
-    pub(crate) fn callee_sig<'b, A>(&'b mut self, callee: &'b Callee) -> &'b ABISig
+    pub(crate) fn callee_sig<'b, A>(&'b mut self, callee: &'b Callee) -> Result<&'b ABISig>
     where
         A: ABI,
     {
         match callee {
             Callee::Local(idx) | Callee::Import(idx) => {
-                let types = self.translation.get_types();
-                let types = types.as_ref();
-                let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
-                let val = || {
+                if self.resolved_callees.contains_key(idx) {
+                    Ok(self.resolved_callees.get(idx).unwrap())
+                } else {
+                    let types = self.translation.get_types();
+                    let types = types.as_ref();
+                    let ty = types[types.core_function_at(idx.as_u32())].unwrap_func();
                     let converter = TypeConverter::new(self.translation, self.types);
                     let ty = converter.convert_func_type(&ty);
-                    wasm_sig::<A>(&ty)
-                };
-                self.resolved_callees.entry(*idx).or_insert_with(val)
+                    let sig = wasm_sig::<A>(&ty)?;
+                    self.resolved_callees.insert(*idx, sig);
+                    Ok(self.resolved_callees.get(idx).unwrap())
+                }
             }
             Callee::FuncRef(idx) => {
-                let val = || {
+                if self.resolved_sigs.contains_key(idx) {
+                    Ok(self.resolved_sigs.get(idx).unwrap())
+                } else {
                     let sig_index = self.translation.module.types[*idx];
                     let ty = self.types[sig_index].unwrap_func();
-                    let sig = wasm_sig::<A>(ty);
-                    sig
-                };
-                self.resolved_sigs.entry(*idx).or_insert_with(val)
+                    let sig = wasm_sig::<A>(ty)?;
+                    self.resolved_sigs.insert(*idx, sig);
+                    Ok(self.resolved_sigs.get(idx).unwrap())
+                }
             }
-            Callee::Builtin(b) => b.sig(),
+            Callee::Builtin(b) => Ok(b.sig()),
         }
     }
 
diff --git a/winch/codegen/src/codegen/error.rs b/winch/codegen/src/codegen/error.rs
index 8834aefbb..674468741 100644
--- a/winch/codegen/src/codegen/error.rs
+++ b/winch/codegen/src/codegen/error.rs
@@ -17,6 +17,12 @@ pub(crate) enum CodeGenError {
     /// Unimplemented MacroAssembler instruction.
     #[error("Unimplemented Masm instruction")]
     UnimplementedMasmInstruction,
+    /// Unimplemented Wasm load kind.
+    #[error("Unimplemented Wasm load kind")]
+    UnimplementedWasmLoadKind,
+    /// Unimplemented due to requiring AVX.
+    #[error("Instruction not implemented for CPUs without AVX support")]
+    UnimplementedForNoAvx,
     /// Unsupported eager initialization of tables.
     #[error("Unsupported eager initialization of tables")]
     UnsupportedTableEagerInit,
diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs
index 5a8010852..4fab71b7f 100644
--- a/winch/codegen/src/codegen/mod.rs
+++ b/winch/codegen/src/codegen/mod.rs
@@ -3,7 +3,8 @@ use crate::{
     codegen::BlockSig,
     isa::reg::{writable, Reg},
     masm::{
-        ExtendKind, IntCmpKind, MacroAssembler, OperandSize, RegImm, SPOffset, ShiftKind, TrapCode,
+        IntCmpKind, LoadKind, MacroAssembler, MemOpKind, OperandSize, RegImm, SPOffset, ShiftKind,
+        TrapCode,
     },
     stack::TypedReg,
 };
@@ -548,7 +549,7 @@ where
         let builtin = self
             .env
             .builtins
-            .table_get_lazy_init_func_ref::<M::ABI, M::Ptr>();
+            .table_get_lazy_init_func_ref::<M::ABI, M::Ptr>()?;
 
         // Request the builtin's  result register and use it to hold the table
         // element value. We preemptively spill and request this register to
@@ -845,10 +846,10 @@ where
         arg: &MemArg,
         ty: WasmValType,
         size: OperandSize,
-        sextend: Option<ExtendKind>,
+        kind: LoadKind,
+        op_kind: MemOpKind,
     ) -> Result<()> {
-        let addr = self.emit_compute_heap_address(&arg, size)?;
-        if let Some(addr) = addr {
+        if let Some(addr) = self.emit_compute_heap_address(&arg, size)? {
             let dst = match ty {
                 WasmValType::I32 | WasmValType::I64 => self.context.any_gpr(self.masm)?,
                 WasmValType::F32 | WasmValType::F64 => self.context.any_fpr(self.masm)?,
@@ -857,7 +858,8 @@ where
             };
 
             let src = self.masm.address_at_reg(addr, 0)?;
-            self.masm.wasm_load(src, writable!(dst), size, sextend)?;
+            self.masm
+                .wasm_load(src, writable!(dst), size, kind, op_kind)?;
             self.context.stack.push(TypedReg::new(ty, dst).into());
             self.context.free_reg(addr);
         }
@@ -1016,7 +1018,7 @@ where
             return Ok(());
         }
 
-        let out_of_fuel = self.env.builtins.out_of_gas::<M::ABI, M::Ptr>();
+        let out_of_fuel = self.env.builtins.out_of_gas::<M::ABI, M::Ptr>()?;
         let fuel_reg = self.context.without::<Result<Reg>, M, _>(
             &out_of_fuel.sig().regs,
             self.masm,
@@ -1084,7 +1086,7 @@ where
         // The continuation branch if the current epoch hasn't reached the
         // configured deadline.
         let cont = self.masm.get_label()?;
-        let new_epoch = self.env.builtins.new_epoch::<M::ABI, M::Ptr>();
+        let new_epoch = self.env.builtins.new_epoch::<M::ABI, M::Ptr>()?;
 
         // Checks for runtime limits (e.g., fuel, epoch) are special since they
         // require inserting arbitrary function calls and control flow.
diff --git a/winch/codegen/src/isa/aarch64/abi.rs b/winch/codegen/src/isa/aarch64/abi.rs
index 242d2c92b..0617b9568 100644
--- a/winch/codegen/src/isa/aarch64/abi.rs
+++ b/winch/codegen/src/isa/aarch64/abi.rs
@@ -1,7 +1,9 @@
 use super::regs;
 use crate::abi::{align_to, ABIOperand, ABIParams, ABIResults, ABISig, ParamsOrReturns, ABI};
+use crate::codegen::CodeGenError;
 use crate::isa::{reg::Reg, CallingConvention};
 use crate::RegIndexEnv;
+use anyhow::{bail, Result};
 use wasmtime_environ::{WasmHeapType, WasmRefType, WasmValType};
 
 #[derive(Default)]
@@ -29,14 +31,14 @@ impl ABI for Aarch64ABI {
         params: &[WasmValType],
         returns: &[WasmValType],
         call_conv: &CallingConvention,
-    ) -> ABISig {
+    ) -> Result<ABISig> {
         assert!(call_conv.is_apple_aarch64() || call_conv.is_default());
         // The first element tracks the general purpose register index, capped at 7 (x0-x7).
         // The second element tracks the floating point register index, capped at 7 (v0-v7).
         // Follows
         // https://github.com/ARM-software/abi-aa/blob/2021Q1/aapcs64/aapcs64.rst#64parameter-passing
         let mut params_index_env = RegIndexEnv::with_limits_per_class(8, 8);
-        let results = Self::abi_results(returns, call_conv);
+        let results = Self::abi_results(returns, call_conv)?;
         let params =
             ABIParams::from::<_, Self>(params, 0, results.on_stack(), |ty, stack_offset| {
                 Self::to_abi_operand(
@@ -46,12 +48,12 @@ impl ABI for Aarch64ABI {
                     call_conv,
                     ParamsOrReturns::Params,
                 )
-            });
+            })?;
 
-        ABISig::new(*call_conv, params, results)
+        Ok(ABISig::new(*call_conv, params, results))
     }
 
-    fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> ABIResults {
+    fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults> {
         assert!(call_conv.is_apple_aarch64() || call_conv.is_default());
         // Use absolute count for results given that for Winch's
         // default CallingConvention only one register is used for results
@@ -112,7 +114,7 @@ impl Aarch64ABI {
         index_env: &mut RegIndexEnv,
         call_conv: &CallingConvention,
         params_or_returns: ParamsOrReturns,
-    ) -> (ABIOperand, u32) {
+    ) -> Result<(ABIOperand, u32)> {
         let (reg, ty) = match wasm_arg {
             ty @ (WasmValType::I32 | WasmValType::I64) => {
                 (index_env.next_gpr().map(regs::xreg), ty)
@@ -122,7 +124,7 @@ impl Aarch64ABI {
                 (index_env.next_fpr().map(regs::vreg), ty)
             }
 
-            ty => unreachable!("Unsupported argument type {:?}", ty),
+            _ => bail!(CodeGenError::unsupported_wasm_type()),
         };
 
         let ty_size = <Self as ABI>::sizeof(wasm_arg);
@@ -145,9 +147,9 @@ impl Aarch64ABI {
             };
             (arg, next_stack)
         };
-        reg.map_or_else(default, |reg| {
+        Ok(reg.map_or_else(default, |reg| {
             (ABIOperand::reg(reg, *ty, ty_size as u32), stack_offset)
-        })
+        }))
     }
 }
 
@@ -165,14 +167,16 @@ mod tests {
         WasmValType::{self, *},
     };
 
+    use anyhow::Result;
+
     #[test]
-    fn xreg_abi_sig() {
+    fn xreg_abi_sig() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [I32, I64, I32, I64, I32, I32, I64, I32, I64].into(),
             [].into(),
         );
 
-        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), I32, regs::xreg(0));
@@ -184,16 +188,17 @@ mod tests {
         match_reg_arg(params.get(6).unwrap(), I64, regs::xreg(6));
         match_reg_arg(params.get(7).unwrap(), I32, regs::xreg(7));
         match_stack_arg(params.get(8).unwrap(), I64, 0);
+        Ok(())
     }
 
     #[test]
-    fn vreg_abi_sig() {
+    fn vreg_abi_sig() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, F64, F32, F64, F32, F32, F64, F32, F64].into(),
             [].into(),
         );
 
-        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), F32, regs::vreg(0));
@@ -205,16 +210,17 @@ mod tests {
         match_reg_arg(params.get(6).unwrap(), F64, regs::vreg(6));
         match_reg_arg(params.get(7).unwrap(), F32, regs::vreg(7));
         match_stack_arg(params.get(8).unwrap(), F64, 0);
+        Ok(())
     }
 
     #[test]
-    fn mixed_abi_sig() {
+    fn mixed_abi_sig() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, I32, I64, F64, I32, F32, F64, F32, F64].into(),
             [].into(),
         );
 
-        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), F32, regs::vreg(0));
@@ -226,16 +232,17 @@ mod tests {
         match_reg_arg(params.get(6).unwrap(), F64, regs::vreg(3));
         match_reg_arg(params.get(7).unwrap(), F32, regs::vreg(4));
         match_reg_arg(params.get(8).unwrap(), F64, regs::vreg(5));
+        Ok(())
     }
 
     #[test]
-    fn int_abi_sig_multi_returns() {
+    fn int_abi_sig_multi_returns() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [I32, I64, I32, I64, I32, I32].into(),
             [I32, I32, I32].into(),
         );
 
-        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
         let results = sig.results;
 
@@ -249,16 +256,17 @@ mod tests {
         match_stack_arg(results.get(0).unwrap(), I32, 4);
         match_stack_arg(results.get(1).unwrap(), I32, 0);
         match_reg_arg(results.get(2).unwrap(), I32, regs::xreg(0));
+        Ok(())
     }
 
     #[test]
-    fn mixed_abi_sig_multi_returns() {
+    fn mixed_abi_sig_multi_returns() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, I32, I64, F64, I32].into(),
             [I32, F32, I32, F32, I64].into(),
         );
 
-        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = Aarch64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
         let results = sig.results;
 
@@ -273,6 +281,7 @@ mod tests {
         match_stack_arg(results.get(2).unwrap(), I32, 4);
         match_stack_arg(results.get(3).unwrap(), F32, 0);
         match_reg_arg(results.get(4).unwrap(), I64, regs::xreg(0));
+        Ok(())
     }
 
     #[track_caller]
diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs
index a8c17c514..6549e9d5d 100644
--- a/winch/codegen/src/isa/aarch64/masm.rs
+++ b/winch/codegen/src/isa/aarch64/masm.rs
@@ -12,9 +12,9 @@ use crate::{
         CallingConvention,
     },
     masm::{
-        CalleeKind, DivKind, ExtendKind, FloatCmpKind, Imm as I, IntCmpKind,
-        MacroAssembler as Masm, MulWideKind, OperandSize, RegImm, RemKind, RoundingMode, SPOffset,
-        ShiftKind, StackSlot, TrapCode, TruncKind,
+        CalleeKind, DivKind, ExtendKind, FloatCmpKind, Imm as I, IntCmpKind, LoadKind,
+        MacroAssembler as Masm, MemOpKind, MulWideKind, OperandSize, RegImm, RemKind, RoundingMode,
+        SPOffset, ShiftKind, StackSlot, TrapCode, TruncKind,
     },
     stack::TypedReg,
 };
@@ -215,14 +215,26 @@ impl Masm for MacroAssembler {
         src: Self::Address,
         dst: WritableReg,
         size: OperandSize,
-        kind: Option<ExtendKind>,
+        kind: LoadKind,
+        op_kind: MemOpKind,
     ) -> Result<()> {
-        // kind is some if the value is signed
-        // unlike x64, unused bits are set to zero so we don't need to extend
-        if kind.is_some() {
-            self.asm.sload(src, dst, size);
-        } else {
-            self.asm.uload(src, dst, size);
+        match op_kind {
+            MemOpKind::Normal => match kind {
+                LoadKind::Simple => self.asm.uload(src, dst, size),
+                LoadKind::Splat => bail!(CodeGenError::UnimplementedWasmLoadKind),
+                LoadKind::ScalarExtend(extend_kind) => {
+                    if extend_kind.signed() {
+                        self.asm.sload(src, dst, size)
+                    } else {
+                        // unlike x64, unused bits are set to zero so we don't need to extend
+                        self.asm.uload(src, dst, size)
+                    }
+                }
+                LoadKind::VectorExtend(_vector_extend_kind) => {
+                    bail!(CodeGenError::UnimplementedWasmLoadKind)
+                }
+            },
+            MemOpKind::Atomic => bail!(CodeGenError::unimplemented_masm_instruction()),
         }
         Ok(())
     }
diff --git a/winch/codegen/src/isa/aarch64/mod.rs b/winch/codegen/src/isa/aarch64/mod.rs
index ed75be845..3a90e77e1 100644
--- a/winch/codegen/src/isa/aarch64/mod.rs
+++ b/winch/codegen/src/isa/aarch64/mod.rs
@@ -97,7 +97,7 @@ impl TargetIsa for Aarch64 {
         let mut body = body.get_binary_reader();
         let mut masm = Aarch64Masm::new(pointer_bytes, self.shared_flags.clone())?;
         let stack = Stack::new();
-        let abi_sig = wasm_sig::<abi::Aarch64ABI>(sig);
+        let abi_sig = wasm_sig::<abi::Aarch64ABI>(sig)?;
 
         let env = FuncEnv::new(
             &vmoffsets,
diff --git a/winch/codegen/src/isa/x64/abi.rs b/winch/codegen/src/isa/x64/abi.rs
index de60ee1f5..d310a4dd3 100644
--- a/winch/codegen/src/isa/x64/abi.rs
+++ b/winch/codegen/src/isa/x64/abi.rs
@@ -1,9 +1,11 @@
 use super::regs;
 use crate::{
     abi::{align_to, ABIOperand, ABIParams, ABIResults, ABISig, ParamsOrReturns, ABI},
+    codegen::CodeGenError,
     isa::{reg::Reg, CallingConvention},
     RegIndexEnv,
 };
+use anyhow::{bail, Result};
 use wasmtime_environ::{WasmHeapType, WasmRefType, WasmValType};
 
 #[derive(Default)]
@@ -39,7 +41,7 @@ impl ABI for X64ABI {
         params: &[WasmValType],
         returns: &[WasmValType],
         call_conv: &CallingConvention,
-    ) -> ABISig {
+    ) -> Result<ABISig> {
         assert!(call_conv.is_fastcall() || call_conv.is_systemv() || call_conv.is_default());
         let is_fastcall = call_conv.is_fastcall();
         // In the fastcall calling convention, the callee gets a contiguous
@@ -52,7 +54,7 @@ impl ABI for X64ABI {
             (0, RegIndexEnv::with_limits_per_class(6, 8))
         };
 
-        let results = Self::abi_results(returns, call_conv);
+        let results = Self::abi_results(returns, call_conv)?;
         let params = ABIParams::from::<_, Self>(
             params,
             params_stack_offset,
@@ -66,12 +68,12 @@ impl ABI for X64ABI {
                     ParamsOrReturns::Params,
                 )
             },
-        );
+        )?;
 
-        ABISig::new(*call_conv, params, results)
+        Ok(ABISig::new(*call_conv, params, results))
     }
 
-    fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> ABIResults {
+    fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults> {
         assert!(call_conv.is_default() || call_conv.is_fastcall() || call_conv.is_systemv());
         // Use absolute count for results given that for Winch's
         // default CallingConvention only one register is used for results
@@ -137,14 +139,14 @@ impl X64ABI {
         index_env: &mut RegIndexEnv,
         call_conv: &CallingConvention,
         params_or_returns: ParamsOrReturns,
-    ) -> (ABIOperand, u32) {
+    ) -> Result<(ABIOperand, u32)> {
         let (reg, ty) = match wasm_arg {
             ty @ WasmValType::Ref(rt) => match rt.heap_type {
                 WasmHeapType::Func | WasmHeapType::Extern => (
                     Self::int_reg_for(index_env.next_gpr(), call_conv, params_or_returns),
                     ty,
                 ),
-                ht => unimplemented!("Support for WasmHeapType: {ht}"),
+                _ => bail!(CodeGenError::unsupported_wasm_type()),
             },
 
             ty @ (WasmValType::I32 | WasmValType::I64) => (
@@ -187,9 +189,9 @@ impl X64ABI {
             (arg, next_stack)
         };
 
-        reg.map_or_else(default, |reg| {
+        Ok(reg.map_or_else(default, |reg| {
             (ABIOperand::reg(reg, *ty, ty_size as u32), stack_offset)
-        })
+        }))
     }
 
     fn int_reg_for(
@@ -280,17 +282,20 @@ mod tests {
         abi::{ABIOperand, ABI},
         isa::{reg::Reg, x64::regs, CallingConvention},
     };
+
+    use anyhow::Result;
+
     use wasmtime_environ::{
         WasmFuncType,
         WasmValType::{self, *},
     };
 
     #[test]
-    fn int_abi_sig() {
+    fn int_abi_sig() -> Result<()> {
         let wasm_sig =
             WasmFuncType::new([I32, I64, I32, I64, I32, I32, I64, I32].into(), [].into());
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), I32, regs::rdi());
@@ -301,16 +306,17 @@ mod tests {
         match_reg_arg(params.get(5).unwrap(), I32, regs::r9());
         match_stack_arg(params.get(6).unwrap(), I64, 0);
         match_stack_arg(params.get(7).unwrap(), I32, 8);
+        Ok(())
     }
 
     #[test]
-    fn int_abi_sig_multi_returns() {
+    fn int_abi_sig_multi_returns() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [I32, I64, I32, I64, I32, I32, I64, I32].into(),
             [I32, I32, I32].into(),
         );
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
         let results = sig.results;
 
@@ -326,16 +332,17 @@ mod tests {
         match_stack_arg(results.get(0).unwrap(), I32, 4);
         match_stack_arg(results.get(1).unwrap(), I32, 0);
         match_reg_arg(results.get(2).unwrap(), I32, regs::rax());
+        Ok(())
     }
 
     #[test]
-    fn float_abi_sig() {
+    fn float_abi_sig() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, F64, F32, F64, F32, F32, F64, F32, F64].into(),
             [].into(),
         );
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), F32, regs::xmm0());
@@ -347,16 +354,17 @@ mod tests {
         match_reg_arg(params.get(6).unwrap(), F64, regs::xmm6());
         match_reg_arg(params.get(7).unwrap(), F32, regs::xmm7());
         match_stack_arg(params.get(8).unwrap(), F64, 0);
+        Ok(())
     }
 
     #[test]
-    fn vector_abi_sig() {
+    fn vector_abi_sig() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [V128, V128, V128, V128, V128, V128, V128, V128, V128, V128].into(),
             [].into(),
         );
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), V128, regs::xmm0());
@@ -369,28 +377,30 @@ mod tests {
         match_reg_arg(params.get(7).unwrap(), V128, regs::xmm7());
         match_stack_arg(params.get(8).unwrap(), V128, 0);
         match_stack_arg(params.get(9).unwrap(), V128, 16);
+        Ok(())
     }
 
     #[test]
-    fn vector_abi_sig_multi_returns() {
+    fn vector_abi_sig_multi_returns() -> Result<()> {
         let wasm_sig = WasmFuncType::new([].into(), [V128, V128, V128].into());
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let results = sig.results;
 
         match_stack_arg(results.get(0).unwrap(), V128, 16);
         match_stack_arg(results.get(1).unwrap(), V128, 0);
         match_reg_arg(results.get(2).unwrap(), V128, regs::xmm0());
+        Ok(())
     }
 
     #[test]
-    fn mixed_abi_sig() {
+    fn mixed_abi_sig() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, I32, I64, F64, I32, F32, F64, F32, F64].into(),
             [].into(),
         );
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::Default)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), F32, regs::xmm0());
@@ -402,16 +412,17 @@ mod tests {
         match_reg_arg(params.get(6).unwrap(), F64, regs::xmm3());
         match_reg_arg(params.get(7).unwrap(), F32, regs::xmm4());
         match_reg_arg(params.get(8).unwrap(), F64, regs::xmm5());
+        Ok(())
     }
 
     #[test]
-    fn system_v_call_conv() {
+    fn system_v_call_conv() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, I32, I64, F64, I32, F32, F64, F32, F64].into(),
             [].into(),
         );
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::SystemV);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::SystemV)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), F32, regs::xmm0());
@@ -423,16 +434,17 @@ mod tests {
         match_reg_arg(params.get(6).unwrap(), F64, regs::xmm3());
         match_reg_arg(params.get(7).unwrap(), F32, regs::xmm4());
         match_reg_arg(params.get(8).unwrap(), F64, regs::xmm5());
+        Ok(())
     }
 
     #[test]
-    fn fastcall_call_conv() {
+    fn fastcall_call_conv() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, I32, I64, F64, I32, F32, F64, F32, F64].into(),
             [].into(),
         );
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::WindowsFastcall);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::WindowsFastcall)?;
         let params = sig.params;
 
         match_reg_arg(params.get(0).unwrap(), F32, regs::xmm0());
@@ -441,16 +453,17 @@ mod tests {
         match_reg_arg(params.get(3).unwrap(), F64, regs::xmm3());
         match_stack_arg(params.get(4).unwrap(), I32, 32);
         match_stack_arg(params.get(5).unwrap(), F32, 40);
+        Ok(())
     }
 
     #[test]
-    fn fastcall_call_conv_multi_returns() {
+    fn fastcall_call_conv_multi_returns() -> Result<()> {
         let wasm_sig = WasmFuncType::new(
             [F32, I32, I64, F64, I32, F32, F64, F32, F64].into(),
             [I32, F32, I32, F32, I64].into(),
         );
 
-        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::WindowsFastcall);
+        let sig = X64ABI::sig(&wasm_sig, &CallingConvention::WindowsFastcall)?;
         let params = sig.params;
         let results = sig.results;
 
@@ -468,6 +481,7 @@ mod tests {
         match_stack_arg(results.get(2).unwrap(), I32, 4);
         match_stack_arg(results.get(3).unwrap(), F32, 8);
         match_stack_arg(results.get(4).unwrap(), I64, 12);
+        Ok(())
     }
 
     #[track_caller]
diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs
index 4cae0f512..27f20ee84 100644
--- a/winch/codegen/src/isa/x64/asm.rs
+++ b/winch/codegen/src/isa/x64/asm.rs
@@ -3,7 +3,8 @@
 use crate::{
     isa::{reg::Reg, CallingConvention},
     masm::{
-        DivKind, ExtendKind, IntCmpKind, MulWideKind, OperandSize, RemKind, RoundingMode, ShiftKind,
+        DivKind, ExtendKind, IntCmpKind, MulWideKind, OperandSize, RemKind, RoundingMode,
+        ShiftKind, VectorExtendKind,
     },
 };
 use cranelift_codegen::{
@@ -15,8 +16,8 @@ use cranelift_codegen::{
         unwind::UnwindInst,
         x64::{
             args::{
-                self, AluRmiROpcode, Amode, CmpOpcode, DivSignedness, ExtMode, FromWritableReg,
-                Gpr, GprMem, GprMemImm, Imm8Gpr, Imm8Reg, RegMem, RegMemImm,
+                self, AluRmiROpcode, Amode, AvxOpcode, CmpOpcode, DivSignedness, ExtMode,
+                FromWritableReg, Gpr, GprMem, GprMemImm, Imm8Gpr, Imm8Reg, RegMem, RegMemImm,
                 ShiftKind as CraneliftShiftKind, SseOpcode, SyntheticAmode, WritableGpr,
                 WritableXmm, Xmm, XmmMem, XmmMemAligned, CC,
             },
@@ -471,6 +472,90 @@ impl Assembler {
         });
     }
 
+    /// Vector load and extend.
+    pub fn xmm_vpmov_mr(
+        &mut self,
+        src: &Address,
+        dst: WritableReg,
+        ext: VectorExtendKind,
+        flags: MemFlags,
+    ) {
+        assert!(dst.to_reg().is_float());
+
+        let op = match ext {
+            VectorExtendKind::V128Extend8x8S => AvxOpcode::Vpmovsxbw,
+            VectorExtendKind::V128Extend8x8U => AvxOpcode::Vpmovzxbw,
+            VectorExtendKind::V128Extend16x4S => AvxOpcode::Vpmovsxwd,
+            VectorExtendKind::V128Extend16x4U => AvxOpcode::Vpmovzxwd,
+            VectorExtendKind::V128Extend32x2S => AvxOpcode::Vpmovsxdq,
+            VectorExtendKind::V128Extend32x2U => AvxOpcode::Vpmovzxdq,
+        };
+
+        let src = Self::to_synthetic_amode(
+            src,
+            &mut self.pool,
+            &mut self.constants,
+            &mut self.buffer,
+            flags,
+        );
+
+        self.emit(Inst::XmmUnaryRmRVex {
+            op,
+            src: XmmMem::unwrap_new(RegMem::mem(src)),
+            dst: dst.to_reg().into(),
+        });
+    }
+
+    /// Vector load and broadcast.
+    pub fn xmm_vpbroadcast_mr(
+        &mut self,
+        src: &Address,
+        dst: WritableReg,
+        size: OperandSize,
+        flags: MemFlags,
+    ) {
+        assert!(dst.to_reg().is_float());
+
+        let src = Self::to_synthetic_amode(
+            src,
+            &mut self.pool,
+            &mut self.constants,
+            &mut self.buffer,
+            flags,
+        );
+
+        let op = match size {
+            OperandSize::S8 => AvxOpcode::Vpbroadcastb,
+            OperandSize::S16 => AvxOpcode::Vpbroadcastw,
+            OperandSize::S32 => AvxOpcode::Vpbroadcastd,
+            _ => unimplemented!(),
+        };
+
+        self.emit(Inst::XmmUnaryRmRVex {
+            op,
+            src: XmmMem::unwrap_new(RegMem::mem(src)),
+            dst: dst.to_reg().into(),
+        });
+    }
+
+    /// Shuffle of bytes in vector.
+    pub fn xmm_vpshuf_rr(&mut self, src: Reg, dst: WritableReg, mask: u8, size: OperandSize) {
+        assert!(src.is_float() && dst.to_reg().is_float());
+
+        let op = match size {
+            OperandSize::S16 => AvxOpcode::Vpshuflw,
+            OperandSize::S64 => AvxOpcode::Vpshufd,
+            _ => unimplemented!(),
+        };
+
+        self.emit(Inst::XmmUnaryRmRImmVex {
+            op,
+            src: XmmMem::from(Xmm::from(src)),
+            imm: mask,
+            dst: dst.to_reg().into(),
+        });
+    }
+
     /// Single and double precision floating point store.
     pub fn xmm_mov_rm(&mut self, src: Reg, dst: &Address, size: OperandSize, flags: MemFlags) {
         use OperandSize::*;
diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs
index 6b16203f2..fa43eeddb 100644
--- a/winch/codegen/src/isa/x64/masm.rs
+++ b/winch/codegen/src/isa/x64/masm.rs
@@ -7,9 +7,9 @@ use super::{
 use anyhow::{anyhow, bail, Result};
 
 use crate::masm::{
-    DivKind, ExtendKind, FloatCmpKind, Imm as I, IntCmpKind, MacroAssembler as Masm, MulWideKind,
-    OperandSize, RegImm, RemKind, RoundingMode, ShiftKind, TrapCode, TruncKind, TRUSTED_FLAGS,
-    UNTRUSTED_FLAGS,
+    DivKind, ExtendKind, FloatCmpKind, Imm as I, IntCmpKind, LoadKind, MacroAssembler as Masm,
+    MemOpKind, MulWideKind, OperandSize, RegImm, RemKind, RoundingMode, ShiftKind, TrapCode,
+    TruncKind, TRUSTED_FLAGS, UNTRUSTED_FLAGS,
 };
 use crate::{
     abi::{self, align_to, calculate_frame_adjustment, LocalSlot},
@@ -30,10 +30,12 @@ use crate::{
 use cranelift_codegen::{
     binemit::CodeOffset,
     ir::{MemFlags, RelSourceLoc, SourceLoc},
-    isa::unwind::UnwindInst,
-    isa::x64::{
-        args::{ExtMode, CC},
-        settings as x64_settings,
+    isa::{
+        unwind::UnwindInst,
+        x64::{
+            args::{ExtMode, CC},
+            settings as x64_settings,
+        },
     },
     settings, Final, MachBufferFinalized, MachLabel,
 };
@@ -279,14 +281,59 @@ impl Masm for MacroAssembler {
         src: Self::Address,
         dst: WritableReg,
         size: OperandSize,
-        kind: Option<ExtendKind>,
+        kind: LoadKind,
+        op_kind: MemOpKind,
     ) -> Result<()> {
-        if let Some(ext) = kind {
-            self.asm.movsx_mr(&src, dst, ext, UNTRUSTED_FLAGS);
-            Ok(())
-        } else {
-            self.load_impl::<Self>(src, dst, size, UNTRUSTED_FLAGS)
+        if op_kind == MemOpKind::Atomic && size == OperandSize::S128 {
+            // TODO: handle 128bits atomic loads
+            bail!(CodeGenError::unexpected_operand_size())
+        }
+
+        match kind {
+            // The guarantees of the x86-64 memory model ensure that `SeqCst`
+            // loads are equivalent to normal loads.
+            LoadKind::ScalarExtend(ext) => self.asm.movsx_mr(&src, dst, ext, UNTRUSTED_FLAGS),
+            LoadKind::Simple => self.load_impl::<Self>(src, dst, size, UNTRUSTED_FLAGS)?,
+            LoadKind::VectorExtend(ext) => match op_kind {
+                MemOpKind::Normal => {
+                    if self.flags.has_avx() {
+                        self.asm.xmm_vpmov_mr(&src, dst, ext, UNTRUSTED_FLAGS)
+                    } else {
+                        bail!(CodeGenError::UnimplementedForNoAvx)
+                    }
+                }
+                MemOpKind::Atomic => bail!(CodeGenError::unimplemented_masm_instruction()),
+            },
+            LoadKind::Splat => {
+                match op_kind {
+                    MemOpKind::Normal => {
+                        if self.flags.has_avx() {
+                            if size == OperandSize::S64 {
+                                self.asm
+                                    .xmm_mov_mr(&src, dst, OperandSize::S64, UNTRUSTED_FLAGS);
+                                // Results in the first 4 bytes and second 4 bytes being
+                                // swapped and then the swapped bytes being copied.
+                                // [d0, d1, d2, d3, d4, d5, d6, d7, ...] yields
+                                // [d4, d5, d6, d7, d0, d1, d2, d3, d4, d5, d6, d7, d0, d1, d2, d3].
+                                self.asm.xmm_vpshuf_rr(
+                                    dst.to_reg(),
+                                    dst,
+                                    0b0100_0100,
+                                    OperandSize::S64,
+                                );
+                            } else {
+                                self.asm
+                                    .xmm_vpbroadcast_mr(&src, dst, size, UNTRUSTED_FLAGS);
+                            }
+                        } else {
+                            bail!(CodeGenError::UnimplementedForNoAvx)
+                        }
+                    }
+                    MemOpKind::Atomic => bail!(CodeGenError::unimplemented_masm_instruction()),
+                }
+            }
         }
+        Ok(())
     }
 
     fn sp_offset(&self) -> Result<SPOffset> {
diff --git a/winch/codegen/src/isa/x64/mod.rs b/winch/codegen/src/isa/x64/mod.rs
index 3c976c631..846fe5d0c 100644
--- a/winch/codegen/src/isa/x64/mod.rs
+++ b/winch/codegen/src/isa/x64/mod.rs
@@ -107,7 +107,7 @@ impl TargetIsa for X64 {
         )?;
         let stack = Stack::new();
 
-        let abi_sig = wasm_sig::<abi::X64ABI>(sig);
+        let abi_sig = wasm_sig::<abi::X64ABI>(sig)?;
 
         let env = FuncEnv::new(
             &vmoffsets,
diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs
index 42afba43f..6b9dbbb60 100644
--- a/winch/codegen/src/masm.rs
+++ b/winch/codegen/src/masm.rs
@@ -38,6 +38,14 @@ impl RemKind {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub(crate) enum MemOpKind {
+    /// An atomic memory operation with SeqCst memory ordering.
+    Atomic,
+    /// A memory operation with no memory ordering constraint.
+    Normal,
+}
+
 #[derive(Eq, PartialEq)]
 pub(crate) enum MulWideKind {
     Signed,
@@ -226,6 +234,36 @@ impl ExtendKind {
     }
 }
 
+/// Kinds of vector extends in WebAssembly. Each MacroAssembler implementation
+/// is responsible for emitting the correct sequence of instructions when
+/// lowering to machine code.
+pub(crate) enum VectorExtendKind {
+    /// Sign extends eight 8 bit integers to eight 16 bit lanes.
+    V128Extend8x8S,
+    /// Zero extends eight 8 bit integers to eight 16 bit lanes.
+    V128Extend8x8U,
+    /// Sign extends four 16 bit integers to four 32 bit lanes.
+    V128Extend16x4S,
+    /// Zero extends four 16 bit integers to four 32 bit lanes.
+    V128Extend16x4U,
+    /// Sign extends two 32 bit integers to two 64 bit lanes.
+    V128Extend32x2S,
+    /// Zero extends two 32 bit integers to two 64 bit lanes.
+    V128Extend32x2U,
+}
+
+/// Kinds of behavior supported by Wasm loads.
+pub(crate) enum LoadKind {
+    /// Do not extend or splat.
+    Simple,
+    /// Duplicate value into vector lanes.
+    Splat,
+    /// Scalar (non-vector) extend.
+    ScalarExtend(ExtendKind),
+    /// Vector extend.
+    VectorExtend(VectorExtendKind),
+}
+
 /// Operand size, in bits.
 #[derive(Copy, Debug, Clone, Eq, PartialEq)]
 pub(crate) enum OperandSize {
@@ -646,7 +684,8 @@ pub(crate) trait MacroAssembler {
         src: Self::Address,
         dst: WritableReg,
         size: OperandSize,
-        kind: Option<ExtendKind>,
+        kind: LoadKind,
+        op_kind: MemOpKind,
     ) -> Result<()>;
 
     /// Alias for `MacroAssembler::load` with the operand size corresponding
diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs
index 86e8bfd56..85b9aa827 100644
--- a/winch/codegen/src/visitor.rs
+++ b/winch/codegen/src/visitor.rs
@@ -9,8 +9,9 @@ use crate::codegen::{
     control_index, Callee, CodeGen, CodeGenError, ControlStackFrame, Emission, FnCall,
 };
 use crate::masm::{
-    DivKind, ExtendKind, FloatCmpKind, IntCmpKind, MacroAssembler, MemMoveDirection, MulWideKind,
-    OperandSize, RegImm, RemKind, RoundingMode, SPOffset, ShiftKind, TruncKind,
+    DivKind, ExtendKind, FloatCmpKind, IntCmpKind, LoadKind, MacroAssembler, MemMoveDirection,
+    MemOpKind, MulWideKind, OperandSize, RegImm, RemKind, RoundingMode, SPOffset, ShiftKind,
+    TruncKind, VectorExtendKind,
 };
 
 use crate::reg::{writable, Reg};
@@ -253,7 +254,23 @@ macro_rules! def_unsupported {
     (emit I64Sub128 $($rest:tt)*) => {};
     (emit I64MulWideS $($rest:tt)*) => {};
     (emit I64MulWideU $($rest:tt)*) => {};
-
+    (emit I32AtomicLoad8U $($rest:tt)*) => {};
+    (emit I32AtomicLoad16U $($rest:tt)*) => {};
+    (emit I32AtomicLoad $($rest:tt)*) => {};
+    (emit I64AtomicLoad8U $($rest:tt)*) => {};
+    (emit I64AtomicLoad16U $($rest:tt)*) => {};
+    (emit I64AtomicLoad32U $($rest:tt)*) => {};
+    (emit I64AtomicLoad $($rest:tt)*) => {};
+    (emit V128Load8x8S $($rest:tt)*) => {};
+    (emit V128Load8x8U $($rest:tt)*) => {};
+    (emit V128Load16x4S $($rest:tt)*) => {};
+    (emit V128Load16x4U $($rest:tt)*) => {};
+    (emit V128Load32x2S $($rest:tt)*) => {};
+    (emit V128Load32x2U $($rest:tt)*) => {};
+    (emit V128Load8Splat $($rest:tt)*) => {};
+    (emit V128Load16Splat $($rest:tt)*) => {};
+    (emit V128Load32Splat $($rest:tt)*) => {};
+    (emit V128Load64Splat $($rest:tt)*) => {};
     (emit $unsupported:tt $($rest:tt)*) => {$($rest)*};
 }
 
@@ -477,7 +494,7 @@ where
             &mut self.context,
             OperandSize::S32,
             |env, cx, masm| {
-                let builtin = env.builtins.floor_f32::<M::ABI>();
+                let builtin = env.builtins.floor_f32::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -490,7 +507,7 @@ where
             &mut self.context,
             OperandSize::S64,
             |env, cx, masm| {
-                let builtin = env.builtins.floor_f64::<M::ABI>();
+                let builtin = env.builtins.floor_f64::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -503,7 +520,7 @@ where
             &mut self.context,
             OperandSize::S32,
             |env, cx, masm| {
-                let builtin = env.builtins.ceil_f32::<M::ABI>();
+                let builtin = env.builtins.ceil_f32::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -516,7 +533,7 @@ where
             &mut self.context,
             OperandSize::S64,
             |env, cx, masm| {
-                let builtin = env.builtins.ceil_f64::<M::ABI>();
+                let builtin = env.builtins.ceil_f64::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -529,7 +546,7 @@ where
             &mut self.context,
             OperandSize::S32,
             |env, cx, masm| {
-                let builtin = env.builtins.nearest_f32::<M::ABI>();
+                let builtin = env.builtins.nearest_f32::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -542,7 +559,7 @@ where
             &mut self.context,
             OperandSize::S64,
             |env, cx, masm| {
-                let builtin = env.builtins.nearest_f64::<M::ABI>();
+                let builtin = env.builtins.nearest_f64::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -555,7 +572,7 @@ where
             &mut self.context,
             OperandSize::S32,
             |env, cx, masm| {
-                let builtin = env.builtins.trunc_f32::<M::ABI>();
+                let builtin = env.builtins.trunc_f32::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -568,7 +585,7 @@ where
             &mut self.context,
             OperandSize::S64,
             |env, cx, masm| {
-                let builtin = env.builtins.trunc_f64::<M::ABI>();
+                let builtin = env.builtins.trunc_f64::<M::ABI>()?;
                 FnCall::emit::<M>(env, masm, cx, Callee::Builtin(builtin))
             },
         )
@@ -1381,7 +1398,7 @@ where
             .stack
             .insert_many(at, &[table.try_into()?, elem.try_into()?]);
 
-        let builtin = self.env.builtins.table_init::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.table_init::<M::ABI, M::Ptr>()?;
         FnCall::emit::<M>(
             &mut self.env,
             self.masm,
@@ -1397,7 +1414,7 @@ where
             .stack
             .insert_many(at, &[dst.try_into()?, src.try_into()?]);
 
-        let builtin = self.env.builtins.table_copy::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.table_copy::<M::ABI, M::Ptr>()?;
         FnCall::emit::<M>(
             &mut self.env,
             self.masm,
@@ -1422,7 +1439,7 @@ where
         let table_index = TableIndex::from_u32(table);
         let table_ty = self.env.table(table_index);
         let builtin = match table_ty.ref_type.heap_type {
-            WasmHeapType::Func => self.env.builtins.table_grow_func_ref::<M::ABI, M::Ptr>(),
+            WasmHeapType::Func => self.env.builtins.table_grow_func_ref::<M::ABI, M::Ptr>()?,
             _ => bail!(CodeGenError::unsupported_wasm_type()),
         };
 
@@ -1464,7 +1481,7 @@ where
             CodeGenError::unsupported_wasm_type()
         );
 
-        let builtin = self.env.builtins.table_fill_func_ref::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.table_fill_func_ref::<M::ABI, M::Ptr>()?;
 
         let at = self.context.stack.ensure_index_at(3)?;
 
@@ -1514,7 +1531,7 @@ where
     }
 
     fn visit_elem_drop(&mut self, index: u32) -> Self::Output {
-        let elem_drop = self.env.builtins.elem_drop::<M::ABI, M::Ptr>();
+        let elem_drop = self.env.builtins.elem_drop::<M::ABI, M::Ptr>()?;
         self.context.stack.extend([index.try_into()?]);
         FnCall::emit::<M>(
             &mut self.env,
@@ -1530,7 +1547,7 @@ where
         self.context
             .stack
             .insert_many(at, &[mem.try_into()?, data_index.try_into()?]);
-        let builtin = self.env.builtins.memory_init::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.memory_init::<M::ABI, M::Ptr>()?;
         FnCall::emit::<M>(
             &mut self.env,
             self.masm,
@@ -1554,7 +1571,7 @@ where
         let at = self.context.stack.ensure_index_at(4)?;
         self.context.stack.insert_many(at, &[dst_mem.try_into()?]);
 
-        let builtin = self.env.builtins.memory_copy::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.memory_copy::<M::ABI, M::Ptr>()?;
 
         FnCall::emit::<M>(
             &mut self.env,
@@ -1570,7 +1587,7 @@ where
 
         self.context.stack.insert_many(at, &[mem.try_into()?]);
 
-        let builtin = self.env.builtins.memory_fill::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.memory_fill::<M::ABI, M::Ptr>()?;
         FnCall::emit::<M>(
             &mut self.env,
             self.masm,
@@ -1593,7 +1610,7 @@ where
         self.context.stack.extend([mem.try_into()?]);
 
         let heap = self.env.resolve_heap(MemoryIndex::from_u32(mem));
-        let builtin = self.env.builtins.memory32_grow::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.memory32_grow::<M::ABI, M::Ptr>()?;
         FnCall::emit::<M>(
             &mut self.env,
             self.masm,
@@ -1621,7 +1638,7 @@ where
     fn visit_data_drop(&mut self, data_index: u32) -> Self::Output {
         self.context.stack.extend([data_index.try_into()?]);
 
-        let builtin = self.env.builtins.data_drop::<M::ABI, M::Ptr>();
+        let builtin = self.env.builtins.data_drop::<M::ABI, M::Ptr>()?;
         FnCall::emit::<M>(
             &mut self.env,
             self.masm,
@@ -1695,7 +1712,7 @@ where
 
         let top = {
             let top = self.context.without::<Result<TypedReg>, M, _>(
-                frame.results::<M>().regs(),
+                frame.results::<M>()?.regs(),
                 self.masm,
                 |ctx, masm| ctx.pop_to_reg(masm, None),
             )??;
@@ -1730,7 +1747,7 @@ where
         // Emit instructions to balance the machine stack if the frame has
         // a different offset.
         let current_sp_offset = self.masm.sp_offset()?;
-        let results_size = frame.results::<M>().size();
+        let results_size = frame.results::<M>()?.size();
         let state = frame.stack_state();
         let (label, cmp, needs_cleanup) = if current_sp_offset > state.target_offset {
             (self.masm.get_label()?, IntCmpKind::Eq, true)
@@ -1776,7 +1793,7 @@ where
 
         let default_index = control_index(targets.default(), self.control_frames.len())?;
         let default_frame = &mut self.control_frames[default_index];
-        let default_result = default_frame.results::<M>();
+        let default_result = default_frame.results::<M>()?;
 
         let (index, tmp) = {
             let index_and_tmp = self.context.without::<Result<(TypedReg, _)>, M, _>(
@@ -1921,7 +1938,13 @@ where
     }
 
     fn visit_i32_load(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::I32, OperandSize::S32, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I32,
+            OperandSize::S32,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_i32_load8_s(&mut self, memarg: MemArg) -> Self::Output {
@@ -1929,12 +1952,19 @@ where
             &memarg,
             WasmValType::I32,
             OperandSize::S8,
-            Some(ExtendKind::I32Extend8S),
+            LoadKind::ScalarExtend(ExtendKind::I32Extend8S),
+            MemOpKind::Normal,
         )
     }
 
     fn visit_i32_load8_u(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::I32, OperandSize::S8, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I32,
+            OperandSize::S8,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_i32_load16_s(&mut self, memarg: MemArg) -> Self::Output {
@@ -1942,12 +1972,19 @@ where
             &memarg,
             WasmValType::I32,
             OperandSize::S16,
-            Some(ExtendKind::I32Extend16S),
+            LoadKind::ScalarExtend(ExtendKind::I32Extend16S),
+            MemOpKind::Normal,
         )
     }
 
     fn visit_i32_load16_u(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::I32, OperandSize::S16, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I32,
+            OperandSize::S16,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_i32_store(&mut self, memarg: MemArg) -> Self::Output {
@@ -1967,16 +2004,29 @@ where
             &memarg,
             WasmValType::I64,
             OperandSize::S8,
-            Some(ExtendKind::I64Extend8S),
+            LoadKind::ScalarExtend(ExtendKind::I64Extend8S),
+            MemOpKind::Normal,
         )
     }
 
     fn visit_i64_load8_u(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::I64, OperandSize::S8, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S8,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_i64_load16_u(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::I64, OperandSize::S16, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S16,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_i64_load16_s(&mut self, memarg: MemArg) -> Self::Output {
@@ -1984,12 +2034,19 @@ where
             &memarg,
             WasmValType::I64,
             OperandSize::S16,
-            Some(ExtendKind::I64Extend16S),
+            LoadKind::ScalarExtend(ExtendKind::I64Extend16S),
+            MemOpKind::Normal,
         )
     }
 
     fn visit_i64_load32_u(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::I64, OperandSize::S32, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S32,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_i64_load32_s(&mut self, memarg: MemArg) -> Self::Output {
@@ -1997,12 +2054,19 @@ where
             &memarg,
             WasmValType::I64,
             OperandSize::S32,
-            Some(ExtendKind::I64Extend32S),
+            LoadKind::ScalarExtend(ExtendKind::I64Extend32S),
+            MemOpKind::Normal,
         )
     }
 
     fn visit_i64_load(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::I64, OperandSize::S64, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S64,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_i64_store(&mut self, memarg: MemArg) -> Self::Output {
@@ -2022,7 +2086,13 @@ where
     }
 
     fn visit_f32_load(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::F32, OperandSize::S32, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::F32,
+            OperandSize::S32,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_f32_store(&mut self, memarg: MemArg) -> Self::Output {
@@ -2030,7 +2100,13 @@ where
     }
 
     fn visit_f64_load(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::F64, OperandSize::S64, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::F64,
+            OperandSize::S64,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_f64_store(&mut self, memarg: MemArg) -> Self::Output {
@@ -2139,6 +2215,76 @@ where
         self.masm.mul_wide(&mut self.context, MulWideKind::Unsigned)
     }
 
+    fn visit_i32_atomic_load8_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I32,
+            OperandSize::S8,
+            LoadKind::Simple,
+            MemOpKind::Atomic,
+        )
+    }
+
+    fn visit_i32_atomic_load16_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I32,
+            OperandSize::S16,
+            LoadKind::Simple,
+            MemOpKind::Atomic,
+        )
+    }
+
+    fn visit_i32_atomic_load(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I32,
+            OperandSize::S32,
+            LoadKind::Simple,
+            MemOpKind::Atomic,
+        )
+    }
+
+    fn visit_i64_atomic_load8_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S8,
+            LoadKind::Simple,
+            MemOpKind::Atomic,
+        )
+    }
+
+    fn visit_i64_atomic_load16_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S16,
+            LoadKind::Simple,
+            MemOpKind::Atomic,
+        )
+    }
+
+    fn visit_i64_atomic_load32_u(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S32,
+            LoadKind::Simple,
+            MemOpKind::Atomic,
+        )
+    }
+
+    fn visit_i64_atomic_load(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::I64,
+            OperandSize::S64,
+            LoadKind::Simple,
+            MemOpKind::Atomic,
+        )
+    }
+
     wasmparser::for_each_visit_operator!(def_unsupported);
 }
 
@@ -2153,13 +2299,119 @@ where
     }
 
     fn visit_v128_load(&mut self, memarg: MemArg) -> Self::Output {
-        self.emit_wasm_load(&memarg, WasmValType::V128, OperandSize::S128, None)
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S128,
+            LoadKind::Simple,
+            MemOpKind::Normal,
+        )
     }
 
     fn visit_v128_store(&mut self, memarg: MemArg) -> Self::Output {
         self.emit_wasm_store(&memarg, OperandSize::S128)
     }
 
+    fn visit_v128_load8x8_s(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S64,
+            LoadKind::VectorExtend(VectorExtendKind::V128Extend8x8S),
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load8x8_u(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S64,
+            LoadKind::VectorExtend(VectorExtendKind::V128Extend8x8U),
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load16x4_s(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S64,
+            LoadKind::VectorExtend(VectorExtendKind::V128Extend16x4S),
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load16x4_u(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S64,
+            LoadKind::VectorExtend(VectorExtendKind::V128Extend16x4U),
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load32x2_s(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S64,
+            LoadKind::VectorExtend(VectorExtendKind::V128Extend32x2S),
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load32x2_u(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S64,
+            LoadKind::VectorExtend(VectorExtendKind::V128Extend32x2U),
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load8_splat(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S8,
+            LoadKind::Splat,
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load16_splat(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S16,
+            LoadKind::Splat,
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load32_splat(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S32,
+            LoadKind::Splat,
+            MemOpKind::Normal,
+        )
+    }
+
+    fn visit_v128_load64_splat(&mut self, memarg: MemArg) -> Self::Output {
+        self.emit_wasm_load(
+            &memarg,
+            WasmValType::V128,
+            OperandSize::S64,
+            LoadKind::Splat,
+            MemOpKind::Normal,
+        )
+    }
+
     wasmparser::for_each_visit_simd_operator!(def_unsupported);
 }
 


Test patch:
diff --git a/cranelift/filetests/filetests/isa/pulley32/br_table.clif b/cranelift/filetests/filetests/isa/pulley32/br_table.clif
index c9f6844ad..7ff448c7e 100644
--- a/cranelift/filetests/filetests/isa/pulley32/br_table.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/br_table.clif
@@ -40,7 +40,7 @@ block5(v5: i32):
 ;   xconst8 x5, 2
 ;   jump label7
 ; block5:
-;   xconst8 x5, 1
+;   xone x5
 ;   jump label7
 ; block6:
 ;   xconst8 x5, 4
@@ -55,14 +55,14 @@ block5(v5: i32):
 ; 0x1d    // target = 0x27
 ; 0x19    // target = 0x27
 ; 0xd    // target = 0x1f
-; 0x21    // target = 0x37
+; 0x20    // target = 0x36
 ; jump 0xd    // target = 0x27
 ; xconst8 x5, 3
-; jump 0x18    // target = 0x3a
+; jump 0x17    // target = 0x39
 ; xconst8 x5, 2
-; jump 0x10    // target = 0x3a
-; xconst8 x5, 1
-; jump 0x8    // target = 0x3a
+; jump 0xf    // target = 0x39
+; xone x5
+; jump 0x8    // target = 0x39
 ; xconst8 x5, 4
 ; xadd32 x0, x0, x5
 ; ret
diff --git a/cranelift/filetests/filetests/isa/pulley32/brif-icmp.clif b/cranelift/filetests/filetests/isa/pulley32/brif-icmp.clif
index 3e51993a7..f835d498e 100644
--- a/cranelift/filetests/filetests/isa/pulley32/brif-icmp.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/brif-icmp.clif
@@ -19,15 +19,15 @@ block2:
 ; block0:
 ;   br_if_xeq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xeq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xeq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -50,15 +50,15 @@ block2:
 ; block0:
 ;   br_if_xneq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xneq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -81,15 +81,15 @@ block2:
 ; block0:
 ;   br_if_xult32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xult32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xult32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -112,15 +112,15 @@ block2:
 ; block0:
 ;   br_if_xulteq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xulteq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xulteq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -143,15 +143,15 @@ block2:
 ; block0:
 ;   br_if_xslt32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslt32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -174,15 +174,15 @@ block2:
 ; block0:
 ;   br_if_xslteq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslteq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslteq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -205,15 +205,15 @@ block2:
 ; block0:
 ;   br_if_xult32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xult32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xult32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -236,15 +236,15 @@ block2:
 ; block0:
 ;   br_if_xulteq32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xulteq32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xulteq32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -267,15 +267,15 @@ block2:
 ; block0:
 ;   br_if_xslt32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslt32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -298,15 +298,15 @@ block2:
 ; block0:
 ;   br_if_xslteq32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslteq32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslteq32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -330,15 +330,15 @@ block2:
 ; block0:
 ;   br_if_xeq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xeq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xeq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
diff --git a/cranelift/filetests/filetests/isa/pulley32/brif.clif b/cranelift/filetests/filetests/isa/pulley32/brif.clif
index 5e29ea3ba..782354769 100644
--- a/cranelift/filetests/filetests/isa/pulley32/brif.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/brif.clif
@@ -19,18 +19,18 @@ block2:
 ;   zext8 x4, x0
 ;   br_if32 x4, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
 ; zext8 x4, x0
-; br_if32 x4, 0xa    // target = 0xd
-; xconst8 x0, 0
+; br_if32 x4, 0x9    // target = 0xc
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_i16(i16) -> i8 {
@@ -51,18 +51,18 @@ block2:
 ;   zext16 x4, x0
 ;   br_if32 x4, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
 ; zext16 x4, x0
-; br_if32 x4, 0xa    // target = 0xd
-; xconst8 x0, 0
+; br_if32 x4, 0x9    // target = 0xc
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_i32(i32) -> i8 {
@@ -82,17 +82,17 @@ block2:
 ; block0:
 ;   br_if32 x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if32 x0, 0xa    // target = 0xa
-; xconst8 x0, 0
+; br_if32 x0, 0x9    // target = 0x9
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_i64(i64) -> i8 {
@@ -112,17 +112,17 @@ block2:
 ; block0:
 ;   br_if_xneq64_i32 x0, 0, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq64_i8 x0, 0, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xneq64_i8 x0, 0, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i8(i8, i8) -> i8 {
@@ -147,10 +147,10 @@ block2:
 ;   zext8 x8, x10
 ;   br_if32 x8, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
@@ -158,10 +158,10 @@ block2:
 ; zext8 x8, x1
 ; xeq32 x10, x6, x8
 ; zext8 x8, x10
-; br_if32 x8, 0xa    // target = 0x16
-; xconst8 x0, 0
+; br_if32 x8, 0x9    // target = 0x15
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i16(i16, i16) -> i8 {
@@ -186,10 +186,10 @@ block2:
 ;   zext8 x8, x10
 ;   br_if32 x8, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
@@ -197,10 +197,10 @@ block2:
 ; zext16 x8, x1
 ; xneq32 x10, x6, x8
 ; zext8 x8, x10
-; br_if32 x8, 0xa    // target = 0x16
-; xconst8 x0, 0
+; br_if32 x8, 0x9    // target = 0x15
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i32(i32, i32) -> i8 {
@@ -221,17 +221,17 @@ block2:
 ; block0:
 ;   br_if_xslt32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xslt32 x0, x1, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i64(i64, i64) -> i8 {
@@ -252,17 +252,17 @@ block2:
 ; block0:
 ;   br_if_xulteq64 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xulteq64 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xulteq64 x1, x0, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i32_imm(i32) -> i8 {
@@ -283,17 +283,17 @@ block2:
 ; block0:
 ;   br_if_xslt32_i32 x0, 10, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32_i8 x0, 10, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xslt32_i8 x0, 10, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i32_imm_big(i32) -> i8 {
@@ -314,17 +314,17 @@ block2:
 ; block0:
 ;   br_if_xslt32_i32 x0, 88888, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32_i32 x0, 88888, 0xe    // target = 0xe
-; xconst8 x0, 0
+; br_if_xslt32_i32 x0, 88888, 0xd    // target = 0xd
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i64_imm(i64) -> i8 {
@@ -345,17 +345,17 @@ block2:
 ; block0:
 ;   br_if_xslt64_i32 x0, 10, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt64_i8 x0, 10, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xslt64_i8 x0, 10, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i64_imm_big(i64) -> i8 {
@@ -376,15 +376,16 @@ block2:
 ; block0:
 ;   br_if_xslt64_i32 x0, 88888, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt64_i32 x0, 88888, 0xe    // target = 0xe
-; xconst8 x0, 0
+; br_if_xslt64_i32 x0, 88888, 0xd    // target = 0xd
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
+
diff --git a/cranelift/filetests/filetests/isa/pulley32/call.clif b/cranelift/filetests/filetests/isa/pulley32/call.clif
index d2d9a29f2..8060a8c0c 100644
--- a/cranelift/filetests/filetests/isa/pulley32/call.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/call.clif
@@ -15,17 +15,17 @@ block0:
 ; VCode:
 ;   push_frame
 ; block0:
-;   xconst8 x2, 0
+;   xzero x2
 ;   call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p2i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [65534, 65535, 4294967295, 0] }, callee_conv: Fast, caller_conv: Fast, callee_pop_size: 0 }
-;   xconst8 x0, 1
+;   xone x0
 ;   pop_frame
 ;   ret
 ;
 ; Disassembled:
 ; push_frame
-; xconst8 x2, 0
-; call1 x2, 0x0    // target = 0x4
-; xconst8 x0, 1
+; xzero x2
+; call1 x2, 0x0    // target = 0x3
+; xone x0
 ; pop_frame
 ; ret
 
@@ -42,17 +42,17 @@ block0:
 ; VCode:
 ;   push_frame
 ; block0:
-;   xconst8 x2, 0
+;   xzero x2
 ;   call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p2i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [65534, 65535, 4294967295, 0] }, callee_conv: Fast, caller_conv: Fast, callee_pop_size: 0 }
-;   xconst8 x0, 1
+;   xone x0
 ;   pop_frame
 ;   ret
 ;
 ; Disassembled:
 ; push_frame
-; xconst8 x2, 0
-; call1 x2, 0x0    // target = 0x4
-; xconst8 x0, 1
+; xzero x2
+; call1 x2, 0x0    // target = 0x3
+; xone x0
 ; pop_frame
 ; ret
 
@@ -71,8 +71,8 @@ block0:
 ; VCode:
 ;   push_frame
 ; block0:
-;   xconst8 x3, 0
-;   xconst8 x4, 1
+;   xzero x3
+;   xone x4
 ;   xconst8 x5, 2
 ;   xconst8 x6, 3
 ;   call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p3i), XReg(p4i), XReg(p5i), XReg(p6i)] }, uses: [], defs: [], clobbers: PRegSet { bits: [65535, 65535, 4294967295, 0] }, callee_conv: Fast, caller_conv: Fast, callee_pop_size: 0 }
@@ -81,11 +81,11 @@ block0:
 ;
 ; Disassembled:
 ; push_frame
-; xconst8 x3, 0
-; xconst8 x4, 1
+; xzero x3
+; xone x4
 ; xconst8 x5, 2
 ; xconst8 x6, 3
-; call4 x3, x4, x5, x6, 0x0    // target = 0xd
+; call4 x3, x4, x5, x6, 0x0    // target = 0xb
 ; pop_frame
 ; ret
 
@@ -131,7 +131,7 @@ block0:
 ; VCode:
 ;   push_frame_save 48, {}
 ; block0:
-;   xconst8 x15, 0
+;   xzero x15
 ;   xstore64 OutgoingArg(0), x15 // flags =  notrap aligned
 ;   xstore64 OutgoingArg(8), x15 // flags =  notrap aligned
 ;   xstore64 OutgoingArg(16), x15 // flags =  notrap aligned
@@ -155,7 +155,7 @@ block0:
 ;
 ; Disassembled:
 ; push_frame_save 48, 
-; xconst8 x15, 0
+; xzero x15
 ; xstore64le_offset8 sp, 0, x15
 ; xstore64le_offset8 sp, 8, x15
 ; xstore64le_offset8 sp, 16, x15
@@ -173,7 +173,7 @@ block0:
 ; xmov x12, x15
 ; xmov x13, x15
 ; xmov x14, x15
-; call4 x15, x15, x15, x15, 0x0    // target = 0x45
+; call4 x15, x15, x15, x15, 0x0    // target = 0x44
 ; pop_frame_restore 48, 
 ; ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley32/jump.clif b/cranelift/filetests/filetests/isa/pulley32/jump.clif
index d10291178..87d4c44b7 100644
--- a/cranelift/filetests/filetests/isa/pulley32/jump.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/jump.clif
@@ -22,19 +22,19 @@ block3(v3: i8):
 ;   zext8 x5, x0
 ;   br_if32 x5, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   jump label3
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   jump label3
 ; block3:
 ;   ret
 ;
 ; Disassembled:
 ; zext8 x5, x0
-; br_if32 x5, 0xe    // target = 0x11
-; xconst8 x0, 0
-; jump 0x8    // target = 0x14
-; xconst8 x0, 1
+; br_if32 x5, 0xd    // target = 0x10
+; xzero x0
+; jump 0x7    // target = 0x12
+; xone x0
 ; ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley32/trap.clif b/cranelift/filetests/filetests/isa/pulley32/trap.clif
index c94c85bc5..b6548c032 100644
--- a/cranelift/filetests/filetests/isa/pulley32/trap.clif
+++ b/cranelift/filetests/filetests/isa/pulley32/trap.clif
@@ -104,7 +104,7 @@ block2:
 ; block0:
 ;   br_if_xneq64_i32 x0, 0, label2; jump label1
 ; block1:
-;   xconst8 x4, 0
+;   xzero x4
 ;   trap_if_xneq64_i32 x4, 0 // code = TrapCode(1)
 ;   ret
 ; block2:
@@ -113,12 +113,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq64_i8 x0, 0, 0x12    // target = 0x12
-; xconst8 x4, 0
-; br_if_xneq64_i8 x4, 0, 0x13    // target = 0x1d
+; br_if_xneq64_i8 x0, 0, 0x11    // target = 0x11
+; xzero x4
+; br_if_xneq64_i8 x4, 0, 0x13    // target = 0x1c
 ; ret
 ; xconst8 x6, 42
-; br_if_xneq64_i8 x6, 0, 0xb    // target = 0x20
+; br_if_xneq64_i8 x6, 0, 0xb    // target = 0x1f
 ; ret
 ; trap
 ; trap
@@ -142,7 +142,7 @@ block2:
 ; block0:
 ;   br_if_xneq64_i32 x0, 0, label2; jump label1
 ; block1:
-;   xconst8 x4, 0
+;   xzero x4
 ;   trap_if_xeq64_i32 x4, 0 // code = TrapCode(1)
 ;   ret
 ; block2:
@@ -151,12 +151,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq64_i8 x0, 0, 0x12    // target = 0x12
-; xconst8 x4, 0
-; br_if_xeq64_i8 x4, 0, 0x13    // target = 0x1d
+; br_if_xneq64_i8 x0, 0, 0x11    // target = 0x11
+; xzero x4
+; br_if_xeq64_i8 x4, 0, 0x13    // target = 0x1c
 ; ret
 ; xconst8 x6, 42
-; br_if_xeq64_i8 x6, 0, 0xb    // target = 0x20
+; br_if_xeq64_i8 x6, 0, 0xb    // target = 0x1f
 ; ret
 ; trap
 ; trap
diff --git a/cranelift/filetests/filetests/isa/pulley64/br_table.clif b/cranelift/filetests/filetests/isa/pulley64/br_table.clif
index 3adf03c6f..c880668ad 100644
--- a/cranelift/filetests/filetests/isa/pulley64/br_table.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/br_table.clif
@@ -40,7 +40,7 @@ block5(v5: i32):
 ;   xconst8 x5, 2
 ;   jump label7
 ; block5:
-;   xconst8 x5, 1
+;   xone x5
 ;   jump label7
 ; block6:
 ;   xconst8 x5, 4
@@ -55,14 +55,14 @@ block5(v5: i32):
 ; 0x1d    // target = 0x27
 ; 0x19    // target = 0x27
 ; 0xd    // target = 0x1f
-; 0x21    // target = 0x37
+; 0x20    // target = 0x36
 ; jump 0xd    // target = 0x27
 ; xconst8 x5, 3
-; jump 0x18    // target = 0x3a
+; jump 0x17    // target = 0x39
 ; xconst8 x5, 2
-; jump 0x10    // target = 0x3a
-; xconst8 x5, 1
-; jump 0x8    // target = 0x3a
+; jump 0xf    // target = 0x39
+; xone x5
+; jump 0x8    // target = 0x39
 ; xconst8 x5, 4
 ; xadd32 x0, x0, x5
 ; ret
diff --git a/cranelift/filetests/filetests/isa/pulley64/brif-icmp.clif b/cranelift/filetests/filetests/isa/pulley64/brif-icmp.clif
index 8a7ab52dd..9f4f88eb0 100644
--- a/cranelift/filetests/filetests/isa/pulley64/brif-icmp.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/brif-icmp.clif
@@ -19,15 +19,15 @@ block2:
 ; block0:
 ;   br_if_xeq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xeq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xeq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -50,15 +50,15 @@ block2:
 ; block0:
 ;   br_if_xneq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xneq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -81,15 +81,15 @@ block2:
 ; block0:
 ;   br_if_xult32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xult32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xult32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -112,15 +112,15 @@ block2:
 ; block0:
 ;   br_if_xulteq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xulteq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xulteq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -143,15 +143,15 @@ block2:
 ; block0:
 ;   br_if_xslt32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslt32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -174,15 +174,15 @@ block2:
 ; block0:
 ;   br_if_xslteq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslteq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslteq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -205,15 +205,15 @@ block2:
 ; block0:
 ;   br_if_xult32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xult32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xult32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -236,15 +236,15 @@ block2:
 ; block0:
 ;   br_if_xulteq32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xulteq32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xulteq32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -267,15 +267,15 @@ block2:
 ; block0:
 ;   br_if_xslt32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslt32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -298,15 +298,15 @@ block2:
 ; block0:
 ;   br_if_xslteq32 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslteq32 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xslteq32 x1, x0, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
@@ -330,15 +330,15 @@ block2:
 ; block0:
 ;   br_if_xeq32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ; block2:
 ;   xconst8 x0, 2
 ;   ret
 ;
 ; Disassembled:
-; br_if_xeq32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 1
+; br_if_xeq32 x0, x1, 0xa    // target = 0xa
+; xone x0
 ; ret
 ; xconst8 x0, 2
 ; ret
diff --git a/cranelift/filetests/filetests/isa/pulley64/brif.clif b/cranelift/filetests/filetests/isa/pulley64/brif.clif
index 07c1311b6..b7ba62167 100644
--- a/cranelift/filetests/filetests/isa/pulley64/brif.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/brif.clif
@@ -19,18 +19,18 @@ block2:
 ;   zext8 x4, x0
 ;   br_if32 x4, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
 ; zext8 x4, x0
-; br_if32 x4, 0xa    // target = 0xd
-; xconst8 x0, 0
+; br_if32 x4, 0x9    // target = 0xc
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_i16(i16) -> i8 {
@@ -51,18 +51,18 @@ block2:
 ;   zext16 x4, x0
 ;   br_if32 x4, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
 ; zext16 x4, x0
-; br_if32 x4, 0xa    // target = 0xd
-; xconst8 x0, 0
+; br_if32 x4, 0x9    // target = 0xc
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_i32(i32) -> i8 {
@@ -82,17 +82,17 @@ block2:
 ; block0:
 ;   br_if32 x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if32 x0, 0xa    // target = 0xa
-; xconst8 x0, 0
+; br_if32 x0, 0x9    // target = 0x9
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_i64(i64) -> i8 {
@@ -112,17 +112,17 @@ block2:
 ; block0:
 ;   br_if_xneq64_i32 x0, 0, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq64_i8 x0, 0, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xneq64_i8 x0, 0, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i8(i8, i8) -> i8 {
@@ -147,10 +147,10 @@ block2:
 ;   zext8 x8, x10
 ;   br_if32 x8, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
@@ -158,10 +158,10 @@ block2:
 ; zext8 x8, x1
 ; xeq32 x10, x6, x8
 ; zext8 x8, x10
-; br_if32 x8, 0xa    // target = 0x16
-; xconst8 x0, 0
+; br_if32 x8, 0x9    // target = 0x15
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i16(i16, i16) -> i8 {
@@ -186,10 +186,10 @@ block2:
 ;   zext8 x8, x10
 ;   br_if32 x8, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
@@ -197,10 +197,10 @@ block2:
 ; zext16 x8, x1
 ; xneq32 x10, x6, x8
 ; zext8 x8, x10
-; br_if32 x8, 0xa    // target = 0x16
-; xconst8 x0, 0
+; br_if32 x8, 0x9    // target = 0x15
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i32(i32, i32) -> i8 {
@@ -221,17 +221,17 @@ block2:
 ; block0:
 ;   br_if_xslt32 x0, x1, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xslt32 x0, x1, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xslt32 x0, x1, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
 function %brif_icmp_i64(i64, i64) -> i8 {
@@ -252,16 +252,16 @@ block2:
 ; block0:
 ;   br_if_xulteq64 x1, x0, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   ret
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   ret
 ;
 ; Disassembled:
-; br_if_xulteq64 x1, x0, 0xb    // target = 0xb
-; xconst8 x0, 0
+; br_if_xulteq64 x1, x0, 0xa    // target = 0xa
+; xzero x0
 ; ret
-; xconst8 x0, 1
+; xone x0
 ; ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/call.clif b/cranelift/filetests/filetests/isa/pulley64/call.clif
index e00fd5909..eaa1bdf29 100644
--- a/cranelift/filetests/filetests/isa/pulley64/call.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/call.clif
@@ -15,17 +15,17 @@ block0:
 ; VCode:
 ;   push_frame
 ; block0:
-;   xconst8 x2, 0
+;   xzero x2
 ;   call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p2i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [65534, 65535, 4294967295, 0] }, callee_conv: Fast, caller_conv: Fast, callee_pop_size: 0 }
-;   xconst8 x0, 1
+;   xone x0
 ;   pop_frame
 ;   ret
 ;
 ; Disassembled:
 ; push_frame
-; xconst8 x2, 0
-; call1 x2, 0x0    // target = 0x4
-; xconst8 x0, 1
+; xzero x2
+; call1 x2, 0x0    // target = 0x3
+; xone x0
 ; pop_frame
 ; ret
 
@@ -42,17 +42,17 @@ block0:
 ; VCode:
 ;   push_frame
 ; block0:
-;   xconst8 x2, 0
+;   xzero x2
 ;   call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p2i)] }, uses: [], defs: [CallRetPair { vreg: Writable { reg: p0i }, preg: p0i }], clobbers: PRegSet { bits: [65534, 65535, 4294967295, 0] }, callee_conv: Fast, caller_conv: Fast, callee_pop_size: 0 }
-;   xconst8 x0, 1
+;   xone x0
 ;   pop_frame
 ;   ret
 ;
 ; Disassembled:
 ; push_frame
-; xconst8 x2, 0
-; call1 x2, 0x0    // target = 0x4
-; xconst8 x0, 1
+; xzero x2
+; call1 x2, 0x0    // target = 0x3
+; xone x0
 ; pop_frame
 ; ret
 
@@ -71,8 +71,8 @@ block0:
 ; VCode:
 ;   push_frame
 ; block0:
-;   xconst8 x3, 0
-;   xconst8 x4, 1
+;   xzero x3
+;   xone x4
 ;   xconst8 x5, 2
 ;   xconst8 x6, 3
 ;   call CallInfo { dest: PulleyCall { name: TestCase(%g), args: [XReg(p3i), XReg(p4i), XReg(p5i), XReg(p6i)] }, uses: [], defs: [], clobbers: PRegSet { bits: [65535, 65535, 4294967295, 0] }, callee_conv: Fast, caller_conv: Fast, callee_pop_size: 0 }
@@ -81,11 +81,11 @@ block0:
 ;
 ; Disassembled:
 ; push_frame
-; xconst8 x3, 0
-; xconst8 x4, 1
+; xzero x3
+; xone x4
 ; xconst8 x5, 2
 ; xconst8 x6, 3
-; call4 x3, x4, x5, x6, 0x0    // target = 0xd
+; call4 x3, x4, x5, x6, 0x0    // target = 0xb
 ; pop_frame
 ; ret
 
@@ -131,7 +131,7 @@ block0:
 ; VCode:
 ;   push_frame_save 48, {}
 ; block0:
-;   xconst8 x15, 0
+;   xzero x15
 ;   xstore64 OutgoingArg(0), x15 // flags =  notrap aligned
 ;   xstore64 OutgoingArg(8), x15 // flags =  notrap aligned
 ;   xstore64 OutgoingArg(16), x15 // flags =  notrap aligned
@@ -155,7 +155,7 @@ block0:
 ;
 ; Disassembled:
 ; push_frame_save 48, 
-; xconst8 x15, 0
+; xzero x15
 ; xstore64le_offset8 sp, 0, x15
 ; xstore64le_offset8 sp, 8, x15
 ; xstore64le_offset8 sp, 16, x15
@@ -173,7 +173,7 @@ block0:
 ; xmov x12, x15
 ; xmov x13, x15
 ; xmov x14, x15
-; call4 x15, x15, x15, x15, 0x0    // target = 0x45
+; call4 x15, x15, x15, x15, 0x0    // target = 0x44
 ; pop_frame_restore 48, 
 ; ret
 
@@ -333,7 +333,7 @@ block0:
 ; VCode:
 ;   push_frame_save 64, {}
 ; block0:
-;   xconst8 x15, 0
+;   xzero x15
 ;   xstore64 OutgoingArg(0), x15 // flags =  notrap aligned
 ;   xstore64 OutgoingArg(8), x15 // flags =  notrap aligned
 ;   xstore64 OutgoingArg(16), x15 // flags =  notrap aligned
@@ -359,7 +359,7 @@ block0:
 ;
 ; Disassembled:
 ; push_frame_save 64, 
-; xconst8 x15, 0
+; xzero x15
 ; xstore64le_offset8 sp, 0, x15
 ; xstore64le_offset8 sp, 8, x15
 ; xstore64le_offset8 sp, 16, x15
@@ -379,7 +379,7 @@ block0:
 ; xmov x12, x15
 ; xmov x13, x15
 ; xmov x14, x15
-; call4 x15, x15, x15, x15, 0x0    // target = 0x4d
+; call4 x15, x15, x15, x15, 0x0    // target = 0x4c
 ; pop_frame_restore 64, 
 ; ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/jump.clif b/cranelift/filetests/filetests/isa/pulley64/jump.clif
index a4b187bcc..d49bb9b66 100644
--- a/cranelift/filetests/filetests/isa/pulley64/jump.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/jump.clif
@@ -22,19 +22,19 @@ block3(v3: i8):
 ;   zext8 x5, x0
 ;   br_if32 x5, label2; jump label1
 ; block1:
-;   xconst8 x0, 0
+;   xzero x0
 ;   jump label3
 ; block2:
-;   xconst8 x0, 1
+;   xone x0
 ;   jump label3
 ; block3:
 ;   ret
 ;
 ; Disassembled:
 ; zext8 x5, x0
-; br_if32 x5, 0xe    // target = 0x11
-; xconst8 x0, 0
-; jump 0x8    // target = 0x14
-; xconst8 x0, 1
+; br_if32 x5, 0xd    // target = 0x10
+; xzero x0
+; jump 0x7    // target = 0x12
+; xone x0
 ; ret
 
diff --git a/cranelift/filetests/filetests/isa/pulley64/trap.clif b/cranelift/filetests/filetests/isa/pulley64/trap.clif
index 6af273784..9968556b0 100644
--- a/cranelift/filetests/filetests/isa/pulley64/trap.clif
+++ b/cranelift/filetests/filetests/isa/pulley64/trap.clif
@@ -104,7 +104,7 @@ block2:
 ; block0:
 ;   br_if_xneq64_i32 x0, 0, label2; jump label1
 ; block1:
-;   xconst8 x4, 0
+;   xzero x4
 ;   trap_if_xneq64_i32 x4, 0 // code = TrapCode(1)
 ;   ret
 ; block2:
@@ -113,12 +113,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq64_i8 x0, 0, 0x12    // target = 0x12
-; xconst8 x4, 0
-; br_if_xneq64_i8 x4, 0, 0x13    // target = 0x1d
+; br_if_xneq64_i8 x0, 0, 0x11    // target = 0x11
+; xzero x4
+; br_if_xneq64_i8 x4, 0, 0x13    // target = 0x1c
 ; ret
 ; xconst8 x6, 42
-; br_if_xneq64_i8 x6, 0, 0xb    // target = 0x20
+; br_if_xneq64_i8 x6, 0, 0xb    // target = 0x1f
 ; ret
 ; trap
 ; trap
@@ -142,7 +142,7 @@ block2:
 ; block0:
 ;   br_if_xneq64_i32 x0, 0, label2; jump label1
 ; block1:
-;   xconst8 x4, 0
+;   xzero x4
 ;   trap_if_xeq64_i32 x4, 0 // code = TrapCode(1)
 ;   ret
 ; block2:
@@ -151,12 +151,12 @@ block2:
 ;   ret
 ;
 ; Disassembled:
-; br_if_xneq64_i8 x0, 0, 0x12    // target = 0x12
-; xconst8 x4, 0
-; br_if_xeq64_i8 x4, 0, 0x13    // target = 0x1d
+; br_if_xneq64_i8 x0, 0, 0x11    // target = 0x11
+; xzero x4
+; br_if_xeq64_i8 x4, 0, 0x13    // target = 0x1c
 ; ret
 ; xconst8 x6, 42
-; br_if_xeq64_i8 x6, 0, 0xb    // target = 0x20
+; br_if_xeq64_i8 x6, 0, 0xb    // target = 0x1f
 ; ret
 ; trap
 ; trap
diff --git a/crates/test-programs/src/bin/api_proxy.rs b/crates/test-programs/src/bin/api_proxy.rs
index bbbbecd1e..773efe73d 100644
--- a/crates/test-programs/src/bin/api_proxy.rs
+++ b/crates/test-programs/src/bin/api_proxy.rs
@@ -23,7 +23,7 @@ impl test_programs::proxy::exports::wasi::http::incoming_handler::Guest for T {
         );
 
         assert!(req_hdrs.delete(&header).is_err());
-        assert!(req_hdrs.append(&header, &b"no".to_vec()).is_err());
+        assert!(req_hdrs.append(&header, b"no".as_ref()).is_err());
 
         assert!(
             !req_hdrs.has(&header),
@@ -31,7 +31,7 @@ impl test_programs::proxy::exports::wasi::http::incoming_handler::Guest for T {
         );
 
         assert!(
-            !req_hdrs.has(&"host".to_owned()),
+            !req_hdrs.has("host"),
             "forbidden host header present in incoming request"
         );
 
diff --git a/crates/test-programs/src/bin/api_reactor.rs b/crates/test-programs/src/bin/api_reactor.rs
index f232743a8..360c96fab 100644
--- a/crates/test-programs/src/bin/api_reactor.rs
+++ b/crates/test-programs/src/bin/api_reactor.rs
@@ -1,8 +1,3 @@
-#![expect(
-    unsafe_attr_outside_unsafe,
-    reason = "needs fixes in upstream wit-bindgen to satisfy this lint"
-)]
-
 use std::sync::{Mutex, MutexGuard};
 
 wit_bindgen::generate!({
diff --git a/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs b/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs
index 1f532ee25..d1e2a9345 100644
--- a/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs
+++ b/crates/test-programs/src/bin/http_outbound_request_invalid_header.rs
@@ -3,48 +3,42 @@ use test_programs::wasi::http::types::{HeaderError, Headers, OutgoingRequest};
 fn main() {
     let hdrs = Headers::new();
     assert!(matches!(
-        hdrs.append(&"malformed header name".to_owned(), &b"ok value".to_vec()),
+        hdrs.append("malformed header name", b"ok value".as_ref()),
         Err(HeaderError::InvalidSyntax)
     ));
 
     assert!(matches!(
-        hdrs.append(&"ok-header-name".to_owned(), &b"ok value".to_vec()),
+        hdrs.append("ok-header-name", b"ok value".as_ref()),
         Ok(())
     ));
 
     assert!(matches!(
-        hdrs.append(&"ok-header-name".to_owned(), &b"bad\nvalue".to_vec()),
+        hdrs.append("ok-header-name", b"bad\nvalue".as_ref()),
         Err(HeaderError::InvalidSyntax)
     ));
 
     assert!(matches!(
-        hdrs.append(&"Connection".to_owned(), &b"keep-alive".to_vec()),
+        hdrs.append("Connection", b"keep-alive".as_ref()),
         Err(HeaderError::Forbidden)
     ));
 
     assert!(matches!(
-        hdrs.append(&"Keep-Alive".to_owned(), &b"stuff".to_vec()),
+        hdrs.append("Keep-Alive", b"stuff".as_ref()),
         Err(HeaderError::Forbidden)
     ));
 
     assert!(matches!(
-        hdrs.append(&"Host".to_owned(), &b"example.com".to_vec()),
+        hdrs.append("Host", b"example.com".as_ref()),
         Err(HeaderError::Forbidden)
     ));
 
     assert!(matches!(
-        hdrs.append(
-            &"custom-forbidden-header".to_owned(),
-            &b"keep-alive".to_vec()
-        ),
+        hdrs.append("custom-forbidden-header", b"keep-alive".as_ref()),
         Err(HeaderError::Forbidden)
     ));
 
     assert!(matches!(
-        hdrs.append(
-            &"Custom-Forbidden-Header".to_owned(),
-            &b"keep-alive".to_vec()
-        ),
+        hdrs.append("Custom-Forbidden-Header", b"keep-alive".as_ref()),
         Err(HeaderError::Forbidden)
     ));
 
@@ -67,17 +61,17 @@ fn main() {
     let hdrs = req.headers();
 
     assert!(matches!(
-        hdrs.set(&"Content-Length".to_owned(), &[b"10".to_vec()]),
+        hdrs.set("Content-Length", &[b"10".to_vec()]),
         Err(HeaderError::Immutable),
     ));
 
     assert!(matches!(
-        hdrs.append(&"Content-Length".to_owned(), &b"10".to_vec()),
+        hdrs.append("Content-Length", b"10".as_ref()),
         Err(HeaderError::Immutable),
     ));
 
     assert!(matches!(
-        hdrs.delete(&"Content-Length".to_owned()),
+        hdrs.delete("Content-Length"),
         Err(HeaderError::Immutable),
     ));
 }
diff --git a/tests/disas/pulley/br_table.wat b/tests/disas/pulley/br_table.wat
index ad6c1fc2f..56ec6c54c 100644
--- a/tests/disas/pulley/br_table.wat
+++ b/tests/disas/pulley/br_table.wat
@@ -21,15 +21,15 @@
 ;; wasm[0]::function[0]:
 ;;       push_frame
 ;;       br_table32 x2, 3
-;;       0x11    // target = 0x18
+;;       0x10    // target = 0x17
 ;;       0x8    // target = 0x13
-;;       0xe    // target = 0x1d
-;;   13: xconst8 x0, 1
+;;       0xd    // target = 0x1c
+;;   13: xone x0
 ;;       pop_frame
 ;;       ret
-;;   18: xconst8 x0, 2
-;;   1b: pop_frame
-;;   1c: ret
-;;   1d: xconst8 x0, 0
-;;   20: pop_frame
-;;   21: ret
+;;   17: xconst8 x0, 2
+;;   1a: pop_frame
+;;   1b: ret
+;;   1c: xzero x0
+;;   1e: pop_frame
+;;   1f: ret
diff --git a/tests/disas/pulley/epoch-simple.wat b/tests/disas/pulley/epoch-simple.wat
index a80f73a8d..bb8bfccd9 100644
--- a/tests/disas/pulley/epoch-simple.wat
+++ b/tests/disas/pulley/epoch-simple.wat
@@ -14,5 +14,5 @@
 ;;       br_if_xulteq64 x6, x7, 0x9    // target = 0x1a
 ;;   18: pop_frame
 ;;       ret
-;;   1a: call 0x9f    // target = 0xb9
+;;   1a: call 0x9c    // target = 0xb6
 ;;   1f: jump 0xfffffffffffffff9    // target = 0x18
diff --git a/tests/disas/pulley/memory-inbounds.wat b/tests/disas/pulley/memory-inbounds.wat
index 3b5f021d2..63958055b 100644
--- a/tests/disas/pulley/memory-inbounds.wat
+++ b/tests/disas/pulley/memory-inbounds.wat
@@ -88,16 +88,16 @@
 ;;
 ;; wasm[0]::function[8]::maybe_inbounds_v2:
 ;;       push_frame
-;;       xconst8 x7, 0
+;;       xzero x7
 ;;       xconst32 x8, 131072
 ;;       xadd64_uoverflow_trap x7, x7, x8
 ;;       xload64le_offset8 x8, x0, 104
-;;       br_if_xult64 x8, x7, 0x14    // target = 0x27
-;;   1a: xload64le_offset8 x8, x0, 96
+;;       br_if_xult64 x8, x7, 0x14    // target = 0x26
+;;   19: xload64le_offset8 x8, x0, 96
 ;;       xload32le_offset32 x0, x8, 131068
 ;;       pop_frame
 ;;       ret
-;;   27: trap
+;;   26: trap
 ;;
 ;; wasm[0]::function[9]::never_inbounds:
 ;;       push_frame
diff --git a/tests/disas/winch/x64/atomic/load/i32_atomic_load.wat b/tests/disas/winch/x64/atomic/load/i32_atomic_load.wat
new file mode 100644
index 000000000..b561f66e8
--- /dev/null
+++ b/tests/disas/winch/x64/atomic/load/i32_atomic_load.wat
@@ -0,0 +1,30 @@
+;;! target = "x86_64"
+;;! test = "winch"
+
+(module 
+  (import "env" "memory" (memory 1 1 shared))
+  (func (param $foo i32) (result i32)
+        (i32.atomic.load
+          (local.get $foo))))
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x20, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x47
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x20, %rsp
+;;       movq    %rdi, 0x18(%rsp)
+;;       movq    %rsi, 0x10(%rsp)
+;;       movl    %edx, 0xc(%rsp)
+;;       movl    0xc(%rsp), %eax
+;;       movq    0x58(%r14), %r11
+;;       movq    (%r11), %rcx
+;;       addq    %rax, %rcx
+;;       movl    (%rcx), %eax
+;;       addq    $0x20, %rsp
+;;       popq    %rbp
+;;       retq
+;;   47: ud2
diff --git a/tests/disas/winch/x64/atomic/load/i32_atomic_load16_u.wat b/tests/disas/winch/x64/atomic/load/i32_atomic_load16_u.wat
new file mode 100644
index 000000000..ed9dbdf80
--- /dev/null
+++ b/tests/disas/winch/x64/atomic/load/i32_atomic_load16_u.wat
@@ -0,0 +1,30 @@
+;;! target = "x86_64"
+;;! test = "winch"
+
+(module 
+  (import "env" "memory" (memory 1 1 shared))
+  (func (param $foo i32) (result i32)
+        (i32.atomic.load16_u
+          (local.get $foo))))
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x20, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x49
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x20, %rsp
+;;       movq    %rdi, 0x18(%rsp)
+;;       movq    %rsi, 0x10(%rsp)
+;;       movl    %edx, 0xc(%rsp)
+;;       movl    0xc(%rsp), %eax
+;;       movq    0x58(%r14), %r11
+;;       movq    (%r11), %rcx
+;;       addq    %rax, %rcx
+;;       movzwq  (%rcx), %rax
+;;       addq    $0x20, %rsp
+;;       popq    %rbp
+;;       retq
+;;   49: ud2
diff --git a/tests/disas/winch/x64/atomic/load/i32_atomic_load8_u.wat b/tests/disas/winch/x64/atomic/load/i32_atomic_load8_u.wat
new file mode 100644
index 000000000..31157179c
--- /dev/null
+++ b/tests/disas/winch/x64/atomic/load/i32_atomic_load8_u.wat
@@ -0,0 +1,28 @@
+;;! target = "x86_64"
+;;! test = "winch"
+
+(module 
+  (memory (data "\00\00\00\00\00\00\f4\7f"))
+
+  (func (result i32)
+        (i32.atomic.load8_u (i32.const 0))))
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x42
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       movzbq  (%rcx), %rax
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   42: ud2
diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load.wat
new file mode 100644
index 000000000..3aa41b5bb
--- /dev/null
+++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+
+(module 
+  (memory (data "\00\00\00\00\00\00\f4\7f"))
+
+  (func (result i64)
+        (i64.atomic.load
+          (i32.const 0))))
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x41
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       movq    (%rcx), %rax
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   41: ud2
diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load16_u.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load16_u.wat
new file mode 100644
index 000000000..a5a36d758
--- /dev/null
+++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load16_u.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+
+(module 
+  (memory (data "\00\00\00\00\00\00\f4\7f"))
+
+  (func (result i64)
+        (i64.atomic.load16_u
+          (i32.const 0))))
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x42
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       movzwq  (%rcx), %rax
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   42: ud2
diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load32_u.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load32_u.wat
new file mode 100644
index 000000000..4cc24de13
--- /dev/null
+++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load32_u.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+
+(module 
+  (memory (data "\00\00\00\00\00\00\f4\7f"))
+
+  (func (result i64)
+        (i64.atomic.load32_u
+          (i32.const 0))))
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x40
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       movl    (%rcx), %eax
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   40: ud2
diff --git a/tests/disas/winch/x64/atomic/load/i64_atomic_load8_u.wat b/tests/disas/winch/x64/atomic/load/i64_atomic_load8_u.wat
new file mode 100644
index 000000000..e922a0f48
--- /dev/null
+++ b/tests/disas/winch/x64/atomic/load/i64_atomic_load8_u.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+
+(module 
+  (memory (data "\00\00\00\00\00\00\f4\7f"))
+
+  (func (result i64)
+        (i64.atomic.load8_u
+          (i32.const 0))))
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x42
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       movzbq  (%rcx), %rax
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   42: ud2
diff --git a/tests/disas/winch/x64/load/v128_load16_splat_avx2.wat b/tests/disas/winch/x64/load/v128_load16_splat_avx2.wat
new file mode 100644
index 000000000..5350689d3
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load16_splat_avx2.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true", "-Ccranelift-has-avx2=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load16_splat") (result v128) (v128.load16_splat (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpbroadcastw (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load16x4_s_avx.wat b/tests/disas/winch/x64/load/v128_load16x4_s_avx.wat
new file mode 100644
index 000000000..2e95463b2
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load16x4_s_avx.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load16x4_s") (result v128) (v128.load16x4_s (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpmovsxwd (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load16x4_u_avx.wat b/tests/disas/winch/x64/load/v128_load16x4_u_avx.wat
new file mode 100644
index 000000000..641275ac2
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load16x4_u_avx.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load16x4_u") (result v128) (v128.load16x4_u (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpmovzxwd (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load32_splat_avx2.wat b/tests/disas/winch/x64/load/v128_load32_splat_avx2.wat
new file mode 100644
index 000000000..a932a1e9e
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load32_splat_avx2.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true", "-Ccranelift-has-avx2=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load32_splat") (result v128) (v128.load32_splat (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpbroadcastd (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load32x2_s_avx.wat b/tests/disas/winch/x64/load/v128_load32x2_s_avx.wat
new file mode 100644
index 000000000..ca2f20219
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load32x2_s_avx.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load32x2_s") (result v128) (v128.load32x2_s (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpmovsxdq (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load32x2_u_avx.wat b/tests/disas/winch/x64/load/v128_load32x2_u_avx.wat
new file mode 100644
index 000000000..b81f42c01
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load32x2_u_avx.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load32x2_u") (result v128) (v128.load32x2_u (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpmovzxdq (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load64_splat_avx.wat b/tests/disas/winch/x64/load/v128_load64_splat_avx.wat
new file mode 100644
index 000000000..4170e9ba1
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load64_splat_avx.wat
@@ -0,0 +1,30 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load64_splat") (result v128) (v128.load64_splat (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x47
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       movsd   (%rcx), %xmm0
+;;       vpshufd $0x44, %xmm0, %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   47: ud2
diff --git a/tests/disas/winch/x64/load/v128_load8_splat_avx2.wat b/tests/disas/winch/x64/load/v128_load8_splat_avx2.wat
new file mode 100644
index 000000000..57dc05138
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load8_splat_avx2.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true", "-Ccranelift-has-avx2=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load8_splat") (result v128) (v128.load8_splat (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpbroadcastb (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load8x8_s_avx.wat b/tests/disas/winch/x64/load/v128_load8x8_s_avx.wat
new file mode 100644
index 000000000..58e8c44a3
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load8x8_s_avx.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load8x8_s") (result v128) (v128.load8x8_s (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpmovsxbw (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/disas/winch/x64/load/v128_load8x8_u_avx.wat b/tests/disas/winch/x64/load/v128_load8x8_u_avx.wat
new file mode 100644
index 000000000..3611ca60e
--- /dev/null
+++ b/tests/disas/winch/x64/load/v128_load8x8_u_avx.wat
@@ -0,0 +1,29 @@
+;;! target = "x86_64"
+;;! test = "winch"
+;;! flags = [ "-Ccranelift-has-avx=true" ]
+
+(module
+  (memory (data "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\a0\7f"))
+
+  (func (export "v128.load8x8_u") (result v128) (v128.load8x8_u (i32.const 0)))
+)
+;; wasm[0]::function[0]:
+;;       pushq   %rbp
+;;       movq    %rsp, %rbp
+;;       movq    8(%rdi), %r11
+;;       movq    0x10(%r11), %r11
+;;       addq    $0x10, %r11
+;;       cmpq    %rsp, %r11
+;;       ja      0x43
+;;   1c: movq    %rdi, %r14
+;;       subq    $0x10, %rsp
+;;       movq    %rdi, 8(%rsp)
+;;       movq    %rsi, (%rsp)
+;;       movl    $0, %eax
+;;       movq    0x60(%r14), %rcx
+;;       addq    %rax, %rcx
+;;       vpmovzxbw (%rcx), %xmm0
+;;       addq    $0x10, %rsp
+;;       popq    %rbp
+;;       retq
+;;   43: ud2
diff --git a/tests/misc_testsuite/many-results.wast b/tests/misc_testsuite/many-results.wast
new file mode 100644
index 000000000..04dd82910
--- /dev/null
+++ b/tests/misc_testsuite/many-results.wast
@@ -0,0 +1,49 @@
+(module
+  (func (export "f")
+    (result
+      i32 i32 i32 i32
+      i32 i32 i32 i32
+      i32 i32 i32 i32
+      i32 i32 i32 i32
+      i32
+    )
+
+    i32.const 0
+    i32.const 1
+    i32.const 2
+    i32.const 3
+    i32.const 4
+    i32.const 5
+    i32.const 6
+    i32.const 7
+    i32.const 8
+    i32.const 9
+    i32.const 10
+    i32.const 11
+    i32.const 12
+    i32.const 13
+    i32.const 14
+    i32.const 15
+    i32.const 16
+  )
+)
+
+(assert_return (invoke "f")
+  (i32.const 0)
+  (i32.const 1)
+  (i32.const 2)
+  (i32.const 3)
+  (i32.const 4)
+  (i32.const 5)
+  (i32.const 6)
+  (i32.const 7)
+  (i32.const 8)
+  (i32.const 9)
+  (i32.const 10)
+  (i32.const 11)
+  (i32.const 12)
+  (i32.const 13)
+  (i32.const 14)
+  (i32.const 15)
+  (i32.const 16)
+)