[llvm] [SPIR-V] Scalarize vector of pointers for ptrtoint/inttoptr (PR #184817)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 12 08:41:50 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/184817
>From 42600273d1a6adf5350d0f539ab3d85ab36b4662 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 5 Mar 2026 16:38:46 +0100
Subject: [PATCH 1/3] [SPIR-V] Scalarize vector of pointers for
ptrtoint/inttoptr
Scalarize ptrtoint and inttoptr instructions operating on vector-of-pointer types by converting them to extract/convert/insert sequences
---
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 55 +++++++++++++++++++
llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 6 +-
llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp | 1 +
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp | 12 +++-
.../pointers/vector-of-pointers-scalarized.ll | 29 ++++++++++
.../CodeGen/SPIRV/pointers/vector-ptrtoint.ll | 27 +++++++++
6 files changed, 125 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll
create mode 100644 llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 7a886de005b88..40262ee40340a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -305,6 +305,7 @@ class SPIRVEmitIntrinsics
bool postprocessTypes(Module &M);
bool processFunctionPointers(Module &M);
void parseFunDeclarations(Module &M);
+
void useRoundingMode(ConstrainedFPIntrinsic *FPI, IRBuilder<> &B);
bool processMaskedMemIntrinsic(IntrinsicInst &I);
bool convertMaskedMemIntrinsics(Module &M);
@@ -366,6 +367,8 @@ class SPIRVEmitIntrinsics
Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
Instruction *visitUnreachableInst(UnreachableInst &I);
Instruction *visitCallInst(CallInst &I);
+ Instruction *visitPtrToIntInst(PtrToIntInst &I);
+ Instruction *visitIntToPtrInst(IntToPtrInst &I);
StringRef getPassName() const override { return "SPIRV emit intrinsics"; }
@@ -2124,6 +2127,58 @@ SPIRVEmitIntrinsics::visitExtractElementInst(ExtractElementInst &I) {
return NewI;
}
+Instruction *SPIRVEmitIntrinsics::visitPtrToIntInst(PtrToIntInst &I) {
+ // Scalarize ptrtoint on vectors of pointers, since SPIR-V does not support
+ // vectors of pointers.
+ Type *SrcTy = I.getOperand(0)->getType();
+ auto *VecTy = dyn_cast<FixedVectorType>(SrcTy);
+
+ if (!VecTy || !VecTy->getElementType()->isPointerTy())
+ return &I;
+
+ IRBuilder<> B(I.getParent());
+ B.SetInsertPoint(&I);
+
+ unsigned NumElems = VecTy->getNumElements();
+ Type *ElemIntTy = cast<VectorType>(I.getType())->getElementType();
+ Value *Result = PoisonValue::get(I.getType());
+
+ for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
+ Value *Elem = B.CreateExtractElement(I.getOperand(0), B.getInt32(Idx));
+ Value *Conv = B.CreatePtrToInt(Elem, ElemIntTy);
+ Result = B.CreateInsertElement(Result, Conv, B.getInt32(Idx));
+ }
+
+ replaceAllUsesWithAndErase(B, &I, cast<Instruction>(Result));
+ return cast<Instruction>(Result);
+}
+
+Instruction *SPIRVEmitIntrinsics::visitIntToPtrInst(IntToPtrInst &I) {
+ // Scalarize inttoptr producing vectors of pointers, since SPIR-V does not
+ // support vectors of pointers.
+ Type *DstTy = I.getType();
+ auto *VecTy = dyn_cast<FixedVectorType>(DstTy);
+
+ if (!VecTy || !VecTy->getElementType()->isPointerTy())
+ return &I;
+
+ IRBuilder<> B(I.getParent());
+ B.SetInsertPoint(&I);
+
+ unsigned NumElems = VecTy->getNumElements();
+ Type *ElemPtrTy = VecTy->getElementType();
+ Value *Result = PoisonValue::get(I.getType());
+
+ for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
+ Value *Elem = B.CreateExtractElement(I.getOperand(0), B.getInt32(Idx));
+ Value *Conv = B.CreateIntToPtr(Elem, ElemPtrTy);
+ Result = B.CreateInsertElement(Result, Conv, B.getInt32(Idx));
+ }
+
+ replaceAllUsesWithAndErase(B, &I, cast<Instruction>(Result));
+ return cast<Instruction>(Result);
+}
+
Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &I) {
IRBuilder<> B(I.getParent());
B.SetInsertPoint(&I);
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 47ffecc4085ab..7acb632bacf2c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -376,7 +376,8 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
return DstTy.isPointerVector() && SrcTy.isVector() &&
!SrcTy.isPointer() &&
DstTy.getNumElements() == SrcTy.getNumElements();
- });
+ })
+ .scalarize(0);
getActionDefinitionsBuilder(G_PTRTOINT)
.legalForCartesianProduct(allIntScalars, allPtrs)
.legalIf(
@@ -387,7 +388,8 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
return SrcTy.isPointerVector() && DstTy.isVector() &&
!DstTy.isPointer() &&
DstTy.getNumElements() == SrcTy.getNumElements();
- });
+ })
+ .scalarize(0);
getActionDefinitionsBuilder(G_PTR_ADD)
.legalForCartesianProduct(allPtrs, allIntScalars)
.legalIf(
diff --git a/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
index d9e134552a269..c467433adbcc1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
@@ -277,6 +277,7 @@ static SPIRVTypeInst deduceResultTypeFromOperands(MachineInstr *I,
case TargetOpcode::G_ANYEXT:
case TargetOpcode::G_SEXT:
case TargetOpcode::G_ZEXT:
+ case TargetOpcode::G_PTRTOINT:
return deduceIntTypeFromResult(ResVReg, MIB, GR);
case TargetOpcode::G_BUILD_VECTOR:
return deduceTypeFromOperandRange(I, MIB, GR, 1, I->getNumOperands());
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index 9df5d18221a6d..c86cc076cc5da 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -349,10 +349,16 @@ static SPIRVTypeInst propagateSPIRVType(MachineInstr *MI,
}
break;
}
- case TargetOpcode::G_PTRTOINT:
- SpvType = GR->getOrCreateSPIRVIntegerType(
- MRI.getType(Reg).getScalarSizeInBits(), MIB);
+ case TargetOpcode::G_PTRTOINT: {
+ LLT RegType = MRI.getType(Reg);
+ SpvType =
+ GR->getOrCreateSPIRVIntegerType(RegType.getScalarSizeInBits(), MIB);
+ if (RegType.isVector()) {
+ SpvType = GR->getOrCreateSPIRVVectorType(
+ SpvType, RegType.getNumElements(), MIB, true);
+ }
break;
+ }
case TargetOpcode::G_TRUNC:
case TargetOpcode::G_ADDRSPACE_CAST:
case TargetOpcode::G_PTR_ADD:
diff --git a/llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll b/llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll
new file mode 100644
index 0000000000000..34befb07728a3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll
@@ -0,0 +1,29 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+declare spir_func void @foo(<2 x i64>)
+
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#I8:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#PTR:]] = OpTypePointer CrossWorkgroup %[[#I8]]
+; CHECK-DAG: %[[#V2I64:]] = OpTypeVector %[[#I64]] 2
+; CHECK-DAG: OpName %[[#P0:]] "p.elem0"
+; CHECK-DAG: OpName %[[#P1:]] "p.elem1"
+
+; CHECK: %[[#ENTRY:]] = OpFunction
+; CHECK: %[[#P0]] = OpFunctionParameter %[[#PTR]]
+; CHECK: %[[#P1]] = OpFunctionParameter %[[#PTR]]
+; CHECK: OpFunctionParameter %[[#PTR]]
+; CHECK: OpFunctionParameter %[[#PTR]]
+
+; CHECK: %[[#CONV0:]] = OpConvertPtrToU %[[#I64]] %[[#P0]]
+; CHECK: OpCompositeInsert %[[#V2I64]] %[[#CONV0]]
+; CHECK: %[[#CONV1:]] = OpConvertPtrToU %[[#I64]] %[[#P1]]
+; CHECK: OpCompositeInsert %[[#V2I64]] %[[#CONV1]]
+
+define spir_kernel void @test_ptrtoaddr(<2 x ptr addrspace(1)> %p, <2 x ptr addrspace(1)> %res) {
+entry:
+ %addr = ptrtoint <2 x ptr addrspace(1)> %p to <2 x i64>
+ call void @foo(<2 x i64> %addr)
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll b/llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll
new file mode 100644
index 0000000000000..cc4a89380e316
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll
@@ -0,0 +1,27 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+declare spir_func void @foo(<2 x i64>)
+
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#V2I64:]] = OpTypeVector %[[#I64]] 2
+; CHECK-DAG: OpName %[[#FUNC:]] "test_scalar_ptrtoint_to_vector"
+
+; CHECK: %[[#FUNC]] = OpFunction
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpCompositeInsert %[[#V2I64]]
+; CHECK: OpCompositeInsert %[[#V2I64]]
+; CHECK: OpFunctionEnd
+
+define spir_kernel void @test_scalar_ptrtoint_to_vector(ptr addrspace(1) %p0, ptr addrspace(1) %p1) {
+entry:
+ ; Convert each pointer to integer separately
+ %addr0 = ptrtoint ptr addrspace(1) %p0 to i64
+ %addr1 = ptrtoint ptr addrspace(1) %p1 to i64
+ ; Combine into a vector
+ %vec0 = insertelement <2 x i64> poison, i64 %addr0, i32 0
+ %vec = insertelement <2 x i64> %vec0, i64 %addr1, i32 1
+ call void @foo(<2 x i64> %vec)
+ ret void
+}
>From 559cd5a82f0449a129d5475f5be56e53ed5152a1 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 21 Apr 2026 14:17:50 +0200
Subject: [PATCH 2/3] Update + address comments
---
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 139 ++++++++++++------
.../pointers/scalarize-vector-inttoptr.ll | 27 ++++
.../pointers/scalarize-vector-ptrtoint.ll | 27 ++++
.../pointers/vector-of-pointers-scalarized.ll | 29 ----
.../CodeGen/SPIRV/pointers/vector-ptrtoint.ll | 27 ----
5 files changed, 151 insertions(+), 98 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll
create mode 100644 llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll
delete mode 100644 llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll
delete mode 100644 llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 40262ee40340a..860b926512999 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -305,11 +305,11 @@ class SPIRVEmitIntrinsics
bool postprocessTypes(Module &M);
bool processFunctionPointers(Module &M);
void parseFunDeclarations(Module &M);
-
void useRoundingMode(ConstrainedFPIntrinsic *FPI, IRBuilder<> &B);
bool processMaskedMemIntrinsic(IntrinsicInst &I);
bool convertMaskedMemIntrinsics(Module &M);
void preprocessBoolVectorBitcasts(Function &F);
+ void scalarizeVectorPtrCasts(Function &F);
void emitUnstructuredLoopControls(Function &F, IRBuilder<> &B);
@@ -367,8 +367,6 @@ class SPIRVEmitIntrinsics
Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
Instruction *visitUnreachableInst(UnreachableInst &I);
Instruction *visitCallInst(CallInst &I);
- Instruction *visitPtrToIntInst(PtrToIntInst &I);
- Instruction *visitIntToPtrInst(IntToPtrInst &I);
StringRef getPassName() const override { return "SPIRV emit intrinsics"; }
@@ -2127,56 +2125,112 @@ SPIRVEmitIntrinsics::visitExtractElementInst(ExtractElementInst &I) {
return NewI;
}
-Instruction *SPIRVEmitIntrinsics::visitPtrToIntInst(PtrToIntInst &I) {
- // Scalarize ptrtoint on vectors of pointers, since SPIR-V does not support
- // vectors of pointers.
- Type *SrcTy = I.getOperand(0)->getType();
- auto *VecTy = dyn_cast<FixedVectorType>(SrcTy);
+// Trace a vector Value to find which scalar value was inserted at a given
+// index. Walks backwards through insertelement chains. Returns nullptr if the
+// element cannot be determined statically.
+static Value *findInsertedElement(Value *Vec, unsigned TargetIdx) {
+ while (auto *IE = dyn_cast<InsertElementInst>(Vec)) {
+ auto *IdxC = dyn_cast<ConstantInt>(IE->getOperand(2));
+ if (!IdxC)
+ return nullptr;
+ if (IdxC->getZExtValue() == TargetIdx)
+ return IE->getOperand(1);
+ Vec = IE->getOperand(0);
+ }
+ return nullptr;
+}
- if (!VecTy || !VecTy->getElementType()->isPointerTy())
- return &I;
+// Scalarize ptrtoint and inttoptr instructions operating on vectors of
+// pointers, since SPIR-V does not support vectors of pointers without
+// extensions. This must run before the main visitor loop to ensure
+// insertelement/extractelement chains are still in their original IR form.
+void SPIRVEmitIntrinsics::scalarizeVectorPtrCasts(Function &F) {
+ // If the extension for vector-of-pointers is available, no scalarization
+ // is needed - the backend can handle them natively.
+ const SPIRVSubtarget &ST = TM.getSubtarget<SPIRVSubtarget>(F);
+ if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_masked_gather_scatter))
+ return;
- IRBuilder<> B(I.getParent());
- B.SetInsertPoint(&I);
+ SmallVector<Instruction *, 8> ToErase;
- unsigned NumElems = VecTy->getNumElements();
- Type *ElemIntTy = cast<VectorType>(I.getType())->getElementType();
- Value *Result = PoisonValue::get(I.getType());
+ for (Instruction &I : make_early_inc_range(instructions(F))) {
+ if (auto *PTI = dyn_cast<PtrToIntInst>(&I)) {
+ auto *VecTy = dyn_cast<FixedVectorType>(PTI->getOperand(0)->getType());
+ if (!VecTy || !VecTy->getElementType()->isPointerTy())
+ continue;
- for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
- Value *Elem = B.CreateExtractElement(I.getOperand(0), B.getInt32(Idx));
- Value *Conv = B.CreatePtrToInt(Elem, ElemIntTy);
- Result = B.CreateInsertElement(Result, Conv, B.getInt32(Idx));
- }
+ IRBuilder<> B(PTI);
+ unsigned NumElems = VecTy->getNumElements();
+ Type *ElemIntTy = cast<VectorType>(PTI->getType())->getElementType();
+ Value *Result = PoisonValue::get(PTI->getType());
+
+ for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
+ Value *ElemPtr = findInsertedElement(PTI->getOperand(0), Idx);
+ if (!ElemPtr)
+ ElemPtr = B.CreateExtractElement(PTI->getOperand(0), B.getInt32(Idx));
+ Value *Conv = B.CreatePtrToInt(ElemPtr, ElemIntTy);
+ Result = B.CreateInsertElement(Result, Conv, B.getInt32(Idx));
+ }
- replaceAllUsesWithAndErase(B, &I, cast<Instruction>(Result));
- return cast<Instruction>(Result);
-}
+ PTI->replaceAllUsesWith(Result);
+ ToErase.push_back(PTI);
+ } else if (auto *ITP = dyn_cast<IntToPtrInst>(&I)) {
+ auto *VecTy = dyn_cast<FixedVectorType>(ITP->getType());
+ if (!VecTy || !VecTy->getElementType()->isPointerTy())
+ continue;
-Instruction *SPIRVEmitIntrinsics::visitIntToPtrInst(IntToPtrInst &I) {
- // Scalarize inttoptr producing vectors of pointers, since SPIR-V does not
- // support vectors of pointers.
- Type *DstTy = I.getType();
- auto *VecTy = dyn_cast<FixedVectorType>(DstTy);
+ // Only scalarize if all users are extractelement with constant index.
+ // Other uses (e.g. masked.gather) need the vector-of-pointers and
+ // should be handled by extension-specific lowering.
+ bool AllUsersExtract = llvm::all_of(ITP->users(), [&](User *U) {
+ auto *EE = dyn_cast<ExtractElementInst>(U);
+ return EE && isa<ConstantInt>(EE->getIndexOperand());
+ });
+ if (!AllUsersExtract)
+ continue;
- if (!VecTy || !VecTy->getElementType()->isPointerTy())
- return &I;
+ IRBuilder<> B(ITP);
+ Type *ElemPtrTy = VecTy->getElementType();
+
+ // Replace extractelement users with scalar inttoptr.
+ for (User *U : make_early_inc_range(ITP->users())) {
+ auto *EE = cast<ExtractElementInst>(U);
+ unsigned Idx = cast<ConstantInt>(EE->getIndexOperand())->getZExtValue();
+ B.SetInsertPoint(EE);
+ Value *Elem =
+ B.CreateExtractElement(ITP->getOperand(0), B.getInt32(Idx));
+ Value *Conv = B.CreateIntToPtr(Elem, ElemPtrTy);
+ EE->replaceAllUsesWith(Conv);
+ ToErase.push_back(EE);
+ }
- IRBuilder<> B(I.getParent());
- B.SetInsertPoint(&I);
+ ToErase.push_back(ITP);
+ }
+ }
- unsigned NumElems = VecTy->getNumElements();
- Type *ElemPtrTy = VecTy->getElementType();
- Value *Result = PoisonValue::get(I.getType());
+ for (Instruction *I : ToErase)
+ I->eraseFromParent();
- for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
- Value *Elem = B.CreateExtractElement(I.getOperand(0), B.getInt32(Idx));
- Value *Conv = B.CreateIntToPtr(Elem, ElemPtrTy);
- Result = B.CreateInsertElement(Result, Conv, B.getInt32(Idx));
+ // Clean up dead insertelement chains that produced vectors of pointers
+ // (e.g. the chain feeding a now-erased ptrtoint). Use a worklist to
+ // handle chains where erasing one insert makes the previous one dead.
+ SmallVector<Instruction *, 8> DeadInsts;
+ for (Instruction &I : instructions(F)) {
+ if (!isa<InsertElementInst>(&I))
+ continue;
+ auto *VecTy = dyn_cast<FixedVectorType>(I.getType());
+ if (VecTy && VecTy->getElementType()->isPointerTy() && I.use_empty())
+ DeadInsts.push_back(&I);
+ }
+ while (!DeadInsts.empty()) {
+ Instruction *I = DeadInsts.pop_back_val();
+ SmallVector<Value *, 4> Ops(I->operand_values());
+ I->eraseFromParent();
+ for (Value *Op : Ops)
+ if (auto *OpI = dyn_cast<Instruction>(Op);
+ OpI && OpI->use_empty() && isa<InsertElementInst>(OpI))
+ DeadInsts.push_back(OpI);
}
-
- replaceAllUsesWithAndErase(B, &I, cast<Instruction>(Result));
- return cast<Instruction>(Result);
}
Instruction *SPIRVEmitIntrinsics::visitInsertValueInst(InsertValueInst &I) {
@@ -3260,6 +3314,7 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
Phi.mutateType(B.getInt32Ty());
}
+ scalarizeVectorPtrCasts(Func);
preprocessBoolVectorBitcasts(Func);
SmallVector<Instruction *> Worklist(
llvm::make_pointer_range(instructions(Func)));
diff --git a/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll
new file mode 100644
index 0000000000000..5a62737254ff8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll
@@ -0,0 +1,27 @@
+; Test that inttoptr on a vector of integers to a vector of pointers is
+; scalarized into per-element inttoptr operations, since SPIR-V does not
+; support vectors of pointers without extensions.
+
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#I8:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#PTR:]] = OpTypePointer CrossWorkgroup %[[#I8]]
+
+; CHECK: OpFunction
+; CHECK: OpConvertUToPtr %[[#PTR]]
+; CHECK: OpConvertUToPtr %[[#PTR]]
+; CHECK: OpFunctionEnd
+
+define spir_kernel void @test_inttoptr_scalarized(<2 x i64> %addr) {
+entry:
+ %ptrs = inttoptr <2 x i64> %addr to <2 x ptr addrspace(1)>
+ %p0 = extractelement <2 x ptr addrspace(1)> %ptrs, i32 0
+ %p1 = extractelement <2 x ptr addrspace(1)> %ptrs, i32 1
+ %val0 = load i8, ptr addrspace(1) %p0
+ %val1 = load i8, ptr addrspace(1) %p1
+ %sum = add i8 %val0, %val1
+ store i8 %sum, ptr addrspace(1) %p0
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll
new file mode 100644
index 0000000000000..8892d0ec942cc
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll
@@ -0,0 +1,27 @@
+; Test that ptrtoint on a vector of pointers to a vector of integers is
+; scalarized into per-element ptrtoint operations, since SPIR-V does not
+; support vectors of pointers without extensions.
+
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+declare spir_func void @foo(<2 x i64>)
+
+; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#V2I64:]] = OpTypeVector %[[#I64]] 2
+
+; CHECK: OpFunction
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpCompositeInsert %[[#V2I64]]
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpCompositeInsert %[[#V2I64]]
+; CHECK: OpFunctionEnd
+
+define spir_kernel void @test_ptrtoint_scalarized(ptr addrspace(1) %p0, ptr addrspace(1) %p1) {
+entry:
+ %vec0 = insertelement <2 x ptr addrspace(1)> poison, ptr addrspace(1) %p0, i32 0
+ %vec = insertelement <2 x ptr addrspace(1)> %vec0, ptr addrspace(1) %p1, i32 1
+ %addr = ptrtoint <2 x ptr addrspace(1)> %vec to <2 x i64>
+ call void @foo(<2 x i64> %addr)
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll b/llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll
deleted file mode 100644
index 34befb07728a3..0000000000000
--- a/llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll
+++ /dev/null
@@ -1,29 +0,0 @@
-; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-
-declare spir_func void @foo(<2 x i64>)
-
-; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
-; CHECK-DAG: %[[#I8:]] = OpTypeInt 8 0
-; CHECK-DAG: %[[#PTR:]] = OpTypePointer CrossWorkgroup %[[#I8]]
-; CHECK-DAG: %[[#V2I64:]] = OpTypeVector %[[#I64]] 2
-; CHECK-DAG: OpName %[[#P0:]] "p.elem0"
-; CHECK-DAG: OpName %[[#P1:]] "p.elem1"
-
-; CHECK: %[[#ENTRY:]] = OpFunction
-; CHECK: %[[#P0]] = OpFunctionParameter %[[#PTR]]
-; CHECK: %[[#P1]] = OpFunctionParameter %[[#PTR]]
-; CHECK: OpFunctionParameter %[[#PTR]]
-; CHECK: OpFunctionParameter %[[#PTR]]
-
-; CHECK: %[[#CONV0:]] = OpConvertPtrToU %[[#I64]] %[[#P0]]
-; CHECK: OpCompositeInsert %[[#V2I64]] %[[#CONV0]]
-; CHECK: %[[#CONV1:]] = OpConvertPtrToU %[[#I64]] %[[#P1]]
-; CHECK: OpCompositeInsert %[[#V2I64]] %[[#CONV1]]
-
-define spir_kernel void @test_ptrtoaddr(<2 x ptr addrspace(1)> %p, <2 x ptr addrspace(1)> %res) {
-entry:
- %addr = ptrtoint <2 x ptr addrspace(1)> %p to <2 x i64>
- call void @foo(<2 x i64> %addr)
- ret void
-}
diff --git a/llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll b/llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll
deleted file mode 100644
index cc4a89380e316..0000000000000
--- a/llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll
+++ /dev/null
@@ -1,27 +0,0 @@
-; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
-; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-
-declare spir_func void @foo(<2 x i64>)
-
-; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
-; CHECK-DAG: %[[#V2I64:]] = OpTypeVector %[[#I64]] 2
-; CHECK-DAG: OpName %[[#FUNC:]] "test_scalar_ptrtoint_to_vector"
-
-; CHECK: %[[#FUNC]] = OpFunction
-; CHECK: OpConvertPtrToU %[[#I64]]
-; CHECK: OpConvertPtrToU %[[#I64]]
-; CHECK: OpCompositeInsert %[[#V2I64]]
-; CHECK: OpCompositeInsert %[[#V2I64]]
-; CHECK: OpFunctionEnd
-
-define spir_kernel void @test_scalar_ptrtoint_to_vector(ptr addrspace(1) %p0, ptr addrspace(1) %p1) {
-entry:
- ; Convert each pointer to integer separately
- %addr0 = ptrtoint ptr addrspace(1) %p0 to i64
- %addr1 = ptrtoint ptr addrspace(1) %p1 to i64
- ; Combine into a vector
- %vec0 = insertelement <2 x i64> poison, i64 %addr0, i32 0
- %vec = insertelement <2 x i64> %vec0, i64 %addr1, i32 1
- call void @foo(<2 x i64> %vec)
- ret void
-}
>From 3fd0774d1f050e9a90945b1c883ee64400a81020 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 21 Apr 2026 15:12:37 +0200
Subject: [PATCH 3/3] fix empty uses
---
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 9 ++---
.../masked-gather-no-extension.ll | 17 +++++----
.../masked-scatter-no-extension.ll | 13 ++++---
.../pointers/scalarize-vector-inttoptr.ll | 35 +++++++++++++++++++
.../pointers/scalarize-vector-ptrtoint.ll | 25 +++++++++++++
5 files changed, 84 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 860b926512999..1e45d4a660f80 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2182,10 +2182,11 @@ void SPIRVEmitIntrinsics::scalarizeVectorPtrCasts(Function &F) {
// Only scalarize if all users are extractelement with constant index.
// Other uses (e.g. masked.gather) need the vector-of-pointers and
// should be handled by extension-specific lowering.
- bool AllUsersExtract = llvm::all_of(ITP->users(), [&](User *U) {
- auto *EE = dyn_cast<ExtractElementInst>(U);
- return EE && isa<ConstantInt>(EE->getIndexOperand());
- });
+ bool AllUsersExtract =
+ !ITP->use_empty() && llvm::all_of(ITP->users(), [&](User *U) {
+ auto *EE = dyn_cast<ExtractElementInst>(U);
+ return EE && isa<ConstantInt>(EE->getIndexOperand());
+ });
if (!AllUsersExtract)
continue;
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-gather-no-extension.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-gather-no-extension.ll
index 2270d384834fb..3cbaf6fcd5dfd 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-gather-no-extension.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-gather-no-extension.ll
@@ -1,16 +1,21 @@
-; Test that llvm.masked.gather produces an error when the
-; SPV_INTEL_masked_gather_scatter extension is not enabled, since vector of
-; pointers is not supported in SPIR-V without this extension.
+; Test that llvm.masked.gather is scalarized into individual loads when the
+; SPV_INTEL_masked_gather_scatter extension is not enabled. The generic
+; ScalarizeMaskedMemIntrin pass handles this before SPIR-V-specific passes run.
-; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
declare <4 x i32> @llvm.masked.gather.v4i32.v4p1(<4 x ptr addrspace(1)>, i32, <4 x i1>, <4 x i32>)
-; CHECK: error: {{.*}}Vector of pointers requires SPV_INTEL_masked_gather_scatter extension
+; CHECK: OpFunction
+; CHECK: OpLoad
+; CHECK: OpLoad
+; CHECK: OpLoad
+; CHECK: OpLoad
-define spir_kernel void @test_gather_no_ext(<4 x i64> %addrs) {
+define spir_kernel void @test_gather_no_ext(<4 x i64> %addrs, ptr addrspace(1) %out) {
entry:
%ptrs = inttoptr <4 x i64> %addrs to <4 x ptr addrspace(1)>
%data = call <4 x i32> @llvm.masked.gather.v4i32.v4p1(<4 x ptr addrspace(1)> %ptrs, i32 4, <4 x i1> <i1 true, i1 true, i1 true, i1 true>, <4 x i32> zeroinitializer)
+ store <4 x i32> %data, ptr addrspace(1) %out
ret void
}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-scatter-no-extension.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-scatter-no-extension.ll
index b76b41dd5b62b..dd3a6e2d837dd 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-scatter-no-extension.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_masked_gather_scatter/masked-scatter-no-extension.ll
@@ -1,12 +1,15 @@
-; Test that llvm.masked.scatter produces an error when the
-; SPV_INTEL_masked_gather_scatter extension is not enabled, since vector of
-; pointers is not supported in SPIR-V without this extension.
+; Test that llvm.masked.scatter is scalarized into individual stores when the
+; SPV_INTEL_masked_gather_scatter extension is not enabled. The generic
+; ScalarizeMaskedMemIntrin pass handles this before SPIR-V-specific passes run.
-; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown %s -o /dev/null 2>&1 | FileCheck %s
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
declare void @llvm.masked.scatter.v4i32.v4p1(<4 x i32>, <4 x ptr addrspace(1)>, i32, <4 x i1>)
-; CHECK: error: {{.*}}Vector of pointers requires SPV_INTEL_masked_gather_scatter extension
+; CHECK: OpFunction
+; CHECK: OpStore
+; CHECK: OpStore
+; CHECK: OpReturn
define spir_kernel void @test_scatter_no_ext(<4 x i32> %data, <4 x i64> %addrs) {
entry:
diff --git a/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll
index 5a62737254ff8..7ea135fa8a0c9 100644
--- a/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll
+++ b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-inttoptr.ll
@@ -25,3 +25,38 @@ entry:
store i8 %sum, ptr addrspace(1) %p0
ret void
}
+
+; Test scalarization with a 4-element vector.
+; CHECK: OpFunction
+; CHECK: OpConvertUToPtr %[[#PTR]]
+; CHECK: OpConvertUToPtr %[[#PTR]]
+; CHECK: OpConvertUToPtr %[[#PTR]]
+; CHECK: OpConvertUToPtr %[[#PTR]]
+; CHECK: OpFunctionEnd
+
+define spir_kernel void @test_inttoptr_v4(<4 x i64> %addr) {
+entry:
+ %ptrs = inttoptr <4 x i64> %addr to <4 x ptr addrspace(1)>
+ %p0 = extractelement <4 x ptr addrspace(1)> %ptrs, i32 0
+ %p1 = extractelement <4 x ptr addrspace(1)> %ptrs, i32 1
+ %p2 = extractelement <4 x ptr addrspace(1)> %ptrs, i32 2
+ %p3 = extractelement <4 x ptr addrspace(1)> %ptrs, i32 3
+ store i8 1, ptr addrspace(1) %p0
+ store i8 2, ptr addrspace(1) %p1
+ store i8 3, ptr addrspace(1) %p2
+ store i8 4, ptr addrspace(1) %p3
+ ret void
+}
+
+; CHECK: OpFunction
+; CHECK: OpConvertUToPtr %[[#PTR]]
+; CHECK-NOT: OpConvertUToPtr
+; CHECK: OpFunctionEnd
+
+define spir_kernel void @test_inttoptr_partial_use(<2 x i64> %addr) {
+entry:
+ %ptrs = inttoptr <2 x i64> %addr to <2 x ptr addrspace(1)>
+ %p0 = extractelement <2 x ptr addrspace(1)> %ptrs, i32 0
+ store i8 42, ptr addrspace(1) %p0
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll
index 8892d0ec942cc..1a538e08e7ee8 100644
--- a/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll
+++ b/llvm/test/CodeGen/SPIRV/pointers/scalarize-vector-ptrtoint.ll
@@ -6,9 +6,11 @@
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
declare spir_func void @foo(<2 x i64>)
+declare spir_func void @bar(<4 x i64>)
; CHECK-DAG: %[[#I64:]] = OpTypeInt 64 0
; CHECK-DAG: %[[#V2I64:]] = OpTypeVector %[[#I64]] 2
+; CHECK-DAG: %[[#V4I64:]] = OpTypeVector %[[#I64]] 4
; CHECK: OpFunction
; CHECK: OpConvertPtrToU %[[#I64]]
@@ -25,3 +27,26 @@ entry:
call void @foo(<2 x i64> %addr)
ret void
}
+
+; Test ptrtoint scalarization with a 4-element vector.
+; CHECK: OpFunction
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpCompositeInsert %[[#V4I64]]
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpCompositeInsert %[[#V4I64]]
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpCompositeInsert %[[#V4I64]]
+; CHECK: OpConvertPtrToU %[[#I64]]
+; CHECK: OpCompositeInsert %[[#V4I64]]
+; CHECK: OpFunctionEnd
+
+define spir_kernel void @test_ptrtoint_v4(ptr addrspace(1) %p0, ptr addrspace(1) %p1, ptr addrspace(1) %p2, ptr addrspace(1) %p3) {
+entry:
+ %vec0 = insertelement <4 x ptr addrspace(1)> poison, ptr addrspace(1) %p0, i32 0
+ %vec1 = insertelement <4 x ptr addrspace(1)> %vec0, ptr addrspace(1) %p1, i32 1
+ %vec2 = insertelement <4 x ptr addrspace(1)> %vec1, ptr addrspace(1) %p2, i32 2
+ %vec3 = insertelement <4 x ptr addrspace(1)> %vec2, ptr addrspace(1) %p3, i32 3
+ %addr = ptrtoint <4 x ptr addrspace(1)> %vec3 to <4 x i64>
+ call void @bar(<4 x i64> %addr)
+ ret void
+}
More information about the llvm-commits
mailing list