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

via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 13 09:51:30 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Jiachen Yuan (JiachenYuan)

<details>
<summary>Changes</summary>

This PR refactors `LaneBitmask` to be a wrapper around `llvm::Bitset`. It serves as the groundwork for targets to have a more extensible `LaneBitmask`.
## Context

**Discourse discussion:** https://discourse.llvm.org/t/rfc-out-of-lanebitmask-bits-again/88613
**Other PR along the effort**:  `Bitset` refactor - https://github.com/llvm/llvm-project/pull/189497

## Overview

This patch replaces `LaneBitmask`'s `uint64_t` implementation with a template class `detail::LaneBitmaskImpl<NumBits>` that inherits from `Bitset<NumBits>`. For the current stage of the refactor, it remains 64-bit:

```cpp
using LaneBitmask = detail::LaneBitmaskImpl<64>;
```

This is a NFC refactor for all upstream targets. The key design goal is that a downstream target needing a wider lane bitmask (e.g. 128-bit) can simply change this alias — all LaneBitmask operations, printing, parsing, hashing, and TableGen emission are already templated and will work at any width.

To maintain backward compatibility, the refactored class preserves the existing public APIs (`getNone()`, `getAll()`, `getLane()`, `getNumLanes()`, `getHighestLane()`, bitwise operators, comparisons) and all existing constructors. The `getAsInteger()` "escape hatch" is **intentionally removed** — all consumer code is adjusted to work through the type-safe LaneBitmask API instead of extracting raw integers.

As a prerequisite, `Bitset` is extended with shift operators (`<<`, `>>`), hash support (`hash_value`, `std::hash`), and a `getData()` accessor (protected, for use by `LaneBitmaskImpl` and `format_provider`).

## Changes

### Core headers

- **`llvm/ADT/Bitset.h`** — Add shift operators (`<<=`, `>>=`, `<<`, `>>`), `getData()` (protected), hash support (`hash_value`, `std::hash` specialization), and the necessary friend declarations.

- **`llvm/MC/LaneBitmask.h`** — Replace the `uint64_t`-based `LaneBitmask` struct with a template `detail::LaneBitmaskImpl<NumBits>` inheriting from `Bitset<NumBits>`. The class wraps all inherited bitwise operators (`|`, `&`, `^`, `~`, `<<`, `>>`) to return the correct `LaneBitmaskImpl` type (avoiding slicing from the `Bitset` base). Adds `rotateLeft()`/`rotateRight()` methods needed by the register info emitter. Provides constructors from `uint64_t`, `std::array<uint64_t, N>`, and `APInt`. The `format_provider` and `PrintLaneMask` are templatized so they work at any width.

### Adjusted consumers

- **`MIRParser/MIParser.cpp`** — Lane mask parsing now distinguishes integer literals (parsed as `uint64_t`) from hex literals (parsed as `APInt`), enabling parsing of masks wider than 64 bits.

- **`MachineOperand.cpp`** — Uses `hash_value(LaneBitmask)` instead of `getAsInteger()` for hashing.

- **`MachineStableHash.cpp`** — Uses `stable_hash_name` on the deterministic `PrintLaneMask` output for stable hashing, instead of the removed `getAsInteger()`.

- **`RDFLiveness.h` / `RDFRegisters.h`** — `std::hash<LaneBitmask>` replaces `std::hash<LaneBitmask::Type>{}(mask.getAsInteger())`.

- **`RDFRegisters.cpp`** — Short-form debug printing now always uses `PrintLaneMask`, removing the dependency on `getAsInteger()`.

- **`SIRegisterInfo.cpp` / `SIRegisterInfo.h` (AMDGPU)** — Assertions and `getNumCoveredRegs` rewritten to use LaneBitmask comparisons and bitwise operations directly instead of extracting raw integers.

### TableGen

