[libc-commits] [libc] [llvm] [libc][NFC] Refactor FPBits and remove LongDoubleBits specialization (PR #78192)

Guillaume Chatelet via libc-commits libc-commits at lists.llvm.org
Tue Jan 16 02:36:51 PST 2024


https://github.com/gchatelet updated https://github.com/llvm/llvm-project/pull/78192

>From d97b3198ce7d4460256574d09bbd3c267c4bfdbe Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Mon, 15 Jan 2024 16:46:49 +0000
Subject: [PATCH 1/7] [libc][NFC] Refactor FPBits and remove LongDoubleBits
 specialization

---
 libc/src/__support/FPUtil/FPBits.h            | 222 +++++++++++++-----
 .../__support/FPUtil/x86_64/LongDoubleBits.h  | 205 ++++++++++------
 .../test/src/__support/FPUtil/fpbits_test.cpp | 216 +++++++++++++++++
 .../test/src/__support/FPUtil/BUILD.bazel     |  42 ++++
 4 files changed, 548 insertions(+), 137 deletions(-)
 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..9067e37c52a6d9 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -132,6 +132,70 @@ 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");
 
+protected:
+  enum class Exp : int32_t {
+    ZERO = 0,
+    // The exponent value for denormal numbers.
+    SUBNORMAL = -EXP_BIAS,
+    // The minimum exponent value for normal numbers.
+    MIN = SUBNORMAL + 1,
+    // The maximum exponent value for normal numbers.
+    MAX = EXP_BIAS,
+    // Special value all ones.
+    INF = MAX + 1,
+    // Aliases
+    BITS_ALL_ZEROES = SUBNORMAL,
+    BITS_ALL_ONES = INF,
+  };
+
+  enum class Sig : StorageType {
+    ZERO = 0,
+    ONE = 1,
+    MSB = StorageType(1) << (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 Sig operator|(const Sig a, const Sig b) {
+    return Sig{storage_cast(storage_cast(a) | storage_cast(b))};
+  }
+  LIBC_INLINE friend constexpr Sig operator^(const Sig a, const Sig b) {
+    return Sig{storage_cast(storage_cast(a) ^ storage_cast(b))};
+  }
+  LIBC_INLINE friend constexpr Sig operator>>(const Sig a, int shift) {
+    return Sig{storage_cast(storage_cast(a) >> shift)};
+  }
+
+  LIBC_INLINE static constexpr StorageType encode(Exp exp) {
+    return storage_cast(static_cast<int32_t>(exp) + EXP_BIAS) << SIG_LEN;
+  }
+
+  LIBC_INLINE static constexpr StorageType encode(Sig value) {
+    return storage_cast(value) & SIG_MASK;
+  }
+
+  LIBC_INLINE static constexpr StorageType encode(Exp exp, Sig sig) {
+    return encode(exp) | encode(sig);
+  }
+
+  LIBC_INLINE static constexpr StorageType encode(bool sign, Exp exp, Sig 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:
   LIBC_INLINE static constexpr StorageType bit_at(int position) {
     return StorageType(1) << position;
@@ -155,20 +219,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 +270,9 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
   }
 
   LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
+  LIBC_INLINE constexpr StorageType set_uintval(StorageType value) {
+    return bits = (value & FP_MASK);
+  }
 
   LIBC_INLINE constexpr bool is_zero() const {
     return (bits & EXP_SIG_MASK) == 0;
@@ -241,6 +294,79 @@ 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::Exp;
+  using typename UP::Sig;
+  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(Exp::BITS_ALL_ONES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE constexpr bool is_quiet_nan() const {
+    return exp_sig_bits() >= encode(Exp::BITS_ALL_ONES, Sig::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(Exp::BITS_ALL_ONES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE constexpr bool is_zero() const {
+    return exp_sig_bits() == encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE constexpr bool is_finite() const {
+    return exp_bits() != encode(Exp::BITS_ALL_ONES);
+  }
+  LIBC_INLINE
+  constexpr bool is_subnormal() const {
+    return exp_bits() == encode(Exp::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, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE static constexpr StorageType one(bool sign = false) {
+    return encode(sign, Exp::ZERO, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::ONE);
+  }
+  LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ONES);
+  }
+  LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) {
+    return encode(sign, Exp::MIN, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
+    return encode(sign, Exp::MAX, Sig::BITS_ALL_ONES);
+  }
+  LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ONES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE static constexpr StorageType build_nan(bool sign = false,
+                                                     StorageType v = 0) {
+    return encode(sign, Exp::BITS_ALL_ONES, (v ? Sig{v} : (Sig::MSB >> 1)));
+  }
+  LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false,
+                                                           StorageType v = 0) {
+    return encode(sign, Exp::BITS_ALL_ONES, Sig::MSB | Sig{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();
+  }
 };
 
 } // namespace internal
@@ -288,10 +414,10 @@ 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>()>;
+  using Rep = UP;
 
 private:
   using UP::EXP_SIG_MASK;
-  using UP::QUIET_NAN_MASK;
   using UP::SIG_LEN;
   using UP::SIG_MASK;
 
@@ -308,15 +434,15 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
   using UP::TOTAL_LEN;
   using UP::UP;
 
+  using UP::encode;
   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,30 +464,7 @@ 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);
@@ -370,56 +473,45 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
   // 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);
+    return FPBits(encode(sign, typename UP::Exp(biased_exp - UP::EXP_BIAS),
+                         typename UP::Sig(mantissa)));
   }
 
   // The function convert integer number and unbiased exponent to proper float
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index c18abcee77ea50..34e685d3f888ac 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -26,19 +26,22 @@
 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;
+namespace internal {
 
-private:
-  using UP::bits;
-  using UP::EXP_SIG_MASK;
-  using UP::QUIET_NAN_MASK;
+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::Exp;
+  using typename UP::Sig;
+  using UP::encode;
 
 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)
@@ -49,13 +52,117 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
   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;
+
+  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(Exp::BITS_ALL_ONES))
+      return !is_inf();
+    if (exp_bits() != encode(Exp::BITS_ALL_ZEROES))
+      return (sig_bits() & encode(Sig::MSB)) == 0;
+    return false;
+  }
+  LIBC_INLINE constexpr bool is_quiet_nan() const {
+    return exp_sig_bits() >=
+           encode(Exp::BITS_ALL_ONES, Sig::MSB | (Sig::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(Exp::BITS_ALL_ONES, Sig::MSB);
+  }
+  LIBC_INLINE constexpr bool is_zero() const {
+    return exp_sig_bits() == encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE constexpr bool is_finite() const {
+    return !is_inf() && !is_nan();
+  }
+  LIBC_INLINE
+  constexpr bool is_subnormal() const {
+    return exp_sig_bits() > encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE constexpr bool is_normal() const {
+    const auto exp = exp_bits();
+    if (exp == encode(Exp::BITS_ALL_ZEROES) ||
+        exp == encode(Exp::BITS_ALL_ONES))
+      return false;
+    return get_implicit_bit();
+  }
+
+  LIBC_INLINE static constexpr StorageType zero(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE static constexpr StorageType one(bool sign = false) {
+    return encode(sign, Exp::ZERO, Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::ONE);
+  }
+  LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ONES ^ Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) {
+    return encode(sign, Exp::MIN, Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
+    return encode(sign, Exp::MAX, Sig::BITS_ALL_ONES);
+  }
+  LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ONES, Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType build_nan(bool sign = false,
+                                                     StorageType v = 0) {
+    return encode(sign, Exp::BITS_ALL_ONES,
+                  Sig::MSB | (v ? Sig{v} : (Sig::MSB >> 2)));
+  }
+  LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false,
+                                                           StorageType v = 0) {
+    return encode(sign, Exp::BITS_ALL_ONES,
+                  Sig::MSB | (Sig::MSB >> 1) | Sig{v});
+  }
+
+  LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
+    return sig_bits();
+  }
+
+  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
+
+template <>
+struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
+  using UP = internal::FPRep<FPType::X86_Binary80>;
+  using Rep = UP;
+  using StorageType = typename UP::StorageType;
+
+private:
+  using UP::bits;
+  using UP::EXP_SIG_MASK;
+
+public:
+  // 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);
 
   // Constructors.
   LIBC_INLINE constexpr FPBits() = default;
@@ -82,90 +189,44 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
     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);
-  }
+  LIBC_INLINE constexpr bool is_inf_or_nan() const { return !UP::is_finite(); }
 
   // 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();
+    return FPBits(UP::zero(sign)).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();
+    return FPBits(UP::inf(sign)).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();
+    return FPBits(UP::min_normal(false)).get_val();
   }
 
   LIBC_INLINE static constexpr long double max_normal() {
-    return FPBits(MAX_NORMAL).get_val();
+    return FPBits(UP::max_normal(false)).get_val();
   }
 
   LIBC_INLINE static constexpr long double min_denormal() {
-    return FPBits(MIN_SUBNORMAL).get_val();
+    return FPBits(UP::min_subnormal(false)).get_val();
   }
 
   LIBC_INLINE static constexpr long double max_denormal() {
-    return FPBits(MAX_SUBNORMAL).get_val();
+    return FPBits(UP::max_subnormal(false)).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();
+    return FPBits(UP::build_nan(false, v)).get_val();
   }
 
   LIBC_INLINE static constexpr long double build_quiet_nan(StorageType v) {
-    return build_nan(QUIET_NAN_MASK | v);
+    return FPBits(UP::build_quiet_nan(false, v)).get_val();
   }
 };
 
diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp
index e2dbe248ef2131..8a5b3c7370ef9e 100644
--- a/libc/test/src/__support/FPUtil/fpbits_test.cpp
+++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp
@@ -12,6 +12,222 @@
 
 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-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/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 e9b580c29719c71d7ed21b621d96460892ec8675 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Mon, 15 Jan 2024 17:01:39 +0000
Subject: [PATCH 2/7] Actually remove LongDoubleBits

---
 libc/src/__support/FPUtil/FPBits.h            | 158 ++++++++++--
 .../FPUtil/generic/sqrt_80_bit_long_double.h  |   2 +-
 .../__support/FPUtil/x86_64/LongDoubleBits.h  | 240 ------------------
 .../FPUtil/x86_64/NextAfterLongDouble.h       |   8 +-
 .../utils/FPUtil/x86_long_double_test.cpp     |  12 +-
 .../llvm-project-overlay/libc/BUILD.bazel     |   1 -
 6 files changed, 141 insertions(+), 280 deletions(-)
 delete mode 100644 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 9067e37c52a6d9..58d63a7d01fb82 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -369,6 +369,126 @@ template <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
   }
 };
 
+// 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::Exp;
+  using typename UP::Sig;
+  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(Exp::BITS_ALL_ONES))
+      return !is_inf();
+    if (exp_bits() != encode(Exp::BITS_ALL_ZEROES))
+      return (sig_bits() & encode(Sig::MSB)) == 0;
+    return false;
+  }
+  LIBC_INLINE constexpr bool is_quiet_nan() const {
+    return exp_sig_bits() >=
+           encode(Exp::BITS_ALL_ONES, Sig::MSB | (Sig::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(Exp::BITS_ALL_ONES, Sig::MSB);
+  }
+  LIBC_INLINE constexpr bool is_zero() const {
+    return exp_sig_bits() == encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE constexpr bool is_finite() const {
+    return !is_inf() && !is_nan();
+  }
+  LIBC_INLINE
+  constexpr bool is_subnormal() const {
+    return exp_sig_bits() > encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE constexpr bool is_normal() const {
+    const auto exp = exp_bits();
+    if (exp == encode(Exp::BITS_ALL_ZEROES) ||
+        exp == encode(Exp::BITS_ALL_ONES))
+      return false;
+    return get_implicit_bit();
+  }
+
+  LIBC_INLINE static constexpr StorageType zero(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+  }
+  LIBC_INLINE static constexpr StorageType one(bool sign = false) {
+    return encode(sign, Exp::ZERO, Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::ONE);
+  }
+  LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ONES ^ Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) {
+    return encode(sign, Exp::MIN, Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
+    return encode(sign, Exp::MAX, Sig::BITS_ALL_ONES);
+  }
+  LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
+    return encode(sign, Exp::BITS_ALL_ONES, Sig::MSB);
+  }
+  LIBC_INLINE static constexpr StorageType build_nan(bool sign = false,
+                                                     StorageType v = 0) {
+    return encode(sign, Exp::BITS_ALL_ONES,
+                  Sig::MSB | (v ? Sig{v} : (Sig::MSB >> 2)));
+  }
+  LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false,
+                                                           StorageType v = 0) {
+    return encode(sign, Exp::BITS_ALL_ONES,
+                  Sig::MSB | (Sig::MSB >> 1) | Sig{v});
+  }
+
+  LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
+    return sig_bits();
+  }
+
+  // The following functions are specific to this implementation.
+  // 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
 
 // Returns the FPType corresponding to C++ type T on the host.
@@ -402,41 +522,23 @@ 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>()>;
   using Rep = UP;
-
-private:
-  using UP::EXP_SIG_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::encode;
-  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);
@@ -467,7 +569,7 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
   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.
