[llvm] [SPIR-V] Prevent generating OpTypePointer with OpTypeFunction pointee without extension (PR #207347)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 9 08:21:56 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/207347
>From 114c2b346d825b1835a011e9d44dd1b9fbe7902b Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 3 Jul 2026 10:31:37 +0200
Subject: [PATCH 1/2] [SPIR-V] Prevent generating OpTypePointer with
OpTypeFunction pointee without extension
---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 6 ++-
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 4 +-
.../pointers/fun-ptr-as-data-load-store.ll | 50 +++++++++++++++++++
3 files changed, 58 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 6dd965c266fcb..b507a5cd16921 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -1998,6 +1998,10 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
const Type *BaseType, MachineIRBuilder &MIRBuilder,
SPIRV::StorageClass::StorageClass SC) {
+ if (BaseType->isFunctionTy() &&
+ !cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget())
+ .canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers))
+ BaseType = Type::getInt8Ty(MIRBuilder.getContext());
// TODO: Need to check if EmitIr should always be true.
SPIRVTypeInst SpirvBaseType = getOrCreateSPIRVType(
BaseType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
@@ -2026,7 +2030,7 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
assert(!storageClassRequiresExplictLayout(SC));
SPIRVTypeInst R = getOrCreateSPIRVPointerType(LLVMType, MIRBuilder, SC);
assert(
- getPointeeType(R) == BaseType &&
+ (getPointeeType(R) == BaseType || LLVMType->isFunctionTy()) &&
"The base type was not correctly laid out for the given storage class.");
return R;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index a8772abfe4c27..3db324edfa498 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -258,7 +258,9 @@ static void insertBitcasts(MachineFunction &MF, SPIRVGlobalRegistry *GR,
Register Source = MI.getOperand(2).getReg();
Type *ElemTy = getMDOperandAsType(MI.getOperand(3).getMetadata(), 0);
auto SC =
- isa<FunctionType>(ElemTy)
+ isa<FunctionType>(ElemTy) &&
+ ST->canUseExtension(
+ SPIRV::Extension::SPV_INTEL_function_pointers)
? SPIRV::StorageClass::CodeSectionINTEL
: addressSpaceToStorageClass(MI.getOperand(4).getImm(), *ST);
SPIRVTypeInst AssignedPtrType =
diff --git a/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll
new file mode 100644
index 0000000000000..312b8656fb593
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll
@@ -0,0 +1,50 @@
+; A function used as a data pointer (load/store through @fn) must produce
+; OpTypePointer with an i8 pointee when SPV_INTEL_function_pointers is absent,
+; or a proper OpTypePointer/CodeSectionINTEL to the function type when present.
+
+; Without the extension: pointer pointee must be i8, never OpTypeFunction.
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: llc -verify-machineinstrs -O2 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; RUN: %if spirv-tools %{ llc -O2 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; With the extension: pointer-to-function types and FunctionPointerINTEL constants are emitted.
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o - | FileCheck %s --check-prefix=CHECK-EXT
+; RUN: llc -verify-machineinstrs -O2 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o - | FileCheck %s --check-prefix=CHECK-EXT
+
+; CHECK-DAG: %[[#I8:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#]] = OpTypePointer CrossWorkgroup %[[#I8]]
+; CHECK-DAG: %[[#FUNCTY:]] = OpTypeFunction
+; CHECK-NOT: OpTypePointer {{[A-Za-z]+}} %[[#FUNCTY]]{{$}}
+
+; CHECK-EXT-DAG: OpCapability FunctionPointersINTEL
+; CHECK-EXT-DAG: OpExtension "SPV_INTEL_function_pointers"
+; CHECK-EXT-DAG: %[[#I8:]] = OpTypeInt 8 0
+; CHECK-EXT-DAG: %[[#FUNCTY:]] = OpTypeFunction
+; CHECK-EXT-DAG: %[[#PTR_CW:]] = OpTypePointer CrossWorkgroup %[[#FUNCTY]]
+; CHECK-EXT-DAG: %[[#PTR_CS:]] = OpTypePointer CodeSectionINTEL %[[#FUNCTY]]
+; CHECK-EXT-DAG: OpConstantFunctionPointerINTEL %[[#PTR_CS]]
+
+define spir_kernel void @fuzz_kernel_load(ptr addrspace(1) %0, ptr addrspace(1) %1, i32 %2) {
+ %4 = icmp sgt i32 %2, 0
+ br i1 %4, label %5, label %9
+5:
+ %6 = load i32, ptr @fuzz_kernel_load, align 4
+ %7 = mul i32 %2, -1640531527
+ %8 = add i32 %6, %6
+ ret void
+9:
+ unreachable
+}
+
+define spir_kernel void @fuzz_kernel_store(ptr addrspace(1) nofree readnone captures(none) %in,
+ ptr addrspace(1) nofree readnone captures(none) %out,
+ i32 %n) local_unnamed_addr {
+ %ok = icmp sgt i32 %n, 0
+ br i1 %ok, label %1, label %2
+1:
+ store i32 -1640531527, ptr @fuzz_kernel_store, align 4
+ br label %2
+2:
+ ret void
+}
>From 0cfe91a531ff393983b4230f4e37a8b06024c602 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 9 Jul 2026 17:14:33 +0200
Subject: [PATCH 2/2] Address comment
---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 82 ++++++++++---------
.../pointers/fun-ptr-as-data-load-store.ll | 20 ++---
2 files changed, 51 insertions(+), 51 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index b507a5cd16921..fed529aed308e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -201,25 +201,25 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeInt(unsigned Width,
SPIRVTypeInst
SPIRVGlobalRegistry::getOpTypeFloat(uint32_t Width,
MachineIRBuilder &MIRBuilder) {
- return createConstOrTypeAtFunctionEntry(MIRBuilder, [&](MachineIRBuilder
- &MIRBuilder) {
- return MIRBuilder.buildInstr(SPIRV::OpTypeFloat)
- .addDef(createTypeVReg(MIRBuilder))
- .addImm(Width);
- });
+ return createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypeFloat)
+ .addDef(createTypeVReg(MIRBuilder))
+ .addImm(Width);
+ });
}
SPIRVTypeInst
SPIRVGlobalRegistry::getOpTypeFloat(uint32_t Width,
MachineIRBuilder &MIRBuilder,
SPIRV::FPEncoding::FPEncoding FPEncode) {
- return createConstOrTypeAtFunctionEntry(MIRBuilder, [&](MachineIRBuilder
- &MIRBuilder) {
- return MIRBuilder.buildInstr(SPIRV::OpTypeFloat)
- .addDef(createTypeVReg(MIRBuilder))
- .addImm(Width)
- .addImm(FPEncode);
- });
+ return createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypeFloat)
+ .addDef(createTypeVReg(MIRBuilder))
+ .addImm(Width)
+ .addImm(FPEncode);
+ });
}
SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeVoid(MachineIRBuilder &MIRBuilder) {
@@ -1084,23 +1084,23 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOpTypePointer(
if (!Reg.isValid())
Reg = createTypeVReg(MIRBuilder);
- return createConstOrTypeAtFunctionEntry(MIRBuilder, [&](MachineIRBuilder
- &MIRBuilder) {
- return MIRBuilder.buildInstr(SPIRV::OpTypePointer)
- .addDef(Reg)
- .addImm(static_cast<uint32_t>(SC))
- .addUse(getSPIRVTypeID(ElemType));
- });
+ return createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypePointer)
+ .addDef(Reg)
+ .addImm(static_cast<uint32_t>(SC))
+ .addUse(getSPIRVTypeID(ElemType));
+ });
}
SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeForwardPointer(
SPIRV::StorageClass::StorageClass SC, MachineIRBuilder &MIRBuilder) {
- return createConstOrTypeAtFunctionEntry(MIRBuilder, [&](MachineIRBuilder
- &MIRBuilder) {
- return MIRBuilder.buildInstr(SPIRV::OpTypeForwardPointer)
- .addUse(createTypeVReg(MIRBuilder))
- .addImm(static_cast<uint32_t>(SC));
- });
+ return createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ return MIRBuilder.buildInstr(SPIRV::OpTypeForwardPointer)
+ .addUse(createTypeVReg(MIRBuilder))
+ .addImm(static_cast<uint32_t>(SC));
+ });
}
SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeFunction(
@@ -1115,15 +1115,15 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOpTypeFunction(
Fn, "SPIR-V shaders do not support variadic functions",
MIRBuilder.getDebugLoc()));
}
- return createConstOrTypeAtFunctionEntry(MIRBuilder, [&](MachineIRBuilder
- &MIRBuilder) {
- auto MIB = MIRBuilder.buildInstr(SPIRV::OpTypeFunction)
- .addDef(createTypeVReg(MIRBuilder))
- .addUse(getSPIRVTypeID(RetType));
- for (auto &ArgType : ArgTypes)
- MIB.addUse(getSPIRVTypeID(ArgType));
- return MIB;
- });
+ return createConstOrTypeAtFunctionEntry(
+ MIRBuilder, [&](MachineIRBuilder &MIRBuilder) {
+ auto MIB = MIRBuilder.buildInstr(SPIRV::OpTypeFunction)
+ .addDef(createTypeVReg(MIRBuilder))
+ .addUse(getSPIRVTypeID(RetType));
+ for (auto &ArgType : ArgTypes)
+ MIB.addUse(getSPIRVTypeID(ArgType));
+ return MIB;
+ });
}
SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateOpTypeFunctionWithArgs(
@@ -2000,8 +2000,14 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
SPIRV::StorageClass::StorageClass SC) {
if (BaseType->isFunctionTy() &&
!cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget())
- .canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers))
- BaseType = Type::getInt8Ty(MIRBuilder.getContext());
+ .canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)) {
+ const Function &F = MIRBuilder.getMF().getFunction();
+ F.getContext().diagnose(
+ DiagnosticInfoUnsupported(F,
+ "Function used as a data pointer requires "
+ "SPV_INTEL_function_pointers extension",
+ DebugLoc(), DS_Error));
+ }
// TODO: Need to check if EmitIr should always be true.
SPIRVTypeInst SpirvBaseType = getOrCreateSPIRVType(
BaseType, MIRBuilder, SPIRV::AccessQualifier::ReadWrite,
@@ -2030,7 +2036,7 @@ SPIRVTypeInst SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
assert(!storageClassRequiresExplictLayout(SC));
SPIRVTypeInst R = getOrCreateSPIRVPointerType(LLVMType, MIRBuilder, SC);
assert(
- (getPointeeType(R) == BaseType || LLVMType->isFunctionTy()) &&
+ getPointeeType(R) == BaseType &&
"The base type was not correctly laid out for the given storage class.");
return R;
}
diff --git a/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll
index 312b8656fb593..a111e4efa8ba8 100644
--- a/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll
+++ b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-as-data-load-store.ll
@@ -1,25 +1,19 @@
-; A function used as a data pointer (load/store through @fn) must produce
-; OpTypePointer with an i8 pointee when SPV_INTEL_function_pointers is absent,
-; or a proper OpTypePointer/CodeSectionINTEL to the function type when present.
+; A function used as a data pointer (load/store through @fn) requires
+; SPV_INTEL_function_pointers, since it needs OpTypePointer with an
+; OpTypeFunction pointee.
-; Without the extension: pointer pointee must be i8, never OpTypeFunction.
-; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
-; RUN: llc -verify-machineinstrs -O2 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-; RUN: %if spirv-tools %{ llc -O2 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; Without the extension: using a function as a data pointer is an error.
+; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
+; RUN: not llc -O2 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
; With the extension: pointer-to-function types and FunctionPointerINTEL constants are emitted.
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o - | FileCheck %s --check-prefix=CHECK-EXT
; RUN: llc -verify-machineinstrs -O2 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o - | FileCheck %s --check-prefix=CHECK-EXT
-; CHECK-DAG: %[[#I8:]] = OpTypeInt 8 0
-; CHECK-DAG: %[[#]] = OpTypePointer CrossWorkgroup %[[#I8]]
-; CHECK-DAG: %[[#FUNCTY:]] = OpTypeFunction
-; CHECK-NOT: OpTypePointer {{[A-Za-z]+}} %[[#FUNCTY]]{{$}}
+; CHECK-ERROR: error:{{.*}}Function used as a data pointer requires SPV_INTEL_function_pointers extension
; CHECK-EXT-DAG: OpCapability FunctionPointersINTEL
; CHECK-EXT-DAG: OpExtension "SPV_INTEL_function_pointers"
-; CHECK-EXT-DAG: %[[#I8:]] = OpTypeInt 8 0
; CHECK-EXT-DAG: %[[#FUNCTY:]] = OpTypeFunction
; CHECK-EXT-DAG: %[[#PTR_CW:]] = OpTypePointer CrossWorkgroup %[[#FUNCTY]]
; CHECK-EXT-DAG: %[[#PTR_CS:]] = OpTypePointer CodeSectionINTEL %[[#FUNCTY]]
More information about the llvm-commits
mailing list