[llvm] [TableGen] Handle the non-`CoveredBySubRegs` case in `computeRegUnitLaneMasks` (PR #223653)

Igor Wodiany via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 15 03:54:18 PDT 2026


https://github.com/IgWod created https://github.com/llvm/llvm-project/pull/223653

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.

Assisted-by: Claude Code

>From 72ef80d49f947a62b6c658517b455829e70800e1 Mon Sep 17 00:00:00 2001
From: Igor Wodiany <igor.wodiany at amd.com>
Date: Tue, 15 Sep 2026 11:38:13 +0100
Subject: [PATCH] [TableGen] Handle the non-`CoveredBySubRegs` case in
 `computeRegUnitLaneMasks`

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.
---
 .../issue197467-virt-reg-rewrite-failure.mir  | 83 +++++++++++++++++++
 .../TableGen/Common/CodeGenRegisters.cpp      | 29 ++++++-
 2 files changed, 111 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/issue197467-virt-reg-rewrite-failure.mir

diff --git a/llvm/test/CodeGen/AMDGPU/issue197467-virt-reg-rewrite-failure.mir b/llvm/test/CodeGen/AMDGPU/issue197467-virt-reg-rewrite-failure.mir
new file mode 100644
index 00000000000000..f28b600ca5fb8e
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/issue197467-virt-reg-rewrite-failure.mir
@@ -0,0 +1,83 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=amdgpu9.0a-amd-amdhsa -start-before=greedy -stop-after=virtregrewriter %s -o - | FileCheck %s
+
+# Check VirtRegRewriter does not crashed with "cannot represent required
+# subregister defs". Regression caused by #197467.
+
+---
+name:            issue197467
+tracksRegLiveness: true
+registers:
+  - { id: 5, class: sreg_32, preferred-register: '%3' }
+machineFunctionInfo:
+  scratchRSrcReg:    '$sgpr0_sgpr1_sgpr2_sgpr3'
+  frameOffsetReg:    '$sgpr33'
+  stackPtrOffsetReg: '$sgpr32'
+  returnsVoid:       true
+body:             |
+  ; CHECK-LABEL: name: issue197467
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   renamable $sgpr4_sgpr5 = S_MOV_B64_IMM_PSEUDO 1900
+  ; CHECK-NEXT:   renamable $sgpr6 = S_MOV_B32 1092616192
+  ; CHECK-NEXT:   renamable $vcc = S_AND_B64 $exec, -1, implicit-def dead $scc
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.1(0x7c000000), %bb.2(0x04000000)
+  ; CHECK-NEXT:   liveins: $vcc, $sgpr6, $sgpr4_sgpr5:0x000000000000000F
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[V_CVT_F32_U32_e32_:%[0-9]+]]:vgpr_32 = V_CVT_F32_U32_e32 $sgpr4, implicit $mode, implicit $exec
+  ; CHECK-NEXT:   renamable $sgpr5 = S_MUL_I32 renamable $sgpr4, 246
+  ; CHECK-NEXT:   [[V_MUL_F32_e32_:%[0-9]+]]:vgpr_32 = nofpexcept V_MUL_F32_e32 1036831949, [[V_CVT_F32_U32_e32_]], implicit $mode, implicit $exec
+  ; CHECK-NEXT:   [[V_TRUNC_F32_e32_:%[0-9]+]]:vgpr_32 = nofpexcept V_TRUNC_F32_e32 [[V_MUL_F32_e32_]], implicit $mode, implicit $exec
+  ; CHECK-NEXT:   [[V_CVT_U32_F32_e32_:%[0-9]+]]:vgpr_32 = nofpexcept V_CVT_U32_F32_e32 [[V_TRUNC_F32_e32_]], implicit $mode, implicit $exec
+  ; CHECK-NEXT:   [[V_MAD_F32_e64_:%[0-9]+]]:vgpr_32 = V_MAD_F32_e64 1, [[V_TRUNC_F32_e32_]], 0, $sgpr6, 0, [[V_CVT_F32_U32_e32_]], 0, 0, implicit $mode, implicit $exec
+  ; CHECK-NEXT:   renamable $sgpr8_sgpr9 = nofpexcept V_CMP_LE_F32_e64 0, $sgpr6, 2, [[V_MAD_F32_e64_]], 0, implicit $mode, implicit $exec
+  ; CHECK-NEXT:   S_CMP_LG_U64 killed renamable $sgpr8_sgpr9, 0, implicit-def $scc
+  ; CHECK-NEXT:   renamable $sgpr7 = V_READFIRSTLANE_B32 [[V_CVT_U32_F32_e32_]], implicit $exec
+  ; CHECK-NEXT:   renamable $sgpr7 = S_ADDC_U32 killed renamable $sgpr7, 0, implicit-def dead $scc, implicit $scc
+  ; CHECK-NEXT:   renamable $sgpr5 = S_OR_B32 killed renamable $sgpr5, killed renamable $sgpr4, implicit-def dead $scc
+  ; CHECK-NEXT:   renamable $sgpr4 = S_AND_B32 killed renamable $sgpr7, 2047, implicit-def dead $scc
+  ; CHECK-NEXT:   renamable $sgpr5 = S_OR_B32 killed renamable $sgpr5, 1, implicit-def dead $scc
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:av_32 = COPY killed renamable $sgpr5
+  ; CHECK-NEXT:   BUFFER_STORE_BYTE_OFFSET [[COPY]], $sgpr0_sgpr1_sgpr2_sgpr3, 0, 0, 0, 0, implicit $exec :: (store (s8) into `ptr addrspace(5) null`, addrspace 5)
+  ; CHECK-NEXT:   S_CBRANCH_VCCNZ %bb.1, implicit $vcc
+  ; CHECK-NEXT:   S_BRANCH %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   SI_RETURN
+  bb.0:
+    successors: %bb.1(0x80000000)
+
+    %0:sreg_64 = S_MOV_B64_IMM_PSEUDO 1900
+    %1:sgpr_32 = S_MOV_B32 1092616192
+    %2:sreg_64 = S_AND_B64 $exec, -1, implicit-def dead $scc
+
+  bb.1:
+    successors: %bb.1(0x7c000000), %bb.2(0x04000000)
+
+    %3:sreg_64 = COPY %0
+    %4:vgpr_32 = V_CVT_F32_U32_e32 %3.sub0, implicit $mode, implicit $exec
+    %5:sreg_32 = S_MUL_I32 %3.sub0, 246
+    %6:vgpr_32 = nofpexcept V_MUL_F32_e32 1036831949, %4, implicit $mode, implicit $exec
+    %7:vgpr_32 = nofpexcept V_TRUNC_F32_e32 %6, implicit $mode, implicit $exec
+    %8:vgpr_32 = nofpexcept V_CVT_U32_F32_e32 %7, implicit $mode, implicit $exec
+    %9:vgpr_32 = V_MAD_F32_e64 1, %7, 0, %1, 0, %4, 0, 0, implicit $mode, implicit $exec
+    %10:sreg_64_xexec = nofpexcept V_CMP_LE_F32_e64 0, %1, 2, %9, 0, implicit $mode, implicit $exec
+    S_CMP_LG_U64 %10, 0, implicit-def $scc
+    %11:sreg_32_xm0 = V_READFIRSTLANE_B32 %8, implicit $exec
+    %12:sreg_32 = S_ADDC_U32 %11, 0, implicit-def dead $scc, implicit $scc
+    %13:sreg_32 = S_OR_B32 %5, %3.sub0, implicit-def dead $scc
+    undef %0.sub0:sreg_64 = S_AND_B32 %12, 2047, implicit-def dead $scc
+    %14:sreg_32 = S_OR_B32 %13, 1, implicit-def dead $scc
+    %15:av_32 = COPY %14
+    BUFFER_STORE_BYTE_OFFSET %15, $sgpr0_sgpr1_sgpr2_sgpr3, 0, 0, 0, 0, implicit $exec :: (store (s8) into `ptr addrspace(5) null`, addrspace 5)
+    $vcc = COPY %2
+    S_CBRANCH_VCCNZ %bb.1, implicit $vcc
+    S_BRANCH %bb.2
+
+  bb.2:
+    SI_RETURN
+...
+
diff --git a/llvm/utils/TableGen/Common/CodeGenRegisters.cpp b/llvm/utils/TableGen/Common/CodeGenRegisters.cpp
index 10a5a16feef787..d1ff715333dbd6 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 units.
+    for (auto [SubRegIndex, SubReg] : SubRegs) {
+      if (SubReg->CoveredBySubRegs || SubReg->getSubRegs().empty())
+        continue;
+      LaneBitmask Unclaimed = SubRegIndex->LaneMask;
+      for (unsigned SUI : SubReg->getRegUnits())
+        Unclaimed &= ~RegUnitLaneMasks[UnitMaskIdx(SUI)];
+      for (unsigned SUI : SubReg->getRegUnits())
+        RegUnitLaneMasks[UnitMaskIdx(SUI)] |= Unclaimed;
+    }
+
     Register.setRegUnitLaneMasks(RegUnitLaneMasks);
   }
 }



More information about the llvm-commits mailing list