Skip to content

Conversation

kazutakahirata
Copy link
Contributor

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.

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.
@llvmbot
Copy link
Member

llvmbot commented Sep 8, 2025

@llvm/pr-subscribers-llvm-adt

Author: Kazu Hirata (kazutakahirata)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/157391.diff

2 Files Affected:

  • (modified) llvm/include/llvm/ADT/bit.h (+23-13)
  • (modified) llvm/unittests/ADT/BitTest.cpp (+20)
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;

@kazutakahirata
Copy link
Contributor Author

I'll drop this. We can use CTLog2 from MathExtras.h in PointerLikeTypeTraits.h.

@kazutakahirata kazutakahirata deleted the cleanup_20250907_ADT_bit_countr_zero_constexpr branch September 10, 2025 02:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants