Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 28 additions & 46 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1256,55 +1256,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
CallKind::Tail => {
match fn_abi.args[i].mode {
PassMode::Indirect { on_stack: false, .. } => {
let Some(tmp) = tail_call_temporaries[i].take() else {
span_bug!(
fn_span,
"missing temporary for indirect tail call argument #{i}"
)
};

let local = self.mir.args_iter().nth(i).unwrap();

match &self.locals[local] {
LocalRef::Place(arg) => {
bx.typed_place_copy(arg.val, tmp.val, fn_abi.args[i].layout);
op.val = Ref(arg.val);
}
LocalRef::Operand(arg) => {
let Ref(place_value) = arg.val else {
bug!("only `Ref` should use `PassMode::Indirect`");
};
bx.typed_place_copy(
place_value,
tmp.val,
fn_abi.args[i].layout,
);
op.val = arg.val;
}
LocalRef::UnsizedPlace(_) => {
span_bug!(fn_span, "unsized types are not supported")
}
LocalRef::PendingOperand => {
span_bug!(fn_span, "argument local should not be pending")
}
};

bx.lifetime_end(tmp.val.llval, tmp.layout.size);
}
PassMode::Indirect { on_stack: true, .. } => {
// FIXME: some LLVM backends (notably x86) do not correctly pass byval
// arguments to tail calls (as of LLVM 21). See also:
//
// - https://github.com/rust-lang/rust/pull/144232#discussion_r2218543841
// - https://github.com/rust-lang/rust/issues/144855
Comment on lines -1297 to -1301
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment not accurate anymore, shouldn't we wait until we don't support llvm 21 anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can only use tail calls with nightly, which builds with the latest LLVM? We'll need to wait until we don't support LLVM 21 (or higher, given that e.g. riscv will only add support in LLVM 23) for stabilization, but not for experimentation.

if let PassMode::Indirect { on_stack: false, .. } = fn_abi.args[i].mode {
let Some(tmp) = tail_call_temporaries[i].take() else {
span_bug!(
fn_span,
"arguments using PassMode::Indirect {{ on_stack: true, .. }} are currently not supported for tail calls"
"missing temporary for indirect tail call argument #{i}"
)
}
_ => (),
};

let local = self.mir.args_iter().nth(i).unwrap();

match &self.locals[local] {
LocalRef::Place(arg) => {
bx.typed_place_copy(arg.val, tmp.val, fn_abi.args[i].layout);
op.val = Ref(arg.val);
}
LocalRef::Operand(arg) => {
let Ref(place_value) = arg.val else {
bug!("only `Ref` should use `PassMode::Indirect`");
};
bx.typed_place_copy(place_value, tmp.val, fn_abi.args[i].layout);
op.val = arg.val;
}
LocalRef::UnsizedPlace(_) => {
span_bug!(fn_span, "unsized types are not supported")
}
LocalRef::PendingOperand => {
span_bug!(fn_span, "argument local should not be pending")
}
};

bx.lifetime_end(tmp.val.llval, tmp.layout.size);
}
}
}
Expand Down
97 changes: 97 additions & 0 deletions tests/ui/explicit-tail-calls/support/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//@ build-pass
//@ ignore-backends: gcc
//@ add-minicore
//@ min-llvm-version: 22
//
//@ revisions: i686
//@[i686] compile-flags: --target i686-unknown-linux-gnu
//@[i686] needs-llvm-components: x86
//@ revisions: x86-64
//@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu
//@[x86-64] needs-llvm-components: x86
//@ revisions: x86-64-win
//@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc
//@[x86-64-win] needs-llvm-components: x86
//@ revisions: arm
//@[arm] compile-flags: --target arm-unknown-linux-gnueabi
//@[arm] needs-llvm-components: arm
//@ revisions: thumb
//@[thumb] compile-flags: --target thumbv8m.main-none-eabi
//@[thumb] needs-llvm-components: arm
//@ revisions: aarch64
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
//@[aarch64] needs-llvm-components: aarch64
//@ revisions: s390x
//@[s390x] compile-flags: --target s390x-unknown-linux-gnu
//@[s390x] needs-llvm-components: systemz
//@ revisions: sparc
//@[sparc] compile-flags: --target sparc-unknown-linux-gnu
//@[sparc] needs-llvm-components: sparc
//@ revisions: sparc64
//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu
//@[sparc64] needs-llvm-components: sparc
//@ revisions: powerpc64
//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu
//@[powerpc64] needs-llvm-components: powerpc
//@ revisions: riscv
//@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu
//@[riscv] needs-llvm-components: riscv
//@ revisions: loongarch32
//@[loongarch32] compile-flags: --target loongarch32-unknown-none
//@[loongarch32] needs-llvm-components: loongarch
//@ revisions: loongarch64
//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu
//@[loongarch64] needs-llvm-components: loongarch
//@ revisions: bpf
//@[bpf] compile-flags: --target bpfeb-unknown-none
//@[bpf] needs-llvm-components: bpf
//@ revisions: m68k
//@[m68k] compile-flags: --target m68k-unknown-linux-gnu
//@[m68k] needs-llvm-components: m68k
//@ revisions: nvptx64
//@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda
//@[nvptx64] needs-llvm-components: nvptx
//
// Wasm needs a special target feature.
//
//@ revisions: wasm
//@[wasm] compile-flags: --target wasm32-unknown-unknown -Ctarget-feature=+tail-call
//@[wasm] needs-llvm-components: webassembly
//@ revisions: wasip1
//@[wasip1] compile-flags: --target wasm32-wasip1 -Ctarget-feature=+tail-call
//@[wasip1] needs-llvm-components: webassembly
//
// Failing cases (just zero support)
//
// //@ revisions: powerpc
// //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu
// //@[powerpc] needs-llvm-components: powerpc
// //@ revisions: aix
// //@[aix] compile-flags: --target powerpc64-ibm-aix
// //@[aix] needs-llvm-components: powerpc
// //@ revisions: csky
// //@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2
// //@[csky] needs-llvm-components: csky
// //@ revisions: mips
// //@[mips] compile-flags: --target mips-unknown-linux-gnu
// //@[mips] needs-llvm-components: mips
// //@ revisions: mips64
// //@[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64
// //@[mips64] needs-llvm-components: mips
#![feature(no_core, explicit_tail_calls)]
#![expect(incomplete_features)]
#![no_core]
#![crate_type = "lib"]

