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

more complete sparc64 ABI fix for aggregates with floating point members

Previous fix didn't handle nested structures at all.
This commit is contained in:
bors 2022-02-28 11:54:17 +00:00
commit edda7e959d
2 changed files with 200 additions and 104 deletions

View File

@ -1,29 +1,135 @@
// FIXME: This needs an audit for correctness and completeness.
use crate::abi::call::{
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, RegKind, Uniform,
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, Uniform,
};
use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
use crate::abi::{self, HasDataLayout, Scalar, Size, TyAbiInterface, TyAndLayout};
fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
#[derive(Clone, Debug)]
pub struct Sdata {
pub prefix: [Option<Reg>; 8],
pub prefix_index: usize,
pub last_offset: Size,
pub has_float: bool,
pub arg_attribute: ArgAttribute,
}
fn arg_scalar<C>(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata
where
C: HasDataLayout,
{
let dl = cx.data_layout();
if scalar.value != abi::F32 && scalar.value != abi::F64 {
return data;
}
data.has_float = true;
if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset {
if data.prefix_index == data.prefix.len() {
return data;
}
data.prefix[data.prefix_index] = Some(Reg::i32());
data.prefix_index += 1;
data.last_offset = data.last_offset + Reg::i32().size;
}
for _ in 0..((offset - data.last_offset).bits() / 64)
.min((data.prefix.len() - data.prefix_index) as u64)
{
data.prefix[data.prefix_index] = Some(Reg::i64());
data.prefix_index += 1;
data.last_offset = data.last_offset + Reg::i64().size;
}
if data.last_offset < offset {
if data.prefix_index == data.prefix.len() {
return data;
}
data.prefix[data.prefix_index] = Some(Reg::i32());
data.prefix_index += 1;
data.last_offset = data.last_offset + Reg::i32().size;
}
if data.prefix_index == data.prefix.len() {
return data;
}
if scalar.value == abi::F32 {
data.arg_attribute = ArgAttribute::InReg;
data.prefix[data.prefix_index] = Some(Reg::f32());
data.last_offset = offset + Reg::f32().size;
} else {
data.prefix[data.prefix_index] = Some(Reg::f64());
data.last_offset = offset + Reg::f64().size;
}
data.prefix_index += 1;
return data;
}
fn arg_scalar_pair<C>(
cx: &C,
scalar1: &Scalar,
scalar2: &Scalar,
mut offset: Size,
mut data: Sdata,
) -> Sdata
where
C: HasDataLayout,
{
data = arg_scalar(cx, &scalar1, offset, data);
if scalar1.value == abi::F32 {
offset += Reg::f32().size;
} else if scalar2.value == abi::F64 {
offset += Reg::f64().size;
} else if let abi::Int(i, _signed) = scalar1.value {
offset += i.size();
} else if scalar1.value == abi::Pointer {
offset = offset + Reg::i64().size;
}
if (offset.raw % 4) != 0 && (scalar2.value == abi::F32 || scalar2.value == abi::F64) {
offset.raw += 4 - (offset.raw % 4);
}
data = arg_scalar(cx, &scalar2, offset, data);
return data;
}
fn parse_structure<'a, Ty, C>(
cx: &C,
layout: TyAndLayout<'a, Ty>,
mut data: Sdata,
mut offset: Size,
) -> Sdata
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
// Ensure we have at most eight uniquely addressable members.
if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
return None;
if let abi::FieldsShape::Union(_) = layout.fields {
return data;
}
match layout.abi {
abi::Abi::Scalar(scalar) => {
data = arg_scalar(cx, &scalar, offset, data);
}
abi::Abi::Aggregate { .. } => {
for i in 0..layout.fields.count().clone() {
if offset < layout.fields.offset(i) {
offset = layout.fields.offset(i);
}
data = parse_structure(cx, layout.field(cx, i).clone(), data.clone(), offset);
}
}
_ => {
if let abi::Abi::ScalarPair(scalar1, scalar2) = &layout.abi {
data = arg_scalar_pair(cx, scalar1, scalar2, offset, data);
}
}
}
let valid_unit = match unit.kind {
RegKind::Integer => false,
RegKind::Float => false,
RegKind::Vector => arg.layout.size.bits() == 128,
};
valid_unit.then_some(Uniform { unit, total: arg.layout.size })
})
return data;
}
fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
@ -36,101 +142,71 @@ 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 > in_registers_max {
arg.make_indirect();
return;
}
match arg.layout.fields {
abi::FieldsShape::Primitive => unreachable!(),
abi::FieldsShape::Array { .. } => {
// Arrays are passed indirectly
arg.make_indirect();
return;
}
abi::FieldsShape::Union(_) => {
// Unions and are always treated as a series of 64-bit integer chunks
}
abi::FieldsShape::Arbitrary { .. } => {
// Stuctures with floating point numbers need special care.
let mut data = parse_structure(
cx,
arg.layout.clone(),
Sdata {
prefix: [None; 8],
prefix_index: 0,
last_offset: Size::ZERO,
has_float: false,
arg_attribute: ArgAttribute::default(),
},
Size { raw: 0 },
);
if data.has_float {
// Structure { float, int, int } doesn't like to be handled like
// { float, long int }. Other way around it doesn't mind.
if data.last_offset < arg.layout.size
&& (data.last_offset.raw % 8) != 0
&& data.prefix_index < data.prefix.len()
{
data.prefix[data.prefix_index] = Some(Reg::i32());
data.prefix_index += 1;
data.last_offset += Reg::i32().size;
}
let mut rest_size = arg.layout.size - data.last_offset;
if (rest_size.raw % 8) != 0 && data.prefix_index < data.prefix.len() {
data.prefix[data.prefix_index] = Some(Reg::i32());
rest_size = rest_size - Reg::i32().size;
}
arg.cast_to(CastTarget {
prefix: data.prefix,
rest: Uniform { unit: Reg::i64(), total: rest_size },
attrs: ArgAttributes {
regular: data.arg_attribute,
arg_ext: ArgExtension::None,
pointee_size: Size::ZERO,
pointee_align: None,
},
});
return;
}
}
}
arg.cast_to(Uniform { unit: Reg::i64(), total });
}

View File

@ -81,3 +81,23 @@ pub struct FloatLongFloat {
pub extern "C" fn structfloatlongfloat() -> FloatLongFloat {
FloatLongFloat { f: 0.1, i: 123, g: 3.14 }
}
#[repr(C)]
pub struct FloatFloat {
f: f32,
g: f32,
}
#[repr(C)]
pub struct NestedStructs {
a: FloatFloat,
b: FloatFloat,
}
// CHECK: define inreg { float, float, float, float } @structnestestructs()
// CHECK-NEXT: start:
// CHECK-NEXT: ret { float, float, float, float } { float 0x3FB99999A0000000, float 0x3FF19999A0000000, float 0x40019999A0000000, float 0x400A666660000000 }
#[no_mangle]
pub extern "C" fn structnestestructs() -> NestedStructs {
NestedStructs { a: FloatFloat { f: 0.1, g: 1.1 }, b: FloatFloat { f: 2.2, g: 3.3 } }
}