From 961481d1a8ea664e1420ab8f0b2c80e8d72b098c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 30 Aug 2026 15:47:04 +0200 Subject: [PATCH 1/6] powerpc `is_homogeneous_aggregate` cleanup --- .../rustc_target/src/callconv/powerpc64.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index d4617d8e7d693..9c51a3e80edaa 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -25,12 +25,19 @@ 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 => { + // Pass up to eight uniquely addressable members. + if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() { + return None; + } + } } let valid_unit = match unit.kind { From 150983cc0ed1423cdb82cab6f3ff6784dbc3f93e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 14 Aug 2026 17:08:30 +0200 Subject: [PATCH 2/6] add `Float::PpcF128` --- compiler/rustc_abi/src/layout/ty.rs | 6 +++--- compiler/rustc_abi/src/lib.rs | 4 ++++ compiler/rustc_attr_ir/src/lang_items.rs | 1 + compiler/rustc_codegen_cranelift/src/common.rs | 1 + compiler/rustc_codegen_gcc/src/type_.rs | 5 +++++ compiler/rustc_codegen_llvm/src/abi.rs | 1 + compiler/rustc_codegen_llvm/src/intrinsic.rs | 4 ++++ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_codegen_llvm/src/type_.rs | 4 ++++ compiler/rustc_codegen_llvm/src/va_arg.rs | 4 ++++ compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 1 + compiler/rustc_codegen_ssa/src/traits/type_.rs | 2 ++ compiler/rustc_middle/src/ty/layout.rs | 5 +++++ compiler/rustc_public/src/abi.rs | 2 ++ compiler/rustc_public/src/unstable/convert/stable/abi.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/callconv/mips64.rs | 3 ++- compiler/rustc_target/src/callconv/sparc64.rs | 1 + compiler/rustc_ty_utils/src/layout.rs | 7 +++++++ 19 files changed, 50 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index e51255dc5963f..b49923d13ec18 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -339,9 +339,9 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { let primitive = scalar.primitive(); match primitive { Primitive::Int(integer, is_signed) => Some(Numeric::Int(integer, is_signed)), - Primitive::Float(float @ (Float::F16 | Float::F32 | Float::F64 | Float::F128)) => { - Some(Numeric::Float(float)) - } + Primitive::Float( + float @ (Float::F16 | Float::F32 | Float::F64 | Float::F128 | Float::PpcF128), + ) => Some(Numeric::Float(float)), Primitive::Pointer(..) | Primitive::Float(Float::F16B) => None, } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index e336d2edca92e..2683d84e5b578 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1427,6 +1427,7 @@ pub enum Float { F32, F64, F128, + PpcF128, } impl Float { @@ -1439,6 +1440,7 @@ impl Float { F32 => Size::from_bits(32), F64 => Size::from_bits(64), F128 => Size::from_bits(128), + PpcF128 => Size::from_bits(128), } } @@ -1451,6 +1453,7 @@ impl Float { F32 => dl.f32_align, F64 => dl.f64_align, F128 => dl.f128_align, + PpcF128 => dl.f128_align, }) } @@ -1463,6 +1466,7 @@ impl Float { F32 => "f32", F64 => "f64", F128 => "f128", + PpcF128 => "ppcf128", } } } diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index 60890f7799290..d715a6d3af8c5 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, 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 30bd2f28af53f..94a71c65ce261 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -39,6 +39,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), diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 1d8582fe337ad..b77f336084feb 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -189,6 +189,11 @@ impl<'gcc, 'tcx> BaseTypeCodegenMethods for CodegenCx<'gcc, 'tcx> { bug!("unsupported float width 128") } + fn type_ppcf128(&self) -> Type<'gcc> { + // FIXME(ppcf128): GCC has long double but it may not correspond to ppc f128. + 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 dce8db6841b7f..58e6ef1cd9842 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -163,6 +163,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/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 2e01ebca05c26..19e6b505bab78 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -352,6 +352,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Primitive::Float(Float::F128) => { // Supported on some targets, especially where long double is IEEE 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) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 52218bfa336b8..74481f36cd3c7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -948,6 +948,7 @@ unsafe extern "C" { // Operations on non-IEEE real types pub(crate) fn LLVMBFloatTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMPPCFP128TypeInContext(C: &Context) -> &Type; // Operations on function types pub(crate) fn LLVMFunctionType<'a>( diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index ff1b5851db0f9..f03eb3d4a36fa 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -230,6 +230,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 2a02df05f26f5..204d24fb2b7a5 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::F16B | Float::F32 => unreachable!(), Float::F64 => { /* fall through */ } Float::F128 => return Align::from_bytes(16).unwrap(), + Float::PpcF128 => { /* fall through */ } }, Primitive::Pointer(_) => { /* fall through */ } }, @@ -471,6 +472,9 @@ fn emit_s390x_va_arg<'ll, 'tcx>( Primitive::Float(Float::F16B) => { bug!("`f16b` use in varadics unsupported on s390x") } + Primitive::Float(Float::PpcF128) => { + bug!("`ppcf128` 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 3162463d222ee..5fd7f10037494 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::F16 | Float::F32 => "f32", Float::F64 => "f64", Float::F128 => "i64, i64", + Float::PpcF128 => bug!("`ppcf128` unsupported on wasm"), }, 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 986481e7f132f..c9b5cb7142978 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -22,6 +22,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; @@ -72,6 +73,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_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f717f8270daf3..e591b3e28778c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -145,6 +145,11 @@ impl abi::Float { F32 => tcx.types.f32, F64 => tcx.types.f64, F128 => tcx.types.f128, + PpcF128 => Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::PpcF128, DUMMY_SP)), + ty::List::empty(), + ), } } diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 67d609c780c42..ed610097fd504 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -500,6 +500,7 @@ pub enum FloatLength { F32, F64, F128, + PpcF128, } impl IntegerLength { @@ -521,6 +522,7 @@ impl FloatLength { FloatLength::F32 => 32, FloatLength::F64 => 64, 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 3c268a6dd23a4..44b42e9b64de2 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -467,6 +467,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_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 098b6595f166e..405fe93c779bc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1619,6 +1619,7 @@ symbols! { powif32, powif64, powif128, + ppcf128, pre_dash_lto: "pre-lto", precise_capturing, precise_capturing_in_traits, diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 0e3ccf33c3ffe..9ce6f7b893ff6 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -31,10 +31,11 @@ where match float { // C does not have the f16 type Float::F16 => None, - Float::F16B => unreachable!("`f16b` unsupported on mips64"), Float::F32 => Some(Reg::f32()), Float::F64 => Some(Reg::f64()), Float::F128 => Some(Reg::f128()), + Float::F16B => unreachable!("`f16b` unsupported on mips64"), + Float::PpcF128 => unreachable!("`ppcf128` unsupported on mips64"), } } _ => None, diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index abef388ef2e12..22b388cf54101 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::PpcF128 => unreachable!("`ppcf128` unsupported on sparc64"), } } else { /* pass unaligned floats in integer registers */ diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index dee4b785e418d..282dac43ec576 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -812,6 +812,13 @@ fn layout_of_uncached<'tcx>( layout.backend_repr = BackendRepr::Scalar(bfloat); } + if tcx.is_lang_item(def.did(), LangItem::PpcF128) { + 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); + } + tcx.mk_layout(layout) } From 499549ac61735d42eec87e51ce0c472b4948eda2 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 14 Aug 2026 20:02:11 +0200 Subject: [PATCH 3/6] add `core::arch::powerpc::ppcf128` --- library/stdarch/crates/core_arch/src/lib.rs | 3 +- .../crates/core_arch/src/powerpc/mod.rs | 169 ++++++++++++++++++ tests/auxiliary/minicore.rs | 18 ++ 3 files changed, 189 insertions(+), 1 deletion(-) diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 55163fffedd78..c3225b3b1aa0f 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -40,7 +40,8 @@ maybe_uninit_as_bytes, movrs_target_feature, clflushopt_target_feature, - min_adt_const_params + 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..4b61affbc5288 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -2,6 +2,175 @@ 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(u128); + +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`](Self::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 = "none")] + #[inline] + pub const fn to_le_bytes(self) -> [u8; 16] { + let mut bytes = self.to_ne_bytes(); + if cfg!(target_endian = "big") { + bytes.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 = "none")] + #[inline] + pub const fn to_be_bytes(self) -> [u8; 16] { + let mut bytes = self.to_ne_bytes(); + if cfg!(target_endian = "little") { + bytes.reverse(); + } + bytes + } + + /// Returns the large and small component of this floating point number. + #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[inline] + pub const fn to_components(self) -> (f64, f64) { + // SAFETY: every bit pattern of a `ppcf128` is a valid `[f64; 2]`. + let [large, small] = unsafe { crate::mem::transmute::(self) }; + (large, small) + } + + /// 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!(all(target_arch = "powerpc64", target_endian = "little")); + + if large.is_nan() { + true + } else if large.is_infinite() && is_elfv2 { + small == 0.0 + } else { + 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 = "none")] + pub const fn from_components(mut large: f64, mut small: f64) -> Self { + (large, small) = if Self::is_normal_form(large, small) { + (large, small) + } else if !(large + small).is_finite() { + (large, 0.0) + } else { + let (x, y) = (large, small); + 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 = "none")] +impl PartialEq for ppcf128 { + #[inline] + fn eq(&self, other: &ppcf128) -> bool { + self.to_components() == other.to_components() + } +} + +#[unstable(feature = "powerpc_ppcf128", issue = "161787")] +impl PartialOrd for ppcf128 { + #[inline] + fn partial_cmp(&self, other: &ppcf128) -> Option { + self.to_components().partial_cmp(&other.to_components()) + } +} + mod altivec; #[unstable(feature = "stdarch_powerpc", issue = "111145")] pub use self::altivec::*; diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 2262c293af64a..e7e924f9ebc08 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -115,6 +115,24 @@ impl Copy for *const T {} impl Copy for *mut T {} impl Copy for [T; N] {} +pub mod arch { + #[cfg(target_arch = "powerpc")] + pub mod powerpc { + #[lang = "ppcf128"] + pub struct ppcf128(u128); + + impl crate::Copy for ppcf128 {} + } + + #[cfg(any(target_arch = "powerpc64"))] + pub mod powerpc64 { + #[lang = "ppcf128"] + pub struct ppcf128(u128); + + impl crate::Copy for ppcf128 {} + } +} + #[lang = "phantom_data"] pub struct PhantomData; impl Copy for PhantomData {} From e8caa74a33e5c42e58d69268dc1d0be93d33c467 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 14 Aug 2026 20:10:56 +0200 Subject: [PATCH 4/6] add `RegKind::PpcF128` --- compiler/rustc_abi/src/callconv.rs | 5 ++++- compiler/rustc_abi/src/callconv/reg.rs | 9 ++++++++- compiler/rustc_codegen_gcc/src/abi.rs | 1 + compiler/rustc_codegen_llvm/src/abi.rs | 1 + compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 1 + compiler/rustc_public/src/unstable/convert/stable/abi.rs | 1 + compiler/rustc_target/src/callconv/aarch64.rs | 2 +- compiler/rustc_target/src/callconv/arm.rs | 1 + compiler/rustc_target/src/callconv/powerpc64.rs | 9 ++++++--- 9 files changed, 24 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 4fda4735b613c..a4e6afd19664d 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::PpcF128, 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 a408aa034e785..aaaf69b54377d 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -1,13 +1,18 @@ #[cfg(feature = "nightly")] use rustc_macros::StableHash; -use crate::{Align, HasDataLayout, Integer, Primitive, Size}; +use crate::{Align, Float, HasDataLayout, Integer, Primitive, Size}; #[cfg_attr(feature = "nightly", derive(StableHash))] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] 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. + PpcF128, 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, @@ -20,6 +25,7 @@ impl RegKind { pub fn from_primitive(primitive: Primitive) -> Self { match primitive { Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer, + Primitive::Float(Float::PpcF128) => RegKind::PpcF128, Primitive::Float(_) => RegKind::Float, } } @@ -79,6 +85,7 @@ impl Reg { 128 => dl.f128_align, _ => panic!("unsupported float: {self:?}"), }, + RegKind::PpcF128 => dl.f128_align, RegKind::Vector { .. } => dl.rust_vector_align(self.size), } } diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 6a05f1cbbeef1..66ad54631c639 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::PpcF128 => cx.type_ppcf128(), RegKind::Vector { hint_vector_elem: _ } => { cx.type_vector(cx.type_i8(), self.size.bytes()) } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 58e6ef1cd9842..ebb5cc7ccdc96 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -145,6 +145,7 @@ impl LlvmType for Reg { 128 => cx.type_f128(), _ => bug!("unsupported float: {:?}", self), }, + RegKind::PpcF128 => 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, diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 5fd7f10037494..859d27e37c6e2 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::PpcF128 => bug!("not a valid wasm type"), RegKind::Vector { .. } => "v128", }; diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 44b42e9b64de2..ef0305aa48cdf 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -225,6 +225,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Reg { kind: match self.kind { rustc_abi::RegKind::Integer => RegKind::Integer, rustc_abi::RegKind::Float => RegKind::Float, + rustc_abi::RegKind::PpcF128 => RegKind::Float, rustc_abi::RegKind::Vector { .. } => RegKind::Vector, }, size: Size::from_bits(self.size.bits_usize()), diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index 09187836ee65a..354f3b1628525 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::PpcF128 => 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 615bd4f540068..f226dcdcd6cfa 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::PpcF128 => unreachable!(), RegKind::Vector { .. } => unit.size.bits() == 64 || unit.size.bits() == 128, }; diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 9c51a3e80edaa..f9cda375d674d 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -33,8 +33,11 @@ where } } ELFv2 => { - // Pass up to eight uniquely addressable members. - if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() { + // A `ppcf128` occupies two floating-point registers, so only four of them fit. + let max_members = if unit.kind == RegKind::PpcF128 { 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; } } @@ -42,7 +45,7 @@ where let valid_unit = match unit.kind { RegKind::Integer => false, - RegKind::Float => true, + RegKind::Float | RegKind::PpcF128 => true, RegKind::Vector { .. } => unit.size.bits() == 128, }; From bbce18b9114dc36ac3de9f71bf7bd1d423ba0009 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Sep 2026 18:35:24 +0200 Subject: [PATCH 5/6] add ppcf128 abi test --- tests/codegen-llvm/powerpc-abi/ppcf128.rs | 93 +++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tests/codegen-llvm/powerpc-abi/ppcf128.rs 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 +} From c6f8c21172a81b29ee2a222aed160c2a5e90ffee Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 30 Aug 2026 18:05:10 +0200 Subject: [PATCH 6/6] add core tests --- ...sroot_tests-128bit-atomic-operations.patch | 4 +- library/coretests/tests/lib.rs | 2 + library/coretests/tests/num/mod.rs | 2 + library/coretests/tests/num/ppcf128.rs | 166 ++++++++++++++++++ .../crates/core_arch/src/powerpc/mod.rs | 47 +++-- 5 files changed, 206 insertions(+), 15 deletions(-) create mode 100644 library/coretests/tests/num/ppcf128.rs 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/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 69bc6edd1b833..5833212e8318c 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 b1c3001790f07..dcc5e5e6f1211 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -38,6 +38,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..912830c8066e3 --- /dev/null +++ b/library/coretests/tests/num/ppcf128.rs @@ -0,0 +1,166 @@ +#[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). + // The small component of an infinity is normalized to zero. + assert_eq_normalized!((f64::INFINITY, 42.0) ==> (f64::INFINITY, 0.0)); + assert_eq_normalized!((f64::NEG_INFINITY, 42.0) ==> (f64::NEG_INFINITY, 0.0)); + } + target_endian = "big" => { + // Covers powerpc64 (elfv1), powerpc and aix. + // The small component of an infinity is untouched. + assert_eq_normalized!((f64::INFINITY, 42.0) ==> (f64::INFINITY, 42.0)); + assert_eq_normalized!((f64::NEG_INFINITY, 42.0) ==> (f64::NEG_INFINITY, 42.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/powerpc/mod.rs b/library/stdarch/crates/core_arch/src/powerpc/mod.rs index 4b61affbc5288..267c29d25f57e 100644 --- a/library/stdarch/crates/core_arch/src/powerpc/mod.rs +++ b/library/stdarch/crates/core_arch/src/powerpc/mod.rs @@ -51,30 +51,35 @@ impl ppcf128 { /// Returns the memory representation of this floating point number as a byte array in /// little-endian byte order. - #[unstable(feature = "powerpc_ppcf128", issue = "none")] + #[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.reverse(); + 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 = "none")] + #[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.reverse(); + bytes[..8].reverse(); + bytes[8..].reverse(); } bytes } /// Returns the large and small component of this floating point number. #[unstable(feature = "powerpc_ppcf128", issue = "161787")] + #[rustc_const_unstable(feature = "powerpc_ppcf128", issue = "161787")] #[inline] pub const fn to_components(self) -> (f64, f64) { // SAFETY: every bit pattern of a `ppcf128` is a valid `[f64; 2]`. @@ -84,28 +89,27 @@ impl ppcf128 { /// 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!(all(target_arch = "powerpc64", target_endian = "little")); + let is_elfv2 = cfg!(target_endian = "little"); if large.is_nan() { true } else if large.is_infinite() && is_elfv2 { small == 0.0 } else { - large + small == large + 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 = "none")] - pub const fn from_components(mut large: f64, mut small: f64) -> Self { - (large, small) = if Self::is_normal_form(large, small) { - (large, small) - } else if !(large + small).is_finite() { - (large, 0.0) + #[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 (x, y) = (large, small); let large = x + y; // Per https://doi.org/10.1145/3121432, Algorithm 2 let x1 = large - y; @@ -155,7 +159,22 @@ impl Clone for ppcf128 { #[unstable(feature = "powerpc_ppcf128", issue = "161787")] impl Copy for ppcf128 {} -#[unstable(feature = "powerpc_ppcf128", issue = "none")] +#[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 {