[llvm] [SPIR-V] Fix inttoptr type deduction with ptr.annotation (PR #189219)

Michal Paszkowski via llvm-commits llvm-commits at lists.llvm.org
Sun Mar 29 03:33:08 PDT 2026


https://github.com/michalpaszkowski created https://github.com/llvm/llvm-project/pull/189219

Opaque pointer inttoptr was recording ptr as a pointee type, so OpConvertUToPtr was emitted as pointer-to-pointer and then bitcasted back. Please see an example below.

LLVM IR:
```
%p = inttoptr i64 %x to ptr addrspace(1)
%a = call ptr addrspace(1) @llvm.ptr.annotation(... %p ...)
call spir_func void @prefetch(ptr addrspace(1) %a, ...)
```

SPIR-V (before the change):
```
%p2 = OpConvertUToPtr %_ptr_CrossWorkgroup__ptr_CrossWorkgroup_uchar %x
%p1 = OpBitcast %_ptr_CrossWorkgroup_uchar %p2
OpFunctionCall ... %p1 ...
```

Skip assigning pointee type for inttoptr when the destination is untyped, fallback later recovers the correct single pointer type.

>From 2f3116cbc8ce124901117fc0aa4fc8592cc44206 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal at michalpaszkowski.com>
Date: Sun, 29 Mar 2026 03:25:18 -0700
Subject: [PATCH] [SPIR-V] Fix inttoptr type deduction with ptr.annotation

Opaque pointer inttoptr was recording ptr addrspace(N) as a pointee type,
so OpConvertUToPtr was emitted as pointer-to-pointer and then bitcasted back.

Example:
	LLVM IR:
		%p = inttoptr i64 %x to ptr addrspace(1)
		%a = call ptr addrspace(1) @llvm.ptr.annotation(... %p ...)
		call spir_func void @prefetch(ptr addrspace(1) %a, ...)

	SPIR-V (before the change):
		%p2 = OpConvertUToPtr %_ptr_CrossWorkgroup__ptr_CrossWorkgroup_uchar %x
		%p1 = OpBitcast %_ptr_CrossWorkgroup_uchar %p2
		OpFunctionCall ... %p1 ...

Skip assigning pointee type for inttoptr when the destination is untyped,
fallback later recovers the correct single pointer type.
---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp |  8 +++-
 .../inttoptr-no-double-pointer.ll             | 37 +++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_cache_controls/inttoptr-no-double-pointer.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index be071baf7fa72..970b10ec064e4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -909,7 +909,13 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
                                           UnknownElemTypeI8);
     maybeAssignPtrType(Ty, I, RefTy, UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<IntToPtrInst>(I)) {
-    maybeAssignPtrType(Ty, I, Ref->getDestTy(), UnknownElemTypeI8);
+    // Passing an untyped pointer to maybeAssignPtrType would record it as the
+    // pointee type, producing a pointer‑to‑pointer in SPIR-V (OpConvertUToPtr
+    // with a double‑pointer result type). In this case do not assign type,
+    // leave Ty as nullptr so this can be later refined from the actual users of
+    // the inttoptr.
+    if (!isUntypedPointerTy(Ref->getDestTy()))
+      maybeAssignPtrType(Ty, I, Ref->getDestTy(), UnknownElemTypeI8);
   } else if (auto *Ref = dyn_cast<BitCastInst>(I)) {
     if (Type *Src = Ref->getSrcTy(), *Dest = Ref->getDestTy();
         isPointerTy(Src) && isPointerTy(Dest))
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_cache_controls/inttoptr-no-double-pointer.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_cache_controls/inttoptr-no-double-pointer.ll
new file mode 100644
index 0000000000000..7c388bb6b5f35
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_cache_controls/inttoptr-no-double-pointer.ll
@@ -0,0 +1,37 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_cache_controls,+SPV_INTEL_variable_length_array %s -o - | FileCheck %s
+; TODO: %if spirv-tools %{ llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_cache_controls,+SPV_INTEL_variable_length_array %s -o - -filetype=obj | spirv-val %}
+
+; Check that inttoptr followed by ptr.annotation does not produce a
+; double-pointer type for OpConvertUToPtr (and there is no OpBitcast back to
+; single-pointer type).
+
+; CHECK-DAG: %[[#UCHAR:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#PTR:]] = OpTypePointer CrossWorkgroup %[[#UCHAR]]
+; CHECK:     %[[#CONV:]] = OpConvertUToPtr %[[#PTR]]
+; CHECK-NOT: OpBitcast %[[#PTR]] %[[#CONV]]
+; CHECK:     OpFunctionCall %[[#]] %[[#]] %[[#CONV]]
+
+ at .str.file = private unnamed_addr addrspace(1) constant [1 x i8] zeroinitializer, section "llvm.metadata", align 1
+ at .str.cachecontrol = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\220,1\22}\00", section "llvm.metadata", align 1
+ at .str.cachecontrol.1 = private unnamed_addr addrspace(1) constant [13 x i8] c"{6442:\221,1\22}\00", section "llvm.metadata", align 1
+
+declare spir_func void @_Z45intel_sub_group_2d_block_prefetch_16b_8r16x2cPU3AS1viiiDv2_i(ptr addrspace(1) nonnull, i32, i32, i32, <2 x i32>) #0
+
+define spir_kernel void @test_inttoptr_annotation(ptr addrspace(1) %base) {
+entry:
+  %int = ptrtoint ptr addrspace(1) %base to i64
+  %vec = insertelement <4 x i64> zeroinitializer, i64 %int, i64 0
+  %cast = bitcast <4 x i64> %vec to <8 x i32>
+  %recast = bitcast <8 x i32> %cast to <4 x i64>
+  %extracted = extractelement <4 x i64> %recast, i64 0
+  %int2ptr = inttoptr i64 %extracted to ptr addrspace(1)
+  %coords = insertelement <2 x i32> <i32 0, i32 undef>, i32 42, i32 1
+  %a1 = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %int2ptr, ptr addrspace(1) @.str.cachecontrol, ptr addrspace(1) @.str.file, i32 0, ptr addrspace(1) null)
+  %a2 = call ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1) %a1, ptr addrspace(1) @.str.cachecontrol.1, ptr addrspace(1) @.str.file, i32 0, ptr addrspace(1) null)
+  call spir_func void @_Z45intel_sub_group_2d_block_prefetch_16b_8r16x2cPU3AS1viiiDv2_i(ptr addrspace(1) nonnull %a2, i32 8192, i32 4096, i32 8192, <2 x i32> %coords) #0
+  ret void
+}
+
+declare ptr addrspace(1) @llvm.ptr.annotation.p1.p1(ptr addrspace(1), ptr addrspace(1), ptr addrspace(1), i32, ptr addrspace(1))
+
+attributes #0 = { nounwind memory(argmem: read) }



More information about the llvm-commits mailing list