diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index b835909d2c2ca..013a42b0df050 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -7,7 +7,7 @@ use rustc_macros::StableHash; use crate::layout::{FieldIdx, VariantIdx}; use crate::{ - AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche, + AbiAlign, Align, BackendRepr, FieldsShape, Float, HasDataLayout, LayoutData, Niche, Numeric, PointeeInfo, Primitive, Size, Variants, }; @@ -299,7 +299,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { found } - pub fn complex_float(&self, cx: &C) -> Option + pub fn complex_number(&self, cx: &C) -> Option where Ty: TyAbiInterface<'a, C> + Copy, { @@ -314,7 +314,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { debug_assert_eq!(a, b); match a.primitive() { - Primitive::Float(f) => Some(f), + Primitive::Int(i, sign) => Some(Numeric::Int(i, sign)), + Primitive::Float(f) => Some(Numeric::Float(f)), + Primitive::Pointer(_) => None, + } + } + + pub fn complex_float(&self, cx: &C) -> Option + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match self.complex_number(cx) { + Some(Numeric::Float(f)) => Some(f), _ => None, } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 589c5a5cb1229..75b8a606d66f7 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1445,6 +1445,31 @@ impl Float { } } +/// Numeric primitives. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "nightly", derive(StableHash))] +pub enum Numeric { + /// The `bool` is the signedness of the `Integer` type. + Int(Integer, bool), + Float(Float), +} + +impl Numeric { + pub fn size(self) -> Size { + match self { + Numeric::Int(integer, _) => integer.size(), + Numeric::Float(float) => float.size(), + } + } + + pub fn reg_kind(self) -> RegKind { + match self { + Numeric::Int(_, _) => RegKind::Integer, + Numeric::Float(_) => RegKind::Float, + } + } +} + /// Fundamental unit of memory access and layout. #[derive(Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(StableHash))] diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index c4d4ddcf6b753..f5500169be254 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -123,7 +123,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.backend_repr), }, PassMode::Cast { ref cast, pad_i32 } => { - assert!(!pad_i32, "padding support not yet implemented"); + assert_eq!(pad_i32, 0, "padding support not yet implemented"); cast_target_to_abi_params(cast).into_iter().map(|(_, param)| param).collect() } PassMode::Indirect { attrs, meta_attrs: None, on_stack } => { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 1b7bb8c907735..0ce8eba510336 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -169,10 +169,10 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { continue; } PassMode::Cast { ref cast, pad_i32 } => { - // add padding - if pad_i32 { - argument_tys.push(Reg::i32().gcc_type(cx)); - } + // Add padding. + argument_tys + .extend(std::iter::repeat_n(Reg::i32().gcc_type(cx), usize::from(pad_i32))); + let ty = cast.gcc_type(cx); apply_attrs(ty, &cast.attrs, argument_tys.len()) } diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index c6c32236ab49f..53192c0a087e4 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -346,8 +346,8 @@ impl<'gcc, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn_abi.ptr_to_gcc_type(self) } - fn reg_backend_type(&self, _ty: &Reg) -> Type<'gcc> { - unimplemented!(); + fn reg_backend_type(&self, ty: &Reg) -> Type<'gcc> { + ty.gcc_type(self) } fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 65bb32ee666f2..f4fcfec5f1ced 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -406,10 +406,12 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => cx.type_ptr(), PassMode::Cast { cast, pad_i32 } => { - // add padding - if *pad_i32 { - llargument_tys.push(Reg::i32().llvm_type(cx)); - } + // Add padding. + llargument_tys.extend(std::iter::repeat_n( + Reg::i32().llvm_type(cx), + usize::from(*pad_i32), + )); + // Compute the LLVM type we use for this function from the cast type. // We assume here that ABI-compatible Rust types have the same cast type. cast.llvm_type(cx) @@ -581,7 +583,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } } PassMode::Cast { cast, pad_i32 } => { - if *pad_i32 { + for _ in 0..*pad_i32 { apply(&ArgAttributes::new()); } apply(&cast.attrs); @@ -667,7 +669,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { apply(bx.cx, b); } PassMode::Cast { cast, pad_i32 } => { - if *pad_i32 { + for _ in 0..*pad_i32 { apply(bx.cx, &ArgAttributes::new()); } apply(bx.cx, &cast.attrs); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 7f907bc630b2f..2d07647f4361b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1936,9 +1936,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) { match arg.mode { PassMode::Ignore => return, - PassMode::Cast { pad_i32: true, .. } => { + PassMode::Cast { pad_i32, .. } => { // Fill padding with undef value, where applicable. - llargs.push(bx.const_undef(bx.reg_backend_type(&Reg::i32()))); + let undef = bx.const_undef(bx.reg_backend_type(&Reg::i32())); + llargs.extend(std::iter::repeat_n(undef, usize::from(pad_i32))); } PassMode::Pair(..) => match op.val { Pair(a, b) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index f8f4f09f75825..c7d615308e6e4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -501,8 +501,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( for i in 0..tupled_arg_tys.len() { let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast { pad_i32: true, .. } = arg.mode { - llarg_idx += 1; + if let PassMode::Cast { pad_i32, .. } = arg.mode { + llarg_idx += usize::from(pad_i32); } let pr_field = place.project_field(bx, i); bx.store_fn_arg(arg, &mut llarg_idx, pr_field); @@ -529,8 +529,8 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let arg = &fx.fn_abi.args[idx]; idx += 1; - if let PassMode::Cast { pad_i32: true, .. } = arg.mode { - llarg_idx += 1; + if let PassMode::Cast { pad_i32, .. } = arg.mode { + llarg_idx += usize::from(pad_i32); } if !memory_locals.contains(local) { diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 33cc321ea6d32..d51565f45a3ba 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -474,7 +474,7 @@ fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_t }, PassMode::Cast { pad_i32, ref cast } => { // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);` - assert!(!pad_i32, "not currently used by wasm calling convention"); + assert_eq!(pad_i32, 0, "not currently used by wasm calling convention"); assert!(cast.prefix.is_empty(), "no prefix"); assert_eq!(cast.rest.total, arg_abi.layout.size, "single item"); diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 02674e4107c77..054867c0cb4a2 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -55,7 +55,7 @@ pub enum PassMode { /// The argument has a layout abi of `ScalarPair`. Pair(Opaque, Opaque), /// Pass the argument after casting it. - Cast { pad_i32: bool, cast: Opaque }, + Cast { pad_i32: u8, cast: Opaque }, /// Pass the argument indirectly via a hidden pointer. Indirect { attrs: Opaque, meta_attrs: Opaque, on_stack: bool }, } diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index d2572cc035c1c..8b5be8dd3daec 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -1,16 +1,36 @@ -use rustc_abi::{HasDataLayout, Size, TyAbiInterface}; +use rustc_abi::{Float, HasDataLayout, Integer, Numeric, Reg, RegKind, Size, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, CastTarget, FnAbi, Uniform}; -fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, offset: &mut Size) where + Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if !ret.layout.is_aggregate() { - ret.extend_integer_width_to(32); - } else { + let dl = cx.data_layout(); + let size = ret.layout.size; + + if let Some(component) = ret.layout.complex_number(cx) { + match component { + Numeric::Float(Float::F128) => { + // Same as an aggregate. + ret.make_indirect(); + *offset += dl.pointer_size(); + } + Numeric::Int(Integer::I8 | Integer::I16, _) => { + // Pack Complex<{integer}> into a single register if that fits. + ret.cast_to(Reg { kind: RegKind::Integer, size }); + } + _ => { + let reg = Reg { kind: component.reg_kind(), size: ret.layout.field(cx, 0).size }; + ret.cast_to(CastTarget::pair(reg, reg)); + } + } + } else if ret.layout.is_aggregate() { ret.make_indirect(); - *offset += cx.data_layout().pointer_size(); + *offset += dl.pointer_size(); + } else { + ret.extend_integer_width_to(32); } } @@ -35,7 +55,7 @@ where let size = arg.layout.size; if arg.layout.is_aggregate() { - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); } else { arg.extend_integer_width_to(32); diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index a9d5ec958889f..ac059547d45b5 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -1,10 +1,13 @@ use arrayvec::ArrayVec; use rustc_abi::{ - BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, + BackendRepr, FieldsShape, Float, HasDataLayout, Numeric, Primitive, Reg, RegKind, Size, + TyAbiInterface, }; use crate::callconv::{ArgAbi, ArgAttribute, ArgExtension, CastTarget, FnAbi, PassMode, Uniform}; +const NUM_ARG_SLOTS: u64 = 8; + fn extend_integer_width_mips(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr @@ -55,6 +58,19 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { + if let Some(component) = ret.layout.complex_number(cx) { + if matches!(component, Numeric::Int(..)) && size <= cx.data_layout().pointer_size() { + // Return a Complex<{integer}> packed into a single register when that fits. + // We match GCC, not Clang, see https://github.com/llvm/llvm-project/issues/212109. + ret.cast_to(Reg { kind: RegKind::Integer, size }); + } else { + // 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. @@ -96,12 +112,40 @@ where // Detect need for padding let align = Ord::clamp(arg.layout.align.abi, dl.i64_align, dl.i128_align); - let pad_i32 = !offset.is_aligned(align); + let pad_i32 = u8::from(!offset.is_aligned(align)); if !arg.layout.is_aggregate() { extend_integer_width_mips(arg, 64); } else if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); + } else if arg.layout.complex_float(cx).is_some() && size > dl.pointer_size() * 2 { + // Complex is passed in 4 GPRs, 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); + } else if arg.layout.is_complex_number(cx) && size <= dl.pointer_size() * 2 { + let slot = dl.pointer_size(); + let curr_offset = offset.align_to(align); + + if arg.layout.complex_float(cx).is_some() { + // Only pass a Complex/Complex in FPRs when two argument slots are free, + // otherwise pack it into GPRs (or the stack) like an integer of the same size. + if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS { + // The default `PassMode::Pair` already passes one component per register. Both + // components claim a slot, even for a Complex which could fit into one slot. + *offset = curr_offset + slot * 2; + return; + } + + arg.cast_to_and_pad_i32(Uniform::new(Reg::i64(), size), pad_i32); + } else if size > slot { + // Complex is passed as 2 separate arguments, which is what the default + // `PassMode::Pair` already does. + } else { + // Cast Complex into i16, Complex to i32, etc. The inreg attribute is required + // to make the bits land in the right (upper) bits on big endian targets. + let cast_target = CastTarget::from(Reg { kind: RegKind::Integer, size }); + arg.cast_to(cast_target.with_attrs(ArgAttribute::InReg.into())); + } } else { match arg.layout.fields { FieldsShape::Primitive => unreachable!(), diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index a06a6a0a69e12..22595b9ad24d5 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -55,8 +55,9 @@ pub enum PassMode { Pair(ArgAttributes, ArgAttributes), /// Pass the argument after casting it. See the `CastTarget` docs for details. /// - /// `pad_i32` indicates if a `Reg::i32()` dummy argument is emitted before the real argument. - Cast { pad_i32: bool, cast: Box }, + /// `pad_i32` indicates how many `Reg::i32()` dummy arguments are emitted before the real + /// argument. + Cast { pad_i32: u8, cast: Box }, /// Pass the argument indirectly via a hidden pointer. /// /// The `meta_attrs` value, if any, is for the metadata (vtable or length) of an unsized @@ -507,12 +508,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn cast_to>(&mut self, target: T) { - self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; + self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: 0 }; } pub fn cast_to_with_attrs>(&mut self, target: T, attrs: ArgAttributes) { - self.mode = - PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: false }; + self.mode = PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: 0 }; } /// Cast to `target`, forwarding `NoUndef` only when the layout provably has no uninit @@ -535,7 +535,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { self.cast_to_with_attrs(target, attr.into()); } - pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: bool) { + pub fn cast_to_and_pad_i32>(&mut self, target: T, pad_i32: u8) { self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; } diff --git a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index 2b6a104e1221d..5c32e68aefe36 100644 --- a/compiler/rustc_target/src/callconv/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs @@ -1,17 +1,45 @@ -use rustc_abi::TyAbiInterface; +use rustc_abi::{BackendRepr, Primitive, Reg, RegKind, TyAbiInterface}; -use crate::callconv::{ArgAbi, FnAbi}; +use crate::callconv::{ArgAbi, CastTarget, FnAbi, Uniform}; use crate::spec::{Env, HasTargetSpec, Os}; -fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { - if ret.layout.is_aggregate() { +const NUM_ARG_GPRS: u32 = 8; // r3..=r10 + +/// How to cast `Complex` so that we match the GCC ABI. +fn complex_cast_target(arg: &ArgAbi<'_, Ty>) -> CastTarget { + let size = arg.layout.size; + + if size.bytes() <= 4 { + // Coerce to an integer for `Complex` and `Complex`. + CastTarget::from(Reg { kind: RegKind::Integer, size }) + } else if size.bytes() == 8 { + // Coerce to a single `i64` for `Complex` and `Complex`, which has the correct + // register alignment of 8 bytes. + // + // NOTE: clang uses a vector (e.g. <2 x f32>) here, but if we try that we run into + // ABI issues because vectors require the altivec target feature. + CastTarget::from(Reg::i64()) + } else { + // Coerce to an array `[N x i32]` for everything wider. An array of i32 gives the correct + // 4-byte register alignment. + CastTarget::from(Uniform::new(Reg::i32(), size)) + } +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAbiInterface<'a, C> + Copy, +{ + if ret.layout.is_complex_number(cx) { + ret.cast_to(complex_cast_target(ret)); + } else if ret.layout.is_aggregate() { ret.make_indirect(); } else { ret.extend_integer_width_to(32); } } -fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>, arg_gprs_left: &mut u32) where Ty: TyAbiInterface<'a, C> + Copy, { @@ -25,11 +53,70 @@ where } return; } - if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() { - arg.make_indirect(); + + let default = |arg: &mut ArgAbi<'a, Ty>| { + if arg.layout.pass_indirectly_in_non_rustic_abis(cx) || arg.layout.is_aggregate() { + arg.make_indirect(); + } else { + arg.extend_integer_width_to(32); + } + }; + + let is_complex = arg.layout.is_complex_number(cx); + let is_float = match arg.layout.backend_repr { + BackendRepr::Scalar(scalar) => matches!(scalar.primitive(), Primitive::Float(_)), + _ => false, + }; + + // Arguments that are not relevant for the GPR budget: floats go in the FPRs, and once the GPRs + // are exhausted everything lands on the stack anyway. Complex always needs custom handling. + if (*arg_gprs_left == 0 || is_float) && !is_complex { + return default(arg); + } + + let size = arg.layout.size; + let regs_needed = size.bytes().div_ceil(4) as u32; // 32-bit registers + + if arg.layout.is_aggregate() && !is_complex { + // Non-complex aggregates are passed indirectly, and consume one GPR. + *arg_gprs_left -= 1; } else { - arg.extend_integer_width_to(32); + let mut padding = 0; + + // The powerpc ABI in GCC hardcodes a special rule for values of size 8. It remarks + // + // > V.4 wants long longs and doubles to be double word aligned. Just + // > testing the mode size is a boneheaded way to do this as it means + // > that other types such as complex int are also double word aligned. + // > However, we're stuck with this because changing the ABI might break + // > existing library interfaces. + // + // An eight-byte value must start in an even-numbered GPR. The `i64` it is coerced to + // already makes LLVM skip an odd register, so only account for it in the budget. + if size.bytes() == 8 && !arg_gprs_left.is_multiple_of(2) { + *arg_gprs_left -= 1; + } + + if regs_needed <= *arg_gprs_left { + // Everything fits, great! + *arg_gprs_left -= regs_needed; + } else if is_complex { + // Never split a Complex across the GPRs and the stack. + // + // The full complex value is passed via the stack, and the remaining GPRs are consumed, + // so all subsequent arguments will also be passed via the stack. Use the padding value + // to fill up the remaining GPRs. + padding += *arg_gprs_left; + *arg_gprs_left = 0; + } + + if is_complex { + arg.cast_to_and_pad_i32(complex_cast_target(arg), padding as u8); + return; + } } + + default(arg) } pub(crate) fn compute_abi_info<'a, Ty, C: HasTargetSpec>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -37,10 +124,11 @@ where Ty: TyAbiInterface<'a, C> + Copy, { if !fn_abi.ret.is_ignore() { - classify_ret(&mut fn_abi.ret); + classify_ret(cx, &mut fn_abi.ret); } + let mut arg_gprs_left = NUM_ARG_GPRS; for arg in fn_abi.args.iter_mut() { - classify_arg(cx, arg); + classify_arg(cx, arg, &mut arg_gprs_left); } } diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 3eb40abe90f33..7d017d5a19174 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -4,7 +4,7 @@ use rustc_abi::{HasDataLayout, TyAbiInterface}; -use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; +use crate::callconv::{Align, ArgAbi, CastTarget, FnAbi, Reg, RegKind, Uniform}; use crate::spec::{HasTargetSpec, LlvmAbi, Os}; #[derive(Debug, Clone, Copy, PartialEq)] @@ -60,6 +60,13 @@ where arg.extend_integer_width_to(64); return; } + if let Some(component) = arg.layout.complex_number(cx) { + if is_ret { + let reg = Reg { kind: component.reg_kind(), size: component.size() }; + arg.cast_to(CastTarget::pair(reg, reg)); + } + return; + } // The AIX ABI expect byval for aggregates // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp. diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index 581c1e2e862c5..f0d9675de34f4 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -42,6 +42,11 @@ where return; } + if arg.layout.is_complex_number(cx) { + arg.make_indirect(); + return; + } + let size = arg.layout.size; if size.bits() <= 128 { if let BackendRepr::SimdVector { .. } = arg.layout.backend_repr { diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index d424214aa497e..6866d7e717d9e 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -1,46 +1,77 @@ -use rustc_abi::{HasDataLayout, Size, TyAbiInterface}; +use rustc_abi::{ + BackendRepr, Float, HasDataLayout, Integer, Numeric, Primitive, RegKind, TyAbiInterface, +}; -use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Reg}; -fn classify_ret(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) +/// C `long double` is IEEE binary128 on 32-bit SPARC, i.e. Rust's `f128`. It is passed and +/// returned indirectly. +fn is_long_double(repr: BackendRepr) -> bool { + matches!(repr, BackendRepr::Scalar(scalar) if scalar.primitive() == Primitive::Float(Float::F128)) +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) where + Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - if !ret.layout.is_aggregate() { - ret.extend_integer_width_to(32); - } else { + if let Some(component) = ret.layout.complex_number(cx) { + let reg = Reg { kind: component.reg_kind(), size: component.size() }; + let mut cast = CastTarget::pair(reg, reg); + + match component { + Numeric::Float(Float::F128) => { + // long double _Complex is special in that it should be marked as inreg. + // See Clang `SparcV8ABIInfo::classifyReturnType`. + cast.attrs.set(ArgAttribute::InReg); + } + Numeric::Float(Float::F16) + | Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + let size = ret.layout.size; + cast = CastTarget::from(Reg { kind: RegKind::Integer, size }); + } + _ => { /* default behavior */ } + } + + ret.cast_to(cast); + } else if is_long_double(ret.layout.backend_repr) || ret.layout.is_aggregate() { + // See Clang `SparcV8ABIInfo::classifyReturnType`, which returns `long double` through an + // `sret` pointer. The `sret` attribute is also what makes LLVM emit the `unimp` marker + // after the call and return to `%o7+12` in the callee. ret.make_indirect(); - *offset += cx.data_layout().pointer_size(); + } else { + ret.extend_integer_width_to(32); } } -fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, offset: &mut Size) +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { if !arg.layout.is_sized() { - // FIXME: Update offset? // Not touching this... return; } - let dl = cx.data_layout(); if arg.layout.pass_indirectly_in_non_rustic_abis(cx) { arg.make_indirect(); - *offset += dl.pointer_size(); return; } - let size = arg.layout.size; - let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); - if arg.layout.is_aggregate() { - let pad_i32 = !offset.is_aligned(align); - arg.cast_to_and_pad_i32(Uniform::new(Reg::i32(), size), pad_i32); + if let Some(component) = arg.layout.complex_number(cx) { + if let Numeric::Int(Integer::I8 | Integer::I16 | Integer::I32, _) = component { + arg.cast_to(Reg { kind: RegKind::Integer, size: 2 * component.size() }); + } else { + arg.pass_by_stack_offset(None); + } + } else if is_long_double(arg.layout.backend_repr) || arg.layout.is_aggregate() { + // `long double` and aggregates are passed by reference: the caller makes a copy and passes + // its address. See Clang `SparcV8ABIInfo::classifyArgumentType` and the + // `DefaultABIInfo::classifyArgumentType` it falls back to. + arg.pass_by_stack_offset(None); } else { arg.extend_integer_width_to(32); } - - *offset = offset.align_to(align) + size.align_to(align); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) @@ -48,15 +79,17 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout, { - let mut offset = Size::ZERO; if !fn_abi.ret.is_ignore() { - classify_ret(cx, &mut fn_abi.ret, &mut offset); + classify_ret(cx, &mut fn_abi.ret); } for arg in fn_abi.args.iter_mut() { if arg.is_ignore() { + if arg.layout.is_zst() { + arg.make_indirect_from_ignore(); + } continue; } - classify_arg(cx, arg, &mut offset); + classify_arg(cx, arg); } } diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 6b19f8ebd76ce..2ea4cfae1038c 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -1,7 +1,7 @@ use arrayvec::ArrayVec; use rustc_abi::{ - Align, BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Size, TyAbiInterface, - TyAndLayout, Variants, + Align, BackendRepr, FieldsShape, Float, HasDataLayout, Integer, Numeric, Primitive, Reg, + RegKind, Size, TyAbiInterface, TyAndLayout, Variants, }; use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform}; @@ -143,6 +143,13 @@ fn classify_arg<'a, Ty, C>( *total_double_word_count = start_double_word_count + double_word_count; + // Clang treats `_Complex` like a struct, GCC like a big scalar. That changes how the bits get + // packed. We follow GCC here. See also https://github.com/llvm/llvm-project/pull/212340. + if let Some(Numeric::Int(Integer::I8 | Integer::I16, _)) = arg.layout.complex_number(cx) { + arg.cast_to(Reg { kind: RegKind::Integer, size: total }); + return; + } + const ARGUMENT_REGISTERS: usize = 8; let mut double_words = [DoubleWord::Words([Word::Integer; 2]); ARGUMENT_REGISTERS / 2]; @@ -190,7 +197,7 @@ fn classify_arg<'a, Ty, C>( _ => CastTarget::prefixed(regs, Uniform::new(Reg::i8(), Size::ZERO)), }; - arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), pad); + arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), u8::from(pad)); } pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) diff --git a/tests/codegen-llvm/abi-sparc-aggregate.rs b/tests/codegen-llvm/abi-sparc-aggregate.rs new file mode 100644 index 0000000000000..429640c35726c --- /dev/null +++ b/tests/codegen-llvm/abi-sparc-aggregate.rs @@ -0,0 +1,65 @@ +// Checks that 32-bit SPARC passes aggregate arguments by reference, matching GCC and Clang +// (`SparcV8ABIInfo` falls back to Clang's `DefaultABIInfo::classifyArgumentType`, which makes +// aggregates `byval`). Regression test for rustc passing them as a run of `i32` registers. + +//@ add-minicore +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes --target=sparc-unknown-linux-gnu +//@ needs-llvm-components: sparc + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[repr(C)] +pub struct S4 { + a: u32, +} + +#[repr(C)] +pub struct S16 { + a: u32, + b: u64, +} + +#[repr(C)] +pub struct S240 { + a: [u32; 60], +} + +// CHECK-LABEL: define void @take_s4(ptr {{.*}}byval([4 x i8]) align 4 {{.*}}) +#[no_mangle] +pub extern "C" fn take_s4(_: S4) {} + +// CHECK-LABEL: define void @take_s16(ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn take_s16(_: S16) {} + +// CHECK-LABEL: define void @take_s240(ptr {{.*}}byval([240 x i8]) align 4 {{.*}}) +#[no_mangle] +pub extern "C" fn take_s240(_: S240) {} + +// An aggregate return keeps using `sret`. +// CHECK-LABEL: define void @roundtrip_s16(ptr {{.*}}sret([16 x i8]) align 8 {{.*}}, ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn roundtrip_s16(s: S16) -> S16 { + s +} + +extern "C" { + fn extern_take_s16(s: S16); +} + +// CHECK-LABEL: define void @call_s16( +// CHECK: call void @extern_take_s16(ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn call_s16(s: S16) { + unsafe { extern_take_s16(s) } +} + +// Scalars keep being passed directly, and small integers are sign/zero extended to 32 bits. +// CHECK-LABEL: define void @take_scalars(i8 noundef signext %_a, i16 noundef zeroext %_b, i32 noundef %_c) +#[no_mangle] +pub extern "C" fn take_scalars(_a: i8, _b: u16, _c: u32) {} diff --git a/tests/codegen-llvm/abi-sparc-f128.rs b/tests/codegen-llvm/abi-sparc-f128.rs new file mode 100644 index 0000000000000..fdc427851c903 --- /dev/null +++ b/tests/codegen-llvm/abi-sparc-f128.rs @@ -0,0 +1,50 @@ +// Checks that 32-bit SPARC passes and returns `f128` indirectly, like C `long double`. GCC and +// Clang both use an `sret` pointer for the return value, which is also what makes LLVM emit the +// `unimp` marker after the call and the matching `%o7+12` return in the callee. + +//@ add-minicore +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes --target=sparc-unknown-linux-gnu +//@ needs-llvm-components: sparc + +#![feature(no_core, lang_items, f128)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK-LABEL: define void @ret_f128(ptr {{.*}}sret([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn ret_f128() -> f128 { + 1.5 +} + +// CHECK-LABEL: define void @id_f128(ptr {{.*}}sret([16 x i8]) align 8 {{.*}}, ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn id_f128(x: f128) -> f128 { + x +} + +extern "C" { + fn take_f128(x: f128); +} + +// CHECK-LABEL: define void @call_f128( +// CHECK: call void @take_f128(ptr {{.*}}byval([16 x i8]) align 8 {{.*}}) +#[no_mangle] +pub extern "C" fn call_f128(x: f128) { + unsafe { take_f128(x) } +} + +// `f32` and `f64` are still passed and returned directly. +// CHECK-LABEL: define{{.*}} float @id_f32(float {{.*}}) +#[no_mangle] +pub extern "C" fn id_f32(x: f32) -> f32 { + x +} + +// CHECK-LABEL: define{{.*}} double @id_f64(double {{.*}}) +#[no_mangle] +pub extern "C" fn id_f64(x: f64) -> f64 { + x +} diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 277936d0bac2a..e056cd1d85a7c 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -21,74 +21,88 @@ //@ [WIN32_GNU] compile-flags: --target i686-pc-windows-gnu //@ [WIN32_GNU] needs-llvm-components: x86 -// FIXME: the below revisions are deliberately disabled for now. +//@ revisions: AARCH64 AARCH64_DARWIN AARCH64_MSVC ARM64EC +//@ [AARCH64] compile-flags: --target aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 +//@ [AARCH64_DARWIN] compile-flags: --target aarch64-apple-darwin +//@ [AARCH64_DARWIN] needs-llvm-components: aarch64 +//@ [AARCH64_MSVC] compile-flags: --target aarch64-pc-windows-msvc +//@ [AARCH64_MSVC] needs-llvm-components: aarch64 +//@ [ARM64EC] compile-flags: --target arm64ec-pc-windows-msvc +//@ [ARM64EC] needs-llvm-components: aarch64 + +//@ revisions: ARM +//@ [ARM] compile-flags: --target arm-unknown-linux-gnueabihf +//@ [ARM] needs-llvm-components: arm -// revisions: AARCH64 AARCH64_DARWIN AARCH64_MSVC ARM64EC -// [AARCH64] compile-flags: --target aarch64-unknown-linux-gnu -// [AARCH64] needs-llvm-components: aarch64 -// [AARCH64_DARWIN] compile-flags: --target aarch64-apple-darwin -// [AARCH64_DARWIN] needs-llvm-components: aarch64 -// [AARCH64_MSVC] compile-flags: --target aarch64-pc-windows-msvc -// [AARCH64_MSVC] needs-llvm-components: aarch64 -// [ARM64EC] compile-flags: --target arm64ec-pc-windows-msvc -// [ARM64EC] needs-llvm-components: aarch64 +//@ revisions: RISCV64 RISCV32 +//@ [RISCV64] compile-flags: --target riscv64gc-unknown-linux-gnu +//@ [RISCV64] needs-llvm-components: riscv +//@ [RISCV32] compile-flags: --target riscv32gc-unknown-linux-gnu +//@ [RISCV32] needs-llvm-components: riscv -// revisions: ARM -// [ARM] compile-flags: --target arm-unknown-linux-gnueabihf -// [ARM] needs-llvm-components: arm +//@ revisions: LOONGARCH64 LOONGARCH32 +//@ [LOONGARCH64] compile-flags: --target loongarch64-unknown-linux-gnu +//@ [LOONGARCH64] needs-llvm-components: loongarch +//@ [LOONGARCH32] compile-flags: --target loongarch32-unknown-none +//@ [LOONGARCH32] needs-llvm-components: loongarch -// revisions: RISCV64 RISCV32 -// [RISCV64] compile-flags: --target riscv64gc-unknown-linux-gnu -// [RISCV64] needs-llvm-components: riscv -// [RISCV32] compile-flags: --target riscv32gc-unknown-linux-gnu -// [RISCV32] needs-llvm-components: riscv +//@ revisions: SPARC64 SPARC +//@ [SPARC64] compile-flags: --target sparc64-unknown-linux-gnu +//@ [SPARC64] needs-llvm-components: sparc +//@ [SPARC] compile-flags: --target sparc-unknown-linux-gnu +//@ [SPARC] needs-llvm-components: sparc -// revisions: LOONGARCH64 LOONGARCH32 -// [LOONGARCH64] compile-flags: --target loongarch64-unknown-linux-gnu -// [LOONGARCH64] needs-llvm-components: loongarch -// [LOONGARCH32] compile-flags: --target loongarch32-unknown-none -// [LOONGARCH32] needs-llvm-components: loongarch +//@ revisions: S390X +//@ [S390X] compile-flags: --target s390x-unknown-linux-gnu +//@ [S390X] needs-llvm-components: systemz -// revisions: SPARC64 SPARC -// [SPARC64] compile-flags: --target sparc64-unknown-linux-gnu -// [SPARC64] needs-llvm-components: sparc -// [SPARC] compile-flags: --target sparc-unknown-linux-gnu -// [SPARC] needs-llvm-components: sparc +//@ revisions: WASM32 WASM64 +//@ [WASM32] compile-flags: --target wasm32-unknown-unknown +//@ [WASM32] needs-llvm-components: webassembly +//@ [WASM64] compile-flags: --target wasm64-unknown-unknown +//@ [WASM64] needs-llvm-components: webassembly -// revisions: S390X -// [S390X] compile-flags: --target s390x-unknown-linux-gnu -// [S390X] needs-llvm-components: systemz +//@ revisions: CSKY +//@ [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 +//@ [CSKY] needs-llvm-components: csky -// revisions: POWERPC POWERPC64LE POWERPC64 AIX -// [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu -// [POWERPC] needs-llvm-components: powerpc -// [POWERPC64LE] compile-flags: --target powerpc64le-unknown-linux-gnu -// [POWERPC64LE] needs-llvm-components: powerpc -// [POWERPC64] compile-flags: --target powerpc64-unknown-linux-gnu -// [POWERPC64] needs-llvm-components: powerpc -// [AIX] compile-flags: --target powerpc64-ibm-aix -// [AIX] needs-llvm-components: powerpc +// NOTE: for Mips we follow the GCC ABI, not the Clang ABI. +// 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 -// revisions: MIPS64EL MIPS -// [MIPS64EL] compile-flags: --target mips64el-unknown-linux-gnuabi64 -// [MIPS64EL] needs-llvm-components: mips -// [MIPS] compile-flags: --target mips-unknown-linux-gnu -// [MIPS] needs-llvm-components: mips +//@ revisions: MIPS MIPSEL +//@ [MIPS] compile-flags: --target mips-unknown-linux-gnu +//@ [MIPS] needs-llvm-components: mips +//@ [MIPSEL] compile-flags: --target mipsel-unknown-linux-gnu +//@ [MIPSEL] needs-llvm-components: mips -// revisions: WASM32 WASM64 -// [WASM32] compile-flags: --target wasm32-unknown-unknown -// [WASM32] needs-llvm-components: webassembly -// [WASM64] compile-flags: --target wasm64-unknown-unknown -// [WASM64] needs-llvm-components: webassembly +//@ revisions: POWERPC POWERPC64LE POWERPC64 AIX +//@ [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu +//@ [POWERPC] needs-llvm-components: powerpc +//@ [POWERPC64LE] compile-flags: --target powerpc64le-unknown-linux-gnu +//@ [POWERPC64LE] needs-llvm-components: powerpc +//@ [POWERPC64] compile-flags: --target powerpc64-unknown-linux-gnu +//@ [POWERPC64] needs-llvm-components: powerpc +//@ [AIX] compile-flags: --target powerpc64-ibm-aix +//@ [AIX] needs-llvm-components: powerpc -// revisions: CSKY -// [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 -// [CSKY] needs-llvm-components: csky +// FIXME: the below revisions are deliberately disabled for now. // revisions: NVPTX // [NVPTX] compile-flags: --target nvptx64-nvidia-cuda // [NVPTX] needs-llvm-components: nvptx +// revisions: AMDGPU +// [AMDGPU] compile-flags: --target amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +// [AMDGPU] needs-llvm-components: amdgpu + +// NOTE: BPF cannot codegen sret, which the larger Complex<..> types use. +// LLVM 23 adds support, see https://github.com/llvm/llvm-project/pull/206876. // revisions: BPF // [BPF] compile-flags: --target bpfel-unknown-none // [BPF] needs-llvm-components: bpf @@ -103,18 +117,18 @@ use minicore::num::Complex; #[no_mangle] pub extern "C" fn cplx_f16(x: Complex) -> Complex { - // AARCH64: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) - // AARCH64_DARWIN: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) - // AARCH64_MSVC: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) - // ARM64EC: define{{.*}} { half, half } @cplx_f16([2 x half] {{.*}}) - // ARM: define{{.*}} i32 @cplx_f16([1 x i32] {{.*}}) + // AARCH64: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) + // AARCH64_DARWIN: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) + // AARCH64_MSVC: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) + // ARM64EC: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) + // ARM: define{{.*}} [2 x half] @cplx_f16([2 x half] {{.*}}) // I686: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) - // LOONGARCH32: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) - // LOONGARCH64: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) + // LOONGARCH32: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) + // LOONGARCH64: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) // NVPTX: define{{.*}} { half, half } @cplx_f16(ptr {{.*}} byval({ half, half }) {{.*}}) - // RISCV32: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) - // RISCV64: define{{.*}} { half, half } @cplx_f16(half {{.*}}, half {{.*}}) - // S390X: define{{.*}} void @cplx_f16(ptr {{.*}} sret({ half, half }) {{.*}}, ptr {{.*}}) + // RISCV32: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) + // RISCV64: define{{.*}} { half, half } @cplx_f16({ half, half } {{.*}}) + // S390X: define{{.*}} void @cplx_f16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} <2 x half> @cplx_f16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i32 @cplx_f16(i32 {{.*}}) @@ -125,30 +139,32 @@ pub extern "C" fn cplx_f16(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_f32(x: Complex) -> Complex { - // AARCH64: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) - // AARCH64_DARWIN: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) - // AARCH64_MSVC: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) + // AARCH64: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) + // AARCH64_DARWIN: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) + // AARCH64_MSVC: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) // AIX: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // ARM64EC: define{{.*}} { float, float } @cplx_f32([2 x float] {{.*}}) - // ARM: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // ARM64EC: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) + // ARM: define{{.*}} [2 x float] @cplx_f32([2 x float] {{.*}}) // BPF: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, i64 {{.*}}) // CSKY: define{{.*}} [2 x i32] @cplx_f32([2 x i32] {{.*}}) // 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 {{.*}}) + // LOONGARCH32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // LOONGARCH64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // MIPS64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // MIPS: define{{.*}} { float, float } @cplx_f32(i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { float, float } @cplx_f32([2 x i32] {{.*}}) + // MIPSEL: 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 {{.*}}) // POWERPC64LE: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // POWERPC: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) - // RISCV32: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // RISCV64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} {{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) - // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval({ float, float }) {{.*}}) - // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) - // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret({ float, float }) {{.*}}, ptr {{.*}} byval({ float, float }) {{.*}}) + // POWERPC: define{{.*}} i64 @cplx_f32(i64 {{.*}}) + // RISCV32: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // RISCV64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // S390X: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) + // SPARC64: define{{.*}} { float, float } @cplx_f32({ float, float } {{.*}}) + // SPARC: define{{.*}} { float, float } @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) + // WASM32: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_f32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i64 @cplx_f32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i64 @cplx_f32(i64 {{.*}}) @@ -159,30 +175,32 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_f64(x: Complex) -> Complex { - // AARCH64: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) - // AARCH64_DARWIN: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) - // AARCH64_MSVC: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) + // AARCH64: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) + // AARCH64_DARWIN: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) + // AARCH64_MSVC: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // AIX: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // ARM64EC: define{{.*}} { double, double } @cplx_f64([2 x double] {{.*}}) - // ARM: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // ARM64EC: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) + // ARM: define{{.*}} [2 x double] @cplx_f64([2 x double] {{.*}}) // BPF: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [2 x i64] {{.*}}) - // CSKY: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, [4 x i32] {{.*}}) + // CSKY: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, [4 x i32] {{.*}}) // 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 {{.*}}) + // LOONGARCH32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // LOONGARCH64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // MIPS64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // MIPS64EL: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // MIPS: define{{.*}} { double, double } @cplx_f64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { double, double } @cplx_f64([4 x i32] {{.*}}) + // MIPSEL: define{{.*}} { double, double } @cplx_f64([4 x i32] {{.*}}) // NVPTX: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) // POWERPC64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) // POWERPC64LE: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // POWERPC: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) - // RISCV32: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // RISCV64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} { double, double } @cplx_f64(double {{.*}}, double {{.*}}) - // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval({ double, double }) {{.*}}) - // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) - // WASM64: define{{.*}} void @cplx_f64(ptr {{.*}} sret({ double, double }) {{.*}}, ptr {{.*}} byval({ double, double }) {{.*}}) + // POWERPC: define{{.*}} [4 x i32] @cplx_f64([4 x i32] {{.*}}) + // RISCV32: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // RISCV64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // S390X: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) + // SPARC64: define{{.*}} { double, double } @cplx_f64({ double, double } {{.*}}) + // SPARC: define{{.*}} { double, double } @cplx_f64(ptr {{.*}} byval([16 x i8]) {{.*}}) + // WASM32: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} void @cplx_f64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) @@ -193,41 +211,49 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_f128(x: Complex) -> Complex { + // AARCH64: define{{.*}} [2 x fp128] {{.*}}) // I686: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) - // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret({ fp128, fp128 }) {{.*}}, ptr {{.*}} byval({ fp128, fp128 }) {{.*}}) + // 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 {{.*}} byval([32 x i8]) {{.*}}) + // WASM32: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) // X86_64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}} byval([32 x i8]) {{.*}}) + // LOONGARCH64: define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) + // RISCV64 define{{.*}} void @cplx_f128(ptr {{.*}} sret([32 x i8]) {{.*}}, ptr {{.*}}) x } #[no_mangle] pub extern "C" fn cplx_i8(x: Complex) -> Complex { - // AARCH64: define{{.*}} i16 @cplx_i8(i64{{.*}}) - // AARCH64_DARWIN: define{{.*}} i16 @cplx_i8(i64{{.*}}) - // AARCH64_MSVC: define{{.*}} i16 @cplx_i8(i64{{.*}}) + // AARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // AARCH64_DARWIN: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // AARCH64_MSVC: define{{.*}} i64 @cplx_i8(i64{{.*}}) // AIX: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) - // ARM64EC: define{{.*}} i16 @cplx_i8(i64{{.*}}) - // ARM: define{{.*}} i16 @cplx_i8([1 x i32]{{.*}}) + // ARM64EC: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // ARM: define{{.*}} i32 @cplx_i8(i32{{.*}}) // BPF: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, i16 {{.*}}) - // CSKY: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) + // CSKY: define{{.*}} i32 @cplx_i8(i32{{.*}}) // 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 {{.*}}) - // MIPS: define{{.*}} { i8, i8 } @cplx_i8(i16 {{.*}}) + // LOONGARCH32: define{{.*}} i32 @cplx_i8(i32{{.*}}) + // LOONGARCH64: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // MIPS64: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) + // MIPS64EL: define{{.*}} i16 @cplx_i8(i16 inreg {{.*}}) + // MIPS: define{{.*}} i16 @cplx_i8(i32 {{.*}}) + // MIPSEL: define{{.*}} i16 @cplx_i8(i32 {{.*}}) // NVPTX: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) // POWERPC64: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) // POWERPC64LE: define{{.*}} { i8, i8 } @cplx_i8(i8 {{.*}}, i8 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) - // RISCV32: define{{.*}} {{.*}} i32 @cplx_i8(i32{{.*}}) - // RISCV64: define{{.*}} {{.*}} i64 @cplx_i8(i64{{.*}}) - // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} {{.*}} i64 @cplx_i8(i64{{.*}}) - // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) - // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret({ i8, i8 }) {{.*}}, ptr {{.*}} byval({ i8, i8 }) {{.*}}) + // POWERPC: define{{.*}} i16 @cplx_i8(i16 {{.*}}) + // RISCV32: define{{.*}} i32 @cplx_i8(i32{{.*}}) + // RISCV64: define{{.*}} i64 @cplx_i8(i64{{.*}}) + // S390X: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) + // SPARC64: define{{.*}} i16 @cplx_i8(i16{{.*}}) + // SPARC: define{{.*}} i16 @cplx_i8(i16{{.*}}) + // WASM32: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i8(ptr {{.*}} sret([2 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i16 @cplx_i8(ptr {{.*}} byval([2 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i16 @cplx_i8(i16 {{.*}}) @@ -238,30 +264,32 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { #[no_mangle] pub extern "C" fn cplx_i16(x: Complex) -> Complex { - // AARCH64: define{{.*}} i32 @cplx_i16(i64{{.*}}) - // AARCH64_DARWIN: define{{.*}} i32 @cplx_i16(i64{{.*}}) - // AARCH64_MSVC: define{{.*}} i32 @cplx_i16(i64{{.*}}) + // AARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // AARCH64_DARWIN: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // AARCH64_MSVC: define{{.*}} i64 @cplx_i16(i64{{.*}}) // AIX: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) - // ARM64EC: define{{.*}} i32 @cplx_i16(i64{{.*}}) - // ARM: define{{.*}} i32 @cplx_i16([1 x i32] {{.*}}) + // ARM64EC: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // ARM: define{{.*}} i32 @cplx_i16(i32{{.*}}) // BPF: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, i32 {{.*}}) // CSKY: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // 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 {{.*}}) - // MIPS: define{{.*}} { i16, i16 } @cplx_i16(i32 {{.*}}) + // LOONGARCH64: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // MIPS64: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) + // MIPS64EL: define{{.*}} i32 @cplx_i16(i32 inreg {{.*}}) + // MIPS: define{{.*}} i32 @cplx_i16(i32 {{.*}}) + // MIPSEL: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // NVPTX: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) // POWERPC64: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) // POWERPC64LE: define{{.*}} { i16, i16 } @cplx_i16(i16 {{.*}}, i16 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) + // POWERPC: define{{.*}} i32 @cplx_i16(i32 {{.*}}) // RISCV32: define{{.*}} i32 @cplx_i16(i32 {{.*}}) - // RISCV64: define{{.*}} {{.*}} i64 @cplx_i16(i64{{.*}}) - // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} {{.*}} i64 @cplx_i16(i64{{.*}}) - // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) - // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret({ i16, i16 }) {{.*}}, ptr {{.*}} byval({ i16, i16 }) {{.*}}) + // RISCV64: define{{.*}} i64 @cplx_i16(i64{{.*}}) + // S390X: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) + // SPARC64: define{{.*}} i32 @cplx_i16(i32{{.*}}) + // SPARC: define{{.*}} i32 @cplx_i16(i32{{.*}}) + // WASM32: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i16(ptr {{.*}} sret([4 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i32 @cplx_i16(ptr {{.*}} byval([4 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i32 @cplx_i16(i32 {{.*}}) @@ -277,25 +305,27 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // AIX: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // ARM64EC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // ARM: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, [2 x i32] {{.*}}) + // ARM: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, [2 x i32] {{.*}}) // BPF: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, i64 {{.*}}) // CSKY: define{{.*}} [2 x i32] @cplx_i32([2 x 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 {{.*}}) - // MIPS: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) + // MIPS64: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) + // MIPS64EL: define{{.*}} i64 @cplx_i32(i64 inreg {{.*}}) + // MIPS: define{{.*}} { i32, i32 } @cplx_i32([2 x i32] {{.*}}) + // MIPSEL: define{{.*}} { i32, i32 } @cplx_i32([2 x i32] {{.*}}) // NVPTX: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) // POWERPC64: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) // POWERPC64LE: define{{.*}} { i32, i32 } @cplx_i32(i32 {{.*}}, i32 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) + // POWERPC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) // RISCV32: define{{.*}} [2 x i32] @cplx_i32([2 x i32] {{.*}}) // RISCV64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}}) + // S390X: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // SPARC: define{{.*}} { i32, i32 } @cplx_i32(ptr {{.*}} byval({ i32, i32 }) {{.*}}) - // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i32(ptr {{.*}} sret({ i32, i32 }) {{.*}}, ptr {{.*}} byval({ i32, i32 }) {{.*}}) + // SPARC: define{{.*}} i64 @cplx_i32(i64 {{.*}}) + // WASM32: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i32(ptr {{.*}} sret([8 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} i64 @cplx_i32(ptr {{.*}} byval([8 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} i64 @cplx_i32(i64 {{.*}}) @@ -311,25 +341,27 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // AARCH64_MSVC: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) // AIX: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // ARM64EC: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) - // ARM: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [2 x i64] {{.*}}) + // ARM: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, [2 x i64] {{.*}}) // BPF: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [2 x i64] {{.*}}) - // CSKY: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, [4 x i32] {{.*}}) + // CSKY: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, [4 x i32] {{.*}}) // I686: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) - // LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) + // LOONGARCH32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // LOONGARCH64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) + // MIPS64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS64EL: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) - // MIPS: define{{.*}} { i64, i64 } @cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { i64, i64 } @cplx_i64([4 x i32] {{.*}}) + // MIPSEL: define{{.*}} { i64, i64 } @cplx_i64([4 x i32] {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) // POWERPC64LE: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) - // POWERPC: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) - // RISCV32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) + // POWERPC: define{{.*}} [4 x i32] @cplx_i64([4 x i32] {{.*}}) + // RISCV32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // RISCV64: define{{.*}} [2 x i64] @cplx_i64([2 x i64] {{.*}}) - // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) - // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) - // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) - // WASM64: define{{.*}} void @cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // S390X: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) + // SPARC64: define { i64, i64 } @cplx_i64({ i64, i64 } {{.*}}) + // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval([16 x i8]) {{.*}}) + // WASM32: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) + // WASM64: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WIN32_MSVC: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) // WINDOWS_GNU: define{{.*}} void @cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) diff --git a/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs b/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs index 6ecad80b008f5..a16f9dcc3a43e 100644 --- a/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs +++ b/tests/codegen-llvm/repr/transparent-byval-struct-ptr.rs @@ -1,5 +1,5 @@ //@ add-minicore -//@ revisions: i686-linux i686-freebsd x64-linux x64-apple +//@ revisions: i686-linux i686-freebsd x64-linux x64-apple sparc //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes //@[i686-linux] compile-flags: --target i686-unknown-linux-gnu @@ -10,6 +10,8 @@ //@[x64-linux] needs-llvm-components: x86 //@[x64-apple] compile-flags: --target x86_64-apple-darwin //@[x64-apple] needs-llvm-components: x86 +//@[sparc] compile-flags: --target sparc-unknown-linux-gnu +//@[sparc] needs-llvm-components: sparc // See ./transparent.rs // Some platforms pass large aggregates using immediate arrays in LLVMIR diff --git a/tests/codegen-llvm/repr/transparent-imm-array.rs b/tests/codegen-llvm/repr/transparent-imm-array.rs index c72151741400a..04c9727b815d4 100644 --- a/tests/codegen-llvm/repr/transparent-imm-array.rs +++ b/tests/codegen-llvm/repr/transparent-imm-array.rs @@ -1,5 +1,5 @@ //@ add-minicore -//@ revisions: arm-linux arm-android armv7-linux armv7-android mips thumb sparc +//@ revisions: arm-linux arm-android armv7-linux armv7-android mips thumb //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes //@[arm-linux] compile-flags: --target arm-unknown-linux-gnueabi @@ -14,8 +14,6 @@ //@[mips] needs-llvm-components: mips //@[thumb] compile-flags: --target thumbv7neon-linux-androideabi //@[thumb] needs-llvm-components: arm -//@[sparc] compile-flags: --target sparc-unknown-linux-gnu -//@[sparc] needs-llvm-components: sparc // See ./transparent.rs // Some platforms pass large aggregates using immediate arrays in LLVMIR diff --git a/tests/run-make/complex-abi/main.rs b/tests/run-make/complex-abi/main.rs new file mode 100644 index 0000000000000..5c9a4367b8c62 --- /dev/null +++ b/tests/run-make/complex-abi/main.rs @@ -0,0 +1,499 @@ +// ignore-tidy-file-linelength +#![feature(complex_numbers, f128)] +#![allow(improper_ctypes)] +#![allow(unused_features)] +#![deny(dead_code)] + +use std::ffi::*; +use std::num::Complex; + +fn main() { + sqrt(); + #[cfg(not(target_family = "wasm"))] + mul(); + #[cfg(not(target_family = "wasm"))] + div(); + pass_simple(); + aligned_int(); + aligned_float(); + spill_gpr(); + spill_fpr(); + partial_gpr(); +} + +// This is just a placeholder until we actually add c_longdouble. +#[allow(non_camel_case_types)] +#[allow(unused)] +type c_longdouble = cfg_select! { + target_arch = "aarch64" => f128, + _ => (), +}; + +fn sqrt() { + unsafe extern "C" { + safe fn csqrtf(_: Complex) -> Complex; + safe fn csqrt(_: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn csqrtl(_: Complex) -> Complex; + } + + let c = Complex::new(-1.0, 0.0); + assert_eq!(csqrtf(c), Complex::new(0.0, 1.0)); + + let c = Complex::new(-1.0, 0.0); + assert_eq!(csqrt(c), Complex::new(0.0, 1.0)); + + #[cfg(target_arch = "aarch64")] + { + let c = Complex::new(-1.0, 0.0); + assert_eq!(csqrtl(c), Complex::new(0.0, 1.0)); + } +} + +#[cfg(not(target_family = "wasm"))] +fn mul() { + unsafe extern "C" { + safe fn __mulsc3(a: c_float, b: c_float, c: c_float, d: c_float) -> Complex; + safe fn __muldc3(a: c_double, b: c_double, c: c_double, d: c_double) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn __multc3( + a: c_longdouble, + b: c_longdouble, + c: c_longdouble, + d: c_longdouble, + ) -> Complex; + } + + assert_eq!(__mulsc3(1.0, 2.0, 3.0, 4.0), Complex::new(-5.0, 10.0)); + assert_eq!(__muldc3(1.0, 2.0, 3.0, 4.0), Complex::new(-5.0, 10.0)); + + #[cfg(target_arch = "aarch64")] + assert_eq!(__multc3(1.0, 2.0, 3.0, 4.0), Complex::new(-5.0, 10.0)); + + // The naive algorithm would return NaN + NaNi for these inputs, but the libcall handles it. + assert_eq!( + __mulsc3(1.0, 0.0, c_float::INFINITY, c_float::INFINITY), + Complex::new(c_float::INFINITY, c_float::INFINITY) + ); + assert_eq!( + __muldc3(1.0, 0.0, c_double::INFINITY, c_double::INFINITY), + Complex::new(c_double::INFINITY, c_double::INFINITY) + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + __multc3(1.0, 0.0, c_longdouble::INFINITY, c_longdouble::INFINITY), + Complex::new(c_longdouble::INFINITY, c_longdouble::INFINITY) + ); +} + +#[cfg(not(target_family = "wasm"))] +fn div() { + unsafe extern "C" { + safe fn __divsc3(a: c_float, b: c_float, c: c_float, d: c_float) -> Complex; + safe fn __divdc3(a: c_double, b: c_double, c: c_double, d: c_double) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn __divtc3( + a: c_longdouble, + b: c_longdouble, + c: c_longdouble, + d: c_longdouble, + ) -> Complex; + } + + assert_eq!(__divsc3(2.0, 11.0, 2.0, 1.0), Complex::new(3.0, 4.0)); + assert_eq!(__divdc3(2.0, 11.0, 2.0, 1.0), Complex::new(3.0, 4.0)); + + #[cfg(target_arch = "aarch64")] + assert_eq!(__divtc3(2.0, 11.0, 2.0, 1.0), Complex::new(3.0, 4.0)); + + assert_eq!( + __divsc3(c_float::INFINITY, 0.0, 1.0, 1.0), + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY) + ); + assert_eq!( + __divdc3(c_double::INFINITY, 0.0, 1.0, 1.0), + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY) + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + __divtc3(c_longdouble::INFINITY, 0.0, 1.0, 1.0), + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY) + ); +} + +fn pass_simple() { + #[rustfmt::skip] + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn pass_simple_complex_float(x: Complex) -> Complex; + safe fn pass_simple_complex_double(x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn pass_simple_complex_long_double(x: Complex) -> Complex; + + safe fn pass_simple_complex_char(x: Complex) -> Complex; + safe fn pass_simple_complex_short(x: Complex) -> Complex; + safe fn pass_simple_complex_int(x: Complex) -> Complex; + safe fn pass_simple_complex_long(x: Complex) -> Complex; + safe fn pass_simple_complex_long_long(x: Complex) -> Complex; + } + + assert_eq!(Complex::new(3.5, 4.5), pass_simple_complex_float(Complex::new(3.5, 4.5))); + assert_eq!(Complex::new(3.5, 4.5), pass_simple_complex_double(Complex::new(3.5, 4.5))); + + assert_eq!( + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY), + pass_simple_complex_float(Complex::new(c_float::INFINITY, c_float::NEG_INFINITY)) + ); + assert_eq!( + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY), + pass_simple_complex_double(Complex::new(c_double::INFINITY, c_double::NEG_INFINITY)) + ); + #[cfg(target_arch = "aarch64")] + assert_eq!( + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY), + pass_simple_complex_long_double(Complex::new( + c_longdouble::INFINITY, + c_longdouble::NEG_INFINITY + )) + ); + + assert_eq!(Complex::new(3, 4), pass_simple_complex_char(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_short(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_int(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_long(Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), pass_simple_complex_long_long(Complex::new(3, 4))); + + assert_eq!( + Complex::new(c_char::MIN, c_char::MAX), + pass_simple_complex_char(Complex::new(c_char::MIN, c_char::MAX)) + ); + assert_eq!( + Complex::new(c_short::MIN, c_short::MAX), + pass_simple_complex_short(Complex::new(c_short::MIN, c_short::MAX)) + ); + assert_eq!( + Complex::new(c_int::MIN, c_int::MAX), + pass_simple_complex_int(Complex::new(c_int::MIN, c_int::MAX)) + ); + assert_eq!( + Complex::new(c_long::MIN, c_long::MAX), + pass_simple_complex_long(Complex::new(c_long::MIN, c_long::MAX)) + ); + assert_eq!( + Complex::new(c_longlong::MIN, c_longlong::MAX), + pass_simple_complex_long_long(Complex::new(c_longlong::MIN, c_longlong::MAX)) + ); +} + +fn aligned_int() { + #[rustfmt::skip] + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn complex_float_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_double_align_int(_: c_int, x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn complex_long_double_align_int(_: c_int, x: Complex) -> Complex; + + safe fn complex_char_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_short_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_int_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_long_align_int(_: c_int, x: Complex) -> Complex; + safe fn complex_long_long_align_int(_: c_int, x: Complex) -> Complex; + } + + let a = 0xAAAA_AAAAu32 as c_int; + + assert_eq!(Complex::new(3.5, 4.5), complex_float_align_int(a, Complex::new(3.5, 4.5))); + assert_eq!(Complex::new(3.5, 4.5), complex_double_align_int(a, Complex::new(3.5, 4.5))); + + assert_eq!( + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY), + complex_float_align_int(a, Complex::new(c_float::INFINITY, c_float::NEG_INFINITY)) + ); + assert_eq!( + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY), + complex_double_align_int(a, Complex::new(c_double::INFINITY, c_double::NEG_INFINITY)) + ); + #[cfg(target_arch = "aarch64")] + assert_eq!( + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY), + complex_long_double_align_int( + a, + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY) + ) + ); + + assert_eq!(Complex::new(3, 4), complex_char_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_short_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_int_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_align_int(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_long_align_int(a, Complex::new(3, 4))); + + assert_eq!( + Complex::new(c_char::MIN, c_char::MAX), + complex_char_align_int(a, Complex::new(c_char::MIN, c_char::MAX)) + ); + assert_eq!( + Complex::new(c_short::MIN, c_short::MAX), + complex_short_align_int(a, Complex::new(c_short::MIN, c_short::MAX)) + ); + assert_eq!( + Complex::new(c_int::MIN, c_int::MAX), + complex_int_align_int(a, Complex::new(c_int::MIN, c_int::MAX)) + ); + assert_eq!( + Complex::new(c_long::MIN, c_long::MAX), + complex_long_align_int(a, Complex::new(c_long::MIN, c_long::MAX)) + ); + assert_eq!( + Complex::new(c_longlong::MIN, c_longlong::MAX), + complex_long_long_align_int(a, Complex::new(c_longlong::MIN, c_longlong::MAX)) + ); +} + +fn aligned_float() { + #[rustfmt::skip] + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn complex_float_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_double_align_float(_: c_float, x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn complex_long_double_align_float(_: c_float, x: Complex) -> Complex; + + safe fn complex_char_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_short_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_int_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_long_align_float(_: c_float, x: Complex) -> Complex; + safe fn complex_long_long_align_float(_: c_float, x: Complex) -> Complex; + } + + let a = 3.14 as c_float; + + assert_eq!(Complex::new(3.5, 4.5), complex_float_align_float(a, Complex::new(3.5, 4.5))); + assert_eq!(Complex::new(3.5, 4.5), complex_double_align_float(a, Complex::new(3.5, 4.5))); + + assert_eq!( + Complex::new(c_float::INFINITY, c_float::NEG_INFINITY), + complex_float_align_float(a, Complex::new(c_float::INFINITY, c_float::NEG_INFINITY)) + ); + assert_eq!( + Complex::new(c_double::INFINITY, c_double::NEG_INFINITY), + complex_double_align_float(a, Complex::new(c_double::INFINITY, c_double::NEG_INFINITY)) + ); + #[cfg(target_arch = "aarch64")] + assert_eq!( + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY), + complex_long_double_align_float( + a, + Complex::new(c_longdouble::INFINITY, c_longdouble::NEG_INFINITY) + ) + ); + + assert_eq!(Complex::new(3, 4), complex_char_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_short_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_int_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_align_float(a, Complex::new(3, 4))); + assert_eq!(Complex::new(3, 4), complex_long_long_align_float(a, Complex::new(3, 4))); + + assert_eq!( + Complex::new(c_char::MIN, c_char::MAX), + complex_char_align_float(a, Complex::new(c_char::MIN, c_char::MAX)) + ); + assert_eq!( + Complex::new(c_short::MIN, c_short::MAX), + complex_short_align_float(a, Complex::new(c_short::MIN, c_short::MAX)) + ); + assert_eq!( + Complex::new(c_int::MIN, c_int::MAX), + complex_int_align_float(a, Complex::new(c_int::MIN, c_int::MAX)) + ); + assert_eq!( + Complex::new(c_long::MIN, c_long::MAX), + complex_long_align_float(a, Complex::new(c_long::MIN, c_long::MAX)) + ); + assert_eq!( + Complex::new(c_longlong::MIN, c_longlong::MAX), + complex_long_long_align_float(a, Complex::new(c_longlong::MIN, c_longlong::MAX)) + ); +} + +#[rustfmt::skip] +fn spill_gpr() { + #[link(name = "test", kind = "static")] + unsafe extern "C" { + fn spill_trailing_complex_float_1(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex, h: c_int) -> c_int; + + fn spill_trailing_complex_double_1(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, x: Complex, g: c_int) -> c_int; + fn spill_trailing_complex_double_2(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, x: Complex, f: c_int, g: c_int) -> c_int; + fn spill_trailing_complex_double_3(a: c_int, b: c_int, c: c_int, d: c_int, x: Complex, e: c_int, f: c_int, g: c_int) -> c_int; + + #[cfg(target_arch = "aarch64")] + fn spill_trailing_complex_long_double_1(a: c_int, x: Complex, b: c_int) -> c_int; + } + + unsafe { + assert_eq!( + spill_trailing_complex_float_1(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, 0x77777777, Complex::new(1.0, 2.0), 0x88888888_u32 as c_int), + 0x88888888_u32 as c_int, + ); + + assert_eq!( + spill_trailing_complex_double_1(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, Complex::new(1.0, 2.0), 0x77777777), + 0x77777777, + ); + + assert_eq!( + spill_trailing_complex_double_2(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, Complex::new(1.0, 2.0), 0x66666666, 0x77777777), + 0x77777777, + ); + + assert_eq!( + spill_trailing_complex_double_3(0x11111111, 0x22222222, 0x33333333, 0x44444444, Complex::new(1.0, 2.0), 0x55555555, 0x66666666, 0x77777777), + 0x77777777, + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + spill_trailing_complex_long_double_1(0x11111111, Complex::new(1.0, 2.0), 0x22222222,), + 0x22222222, + ); + } +} + +#[rustfmt::skip] +fn spill_fpr() { + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn spill_trailing_complex_float_1_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, value: Complex, x: f32) -> f32; + safe fn spill_trailing_complex_float_2_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, a7: f32, value: Complex, x: f32) -> f32; + + safe fn spill_trailing_complex_double_1_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, value: Complex, x: f32) -> f32; + safe fn spill_trailing_complex_double_2_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, value: Complex, x: f32, y: f32) -> f32; + safe fn spill_trailing_complex_double_3_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, a6: f32, a7: f32, value: Complex, x: f32, y: f32) -> f32; + + #[cfg(target_arch = "aarch64")] + safe fn spill_trailing_complex_long_double_1_float(a0: f32, a1: f32, a2: f32, a3: f32, a4: f32, a5: f32, value: Complex, x: f32) -> f32; + } + + assert_eq!( + spill_trailing_complex_float_1_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, Complex::new(1.0, 2.0), 0.8), + 0.8, + ); + assert_eq!( + spill_trailing_complex_float_2_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, Complex::new(1.0, 2.0), 0.8), + 0.8, + ); + + assert_eq!( + spill_trailing_complex_double_1_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, Complex::new(1.0, 2.0), 0.6), + 0.6, + ); + assert_eq!( + spill_trailing_complex_double_2_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, Complex::new(1.0, 2.0), 0.7, 0.8), + 0.8, + ); + assert_eq!( + spill_trailing_complex_double_3_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, Complex::new(1.0, 2.0), 0.8, 0.9), + 0.9, + ); + + #[cfg(target_arch = "aarch64")] + assert_eq!( + spill_trailing_complex_long_double_1_float(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, Complex::new(1.0, 2.0), 0.6), + 0.6, + ); +} + +#[rustfmt::skip] +fn partial_gpr() { + #[link(name = "test", kind = "static")] + unsafe extern "C" { + safe fn partial_complex_float(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_double(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + #[cfg(target_arch = "aarch64")] + safe fn partial_complex_long_double(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + + safe fn partial_complex_char(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_short(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_int(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_long(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + safe fn partial_complex_long_long(a: c_int, b: c_int, c: c_int, d: c_int, e: c_int, f: c_int, g: c_int, x: Complex) -> Complex; + } + + let complex = Complex { re: 3.14, im: 6.28 }; + assert_eq!( + partial_complex_float( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3.14, im: 6.28 }; + assert_eq!( + partial_complex_double( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + #[cfg(target_arch = "aarch64")] + { + let complex = Complex { re: 3.14 as c_longdouble, im: 6.28 as c_longdouble }; + assert_eq!( + partial_complex_long_double( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + } + + let complex = Complex { re: 3 as c_schar, im: 6 as c_schar }; + assert_eq!( + partial_complex_char( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_short, im: 6 as c_short }; + assert_eq!( + partial_complex_short( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_int, im: 6 as c_int }; + assert_eq!( + partial_complex_int( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_long, im: 6 as c_long }; + assert_eq!( + partial_complex_long( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); + + let complex = Complex { re: 3 as c_longlong, im: 6 as c_longlong }; + assert_eq!( + partial_complex_long_long( + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0x55555555, 0x66666666, 0x77777777, complex, + ), + complex, + ); +} diff --git a/tests/run-make/complex-abi/rmake.rs b/tests/run-make/complex-abi/rmake.rs new file mode 100644 index 0000000000000..afb4dc9621d6b --- /dev/null +++ b/tests/run-make/complex-abi/rmake.rs @@ -0,0 +1,13 @@ +//@ needs-target-std +//@ ignore-android: FIXME(#142855) +//@ ignore-sgx: (x86 machine code cannot be directly executed) +//@ ignore-pauthtest: (it requires non-trivial compilation of c sources, and only supports dynamic +// linking, ignore the test). + +use run_make_support::{bin_name, build_native_static_lib, env_var, run, rustc}; + +fn main() { + build_native_static_lib("test"); + rustc().linker(&env_var("CC")).input("main.rs").run(); + run(&bin_name("main")); +} diff --git a/tests/run-make/complex-abi/test.c b/tests/run-make/complex-abi/test.c new file mode 100644 index 0000000000000..45155ca6f113d --- /dev/null +++ b/tests/run-make/complex-abi/test.c @@ -0,0 +1,113 @@ +// ignore-tidy-file-linelength + +_Complex float pass_simple_complex_float(_Complex float x) { return x; } +_Complex double pass_simple_complex_double(_Complex double x) { return x; } +_Complex long double pass_simple_complex_long_double(_Complex long double x) { return x; } + +_Complex char pass_simple_complex_char(_Complex char x) { return x; } +_Complex short pass_simple_complex_short(_Complex short x) { return x; } +_Complex int pass_simple_complex_int(_Complex int x) { return x; } +_Complex long pass_simple_complex_long(_Complex long x) { return x; } +_Complex long long pass_simple_complex_long_long(_Complex long long x) { return x; } + + + +_Complex float complex_float_align_int(int a0, _Complex float value) { return value; } +_Complex double complex_double_align_int(int a0, _Complex double value) { return value; } +_Complex long double complex_long_double_align_int(int a0, _Complex long double value) { return value; } + +_Complex char complex_char_align_int(int a0, _Complex char value) { return value; } +_Complex short complex_short_align_int(int a0, _Complex short value) { return value; } +_Complex int complex_int_align_int(int a0, _Complex int value) { return value; } +_Complex long complex_long_align_int(int a0, _Complex long value) { return value; } +_Complex long long complex_long_long_align_int(int a0, _Complex long long value) { return value; } + + + +_Complex float complex_float_align_float(float a0, _Complex float value) { return value; } +_Complex double complex_double_align_float(float a0, _Complex double value) { return value; } +_Complex long double complex_long_double_align_float(float a0, _Complex long double value) { return value; } + +_Complex char complex_char_align_float(float a0, _Complex char value) { return value; } +_Complex short complex_short_align_float(float a0, _Complex short value) { return value; } +_Complex int complex_int_align_float(float a0, _Complex int value) { return value; } +_Complex long complex_long_align_float(float a0, _Complex long value) { return value; } +_Complex long long complex_long_long_align_float(float a0, _Complex long long value) { return value; } + + + +int spill_trailing_complex_float_1(int a0, int a1, int a2, int a3, int a4, int a5, int a6, _Complex float value, int x) { return x; } + +int spill_trailing_complex_double_1(int a0, int a1, int a2, int a3, int a4, int a5, _Complex double value, int x) { return x; } +int spill_trailing_complex_double_2(int a0, int a1, int a2, int a3, int a4, _Complex double value, int x, int y) { return y; } +int spill_trailing_complex_double_3(int a0, int a1, int a2, int a3, _Complex double value, int x, int y, int z) { return z; } + +int spill_trailing_complex_long_double_1(int a0, _Complex long double value, int x) { return x; } + + + +float spill_trailing_complex_float_1_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, _Complex float value, float x) { return x; } +float spill_trailing_complex_float_2_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, _Complex float value, float x) { return x; } + +float spill_trailing_complex_double_1_float(float a0, float a1, float a2, float a3, float a4, float a5, _Complex double value, float x) { return x; } +float spill_trailing_complex_double_2_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, _Complex double value, float x, float y) { return y; } +float spill_trailing_complex_double_3_float(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, _Complex double value, float x, float y) { return y; } + +float spill_trailing_complex_long_double_1_float(float a0, float a1, float a2, float a3, float a4, float a5, _Complex long double value, float x) { return x; } + + +_Complex float partial_complex_float( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex float value) { + return value; +} + +_Complex double partial_complex_double( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex double value) { + return value; +} + +_Complex long double partial_complex_long_double( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex long double value) { + return value; +} + +_Complex char partial_complex_char( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex char value) { + return value; +} + +_Complex short partial_complex_short( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex short value) { + return value; +} + +_Complex int partial_complex_int( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex int value) { + return value; +} + +_Complex long partial_complex_long( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex long value) { + return value; +} + +_Complex long long partial_complex_long_long( + int a0, int a1, int a2, int a3, + int a4, int a5, int a6, + _Complex long long value) { + return value; +} diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index 320840c8149f5..232810d7b57e0 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -121,7 +121,7 @@ error: fn_abi_of(extern_rust) = FnAbi { }, }, mode: Cast { - pad_i32: false, + pad_i32: 0, cast: CastTarget { prefix: [], rest_offset: None,