[llvm] PeepholeOpt: Clear kill flags in foldImmediate (PR #195680)
Frederik Harwath via llvm-commits
llvm-commits at lists.llvm.org
Tue May 5 06:29:41 PDT 2026
https://github.com/frederik-h updated https://github.com/llvm/llvm-project/pull/195680
>From 68d607560a64a6101e99bf3a1b0e3e3268bd0db8 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Tue, 5 May 2026 04:17:00 -0400
Subject: [PATCH 1/4] Add tests for peephole foldImmediate kill flag bug
Add minimal test cases demonstrating a kill flag bug in the peephole
optimizer's foldImmediate function.
The bug occurs when foldImmediate processes a COPY of an immediate-defining
register:
1. The target's foldImmediate transforms the COPY into a MOV immediate
2. This makes it identical to the original immediate definition
3. foldImmediate eliminates the redundancy by replacing all uses
4. Kill flags are not cleared on the source register during replacement
5. Subsequent uses trigger "Using a killed virtual register"
Both AMDGPU and x86 tests are included to show this is a general issue
in the peephole optimizer, not architecture-specific.
Co-Authored-By: Claude Sonnet 4 <noreply at anthropic.com>
---
.../peephole-fold-imm-copy-kill-bug.mir | 33 +++++++++++++++++++
.../X86/peephole-fold-imm-copy-kill-bug.mir | 32 ++++++++++++++++++
2 files changed, 65 insertions(+)
create mode 100644 llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
create mode 100644 llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
diff --git a/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir b/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
new file mode 100644
index 0000000000000..67412763ffcce
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
@@ -0,0 +1,33 @@
+# RUN: llc -mtriple=amdgcn-- -run-pass=peephole-opt %s -o - | FileCheck %s
+# RUN: not --crash llc -mtriple=amdgcn-- -run-pass=peephole-opt -verify-machineinstrs %s -o - 2>&1 | FileCheck %s --check-prefix=VERIFY
+
+# Test that foldImmediate correctly handles kill flags when eliminating
+# redundant instructions. Without the fix, kill flags are not cleared when
+# replacing the COPY's destination with the original immediate-defining register.
+
+---
+name: fold_imm_copy_kill_bug
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: fold_imm_copy_kill_bug
+ ; CHECK: %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+ ; CHECK-NEXT: %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+ ; CHECK-NEXT: %3:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, killed %0, %subreg.sub1
+ ; CHECK-NEXT: %4:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, %0, %subreg.sub1
+ ; CHECK-NEXT: S_ENDPGM 0
+
+ %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+ %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+
+ %2:vgpr_32 = COPY %0:vgpr_32
+
+ %3:vreg_64 = REG_SEQUENCE %1:vgpr_32, %subreg.sub0, killed %2:vgpr_32, %subreg.sub1
+ %4:vreg_64 = REG_SEQUENCE %1:vgpr_32, %subreg.sub0, %0:vgpr_32, %subreg.sub1
+
+ S_ENDPGM 0
+
+# VERIFY: *** Bad machine code: Using a killed virtual register ***
+# VERIFY: - function: fold_imm_copy_kill_bug
+# VERIFY: LLVM ERROR: Found 1 machine code errors.
+...
diff --git a/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir b/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
new file mode 100644
index 0000000000000..904146dedfd85
--- /dev/null
+++ b/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
@@ -0,0 +1,32 @@
+# RUN: llc -mtriple=x86_64-- -run-pass=peephole-opt %s -o - | FileCheck %s
+# RUN: not --crash llc -mtriple=x86_64-- -run-pass=peephole-opt -verify-machineinstrs %s -o - 2>&1 | FileCheck %s --check-prefix=VERIFY
+
+# Test that foldImmediate correctly handles kill flags when eliminating
+# redundant instructions. Without the fix, kill flags are not cleared when
+# replacing the COPY's destination with the original immediate-defining register.
+
+---
+name: fold_imm_copy_kill_bug
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: fold_imm_copy_kill_bug
+ ; CHECK: %0:gr32 = MOV32r0 implicit-def dead $eflags
+ ; CHECK-NEXT: %3:gr32 = ADD32ri killed %0, 42, implicit-def dead $eflags
+ ; CHECK-NEXT: %4:gr32 = ADD32ri %0, 42, implicit-def dead $eflags
+ ; CHECK-NEXT: RET 0
+
+ %0:gr32 = MOV32r0 implicit-def dead $eflags
+ %1:gr32 = MOV32ri 42
+
+ %2:gr32 = COPY %0:gr32
+
+ %3:gr32 = ADD32rr %1:gr32, killed %2:gr32, implicit-def dead $eflags
+ %4:gr32 = ADD32ri %0:gr32, 42, implicit-def dead $eflags
+
+ RET 0
+
+# VERIFY: *** Bad machine code: Using a killed virtual register ***
+# VERIFY: - function: fold_imm_copy_kill_bug
+# VERIFY: LLVM ERROR: Found 1 machine code errors.
+...
>From 0bddc27afc4bc1298df867d62664a522fe5fa637 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Mon, 4 May 2026 08:56:25 -0400
Subject: [PATCH 2/4] PeepholeOpt: Clear kill flags in foldImmediate
When foldImmediate replaces a COPY destination with its source,
it extends the live range of the source, but it does not
update the kill flags.
Clear kill flags on the source register after replacement.
---
llvm/lib/CodeGen/PeepholeOptimizer.cpp | 1 +
.../CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir | 9 ++-------
.../test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir | 9 ++-------
3 files changed, 5 insertions(+), 14 deletions(-)
diff --git a/llvm/lib/CodeGen/PeepholeOptimizer.cpp b/llvm/lib/CodeGen/PeepholeOptimizer.cpp
index 9365ea883eec9..1acca1fb2659f 100644
--- a/llvm/lib/CodeGen/PeepholeOptimizer.cpp
+++ b/llvm/lib/CodeGen/PeepholeOptimizer.cpp
@@ -1440,6 +1440,7 @@ bool PeepholeOptimizer::foldImmediate(
if (DstReg.isVirtual() &&
MRI->getRegClass(DstReg) == MRI->getRegClass(Reg)) {
MRI->replaceRegWith(DstReg, Reg);
+ MRI->clearKillFlags(Reg);
MI.eraseFromParent();
Deleted = true;
}
diff --git a/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir b/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
index 67412763ffcce..f710bba4627f4 100644
--- a/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
+++ b/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
@@ -1,5 +1,4 @@
-# RUN: llc -mtriple=amdgcn-- -run-pass=peephole-opt %s -o - | FileCheck %s
-# RUN: not --crash llc -mtriple=amdgcn-- -run-pass=peephole-opt -verify-machineinstrs %s -o - 2>&1 | FileCheck %s --check-prefix=VERIFY
+# RUN: llc -mtriple=amdgcn-- -run-pass=peephole-opt -verify-machineinstrs %s -o - | FileCheck %s
# Test that foldImmediate correctly handles kill flags when eliminating
# redundant instructions. Without the fix, kill flags are not cleared when
@@ -13,7 +12,7 @@ body: |
; CHECK-LABEL: name: fold_imm_copy_kill_bug
; CHECK: %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
; CHECK-NEXT: %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
- ; CHECK-NEXT: %3:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, killed %0, %subreg.sub1
+ ; CHECK-NEXT: %3:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, %0, %subreg.sub1
; CHECK-NEXT: %4:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, %0, %subreg.sub1
; CHECK-NEXT: S_ENDPGM 0
@@ -26,8 +25,4 @@ body: |
%4:vreg_64 = REG_SEQUENCE %1:vgpr_32, %subreg.sub0, %0:vgpr_32, %subreg.sub1
S_ENDPGM 0
-
-# VERIFY: *** Bad machine code: Using a killed virtual register ***
-# VERIFY: - function: fold_imm_copy_kill_bug
-# VERIFY: LLVM ERROR: Found 1 machine code errors.
...
diff --git a/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir b/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
index 904146dedfd85..e3802095d4173 100644
--- a/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
+++ b/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
@@ -1,5 +1,4 @@
-# RUN: llc -mtriple=x86_64-- -run-pass=peephole-opt %s -o - | FileCheck %s
-# RUN: not --crash llc -mtriple=x86_64-- -run-pass=peephole-opt -verify-machineinstrs %s -o - 2>&1 | FileCheck %s --check-prefix=VERIFY
+# RUN: llc -mtriple=x86_64-- -run-pass=peephole-opt -verify-machineinstrs %s -o - | FileCheck %s
# Test that foldImmediate correctly handles kill flags when eliminating
# redundant instructions. Without the fix, kill flags are not cleared when
@@ -12,7 +11,7 @@ body: |
bb.0:
; CHECK-LABEL: name: fold_imm_copy_kill_bug
; CHECK: %0:gr32 = MOV32r0 implicit-def dead $eflags
- ; CHECK-NEXT: %3:gr32 = ADD32ri killed %0, 42, implicit-def dead $eflags
+ ; CHECK-NEXT: %3:gr32 = ADD32ri %0, 42, implicit-def dead $eflags
; CHECK-NEXT: %4:gr32 = ADD32ri %0, 42, implicit-def dead $eflags
; CHECK-NEXT: RET 0
@@ -25,8 +24,4 @@ body: |
%4:gr32 = ADD32ri %0:gr32, 42, implicit-def dead $eflags
RET 0
-
-# VERIFY: *** Bad machine code: Using a killed virtual register ***
-# VERIFY: - function: fold_imm_copy_kill_bug
-# VERIFY: LLVM ERROR: Found 1 machine code errors.
...
>From 60ae3dc14cf79d7eea2bd4477e4e54bf006599f1 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Tue, 5 May 2026 08:24:48 -0400
Subject: [PATCH 3/4] Move test to existing files
---
.../peephole-fold-imm-copy-kill-bug.mir | 28 -------------------
.../test/CodeGen/AMDGPU/peephole-fold-imm.mir | 26 +++++++++++++++++
.../X86/peephole-fold-imm-copy-kill-bug.mir | 27 ------------------
llvm/test/CodeGen/X86/peephole.mir | 27 ++++++++++++++++++
4 files changed, 53 insertions(+), 55 deletions(-)
delete mode 100644 llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
delete mode 100644 llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
diff --git a/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir b/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
deleted file mode 100644
index f710bba4627f4..0000000000000
--- a/llvm/test/CodeGen/AMDGPU/peephole-fold-imm-copy-kill-bug.mir
+++ /dev/null
@@ -1,28 +0,0 @@
-# RUN: llc -mtriple=amdgcn-- -run-pass=peephole-opt -verify-machineinstrs %s -o - | FileCheck %s
-
-# Test that foldImmediate correctly handles kill flags when eliminating
-# redundant instructions. Without the fix, kill flags are not cleared when
-# replacing the COPY's destination with the original immediate-defining register.
-
----
-name: fold_imm_copy_kill_bug
-tracksRegLiveness: true
-body: |
- bb.0:
- ; CHECK-LABEL: name: fold_imm_copy_kill_bug
- ; CHECK: %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
- ; CHECK-NEXT: %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
- ; CHECK-NEXT: %3:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, %0, %subreg.sub1
- ; CHECK-NEXT: %4:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, %0, %subreg.sub1
- ; CHECK-NEXT: S_ENDPGM 0
-
- %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
- %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
-
- %2:vgpr_32 = COPY %0:vgpr_32
-
- %3:vreg_64 = REG_SEQUENCE %1:vgpr_32, %subreg.sub0, killed %2:vgpr_32, %subreg.sub1
- %4:vreg_64 = REG_SEQUENCE %1:vgpr_32, %subreg.sub0, %0:vgpr_32, %subreg.sub1
-
- S_ENDPGM 0
-...
diff --git a/llvm/test/CodeGen/AMDGPU/peephole-fold-imm.mir b/llvm/test/CodeGen/AMDGPU/peephole-fold-imm.mir
index 176d7752133cf..e4fe3b951cf98 100644
--- a/llvm/test/CodeGen/AMDGPU/peephole-fold-imm.mir
+++ b/llvm/test/CodeGen/AMDGPU/peephole-fold-imm.mir
@@ -864,3 +864,29 @@ body: |
SI_RETURN_TO_EPILOG $sgpr0_lo16
...
+---
+# Test that foldImmediate correctly handles kill flags when eliminating
+# redundant instructions. Without the fix, kill flags are not cleared when
+# replacing the COPY's destination with the original immediate-defining register.
+name: fold_imm_copy_kill_bug
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; GCN-LABEL: name: fold_imm_copy_kill_bug
+ ; GCN: %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+ ; GCN-NEXT: %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+ ; GCN-NEXT: %3:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, %0, %subreg.sub1
+ ; GCN-NEXT: %4:vreg_64 = REG_SEQUENCE %1, %subreg.sub0, %0, %subreg.sub1
+ ; GCN-NEXT: S_ENDPGM 0
+
+ %0:vgpr_32 = V_MOV_B32_e32 0, implicit $exec
+ %1:vgpr_32 = V_MOV_B32_e32 1, implicit $exec
+
+ %2:vgpr_32 = COPY %0:vgpr_32
+
+ %3:vreg_64 = REG_SEQUENCE %1:vgpr_32, %subreg.sub0, killed %2:vgpr_32, %subreg.sub1
+ %4:vreg_64 = REG_SEQUENCE %1:vgpr_32, %subreg.sub0, %0:vgpr_32, %subreg.sub1
+
+ S_ENDPGM 0
+
+...
diff --git a/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir b/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
deleted file mode 100644
index e3802095d4173..0000000000000
--- a/llvm/test/CodeGen/X86/peephole-fold-imm-copy-kill-bug.mir
+++ /dev/null
@@ -1,27 +0,0 @@
-# RUN: llc -mtriple=x86_64-- -run-pass=peephole-opt -verify-machineinstrs %s -o - | FileCheck %s
-
-# Test that foldImmediate correctly handles kill flags when eliminating
-# redundant instructions. Without the fix, kill flags are not cleared when
-# replacing the COPY's destination with the original immediate-defining register.
-
----
-name: fold_imm_copy_kill_bug
-tracksRegLiveness: true
-body: |
- bb.0:
- ; CHECK-LABEL: name: fold_imm_copy_kill_bug
- ; CHECK: %0:gr32 = MOV32r0 implicit-def dead $eflags
- ; CHECK-NEXT: %3:gr32 = ADD32ri %0, 42, implicit-def dead $eflags
- ; CHECK-NEXT: %4:gr32 = ADD32ri %0, 42, implicit-def dead $eflags
- ; CHECK-NEXT: RET 0
-
- %0:gr32 = MOV32r0 implicit-def dead $eflags
- %1:gr32 = MOV32ri 42
-
- %2:gr32 = COPY %0:gr32
-
- %3:gr32 = ADD32rr %1:gr32, killed %2:gr32, implicit-def dead $eflags
- %4:gr32 = ADD32ri %0:gr32, 42, implicit-def dead $eflags
-
- RET 0
-...
diff --git a/llvm/test/CodeGen/X86/peephole.mir b/llvm/test/CodeGen/X86/peephole.mir
index efcb1582e19cc..b23af5ce41c6d 100644
--- a/llvm/test/CodeGen/X86/peephole.mir
+++ b/llvm/test/CodeGen/X86/peephole.mir
@@ -1,6 +1,7 @@
# RUN: llc -mtriple=x86_64-- -run-pass=peephole-opt %s -o - | FileCheck %s
--- |
define void @func() { ret void }
+ define void @fold_imm_copy_kill_bug() { ret void }
...
---
# Check that instructions with MI.isBitcast() are only replaced by COPY if there
@@ -38,3 +39,29 @@ body: |
%6 = SUBREG_TO_REG %5, %subreg.sub_32bit
NOOP implicit %6
...
+
+---
+# Test that foldImmediate correctly handles kill flags when eliminating
+# redundant instructions. Without the fix, kill flags are not cleared when
+# replacing the COPY's destination with the original immediate-defining register.
+name: fold_imm_copy_kill_bug
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: fold_imm_copy_kill_bug
+ ; CHECK: %0:gr32 = MOV32r0 implicit-def dead $eflags
+ ; CHECK-NEXT: %3:gr32 = ADD32ri %0, 42, implicit-def dead $eflags
+ ; CHECK-NEXT: %4:gr32 = ADD32ri %0, 42, implicit-def dead $eflags
+ ; CHECK-NEXT: RET 0
+
+ %0:gr32 = MOV32r0 implicit-def dead $eflags
+ %1:gr32 = MOV32ri 42
+
+ %2:gr32 = COPY %0:gr32
+
+ %3:gr32 = ADD32rr %1:gr32, killed %2:gr32, implicit-def dead $eflags
+ %4:gr32 = ADD32ri %0:gr32, 42, implicit-def dead $eflags
+
+ RET 0
+
+...
>From 6adfd08f10f414849291c8431a2c73cf3c396996 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Tue, 5 May 2026 09:29:23 -0400
Subject: [PATCH 4/4] Trigger build
More information about the llvm-commits
mailing list