Repository: bytecodealliance/wasmtime
Pull request #8378 (2024-04-16 01:08:50)

cranelift/PCC: Only process real address modes
The `compute_addr` helper only works on real address modes, and one of its callers checks that it only passes a real address mode to it. On x64 we have different Rust types for address modes which are real versus synthetic, so we can simplify a little by reflecting this requirement in the types.

cc: @elliottt


Patch:
diff --git a/.github/subscribe-to-label.json b/.github/subscribe-to-label.json
index a39d31632..6714782ca 100644
--- a/.github/subscribe-to-label.json
+++ b/.github/subscribe-to-label.json
@@ -1,6 +1,5 @@
 {
   "cfallin": ["isle"],
   "fitzgen": ["fuzzing", "isle", "wasmtime:ref-types"],
-  "peterhuene": ["wasmtime:api", "wasmtime:c-api"],
   "saulecabrera": ["winch"]
 }
diff --git a/Cargo.lock b/Cargo.lock
index edf2288fe..ea48ab4d5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -849,7 +849,7 @@ dependencies = [
  "serde_derive",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmtime-types",
  "wat",
 ]
@@ -2756,7 +2756,7 @@ dependencies = [
  "cargo_metadata",
  "heck",
  "wasmtime",
- "wit-component 0.203.0",
+ "wit-component 0.204.0",
 ]
 
 [[package]]
@@ -3092,7 +3092,7 @@ name = "verify-component-adapter"
 version = "21.0.0"
 dependencies = [
  "anyhow",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wat",
 ]
 
@@ -3184,7 +3184,7 @@ dependencies = [
  "byte-array-literals",
  "object 0.33.0",
  "wasi",
- "wasm-encoder 0.203.0",
+ "wasm-encoder 0.204.0",
  "wit-bindgen",
 ]
 
@@ -3253,9 +3253,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-encoder"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87e3b46a0d9d9143d57aa926c42e2af284b5cb8f0c7b71079afc7a6fca42db50"
+checksum = "500cbde9b4d8dfc0335ec729d226dbf083e51e47501ac71e6addaed10ccb0a51"
 dependencies = [
  "leb128",
 ]
@@ -3278,9 +3278,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-metadata"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19fbd9b7017bdb3ceb63503a18d1aa7b0d026e18ce659d40b93bd9a15391424f"
+checksum = "f33b61754be27af5abf0fda890badb7c3265f7b634077157b4214704303ebbcc"
 dependencies = [
  "anyhow",
  "indexmap 2.0.0",
@@ -3288,36 +3288,36 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "spdx",
- "wasm-encoder 0.203.0",
- "wasmparser 0.203.0",
+ "wasm-encoder 0.204.0",
+ "wasmparser 0.204.0",
 ]
 
 [[package]]
 name = "wasm-mutate"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1980f4daf79655cc22aa9ba155f47e47efd67f7e628274f3c71c46c3f1d42fb0"
+checksum = "34d24ec979ae72a27f3770a7db426e7f2d459e67ecf26983ca727675e4956037"
 dependencies = [
  "egg",
  "log",
  "rand",
  "thiserror",
- "wasm-encoder 0.203.0",
- "wasmparser 0.203.0",
+ "wasm-encoder 0.204.0",
+ "wasmparser 0.204.0",
 ]
 
 [[package]]
 name = "wasm-smith"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2d2a6692cfc637bd685a8a114f1a4f83b5856725f84cf8163b14ba6024467c5"
+checksum = "e1fe1d435b8a14ebf05c2669cf76d1ca1cef6b632320c8e0b0e703ff0259a328"
 dependencies = [
  "anyhow",
  "arbitrary",
  "flagset",
  "indexmap 2.0.0",
  "leb128",
- "wasm-encoder 0.203.0",
+ "wasm-encoder 0.204.0",
 ]
 
 [[package]]
