[llvm] [TableGen] Allow targets to enforce regunits assignment as intervals (PR #175823)

Ryan Mitchell via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 20 15:47:30 PST 2026


https://github.com/RyanRio updated https://github.com/llvm/llvm-project/pull/175823

>From f824fe8d90df41d16e6cc1398433cce959c95fa7 Mon Sep 17 00:00:00 2001
From: Ryan Mitchell <Ryan.Mitchell at amd.com>
Date: Tue, 13 Jan 2026 12:14:31 -0800
Subject: [PATCH 1/7] [TableGen] Allow targets to enforce regunits assignment
 as intervals

---
 llvm/include/llvm/MC/MCRegisterInfo.h         |  18 +++-
 llvm/include/llvm/Target/Target.td            |   5 +
 .../TableGen/regunit-intervals-impossible.td  |  35 ++++++
 llvm/test/TableGen/regunit-intervals.td       |  73 +++++++++++++
 .../TableGen/Common/CodeGenRegisters.cpp      | 100 ++++++++++++++++++
 llvm/utils/TableGen/Common/CodeGenRegisters.h |   9 ++
 llvm/utils/TableGen/RegisterInfoEmitter.cpp   |  17 +++
 7 files changed, 256 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/TableGen/regunit-intervals-impossible.td
 create mode 100644 llvm/test/TableGen/regunit-intervals.td

diff --git a/llvm/include/llvm/MC/MCRegisterInfo.h b/llvm/include/llvm/MC/MCRegisterInfo.h
index f4897b6a406fb..76ef62da3d35b 100644
--- a/llvm/include/llvm/MC/MCRegisterInfo.h
+++ b/llvm/include/llvm/MC/MCRegisterInfo.h
@@ -180,6 +180,7 @@ class LLVM_ABI MCRegisterInfo {
   unsigned NumSubRegIndices;                  // Number of subreg indices.
   const uint16_t *RegEncodingTable;           // Pointer to array of register
                                               // encodings.
+  const unsigned (*RegUnitIntervals)[2]; // Pointer to regunit interval table.
 
   unsigned L2DwarfRegsSize;
   unsigned EHL2DwarfRegsSize;
@@ -286,7 +287,8 @@ class LLVM_ABI MCRegisterInfo {
                           const int16_t *DL, const LaneBitmask *RUMS,
                           const char *Strings, const char *ClassStrings,
                           const uint16_t *SubIndices, unsigned NumIndices,
-                          const uint16_t *RET) {
+                          const uint16_t *RET,
+                          const unsigned (*RUI)[2] = nullptr) {
     Desc = D;
     NumRegs = NR;
     RAReg = RA;
@@ -302,6 +304,7 @@ class LLVM_ABI MCRegisterInfo {
     SubRegIndices = SubIndices;
     NumSubRegIndices = NumIndices;
     RegEncodingTable = RET;
+    RegUnitIntervals = RUI;
 
     // Initialize DWARF register mapping variables
     EHL2DwarfRegs = nullptr;
@@ -511,6 +514,19 @@ class LLVM_ABI MCRegisterInfo {
 
   /// Returns true if the two registers are equal or alias each other.
   bool regsOverlap(MCRegister RegA, MCRegister RegB) const;
+
+  /// Returns true if this target uses regunit intervals.
+  bool hasRegUnitIntervals() const { return RegUnitIntervals != nullptr; }
+
+  /// Returns an iterator range over all native regunits in the RegUnitInterval
+  /// table for \p Reg.
+  iota_range<unsigned> regunits_interval(MCRegister Reg) const {
+    assert(hasRegUnitIntervals() &&
+           "Target does not support regunit intervals");
+    assert(Reg.id() < NumRegs && "Invalid register number");
+    return seq<unsigned>(RegUnitIntervals[Reg.id()][0],
+                         RegUnitIntervals[Reg.id()][1]);
+  }
 };
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 315de55b75510..45ed2a674860b 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -1935,6 +1935,11 @@ class Target {
   // setting hasExtraDefRegAllocReq and hasExtraSrcRegAllocReq to 1
   // for all opcodes if this flag is set to 0.
   int AllowRegisterRenaming = 0;
+
+  // RegistersAreIntervals - Controls whether this target requires
+  // all Registers to have RegUnit intervals. Will attempt to reorder
+  // RegUnits to enforce this, and if a solution is not found, will error.
+  bit RegistersAreIntervals = 0;
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/test/TableGen/regunit-intervals-impossible.td b/llvm/test/TableGen/regunit-intervals-impossible.td
new file mode 100644
index 0000000000000..9b4f6561178b7
--- /dev/null
+++ b/llvm/test/TableGen/regunit-intervals-impossible.td
@@ -0,0 +1,35 @@
+// RUN: not llvm-tblgen -gen-register-info -I %p/../../include %s 2>&1 | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def TestInstrInfo : InstrInfo;
+def TestTarget : Target {
+  let InstructionSet = TestInstrInfo;
+  let RegistersAreIntervals = 1;
+}
+
+def sub_lo : SubRegIndex<32>;
+def sub_hi : SubRegIndex<32, 32>;
+
+let Namespace = "Test" in {
+  def R1 : Register<"r1">;  // unit 0
+  def R2 : Register<"r2">;  // unit 1
+  def R3 : Register<"r3">;  // unit 2
+
+  // First composite: units {0, 1} - contiguous, OK
+  def R1_R2 : Register<"r1_r2"> {
+    let SubRegs = [R1, R2];
+    let SubRegIndices = [sub_lo, sub_hi];
+  }
+
+  // Second composite: units {0, 2} - non-contiguous!
+  // Algorithm will swap 1 and 2, making R1_R2 not contiguous
+  def R1_R3 : Register<"r1_r3"> {
+    let SubRegs = [R1, R3];
+    let SubRegIndices = [sub_lo, sub_hi];
+  }
+}
+
+def GPR32 : RegisterClass<"Test", [i32], 32, (add R1, R2, R3, R4)>;
+
+// CHECK: error: Cannot enforce regunit intervals, final renumbering did not produce contiguous units for register R10_R11
diff --git a/llvm/test/TableGen/regunit-intervals.td b/llvm/test/TableGen/regunit-intervals.td
new file mode 100644
index 0000000000000..3d5dcd7c404dc
--- /dev/null
+++ b/llvm/test/TableGen/regunit-intervals.td
@@ -0,0 +1,73 @@
+// RUN: llvm-tblgen -gen-register-info -I %p/../../include %s | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def TestInstrInfo : InstrInfo;
+def TestTarget : Target {
+  let InstructionSet = TestInstrInfo;
+  let RegistersAreIntervals  = 1;
+}
+
+def sub_lo : SubRegIndex<32>;
+def sub_hi : SubRegIndex<32, 32>;
+
+let Namespace = "Test" in {
+  // Simple 32-bit registers (each gets 1 regunit)
+  def R0 : Register<"r0">;
+  def R1 : Register<"r1">;
+  def R2 : Register<"r2">;
+  def R3 : Register<"r3">;
+
+  // 64-bit register composed of R0:R1 (gets 2 regunits)
+  def R0_R1 : Register<"r0_r1"> {
+    let SubRegs = [R0, R1];
+    let SubRegIndices = [sub_lo, sub_hi];
+  }
+
+  // 64-bit register composed of R2:R3 (gets 2 regunits)
+  def R2_R3 : Register<"r2_r3"> {
+    let SubRegs = [R2, R3];
+    let SubRegIndices = [sub_lo, sub_hi];
+  }
+}
+
+// CHECK: extern const uint16_t TestRegUnitIntervals[][2] = {
+// CHECK-NEXT: { 0, 1 },
+// CHECK-NEXT: { 1, 2 },
+// CHECK-NEXT: { 2, 3 },
+// CHECK-NEXT: { 3, 4 },
+
+let Namespace = "Test" in {
+  def R4 : Register<"r4">;      // Gets unit 4
+  def R5 : Register<"r5">;      // Gets unit 5
+  def R6 : Register<"r6">;      // Gets unit 6
+  def R7 : Register<"r7">;      // Gets unit 7
+  
+  // This register skips R5, creating non-contiguous units {4, 6}
+  def R4_R6 : Register<"r4_r6"> {
+    let SubRegs = [R4, R6];
+    let SubRegIndices = [sub_lo, sub_hi];
+  }
+
+  // This register skips R6, creating non-contiguous units {5, 7}
+  def R5_R7 : Register<"r4_r6"> {
+    let SubRegs = [R5, R7];
+    let SubRegIndices = [sub_lo, sub_hi];
+  }
+}
+
+
+def GPR32 : RegisterClass<"Test", [i32], 32, (add R0, R1, R2, R3)>;
+def GPR64 : RegisterClass<"Test", [i64], 64, (add R0_R1, R2_R3)>;
+
+// Note R5 is assigned 6,7 so that R6 gets 5,6
+// CHECK-NEXT: { 4, 5 },
+// CHECK-NEXT: { 6, 7 },
+// CHECK-NEXT: { 5, 6 },
+// CHECK-NEXT: { 7, 8 },
+// All contiguous
+// CHECK-NEXT: { 0, 2 },
+// CHECK-NEXT: { 2, 4 },
+// CHECK-NEXT: { 4, 6 },
+// CHECK-NEXT: { 6, 8 },
+// CHECK-NEXT: };
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
index 65a2594859e69..bf1a2e767370d 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
@@ -1929,6 +1929,101 @@ void CodeGenRegBank::computeRegUnitWeights() {
   }
 }
 
+// Enforce that all registers are intervals of regunits if the target
+// requests this property. This will renumber regunits to ensure the
+// interval property holds, or error out if it cannot be satisfied.
+void CodeGenRegBank::enforceRegUnitIntervals() {
+  std::vector<const Record *> Targets =
+      Records.getAllDerivedDefinitions("Target");
+
+  if (Targets.empty())
+    return;
+
+  const Record *Target = Targets[0];
+  if (!Target->getValueAsBit("RegistersAreIntervals"))
+    return;
+
+  LLVM_DEBUG(dbgs() << "Enforcing regunit intervals for target\n");
+  std::vector<unsigned> RegUnitRenumbering(RegUnits.size(), ~0u);
+
+  // RegUnits that have been renumbered from X -> Y. Y is what is marked so that
+  // it doesn't create a chain of swaps.
+  SparseBitVector DontRenumberUnits;
+
+  auto GetRenumberedUnit = [&](unsigned RegUnit) -> unsigned {
+    if (RegUnitRenumbering[RegUnit] != ~0u)
+      return RegUnitRenumbering[RegUnit];
+    return RegUnit;
+  };
+
+  auto IsContiguous = [&](CodeGenRegister::RegUnitList &Units) -> bool {
+    unsigned LastUnit = Units.find_first();
+    for (auto ThisUnit : llvm::make_range(++Units.begin(), Units.end())) {
+      if (ThisUnit != LastUnit + 1)
+        return false;
+      LastUnit = ThisUnit;
+    }
+    return true;
+  };
+
+  // Process registers in definition order
+  for (CodeGenRegister &Reg : Registers) {
+    LLVM_DEBUG(dbgs() << "Processing register " << Reg.getName() << "\n");
+    const auto &Units = Reg.getNativeRegUnits();
+    if (Units.empty())
+      continue;
+    SparseBitVector RenumberedUnits;
+    // First renumber all the units for this register according to previous
+    // renumbering.
+    LLVM_DEBUG(dbgs() << "  Original (Renumbered) units:");
+    for (unsigned U : Units) {
+      LLVM_DEBUG(dbgs() << " " << U << "(" << GetRenumberedUnit(U) << "), ");
+      RenumberedUnits.set(GetRenumberedUnit(U));
+    }
+    LLVM_DEBUG(dbgs() << "\n");
+
+    unsigned LastUnit = RenumberedUnits.find_first();
+    for (auto ThisUnit :
+         llvm::make_range(++RenumberedUnits.begin(), RenumberedUnits.end())) {
+      if (ThisUnit != LastUnit + 1) {
+        if (DontRenumberUnits.test(LastUnit + 1)) {
+          PrintFatalError(
+              "Cannot enforce regunit intervals for register " + Reg.getName() +
+              ": unit " + Twine(LastUnit + 1) +
+              " (root: " + RegUnits[LastUnit + 1].Roots[0]->getName() +
+              ") has already been renumbered and cannot be swapped");
+        }
+        LLVM_DEBUG(dbgs() << "  Renumbering unit " << ThisUnit << " to "
+                          << (LastUnit + 1) << "\n");
+        RegUnitRenumbering[LastUnit + 1] = ThisUnit;
+        RegUnitRenumbering[ThisUnit] = LastUnit + 1;
+        DontRenumberUnits.set(LastUnit + 1);
+        ThisUnit = LastUnit + 1;
+      }
+      LastUnit = ThisUnit;
+    }
+  }
+
+  // Apply the renumbering to all registers
+  for (CodeGenRegister &Reg : Registers) {
+    CodeGenRegister::RegUnitList NewRegUnits;
+    for (unsigned OldUnit : Reg.getRegUnits())
+      NewRegUnits.set(GetRenumberedUnit(OldUnit));
+    Reg.setNewRegUnits(NewRegUnits);
+
+    CodeGenRegister::RegUnitList NewNativeUnits;
+    for (unsigned OldUnit : Reg.getNativeRegUnits())
+      NewNativeUnits.set(GetRenumberedUnit(OldUnit));
+    if (!IsContiguous(NewNativeUnits)) {
+      reportFatalInternalError("Cannot enforce regunit intervals, final "
+                               "renumbering did not produce contiguous units "
+                               "for register " +
+                               Reg.getName() + "\n");
+    }
+    Reg.NativeRegUnits = NewNativeUnits;
+  }
+}
+
 // Find a set in UniqueSets with the same elements as Set.
 // Return an iterator into UniqueSets.
 static std::vector<RegUnitSet>::const_iterator
@@ -2209,6 +2304,11 @@ void CodeGenRegBank::computeDerivedInfo() {
   computeRegUnitWeights();
   Records.getTimer().stopTimer();
 
+  // Enforce regunit intervals if requested by the target.
+  Records.getTimer().startTimer("Enforce regunit intervals");
+  enforceRegUnitIntervals();
+  Records.getTimer().stopTimer();
+
   // Compute a unique set of RegUnitSets. One for each RegClass and inferred
   // supersets for the union of overlapping sets.
   computeRegUnitSets();
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.h b/llvm/utils/TableGen/Common/CodeGenRegisters.h
index a3ad0b797a704..176cb9dafd22b 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.h
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.h
@@ -169,6 +169,8 @@ class CodeGenSubRegIndex {
 
 /// CodeGenRegister - Represents a register definition.
 class CodeGenRegister {
+  friend class CodeGenRegBank;
+
 public:
   const Record *TheDef;
   unsigned EnumValue;
@@ -257,6 +259,10 @@ class CodeGenRegister {
   // This is only valid after computeSubRegs() completes.
   const RegUnitList &getRegUnits() const { return RegUnits; }
 
+  void setNewRegUnits(const RegUnitList &NewRegUnits) {
+    RegUnits = NewRegUnits;
+  }
+
   ArrayRef<LaneBitmask> getRegUnitLaneMasks() const {
     return ArrayRef(RegUnitLaneMasks).slice(0, NativeRegUnits.count());
   }
@@ -693,6 +699,9 @@ class CodeGenRegBank {
   // Compute a weight for each register unit created during getSubRegs.
   void computeRegUnitWeights();
 
+  // Enforce that all registers are intervals of regunits if requested.
+  void enforceRegUnitIntervals();
+
   // Create a RegUnitSet for each RegClass and infer superclasses.
   void computeRegUnitSets();
 
diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
index 02fd8648302f1..552b32736509c 100644
--- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp
+++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
@@ -1044,6 +1044,23 @@ void RegisterInfoEmitter::runMCDesc(raw_ostream &OS, raw_ostream &MainOS,
   }
   OS << "};\n\n";
 
+  // Emit the table of register unit intervals.
+  if (Target.getTargetRecord()->getValueAsBit("RegistersAreIntervals")) {
+    OS << "extern const unsigned " << TargetName
+       << "RegUnitIntervals[][2] = {\n";
+    for (const auto &Reg : Regs) {
+      const auto &Units = Reg.getNativeRegUnits();
+      if (Units.empty()) {
+        OS << "  { 0, 0 },\n";
+      } else {
+        unsigned First = Units.find_first();
+        unsigned Last = Units.find_last();
+        OS << "  { " << First << ", " << Last + 1 << " },\n";
+      }
+    }
+    OS << "};\n\n";
+  }
+
   const auto &RegisterClasses = RegBank.getRegClasses();
 
   // Loop over all of the register classes... emitting each one.

>From 436b2ea38832ffee800358d99fe6ddc4679a0605 Mon Sep 17 00:00:00 2001
From: Ryan Mitchell <Ryan.Mitchell at amd.com>
Date: Mon, 19 Jan 2026 12:23:32 -0800
Subject: [PATCH 2/7] Test and build fixes

---
 llvm/test/TableGen/regunit-intervals-impossible.td | 2 +-
 llvm/test/TableGen/regunit-intervals.td            | 2 +-
 llvm/utils/TableGen/Common/CodeGenRegisters.cpp    | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/test/TableGen/regunit-intervals-impossible.td b/llvm/test/TableGen/regunit-intervals-impossible.td
index 9b4f6561178b7..ded63e183fd6a 100644
--- a/llvm/test/TableGen/regunit-intervals-impossible.td
+++ b/llvm/test/TableGen/regunit-intervals-impossible.td
@@ -30,6 +30,6 @@ let Namespace = "Test" in {
   }
 }
 
-def GPR32 : RegisterClass<"Test", [i32], 32, (add R1, R2, R3, R4)>;
+def GPR32 : RegisterClass<"Test", [i32], 32, (add R1, R2, R3)>;
 
 // CHECK: error: Cannot enforce regunit intervals, final renumbering did not produce contiguous units for register R10_R11
diff --git a/llvm/test/TableGen/regunit-intervals.td b/llvm/test/TableGen/regunit-intervals.td
index 3d5dcd7c404dc..906dcd71b9ebe 100644
--- a/llvm/test/TableGen/regunit-intervals.td
+++ b/llvm/test/TableGen/regunit-intervals.td
@@ -31,7 +31,7 @@ let Namespace = "Test" in {
   }
 }
 
-// CHECK: extern const uint16_t TestRegUnitIntervals[][2] = {
+// CHECK: extern const uint16_t TestTargetRegUnitIntervals[][2] = {
 // CHECK-NEXT: { 0, 1 },
 // CHECK-NEXT: { 1, 2 },
 // CHECK-NEXT: { 2, 3 },
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
index bf1a2e767370d..f7b085d285f38 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
@@ -1948,7 +1948,7 @@ void CodeGenRegBank::enforceRegUnitIntervals() {
 
   // RegUnits that have been renumbered from X -> Y. Y is what is marked so that
   // it doesn't create a chain of swaps.
-  SparseBitVector DontRenumberUnits;
+  SparseBitVector<> DontRenumberUnits;
 
   auto GetRenumberedUnit = [&](unsigned RegUnit) -> unsigned {
     if (RegUnitRenumbering[RegUnit] != ~0u)
@@ -1972,7 +1972,7 @@ void CodeGenRegBank::enforceRegUnitIntervals() {
     const auto &Units = Reg.getNativeRegUnits();
     if (Units.empty())
       continue;
-    SparseBitVector RenumberedUnits;
+    SparseBitVector<> RenumberedUnits;
     // First renumber all the units for this register according to previous
     // renumbering.
     LLVM_DEBUG(dbgs() << "  Original (Renumbered) units:");

>From 596009cf8f04326b6f4f90a5a321eaa6e4478926 Mon Sep 17 00:00:00 2001
From: Ryan Mitchell <Ryan.Mitchell at amd.com>
Date: Mon, 19 Jan 2026 21:22:27 -0800
Subject: [PATCH 3/7] Use CodeGenTarget to access bool

---
 llvm/include/llvm/Target/Target.td            | 14 +++++++++++---
 .../TableGen/regunit-intervals-impossible.td  |  2 +-
 llvm/test/TableGen/regunit-intervals.td       |  2 +-
 .../TableGen/Common/CodeGenRegisters.cpp      | 19 +++++++------------
 llvm/utils/TableGen/Common/CodeGenTarget.cpp  |  4 ++++
 llvm/utils/TableGen/Common/CodeGenTarget.h    |  5 +++++
 6 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index 45ed2a674860b..c06d7885a4cea 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -1936,9 +1936,17 @@ class Target {
   // for all opcodes if this flag is set to 0.
   int AllowRegisterRenaming = 0;
 
-  // RegistersAreIntervals - Controls whether this target requires
-  // all Registers to have RegUnit intervals. Will attempt to reorder
-  // RegUnits to enforce this, and if a solution is not found, will error.
+  // RegistersAreIntervals - Controls whether registers units must form
+  // contiguous intervals for each register.
+  //
+  // Specifically, for each register, its register units must be numbered
+  // consecutively (e.g, units 5,6,7 rather than 5,7,9). If enabled,
+  // CodeGenRegisters will enforce this property and attempt to fix violations
+  // that can be resolved by rearranging regunits. Unresolved violations will
+  // result in a fatal error.
+  //
+  // This enables interval-based representation of register units, which can
+  // improve data structure representations in target backends.
   bit RegistersAreIntervals = 0;
 }
 
diff --git a/llvm/test/TableGen/regunit-intervals-impossible.td b/llvm/test/TableGen/regunit-intervals-impossible.td
index ded63e183fd6a..5d40b3bc512ef 100644
--- a/llvm/test/TableGen/regunit-intervals-impossible.td
+++ b/llvm/test/TableGen/regunit-intervals-impossible.td
@@ -32,4 +32,4 @@ let Namespace = "Test" in {
 
 def GPR32 : RegisterClass<"Test", [i32], 32, (add R1, R2, R3)>;
 
-// CHECK: error: Cannot enforce regunit intervals, final renumbering did not produce contiguous units for register R10_R11
+// CHECK: LLVM ERROR: Cannot enforce regunit intervals, final renumbering did not produce contiguous units for register R1_R2
diff --git a/llvm/test/TableGen/regunit-intervals.td b/llvm/test/TableGen/regunit-intervals.td
index 906dcd71b9ebe..a78f62836a7bc 100644
--- a/llvm/test/TableGen/regunit-intervals.td
+++ b/llvm/test/TableGen/regunit-intervals.td
@@ -31,7 +31,7 @@ let Namespace = "Test" in {
   }
 }
 
-// CHECK: extern const uint16_t TestTargetRegUnitIntervals[][2] = {
+// CHECK: extern const unsigned TestTargetRegUnitIntervals[][2] = {
 // CHECK-NEXT: { 0, 1 },
 // CHECK-NEXT: { 1, 2 },
 // CHECK-NEXT: { 2, 3 },
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
index f7b085d285f38..226f2178a066f 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "CodeGenRegisters.h"
+#include "CodeGenTarget.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/DenseMap.h"
@@ -1933,14 +1934,8 @@ void CodeGenRegBank::computeRegUnitWeights() {
 // requests this property. This will renumber regunits to ensure the
 // interval property holds, or error out if it cannot be satisfied.
 void CodeGenRegBank::enforceRegUnitIntervals() {
-  std::vector<const Record *> Targets =
-      Records.getAllDerivedDefinitions("Target");
-
-  if (Targets.empty())
-    return;
-
-  const Record *Target = Targets[0];
-  if (!Target->getValueAsBit("RegistersAreIntervals"))
+  CodeGenTarget Target(Records);
+  if (!Target.getRegistersAreIntervals())
     return;
 
   LLVM_DEBUG(dbgs() << "Enforcing regunit intervals for target\n");
@@ -2015,10 +2010,10 @@ void CodeGenRegBank::enforceRegUnitIntervals() {
     for (unsigned OldUnit : Reg.getNativeRegUnits())
       NewNativeUnits.set(GetRenumberedUnit(OldUnit));
     if (!IsContiguous(NewNativeUnits)) {
-      reportFatalInternalError("Cannot enforce regunit intervals, final "
-                               "renumbering did not produce contiguous units "
-                               "for register " +
-                               Reg.getName() + "\n");
+      PrintFatalError("Cannot enforce regunit intervals, final "
+                      "renumbering did not produce contiguous units "
+                      "for register " +
+                      Reg.getName() + "\n");
     }
     Reg.NativeRegUnits = NewNativeUnits;
   }
diff --git a/llvm/utils/TableGen/Common/CodeGenTarget.cpp b/llvm/utils/TableGen/Common/CodeGenTarget.cpp
index cca318a6e2047..eb271507bd6cb 100644
--- a/llvm/utils/TableGen/Common/CodeGenTarget.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenTarget.cpp
@@ -119,6 +119,10 @@ bool CodeGenTarget::getAllowRegisterRenaming() const {
   return TargetRec->getValueAsInt("AllowRegisterRenaming");
 }
 
+bool CodeGenTarget::getRegistersAreIntervals() const {
+  return TargetRec->getValueAsInt("RegistersAreIntervals");
+}
+
 /// getAsmParser - Return the AssemblyParser definition for this target.
 ///
 const Record *CodeGenTarget::getAsmParser() const {
diff --git a/llvm/utils/TableGen/Common/CodeGenTarget.h b/llvm/utils/TableGen/Common/CodeGenTarget.h
index dc1740b174148..482816f774df9 100644
--- a/llvm/utils/TableGen/Common/CodeGenTarget.h
+++ b/llvm/utils/TableGen/Common/CodeGenTarget.h
@@ -100,6 +100,11 @@ class CodeGenTarget {
   ///
   bool getAllowRegisterRenaming() const;
 
+  /// getRegistersAreIntervals - Return the RegistersAreIntervals flag value for
+  /// this target.
+  ///
+  bool getRegistersAreIntervals() const;
+
   /// getAsmParser - Return the AssemblyParser definition for this target.
   ///
   const Record *getAsmParser() const;

>From 95dda86274dc3621d64b8af21532e96160c5d5ef Mon Sep 17 00:00:00 2001
From: Ryan Mitchell <Ryan.Mitchell at amd.com>
Date: Mon, 19 Jan 2026 21:35:19 -0800
Subject: [PATCH 4/7] Make field int to match other fields

---
 llvm/include/llvm/Target/Target.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index c06d7885a4cea..abe3a7d1701ee 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -1947,7 +1947,7 @@ class Target {
   //
   // This enables interval-based representation of register units, which can
   // improve data structure representations in target backends.
-  bit RegistersAreIntervals = 0;
+  int RegistersAreIntervals = 0;
 }
 
 //===----------------------------------------------------------------------===//

>From dd6d7867b6b2ca741f7e78818e7cda118504ecc8 Mon Sep 17 00:00:00 2001
From: Ryan Mitchell <Ryan.Mitchell at amd.com>
Date: Mon, 19 Jan 2026 21:52:51 -0800
Subject: [PATCH 5/7] Fix missed use of getRegistersAreIntervals

---
 llvm/utils/TableGen/RegisterInfoEmitter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
index 552b32736509c..530a075a793ba 100644
--- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp
+++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
@@ -1045,7 +1045,7 @@ void RegisterInfoEmitter::runMCDesc(raw_ostream &OS, raw_ostream &MainOS,
   OS << "};\n\n";
 
   // Emit the table of register unit intervals.
-  if (Target.getTargetRecord()->getValueAsBit("RegistersAreIntervals")) {
+  if (Target.getRegistersAreIntervals()) {
     OS << "extern const unsigned " << TargetName
        << "RegUnitIntervals[][2] = {\n";
     for (const auto &Reg : Regs) {

>From bfd01267c8e92745d1deaca9b98d054ec7c76789 Mon Sep 17 00:00:00 2001
From: Ryan Mitchell <Ryan.Mitchell at amd.com>
Date: Mon, 19 Jan 2026 23:15:39 -0800
Subject: [PATCH 6/7] Fix imposssible case test

---
 llvm/test/TableGen/regunit-intervals-impossible.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/TableGen/regunit-intervals-impossible.td b/llvm/test/TableGen/regunit-intervals-impossible.td
index 5d40b3bc512ef..44c5fba0f7043 100644
--- a/llvm/test/TableGen/regunit-intervals-impossible.td
+++ b/llvm/test/TableGen/regunit-intervals-impossible.td
@@ -32,4 +32,4 @@ let Namespace = "Test" in {
 
 def GPR32 : RegisterClass<"Test", [i32], 32, (add R1, R2, R3)>;
 
-// CHECK: LLVM ERROR: Cannot enforce regunit intervals, final renumbering did not produce contiguous units for register R1_R2
+// CHECK: error: Cannot enforce regunit intervals, final renumbering did not produce contiguous units for register R1_R2

>From b49102a2ccebe9788de07a19fb3c8dd64c335cca Mon Sep 17 00:00:00 2001
From: Ryan Mitchell <Ryan.Mitchell at amd.com>
Date: Tue, 20 Jan 2026 15:47:14 -0800
Subject: [PATCH 7/7] Pass bool from target

---
 .../TableGen/Common/CodeGenRegisters.cpp      | 43 ++++++++++---------
 llvm/utils/TableGen/Common/CodeGenRegisters.h |  5 ++-
 llvm/utils/TableGen/Common/CodeGenTarget.cpp  |  3 +-
 llvm/utils/TableGen/RegisterInfoEmitter.cpp   |  2 +-
 4 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
index 226f2178a066f..2c878ee0d7bf7 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
@@ -12,7 +12,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "CodeGenRegisters.h"
-#include "CodeGenTarget.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/DenseMap.h"
@@ -1126,8 +1125,10 @@ CodeGenRegisterCategory::CodeGenRegisterCategory(CodeGenRegBank &RegBank,
 //===----------------------------------------------------------------------===//
 
 CodeGenRegBank::CodeGenRegBank(const RecordKeeper &Records,
-                               const CodeGenHwModes &Modes)
-    : Records(Records), CGH(Modes) {
+                               const CodeGenHwModes &Modes,
+                               const bool RegistersAreIntervals)
+    : Records(Records), CGH(Modes),
+      RegistersAreIntervals(RegistersAreIntervals) {
   // Configure register Sets to understand register classes and tuples.
   Sets.addFieldExpander("RegisterClass", "MemberList");
   Sets.addFieldExpander("CalleeSavedRegs", "SaveList");
@@ -1930,12 +1931,23 @@ void CodeGenRegBank::computeRegUnitWeights() {
   }
 }
 
+// isContiguous is a enforceRegUnitIntervals helper that returns true if all
+// units in Units form a contiguous interval.
+static bool isContiguous(const CodeGenRegister::RegUnitList &Units) {
+  unsigned LastUnit = Units.find_first();
+  for (auto ThisUnit : llvm::make_range(++Units.begin(), Units.end())) {
+    if (ThisUnit != LastUnit + 1)
+      return false;
+    LastUnit = ThisUnit;
+  }
+  return true;
+}
+
 // Enforce that all registers are intervals of regunits if the target
 // requests this property. This will renumber regunits to ensure the
 // interval property holds, or error out if it cannot be satisfied.
 void CodeGenRegBank::enforceRegUnitIntervals() {
-  CodeGenTarget Target(Records);
-  if (!Target.getRegistersAreIntervals())
+  if (!RegistersAreIntervals)
     return;
 
   LLVM_DEBUG(dbgs() << "Enforcing regunit intervals for target\n");
@@ -1946,21 +1958,12 @@ void CodeGenRegBank::enforceRegUnitIntervals() {
   SparseBitVector<> DontRenumberUnits;
 
   auto GetRenumberedUnit = [&](unsigned RegUnit) -> unsigned {
-    if (RegUnitRenumbering[RegUnit] != ~0u)
-      return RegUnitRenumbering[RegUnit];
+    if (unsigned RenumberedUnit = RegUnitRenumbering[RegUnit];
+        RenumberedUnit != ~0u)
+      return RenumberedUnit;
     return RegUnit;
   };
 
-  auto IsContiguous = [&](CodeGenRegister::RegUnitList &Units) -> bool {
-    unsigned LastUnit = Units.find_first();
-    for (auto ThisUnit : llvm::make_range(++Units.begin(), Units.end())) {
-      if (ThisUnit != LastUnit + 1)
-        return false;
-      LastUnit = ThisUnit;
-    }
-    return true;
-  };
-
   // Process registers in definition order
   for (CodeGenRegister &Reg : Registers) {
     LLVM_DEBUG(dbgs() << "Processing register " << Reg.getName() << "\n");
@@ -1983,7 +1986,7 @@ void CodeGenRegBank::enforceRegUnitIntervals() {
       if (ThisUnit != LastUnit + 1) {
         if (DontRenumberUnits.test(LastUnit + 1)) {
           PrintFatalError(
-              "Cannot enforce regunit intervals for register " + Reg.getName() +
+              "cannot enforce regunit intervals for register " + Reg.getName() +
               ": unit " + Twine(LastUnit + 1) +
               " (root: " + RegUnits[LastUnit + 1].Roots[0]->getName() +
               ") has already been renumbered and cannot be swapped");
@@ -2009,8 +2012,8 @@ void CodeGenRegBank::enforceRegUnitIntervals() {
     CodeGenRegister::RegUnitList NewNativeUnits;
     for (unsigned OldUnit : Reg.getNativeRegUnits())
       NewNativeUnits.set(GetRenumberedUnit(OldUnit));
-    if (!IsContiguous(NewNativeUnits)) {
-      PrintFatalError("Cannot enforce regunit intervals, final "
+    if (!isContiguous(NewNativeUnits)) {
+      PrintFatalError("cannot enforce regunit intervals, final "
                       "renumbering did not produce contiguous units "
                       "for register " +
                       Reg.getName() + "\n");
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.h b/llvm/utils/TableGen/Common/CodeGenRegisters.h
index 176cb9dafd22b..1e9a5feb96bd1 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.h
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.h
@@ -618,6 +618,8 @@ class CodeGenRegBank {
 
   const CodeGenHwModes &CGH;
 
+  const bool RegistersAreIntervals;
+
   std::deque<CodeGenSubRegIndex> SubRegIndices;
   DenseMap<const Record *, CodeGenSubRegIndex *> Def2SubRegIdx;
 
@@ -723,7 +725,8 @@ class CodeGenRegBank {
   void printRegUnitNames(ArrayRef<unsigned> Units) const;
 
 public:
-  CodeGenRegBank(const RecordKeeper &, const CodeGenHwModes &);
+  CodeGenRegBank(const RecordKeeper &, const CodeGenHwModes &,
+                 const bool RegistersAreIntervals);
   CodeGenRegBank(CodeGenRegBank &) = delete;
 
   SetTheory &getSets() { return Sets; }
diff --git a/llvm/utils/TableGen/Common/CodeGenTarget.cpp b/llvm/utils/TableGen/Common/CodeGenTarget.cpp
index eb271507bd6cb..bce115cafaab2 100644
--- a/llvm/utils/TableGen/Common/CodeGenTarget.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenTarget.cpp
@@ -166,7 +166,8 @@ const Record *CodeGenTarget::getAsmWriter() const {
 
 CodeGenRegBank &CodeGenTarget::getRegBank() const {
   if (!RegBank)
-    RegBank = std::make_unique<CodeGenRegBank>(Records, getHwModes());
+    RegBank = std::make_unique<CodeGenRegBank>(Records, getHwModes(),
+                                               getRegistersAreIntervals());
   return *RegBank;
 }
 
diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
index 530a075a793ba..548c1503f4647 100644
--- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp
+++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp
@@ -1048,7 +1048,7 @@ void RegisterInfoEmitter::runMCDesc(raw_ostream &OS, raw_ostream &MainOS,
   if (Target.getRegistersAreIntervals()) {
     OS << "extern const unsigned " << TargetName
        << "RegUnitIntervals[][2] = {\n";
-    for (const auto &Reg : Regs) {
+    for (const CodeGenRegister &Reg : Regs) {
       const auto &Units = Reg.getNativeRegUnits();
       if (Units.empty()) {
         OS << "  { 0, 0 },\n";



More information about the llvm-commits mailing list