[llvm] [AMDGPU] Fix si-pre-emit-peephole to preserve S_AND when SCC is alive (PR #215829)
Frederik Harwath via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 13 02:14:49 PDT 2026
https://github.com/frederik-h updated https://github.com/llvm/llvm-project/pull/215829
>From 4b018dd1053722c9cc056ec91da77d8b500ac4b3 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Wed, 12 Aug 2026 09:08:09 -0400
Subject: [PATCH 1/3] [AMDGPU] Add test showing SCC liveness bug in
si-pre-emit-peephole
si-pre-emit-peephole incorrectly removes S_AND_B64 instructions in VCC
branch optimization, without considering that the SCC value may be
needed by subsequent instructions like S_CSELECT.
---
.../CodeGen/AMDGPU/insert-skip-from-vcc.mir | 19 ++++
...eemit-peephole-scc-liveness-issue215745.ll | 86 +++++++++++++++++++
2 files changed, 105 insertions(+)
create mode 100644 llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir b/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
index 27f84ca8e3b5c..ac731c283e610 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
@@ -708,3 +708,22 @@ body: |
S_CBRANCH_VCCZ %bb.1, implicit killed $vcc
S_ENDPGM 0
...
+
+---
+# GCN-LABEL: name: and_cmp_vccnz_live_scc
+# GCN: V_CMP_GT_I64_e32 $sgpr0_sgpr1, $vgpr0_vgpr1, implicit-def $vcc, implicit $exec
+# GCN-NOT: S_AND_
+# GCN-NEXT: S_CBRANCH_VCCNZ %bb.1, implicit $vcc
+# GCN: $sgpr0 = S_CSELECT_B32 $sgpr2, $sgpr3, implicit $scc
+name: and_cmp_vccnz_live_scc
+body: |
+ bb.0:
+ liveins: $sgpr0_sgpr1, $sgpr2, $sgpr3, $vgpr0_vgpr1
+ V_CMP_GT_I64_e32 $sgpr0_sgpr1, $vgpr0_vgpr1, implicit-def $vcc, implicit $exec
+ $vcc = S_AND_B64 $exec, $vcc, implicit-def $scc
+ S_CBRANCH_VCCNZ %bb.1, implicit $vcc
+
+ bb.1:
+ $sgpr0 = S_CSELECT_B32 $sgpr2, $sgpr3, implicit $scc
+ S_ENDPGM 0
+...
diff --git a/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll b/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
new file mode 100644
index 0000000000000..7336b944ecfa4
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
@@ -0,0 +1,86 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 < %s | FileCheck -check-prefix=GFX942 %s
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1100 < %s | FileCheck -check-prefix=GFX1100 %s
+
+; Test that si-pre-emit-peephole does not remove S_AND_B64 when its implicit
+; SCC definition has a user. This is a reduced version of the reproducer from issue
+; #215745.
+
+define amdgpu_kernel void @triangular_loop_scc_liveness(i64 %n) {
+; GFX942-LABEL: triangular_loop_scc_liveness:
+; GFX942: ; %bb.0: ; %entry
+; GFX942-NEXT: s_load_dwordx2 s[2:3], s[4:5], 0x0
+; GFX942-NEXT: s_mov_b64 s[4:5], 1
+; GFX942-NEXT: s_and_b64 s[0:1], exec, 0
+; GFX942-NEXT: .LBB0_1: ; %outer
+; GFX942-NEXT: ; =>This Loop Header: Depth=1
+; GFX942-NEXT: ; Child Loop BB0_3 Depth 2
+; GFX942-NEXT: s_waitcnt lgkmcnt(0)
+; GFX942-NEXT: v_mov_b64_e32 v[0:1], s[2:3]
+; GFX942-NEXT: v_cmp_gt_i64_e32 vcc, s[4:5], v[0:1]
+; GFX942-NEXT: s_mov_b64 s[4:5], 0
+; GFX942-NEXT: s_cbranch_vccnz .LBB0_1
+; GFX942-NEXT: ; %bb.2: ; %inner.preheader
+; GFX942-NEXT: ; in Loop: Header=BB0_1 Depth=1
+; GFX942-NEXT: s_cselect_b32 s5, 0, s3
+; GFX942-NEXT: s_cselect_b32 s4, 0, s2
+; GFX942-NEXT: s_cmp_eq_u64 s[4:5], 0
+; GFX942-NEXT: s_cselect_b64 s[4:5], -1, 0
+; GFX942-NEXT: s_and_b64 vcc, exec, s[4:5]
+; GFX942-NEXT: .LBB0_3: ; %TransitionBlock
+; GFX942-NEXT: ; Parent Loop BB0_1 Depth=1
+; GFX942-NEXT: ; => This Inner Loop Header: Depth=2
+; GFX942-NEXT: s_mov_b64 vcc, vcc
+; GFX942-NEXT: s_cbranch_vccz .LBB0_3
+; GFX942-NEXT: ; %bb.4: ; %loop.exit.guard
+; GFX942-NEXT: ; in Loop: Header=BB0_1 Depth=1
+; GFX942-NEXT: s_mov_b64 s[4:5], 0
+; GFX942-NEXT: s_mov_b64 vcc, s[0:1]
+; GFX942-NEXT: s_cbranch_vccz .LBB0_1
+; GFX942-NEXT: ; %bb.5: ; %DummyReturnBlock
+; GFX942-NEXT: s_endpgm
+;
+; GFX1100-LABEL: triangular_loop_scc_liveness:
+; GFX1100: ; %bb.0: ; %entry
+; GFX1100-NEXT: s_load_b64 s[0:1], s[4:5], 0x0
+; GFX1100-NEXT: s_mov_b64 s[2:3], 1
+; GFX1100-NEXT: .LBB0_1: ; %outer
+; GFX1100-NEXT: ; =>This Loop Header: Depth=1
+; GFX1100-NEXT: ; Child Loop BB0_3 Depth 2
+; GFX1100-NEXT: s_waitcnt lgkmcnt(0)
+; GFX1100-NEXT: v_cmp_gt_i64_e64 s2, s[2:3], s[0:1]
+; GFX1100-NEXT: s_and_b32 vcc_lo, exec_lo, s2
+; GFX1100-NEXT: s_mov_b64 s[2:3], 0
+; GFX1100-NEXT: s_cbranch_vccnz .LBB0_1
+; GFX1100-NEXT: ; %bb.2: ; %inner.preheader
+; GFX1100-NEXT: ; in Loop: Header=BB0_1 Depth=1
+; GFX1100-NEXT: s_cselect_b32 s3, 0, s1
+; GFX1100-NEXT: s_cselect_b32 s2, 0, s0
+; GFX1100-NEXT: s_delay_alu instid0(SALU_CYCLE_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
+; GFX1100-NEXT: s_cmp_eq_u64 s[2:3], 0
+; GFX1100-NEXT: s_cselect_b32 s2, -1, 0
+; GFX1100-NEXT: s_and_b32 vcc_lo, exec_lo, s2
+; GFX1100-NEXT: .LBB0_3: ; %TransitionBlock
+; GFX1100-NEXT: ; Parent Loop BB0_1 Depth=1
+; GFX1100-NEXT: ; => This Inner Loop Header: Depth=2
+; GFX1100-NEXT: s_cbranch_vccz .LBB0_3
+; GFX1100-NEXT: ; %bb.4: ; %loop.exit.guard
+; GFX1100-NEXT: ; in Loop: Header=BB0_1 Depth=1
+; GFX1100-NEXT: s_mov_b64 s[2:3], 0
+; GFX1100-NEXT: s_mov_b32 vcc_lo, 0
+; GFX1100-NEXT: s_cbranch_vccz .LBB0_1
+; GFX1100-NEXT: ; %bb.5: ; %DummyReturnBlock
+; GFX1100-NEXT: s_endpgm
+entry:
+ br label %outer
+
+outer: ; preds = %inner, %outer, %entry
+ %k = phi i64 [ 1, %entry ], [ 0, %inner ], [ 0, %outer ]
+ %kgtn = icmp sgt i64 %k, %n
+ %bound = select i1 %kgtn, i64 0, i64 %n
+ br i1 %kgtn, label %outer, label %inner
+
+inner: ; preds = %inner, %outer
+ %done = icmp eq i64 0, %bound
+ br i1 %done, label %outer, label %inner
+}
>From 6333dc9f8cb40f4eb5d75675df8d222c8897375f Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Wed, 12 Aug 2026 09:50:06 -0400
Subject: [PATCH 2/3] [AMDGPU] Fix si-pre-emit-peephole to preserve S_AND when
SCC is alive
The optimization in si-pre-emit-peephole that removes S_AND_B64 after
V_CMP instructions was not checking if the implicit SCC def was alive.
This caused miscompiles when the SCC value was used by subsequent instructions.
Fixes #215745
---
llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp | 6 ++++--
llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir | 2 +-
.../AMDGPU/preemit-peephole-scc-liveness-issue215745.ll | 1 +
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
index e43e753e1314d..45b9dd3df39b9 100644
--- a/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
+++ b/llvm/lib/Target/AMDGPU/SIPreEmitPeephole.cpp
@@ -237,14 +237,16 @@ bool SIPreEmitPeephole::optimizeVccBranch(MachineInstr &MI) const {
if (M == E)
return Changed;
// If SReg is VCC and SReg definition is a VALU comparison.
- // This means S_AND with EXEC is not required.
+ // This means S_AND with EXEC is not required, unless
+ // the implicit def of SCC is alive.
// Erase the S_AND and return.
// Note: isVOPC is used instead of isCompare to catch V_CMP_CLASS
if (A->getOpcode() == And && SReg == CondReg && !ModifiesExec &&
- TII->isVOPC(*M)) {
+ TII->isVOPC(*M) && A->registerDefIsDead(AMDGPU::SCC, /*TRI=*/nullptr)) {
A->eraseFromParent();
return true;
}
+
if (!M->isMoveImmediate() || !M->getOperand(1).isImm() ||
(M->getOperand(1).getImm() != -1 && M->getOperand(1).getImm() != 0))
return Changed;
diff --git a/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir b/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
index ac731c283e610..3081a2c1d1fe1 100644
--- a/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
+++ b/llvm/test/CodeGen/AMDGPU/insert-skip-from-vcc.mir
@@ -712,7 +712,7 @@ body: |
---
# GCN-LABEL: name: and_cmp_vccnz_live_scc
# GCN: V_CMP_GT_I64_e32 $sgpr0_sgpr1, $vgpr0_vgpr1, implicit-def $vcc, implicit $exec
-# GCN-NOT: S_AND_
+# GCN-NEXT: $vcc = S_AND_B64 $exec, $vcc, implicit-def $scc
# GCN-NEXT: S_CBRANCH_VCCNZ %bb.1, implicit $vcc
# GCN: $sgpr0 = S_CSELECT_B32 $sgpr2, $sgpr3, implicit $scc
name: and_cmp_vccnz_live_scc
diff --git a/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll b/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
index 7336b944ecfa4..7a24ee933c173 100644
--- a/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
+++ b/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
@@ -19,6 +19,7 @@ define amdgpu_kernel void @triangular_loop_scc_liveness(i64 %n) {
; GFX942-NEXT: v_mov_b64_e32 v[0:1], s[2:3]
; GFX942-NEXT: v_cmp_gt_i64_e32 vcc, s[4:5], v[0:1]
; GFX942-NEXT: s_mov_b64 s[4:5], 0
+; GFX942-NEXT: s_and_b64 vcc, exec, vcc
; GFX942-NEXT: s_cbranch_vccnz .LBB0_1
; GFX942-NEXT: ; %bb.2: ; %inner.preheader
; GFX942-NEXT: ; in Loop: Header=BB0_1 Depth=1
>From 77f369fc362bfd5ef0809a5444e786f9cde52057 Mon Sep 17 00:00:00 2001
From: Frederik Harwath <fharwath at amd.com>
Date: Thu, 13 Aug 2026 05:13:55 -0400
Subject: [PATCH 3/3] Use new subarch triples
---
.../AMDGPU/preemit-peephole-scc-liveness-issue215745.ll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll b/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
index 7a24ee933c173..09174fcfc3f35 100644
--- a/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
+++ b/llvm/test/CodeGen/AMDGPU/preemit-peephole-scc-liveness-issue215745.ll
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx942 < %s | FileCheck -check-prefix=GFX942 %s
-; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1100 < %s | FileCheck -check-prefix=GFX1100 %s
+; RUN: llc -mtriple=amdgpu9.42-amd-amdhsa < %s | FileCheck -check-prefix=GFX942 %s
+; RUN: llc -mtriple=amdgpu11.00-amd-amdhsa < %s | FileCheck -check-prefix=GFX1100 %s
; Test that si-pre-emit-peephole does not remove S_AND_B64 when its implicit
; SCC definition has a user. This is a reduced version of the reproducer from issue
More information about the llvm-commits
mailing list