diff options
Diffstat (limited to 'tests/codegen')
| -rw-r--r-- | tests/codegen/abi-win64-zst.rs | 52 | ||||
| -rw-r--r-- | tests/codegen/f128-wasm32-callconv.rs | 49 | ||||
| -rw-r--r-- | tests/codegen/gpu-kernel-abi.rs | 18 | ||||
| -rw-r--r-- | tests/codegen/i128-wasm32-callconv.rs | 49 | ||||
| -rw-r--r-- | tests/codegen/min-function-alignment.rs | 43 | ||||
| -rw-r--r-- | tests/codegen/naked-fn/min-function-alignment.rs | 44 | ||||
| -rw-r--r-- | tests/codegen/slice-init.rs | 26 | ||||
| -rw-r--r-- | tests/codegen/vec-in-place.rs | 54 | ||||
| -rw-r--r-- | tests/codegen/vec_pop_push_noop.rs | 3 |
9 files changed, 323 insertions, 15 deletions
diff --git a/tests/codegen/abi-win64-zst.rs b/tests/codegen/abi-win64-zst.rs new file mode 100644 index 00000000000..dd361898144 --- /dev/null +++ b/tests/codegen/abi-win64-zst.rs @@ -0,0 +1,52 @@ +//@ compile-flags: -Z merge-functions=disabled + +//@ revisions: windows-gnu +//@[windows-gnu] compile-flags: --target x86_64-pc-windows-gnu +//@[windows-gnu] needs-llvm-components: x86 + +//@ revisions: windows-msvc +//@[windows-msvc] compile-flags: --target x86_64-pc-windows-msvc +//@[windows-msvc] needs-llvm-components: x86 + +// Also test what happens when using a Windows ABI on Linux. +//@ revisions: linux +//@[linux] compile-flags: --target x86_64-unknown-linux-gnu +//@[linux] needs-llvm-components: x86 + +#![feature(no_core, lang_items, rustc_attrs, abi_vectorcall)] +#![no_core] +#![crate_type = "lib"] + +#[lang = "sized"] +trait Sized {} + +// Make sure the argument is always passed when explicitly requesting a Windows ABI. +// Our goal here is to match clang: <https://clang.godbolt.org/z/Wr4jMWq3P>. + +// CHECK: define win64cc void @pass_zst_win64(ptr {{[^,]*}}) +#[no_mangle] +extern "win64" fn pass_zst_win64(_: ()) {} + +// CHECK: define x86_vectorcallcc void @pass_zst_vectorcall(ptr {{[^,]*}}) +#[no_mangle] +extern "vectorcall" fn pass_zst_vectorcall(_: ()) {} + +// windows-gnu: define void @pass_zst_fastcall(ptr {{[^,]*}}) +// windows-msvc: define void @pass_zst_fastcall(ptr {{[^,]*}}) +#[no_mangle] +#[cfg(windows)] // "fastcall" is not valid on 64bit Linux +extern "fastcall" fn pass_zst_fastcall(_: ()) {} + +// The sysv64 ABI ignores ZST. + +// CHECK: define x86_64_sysvcc void @pass_zst_sysv64() +#[no_mangle] +extern "sysv64" fn pass_zst_sysv64(_: ()) {} + +// For `extern "C"` functions, ZST are ignored on Linux put passed on Windows. + +// linux: define void @pass_zst_c() +// windows-msvc: define void @pass_zst_c(ptr {{[^,]*}}) +// windows-gnu: define void @pass_zst_c(ptr {{[^,]*}}) +#[no_mangle] +extern "C" fn pass_zst_c(_: ()) {} diff --git a/tests/codegen/f128-wasm32-callconv.rs b/tests/codegen/f128-wasm32-callconv.rs new file mode 100644 index 00000000000..8b1b5e7fb01 --- /dev/null +++ b/tests/codegen/f128-wasm32-callconv.rs @@ -0,0 +1,49 @@ +//! Verify that Rust implements the expected calling convention for `f128` + +//@ add-core-stubs +//@ compile-flags: -O --target wasm32-wasip1 +//@ needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items, f128)] + +extern crate minicore; + +extern "C" { + fn extern_call(arg0: f128); + fn extern_ret() -> f128; +} + +#[no_mangle] +pub extern "C" fn pass(_arg0: u32, arg1: f128) { + // CHECK-LABEL: @pass( + // an f128 is passed via registers + // CHECK-SAME: fp128 noundef %arg1 + // CHECK: call void @extern_call + unsafe { extern_call(arg1) }; +} + +// Check that we produce the correct return ABI +#[no_mangle] +pub extern "C" fn ret(_arg0: u32, arg1: f128) -> f128 { + // CHECK-LABEL: @ret( + // but an f128 is returned via the stack + // CHECK-SAME: sret + // CHECK: store fp128 %arg1 + // CHECK-NEXT: ret void + arg1 +} + +// Check that we consume the correct return ABI +#[no_mangle] +pub extern "C" fn forward(dst: *mut f128) { + // CHECK-LABEL: @forward + // CHECK-SAME: ptr{{.*}} %dst) + // without optimizatons, an intermediate alloca is used + // CHECK: call void @extern_ret + // CHECK: store fp128 + // CHECK: ret void + unsafe { *dst = extern_ret() }; +} diff --git a/tests/codegen/gpu-kernel-abi.rs b/tests/codegen/gpu-kernel-abi.rs new file mode 100644 index 00000000000..fba17936494 --- /dev/null +++ b/tests/codegen/gpu-kernel-abi.rs @@ -0,0 +1,18 @@ +// Checks that the gpu-kernel calling convention correctly translates to LLVM calling conventions. + +//@ revisions: nvptx +//@ [nvptx] compile-flags: --crate-type=rlib --target=nvptx64-nvidia-cuda +//@ [nvptx] needs-llvm-components: nvptx +#![feature(no_core, lang_items, abi_gpu_kernel)] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} + +// nvptx: define ptx_kernel void @fun(i32 +#[no_mangle] +pub extern "gpu-kernel" fn fun(_: i32) {} diff --git a/tests/codegen/i128-wasm32-callconv.rs b/tests/codegen/i128-wasm32-callconv.rs new file mode 100644 index 00000000000..c6d25fbe8be --- /dev/null +++ b/tests/codegen/i128-wasm32-callconv.rs @@ -0,0 +1,49 @@ +//! Verify that Rust implements the expected calling convention for `i128`/`u128`. + +//@ add-core-stubs +//@ compile-flags: -O --target wasm32-wasip1 +//@ needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items)] + +extern crate minicore; + +extern "C" { + fn extern_call(arg0: i128); + fn extern_ret() -> i128; +} + +#[no_mangle] +pub extern "C" fn pass(_arg0: u32, arg1: i128) { + // CHECK-LABEL: @pass( + // an i128 is passed via registers + // CHECK-SAME: i128 noundef %arg1 + // CHECK: call void @extern_call + unsafe { extern_call(arg1) }; +} + +// Check that we produce the correct return ABI +#[no_mangle] +pub extern "C" fn ret(_arg0: u32, arg1: i128) -> i128 { + // CHECK-LABEL: @ret( + // but an i128 is returned via the stack + // CHECK-SAME: sret + // CHECK: store i128 %arg1 + // CHECK-NEXT: ret void + arg1 +} + +// Check that we consume the correct return ABI +#[no_mangle] +pub extern "C" fn forward(dst: *mut i128) { + // CHECK-LABEL: @forward + // CHECK-SAME: ptr{{.*}} %dst) + // without optimizatons, an intermediate alloca is used + // CHECK: call void @extern_ret + // CHECK: store i128 + // CHECK: ret void + unsafe { *dst = extern_ret() }; +} diff --git a/tests/codegen/min-function-alignment.rs b/tests/codegen/min-function-alignment.rs new file mode 100644 index 00000000000..7c0ad12402a --- /dev/null +++ b/tests/codegen/min-function-alignment.rs @@ -0,0 +1,43 @@ +//@ revisions: align16 align1024 +//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 +//@ [align16] compile-flags: -Zmin-function-alignment=16 +//@ [align1024] compile-flags: -Zmin-function-alignment=1024 + +#![crate_type = "lib"] +#![feature(fn_align)] + +// functions without explicit alignment use the global minimum +// +// CHECK-LABEL: @no_explicit_align +// align16: align 16 +// align1024: align 1024 +#[no_mangle] +pub fn no_explicit_align() {} + +// CHECK-LABEL: @lower_align +// align16: align 16 +// align1024: align 1024 +#[no_mangle] +#[repr(align(8))] +pub fn lower_align() {} + +// the higher value of min-function-alignment and repr(align) wins out +// +// CHECK-LABEL: @higher_align +// align16: align 32 +// align1024: align 1024 +#[no_mangle] +#[repr(align(32))] +pub fn higher_align() {} + +// cold functions follow the same rules as other functions +// +// in GCC, the `-falign-functions` does not apply to cold functions, but +// `-Zmin-function-alignment` applies to all functions. +// +// CHECK-LABEL: @no_explicit_align_cold +// align16: align 16 +// align1024: align 1024 +#[no_mangle] +#[cold] +pub fn no_explicit_align_cold() {} diff --git a/tests/codegen/naked-fn/min-function-alignment.rs b/tests/codegen/naked-fn/min-function-alignment.rs new file mode 100644 index 00000000000..1330d796d39 --- /dev/null +++ b/tests/codegen/naked-fn/min-function-alignment.rs @@ -0,0 +1,44 @@ +//@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmin-function-alignment=16 +//@ needs-asm-support +//@ ignore-arm no "ret" mnemonic + +#![feature(naked_functions, fn_align)] +#![crate_type = "lib"] + +// functions without explicit alignment use the global minimum +// +// CHECK: .balign 16 +#[no_mangle] +#[naked] +pub unsafe extern "C" fn naked_no_explicit_align() { + core::arch::naked_asm!("ret") +} + +// CHECK: .balign 16 +#[no_mangle] +#[repr(align(8))] +#[naked] +pub unsafe extern "C" fn naked_lower_align() { + core::arch::naked_asm!("ret") +} + +// CHECK: .balign 32 +#[no_mangle] +#[repr(align(32))] +#[naked] +pub unsafe extern "C" fn naked_higher_align() { + core::arch::naked_asm!("ret") +} + +// cold functions follow the same rules as other functions +// +// in GCC, the `-falign-functions` does not apply to cold functions, but +// `-Zmin-function-alignment` applies to all functions. +// +// CHECK: .balign 16 +#[no_mangle] +#[cold] +#[naked] +pub unsafe extern "C" fn no_explicit_align_cold() { + core::arch::naked_asm!("ret") +} diff --git a/tests/codegen/slice-init.rs b/tests/codegen/slice-init.rs index 8126bf84618..1c2dd3e8875 100644 --- a/tests/codegen/slice-init.rs +++ b/tests/codegen/slice-init.rs @@ -63,6 +63,32 @@ pub fn nonzero_integer_array() { opaque(&x); } +const N: usize = 100; + +// CHECK-LABEL: @u16_init_one_bytes +#[no_mangle] +pub fn u16_init_one_bytes() -> [u16; N] { + // CHECK-NOT: select + // CHECK-NOT: br + // CHECK-NOT: switch + // CHECK-NOT: icmp + // CHECK: call void @llvm.memset.p0 + [const { u16::from_be_bytes([1, 1]) }; N] +} + +// FIXME: undef bytes can just be initialized with the same value as the +// defined bytes, if the defines bytes are all the same. +// CHECK-LABEL: @option_none_init +#[no_mangle] +pub fn option_none_init() -> [Option<u8>; N] { + // CHECK-NOT: select + // CHECK: br label %repeat_loop_header{{.*}} + // CHECK-NOT: switch + // CHECK: icmp + // CHECK-NOT: call void @llvm.memset.p0 + [None; N] +} + // Use an opaque function to prevent rustc from removing useless drops. #[inline(never)] pub fn opaque(_: impl Sized) {} diff --git a/tests/codegen/vec-in-place.rs b/tests/codegen/vec-in-place.rs index 33de0913f77..e835a7ef69b 100644 --- a/tests/codegen/vec-in-place.rs +++ b/tests/codegen/vec-in-place.rs @@ -37,8 +37,11 @@ pub struct Baz { #[no_mangle] pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> { // CHECK-NOT: loop - // CHECK: call - // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: loop + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) // CHECK-NOT: loop // CHECK-NOT: call vec.into_iter().map(|e| e as u8).collect() @@ -48,8 +51,11 @@ pub fn vec_iterator_cast_primitive(vec: Vec<i8>) -> Vec<u8> { #[no_mangle] pub fn vec_iterator_cast_wrapper(vec: Vec<u8>) -> Vec<Wrapper<u8>> { // CHECK-NOT: loop - // CHECK: call - // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: loop + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) // CHECK-NOT: loop // CHECK-NOT: call vec.into_iter().map(|e| Wrapper(e)).collect() @@ -76,8 +82,11 @@ pub fn vec_iterator_cast_signed_nested(vec: Vec<Vec<i32>>) -> Vec<Vec<u32>> { #[no_mangle] pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> { // CHECK-NOT: loop - // CHECK: call - // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: loop + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) // CHECK-NOT: loop // CHECK-NOT: call vec.into_iter().map(|e| e.0).collect() @@ -87,8 +96,11 @@ pub fn vec_iterator_cast_unwrap(vec: Vec<Wrapper<u8>>) -> Vec<u8> { #[no_mangle] pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> { // CHECK-NOT: loop - // CHECK: call - // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: loop + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) // CHECK-NOT: loop // CHECK-NOT: call vec.into_iter().map(|e| unsafe { std::mem::transmute(e) }).collect() @@ -98,8 +110,11 @@ pub fn vec_iterator_cast_aggregate(vec: Vec<[u64; 4]>) -> Vec<Foo> { #[no_mangle] pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> { // CHECK-NOT: loop - // CHECK: call - // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: loop + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) // CHECK-NOT: loop // CHECK-NOT: call @@ -114,8 +129,11 @@ pub fn vec_iterator_cast_deaggregate_tra(vec: Vec<Bar>) -> Vec<[u64; 4]> { #[no_mangle] pub fn vec_iterator_cast_deaggregate_fold(vec: Vec<Baz>) -> Vec<[u64; 4]> { // CHECK-NOT: loop - // CHECK: call - // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: loop + // CHECK-NOT: call + // CHECK: call{{.+}}void @llvm.assume(i1 %{{.+}}) // CHECK-NOT: loop // CHECK-NOT: call @@ -138,6 +156,12 @@ pub fn vec_iterator_cast_unwrap_drop(vec: Vec<Wrapper<String>>) -> Vec<String> { // CHECK-NOT: call // CHECK-NOT: %{{.*}} = mul // CHECK-NOT: %{{.*}} = udiv + // CHECK: call + // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} + // CHECK-NOT: call + // CHECK-NOT: %{{.*}} = mul + // CHECK-NOT: %{{.*}} = udiv vec.into_iter().map(|Wrapper(e)| e).collect() } @@ -154,6 +178,12 @@ pub fn vec_iterator_cast_wrap_drop(vec: Vec<String>) -> Vec<Wrapper<String>> { // CHECK-NOT: call // CHECK-NOT: %{{.*}} = mul // CHECK-NOT: %{{.*}} = udiv + // CHECK: call + // CHECK-SAME: void @llvm.assume(i1 %{{.+}}) + // CHECK-NOT: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} + // CHECK-NOT: call + // CHECK-NOT: %{{.*}} = mul + // CHECK-NOT: %{{.*}} = udiv // CHECK: ret void vec.into_iter().map(Wrapper).collect() diff --git a/tests/codegen/vec_pop_push_noop.rs b/tests/codegen/vec_pop_push_noop.rs index a8ad5b6f1a3..4821e840884 100644 --- a/tests/codegen/vec_pop_push_noop.rs +++ b/tests/codegen/vec_pop_push_noop.rs @@ -12,9 +12,6 @@ pub fn noop(v: &mut Vec<u8>) { // CHECK-NOT: call // CHECK: tail call void @llvm.assume // CHECK-NOT: grow_one - // llvm-pre-19: call - // llvm-pre-19-same: void @llvm.assume - // llvm-pre-19-NOT: grow_one // CHECK-NOT: call // CHECK: {{ret|[}]}} if let Some(x) = v.pop() { |
