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