Fix RISC-V C function ABI when passing/returning structs containing floats
This commit is contained in:
@@ -2,9 +2,7 @@ use rustc_abi::{
|
||||
BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface,
|
||||
};
|
||||
|
||||
use crate::callconv::{
|
||||
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, PassMode, Uniform,
|
||||
};
|
||||
use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform};
|
||||
|
||||
fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) {
|
||||
// Always sign extend u32 values on 64-bit mips
|
||||
@@ -140,16 +138,7 @@ where
|
||||
|
||||
// Extract first 8 chunks as the prefix
|
||||
let rest_size = size - Size::from_bytes(8) * prefix_index as u64;
|
||||
arg.cast_to(CastTarget {
|
||||
prefix,
|
||||
rest: Uniform::new(Reg::i64(), rest_size),
|
||||
attrs: ArgAttributes {
|
||||
regular: ArgAttribute::default(),
|
||||
arg_ext: ArgExtension::None,
|
||||
pointee_size: Size::ZERO,
|
||||
pointee_align: None,
|
||||
},
|
||||
});
|
||||
arg.cast_to(CastTarget::prefixed(prefix, Uniform::new(Reg::i64(), rest_size)));
|
||||
}
|
||||
|
||||
pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
|
||||
|
||||
@@ -197,6 +197,17 @@ impl ArgAttributes {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArgAttribute> for ArgAttributes {
|
||||
fn from(value: ArgAttribute) -> Self {
|
||||
Self {
|
||||
regular: value,
|
||||
arg_ext: ArgExtension::None,
|
||||
pointee_size: Size::ZERO,
|
||||
pointee_align: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An argument passed entirely registers with the
|
||||
/// same kind (e.g., HFA / HVA on PPC64 and AArch64).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
@@ -251,6 +262,9 @@ impl Uniform {
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
|
||||
pub struct CastTarget {
|
||||
pub prefix: [Option<Reg>; 8],
|
||||
/// The offset of `rest` from the start of the value. Currently only implemented for a `Reg`
|
||||
/// pair created by the `offset_pair` method.
|
||||
pub rest_offset: Option<Size>,
|
||||
pub rest: Uniform,
|
||||
pub attrs: ArgAttributes,
|
||||
}
|
||||
@@ -263,42 +277,45 @@ impl From<Reg> for CastTarget {
|
||||
|
||||
impl From<Uniform> for CastTarget {
|
||||
fn from(uniform: Uniform) -> CastTarget {
|
||||
CastTarget {
|
||||
prefix: [None; 8],
|
||||
rest: uniform,
|
||||
attrs: ArgAttributes {
|
||||
regular: ArgAttribute::default(),
|
||||
arg_ext: ArgExtension::None,
|
||||
pointee_size: Size::ZERO,
|
||||
pointee_align: None,
|
||||
},
|
||||
}
|
||||
Self::prefixed([None; 8], uniform)
|
||||
}
|
||||
}
|
||||
|
||||
impl CastTarget {
|
||||
pub fn pair(a: Reg, b: Reg) -> CastTarget {
|
||||
CastTarget {
|
||||
pub fn prefixed(prefix: [Option<Reg>; 8], rest: Uniform) -> Self {
|
||||
Self { prefix, rest_offset: None, rest, attrs: ArgAttributes::new() }
|
||||
}
|
||||
|
||||
pub fn offset_pair(a: Reg, offset_from_start: Size, b: Reg) -> Self {
|
||||
Self {
|
||||
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,
|
||||
},
|
||||
rest_offset: Some(offset_from_start),
|
||||
rest: b.into(),
|
||||
attrs: ArgAttributes::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_attrs(mut self, attrs: ArgAttributes) -> Self {
|
||||
self.attrs = attrs;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pair(a: Reg, b: Reg) -> CastTarget {
|
||||
Self::prefixed([Some(a), None, None, None, None, None, None, None], Uniform::from(b))
|
||||
}
|
||||
|
||||
/// When you only access the range containing valid data, you can use this unaligned size;
|
||||
/// otherwise, use the safer `size` method.
|
||||
pub fn unaligned_size<C: HasDataLayout>(&self, _cx: &C) -> Size {
|
||||
// Prefix arguments are passed in specific designated registers
|
||||
let prefix_size = self
|
||||
.prefix
|
||||
.iter()
|
||||
.filter_map(|x| x.map(|reg| reg.size))
|
||||
.fold(Size::ZERO, |acc, size| acc + size);
|
||||
let prefix_size = if let Some(offset_from_start) = self.rest_offset {
|
||||
offset_from_start
|
||||
} else {
|
||||
self.prefix
|
||||
.iter()
|
||||
.filter_map(|x| x.map(|reg| reg.size))
|
||||
.fold(Size::ZERO, |acc, size| acc + size)
|
||||
};
|
||||
// Remaining arguments are passed in chunks of the unit size
|
||||
let rest_size =
|
||||
self.rest.unit.size * self.rest.total.bytes().div_ceil(self.rest.unit.size.bytes());
|
||||
@@ -322,9 +339,22 @@ impl CastTarget {
|
||||
/// Checks if these two `CastTarget` are equal enough to be considered "the same for all
|
||||
/// function call ABIs".
|
||||
pub fn eq_abi(&self, other: &Self) -> bool {
|
||||
let CastTarget { prefix: prefix_l, rest: rest_l, attrs: attrs_l } = self;
|
||||
let CastTarget { prefix: prefix_r, rest: rest_r, attrs: attrs_r } = other;
|
||||
prefix_l == prefix_r && rest_l == rest_r && attrs_l.eq_abi(attrs_r)
|
||||
let CastTarget {
|
||||
prefix: prefix_l,
|
||||
rest_offset: rest_offset_l,
|
||||
rest: rest_l,
|
||||
attrs: attrs_l,
|
||||
} = self;
|
||||
let CastTarget {
|
||||
prefix: prefix_r,
|
||||
rest_offset: rest_offset_r,
|
||||
rest: rest_r,
|
||||
attrs: attrs_r,
|
||||
} = other;
|
||||
prefix_l == prefix_r
|
||||
&& rest_offset_l == rest_offset_r
|
||||
&& rest_l == rest_r
|
||||
&& attrs_l.eq_abi(attrs_r)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rustc_abi::{HasDataLayout, Reg, Size, TyAbiInterface};
|
||||
|
||||
use super::{ArgAttribute, ArgAttributes, ArgExtension, CastTarget};
|
||||
use super::CastTarget;
|
||||
use crate::callconv::{ArgAbi, FnAbi, Uniform};
|
||||
|
||||
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
|
||||
@@ -34,16 +34,10 @@ fn classify_aggregate<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||
};
|
||||
|
||||
if align_bytes == size.bytes() {
|
||||
arg.cast_to(CastTarget {
|
||||
prefix: [Some(reg), None, None, None, None, None, None, None],
|
||||
rest: Uniform::new(Reg::i8(), Size::from_bytes(0)),
|
||||
attrs: ArgAttributes {
|
||||
regular: ArgAttribute::default(),
|
||||
arg_ext: ArgExtension::None,
|
||||
pointee_size: Size::ZERO,
|
||||
pointee_align: None,
|
||||
},
|
||||
});
|
||||
arg.cast_to(CastTarget::prefixed(
|
||||
[Some(reg), None, None, None, None, None, None, None],
|
||||
Uniform::new(Reg::i8(), Size::ZERO),
|
||||
));
|
||||
} else {
|
||||
arg.cast_to(Uniform::new(reg, size));
|
||||
}
|
||||
@@ -78,11 +72,10 @@ where
|
||||
};
|
||||
if arg.layout.size.bytes() / align_bytes == 1 {
|
||||
// Make sure we pass the struct as array at the LLVM IR level and not as a single integer.
|
||||
arg.cast_to(CastTarget {
|
||||
prefix: [Some(unit), None, None, None, None, None, None, None],
|
||||
rest: Uniform::new(unit, Size::ZERO),
|
||||
attrs: ArgAttributes::new(),
|
||||
});
|
||||
arg.cast_to(CastTarget::prefixed(
|
||||
[Some(unit), None, None, None, None, None, None, None],
|
||||
Uniform::new(unit, Size::ZERO),
|
||||
));
|
||||
} else {
|
||||
arg.cast_to(Uniform::new(unit, arg.layout.size));
|
||||
}
|
||||
|
||||
@@ -14,16 +14,16 @@ use crate::spec::HasTargetSpec;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum RegPassKind {
|
||||
Float(Reg),
|
||||
Integer(Reg),
|
||||
Float { offset_from_start: Size, ty: Reg },
|
||||
Integer { offset_from_start: Size, ty: Reg },
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum FloatConv {
|
||||
FloatPair(Reg, Reg),
|
||||
FloatPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg },
|
||||
Float(Reg),
|
||||
MixedPair(Reg, Reg),
|
||||
MixedPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg },
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -43,6 +43,7 @@ fn should_use_fp_conv_helper<'a, Ty, C>(
|
||||
flen: u64,
|
||||
field1_kind: &mut RegPassKind,
|
||||
field2_kind: &mut RegPassKind,
|
||||
offset_from_start: Size,
|
||||
) -> Result<(), CannotUseFpConv>
|
||||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
@@ -55,16 +56,16 @@ where
|
||||
}
|
||||
match (*field1_kind, *field2_kind) {
|
||||
(RegPassKind::Unknown, _) => {
|
||||
*field1_kind = RegPassKind::Integer(Reg {
|
||||
kind: RegKind::Integer,
|
||||
size: arg_layout.size,
|
||||
});
|
||||
*field1_kind = RegPassKind::Integer {
|
||||
offset_from_start,
|
||||
ty: Reg { kind: RegKind::Integer, size: arg_layout.size },
|
||||
};
|
||||
}
|
||||
(RegPassKind::Float(_), RegPassKind::Unknown) => {
|
||||
*field2_kind = RegPassKind::Integer(Reg {
|
||||
kind: RegKind::Integer,
|
||||
size: arg_layout.size,
|
||||
});
|
||||
(RegPassKind::Float { .. }, RegPassKind::Unknown) => {
|
||||
*field2_kind = RegPassKind::Integer {
|
||||
offset_from_start,
|
||||
ty: Reg { kind: RegKind::Integer, size: arg_layout.size },
|
||||
};
|
||||
}
|
||||
_ => return Err(CannotUseFpConv),
|
||||
}
|
||||
@@ -75,12 +76,16 @@ where
|
||||
}
|
||||
match (*field1_kind, *field2_kind) {
|
||||
(RegPassKind::Unknown, _) => {
|
||||
*field1_kind =
|
||||
RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
|
||||
*field1_kind = RegPassKind::Float {
|
||||
offset_from_start,
|
||||
ty: Reg { kind: RegKind::Float, size: arg_layout.size },
|
||||
};
|
||||
}
|
||||
(_, RegPassKind::Unknown) => {
|
||||
*field2_kind =
|
||||
RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
|
||||
*field2_kind = RegPassKind::Float {
|
||||
offset_from_start,
|
||||
ty: Reg { kind: RegKind::Float, size: arg_layout.size },
|
||||
};
|
||||
}
|
||||
_ => return Err(CannotUseFpConv),
|
||||
}
|
||||
@@ -102,13 +107,14 @@ where
|
||||
flen,
|
||||
field1_kind,
|
||||
field2_kind,
|
||||
offset_from_start,
|
||||
);
|
||||
}
|
||||
return Err(CannotUseFpConv);
|
||||
}
|
||||
}
|
||||
FieldsShape::Array { count, .. } => {
|
||||
for _ in 0..count {
|
||||
for i in 0..count {
|
||||
let elem_layout = arg_layout.field(cx, 0);
|
||||
should_use_fp_conv_helper(
|
||||
cx,
|
||||
@@ -117,6 +123,7 @@ where
|
||||
flen,
|
||||
field1_kind,
|
||||
field2_kind,
|
||||
offset_from_start + elem_layout.size * i,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -127,7 +134,15 @@ where
|
||||
}
|
||||
for i in arg_layout.fields.index_by_increasing_offset() {
|
||||
let field = arg_layout.field(cx, i);
|
||||
should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?;
|
||||
should_use_fp_conv_helper(
|
||||
cx,
|
||||
&field,
|
||||
xlen,
|
||||
flen,
|
||||
field1_kind,
|
||||
field2_kind,
|
||||
offset_from_start + arg_layout.fields.offset(i),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -146,14 +161,52 @@ where
|
||||
{
|
||||
let mut field1_kind = RegPassKind::Unknown;
|
||||
let mut field2_kind = RegPassKind::Unknown;
|
||||
if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() {
|
||||
if should_use_fp_conv_helper(
|
||||
cx,
|
||||
arg,
|
||||
xlen,
|
||||
flen,
|
||||
&mut field1_kind,
|
||||
&mut field2_kind,
|
||||
Size::ZERO,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
match (field1_kind, field2_kind) {
|
||||
(RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)),
|
||||
(RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)),
|
||||
(RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)),
|
||||
(RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)),
|
||||
(
|
||||
RegPassKind::Integer { offset_from_start, .. }
|
||||
| RegPassKind::Float { offset_from_start, .. },
|
||||
_,
|
||||
) if offset_from_start != Size::ZERO => {
|
||||
panic!("type {:?} has a first field with non-zero offset {offset_from_start:?}", arg.ty)
|
||||
}
|
||||
(
|
||||
RegPassKind::Integer { ty: first_ty, .. },
|
||||
RegPassKind::Float { offset_from_start, ty: second_ty },
|
||||
) => Some(FloatConv::MixedPair {
|
||||
first_ty,
|
||||
second_ty_offset_from_start: offset_from_start,
|
||||
second_ty,
|
||||
}),
|
||||
(
|
||||
RegPassKind::Float { ty: first_ty, .. },
|
||||
RegPassKind::Integer { offset_from_start, ty: second_ty },
|
||||
) => Some(FloatConv::MixedPair {
|
||||
first_ty,
|
||||
second_ty_offset_from_start: offset_from_start,
|
||||
second_ty,
|
||||
}),
|
||||
(
|
||||
RegPassKind::Float { ty: first_ty, .. },
|
||||
RegPassKind::Float { offset_from_start, ty: second_ty },
|
||||
) => Some(FloatConv::FloatPair {
|
||||
first_ty,
|
||||
second_ty_offset_from_start: offset_from_start,
|
||||
second_ty,
|
||||
}),
|
||||
(RegPassKind::Float { ty, .. }, RegPassKind::Unknown) => Some(FloatConv::Float(ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -171,11 +224,19 @@ where
|
||||
FloatConv::Float(f) => {
|
||||
arg.cast_to(f);
|
||||
}
|
||||
FloatConv::FloatPair(l, r) => {
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty } => {
|
||||
arg.cast_to(CastTarget::offset_pair(
|
||||
first_ty,
|
||||
second_ty_offset_from_start,
|
||||
second_ty,
|
||||
));
|
||||
}
|
||||
FloatConv::MixedPair(l, r) => {
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty } => {
|
||||
arg.cast_to(CastTarget::offset_pair(
|
||||
first_ty,
|
||||
second_ty_offset_from_start,
|
||||
second_ty,
|
||||
));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -239,15 +300,27 @@ fn classify_arg<'a, Ty, C>(
|
||||
arg.cast_to(f);
|
||||
return;
|
||||
}
|
||||
Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => {
|
||||
Some(FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty })
|
||||
if *avail_fprs >= 2 =>
|
||||
{
|
||||
*avail_fprs -= 2;
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
arg.cast_to(CastTarget::offset_pair(
|
||||
first_ty,
|
||||
second_ty_offset_from_start,
|
||||
second_ty,
|
||||
));
|
||||
return;
|
||||
}
|
||||
Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => {
|
||||
Some(FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty })
|
||||
if *avail_fprs >= 1 && *avail_gprs >= 1 =>
|
||||
{
|
||||
*avail_gprs -= 1;
|
||||
*avail_fprs -= 1;
|
||||
arg.cast_to(CastTarget::pair(l, r));
|
||||
arg.cast_to(CastTarget::offset_pair(
|
||||
first_ty,
|
||||
second_ty_offset_from_start,
|
||||
second_ty,
|
||||
));
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
|
||||
@@ -5,9 +5,7 @@ use rustc_abi::{
|
||||
TyAndLayout,
|
||||
};
|
||||
|
||||
use crate::callconv::{
|
||||
ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Uniform,
|
||||
};
|
||||
use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform};
|
||||
use crate::spec::HasTargetSpec;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -197,16 +195,10 @@ where
|
||||
rest_size = rest_size - Reg::i32().size;
|
||||
}
|
||||
|
||||
arg.cast_to(CastTarget {
|
||||
prefix: data.prefix,
|
||||
rest: Uniform::new(Reg::i64(), rest_size),
|
||||
attrs: ArgAttributes {
|
||||
regular: data.arg_attribute,
|
||||
arg_ext: ArgExtension::None,
|
||||
pointee_size: Size::ZERO,
|
||||
pointee_align: None,
|
||||
},
|
||||
});
|
||||
arg.cast_to(
|
||||
CastTarget::prefixed(data.prefix, Uniform::new(Reg::i64(), rest_size))
|
||||
.with_attrs(data.arg_attribute.into()),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user