[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