[llvm] [ExpandVariadics] Don't lower unmangled C printf on SPIR-V (PR #206998)

Paulius Velesko via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 1 07:32:35 PDT 2026


https://github.com/pvelesko created https://github.com/llvm/llvm-project/pull/206998

An unmangled C `printf` declaration demangles to bare `printf` (no argument list), so `SPIRV::ignoreFunction`'s `printf(` prefix check missed it and ExpandVariadics packed its arguments into a vararg buffer; the SPIR-V backend then passed the buffer pointer as printf's first variadic operand, so device printf printed pointer values instead of the actual arguments. Match the bare name too so OpenCL/HIP printf (emitted unmangled) is left as a variadic call for the backend's OpenCL.std printf lowering.


>From 1bc34f7d80b826585fb237a2e41606a27a6189bb Mon Sep 17 00:00:00 2001
From: Paulius Velesko <pvelesko at pglc.io>
Date: Sun, 7 Jun 2026 15:43:25 +0300
Subject: [PATCH 1/2] Don't lower unmangled C printf on SPIR-V

The SPIR-V VariadicABIInfo::ignoreFunction already skips OpenCL printf so
the backend can emit OpExtInst printf with inline arguments, but it only
matched the demangled spelling "printf(" (the C++/OpenCL mangled form).
An unmangled C printf declaration demangles to bare "printf" with no
argument list, so it slipped through and ExpandVariadics packed its
arguments into a vararg buffer. The backend then passed the buffer
pointer as printf's first variadic operand, so device printf printed
pointer values instead of the actual arguments.

Match the bare function name as well so OpenCL/HIP printf (emitted
unmangled) is left intact for the OpenCL.std printf lowering.
---
 llvm/lib/Transforms/IPO/ExpandVariadics.cpp | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
index 2996657bc693a..3f6808665b35d 100644
--- a/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
+++ b/llvm/lib/Transforms/IPO/ExpandVariadics.cpp
@@ -1067,8 +1067,13 @@ struct SPIRV final : public VariadicABIInfo {
     StringRef DemangledName(Demangled);
 
     // Skip any SPIR-V builtins.
+    // Note: an unmangled C `printf` declaration demangles to "printf" with no
+    // argument list, so the "printf(" prefix check below misses it. Match the
+    // bare name as well so OpenCL/HIP printf (emitted unmangled) is left as a
+    // variadic call for the backend's OpenCL.std printf lowering to expand
+    // inline, rather than being packed into a vararg buffer here.
     if (DemangledName.starts_with("__spirv_") ||
-        DemangledName.starts_with("printf("))
+        DemangledName.starts_with("printf(") || F->getName() == "printf")
       return true;
 
     return false;

>From 32fdc2b19f58fdefccc21ef3cf449970031a258d Mon Sep 17 00:00:00 2001
From: Paulius Velesko <pvelesko at pglc.io>
Date: Tue, 30 Jun 2026 10:58:26 +0300
Subject: [PATCH 2/2] [ExpandVariadics] Add SPIR-V test for unmangled printf

Covers the fix that leaves an unmangled C `printf` declaration as a
variadic call on SPIR-V (so the backend lowers it to OpenCL.std printf
with inline operands) while still expanding an ordinary user variadic.
---
 .../ExpandVariadics/spirv-printf.ll           | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 llvm/test/Transforms/ExpandVariadics/spirv-printf.ll

diff --git a/llvm/test/Transforms/ExpandVariadics/spirv-printf.ll b/llvm/test/Transforms/ExpandVariadics/spirv-printf.ll
new file mode 100644
index 0000000000000..c9be77a405243
--- /dev/null
+++ b/llvm/test/Transforms/ExpandVariadics/spirv-printf.ll
@@ -0,0 +1,35 @@
+; RUN: opt -mtriple=spirv64-unknown-unknown -S --passes=expand-variadics --expand-variadics-override=lowering %s | FileCheck %s
+;
+; An unmangled C `printf` declaration demangles to bare "printf" (no argument
+; list). On SPIR-V it must be left as a variadic call so the backend can lower
+; it to the OpenCL.std printf ExtInst with inline operands, instead of having
+; its arguments packed into a vararg buffer here. A user-defined variadic
+; function with a body is still expanded as usual.
+
+ at .str = private unnamed_addr addrspace(2) constant [4 x i8] c"%d\0A\00"
+
+declare spir_func i32 @printf(ptr addrspace(2), ...)
+
+; printf is left untouched: the call stays variadic and nothing is packed
+; (CHECK-NEXT pins that ret immediately follows the call).
+; CHECK-LABEL: define spir_kernel void @uses_printf(
+; CHECK-NEXT:    {{%.*}} = call spir_func i32 (ptr addrspace(2), ...) @printf(ptr addrspace(2) @.str, i32 42)
+; CHECK-NEXT:    ret void
+define spir_kernel void @uses_printf() {
+  %r = call spir_func i32 (ptr addrspace(2), ...) @printf(ptr addrspace(2) @.str, i32 42)
+  ret void
+}
+
+; Control: an ordinary variadic function with a body is still expanded, i.e.
+; its caller packs the arguments into a vararg buffer.
+define spir_func i32 @my_variadic(...) {
+  ret i32 0
+}
+
+; CHECK-LABEL: define spir_kernel void @uses_my_variadic(
+; CHECK:    %vararg_buffer = alloca %uses_my_variadic.vararg
+; CHECK:    call spir_func i32 @my_variadic(ptr %vararg_buffer)
+define spir_kernel void @uses_my_variadic() {
+  %r = call spir_func i32 (...) @my_variadic(i32 7)
+  ret void
+}



More information about the llvm-commits mailing list