[llvm] [CodeGen] Ensure `undef` propogation when an undef copy is eliminated if used as a subregister def (PR #204039)

Ryan Buchner via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 16 12:56:35 PDT 2026


https://github.com/bababuck updated https://github.com/llvm/llvm-project/pull/204039

>From 6e8816144471f743161f69bb9a47d41b8c9ceb5b Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 15 Jun 2026 16:09:49 -0700
Subject: [PATCH 1/8] [CodeGen] Add test for undef propogation bug

---
 llvm/test/CodeGen/ARM/copy-undef.ll | 45 +++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 llvm/test/CodeGen/ARM/copy-undef.ll

diff --git a/llvm/test/CodeGen/ARM/copy-undef.ll b/llvm/test/CodeGen/ARM/copy-undef.ll
new file mode 100644
index 0000000000000..d85b2de636ace
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/copy-undef.ll
@@ -0,0 +1,45 @@
+; RUN: llc < %s | FileCheck %s
+
+target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
+target triple = "thumbv7-unknown-linux-android29"
+
+ at test_crash_format = dso_local local_unnamed_addr global i32 0, align 4
+ at global_out = external local_unnamed_addr global ptr, align 4
+
+; Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind optsize memory(readwrite, argmem: write, inaccessiblemem: none, target_mem: none)
+define dso_local void @_Z10test_crashv() local_unnamed_addr #0 {
+entry:
+  %0 = load i32, ptr @test_crash_format, align 4, !tbaa !5
+  %1 = insertelement <8 x i32> <i32 undef, i32 undef, i32 undef, i32 undef, i32 poison, i32 poison, i32 poison, i32 undef>, i32 %0, i64 6
+  br label %for.cond
+
+for.cond:                                         ; preds = %for.cond, %entry
+  %i.0 = phi i32 [ undef, %entry ], [ %inc, %for.cond ]
+  %2 = insertelement <8 x i32> %1, i32 %i.0, i64 4
+  %3 = insertelement <8 x i32> %2, i32 %i.0, i64 5
+  %4 = load ptr, ptr @global_out, align 4, !tbaa !9
+  store <8 x i32> %3, ptr %4, align 32, !tbaa !11
+  %inc = add nsw i32 %i.0, 1
+  br label %for.cond, !llvm.loop !12
+}
+
+attributes #0 = { mustprogress nofree norecurse noreturn nosync nounwind optsize memory(readwrite, argmem: write, inaccessiblemem: none, target_mem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+armv7-a,+d32,+dsp,+fp64,+neon,+read-tp-tpidruro,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
+
+!llvm.module.flags = !{!0, !1, !2, !3}
+!llvm.ident = !{!4}
+!llvm.errno.tbaa = !{!5}
+
+!0 = !{i32 1, !"min_enum_size", i32 4}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 7, !"PIE Level", i32 2}
+!3 = !{i32 7, !"frame-pointer", i32 2}
+!4 = !{!"clang version 23.0.0git (git at github.com:bababuck/llvm-project.git a6351d882ca3086c62e052fbc4f7f585e157213b)"}
+!5 = !{!6, !6, i64 0}
+!6 = !{!"int", !7, i64 0}
+!7 = !{!"omnipotent char", !8, i64 0}
+!8 = !{!"Simple C++ TBAA"}
+!9 = !{!10, !10, i64 0}
+!10 = !{!"any pointer", !7, i64 0}
+!11 = !{!7, !7, i64 0}
+!12 = distinct !{!12, !13}
+!13 = !{!"llvm.loop.mustprogress"}

>From be8a8f44015f4db4f363cd0772787f332903fc35 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Mon, 15 Jun 2026 16:02:25 -0700
Subject: [PATCH 2/8] [SLP] Ensure `undef` propogation when an undef copy is
 eliminated if used as a subregister def

