Skip to content

Commit

Permalink
[libc][__support][bit] add count_zeros (#82076)
Browse files Browse the repository at this point in the history
Will be useful for implementing C23 stdbit.h's stdc_count_zeros and
stdc_count_ones.
  • Loading branch information
nickdesaulniers committed Feb 20, 2024
1 parent a468d02 commit ed4bdb8
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
29 changes: 29 additions & 0 deletions libc/src/__support/CPP/bit.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,35 @@ template <typename T, typename = cpp::enable_if_t<cpp::is_unsigned_v<T>>>
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.
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)
++count;
return count;
}
#define ADD_SPECIALIZATION(TYPE, BUILTIN) \
template <> \
[[nodiscard]] LIBC_INLINE constexpr int count_ones<TYPE>(TYPE value) { \
return BUILTIN(value); \
}
ADD_SPECIALIZATION(unsigned char, __builtin_popcount)
ADD_SPECIALIZATION(unsigned short, __builtin_popcount)
ADD_SPECIALIZATION(unsigned, __builtin_popcount)
ADD_SPECIALIZATION(unsigned long, __builtin_popcountl)
ADD_SPECIALIZATION(unsigned long long, __builtin_popcountll)
// TODO: 128b specializations?
#undef ADD_SPECIALIZATION

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));
}

} // namespace LIBC_NAMESPACE::cpp

#endif // LLVM_LIBC_SRC___SUPPORT_CPP_BIT_H
13 changes: 13 additions & 0 deletions libc/test/src/__support/CPP/bit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,17 @@ TYPED_TEST(LlvmLibcBitTest, FirstTrailingOne, UnsignedTypes) {
EXPECT_EQ(first_trailing_one<T>(T(1) << i), i + 1);
}

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, 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),
cpp::numeric_limits<T>::digits - i);
}

} // namespace LIBC_NAMESPACE::cpp

0 comments on commit ed4bdb8

Please sign in to comment.