[llvm] Refactor LaneBitmask to be a Bitset (PR #191757)

Robert Imschweiler via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 13 12:57:10 PDT 2026


================
@@ -29,72 +29,193 @@
 #ifndef LLVM_MC_LANEBITMASK_H
 #define LLVM_MC_LANEBITMASK_H
 
-#include "llvm/Support/Compiler.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/Bitset.h"
 #include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Printable.h"
 #include "llvm/Support/raw_ostream.h"
+#include <array>
+#include <cassert>
 
-namespace llvm {
+namespace llvm::detail {
+template <unsigned NumBits> struct LaneBitmaskImpl : public Bitset<NumBits> {
+  static constexpr unsigned BitWidth = NumBits;
 
-  struct LaneBitmask {
-    // When changing the underlying type, change the format string as well.
-    using Type = uint64_t;
-    enum : unsigned { BitWidth = 8*sizeof(Type) };
-    constexpr static const char *const FormatStr = "%016llX";
+  constexpr LaneBitmaskImpl() = default;
+  constexpr LaneBitmaskImpl(const LaneBitmaskImpl &) = default;
+  explicit constexpr LaneBitmaskImpl(uint64_t V)
+      : Bitset<NumBits>(std::array<uint64_t, (NumBits + 63) / 64>{V}) {}
+  explicit constexpr LaneBitmaskImpl(
+      const std::array<uint64_t, (NumBits + 63) / 64> &B)
+      : Bitset<NumBits>(B) {}
+  explicit LaneBitmaskImpl(const APInt &N)
+      : Bitset<NumBits>(convertAPIntToArray(N)) {}
+  // Delete the initializer_list constructor to avoid ambiguity with the
+  // std::array constructor.
+  LaneBitmaskImpl(std::initializer_list<unsigned>) = delete;
+  constexpr LaneBitmaskImpl &operator=(const LaneBitmaskImpl &) = default;
 
-    constexpr LaneBitmask() = default;
-    explicit constexpr LaneBitmask(Type V) : Mask(V) {}
+  /// Compare as unsigned integers (most-significant word first). This differs
+  /// from Bitset::operator< which compares bit-by-bit from LSB.
+  constexpr bool operator<(const LaneBitmaskImpl &Other) const {
+    const auto &ThisBits = this->getData();
+    const auto &OtherBits = Other.getData();
+    for (int I = ThisBits.size() - 1; I >= 0; --I) {
+      if (ThisBits[I] != OtherBits[I])
+        return ThisBits[I] < OtherBits[I];
+    }
+    return false;
+  }
 
-    constexpr bool operator== (LaneBitmask M) const { return Mask == M.Mask; }
-    constexpr bool operator!= (LaneBitmask M) const { return Mask != M.Mask; }
-    constexpr bool operator< (LaneBitmask M)  const { return Mask < M.Mask; }
-    constexpr bool none() const { return Mask == 0; }
-    constexpr bool any()  const { return Mask != 0; }
-    constexpr bool all()  const { return ~Mask == 0; }
+  constexpr LaneBitmaskImpl operator~() const {
+    return Bitset<NumBits>::operator~();
+  }
+  constexpr LaneBitmaskImpl operator|(LaneBitmaskImpl M) const {
+    return Bitset<NumBits>::operator|(M);
+  }
+  constexpr LaneBitmaskImpl operator&(LaneBitmaskImpl M) const {
+    return Bitset<NumBits>::operator&(M);
+  }
+  constexpr LaneBitmaskImpl &operator|=(LaneBitmaskImpl M) {
+    Bitset<NumBits>::operator|=(M);
+    return *this;
+  }
+  constexpr LaneBitmaskImpl &operator&=(LaneBitmaskImpl M) {
+    Bitset<NumBits>::operator&=(M);
+    return *this;
+  }
+  constexpr LaneBitmaskImpl operator^(LaneBitmaskImpl M) const {
+    return Bitset<NumBits>::operator^(M);
+  }
+  constexpr LaneBitmaskImpl &operator^=(LaneBitmaskImpl M) {
+    Bitset<NumBits>::operator^=(M);
+    return *this;
+  }
 
-    constexpr LaneBitmask operator~() const {
-      return LaneBitmask(~Mask);
-    }
-    constexpr LaneBitmask operator|(LaneBitmask M) const {
-      return LaneBitmask(Mask | M.Mask);
-    }
-    constexpr LaneBitmask operator&(LaneBitmask M) const {
-      return LaneBitmask(Mask & M.Mask);
-    }
-    LaneBitmask &operator|=(LaneBitmask M) {
-      Mask |= M.Mask;
+  constexpr size_t getNumLanes() const { return this->count(); }
+
+  unsigned getHighestLane() const {
+    assert(this->any() && "getHighestLane called on empty mask");
+    const auto &Bits = this->getData();
+    constexpr size_t WordBits = sizeof(decltype(Bits[0])) * 8;
+    for (int I = Bits.size() - 1; I >= 0; --I)
+      if (Bits[I] != 0)
+        return I * WordBits + Log2_64(Bits[I]);
+    llvm_unreachable("should have found a set bit");
+  }
+
+  /// Shift bits left by \p S positions. Zeroes are shifted in from the right.
+  constexpr LaneBitmaskImpl operator<<(unsigned S) const {
+    return Bitset<NumBits>::operator<<(S);
+  }
+
+  /// Shift bits right by \p S positions. Zeroes are shifted in from the left.
+  constexpr LaneBitmaskImpl operator>>(unsigned S) const {
+    return Bitset<NumBits>::operator>>(S);
+  }
+
+  /// Rotate bits left by \p S positions.
+  constexpr LaneBitmaskImpl rotateLeft(unsigned S) const {
+    S = S % NumBits;
+    if (S == 0)
       return *this;
-    }
-    LaneBitmask &operator&=(LaneBitmask M) {
-      Mask &= M.Mask;
+    return (*this << S) | (*this >> (NumBits - S));
+  }
+
+  /// Rotate bits right by \p S positions.
+  constexpr LaneBitmaskImpl rotateRight(unsigned S) const {
+    S = S % NumBits;
+    if (S == 0)
       return *this;
-    }
+    return (*this >> S) | (*this << (NumBits - S));
+  }
 
-    constexpr Type getAsInteger() const { return Mask; }
+  static constexpr LaneBitmaskImpl getNone() { return LaneBitmaskImpl(); }
 
-    unsigned getNumLanes() const { return llvm::popcount(Mask); }
-    unsigned getHighestLane() const {
-      return Log2_64(Mask);
-    }
+  static constexpr LaneBitmaskImpl getAll() {
+    LaneBitmaskImpl Result;
+    Result.set();
+    return Result;
+  }
 
-    static constexpr LaneBitmask getNone() { return LaneBitmask(0); }
-    static constexpr LaneBitmask getAll() { return ~LaneBitmask(0); }
-    static constexpr LaneBitmask getLane(unsigned Lane) {
-      return LaneBitmask(Type(1) << Lane);
-    }
+  static constexpr LaneBitmaskImpl getLane(unsigned Lane) {
+    LaneBitmaskImpl Result;
+    Result.set(Lane);
+    return Result;
+  }
+
+private:
+  constexpr LaneBitmaskImpl(const Bitset<NumBits> &B) : Bitset<NumBits>(B) {}
+
+  /// Helper to convert APInt to array format for Bitset constructor.
+  static std::array<uint64_t, (NumBits + 63) / 64>
+  convertAPIntToArray(const APInt &N) {
+    static_assert(std::is_same_v<APInt::WordType, uint64_t>,
+                  "APInt::WordType needs to be uint64_t for word-level copy.");
+    assert(N.getBitWidth() <= NumBits &&
+           "Cannot convert to LaneBitmask. The input APInt has "
+           "more bits than LaneBitmask can hold.");
+    std::array<uint64_t, (NumBits + 63) / 64> Result{};
+    const uint64_t *RawData = N.getRawData();
+    const size_t NumWords = N.getNumWords();
+    for (size_t I = 0; I < NumWords && I < Result.size(); ++I)
+      Result[I] = RawData[I];
+    return Result;
+  }
+
+  template <typename, typename> friend struct llvm::format_provider;
+};
 
-  private:
-    Type Mask = 0;
-  };
+} // end namespace llvm::detail
 
-  /// Create Printable object to print LaneBitmasks on a \ref raw_ostream.
-  inline Printable PrintLaneMask(LaneBitmask LaneMask) {
-    return Printable([LaneMask](raw_ostream &OS) {
-      OS << format(LaneBitmask::FormatStr, LaneMask.getAsInteger());
-    });
+namespace llvm {
+using LaneBitmask = detail::LaneBitmaskImpl<64>;
+
+template <unsigned NumBits>
+struct format_provider<detail::LaneBitmaskImpl<NumBits>> {
+  using T = detail::LaneBitmaskImpl<NumBits>;
+  static void format(const T &V, raw_ostream &Stream, StringRef Style) {
+    // Print as hex using platform words from most significant to least.
+    // Only print the first 64 bits if all upper words are zero.
+    const auto &Data = V.getData();
+    constexpr unsigned SizeOfBitword = sizeof(Data[0]);
+    constexpr unsigned HexWidth = SizeOfBitword * 2;
+    constexpr unsigned NumWordsIn64Bits = 8 / SizeOfBitword;
----------------
ro-i wrote:

why magic number? I guess 8 is supposed to be the number of bytes (assuming `CHAR_BIT = 8`) per 64bit integer?

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


More information about the llvm-commits mailing list