[llvm] Reapply "[ADT] Bitset: add shift operators, word accessors, and etc" (PR #195874)

Rahul Joshi via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 05:49:10 PDT 2026


https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/195874

>From 14abe6376e4b16d5c1ac85abe00128f7d68cd717 Mon Sep 17 00:00:00 2001
From: Jiachen Yuan <jiacheny at nvidia.com>
Date: Wed, 22 Apr 2026 03:42:10 +0000
Subject: [PATCH 1/4] [ADT] Bitset: add shift operators, word accessors, and
 etc

---
 llvm/include/llvm/ADT/Bitset.h    |  97 ++++++++++++++-
 llvm/unittests/ADT/BitsetTest.cpp | 198 ++++++++++++++++++++++++++++++
 2 files changed, 291 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/ADT/Bitset.h b/llvm/include/llvm/ADT/Bitset.h
index 9dc0f24b1d9f5..05daa0ec9a5db 100644
--- a/llvm/include/llvm/ADT/Bitset.h
+++ b/llvm/include/llvm/ADT/Bitset.h
@@ -18,6 +18,7 @@
 
 #include "llvm/ADT/bit.h"
 #include <array>
+#include <cassert>
 #include <climits>
 #include <cstdint>
 
@@ -51,8 +52,9 @@ template <unsigned NumBits> class Bitset {
 
   constexpr void maskLastWord() { Bits[getLastWordIndex()] &= RemainderMask; }
 
-protected:
-  constexpr Bitset(const std::array<uint64_t, (NumBits + 63) / 64> &B) {
+public:
+  explicit constexpr Bitset(
+      const std::array<uint64_t, (NumBits + 63) / 64> &B) {
     if constexpr (sizeof(BitWord) == sizeof(uint64_t)) {
       for (size_t I = 0; I != B.size(); ++I)
         Bits[I] = B[I];
@@ -70,8 +72,6 @@ template <unsigned NumBits> class Bitset {
     }
     maskLastWord();
   }
-
-public:
   constexpr Bitset() = default;
   constexpr Bitset(std::initializer_list<unsigned> Init) {
     for (auto I : Init)
@@ -194,6 +194,95 @@ template <unsigned NumBits> class Bitset {
     }
     return false;
   }
+
+  constexpr Bitset &operator<<=(unsigned N) {
+    if (N == 0)
+      return *this;
+    if (N >= NumBits) {
+      return *this = Bitset();
+    }
+    const unsigned WordShift = N / BitwordBits;
+    const unsigned BitShift = N % BitwordBits;
+    if (BitShift == 0) {
+      for (int I = NumWords - 1; I >= static_cast<int>(WordShift); --I)
+        Bits[I] = Bits[I - WordShift];
+    } else {
+      const unsigned CarryShift = BitwordBits - BitShift;
+      for (int I = NumWords - 1; I > static_cast<int>(WordShift); --I) {
+        Bits[I] = (Bits[I - WordShift] << BitShift) |
+                  (Bits[I - WordShift - 1] >> CarryShift);
+      }
+      Bits[WordShift] = Bits[0] << BitShift;
+    }
+    for (unsigned I = 0; I < WordShift; ++I)
+      Bits[I] = 0;
+    maskLastWord();
+    return *this;
+  }
+
+  constexpr Bitset operator<<(unsigned N) const {
+    Bitset Result(*this);
+    Result <<= N;
+    return Result;
+  }
+
+  constexpr Bitset &operator>>=(unsigned N) {
+    if (N == 0)
+      return *this;
+    if (N >= NumBits) {
+      return *this = Bitset();
+    }
+    const unsigned WordShift = N / BitwordBits;
+    const unsigned BitShift = N % BitwordBits;
+    if (BitShift == 0) {
+      for (unsigned I = 0; I < NumWords - WordShift; ++I)
+        Bits[I] = Bits[I + WordShift];
+    } else {
+      const unsigned CarryShift = BitwordBits - BitShift;
+      for (unsigned I = 0; I < NumWords - WordShift - 1; ++I) {
+        Bits[I] = (Bits[I + WordShift] >> BitShift) |
+                  (Bits[I + WordShift + 1] << CarryShift);
+      }
+      Bits[NumWords - WordShift - 1] = Bits[NumWords - 1] >> BitShift;
+    }
+    for (unsigned I = NumWords - WordShift; I < NumWords; ++I)
+      Bits[I] = 0;
+    maskLastWord();
+    return *this;
+  }
+
+  constexpr Bitset operator>>(unsigned N) const {
+    Bitset Result(*this);
+    Result >>= N;
+    return Result;
+  }
+
+  /// Return the I-th 64-bit word of the bitset, from least significant to most.
+  constexpr uint64_t getWord64(unsigned I) const {
+    assert(I < getNumWords64() && "Word index out of range");
+    if constexpr (BitwordBits == 64) {
+      return Bits[I];
+    } else {
+      static_assert(BitwordBits == 32, "Unsupported word size");
+      // When Bitword is 32-bit, for a valid I, the first word is always
+      // present, but the second may not be present.
+      uint64_t Lo = Bits[2 * I];
+      uint64_t Hi = (2 * I + 1 < NumWords) ? Bits[2 * I + 1] : 0;
+      return Lo | (Hi << 32);
+    }
+  }
+
+  /// Return the index of the highest set bit, or -1 if no bits are set.
+  constexpr int findLastSet() const {
+    for (int I = NumWords - 1; I >= 0; --I)
+      if (Bits[I] != 0)
+        return I * BitwordBits +
+               (BitwordBits - 1 - countl_zero_constexpr(Bits[I]));
+    return -1;
+  }
+
+  /// Return the number of 64-bit words needed to hold all bits.
+  static constexpr unsigned getNumWords64() { return (NumBits + 63) / 64; }
 };
 
 } // end namespace llvm
diff --git a/llvm/unittests/ADT/BitsetTest.cpp b/llvm/unittests/ADT/BitsetTest.cpp
index 678197e31a379..31a004c903b3a 100644
--- a/llvm/unittests/ADT/BitsetTest.cpp
+++ b/llvm/unittests/ADT/BitsetTest.cpp
@@ -294,4 +294,202 @@ TEST(BitsetTest, BitwiseOperators) {
                 TestXor128.test(127));
 }
 
+TEST(BitsetTest, ShiftOperators) {
+  // Test left shift.
+  static_assert((Bitset<64>({0}) << 10).test(10));
+  static_assert(!(Bitset<64>({0}) << 10).test(0));
+  static_assert((Bitset<64>({63}) << 1).none());
+  static_assert((Bitset<128>({0}) << 64).test(64));
+  static_assert((Bitset<128>({63}) << 1).test(64));
+  static_assert((Bitset<128>({127}) << 1).none());
+
+  // Test right shift.
+  static_assert((Bitset<64>({10}) >> 10).test(0));
+  static_assert(!(Bitset<64>({10}) >> 10).test(10));
+  static_assert((Bitset<64>({0}) >> 1).none());
+  static_assert((Bitset<128>({64}) >> 64).test(0));
+  static_assert((Bitset<128>({64}) >> 1).test(63));
+  static_assert((Bitset<128>({0}) >> 1).none());
+
+  // Test shift by 0.
+  static_assert((Bitset<64>({10, 20}) << 0) == Bitset<64>({10, 20}));
+  static_assert((Bitset<64>({10, 20}) >> 0) == Bitset<64>({10, 20}));
+
+  // Test shift by NumBits (clears all).
+  static_assert((Bitset<64>({0, 63}) << 64).none());
+  static_assert((Bitset<64>({0, 63}) >> 64).none());
+  static_assert((Bitset<128>({0, 127}) << 128).none());
+  static_assert((Bitset<128>({0, 127}) >> 128).none());
+}
+
+TEST(BitsetTest, GetNumWords64) {
+  static_assert(Bitset<1>::getNumWords64() == 1);
+  static_assert(Bitset<32>::getNumWords64() == 1);
+  static_assert(Bitset<64>::getNumWords64() == 1);
+  static_assert(Bitset<65>::getNumWords64() == 2);
+  static_assert(Bitset<96>::getNumWords64() == 2);
+  static_assert(Bitset<128>::getNumWords64() == 2);
+  static_assert(Bitset<129>::getNumWords64() == 3);
+}
+
+TEST(BitsetTest, GetWord) {
+  // Single-word bitset.
+  constexpr auto B64 = Bitset<64>(std::array<uint64_t, 1>{0xdeadbeefcafe1234});
+  static_assert(B64.getWord64(0) == 0xdeadbeefcafe1234);
+
+  // Multi-word bitset.
+  constexpr auto B128 = Bitset<128>(
+      std::array<uint64_t, 2>{0x1111222233334444, 0xaaaabbbbccccdddd});
+  static_assert(B128.getWord64(0) == 0x1111222233334444);
+  static_assert(B128.getWord64(1) == 0xaaaabbbbccccdddd);
+
+  // Partial last word — high bits should be masked off.
+  constexpr auto B96 = Bitset<96>(
+      std::array<uint64_t, 2>{0xffffffffffffffff, 0xffffffffffffffff});
+  static_assert(B96.getWord64(0) == 0xffffffffffffffff);
+  // Only lower 32 bits.
+  static_assert(B96.getWord64(1) == 0x00000000ffffffff);
+
+  // Empty bitset.
+  static_assert(Bitset<64>().getWord64(0) == 0);
+  static_assert(Bitset<128>().getWord64(0) == 0);
+  static_assert(Bitset<128>().getWord64(1) == 0);
+}
+
+TEST(BitsetTest, FindLastSet) {
+  // Empty bitset returns -1.
+  static_assert(Bitset<64>().findLastSet() == -1);
+  static_assert(Bitset<128>().findLastSet() == -1);
+
+  // Single bit set.
+  static_assert(Bitset<64>({0}).findLastSet() == 0);
+  static_assert(Bitset<64>({63}).findLastSet() == 63);
+  static_assert(Bitset<64>({31}).findLastSet() == 31);
+  static_assert(Bitset<128>({0}).findLastSet() == 0);
+  static_assert(Bitset<128>({64}).findLastSet() == 64);
+  static_assert(Bitset<128>({127}).findLastSet() == 127);
+
+  // Multiple bits — returns highest.
+  static_assert(Bitset<64>({0, 10, 50}).findLastSet() == 50);
+  static_assert(Bitset<128>({0, 63, 64, 100}).findLastSet() == 100);
+
+  // All bits set.
+  static_assert(Bitset<64>().set().findLastSet() == 63);
+  static_assert(Bitset<128>().set().findLastSet() == 127);
+  static_assert(Bitset<96>().set().findLastSet() == 95);
+
+  // Non-power-of-2 sizes.
+  static_assert(Bitset<33>({32}).findLastSet() == 32);
+  static_assert(Bitset<33>({0, 32}).findLastSet() == 32);
+  static_assert(Bitset<65>({64}).findLastSet() == 64);
+}
+
+TEST(BitsetTest, ShiftMultiWords) {
+  constexpr auto B192 = Bitset<192>({0, 64, 128});
+  static_assert((B192 << 1) == Bitset<192>({1, 65, 129}));
+  static_assert((B192 >> 1) == Bitset<192>({63, 127}));
+  static_assert((B192 << 64) == Bitset<192>({64, 128}));
+  static_assert((B192 >> 64) == Bitset<192>({0, 64}));
+  static_assert((Bitset<192>({63, 127}) << 1) == Bitset<192>({64, 128}));
+  static_assert((Bitset<192>({64, 128}) >> 1) == Bitset<192>({63, 127}));
+}
+
+TEST(BitsetTest, ShiftBoundaryBitShifts) {
+  static_assert((Bitset<128>({1}) << 63) == Bitset<128>({64}));
+  static_assert((Bitset<128>({64}) >> 63) == Bitset<128>({1}));
+  static_assert((Bitset<192>({1, 65}) << 63) == Bitset<192>({64, 128}));
+  // Shift by NumBits - 1.
+  static_assert((Bitset<64>({0}) << 63) == Bitset<64>({63}));
+  static_assert((Bitset<64>({63}) >> 63) == Bitset<64>({0}));
+  static_assert((Bitset<33>({0}) << 32) == Bitset<33>({32}));
+  // Full-width shift of a fully-set bitset loses exactly one bit.
+  static_assert((Bitset<128>().set() << 1).count() == 127);
+  static_assert((Bitset<128>().set() >> 1).count() == 127);
+  static_assert((Bitset<100>().set() >> 1).count() == 99);
+}
+
+TEST(BitsetTest, ShiftExcessAmount) {
+  static_assert((Bitset<64>().set() << 65).none());
+  static_assert((Bitset<64>().set() >> 200).none());
+  static_assert((Bitset<33>({0, 10, 32}) << 1000).none());
+  static_assert((Bitset<128>({0, 127}) >> 1000).none());
+  static_assert((Bitset<192>().set() << 193).none());
+}
+
+TEST(BitsetTest, ShiftAssignReturnsReference) {
+  constexpr Bitset<64> L = [] {
+    Bitset<64> X({0});
+    (X <<= 3) <<= 2;
+    return X;
+  }();
+  static_assert(L == Bitset<64>({5}));
+
+  constexpr Bitset<128> R = [] {
+    Bitset<128> X({100});
+    (X >>= 30) >>= 10;
+    return X;
+  }();
+  static_assert(R == Bitset<128>({60}));
+}
+
+TEST(BitsetTest, GetWordConsistencyWithTest) {
+  // For every set bit, getWord must report it in the expected 64-bit word.
+  constexpr auto B100 = Bitset<100>({0, 50, 64, 99});
+  static_assert((B100.getWord64(0) & 1) != 0);
+  static_assert((B100.getWord64(0) & (uint64_t(1) << 50)) != 0);
+  static_assert((B100.getWord64(1) & 1) != 0);
+  static_assert((B100.getWord64(1) & (uint64_t(1) << 35)) != 0);
+}
+
+TEST(BitsetTest, GetWordAfterMutation) {
+  // getWord reflects subsequent set / shift.
+  constexpr auto B = [] {
+    Bitset<128> X;
+    X.set(5).set(70);
+    return X;
+  }();
+  static_assert(B.getWord64(0) == (uint64_t(1) << 5));
+  static_assert(B.getWord64(1) == (uint64_t(1) << 6));
+
+  constexpr auto Shifted = Bitset<128>({5}) << 64;
+  static_assert(Shifted.getWord64(0) == 0);
+  static_assert(Shifted.getWord64(1) == (uint64_t(1) << 5));
+}
+
+TEST(BitsetTest, GetNumWordsMoreWidths) {
+  static_assert(Bitset<2>::getNumWords64() == 1);
+  static_assert(Bitset<192>::getNumWords64() == 3);
+  static_assert(Bitset<193>::getNumWords64() == 4);
+  static_assert(Bitset<256>::getNumWords64() == 4);
+}
+
+TEST(BitsetTest, FindLastSetSmallWidths) {
+  static_assert(Bitset<1>().findLastSet() == -1);
+  static_assert(Bitset<1>({0}).findLastSet() == 0);
+  static_assert(Bitset<2>({0, 1}).findLastSet() == 1);
+  static_assert(Bitset<32>({31}).findLastSet() == 31);
+  static_assert(Bitset<32>().set().findLastSet() == 31);
+}
+
+TEST(BitsetTest, FindLastSetMultiWordScan) {
+  static_assert(Bitset<192>({70}).findLastSet() == 70);
+  static_assert(Bitset<192>({64, 70, 127}).findLastSet() == 127);
+  static_assert(Bitset<192>({3}).findLastSet() == 3);
+  static_assert(Bitset<100>({99}).findLastSet() == 99);
+}
+
+TEST(BitsetTest, FindLastSetAfterMutation) {
+  constexpr auto A = Bitset<128>({0, 50, 100}).reset(100);
+  static_assert(A.findLastSet() == 50);
+
+  constexpr auto B = Bitset<64>({10}) << 20;
+  static_assert(B.findLastSet() == 30);
+
+  constexpr auto C = Bitset<64>({63}) >> 10;
+  static_assert(C.findLastSet() == 53);
+
+  constexpr auto D = Bitset<64>({63}) << 1;
+  static_assert(D.findLastSet() == -1);
+}
+
 } // namespace

