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
65 changes: 64 additions & 1 deletion compiler/rustc_target/src/callconv/mips64.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use arrayvec::ArrayVec;
use rustc_abi::{
BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface,
BackendRepr, FieldsShape, Float, HasDataLayout, Integer, Numeric, Primitive, Reg, RegKind,
Size, TyAbiInterface,
};

use crate::callconv::{ArgAbi, ArgAttribute, ArgExtension, CastTarget, FnAbi, PassMode, Uniform};
Expand Down Expand Up @@ -56,6 +57,22 @@ where
let size = ret.layout.size;
let bits = size.bits();
if bits <= 128 {
// NOTE: Complex<f128> is returned indirectly.
if let Some(component) = ret.layout.complex_number(cx) {
match component {
Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => {
// Return a Complex<{integer}> packed into a single register when that fits.
ret.cast_to(Reg { kind: RegKind::Integer, size });
}
_ => {
// Otherwise pass in 2 registers.
let reg = Reg { kind: component.reg_kind(), size: component.size() };
ret.cast_to(CastTarget::pair(reg, reg));
}
}
return;
}

// Unlike other architectures which return aggregates in registers, MIPS n64 limits the
// use of float registers to structures (not unions) containing exactly one or two
// float fields.
Expand Down Expand Up @@ -103,6 +120,52 @@ where
extend_integer_width_mips(arg, 64);
} else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
arg.make_indirect();
} else if let Some(component) = arg.layout.complex_number(cx)
&& !matches!(component, Numeric::Float(Float::F16))
{
let slot = dl.pointer_size();
let curr_offset = offset.align_to(align);

const NUM_ARG_SLOTS: u64 = 8;

match component {
Numeric::Float(Float::F16B) => unreachable!("Complex<f16b> is not C-compatible"),
Numeric::Float(Float::F16) => unreachable!("not supported on mips64"),
Numeric::Float(Float::F32 | Float::F64) => {
// Only pass a Complex<f32>/Complex<f64> in FPRs when two argument slots are free.
if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS {

@beetrees beetrees Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C-variadic arguments are always passed in GPRs, so Complex<f32> needs to be passed in one slot in that case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're wanting to leave C-variadic compatibility till later, leave a FIXME here instead?

// Both components claim a slot, even a Complex<f32> which could fit in one
// slot.
//
// FIXME(complex_numbers): c-variadic arguments are passed in GPRs, so need a
// special carve-out here and Complex<f32> is bitpacked into one 64-bit GPR.
*offset = curr_offset + slot * 2;
let unit = Reg { kind: RegKind::Float, size: component.size() };
let cast_target = CastTarget::from(Uniform::new(unit, size));
arg.cast_to(cast_target);
return;
}

// Otherwise pack it into GPRs (or the stack) like an integer of the same size.
arg.cast_to_and_pad_i32(Uniform::new(Reg::i64(), size), pad_i32);
}
Numeric::Float(Float::F128) => {
// Complex<f128> is passed in 4 FPRs, but aligned to 16 so may need padding.
let reg = Reg { kind: RegKind::Float, size: arg.layout.field(cx, 0).size };
arg.cast_to_and_pad_i32(CastTarget::pair(reg, reg), pad_i32);
}
Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => {
// Cast Complex<i8> into i16, Complex<i16> to i32, etc.
let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size });
// The inreg attribute makes the bits land in the right (upper) bits on BE targets.
arg.cast_to(cast_target.with_attrs(ArgAttribute::InReg.into()));
}
Numeric::Int(Integer::I64 | Integer::I128, _) => {
// Complex<i64> and Complex<i128> are passed as 2 separate arguments.
let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size });
arg.cast_to(cast_target);
}
}
} else {
match arg.layout.fields {
FieldsShape::Primitive => unreachable!(),
Expand Down
35 changes: 25 additions & 10 deletions tests/codegen-llvm/complex-abi.rs

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a test for passing Complex as a C-variadic argument?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our va_arg can't handle that for most targets at the moment (and VaArgSafe is not implemented for Complex yet). I intend to do another sweep for c-variadic once we have "normal" call support across targets.

Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,21 @@
//@ [AIX] compile-flags: --target powerpc64-ibm-aix
//@ [AIX] needs-llvm-components: powerpc

// NOTE: in clang <= 23 the Complex is passed incorrectly.
// See https://github.com/llvm/llvm-project/issues/212109.
//@ revisions: MIPS64 MIPS64EL
//@ [MIPS64] compile-flags: --target mips64-unknown-linux-gnuabi64
//@ [MIPS64] needs-llvm-components: mips
//@ [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64
//@ [MIPS64EL] needs-llvm-components: mips

// FIXME: the below revisions are deliberately disabled for now.

// revisions: POWERPC
// [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu
// [POWERPC] needs-llvm-components: powerpc

// revisions: MIPS64EL MIPS
// [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64
// [MIPS64EL] needs-llvm-components: mips
// revisions: MIPS
// [MIPS] compile-flags: --target mips-unknown-linux-gnu
// [MIPS] needs-llvm-components: mips

Expand Down Expand Up @@ -143,7 +149,8 @@ pub extern "C" fn cplx_f32(x: Complex<f32>) -> Complex<f32> {
// I686: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}})
// LOONGARCH32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}})
// LOONGARCH64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}})
// MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}})
// MIPS64: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}})
// MIPS64EL: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}})
// MIPS: define{{.*}} { float, float } @cplx_f32([2 x i32] {{.*}})
// NVPTX: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}})
// POWERPC64: define{{.*}} { float, float } @cplx_f32({ float, float } %0)
Expand Down Expand Up @@ -177,7 +184,8 @@ pub extern "C" fn cplx_f64(x: Complex<f64>) -> Complex<f64> {
// I686: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}})
// LOONGARCH32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}})
// LOONGARCH64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}})
// MIPS64EL: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}})
// MIPS64: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}})
// MIPS64EL: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}})
// MIPS: define{{.*}} { double, double } @cplx_f64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}})
// NVPTX: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}})
// POWERPC64: define{{.*}} { double, double } @cplx_f64({ double, double } %0)
Expand All @@ -202,6 +210,8 @@ pub extern "C" fn cplx_f64(x: Complex<f64>) -> Complex<f64> {
pub extern "C" fn cplx_f128(x: Complex<f128>) -> Complex<f128> {
// AARCH64: define{{.*}} [2 x fp128] {{.*}})
// I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}})
// MIPS64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, i32 %0, { fp128, fp128 } %1)
// MIPS64EL: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, i32 %0, { fp128, fp128 } %1)
// SPARC: define{{.*}} inreg { fp128, fp128 } @cplx_f128(ptr {{.*}} dereferenceable(32) {{.*}})
// WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}})
// WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}})
Expand All @@ -226,7 +236,8 @@ pub extern "C" fn cplx_i8(x: Complex<i8>) -> Complex<i8> {
// I686: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}})
// LOONGARCH32: define{{.*}} i32 @cplx_i8(i32{{.*}})
// LOONGARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}})
// MIPS64EL: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}})
// MIPS64: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}})
// MIPS64EL: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}})
// MIPS: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}})
// NVPTX: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}})
// POWERPC64: define{{.*}} { i8, i8 } @cplx_i8({ i8, i8 } %0)
Expand Down Expand Up @@ -260,7 +271,8 @@ pub extern "C" fn cplx_i16(x: Complex<i16>) -> Complex<i16> {
// I686: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}})
// LOONGARCH32: define{{.*}} i32 @cplx_i16(i32 {{.*}})
// LOONGARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}})
// MIPS64EL: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}})
// MIPS64: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}})
// MIPS64EL: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}})
// MIPS: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}})
// NVPTX: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}})
// POWERPC64: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}})
Expand Down Expand Up @@ -294,7 +306,8 @@ pub extern "C" fn cplx_i32(x: Complex<i32>) -> Complex<i32> {
// I686: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}})
// LOONGARCH32: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}})
// LOONGARCH64: define{{.*}} i64 @cplx_i32(i64 {{.*}})
// MIPS64EL: define{{.*}} { i32, i32 } @cplx_i32(i64 {{.*}})
// MIPS64: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}})
// MIPS64EL: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}})
// MIPS: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}})
// NVPTX: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}})
// POWERPC64: define{{.*}} { i32, i32 } @cplx_i32({ i32, i32 } %0)
Expand Down Expand Up @@ -328,7 +341,8 @@ pub extern "C" fn cplx_i64(x: Complex<i64>) -> Complex<i64> {
// I686: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}})
// LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}})
// LOONGARCH64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}})
// MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}})
// MIPS64: define{{.*}} { i64, i64 } @cplx_i64(i128 {{.*}})
// MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i128 {{.*}})
// MIPS: define{{.*}} { i64, i64 } @cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}})
// NVPTX: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}})
// POWERPC64: define{{.*}} { i64, i64 } @cplx_i64({ i64, i64 } %0)
Expand Down Expand Up @@ -367,7 +381,8 @@ pub extern "C" fn wrapper_cplx_i64(
// I686: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}})
// LOONGARCH32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}})
// LOONGARCH64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}})
// MIPS64EL: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}})
// MIPS64: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i128 {{.*}})
// MIPS64EL: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i128 {{.*}})
// MIPS: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}})
// NVPTX: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}})
// POWERPC64: define{{.*}} { i64, i64 } @wrapper_cplx_i64({ i64, i64 } %0)
Expand Down
Loading