blob: 537976b7f6f43f90602be3dd1ea90c065d96965b [file] [log] [blame] [edit]
//@ run-pass
//@ ignore-backends: gcc
//
//@ ignore-wasm
//@ ignore-riscv64
#![feature(explicit_tail_calls)]
#![expect(incomplete_features)]
// Test tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments.
//
// Normally an indirect argument with `on_stack: false` would be passed as a pointer to the
// caller's stack frame. For tail calls, that would be unsound, because the caller's stack
// frame is overwritten by the callee's stack frame.
//
// The solution is to write the argument into the caller's argument place (stored somewhere further
// up the stack), and forward that place.
// A struct big enough that it is not passed via registers, so that the rust calling convention uses
// `Indirect { on_stack: false, .. }`.
#[repr(C)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub struct Big([u64; 4]);
#[inline(never)]
fn update_in_caller(y: Big) -> u64 {
#[inline(never)]
fn helper(x: Big) -> u64 {
x.0.iter().sum()
}
let x = Big([y.0[0], 2, 3, 4]);
// `x` is actually stored in `y`'s space.
become helper(x)
}
#[inline(never)]
fn swapper<T>(a: T, b: T) -> (T, T) {
#[inline(never)]
fn helper<T>(a: T, b: T) -> (T, T) {
(a, b)
}
become helper(b, a)
}
#[inline(never)]
fn swapper_derived(a: Big, _: (u64, u64), b: Big, _: (u64, u64)) -> ((u64, u64), (u64, u64)) {
#[inline(never)]
fn helper(_: Big, x: (u64, u64), _: Big, y: (u64, u64)) -> ((u64, u64), (u64, u64)) {
(x, y)
}
// Read the values at various points in the swapping process, testing that they have the correct
// value at every point.
become helper(b, (a.0[0], b.0[0]), a, (a.0[0], b.0[0]));
}
fn main() {
assert_eq!(update_in_caller(Big::default()), 0 + 2 + 3 + 4);
assert_eq!(swapper(u8::MIN, u8::MAX), (u8::MAX, u8::MIN));
// i128 uses `PassMode::Indirect { on_stack: false, .. }` on x86_64 MSVC.
assert_eq!(swapper(i128::MIN, i128::MAX), (i128::MAX, i128::MIN));
assert_eq!(swapper(Big([1; 4]), Big([2; 4])), (Big([2; 4]), Big([1; 4])));
assert_eq!(swapper_derived(Big([1; 4]), (0, 0), Big([2; 4]), (0, 0)), ((1, 2), (1, 2)));
}