Fix RISC-V C function ABI when passing/returning structs containing floats

This commit is contained in:
beetrees 2025-04-03 23:53:06 +01:00
parent 68ac5abb06
commit 5723c9997c
No known key found for this signature in database
GPG key ID: 8791BD754191EBD6
13 changed files with 537 additions and 134 deletions

View file

@ -40,7 +40,18 @@ fn apply_attrs_to_abi_param(param: AbiParam, arg_attrs: ArgAttributes) -> AbiPar
}
}
fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[AbiParam; 2]> {
fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[(Size, AbiParam); 2]> {
if let Some(offset_from_start) = cast.rest_offset {
assert!(cast.prefix[1..].iter().all(|p| p.is_none()));
assert_eq!(cast.rest.unit.size, cast.rest.total);
let first = cast.prefix[0].unwrap();
let second = cast.rest.unit;
return smallvec![
(Size::ZERO, reg_to_abi_param(first)),
(offset_from_start, reg_to_abi_param(second))
];
}
let (rest_count, rem_bytes) = if cast.rest.unit.size.bytes() == 0 {
(0, 0)
} else {
@ -55,25 +66,32 @@ fn cast_target_to_abi_params(cast: &CastTarget) -> SmallVec<[AbiParam; 2]> {
// different types in Cranelift IR. Instead a single array of primitive types is used.
// Create list of fields in the main structure
let mut args = cast
let args = cast
.prefix
.iter()
.flatten()
.map(|&reg| reg_to_abi_param(reg))
.chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)))
.collect::<SmallVec<_>>();
.chain((0..rest_count).map(|_| reg_to_abi_param(cast.rest.unit)));
let mut res = SmallVec::new();
let mut offset = Size::ZERO;
for arg in args {
res.push((offset, arg));
offset += Size::from_bytes(arg.value_type.bytes());
}
// Append final integer
if rem_bytes != 0 {
// Only integers can be really split further.
assert_eq!(cast.rest.unit.kind, RegKind::Integer);
args.push(reg_to_abi_param(Reg {
kind: RegKind::Integer,
size: Size::from_bytes(rem_bytes),
}));
res.push((
offset,
reg_to_abi_param(Reg { kind: RegKind::Integer, size: Size::from_bytes(rem_bytes) }),
));
}
args
res
}
impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
@ -104,7 +122,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
},
PassMode::Cast { ref cast, pad_i32 } => {
assert!(!pad_i32, "padding support not yet implemented");
cast_target_to_abi_params(cast)
cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect()
}
PassMode::Indirect { attrs, meta_attrs: None, on_stack } => {
if on_stack {
@ -160,9 +178,10 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
}
_ => unreachable!("{:?}", self.layout.backend_repr),
},
PassMode::Cast { ref cast, .. } => {
(None, cast_target_to_abi_params(cast).into_iter().collect())
}
PassMode::Cast { ref cast, .. } => (
None,
cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect(),
),
PassMode::Indirect { attrs, meta_attrs: None, on_stack } => {
assert!(!on_stack);
(
@ -187,12 +206,14 @@ pub(super) fn to_casted_value<'tcx>(
) -> SmallVec<[Value; 2]> {
let (ptr, meta) = arg.force_stack(fx);
assert!(meta.is_none());
let mut offset = 0;
cast_target_to_abi_params(cast)
.into_iter()
.map(|param| {
let val = ptr.offset_i64(fx, offset).load(fx, param.value_type, MemFlags::new());
offset += i64::from(param.value_type.bytes());
.map(|(offset, param)| {
let val = ptr.offset_i64(fx, offset.bytes() as i64).load(
fx,
param.value_type,
MemFlags::new(),
);
val
})
.collect()
@ -205,7 +226,7 @@ pub(super) fn from_casted_value<'tcx>(
cast: &CastTarget,
) -> CValue<'tcx> {
let abi_params = cast_target_to_abi_params(cast);
let abi_param_size: u32 = abi_params.iter().map(|param| param.value_type.bytes()).sum();
let abi_param_size: u32 = abi_params.iter().map(|(_, param)| param.value_type.bytes()).sum();
let layout_size = u32::try_from(layout.size.bytes()).unwrap();
let ptr = fx.create_stack_slot(
// Stack slot size may be bigger for example `[u8; 3]` which is packed into an `i32`.
@ -214,16 +235,13 @@ pub(super) fn from_casted_value<'tcx>(
std::cmp::max(abi_param_size, layout_size),
u32::try_from(layout.align.abi.bytes()).unwrap(),
);
let mut offset = 0;
let mut block_params_iter = block_params.iter().copied();
for param in abi_params {
let val = ptr.offset_i64(fx, offset).store(
for (offset, _) in abi_params {
ptr.offset_i64(fx, offset.bytes() as i64).store(
fx,
block_params_iter.next().unwrap(),
MemFlags::new(),
);
offset += i64::from(param.value_type.bytes());
val
)
}
assert_eq!(block_params_iter.next(), None, "Leftover block param");
CValue::by_ref(ptr, layout)

View file

@ -626,7 +626,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
bx.lifetime_start(llscratch, scratch_size);
// ... where we first store the value...
bx.store(val, llscratch, scratch_align);
rustc_codegen_ssa::mir::store_cast(bx, cast, val, llscratch, scratch_align);
// ... and then memcpy it to the intended destination.
bx.memcpy(

View file

@ -229,7 +229,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
let llscratch = bx.alloca(scratch_size, scratch_align);
bx.lifetime_start(llscratch, scratch_size);
// ...store the value...
bx.store(val, llscratch, scratch_align);
rustc_codegen_ssa::mir::store_cast(bx, cast, val, llscratch, scratch_align);
// ... and then memcpy it to the intended destination.
bx.memcpy(
dst.val.llval,

View file

@ -1,6 +1,6 @@
use std::cmp;
use rustc_abi::{BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange};
use rustc_abi::{Align, BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange};
use rustc_ast as ast;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_data_structures::packed::Pu128;
@ -13,7 +13,7 @@ use rustc_middle::{bug, span_bug};
use rustc_session::config::OptLevel;
use rustc_span::Span;
use rustc_span::source_map::Spanned;
use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
use rustc_target::callconv::{ArgAbi, CastTarget, FnAbi, PassMode};
use tracing::{debug, info};
use super::operand::OperandRef;
@ -558,8 +558,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"),
};
let ty = bx.cast_backend_type(cast_ty);
bx.load(ty, llslot, self.fn_abi.ret.layout.align.abi)
load_cast(bx, cast_ty, llslot, self.fn_abi.ret.layout.align.abi)
}
};
bx.ret(llval);
@ -1618,8 +1617,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
MemFlags::empty(),
);
// ...and then load it with the ABI type.
let cast_ty = bx.cast_backend_type(cast);
llval = bx.load(cast_ty, llscratch, scratch_align);
llval = load_cast(bx, cast, llscratch, scratch_align);
bx.lifetime_end(llscratch, scratch_size);
} else {
// We can't use `PlaceRef::load` here because the argument
@ -1969,3 +1967,47 @@ enum ReturnDest<'tcx, V> {
/// Store a direct return value to an operand local place.
DirectOperand(mir::Local),
}
fn load_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
cast: &CastTarget,
ptr: Bx::Value,
align: Align,
) -> Bx::Value {
let cast_ty = bx.cast_backend_type(cast);
if let Some(offset_from_start) = cast.rest_offset {
assert!(cast.prefix[1..].iter().all(|p| p.is_none()));
assert_eq!(cast.rest.unit.size, cast.rest.total);
let first_ty = bx.reg_backend_type(&cast.prefix[0].unwrap());
let second_ty = bx.reg_backend_type(&cast.rest.unit);
let first = bx.load(first_ty, ptr, align);
let second_ptr = bx.inbounds_ptradd(ptr, bx.const_usize(offset_from_start.bytes()));
let second = bx.load(second_ty, second_ptr, align.restrict_for_offset(offset_from_start));
let res = bx.cx().const_poison(cast_ty);
let res = bx.insert_value(res, first, 0);
bx.insert_value(res, second, 1)
} else {
bx.load(cast_ty, ptr, align)
}
}
pub fn store_cast<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
cast: &CastTarget,
value: Bx::Value,
ptr: Bx::Value,
align: Align,
) {
if let Some(offset_from_start) = cast.rest_offset {
assert!(cast.prefix[1..].iter().all(|p| p.is_none()));
assert_eq!(cast.rest.unit.size, cast.rest.total);
assert!(cast.prefix[0].is_some());
let first = bx.extract_value(value, 0);
let second = bx.extract_value(value, 1);
bx.store(first, ptr, align);
let second_ptr = bx.inbounds_ptradd(ptr, bx.const_usize(offset_from_start.bytes()));
bx.store(second, second_ptr, align.restrict_for_offset(offset_from_start));
} else {
bx.store(value, ptr, align);
};
}

View file

@ -26,6 +26,7 @@ pub mod place;
mod rvalue;
mod statement;
pub use self::block::store_cast;
use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo};
use self::operand::{OperandRef, OperandValue};
use self::place::PlaceRef;
@ -259,7 +260,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
PassMode::Cast { ref cast, .. } => {
debug!("alloc: {:?} (return place) -> place", local);
let size = cast.size(&start_bx);
let size = cast.size(&start_bx).max(layout.size);
return LocalRef::Place(PlaceRef::alloca_size(&mut start_bx, size, layout));
}
_ => {}

View file

@ -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>)

View file

@ -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)
}
}

View file

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

View file

@ -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;
}
_ => (),

View file

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