- **`RegisterInfoEmitter.cpp`** — The emitted `composeSubRegIndexLaneMask` code now uses `rotateLeft()`/`rotateRight()` methods instead of manual integer bit manipulation. `printMask` emits the simple `LaneBitmask(0x...)` form for single-word values, and the `std::array` constructor form for multi-word values (extracting each 64-bit word via public shift/mask operations).

### Tests

- **`LaneBitmaskTest.cpp`** (new, 933 lines) — Comprehensive unit tests covering both 64-bit and 128-bit `LaneBitmaskImpl` instantiations: constructors, comparisons, bitwise operators, shifts, rotations, inherited `Bitset` operations, `APInt` construction, printing, and hashing. Includes extensive `static_assert` tests verifying constexpr-ness.

## Next Steps

The next step after this PR is to add a mechanism — likely in TableGen/CMake — for targets to opt in to a wider `LaneBitmask`. With this patch landed, that becomes a matter of changing the `using` alias and the configuration plumbing — all the C++ infrastructure is already in place.

---

Patch is 51.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/191757.diff


13 Files Affected:

- (modified) llvm/include/llvm/ADT/Bitset.h (+85) 
- (modified) llvm/include/llvm/CodeGen/RDFLiveness.h (+1-1) 
- (modified) llvm/include/llvm/CodeGen/RDFRegisters.h (+1-2) 
- (modified) llvm/include/llvm/MC/LaneBitmask.h (+169-48) 
- (modified) llvm/lib/CodeGen/MIRParser/MIParser.cpp (+29-12) 
- (modified) llvm/lib/CodeGen/MachineOperand.cpp (+1-1) 
- (modified) llvm/lib/CodeGen/MachineStableHash.cpp (+5-1) 
- (modified) llvm/lib/CodeGen/RDFRegisters.cpp (-5) 
- (modified) llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp (+4-4) 
- (modified) llvm/lib/Target/AMDGPU/SIRegisterInfo.h (+4-5) 
- (modified) llvm/unittests/CodeGen/CMakeLists.txt (+2-1) 
- (added) llvm/unittests/CodeGen/LaneBitmaskTest.cpp (+933) 
- (modified) llvm/utils/TableGen/RegisterInfoEmitter.cpp (+22-10) 


``````````diff
diff --git a/llvm/include/llvm/ADT/Bitset.h b/llvm/include/llvm/ADT/Bitset.h
index 9dc0f24b1d9f5..1acf457cf6265 100644
--- a/llvm/include/llvm/ADT/Bitset.h
+++ b/llvm/include/llvm/ADT/Bitset.h
@@ -16,6 +16,7 @@
 #ifndef LLVM_ADT_BITSET_H
 #define LLVM_ADT_BITSET_H
 
+#include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/bit.h"
 #include <array>
 #include <climits>
@@ -23,6 +24,10 @@
 
 namespace llvm {
 
+// Forward declare Bitset and hash_value for friend declarations.
+template <unsigned NumBits> class Bitset;
+template <unsigned NumBits> hash_code hash_value(const Bitset<NumBits> &);
+
 /// This is a constexpr reimplementation of a subset of std::bitset. It would be
 /// nice to use std::bitset directly, but it doesn't support constant
 /// initialization.
@@ -52,6 +57,8 @@ template <unsigned NumBits> class Bitset {
   constexpr void maskLastWord() { Bits[getLastWordIndex()] &= RemainderMask; }
 
 protected:
+  constexpr const StorageType &getData() const { return Bits; }
+
   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)
@@ -194,8 +201,86 @@ 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;
+  }
+
+  friend hash_code hash_value<NumBits>(const Bitset<NumBits> &);
+  friend struct std::hash<Bitset<NumBits>>;
 };
 
+template <unsigned NumBits>
+inline hash_code hash_value(const Bitset<NumBits> &B) {
+  return hash_combine_range(B.Bits.begin(), B.Bits.end());
+}
+
 } // end namespace llvm
 
