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:
commit
edda7e959d
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -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 } }
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user