[llvm] [AMDGPU] Skip printf runtime binding if function signature is unexpected (PR #177573)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 23 04:21:05 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
Author: Steffen Larsen (steffenlarsen)
<details>
<summary>Changes</summary>
When creating the binding for OpenCL printf calls, we expect the signature of the function to match the OpenCL specification. If the signature is unexpected, this patch makes the pass skip creating the binding to avoid incorrect behavior and crashes.
---
Full diff: https://github.com/llvm/llvm-project/pull/177573.diff
5 Files Affected:
- (modified) llvm/lib/Target/AMDGPU/AMDGPUPrintfRuntimeBinding.cpp (+7)
- (added) llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-first-arg.ll (+38)
- (added) llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-return.ll (+52)
- (added) llvm/test/CodeGen/AMDGPU/opencl-printf-non-variadic.ll (+46)
- (added) llvm/test/CodeGen/AMDGPU/opencl-printf-too-many-args.ll (+52)
``````````diff
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPrintfRuntimeBinding.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPrintfRuntimeBinding.cpp
index e920fd91a8835..5ccba80810174 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUPrintfRuntimeBinding.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUPrintfRuntimeBinding.cpp
@@ -438,6 +438,13 @@ bool AMDGPUPrintfRuntimeBindingImpl::run(Module &M) {
M.getModuleFlag("openmp"))
return false;
+ // Verify the signature of the printf function and skip if it isn't correct.
+ const FunctionType *PrintfFunctionTy = PrintfFunction->getFunctionType();
+ if (PrintfFunctionTy->getNumParams() != 1 || !PrintfFunctionTy->isVarArg() ||
+ !PrintfFunctionTy->getReturnType()->isIntegerTy(32) ||
+ !PrintfFunctionTy->getParamType(0)->isPointerTy())
+ return false;
+
for (auto &U : PrintfFunction->uses()) {
if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
if (CI->isCallee(&U) && !CI->isNoBuiltin())
diff --git a/llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-first-arg.ll b/llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-first-arg.ll
new file mode 100644
index 0000000000000..fc00187c4a897
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-first-arg.ll
@@ -0,0 +1,38 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -mtriple=r600-- -passes=amdgpu-printf-runtime-binding -mcpu=r600 -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn-- -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn--amdhsa -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+
+define amdgpu_kernel void @test_kernel(i32 %n) {
+; CHECK-LABEL: define amdgpu_kernel void @test_kernel(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[MEM:%.*]] = alloca float, align 4, addrspace(5)
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (i32, ...) @printf(i32 [[N]], ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: store float [[CALL1]], ptr addrspace(5) [[MEM]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %mem = alloca float, align 4, addrspace(5)
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (i32, ...) @printf(i32 %n, ptr addrspace(5) %str, i32 %n)
+ store float %call1, ptr addrspace(5) %mem, align 4
+ ret void
+}
+
+define float @test_func(i32 %n) {
+; CHECK-LABEL: define float @test_func(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (i32, ...) @printf(i32 [[N]], ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: ret float [[CALL1]]
+;
+entry:
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (i32, ...) @printf(i32 %n, ptr addrspace(5) %str, i32 %n)
+ ret float %call1
+}
+
+declare float @printf(i32, ...)
diff --git a/llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-return.ll b/llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-return.ll
new file mode 100644
index 0000000000000..584aaf6ca99ad
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/opencl-printf-invalid-return.ll
@@ -0,0 +1,52 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -mtriple=r600-- -passes=amdgpu-printf-runtime-binding -mcpu=r600 -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn-- -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn--amdhsa -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+
+ at .str = private unnamed_addr addrspace(4) constant [6 x i8] c"%s:%d\00", align 1
+
+define amdgpu_kernel void @test_kernel(i32 %n) {
+; CHECK-LABEL: define amdgpu_kernel void @test_kernel(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[MEM:%.*]] = alloca float, align 4, addrspace(5)
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (ptr addrspace(4), ...) @printf(ptr addrspace(4) @.str, ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: store float [[CALL1]], ptr addrspace(5) [[MEM]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %mem = alloca float, align 4, addrspace(5)
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (ptr addrspace(4), ...) @printf(ptr addrspace(4) @.str, ptr addrspace(5) %str, i32 %n)
+ store float %call1, ptr addrspace(5) %mem, align 4
+ ret void
+}
+
+define float @test_func(i32 %n) {
+; CHECK-LABEL: define float @test_func(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (ptr addrspace(4), ...) @printf(ptr addrspace(4) @.str, ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: ret float [[CALL1]]
+;
+entry:
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (ptr addrspace(4), ...) @printf(ptr addrspace(4) @.str, ptr addrspace(5) %str, i32 %n)
+ ret float %call1
+}
+
+define float @test_null_argument(i32 %n) {
+; CHECK-LABEL: define float @test_null_argument(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (ptr addrspace(4), ...) @printf(ptr addrspace(4) null, ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: ret float [[CALL1]]
+;
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (ptr addrspace(4), ...) @printf(ptr addrspace(4) null, ptr addrspace(5) %str, i32 %n)
+ ret float %call1
+}
+
+declare float @printf(ptr addrspace(4), ...)
diff --git a/llvm/test/CodeGen/AMDGPU/opencl-printf-non-variadic.ll b/llvm/test/CodeGen/AMDGPU/opencl-printf-non-variadic.ll
new file mode 100644
index 0000000000000..7380c097ecb8f
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/opencl-printf-non-variadic.ll
@@ -0,0 +1,46 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -mtriple=r600-- -passes=amdgpu-printf-runtime-binding -mcpu=r600 -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn-- -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn--amdhsa -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+
+ at .str = private unnamed_addr addrspace(4) constant [6 x i8] c"%s:%d\00", align 1
+
+define amdgpu_kernel void @test_kernel(i32 %n) {
+; CHECK-LABEL: define amdgpu_kernel void @test_kernel(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[MEM:%.*]] = alloca i32, align 4, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call i32 @printf(ptr addrspace(4) @.str, i32 [[N]])
+; CHECK-NEXT: store i32 [[CALL1]], ptr addrspace(5) [[MEM]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %mem = alloca i32, align 4, addrspace(5)
+ %call1 = call i32 (ptr addrspace(4), i32) @printf(ptr addrspace(4) @.str, i32 %n)
+ store i32 %call1, ptr addrspace(5) %mem, align 4
+ ret void
+}
+
+define i32 @test_func(i32 %n) {
+; CHECK-LABEL: define i32 @test_func(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALL1:%.*]] = call i32 @printf(ptr addrspace(4) @.str, i32 [[N]])
+; CHECK-NEXT: ret i32 [[CALL1]]
+;
+entry:
+ %call1 = call i32 (ptr addrspace(4), i32) @printf(ptr addrspace(4) @.str, i32 %n)
+ ret i32 %call1
+}
+
+define i32 @test_null_argument(i32 %n) {
+; CHECK-LABEL: define i32 @test_null_argument(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[CALL1:%.*]] = call i32 @printf(ptr addrspace(4) null, i32 [[N]])
+; CHECK-NEXT: ret i32 [[CALL1]]
+;
+ %call1 = call i32 (ptr addrspace(4), i32) @printf(ptr addrspace(4) null, i32 %n)
+ ret i32 %call1
+}
+
+declare i32 @printf(ptr addrspace(4), i32)
diff --git a/llvm/test/CodeGen/AMDGPU/opencl-printf-too-many-args.ll b/llvm/test/CodeGen/AMDGPU/opencl-printf-too-many-args.ll
new file mode 100644
index 0000000000000..ffed256a47491
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/opencl-printf-too-many-args.ll
@@ -0,0 +1,52 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -mtriple=r600-- -passes=amdgpu-printf-runtime-binding -mcpu=r600 -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn-- -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+; RUN: opt -mtriple=amdgcn--amdhsa -passes=amdgpu-printf-runtime-binding -mcpu=fiji -S < %s | FileCheck %s
+
+ at .str = private unnamed_addr addrspace(4) constant [6 x i8] c"%s:%d\00", align 1
+
+define amdgpu_kernel void @test_kernel(i32 %n) {
+; CHECK-LABEL: define amdgpu_kernel void @test_kernel(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[MEM:%.*]] = alloca float, align 4, addrspace(5)
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (ptr addrspace(4), i32, ...) @printf(ptr addrspace(4) @.str, i32 [[N]], ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: store float [[CALL1]], ptr addrspace(5) [[MEM]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %mem = alloca float, align 4, addrspace(5)
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (ptr addrspace(4), i32, ...) @printf(ptr addrspace(4) @.str, i32 %n, ptr addrspace(5) %str, i32 %n)
+ store float %call1, ptr addrspace(5) %mem, align 4
+ ret void
+}
+
+define float @test_func(i32 %n) {
+; CHECK-LABEL: define float @test_func(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (ptr addrspace(4), i32, ...) @printf(ptr addrspace(4) @.str, i32 [[N]], ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: ret float [[CALL1]]
+;
+entry:
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (ptr addrspace(4), i32, ...) @printf(ptr addrspace(4) @.str, i32 %n, ptr addrspace(5) %str, i32 %n)
+ ret float %call1
+}
+
+define float @test_null_argument(i32 %n) {
+; CHECK-LABEL: define float @test_null_argument(
+; CHECK-SAME: i32 [[N:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT: [[STR:%.*]] = alloca [9 x i8], align 1, addrspace(5)
+; CHECK-NEXT: [[CALL1:%.*]] = call float (ptr addrspace(4), i32, ...) @printf(ptr addrspace(4) null, i32 [[N]], ptr addrspace(5) [[STR]], i32 [[N]])
+; CHECK-NEXT: ret float [[CALL1]]
+;
+ %str = alloca [9 x i8], align 1, addrspace(5)
+ %call1 = call float (ptr addrspace(4), i32, ...) @printf(ptr addrspace(4) null, i32 %n, ptr addrspace(5) %str, i32 %n)
+ ret float %call1
+}
+
+declare float @printf(ptr addrspace(4), i32, ...)
``````````
</details>
https://github.com/llvm/llvm-project/pull/177573
More information about the llvm-commits
mailing list