extern crate minicore;
use minicore::*;

#[inline(never)]
fn simple1(x: u64) -> u64 {
x
}

#[unsafe(no_mangle)]
fn simple2(x: u64) -> u64 {
become simple1(x);
}
108 changes: 108 additions & 0 deletions tests/ui/explicit-tail-calls/support/bystack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//@ build-pass
//@ ignore-backends: gcc
//@ add-minicore
//@ min-llvm-version: 22
//
//@ revisions: i686
//@[i686] compile-flags: --target i686-unknown-linux-gnu
//@[i686] needs-llvm-components: x86
//@ revisions: x86-64
//@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu
//@[x86-64] needs-llvm-components: x86
//@ revisions: x86-64-win
//@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc
//@[x86-64-win] needs-llvm-components: x86
//@ revisions: arm
//@[arm] compile-flags: --target arm-unknown-linux-gnueabi
//@[arm] needs-llvm-components: arm
//@ revisions: thumb
//@[thumb] compile-flags: --target thumbv8m.main-none-eabi
//@[thumb] needs-llvm-components: arm
//@ revisions: aarch64
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
//@[aarch64] needs-llvm-components: aarch64
//@ revisions: s390x
//@[s390x] compile-flags: --target s390x-unknown-linux-gnu
//@[s390x] needs-llvm-components: systemz
//@ revisions: sparc
//@[sparc] compile-flags: --target sparc-unknown-linux-gnu
//@[sparc] needs-llvm-components: sparc
//@ revisions: sparc64
//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu
//@[sparc64] needs-llvm-components: sparc
//@ revisions: powerpc64
//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu
//@[powerpc64] needs-llvm-components: powerpc
//@ revisions: loongarch32
//@[loongarch32] compile-flags: --target loongarch32-unknown-none
//@[loongarch32] needs-llvm-components: loongarch
//@ revisions: loongarch64
//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu
//@[loongarch64] needs-llvm-components: loongarch
//@ revisions: bpf
//@[bpf] compile-flags: --target bpfeb-unknown-none
//@[bpf] needs-llvm-components: bpf
//@ revisions: m68k
//@[m68k] compile-flags: --target m68k-unknown-linux-gnu
//@[m68k] needs-llvm-components: m68k
//@ revisions: nvptx64
//@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda
//@[nvptx64] needs-llvm-components: nvptx
//
// Riscv does not support byval in LLVM 22 (but wil in LLVM 23)
//
// //@ revisions: riscv
// //@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu
// //@[riscv] needs-llvm-components: riscv
//
// Wasm needs a special target feature.
//
//@ revisions: wasm
//@[wasm] compile-flags: --target wasm32-unknown-unknown -Ctarget-feature=+tail-call
//@[wasm] needs-llvm-components: webassembly
//@ revisions: wasip1
//@[wasip1] compile-flags: --target wasm32-wasip1 -Ctarget-feature=+tail-call
//@[wasip1] needs-llvm-components: webassembly
//
// Failing cases (just zero support)
//
// //@ revisions: powerpc
// //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu
// //@[powerpc] needs-llvm-components: powerpc
// //@ revisions: aix
// //@[aix] compile-flags: --target powerpc64-ibm-aix
// //@[aix] needs-llvm-components: powerpc
// //@ revisions: csky
// //@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2
// //@[csky] needs-llvm-components: csky
// //@ revisions: mips
// //@[mips] compile-flags: --target mips-unknown-linux-gnu
// //@[mips] needs-llvm-components: mips
// //@ revisions: mips64
// //@[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64
// //@[mips64] needs-llvm-components: mips
#![feature(no_core, explicit_tail_calls)]
#![expect(incomplete_features)]
#![no_core]
#![crate_type = "lib"]

