| //! Tests that -Zreg-struct-return changes ABI for small struct returns | |
| //! from hidden-pointer convention to register-return on x86_32. | |
| //! This test covers: | |
| //! * Callee side, verifying that the structs are correctly loaded into registers when | |
| //! `-Zreg-struct-return` is activated | |
| //! * Caller side, verifying callers do receive returned structs in registers when | |
| //! `-Zreg-struct-return` is activated | |
| //@ add-minicore | |
| //@ assembly-output: emit-asm | |
| //@ compile-flags: -O --target=i686-unknown-linux-gnu -Crelocation-model=static | |
| //@ revisions: WITH WITHOUT | |
| //@[WITH] compile-flags: -Zreg-struct-return | |
| //@ needs-llvm-components: x86 | |
| #![feature(no_core)] | |
| #![no_std] | |
| #![no_core] | |
| #![crate_type = "lib"] | |
| extern crate minicore; | |
| use minicore::*; | |
| // Verifies ABI changes for small structs, where both fields fit into one register. | |
| // WITH is expected to use register return, WITHOUT should use hidden pointer. | |
| mod Small { | |
| #[repr(C)] | |
| struct SmallStruct { | |
| a: i8, | |
| b: i8, | |
| } | |
| unsafe extern "C" { | |
| fn small() -> SmallStruct; | |
| } | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn small_callee() -> SmallStruct { | |
| // (42 << 8) | 42 = 10794 | |
| // WITH-LABEL: small_callee | |
| // WITH: movw $10794, %ax | |
| // WITH: retl | |
| // WITHOUT-LABEL: small_callee | |
| // WITHOUT: movl 4(%esp), %e{{.*}} | |
| // WITHOUT: movw $10794, (%e{{.*}}) | |
| // WITHOUT: retl $4 | |
| SmallStruct { a: 42, b: 42 } | |
| } | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn small_caller(dst: &mut SmallStruct) { | |
| // WITH-LABEL: small_caller | |
| // WITH: calll small | |
| // WITH: movw %ax, (%e{{.*}}) | |
| // WITHOUT-LABEL: small_caller | |
| // WITHOUT: calll small | |
| // WITHOUT: movzwl {{.*}}(%esp), %e[[TMP:..]] | |
| // WITHOUT: movw %[[TMP]], (%e{{..}}) | |
| *dst = small(); | |
| } | |
| } | |
| // Verifies ABI changes for a struct of size 8, which is the maximum size | |
| // for reg-struct-return. | |
| // WITH is expected to still use register return, WITHOUT should use hidden | |
| // pointer. | |
| mod Pivot { | |
| #[repr(C)] | |
| struct PivotStruct { | |
| a: i32, | |
| b: i32, | |
| } | |
| unsafe extern "C" { | |
| fn pivot() -> PivotStruct; | |
| } | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn pivot_callee() -> PivotStruct { | |
| // WITH-LABEL: pivot_callee | |
| // WITH: movl $42, %e{{.*}} | |
| // WITH: movl $42, %e{{.*}} | |
| // WITH: retl | |
| // WITHOUT-LABEL: pivot_callee | |
| // WITHOUT: movl 4(%esp), %e{{.*}} | |
| // WITHOUT-DAG: movl $42, (%e{{.*}}) | |
| // WITHOUT-DAG: movl $42, 4(%e{{.*}}) | |
| // WITHOUT: retl $4 | |
| PivotStruct { a: 42, b: 42 } | |
| } | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn pivot_caller(dst: &mut PivotStruct) { | |
| // WITH-LABEL: pivot_caller | |
| // WITH: calll pivot | |
| // WITH-DAG: movl %e{{.*}}, 4(%e{{.*}}) | |
| // WITH-DAG: movl %e{{.*}}, (%e{{.*}}) | |
| // WITHOUT-LABEL: pivot_caller | |
| // WITHOUT: calll pivot | |
| // WITHOUT: movsd {{.*}}(%esp), %[[TMP:xmm.]] | |
| // WITHOUT: movsd %[[TMP]], (%e{{..}}) | |
| *dst = pivot(); | |
| } | |
| } | |
| // Verifies ABI changes for a struct of size 12, which is larger than the | |
| // maximum size for reg-struct-return (8 bytes). | |
| // Here, the hidden pointer convention should be used even when `-Zreg-struct-return` is set. | |
| mod Large { | |
| #[repr(C)] | |
| struct LargeStruct { | |
| a: i32, | |
| b: i32, | |
| c: i32, | |
| } | |
| unsafe extern "C" { | |
| fn large() -> LargeStruct; | |
| } | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn large_callee() -> LargeStruct { | |
| // CHECK-LABEL: large_callee | |
| // CHECK: movl 4(%esp), %e{{.*}} | |
| // CHECK-DAG: movl $42, (%e{{.*}}) | |
| // CHECK-DAG: movl $42, 4(%e{{.*}}) | |
| // CHECK-DAG: movl $42, 8(%e{{.*}}) | |
| // CHECK: retl $4 | |
| LargeStruct { a: 42, b: 42, c: 42 } | |
| } | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn large_caller(dst: &mut LargeStruct) { | |
| // CHECK-LABEL: large_caller | |
| // CHECK: calll large | |
| // CHECK-DAG: movl {{.*}}(%esp), %[[TMP1:e..]] | |
| // CHECK-DAG: movl %[[TMP1]], {{.*}}(%e{{..}}) | |
| // CHECK-DAG: movsd {{.*}}(%esp), %[[TMP2:xmm.]] | |
| // CHECK-DAG: movsd %[[TMP2]], {{.*}}(%e{{..}}) | |
| *dst = large(); | |
| } | |
| } |