diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h index 93e32ba7cc941..d6a9f60f71208 100644 --- a/libc/src/__support/FPUtil/FPBits.h +++ b/libc/src/__support/FPUtil/FPBits.h @@ -31,6 +31,40 @@ enum class FPType { X86_Binary80, }; +// The classes hierarchy is as follows: +// +// ┌───────────────────┐ +// │ FPLayout │ +// └─────────▲─────────┘ +// │ +// ┌─────────┴─────────┐ +// │ FPRepBase │ +// └─────────▲─────────┘ +// │ +// ┌────────────┴─────────────┐ +// │ │ +// ┌────────┴──────┐ ┌─────────────┴──────────────┐ +// │ FPRep │ │ FPRep │ +// └───────────┘ +// +// - 'FPLayout' defines only a few constants, namely the 'StorageType' and the +// length of the sign, the exponent and significand parts. +// - 'FPRepBase' builds more constants on top of those from 'FPLayout' like +// exponent bias, shifts and masks. It also defines tools to assemble or test +// these parts. +// - 'FPRep' defines functions to interact with the floating point +// representation. The default implementation is the one for 'IEEE754', a +// specialization is provided for X86 Extended Precision that has a different +// encoding. +// - 'FPBits' is templated on the platform floating point types. Contrary to +// 'FPRep' that is platform agnostic 'FPBits' is architecture dependent. + namespace internal { // Defines the layout (sign, exponent, significand) of a floating point type in @@ -132,11 +166,94 @@ struct FPRepBase : public internal::FPLayout { static_assert((SIG_MASK & EXP_MASK & SIGN_MASK) == 0, "masks disjoint"); static_assert((SIG_MASK | EXP_MASK | SIGN_MASK) == FP_MASK, "masks cover"); -private: +protected: LIBC_INLINE static constexpr StorageType bit_at(int position) { return StorageType(1) << position; } + // An opaque type to store a floating point exponent. + // We define special values but it is valid to create arbitrary values as long + // as they are in the range [MIN, MAX]. + enum class Exponent : int32_t { + MIN = 1 - EXP_BIAS, + ZERO = 0, + MAX = EXP_BIAS, + }; + + // An opaque type to store a floating point biased exponent. + // We define special values but it is valid to create arbitrary values as long + // as they are in the range [BITS_ALL_ZEROES, BITS_ALL_ONES]. + // Values greater than BITS_ALL_ONES are truncated. + enum class BiasedExponent : uint32_t { + // The exponent value for denormal numbers. + BITS_ALL_ZEROES = 0, + // The exponent value for infinity. + BITS_ALL_ONES = 2 * EXP_BIAS + 1, + }; + + LIBC_INLINE static constexpr BiasedExponent biased(Exponent value) { + return static_cast(static_cast(value) + EXP_BIAS); + } + + // An opaque type to store a floating point significand. + // We define special values but it is valid to create arbitrary values as long + // as they are in the range [BITS_ALL_ZEROES, BITS_ALL_ONES]. + // Note that the semantics of the Significand are implementation dependent. + // Values greater than BITS_ALL_ONES are truncated. + enum class Significand : StorageType { + ZERO = 0, + LSB = 1, + MSB = bit_at(SIG_LEN - 1), + // Aliases + BITS_ALL_ZEROES = ZERO, + BITS_ALL_ONES = SIG_MASK, + }; + + template + LIBC_INLINE static constexpr auto storage_cast(T value) { + return static_cast(value); + } + + LIBC_INLINE friend constexpr Significand operator|(const Significand a, + const Significand b) { + return Significand{storage_cast(storage_cast(a) | storage_cast(b))}; + } + LIBC_INLINE friend constexpr Significand operator^(const Significand a, + const Significand b) { + return Significand{storage_cast(storage_cast(a) ^ storage_cast(b))}; + } + LIBC_INLINE friend constexpr Significand operator>>(const Significand a, + int shift) { + return Significand{storage_cast(storage_cast(a) >> shift)}; + } + + LIBC_INLINE static constexpr StorageType encode(BiasedExponent exp) { + return (storage_cast(exp) << SIG_LEN) & EXP_MASK; + } + + LIBC_INLINE static constexpr StorageType encode(Significand value) { + return storage_cast(value) & SIG_MASK; + } + + LIBC_INLINE static constexpr StorageType encode(BiasedExponent exp, + Significand sig) { + return encode(exp) | encode(sig); + } + + LIBC_INLINE static constexpr StorageType encode(bool sign, BiasedExponent exp, + Significand sig) { + if (sign) + return SIGN_MASK | encode(exp, sig); + return encode(exp, sig); + } + + LIBC_INLINE constexpr StorageType exp_bits() const { return bits & EXP_MASK; } + LIBC_INLINE constexpr StorageType sig_bits() const { return bits & SIG_MASK; } + LIBC_INLINE constexpr StorageType exp_sig_bits() const { + return bits & EXP_SIG_MASK; + } + +private: // Merge bits from 'a' and 'b' values according to 'mask'. // Use 'a' bits when corresponding 'mask' bits are zeroes and 'b' bits when // corresponding bits are ones. @@ -155,20 +272,6 @@ struct FPRepBase : public internal::FPLayout { LIBC_INLINE_VAR static constexpr StorageType FRACTION_MASK = mask_trailing_ones(); - // If a number x is a NAN, then it is a quiet NAN if: - // QUIET_NAN_MASK & bits(x) != 0 - LIBC_INLINE_VAR static constexpr StorageType QUIET_NAN_MASK = - fp_type == FPType::X86_Binary80 - ? bit_at(SIG_LEN - 1) | bit_at(SIG_LEN - 2) // 0b1100... - : bit_at(SIG_LEN - 1); // 0b1000... - - // Mask to generate a default signaling NAN. Any NAN that is not - // a quiet NAN is considered a signaling NAN. - LIBC_INLINE_VAR static constexpr StorageType DEFAULT_SIGNALING_NAN = - fp_type == FPType::X86_Binary80 - ? bit_at(SIG_LEN - 1) | bit_at(SIG_LEN - 3) // 0b1010... - : bit_at(SIG_LEN - 2); // 0b0100... - // The floating point number representation as an unsigned integer. StorageType bits = 0; @@ -220,6 +323,9 @@ struct FPRepBase : public internal::FPLayout { } LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; } + LIBC_INLINE constexpr void set_uintval(StorageType value) { + bits = (value & FP_MASK); + } LIBC_INLINE constexpr bool is_zero() const { return (bits & EXP_SIG_MASK) == 0; @@ -241,6 +347,213 @@ template struct FPRep : public FPRepBase { using UP::FRACTION_LEN; using UP::FRACTION_MASK; using UP::MANTISSA_PRECISION; + +protected: + using typename UP::BiasedExponent; + using typename UP::Exponent; + using typename UP::Significand; + using UP::biased; + using UP::encode; + using UP::exp_bits; + using UP::exp_sig_bits; + using UP::sig_bits; + +public: + LIBC_INLINE constexpr bool is_nan() const { + return exp_sig_bits() > + encode(BiasedExponent::BITS_ALL_ONES, Significand::ZERO); + } + LIBC_INLINE constexpr bool is_quiet_nan() const { + return exp_sig_bits() >= + encode(BiasedExponent::BITS_ALL_ONES, Significand::MSB); + } + LIBC_INLINE constexpr bool is_signaling_nan() const { + return is_nan() && !is_quiet_nan(); + } + LIBC_INLINE constexpr bool is_inf() const { + return exp_sig_bits() == + encode(BiasedExponent::BITS_ALL_ONES, Significand::ZERO); + } + LIBC_INLINE constexpr bool is_zero() const { + return exp_sig_bits() == + encode(BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO); + } + LIBC_INLINE constexpr bool is_finite() const { + return exp_bits() != encode(BiasedExponent::BITS_ALL_ONES); + } + LIBC_INLINE + constexpr bool is_subnormal() const { + return exp_bits() == encode(BiasedExponent::BITS_ALL_ZEROES); + } + LIBC_INLINE constexpr bool is_normal() const { + return is_finite() && !is_subnormal(); + } + + LIBC_INLINE static constexpr StorageType zero(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO); + } + LIBC_INLINE static constexpr StorageType one(bool sign = false) { + return encode(sign, biased(Exponent::ZERO), Significand::ZERO); + } + LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::LSB); + } + LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ZEROES, + Significand::BITS_ALL_ONES); + } + LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) { + return encode(sign, biased(Exponent::MIN), Significand::ZERO); + } + LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) { + return encode(sign, biased(Exponent::MAX), Significand::BITS_ALL_ONES); + } + LIBC_INLINE static constexpr StorageType inf(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ONES, Significand::ZERO); + } + LIBC_INLINE static constexpr StorageType build_nan(bool sign = false, + StorageType v = 0) { + return encode(sign, BiasedExponent::BITS_ALL_ONES, + (v ? Significand{v} : (Significand::MSB >> 1))); + } + LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false, + StorageType v = 0) { + return encode(sign, BiasedExponent::BITS_ALL_ONES, + Significand::MSB | Significand{v}); + } + + // The function return mantissa with the implicit bit set iff the current + // value is a valid normal number. + LIBC_INLINE constexpr StorageType get_explicit_mantissa() { + if (is_subnormal()) + return sig_bits(); + return (StorageType(1) << UP::SIG_LEN) | sig_bits(); + } +}; + +// Specialization for the X86 Extended Precision type. +template <> +struct FPRep : public FPRepBase { + using UP = FPRepBase; + using typename UP::StorageType; + using UP::FRACTION_LEN; + using UP::FRACTION_MASK; + using UP::MANTISSA_PRECISION; + +protected: + using typename UP::BiasedExponent; + using typename UP::Significand; + using UP::encode; + +public: + // The x86 80 bit float represents the leading digit of the mantissa + // explicitly. This is the mask for that bit. + static constexpr StorageType EXPLICIT_BIT_MASK = StorageType(1) + << FRACTION_LEN; + // The X80 significand is made of an explicit bit and the fractional part. + static_assert((EXPLICIT_BIT_MASK & FRACTION_MASK) == 0, + "the explicit bit and the fractional part should not overlap"); + static_assert((EXPLICIT_BIT_MASK | FRACTION_MASK) == SIG_MASK, + "the explicit bit and the fractional part should cover the " + "whole significand"); + + LIBC_INLINE constexpr bool is_nan() const { + // Most encoding forms from the table found in + // https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format + // are interpreted as NaN. + // More precisely : + // - Pseudo-Infinity + // - Pseudo Not a Number + // - Signalling Not a Number + // - Floating-point Indefinite + // - Quiet Not a Number + // - Unnormal + // This can be reduced to the following logic: + if (exp_bits() == encode(BiasedExponent::BITS_ALL_ONES)) + return !is_inf(); + if (exp_bits() != encode(BiasedExponent::BITS_ALL_ZEROES)) + return (sig_bits() & encode(Significand::MSB)) == 0; + return false; + } + LIBC_INLINE constexpr bool is_quiet_nan() const { + return exp_sig_bits() >= encode(BiasedExponent::BITS_ALL_ONES, + Significand::MSB | (Significand::MSB >> 1)); + } + LIBC_INLINE constexpr bool is_signaling_nan() const { + return is_nan() && !is_quiet_nan(); + } + LIBC_INLINE constexpr bool is_inf() const { + return exp_sig_bits() == + encode(BiasedExponent::BITS_ALL_ONES, Significand::MSB); + } + LIBC_INLINE constexpr bool is_zero() const { + return exp_sig_bits() == + encode(BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO); + } + LIBC_INLINE constexpr bool is_finite() const { + return !is_inf() && !is_nan(); + } + LIBC_INLINE + constexpr bool is_subnormal() const { + return exp_sig_bits() > + encode(BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO); + } + LIBC_INLINE constexpr bool is_normal() const { + const auto exp = exp_bits(); + if (exp == encode(BiasedExponent::BITS_ALL_ZEROES) || + exp == encode(BiasedExponent::BITS_ALL_ONES)) + return false; + return get_implicit_bit(); + } + + LIBC_INLINE static constexpr StorageType zero(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO); + } + LIBC_INLINE static constexpr StorageType one(bool sign = false) { + return encode(sign, biased(Exponent::ZERO), Significand::MSB); + } + LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::LSB); + } + LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ZEROES, + Significand::BITS_ALL_ONES ^ Significand::MSB); + } + LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) { + return encode(sign, biased(Exponent::MIN), Significand::MSB); + } + LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) { + return encode(sign, biased(Exponent::MAX), Significand::BITS_ALL_ONES); + } + LIBC_INLINE static constexpr StorageType inf(bool sign = false) { + return encode(sign, BiasedExponent::BITS_ALL_ONES, Significand::MSB); + } + LIBC_INLINE static constexpr StorageType build_nan(bool sign = false, + StorageType v = 0) { + return encode(sign, BiasedExponent::BITS_ALL_ONES, + Significand::MSB | + (v ? Significand{v} : (Significand::MSB >> 2))); + } + LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false, + StorageType v = 0) { + return encode(sign, BiasedExponent::BITS_ALL_ONES, + Significand::MSB | (Significand::MSB >> 1) | Significand{v}); + } + + LIBC_INLINE constexpr StorageType get_explicit_mantissa() const { + return sig_bits(); + } + + // The following functions are specific to FPRep. + // TODO: Remove if possible. + LIBC_INLINE constexpr bool get_implicit_bit() const { + return bits & EXPLICIT_BIT_MASK; + } + + LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) { + if (get_implicit_bit() != implicitVal) + bits ^= EXPLICIT_BIT_MASK; + } }; } // namespace internal @@ -276,47 +589,29 @@ template LIBC_INLINE static constexpr FPType get_fp_type() { static_assert(cpp::always_false, "Unsupported type"); } -// A generic class to represent single precision, double precision, and quad -// precision IEEE 754 floating point formats. +// A generic class to represent floating point formats. // On most platforms, the 'float' type corresponds to single precision floating // point numbers, the 'double' type corresponds to double precision floating // point numers, and the 'long double' type corresponds to the quad precision // floating numbers. On x86 platforms however, the 'long double' type maps to -// an x87 floating point format. This format is an IEEE 754 extension format. -// It is handled as an explicit specialization of this class. +// an x87 floating point format. template struct FPBits : public internal::FPRep()> { static_assert(cpp::is_floating_point_v, "FPBits instantiated with invalid type."); using UP = internal::FPRep()>; - -private: - using UP::EXP_SIG_MASK; - using UP::QUIET_NAN_MASK; - using UP::SIG_LEN; - using UP::SIG_MASK; - -public: + using Rep = UP; using StorageType = typename UP::StorageType; + using UP::bits; - using UP::EXP_BIAS; using UP::EXP_LEN; - using UP::EXP_MASK; - using UP::EXP_MASK_SHIFT; - using UP::FRACTION_LEN; - using UP::FRACTION_MASK; - using UP::SIGN_MASK; - using UP::TOTAL_LEN; using UP::UP; - using UP::get_biased_exponent; - using UP::is_zero; // Constants. static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1; - static constexpr StorageType MIN_SUBNORMAL = StorageType(1); - static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK; - static constexpr StorageType MIN_NORMAL = (StorageType(1) << FRACTION_LEN); - static constexpr StorageType MAX_NORMAL = - (StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK; + static constexpr StorageType MIN_NORMAL = UP::min_normal(false); + static constexpr StorageType MAX_NORMAL = UP::max_normal(false); + static constexpr StorageType MIN_SUBNORMAL = UP::min_subnormal(false); + static constexpr StorageType MAX_SUBNORMAL = UP::max_subnormal(false); // Constructors. LIBC_INLINE constexpr FPBits() = default; @@ -338,88 +633,56 @@ template struct FPBits : public internal::FPRep()> { LIBC_INLINE constexpr explicit operator T() const { return get_val(); } - // The function return mantissa with the implicit bit set iff the current - // value is a valid normal number. - LIBC_INLINE constexpr StorageType get_explicit_mantissa() { - return ((get_biased_exponent() > 0 && !is_inf_or_nan()) - ? (FRACTION_MASK + 1) - : 0) | - (FRACTION_MASK & bits); - } - - LIBC_INLINE constexpr bool is_inf() const { - return (bits & EXP_SIG_MASK) == EXP_MASK; - } - - LIBC_INLINE constexpr bool is_nan() const { - return (bits & EXP_SIG_MASK) > EXP_MASK; - } - - LIBC_INLINE constexpr bool is_quiet_nan() const { - return (bits & EXP_SIG_MASK) >= (EXP_MASK | QUIET_NAN_MASK); - } - - LIBC_INLINE constexpr bool is_inf_or_nan() const { - return (bits & EXP_MASK) == EXP_MASK; - } + LIBC_INLINE constexpr bool is_inf_or_nan() const { return !UP::is_finite(); } LIBC_INLINE constexpr FPBits abs() const { - return FPBits(bits & EXP_SIG_MASK); + return FPBits(bits & UP::EXP_SIG_MASK); } // Methods below this are used by tests. LIBC_INLINE static constexpr T zero(bool sign = false) { - StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign - | 0 // exponent - | 0; // mantissa - return FPBits(rep).get_val(); + return FPBits(UP::zero(sign)).get_val(); } LIBC_INLINE static constexpr T neg_zero() { return zero(true); } LIBC_INLINE static constexpr T inf(bool sign = false) { - StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign - | EXP_MASK // exponent - | 0; // mantissa - return FPBits(rep).get_val(); + return FPBits(UP::inf(sign)).get_val(); } LIBC_INLINE static constexpr T neg_inf() { return inf(true); } LIBC_INLINE static constexpr T min_normal() { - return FPBits(MIN_NORMAL).get_val(); + return FPBits(UP::min_normal(false)).get_val(); } LIBC_INLINE static constexpr T max_normal() { - return FPBits(MAX_NORMAL).get_val(); + return FPBits(UP::max_normal(false)).get_val(); } LIBC_INLINE static constexpr T min_denormal() { - return FPBits(MIN_SUBNORMAL).get_val(); + return FPBits(UP::min_subnormal(false)).get_val(); } LIBC_INLINE static constexpr T max_denormal() { - return FPBits(MAX_SUBNORMAL).get_val(); + return FPBits(UP::max_subnormal(false)).get_val(); } LIBC_INLINE static constexpr T build_nan(StorageType v) { - StorageType rep = 0 // sign - | EXP_MASK // exponent - | (v & FRACTION_MASK); // mantissa - return FPBits(rep).get_val(); + return FPBits(UP::build_nan(false, v)).get_val(); } LIBC_INLINE static constexpr T build_quiet_nan(StorageType v) { - return build_nan(QUIET_NAN_MASK | v); + return FPBits(UP::build_quiet_nan(false, v)).get_val(); } LIBC_INLINE static constexpr FPBits create_value(bool sign, StorageType biased_exp, StorageType mantissa) { - StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign - | ((biased_exp << EXP_MASK_SHIFT) & EXP_MASK) // exponent - | (mantissa & FRACTION_MASK); // mantissa - return FPBits(rep); + static_assert(get_fp_type() != FPType::X86_Binary80, + "This function is not tested for X86 Extended Precision"); + return FPBits(UP::encode(sign, typename UP::BiasedExponent(biased_exp), + typename UP::Significand(mantissa))); } // The function convert integer number and unbiased exponent to proper float @@ -434,6 +697,8 @@ template struct FPBits : public internal::FPRep()> { // 5) Number is unsigned, so the result can be only positive. LIBC_INLINE static constexpr FPBits make_value(StorageType number, int ep) { + static_assert(get_fp_type() != FPType::X86_Binary80, + "This function is not tested for X86 Extended Precision"); FPBits result; // offset: +1 for sign, but -1 for implicit first bit int lz = cpp::countl_zero(number) - EXP_LEN; @@ -454,8 +719,4 @@ template struct FPBits : public internal::FPRep()> { } // namespace fputil } // namespace LIBC_NAMESPACE -#ifdef LIBC_LONG_DOUBLE_IS_X86_FLOAT80 -#include "x86_64/LongDoubleBits.h" -#endif - #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_FPBITS_H diff --git a/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h b/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h index 257c02e17d004..8815a18cfbc39 100644 --- a/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h +++ b/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h @@ -131,7 +131,7 @@ LIBC_INLINE long double sqrt(long double x) { out.set_implicit_bit(1); out.set_mantissa((y & (ONE - 1))); - return out; + return out.get_val(); } } #endif // LIBC_LONG_DOUBLE_IS_X86_FLOAT80 diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h deleted file mode 100644 index c18abcee77ea5..0000000000000 --- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h +++ /dev/null @@ -1,179 +0,0 @@ -//===-- Bit representation of x86 long double numbers -----------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_LONGDOUBLEBITS_H -#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_LONGDOUBLEBITS_H - -#include "src/__support/CPP/bit.h" -#include "src/__support/UInt128.h" -#include "src/__support/common.h" -#include "src/__support/macros/attributes.h" // LIBC_INLINE -#include "src/__support/macros/properties/architectures.h" - -#if !defined(LIBC_TARGET_ARCH_IS_X86) -#error "Invalid include" -#endif - -#include "src/__support/FPUtil/FPBits.h" - -#include - -namespace LIBC_NAMESPACE { -namespace fputil { - -template <> -struct FPBits : public internal::FPRep { - using UP = internal::FPRep; - using StorageType = typename UP::StorageType; - -private: - using UP::bits; - using UP::EXP_SIG_MASK; - using UP::QUIET_NAN_MASK; - -public: - // Constants. - static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1; - // The x86 80 bit float represents the leading digit of the mantissa - // explicitly. This is the mask for that bit. - static constexpr StorageType EXPLICIT_BIT_MASK = StorageType(1) - << FRACTION_LEN; - // The X80 significand is made of an explicit bit and the fractional part. - static_assert((EXPLICIT_BIT_MASK & FRACTION_MASK) == 0, - "the explicit bit and the fractional part should not overlap"); - static_assert((EXPLICIT_BIT_MASK | FRACTION_MASK) == SIG_MASK, - "the explicit bit and the fractional part should cover the " - "whole significand"); - static constexpr StorageType MIN_SUBNORMAL = StorageType(1); - // Subnormal numbers include the implicit bit in x86 long double formats. - static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK; - static constexpr StorageType MIN_NORMAL = - (StorageType(1) << SIG_LEN) | EXPLICIT_BIT_MASK; - static constexpr StorageType MAX_NORMAL = - (StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK; - - // Constructors. - LIBC_INLINE constexpr FPBits() = default; - - template LIBC_INLINE constexpr explicit FPBits(XType x) { - using Unqual = typename cpp::remove_cv_t; - if constexpr (cpp::is_same_v) { - bits = cpp::bit_cast(x); - } else if constexpr (cpp::is_same_v) { - bits = x; - } else { - // We don't want accidental type promotions/conversions, so we require - // exact type match. - static_assert(cpp::always_false); - } - } - - // Floating-point conversions. - LIBC_INLINE constexpr long double get_val() const { - return cpp::bit_cast(bits); - } - - LIBC_INLINE constexpr operator long double() const { - return cpp::bit_cast(bits); - } - - LIBC_INLINE constexpr StorageType get_explicit_mantissa() const { - return bits & SIG_MASK; - } - - LIBC_INLINE constexpr bool get_implicit_bit() const { - return bits & EXPLICIT_BIT_MASK; - } - - LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) { - if (get_implicit_bit() != implicitVal) - bits ^= EXPLICIT_BIT_MASK; - } - - LIBC_INLINE constexpr bool is_inf() const { - return get_biased_exponent() == MAX_BIASED_EXPONENT && - get_mantissa() == 0 && get_implicit_bit() == 1; - } - - LIBC_INLINE constexpr bool is_nan() const { - if (get_biased_exponent() == MAX_BIASED_EXPONENT) { - return (get_implicit_bit() == 0) || get_mantissa() != 0; - } else if (get_biased_exponent() != 0) { - return get_implicit_bit() == 0; - } - return false; - } - - LIBC_INLINE constexpr bool is_inf_or_nan() const { - return (get_biased_exponent() == MAX_BIASED_EXPONENT) || - (get_biased_exponent() != 0 && get_implicit_bit() == 0); - } - - LIBC_INLINE constexpr bool is_quiet_nan() const { - return (bits & EXP_SIG_MASK) >= (EXP_MASK | QUIET_NAN_MASK); - } - - // Methods below this are used by tests. - - LIBC_INLINE static constexpr long double zero(bool sign = false) { - StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign - | 0 // exponent - | 0 // explicit bit - | 0; // mantissa - return FPBits(rep).get_val(); - } - - LIBC_INLINE static constexpr long double neg_zero() { return zero(true); } - - LIBC_INLINE static constexpr long double inf(bool sign = false) { - StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign - | EXP_MASK // exponent - | EXPLICIT_BIT_MASK // explicit bit - | 0; // mantissa - return FPBits(rep).get_val(); - } - - LIBC_INLINE static constexpr long double neg_inf() { return inf(true); } - - LIBC_INLINE static constexpr long double min_normal() { - return FPBits(MIN_NORMAL).get_val(); - } - - LIBC_INLINE static constexpr long double max_normal() { - return FPBits(MAX_NORMAL).get_val(); - } - - LIBC_INLINE static constexpr long double min_denormal() { - return FPBits(MIN_SUBNORMAL).get_val(); - } - - LIBC_INLINE static constexpr long double max_denormal() { - return FPBits(MAX_SUBNORMAL).get_val(); - } - - LIBC_INLINE static constexpr long double build_nan(StorageType v) { - StorageType rep = 0 // sign - | EXP_MASK // exponent - | EXPLICIT_BIT_MASK // explicit bit - | (v & FRACTION_MASK); // mantissa - return FPBits(rep).get_val(); - } - - LIBC_INLINE static constexpr long double build_quiet_nan(StorageType v) { - return build_nan(QUIET_NAN_MASK | v); - } -}; - -static_assert( - sizeof(FPBits) == sizeof(long double), - "Internal long double representation does not match the machine format."); - -} // namespace fputil -} // namespace LIBC_NAMESPACE - -#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_X86_64_LONGDOUBLEBITS_H diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h index b461da3a4c0ab..5f15bac5df77f 100644 --- a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h +++ b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h @@ -61,7 +61,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) { from_bits.set_biased_exponent(from_bits.get_biased_exponent() + 1); if (from_bits.is_inf()) raise_except_if_required(FE_OVERFLOW | FE_INEXACT); - return from_bits; + return from_bits.get_val(); } else { ++int_val; } @@ -75,7 +75,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) { // from == 0 is handled separately so decrementing the exponent will not // lead to underflow. from_bits.set_biased_exponent(from_bits.get_biased_exponent() - 1); - return from_bits; + return from_bits.get_val(); } else { --int_val; } @@ -94,7 +94,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) { // from == 0 is handled separately so decrementing the exponent will not // lead to underflow. from_bits.set_biased_exponent(from_bits.get_biased_exponent() - 1); - return from_bits; + return from_bits.get_val(); } else { --int_val; } @@ -109,7 +109,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) { from_bits.set_biased_exponent(from_bits.get_biased_exponent() + 1); if (from_bits.is_inf()) raise_except_if_required(FE_OVERFLOW | FE_INEXACT); - return from_bits; + return from_bits.get_val(); } else { ++int_val; } diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp index e2dbe248ef213..3a452f0b7993c 100644 --- a/libc/test/src/__support/FPUtil/fpbits_test.cpp +++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp @@ -12,6 +12,219 @@ using LIBC_NAMESPACE::fputil::FPBits; +TEST(LlvmLibcFPBitsTest, FPType_IEEE754_Binary16) { + using LIBC_NAMESPACE::fputil::FPType; + using LIBC_NAMESPACE::fputil::internal::FPRep; + using Rep = FPRep; + using u16 = uint16_t; + + EXPECT_EQ(u16(0b0'00000'0000000000), Rep::zero()); + EXPECT_EQ(u16(0b0'01111'0000000000), Rep::one()); + EXPECT_EQ(u16(0b0'00000'0000000001), Rep::min_subnormal()); + EXPECT_EQ(u16(0b0'00000'1111111111), Rep::max_subnormal()); + EXPECT_EQ(u16(0b0'00001'0000000000), Rep::min_normal()); + EXPECT_EQ(u16(0b0'11110'1111111111), Rep::max_normal()); + EXPECT_EQ(u16(0b0'11111'0000000000), Rep::inf()); + EXPECT_EQ(u16(0b0'11111'0100000000), Rep::build_nan()); + EXPECT_EQ(u16(0b0'11111'1000000000), Rep::build_quiet_nan()); +} + +TEST(LlvmLibcFPBitsTest, FPType_IEEE754_Binary32) { + using LIBC_NAMESPACE::fputil::FPType; + using LIBC_NAMESPACE::fputil::internal::FPRep; + using Rep = FPRep; + using u32 = uint32_t; + + EXPECT_EQ(u32(0b0'00000000'00000000000000000000000), Rep::zero()); + EXPECT_EQ(u32(0b0'01111111'00000000000000000000000), Rep::one()); + EXPECT_EQ(u32(0b0'00000000'00000000000000000000001), Rep::min_subnormal()); + EXPECT_EQ(u32(0b0'00000000'11111111111111111111111), Rep::max_subnormal()); + EXPECT_EQ(u32(0b0'00000001'00000000000000000000000), Rep::min_normal()); + EXPECT_EQ(u32(0b0'11111110'11111111111111111111111), Rep::max_normal()); + EXPECT_EQ(u32(0b0'11111111'00000000000000000000000), Rep::inf()); + EXPECT_EQ(u32(0b0'11111111'01000000000000000000000), Rep::build_nan()); + EXPECT_EQ(u32(0b0'11111111'10000000000000000000000), Rep::build_quiet_nan()); +} + +TEST(LlvmLibcFPBitsTest, FPType_IEEE754_Binary64) { + using LIBC_NAMESPACE::fputil::FPType; + using LIBC_NAMESPACE::fputil::internal::FPRep; + using Rep = FPRep; + using u64 = uint64_t; + + EXPECT_EQ( + u64(0b0'00000000000'0000000000000000000000000000000000000000000000000000), + Rep::zero()); + EXPECT_EQ( + u64(0b0'01111111111'0000000000000000000000000000000000000000000000000000), + Rep::one()); + EXPECT_EQ( + u64(0b0'00000000000'0000000000000000000000000000000000000000000000000001), + Rep::min_subnormal()); + EXPECT_EQ( + u64(0b0'00000000000'1111111111111111111111111111111111111111111111111111), + Rep::max_subnormal()); + EXPECT_EQ( + u64(0b0'00000000001'0000000000000000000000000000000000000000000000000000), + Rep::min_normal()); + EXPECT_EQ( + u64(0b0'11111111110'1111111111111111111111111111111111111111111111111111), + Rep::max_normal()); + EXPECT_EQ( + u64(0b0'11111111111'0000000000000000000000000000000000000000000000000000), + Rep::inf()); + EXPECT_EQ( + u64(0b0'11111111111'0100000000000000000000000000000000000000000000000000), + Rep::build_nan()); + EXPECT_EQ( + u64(0b0'11111111111'1000000000000000000000000000000000000000000000000000), + Rep::build_quiet_nan()); +} + +static constexpr UInt128 u128(uint64_t hi, uint64_t lo) { +#if defined(__SIZEOF_INT128__) + return __uint128_t(hi) << 64 | __uint128_t(lo); +#else + return UInt128({hi, lo}); +#endif +} + +TEST(LlvmLibcFPBitsTest, FPType_X86_Binary80) { + using LIBC_NAMESPACE::fputil::FPType; + using LIBC_NAMESPACE::fputil::internal::FPRep; + using Rep = FPRep; + + EXPECT_EQ( + u128(0b0'000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000000), + Rep::zero()); + EXPECT_EQ( + u128(0b0'011111111111111, + 0b1000000000000000000000000000000000000000000000000000000000000000), + Rep::one()); + EXPECT_EQ( + u128(0b0'000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000001), + Rep::min_subnormal()); + EXPECT_EQ( + u128(0b0'000000000000000, + 0b0111111111111111111111111111111111111111111111111111111111111111), + Rep::max_subnormal()); + EXPECT_EQ( + u128(0b0'000000000000001, + 0b1000000000000000000000000000000000000000000000000000000000000000), + Rep::min_normal()); + EXPECT_EQ( + u128(0b0'111111111111110, + 0b1111111111111111111111111111111111111111111111111111111111111111), + Rep::max_normal()); + EXPECT_EQ( + u128(0b0'111111111111111, + 0b1000000000000000000000000000000000000000000000000000000000000000), + Rep::inf()); + EXPECT_EQ( + u128(0b0'111111111111111, + 0b1010000000000000000000000000000000000000000000000000000000000000), + Rep::build_nan()); + EXPECT_EQ( + u128(0b0'111111111111111, + 0b1100000000000000000000000000000000000000000000000000000000000000), + Rep::build_quiet_nan()); +} + +TEST(LlvmLibcFPBitsTest, FPType_X86_Binary80_IsNan) { + using LIBC_NAMESPACE::fputil::FPType; + using LIBC_NAMESPACE::fputil::internal::FPRep; + using Rep = FPRep; + + const auto is_nan = [](uint64_t hi, uint64_t lo) { + Rep rep; + rep.set_uintval(u128(hi, lo)); + return rep.is_nan(); + }; + + EXPECT_TRUE(is_nan( + 0b0'111111111111111, // NAN : Pseudo-Infinity + 0b0000000000000000000000000000000000000000000000000000000000000000)); + EXPECT_TRUE(is_nan( + 0b0'111111111111111, // NAN : Pseudo Not a Number + 0b0000000000000000000000000000000000000000000000000000000000000001)); + EXPECT_TRUE(is_nan( + 0b0'111111111111111, // NAN : Pseudo Not a Number + 0b0100000000000000000000000000000000000000000000000000000000000000)); + EXPECT_TRUE(is_nan( + 0b0'111111111111111, // NAN : Signalling Not a Number + 0b1000000000000000000000000000000000000000000000000000000000000001)); + EXPECT_TRUE(is_nan( + 0b0'111111111111111, // NAN : Floating-point Indefinite + 0b1100000000000000000000000000000000000000000000000000000000000000)); + EXPECT_TRUE(is_nan( + 0b0'111111111111111, // NAN : Quiet Not a Number + 0b1100000000000000000000000000000000000000000000000000000000000001)); + EXPECT_TRUE(is_nan( + 0b0'111111111111110, // NAN : Unnormal + 0b0000000000000000000000000000000000000000000000000000000000000000)); + + EXPECT_FALSE(is_nan( + 0b0'000000000000000, // Zero + 0b0000000000000000000000000000000000000000000000000000000000000000)); + EXPECT_FALSE(is_nan( + 0b0'000000000000000, // Subnormal + 0b0000000000000000000000000000000000000000000000000000000000000001)); + EXPECT_FALSE(is_nan( + 0b0'000000000000000, // Pseudo Denormal + 0b1000000000000000000000000000000000000000000000000000000000000001)); + EXPECT_FALSE(is_nan( + 0b0'111111111111111, // Infinity + 0b1000000000000000000000000000000000000000000000000000000000000000)); + EXPECT_FALSE(is_nan( + 0b0'111111111111110, // Normalized + 0b1000000000000000000000000000000000000000000000000000000000000000)); +} + +TEST(LlvmLibcFPBitsTest, FPType_IEEE754_Binary128) { + using LIBC_NAMESPACE::fputil::FPType; + using LIBC_NAMESPACE::fputil::internal::FPRep; + using Rep = FPRep; + + EXPECT_EQ( + u128(0b0'000000000000000'000000000000000000000000000000000000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000000), + Rep::zero()); + EXPECT_EQ( + u128(0b0'011111111111111'000000000000000000000000000000000000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000000), + Rep::one()); + EXPECT_EQ( + u128(0b0'000000000000000'000000000000000000000000000000000000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000001), + Rep::min_subnormal()); + EXPECT_EQ( + u128(0b0'000000000000000'111111111111111111111111111111111111111111111111, + 0b1111111111111111111111111111111111111111111111111111111111111111), + Rep::max_subnormal()); + EXPECT_EQ( + u128(0b0'000000000000001'000000000000000000000000000000000000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000000), + Rep::min_normal()); + EXPECT_EQ( + u128(0b0'111111111111110'111111111111111111111111111111111111111111111111, + 0b1111111111111111111111111111111111111111111111111111111111111111), + Rep::max_normal()); + EXPECT_EQ( + u128(0b0'111111111111111'000000000000000000000000000000000000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000000), + Rep::inf()); + EXPECT_EQ( + u128(0b0'111111111111111'010000000000000000000000000000000000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000000), + Rep::build_nan()); + EXPECT_EQ( + u128(0b0'111111111111111'100000000000000000000000000000000000000000000000, + 0b0000000000000000000000000000000000000000000000000000000000000000), + Rep::build_quiet_nan()); +} + TEST(LlvmLibcFPBitsTest, FloatType) { using FloatBits = FPBits; diff --git a/libc/test/utils/FPUtil/x86_long_double_test.cpp b/libc/test/utils/FPUtil/x86_long_double_test.cpp index 7da835fc95fc9..bafbbe2a41075 100644 --- a/libc/test/utils/FPUtil/x86_long_double_test.cpp +++ b/libc/test/utils/FPUtil/x86_long_double_test.cpp @@ -27,7 +27,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) { // If exponent has the max value and the implicit bit is 0, // then the number is a NaN for all values of mantissa. bits.set_mantissa(i); - long double nan = bits; + long double nan = bits.get_val(); ASSERT_NE(static_cast(isnan(nan)), 0); ASSERT_TRUE(bits.is_nan()); } @@ -38,7 +38,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) { // then the number is a NaN for all non-zero values of mantissa. // Note the initial value of |i| of 1 to avoid a zero mantissa. bits.set_mantissa(i); - long double nan = bits; + long double nan = bits.get_val(); ASSERT_NE(static_cast(isnan(nan)), 0); ASSERT_TRUE(bits.is_nan()); } @@ -49,7 +49,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) { // If exponent is non-zero and also not max, and the implicit bit is 0, // then the number is a NaN for all values of mantissa. bits.set_mantissa(i); - long double nan = bits; + long double nan = bits.get_val(); ASSERT_NE(static_cast(isnan(nan)), 0); ASSERT_TRUE(bits.is_nan()); } @@ -60,7 +60,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) { // If exponent is non-zero and also not max, and the implicit bit is 1, // then the number is normal value for all values of mantissa. bits.set_mantissa(i); - long double valid = bits; + long double valid = bits.get_val(); ASSERT_EQ(static_cast(isnan(valid)), 0); ASSERT_FALSE(bits.is_nan()); } @@ -70,7 +70,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) { for (unsigned int i = 0; i < COUNT; ++i) { // If exponent is zero, then the number is a valid but denormal value. bits.set_mantissa(i); - long double valid = bits; + long double valid = bits.get_val(); ASSERT_EQ(static_cast(isnan(valid)), 0); ASSERT_FALSE(bits.is_nan()); } @@ -80,7 +80,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) { for (unsigned int i = 0; i < COUNT; ++i) { // If exponent is zero, then the number is a valid but denormal value. bits.set_mantissa(i); - long double valid = bits; + long double valid = bits.get_val(); ASSERT_EQ(static_cast(isnan(valid)), 0); ASSERT_FALSE(bits.is_nan()); } diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index f222831eefd76..6fa47c0090b87 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -662,7 +662,6 @@ libc_support_library( libc_support_library( name = "__support_fputil_fp_bits", hdrs = ["src/__support/FPUtil/FPBits.h"], - textual_hdrs = ["src/__support/FPUtil/x86_64/LongDoubleBits.h"], deps = [ ":__support_common", ":__support_cpp_bit", diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel new file mode 100644 index 0000000000000..4f206b21e478b --- /dev/null +++ b/utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel @@ -0,0 +1,42 @@ +# This file is licensed under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Tests for LLVM libc __support functions. + +load("//libc/test:libc_test_rules.bzl", "libc_test") + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +libc_test( + name = "fpbits_test", + srcs = ["fpbits_test.cpp"], + deps = [ + "//libc:__support_fputil_fp_bits", + "//libc:__support_fputil_fpbits_str", + ], +) + +libc_test( + name = "dyadic_float_test", + srcs = ["dyadic_float_test.cpp"], + deps = [ + "//libc:__support_fputil_dyadic_float", + "//libc:__support_uint", + "//libc:__support_uint128", + "//libc/test/UnitTest:fp_test_helpers", + "//libc/utils/MPFRWrapper:mpfr_wrapper", + ], +) + +libc_test( + name = "rounding_mode_test", + srcs = ["rounding_mode_test.cpp"], + deps = [ + "//libc:__support_fputil_rounding_mode", + "//libc:__support_uint128", + "//libc/utils/MPFRWrapper:mpfr_wrapper", + ], +)