//! 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-core-stubs //@ 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 { 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 { 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 { 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(); } }