Skip to content

Commit 9e3982d

Browse files
authored
[libc++] Replace __libcpp_{ctz, clz} with __builtin_{ctzg, clzg} (#133920)
`__libcpp_{ctz, clz}` were previously used as fallbacks for `__builtin_{ctzg, clzg}` to ensure compatibility with older compilers (Clang 18 and earlier), as `__builtin_{ctzg, clzg}` became available in Clang 19. Now that support for Clang 18 has been officially dropped in #130142, we can now safely replace all instances of `__libcpp_{ctz, clz}` with `__count{l,r}_zero` (which internally call `__builtin_{ctzg, clzg}` and eliminate the fallback logic. Closes #131179.
1 parent 5c4e6c6 commit 9e3982d

File tree

7 files changed

+21
-144
lines changed

7 files changed

+21
-144
lines changed

libcxx/include/__algorithm/sort.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,10 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos(
359359
// Swap one pair on each iteration as long as both bitsets have at least one
360360
// element for swapping.
361361
while (__left_bitset != 0 && __right_bitset != 0) {
362-
difference_type __tz_left = __libcpp_ctz(__left_bitset);
363-
__left_bitset = __libcpp_blsr(__left_bitset);
364-
difference_type __tz_right = __libcpp_ctz(__right_bitset);
365-
__right_bitset = __libcpp_blsr(__right_bitset);
362+
difference_type __tz_left = std::__countr_zero(__left_bitset);
363+
__left_bitset = std::__libcpp_blsr(__left_bitset);
364+
difference_type __tz_right = std::__countr_zero(__right_bitset);
365+
__right_bitset = std::__libcpp_blsr(__right_bitset);
366366
_Ops::iter_swap(__first + __tz_left, __last - __tz_right);
367367
}
368368
}
@@ -458,7 +458,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
458458
// Swap within the left side. Need to find set positions in the reverse
459459
// order.
460460
while (__left_bitset != 0) {
461-
difference_type __tz_left = __detail::__block_size - 1 - __libcpp_clz(__left_bitset);
461+
difference_type __tz_left = __detail::__block_size - 1 - std::__countl_zero(__left_bitset);
462462
__left_bitset &= (static_cast<uint64_t>(1) << __tz_left) - 1;
463463
_RandomAccessIterator __it = __first + __tz_left;
464464
if (__it != __lm1) {
@@ -471,7 +471,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
471471
// Swap within the right side. Need to find set positions in the reverse
472472
// order.
473473
while (__right_bitset != 0) {
474-
difference_type __tz_right = __detail::__block_size - 1 - __libcpp_clz(__right_bitset);
474+
difference_type __tz_right = __detail::__block_size - 1 - std::__countl_zero(__right_bitset);
475475
__right_bitset &= (static_cast<uint64_t>(1) << __tz_right) - 1;
476476
_RandomAccessIterator __it = __lm1 - __tz_right;
477477
if (__it != __first) {

libcxx/include/__bit/countl.h

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
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-
129
#ifndef _LIBCPP___BIT_COUNTL_H
1310
#define _LIBCPP___BIT_COUNTL_H
1411

@@ -27,69 +24,10 @@ _LIBCPP_PUSH_MACROS
2724

2825
_LIBCPP_BEGIN_NAMESPACE_STD
2926

30-
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned __x) _NOEXCEPT {
31-
return __builtin_clz(__x);
32-
}
33-
34-
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned long __x) _NOEXCEPT {
35-
return __builtin_clzl(__x);
36-
}
37-
38-
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned long long __x) _NOEXCEPT {
39-
return __builtin_clzll(__x);
40-
}
41-
42-
#if _LIBCPP_HAS_INT128
43-
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
47-
// The function is written in this form due to C++ constexpr limitations.
48-
// The algorithm:
49-
// - Test whether any bit in the high 64-bits is set
50-
// - No bits set:
51-
// - The high 64-bits contain 64 leading zeros,
52-
// - Add the result of the low 64-bits.
53-
// - Any bits set:
54-
// - The number of leading zeros of the input is the number of leading
55-
// zeros in the high 64-bits.
56-
return ((__x >> 64) == 0) ? (64 + __builtin_clzll(static_cast<unsigned long long>(__x)))
57-
: __builtin_clzll(static_cast<unsigned long long>(__x >> 64));
58-
# endif
59-
}
60-
#endif // _LIBCPP_HAS_INT128
61-
6227
template <class _Tp>
6328
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countl_zero(_Tp __t) _NOEXCEPT {
6429
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__countl_zero requires an unsigned integer type");
65-
#if __has_builtin(__builtin_clzg)
6630
return __builtin_clzg(__t, numeric_limits<_Tp>::digits);
67-
#else // __has_builtin(__builtin_clzg)
68-
if (__t == 0)
69-
return numeric_limits<_Tp>::digits;
70-
71-
if (sizeof(_Tp) <= sizeof(unsigned int))
72-
return std::__libcpp_clz(static_cast<unsigned int>(__t)) -
73-
(numeric_limits<unsigned int>::digits - numeric_limits<_Tp>::digits);
74-
else if (sizeof(_Tp) <= sizeof(unsigned long))
75-
return std::__libcpp_clz(static_cast<unsigned long>(__t)) -
76-
(numeric_limits<unsigned long>::digits - numeric_limits<_Tp>::digits);
77-
else if (sizeof(_Tp) <= sizeof(unsigned long long))
78-
return std::__libcpp_clz(static_cast<unsigned long long>(__t)) -
79-
(numeric_limits<unsigned long long>::digits - numeric_limits<_Tp>::digits);
80-
else {
81-
int __ret = 0;
82-
int __iter = 0;
83-
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
84-
while (true) {
85-
__t = std::__rotl(__t, __ulldigits);
86-
if ((__iter = std::__countl_zero(static_cast<unsigned long long>(__t))) != __ulldigits)
87-
break;
88-
__ret += __iter;
89-
}
90-
return __ret + __iter;
91-
}
92-
#endif // __has_builtin(__builtin_clzg)
9331
}
9432

9533
#if _LIBCPP_STD_VER >= 20

libcxx/include/__bit/countr.h

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,10 @@
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-
129
#ifndef _LIBCPP___BIT_COUNTR_H
1310
#define _LIBCPP___BIT_COUNTR_H
1411

1512
#include <__assert>
16-
#include <__bit/rotate.h>
1713
#include <__concepts/arithmetic.h>
1814
#include <__config>
1915
#include <__type_traits/is_unsigned.h>
@@ -28,55 +24,10 @@ _LIBCPP_PUSH_MACROS
2824

2925
_LIBCPP_BEGIN_NAMESPACE_STD
3026

31-
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned __x) _NOEXCEPT {
32-
return __builtin_ctz(__x);
33-
}
34-
35-
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned long __x) _NOEXCEPT {
36-
return __builtin_ctzl(__x);
37-
}
38-
39-
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned long long __x) _NOEXCEPT {
40-
return __builtin_ctzll(__x);
41-
}
42-
43-
// A constexpr implementation for C++11 and later (using clang extensions for constexpr support)
44-
// Precondition: __t != 0 (the caller __countr_zero handles __t == 0 as a special case)
45-
template <class _Tp>
46-
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
47-
_LIBCPP_ASSERT_INTERNAL(__t != 0, "__countr_zero_impl called with zero value");
48-
static_assert(is_unsigned<_Tp>::value, "__countr_zero_impl only works with unsigned types");
49-
if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned int)) {
50-
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
51-
} else if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned long)) {
52-
return std::__libcpp_ctz(static_cast<unsigned long>(__t));
53-
} else if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned long long)) {
54-
return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
55-
} else {
56-
#if _LIBCPP_STD_VER == 11
57-
unsigned long long __ull = static_cast<unsigned long long>(__t);
58-
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
59-
return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz(__ull);
60-
#else
61-
int __ret = 0;
62-
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
63-
while (static_cast<unsigned long long>(__t) == 0uLL) {
64-
__ret += __ulldigits;
65-
__t >>= __ulldigits;
66-
}
67-
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
68-
#endif
69-
}
70-
}
71-
7227
template <class _Tp>
7328
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
7429
static_assert(is_unsigned<_Tp>::value, "__countr_zero only works with unsigned types");
75-
#if __has_builtin(__builtin_ctzg) // TODO (LLVM 21): This can be dropped once we only support Clang >= 19.
7630
return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
77-
#else
78-
return __t != 0 ? std::__countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
79-
#endif
8031
}
8132

