Skip to content

Commit 6284041

Browse files
[ADT] Add llvm::countr_zero_constexpr (#164188)
This patch implements llvm::countr_zero_constexpr, a constexpr version of llvm::countr_zero, in terms of llvm::popcount while making llvm::popcount a constexpr function at the same time. The new function is intended to serve as a marker. When we switch to C++20, we will most likely go through functions in llvm/ADT/bit.h and replace them with their counterparts from <bit>. With llvm::countr_zero_constexpr, we can easily replace its use with std::countr_zero. This patch reimplements ConstantLog2 in terms of the new function.
1 parent 7e1f79c commit 6284041

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

llvm/include/llvm/ADT/bit.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
151151
/// Count the number of set bits in a value.
152152
/// Ex. popcount(0xF000F000) = 8
153153
/// Returns 0 if Value is zero.
154-
template <typename T> [[nodiscard]] inline int popcount(T Value) noexcept {
154+
template <typename T> [[nodiscard]] constexpr int popcount(T Value) noexcept {
155155
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integer type");
156156
static_assert(sizeof(T) <= 8, "T must be 8 bytes or less");
157157

@@ -178,7 +178,23 @@ template <typename T> [[nodiscard]] inline int popcount(T Value) noexcept {
178178
}
179179

180180
/// Count number of 0's from the least significant bit to the most
181-
/// stopping at the first 1.
181+
/// stopping at the first 1.
182+
///
183+
/// A constexpr version of countr_zero.
184+
///
185+
/// Only unsigned integral types are allowed.
186+
///
187+
/// Returns std::numeric_limits<T>::digits on an input of 0.
188+
template <typename T> [[nodiscard]] constexpr int countr_zero_constexpr(T Val) {
189+
static_assert(std::is_unsigned_v<T>,
190+
"Only unsigned integral types are allowed.");
191+
// "(Val & -Val) - 1" generates a mask with all bits set up to (but not
192+
// including) the least significant set bit of Val.
193+
return llvm::popcount(static_cast<std::make_unsigned_t<T>>((Val & -Val) - 1));
194+
}
195+
196+
/// Count number of 0's from the least significant bit to the most
197+
/// stopping at the first 1.
182198
///
183199
/// Only unsigned integral types are allowed.
184200
///
@@ -208,9 +224,7 @@ template <typename T> [[nodiscard]] int countr_zero(T Val) {
208224
#endif
209225
}
210226

211-
// Fallback to popcount. "(Val & -Val) - 1" is a bitmask with all bits below
212-
// the least significant 1 set.
213-
return llvm::popcount(static_cast<std::make_unsigned_t<T>>((Val & -Val) - 1));
227+
return countr_zero_constexpr(Val);
214228
}
215229

216230
/// Count number of 0's from the most significant bit to the least

llvm/include/llvm/Support/MathExtras.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,9 @@ inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
316316
/// Valid only for positive powers of two.
317317
template <size_t kValue> constexpr size_t ConstantLog2() {
318318
static_assert(llvm::isPowerOf2_64(kValue), "Value is not a valid power of 2");
319-
return 1 + ConstantLog2<kValue / 2>();
319+
return llvm::countr_zero_constexpr(kValue);
320320
}
321321

322-
template <> constexpr size_t ConstantLog2<1>() { return 0; }
323-
324322
template <size_t kValue>
325323
LLVM_DEPRECATED("Use ConstantLog2 instead", "ConstantLog2")
326324
constexpr size_t CTLog2() {

llvm/unittests/ADT/BitTest.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,28 @@ TEST(BitTest, BitCeilConstexpr) {
286286
static_assert(llvm::bit_ceil_constexpr(257u) == 512);
287287
}
288288

289+
TEST(BitTest, CountrZeroConstexpr) {
290+
static_assert(llvm::countr_zero_constexpr(0u) == 32);
291+
static_assert(llvm::countr_zero_constexpr(1u) == 0);
292+
static_assert(llvm::countr_zero_constexpr(2u) == 1);
293+
static_assert(llvm::countr_zero_constexpr(3u) == 0);
294+
static_assert(llvm::countr_zero_constexpr(4u) == 2);
295+
static_assert(llvm::countr_zero_constexpr(8u) == 3);
296+
static_assert(llvm::countr_zero_constexpr(0x80000000u) == 31);
297+
298+
static_assert(llvm::countr_zero_constexpr(0ull) == 64);
299+
static_assert(llvm::countr_zero_constexpr(1ull) == 0);
300+
static_assert(llvm::countr_zero_constexpr(0x100000000ull) == 32);
301+
static_assert(llvm::countr_zero_constexpr(0x8000000000000000ull) == 63);
302+
303+
static_assert(
304+
llvm::countr_zero_constexpr(std::numeric_limits<uint16_t>::max()) == 0);
305+
static_assert(
306+
llvm::countr_zero_constexpr(std::numeric_limits<uint32_t>::max()) == 0);
307+
static_assert(
308+
llvm::countr_zero_constexpr(std::numeric_limits<uint64_t>::max()) == 0);
309+
}
310+
289311
TEST(BitTest, CountlZero) {
290312
uint8_t Z8 = 0;
291313
uint16_t Z16 = 0;

0 commit comments

Comments
 (0)