@@ -510,8 +612,10 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
 
   LIBC_INLINE static constexpr FPBits<T>
   create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
-    return FPBits(encode(sign, typename UP::Exp(biased_exp - UP::EXP_BIAS),
-                         typename UP::Sig(mantissa)));
+    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::Exp(biased_exp - UP::EXP_BIAS),
+                             typename UP::Sig(mantissa)));
   }
 
   // The function convert integer number and unbiased exponent to proper float
@@ -526,6 +630,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;
@@ -546,8 +652,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 34e685d3f888ac..00000000000000
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ /dev/null
@@ -1,240 +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 {
-
-namespace internal {
-
-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::Exp;
-  using typename UP::Sig;
-  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(Exp::BITS_ALL_ONES))
-      return !is_inf();
-    if (exp_bits() != encode(Exp::BITS_ALL_ZEROES))
-      return (sig_bits() & encode(Sig::MSB)) == 0;
-    return false;
-  }
-  LIBC_INLINE constexpr bool is_quiet_nan() const {
-    return exp_sig_bits() >=
-           encode(Exp::BITS_ALL_ONES, Sig::MSB | (Sig::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(Exp::BITS_ALL_ONES, Sig::MSB);
-  }
-  LIBC_INLINE constexpr bool is_zero() const {
-    return exp_sig_bits() == encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
-  }
-  LIBC_INLINE constexpr bool is_finite() const {
-    return !is_inf() && !is_nan();
-  }
-  LIBC_INLINE
-  constexpr bool is_subnormal() const {
-    return exp_sig_bits() > encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
-  }
-  LIBC_INLINE constexpr bool is_normal() const {
-    const auto exp = exp_bits();
-    if (exp == encode(Exp::BITS_ALL_ZEROES) ||
-        exp == encode(Exp::BITS_ALL_ONES))
-      return false;
-    return get_implicit_bit();
-  }
-
-  LIBC_INLINE static constexpr StorageType zero(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
-  }
-  LIBC_INLINE static constexpr StorageType one(bool sign = false) {
-    return encode(sign, Exp::ZERO, Sig::MSB);
-  }
-  LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::ONE);
-  }
-  LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ONES ^ Sig::MSB);
-  }
-  LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) {
-    return encode(sign, Exp::MIN, Sig::MSB);
-  }
-  LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
-    return encode(sign, Exp::MAX, Sig::BITS_ALL_ONES);
-  }
-  LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ONES, Sig::MSB);
-  }
-  LIBC_INLINE static constexpr StorageType build_nan(bool sign = false,
-                                                     StorageType v = 0) {
-    return encode(sign, Exp::BITS_ALL_ONES,
-                  Sig::MSB | (v ? Sig{v} : (Sig::MSB >> 2)));
-  }
-  LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false,
-                                                           StorageType v = 0) {
-    return encode(sign, Exp::BITS_ALL_ONES,
-                  Sig::MSB | (Sig::MSB >> 1) | Sig{v});
-  }
-
-  LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
-    return sig_bits();
-  }
-
-  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
-
-template <>
-struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
-  using UP = internal::FPRep<FPType::X86_Binary80>;
-  using Rep = UP;
-  using StorageType = typename UP::StorageType;
-
-private:
-  using UP::bits;
-  using UP::EXP_SIG_MASK;
-
-public:
-  // 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);
-
-  // 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 bool is_inf_or_nan() const { return !UP::is_finite(); }
-
-  // Methods below this are used by tests.
-
-  LIBC_INLINE static constexpr long double zero(bool sign = false) {
-    return FPBits(UP::zero(sign)).get_val();
-  }
-
-  LIBC_INLINE static constexpr long double neg_zero() { return zero(true); }
-
-  LIBC_INLINE static constexpr long double inf(bool sign = false) {
-    return FPBits(UP::inf(sign)).get_val();
-  }
-
-  LIBC_INLINE static constexpr long double neg_inf() { return inf(true); }
-
-  LIBC_INLINE static constexpr long double min_normal() {
-    return FPBits(UP::min_normal(false)).get_val();
-  }
-
-  LIBC_INLINE static constexpr long double max_normal() {
-    return FPBits(UP::max_normal(false)).get_val();
-  }
-
-  LIBC_INLINE static constexpr long double min_denormal() {
-    return FPBits(UP::min_subnormal(false)).get_val();
-  }
-
-  LIBC_INLINE static constexpr long double max_denormal() {
-    return FPBits(UP::max_subnormal(false)).get_val();
-  }
-
-  LIBC_INLINE static constexpr long double build_nan(StorageType v) {
-    return FPBits(UP::build_nan(false, v)).get_val();
-  }
-
-  LIBC_INLINE static constexpr long double build_quiet_nan(StorageType v) {
-    return FPBits(UP::build_quiet_nan(false, v)).get_val();
-  }
-};
-
-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..b6e56b459997f5 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/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",

