Skip to content

Add assembly test for -Zreg-struct-return option #145382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
150 changes: 150 additions & 0 deletions tests/assembly-llvm/reg-struct-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//! 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 small_t {
a: i8,
b: i8,
}

unsafe extern "C" {
fn small() -> small_t;
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn small_callee() -> small_t {
// (42 << 8) | 42 = 10794

// WITH-LABEL: small_callee
// WITH: movw $10794, %ax
// WITH: retl

// WITHOUT-LABEL: small_callee
// WITHOUT-DAG: movl 4(%esp), %e{{.*}}
// WITHOUT-DAG: movw $10794, (%e{{.*}})
// WITHOUT: retl $4
small_t { a: 42, b: 42 }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn small_caller(dst: &mut small_t) {
// WITH-LABEL: small_caller
// WITH: calll small
// WITH: movw %ax, (%e{{.*}})

// WITHOUT-LABEL: small_caller
// WITHOUT: calll small
// WITHOUT: subl $4, %esp
*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 pivot_t {
a: i32,
b: i32,
}

unsafe extern "C" {
fn pivot() -> pivot_t;
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn pivot_callee() -> pivot_t {
// WITH-LABEL: pivot_callee
// WITH-DAG: movl $42, %e{{.*}}
// WITH-DAG: 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
pivot_t { a: 42, b: 42 }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn pivot_caller(dst: &mut pivot_t) {
// 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: subl $4, %esp
*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, both WITH and WITHOUT should use the hidden pointer convention (i.e, they
// should be identical).
mod large {
struct large_t {
a: i32,
b: i32,
c: i32,
}

unsafe extern "C" {
fn large() -> large_t;
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn large_callee() -> large_t {
// WITH-LABEL: large_callee
// WITH: movl 4(%esp), %e{{.*}}
// WITH-DAG: movl $42, (%e{{.*}})
// WITH-DAG: movl $42, 4(%e{{.*}})
// WITH-DAG: movl $42, 8(%e{{.*}})
// WITH: retl $4

// WITHOUT-LABEL: large_callee
// WITHOUT: movl 4(%esp), %e{{.*}}
// WITHOUT-DAG: movl $42, (%e{{.*}})
// WITHOUT-DAG: movl $42, 4(%e{{.*}})
// WITHOUT-DAG: movl $42, 8(%e{{.*}})
// WITHOUT: retl $4
large_t { a: 42, b: 42, c: 42 }
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn large_caller(dst: *mut large_t) {
// WITH-LABEL: large_caller
// WITH: calll large
// WITH: subl $4, %esp

// WITHOUT-LABEL: large_caller
// WITHOUT: calll large
// WITHOUT: subl $4, %esp
*dst = large();
}
}
Loading