[llvm] [CodeGen][ARM] Keep undef REG_SEQUENCE operands live for full-register uses without LIS (PR #203178)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 22 22:00:30 PDT 2026


https://github.com/hsnaveen2u updated https://github.com/llvm/llvm-project/pull/203178

>From b07d0d9ef6892a8fc083a189591ca3e6920def4c Mon Sep 17 00:00:00 2001
From: Naveen <naveen.siddegowda at oss.qualcomm.com>
Date: Tue, 16 Jun 2026 00:12:52 -0700
Subject: [PATCH 1/2] [CodeGen] Keep undef copy values feeding partial redefs

When RegisterCoalescer eliminates a COPY from an undefined value, it removes
the destination value and marks ordinary uses as undef if they no longer have a
reaching live interval value.

Partial subregister defs need the same treatment. A subregister def without
<read-undef> also reads the previous value of the lanes it does not write. If
that previous value was introduced by the undef COPY being deleted, the partial
def is left as a read-modify-write with no incoming value. Later,
JoinVals::analyzeValue sees the partial redef but finds no value-in and hits:

  (TrackSubRegLiveness || V.RedefVNI) &&
      "Instruction is reading nonexistent value"

Teach eliminateUndefCopy to consider such defs when repairing exposed undef
uses. For subregister defs that read the previous register value mark the def
read-undef when the incoming value has been removed.

This keeps the existing undef COPY elimination behavior while correctly
representing the exposed read of old lanes as undef.

Fixes #202263.
---
 llvm/lib/CodeGen/RegisterCoalescer.cpp        | 44 ++++++++++++++-----
 .../regcoalescer-undef-copy-partial-redef.ll  | 27 ++++++++++++
 .../regcoalescer-undef-copy-partial-redef.mir | 19 ++++++++
 3 files changed, 79 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll
 create mode 100644 llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir

diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp
index b61a87a2ee497..fdcb019c09465 100644
--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -1823,11 +1823,41 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
   } else
     LIS->removeVRegDefAt(DstLI, RegIndex);
 
-  // Mark uses as undef.
+  // CopyMI is about to be erased. Mark its defs undef before scanning DstReg
+  // operands, so they are ignored by this scan and by shrinkToUses().
+  for (MachineOperand &MO : CopyMI->all_defs())
+    if (MO.getReg() == DstReg)
+      MO.setIsUndef(true);
+
+  // Mark uexposed uses as undef.
   for (MachineOperand &MO : MRI->reg_nodbg_operands(DstReg)) {
-    if (MO.isDef() && !MO.getSubReg())
+    if (MO.isUndef() || (MO.isDef() && !MO.getSubReg()))
       continue;
-    const MachineInstr &MI = *MO.getParent();
+
+    MachineInstr &MI = *MO.getParent();
+
+    // A subregister def without the <read-undef> flag also reads the lanes it
+    // does not write. After deleting a copy from an undefined value, that
+    // implicit read may be exposed as having no incoming value. Preserve it as
+    // a read-undef partial def so JoinVals::analyzeValue() does not treat it
+    // as a read-modify-write with a missing value-in.
+    if (MO.isDef()) {
+      unsigned SubReg = MO.getSubReg();
+      if (SubReg == 0 || !MO.readsReg())
+        continue;
+
+      SlotIndex ReadIdx =
+          LIS->getInstructionIndex(MI).getRegSlot(true);
+      if (DstLI.hasSubRanges()) {
+        addUndefFlag(DstLI, ReadIdx, MO, SubReg);
+      } else if (!DstLI.Query(ReadIdx).valueIn()) {
+        MO.setIsUndef(true);
+        LLVM_DEBUG(dbgs() << "\tnew read-undef: " << ReadIdx << '\t' << MI);
+      }
+      continue;
+    }
+
+    // Regular uses read at the instruction's use slot.
     SlotIndex UseIdx = LIS->getInstructionIndex(MI);
     LaneBitmask UseMask = TRI->getSubRegIndexLaneMask(MO.getSubReg());
     if (MO.isDef())
@@ -1851,14 +1881,6 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
     LLVM_DEBUG(dbgs() << "\tnew undef: " << UseIdx << '\t' << MI);
   }
 
-  // A def of a subregister may be a use of the other subregisters, so
-  // deleting a def of a subregister may also remove uses. Since CopyMI
-  // is still part of the function (but about to be erased), mark all
-  // defs of DstReg in it as <undef>, so that shrinkToUses would
-  // ignore them.
-  for (MachineOperand &MO : CopyMI->all_defs())
-    if (MO.getReg() == DstReg)
-      MO.setIsUndef(true);
   LIS->shrinkToUses(&DstLI);
 
   return CopyMI;
