blob: af45700f58cf6028d56e40915cd0937f84224c84 [file] [log] [blame]
Austin Schuhb4691e92020-12-31 12:37:18 -08001// Copyright 2020 The Abseil Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef ABSL_NUMERIC_INTERNAL_BITS_H_
16#define ABSL_NUMERIC_INTERNAL_BITS_H_
17
18#include <cstdint>
19#include <limits>
20#include <type_traits>
21
22// Clang on Windows has __builtin_clzll; otherwise we need to use the
23// windows intrinsic functions.
24#if defined(_MSC_VER) && !defined(__clang__)
25#include <intrin.h>
26#endif
27
28#include "absl/base/attributes.h"
29#include "absl/base/config.h"
30
31#if ABSL_HAVE_BUILTIN(__builtin_popcountl) && \
32 ABSL_HAVE_BUILTIN(__builtin_popcountll)
33#define ABSL_INTERNAL_CONSTEXPR_POPCOUNT constexpr
34#define ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT 1
35#else
36#define ABSL_INTERNAL_CONSTEXPR_POPCOUNT
37#define ABSL_INTERNAL_HAS_CONSTEXPR_POPCOUNT 0
38#endif
39
40#if ABSL_HAVE_BUILTIN(__builtin_clz) && ABSL_HAVE_BUILTIN(__builtin_clzll)
41#define ABSL_INTERNAL_CONSTEXPR_CLZ constexpr
42#define ABSL_INTERNAL_HAS_CONSTEXPR_CLZ 1
43#else
44#define ABSL_INTERNAL_CONSTEXPR_CLZ
45#define ABSL_INTERNAL_HAS_CONSTEXPR_CLZ 0
46#endif
47
48#if ABSL_HAVE_BUILTIN(__builtin_ctz) && ABSL_HAVE_BUILTIN(__builtin_ctzll)
49#define ABSL_INTERNAL_CONSTEXPR_CTZ constexpr
50#define ABSL_INTERNAL_HAS_CONSTEXPR_CTZ 1
51#else
52#define ABSL_INTERNAL_CONSTEXPR_CTZ
53#define ABSL_INTERNAL_HAS_CONSTEXPR_CTZ 0
54#endif
55
56namespace absl {
57ABSL_NAMESPACE_BEGIN
58namespace numeric_internal {
59
60constexpr bool IsPowerOf2(unsigned int x) noexcept {
61 return x != 0 && (x & (x - 1)) == 0;
62}
63
64template <class T>
65ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_ALWAYS_INLINE constexpr T RotateRight(
66 T x, int s) noexcept {
67 static_assert(std::is_unsigned<T>::value, "T must be unsigned");
68 static_assert(IsPowerOf2(std::numeric_limits<T>::digits),
69 "T must have a power-of-2 size");
70
71 return static_cast<T>(x >> (s & (std::numeric_limits<T>::digits - 1))) |
72 static_cast<T>(x << ((-s) & (std::numeric_limits<T>::digits - 1)));
73}
74
75template <class T>
76ABSL_MUST_USE_RESULT ABSL_ATTRIBUTE_ALWAYS_INLINE constexpr T RotateLeft(
77 T x, int s) noexcept {
78 static_assert(std::is_unsigned<T>::value, "T must be unsigned");
79 static_assert(IsPowerOf2(std::numeric_limits<T>::digits),
80 "T must have a power-of-2 size");
81
82 return static_cast<T>(x << (s & (std::numeric_limits<T>::digits - 1))) |
83 static_cast<T>(x >> ((-s) & (std::numeric_limits<T>::digits - 1)));
84}
85
86ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int
87Popcount32(uint32_t x) noexcept {
88#if ABSL_HAVE_BUILTIN(__builtin_popcount)
89 static_assert(sizeof(unsigned int) == sizeof(x),
90 "__builtin_popcount does not take 32-bit arg");
91 return __builtin_popcount(x);
92#else
93 x -= ((x >> 1) & 0x55555555);
94 x = ((x >> 2) & 0x33333333) + (x & 0x33333333);
95 return static_cast<int>((((x + (x >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24);
96#endif
97}
98
99ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int
100Popcount64(uint64_t x) noexcept {
101#if ABSL_HAVE_BUILTIN(__builtin_popcountll)
102 static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int)
103 "__builtin_popcount does not take 64-bit arg");
104 return __builtin_popcountll(x);
105#else
106 x -= (x >> 1) & 0x5555555555555555ULL;
107 x = ((x >> 2) & 0x3333333333333333ULL) + (x & 0x3333333333333333ULL);
108 return static_cast<int>(
109 (((x + (x >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);
110#endif
111}
112
113template <class T>
114ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_POPCOUNT inline int
115Popcount(T x) noexcept {
116 static_assert(std::is_unsigned<T>::value, "T must be unsigned");
117 static_assert(IsPowerOf2(std::numeric_limits<T>::digits),
118 "T must have a power-of-2 size");
119 static_assert(sizeof(x) <= sizeof(uint64_t), "T is too large");
120 return sizeof(x) <= sizeof(uint32_t) ? Popcount32(x) : Popcount64(x);
121}
122
123ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int
124CountLeadingZeroes32(uint32_t x) {
125#if ABSL_HAVE_BUILTIN(__builtin_clz)
126 // Use __builtin_clz, which uses the following instructions:
127 // x86: bsr, lzcnt
128 // ARM64: clz
129 // PPC: cntlzd
130
131 static_assert(sizeof(unsigned int) == sizeof(x),
132 "__builtin_clz does not take 32-bit arg");
133 // Handle 0 as a special case because __builtin_clz(0) is undefined.
134 return x == 0 ? 32 : __builtin_clz(x);
135#elif defined(_MSC_VER) && !defined(__clang__)
136 unsigned long result = 0; // NOLINT(runtime/int)
137 if (_BitScanReverse(&result, x)) {
138 return 31 - result;
139 }
140 return 32;
141#else
142 int zeroes = 28;
143 if (x >> 16) {
144 zeroes -= 16;
145 x >>= 16;
146 }
147 if (x >> 8) {
148 zeroes -= 8;
149 x >>= 8;
150 }
151 if (x >> 4) {
152 zeroes -= 4;
153 x >>= 4;
154 }
155 return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[x] + zeroes;
156#endif
157}
158
159ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int
160CountLeadingZeroes16(uint16_t x) {
161#if ABSL_HAVE_BUILTIN(__builtin_clzs)
162 static_assert(sizeof(unsigned short) == sizeof(x), // NOLINT(runtime/int)
163 "__builtin_clzs does not take 16-bit arg");
164 return x == 0 ? 16 : __builtin_clzs(x);
165#else
166 return CountLeadingZeroes32(x) - 16;
167#endif
168}
169
170ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int
171CountLeadingZeroes64(uint64_t x) {
172#if ABSL_HAVE_BUILTIN(__builtin_clzll)
173 // Use __builtin_clzll, which uses the following instructions:
174 // x86: bsr, lzcnt
175 // ARM64: clz
176 // PPC: cntlzd
177 static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int)
178 "__builtin_clzll does not take 64-bit arg");
179
180 // Handle 0 as a special case because __builtin_clzll(0) is undefined.
181 return x == 0 ? 64 : __builtin_clzll(x);
182#elif defined(_MSC_VER) && !defined(__clang__) && \
183 (defined(_M_X64) || defined(_M_ARM64))
184 // MSVC does not have __buitin_clzll. Use _BitScanReverse64.
185 unsigned long result = 0; // NOLINT(runtime/int)
186 if (_BitScanReverse64(&result, x)) {
187 return 63 - result;
188 }
189 return 64;
190#elif defined(_MSC_VER) && !defined(__clang__)
191 // MSVC does not have __buitin_clzll. Compose two calls to _BitScanReverse
192 unsigned long result = 0; // NOLINT(runtime/int)
193 if ((x >> 32) &&
194 _BitScanReverse(&result, static_cast<unsigned long>(x >> 32))) {
195 return 31 - result;
196 }
197 if (_BitScanReverse(&result, static_cast<unsigned long>(x))) {
198 return 63 - result;
199 }
200 return 64;
201#else
202 int zeroes = 60;
203 if (x >> 32) {
204 zeroes -= 32;
205 x >>= 32;
206 }
207 if (x >> 16) {
208 zeroes -= 16;
209 x >>= 16;
210 }
211 if (x >> 8) {
212 zeroes -= 8;
213 x >>= 8;
214 }
215 if (x >> 4) {
216 zeroes -= 4;
217 x >>= 4;
218 }
219 return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[x] + zeroes;
220#endif
221}
222
223template <typename T>
224ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline int
225CountLeadingZeroes(T x) {
226 static_assert(std::is_unsigned<T>::value, "T must be unsigned");
227 static_assert(IsPowerOf2(std::numeric_limits<T>::digits),
228 "T must have a power-of-2 size");
229 static_assert(sizeof(T) <= sizeof(uint64_t), "T too large");
230 return sizeof(T) <= sizeof(uint16_t)
231 ? CountLeadingZeroes16(static_cast<uint16_t>(x)) -
232 (std::numeric_limits<uint16_t>::digits -
233 std::numeric_limits<T>::digits)
234 : (sizeof(T) <= sizeof(uint32_t)
235 ? CountLeadingZeroes32(static_cast<uint32_t>(x)) -
236 (std::numeric_limits<uint32_t>::digits -
237 std::numeric_limits<T>::digits)
238 : CountLeadingZeroes64(x));
239}
240
241ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int
242CountTrailingZeroesNonzero32(uint32_t x) {
243#if ABSL_HAVE_BUILTIN(__builtin_ctz)
244 static_assert(sizeof(unsigned int) == sizeof(x),
245 "__builtin_ctz does not take 32-bit arg");
246 return __builtin_ctz(x);
247#elif defined(_MSC_VER) && !defined(__clang__)
248 unsigned long result = 0; // NOLINT(runtime/int)
249 _BitScanForward(&result, x);
250 return result;
251#else
252 int c = 31;
253 x &= ~x + 1;
254 if (x & 0x0000FFFF) c -= 16;
255 if (x & 0x00FF00FF) c -= 8;
256 if (x & 0x0F0F0F0F) c -= 4;
257 if (x & 0x33333333) c -= 2;
258 if (x & 0x55555555) c -= 1;
259 return c;
260#endif
261}
262
263ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int
264CountTrailingZeroesNonzero64(uint64_t x) {
265#if ABSL_HAVE_BUILTIN(__builtin_ctzll)
266 static_assert(sizeof(unsigned long long) == sizeof(x), // NOLINT(runtime/int)
267 "__builtin_ctzll does not take 64-bit arg");
268 return __builtin_ctzll(x);
269#elif defined(_MSC_VER) && !defined(__clang__) && \
270 (defined(_M_X64) || defined(_M_ARM64))
271 unsigned long result = 0; // NOLINT(runtime/int)
272 _BitScanForward64(&result, x);
273 return result;
274#elif defined(_MSC_VER) && !defined(__clang__)
275 unsigned long result = 0; // NOLINT(runtime/int)
276 if (static_cast<uint32_t>(x) == 0) {
277 _BitScanForward(&result, static_cast<unsigned long>(x >> 32));
278 return result + 32;
279 }
280 _BitScanForward(&result, static_cast<unsigned long>(x));
281 return result;
282#else
283 int c = 63;
284 x &= ~x + 1;
285 if (x & 0x00000000FFFFFFFF) c -= 32;
286 if (x & 0x0000FFFF0000FFFF) c -= 16;
287 if (x & 0x00FF00FF00FF00FF) c -= 8;
288 if (x & 0x0F0F0F0F0F0F0F0F) c -= 4;
289 if (x & 0x3333333333333333) c -= 2;
290 if (x & 0x5555555555555555) c -= 1;
291 return c;
292#endif
293}
294
295ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int
296CountTrailingZeroesNonzero16(uint16_t x) {
297#if ABSL_HAVE_BUILTIN(__builtin_ctzs)
298 static_assert(sizeof(unsigned short) == sizeof(x), // NOLINT(runtime/int)
299 "__builtin_ctzs does not take 16-bit arg");
300 return __builtin_ctzs(x);
301#else
302 return CountTrailingZeroesNonzero32(x);
303#endif
304}
305
306template <class T>
307ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CTZ inline int
308CountTrailingZeroes(T x) noexcept {
309 static_assert(std::is_unsigned<T>::value, "T must be unsigned");
310 static_assert(IsPowerOf2(std::numeric_limits<T>::digits),
311 "T must have a power-of-2 size");
312 static_assert(sizeof(T) <= sizeof(uint64_t), "T too large");
313 return x == 0 ? std::numeric_limits<T>::digits
314 : (sizeof(T) <= sizeof(uint16_t)
315 ? CountTrailingZeroesNonzero16(static_cast<uint16_t>(x))
316 : (sizeof(T) <= sizeof(uint32_t)
317 ? CountTrailingZeroesNonzero32(
318 static_cast<uint32_t>(x))
319 : CountTrailingZeroesNonzero64(x)));
320}
321
322// If T is narrower than unsigned, T{1} << bit_width will be promoted. We
323// want to force it to wraparound so that bit_ceil of an invalid value are not
324// core constant expressions.
325template <class T>
326ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline
327 typename std::enable_if<std::is_unsigned<T>::value, T>::type
328 BitCeilPromotionHelper(T x, T promotion) {
329 return (T{1} << (x + promotion)) >> promotion;
330}
331
332template <class T>
333ABSL_ATTRIBUTE_ALWAYS_INLINE ABSL_INTERNAL_CONSTEXPR_CLZ inline
334 typename std::enable_if<std::is_unsigned<T>::value, T>::type
335 BitCeilNonPowerOf2(T x) {
336 // If T is narrower than unsigned, it undergoes promotion to unsigned when we
337 // shift. We calcualte the number of bits added by the wider type.
338 return BitCeilPromotionHelper(
339 static_cast<T>(std::numeric_limits<T>::digits - CountLeadingZeroes(x)),
340 T{sizeof(T) >= sizeof(unsigned) ? 0
341 : std::numeric_limits<unsigned>::digits -
342 std::numeric_limits<T>::digits});
343}
344
345} // namespace numeric_internal
346ABSL_NAMESPACE_END
347} // namespace absl
348
349#endif // ABSL_NUMERIC_INTERNAL_BITS_H_