Skip to content

Commit

Permalink
Auto merge of rust-lang#94216 - psumbera:sparc64-abi-fix2, r=nagisa
Browse files Browse the repository at this point in the history
more complete sparc64 ABI fix for aggregates with floating point members

Previous fix didn't handle nested structures at all.
  • Loading branch information
bors committed Feb 28, 2022
2 parents 48132ca + 992c27c commit edda7e9
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 100 deletions.
276 changes: 176 additions & 100 deletions compiler/rustc_target/src/abi/call/sparc64.rs
Original file line number Diff line number Diff line change
@@ -1,136 +1,212 @@
// 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};

#[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 is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
fn arg_scalar<C>(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> 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;
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;
}

let valid_unit = match unit.kind {
RegKind::Integer => false,
RegKind::Float => false,
RegKind::Vector => arg.layout.size.bits() == 128,
};
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;
}

valid_unit.then_some(Uniform { unit, total: arg.layout.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 classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
fn arg_scalar_pair<C>(
cx: &C,
scalar1: &Scalar,
scalar2: &Scalar,
mut offset: Size,
mut data: Sdata,
) -> Sdata
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout,
{
if !arg.layout.is_aggregate() {
arg.extend_integer_width_to(64);
return;
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;
}

// 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 (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,
{
if let abi::FieldsShape::Union(_) = layout.fields {
return data;
}

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;
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 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;
_ => {
if let abi::Abi::ScalarPair(scalar1, scalar2) = &layout.abi {
data = arg_scalar_pair(cx, scalar1, scalar2, offset, data);
}

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;
}
}

return data;
}

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,
{
if !arg.layout.is_aggregate() {
arg.extend_integer_width_to(64);
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 });
}

Expand Down
20 changes: 20 additions & 0 deletions src/test/codegen/sparc-struct-abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 } }
}

0 comments on commit edda7e9

Please sign in to comment.