[llvm] [AMDGPU][Verifier] Mark calls to entry functions as invalid in the IR verifier (PR #134910)

Shilei Tian via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 9 07:02:49 PDT 2025


https://github.com/shiltian updated https://github.com/llvm/llvm-project/pull/134910

>From fab4fdde2a7ec951504b60bbd9d567e77348649f Mon Sep 17 00:00:00 2001
From: Shilei Tian <i at tianshilei.me>
Date: Wed, 9 Apr 2025 10:02:12 -0400
Subject: [PATCH] [AMDGPU][Verifier] Mark calls to entry functions as invalid
 in the IR verifier

For AMDGPU, calls to entry functions are invalid. Previously, due to certain
limitations, this restriction was not enforced by the IR verifier. These
limitations have now been resolved, enabling us to enforce this check.

Adding target-dependent checks directly into the IR verifier is not ideal.
However, a cleaner solution, such as a dedicated target-dependent IR verifier,
is underway (e.g., https://github.com/llvm/llvm-project/pull/123609). Once that
or similar code is merged, we can move this check accordingly.
---
 llvm/include/llvm/IR/CallingConv.h            |  20 ++
 llvm/lib/IR/Verifier.cpp                      |  10 +-
 .../AMDGPU/attributor-flatscratchinit.ll      |  15 --
 .../AMDGPU/call-to-kernel-undefined.ll        |  20 --
 llvm/test/CodeGen/AMDGPU/call-to-kernel.ll    |  18 --
 llvm/test/Verifier/amdgpu-cc.ll               |  23 ---
 .../call-to-non-callable-functions.ll         | 181 ++++++++++++++++++
 7 files changed, 203 insertions(+), 84 deletions(-)
 delete mode 100644 llvm/test/CodeGen/AMDGPU/call-to-kernel-undefined.ll
 delete mode 100644 llvm/test/CodeGen/AMDGPU/call-to-kernel.ll
 create mode 100644 llvm/test/Verifier/call-to-non-callable-functions.ll

diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h
index 7897aabb6c1a9..19892c94159ef 100644
--- a/llvm/include/llvm/IR/CallingConv.h
+++ b/llvm/include/llvm/IR/CallingConv.h
@@ -290,6 +290,26 @@ namespace CallingConv {
 
 } // end namespace CallingConv
 
+/// \return true if the calling convention allows the function to be called
+/// directly or indirectly via a call-like instruction.
+constexpr bool isCallableCC(CallingConv::ID CC) {
+  switch (CC) {
+  case CallingConv::AMDGPU_CS_Chain:
+  case CallingConv::AMDGPU_CS_ChainPreserve:
+  case CallingConv::AMDGPU_CS:
+  case CallingConv::AMDGPU_ES:
+  case CallingConv::AMDGPU_GS:
+  case CallingConv::AMDGPU_HS:
+  case CallingConv::AMDGPU_KERNEL:
+  case CallingConv::AMDGPU_LS:
+  case CallingConv::AMDGPU_PS:
+  case CallingConv::AMDGPU_VS:
+    return false;
+  default:
+    return true;
+  }
+}
+
 } // end namespace llvm
 
 #endif // LLVM_IR_CALLINGCONV_H
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 7423e746dfa9a..7e08d4eda2b60 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -3598,14 +3598,8 @@ void Verifier::visitCallBase(CallBase &Call) {
     Check(Callee->getValueType() == FTy,
           "Intrinsic called with incompatible signature", Call);
 
-  // Disallow calls to functions with the amdgpu_cs_chain[_preserve] calling
-  // convention.
-  auto CC = Call.getCallingConv();
-  Check(CC != CallingConv::AMDGPU_CS_Chain &&
-            CC != CallingConv::AMDGPU_CS_ChainPreserve,
-        "Direct calls to amdgpu_cs_chain/amdgpu_cs_chain_preserve functions "
-        "not allowed. Please use the @llvm.amdgpu.cs.chain intrinsic instead.",
-        Call);
+  // Verify if the calling convention of the callee is callable.
+  Check(isCallableCC(Call.getCallingConv()), "Callee is not callable", Call);
 
   // Disallow passing/returning values with alignment higher than we can
   // represent.
diff --git a/llvm/test/CodeGen/AMDGPU/attributor-flatscratchinit.ll b/llvm/test/CodeGen/AMDGPU/attributor-flatscratchinit.ll
index fc61aa5f981dd..a3d6c3b729523 100644
--- a/llvm/test/CodeGen/AMDGPU/attributor-flatscratchinit.ll
+++ b/llvm/test/CodeGen/AMDGPU/attributor-flatscratchinit.ll
@@ -849,21 +849,6 @@ define amdgpu_kernel void @calls_intrin_ascast_cc_kernel(ptr addrspace(3) %ptr)
   ret void
 }
 
-define amdgpu_kernel void @call_calls_intrin_ascast_cc_kernel(ptr addrspace(3) %ptr) {
-; GFX9-LABEL: define amdgpu_kernel void @call_calls_intrin_ascast_cc_kernel(
-; GFX9-SAME: ptr addrspace(3) [[PTR:%.*]]) #[[ATTR1]] {
-; GFX9-NEXT:    call void @calls_intrin_ascast_cc_kernel(ptr addrspace(3) [[PTR]])
-; GFX9-NEXT:    ret void
-;
-; GFX10-LABEL: define amdgpu_kernel void @call_calls_intrin_ascast_cc_kernel(
-; GFX10-SAME: ptr addrspace(3) [[PTR:%.*]]) #[[ATTR1]] {
-; GFX10-NEXT:    call void @calls_intrin_ascast_cc_kernel(ptr addrspace(3) [[PTR]])
-; GFX10-NEXT:    ret void
-;
-  call void @calls_intrin_ascast_cc_kernel(ptr addrspace(3) %ptr)
-  ret void
-}
-
 define amdgpu_kernel void @with_inline_asm() {
 ; GFX9-LABEL: define amdgpu_kernel void @with_inline_asm(
 ; GFX9-SAME: ) #[[ATTR3]] {
diff --git a/llvm/test/CodeGen/AMDGPU/call-to-kernel-undefined.ll b/llvm/test/CodeGen/AMDGPU/call-to-kernel-undefined.ll
deleted file mode 100644
index da7385475088b..0000000000000
--- a/llvm/test/CodeGen/AMDGPU/call-to-kernel-undefined.ll
+++ /dev/null
@@ -1,20 +0,0 @@
-; RUN: not --crash llc -mtriple=amdgcn -mcpu=tahiti -verify-machineinstrs -o /dev/null %s 2>&1 | FileCheck %s
-
-; FIXME: It should be invalid IR to have a call to a kernel, but this
-; is currently relied on, but should be eliminated before codegen.
-define amdgpu_kernel void @callee_kernel(ptr addrspace(1) %out) #0 {
-entry:
-  store volatile i32 0, ptr addrspace(1) %out
-  ret void
-}
-
-; Make sure there's no crash when the callsite calling convention
-; doesn't match.
-; CHECK: LLVM ERROR: invalid call to entry function
-define amdgpu_kernel void @caller_kernel(ptr addrspace(1) %out) #0 {
-entry:
-  call void @callee_kernel(ptr addrspace(1) %out)
-  ret void
-}
-
-attributes #0 = { nounwind noinline }
diff --git a/llvm/test/CodeGen/AMDGPU/call-to-kernel.ll b/llvm/test/CodeGen/AMDGPU/call-to-kernel.ll
deleted file mode 100644
index 1f4f6471fcdba..0000000000000
--- a/llvm/test/CodeGen/AMDGPU/call-to-kernel.ll
+++ /dev/null
@@ -1,18 +0,0 @@
-; RUN: not --crash llc -mtriple=amdgcn -mcpu=tahiti -verify-machineinstrs -o /dev/null %s 2>&1 | FileCheck %s
-
-; FIXME: It should be invalid IR to have a call to a kernel, but this
-; is currently relied on, but should be eliminated before codegen.
-define amdgpu_kernel void @callee_kernel(ptr addrspace(1) %out) #0 {
-entry:
-  store volatile i32 0, ptr addrspace(1) %out
-  ret void
-}
-
-; CHECK: LLVM ERROR: Unsupported calling convention for call
-define amdgpu_kernel void @caller_kernel(ptr addrspace(1) %out) #0 {
-entry:
-  call amdgpu_kernel void @callee_kernel(ptr addrspace(1) %out)
-  ret void
-}
-
-attributes #0 = { nounwind noinline }
diff --git a/llvm/test/Verifier/amdgpu-cc.ll b/llvm/test/Verifier/amdgpu-cc.ll
index ce48d1fdae3e5..aec09771d2e4f 100644
--- a/llvm/test/Verifier/amdgpu-cc.ll
+++ b/llvm/test/Verifier/amdgpu-cc.ll
@@ -217,26 +217,3 @@ define amdgpu_cs_chain_preserve void @preallocated_cc_amdgpu_cs_chain_preserve(p
 define amdgpu_cs_chain_preserve void @inalloca_cc_amdgpu_cs_chain_preserve(ptr inalloca(i32) %ptr) {
   ret void
 }
-
-declare amdgpu_cs_chain void @amdgpu_cs_chain_call_target()
-declare amdgpu_cs_chain_preserve void @amdgpu_cs_chain_preserve_call_target()
-
-define amdgpu_cs_chain void @cant_call_amdgpu_cs_chain_functions(ptr %f) {
-  ; CHECK: Direct calls to amdgpu_cs_chain/amdgpu_cs_chain_preserve functions not allowed. Please use the @llvm.amdgpu.cs.chain intrinsic instead.
-  ; CHECK-NEXT: call amdgpu_cs_chain
-  call amdgpu_cs_chain void @amdgpu_cs_chain_call_target()
-
-  ; CHECK: Direct calls to amdgpu_cs_chain/amdgpu_cs_chain_preserve functions not allowed. Please use the @llvm.amdgpu.cs.chain intrinsic instead.
-  ; CHECK-NEXT: call amdgpu_cs_chain_preserve
-  call amdgpu_cs_chain_preserve void @amdgpu_cs_chain_preserve_call_target()
-
-  ; CHECK: Direct calls to amdgpu_cs_chain/amdgpu_cs_chain_preserve functions not allowed. Please use the @llvm.amdgpu.cs.chain intrinsic instead.
-  ; CHECK-NEXT: call amdgpu_cs_chain
-  call amdgpu_cs_chain void %f()
-
-  ; CHECK: Direct calls to amdgpu_cs_chain/amdgpu_cs_chain_preserve functions not allowed. Please use the @llvm.amdgpu.cs.chain intrinsic instead.
-  ; CHECK-NEXT: call amdgpu_cs_chain
-  call amdgpu_cs_chain_preserve void %f()
-
-  ret void
-}
diff --git a/llvm/test/Verifier/call-to-non-callable-functions.ll b/llvm/test/Verifier/call-to-non-callable-functions.ll
new file mode 100644
index 0000000000000..3bc15e431a2c2
--- /dev/null
+++ b/llvm/test/Verifier/call-to-non-callable-functions.ll
@@ -0,0 +1,181 @@
+; RUN: not llvm-as %s -o /dev/null 2>&1 | FileCheck %s
+
+declare amdgpu_cs void @callee_cs()
+
+declare amdgpu_cs_chain void @callee_cs_chain()
+
+declare amdgpu_cs_chain_preserve void @callee_cs_chain_preserve()
+
+declare amdgpu_es void @callee_es()
+
+declare amdgpu_gs void @callee_gs()
+
+declare amdgpu_hs void @callee_hs()
+
+declare amdgpu_kernel void @callee_kernel()
+
+declare amdgpu_ls void @callee_ls()
+
+declare amdgpu_ps void @callee_ps()
+
+declare amdgpu_vs void @callee_vs()
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_cs_chain void @callee_cs_chain()
+define amdgpu_cs_chain void @caller_cs_chain() {
+entry:
+  call amdgpu_cs_chain void @callee_cs_chain()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_cs_chain_preserve void @callee_cs_chain_preserve()
+define amdgpu_cs_chain_preserve void @caller_cs_chain_preserve() {
+entry:
+  call amdgpu_cs_chain_preserve void @callee_cs_chain_preserve()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_kernel void @callee_kernel()
+define amdgpu_kernel void @caller_kernel() {
+entry:
+  call amdgpu_kernel void @callee_kernel()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_vs void @callee_vs()
+define amdgpu_vs void @caller_vs() {
+entry:
+  call amdgpu_vs void @callee_vs()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_gs void @callee_gs()
+define amdgpu_gs void @caller_gs() {
+entry:
+  call amdgpu_gs void @callee_gs()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_ps void @callee_ps()
+define amdgpu_ps void @caller_ps() {
+entry:
+  call amdgpu_ps void @callee_ps()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_cs void @callee_cs()
+define amdgpu_cs void @caller_cs() {
+entry:
+  call amdgpu_cs void @callee_cs()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_es void @callee_es()
+define amdgpu_es void @caller_es() {
+entry:
+  call amdgpu_es void @callee_es()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_hs void @callee_hs()
+define amdgpu_hs void @caller_hs() {
+entry:
+  call amdgpu_hs void @callee_hs()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_ls void @callee_ls()
+define amdgpu_ls void @caller_ls() {
+entry:
+  call amdgpu_ls void @callee_ls()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_cs_chain void %func()
+define amdgpu_cs_chain void @indirect_caller_cs_chain(ptr %func) {
+entry:
+  call amdgpu_cs_chain void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_cs_chain_preserve void %func()
+define amdgpu_cs_chain_preserve void @indirect_caller_cs_chain_preserve(ptr %func) {
+entry:
+  call amdgpu_cs_chain_preserve void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_kernel void %func()
+define void @indirect_caller_kernel(ptr %func) {
+entry:
+  call amdgpu_kernel void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_vs void %func()
+define void @indirect_caller_vs(ptr %func) {
+entry:
+  call amdgpu_vs void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_gs void %func()
+define void @indirect_caller_gs(ptr %func) {
+entry:
+  call amdgpu_gs void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_ps void %func()
+define void @indirect_caller_ps(ptr %func) {
+entry:
+  call amdgpu_ps void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_cs void %func()
+define void @indirect_caller_cs(ptr %func) {
+entry:
+  call amdgpu_cs void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_es void %func()
+define void @indirect_caller_es(ptr %func) {
+entry:
+  call amdgpu_es void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_hs void %func()
+define void @indirect_caller_hs(ptr %func) {
+entry:
+  call amdgpu_hs void %func()
+  ret void
+}
+
+; CHECK: Callee is not callable
+; CHECK-NEXT: call amdgpu_ls void %func()
+define void @indirect_caller_ls(ptr %func) {
+entry:
+  call amdgpu_ls void %func()
+  ret void
+}



More information about the llvm-commits mailing list