mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 06:06:07 +00:00
Revert "[libc] Remove UB specializations of type traits for BigInt
" (#84297)
Reverts llvm/llvm-project#84035 Several bots are failing: - https://lab.llvm.org/buildbot/#/builders/223/builds/37522 - https://lab.llvm.org/buildbot/#/builders/162/builds/51978 - https://lab.llvm.org/buildbot/#/builders/163/builds/52560 - https://lab.llvm.org/buildbot/#/builders/250/builds/19619
This commit is contained in:
parent
84f483dbee
commit
27844cb2fa
@ -95,7 +95,6 @@ add_header_library(
|
||||
HDRS
|
||||
integer_to_string.h
|
||||
DEPENDS
|
||||
.uint
|
||||
libc.src.__support.common
|
||||
libc.src.__support.CPP.algorithm
|
||||
libc.src.__support.CPP.limits
|
||||
|
@ -27,14 +27,13 @@ namespace LIBC_NAMESPACE::cpp {
|
||||
|
||||
// This implementation of bit_cast requires trivially-constructible To, to avoid
|
||||
// UB in the implementation.
|
||||
template <typename To, typename From>
|
||||
LIBC_INLINE constexpr cpp::enable_if_t<
|
||||
(sizeof(To) == sizeof(From)) &&
|
||||
cpp::is_trivially_constructible<To>::value &&
|
||||
cpp::is_trivially_copyable<To>::value &&
|
||||
cpp::is_trivially_copyable<From>::value,
|
||||
To>
|
||||
bit_cast(const From &from) {
|
||||
template <
|
||||
typename To, typename From,
|
||||
typename = cpp::enable_if_t<sizeof(To) == sizeof(From) &&
|
||||
cpp::is_trivially_constructible<To>::value &&
|
||||
cpp::is_trivially_copyable<To>::value &&
|
||||
cpp::is_trivially_copyable<From>::value>>
|
||||
LIBC_INLINE constexpr To bit_cast(const From &from) {
|
||||
MSAN_UNPOISON(&from, sizeof(From));
|
||||
#if LIBC_HAS_BUILTIN(__builtin_bit_cast)
|
||||
return __builtin_bit_cast(To, from);
|
||||
@ -52,10 +51,8 @@ bit_cast(const From &from) {
|
||||
#endif // LIBC_HAS_BUILTIN(__builtin_bit_cast)
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>,
|
||||
bool>
|
||||
has_single_bit(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr bool has_single_bit(T value) {
|
||||
return (value != 0) && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
@ -73,9 +70,8 @@ has_single_bit(T value) {
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// Returns cpp::numeric_limits<T>::digits on an input of 0.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
countr_zero(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int countr_zero(T value) {
|
||||
if (!value)
|
||||
return cpp::numeric_limits<T>::digits;
|
||||
if (value & 0x1)
|
||||
@ -107,9 +103,8 @@ ADD_SPECIALIZATION(countr_zero, unsigned long long, __builtin_ctzll)
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// Returns cpp::numeric_limits<T>::digits on an input of 0.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
countl_zero(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int countl_zero(T value) {
|
||||
if (!value)
|
||||
return cpp::numeric_limits<T>::digits;
|
||||
// Bisection method.
|
||||
@ -140,9 +135,8 @@ ADD_SPECIALIZATION(countl_zero, unsigned long long, __builtin_clzll)
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// Returns cpp::numeric_limits<T>::digits on an input of all ones.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
countl_one(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int countl_one(T value) {
|
||||
return cpp::countl_zero<T>(~value);
|
||||
}
|
||||
|
||||
@ -153,9 +147,8 @@ countl_one(T value) {
|
||||
/// Only unsigned integral types are allowed.
|
||||
///
|
||||
/// Returns cpp::numeric_limits<T>::digits on an input of all ones.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
countr_one(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int countr_one(T value) {
|
||||
return cpp::countr_zero<T>(~value);
|
||||
}
|
||||
|
||||
@ -163,9 +156,8 @@ countr_one(T value) {
|
||||
/// Returns 0 otherwise.
|
||||
///
|
||||
/// Ex. bit_width(5) == 3.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
bit_width(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int bit_width(T value) {
|
||||
return cpp::numeric_limits<T>::digits - cpp::countl_zero(value);
|
||||
}
|
||||
|
||||
@ -173,9 +165,8 @@ bit_width(T value) {
|
||||
/// nonzero. Returns 0 otherwise.
|
||||
///
|
||||
/// Ex. bit_floor(5) == 4.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
|
||||
bit_floor(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr T bit_floor(T value) {
|
||||
if (!value)
|
||||
return 0;
|
||||
return T(1) << (cpp::bit_width(value) - 1);
|
||||
@ -188,9 +179,8 @@ bit_floor(T value) {
|
||||
///
|
||||
/// The return value is undefined if the input is larger than the largest power
|
||||
/// of two representable in T.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
|
||||
bit_ceil(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr T bit_ceil(T value) {
|
||||
if (value < 2)
|
||||
return 1;
|
||||
return T(1) << cpp::bit_width<T>(value - 1u);
|
||||
@ -200,31 +190,28 @@ bit_ceil(T value) {
|
||||
// from https://blog.regehr.org/archives/1063.
|
||||
|
||||
// Forward-declare rotr so that rotl can use it.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
|
||||
rotr(T value, int rotate);
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate);
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
|
||||
rotl(T value, int rotate) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr T rotl(T value, int rotate) {
|
||||
constexpr unsigned N = cpp::numeric_limits<T>::digits;
|
||||
rotate = rotate % N;
|
||||
if (!rotate)
|
||||
return value;
|
||||
if (rotate < 0)
|
||||
return cpp::rotr<T>(value, -rotate);
|
||||
return cpp::rotr(value, -rotate);
|
||||
return (value << rotate) | (value >> (N - rotate));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, T>
|
||||
rotr(T value, int rotate) {
|
||||
template <typename T, typename>
|
||||
[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate) {
|
||||
constexpr unsigned N = cpp::numeric_limits<T>::digits;
|
||||
rotate = rotate % N;
|
||||
if (!rotate)
|
||||
return value;
|
||||
if (rotate < 0)
|
||||
return cpp::rotl<T>(value, -rotate);
|
||||
return cpp::rotl(value, -rotate);
|
||||
return (value >> rotate) | (value << (N - rotate));
|
||||
}
|
||||
|
||||
@ -239,44 +226,33 @@ LIBC_INLINE constexpr To bit_or_static_cast(const From &from) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove from 'bit.h' as it is not a standard function.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
first_leading_zero(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int first_leading_zero(T value) {
|
||||
return value == cpp::numeric_limits<T>::max() ? 0 : countl_one(value) + 1;
|
||||
}
|
||||
|
||||
// TODO: remove from 'bit.h' as it is not a standard function.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
first_leading_one(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int first_leading_one(T value) {
|
||||
return first_leading_zero(static_cast<T>(~value));
|
||||
}
|
||||
|
||||
// TODO: remove from 'bit.h' as it is not a standard function.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
first_trailing_zero(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int first_trailing_zero(T value) {
|
||||
return value == cpp::numeric_limits<T>::max()
|
||||
? 0
|
||||
: countr_zero(static_cast<T>(~value)) + 1;
|
||||
}
|
||||
|
||||
// TODO: remove from 'bit.h' as it is not a standard function.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
first_trailing_one(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int first_trailing_one(T value) {
|
||||
return value == cpp::numeric_limits<T>::max() ? 0 : countr_zero(value) + 1;
|
||||
}
|
||||
|
||||
/// Count number of 1's aka population count or hamming weight.
|
||||
///
|
||||
/// Only unsigned integral types are allowed.
|
||||
// TODO: rename as 'popcount' to follow the standard
|
||||
// https://en.cppreference.com/w/cpp/numeric/popcount
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
count_ones(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int count_ones(T value) {
|
||||
int count = 0;
|
||||
for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
|
||||
if ((value >> i) & 0x1)
|
||||
@ -296,10 +272,8 @@ ADD_SPECIALIZATION(unsigned long long, __builtin_popcountll)
|
||||
// TODO: 128b specializations?
|
||||
#undef ADD_SPECIALIZATION
|
||||
|
||||
// TODO: remove from 'bit.h' as it is not a standard function.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
|
||||
count_zeros(T value) {
|
||||
template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
|
||||
[[nodiscard]] LIBC_INLINE constexpr int count_zeros(T value) {
|
||||
return count_ones<T>(static_cast<T>(~value));
|
||||
}
|
||||
|
||||
|
@ -43,9 +43,6 @@ struct BigInt {
|
||||
static_assert(is_integral_v<WordType> && is_unsigned_v<WordType>,
|
||||
"WordType must be unsigned integer.");
|
||||
|
||||
using word_type = WordType;
|
||||
LIBC_INLINE_VAR static constexpr bool SIGNED = Signed;
|
||||
LIBC_INLINE_VAR static constexpr size_t BITS = Bits;
|
||||
LIBC_INLINE_VAR
|
||||
static constexpr size_t WORD_SIZE = sizeof(WordType) * CHAR_BIT;
|
||||
|
||||
@ -53,10 +50,6 @@ struct BigInt {
|
||||
"Number of bits in BigInt should be a multiple of WORD_SIZE.");
|
||||
|
||||
LIBC_INLINE_VAR static constexpr size_t WORD_COUNT = Bits / WORD_SIZE;
|
||||
|
||||
using unsigned_type = BigInt<BITS, false, word_type>;
|
||||
using signed_type = BigInt<BITS, true, word_type>;
|
||||
|
||||
cpp::array<WordType, WORD_COUNT> val{};
|
||||
|
||||
LIBC_INLINE constexpr BigInt() = default;
|
||||
@ -586,33 +579,19 @@ struct BigInt {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO: remove and use cpp::countl_zero below.
|
||||
[[nodiscard]] LIBC_INLINE constexpr int clz() const {
|
||||
constexpr int word_digits = cpp::numeric_limits<word_type>::digits;
|
||||
int leading_zeroes = 0;
|
||||
for (auto i = val.size(); i > 0;) {
|
||||
--i;
|
||||
const int zeroes = countl_zero(val[i]);
|
||||
leading_zeroes += zeroes;
|
||||
if (zeroes != word_digits)
|
||||
LIBC_INLINE constexpr uint64_t clz() {
|
||||
uint64_t leading_zeroes = 0;
|
||||
for (size_t i = WORD_COUNT; i > 0; --i) {
|
||||
if (val[i - 1] == 0) {
|
||||
leading_zeroes += WORD_SIZE;
|
||||
} else {
|
||||
leading_zeroes += countl_zero(val[i - 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return leading_zeroes;
|
||||
}
|
||||
|
||||
// TODO: remove and use cpp::countr_zero below.
|
||||
[[nodiscard]] LIBC_INLINE constexpr int ctz() const {
|
||||
constexpr int word_digits = cpp::numeric_limits<word_type>::digits;
|
||||
int trailing_zeroes = 0;
|
||||
for (auto word : val) {
|
||||
const int zeroes = countr_zero(word);
|
||||
trailing_zeroes += zeroes;
|
||||
if (zeroes != word_digits)
|
||||
break;
|
||||
}
|
||||
return trailing_zeroes;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr void shift_left(size_t s) {
|
||||
if constexpr (Bits == WORD_SIZE) {
|
||||
// Use native types if possible.
|
||||
@ -937,123 +916,66 @@ public:
|
||||
LIBC_INLINE_VAR static constexpr int digits = 128;
|
||||
};
|
||||
|
||||
// type traits to determine whether a T is a cpp::BigInt.
|
||||
template <typename T> struct is_big_int : cpp::false_type {};
|
||||
// Provides is_integral of U/Int<128>, U/Int<192>, U/Int<256>.
|
||||
template <size_t Bits, bool Signed, typename T>
|
||||
struct is_integral<BigInt<Bits, Signed, T>> : cpp::true_type {};
|
||||
|
||||
// Provides is_unsigned of UInt<128>, UInt<192>, UInt<256>.
|
||||
template <size_t Bits, bool Signed, typename T>
|
||||
struct is_unsigned<BigInt<Bits, Signed, T>> : cpp::bool_constant<!Signed> {};
|
||||
|
||||
template <size_t Bits, bool Signed, typename T>
|
||||
struct is_big_int<BigInt<Bits, Signed, T>> : cpp::true_type {};
|
||||
struct make_unsigned<BigInt<Bits, Signed, T>>
|
||||
: type_identity<BigInt<Bits, false, T>> {};
|
||||
|
||||
template <class T>
|
||||
LIBC_INLINE_VAR constexpr bool is_big_int_v = is_big_int<T>::value;
|
||||
template <size_t Bits, bool Signed, typename T>
|
||||
struct make_signed<BigInt<Bits, Signed, T>>
|
||||
: type_identity<BigInt<Bits, true, T>> {};
|
||||
|
||||
// Specialization of cpp::bit_cast ('bit.h') from T to BigInt.
|
||||
template <typename To, typename From>
|
||||
LIBC_INLINE constexpr cpp::enable_if_t<
|
||||
(sizeof(To) == sizeof(From)) && cpp::is_trivially_copyable<To>::value &&
|
||||
cpp::is_trivially_copyable<From>::value && is_big_int<To>::value,
|
||||
To>
|
||||
bit_cast(const From &from) {
|
||||
namespace internal {
|
||||
template <typename T> struct is_custom_uint : cpp::false_type {};
|
||||
|
||||
template <size_t Bits, bool Signed, typename T>
|
||||
struct is_custom_uint<BigInt<Bits, Signed, T>> : cpp::true_type {};
|
||||
} // namespace internal
|
||||
|
||||
// bit_cast to UInt
|
||||
// Note: The standard scheme for SFINAE selection is to have exactly one
|
||||
// function instanciation valid at a time. This is usually done by having a
|
||||
// predicate in one function and the negated predicate in the other one.
|
||||
// e.g.
|
||||
// template<typename = cpp::enable_if_t< is_custom_uint<To>::value == true> ...
|
||||
// template<typename = cpp::enable_if_t< is_custom_uint<To>::value == false> ...
|
||||
//
|
||||
// Unfortunately this would make the default 'cpp::bit_cast' aware of
|
||||
// 'is_custom_uint' (or any other customization). To prevent exposing all
|
||||
// customizations in the original function, we create a different function with
|
||||
// four 'typename's instead of three - otherwise it would be considered as a
|
||||
// redeclaration of the same function leading to "error: template parameter
|
||||
// redefines default argument".
|
||||
template <typename To, typename From,
|
||||
typename = cpp::enable_if_t<sizeof(To) == sizeof(From) &&
|
||||
cpp::is_trivially_copyable<To>::value &&
|
||||
cpp::is_trivially_copyable<From>::value>,
|
||||
typename = cpp::enable_if_t<internal::is_custom_uint<To>::value>>
|
||||
LIBC_INLINE constexpr To bit_cast(const From &from) {
|
||||
To out;
|
||||
using Storage = decltype(out.val);
|
||||
out.val = cpp::bit_cast<Storage>(from);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Specialization of cpp::bit_cast ('bit.h') from BigInt to T.
|
||||
template <typename To, size_t Bits>
|
||||
LIBC_INLINE constexpr cpp::enable_if_t<
|
||||
sizeof(To) == sizeof(UInt<Bits>) &&
|
||||
cpp::is_trivially_constructible<To>::value &&
|
||||
cpp::is_trivially_copyable<To>::value &&
|
||||
cpp::is_trivially_copyable<UInt<Bits>>::value,
|
||||
To>
|
||||
bit_cast(const UInt<Bits> &from) {
|
||||
// bit_cast from UInt
|
||||
template <
|
||||
typename To, size_t Bits,
|
||||
typename = cpp::enable_if_t<sizeof(To) == sizeof(UInt<Bits>) &&
|
||||
cpp::is_trivially_constructible<To>::value &&
|
||||
cpp::is_trivially_copyable<To>::value &&
|
||||
cpp::is_trivially_copyable<UInt<Bits>>::value>>
|
||||
LIBC_INLINE constexpr To bit_cast(const UInt<Bits> &from) {
|
||||
return cpp::bit_cast<To>(from.val);
|
||||
}
|
||||
|
||||
// Specialization of cpp::has_single_bit ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, bool>
|
||||
has_single_bit(T value) {
|
||||
int bits = 0;
|
||||
for (auto word : value.val) {
|
||||
if (word == 0)
|
||||
continue;
|
||||
bits += count_ones(word);
|
||||
if (bits > 1)
|
||||
return false;
|
||||
}
|
||||
return bits == 1;
|
||||
}
|
||||
|
||||
// Specialization of cpp::countr_zero ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
|
||||
countr_zero(const T &value) {
|
||||
return value.ctz();
|
||||
}
|
||||
|
||||
// Specialization of cpp::countl_zero ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
|
||||
countl_zero(const T &value) {
|
||||
return value.clz();
|
||||
}
|
||||
|
||||
// Specialization of cpp::countl_one ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
|
||||
countl_one(T value) {
|
||||
// TODO : Implement a faster version not involving operator~.
|
||||
return cpp::countl_zero<T>(~value);
|
||||
}
|
||||
|
||||
// Specialization of cpp::countr_one ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
|
||||
countr_one(T value) {
|
||||
// TODO : Implement a faster version not involving operator~.
|
||||
return cpp::countr_zero<T>(~value);
|
||||
}
|
||||
|
||||
// Specialization of cpp::bit_width ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, int>
|
||||
bit_width(T value) {
|
||||
return cpp::numeric_limits<T>::digits - cpp::countl_zero(value);
|
||||
}
|
||||
|
||||
// Forward-declare rotr so that rotl can use it.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, T>
|
||||
rotr(T value, int rotate);
|
||||
|
||||
// Specialization of cpp::rotl ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, T>
|
||||
rotl(T value, int rotate) {
|
||||
constexpr unsigned N = cpp::numeric_limits<T>::digits;
|
||||
rotate = rotate % N;
|
||||
if (!rotate)
|
||||
return value;
|
||||
if (rotate < 0)
|
||||
return cpp::rotr<T>(value, -rotate);
|
||||
return (value << rotate) | (value >> (N - rotate));
|
||||
}
|
||||
|
||||
// Specialization of cpp::rotr ('bit.h') for BigInt.
|
||||
template <typename T>
|
||||
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_big_int_v<T>, T>
|
||||
rotr(T value, int rotate) {
|
||||
constexpr unsigned N = cpp::numeric_limits<T>::digits;
|
||||
rotate = rotate % N;
|
||||
if (!rotate)
|
||||
return value;
|
||||
if (rotate < 0)
|
||||
return cpp::rotl<T>(value, -rotate);
|
||||
return (value >> rotate) | (value << (N - rotate));
|
||||
}
|
||||
|
||||
} // namespace LIBC_NAMESPACE::cpp
|
||||
|
||||
#endif // LLVM_LIBC_SRC___SUPPORT_UINT_H
|
||||
|
@ -713,7 +713,7 @@ template <> class FloatToString<long double> {
|
||||
float_as_fixed.shift_left(SHIFT_AMOUNT);
|
||||
|
||||
// If there are still digits above the decimal point, handle those.
|
||||
if (float_as_fixed.clz() < static_cast<int>(EXTRA_INT_WIDTH)) {
|
||||
if (float_as_fixed.clz() < EXTRA_INT_WIDTH) {
|
||||
cpp::UInt<EXTRA_INT_WIDTH> above_decimal_point =
|
||||
float_as_fixed >> FLOAT_AS_INT_WIDTH;
|
||||
|
||||
|
@ -67,7 +67,6 @@
|
||||
#include "src/__support/CPP/span.h"
|
||||
#include "src/__support/CPP/string_view.h"
|
||||
#include "src/__support/CPP/type_traits.h"
|
||||
#include "src/__support/UInt.h" // is_big_int
|
||||
#include "src/__support/common.h"
|
||||
|
||||
namespace LIBC_NAMESPACE {
|
||||
@ -150,18 +149,6 @@ public:
|
||||
using StringBufferWriter = StringBufferWriterImpl<true>;
|
||||
using BackwardStringBufferWriter = StringBufferWriterImpl<false>;
|
||||
|
||||
template <typename T, class = void> struct IntegerWriterUnsigned {};
|
||||
|
||||
template <typename T>
|
||||
struct IntegerWriterUnsigned<T, cpp::enable_if_t<cpp::is_integral_v<T>>> {
|
||||
using type = cpp::make_unsigned_t<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IntegerWriterUnsigned<T, cpp::enable_if_t<cpp::is_big_int_v<T>>> {
|
||||
using type = typename T::unsigned_type;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
namespace radix {
|
||||
@ -176,7 +163,7 @@ template <size_t radix> using Custom = details::Fmt<radix>;
|
||||
|
||||
// See file header for documentation.
|
||||
template <typename T, typename Fmt = radix::Dec> class IntegerToString {
|
||||
static_assert(cpp::is_integral_v<T> || cpp::is_big_int_v<T>);
|
||||
static_assert(cpp::is_integral_v<T>);
|
||||
|
||||
LIBC_INLINE static constexpr size_t compute_buffer_size() {
|
||||
constexpr auto MAX_DIGITS = []() -> size_t {
|
||||
@ -221,8 +208,8 @@ template <typename T, typename Fmt = radix::Dec> class IntegerToString {
|
||||
|
||||
// An internal stateless structure that handles the number formatting logic.
|
||||
struct IntegerWriter {
|
||||
static_assert(cpp::is_integral_v<T> || cpp::is_big_int_v<T>);
|
||||
using UNSIGNED_T = typename details::IntegerWriterUnsigned<T>::type;
|
||||
static_assert(cpp::is_integral_v<T>);
|
||||
using UNSIGNED_T = cpp::make_unsigned_t<T>;
|
||||
|
||||
LIBC_INLINE static char digit_char(uint8_t digit) {
|
||||
if (digit < 10)
|
||||
|
@ -74,7 +74,6 @@ add_unittest_framework_library(
|
||||
libc.src.__support.CPP.type_traits
|
||||
libc.src.__support.fixed_point.fx_rep
|
||||
libc.src.__support.OSUtil.osutil
|
||||
libc.src.__support.uint
|
||||
libc.src.__support.uint128
|
||||
)
|
||||
|
||||
|
@ -38,8 +38,7 @@ TestLogger &operator<<(TestLogger &logger, Location Loc) {
|
||||
// When the value is UInt128, __uint128_t or wider, show its hexadecimal
|
||||
// digits.
|
||||
template <typename T>
|
||||
cpp::enable_if_t<(cpp::is_integral_v<T> && (sizeof(T) > sizeof(uint64_t))) ||
|
||||
cpp::is_big_int_v<T>,
|
||||
cpp::enable_if_t<cpp::is_integral_v<T> && (sizeof(T) > sizeof(uint64_t)),
|
||||
cpp::string>
|
||||
describeValue(T Value) {
|
||||
static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
|
||||
@ -48,10 +47,11 @@ describeValue(T Value) {
|
||||
}
|
||||
|
||||
// When the value is of a standard integral type, just display it as normal.
|
||||
template <typename T>
|
||||
cpp::enable_if_t<cpp::is_integral_v<T> && (sizeof(T) <= sizeof(uint64_t)),
|
||||
template <typename ValType>
|
||||
cpp::enable_if_t<cpp::is_integral_v<ValType> &&
|
||||
sizeof(ValType) <= sizeof(uint64_t),
|
||||
cpp::string>
|
||||
describeValue(T Value) {
|
||||
describeValue(ValType Value) {
|
||||
return cpp::to_string(Value);
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,6 @@ protected:
|
||||
// of type promotion.
|
||||
template <typename ValType,
|
||||
cpp::enable_if_t<cpp::is_integral_v<ValType> ||
|
||||
cpp::is_big_int_v<ValType> ||
|
||||
cpp::is_fixed_point_v<ValType>,
|
||||
int> = 0>
|
||||
bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "src/__support/CPP/string.h"
|
||||
#include "src/__support/CPP/string_view.h"
|
||||
#include "src/__support/OSUtil/io.h" // write_to_stderr
|
||||
#include "src/__support/UInt.h" // is_big_int
|
||||
#include "src/__support/UInt128.h"
|
||||
|
||||
#include <stdint.h>
|
||||
@ -48,9 +47,8 @@ template <> TestLogger &TestLogger::operator<<(void *addr) {
|
||||
}
|
||||
|
||||
template <typename T> TestLogger &TestLogger::operator<<(T t) {
|
||||
if constexpr (cpp::is_big_int_v<T> ||
|
||||
(cpp::is_integral_v<T> && cpp::is_unsigned_v<T> &&
|
||||
(sizeof(T) > sizeof(uint64_t)))) {
|
||||
if constexpr (cpp::is_integral_v<T> && cpp::is_unsigned_v<T> &&
|
||||
sizeof(T) > sizeof(uint64_t)) {
|
||||
static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
|
||||
const IntegerToString<T, radix::Hex::WithPrefix> buffer(t);
|
||||
return *this << buffer.view();
|
||||
@ -70,7 +68,7 @@ template TestLogger &TestLogger::operator<< <unsigned short>(unsigned short);
|
||||
template TestLogger &TestLogger::operator<< <unsigned int>(unsigned int);
|
||||
template TestLogger &TestLogger::operator<< <unsigned long>(unsigned long);
|
||||
template TestLogger &
|
||||
TestLogger::operator<< <unsigned long long>(unsigned long long);
|
||||
TestLogger::operator<< <unsigned long long>(unsigned long long);
|
||||
|
||||
#ifdef __SIZEOF_INT128__
|
||||
template TestLogger &TestLogger::operator<< <__uint128_t>(__uint128_t);
|
||||
|
@ -12,44 +12,21 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace LIBC_NAMESPACE::cpp {
|
||||
|
||||
using UnsignedTypesNoBigInt = testing::TypeList<
|
||||
using UnsignedTypes =
|
||||
testing::TypeList<unsigned char, unsigned short, unsigned int,
|
||||
unsigned long, unsigned long long,
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
__uint128_t,
|
||||
__uint128_t,
|
||||
#endif
|
||||
unsigned char, unsigned short, unsigned int, unsigned long,
|
||||
unsigned long long>;
|
||||
|
||||
using UnsignedTypes = testing::TypeList<
|
||||
#if defined(__SIZEOF_INT128__)
|
||||
__uint128_t,
|
||||
#endif
|
||||
unsigned char, unsigned short, unsigned int, unsigned long,
|
||||
unsigned long long, cpp::UInt<128>>;
|
||||
cpp::UInt<128>>;
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, HasSingleBit, UnsignedTypes) {
|
||||
constexpr auto ZERO = T(0);
|
||||
constexpr auto ALL_ONES = T(~ZERO);
|
||||
EXPECT_FALSE(has_single_bit<T>(ZERO));
|
||||
EXPECT_FALSE(has_single_bit<T>(ALL_ONES));
|
||||
|
||||
EXPECT_FALSE(has_single_bit<T>(T(0)));
|
||||
EXPECT_FALSE(has_single_bit<T>(~T(0)));
|
||||
for (T value = 1; value; value <<= 1)
|
||||
EXPECT_TRUE(has_single_bit<T>(value));
|
||||
|
||||
// We test that if two bits are set has_single_bit returns false.
|
||||
// We do this by setting the highest or lowest bit depending or where the
|
||||
// current bit is. This is a bit convoluted but it helps catch a bug on BigInt
|
||||
// where we have to work on an element-by-element basis.
|
||||
constexpr auto MIDPOINT = T(ALL_ONES / 2);
|
||||
constexpr auto LSB = T(1);
|
||||
constexpr auto MSB = T(~(ALL_ONES >> 1));
|
||||
for (T value = 1; value; value <<= 1) {
|
||||
auto two_bits_value = value | ((value <= MIDPOINT) ? MSB : LSB);
|
||||
EXPECT_FALSE(has_single_bit<T>(two_bits_value));
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, CountLZero, UnsignedTypes) {
|
||||
@ -229,39 +206,39 @@ TEST(LlvmLibcBitTest, Rotr) {
|
||||
rotr<uint64_t>(0x12345678deadbeefULL, -19));
|
||||
}
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstLeadingZero, UnsignedTypesNoBigInt) {
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstLeadingZero, UnsignedTypes) {
|
||||
EXPECT_EQ(first_leading_zero<T>(cpp::numeric_limits<T>::max()), 0);
|
||||
for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
|
||||
EXPECT_EQ(first_leading_zero<T>(~(T(1) << i)),
|
||||
cpp::numeric_limits<T>::digits - i);
|
||||
}
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstLeadingOne, UnsignedTypesNoBigInt) {
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstLeadingOne, UnsignedTypes) {
|
||||
EXPECT_EQ(first_leading_one<T>(static_cast<T>(0)), 0);
|
||||
for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
|
||||
EXPECT_EQ(first_leading_one<T>(T(1) << i),
|
||||
cpp::numeric_limits<T>::digits - i);
|
||||
}
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstTrailingZero, UnsignedTypesNoBigInt) {
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstTrailingZero, UnsignedTypes) {
|
||||
EXPECT_EQ(first_trailing_zero<T>(cpp::numeric_limits<T>::max()), 0);
|
||||
for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
|
||||
EXPECT_EQ(first_trailing_zero<T>(~(T(1) << i)), i + 1);
|
||||
}
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstTrailingOne, UnsignedTypesNoBigInt) {
|
||||
TYPED_TEST(LlvmLibcBitTest, FirstTrailingOne, UnsignedTypes) {
|
||||
EXPECT_EQ(first_trailing_one<T>(cpp::numeric_limits<T>::max()), 0);
|
||||
for (int i = 0U; i != cpp::numeric_limits<T>::digits; ++i)
|
||||
EXPECT_EQ(first_trailing_one<T>(T(1) << i), i + 1);
|
||||
}
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, CountZeros, UnsignedTypesNoBigInt) {
|
||||
TYPED_TEST(LlvmLibcBitTest, CountZeros, UnsignedTypes) {
|
||||
EXPECT_EQ(count_zeros(T(0)), cpp::numeric_limits<T>::digits);
|
||||
for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
|
||||
EXPECT_EQ(count_zeros<T>(cpp::numeric_limits<T>::max() >> i), i);
|
||||
}
|
||||
|
||||
TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypesNoBigInt) {
|
||||
TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypes) {
|
||||
EXPECT_EQ(count_ones(T(0)), 0);
|
||||
for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
|
||||
EXPECT_EQ(count_ones<T>(cpp::numeric_limits<T>::max() >> i),
|
||||
|
@ -507,7 +507,6 @@ libc_support_library(
|
||||
":__support_cpp_span",
|
||||
":__support_cpp_string_view",
|
||||
":__support_cpp_type_traits",
|
||||
":__support_uint",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -18,7 +18,6 @@ libc_support_library(
|
||||
"//libc:__support_cpp_string",
|
||||
"//libc:__support_cpp_string_view",
|
||||
"//libc:__support_osutil_io",
|
||||
"//libc:__support_uint",
|
||||
"//libc:__support_uint128",
|
||||
],
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user