[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