blob: b251d791d5136e7db4254691207a40d03f0cfe8a [file] [log] [blame]
//! 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();
}
}