8233
#if _LIBCPP_STD_VER >= 20

libcxx/include/__bit_reference

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public:
165165

166166
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void flip() _NOEXCEPT { *__seg_ ^= __mask_; }
167167
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __bit_iterator<_Cp, false> operator&() const _NOEXCEPT {
168-
return __bit_iterator<_Cp, false>(__seg_, static_cast<unsigned>(std::__libcpp_ctz(__mask_)));
168+
return __bit_iterator<_Cp, false>(__seg_, static_cast<unsigned>(std::__countr_zero(__mask_)));
169169
}
170170

171171
private:
@@ -234,7 +234,7 @@ public:
234234
}
235235

236236
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __bit_iterator<_Cp, true> operator&() const _NOEXCEPT {
237-
return __bit_iterator<_Cp, true>(__seg_, static_cast<unsigned>(std::__libcpp_ctz(__mask_)));
237+
return __bit_iterator<_Cp, true>(__seg_, static_cast<unsigned>(std::__countr_zero(__mask_)));
238238
}
239239

240240
private:

libcxx/include/__charconv/to_chars_integral.h

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,8 @@ struct _LIBCPP_HIDDEN __integral<2> {
114114
template <typename _Tp>
115115
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
116116
// If value == 0 still need one digit. If the value != this has no
117-
// effect since the code scans for the most significant bit set. (Note
118-
// that __libcpp_clz doesn't work for 0.)
119-
return numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1);
117+
// effect since the code scans for the most significant bit set.
118+
return numeric_limits<_Tp>::digits - std::__countl_zero(__value | 1);
120119
}
121120

