[llvm] [TableGen] Handle the non-`CoveredBySubRegs` case in `computeRegUnitLaneMasks` (PR #223653)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 23 05:59:52 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: Igor Wodiany (IgWod)
<details>
<summary>Changes</summary>
Currently `computeRegUnitLaneMasks` ignores non-leaf subregisters as "their lane masks are fully covered by the leaf subregisters". However this only holds when `CoveredBySubRegs` is set to true. Otherwise it causes subregisters with `CoveredBySubRegs` set to false to have missing lanes.
For example removing `hi16` from SGPR in AMDGPU causes `sub1` to have a lane mask of `0x8`, which is equal to `sub1_lo16`, whereas the correct mask should be `0xC` as although `hi16` is not addressable `sub1` covers both halves.
This patch adds a logic that identifies unclaimed lanes and backfills them into appropriate subregister units.
This is an alternative to #<!-- -->201550.
Assisted-by: Claude Code
---
Full diff: https://github.com/llvm/llvm-project/pull/223653.diff
2 Files Affected:
- (added) llvm/test/TableGen/RegisterInfoEmitter-noncovered-subreg-lanemask.td (+71)
- (modified) llvm/utils/TableGen/Common/CodeGenRegisters.cpp (+28-1)
``````````diff
diff --git a/llvm/test/TableGen/RegisterInfoEmitter-noncovered-subreg-lanemask.td b/llvm/test/TableGen/RegisterInfoEmitter-noncovered-subreg-lanemask.td
new file mode 100644
index 00000000000000..4b7bf184472765
--- /dev/null
+++ b/llvm/test/TableGen/RegisterInfoEmitter-noncovered-subreg-lanemask.td
@@ -0,0 +1,71 @@
+// RUN: llvm-tblgen -gen-register-info -I %p/../../include %s | FileCheck %s
+
+// Checks that computeRegUnitLaneMasks() handles noncovered subregs correctly.
+include "llvm/Target/Target.td"
+
+def TestTarget : Target;
+
+def lo16 : SubRegIndex<16, 0>;
+def hi16 : SubRegIndex<16, 16>;
+def sub0 : SubRegIndex<32, 0>;
+def sub1 : SubRegIndex<32, 32>;
+
+// Fully-covered 32-bit register. This forces sub0/sub1 to be assigned
+// lanes wide enough to cover both lo16 and hi16 of a 32-bit slot.
+def R2_LO16 : Register<"r2l">;
+def R2_HI16 : Register<"r2h">;
+def R2 : RegisterWithSubRegs<"r2", [R2_LO16, R2_HI16]> {
+ let SubRegIndices = [lo16, hi16];
+ let CoveredBySubRegs = 1;
+}
+
+def R3_LO16 : Register<"r3l">;
+def R3_HI16 : Register<"r3h">;
+def R3 : RegisterWithSubRegs<"r3", [R3_LO16, R3_HI16]> {
+ let SubRegIndices = [lo16, hi16];
+ let CoveredBySubRegs = 1;
+}
+
+def R2R3 : RegisterWithSubRegs<"r2r3", [R2, R3]> {
+ let SubRegIndices = [sub0, sub1];
+ let CoveredBySubRegs = 1;
+}
+
+// Case with hi16 removed
+def R0_LO16 : Register<"r0l">;
+def R0 : RegisterWithSubRegs<"r0", [R0_LO16]> {
+ let SubRegIndices = [lo16];
+ let CoveredBySubRegs = 0;
+}
+
+def R1_LO16 : Register<"r1l">;
+def R1 : RegisterWithSubRegs<"r1", [R1_LO16]> {
+ let SubRegIndices = [lo16];
+ let CoveredBySubRegs = 0;
+}
+
+def R0R1 : RegisterWithSubRegs<"r0r1", [R0, R1]> {
+ let SubRegIndices = [sub0, sub1];
+ let CoveredBySubRegs = 1;
+}
+
+def TestRC32 : RegisterClass<"Test", [i32], 32, (add R0, R1, R2, R3)>;
+def TestRC64 : RegisterClass<"Test", [i64], 64, (add R0R1, R2R3)>;
+
+// CHECK: extern const LaneBitmask TestTargetLaneMaskLists[] = {
+// CHECK-NEXT: /* 0 */ LaneBitmask(0x0000000000000002), LaneBitmask(0x0000000000000001),
+// CHECK-NEXT: /* 2 */ LaneBitmask(0x0000000000000002),
+// CHECK-NEXT: /* 3 */ LaneBitmask(0x0000000000000002), LaneBitmask(0x0000000000000001), LaneBitmask(0x0000000000000004), LaneBitmask(0x0000000000000008),
+// CHECK-NEXT: /* 7 */ LaneBitmask(0x0000000000000003), LaneBitmask(0x000000000000000C),
+// CHECK-NEXT: /* 9 */ LaneBitmask(0xFFFFFFFFFFFFFFFF),
+// CHECK-NEXT: };
+
+// CHECK: extern const MCRegisterDesc TestTargetRegDesc[] = {
+// CHECK: { 3, 26, 6, 3, 36864, 7, 0, 0 },
+
+// CHECK: static const LaneBitmask TestTargetSubRegIndexLaneMaskTable[] = {
+// CHECK-NEXT: LaneBitmask::getAll(),
+// CHECK-NEXT: LaneBitmask(0x0000000000000001), // hi16
+// CHECK-NEXT: LaneBitmask(0x0000000000000002), // lo16
+// CHECK-NEXT: LaneBitmask(0x0000000000000003), // sub0
+// CHECK-NEXT: LaneBitmask(0x000000000000000C), // sub1
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
index 10a5a16feef787..e1675ab68d20c1 100644
--- a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
@@ -2378,7 +2378,8 @@ void CodeGenRegBank::computeRegUnitLaneMasks() {
const SubRegMap &SubRegs = Register.getSubRegs();
for (auto [SubRegIndex, SubReg] : SubRegs) {
// Ignore non-leaf subregisters, their lane masks are fully covered by
- // the leaf subregisters anyway.
+ // the leaf subregisters, unless the subregister is not CoveredBySubRegs -
+ // this is dealt with by the loop below.
if (!SubReg->getSubRegs().empty())
continue;
LaneBitmask LaneMask = SubRegIndex->LaneMask;
@@ -2398,6 +2399,32 @@ void CodeGenRegBank::computeRegUnitLaneMasks() {
assert(Found);
}
}
+
+ auto UnitMaskIdx = [&](unsigned SUI) {
+ unsigned U = 0;
+ for (unsigned RU : RegUnits) {
+ if (SUI == RU)
+ return U;
+ ++U;
+ }
+ llvm_unreachable("unit is not part of the register");
+ };
+
+ // A sub-register that is not CoveredBySubRegs may be missing lanes that
+ // none of its leaves account for. If left unclaimed, those lanes would
+ // not appear in any register unit's mask, making them invisible to
+ // interference and liveness queries. Backfill the missing lanes onto the
+ // sub-register's own native units.
+ for (auto [SubRegIndex, SubReg] : SubRegs) {
+ if (SubReg->CoveredBySubRegs || SubReg->getSubRegs().empty())
+ continue;
+ LaneBitmask Unclaimed = SubRegIndex->LaneMask;
+ for (unsigned SUI : SubReg->getNativeRegUnits())
+ Unclaimed &= ~RegUnitLaneMasks[UnitMaskIdx(SUI)];
+ for (unsigned SUI : SubReg->getNativeRegUnits())
+ RegUnitLaneMasks[UnitMaskIdx(SUI)] |= Unclaimed;
+ }
+
Register.setRegUnitLaneMasks(RegUnitLaneMasks);
}
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/223653
More information about the llvm-commits
mailing list