[libc-commits] [libc] [libc][i386] FPBit support for 96b long double (PR #115084)

Nick Desaulniers via libc-commits libc-commits at lists.llvm.org
Wed Nov 6 10:45:32 PST 2024


https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/115084

>From 12468c7634a55ab3b2cb511cc338b95387d00902 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 31 Oct 2024 13:52:03 -0700
Subject: [PATCH 1/3] [libc][i386] FPBit support for 96b long double

`long double` is haunted on most architectures, but it is especially so on
i386-linux-gnu. While have 80b of significant data, on i386-linux-gnu this type
has 96b of storage.

Fixes for supporting printf family of conversions for `long double` on
i386-linux-gnu. This allows the libc-stdlib-tests and libc_stdio_unittests
ninja target tests to pass on i386-linux-gnu.

Fixes: #110894
Link: #93709
Link: https://developer.android.com/ndk/guides/abis
Co-authored-by: Michael Jones <michaelrj at google.com>
---
 libc/config/linux/i386/entrypoints.txt        |  49 ++++++
 libc/src/__support/FPUtil/FPBits.h            |   4 +
 .../FPUtil/generic/sqrt_80_bit_long_double.h  |   3 +-
 libc/src/__support/big_int.h                  |  10 +-
 libc/src/__support/float_to_string.h          |  17 +--
 libc/src/__support/integer_literals.h         |   4 +
 libc/src/__support/str_to_float.h             |  14 +-
 libc/test/UnitTest/LibcTest.cpp               |   2 +-
 .../test/src/__support/FPUtil/fpbits_test.cpp | 142 +++++++++++++++++-
 .../src/__support/str_to_long_double_test.cpp |  11 ++
 libc/test/src/stdlib/strtold_test.cpp         |   5 +-
 11 files changed, 230 insertions(+), 31 deletions(-)

diff --git a/libc/config/linux/i386/entrypoints.txt b/libc/config/linux/i386/entrypoints.txt
index 6548c9b816c93f..3a1952aeaa4e9a 100644
--- a/libc/config/linux/i386/entrypoints.txt
+++ b/libc/config/linux/i386/entrypoints.txt
@@ -1,6 +1,55 @@
 set(TARGET_LIBC_ENTRYPOINTS
   # errno.h entrypoints
   libc.src.errno.errno
+
+  # stdio.h entrypoints
+  libc.src.stdio.fdopen
+  libc.src.stdio.fileno
+  libc.src.stdio.fprintf
+  libc.src.stdio.fscanf
+  libc.src.stdio.vfscanf
+  libc.src.stdio.printf
+  libc.src.stdio.remove
+  libc.src.stdio.rename
+  libc.src.stdio.scanf
+  libc.src.stdio.vscanf
+  libc.src.stdio.snprintf
+  libc.src.stdio.sprintf
+  libc.src.stdio.asprintf
+  libc.src.stdio.sscanf
+  libc.src.stdio.vsscanf
+  libc.src.stdio.vfprintf
+  libc.src.stdio.vprintf
+  libc.src.stdio.vsnprintf
+  libc.src.stdio.vsprintf
+  libc.src.stdio.vasprintf
+
+  # stdlib.h entrypoints
+  libc.src.stdlib.abs
+  libc.src.stdlib.atof
+  libc.src.stdlib.atoi
+  libc.src.stdlib.atol
+  libc.src.stdlib.atoll
+  libc.src.stdlib.bsearch
+  libc.src.stdlib.div
+  libc.src.stdlib.labs
+  libc.src.stdlib.ldiv
+  libc.src.stdlib.llabs
+  libc.src.stdlib.lldiv
+  libc.src.stdlib.qsort
+  libc.src.stdlib.qsort_r
+  libc.src.stdlib.rand
+  libc.src.stdlib.srand
+  libc.src.stdlib.strfromd
+  libc.src.stdlib.strfromf
+  libc.src.stdlib.strfroml
+  libc.src.stdlib.strtod
+  libc.src.stdlib.strtof
+  libc.src.stdlib.strtol
+  libc.src.stdlib.strtold
+  libc.src.stdlib.strtoll
+  libc.src.stdlib.strtoul
+  libc.src.stdlib.strtoull
 )
 
 set(TARGET_LIBM_ENTRYPOINTS "")
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 6da89091a8ced9..2994febff2f8a5 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -127,7 +127,11 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
 };
 
 template <> struct FPLayout<FPType::X86_Binary80> {
+#if __SIZEOF_LONG_DOUBLE__ == 16
   using StorageType = UInt128;
+#else
+  using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
+#endif
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
   LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;
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 053348d4c975da..9492d52da04558 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
@@ -21,7 +21,8 @@ namespace LIBC_NAMESPACE_DECL {
 namespace fputil {
 namespace x86 {
 
-LIBC_INLINE void normalize(int &exponent, UInt128 &mantissa) {
+LIBC_INLINE void normalize(int &exponent,
+                           FPBits<long double>::StorageType &mantissa) {
   const unsigned int shift = static_cast<unsigned int>(
       cpp::countl_zero(static_cast<uint64_t>(mantissa)) -
       (8 * sizeof(uint64_t) - 1 - FPBits<long double>::FRACTION_LEN));
diff --git a/libc/src/__support/big_int.h b/libc/src/__support/big_int.h
index bea0abc84b2136..a95ab4ff8e1abf 100644
--- a/libc/src/__support/big_int.h
+++ b/libc/src/__support/big_int.h
@@ -469,7 +469,7 @@ struct BigInt {
                                                     !cpp::is_same_v<T, bool>>>
   LIBC_INLINE constexpr BigInt(T v) {
     constexpr size_t T_SIZE = sizeof(T) * CHAR_BIT;
-    const bool is_neg = Signed && (v < 0);
+    const bool is_neg = v < 0;
     for (size_t i = 0; i < WORD_COUNT; ++i) {
       if (v == 0) {
         extend(i, is_neg);
@@ -504,6 +504,12 @@ struct BigInt {
   // TODO: Reuse the Sign type.
   LIBC_INLINE constexpr bool is_neg() const { return SIGNED && get_msb(); }
 
+  template <size_t OtherBits, bool OtherSigned, typename OtherWordType>
+  LIBC_INLINE constexpr explicit
+  operator BigInt<OtherBits, OtherSigned, OtherWordType>() const {
+    return BigInt<OtherBits, OtherSigned, OtherWordType>(this);
+  }
+
   template <typename T> LIBC_INLINE constexpr explicit operator T() const {
     return to<T>();
   }
@@ -1058,6 +1064,8 @@ struct WordTypeSelector : cpp::type_identity<
 // Except if we request 16 or 32 bits explicitly.
 template <> struct WordTypeSelector<16> : cpp::type_identity<uint16_t> {};
 template <> struct WordTypeSelector<32> : cpp::type_identity<uint32_t> {};
+template <> struct WordTypeSelector<96> : cpp::type_identity<uint32_t> {};
+
 template <size_t Bits>
 using WordTypeSelectorT = typename WordTypeSelector<Bits>::type;
 } // namespace internal
diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h
index e2e06cd0492a90..d5de6f38cb6557 100644
--- a/libc/src/__support/float_to_string.h
+++ b/libc/src/__support/float_to_string.h
@@ -373,23 +373,12 @@ LIBC_INLINE UInt<MID_INT_SIZE> get_table_negative_df(int exponent, size_t i) {
   return result;
 }
 
-LIBC_INLINE uint32_t fast_uint_mod_1e9(const UInt<MID_INT_SIZE> &val) {
-  // The formula for mult_const is:
-  //  1 + floor((2^(bits in target integer size + log_2(divider))) / divider)
-  // Where divider is 10^9 and target integer size is 128.
-  const UInt<MID_INT_SIZE> mult_const(
-      {0x31680A88F8953031u, 0x89705F4136B4A597u, 0});
-  const auto middle = (mult_const * val);
-  const uint64_t result = static_cast<uint64_t>(middle[2]);
-  const uint64_t shifted = result >> 29;
-  return static_cast<uint32_t>(static_cast<uint32_t>(val) -
-                               (EXP10_9 * shifted));
-}
-
 LIBC_INLINE uint32_t mul_shift_mod_1e9(const FPBits::StorageType mantissa,
                                        const UInt<MID_INT_SIZE> &large,
                                        const int32_t shift_amount) {
-  UInt<MID_INT_SIZE + FPBits::STORAGE_LEN> val(large);
+  // make sure the number of bits is always divisible by 64
+  UInt<internal::div_ceil(MID_INT_SIZE + FPBits::STORAGE_LEN, 64) * 64> val(
+      large);
   val = (val * mantissa) >> shift_amount;
   return static_cast<uint32_t>(
       val.div_uint_half_times_pow_2(static_cast<uint32_t>(EXP10_9), 0).value());
diff --git a/libc/src/__support/integer_literals.h b/libc/src/__support/integer_literals.h
index af3da1c443d7b7..4c5c4c41666811 100644
--- a/libc/src/__support/integer_literals.h
+++ b/libc/src/__support/integer_literals.h
@@ -165,6 +165,10 @@ LIBC_INLINE constexpr T parse_with_prefix(const char *ptr) {
 
 } // namespace internal
 
+LIBC_INLINE constexpr UInt<96> operator""_u96(const char *x) {
+  return internal::parse_with_prefix<UInt<96>>(x);
+}
+
 LIBC_INLINE constexpr UInt128 operator""_u128(const char *x) {
   return internal::parse_with_prefix<UInt128>(x);
 }
diff --git a/libc/src/__support/str_to_float.h b/libc/src/__support/str_to_float.h
index a1f4eef03fc3ce..80ea334d15c03f 100644
--- a/libc/src/__support/str_to_float.h
+++ b/libc/src/__support/str_to_float.h
@@ -206,7 +206,7 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
   using FPBits = typename fputil::FPBits<long double>;
   using StorageType = typename FPBits::StorageType;
 
-  StorageType mantissa = init_num.mantissa;
+  UInt128 mantissa = init_num.mantissa;
   int32_t exp10 = init_num.exponent;
 
   // Exp10 Range
@@ -225,7 +225,8 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
   }
 
   // Normalization
-  uint32_t clz = cpp::countl_zero<StorageType>(mantissa);
+  uint32_t clz = cpp::countl_zero(mantissa) -
+                 ((sizeof(UInt128) - sizeof(StorageType)) * CHAR_BIT);
   mantissa <<= clz;
 
   int32_t exp2 =
@@ -276,9 +277,8 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
   // Shifting to 65 bits for 80 bit floats and 113 bits for 128 bit floats
   uint32_t msb =
       static_cast<uint32_t>(final_approx_upper >> (FPBits::STORAGE_LEN - 1));
-  StorageType final_mantissa =
-      final_approx_upper >>
-      (msb + FPBits::STORAGE_LEN - (FPBits::FRACTION_LEN + 3));
+  UInt128 final_mantissa = final_approx_upper >> (msb + FPBits::STORAGE_LEN -
+                                                  (FPBits::FRACTION_LEN + 3));
   exp2 -= static_cast<uint32_t>(1 ^ msb); // same as !msb
 
   if (round == RoundDirection::Nearest) {
@@ -315,7 +315,7 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
   }
 
   ExpandedFloat<long double> output;
-  output.mantissa = final_mantissa;
+  output.mantissa = static_cast<StorageType>(final_mantissa);
   output.exponent = exp2;
   return output;
 }
@@ -558,7 +558,7 @@ clinger_fast_path(ExpandedFloat<T> init_num,
 
   FPBits result;
   T float_mantissa;
-  if constexpr (cpp::is_same_v<StorageType, UInt<128>>) {
+  if constexpr (is_big_int_v<StorageType> || sizeof(T) > sizeof(uint64_t)) {
     float_mantissa =
         (static_cast<T>(uint64_t(mantissa >> 64)) * static_cast<T>(0x1.0p64)) +
         static_cast<T>(uint64_t(mantissa));
diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp
index ad5722f99a4369..afb1368f009050 100644
--- a/libc/test/UnitTest/LibcTest.cpp
+++ b/libc/test/UnitTest/LibcTest.cpp
@@ -44,7 +44,6 @@ cpp::enable_if_t<(cpp::is_integral_v<T> && (sizeof(T) > sizeof(uint64_t))) ||
                      is_big_int_v<T>,
                  cpp::string>
 describeValue(T Value) {
-  static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
   const IntegerToString<T, radix::Hex::WithPrefix> buffer(Value);
   return buffer.view();
 }
@@ -242,6 +241,7 @@ TEST_SPECIALIZATION(__uint128_t);
 
 TEST_SPECIALIZATION(LIBC_NAMESPACE::Int<128>);
 
+TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<96>);
 TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<128>);
 TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<192>);
 TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<256>);
diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp
index bcab0286480c35..edb04c24ae3876 100644
--- a/libc/test/src/__support/FPUtil/fpbits_test.cpp
+++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp
@@ -8,12 +8,14 @@
 
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/FPUtil/fpbits_str.h"
+#include "src/__support/big_int.h"
 #include "src/__support/integer_literals.h"
 #include "src/__support/macros/properties/types.h"
 #include "src/__support/sign.h" // Sign
 #include "test/UnitTest/Test.h"
 
 using LIBC_NAMESPACE::Sign;
+using LIBC_NAMESPACE::UInt;
 using LIBC_NAMESPACE::fputil::FPBits;
 using LIBC_NAMESPACE::fputil::FPType;
 using LIBC_NAMESPACE::fputil::internal::FPRep;
@@ -21,6 +23,7 @@ using LIBC_NAMESPACE::fputil::internal::FPRep;
 using LIBC_NAMESPACE::operator""_u16;
 using LIBC_NAMESPACE::operator""_u32;
 using LIBC_NAMESPACE::operator""_u64;
+using LIBC_NAMESPACE::operator""_u96;
 using LIBC_NAMESPACE::operator""_u128;
 
 TEST(LlvmLibcFPBitsTest, FPType_IEEE754_Binary16) {
@@ -124,6 +127,7 @@ TEST(LlvmLibcFPBitsTest, FPType_IEEE754_Binary128) {
 TEST(LlvmLibcFPBitsTest, FPType_X86_Binary80) {
   using Rep = FPRep<FPType::X86_Binary80>;
 
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_EQ(
       0b0'0000000000000000000000000000000000000000000000000000000000000000000000000000000_u128,
       UInt128(Rep::zero()));
@@ -151,11 +155,43 @@ TEST(LlvmLibcFPBitsTest, FPType_X86_Binary80) {
   EXPECT_EQ(
       0b0'1111111111111111100000000000000000000000000000000000000000000000000000000000000_u128,
       UInt128(Rep::quiet_nan()));
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_EQ(
+      0b0'0000000000000000000000000000000000000000000000000000000000000000000000000000000_u96,
+      UInt<96>(Rep::zero()));
+  EXPECT_EQ(
+      0b0'0111111111111111000000000000000000000000000000000000000000000000000000000000000_u96,
+      UInt<96>(Rep::one()));
+  EXPECT_EQ(
+      0b0'0000000000000000000000000000000000000000000000000000000000000000000000000000001_u96,
+      UInt<96>(Rep::min_subnormal()));
+  EXPECT_EQ(
+      0b0'0000000000000000111111111111111111111111111111111111111111111111111111111111111_u96,
+      UInt<96>(Rep::max_subnormal()));
+  EXPECT_EQ(
+      0b0'0000000000000011000000000000000000000000000000000000000000000000000000000000000_u96,
+      UInt<96>(Rep::min_normal()));
+  EXPECT_EQ(
+      0b0'1111111111111101111111111111111111111111111111111111111111111111111111111111111_u96,
+      UInt<96>(Rep::max_normal()));
+  EXPECT_EQ(
+      0b0'1111111111111111000000000000000000000000000000000000000000000000000000000000000_u96,
+      UInt<96>(Rep::inf()));
+  EXPECT_EQ(
+      0b0'1111111111111111010000000000000000000000000000000000000000000000000000000000000_u96,
+      UInt<96>(Rep::signaling_nan()));
+  EXPECT_EQ(
+      0b0'1111111111111111100000000000000000000000000000000000000000000000000000000000000_u96,
+      UInt<96>(Rep::quiet_nan()));
+#else
+#error "unhandled long double type"
+#endif
 }
 
 TEST(LlvmLibcFPBitsTest, FPType_X86_Binary80_IsNan) {
   using Rep = FPRep<FPType::X86_Binary80>;
 
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_TRUE( // NAN : Pseudo-Infinity
       Rep(0b0'111111111111111'0000000000000000000000000000000000000000000000000000000000000000_u128)
           .is_nan());
@@ -192,6 +228,46 @@ TEST(LlvmLibcFPBitsTest, FPType_X86_Binary80_IsNan) {
   EXPECT_FALSE( // Normalized
       Rep(0b0'111111111111110'1000000000000000000000000000000000000000000000000000000000000000_u128)
           .is_nan());
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_TRUE( // NAN : Pseudo-Infinity
+      Rep(0b0'111111111111111'0000000000000000000000000000000000000000000000000000000000000000_u96)
+          .is_nan());
+  EXPECT_TRUE( // NAN : Pseudo Not a Number
+      Rep(0b0'111111111111111'0000000000000000000000000000000000000000000000000000000000000001_u96)
+          .is_nan());
+  EXPECT_TRUE( // NAN : Pseudo Not a Number
+      Rep(0b0'111111111111111'0100000000000000000000000000000000000000000000000000000000000000_u96)
+          .is_nan());
+  EXPECT_TRUE( // NAN : Signalling Not a Number
+      Rep(0b0'111111111111111'1000000000000000000000000000000000000000000000000000000000000001_u96)
+          .is_nan());
+  EXPECT_TRUE( // NAN : Floating-point Indefinite
+      Rep(0b0'111111111111111'1100000000000000000000000000000000000000000000000000000000000000_u96)
+          .is_nan());
+  EXPECT_TRUE( // NAN : Quiet Not a Number
+      Rep(0b0'111111111111111'1100000000000000000000000000000000000000000000000000000000000001_u96)
+          .is_nan());
+  EXPECT_TRUE( // NAN : Unnormal
+      Rep(0b0'111111111111110'0000000000000000000000000000000000000000000000000000000000000000_u96)
+          .is_nan());
+  EXPECT_FALSE( // Zero
+      Rep(0b0'000000000000000'0000000000000000000000000000000000000000000000000000000000000000_u96)
+          .is_nan());
+  EXPECT_FALSE( // Subnormal
+      Rep(0b0'000000000000000'0000000000000000000000000000000000000000000000000000000000000001_u96)
+          .is_nan());
+  EXPECT_FALSE( // Pseudo Denormal
+      Rep(0b0'000000000000000'1000000000000000000000000000000000000000000000000000000000000001_u96)
+          .is_nan());
+  EXPECT_FALSE( // Infinity
+      Rep(0b0'111111111111111'1000000000000000000000000000000000000000000000000000000000000000_u96)
+          .is_nan());
+  EXPECT_FALSE( // Normalized
+      Rep(0b0'111111111111110'1000000000000000000000000000000000000000000000000000000000000000_u96)
+          .is_nan());
+#else
+#error "unhandled long double type"
+#endif
 }
 
 enum class FP {
@@ -430,6 +506,7 @@ TEST(LlvmLibcFPBitsTest, DoubleType) {
 #ifdef LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80
 TEST(LlvmLibcFPBitsTest, X86LongDoubleType) {
   using LongDoubleBits = FPBits<long double>;
+  using Rep = FPRep<FPType::X86_Binary80>;
 
   EXPECT_STREQ(LIBC_NAMESPACE::str(LongDoubleBits::inf(Sign::POS)).c_str(),
                "(+Infinity)");
@@ -441,62 +518,117 @@ TEST(LlvmLibcFPBitsTest, X86LongDoubleType) {
   LongDoubleBits zero(0.0l);
   EXPECT_TRUE(zero.is_pos());
   EXPECT_EQ(zero.get_biased_exponent(), 0_u16);
-  EXPECT_EQ(zero.get_mantissa(), 0_u128);
-  EXPECT_EQ(zero.uintval(), 0_u128);
+  EXPECT_EQ(zero.get_mantissa(), LongDoubleBits::StorageType(Rep::zero()));
+  EXPECT_EQ(zero.uintval(), LongDoubleBits::StorageType(Rep::zero()));
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_STREQ(
       LIBC_NAMESPACE::str(zero).c_str(),
       "0x00000000000000000000000000000000 = "
       "(S: 0, E: 0x0000, I: 0, M: 0x00000000000000000000000000000000)");
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_STREQ(LIBC_NAMESPACE::str(zero).c_str(),
+               "0x000000000000000000000000 = "
+               "(S: 0, E: 0x0000, I: 0, M: 0x000000000000000000000000)");
+#else
+#error "unhandled long double type"
+#endif
 
   LongDoubleBits negzero(-0.0l);
   EXPECT_TRUE(negzero.is_neg());
   EXPECT_EQ(negzero.get_biased_exponent(), 0_u16);
-  EXPECT_EQ(negzero.get_mantissa(), 0_u128);
+  EXPECT_EQ(negzero.get_mantissa(), LongDoubleBits::StorageType(Rep::zero()));
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_EQ(negzero.uintval(), 0x8000'00000000'00000000_u128);
   EXPECT_STREQ(
       LIBC_NAMESPACE::str(negzero).c_str(),
       "0x00000000000080000000000000000000 = "
       "(S: 1, E: 0x0000, I: 0, M: 0x00000000000000000000000000000000)");
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_EQ(negzero.uintval(), 0x8000'00000000'00000000_u96);
+  EXPECT_STREQ(LIBC_NAMESPACE::str(negzero).c_str(),
+               "0x000080000000000000000000 = "
+               "(S: 1, E: 0x0000, I: 0, M: 0x000000000000000000000000)");
+#else
+#error "unhandled long double type"
+#endif
 
   LongDoubleBits one(1.0l);
   EXPECT_TRUE(one.is_pos());
   EXPECT_EQ(one.get_biased_exponent(), 0x3FFF_u16);
-  EXPECT_EQ(one.get_mantissa(), 0_u128);
+  EXPECT_EQ(one.get_mantissa(), LongDoubleBits::StorageType(Rep::zero()));
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_EQ(one.uintval(), 0x3FFF'80000000'00000000_u128);
   EXPECT_STREQ(
       LIBC_NAMESPACE::str(one).c_str(),
       "0x0000000000003FFF8000000000000000 = "
       "(S: 0, E: 0x3FFF, I: 1, M: 0x00000000000000000000000000000000)");
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_EQ(one.uintval(), 0x3FFF'80000000'00000000_u96);
+  EXPECT_STREQ(LIBC_NAMESPACE::str(one).c_str(),
+               "0x00003FFF8000000000000000 = "
+               "(S: 0, E: 0x3FFF, I: 1, M: 0x000000000000000000000000)");
+#else
+#error "unhandled long double type"
+#endif
 
   LongDoubleBits negone(-1.0l);
   EXPECT_TRUE(negone.is_neg());
   EXPECT_EQ(negone.get_biased_exponent(), 0x3FFF_u16);
-  EXPECT_EQ(negone.get_mantissa(), 0_u128);
+  EXPECT_EQ(negone.get_mantissa(), LongDoubleBits::StorageType(Rep::zero()));
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_EQ(negone.uintval(), 0xBFFF'80000000'00000000_u128);
   EXPECT_STREQ(
       LIBC_NAMESPACE::str(negone).c_str(),
       "0x000000000000BFFF8000000000000000 = "
       "(S: 1, E: 0x3FFF, I: 1, M: 0x00000000000000000000000000000000)");
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_EQ(negone.uintval(), 0xBFFF'80000000'00000000_u96);
+  EXPECT_STREQ(LIBC_NAMESPACE::str(negone).c_str(),
+               "0x0000BFFF8000000000000000 = "
+               "(S: 1, E: 0x3FFF, I: 1, M: 0x000000000000000000000000)");
+#else
+#error "unhandled long double type"
+#endif
 
   LongDoubleBits num(1.125l);
   EXPECT_TRUE(num.is_pos());
   EXPECT_EQ(num.get_biased_exponent(), 0x3FFF_u16);
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_EQ(num.get_mantissa(), 0x10000000'00000000_u128);
   EXPECT_EQ(num.uintval(), 0x3FFF'90000000'00000000_u128);
   EXPECT_STREQ(
       LIBC_NAMESPACE::str(num).c_str(),
       "0x0000000000003FFF9000000000000000 = "
       "(S: 0, E: 0x3FFF, I: 1, M: 0x00000000000000001000000000000000)");
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_EQ(num.get_mantissa(), 0x10000000'00000000_u96);
+  EXPECT_EQ(num.uintval(), 0x3FFF'90000000'00000000_u96);
+  EXPECT_STREQ(LIBC_NAMESPACE::str(num).c_str(),
+               "0x00003FFF9000000000000000 = "
+               "(S: 0, E: 0x3FFF, I: 1, M: 0x000000001000000000000000)");
+#else
+#error "unhandled long double type"
+#endif
 
   LongDoubleBits negnum(-1.125l);
   EXPECT_TRUE(negnum.is_neg());
   EXPECT_EQ(negnum.get_biased_exponent(), 0x3FFF_u16);
+#if __SIZEOF_LONG_DOUBLE__ == 16
   EXPECT_EQ(negnum.get_mantissa(), 0x10000000'00000000_u128);
   EXPECT_EQ(negnum.uintval(), 0xBFFF'90000000'00000000_u128);
   EXPECT_STREQ(
       LIBC_NAMESPACE::str(negnum).c_str(),
       "0x000000000000BFFF9000000000000000 = "
       "(S: 1, E: 0x3FFF, I: 1, M: 0x00000000000000001000000000000000)");
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  EXPECT_EQ(negnum.get_mantissa(), 0x10000000'00000000_u96);
+  EXPECT_EQ(negnum.uintval(), 0xBFFF'90000000'00000000_u96);
+  EXPECT_STREQ(LIBC_NAMESPACE::str(negnum).c_str(),
+               "0x0000BFFF9000000000000000 = "
+               "(S: 1, E: 0x3FFF, I: 1, M: 0x000000001000000000000000)");
+#else
+#error "unhandled long double type"
+#endif
 
   LongDoubleBits quiet_nan = LongDoubleBits::quiet_nan();
   EXPECT_EQ(quiet_nan.is_quiet_nan(), true);
diff --git a/libc/test/src/__support/str_to_long_double_test.cpp b/libc/test/src/__support/str_to_long_double_test.cpp
index 9efa457aac9577..fa7d600f9bff4e 100644
--- a/libc/test/src/__support/str_to_long_double_test.cpp
+++ b/libc/test/src/__support/str_to_long_double_test.cpp
@@ -30,12 +30,23 @@ TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat80Simple) {
 }
 
 TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat80LongerMantissa) {
+#if __SIZEOF_LONG_DOUBLE__ == 16
   eisel_lemire_test(0x12345678'12345678'12345678'12345678_u128, 0,
                     0x91a2b3c091a2b3c1, 16507);
   eisel_lemire_test(0x12345678'12345678'12345678'12345678_u128, 300,
                     0xd97757de56adb65c, 17503);
   eisel_lemire_test(0x12345678'12345678'12345678'12345678_u128, -300,
                     0xc30feb9a7618457d, 15510);
+#elif __SIZEOF_LONG_DOUBLE__ == 12
+  eisel_lemire_test(0x12345678'12345678'12345678_u96, 0, 0x91a2b3c091a2b3c1,
+                    16475);
+  eisel_lemire_test(0x12345678'12345678'12345678_u96, 300, 0xd97757de56adb65c,
+                    17471);
+  eisel_lemire_test(0x12345678'12345678'12345678_u96, -300, 0xc30feb9a7618457d,
+                    15478);
+#else
+#error "unhandled long double type"
+#endif
 }
 
 // These tests check numbers at the edge of the DETAILED_POWERS_OF_TEN table.
diff --git a/libc/test/src/stdlib/strtold_test.cpp b/libc/test/src/stdlib/strtold_test.cpp
index 2c9f542930bf86..b209c85b88e369 100644
--- a/libc/test/src/stdlib/strtold_test.cpp
+++ b/libc/test/src/stdlib/strtold_test.cpp
@@ -75,8 +75,9 @@ class LlvmLibcStrToLDTest : public LIBC_NAMESPACE::testing::Test {
     //         +-- 15 Exponent Bits
     char *str_end = nullptr;
 
-    LIBC_NAMESPACE::fputil::FPBits<long double> expected_fp =
-        LIBC_NAMESPACE::fputil::FPBits<long double>(expectedRawData);
+    using FPBits = LIBC_NAMESPACE::fputil::FPBits<long double>;
+    FPBits expected_fp =
+        FPBits(static_cast<FPBits::StorageType>(expectedRawData));
     const int expected_errno = expectedErrno;
 
     LIBC_NAMESPACE::libc_errno = 0;

>From be223620e9c8466ca88e4b4f3cf06ce92cfe329f Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Wed, 6 Nov 2024 10:44:54 -0800
Subject: [PATCH 2/3] test case

---
 libc/test/src/__support/big_int_test.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/libc/test/src/__support/big_int_test.cpp b/libc/test/src/__support/big_int_test.cpp
index 471ca72a8f6e0c..02dd9ff70f942a 100644
--- a/libc/test/src/__support/big_int_test.cpp
+++ b/libc/test/src/__support/big_int_test.cpp
@@ -1067,4 +1067,12 @@ TEST(LlvmLibcUIntClassTest, SignedOtherWordTypeCastTests) {
   ASSERT_TRUE(bigger_back_plus_a + bigger_back_minus_a == zero_96);
 }
 
+TEST(LlvmLibcUIntClassTest, MixedSignednessOtherWordTypeCastTests) {
+  using LL_UInt96 = BigInt<96, false, uint32_t>;
+  LL_UInt96 x = -123;
+  // ensure that -123 gets extended, even though the input type is signed while
+  // the BigInt is unsigned.
+  ASSERT_EQ(int64_t(x), -123LL);
+}
+
 } // namespace LIBC_NAMESPACE_DECL

>From 474aad73d1213340663ceb631ba2c78ef774b9ca Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Wed, 6 Nov 2024 10:45:11 -0800
Subject: [PATCH 3/3] todo for android

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

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 2994febff2f8a5..2b030b51c3a8b3 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -127,10 +127,17 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
 };
 
 template <> struct FPLayout<FPType::X86_Binary80> {
+// x86_64
 #if __SIZEOF_LONG_DOUBLE__ == 16
   using StorageType = UInt128;
-#else
+// i386-linux-gnu
+#elif __SIZEOF_LONG_DOUBLE__ == 12
   using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
+#else
+# TODO: https://github.com/llvm/llvm-project/issues/115184
+# Android i386 uses `long double == double` i.e. `sizeof(long double) == 8`
+# https://developer.android.com/ndk/guides/abis#x86
+#error "unexpected size of long double"
 #endif
   LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
   LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;



More information about the libc-commits mailing list