Auto merge of #91003 - psumbera:sparc64-abi, r=nagisa

fix sparc64 ABI for aggregates with floating point members

Fixes #86163
This commit is contained in:
bors 2021-12-02 02:59:44 +00:00
commit a2b7b7891e
8 changed files with 272 additions and 58 deletions

View File

@ -71,7 +71,7 @@ fn cast_target_to_abi_params(cast: CastTarget) -> SmallVec<[AbiParam; 2]> {
.prefix
.iter()
.flatten()
.map(|&kind| reg_to_abi_param(Reg { kind, size: cast.prefix_chunk_size }))
.map(|&reg| reg_to_abi_param(reg))
.chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)))
.collect::<SmallVec<_>>();

View File

@ -48,8 +48,8 @@ impl GccType for CastTarget {
let mut args: Vec<_> = self
.prefix
.iter()
.flat_map(|option_kind| {
option_kind.map(|kind| Reg { kind, size: self.prefix_chunk_size }.gcc_type(cx))
.flat_map(|option_reg| {
option_reg.map(|reg| reg.gcc_type(cx))
})
.chain((0..rest_count).map(|_| rest_gcc_unit))
.collect();

View File

@ -181,9 +181,7 @@ impl LlvmType for CastTarget {
let mut args: Vec<_> = self
.prefix
.iter()
.flat_map(|option_kind| {
option_kind.map(|kind| Reg { kind, size: self.prefix_chunk_size }.llvm_type(cx))
})
.flat_map(|option_reg| option_reg.map(|reg| reg.llvm_type(cx)))
.chain((0..rest_count).map(|_| rest_ll_unit))
.collect();
@ -466,6 +464,9 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
);
}
}
PassMode::Cast(cast) => {
cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
}
_ => {}
}
for arg in &self.args {
@ -497,8 +498,8 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
apply(a);
apply(b);
}
PassMode::Cast(_) => {
apply(&ArgAttributes::new());
PassMode::Cast(cast) => {
apply(&cast.attrs);
}
}
}
@ -533,6 +534,13 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
);
}
}
PassMode::Cast(cast) => {
cast.attrs.apply_attrs_to_callsite(
llvm::AttributePlace::ReturnValue,
&bx.cx,
callsite,
);
}
_ => {}
}
if let abi::Abi::Scalar(scalar) = self.ret.layout.abi {
@ -577,8 +585,8 @@ impl<'tcx> FnAbiLlvmExt<'tcx> for FnAbi<'tcx, Ty<'tcx>> {
apply(bx.cx, a);
apply(bx.cx, b);
}
PassMode::Cast(_) => {
apply(bx.cx, &ArgAttributes::new());
PassMode::Cast(cast) => {
apply(bx.cx, &cast.attrs);
}
}
}

View File

@ -1,4 +1,6 @@
use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
use crate::abi::call::{
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, Reg, Uniform,
};
use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) {
@ -115,7 +117,7 @@ where
for _ in 0..((offset - last_offset).bits() / 64)
.min((prefix.len() - prefix_index) as u64)
{
prefix[prefix_index] = Some(RegKind::Integer);
prefix[prefix_index] = Some(Reg::i64());
prefix_index += 1;
}
@ -123,7 +125,7 @@ where
break;
}
prefix[prefix_index] = Some(RegKind::Float);
prefix[prefix_index] = Some(Reg::f64());
prefix_index += 1;
last_offset = offset + Reg::f64().size;
}
@ -137,8 +139,13 @@ where
let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
arg.cast_to(CastTarget {
prefix,
prefix_chunk_size: Size::from_bytes(8),
rest: Uniform { unit: Reg::i64(), total: rest_size },
attrs: ArgAttributes {
regular: ArgAttribute::default(),
arg_ext: ArgExtension::None,
pointee_size: Size::ZERO,
pointee_align: None,
},
});
}

View File

