[llvm] [SPIR-V] Fix illegal pointer types for functions used as plain values (PR #208250)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 8 09:10:13 PDT 2026
https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/208250
Without SPV_INTEL_function_pointers, a Function referenced as a value (not a call target) could leak its own FunctionType or address-space attribute into a pointer type, producing invalid SPIR-V
Guard call-site element-type deduction against non-call users of the function, such as a ptrcast wrapping an address-of-function load
>From 232c2f3593b2ec53a100c93d9ef775ad0f1a23bb Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 8 Jul 2026 18:08:31 +0200
Subject: [PATCH] [SPIR-V] Fix illegal pointer types for functions used as
plain values
Without SPV_INTEL_function_pointers, a Function referenced as a value (not a call target) could leak its own FunctionType or address-space attribute into a pointer type, producing invalid SPIR-V
Guard call-site element-type deduction against non-call users of the function, such as a ptrcast wrapping an address-of-function load
---
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 4 +-
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 10 +++--
.../SPIRV/transcoding/function-as-value.ll | 42 +++++++++++++++++++
3 files changed, 50 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/transcoding/function-as-value.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 19e1e71488ee3..ea421d5b579bb 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -3277,7 +3277,7 @@ Type *SPIRVEmitIntrinsics::deduceFunParamElementType(
// search in function's call sites
for (User *U : F->users()) {
CallInst *CI = dyn_cast<CallInst>(U);
- if (!CI || OpIdx >= CI->arg_size())
+ if (!CI || CI->getCalledFunction() != F || OpIdx >= CI->arg_size())
continue;
Value *OpArg = CI->getArgOperand(OpIdx);
if (!isPointerTy(OpArg->getType()))
@@ -3352,7 +3352,7 @@ void SPIRVEmitIntrinsics::processParamTypesByFunHeader(Function *F,
// search in function's call sites
for (User *U : F->users()) {
CallInst *CI = dyn_cast<CallInst>(U);
- if (!CI || OpIdx >= CI->arg_size())
+ if (!CI || CI->getCalledFunction() != F || OpIdx >= CI->arg_size())
continue;
Value *OpArg = CI->getArgOperand(OpIdx);
if (!isPointerTy(OpArg->getType()))
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 274a3265f7115..41d68aa38f1af 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -6916,10 +6916,12 @@ bool SPIRVInstructionSelector::selectGlobalValue(
STI.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)
? dyn_cast<Function>(GV)
: nullptr;
- SPIRVTypeInst ResType = GR.getOrCreateSPIRVPointerType(
- GVType, I,
- GVFun ? SPIRV::StorageClass::CodeSectionINTEL
- : addressSpaceToStorageClass(GV->getAddressSpace(), STI));
+ SPIRVTypeInst ResType =
+ GVFun ? GR.getOrCreateSPIRVPointerType(
+ GVType, I, SPIRV::StorageClass::CodeSectionINTEL)
+ : GR.getOrCreateSPIRVPointerType(
+ IntegerType::getInt8Ty(GV->getContext()), I,
+ SPIRV::StorageClass::Function);
if (GVFun) {
// References to a function via function pointers generate virtual
// registers without a definition. We will resolve it later, during
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/function-as-value.ll b/llvm/test/CodeGen/SPIRV/transcoding/function-as-value.ll
new file mode 100644
index 0000000000000..293e40d18c2ea
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/function-as-value.ll
@@ -0,0 +1,42 @@
+; Without SPV_INTEL_function_pointers, a Function referenced as a plain value
+; (not a call target) must not leak its own FunctionType or address-space
+; attribute into a pointer type. Two symptoms of the same root cause, kept as
+; separate modules so each regresses on its own (a single module lets one
+; case's incidental OpUndef mask the other's).
+
+; RUN: split-file %s %t
+
+; The reference lowers to a placeholder that must be OpTypePointer Function
+; %uchar, not a pointer built from the function's own type or its
+; address-space attribute.
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %t/undef-placeholder.ll -o - | FileCheck %s --check-prefix=UNDEF
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %t/undef-placeholder.ll -o - -filetype=obj | spirv-val %}
+
+; UNDEF-DAG: %[[#UCHAR:]] = OpTypeInt 8 0
+; UNDEF-DAG: %[[#PTR:]] = OpTypePointer Function %[[#UCHAR]]
+; UNDEF: OpUndef %[[#PTR]]
+
+; Element-type deduction for the function's own parameters must ignore
+; non-call users of F (such as the ptrcast wrapping an address-of-function
+; load), so the parameter keeps its real pointee type instead of the
+; function's FunctionType.
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %t/param-deduction.ll -o - | FileCheck %s --check-prefix=PARAM
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %t/param-deduction.ll -o - -filetype=obj | spirv-val %}
+
+; PARAM-DAG: %[[#CHAR:]] = OpTypeInt 8 0
+; PARAM-DAG: %[[#PTRCHAR:]] = OpTypePointer CrossWorkgroup %[[#CHAR]]
+; PARAM: OpFunctionParameter %[[#PTRCHAR]]
+
+;--- undef-placeholder.ll
+define spir_kernel void @foo() addrspace(5) {
+entry:
+ store i32 0, ptr addrspace(5) @foo, align 4
+ ret void
+}
+
+;--- param-deduction.ll
+define spir_kernel void @foo(ptr addrspace(1) %in) {
+entry:
+ %v = load i32, ptr @foo, align 4
+ ret void
+}
More information about the llvm-commits
mailing list