diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt index 1a4b3e9a2145c..17c04aa57e6fd 100644 --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -95,6 +95,7 @@ add_header_library( HDRS integer_to_string.h DEPENDS + .uint libc.src.__support.common libc.src.__support.CPP.algorithm libc.src.__support.CPP.limits diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h index 7d11e7d5c497e..bc2f595845a95 100644 --- a/libc/src/__support/CPP/bit.h +++ b/libc/src/__support/CPP/bit.h @@ -27,13 +27,14 @@ namespace LIBC_NAMESPACE::cpp { // This implementation of bit_cast requires trivially-constructible To, to avoid // UB in the implementation. -template < - typename To, typename From, - typename = cpp::enable_if_t::value && - cpp::is_trivially_copyable::value && - cpp::is_trivially_copyable::value>> -LIBC_INLINE constexpr To bit_cast(const From &from) { +template +LIBC_INLINE constexpr cpp::enable_if_t< + (sizeof(To) == sizeof(From)) && + cpp::is_trivially_constructible::value && + cpp::is_trivially_copyable::value && + cpp::is_trivially_copyable::value, + To> +bit_cast(const From &from) { MSAN_UNPOISON(&from, sizeof(From)); #if LIBC_HAS_BUILTIN(__builtin_bit_cast) return __builtin_bit_cast(To, from); @@ -51,8 +52,10 @@ LIBC_INLINE constexpr To bit_cast(const From &from) { #endif // LIBC_HAS_BUILTIN(__builtin_bit_cast) } -template >> -[[nodiscard]] LIBC_INLINE constexpr bool has_single_bit(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, + bool> +has_single_bit(T value) { return (value != 0) && ((value & (value - 1)) == 0); } @@ -70,8 +73,9 @@ template >> /// Only unsigned integral types are allowed. /// /// Returns cpp::numeric_limits::digits on an input of 0. -template >> -[[nodiscard]] LIBC_INLINE constexpr int countr_zero(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countr_zero(T value) { if (!value) return cpp::numeric_limits::digits; if (value & 0x1) @@ -103,8 +107,9 @@ ADD_SPECIALIZATION(countr_zero, unsigned long long, __builtin_ctzll) /// Only unsigned integral types are allowed. /// /// Returns cpp::numeric_limits::digits on an input of 0. -template >> -[[nodiscard]] LIBC_INLINE constexpr int countl_zero(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countl_zero(T value) { if (!value) return cpp::numeric_limits::digits; // Bisection method. @@ -135,8 +140,9 @@ ADD_SPECIALIZATION(countl_zero, unsigned long long, __builtin_clzll) /// Only unsigned integral types are allowed. /// /// Returns cpp::numeric_limits::digits on an input of all ones. -template >> -[[nodiscard]] LIBC_INLINE constexpr int countl_one(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countl_one(T value) { return cpp::countl_zero(~value); } @@ -147,8 +153,9 @@ template >> /// Only unsigned integral types are allowed. /// /// Returns cpp::numeric_limits::digits on an input of all ones. -template >> -[[nodiscard]] LIBC_INLINE constexpr int countr_one(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countr_one(T value) { return cpp::countr_zero(~value); } @@ -156,8 +163,9 @@ template >> /// Returns 0 otherwise. /// /// Ex. bit_width(5) == 3. -template >> -[[nodiscard]] LIBC_INLINE constexpr int bit_width(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +bit_width(T value) { return cpp::numeric_limits::digits - cpp::countl_zero(value); } @@ -165,8 +173,9 @@ template >> /// nonzero. Returns 0 otherwise. /// /// Ex. bit_floor(5) == 4. -template >> -[[nodiscard]] LIBC_INLINE constexpr T bit_floor(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +bit_floor(T value) { if (!value) return 0; return T(1) << (cpp::bit_width(value) - 1); @@ -179,8 +188,9 @@ template >> /// /// The return value is undefined if the input is larger than the largest power /// of two representable in T. -template >> -[[nodiscard]] LIBC_INLINE constexpr T bit_ceil(T value) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +bit_ceil(T value) { if (value < 2) return 1; return T(1) << cpp::bit_width(value - 1u); @@ -190,28 +200,31 @@ template >> // from https://blog.regehr.org/archives/1063. // Forward-declare rotr so that rotl can use it. -template >> -[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate); +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +rotr(T value, int rotate); -template >> -[[nodiscard]] LIBC_INLINE constexpr T rotl(T value, int rotate) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +rotl(T value, int rotate) { constexpr unsigned N = cpp::numeric_limits::digits; rotate = rotate % N; if (!rotate) return value; if (rotate < 0) - return cpp::rotr(value, -rotate); + return cpp::rotr(value, -rotate); return (value << rotate) | (value >> (N - rotate)); } -template -[[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate) { +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +rotr(T value, int rotate) { constexpr unsigned N = cpp::numeric_limits::digits; rotate = rotate % N; if (!rotate) return value; if (rotate < 0) - return cpp::rotl(value, -rotate); + return cpp::rotl(value, -rotate); return (value >> rotate) | (value << (N - rotate)); } @@ -226,33 +239,44 @@ LIBC_INLINE constexpr To bit_or_static_cast(const From &from) { } } -template >> -[[nodiscard]] LIBC_INLINE constexpr int first_leading_zero(T value) { +// TODO: remove from 'bit.h' as it is not a standard function. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +first_leading_zero(T value) { return value == cpp::numeric_limits::max() ? 0 : countl_one(value) + 1; } -template >> -[[nodiscard]] LIBC_INLINE constexpr int first_leading_one(T value) { +// TODO: remove from 'bit.h' as it is not a standard function. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +first_leading_one(T value) { return first_leading_zero(static_cast(~value)); } -template >> -[[nodiscard]] LIBC_INLINE constexpr int first_trailing_zero(T value) { +// TODO: remove from 'bit.h' as it is not a standard function. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +first_trailing_zero(T value) { return value == cpp::numeric_limits::max() ? 0 : countr_zero(static_cast(~value)) + 1; } -template >> -[[nodiscard]] LIBC_INLINE constexpr int first_trailing_one(T value) { +// TODO: remove from 'bit.h' as it is not a standard function. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +first_trailing_one(T value) { return value == cpp::numeric_limits::max() ? 0 : countr_zero(value) + 1; } /// Count number of 1's aka population count or hamming weight. /// /// Only unsigned integral types are allowed. -template >> -[[nodiscard]] LIBC_INLINE constexpr int count_ones(T value) { +// TODO: rename as 'popcount' to follow the standard +// https://en.cppreference.com/w/cpp/numeric/popcount +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +count_ones(T value) { int count = 0; for (int i = 0; i != cpp::numeric_limits::digits; ++i) if ((value >> i) & 0x1) @@ -272,8 +296,10 @@ ADD_SPECIALIZATION(unsigned long long, __builtin_popcountll) // TODO: 128b specializations? #undef ADD_SPECIALIZATION -template >> -[[nodiscard]] LIBC_INLINE constexpr int count_zeros(T value) { +// TODO: remove from 'bit.h' as it is not a standard function. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +count_zeros(T value) { return count_ones(static_cast(~value)); } diff --git a/libc/src/__support/UInt.h b/libc/src/__support/UInt.h index 5973e6fab1d7d..b3d8f00b9a01a 100644 --- a/libc/src/__support/UInt.h +++ b/libc/src/__support/UInt.h @@ -43,6 +43,9 @@ struct BigInt { static_assert(is_integral_v && is_unsigned_v, "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; @@ -50,6 +53,10 @@ 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; + using signed_type = BigInt; + cpp::array val{}; LIBC_INLINE constexpr BigInt() = default; @@ -579,19 +586,33 @@ struct BigInt { return *this; } - 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]); + // TODO: remove and use cpp::countl_zero below. + [[nodiscard]] LIBC_INLINE constexpr int clz() const { + constexpr int word_digits = cpp::numeric_limits::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) 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::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. @@ -916,66 +937,123 @@ template <> class numeric_limits> { LIBC_INLINE_VAR static constexpr int digits = 128; }; -// Provides is_integral of U/Int<128>, U/Int<192>, U/Int<256>. -template -struct is_integral> : cpp::true_type {}; +// type traits to determine whether a T is a cpp::BigInt. +template struct is_big_int : cpp::false_type {}; -// Provides is_unsigned of UInt<128>, UInt<192>, UInt<256>. template -struct is_unsigned> : cpp::bool_constant {}; - -template -struct make_unsigned> - : type_identity> {}; - -template -struct make_signed> - : type_identity> {}; - -namespace internal { -template struct is_custom_uint : cpp::false_type {}; - -template -struct is_custom_uint> : 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::value == true> ... -// template::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 ::value && - cpp::is_trivially_copyable::value>, - typename = cpp::enable_if_t::value>> -LIBC_INLINE constexpr To bit_cast(const From &from) { +struct is_big_int> : cpp::true_type {}; + +template +LIBC_INLINE_VAR constexpr bool is_big_int_v = is_big_int::value; + +// Specialization of cpp::bit_cast ('bit.h') from T to BigInt. +template +LIBC_INLINE constexpr cpp::enable_if_t< + (sizeof(To) == sizeof(From)) && cpp::is_trivially_copyable::value && + cpp::is_trivially_copyable::value && is_big_int::value, + To> +bit_cast(const From &from) { To out; using Storage = decltype(out.val); out.val = cpp::bit_cast(from); return out; } -// bit_cast from UInt -template < - typename To, size_t Bits, - typename = cpp::enable_if_t) && - cpp::is_trivially_constructible::value && - cpp::is_trivially_copyable::value && - cpp::is_trivially_copyable>::value>> -LIBC_INLINE constexpr To bit_cast(const UInt &from) { +// Specialization of cpp::bit_cast ('bit.h') from BigInt to T. +template +LIBC_INLINE constexpr cpp::enable_if_t< + sizeof(To) == sizeof(UInt) && + cpp::is_trivially_constructible::value && + cpp::is_trivially_copyable::value && + cpp::is_trivially_copyable>::value, + To> +bit_cast(const UInt &from) { return cpp::bit_cast(from.val); } +// Specialization of cpp::has_single_bit ('bit.h') for BigInt. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_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 +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countr_zero(const T &value) { + return value.ctz(); +} + +// Specialization of cpp::countl_zero ('bit.h') for BigInt. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countl_zero(const T &value) { + return value.clz(); +} + +// Specialization of cpp::countl_one ('bit.h') for BigInt. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countl_one(T value) { + // TODO : Implement a faster version not involving operator~. + return cpp::countl_zero(~value); +} + +// Specialization of cpp::countr_one ('bit.h') for BigInt. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +countr_one(T value) { + // TODO : Implement a faster version not involving operator~. + return cpp::countr_zero(~value); +} + +// Specialization of cpp::bit_width ('bit.h') for BigInt. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, int> +bit_width(T value) { + return cpp::numeric_limits::digits - cpp::countl_zero(value); +} + +// Forward-declare rotr so that rotl can use it. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +rotr(T value, int rotate); + +// Specialization of cpp::rotl ('bit.h') for BigInt. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +rotl(T value, int rotate) { + constexpr unsigned N = cpp::numeric_limits::digits; + rotate = rotate % N; + if (!rotate) + return value; + if (rotate < 0) + return cpp::rotr(value, -rotate); + return (value << rotate) | (value >> (N - rotate)); +} + +// Specialization of cpp::rotr ('bit.h') for BigInt. +template +[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t, T> +rotr(T value, int rotate) { + constexpr unsigned N = cpp::numeric_limits::digits; + rotate = rotate % N; + if (!rotate) + return value; + if (rotate < 0) + return cpp::rotl(value, -rotate); + return (value >> rotate) | (value << (N - rotate)); +} + } // namespace LIBC_NAMESPACE::cpp #endif // LLVM_LIBC_SRC___SUPPORT_UINT_H diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h index 744842ced8d77..27476433a9457 100644 --- a/libc/src/__support/float_to_string.h +++ b/libc/src/__support/float_to_string.h @@ -713,7 +713,7 @@ template <> class FloatToString { float_as_fixed.shift_left(SHIFT_AMOUNT); // If there are still digits above the decimal point, handle those. - if (float_as_fixed.clz() < EXTRA_INT_WIDTH) { + if (float_as_fixed.clz() < static_cast(EXTRA_INT_WIDTH)) { cpp::UInt above_decimal_point = float_as_fixed >> FLOAT_AS_INT_WIDTH; diff --git a/libc/src/__support/integer_to_string.h b/libc/src/__support/integer_to_string.h index 81ed21ccfca16..a5872dce65203 100644 --- a/libc/src/__support/integer_to_string.h +++ b/libc/src/__support/integer_to_string.h @@ -67,6 +67,7 @@ #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 { @@ -149,6 +150,18 @@ template class StringBufferWriterImpl { using StringBufferWriter = StringBufferWriterImpl; using BackwardStringBufferWriter = StringBufferWriterImpl; +template struct IntegerWriterUnsigned {}; + +template +struct IntegerWriterUnsigned>> { + using type = cpp::make_unsigned_t; +}; + +template +struct IntegerWriterUnsigned>> { + using type = typename T::unsigned_type; +}; + } // namespace details namespace radix { @@ -163,7 +176,7 @@ template using Custom = details::Fmt; // See file header for documentation. template class IntegerToString { - static_assert(cpp::is_integral_v); + static_assert(cpp::is_integral_v || cpp::is_big_int_v); LIBC_INLINE static constexpr size_t compute_buffer_size() { constexpr auto MAX_DIGITS = []() -> size_t { @@ -208,8 +221,8 @@ template class IntegerToString { // An internal stateless structure that handles the number formatting logic. struct IntegerWriter { - static_assert(cpp::is_integral_v); - using UNSIGNED_T = cpp::make_unsigned_t; + static_assert(cpp::is_integral_v || cpp::is_big_int_v); + using UNSIGNED_T = typename details::IntegerWriterUnsigned::type; LIBC_INLINE static char digit_char(uint8_t digit) { if (digit < 10) diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt index 4668f0061975f..36837c553efce 100644 --- a/libc/test/UnitTest/CMakeLists.txt +++ b/libc/test/UnitTest/CMakeLists.txt @@ -74,6 +74,7 @@ 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 ) diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp index 7b0e4fca83683..0340f7ed37100 100644 --- a/libc/test/UnitTest/LibcTest.cpp +++ b/libc/test/UnitTest/LibcTest.cpp @@ -38,7 +38,8 @@ TestLogger &operator<<(TestLogger &logger, Location Loc) { // When the value is UInt128, __uint128_t or wider, show its hexadecimal // digits. template -cpp::enable_if_t && (sizeof(T) > sizeof(uint64_t)), +cpp::enable_if_t<(cpp::is_integral_v && (sizeof(T) > sizeof(uint64_t))) || + cpp::is_big_int_v, cpp::string> describeValue(T Value) { static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt"); @@ -47,11 +48,10 @@ describeValue(T Value) { } // When the value is of a standard integral type, just display it as normal. -template -cpp::enable_if_t && - sizeof(ValType) <= sizeof(uint64_t), +template +cpp::enable_if_t && (sizeof(T) <= sizeof(uint64_t)), cpp::string> -describeValue(ValType Value) { +describeValue(T Value) { return cpp::to_string(Value); } diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h index 639f600583257..d26d6490bcb57 100644 --- a/libc/test/UnitTest/LibcTest.h +++ b/libc/test/UnitTest/LibcTest.h @@ -127,6 +127,7 @@ class Test { // of type promotion. template || + cpp::is_big_int_v || cpp::is_fixed_point_v, int> = 0> bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr, diff --git a/libc/test/UnitTest/TestLogger.cpp b/libc/test/UnitTest/TestLogger.cpp index 6bb0e17dc3888..469b3a11d57d9 100644 --- a/libc/test/UnitTest/TestLogger.cpp +++ b/libc/test/UnitTest/TestLogger.cpp @@ -2,6 +2,7 @@ #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 @@ -47,8 +48,9 @@ template <> TestLogger &TestLogger::operator<<(void *addr) { } template TestLogger &TestLogger::operator<<(T t) { - if constexpr (cpp::is_integral_v && cpp::is_unsigned_v && - sizeof(T) > sizeof(uint64_t)) { + if constexpr (cpp::is_big_int_v || + (cpp::is_integral_v && cpp::is_unsigned_v && + (sizeof(T) > sizeof(uint64_t)))) { static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt"); const IntegerToString buffer(t); return *this << buffer.view(); @@ -68,7 +70,7 @@ template TestLogger &TestLogger::operator<< (unsigned short); template TestLogger &TestLogger::operator<< (unsigned int); template TestLogger &TestLogger::operator<< (unsigned long); template TestLogger & -TestLogger::operator<< (unsigned long long); + TestLogger::operator<< (unsigned long long); #ifdef __SIZEOF_INT128__ template TestLogger &TestLogger::operator<< <__uint128_t>(__uint128_t); diff --git a/libc/test/src/__support/CPP/bit_test.cpp b/libc/test/src/__support/CPP/bit_test.cpp index 115a5d505c4b7..25a80ca9209c2 100644 --- a/libc/test/src/__support/CPP/bit_test.cpp +++ b/libc/test/src/__support/CPP/bit_test.cpp @@ -14,19 +14,40 @@ namespace LIBC_NAMESPACE::cpp { -using UnsignedTypes = - testing::TypeList>; + 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>>; TYPED_TEST(LlvmLibcBitTest, HasSingleBit, UnsignedTypes) { - EXPECT_FALSE(has_single_bit(T(0))); - EXPECT_FALSE(has_single_bit(~T(0))); + constexpr auto ZERO = T(0); + constexpr auto ALL_ONES = T(~ZERO); + EXPECT_FALSE(has_single_bit(ZERO)); + EXPECT_FALSE(has_single_bit(ALL_ONES)); + for (T value = 1; value; value <<= 1) EXPECT_TRUE(has_single_bit(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(two_bits_value)); + } } TYPED_TEST(LlvmLibcBitTest, CountLZero, UnsignedTypes) { @@ -206,39 +227,39 @@ TEST(LlvmLibcBitTest, Rotr) { rotr(0x12345678deadbeefULL, -19)); } -TYPED_TEST(LlvmLibcBitTest, FirstLeadingZero, UnsignedTypes) { +TYPED_TEST(LlvmLibcBitTest, FirstLeadingZero, UnsignedTypesNoBigInt) { EXPECT_EQ(first_leading_zero(cpp::numeric_limits::max()), 0); for (int i = 0U; i != cpp::numeric_limits::digits; ++i) EXPECT_EQ(first_leading_zero(~(T(1) << i)), cpp::numeric_limits::digits - i); } -TYPED_TEST(LlvmLibcBitTest, FirstLeadingOne, UnsignedTypes) { +TYPED_TEST(LlvmLibcBitTest, FirstLeadingOne, UnsignedTypesNoBigInt) { EXPECT_EQ(first_leading_one(static_cast(0)), 0); for (int i = 0U; i != cpp::numeric_limits::digits; ++i) EXPECT_EQ(first_leading_one(T(1) << i), cpp::numeric_limits::digits - i); } -TYPED_TEST(LlvmLibcBitTest, FirstTrailingZero, UnsignedTypes) { +TYPED_TEST(LlvmLibcBitTest, FirstTrailingZero, UnsignedTypesNoBigInt) { EXPECT_EQ(first_trailing_zero(cpp::numeric_limits::max()), 0); for (int i = 0U; i != cpp::numeric_limits::digits; ++i) EXPECT_EQ(first_trailing_zero(~(T(1) << i)), i + 1); } -TYPED_TEST(LlvmLibcBitTest, FirstTrailingOne, UnsignedTypes) { +TYPED_TEST(LlvmLibcBitTest, FirstTrailingOne, UnsignedTypesNoBigInt) { EXPECT_EQ(first_trailing_one(cpp::numeric_limits::max()), 0); for (int i = 0U; i != cpp::numeric_limits::digits; ++i) EXPECT_EQ(first_trailing_one(T(1) << i), i + 1); } -TYPED_TEST(LlvmLibcBitTest, CountZeros, UnsignedTypes) { +TYPED_TEST(LlvmLibcBitTest, CountZeros, UnsignedTypesNoBigInt) { EXPECT_EQ(count_zeros(T(0)), cpp::numeric_limits::digits); for (int i = 0; i != cpp::numeric_limits::digits; ++i) EXPECT_EQ(count_zeros(cpp::numeric_limits::max() >> i), i); } -TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypes) { +TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypesNoBigInt) { EXPECT_EQ(count_ones(T(0)), 0); for (int i = 0; i != cpp::numeric_limits::digits; ++i) EXPECT_EQ(count_ones(cpp::numeric_limits::max() >> i), diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp index f5c27d4fc0302..760031569c81f 100644 --- a/libc/test/src/__support/FPUtil/fpbits_test.cpp +++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp @@ -237,6 +237,8 @@ template constexpr auto make(Sign sign, FP fp) { return T::signaling_nan(sign); case FP::QUIET_NAN: return T::quiet_nan(sign); + default: + __builtin_unreachable(); } } diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 49a454379e1c7..5c6cf761ebe7d 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -507,6 +507,7 @@ libc_support_library( ":__support_cpp_span", ":__support_cpp_string_view", ":__support_cpp_type_traits", + ":__support_uint", ], ) diff --git a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel index a5c18fbb68b39..44692947af7c0 100644 --- a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel @@ -18,6 +18,7 @@ libc_support_library( "//libc:__support_cpp_string", "//libc:__support_cpp_string_view", "//libc:__support_osutil_io", + "//libc:__support_uint", "//libc:__support_uint128", ], )