@ -214,9 +214,9 @@ impl Uniform {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
pub struct CastTarget {
pub prefix: [Option<RegKind>; 8],
pub prefix_chunk_size: Size,
pub prefix: [Option<Reg>; 8],
pub rest: Uniform,
pub attrs: ArgAttributes,
}
impl From<Reg> for CastTarget {
@ -227,29 +227,48 @@ impl From<Reg> for CastTarget {
impl From<Uniform> for CastTarget {
fn from(uniform: Uniform) -> CastTarget {
CastTarget { prefix: [None; 8], prefix_chunk_size: Size::ZERO, rest: uniform }
CastTarget {
prefix: [None; 8],
rest: uniform,
attrs: ArgAttributes {
regular: ArgAttribute::default(),
arg_ext: ArgExtension::None,
pointee_size: Size::ZERO,
pointee_align: None,
},
}
}
}
impl CastTarget {
pub fn pair(a: Reg, b: Reg) -> CastTarget {
CastTarget {
prefix: [Some(a.kind), None, None, None, None, None, None, None],
prefix_chunk_size: a.size,
prefix: [Some(a), None, None, None, None, None, None, None],
rest: Uniform::from(b),
attrs: ArgAttributes {
regular: ArgAttribute::default(),
arg_ext: ArgExtension::None,
pointee_size: Size::ZERO,
pointee_align: None,
},
}
}
pub fn size<C: HasDataLayout>(&self, cx: &C) -> Size {
(self.prefix_chunk_size * self.prefix.iter().filter(|x| x.is_some()).count() as u64)
.align_to(self.rest.align(cx))
+ self.rest.total
pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size {
let mut size = self.rest.total;
for i in 0..self.prefix.iter().count() {
match self.prefix[i] {
Some(v) => size += Size { raw: v.size.bytes() },
None => {}
}
}
return size;
}
pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
self.prefix
.iter()
.filter_map(|x| x.map(|kind| Reg { kind, size: self.prefix_chunk_size }.align(cx)))
.filter_map(|x| x.map(|reg| reg.align(cx)))
.fold(cx.data_layout().aggregate_align.abi.max(self.rest.align(cx)), |acc, align| {
acc.max(align)
})

View File

@ -1,7 +1,9 @@
// FIXME: This needs an audit for correctness and completeness.
use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
use crate::abi::{HasDataLayout, TyAbiInterface};
use crate::abi::call::{
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, RegKind, Uniform,
};
use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
where
@ -16,7 +18,7 @@ where
let valid_unit = match unit.kind {
RegKind::Integer => false,
RegKind::Float => true,
RegKind::Float => false,
RegKind::Vector => arg.layout.size.bits() == 128,
};
@ -24,33 +26,7 @@ where
})
}
fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !ret.layout.is_aggregate() {
ret.extend_integer_width_to(64);
return;
}
if let Some(uniform) = is_homogeneous_aggregate(cx, ret) {
ret.cast_to(uniform);
return;
}
let size = ret.layout.size;
let bits = size.bits();
if bits <= 256 {
let unit = Reg::i64();
ret.cast_to(Uniform { unit, total: size });
return;
}
// don't return aggregates in registers
ret.make_indirect();
}
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
@ -60,13 +36,97 @@ where
return;
}
// This doesn't intentionally handle structures with floats which needs
// special care below.
if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
arg.cast_to(uniform);
return;
}
if let abi::FieldsShape::Arbitrary { .. } = arg.layout.fields {
let dl = cx.data_layout();
let size = arg.layout.size;
let mut prefix = [None; 8];
let mut prefix_index = 0;
let mut last_offset = Size::ZERO;
let mut has_float = false;
let mut arg_attribute = ArgAttribute::default();
for i in 0..arg.layout.fields.count() {
let field = arg.layout.field(cx, i);
let offset = arg.layout.fields.offset(i);
if let abi::Abi::Scalar(scalar) = &field.abi {
if scalar.value == abi::F32 || scalar.value == abi::F64 {
has_float = true;
if !last_offset.is_aligned(dl.f64_align.abi) && last_offset < offset {
if prefix_index == prefix.len() {
break;
}
prefix[prefix_index] = Some(Reg::i32());
prefix_index += 1;
last_offset = last_offset + Reg::i32().size;
}
for _ in 0..((offset - last_offset).bits() / 64)
.min((prefix.len() - prefix_index) as u64)
{
prefix[prefix_index] = Some(Reg::i64());
prefix_index += 1;
last_offset = last_offset + Reg::i64().size;
}
if last_offset < offset {
if prefix_index == prefix.len() {
break;
}
prefix[prefix_index] = Some(Reg::i32());
prefix_index += 1;
last_offset = last_offset + Reg::i32().size;
}
if prefix_index == prefix.len() {
break;
}
if scalar.value == abi::F32 {
arg_attribute = ArgAttribute::InReg;
prefix[prefix_index] = Some(Reg::f32());
last_offset = offset + Reg::f32().size;
} else {
prefix[prefix_index] = Some(Reg::f64());
last_offset = offset + Reg::f64().size;
}
prefix_index += 1;
}
}
}
if has_float && arg.layout.size <= in_registers_max {
let mut rest_size = size - last_offset;
if (rest_size.raw % 8) != 0 && prefix_index < prefix.len() {
prefix[prefix_index] = Some(Reg::i32());
rest_size = rest_size - Reg::i32().size;
}
arg.cast_to(CastTarget {
prefix,
rest: Uniform { unit: Reg::i64(), total: rest_size },
attrs: ArgAttributes {
regular: arg_attribute,
arg_ext: ArgExtension::None,
pointee_size: Size::ZERO,
pointee_align: None,
},
});
return;
}
}
let total = arg.layout.size;
if total.bits() > 128 {
if total > in_registers_max {
arg.make_indirect();
return;
}
@ -80,13 +140,13 @@ where
C: HasDataLayout,
{
if !fn_abi.ret.is_ignore() {
classify_ret(cx, &mut fn_abi.ret);
classify_arg(cx, &mut fn_abi.ret, Size { raw: 32 });
}
for arg in &mut fn_abi.args {
if arg.is_ignore() {
continue;
}
classify_arg(cx, arg);
classify_arg(cx, arg, Size { raw: 16 });
}
}

View File

@ -0,0 +1,64 @@
// Test SPARC64 ABI
// - float structure members are passes in floating point registers
// (#86163)
// assembly-output: emit-asm
// needs-llvm-components: sparc
// compile-flags: --target=sparcv9-sun-solaris -Copt-level=3
#![crate_type = "lib"]
#![feature(no_core, lang_items)]
#![no_core]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
#[repr(C)]
pub struct Franta {
a: f32,
b: f32,
c: f32,
d: f32,
}
// NB: due to delay slots the `ld` following the call is actually executed before the call.
#[no_mangle]
pub unsafe extern "C" fn callee(arg: Franta) {
// CHECK-LABEL: callee:
// CHECK: st %f3, [[PLACE_D:.*]]
// CHECK: st %f2, [[PLACE_C:.*]]
// CHECK: st %f1, [[PLACE_B:.*]]
// CHECK: st %f0, [[PLACE_A:.*]]
// CHECK: call tst_use
// CHECK-NEXT: ld [[PLACE_A]], %f1
// CHECK: call tst_use
// CHECK-NEXT: ld [[PLACE_B]], %f1
// CHECK: call tst_use
// CHECK-NEXT: ld [[PLACE_C]], %f1
// CHECK: call tst_use
// CHECK-NEXT: ld [[PLACE_D]], %f1
clobber();
tst_use(arg.a);
tst_use(arg.b);
tst_use(arg.c);
tst_use(arg.d);
}
extern "C" {
fn opaque_callee(arg: Franta, intarg: i32);
fn tst_use(arg: f32);
fn clobber();
}
#[no_mangle]
pub unsafe extern "C" fn caller() {
// CHECK-LABEL: caller:
// CHECK: ld [{{.*}}], %f0
// CHECK: ld [{{.*}}], %f1
// CHECK: ld [{{.*}}], %f2
// CHECK: ld [{{.*}}], %f3
// CHECK: call opaque_callee
// CHECK: mov 3, %o2
opaque_callee(Franta { a: 1.0, b: 2.0, c: 3.0, d: 4.0 }, 3);
}

View File

@ -1,5 +1,5 @@
// Checks that we correctly codegen extern "C" functions returning structs.
// See issue #52638.
// See issues #52638 and #86163.
// compile-flags: -O --target=sparc64-unknown-linux-gnu --crate-type=rlib
// needs-llvm-components: sparc
@ -25,3 +25,59 @@ pub struct Bool {
pub extern "C" fn structbool() -> Bool {
Bool { b: true }
}
#[repr(C)]
pub struct BoolFloat {
b: bool,
f: f32,
}
// CHECK: define inreg { i32, float } @structboolfloat()
// CHECK-NEXT: start:
// CHECK-NEXT: ret { i32, float } { i32 16777216, float 0x40091EB860000000 }
#[no_mangle]
pub extern "C" fn structboolfloat() -> BoolFloat {
BoolFloat { b: true, f: 3.14 }
}
// CHECK: define void @structboolfloat_input({ i32, float } inreg %0)
// CHECK-NEXT: start:
#[no_mangle]
pub extern "C" fn structboolfloat_input(a: BoolFloat) { }
#[repr(C)]
pub struct ShortDouble {
s: i16,
d: f64,
}
// CHECK: define { i64, double } @structshortdouble()
// CHECK-NEXT: start:
// CHECK-NEXT: ret { i64, double } { i64 34621422135410688, double 3.140000e+00 }
#[no_mangle]
pub extern "C" fn structshortdouble() -> ShortDouble {
ShortDouble { s: 123, d: 3.14 }
}
// CHECK: define void @structshortdouble_input({ i64, double } %0)
// CHECK-NEXT: start:
#[no_mangle]
pub extern "C" fn structshortdouble_input(a: ShortDouble) { }
#[repr(C)]
pub struct FloatLongFloat {
f: f32,
i: i64,
g: f32,
}
// CHECK: define inreg { float, i32, i64, float, i32 } @structfloatlongfloat()
// CHECK-NEXT: start:
// CHECK-NEXT: ret { float, i32, i64, float, i32 } { float 0x3FB99999A0000000, i32 undef, i64 123, float 0x40091EB860000000, i32 undef }
#[no_mangle]
pub extern "C" fn structfloatlongfloat() -> FloatLongFloat {
FloatLongFloat { f: 0.1, i: 123, g: 3.14 }
}