---
 llvm/lib/CodeGen/RegisterCoalescer.cpp |  4 +++-
 llvm/test/CodeGen/ARM/copy-undef.ll    | 22 ++++++++++++++++++++++
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp
index eb43eddfa081e..667f357c79579 100644
--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -1816,11 +1816,13 @@ MachineInstr *RegisterCoalescer::eliminateUndefCopy(MachineInstr *CopyMI) {
 
   // Mark uses as undef.
   for (MachineOperand &MO : MRI->reg_nodbg_operands(DstReg)) {
-    if (MO.isDef() /*|| MO.isUndef()*/)
+    if (MO.isDef() && !MO.getSubReg())
       continue;
     const MachineInstr &MI = *MO.getParent();
     SlotIndex UseIdx = LIS->getInstructionIndex(MI);
     LaneBitmask UseMask = TRI->getSubRegIndexLaneMask(MO.getSubReg());
+    if (MO.isDef())
+      UseMask = ~UseMask;
     bool isLive;
     if (!UseMask.all() && DstLI.hasSubRanges()) {
       isLive = false;
diff --git a/llvm/test/CodeGen/ARM/copy-undef.ll b/llvm/test/CodeGen/ARM/copy-undef.ll
index d85b2de636ace..bdb0e485ece88 100644
--- a/llvm/test/CodeGen/ARM/copy-undef.ll
+++ b/llvm/test/CodeGen/ARM/copy-undef.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 ; RUN: llc < %s | FileCheck %s
 
 target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
@@ -8,6 +9,27 @@ target triple = "thumbv7-unknown-linux-android29"
 
 ; Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind optsize memory(readwrite, argmem: write, inaccessiblemem: none, target_mem: none)
 define dso_local void @_Z10test_crashv() local_unnamed_addr #0 {
+; CHECK-LABEL: _Z10test_crashv:
+; CHECK:       @ %bb.0: @ %entry
+; CHECK-NEXT:    .save {r7, lr}
+; CHECK-NEXT:    push {r7, lr}
+; CHECK-NEXT:    .setfp r7, sp
+; CHECK-NEXT:    mov r7, sp
+; CHECK-NEXT:    movw r0, :lower16:test_crash_format
+; CHECK-NEXT:    @ implicit-def: $r1
+; CHECK-NEXT:    movt r0, :upper16:test_crash_format
+; CHECK-NEXT:    vld1.32 {d17[0]}, [r0:32]
+; CHECK-NEXT:    movw r0, :lower16:global_out
+; CHECK-NEXT:    movt r0, :upper16:global_out
+; CHECK-NEXT:  .LBB0_1: @ %for.cond
+; CHECK-NEXT:    @ =>This Inner Loop Header: Depth=1
+; CHECK-NEXT:    vmov.32 d16[0], r1
+; CHECK-NEXT:    ldr r2, [r0]
+; CHECK-NEXT:    vmov.32 d16[1], r1
+; CHECK-NEXT:    adds r1, #1
+; CHECK-NEXT:    vst1.32 {d16, d17}, [r2:128]!
+; CHECK-NEXT:    vst1.64 {d16, d17}, [r2:128]
+; CHECK-NEXT:    b .LBB0_1
 entry:
   %0 = load i32, ptr @test_crash_format, align 4, !tbaa !5
   %1 = insertelement <8 x i32> <i32 undef, i32 undef, i32 undef, i32 undef, i32 poison, i32 poison, i32 poison, i32 undef>, i32 %0, i64 6

>From e1fc4020f23ed73a91ee34c27e3749b5f6e128a6 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 16 Jun 2026 11:57:56 -0700
Subject: [PATCH 3/8] Simplify .ll test

---
 llvm/test/CodeGen/ARM/copy-undef.ll | 63 ++++++-----------------------
 1 file changed, 13 insertions(+), 50 deletions(-)

diff --git a/llvm/test/CodeGen/ARM/copy-undef.ll b/llvm/test/CodeGen/ARM/copy-undef.ll
index bdb0e485ece88..773ce0f8825ef 100644
--- a/llvm/test/CodeGen/ARM/copy-undef.ll
+++ b/llvm/test/CodeGen/ARM/copy-undef.ll
@@ -1,67 +1,30 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: llc < %s | FileCheck %s
+; RUN: llc -mtriple=armv7-- < %s | FileCheck %s
 
-target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
-target triple = "thumbv7-unknown-linux-android29"
-
- at test_crash_format = dso_local local_unnamed_addr global i32 0, align 4
- at global_out = external local_unnamed_addr global ptr, align 4
-
-; Function Attrs: mustprogress nofree norecurse noreturn nosync nounwind optsize memory(readwrite, argmem: write, inaccessiblemem: none, target_mem: none)
-define dso_local void @_Z10test_crashv() local_unnamed_addr #0 {
-; CHECK-LABEL: _Z10test_crashv:
+define void @copy_undef_propogate(i32 %in, ptr %out) {
+; CHECK-LABEL: copy_undef_propogate:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    .save {r7, lr}
-; CHECK-NEXT:    push {r7, lr}
-; CHECK-NEXT:    .setfp r7, sp
-; CHECK-NEXT:    mov r7, sp
-; CHECK-NEXT:    movw r0, :lower16:test_crash_format
-; CHECK-NEXT:    @ implicit-def: $r1
-; CHECK-NEXT:    movt r0, :upper16:test_crash_format
-; CHECK-NEXT:    vld1.32 {d17[0]}, [r0:32]
-; CHECK-NEXT:    movw r0, :lower16:global_out
-; CHECK-NEXT:    movt r0, :upper16:global_out
+; CHECK-NEXT:    vmov.32 d17[0], r0
+; CHECK-NEXT:    @ implicit-def: $r0
 ; CHECK-NEXT:  .LBB0_1: @ %for.cond
 ; CHECK-NEXT:    @ =>This Inner Loop Header: Depth=1
-; CHECK-NEXT:    vmov.32 d16[0], r1
-; CHECK-NEXT:    ldr r2, [r0]
-; CHECK-NEXT:    vmov.32 d16[1], r1
-; CHECK-NEXT:    adds r1, #1
+; CHECK-NEXT:    vmov.32 d16[0], r0
+; CHECK-NEXT:    mov r2, r1
+; CHECK-NEXT:    vmov.32 d16[1], r0
+; CHECK-NEXT:    add r0, r0, #1
 ; CHECK-NEXT:    vst1.32 {d16, d17}, [r2:128]!
 ; CHECK-NEXT:    vst1.64 {d16, d17}, [r2:128]
 ; CHECK-NEXT:    b .LBB0_1
 entry:
-  %0 = load i32, ptr @test_crash_format, align 4, !tbaa !5
-  %1 = insertelement <8 x i32> <i32 undef, i32 undef, i32 undef, i32 undef, i32 poison, i32 poison, i32 poison, i32 undef>, i32 %0, i64 6
+  %1 = insertelement <8 x i32> <i32 undef, i32 undef, i32 undef, i32 undef, i32 poison, i32 poison, i32 poison, i32 undef>, i32 %in, i64 6
   br label %for.cond
 
-for.cond:                                         ; preds = %for.cond, %entry
+for.cond:
   %i.0 = phi i32 [ undef, %entry ], [ %inc, %for.cond ]
   %2 = insertelement <8 x i32> %1, i32 %i.0, i64 4
   %3 = insertelement <8 x i32> %2, i32 %i.0, i64 5
-  %4 = load ptr, ptr @global_out, align 4, !tbaa !9
-  store <8 x i32> %3, ptr %4, align 32, !tbaa !11
+  store <8 x i32> %3, ptr %out
   %inc = add nsw i32 %i.0, 1
-  br label %for.cond, !llvm.loop !12
+  br label %for.cond
 }
 
-attributes #0 = { mustprogress nofree norecurse noreturn nosync nounwind optsize memory(readwrite, argmem: write, inaccessiblemem: none, target_mem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+armv7-a,+d32,+dsp,+fp64,+neon,+read-tp-tpidruro,+thumb-mode,+vfp2,+vfp2sp,+vfp3,+vfp3d16,+vfp3d16sp,+vfp3sp,-aes,-fp-armv8,-fp-armv8d16,-fp-armv8d16sp,-fp-armv8sp,-fp16,-fp16fml,-fullfp16,-sha2,-vfp4,-vfp4d16,-vfp4d16sp,-vfp4sp" }
-
-!llvm.module.flags = !{!0, !1, !2, !3}
-!llvm.ident = !{!4}
-!llvm.errno.tbaa = !{!5}
-
-!0 = !{i32 1, !"min_enum_size", i32 4}
-!1 = !{i32 8, !"PIC Level", i32 2}
-!2 = !{i32 7, !"PIE Level", i32 2}
-!3 = !{i32 7, !"frame-pointer", i32 2}
-!4 = !{!"clang version 23.0.0git (git at github.com:bababuck/llvm-project.git a6351d882ca3086c62e052fbc4f7f585e157213b)"}
-!5 = !{!6, !6, i64 0}
-!6 = !{!"int", !7, i64 0}
-!7 = !{!"omnipotent char", !8, i64 0}
-!8 = !{!"Simple C++ TBAA"}
-!9 = !{!10, !10, i64 0}
-!10 = !{!"any pointer", !7, i64 0}
-!11 = !{!7, !7, i64 0}
-!12 = distinct !{!12, !13}
-!13 = !{!"llvm.loop.mustprogress"}

>From 7ae9c486b71c26d8825480a713f296c54aeef507 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 16 Jun 2026 12:09:41 -0700
Subject: [PATCH 4/8] Add .mir test

---
 llvm/test/CodeGen/ARM/copy-undef.mir | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 llvm/test/CodeGen/ARM/copy-undef.mir

diff --git a/llvm/test/CodeGen/ARM/copy-undef.mir b/llvm/test/CodeGen/ARM/copy-undef.mir
new file mode 100644
index 0000000000000..97ee1d3575bea
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/copy-undef.mir
@@ -0,0 +1,18 @@
+# RUN: llc -o - %s -mtriple=armv7-- -run-pass=register-coalescer | FileCheck %s
+
+# Make sure we don't crash on this input
+
+---
+---
+name: copy_undef_propogate
+tracksRegLiveness: true
+body:             |
+  bb.0.entry:
+    liveins: $r0
+  
+    %4:gpr = COPY killed $r0
+    %7:dpr = VSETLNi32 undef %7, killed %4, 0, 14 /* CC::al */, $noreg
+    undef %9.dsub_0:qpr = COPY undef %10:dpr
+    %9.dsub_1:qpr = COPY killed %7
+    %12:dpr = COPY %9.dsub_0
+...

>From 419da911b9188d52c28066eea999215d0506fe72 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 16 Jun 2026 12:43:01 -0700
Subject: [PATCH 5/8] undef -> poison in .ll test

---
 llvm/test/CodeGen/ARM/copy-undef.ll | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/ARM/copy-undef.ll b/llvm/test/CodeGen/ARM/copy-undef.ll
index 773ce0f8825ef..50a9131e2c044 100644
--- a/llvm/test/CodeGen/ARM/copy-undef.ll
+++ b/llvm/test/CodeGen/ARM/copy-undef.ll
@@ -16,11 +16,11 @@ define void @copy_undef_propogate(i32 %in, ptr %out) {
 ; CHECK-NEXT:    vst1.64 {d16, d17}, [r2:128]
 ; CHECK-NEXT:    b .LBB0_1
 entry:
-  %1 = insertelement <8 x i32> <i32 undef, i32 undef, i32 undef, i32 undef, i32 poison, i32 poison, i32 poison, i32 undef>, i32 %in, i64 6
+  %1 = insertelement <8 x i32> <i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>, i32 %in, i64 6
   br label %for.cond
 
 for.cond:
-  %i.0 = phi i32 [ undef, %entry ], [ %inc, %for.cond ]
+  %i.0 = phi i32 [ poison, %entry ], [ %inc, %for.cond ]
   %2 = insertelement <8 x i32> %1, i32 %i.0, i64 4
   %3 = insertelement <8 x i32> %2, i32 %i.0, i64 5
   store <8 x i32> %3, ptr %out

>From 95e977dd236246cba6c9789da4f9a6b4575cf058 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 16 Jun 2026 12:47:23 -0700
Subject: [PATCH 6/8] Compress reg numbers in .mir

---
 llvm/test/CodeGen/ARM/copy-undef.mir | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/test/CodeGen/ARM/copy-undef.mir b/llvm/test/CodeGen/ARM/copy-undef.mir
index 97ee1d3575bea..7dd3c757ecef8 100644
--- a/llvm/test/CodeGen/ARM/copy-undef.mir
+++ b/llvm/test/CodeGen/ARM/copy-undef.mir
@@ -10,9 +10,9 @@ body:             |
   bb.0.entry:
     liveins: $r0
   
-    %4:gpr = COPY killed $r0
-    %7:dpr = VSETLNi32 undef %7, killed %4, 0, 14 /* CC::al */, $noreg
-    undef %9.dsub_0:qpr = COPY undef %10:dpr
-    %9.dsub_1:qpr = COPY killed %7
-    %12:dpr = COPY %9.dsub_0
+    %0:gpr = COPY killed $r0
+    %1:dpr = VSETLNi32 undef %1, killed %0, 0, 14 /* CC::al */, $noreg
+    undef %2.dsub_0:qpr = COPY undef %3:dpr
+    %2.dsub_1:qpr = COPY killed %1
+    %4:dpr = COPY %2.dsub_0
 ...

>From aec777037be05ae66447936d99ef35f251784509 Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 16 Jun 2026 12:55:20 -0700
Subject: [PATCH 7/8] Add checks to .mir

---
 llvm/test/CodeGen/ARM/copy-undef.mir | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/ARM/copy-undef.mir b/llvm/test/CodeGen/ARM/copy-undef.mir
index 7dd3c757ecef8..4374ccecfd279 100644
--- a/llvm/test/CodeGen/ARM/copy-undef.mir
+++ b/llvm/test/CodeGen/ARM/copy-undef.mir
@@ -1,15 +1,19 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
 # RUN: llc -o - %s -mtriple=armv7-- -run-pass=register-coalescer | FileCheck %s
 
 # Make sure we don't crash on this input
 
----
 ---
 name: copy_undef_propogate
 tracksRegLiveness: true
 body:             |
   bb.0.entry:
     liveins: $r0
-  
+
+    ; CHECK-LABEL: name: copy_undef_propogate
+    ; CHECK: liveins: $r0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: KILL $r0
     %0:gpr = COPY killed $r0
     %1:dpr = VSETLNi32 undef %1, killed %0, 0, 14 /* CC::al */, $noreg
     undef %2.dsub_0:qpr = COPY undef %3:dpr

>From 0f6dd5e64a206336f28a08631414368730947e3d Mon Sep 17 00:00:00 2001
From: bababuck <buchner.ryan at gmail.com>
Date: Tue, 16 Jun 2026 12:55:56 -0700
Subject: [PATCH 8/8] Remove .entry

---
 llvm/test/CodeGen/ARM/copy-undef.mir | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/ARM/copy-undef.mir b/llvm/test/CodeGen/ARM/copy-undef.mir
index 4374ccecfd279..75432cab06222 100644
--- a/llvm/test/CodeGen/ARM/copy-undef.mir
+++ b/llvm/test/CodeGen/ARM/copy-undef.mir
@@ -7,7 +7,7 @@
 name: copy_undef_propogate
 tracksRegLiveness: true
 body:             |
-  bb.0.entry:
+  bb.0:
     liveins: $r0
 
     ; CHECK-LABEL: name: copy_undef_propogate



More information about the llvm-commits mailing list