From 337b7715929909fd2bb55069ecb939cf23dbfe52 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Tue, 16 Jan 2024 12:09:58 -0800 Subject: [PATCH] Revert "[libc][NFC] Refactor FPBits and remove LongDoubleBits specialization (#78192)" (#78329) This reverts commit fdbf255c96cb18bc1fb08fa7264446bcbbd5fbd0. Causes build breakage on 32b arm (see reports: https://github.com/llvm/llvm-project/pull/78192). These are reproducible for the 32b arm baremetal target on x86 hosts as well. --- libc/src/__support/FPUtil/FPBits.h | 443 ++++-------------- .../FPUtil/generic/sqrt_80_bit_long_double.h | 2 +- .../__support/FPUtil/x86_64/LongDoubleBits.h | 179 +++++++ .../FPUtil/x86_64/NextAfterLongDouble.h | 8 +- .../test/src/__support/FPUtil/fpbits_test.cpp | 213 --------- .../utils/FPUtil/x86_long_double_test.cpp | 12 +- .../llvm-project-overlay/libc/BUILD.bazel | 1 + .../test/src/__support/FPUtil/BUILD.bazel | 42 -- 8 files changed, 282 insertions(+), 618 deletions(-) create mode 100644 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h delete mode 100644 utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h index d6a9f60f71208c..93e32ba7cc9415 100644 --- a/libc/src/__support/FPUtil/FPBits.h +++ b/libc/src/__support/FPUtil/FPBits.h @@ -31,40 +31,6 @@ 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 @@ -166,94 +132,11 @@ 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"); -protected: +private: 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. @@ -272,6 +155,20 @@ 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; @@ -323,9 +220,6 @@ 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; @@ -347,213 +241,6 @@ 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 @@ -589,29 +276,47 @@ template LIBC_INLINE static constexpr FPType get_fp_type() { static_assert(cpp::always_false, "Unsupported type"); } -// A generic class to represent floating point formats. +// A generic class to represent single precision, double precision, and quad +// precision IEEE 754 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. +// an x87 floating point format. This format is an IEEE 754 extension format. +// It is handled as an explicit specialization of this class. template struct FPBits : public internal::FPRep()> { static_assert(cpp::is_floating_point_v, "FPBits instantiated with invalid type."); using UP = internal::FPRep()>; - using Rep = UP; - using StorageType = typename UP::StorageType; +private: + using UP::EXP_SIG_MASK; + using UP::QUIET_NAN_MASK; + using UP::SIG_LEN; + using UP::SIG_MASK; + +public: + 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_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); + 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; // Constructors. LIBC_INLINE constexpr FPBits() = default; @@ -633,56 +338,88 @@ template struct FPBits : public internal::FPRep()> { LIBC_INLINE constexpr explicit operator T() const { return get_val(); } - LIBC_INLINE constexpr bool is_inf_or_nan() const { return !UP::is_finite(); } + // 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 FPBits abs() const { - return FPBits(bits & UP::EXP_SIG_MASK); + return FPBits(bits & EXP_SIG_MASK); } // Methods below this are used by tests. LIBC_INLINE static constexpr T zero(bool sign = false) { - return FPBits(UP::zero(sign)).get_val(); + StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign + | 0 // exponent + | 0; // mantissa + return FPBits(rep).get_val(); } LIBC_INLINE static constexpr T neg_zero() { return zero(true); } LIBC_INLINE static constexpr T inf(bool sign = false) { - return FPBits(UP::inf(sign)).get_val(); + StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign + | EXP_MASK // exponent + | 0; // mantissa + return FPBits(rep).get_val(); } LIBC_INLINE static constexpr T neg_inf() { return inf(true); } LIBC_INLINE static constexpr T min_normal() { - return FPBits(UP::min_normal(false)).get_val(); + return FPBits(MIN_NORMAL).get_val(); } LIBC_INLINE static constexpr T max_normal() { - return FPBits(UP::max_normal(false)).get_val(); + return FPBits(MAX_NORMAL).get_val(); } LIBC_INLINE static constexpr T min_denormal() { - return FPBits(UP::min_subnormal(false)).get_val(); + return FPBits(MIN_SUBNORMAL).get_val(); } LIBC_INLINE static constexpr T max_denormal() { - return FPBits(UP::max_subnormal(false)).get_val(); + return FPBits(MAX_SUBNORMAL).get_val(); } LIBC_INLINE static constexpr T build_nan(StorageType v) { - return FPBits(UP::build_nan(false, v)).get_val(); + StorageType rep = 0 // sign + | EXP_MASK // exponent + | (v & FRACTION_MASK); // mantissa + return FPBits(rep).get_val(); } LIBC_INLINE static constexpr T build_quiet_nan(StorageType v) { - return FPBits(UP::build_quiet_nan(false, v)).get_val(); + return build_nan(QUIET_NAN_MASK | v); } LIBC_INLINE static constexpr FPBits create_value(bool sign, StorageType biased_exp, StorageType mantissa) { - 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))); + StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign + | ((biased_exp << EXP_MASK_SHIFT) & EXP_MASK) // exponent + | (mantissa & FRACTION_MASK); // mantissa + return FPBits(rep); } // The function convert integer number and unbiased exponent to proper float @@ -697,8 +434,6 @@ 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; @@ -719,4 +454,8 @@ 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 8815a18cfbc393..257c02e17d0045 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.get_val(); + return out; } } #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 new file mode 100644 index 00000000000000..c18abcee77ea50 --- /dev/null +++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h @@ -0,0 +1,179 @@ +//===-- 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 5f15bac5df77f8..b461da3a4c0abc 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.get_val(); + return from_bits; } 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.get_val(); + return from_bits; } 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.get_val(); + return from_bits; } 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.get_val(); + return from_bits; } else { ++int_val; } diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp index 3a452f0b7993c4..e2dbe248ef2131 100644 --- a/libc/test/src/__support/FPUtil/fpbits_test.cpp +++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp @@ -12,219 +12,6 @@ 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 bafbbe2a410759..7da835fc95fc92 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.get_val(); + long double nan = bits; 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.get_val(); + long double nan = bits; 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.get_val(); + long double nan = bits; 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.get_val(); + long double valid = bits; 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.get_val(); + long double valid = bits; 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.get_val(); + long double valid = bits; 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 6fa47c0090b87a..f222831eefd762 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -662,6 +662,7 @@ 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 deleted file mode 100644 index 4f206b21e478b1..00000000000000 --- a/utils/bazel/llvm-project-overlay/libc/test/src/__support/FPUtil/BUILD.bazel +++ /dev/null @@ -1,42 +0,0 @@ -# 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", - ], -)