@@ -3373,9 +3373,9 @@ dependencies = [
 
 [[package]]
 name = "wasmparser"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b473b35fff082e8c8377f4e2c8d48075e562aa051e48a02229bdcafbf349d37"
+checksum = "1239d539d9b92489cf729d40df694a62b09f5159f2ab058f4de15d4f820d6585"
 dependencies = [
  "bitflags 2.4.1",
  "indexmap 2.0.0",
@@ -3393,12 +3393,12 @@ dependencies = [
 
 [[package]]
 name = "wasmprinter"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74143e920e9f672eda5c40fa1d25a2d6a928e64debc0df90209533a84b1887bd"
+checksum = "ce3e6a3111855bf59cb6a16a95e217c59eff1d30d9f09483039b81b0739c3128"
 dependencies = [
  "anyhow",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
 ]
 
 [[package]]
@@ -3430,8 +3430,8 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "wasi-common",
- "wasm-encoder 0.203.0",
- "wasmparser 0.203.0",
+ "wasm-encoder 0.204.0",
+ "wasmparser 0.204.0",
  "wasmtime-cache",
  "wasmtime-component-macro",
  "wasmtime-component-util",
@@ -3571,7 +3571,7 @@ dependencies = [
  "tracing",
  "walkdir",
  "wasi-common",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmtime",
  "wasmtime-cache",
  "wasmtime-cli-flags",
@@ -3585,10 +3585,10 @@ dependencies = [
  "wasmtime-wasi-nn",
  "wasmtime-wasi-threads",
  "wasmtime-wast",
- "wast 203.0.0",
+ "wast 204.0.0",
  "wat",
  "windows-sys 0.52.0",
- "wit-component 0.203.0",
+ "wit-component 0.204.0",
 ]
 
 [[package]]
@@ -3617,7 +3617,7 @@ dependencies = [
  "wasmtime",
  "wasmtime-component-util",
  "wasmtime-wit-bindgen",
- "wit-parser 0.203.0",
+ "wit-parser 0.204.0",
 ]
 
 [[package]]
@@ -3641,7 +3641,7 @@ dependencies = [
  "object 0.33.0",
  "target-lexicon",
  "thiserror",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmtime-environ",
  "wasmtime-versioned-export-macros",
 ]
@@ -3665,8 +3665,8 @@ dependencies = [
  "serde_derive",
  "target-lexicon",
  "thiserror",
- "wasm-encoder 0.203.0",
- "wasmparser 0.203.0",
+ "wasm-encoder 0.204.0",
+ "wasmparser 0.204.0",
  "wasmprinter",
  "wasmtime-component-util",
  "wasmtime-types",
@@ -3681,7 +3681,7 @@ dependencies = [
  "component-fuzz-util",
  "env_logger",
  "libfuzzer-sys",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmprinter",
  "wasmtime-environ",
  "wat",
@@ -3738,7 +3738,7 @@ dependencies = [
  "rand",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmtime",
  "wasmtime-fuzzing",
 ]
@@ -3758,12 +3758,12 @@ dependencies = [
  "target-lexicon",
  "tempfile",
  "v8",
- "wasm-encoder 0.203.0",
+ "wasm-encoder 0.204.0",
  "wasm-mutate",
  "wasm-smith",
  "wasm-spec-interpreter",
  "wasmi",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmprinter",
  "wasmtime",
  "wasmtime-wast",
@@ -3810,7 +3810,7 @@ dependencies = [
  "rand",
  "rustix",
  "sptr",
- "wasm-encoder 0.203.0",
+ "wasm-encoder 0.204.0",
  "wasmtime-asm-macros",
  "wasmtime-environ",
  "wasmtime-fiber",
@@ -3833,7 +3833,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "thiserror",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
 ]
 
 [[package]]
@@ -3940,7 +3940,7 @@ dependencies = [
  "anyhow",
  "log",
  "wasmtime",
- "wast 203.0.0",
+ "wast 204.0.0",
 ]
 
 [[package]]
@@ -3952,7 +3952,7 @@ dependencies = [
  "gimli",
  "object 0.33.0",
  "target-lexicon",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmtime-cranelift",
  "wasmtime-environ",
  "winch-codegen",
@@ -3965,7 +3965,7 @@ dependencies = [
  "anyhow",
  "heck",
  "indexmap 2.0.0",
- "wit-parser 0.203.0",
+ "wit-parser 0.204.0",
 ]
 
 [[package]]
@@ -3983,24 +3983,24 @@ dependencies = [
 
 [[package]]
 name = "wast"
-version = "203.0.0"
+version = "204.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "872020f79fa7a09aeaec308d0ba60816e2f808d9b402e2f89d2a9c76eda482cd"
+checksum = "a0e3de19692b3d4c2fa13775271a751935decf530ae59c408c9f0b510b4ead62"
 dependencies = [
  "bumpalo",
  "leb128",
  "memchr",
  "unicode-width",
- "wasm-encoder 0.203.0",
+ "wasm-encoder 0.204.0",
 ]
 
 [[package]]
 name = "wat"
-version = "1.203.0"
+version = "1.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2913b554125048798744fce03832d5330d87624782f8ec6ad8c9716a1ef7a37"
+checksum = "4280322d523214024d03bc05e25bdda6088d5229d9515aecd78c5914b1f3e734"
 dependencies = [
- "wast 203.0.0",
+ "wast 204.0.0",
 ]
 
 [[package]]
@@ -4130,7 +4130,7 @@ dependencies = [
  "regalloc2",
  "smallvec",
  "target-lexicon",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
  "wasmtime-cranelift",
  "wasmtime-environ",
 ]
@@ -4381,9 +4381,9 @@ dependencies = [
 
 [[package]]
 name = "wit-component"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "379c4e193d37b3e2c808e99515c2a91dc19e7b3b160adfbb374463875375d6ff"
+checksum = "657fab6fd5f9ad3e1b7fd65a76f06ff25cac7f4fd8f885715bed4147095a6c38"
 dependencies = [
  "anyhow",
  "bitflags 2.4.1",
@@ -4392,10 +4392,10 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "wasm-encoder 0.203.0",
- "wasm-metadata 0.203.0",
- "wasmparser 0.203.0",
- "wit-parser 0.203.0",
+ "wasm-encoder 0.204.0",
+ "wasm-metadata 0.204.0",
+ "wasmparser 0.204.0",
+ "wit-parser 0.204.0",
 ]
 
 [[package]]
@@ -4418,9 +4418,9 @@ dependencies = [
 
 [[package]]
 name = "wit-parser"
-version = "0.203.0"
+version = "0.204.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f547f9154c0fbd020c81c348d703b549bafd16ea68b15927acb5c890467e734"
+checksum = "bdc20f9284017574e942347e953e82fb3a0dccdfafa3beea7840b0bcbfa4fe00"
 dependencies = [
  "anyhow",
  "id-arena",
@@ -4431,7 +4431,7 @@ dependencies = [
  "serde_derive",
  "serde_json",
  "unicode-xid",
- "wasmparser 0.203.0",
+ "wasmparser 0.204.0",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 2ab862b30..ab812cbc6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -234,15 +234,15 @@ rustix = "0.38.31"
 wit-bindgen = { version = "0.22.0", default-features = false }
 
 # wasm-tools family:
-wasmparser = "0.203.0"
-wat = "1.203.0"
-wast = "203.0.0"
-wasmprinter = "0.203.0"
-wasm-encoder = "0.203.0"
-wasm-smith = "0.203.0"
-wasm-mutate = "0.203.0"
-wit-parser = "0.203.0"
-wit-component = "0.203.0"
+wasmparser = "0.204.0"
+wat = "1.204.0"
+wast = "204.0.0"
+wasmprinter = "0.204.0"
+wasm-encoder = "0.204.0"
+wasm-smith = "0.204.0"
+wasm-mutate = "0.204.0"
+wit-parser = "0.204.0"
+wit-component = "0.204.0"
 
 # Non-Bytecode Alliance maintained dependencies:
 # --------------------------
diff --git a/cranelift/codegen/meta/src/shared/settings.rs b/cranelift/codegen/meta/src/shared/settings.rs
index f63228014..65ff05be8 100644
--- a/cranelift/codegen/meta/src/shared/settings.rs
+++ b/cranelift/codegen/meta/src/shared/settings.rs
@@ -244,13 +244,6 @@ pub(crate) fn define() -> SettingGroup {
         false,
     );
 
-    settings.add_bool(
-        "probestack_func_adjusts_sp",
-        "Enable if the stack probe adjusts the stack pointer.",
-        "",
-        false,
-    );
-
     settings.add_num(
         "probestack_size_log2",
         "The log2 of the size of the stack guard region.",
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index 98f746a4a..060af446b 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -14,7 +14,7 @@ use crate::settings;
 use crate::{CodegenError, CodegenResult};
 use alloc::boxed::Box;
 use alloc::vec::Vec;
-use regalloc2::{MachineEnv, PReg, PRegSet, VReg};
+use regalloc2::{MachineEnv, PReg, PRegSet};
 use smallvec::{smallvec, SmallVec};
 use std::sync::OnceLock;
 
@@ -35,8 +35,9 @@ static STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
 impl Into<AMode> for StackAMode {
     fn into(self) -> AMode {
         match self {
-            // Argument area begins after saved frame pointer + return address.
-            StackAMode::IncomingArg(off, _) => AMode::FPOffset { off: off + 16 },
+            StackAMode::IncomingArg(off, stack_args_size) => AMode::IncomingArg {
+                off: i64::from(stack_args_size) - off,
+            },
             StackAMode::Slot(off) => AMode::NominalSPOffset { off },
             StackAMode::OutgoingArg(off) => AMode::SPOffset { off },
         }
@@ -630,9 +631,9 @@ impl ABIMachineSpec for AArch64MachineDeps {
             });
         }
 
-        if call_conv == isa::CallConv::Tail && frame_layout.incoming_args_size > 0 {
+        if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {
             insts.extend(Self::gen_sp_reg_adjust(
-                frame_layout.incoming_args_size.try_into().unwrap(),
+                frame_layout.tail_args_size.try_into().unwrap(),
             ));
         }
 
@@ -689,20 +690,50 @@ impl ABIMachineSpec for AArch64MachineDeps {
         flags: &settings::Flags,
         frame_layout: &FrameLayout,
     ) -> SmallVec<[Inst; 16]> {
-        let mut clobbered_int = vec![];
-        let mut clobbered_vec = vec![];
-
-        for &reg in frame_layout.clobbered_callee_saves.iter() {
-            match reg.to_reg().class() {
-                RegClass::Int => clobbered_int.push(reg),
-                RegClass::Float => clobbered_vec.push(reg),
-                RegClass::Vector => unreachable!(),
-            }
-        }
+        let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class();
 
         let mut insts = SmallVec::new();
+        let setup_frame = frame_layout.setup_area_size > 0;
+
+        // When a return_call within this function required more stack arguments than we have
+        // present, resize the incoming argument area of the frame to accommodate those arguments.
+        let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
+        if incoming_args_diff > 0 {
+            // Decrement SP to account for the additional space required by a tail call
+            insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
+
+            // Move fp and lr down
+            if setup_frame {
+                // Reload the frame pointer from the stack
+                insts.push(Inst::ULoad64 {
+                    rd: regs::writable_fp_reg(),
+                    mem: AMode::SPOffset {
+                        off: i64::from(incoming_args_diff),
+                    },
+                    flags: MemFlags::trusted(),
+                });
+
+                // Store the frame pointer and link register again at the new SP
+                insts.push(Inst::StoreP64 {
+                    rt: fp_reg(),
+                    rt2: link_reg(),
+                    mem: PairAMode::SignedOffset {
+                        reg: regs::stack_reg(),
+                        simm7: SImm7Scaled::maybe_from_i64(0, types::I64).unwrap(),
+                    },
+                    flags: MemFlags::trusted(),
+                });
+
+                // Keep the frame pointer in sync
+                insts.push(Self::gen_move(
+                    regs::writable_fp_reg(),
+                    regs::stack_reg(),
+                    types::I64,
+                ));
+            }
+        }
 
-        if flags.unwind_info() && frame_layout.setup_area_size > 0 {
+        if flags.unwind_info() && setup_frame {
             // The *unwind* frame (but not the actual frame) starts at the
             // clobbers, just below the saved FP/LR pair.
             insts.push(Inst::Unwind {
@@ -883,16 +914,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
         frame_layout: &FrameLayout,
     ) -> SmallVec<[Inst; 16]> {
         let mut insts = SmallVec::new();
-        let mut clobbered_int = vec![];
-        let mut clobbered_vec = vec![];
-
-        for &reg in frame_layout.clobbered_callee_saves.iter() {
-            match reg.to_reg().class() {
-                RegClass::Int => clobbered_int.push(reg),
-                RegClass::Float => clobbered_vec.push(reg),
-                RegClass::Vector => unreachable!(),
-            }
-        }
+        let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class();
 
         // Free the fixed frame if necessary.
         let stack_size = frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size;
@@ -1149,7 +1171,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
 
         // Sort registers for deterministic code output. We can do an unstable
         // sort because the registers will be unique (there are no dups).
-        regs.sort_unstable_by_key(|r| VReg::from(r.to_reg()).vreg());
+        regs.sort_unstable();
 
         // Compute clobber size.
         let clobber_size = compute_clobber_size(&regs);
@@ -1261,8 +1283,15 @@ impl AArch64CallSite {
         args: isle::ValueSlice,
         isa_flags: &aarch64_settings::Flags,
     ) {
-        let (new_stack_arg_size, old_stack_arg_size) =
-            self.emit_temporary_tail_call_frame(ctx, args);
+        let new_stack_arg_size =
+            u32::try_from(self.sig(ctx.sigs()).sized_stack_arg_space()).unwrap();
+
+        ctx.abi_mut().accumulate_tail_args_size(new_stack_arg_size);
+
+        // Put all arguments in registers and stack slots (within that newly
+        // allocated stack space).
+        self.emit_args(ctx, args);
+        self.emit_stack_ret_arg_for_tail_call(ctx);
 
         let dest = self.dest().clone();
         let opcode = self.opcode();
@@ -1270,7 +1299,6 @@ impl AArch64CallSite {
         let info = Box::new(ReturnCallInfo {
             uses,
             opcode,
-            old_stack_arg_size,
             new_stack_arg_size,
             key: select_api_key(isa_flags, isa::CallConv::Tail, true),
         });
diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle
index f706f8048..f45814ecc 100644
--- a/cranelift/codegen/src/isa/aarch64/inst.isle
+++ b/cranelift/codegen/src/isa/aarch64/inst.isle
@@ -1170,6 +1170,14 @@
         (Const
           (addr VCodeConstant))
 
+        ;; Offset from the beginning of the argument area to the argument
+        ;; referenced. This can only be determined when the function has been
+        ;; processed fully, as the size of the argument area after the prologue
+        ;; is only known once all return_call instructions in the function body
+        ;; have been processed.
+        (IncomingArg
+          (off i64))
+
         ;; Offset from the "nominal stack pointer", which is where the real SP is
         ;; just after stack and spill slots are allocated in the function prologue.
         ;; At emission time, this is converted to `SPOffset` with a fixup added to
diff --git a/cranelift/codegen/src/isa/aarch64/inst/args.rs b/cranelift/codegen/src/isa/aarch64/inst/args.rs
index 3375215d2..d91ada314 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/args.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/args.rs
@@ -183,6 +183,7 @@ impl AMode {
             | &AMode::SPPostIndexed { .. }
             | &AMode::FPOffset { .. }
             | &AMode::SPOffset { .. }
+            | &AMode::IncomingArg { .. }
             | &AMode::NominalSPOffset { .. }
             | &AMode::Const { .. }
             | AMode::Label { .. } => self.clone(),
@@ -455,6 +456,7 @@ impl PrettyPrint for AMode {
             // Eliminated by `mem_finalize()`.
             &AMode::SPOffset { .. }
             | &AMode::FPOffset { .. }
+            | &AMode::IncomingArg { .. }
             | &AMode::NominalSPOffset { .. }
             | &AMode::RegOffset { .. } => {
                 panic!("Unexpected pseudo mem-arg mode: {:?}", self)
diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
index df840e5b1..73d11a5db 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs
@@ -22,14 +22,27 @@ pub fn mem_finalize(
         &AMode::RegOffset { off, .. }
         | &AMode::SPOffset { off }
         | &AMode::FPOffset { off }
+        | &AMode::IncomingArg { off }
         | &AMode::NominalSPOffset { off } => {
             let basereg = match mem {
                 &AMode::RegOffset { rn, .. } => rn,
-                &AMode::SPOffset { .. } | &AMode::NominalSPOffset { .. } => stack_reg(),
+                &AMode::SPOffset { .. }
+                | &AMode::NominalSPOffset { .. }
+                | &AMode::IncomingArg { .. } => stack_reg(),
                 &AMode::FPOffset { .. } => fp_reg(),
                 _ => unreachable!(),
             };
-            let adj = match mem {
+            let off = match mem {
+                &AMode::IncomingArg { .. } => {
+                    let frame_layout = state.frame_layout();
+                    i64::from(
+                        frame_layout.setup_area_size
+                            + frame_layout.tail_args_size
+                            + frame_layout.clobber_size
+                            + frame_layout.fixed_frame_storage_size
+                            + frame_layout.outgoing_args_size,
+                    ) - off
+                }
                 &AMode::NominalSPOffset { .. } => {
                     trace!(
                         "mem_finalize: nominal SP offset {} + adj {} -> {}",
@@ -37,11 +50,10 @@ pub fn mem_finalize(
                         state.virtual_sp_offset,
                         off + state.virtual_sp_offset
                     );
-                    state.virtual_sp_offset
+                    off + state.virtual_sp_offset
                 }
-                _ => 0,
+                _ => off,
             };
-            let off = off + adj;
 
             if let Some(simm9) = SImm9::maybe_from_i64(off) {
                 let mem = AMode::Unscaled { rn: basereg, simm9 };
@@ -659,6 +671,7 @@ pub struct EmitState {
     /// Only used during fuzz-testing. Otherwise, it is a zero-sized struct and
     /// optimized away at compiletime. See [cranelift_control].
     ctrl_plane: ControlPlane,
+    frame_layout: FrameLayout,
 }
 
 impl MachInstEmitState<Inst> for EmitState {
@@ -668,6 +681,7 @@ impl MachInstEmitState<Inst> for EmitState {
             nominal_sp_to_fp: abi.frame_size() as i64,
             stack_map: None,
             ctrl_plane,
+            frame_layout: abi.frame_layout().clone(),
         }
     }
 
@@ -692,6 +706,10 @@ impl EmitState {
     fn clear_post_insn(&mut self) {
         self.stack_map = None;
     }
+
+    fn frame_layout(&self) -> &FrameLayout {
+        &self.frame_layout
+    }
 }
 
 /// Constant state used during function compilation.
@@ -1103,6 +1121,7 @@ impl MachInstEmit for Inst {
                     // Eliminated by `mem_finalize()` above.
                     &AMode::SPOffset { .. }
                     | &AMode::FPOffset { .. }
+                    | &AMode::IncomingArg { .. }
                     | &AMode::NominalSPOffset { .. }
                     | &AMode::Const { .. }
                     | &AMode::RegOffset { .. } => {
@@ -1197,6 +1216,7 @@ impl MachInstEmit for Inst {
                     // Eliminated by `mem_finalize()` above.
                     &AMode::SPOffset { .. }
                     | &AMode::FPOffset { .. }
+                    | &AMode::IncomingArg { .. }
                     | &AMode::NominalSPOffset { .. }
                     | &AMode::Const { .. }
                     | &AMode::RegOffset { .. } => {
@@ -3778,145 +3798,42 @@ fn emit_return_call_common_sequence(
         let _ = allocs.next(u.vreg);
     }
 
-    // We are emitting a dynamic number of instructions and might need an
-    // island. We emit four instructions regardless of how many stack arguments
-    // we have, and then two instructions per word of stack argument space.
-    let new_stack_words = info.new_stack_arg_size / 8;
-    let insts = 4 + 2 * new_stack_words;
-    let size_of_inst = 4;
-    let space_needed = insts * size_of_inst;
-    if sink.island_needed(space_needed) {
-        let jump_around_label = sink.get_label();
-        let jmp = Inst::Jump {
-            dest: BranchTarget::Label(jump_around_label),
-        };
-        jmp.emit(&[], sink, emit_info, state);
-        sink.emit_island(space_needed + 4, &mut state.ctrl_plane);
-        sink.bind_label(jump_around_label, &mut state.ctrl_plane);
-    }
-
-    // Copy the new frame on top of our current frame.
-    //
-    // The current stack layout is the following:
-    //
-    //            | ...                 |
-    //            +---------------------+
-    //            | ...                 |
-    //            | stack arguments     |
-    //            | ...                 |
-    //    current | return address      |
-    //    frame   | old FP              | <-- FP
-    //            | ...                 |
-    //            | old stack slots     |
-    //            | ...                 |
-    //            +---------------------+
-    //            | ...                 |
-    //    new     | new stack arguments |
-    //    frame   | ...                 | <-- SP
-    //            +---------------------+
-    //
-    // We need to restore the old FP, restore the return address from the stack
-    // to the link register, copy the new stack arguments over the old stack
-    // arguments, adjust SP to point to the new stack arguments, and then jump
-    // to the callee (which will push the old FP and RA again). Note that the
-    // actual jump happens outside this helper function.
-
-    assert_eq!(
-        info.new_stack_arg_size % 8,
-        0,
-        "size of new stack arguments must be 8-byte aligned"
-    );
-
-    // The delta from our frame pointer to the (eventual) stack pointer value
-    // when we jump to the tail callee. This is the difference in size of stack
-    // arguments as well as accounting for the two words we pushed onto the
-    // stack upon entry to this function (the return address and old frame
-    // pointer).
-    let fp_to_callee_sp =
-        i64::from(info.old_stack_arg_size) - i64::from(info.new_stack_arg_size) + 16;
-
-    let tmp1 = regs::writable_spilltmp_reg();
-    let tmp2 = regs::writable_tmp2_reg();
-
-    // Restore the return address to the link register, and load the old FP into
-    // a temporary register.
-    //
-    // We can't put the old FP into the FP register until after we copy the
-    // stack arguments into place, since that uses address modes that are
-    // relative to our current FP.
-    //
-    // Note that the FP is saved in the function prologue for all non-leaf
-    // functions, even when `preserve_frame_pointers=false`. Note also that
-    // `return_call` instructions make it so that a function is considered
-    // non-leaf. Therefore we always have an FP to restore here.
-    Inst::LoadP64 {
-        rt: tmp1,
-        rt2: writable_link_reg(),
-        mem: PairAMode::SignedOffset {
-            reg: regs::fp_reg(),
-            simm7: SImm7Scaled::maybe_from_i64(0, types::I64).unwrap(),
-        },
-        flags: MemFlags::trusted(),
+    for inst in
+        AArch64MachineDeps::gen_clobber_restore(CallConv::Tail, &emit_info.0, state.frame_layout())
+    {
+        inst.emit(&[], sink, emit_info, state);
     }
-    .emit(&[], sink, emit_info, state);
-
-    // Copy the new stack arguments over the old stack arguments.
-    for i in (0..new_stack_words).rev() {
-        // Load the `i`th new stack argument word from the temporary stack
-        // space.
-        Inst::ULoad64 {
-            rd: tmp2,
-            mem: AMode::SPOffset {
-                off: i64::from(i * 8),
-            },
-            flags: ir::MemFlags::trusted(),
-        }
-        .emit(&[], sink, emit_info, state);
 
-        // Store it to its final destination on the stack, overwriting our
-        // current frame.
-        Inst::Store64 {
-            rd: tmp2.to_reg(),
-            mem: AMode::FPOffset {
-                off: fp_to_callee_sp + i64::from(i * 8),
+    let setup_area_size = state.frame_layout().setup_area_size;
+    if setup_area_size > 0 {
+        // N.B.: sp is already adjusted to the appropriate place by the
+        // clobber-restore code (which also frees the fixed frame). Hence, there
+        // is no need for the usual `mov sp, fp` here.
+
+        // `ldp fp, lr, [sp], #16`
+        Inst::LoadP64 {
+            rt: writable_fp_reg(),
+            rt2: writable_link_reg(),
+            mem: PairAMode::SPPostIndexed {
+                // TODO: we could fold the increment for incoming_args_diff here, as long as that
+                // value is less than 502*8, by adding it to `setup_area_size`.
+                // https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/LDP--Load-Pair-of-Registers-
+                simm7: SImm7Scaled::maybe_from_i64(i64::from(setup_area_size), types::I64).unwrap(),
             },
-            flags: ir::MemFlags::trusted(),
+            flags: MemFlags::trusted(),
         }
         .emit(&[], sink, emit_info, state);
     }
 
-    // Initialize the SP for the tail callee, deallocating the temporary stack
-    // argument space and our current frame at the same time.
-    let (off, alu_op) = if let Ok(off) = u64::try_from(fp_to_callee_sp) {
-        (off, ALUOp::Add)
-    } else {
-        let abs = fp_to_callee_sp.abs();
-        let off = u64::try_from(abs).unwrap();
-        (off, ALUOp::Sub)
-    };
-    Inst::AluRRImm12 {
-        alu_op,
-        size: OperandSize::Size64,
-        rd: regs::writable_stack_reg(),
-        rn: regs::fp_reg(),
-        imm12: Imm12::maybe_from_u64(off).unwrap(),
-    }
-    .emit(&[], sink, emit_info, state);
-
-    // Move the old FP value from the temporary into the FP register.
-    Inst::Mov {
-        size: OperandSize::Size64,
-        rd: regs::writable_fp_reg(),
-        rm: tmp1.to_reg(),
+    // Adjust SP to account for the possible over-allocation in the prologue.
+    let incoming_args_diff = state.frame_layout().tail_args_size - info.new_stack_arg_size;
+    if incoming_args_diff > 0 {
+        for inst in
+            AArch64MachineDeps::gen_sp_reg_adjust(i32::try_from(incoming_args_diff).unwrap())
+        {
+            inst.emit(&[], sink, emit_info, state);
+        }
     }
-    .emit(&[], sink, emit_info, state);
-
-    state.virtual_sp_offset -= i64::from(info.new_stack_arg_size);
-    trace!(
-        "return_call[_ind] adjusts virtual sp offset by {} -> {}",
-        info.new_stack_arg_size,
-        state.virtual_sp_offset
-    );
 
     if let Some(key) = info.key {
         sink.put4(key.enc_auti_hint());
diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
index e1447d60a..9cc2a5740 100644
--- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs
@@ -130,8 +130,6 @@ pub struct ReturnCallInfo {
     pub uses: CallArgList,
     /// Instruction opcode.
     pub opcode: Opcode,
-    /// The size of the current/old stack frame's stack arguments.
-    pub old_stack_arg_size: u32,
     /// 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.
@@ -413,7 +411,7 @@ fn memarg_operands<F: Fn(VReg) -> VReg>(memarg: &AMode, collector: &mut OperandC
         }
         &AMode::Label { .. } => {}
         &AMode::SPPreIndexed { .. } | &AMode::SPPostIndexed { .. } => {}
-        &AMode::FPOffset { .. } => {}
+        &AMode::FPOffset { .. } | &AMode::IncomingArg { .. } => {}
         &AMode::SPOffset { .. } | &AMode::NominalSPOffset { .. } => {}
         &AMode::RegOffset { rn, .. } => {
             collector.reg_use(rn);
@@ -2615,8 +2613,8 @@ impl Inst {
                 ref info,
             } => {
                 let mut s = format!(
-                    "return_call {callee:?} old_stack_arg_size:{} new_stack_arg_size:{}",
-                    info.old_stack_arg_size, info.new_stack_arg_size
+                    "return_call {callee:?} new_stack_arg_size:{}",
+                    info.new_stack_arg_size
                 );
                 for ret in &info.uses {
                     let preg = pretty_print_reg(ret.preg, &mut empty_allocs);
@@ -2628,8 +2626,8 @@ impl Inst {
             &Inst::ReturnCallInd { callee, ref info } => {
                 let callee = pretty_print_reg(callee, allocs);
                 let mut s = format!(
-                    "return_call_ind {callee} old_stack_arg_size:{} new_stack_arg_size:{}",
-                    info.old_stack_arg_size, info.new_stack_arg_size
+                    "return_call_ind {callee} new_stack_arg_size:{}",
+                    info.new_stack_arg_size
                 );
                 for ret in &info.uses {
                     let preg = pretty_print_reg(ret.preg, &mut empty_allocs);
diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
index 0f50361a7..36eda61e6 100644
--- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
@@ -100,9 +100,7 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
             self.lower_ctx.sigs(),
             callee_sig,
             &callee,
-            // TODO: this should be Opcode::ReturnCall, once aarch64 has been ported to the new
-            // tail call strategy.
-            Opcode::Call,
+            Opcode::ReturnCall,
             distance,
             caller_conv,
             self.backend.flags().clone(),
@@ -131,9 +129,7 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
             self.lower_ctx.sigs(),
             callee_sig,
             callee,
-            // TODO: this should be Opcode::ReturnCallIndirect, once aarch64 has
-            // been ported to the new tail call strategy.
-            Opcode::CallIndirect,
+            Opcode::ReturnCallIndirect,
             caller_conv,
             self.backend.flags().clone(),
         );
diff --git a/cranelift/codegen/src/isa/aarch64/pcc.rs b/cranelift/codegen/src/isa/aarch64/pcc.rs
index cdc6ca42e..e8e38232b 100644
--- a/cranelift/codegen/src/isa/aarch64/pcc.rs
+++ b/cranelift/codegen/src/isa/aarch64/pcc.rs
@@ -505,6 +505,7 @@ fn check_addr<'a>(
         }
         &AMode::SPOffset { .. }
         | &AMode::FPOffset { .. }
+        | &AMode::IncomingArg { .. }
         | &AMode::NominalSPOffset { .. }
         | &AMode::SPPostIndexed { .. }
         | &AMode::SPPreIndexed { .. } => {
diff --git a/cranelift/codegen/src/isa/riscv64/abi.rs b/cranelift/codegen/src/isa/riscv64/abi.rs
index 12ffc9661..ee9869904 100644
--- a/cranelift/codegen/src/isa/riscv64/abi.rs
+++ b/cranelift/codegen/src/isa/riscv64/abi.rs
@@ -491,7 +491,7 @@ impl ABIMachineSpec for Riscv64MachineDeps {
                 }
                 insts.push(Inst::gen_store(
                     AMode::SPOffset(-(cur_offset as i64)),
-                    real_reg_to_reg(reg.to_reg()),
+                    Reg::from(reg.to_reg()),
                     ty,
                     MemFlags::trusted(),
                 ));
@@ -530,7 +530,7 @@ impl ABIMachineSpec for Riscv64MachineDeps {
                 RegClass::Vector => unimplemented!("Vector Clobber Restores"),
             };
             insts.push(Inst::gen_load(
-                Writable::from_reg(real_reg_to_reg(reg.to_reg())),
+                reg.map(Reg::from),
                 AMode::SPOffset(-cur_offset),
                 ty,
                 MemFlags::trusted(),
@@ -694,7 +694,7 @@ impl ABIMachineSpec for Riscv64MachineDeps {
             .filter(|r| is_reg_saved_in_prologue(call_conv, r.to_reg()))
             .collect();
 
-        regs.sort();
+        regs.sort_unstable();
 
         // Compute clobber size.
         let clobber_size = compute_clobber_size(&regs);
diff --git a/cranelift/codegen/src/isa/riscv64/inst/regs.rs b/cranelift/codegen/src/isa/riscv64/inst/regs.rs
index d17e19b37..ffdc484a0 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/regs.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/regs.rs
@@ -3,12 +3,10 @@
 
 use crate::machinst::{Reg, Writable};
 
-use crate::machinst::RealReg;
 use alloc::vec;
 use alloc::vec::Vec;
 
-use regalloc2::VReg;
-use regalloc2::{PReg, RegClass};
+use regalloc2::{PReg, RegClass, VReg};
 
 // first argument of function call
 #[inline]
@@ -155,11 +153,6 @@ pub fn f_reg(enc: usize) -> Reg {
 pub const fn pf_reg(enc: usize) -> PReg {
     PReg::new(enc, RegClass::Float)
 }
-#[inline]
-pub(crate) fn real_reg_to_reg(x: RealReg) -> Reg {
-    let v_reg = VReg::new(x.hw_enc() as usize, x.class());
-    Reg::from(v_reg)
-}
 
 #[allow(dead_code)]
 pub(crate) fn x_reg_range(start: usize, end: usize) -> Vec<Writable<Reg>> {
diff --git a/cranelift/codegen/src/isa/s390x/abi.rs b/cranelift/codegen/src/isa/s390x/abi.rs
index bbed8485c..321ec1e5d 100644
--- a/cranelift/codegen/src/isa/s390x/abi.rs
+++ b/cranelift/codegen/src/isa/s390x/abi.rs
@@ -79,7 +79,7 @@ use crate::machinst::*;
 use crate::settings;
 use crate::{CodegenError, CodegenResult};
 use alloc::vec::Vec;
-use regalloc2::{MachineEnv, PReg, PRegSet};
+use regalloc2::{MachineEnv, PRegSet};
 use smallvec::{smallvec, SmallVec};
 use std::sync::OnceLock;
 
@@ -599,8 +599,7 @@ impl ABIMachineSpec for S390xMachineDeps {
         let mut insts = SmallVec::new();
 
         // Collect clobbered registers.
-        let (first_clobbered_gpr, clobbered_fpr) =
-            get_clobbered_gpr_fpr(&frame_layout.clobbered_callee_saves);
+        let (first_clobbered_gpr, clobbered_fpr) = get_clobbered_gpr_fpr(frame_layout);
         if flags.unwind_info() {
             insts.push(Inst::Unwind {
                 inst: UnwindInst::DefineNewFrame {
@@ -696,8 +695,7 @@ impl ABIMachineSpec for S390xMachineDeps {
         let mut insts = SmallVec::new();
 
         // Collect clobbered registers.
-        let (first_clobbered_gpr, clobbered_fpr) =
-            get_clobbered_gpr_fpr(&frame_layout.clobbered_callee_saves);
+        let (first_clobbered_gpr, clobbered_fpr) = get_clobbered_gpr_fpr(frame_layout);
 
         // Restore FPRs.
         for (i, reg) in clobbered_fpr.iter().enumerate() {
@@ -851,7 +849,7 @@ impl ABIMachineSpec for S390xMachineDeps {
 
         // Sort registers for deterministic code output. We can do an unstable
         // sort because the registers will be unique (there are no dups).
-        regs.sort_unstable_by_key(|r| PReg::from(r.to_reg()).index());
+        regs.sort_unstable();
 
         // Compute clobber size.  We only need to count FPR save slots.
         let mut clobber_size = 0;
@@ -892,28 +890,18 @@ fn is_reg_saved_in_prologue(_call_conv: isa::CallConv, r: RealReg) -> bool {
     }
 }
 
-fn get_clobbered_gpr_fpr(
-    clobbered_callee_saves: &[Writable<RealReg>],
-) -> (u8, SmallVec<[Writable<RealReg>; 8]>) {
+fn get_clobbered_gpr_fpr(frame_layout: &FrameLayout) -> (u8, &[Writable<RealReg>]) {
     // Collect clobbered registers.  Note we save/restore GPR always as
     // a block of registers using LOAD MULTIPLE / STORE MULTIPLE, starting
     // with the clobbered GPR with the lowest number up to %r15.  We
     // return the number of that first GPR (or 16 if none is to be saved).
-    let mut clobbered_fpr = SmallVec::new();
-    let mut first_clobbered_gpr = 16;
-
-    for &reg in clobbered_callee_saves.iter() {
-        match reg.to_reg().class() {
-            RegClass::Int => {
-                let enc = reg.to_reg().hw_enc();
-                if enc < first_clobbered_gpr {
-                    first_clobbered_gpr = enc;
-                }
-            }
-            RegClass::Float => clobbered_fpr.push(reg),
-            RegClass::Vector => unreachable!(),
-        }
-    }
+    let (clobbered_gpr, clobbered_fpr) = frame_layout.clobbered_callee_saves_by_class();
+
+    let first_clobbered_gpr = clobbered_gpr.split_first().map_or(16, |(first, rest)| {
+        let first = first.to_reg().hw_enc();
+        debug_assert!(rest.iter().all(|r| r.to_reg().hw_enc() > first));
+        first
+    });
 
     (first_clobbered_gpr, clobbered_fpr)
 }
diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs
index 08ed89575..4f13df1de 100644
--- a/cranelift/codegen/src/isa/x64/abi.rs
+++ b/cranelift/codegen/src/isa/x64/abi.rs
@@ -11,7 +11,7 @@ use crate::{CodegenError, CodegenResult};
 use alloc::boxed::Box;
 use alloc::vec::Vec;
 use args::*;
-use regalloc2::{MachineEnv, PReg, PRegSet, VReg};
+use regalloc2::{MachineEnv, PReg, PRegSet};
 use smallvec::{smallvec, SmallVec};
 use std::sync::OnceLock;
 
@@ -999,7 +999,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
         };
         // Sort registers for deterministic code output. We can do an unstable sort because the
         // registers will be unique (there are no dups).
-        regs.sort_unstable_by_key(|r| VReg::from(r.to_reg()).vreg());
+        regs.sort_unstable();
 
         // Compute clobber size.
         let clobber_size = compute_clobber_size(&regs);
@@ -1037,6 +1037,7 @@ impl X64CallSite {
         let info = Box::new(ReturnCallInfo {
             new_stack_arg_size,
             uses: self.take_uses(),
+            tmp: ctx.temp_writable_gpr(),
         });
         match dest {
             CallDest::ExtName(callee, RelocDistance::Near) => {
diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs
index 1206ba27d..149fa03b8 100644
--- a/cranelift/codegen/src/isa/x64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/x64/inst/emit.rs
@@ -4238,6 +4238,8 @@ fn emit_return_call_common_sequence(
                  but the current implementation relies on them being present"
     );
 
+    let tmp = allocs.next_writable(call_info.tmp.to_writable_reg());
+
     for u in call_info.uses.iter() {
         let _ = allocs.next(u.vreg);
     }
@@ -4260,14 +4262,10 @@ fn emit_return_call_common_sequence(
     let incoming_args_diff = state.frame_layout().tail_args_size - call_info.new_stack_arg_size;
     if incoming_args_diff > 0 {
         // Move the saved return address up by `incoming_args_diff`
-        Inst::mov64_m_r(
-            Amode::imm_reg(0, regs::rsp()),
-            Writable::from_reg(regs::r11()),
-        )
-        .emit(&[], sink, info, state);
+        Inst::mov64_m_r(Amode::imm_reg(0, regs::rsp()), tmp).emit(&[], sink, info, state);
         Inst::mov_r_m(
             OperandSize::Size64,
-            regs::r11(),
+            tmp.to_reg(),
             Amode::imm_reg(i32::try_from(incoming_args_diff).unwrap(), regs::rsp()),
         )
         .emit(&[], sink, info, state);
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index f57a8011f..82a6b1d22 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -58,6 +58,9 @@ pub struct ReturnCallInfo {
 
     /// The in-register arguments and their constraints.
     pub uses: CallArgList,
+
+    /// A temporary for use when moving the return address.
+    pub tmp: WritableGpr,
 }
 
 #[test]
@@ -1668,8 +1671,11 @@ impl PrettyPrint for Inst {
                 let ReturnCallInfo {
                     uses,
                     new_stack_arg_size,
+                    tmp,
                 } = &**info;
-                let mut s = format!("return_call_known {callee:?} ({new_stack_arg_size})");
+                let tmp = pretty_print_reg(tmp.to_reg().to_reg(), 8, allocs);
+                let mut s =
+                    format!("return_call_known {callee:?} ({new_stack_arg_size}) tmp={tmp}");
                 for ret in uses {
                     let preg = regs::show_reg(ret.preg);
                     let vreg = pretty_print_reg(ret.vreg, 8, allocs);
@@ -1682,9 +1688,12 @@ impl PrettyPrint for Inst {
                 let ReturnCallInfo {
                     uses,
                     new_stack_arg_size,
+                    tmp,
                 } = &**info;
                 let callee = callee.pretty_print(8, allocs);
-                let mut s = format!("return_call_unknown {callee} ({new_stack_arg_size})");
+                let tmp = pretty_print_reg(tmp.to_reg().to_reg(), 8, allocs);
+                let mut s =
+                    format!("return_call_unknown {callee} ({new_stack_arg_size}) tmp={tmp}");
                 for ret in uses {
                     let preg = regs::show_reg(ret.preg);
                     let vreg = pretty_print_reg(ret.vreg, 8, allocs);
@@ -2342,7 +2351,8 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         }
 
         Inst::ReturnCallKnown { callee, info } => {
-            let ReturnCallInfo { uses, .. } = &**info;
+            let ReturnCallInfo { uses, tmp, .. } = &**info;
+            collector.reg_early_def(tmp.to_writable_reg());
             // Same as in the `Inst::CallKnown` branch.
             debug_assert_ne!(*callee, ExternalName::LibCall(LibCall::Probestack));
             for u in uses {
@@ -2351,8 +2361,9 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         }
 
         Inst::ReturnCallUnknown { callee, info } => {
-            let ReturnCallInfo { uses, .. } = &**info;
+            let ReturnCallInfo { uses, tmp, .. } = &**info;
             callee.get_operands(collector);
+            collector.reg_early_def(tmp.to_writable_reg());
             for u in uses {
                 collector.reg_fixed_use(u.vreg, u.preg);
             }
diff --git a/cranelift/codegen/src/isa/x64/pcc.rs b/cranelift/codegen/src/isa/x64/pcc.rs
index acf8e54c2..bdae2d57a 100644
--- a/cranelift/codegen/src/isa/x64/pcc.rs
+++ b/cranelift/codegen/src/isa/x64/pcc.rs
@@ -374,7 +374,12 @@ pub(crate) fn check(
             let addr = addr.clone();
             let bits: u16 = size.to_bits().into();
             check_output(ctx, vcode, dst.to_writable_reg(), &[], |vcode| {
-                clamp_range(ctx, 64, bits, compute_addr(ctx, vcode, &addr, bits))
+                let fact = if let SyntheticAmode::Real(amode) = &addr {
+                    compute_addr(ctx, vcode, amode, bits)
+                } else {
+                    None
+                };
+                clamp_range(ctx, 64, bits, fact)
             })
         }
 
@@ -922,15 +927,12 @@ fn check_mem<'a>(
     ty: Type,
     op: LoadOrStore<'a>,
 ) -> PccResult<Option<Fact>> {
-    match amode {
-        SyntheticAmode::Real(amode) if !amode.get_flags().checked() => return Ok(None),
-        SyntheticAmode::IncomingArg { .. }
-        | SyntheticAmode::NominalSPOffset { .. }
-        | SyntheticAmode::ConstantOffset(_) => return Ok(None),
-        _ => {}
-    }
-
-    let addr = compute_addr(ctx, vcode, amode, 64).ok_or(PccError::MissingFact)?;
+    let addr = match amode {
+        SyntheticAmode::Real(amode) if amode.get_flags().checked() => {
+            compute_addr(ctx, vcode, amode, 64).ok_or(PccError::MissingFact)?
+        }
+        _ => return Ok(None),
+    };
 
     match op {
         LoadOrStore::Load {
@@ -957,15 +959,10 @@ fn check_mem<'a>(
     }
 }
 
-fn compute_addr(
-    ctx: &FactContext,
-    vcode: &VCode<Inst>,
-    amode: &SyntheticAmode,
-    bits: u16,
-) -> Option<Fact> {
+fn compute_addr(ctx: &FactContext, vcode: &VCode<Inst>, amode: &Amode, bits: u16) -> Option<Fact> {
     trace!("compute_addr: {:?}", amode);
     match *amode {
-        SyntheticAmode::Real(Amode::ImmReg { simm32, base, .. }) => {
+        Amode::ImmReg { simm32, base, .. } => {
             let base = get_fact_or_default(vcode, base, bits);
             trace!("base = {:?}", base);
             let simm32: i64 = simm32.into();
@@ -975,13 +972,13 @@ fn compute_addr(
             trace!("sum = {:?}", sum);
             Some(sum)
         }
-        SyntheticAmode::Real(Amode::ImmRegRegShift {
+        Amode::ImmRegRegShift {
             simm32,
             base,
             index,
             shift,
             ..
-        }) => {
+        } => {
             let base = get_fact_or_default(vcode, base.into(), bits);
             let index = get_fact_or_default(vcode, index.into(), bits);
             trace!("base = {:?} index = {:?}", base, index);
@@ -994,9 +991,6 @@ fn compute_addr(
             trace!("sum = {:?}", sum);
             Some(sum)
         }
-        SyntheticAmode::Real(Amode::RipRelative { .. })
-        | SyntheticAmode::IncomingArg { .. }
-        | SyntheticAmode::ConstantOffset(_)
-        | SyntheticAmode::NominalSPOffset { .. } => None,
+        Amode::RipRelative { .. } => None,
     }
 }
diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs
index b9ef2af6f..b4edf0323 100644
--- a/cranelift/codegen/src/machinst/abi.rs
+++ b/cranelift/codegen/src/machinst/abi.rs
@@ -1055,6 +1055,22 @@ pub struct FrameLayout {
     pub clobbered_callee_saves: Vec<Writable<RealReg>>,
 }
 
+impl FrameLayout {
+    /// Split the clobbered callee-save registers into integer-class and
+    /// float-class groups.
+    ///
+    /// This method does not currently support vector-class callee-save
+    /// registers because no current backend has them.
+    pub fn clobbered_callee_saves_by_class(&self) -> (&[Writable<RealReg>], &[Writable<RealReg>]) {
+        let (ints, floats) = self.clobbered_callee_saves.split_at(
+            self.clobbered_callee_saves
+                .partition_point(|r| r.to_reg().class() == RegClass::Int),
+        );
+        debug_assert!(floats.iter().all(|r| r.to_reg().class() == RegClass::Float));
+        (ints, floats)
+    }
+}
+
 /// ABI object for a function body.
 pub struct Callee<M: ABIMachineSpec> {
     /// CLIF-level signature, possibly normalized.
@@ -1108,9 +1124,6 @@ pub struct Callee<M: ABIMachineSpec> {
     /// manually register-allocated and carefully only use caller-saved
     /// registers and keep nothing live after this sequence of instructions.
     stack_limit: Option<(Reg, SmallInstVec<M::I>)>,
-    /// Are we to invoke the probestack function in the prologue? If so,
-    /// what is the minimum size at which we must invoke it?
-    probestack_min_frame: Option<u32>,
 
     _mach: PhantomData<M>,
 }
@@ -1218,18 +1231,6 @@ impl<M: ABIMachineSpec> Callee<M> {
                         .map(|gv| gen_stack_limit::<M>(f, sigs, sig, gv))
                 });
 
-        // Determine whether a probestack call is required for large enough
-        // frames (and the minimum frame size if so).
-        let probestack_min_frame = if flags.enable_probestack() {
-            assert!(
-                !flags.probestack_func_adjusts_sp(),
-                "SP-adjusting probestack not supported in new backends"
-            );
-            Some(1 << flags.probestack_size_log2())
-        } else {
-            None
-        };
-
         let tail_args_size = sigs[sig].sized_stack_arg_space;
 
         Ok(Self {
@@ -1252,7 +1253,6 @@ impl<M: ABIMachineSpec> Callee<M> {
             isa_flags: isa_flags.clone(),
             is_leaf: f.is_leaf(),
             stack_limit,
-            probestack_min_frame,
             _mach: PhantomData,
         })
     }
@@ -1890,22 +1890,20 @@ impl<M: ABIMachineSpec> Callee<M> {
                 self.insert_stack_check(*reg, total_stacksize, &mut insts);
             }
 
-            let needs_probestack = self
-                .probestack_min_frame
-                .map_or(false, |min_frame| total_stacksize >= min_frame);
-
-            if needs_probestack {
-                match self.flags.probestack_strategy() {
-                    ProbestackStrategy::Inline => {
-                        let guard_size = 1 << self.flags.probestack_size_log2();
-                        M::gen_inline_probestack(
+            if self.flags.enable_probestack() {
+                let guard_size = 1 << self.flags.probestack_size_log2();
+                if total_stacksize >= guard_size {
+                    match self.flags.probestack_strategy() {
+                        ProbestackStrategy::Inline => M::gen_inline_probestack(
                             &mut insts,
                             self.call_conv,
                             total_stacksize,
                             guard_size,
-                        )
+                        ),
+                        ProbestackStrategy::Outline => {
+                            M::gen_probestack(&mut insts, total_stacksize)
+                        }
                     }
-                    ProbestackStrategy::Outline => M::gen_probestack(&mut insts, total_stacksize),
                 }
             }
         }
@@ -2311,11 +2309,6 @@ impl<M: ABIMachineSpec> CallSite<M> {
                 }),
                 ArgLoc::Stack { offset, ty } => {
                     let amode = if self.is_tail_call() {
-                        assert!(
-                            self.flags.preserve_frame_pointers(),
-                            "tail calls require frame pointers to be enabled"
-                        );
-
                         StackAMode::IncomingArg(offset, ctx.sigs()[self.sig].sized_stack_arg_space)
                     } else {
                         StackAMode::OutgoingArg(offset)
diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs
index 4e21377d2..cd5e5f11c 100644
--- a/cranelift/codegen/src/machinst/reg.rs
+++ b/cranelift/codegen/src/machinst/reg.rs
@@ -53,11 +53,7 @@ impl Reg {
     /// Get the physical register (`RealReg`), if this register is
     /// one.
     pub fn to_real_reg(self) -> Option<RealReg> {
-        if pinned_vreg_to_preg(self.0).is_some() {
-            Some(RealReg(self.0))
-        } else {
-            None
-        }
+        pinned_vreg_to_preg(self.0).map(RealReg)
     }
 
     /// Get the virtual (non-physical) register, if this register is
@@ -104,7 +100,7 @@ impl std::fmt::Debug for Reg {
 /// ISA's named registers and can be used as an instruction operand.
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
-pub struct RealReg(VReg);
+pub struct RealReg(PReg);
 
 impl RealReg {
     /// Get the class of this register.
@@ -114,7 +110,7 @@ impl RealReg {
 
     /// The physical register number.
     pub fn hw_enc(self) -> u8 {
-        PReg::from(self).hw_enc() as u8
+        self.0.hw_enc() as u8
     }
 }
 
@@ -189,7 +185,7 @@ impl<T: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> Writabl
     }
 }
 
-// Conversions between regalloc2 types (VReg) and our types
+// Conversions between regalloc2 types (VReg, PReg) and our types
 // (VirtualReg, RealReg, Reg).
 
 impl std::convert::From<regalloc2::VReg> for Reg {
@@ -205,13 +201,6 @@ impl std::convert::From<regalloc2::VReg> for VirtualReg {
     }
 }
 
-impl std::convert::From<regalloc2::VReg> for RealReg {
-    fn from(vreg: regalloc2::VReg) -> RealReg {
-        debug_assert!(pinned_vreg_to_preg(vreg).is_some());
-        RealReg(vreg)
-    }
-}
-
 impl std::convert::From<Reg> for regalloc2::VReg {
     /// Extract the underlying `regalloc2::VReg`. Note that physical
     /// registers also map to particular (special) VRegs, so this
@@ -234,31 +223,33 @@ impl std::convert::From<VirtualReg> for regalloc2::VReg {
 
 impl std::convert::From<RealReg> for regalloc2::VReg {
     fn from(reg: RealReg) -> regalloc2::VReg {
-        reg.0
+        // This representation is redundant: the class is implied in the vreg
+        // index as well as being in the vreg class field.
+        VReg::new(reg.0.index(), reg.0.class())
     }
 }
 
 impl std::convert::From<RealReg> for regalloc2::PReg {
     fn from(reg: RealReg) -> regalloc2::PReg {
-        PReg::from_index(reg.0.vreg())
+        reg.0
     }
 }
 
 impl std::convert::From<regalloc2::PReg> for RealReg {
     fn from(preg: regalloc2::PReg) -> RealReg {
-        RealReg(VReg::new(preg.index(), preg.class()))
+        RealReg(preg)
     }
 }
 
 impl std::convert::From<regalloc2::PReg> for Reg {
     fn from(preg: regalloc2::PReg) -> Reg {
-        Reg(VReg::new(preg.index(), preg.class()))
+        RealReg(preg).into()
     }
 }
 
 impl std::convert::From<RealReg> for Reg {
     fn from(reg: RealReg) -> Reg {
-        Reg(reg.0)
+        Reg(reg.into())
     }
 }
 
diff --git a/cranelift/codegen/src/settings.rs b/cranelift/codegen/src/settings.rs
index 26ae6bc3e..0672cb093 100644
--- a/cranelift/codegen/src/settings.rs
+++ b/cranelift/codegen/src/settings.rs
@@ -532,7 +532,6 @@ unwind_info = true
 preserve_frame_pointers = false
 machine_code_cfg_info = false
 enable_probestack = false
-probestack_func_adjusts_sp = false
 enable_jump_tables = true
 enable_heap_access_spectre_mitigation = true
 enable_table_access_spectre_mitigation = true
diff --git a/cranelift/docs/ir.md b/cranelift/docs/ir.md
index 701cc0cce..9ba944c63 100644
--- a/cranelift/docs/ir.md
+++ b/cranelift/docs/ir.md
@@ -28,8 +28,7 @@ each other directly.
 This is a simple C function that computes the average of an array of floats:
 
 ```c
-float
-average(const float *array, size_t count)
+float average(const float *array, size_t count)
 {
     double sum = 0;
     for (size_t i = 0; i < count; i++)
diff --git a/cranelift/wasm/src/environ/dummy.rs b/cranelift/wasm/src/environ/dummy.rs
index 1a2c10479..4b5bdb8e7 100644
--- a/cranelift/wasm/src/environ/dummy.rs
+++ b/cranelift/wasm/src/environ/dummy.rs
@@ -901,12 +901,6 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
     }
 
     fn wasm_features(&self) -> WasmFeatures {
-        WasmFeatures {
-            multi_value: true,
-            simd: true,
-            reference_types: true,
-            bulk_memory: true,
-            ..WasmFeatures::default()
-        }
+        WasmFeatures::default()
     }
 }
diff --git a/crates/environ/examples/factc.rs b/crates/environ/examples/factc.rs
index d0f49b547..979e617b4 100644
--- a/crates/environ/examples/factc.rs
+++ b/crates/environ/examples/factc.rs
@@ -120,10 +120,7 @@ impl Factc {
 
         let mut adapters = Vec::new();
         let input = wat::parse_file(&self.input)?;
-        let mut validator = Validator::new_with_features(WasmFeatures {
-            component_model: true,
-            ..Default::default()
-        });
+        let mut validator = Validator::new();
         let wasm_types = validator
             .validate_all(&input)
             .context("failed to validate input wasm")?;
@@ -191,13 +188,9 @@ impl Factc {
         }
 
         if !self.skip_validate {
-            Validator::new_with_features(WasmFeatures {
-                multi_memory: true,
-                memory64: true,
-                ..WasmFeatures::default()
-            })
-            .validate_all(&wasm)
-            .context("failed to validate generated module")?;
+            Validator::new_with_features(WasmFeatures::default() | WasmFeatures::MEMORY64)
+                .validate_all(&wasm)
+                .context("failed to validate generated module")?;
         }
 
         Ok(())
diff --git a/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs b/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs
index b43f8cda6..d6b323285 100644
--- a/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs
+++ b/crates/environ/fuzz/fuzz_targets/fact-valid-module.rs
@@ -87,10 +87,7 @@ fn target(module: GenAdapterModule) {
         );
         let wasm = wat::parse_str(&wat).unwrap();
 
-        let mut validator = Validator::new_with_features(WasmFeatures {
-            component_model: true,
-            ..Default::default()
-        });
+        let mut validator = Validator::new();
 
         types.push_type_scope();
         for payload in Parser::new(0).parse_all(&wasm) {
@@ -148,12 +145,8 @@ fn target(module: GenAdapterModule) {
         fact_module.adapt(&format!("adapter{i}"), adapter);
     }
     let wasm = fact_module.encode();
-    let result = Validator::new_with_features(WasmFeatures {
-        multi_memory: true,
-        memory64: true,
-        ..WasmFeatures::default()
-    })
-    .validate_all(&wasm);
+    let result = Validator::new_with_features(WasmFeatures::default() | WasmFeatures::MEMORY64)
+        .validate_all(&wasm);
 
     let err = match result {
         Ok(_) => return,
diff --git a/crates/environ/src/component/translate/adapt.rs b/crates/environ/src/component/translate/adapt.rs
index 7b36fb102..d112fa808 100644
--- a/crates/environ/src/component/translate/adapt.rs
+++ b/crates/environ/src/component/translate/adapt.rs
@@ -212,10 +212,9 @@ impl<'data> Translator<'_, 'data> {
             // specifically enabled here since the adapter module is highly
             // likely to use that if anything is actually indirected through
             // memory.
-            let mut validator = Validator::new_with_features(WasmFeatures {
-                multi_memory: true,
-                ..*self.validator.features()
-            });
+            let mut validator = Validator::new_with_features(
+                *self.validator.features() | WasmFeatures::MULTI_MEMORY,
+            );
             let translation = ModuleEnvironment::new(
                 self.tunables,
                 &mut validator,
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index fe81cac53..77e96db9c 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -19,7 +19,7 @@ use wasmparser::types::{CoreTypeId, Types};
 use wasmparser::{
     CompositeType, CustomSectionReader, DataKind, ElementItems, ElementKind, Encoding,
     ExternalKind, FuncToValidate, FunctionBody, NameSectionReader, Naming, Operator, Parser,
-    Payload, TypeRef, Validator, ValidatorResources,
+    Payload, TypeRef, Validator, ValidatorResources, WasmFeatures,
 };
 use wasmtime_types::ModuleInternedTypeIndex;
 
@@ -605,7 +605,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
                             params: sig.params().into(),
                         });
                 }
-                body.allow_memarg64(self.validator.features().memory64);
+                body.allow_memarg64(self.validator.features().contains(WasmFeatures::MEMORY64));
                 self.result
                     .function_body_inputs
                     .push(FunctionBodyData { validator, body });
diff --git a/crates/fuzzing/src/generators/table_ops.rs b/crates/fuzzing/src/generators/table_ops.rs
index ee770c1a9..7e879367c 100644
--- a/crates/fuzzing/src/generators/table_ops.rs
+++ b/crates/fuzzing/src/generators/table_ops.rs
@@ -283,11 +283,7 @@ mod tests {
             let u = Unstructured::new(&buf);
             if let Ok(ops) = TableOps::arbitrary_take_rest(u) {
                 let wasm = ops.to_wasm_binary();
-                let mut validator =
-                    wasmparser::Validator::new_with_features(wasmparser::WasmFeatures {
-                        reference_types: true,
-                        ..Default::default()
-                    });
+                let mut validator = wasmparser::Validator::new();
                 let result = validator.validate_all(&wasm);
                 assert!(result.is_ok());
             }
diff --git a/crates/runtime/src/mmap_vec.rs b/crates/runtime/src/mmap_vec.rs
index 8710a23a3..1b089fc03 100644
--- a/crates/runtime/src/mmap_vec.rs
+++ b/crates/runtime/src/mmap_vec.rs
@@ -68,28 +68,6 @@ impl MmapVec {
         Ok(MmapVec::new(mmap, len))
     }
 
-    /// Splits the collection into two at the given index.
-    ///
-    /// Returns a separate `MmapVec` which shares the underlying mapping, but
-    /// only has access to elements in the range `[at, len)`. After the call,
-    /// the original `MmapVec` will be left with access to the elements in the
-    /// range `[0, at)`.
-    ///
-    /// This is an `O(1)` operation which does not involve copies.
-    pub fn split_off(&mut self, at: usize) -> MmapVec {
-        assert!(at <= self.range.len());
-
-        // Create a new `MmapVec` which refers to the same underlying mmap, but
-        // has a disjoint range from ours. Our own range is adjusted to be
-        // disjoint just after `ret` is created.
-        let ret = MmapVec {
-            mmap: self.mmap.clone(),
-            range: at..self.range.end,
-        };
-        self.range.end = self.range.start + at;
-        return ret;
-    }
-
     /// Makes the specified `range` within this `mmap` to be read/execute.
     pub unsafe fn make_executable(
         &self,
@@ -176,26 +154,4 @@ mod tests {
         assert_eq!(mmap[0], 1);
         assert_eq!(mmap[2], 3);
     }
-
-    #[test]
-    fn split_off() {
-        let mut vec = Vec::from([1, 2, 3, 4]);
-        let mut mmap = MmapVec::from_slice(&vec).unwrap();
-        assert_eq!(&mmap[..], &vec[..]);
-        // remove nothing; vec length remains 4
-        assert_eq!(&mmap.split_off(4)[..], &vec.split_off(4)[..]);
-        assert_eq!(&mmap[..], &vec[..]);
-        // remove 1 element; vec length is now 3
-        assert_eq!(&mmap.split_off(3)[..], &vec.split_off(3)[..]);
-        assert_eq!(&mmap[..], &vec[..]);
-        // remove 2 elements; vec length is now 1
-        assert_eq!(&mmap.split_off(1)[..], &vec.split_off(1)[..]);
-        assert_eq!(&mmap[..], &vec[..]);
-        // remove last element; vec length is now 0
-        assert_eq!(&mmap.split_off(0)[..], &vec.split_off(0)[..]);
-        assert_eq!(&mmap[..], &vec[..]);
-        // nothing left to remove, but that's okay
-        assert_eq!(&mmap.split_off(0)[..], &vec.split_off(0)[..]);
-        assert_eq!(&mmap[..], &vec[..]);
-    }
 }
diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs
index 467304355..28ab76863 100644
--- a/crates/wasi-http/src/types.rs
+++ b/crates/wasi-http/src/types.rs
@@ -140,11 +140,17 @@ pub fn default_send_request(
     request: hyper::Request<HyperOutgoingBody>,
     config: OutgoingRequestConfig,
 ) -> HostFutureIncomingResponse {
-    let handle = wasmtime_wasi::runtime::spawn(async move { Ok(handler(request, config).await) });
+    let handle = wasmtime_wasi::runtime::spawn(async move {
+        Ok(default_send_request_handler(request, config).await)
+    });
     HostFutureIncomingResponse::pending(handle)
 }
 
-async fn handler(
+/// The underlying implementation of how an outgoing request is sent. This should likely be spawned
+/// in a task.
+///
+/// This is called from [default_send_request] to actually send the request.
+pub async fn default_send_request_handler(
     mut request: hyper::Request<HyperOutgoingBody>,
     OutgoingRequestConfig {
         use_tls,
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 2070c1c0c..8437647e6 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -259,16 +259,21 @@ impl Config {
         // Conditionally enabled features depending on compile-time crate
         // features. Note that if these features are disabled then `Config` has
         // no way of re-enabling them.
-        ret.features.reference_types = cfg!(feature = "gc");
-        ret.features.threads = cfg!(feature = "threads");
-        ret.features.component_model = cfg!(feature = "component-model");
+        ret.features
+            .set(WasmFeatures::REFERENCE_TYPES, cfg!(feature = "gc"));
+        ret.features
+            .set(WasmFeatures::THREADS, cfg!(feature = "threads"));
+        ret.features.set(
+            WasmFeatures::COMPONENT_MODEL,
+            cfg!(feature = "component-model"),
+        );
 
         // If GC is disabled at compile time also disable it in features
         // forcibly irrespective of `wasmparser` defaults. Note that these also
         // aren't yet fully implemented in Wasmtime.
         if !cfg!(feature = "gc") {
-            ret.features.function_references = false;
-            ret.features.gc = false;
+            ret.features.set(WasmFeatures::FUNCTION_REFERENCES, false);
+            ret.features.set(WasmFeatures::GC, false);
         }
 
         ret.wasm_multi_value(true);
@@ -278,7 +283,7 @@ impl Config {
 
         // This is on-by-default in `wasmparser` since it's a stage 4+ proposal
         // but it's not implemented in Wasmtime yet so disable it.
-        ret.features.tail_call = false;
+        ret.features.set(WasmFeatures::TAIL_CALL, false);
 
         ret
     }
@@ -702,7 +707,7 @@ impl Config {
     ///
     /// [WebAssembly tail calls proposal]: https://github.com/WebAssembly/tail-call
     pub fn wasm_tail_call(&mut self, enable: bool) -> &mut Self {
-        self.features.tail_call = enable;
+        self.features.set(WasmFeatures::TAIL_CALL, enable);
         self.tunables.tail_callable = Some(enable);
         self
     }
@@ -727,7 +732,7 @@ impl Config {
     #[cfg(feature = "threads")]
     #[cfg_attr(docsrs, doc(cfg(feature = "threads")))]
     pub fn wasm_threads(&mut self, enable: bool) -> &mut Self {
-        self.features.threads = enable;
+        self.features.set(WasmFeatures::THREADS, enable);
         self
     }
 
@@ -750,7 +755,7 @@ impl Config {
     #[cfg(feature = "gc")]
     #[cfg_attr(docsrs, doc(cfg(feature = "gc")))]
     pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
-        self.features.reference_types = enable;
+        self.features.set(WasmFeatures::REFERENCE_TYPES, enable);
         self
     }
 
@@ -770,7 +775,7 @@ impl Config {
     #[cfg(feature = "gc")]
     #[cfg_attr(docsrs, doc(cfg(feature = "gc")))]
     pub fn wasm_function_references(&mut self, enable: bool) -> &mut Self {
-        self.features.function_references = enable;
+        self.features.set(WasmFeatures::FUNCTION_REFERENCES, enable);
         self
     }
 
@@ -792,7 +797,7 @@ impl Config {
     #[cfg(feature = "gc")]
     #[cfg_attr(docsrs, doc(cfg(feature = "gc")))]
     pub fn wasm_gc(&mut self, enable: bool) -> &mut Self {
-        self.features.gc = enable;
+        self.features.set(WasmFeatures::GC, enable);
         self
     }
 
@@ -812,7 +817,7 @@ impl Config {
     /// [proposal]: https://github.com/webassembly/simd
     /// [relaxed simd proposal]: https://github.com/WebAssembly/relaxed-simd
     pub fn wasm_simd(&mut self, enable: bool) -> &mut Self {
-        self.features.simd = enable;
+        self.features.set(WasmFeatures::SIMD, enable);
         self
     }
 
@@ -839,7 +844,7 @@ impl Config {
     ///
     /// [proposal]: https://github.com/webassembly/relaxed-simd
     pub fn wasm_relaxed_simd(&mut self, enable: bool) -> &mut Self {
-        self.features.relaxed_simd = enable;
+        self.features.set(WasmFeatures::RELAXED_SIMD, enable);
         self
     }
 
@@ -883,7 +888,7 @@ impl Config {
     ///
     /// [proposal]: https://github.com/webassembly/bulk-memory-operations
     pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
-        self.features.bulk_memory = enable;
+        self.features.set(WasmFeatures::BULK_MEMORY, enable);
         self
     }
 
@@ -897,7 +902,7 @@ impl Config {
     ///
     /// [proposal]: https://github.com/webassembly/multi-value
     pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self {
-        self.features.multi_value = enable;
+        self.features.set(WasmFeatures::MULTI_VALUE, enable);
         self
     }
 
@@ -911,7 +916,7 @@ impl Config {
     ///
     /// [proposal]: https://github.com/webassembly/multi-memory
     pub fn wasm_multi_memory(&mut self, enable: bool) -> &mut Self {
-        self.features.multi_memory = enable;
+        self.features.set(WasmFeatures::MULTI_MEMORY, enable);
         self
     }
 
@@ -926,7 +931,7 @@ impl Config {
     ///
     /// [proposal]: https://github.com/webassembly/memory64
     pub fn wasm_memory64(&mut self, enable: bool) -> &mut Self {
-        self.features.memory64 = enable;
+        self.features.set(WasmFeatures::MEMORY64, enable);
         self
     }
 
@@ -940,7 +945,7 @@ impl Config {
     /// [proposal]: https://github.com/webassembly/component-model
     #[cfg(feature = "component-model")]
     pub fn wasm_component_model(&mut self, enable: bool) -> &mut Self {
-        self.features.component_model = enable;
+        self.features.set(WasmFeatures::COMPONENT_MODEL, enable);
         self
     }
 
@@ -1671,16 +1676,24 @@ impl Config {
     }
 
     pub(crate) fn validate(&self) -> Result<Tunables> {
-        if self.features.reference_types && !self.features.bulk_memory {
+        if self.features.contains(WasmFeatures::REFERENCE_TYPES)
+            && !self.features.contains(WasmFeatures::BULK_MEMORY)
+        {
             bail!("feature 'reference_types' requires 'bulk_memory' to be enabled");
         }
-        if self.features.threads && !self.features.bulk_memory {
+        if self.features.contains(WasmFeatures::THREADS)
+            && !self.features.contains(WasmFeatures::BULK_MEMORY)
+        {
             bail!("feature 'threads' requires 'bulk_memory' to be enabled");
         }
-        if self.features.function_references && !self.features.reference_types {
+        if self.features.contains(WasmFeatures::FUNCTION_REFERENCES)
+            && !self.features.contains(WasmFeatures::REFERENCE_TYPES)
+        {
             bail!("feature 'function_references' requires 'reference_types' to be enabled");
         }
-        if self.features.gc && !self.features.function_references {
+        if self.features.contains(WasmFeatures::GC)
+            && !self.features.contains(WasmFeatures::FUNCTION_REFERENCES)
+        {
             bail!("feature 'gc' requires 'function_references' to be enabled");
         }
         #[cfg(feature = "async")]
@@ -1854,7 +1867,7 @@ impl Config {
                 .insert("enable_probestack".into());
         }
 
-        if self.features.tail_call {
+        if self.features.contains(WasmFeatures::TAIL_CALL) {
             ensure!(
                 target.architecture != Architecture::S390x,
                 "Tail calls are not supported on s390x yet: \
@@ -1888,7 +1901,7 @@ impl Config {
             .insert("preserve_frame_pointers".into(), "true".into());
 
         // check for incompatible compiler options and set required values
-        if self.features.reference_types {
+        if self.features.contains(WasmFeatures::REFERENCE_TYPES) {
             if !self
                 .compiler_config
                 .ensure_setting_unset_or_given("enable_safepoints", "true")
@@ -1897,7 +1910,9 @@ impl Config {
             }
         }
 
-        if self.features.relaxed_simd && !self.features.simd {
+        if self.features.contains(WasmFeatures::RELAXED_SIMD)
+            && !self.features.contains(WasmFeatures::SIMD)
+        {
             bail!("cannot disable the simd proposal but enable the relaxed simd proposal");
         }
 
@@ -1996,17 +2011,32 @@ impl fmt::Debug for Config {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let mut f = f.debug_struct("Config");
         f.field("debug_info", &self.tunables.generate_native_debuginfo)
-            .field("wasm_threads", &self.features.threads)
-            .field("wasm_reference_types", &self.features.reference_types)
+            .field(
+                "wasm_threads",
+                &self.features.contains(WasmFeatures::THREADS),
+            )
+            .field(
+                "wasm_reference_types",
+                &self.features.contains(WasmFeatures::REFERENCE_TYPES),
+            )
             .field(
                 "wasm_function_references",
-                &self.features.function_references,
+                &self.features.contains(WasmFeatures::FUNCTION_REFERENCES),
+            )
+            .field("wasm_gc", &self.features.contains(WasmFeatures::GC))
+            .field(
+                "wasm_bulk_memory",
+                &self.features.contains(WasmFeatures::BULK_MEMORY),
+            )
+            .field("wasm_simd", &self.features.contains(WasmFeatures::SIMD))
+            .field(
+                "wasm_relaxed_simd",
+                &self.features.contains(WasmFeatures::RELAXED_SIMD),
+            )
+            .field(
+                "wasm_multi_value",
+                &self.features.contains(WasmFeatures::MULTI_VALUE),
             )
-            .field("wasm_gc", &self.features.gc)
-            .field("wasm_bulk_memory", &self.features.bulk_memory)
-            .field("wasm_simd", &self.features.simd)
-            .field("wasm_relaxed_simd", &self.features.relaxed_simd)
-            .field("wasm_multi_value", &self.features.multi_value)
             .field("parallel_compilation", &self.parallel_compilation);
         #[cfg(any(feature = "cranelift", feature = "winch"))]
         {
diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs
index 95df56a79..02b2f376a 100644
--- a/crates/wasmtime/src/engine.rs
+++ b/crates/wasmtime/src/engine.rs
@@ -9,6 +9,7 @@ use once_cell::sync::OnceCell;
 use std::path::Path;
 use std::sync::atomic::{AtomicU64, Ordering};
 use std::sync::Arc;
+use wasmparser::WasmFeatures;
 use wasmtime_environ::obj;
 use wasmtime_environ::{FlagValue, ObjectKind, Tunables};
 #[cfg(feature = "runtime")]
@@ -299,7 +300,7 @@ impl Engine {
             // like typed function references and GC) are enabled this must be
             // enabled, otherwise this setting can have any value.
             "enable_safepoints" => {
-                if self.config().features.reference_types {
+                if self.config().features.contains(WasmFeatures::REFERENCE_TYPES) {
                     *value == FlagValue::Bool(true)
                 } else {
                     return Ok(())
@@ -333,7 +334,6 @@ impl Engine {
             | "tls_model" // wasmtime doesn't use tls right now
             | "opt_level" // opt level doesn't change semantics
             | "enable_alias_analysis" // alias analysis-based opts don't change semantics
-            | "probestack_func_adjusts_sp" // probestack above asserted disabled
             | "probestack_size_log2" // probestack above asserted disabled
             | "regalloc" // shouldn't change semantics
             | "enable_incremental_compilation_cache_checks" // shouldn't change semantics
diff --git a/crates/wasmtime/src/engine/serialization.rs b/crates/wasmtime/src/engine/serialization.rs
index aaa68960b..d6254cdff 100644
--- a/crates/wasmtime/src/engine/serialization.rs
+++ b/crates/wasmtime/src/engine/serialization.rs
@@ -199,7 +199,7 @@ struct WasmFeatures {
 impl Metadata<'_> {
     #[cfg(any(feature = "cranelift", feature = "winch"))]
     pub fn new(engine: &Engine) -> Metadata<'static> {
-        let wasmparser::WasmFeatures {
+        let wasmparser::WasmFeaturesInflated {
             reference_types,
             multi_value,
             bulk_memory,
@@ -225,7 +225,7 @@ impl Metadata<'_> {
             saturating_float_to_int: _,
             sign_extension: _,
             floats: _,
-        } = engine.config().features;
+        } = engine.config().features.inflate();
 
         // These features are not implemented in Wasmtime yet. We match on them
         // above so that once we do implement support for them, we won't
@@ -460,69 +460,78 @@ impl Metadata<'_> {
             gc,
         } = self.features;
 
+        use wasmparser::WasmFeatures as F;
         Self::check_cfg_bool(
             cfg!(feature = "gc"),
             "gc",
             reference_types,
-            other.reference_types,
+            other.contains(F::REFERENCE_TYPES),
             "WebAssembly reference types support",
         )?;
         Self::check_cfg_bool(
             cfg!(feature = "gc"),
             "gc",
             function_references,
-            other.function_references,
+            other.contains(F::FUNCTION_REFERENCES),
             "WebAssembly function-references support",
         )?;
         Self::check_cfg_bool(
             cfg!(feature = "gc"),
             "gc",
             gc,
-            other.gc,
+            other.contains(F::GC),
             "WebAssembly garbage collection support",
         )?;
 
         Self::check_bool(
             multi_value,
-            other.multi_value,
+            other.contains(F::MULTI_VALUE),
             "WebAssembly multi-value support",
         )?;
         Self::check_bool(
             bulk_memory,
-            other.bulk_memory,
+            other.contains(F::BULK_MEMORY),
             "WebAssembly bulk memory support",
         )?;
         Self::check_bool(
             component_model,
-            other.component_model,
+            other.contains(F::COMPONENT_MODEL),
             "WebAssembly component model support",
         )?;
-        Self::check_bool(simd, other.simd, "WebAssembly SIMD support")?;
-        Self::check_bool(tail_call, other.tail_call, "WebAssembly tail calls support")?;
-        Self::check_bool(threads, other.threads, "WebAssembly threads support")?;
+        Self::check_bool(simd, other.contains(F::SIMD), "WebAssembly SIMD support")?;
+        Self::check_bool(
+            tail_call,
+            other.contains(F::TAIL_CALL),
+            "WebAssembly tail calls support",
+        )?;
+        Self::check_bool(
+            threads,
+            other.contains(F::THREADS),
+            "WebAssembly threads support",
+        )?;
         Self::check_bool(
             multi_memory,
-            other.multi_memory,
+            other.contains(F::MULTI_MEMORY),
             "WebAssembly multi-memory support",
         )?;
         Self::check_bool(
             exceptions,
-            other.exceptions,
+            other.contains(F::EXCEPTIONS),
             "WebAssembly exceptions support",
         )?;
         Self::check_bool(
             memory64,
-            other.memory64,
+            other.contains(F::MEMORY64),
             "WebAssembly 64-bit memory support",
         )?;
         Self::check_bool(
             extended_const,
-            other.extended_const,
+            other.contains(F::EXTENDED_CONST),
             "WebAssembly extended-const support",
         )?;
         Self::check_bool(
             relaxed_simd,
-            other.relaxed_simd,
+            other.contains(F::RELAXED_SIMD),
             "WebAssembly relaxed-simd support",
         )?;
 
diff --git a/crates/wasmtime/src/runtime/instance.rs b/crates/wasmtime/src/runtime/instance.rs
index 690591caf..5d55fb888 100644
--- a/crates/wasmtime/src/runtime/instance.rs
+++ b/crates/wasmtime/src/runtime/instance.rs
@@ -9,6 +9,7 @@ use anyhow::{anyhow, bail, Context, Result};
 use std::mem;
 use std::ptr::NonNull;
 use std::sync::Arc;
+use wasmparser::WasmFeatures;
 use wasmtime_environ::{
     EntityIndex, EntityType, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, TableIndex, TypeTrace,
 };
@@ -340,7 +341,11 @@ impl Instance {
         // look at them.
         instance_handle.initialize(
             compiled_module.module(),
-            store.engine().config().features.bulk_memory,
+            store
+                .engine()
+                .config()
+                .features
+                .contains(WasmFeatures::BULK_MEMORY),
         )?;
 
         Ok((instance, compiled_module.module().start_func))
diff --git a/crates/wasmtime/src/runtime/store.rs b/crates/wasmtime/src/runtime/store.rs
index c21273fab..a0c045bea 100644
--- a/crates/wasmtime/src/runtime/store.rs
+++ b/crates/wasmtime/src/runtime/store.rs
@@ -1467,7 +1467,11 @@ impl StoreOpaque {
 
         #[cfg(feature = "gc")]
         fn allocate_gc_store(engine: &Engine) -> Result<GcStore> {
-            let (index, heap) = if engine.config().features.reference_types {
+            let (index, heap) = if engine
+                .config()
+                .features
+                .contains(wasmparser::WasmFeatures::REFERENCE_TYPES)
+            {
                 engine
                     .allocator()
                     .allocate_gc_heap(&**engine.gc_runtime())?
diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock
index 1aa8ac186..263bfd6e6 100644
--- a/supply-chain/imports.lock
+++ b/supply-chain/imports.lock
@@ -1184,6 +1184,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-encoder]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-metadata]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1202,6 +1208,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-metadata]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-mutate]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1220,6 +1232,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-mutate]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasm-smith]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1238,6 +1256,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasm-smith]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmparser]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1256,6 +1280,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmparser]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmprinter]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1274,6 +1304,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wasmprinter]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wasmtime]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -1580,6 +1616,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wast]]
+version = "204.0.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wat]]
 version = "1.201.0"
 when = "2024-02-27"
@@ -1598,6 +1640,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wat]]
+version = "1.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wiggle]]
 version = "18.0.2"
 when = "2024-02-28"
@@ -1886,6 +1934,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-component]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.wit-parser]]
 version = "0.201.0"
 when = "2024-02-27"
@@ -1904,6 +1958,12 @@ when = "2024-04-12"
 user-id = 73222
 user-login = "wasmtime-publish"
 
+[[publisher.wit-parser]]
+version = "0.204.0"
+when = "2024-04-16"
+user-id = 73222
+user-login = "wasmtime-publish"
+
 [[publisher.witx]]
 version = "0.9.1"
 when = "2021-06-22"
diff --git a/winch/codegen/src/isa/reg.rs b/winch/codegen/src/isa/reg.rs
index bfdcdb8fc..0a96df329 100644
--- a/winch/codegen/src/isa/reg.rs
+++ b/winch/codegen/src/isa/reg.rs
@@ -1,6 +1,5 @@
-use cranelift_codegen::RealReg;
+use regalloc2::PReg;
 pub use regalloc2::RegClass;
-use regalloc2::{PReg, VReg};
 
 /// A newtype abstraction on top of a physical register.
 //
@@ -72,15 +71,3 @@ impl std::fmt::Debug for Reg {
         write!(f, "{}", self.0)
     }
 }
-
-impl Into<VReg> for Reg {
-    fn into(self) -> VReg {
-        VReg::new(self.inner().index(), self.class())
-    }
-}
-
-impl Into<RealReg> for Reg {
-    fn into(self) -> RealReg {
-        Into::<VReg>::into(self).into()
-    }
-}


Test patch:
diff --git a/cranelift/filetests/filetests/isa/aarch64/call-pauth-bkey.clif b/cranelift/filetests/filetests/isa/aarch64/call-pauth-bkey.clif
index 06e6ecc54..4393309de 100644
--- a/cranelift/filetests/filetests/isa/aarch64/call-pauth-bkey.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/call-pauth-bkey.clif
@@ -100,7 +100,7 @@ block0(v0: i64):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x3, TestCase(%g)+0
-;   return_call_ind x3 old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call_ind x3 new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -112,9 +112,7 @@ block0(v0: i64):
 ;   b #0x1c
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   autibz
 ;   br x3
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/call-pauth.clif b/cranelift/filetests/filetests/isa/aarch64/call-pauth.clif
index 74dbb2cfd..705b6fc66 100644
--- a/cranelift/filetests/filetests/isa/aarch64/call-pauth.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/call-pauth.clif
@@ -101,7 +101,7 @@ block0(v0: i64):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x3, TestCase(%g)+0
-;   return_call_ind x3 old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call_ind x3 new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -113,9 +113,7 @@ block0(v0: i64):
 ;   b #0x1c
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %g 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   autiaz
 ;   br x3
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/call.clif b/cranelift/filetests/filetests/isa/aarch64/call.clif
index def87fb8b..d350af732 100644
--- a/cranelift/filetests/filetests/isa/aarch64/call.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/call.clif
@@ -670,8 +670,8 @@ block0(v0: i128, v1: i128, v2: i128, v3: i64, v4: i128):
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   ldr x0, [fp, #16]
-;   ldr x1, [fp, #24]
+;   ldr x0, [sp, #16]
+;   ldr x1, [sp, #24]
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -680,8 +680,8 @@ block0(v0: i128, v1: i128, v2: i128, v3: i64, v4: i128):
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   ldur x0, [x29, #0x10]
-;   ldur x1, [x29, #0x18]
+;   ldur x0, [sp, #0x10]
+;   ldur x1, [sp, #0x18]
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
@@ -747,8 +747,8 @@ block0(v0: i128, v1: i128, v2: i128, v3: i64, v4: i128):
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   ldr x0, [fp, #16]
-;   ldr x1, [fp, #24]
+;   ldr x0, [sp, #16]
+;   ldr x1, [sp, #24]
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -757,8 +757,8 @@ block0(v0: i128, v1: i128, v2: i128, v3: i64, v4: i128):
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   ldur x0, [x29, #0x10]
-;   ldur x1, [x29, #0x18]
+;   ldur x0, [sp, #0x10]
+;   ldur x1, [sp, #0x18]
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/return-call-indirect.clif b/cranelift/filetests/filetests/isa/aarch64/return-call-indirect.clif
index d02b2b868..1e587efb4 100644
--- a/cranelift/filetests/filetests/isa/aarch64/return-call-indirect.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/return-call-indirect.clif
@@ -34,7 +34,7 @@ block0(v0: i64):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x3, TestCase(%callee_i64)+0
-;   return_call_ind x3 old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call_ind x3 new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -45,9 +45,7 @@ block0(v0: i64):
 ;   b #0x18
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %callee_i64 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x3
 
 ;;;; Test colocated tail calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -66,7 +64,7 @@ block0(v0: i64):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x3, TestCase(%callee_i64)+0
-;   return_call_ind x3 old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call_ind x3 new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -77,9 +75,7 @@ block0(v0: i64):
 ;   b #0x18
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %callee_i64 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x3
 
 ;;;; Test passing `f64`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -117,7 +113,7 @@ block0(v0: f64):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x2, TestCase(%callee_f64)+0
-;   return_call_ind x2 old_stack_arg_size:0 new_stack_arg_size:0 v0=v0
+;   return_call_ind x2 new_stack_arg_size:0 v0=v0
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -128,9 +124,7 @@ block0(v0: f64):
 ;   b #0x18
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %callee_f64 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x2
 
 ;;;; Test passing `i8`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -170,7 +164,7 @@ block0(v0: i8):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x3, TestCase(%callee_i8)+0
-;   return_call_ind x3 old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call_ind x3 new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -181,9 +175,7 @@ block0(v0: i8):
 ;   b #0x18
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %callee_i8 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x3
 
 ;;;; Test passing many arguments on stack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -226,6 +218,10 @@ block0:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ;   sub sp, sp, #16
+;   ldr fp, [sp, #16]
+;   stp fp, lr, [sp]
+;   mov fp, sp
+;   sub sp, sp, #16
 ; block0:
 ;   movz x2, #10
 ;   movz x3, #15
@@ -255,20 +251,22 @@ block0:
 ;   movz x0, #130
 ;   movz x1, #135
 ;   load_ext_name x10, TestCase(%tail_callee_stack_args)+0
-;   sub sp, sp, #16
-;   virtual_sp_offset_adjust 16
-;   str x0, [sp]
-;   str x1, [sp, #8]
+;   str x0, [sp, #32]
+;   str x1, [sp, #40]
 ;   mov x0, x10
-;   ldr x10, [sp, #16]
-;   return_call_ind x0 old_stack_arg_size:0 new_stack_arg_size:16 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
+;   ldr x10, [sp]
+;   return_call_ind x0 new_stack_arg_size:16 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ;   sub sp, sp, #0x10
-; block1: ; offset 0xc
+;   ldur x29, [sp, #0x10]
+;   stp x29, x30, [sp]
+;   mov x29, sp
+;   sub sp, sp, #0x10
+; block1: ; offset 0x1c
 ;   mov x2, #0xa
 ;   mov x3, #0xf
 ;   mov x4, #0x14
@@ -296,21 +294,15 @@ block0:
 ;   mov x28, #0x7d
 ;   mov x0, #0x82
 ;   mov x1, #0x87
-;   ldr x10, #0x80
-;   b #0x88
+;   ldr x10, #0x90
+;   b #0x98
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   sub sp, sp, #0x10
-;   stur x0, [sp]
-;   stur x1, [sp, #8]
+;   stur x0, [sp, #0x20]
+;   stur x1, [sp, #0x28]
 ;   mov x0, x10
-;   ldur x10, [sp, #0x10]
-;   ldp x16, x30, [x29]
-;   ldur x17, [sp, #8]
-;   stur x17, [x29, #8]
-;   ldur x17, [sp]
-;   stur x17, [x29]
-;   mov sp, x29
-;   mov x29, x16
+;   ldur x10, [sp]
+;   add sp, sp, #0x10
+;   ldp x29, x30, [sp], #0x10
 ;   br x0
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/return-call.clif b/cranelift/filetests/filetests/isa/aarch64/return-call.clif
index ebcc8a1e3..cd00960f1 100644
--- a/cranelift/filetests/filetests/isa/aarch64/return-call.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/return-call.clif
@@ -32,7 +32,7 @@ block0(v0: i64):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x3, TestCase(%callee_i64)+0
-;   return_call_ind x3 old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call_ind x3 new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -43,9 +43,7 @@ block0(v0: i64):
 ;   b #0x18
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %callee_i64 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x3
 
 ;;;; Test colocated tail calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -61,17 +59,15 @@ block0(v0: i64):
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   return_call TestCase(%callee_i64) old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call TestCase(%callee_i64) new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
-;   b #0x14 ; reloc_external Call %callee_i64 0
+;   ldp x29, x30, [sp], #0x10
+;   b #0xc ; reloc_external Call %callee_i64 0
 
 ;;;; Test passing `f64`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -106,7 +102,7 @@ block0(v0: f64):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x2, TestCase(%callee_f64)+0
-;   return_call_ind x2 old_stack_arg_size:0 new_stack_arg_size:0 v0=v0
+;   return_call_ind x2 new_stack_arg_size:0 v0=v0
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -117,9 +113,7 @@ block0(v0: f64):
 ;   b #0x18
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %callee_f64 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x2
 
 ;;;; Test passing `i8`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -157,7 +151,7 @@ block0(v0: i8):
 ;   mov fp, sp
 ; block0:
 ;   load_ext_name x3, TestCase(%callee_i8)+0
-;   return_call_ind x3 old_stack_arg_size:0 new_stack_arg_size:0 x2=x2
+;   return_call_ind x3 new_stack_arg_size:0 x2=x2
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -168,9 +162,7 @@ block0(v0: i8):
 ;   b #0x18
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %callee_i8 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   add sp, x29, #0x10
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x3
 
 ;;;; Test passing many arguments on stack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -184,8 +176,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   ldr x9, [fp, #16]
-;   ldr x2, [fp, #24]
+;   ldr x9, [sp, #16]
+;   ldr x2, [sp, #24]
 ;   ldp fp, lr, [sp], #16
 ;   add sp, sp, #16
 ;   ret
@@ -195,8 +187,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   ldur x9, [x29, #0x10]
-;   ldur x2, [x29, #0x18]
+;   ldur x9, [sp, #0x10]
+;   ldur x2, [sp, #0x18]
 ;   ldp x29, x30, [sp], #0x10
 ;   add sp, sp, #0x10
 ;   ret
@@ -237,6 +229,10 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
+;   sub sp, sp, #16
+;   ldr fp, [sp, #16]
+;   stp fp, lr, [sp]
+;   mov fp, sp
 ; block0:
 ;   movz x2, #10
 ;   movz x3, #15
@@ -264,18 +260,20 @@ block0:
 ;   movz x28, #125
 ;   movz x0, #130
 ;   movz x1, #135
-;   sub sp, sp, #16
-;   virtual_sp_offset_adjust 16
-;   str x0, [sp]
-;   str x1, [sp, #8]
+;   str x0, [sp, #16]
+;   str x1, [sp, #24]
 ;   load_ext_name x0, TestCase(%tail_callee_stack_args)+0
-;   return_call_ind x0 old_stack_arg_size:0 new_stack_arg_size:16 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
+;   return_call_ind x0 new_stack_arg_size:16 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-; block1: ; offset 0x8
+;   sub sp, sp, #0x10
+;   ldur x29, [sp, #0x10]
+;   stp x29, x30, [sp]
+;   mov x29, sp
+; block1: ; offset 0x18
 ;   mov x2, #0xa
 ;   mov x3, #0xf
 ;   mov x4, #0x14
@@ -302,20 +300,13 @@ block0:
 ;   mov x28, #0x7d
 ;   mov x0, #0x82
 ;   mov x1, #0x87
-;   sub sp, sp, #0x10
-;   stur x0, [sp]
-;   stur x1, [sp, #8]
-;   ldr x0, #0x84
-;   b #0x8c
+;   stur x0, [sp, #0x10]
+;   stur x1, [sp, #0x18]
+;   ldr x0, #0x90
+;   b #0x98
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   ldur x17, [sp, #8]
-;   stur x17, [x29, #8]
-;   ldur x17, [sp]
-;   stur x17, [x29]
-;   mov sp, x29
-;   mov x29, x16
+;   ldp x29, x30, [sp], #0x10
 ;   br x0
 
 ;;;; Test diff blocks with diff return calls with diff # of stack args ;;;;;;;;;
@@ -329,8 +320,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   ldr x9, [fp, #16]
-;   ldr x2, [fp, #24]
+;   ldr x9, [sp, #16]
+;   ldr x2, [sp, #24]
 ;   ldp fp, lr, [sp], #16
 ;   add sp, sp, #16
 ;   ret
@@ -340,8 +331,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   ldur x9, [x29, #0x10]
-;   ldur x2, [x29, #0x18]
+;   ldur x9, [sp, #0x10]
+;   ldur x2, [sp, #0x18]
 ;   ldp x29, x30, [sp], #0x10
 ;   add sp, sp, #0x10
 ;   ret
@@ -355,9 +346,9 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   ldr x9, [fp, #16]
-;   ldr x11, [fp, #24]
-;   ldr x2, [fp, #32]
+;   ldr x9, [sp, #16]
+;   ldr x11, [sp, #24]
+;   ldr x2, [sp, #32]
 ;   ldp fp, lr, [sp], #16
 ;   add sp, sp, #32
 ;   ret
@@ -367,9 +358,9 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   ldur x9, [x29, #0x10]
-;   ldur x11, [x29, #0x18]
-;   ldur x2, [x29, #0x20]
+;   ldur x9, [sp, #0x10]
+;   ldur x11, [sp, #0x18]
+;   ldur x2, [sp, #0x20]
 ;   ldp x29, x30, [sp], #0x10
 ;   add sp, sp, #0x20
 ;   ret
@@ -418,10 +409,15 @@ block2:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
+;   sub sp, sp, #32
+;   ldr fp, [sp, #32]
+;   stp fp, lr, [sp]
+;   mov fp, sp
 ;   sub sp, sp, #16
 ; block0:
-;   movz x14, #10
-;   str x14, [sp]
+;   str x2, [sp]
+;   movz x2, #10
+;   str x2, [sp, #8]
 ;   movz x3, #15
 ;   movz x4, #20
 ;   movz x5, #25
@@ -447,34 +443,36 @@ block2:
 ;   movz x28, #125
 ;   movz x1, #130
 ;   movz x0, #135
+;   ldr x2, [sp]
 ;   cbnz x2, label2 ; b label1
 ; block1:
 ;   movz x2, #140
-;   sub sp, sp, #32
-;   virtual_sp_offset_adjust 32
-;   str x1, [sp]
-;   str x0, [sp, #8]
-;   str x2, [sp, #16]
+;   str x1, [sp, #32]
+;   str x0, [sp, #40]
+;   str x2, [sp, #48]
 ;   load_ext_name x0, TestCase(%different_callee2)+0
-;   ldr x2, [sp, #32]
-;   return_call_ind x0 old_stack_arg_size:0 new_stack_arg_size:32 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
+;   ldr x2, [sp, #8]
+;   return_call_ind x0 new_stack_arg_size:32 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
 ; block2:
-;   ldr x2, [sp]
-;   sub sp, sp, #16
-;   virtual_sp_offset_adjust 16
-;   str x1, [sp]
-;   str x0, [sp, #8]
+;   ldr x2, [sp, #8]
+;   str x1, [sp, #48]
+;   str x0, [sp, #56]
 ;   load_ext_name x0, TestCase(%different_callee1)+0
-;   return_call_ind x0 old_stack_arg_size:0 new_stack_arg_size:16 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
+;   return_call_ind x0 new_stack_arg_size:16 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7 x8=x8 x9=x9 x10=x10 x11=x11 x12=x12 x13=x13 x14=x14 x15=x15 x19=x19 x20=x20 x21=x21 x22=x22 x23=x23 x24=x24 x25=x25 x26=x26 x27=x27 x28=x28
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
+;   sub sp, sp, #0x20
+;   ldur x29, [sp, #0x20]
+;   stp x29, x30, [sp]
+;   mov x29, sp
 ;   sub sp, sp, #0x10
-; block1: ; offset 0xc
-;   mov x14, #0xa
-;   stur x14, [sp]
+; block1: ; offset 0x1c
+;   stur x2, [sp]
+;   mov x2, #0xa
+;   stur x2, [sp, #8]
 ;   mov x3, #0xf
 ;   mov x4, #0x14
 ;   mov x5, #0x19
@@ -500,45 +498,31 @@ block2:
 ;   mov x28, #0x7d
 ;   mov x1, #0x82
 ;   mov x0, #0x87
-;   cbnz x2, #0xd4
-; block2: ; offset 0x7c
+;   ldur x2, [sp]
+;   cbnz x2, #0xc4
+; block2: ; offset 0x94
 ;   mov x2, #0x8c
-;   sub sp, sp, #0x20
-;   stur x1, [sp]
-;   stur x0, [sp, #8]
-;   stur x2, [sp, #0x10]
-;   ldr x0, #0x98
-;   b #0xa0
+;   stur x1, [sp, #0x20]
+;   stur x0, [sp, #0x28]
+;   stur x2, [sp, #0x30]
+;   ldr x0, #0xac
+;   b #0xb4
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %different_callee2 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldur x2, [sp, #0x20]
-;   ldp x16, x30, [x29]
-;   ldur x17, [sp, #0x18]
-;   stur x17, [x29, #8]
-;   ldur x17, [sp, #0x10]
-;   stur x17, [x29]
-;   ldur x17, [sp, #8]
-;   stur x17, [x29, #-8]
-;   ldur x17, [sp]
-;   stur x17, [x29, #-0x10]
-;   sub sp, x29, #0x10
-;   mov x29, x16
+;   ldur x2, [sp, #8]
+;   add sp, sp, #0x10
+;   ldp x29, x30, [sp], #0x10
 ;   br x0
-; block3: ; offset 0xd4
-;   ldur x2, [sp]
-;   sub sp, sp, #0x10
-;   stur x1, [sp]
-;   stur x0, [sp, #8]
-;   ldr x0, #0xec
-;   b #0xf4
+; block3: ; offset 0xc4
+;   ldur x2, [sp, #8]
+;   stur x1, [sp, #0x30]
+;   stur x0, [sp, #0x38]
+;   ldr x0, #0xd8
+;   b #0xe0
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %different_callee1 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldp x16, x30, [x29]
-;   ldur x17, [sp, #8]
-;   stur x17, [x29, #8]
-;   ldur x17, [sp]
-;   stur x17, [x29]
-;   mov sp, x29
-;   mov x29, x16
+;   add sp, sp, #0x10
+;   ldp x29, x30, [sp], #0x10
+;   add sp, sp, #0x10
 ;   br x0
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif b/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif
index 64b8b4f4e..a68287270 100644
--- a/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif
@@ -12,8 +12,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
 ; block0:
-;   ldr x9, [fp, #16]
-;   ldr x2, [fp, #24]
+;   ldr x9, [sp, #16]
+;   ldr x2, [sp, #24]
 ;   ldp fp, lr, [sp], #16
 ;   add sp, sp, #16
 ;   ret
@@ -23,8 +23,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
 ; block1: ; offset 0x8
-;   ldur x9, [x29, #0x10]
-;   ldur x2, [x29, #0x18]
+;   ldur x9, [sp, #0x10]
+;   ldur x2, [sp, #0x18]
 ;   ldp x29, x30, [sp], #0x10
 ;   add sp, sp, #0x10
 ;   ret
@@ -316,8 +316,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   sub sp, sp, #16
 ; block0:
 ;   str x9, [sp]
-;   ldr x9, [fp, #16]
-;   ldr x1, [fp, #24]
+;   ldr x9, [sp, #32]
+;   ldr x1, [sp, #40]
 ;   str x9, [x0]
 ;   str x1, [x0, #8]
 ;   ldr x9, [sp]
@@ -333,8 +333,8 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ;   sub sp, sp, #0x10
 ; block1: ; offset 0xc
 ;   stur x9, [sp]
-;   ldur x9, [x29, #0x10]
-;   ldur x1, [x29, #0x18]
+;   ldur x9, [sp, #0x20]
+;   ldur x1, [sp, #0x28]
 ;   stur x9, [x0]
 ;   stur x1, [x0, #8]
 ;   ldur x9, [sp]
diff --git a/cranelift/filetests/filetests/isa/x64/return-call-indirect.clif b/cranelift/filetests/filetests/isa/x64/return-call-indirect.clif
index c18d49b37..ba16a98c9 100644
--- a/cranelift/filetests/filetests/isa/x64/return-call-indirect.clif
+++ b/cranelift/filetests/filetests/isa/x64/return-call-indirect.clif
@@ -42,18 +42,18 @@ block0(v0: i64):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   load_ext_name %callee_i64+0, %rax
-;   return_call_unknown %rax (0) %rdi=%rdi
+;   load_ext_name %callee_i64+0, %rcx
+;   return_call_unknown %rcx (0) tmp=%rdx %rdi=%rdi
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movabsq $0, %rax ; reloc_external Abs8 %callee_i64 0
+;   movabsq $0, %rcx ; reloc_external Abs8 %callee_i64 0
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%rax
+;   jmpq *%rcx
 
 ;;;; Test colocated tail calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -70,18 +70,18 @@ block0(v0: i64):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   load_ext_name %callee_i64+0, %rax
-;   return_call_unknown %rax (0) %rdi=%rdi
+;   load_ext_name %callee_i64+0, %rcx
+;   return_call_unknown %rcx (0) tmp=%rdx %rdi=%rdi
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   leaq (%rip), %rax ; reloc_external CallPCRel4 %callee_i64 -4
+;   leaq (%rip), %rcx ; reloc_external CallPCRel4 %callee_i64 -4
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%rax
+;   jmpq *%rcx
 
 ;;;; Test passing `f64`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -139,18 +139,18 @@ block0(v0: f64):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   load_ext_name %callee_f64+0, %rax
-;   return_call_unknown %rax (0) %xmm0=%xmm0
+;   load_ext_name %callee_f64+0, %rcx
+;   return_call_unknown %rcx (0) tmp=%rdx %xmm0=%xmm0
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movabsq $0, %rax ; reloc_external Abs8 %callee_f64 0
+;   movabsq $0, %rcx ; reloc_external Abs8 %callee_f64 0
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%rax
+;   jmpq *%rcx
 
 ;;;; Test passing `i8`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -195,18 +195,18 @@ block0(v0: i8):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   load_ext_name %callee_i8+0, %rax
-;   return_call_unknown %rax (0) %rdi=%rdi
+;   load_ext_name %callee_i8+0, %rcx
+;   return_call_unknown %rcx (0) tmp=%rdx %rdi=%rdi
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movabsq $0, %rax ; reloc_external Abs8 %callee_i8 0
+;   movabsq $0, %rcx ; reloc_external Abs8 %callee_i8 0
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%rax
+;   jmpq *%rcx
 
 ;;;; Test passing many arguments on stack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -272,70 +272,70 @@ block0:
 ;   movq    %r8, rsp(72 + virtual offset)
 ;   movl    $35, %r9d
 ;   movq    %r9, rsp(64 + virtual offset)
-;   movl    $40, %esi
-;   movq    %rsi, rsp(56 + virtual offset)
-;   movl    $45, %eax
-;   movl    $50, %r10d
-;   movl    $55, %r12d
-;   movl    $60, %r13d
-;   movl    $65, %r14d
-;   movl    $70, %r15d
-;   movl    $75, %ebx
+;   movl    $40, %eax
+;   movl    $45, %r10d
+;   movl    $50, %r11d
+;   movl    $55, %r13d
+;   movl    $60, %r14d
+;   movl    $65, %r15d
+;   movl    $70, %ebx
+;   movl    $75, %r12d
 ;   movl    $80, %edi
 ;   movl    $85, %esi
+;   movq    %rsi, rsp(56 + virtual offset)
 ;   movl    $90, %edx
 ;   movl    $95, %ecx
 ;   movl    $100, %r8d
 ;   movl    $105, %r9d
-;   movl    $110, %r11d
-;   movq    %r11, rsp(48 + virtual offset)
-;   movl    $115, %r11d
-;   movq    %r11, rsp(40 + virtual offset)
-;   movl    $120, %r11d
-;   movq    %r11, rsp(32 + virtual offset)
-;   movl    $125, %r11d
-;   movq    %r11, rsp(24 + virtual offset)
-;   movl    $130, %r11d
-;   movq    %r11, rsp(16 + virtual offset)
-;   movl    $135, %r11d
-;   movq    %r11, rsp(8 + virtual offset)
-;   load_ext_name %tail_callee_stack_args+0, %r11
-;   movq    %r11, rsp(0 + virtual offset)
-;   movq    rsp(56 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 160)
-;   movq    %rax, rbp(stack args max - 152)
-;   movq    %r10, rbp(stack args max - 144)
-;   movq    %r12, rbp(stack args max - 136)
-;   movq    %r13, rbp(stack args max - 128)
-;   movq    %r14, rbp(stack args max - 120)
-;   movq    %r15, rbp(stack args max - 112)
-;   movq    %rbx, rbp(stack args max - 104)
+;   movl    $110, %esi
+;   movq    %rsi, rsp(48 + virtual offset)
+;   movl    $115, %esi
+;   movq    %rsi, rsp(40 + virtual offset)
+;   movl    $120, %esi
+;   movq    %rsi, rsp(32 + virtual offset)
+;   movl    $125, %esi
+;   movq    %rsi, rsp(24 + virtual offset)
+;   movl    $130, %esi
+;   movq    %rsi, rsp(16 + virtual offset)
+;   movl    $135, %esi
+;   movq    %rsi, rsp(8 + virtual offset)
+;   load_ext_name %tail_callee_stack_args+0, %rsi
+;   movq    %rsi, rsp(0 + virtual offset)
+;   movq    %rax, rbp(stack args max - 160)
+;   movq    %r10, rbp(stack args max - 152)
+;   movq    %r11, rbp(stack args max - 144)
+;   movq    %r13, rbp(stack args max - 136)
+;   movq    %r14, rbp(stack args max - 128)
+;   movq    %r15, rbp(stack args max - 120)
+;   movq    %rbx, rbp(stack args max - 112)
+;   movq    %r12, rbp(stack args max - 104)
 ;   movq    %rdi, rbp(stack args max - 96)
-;   movq    %rsi, rbp(stack args max - 88)
+;   movq    rsp(56 + virtual offset), %rdi
+;   movq    %rdi, rbp(stack args max - 88)
 ;   movq    %rdx, rbp(stack args max - 80)
 ;   movq    %rcx, rbp(stack args max - 72)
 ;   movq    %r8, rbp(stack args max - 64)
 ;   movq    %r9, rbp(stack args max - 56)
-;   movq    rsp(48 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 48)
-;   movq    rsp(40 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 40)
-;   movq    rsp(32 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 32)
-;   movq    rsp(24 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 24)
-;   movq    rsp(16 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 16)
-;   movq    rsp(8 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 8)
+;   movq    rsp(48 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 48)
+;   movq    rsp(40 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 40)
+;   movq    rsp(32 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 32)
+;   movq    rsp(24 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 24)
+;   movq    rsp(16 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 16)
+;   movq    rsp(8 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 8)
 ;   movq    rsp(80 + virtual offset), %rcx
 ;   movq    rsp(88 + virtual offset), %rdx
 ;   movq    rsp(96 + virtual offset), %rsi
 ;   movq    rsp(104 + virtual offset), %rdi
 ;   movq    rsp(72 + virtual offset), %r8
 ;   movq    rsp(64 + virtual offset), %r9
-;   movq    rsp(0 + virtual offset), %r11
-;   return_call_unknown %r11 (160) %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
+;   movq    rsp(0 + virtual offset), %r10
+;   return_call_unknown %r10 (160) tmp=%rax %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -366,69 +366,69 @@ block0:
 ;   movq %r8, 0x48(%rsp)
 ;   movl $0x23, %r9d
 ;   movq %r9, 0x40(%rsp)
-;   movl $0x28, %esi
-;   movq %rsi, 0x38(%rsp)
-;   movl $0x2d, %eax
-;   movl $0x32, %r10d
-;   movl $0x37, %r12d
-;   movl $0x3c, %r13d
-;   movl $0x41, %r14d
-;   movl $0x46, %r15d
-;   movl $0x4b, %ebx
+;   movl $0x28, %eax
+;   movl $0x2d, %r10d
+;   movl $0x32, %r11d
+;   movl $0x37, %r13d
+;   movl $0x3c, %r14d
+;   movl $0x41, %r15d
+;   movl $0x46, %ebx
+;   movl $0x4b, %r12d
 ;   movl $0x50, %edi
 ;   movl $0x55, %esi
+;   movq %rsi, 0x38(%rsp)
 ;   movl $0x5a, %edx
 ;   movl $0x5f, %ecx
 ;   movl $0x64, %r8d
 ;   movl $0x69, %r9d
-;   movl $0x6e, %r11d
-;   movq %r11, 0x30(%rsp)
-;   movl $0x73, %r11d
-;   movq %r11, 0x28(%rsp)
-;   movl $0x78, %r11d
-;   movq %r11, 0x20(%rsp)
-;   movl $0x7d, %r11d
-;   movq %r11, 0x18(%rsp)
-;   movl $0x82, %r11d
-;   movq %r11, 0x10(%rsp)
-;   movl $0x87, %r11d
-;   movq %r11, 8(%rsp)
-;   movabsq $0, %r11 ; reloc_external Abs8 %tail_callee_stack_args 0
-;   movq %r11, (%rsp)
-;   movq 0x38(%rsp), %r11
-;   movq %r11, 0x10(%rbp)
-;   movq %rax, 0x18(%rbp)
-;   movq %r10, 0x20(%rbp)
-;   movq %r12, 0x28(%rbp)
-;   movq %r13, 0x30(%rbp)
-;   movq %r14, 0x38(%rbp)
-;   movq %r15, 0x40(%rbp)
-;   movq %rbx, 0x48(%rbp)
+;   movl $0x6e, %esi
+;   movq %rsi, 0x30(%rsp)
+;   movl $0x73, %esi
+;   movq %rsi, 0x28(%rsp)
+;   movl $0x78, %esi
+;   movq %rsi, 0x20(%rsp)
+;   movl $0x7d, %esi
+;   movq %rsi, 0x18(%rsp)
+;   movl $0x82, %esi
+;   movq %rsi, 0x10(%rsp)
+;   movl $0x87, %esi
+;   movq %rsi, 8(%rsp)
+;   movabsq $0, %rsi ; reloc_external Abs8 %tail_callee_stack_args 0
+;   movq %rsi, (%rsp)
+;   movq %rax, 0x10(%rbp)
+;   movq %r10, 0x18(%rbp)
+;   movq %r11, 0x20(%rbp)
+;   movq %r13, 0x28(%rbp)
+;   movq %r14, 0x30(%rbp)
+;   movq %r15, 0x38(%rbp)
+;   movq %rbx, 0x40(%rbp)
+;   movq %r12, 0x48(%rbp)
 ;   movq %rdi, 0x50(%rbp)
-;   movq %rsi, 0x58(%rbp)
+;   movq 0x38(%rsp), %rdi
+;   movq %rdi, 0x58(%rbp)
 ;   movq %rdx, 0x60(%rbp)
 ;   movq %rcx, 0x68(%rbp)
 ;   movq %r8, 0x70(%rbp)
 ;   movq %r9, 0x78(%rbp)
-;   movq 0x30(%rsp), %r11
-;   movq %r11, 0x80(%rbp)
-;   movq 0x28(%rsp), %r11
-;   movq %r11, 0x88(%rbp)
-;   movq 0x20(%rsp), %r11
-;   movq %r11, 0x90(%rbp)
-;   movq 0x18(%rsp), %r11
-;   movq %r11, 0x98(%rbp)
-;   movq 0x10(%rsp), %r11
-;   movq %r11, 0xa0(%rbp)
-;   movq 8(%rsp), %r11
-;   movq %r11, 0xa8(%rbp)
+;   movq 0x30(%rsp), %rsi
+;   movq %rsi, 0x80(%rbp)
+;   movq 0x28(%rsp), %rsi
+;   movq %rsi, 0x88(%rbp)
+;   movq 0x20(%rsp), %rsi
+;   movq %rsi, 0x90(%rbp)
+;   movq 0x18(%rsp), %rsi
+;   movq %rsi, 0x98(%rbp)
+;   movq 0x10(%rsp), %rsi
+;   movq %rsi, 0xa0(%rbp)
+;   movq 8(%rsp), %rsi
+;   movq %rsi, 0xa8(%rbp)
 ;   movq 0x50(%rsp), %rcx
 ;   movq 0x58(%rsp), %rdx
 ;   movq 0x60(%rsp), %rsi
 ;   movq 0x68(%rsp), %rdi
 ;   movq 0x48(%rsp), %r8
 ;   movq 0x40(%rsp), %r9
-;   movq (%rsp), %r11
+;   movq (%rsp), %r10
 ;   movq 0x70(%rsp), %rbx
 ;   movq 0x78(%rsp), %r12
 ;   movq 0x80(%rsp), %r13
@@ -437,5 +437,5 @@ block0:
 ;   addq $0xa0, %rsp
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%r11
+;   jmpq *%r10
 
diff --git a/cranelift/filetests/filetests/isa/x64/return-call.clif b/cranelift/filetests/filetests/isa/x64/return-call.clif
index b556aad98..8ffb1c2fd 100644
--- a/cranelift/filetests/filetests/isa/x64/return-call.clif
+++ b/cranelift/filetests/filetests/isa/x64/return-call.clif
@@ -40,18 +40,18 @@ block0(v0: i64):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   load_ext_name %callee_i64+0, %rax
-;   return_call_unknown %rax (0) %rdi=%rdi
+;   load_ext_name %callee_i64+0, %rcx
+;   return_call_unknown %rcx (0) tmp=%rdx %rdi=%rdi
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movabsq $0, %rax ; reloc_external Abs8 %callee_i64 0
+;   movabsq $0, %rcx ; reloc_external Abs8 %callee_i64 0
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%rax
+;   jmpq *%rcx
 
 ;;;; Test colocated tail calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -66,7 +66,7 @@ block0(v0: i64):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   return_call_known TestCase(%callee_i64) (0) %rdi=%rdi
+;   return_call_known TestCase(%callee_i64) (0) tmp=%rax %rdi=%rdi
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -131,18 +131,18 @@ block0(v0: f64):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   load_ext_name %callee_f64+0, %rax
-;   return_call_unknown %rax (0) %xmm0=%xmm0
+;   load_ext_name %callee_f64+0, %rcx
+;   return_call_unknown %rcx (0) tmp=%rdx %xmm0=%xmm0
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movabsq $0, %rax ; reloc_external Abs8 %callee_f64 0
+;   movabsq $0, %rcx ; reloc_external Abs8 %callee_f64 0
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%rax
+;   jmpq *%rcx
 
 ;;;; Test passing `i8`s ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -185,18 +185,18 @@ block0(v0: i8):
 ;   pushq   %rbp
 ;   movq    %rsp, %rbp
 ; block0:
-;   load_ext_name %callee_i8+0, %rax
-;   return_call_unknown %rax (0) %rdi=%rdi
+;   load_ext_name %callee_i8+0, %rcx
+;   return_call_unknown %rcx (0) tmp=%rdx %rdi=%rdi
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   pushq %rbp
 ;   movq %rsp, %rbp
 ; block1: ; offset 0x4
-;   movabsq $0, %rax ; reloc_external Abs8 %callee_i8 0
+;   movabsq $0, %rcx ; reloc_external Abs8 %callee_i8 0
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   jmpq *%rax
+;   jmpq *%rcx
 
 ;;;; Test passing fewer arguments on the stack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -243,7 +243,7 @@ block0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i32, v5: i32, v6: i32, v7: i32, v
 ;   movq    rbp(stack args max - 24), %r9
 ;   movq    rbp(stack args max - 16), %rax
 ;   movl    %eax, rbp(stack args max - 16)
-;   return_call_known TestCase(%one_stack_arg) (16) %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
+;   return_call_known TestCase(%one_stack_arg) (16) tmp=%r10 %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -260,8 +260,8 @@ block0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i32, v5: i32, v6: i32, v7: i32, v
 ;   movl %eax, 0x20(%rbp)
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   movq (%rsp), %r11
-;   movq %r11, 0x10(%rsp)
+;   movq (%rsp), %r10
+;   movq %r10, 0x10(%rsp)
 ;   addq $0x10, %rsp
 ;   jmp 0x35 ; reloc_external CallPCRel4 %one_stack_arg -4
 
@@ -279,7 +279,7 @@ block0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i32, v5: i32, v6: i32, v7: i32, v
 ;   movq    rbp(stack args max - 32), %r10
 ;   movq    rbp(stack args max - 24), %rsi
 ;   movq    rbp(stack args max - 16), %rdi
-;   return_call_known TestCase(%callee_i8) (0) %rdi=%rdi
+;   return_call_known TestCase(%callee_i8) (0) tmp=%rdx %rdi=%rdi
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -291,8 +291,8 @@ block0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i32, v5: i32, v6: i32, v7: i32, v
 ;   movq 0x20(%rbp), %rdi
 ;   movq %rbp, %rsp
 ;   popq %rbp
-;   movq (%rsp), %r11
-;   movq %r11, 0x20(%rsp)
+;   movq (%rsp), %rdx
+;   movq %rdx, 0x20(%rsp)
 ;   addq $0x20, %rsp
 ;   jmp 0x26 ; reloc_external CallPCRel4 %callee_i8 -4
 
@@ -325,7 +325,7 @@ block0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i32, v5: i32, v6: i32):
 ;   movl    %esi, rbp(stack args max - 16)
 ;   movq    %rsi, %rdi
 ;   movq    %r10, %rsi
-;   return_call_known TestCase(%call_one_stack_arg) (32) %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
+;   return_call_known TestCase(%call_one_stack_arg) (32) tmp=%r10 %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -477,60 +477,60 @@ block0:
 ;   movq    %r8, rsp(64 + virtual offset)
 ;   movl    $35, %r9d
 ;   movq    %r9, rsp(56 + virtual offset)
-;   movl    $40, %esi
-;   movq    %rsi, rsp(48 + virtual offset)
-;   movl    $45, %eax
-;   movl    $50, %r10d
-;   movl    $55, %r12d
-;   movl    $60, %r13d
-;   movl    $65, %r14d
-;   movl    $70, %r15d
-;   movl    $75, %ebx
+;   movl    $40, %eax
+;   movl    $45, %r10d
+;   movl    $50, %r11d
+;   movl    $55, %r13d
+;   movl    $60, %r14d
+;   movl    $65, %r15d
+;   movl    $70, %ebx
+;   movl    $75, %r12d
 ;   movl    $80, %edi
 ;   movl    $85, %esi
+;   movq    %rsi, rsp(48 + virtual offset)
 ;   movl    $90, %edx
 ;   movl    $95, %ecx
 ;   movl    $100, %r8d
 ;   movl    $105, %r9d
-;   movl    $110, %r11d
-;   movq    %r11, rsp(40 + virtual offset)
-;   movl    $115, %r11d
-;   movq    %r11, rsp(32 + virtual offset)
-;   movl    $120, %r11d
-;   movq    %r11, rsp(24 + virtual offset)
-;   movl    $125, %r11d
-;   movq    %r11, rsp(16 + virtual offset)
-;   movl    $130, %r11d
-;   movq    %r11, rsp(8 + virtual offset)
-;   movl    $135, %r11d
-;   movq    %r11, rsp(0 + virtual offset)
-;   movq    rsp(48 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 160)
-;   movq    %rax, rbp(stack args max - 152)
-;   movq    %r10, rbp(stack args max - 144)
-;   movq    %r12, rbp(stack args max - 136)
-;   movq    %r13, rbp(stack args max - 128)
-;   movq    %r14, rbp(stack args max - 120)
-;   movq    %r15, rbp(stack args max - 112)
-;   movq    %rbx, rbp(stack args max - 104)
+;   movl    $110, %esi
+;   movq    %rsi, rsp(40 + virtual offset)
+;   movl    $115, %esi
+;   movq    %rsi, rsp(32 + virtual offset)
+;   movl    $120, %esi
+;   movq    %rsi, rsp(24 + virtual offset)
+;   movl    $125, %esi
+;   movq    %rsi, rsp(16 + virtual offset)
+;   movl    $130, %esi
+;   movq    %rsi, rsp(8 + virtual offset)
+;   movl    $135, %esi
+;   movq    %rsi, rsp(0 + virtual offset)
+;   movq    %rax, rbp(stack args max - 160)
+;   movq    %r10, rbp(stack args max - 152)
+;   movq    %r11, rbp(stack args max - 144)
+;   movq    %r13, rbp(stack args max - 136)
+;   movq    %r14, rbp(stack args max - 128)
+;   movq    %r15, rbp(stack args max - 120)
+;   movq    %rbx, rbp(stack args max - 112)
+;   movq    %r12, rbp(stack args max - 104)
 ;   movq    %rdi, rbp(stack args max - 96)
-;   movq    %rsi, rbp(stack args max - 88)
+;   movq    rsp(48 + virtual offset), %rdi
+;   movq    %rdi, rbp(stack args max - 88)
 ;   movq    %rdx, rbp(stack args max - 80)
 ;   movq    %rcx, rbp(stack args max - 72)
 ;   movq    %r8, rbp(stack args max - 64)
 ;   movq    %r9, rbp(stack args max - 56)
-;   movq    rsp(40 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 48)
-;   movq    rsp(32 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 40)
-;   movq    rsp(24 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 32)
-;   movq    rsp(16 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 24)
-;   movq    rsp(8 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 16)
-;   movq    rsp(0 + virtual offset), %r11
-;   movq    %r11, rbp(stack args max - 8)
+;   movq    rsp(40 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 48)
+;   movq    rsp(32 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 40)
+;   movq    rsp(24 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 32)
+;   movq    rsp(16 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 24)
+;   movq    rsp(8 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 16)
+;   movq    rsp(0 + virtual offset), %rsi
+;   movq    %rsi, rbp(stack args max - 8)
 ;   load_ext_name %tail_callee_stack_args+0, %rax
 ;   movq    rsp(72 + virtual offset), %rcx
 ;   movq    rsp(80 + virtual offset), %rdx
@@ -538,7 +538,7 @@ block0:
 ;   movq    rsp(96 + virtual offset), %rdi
 ;   movq    rsp(64 + virtual offset), %r8
 ;   movq    rsp(56 + virtual offset), %r9
-;   return_call_unknown %rax (160) %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
+;   return_call_unknown %rax (160) tmp=%r10 %rdi=%rdi %rsi=%rsi %rdx=%rdx %rcx=%rcx %r8=%r8 %r9=%r9
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
@@ -569,60 +569,60 @@ block0:
 ;   movq %r8, 0x40(%rsp)
 ;   movl $0x23, %r9d
 ;   movq %r9, 0x38(%rsp)
-;   movl $0x28, %esi
-;   movq %rsi, 0x30(%rsp)
-;   movl $0x2d, %eax
-;   movl $0x32, %r10d
-;   movl $0x37, %r12d
-;   movl $0x3c, %r13d
-;   movl $0x41, %r14d
-;   movl $0x46, %r15d
-;   movl $0x4b, %ebx
+;   movl $0x28, %eax
+;   movl $0x2d, %r10d
+;   movl $0x32, %r11d
+;   movl $0x37, %r13d
+;   movl $0x3c, %r14d
+;   movl $0x41, %r15d
+;   movl $0x46, %ebx
+;   movl $0x4b, %r12d
 ;   movl $0x50, %edi
 ;   movl $0x55, %esi
+;   movq %rsi, 0x30(%rsp)
 ;   movl $0x5a, %edx
 ;   movl $0x5f, %ecx
 ;   movl $0x64, %r8d
 ;   movl $0x69, %r9d
-;   movl $0x6e, %r11d
-;   movq %r11, 0x28(%rsp)
-;   movl $0x73, %r11d
-;   movq %r11, 0x20(%rsp)
-;   movl $0x78, %r11d
-;   movq %r11, 0x18(%rsp)
-;   movl $0x7d, %r11d
-;   movq %r11, 0x10(%rsp)
-;   movl $0x82, %r11d
-;   movq %r11, 8(%rsp)
-;   movl $0x87, %r11d
-;   movq %r11, (%rsp)
-;   movq 0x30(%rsp), %r11
-;   movq %r11, 0x10(%rbp)
-;   movq %rax, 0x18(%rbp)
-;   movq %r10, 0x20(%rbp)
-;   movq %r12, 0x28(%rbp)
-;   movq %r13, 0x30(%rbp)
-;   movq %r14, 0x38(%rbp)
-;   movq %r15, 0x40(%rbp)
-;   movq %rbx, 0x48(%rbp)
+;   movl $0x6e, %esi
+;   movq %rsi, 0x28(%rsp)
+;   movl $0x73, %esi
+;   movq %rsi, 0x20(%rsp)
+;   movl $0x78, %esi
+;   movq %rsi, 0x18(%rsp)
+;   movl $0x7d, %esi
+;   movq %rsi, 0x10(%rsp)
+;   movl $0x82, %esi
+;   movq %rsi, 8(%rsp)
+;   movl $0x87, %esi
+;   movq %rsi, (%rsp)
+;   movq %rax, 0x10(%rbp)
+;   movq %r10, 0x18(%rbp)
+;   movq %r11, 0x20(%rbp)
+;   movq %r13, 0x28(%rbp)
+;   movq %r14, 0x30(%rbp)
+;   movq %r15, 0x38(%rbp)
+;   movq %rbx, 0x40(%rbp)
+;   movq %r12, 0x48(%rbp)
 ;   movq %rdi, 0x50(%rbp)
-;   movq %rsi, 0x58(%rbp)
+;   movq 0x30(%rsp), %rdi
+;   movq %rdi, 0x58(%rbp)
 ;   movq %rdx, 0x60(%rbp)
 ;   movq %rcx, 0x68(%rbp)
 ;   movq %r8, 0x70(%rbp)
 ;   movq %r9, 0x78(%rbp)
-;   movq 0x28(%rsp), %r11
-;   movq %r11, 0x80(%rbp)
-;   movq 0x20(%rsp), %r11
-;   movq %r11, 0x88(%rbp)
-;   movq 0x18(%rsp), %r11
-;   movq %r11, 0x90(%rbp)
-;   movq 0x10(%rsp), %r11
-;   movq %r11, 0x98(%rbp)
-;   movq 8(%rsp), %r11
-;   movq %r11, 0xa0(%rbp)
-;   movq (%rsp), %r11
-;   movq %r11, 0xa8(%rbp)
+;   movq 0x28(%rsp), %rsi
+;   movq %rsi, 0x80(%rbp)
+;   movq 0x20(%rsp), %rsi
+;   movq %rsi, 0x88(%rbp)
+;   movq 0x18(%rsp), %rsi
+;   movq %rsi, 0x90(%rbp)
+;   movq 0x10(%rsp), %rsi
+;   movq %rsi, 0x98(%rbp)
+;   movq 8(%rsp), %rsi
+;   movq %rsi, 0xa0(%rbp)
+;   movq (%rsp), %rsi
+;   movq %rsi, 0xa8(%rbp)
 ;   movabsq $0, %rax ; reloc_external Abs8 %tail_callee_stack_args 0
 ;   movq 0x48(%rsp), %rcx
 ;   movq 0x50(%rsp), %rdx
diff --git a/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif b/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif
index 1469721e4..93ccab77f 100644
--- a/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif
+++ b/cranelift/filetests/filetests/isa/x64/tail-call-conv.clif
@@ -1,4 +1,6 @@
 test compile precise-output
+set enable_llvm_abi_extensions
+set preserve_frame_pointers
 target x86_64
 
 ;; Test the `tail` calling convention with non-tail calls and stack arguments.
@@ -893,3 +895,44 @@ block0:
 ;   popq %rbp
 ;   retq
 
+
+;; Test that tail calls that shrink the argument area don't clobber the location
+;; of an indirect jump
+
+function %tail_call_indirect_with_shrink(f64, f64, i8, i32 sext, i128, i32 sext, i128, i32, i128) -> i8 tail {
+    sig0 = () -> i8 tail
+    fn0 = %callee_simple sig0
+
+block0(v0: f64, v1: f64, v2: i8, v3: i32, v4: i128, v5: i32, v6: i128, v7: i32, v8: i128):
+    v14 = func_addr.i64 fn0
+    return_call_indirect sig0, v14()
+}
+
+; VCode:
+;   pushq   %rbp
+;   movq    %rsp, %rbp
+; block0:
+;   movq    rbp(stack args max - 32), %rsi
+;   movq    rbp(stack args max - 24), %rax
+;   movq    rbp(stack args max - 16), %rdx
+;   movq    rbp(stack args max - 8), %r9
+;   load_ext_name %callee_simple+0, %rsi
+;   return_call_unknown %rsi (0) tmp=%rdi
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   pushq %rbp
+;   movq %rsp, %rbp
+; block1: ; offset 0x4
+;   movq 0x10(%rbp), %rsi
+;   movq 0x18(%rbp), %rax
+;   movq 0x20(%rbp), %rdx
+;   movq 0x28(%rbp), %r9
+;   movabsq $0, %rsi ; reloc_external Abs8 %callee_simple 0
+;   movq %rbp, %rsp
+;   popq %rbp
+;   movq (%rsp), %rdi
+;   movq %rdi, 0x20(%rsp)
+;   addq $0x20, %rsp
+;   jmpq *%rsi
+
diff --git a/cranelift/filetests/filetests/runtests/tail-call-conv.clif b/cranelift/filetests/filetests/runtests/tail-call-conv.clif
index 195bc82ac..94d6b6a75 100644
--- a/cranelift/filetests/filetests/runtests/tail-call-conv.clif
+++ b/cranelift/filetests/filetests/runtests/tail-call-conv.clif
@@ -1,5 +1,7 @@
 test interpret
 test run
+set enable_llvm_abi_extensions
+set preserve_frame_pointers
 target x86_64
 target aarch64
 target aarch64 sign_return_address
@@ -125,3 +127,24 @@ block0:
 }
 
 ; run: %tail_caller_stack_rets() == 135
+
+
+;; Test that tail calls that shrink the argument area don't clobber the location
+;; of an indirect jump
+
+function %g() -> i8 tail {
+block0:
+    v0 = iconst.i8 0
+    return v0
+}
+
+function %f(f64, f64, i8, i32 sext, i128, i32 sext, i128, i32, i128) -> i8 tail {
+    sig0 = () -> i8 tail
+    fn0 = %g sig0
+
+block0(v0: f64, v1: f64, v2: i8, v3: i32, v4: i128, v5: i32, v6: i128, v7: i32, v8: i128):
+    v14 = func_addr.i64 fn0
+    return_call_indirect sig0, v14()
+}
+
+; run: %f(0.0, 0.0, 0, 0, 0, 0, 0, 0, 0) == 0
diff --git a/cranelift/filetests/src/test_wasm/env.rs b/cranelift/filetests/src/test_wasm/env.rs
index f8b7d419f..dccc9c31f 100644
--- a/cranelift/filetests/src/test_wasm/env.rs
+++ b/cranelift/filetests/src/test_wasm/env.rs
@@ -79,12 +79,10 @@ impl<'data> ModuleEnvironment<'data> for ModuleEnv {
     }
 
     fn wasm_features(&self) -> wasmparser::WasmFeatures {
-        wasmparser::WasmFeatures {
-            memory64: true,
-            multi_memory: true,
-            relaxed_simd: true,
-            ..self.inner.wasm_features()
-        }
+        self.inner.wasm_features()
+            | wasmparser::WasmFeatures::MEMORY64
+            | wasmparser::WasmFeatures::MULTI_MEMORY
+            | wasmparser::WasmFeatures::RELAXED_SIMD
     }
 
     // ================================================================