[llvm] TwoAddressInstructions: Keep undef INSERT_SUBREG lanes defined (PR #230931)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 11 02:36:42 PDT 2026
https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/230931
%reg = INSERT_SUBREG undef %reg, %subreg, subidx defines all of %reg, so
reading the lanes outside subidx afterwards is valid. Rewriting it to
undef %reg.subidx = COPY %subreg narrows the definition to subidx and
leaves those reads without a live subrange, failing the "No live subrange
at use"
machine verifier check.
When a subrange outside subidx is still live past the def, insert an
IMPLICIT_DEF of the full register and drop the undef flag. In the common
case, where the lanes the INSERT_SUBREG left undefined are dead, keep the
undef flag and avoid an IMPLICIT_DEF that survives to the end of codegen
when the COPY is not coalesced.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply at anthropic.com>
>From ee9bb53e17b64535d73c3963474c63c050703dd5 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Sat, 10 Oct 2026 10:06:57 +0200
Subject: [PATCH] TwoAddressInstructions: Keep undef INSERT_SUBREG lanes
defined
%reg = INSERT_SUBREG undef %reg, %subreg, subidx defines all of %reg, so
reading the lanes outside subidx afterwards is valid. Rewriting it to
undef %reg.subidx = COPY %subreg narrows the definition to subidx and
leaves those reads without a live subrange, failing the "No live subrange
at use"
machine verifier check.
When a subrange outside subidx is still live past the def, insert an
IMPLICIT_DEF of the full register and drop the undef flag. In the common
case, where the lanes the INSERT_SUBREG left undefined are dead, keep the
undef flag and avoid an IMPLICIT_DEF that survives to the end of codegen
when the COPY is not coalesced.
Co-authored-by: Claude Opus 5.5 (1M context) <noreply at anthropic.com>
---
.../lib/CodeGen/TwoAddressInstructionPass.cpp | 35 +++++++++---
.../AMDGPU/twoaddr-insert-subreg-undef.mir | 55 +++++++++++++++++++
2 files changed, 83 insertions(+), 7 deletions(-)
create mode 100644 llvm/test/CodeGen/AMDGPU/twoaddr-insert-subreg-undef.mir
diff --git a/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp b/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp
index 4a588a1873d6c..f32b50974f7a7 100644
--- a/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp
+++ b/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp
@@ -1897,26 +1897,46 @@ bool TwoAddressInstructionImpl::run() {
LaneBitmask LaneMask = TRI->getSubRegIndexLaneMask(SubIdx);
LiveInterval *LI = LIS ? &LIS->getInterval(Reg) : nullptr;
- // The fixup below keeps or discards a subrange's value as a whole, so
- // split the ones straddling SubIdx. This must precede narrowing the
- // def, or refineSubRanges drops the value for the untouched lanes.
+ bool UndefLanesAreLive = false;
if (LI && LI->hasSubRanges()) {
+ // The fixup below keeps or discards a subrange's value as a whole,
+ // so split the ones straddling SubIdx. This must precede narrowing
+ // the def, or refineSubRanges drops the value for the untouched
+ // lanes.
LI->refineSubRanges(
LIS->getVNInfoAllocator(), LaneMask,
[](LiveInterval::SubRange &) {}, *LIS->getSlotIndexes(), *TRI);
+
+ if (mi->getOperand(1).isUndef()) {
+ SlotIndex Idx = LIS->getInstructionIndex(*mi).getDeadSlot();
+ UndefLanesAreLive =
+ any_of(LI->subranges(), [&](const LiveInterval::SubRange &S) {
+ return (S.LaneMask & LaneMask).none() && S.liveAt(Idx);
+ });
+ }
}
mi->removeOperand(3);
assert(mi->getOperand(0).getSubReg() == 0 && "Unexpected subreg idx");
mi->getOperand(0).setSubReg(SubIdx);
- mi->getOperand(0).setIsUndef(mi->getOperand(1).isUndef());
+ mi->getOperand(0).setIsUndef(mi->getOperand(1).isUndef() &&
+ !UndefLanesAreLive);
mi->removeOperand(1);
mi->setDesc(TII->get(TargetOpcode::COPY));
LLVM_DEBUG(dbgs() << "\t\tconvert to:\t" << *mi);
+ if (UndefLanesAreLive) {
+ MachineInstr *ImpDef =
+ BuildMI(*mi->getParent(), mi, mi->getDebugLoc(),
+ TII->get(TargetOpcode::IMPLICIT_DEF), Reg);
+ DistanceMap.insert(std::make_pair(ImpDef, Dist));
+ DistanceMap[&*mi] = ++Dist;
+ LIS->InsertMachineInstrInMaps(*ImpDef);
+ }
+
// Update LiveIntervals.
if (LI) {
- if (LI->hasSubRanges()) {
+ if (LI->hasSubRanges() && !UndefLanesAreLive) {
// The COPY no longer defines subregs of %reg except for
// %reg.subidx.
SlotIndex Idx = LIS->getInstructionIndex(*mi).getRegSlot();
@@ -1935,8 +1955,9 @@ bool TwoAddressInstructionImpl::run() {
// The COPY no longer has a use of %reg.
LIS->shrinkToUses(LI);
} else {
- // The live interval for Reg did not have subranges but now it needs
- // them because we have introduced a subreg def. Recompute it.
+ // Either the live interval for Reg did not have subranges but now
+ // it needs them because we have introduced a subreg def, or an
+ // IMPLICIT_DEF was inserted. Recompute it.
LIS->removeInterval(Reg);
LIS->createAndComputeVirtRegInterval(Reg);
}
diff --git a/llvm/test/CodeGen/AMDGPU/twoaddr-insert-subreg-undef.mir b/llvm/test/CodeGen/AMDGPU/twoaddr-insert-subreg-undef.mir
new file mode 100644
index 0000000000000..92015363e320f
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/twoaddr-insert-subreg-undef.mir
@@ -0,0 +1,55 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=amdgpu9.42 -run-pass=liveintervals,two-address-instruction -o - %s | FileCheck %s
+
+# An undef super-register source leaves the lanes outside
+# sub0_sub1_sub2_sub3 undefined, but they are still read. They need an
+# IMPLICIT_DEF.
+
+---
+name: insert_subreg_undef_lanes_read
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr8_vgpr9_vgpr10_vgpr11
+
+ ; CHECK-LABEL: name: insert_subreg_undef_lanes_read
+ ; CHECK: liveins: $vgpr8_vgpr9_vgpr10_vgpr11
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: dead [[DEF:%[0-9]+]]:vreg_256 = IMPLICIT_DEF
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:vreg_128 = COPY $vgpr8_vgpr9_vgpr10_vgpr11
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:vreg_256 = IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]].sub0_sub1_sub2_sub3:vreg_256 = COPY [[COPY]]
+ ; CHECK-NEXT: $vgpr12_vgpr13_vgpr14_vgpr15 = COPY [[DEF1]].sub4_sub5_sub6_sub7
+ ; CHECK-NEXT: $vgpr8_vgpr9_vgpr10_vgpr11 = COPY [[DEF1]].sub0_sub1_sub2_sub3
+ ; CHECK-NEXT: SI_RETURN implicit $vgpr8_vgpr9_vgpr10_vgpr11, implicit $vgpr12_vgpr13_vgpr14_vgpr15
+ %0:vreg_256 = IMPLICIT_DEF
+ %1:vreg_128 = COPY $vgpr8_vgpr9_vgpr10_vgpr11
+ %2:vreg_256 = INSERT_SUBREG undef %0, %1, %subreg.sub0_sub1_sub2_sub3
+ $vgpr12_vgpr13_vgpr14_vgpr15 = COPY %2.sub4_sub5_sub6_sub7
+ $vgpr8_vgpr9_vgpr10_vgpr11 = COPY %2.sub0_sub1_sub2_sub3
+ SI_RETURN implicit $vgpr8_vgpr9_vgpr10_vgpr11, implicit $vgpr12_vgpr13_vgpr14_vgpr15
+...
+
+# Nothing reads the undefined lanes, so no IMPLICIT_DEF is needed.
+
+---
+name: insert_subreg_undef_lanes_unused
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $vgpr8_vgpr9_vgpr10_vgpr11
+
+ ; CHECK-LABEL: name: insert_subreg_undef_lanes_unused
+ ; CHECK: liveins: $vgpr8_vgpr9_vgpr10_vgpr11
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: dead [[DEF:%[0-9]+]]:vreg_256 = IMPLICIT_DEF
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:vreg_128 = COPY $vgpr8_vgpr9_vgpr10_vgpr11
+ ; CHECK-NEXT: undef [[COPY1:%[0-9]+]].sub0_sub1_sub2_sub3:vreg_256 = COPY [[COPY]]
+ ; CHECK-NEXT: $vgpr8_vgpr9_vgpr10_vgpr11 = COPY [[COPY1]].sub0_sub1_sub2_sub3
+ ; CHECK-NEXT: SI_RETURN implicit $vgpr8_vgpr9_vgpr10_vgpr11
+ %0:vreg_256 = IMPLICIT_DEF
+ %1:vreg_128 = COPY $vgpr8_vgpr9_vgpr10_vgpr11
+ %2:vreg_256 = INSERT_SUBREG undef %0, %1, %subreg.sub0_sub1_sub2_sub3
+ $vgpr8_vgpr9_vgpr10_vgpr11 = COPY %2.sub0_sub1_sub2_sub3
+ SI_RETURN implicit $vgpr8_vgpr9_vgpr10_vgpr11
+...
More information about the llvm-commits
mailing list