[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