[llvm] [SPIRV] Update the global registry when expanding function pointer (PR #183873)

Steven Perron via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 4 10:49:35 PST 2026


https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/183873

>From 36a5590143da35a541d9c0f6a3feab172b80c409 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Fri, 27 Feb 2026 20:17:08 -0500
Subject: [PATCH 1/4] [SPIRV] Update the global registry when expanding
 function pointer

We do not update the global registry when expanding a G_GLOBAL_VALUE for
a function pointer. Then during pointer validation, we can get a
garbage value from the global registry.
---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp |  2 ++
 .../SPIRV/pointers/fun-ptr-to-itself.ll       | 31 +++++++++++++++++++
 2 files changed, 33 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 8c3dd6f9ca800..8296e0b5d82c9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -5341,6 +5341,8 @@ bool SPIRVInstructionSelector::selectGlobalValue(
         GR.add(ConstVal, MIB2);
         // mapping the function pointer to the used Function
         GR.recordFunctionPointer(&MIB2.getInstr()->getOperand(2), GVFun);
+        GR.assignSPIRVTypeToVReg(ResType, FuncVReg, *GR.CurMF);
+        GR.assignSPIRVTypeToVReg(ResType, NewReg, *GR.CurMF);
         MIB1.constrainAllUses(TII, TRI, RBI);
         MIB2.constrainAllUses(TII, TRI, RBI);
         return true;
diff --git a/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll
new file mode 100644
index 0000000000000..7969a2f6bd80a
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll
@@ -0,0 +1,31 @@
+; RUN: llc -mtriple=spirv32-unknown-unknown < %s --spirv-ext=+SPV_INTEL_function_pointers | FileCheck %s
+; RUN: %if spirv-tools %{ llc -mtriple=spirv32-unknown-unknown < %s -filetype=obj | not spirv-val 2>&1 | FileCheck --check-prefix=SPIRV-VAL %s %}
+
+; spirv-val poorly supports the SPV_INTEL_function_pointers extension.
+; In this case the function type used in a pointer type fails.
+; SPIRV-VAL: Invalid use of function type result id '[[#ID:]][%{{.*}}]'.
+; SPIRV-VAL: %_ptr_Generic_4 = OpTypePointer Generic %[[#ID]]
+
+; CHECK-DAG: OpCapability FunctionPointersINTEL
+; CHECK-DAG: OpExtension "SPV_INTEL_function_pointers"
+; CHECK-DAG: %[[#Void:]] = OpTypeVoid
+; CHECK-DAG: %[[#FnTy:]] = OpTypeFunction %[[#Void]]
+; CHECK-DAG: %[[#GenPtrTy:]] = OpTypePointer Generic %[[#FnTy]]
+; CHECK-DAG: %[[#GenPtrPtrTy:]] = OpTypePointer Function %[[#GenPtrTy]]
+; CHECK-DAG: %[[#Int8:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#Int8PtrTy:]] = OpTypePointer Function %[[#Int8]]
+; CHECK-DAG: %[[#CodePtrTy:]] = OpTypePointer CodeSectionINTEL %[[#FnTy]]
+; CHECK-DAG: %[[#Null:]] = OpConstantNull %[[#Int8PtrTy]]
+; CHECK-DAG: %[[#FnPtr:]] = OpConstantFunctionPointerINTEL %[[#CodePtrTy]] %[[#FnDef:]]
+; CHECK:     %[[#FnDef]] = OpFunction %[[#Void]] None %[[#FnTy]]
+; CHECK:     %[[#Cast:]] = OpPtrCastToGeneric %[[#GenPtrTy]] %[[#FnPtr]]
+; CHECK:     %[[#BC:]] = OpBitcast %[[#GenPtrPtrTy]] %[[#Null]]
+; CHECK:     OpStore %[[#BC]] %[[#Cast]] Aligned 8
+; CHECK:     OpReturn
+; CHECK:     OpFunctionEnd
+
+define void @foo() {
+entry:
+  store ptr addrspace(4) addrspacecast (ptr @foo to ptr addrspace(4)), ptr null, align 8
+  ret void
+}

>From c8e9b37bef340767ead2e3aa3e8e8ff3e0f1dff7 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Mon, 2 Mar 2026 09:23:21 -0500
Subject: [PATCH 2/4] Fix up tests, and small changes.

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp       | 7 +++----
 .../extensions/SPV_INTEL_function_pointers/fp_const.ll   | 6 ++++--
 llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll    | 9 +++------
 3 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 8296e0b5d82c9..ea1172530d877 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -5310,7 +5310,6 @@ bool SPIRVInstructionSelector::selectGlobalValue(
     MachineBasicBlock &BB = *I.getParent();
     Register NewReg = GR.find(ConstVal, GR.CurMF);
     if (!NewReg.isValid()) {
-      Register NewReg = ResVReg;
       const Function *GVFun =
           STI.canUseExtension(SPIRV::Extension::SPV_INTEL_function_pointers)
               ? dyn_cast<Function>(GV)
@@ -5328,6 +5327,7 @@ bool SPIRVInstructionSelector::selectGlobalValue(
         Register FuncVReg =
             MRI->createGenericVirtualRegister(GR.getRegType(ResType));
         MRI->setRegClass(FuncVReg, &SPIRV::pIDRegClass);
+        GR.assignSPIRVTypeToVReg(ResType, FuncVReg, *GR.CurMF);
         MachineInstrBuilder MIB1 =
             BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUndef))
                 .addDef(FuncVReg)
@@ -5335,14 +5335,13 @@ bool SPIRVInstructionSelector::selectGlobalValue(
         MachineInstrBuilder MIB2 =
             BuildMI(BB, I, I.getDebugLoc(),
                     TII.get(SPIRV::OpConstantFunctionPointerINTEL))
-                .addDef(NewReg)
+                .addDef(ResVReg)
                 .addUse(ResTypeReg)
                 .addUse(FuncVReg);
         GR.add(ConstVal, MIB2);
         // mapping the function pointer to the used Function
         GR.recordFunctionPointer(&MIB2.getInstr()->getOperand(2), GVFun);
-        GR.assignSPIRVTypeToVReg(ResType, FuncVReg, *GR.CurMF);
-        GR.assignSPIRVTypeToVReg(ResType, NewReg, *GR.CurMF);
+        GR.assignSPIRVTypeToVReg(ResType, ResVReg, *GR.CurMF);
         MIB1.constrainAllUses(TII, TRI, RBI);
         MIB2.constrainAllUses(TII, TRI, RBI);
         return true;
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp_const.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp_const.ll
index b96da631c0a85..fe3f907957243 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp_const.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_function_pointers/fp_const.ll
@@ -1,5 +1,5 @@
 ; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o - | FileCheck %s
-; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o - -filetype=obj | spirv-val %}
 
 ; CHECK-DAG: OpCapability FunctionPointersINTEL
 ; CHECK-DAG: OpCapability Int64
@@ -9,12 +9,14 @@
 ; CHECK-DAG: %[[TyInt64:.*]] = OpTypeInt 64 0
 ; CHECK-DAG: %[[TyFun:.*]] = OpTypeFunction %[[TyInt64]] %[[TyInt64]]
 ; CHECK-DAG: %[[TyPtrFunCodeSection:.*]] = OpTypePointer CodeSectionINTEL %[[TyFun]]
+; CHECK-DAG: %[[TyPtrPtrFunCodeSection:.*]] = OpTypePointer Function %[[TyPtrFunCodeSection]]
 ; CHECK-DAG: %[[ConstFunFp:.*]] = OpConstantFunctionPointerINTEL %[[TyPtrFunCodeSection]] %[[DefFunFp:.*]]
 ; CHECK-DAG: %[[TyPtrFun:.*]] = OpTypePointer Function %[[TyFun]]
 ; CHECK-DAG: %[[TyPtrPtrFun:.*]] = OpTypePointer Function %[[TyPtrFun]]
 ; CHECK: OpFunction
 ; CHECK: %[[Var:.*]] = OpVariable %[[TyPtrPtrFun]] Function
-; CHECK: OpStore %[[Var]] %[[ConstFunFp]]
+; CHECK: %[[Cast:.*]] = OpBitcast %[[TyPtrPtrFunCodeSection]] %[[Var]]
+; CHECK: OpStore %[[Cast]] %[[ConstFunFp]]
 ; CHECK: %[[FP:.*]] = OpLoad %[[TyPtrFun]] %[[Var]]
 ; CHECK: OpFunctionPointerCallINTEL %[[TyInt64]] %[[FP]] %[[#]]
 ; CHECK: OpFunctionEnd
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 7969a2f6bd80a..958ab99202439 100644
--- a/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll
+++ b/llvm/test/CodeGen/SPIRV/pointers/fun-ptr-to-itself.ll
@@ -1,10 +1,7 @@
-; RUN: llc -mtriple=spirv32-unknown-unknown < %s --spirv-ext=+SPV_INTEL_function_pointers | FileCheck %s
-; RUN: %if spirv-tools %{ llc -mtriple=spirv32-unknown-unknown < %s -filetype=obj | not spirv-val 2>&1 | FileCheck --check-prefix=SPIRV-VAL %s %}
+; RUN: llc -mtriple=spirv32-unknown-unknown -O0 %s -o - --spirv-ext=+SPV_INTEL_function_pointers | FileCheck %s
 
-; spirv-val poorly supports the SPV_INTEL_function_pointers extension.
-; In this case the function type used in a pointer type fails.
-; SPIRV-VAL: Invalid use of function type result id '[[#ID:]][%{{.*}}]'.
-; SPIRV-VAL: %_ptr_Generic_4 = OpTypePointer Generic %[[#ID]]
+; This still fails validation, because %foo is considered undefined. 
+; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - --spirv-ext=+SPV_INTEL_function_pointers -filetype=obj | spirv-val %}
 
 ; CHECK-DAG: OpCapability FunctionPointersINTEL
 ; CHECK-DAG: OpExtension "SPV_INTEL_function_pointers"

>From 5484c093fb274605ffa1cf7449fb44e432622897 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Tue, 3 Mar 2026 09:38:06 -0500
Subject: [PATCH 3/4] Replace remaining uses of the deleted variable.

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index ea1172530d877..ca5ecfa4c993b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -5348,14 +5348,14 @@ bool SPIRVInstructionSelector::selectGlobalValue(
       }
       MachineInstrBuilder MIB3 =
           BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull))
-              .addDef(NewReg)
+              .addDef(ResVReg)
               .addUse(GR.getSPIRVTypeID(ResType));
       GR.add(ConstVal, MIB3);
       MIB3.constrainAllUses(TII, TRI, RBI);
       return true;
     }
-    assert(NewReg != ResVReg);
-    return BuildCOPY(ResVReg, NewReg, I);
+    assert(ResVReg != ResVReg);
+    return BuildCOPY(ResVReg, ResVReg, I);
   }
   auto GlobalVar = cast<GlobalVariable>(GV);
   assert(GlobalVar->getName() != "llvm.global.annotations");

>From 0da94b8a350e95d87d619404251983460e5fb4c8 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Wed, 4 Mar 2026 09:10:28 -0500
Subject: [PATCH 4/4] More fixes for stupid mistakes.

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index ca5ecfa4c993b..d5ba02ad382ed 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -5354,8 +5354,8 @@ bool SPIRVInstructionSelector::selectGlobalValue(
       MIB3.constrainAllUses(TII, TRI, RBI);
       return true;
     }
-    assert(ResVReg != ResVReg);
-    return BuildCOPY(ResVReg, ResVReg, I);
+    assert(NewReg != ResVReg);
+    return BuildCOPY(ResVReg, NewReg, I);
   }
   auto GlobalVar = cast<GlobalVariable>(GV);
   assert(GlobalVar->getName() != "llvm.global.annotations");



More information about the llvm-commits mailing list