[llvm] [ADT] Refactor Bitset to Be More Constexpr-Usable and Add More Member Functions (PR #172062)

Nicolai Hähnle via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 12 14:01:11 PST 2025


================
@@ -146,23 +173,90 @@ class Bitset {
     Bitset Result = *this;
     for (auto &B : Result.Bits)
       B = ~B;
+    Result.maskLastWord();
     return Result;
   }
 
-  bool operator==(const Bitset &RHS) const {
-    return std::equal(std::begin(Bits), std::end(Bits), std::begin(RHS.Bits));
+  constexpr bool operator==(const Bitset &RHS) const {
+    for (unsigned I = 0; I < NumWords - 1; ++I)
+      if (Bits[I] != RHS.Bits[I])
+        return false;
+    return (Bits[NumWords - 1] & RemainderMask) ==
+           (RHS.Bits[NumWords - 1] & RemainderMask);
   }
 
-  bool operator!=(const Bitset &RHS) const { return !(*this == RHS); }
+  constexpr bool operator!=(const Bitset &RHS) const { return !(*this == RHS); }
 
-  bool operator < (const Bitset &Other) const {
+  constexpr bool operator<(const Bitset &Other) const {
     for (unsigned I = 0, E = size(); I != E; ++I) {
       bool LHS = test(I), RHS = Other.test(I);
       if (LHS != RHS)
         return LHS < RHS;
     }
     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();
----------------
nhaehnle wrote:

It doesn't make sense to do `maskLastWord` at the *end* of shifting the bits to lower positions. And in general, introducing `maskLastWord` in so many places feels weird to me.

There are two reasonable choices for invariants:

* Remainder bits may be garbage
* Remainder bits must be 0 at all times

Pick one and stick to it. (I'd lean slightly towards the second option because it makes the bitset contents less confusing when looking at it in a debugger.)

https://github.com/llvm/llvm-project/pull/172062


More information about the llvm-commits mailing list