Skip to content

Commit

Permalink
[libc][i386] FPBit support for 96b long double (#115084)
Browse files Browse the repository at this point in the history
`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
Co-authored-by: Michael Jones <michaelrj@google.com>
  • Loading branch information
nickdesaulniers and michaelrj-google authored Nov 12, 2024
1 parent 0d2ef7a commit 7302c8d
Show file tree
Hide file tree
Showing 12 changed files with 266 additions and 31 deletions.
4 changes: 4 additions & 0 deletions libc/src/__support/FPUtil/FPBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
};

template <> struct FPLayout<FPType::X86_Binary80> {
#if __SIZEOF_LONG_DOUBLE__ == 12
using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
#else
using StorageType = UInt128;
#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;
Expand Down
3 changes: 2 additions & 1 deletion libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
10 changes: 9 additions & 1 deletion libc/src/__support/big_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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>();
}
Expand Down Expand Up @@ -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
Expand Down
17 changes: 3 additions & 14 deletions libc/src/__support/float_to_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
4 changes: 4 additions & 0 deletions libc/src/__support/integer_literals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
14 changes: 7 additions & 7 deletions libc/src/__support/str_to_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 =
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion libc/test/UnitTest/LibcTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -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>);
Expand Down
Loading

0 comments on commit 7302c8d

Please sign in to comment.