[llvm] [ADT] implement countl_zero_constexpr and reuse it for countl_zero & bit_width_constexpr (PR #189111)
Max Graey via llvm-commits
llvm-commits at lists.llvm.org
Mon Mar 30 07:39:27 PDT 2026
https://github.com/MaxGraey updated https://github.com/llvm/llvm-project/pull/189111
>From 8459ed648e485798ac853616fc221099688dc168 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 27 Mar 2026 22:52:18 +0200
Subject: [PATCH 1/8] add countl_zero_constexpr and reuse it in countl_zero
---
llvm/include/llvm/ADT/bit.h | 38 +++++++++++++++++++++++++++----------
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 5971b75045b6b..14244efc7e230 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -227,6 +227,33 @@ template <typename T> [[nodiscard]] int countr_zero(T Val) {
return countr_zero_constexpr(Val);
}
+/// Count number of 0's from the most significant bit to the least
+/// stopping at the first 1.
+///
+/// A constexpr version of countl_zero.
+///
+/// Only unsigned integral types are allowed.
+///
+/// Returns std::numeric_limits<T>::digits on an input of 0.
+template <typename T> [[nodiscard]] constexpr int countl_zero_constexpr(T Val) {
+ static_assert(std::is_unsigned_v<T>,
+ "Only unsigned integral types are allowed.");
+
+ constexpr int Digits = std::numeric_limits<T>::digits;
+
+ if (!Val)
+ return Digits;
+
+ Val |= (Val >> 1);
+ Val |= (Val >> 2);
+ Val |= (Val >> 4);
+ if constexpr (Digits > 8) Val |= (Val >> 8);
+ if constexpr (Digits > 16) Val |= (Val >> 16);
+ if constexpr (Digits > 32) Val |= (Val >> 32);
+
+ return Digits - llvm::popcount(Val);
+}
+
/// Count number of 0's from the most significant bit to the least
/// stopping at the first 1.
///
@@ -258,16 +285,7 @@ template <typename T> [[nodiscard]] int countl_zero(T Val) {
#endif
}
- // Fall back to the bisection method.
- unsigned ZeroBits = 0;
- for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
- T Tmp = Val >> Shift;
- if (Tmp)
- Val = Tmp;
- else
- ZeroBits |= Shift;
- }
- return ZeroBits;
+ return countl_zero_constexpr(Val);
}
/// Count the number of ones from the most significant bit to the first
>From 78f33f8a2088f7d8050a2e1ce338d678cd37f445 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 27 Mar 2026 22:59:10 +0200
Subject: [PATCH 2/8] fix format
---
llvm/include/llvm/ADT/bit.h | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 14244efc7e230..f0f655bb55cf3 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -247,10 +247,15 @@ template <typename T> [[nodiscard]] constexpr int countl_zero_constexpr(T Val) {
Val |= (Val >> 1);
Val |= (Val >> 2);
Val |= (Val >> 4);
- if constexpr (Digits > 8) Val |= (Val >> 8);
- if constexpr (Digits > 16) Val |= (Val >> 16);
- if constexpr (Digits > 32) Val |= (Val >> 32);
-
+ if constexpr (Digits > 8) {
+ Val |= (Val >> 8);
+ }
+ if constexpr (Digits > 16) {
+ Val |= (Val >> 16);
+ }
+ if constexpr (Digits > 32) {
+ Val |= (Val >> 32);
+ }
return Digits - llvm::popcount(Val);
}
>From b6253f2045ae980c6798ce64e0b2c7e94c54a6c3 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Fri, 27 Mar 2026 23:16:26 +0200
Subject: [PATCH 3/8] also reuse for bit_width_constexpr
---
llvm/include/llvm/ADT/bit.h | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index f0f655bb55cf3..160231c12574f 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -338,12 +338,7 @@ template <typename T> [[nodiscard]] int bit_width(T Value) {
template <typename T> [[nodiscard]] constexpr int bit_width_constexpr(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
- int Width = 0;
- while (Value > 0) {
- Value >>= 1;
- ++Width;
- }
- return Width;
+ return std::numeric_limits<T>::digits - llvm::countl_zero_constexpr(Value);
}
/// Returns the largest integral power of two no greater than Value if Value is
>From 7f790812cbce0f242cb9c616d40392c676c405a0 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 28 Mar 2026 02:45:02 +0200
Subject: [PATCH 4/8] also allow small types for countl_zero
---
llvm/include/llvm/ADT/bit.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 160231c12574f..892a82eb2c405 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -272,7 +272,7 @@ template <typename T> [[nodiscard]] int countl_zero(T Val) {
return std::numeric_limits<T>::digits;
// Use the intrinsic if available.
- if constexpr (sizeof(T) == 4) {
+ if constexpr (sizeof(T) <= 4) {
#if __has_builtin(__builtin_clz) || defined(__GNUC__)
return __builtin_clz(Val);
#elif defined(_MSC_VER)
>From bf0d5d37075fbfd119cf75a54c6ebf3178a33989 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 28 Mar 2026 03:36:56 +0200
Subject: [PATCH 5/8] fix
---
llvm/include/llvm/ADT/bit.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 892a82eb2c405..099cc03ae673a 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -273,12 +273,14 @@ template <typename T> [[nodiscard]] int countl_zero(T Val) {
// Use the intrinsic if available.
if constexpr (sizeof(T) <= 4) {
+ constexpr int ExtraBits =
+ std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits;
#if __has_builtin(__builtin_clz) || defined(__GNUC__)
- return __builtin_clz(Val);
+ return __builtin_clz(Val) - ExtraBits;
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanReverse(&Index, Val);
- return Index ^ 31;
+ return static_cast<int>((std::numeric_limits<T>::digits - 1) - Index);
#endif
} else if constexpr (sizeof(T) == 8) {
#if __has_builtin(__builtin_clzll) || defined(__GNUC__)
>From 0a5444bd515db86f521ab7121e101c19ce66a3d7 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 28 Mar 2026 03:41:26 +0200
Subject: [PATCH 6/8] fix format
---
llvm/include/llvm/ADT/bit.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 099cc03ae673a..8b7dab5e4c576 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -274,7 +274,7 @@ template <typename T> [[nodiscard]] int countl_zero(T Val) {
// Use the intrinsic if available.
if constexpr (sizeof(T) <= 4) {
constexpr int ExtraBits =
- std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits;
+ std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits;
#if __has_builtin(__builtin_clz) || defined(__GNUC__)
return __builtin_clz(Val) - ExtraBits;
#elif defined(_MSC_VER)
>From b41daff4babe4f232c286747e664b0aef18c2463 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Sat, 28 Mar 2026 04:04:56 +0200
Subject: [PATCH 7/8] refactoring
---
llvm/include/llvm/ADT/bit.h | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 8b7dab5e4c576..2bbc4a03be57e 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -239,24 +239,24 @@ template <typename T> [[nodiscard]] constexpr int countl_zero_constexpr(T Val) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
- constexpr int Digits = std::numeric_limits<T>::digits;
+ constexpr int BitWidth = std::numeric_limits<T>::digits;
if (!Val)
- return Digits;
+ return BitWidth;
Val |= (Val >> 1);
Val |= (Val >> 2);
Val |= (Val >> 4);
- if constexpr (Digits > 8) {
+ if constexpr (BitWidth > 8) {
Val |= (Val >> 8);
}
- if constexpr (Digits > 16) {
+ if constexpr (BitWidth > 16) {
Val |= (Val >> 16);
}
- if constexpr (Digits > 32) {
+ if constexpr (BitWidth > 32) {
Val |= (Val >> 32);
}
- return Digits - llvm::popcount(Val);
+ return BitWidth - llvm::popcount(Val);
}
/// Count number of 0's from the most significant bit to the least
@@ -268,19 +268,21 @@ template <typename T> [[nodiscard]] constexpr int countl_zero_constexpr(T Val) {
template <typename T> [[nodiscard]] int countl_zero(T Val) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
+
+ constexpr int BitWidth = std::numeric_limits<T>::digits;
+
if (!Val)
- return std::numeric_limits<T>::digits;
+ return BitWidth;
// Use the intrinsic if available.
if constexpr (sizeof(T) <= 4) {
- constexpr int ExtraBits =
- std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits;
#if __has_builtin(__builtin_clz) || defined(__GNUC__)
- return __builtin_clz(Val) - ExtraBits;
+ constexpr int Padding = std::numeric_limits<uint32_t>::digits - BitWidth;
+ return __builtin_clz(Val) - Padding;
#elif defined(_MSC_VER)
unsigned long Index;
_BitScanReverse(&Index, Val);
- return static_cast<int>((std::numeric_limits<T>::digits - 1) - Index);
+ return static_cast<int>((BitWidth - 1) - Index);
#endif
} else if constexpr (sizeof(T) == 8) {
#if __has_builtin(__builtin_clzll) || defined(__GNUC__)
>From e08e3a13a8539d5e81f1d955771df2a5d58c48c2 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Mon, 30 Mar 2026 17:39:09 +0300
Subject: [PATCH 8/8] use bisection instead utilize popcount for
countl_zero_constexpr
---
llvm/include/llvm/ADT/bit.h | 26 ++++++++------------------
1 file changed, 8 insertions(+), 18 deletions(-)
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 2bbc4a03be57e..505be0f682678 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -238,25 +238,15 @@ template <typename T> [[nodiscard]] int countr_zero(T Val) {
template <typename T> [[nodiscard]] constexpr int countl_zero_constexpr(T Val) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
-
- constexpr int BitWidth = std::numeric_limits<T>::digits;
-
- if (!Val)
- return BitWidth;
-
- Val |= (Val >> 1);
- Val |= (Val >> 2);
- Val |= (Val >> 4);
- if constexpr (BitWidth > 8) {
- Val |= (Val >> 8);
- }
- if constexpr (BitWidth > 16) {
- Val |= (Val >> 16);
- }
- if constexpr (BitWidth > 32) {
- Val |= (Val >> 32);
+ unsigned ZeroBits = 0;
+ for (T Shift = std::numeric_limits<T>::digits >> 1; Shift; Shift >>= 1) {
+ T Tmp = Val >> Shift;
+ if (Tmp)
+ Val = Tmp;
+ else
+ ZeroBits |= Shift;
}
- return BitWidth - llvm::popcount(Val);
+ return ZeroBits;
}
/// Count number of 0's from the most significant bit to the least
More information about the llvm-commits
mailing list