[llvm] [CodeGen] RegisterCoalescer: keep partial redefs valid when eliminating undef copies (PR #202599)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 27 04:14:13 PDT 2026
https://github.com/HelloWorldU updated https://github.com/llvm/llvm-project/pull/202599
>From 00727f54b5ba995c9e870e94104acc5ba5ff9f3c Mon Sep 17 00:00:00 2001
From: HelloWorldU <asd001liu at gmail.com>
Date: Wed, 17 Jun 2026 18:44:41 +0800
Subject: [PATCH 1/2] [CodeGen] RegisterCoalescer: keep partial redefs valid
when eliminating undef copies
eliminateUndefCopy deletes a COPY of an undef value and marks the explicit
uses of the removed value as undef. A partial subregister def that implicitly
reads the lanes it does not write was skipped, so after the value was removed
it kept reading a value that no longer exists. On targets that do not track
subregister liveness (e.g. armv8a without MVE) RegisterCoalescer then asserted
with "Instruction is reading nonexistent value". Turn such exposed partial
redefs into read-undef defs.
Fixes #202263.
---
llvm/lib/CodeGen/RegisterCoalescer.cpp | 33 ++++++++++++++++---
.../regcoalesce-undef-copy-subreg-redef.ll | 26 +++++++++++++++
2 files changed, 55 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.ll
diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp
index eb43eddfa081e..117d2cfa657e7 100644
--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -1816,10 +1816,35 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
// Mark uses as undef.
for (MachineOperand &MO : MRI->reg_nodbg_operands(DstReg)) {
- if (MO.isDef() /*|| MO.isUndef()*/)
+ if (MO.isUndef())
continue;
- const MachineInstr &MI = *MO.getParent();
- SlotIndex UseIdx = LIS->getInstructionIndex(MI);
+
+ MachineInstr *MI = MO.getParent();
+
+ // A partial subreg def also reads the lanes it does not write. Once the
+ // undef value it read has been removed, that read has no incoming value,
+ // so turn it into a read-undef def.
+ if (MO.isDef()) {
+ if (MI == CopyMI)
+ continue;
+
+ unsigned SubReg = MO.getSubReg();
+ if (SubReg == 0 || !MO.readsReg())
+ continue;
+
+ SlotIndex DefIdx =
+ LIS->getInstructionIndex(*MI).getRegSlot(MO.isEarlyClobber());
+ if (DstLI.hasSubRanges()) {
+ addUndefFlag(DstLI, DefIdx, MO, SubReg);
+ } else if (!DstLI.Query(DefIdx).valueIn()) {
+ MO.setIsUndef(true);
+ LLVM_DEBUG(dbgs() << "\tnew read-undef: " << DefIdx << '\t' << *MI);
+ }
+ continue;
+ }
+
+ // Regular uses read at the instruction's use slot.
+ SlotIndex UseIdx = LIS->getInstructionIndex(*MI);
LaneBitmask UseMask = TRI->getSubRegIndexLaneMask(MO.getSubReg());
bool isLive;
if (!UseMask.all() && DstLI.hasSubRanges()) {
@@ -1837,7 +1862,7 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
if (isLive)
continue;
MO.setIsUndef(true);
- LLVM_DEBUG(dbgs() << "\tnew undef: " << UseIdx << '\t' << MI);
+ LLVM_DEBUG(dbgs() << "\tnew undef: " << UseIdx << '\t' << *MI);
}
// A def of a subregister may be a use of the other subregisters, so
diff --git a/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.ll b/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.ll
new file mode 100644
index 0000000000000..34e34cfc63ec6
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.ll
@@ -0,0 +1,26 @@
+; REQUIRES: arm-registered-target
+; RUN: llc -mtriple=armv8a-unknown-linux -verify-machineinstrs < %s -o /dev/null
+
+; Regression test for issue #202263. Lowering the REG_SEQUENCE that builds the
+; <2 x i64> keeps a COPY of an undef value which feeds a later partial
+; subregister redef. When RegisterCoalescer eliminates that undef copy, the
+; redef must be turned into a read-undef def. Otherwise the coalescer asserts
+; with "Instruction is reading nonexistent value" on targets that do not track
+; subregister liveness (armv8a A-profile has no MVE).
+
+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 %c, ptr %p) {
+entry:
+ %v0 = insertelement <2 x i64> poison, i64 %x, i64 1
+ br i1 %c, label %exit, label %body
+
+body:
+ %v1 = insertelement <2 x i64> %v0, i64 1, i64 0
+ store <2 x i64> %v1, ptr %p, align 8
+ br label %exit
+
+exit:
+ ret void
+}
>From 6a2172196bef832e1b593cee5dbb3468d174c036 Mon Sep 17 00:00:00 2001
From: HelloWorldU <asd001liu at gmail.com>
Date: Sat, 27 Jun 2026 19:14:21 +0800
Subject: [PATCH 2/2] [CodeGen] RegisterCoalescer: address review feedback
Drop the MI == CopyMI special-case in eliminateUndefCopy by flagging
CopyMI's own def of DstReg as read-undef before scanning the register
operands, so the existing MO.isUndef() check skips it.
Add a -run-pass=register-coalescer MIR regression test, and trim the IR
test (drop the redundant REQUIRES line and datalayout; reword "undef" in
the comments to avoid the undef-deprecator).
---
llvm/lib/CodeGen/RegisterCoalescer.cpp | 19 ++---
.../regcoalesce-undef-copy-subreg-redef.ll | 10 +--
.../regcoalesce-undef-copy-subreg-redef.mir | 82 +++++++++++++++++++
3 files changed, 94 insertions(+), 17 deletions(-)
create mode 100644 llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.mir
diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp
index 117d2cfa657e7..68c1178c0b2b8 100644
--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -1814,6 +1814,14 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
} else
LIS->removeVRegDefAt(DstLI, RegIndex);
+ // CopyMI is about to be erased. Its def of DstReg may itself be a partial
+ // subregister def, so flag it as <undef> up front: this keeps the scan below
+ // from treating it as a partial redef to repair, and lets shrinkToUses
+ // ignore it.
+ for (MachineOperand &MO : CopyMI->all_defs())
+ if (MO.getReg() == DstReg)
+ MO.setIsUndef(true);
+
// Mark uses as undef.
for (MachineOperand &MO : MRI->reg_nodbg_operands(DstReg)) {
if (MO.isUndef())
@@ -1825,9 +1833,6 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
// undef value it read has been removed, that read has no incoming value,
// so turn it into a read-undef def.
if (MO.isDef()) {
- if (MI == CopyMI)
- continue;
-
unsigned SubReg = MO.getSubReg();
if (SubReg == 0 || !MO.readsReg())
continue;
@@ -1865,14 +1870,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/regcoalesce-undef-copy-subreg-redef.ll b/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.ll
index 34e34cfc63ec6..3f0cde8fa92e1 100644
--- a/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.ll
+++ b/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.ll
@@ -1,14 +1,12 @@
-; REQUIRES: arm-registered-target
; RUN: llc -mtriple=armv8a-unknown-linux -verify-machineinstrs < %s -o /dev/null
; Regression test for issue #202263. Lowering the REG_SEQUENCE that builds the
-; <2 x i64> keeps a COPY of an undef value which feeds a later partial
-; subregister redef. When RegisterCoalescer eliminates that undef copy, the
-; redef must be turned into a read-undef def. Otherwise the coalescer asserts
-; with "Instruction is reading nonexistent value" on targets that do not track
+; <2 x i64> keeps a COPY of an undefined value which feeds a later partial
+; subregister redef. When RegisterCoalescer eliminates that copy, the redef
+; must be turned into a read-undef def. Otherwise the coalescer asserts with
+; "Instruction is reading nonexistent value" on targets that do not track
; subregister liveness (armv8a A-profile has no MVE).
-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 %c, ptr %p) {
diff --git a/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.mir b/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.mir
new file mode 100644
index 0000000000000..3431695a7a6c3
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/regcoalesce-undef-copy-subreg-redef.mir
@@ -0,0 +1,82 @@
+# RUN: llc -mtriple=armv8a-unknown-linux -run-pass=register-coalescer -verify-machineinstrs -o - %s | FileCheck %s
+
+# Regression test for issue #202263 (and the long-standing #200378). After
+# RegisterCoalescer eliminates the COPY of an undefined value, the partial
+# subregister redef that implicitly read it must become a read-undef def;
+# otherwise the coalescer asserts "Instruction is reading nonexistent value"
+# on targets that do not track subregister liveness.
+
+--- |
+ define void @init(i64 %0, i1 %c, ptr %1) {
+ ret void
+ }
+...
+---
+name: init
+tracksRegLiveness: true
+registers:
+ - { id: 0, class: dpair }
+ - { id: 1, class: gpr }
+ - { id: 2, class: gpr }
+ - { id: 3, class: gpr }
+ - { id: 4, class: gpr }
+ - { id: 5, class: dpr }
+ - { id: 7, class: dpr }
+ - { id: 8, class: qpr }
+ - { id: 9, class: dpr }
+ - { id: 11, class: gpr }
+ - { id: 12, class: dpr }
+ - { id: 13, class: dpr }
+ - { id: 14, class: gpr }
+ - { id: 15, class: qpr }
+ - { id: 16, class: dpr }
+ - { id: 17, class: dpr }
+ - { id: 18, class: qpr }
+liveins:
+ - { reg: '$r0', virtual-reg: '%1' }
+ - { reg: '$r1', virtual-reg: '%2' }
+ - { reg: '$r2', virtual-reg: '%3' }
+ - { reg: '$r3', virtual-reg: '%4' }
+body: |
+ bb.0:
+ successors: %bb.1, %bb.2
+ liveins: $r0, $r1, $r2, $r3
+
+ %4:gpr = COPY killed $r3
+ %3:gpr = COPY killed $r2
+ %2:gpr = COPY killed $r1
+ %1:gpr = COPY killed $r0
+ TSTri killed %3, 1, 14 /* CC::al */, $noreg, implicit-def $cpsr
+ Bcc %bb.2, 0 /* CC::eq */, killed $cpsr
+ B %bb.1
+
+ bb.1:
+ BX_RET 14 /* CC::al */, $noreg
+
+ bb.2:
+ successors: %bb.1
+
+ ; The COPY of an undef value below feeds the dsub_1 partial redef. After the
+ ; copy is eliminated, that redef must become a read-undef def.
+ ; CHECK-LABEL: name: init
+ ; CHECK: undef [[Q:%[0-9]+]].dsub_1:qpr = VSETLNi32 undef [[Q]].dsub_1,
+ %5:dpr = VSETLNi32 undef %5, killed %1, 0, 14 /* CC::al */, $noreg
+ %7:dpr = COPY killed %5
+ %7:dpr = VSETLNi32 %7, killed %2, 1, 14 /* CC::al */, $noreg
+ undef %8.dsub_0:qpr = COPY undef %9:dpr
+ %8.dsub_1:qpr = COPY killed %7
+ %11:gpr = MOVi 1, 14 /* CC::al */, $noreg, $noreg
+ %12:dpr = COPY %8.dsub_0
+ %13:dpr = COPY killed %12
+ %13:dpr = VSETLNi32 %13, killed %11, 0, 14 /* CC::al */, $noreg
+ %14:gpr = MOVi 0, 14 /* CC::al */, $noreg, $noreg
+ %15:qpr = COPY killed %8
+ %15.dsub_0:qpr = COPY %13
+ %16:dpr = COPY killed %13
+ %17:dpr = COPY killed %16
+ %17:dpr = VSETLNi32 %17, killed %14, 1, 14 /* CC::al */, $noreg
+ %18:qpr = COPY killed %15
+ %18.dsub_0:qpr = COPY killed %17
+ VST1q64 killed %4, 0, killed %18, 14 /* CC::al */, $noreg :: (store (s128) into %ir.1, align 8)
+ B %bb.1
+...
More information about the llvm-commits
mailing list