[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