122121
template <typename _Tp>
@@ -150,9 +149,8 @@ struct _LIBCPP_HIDDEN __integral<8> {
150149
template <typename _Tp>
151150
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
152151
// If value == 0 still need one digit. If the value != this has no
153-
// effect since the code scans for the most significat bit set. (Note
154-
// that __libcpp_clz doesn't work for 0.)
155-
return ((numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1)) + 2) / 3;
152+
// effect since the code scans for the most significat bit set.
153+
return ((numeric_limits<_Tp>::digits - std::__countl_zero(__value | 1)) + 2) / 3;
156154
}
157155

158156
template <typename _Tp>
@@ -186,9 +184,8 @@ struct _LIBCPP_HIDDEN __integral<16> {
186184
template <typename _Tp>
187185
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
188186
// If value == 0 still need one digit. If the value != this has no
189-
// effect since the code scans for the most significat bit set. (Note
190-
// that __libcpp_clz doesn't work for 0.)
191-
return (numeric_limits<_Tp>::digits - std::__libcpp_clz(__value | 1) + 3) / 4;
187+
// effect since the code scans for the most significat bit set.
188+
return (numeric_limits<_Tp>::digits - std::__countl_zero(__value | 1) + 3) / 4;
192189
}
193190

194191
template <typename _Tp>

libcxx/include/__charconv/traits.h

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,9 @@ struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) <= sizeof(uin
4343
///
4444
/// The algorithm is based on
4545
/// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
46-
/// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that
47-
/// function requires its input to have at least one bit set the value of
48-
/// zero is set to one. This means the first element of the lookup table is
49-
/// zero.
46+
/// Instead of using IntegerLogBase2 it uses __countl_zero.
5047
static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
51-
auto __t = (32 - std::__libcpp_clz(static_cast<type>(__v | 1))) * 1233 >> 12;
48+
auto __t = (32 - std::__countl_zero(static_cast<type>(__v | 1))) * 1233 >> 12;
5249
return __t - (__v < __itoa::__pow10_32[__t]) + 1;
5350
}
5451

@@ -69,12 +66,9 @@ struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(uin
6966
///
7067
/// The algorithm is based on
7168
/// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
72-
/// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that
73-
/// function requires its input to have at least one bit set the value of
74-
/// zero is set to one. This means the first element of the lookup table is
75-
/// zero.
69+
/// Instead of using IntegerLogBase2 it uses __countl_zero.
7670
static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
77-
auto __t = (64 - std::__libcpp_clz(static_cast<type>(__v | 1))) * 1233 >> 12;
71+
auto __t = (64 - std::__countl_zero(static_cast<type>(__v | 1))) * 1233 >> 12;
7872
return __t - (__v < __itoa::__pow10_64[__t]) + 1;
7973
}
8074

@@ -96,15 +90,12 @@ struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(__u
9690
///
9791
/// The algorithm is based on
9892
/// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
99-
/// Instead of using IntegerLogBase2 it uses __libcpp_clz. Since that
100-
/// function requires its input to have at least one bit set the value of
101-
/// zero is set to one. This means the first element of the lookup table is
102-
/// zero.
93+
/// Instead of using IntegerLogBase2 it uses __countl_zero.
10394
static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
10495
_LIBCPP_ASSERT_INTERNAL(
10596
__v > numeric_limits<uint64_t>::max(), "The optimizations for this algorithm fail when this isn't true.");
10697
// There's always a bit set in the upper 64-bits.
107-
auto __t = (128 - std::__libcpp_clz(static_cast<uint64_t>(__v >> 64))) * 1233 >> 12;
98+
auto __t = (128 - std::__countl_zero(static_cast<uint64_t>(__v >> 64))) * 1233 >> 12;
10899
_LIBCPP_ASSERT_INTERNAL(__t >= __itoa::__pow10_128_offset, "Index out of bounds");
109100
// __t is adjusted since the lookup table misses the lower entries.
110101
return __t - (__v < __itoa::__pow10_128[__t - __itoa::__pow10_128_offset]) + 1;

libcxx/include/__hash_table

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ inline _LIBCPP_HIDE_FROM_ABI size_t __constrain_hash(size_t __h, size_t __bc) {
147147
}
148148

149149
inline _LIBCPP_HIDE_FROM_ABI size_t __next_hash_pow2(size_t __n) {
150-
return __n < 2 ? __n : (size_t(1) << (numeric_limits<size_t>::digits - __libcpp_clz(__n - 1)));
150+
return __n < 2 ? __n : (size_t(1) << (numeric_limits<size_t>::digits - std::__countl_zero(__n - 1)));
151151
}
152152

153153
template <class _Tp, class _Hash, class _Equal, class _Alloc>

0 commit comments

Comments
 (0)