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+
2762template <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
0 commit comments