Skip to content

Commit a6db20f

Browse files
[libcxx] Use generic builtins for popcount, clz and ctz (#86563)
Fixes #86556 Use `__builtin_popcountg` instead of `__buildin_popcount{l|ll}` Use `__builtin_clzg instead` of `__buildin_clz{l|ll}` Use `__builtin_ctzg instead` of `__builtin_ctz{l|ll}` The generic variant of the builtins can be used to simplify some logic with >= Clang 19 or >= GCC 14, where these generic variants are available. As for backwards compatibility reasons, we can't completely remove the old logic. Therefore, I left ToDo comments to address this, as soon as support for pre Clang 19 as well as pre GCC 14 is dropped. --------- Co-authored-by: Nick Desaulniers <[email protected]>
1 parent 496de32 commit a6db20f

File tree

3 files changed

+25
-1
lines changed

3 files changed

+25
-1
lines changed

libcxx/include/__bit/countl.h

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

9+
// TODO: __builtin_clzg is available since Clang 19 and GCC 14. When support for older versions is dropped, we can
10+
// refactor this code to exclusively use __builtin_clzg.
11+
912
#ifndef _LIBCPP___BIT_COUNTL_H
1013
#define _LIBCPP___BIT_COUNTL_H
1114

@@ -38,6 +41,9 @@ _LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_cl
3841

3942
#ifndef _LIBCPP_HAS_NO_INT128
4043
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(__uint128_t __x) _NOEXCEPT {
44+
# if __has_builtin(__builtin_clzg)
45+
return __builtin_clzg(__x);
46+
# else
4147
// The function is written in this form due to C++ constexpr limitations.
4248
// The algorithm:
4349
// - Test whether any bit in the high 64-bits is set
@@ -49,12 +55,16 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(__uint128_t __x)
4955
// zeros in the high 64-bits.
5056
return ((__x >> 64) == 0) ? (64 + __builtin_clzll(static_cast<unsigned long long>(__x)))
5157
: __builtin_clzll(static_cast<unsigned long long>(__x >> 64));
58+
# endif
5259
}
5360
#endif // _LIBCPP_HAS_NO_INT128
5461

5562
template <class _Tp>
5663
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countl_zero(_Tp __t) _NOEXCEPT {
5764
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__countl_zero requires an unsigned integer type");
65+
#if __has_builtin(__builtin_clzg)
66+
return __builtin_clzg(__t, numeric_limits<_Tp>::digits);
67+
#else // __has_builtin(__builtin_clzg)
5868
if (__t == 0)
5969
return numeric_limits<_Tp>::digits;
6070

@@ -79,6 +89,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countl_zero(_Tp __t) _
7989
}
8090
return __ret + __iter;
8191
}
92+
#endif // __has_builtin(__builtin_clzg)
8293
}
8394

8495
#if _LIBCPP_STD_VER >= 20

libcxx/include/__bit/countr.h

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

9+
// TODO: __builtin_ctzg is available since Clang 19 and GCC 14. When support for older versions is dropped, we can
10+
// refactor this code to exclusively use __builtin_ctzg.
11+
912
#ifndef _LIBCPP___BIT_COUNTR_H
1013
#define _LIBCPP___BIT_COUNTR_H
1114

@@ -37,9 +40,11 @@ _LIBCPP_NODISCARD inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ct
3740

3841
template <class _Tp>
3942
_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero(_Tp __t) _NOEXCEPT {
43+
#if __has_builtin(__builtin_ctzg)
44+
return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
45+
#else // __has_builtin(__builtin_ctzg)
4046
if (__t == 0)
4147
return numeric_limits<_Tp>::digits;
42-
4348
if (sizeof(_Tp) <= sizeof(unsigned int))
4449
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
4550
else if (sizeof(_Tp) <= sizeof(unsigned long))
@@ -55,6 +60,7 @@ _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __coun
5560
}
5661
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
5762
}
63+
#endif // __has_builtin(__builtin_ctzg)
5864
}
5965

6066
#if _LIBCPP_STD_VER >= 20

libcxx/include/__bit/popcount.h

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

9+
// TODO: __builtin_popcountg is available since Clang 19 and GCC 14. When support for older versions is dropped, we can
10+
// refactor this code to exclusively use __builtin_popcountg.
11+
912
#ifndef _LIBCPP___BIT_POPCOUNT_H
1013
#define _LIBCPP___BIT_POPCOUNT_H
1114

@@ -39,6 +42,9 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_popcount(unsigned lo
3942

4043
template <__libcpp_unsigned_integer _Tp>
4144
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr int popcount(_Tp __t) noexcept {
45+
# if __has_builtin(__builtin_popcountg)
46+
return __builtin_popcountg(__t);
47+
# else // __has_builtin(__builtin_popcountg)
4248
if (sizeof(_Tp) <= sizeof(unsigned int))
4349
return std::__libcpp_popcount(static_cast<unsigned int>(__t));
4450
else if (sizeof(_Tp) <= sizeof(unsigned long))
@@ -53,6 +59,7 @@ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr int popcount(_Tp __t) noex
5359
}
5460
return __ret;
5561
}
62+
# endif // __has_builtin(__builtin_popcountg)
5663
}
5764

5865
#endif // _LIBCPP_STD_VER >= 20

0 commit comments

Comments
 (0)