Repository: bytecodealliance/wasmtime
Pull request #8385 (2024-04-16 18:26:52)

Ensure that indirect tail calls on x64 don't clobber the destination register
When we changed how `return_call` is handled during instruction emission, we assumed that `r11` would always be safe to use as it's a caller-save register on x64. However, this also means that it's available to RA2 for allocation, and could end up holding the destination of a `return_call_indirect`. To fix this, I've added a temporary writable register to the x64 `ReturnCallInfo` structure, and marked it with an early def constraint to ensure that it won't conflict with the destination of an indirect call.
<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs
index 08ed89575..c1e15680a 100644
--- a/cranelift/codegen/src/isa/x64/abi.rs
+++ b/cranelift/codegen/src/isa/x64/abi.rs
@@ -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);
             }


Test patch:
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