| // Tests that MIPS targets return structs that are up to 128 bits large, only | |
| // consist of 1 or 2 floating point fields and have a first field with offset 0 | |
| // in FPRs rather than GPRs. | |
| // | |
| //@ add-minicore | |
| //@ assembly-output: emit-asm | |
| //@ compile-flags: -Copt-level=3 | |
| // | |
| //@ revisions: LE | |
| //@[LE] compile-flags: --target=mips64el-unknown-linux-gnuabi64 | |
| //@[LE] needs-llvm-components: mips | |
| //@ revisions: BE | |
| //@[BE] compile-flags: --target=mips64-unknown-linux-gnuabi64 | |
| //@[BE] needs-llvm-components: mips | |
| #![crate_type = "lib"] | |
| #![feature(no_core, f128)] | |
| #![no_core] | |
| extern crate minicore; | |
| // 128-bit large struct with one floating point field, should be returned in | |
| // $f0 and $f1 to match the de-facto GCC ABI. | |
| #[repr(C)] | |
| struct SingleF128 { | |
| x: f128, | |
| } | |
| // 64-bit large struct with one floating point field, should be returned in $f0. | |
| #[repr(C)] | |
| struct SingleF64 { | |
| x: f64, | |
| } | |
| // 32-bit large struct with one floating point field, should be returned in $f0. | |
| #[repr(C)] | |
| struct SingleF32 { | |
| x: f32, | |
| } | |
| // 128-bit large struct with two floating point fields, should be returned in | |
| // $f0 and $f2. | |
| #[repr(C)] | |
| struct TwoF64 { | |
| x: f64, | |
| y: f64, | |
| } | |
| // 64-bit large struct with two floating point fields, should be returned in | |
| // $f0 and $f2. | |
| #[repr(C)] | |
| struct TwoF32 { | |
| x: f32, | |
| y: f32, | |
| } | |
| // 96-bit large struct with two floating point fields, should be returned in | |
| // $f0 and $f2. | |
| #[repr(C)] | |
| struct F32AndF64 { | |
| x: f32, | |
| y: f64, | |
| } | |
| // 96-bit large struct with two floating-point fields, should be returned in | |
| // $f0 and $f2. | |
| #[repr(C)] | |
| struct F64AndF32 { | |
| x: f64, | |
| y: f32, | |
| } | |
| // CHECK-LABEL: single_f128 | |
| // CHECK: dmtc1 $4, $f0 | |
| // CHECK-NEXT: jr $ra | |
| // CHECK-NEXT: dmtc1 $5, $f1 | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn single_f128(a: SingleF128) -> SingleF128 { | |
| a | |
| } | |
| // CHECK-LABEL: single_f64 | |
| // CHECK: jr $ra | |
| // CHECK-NEXT: mov.d $f0, $f12 | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn single_f64(a: SingleF64) -> SingleF64 { | |
| a | |
| } | |
| // CHECK-LABEL: single_f32 | |
| // BE: dsrl $1, $4, 32 | |
| // LE: sll $1, $4, 0 | |
| // BE-NEXT: sll $1, $1, 0 | |
| // CHECK-NEXT: jr $ra | |
| // CHECK-NEXT: mtc1 $1, $f0 | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn single_f32(a: SingleF32) -> SingleF32 { | |
| a | |
| } | |
| // CHECK-LABEL: two_f64 | |
| // CHECK: mov.d $f2, $f13 | |
| // CHECK-NEXT: jr $ra | |
| // CHECK-NEXT: mov.d $f0, $f12 | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn two_f64(a: TwoF64) -> TwoF64 { | |
| a | |
| } | |
| // CHECK-LABEL: two_f32 | |
| // CHECK: sll $1, $4, 0 | |
| // LE-NEXT: mtc1 $1, $f0 | |
| // BE-NEXT: mtc1 $1, $f2 | |
| // CHECK-NEXT: dsrl $1, $4, 32 | |
| // CHECK-NEXT: sll $1, $1, 0 | |
| // CHECK-NEXT: jr $ra | |
| // LE-NEXT: mtc1 $1, $f2 | |
| // BE-NEXT: mtc1 $1, $f0 | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn two_f32(a: TwoF32) -> TwoF32 { | |
| a | |
| } | |
| // We deviate from Clang in the order of instructions in the next two functions | |
| // but that's okay | |
| // CHECK-LABEL: f32_and_f64 | |
| // BE: dsrl $1, $4, 32 | |
| // LE: mov.d $f2, $f13 | |
| // BE-NEXT: mov.d $f2, $f13 | |
| // LE-NEXT: sll $1, $4, 0 | |
| // BE-NEXT: sll $1, $1, 0 | |
| // CHECK-NEXT: jr $ra | |
| // CHECK-NEXT: mtc1 $1, $f0 | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn f32_and_f64(a: F32AndF64) -> F32AndF64 { | |
| a | |
| } | |
| // CHECK-LABEL: f64_and_f32 | |
| // BE: dsrl $1, $5, 32 | |
| // LE: mov.d $f0, $f12 | |
| // BE-NEXT: mov.d $f0, $f12 | |
| // LE-NEXT: sll $1, $5, 0 | |
| // BE-NEXT: sll $1, $1, 0 | |
| // CHECK-NEXT: jr $ra | |
| // CHECK-NEXT: mtc1 $1, $f2 | |
| #[unsafe(no_mangle)] | |
| pub extern "C" fn f64_and_f32(a: F64AndF32) -> F64AndF32 { | |
| a | |
| } |