[llvm] [SPIR-V] Fix storage class mismatch for globals initialized with a function pointer (PR #216744)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 08:38:14 PDT 2026


https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/216744

>From 5c17bcc6e8f4af164abe173bd0bb49fbe7b1bb47 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 17 Aug 2026 15:54:29 +0200
Subject: [PATCH 1/2] [SPIR-V] Fix storage class mismatch for globals
 initialized with a function pointer

Related to https://github.com/llvm/llvm-project/pull/216638
---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 17 +++++++++---
 .../SPIRV/pointers/fun-ptr-to-itself.ll       | 26 +++++++++++++++++++
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 0f097c742fac2..edc302a14589d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -987,10 +987,19 @@ Type *SPIRVEmitIntrinsicsImpl::deduceElementTypeHelper(
       Ty = SPIRV::getOriginalFunctionType(*Fn);
       GR->addDeducedElementType(I, Ty);
     } else {
-      Ty = deduceElementTypeByValueDeep(
-          Ref->getValueType(),
-          Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr, Visited,
-          UnknownElemTypeI8);
+      Value *Op = Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr;
+      if (isa_and_nonnull<Function>(Op) &&
+          TM.getSubtargetImpl()->canUseExtension(
+              SPIRV::Extension::SPV_INTEL_function_pointers)) {
+        if (Type *NestedTy =
+                deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8))
+          Ty = getTypedPointerWrapper(
+              NestedTy, storageClassToAddressSpace(
+                            SPIRV::StorageClass::CodeSectionINTEL));
+      } else {
+        Ty = deduceElementTypeByValueDeep(Ref->getValueType(), Op, Visited,
+                                          UnknownElemTypeI8);
+      }
     }
   } else if (auto *Ref = dyn_cast<AddrSpaceCastInst>(I)) {
     Type *RefTy = deduceElementTypeHelper(Ref->getPointerOperand(), Visited,
diff --git a/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll
index 49ac8a53ae28b..ba64500784c56 100644
--- a/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll
+++ b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll
@@ -12,6 +12,14 @@
 ; CHECK-DAG: %[[#CodePtrTy:]] = OpTypePointer CodeSectionINTEL %[[#FnTy]]
 ; CHECK-DAG: %[[#Null:]] = OpConstantNull %[[#Int8PtrTy]]
 ; CHECK-DAG: %[[#FnPtr:]] = OpConstantFunctionPointerINTEL %[[#CodePtrTy]] %[[#FnDef:]]
+; The OpVariable for @fp must use CodeSectionINTEL to match its OpConstantFunctionPointerINTEL initializer.
+; CHECK-DAG: %[[#Int32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#FunTy2:]] = OpTypeFunction %[[#Int32]] %[[#Int32]]
+; CHECK-DAG: %[[#CodePtrTy2:]] = OpTypePointer CodeSectionINTEL %[[#FunTy2]]
+; CHECK-DAG: %[[#GenPtrTy2:]] = OpTypePointer Generic %[[#FunTy2]]
+; CHECK-DAG: %[[#GenPtrPtrTy2:]] = OpTypePointer Function %[[#GenPtrTy2]]
+; CHECK-DAG: %[[#VarTy2:]] = OpTypePointer Function %[[#CodePtrTy2]]
+; CHECK-DAG: %[[#FnPtr2:]] = OpConstantFunctionPointerINTEL %[[#CodePtrTy2]] %[[#Callback:]]
 ; CHECK:     %[[#FnDef]] = OpFunction %[[#Void]] None %[[#FnTy]]
 ; CHECK:     %[[#Cast:]] = OpPtrCastToGeneric %[[#GenPtrTy]] %[[#FnPtr]]
 ; CHECK:     %[[#BC:]] = OpBitcast %[[#GenPtrPtrTy]] %[[#Null]]
@@ -24,3 +32,21 @@ entry:
   store ptr addrspace(4) addrspacecast (ptr @foo to ptr addrspace(4)), ptr null, align 8
   ret void
 }
+
+; CHECK:     %[[#Fp:]] = OpVariable %[[#VarTy2]] Function %[[#FnPtr2]]
+; CHECK:     %[[#BC2:]] = OpBitcast %[[#GenPtrPtrTy2]] %[[#Fp]]
+; CHECK:     %[[#Ptr:]] = OpLoad %[[#GenPtrTy2]] %[[#BC2]]
+; CHECK:     OpFunctionPointerCallINTEL %[[#Int32]] %[[#Ptr]]
+
+ at fp = global ptr addrspace(4) @callback
+
+define void @caller() {
+  %ptr = load ptr addrspace(4), ptr @fp
+  %r = call addrspace(4) i32 %ptr(i32 0)
+  ret void
+}
+
+; CHECK: %[[#Callback]] = OpFunction %[[#Int32]] None %[[#FunTy2]]
+define i32 @callback(i32 %x) addrspace(4) {
+  ret i32 %x
+}

>From b75b8eaf580996f1e97bfb9853c0947c25916163 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 28 Aug 2026 17:38:03 +0200
Subject: [PATCH 2/2] address comment

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 31 ++++++++++---------
 1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index edc302a14589d..05401db456dab 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -237,7 +237,8 @@ class SPIRVEmitIntrinsicsImpl
                                      bool UnknownElemTypeI8);
   Type *deduceElementTypeByValueDeep(Type *ValueTy, Value *Operand,
                                      SmallPtrSetImpl<Value *> &Visited,
-                                     bool UnknownElemTypeI8);
+                                     bool UnknownElemTypeI8,
+                                     std::optional<unsigned> AddrSpace = {});
   Type *deduceElementTypeByUsersDeep(Value *Op,
                                      SmallPtrSetImpl<Value *> &Visited,
                                      bool UnknownElemTypeI8);
@@ -707,13 +708,14 @@ Type *SPIRVEmitIntrinsicsImpl::deduceElementTypeByValueDeep(
 
 Type *SPIRVEmitIntrinsicsImpl::deduceElementTypeByValueDeep(
     Type *ValueTy, Value *Operand, SmallPtrSetImpl<Value *> &Visited,
-    bool UnknownElemTypeI8) {
+    bool UnknownElemTypeI8, std::optional<unsigned> AddrSpace) {
   Type *Ty = ValueTy;
   if (Operand) {
     if (auto *PtrTy = dyn_cast<PointerType>(Ty)) {
       if (Type *NestedTy =
               deduceElementTypeHelper(Operand, Visited, UnknownElemTypeI8))
-        Ty = getTypedPointerWrapper(NestedTy, PtrTy->getAddressSpace());
+        Ty = getTypedPointerWrapper(
+            NestedTy, AddrSpace.value_or(PtrTy->getAddressSpace()));
     } else {
       Ty = deduceNestedTypeHelper(dyn_cast<User>(Operand), Ty, Visited,
                                   UnknownElemTypeI8);
@@ -988,18 +990,19 @@ Type *SPIRVEmitIntrinsicsImpl::deduceElementTypeHelper(
       GR->addDeducedElementType(I, Ty);
     } else {
       Value *Op = Ref->getNumOperands() > 0 ? Ref->getOperand(0) : nullptr;
-      if (isa_and_nonnull<Function>(Op) &&
-          TM.getSubtargetImpl()->canUseExtension(
-              SPIRV::Extension::SPV_INTEL_function_pointers)) {
-        if (Type *NestedTy =
-                deduceElementTypeHelper(Op, Visited, UnknownElemTypeI8))
-          Ty = getTypedPointerWrapper(
-              NestedTy, storageClassToAddressSpace(
-                            SPIRV::StorageClass::CodeSectionINTEL));
-      } else {
-        Ty = deduceElementTypeByValueDeep(Ref->getValueType(), Op, Visited,
-                                          UnknownElemTypeI8);
+      // Code lives in the program address space, not in the address space of
+      // the global. Program address space 0 means the data layout omits it.
+      std::optional<unsigned> ProgramAS;
+      if (isa_and_nonnull<Function>(Op)) {
+        if (unsigned AS = CurrF->getDataLayout().getProgramAddressSpace())
+          ProgramAS = AS;
+        else if (TM.getSubtargetImpl()->canUseExtension(
+                     SPIRV::Extension::SPV_INTEL_function_pointers))
+          ProgramAS =
+              storageClassToAddressSpace(SPIRV::StorageClass::CodeSectionINTEL);
       }
+      Ty = deduceElementTypeByValueDeep(Ref->getValueType(), Op, Visited,
+                                        UnknownElemTypeI8, ProgramAS);
     }
   } else if (auto *Ref = dyn_cast<AddrSpaceCastInst>(I)) {
     Type *RefTy = deduceElementTypeHelper(Ref->getPointerOperand(), Visited,



More information about the llvm-commits mailing list