diff --git a/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll
new file mode 100644
index 0000000000000..28f616776d4e1
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll
@@ -0,0 +1,27 @@
+; RUN: llc -mtriple=armv8a-unknown-linux -verify-machineinstrs < %s -o /dev/null
+
+; This used to assert in RegisterCoalescer:
+;
+;   (TrackSubRegLiveness || V.RedefVNI) &&
+;   "Instruction is reading nonexistent value"
+;
+; When an undef copy value feeds a later partial redef, eliminating the copy
+; can expose a partial redef with no incoming value.  Keep the undef value as
+; an IMPLICIT_DEF instead.
+
+target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "armv8a-unknown-linux"
+
+define void @init(i64 %x, i1 %min.iters.check, ptr %p) {
+entry:
+  %v0 = insertelement <2 x i64> poison, i64 %x, i64 1
+  br i1 %min.iters.check, label %common.ret, label %vector.body
+
+vector.body:
+  %v1 = insertelement <2 x i64> %v0, i64 1, i64 0
+  store <2 x i64> %v1, ptr %p, align 8
+  br label %common.ret
+
+common.ret:
+  ret void
+}
diff --git a/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir
new file mode 100644
index 0000000000000..2a588fa36def7
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir
@@ -0,0 +1,19 @@
+# RUN: llc -mtriple=armv8a-unknown-linux -run-pass=register-coalescer \
+# RUN:   -verify-machineinstrs -o /dev/null %s
+#
+# Eliminating the undef COPY for dsub_0 must mark the following dsub_1
+# partial def as a read-undef def.  Otherwise dsub_1 implicitly reads a
+# nonexistent incoming value and RegisterCoalescer asserts.
+
+---
+name:            undef_copy_partial_redef
+tracksRegLiveness: true
+isSSA:           false
+body:             |
+  bb.0:
+    %0:dpr = VMOVv2i32 0, 14 /* CC::al */, $noreg
+    undef %1.dsub_0:qpr = COPY undef %2:dpr
+    %1.dsub_1:qpr = COPY killed %0
+    %3:dpr = COPY %1.dsub_0
+    VST1d64 $noreg, 0, killed %3, 14 /* CC::al */, $noreg
+...

>From 10271070a526d95c9676e21b65c959a8223cb824 Mon Sep 17 00:00:00 2001
From: Naveen <naveen.siddegowda at oss.qualcomm.com>
Date: Wed, 22 Jul 2026 21:19:14 -0700
Subject: [PATCH 2/2] [CodeGen] Address review comments for undef copy handling

---
 llvm/lib/CodeGen/RegisterCoalescer.cpp           |  3 +--
 .../ARM/regcoalescer-undef-copy-partial-redef.ll | 16 +++++++++++++++-
 .../regcoalescer-undef-copy-partial-redef.mir    | 10 +++++++++-
 3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp
index fdcb019c09465..008699bc0a6d9 100644
--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -1846,8 +1846,7 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
       if (SubReg == 0 || !MO.readsReg())
         continue;
 
-      SlotIndex ReadIdx =
-          LIS->getInstructionIndex(MI).getRegSlot(true);
+      SlotIndex ReadIdx = LIS->getInstructionIndex(MI).getRegSlot(true);
       if (DstLI.hasSubRanges()) {
         addUndefFlag(DstLI, ReadIdx, MO, SubReg);
       } else if (!DstLI.Query(ReadIdx).valueIn()) {
diff --git a/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll
index 28f616776d4e1..6a503762678d1 100644
--- a/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll
+++ b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.ll
@@ -1,4 +1,5 @@
-; RUN: llc -mtriple=armv8a-unknown-linux -verify-machineinstrs < %s -o /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=armv8a-unknown-linux < %s | FileCheck %s
 
 ; This used to assert in RegisterCoalescer:
 ;
@@ -13,6 +14,19 @@ target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
 target triple = "armv8a-unknown-linux"
 
 define void @init(i64 %x, i1 %min.iters.check, ptr %p) {
+; CHECK-LABEL: init:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    tst r2, #1
+; CHECK-NEXT:    bxne lr
+; CHECK-NEXT:  .LBB0_1: @ %vector.body
+; CHECK-NEXT:    vmov.32 d17[0], r0
+; CHECK-NEXT:    mov r0, #1
+; CHECK-NEXT:    vmov.32 d17[1], r1
+; CHECK-NEXT:    vmov.32 d16[0], r0
+; CHECK-NEXT:    mov r0, #0
+; CHECK-NEXT:    vmov.32 d16[1], r0
+; CHECK-NEXT:    vst1.64 {d16, d17}, [r3]
+; CHECK-NEXT:    bx lr
 entry:
   %v0 = insertelement <2 x i64> poison, i64 %x, i64 1
   br i1 %min.iters.check, label %common.ret, label %vector.body
diff --git a/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir
index 2a588fa36def7..fdbf42d578a38 100644
--- a/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir
+++ b/llvm/test/CodeGen/ARM/regcoalescer-undef-copy-partial-redef.mir
@@ -1,5 +1,10 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
 # RUN: llc -mtriple=armv8a-unknown-linux -run-pass=register-coalescer \
-# RUN:   -verify-machineinstrs -o /dev/null %s
+# RUN:   -verify-coalescing -o - %s | FileCheck %s
+#
+# This is the post-TwoAddressInstruction form after REG_SEQUENCE lowering.
+# It directly tests the undef copy followed by a partial redef seen by
+# RegisterCoalescer.
 #
 # Eliminating the undef COPY for dsub_0 must mark the following dsub_1
 # partial def as a read-undef def.  Otherwise dsub_1 implicitly reads a
@@ -11,6 +16,9 @@ tracksRegLiveness: true
 isSSA:           false
 body:             |
   bb.0:
+    ; CHECK-LABEL: name: undef_copy_partial_redef
+    ; CHECK: undef [[VMOVv2i32_:%[0-9]+]].dsub_1:qpr = VMOVv2i32 0, 14 /* CC::al */, $noreg
+    ; CHECK-NEXT: VST1d64 $noreg, 0, [[VMOVv2i32_]].dsub_0, 14 /* CC::al */, $noreg
     %0:dpr = VMOVv2i32 0, 14 /* CC::al */, $noreg
     undef %1.dsub_0:qpr = COPY undef %2:dpr
     %1.dsub_1:qpr = COPY killed %0



More information about the llvm-commits mailing list