[llvm] [SPIR-V] Scalarize vector of pointers for ptrtoint/inttoptr (PR #184817)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 5 07:56:01 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
Scalarize ptrtoint and inttoptr instructions operating by converting them to extract/convert/insert sequences
Fixes #<!-- -->184638
---
Full diff: https://github.com/llvm/llvm-project/pull/184817.diff
6 Files Affected:
- (modified) llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp (+199)
- (modified) llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp (+4-2)
- (modified) llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp (+1)
- (modified) llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp (+9-3)
- (added) llvm/test/CodeGen/SPIRV/pointers/vector-of-pointers-scalarized.ll (+29)
- (added) llvm/test/CodeGen/SPIRV/pointers/vector-ptrtoint.ll (+27)
``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index c84f41dada005..f8217321451d5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -156,6 +156,11 @@ static bool isaGEP(const Value *V) {
return isa<StructuredGEPInst>(V) || isa<GetElementPtrInst>(V);
}
+static bool isVectorOfPointers(Type *Ty) {
+ auto *VecTy = dyn_cast<FixedVectorType>(Ty);
+ return VecTy && VecTy->getElementType()->isPointerTy();
+}
+
class SPIRVEmitIntrinsics
: public ModulePass,
public InstVisitor<SPIRVEmitIntrinsics, Instruction *> {
@@ -298,6 +303,8 @@ class SPIRVEmitIntrinsics
bool postprocessTypes(Module &M);
bool processFunctionPointers(Module &M);
void parseFunDeclarations(Module &M);
+ bool scalarizeVectorOfPointersArgs(Module &M);
+
void useRoundingMode(ConstrainedFPIntrinsic *FPI, IRBuilder<> &B);
void emitUnstructuredLoopControls(Function &F, IRBuilder<> &B);
@@ -357,6 +364,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"; }
@@ -2106,6 +2115,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);
@@ -3281,9 +3342,147 @@ void SPIRVEmitIntrinsics::parseFunDeclarations(Module &M) {
}
}
+bool SPIRVEmitIntrinsics::scalarizeVectorOfPointersArgs(Module &M) {
+ bool Changed = false;
+ SmallVector<Function *> FuncsToProcess;
+
+ for (auto &F : M) {
+ if (F.isDeclaration() || F.isIntrinsic())
+ continue;
+ for (auto &Arg : F.args()) {
+ if (isVectorOfPointers(Arg.getType())) {
+ FuncsToProcess.push_back(&F);
+ break;
+ }
+ }
+ }
+
+ for (Function *OldF : FuncsToProcess) {
+ // Build the new function type with scalarized vector-of-pointer arguments
+ SmallVector<Type *, 8> NewArgTypes;
+
+ unsigned NewArgIdx = 0;
+ for (unsigned I = 0; I < OldF->arg_size(); ++I) {
+ Type *ArgTy = OldF->getArg(I)->getType();
+ if (isVectorOfPointers(ArgTy)) {
+ auto *VecTy = cast<FixedVectorType>(ArgTy);
+ unsigned NumElems = VecTy->getNumElements();
+ Type *ElemTy = VecTy->getElementType();
+ for (unsigned J = 0; J < NumElems; ++J) {
+ NewArgTypes.push_back(ElemTy);
+ ++NewArgIdx;
+ }
+ } else {
+ NewArgTypes.push_back(ArgTy);
+ ++NewArgIdx;
+ }
+ }
+
+ FunctionType *NewFTy =
+ FunctionType::get(OldF->getReturnType(), NewArgTypes, OldF->isVarArg());
+ Function *NewF =
+ Function::Create(NewFTy, OldF->getLinkage(), OldF->getAddressSpace(),
+ OldF->getName() + ".scalarized", &M);
+ NewF->setCallingConv(OldF->getCallingConv());
+ NewF->copyAttributesFrom(OldF);
+ // Clear parameter attributes since the signature changed
+ NewF->setAttributes(AttributeList());
+
+ // Move basic blocks from old function to new
+ NewF->splice(NewF->begin(), OldF);
+
+ // Build mapping from old arguments to new scalar arguments and transform
+ // uses of vector-of-pointer arguments inline
+ SmallVector<Instruction *, 8> ToErase;
+ unsigned NewArgI = 0;
+ for (unsigned I = 0; I < OldF->arg_size(); ++I) {
+ Argument *OldArg = OldF->getArg(I);
+ if (isVectorOfPointers(OldArg->getType())) {
+ auto *VecTy = cast<FixedVectorType>(OldArg->getType());
+ unsigned NumElems = VecTy->getNumElements();
+ SmallVector<Value *, 4> ScalarElems;
+ for (unsigned J = 0; J < NumElems; ++J) {
+ Argument *NewArg = NewF->getArg(NewArgI++);
+ NewArg->setName(OldArg->getName() + ".elem" + Twine(J));
+ ScalarElems.push_back(NewArg);
+ }
+ // Scalarize all uses of the old vector argument inline
+ for (User *U : make_early_inc_range(OldArg->users())) {
+ if (auto *EE = dyn_cast<ExtractElementInst>(U)) {
+ // extractelement <N x ptr> %v, i32 K -> scalar element K
+ if (auto *IdxC = dyn_cast<ConstantInt>(EE->getIndexOperand())) {
+ unsigned Idx = IdxC->getZExtValue();
+ if (Idx < NumElems) {
+ EE->replaceAllUsesWith(ScalarElems[Idx]);
+ ToErase.push_back(EE);
+ }
+ }
+ } else if (auto *PTI = dyn_cast<PtrToIntInst>(U)) {
+ // ptrtoint <N x ptr> %v to <N x iK> -> scalarize
+ auto *ResultTy = cast<FixedVectorType>(PTI->getType());
+ Type *ElemIntTy = ResultTy->getElementType();
+ IRBuilder<> B(PTI);
+ Value *Result = PoisonValue::get(ResultTy);
+ for (unsigned Idx = 0; Idx < NumElems; ++Idx) {
+ Value *Conv = B.CreatePtrToInt(ScalarElems[Idx], ElemIntTy);
+ Result = B.CreateInsertElement(Result, Conv, B.getInt32(Idx));
+ }
+ PTI->replaceAllUsesWith(Result);
+ ToErase.push_back(PTI);
+ }
+ }
+ } else {
+ Argument *NewArg = NewF->getArg(NewArgI++);
+ NewArg->setName(OldArg->getName());
+ OldArg->replaceAllUsesWith(NewArg);
+ }
+ }
+
+ for (Instruction *I : ToErase)
+ I->eraseFromParent();
+
+ SmallVector<CallInst *, 8> CallsToUpdate;
+ for (User *U : make_early_inc_range(OldF->users())) {
+ if (auto *CI = dyn_cast<CallInst>(U))
+ if (CI->getCalledFunction() == OldF)
+ CallsToUpdate.push_back(CI);
+ }
+
+ for (CallInst *CI : CallsToUpdate) {
+ SmallVector<Value *, 8> NewArgs;
+ IRBuilder<> CallB(CI);
+ for (unsigned I = 0; I < CI->arg_size(); ++I) {
+ Value *Arg = CI->getArgOperand(I);
+ if (isVectorOfPointers(Arg->getType())) {
+ auto *VecTy = cast<FixedVectorType>(Arg->getType());
+ unsigned NumElems = VecTy->getNumElements();
+ for (unsigned J = 0; J < NumElems; ++J) {
+ Value *Elem = CallB.CreateExtractElement(Arg, CallB.getInt32(J));
+ NewArgs.push_back(Elem);
+ }
+ } else {
+ NewArgs.push_back(Arg);
+ }
+ }
+ CallInst *NewCI = CallB.CreateCall(NewF, NewArgs);
+ NewCI->setCallingConv(NewF->getCallingConv());
+ if (!CI->getType()->isVoidTy())
+ CI->replaceAllUsesWith(NewCI);
+ CI->eraseFromParent();
+ }
+
+ NewF->takeName(OldF);
+ OldF->eraseFromParent();
+ Changed = true;
+ }
+
+ return Changed;
+}
+
bool SPIRVEmitIntrinsics::runOnModule(Module &M) {
bool Changed = false;
+ Changed |= scalarizeVectorOfPointersArgs(M);
parseFunDeclarations(M);
insertConstantsForFPFastMathDefault(M);
GVUsers.init(M);
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 93e82750c4f32..2fe94c172bf81 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -366,11 +366,13 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
getActionDefinitionsBuilder(G_INTTOPTR)
.legalForCartesianProduct(allPtrs, allIntScalars)
.legalIf(
- all(typeInSet(0, allPtrs), typeOfExtendedScalars(1, IsExtendedInts)));
+ all(typeInSet(0, allPtrs), typeOfExtendedScalars(1, IsExtendedInts)))
+ .scalarize(0);
getActionDefinitionsBuilder(G_PTRTOINT)
.legalForCartesianProduct(allIntScalars, allPtrs)
.legalIf(
- all(typeOfExtendedScalars(0, IsExtendedInts), typeInSet(1, allPtrs)));
+ all(typeOfExtendedScalars(0, IsExtendedInts), typeInSet(1, allPtrs)))
+ .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 a2eff509bdf4d..4673a647a63bc 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 aead1ce735c49..44d1bc3464e91 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
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/184817
More information about the llvm-commits
mailing list