[llvm] [ADT] Add bit_width_constexpr (PR #161775)
Kazu Hirata via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 2 21:49:09 PDT 2025
https://github.com/kazutakahirata created https://github.com/llvm/llvm-project/pull/161775
This patch adds llvm::bit_width_constexpr, a constexpr version of
llvm::bit_width.
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::bit_width_constexpr, we can easily replace its use with
std::bit_width.
This patch refactors a couple of places. Specifically:
- bitWidth in BitmaskEnum.h is replaced with the new function.
- bitsRequired in PointerUnion.h is redefined in terms of the new
function.
I've used Compiler Explorer to check the equivalence:
https://godbolt.org/z/1oKMK9Ez7
>From 13e266b1f67039aa6b0f4ec6b14549e58965f15f Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Wed, 17 Sep 2025 19:18:25 -0700
Subject: [PATCH] [ADT] Add bit_width_constexpr
This patch adds llvm::bit_width_constexpr, a constexpr version of
llvm::bit_width.
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::bit_width_constexpr, we can easily replace its use with
std::bit_width.
This patch refactors a couple of places. Specifically:
- bitWidth in BitmaskEnum.h is replaced with the new function.
- bitsRequired in PointerUnion.h is redefined in terms of the new
function.
I've used Compiler Explorer to check the equivalence:
https://godbolt.org/z/1oKMK9Ez7
---
llvm/include/llvm/ADT/BitmaskEnum.h | 7 ++-----
llvm/include/llvm/ADT/PointerUnion.h | 2 +-
llvm/include/llvm/ADT/bit.h | 17 +++++++++++++++++
llvm/unittests/ADT/BitTest.cpp | 23 +++++++++++++++++++++++
4 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/ADT/BitmaskEnum.h b/llvm/include/llvm/ADT/BitmaskEnum.h
index 7214f25b0aa10..d464cbcfcdffd 100644
--- a/llvm/include/llvm/ADT/BitmaskEnum.h
+++ b/llvm/include/llvm/ADT/BitmaskEnum.h
@@ -14,6 +14,7 @@
#include <utility>
#include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/bit.h"
#include "llvm/Support/MathExtras.h"
/// LLVM_MARK_AS_BITMASK_ENUM lets you opt in an individual enum type so you can
@@ -138,10 +139,6 @@ template <typename E> constexpr std::underlying_type_t<E> Underlying(E Val) {
return U;
}
-constexpr unsigned bitWidth(uint64_t Value) {
- return Value ? 1 + bitWidth(Value >> 1) : 0;
-}
-
template <typename E, typename = std::enable_if_t<is_bitmask_enum<E>::value>>
constexpr bool operator!(E Val) {
return Val == static_cast<E>(0);
@@ -220,7 +217,7 @@ e &operator>>=(e &lhs, e rhs) {
// Enable bitmask enums in namespace ::llvm and all nested namespaces.
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
template <typename E, typename = std::enable_if_t<is_bitmask_enum<E>::value>>
-constexpr unsigned BitWidth = BitmaskEnumDetail::bitWidth(
+constexpr unsigned BitWidth = llvm::bit_width_constexpr(
uint64_t{llvm::to_underlying(E::LLVM_BITMASK_LARGEST_ENUMERATOR)});
} // namespace llvm
diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h
index ca0e1ed3d49d1..7b66177373011 100644
--- a/llvm/include/llvm/ADT/PointerUnion.h
+++ b/llvm/include/llvm/ADT/PointerUnion.h
@@ -31,7 +31,7 @@ namespace pointer_union_detail {
/// Determine the number of bits required to store integers with values < n.
/// This is ceil(log2(n)).
constexpr int bitsRequired(unsigned n) {
- return n > 1 ? 1 + bitsRequired((n + 1) / 2) : 0;
+ return n == 0 ? 0 : llvm::bit_width_constexpr(n - 1);
}
template <typename... Ts> constexpr int lowBitsAvailable() {
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 67c0a1c3300fa..e419ea975c02e 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -292,6 +292,23 @@ template <typename T> [[nodiscard]] int bit_width(T Value) {
return std::numeric_limits<T>::digits - llvm::countl_zero(Value);
}
+/// Returns the number of bits needed to represent Value if Value is nonzero.
+/// Returns 0 otherwise.
+///
+/// A constexpr version of bit_width.
+///
+/// Ex. bit_width_constexpr(5) == 3.
+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;
+}
+
/// Returns the largest integral power of two no greater than Value if Value is
/// nonzero. Returns 0 otherwise.
///
diff --git a/llvm/unittests/ADT/BitTest.cpp b/llvm/unittests/ADT/BitTest.cpp
index 88ae36c44bdb9..eaed4e1fe327d 100644
--- a/llvm/unittests/ADT/BitTest.cpp
+++ b/llvm/unittests/ADT/BitTest.cpp
@@ -247,6 +247,29 @@ TEST(BitTest, BitWidth) {
EXPECT_EQ(64, llvm::bit_width(uint64_t(0xffffffffffffffffull)));
}
+TEST(BitTest, BitWidthConstexpr) {
+ static_assert(llvm::bit_width_constexpr(0u) == 0);
+ static_assert(llvm::bit_width_constexpr(1u) == 1);
+ static_assert(llvm::bit_width_constexpr(2u) == 2);
+ static_assert(llvm::bit_width_constexpr(3u) == 2);
+ static_assert(llvm::bit_width_constexpr(4u) == 3);
+ static_assert(llvm::bit_width_constexpr(5u) == 3);
+ static_assert(llvm::bit_width_constexpr(6u) == 3);
+ static_assert(llvm::bit_width_constexpr(7u) == 3);
+ static_assert(llvm::bit_width_constexpr(8u) == 4);
+
+ static_assert(llvm::bit_width_constexpr(255u) == 8);
+ static_assert(llvm::bit_width_constexpr(256u) == 9);
+ static_assert(llvm::bit_width_constexpr(257u) == 9);
+
+ static_assert(
+ llvm::bit_width_constexpr(std::numeric_limits<uint16_t>::max()) == 16);
+ static_assert(
+ llvm::bit_width_constexpr(std::numeric_limits<uint32_t>::max()) == 32);
+ static_assert(
+ llvm::bit_width_constexpr(std::numeric_limits<uint64_t>::max()) == 64);
+}
+
TEST(BitTest, CountlZero) {
uint8_t Z8 = 0;
uint16_t Z16 = 0;
More information about the llvm-commits
mailing list