>From b897c069c3e0420f194a2db7840549cd6cac543a Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Mon, 15 Jan 2024 17:04:25 +0000
Subject: [PATCH 3/7] Remove duplicated EXPECT

---
 libc/test/src/__support/FPUtil/fpbits_test.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp
index 8a5b3c7370ef9e..3a452f0b7993c4 100644
--- a/libc/test/src/__support/FPUtil/fpbits_test.cpp
+++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp
@@ -143,9 +143,6 @@ TEST(LlvmLibcFPBitsTest, FPType_X86_Binary80_IsNan) {
     return rep.is_nan();
   };
 
-  EXPECT_TRUE(is_nan(
-      0b0'111111111111111, // NAN : Pseudo-Infinity
-      0b0000000000000000000000000000000000000000000000000000000000000000));
   EXPECT_TRUE(is_nan(
       0b0'111111111111111, // NAN : Pseudo-Infinity
       0b0000000000000000000000000000000000000000000000000000000000000000));

>From b249b1bd78771ec8f1d5efa5363feab696aa9467 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Mon, 15 Jan 2024 17:06:33 +0000
Subject: [PATCH 4/7] Remove duplicate semicolon

---
 libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
index b6e56b459997f5..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.get_val();;
+        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.get_val();;
+        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.get_val();;
+        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.get_val();;
+        return from_bits.get_val();
       } else {
         ++int_val;
       }

>From 7d826ccb7f6f525d4ff8fe5d2de673f33c0163be Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Tue, 16 Jan 2024 09:23:43 +0000
Subject: [PATCH 5/7] Add implementation overview

---
 libc/src/__support/FPUtil/FPBits.h | 34 ++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 58d63a7d01fb82..53bb7cee7e95ca 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 are 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

>From c30b795d3680c4b957f59fcbdae6ba185b7fe287 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Tue, 16 Jan 2024 10:30:31 +0000
Subject: [PATCH 6/7] Address comments

---
 libc/src/__support/FPUtil/FPBits.h | 169 ++++++++++++++++-------------
 1 file changed, 95 insertions(+), 74 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 53bb7cee7e95ca..c2315630e78266 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -167,25 +167,33 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
   static_assert((SIG_MASK | EXP_MASK | SIGN_MASK) == FP_MASK, "masks cover");
 
 protected:
-  enum class Exp : int32_t {
-    ZERO = 0,
+  LIBC_INLINE static constexpr StorageType bit_at(int position) {
+    return StorageType(1) << position;
+  }
+
+  // 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.
-    SUBNORMAL = -EXP_BIAS,
-    // The minimum exponent value for normal numbers.
-    MIN = SUBNORMAL + 1,
-    // The maximum exponent value for normal numbers.
-    MAX = EXP_BIAS,
-    // Special value all ones.
-    INF = MAX + 1,
-    // Aliases
-    BITS_ALL_ZEROES = SUBNORMAL,
-    BITS_ALL_ONES = INF,
+    BITS_ALL_ZEROES = 0,
+    NORMAL_MIN = 1,
+    NORMAL_ZERO = EXP_BIAS,
+    NORMAL_MAX = 2 * EXP_BIAS,
+    // The exponent value for infinity.
+    BITS_ALL_ONES = NORMAL_MAX + 1,
   };
 