extern crate minicore;
use minicore::*;

#[repr(C)]
struct PassedByVal {
a: u64,
b: u64,
c: u64,
d: u64,
}

#[inline(never)]
extern "C" fn callee(x: PassedByVal) -> PassedByVal {
x
}

#[unsafe(no_mangle)]
extern "C" fn byval(x: PassedByVal) -> PassedByVal {
become callee(x);
}
108 changes: 108 additions & 0 deletions tests/ui/explicit-tail-calls/support/byval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//@ build-pass
//@ ignore-backends: gcc
//@ add-minicore
//@ min-llvm-version: 22
//
//@ revisions: i686
//@[i686] compile-flags: --target i686-unknown-linux-gnu
//@[i686] needs-llvm-components: x86
//@ revisions: x86-64
//@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu
//@[x86-64] needs-llvm-components: x86
//@ revisions: x86-64-win
//@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc
//@[x86-64-win] needs-llvm-components: x86
//@ revisions: arm
//@[arm] compile-flags: --target arm-unknown-linux-gnueabi
//@[arm] needs-llvm-components: arm
//@ revisions: thumb
//@[thumb] compile-flags: --target thumbv8m.main-none-eabi
//@[thumb] needs-llvm-components: arm
//@ revisions: aarch64
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
//@[aarch64] needs-llvm-components: aarch64
//@ revisions: s390x
//@[s390x] compile-flags: --target s390x-unknown-linux-gnu
//@[s390x] needs-llvm-components: systemz
//@ revisions: sparc
//@[sparc] compile-flags: --target sparc-unknown-linux-gnu
//@[sparc] needs-llvm-components: sparc
//@ revisions: sparc64
//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu
//@[sparc64] needs-llvm-components: sparc
//@ revisions: powerpc64
//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu
//@[powerpc64] needs-llvm-components: powerpc
//@ revisions: loongarch32
//@[loongarch32] compile-flags: --target loongarch32-unknown-none
//@[loongarch32] needs-llvm-components: loongarch
//@ revisions: loongarch64
//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu
//@[loongarch64] needs-llvm-components: loongarch
//@ revisions: bpf
//@[bpf] compile-flags: --target bpfeb-unknown-none
//@[bpf] needs-llvm-components: bpf
//@ revisions: m68k
//@[m68k] compile-flags: --target m68k-unknown-linux-gnu
//@[m68k] needs-llvm-components: m68k
//@ revisions: nvptx64
//@[nvptx64] compile-flags: --target nvptx64-nvidia-cuda
//@[nvptx64] needs-llvm-components: nvptx
//
// Riscv does not support byval in LLVM 22 (but wil in LLVM 23)
//
// //@ revisions: riscv
// //@[riscv] compile-flags: --target riscv64gc-unknown-linux-gnu
// //@[riscv] needs-llvm-components: riscv
//
// Wasm needs a special target feature.
//
//@ revisions: wasm
//@[wasm] compile-flags: --target wasm32-unknown-unknown -Ctarget-feature=+tail-call
//@[wasm] needs-llvm-components: webassembly
//@ revisions: wasip1
//@[wasip1] compile-flags: --target wasm32-wasip1 -Ctarget-feature=+tail-call
//@[wasip1] needs-llvm-components: webassembly
//
// Failing cases (just zero support)
//
// //@ revisions: powerpc
// //@[powerpc] compile-flags: --target powerpc-unknown-linux-gnu
// //@[powerpc] needs-llvm-components: powerpc
// //@ revisions: aix
// //@[aix] compile-flags: --target powerpc64-ibm-aix
// //@[aix] needs-llvm-components: powerpc
// //@ revisions: csky
// //@[csky] compile-flags: --target csky-unknown-linux-gnuabiv2
// //@[csky] needs-llvm-components: csky
// //@ revisions: mips
// //@[mips] compile-flags: --target mips-unknown-linux-gnu
// //@[mips] needs-llvm-components: mips
// //@ revisions: mips64
// //@[mips64] compile-flags: --target mips64-unknown-linux-gnuabi64
// //@[mips64] needs-llvm-components: mips
#![feature(no_core, explicit_tail_calls)]
#![expect(incomplete_features)]
#![no_core]
#![crate_type = "lib"]

extern crate minicore;
use minicore::*;

// The rust calling convention will pass this by-value.
struct PassedByVal {
a: u64,
b: u64,
c: u64,
d: u64,
}

#[inline(never)]
fn callee(x: PassedByVal) -> PassedByVal {
x
}

#[unsafe(no_mangle)]
fn byval(x: PassedByVal) -> PassedByVal {
become callee(x);
}
Loading