Skip to content

Commit cd7e7f3

Browse files
committed
Revert "[libc++] Replace __libcpp_{ctz, clz} with __builtin_{ctzg, clzg} (llvm#133920)"
This reverts commit 9e3982d.
1 parent 35e6998 commit cd7e7f3

File tree

7 files changed

+144
-21
lines changed

7 files changed

+144
-21
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 = 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);
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);
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 - std::__countl_zero(__left_bitset);
461+
difference_type __tz_left = __detail::__block_size - 1 - __libcpp_clz(__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 - std::__countl_zero(__right_bitset);
474+
difference_type __tz_right = __detail::__block_size - 1 - __libcpp_clz(__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: 62 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

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

2528
_LIBCPP_BEGIN_NAMESPACE_STD
2629

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+
2762
template <class _Tp>
2863
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countl_zero(_Tp __t) _NOEXCEPT {
2964
static_assert(__libcpp_is_unsigned_integer<_Tp>::value, "__countl_zero requires an unsigned integer type");
65+
#if __has_builtin(__builtin_clzg)
3066
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)
3193
}
3294

3395
#if _LIBCPP_STD_VER >= 20

libcxx/include/__bit/countr.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
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

1215
#include <__assert>
16+
#include <__bit/rotate.h>
1317
#include <__concepts/arithmetic.h>
1418
#include <__config>
1519
#include <__type_traits/is_unsigned.h>
@@ -24,10 +28,55 @@ _LIBCPP_PUSH_MACROS
2428

2529
_LIBCPP_BEGIN_NAMESPACE_STD
2630

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+
2772
template <class _Tp>
2873
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
2974
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.
3076
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
3180
}
3281

3382
#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::__countr_zero(__mask_)));
168+
return __bit_iterator<_Cp, false>(__seg_, static_cast<unsigned>(std::__libcpp_ctz(__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::__countr_zero(__mask_)));
237+
return __bit_iterator<_Cp, true>(__seg_, static_cast<unsigned>(std::__libcpp_ctz(__mask_)));
238238
}
239239

240240
private:

libcxx/include/__charconv/to_chars_integral.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,9 @@ 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.
118-
return numeric_limits<_Tp>::digits - std::__countl_zero(__value | 1);
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);
119120
}
120121

121122
template <typename _Tp>
@@ -149,8 +150,9 @@ struct _LIBCPP_HIDDEN __integral<8> {
149150
template <typename _Tp>
150151
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
151152
// If value == 0 still need one digit. If the value != this has no
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;
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;
154156
}
155157

156158
template <typename _Tp>
@@ -184,8 +186,9 @@ struct _LIBCPP_HIDDEN __integral<16> {
184186
template <typename _Tp>
185187
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int __width(_Tp __value) _NOEXCEPT {
186188
// If value == 0 still need one digit. If the value != this has no
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;
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;
189192
}
190193

191194
template <typename _Tp>

libcxx/include/__charconv/traits.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,12 @@ 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 __countl_zero.
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.
4750
static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
48-
auto __t = (32 - std::__countl_zero(static_cast<type>(__v | 1))) * 1233 >> 12;
51+
auto __t = (32 - std::__libcpp_clz(static_cast<type>(__v | 1))) * 1233 >> 12;
4952
return __t - (__v < __itoa::__pow10_32[__t]) + 1;
5053
}
5154

@@ -66,9 +69,12 @@ struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(uin
6669
///
6770
/// The algorithm is based on
6871
/// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
69-
/// Instead of using IntegerLogBase2 it uses __countl_zero.
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.
7076
static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
71-
auto __t = (64 - std::__countl_zero(static_cast<type>(__v | 1))) * 1233 >> 12;
77+
auto __t = (64 - std::__libcpp_clz(static_cast<type>(__v | 1))) * 1233 >> 12;
7278
return __t - (__v < __itoa::__pow10_64[__t]) + 1;
7379
}
7480

@@ -90,12 +96,15 @@ struct _LIBCPP_HIDDEN __traits_base<_Tp, __enable_if_t<sizeof(_Tp) == sizeof(__u
9096
///
9197
/// The algorithm is based on
9298
/// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
93-
/// Instead of using IntegerLogBase2 it uses __countl_zero.
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.
94103
static _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI int __width(_Tp __v) {
95104
_LIBCPP_ASSERT_INTERNAL(
96105
__v > numeric_limits<uint64_t>::max(), "The optimizations for this algorithm fail when this isn't true.");
97106
// There's always a bit set in the upper 64-bits.
98-
auto __t = (128 - std::__countl_zero(static_cast<uint64_t>(__v >> 64))) * 1233 >> 12;
107+
auto __t = (128 - std::__libcpp_clz(static_cast<uint64_t>(__v >> 64))) * 1233 >> 12;
99108
_LIBCPP_ASSERT_INTERNAL(__t >= __itoa::__pow10_128_offset, "Index out of bounds");
100109
// __t is adjusted since the lookup table misses the lower entries.
101110
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 - std::__countl_zero(__n - 1)));
150+
return __n < 2 ? __n : (size_t(1) << (numeric_limits<size_t>::digits - __libcpp_clz(__n - 1)));
151151
}
152152

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

0 commit comments

Comments
 (0)