Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 105 additions & 32 deletions compiler/rustc_target/src/callconv/loongarch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,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)]
Expand All @@ -37,6 +37,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,
Expand All @@ -49,16 +50,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),
}
Expand All @@ -69,12 +70,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),
}
Expand All @@ -96,13 +101,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,
Expand All @@ -111,6 +117,7 @@ where
flen,
field1_kind,
field2_kind,
offset_from_start + elem_layout.size * i,
)?;
}
}
Expand All @@ -121,7 +128,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),
)?;
}
}
},
Expand All @@ -140,14 +155,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,
}
}
Expand All @@ -165,11 +218,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;
Expand Down Expand Up @@ -233,15 +294,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;
}
_ => (),
Expand Down
134 changes: 134 additions & 0 deletions tests/assembly-llvm/loongarch-float-struct-abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//@ add-core-stubs
//@ assembly-output: emit-asm
//@ compile-flags: -Copt-level=3 --target loongarch64-unknown-linux-gnu
//@ needs-llvm-components: loongarch

#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#![crate_type = "lib"]

extern crate minicore;
use minicore::*;

#[repr(C, align(64))]
struct Aligned(f64);

#[repr(C)]
struct Padded(u8, Aligned);

#[repr(C, packed)]
struct Packed(u8, f32);

impl Copy for Aligned {}
impl Copy for Padded {}
impl Copy for Packed {}

extern "C" {
fn take_padded(x: Padded);
fn get_padded() -> Padded;
fn take_packed(x: Packed);
fn get_packed() -> Packed;
}

// CHECK-LABEL: pass_padded
#[unsafe(no_mangle)]
extern "C" fn pass_padded(out: &mut Padded, x: Padded) {
// CHECK: st.b $a1, $a0, 0
// CHECK-NEXT: fst.d $fa0, $a0, 64
// CHECK-NEXT: ret
*out = x;
}

// CHECK-LABEL: ret_padded
#[unsafe(no_mangle)]
extern "C" fn ret_padded(x: &Padded) -> Padded {
// CHECK: fld.d $fa0, $a0, 64
// CHECK-NEXT: ld.b $a0, $a0, 0
// CHECK-NEXT: ret
*x
}

#[unsafe(no_mangle)]
extern "C" fn call_padded(x: &Padded) {
// CHECK: fld.d $fa0, $a0, 64
// CHECK-NEXT: ld.b $a0, $a0, 0
// CHECK-NEXT: pcaddu18i $t8, %call36(take_padded)
// CHECK-NEXT: jr $t8
unsafe {
take_padded(*x);
}
}

#[unsafe(no_mangle)]
extern "C" fn receive_padded(out: &mut Padded) {
// CHECK: addi.d $sp, $sp, -16
// CHECK-NEXT: .cfi_def_cfa_offset 16
// CHECK-NEXT: st.d $ra, $sp, [[#%d,RA_SPILL:]]
// CHECK-NEXT: st.d [[TEMP:.*]], $sp, [[#%d,TEMP_SPILL:]]
// CHECK-NEXT: .cfi_offset 1, [[#%d,RA_SPILL - 16]]
// CHECK-NEXT: .cfi_offset [[#%d,TEMP_NUM:]], [[#%d,TEMP_SPILL - 16]]
// CHECK-NEXT: move [[TEMP]], $a0
// CHECK-NEXT: pcaddu18i $ra, %call36(get_padded)
// CHECK-NEXT: jirl $ra, $ra, 0
// CHECK-NEXT: st.b $a0, [[TEMP]], 0
// CHECK-NEXT: fst.d $fa0, [[TEMP]], 64
// CHECK-NEXT: ld.d [[TEMP]], $sp, [[#%d,TEMP_SPILL]]
// CHECK-NEXT: ld.d $ra, $sp, [[#%d,RA_SPILL]]
// CHECK: addi.d $sp, $sp, 16
// CHECK: ret
unsafe {
*out = get_padded();
}
}

// CHECK-LABEL: pass_packed
#[unsafe(no_mangle)]
extern "C" fn pass_packed(out: &mut Packed, x: Packed) {
// CHECK: st.b $a1, $a0, 0
// CHECK-NEXT: fst.s $fa0, $a0, 1
// CHECK-NEXT: ret
*out = x;
}

// CHECK-LABEL: ret_packed
#[unsafe(no_mangle)]
extern "C" fn ret_packed(x: &Packed) -> Packed {
// CHECK: fld.s $fa0, $a0, 1
// CHECK-NEXT: ld.b $a0, $a0, 0
// CHECK-NEXT: ret
*x
}

#[unsafe(no_mangle)]
extern "C" fn call_packed(x: &Packed) {
// CHECK: fld.s $fa0, $a0, 1
// CHECK-NEXT: ld.b $a0, $a0, 0
// CHECK-NEXT: pcaddu18i $t8, %call36(take_packed)
// CHECK-NEXT: jr $t8
unsafe {
take_packed(*x);
}
}

#[unsafe(no_mangle)]
extern "C" fn receive_packed(out: &mut Packed) {
// CHECK: addi.d $sp, $sp, -16
// CHECK-NEXT: .cfi_def_cfa_offset 16
// CHECK-NEXT: st.d $ra, $sp, [[#%d,RA_SPILL:]]
// CHECK-NEXT: st.d [[TEMP:.*]], $sp, [[#%d,TEMP_SPILL:]]
// CHECK-NEXT: .cfi_offset 1, [[#%d,RA_SPILL - 16]]
// CHECK-NEXT: .cfi_offset [[#%d,TEMP_NUM:]], [[#%d,TEMP_SPILL - 16]]
// CHECK-NEXT: move [[TEMP]], $a0
// CHECK-NEXT: pcaddu18i $ra, %call36(get_packed)
// CHECK-NEXT: jirl $ra, $ra, 0
// CHECK-NEXT: st.b $a0, [[TEMP]], 0
// CHECK-NEXT: fst.s $fa0, [[TEMP]], 1
// CHECK-NEXT: ld.d [[TEMP]], $sp, [[#%d,TEMP_SPILL]]
// CHECK-NEXT: ld.d $ra, $sp, [[#%d,RA_SPILL]]
// CHECK: addi.d $sp, $sp, 16
// CHECK: ret
unsafe {
*out = get_packed();
}
}
Loading
Loading