[llvm] [AMDGPU] Improve amdgcn_kill verification (PR #208220)
Robert Imschweiler via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 8 09:20:07 PDT 2026
https://github.com/ro-i updated https://github.com/llvm/llvm-project/pull/208220
>From 1d9708a43f1ab3fbe0df197170097e4ae189cb8c Mon Sep 17 00:00:00 2001
From: Robert Imschweiler <robert.imschweiler at amd.com>
Date: Wed, 8 Jul 2026 07:55:45 -0500
Subject: [PATCH 1/3] [AMDGPU] Improve amdgcn_kill verification
---
llvm/lib/IR/VerifierAMDGPU.cpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/IR/VerifierAMDGPU.cpp b/llvm/lib/IR/VerifierAMDGPU.cpp
index 04cb214ef2520..ab63307570b28 100644
--- a/llvm/lib/IR/VerifierAMDGPU.cpp
+++ b/llvm/lib/IR/VerifierAMDGPU.cpp
@@ -145,7 +145,12 @@ void llvm::verifyAMDGPUIntrinsicCall(VerifierSupport &VS, Intrinsic::ID ID,
Check(CBI->getNumIndirectDests() == 1,
"callbr amdgcn_kill only supports one indirect dest");
bool Unreachable = isa<UnreachableInst>(CBI->getIndirectDest(0)->begin());
- CallInst *CI = dyn_cast<CallInst>(CBI->getIndirectDest(0)->begin());
+ // We assume that amdgcn_unreachable is only introduced by
+ // AMDGPUUnifyDivergentExitNodes, which replaces the block's original
+ // unreachable terminator by a call to amdgcn_unreachable + a return.
+ const Instruction *Term = CBI->getIndirectDest(0)->getTerminator();
+ const CallInst *CI =
+ Term ? dyn_cast_if_present<CallInst>(Term->getPrevNode()) : nullptr;
Check(Unreachable ||
(CI && CI->getIntrinsicID() == Intrinsic::amdgcn_unreachable),
"callbr amdgcn_kill indirect dest needs to be unreachable");
>From 553ef7e5e5f318b82a777456117b57c64a64941b Mon Sep 17 00:00:00 2001
From: Robert Imschweiler <robert.imschweiler at amd.com>
Date: Wed, 8 Jul 2026 09:07:20 -0500
Subject: [PATCH 2/3] add test and also improve unreachable detection
---
llvm/lib/IR/VerifierAMDGPU.cpp | 3 +-
llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll | 60 +++++++++++++++++++
2 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/IR/VerifierAMDGPU.cpp b/llvm/lib/IR/VerifierAMDGPU.cpp
index ab63307570b28..a668d3135644e 100644
--- a/llvm/lib/IR/VerifierAMDGPU.cpp
+++ b/llvm/lib/IR/VerifierAMDGPU.cpp
@@ -144,14 +144,13 @@ void llvm::verifyAMDGPUIntrinsicCall(VerifierSupport &VS, Intrinsic::ID ID,
if (auto *CBI = dyn_cast<CallBrInst>(&Call)) {
Check(CBI->getNumIndirectDests() == 1,
"callbr amdgcn_kill only supports one indirect dest");
- bool Unreachable = isa<UnreachableInst>(CBI->getIndirectDest(0)->begin());
// We assume that amdgcn_unreachable is only introduced by
// AMDGPUUnifyDivergentExitNodes, which replaces the block's original
// unreachable terminator by a call to amdgcn_unreachable + a return.
const Instruction *Term = CBI->getIndirectDest(0)->getTerminator();
const CallInst *CI =
Term ? dyn_cast_if_present<CallInst>(Term->getPrevNode()) : nullptr;
- Check(Unreachable ||
+ Check(isa_and_nonnull<UnreachableInst>(Term) ||
(CI && CI->getIntrinsicID() == Intrinsic::amdgcn_unreachable),
"callbr amdgcn_kill indirect dest needs to be unreachable");
}
diff --git a/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll b/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll
index 8ac31b3c70ed7..123c817202723 100644
--- a/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll
+++ b/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll
@@ -113,3 +113,63 @@ cont:
kill:
unreachable
}
+
+define void @test_kill_unreachable_not_first(ptr %src, ptr %dst, i1 %c) {
+; CHECK-LABEL: test_kill_unreachable_not_first:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT: flat_load_dword v0, v[0:1]
+; CHECK-NEXT: v_and_b32_e32 v1, 1, v4
+; CHECK-NEXT: v_cmp_eq_u32_e32 vcc, 1, v1
+; CHECK-NEXT: s_mov_b64 s[4:5], exec
+; CHECK-NEXT: s_andn2_b64 s[6:7], exec, vcc
+; CHECK-NEXT: s_andn2_b64 s[4:5], s[4:5], s[6:7]
+; CHECK-NEXT: s_cbranch_scc0 .LBB2_2
+; CHECK-NEXT: ; %bb.1:
+; CHECK-NEXT: s_and_b64 exec, exec, s[4:5]
+; CHECK-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; CHECK-NEXT: flat_store_dword v[2:3], v0
+; CHECK-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; CHECK-NEXT: s_setpc_b64 s[30:31]
+; CHECK-NEXT: .LBB2_2:
+; CHECK-NEXT: s_mov_b64 exec, 0
+; CHECK-NEXT: s_endpgm
+;
+; GISEL-LABEL: test_kill_unreachable_not_first:
+; GISEL: ; %bb.0:
+; GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; GISEL-NEXT: flat_load_dword v0, v[0:1]
+; GISEL-NEXT: v_and_b32_e32 v1, 1, v4
+; GISEL-NEXT: v_cmp_ne_u32_e32 vcc, 0, v1
+; GISEL-NEXT: s_mov_b64 s[4:5], exec
+; GISEL-NEXT: s_andn2_b64 s[6:7], exec, vcc
+; GISEL-NEXT: s_andn2_b64 s[4:5], s[4:5], s[6:7]
+; GISEL-NEXT: s_cbranch_scc0 .LBB2_4
+; GISEL-NEXT: ; %bb.1:
+; GISEL-NEXT: s_and_b64 exec, exec, s[4:5]
+; GISEL-NEXT: ; %bb.2: ; %cont
+; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; GISEL-NEXT: flat_store_dword v[2:3], v0
+; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; GISEL-NEXT: s_setpc_b64 s[30:31]
+; GISEL-NEXT: .LBB2_3: ; Inline asm indirect target
+; GISEL-NEXT: ; %kill
+; GISEL-NEXT: ; Label of block must be emitted
+; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; GISEL-NEXT: v_mov_b32_e32 v0, 0
+; GISEL-NEXT: flat_store_dword v[2:3], v0
+; GISEL-NEXT: ; divergent unreachable
+; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
+; GISEL-NEXT: s_setpc_b64 s[30:31]
+; GISEL-NEXT: .LBB2_4:
+; GISEL-NEXT: s_mov_b64 exec, 0
+; GISEL-NEXT: s_endpgm
+ %a = load i32, ptr %src, align 4
+ callbr void @llvm.amdgcn.kill(i1 %c) to label %cont [label %kill]
+kill:
+ store i32 0, ptr %dst, align 4
+ unreachable
+cont:
+ store i32 %a, ptr %dst, align 4
+ ret void
+}
>From 940824d4d5775ac557a8ac6e36ec5f7c616d3924 Mon Sep 17 00:00:00 2001
From: Robert Imschweiler <robert.imschweiler at amd.com>
Date: Wed, 8 Jul 2026 11:19:25 -0500
Subject: [PATCH 3/3] move tests
---
llvm/test/Assembler/callbr.ll | 41 +++++++++++++
llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll | 60 -------------------
2 files changed, 41 insertions(+), 60 deletions(-)
diff --git a/llvm/test/Assembler/callbr.ll b/llvm/test/Assembler/callbr.ll
index 37fd777e1a395..6e63bdcbfa9ca 100644
--- a/llvm/test/Assembler/callbr.ll
+++ b/llvm/test/Assembler/callbr.ll
@@ -1,6 +1,7 @@
; RUN: llvm-as < %s | llvm-dis | FileCheck %s
declare void @llvm.amdgcn.kill(i1)
+declare void @llvm.amdgcn.unreachable()
define void @test_kill(i1 %c) {
; CHECK-LABEL: define void @test_kill(
@@ -18,3 +19,43 @@ kill:
cont:
ret void
}
+
+define void @test_kill_unreachable_not_first(i1 %c, ptr %p) {
+; CHECK-LABEL: define void @test_kill_unreachable_not_first(
+; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: callbr void @llvm.amdgcn.kill(i1 [[C]])
+; CHECK-NEXT: to label %[[CONT:.*]] [label %kill]
+; CHECK: [[KILL:.*:]]
+; CHECK-NEXT: store i32 0, ptr [[P]], align 4
+; CHECK-NEXT: unreachable
+; CHECK: [[CONT]]:
+; CHECK-NEXT: ret void
+;
+ callbr void @llvm.amdgcn.kill(i1 %c) to label %cont [label %kill]
+kill:
+ store i32 0, ptr %p, align 4
+ unreachable
+cont:
+ ret void
+}
+
+define void @test_kill_amdgcn_unreachable_not_first(i1 %c, ptr %p) {
+; CHECK-LABEL: define void @test_kill_amdgcn_unreachable_not_first(
+; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: callbr void @llvm.amdgcn.kill(i1 [[C]])
+; CHECK-NEXT: to label %[[CONT:.*]] [label %kill]
+; CHECK: [[KILL:.*:]]
+; CHECK-NEXT: store i32 0, ptr [[P]], align 4
+; CHECK-NEXT: call void @llvm.amdgcn.unreachable()
+; CHECK-NEXT: ret void
+; CHECK: [[CONT]]:
+; CHECK-NEXT: ret void
+;
+ callbr void @llvm.amdgcn.kill(i1 %c) to label %cont [label %kill]
+kill:
+ store i32 0, ptr %p, align 4
+ call void @llvm.amdgcn.unreachable()
+ ret void
+cont:
+ ret void
+}
diff --git a/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll b/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll
index 123c817202723..8ac31b3c70ed7 100644
--- a/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll
+++ b/llvm/test/CodeGen/AMDGPU/callbr-intrinsics.ll
@@ -113,63 +113,3 @@ cont:
kill:
unreachable
}
-
-define void @test_kill_unreachable_not_first(ptr %src, ptr %dst, i1 %c) {
-; CHECK-LABEL: test_kill_unreachable_not_first:
-; CHECK: ; %bb.0:
-; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; CHECK-NEXT: flat_load_dword v0, v[0:1]
-; CHECK-NEXT: v_and_b32_e32 v1, 1, v4
-; CHECK-NEXT: v_cmp_eq_u32_e32 vcc, 1, v1
-; CHECK-NEXT: s_mov_b64 s[4:5], exec
-; CHECK-NEXT: s_andn2_b64 s[6:7], exec, vcc
-; CHECK-NEXT: s_andn2_b64 s[4:5], s[4:5], s[6:7]
-; CHECK-NEXT: s_cbranch_scc0 .LBB2_2
-; CHECK-NEXT: ; %bb.1:
-; CHECK-NEXT: s_and_b64 exec, exec, s[4:5]
-; CHECK-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
-; CHECK-NEXT: flat_store_dword v[2:3], v0
-; CHECK-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
-; CHECK-NEXT: s_setpc_b64 s[30:31]
-; CHECK-NEXT: .LBB2_2:
-; CHECK-NEXT: s_mov_b64 exec, 0
-; CHECK-NEXT: s_endpgm
-;
-; GISEL-LABEL: test_kill_unreachable_not_first:
-; GISEL: ; %bb.0:
-; GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
-; GISEL-NEXT: flat_load_dword v0, v[0:1]
-; GISEL-NEXT: v_and_b32_e32 v1, 1, v4
-; GISEL-NEXT: v_cmp_ne_u32_e32 vcc, 0, v1
-; GISEL-NEXT: s_mov_b64 s[4:5], exec
-; GISEL-NEXT: s_andn2_b64 s[6:7], exec, vcc
-; GISEL-NEXT: s_andn2_b64 s[4:5], s[4:5], s[6:7]
-; GISEL-NEXT: s_cbranch_scc0 .LBB2_4
-; GISEL-NEXT: ; %bb.1:
-; GISEL-NEXT: s_and_b64 exec, exec, s[4:5]
-; GISEL-NEXT: ; %bb.2: ; %cont
-; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
-; GISEL-NEXT: flat_store_dword v[2:3], v0
-; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
-; GISEL-NEXT: s_setpc_b64 s[30:31]
-; GISEL-NEXT: .LBB2_3: ; Inline asm indirect target
-; GISEL-NEXT: ; %kill
-; GISEL-NEXT: ; Label of block must be emitted
-; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
-; GISEL-NEXT: v_mov_b32_e32 v0, 0
-; GISEL-NEXT: flat_store_dword v[2:3], v0
-; GISEL-NEXT: ; divergent unreachable
-; GISEL-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0)
-; GISEL-NEXT: s_setpc_b64 s[30:31]
-; GISEL-NEXT: .LBB2_4:
-; GISEL-NEXT: s_mov_b64 exec, 0
-; GISEL-NEXT: s_endpgm
- %a = load i32, ptr %src, align 4
- callbr void @llvm.amdgcn.kill(i1 %c) to label %cont [label %kill]
-kill:
- store i32 0, ptr %dst, align 4
- unreachable
-cont:
- store i32 %a, ptr %dst, align 4
- ret void
-}
More information about the llvm-commits
mailing list