diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 4fda4735b613c..61638f98132e8 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -1,5 +1,7 @@ #[cfg(feature = "nightly")] -use crate::{BackendRepr, FieldsShape, Primitive, Size, TyAbiInterface, TyAndLayout, Variants}; +use crate::{ + BackendRepr, FieldsShape, Float, Primitive, Size, TyAbiInterface, TyAndLayout, Variants, +}; mod reg; @@ -71,6 +73,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { BackendRepr::Scalar(scalar) => { let kind = match scalar.primitive() { Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer, + Primitive::Float(Float::PpcF128) => RegKind::DoubleDouble, Primitive::Float(_) => RegKind::Float, }; Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 745a2ecfc6159..2531750f0e005 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -8,6 +8,11 @@ use crate::{Align, HasDataLayout, Integer, Primitive, Size}; pub enum RegKind { Integer, Float, + /// The IBM extended-precision format: a pair of `f64`s, each passed in its own register. + /// This variant is needed to distinguish IEEE f128 and IBM f128 in the backends. + /// + /// Only used on PowerPC targets. + DoubleDouble, Vector { /// The `hint_vector_elem` is strictly for optimization purposes. E.g. it can be used by /// a codegen backend to prevent extra bitcasts that obscure a pattern. Alternatively, @@ -69,6 +74,7 @@ impl Reg { 128 => dl.f128_align, _ => panic!("unsupported float: {self:?}"), }, + RegKind::DoubleDouble => dl.f128_align, RegKind::Vector { .. } => dl.rust_vector_align(self.size), } } diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 7c878cc619472..fd3d8d9c15edc 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -321,10 +321,11 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { match primitive { // Explicitly spell out all the float types so that any new ones have to be added to // one of the match branches. - Primitive::Int(..) - | Primitive::Float(Float::F16 | Float::F32 | Float::F64 | Float::F128) => { + Primitive::Int(..) => Some(primitive), + Primitive::Float(Float::F16 | Float::F32 | Float::F64 | Float::F128) => { Some(primitive) } + Primitive::Float(Float::PpcF128) => Some(primitive), Primitive::Pointer(..) => None, } } else { diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 7cfb93ca1b86d..a81aec78f01f1 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1413,6 +1413,7 @@ pub enum Float { F32, F64, F128, + PpcF128, } impl Float { @@ -1424,6 +1425,7 @@ impl Float { F32 => Size::from_bits(32), F64 => Size::from_bits(64), F128 => Size::from_bits(128), + PpcF128 => Size::from_bits(128), } } @@ -1436,6 +1438,7 @@ impl Float { F32 => dl.f32_align, F64 => dl.f64_align, F128 => dl.f128_align, + PpcF128 => dl.f128_align, }) } @@ -1447,6 +1450,7 @@ impl Float { F32 => "f32", F64 => "f64", F128 => "f128", + PpcF128 => "ppcf128", } } } diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index e919e7df99ee0..4161160479509 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -179,6 +179,7 @@ pub enum FloatTy { F32, F64, F128, + PpcF128, } impl FloatTy { @@ -188,6 +189,7 @@ impl FloatTy { FloatTy::F32 => "f32", FloatTy::F64 => "f64", FloatTy::F128 => "f128", + FloatTy::PpcF128 => "ppcf128", } } @@ -198,6 +200,7 @@ impl FloatTy { FloatTy::F32 => sym::f32, FloatTy::F64 => sym::f64, FloatTy::F128 => sym::f128, + FloatTy::PpcF128 => sym::ppcf128, } } @@ -207,6 +210,7 @@ impl FloatTy { FloatTy::F32 => 32, FloatTy::F64 => 64, FloatTy::F128 => 128, + FloatTy::PpcF128 => 128, } } } diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index f2ad7abba755d..ac389b281fe39 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -237,6 +237,7 @@ language_item_table! { VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None; Complex, sym::complex, complex, Target::Struct, GenericRequirement::Exact(1); + PpcF128, sym::ppcf128, ppcf128_type, 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/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index 7194d8144ca69..b48685cc4aae9 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -14,8 +14,10 @@ diff --git a/coretests/tests/lib.rs b/coretests/tests/lib.rs index 1e336bf..35e6f54 100644 --- a/coretests/tests/lib.rs +++ b/coretests/tests/lib.rs -@@ -2,4 +2,3 @@ +@@ -2,7 +2,6 @@ // tidy-alphabetical-start + #![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(powerpc_ppcf128))] + #![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc))] #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 1bdb3efefa1aa..4771e4b17578e 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -37,6 +37,7 @@ pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type { Float::F32 => types::F32, Float::F64 => types::F64, Float::F128 => types::F128, + Float::PpcF128 => bug!("cranelift does not support powerpc"), }, // FIXME(erikdesjardins): handle non-default addrspace ptr sizes Primitive::Pointer(_) => pointer_ty(tcx), @@ -68,6 +69,7 @@ fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option types::F32, FloatTy::F64 => types::F64, FloatTy::F128 => types::F128, + FloatTy::PpcF128 => bug!("cranelift does not support powerpc"), }, ty::FnPtr(..) => pointer_ty(tcx), ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) => { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 2901eb8b1a6d2..a90f1553860bb 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -90,6 +90,7 @@ impl GccType for Reg { 64 => cx.type_f64(), _ => bug!("unsupported float: {:?}", self), }, + RegKind::DoubleDouble => cx.type_ppcf128(), RegKind::Vector { hint_vector_elem: _ } => { cx.type_vector(cx.type_i8(), self.size.bytes()) } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 5550d22b33aa3..24064ae161d87 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -461,6 +461,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } get_simple_function_f128(span, self, name) } + (_, PpcF128) => span_bug!(span, "ppcf128 {name} is unimplemented"), }; self.cx.context.new_call( self.location, diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 5252f93a92ebe..570759913166b 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -94,6 +94,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { ty::FloatTy::F32 => self.type_f32(), ty::FloatTy::F64 => self.type_f64(), ty::FloatTy::F128 => self.type_f128(), + ty::FloatTy::PpcF128 => self.type_ppcf128(), } } @@ -180,6 +181,10 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> { bug!("unsupported float width 128") } + fn type_ppcf128(&self) -> Type<'gcc> { + bug!("unsupported ppcf128 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 816ebe3fcf3d9..b04f0bfff12a1 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 { 128 => cx.type_f128(), _ => bug!("unsupported float: {:?}", self), }, + RegKind::DoubleDouble => cx.type_ppcf128(), RegKind::Vector { hint_vector_elem } => { // NOTE: it is valid to ignore the element type hint (and always pick i8). // But providing a more accurate type means fewer casts in LLVM IR, @@ -161,6 +162,7 @@ impl LlvmType for Reg { Float::F32 => cx.type_f32(), Float::F64 => cx.type_f64(), Float::F128 => cx.type_f128(), + Float::PpcF128 => cx.type_ppcf128(), }, Primitive::Pointer(_) => cx.type_ptr(), }; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 54bdfb5f442d9..5a476613a2677 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -703,6 +703,7 @@ impl MsvcBasicName for ty::FloatTy { ty::FloatTy::F32 => "float", ty::FloatTy::F64 => "double", ty::FloatTy::F128 => "fp128", + ty::FloatTy::PpcF128 => bug!("MSVC does not support powerpc"), } } } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c4ff1eee56750..9824d2ab356ae 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -350,6 +350,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // FIXME(f128) figure out whether we should support this. bug!("the va_arg intrinsic does not support `f128`") } + Primitive::Float(Float::PpcF128) => { + // FIXME(ppcf128) we should support this. + bug!("the va_arg intrinsic does not currently support `ppcf128`") + } } emit_va_arg(self, args[0], result_layout.ty) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c20b4ccd776da..60ad6402d5f0f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -939,6 +939,7 @@ unsafe extern "C" { pub(crate) fn LLVMFloatTypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMDoubleTypeInContext(C: &Context) -> &Type; pub(crate) fn LLVMFP128TypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMPPCFP128TypeInContext(C: &Context) -> &Type; // Operations on non-IEEE real types pub(crate) fn LLVMBFloatTypeInContext(C: &Context) -> &Type; diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 22d43f22e24a4..fe25694735362 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -134,6 +134,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { ty::FloatTy::F32 => self.type_f32(), ty::FloatTy::F64 => self.type_f64(), ty::FloatTy::F128 => self.type_f128(), + ty::FloatTy::PpcF128 => self.type_ppcf128(), } } @@ -226,6 +227,10 @@ impl<'ll, CX: Borrow>> BaseTypeCodegenMethods for GenericCx<'ll, CX> { unsafe { llvm::LLVMFP128TypeInContext(self.llcx()) } } + fn type_ppcf128(&self) -> &'ll Type { + unsafe { llvm::LLVMPPCFP128TypeInContext(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/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index a64452dbc5a7e..8ab8adfe7339c 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -98,6 +98,7 @@ fn get_param_type_alignment<'ll, 'tcx>( Float::F16 | Float::F32 => unreachable!(), Float::F64 => { /* fall through */ } Float::F128 => return Align::from_bytes(16).unwrap(), + Float::PpcF128 => { /* fall through */ } }, Primitive::Pointer(_) => { /* fall through */ } }, diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 939e5395e4741..7181d5bb8cef9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -491,6 +491,7 @@ fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_t ..=8 => "f64", _ => ptr_type, }, + RegKind::DoubleDouble => bug!("not a valid wasm type"), RegKind::Vector { .. } => "v128", }; @@ -511,6 +512,7 @@ fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str Float::F16 | Float::F32 => "f32", Float::F64 => "f64", Float::F128 => "i64, i64", + Float::PpcF128 => bug!("not a valid wasm type"), }, Primitive::Pointer(_) => ptr_type, } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 707eb3a6ee85d..673d69cb5d7e2 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -21,6 +21,7 @@ pub trait BaseTypeCodegenMethods: BackendTypes { fn type_f32(&self) -> Self::Type; fn type_f64(&self) -> Self::Type; fn type_f128(&self) -> Self::Type; + fn type_ppcf128(&self) -> Self::Type; fn type_array(&self, ty: Self::Type, len: u64) -> Self::Type; fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::FunctionSignature; @@ -70,6 +71,7 @@ pub trait DerivedTypeCodegenMethods<'tcx>: F32 => self.type_f32(), F64 => self.type_f64(), F128 => self.type_f128(), + PpcF128 => self.type_ppcf128(), } } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 6c8673b278ec0..5a02c2f28fb1b 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -2,13 +2,15 @@ use std::assert_matches; use rustc_abi::{FieldIdx, Integer}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_apfloat::ppc::DoubleDouble; use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir::CastKind; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; +use rustc_middle::ty::layout::{HasTyCtxt, IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_middle::{bug, span_bug}; +use rustc_target::spec::HasTargetSpec; use tracing::trace; use super::util::ensure_monomorphic_enough; @@ -249,6 +251,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => self.cast_from_float(src.to_scalar().to_f32()?, cast_to.ty), FloatTy::F64 => self.cast_from_float(src.to_scalar().to_f64()?, cast_to.ty), FloatTy::F128 => self.cast_from_float(src.to_scalar().to_f128()?, cast_to.ty), + FloatTy::PpcF128 => { + // FIXME(ppcf128): this needs a better algorithm in rustc_apfloat. + span_bug!(self.cur_span(), "casting ppcf128 is not currently supported") + } }; interp_ok(ImmTy::from_scalar(val, cast_to)) } @@ -330,6 +336,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { src_layout: TyAndLayout<'tcx>, cast_ty: Ty<'tcx>, ) -> InterpResult<'tcx, Scalar> { + let target_endian = self.tcx().target_spec().endian; + // Let's make sure v is sign-extended *if* it has a signed type. let signed = src_layout.backend_repr.is_signed(); // Also asserts that abi is `Scalar`. @@ -362,6 +370,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => Scalar::from_f32(Single::from_i128(v).value), FloatTy::F64 => Scalar::from_f64(Double::from_i128(v).value), FloatTy::F128 => Scalar::from_f128(Quad::from_i128(v).value), + FloatTy::PpcF128 => { + Scalar::from_ppcf128(DoubleDouble::from_i128(v).value, target_endian) + } } } // unsigned int -> float @@ -370,6 +381,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => Scalar::from_f32(Single::from_u128(v).value), FloatTy::F64 => Scalar::from_f64(Double::from_u128(v).value), FloatTy::F128 => Scalar::from_f128(Quad::from_u128(v).value), + FloatTy::PpcF128 => { + Scalar::from_ppcf128(DoubleDouble::from_u128(v).value, target_endian) + } }, // u8 -> char @@ -422,6 +436,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F128 => { Scalar::from_f128(self.adjust_nan(f.convert(&mut false).value, &[f])) } + FloatTy::PpcF128 => { + // FIXME(ppcf128): this needs a better algorithm in rustc_apfloat. + span_bug!(self.cur_span(), "casting ppcf128 is not currently supported") + } }, // That's it. _ => span_bug!(self.cur_span(), "invalid float to {} cast", dest_ty), diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 00057dc503827..473c14d6fff60 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -631,6 +631,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => self.unop_float_intrinsic::(intrinsic_name, arg)?, FloatTy::F64 => self.unop_float_intrinsic::(intrinsic_name, arg)?, FloatTy::F128 => self.unop_float_intrinsic::(intrinsic_name, arg)?, + FloatTy::PpcF128 => { + span_bug!(self.cur_span(), "fabs on ppcf128 is unimplemented") + } }; self.write_scalar(out_val, dest)?; } @@ -1377,6 +1380,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => float_to_int_inner(self, src.to_scalar().to_f32()?, cast_to, round), FloatTy::F64 => float_to_int_inner(self, src.to_scalar().to_f64()?, cast_to, round), FloatTy::F128 => float_to_int_inner(self, src.to_scalar().to_f128()?, cast_to, round), + FloatTy::PpcF128 => { + let target_endian = self.tcx.sess.target.options.endian; + float_to_int_inner(self, src.to_scalar().to_ppcf128(target_endian)?, cast_to, round) + } }; if status.intersects( diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index 2ddb20fe8c987..243e33caa90fc 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -133,6 +133,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()), FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()), FloatTy::F128 => Scalar::from_f128(op.to_f128()?.abs()), + FloatTy::PpcF128 => { + span_bug!(self.cur_span(), "ppcf128 is not a valid vector type") + } } } Op::Round(rounding) => { @@ -149,6 +152,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => self.float_round::(op, rounding)?, FloatTy::F64 => self.float_round::(op, rounding)?, FloatTy::F128 => self.float_round::(op, rounding)?, + FloatTy::PpcF128 => { + span_bug!(self.cur_span(), "ppcf128 is not a valid vector type") + } } } Op::Numeric(name) => { @@ -745,6 +751,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => self.float_muladd::(a, b, c, typ)?, FloatTy::F64 => self.float_muladd::(a, b, c, typ)?, FloatTy::F128 => self.float_muladd::(a, b, c, typ)?, + FloatTy::PpcF128 => { + span_bug!(self.cur_span(), "ppcf128 is not a valid vector type") + } }; self.write_scalar(val, &dest)?; } @@ -828,6 +837,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => self.float_minmax::(left, right, op)?, FloatTy::F64 => self.float_minmax::(left, right, op)?, FloatTy::F128 => self.float_minmax::(left, right, op)?, + FloatTy::PpcF128 => { + span_bug!(self.cur_span(), "ppcf128 is not a valid vector type") + } }) } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 33c8cb2499c85..3071e255beebc 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -1,5 +1,6 @@ use either::Either; use rustc_abi::Size; +use rustc_apfloat::ppc::DoubleDouble; use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::TyAndLayout; @@ -94,6 +95,27 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + /// A separate function because DoubleDouble does not currently implement FloatConvert. + fn binary_ppcf128_op( + &self, + bin_op: mir::BinOp, + _layout: TyAndLayout<'tcx>, + l: DoubleDouble, + r: DoubleDouble, + ) -> ImmTy<'tcx, M::Provenance> { + use rustc_middle::mir::BinOp::*; + + match bin_op { + Eq => ImmTy::from_bool(l == r, *self.tcx), + Ne => ImmTy::from_bool(l != r, *self.tcx), + Lt => ImmTy::from_bool(l < r, *self.tcx), + Le => ImmTy::from_bool(l <= r, *self.tcx), + Gt => ImmTy::from_bool(l > r, *self.tcx), + Ge => ImmTy::from_bool(l >= r, *self.tcx), + _ => span_bug!(self.cur_span(), "invalid ppcf128 op: `{:?}`", bin_op), + } + } + fn binary_int_op( &self, bin_op: mir::BinOp, @@ -406,6 +428,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F128 => { self.binary_float_op(bin_op, layout, left.to_f128()?, right.to_f128()?) } + FloatTy::PpcF128 => { + let target_endian = self.tcx.sess.target.options.endian; + self.binary_ppcf128_op( + bin_op, + layout, + left.to_ppcf128(target_endian)?, + right.to_ppcf128(target_endian)?, + ) + } }) } _ if left.layout.ty.is_integral() => { @@ -463,6 +494,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(ImmTy::from_bool(res, *self.tcx)) } ty::Float(fty) => { + let target_endian = self.tcx.sess.target.options.endian; + let val = val.to_scalar(); if un_op != Neg { span_bug!(self.cur_span(), "Invalid float op {:?}", un_op); @@ -474,6 +507,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { FloatTy::F32 => Scalar::from_f32(-val.to_f32()?), FloatTy::F64 => Scalar::from_f64(-val.to_f64()?), FloatTy::F128 => Scalar::from_f128(-val.to_f128()?), + FloatTy::PpcF128 => { + Scalar::from_ppcf128(-val.to_ppcf128(target_endian)?, target_endian) + } }; interp_ok(ImmTy::from_scalar(res, layout)) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index cfff8d1768f0e..b184b0a741bc8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2223,7 +2223,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let [leading_segments @ .., segment] = path.segments else { bug!() }; let _ = self .prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None); - self.lower_path_segment(span, did, segment) + if tcx.is_lang_item(did, LangItem::PpcF128) { + Ty::new_float(tcx, ty::FloatTy::PpcF128) + } else { + self.lower_path_segment(span, did, segment) + } } Res::Def(kind @ DefKind::Variant, def_id) if let PermitVariants::Yes = permit_variants => diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index bffd5d144c778..d595dbda201f8 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -440,6 +440,7 @@ pub(crate) fn lint_literal<'tcx>( ty::FloatTy::F32 => float_is_infinite::(v), ty::FloatTy::F64 => float_is_infinite::(v), ty::FloatTy::F128 => float_is_infinite::(v), + ty::FloatTy::PpcF128 => bug!("there are no ppcf128 literals"), }; if is_infinite == Some(true) { diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index e78b000a84c0f..1963786ea3e60 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -2,9 +2,10 @@ use std::fmt; use std::num::NonZero; use either::{Either, Left, Right}; -use rustc_abi::{HasDataLayout, Size}; +use rustc_abi::{Endian, HasDataLayout, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_apfloat::ppc::DoubleDouble; use rustc_macros::{StableHash, TyDecodable, TyEncodable}; use super::{ @@ -229,6 +230,11 @@ impl Scalar { Scalar::Int(f.into()) } + #[inline] + pub fn from_ppcf128(f: DoubleDouble, target_endian: Endian) -> Self { + Scalar::Int(ScalarInt::from_ppcf128(f, target_endian)) + } + /// This is almost certainly not the method you want! You should dispatch on the type /// and use `to_{u8,u16,...}`/`to_pointer` to perform ptr-to-int / int-to-ptr casts as needed. /// @@ -451,4 +457,9 @@ impl<'tcx, Prov: Provenance> Scalar { pub fn to_f128(self) -> InterpResult<'tcx, Quad> { self.to_float() } + + #[inline] + pub fn to_ppcf128(self, target_endian: Endian) -> InterpResult<'tcx, DoubleDouble> { + self.to_scalar_int().map(|scalar_int| scalar_int.to_ppcf128(target_endian)) + } } diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 9fa4f157ebffa..e553403b127ff 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -1,9 +1,10 @@ use std::fmt; use std::num::NonZero; -use rustc_abi::Size; +use rustc_abi::{Endian, Size}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_apfloat::ppc::DoubleDouble; use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -445,6 +446,48 @@ impl ScalarInt { pub fn to_f128(self) -> Quad { self.to_float() } + + #[inline] + pub fn to_ppcf128(self, target_endian: Endian) -> DoubleDouble { + // DoubleDouble always stores the large component in the low 64 bits, and the small + // component in the high 64 bits of the underlying u128. Hence, this representation + // is endian-agnostic. + // + // ScalarInt has the in-memory bitpattern and is subject to endianness. On BE, the + // large component is in the high 64 bits, the small component in the low 64 bits. + // + // The representations agree on LE but differ on BE, and this must be corrected. + match target_endian { + Endian::Little => DoubleDouble::from_bits(self.to_u128()), + Endian::Big => { + let bits = self.to_u128(); + let swapped = (bits >> 64) | (bits << 64); + DoubleDouble::from_bits(swapped) + } + } + } + + #[inline] + pub fn from_ppcf128(f: DoubleDouble, target_endian: Endian) -> Self { + // DoubleDouble always stores the large component in the low 64 bits, and the small + // component in the high 64 bits of the underlying u128. Hence, this representation + // is endian-agnostic. + // + // ScalarInt has the in-memory bitpattern and is subject to endianness. On BE, the + // large component is in the high 64 bits, the small component in the low 64 bits. + // + // The representations agree on LE but differ on BE, and this must be corrected. + let bits = f.to_bits(); + let size = Size::from_bits(DoubleDouble::BITS); + + match target_endian { + Endian::Little => ScalarInt::try_from_uint(bits, size).unwrap(), + Endian::Big => { + let swapped = (bits >> 64) | (bits << 64); + ScalarInt::try_from_uint(swapped, size).unwrap() + } + } + } } macro_rules! from_x_for_scalar_int { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5b5656c05f10d..ebfdd29585e86 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -326,6 +326,7 @@ pub struct CommonTypes<'tcx> { pub f32: Ty<'tcx>, pub f64: Ty<'tcx>, pub f128: Ty<'tcx>, + pub ppcf128: Ty<'tcx>, pub str_: Ty<'tcx>, pub never: Ty<'tcx>, pub self_param: Ty<'tcx>, @@ -466,6 +467,7 @@ impl<'tcx> CommonTypes<'tcx> { f32: mk(Float(ty::FloatTy::F32)), f64: mk(Float(ty::FloatTy::F64)), f128: mk(Float(ty::FloatTy::F128)), + ppcf128: mk(Float(ty::FloatTy::PpcF128)), str_: mk(Str), self_param: mk(ty::Param(ty::ParamTy { index: 0, name: kw::SelfUpper })), diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 764f3b5b93318..e6f311d9f0302 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -142,6 +142,7 @@ impl abi::Float { F32 => tcx.types.f32, F64 => tcx.types.f64, F128 => tcx.types.f128, + PpcF128 => tcx.types.ppcf128, } } @@ -152,6 +153,7 @@ impl abi::Float { ty::FloatTy::F32 => F32, ty::FloatTy::F64 => F64, ty::FloatTy::F128 => F128, + ty::FloatTy::PpcF128 => PpcF128, } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f5960e65c4493..da65b65d1f782 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -17,6 +17,7 @@ use rustc_hir::definitions::{DefKey, DefPathDataName}; use rustc_macros::{Lift, extension}; use rustc_span::{Ident, RemapPathScopeComponents, Symbol, kw, sym}; use rustc_structures::Limit; +use rustc_target::spec::HasTargetSpec; use rustc_type_ir::{FieldInfo, Unnormalized, Upcast as _, elaborate}; use smallvec::SmallVec; @@ -1800,6 +1801,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let val = Quad::try_from(int).unwrap(); write!(self, "{}{}f128", val, if val.is_finite() { "" } else { "_" })?; } + ty::FloatTy::PpcF128 => { + let target_endian = self.tcx().target_spec().endian; + let val = int.to_ppcf128(target_endian); + write!(self, "{}{}ppcf128", val, if val.is_finite() { "" } else { "_" })?; + } }, // Int ty::Uint(_) | ty::Int(_) => { diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 013064b5cec4b..9e505022a053c 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -583,6 +583,7 @@ impl<'tcx> Ty<'tcx> { F32 => tcx.types.f32, F64 => tcx.types.f64, F128 => tcx.types.f128, + PpcF128 => tcx.types.ppcf128, } } @@ -2125,6 +2126,7 @@ impl<'tcx> Ty<'tcx> { ty::FloatTy::F32 => Some(sym::f32), ty::FloatTy::F64 => Some(sym::f64), ty::FloatTy::F128 => Some(sym::f128), + ty::FloatTy::PpcF128 => Some(sym::ppcf128), }, ty::Int(f) => match f { ty::IntTy::Isize => Some(sym::isize), diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 5309e35b1073c..d5c0d23688d13 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -215,12 +215,17 @@ impl<'tcx> TyCtxt<'tcx> { ty::Infer(ty::FloatVar(_)) => { // This causes a compiler error if any new float kinds are added. - let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128); + let (ty::FloatTy::F16 + | ty::FloatTy::F32 + | ty::FloatTy::F64 + | ty::FloatTy::F128 + | ty::FloatTy::PpcF128); let possible_floats = [ ty::SimplifiedType::Float(ty::FloatTy::F16), ty::SimplifiedType::Float(ty::FloatTy::F32), ty::SimplifiedType::Float(ty::FloatTy::F64), ty::SimplifiedType::Float(ty::FloatTy::F128), + ty::SimplifiedType::Float(ty::FloatTy::PpcF128), ]; for simp in possible_floats { diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 5996073241e2c..53681b8b19025 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -173,7 +173,8 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx> trunc(if neg { u128::wrapping_neg(n.get()) } else { n.get() }) } (ast::LitKind::Float(n, _), ty::Float(fty)) => { - parse_float_into_constval(n, *fty, neg).unwrap() + let target_endian = tcx.sess.target.options.endian; + parse_float_into_constval(n, *fty, neg, target_endian).unwrap() } (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(b)), (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(c)), diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 528300804c172..210347284e9eb 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2984,6 +2984,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { RangeEnd::Excluded => (*l..*h).contains(&actual), } } + Constructor::PpcF128Range(l, h, end) => { + let target_endian = self.tcx.sess.target.options.endian; + let actual = valtree.to_leaf().to_ppcf128(target_endian); + match end { + RangeEnd::Included => (*l..=*h).contains(&actual), + RangeEnd::Excluded => (*l..*h).contains(&actual), + } + } Constructor::Wildcard => true, // Opaque patterns must not be matched on structurally. diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 60b8cf3f78a06..9d5faaa376a29 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -21,9 +21,10 @@ //! to the builder as `this` (and never `self`), even when not nested. use itertools::Itertools; -use rustc_abi::{ExternAbi, FieldIdx}; +use rustc_abi::{Endian, ExternAbi, FieldIdx}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_apfloat::ppc::DoubleDouble; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sorted_map::SortedIndexMultiMap; use rustc_errors::ErrorGuaranteed; @@ -1092,14 +1093,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -fn parse_float_into_constval(num: Symbol, float_ty: ty::FloatTy, neg: bool) -> Option { - parse_float_into_scalar(num, float_ty, neg).map(|s| ConstValue::Scalar(s.into())) +fn parse_float_into_constval( + num: Symbol, + float_ty: ty::FloatTy, + neg: bool, + target_endian: Endian, +) -> Option { + parse_float_into_scalar(num, float_ty, neg, target_endian).map(|s| ConstValue::Scalar(s.into())) } pub(crate) fn parse_float_into_scalar( num: Symbol, float_ty: ty::FloatTy, neg: bool, + target_endian: Endian, ) -> Option { let num = num.as_str(); match float_ty { @@ -1165,6 +1172,13 @@ pub(crate) fn parse_float_into_scalar( } Some(ScalarInt::from(f)) } + ty::FloatTy::PpcF128 => { + let mut f = num.parse::().ok()?; + if neg { + f = -f; + } + Some(ScalarInt::from_ppcf128(f, target_endian)) + } } } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index bf1dacceee46b..66b119f00c974 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -99,12 +99,15 @@ pub(crate) fn lit_to_const<'tcx>( ast::FloatTy::F32 => ty::FloatTy::F32, ast::FloatTy::F64 => ty::FloatTy::F64, ast::FloatTy::F128 => ty::FloatTy::F128, + ast::FloatTy::PpcF128 => ty::FloatTy::PpcF128, }; - let bits = parse_float_into_scalar(n, fty, neg)?; + let target_endian = tcx.sess.target.options.endian; + let bits = parse_float_into_scalar(n, fty, neg, target_endian)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, fty)) } (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), Some(ty::Float(fty))) => { - let bits = parse_float_into_scalar(n, *fty, neg)?; + let target_endian = tcx.sess.target.options.endian; + let bits = parse_float_into_scalar(n, *fty, neg, target_endian)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, *fty)) } (ast::LitKind::Char(c), _) => (ty::ValTree::from_scalar_int(tcx, c.into()), tcx.types.char), diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 86387f5caf325..f023f91f62cf4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -443,11 +443,13 @@ impl<'tcx> ConstToPat<'tcx> { } ty::Float(flt) => { let v = valtree.to_leaf(); + let target_endian = tcx.sess.target.options.endian; let is_nan = match flt { ty::FloatTy::F16 => v.to_f16().is_nan(), ty::FloatTy::F32 => v.to_f32().is_nan(), ty::FloatTy::F64 => v.to_f64().is_nan(), ty::FloatTy::F128 => v.to_f128().is_nan(), + ty::FloatTy::PpcF128 => v.to_ppcf128(target_endian).is_nan(), }; if is_nan { // NaNs are not ever equal to anything so they make no sense as patterns. diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index 2159a8533508f..4dcfe81b41c76 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -180,6 +180,7 @@ use std::fmt; use std::iter::once; use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS}; +use rustc_apfloat::ppc::DoubleDouble; use rustc_index::IndexVec; use rustc_index::bit_set::{DenseBitSet, GrowableBitSet}; use smallvec::SmallVec; @@ -703,6 +704,7 @@ pub enum Constructor { F32Range(IeeeFloat, IeeeFloat, RangeEnd), F64Range(IeeeFloat, IeeeFloat, RangeEnd), F128Range(IeeeFloat, IeeeFloat, RangeEnd), + PpcF128Range(DoubleDouble, DoubleDouble, RangeEnd), /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately. Str(Cx::StrLit), /// Deref patterns (enabled by the `deref_patterns` feature) provide a way of matching on a @@ -752,6 +754,7 @@ impl Clone for Constructor { Constructor::F32Range(lo, hi, end) => Constructor::F32Range(*lo, *hi, *end), Constructor::F64Range(lo, hi, end) => Constructor::F64Range(*lo, *hi, *end), Constructor::F128Range(lo, hi, end) => Constructor::F128Range(*lo, *hi, *end), + Constructor::PpcF128Range(lo, hi, end) => Constructor::PpcF128Range(*lo, *hi, *end), Constructor::Str(value) => Constructor::Str(value.clone()), Constructor::DerefPattern(ty) => Constructor::DerefPattern(ty.clone()), Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()), @@ -949,6 +952,7 @@ impl Constructor { F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, F128Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, + PpcF128Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?, Str(value) => write!(f, "{value:?}")?, DerefPattern(_) => write!(f, "deref!({:?})", fields.next().unwrap())?, Opaque(..) => write!(f, "")?, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index ae5eefa1cded8..436c987b8151d 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -272,8 +272,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { }, DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())), Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) - | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing - | PrivateUninhabited | Wildcard => &[], + | F128Range(..) | PpcF128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive + | Hidden | Missing | PrivateUninhabited | Wildcard => &[], Or => { bug!("called `Fields::wildcards` on an `Or` ctor") } @@ -295,8 +295,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { Ref | DerefPattern(_) => 1, Slice(slice) => slice.arity(), Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) - | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing - | PrivateUninhabited | Wildcard => 0, + | F128Range(..) | PpcF128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive + | Hidden | Missing | PrivateUninhabited | Wildcard => 0, Or => bug!("The `Or` constructor doesn't have a fixed arity"), } } @@ -647,6 +647,16 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY); F128Range(lo, hi, end) } + ty::FloatTy::PpcF128 => { + use rustc_apfloat::ppc::DoubleDouble; + let lo = lo + .map(DoubleDouble::from_bits) + .unwrap_or(-DoubleDouble::INFINITY); + let hi = hi + .map(DoubleDouble::from_bits) + .unwrap_or(DoubleDouble::INFINITY); + PpcF128Range(lo, hi, end) + } } } _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()), @@ -864,7 +874,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, `Missing` should have been processed in `apply_constructors`" ), - F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => { + F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | PpcF128Range(..) + | Opaque(..) | Or => { bug!("can't convert to pattern: {:?}", pat) } } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 910f4a5745a7d..54ea97bdaa5c8 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -356,6 +356,7 @@ pub enum FloatLength { F32, F64, F128, + PpcF128, } impl IntegerLength { @@ -377,6 +378,7 @@ impl FloatLength { FloatLength::F32 => 32, FloatLength::F64 => 64, FloatLength::F128 => 128, + FloatLength::PpcF128 => 128, } } } diff --git a/compiler/rustc_public/src/ty/tys.rs b/compiler/rustc_public/src/ty/tys.rs index 179d969a53675..157787316e5c8 100644 --- a/compiler/rustc_public/src/ty/tys.rs +++ b/compiler/rustc_public/src/ty/tys.rs @@ -645,6 +645,7 @@ pub enum FloatTy { F32, F64, F128, + PpcF128, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index e8a089fd86f60..37abd84aa1581 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -255,6 +255,7 @@ impl RustcInternal for FloatTy { FloatTy::F32 => rustc_ty::FloatTy::F32, FloatTy::F64 => rustc_ty::FloatTy::F64, FloatTy::F128 => rustc_ty::FloatTy::F128, + FloatTy::PpcF128 => rustc_ty::FloatTy::PpcF128, } } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 4bb00b4c04394..18728d01e900f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -391,6 +391,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Float { rustc_abi::Float::F32 => FloatLength::F32, rustc_abi::Float::F64 => FloatLength::F64, rustc_abi::Float::F128 => FloatLength::F128, + rustc_abi::Float::PpcF128 => FloatLength::PpcF128, } } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 17ce015d4ce10..eebe13c8bdba3 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -410,6 +410,7 @@ impl<'tcx> Stable<'tcx> for ty::FloatTy { ty::FloatTy::F32 => FloatTy::F32, ty::FloatTy::F64 => FloatTy::F64, ty::FloatTy::F128 => FloatTy::F128, + ty::FloatTy::PpcF128 => FloatTy::PpcF128, } } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index b0462ab4867cb..d66f3eda5e52c 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -474,6 +474,7 @@ pub(crate) fn encode_ty<'tcx>( Arch::PowerPC | Arch::PowerPC64 => "u9__ieee128", // "g" is used for __ibm128 _ => "g", }, + FloatTy::PpcF128 => "g", }); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 768f1be1cd48d..f52b921d0b448 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1587,6 +1587,7 @@ symbols! { powif32, powif64, powif128, + ppcf128, pre_dash_lto: "pre-lto", precise_capturing, precise_capturing_in_traits, diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 5ed41ac456031..b861bb98441ae 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -507,6 +507,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { ty::Float(FloatTy::F32) => "f", ty::Float(FloatTy::F64) => "d", ty::Float(FloatTy::F128) => "C4f128", + ty::Float(FloatTy::PpcF128) => "C7ppcf128", ty::Never => "z", ty::Tuple(_) if ty.is_unit() => "u", diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 0162aa838cb6b..fb12e658993e2 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -31,7 +31,7 @@ where } let valid_unit = match unit.kind { - RegKind::Integer => false, + RegKind::Integer | RegKind::DoubleDouble => false, // The softfloat ABI treats floats like integers, so they // do not get homogeneous aggregate treatment. RegKind::Float => cx.target_spec().rustc_abi != Some(RustcAbi::Softfloat), diff --git a/compiler/rustc_target/src/callconv/arm.rs b/compiler/rustc_target/src/callconv/arm.rs index 66f0ded3874f9..4abc78bdf284f 100644 --- a/compiler/rustc_target/src/callconv/arm.rs +++ b/compiler/rustc_target/src/callconv/arm.rs @@ -26,6 +26,7 @@ where let valid_unit = match unit.kind { RegKind::Integer => false, RegKind::Float => true, + RegKind::DoubleDouble => unreachable!(), RegKind::Vector { .. } => size.bits() == 64 || size.bits() == 128, }; diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 8002f98507ba8..69d062c7df1ec 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -34,6 +34,7 @@ where Float::F32 => Some(Reg::f32()), Float::F64 => Some(Reg::f64()), Float::F128 => Some(Reg::f128()), + Float::PpcF128 => unreachable!(), } } _ => None, diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 3eb40abe90f33..1a39795724004 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -25,17 +25,27 @@ where C: HasDataLayout, { arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { - // ELFv1 and AIX only passes one-member aggregates transparently. - // ELFv2 passes up to eight uniquely addressable members. - if ((abi == ELFv1 || abi == AIX) && arg.layout.size > unit.size) - || arg.layout.size > unit.size.checked_mul(8, cx).unwrap() - { - return None; + match abi { + ELFv1 | AIX => { + // Pass only one-member aggregates transparently. + if arg.layout.size > unit.size { + return None; + } + } + ELFv2 => { + // A `ppcf128` occupies two floating-point registers, so only four of them fit. + let max_members = if unit.kind == RegKind::DoubleDouble { 4 } else { 8 }; + + // Pass up to max_members uniquely addressable members. + if arg.layout.size > unit.size.checked_mul(max_members, cx).unwrap() { + return None; + } + } } let valid_unit = match unit.kind { RegKind::Integer => false, - RegKind::Float => true, + RegKind::Float | RegKind::DoubleDouble => true, RegKind::Vector { .. } => arg.layout.size.bits() == 128, }; diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 7e441d7100c39..2c7611e954945 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -46,6 +46,7 @@ fn classify<'a, Ty, C>( double_words[index] = DoubleWord::F128Start; double_words[index + 1] = DoubleWord::F128End; } + Float::PpcF128 => unreachable!(), Float::F64 => { double_words[index] = DoubleWord::F64; } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 142df37c2b7fe..a4bc7520c95fd 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,4 +1,6 @@ // tidy-alphabetical-start +#![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(powerpc_ppcf128))] +#![cfg_attr(any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc))] #![cfg_attr(not(panic = "abort"), feature(reentrant_lock))] #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![feature(array_ptr_get)] diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 0e003e5a9ec27..4283bd0242633 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -37,6 +37,8 @@ mod midpoint; mod nan; mod niche_types; mod ops; +#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] +mod ppcf128; mod wrapping; use floats::{assert_biteq, float_test}; diff --git a/library/coretests/tests/num/ppcf128.rs b/library/coretests/tests/num/ppcf128.rs new file mode 100644 index 0000000000000..e2a54c725b291 --- /dev/null +++ b/library/coretests/tests/num/ppcf128.rs @@ -0,0 +1,164 @@ +#[cfg(target_arch = "powerpc")] +use core::arch::powerpc::ppcf128; +#[cfg(target_arch = "powerpc64")] +use core::arch::powerpc64::ppcf128; +use std::assert_matches; + +const _: () = assert!(size_of::() == 16); +const _: () = assert!(align_of::() == 16); + +#[test] +fn constants() { + assert_matches!( + ppcf128::MIN.to_le_bytes(), + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, // + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xfc, + ] + ); + + assert_eq!( + ppcf128::MAX.to_le_bytes(), + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f, // + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0x7c, + ] + ); + + assert_eq!( + ppcf128::NAN.to_le_bytes(), + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ); + + assert_eq!( + ppcf128::INFINITY.to_le_bytes(), + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ); + + assert_eq!( + ppcf128::NEG_INFINITY.to_le_bytes(), + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + ); +} + +#[test] +fn impls() { + const ONE: ppcf128 = ppcf128::from_components(1.0, 0.0); + const TWO: ppcf128 = ppcf128::from_components(2.0, 0.0); + + assert_eq!(ONE, ONE); + assert_ne!(ONE, TWO); + + assert!(ONE <= ONE); + assert!(ONE < TWO); + assert!(TWO > ONE); + assert!(TWO >= TWO); + + assert!(ONE < ppcf128::INFINITY); + assert!(ONE > ppcf128::NEG_INFINITY); + + assert_ne!(ONE, ppcf128::NAN); + assert_ne!(ppcf128::NAN, ppcf128::NAN); + + assert_eq!(ppcf128::default().to_components(), (0.0, 0.0)); +} + +macro_rules! assert_eq_normalized { + (($large:expr, $small:expr) ==> ($expected_large:expr, $expected_small:expr)) => {{ + let (large, small) = ppcf128::from_components($large, $small).to_components(); + let (expected_large, expected_small): (f64, f64) = ($expected_large, $expected_small); + + if expected_large.is_nan() { + assert!(large.is_nan(), "expected NaN, got {large}"); + } else { + assert_eq!(large, expected_large); + } + + if expected_small.is_nan() { + assert!(small.is_nan(), "expected NaN, got {small}"); + } else { + assert_eq!(small, expected_small); + } + }}; +} + +#[test] +fn normalize() { + // Already normalized. + assert_eq_normalized!((1.0, 0.0) ==> (1.0, 0.0)); + assert_eq_normalized!((-1.0, 0.0) ==> (-1.0, 0.0)); + + // The value is normalized. + assert_eq_normalized!((1.0, 0.5) ==> (1.5, 0.0)); + assert_eq_normalized!((-1.0, 0.5) ==> (-0.5, 0.0)); + assert_eq_normalized!((0.0, 1.0) ==> (1.0, 0.0)); + assert_eq_normalized!((0.0, -1.0) ==> (-1.0, 0.0)); + + let large = 2.0f64.powi(53); + assert_eq_normalized!((large, -(large - 1.0)) ==> (1.0, 0.0)); + + // Exact cancellation. + assert_eq_normalized!((1.0, -1.0) ==> (0.0, 0.0)); + assert_eq_normalized!((-1.0, 1.0) ==> (0.0, 0.0)); + + // A component too small to affect the high part remains in the low part. + let half_ulp = f64::EPSILON / 2.0; + assert_eq_normalized!((1.0, half_ulp) ==> (1.0, half_ulp)); + assert_eq_normalized!((-1.0, -half_ulp) ==> (-1.0, -half_ulp)); + + assert_eq_normalized!((1.0, -half_ulp) ==> (1.0 - half_ulp, 0.0)); + assert_eq_normalized!((-1.0, half_ulp) ==> (-1.0 + half_ulp, 0.0)); + + // Rounding the sum produces a compensating low component. + let three_quarters_ulp = 3.0 * f64::EPSILON / 4.0; + assert_eq_normalized!( + (1.0, three_quarters_ulp) ==> + (1.0 + f64::EPSILON, -f64::EPSILON / 4.0) + ); + + // Normalization works across a large difference in exponent. + let large = 2.0f64.powi(100); + assert_eq_normalized!((1.0, large) ==> (large, 1.0)); + + // Infinity with a zero low component is already normalized. + assert_eq_normalized!((f64::INFINITY, 0.0) ==> (f64::INFINITY, 0.0)); + assert_eq_normalized!((f64::NEG_INFINITY, 0.0) ==> (f64::NEG_INFINITY, 0.0)); + + cfg_select! { + target_endian = "little" => { + // Covers powerpc64le (elfv2) + assert_eq_normalized!((f64::INFINITY, 1.0) ==> (f64::INFINITY, 0.0)); + assert_eq_normalized!((f64::NEG_INFINITY, 1.0) ==> (f64::NEG_INFINITY, 0.0)); + } + target_endian = "big" => { + // Covers powerpc64 (elfv1), powerpc and aix + assert_eq_normalized!((f64::INFINITY, 1.0) ==> (f64::INFINITY, 1.0)); + assert_eq_normalized!((f64::NEG_INFINITY, 1.0) ==> (f64::NEG_INFINITY, 1.0)); + } + } + + // A finite high component combined with infinity normalizes to infinity. + assert_eq_normalized!((1.0, f64::INFINITY) ==> (f64::INFINITY, 0.0)); + assert_eq_normalized!((1.0, f64::NEG_INFINITY) ==> (f64::NEG_INFINITY, 0.0)); + + // Opposite infinities produce NaN. + assert_eq_normalized!( + (f64::INFINITY, f64::NEG_INFINITY) ==> + (f64::NAN, 0.0) + ); + + assert_eq_normalized!((1.0, f64::NAN) ==> (f64::NAN, 0.0)); + assert_eq_normalized!((f64::NAN, 1.0) ==> (f64::NAN, 1.0)); + + assert_matches!(ppcf128::checked_from_components(1.0, 0.0), Some(_)); + assert_matches!(ppcf128::checked_from_components(0.0, 1.0), None); +} diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 55163fffedd78..155e3cef29736 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -41,6 +41,7 @@ movrs_target_feature, clflushopt_target_feature, min_adt_const_params + lang_items, )] #![cfg_attr(test, feature(test, abi_vectorcall, stdarch_internal))] #![deny(clippy::missing_inline_in_public_items)] diff --git a/library/stdarch/crates/core_arch/src/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index 53227215d946c..d6c1210cef5f3 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -2,6 +2,205 @@ pub(crate) mod macros; +/// The IBM extended-precision (double-double) floating-point type. +#[lang = "ppcf128"] +#[doc(alias = "__ibm128")] +#[doc(alias = "doubledouble")] +#[doc(alias = "f64f64")] +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +#[allow(non_camel_case_types)] +#[doc(cfg(any(target_arch = "powerpc", target_arch = "powerpc64")))] +pub struct ppcf128([u8; 16]); + +impl ppcf128 { + /// The size of this float type in bits. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + const BITS: u32 = 128; + + /// Smallest finite `ppcf128` value. + /// + /// Equal to −`MAX`. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const MIN: Self = + unsafe { Self::from_components_unchecked(f64::MIN, f64::MIN * f64::EPSILON / 4.0) }; + + /// Largest finite `ppcf128` value. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const MAX: Self = Self::from_components(f64::MAX, f64::MAX * f64::EPSILON / 4.0); + + /// Not a Number (NaN). + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const NAN: Self = Self::from_components(f64::NAN, 0.0); + + /// Infinity (∞). + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const INFINITY: Self = Self::from_components(f64::INFINITY, 0.0); + + /// Negative infinity (−∞). + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const NEG_INFINITY: Self = Self::from_components(f64::NEG_INFINITY, 0.0); + + /// Returns the memory representation of this floating point number as a byte array in + /// native byte order. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[inline] + pub const fn to_ne_bytes(self) -> [u8; 16] { + // SAFETY: every bit pattern of a `ppcf128` is a valid `[u8; 16]`. + unsafe { crate::mem::transmute::(self) } + } + + /// Returns the memory representation of this floating point number as a byte array in + /// little-endian byte order. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; 16] { + let mut bytes = self.to_ne_bytes(); + if cfg!(target_endian = "big") { + bytes[..8].reverse(); + bytes[8..].reverse(); + } + bytes + } + + /// Returns the memory representation of this floating point number as a byte array in + /// big-endian (network) byte order. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; 16] { + let mut bytes = self.to_ne_bytes(); + if cfg!(target_endian = "little") { + bytes[..8].reverse(); + bytes[8..].reverse(); + } + bytes + } + + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[inline] + pub const fn to_components(self) -> (f64, f64) { + let bytes = self.to_ne_bytes(); + let ([hi, lo], &[]) = bytes.as_chunks() else { + unreachable!() + }; + + cfg_select! { + target_endian = "little" => (f64::from_le_bytes(*hi), f64::from_le_bytes(*lo)), + target_endian = "big" => (f64::from_be_bytes(*hi), f64::from_be_bytes(*lo)), + } + } + + /// Check whether the large and small component are in normal form. + const fn is_normal_form(large: f64, small: f64) -> bool { + let is_elfv2 = cfg!(target_endian = "little"); + + if large.is_nan() { + true + } else if large.is_infinite() && is_elfv2 { + small == 0.0 + } else { + large.abs() > small.abs() && large + small == large + } + } + + /// Create a [`ppcf128`] from its large and small components. + /// + /// This function will normalize the components if they are not already in normal form. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const fn from_components(x: f64, y: f64) -> Self { + let (large, small) = if Self::is_normal_form(x, y) { + (x, y) + } else if !(x + y).is_finite() { + (x + y, 0.0) + } else { + let large = x + y; + // Per https://doi.org/10.1145/3121432, Algorithm 2 + let x1 = large - y; + let y1 = large - x1; + let x2 = x - x1; + let y2 = y - y1; + let small = x2 + y2; + debug_assert!(Self::is_normal_form(large, small)); + (large, small) + }; + + // SAFETY: the components are in normal form. + unsafe { Self::from_components_unchecked(large, small) } + } + + /// Create a [`ppcf128`] from its large and small components. + /// + /// # Safety + /// + /// This function is safe to call only when the large and small components are normalized. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const unsafe fn from_components_unchecked(large: f64, small: f64) -> Self { + unsafe { core::mem::transmute([large, small]) } + } + + /// Create a [`ppcf128`] from its large and small components. + /// + /// Returns `None` when the components are not in normal form. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + pub const fn checked_from_components(large: f64, small: f64) -> Option { + if Self::is_normal_form(large, small) { + // SAFETY: the components are in normal form. + Some(unsafe { Self::from_components_unchecked(large, small) }) + } else { + None + } + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +impl Clone for ppcf128 { + #[inline] + fn clone(&self) -> Self { + *self + } +} +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +impl Copy for ppcf128 {} + +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +impl Default for ppcf128 { + fn default() -> Self { + Self::from_components(0.0, 0.0) + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +impl crate::fmt::Debug for ppcf128 { + fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result { + let (hi, lo) = self.to_components(); + write!(f, "ppcf128({hi}, {lo})") + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +impl PartialEq for ppcf128 { + #[inline] + fn eq(&self, other: &ppcf128) -> bool { + *self == *other + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +impl PartialOrd for ppcf128 { + #[inline] + fn partial_cmp(&self, other: &ppcf128) -> Option { + use crate::cmp::Ordering; + match ((*self) <= (*other), (*self) >= (*other)) { + (false, false) => None, + (false, true) => Some(Ordering::Greater), + (true, false) => Some(Ordering::Less), + (true, true) => Some(Ordering::Equal), + } + } +} + mod altivec; #[unstable(feature = "stdarch_powerpc", issue = "111145")] pub use self::altivec::*; diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 47b701e3c42d7..a966d0d4e62c2 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1684,6 +1684,7 @@ pub(crate) enum PrimitiveType { F32, F64, F128, + PpcF128, Char, Bool, Str, @@ -1720,6 +1721,7 @@ impl PrimitiveType { hir::PrimTy::Float(FloatTy::F32) => PrimitiveType::F32, hir::PrimTy::Float(FloatTy::F64) => PrimitiveType::F64, hir::PrimTy::Float(FloatTy::F128) => PrimitiveType::F128, + hir::PrimTy::Float(FloatTy::PpcF128) => PrimitiveType::PpcF128, hir::PrimTy::Str => PrimitiveType::Str, hir::PrimTy::Bool => PrimitiveType::Bool, hir::PrimTy::Char => PrimitiveType::Char, @@ -1747,6 +1749,7 @@ impl PrimitiveType { sym::f32 => Some(PrimitiveType::F32), sym::f64 => Some(PrimitiveType::F64), sym::f128 => Some(PrimitiveType::F128), + sym::ppcf128 => Some(PrimitiveType::PpcF128), sym::array => Some(PrimitiveType::Array), sym::slice => Some(PrimitiveType::Slice), sym::tuple => Some(PrimitiveType::Tuple), @@ -1867,6 +1870,7 @@ impl PrimitiveType { F32 => sym::f32, F64 => sym::f64, F128 => sym::f128, + PpcF128 => sym::ppcf128, Str => sym::str, Bool => sym::bool, Char => sym::char, @@ -1954,6 +1958,7 @@ impl From for PrimitiveType { ty::FloatTy::F32 => PrimitiveType::F32, ty::FloatTy::F64 => PrimitiveType::F64, ty::FloatTy::F128 => PrimitiveType::F128, + ty::FloatTy::PpcF128 => PrimitiveType::PpcF128, } } } diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index ee211d56ccca2..e45b3c03be3e2 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -5,6 +5,7 @@ use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::attrs::RustcVersion; use rustc_hir::{HirId, Lit}; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; +use rustc_middle::bug; use rustc_span::{Span, symbol}; use std::f64::consts as f64; @@ -84,6 +85,7 @@ impl LateLintPass<'_> for ApproxConstant { FloatTy::F32 => self.check_known_consts(cx, lit.span, s, "f32"), FloatTy::F64 => self.check_known_consts(cx, lit.span, s, "f64"), FloatTy::F128 => self.check_known_consts(cx, lit.span, s, "f128"), + FloatTy::PpcF128 => bug!("there are no ppcf128 literals"), }, // FIXME(f16_f128): add `f16` and `f128` when these types become stable. LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, lit.span, s, "f{32, 64}"), diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs index ff5c7d3d5b3fe..497b9d9028365 100644 --- a/src/tools/clippy/clippy_lints/src/float_literal.rs +++ b/src/tools/clippy/clippy_lints/src/float_literal.rs @@ -5,6 +5,7 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, impl_lint_pass}; +use rustc_middle::bug; use rustc_middle::ty::{self, FloatTy}; use std::fmt; @@ -93,6 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { LitFloatType::Suffixed(FloatTy::F32) => Some("f32"), LitFloatType::Suffixed(FloatTy::F64) => Some("f64"), LitFloatType::Suffixed(FloatTy::F128) => Some("f128"), + LitFloatType::Suffixed(FloatTy::PpcF128) => bug!("there are no ppcf128 literals"), LitFloatType::Unsuffixed => None, }; let (is_whole, is_inf, mut float_str) = match fty { @@ -110,6 +112,9 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { (value.fract() == 0.0, value.is_infinite(), formatter.format(value)) }, + FloatTy::PpcF128 => { + bug!("there are no ppcf128 literals") + }, }; if is_inf { @@ -182,6 +187,7 @@ fn max_digits(fty: FloatTy) -> u32 { FloatTy::F32 => f32::DIGITS, FloatTy::F64 => f64::DIGITS, FloatTy::F128 => f128::DIGITS, + FloatTy::PpcF128 => bug!("there are no ppcf128 literals"), } } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index a32a641c34ee5..4d666775b3e2c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -393,6 +393,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { FloatTy::F32 => "F32", FloatTy::F64 => "F64", FloatTy::F128 => "F128", + FloatTy::PpcF128 => "PpcF128", }; format!("LitFloatType::Suffixed(FloatTy::{t})") }, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index bcdc7754da6fa..1606f2ec773b1 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -428,6 +428,7 @@ pub fn lit_to_mir_constant(lit: &LitKind, ty: Option>) -> Constant { FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()), FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()), FloatTy::F128 => Constant::parse_f128(is.as_str()), + FloatTy::PpcF128 => bug!("no ppcf128 literals"), }, LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() { ty::Float(FloatTy::F16) => Constant::parse_f16(is.as_str()), @@ -991,8 +992,9 @@ impl<'tcx> ConstEvalCtxt<'tcx> { let l = sext(self.tcx, l, ity); let r = sext(self.tcx, r, ity); - // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and - // the right-hand argument is -1 always panics, even with overflow-checks disabled + // Using / or %, where the left-hand argument is the smallest integer of a + // signed integer type and the right-hand argument is -1 + // always panics, even with overflow-checks disabled if let BinOpKind::Div | BinOpKind::Rem = op && l == ty_min_value && r == -1 @@ -1106,10 +1108,14 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> O (ConstValue::Scalar(Scalar::Int(int)), _) => match ty.kind() { ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)), ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.to_bits(int.size()))), - ty::Float(FloatTy::F16) => Some(Constant::F16(int.into())), - ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(int.into()))), - ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(int.into()))), - ty::Float(FloatTy::F128) => Some(Constant::F128(int.into())), + + ty::Float(float) => match float { + FloatTy::F16 => Some(Constant::F16(int.into())), + FloatTy::F32 => Some(Constant::F32(f32::from_bits(int.into()))), + FloatTy::F64 => Some(Constant::F64(f64::from_bits(int.into()))), + FloatTy::F128 => Some(Constant::F128(int.into())), + FloatTy::PpcF128 => None, + }, ty::RawPtr(_, _) => Some(Constant::RawPtr(int.to_bits(int.size()))), _ => None, }, @@ -1133,6 +1139,7 @@ pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, val: ConstValue, ty: Ty<'tcx>) -> O FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().discard_err()?)), FloatTy::F64 => Constant::F64(f64::from_bits(val.to_u64().discard_err()?)), FloatTy::F128 => Constant::F128(val.to_u128().discard_err()?), + FloatTy::PpcF128 => return None, }); } Some(Constant::Vec(res)) diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs index 637c8b089a9ea..6b87adda62eaa 100644 --- a/src/tools/miri/src/intrinsics/math.rs +++ b/src/tools/miri/src/intrinsics/math.rs @@ -127,11 +127,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ty::Float(fty) = x.layout.ty.kind() else { bug!("float_finite: non-float input type {}", x.layout.ty) }; + let target_endian = this.tcx.sess.target.options.endian; interp_ok(match fty { FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(), FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(), + FloatTy::PpcF128 => x.to_scalar().to_ppcf128(target_endian)?.is_finite(), }) }; match (float_finite(&a)?, float_finite(&b)?) { @@ -184,6 +186,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { FloatTy::F32 => host_unary_float_op::(this, f, op, dest)?, FloatTy::F64 => host_unary_float_op::(this, f, op, dest)?, FloatTy::F128 => todo!("f128"), // FIXME(f128) + FloatTy::PpcF128 => todo!("ppcf128"), // FIXME(ppcf128) }; } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index 1f2fd9a8a64df..756e3a17fc892 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -37,6 +37,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { FloatTy::F32 => math::sqrt_op::>(this, &op, &dest)?, FloatTy::F64 => math::sqrt_op::>(this, &op, &dest)?, FloatTy::F128 => math::sqrt_op::>(this, &op, &dest)?, + FloatTy::PpcF128 => { + span_bug!(this.cur_span(), "ppcf128 is not a valid vector element type") + } }; } } @@ -73,6 +76,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { FloatTy::F32 => host_unary_float_op::(this, &op, host_op, &dest)?, FloatTy::F64 => host_unary_float_op::(this, &op, host_op, &dest)?, FloatTy::F128 => unimplemented!("f128"), // FIXME(f128) + FloatTy::PpcF128 => { + span_bug!(this.cur_span(), "ppcf128 is not a valid vector element type") + } } } } diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 04564049dbed2..f269099da849f 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -109,6 +109,21 @@ impl Copy for *const T {} impl Copy for *mut T {} impl Copy for [T; N] {} +pub mod arch { + #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] + pub mod powerpc { + #[lang = "ppcf128"] + pub struct ppcf128([u8; 16]); + + impl crate::Copy for ppcf128 {} + } + + #[cfg(any(target_arch = "powerpc64"))] + pub mod powerpc64 { + pub use super::powerpc::*; + } +} + #[lang = "phantom_data"] pub struct PhantomData; impl Copy for PhantomData {} diff --git a/tests/codegen-llvm/powerpc-abi/ppcf128.rs b/tests/codegen-llvm/powerpc-abi/ppcf128.rs new file mode 100644 index 0000000000000..0856837ebd565 --- /dev/null +++ b/tests/codegen-llvm/powerpc-abi/ppcf128.rs @@ -0,0 +1,93 @@ +//@ add-minicore +// +//@ revisions: POWERPC POWERPC64LE POWERPC64 AIX +//@ [POWERPC] compile-flags: --target powerpc-unknown-linux-gnu +//@ [POWERPC64LE] compile-flags: --target powerpc64le-unknown-linux-gnu +//@ [POWERPC64] compile-flags: --target powerpc64-unknown-linux-gnu +//@ [AIX] compile-flags: --target powerpc64-ibm-aix +//@ compile-flags: -Copt-level=3 --crate-type=lib +//@ needs-llvm-components: powerpc + +#![feature(no_core)] +#![no_std] +#![no_core] + +extern crate minicore; +#[cfg(target_arch = "powerpc")] +use minicore::arch::powerpc::ppcf128; +#[cfg(target_arch = "powerpc64")] +use minicore::arch::powerpc64::ppcf128; +use minicore::*; + +/// On elfv1 and aix single-float structs are passed as scalar arguments. +#[repr(C)] +struct Hfa1 { + a: ppcf128, +} + +/// On elfv2 homogenous aggregates of up to 4 elements are passed as scalars. +#[repr(C)] +struct Hfa2 { + a: ppcf128, + b: ppcf128, +} + +#[repr(C)] +struct Hfa4 { + a: ppcf128, + b: ppcf128, + c: ppcf128, + d: ppcf128, +} + +#[repr(C)] +struct NonHfa5 { + a: ppcf128, + b: ppcf128, + c: ppcf128, + d: ppcf128, + e: ppcf128, +} + +// CHECK-LABEL: ppc_fp128 @scalar_second(ppc_fp128 noundef %_a, ppc_fp128 noundef returned %b) +#[unsafe(no_mangle)] +extern "C" fn scalar_second(_a: ppcf128, b: ppcf128) -> ppcf128 { + // CHECK: ret ppc_fp128 %b + b +} + +// POWERPC64-LABEL: void @hfa1(ptr {{.*}}sret([16 x i8]) {{.*}}, ppc_fp128 %0) +// POWERPC64LE-LABEL: ppc_fp128 @hfa1(ppc_fp128 returned %0) +// AIX-LABEL: void @hfa1(ptr {{.*}}sret([16 x i8]) {{.*}}, ptr {{.*}}byval([16 x i8]) {{.*}}) +// POWER-LABEL: void @hfa1(ptr {{.*}}sret([16 x i8]) {{.*}}, ptr {{.*}}byval([16 x i8]) {{.*}}) +#[unsafe(no_mangle)] +extern "C" fn hfa1(x: Hfa1) -> Hfa1 { + x +} + +// POWERPC64-LABEL: ppc_fp128 @hfa2([2 x i128] %0) +// POWERPC64LE-LABEL: ppc_fp128 @hfa2([2 x ppc_fp128] %0) +// AIX-LABEL: ppc_fp128 @hfa2(ptr {{.*}}byval([32 x i8]) +// POWER-LABEL: ppc_fp128 @hfa2(ptr {{.*}}byval([32 x i8]) +#[unsafe(no_mangle)] +extern "C" fn hfa2(x: Hfa2) -> ppcf128 { + x.b +} + +// POWERPC64-LABEL: ppc_fp128 @hfa4([4 x i128] %0) +// POWERPC64LE-LABEL: ppc_fp128 @hfa4([4 x ppc_fp128] %0) +// AIX-LABEL: ppc_fp128 @hfa4(ptr {{.*}}byval([64 x i8]) +// POWER-LABEL: ppc_fp128 @hfa4(ptr {{.*}}byval([64 x i8]) +#[unsafe(no_mangle)] +extern "C" fn hfa4(x: Hfa4) -> ppcf128 { + x.d +} + +// POWERPC64-LABEL: ppc_fp128 @non_hfa5([5 x i128] %0) +// POWERPC64LE-LABEL: ppc_fp128 @non_hfa5([5 x i128] %0) +// AIX-LABEL: ppc_fp128 @non_hfa5(ptr {{.*}}byval([80 x i8]) +// POWERPC-LABEL: ppc_fp128 @non_hfa5(ptr {{.*}}dereferenceable(80) +#[unsafe(no_mangle)] +extern "C" fn non_hfa5(x: NonHfa5) -> ppcf128 { + x.c +}