Repository: bytecodealliance/wasmtime
Pull request #8402 (2024-04-18 18:04:07)

Fix x64 clobber offsets with unwind info
The stack offsets provided to `UnwindInst::SaveReg` instructions are relative to the beginning of the clobber area of the stack, and on main the x64 backend computes those offsets by assuming a SP-relative offset and then adjusting it. This is problematic, as any changes to the frame layout might require updates in two locations: where we compute the initial SP offset, and where we compute the adjustment to make the offset relative to the clobber area again.

This PR fixes this issue by making the offset we track in the loop always relative to the beginning of the clobber area, and adding the offset from SP only when emitting the store. This relies on the assumption that the clobber area is sized according to the constraints of the registers stored within it (16-byte alignment is required for floating point registers) but this assumption is enforced by existing behavior in `compute_clobber_size`.

This also fixes a long-standing bug with floating point register spill offsets, as the offset was saved before the alignment to a 16-byte offset was made, leaving the resulting unwind info potentially pointing at the wrong offset.
<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->


elliottt: > Didn't audit this super carefully, since I trust you and Jamey on this stuff more than myself right now since you've been digging into it very recently, but I wonder if we should (in a follow up at some point in the future) only adjust SP via a helper function that not only emits the SP adjustment, but also the unwind info metadata instruction at the same time.

Possibly, though in this case the offset is expected to be relative to part of the stack. Were you thinking that we'd have a helper that assumed all unwind offsets to be SP relative, and made the adjustment when emitting the underlying `UnwindInst`?

Patch:
diff --git a/cranelift/codegen/src/egraph.rs b/cranelift/codegen/src/egraph.rs
index 1f3f2a882..6b139b1ba 100644
--- a/cranelift/codegen/src/egraph.rs
+++ b/cranelift/codegen/src/egraph.rs
@@ -209,7 +209,7 @@ where
                 }
             };
 
-            self.attach_constant_fact(inst, result);
+            self.attach_constant_fact(inst, result, ty);
 
             self.available_block[result] = self.get_available_block(inst);
             let opt_value = self.optimize_pure_enode(inst);
@@ -485,7 +485,7 @@ where
     /// Helper to propagate facts on constant values: if PCC is
     /// enabled, then unconditionally add a fact attesting to the
     /// Value's concrete value.
