[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