-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
more complete sparc64 ABI fix for aggregates with floating point members #94216
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
} | ||
Comment on lines
+118
to
121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a silent failure (it will cause data corruption AFAICT). If the fields aren't in ascending order, this is a non-C-friendly type and you have to either ICE (which is arguably unfriendly), or better just fall back to "this cannot be passed in any optimized way". Sadly SPARC64 seems to be one of the architectures unfortunate enough to use "turn But still, you could use something similar to the Actually, wait, this is wronger than I thought, it doesn't add field offsets to the enclosing There's no way the testcases added in this PR could work, I think it's just hitting all the silent failure modes around here and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for looking at it! I'm sure there are problems with my fix. |
||
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 }); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's three issues here:
Abi::ScalarPair
should not be special-cased in call ABIs - it's justAbi::Aggregate
with extra Rust-only optimizations tacked on (none of the other issues matter if theparse_structure
codepath is taken)Abi::ScalarPair
(scalar1.size(cx).align_to(scalar2.align(cx).abi)
is more or less the typical formula, anything else risks being wrong for certain scalars)else if scalar2.value == abi::F64
is probably a typo? certainly seems like the intention is to get the size ofscalar1
(which is justscalar1.size(cx)
)As it stands, I believe this code passes
(u128, f64)
incorrectly (thanks to alignment, I can't think of anything else that would be mismatched), so it was probably unobserved for that reason.