-    fn attach_constant_fact(&mut self, inst: Inst, value: Value) {
+    fn attach_constant_fact(&mut self, inst: Inst, value: Value, ty: Type) {
         if self.flags.enable_pcc() {
             if let InstructionData::UnaryImm {
                 opcode: Opcode::Iconst,
@@ -493,7 +493,8 @@ where
             } = self.func.dfg.insts[inst]
             {
                 let imm: i64 = imm.into();
-                self.func.dfg.facts[value] = Some(Fact::constant(64, imm as u64));
+                self.func.dfg.facts[value] =
+                    Some(Fact::constant(ty.bits().try_into().unwrap(), imm as u64));
             }
         }
     }
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index 060af446b..1c3ffc37b 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -109,10 +109,6 @@ impl ABIMachineSpec for AArch64MachineDeps {
         add_ret_area_ptr: bool,
         mut args: ArgsAccumulator,
     ) -> CodegenResult<(u32, Option<usize>)> {
-        if matches!(call_conv, isa::CallConv::Tail) {
-            return compute_arg_locs_tail(params, add_ret_area_ptr, args);
-        }
-
         let is_apple_cc = call_conv.extends_apple_aarch64();
 
         // See AArch64 ABI (https://github.com/ARM-software/abi-aa/blob/2021Q1/aapcs64/aapcs64.rst#64parameter-passing), sections 6.4.
@@ -129,7 +125,20 @@ impl ABIMachineSpec for AArch64MachineDeps {
         // break our other invariants that the stack is always allocated in
         // 16-bytes chunks.
 
-        let mut next_xreg = 0;
+        let mut next_xreg = if call_conv == isa::CallConv::Tail {
+            // We reserve `x0` for the return area pointer. For simplicity, we
+            // reserve it even when there is no return area pointer needed. This
+            // also means that identity functions don't have to shuffle arguments to
+            // different return registers because we shifted all argument register
+            // numbers down by one to make space for the return area pointer.
+            //
+            // Also, we cannot use all allocatable GPRs as arguments because we need
+            // at least one allocatable register for holding the callee address in
+            // indirect calls. So skip `x1` also, reserving it for that role.
+            2
+        } else {
+            0
+        };
         let mut next_vreg = 0;
         let mut next_stack: u32 = 0;
 
@@ -155,6 +164,20 @@ impl ABIMachineSpec for AArch64MachineDeps {
 
             let (rcs, reg_types) = Inst::rc_for_type(param.value_type)?;
 
+            if matches!(
+                param.purpose,
+                ir::ArgumentPurpose::StructArgument(_)
+                    | ir::ArgumentPurpose::StructReturn
+                    | ir::ArgumentPurpose::StackLimit
+            ) {
+                assert!(
+                    call_conv != isa::CallConv::Tail,
+                    "support for {:?} parameters is not implemented for the `tail` calling \
+                    convention yet",
+                    param.purpose,
+                );
+            }
+
             if let ir::ArgumentPurpose::StructArgument(size) = param.purpose {
                 assert_eq!(args_or_rets, ArgsOrRets::Args);
                 let offset = next_stack as i64;
@@ -335,21 +358,30 @@ impl ABIMachineSpec for AArch64MachineDeps {
 
         let extra_arg = if add_ret_area_ptr {
             debug_assert!(args_or_rets == ArgsOrRets::Args);
-            if next_xreg < max_per_class_reg_vals && remaining_reg_vals > 0 {
+            if call_conv == isa::CallConv::Tail {
                 args.push_non_formal(ABIArg::reg(
-                    xreg(next_xreg).to_real_reg().unwrap(),
+                    xreg_preg(0).into(),
                     I64,
                     ir::ArgumentExtension::None,
                     ir::ArgumentPurpose::Normal,
                 ));
             } else {
-                args.push_non_formal(ABIArg::stack(
-                    next_stack as i64,
-                    I64,
-                    ir::ArgumentExtension::None,
-                    ir::ArgumentPurpose::Normal,
-                ));
-                next_stack += 8;
+                if next_xreg < max_per_class_reg_vals && remaining_reg_vals > 0 {
+                    args.push_non_formal(ABIArg::reg(
+                        xreg(next_xreg).to_real_reg().unwrap(),
+                        I64,
+                        ir::ArgumentExtension::None,
+                        ir::ArgumentPurpose::Normal,
+                    ));
+                } else {
+                    args.push_non_formal(ABIArg::stack(
+                        next_stack as i64,
+                        I64,
+                        ir::ArgumentExtension::None,
+                        ir::ArgumentPurpose::Normal,
+                    ));
+                    next_stack += 8;
+                }
             }
             Some(args.args().len() - 1)
         } else {
@@ -1132,11 +1164,8 @@ impl ABIMachineSpec for AArch64MachineDeps {
         }
     }
 
-    fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> PRegSet {
-        match call_conv_of_callee {
-            isa::CallConv::Tail => ALL_CLOBBERS,
-            _ => DEFAULT_AAPCS_CLOBBERS,
-        }
+    fn get_regs_clobbered_by_call(_call_conv: isa::CallConv) -> PRegSet {
+        DEFAULT_AAPCS_CLOBBERS
     }
 
     fn get_ext_mode(
@@ -1325,124 +1354,6 @@ impl AArch64CallSite {
     }
 }
 
-fn compute_arg_locs_tail(
-    params: &[ir::AbiParam],
-    add_ret_area_ptr: bool,
-    mut args: ArgsAccumulator,
-) -> CodegenResult<(u32, Option<usize>)> {
-    let mut xregs = ALL_CLOBBERS
-        .into_iter()
-        .filter(|r| r.class() == RegClass::Int)
-        // We reserve `x0` for the return area pointer. For simplicity, we
-        // reserve it even when there is no return area pointer needed. This
-        // also means that identity functions don't have to shuffle arguments to
-        // different return registers because we shifted all argument register
-        // numbers down by one to make space for the return area pointer.
-        //
-        // Also, we cannot use all allocatable GPRs as arguments because we need
-        // at least one allocatable register for holding the callee address in
-        // indirect calls. So skip `x1` also, reserving it for that role.
-        .skip(2);
-
-    let mut vregs = ALL_CLOBBERS
-        .into_iter()
-        .filter(|r| r.class() == RegClass::Float);
-
-    let mut next_stack: u32 = 0;
-
-    // Get the next stack slot for the given type.
-    let stack = |next_stack: &mut u32, ty: ir::Type| {
-        *next_stack = align_to(*next_stack, ty.bytes());
-        let offset = i64::from(*next_stack);
-        *next_stack += ty.bytes();
-        ABIArgSlot::Stack {
-            offset,
-            ty,
-            extension: ir::ArgumentExtension::None,
-        }
-    };
-
-    // Get the next `x` register available, or a stack slot if all are in use.
-    let mut xreg = |next_stack: &mut u32, ty| {
-        xregs
-            .next()
-            .map(|reg| ABIArgSlot::Reg {
-                reg: reg.into(),
-                ty,
-                extension: ir::ArgumentExtension::None,
-            })
-            .unwrap_or_else(|| stack(next_stack, ty))
-    };
-
-    // Get the next `v` register available, or a stack slot if all are in use.
-    let mut vreg = |next_stack: &mut u32, ty| {
-        vregs
-            .next()
-            .map(|reg| ABIArgSlot::Reg {
-                reg: reg.into(),
-                ty,
-                extension: ir::ArgumentExtension::None,
-            })
-            .unwrap_or_else(|| stack(next_stack, ty))
-    };
-
-    for param in params {
-        assert!(
-            legal_type_for_machine(param.value_type),
-            "Invalid type for AArch64: {:?}",
-            param.value_type
-        );
-
-        match param.purpose {
-            ir::ArgumentPurpose::Normal | ir::ArgumentPurpose::VMContext => {}
-            ir::ArgumentPurpose::StructArgument(_)
-            | ir::ArgumentPurpose::StructReturn
-            | ir::ArgumentPurpose::StackLimit => unimplemented!(
-                "support for {:?} parameters is not implemented for the `tail` \
-                 calling convention yet",
-                param.purpose,
-            ),
-        }
-
-        let (reg_classes, reg_types) = Inst::rc_for_type(param.value_type)?;
-        args.push(ABIArg::Slots {
-            slots: reg_classes
-                .iter()
-                .zip(reg_types)
-                .map(|(cls, ty)| match cls {
-                    RegClass::Int => xreg(&mut next_stack, *ty),
-                    RegClass::Float => vreg(&mut next_stack, *ty),
-                    RegClass::Vector => unreachable!(),
-                })
-                .collect(),
-            purpose: param.purpose,
-        });
-    }
-
-    let ret_ptr = if add_ret_area_ptr {
-        let idx = args.args().len();
-        args.push(ABIArg::reg(
-            xreg_preg(0).into(),
-            types::I64,
-            ir::ArgumentExtension::None,
-            ir::ArgumentPurpose::Normal,
-        ));
-        Some(idx)
-    } else {
-        None
-    };
-
-    next_stack = align_to(next_stack, 16);
-
-    // To avoid overflow issues, limit the arg/return size to something
-    // reasonable -- here, 128 MB.
-    if next_stack > STACK_ARG_RET_SIZE_LIMIT {
-        return Err(CodegenError::ImplLimitExceeded);
-    }
-
-    Ok((next_stack, ret_ptr))
-}
-
 /// Is this type supposed to be seen on this machine? E.g. references of the
 /// wrong width are invalid.
 fn legal_type_for_machine(ty: Type) -> bool {
@@ -1455,15 +1366,11 @@ fn legal_type_for_machine(ty: Type) -> bool {
 /// Is the given register saved in the prologue if clobbered, i.e., is it a
 /// callee-save?
 fn is_reg_saved_in_prologue(
-    call_conv: isa::CallConv,
+    _call_conv: isa::CallConv,
     enable_pinned_reg: bool,
     sig: &Signature,
     r: RealReg,
 ) -> bool {
-    if call_conv == isa::CallConv::Tail {
-        return false;
-    }
-
     // FIXME: We need to inspect whether a function is returning Z or P regs too.
     let save_z_regs = sig
         .params
@@ -1573,72 +1480,6 @@ const fn default_aapcs_clobbers() -> PRegSet {
 
 const DEFAULT_AAPCS_CLOBBERS: PRegSet = default_aapcs_clobbers();
 
-// For calling conventions that clobber all registers.
-const ALL_CLOBBERS: PRegSet = PRegSet::empty()
-    .with(xreg_preg(0))
-    .with(xreg_preg(1))
-    .with(xreg_preg(2))
-    .with(xreg_preg(3))
-    .with(xreg_preg(4))
-    .with(xreg_preg(5))
-    .with(xreg_preg(6))
-    .with(xreg_preg(7))
-    .with(xreg_preg(8))
-    .with(xreg_preg(9))
-    .with(xreg_preg(10))
-    .with(xreg_preg(11))
-    .with(xreg_preg(12))
-    .with(xreg_preg(13))
-    .with(xreg_preg(14))
-    .with(xreg_preg(15))
-    // Cranelift reserves x16 and x17 as unallocatable scratch registers.
-    //
-    // x18 can be used by the platform and therefore is not allocatable.
-    .with(xreg_preg(19))
-    .with(xreg_preg(20))
-    .with(xreg_preg(21))
-    .with(xreg_preg(22))
-    .with(xreg_preg(23))
-    .with(xreg_preg(24))
-    .with(xreg_preg(25))
-    .with(xreg_preg(26))
-    .with(xreg_preg(27))
-    .with(xreg_preg(28))
-    // NB: x29 is the FP, x30 is the link register, and x31 is the SP. None of
-    // these are allocatable.
-    .with(vreg_preg(0))
-    .with(vreg_preg(1))
-    .with(vreg_preg(2))
-    .with(vreg_preg(3))
-    .with(vreg_preg(4))
-    .with(vreg_preg(5))
-    .with(vreg_preg(6))
-    .with(vreg_preg(7))
-    .with(vreg_preg(8))
-    .with(vreg_preg(9))
-    .with(vreg_preg(10))
-    .with(vreg_preg(11))
-    .with(vreg_preg(12))
-    .with(vreg_preg(13))
-    .with(vreg_preg(14))
-    .with(vreg_preg(15))
-    .with(vreg_preg(16))
-    .with(vreg_preg(17))
-    .with(vreg_preg(18))
-    .with(vreg_preg(19))
-    .with(vreg_preg(20))
-    .with(vreg_preg(21))
-    .with(vreg_preg(22))
-    .with(vreg_preg(23))
-    .with(vreg_preg(24))
-    .with(vreg_preg(25))
-    .with(vreg_preg(26))
-    .with(vreg_preg(27))
-    .with(vreg_preg(28))
-    .with(vreg_preg(29))
-    .with(vreg_preg(30))
-    .with(vreg_preg(31));
-
 fn create_reg_env(enable_pinned_reg: bool) -> MachineEnv {
     fn preg(r: Reg) -> PReg {
         r.to_real_reg().unwrap().into()
diff --git a/cranelift/codegen/src/isa/aarch64/lower/isle.rs b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
index 36eda61e6..2f3520fe8 100644
--- a/cranelift/codegen/src/isa/aarch64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/aarch64/lower/isle.rs
@@ -386,6 +386,8 @@ impl Context for IsleContext<'_, '_, MInst, AArch64Backend> {
                 let imm = MoveWideConst::maybe_with_shift(imm16 as u16, shift).unwrap();
                 self.emit(&MInst::MovK { rd, rn, imm, size });
                 if pcc {
+                    let mask = 0xffff << shift;
+                    running_value &= !mask;
                     running_value |= imm16 << shift;
                     self.lower_ctx
                         .add_range_fact(rd.to_reg(), 64, running_value, running_value);
diff --git a/cranelift/codegen/src/isa/aarch64/pcc.rs b/cranelift/codegen/src/isa/aarch64/pcc.rs
index e8e38232b..be8717725 100644
--- a/cranelift/codegen/src/isa/aarch64/pcc.rs
+++ b/cranelift/codegen/src/isa/aarch64/pcc.rs
@@ -304,8 +304,9 @@ pub(crate) fn check(
         Inst::MovK { rd, rn, imm, .. } => {
             let input = get_fact_or_default(vcode, rn, 64);
             if let Some(input_constant) = input.as_const(64) {
+                let mask = 0xffff << (imm.shift * 16);
                 let constant = u64::from(imm.bits) << (imm.shift * 16);
-                let constant = input_constant | constant;
+                let constant = (input_constant & !mask) | constant;
                 check_constant(ctx, vcode, rd, 64, constant)
             } else {
                 check_output(ctx, vcode, rd, &[], |_vcode| {
diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs
index 4f13df1de..ea2672328 100644
--- a/cranelift/codegen/src/isa/x64/abi.rs
+++ b/cranelift/codegen/src/isa/x64/abi.rs
@@ -723,35 +723,32 @@ impl ABIMachineSpec for X64ABIMachineSpec {
 
         // Store each clobbered register in order at offsets from RSP,
         // placing them above the fixed frame slots.
-        let mut cur_offset =
+        let clobber_offset =
             frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size;
+        let mut cur_offset = 0;
         for reg in &frame_layout.clobbered_callee_saves {
             let r_reg = reg.to_reg();
-            let off = cur_offset;
-            match r_reg.class() {
-                RegClass::Int => {
-                    insts.push(Inst::store(
-                        types::I64,
-                        r_reg.into(),
-                        Amode::imm_reg(cur_offset.try_into().unwrap(), regs::rsp()),
-                    ));
-                    cur_offset += 8;
-                }
-                RegClass::Float => {
-                    cur_offset = align_to(cur_offset, 16);
-                    insts.push(Inst::store(
-                        types::I8X16,
-                        r_reg.into(),
-                        Amode::imm_reg(cur_offset.try_into().unwrap(), regs::rsp()),
-                    ));
-                    cur_offset += 16;
-                }
+            let ty = match r_reg.class() {
+                RegClass::Int => types::I64,
+                RegClass::Float => types::I8X16,
                 RegClass::Vector => unreachable!(),
             };
+
+            // Align to 8 or 16 bytes as required by the storage type of the clobber.
+            cur_offset = align_to(cur_offset, ty.bytes());
+            let off = cur_offset;
+            cur_offset += ty.bytes();
+
+            insts.push(Inst::store(
+                ty,
+                r_reg.into(),
+                Amode::imm_reg(i32::try_from(off + clobber_offset).unwrap(), regs::rsp()),
+            ));
+
             if flags.unwind_info() {
                 insts.push(Inst::Unwind {
                     inst: UnwindInst::SaveReg {
-                        clobber_offset: off - frame_layout.fixed_frame_storage_size,
+                        clobber_offset: off,
                         reg: r_reg,
                     },
                 });
@@ -775,26 +772,23 @@ impl ABIMachineSpec for X64ABIMachineSpec {
             frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size;
         for reg in &frame_layout.clobbered_callee_saves {
             let rreg = reg.to_reg();
-            match rreg.class() {
-                RegClass::Int => {
-                    insts.push(Inst::mov64_m_r(
-                        Amode::imm_reg(cur_offset.try_into().unwrap(), regs::rsp()),
-                        Writable::from_reg(rreg.into()),
-                    ));
-                    cur_offset += 8;
-                }
-                RegClass::Float => {
-                    cur_offset = align_to(cur_offset, 16);
-                    insts.push(Inst::load(
-                        types::I8X16,
-                        Amode::imm_reg(cur_offset.try_into().unwrap(), regs::rsp()),
-                        Writable::from_reg(rreg.into()),
-                        ExtKind::None,
-                    ));
-                    cur_offset += 16;
-                }
+            let ty = match rreg.class() {
+                RegClass::Int => types::I64,
+                RegClass::Float => types::I8X16,
                 RegClass::Vector => unreachable!(),
-            }
+            };
+
+            // Align to 8 or 16 bytes as required by the storage type of the clobber.
+            cur_offset = align_to(cur_offset, ty.bytes());
+
+            insts.push(Inst::load(
+                ty,
+                Amode::imm_reg(cur_offset.try_into().unwrap(), regs::rsp()),
+                Writable::from_reg(rreg.into()),
+                ExtKind::None,
+            ));
+
+            cur_offset += ty.bytes();
         }
 
         let stack_size = frame_layout.fixed_frame_storage_size
diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs
index 6c7f41134..5f63a2194 100644
--- a/cranelift/codegen/src/isa/x64/lower.rs
+++ b/cranelift/codegen/src/isa/x64/lower.rs
@@ -178,8 +178,7 @@ fn emit_vm_call(
     assert_eq!(inputs.len(), abi.num_args(ctx.sigs()));
 
     for (i, input) in inputs.iter().enumerate() {
-        let moves = abi.gen_arg(ctx, i, ValueRegs::one(*input));
-        abi.emit_arg_moves(ctx, moves);
+        abi.gen_arg(ctx, i, ValueRegs::one(*input));
     }
 
     let mut retval_insts: SmallInstVec<_> = smallvec![];
diff --git a/cranelift/codegen/src/machinst/abi.rs b/cranelift/codegen/src/machinst/abi.rs
index b4edf0323..362e47736 100644
--- a/cranelift/codegen/src/machinst/abi.rs
+++ b/cranelift/codegen/src/machinst/abi.rs
@@ -2051,19 +2051,6 @@ impl<M: ABIMachineSpec> Callee<M> {
     }
 }
 
-/// The register or stack slot location of an argument.
-#[derive(Clone, Debug)]
-pub enum ArgLoc {
-    /// The physical register that the value will be passed through.
-    Reg(PReg),
-
-    /// The offset into the argument area where this value will be passed. It's up to the consumer
-    /// of the `ArgLoc::Stack` variant to decide how to find the argument area that the `offset`
-    /// value is relative to. Depending on the abi, this may end up being relative to SP or FP, for
-    /// example with a tail call where the frame is reused.
-    Stack { offset: i64, ty: ir::Type },
-}
-
 /// An input argument to a call instruction: the vreg that is used,
 /// and the preg it is constrained to (per the ABI).
 #[derive(Clone, Debug)]
@@ -2298,38 +2285,17 @@ impl<M: ABIMachineSpec> CallSite<M> {
         }
     }
 
-    /// Emit moves or uses for the moves list generated by [`Self::gen_arg`].
-    pub fn emit_arg_moves(&mut self, ctx: &mut Lower<M::I>, moves: SmallVec<[(VReg, ArgLoc); 2]>) {
-        for (vreg, loc) in moves {
-            let vreg = vreg.into();
-            match loc {
-                ArgLoc::Reg(preg) => self.uses.push(CallArgPair {
-                    vreg,
-                    preg: preg.into(),
-                }),
-                ArgLoc::Stack { offset, ty } => {
-                    let amode = if self.is_tail_call() {
-                        StackAMode::IncomingArg(offset, ctx.sigs()[self.sig].sized_stack_arg_space)
-                    } else {
-                        StackAMode::OutgoingArg(offset)
-                    };
-                    ctx.emit(M::gen_store_stack(amode, vreg, ty))
-                }
-            }
-        }
-    }
-
     /// Add a constraint for an argument value from a source register.
     /// For large arguments with associated stack buffer, this may
     /// load the address of the buffer into the argument register, if
     /// required by the ABI.
-    pub fn gen_arg(
-        &mut self,
-        ctx: &mut Lower<M::I>,
-        idx: usize,
-        from_regs: ValueRegs<Reg>,
-    ) -> SmallVec<[(VReg, ArgLoc); 2]> {
-        let mut locs = smallvec![];
+    pub fn gen_arg(&mut self, ctx: &mut Lower<M::I>, idx: usize, from_regs: ValueRegs<Reg>) {
+        let stack_arg_space = ctx.sigs()[self.sig].sized_stack_arg_space;
+        let stack_arg = if self.is_tail_call() {
+            StackAMode::IncomingArg
+        } else {
+            |offset, _| StackAMode::OutgoingArg(offset)
+        };
         let word_rc = M::word_reg_class();
         let word_bits = M::word_bits() as usize;
 
@@ -2342,7 +2308,9 @@ impl<M: ABIMachineSpec> CallSite<M> {
                             reg, ty, extension, ..
                         } => {
                             let ext = M::get_ext_mode(ctx.sigs()[self.sig].call_conv, extension);
-                            if ext != ir::ArgumentExtension::None && ty_bits(ty) < word_bits {
+                            let vreg = if ext != ir::ArgumentExtension::None
+                                && ty_bits(ty) < word_bits
+                            {
                                 assert_eq!(word_rc, reg.class());
                                 let signed = match ext {
                                     ir::ArgumentExtension::Uext => false,
@@ -2358,7 +2326,7 @@ impl<M: ABIMachineSpec> CallSite<M> {
                                     ty_bits(ty) as u8,
                                     word_bits as u8,
                                 ));
-                                locs.push((extend_result.to_reg().into(), ArgLoc::Reg(reg.into())));
+                                extend_result.to_reg()
                             } else if ty.is_ref() {
                                 // Reference-typed args need to be
                                 // passed as a copy; the original vreg
@@ -2367,10 +2335,13 @@ impl<M: ABIMachineSpec> CallSite<M> {
                                 let ref_copy = ctx.alloc_tmp(M::word_type()).only_reg().unwrap();
                                 ctx.emit(M::gen_move(ref_copy, *from_reg, M::word_type()));
 
-                                locs.push((ref_copy.to_reg().into(), ArgLoc::Reg(reg.into())));
+                                ref_copy.to_reg()
                             } else {
-                                locs.push((from_reg.into(), ArgLoc::Reg(reg.into())));
-                            }
+                                *from_reg
+                            };
+
+                            let preg = reg.into();
+                            self.uses.push(CallArgPair { vreg, preg });
                         }
                         &ABIArgSlot::Stack {
                             offset,
@@ -2401,7 +2372,11 @@ impl<M: ABIMachineSpec> CallSite<M> {
                                 } else {
                                     (*from_reg, ty)
                                 };
-                            locs.push((data.into(), ArgLoc::Stack { offset, ty }));
+                            ctx.emit(M::gen_store_stack(
+                                stack_arg(offset, stack_arg_space),
+                                data,
+                                ty,
+                            ));
                         }
                     }
                 }
@@ -2422,18 +2397,19 @@ impl<M: ABIMachineSpec> CallSite<M> {
                 ctx.emit(M::gen_get_stack_addr(amode, tmp));
                 let tmp = tmp.to_reg();
                 ctx.emit(M::gen_store_base_offset(tmp, 0, vreg, ty));
-                let loc = match pointer {
-                    ABIArgSlot::Reg { reg, .. } => ArgLoc::Reg(reg.into()),
-                    ABIArgSlot::Stack { offset, .. } => {
-                        let ty = M::word_type();
-                        ArgLoc::Stack { offset, ty }
-                    }
-                };
-                locs.push((tmp.into(), loc));
+                match pointer {
+                    ABIArgSlot::Reg { reg, .. } => self.uses.push(CallArgPair {
+                        vreg: tmp,
+                        preg: reg.into(),
+                    }),
+                    ABIArgSlot::Stack { offset, .. } => ctx.emit(M::gen_store_stack(
+                        stack_arg(offset, stack_arg_space),
+                        tmp,
+                        M::word_type(),
+                    )),
+                }
             }
         }
-
-        locs
     }
 
     /// Call `gen_arg` for each non-hidden argument and emit all instructions
@@ -2451,8 +2427,7 @@ impl<M: ABIMachineSpec> CallSite<M> {
             self.emit_copy_regs_to_buffer(ctx, i, *arg_regs);
         }
         for (i, value_regs) in arg_value_regs.iter().enumerate() {
-            let moves = self.gen_arg(ctx, i, *value_regs);
-            self.emit_arg_moves(ctx, moves);
+            self.gen_arg(ctx, i, *value_regs);
         }
     }
 
@@ -2464,8 +2439,7 @@ impl<M: ABIMachineSpec> CallSite<M> {
                 "if the tail callee has a return pointer, then the tail caller \
                  must as well",
             );
-            let moves = self.gen_arg(ctx, i.into(), ValueRegs::one(ret_area_ptr.to_reg()));
-            self.emit_arg_moves(ctx, moves);
+            self.gen_arg(ctx, i.into(), ValueRegs::one(ret_area_ptr.to_reg()));
         }
     }
 
@@ -2564,8 +2538,7 @@ impl<M: ABIMachineSpec> CallSite<M> {
                 StackAMode::OutgoingArg(ret_area_base),
                 rd,
             ));
-            let moves = self.gen_arg(ctx, i.into(), ValueRegs::one(rd.to_reg()));
-            self.emit_arg_moves(ctx, moves);
+            self.gen_arg(ctx, i.into(), ValueRegs::one(rd.to_reg()));
         }
 
         let (uses, defs) = (
diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs
index b74a60ed0..b771ed2bc 100644
--- a/cranelift/codegen/src/machinst/isle.rs
+++ b/cranelift/codegen/src/machinst/isle.rs
@@ -826,8 +826,7 @@ macro_rules! isle_prelude_method_helpers {
                 call_site.emit_copy_regs_to_buffer(self.lower_ctx, i, *arg_regs);
             }
             for (i, arg_regs) in arg_regs.iter().enumerate() {
-                let moves = call_site.gen_arg(self.lower_ctx, i, *arg_regs);
-                call_site.emit_arg_moves(self.lower_ctx, moves);
+                call_site.gen_arg(self.lower_ctx, i, *arg_regs);
             }
         }
 


Test patch:
diff --git a/cranelift/filetests/filetests/isa/aarch64/fuzzbug-60035.clif b/cranelift/filetests/filetests/isa/aarch64/fuzzbug-60035.clif
index ebddc1125..fa453d757 100644
--- a/cranelift/filetests/filetests/isa/aarch64/fuzzbug-60035.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/fuzzbug-60035.clif
@@ -15,33 +15,15 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
-;   stp x27, x28, [sp, #-16]!
-;   stp x25, x26, [sp, #-16]!
-;   stp x23, x24, [sp, #-16]!
-;   stp x21, x22, [sp, #-16]!
-;   stp x19, x20, [sp, #-16]!
-;   stp d14, d15, [sp, #-16]!
-;   stp d12, d13, [sp, #-16]!
-;   stp d10, d11, [sp, #-16]!
-;   stp d8, d9, [sp, #-16]!
-;   sub sp, sp, #16
+;   str x20, [sp, #-16]!
 ; block0:
 ;   load_ext_name x1, User(userextname0)+0
-;   str x1, [sp]
-;   ldr x1, [sp]
+;   mov x20, x1
+;   mov x1, x20
 ;   blr x1
-;   ldr x1, [sp]
+;   mov x1, x20
 ;   blr x1
-;   add sp, sp, #16
-;   ldp d8, d9, [sp], #16
-;   ldp d10, d11, [sp], #16
-;   ldp d12, d13, [sp], #16
-;   ldp d14, d15, [sp], #16
-;   ldp x19, x20, [sp], #16
-;   ldp x21, x22, [sp], #16
-;   ldp x23, x24, [sp], #16
-;   ldp x25, x26, [sp], #16
-;   ldp x27, x28, [sp], #16
+;   ldr x20, [sp], #16
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -49,36 +31,18 @@ block0:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-;   stp x27, x28, [sp, #-0x10]!
-;   stp x25, x26, [sp, #-0x10]!
-;   stp x23, x24, [sp, #-0x10]!
-;   stp x21, x22, [sp, #-0x10]!
-;   stp x19, x20, [sp, #-0x10]!
-;   stp d14, d15, [sp, #-0x10]!
-;   stp d12, d13, [sp, #-0x10]!
-;   stp d10, d11, [sp, #-0x10]!
-;   stp d8, d9, [sp, #-0x10]!
-;   sub sp, sp, #0x10
-; block1: ; offset 0x30
-;   ldr x1, #0x38
-;   b #0x40
+;   str x20, [sp, #-0x10]!
+; block1: ; offset 0xc
+;   ldr x1, #0x14
+;   b #0x1c
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 u1:7 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   stur x1, [sp]
-;   ldur x1, [sp]
+;   mov x20, x1
+;   mov x1, x20
 ;   blr x1
-;   ldur x1, [sp]
+;   mov x1, x20
 ;   blr x1
-;   add sp, sp, #0x10
-;   ldp d8, d9, [sp], #0x10
-;   ldp d10, d11, [sp], #0x10
-;   ldp d12, d13, [sp], #0x10
-;   ldp d14, d15, [sp], #0x10
-;   ldp x19, x20, [sp], #0x10
-;   ldp x21, x22, [sp], #0x10
-;   ldp x23, x24, [sp], #0x10
-;   ldp x25, x26, [sp], #0x10
-;   ldp x27, x28, [sp], #0x10
+;   ldr x20, [sp], #0x10
 ;   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 1e587efb4..96b682174 100644
--- a/cranelift/filetests/filetests/isa/aarch64/return-call-indirect.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/return-call-indirect.clif
@@ -217,92 +217,143 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
-;   sub sp, sp, #16
-;   ldr fp, [sp, #16]
+;   sub sp, sp, #160
+;   ldr fp, [sp, #160]
 ;   stp fp, lr, [sp]
 ;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
 ;   sub sp, sp, #16
 ; block0:
 ;   movz x2, #10
+;   str x2, [sp]
 ;   movz x3, #15
 ;   movz x4, #20
 ;   movz x5, #25
 ;   movz x6, #30
 ;   movz x7, #35
-;   movz x8, #40
-;   movz x9, #45
-;   movz x10, #50
-;   str x10, [sp]
-;   movz x11, #55
-;   movz x12, #60
-;   movz x13, #65
-;   movz x14, #70
-;   movz x15, #75
-;   movz x19, #80
-;   movz x20, #85
-;   movz x21, #90
-;   movz x22, #95
-;   movz x23, #100
-;   movz x24, #105
-;   movz x25, #110
-;   movz x26, #115
-;   movz x27, #120
-;   movz x28, #125
-;   movz x0, #130
-;   movz x1, #135
-;   load_ext_name x10, TestCase(%tail_callee_stack_args)+0
-;   str x0, [sp, #32]
-;   str x1, [sp, #40]
-;   mov x0, x10
-;   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
+;   movz x10, #40
+;   movz x11, #45
+;   movz x12, #50
+;   movz x13, #55
+;   movz x14, #60
+;   movz x15, #65
+;   movz x0, #70
+;   movz x1, #75
+;   movz x8, #80
+;   movz x9, #85
+;   movz x26, #90
+;   movz x27, #95
+;   movz x28, #100
+;   movz x21, #105
+;   movz x19, #110
+;   movz x20, #115
+;   movz x22, #120
+;   movz x23, #125
+;   movz x24, #130
+;   movz x25, #135
+;   load_ext_name x2, TestCase(%tail_callee_stack_args)+0
+;   str x10, [sp, #112]
+;   str x11, [sp, #120]
+;   str x12, [sp, #128]
+;   str x13, [sp, #136]
+;   str x14, [sp, #144]
+;   str x15, [sp, #152]
+;   str x0, [sp, #160]
+;   str x1, [sp, #168]
+;   str x8, [sp, #176]
+;   str x9, [sp, #184]
+;   str x26, [sp, #192]
+;   str x27, [sp, #200]
+;   str x28, [sp, #208]
+;   str x21, [sp, #216]
+;   str x19, [sp, #224]
+;   str x20, [sp, #232]
+;   str x22, [sp, #240]
+;   str x23, [sp, #248]
+;   str x24, [sp, #256]
+;   str x25, [sp, #264]
+;   mov x10, x2
+;   ldr x2, [sp]
+;   return_call_ind x10 new_stack_arg_size:160 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-;   sub sp, sp, #0x10
-;   ldur x29, [sp, #0x10]
+;   sub sp, sp, #0xa0
+;   ldur x29, [sp, #0xa0]
 ;   stp x29, x30, [sp]
 ;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
 ;   sub sp, sp, #0x10
-; block1: ; offset 0x1c
+; block1: ; offset 0x30
 ;   mov x2, #0xa
+;   stur x2, [sp]
 ;   mov x3, #0xf
 ;   mov x4, #0x14
 ;   mov x5, #0x19
 ;   mov x6, #0x1e
 ;   mov x7, #0x23
-;   mov x8, #0x28
-;   mov x9, #0x2d
-;   mov x10, #0x32
-;   stur x10, [sp]
-;   mov x11, #0x37
-;   mov x12, #0x3c
-;   mov x13, #0x41
-;   mov x14, #0x46
-;   mov x15, #0x4b
-;   mov x19, #0x50
-;   mov x20, #0x55
-;   mov x21, #0x5a
-;   mov x22, #0x5f
-;   mov x23, #0x64
-;   mov x24, #0x69
-;   mov x25, #0x6e
-;   mov x26, #0x73
-;   mov x27, #0x78
-;   mov x28, #0x7d
-;   mov x0, #0x82
-;   mov x1, #0x87
-;   ldr x10, #0x90
-;   b #0x98
+;   mov x10, #0x28
+;   mov x11, #0x2d
+;   mov x12, #0x32
+;   mov x13, #0x37
+;   mov x14, #0x3c
+;   mov x15, #0x41
+;   mov x0, #0x46
+;   mov x1, #0x4b
+;   mov x8, #0x50
+;   mov x9, #0x55
+;   mov x26, #0x5a
+;   mov x27, #0x5f
+;   mov x28, #0x64
+;   mov x21, #0x69
+;   mov x19, #0x6e
+;   mov x20, #0x73
+;   mov x22, #0x78
+;   mov x23, #0x7d
+;   mov x24, #0x82
+;   mov x25, #0x87
+;   ldr x2, #0xa4
+;   b #0xac
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   stur x0, [sp, #0x20]
-;   stur x1, [sp, #0x28]
-;   mov x0, x10
-;   ldur x10, [sp]
+;   stur x10, [sp, #0x70]
+;   stur x11, [sp, #0x78]
+;   stur x12, [sp, #0x80]
+;   stur x13, [sp, #0x88]
+;   stur x14, [sp, #0x90]
+;   stur x15, [sp, #0x98]
+;   stur x0, [sp, #0xa0]
+;   stur x1, [sp, #0xa8]
+;   stur x8, [sp, #0xb0]
+;   stur x9, [sp, #0xb8]
+;   stur x26, [sp, #0xc0]
+;   stur x27, [sp, #0xc8]
+;   stur x28, [sp, #0xd0]
+;   stur x21, [sp, #0xd8]
+;   stur x19, [sp, #0xe0]
+;   stur x20, [sp, #0xe8]
+;   stur x22, [sp, #0xf0]
+;   stur x23, [sp, #0xf8]
+;   str x24, [sp, #0x100]
+;   str x25, [sp, #0x108]
+;   mov x10, x2
+;   ldur x2, [sp]
 ;   add sp, sp, #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
-;   br x0
+;   br x10
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/return-call.clif b/cranelift/filetests/filetests/isa/aarch64/return-call.clif
index cd00960f1..3f8f7d20f 100644
--- a/cranelift/filetests/filetests/isa/aarch64/return-call.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/return-call.clif
@@ -176,10 +176,28 @@ 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, [sp, #16]
-;   ldr x2, [sp, #24]
+;   ldr x7, [sp, #16]
+;   ldr x9, [sp, #24]
+;   ldr x11, [sp, #32]
+;   ldr x13, [sp, #40]
+;   ldr x15, [sp, #48]
+;   ldr x1, [sp, #56]
+;   ldr x3, [sp, #64]
+;   ldr x5, [sp, #72]
+;   ldr x7, [sp, #80]
+;   ldr x9, [sp, #88]
+;   ldr x11, [sp, #96]
+;   ldr x13, [sp, #104]
+;   ldr x15, [sp, #112]
+;   ldr x1, [sp, #120]
+;   ldr x3, [sp, #128]
+;   ldr x5, [sp, #136]
+;   ldr x7, [sp, #144]
+;   ldr x9, [sp, #152]
+;   ldr x11, [sp, #160]
+;   ldr x2, [sp, #168]
 ;   ldp fp, lr, [sp], #16
-;   add sp, sp, #16
+;   add sp, sp, #160
 ;   ret
 ;
 ; Disassembled:
@@ -187,10 +205,28 @@ 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, [sp, #0x10]
-;   ldur x2, [sp, #0x18]
+;   ldur x7, [sp, #0x10]
+;   ldur x9, [sp, #0x18]
+;   ldur x11, [sp, #0x20]
+;   ldur x13, [sp, #0x28]
+;   ldur x15, [sp, #0x30]
+;   ldur x1, [sp, #0x38]
+;   ldur x3, [sp, #0x40]
+;   ldur x5, [sp, #0x48]
+;   ldur x7, [sp, #0x50]
+;   ldur x9, [sp, #0x58]
+;   ldur x11, [sp, #0x60]
+;   ldur x13, [sp, #0x68]
+;   ldur x15, [sp, #0x70]
+;   ldur x1, [sp, #0x78]
+;   ldur x3, [sp, #0x80]
+;   ldur x5, [sp, #0x88]
+;   ldur x7, [sp, #0x90]
+;   ldur x9, [sp, #0x98]
+;   ldur x11, [sp, #0xa0]
+;   ldur x2, [sp, #0xa8]
 ;   ldp x29, x30, [sp], #0x10
-;   add sp, sp, #0x10
+;   add sp, sp, #0xa0
 ;   ret
 
 function %tail_caller_stack_args() -> i64 tail {
@@ -229,10 +265,15 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
-;   sub sp, sp, #16
-;   ldr fp, [sp, #16]
+;   sub sp, sp, #160
+;   ldr fp, [sp, #160]
 ;   stp fp, lr, [sp]
 ;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
 ; block0:
 ;   movz x2, #10
 ;   movz x3, #15
@@ -240,74 +281,120 @@ block0:
 ;   movz x5, #25
 ;   movz x6, #30
 ;   movz x7, #35
-;   movz x8, #40
-;   movz x9, #45
-;   movz x10, #50
-;   movz x11, #55
-;   movz x12, #60
-;   movz x13, #65
-;   movz x14, #70
-;   movz x15, #75
-;   movz x19, #80
-;   movz x20, #85
-;   movz x21, #90
-;   movz x22, #95
-;   movz x23, #100
-;   movz x24, #105
-;   movz x25, #110
-;   movz x26, #115
-;   movz x27, #120
-;   movz x28, #125
-;   movz x0, #130
-;   movz x1, #135
-;   str x0, [sp, #16]
-;   str x1, [sp, #24]
-;   load_ext_name x0, TestCase(%tail_callee_stack_args)+0
-;   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
+;   movz x10, #40
+;   movz x11, #45
+;   movz x12, #50
+;   movz x13, #55
+;   movz x14, #60
+;   movz x15, #65
+;   movz x0, #70
+;   movz x1, #75
+;   movz x8, #80
+;   movz x9, #85
+;   movz x26, #90
+;   movz x27, #95
+;   movz x28, #100
+;   movz x21, #105
+;   movz x19, #110
+;   movz x20, #115
+;   movz x22, #120
+;   movz x23, #125
+;   movz x24, #130
+;   movz x25, #135
+;   str x10, [sp, #96]
+;   str x11, [sp, #104]
+;   str x12, [sp, #112]
+;   str x13, [sp, #120]
+;   str x14, [sp, #128]
+;   str x15, [sp, #136]
+;   str x0, [sp, #144]
+;   str x1, [sp, #152]
+;   str x8, [sp, #160]
+;   str x9, [sp, #168]
+;   str x26, [sp, #176]
+;   str x27, [sp, #184]
+;   str x28, [sp, #192]
+;   str x21, [sp, #200]
+;   str x19, [sp, #208]
+;   str x20, [sp, #216]
+;   str x22, [sp, #224]
+;   str x23, [sp, #232]
+;   str x24, [sp, #240]
+;   str x25, [sp, #248]
+;   load_ext_name x14, TestCase(%tail_callee_stack_args)+0
+;   return_call_ind x14 new_stack_arg_size:160 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-;   sub sp, sp, #0x10
-;   ldur x29, [sp, #0x10]
+;   sub sp, sp, #0xa0
+;   ldur x29, [sp, #0xa0]
 ;   stp x29, x30, [sp]
 ;   mov x29, sp
-; block1: ; offset 0x18
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
+; block1: ; offset 0x2c
 ;   mov x2, #0xa
 ;   mov x3, #0xf
 ;   mov x4, #0x14
 ;   mov x5, #0x19
 ;   mov x6, #0x1e
 ;   mov x7, #0x23
-;   mov x8, #0x28
-;   mov x9, #0x2d
-;   mov x10, #0x32
-;   mov x11, #0x37
-;   mov x12, #0x3c
-;   mov x13, #0x41
-;   mov x14, #0x46
-;   mov x15, #0x4b
-;   mov x19, #0x50
-;   mov x20, #0x55
-;   mov x21, #0x5a
-;   mov x22, #0x5f
-;   mov x23, #0x64
-;   mov x24, #0x69
-;   mov x25, #0x6e
-;   mov x26, #0x73
-;   mov x27, #0x78
-;   mov x28, #0x7d
-;   mov x0, #0x82
-;   mov x1, #0x87
-;   stur x0, [sp, #0x10]
-;   stur x1, [sp, #0x18]
-;   ldr x0, #0x90
-;   b #0x98
+;   mov x10, #0x28
+;   mov x11, #0x2d
+;   mov x12, #0x32
+;   mov x13, #0x37
+;   mov x14, #0x3c
+;   mov x15, #0x41
+;   mov x0, #0x46
+;   mov x1, #0x4b
+;   mov x8, #0x50
+;   mov x9, #0x55
+;   mov x26, #0x5a
+;   mov x27, #0x5f
+;   mov x28, #0x64
+;   mov x21, #0x69
+;   mov x19, #0x6e
+;   mov x20, #0x73
+;   mov x22, #0x78
+;   mov x23, #0x7d
+;   mov x24, #0x82
+;   mov x25, #0x87
+;   stur x10, [sp, #0x60]
+;   stur x11, [sp, #0x68]
+;   stur x12, [sp, #0x70]
+;   stur x13, [sp, #0x78]
+;   stur x14, [sp, #0x80]
+;   stur x15, [sp, #0x88]
+;   stur x0, [sp, #0x90]
+;   stur x1, [sp, #0x98]
+;   stur x8, [sp, #0xa0]
+;   stur x9, [sp, #0xa8]
+;   stur x26, [sp, #0xb0]
+;   stur x27, [sp, #0xb8]
+;   stur x28, [sp, #0xc0]
+;   stur x21, [sp, #0xc8]
+;   stur x19, [sp, #0xd0]
+;   stur x20, [sp, #0xd8]
+;   stur x22, [sp, #0xe0]
+;   stur x23, [sp, #0xe8]
+;   stur x24, [sp, #0xf0]
+;   stur x25, [sp, #0xf8]
+;   ldr x14, #0xec
+;   b #0xf4
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
-;   br x0
+;   br x14
 
 ;;;; Test diff blocks with diff return calls with diff # of stack args ;;;;;;;;;
 
@@ -320,10 +407,28 @@ 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, [sp, #16]
-;   ldr x2, [sp, #24]
+;   ldr x7, [sp, #16]
+;   ldr x9, [sp, #24]
+;   ldr x11, [sp, #32]
+;   ldr x13, [sp, #40]
+;   ldr x15, [sp, #48]
+;   ldr x1, [sp, #56]
+;   ldr x3, [sp, #64]
+;   ldr x5, [sp, #72]
+;   ldr x7, [sp, #80]
+;   ldr x9, [sp, #88]
+;   ldr x11, [sp, #96]
+;   ldr x13, [sp, #104]
+;   ldr x15, [sp, #112]
+;   ldr x1, [sp, #120]
+;   ldr x3, [sp, #128]
+;   ldr x5, [sp, #136]
+;   ldr x7, [sp, #144]
+;   ldr x9, [sp, #152]
+;   ldr x11, [sp, #160]
+;   ldr x2, [sp, #168]
 ;   ldp fp, lr, [sp], #16
-;   add sp, sp, #16
+;   add sp, sp, #160
 ;   ret
 ;
 ; Disassembled:
@@ -331,10 +436,28 @@ 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, [sp, #0x10]
-;   ldur x2, [sp, #0x18]
+;   ldur x7, [sp, #0x10]
+;   ldur x9, [sp, #0x18]
+;   ldur x11, [sp, #0x20]
+;   ldur x13, [sp, #0x28]
+;   ldur x15, [sp, #0x30]
+;   ldur x1, [sp, #0x38]
+;   ldur x3, [sp, #0x40]
+;   ldur x5, [sp, #0x48]
+;   ldur x7, [sp, #0x50]
+;   ldur x9, [sp, #0x58]
+;   ldur x11, [sp, #0x60]
+;   ldur x13, [sp, #0x68]
+;   ldur x15, [sp, #0x70]
+;   ldur x1, [sp, #0x78]
+;   ldur x3, [sp, #0x80]
+;   ldur x5, [sp, #0x88]
+;   ldur x7, [sp, #0x90]
+;   ldur x9, [sp, #0x98]
+;   ldur x11, [sp, #0xa0]
+;   ldur x2, [sp, #0xa8]
 ;   ldp x29, x30, [sp], #0x10
-;   add sp, sp, #0x10
+;   add sp, sp, #0xa0
 ;   ret
 
 function %different_callee2(i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, i64) -> i64 tail {
@@ -346,11 +469,29 @@ 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, [sp, #16]
-;   ldr x11, [sp, #24]
-;   ldr x2, [sp, #32]
+;   ldr x7, [sp, #16]
+;   ldr x9, [sp, #24]
+;   ldr x11, [sp, #32]
+;   ldr x13, [sp, #40]
+;   ldr x15, [sp, #48]
+;   ldr x1, [sp, #56]
+;   ldr x3, [sp, #64]
+;   ldr x5, [sp, #72]
+;   ldr x7, [sp, #80]
+;   ldr x9, [sp, #88]
+;   ldr x11, [sp, #96]
+;   ldr x13, [sp, #104]
+;   ldr x15, [sp, #112]
+;   ldr x1, [sp, #120]
+;   ldr x3, [sp, #128]
+;   ldr x5, [sp, #136]
+;   ldr x7, [sp, #144]
+;   ldr x9, [sp, #152]
+;   ldr x11, [sp, #160]
+;   ldr x13, [sp, #168]
+;   ldr x2, [sp, #176]
 ;   ldp fp, lr, [sp], #16
-;   add sp, sp, #32
+;   add sp, sp, #176
 ;   ret
 ;
 ; Disassembled:
@@ -358,11 +499,29 @@ 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, [sp, #0x10]
-;   ldur x11, [sp, #0x18]
-;   ldur x2, [sp, #0x20]
+;   ldur x7, [sp, #0x10]
+;   ldur x9, [sp, #0x18]
+;   ldur x11, [sp, #0x20]
+;   ldur x13, [sp, #0x28]
+;   ldur x15, [sp, #0x30]
+;   ldur x1, [sp, #0x38]
+;   ldur x3, [sp, #0x40]
+;   ldur x5, [sp, #0x48]
+;   ldur x7, [sp, #0x50]
+;   ldur x9, [sp, #0x58]
+;   ldur x11, [sp, #0x60]
+;   ldur x13, [sp, #0x68]
+;   ldur x15, [sp, #0x70]
+;   ldur x1, [sp, #0x78]
+;   ldur x3, [sp, #0x80]
+;   ldur x5, [sp, #0x88]
+;   ldur x7, [sp, #0x90]
+;   ldur x9, [sp, #0x98]
+;   ldur x11, [sp, #0xa0]
+;   ldur x13, [sp, #0xa8]
+;   ldur x2, [sp, #0xb0]
 ;   ldp x29, x30, [sp], #0x10
-;   add sp, sp, #0x20
+;   add sp, sp, #0xb0
 ;   ret
 
 function %caller_of_different_callees(i64) -> i64 tail {
@@ -409,120 +568,208 @@ block2:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
-;   sub sp, sp, #32
-;   ldr fp, [sp, #32]
+;   sub sp, sp, #176
+;   ldr fp, [sp, #176]
 ;   stp fp, lr, [sp]
 ;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
 ;   sub sp, sp, #16
 ; block0:
-;   str x2, [sp]
-;   movz x2, #10
-;   str x2, [sp, #8]
+;   movz x14, #10
+;   str x14, [sp]
 ;   movz x3, #15
 ;   movz x4, #20
 ;   movz x5, #25
 ;   movz x6, #30
 ;   movz x7, #35
-;   movz x8, #40
-;   movz x9, #45
-;   movz x10, #50
-;   movz x11, #55
-;   movz x12, #60
-;   movz x13, #65
-;   movz x14, #70
-;   movz x15, #75
-;   movz x19, #80
-;   movz x20, #85
-;   movz x21, #90
-;   movz x22, #95
-;   movz x23, #100
-;   movz x24, #105
-;   movz x25, #110
-;   movz x26, #115
-;   movz x27, #120
-;   movz x28, #125
-;   movz x1, #130
-;   movz x0, #135
-;   ldr x2, [sp]
+;   movz x21, #40
+;   movz x28, #45
+;   movz x27, #50
+;   movz x26, #55
+;   movz x25, #60
+;   movz x24, #65
+;   movz x23, #70
+;   movz x22, #75
+;   movz x20, #80
+;   movz x19, #85
+;   movz x13, #90
+;   movz x12, #95
+;   movz x11, #100
+;   movz x10, #105
+;   movz x9, #110
+;   movz x8, #115
+;   movz x1, #120
+;   movz x0, #125
+;   movz x15, #130
+;   movz x14, #135
 ;   cbnz x2, label2 ; b label1
 ; block1:
 ;   movz x2, #140
-;   str x1, [sp, #32]
-;   str x0, [sp, #40]
-;   str x2, [sp, #48]
-;   load_ext_name x0, TestCase(%different_callee2)+0
-;   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
+;   str x21, [sp, #112]
+;   str x28, [sp, #120]
+;   str x27, [sp, #128]
+;   str x26, [sp, #136]
+;   str x25, [sp, #144]
+;   str x24, [sp, #152]
+;   str x23, [sp, #160]
+;   str x22, [sp, #168]
+;   str x20, [sp, #176]
+;   str x19, [sp, #184]
+;   str x13, [sp, #192]
+;   str x12, [sp, #200]
+;   str x11, [sp, #208]
+;   str x10, [sp, #216]
+;   str x9, [sp, #224]
+;   str x8, [sp, #232]
+;   str x1, [sp, #240]
+;   str x0, [sp, #248]
+;   str x15, [sp, #256]
+;   str x14, [sp, #264]
+;   str x2, [sp, #272]
+;   load_ext_name x8, TestCase(%different_callee2)+0
+;   ldr x2, [sp]
+;   return_call_ind x8 new_stack_arg_size:176 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7
 ; block2:
-;   ldr x2, [sp, #8]
-;   str x1, [sp, #48]
-;   str x0, [sp, #56]
-;   load_ext_name x0, TestCase(%different_callee1)+0
-;   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
+;   ldr x2, [sp]
+;   str x21, [sp, #128]
+;   str x28, [sp, #136]
+;   str x27, [sp, #144]
+;   str x26, [sp, #152]
+;   str x25, [sp, #160]
+;   str x24, [sp, #168]
+;   str x23, [sp, #176]
+;   str x22, [sp, #184]
+;   str x20, [sp, #192]
+;   str x19, [sp, #200]
+;   str x13, [sp, #208]
+;   str x12, [sp, #216]
+;   str x11, [sp, #224]
+;   str x10, [sp, #232]
+;   str x9, [sp, #240]
+;   str x8, [sp, #248]
+;   str x1, [sp, #256]
+;   str x0, [sp, #264]
+;   str x15, [sp, #272]
+;   str x14, [sp, #280]
+;   load_ext_name x9, TestCase(%different_callee1)+0
+;   return_call_ind x9 new_stack_arg_size:160 x2=x2 x3=x3 x4=x4 x5=x5 x6=x6 x7=x7
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-;   sub sp, sp, #0x20
-;   ldur x29, [sp, #0x20]
+;   sub sp, sp, #0xb0
+;   ldur x29, [sp, #0xb0]
 ;   stp x29, x30, [sp]
 ;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
 ;   sub sp, sp, #0x10
-; block1: ; offset 0x1c
-;   stur x2, [sp]
-;   mov x2, #0xa
-;   stur x2, [sp, #8]
+; block1: ; offset 0x30
+;   mov x14, #0xa
+;   stur x14, [sp]
 ;   mov x3, #0xf
 ;   mov x4, #0x14
 ;   mov x5, #0x19
 ;   mov x6, #0x1e
 ;   mov x7, #0x23
-;   mov x8, #0x28
-;   mov x9, #0x2d
-;   mov x10, #0x32
-;   mov x11, #0x37
-;   mov x12, #0x3c
-;   mov x13, #0x41
-;   mov x14, #0x46
-;   mov x15, #0x4b
-;   mov x19, #0x50
-;   mov x20, #0x55
-;   mov x21, #0x5a
-;   mov x22, #0x5f
-;   mov x23, #0x64
-;   mov x24, #0x69
-;   mov x25, #0x6e
-;   mov x26, #0x73
-;   mov x27, #0x78
-;   mov x28, #0x7d
-;   mov x1, #0x82
-;   mov x0, #0x87
-;   ldur x2, [sp]
-;   cbnz x2, #0xc4
-; block2: ; offset 0x94
+;   mov x21, #0x28
+;   mov x28, #0x2d
+;   mov x27, #0x32
+;   mov x26, #0x37
+;   mov x25, #0x3c
+;   mov x24, #0x41
+;   mov x23, #0x46
+;   mov x22, #0x4b
+;   mov x20, #0x50
+;   mov x19, #0x55
+;   mov x13, #0x5a
+;   mov x12, #0x5f
+;   mov x11, #0x64
+;   mov x10, #0x69
+;   mov x9, #0x6e
+;   mov x8, #0x73
+;   mov x1, #0x78
+;   mov x0, #0x7d
+;   mov x15, #0x82
+;   mov x14, #0x87
+;   cbnz x2, #0x12c
+; block2: ; offset 0xa0
 ;   mov x2, #0x8c
-;   stur x1, [sp, #0x20]
-;   stur x0, [sp, #0x28]
-;   stur x2, [sp, #0x30]
-;   ldr x0, #0xac
-;   b #0xb4
+;   stur x21, [sp, #0x70]
+;   stur x28, [sp, #0x78]
+;   stur x27, [sp, #0x80]
+;   stur x26, [sp, #0x88]
+;   stur x25, [sp, #0x90]
+;   stur x24, [sp, #0x98]
+;   stur x23, [sp, #0xa0]
+;   stur x22, [sp, #0xa8]
+;   stur x20, [sp, #0xb0]
+;   stur x19, [sp, #0xb8]
+;   stur x13, [sp, #0xc0]
+;   stur x12, [sp, #0xc8]
+;   stur x11, [sp, #0xd0]
+;   stur x10, [sp, #0xd8]
+;   stur x9, [sp, #0xe0]
+;   stur x8, [sp, #0xe8]
+;   stur x1, [sp, #0xf0]
+;   stur x0, [sp, #0xf8]
+;   str x15, [sp, #0x100]
+;   str x14, [sp, #0x108]
+;   str x2, [sp, #0x110]
+;   ldr x8, #0x100
+;   b #0x108
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %different_callee2 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
-;   ldur x2, [sp, #8]
+;   ldur x2, [sp]
 ;   add sp, sp, #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
-;   br x0
-; block3: ; offset 0xc4
-;   ldur x2, [sp, #8]
-;   stur x1, [sp, #0x30]
-;   stur x0, [sp, #0x38]
-;   ldr x0, #0xd8
-;   b #0xe0
+;   br x8
+; block3: ; offset 0x12c
+;   ldur x2, [sp]
+;   stur x21, [sp, #0x80]
+;   stur x28, [sp, #0x88]
+;   stur x27, [sp, #0x90]
+;   stur x26, [sp, #0x98]
+;   stur x25, [sp, #0xa0]
+;   stur x24, [sp, #0xa8]
+;   stur x23, [sp, #0xb0]
+;   stur x22, [sp, #0xb8]
+;   stur x20, [sp, #0xc0]
+;   stur x19, [sp, #0xc8]
+;   stur x13, [sp, #0xd0]
+;   stur x12, [sp, #0xd8]
+;   stur x11, [sp, #0xe0]
+;   stur x10, [sp, #0xe8]
+;   stur x9, [sp, #0xf0]
+;   stur x8, [sp, #0xf8]
+;   str x1, [sp, #0x100]
+;   str x0, [sp, #0x108]
+;   str x15, [sp, #0x110]
+;   str x14, [sp, #0x118]
+;   ldr x9, #0x188
+;   b #0x190
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %different_callee1 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
 ;   add sp, sp, #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
 ;   add sp, sp, #0x10
-;   br x0
+;   br x9
 
diff --git a/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif b/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif
index a68287270..f90085e27 100644
--- a/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif
+++ b/cranelift/filetests/filetests/isa/aarch64/tail-call-conv.clif
@@ -12,10 +12,28 @@ 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, [sp, #16]
-;   ldr x2, [sp, #24]
+;   ldr x7, [sp, #16]
+;   ldr x9, [sp, #24]
+;   ldr x11, [sp, #32]
+;   ldr x13, [sp, #40]
+;   ldr x15, [sp, #48]
+;   ldr x1, [sp, #56]
+;   ldr x3, [sp, #64]
+;   ldr x5, [sp, #72]
+;   ldr x7, [sp, #80]
+;   ldr x9, [sp, #88]
+;   ldr x11, [sp, #96]
+;   ldr x13, [sp, #104]
+;   ldr x15, [sp, #112]
+;   ldr x1, [sp, #120]
+;   ldr x3, [sp, #128]
+;   ldr x5, [sp, #136]
+;   ldr x7, [sp, #144]
+;   ldr x9, [sp, #152]
+;   ldr x11, [sp, #160]
+;   ldr x2, [sp, #168]
 ;   ldp fp, lr, [sp], #16
-;   add sp, sp, #16
+;   add sp, sp, #160
 ;   ret
 ;
 ; Disassembled:
@@ -23,10 +41,28 @@ 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, [sp, #0x10]
-;   ldur x2, [sp, #0x18]
+;   ldur x7, [sp, #0x10]
+;   ldur x9, [sp, #0x18]
+;   ldur x11, [sp, #0x20]
+;   ldur x13, [sp, #0x28]
+;   ldur x15, [sp, #0x30]
+;   ldur x1, [sp, #0x38]
+;   ldur x3, [sp, #0x40]
+;   ldur x5, [sp, #0x48]
+;   ldur x7, [sp, #0x50]
+;   ldur x9, [sp, #0x58]
+;   ldur x11, [sp, #0x60]
+;   ldur x13, [sp, #0x68]
+;   ldur x15, [sp, #0x70]
+;   ldur x1, [sp, #0x78]
+;   ldur x3, [sp, #0x80]
+;   ldur x5, [sp, #0x88]
+;   ldur x7, [sp, #0x90]
+;   ldur x9, [sp, #0x98]
+;   ldur x11, [sp, #0xa0]
+;   ldur x2, [sp, #0xa8]
 ;   ldp x29, x30, [sp], #0x10
-;   add sp, sp, #0x10
+;   add sp, sp, #0xa0
 ;   ret
 
 function %tail_caller_stack_args() -> i64 tail {
@@ -66,8 +102,13 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
-;   sub sp, sp, #16
-;   virtual_sp_offset_adjust 16
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
+;   sub sp, sp, #160
+;   virtual_sp_offset_adjust 160
 ; block0:
 ;   movz x2, #10
 ;   movz x3, #15
@@ -75,33 +116,56 @@ block0:
 ;   movz x5, #25
 ;   movz x6, #30
 ;   movz x7, #35
-;   movz x8, #40
-;   movz x9, #45
-;   movz x10, #50
-;   movz x11, #55
-;   movz x12, #60
-;   movz x13, #65
-;   movz x14, #70
-;   movz x15, #75
-;   movz x19, #80
-;   movz x20, #85
-;   movz x21, #90
-;   movz x22, #95
-;   movz x23, #100
-;   movz x24, #105
-;   movz x25, #110
-;   movz x26, #115
-;   movz x27, #120
-;   movz x28, #125
-;   movz x0, #130
-;   movz x1, #135
-;   str x0, [sp]
-;   str x1, [sp, #8]
+;   movz x11, #40
+;   movz x12, #45
+;   movz x13, #50
+;   movz x14, #55
+;   movz x15, #60
+;   movz x0, #65
+;   movz x1, #70
+;   movz x8, #75
+;   movz x9, #80
+;   movz x10, #85
+;   movz x27, #90
+;   movz x28, #95
+;   movz x21, #100
+;   movz x19, #105
+;   movz x20, #110
+;   movz x22, #115
+;   movz x23, #120
+;   movz x24, #125
+;   movz x25, #130
+;   movz x26, #135
+;   str x11, [sp]
+;   str x12, [sp, #8]
+;   str x13, [sp, #16]
+;   str x14, [sp, #24]
+;   str x15, [sp, #32]
+;   str x0, [sp, #40]
+;   str x1, [sp, #48]
+;   str x8, [sp, #56]
+;   str x9, [sp, #64]
+;   str x10, [sp, #72]
+;   str x27, [sp, #80]
+;   str x28, [sp, #88]
+;   str x21, [sp, #96]
+;   str x19, [sp, #104]
+;   str x20, [sp, #112]
+;   str x22, [sp, #120]
+;   str x23, [sp, #128]
+;   str x24, [sp, #136]
+;   str x25, [sp, #144]
+;   str x26, [sp, #152]
 ;   load_ext_name x1, TestCase(%tail_callee_stack_args)+0
 ;   blr x1
-;   sub sp, sp, #16
-;   virtual_sp_offset_adjust 16
-;   add sp, sp, #16
+;   sub sp, sp, #160
+;   virtual_sp_offset_adjust 160
+;   add sp, sp, #160
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -109,43 +173,71 @@ block0:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-;   sub sp, sp, #0x10
-; block1: ; offset 0xc
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
+;   sub sp, sp, #0xa0
+; block1: ; offset 0x20
 ;   mov x2, #0xa
 ;   mov x3, #0xf
 ;   mov x4, #0x14
 ;   mov x5, #0x19
 ;   mov x6, #0x1e
 ;   mov x7, #0x23
-;   mov x8, #0x28
-;   mov x9, #0x2d
-;   mov x10, #0x32
-;   mov x11, #0x37
-;   mov x12, #0x3c
-;   mov x13, #0x41
-;   mov x14, #0x46
-;   mov x15, #0x4b
-;   mov x19, #0x50
-;   mov x20, #0x55
-;   mov x21, #0x5a
-;   mov x22, #0x5f
-;   mov x23, #0x64
-;   mov x24, #0x69
-;   mov x25, #0x6e
-;   mov x26, #0x73
-;   mov x27, #0x78
-;   mov x28, #0x7d
-;   mov x0, #0x82
-;   mov x1, #0x87
-;   stur x0, [sp]
-;   stur x1, [sp, #8]
-;   ldr x1, #0x84
-;   b #0x8c
+;   mov x11, #0x28
+;   mov x12, #0x2d
+;   mov x13, #0x32
+;   mov x14, #0x37
+;   mov x15, #0x3c
+;   mov x0, #0x41
+;   mov x1, #0x46
+;   mov x8, #0x4b
+;   mov x9, #0x50
+;   mov x10, #0x55
+;   mov x27, #0x5a
+;   mov x28, #0x5f
+;   mov x21, #0x64
+;   mov x19, #0x69
+;   mov x20, #0x6e
+;   mov x22, #0x73
+;   mov x23, #0x78
+;   mov x24, #0x7d
+;   mov x25, #0x82
+;   mov x26, #0x87
+;   stur x11, [sp]
+;   stur x12, [sp, #8]
+;   stur x13, [sp, #0x10]
+;   stur x14, [sp, #0x18]
+;   stur x15, [sp, #0x20]
+;   stur x0, [sp, #0x28]
+;   stur x1, [sp, #0x30]
+;   stur x8, [sp, #0x38]
+;   stur x9, [sp, #0x40]
+;   stur x10, [sp, #0x48]
+;   stur x27, [sp, #0x50]
+;   stur x28, [sp, #0x58]
+;   stur x21, [sp, #0x60]
+;   stur x19, [sp, #0x68]
+;   stur x20, [sp, #0x70]
+;   stur x22, [sp, #0x78]
+;   stur x23, [sp, #0x80]
+;   stur x24, [sp, #0x88]
+;   stur x25, [sp, #0x90]
+;   stur x26, [sp, #0x98]
+;   ldr x1, #0xe0
+;   b #0xe8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
 ;   blr x1
-;   sub sp, sp, #0x10
-;   add sp, sp, #0x10
+;   sub sp, sp, #0xa0
+;   add sp, sp, #0xa0
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
@@ -185,6 +277,11 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
 ;   sub sp, sp, #16
 ; block0:
 ;   movz x2, #10
@@ -194,30 +291,53 @@ block0:
 ;   movz x5, #25
 ;   movz x6, #30
 ;   movz x7, #35
-;   movz x8, #40
-;   movz x9, #45
-;   movz x10, #50
-;   movz x11, #55
-;   movz x12, #60
-;   movz x13, #65
-;   movz x14, #70
-;   movz x15, #75
-;   movz x19, #80
-;   movz x20, #85
-;   movz x21, #90
-;   movz x22, #95
-;   movz x23, #100
-;   movz x24, #105
-;   movz x25, #110
-;   movz x26, #115
-;   movz x27, #120
-;   movz x28, #125
-;   movz x1, #130
+;   movz x11, #40
+;   movz x12, #45
+;   movz x13, #50
+;   movz x14, #55
+;   movz x15, #60
+;   movz x1, #65
+;   movz x8, #70
+;   movz x9, #75
+;   movz x10, #80
+;   movz x27, #85
+;   movz x28, #90
+;   movz x21, #95
+;   movz x19, #100
+;   movz x20, #105
+;   movz x22, #110
+;   movz x23, #115
+;   movz x24, #120
+;   movz x25, #125
+;   movz x26, #130
 ;   movz x2, #135
-;   str x1, [x0]
-;   str x2, [x0, #8]
+;   str x11, [x0]
+;   str x12, [x0, #8]
+;   str x13, [x0, #16]
+;   str x14, [x0, #24]
+;   str x15, [x0, #32]
+;   str x1, [x0, #40]
+;   str x8, [x0, #48]
+;   str x9, [x0, #56]
+;   str x10, [x0, #64]
+;   str x27, [x0, #72]
+;   str x28, [x0, #80]
+;   str x21, [x0, #88]
+;   str x19, [x0, #96]
+;   str x20, [x0, #104]
+;   str x22, [x0, #112]
+;   str x23, [x0, #120]
+;   str x24, [x0, #128]
+;   str x25, [x0, #136]
+;   str x26, [x0, #144]
+;   str x2, [x0, #152]
 ;   ldr x2, [sp]
 ;   add sp, sp, #16
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -225,8 +345,13 @@ block0:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
 ;   sub sp, sp, #0x10
-; block1: ; offset 0xc
+; block1: ; offset 0x20
 ;   mov x2, #0xa
 ;   stur x2, [sp]
 ;   mov x3, #0xf
@@ -234,30 +359,53 @@ block0:
 ;   mov x5, #0x19
 ;   mov x6, #0x1e
 ;   mov x7, #0x23
-;   mov x8, #0x28
-;   mov x9, #0x2d
-;   mov x10, #0x32
-;   mov x11, #0x37
-;   mov x12, #0x3c
-;   mov x13, #0x41
-;   mov x14, #0x46
-;   mov x15, #0x4b
-;   mov x19, #0x50
-;   mov x20, #0x55
-;   mov x21, #0x5a
-;   mov x22, #0x5f
-;   mov x23, #0x64
-;   mov x24, #0x69
-;   mov x25, #0x6e
-;   mov x26, #0x73
-;   mov x27, #0x78
-;   mov x28, #0x7d
-;   mov x1, #0x82
+;   mov x11, #0x28
+;   mov x12, #0x2d
+;   mov x13, #0x32
+;   mov x14, #0x37
+;   mov x15, #0x3c
+;   mov x1, #0x41
+;   mov x8, #0x46
+;   mov x9, #0x4b
+;   mov x10, #0x50
+;   mov x27, #0x55
+;   mov x28, #0x5a
+;   mov x21, #0x5f
+;   mov x19, #0x64
+;   mov x20, #0x69
+;   mov x22, #0x6e
+;   mov x23, #0x73
+;   mov x24, #0x78
+;   mov x25, #0x7d
+;   mov x26, #0x82
 ;   mov x2, #0x87
-;   stur x1, [x0]
-;   stur x2, [x0, #8]
+;   stur x11, [x0]
+;   stur x12, [x0, #8]
+;   stur x13, [x0, #0x10]
+;   stur x14, [x0, #0x18]
+;   stur x15, [x0, #0x20]
+;   stur x1, [x0, #0x28]
+;   stur x8, [x0, #0x30]
+;   stur x9, [x0, #0x38]
+;   stur x10, [x0, #0x40]
+;   stur x27, [x0, #0x48]
+;   stur x28, [x0, #0x50]
+;   stur x21, [x0, #0x58]
+;   stur x19, [x0, #0x60]
+;   stur x20, [x0, #0x68]
+;   stur x22, [x0, #0x70]
+;   stur x23, [x0, #0x78]
+;   stur x24, [x0, #0x80]
+;   stur x25, [x0, #0x88]
+;   stur x26, [x0, #0x90]
+;   stur x2, [x0, #0x98]
 ;   ldur x2, [sp]
 ;   add sp, sp, #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
@@ -272,15 +420,33 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
-;   sub sp, sp, #16
-;   virtual_sp_offset_adjust 16
+;   sub sp, sp, #160
+;   virtual_sp_offset_adjust 160
 ; block0:
 ;   mov x0, sp
 ;   load_ext_name x1, TestCase(%tail_callee_stack_rets)+0
 ;   blr x1
-;   ldr x11, [sp]
-;   ldr x2, [sp, #8]
-;   add sp, sp, #16
+;   ldr x9, [sp]
+;   ldr x11, [sp, #8]
+;   ldr x13, [sp, #16]
+;   ldr x15, [sp, #24]
+;   ldr x1, [sp, #32]
+;   ldr x3, [sp, #40]
+;   ldr x5, [sp, #48]
+;   ldr x7, [sp, #56]
+;   ldr x9, [sp, #64]
+;   ldr x11, [sp, #72]
+;   ldr x13, [sp, #80]
+;   ldr x15, [sp, #88]
+;   ldr x1, [sp, #96]
+;   ldr x3, [sp, #104]
+;   ldr x5, [sp, #112]
+;   ldr x7, [sp, #120]
+;   ldr x9, [sp, #128]
+;   ldr x11, [sp, #136]
+;   ldr x13, [sp, #144]
+;   ldr x2, [sp, #152]
+;   add sp, sp, #160
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -288,7 +454,7 @@ block0:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-;   sub sp, sp, #0x10
+;   sub sp, sp, #0xa0
 ; block1: ; offset 0xc
 ;   mov x0, sp
 ;   ldr x1, #0x18
@@ -296,9 +462,27 @@ block0:
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_rets 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
 ;   blr x1
-;   ldur x11, [sp]
-;   ldur x2, [sp, #8]
-;   add sp, sp, #0x10
+;   ldur x9, [sp]
+;   ldur x11, [sp, #8]
+;   ldur x13, [sp, #0x10]
+;   ldur x15, [sp, #0x18]
+;   ldur x1, [sp, #0x20]
+;   ldur x3, [sp, #0x28]
+;   ldur x5, [sp, #0x30]
+;   ldur x7, [sp, #0x38]
+;   ldur x9, [sp, #0x40]
+;   ldur x11, [sp, #0x48]
+;   ldur x13, [sp, #0x50]
+;   ldur x15, [sp, #0x58]
+;   ldur x1, [sp, #0x60]
+;   ldur x3, [sp, #0x68]
+;   ldur x5, [sp, #0x70]
+;   ldur x7, [sp, #0x78]
+;   ldur x9, [sp, #0x80]
+;   ldur x11, [sp, #0x88]
+;   ldur x13, [sp, #0x90]
+;   ldur x2, [sp, #0x98]
+;   add sp, sp, #0xa0
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
@@ -313,34 +497,126 @@ block0(v0: i64, v1: i64, v2: i64, v3: i64, v4: i64, v5: i64, v6: i64, v7: i64, v
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
 ;   sub sp, sp, #16
 ; block0:
-;   str x9, [sp]
-;   ldr x9, [sp, #32]
-;   ldr x1, [sp, #40]
-;   str x9, [x0]
-;   str x1, [x0, #8]
-;   ldr x9, [sp]
+;   str x7, [sp]
+;   ldr x7, [sp, #112]
+;   ldr x19, [sp, #120]
+;   ldr x22, [sp, #128]
+;   ldr x24, [sp, #136]
+;   ldr x26, [sp, #144]
+;   ldr x28, [sp, #152]
+;   ldr x21, [sp, #160]
+;   ldr x20, [sp, #168]
+;   ldr x23, [sp, #176]
+;   ldr x25, [sp, #184]
+;   ldr x27, [sp, #192]
+;   ldr x14, [sp, #200]
+;   ldr x15, [sp, #208]
+;   ldr x1, [sp, #216]
+;   ldr x12, [sp, #224]
+;   ldr x10, [sp, #232]
+;   ldr x8, [sp, #240]
+;   ldr x9, [sp, #248]
+;   ldr x11, [sp, #256]
+;   ldr x13, [sp, #264]
+;   str x7, [x0]
+;   str x19, [x0, #8]
+;   str x22, [x0, #16]
+;   str x24, [x0, #24]
+;   str x26, [x0, #32]
+;   str x28, [x0, #40]
+;   str x21, [x0, #48]
+;   str x20, [x0, #56]
+;   str x23, [x0, #64]
+;   str x25, [x0, #72]
+;   str x27, [x0, #80]
+;   str x14, [x0, #88]
+;   str x15, [x0, #96]
+;   str x1, [x0, #104]
+;   str x12, [x0, #112]
+;   str x10, [x0, #120]
+;   str x8, [x0, #128]
+;   str x9, [x0, #136]
+;   str x11, [x0, #144]
+;   str x13, [x0, #152]
+;   ldr x7, [sp]
 ;   add sp, sp, #16
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
 ;   ldp fp, lr, [sp], #16
-;   add sp, sp, #16
+;   add sp, sp, #160
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
 ;   sub sp, sp, #0x10
-; block1: ; offset 0xc
-;   stur x9, [sp]
-;   ldur x9, [sp, #0x20]
-;   ldur x1, [sp, #0x28]
-;   stur x9, [x0]
-;   stur x1, [x0, #8]
-;   ldur x9, [sp]
+; block1: ; offset 0x20
+;   stur x7, [sp]
+;   ldur x7, [sp, #0x70]
+;   ldur x19, [sp, #0x78]
+;   ldur x22, [sp, #0x80]
+;   ldur x24, [sp, #0x88]
+;   ldur x26, [sp, #0x90]
+;   ldur x28, [sp, #0x98]
+;   ldur x21, [sp, #0xa0]
+;   ldur x20, [sp, #0xa8]
+;   ldur x23, [sp, #0xb0]
+;   ldur x25, [sp, #0xb8]
+;   ldur x27, [sp, #0xc0]
+;   ldur x14, [sp, #0xc8]
+;   ldur x15, [sp, #0xd0]
+;   ldur x1, [sp, #0xd8]
+;   ldur x12, [sp, #0xe0]
+;   ldur x10, [sp, #0xe8]
+;   ldur x8, [sp, #0xf0]
+;   ldur x9, [sp, #0xf8]
+;   ldr x11, [sp, #0x100]
+;   ldr x13, [sp, #0x108]
+;   stur x7, [x0]
+;   stur x19, [x0, #8]
+;   stur x22, [x0, #0x10]
+;   stur x24, [x0, #0x18]
+;   stur x26, [x0, #0x20]
+;   stur x28, [x0, #0x28]
+;   stur x21, [x0, #0x30]
+;   stur x20, [x0, #0x38]
+;   stur x23, [x0, #0x40]
+;   stur x25, [x0, #0x48]
+;   stur x27, [x0, #0x50]
+;   stur x14, [x0, #0x58]
+;   stur x15, [x0, #0x60]
+;   stur x1, [x0, #0x68]
+;   stur x12, [x0, #0x70]
+;   stur x10, [x0, #0x78]
+;   stur x8, [x0, #0x80]
+;   stur x9, [x0, #0x88]
+;   stur x11, [x0, #0x90]
+;   stur x13, [x0, #0x98]
+;   ldur x7, [sp]
 ;   add sp, sp, #0x10
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
-;   add sp, sp, #0x10
+;   add sp, sp, #0xa0
 ;   ret
 
 function %tail_caller_stack_args_and_rets() -> i64 tail {
@@ -380,8 +656,13 @@ block0:
 ; VCode:
 ;   stp fp, lr, [sp, #-16]!
 ;   mov fp, sp
-;   sub sp, sp, #32
-;   virtual_sp_offset_adjust 32
+;   stp x27, x28, [sp, #-16]!
+;   stp x25, x26, [sp, #-16]!
+;   stp x23, x24, [sp, #-16]!
+;   stp x21, x22, [sp, #-16]!
+;   stp x19, x20, [sp, #-16]!
+;   sub sp, sp, #320
+;   virtual_sp_offset_adjust 320
 ; block0:
 ;   movz x2, #10
 ;   movz x3, #15
@@ -397,28 +678,69 @@ block0:
 ;   movz x13, #65
 ;   movz x14, #70
 ;   movz x15, #75
-;   movz x19, #80
-;   movz x20, #85
-;   movz x21, #90
-;   movz x22, #95
-;   movz x23, #100
-;   movz x24, #105
-;   movz x25, #110
-;   movz x26, #115
-;   movz x27, #120
-;   movz x28, #125
-;   movz x0, #130
-;   movz x1, #135
-;   str x0, [sp]
-;   str x1, [sp, #8]
-;   add x0, sp, #16
+;   movz x0, #80
+;   movz x1, #85
+;   movz x23, #90
+;   movz x24, #95
+;   movz x25, #100
+;   movz x26, #105
+;   movz x27, #110
+;   movz x28, #115
+;   movz x21, #120
+;   movz x19, #125
+;   movz x20, #130
+;   movz x22, #135
+;   str x8, [sp]
+;   str x9, [sp, #8]
+;   str x10, [sp, #16]
+;   str x11, [sp, #24]
+;   str x12, [sp, #32]
+;   str x13, [sp, #40]
+;   str x14, [sp, #48]
+;   str x15, [sp, #56]
+;   str x0, [sp, #64]
+;   str x1, [sp, #72]
+;   str x23, [sp, #80]
+;   str x24, [sp, #88]
+;   str x25, [sp, #96]
+;   str x26, [sp, #104]
+;   str x27, [sp, #112]
+;   str x28, [sp, #120]
+;   str x21, [sp, #128]
+;   str x19, [sp, #136]
+;   str x20, [sp, #144]
+;   str x22, [sp, #152]
+;   add x0, sp, #160
 ;   load_ext_name x1, TestCase(%tail_callee_stack_args_and_rets)+0
 ;   blr x1
-;   sub sp, sp, #16
-;   virtual_sp_offset_adjust 16
-;   ldr x9, [sp, #16]
-;   ldr x2, [sp, #24]
-;   add sp, sp, #32
+;   sub sp, sp, #160
+;   virtual_sp_offset_adjust 160
+;   ldr x9, [sp, #160]
+;   ldr x11, [sp, #168]
+;   ldr x13, [sp, #176]
+;   ldr x15, [sp, #184]
+;   ldr x1, [sp, #192]
+;   ldr x3, [sp, #200]
+;   ldr x5, [sp, #208]
+;   ldr x7, [sp, #216]
+;   ldr x9, [sp, #224]
+;   ldr x11, [sp, #232]
+;   ldr x13, [sp, #240]
+;   ldr x15, [sp, #248]
+;   ldr x1, [sp, #256]
+;   ldr x3, [sp, #264]
+;   ldr x5, [sp, #272]
+;   ldr x7, [sp, #280]
+;   ldr x9, [sp, #288]
+;   ldr x11, [sp, #296]
+;   ldr x13, [sp, #304]
+;   ldr x2, [sp, #312]
+;   add sp, sp, #320
+;   ldp x19, x20, [sp], #16
+;   ldp x21, x22, [sp], #16
+;   ldp x23, x24, [sp], #16
+;   ldp x25, x26, [sp], #16
+;   ldp x27, x28, [sp], #16
 ;   ldp fp, lr, [sp], #16
 ;   ret
 ;
@@ -426,8 +748,13 @@ block0:
 ; block0: ; offset 0x0
 ;   stp x29, x30, [sp, #-0x10]!
 ;   mov x29, sp
-;   sub sp, sp, #0x20
-; block1: ; offset 0xc
+;   stp x27, x28, [sp, #-0x10]!
+;   stp x25, x26, [sp, #-0x10]!
+;   stp x23, x24, [sp, #-0x10]!
+;   stp x21, x22, [sp, #-0x10]!
+;   stp x19, x20, [sp, #-0x10]!
+;   sub sp, sp, #0x140
+; block1: ; offset 0x20
 ;   mov x2, #0xa
 ;   mov x3, #0xf
 ;   mov x4, #0x14
@@ -442,30 +769,71 @@ block0:
 ;   mov x13, #0x41
 ;   mov x14, #0x46
 ;   mov x15, #0x4b
-;   mov x19, #0x50
-;   mov x20, #0x55
-;   mov x21, #0x5a
-;   mov x22, #0x5f
-;   mov x23, #0x64
-;   mov x24, #0x69
-;   mov x25, #0x6e
-;   mov x26, #0x73
-;   mov x27, #0x78
-;   mov x28, #0x7d
-;   mov x0, #0x82
-;   mov x1, #0x87
-;   stur x0, [sp]
-;   stur x1, [sp, #8]
-;   add x0, sp, #0x10
-;   ldr x1, #0x88
-;   b #0x90
+;   mov x0, #0x50
+;   mov x1, #0x55
+;   mov x23, #0x5a
+;   mov x24, #0x5f
+;   mov x25, #0x64
+;   mov x26, #0x69
+;   mov x27, #0x6e
+;   mov x28, #0x73
+;   mov x21, #0x78
+;   mov x19, #0x7d
+;   mov x20, #0x82
+;   mov x22, #0x87
+;   stur x8, [sp]
+;   stur x9, [sp, #8]
+;   stur x10, [sp, #0x10]
+;   stur x11, [sp, #0x18]
+;   stur x12, [sp, #0x20]
+;   stur x13, [sp, #0x28]
+;   stur x14, [sp, #0x30]
+;   stur x15, [sp, #0x38]
+;   stur x0, [sp, #0x40]
+;   stur x1, [sp, #0x48]
+;   stur x23, [sp, #0x50]
+;   stur x24, [sp, #0x58]
+;   stur x25, [sp, #0x60]
+;   stur x26, [sp, #0x68]
+;   stur x27, [sp, #0x70]
+;   stur x28, [sp, #0x78]
+;   stur x21, [sp, #0x80]
+;   stur x19, [sp, #0x88]
+;   stur x20, [sp, #0x90]
+;   stur x22, [sp, #0x98]
+;   add x0, sp, #0xa0
+;   ldr x1, #0xe4
+;   b #0xec
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; reloc_external Abs8 %tail_callee_stack_args_and_rets 0
 ;   .byte 0x00, 0x00, 0x00, 0x00
 ;   blr x1
-;   sub sp, sp, #0x10
-;   ldur x9, [sp, #0x10]
-;   ldur x2, [sp, #0x18]
-;   add sp, sp, #0x20
+;   sub sp, sp, #0xa0
+;   ldur x9, [sp, #0xa0]
+;   ldur x11, [sp, #0xa8]
+;   ldur x13, [sp, #0xb0]
+;   ldur x15, [sp, #0xb8]
+;   ldur x1, [sp, #0xc0]
+;   ldur x3, [sp, #0xc8]
+;   ldur x5, [sp, #0xd0]
+;   ldur x7, [sp, #0xd8]
+;   ldur x9, [sp, #0xe0]
+;   ldur x11, [sp, #0xe8]
+;   ldur x13, [sp, #0xf0]
+;   ldur x15, [sp, #0xf8]
+;   ldr x1, [sp, #0x100]
+;   ldr x3, [sp, #0x108]
+;   ldr x5, [sp, #0x110]
+;   ldr x7, [sp, #0x118]
+;   ldr x9, [sp, #0x120]
+;   ldr x11, [sp, #0x128]
+;   ldr x13, [sp, #0x130]
+;   ldr x2, [sp, #0x138]
+;   add sp, sp, #0x140
+;   ldp x19, x20, [sp], #0x10
+;   ldp x21, x22, [sp], #0x10
+;   ldp x23, x24, [sp], #0x10
+;   ldp x25, x26, [sp], #0x10
+;   ldp x27, x28, [sp], #0x10
 ;   ldp x29, x30, [sp], #0x10
 ;   ret
 
diff --git a/cranelift/filetests/filetests/pcc/succeed/const.clif b/cranelift/filetests/filetests/pcc/succeed/const.clif
index 529c43eaa..dd1e258d4 100644
--- a/cranelift/filetests/filetests/pcc/succeed/const.clif
+++ b/cranelift/filetests/filetests/pcc/succeed/const.clif
@@ -34,3 +34,9 @@ block0:
     v0 = iconst.i64 0xffff_fff7
     return v0
 }
+
+function %f4() -> i64 {
+block0:
+    v0 ! range(64, 0x1_0001_ffff_ffff, 0x1_0001_ffff_ffff) = iconst.i64 0x1_0001_ffff_ffff
+    return v0
+}