[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