[libc-commits] [libc] [llvm] [reland][libc][NFC] Refactor FPBits and remove LongDoubleBits specialization (PR #78447)
Guillaume Chatelet via libc-commits
libc-commits at lists.llvm.org
Wed Jan 17 05:45:49 PST 2024
https://github.com/gchatelet created https://github.com/llvm/llvm-project/pull/78447
- [reland][libc][NFC] Refactor FPBits and remove LongDoubleBits specialization
- Use opaque structs instead of enum so that is works when StorageType is BigInt
>From d22c78da8cefaeb8b5d641c7805fb3cb3e33a1b3 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 17 Jan 2024 13:44:01 +0000
Subject: [PATCH 1/2] [reland][libc][NFC] Refactor FPBits and remove
LongDoubleBits specialization
This patch removes the `FPBits` specialization for x86 Extended Precision by moving it up to `FPRep`.
It also introduces enums (`Exponent`, `BiasedExponent` and `Significand`) to represent the exponent and significant parts of the floating point numbers. These enums are used to construct and observe floating point representations.
Additionally, we remove `LongDoubleBits.h` that is now unnecessary.
---
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, 618 insertions(+), 282 deletions(-)
delete mode 100644 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
create 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 93e32ba7cc9415..d6a9f60f71208c 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<FPType> │
+// └─────────▲─────────┘
+// │
+// ┌─────────┴─────────┐
+// │ FPRepBase<FPType> │
+// └─────────▲─────────┘
+// │
+// ┌────────────┴─────────────┐
+// │ │
+// ┌────────┴──────┐ ┌─────────────┴──────────────┐
+// │ FPRep<FPType> │ │ FPRep<FPType::X86_Binary80 │
+// └────────▲──────┘ └─────────────▲──────────────┘
+// │ │
+// └────────────┬─────────────┘
+// │
+// ┌─────┴─────┐
+// │ FPBits<T> │
+// └───────────┘
+//
+// - '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<fp_type> {
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<BiasedExponent>(static_cast<int32_t>(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 <typename T>
+ LIBC_INLINE static constexpr auto storage_cast(T value) {
+ return static_cast<StorageType>(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<fp_type> {
LIBC_INLINE_VAR static constexpr StorageType FRACTION_MASK =
mask_trailing_ones<StorageType, FRACTION_LEN>();
- // 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<fp_type> {
}
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 <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
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<FPType::X86_Binary80> : public FPRepBase<FPType::X86_Binary80> {
+ using UP = FPRepBase<FPType::X86_Binary80>;
+ 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<FPType::X86_Binary80>.
+ // 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 <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
static_assert(cpp::always_false<UnqualT>, "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 <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
static_assert(cpp::is_floating_point_v<T>,
"FPBits instantiated with invalid type.");
using UP = internal::FPRep<get_fp_type<T>()>;
-
-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 <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
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<T>
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<T>() != 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 <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
// 5) Number is unsigned, so the result can be only positive.
LIBC_INLINE static constexpr FPBits<T> make_value(StorageType number,
int ep) {
+ static_assert(get_fp_type<T>() != FPType::X86_Binary80,
+ "This function is not tested for X86 Extended Precision");
FPBits<T> result;
// offset: +1 for sign, but -1 for implicit first bit
int lz = cpp::countl_zero(number) - EXP_LEN;
@@ -454,8 +719,4 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
} // 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 257c02e17d0045..8815a18cfbc393 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 c18abcee77ea50..00000000000000
--- 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 <stdint.h>
-
-namespace LIBC_NAMESPACE {
-namespace fputil {
-
-template <>
-struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
- using UP = internal::FPRep<FPType::X86_Binary80>;
- 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 <typename XType> LIBC_INLINE constexpr explicit FPBits(XType x) {
- using Unqual = typename cpp::remove_cv_t<XType>;
- if constexpr (cpp::is_same_v<Unqual, long double>) {
- bits = cpp::bit_cast<StorageType>(x);
- } else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
- bits = x;
- } else {
- // We don't want accidental type promotions/conversions, so we require
- // exact type match.
- static_assert(cpp::always_false<XType>);
- }
- }
-
- // Floating-point conversions.
- LIBC_INLINE constexpr long double get_val() const {
- return cpp::bit_cast<long double>(bits);
- }
-
- LIBC_INLINE constexpr operator long double() const {
- return cpp::bit_cast<long double>(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<long double>) == 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 b461da3a4c0abc..5f15bac5df77f8 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 e2dbe248ef2131..3a452f0b7993c4 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<FPType::IEEE754_Binary16>;
+ 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<FPType::IEEE754_Binary32>;
+ 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<FPType::IEEE754_Binary64>;
+ 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<FPType::X86_Binary80>;
+
+ 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<FPType::X86_Binary80>;
+
+ 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<FPType::IEEE754_Binary128>;
+
+ 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<float>;
diff --git a/libc/test/utils/FPUtil/x86_long_double_test.cpp b/libc/test/utils/FPUtil/x86_long_double_test.cpp
index 7da835fc95fc92..bafbbe2a410759 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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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 f222831eefd762..6fa47c0090b87a 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 00000000000000..4f206b21e478b1
--- /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",
+ ],
+)
>From 672589c3d1ab3507e382d11f6b0ebce7e1c01ebb Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 17 Jan 2024 13:44:54 +0000
Subject: [PATCH 2/2] Use opaque structs instead of enum so that is works when
StorageType is BigInt
---
libc/src/__support/FPUtil/FPBits.h | 169 +++++++++++++++++------------
1 file changed, 100 insertions(+), 69 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index d6a9f60f71208c..7456913a01ec38 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -171,68 +171,98 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
return StorageType(1) << position;
}
+ template <typename T> struct Opaque {
+ using value_type = T;
+ LIBC_INLINE constexpr explicit Opaque(T value) : value(value) {}
+ LIBC_INLINE constexpr Opaque(const Opaque &value) = default;
+
+ LIBC_INLINE constexpr explicit operator T() const { return value; }
+
+ private:
+ T value;
+ };
+
// 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,
+ struct Exponent : public Opaque<int32_t> {
+ using UP = Opaque<int32_t>;
+ using UP::UP;
+ LIBC_INLINE
+ static constexpr auto MIN() { return Exponent{1 - EXP_BIAS}; }
+ LIBC_INLINE static constexpr auto ZERO() { return Exponent{0}; }
+ LIBC_INLINE static constexpr auto MAX() { return Exponent{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 {
+ struct BiasedExponent : public Opaque<uint32_t> {
+ using UP = Opaque<uint32_t>;
+ using UP::UP;
+
+ LIBC_INLINE constexpr BiasedExponent(Exponent exp)
+ : UP(static_cast<int32_t>(exp) + EXP_BIAS) {}
// The exponent value for denormal numbers.
- BITS_ALL_ZEROES = 0,
+ LIBC_INLINE static constexpr auto BITS_ALL_ZEROES() {
+ return BiasedExponent{uint32_t(0)};
+ }
// The exponent value for infinity.
- BITS_ALL_ONES = 2 * EXP_BIAS + 1,
+ LIBC_INLINE static constexpr auto BITS_ALL_ONES() {
+ return BiasedExponent{uint32_t(2 * EXP_BIAS + 1)};
+ }
};
- LIBC_INLINE static constexpr BiasedExponent biased(Exponent value) {
- return static_cast<BiasedExponent>(static_cast<int32_t>(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),
+ struct Significand : public Opaque<StorageType> {
+ using UP = Opaque<StorageType>;
+ using UP::UP;
+
+ LIBC_INLINE static constexpr auto ZERO() {
+ return Significand{StorageType(0)};
+ }
+ LIBC_INLINE static constexpr auto LSB() {
+ return Significand{StorageType(1)};
+ }
+ LIBC_INLINE static constexpr auto MSB() {
+ return Significand{StorageType(bit_at(SIG_LEN - 1))};
+ }
// Aliases
- BITS_ALL_ZEROES = ZERO,
- BITS_ALL_ONES = SIG_MASK,
+ LIBC_INLINE static constexpr auto BITS_ALL_ZEROES() { return ZERO(); }
+ LIBC_INLINE static constexpr auto BITS_ALL_ONES() {
+ return Significand{SIG_MASK};
+ }
};
- template <typename T>
- LIBC_INLINE static constexpr auto storage_cast(T value) {
- return static_cast<StorageType>(value);
+ template <typename To, typename T>
+ LIBC_INLINE static constexpr To as(Opaque<T> opaque) {
+ return To(static_cast<T>(opaque));
}
LIBC_INLINE friend constexpr Significand operator|(const Significand a,
const Significand b) {
- return Significand{storage_cast(storage_cast(a) | storage_cast(b))};
+ return Significand{StorageType(as<StorageType>(a) | as<StorageType>(b))};
}
LIBC_INLINE friend constexpr Significand operator^(const Significand a,
const Significand b) {
- return Significand{storage_cast(storage_cast(a) ^ storage_cast(b))};
+ return Significand{StorageType(as<StorageType>(a) ^ as<StorageType>(b))};
}
LIBC_INLINE friend constexpr Significand operator>>(const Significand a,
int shift) {
- return Significand{storage_cast(storage_cast(a) >> shift)};
+ return Significand{StorageType(as<StorageType>(a) >> shift)};
}
LIBC_INLINE static constexpr StorageType encode(BiasedExponent exp) {
- return (storage_cast(exp) << SIG_LEN) & EXP_MASK;
+ return (as<StorageType>(exp) << SIG_LEN) & EXP_MASK;
}
LIBC_INLINE static constexpr StorageType encode(Significand value) {
- return storage_cast(value) & SIG_MASK;
+ return as<StorageType>(value) & SIG_MASK;
}
LIBC_INLINE static constexpr StorageType encode(BiasedExponent exp,
@@ -330,7 +360,7 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
LIBC_INLINE constexpr bool is_zero() const {
return (bits & EXP_SIG_MASK) == 0;
}
-};
+}; // namespace fputil
namespace internal {
@@ -352,7 +382,6 @@ template <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
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;
@@ -361,65 +390,65 @@ template <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
public:
LIBC_INLINE constexpr bool is_nan() const {
return exp_sig_bits() >
- encode(BiasedExponent::BITS_ALL_ONES, Significand::ZERO);
+ 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);
+ 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);
+ 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);
+ encode(BiasedExponent::BITS_ALL_ZEROES(), Significand::ZERO());
}
LIBC_INLINE constexpr bool is_finite() const {
- return exp_bits() != encode(BiasedExponent::BITS_ALL_ONES);
+ return exp_bits() != encode(BiasedExponent::BITS_ALL_ONES());
}
LIBC_INLINE
constexpr bool is_subnormal() const {
- return exp_bits() == encode(BiasedExponent::BITS_ALL_ZEROES);
+ 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);
+ 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);
+ return encode(sign, Exponent::ZERO(), Significand::ZERO());
}
LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
- return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::LSB);
+ 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);
+ 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);
+ return encode(sign, Exponent::MIN(), Significand::ZERO());
}
LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
- return encode(sign, biased(Exponent::MAX), Significand::BITS_ALL_ONES);
+ return encode(sign, Exponent::MAX(), Significand::BITS_ALL_ONES());
}
LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
- return encode(sign, BiasedExponent::BITS_ALL_ONES, Significand::ZERO);
+ 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)));
+ 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});
+ return encode(sign, BiasedExponent::BITS_ALL_ONES(),
+ Significand::MSB() | Significand{v});
}
// The function return mantissa with the implicit bit set iff the current
@@ -469,26 +498,27 @@ struct FPRep<FPType::X86_Binary80> : public FPRepBase<FPType::X86_Binary80> {
// - Quiet Not a Number
// - Unnormal
// This can be reduced to the following logic:
- if (exp_bits() == encode(BiasedExponent::BITS_ALL_ONES))
+ 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;
+ 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));
+ 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);
+ 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);
+ encode(BiasedExponent::BITS_ALL_ZEROES(), Significand::ZERO());
}
LIBC_INLINE constexpr bool is_finite() const {
return !is_inf() && !is_nan();
@@ -496,48 +526,49 @@ struct FPRep<FPType::X86_Binary80> : public FPRepBase<FPType::X86_Binary80> {
LIBC_INLINE
constexpr bool is_subnormal() const {
return exp_sig_bits() >
- encode(BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO);
+ 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))
+ 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);
+ 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);
+ return encode(sign, Exponent::ZERO(), Significand::MSB());
}
LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
- return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::LSB);
+ 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);
+ 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);
+ return encode(sign, Exponent::MIN(), Significand::MSB());
}
LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
- return encode(sign, biased(Exponent::MAX), Significand::BITS_ALL_ONES);
+ return encode(sign, Exponent::MAX(), Significand::BITS_ALL_ONES());
}
LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
- return encode(sign, BiasedExponent::BITS_ALL_ONES, Significand::MSB);
+ 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)));
+ 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});
+ return encode(sign, BiasedExponent::BITS_ALL_ONES(),
+ Significand::MSB() | (Significand::MSB() >> 1) |
+ Significand{v});
}
LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
@@ -547,7 +578,7 @@ struct FPRep<FPType::X86_Binary80> : public FPRepBase<FPType::X86_Binary80> {
// The following functions are specific to FPRep<FPType::X86_Binary80>.
// TODO: Remove if possible.
LIBC_INLINE constexpr bool get_implicit_bit() const {
- return bits & EXPLICIT_BIT_MASK;
+ return static_cast<bool>(bits & EXPLICIT_BIT_MASK);
}
LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
More information about the libc-commits
mailing list