+namespace std {
+template <unsigned NumBits> struct hash<llvm::Bitset<NumBits>> {
+  size_t operator()(const llvm::Bitset<NumBits> &B) const {
+    return llvm::hash_combine_range(B.Bits.begin(), B.Bits.end());
+  }
+};
+} // end namespace std
+
 #endif
diff --git a/llvm/include/llvm/CodeGen/RDFLiveness.h b/llvm/include/llvm/CodeGen/RDFLiveness.h
index fe1034f9b6f8e..bc78b25b36177 100644
--- a/llvm/include/llvm/CodeGen/RDFLiveness.h
+++ b/llvm/include/llvm/CodeGen/RDFLiveness.h
@@ -44,7 +44,7 @@ namespace std {
 template <> struct hash<llvm::rdf::detail::NodeRef> {
   std::size_t operator()(llvm::rdf::detail::NodeRef R) const {
     return std::hash<llvm::rdf::NodeId>{}(R.first) ^
-           std::hash<llvm::LaneBitmask::Type>{}(R.second.getAsInteger());
+           std::hash<llvm::LaneBitmask>{}(R.second);
   }
 };
 
diff --git a/llvm/include/llvm/CodeGen/RDFRegisters.h b/llvm/include/llvm/CodeGen/RDFRegisters.h
index 48e1e3487f11f..89ca56b32d3e6 100644
--- a/llvm/include/llvm/CodeGen/RDFRegisters.h
+++ b/llvm/include/llvm/CodeGen/RDFRegisters.h
@@ -125,8 +125,7 @@ struct RegisterRef {
   }
 
   size_t hash() const {
-    return std::hash<RegisterId>{}(Id) ^
-           std::hash<LaneBitmask::Type>{}(Mask.getAsInteger());
+    return std::hash<RegisterId>{}(Id) ^ std::hash<LaneBitmask>{}(Mask);
   }
 
   static constexpr bool isRegId(RegisterId Id) {
diff --git a/llvm/include/llvm/MC/LaneBitmask.h b/llvm/include/llvm/MC/LaneBitmask.h
index c06ca7dd5b8fc..ba2e285d2f5ae 100644
--- a/llvm/include/llvm/MC/LaneBitmask.h
+++ b/llvm/include/llvm/MC/LaneBitmask.h
@@ -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;
+    T UpperWords = ~T(~0ULL) & V;
+    if (UpperWords.none())
+      for (int I = NumWordsIn64Bits - 1; I >= 0; --I)
+        Stream << format_hex_no_prefix(Data[I], HexWidth, true);
+    else
+      for (int I = Data.size() - 1; I >= 0; --I)
+        Stream << format_hex_no_prefix(Data[I], HexWidth, true);
   }
+};
+
+/// Create Printable object to print LaneBitmasks on a \ref raw_ostream.
+template <unsigned NumBits>
+inline Printable PrintLaneMask(detail::LaneBitmaskImpl<NumBits> LaneMask) {
+  return Printable(
+      [LaneMask](raw_ostream &OS) { OS << formatv("{0}", LaneMask); });
+}
+
+template <unsigned NumBits>
+inline hash_code hash_value(const detail::LaneBitmaskImpl<NumBits> &LM) {
+  return hash_value(static_cast<const Bitset<NumBits> &>(LM));
+}
 
 } // end namespace llvm
 
+namespace std {
+
+template <unsigned NumBits>
+struct hash<llvm::detail::LaneBitmaskImpl<NumBits>> {
+  size_t operator()(const llvm::detail::LaneBitmaskImpl<NumBits> &LM) const {
+    return hash<llvm::Bitset<NumBits>>{}(LM);
+  }
+};
+
+} // end namespace std
+
 #endif // LLVM_MC_LANEBITMASK_H
diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index 84b806ae81f39..39578172862d4 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -912,12 +912,20 @@ bool MIParser::parseBasicBlockLiveins(MachineBasicBlock &MBB) {
       if (Token.isNot(MIToken::IntegerLiteral) &&
           Token.isNot(MIToken::HexLiteral))
         return error("expected a lane mask");
-      static_assert(sizeof(LaneBitmask::Type) == sizeof(uint64_t),
-                    "Use correct get-function for lane mask");
-      LaneBitmask::Type V;
-      if (getUint64(V))
-        return error("invalid lane mask value");
-      Mask = LaneBitmask(V);
+
+      if (Token.is(MIToken::IntegerLiteral)) {
+        // Parse as integer literal (fits in 64 bits).
+        uint64_t V;
+        if (getUint64(V))
+          return error("invalid lane mask value");
+        Mask = LaneBitmask(V);
+      } else {
+        // Parse as hex literal (may be > 64 bits).
+        APInt A;
+        if (getHexUint(A))
+          return error("invalid lane mask value");
+        Mask = LaneBitmask(A);
+      }
       lex();
     }
     MBB.addLiveIn(Reg, Mask);
@@ -3117,12 +3125,21 @@ bool MIParser::parseLaneMaskOperand(MachineOperand &Dest) {
   // Parse lanemask.
   if (Token.isNot(MIToken::IntegerLiteral) && Token.isNot(MIToken::HexLiteral))
     return error("expected a valid lane mask value");
-  static_assert(sizeof(LaneBitmask::Type) == sizeof(uint64_t),
-                "Use correct get-function for lane mask.");
-  LaneBitmask::Type V;
-  if (getUint64(V))
-    return true;
-  LaneBitmask LaneMask(V);
+
+  LaneBitmask LaneMask;
+  if (Token.is(MIToken::IntegerLiteral)) {
+    // Parse as integer literal (fits in 64 bits).
+    uint64_t V;
+    if (getUint64(V))
+      return error("invalid lane mask value");
+    LaneMask = LaneBitmask(V);
+  } else {
+    // Parse as hex literal (may be > 64 bits).
+    APInt A;
+    if (getHexUint(A))
+      return error("invalid lane mask value");
+    LaneMask = LaneBitmask(A);
+  }
   lex();
 
   if (expectAndConsume(MIToken::rparen))
diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp
index ac1f201bc8b83..72ff3578dbb2a 100644
--- a/llvm/lib/CodeGen/MachineOperand.cpp
+++ b/llvm/lib/CodeGen/MachineOperand.cpp
@@ -464,7 +464,7 @@ hash_code llvm::hash_value(const MachineOperand &MO) {
     return hash_combine(MO.getType(), MO.getTargetFlags(), MO.getShuffleMask());
   case MachineOperand::MO_LaneMask:
     return hash_combine(MO.getType(), MO.getTargetFlags(),
-                        MO.getLaneMask().getAsInteger());
+                        hash_value(MO.getLaneMask()));
   }
   llvm_unreachable("Invalid machine operand type");
 }
