https://github.com/gchatelet updated https://github.com/llvm/llvm-project/pull/73814
>From bdd0a0855c0dd98c93977db6982e480ab270b3cd Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Wed, 29 Nov 2023 16:29:12 +0000 Subject: [PATCH 1/8] [libc] Add more functions in CPP/bit.h --- libc/src/__support/CPP/CMakeLists.txt | 1 + libc/src/__support/CPP/bit.h | 242 ++++++++++++++++-- libc/src/__support/CPP/limits.h | 18 +- libc/test/src/__support/CPP/CMakeLists.txt | 11 + libc/test/src/__support/CPP/bit_test.cpp | 216 ++++++++++++++++ .../llvm-project-overlay/libc/BUILD.bazel | 1 + .../libc/test/src/__support/CPP/BUILD.bazel | 9 + 7 files changed, 478 insertions(+), 20 deletions(-) create mode 100644 libc/test/src/__support/CPP/bit_test.cpp diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt index 10bcebf9b04f61d..5c47b2a815356b9 100644 --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -15,6 +15,7 @@ add_header_library( HDRS bit.h DEPENDS + .limits .type_traits libc.src.__support.macros.attributes libc.src.__support.macros.config diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h index f6f3131c1ccfd81..f744d804398e213 100644 --- a/libc/src/__support/CPP/bit.h +++ b/libc/src/__support/CPP/bit.h @@ -1,57 +1,261 @@ -//===-- Freestanding version of bit_cast -----------------------*- C++ -*-===// +//===-- Implementation of the C++20 bit header -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// This is inspired from LLVM ADT/bit.h header. #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H #define LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H +#include "src/__support/CPP/limits.h" // numeric_limits #include "src/__support/CPP/type_traits.h" #include "src/__support/macros/attributes.h" #include "src/__support/macros/config.h" // LIBC_HAS_BUILTIN #include "src/__support/macros/sanitizer.h" -namespace LIBC_NAMESPACE::cpp { +#include <stdint.h> -#if LIBC_HAS_BUILTIN(__builtin_bit_cast) -#define LLVM_LIBC_HAS_BUILTIN_BIT_CAST -#endif +namespace LIBC_NAMESPACE::cpp { #if LIBC_HAS_BUILTIN(__builtin_memcpy_inline) #define LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE #endif -// This function guarantees the bitcast to be optimized away by the compiler for -// GCC >= 8 and Clang >= 6. -template <class To, class From> +// 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<sizeof(To) == sizeof(From)>, + typename = cpp::enable_if_t<cpp::is_trivially_constructible<To>::value>, + typename = cpp::enable_if_t<cpp::is_trivially_copyable<To>::value>, + typename = cpp::enable_if_t<cpp::is_trivially_copyable<From>::value>> LIBC_INLINE constexpr To bit_cast(const From &from) { - static_assert(sizeof(To) == sizeof(From), "To and From must be of same size"); - static_assert(cpp::is_trivially_copyable<To>::value && - cpp::is_trivially_copyable<From>::value, - "Cannot bit-cast instances of non-trivially copyable classes."); MSAN_UNPOISON(&from, sizeof(From)); -#if defined(LLVM_LIBC_HAS_BUILTIN_BIT_CAST) +#if LIBC_HAS_BUILTIN(__builtin_bit_cast) return __builtin_bit_cast(To, from); #else - static_assert(cpp::is_trivially_constructible<To>::value, - "This implementation additionally requires destination type to " - "be trivially constructible"); To to; char *dst = reinterpret_cast<char *>(&to); const char *src = reinterpret_cast<const char *>(&from); -#if defined(LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE) +#if LIBC_HAS_BUILTIN(__builtin_memcpy_inline) __builtin_memcpy_inline(dst, src, sizeof(To)); #else for (unsigned i = 0; i < sizeof(To); ++i) dst[i] = src[i]; -#endif // defined(LLVM_LIBC_HAS_BUILTIN_MEMCPY_INLINE) +#endif // LIBC_HAS_BUILTIN(__builtin_memcpy_inline) return to; -#endif // defined(LLVM_LIBC_HAS_BUILTIN_BIT_CAST) +#endif // LIBC_HAS_BUILTIN(__builtin_bit_cast) +} + +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); +} + +// A temporary macro to add template function specialization when compiler +// builtin is available. +#define ADD_SPECIALIZATION(NAME, TYPE, BUILTIN) \ + template <> [[nodiscard]] LIBC_INLINE constexpr int NAME<TYPE>(TYPE value) { \ + static_assert(cpp::is_unsigned_v<TYPE>); \ + return value == 0 ? cpp::numeric_limits<TYPE>::digits : BUILTIN(value); \ + } + +/// Count number of 0's from the least significant bit to the most +/// stopping at the first 1. +/// +/// Only unsigned integral types are allowed. +/// +/// Returns cpp::numeric_limits<T>::digits on an input of 0. +template <typename T> +[[nodiscard]] LIBC_INLINE constexpr int countr_zero(T value) { + static_assert(cpp::is_unsigned_v<T>); + if (!value) + return cpp::numeric_limits<T>::digits; + if (value & 0x1) + return 0; + // Bisection method. + unsigned zero_bits = 0; + T shift = cpp::numeric_limits<T>::digits >> 1; + T mask = cpp::numeric_limits<T>::max() >> shift; + while (shift) { + if ((value & mask) == 0) { + value >>= shift; + zero_bits |= shift; + } + shift >>= 1; + mask >>= shift; + } + return zero_bits; +} +#if LIBC_HAS_BUILTIN(__builtin_ctzs) +ADD_SPECIALIZATION(countr_zero, unsigned short, __builtin_ctzs) +#endif +#if LIBC_HAS_BUILTIN(__builtin_ctz) +ADD_SPECIALIZATION(countr_zero, unsigned int, __builtin_ctz) +#endif +#if LIBC_HAS_BUILTIN(__builtin_ctzl) +ADD_SPECIALIZATION(countr_zero, unsigned long, __builtin_ctzl) +#endif +#if LIBC_HAS_BUILTIN(__builtin_ctzll) +ADD_SPECIALIZATION(countr_zero, unsigned long long, __builtin_ctzll) +#endif + +/// Count number of 0's from the most significant bit to the least +/// stopping at the first 1. +/// +/// Only unsigned integral types are allowed. +/// +/// Returns cpp::numeric_limits<T>::digits on an input of 0. +template <typename T> +[[nodiscard]] LIBC_INLINE constexpr int countl_zero(T value) { + static_assert(cpp::is_unsigned_v<T>); + if (!value) + return cpp::numeric_limits<T>::digits; + // Bisection method. + unsigned zero_bits = 0; + for (T shift = cpp::numeric_limits<T>::digits >> 1; shift; shift >>= 1) { + T tmp = value >> shift; + if (tmp) + value = tmp; + else + zero_bits |= shift; + } + return zero_bits; +} +#if LIBC_HAS_BUILTIN(__builtin_clzs) +ADD_SPECIALIZATION(countl_zero, unsigned short, __builtin_clzs) +#endif +#if LIBC_HAS_BUILTIN(__builtin_clz) +ADD_SPECIALIZATION(countl_zero, unsigned int, __builtin_clz) +#endif +#if LIBC_HAS_BUILTIN(__builtin_clzl) +ADD_SPECIALIZATION(countl_zero, unsigned long, __builtin_clzl) +#endif +#if LIBC_HAS_BUILTIN(__builtin_clzll) +ADD_SPECIALIZATION(countl_zero, unsigned long long, __builtin_clzll) +#endif + +#undef ADD_SPECIALIZATION + +/// Count the number of ones from the most significant bit to the first +/// zero bit. +/// +/// Ex. countl_one(0xFF0FFF00) == 8. +/// 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 int countl_one(T value) { + static_assert(cpp::is_unsigned_v<T>); + return cpp::countl_zero<T>(~value); +} + +/// Count the number of ones from the least significant bit to the first +/// zero bit. +/// +/// Ex. countr_one(0x00FF00FF) == 8. +/// 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 int countr_one(T value) { + static_assert(cpp::is_unsigned_v<T>); + return cpp::countr_zero<T>(~value); +} + +/// Returns the number of bits needed to represent value if value is nonzero. +/// Returns 0 otherwise. +/// +/// Ex. bit_width(5) == 3. +template <typename T> +[[nodiscard]] LIBC_INLINE constexpr int bit_width(T value) { + static_assert(cpp::is_unsigned_v<T>); + return cpp::numeric_limits<T>::digits - cpp::countl_zero(value); +} + +/// Returns the largest integral power of two no greater than value if value is +/// nonzero. Returns 0 otherwise. +/// +/// Ex. bit_floor(5) == 4. +template <typename T> [[nodiscard]] LIBC_INLINE constexpr T bit_floor(T value) { + static_assert(cpp::is_unsigned_v<T>); + if (!value) + return 0; + return T(1) << (cpp::bit_width(value) - 1); +} + +/// Returns the smallest integral power of two no smaller than value if value is +/// nonzero. Returns 1 otherwise. +/// +/// Ex. bit_ceil(5) == 8. +/// +/// 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 T bit_ceil(T value) { + static_assert(cpp::is_unsigned_v<T>); + if (value < 2) + return 1; + return T(1) << cpp::bit_width<T>(value - 1u); +} + +/// Count the number of set bits in a value. +/// Ex. popcount(0xF000F000) = 8 +/// Returns 0 if the word is zero. +template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> +[[nodiscard]] LIBC_INLINE constexpr int popcount(T value) { + if constexpr (sizeof(T) == 8) { +#if LIBC_HAS_BUILTIN(__builtin_popcountll) + return (int)__builtin_popcountll(value); +#endif + uint64_t v = value; + v = v - ((v >> 1) & 0x5555555555555555ULL); + v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL); + v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + return int((uint64_t)(v * 0x0101010101010101ULL) >> 56); + } else if constexpr (sizeof(T) <= 4) { +#if LIBC_HAS_BUILTIN(__builtin_popcount) + return (int)__builtin_popcount(value); +#endif + uint32_t v = value; + v = v - ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + return int(((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24); + } else { + static_assert(cpp::always_false<T>); + } +} + +// Forward-declare rotr so that rotl can use it. +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, 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(value, -rotate); + return (value << rotate) | (value >> (N - 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(value, -rotate); + return (value >> rotate) | (value << (N - rotate)); } +// TODO: Do we need this function at all? How is it different from +// 'static_cast'? template <class To, class From> LIBC_INLINE constexpr To bit_or_static_cast(const From &from) { if constexpr (sizeof(To) == sizeof(From)) { diff --git a/libc/src/__support/CPP/limits.h b/libc/src/__support/CPP/limits.h index b1a1203df79674a..b56f2557a737cab 100644 --- a/libc/src/__support/CPP/limits.h +++ b/libc/src/__support/CPP/limits.h @@ -11,7 +11,7 @@ #include "src/__support/macros/attributes.h" // LIBC_INLINE -#include <limits.h> +#include <limits.h> // CHAR_BIT namespace LIBC_NAMESPACE { namespace cpp { @@ -36,41 +36,53 @@ template <> class numeric_limits<int> { public: LIBC_INLINE static constexpr int max() { return INT_MAX; } LIBC_INLINE static constexpr int min() { return INT_MIN; } + LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(int) - 1; }; template <> class numeric_limits<unsigned int> { public: LIBC_INLINE static constexpr unsigned int max() { return UINT_MAX; } LIBC_INLINE static constexpr unsigned int min() { return 0; } + LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(unsigned int); }; template <> class numeric_limits<long> { public: LIBC_INLINE static constexpr long max() { return LONG_MAX; } LIBC_INLINE static constexpr long min() { return LONG_MIN; } + LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(long) - 1; }; template <> class numeric_limits<unsigned long> { public: LIBC_INLINE static constexpr unsigned long max() { return ULONG_MAX; } LIBC_INLINE static constexpr unsigned long min() { return 0; } + LIBC_INLINE_VAR static constexpr int digits = + CHAR_BIT * sizeof(unsigned long); }; template <> class numeric_limits<long long> { public: LIBC_INLINE static constexpr long long max() { return LLONG_MAX; } LIBC_INLINE static constexpr long long min() { return LLONG_MIN; } + LIBC_INLINE_VAR static constexpr int digits = + CHAR_BIT * sizeof(long long) - 1; }; template <> class numeric_limits<unsigned long long> { public: LIBC_INLINE static constexpr unsigned long long max() { return ULLONG_MAX; } LIBC_INLINE static constexpr unsigned long long min() { return 0; } + LIBC_INLINE_VAR static constexpr int digits = + CHAR_BIT * sizeof(unsigned long long); }; template <> class numeric_limits<short> { public: LIBC_INLINE static constexpr short max() { return SHRT_MAX; } LIBC_INLINE static constexpr short min() { return SHRT_MIN; } + LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(short) - 1; }; template <> class numeric_limits<unsigned short> { public: LIBC_INLINE static constexpr unsigned short max() { return USHRT_MAX; } LIBC_INLINE static constexpr unsigned short min() { return 0; } + LIBC_INLINE_VAR static constexpr int digits = + CHAR_BIT * sizeof(unsigned short); }; template <> class numeric_limits<char> { public: @@ -81,11 +93,13 @@ template <> class numeric_limits<signed char> { public: LIBC_INLINE static constexpr signed char max() { return SCHAR_MAX; } LIBC_INLINE static constexpr signed char min() { return SCHAR_MIN; } + LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT - 1; }; template <> class numeric_limits<unsigned char> { public: LIBC_INLINE static constexpr unsigned char max() { return UCHAR_MAX; } LIBC_INLINE static constexpr unsigned char min() { return 0; } + LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT; }; #ifdef __SIZEOF_INT128__ // On platform where UInt128 resolves to __uint128_t, this specialization @@ -94,6 +108,8 @@ template <> class numeric_limits<__uint128_t> { public: LIBC_INLINE static constexpr __uint128_t max() { return ~__uint128_t(0); } LIBC_INLINE static constexpr __uint128_t min() { return 0; } + LIBC_INLINE_VAR static constexpr int digits = + CHAR_BIT * sizeof(__uint128_t) - 1; }; #endif diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt index 5772a83a65cad13..6927579289bc2f3 100644 --- a/libc/test/src/__support/CPP/CMakeLists.txt +++ b/libc/test/src/__support/CPP/CMakeLists.txt @@ -1,5 +1,16 @@ add_custom_target(libc-cpp-utils-tests) +add_libc_test( + bit_test + SUITE + libc-cpp-utils-tests + SRCS + bit_test.cpp + DEPENDS + libc.src.__support.CPP.bit + libc.src.__support.uint +) + add_libc_test( bitset_test SUITE diff --git a/libc/test/src/__support/CPP/bit_test.cpp b/libc/test/src/__support/CPP/bit_test.cpp new file mode 100644 index 000000000000000..caec621c5a6f64a --- /dev/null +++ b/libc/test/src/__support/CPP/bit_test.cpp @@ -0,0 +1,216 @@ +//===-- Unittests for Bit -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/bit.h" +#include "src/__support/UInt.h" +#include "test/UnitTest/Test.h" + +#include <stdint.h> + +namespace LIBC_NAMESPACE::cpp { + +using UnsignedTypes = + testing::TypeList<unsigned char, unsigned short, unsigned int, + unsigned long, unsigned long long>; + +TYPED_TEST(LlvmLibcBitTest, HasSingleBit, UnsignedTypes) { + 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)); +} + +TYPED_TEST(LlvmLibcBitTest, CountLZero, UnsignedTypes) { + EXPECT_EQ(countl_zero<T>(T(0)), cpp::numeric_limits<T>::digits); + int expected = 0; + for (T value = ~T(0); value; value >>= 1, ++expected) + EXPECT_EQ(countl_zero<T>(value), expected); +} + +TYPED_TEST(LlvmLibcBitTest, CountRZero, UnsignedTypes) { + EXPECT_EQ(countr_zero<T>(T(0)), cpp::numeric_limits<T>::digits); + int expected = 0; + for (T value = ~T(0); value; value <<= 1, ++expected) + EXPECT_EQ(countr_zero<T>(value), expected); +} + +TYPED_TEST(LlvmLibcBitTest, CountLOne, UnsignedTypes) { + EXPECT_EQ(countl_one<T>(T(0)), 0); + int expected = cpp::numeric_limits<T>::digits; + for (T value = ~T(0); value; value <<= 1, --expected) + EXPECT_EQ(countl_one<T>(value), expected); +} + +TYPED_TEST(LlvmLibcBitTest, CountROne, UnsignedTypes) { + EXPECT_EQ(countr_one<T>(T(0)), 0); + int expected = cpp::numeric_limits<T>::digits; + for (T value = ~T(0); value; value >>= 1, --expected) + EXPECT_EQ(countr_one<T>(value), expected); +} + +TYPED_TEST(LlvmLibcBitTest, BitWidth, UnsignedTypes) { + EXPECT_EQ(bit_width<T>(T(0)), 0); + int one_index = 0; + for (T value = 1; value; value <<= 1, ++one_index) + EXPECT_EQ(bit_width<T>(value), one_index + 1); +} + +TEST(LlvmLibcBitTest, BitCeil) { + EXPECT_EQ(uint8_t(1), bit_ceil(uint8_t(0))); + EXPECT_EQ(uint16_t(1), bit_ceil(uint16_t(0))); + EXPECT_EQ(uint32_t(1), bit_ceil(uint32_t(0))); + EXPECT_EQ(uint64_t(1), bit_ceil(uint64_t(0))); + + EXPECT_EQ(uint8_t(1), bit_ceil(uint8_t(1))); + EXPECT_EQ(uint16_t(1), bit_ceil(uint16_t(1))); + EXPECT_EQ(uint32_t(1), bit_ceil(uint32_t(1))); + EXPECT_EQ(uint64_t(1), bit_ceil(uint64_t(1))); + + EXPECT_EQ(uint8_t(2), bit_ceil(uint8_t(2))); + EXPECT_EQ(uint16_t(2), bit_ceil(uint16_t(2))); + EXPECT_EQ(uint32_t(2), bit_ceil(uint32_t(2))); + EXPECT_EQ(uint64_t(2), bit_ceil(uint64_t(2))); + + EXPECT_EQ(uint8_t(4), bit_ceil(uint8_t(3))); + EXPECT_EQ(uint16_t(4), bit_ceil(uint16_t(3))); + EXPECT_EQ(uint32_t(4), bit_ceil(uint32_t(3))); + EXPECT_EQ(uint64_t(4), bit_ceil(uint64_t(3))); + + EXPECT_EQ(uint8_t(4), bit_ceil(uint8_t(4))); + EXPECT_EQ(uint16_t(4), bit_ceil(uint16_t(4))); + EXPECT_EQ(uint32_t(4), bit_ceil(uint32_t(4))); + EXPECT_EQ(uint64_t(4), bit_ceil(uint64_t(4))); + + // The result is the largest representable value for each type. + EXPECT_EQ(uint8_t(0x80), bit_ceil(uint8_t(0x7f))); + EXPECT_EQ(uint16_t(0x8000), bit_ceil(uint16_t(0x7fff))); + EXPECT_EQ(uint32_t(0x80000000), bit_ceil(uint32_t(0x7fffffff))); + EXPECT_EQ(uint64_t(0x8000000000000000), + bit_ceil(uint64_t(0x7fffffffffffffff))); +} + +TEST(LlvmLibcBitTest, BitFloor) { + EXPECT_EQ(uint8_t(0), bit_floor(uint8_t(0))); + EXPECT_EQ(uint16_t(0), bit_floor(uint16_t(0))); + EXPECT_EQ(uint32_t(0), bit_floor(uint32_t(0))); + EXPECT_EQ(uint64_t(0), bit_floor(uint64_t(0))); + + EXPECT_EQ(uint8_t(1), bit_floor(uint8_t(1))); + EXPECT_EQ(uint16_t(1), bit_floor(uint16_t(1))); + EXPECT_EQ(uint32_t(1), bit_floor(uint32_t(1))); + EXPECT_EQ(uint64_t(1), bit_floor(uint64_t(1))); + + EXPECT_EQ(uint8_t(2), bit_floor(uint8_t(2))); + EXPECT_EQ(uint16_t(2), bit_floor(uint16_t(2))); + EXPECT_EQ(uint32_t(2), bit_floor(uint32_t(2))); + EXPECT_EQ(uint64_t(2), bit_floor(uint64_t(2))); + + EXPECT_EQ(uint8_t(2), bit_floor(uint8_t(3))); + EXPECT_EQ(uint16_t(2), bit_floor(uint16_t(3))); + EXPECT_EQ(uint32_t(2), bit_floor(uint32_t(3))); + EXPECT_EQ(uint64_t(2), bit_floor(uint64_t(3))); + + EXPECT_EQ(uint8_t(4), bit_floor(uint8_t(4))); + EXPECT_EQ(uint16_t(4), bit_floor(uint16_t(4))); + EXPECT_EQ(uint32_t(4), bit_floor(uint32_t(4))); + EXPECT_EQ(uint64_t(4), bit_floor(uint64_t(4))); + + EXPECT_EQ(uint8_t(0x40), bit_floor(uint8_t(0x7f))); + EXPECT_EQ(uint16_t(0x4000), bit_floor(uint16_t(0x7fff))); + EXPECT_EQ(uint32_t(0x40000000), bit_floor(uint32_t(0x7fffffff))); + EXPECT_EQ(uint64_t(0x4000000000000000), + bit_floor(uint64_t(0x7fffffffffffffffull))); + + EXPECT_EQ(uint8_t(0x80), bit_floor(uint8_t(0x80))); + EXPECT_EQ(uint16_t(0x8000), bit_floor(uint16_t(0x8000))); + EXPECT_EQ(uint32_t(0x80000000), bit_floor(uint32_t(0x80000000))); + EXPECT_EQ(uint64_t(0x8000000000000000), + bit_floor(uint64_t(0x8000000000000000))); + + EXPECT_EQ(uint8_t(0x80), bit_floor(uint8_t(0xff))); + EXPECT_EQ(uint16_t(0x8000), bit_floor(uint16_t(0xffff))); + EXPECT_EQ(uint32_t(0x80000000), bit_floor(uint32_t(0xffffffff))); + EXPECT_EQ(uint64_t(0x8000000000000000), + bit_floor(uint64_t(0xffffffffffffffff))); +} + +TEST(LlvmLibcBitTest, PopCount) { + EXPECT_EQ(0, popcount(0U)); + EXPECT_EQ(0, popcount(0ULL)); + + EXPECT_EQ(32, popcount(~0U)); + EXPECT_EQ(64, popcount(~0ULL)); + + for (int i = 0; i != 32; ++i) + EXPECT_EQ(1, popcount(1U << i)); +} + +TYPED_TEST(LlvmLibcBitTest, RotateIsInvariantForZeroAndOne, UnsignedTypes) { + constexpr T all_zeros = T(0); + constexpr T all_ones = ~T(0); + for (int i = 0; i < cpp::numeric_limits<T>::digits; ++i) { + EXPECT_EQ(rotl<T>(all_zeros, i), all_zeros); + EXPECT_EQ(rotl<T>(all_ones, i), all_ones); + EXPECT_EQ(rotr<T>(all_zeros, i), all_zeros); + EXPECT_EQ(rotr<T>(all_ones, i), all_ones); + } +} + +TEST(LlvmLibcBitTest, Rotl) { + EXPECT_EQ(uint8_t(0x53U), rotl<uint8_t>(0x53, 0)); + EXPECT_EQ(uint8_t(0x4dU), rotl<uint8_t>(0x53, 2)); + EXPECT_EQ(uint8_t(0xa6U), rotl<uint8_t>(0x53, 9)); + EXPECT_EQ(uint8_t(0x9aU), rotl<uint8_t>(0x53, -5)); + + EXPECT_EQ(uint16_t(0xabcdU), rotl<uint16_t>(0xabcd, 0)); + EXPECT_EQ(uint16_t(0xf36aU), rotl<uint16_t>(0xabcd, 6)); + EXPECT_EQ(uint16_t(0xaf36U), rotl<uint16_t>(0xabcd, 18)); + EXPECT_EQ(uint16_t(0xf36aU), rotl<uint16_t>(0xabcd, -10)); + + EXPECT_EQ(uint32_t(0xdeadbeefU), rotl<uint32_t>(0xdeadbeef, 0)); + EXPECT_EQ(uint32_t(0x7ddfbd5bU), rotl<uint32_t>(0xdeadbeef, 17)); + EXPECT_EQ(uint32_t(0x5b7ddfbdU), rotl<uint32_t>(0xdeadbeef, 41)); + EXPECT_EQ(uint32_t(0xb6fbbf7aU), rotl<uint32_t>(0xdeadbeef, -22)); + + EXPECT_EQ(uint64_t(0x12345678deadbeefULL), + rotl<uint64_t>(0x12345678deadbeefULL, 0)); + EXPECT_EQ(uint64_t(0xf56df77891a2b3c6ULL), + rotl<uint64_t>(0x12345678deadbeefULL, 35)); + EXPECT_EQ(uint64_t(0x8d159e37ab6fbbc4ULL), + rotl<uint64_t>(0x12345678deadbeefULL, 70)); + EXPECT_EQ(uint64_t(0xb7dde2468acf1bd5ULL), + rotl<uint64_t>(0x12345678deadbeefULL, -19)); +} + +TEST(LlvmLibcBitTest, Rotr) { + EXPECT_EQ(uint8_t(0x53U), rotr<uint8_t>(0x53, 0)); + EXPECT_EQ(uint8_t(0xd4U), rotr<uint8_t>(0x53, 2)); + EXPECT_EQ(uint8_t(0xa9U), rotr<uint8_t>(0x53, 9)); + EXPECT_EQ(uint8_t(0x6aU), rotr<uint8_t>(0x53, -5)); + + EXPECT_EQ(uint16_t(0xabcdU), rotr<uint16_t>(0xabcd, 0)); + EXPECT_EQ(uint16_t(0x36afU), rotr<uint16_t>(0xabcd, 6)); + EXPECT_EQ(uint16_t(0x6af3U), rotr<uint16_t>(0xabcd, 18)); + EXPECT_EQ(uint16_t(0x36afU), rotr<uint16_t>(0xabcd, -10)); + + EXPECT_EQ(uint32_t(0xdeadbeefU), rotr<uint32_t>(0xdeadbeef, 0)); + EXPECT_EQ(uint32_t(0xdf77ef56U), rotr<uint32_t>(0xdeadbeef, 17)); + EXPECT_EQ(uint32_t(0x77ef56dfU), rotr<uint32_t>(0xdeadbeef, 41)); + EXPECT_EQ(uint32_t(0xbbf7ab6fU), rotr<uint32_t>(0xdeadbeef, -22)); + + EXPECT_EQ(uint64_t(0x12345678deadbeefULL), + rotr<uint64_t>(0x12345678deadbeefULL, 0)); + EXPECT_EQ(uint64_t(0x1bd5b7dde2468acfULL), + rotr<uint64_t>(0x12345678deadbeefULL, 35)); + EXPECT_EQ(uint64_t(0xbc48d159e37ab6fbULL), + rotr<uint64_t>(0x12345678deadbeefULL, 70)); + EXPECT_EQ(uint64_t(0xb3c6f56df77891a2ULL), + rotr<uint64_t>(0x12345678deadbeefULL, -19)); +} + +} // namespace LIBC_NAMESPACE::cpp diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index ffb2652ed1419d0..4146fb4cb36f3d3 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -161,6 +161,7 @@ libc_support_library( name = "__support_cpp_bit", hdrs = ["src/__support/CPP/bit.h"], deps = [ + ":__support_cpp_limits", ":__support_cpp_type_traits", ":__support_macros_attributes", ":__support_macros_config", diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel index 59dcfd6ca93ca2a..07d9ac1d500820b 100644 --- a/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/test/src/__support/CPP/BUILD.bazel @@ -22,6 +22,15 @@ libc_test( deps = ["//libc:__support_cpp_bitset"], ) +libc_test( + name = "bit_test", + srcs = ["bit_test.cpp"], + deps = [ + "//libc:__support_cpp_bit", + "//libc:__support_uint", + ], +) + libc_test( name = "cstddef_test", srcs = ["cstddef_test.cpp"], >From c91367d4d99e0857efbd8c51cc743e74bee750aa Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Thu, 30 Nov 2023 09:31:44 +0000 Subject: [PATCH 2/8] SFINAE all the functions --- libc/src/__support/CPP/bit.h | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h index f744d804398e213..0059ad1ef7e3559 100644 --- a/libc/src/__support/CPP/bit.h +++ b/libc/src/__support/CPP/bit.h @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// This is inspired from LLVM ADT/bit.h header. +// This is inspired by LLVM ADT/bit.h header. #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H #define LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H @@ -69,9 +69,8 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> /// Only unsigned integral types are allowed. /// /// Returns cpp::numeric_limits<T>::digits on an input of 0. -template <typename T> +template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> [[nodiscard]] LIBC_INLINE constexpr int countr_zero(T value) { - static_assert(cpp::is_unsigned_v<T>); if (!value) return cpp::numeric_limits<T>::digits; if (value & 0x1) @@ -109,9 +108,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> +template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> [[nodiscard]] LIBC_INLINE constexpr int countl_zero(T value) { - static_assert(cpp::is_unsigned_v<T>); if (!value) return cpp::numeric_limits<T>::digits; // Bisection method. @@ -147,9 +145,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> +template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> [[nodiscard]] LIBC_INLINE constexpr int countl_one(T value) { - static_assert(cpp::is_unsigned_v<T>); return cpp::countl_zero<T>(~value); } @@ -160,9 +157,8 @@ template <typename T> /// Only unsigned integral types are allowed. /// /// Returns cpp::numeric_limits<T>::digits on an input of all ones. -template <typename T> +template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> [[nodiscard]] LIBC_INLINE constexpr int countr_one(T value) { - static_assert(cpp::is_unsigned_v<T>); return cpp::countr_zero<T>(~value); } @@ -170,9 +166,8 @@ template <typename T> /// Returns 0 otherwise. /// /// Ex. bit_width(5) == 3. -template <typename T> +template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> [[nodiscard]] LIBC_INLINE constexpr int bit_width(T value) { - static_assert(cpp::is_unsigned_v<T>); return cpp::numeric_limits<T>::digits - cpp::countl_zero(value); } @@ -180,8 +175,8 @@ template <typename T> /// nonzero. Returns 0 otherwise. /// /// Ex. bit_floor(5) == 4. -template <typename T> [[nodiscard]] LIBC_INLINE constexpr T bit_floor(T value) { - static_assert(cpp::is_unsigned_v<T>); +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); @@ -194,8 +189,8 @@ template <typename T> [[nodiscard]] LIBC_INLINE constexpr T 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 T bit_ceil(T value) { - static_assert(cpp::is_unsigned_v<T>); +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); >From 95ade514c90aaf5caf9e1b442adc2da94c15a9a9 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Thu, 30 Nov 2023 09:56:02 +0000 Subject: [PATCH 3/8] Add more documentation --- libc/src/__support/CPP/bit.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h index 0059ad1ef7e3559..01cec4ce6b01dd0 100644 --- a/libc/src/__support/CPP/bit.h +++ b/libc/src/__support/CPP/bit.h @@ -199,6 +199,10 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> /// Count the number of set bits in a value. /// Ex. popcount(0xF000F000) = 8 /// Returns 0 if the word is zero. +/// +/// The non-builtin version uses an algorithm that counts bit set by dividing +/// the integer into multiple lanes and reducing the result. +/// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> [[nodiscard]] LIBC_INLINE constexpr int popcount(T value) { if constexpr (sizeof(T) == 8) { @@ -223,6 +227,9 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> } } +// Rotate algorithms make use of "Safe, Efficient, and Portable Rotate in C/C++" +// from https://blog.regehr.org/archives/1063. + // Forward-declare rotr so that rotl can use it. template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> [[nodiscard]] LIBC_INLINE constexpr T rotr(T value, int rotate); >From 0404e52cd15b5893fddce0d7551a0f03ae02bd3a Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Thu, 30 Nov 2023 10:07:49 +0000 Subject: [PATCH 4/8] Refactor numeric_limits --- libc/src/__support/CPP/limits.h | 136 ++++++++++++++------------------ 1 file changed, 58 insertions(+), 78 deletions(-) diff --git a/libc/src/__support/CPP/limits.h b/libc/src/__support/CPP/limits.h index b56f2557a737cab..3126b38ec48e612 100644 --- a/libc/src/__support/CPP/limits.h +++ b/libc/src/__support/CPP/limits.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_LIMITS_H #define LLVM_LIBC_SRC___SUPPORT_CPP_LIMITS_H +#include "src/__support/CPP/type_traits/is_signed.h" #include "src/__support/macros/attributes.h" // LIBC_INLINE #include <limits.h> // CHAR_BIT @@ -24,93 +25,72 @@ constexpr long long LLONG_MIN = 1LL << (LLONG_BIT_WIDTH - 1); constexpr unsigned long long ULLONG_MAX = ~0ULL; #endif -template <class T> class numeric_limits { -public: - LIBC_INLINE static constexpr T max(); - LIBC_INLINE static constexpr T min(); +namespace internal { + +template <typename T, T min_value, T max_value> struct numeric_limits_impl { + LIBC_INLINE static constexpr T max() { return max_value; } + LIBC_INLINE static constexpr T min() { return min_value; } + LIBC_INLINE_VAR static constexpr int digits = + CHAR_BIT * sizeof(T) - cpp::is_signed_v<T>; }; +} // namespace internal + +template <class T> struct numeric_limits {}; + // TODO: Add numeric_limits specializations as needed for new types. +template <> +struct numeric_limits<short> + : public internal::numeric_limits_impl<short, SHRT_MIN, SHRT_MAX> {}; -template <> class numeric_limits<int> { -public: - LIBC_INLINE static constexpr int max() { return INT_MAX; } - LIBC_INLINE static constexpr int min() { return INT_MIN; } - LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(int) - 1; -}; -template <> class numeric_limits<unsigned int> { -public: - LIBC_INLINE static constexpr unsigned int max() { return UINT_MAX; } - LIBC_INLINE static constexpr unsigned int min() { return 0; } - LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(unsigned int); -}; -template <> class numeric_limits<long> { -public: - LIBC_INLINE static constexpr long max() { return LONG_MAX; } - LIBC_INLINE static constexpr long min() { return LONG_MIN; } - LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(long) - 1; -}; -template <> class numeric_limits<unsigned long> { -public: - LIBC_INLINE static constexpr unsigned long max() { return ULONG_MAX; } - LIBC_INLINE static constexpr unsigned long min() { return 0; } - LIBC_INLINE_VAR static constexpr int digits = - CHAR_BIT * sizeof(unsigned long); -}; -template <> class numeric_limits<long long> { -public: - LIBC_INLINE static constexpr long long max() { return LLONG_MAX; } - LIBC_INLINE static constexpr long long min() { return LLONG_MIN; } - LIBC_INLINE_VAR static constexpr int digits = - CHAR_BIT * sizeof(long long) - 1; -}; -template <> class numeric_limits<unsigned long long> { -public: - LIBC_INLINE static constexpr unsigned long long max() { return ULLONG_MAX; } - LIBC_INLINE static constexpr unsigned long long min() { return 0; } - LIBC_INLINE_VAR static constexpr int digits = - CHAR_BIT * sizeof(unsigned long long); -}; -template <> class numeric_limits<short> { -public: - LIBC_INLINE static constexpr short max() { return SHRT_MAX; } - LIBC_INLINE static constexpr short min() { return SHRT_MIN; } - LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT * sizeof(short) - 1; -}; -template <> class numeric_limits<unsigned short> { -public: - LIBC_INLINE static constexpr unsigned short max() { return USHRT_MAX; } - LIBC_INLINE static constexpr unsigned short min() { return 0; } - LIBC_INLINE_VAR static constexpr int digits = - CHAR_BIT * sizeof(unsigned short); -}; -template <> class numeric_limits<char> { -public: - LIBC_INLINE static constexpr char max() { return CHAR_MAX; } - LIBC_INLINE static constexpr char min() { return CHAR_MIN; } -}; -template <> class numeric_limits<signed char> { -public: - LIBC_INLINE static constexpr signed char max() { return SCHAR_MAX; } - LIBC_INLINE static constexpr signed char min() { return SCHAR_MIN; } - LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT - 1; +template <> +struct numeric_limits<unsigned short> + : public internal::numeric_limits_impl<unsigned short, 0, USHRT_MAX> {}; + +template <> +struct numeric_limits<int> + : public internal::numeric_limits_impl<int, INT_MIN, INT_MAX> {}; + +template <> +struct numeric_limits<unsigned int> + : public internal::numeric_limits_impl<unsigned int, 0, UINT_MAX> {}; + +template <> +struct numeric_limits<long> + : public internal::numeric_limits_impl<long, LONG_MIN, LONG_MAX> {}; + +template <> +struct numeric_limits<unsigned long> + : public internal::numeric_limits_impl<unsigned long, 0, ULONG_MAX> {}; + +template <> +struct numeric_limits<long long> + : public internal::numeric_limits_impl<long long, LLONG_MIN, LLONG_MAX> {}; + +template <> +struct numeric_limits<unsigned long long> + : public internal::numeric_limits_impl<unsigned long long, 0, ULLONG_MAX> { }; -template <> class numeric_limits<unsigned char> { -public: - LIBC_INLINE static constexpr unsigned char max() { return UCHAR_MAX; } - LIBC_INLINE static constexpr unsigned char min() { return 0; } - LIBC_INLINE_VAR static constexpr int digits = CHAR_BIT; + +template <> +struct numeric_limits<char> + : public internal::numeric_limits_impl<char, CHAR_MIN, CHAR_MAX> {}; + +template <> +struct numeric_limits<signed char> + : public internal::numeric_limits_impl<signed char, SCHAR_MIN, SCHAR_MAX> { }; + +template <> +struct numeric_limits<unsigned char> + : public internal::numeric_limits_impl<unsigned char, 0, UCHAR_MAX> {}; + #ifdef __SIZEOF_INT128__ // On platform where UInt128 resolves to __uint128_t, this specialization // provides the limits of UInt128. -template <> class numeric_limits<__uint128_t> { -public: - LIBC_INLINE static constexpr __uint128_t max() { return ~__uint128_t(0); } - LIBC_INLINE static constexpr __uint128_t min() { return 0; } - LIBC_INLINE_VAR static constexpr int digits = - CHAR_BIT * sizeof(__uint128_t) - 1; -}; +template <> +struct numeric_limits<__uint128_t> + : public internal::numeric_limits_impl<__uint128_t, 0, ~__uint128_t(0)> {}; #endif } // namespace cpp >From d2c83406d1e5dedd7b4ca87a8d6cc7abde967ee4 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Thu, 30 Nov 2023 10:09:33 +0000 Subject: [PATCH 5/8] Fix comment --- libc/test/src/__support/CPP/bit_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/test/src/__support/CPP/bit_test.cpp b/libc/test/src/__support/CPP/bit_test.cpp index caec621c5a6f64a..c64ee3d232f09e8 100644 --- a/libc/test/src/__support/CPP/bit_test.cpp +++ b/libc/test/src/__support/CPP/bit_test.cpp @@ -86,7 +86,7 @@ TEST(LlvmLibcBitTest, BitCeil) { EXPECT_EQ(uint32_t(4), bit_ceil(uint32_t(4))); EXPECT_EQ(uint64_t(4), bit_ceil(uint64_t(4))); - // The result is the largest representable value for each type. + // The result is the representable power of 2 for each type. EXPECT_EQ(uint8_t(0x80), bit_ceil(uint8_t(0x7f))); EXPECT_EQ(uint16_t(0x8000), bit_ceil(uint16_t(0x7fff))); EXPECT_EQ(uint32_t(0x80000000), bit_ceil(uint32_t(0x7fffffff))); >From 8de6a9058244c6797b1f00068f31c4dfc2643e1f Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Thu, 30 Nov 2023 10:22:04 +0000 Subject: [PATCH 6/8] Remove popcount as we don't have a use case for it now. --- libc/src/__support/CPP/bit.h | 31 ------------------------ libc/test/src/__support/CPP/bit_test.cpp | 11 --------- 2 files changed, 42 deletions(-) diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h index 01cec4ce6b01dd0..abea20b67537125 100644 --- a/libc/src/__support/CPP/bit.h +++ b/libc/src/__support/CPP/bit.h @@ -196,37 +196,6 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> return T(1) << cpp::bit_width<T>(value - 1u); } -/// Count the number of set bits in a value. -/// Ex. popcount(0xF000F000) = 8 -/// Returns 0 if the word is zero. -/// -/// The non-builtin version uses an algorithm that counts bit set by dividing -/// the integer into multiple lanes and reducing the result. -/// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel -template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>> -[[nodiscard]] LIBC_INLINE constexpr int popcount(T value) { - if constexpr (sizeof(T) == 8) { -#if LIBC_HAS_BUILTIN(__builtin_popcountll) - return (int)__builtin_popcountll(value); -#endif - uint64_t v = value; - v = v - ((v >> 1) & 0x5555555555555555ULL); - v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL); - v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL; - return int((uint64_t)(v * 0x0101010101010101ULL) >> 56); - } else if constexpr (sizeof(T) <= 4) { -#if LIBC_HAS_BUILTIN(__builtin_popcount) - return (int)__builtin_popcount(value); -#endif - uint32_t v = value; - v = v - ((v >> 1) & 0x55555555); - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); - return int(((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24); - } else { - static_assert(cpp::always_false<T>); - } -} - // Rotate algorithms make use of "Safe, Efficient, and Portable Rotate in C/C++" // from https://blog.regehr.org/archives/1063. diff --git a/libc/test/src/__support/CPP/bit_test.cpp b/libc/test/src/__support/CPP/bit_test.cpp index c64ee3d232f09e8..b7cd99d169fc52e 100644 --- a/libc/test/src/__support/CPP/bit_test.cpp +++ b/libc/test/src/__support/CPP/bit_test.cpp @@ -139,17 +139,6 @@ TEST(LlvmLibcBitTest, BitFloor) { bit_floor(uint64_t(0xffffffffffffffff))); } -TEST(LlvmLibcBitTest, PopCount) { - EXPECT_EQ(0, popcount(0U)); - EXPECT_EQ(0, popcount(0ULL)); - - EXPECT_EQ(32, popcount(~0U)); - EXPECT_EQ(64, popcount(~0ULL)); - - for (int i = 0; i != 32; ++i) - EXPECT_EQ(1, popcount(1U << i)); -} - TYPED_TEST(LlvmLibcBitTest, RotateIsInvariantForZeroAndOne, UnsignedTypes) { constexpr T all_zeros = T(0); constexpr T all_ones = ~T(0); >From 4cfc2df0370ef3c33df983cdf6bde60eee5d0499 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Thu, 30 Nov 2023 10:23:43 +0000 Subject: [PATCH 7/8] Add comment --- libc/src/__support/CPP/bit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/src/__support/CPP/bit.h b/libc/src/__support/CPP/bit.h index abea20b67537125..2091c3662d5076b 100644 --- a/libc/src/__support/CPP/bit.h +++ b/libc/src/__support/CPP/bit.h @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// // This is inspired by LLVM ADT/bit.h header. +// Some functions are missing, we can add them as needed (popcount, byteswap). #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H #define LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H >From d5969755d9892f4eab01bc51165f9e7eca99f4c6 Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet <gchate...@google.com> Date: Thu, 30 Nov 2023 12:50:18 +0000 Subject: [PATCH 8/8] Commit newline at EOF --- libc/src/__support/CPP/limits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/src/__support/CPP/limits.h b/libc/src/__support/CPP/limits.h index f471a6016a0fa81..e92ad00f80e0379 100644 --- a/libc/src/__support/CPP/limits.h +++ b/libc/src/__support/CPP/limits.h @@ -96,4 +96,4 @@ struct numeric_limits<__uint128_t> } // namespace cpp } // namespace LIBC_NAMESPACE -#endif // LLVM_LIBC_SRC___SUPPORT_CPP_LIMITS_H \ No newline at end of file +#endif // LLVM_LIBC_SRC___SUPPORT_CPP_LIMITS_H _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits