diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 7c878cc619472..10921f94dc74b 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, }; @@ -345,6 +345,17 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { } } + pub fn complex_number(&self, cx: &C) -> Option + where + Ty: TyAbiInterface<'a, C> + Copy, + { + match self.complex_number_primitive(cx)? { + Primitive::Int(i, sign) => Some(Numeric::Int(i, sign)), + Primitive::Float(f) => Some(Numeric::Float(f)), + Primitive::Pointer(_) => None, + } + } + /// Whether this type/layout has any padding that is dependent on a variant, i.e. has bytes that /// are padding for some, but not all, valid values of this type. pub fn has_variant_dependent_padding(&self, cx: &C) -> bool diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 7cfb93ca1b86d..a306b494ceced 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1451,6 +1451,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_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index d3a39d05b4964..a68c2e71c923d 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: component.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); } } diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 8002f98507ba8..d28edb46bed85 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, Integer, 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,22 @@ where let size = ret.layout.size; let bits = size.bits(); if bits <= 128 { + 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. + // We match GCC, not Clang, see https://github.com/llvm/llvm-project/issues/212109. + 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. @@ -102,6 +121,39 @@ 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) { + let slot = dl.pointer_size(); + let curr_offset = offset.align_to(align); + + match component { + Numeric::Float(Float::F128) => { + // 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); + } + Numeric::Float(_) => { + // Only pass a Complex/Complex in FPRs when two argument slots are free, + 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 a Complex which could fit into one slot. + *offset = curr_offset + slot * 2; + 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::Int(Integer::I8 | Integer::I16 | Integer::I32, _) => { + // Cast Complex into i16, Complex 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 is passed as 2 separate arguments, which is what the default + // `PassMode::Pair` already does. + } + } } else { match arg.layout.fields { FieldsShape::Primitive => unreachable!(), 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/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 71af508915e59..d58a17383c2b0 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 = u8::from(!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.make_indirect(); } 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 7e441d7100c39..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]; diff --git a/tests/codegen-llvm/complex-abi.rs b/tests/codegen-llvm/complex-abi.rs index 81d93b704ee1c..3b404fad2af32 100644 --- a/tests/codegen-llvm/complex-abi.rs +++ b/tests/codegen-llvm/complex-abi.rs @@ -61,28 +61,48 @@ //@ [CSKY] compile-flags: --target csky-unknown-linux-gnuabiv2 //@ [CSKY] needs-llvm-components: csky -// FIXME: the below revisions are deliberately disabled for now. +//@ 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 + +// 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: 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: 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: 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: 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 + +// 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 @@ -130,17 +150,19 @@ pub extern "C" fn cplx_f32(x: Complex) -> Complex { // 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 } {{.*}}) + // MIPS64: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // MIPS64EL: define{{.*}} { float, float } @cplx_f32(float {{.*}}, float {{.*}}) // 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 }) {{.*}}) + // 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({ float, float }) {{.*}}) + // 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]) {{.*}}) @@ -164,17 +186,19 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { // 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 } {{.*}}) + // 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 }) {{.*}}) + // 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({ double, double }) {{.*}}) + // 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]) {{.*}}) @@ -189,6 +213,9 @@ pub extern "C" fn cplx_f64(x: Complex) -> Complex { 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]) {{.*}}) + // 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]) {{.*}}) @@ -212,17 +239,19 @@ pub extern "C" fn cplx_i8(x: Complex) -> Complex { // 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 {{.*}}) + // 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 }) {{.*}}) + // 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{{.*}} {{.*}} i64 @cplx_i8(i64{{.*}}) - // SPARC: define{{.*}} { i8, i8 } @cplx_i8(ptr {{.*}} byval({ i8, i8 }) {{.*}}) + // 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]) {{.*}}) @@ -246,17 +275,19 @@ pub extern "C" fn cplx_i16(x: Complex) -> Complex { // 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 {{.*}}) + // 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([4 x i8]) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} {{.*}} i64 @cplx_i16(i64{{.*}}) - // SPARC: define{{.*}} { i16, i16 } @cplx_i16(ptr {{.*}} byval({ i16, i16 }) {{.*}}) + // 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]) {{.*}}) @@ -280,17 +311,19 @@ pub extern "C" fn cplx_i32(x: Complex) -> Complex { // 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([8 x i8]) {{.*}}, ptr {{.*}}) // SPARC64: define{{.*}} i64 @cplx_i32(i64 {{.*}}) - // SPARC: define{{.*}} { i32, i32 } @cplx_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]) {{.*}}) @@ -314,17 +347,19 @@ pub extern "C" fn cplx_i64(x: Complex) -> Complex { // 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] {{.*}}) + // 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 }) {{.*}}) + // 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([16 x i8]) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} { i64, i64 } @cplx_i64(i64 {{.*}}, i64 {{.*}}) - // SPARC: define{{.*}} { i64, i64 } @cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // 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]) {{.*}}) @@ -353,17 +388,19 @@ 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] {{.*}}) + // MIPS64: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) // MIPS64EL: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) - // MIPS: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i32 {{.*}}, i32 {{.*}}, i32 {{.*}}, i32 {{.*}}) + // MIPS: define{{.*}} { i64, i64 } @wrapper_cplx_i64([4 x i32] {{.*}}) + // MIPSEL: define{{.*}} { i64, i64 } @wrapper_cplx_i64([4 x i32] {{.*}}) // NVPTX: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) // POWERPC64: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) // POWERPC64LE: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) - // POWERPC: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret({ i64, i64 }) {{.*}}, ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // POWERPC: define{{.*}} [4 x i32] @wrapper_cplx_i64([4 x i32] {{.*}}) // RISCV32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // RISCV64: define{{.*}} [2 x i64] @wrapper_cplx_i64([2 x i64] {{.*}}) // S390X: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) - // SPARC64: define{{.*}} { i64, i64 } @wrapper_cplx_i64(i64 {{.*}}, i64 {{.*}}) - // SPARC: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval({ i64, i64 }) {{.*}}) + // SPARC64: define { i64, i64 } @wrapper_cplx_i64({ i64, i64 } {{.*}}) + // SPARC: define{{.*}} { i64, i64 } @wrapper_cplx_i64(ptr {{.*}} byval([16 x i8]) {{.*}}) // WASM32: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WASM64: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}}) // WIN32_GNU: define{{.*}} void @wrapper_cplx_i64(ptr {{.*}} sret([16 x i8]) {{.*}}, ptr {{.*}} byval([16 x i8]) {{.*}}) diff --git a/tests/codegen-llvm/f128-sparc-callconv.rs b/tests/codegen-llvm/f128-sparc-callconv.rs new file mode 100644 index 0000000000000..d7fd1a0ff4953 --- /dev/null +++ b/tests/codegen-llvm/f128-sparc-callconv.rs @@ -0,0 +1,53 @@ +//! Verify that Rust implements the expected calling convention for `f128` + +//@ add-minicore +//@ revisions: sparc-none sparc-linux +//@ [sparc-none] compile-flags: --target sparc-unknown-none-elf +//@ [sparc-linux] compile-flags: --target sparc-unknown-linux-gnu +//@ compile-flags: -Copt-level=3 +//@ needs-llvm-components: sparc + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core, lang_items, f128)] + +extern crate minicore; + +extern "C" { + fn extern_call(arg0: f128); + fn extern_ret() -> f128; +} + +#[no_mangle] +pub extern "C" fn pass(_arg0: u32, arg1: f128) { + // CHECK-LABEL: @pass( + // an f128 is passed via the stack + // CHECK-SAME: ptr {{.*}} + // CHECK: call void @extern_call + unsafe { extern_call(arg1) }; +} + +// Check that we produce the correct return ABI +#[no_mangle] +pub extern "C" fn ret(_arg0: u32, arg1: f128) -> f128 { + // CHECK-LABEL: @ret( + // and an f128 is returned via the stack + // CHECK-SAME: sret([16 x i8]) + // CHECK: %0 = load fp128, ptr %arg1 + // CHECK-NEXT: store fp128 %0, ptr %_0 + // CHECK-NEXT: ret void + arg1 +} + +// Check that we consume the correct return ABI +#[no_mangle] +pub extern "C" fn forward(dst: *mut f128) { + // CHECK-LABEL: @forward + // CHECK-SAME: ptr{{.*}} %dst) + // without optimizatons, an intermediate alloca is used + // CHECK: call void @extern_ret + // CHECK: store fp128 + // CHECK: ret void + unsafe { *dst = extern_ret() }; +} 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/codegen-llvm/repr/transparent-sparc.rs b/tests/codegen-llvm/repr/transparent-sparc.rs new file mode 100644 index 0000000000000..a9532a966e170 --- /dev/null +++ b/tests/codegen-llvm/repr/transparent-sparc.rs @@ -0,0 +1,116 @@ +//@ add-minicore +//@ revisions: sparc-none sparc-linux +//@ [sparc-none] compile-flags: --target sparc-unknown-none-elf +//@ [sparc-linux] compile-flags: --target sparc-unknown-linux-gnu +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes +//@ needs-llvm-components: sparc + +// See ./transparent.rs + +#![feature(no_core, lang_items, transparent_unions)] +#![crate_type = "lib"] +#![no_std] +#![no_core] + +extern crate minicore; +use minicore::*; +impl Copy for BigS {} +impl Copy for BigU {} + +#[repr(C)] +pub struct BigS([u32; 16]); + +#[repr(transparent)] +pub struct TsBigS(BigS); + +#[repr(transparent)] +pub union TuBigS { + field: BigS, +} + +#[repr(transparent)] +pub enum TeBigS { + Variant(BigS), +} + +// CHECK: define{{.*}}void @test_BigS(ptr [[BIGS_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGS_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_BigS(_: BigS) -> BigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { + loop {} +} + +#[repr(C)] +pub union BigU { + foo: [u32; 16], +} + +#[repr(transparent)] +pub struct TsBigU(BigU); + +#[repr(transparent)] +pub union TuBigU { + field: BigU, +} + +#[repr(transparent)] +pub enum TeBigU { + Variant(BigU), +} + +// CHECK: define{{.*}}void @test_BigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_BigU(_: BigU) -> BigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TsBigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr +// CHECK-NOT: byval +// CHECK-SAME: %{{[0-9a-z_]+}}) +#[no_mangle] +pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { + loop {} +} diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index 6d2ac90c0c975..2ed9ffdf791f6 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -60,7 +60,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.rs b/tests/ui/abi/c-zst.rs index 22cb3f98f28dc..973f5f0002f25 100644 --- a/tests/ui/abi/c-zst.rs +++ b/tests/ui/abi/c-zst.rs @@ -15,9 +15,9 @@ extern "C" fn(i32, (), i32); ``` */ -/* - * ZST IN "C" IS ZERO-SIZED - */ +// +// ZST IN "C" IS ZERO-SIZED +// //@ revisions: aarch64-darwin //@[aarch64-darwin] compile-flags: --target aarch64-apple-darwin @@ -27,10 +27,9 @@ extern "C" fn(i32, (), i32); //@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64-linux] needs-llvm-components: x86 - -/* - * ZST IN "C" IS PASS-BY-POINTER - */ +// +// ZST IN "C" IS PASS-BY-POINTER +// // according to the SRV4 ABI, an aggregate is always passed in registers, // and it so happens the GCC extension for ZSTs considers them as structs. @@ -42,7 +41,11 @@ extern "C" fn(i32, (), i32); //@[s390x-linux] compile-flags: --target s390x-unknown-linux-gnu //@[s390x-linux] needs-llvm-components: systemz -//@ revisions: sparc64-linux +//@ revisions: sparc-none sparc-linux sparc64-linux +//@[sparc-none] compile-flags: --target sparc-unknown-none-elf +//@[sparc-none] needs-llvm-components: sparc +//@[sparc-linux] compile-flags: --target sparc-unknown-linux-gnu +//@[sparc-linux] needs-llvm-components: sparc //@[sparc64-linux] compile-flags: --target sparc64-unknown-linux-gnu //@[sparc64-linux] needs-llvm-components: sparc @@ -53,7 +56,6 @@ extern "C" fn(i32, (), i32); //@[x86_64-pc-windows-gnu] needs-llvm-components: x86 //@ ignore-backends: gcc - #![feature(no_core, rustc_attrs)] #![no_core] #![crate_type = "lib"] diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.sparc-linux.stderr b/tests/ui/abi/c-zst.sparc-linux.stderr new file mode 100644 index 0000000000000..302ffe1efc8b8 --- /dev/null +++ b/tests/ui/abi/c-zst.sparc-linux.stderr @@ -0,0 +1,80 @@ +error: fn_abi_of(pass_zst) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, + arg_ext: None, + pointee_size: Size(0 bytes), + pointee_align: Some( + Align(1 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/c-zst.rs:67:1 + | +LL | extern "C" fn pass_zst(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/c-zst.sparc-none.stderr b/tests/ui/abi/c-zst.sparc-none.stderr new file mode 100644 index 0000000000000..302ffe1efc8b8 --- /dev/null +++ b/tests/ui/abi/c-zst.sparc-none.stderr @@ -0,0 +1,80 @@ +error: fn_abi_of(pass_zst) = FnAbi { + args: [ + ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Indirect { + attrs: ArgAttributes { + regular: CapturesAddress | NoAlias | NonNull | NoUndef | NoFree, + arg_ext: None, + pointee_size: Size(0 bytes), + pointee_align: Some( + Align(1 bytes), + ), + }, + meta_attrs: None, + on_stack: false, + }, + }, + ], + ret: ArgAbi { + layout: TyAndLayout { + ty: (), + layout: Layout { + size: Size(0 bytes), + align: AbiAlign { + abi: $SOME_ALIGN, + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + in_memory_order: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: $SOME_ALIGN, + randomization_seed: 0, + }, + }, + mode: Ignore, + }, + c_variadic: false, + fixed_count: 1, + conv: C, + can_unwind: false, + } + --> $DIR/c-zst.rs:67:1 + | +LL | extern "C" fn pass_zst(_: ()) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 6d2ac90c0c975..2ed9ffdf791f6 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -60,7 +60,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index edea2d5772280..302ffe1efc8b8 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -71,7 +71,7 @@ error: fn_abi_of(pass_zst) = FnAbi { conv: C, can_unwind: false, } - --> $DIR/c-zst.rs:65:1 + --> $DIR/c-zst.rs:67:1 | LL | extern "C" fn pass_zst(_: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^