diff --git a/llvm/lib/CodeGen/MachineStableHash.cpp b/llvm/lib/CodeGen/MachineStableHash.cpp
index 2f5f5aeccb2e4..f777d5fe7722b 100644
--- a/llvm/lib/CodeGen/MachineStableHash.cpp
+++ b/llvm/lib/CodeGen/MachineStableHash.cpp
@@ -166,8 +166,12 @@ stable_hash llvm::stableHashValue(const MachineOperand &MO) {
                                stable_hash_name(SymbolName));
   }
   case MachineOperand::MO_LaneMask: {
+    // Use the deterministic printed representation for stable hashing.
+    std::string Str;
+    raw_string_ostream OS(Str);
+    OS << PrintLaneMask(MO.getLaneMask());
     return stable_hash_combine(MO.getType(), MO.getTargetFlags(),
-                               MO.getLaneMask().getAsInteger());
+                               stable_hash_name(OS.str()));
   }
   case MachineOperand::MO_CFIIndex:
     return stable_hash_combine(MO.getType(), MO.getTargetFlags(),
diff --git a/llvm/lib/CodeGen/RDFRegisters.cpp b/llvm/lib/CodeGen/RDFRegisters.cpp
index ee3e531c6fd5a..55d059ee7092e 100644
--- a/llvm/lib/CodeGen/RDFRegisters.cpp
+++ b/llvm/lib/CodeGen/RDFRegisters.cpp
@@ -407,11 +407,6 @@ raw_ostream &operator<<(raw_ostream &OS, const PrintLaneMaskShort &P) {
   if (P.Mask.none())
     return OS << ":*none*";
 
-  LaneBitmask::Type Val = P.Mask.getAsInteger();
-  if ((Val & 0xffff) == Val)
-    return OS << ':' << format("%04llX", Val);
-  if ((Val & 0xffffffff) == Val)
-    return OS << ':' << format("%08llX", Val);
   return OS << ':' << PrintLaneMask(P.Mask);
 }
 
diff --git a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
index e00ce4f167c3f..b424858f95ea6 100644
--- a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
@@ -332,11 +332,11 @@ SIRegisterInfo::SIRegisterInfo(const GCNSubtarget &ST)
                             ST.getHwMode(MCSubtargetInfo::HwMode_RegInfo)),
       ST(ST), SpillSGPRToVGPR(EnableSpillSGPRToVGPR), isWave32(ST.isWave32()) {
 
-  assert(getSubRegIndexLaneMask(AMDGPU::sub0).getAsInteger() == 3 &&
-         getSubRegIndexLaneMask(AMDGPU::sub31).getAsInteger() == (3ULL << 62) &&
+  assert(getSubRegIndexLaneMask(AMDGPU::sub0) == LaneBitmask(3) &&
+         getSubRegIndexLaneMask(AMDGPU::sub31) == LaneBitmask(3ULL << 62) &&
          (getSubRegIndexLaneMask(AMDGPU::lo16) |
-          getSubRegIndexLaneMask(AMDGPU::hi16)).getAsInteger() ==
-           getSubRegIndexLaneMask(AMDGPU::sub0).getAsInteger() &&
+          getSubRegIndexLaneMask(AMDGPU::hi16)) ==
+             getSubRegIndexLaneMask(AMDGPU::sub0) &&
          "getNumCoveredRegs() will not work with generated subreg masks!");
 
   RegPressureIgnoredUnits.resize(getNumRegUnits());
diff --git a/llvm/lib/Target/AMDGPU/SIRegisterInfo.h b/llvm/lib/Target/AMDGPU/SIRegisterInfo.h
index 9d1a9eae75020..e741b14c07b1a 100644
--- a/llvm/lib/Target/AMDGPU/SIRegisterInfo.h
+++ b/llvm/lib/Target/AMDGPU/SIRegisterInfo.h
@@ -404,11 +404,10 @@ class SIRegisterInfo final : public AMDGPUGenRegisterInfo {
   static unsigned getNumCoveredRegs(LaneBitmask LM) {
     // The assumption is that every lo16 subreg is an even bit and every hi16
     // is an adjacent odd bit or vice versa.
-    uint64_t Mask = LM.getAsInteger();
-    uint64_t Even = Mask & 0xAAAAAAAAAAAAAAAAULL;
-    Mask = (Even >> 1) | Mask;
-    uint64_t Odd = Mask & 0x5555555555555555ULL;
-    return llvm::popcount(Odd);
+    LaneBitmask Even = LM & LaneBitmask(0xAAAAAAAAAAAAAAAAULL);
+    LaneBitmask Mask = (Even >> 1) | LM;
+    LaneBitmask Odd = Mask & LaneBitmask(0x5555555555555555ULL);
+    return Odd.count();
   }
 
   // \returns a DWORD offset of a \p SubReg
diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt
index 709017380fa4e..7324c0ba9b5f6 100644
--- a/llvm/unittest...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list