>From 4f03b7a6f4e6c9382143dfcc74f10b83515c0d08 Mon Sep 17 00:00:00 2001
From: Jiachen Yuan <jiacheny at nvidia.com>
Date: Sun, 3 May 2026 22:58:36 +0000
Subject: [PATCH 2/4] refactor loops to be safer

---
 llvm/include/llvm/ADT/Bitset.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/ADT/Bitset.h b/llvm/include/llvm/ADT/Bitset.h
index 05daa0ec9a5db..393fa8f730768 100644
--- a/llvm/include/llvm/ADT/Bitset.h
+++ b/llvm/include/llvm/ADT/Bitset.h
@@ -204,11 +204,11 @@ template <unsigned NumBits> class Bitset {
     const unsigned WordShift = N / BitwordBits;
     const unsigned BitShift = N % BitwordBits;
     if (BitShift == 0) {
-      for (int I = NumWords - 1; I >= static_cast<int>(WordShift); --I)
+      for (unsigned I = NumWords; I-- > WordShift;)
         Bits[I] = Bits[I - WordShift];
     } else {
       const unsigned CarryShift = BitwordBits - BitShift;
-      for (int I = NumWords - 1; I > static_cast<int>(WordShift); --I) {
+      for (unsigned I = NumWords - 1; I > WordShift; --I) {
         Bits[I] = (Bits[I - WordShift] << BitShift) |
                   (Bits[I - WordShift - 1] >> CarryShift);
       }
@@ -274,7 +274,7 @@ template <unsigned NumBits> class Bitset {
 
   /// Return the index of the highest set bit, or -1 if no bits are set.
   constexpr int findLastSet() const {
-    for (int I = NumWords - 1; I >= 0; --I)
+    for (unsigned I = NumWords; I-- > 0;)
       if (Bits[I] != 0)
         return I * BitwordBits +
                (BitwordBits - 1 - countl_zero_constexpr(Bits[I]));

>From db1372ec1b07adc72b6400158f87d2fd54a54584 Mon Sep 17 00:00:00 2001
From: Jiachen Yuan <jiacheny at nvidia.com>
Date: Mon, 4 May 2026 05:44:55 +0000
Subject: [PATCH 3/4] address review feedback on shift/word APIs and tests

---
 llvm/include/llvm/ADT/Bitset.h    |  19 +-
 llvm/unittests/ADT/BitsetTest.cpp | 456 +++++++++++++++---------------
 2 files changed, 243 insertions(+), 232 deletions(-)

diff --git a/llvm/include/llvm/ADT/Bitset.h b/llvm/include/llvm/ADT/Bitset.h
index 393fa8f730768..38e9684df9714 100644
--- a/llvm/include/llvm/ADT/Bitset.h
+++ b/llvm/include/llvm/ADT/Bitset.h
@@ -198,14 +198,15 @@ template <unsigned NumBits> class Bitset {
   constexpr Bitset &operator<<=(unsigned N) {
     if (N == 0)
       return *this;
-    if (N >= NumBits) {
+    if (N >= NumBits)
       return *this = Bitset();
-    }
     const unsigned WordShift = N / BitwordBits;
     const unsigned BitShift = N % BitwordBits;
     if (BitShift == 0) {
-      for (unsigned I = NumWords; I-- > WordShift;)
+      for (unsigned I = NumWords; I > WordShift;) {
+        --I;
         Bits[I] = Bits[I - WordShift];
+      }
     } else {
       const unsigned CarryShift = BitwordBits - BitShift;
       for (unsigned I = NumWords - 1; I > WordShift; --I) {
@@ -229,9 +230,8 @@ template <unsigned NumBits> class Bitset {
   constexpr Bitset &operator>>=(unsigned N) {
     if (N == 0)
       return *this;
-    if (N >= NumBits) {
+    if (N >= NumBits)
       return *this = Bitset();
-    }
     const unsigned WordShift = N / BitwordBits;
     const unsigned BitShift = N % BitwordBits;
     if (BitShift == 0) {
@@ -258,6 +258,11 @@ template <unsigned NumBits> class Bitset {
   }
 
   /// Return the I-th 64-bit word of the bitset, from least significant to most.
+  ///
+  /// All words other than the last contain exactly 64 stored bits. The last
+  /// word (\p I == \c getNumWords64() - 1) may cover fewer than 64 stored bits
+  /// when \c NumBits is not a multiple of 64; in that case the unused high bits
+  /// are reported as 0.
   constexpr uint64_t getWord64(unsigned I) const {
     assert(I < getNumWords64() && "Word index out of range");
     if constexpr (BitwordBits == 64) {
@@ -274,10 +279,12 @@ template <unsigned NumBits> class Bitset {
 
   /// Return the index of the highest set bit, or -1 if no bits are set.
   constexpr int findLastSet() const {
-    for (unsigned I = NumWords; I-- > 0;)
+    for (unsigned I = NumWords; I > 0;) {
+      --I;
       if (Bits[I] != 0)
         return I * BitwordBits +
                (BitwordBits - 1 - countl_zero_constexpr(Bits[I]));
+    }
     return -1;
   }
 
diff --git a/llvm/unittests/ADT/BitsetTest.cpp b/llvm/unittests/ADT/BitsetTest.cpp
index 31a004c903b3a..fc5f2bf8e844d 100644
--- a/llvm/unittests/ADT/BitsetTest.cpp
+++ b/llvm/unittests/ADT/BitsetTest.cpp
@@ -14,59 +14,52 @@ using namespace llvm;
 namespace {
 
 template <unsigned NumBits>
-class TestBitsetUInt64Array : public Bitset<NumBits> {
-  static constexpr unsigned NumElts = (NumBits + 63) / 64;
-
-public:
-  TestBitsetUInt64Array(const std::array<uint64_t, NumElts> &B)
-      : Bitset<NumBits>(B) {}
-
-  bool verifyValue(const std::array<uint64_t, NumElts> &B) const {
-    for (unsigned I = 0; I != NumBits; ++I) {
-      bool ReferenceVal =
-          (B[(I / 64)] & (static_cast<uint64_t>(1) << (I % 64))) != 0;
-      if (ReferenceVal != this->test(I))
-        return false;
-    }
-
-    return true;
+bool verifyBitsetValue(const Bitset<NumBits> &Bits,
+                       const std::array<uint64_t, (NumBits + 63) / 64> &Ref) {
+  for (unsigned I = 0; I != NumBits; ++I) {
+    bool ReferenceVal =
+        (Ref[I / 64] & (static_cast<uint64_t>(1) << (I % 64))) != 0;
+    if (ReferenceVal != Bits.test(I))
+      return false;
   }
+  return true;
+}
 
-  void verifyStorageSize(size_t elements_64_bit, size_t elements_32_bit) {
-    if constexpr (sizeof(uintptr_t) == sizeof(uint64_t))
-      EXPECT_EQ(sizeof(*this), elements_64_bit * sizeof(uintptr_t));
-    else
-      EXPECT_EQ(sizeof(*this), elements_32_bit * sizeof(uintptr_t));
-  }
-};
+template <unsigned NumBits>
+void verifyBitsetStorageSize(size_t Elements64, size_t Elements32) {
+  if constexpr (sizeof(uintptr_t) == sizeof(uint64_t))
+    EXPECT_EQ(sizeof(Bitset<NumBits>), Elements64 * sizeof(uintptr_t));
+  else
+    EXPECT_EQ(sizeof(Bitset<NumBits>), Elements32 * sizeof(uintptr_t));
+}
 
 TEST(BitsetTest, Construction) {
   std::array<uint64_t, 2> TestVals = {0x123456789abcdef3, 0x1337d3a0b22c24};
-  TestBitsetUInt64Array<96> Test(TestVals);
-  EXPECT_TRUE(Test.verifyValue(TestVals));
-  Test.verifyStorageSize(2, 3);
+  Bitset<96> Test(TestVals);
+  EXPECT_TRUE(verifyBitsetValue(Test, TestVals));
+  verifyBitsetStorageSize<96>(2, 3);
 
-  TestBitsetUInt64Array<65> Test1(TestVals);
-  EXPECT_TRUE(Test1.verifyValue(TestVals));
-  Test1.verifyStorageSize(2, 3);
+  Bitset<65> Test1(TestVals);
+  EXPECT_TRUE(verifyBitsetValue(Test1, TestVals));
+  verifyBitsetStorageSize<65>(2, 3);
 
   std::array<uint64_t, 1> TestSingleVal = {0x12345678abcdef99};
 
-  TestBitsetUInt64Array<64> Test64(TestSingleVal);
-  EXPECT_TRUE(Test64.verifyValue(TestSingleVal));
-  Test64.verifyStorageSize(1, 2);
+  Bitset<64> Test64(TestSingleVal);
+  EXPECT_TRUE(verifyBitsetValue(Test64, TestSingleVal));
+  verifyBitsetStorageSize<64>(1, 2);
 
-  TestBitsetUInt64Array<30> Test30(TestSingleVal);
-  EXPECT_TRUE(Test30.verifyValue(TestSingleVal));
-  Test30.verifyStorageSize(1, 1);
+  Bitset<30> Test30(TestSingleVal);
+  EXPECT_TRUE(verifyBitsetValue(Test30, TestSingleVal));
+  verifyBitsetStorageSize<30>(1, 1);
 
-  TestBitsetUInt64Array<32> Test32(TestSingleVal);
-  EXPECT_TRUE(Test32.verifyValue(TestSingleVal));
-  Test32.verifyStorageSize(1, 1);
+  Bitset<32> Test32(TestSingleVal);
+  EXPECT_TRUE(verifyBitsetValue(Test32, TestSingleVal));
+  verifyBitsetStorageSize<32>(1, 1);
 
-  TestBitsetUInt64Array<33> Test33(TestSingleVal);
-  EXPECT_TRUE(Test33.verifyValue(TestSingleVal));
-  Test33.verifyStorageSize(1, 2);
+  Bitset<33> Test33(TestSingleVal);
+  EXPECT_TRUE(verifyBitsetValue(Test33, TestSingleVal));
+  verifyBitsetStorageSize<33>(1, 2);
 }
 
 TEST(BitsetTest, SetAndQuery) {
@@ -78,7 +71,7 @@ TEST(BitsetTest, SetAndQuery) {
   EXPECT_FALSE(A.none());
 
   static_assert(Bitset<64>().set().all());
-  static_assert(Bitset<33>().set().all());
+  EXPECT_TRUE(Bitset<33>().set().all());
 
   // Test set() with single bit.
   Bitset<64> B;
@@ -89,11 +82,11 @@ TEST(BitsetTest, SetAndQuery) {
   EXPECT_FALSE(B.test(15));
 
   static_assert(Bitset<64>().set(10).test(10));
-  static_assert(Bitset<64>().set(0).set(63).test(0) &&
-                Bitset<64>().set(0).set(63).test(63));
-  static_assert(Bitset<33>().set(32).test(32));
-  static_assert(Bitset<128>().set(64).set(127).test(64) &&
-                Bitset<128>().set(64).set(127).test(127));
+  EXPECT_TRUE(Bitset<64>().set(0).set(63).test(0));
+  EXPECT_TRUE(Bitset<64>().set(0).set(63).test(63));
+  EXPECT_TRUE(Bitset<33>().set(32).test(32));
+  EXPECT_TRUE(Bitset<128>().set(64).set(127).test(64));
+  EXPECT_TRUE(Bitset<128>().set(64).set(127).test(127));
 
   // Test reset() with single bit.
   Bitset<64> C({10, 20, 30});
@@ -103,9 +96,9 @@ TEST(BitsetTest, SetAndQuery) {
   EXPECT_TRUE(C.test(30));
 
   static_assert(!Bitset<64>({10, 20}).reset(10).test(10));
-  static_assert(Bitset<64>({10, 20}).reset(10).test(20));
-  static_assert(!Bitset<96>({31, 32, 63}).reset(32).test(32));
-  static_assert(Bitset<33>({0, 32}).reset(0).test(32));
+  EXPECT_TRUE(Bitset<64>({10, 20}).reset(10).test(20));
+  EXPECT_FALSE(Bitset<96>({31, 32, 63}).reset(32).test(32));
+  EXPECT_TRUE(Bitset<33>({0, 32}).reset(0).test(32));
 
   // Test flip() with single bit.
   Bitset<64> D({10, 20});
@@ -116,10 +109,10 @@ TEST(BitsetTest, SetAndQuery) {
   EXPECT_TRUE(D.test(30));
 
   static_assert(!Bitset<64>({10, 20}).flip(10).test(10));
-  static_assert(Bitset<64>({10, 20}).flip(30).test(30));
-  static_assert(Bitset<100>({50, 99}).flip(50).test(99) &&
-                !Bitset<100>({50, 99}).flip(50).test(50));
-  static_assert(Bitset<33>().flip(32).test(32));
+  EXPECT_TRUE(Bitset<64>({10, 20}).flip(30).test(30));
+  EXPECT_TRUE(Bitset<100>({50, 99}).flip(50).test(99));
+  EXPECT_FALSE(Bitset<100>({50, 99}).flip(50).test(50));
+  EXPECT_TRUE(Bitset<33>().flip(32).test(32));
 
   // Test operator[].
   Bitset<64> E({5, 15, 25});
@@ -128,9 +121,10 @@ TEST(BitsetTest, SetAndQuery) {
   EXPECT_TRUE(E[15]);
 
   static_assert(Bitset<64>({10, 20})[10]);
-  static_assert(!Bitset<64>({10, 20})[15]);
-  static_assert(Bitset<128>({127})[127]);
-  static_assert(Bitset<96>({63, 64})[63] && Bitset<96>({63, 64})[64]);
+  EXPECT_FALSE(Bitset<64>({10, 20})[15]);
+  EXPECT_TRUE(Bitset<128>({127})[127]);
+  EXPECT_TRUE(Bitset<96>({63, 64})[63]);
+  EXPECT_TRUE(Bitset<96>({63, 64})[64]);
 
   // Test size().
   EXPECT_EQ(A.size(), 64u);
@@ -138,14 +132,14 @@ TEST(BitsetTest, SetAndQuery) {
   EXPECT_EQ(F.size(), 33u);
 
   static_assert(Bitset<64>().size() == 64);
-  static_assert(Bitset<128>().size() == 128);
-  static_assert(Bitset<33>().size() == 33);
+  EXPECT_EQ(Bitset<128>().size(), 128u);
+  EXPECT_EQ(Bitset<33>().size(), 33u);
 
   // Test any() and none().
   static_assert(!Bitset<64>().any());
-  static_assert(Bitset<64>().none());
-  static_assert(Bitset<64>({10}).any());
-  static_assert(!Bitset<64>({10}).none());
+  EXPECT_TRUE(Bitset<64>().none());
+  EXPECT_TRUE(Bitset<64>({10}).any());
+  EXPECT_FALSE(Bitset<64>({10}).none());
 }
 
 TEST(BitsetTest, ComparisonOperators) {
@@ -157,12 +151,12 @@ TEST(BitsetTest, ComparisonOperators) {
   EXPECT_FALSE(A == C);
 
   static_assert(Bitset<64>({10, 20}) == Bitset<64>({10, 20}));
-  static_assert(Bitset<64>({10, 20}) != Bitset<64>({10, 21}));
+  EXPECT_TRUE(Bitset<64>({10, 20}) != Bitset<64>({10, 21}));
 
   // Test operator< (lexicographic comparison, bit 0 is least significant).
   static_assert(Bitset<64>({5, 11}) <
                 Bitset<64>({5, 10})); // At bit 10: A=0, B=1.
-  static_assert(!(Bitset<64>({5, 10}) < Bitset<64>({5, 10})));
+  EXPECT_FALSE(Bitset<64>({5, 10}) < Bitset<64>({5, 10}));
 }
 
 TEST(BitsetTest, BitwiseNot) {
@@ -173,8 +167,8 @@ TEST(BitsetTest, BitwiseNot) {
   EXPECT_TRUE(B.none());
 
   static_assert((~Bitset<64>()).all());
-  static_assert((~Bitset<64>().set()).none());
-  static_assert((~Bitset<33>().set()).none());
+  EXPECT_TRUE((~Bitset<64>().set()).none());
+  EXPECT_TRUE((~Bitset<33>().set()).none());
 }
 
 TEST(BitsetTest, BitwiseOperators) {
@@ -189,11 +183,11 @@ TEST(BitsetTest, BitwiseOperators) {
   EXPECT_EQ(Result1.count(), 2u);
 
   static_assert((Bitset<64>({10, 20}) & Bitset<64>({20, 30})).test(20));
-  static_assert(!(Bitset<64>({10, 20}) & Bitset<64>({20, 30})).test(10));
-  static_assert((Bitset<64>({10, 20}) & Bitset<64>({20, 30})).count() == 1);
-  static_assert(
-      (Bitset<96>({31, 32, 63, 64}) & Bitset<96>({32, 64, 95})).count() == 2);
-  static_assert((Bitset<33>({0, 32}) & Bitset<33>({32})).test(32));
+  EXPECT_FALSE((Bitset<64>({10, 20}) & Bitset<64>({20, 30})).test(10));
+  EXPECT_EQ((Bitset<64>({10, 20}) & Bitset<64>({20, 30})).count(), 1u);
+  EXPECT_EQ((Bitset<96>({31, 32, 63, 64}) & Bitset<96>({32, 64, 95})).count(),
+            2u);
+  EXPECT_TRUE((Bitset<33>({0, 32}) & Bitset<33>({32})).test(32));
 
   // Test operator&=.
   Bitset<64> C({10, 20, 30});
@@ -203,20 +197,17 @@ TEST(BitsetTest, BitwiseOperators) {
   EXPECT_TRUE(C.test(30));
   EXPECT_FALSE(C.test(40));
 
-  constexpr Bitset<64> TestAnd = [] {
+  static_assert([] {
     Bitset<64> X({10, 20, 30});
     X &= Bitset<64>({20, 30, 40});
-    return X;
-  }();
-  static_assert(TestAnd.test(20) && TestAnd.test(30) && !TestAnd.test(10));
-
-  constexpr Bitset<100> TestAnd100 = [] {
-    Bitset<100> X({10, 50, 99});
-    X &= Bitset<100>({50, 99});
-    return X;
-  }();
-  static_assert(TestAnd100.count() == 2 && TestAnd100.test(50) &&
-                TestAnd100.test(99));
+    return X.test(20) && X.test(30) && !X.test(10);
+  }());
+
+  Bitset<100> TestAnd100({10, 50, 99});
+  TestAnd100 &= Bitset<100>({50, 99});
+  EXPECT_EQ(TestAnd100.count(), 2u);
+  EXPECT_TRUE(TestAnd100.test(50));
+  EXPECT_TRUE(TestAnd100.test(99));
 
   // Test operator|.
   Bitset<64> D({10, 20});
@@ -228,9 +219,8 @@ TEST(BitsetTest, BitwiseOperators) {
   EXPECT_EQ(Result2.count(), 3u);
 
   static_assert((Bitset<64>({10}) | Bitset<64>({20})).count() == 2);
-  static_assert((Bitset<128>({0, 64, 127}) | Bitset<128>({64, 100})).count() ==
-                4);
-  static_assert((Bitset<33>({0, 16}) | Bitset<33>({16, 32})).count() == 3);
+  EXPECT_EQ((Bitset<128>({0, 64, 127}) | Bitset<128>({64, 100})).count(), 4u);
+  EXPECT_EQ((Bitset<33>({0, 16}) | Bitset<33>({16, 32})).count(), 3u);
 
   // Test operator|=.
   Bitset<64> F({10, 20});
@@ -239,19 +229,15 @@ TEST(BitsetTest, BitwiseOperators) {
   EXPECT_TRUE(F.test(20));
   EXPECT_TRUE(F.test(30));
 
-  constexpr Bitset<64> TestOr = [] {
+  static_assert([] {
     Bitset<64> X({10});
     X |= Bitset<64>({20});
-    return X;
-  }();
-  static_assert(TestOr.test(10) && TestOr.test(20));
+    return X.test(10) && X.test(20);
+  }());
 
-  constexpr Bitset<96> TestOr96 = [] {
-    Bitset<96> X({31, 63});
-    X |= Bitset<96>({32, 64});
-    return X;
-  }();
-  static_assert(TestOr96.count() == 4);
+  Bitset<96> TestOr96({31, 63});
+  TestOr96 |= Bitset<96>({32, 64});
+  EXPECT_EQ(TestOr96.count(), 4u);
 
   // Test operator^.
   Bitset<64> G({10, 20, 30});
@@ -264,11 +250,11 @@ TEST(BitsetTest, BitwiseOperators) {
   EXPECT_EQ(Result3.count(), 2u);
 
   static_assert((Bitset<64>({10, 20}) ^ Bitset<64>({20, 30})).test(10));
-  static_assert(!(Bitset<64>({10, 20}) ^ Bitset<64>({20, 30})).test(20));
-  static_assert((Bitset<64>({10, 20}) ^ Bitset<64>({20, 30})).test(30));
-  static_assert((Bitset<64>({10, 20}) ^ Bitset<64>({20, 30})).count() == 2);
-  static_assert((Bitset<100>({0, 50, 99}) ^ Bitset<100>({50})).count() == 2);
-  static_assert((Bitset<33>({0, 32}) ^ Bitset<33>({0, 16})).count() == 2);
+  EXPECT_FALSE((Bitset<64>({10, 20}) ^ Bitset<64>({20, 30})).test(20));
+  EXPECT_TRUE((Bitset<64>({10, 20}) ^ Bitset<64>({20, 30})).test(30));
+  EXPECT_EQ((Bitset<64>({10, 20}) ^ Bitset<64>({20, 30})).count(), 2u);
+  EXPECT_EQ((Bitset<100>({0, 50, 99}) ^ Bitset<100>({50})).count(), 2u);
+  EXPECT_EQ((Bitset<33>({0, 32}) ^ Bitset<33>({0, 16})).count(), 2u);
 
   // Test operator^=.
   Bitset<64> I({10, 20, 30});
@@ -278,218 +264,236 @@ TEST(BitsetTest, BitwiseOperators) {
   EXPECT_FALSE(I.test(30));
   EXPECT_TRUE(I.test(40));
 
-  constexpr Bitset<64> TestXor = [] {
+  static_assert([] {
     Bitset<64> X({10, 20});
     X ^= Bitset<64>({20, 30});
-    return X;
-  }();
-  static_assert(TestXor.test(10) && !TestXor.test(20) && TestXor.test(30));
-
-  constexpr Bitset<128> TestXor128 = [] {
-    Bitset<128> X({0, 64, 127});
-    X ^= Bitset<128>({64});
-    return X;
-  }();
-  static_assert(TestXor128.count() == 2 && TestXor128.test(0) &&
-                TestXor128.test(127));
+    return X.test(10) && !X.test(20) && X.test(30);
+  }());
+
+  Bitset<128> TestXor128({0, 64, 127});
+  TestXor128 ^= Bitset<128>({64});
+  EXPECT_EQ(TestXor128.count(), 2u);
+  EXPECT_TRUE(TestXor128.test(0));
+  EXPECT_TRUE(TestXor128.test(127));
 }
 
 TEST(BitsetTest, ShiftOperators) {
   // Test left shift.
   static_assert((Bitset<64>({0}) << 10).test(10));
-  static_assert(!(Bitset<64>({0}) << 10).test(0));
-  static_assert((Bitset<64>({63}) << 1).none());
-  static_assert((Bitset<128>({0}) << 64).test(64));
-  static_assert((Bitset<128>({63}) << 1).test(64));
-  static_assert((Bitset<128>({127}) << 1).none());
+  EXPECT_FALSE((Bitset<64>({0}) << 10).test(0));
+  EXPECT_TRUE((Bitset<64>({63}) << 1).none());
+  EXPECT_TRUE((Bitset<128>({0}) << 64).test(64));
+  EXPECT_TRUE((Bitset<128>({63}) << 1).test(64));
+  EXPECT_TRUE((Bitset<128>({127}) << 1).none());
 
   // Test right shift.
   static_assert((Bitset<64>({10}) >> 10).test(0));
-  static_assert(!(Bitset<64>({10}) >> 10).test(10));
-  static_assert((Bitset<64>({0}) >> 1).none());
-  static_assert((Bitset<128>({64}) >> 64).test(0));
-  static_assert((Bitset<128>({64}) >> 1).test(63));
-  static_assert((Bitset<128>({0}) >> 1).none());
+  EXPECT_FALSE((Bitset<64>({10}) >> 10).test(10));
+  EXPECT_TRUE((Bitset<64>({0}) >> 1).none());
+  EXPECT_TRUE((Bitset<128>({64}) >> 64).test(0));
+  EXPECT_TRUE((Bitset<128>({64}) >> 1).test(63));
+  EXPECT_TRUE((Bitset<128>({0}) >> 1).none());
 
   // Test shift by 0.
-  static_assert((Bitset<64>({10, 20}) << 0) == Bitset<64>({10, 20}));
-  static_assert((Bitset<64>({10, 20}) >> 0) == Bitset<64>({10, 20}));
+  EXPECT_TRUE((Bitset<64>({10, 20}) << 0) == Bitset<64>({10, 20}));
+  EXPECT_TRUE((Bitset<64>({10, 20}) >> 0) == Bitset<64>({10, 20}));
 
   // Test shift by NumBits (clears all).
-  static_assert((Bitset<64>({0, 63}) << 64).none());
-  static_assert((Bitset<64>({0, 63}) >> 64).none());
-  static_assert((Bitset<128>({0, 127}) << 128).none());
-  static_assert((Bitset<128>({0, 127}) >> 128).none());
+  EXPECT_TRUE((Bitset<64>({0, 63}) << 64).none());
+  EXPECT_TRUE((Bitset<64>({0, 63}) >> 64).none());
+  EXPECT_TRUE((Bitset<128>({0, 127}) << 128).none());
+  EXPECT_TRUE((Bitset<128>({0, 127}) >> 128).none());
 }
 
 TEST(BitsetTest, GetNumWords64) {
   static_assert(Bitset<1>::getNumWords64() == 1);
-  static_assert(Bitset<32>::getNumWords64() == 1);
-  static_assert(Bitset<64>::getNumWords64() == 1);
-  static_assert(Bitset<65>::getNumWords64() == 2);
-  static_assert(Bitset<96>::getNumWords64() == 2);
-  static_assert(Bitset<128>::getNumWords64() == 2);
-  static_assert(Bitset<129>::getNumWords64() == 3);
+  EXPECT_EQ(Bitset<32>::getNumWords64(), 1u);
+  EXPECT_EQ(Bitset<64>::getNumWords64(), 1u);
+  EXPECT_EQ(Bitset<65>::getNumWords64(), 2u);
+  EXPECT_EQ(Bitset<96>::getNumWords64(), 2u);
+  EXPECT_EQ(Bitset<128>::getNumWords64(), 2u);
+  EXPECT_EQ(Bitset<129>::getNumWords64(), 3u);
 }
 
-TEST(BitsetTest, GetWord) {
+TEST(BitsetTest, GetWord64) {
   // Single-word bitset.
   constexpr auto B64 = Bitset<64>(std::array<uint64_t, 1>{0xdeadbeefcafe1234});
   static_assert(B64.getWord64(0) == 0xdeadbeefcafe1234);
 
   // Multi-word bitset.
-  constexpr auto B128 = Bitset<128>(
+  Bitset<128> B128(
       std::array<uint64_t, 2>{0x1111222233334444, 0xaaaabbbbccccdddd});
-  static_assert(B128.getWord64(0) == 0x1111222233334444);
-  static_assert(B128.getWord64(1) == 0xaaaabbbbccccdddd);
+  EXPECT_EQ(B128.getWord64(0), 0x1111222233334444u);
+  EXPECT_EQ(B128.getWord64(1), uint64_t(0xaaaabbbbccccdddd));
 
-  // Partial last word — high bits should be masked off.
-  constexpr auto B96 = Bitset<96>(
+  // Partial last word - high bits should be masked off.
+  Bitset<96> B96(
       std::array<uint64_t, 2>{0xffffffffffffffff, 0xffffffffffffffff});
-  static_assert(B96.getWord64(0) == 0xffffffffffffffff);
+  EXPECT_EQ(B96.getWord64(0), uint64_t(0xffffffffffffffff));
   // Only lower 32 bits.
-  static_assert(B96.getWord64(1) == 0x00000000ffffffff);
+  EXPECT_EQ(B96.getWord64(1), uint64_t(0x00000000ffffffff));
 
   // Empty bitset.
-  static_assert(Bitset<64>().getWord64(0) == 0);
-  static_assert(Bitset<128>().getWord64(0) == 0);
-  static_assert(Bitset<128>().getWord64(1) == 0);
+  EXPECT_EQ(Bitset<64>().getWord64(0), 0u);
+  EXPECT_EQ(Bitset<128>().getWord64(0), 0u);
+  EXPECT_EQ(Bitset<128>().getWord64(1), 0u);
 }
 
 TEST(BitsetTest, FindLastSet) {
   // Empty bitset returns -1.
   static_assert(Bitset<64>().findLastSet() == -1);
-  static_assert(Bitset<128>().findLastSet() == -1);
+  EXPECT_EQ(Bitset<128>().findLastSet(), -1);
 
   // Single bit set.
-  static_assert(Bitset<64>({0}).findLastSet() == 0);
-  static_assert(Bitset<64>({63}).findLastSet() == 63);
-  static_assert(Bitset<64>({31}).findLastSet() == 31);
-  static_assert(Bitset<128>({0}).findLastSet() == 0);
-  static_assert(Bitset<128>({64}).findLastSet() == 64);
-  static_assert(Bitset<128>({127}).findLastSet() == 127);
+  EXPECT_EQ(Bitset<64>({0}).findLastSet(), 0);
+  EXPECT_EQ(Bitset<64>({63}).findLastSet(), 63);
+  EXPECT_EQ(Bitset<64>({31}).findLastSet(), 31);
+  EXPECT_EQ(Bitset<128>({0}).findLastSet(), 0);
+  EXPECT_EQ(Bitset<128>({64}).findLastSet(), 64);
+  EXPECT_EQ(Bitset<128>({127}).findLastSet(), 127);
 
-  // Multiple bits — returns highest.
-  static_assert(Bitset<64>({0, 10, 50}).findLastSet() == 50);
-  static_assert(Bitset<128>({0, 63, 64, 100}).findLastSet() == 100);
+  // Multiple bits - returns highest.
+  EXPECT_EQ(Bitset<64>({0, 10, 50}).findLastSet(), 50);
+  EXPECT_EQ(Bitset<128>({0, 63, 64, 100}).findLastSet(), 100);
 
   // All bits set.
-  static_assert(Bitset<64>().set().findLastSet() == 63);
-  static_assert(Bitset<128>().set().findLastSet() == 127);
-  static_assert(Bitset<96>().set().findLastSet() == 95);
+  EXPECT_EQ(Bitset<64>().set().findLastSet(), 63);
+  EXPECT_EQ(Bitset<128>().set().findLastSet(), 127);
+  EXPECT_EQ(Bitset<96>().set().findLastSet(), 95);
 
   // Non-power-of-2 sizes.
-  static_assert(Bitset<33>({32}).findLastSet() == 32);
-  static_assert(Bitset<33>({0, 32}).findLastSet() == 32);
-  static_assert(Bitset<65>({64}).findLastSet() == 64);
+  EXPECT_EQ(Bitset<33>({32}).findLastSet(), 32);
+  EXPECT_EQ(Bitset<33>({0, 32}).findLastSet(), 32);
+  EXPECT_EQ(Bitset<65>({64}).findLastSet(), 64);
 }
 
 TEST(BitsetTest, ShiftMultiWords) {
   constexpr auto B192 = Bitset<192>({0, 64, 128});
   static_assert((B192 << 1) == Bitset<192>({1, 65, 129}));
-  static_assert((B192 >> 1) == Bitset<192>({63, 127}));
-  static_assert((B192 << 64) == Bitset<192>({64, 128}));
-  static_assert((B192 >> 64) == Bitset<192>({0, 64}));
-  static_assert((Bitset<192>({63, 127}) << 1) == Bitset<192>({64, 128}));
-  static_assert((Bitset<192>({64, 128}) >> 1) == Bitset<192>({63, 127}));
+  EXPECT_TRUE((B192 >> 1) == Bitset<192>({63, 127}));
+  EXPECT_TRUE((B192 << 64) == Bitset<192>({64, 128}));
+  EXPECT_TRUE((B192 >> 64) == Bitset<192>({0, 64}));
+  EXPECT_TRUE((Bitset<192>({63, 127}) << 1) == Bitset<192>({64, 128}));
+  EXPECT_TRUE((Bitset<192>({64, 128}) >> 1) == Bitset<192>({63, 127}));
 }
 
 TEST(BitsetTest, ShiftBoundaryBitShifts) {
   static_assert((Bitset<128>({1}) << 63) == Bitset<128>({64}));
-  static_assert((Bitset<128>({64}) >> 63) == Bitset<128>({1}));
-  static_assert((Bitset<192>({1, 65}) << 63) == Bitset<192>({64, 128}));
+  EXPECT_TRUE((Bitset<128>({64}) >> 63) == Bitset<128>({1}));
+  EXPECT_TRUE((Bitset<192>({1, 65}) << 63) == Bitset<192>({64, 128}));
   // Shift by NumBits - 1.
-  static_assert((Bitset<64>({0}) << 63) == Bitset<64>({63}));
-  static_assert((Bitset<64>({63}) >> 63) == Bitset<64>({0}));
-  static_assert((Bitset<33>({0}) << 32) == Bitset<33>({32}));
-  // Full-width shift of a fully-set bitset loses exactly one bit.
-  static_assert((Bitset<128>().set() << 1).count() == 127);
-  static_assert((Bitset<128>().set() >> 1).count() == 127);
-  static_assert((Bitset<100>().set() >> 1).count() == 99);
+  EXPECT_TRUE((Bitset<64>({0}) << 63) == Bitset<64>({63}));
+  EXPECT_TRUE((Bitset<64>({63}) >> 63) == Bitset<64>({0}));
+  EXPECT_TRUE((Bitset<33>({0}) << 32) == Bitset<33>({32}));
+  // Full-width shift of a fully-set bitset loses exactly one bit, and the
+  // bit that is lost must be the boundary bit.
+  EXPECT_EQ((Bitset<128>().set() << 1).count(), 127u);
+  EXPECT_EQ((Bitset<128>().set() >> 1).count(), 127u);
+  EXPECT_EQ((Bitset<100>().set() >> 1).count(), 99u);
+  EXPECT_FALSE((Bitset<100>().set() >> 1).test(99));
+  EXPECT_FALSE((Bitset<100>().set() << 1).test(0));
+  EXPECT_FALSE((Bitset<128>().set() >> 1).test(127));
+  EXPECT_FALSE((Bitset<128>().set() << 1).test(0));
 }
 
 TEST(BitsetTest, ShiftExcessAmount) {
   static_assert((Bitset<64>().set() << 65).none());
-  static_assert((Bitset<64>().set() >> 200).none());
-  static_assert((Bitset<33>({0, 10, 32}) << 1000).none());
-  static_assert((Bitset<128>({0, 127}) >> 1000).none());
-  static_assert((Bitset<192>().set() << 193).none());
+  EXPECT_TRUE((Bitset<64>().set() >> 200).none());
+  EXPECT_TRUE((Bitset<33>({0, 10, 32}) << 1000).none());
+  EXPECT_TRUE((Bitset<128>({0, 127}) >> 1000).none());
+  EXPECT_TRUE((Bitset<192>().set() << 193).none());
+}
+
+TEST(BitsetTest, ShiftDoesNotMutateOperand) {
+  // Non-mutating operator<< / operator>> must leave the source unchanged.
+  Bitset<128> X({5, 70});
+  Bitset<128> YL = X << 1;
+  EXPECT_TRUE(YL == Bitset<128>({6, 71}));
+  EXPECT_TRUE(X == Bitset<128>({5, 70}));
+
+  Bitset<128> YR = X >> 1;
+  EXPECT_TRUE(YR == Bitset<128>({4, 69}));
+  EXPECT_TRUE(X == Bitset<128>({5, 70}));
+
+  static_assert([] {
+    Bitset<128> X({5, 70});
+    Bitset<128> Y = X << 1;
+    return Y == Bitset<128>({6, 71}) && X == Bitset<128>({5, 70});
+  }());
 }
 
 TEST(BitsetTest, ShiftAssignReturnsReference) {
-  constexpr Bitset<64> L = [] {
+  static_assert([] {
     Bitset<64> X({0});
     (X <<= 3) <<= 2;
-    return X;
-  }();
-  static_assert(L == Bitset<64>({5}));
-
-  constexpr Bitset<128> R = [] {
-    Bitset<128> X({100});
-    (X >>= 30) >>= 10;
-    return X;
-  }();
-  static_assert(R == Bitset<128>({60}));
+    return X == Bitset<64>({5});
+  }());
+
+  Bitset<128> R({100});
+  (R >>= 30) >>= 10;
+  EXPECT_TRUE(R == Bitset<128>({60}));
 }
 
-TEST(BitsetTest, GetWordConsistencyWithTest) {
-  // For every set bit, getWord must report it in the expected 64-bit word.
+TEST(BitsetTest, GetWord64ConsistencyWithTest) {
+  // For every set bit, getWord64 must report it in the expected 64-bit word.
   constexpr auto B100 = Bitset<100>({0, 50, 64, 99});
   static_assert((B100.getWord64(0) & 1) != 0);
-  static_assert((B100.getWord64(0) & (uint64_t(1) << 50)) != 0);
-  static_assert((B100.getWord64(1) & 1) != 0);
-  static_assert((B100.getWord64(1) & (uint64_t(1) << 35)) != 0);
+  EXPECT_NE(B100.getWord64(0) & (uint64_t(1) << 50), 0u);
+  EXPECT_NE(B100.getWord64(1) & 1, 0u);
+  EXPECT_NE(B100.getWord64(1) & (uint64_t(1) << 35), 0u);
 }
 
-TEST(BitsetTest, GetWordAfterMutation) {
-  // getWord reflects subsequent set / shift.
-  constexpr auto B = [] {
+TEST(BitsetTest, GetWord64AfterMutation) {
+  // getWord64() reflects subsequent set / shift.
+  static_assert([] {
     Bitset<128> X;
     X.set(5).set(70);
-    return X;
-  }();
-  static_assert(B.getWord64(0) == (uint64_t(1) << 5));
-  static_assert(B.getWord64(1) == (uint64_t(1) << 6));
-
-  constexpr auto Shifted = Bitset<128>({5}) << 64;
-  static_assert(Shifted.getWord64(0) == 0);
-  static_assert(Shifted.getWord64(1) == (uint64_t(1) << 5));
+    return X.getWord64(0) == (uint64_t(1) << 5) &&
+           X.getWord64(1) == (uint64_t(1) << 6);
+  }());
+
+  Bitset<128> Shifted = Bitset<128>({5}) << 64;
+  EXPECT_EQ(Shifted.getWord64(0), 0u);
+  EXPECT_EQ(Shifted.getWord64(1), uint64_t(1) << 5);
 }
 
-TEST(BitsetTest, GetNumWordsMoreWidths) {
+TEST(BitsetTest, GetNumWords64MoreWidths) {
   static_assert(Bitset<2>::getNumWords64() == 1);
-  static_assert(Bitset<192>::getNumWords64() == 3);
-  static_assert(Bitset<193>::getNumWords64() == 4);
-  static_assert(Bitset<256>::getNumWords64() == 4);
+  EXPECT_EQ(Bitset<192>::getNumWords64(), 3u);
+  EXPECT_EQ(Bitset<193>::getNumWords64(), 4u);
+  EXPECT_EQ(Bitset<256>::getNumWords64(), 4u);
 }
 
 TEST(BitsetTest, FindLastSetSmallWidths) {
   static_assert(Bitset<1>().findLastSet() == -1);
-  static_assert(Bitset<1>({0}).findLastSet() == 0);
-  static_assert(Bitset<2>({0, 1}).findLastSet() == 1);
-  static_assert(Bitset<32>({31}).findLastSet() == 31);
-  static_assert(Bitset<32>().set().findLastSet() == 31);
+  EXPECT_EQ(Bitset<1>({0}).findLastSet(), 0);
+  EXPECT_EQ(Bitset<2>({0, 1}).findLastSet(), 1);
+  EXPECT_EQ(Bitset<32>({31}).findLastSet(), 31);
+  EXPECT_EQ(Bitset<32>().set().findLastSet(), 31);
 }
 
 TEST(BitsetTest, FindLastSetMultiWordScan) {
   static_assert(Bitset<192>({70}).findLastSet() == 70);
-  static_assert(Bitset<192>({64, 70, 127}).findLastSet() == 127);
-  static_assert(Bitset<192>({3}).findLastSet() == 3);
-  static_assert(Bitset<100>({99}).findLastSet() == 99);
+  EXPECT_EQ(Bitset<192>({64, 70, 127}).findLastSet(), 127);
+  EXPECT_EQ(Bitset<192>({3}).findLastSet(), 3);
+  EXPECT_EQ(Bitset<100>({99}).findLastSet(), 99);
+  // Highest set bit lives in the lowest word; the loop must scan past
+  // multiple empty trailing words.
+  EXPECT_EQ(Bitset<192>({0}).findLastSet(), 0);
+  EXPECT_EQ(Bitset<256>({1}).findLastSet(), 1);
 }
 
 TEST(BitsetTest, FindLastSetAfterMutation) {
-  constexpr auto A = Bitset<128>({0, 50, 100}).reset(100);
-  static_assert(A.findLastSet() == 50);
+  static_assert(Bitset<128>({0, 50, 100}).reset(100).findLastSet() == 50);
 
-  constexpr auto B = Bitset<64>({10}) << 20;
-  static_assert(B.findLastSet() == 30);
+  Bitset<64> B = Bitset<64>({10}) << 20;
+  EXPECT_EQ(B.findLastSet(), 30);
 
-  constexpr auto C = Bitset<64>({63}) >> 10;
-  static_assert(C.findLastSet() == 53);
+  Bitset<64> C = Bitset<64>({63}) >> 10;
+  EXPECT_EQ(C.findLastSet(), 53);
 
-  constexpr auto D = Bitset<64>({63}) << 1;
-  static_assert(D.findLastSet() == -1);
+  Bitset<64> D = Bitset<64>({63}) << 1;
+  EXPECT_EQ(D.findLastSet(), -1);
 }
 
 } // namespace

>From 910ad3326205fb367b6f75c8a27b5cbcbff5bd7d Mon Sep 17 00:00:00 2001
From: Jiachen Yuan <jiacheny at nvidia.com>
Date: Tue, 5 May 2026 15:39:32 +0000
Subject: [PATCH 4/4] remove the non-dependent static assert

---
 llvm/include/llvm/ADT/Bitset.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/llvm/include/llvm/ADT/Bitset.h b/llvm/include/llvm/ADT/Bitset.h
index 38e9684df9714..8477f307f5953 100644
--- a/llvm/include/llvm/ADT/Bitset.h
+++ b/llvm/include/llvm/ADT/Bitset.h
@@ -268,9 +268,6 @@ template <unsigned NumBits> class Bitset {
     if constexpr (BitwordBits == 64) {
       return Bits[I];
     } else {
-      static_assert(BitwordBits == 32, "Unsupported word size");
-      // When Bitword is 32-bit, for a valid I, the first word is always
-      // present, but the second may not be present.
       uint64_t Lo = Bits[2 * I];
       uint64_t Hi = (2 * I + 1 < NumWords) ? Bits[2 * I + 1] : 0;
       return Lo | (Hi << 32);



More information about the llvm-commits mailing list