Skip to content

Commit 32c7dc0

Browse files
[ADT] Simplify countr_zero and countl_zero with constexpr if (NFC) (#141517)
The high-level idea in countr_zero and countl_zero is to use the intrinsic if available and fall back otherwise, but the actual implementation is unnecessarily complicated. Specifically, without this patch, we are going through a long chain of macros to check the availability of _BitScanForward64: #if defined(__GNUC__) || defined(_MSC_VER) #if !defined(_MSC_VER) || defined(_M_X64) #if __has_builtin(__builtin_ctzll) || defined(__GNUC__) ... #elif defined(_MSC_VER) _BitScanForward64(...); #endif #endif #endif This basically says we can use _BitScanForward64 if: defined(_MSC_VER) && defined(_M_X64) This patch simplifies all this with "constexpr if" and consolidates the implementation into the body of countr_zero and countl_zero.
1 parent 7786ac0 commit 32c7dc0

File tree

1 file changed

+48
-96
lines changed
  • llvm/include/llvm/ADT

1 file changed

+48
-96
lines changed

llvm/include/llvm/ADT/bit.h

Lines changed: 48 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -148,141 +148,93 @@ template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
148148
return (Value != 0) && ((Value & (Value - 1)) == 0);
149149
}
150150

151-
namespace detail {
152-
template <typename T, std::size_t SizeOfT> struct TrailingZerosCounter {
153-
static unsigned count(T Val) {
154-
if (!Val)
155-
return std::numeric_limits<T>::digits;
156-
if (Val & 0x1)
157-
return 0;
158-
159-
// Bisection method.
160-
unsigned ZeroBits = 0;
161-
T Shift = std::numeric_limits<T>::digits >> 1;
162-
T Mask = std::numeric_limits<T>::max() >> Shift;
163-
while (Shift) {
164-
if ((Val & Mask) == 0) {
165-
Val >>= Shift;
166-
ZeroBits |= Shift;
167-
}
168-
Shift >>= 1;
169-
Mask >>= Shift;
170-
}
171-
return ZeroBits;
172-
}
173-
};
174-
175-
#if defined(__GNUC__) || defined(_MSC_VER)
176-
template <typename T> struct TrailingZerosCounter<T, 4> {
177-
static unsigned count(T Val) {
178-
if (Val == 0)
179-
return 32;
151+
/// Count number of 0's from the least significant bit to the most
152+
/// stopping at the first 1.
153+
///
154+
/// Only unsigned integral types are allowed.
155+
///
156+
/// Returns std::numeric_limits<T>::digits on an input of 0.
157+
template <typename T> [[nodiscard]] int countr_zero(T Val) {
158+
static_assert(std::is_unsigned_v<T>,
159+
"Only unsigned integral types are allowed.");
160+
if (!Val)
161+
return std::numeric_limits<T>::digits;
180162

163+
// Use the intrinsic if available.
164+
if constexpr (sizeof(T) == 4) {
181165
#if __has_builtin(__builtin_ctz) || defined(__GNUC__)
182166
return __builtin_ctz(Val);
183167
#elif defined(_MSC_VER)
184168
unsigned long Index;
185169
_BitScanForward(&Index, Val);
186170
return Index;
187171
#endif
188-
}
189-
};
190-
191-
#if !defined(_MSC_VER) || defined(_M_X64)
192-
template <typename T> struct TrailingZerosCounter<T, 8> {
193-
static unsigned count(T Val) {
194-
if (Val == 0)
195-
return 64;
196-
172+
} else if constexpr (sizeof(T) == 8) {
197173
#if __has_builtin(__builtin_ctzll) || defined(__GNUC__)
198174
return __builtin_ctzll(Val);
199-
#elif defined(_MSC_VER)
175+
#elif defined(_MSC_VER) && defined(_M_X64)
200176
unsigned long Index;
201177
_BitScanForward64(&Index, Val);
202178
return Index;
203179
#endif
204180
}
205-
};
206-
#endif
207-
#endif
208-
} // namespace detail
209181

210-
/// Count number of 0's from the least significant bit to the most
182+
// Fall back to the bisection method.
183+
unsigned ZeroBits = 0;
184+
T Shift = std::numeric_limits<T>::digits >> 1;
185+
T Mask = std::numeric_limits<T>::max() >> Shift;
186+
while (Shift) {
187+
if ((Val & Mask) == 0) {
188+
Val >>= Shift;
189+
ZeroBits |= Shift;
190+
}
191+
Shift >>= 1;
192+
Mask >>= Shift;
193+
}
194+
return ZeroBits;
195+
}
196+
197+
/// Count number of 0's from the most significant bit to the least
211198
/// stopping at the first 1.
212199
///
213200
/// Only unsigned integral types are allowed.
214201
///
215202
/// Returns std::numeric_limits<T>::digits on an input of 0.
216-
template <typename T> [[nodiscard]] int countr_zero(T Val) {
203+
template <typename T> [[nodiscard]] int countl_zero(T Val) {
217204
static_assert(std::is_unsigned_v<T>,
218205
"Only unsigned integral types are allowed.");
219-
return llvm::detail::TrailingZerosCounter<T, sizeof(T)>::count(Val);
220-
}
221-
222-
namespace detail {
223-
template <typename T, std::size_t SizeOfT> struct LeadingZerosCounter {
224-
static unsigned count(T Val) {
225-
if (!Val)
226-
return std::numeric_limits<T>::digits;
227-
228-
// Bisection method.
229-
unsigned ZeroBits = 0;
230-
for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
231-
T Tmp = Val >> Shift;
232-
if (Tmp)
233-
Val = Tmp;
234-
else
235-
ZeroBits |= Shift;
236-
}
237-
return ZeroBits;
238-
}
239-
};
240-
241-
#if defined(__GNUC__) || defined(_MSC_VER)
242-
template <typename T> struct LeadingZerosCounter<T, 4> {
243-
static unsigned count(T Val) {
244-
if (Val == 0)
245-
return 32;
206+
if (!Val)
207+
return std::numeric_limits<T>::digits;
246208

209+
// Use the intrinsic if available.
210+
if constexpr (sizeof(T) == 4) {
247211
#if __has_builtin(__builtin_clz) || defined(__GNUC__)
248212
return __builtin_clz(Val);
249213
#elif defined(_MSC_VER)
250214
unsigned long Index;
251215
_BitScanReverse(&Index, Val);
252216
return Index ^ 31;
253217
#endif
254-
}
255-
};
256-
257-
#if !defined(_MSC_VER) || defined(_M_X64)
258-
template <typename T> struct LeadingZerosCounter<T, 8> {
259-
static unsigned count(T Val) {
260-
if (Val == 0)
261-
return 64;
262-
218+
} else if constexpr (sizeof(T) == 8) {
263219
#if __has_builtin(__builtin_clzll) || defined(__GNUC__)
264220
return __builtin_clzll(Val);
265-
#elif defined(_MSC_VER)
221+
#elif defined(_MSC_VER) && defined(_M_X64)
266222
unsigned long Index;
267223
_BitScanReverse64(&Index, Val);
268224
return Index ^ 63;
269225
#endif
270226
}
271-
};
272-
#endif
273-
#endif
274-
} // namespace detail
275227

276-
/// Count number of 0's from the most significant bit to the least
277-
/// stopping at the first 1.
278-
///
279-
/// Only unsigned integral types are allowed.
280-
///
281-
/// Returns std::numeric_limits<T>::digits on an input of 0.
282-
template <typename T> [[nodiscard]] int countl_zero(T Val) {
283-
static_assert(std::is_unsigned_v<T>,
284-
"Only unsigned integral types are allowed.");
285-
return llvm::detail::LeadingZerosCounter<T, sizeof(T)>::count(Val);
228+
// Fall back to the bisection method.
229+
unsigned ZeroBits = 0;
230+
for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
231+
T Tmp = Val >> Shift;
232+
if (Tmp)
233+
Val = Tmp;
234+
else
235+
ZeroBits |= Shift;
236+
}
237+
return ZeroBits;
286238
}
287239

288240
/// Count the number of ones from the most significant bit to the first

0 commit comments

Comments
 (0)