[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