-  enum class Sig : StorageType {
+  // 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,
-    ONE = 1,
-    MSB = StorageType(1) << (SIG_LEN - 1),
+    LSB = 1,
+    MSB = bit_at(SIG_LEN - 1),
     // Aliases
     BITS_ALL_ZEROES = ZERO,
     BITS_ALL_ONES = SIG_MASK,
@@ -196,29 +204,34 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
     return static_cast<StorageType>(value);
   }
 
-  LIBC_INLINE friend constexpr Sig operator|(const Sig a, const Sig b) {
-    return Sig{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 Sig operator^(const Sig a, const Sig b) {
-    return Sig{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 Sig operator>>(const Sig a, int shift) {
-    return Sig{storage_cast(storage_cast(a) >> shift)};
+  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(Exp exp) {
-    return storage_cast(static_cast<int32_t>(exp) + EXP_BIAS) << SIG_LEN;
+  LIBC_INLINE static constexpr StorageType encode(BiasedExponent exp) {
+    return (storage_cast(exp) << SIG_LEN) & EXP_MASK;
   }
 
-  LIBC_INLINE static constexpr StorageType encode(Sig value) {
+  LIBC_INLINE static constexpr StorageType encode(Significand value) {
     return storage_cast(value) & SIG_MASK;
   }
 
-  LIBC_INLINE static constexpr StorageType encode(Exp exp, Sig sig) {
+  LIBC_INLINE static constexpr StorageType encode(BiasedExponent exp,
+                                                  Significand sig) {
     return encode(exp) | encode(sig);
   }
 
-  LIBC_INLINE static constexpr StorageType encode(bool sign, Exp exp, Sig 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);
@@ -231,10 +244,6 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
   }
 
 private:
-  LIBC_INLINE static constexpr StorageType bit_at(int position) {
-    return StorageType(1) << position;
-  }
-
   // 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.
@@ -304,8 +313,8 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
   }
 
   LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
-  LIBC_INLINE constexpr StorageType set_uintval(StorageType value) {
-    return bits = (value & FP_MASK);
+  LIBC_INLINE constexpr void set_uintval(StorageType value) {
+    bits = (value & FP_MASK);
   }
 
   LIBC_INLINE constexpr bool is_zero() const {
@@ -330,8 +339,8 @@ template <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
   using UP::MANTISSA_PRECISION;
 
 protected:
-  using typename UP::Exp;
-  using typename UP::Sig;
+  using typename UP::BiasedExponent;
+  using typename UP::Significand;
   using UP::encode;
   using UP::exp_bits;
   using UP::exp_sig_bits;
@@ -339,59 +348,66 @@ template <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
 
 public:
   LIBC_INLINE constexpr bool is_nan() const {
-    return exp_sig_bits() > encode(Exp::BITS_ALL_ONES, Sig::BITS_ALL_ZEROES);
+    return exp_sig_bits() >
+           encode(BiasedExponent::BITS_ALL_ONES, Significand::ZERO);
   }
   LIBC_INLINE constexpr bool is_quiet_nan() const {
-    return exp_sig_bits() >= encode(Exp::BITS_ALL_ONES, Sig::MSB);
+    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(Exp::BITS_ALL_ONES, Sig::BITS_ALL_ZEROES);
+    return exp_sig_bits() ==
+           encode(BiasedExponent::BITS_ALL_ONES, Significand::ZERO);
   }
   LIBC_INLINE constexpr bool is_zero() const {
-    return exp_sig_bits() == encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+    return exp_sig_bits() ==
+           encode(BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO);
   }
   LIBC_INLINE constexpr bool is_finite() const {
-    return exp_bits() != encode(Exp::BITS_ALL_ONES);
+    return exp_bits() != encode(BiasedExponent::BITS_ALL_ONES);
   }
   LIBC_INLINE
   constexpr bool is_subnormal() const {
-    return exp_bits() == encode(Exp::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, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+    return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO);
   }
   LIBC_INLINE static constexpr StorageType one(bool sign = false) {
-    return encode(sign, Exp::ZERO, Sig::BITS_ALL_ZEROES);
+    return encode(sign, BiasedExponent::NORMAL_ZERO, Significand::ZERO);
   }
   LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::ONE);
+    return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::LSB);
   }
   LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::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, Exp::MIN, Sig::BITS_ALL_ZEROES);
+    return encode(sign, BiasedExponent::NORMAL_MIN, Significand::ZERO);
   }
   LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
-    return encode(sign, Exp::MAX, Sig::BITS_ALL_ONES);
+    return encode(sign, BiasedExponent::NORMAL_MAX, Significand::BITS_ALL_ONES);
   }
   LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ONES, Sig::BITS_ALL_ZEROES);
+    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, Exp::BITS_ALL_ONES, (v ? Sig{v} : (Sig::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, Exp::BITS_ALL_ONES, Sig::MSB | Sig{v});
+    return encode(sign, BiasedExponent::BITS_ALL_ONES,
+                  Significand::MSB | Significand{v});
   }
 
   // The function return mantissa with the implicit bit set iff the current
@@ -413,8 +429,8 @@ struct FPRep<FPType::X86_Binary80> : public FPRepBase<FPType::X86_Binary80> {
   using UP::MANTISSA_PRECISION;
 
 protected:
-  using typename UP::Exp;
-  using typename UP::Sig;
+  using typename UP::BiasedExponent;
+  using typename UP::Significand;
   using UP::encode;
 
 public:
@@ -441,70 +457,75 @@ 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(Exp::BITS_ALL_ONES))
+    if (exp_bits() == encode(BiasedExponent::BITS_ALL_ONES))
       return !is_inf();
-    if (exp_bits() != encode(Exp::BITS_ALL_ZEROES))
-      return (sig_bits() & encode(Sig::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(Exp::BITS_ALL_ONES, Sig::MSB | (Sig::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(Exp::BITS_ALL_ONES, Sig::MSB);
+    return exp_sig_bits() ==
+           encode(BiasedExponent::BITS_ALL_ONES, Significand::MSB);
   }
   LIBC_INLINE constexpr bool is_zero() const {
-    return exp_sig_bits() == encode(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+    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(Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+    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(Exp::BITS_ALL_ZEROES) ||
-        exp == encode(Exp::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, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ZEROES);
+    return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::ZERO);
   }
   LIBC_INLINE static constexpr StorageType one(bool sign = false) {
-    return encode(sign, Exp::ZERO, Sig::MSB);
+    return encode(sign, BiasedExponent::NORMAL_ZERO, Significand::MSB);
   }
   LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::ONE);
+    return encode(sign, BiasedExponent::BITS_ALL_ZEROES, Significand::LSB);
   }
   LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ZEROES, Sig::BITS_ALL_ONES ^ Sig::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, Exp::MIN, Sig::MSB);
+    return encode(sign, BiasedExponent::NORMAL_MIN, Significand::MSB);
   }
   LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
-    return encode(sign, Exp::MAX, Sig::BITS_ALL_ONES);
+    return encode(sign, BiasedExponent::NORMAL_MAX, Significand::BITS_ALL_ONES);
   }
   LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
-    return encode(sign, Exp::BITS_ALL_ONES, Sig::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, Exp::BITS_ALL_ONES,
-                  Sig::MSB | (v ? Sig{v} : (Sig::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, Exp::BITS_ALL_ONES,
-                  Sig::MSB | (Sig::MSB >> 1) | Sig{v});
+    return encode(sign, BiasedExponent::BITS_ALL_ONES,
+                  Significand::MSB | (Significand::MSB >> 1) | Significand{v});
   }
 
   LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
@@ -648,8 +669,8 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
   create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
     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::Exp(biased_exp - UP::EXP_BIAS),
-                             typename UP::Sig(mantissa)));
+    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

>From e9fcc5d41493e388799dd40f68b47abf8b0ba5a4 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Tue, 16 Jan 2024 10:36:32 +0000
Subject: [PATCH 7/7] Fix typo

---
 libc/src/__support/FPUtil/FPBits.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index c2315630e78266..2f4c7eb37fcc98 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -31,7 +31,7 @@ enum class FPType {
   X86_Binary80,
 };
 
-// The classes hierarchy are as follows:
+// The classes hierarchy is as follows:
 //
 //             ┌───────────────────┐
 //             │ FPLayout<FPType>  │



More information about the libc-commits mailing list