255 lines
6.6 KiB
Rust
255 lines
6.6 KiB
Rust
// Test SPARC64 ABI
|
|
// - float structure members are passes in floating point registers
|
|
// (#86163)
|
|
|
|
//@ add-minicore
|
|
//@ assembly-output: emit-asm
|
|
//@ needs-llvm-components: sparc
|
|
//@ compile-flags: --target=sparcv9-sun-solaris -Copt-level=3
|
|
#![crate_type = "lib"]
|
|
#![feature(no_core, lang_items)]
|
|
#![no_core]
|
|
#![feature(f128)]
|
|
|
|
extern crate minicore;
|
|
use minicore::*;
|
|
|
|
#[repr(C)]
|
|
pub struct Franta {
|
|
a: f32,
|
|
b: f32,
|
|
c: f32,
|
|
d: f32,
|
|
}
|
|
|
|
#[repr(C, packed)]
|
|
struct Misaligned(i32, f64);
|
|
|
|
#[repr(C)]
|
|
struct AlignToMakeAssemblyShorter<T>(T, f64);
|
|
|
|
#[repr(C)]
|
|
pub struct Floats(i32, f32, f64, f128);
|
|
|
|
#[repr(C)]
|
|
pub struct LessFloats(f32, i32, f64);
|
|
|
|
#[repr(C)]
|
|
pub struct NotMisaligned(i32, Misaligned);
|
|
|
|
#[repr(C, align(16))]
|
|
pub struct Align16(f64, i32, i32);
|
|
|
|
impl Copy for Misaligned {}
|
|
impl<T: Copy> Copy for AlignToMakeAssemblyShorter<T> {}
|
|
impl Copy for Floats {}
|
|
impl Copy for LessFloats {}
|
|
impl Copy for NotMisaligned {}
|
|
impl Copy for Align16 {}
|
|
|
|
// NB: due to delay slots the `ld` following the call is actually executed before the call.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn callee(arg: Franta) {
|
|
// CHECK-LABEL: callee:
|
|
// CHECK: st %f3, [[PLACE_D:.*]]
|
|
// CHECK: st %f2, [[PLACE_C:.*]]
|
|
// CHECK: st %f1, [[PLACE_B:.*]]
|
|
// CHECK: st %f0, [[PLACE_A:.*]]
|
|
// CHECK: call tst_use
|
|
// CHECK-NEXT: ld [[PLACE_A]], %f1
|
|
// CHECK: call tst_use
|
|
// CHECK-NEXT: ld [[PLACE_B]], %f1
|
|
// CHECK: call tst_use
|
|
// CHECK-NEXT: ld [[PLACE_C]], %f1
|
|
// CHECK: call tst_use
|
|
// CHECK-NEXT: ld [[PLACE_D]], %f1
|
|
clobber();
|
|
tst_use(arg.a);
|
|
tst_use(arg.b);
|
|
tst_use(arg.c);
|
|
tst_use(arg.d);
|
|
tail_call_avoidance_fn();
|
|
}
|
|
|
|
extern "C" {
|
|
fn opaque_callee(arg: Franta, intarg: i32);
|
|
fn tst_use(arg: f32);
|
|
fn clobber();
|
|
// This exists so that post-https://reviews.llvm.org/D138741 LLVM doesn't
|
|
// tail-call away some of our assertions.
|
|
fn tail_call_avoidance_fn();
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn caller() {
|
|
// CHECK-LABEL: caller:
|
|
// CHECK: ld [{{.*}}], %f0
|
|
// CHECK: ld [{{.*}}], %f1
|
|
// CHECK: ld [{{.*}}], %f2
|
|
// CHECK: ld [{{.*}}], %f3
|
|
// CHECK: call opaque_callee
|
|
// CHECK: mov 3, %o2
|
|
opaque_callee(Franta { a: 1.0, b: 2.0, c: 3.0, d: 4.0 }, 3);
|
|
tail_call_avoidance_fn();
|
|
}
|
|
|
|
// Check that misaligned floats aren't promoted to floating point registers.
|
|
// CHECK-LABEL: misaligned_arg:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn misaligned_arg(x: &mut AlignToMakeAssemblyShorter<Misaligned>, value: Misaligned) {
|
|
// CHECK: srlx %o2, 32, %o2
|
|
// CHECK-NEXT: stx %o1, [%o0]
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: st %o2, [%o0+8]
|
|
x.0 = value;
|
|
}
|
|
|
|
// CHECK-LABEL: misaligned_ret:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn misaligned_ret(x: &AlignToMakeAssemblyShorter<Misaligned>) -> Misaligned {
|
|
// CHECK: ld [%o0+8], %o1
|
|
// CHECK-NEXT: ldx [%o0], %o0
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: sllx %o1, 32, %o1
|
|
x.0
|
|
}
|
|
|
|
// Check structs where 32 >= size > 16 are promoted to register only as an argument.
|
|
// Also check that the various floating-point types are promoted to the correct registers.
|
|
// CHECK-LABEL: floats_arg:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn floats_arg(x: &mut Floats, value: Floats) {
|
|
// CHECK: ldx [%o1+24], %o2
|
|
// CHECK-NEXT: ldx [%o1+16], %o3
|
|
// CHECK-NEXT: ldx [%o1+8], %o4
|
|
// CHECK-NEXT: ldx [%o1], %o1
|
|
// CHECK-NEXT: stx %o2, [%o0+24]
|
|
// CHECK-NEXT: stx %o3, [%o0+16]
|
|
// CHECK-NEXT: stx %o4, [%o0+8]
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: stx %o1, [%o0]
|
|
*x = value;
|
|
}
|
|
|
|
// CHECK-LABEL: floats_ret:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn floats_ret(x: &Floats) -> Floats {
|
|
// CHECK: ld [%o0+4], %f1
|
|
// CHECK-NEXT: ldd [%o0+8], %f2
|
|
// CHECK-NEXT: ldd [%o0+16], %f4
|
|
// CHECK-NEXT: ld [%o0], %o1
|
|
// CHECK-NEXT: ldd [%o0+24], %f6
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: sllx %o1, 32, %o0
|
|
*x
|
|
}
|
|
|
|
// Check float promotion when passing as an argument with a struct where size <= 16.
|
|
// CHECK-LABEL: less_floats_arg:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn less_floats_arg(x: &mut LessFloats, value: LessFloats) {
|
|
// CHECK: st %f2, [%o0]
|
|
// CHECK-NEXT: st %o1, [%o0+4]
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: std %f4, [%o0+8]
|
|
*x = value;
|
|
}
|
|
|
|
// CHECK-LABEL: less_floats_ret:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn less_floats_ret(x: &LessFloats) -> LessFloats {
|
|
// CHECK: ld [%o0], %f0
|
|
// CHECK-NEXT: ldd [%o0+8], %f2
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: ld [%o0+4], %o0
|
|
*x
|
|
}
|
|
|
|
// Check fields are promoted if they are aligned in the overall structure.
|
|
// This matches Clang's behaviour but not GCC's.
|
|
// CHECK-LABEL: not_misaligned_arg:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn not_misaligned_arg(
|
|
x: &mut AlignToMakeAssemblyShorter<NotMisaligned>,
|
|
value: NotMisaligned,
|
|
) {
|
|
// CHECK: stx %o1, [%o0]
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: std %f4, [%o0+8]
|
|
x.0 = value;
|
|
}
|
|
|
|
// CHECK-LABEL: not_misaligned_ret:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn not_misaligned_ret(x: &AlignToMakeAssemblyShorter<NotMisaligned>) -> NotMisaligned {
|
|
// CHECK: ldx [%o0], %o1
|
|
// CHECK-NEXT: ldd [%o0+8], %f2
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: mov %o1, %o0
|
|
x.0
|
|
}
|
|
|
|
// Check that 16-aligned structs are allocated the correct registers.
|
|
// CHECK-LABEL: align_16_arg:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn align_16_arg(x: &mut Align16, value: Align16) {
|
|
// CHECK: std %f4, [%o0]
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: stx %o3, [%o0+8]
|
|
*x = value;
|
|
}
|
|
|
|
// CHECK-LABEL: align_16_ret:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn align_16_ret(x: &Align16) -> Align16 {
|
|
// CHECK: ldd [%o0], %f0
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: ldx [%o0+8], %o1
|
|
*x
|
|
}
|
|
|
|
// Check ZST args don't prevent further arguments from being processed.
|
|
// CHECK-LABEL: zst_arg:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn zst_arg(_: (), value: LessFloats, x: &mut LessFloats) {
|
|
// CHECK: st %f0, [%o2]
|
|
// CHECK-NEXT: st %o0, [%o2+4]
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: std %f2, [%o2+8]
|
|
*x = value;
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct I32F32Input {
|
|
a: i32,
|
|
b: f32,
|
|
}
|
|
|
|
#[repr(C)]
|
|
struct I32F32Output {
|
|
b: f32,
|
|
a: i32,
|
|
}
|
|
|
|
// The clang/LLVM implementation mentions that this case requires special handling.
|
|
// CHECK-LABEL: i32_f32:
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn i32_f32(input: I32F32Input) -> I32F32Output {
|
|
// CHECK: srlx %o0, 32, %o0
|
|
// CHECK-NEXT: fmovs %f1, %f0
|
|
// CHECK-NEXT: retl
|
|
// CHECK-NEXT: nop
|
|
I32F32Output { a: input.a, b: input.b }
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct C {
|
|
a: f64,
|
|
b: f32,
|
|
}
|
|
|
|
// regression test for https://github.com/rust-lang/rust/issues/147883.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn foo(c: C) -> C {
|
|
c
|
|
}
|