Skip to content

Commit 7302c8d

Browse files
[libc][i386] FPBit support for 96b long double (#115084)
`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 <[email protected]>
1 parent 0d2ef7a commit 7302c8d

File tree

12 files changed

+266
-31
lines changed

12 files changed

+266
-31
lines changed

libc/src/__support/FPUtil/FPBits.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,11 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
127127
};
128128

129129
template <> struct FPLayout<FPType::X86_Binary80> {
130+
#if __SIZEOF_LONG_DOUBLE__ == 12
131+
using StorageType = UInt<__SIZEOF_LONG_DOUBLE__ * CHAR_BIT>;
132+
#else
130133
using StorageType = UInt128;
134+
#endif
131135
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
132136
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
133137
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;

libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ namespace LIBC_NAMESPACE_DECL {
2121
namespace fputil {
2222
namespace x86 {
2323

24-
LIBC_INLINE void normalize(int &exponent, UInt128 &mantissa) {
24+
LIBC_INLINE void normalize(int &exponent,
25+
FPBits<long double>::StorageType &mantissa) {
2526
const unsigned int shift = static_cast<unsigned int>(
2627
cpp::countl_zero(static_cast<uint64_t>(mantissa)) -
2728
(8 * sizeof(uint64_t) - 1 - FPBits<long double>::FRACTION_LEN));

libc/src/__support/big_int.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ struct BigInt {
469469
!cpp::is_same_v<T, bool>>>
470470
LIBC_INLINE constexpr BigInt(T v) {
471471
constexpr size_t T_SIZE = sizeof(T) * CHAR_BIT;
472-
const bool is_neg = Signed && (v < 0);
472+
const bool is_neg = v < 0;
473473
for (size_t i = 0; i < WORD_COUNT; ++i) {
474474
if (v == 0) {
475475
extend(i, is_neg);
@@ -504,6 +504,12 @@ struct BigInt {
504504
// TODO: Reuse the Sign type.
505505
LIBC_INLINE constexpr bool is_neg() const { return SIGNED && get_msb(); }
506506

507+
template <size_t OtherBits, bool OtherSigned, typename OtherWordType>
508+
LIBC_INLINE constexpr explicit
509+
operator BigInt<OtherBits, OtherSigned, OtherWordType>() const {
510+
return BigInt<OtherBits, OtherSigned, OtherWordType>(this);
511+
}
512+
507513
template <typename T> LIBC_INLINE constexpr explicit operator T() const {
508514
return to<T>();
509515
}
@@ -1058,6 +1064,8 @@ struct WordTypeSelector : cpp::type_identity<
10581064
// Except if we request 16 or 32 bits explicitly.
10591065
template <> struct WordTypeSelector<16> : cpp::type_identity<uint16_t> {};
10601066
template <> struct WordTypeSelector<32> : cpp::type_identity<uint32_t> {};
1067+
template <> struct WordTypeSelector<96> : cpp::type_identity<uint32_t> {};
1068+
10611069
template <size_t Bits>
10621070
using WordTypeSelectorT = typename WordTypeSelector<Bits>::type;
10631071
} // namespace internal

libc/src/__support/float_to_string.h

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -373,23 +373,12 @@ LIBC_INLINE UInt<MID_INT_SIZE> get_table_negative_df(int exponent, size_t i) {
373373
return result;
374374
}
375375

376-
LIBC_INLINE uint32_t fast_uint_mod_1e9(const UInt<MID_INT_SIZE> &val) {
377-
// The formula for mult_const is:
378-
// 1 + floor((2^(bits in target integer size + log_2(divider))) / divider)
379-
// Where divider is 10^9 and target integer size is 128.
380-
const UInt<MID_INT_SIZE> mult_const(
381-
{0x31680A88F8953031u, 0x89705F4136B4A597u, 0});
382-
const auto middle = (mult_const * val);
383-
const uint64_t result = static_cast<uint64_t>(middle[2]);
384-
const uint64_t shifted = result >> 29;
385-
return static_cast<uint32_t>(static_cast<uint32_t>(val) -
386-
(EXP10_9 * shifted));
387-
}
388-
389376
LIBC_INLINE uint32_t mul_shift_mod_1e9(const FPBits::StorageType mantissa,
390377
const UInt<MID_INT_SIZE> &large,
391378
const int32_t shift_amount) {
392-
UInt<MID_INT_SIZE + FPBits::STORAGE_LEN> val(large);
379+
// make sure the number of bits is always divisible by 64
380+
UInt<internal::div_ceil(MID_INT_SIZE + FPBits::STORAGE_LEN, 64) * 64> val(
381+
large);
393382
val = (val * mantissa) >> shift_amount;
394383
return static_cast<uint32_t>(
395384
val.div_uint_half_times_pow_2(static_cast<uint32_t>(EXP10_9), 0).value());

libc/src/__support/integer_literals.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ LIBC_INLINE constexpr T parse_with_prefix(const char *ptr) {
165165

166166
} // namespace internal
167167

168+
LIBC_INLINE constexpr UInt<96> operator""_u96(const char *x) {
169+
return internal::parse_with_prefix<UInt<96>>(x);
170+
}
171+
168172
LIBC_INLINE constexpr UInt128 operator""_u128(const char *x) {
169173
return internal::parse_with_prefix<UInt128>(x);
170174
}

libc/src/__support/str_to_float.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
206206
using FPBits = typename fputil::FPBits<long double>;
207207
using StorageType = typename FPBits::StorageType;
208208

209-
StorageType mantissa = init_num.mantissa;
209+
UInt128 mantissa = init_num.mantissa;
210210
int32_t exp10 = init_num.exponent;
211211

212212
// Exp10 Range
@@ -225,7 +225,8 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
225225
}
226226

227227
// Normalization
228-
uint32_t clz = cpp::countl_zero<StorageType>(mantissa);
228+
uint32_t clz = cpp::countl_zero(mantissa) -
229+
((sizeof(UInt128) - sizeof(StorageType)) * CHAR_BIT);
229230
mantissa <<= clz;
230231

231232
int32_t exp2 =
@@ -276,9 +277,8 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
276277
// Shifting to 65 bits for 80 bit floats and 113 bits for 128 bit floats
277278
uint32_t msb =
278279
static_cast<uint32_t>(final_approx_upper >> (FPBits::STORAGE_LEN - 1));
279-
StorageType final_mantissa =
280-
final_approx_upper >>
281-
(msb + FPBits::STORAGE_LEN - (FPBits::FRACTION_LEN + 3));
280+
UInt128 final_mantissa = final_approx_upper >> (msb + FPBits::STORAGE_LEN -
281+
(FPBits::FRACTION_LEN + 3));
282282
exp2 -= static_cast<uint32_t>(1 ^ msb); // same as !msb
283283

284284
if (round == RoundDirection::Nearest) {
@@ -315,7 +315,7 @@ eisel_lemire<long double>(ExpandedFloat<long double> init_num,
315315
}
316316

317317
ExpandedFloat<long double> output;
318-
output.mantissa = final_mantissa;
318+
output.mantissa = static_cast<StorageType>(final_mantissa);
319319
output.exponent = exp2;
320320
return output;
321321
}
@@ -558,7 +558,7 @@ clinger_fast_path(ExpandedFloat<T> init_num,
558558

559559
FPBits result;
560560
T float_mantissa;
561-
if constexpr (cpp::is_same_v<StorageType, UInt<128>>) {
561+
if constexpr (is_big_int_v<StorageType> || sizeof(T) > sizeof(uint64_t)) {
562562
float_mantissa =
563563
(static_cast<T>(uint64_t(mantissa >> 64)) * static_cast<T>(0x1.0p64)) +
564564
static_cast<T>(uint64_t(mantissa));

libc/test/UnitTest/LibcTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ cpp::enable_if_t<(cpp::is_integral_v<T> && (sizeof(T) > sizeof(uint64_t))) ||
4444
is_big_int_v<T>,
4545
cpp::string>
4646
describeValue(T Value) {
47-
static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
4847
const IntegerToString<T, radix::Hex::WithPrefix> buffer(Value);
4948
return buffer.view();
5049
}
@@ -242,6 +241,7 @@ TEST_SPECIALIZATION(__uint128_t);
242241

243242
TEST_SPECIALIZATION(LIBC_NAMESPACE::Int<128>);
244243

244+
TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<96>);
245245
TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<128>);
246246
TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<192>);
247247
TEST_SPECIALIZATION(LIBC_NAMESPACE::UInt<256>);

0 commit comments

Comments
 (0)