Repository: bytecodealliance/wasmtime
Pull request #8950 (2024-07-13 06:21:50)

Implement wasi-runtime-config
Part of https://github.com/bytecodealliance/wasmtime/issues/8929, the integration of `wasmtime-cli` will be implemented in the next separate PR.

close https://github.com/bytecodealliance/wasmtime/pull/8939
close https://github.com/bytecodealliance/wasmtime/pull/8928

cc @alexcrichton 

iawia002: > Could this also get hooked up to the CLI as well? Under perhaps `-Sruntime-config` and/or some other `-S` flags for the actual keys themselves?

I made some changes based on all the comments except this one, because I plan to add support for the `wasi-runtime-config` API to `wasmtime-cli` in a separate PR. Otherwise, this PR would have too many changes, let's focus on the `wasi-runtime-config` implementation only in this PR.

And I think all comments have been addressed, PTAL.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index b986ee3a8..3576a2b48 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1973,9 +1973,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
 
 [[package]]
 name = "openvino"
-version = "0.6.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24bd3a7ef39968e6a4f1b1206c1c876f9bd50cf739ccbcd69f8539bbac5dcc7a"
+checksum = "aee013796927eec6012a344f10ecdc06bf26de79c626a2395e3f115464907ef6"
 dependencies = [
  "openvino-finder",
  "openvino-sys",
@@ -1984,9 +1984,9 @@ dependencies = [
 
 [[package]]
 name = "openvino-finder"
-version = "0.6.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05d234d1394a413ea8adaf0c40806b9ad1946be6310b441f688840654a331973"
+checksum = "af4c6841df4cd60fef743015f3348f81b6b225bd255ed0c4cab6e8c479e45eaa"
 dependencies = [
  "cfg-if",
  "log",
@@ -1994,9 +1994,9 @@ dependencies = [
 
 [[package]]
 name = "openvino-sys"
-version = "0.6.0"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c98acf37fc84ad9d7da4dc6c18f0f60ad209b43a6f555be01f9003d0a2a43d"
+checksum = "f62fc2bd6882f2300a6b5017eaad292586d70995d333582aabcf1f1121cd147c"
 dependencies = [
  "env_logger",
  "libloading",
@@ -3235,9 +3235,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
 
 [[package]]
 name = "wasm-encoder"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "501940df4418b8929eb6d52f1aade1fdd15a5b86c92453cb696e3c906bd3fc33"
+checksum = "ff694f02a8d7a50b6922b197ae03883fbf18cdb2ae9fbee7b6148456f5f44041"
 dependencies = [
  "leb128",
  "wasmparser",
@@ -3245,9 +3245,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-metadata"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a1849fac257fd76c43268555e73d74848c8dff23975c238c2cbad61cffe5045"
+checksum = "865c5bff5f7a3781b5f92ea4cfa99bb38267da097441cdb09080de1568ef3075"
 dependencies = [
  "anyhow",
  "indexmap 2.2.6",
@@ -3261,9 +3261,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-mutate"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff58202fe6f3750ba930d160a2451087973d902e09a2632b9a78ab7c86150a59"
+checksum = "bf9fb8fb035aaada5f46c4eac0fb93a493143708815f28a0a008e3c92074f270"
 dependencies = [
  "egg",
  "log",
@@ -3275,9 +3275,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-smith"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8185faf41a5630ec7cf301fa1f63bb757f4c267a057f8b6b56bde0cba1144c1"
+checksum = "ffe5856b6e06e8d37dad11167111e49339a5b017ab1399f7cb708fcb4c4550d0"
 dependencies = [
  "anyhow",
  "arbitrary",
@@ -3329,9 +3329,9 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d28bc49ba1e5c5b61ffa7a2eace10820443c4b7d1c0b144109261d14570fdf8"
+checksum = "5309c1090e3e84dad0d382f42064e9933fdaedb87e468cc239f0eabea73ddcb6"
 dependencies = [
  "ahash",
  "bitflags 2.4.1",
@@ -3352,9 +3352,9 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfac65326cc561112af88c3028f6dfdb140acff67ede33a8e86be2dc6b8956f7"
+checksum = "58d4f2b3f7bd2ba10f99e03f885ff90d5db3455e163bccecebbbf60406bd8980"
 dependencies = [
  "anyhow",
  "termcolor",
@@ -3557,9 +3557,10 @@ dependencies = [
  "wasmtime-wasi",
  "wasmtime-wasi-http",
  "wasmtime-wasi-nn",
+ "wasmtime-wasi-runtime-config",
  "wasmtime-wasi-threads",
  "wasmtime-wast",
- "wast 212.0.0",
+ "wast 214.0.0",
  "wat",
  "windows-sys 0.52.0",
  "wit-component",
@@ -3886,6 +3887,17 @@ dependencies = [
  "windows",
 ]
 
+[[package]]
+name = "wasmtime-wasi-runtime-config"
+version = "24.0.0"
+dependencies = [
+ "anyhow",
+ "test-programs-artifacts",
+ "tokio",
+ "wasmtime",
+ "wasmtime-wasi",
+]
+
 [[package]]
 name = "wasmtime-wasi-threads"
 version = "24.0.0"
@@ -3904,7 +3916,7 @@ dependencies = [
  "anyhow",
  "log",
  "wasmtime",
- "wast 212.0.0",
+ "wast 214.0.0",
 ]
 
 [[package]]
@@ -3947,9 +3959,9 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "212.0.0"
+version = "214.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4606a05fb0aae5d11dd7d8280a640d88a63ee019360ba9be552da3d294b8d1f5"
+checksum = "694bcdb24c49c8709bd8713768b71301a11e823923eee355d530f1d8d0a7f8e9"
 dependencies = [
  "bumpalo",
  "leb128",
@@ -3960,11 +3972,11 @@ dependencies = [
 
 [[package]]
 name = "wat"
-version = "1.212.0"
+version = "1.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c74ca7f93f11a5d6eed8499f2a8daaad6e225cab0151bc25a091fff3b987532f"
+checksum = "347249eb56773fa728df2656cfe3a8c19437ded61a922a0b5e0839d9790e278e"
 dependencies = [
- "wast 212.0.0",
+ "wast 214.0.0",
 ]
 
 [[package]]
@@ -4271,9 +4283,9 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen"
-version = "0.27.0"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fabce76bbb8938536c437da0c3e1d4dda9065453f72a68f797c0cb3d67356a28"
+checksum = "89178260ed223de8a5a81f9cff961481dfbbd55b25c17e4dd0b4c8e4b8ae646d"
 dependencies = [
  "wit-bindgen-rt",
  "wit-bindgen-rust-macro",
@@ -4281,9 +4293,9 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen-core"
-version = "0.27.0"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b43fbdd3497c471bbfb6973b1fb9ffe6949f158248cb43171d6f1cf3de7eaa3"
+checksum = "5e3fd9b11c16b9888c1bd159130b1b3487da913c45dbd34d408bfdf81f8a865a"
 dependencies = [
  "anyhow",
  "heck 0.5.0",
@@ -4292,18 +4304,18 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen-rt"
-version = "0.27.0"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75956ff0a04a87ca0526b07199ce3b9baee899f2e4723b5b63aa296ab172ec52"
+checksum = "d7a37bd9274cb2d4754b915d624447ec0dce9105d174361841c0826efc79ceb9"
 dependencies = [
  "bitflags 2.4.1",
 ]
 
 [[package]]
 name = "wit-bindgen-rust"
-version = "0.27.0"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf509c4ef97b18ec0218741c8318706ac30ff16bc1731f990319a42bbbcfe8e3"
+checksum = "0f195cd3774ff22f9bbd582a4ab97667c0a47d36ed8ed0c9ed357afe811b564b"
 dependencies = [
  "anyhow",
  "heck 0.5.0",
@@ -4317,9 +4329,9 @@ dependencies = [
 
 [[package]]
 name = "wit-bindgen-rust-macro"
-version = "0.27.0"
+version = "0.28.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d6f2e025e38395d71fc1bf064e581b2ad275ce322d6f8d87ddc5e76a7b8c42"
+checksum = "683e47441b5d0a82fc4304619dcc0672bc84ef47de2c85cd493c37cb29de062f"
 dependencies = [
  "anyhow",
  "prettyplease",
@@ -4332,9 +4344,9 @@ dependencies = [
 
 [[package]]
 name = "wit-component"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ed5b0f9fc3d6424787d2a49e1142bf954ae4f26ee891992c144f0cfd68c4b7f"
+checksum = "fd9fd46f0e783bf80f1ab7291f9d442fa5553ff0e96cdb71964bd8859b734b55"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
@@ -4351,9 +4363,9 @@ dependencies = [
 
 [[package]]
 name = "wit-parser"
-version = "0.212.0"
+version = "0.214.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ceeb0424aa8679f3fcf2d6e3cfa381f3d6fa6179976a2c05a6249dd2bb426716"
+checksum = "681d526d6ea42e28f9afe9eae2b50e0b0a627aef8822c75eb04078db84d03e57"
 dependencies = [
  "anyhow",
  "id-arena",
diff --git a/Cargo.toml b/Cargo.toml
index 3043a9320..8217a90de 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -53,6 +53,7 @@ wasmtime-wast = { workspace = true, optional = true }
 wasi-common = { workspace = true, default-features = true, features = ["exit"], optional = true }
 wasmtime-wasi = { workspace = true, default-features = true, optional = true }
 wasmtime-wasi-nn = { workspace = true, optional = true }
+wasmtime-wasi-runtime-config = { workspace = true, optional = true }
 wasmtime-wasi-threads = { workspace = true, optional = true }
 wasmtime-wasi-http = { workspace = true, optional = true }
 clap = { workspace = true }
@@ -192,6 +193,7 @@ wasmtime-wast = { path = "crates/wast", version = "=24.0.0" }
 wasmtime-wasi = { path = "crates/wasi", version = "24.0.0", default-features = false }
 wasmtime-wasi-http = { path = "crates/wasi-http", version = "=24.0.0", default-features = false }
 wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "24.0.0" }
+wasmtime-wasi-runtime-config = { path = "crates/wasi-runtime-config", version = "24.0.0" }
 wasmtime-wasi-threads = { path = "crates/wasi-threads", version = "24.0.0" }
 wasmtime-component-util = { path = "crates/component-util", version = "=24.0.0" }
 wasmtime-component-macro = { path = "crates/component-macro", version = "=24.0.0" }
@@ -249,19 +251,19 @@ io-lifetimes = { version = "2.0.3", default-features = false }
 io-extras = "0.18.1"
 rustix = "0.38.31"
 # wit-bindgen:
-wit-bindgen = { version = "0.27.0", default-features = false }
-wit-bindgen-rust-macro = { version = "0.27.0", default-features = false }
+wit-bindgen = { version = "0.28.0", default-features = false }
+wit-bindgen-rust-macro = { version = "0.28.0", default-features = false }
 
 # wasm-tools family:
-wasmparser = { version = "0.212.0", default-features = false }
-wat = "1.212.0"
-wast = "212.0.0"
-wasmprinter = "0.212.0"
-wasm-encoder = "0.212.0"
-wasm-smith = "0.212.0"
-wasm-mutate = "0.212.0"
-wit-parser = "0.212.0"
-wit-component = "0.212.0"
+wasmparser = { version = "0.214.0", default-features = false }
+wat = "1.214.0"
+wast = "214.0.0"
+wasmprinter = "0.214.0"
+wasm-encoder = "0.214.0"
+wasm-smith = "0.214.0"
+wasm-mutate = "0.214.0"
+wit-parser = "0.214.0"
+wit-component = "0.214.0"
 
 # Non-Bytecode Alliance maintained dependencies:
 # --------------------------
diff --git a/ci/vendor-wit.sh b/ci/vendor-wit.sh
index 53ce9ad80..374d9c10f 100755
--- a/ci/vendor-wit.sh
+++ b/ci/vendor-wit.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env bash
 
 # Script to re-vendor the WIT files that Wasmtime uses as defined by a
 # particular tag in upstream repositories.
@@ -6,31 +6,59 @@
 # This script is executed on CI to ensure that everything is up-to-date.
 set -ex
 
-# Space-separated list of wasi proposals that are vendored here along with the
-# tag that they're all vendored at.
-#
-# This assumes that the repositories all have the pattern:
-# https://github.com/WebAssembly/wasi-$repo
-# and every repository has a tag `v$tag` here. That is currently done as part
-# of the WASI release process.
-repos="cli clocks filesystem http io random sockets"
-tag=0.2.0
-
-# First, replace the existing vendored WIT files in the `wasi` crate.
-dst=crates/wasi/wit/deps
-rm -rf $dst
-mkdir -p $dst
-for repo in $repos; do
-  mkdir $dst/$repo
-  curl -L https://github.com/WebAssembly/wasi-$repo/archive/refs/tags/v$tag.tar.gz | \
-    tar xzf - --strip-components=2 -C $dst/$repo wasi-$repo-$tag/wit
-  rm -rf $dst/$repo/deps*
-done
-
-# Also replace the `wasi-http` WIT files since they match those in the `wasi`
-# crate.
-rm -rf crates/wasi-http/wit/deps
-cp -r $dst crates/wasi-http/wit
+# The make_vendor function takes a base path (e.g., "wasi") and a list
+# of packages in the format "name@tag". It constructs the full destination
+# path, downloads the tarballs from GitHub, extracts the relevant files, and
+# removes any unwanted directories.
+make_vendor() {
+  local name=$1
+  local packages=$2
+  local path="crates/$name/wit/deps"
+
+  rm -rf $path
+  mkdir -p $path
+
+  for package in $packages; do
+    IFS='@' read -r repo tag <<< "$package"
+    mkdir -p $path/$repo
+    cached_extracted_dir="$cache_dir/$repo-$tag"
+
+    if [[ ! -d $cached_extracted_dir ]]; then
+      mkdir -p $cached_extracted_dir
+      curl -sL https://github.com/WebAssembly/wasi-$repo/archive/$tag.tar.gz | \
+        tar xzf - --strip-components=1 -C $cached_extracted_dir
+      rm -rf $cached_extracted_dir/wit/deps*
+    fi
+
+    cp -r $cached_extracted_dir/wit/* $path/$repo
+  done
+}
+
+cache_dir=$(mktemp -d)
+
+make_vendor "wasi" "
+  cli@v0.2.0
+  clocks@v0.2.0
+  filesystem@v0.2.0
+  io@v0.2.0
+  random@v0.2.0
+  sockets@v0.2.0
+  http@v0.2.0
+"
+
+make_vendor "wasi-http" "
+  cli@v0.2.0
+  clocks@v0.2.0
+  filesystem@v0.2.0
+  io@v0.2.0
+  random@v0.2.0
+  sockets@v0.2.0
+  http@v0.2.0
+"
+
+make_vendor "wasi-runtime-config" "runtime-config@c667fe6"
+
+rm -rf $cache_dir
 
 # Separately (for now), vendor the `wasi-nn` WIT files since their retrieval is
 # slightly different than above.
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index 922e4ebfc..ff1ccd972 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -4,7 +4,6 @@ use crate::ir;
 use crate::ir::types;
 use crate::ir::types::*;
 use crate::ir::MemFlags;
-use crate::ir::Opcode;
 use crate::ir::{dynamic_to_fixed, ExternalName, LibCall, Signature};
 use crate::isa;
 use crate::isa::aarch64::{inst::*, settings as aarch64_settings};
@@ -1019,7 +1018,6 @@ impl ABIMachineSpec for AArch64MachineDeps {
         uses: CallArgList,
         defs: CallRetList,
         clobbers: PRegSet,
-        opcode: ir::Opcode,
         tmp: Writable<Reg>,
         callee_conv: isa::CallConv,
         caller_conv: isa::CallConv,
@@ -1033,7 +1031,6 @@ impl ABIMachineSpec for AArch64MachineDeps {
                     uses,
                     defs,
                     clobbers,
-                    opcode,
                     caller_callconv: caller_conv,
                     callee_callconv: callee_conv,
                     callee_pop_size,
@@ -1051,7 +1048,6 @@ impl ABIMachineSpec for AArch64MachineDeps {
                         uses,
                         defs,
                         clobbers,
-                        opcode,
                         caller_callconv: caller_conv,
                         callee_callconv: callee_conv,
                         callee_pop_size,
@@ -1064,7 +1060,6 @@ impl ABIMachineSpec for AArch64MachineDeps {
                     uses,
                     defs,
                     clobbers,
-                    opcode,
                     caller_callconv: caller_conv,
                     callee_callconv: callee_conv,
                     callee_pop_size,
@@ -1107,7 +1102,6 @@ impl ABIMachineSpec for AArch64MachineDeps {
                 ],
                 defs: smallvec![],
                 clobbers: Self::get_regs_clobbered_by_call(call_conv),
-                opcode: Opcode::Call,
                 caller_callconv: call_conv,
                 callee_callconv: call_conv,
                 callee_pop_size: 0,
@@ -1299,11 +1293,9 @@ impl AArch64CallSite {
         self.emit_stack_ret_arg_for_tail_call(ctx);
 
         let dest = self.dest().clone();
-        let opcode = self.opcode();
         let uses = self.take_uses();
         let info = Box::new(ReturnCallInfo {
             uses,
-            opcode,
             new_stack_arg_size,
             key: select_api_key(isa_flags, isa::CallConv::Tail, true),
         });
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index d231a3280..de63d7cef 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -2945,9 +2945,7 @@ impl MachInstEmit for Inst {
                     let offset = sink.cur_offset();
                     sink.push_user_stack_map(state, offset, s);
                 }
-                if info.opcode.is_call() {
-                    sink.add_call_site(info.opcode);
-                }
+                sink.add_call_site();
 
                 if info.callee_pop_size > 0 {
                     let callee_pop_size =
@@ -2968,9 +2966,7 @@ impl MachInstEmit for Inst {
                     let offset = sink.cur_offset();
                     sink.push_user_stack_map(state, offset, s);
                 }
-                if info.opcode.is_call() {
-                    sink.add_call_site(info.opcode);
-                }
+                sink.add_call_site();
 
                 if info.callee_pop_size > 0 {
                     let callee_pop_size =
@@ -2991,7 +2987,7 @@ impl MachInstEmit for Inst {
                 // for the target, but rather a function relocation.
                 sink.add_reloc(Reloc::Arm64Call, &**callee, 0);
                 sink.put4(enc_jump26(0b000101, 0));
-                sink.add_call_site(ir::Opcode::ReturnCall);
+                sink.add_call_site();
 
                 // `emit_return_call_common_sequence` emits an island if
                 // necessary, so we can safely disable the worst-case-size check
@@ -3006,7 +3002,7 @@ impl MachInstEmit for Inst {
                     targets: vec![],
                 }
                 .emit(sink, emit_info, state);
-                sink.add_call_site(ir::Opcode::ReturnCallIndirect);
+                sink.add_call_site();
 
                 // `emit_return_call_common_sequence` emits an island if
                 // necessary, so we can safely disable the worst-case-size check
@@ -3398,7 +3394,6 @@ impl MachInstEmit for Inst {
                         uses: smallvec![],
                         defs: smallvec![],
                         clobbers: PRegSet::empty(),
-                        opcode: Opcode::CallIndirect,
                         caller_callconv: CallConv::SystemV,
                         callee_callconv: CallConv::SystemV,
                         callee_pop_size: 0,
@@ -3459,7 +3454,6 @@ impl MachInstEmit for Inst {
                         uses: smallvec![],
                         defs: smallvec![],
                         clobbers: PRegSet::empty(),
-                        opcode: Opcode::CallIndirect,
                         caller_callconv: CallConv::AppleAarch64,
                         callee_callconv: CallConv::AppleAarch64,
                         callee_pop_size: 0,
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index 3098c4c98..8bb7c8529 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -2,7 +2,7 @@
 
 use crate::binemit::{Addend, CodeOffset, Reloc};
 use crate::ir::types::{F32, F64, I128, I16, I32, I64, I8, I8X16, R32, R64};
-use crate::ir::{types, ExternalName, MemFlags, Opcode, Type};
+use crate::ir::{types, ExternalName, MemFlags, Type};
 use crate::isa::{CallConv, FunctionAlignment};
 use crate::machinst::*;
 use crate::{settings, CodegenError, CodegenResult};
@@ -86,8 +86,6 @@ pub struct CallInfo {
     pub defs: CallRetList,
     /// Clobbers register set.
     pub clobbers: PRegSet,
-    /// Instruction opcode.
-    pub opcode: Opcode,
     /// Caller calling convention.
     pub caller_callconv: CallConv,
     /// Callee calling convention.
@@ -110,8 +108,6 @@ pub struct CallIndInfo {
     pub defs: SmallVec<[CallRetPair; 8]>,
     /// Clobbers register set.
     pub clobbers: PRegSet,
-    /// Instruction opcode.
-    pub opcode: Opcode,
     /// Caller calling convention.
     pub caller_callconv: CallConv,
     /// Callee calling convention.
@@ -128,8 +124,6 @@ pub struct CallIndInfo {
 pub struct ReturnCallInfo {
     /// Arguments to the call instruction.
     pub uses: CallArgList,
-    /// Instruction opcode.
-    pub opcode: Opcode,
     /// The size of the new stack frame's stack arguments. This is necessary
     /// for copying the frame over our current frame. It must already be
     /// allocated on the stack.
diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
index 69a67905a..0191e7cd5 100644
--- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
@@ -18,7 +18,6 @@ use crate::isa::aarch64::inst::{FPULeftShiftImm, FPURightShiftImm, ReturnCallInf
 use crate::isa::aarch64::AArch64Backend;
 use crate::isle_common_prelude_methods;
 use crate::machinst::isle::*;
-use crate::machinst::valueregs;
 use crate::{
     binemit::CodeOffset,
     ir::{
@@ -28,7 +27,9 @@ use crate::{
     isa::aarch64::abi::AArch64CallSite,
     isa::aarch64::inst::args::{ShiftOp, ShiftOpShiftImm},
     isa::aarch64::inst::SImm7Scaled,
-    machinst::{abi::ArgPair, ty_bits, InstOutput, MachInst, VCodeConstant, VCodeConstantData},
+    machinst::{
+        abi::ArgPair, ty_bits, InstOutput, IsTailCall, MachInst, VCodeConstant, VCodeConstantData,
+    },
 };
 use regalloc2::PReg;
 use std::boxed::Box;
@@ -70,10 +71,6 @@ pub struct ExtendedValue {
     extend: ExtendOp,
 }
 
-impl IsleContext<'_, '_, MInst, AArch64Backend> {
-    isle_prelude_method_helpers!(AArch64CallSite);
-}
-
 impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
     isle_lower_prelude_methods!();
     isle_prelude_caller_methods!(
@@ -99,7 +96,7 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
             self.lower_ctx.sigs(),
             callee_sig,
             &callee,
-            Opcode::ReturnCall,
+            IsTailCall::Yes,
             distance,
             caller_conv,
             self.backend.flags().clone(),
@@ -128,7 +125,7 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
             self.lower_ctx.sigs(),
             callee_sig,
             callee,
-            Opcode::ReturnCallIndirect,
+            IsTailCall::Yes,
             caller_conv,
             self.backend.flags().clone(),
         );
diff --git a/cranelift/codegen/src/isa/riscv64/abi.rs b/cranelift/codegen/src/isa/riscv64/abi.rs
index 05f3f3a45..2a6b23f3a 100644
--- a/cranelift/codegen/src/isa/riscv64/abi.rs
+++ b/cranelift/codegen/src/isa/riscv64/abi.rs
@@ -433,7 +433,6 @@ impl ABIMachineSpec for Riscv64MachineDeps {
                 }],
                 defs: smallvec![],
                 clobbers: PRegSet::empty(),
-                opcode: Opcode::Call,
                 callee_callconv: CallConv::SystemV,
                 caller_callconv: CallConv::SystemV,
                 callee_pop_size: 0,
@@ -573,7 +572,6 @@ impl ABIMachineSpec for Riscv64MachineDeps {
         uses: CallArgList,
         defs: CallRetList,
         clobbers: PRegSet,
-        opcode: ir::Opcode,
         tmp: Writable<Reg>,
         callee_conv: isa::CallConv,
         caller_conv: isa::CallConv,
@@ -587,7 +585,6 @@ impl ABIMachineSpec for Riscv64MachineDeps {
                     uses,
                     defs,
                     clobbers,
-                    opcode,
                     caller_callconv: caller_conv,
                     callee_callconv: callee_conv,
                     callee_pop_size,
@@ -605,7 +602,6 @@ impl ABIMachineSpec for Riscv64MachineDeps {
                         uses,
                         defs,
                         clobbers,
-                        opcode,
                         caller_callconv: caller_conv,
                         callee_callconv: callee_conv,
                         callee_pop_size,
@@ -618,7 +614,6 @@ impl ABIMachineSpec for Riscv64MachineDeps {
                     uses,
                     defs,
                     clobbers,
-                    opcode,
                     caller_callconv: caller_conv,
                     callee_callconv: callee_conv,
                     callee_pop_size,
@@ -660,7 +655,6 @@ impl ABIMachineSpec for Riscv64MachineDeps {
                 ],
                 defs: smallvec![],
                 clobbers: Self::get_regs_clobbered_by_call(call_conv),
-                opcode: Opcode::Call,
                 caller_callconv: call_conv,
                 callee_callconv: call_conv,
                 callee_pop_size: 0,
@@ -778,11 +772,9 @@ impl Riscv64ABICallSite {
         self.emit_stack_ret_arg_for_tail_call(ctx);
 
         let dest = self.dest().clone();
-        let opcode = self.opcode();
         let uses = self.take_uses();
         let info = Box::new(ReturnCallInfo {
             uses,
-            opcode,
             new_stack_arg_size,
         });
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
index fb63c355f..ea279b4f4 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
@@ -1144,9 +1144,7 @@ impl Inst {
             }
 
             &Inst::Call { ref info } => {
-                if info.opcode.is_call() {
-                    sink.add_call_site(info.opcode);
-                }
+                sink.add_call_site();
                 sink.add_reloc(Reloc::RiscvCallPlt, &info.dest, 0);
 
                 let (stack_map, user_stack_map) = state.take_stack_map();
@@ -1189,9 +1187,7 @@ impl Inst {
                     sink.push_user_stack_map(state, offset, s);
                 }
 
-                if info.opcode.is_call() {
-                    sink.add_call_site(info.opcode);
-                }
+                sink.add_call_site();
 
                 let callee_pop_size = i32::try_from(info.callee_pop_size).unwrap();
                 if callee_pop_size > 0 {
@@ -1207,7 +1203,7 @@ impl Inst {
             } => {
                 emit_return_call_common_sequence(sink, emit_info, state, info);
 
-                sink.add_call_site(ir::Opcode::ReturnCall);
+                sink.add_call_site();
                 sink.add_reloc(Reloc::RiscvCallPlt, &**callee, 0);
                 Inst::construct_auipc_and_jalr(None, writable_spilltmp_reg(), 0)
                     .into_iter()
@@ -2037,7 +2033,6 @@ impl Inst {
                         dest: ExternalName::LibCall(LibCall::ElfTlsGetAddr),
                         uses: smallvec![],
                         defs: smallvec![],
-                        opcode: crate::ir::Opcode::TlsValue,
                         caller_callconv: CallConv::SystemV,
                         callee_callconv: CallConv::SystemV,
                         callee_pop_size: 0,
diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
index f80ca99e2..80bd149b7 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
@@ -5,7 +5,7 @@ use crate::binemit::{Addend, CodeOffset, Reloc};
 pub use crate::ir::condcodes::IntCC;
 use crate::ir::types::{self, F32, F64, I128, I16, I32, I64, I8, I8X16, R32, R64};
 
-pub use crate::ir::{ExternalName, MemFlags, Opcode, Type};
+pub use crate::ir::{ExternalName, MemFlags, Type};
 use crate::isa::{CallConv, FunctionAlignment};
 use crate::machinst::*;
 use crate::{settings, CodegenError, CodegenResult};
@@ -58,7 +58,6 @@ pub struct CallInfo {
     pub dest: ExternalName,
     pub uses: CallArgList,
     pub defs: CallRetList,
-    pub opcode: Opcode,
     pub caller_callconv: CallConv,
     pub callee_callconv: CallConv,
     pub clobbers: PRegSet,
@@ -72,7 +71,6 @@ pub struct CallIndInfo {
     pub rn: Reg,
     pub uses: CallArgList,
     pub defs: CallRetList,
-    pub opcode: Opcode,
     pub caller_callconv: CallConv,
     pub callee_callconv: CallConv,
     pub clobbers: PRegSet,
@@ -84,7 +82,6 @@ pub struct CallIndInfo {
 #[derive(Clone, Debug)]
 pub struct ReturnCallInfo {
     pub uses: CallArgList,
-    pub opcode: Opcode,
     pub new_stack_arg_size: u32,
 }
 
diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
index 14b12e3a6..5695700f4 100644
--- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
@@ -3,7 +3,7 @@
 // Pull in the ISLE generated code.
 #[allow(unused)]
 pub mod generated_code;
-use generated_code::{Context, MInst};
+use generated_code::MInst;
 
 // Types that the generated ISLE code uses via `use super::*`.
 use self::generated_code::{VecAluOpRR, VecLmul};
@@ -21,7 +21,7 @@ use crate::{
         MemFlags, Opcode, TrapCode, Value, ValueList,
     },
     isa::riscv64::inst::*,
-    machinst::{ArgPair, InstOutput},
+    machinst::{ArgPair, InstOutput, IsTailCall},
 };
 use crate::{isa, isle_common_prelude_methods};
 use regalloc2::PReg;
@@ -34,7 +34,6 @@ type BoxReturnCallInfo = Box<ReturnCallInfo>;
 type BoxExternalName = Box<ExternalName>;
 type VecMachLabel = Vec<MachLabel>;
 type VecArgPair = Vec<ArgPair>;
-use crate::machinst::valueregs;
 
 pub(crate) struct RV64IsleContext<'a, 'b, I, B>
 where
@@ -49,8 +48,6 @@ where
 }
 
 impl<'a, 'b> RV64IsleContext<'a, 'b, MInst, Riscv64Backend> {
-    isle_prelude_method_helpers!(Riscv64ABICallSite);
-
     fn new(lower_ctx: &'a mut Lower<'b, MInst>, backend: &'a Riscv64Backend) -> Self {
         Self {
             lower_ctx,
@@ -82,7 +79,7 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
             self.lower_ctx.sigs(),
             callee_sig,
             &callee,
-            Opcode::ReturnCall,
+            IsTailCall::Yes,
             distance,
             caller_conv,
             self.backend.flags().clone(),
@@ -111,7 +108,7 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
             self.lower_ctx.sigs(),
             callee_sig,
             callee,
-            Opcode::ReturnCallIndirect,
+            IsTailCall::Yes,
             caller_conv,
             self.backend.flags().clone(),
         );
diff --git a/cranelift/codegen/src/isa/s390x/abi.rs b/cranelift/codegen/src/isa/s390x/abi.rs
index 16375be8b..dc01aa0fb 100644
--- a/cranelift/codegen/src/isa/s390x/abi.rs
+++ b/cranelift/codegen/src/isa/s390x/abi.rs
@@ -733,7 +733,6 @@ impl ABIMachineSpec for S390xMachineDeps {
         _uses: CallArgList,
         _defs: CallRetList,
         _clobbers: PRegSet,
-        _opcode: ir::Opcode,
         _tmp: Writable<Reg>,
         _callee_conv: isa::CallConv,
         _caller_conv: isa::CallConv,
diff --git a/cranelift/codegen/src/isa/s390x/inst.isle b/cranelift/codegen/src/isa/s390x/inst.isle
index bc0529abf..7f4599741 100644
--- a/cranelift/codegen/src/isa/s390x/inst.isle
+++ b/cranelift/codegen/src/isa/s390x/inst.isle
@@ -3535,22 +3535,22 @@
 (decl abi_first_ret (SigRef Sig) usize)
 (extern constructor abi_first_ret abi_first_ret)
 
-(decl abi_call_info (Sig ExternalName CallArgList CallRetList Opcode) BoxCallInfo)
+(decl abi_call_info (Sig ExternalName CallArgList CallRetList) BoxCallInfo)
 (extern constructor abi_call_info abi_call_info)
 
-(decl abi_call_ind_info (Sig Reg CallArgList CallRetList Opcode) BoxCallIndInfo)
+(decl abi_call_ind_info (Sig Reg CallArgList CallRetList) BoxCallIndInfo)
 (extern constructor abi_call_ind_info abi_call_ind_info)
 
 (decl writable_link_reg () WritableReg)
 (rule (writable_link_reg) (writable_gpr 14))
 
-(decl abi_call (Sig ExternalName CallArgList CallRetList Opcode) SideEffectNoResult)
-(rule (abi_call abi name uses defs opcode)
-      (call_impl (writable_link_reg) (abi_call_info abi name uses defs opcode)))
+(decl abi_call (Sig ExternalName CallArgList CallRetList) SideEffectNoResult)
+(rule (abi_call abi name uses defs)
+      (call_impl (writable_link_reg) (abi_call_info abi name uses defs)))
 
-(decl abi_call_ind (Sig Reg CallArgList CallRetList Opcode) SideEffectNoResult)
-(rule (abi_call_ind abi target uses defs opcode)
-      (call_ind_impl (writable_link_reg) (abi_call_ind_info abi target uses defs opcode)))
+(decl abi_call_ind (Sig Reg CallArgList CallRetList) SideEffectNoResult)
+(rule (abi_call_ind abi target uses defs)
+      (call_ind_impl (writable_link_reg) (abi_call_ind_info abi target uses defs)))
 
 (decl abi_accumulate_outgoing_args_size (Sig) Unit)
 (extern constructor abi_accumulate_outgoing_args_size abi_accumulate_outgoing_args_size)
diff --git a/cranelift/codegen/src/isa/s390x/inst/emit.rs b/cranelift/codegen/src/isa/s390x/inst/emit.rs
index 5c72b887e..dace88c89 100644
--- a/cranelift/codegen/src/isa/s390x/inst/emit.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/emit.rs
@@ -3267,9 +3267,7 @@ impl Inst {
                 }
 
                 put(sink, &enc_ril_b(opcode, link.to_reg(), 0));
-                if info.opcode.is_call() {
-                    sink.add_call_site(info.opcode);
-                }
+                sink.add_call_site();
             }
             &Inst::CallInd { link, ref info } => {
                 debug_assert_eq!(link.to_reg(), gpr(14));
@@ -3286,9 +3284,7 @@ impl Inst {
 
                 let opcode = 0x0d; // BASR
                 put(sink, &enc_rr(opcode, link.to_reg(), rn));
-                if info.opcode.is_call() {
-                    sink.add_call_site(info.opcode);
-                }
+                sink.add_call_site();
             }
             &Inst::Args { .. } => {}
             &Inst::Rets { .. } => {}
diff --git a/cranelift/codegen/src/isa/s390x/inst/mod.rs b/cranelift/codegen/src/isa/s390x/inst/mod.rs
index 6b1d9f531..05a36e098 100644
--- a/cranelift/codegen/src/isa/s390x/inst/mod.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/mod.rs
@@ -1,14 +1,14 @@
 //! This module defines s390x-specific machine instruction types.
 
 use crate::binemit::{Addend, CodeOffset, Reloc};
-use crate::ir::{types, ExternalName, Opcode, Type};
+use crate::ir::{types, ExternalName, Type};
 use crate::isa::s390x::abi::S390xMachineDeps;
 use crate::isa::{CallConv, FunctionAlignment};
 use crate::machinst::*;
 use crate::{settings, CodegenError, CodegenResult};
 use alloc::boxed::Box;
 use alloc::vec::Vec;
-use regalloc2::PRegSet;
+use regalloc2::{PReg, PRegSet};
 use smallvec::SmallVec;
 use std::fmt::Write;
 use std::string::{String, ToString};
@@ -42,7 +42,6 @@ pub struct CallInfo {
     pub uses: CallArgList,
     pub defs: CallRetList,
     pub clobbers: PRegSet,
-    pub opcode: Opcode,
     pub caller_callconv: CallConv,
     pub callee_callconv: CallConv,
     pub tls_symbol: Option<SymbolReloc>,
@@ -56,7 +55,6 @@ pub struct CallIndInfo {
     pub uses: CallArgList,
     pub defs: CallRetList,
     pub clobbers: PRegSet,
-    pub opcode: Opcode,
     pub caller_callconv: CallConv,
     pub callee_callconv: CallConv,
 }
@@ -885,28 +883,41 @@ fn s390x_get_operands(inst: &mut Inst, collector: &mut DenyReuseVisitor<impl Ope
             collector.reg_use(rn);
         }
         Inst::Call { link, info } => {
-            let CallInfo { uses, defs, .. } = &mut **info;
+            let CallInfo {
+                uses,
+                defs,
+                clobbers,
+                ..
+            } = &mut **info;
             for CallArgPair { vreg, preg } in uses {
                 collector.reg_fixed_use(vreg, *preg);
             }
+            let mut clobbers = clobbers.clone();
+            clobbers.add(link.to_reg().to_real_reg().unwrap().into());
             for CallRetPair { vreg, preg } in defs {
+                clobbers.remove(PReg::from(preg.to_real_reg().unwrap()));
                 collector.reg_fixed_def(vreg, *preg);
             }
-            let mut clobbers = info.clobbers.clone();
-            clobbers.add(link.to_reg().to_real_reg().unwrap().into());
             collector.reg_clobbers(clobbers);
         }
         Inst::CallInd { link, info } => {
-            let CallIndInfo { rn, uses, defs, .. } = &mut **info;
+            let CallIndInfo {
+                rn,
+                uses,
+                defs,
+                clobbers,
+                ..
+            } = &mut **info;
             collector.reg_use(rn);
             for CallArgPair { vreg, preg } in uses {
                 collector.reg_fixed_use(vreg, *preg);
             }
+            let mut clobbers = clobbers.clone();
+            clobbers.add(link.to_reg().to_real_reg().unwrap().into());
             for CallRetPair { vreg, preg } in defs {
+                clobbers.remove(PReg::from(preg.to_real_reg().unwrap()));
                 collector.reg_fixed_def(vreg, *preg);
             }
-            let mut clobbers = info.clobbers.clone();
-            clobbers.add(link.to_reg().to_real_reg().unwrap().into());
             collector.reg_clobbers(clobbers);
         }
         Inst::Args { args } => {
diff --git a/cranelift/codegen/src/isa/s390x/lower.isle b/cranelift/codegen/src/isa/s390x/lower.isle
index 977a28c02..c04d3fd8c 100644
--- a/cranelift/codegen/src/isa/s390x/lower.isle
+++ b/cranelift/codegen/src/isa/s390x/lower.isle
@@ -3900,7 +3900,7 @@
             (_ Unit (abi_accumulate_outgoing_args_size abi))
             (uses CallArgList (lower_call_args abi (range 0 (abi_num_args abi)) args))
             (defs CallRetList (defs_init abi))
-            (_ InstOutput (side_effect (abi_call abi name uses defs (Opcode.Call)))))
+            (_ InstOutput (side_effect (abi_call abi name uses defs))))
         (lower_call_rets abi defs (range (abi_first_ret sig_ref abi)
                                          (abi_num_rets abi)) (output_builder_new))))
 
@@ -3911,7 +3911,7 @@
             (uses CallArgList (lower_call_args abi (range 0 (abi_num_args abi)) args))
             (defs CallRetList (defs_init abi))
             (target Reg (load_symbol_reloc (SymbolReloc.Absolute name 0)))
-            (_ InstOutput (side_effect (abi_call_ind abi target uses defs (Opcode.Call)))))
+            (_ InstOutput (side_effect (abi_call_ind abi target uses defs))))
         (lower_call_rets abi defs (range (abi_first_ret sig_ref abi)
                                          (abi_num_rets abi)) (output_builder_new))))
 
@@ -3922,7 +3922,7 @@
             (_ Unit (abi_accumulate_outgoing_args_size abi))
             (uses CallArgList (lower_call_args abi (range 0 (abi_num_args abi)) args))
             (defs CallRetList (defs_init abi))
-            (_ InstOutput (side_effect (abi_call_ind abi target uses defs (Opcode.CallIndirect)))))
+            (_ InstOutput (side_effect (abi_call_ind abi target uses defs))))
         (lower_call_rets abi defs (range (abi_first_ret sig_ref abi)
                                          (abi_num_rets abi)) (output_builder_new))))
 
diff --git a/cranelift/codegen/src/isa/s390x/lower/isle.rs b/cranelift/codegen/src/isa/s390x/lower/isle.rs
index 17b55d0fa..f2937856b 100644
--- a/cranelift/codegen/src/isa/s390x/lower/isle.rs
+++ b/cranelift/codegen/src/isa/s390x/lower/isle.rs
@@ -193,15 +193,16 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> {
         name: ExternalName,
         uses: &CallArgList,
         defs: &CallRetList,
-        opcode: &Opcode,
     ) -> BoxCallInfo {
-        let clobbers = self.lower_ctx.sigs().call_clobbers::<S390xMachineDeps>(abi);
+        let sig_data = &self.lower_ctx.sigs()[abi];
+        // Get clobbers: all caller-saves. These may include return value
+        // regs, which we will remove from the clobber set later.
+        let clobbers = S390xMachineDeps::get_regs_clobbered_by_call(sig_data.call_conv());
         Box::new(CallInfo {
             dest: name.clone(),
             uses: uses.clone(),
             defs: defs.clone(),
             clobbers,
-            opcode: *opcode,
             caller_callconv: self.lower_ctx.abi().call_conv(self.lower_ctx.sigs()),
             callee_callconv: self.lower_ctx.sigs()[abi].call_conv(),
             tls_symbol: None,
@@ -214,15 +215,16 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> {
         target: Reg,
         uses: &CallArgList,
         defs: &CallRetList,
-        opcode: &Opcode,
     ) -> BoxCallIndInfo {
-        let clobbers = self.lower_ctx.sigs().call_clobbers::<S390xMachineDeps>(abi);
+        let sig_data = &self.lower_ctx.sigs()[abi];
+        // Get clobbers: all caller-saves. These may include return value
+        // regs, which we will remove from the clobber set later.
+        let clobbers = S390xMachineDeps::get_regs_clobbered_by_call(sig_data.call_conv());
         Box::new(CallIndInfo {
             rn: target,
             uses: uses.clone(),
             defs: defs.clone(),
             clobbers,
-            opcode: *opcode,
             caller_callconv: self.lower_ctx.abi().call_conv(self.lower_ctx.sigs()),
             callee_callconv: self.lower_ctx.sigs()[abi].call_conv(),
         })
@@ -288,18 +290,14 @@ impl generated_code::Context for IsleContext<'_, '_, MInst, S390xBackend> {
         let caller_callconv = self.lower_ctx.abi().call_conv(self.lower_ctx.sigs());
         let callee_callconv = CallConv::for_libcall(&self.backend.flags, caller_callconv);
 
-        // Clobbers are defined by the calling convention.  Remove defs from clobbers.
-        let mut clobbers = S390xMachineDeps::get_regs_clobbered_by_call(callee_callconv);
-        for reg in &info.defs {
-            clobbers.remove(PReg::from(reg.preg.to_real_reg().unwrap()));
-        }
+        // Clobbers are defined by the calling convention. We will remove return value regs later.
+        let clobbers = S390xMachineDeps::get_regs_clobbered_by_call(callee_callconv);
 
         Box::new(CallInfo {
             dest: ExternalName::LibCall(info.libcall),
             uses: info.uses.clone(),
             defs: info.defs.clone(),
             clobbers,
-            opcode: Opcode::Call,
             caller_callconv,
             callee_callconv,
             tls_symbol: info.tls_symbol.clone(),
diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs
index 6deb81b52..10d71f7aa 100644
--- a/cranelift/codegen/src/isa/x64/abi.rs
+++ b/cranelift/codegen/src/isa/x64/abi.rs
@@ -1,6 +1,6 @@
 //! Implementation of the standard x64 ABI.
 
-use crate::ir::{self, types, LibCall, MemFlags, Opcode, Signature, TrapCode};
+use crate::ir::{self, types, LibCall, MemFlags, Signature, TrapCode};
 use crate::ir::{types::*, ExternalName};
 use crate::isa;
 use crate::isa::{unwind::UnwindInst, x64::inst::*, x64::settings as x64_settings, CallConv};
@@ -591,7 +591,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
             Writable::from_reg(regs::rax()),
         ));
         insts.push(Inst::CallKnown {
-            opcode: Opcode::Call,
             dest: ExternalName::LibCall(LibCall::Probestack),
             info: Some(Box::new(CallInfo {
                 // No need to include arg here: we are post-regalloc
@@ -802,7 +801,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
         uses: CallArgList,
         defs: CallRetList,
         clobbers: PRegSet,
-        opcode: ir::Opcode,
         tmp: Writable<Reg>,
         callee_conv: isa::CallConv,
         _caller_conv: isa::CallConv,
@@ -816,7 +814,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
                     uses,
                     defs,
                     clobbers,
-                    opcode,
                     callee_pop_size,
                     callee_conv,
                 ));
@@ -833,7 +830,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
                     uses,
                     defs,
                     clobbers,
-                    opcode,
                     callee_pop_size,
                     callee_conv,
                 ));
@@ -844,7 +840,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
                     uses,
                     defs,
                     clobbers,
-                    opcode,
                     callee_pop_size,
                     callee_conv,
                 ));
@@ -896,7 +891,6 @@ impl ABIMachineSpec for X64ABIMachineSpec {
             ],
             /* defs = */ smallvec![],
             /* clobbers = */ Self::get_regs_clobbered_by_call(call_conv),
-            Opcode::Call,
             callee_pop_size,
             call_conv,
         ));
diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle
index bae6e211c..56a03c51c 100644
--- a/cranelift/codegen/src/isa/x64/inst.isle
+++ b/cranelift/codegen/src/isa/x64/inst.isle
@@ -532,12 +532,10 @@
 
        ;; Direct call: call simm32.
        (CallKnown (dest ExternalName)
-                  (opcode Opcode)
                   (info BoxCallInfo))
 
        ;; Indirect call: callq (reg mem)
        (CallUnknown (dest RegMem)
-                    (opcode Opcode)
                     (info BoxCallInfo))
 
        ;; Tail call to a direct destination.
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index dd5dfc002..25534059d 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -1,4 +1,3 @@
-use crate::ir;
 use crate::ir::immediates::{Ieee32, Ieee64};
 use crate::ir::KnownSymbol;
 use crate::isa::x64::encoding::evex::{EvexInstruction, EvexVectorLength, RegisterOrAmode};
@@ -1596,8 +1595,8 @@ pub(crate) fn emit(
 
         Inst::CallKnown {
             dest,
-            opcode,
             info: call_info,
+            ..
         } => {
             let (stack_map, user_stack_map) = state.take_stack_map();
             if let Some(s) = stack_map {
@@ -1613,9 +1612,7 @@ pub(crate) fn emit(
             // beginning of the immediate field.
             emit_reloc(sink, Reloc::X86CallPCRel4, &dest, -4);
             sink.put4(0);
-            if opcode.is_call() {
-                sink.add_call_site(*opcode);
-            }
+            sink.add_call_site();
 
             // Reclaim the outgoing argument area that was released by the callee, to ensure that
             // StackAMode values are always computed from a consistent SP.
@@ -1648,7 +1645,7 @@ pub(crate) fn emit(
             // beginning of the immediate field.
             emit_reloc(sink, Reloc::X86CallPCRel4, &callee, -4);
             sink.put4(0);
-            sink.add_call_site(ir::Opcode::ReturnCall);
+            sink.add_call_site();
         }
 
         Inst::ReturnCallUnknown {
@@ -1663,13 +1660,13 @@ pub(crate) fn emit(
                 target: RegMem::reg(callee),
             }
             .emit(sink, info, state);
-            sink.add_call_site(ir::Opcode::ReturnCallIndirect);
+            sink.add_call_site();
         }
 
         Inst::CallUnknown {
             dest,
-            opcode,
             info: call_info,
+            ..
         } => {
             let dest = dest.clone();
 
@@ -1712,9 +1709,7 @@ pub(crate) fn emit(
                 sink.push_user_stack_map(state, offset, s);
             }
 
-            if opcode.is_call() {
-                sink.add_call_site(*opcode);
-            }
+            sink.add_call_site();
 
             // Reclaim the outgoing argument area that was released by the callee, to ensure that
             // StackAMode values are always computed from a consistent SP.
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index fd8c375e6..ee79de792 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -3,7 +3,7 @@
 pub use emit_state::EmitState;
 
 use crate::binemit::{Addend, CodeOffset, Reloc, StackMap};
-use crate::ir::{types, ExternalName, LibCall, Opcode, TrapCode, Type};
+use crate::ir::{types, ExternalName, LibCall, TrapCode, Type};
 use crate::isa::x64::abi::X64ABIMachineSpec;
 use crate::isa::x64::inst::regs::{pretty_print_reg, show_ireg_sized};
 use crate::isa::x64::settings as x64_settings;
@@ -541,13 +541,11 @@ impl Inst {
         uses: CallArgList,
         defs: CallRetList,
         clobbers: PRegSet,
-        opcode: Opcode,
         callee_pop_size: u32,
         callee_conv: CallConv,
     ) -> Inst {
         Inst::CallKnown {
             dest,
-            opcode,
             info: Some(Box::new(CallInfo {
                 uses,
                 defs,
@@ -563,14 +561,12 @@ impl Inst {
         uses: CallArgList,
         defs: CallRetList,
         clobbers: PRegSet,
-        opcode: Opcode,
         callee_pop_size: u32,
         callee_conv: CallConv,
     ) -> Inst {
         dest.assert_regclass_is(RegClass::Int);
         Inst::CallUnknown {
             dest,
-            opcode,
             info: Some(Box::new(CallInfo {
                 uses,
                 defs,
diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs
index 5f63a2194..34b8ca61d 100644
--- a/cranelift/codegen/src/isa/x64/lower.rs
+++ b/cranelift/codegen/src/isa/x64/lower.rs
@@ -14,7 +14,7 @@ use crate::machinst::lower::*;
 use crate::machinst::*;
 use crate::result::CodegenResult;
 use crate::settings::Flags;
-use smallvec::smallvec;
+use smallvec::{smallvec, SmallVec};
 use target_lexicon::Triple;
 
 //=============================================================================
@@ -152,8 +152,7 @@ fn emit_vm_call(
     triple: &Triple,
     libcall: LibCall,
     inputs: &[Reg],
-    outputs: &[Writable<Reg>],
-) -> CodegenResult<()> {
+) -> CodegenResult<SmallVec<[Reg; 1]>> {
     let extname = ExternalName::LibCall(libcall);
 
     let dist = if flags.use_colocated_libcalls() {
@@ -182,8 +181,11 @@ fn emit_vm_call(
     }
 
     let mut retval_insts: SmallInstVec<_> = smallvec![];
-    for (i, output) in outputs.iter().enumerate() {
-        retval_insts.extend(abi.gen_retval(ctx, i, ValueRegs::one(*output)).into_iter());
+    let mut outputs: SmallVec<[_; 1]> = smallvec![];
+    for i in 0..ctx.sigs().num_rets(ctx.sigs().abi_sig_for_signature(&sig)) {
+        let (retval_inst, retval_regs) = abi.gen_retval(ctx, i);
+        retval_insts.extend(retval_inst.into_iter());
+        outputs.push(retval_regs.only_reg().unwrap());
     }
 
     abi.emit_call(ctx);
@@ -192,7 +194,7 @@ fn emit_vm_call(
         ctx.emit(inst);
     }
 
-    Ok(())
+    Ok(outputs)
 }
 
 /// Returns whether the given input is a shift by a constant value less or equal than 3.
diff --git a/cranelift/codegen/src/isa/x64/lower/isle.rs b/cranelift/codegen/src/isa/x64/lower/isle.rs
index a96a3261d..f5a4f8f61 100644
--- a/cranelift/codegen/src/isa/x64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/x64/lower/isle.rs
@@ -22,7 +22,7 @@ use crate::{
         inst::{args::*, regs, CallInfo, ReturnCallInfo},
     },
     machinst::{
-        isle::*, valueregs, ArgPair, InsnInput, InstOutput, MachAtomicRmwOp, MachInst,
+        isle::*, ArgPair, InsnInput, InstOutput, IsTailCall, MachAtomicRmwOp, MachInst,
         VCodeConstant, VCodeConstantData,
     },
 };
@@ -91,7 +91,7 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
             self.lower_ctx.sigs(),
             callee_sig,
             callee,
-            Opcode::ReturnCallIndirect,
+            IsTailCall::Yes,
             caller_conv,
             self.backend.flags().clone(),
         );
@@ -118,7 +118,7 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
             self.lower_ctx.sigs(),
             callee_sig,
             &callee,
-            Opcode::ReturnCall,
+            IsTailCall::Yes,
             distance,
             caller_conv,
             self.backend.flags().clone(),
@@ -689,57 +689,48 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
     }
 
     fn libcall_1(&mut self, libcall: &LibCall, a: Reg) -> Reg {
-        let call_conv = self.lower_ctx.abi().call_conv(self.lower_ctx.sigs());
-        let ret_ty = libcall.signature(call_conv, I64).returns[0].value_type;
-        let output_reg = self.lower_ctx.alloc_tmp(ret_ty).only_reg().unwrap();
-
-        emit_vm_call(
+        let outputs = emit_vm_call(
             self.lower_ctx,
             &self.backend.flags,
             &self.backend.triple,
             libcall.clone(),
             &[a],
-            &[output_reg],
         )
         .expect("Failed to emit LibCall");
 
-        output_reg.to_reg()
+        debug_assert_eq!(outputs.len(), 1);
+
+        outputs[0]
     }
 
     fn libcall_2(&mut self, libcall: &LibCall, a: Reg, b: Reg) -> Reg {
-        let call_conv = self.lower_ctx.abi().call_conv(self.lower_ctx.sigs());
-        let ret_ty = libcall.signature(call_conv, I64).returns[0].value_type;
-        let output_reg = self.lower_ctx.alloc_tmp(ret_ty).only_reg().unwrap();
-
-        emit_vm_call(
+        let outputs = emit_vm_call(
             self.lower_ctx,
             &self.backend.flags,
             &self.backend.triple,
             libcall.clone(),
             &[a, b],
-            &[output_reg],
         )
         .expect("Failed to emit LibCall");
 
-        output_reg.to_reg()
+        debug_assert_eq!(outputs.len(), 1);
+
+        outputs[0]
     }
 
     fn libcall_3(&mut self, libcall: &LibCall, a: Reg, b: Reg, c: Reg) -> Reg {
-        let call_conv = self.lower_ctx.abi().call_conv(self.lower_ctx.sigs());
-        let ret_ty = libcall.signature(call_conv, I64).returns[0].value_type;
-        let output_reg = self.lower_ctx.alloc_tmp(ret_ty).only_reg().unwrap();
-
-        emit_vm_call(
+        let outputs = emit_vm_call(
             self.lower_ctx,
             &self.backend.flags,
             &self.backend.triple,
             libcall.clone(),
             &[a, b, c],
-            &[output_reg],
         )
         .expect("Failed to emit LibCall");
 
-        output_reg.to_reg()
+        debug_assert_eq!(outputs.len(), 1);
+
+        outputs[0]
     }
 
     #[inline]
@@ -1005,8 +996,6 @@ impl Context for IsleContext<'_, '_, MInst, X64Backend> {
 }
 
 impl IsleContext<'_, '_, MInst, X64Backend> {
-    isle_prelude_method_helpers!(X64CallSite);
-
     fn load_xmm_unaligned(&mut self, addr: SyntheticAmode) -> Xmm {
         let tmp = self.lower_ctx.alloc_tmp(types::F32X4).only_reg().unwrap();
         self.lower_ctx.emit(MInst::XmmUnaryRmRUnaligned {
diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs
index fd9555b7c..b5bee22ec 100644
--- a/cranelift/codegen/src/machinst/abi.rs
+++ b/cranelift/codegen/src/machinst/abi.rs
@@ -546,7 +546,6 @@ pub trait ABIMachineSpec {
         uses: CallArgList,
         defs: CallRetList,
         clobbers: PRegSet,
-        opcode: ir::Opcode,
         tmp: Writable<Reg>,
         callee_conv: isa::CallConv,
         caller_conv: isa::CallConv,
@@ -890,39 +889,6 @@ impl SigSet {
         self.rets(sig)[idx].clone()
     }
 
-    /// Return all clobbers for the callsite.
-    pub fn call_clobbers<M: ABIMachineSpec>(&self, sig: Sig) -> PRegSet {
-        let sig_data = &self.sigs[sig];
-        // Get clobbers: all caller-saves. These may include return value
-        // regs, which we will remove from the clobber set below.
-        let mut clobbers = M::get_regs_clobbered_by_call(sig_data.call_conv);
-
-        // Remove retval regs from clobbers. Skip StructRets: these
-        // are not, semantically, returns at the CLIF level, so we
-        // treat such a value as a clobber instead.
-        for ret in self.rets(sig) {
-            if let &ABIArg::Slots {
-                ref slots, purpose, ..
-            } = ret
-            {
-                if purpose == ir::ArgumentPurpose::StructReturn {
-                    continue;
-                }
-                for slot in slots {
-                    match slot {
-                        &ABIArgSlot::Reg { reg, .. } => {
-                            crate::trace!("call_clobbers: retval reg {:?}", reg);
-                            clobbers.remove(PReg::from(reg));
-                        }
-                        _ => {}
-                    }
-                }
-            }
-        }
-
-        clobbers
-    }
-
     /// Get the number of arguments expected.
     pub fn num_args(&self, sig: Sig) -> usize {
         let len = self.args(sig).len();
@@ -1956,6 +1922,11 @@ pub struct CallRetPair {
 pub type CallArgList = SmallVec<[CallArgPair; 8]>;
 pub type CallRetList = SmallVec<[CallRetPair; 8]>;
 
+pub enum IsTailCall {
+    Yes,
+    No,
+}
+
 /// ABI object for a callsite.
 pub struct CallSite<M: ABIMachineSpec> {
     /// The called function's signature.
@@ -1965,12 +1936,9 @@ pub struct CallSite<M: ABIMachineSpec> {
     uses: CallArgList,
     /// All defs for the callsite, i.e., return values.
     defs: CallRetList,
-    /// Caller-save clobbers.
-    clobbers: PRegSet,
     /// Call destination.
     dest: CallDest,
-    /// Actual call opcode; used to distinguish various types of calls.
-    opcode: ir::Opcode,
+    is_tail_call: IsTailCall,
     /// Caller's calling convention.
     caller_conv: isa::CallConv,
     /// The settings controlling this compilation.
@@ -1994,20 +1962,18 @@ impl<M: ABIMachineSpec> CallSite<M> {
         sigs: &SigSet,
         sig_ref: ir::SigRef,
         extname: &ir::ExternalName,
-        opcode: ir::Opcode,
+        is_tail_call: IsTailCall,
         dist: RelocDistance,
         caller_conv: isa::CallConv,
         flags: settings::Flags,
     ) -> CallSite<M> {
         let sig = sigs.abi_sig_for_sig_ref(sig_ref);
-        let clobbers = sigs.call_clobbers::<M>(sig);
         CallSite {
             sig,
             uses: smallvec![],
             defs: smallvec![],
-            clobbers,
             dest: CallDest::ExtName(extname.clone(), dist),
-            opcode,
+            is_tail_call,
             caller_conv,
             flags,
             _mach: PhantomData,
@@ -2025,14 +1991,12 @@ impl<M: ABIMachineSpec> CallSite<M> {
         flags: settings::Flags,
     ) -> CallSite<M> {
         let sig = sigs.abi_sig_for_signature(sig);
-        let clobbers = sigs.call_clobbers::<M>(sig);
         CallSite {
             sig,
             uses: smallvec![],
             defs: smallvec![],
-            clobbers,
             dest: CallDest::ExtName(extname.clone(), dist),
-            opcode: ir::Opcode::Call,
+            is_tail_call: IsTailCall::No,
             caller_conv,
             flags,
             _mach: PhantomData,
@@ -2045,19 +2009,17 @@ impl<M: ABIMachineSpec> CallSite<M> {
         sigs: &SigSet,
         sig_ref: ir::SigRef,
         ptr: Reg,
-        opcode: ir::Opcode,
+        is_tail_call: IsTailCall,
         caller_conv: isa::CallConv,
         flags: settings::Flags,
     ) -> CallSite<M> {
         let sig = sigs.abi_sig_for_sig_ref(sig_ref);
-        let clobbers = sigs.call_clobbers::<M>(sig);
         CallSite {
             sig,
             uses: smallvec![],
             defs: smallvec![],
-            clobbers,
             dest: CallDest::Reg(ptr),
-            opcode,
+            is_tail_call,
             caller_conv,
             flags,
             _mach: PhantomData,
@@ -2068,10 +2030,6 @@ impl<M: ABIMachineSpec> CallSite<M> {
         &self.dest
     }
 
-    pub(crate) fn opcode(&self) -> ir::Opcode {
-        self.opcode
-    }
-
     pub(crate) fn take_uses(self) -> CallArgList {
         self.uses
     }
@@ -2081,10 +2039,7 @@ impl<M: ABIMachineSpec> CallSite<M> {
     }
 
     pub(crate) fn is_tail_call(&self) -> bool {
-        matches!(
-            self.opcode,
-            ir::Opcode::ReturnCall | ir::Opcode::ReturnCallIndirect
-        )
+        matches!(self.is_tail_call, IsTailCall::Yes)
     }
 }
 
@@ -2094,6 +2049,11 @@ impl<M: ABIMachineSpec> CallSite<M> {
         sigs.num_args(self.sig)
     }
 
+    /// Get the number of return values expected.
+    pub fn num_rets(&self, sigs: &SigSet) -> usize {
+        sigs.num_rets(self.sig)
+    }
+
     /// Emit a copy of a large argument into its associated stack buffer, if
     /// any.  We must be careful to perform all these copies (as necessary)
     /// before setting up the argument registers, since we may have to invoke
@@ -2297,25 +2257,28 @@ impl<M: ABIMachineSpec> CallSite<M> {
     /// Define a return value after the call returns.
     pub fn gen_retval(
         &mut self,
-        ctx: &Lower<M::I>,
+        ctx: &mut Lower<M::I>,
         idx: usize,
-        into_regs: ValueRegs<Writable<Reg>>,
-    ) -> SmallInstVec<M::I> {
+    ) -> (SmallInstVec<M::I>, ValueRegs<Reg>) {
         let mut insts = smallvec![];
-        match &ctx.sigs().rets(self.sig)[idx] {
-            &ABIArg::Slots { ref slots, .. } => {
-                assert_eq!(into_regs.len(), slots.len());
-                for (slot, into_reg) in slots.iter().zip(into_regs.regs().iter()) {
+        let mut into_regs: SmallVec<[Reg; 2]> = smallvec![];
+        let ret = ctx.sigs().rets(self.sig)[idx].clone();
+        match ret {
+            ABIArg::Slots { ref slots, .. } => {
+                for slot in slots {
                     match slot {
                         // Extension mode doesn't matter because we're copying out, not in,
                         // and we ignore high bits in our own registers by convention.
-                        &ABIArgSlot::Reg { reg, .. } => {
+                        &ABIArgSlot::Reg { reg, ty, .. } => {
+                            let into_reg = ctx.alloc_tmp(ty).only_reg().unwrap();
                             self.defs.push(CallRetPair {
-                                vreg: *into_reg,
+                                vreg: into_reg,
                                 preg: reg.into(),
                             });
+                            into_regs.push(into_reg.to_reg());
                         }
                         &ABIArgSlot::Stack { offset, ty, .. } => {
+                            let into_reg = ctx.alloc_tmp(ty).only_reg().unwrap();
                             let sig_data = &ctx.sigs()[self.sig];
                             // The outgoing argument area must always be restored after a call,
                             // ensuring that the return values will be in a consistent place after
@@ -2323,21 +2286,28 @@ impl<M: ABIMachineSpec> CallSite<M> {
                             let ret_area_base = sig_data.sized_stack_arg_space();
                             insts.push(M::gen_load_stack(
                                 StackAMode::OutgoingArg(offset + ret_area_base),
-                                *into_reg,
+                                into_reg,
                                 ty,
                             ));
+                            into_regs.push(into_reg.to_reg());
                         }
                     }
                 }
             }
-            &ABIArg::StructArg { .. } => {
+            ABIArg::StructArg { .. } => {
                 panic!("StructArg not supported in return position");
             }
-            &ABIArg::ImplicitPtrArg { .. } => {
+            ABIArg::ImplicitPtrArg { .. } => {
                 panic!("ImplicitPtrArg not supported in return position");
             }
         }
-        insts
+
+        let value_regs = match *into_regs {
+            [a] => ValueRegs::one(a),
+            [a, b] => ValueRegs::two(a, b),
+            _ => panic!("Expected to see one or two slots only from {:?}", ret),
+        };
+        (insts, value_regs)
     }
 
     /// Emit the call itself.
@@ -2365,10 +2335,20 @@ impl<M: ABIMachineSpec> CallSite<M> {
             self.gen_arg(ctx, i.into(), ValueRegs::one(rd.to_reg()));
         }
 
-        let (uses, defs) = (
-            mem::replace(&mut self.uses, Default::default()),
-            mem::replace(&mut self.defs, Default::default()),
-        );
+        let uses = mem::take(&mut self.uses);
+        let defs = mem::take(&mut self.defs);
+        let clobbers = {
+            // Get clobbers: all caller-saves. These may include return value
+            // regs, which we will remove from the clobber set below.
+            let mut clobbers = <M>::get_regs_clobbered_by_call(ctx.sigs()[self.sig].call_conv);
+
+            // Remove retval regs from clobbers.
+            for def in &defs {
+                clobbers.remove(PReg::from(def.preg.to_real_reg().unwrap()));
+            }
+
+            clobbers
+        };
 
         let sig = &ctx.sigs()[self.sig];
         let callee_pop_size = if sig.call_conv() == isa::CallConv::Tail {
@@ -2400,8 +2380,7 @@ impl<M: ABIMachineSpec> CallSite<M> {
             &self.dest,
             uses,
             defs,
-            self.clobbers,
-            self.opcode,
+            clobbers,
             tmp,
             call_conv,
             self.caller_conv,
diff --git a/cranelift/codegen/src/machinst/buffer.rs b/cranelift/codegen/src/machinst/buffer.rs
index c02b8596d..5b631334b 100644
--- a/cranelift/codegen/src/machinst/buffer.rs
+++ b/cranelift/codegen/src/machinst/buffer.rs
@@ -172,7 +172,7 @@
 
 use crate::binemit::{Addend, CodeOffset, Reloc, StackMap};
 use crate::ir::function::FunctionParameters;
-use crate::ir::{ExternalName, Opcode, RelSourceLoc, SourceLoc, TrapCode};
+use crate::ir::{ExternalName, RelSourceLoc, SourceLoc, TrapCode};
 use crate::isa::unwind::UnwindInst;
 use crate::machinst::{
     BlockIndex, MachInstLabelUse, TextSectionBuilder, VCodeConstant, VCodeConstants, VCodeInst,
@@ -1620,14 +1620,9 @@ impl<I: VCodeInst> MachBuffer<I> {
     }
 
     /// Add a call-site record at the current offset.
-    pub fn add_call_site(&mut self, opcode: Opcode) {
-        debug_assert!(
-            opcode.is_call(),
-            "adding call site info for a non-call instruction."
-        );
+    pub fn add_call_site(&mut self) {
         self.call_sites.push(MachCallSite {
             ret_addr: self.data.len() as CodeOffset,
-            opcode,
         });
     }
 
@@ -1939,8 +1934,6 @@ pub struct MachTrap {
 pub struct MachCallSite {
     /// The offset of the call's return address, *relative to the containing section*.
     pub ret_addr: CodeOffset,
-    /// The call's opcode.
-    pub opcode: Opcode,
 }
 
 /// A source-location mapping resulting from a compilation.
@@ -2498,7 +2491,7 @@ mod test {
         buf.put1(2);
         buf.add_trap(TrapCode::IntegerOverflow);
         buf.add_trap(TrapCode::IntegerDivisionByZero);
-        buf.add_call_site(Opcode::Call);
+        buf.add_call_site();
         buf.add_reloc(
             Reloc::Abs4,
             &ExternalName::User(UserExternalNameRef::new(0)),
@@ -2529,9 +2522,9 @@ mod test {
         assert_eq!(
             buf.call_sites()
                 .iter()
-                .map(|call_site| (call_site.ret_addr, call_site.opcode))
+                .map(|call_site| call_site.ret_addr)
                 .collect::<Vec<_>>(),
-            vec![(2, Opcode::Call)]
+            vec![2]
         );
         assert_eq!(
             buf.relocs()
diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs
index 137cf239a..54d88e249 100644
--- a/cranelift/codegen/src/machinst/isle.rs
+++ b/cranelift/codegen/src/machinst/isle.rs
@@ -12,8 +12,8 @@ pub use crate::ir::{
 };
 pub use crate::isa::{unwind::UnwindInst, TargetIsa};
 pub use crate::machinst::{
-    ABIArg, ABIArgSlot, InputSourceInst, Lower, LowerBackend, RealReg, Reg, RelocDistance, Sig,
-    VCodeInst, Writable,
+    ABIArg, ABIArgSlot, ABIMachineSpec, CallSite, InputSourceInst, Lower, LowerBackend, RealReg,
+    Reg, RelocDistance, Sig, VCodeInst, Writable,
 };
 pub use crate::settings::TlsModel;
 
@@ -758,12 +758,11 @@ macro_rules! isle_prelude_caller_methods {
             let caller_conv = self.lower_ctx.abi().call_conv(self.lower_ctx.sigs());
             let sig = &self.lower_ctx.dfg().signatures[sig_ref];
             let num_rets = sig.returns.len();
-            let abi = self.lower_ctx.sigs().abi_sig_for_sig_ref(sig_ref);
             let caller = <$abicaller>::from_func(
                 self.lower_ctx.sigs(),
                 sig_ref,
                 &extname,
-                Opcode::Call,
+                IsTailCall::No,
                 dist,
                 caller_conv,
                 self.backend.flags().clone(),
@@ -774,7 +773,7 @@ macro_rules! isle_prelude_caller_methods {
                 sig.params.len()
             );
 
-            self.gen_call_common(abi, num_rets, caller, args)
+            crate::machinst::isle::gen_call_common(&mut self.lower_ctx, num_rets, caller, args)
         }
 
         fn gen_call_indirect(
@@ -787,12 +786,11 @@ macro_rules! isle_prelude_caller_methods {
             let ptr = self.put_in_reg(val);
             let sig = &self.lower_ctx.dfg().signatures[sig_ref];
             let num_rets = sig.returns.len();
-            let abi = self.lower_ctx.sigs().abi_sig_for_sig_ref(sig_ref);
             let caller = <$abicaller>::from_ptr(
                 self.lower_ctx.sigs(),
                 sig_ref,
                 ptr,
-                Opcode::CallIndirect,
+                IsTailCall::No,
                 caller_conv,
                 self.backend.flags().clone(),
             );
@@ -802,98 +800,62 @@ macro_rules! isle_prelude_caller_methods {
                 sig.params.len()
             );
 
-            self.gen_call_common(abi, num_rets, caller, args)
+            crate::machinst::isle::gen_call_common(&mut self.lower_ctx, num_rets, caller, args)
         }
     };
 }
 
-/// Helpers for the above ISLE prelude implementations. Meant to go
-/// inside the `impl` for the context type, not the trait impl.
-#[macro_export]
-#[doc(hidden)]
-macro_rules! isle_prelude_method_helpers {
-    ($abicaller:ty) => {
-        fn gen_call_common_args(&mut self, call_site: &mut $abicaller, (inputs, off): ValueSlice) {
-            let num_args = call_site.num_args(self.lower_ctx.sigs());
-
-            assert_eq!(
-                inputs.len(&self.lower_ctx.dfg().value_lists) - off,
-                num_args
-            );
-            let mut arg_regs = vec![];
-            for i in 0..num_args {
-                let input = inputs
-                    .get(off + i, &self.lower_ctx.dfg().value_lists)
-                    .unwrap();
-                arg_regs.push(self.put_in_regs(input));
-            }
-            for (i, arg_regs) in arg_regs.iter().enumerate() {
-                call_site.emit_copy_regs_to_buffer(self.lower_ctx, i, *arg_regs);
-            }
-            for (i, arg_regs) in arg_regs.iter().enumerate() {
-                call_site.gen_arg(self.lower_ctx, i, *arg_regs);
-            }
-        }
-
-        fn gen_call_common(
-            &mut self,
-            abi: Sig,
-            num_rets: usize,
-            mut caller: $abicaller,
-            args: ValueSlice,
-        ) -> InstOutput {
-            self.gen_call_common_args(&mut caller, args);
-
-            // Handle retvals prior to emitting call, so the
-            // constraints are on the call instruction; but buffer the
-            // instructions till after the call.
-            let mut outputs = InstOutput::new();
-            let mut retval_insts = crate::machinst::abi::SmallInstVec::new();
-            // We take the *last* `num_rets` returns of the sig:
-            // this skips a StructReturn, if any, that is present.
-            let sigdata_num_rets = self.lower_ctx.sigs().num_rets(abi);
-            debug_assert!(num_rets <= sigdata_num_rets);
-            for i in (sigdata_num_rets - num_rets)..sigdata_num_rets {
-                // Borrow `sigdata` again so we don't hold a `self`
-                // borrow across the `&mut self` arg to
-                // `abi_arg_slot_regs()` below.
-                let ret = self.lower_ctx.sigs().get_ret(abi, i);
-                let retval_regs = self.abi_arg_slot_regs(&ret).unwrap();
-                retval_insts.extend(
-                    caller
-                        .gen_retval(self.lower_ctx, i, retval_regs.clone())
-                        .into_iter(),
-                );
-                outputs.push(valueregs::non_writable_value_regs(retval_regs));
-            }
+fn gen_call_common_args<M: ABIMachineSpec>(
+    ctx: &mut Lower<'_, M::I>,
+    call_site: &mut CallSite<M>,
+    (inputs, off): ValueSlice,
+) {
+    let num_args = call_site.num_args(ctx.sigs());
+
+    assert_eq!(inputs.len(&ctx.dfg().value_lists) - off, num_args);
+    let mut arg_regs = vec![];
+    for i in 0..num_args {
+        let input = inputs.get(off + i, &ctx.dfg().value_lists).unwrap();
+        arg_regs.push(ctx.put_value_in_regs(input));
+    }
+    for (i, arg_regs) in arg_regs.iter().enumerate() {
+        call_site.emit_copy_regs_to_buffer(ctx, i, *arg_regs);
+    }
+    for (i, arg_regs) in arg_regs.iter().enumerate() {
+        call_site.gen_arg(ctx, i, *arg_regs);
+    }
+}
 
-            caller.emit_call(self.lower_ctx);
+pub fn gen_call_common<M: ABIMachineSpec>(
+    ctx: &mut Lower<'_, M::I>,
+    num_rets: usize,
+    mut caller: CallSite<M>,
+    args: ValueSlice,
+) -> InstOutput {
+    gen_call_common_args(ctx, &mut caller, args);
+
+    // Handle retvals prior to emitting call, so the
+    // constraints are on the call instruction; but buffer the
+    // instructions till after the call.
+    let mut outputs = InstOutput::new();
+    let mut retval_insts = crate::machinst::abi::SmallInstVec::new();
+    // We take the *last* `num_rets` returns of the sig:
+    // this skips a StructReturn, if any, that is present.
+    let sigdata_num_rets = caller.num_rets(ctx.sigs());
+    debug_assert!(num_rets <= sigdata_num_rets);
+    for i in (sigdata_num_rets - num_rets)..sigdata_num_rets {
+        let (retval_inst, retval_regs) = caller.gen_retval(ctx, i);
+        retval_insts.extend(retval_inst.into_iter());
+        outputs.push(retval_regs);
+    }
 
-            for inst in retval_insts {
-                self.lower_ctx.emit(inst);
-            }
+    caller.emit_call(ctx);
 
-            outputs
-        }
+    for inst in retval_insts {
+        ctx.emit(inst);
+    }
 
-        fn abi_arg_slot_regs(&mut self, arg: &ABIArg) -> Option<WritableValueRegs> {
-            match arg {
-                &ABIArg::Slots { ref slots, .. } => match slots.len() {
-                    1 => {
-                        let a = self.temp_writable_reg(slots[0].get_type());
-                        Some(WritableValueRegs::one(a))
-                    }
-                    2 => {
-                        let a = self.temp_writable_reg(slots[0].get_type());
-                        let b = self.temp_writable_reg(slots[1].get_type());
-                        Some(WritableValueRegs::two(a, b))
-                    }
-                    _ => panic!("Expected to see one or two slots only from {:?}", arg),
-                },
-                _ => None,
-            }
-        }
-    };
+    outputs
 }
 
 /// This structure is used to implement the ISLE-generated `Context` trait and
diff --git a/cranelift/wasm/src/code_translator.rs b/cranelift/wasm/src/code_translator.rs
index b9308510b..044436aad 100644
--- a/cranelift/wasm/src/code_translator.rs
+++ b/cranelift/wasm/src/code_translator.rs
@@ -2551,7 +2551,34 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
         | Operator::GlobalAtomicRmwXor { .. }
         | Operator::GlobalAtomicRmwAnd { .. }
         | Operator::GlobalAtomicRmwXchg { .. }
-        | Operator::GlobalAtomicRmwCmpxchg { .. } => {
+        | Operator::GlobalAtomicRmwCmpxchg { .. }
+        | Operator::TableAtomicGet { .. }
+        | Operator::TableAtomicSet { .. }
+        | Operator::TableAtomicRmwXchg { .. }
+        | Operator::TableAtomicRmwCmpxchg { .. }
+        | Operator::StructAtomicGet { .. }
+        | Operator::StructAtomicGetS { .. }
+        | Operator::StructAtomicGetU { .. }
+        | Operator::StructAtomicSet { .. }
+        | Operator::StructAtomicRmwAdd { .. }
+        | Operator::StructAtomicRmwSub { .. }
+        | Operator::StructAtomicRmwOr { .. }
+        | Operator::StructAtomicRmwXor { .. }
+        | Operator::StructAtomicRmwAnd { .. }
+        | Operator::StructAtomicRmwXchg { .. }
+        | Operator::StructAtomicRmwCmpxchg { .. }
+        | Operator::ArrayAtomicGet { .. }
+        | Operator::ArrayAtomicGetS { .. }
+        | Operator::ArrayAtomicGetU { .. }
+        | Operator::ArrayAtomicSet { .. }
+        | Operator::ArrayAtomicRmwAdd { .. }
+        | Operator::ArrayAtomicRmwSub { .. }
+        | Operator::ArrayAtomicRmwOr { .. }
+        | Operator::ArrayAtomicRmwXor { .. }
+        | Operator::ArrayAtomicRmwAnd { .. }
+        | Operator::ArrayAtomicRmwXchg { .. }
+        | Operator::ArrayAtomicRmwCmpxchg { .. }
+        | Operator::RefI31Shared { .. } => {
             unimplemented!("shared-everything-threads not yet implemented")
         }
     };
diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs
index 33249451c..d60bd2d26 100644
--- a/crates/cli-flags/src/lib.rs
+++ b/crates/cli-flags/src/lib.rs
@@ -256,6 +256,8 @@ wasmtime_option_group! {
         pub component_model: Option<bool>,
         /// Configure support for 33+ flags in the component model.
         pub component_model_more_flags: Option<bool>,
+        /// Component model support for more than one return value.
+        pub component_model_multiple_returns: Option<bool>,
         /// Configure support for the function-references proposal.
         pub function_references: Option<bool>,
         /// Configure support for the GC proposal.
@@ -682,6 +684,7 @@ impl CommonOptions {
         handle_conditionally_compiled! {
             ("component-model", component_model, wasm_component_model)
             ("component-model", component_model_more_flags, wasm_component_model_more_flags)
+            ("component-model", component_model_multiple_returns, wasm_component_model_multiple_returns)
             ("threads", threads, wasm_threads)
             ("gc", gc, wasm_gc)
             ("gc", reference_types, wasm_reference_types)
diff --git a/crates/environ/src/compile/module_types.rs b/crates/environ/src/compile/module_types.rs
index da8751e2f..74632539f 100644
--- a/crates/environ/src/compile/module_types.rs
+++ b/crates/environ/src/compile/module_types.rs
@@ -392,10 +392,18 @@ impl TypeConvert for WasmparserTypeConverter<'_> {
                         WasmCompositeType::Struct(_) => WasmHeapType::ConcreteStruct(index),
                     }
                 } else if let Some((wasmparser_types, _)) = self.rec_group_context.as_ref() {
-                    match &wasmparser_types[id].composite_type {
-                        wasmparser::CompositeType::Array(_) => WasmHeapType::ConcreteArray(index),
-                        wasmparser::CompositeType::Func(_) => WasmHeapType::ConcreteFunc(index),
-                        wasmparser::CompositeType::Struct(_) => WasmHeapType::ConcreteStruct(index),
+                    let wasmparser_ty = &wasmparser_types[id].composite_type;
+                    assert!(!wasmparser_ty.shared);
+                    match &wasmparser_ty.inner {
+                        wasmparser::CompositeInnerType::Array(_) => {
+                            WasmHeapType::ConcreteArray(index)
+                        }
+                        wasmparser::CompositeInnerType::Func(_) => {
+                            WasmHeapType::ConcreteFunc(index)
+                        }
+                        wasmparser::CompositeInnerType::Struct(_) => {
+                            WasmHeapType::ConcreteStruct(index)
+                        }
                     }
                 } else {
                     panic!("forward reference to type outside of rec group?")
@@ -424,10 +432,18 @@ impl TypeConvert for WasmparserTypeConverter<'_> {
                         .rec_group_elements(*rec_group)
                         .nth(rec_group_index)
                         .unwrap();
-                    match &parser_types[id].composite_type {
-                        wasmparser::CompositeType::Array(_) => WasmHeapType::ConcreteArray(index),
-                        wasmparser::CompositeType::Func(_) => WasmHeapType::ConcreteFunc(index),
-                        wasmparser::CompositeType::Struct(_) => WasmHeapType::ConcreteStruct(index),
+                    let wasmparser_ty = &parser_types[id].composite_type;
+                    assert!(!wasmparser_ty.shared);
+                    match &wasmparser_ty.inner {
+                        wasmparser::CompositeInnerType::Array(_) => {
+                            WasmHeapType::ConcreteArray(index)
+                        }
+                        wasmparser::CompositeInnerType::Func(_) => {
+                            WasmHeapType::ConcreteFunc(index)
+                        }
+                        wasmparser::CompositeInnerType::Struct(_) => {
+                            WasmHeapType::ConcreteStruct(index)
+                        }
                     }
                 } else {
                     panic!("forward reference to type outside of rec group?")
diff --git a/crates/fuzzing/src/generators/table_ops.rs b/crates/fuzzing/src/generators/table_ops.rs
index 0139d1c8d..aa0bdb134 100644
--- a/crates/fuzzing/src/generators/table_ops.rs
+++ b/crates/fuzzing/src/generators/table_ops.rs
@@ -86,6 +86,7 @@ impl TableOps {
             minimum: self.table_size as u64,
             maximum: None,
             table64: false,
+            shared: false,
         });
 
         // Define our globals.
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 0c22a2e70..e4fc74c3f 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -1723,14 +1723,15 @@ pub trait TypeConvert {
     }
 
     fn convert_composite_type(&self, ty: &wasmparser::CompositeType) -> WasmCompositeType {
-        match ty {
-            wasmparser::CompositeType::Func(f) => {
+        assert!(!ty.shared);
+        match &ty.inner {
+            wasmparser::CompositeInnerType::Func(f) => {
                 WasmCompositeType::Func(self.convert_func_type(f))
             }
-            wasmparser::CompositeType::Array(a) => {
+            wasmparser::CompositeInnerType::Array(a) => {
                 WasmCompositeType::Array(self.convert_array_type(a))
             }
-            wasmparser::CompositeType::Struct(s) => {
+            wasmparser::CompositeInnerType::Struct(s) => {
                 WasmCompositeType::Struct(self.convert_struct_type(s))
             }
         }
diff --git a/crates/wasi-nn/Cargo.toml b/crates/wasi-nn/Cargo.toml
index a5ace788d..371e721dd 100644
--- a/crates/wasi-nn/Cargo.toml
+++ b/crates/wasi-nn/Cargo.toml
@@ -29,7 +29,7 @@ wasmtime = { workspace = true, features = [
 # These dependencies are necessary for the wasi-nn implementation:
 tracing = { workspace = true }
 thiserror = { workspace = true }
-openvino = { version = "0.6.0", features = [
+openvino = { version = "0.7.2", features = [
     "runtime-linking",
 ], optional = true }
 
diff --git a/crates/wasi-nn/src/backend/mod.rs b/crates/wasi-nn/src/backend/mod.rs
index 710c93b98..0409ddf52 100644
--- a/crates/wasi-nn/src/backend/mod.rs
+++ b/crates/wasi-nn/src/backend/mod.rs
@@ -107,6 +107,8 @@ pub enum BackendError {
     InvalidNumberOfBuilders(usize, usize),
     #[error("Not enough memory to copy tensor data of size: {0}")]
     NotEnoughMemory(usize),
+    #[error("Unsupported tensor type: {0}")]
+    UnsupportedTensorType(String),
 }
 
 /// Read a file into a byte vector.
diff --git a/crates/wasi-nn/src/backend/openvino.rs b/crates/wasi-nn/src/backend/openvino.rs
index b24f93838..8be15bd25 100644
--- a/crates/wasi-nn/src/backend/openvino.rs
+++ b/crates/wasi-nn/src/backend/openvino.rs
@@ -3,9 +3,9 @@
 use super::{
     read, BackendError, BackendExecutionContext, BackendFromDir, BackendGraph, BackendInner, Id,
 };
-use crate::wit::{self, ExecutionTarget, GraphEncoding, Tensor, TensorType};
+use crate::wit::{ExecutionTarget, GraphEncoding, Tensor, TensorType};
 use crate::{ExecutionContext, Graph};
-use openvino::{InferenceError, Layout, Precision, SetupError, TensorDesc};
+use openvino::{DeviceType, ElementType, InferenceError, SetupError, Shape, Tensor as OvTensor};
 use std::path::Path;
 use std::sync::{Arc, Mutex};
 
@@ -23,41 +23,33 @@ impl BackendInner for OpenvinoBackend {
         if builders.len() != 2 {
             return Err(BackendError::InvalidNumberOfBuilders(2, builders.len()).into());
         }
-
         // Construct the context if none is present; this is done lazily (i.e.
         // upon actually loading a model) because it may fail to find and load
         // the OpenVINO libraries. The laziness limits the extent of the error
         // only to wasi-nn users, not all WASI users.
         if self.0.is_none() {
-            self.0.replace(openvino::Core::new(None)?);
+            self.0.replace(openvino::Core::new()?);
         }
-
         // Read the guest array.
-        let xml = &builders[0];
-        let weights = &builders[1];
+        let xml = builders[0];
+        let weights = builders[1];
 
-        // Construct OpenVINO graph structures: `cnn_network` contains the graph
-        // structure, `exec_network` can perform inference.
+        // Construct a new tensor for the model weights.
+        let shape = Shape::new(&[1, weights.len() as i64])?;
+        let mut weights_tensor = OvTensor::new(ElementType::U8, &shape)?;
+        let buffer = weights_tensor.get_raw_data_mut()?;
+        buffer.copy_from_slice(&weights);
+
+        // Construct OpenVINO graph structures: `model` contains the graph
+        // structure, `compiled_model` can perform inference.
         let core = self
             .0
             .as_mut()
             .expect("openvino::Core was previously constructed");
-        let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?;
-
-        // TODO: this is a temporary workaround. We need a more elegant way to
-        // specify the layout in the long run. However, without this newer
-        // versions of OpenVINO will fail due to parameter mismatch.
-        for i in 0..cnn_network.get_inputs_len()? {
-            let name = cnn_network.get_input_name(i)?;
-            cnn_network.set_input_layout(&name, Layout::NHWC)?;
-        }
-
-        let exec_network =
-            core.load_network(&cnn_network, map_execution_target_to_string(target))?;
-        let box_: Box<dyn BackendGraph> = Box::new(OpenvinoGraph(
-            Arc::new(cnn_network),
-            Arc::new(Mutex::new(exec_network)),
-        ));
+        let model = core.read_model_from_buffer(&xml, Some(&weights_tensor))?;
+        let compiled_model = core.compile_model(&model, target.into())?;
+        let box_: Box<dyn BackendGraph> =
+            Box::new(OpenvinoGraph(Arc::new(Mutex::new(compiled_model))));
         Ok(box_.into())
     }
 
@@ -78,63 +70,62 @@ impl BackendFromDir for OpenvinoBackend {
     }
 }
 
-struct OpenvinoGraph(
-    Arc<openvino::CNNNetwork>,
-    Arc<Mutex<openvino::ExecutableNetwork>>,
-);
+struct OpenvinoGraph(Arc<Mutex<openvino::CompiledModel>>);
 
 unsafe impl Send for OpenvinoGraph {}
 unsafe impl Sync for OpenvinoGraph {}
 
 impl BackendGraph for OpenvinoGraph {
     fn init_execution_context(&self) -> Result<ExecutionContext, BackendError> {
-        let mut network = self.1.lock().unwrap();
-        let infer_request = network.create_infer_request()?;
+        let mut compiled_model = self.0.lock().unwrap();
+        let infer_request = compiled_model.create_infer_request()?;
         let box_: Box<dyn BackendExecutionContext> =
-            Box::new(OpenvinoExecutionContext(self.0.clone(), infer_request));
+            Box::new(OpenvinoExecutionContext(infer_request));
         Ok(box_.into())
     }
 }
 
-struct OpenvinoExecutionContext(Arc<openvino::CNNNetwork>, openvino::InferRequest);
+struct OpenvinoExecutionContext(openvino::InferRequest);
 
 impl BackendExecutionContext for OpenvinoExecutionContext {
     fn set_input(&mut self, id: Id, tensor: &Tensor) -> Result<(), BackendError> {
-        let input_name = match id {
-            Id::Index(i) => self.0.get_input_name(i as usize)?,
-            Id::Name(name) => name,
-        };
-
-        // Construct the blob structure. TODO: there must be some good way to
-        // discover the layout here; `desc` should not have to default to NHWC.
-        let precision = map_tensor_type_to_precision(tensor.ty);
+        // Construct the tensor.
+        let precision = tensor.ty.into();
         let dimensions = tensor
             .dimensions
             .iter()
-            .map(|&d| d as usize)
+            .map(|&d| d as i64)
             .collect::<Vec<_>>();
-        let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision);
-        let blob = openvino::Blob::new(&desc, &tensor.data)?;
-
-        // Actually assign the blob to the request.
-        self.1.set_blob(&input_name, &blob)?;
+        let shape = Shape::new(&dimensions)?;
+        let mut new_tensor = OvTensor::new(precision, &shape)?;
+        let buffer = new_tensor.get_raw_data_mut()?;
+        buffer.copy_from_slice(&tensor.data);
+        // Assign the tensor to the request.
+        match id {
+            Id::Index(i) => self.0.set_input_tensor_by_index(i as usize, &new_tensor)?,
+            Id::Name(name) => self.0.set_tensor(&name, &new_tensor)?,
+        };
         Ok(())
     }
 
     fn compute(&mut self) -> Result<(), BackendError> {
-        self.1.infer()?;
+        self.0.infer()?;
         Ok(())
     }
 
     fn get_output(&mut self, id: Id) -> Result<Tensor, BackendError> {
         let output_name = match id {
-            Id::Index(i) => self.0.get_output_name(i as usize)?,
-            Id::Name(name) => name,
+            Id::Index(i) => self.0.get_output_tensor_by_index(i as usize)?,
+            Id::Name(name) => self.0.get_tensor(&name)?,
         };
-        let dimensions = vec![]; // TODO: get actual shape
-        let ty = wit::TensorType::Fp32; // TODO: get actual type.
-        let blob = self.1.get_blob(&output_name)?;
-        let data = blob.buffer()?.to_vec();
+        let dimensions = output_name
+            .get_shape()?
+            .get_dimensions()
+            .iter()
+            .map(|&dim| dim as u32)
+            .collect::<Vec<u32>>();
+        let ty = output_name.get_element_type()?.try_into()?;
+        let data = output_name.get_raw_data()?.to_vec();
         Ok(Tensor {
             dimensions,
             ty,
@@ -157,24 +148,49 @@ impl From<SetupError> for BackendError {
 
 /// Return the execution target string expected by OpenVINO from the
 /// `ExecutionTarget` enum provided by wasi-nn.
-fn map_execution_target_to_string(target: ExecutionTarget) -> &'static str {
-    match target {
-        ExecutionTarget::Cpu => "CPU",
-        ExecutionTarget::Gpu => "GPU",
-        ExecutionTarget::Tpu => unimplemented!("OpenVINO does not support TPU execution targets"),
+impl From<ExecutionTarget> for DeviceType<'static> {
+    fn from(target: ExecutionTarget) -> Self {
+        match target {
+            ExecutionTarget::Cpu => DeviceType::CPU,
+            ExecutionTarget::Gpu => DeviceType::GPU,
+            ExecutionTarget::Tpu => {
+                unimplemented!("OpenVINO does not support TPU execution targets")
+            }
+        }
     }
 }
 
 /// Return OpenVINO's precision type for the `TensorType` enum provided by
 /// wasi-nn.
-fn map_tensor_type_to_precision(tensor_type: TensorType) -> openvino::Precision {
-    match tensor_type {
-        TensorType::Fp16 => Precision::FP16,
-        TensorType::Fp32 => Precision::FP32,
-        TensorType::Fp64 => Precision::FP64,
-        TensorType::U8 => Precision::U8,
-        TensorType::I32 => Precision::I32,
-        TensorType::I64 => Precision::I64,
-        TensorType::Bf16 => todo!("not yet supported in `openvino` bindings"),
+impl From<TensorType> for ElementType {
+    fn from(tensor_type: TensorType) -> Self {
+        match tensor_type {
+            TensorType::Fp16 => ElementType::F16,
+            TensorType::Fp32 => ElementType::F32,
+            TensorType::Fp64 => ElementType::F64,
+            TensorType::U8 => ElementType::U8,
+            TensorType::I32 => ElementType::I32,
+            TensorType::I64 => ElementType::I64,
+            TensorType::Bf16 => ElementType::Bf16,
+        }
+    }
+}
+
+/// Return the `TensorType` enum provided by wasi-nn for OpenVINO's precision type
+impl TryFrom<ElementType> for TensorType {
+    type Error = BackendError;
+    fn try_from(element_type: ElementType) -> Result<Self, Self::Error> {
+        match element_type {
+            ElementType::F16 => Ok(TensorType::Fp16),
+            ElementType::F32 => Ok(TensorType::Fp32),
+            ElementType::F64 => Ok(TensorType::Fp64),
+            ElementType::U8 => Ok(TensorType::U8),
+            ElementType::I32 => Ok(TensorType::I32),
+            ElementType::I64 => Ok(TensorType::I64),
+            ElementType::Bf16 => Ok(TensorType::Bf16),
+            _ => Err(BackendError::UnsupportedTensorType(
+                element_type.to_string(),
+            )),
+        }
     }
 }
diff --git a/crates/wasi-runtime-config/Cargo.toml b/crates/wasi-runtime-config/Cargo.toml
new file mode 100644
index 000000000..c5939b1e1
--- /dev/null
+++ b/crates/wasi-runtime-config/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "wasmtime-wasi-runtime-config"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+repository = "https://github.com/bytecodealliance/wasmtime"
+license = "Apache-2.0 WITH LLVM-exception"
+description = "Wasmtime implementation of the wasi-runtime-config API"
+
+[lints]
+workspace = true
+
+[dependencies]
+anyhow = { workspace = true }
+wasmtime = { workspace = true, features = ["runtime", "component-model"] }
+
+[dev-dependencies]
+test-programs-artifacts = { workspace = true }
+wasmtime-wasi = { workspace = true }
+tokio = { workspace = true, features = ["macros"] }
diff --git a/crates/wasi-runtime-config/src/lib.rs b/crates/wasi-runtime-config/src/lib.rs
new file mode 100644
index 000000000..bae199fb5
--- /dev/null
+++ b/crates/wasi-runtime-config/src/lib.rs
@@ -0,0 +1,140 @@
+//! # Wasmtime's [wasi-runtime-config] Implementation
+//!
+//! This crate provides a Wasmtime host implementation of the [wasi-runtime-config]
+//! API. With this crate, the runtime can run components that call APIs in
+//! [wasi-runtime-config] and provide configuration variables for the component.
+//!
+//! # Examples
+//!
+//! The usage of this crate is very similar to other WASI API implementations
+//! such as [wasi:cli] and [wasi:http].
+//!
+//! A common scenario is getting runtime-passed configurations in a [wasi:cli]
+//! component. A standalone example of doing all this looks like:
+//!
+//! ```
+//! use wasmtime::{
+//!     component::{Linker, ResourceTable},
+//!     Config, Engine, Result, Store,
+//! };
+//! use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiView};
+//! use wasmtime_wasi_runtime_config::{WasiRuntimeConfig, WasiRuntimeConfigVariables};
+//!
+//! #[tokio::main]
+//! async fn main() -> Result<()> {
+//!     let mut config = Config::new();
+//!     config.async_support(true);
+//!     let engine = Engine::new(&config)?;
+//!
+//!     let mut store = Store::new(&engine, Ctx {
+//!         table: ResourceTable::new(),
+//!         wasi_ctx: WasiCtxBuilder::new().build(),
+//!         wasi_runtime_config_vars: WasiRuntimeConfigVariables::from_iter(vec![
+//!             ("config_key1", "value1"),
+//!             ("config_key2", "value2"),
+//!         ]),
+//!     });
+//!
+//!     let mut linker = Linker::<Ctx>::new(&engine);
+//!     wasmtime_wasi::add_to_linker_async(&mut linker)?;
+//!     // add `wasi-runtime-config` world's interfaces to the linker
+//!     wasmtime_wasi_runtime_config::add_to_linker(&mut linker, |h: &mut Ctx| {
+//!         WasiRuntimeConfig::from(&h.wasi_runtime_config_vars)
+//!     })?;
+//!
+//!     // ... use `linker` to instantiate within `store` ...
+//!
+//!     Ok(())
+//! }
+//!
+//! struct Ctx {
+//!     table: ResourceTable,
+//!     wasi_ctx: WasiCtx,
+//!     wasi_runtime_config_vars: WasiRuntimeConfigVariables,
+//! }
+//!
+//! impl WasiView for Ctx {
+//!     fn table(&mut self) -> &mut ResourceTable { &mut self.table }
+//!     fn ctx(&mut self) -> &mut WasiCtx { &mut self.wasi_ctx }
+//! }
+//! ```
+//!
+//! [wasi-runtime-config]: https://github.com/WebAssembly/wasi-runtime-config
+//! [wasi:cli]: https://docs.rs/wasmtime-wasi/latest
+//! [wasi:http]: https://docs.rs/wasmtime-wasi-http/latest
+
+#![deny(missing_docs)]
+
+use anyhow::Result;
+use std::collections::HashMap;
+
+mod gen_ {
+    wasmtime::component::bindgen!({
+        path: "wit",
+        world: "wasi:config/imports",
+        trappable_imports: true,
+    });
+}
+use self::gen_::wasi::config::runtime as generated;
+
+/// Capture the state necessary for use in the `wasi-runtime-config` API implementation.
+#[derive(Default)]
+pub struct WasiRuntimeConfigVariables(HashMap<String, String>);
+
+impl<S: Into<String>> FromIterator<(S, S)> for WasiRuntimeConfigVariables {
+    fn from_iter<I: IntoIterator<Item = (S, S)>>(iter: I) -> Self {
+        Self(
+            iter.into_iter()
+                .map(|(k, v)| (k.into(), v.into()))
+                .collect(),
+        )
+    }
+}
+
+impl WasiRuntimeConfigVariables {
+    /// Create a new runtime configuration.
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    /// Insert a key-value pair into the configuration map.
+    pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
+        self.0.insert(key.into(), value.into());
+        self
+    }
+}
+
+/// A wrapper capturing the needed internal `wasi-runtime-config` state.
+pub struct WasiRuntimeConfig<'a> {
+    vars: &'a WasiRuntimeConfigVariables,
+}
+
+impl<'a> From<&'a WasiRuntimeConfigVariables> for WasiRuntimeConfig<'a> {
+    fn from(vars: &'a WasiRuntimeConfigVariables) -> Self {
+        Self { vars }
+    }
+}
+
+impl generated::Host for WasiRuntimeConfig<'_> {
+    fn get(&mut self, key: String) -> Result<Result<Option<String>, generated::ConfigError>> {
+        Ok(Ok(self.vars.0.get(&key).map(|s| s.to_owned())))
+    }
+
+    fn get_all(&mut self) -> Result<Result<Vec<(String, String)>, generated::ConfigError>> {
+        Ok(Ok(self
+            .vars
+            .0
+            .iter()
+            .map(|(k, v)| (k.to_string(), v.to_string()))
+            .collect()))
+    }
+}
+
+/// Add all the `wasi-runtime-config` world's interfaces to a [`wasmtime::component::Linker`].
+pub fn add_to_linker<T>(
+    l: &mut wasmtime::component::Linker<T>,
+    f: impl Fn(&mut T) -> WasiRuntimeConfig<'_> + Send + Sync + Copy + 'static,
+) -> Result<()> {
+    generated::add_to_linker_get_host(l, f)?;
+    Ok(())
+}
diff --git a/crates/wasi-runtime-config/wit/deps/runtime-config/runtime_config.wit b/crates/wasi-runtime-config/wit/deps/runtime-config/runtime_config.wit
new file mode 100644
index 000000000..29d53b1ac
--- /dev/null
+++ b/crates/wasi-runtime-config/wit/deps/runtime-config/runtime_config.wit
@@ -0,0 +1,25 @@
+interface runtime {
+    /// An error type that encapsulates the different errors that can occur fetching config
+    variant config-error {
+        /// This indicates an error from an "upstream" config source. 
+        /// As this could be almost _anything_ (such as Vault, Kubernetes ConfigMaps, KeyValue buckets, etc), 
+        /// the error message is a string.
+        upstream(string),
+        /// This indicates an error from an I/O operation. 
+        /// As this could be almost _anything_ (such as a file read, network connection, etc), 
+        /// the error message is a string. 
+        /// Depending on how this ends up being consumed, 
+        /// we may consider moving this to use the `wasi:io/error` type instead. 
+        /// For simplicity right now in supporting multiple implementations, it is being left as a string.
+        io(string),
+    }
+
+    /// Gets a single opaque config value set at the given key if it exists
+    get: func(
+        /// A string key to fetch
+        key: string
+    ) -> result<option<string>, config-error>;
+
+    /// Gets a list of all set config data
+    get-all: func() -> result<list<tuple<string, string>>, config-error>;
+}
diff --git a/crates/wasi-runtime-config/wit/deps/runtime-config/world.wit b/crates/wasi-runtime-config/wit/deps/runtime-config/world.wit
new file mode 100644
index 000000000..378c4a779
--- /dev/null
+++ b/crates/wasi-runtime-config/wit/deps/runtime-config/world.wit
@@ -0,0 +1,6 @@
+package wasi:config@0.2.0-draft;
+
+world imports {
+    /// The runtime interface for config
+    import runtime;
+}
\ No newline at end of file
diff --git a/crates/wasi-runtime-config/wit/world.wit b/crates/wasi-runtime-config/wit/world.wit
new file mode 100644
index 000000000..5dce2388b
--- /dev/null
+++ b/crates/wasi-runtime-config/wit/world.wit
@@ -0,0 +1,6 @@
+// We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps.
+package wasmtime:wasi;
+
+world bindings {
+  include wasi:config/imports@0.2.0-draft;
+}
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index ecb14717f..9fdfded73 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -996,6 +996,17 @@ impl Config {
         self
     }
 
+    /// Configures whether components support more than one return value for functions.
+    ///
+    /// This is part of the transition plan in
+    /// https://github.com/WebAssembly/component-model/pull/368.
+    #[cfg(feature = "component-model")]
+    pub fn wasm_component_model_multiple_returns(&mut self, enable: bool) -> &mut Self {
+        self.features
+            .set(WasmFeatures::COMPONENT_MODEL_MULTIPLE_RETURNS, enable);
+        self
+    }
+
     /// Configures which compilation strategy will be used for wasm modules.
     ///
     /// This method can be used to configure which compiler is used for wasm
diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs
index c9e86cdb0..9ec83354c 100644
--- a/crates/wasmtime/src/engine/serialization.rs
+++ b/crates/wasmtime/src/engine/serialization.rs
@@ -203,6 +203,7 @@ struct WasmFeatures {
     gc: bool,
     custom_page_sizes: bool,
     component_model_more_flags: bool,
+    component_model_multiple_returns: bool,
 }
 
 impl Metadata<'_> {
@@ -229,6 +230,8 @@ impl Metadata<'_> {
             component_model_values,
             component_model_nested_names,
             component_model_more_flags,
+            component_model_multiple_returns,
+            legacy_exceptions,
 
             // Always on; we don't currently have knobs for these.
             mutable_global: _,
@@ -244,6 +247,7 @@ impl Metadata<'_> {
         assert!(!component_model_values);
         assert!(!component_model_nested_names);
         assert!(!shared_everything_threads);
+        assert!(!legacy_exceptions);
 
         Metadata {
             target: engine.compiler().triple().to_string(),
@@ -267,6 +271,7 @@ impl Metadata<'_> {
                 gc,
                 custom_page_sizes,
                 component_model_more_flags,
+                component_model_multiple_returns,
             },
         }
     }
@@ -473,6 +478,7 @@ impl Metadata<'_> {
             gc,
             custom_page_sizes,
             component_model_more_flags,
+            component_model_multiple_returns,
         } = self.features;
 
         use wasmparser::WasmFeatures as F;
@@ -559,6 +565,11 @@ impl Metadata<'_> {
             other.contains(F::COMPONENT_MODEL_MORE_FLAGS),
             "WebAssembly component model support for more than 32 flags",
         )?;
+        Self::check_bool(
+            component_model_multiple_returns,
+            other.contains(F::COMPONENT_MODEL_MULTIPLE_RETURNS),
+            "WebAssembly component model support for multiple returns",
+        )?;
 
         Ok(())
     }
diff --git a/scripts/publish.rs b/scripts/publish.rs
index b3d597c9b..245a912ee 100644
--- a/scripts/publish.rs
+++ b/scripts/publish.rs
@@ -67,6 +67,7 @@ const CRATES_TO_PUBLISH: &[&str] = &[
     "wasmtime-wasi",
     "wasmtime-wasi-http",
     "wasmtime-wasi-nn",
+    "wasmtime-wasi-runtime-config",
     "wasmtime-wasi-threads",
     "wasmtime-wast",
     "wasmtime-c-api-macros",
@@ -86,6 +87,7 @@ const PUBLIC_CRATES: &[&str] = &[
     "wasmtime",
     "wasmtime-wasi",
     "wasmtime-wasi-nn",
+    "wasmtime-wasi-runtime-config",
     "wasmtime-wasi-threads",
     "wasmtime-cli",
     // all cranelift crates are considered "public" in that they can't
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 7cc1fa51a..65d4f780c 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -2109,6 +2109,11 @@ who = "Iceber Gu <caiwei95@hotmail.com>"
 criteria = "safe-to-deploy"
 delta = "0.5.0 -> 0.6.0"
 
+[[audits.openvino]]
+who = "Andrew Brown <andrew.brown@intel.com>"
+criteria = "safe-to-deploy"
+delta = "0.6.0 -> 0.7.2"
+
 [[audits.openvino-finder]]
 who = "Matthew Tamayo-Rios <matthew@geekbeast.com>"
 criteria = "safe-to-deploy"
@@ -2127,6 +2132,11 @@ who = "Iceber Gu <caiwei95@hotmail.com>"
 criteria = "safe-to-deploy"
 delta = "0.5.0 -> 0.6.0"
 
+[[audits.openvino-finder]]
+who = "Andrew Brown <andrew.brown@intel.com>"
+criteria = "safe-to-deploy"
+delta = "0.6.0 -> 0.7.2"
+
 [[audits.openvino-sys]]
 who = "Matthew Tamayo-Rios <matthew@geekbeast.com>"
 criteria = "safe-to-deploy"
@@ -2145,6 +2155,11 @@ who = "Iceber Gu <caiwei95@hotmail.com>"
 criteria = "safe-to-deploy"
 delta = "0.5.0 -> 0.6.0"
 
+[[audits.openvino-sys]]
+who = "Andrew Brown <andrew.brown@intel.com>"
+criteria = "safe-to-deploy"
+delta = "0.6.0 -> 0.7.2"
+
 [[audits.ort]]
 who = "Andrew Brown <andrew.brown@intel.com>"
 criteria = "safe-to-deploy"
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index b304b8781..0f63eabac 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -2145,6 +2145,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-encoder]]
+version = "0.214.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-metadata]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -2211,6 +2217,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-metadata]]
+version = "0.214.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-mutate]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -2349,6 +2361,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmparser]]
+version = "0.214.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmprinter]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -2415,6 +2433,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmprinter]]
+version = "0.214.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -3171,6 +3195,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wast]]
+version = "214.0.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wat]]
 version = "1.201.0"
 when = "2024-02-27"
@@ -3237,6 +3267,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wat]]
+version = "1.214.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -3555,6 +3591,12 @@ when = "2024-06-28"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen]]
+version = "0.28.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-core]]
 version = "0.20.0"
 when = "2024-02-29"
@@ -3585,6 +3627,12 @@ when = "2024-06-28"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-core]]
+version = "0.28.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rt]]
 version = "0.22.0"
 when = "2024-03-11"
@@ -3609,6 +3657,12 @@ when = "2024-06-28"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rt]]
+version = "0.28.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust]]
 version = "0.20.0"
 when = "2024-02-29"
@@ -3639,6 +3693,12 @@ when = "2024-06-28"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust]]
+version = "0.28.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-bindgen-rust-macro]]
 version = "0.20.0"
 when = "2024-02-29"
@@ -3669,6 +3729,12 @@ when = "2024-06-28"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-bindgen-rust-macro]]
+version = "0.28.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-component]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -3735,6 +3801,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-component]]
+version = "0.214.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-parser]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -3801,6 +3873,12 @@ when = "2024-06-27"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-parser]]
+version = "0.214.0"
+when = "2024-07-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.witx]]
 version = "0.9.1"
 when = "2021-06-22"
diff --git a/winch/codegen/src/codegen/context.rs b/winch/codegen/src/codegen/context.rs
index dbac5b613..f9a3ead31 100644
--- a/winch/codegen/src/codegen/context.rs
+++ b/winch/codegen/src/codegen/context.rs
@@ -5,7 +5,7 @@ use crate::{
     abi::{vmctx, ABIOperand, ABIResults, RetArea, ABI},
     frame::Frame,
     isa::reg::RegClass,
-    masm::{MacroAssembler, OperandSize, RegImm, SPOffset, StackSlot},
+    masm::{MacroAssembler, OperandSize, RegImm, SPOffset, ShiftKind, StackSlot},
     reg::Reg,
     regalloc::RegAlloc,
     stack::{Stack, TypedReg, Val},
@@ -319,6 +319,57 @@ impl<'a> CodeGenContext<'a> {
         };
     }
 
+    /// Prepares arguments for emitting an i32 shift operation.
+    pub fn i32_shift<M>(&mut self, masm: &mut M, kind: ShiftKind)
+    where
+        M: MacroAssembler,
+    {
+        let top = self.stack.peek().expect("value at stack top");
+
+        if top.is_i32_const() {
+            let val = self
+                .stack
+                .pop_i32_const()
+                .expect("i32 const value at stack top");
+            let typed_reg = self.pop_to_reg(masm, None);
+            masm.shift_ir(
+                typed_reg.reg,
+                val as u64,
+                typed_reg.reg,
+                kind,
+                OperandSize::S32,
+            );
+            self.stack.push(typed_reg.into());
+        } else {
+            masm.shift(self, kind, OperandSize::S32);
+        }
+    }
+
+    /// Prepares arguments for emitting an i64 binary operation.
+    pub fn i64_shift<M>(&mut self, masm: &mut M, kind: ShiftKind)
+    where
+        M: MacroAssembler,
+    {
+        let top = self.stack.peek().expect("value at stack top");
+        if top.is_i64_const() {
+            let val = self
+                .stack
+                .pop_i64_const()
+                .expect("i64 const value at stack top");
+            let typed_reg = self.pop_to_reg(masm, None);
+            masm.shift_ir(
+                typed_reg.reg,
+                val as u64,
+                typed_reg.reg,
+                kind,
+                OperandSize::S64,
+            );
+            self.stack.push(typed_reg.into());
+        } else {
+            masm.shift(self, kind, OperandSize::S64);
+        };
+    }
+
     /// Prepares arguments for emitting a convert operation.
     pub fn convert_op<F, M>(&mut self, masm: &mut M, dst_ty: WasmValType, mut emit: F)
     where
diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs
index 4752bdddc..8ab22e69a 100644
--- a/winch/codegen/src/codegen/mod.rs
+++ b/winch/codegen/src/codegen/mod.rs
@@ -5,7 +5,7 @@ use crate::{
     masm::{
         ExtendKind, IntCmpKind, MacroAssembler, OperandSize, RegImm, SPOffset, ShiftKind, TrapCode,
     },
-    stack::{TypedReg, Val},
+    stack::TypedReg,
 };
 use anyhow::Result;
 use smallvec::SmallVec;
@@ -848,24 +848,17 @@ where
             .masm
             .address_at_reg(base, heap_data.current_length_offset);
         self.masm.load_ptr(size_addr, size_reg);
-        // Prepare the stack to emit a shift to get the size in pages rather
-        // than in bytes.
-        self.context
-            .stack
-            .push(TypedReg::new(heap_data.ty, size_reg).into());
-
+        // Emit a shift to get the size in pages rather than in bytes.
+        let dst = TypedReg::new(heap_data.ty, size_reg);
         let pow = heap_data.page_size_log2;
-
-        // Ensure that the constant is correctly typed according to the heap
-        // type to reduce register pressure when emitting the shift operation.
-        match heap_data.ty {
-            WasmValType::I32 => self.context.stack.push(Val::i32(i32::from(pow))),
-            WasmValType::I64 => self.context.stack.push(Val::i64(i64::from(pow))),
-            _ => unreachable!(),
-        }
-
-        self.masm
-            .shift(&mut self.context, ShiftKind::ShrU, heap_data.ty.into());
+        self.masm.shift_ir(
+            dst.reg,
+            pow as u64,
+            dst.into(),
+            ShiftKind::ShrU,
+            heap_data.ty.into(),
+        );
+        self.context.stack.push(dst.into());
     }
 }
 
diff --git a/winch/codegen/src/isa/aarch64/asm.rs b/winch/codegen/src/isa/aarch64/asm.rs
index a721b22bb..547c67665 100644
--- a/winch/codegen/src/isa/aarch64/asm.rs
+++ b/winch/codegen/src/isa/aarch64/asm.rs
@@ -1,12 +1,12 @@
 //! Assembler library implementation for Aarch64.
 
 use super::{address::Address, regs};
-use crate::masm::RoundingMode;
+use crate::masm::{RoundingMode, ShiftKind};
 use crate::{masm::OperandSize, reg::Reg};
 use cranelift_codegen::isa::aarch64::inst::FPUOpRI::{UShr32, UShr64};
 use cranelift_codegen::isa::aarch64::inst::{
-    FPULeftShiftImm, FPUOp1, FPUOp2, FPUOpRI, FPUOpRIMod, FPURightShiftImm, FpuRoundMode,
-    ScalarSize,
+    FPULeftShiftImm, FPUOp1, FPUOp2, FPUOpRI, FPUOpRIMod, FPURightShiftImm, FpuRoundMode, ImmLogic,
+    ImmShift, ScalarSize,
 };
 use cranelift_codegen::{
     ir::{MemFlags, SourceLoc},
@@ -28,6 +28,7 @@ impl From<OperandSize> for inst::OperandSize {
         }
     }
 }
+
 impl Into<ScalarSize> for OperandSize {
     fn into(self) -> ScalarSize {
         match self {
@@ -239,6 +240,79 @@ impl Assembler {
         self.emit_alu_rrrr(ALUOp3::MAdd, scratch, rn, rd, regs::zero(), size);
     }
 
+    /// And with three registers.
+    pub fn and_rrr(&mut self, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit_alu_rrr(ALUOp::And, rm, rn, rd, size);
+    }
+
+    /// And immediate and register.
+    pub fn and_ir(&mut self, imm: u64, rn: Reg, rd: Reg, size: OperandSize) {
+        let alu_op = ALUOp::And;
+        let cl_size: inst::OperandSize = size.into();
+        if let Some(imm) = ImmLogic::maybe_from_u64(imm, cl_size.to_ty()) {
+            self.emit_alu_rri_logic(alu_op, imm, rn, rd, size);
+        } else {
+            let scratch = regs::scratch();
+            self.load_constant(imm, scratch);
+            self.emit_alu_rrr(alu_op, scratch, rn, rd, size);
+        }
+    }
+
+    /// Or with three registers.
+    pub fn or_rrr(&mut self, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit_alu_rrr(ALUOp::Orr, rm, rn, rd, size);
+    }
+
+    /// Or immediate and register.
+    pub fn or_ir(&mut self, imm: u64, rn: Reg, rd: Reg, size: OperandSize) {
+        let alu_op = ALUOp::Orr;
+        let cl_size: inst::OperandSize = size.into();
+        if let Some(imm) = ImmLogic::maybe_from_u64(imm, cl_size.to_ty()) {
+            self.emit_alu_rri_logic(alu_op, imm, rn, rd, size);
+        } else {
+            let scratch = regs::scratch();
+            self.load_constant(imm, scratch);
+            self.emit_alu_rrr(alu_op, scratch, rn, rd, size);
+        }
+    }
+
+    /// Xor with three registers.
+    pub fn xor_rrr(&mut self, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit_alu_rrr(ALUOp::Eor, rm, rn, rd, size);
+    }
+
+    /// Xor immediate and register.
+    pub fn xor_ir(&mut self, imm: u64, rn: Reg, rd: Reg, size: OperandSize) {
+        let alu_op = ALUOp::Eor;
+        let cl_size: inst::OperandSize = size.into();
+        if let Some(imm) = ImmLogic::maybe_from_u64(imm, cl_size.to_ty()) {
+            self.emit_alu_rri_logic(alu_op, imm, rn, rd, size);
+        } else {
+            let scratch = regs::scratch();
+            self.load_constant(imm, scratch);
+            self.emit_alu_rrr(alu_op, scratch, rn, rd, size);
+        }
+    }
+
+    /// Shift with three registers.
+    pub fn shift_rrr(&mut self, rm: Reg, rn: Reg, rd: Reg, kind: ShiftKind, size: OperandSize) {
+        let shift_op = self.shift_kind_to_alu_op(kind, rm, size);
+        self.emit_alu_rrr(shift_op, rm, rn, rd, size);
+    }
+
+    /// Shift immediate and register.
+    pub fn shift_ir(&mut self, imm: u64, rn: Reg, rd: Reg, kind: ShiftKind, size: OperandSize) {
+        let shift_op = self.shift_kind_to_alu_op(kind, rn, size);
+
+        if let Some(imm) = ImmShift::maybe_from_u64(imm) {
+            self.emit_alu_rri_shift(shift_op, imm, rn, rd, size);
+        } else {
+            let scratch = regs::scratch();
+            self.load_constant(imm, scratch);
+            self.emit_alu_rrr(shift_op, scratch, rn, rd, size);
+        }
+    }
+
     /// Float add with three registers.
     pub fn fadd_rrr(&mut self, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
         self.emit_fpu_rrr(FPUOp2::Add, rm, rn, rd, size);
@@ -346,6 +420,50 @@ impl Assembler {
         });
     }
 
+    fn emit_alu_rri_logic(
+        &mut self,
+        op: ALUOp,
+        imm: ImmLogic,
+        rn: Reg,
+        rd: Reg,
+        size: OperandSize,
+    ) {
+        self.emit(Inst::AluRRImmLogic {
+            alu_op: op,
+            size: size.into(),
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+            imml: imm,
+        });
+    }
+
+    fn emit_alu_rri_shift(
+        &mut self,
+        op: ALUOp,
+        imm: ImmShift,
+        rn: Reg,
+        rd: Reg,
+        size: OperandSize,
+    ) {
+        self.emit(Inst::AluRRImmShift {
+            alu_op: op,
+            size: size.into(),
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+            immshift: imm,
+        });
+    }
+
+    fn emit_alu_rrr(&mut self, op: ALUOp, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
+        self.emit(Inst::AluRRR {
+            alu_op: op,
+            size: size.into(),
+            rd: Writable::from_reg(rd.into()),
+            rn: rn.into(),
+            rm: rm.into(),
+        });
+    }
+
     fn emit_alu_rrr_extend(&mut self, op: ALUOp, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
         self.emit(Inst::AluRRRExtend {
             alu_op: op,
@@ -412,6 +530,22 @@ impl Assembler {
         });
     }
 
+    // Convert ShiftKind to ALUOp. If kind == Rotl, then emulate it by emitting
+    // the negation of the given reg r, and returns ALUOp::RotR.
+    fn shift_kind_to_alu_op(&mut self, kind: ShiftKind, r: Reg, size: OperandSize) -> ALUOp {
+        match kind {
+            ShiftKind::Shl => ALUOp::Lsl,
+            ShiftKind::ShrS => ALUOp::Asr,
+            ShiftKind::ShrU => ALUOp::Lsr,
+            ShiftKind::Rotr => ALUOp::RotR,
+            ShiftKind::Rotl => {
+                // neg(r) is sub(zero, r).
+                self.emit_alu_rrr(ALUOp::Sub, regs::zero(), r, r, size);
+                ALUOp::RotR
+            }
+        }
+    }
+
     /// Get a label from the underlying machine code buffer.
     pub fn get_label(&mut self) -> MachLabel {
         self.buffer.get_label()
diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs
index d5d808b94..1db699f7a 100644
--- a/winch/codegen/src/isa/aarch64/masm.rs
+++ b/winch/codegen/src/isa/aarch64/masm.rs
@@ -347,20 +347,73 @@ impl Masm for MacroAssembler {
         self.asm.fsqrt_rr(src, dst, size);
     }
 
-    fn and(&mut self, _dst: Reg, _lhs: Reg, _rhs: RegImm, _size: OperandSize) {
-        todo!()
+    fn and(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize) {
+        match (rhs, lhs, dst) {
+            (RegImm::Imm(v), rn, rd) => {
+                let imm = match v {
+                    I::I32(v) => v as u64,
+                    I::I64(v) => v,
+                    _ => unreachable!(),
+                };
+
+                self.asm.and_ir(imm, rn, rd, size);
+            }
+
+            (RegImm::Reg(rm), rn, rd) => {
+                self.asm.and_rrr(rm, rn, rd, size);
+            }
+        }
     }
 
-    fn or(&mut self, _dst: Reg, _lhs: Reg, _rhs: RegImm, _size: OperandSize) {
-        todo!()
+    fn or(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize) {
+        match (rhs, lhs, dst) {
+            (RegImm::Imm(v), rn, rd) => {
+                let imm = match v {
+                    I::I32(v) => v as u64,
+                    I::I64(v) => v,
+                    _ => unreachable!(),
+                };
+
+                self.asm.or_ir(imm, rn, rd, size);
+            }
+
+            (RegImm::Reg(rm), rn, rd) => {
+                self.asm.or_rrr(rm, rn, rd, size);
+            }
+        }
     }
 
-    fn xor(&mut self, _dst: Reg, _lhs: Reg, _rhs: RegImm, _size: OperandSize) {
-        todo!()
+    fn xor(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize) {
+        match (rhs, lhs, dst) {
+            (RegImm::Imm(v), rn, rd) => {
+                let imm = match v {
+                    I::I32(v) => v as u64,
+                    I::I64(v) => v,
+                    _ => unreachable!(),
+                };
+
+                self.asm.xor_ir(imm, rn, rd, size);
+            }
+
+            (RegImm::Reg(rm), rn, rd) => {
+                self.asm.xor_rrr(rm, rn, rd, size);
+            }
+        }
     }
 
-    fn shift(&mut self, _context: &mut CodeGenContext, _kind: ShiftKind, _size: OperandSize) {
-        todo!()
+    fn shift_ir(&mut self, dst: Reg, imm: u64, lhs: Reg, kind: ShiftKind, size: OperandSize) {
+        self.asm.shift_ir(imm, lhs, dst, kind, size)
+    }
+
+    fn shift(&mut self, context: &mut CodeGenContext, kind: ShiftKind, size: OperandSize) {
+        let src = context.pop_to_reg(self, None);
+        let dst = context.pop_to_reg(self, None);
+
+        self.asm
+            .shift_rrr(src.into(), dst.into(), dst.into(), kind, size);
+
+        context.free_reg(src);
+        context.stack.push(dst.into());
     }
 
     fn div(&mut self, _context: &mut CodeGenContext, _kind: DivKind, _size: OperandSize) {
diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs
index 85a994c6f..40e804956 100644
--- a/winch/codegen/src/isa/x64/asm.rs
+++ b/winch/codegen/src/isa/x64/asm.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use cranelift_codegen::{
     ir::{
-        types, ConstantPool, ExternalName, LibCall, MemFlags, Opcode, SourceLoc, TrapCode,
+        types, ConstantPool, ExternalName, LibCall, MemFlags, SourceLoc, TrapCode,
         UserExternalNameRef,
     },
     isa::{
@@ -1251,7 +1251,6 @@ impl Assembler {
     pub fn call_with_reg(&mut self, callee: Reg) {
         self.emit(Inst::CallUnknown {
             dest: RegMem::reg(callee.into()),
-            opcode: Opcode::Call,
             info: None,
         });
     }
@@ -1259,11 +1258,7 @@ impl Assembler {
     /// Emit a call to a locally defined function through an index.
     pub fn call_with_name(&mut self, name: UserExternalNameRef) {
         let dest = ExternalName::user(name);
-        self.emit(Inst::CallKnown {
-            dest,
-            opcode: Opcode::Call,
-            info: None,
-        });
+        self.emit(Inst::CallKnown { dest, info: None });
     }
 
     /// Emit a call to a well-known libcall.
diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs
index 95baeb53a..303d11ae5 100644
--- a/winch/codegen/src/isa/x64/masm.rs
+++ b/winch/codegen/src/isa/x64/masm.rs
@@ -530,39 +530,20 @@ impl Masm for MacroAssembler {
         }
     }
 
-    fn shift(&mut self, context: &mut CodeGenContext, kind: ShiftKind, size: OperandSize) {
-        let top = context.stack.peek().expect("value at stack top");
-
-        if size == OperandSize::S32 && top.is_i32_const() {
-            let val = context
-                .stack
-                .pop_i32_const()
-                .expect("i32 const value at stack top");
-            let typed_reg = context.pop_to_reg(self, None);
-
-            self.asm.shift_ir(val as u8, typed_reg.into(), kind, size);
-
-            context.stack.push(typed_reg.into());
-        } else if size == OperandSize::S64 && top.is_i64_const() {
-            let val = context
-                .stack
-                .pop_i64_const()
-                .expect("i64 const value at stack top");
-            let typed_reg = context.pop_to_reg(self, None);
-
-            self.asm.shift_ir(val as u8, typed_reg.into(), kind, size);
+    fn shift_ir(&mut self, dst: Reg, imm: u64, lhs: Reg, kind: ShiftKind, size: OperandSize) {
+        Self::ensure_two_argument_form(&dst, &lhs);
+        self.asm.shift_ir(imm as u8, dst, kind, size)
+    }
 
-            context.stack.push(typed_reg.into());
-        } else {
-            // Number of bits to shift must be in the CL register.
-            let src = context.pop_to_reg(self, Some(regs::rcx()));
-            let dst = context.pop_to_reg(self, None);
+    fn shift(&mut self, context: &mut CodeGenContext, kind: ShiftKind, size: OperandSize) {
+        // Number of bits to shift must be in the CL register.
+        let src = context.pop_to_reg(self, Some(regs::rcx()));
+        let dst = context.pop_to_reg(self, None);
 
-            self.asm.shift_rr(src.into(), dst.into(), kind, size);
+        self.asm.shift_rr(src.into(), dst.into(), kind, size);
 
-            context.free_reg(src);
-            context.stack.push(dst.into());
-        }
+        context.free_reg(src);
+        context.stack.push(dst.into());
     }
 
     fn div(&mut self, context: &mut CodeGenContext, kind: DivKind, size: OperandSize) {
diff --git a/winch/codegen/src/masm.rs b/winch/codegen/src/masm.rs
index 2139a0ce0..eeb73b132 100644
--- a/winch/codegen/src/masm.rs
+++ b/winch/codegen/src/masm.rs
@@ -137,6 +137,7 @@ pub(crate) enum FloatCmpKind {
 /// Kinds of shifts in WebAssembly.The [`masm`] implementation for each ISA is
 /// responsible for emitting the correct sequence of instructions when
 /// lowering to machine code.
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
 pub(crate) enum ShiftKind {
     /// Left shift.
     Shl,
@@ -694,8 +695,11 @@ pub(crate) trait MacroAssembler {
     /// Perform logical exclusive or operation.
     fn xor(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize);
 
-    /// Perform a shift operation.
-    /// Shift is special in that some architectures have specific expectations
+    /// Perform a shift operation between a register and an immediate.
+    fn shift_ir(&mut self, dst: Reg, imm: u64, lhs: Reg, kind: ShiftKind, size: OperandSize);
+
+    /// Perform a shift operation between two registers.
+    /// This case is special in that some architectures have specific expectations
     /// regarding the location of the instruction arguments. To free the
     /// caller from having to deal with the architecture specific constraints
     /// we give this function access to the code generation context, allowing
diff --git a/winch/codegen/src/visitor.rs b/winch/codegen/src/visitor.rs
index fae3189df..85bdbce73 100644
--- a/winch/codegen/src/visitor.rs
+++ b/winch/codegen/src/visitor.rs
@@ -1070,73 +1070,63 @@ where
     }
 
     fn visit_i32_shl(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, Shl, S32);
+        self.context.i32_shift(self.masm, Shl);
     }
 
     fn visit_i64_shl(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, Shl, S64);
+        self.context.i64_shift(self.masm, Shl);
     }
 
     fn visit_i32_shr_s(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, ShrS, S32);
+        self.context.i32_shift(self.masm, ShrS);
     }
 
     fn visit_i64_shr_s(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, ShrS, S64);
+        self.context.i64_shift(self.masm, ShrS);
     }
 
     fn visit_i32_shr_u(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, ShrU, S32);
+        self.context.i32_shift(self.masm, ShrU);
     }
 
     fn visit_i64_shr_u(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, ShrU, S64);
+        self.context.i64_shift(self.masm, ShrU);
     }
 
     fn visit_i32_rotl(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, Rotl, S32);
+        self.context.i32_shift(self.masm, Rotl);
     }
 
     fn visit_i64_rotl(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, Rotl, S64);
+        self.context.i64_shift(self.masm, Rotl);
     }
 
     fn visit_i32_rotr(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, Rotr, S32);
+        self.context.i32_shift(self.masm, Rotr);
     }
 
     fn visit_i64_rotr(&mut self) {
-        use OperandSize::*;
         use ShiftKind::*;
 
-        self.masm.shift(&mut self.context, Rotr, S64);
+        self.context.i64_shift(self.masm, Rotr);
     }
 
     fn visit_end(&mut self) {


Test patch:
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
index f4448b64b..8b9eea9f0 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit_tests.rs
@@ -6061,7 +6061,6 @@ fn test_aarch64_binemit() {
                 uses: smallvec![],
                 defs: smallvec![],
                 clobbers: PRegSet::empty(),
-                opcode: Opcode::Call,
                 caller_callconv: CallConv::SystemV,
                 callee_callconv: CallConv::SystemV,
                 callee_pop_size: 0,
@@ -6078,7 +6077,6 @@ fn test_aarch64_binemit() {
                 uses: smallvec![],
                 defs: smallvec![],
                 clobbers: PRegSet::empty(),
-                opcode: Opcode::CallIndirect,
                 caller_callconv: CallConv::SystemV,
                 callee_callconv: CallConv::SystemV,
                 callee_pop_size: 0,
diff --git a/cranelift/codegen/src/isa/s390x/inst/emit_tests.rs b/cranelift/codegen/src/isa/s390x/inst/emit_tests.rs
index 4732a6173..7051f8d67 100644
--- a/cranelift/codegen/src/isa/s390x/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/s390x/inst/emit_tests.rs
@@ -6989,7 +6989,6 @@ fn test_s390x_binemit() {
                 uses: smallvec![],
                 defs: smallvec![],
                 clobbers: PRegSet::empty(),
-                opcode: Opcode::Call,
                 caller_callconv: CallConv::SystemV,
                 callee_callconv: CallConv::SystemV,
                 tls_symbol: None,
@@ -7007,7 +7006,6 @@ fn test_s390x_binemit() {
                 uses: smallvec![],
                 defs: smallvec![],
                 clobbers: PRegSet::empty(),
-                opcode: Opcode::CallIndirect,
                 caller_callconv: CallConv::SystemV,
                 callee_callconv: CallConv::SystemV,
             }),
diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs
index 9a7993586..2b53fae95 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs
@@ -3762,7 +3762,6 @@ fn test_x64_emit() {
             smallvec![],
             smallvec![],
             PRegSet::default(),
-            Opcode::Call,
             0,
             CallConv::SystemV,
         ),
@@ -3778,7 +3777,6 @@ fn test_x64_emit() {
             smallvec![],
             smallvec![],
             PRegSet::default(),
-            Opcode::CallIndirect,
             0,
             CallConv::SystemV,
         )
diff --git a/crates/test-programs/artifacts/build.rs b/crates/test-programs/artifacts/build.rs
index 7fd653f9b..ea47fd4cc 100644
--- a/crates/test-programs/artifacts/build.rs
+++ b/crates/test-programs/artifacts/build.rs
@@ -76,6 +76,7 @@ fn build_and_generate_tests() {
             s if s.starts_with("nn_") => "nn",
             s if s.starts_with("piped_") => "piped",
             s if s.starts_with("dwarf_") => "dwarf",
+            s if s.starts_with("runtime_config_") => "runtime_config",
             // If you're reading this because you hit this panic, either add it
             // to a test suite above or add a new "suite". The purpose of the
             // categorization above is to have a static assertion that tests
diff --git a/crates/test-programs/src/bin/runtime_config_get.rs b/crates/test-programs/src/bin/runtime_config_get.rs
new file mode 100644
index 000000000..42522f379
--- /dev/null
+++ b/crates/test-programs/src/bin/runtime_config_get.rs
@@ -0,0 +1,8 @@
+use test_programs::config::wasi::config::runtime;
+
+fn main() {
+    let v = runtime::get("hello").unwrap().unwrap();
+    assert_eq!(v, "world");
+    let config = runtime::get_all().unwrap();
+    assert_eq!(config, [("hello".to_string(), "world".to_string())])
+}
diff --git a/crates/test-programs/src/lib.rs b/crates/test-programs/src/lib.rs
index 2ed2dcb8f..d4be5399d 100644
--- a/crates/test-programs/src/lib.rs
+++ b/crates/test-programs/src/lib.rs
@@ -30,3 +30,10 @@ pub mod proxy {
         },
     });
 }
+
+pub mod config {
+    wit_bindgen::generate!({
+        path: "../wasi-runtime-config/wit",
+        world: "wasi:config/imports",
+    });
+}
diff --git a/crates/wasi-nn/tests/check/openvino.rs b/crates/wasi-nn/tests/check/openvino.rs
index fccdc678d..4bf05ba09 100644
--- a/crates/wasi-nn/tests/check/openvino.rs
+++ b/crates/wasi-nn/tests/check/openvino.rs
@@ -4,8 +4,12 @@ use std::fs;
 
 /// Return `Ok` if we find a working OpenVINO installation.
 pub fn is_installed() -> Result<()> {
-    match std::panic::catch_unwind(|| println!("> found openvino version: {}", openvino::version()))
-    {
+    match std::panic::catch_unwind(|| {
+        println!(
+            "> found openvino version: {}",
+            openvino::version().build_number
+        )
+    }) {
         Ok(_) => Ok(()),
         Err(e) => bail!(
             "unable to find an OpenVINO installation: {:?}",
diff --git a/crates/wasi-runtime-config/tests/main.rs b/crates/wasi-runtime-config/tests/main.rs
new file mode 100644
index 000000000..5c07bc9e3
--- /dev/null
+++ b/crates/wasi-runtime-config/tests/main.rs
@@ -0,0 +1,69 @@
+use anyhow::{anyhow, Result};
+use test_programs_artifacts::{foreach_runtime_config, RUNTIME_CONFIG_GET_COMPONENT};
+use wasmtime::{
+    component::{Component, Linker, ResourceTable},
+    Store,
+};
+use wasmtime_wasi::{add_to_linker_async, bindings::Command, WasiCtx, WasiCtxBuilder, WasiView};
+use wasmtime_wasi_runtime_config::{WasiRuntimeConfig, WasiRuntimeConfigVariables};
+
+struct Ctx {
+    table: ResourceTable,
+    wasi_ctx: WasiCtx,
+    wasi_runtime_config_vars: WasiRuntimeConfigVariables,
+}
+
+impl WasiView for Ctx {
+    fn table(&mut self) -> &mut ResourceTable {
+        &mut self.table
+    }
+
+    fn ctx(&mut self) -> &mut WasiCtx {
+        &mut self.wasi_ctx
+    }
+}
+
+async fn run_wasi(path: &str, ctx: Ctx) -> Result<()> {
+    let engine = test_programs_artifacts::engine(|config| {
+        config.async_support(true);
+    });
+    let mut store = Store::new(&engine, ctx);
+    let component = Component::from_file(&engine, path)?;
+
+    let mut linker = Linker::new(&engine);
+    add_to_linker_async(&mut linker)?;
+    wasmtime_wasi_runtime_config::add_to_linker(&mut linker, |h: &mut Ctx| {
+        WasiRuntimeConfig::from(&h.wasi_runtime_config_vars)
+    })?;
+
+    let command = Command::instantiate_async(&mut store, &component, &linker).await?;
+    command
+        .wasi_cli_run()
+        .call_run(&mut store)
+        .await?
+        .map_err(|()| anyhow!("command returned with failing exit status"))
+}
+
+macro_rules! assert_test_exists {
+    ($name:ident) => {
+        #[allow(unused_imports)]
+        use self::$name as _;
+    };
+}
+
+foreach_runtime_config!(assert_test_exists);
+
+#[tokio::test(flavor = "multi_thread")]
+async fn runtime_config_get() -> Result<()> {
+    run_wasi(
+        RUNTIME_CONFIG_GET_COMPONENT,
+        Ctx {
+            table: ResourceTable::new(),
+            wasi_ctx: WasiCtxBuilder::new().build(),
+            wasi_runtime_config_vars: WasiRuntimeConfigVariables::from_iter(vec![(
+                "hello", "world",
+            )]),
+        },
+    )
+    .await
+}
diff --git a/tests/all/component_model/func.rs b/tests/all/component_model/func.rs
index b7481df02..0483ea643 100644
--- a/tests/all/component_model/func.rs
+++ b/tests/all/component_model/func.rs
@@ -5,7 +5,7 @@ use anyhow::Result;
 use std::rc::Rc;
 use std::sync::Arc;
 use wasmtime::component::*;
-use wasmtime::{Store, StoreContextMut, Trap};
+use wasmtime::{Config, Engine, Store, StoreContextMut, Trap};
 
 const CANON_32BIT_NAN: u32 = 0b01111111110000000000000000000000;
 const CANON_64BIT_NAN: u64 = 0b0111111111111000000000000000000000000000000000000000000000000000;
@@ -83,7 +83,9 @@ fn typecheck() -> Result<()> {
         )
     "#;
 
-    let engine = super::engine();
+    let mut config = Config::new();
+    config.wasm_component_model_multiple_returns(true);
+    let engine = Engine::new(&config)?;
     let component = Component::new(&engine, component)?;
     let mut store = Store::new(&engine, ());
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
@@ -874,8 +876,7 @@ fn many_parameters() -> Result<()> {
                 (param "pc" (list char))     ;; offset 56, size 8
                 (param "pd" (list string))   ;; offset 64, size 8
 
-                (result "all-memory" (list u8))
-                (result "pointer" u32)
+                (result (tuple (list u8) u32))
             ))
             (func (export "many-param") (type $t)
                 (canon lift
@@ -905,7 +906,7 @@ fn many_parameters() -> Result<()> {
         &[bool],
         &[char],
         &[&str],
-    ), (WasmList<u8>, u32)>(&mut store, "many-param")?;
+    ), ((WasmList<u8>, u32),)>(&mut store, "many-param")?;
 
     let input = (
         -100,
@@ -929,7 +930,7 @@ fn many_parameters() -> Result<()> {
         ]
         .as_slice(),
     );
-    let (memory, pointer) = func.call(&mut store, input)?;
+    let ((memory, pointer),) = func.call(&mut store, input)?;
     let memory = memory.as_le_slice(&store);
 
     let mut actual = &memory[pointer as usize..][..72];
@@ -1246,7 +1247,7 @@ fn char_bool_memory() -> Result<()> {
             )
             (core instance $i (instantiate $m))
 
-            (func (export "ret-tuple") (param "a" u32) (param "b" u32) (result "c" bool) (result "d" char)
+            (func (export "ret-tuple") (param "a" u32) (param "b" u32) (result (tuple bool char))
                 (canon lift (core func $i "ret-tuple")
                     (memory $i "memory")
                     (realloc (func $i "realloc")))
@@ -1258,17 +1259,17 @@ fn char_bool_memory() -> Result<()> {
     let component = Component::new(&engine, component)?;
     let mut store = Store::new(&engine, ());
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
-    let func = instance.get_typed_func::<(u32, u32), (bool, char)>(&mut store, "ret-tuple")?;
+    let func = instance.get_typed_func::<(u32, u32), ((bool, char),)>(&mut store, "ret-tuple")?;
 
-    let ret = func.call(&mut store, (0, 'a' as u32))?;
+    let (ret,) = func.call(&mut store, (0, 'a' as u32))?;
     assert_eq!(ret, (false, 'a'));
     func.post_return(&mut store)?;
 
-    let ret = func.call(&mut store, (1, '🍰' as u32))?;
+    let (ret,) = func.call(&mut store, (1, '🍰' as u32))?;
     assert_eq!(ret, (true, '🍰'));
     func.post_return(&mut store)?;
 
-    let ret = func.call(&mut store, (2, 'a' as u32))?;
+    let (ret,) = func.call(&mut store, (2, 'a' as u32))?;
     assert_eq!(ret, (true, 'a'));
     func.post_return(&mut store)?;
 
@@ -1441,13 +1442,13 @@ fn option() -> Result<()> {
             )
             (core instance $i (instantiate $m))
 
-            (func (export "option-u8-to-tuple") (param "a" (option u8)) (result "a" u32) (result "b" u32)
+            (func (export "option-u8-to-tuple") (param "a" (option u8)) (result (tuple u32 u32))
                 (canon lift (core func $i "pass1") (memory $i "memory"))
             )
-            (func (export "option-u32-to-tuple") (param "a" (option u32)) (result "a" u32) (result "b" u32)
+            (func (export "option-u32-to-tuple") (param "a" (option u32)) (result (tuple u32 u32))
                 (canon lift (core func $i "pass1") (memory $i "memory"))
             )
-            (func (export "option-string-to-tuple") (param "a" (option string)) (result "a" u32) (result "b" string)
+            (func (export "option-string-to-tuple") (param "a" (option string)) (result (tuple u32 string))
                 (canon lift
                     (core func $i "pass2")
                     (memory $i "memory")
@@ -1479,38 +1480,43 @@ fn option() -> Result<()> {
     let linker = Linker::new(&engine);
     let instance = linker.instantiate(&mut store, &component)?;
 
-    let option_u8_to_tuple =
-        instance.get_typed_func::<(Option<u8>,), (u32, u32)>(&mut store, "option-u8-to-tuple")?;
-    assert_eq!(option_u8_to_tuple.call(&mut store, (None,))?, (0, 0));
+    let option_u8_to_tuple = instance
+        .get_typed_func::<(Option<u8>,), ((u32, u32),)>(&mut store, "option-u8-to-tuple")?;
+    assert_eq!(option_u8_to_tuple.call(&mut store, (None,))?, ((0, 0),));
     option_u8_to_tuple.post_return(&mut store)?;
-    assert_eq!(option_u8_to_tuple.call(&mut store, (Some(0),))?, (1, 0));
+    assert_eq!(option_u8_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));
     option_u8_to_tuple.post_return(&mut store)?;
-    assert_eq!(option_u8_to_tuple.call(&mut store, (Some(100),))?, (1, 100));
+    assert_eq!(
+        option_u8_to_tuple.call(&mut store, (Some(100),))?,
+        ((1, 100),)
+    );
     option_u8_to_tuple.post_return(&mut store)?;
 
-    let option_u32_to_tuple =
-        instance.get_typed_func::<(Option<u32>,), (u32, u32)>(&mut store, "option-u32-to-tuple")?;
-    assert_eq!(option_u32_to_tuple.call(&mut store, (None,))?, (0, 0));
+    let option_u32_to_tuple = instance
+        .get_typed_func::<(Option<u32>,), ((u32, u32),)>(&mut store, "option-u32-to-tuple")?;
+    assert_eq!(option_u32_to_tuple.call(&mut store, (None,))?, ((0, 0),));
     option_u32_to_tuple.post_return(&mut store)?;
-    assert_eq!(option_u32_to_tuple.call(&mut store, (Some(0),))?, (1, 0));
+    assert_eq!(option_u32_to_tuple.call(&mut store, (Some(0),))?, ((1, 0),));
     option_u32_to_tuple.post_return(&mut store)?;
     assert_eq!(
         option_u32_to_tuple.call(&mut store, (Some(100),))?,
-        (1, 100)
+        ((1, 100),)
     );
     option_u32_to_tuple.post_return(&mut store)?;
 
-    let option_string_to_tuple = instance
-        .get_typed_func::<(Option<&str>,), (u32, WasmStr)>(&mut store, "option-string-to-tuple")?;
-    let (a, b) = option_string_to_tuple.call(&mut store, (None,))?;
+    let option_string_to_tuple = instance.get_typed_func::<(Option<&str>,), ((u32, WasmStr),)>(
+        &mut store,
+        "option-string-to-tuple",
+    )?;
+    let ((a, b),) = option_string_to_tuple.call(&mut store, (None,))?;
     assert_eq!(a, 0);
     assert_eq!(b.to_str(&store)?, "");
     option_string_to_tuple.post_return(&mut store)?;
-    let (a, b) = option_string_to_tuple.call(&mut store, (Some(""),))?;
+    let ((a, b),) = option_string_to_tuple.call(&mut store, (Some(""),))?;
     assert_eq!(a, 1);
     assert_eq!(b.to_str(&store)?, "");
     option_string_to_tuple.post_return(&mut store)?;
-    let (a, b) = option_string_to_tuple.call(&mut store, (Some("hello"),))?;
+    let ((a, b),) = option_string_to_tuple.call(&mut store, (Some("hello"),))?;
     assert_eq!(a, 1);
     assert_eq!(b.to_str(&store)?, "hello");
     option_string_to_tuple.post_return(&mut store)?;
@@ -1613,11 +1619,11 @@ fn expected() -> Result<()> {
             (func (export "take-expected-unit") (param "a" (result)) (result u32)
                 (canon lift (core func $i "pass0"))
             )
-            (func (export "take-expected-u8-f32") (param "a" (result u8 (error float32))) (result "a" u32) (result "b" u32)
+            (func (export "take-expected-u8-f32") (param "a" (result u8 (error float32))) (result (tuple u32 u32))
                 (canon lift (core func $i "pass1") (memory $i "memory"))
             )
             (type $list (list u8))
-            (func (export "take-expected-string") (param "a" (result string (error $list))) (result "a" u32) (result "b" string)
+            (func (export "take-expected-string") (param "a" (result string (error $list))) (result (tuple u32 string))
                 (canon lift
                     (core func $i "pass2")
                     (memory $i "memory")
@@ -1650,24 +1656,25 @@ fn expected() -> Result<()> {
     take_expected_unit.post_return(&mut store)?;
 
     let take_expected_u8_f32 = instance
-        .get_typed_func::<(Result<u8, f32>,), (u32, u32)>(&mut store, "take-expected-u8-f32")?;
-    assert_eq!(take_expected_u8_f32.call(&mut store, (Ok(1),))?, (0, 1));
+        .get_typed_func::<(Result<u8, f32>,), ((u32, u32),)>(&mut store, "take-expected-u8-f32")?;
+    assert_eq!(take_expected_u8_f32.call(&mut store, (Ok(1),))?, ((0, 1),));
     take_expected_u8_f32.post_return(&mut store)?;
     assert_eq!(
         take_expected_u8_f32.call(&mut store, (Err(2.0),))?,
-        (1, 2.0f32.to_bits())
+        ((1, 2.0f32.to_bits()),)
     );
     take_expected_u8_f32.post_return(&mut store)?;
 
-    let take_expected_string = instance.get_typed_func::<(Result<&str, &[u8]>,), (u32, WasmStr)>(
-        &mut store,
-        "take-expected-string",
-    )?;
-    let (a, b) = take_expected_string.call(&mut store, (Ok("hello"),))?;
+    let take_expected_string = instance
+        .get_typed_func::<(Result<&str, &[u8]>,), ((u32, WasmStr),)>(
+            &mut store,
+            "take-expected-string",
+        )?;
+    let ((a, b),) = take_expected_string.call(&mut store, (Ok("hello"),))?;
     assert_eq!(a, 0);
     assert_eq!(b.to_str(&store)?, "hello");
     take_expected_string.post_return(&mut store)?;
-    let (a, b) = take_expected_string.call(&mut store, (Err(b"goodbye"),))?;
+    let ((a, b),) = take_expected_string.call(&mut store, (Err(b"goodbye"),))?;
     assert_eq!(a, 1);
     assert_eq!(b.to_str(&store)?, "goodbye");
     take_expected_string.post_return(&mut store)?;
@@ -1746,9 +1753,7 @@ fn fancy_list() -> Result<()> {
             (type $input (list (tuple $a $b)))
             (func (export "take")
                 (param "a" $input)
-                (result "ptr" u32)
-                (result "len" u32)
-                (result "list" (list u8))
+                (result (tuple u32 u32 (list u8)))
                 (canon lift
                     (core func $i "take")
                     (memory $i "memory")
@@ -1764,7 +1769,7 @@ fn fancy_list() -> Result<()> {
     let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
 
     let func = instance
-        .get_typed_func::<(&[(Option<u8>, Result<(), &str>)],), (u32, u32, WasmList<u8>)>(
+        .get_typed_func::<(&[(Option<u8>, Result<(), &str>)],), ((u32, u32, WasmList<u8>),)>(
             &mut store, "take",
         )?;
 
@@ -1773,7 +1778,7 @@ fn fancy_list() -> Result<()> {
         (Some(2), Err("hello there")),
         (Some(200), Err("general kenobi")),
     ];
-    let (ptr, len, list) = func.call(&mut store, (&input,))?;
+    let ((ptr, len, list),) = func.call(&mut store, (&input,))?;
     let memory = list.as_le_slice(&store);
     let ptr = usize::try_from(ptr).unwrap();
     let len = usize::try_from(len).unwrap();
diff --git a/tests/disas/winch/aarch64/i32_and/const.wat b/tests/disas/winch/aarch64/i32_and/const.wat
new file mode 100644
index 000000000..51697a270
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_and/const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.and)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       and     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_and/locals.wat b/tests/disas/winch/aarch64/i32_and/locals.wat
new file mode 100644
index 000000000..907ad21d1
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_and/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.and)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       and     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_and/params.wat b/tests/disas/winch/aarch64/i32_and/params.wat
new file mode 100644
index 000000000..f794d45bf
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_and/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.and)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       and     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_or/const.wat b/tests/disas/winch/aarch64/i32_or/const.wat
new file mode 100644
index 000000000..710a188d1
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_or/const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.or)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       orr     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_or/locals.wat b/tests/disas/winch/aarch64/i32_or/locals.wat
new file mode 100644
index 000000000..a4493e038
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_or/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.or)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       orr     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_or/params.wat b/tests/disas/winch/aarch64/i32_or/params.wat
new file mode 100644
index 000000000..c61280c8e
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_or/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.or)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       orr     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotl/16_const.wat b/tests/disas/winch/aarch64/i32_rotl/16_const.wat
new file mode 100644
index 000000000..b1cc4727c
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotl/16_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 512)
+        (i32.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       sub     w0, w0, wzr
+;;       mov     x16, #0x200
+;;       ror     w0, w0, w16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotl/8_const.wat b/tests/disas/winch/aarch64/i32_rotl/8_const.wat
new file mode 100644
index 000000000..fb6048c26
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotl/8_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       sub     w0, w0, wzr
+;;       ror     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotl/locals.wat b/tests/disas/winch/aarch64/i32_rotl/locals.wat
new file mode 100644
index 000000000..a425fee86
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotl/locals.wat
@@ -0,0 +1,45 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       sub     w0, w0, wzr
+;;       ror     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotl/params.wat b/tests/disas/winch/aarch64/i32_rotl/params.wat
new file mode 100644
index 000000000..2f3d857ab
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotl/params.wat
@@ -0,0 +1,30 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       sub     w0, w0, wzr
+;;       ror     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotr/16_const.wat b/tests/disas/winch/aarch64/i32_rotr/16_const.wat
new file mode 100644
index 000000000..82a0df5ab
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotr/16_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 512)
+        (i32.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       mov     x16, #0x200
+;;       ror     w0, w0, w16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotr/8_const.wat b/tests/disas/winch/aarch64/i32_rotr/8_const.wat
new file mode 100644
index 000000000..68b98462b
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotr/8_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       ror     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotr/locals.wat b/tests/disas/winch/aarch64/i32_rotr/locals.wat
new file mode 100644
index 000000000..998edff0b
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotr/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       ror     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_rotr/params.wat b/tests/disas/winch/aarch64/i32_rotr/params.wat
new file mode 100644
index 000000000..73a2ba691
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_rotr/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       ror     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shl/16_const.wat b/tests/disas/winch/aarch64/i32_shl/16_const.wat
new file mode 100644
index 000000000..f792f5393
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shl/16_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 512)
+        (i32.shl)
+    )
+)
+
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       mov     x16, #0x200
+;;       lsl     w0, w0, w16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shl/8_const.wat b/tests/disas/winch/aarch64/i32_shl/8_const.wat
new file mode 100644
index 000000000..9f2c79e05
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shl/8_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.shl)
+    )
+)
+
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       lsl     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shl/locals.wat b/tests/disas/winch/aarch64/i32_shl/locals.wat
new file mode 100644
index 000000000..9d25ff10c
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shl/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.shl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       lsl     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shl/params.wat b/tests/disas/winch/aarch64/i32_shl/params.wat
new file mode 100644
index 000000000..750bae0c8
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shl/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.shl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       lsl     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_s/16_const.wat b/tests/disas/winch/aarch64/i32_shr_s/16_const.wat
new file mode 100644
index 000000000..2adbff3e1
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_s/16_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 512)
+        (i32.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       mov     x16, #0x200
+;;       asr     w0, w0, w16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_s/8_const.wat b/tests/disas/winch/aarch64/i32_shr_s/8_const.wat
new file mode 100644
index 000000000..ceff09d35
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_s/8_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       asr     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_s/locals.wat b/tests/disas/winch/aarch64/i32_shr_s/locals.wat
new file mode 100644
index 000000000..5bc01fd76
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_s/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       asr     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_s/params.wat b/tests/disas/winch/aarch64/i32_shr_s/params.wat
new file mode 100644
index 000000000..2642370d5
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_s/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       asr     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_u/16_const.wat b/tests/disas/winch/aarch64/i32_shr_u/16_const.wat
new file mode 100644
index 000000000..a66a99857
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_u/16_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 512)
+        (i32.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       mov     x16, #0x200
+;;       lsr     w0, w0, w16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_u/8_const.wat b/tests/disas/winch/aarch64/i32_shr_u/8_const.wat
new file mode 100644
index 000000000..c3bcff0c7
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_u/8_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       lsr     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_u/locals.wat b/tests/disas/winch/aarch64/i32_shr_u/locals.wat
new file mode 100644
index 000000000..25045070d
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_u/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       lsr     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_shr_u/params.wat b/tests/disas/winch/aarch64/i32_shr_u/params.wat
new file mode 100644
index 000000000..e5658d370
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_shr_u/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       lsr     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_xor/const.wat b/tests/disas/winch/aarch64/i32_xor/const.wat
new file mode 100644
index 000000000..76bbd4b68
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_xor/const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (i32.const 1)
+        (i32.const 2)
+        (i32.xor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       eor     w0, w0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_xor/locals.wat b/tests/disas/winch/aarch64/i32_xor/locals.wat
new file mode 100644
index 000000000..7cede8bad
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_xor/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i32)
+        (local $foo i32)  
+        (local $bar i32)
+
+        (i32.const 1)
+        (local.set $foo)
+
+        (i32.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i32.xor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       mov     x16, #0
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     w0, w16
+;;       stur    w0, [x28, #4]
+;;       mov     x16, #2
+;;       mov     w0, w16
+;;       stur    w0, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       eor     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i32_xor/params.wat b/tests/disas/winch/aarch64/i32_xor/params.wat
new file mode 100644
index 000000000..892e9abf6
--- /dev/null
+++ b/tests/disas/winch/aarch64/i32_xor/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i32) (param i32) (result i32)
+        (local.get 0)
+        (local.get 1)
+        (i32.xor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x18
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x10]
+;;       stur    x1, [x28, #8]
+;;       stur    w2, [x28, #4]
+;;       stur    w3, [x28]
+;;       ldur    w0, [x28]
+;;       ldur    w1, [x28, #4]
+;;       eor     w1, w1, w0
+;;       mov     w0, w1
+;;       add     sp, sp, #0x18
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_and/32_const.wat b/tests/disas/winch/aarch64/i64_and/32_const.wat
new file mode 100644
index 000000000..7e82c94f4
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_and/32_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 2)
+        (i64.const 3)
+        (i64.and)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       and     x0, x0, #3
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_and/64_const.wat b/tests/disas/winch/aarch64/i64_and/64_const.wat
new file mode 100644
index 000000000..232bddd07
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_and/64_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 9223372036854775806)
+        (i64.const 9223372036854775807)
+        (i64.and)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       orr     x16, xzr, #0x7ffffffffffffffe
+;;       mov     x0, x16
+;;       and     x0, x0, #0x7fffffffffffffff
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_and/locals.wat b/tests/disas/winch/aarch64/i64_and/locals.wat
new file mode 100644
index 000000000..6b0e94d70
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_and/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)
+        (local $bar i64)
+
+        (i64.const 2)
+        (local.set $foo)
+        (i64.const 3)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.and)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #3
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       and     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_and/params.wat b/tests/disas/winch/aarch64/i64_and/params.wat
new file mode 100644
index 000000000..23e3fb662
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_and/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.and)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       and     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_or/32_const.wat b/tests/disas/winch/aarch64/i64_or/32_const.wat
new file mode 100644
index 000000000..40a6e1864
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_or/32_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 2)
+        (i64.const 3)
+        (i64.or)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       orr     x0, x0, #3
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_or/64_const.wat b/tests/disas/winch/aarch64/i64_or/64_const.wat
new file mode 100644
index 000000000..2e183b986
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_or/64_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 9223372036854775806)
+        (i64.const 9223372036854775807)
+        (i64.or)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       orr     x16, xzr, #0x7ffffffffffffffe
+;;       mov     x0, x16
+;;       orr     x0, x0, #0x7fffffffffffffff
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_or/locals.wat b/tests/disas/winch/aarch64/i64_or/locals.wat
new file mode 100644
index 000000000..02335a63b
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_or/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)
+        (local $bar i64)
+
+        (i64.const 2)
+        (local.set $foo)
+        (i64.const 3)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.or)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #3
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       orr     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_or/params.wat b/tests/disas/winch/aarch64/i64_or/params.wat
new file mode 100644
index 000000000..65760cfbf
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_or/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.or)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       orr     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotl/16_const.wat b/tests/disas/winch/aarch64/i64_rotl/16_const.wat
new file mode 100644
index 000000000..54471ba1b
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotl/16_const.wat
@@ -0,0 +1,28 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 512)
+        (i64.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       sub     x0, x0, xzr
+;;       mov     x16, #0x200
+;;       ror     x0, x0, x16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotl/8_const.wat b/tests/disas/winch/aarch64/i64_rotl/8_const.wat
new file mode 100644
index 000000000..a61e486ad
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotl/8_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 2)
+        (i64.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       sub     x0, x0, xzr
+;;       ror     x0, x0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotl/locals.wat b/tests/disas/winch/aarch64/i64_rotl/locals.wat
new file mode 100644
index 000000000..9784c2509
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotl/locals.wat
@@ -0,0 +1,46 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)  
+        (local $bar i64)
+
+        (i64.const 1)
+        (local.set $foo)
+
+        (i64.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       sub     x0, x0, xzr
+;;       ror     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotl/params.wat b/tests/disas/winch/aarch64/i64_rotl/params.wat
new file mode 100644
index 000000000..7ea35f4da
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotl/params.wat
@@ -0,0 +1,30 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.rotl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       sub     x0, x0, xzr
+;;       ror     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotr/16_const.wat b/tests/disas/winch/aarch64/i64_rotr/16_const.wat
new file mode 100644
index 000000000..c1ead62d5
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotr/16_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 512)
+        (i64.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       mov     x16, #0x200
+;;       ror     x0, x0, x16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotr/8_const.wat b/tests/disas/winch/aarch64/i64_rotr/8_const.wat
new file mode 100644
index 000000000..287d7f178
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotr/8_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 2)
+        (i64.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       ror     x0, x0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotr/locals.wat b/tests/disas/winch/aarch64/i64_rotr/locals.wat
new file mode 100644
index 000000000..31db682d8
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotr/locals.wat
@@ -0,0 +1,45 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)  
+        (local $bar i64)
+
+        (i64.const 1)
+        (local.set $foo)
+
+        (i64.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       ror     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_rotr/params.wat b/tests/disas/winch/aarch64/i64_rotr/params.wat
new file mode 100644
index 000000000..e44c129ea
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_rotr/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.rotr)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       ror     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shl/16_const.wat b/tests/disas/winch/aarch64/i64_shl/16_const.wat
new file mode 100644
index 000000000..e4e5d33e3
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shl/16_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 512)
+        (i64.shl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       mov     x16, #0x200
+;;       lsl     x0, x0, x16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shl/8_const.wat b/tests/disas/winch/aarch64/i64_shl/8_const.wat
new file mode 100644
index 000000000..362642f61
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shl/8_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 2)
+        (i64.shl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       lsl     x0, x0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shl/locals.wat b/tests/disas/winch/aarch64/i64_shl/locals.wat
new file mode 100644
index 000000000..cd529ba3b
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shl/locals.wat
@@ -0,0 +1,45 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)  
+        (local $bar i64)
+
+        (i64.const 1)
+        (local.set $foo)
+
+        (i64.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.shl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       lsl     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shl/params.wat b/tests/disas/winch/aarch64/i64_shl/params.wat
new file mode 100644
index 000000000..0de3ca97e
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shl/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.shl)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       lsl     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_s/16_const.wat b/tests/disas/winch/aarch64/i64_shr_s/16_const.wat
new file mode 100644
index 000000000..99d67d884
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_s/16_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 512)
+        (i64.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       mov     x16, #0x200
+;;       asr     x0, x0, x16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_s/8_const.wat b/tests/disas/winch/aarch64/i64_shr_s/8_const.wat
new file mode 100644
index 000000000..0b07f24af
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_s/8_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 2)
+        (i64.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       asr     x0, x0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_s/locals.wat b/tests/disas/winch/aarch64/i64_shr_s/locals.wat
new file mode 100644
index 000000000..5db726218
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_s/locals.wat
@@ -0,0 +1,45 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)  
+        (local $bar i64)
+
+        (i64.const 1)
+        (local.set $foo)
+
+        (i64.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       asr     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_s/params.wat b/tests/disas/winch/aarch64/i64_shr_s/params.wat
new file mode 100644
index 000000000..005f7e17e
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_s/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.shr_s)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       asr     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_u/16_const.wat b/tests/disas/winch/aarch64/i64_shr_u/16_const.wat
new file mode 100644
index 000000000..b0a759fd6
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_u/16_const.wat
@@ -0,0 +1,27 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 512)
+        (i64.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       mov     x16, #0x200
+;;       lsr     x0, x0, x16
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_u/8_const.wat b/tests/disas/winch/aarch64/i64_shr_u/8_const.wat
new file mode 100644
index 000000000..8c45f245f
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_u/8_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 1)
+        (i64.const 2)
+        (i64.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       lsr     x0, x0, #2
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_u/locals.wat b/tests/disas/winch/aarch64/i64_shr_u/locals.wat
new file mode 100644
index 000000000..aa7906309
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_u/locals.wat
@@ -0,0 +1,45 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)  
+        (local $bar i64)
+
+        (i64.const 1)
+        (local.set $foo)
+
+        (i64.const 2)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #1
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       lsr     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_shr_u/params.wat b/tests/disas/winch/aarch64/i64_shr_u/params.wat
new file mode 100644
index 000000000..39bcda73b
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_shr_u/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.shr_u)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       lsr     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_xor/32_const.wat b/tests/disas/winch/aarch64/i64_xor/32_const.wat
new file mode 100644
index 000000000..eb5235dff
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_xor/32_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 2)
+        (i64.const 3)
+        (i64.xor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       eor     x0, x0, #3
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_xor/64_const.wat b/tests/disas/winch/aarch64/i64_xor/64_const.wat
new file mode 100644
index 000000000..f5bc49de7
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_xor/64_const.wat
@@ -0,0 +1,26 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (i64.const 9223372036854775806)
+        (i64.const 9223372036854775807)
+        (i64.xor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x10
+;;       mov     x28, sp
+;;       stur    x0, [x28, #8]
+;;       stur    x1, [x28]
+;;       orr     x16, xzr, #0x7ffffffffffffffe
+;;       mov     x0, x16
+;;       eor     x0, x0, #0x7fffffffffffffff
+;;       add     sp, sp, #0x10
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_xor/locals.wat b/tests/disas/winch/aarch64/i64_xor/locals.wat
new file mode 100644
index 000000000..b8a1fa5a2
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_xor/locals.wat
@@ -0,0 +1,44 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (result i64)
+        (local $foo i64)
+        (local $bar i64)
+
+        (i64.const 2)
+        (local.set $foo)
+        (i64.const 3)
+        (local.set $bar)
+
+        (local.get $foo)
+        (local.get $bar)
+        (i64.xor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       mov     x16, #0
+;;       stur    x16, [x28, #8]
+;;       stur    x16, [x28]
+;;       mov     x16, #2
+;;       mov     x0, x16
+;;       stur    x0, [x28, #8]
+;;       mov     x16, #3
+;;       mov     x0, x16
+;;       stur    x0, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       eor     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret
diff --git a/tests/disas/winch/aarch64/i64_xor/params.wat b/tests/disas/winch/aarch64/i64_xor/params.wat
new file mode 100644
index 000000000..047db6184
--- /dev/null
+++ b/tests/disas/winch/aarch64/i64_xor/params.wat
@@ -0,0 +1,29 @@
+;;! target = "aarch64"
+;;! test = "winch"
+
+(module
+    (func (param i64) (param i64) (result i64)
+        (local.get 0)
+        (local.get 1)
+        (i64.xor)
+    )
+)
+;; wasm[0]::function[0]:
+;;       stp     x29, x30, [sp, #-0x10]!
+;;       mov     x29, sp
+;;       mov     x28, sp
+;;       mov     x9, x0
+;;       sub     sp, sp, #0x20
+;;       mov     x28, sp
+;;       stur    x0, [x28, #0x18]
+;;       stur    x1, [x28, #0x10]
+;;       stur    x2, [x28, #8]
+;;       stur    x3, [x28]
+;;       ldur    x0, [x28]
+;;       ldur    x1, [x28, #8]
+;;       eor     x1, x1, x0
+;;       mov     x0, x1
+;;       add     sp, sp, #0x20
+;;       mov     x28, sp
+;;       ldp     x29, x30, [sp], #0x10
+;;       ret