[llvm] [ADT] Add llvm::countr_zero_constexpr (PR #157391)

Kazu Hirata via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 8 00:06:17 PDT 2025


https://github.com/kazutakahirata created https://github.com/llvm/llvm-project/pull/157391

This commit introduces llvm::countr_zero_constexpr as a constexpr
version of llvm::countr_zero.

The existing llvm::countr_zero is not constexpr due to its use of
_BitScanForward.

I'm planning to use the new function in PointerLikeTypeTraits.h as a
replacement for ConstantLog2.


>From ffeea668cdbe7ac387fb802abce306217a0160cb Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Wed, 3 Sep 2025 12:13:41 -0700
Subject: [PATCH] [ADT] Add llvm::countr_zero_constexpr

This commit introduces llvm::countr_zero_constexpr as a constexpr
version of llvm::countr_zero.

The existing llvm::countr_zero is not constexpr due to its use of
_BitScanForward.

I'm planning to use the new function in PointerLikeTypeTraits.h as a
replacement for ConstantLog2.
---
 llvm/include/llvm/ADT/bit.h    | 36 ++++++++++++++++++++++------------
 llvm/unittests/ADT/BitTest.cpp | 20 +++++++++++++++++++
 2 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index d6e33c3e6133a..2649f08743ef9 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -154,6 +154,27 @@ template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
 /// Only unsigned integral types are allowed.
 ///
 /// Returns std::numeric_limits<T>::digits on an input of 0.
+template <typename T> [[nodiscard]] constexpr int countr_zero_constexpr(T Val) {
+  static_assert(std::is_unsigned_v<T>,
+                "Only unsigned integral types are allowed.");
+  if (!Val)
+    return std::numeric_limits<T>::digits;
+
+  // Use the bisection method.
+  unsigned ZeroBits = 0;
+  T Shift = std::numeric_limits<T>::digits >> 1;
+  T Mask = std::numeric_limits<T>::max() >> Shift;
+  while (Shift) {
+    if ((Val & Mask) == 0) {
+      Val >>= Shift;
+      ZeroBits |= Shift;
+    }
+    Shift >>= 1;
+    Mask >>= Shift;
+  }
+  return ZeroBits;
+}
+
 template <typename T> [[nodiscard]] int countr_zero(T Val) {
   static_assert(std::is_unsigned_v<T>,
                 "Only unsigned integral types are allowed.");
@@ -179,19 +200,8 @@ template <typename T> [[nodiscard]] int countr_zero(T Val) {
 #endif
   }
 
-  // Fall back to the bisection method.
-  unsigned ZeroBits = 0;
-  T Shift = std::numeric_limits<T>::digits >> 1;
-  T Mask = std::numeric_limits<T>::max() >> Shift;
-  while (Shift) {
-    if ((Val & Mask) == 0) {
-      Val >>= Shift;
-      ZeroBits |= Shift;
-    }
-    Shift >>= 1;
-    Mask >>= Shift;
-  }
-  return ZeroBits;
+  // Fall back to the constexpr implementation.
+  return countr_zero_constexpr(Val);
 }
 
 /// Count number of 0's from the most significant bit to the least
diff --git a/llvm/unittests/ADT/BitTest.cpp b/llvm/unittests/ADT/BitTest.cpp
index 2377ce3b78261..bc441df89f5ad 100644
--- a/llvm/unittests/ADT/BitTest.cpp
+++ b/llvm/unittests/ADT/BitTest.cpp
@@ -279,6 +279,26 @@ TEST(BitTest, CountlZero) {
   }
 }
 
+TEST(BitTest, CountrZeroConstexpr) {
+  constexpr uint8_t Z8 = 0;
+  constexpr uint16_t Z16 = 0;
+  constexpr uint32_t Z32 = 0;
+  constexpr uint64_t Z64 = 0;
+  static_assert(llvm::countr_zero_constexpr(Z8) == 8, "");
+  static_assert(llvm::countr_zero_constexpr(Z16) == 16, "");
+  static_assert(llvm::countr_zero_constexpr(Z32) == 32, "");
+  static_assert(llvm::countr_zero_constexpr(Z64) == 64, "");
+
+  constexpr uint8_t NZ8 = 42;
+  constexpr uint16_t NZ16 = 42;
+  constexpr uint32_t NZ32 = 42;
+  constexpr uint64_t NZ64 = 42;
+  static_assert(llvm::countr_zero_constexpr(NZ8) == 1, "");
+  static_assert(llvm::countr_zero_constexpr(NZ16) == 1, "");
+  static_assert(llvm::countr_zero_constexpr(NZ32) == 1, "");
+  static_assert(llvm::countr_zero_constexpr(NZ64) == 1, "");
+}
+
 TEST(BitTest, CountrZero) {
   uint8_t Z8 = 0;
   uint16_t Z16 = 0;



More information about the llvm-commits mailing list