Skip to content

Commit 51f7b26

Browse files
[libc][support][UInt] implement 128b math helpers (#86531)
Flush out the remaining UInt<128> support and add test coverage. We could have used cpp::popcount in the implementation of UInt::has_single_bit, but has_single_bit has a perhaps useful early return.
1 parent 13b653a commit 51f7b26

File tree

4 files changed

+66
-11
lines changed

4 files changed

+66
-11
lines changed

libc/src/__support/CPP/bit.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,14 @@ LIBC_INLINE constexpr To bit_or_static_cast(const From &from) {
242242
/// Count number of 1's aka population count or Hamming weight.
243243
///
244244
/// Only unsigned integral types are allowed.
245+
// clang-19+, gcc-14+
246+
#if __has_builtin(__builtin_popcountg)
247+
template <typename T>
248+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
249+
popcount(T value) {
250+
return __builtin_popcountg(value);
251+
}
252+
#else // !__has_builtin(__builtin_popcountg)
245253
template <typename T>
246254
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_unsigned_v<T>, int>
247255
popcount(T value) {
@@ -261,7 +269,7 @@ ADD_SPECIALIZATION(unsigned short, __builtin_popcount)
261269
ADD_SPECIALIZATION(unsigned, __builtin_popcount)
262270
ADD_SPECIALIZATION(unsigned long, __builtin_popcountl)
263271
ADD_SPECIALIZATION(unsigned long long, __builtin_popcountll)
264-
// TODO: 128b specializations?
272+
#endif // __builtin_popcountg
265273
#undef ADD_SPECIALIZATION
266274

267275
} // namespace LIBC_NAMESPACE::cpp

libc/src/__support/UInt.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,17 @@ bit_cast(const UInt<Bits> &from) {
10821082
return cpp::bit_cast<To>(from.val);
10831083
}
10841084

1085+
// Specialization of cpp::popcount ('bit.h') for BigInt.
1086+
template <typename T>
1087+
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
1088+
popcount(T value) {
1089+
int bits = 0;
1090+
for (auto word : value.val)
1091+
if (word)
1092+
bits += popcount(word);
1093+
return bits;
1094+
}
1095+
10851096
// Specialization of cpp::has_single_bit ('bit.h') for BigInt.
10861097
template <typename T>
10871098
[[nodiscard]] LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, bool>
@@ -1218,6 +1229,49 @@ LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, T> mask_leading_ones() {
12181229
return out;
12191230
}
12201231

1232+
// Specialization of count_zeros ('math_extras.h') for BigInt.
1233+
template <typename T>
1234+
[[nodiscard]]
1235+
LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
1236+
count_zeros(T value) {
1237+
return cpp::popcount(~value);
1238+
}
1239+
1240+
// Specialization of first_leading_zero ('math_extras.h') for BigInt.
1241+
template <typename T>
1242+
[[nodiscard]]
1243+
LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
1244+
first_leading_zero(T value) {
1245+
return value == cpp::numeric_limits<T>::max() ? 0
1246+
: cpp::countl_one(value) + 1;
1247+
}
1248+
1249+
// Specialization of first_leading_one ('math_extras.h') for BigInt.
1250+
template <typename T>
1251+
[[nodiscard]]
1252+
LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
1253+
first_leading_one(T value) {
1254+
return first_leading_zero(~value);
1255+
}
1256+
1257+
// Specialization of first_trailing_zero ('math_extras.h') for BigInt.
1258+
template <typename T>
1259+
[[nodiscard]]
1260+
LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
1261+
first_trailing_zero(T value) {
1262+
return value == cpp::numeric_limits<T>::max() ? 0
1263+
: cpp::countr_zero(~value) + 1;
1264+
}
1265+
1266+
// Specialization of first_trailing_one ('math_extras.h') for BigInt.
1267+
template <typename T>
1268+
[[nodiscard]]
1269+
LIBC_INLINE constexpr cpp::enable_if_t<is_big_int_v<T>, int>
1270+
first_trailing_one(T value) {
1271+
return value == cpp::numeric_limits<T>::max() ? 0
1272+
: cpp::countr_zero(value) + 1;
1273+
}
1274+
12211275
} // namespace LIBC_NAMESPACE
12221276

12231277
#endif // LLVM_LIBC_SRC___SUPPORT_UINT_H

libc/test/src/__support/CPP/bit_test.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@
1515

1616
namespace LIBC_NAMESPACE::cpp {
1717

18-
using UnsignedTypesNoBigInt = testing::TypeList<
19-
#if defined(LIBC_TYPES_HAS_INT128)
20-
__uint128_t,
21-
#endif // LIBC_TYPES_HAS_INT128
22-
unsigned char, unsigned short, unsigned int, unsigned long,
23-
unsigned long long>;
24-
2518
using UnsignedTypes = testing::TypeList<
2619
#if defined(LIBC_TYPES_HAS_INT128)
2720
__uint128_t,
@@ -228,7 +221,7 @@ TEST(LlvmLibcBitTest, Rotr) {
228221
rotr<uint64_t>(0x12345678deadbeefULL, -19));
229222
}
230223

231-
TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypesNoBigInt) {
224+
TYPED_TEST(LlvmLibcBitTest, CountOnes, UnsignedTypes) {
232225
EXPECT_EQ(popcount(T(0)), 0);
233226
for (int i = 0; i != cpp::numeric_limits<T>::digits; ++i)
234227
EXPECT_EQ(popcount<T>(cpp::numeric_limits<T>::max() >> i),

libc/test/src/__support/math_extras_test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include "src/__support/UInt128.h" // UInt128
9+
#include "src/__support/UInt128.h" // UInt<128>
1010
#include "src/__support/integer_literals.h"
1111
#include "src/__support/math_extras.h"
1212
#include "test/UnitTest/Test.h"
@@ -19,7 +19,7 @@ using UnsignedTypesNoBigInt = testing::TypeList<
1919
__uint128_t,
2020
#endif // LIBC_TYPES_HAS_INT128
2121
unsigned char, unsigned short, unsigned int, unsigned long,
22-
unsigned long long>;
22+
unsigned long long, UInt<128>>;
2323

2424
TEST(LlvmLibcBlockMathExtrasTest, mask_trailing_ones) {
2525
EXPECT_EQ(0_u8, (mask_leading_ones<uint8_t, 0>()));

0 commit comments

Comments
 (0)