diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 5c6715c43caa7..ccf33abac712f 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -27,9 +27,9 @@ impl RegKind { match primitive { Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer, Primitive::Float(Float::PpcF128) => RegKind::PpcF128, - Primitive::Float(Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::F128) => { - RegKind::Float - } + Primitive::Float( + Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::X87F80 | Float::F128, + ) => RegKind::Float, } } } @@ -60,6 +60,7 @@ impl Reg { reg_ctor!(f16, Float, 16); reg_ctor!(f32, Float, 32); reg_ctor!(f64, Float, 64); + reg_ctor!(x87_f80, Float, 80); reg_ctor!(f128, Float, 128); /// A vector of the given size with an unknown (and irrelevant) element type. @@ -86,6 +87,7 @@ impl Reg { 16 => dl.f16_align, 32 => dl.f32_align, 64 => dl.f64_align, + 80 => dl.x87_f80_align, 128 => dl.f128_align, _ => panic!("unsupported float: {self:?}"), }, diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 58d20b9bd3532..4f5b26dfe5864 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -122,6 +122,7 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display { fn is_unit(this: TyAndLayout<'a, Self>) -> bool; fn is_transparent(this: TyAndLayout<'a, Self>) -> bool; fn is_complex_number_lang_item(this: TyAndLayout<'a, Self>, cx: &C) -> bool; + fn is_x87_f80_lang_item(this: TyAndLayout<'a, Self>, cx: &C) -> bool; fn is_scalable_vector(this: TyAndLayout<'a, Self>) -> bool; /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool; @@ -232,6 +233,17 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_scalable_vector(self) } + /// Returns `true` if this type is the `x87_f80` lang item. + /// + /// Note that this function returns `false` for the `x87_f80_raw` lang item. + pub fn is_x87_f80(self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C> + Copy, + { + // A 1-ZST is never x87_f80. + Ty::is_x87_f80_lang_item(self.peel_transparent_wrappers_from_non_1zst(cx), cx) + } + /// If this method returns `true`, then this type should always have a `PassMode` of /// `Indirect { mode: IndirectMode::Pointer, .. }` when being used as the argument type of a /// function with a non-Rustic ABI (this is true for structs annotated with the @@ -339,6 +351,10 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { let component = complex.field(cx, 0).peel_transparent_wrappers_from_non_1zst(cx); + if component.is_x87_f80(cx) { + return Some(Numeric::Float(Float::X87F80)); + } + let BackendRepr::Scalar(scalar) = component.backend_repr else { return None; }; @@ -353,6 +369,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Primitive::Float( float @ (Float::F16 | Float::F32 | Float::F64 | Float::F128 | Float::PpcF128), ) => Some(Numeric::Float(float)), + Primitive::Float(Float::X87F80) => None, // x87_f80_raw has incorrect alignment. Primitive::Pointer(..) | Primitive::Float(Float::F16B) => None, } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 48c0c830325c4..b7795af08a198 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -327,6 +327,7 @@ pub struct TargetDataLayout { pub f16_align: Align, pub f32_align: Align, pub f64_align: Align, + pub x87_f80_align: Align, pub f128_align: Align, pub aggregate_align: Align, @@ -367,6 +368,7 @@ impl Default for TargetDataLayout { f16_align: align(16), f32_align: align(32), f64_align: align(64), + x87_f80_align: align(128), f128_align: align(128), aggregate_align: align(8), vector_align: vec![ @@ -510,6 +512,7 @@ impl TargetDataLayout { ["f16", a @ ..] => dl.f16_align = parse_align_seq(a, "f16")?, ["f32", a @ ..] => dl.f32_align = parse_align_seq(a, "f32")?, ["f64", a @ ..] => dl.f64_align = parse_align_seq(a, "f64")?, + ["f80", a @ ..] => dl.x87_f80_align = parse_align_seq(a, "f80")?, ["f128", a @ ..] => dl.f128_align = parse_align_seq(a, "f128")?, [p, s, a @ ..] if p.starts_with("p") => { let mut p = p.strip_prefix('p').unwrap(); @@ -1427,10 +1430,22 @@ pub enum Float { F16B, F32, F64, + /// `x87_f80`. This is not a builtin type in Rust (it is exposed as a lang item), + /// but it is a builtin type in LLVM so needs to be explicitly represented + /// in the backend. + /// + /// Here we define the 80-bit scalar value that corresponds to the `x87_f80_raw` lang item. + /// In `core::arch::{x86, x86_64}` this type is wrapped by the `x87_f80` lang item that adds + /// the proper alignment and bumps the size to either 12 or 16 bytes depending on the target. + /// + /// Only used on x86 and x86_64 targets. + X87F80, F128, /// `ppcf128`. This is not a builtin type in Rust (it is exposed as a lang item), /// but it is a builtin type in LLVM so needs to be explicitly represented /// in the backend. + /// + /// Only used on powerpc and powerpc64 targets. PpcF128, } @@ -1443,6 +1458,7 @@ impl Float { F16B => Size::from_bits(16), F32 => Size::from_bits(32), F64 => Size::from_bits(64), + X87F80 => Size::from_bits(80), F128 => Size::from_bits(128), PpcF128 => Size::from_bits(128), } @@ -1456,6 +1472,9 @@ impl Float { F16 | F16B => dl.f16_align, F32 => dl.f32_align, F64 => dl.f64_align, + // Use an alignment of 1 for x87_f80_raw, + // so that its size really is 80 bits in memory. + X87F80 => Align::ONE, F128 => dl.f128_align, PpcF128 => dl.f128_align, }) @@ -1469,6 +1488,7 @@ impl Float { F16B => "f16b", F32 => "f32", F64 => "f64", + X87F80 => "x87_f80", F128 => "f128", PpcF128 => "ppcf128", } @@ -1496,9 +1516,9 @@ impl Numeric { match self { Numeric::Int(_, _) => RegKind::Integer, Numeric::Float(Float::PpcF128) => RegKind::PpcF128, - Numeric::Float(Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::F128) => { - RegKind::Float - } + Numeric::Float( + Float::F16 | Float::F16B | Float::F32 | Float::F64 | Float::X87F80 | Float::F128, + ) => RegKind::Float, } } } diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index d715a6d3af8c5..428203a4a3597 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -238,6 +238,8 @@ language_item_table! { Complex, sym::complex, complex, Target::Struct, GenericRequirement::Exact(1); PpcF128, sym::ppcf128, ppcf128, Target::Struct, GenericRequirement::None; + X87F80, sym::x87_f80, x87_f80, Target::Struct, GenericRequirement::None; + X87F80Raw, sym::x87_f80_raw, x87_f80_raw, Target::Struct, GenericRequirement::None; Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index d59fb0f93ed4f..dcd367a4ba97a 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -38,6 +38,7 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type { Float::F16B => bug!("f16b is not supported by the Cranelift codegen backend"), Float::F32 => types::F32, Float::F64 => types::F64, + Float::X87F80 => bug!("cranelift does not support x87 floats"), Float::F128 => types::F128, Float::PpcF128 => bug!("cranelift does not support powerpc"), }, diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index d3e2595ff701a..6fdfa5c682aae 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -308,6 +308,12 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> { bug!("unsupported ppcf128 type") } + fn type_x87_f80(&self) -> Type<'gcc> { + // FIXME(x87_f80): The GCC gcc_jit_types enum (which CType is based on) does not + // have __float80. It does have long double but it may not correspond to x87 f80. + bug!("unsupported x87_f80 type") + } + fn type_func(&self, params: &[Type<'gcc>], return_type: Type<'gcc>) -> Type<'gcc> { self.context.new_function_pointer_type(None, return_type, params, false) } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index e50fc4bea82fd..dcdc52ea787a3 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -144,6 +144,7 @@ impl LlvmType for Reg { 16 => cx.type_f16(), 32 => cx.type_f32(), 64 => cx.type_f64(), + 80 => cx.type_x87_f80(), 128 => cx.type_f128(), _ => bug!("unsupported float: {:?}", self), }, @@ -167,6 +168,7 @@ impl LlvmType for Reg { Float::F64 => cx.type_f64(), Float::F128 => cx.type_f128(), Float::PpcF128 => bug!("ppcf128 is not a valid vector element type"), + Float::X87F80 => bug!("x87_f80 is not a valid vector element type"), }, Primitive::Pointer(_) => cx.type_ptr(), }; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 212e2886fe019..5316e53e9647c 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -301,6 +301,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Primitive::Float(Float::PpcF128) => { // Supported on powerpc and powerpc64 targets. } + Primitive::Float(Float::X87F80) => { + // FIXME(x87_f80) we should support this. + bug!("the va_arg intrinsic does not currently support `x87_f80`") + } } emit_va_arg(self, args[0], result_layout) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7e6c4869f0b26..5ca307791981e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -958,6 +958,7 @@ unsafe extern "C" { // Operations on non-IEEE real types pub(crate) fn LLVMBFloatTypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; + pub(crate) fn LLVMX86FP80TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; pub(crate) fn LLVMPPCFP128TypeInContext<'ll>(C: Context<'ll>) -> &'ll Type; // Operations on function types diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 2143f2b7aec37..0050cbc110cf8 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -238,6 +238,10 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { unsafe { llvm::LLVMPPCFP128TypeInContext(self.llcx()) } } + fn type_x87_f80(&self) -> &'ll Type { + unsafe { llvm::LLVMX86FP80TypeInContext(self.llcx()) } + } + fn type_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, FALSE) } } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 0624d8db460c0..08351c87cdd06 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use rustc_abi::Primitive::{Float, Int, Pointer}; -use rustc_abi::{Align, BackendRepr, FieldsShape, Scalar, Size, Variants}; +use rustc_abi::{Align, BackendRepr, FieldsShape, Scalar, Size, TyAbiInterface, Variants}; use rustc_codegen_ssa::traits::*; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; @@ -75,6 +75,11 @@ fn uncached_llvm_type<'a, 'tcx>( BackendRepr::Memory { .. } | BackendRepr::ScalarPair { .. } => {} } + // Map `core::arch::{x86, x86_64}::x87_f80` to LLVM's `x86_fp80`. + if Ty::is_x87_f80_lang_item(layout, cx) { + return cx.type_x87_f80(); + } + let name = match layout.ty.kind() { // FIXME(eddyb) producing readable type names for trait objects can result // in problematically distinct types due to HRTB and subtyping (see #47638). diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index c74ea60ac11f3..a48a5b2eea461 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -95,7 +95,7 @@ fn get_param_type_alignment<'ll, 'tcx>( Integer::I128 => return Align::EIGHT, }, Primitive::Float(float) => match float { - Float::F16 | Float::F16B | Float::F32 => unreachable!(), + Float::F16 | Float::F16B | Float::F32 | Float::X87F80 => unreachable!(), Float::F64 | Float::F128 => { /* fall through */ } Float::PpcF128 => { return if is_powerpc_softfloat_abi(&bx.cx.tcx.sess.target) { @@ -491,6 +491,9 @@ fn emit_s390x_va_arg<'ll, 'tcx>( Primitive::Float(Float::PpcF128) => { bug!("`ppcf128` use in varadics unsupported on s390x") } + Primitive::Float(Float::X87F80) => { + bug!("`x87_f80` use in varadics unsupported on s390x") + } }, _ => false, diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 691d573739364..06d385998a9dd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -514,6 +514,7 @@ fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str Float::F16B => bug!("`f16b` unsupported on wasm"), Float::F16 | Float::F32 => "f32", Float::F64 => "f64", + Float::X87F80 => bug!("`x87_f80` unsupported on wasm"), Float::F128 => "i64, i64", Float::PpcF128 => bug!("`ppcf128` unsupported on wasm"), }, diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 11dad80fb3417..401491251c5d3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -23,6 +23,7 @@ pub trait BaseTypeCodegenMethods: BackendTypes { fn type_f64(&self) -> Self::Type; fn type_f128(&self) -> Self::Type; fn type_ppcf128(&self) -> Self::Type; + fn type_x87_f80(&self) -> Self::Type; fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::FunctionSignature; fn type_kind(&self, ty: Self::Type) -> TypeKind; @@ -71,6 +72,7 @@ pub trait DerivedTypeCodegenMethods<'tcx>: F16B => self.type_f16b(), F32 => self.type_f32(), F64 => self.type_f64(), + X87F80 => self.type_x87_f80(), F128 => self.type_f128(), PpcF128 => self.type_ppcf128(), } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 998fcf77f7b1f..12aa8d42a3f3d 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -144,6 +144,11 @@ impl abi::Float { ), F32 => tcx.types.f32, F64 => tcx.types.f64, + X87F80 => Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::X87F80Raw, DUMMY_SP)), + ty::List::empty(), + ), F128 => tcx.types.f128, PpcF128 => Ty::new_adt( tcx, @@ -1255,6 +1260,12 @@ where cx.tcx().is_lang_item(def.did(), LangItem::Complex) } + /// Is this type `core::arch::{x86, x86_64}::x87_f80`? + fn is_x87_f80_lang_item(this: TyAndLayout<'tcx>, cx: &C) -> bool { + let Some(def) = this.ty.ty_adt_def() else { return false }; + cx.tcx().is_lang_item(def.did(), LangItem::X87F80) + } + fn is_scalable_vector(this: TyAndLayout<'tcx>) -> bool { this.ty.is_scalable_vector() } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 70d48c3782b7d..d98c12d0fe9f9 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -538,6 +538,7 @@ pub enum FloatLength { F16B, F32, F64, + X87F80, F128, PpcF128, } @@ -560,6 +561,7 @@ impl FloatLength { FloatLength::F16 | FloatLength::F16B => 16, FloatLength::F32 => 32, FloatLength::F64 => 64, + FloatLength::X87F80 => 80, FloatLength::F128 => 128, FloatLength::PpcF128 => 128, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index a5e7302e9a4c7..d1b4391893e6a 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -502,6 +502,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Float { rustc_abi::Float::F16B => FloatLength::F16B, rustc_abi::Float::F32 => FloatLength::F32, rustc_abi::Float::F64 => FloatLength::F64, + rustc_abi::Float::X87F80 => FloatLength::X87F80, rustc_abi::Float::F128 => FloatLength::F128, rustc_abi::Float::PpcF128 => FloatLength::PpcF128, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c3e275971056d..e4ee202ac0ab5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2433,6 +2433,8 @@ symbols! { x86, x86_64, x86_amx_intrinsics, + x87_f80, + x87_f80_raw, x87_reg, x87_target_feature, xcoff, diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index 3cce9095bd847..1534313532f02 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -17,7 +17,7 @@ where Numeric::Int(Integer::I128, _) => false, Numeric::Float(Float::F32 | Float::F64) => true, Numeric::Float(Float::F16 | Float::F128) => false, - Numeric::Float(Float::F16B | Float::PpcF128) => bug!("unsupported on mips"), + Numeric::Float(Float::F16B | Float::X87F80 | Float::PpcF128) => bug!("unsupported on mips"), }; if let Some(component) = ret.layout.complex_number(cx) diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 196d68389ac72..b768ae5fcceb7 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -36,6 +36,7 @@ where Float::F64 => Some(Reg::f64()), Float::F128 => Some(Reg::f128()), Float::F16B => unreachable!("`f16b` unsupported on mips64"), + Float::X87F80 => unreachable!("`x87_f80` unsupported on mips64"), Float::PpcF128 => unreachable!("`ppcf128` unsupported on mips64"), } } @@ -142,8 +143,9 @@ where match component { Numeric::Float(Float::F16B) => unreachable!("Complex is not C-compatible"), - Numeric::Float(Float::F16) => unreachable!("not supported on mips64"), - Numeric::Float(Float::PpcF128) => unreachable!("not supported on mips64"), + Numeric::Float(Float::F16 | Float::X87F80 | Float::PpcF128) => { + unreachable!("not supported on mips64") + } Numeric::Float(Float::F32 | Float::F64) => { // Only pass a Complex/Complex in FPRs when two argument slots are free. if curr_offset.bytes() / slot.bytes() + 2 <= NUM_ARG_SLOTS { diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index bfe7f0cca3cc7..99187a1321039 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -59,6 +59,7 @@ fn classify<'a, Ty, C>( // Match LLVM by passing `f16` in integer registers. } Float::F16B => unreachable!("`f16b` unsupported on sparc64"), + Float::X87F80 => unreachable!("`x87_f80` unsupported on sparc64"), Float::PpcF128 => unreachable!("`ppcf128` unsupported on sparc64"), } } else { diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index fdfcbec18eb95..363c07ddc5c11 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -55,6 +55,10 @@ where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { + if ret.layout.is_x87_f80(cx) { + ret.cast_to(Reg::x87_f80()); + return; + } if ret.layout.is_aggregate() && ret.layout.is_sized() { // Returning a structure. Most often, this will use // a hidden first argument. On some platforms, though, @@ -107,7 +111,11 @@ where let align_4 = Align::from_bytes(4).unwrap(); let align_16 = Align::from_bytes(16).unwrap(); - if arg.layout.is_aggregate() { + if arg.layout.is_x87_f80(cx) { + // This branch and cast are not technically required, but it makes the IR more readable. + arg.cast_to(Reg::x87_f80()); + return; + } else if arg.layout.is_aggregate() { // We need to compute the alignment of the `byval` argument. The rules can be found in // `X86_32ABIInfo::getTypeStackAlignInBytes` in Clang's `TargetInfo.cpp`. Summarized // here, they are: @@ -232,6 +240,7 @@ pub(crate) fn fill_inregs<'a, Ty, C>( PassMode::Ignore | PassMode::Indirect { attrs: _, address_space: _, mode: _ } => { continue; } + PassMode::Cast { .. } if arg.layout.is_x87_f80(cx) => continue, PassMode::Direct(ref mut attrs) => attrs, PassMode::Pair(..) | PassMode::IndirectUnsized { attrs: _, meta_attrs: _ } diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 98f3a626155a8..bebc47135ce34 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -2,7 +2,7 @@ // https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp use rustc_abi::{ - BackendRepr, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, TyAndLayout, + BackendRepr, Float, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface, TyAndLayout, Variants, }; @@ -10,13 +10,34 @@ use crate::callconv::{ArgAbi, CastTarget, FnAbi}; use crate::spec::HasTargetSpec; /// Classification of "eightbyte" components. -// N.B., the order of the variants is from general to specific, -// such that `unify(a, b)` is the "smaller" of `a` and `b`. +/// +/// N.B., the order of the variants is from general to specific, +/// such that `merge(a, b)` is the "smaller" of `a` and `b`, unless it is an error. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] enum Class { Int, Sse, SseUp, + X87, + X87Up, +} + +impl Class { + fn upper_half(self) -> Class { + match self { + Class::Sse | Class::SseUp => Class::SseUp, + Class::X87 | Class::X87Up => Class::X87Up, + Class::Int => Class::Int, + } + } + + fn merge(self, other: Self) -> Result { + match (self, other) { + (Class::X87 | Class::X87Up, Class::Sse | Class::SseUp) => Err(Memory), + (Class::Sse | Class::SseUp, Class::X87 | Class::X87Up) => Err(Memory), + _ => Ok(Ord::min(self, other)), + } + } } #[derive(Clone, Copy, Debug)] @@ -54,6 +75,7 @@ where let mut c = match layout.backend_repr { BackendRepr::Scalar(scalar) => match scalar.primitive() { Primitive::Int(..) | Primitive::Pointer(_) => Class::Int, + Primitive::Float(Float::X87F80) => Class::X87, Primitive::Float(_) => Class::Sse, }, @@ -85,13 +107,13 @@ where let first = (off.bytes() / 8) as usize; let last = ((off.bytes() + layout.size.bytes() - 1) / 8) as usize; for cls in &mut cls[first..=last] { - *cls = Some(cls.map_or(c, |old| old.min(c))); + *cls = match cls { + Some(old) => Some(Class::merge(*old, c)?), + None => Some(c), + }; - // Everything after the first Sse "eightbyte" - // component is the upper half of a register. - if c == Class::Sse { - c = Class::SseUp; - } + // Everything after the first "eightbyte" component is the upper half of a register. + c = c.upper_half(); } Ok(()) @@ -127,6 +149,17 @@ where } } + // If X87UP is not preceded by X87, the whole argument is passed in memory. + if let [Some(Class::X87Up), ..] = cls { + return Err(Memory); + } + + for [prev, curr] in cls[..n].array_windows() { + if *curr == Some(Class::X87Up) && *prev != Some(Class::X87) { + return Err(Memory); + } + } + Ok(cls) } @@ -153,6 +186,13 @@ fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option { + let consumed = + 1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::X87Up)).count(); + assert_eq!(consumed, 2); + *i += consumed; + Some(Reg::x87_f80()) + } c => unreachable!("reg_component: unhandled class {:?}", c), } } @@ -193,10 +233,21 @@ where arg.make_indirect(); return; } + if is_arg && arg.layout.is_x87_f80(cx) { + // This branch and cast are not technically required, but it makes the IR more readable. + arg.cast_to(Reg::x87_f80()); + return; + } let mut cls_or_mem = classify_arg(cx, arg); if is_arg { if let Ok(cls) = cls_or_mem { + // An argument of class X87 is passed in memory. + if cls.contains(&Some(Class::X87)) { + std::assert_matches!(cls, [Some(Class::X87), Some(Class::X87Up), ..]); + cls_or_mem = Err(Memory); + } + let mut needed_int = 0; let mut needed_sse = 0; for c in cls { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 83f9f6d2f87f0..f4d52e5a46925 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -1,12 +1,11 @@ use std::range::{RangeFrom, RangeToInclusive}; -use rustc_abi as abi; use rustc_abi::Integer::{I8, I32}; use rustc_abi::Primitive::{self, Float, Int, Pointer}; use rustc_abi::{ - AddressSpace, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, HasDataLayout, Layout, - LayoutCalculatorError, LayoutData, Niche, NicheOptimizations, ReprOptions, Scalar, Size, - StructKind, TagEncoding, VariantIdx, Variants, WrappingRange, + self as abi, AbiAlign, AddressSpace, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, + HasDataLayout, Layout, LayoutCalculatorError, LayoutData, Niche, NicheOptimizations, + ReprOptions, Scalar, Size, StructKind, TagEncoding, VariantIdx, Variants, WrappingRange, }; use rustc_attr_ir::find_attr; use rustc_attr_ir::lang_items::LangItem; @@ -26,6 +25,7 @@ use rustc_middle::ty::{ use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use rustc_span::{Symbol, bug, sym}; use rustc_structures::Limit; +use rustc_target::spec::{Arch, HasTargetSpec}; use tracing::{debug, instrument}; use crate::diagnostics::NonPrimitiveSimdType; @@ -824,13 +824,37 @@ fn layout_of_uncached<'tcx>( layout.backend_repr = BackendRepr::Scalar(bfloat); } + let arch = &tcx.target_spec().arch; + if tcx.is_lang_item(def.did(), LangItem::PpcF128) { + std::assert_matches!(arch, Arch::PowerPC | Arch::PowerPC64); + let ppcf128 = scalar_unit(Primitive::Float(abi::Float::PpcF128)); assert_eq!(layout.size, abi::Float::PpcF128.size()); layout.align = abi::Float::PpcF128.align(cx); layout.backend_repr = BackendRepr::Scalar(ppcf128); } + if tcx.is_lang_item(def.did(), LangItem::X87F80Raw) { + std::assert_matches!(arch, Arch::X86 | Arch::X86_64); + + // The lang item has no fields: give it a scalar layout. + return Ok(scalar(Primitive::Float(abi::Float::X87F80))); + } + + if tcx.is_lang_item(def.did(), LangItem::X87F80) { + std::assert_matches!(arch, Arch::X86 | Arch::X86_64); + + // A wrapper around `x87_f80_raw` that adds the target-specific alignment. + // It is an ordinary aggregate with trailing padding. + assert_eq!(layout.size, abi::Float::X87F80.size()); + layout.align = AbiAlign::new(cx.data_layout().x87_f80_align); + layout.unadjusted_abi_align = layout.align.abi; + layout.size = layout.size.align_to(layout.align.abi); + layout.backend_repr = BackendRepr::Memory { sized: true }; + layout.largest_niche = None; + } + tcx.mk_layout(layout) } diff --git a/library/core/src/num/imp/mod.rs b/library/core/src/num/imp/mod.rs index e04f3cd93ba5f..843a800267ffb 100644 --- a/library/core/src/num/imp/mod.rs +++ b/library/core/src/num/imp/mod.rs @@ -13,6 +13,7 @@ pub mod fmt; // Public for core_arch. pub mod ppcf128; +pub mod x87_f80; pub(crate) mod int_bits; pub(crate) mod int_log10; diff --git a/library/core/src/num/imp/x87_f80.rs b/library/core/src/num/imp/x87_f80.rs new file mode 100644 index 0000000000000..4d0c64eaf8ab0 --- /dev/null +++ b/library/core/src/num/imp/x87_f80.rs @@ -0,0 +1,35 @@ +/// The raw x87 80-bit extended-precision floating-point type. +/// +/// This type has a size of 80 bits, and corresponds to the store size (what a load/store touches) +/// of x87_f80. This is different to what LLVM calls the alloc size, the number of bytes used to +/// calculate field offsets or the size of an alloca. +/// +/// The `x87_f80` wrapper type below corresponds to the alloc size: it applies the target-specific +/// alignment, raising the total size to 12 or 16. +/// +/// NOTE: this type MUST NOT be used anywhere except in the x87_f80 wrapper below. +#[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), lang = "x87_f80_raw")] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy)] +struct x87_f80_raw {} + +/// The x87 80-bit extended-precision floating-point lang item. +/// +/// Here we define just the lang item, the public-facing type is defined +/// in core_arch. It transparently wraps this definition. +/// +/// This x87_f80 type should only be used on x86 and x86_64 targets, but is always defined +/// for the purpose of doc builds, specifically doc builds in the stdarch repo. +#[doc(hidden)] +#[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), lang = "x87_f80")] +#[unstable(feature = "num_internals", issue = "none")] +#[allow(non_camel_case_types, dead_code)] +#[derive(Clone, Copy)] +pub struct x87_f80(x87_f80_raw); + +#[unstable(feature = "num_internals", issue = "none")] +impl crate::fmt::Debug for x87_f80 { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + f.debug_tuple("x87_f80").finish() + } +} diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index c7e2d93b886a7..c2357eaab5aa0 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -46,6 +46,7 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(powerpc_ppcf128) )] +#![cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), feature(x87_f80))] #![cfg_attr( test, feature( diff --git a/library/stdarch/crates/core_arch/src/x86/mod.rs b/library/stdarch/crates/core_arch/src/x86/mod.rs index e6875a628fbf0..301683f368b20 100644 --- a/library/stdarch/crates/core_arch/src/x86/mod.rs +++ b/library/stdarch/crates/core_arch/src/x86/mod.rs @@ -5,6 +5,79 @@ use crate::mem::transmute; #[macro_use] mod macros; +/// The x87 80-bit extended-precision floating-point type. +/// +/// FIXME: extend the documentation. +#[doc(alias = "long double")] +#[doc(alias = "x86_fp80")] +#[doc(alias = "__float80")] +#[unstable(feature = "x87_f80", issue = "163982")] +#[allow(non_camel_case_types)] +#[doc(cfg(any(target_arch = "x86", target_arch = "x86_64")))] +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct x87_f80(crate::num::imp::x87_f80::x87_f80); + +const _: () = assert!( + size_of::() + == cfg_select! { + target_arch = "x86" => cfg_select! { + target_vendor = "apple" => 16, + target_env = "msvc" => 16, + _ => 12, + }, + target_arch = "x86_64" => 16, + _ => size_of::(), // for doc builds + } +); + +const _: () = assert!( + align_of::() + == cfg_select! { + target_arch = "x86" => cfg_select! { + target_vendor = "apple" => 16, + target_env = "msvc" => 16, + _ => 4, + }, + target_arch = "x86_64" => 16, + _ => align_of::(), // for doc builds + } +); + +#[unstable(feature = "x87_f80", issue = "163982")] +impl crate::fmt::Debug for x87_f80 { + #[inline] + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + let mut bytes = [0u8; 16]; + bytes[..10].copy_from_slice(&self.to_ne_bytes()); + write!(f, "x87_f80({:#022x})", u128::from_le_bytes(bytes)) + } +} + +impl x87_f80 { + /// Returns the memory representation of this floating point number as a byte array in + /// native byte order. + #[unstable(feature = "x87_f80", issue = "163982")] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; 10] { + // SAFETY: the first 10 bytes are initialized. + unsafe { (&raw const self).cast::<[u8; 10]>().read() } + } + + /// Creates a floating point value from its representation as a byte array in native endian. + #[unstable(feature = "x87_f80", issue = "163982")] + #[inline] + pub const fn from_ne_bytes(bytes: [u8; 10]) -> Self { + // SAFETY: we initialize the first 10 bytes, the remaining bytes remain uninitialized. + // That is exactly the layout of x87_f80. + unsafe { + let mut this = crate::mem::MaybeUninit::::uninit(); + this.as_mut_ptr().cast::<[u8; 10]>().write(bytes); + this.assume_init() + } + } +} + types! { #![stable(feature = "simd_x86", since = "1.27.0")] diff --git a/tests/assembly-llvm/x87_f80.rs b/tests/assembly-llvm/x87_f80.rs new file mode 100644 index 0000000000000..356ec2f410201 --- /dev/null +++ b/tests/assembly-llvm/x87_f80.rs @@ -0,0 +1,171 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: X86 X86_64 DARWIN WIN64 WIN32 +//@ [X86] compile-flags: --target i686-unknown-linux-gnu +//@ [X86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [DARWIN] compile-flags: --target i686-apple-darwin +//@ [WIN64] compile-flags: --target x86_64-pc-windows-gnu +//@ [WIN32] compile-flags: --target i686-pc-windows-gnu +//@ compile-flags: -Copt-level=3 --crate-type=lib +//@ needs-llvm-components: x86 + +#![feature(no_core)] +#![no_std] +#![no_core] + +extern crate minicore; +#[cfg(target_arch = "x86")] +use minicore::arch::x86::x87_f80; +#[cfg(target_arch = "x86_64")] +use minicore::arch::x86_64::x87_f80; + +// CHECK-LABEL: identity +// +// X86: .cfi_startproc +// X86-NEXT: fldt 4(%esp) +// X86-NEXT: retl +// +// X86_64: .cfi_startproc +// X86_64-NEXT: fldt 8(%rsp) +// X86_64-NEXT: retq +// +// DARWIN: .cfi_startproc +// DARWIN-NEXT: pushl %ebp +// DARWIN: movl %esp, %ebp +// DARWIN: fldt 8(%ebp) +// DARWIN-NEXT: popl %ebp +// DARWIN-NEXT: retl +// +// WIN64: movq %rcx, %rax +// WIN64-NEXT: movaps (%rdx), %xmm0 +// WIN64-NEXT: movaps %xmm0, (%rcx) +// WIN64-NEXT: retq +// +// WIN32: .cfi_startproc +// WIN32-NEXT: pushl %ebp +// WIN32: movl %esp, %ebp +// WIN32: fldt 8(%ebp) +// WIN32-NEXT: popl %ebp +// WIN32-NEXT: retl +#[unsafe(no_mangle)] +pub extern "C" fn identity(x: x87_f80) -> x87_f80 { + x +} + +// CHECK-LABEL: mixed +// +// X86: .cfi_startproc +// X86-NEXT: fldl 4(%esp) +// X86-NEXT: retl +// +// X86_64: .cfi_startproc +// X86_64-NEXT: movsd 8(%rsp), %xmm0 +// X86_64-NEXT: retq +// +// DARWIN: .cfi_startproc +// DARWIN-NEXT: pushl %ebp +// DARWIN: movl %esp, %ebp +// DARWIN: fldl 8(%ebp) +// DARWIN-NEXT: popl %ebp +// DARWIN-NEXT: retl +// +// WIN64: movsd (%rcx), %xmm0 +// WIN64-NEXT: retq +// +// WIN32: .cfi_startproc +// WIN32-NEXT: pushl %ebp +// WIN32: movl %esp, %ebp +// WIN32: fldl 8(%ebp) +// WIN32-NEXT: popl %ebp +// WIN32-NEXT: retl +#[unsafe(no_mangle)] +pub extern "C" fn mixed(value: Mixed) -> f64 { + value.a +} + +#[repr(C)] +struct Mixed { + a: f64, + b: x87_f80, +} + +// CHECK-LABEL: pair +// +// X86: .cfi_startproc +// X86-NEXT: fldt 16(%esp) +// X86-NEXT: retl +// +// X86_64: .cfi_startproc +// X86_64-NEXT: fldt 24(%rsp) +// X86_64-NEXT: retq +// +// DARWIN: .cfi_startproc +// DARWIN-NEXT: pushl %ebp +// DARWIN: movl %esp, %ebp +// DARWIN: fldt 24(%ebp) +// DARWIN-NEXT: popl %ebp +// DARWIN-NEXT: retl +// +// WIN64: movq %rcx, %rax +// WIN64-NEXT: movaps 16(%rdx), %xmm0 +// WIN64-NEXT: movaps %xmm0, (%rcx) +// WIN64-NEXT: retq +// +// WIN32: .cfi_startproc +// WIN32-NEXT: pushl %ebp +// WIN32: movl %esp, %ebp +// WIN32: fldt 20(%ebp) +// WIN32-NEXT: popl %ebp +// WIN32-NEXT: retl +#[unsafe(no_mangle)] +pub extern "C" fn pair(value: Pair) -> x87_f80 { + value.b +} + +#[repr(C)] +struct Pair { + a: x87_f80, + b: x87_f80, +} + +// CHECK-LABEL: quad +// +// X86: .cfi_startproc +// X86-NEXT: fldt 40(%esp) +// X86-NEXT: retl +// +// X86_64: .cfi_startproc +// X86_64-NEXT: fldt 56(%rsp) +// X86_64-NEXT: retq +// +// DARWIN: .cfi_startproc +// DARWIN-NEXT: pushl %ebp +// DARWIN: movl %esp, %ebp +// DARWIN: fldt 56(%ebp) +// DARWIN-NEXT: popl %ebp +// DARWIN-NEXT: retl +// +// WIN64: movq %rcx, %rax +// WIN64-NEXT: movaps 48(%rdx), %xmm0 +// WIN64-NEXT: movaps %xmm0, (%rcx) +// WIN64-NEXT: retq +// +// WIN32: .cfi_startproc +// WIN32-NEXT: pushl %ebp +// WIN32: movl %esp, %ebp +// WIN32: fldt 44(%ebp) +// WIN32-NEXT: popl %ebp +// WIN32-NEXT: retl +#[unsafe(no_mangle)] +pub extern "C" fn quad(value: Quad) -> x87_f80 { + value.d +} + +#[repr(C)] +struct Quad { + a: x87_f80, + b: x87_f80, + c: x87_f80, + d: x87_f80, +} diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index e7e924f9ebc08..3098d53bfd0dd 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -131,6 +131,30 @@ pub mod arch { impl crate::Copy for ppcf128 {} } + + #[cfg(target_arch = "x86")] + pub mod x86 { + #[lang = "x87_f80_raw"] + struct x87_f80_raw {} + + #[lang = "x87_f80"] + pub struct x87_f80(x87_f80_raw); + + impl crate::Copy for x87_f80_raw {} + impl crate::Copy for x87_f80 {} + } + + #[cfg(target_arch = "x86_64")] + pub mod x86_64 { + #[lang = "x87_f80_raw"] + struct x87_f80_raw {} + + #[lang = "x87_f80"] + pub struct x87_f80(x87_f80_raw); + + impl crate::Copy for x87_f80_raw {} + impl crate::Copy for x87_f80 {} + } } #[lang = "phantom_data"] diff --git a/tests/codegen-llvm/x86-abi/x87_f80.rs b/tests/codegen-llvm/x86-abi/x87_f80.rs new file mode 100644 index 0000000000000..90f20776ffdf1 --- /dev/null +++ b/tests/codegen-llvm/x86-abi/x87_f80.rs @@ -0,0 +1,270 @@ +//@ add-minicore +// +//@ revisions: X86 X86_64 DARWIN WIN64 +//@ [X86] compile-flags: --target i686-unknown-linux-gnu +//@ [X86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [DARWIN] compile-flags: --target i686-apple-darwin +//@ [WIN64] compile-flags: --target x86_64-pc-windows-gnu +//@ compile-flags: -Copt-level=3 --crate-type=lib -Zmerge-functions=disabled +//@ needs-llvm-components: x86 + +#![feature(no_core)] +#![no_std] +#![no_core] +#![allow(non_camel_case_types)] + +extern crate minicore; +#[cfg(target_arch = "x86")] +use minicore::arch::x86::x87_f80; +#[cfg(target_arch = "x86_64")] +use minicore::arch::x86_64::x87_f80; +use minicore::simd::f64x2; +use minicore::{Copy, MaybeUninit}; + +#[repr(C)] +struct Single { + a: x87_f80, +} + +#[repr(C)] +struct Pair { + a: x87_f80, + b: x87_f80, +} + +#[repr(C)] +struct Mixed { + a: f64, + b: x87_f80, +} + +// X86-LABEL: x86_fp80 @scalar_second(x86_fp80 %{{[0-9]+}}, x86_fp80 returned %{{[0-9]+}}) +// X86_64-LABEL: x86_fp80 @scalar_second(x86_fp80 %{{[0-9]+}}, x86_fp80 returned %{{[0-9]+}}) +// DARWIN-LABEL: x86_fp80 @scalar_second(x86_fp80 %{{[0-9]+}}, x86_fp80 returned %{{[0-9]+}}) +// WIN64-LABEL: void @scalar_second(ptr {{.*}}sret([16 x i8]) {{.*}}, ptr {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn scalar_second(_a: x87_f80, b: x87_f80) -> x87_f80 { + b +} + +// X86-LABEL: void @single(ptr {{.*}}sret([12 x i8]) align 4 {{.*}}, ptr {{.*}}byval([12 x i8]) align 4 {{.*}}) +// X86_64-LABEL: x86_fp80 @single(ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @single(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @single(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn single(x: Single) -> Single { + x +} + +// X86-LABEL: void @pair(ptr {{.*}}sret([24 x i8]) align 4 {{.*}}, ptr {{.*}}byval([24 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @pair(ptr {{.*}}sret([32 x i8]) align 16 {{.*}}, ptr {{.*}}byval([32 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @pair(ptr {{.*}}sret([32 x i8]) align 16 {{.*}}, ptr {{.*}}byval([32 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @pair(ptr {{.*}}sret([32 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn pair(x: Pair) -> Pair { + x +} + +// Projecting an x87_f80 out of an aggregate used to ICE, because code asserted that +// a field has the same size as the scalar it contains. +// +// CHECK: @pair_first( +#[unsafe(no_mangle)] +fn pair_first(x: Pair) -> x87_f80 { + x.a +} + +// CHECK: @pair_second( +#[unsafe(no_mangle)] +fn pair_second(x: Pair) -> x87_f80 { + x.b +} + +// X86-LABEL: x86_fp80 @mixed(ptr {{.*}}byval([20 x i8]) align 4 {{.*}}) +// X86_64-LABEL: x86_fp80 @mixed(ptr {{.*}}byval([32 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: x86_fp80 @mixed(ptr {{.*}}byval([32 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @mixed(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn mixed(x: Mixed) -> x87_f80 { + x.b +} + +// X86-LABEL: x86_fp80 @many(x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 returned %{{[0-9]+}}) +// X86_64-LABEL: x86_fp80 @many(x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 returned %{{[0-9]+}}) +// DARWIN-LABEL: x86_fp80 @many(x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 %{{[0-9]+}}, x86_fp80 returned %{{[0-9]+}}) +// WIN64-LABEL: void @many(ptr {{.*}}sret([16 x i8]) {{.*}} +#[unsafe(no_mangle)] +extern "C" fn many( + _a: x87_f80, + _b: x87_f80, + _c: x87_f80, + _d: x87_f80, + _e: x87_f80, + _f: x87_f80, + _g: x87_f80, + _h: x87_f80, + i: x87_f80, +) -> x87_f80 { + i +} + +/// Unions test the overlapping of X87 with SSE/INTEGER register classes. +/// +/// The ABI logic processes the fields in lexicographical order, so check +/// both the x87_f80 coming first and coming last. +#[repr(C)] +union X87Before { + a: x87_f80, + b: T, +} + +#[repr(C)] +union X87After { + b: T, + a: x87_f80, +} + +// X86-LABEL: void @union_x87_with_f64(ptr {{.*}}sret([12 x i8]) align 4 {{.*}}, ptr {{.*}}byval([12 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @union_x87_with_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @union_x87_with_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @union_x87_with_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn union_x87_with_f64(x: X87Before) -> X87Before { + x +} + +// X86-LABEL: void @union_x87_with_u64(ptr {{.*}}sret([12 x i8]) align 4 {{.*}}, ptr {{.*}}byval([12 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @union_x87_with_u64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @union_x87_with_u64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @union_x87_with_u64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn union_x87_with_u64(x: X87Before) -> X87Before { + x +} + +#[repr(C)] +struct F64F64(f64, f64); +impl Copy for F64F64 {} + +// X86-LABEL: void @x87_before_f64_f64(ptr {{.*}}sret([16 x i8]) align 4 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @x87_before_f64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @x87_before_f64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @x87_before_f64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn x87_before_f64_f64(x: X87Before) -> X87Before { + x +} + +// X86-LABEL: void @x87_after_f64_f64(ptr {{.*}}sret([16 x i8]) align 4 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @x87_after_f64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @x87_after_f64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @x87_after_f64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn x87_after_f64_f64(x: X87After) -> X87After { + x +} + +#[repr(C)] +struct U64F64(u64, f64); +impl Copy for U64F64 {} + +// X86-LABEL: void @x87_before_u64_f64(ptr {{.*}}sret([16 x i8]) align 4 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @x87_before_u64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @x87_before_u64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @x87_before_u64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn x87_before_u64_f64(x: X87Before) -> X87Before { + x +} + +// X86-LABEL: void @x87_after_u64_f64(ptr {{.*}}sret([16 x i8]) align 4 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @x87_after_u64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @x87_after_u64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @x87_after_u64_f64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn x87_after_u64_f64(x: X87After) -> X87After { + x +} + +#[repr(C)] +struct U64U64(u64, u64); +impl Copy for U64U64 {} + +// X86-LABEL: void @x87_before_u64_u64(ptr {{.*}}sret([16 x i8]) align 4 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// X86_64-LABEL: { i64, i64 } @x87_before_u64_u64({ i64, i64 } {{.*}}) +// DARWIN-LABEL: void @x87_before_u64_u64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// WIN64-LABEL: void @x87_before_u64_u64(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn x87_before_u64_u64(x: X87Before) -> X87Before { + x +} + +// X86-LABEL: void @x87_before_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @x87_before_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @x87_before_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// WIN64-LABEL: void @x87_before_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn x87_before_f64x2(x: X87Before) -> X87Before { + x +} + +// X86-LABEL: void @x87_after_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 4 {{.*}}) +// X86_64-LABEL: void @x87_after_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// DARWIN-LABEL: void @x87_after_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}byval([16 x i8]) align 16 {{.*}}) +// WIN64-LABEL: void @x87_after_f64x2(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn x87_after_f64x2(x: X87After) -> X87After { + x +} + +// X86-LABEL: x86_fp80 @array_third( +// X86: getelementptr inbounds{{( nuw)?}} i8, ptr %xs, i32 24 +// X86-NEXT: load x86_fp80, ptr {{.*}}, align 4 +// +// X86_64-LABEL: x86_fp80 @array_third( +// X86_64: getelementptr inbounds{{( nuw)?}} i8, ptr %xs, i64 32 +// X86_64-NEXT: load x86_fp80, ptr {{.*}}, align 16 +// +// DARWIN-LABEL: x86_fp80 @array_third( +// DARWIN: getelementptr inbounds{{( nuw)?}} i8, ptr %xs, i32 32 +// DARWIN-NEXT: load x86_fp80, ptr {{.*}}, align 16 +// +// WIN64-LABEL: void @array_third(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}align 16 {{.*}}%xs) +// WIN64: getelementptr inbounds{{( nuw)?}} i8, ptr %xs, i64 32 +// WIN64-NEXT: call void @llvm.memcpy{{.*}}(ptr {{.*}}, ptr {{.*}}, i64 16, i1 false) +#[unsafe(no_mangle)] +extern "C" fn array_third(xs: &[x87_f80; 4]) -> x87_f80 { + let [_, _, c, _] = *xs; + c +} + +// CHECK-LABEL: @array_copy( +// X86: call void @llvm.memcpy{{.*}}(ptr {{.*}}, ptr {{.*}}, i32 48, i1 false) +// X86_64: call void @llvm.memcpy{{.*}}(ptr {{.*}}, ptr {{.*}}, i64 64, i1 false) +// DARWIN: call void @llvm.memcpy{{.*}}(ptr {{.*}}, ptr {{.*}}, i32 64, i1 false) +// WIN64: call void @llvm.memcpy{{.*}}(ptr {{.*}}, ptr {{.*}}, i64 64, i1 false) +#[unsafe(no_mangle)] +fn array_copy(xs: &[x87_f80; 4], ys: &mut [x87_f80; 4]) { + *ys = *xs; +} + +#[repr(transparent)] +struct TransparentWrapper(T); + +// X86-LABEL: x86_fp80 @transparent_struct(x86_fp80 returned %{{[0-9]+}}) +// X86_64-LABEL: x86_fp80 @transparent_struct(x86_fp80 returned %{{[0-9]+}}) +// DARWIN-LABEL: x86_fp80 @transparent_struct(x86_fp80 returned %{{[0-9]+}}) +// WIN64-LABEL: void @transparent_struct(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}align 16 {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn transparent_struct(x: TransparentWrapper) -> TransparentWrapper { + x +} + +// X86-LABEL: x86_fp80 @transparent_union(x86_fp80 returned %{{[0-9]+}}) +// X86_64-LABEL: x86_fp80 @transparent_union(x86_fp80 returned %{{[0-9]+}}) +// DARWIN-LABEL: x86_fp80 @transparent_union(x86_fp80 returned %{{[0-9]+}}) +// WIN64-LABEL: void @transparent_union(ptr {{.*}}sret([16 x i8]) align 16 {{.*}}, ptr {{.*}}align 16 {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn transparent_union(x: MaybeUninit) -> MaybeUninit { + x +}