[llvm] [SPIR-V] Add lowering of ptrtoaddr (PR #184577)

Dmitry Sidorov via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 4 01:51:09 PST 2026


https://github.com/MrSidims created https://github.com/llvm/llvm-project/pull/184577

The patch handles 2 scenarios:
1. In case if the following pattern present in LLVM IR %a = ptrtoaddr
%b = ptrtoaddr
%diff = sub %a, %b

it's being replaced with a newly added spv_ptrdiff intrinsic to be lowered as OpPtrDiff if allowed SPIR-V version > 1.4.

2. If the pattern is not recognized (for example there is a standalone ptrtoaddr instruction in the module) of it allowed
SPIR-V version is < 1.4, then ptrtoaddr is being lowered to OpConvertPtrToU. It's not an ideal solution, but at least for
Physical addressing model it should a correct replacement. 

>From c1684207775de5700593f26b447317facffcdec4 Mon Sep 17 00:00:00 2001
From: Dmitry Sidorov <Dmitry.Sidorov at amd.com>
Date: Tue, 3 Mar 2026 22:11:47 +0100
Subject: [PATCH] [SPIR-V] Add lowering of ptrtoaddr

The patch handles 2 scenarios:
1. In case if the following pattern present in LLVM IR
%a = ptrtoaddr
%b = ptrtoaddr
%diff = sub %a, %b

it's being replaced with a newly added spv_ptrdiff intrinsic to be
lowered as OpPtrDiff if allowed SPIR-V version > 1.4.

2. If the pattern is not recognized (for example there is a standalone
ptrtoaddr instruction in the module) of it allowed SPIR-V version is < 1.4,
then ptrtoaddr is being lowered to OpConvertPtrToU. It's not an ideal
solution.
---
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |  1 +
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 54 +++++++++++++++++++
 .../Target/SPIRV/SPIRVInstructionSelector.cpp |  6 +++
 llvm/test/CodeGen/SPIRV/ptrtoaddr-diff.ll     | 28 ++++++++++
 llvm/test/CodeGen/SPIRV/ptrtoaddr.ll          | 11 ++++
 5 files changed, 100 insertions(+)
 create mode 100644 llvm/test/CodeGen/SPIRV/ptrtoaddr-diff.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/ptrtoaddr.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 9819f881b5c30..6d50fa853dafc 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -32,6 +32,7 @@ let TargetPrefix = "spv" in {
   def int_spv_insertelt : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_any_ty, llvm_anyint_ty]>;
   def int_spv_const_composite : Intrinsic<[llvm_any_ty], [llvm_vararg_ty]>;
   def int_spv_bitcast : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
+  def int_spv_ptrdiff : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_anyptr_ty, LLVMMatchType<1>], [IntrNoMem]>;
   def int_spv_ptrcast : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_metadata_ty, llvm_i32_ty], [ImmArg<ArgIndex<2>>]>;
   def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
   def int_spv_loop_merge : Intrinsic<[], [llvm_vararg_ty]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 1c8774c59f065..2eb070c5c6100 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -146,6 +146,7 @@ class SPIRVEmitIntrinsics
 
   void preprocessCompositeConstants(IRBuilder<> &B);
   void preprocessUndefs(IRBuilder<> &B);
+  void preprocessPtrToAddrDiff(Function &F, IRBuilder<> &B);
 
   Type *reconstructType(Value *Op, bool UnknownElemTypeI8,
                         bool IsPostprocessing);
@@ -268,6 +269,7 @@ class SPIRVEmitIntrinsics
   Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
   Instruction *visitUnreachableInst(UnreachableInst &I);
   Instruction *visitCallInst(CallInst &I);
+  Instruction *visitPtrToAddrInst(PtrToAddrInst &I);
 
   StringRef getPassName() const override { return "SPIRV emit intrinsics"; }
 
@@ -1725,6 +1727,57 @@ Instruction *SPIRVEmitIntrinsics::visitBitCastInst(BitCastInst &I) {
   return NewI;
 }
 
+Instruction *SPIRVEmitIntrinsics::visitPtrToAddrInst(PtrToAddrInst &I) {
+  // If ptrtoaddr instruction wasn't handled previously during
+  // sub(ptrtoaddr, ptrtoaddr) -> OpPtrDiff pattern lowering, then
+  // replace it with PtrToInt.
+  auto *PtrToInt =
+      CastInst::Create(Instruction::PtrToInt, I.getOperand(0), I.getType());
+  PtrToInt->insertBefore(I.getIterator());
+  replaceAllUsesWith(&I, PtrToInt);
+  I.eraseFromParent();
+  return PtrToInt;
+}
+
+// Recognize sub(ptrtoaddr(A), ptrtoaddr(B)) patterns and replace with
+// spv_ptrdiff intrinsic.
+void SPIRVEmitIntrinsics::preprocessPtrToAddrDiff(Function &F,
+                                                  IRBuilder<> &B) {
+  // OpPtrDiff was added in SPIR-V 1.4, check for it.
+  const SPIRVSubtarget *ST = TM->getSubtargetImpl(F);
+  if (!ST->isAtLeastSPIRVVer(VersionTuple(1, 4)))
+    return;
+
+  SmallVector<BinaryOperator *> SubsToReplace;
+  for (auto &I : instructions(F)) {
+    auto *Sub = dyn_cast<BinaryOperator>(&I);
+    if (!Sub || Sub->getOpcode() != Instruction::Sub)
+      continue;
+    if (isa<PtrToAddrInst>(Sub->getOperand(0)) &&
+        isa<PtrToAddrInst>(Sub->getOperand(1)))
+      SubsToReplace.push_back(Sub);
+  }
+
+  for (auto *Sub : SubsToReplace) {
+    auto *PtrToAddrA = cast<PtrToAddrInst>(Sub->getOperand(0));
+    auto *PtrToAddrB = cast<PtrToAddrInst>(Sub->getOperand(1));
+    Value *Ptr1 = PtrToAddrA->getOperand(0);
+    Value *Ptr2 = PtrToAddrB->getOperand(0);
+
+    B.SetInsertPoint(Sub);
+    auto *PtrDiff = B.CreateIntrinsic(
+        Intrinsic::spv_ptrdiff, {Sub->getType(), Ptr1->getType()},
+        {Ptr1, Ptr2});
+    Sub->replaceAllUsesWith(PtrDiff);
+    Sub->eraseFromParent();
+
+    if (PtrToAddrA->use_empty())
+      PtrToAddrA->eraseFromParent();
+    if (PtrToAddrB->use_empty())
+      PtrToAddrB->eraseFromParent();
+  }
+}
+
 void SPIRVEmitIntrinsics::insertAssignPtrTypeTargetExt(
     TargetExtType *AssignedType, Value *V, IRBuilder<> &B) {
   Type *VTy = V->getType();
@@ -3011,6 +3064,7 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) {
 
   preprocessUndefs(B);
   preprocessCompositeConstants(B);
+  preprocessPtrToAddrDiff(Func, B);
   SmallVector<Instruction *> Worklist(
       llvm::make_pointer_range(instructions(Func)));
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 344628f258229..e5cd4e044ebcd 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -609,6 +609,7 @@ static bool intrinsicHasSideEffects(Intrinsic::ID ID) {
   case Intrinsic::spv_num_subgroups:
   case Intrinsic::spv_num_workgroups:
   case Intrinsic::spv_ptrcast:
+  case Intrinsic::spv_ptrdiff:
   case Intrinsic::spv_radians:
   case Intrinsic::spv_reflect:
   case Intrinsic::spv_refract:
@@ -3798,6 +3799,11 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
       report_fatal_error("incompatible result and operand types in a bitcast");
     return selectOpWithSrcs(ResVReg, ResType, I, {OpReg}, SPIRV::OpBitcast);
   }
+  case Intrinsic::spv_ptrdiff:
+    return selectOpWithSrcs(ResVReg, ResType, I,
+                            {I.getOperand(2).getReg(),
+                             I.getOperand(3).getReg()},
+                            SPIRV::OpPtrDiff);
   case Intrinsic::spv_unref_global:
   case Intrinsic::spv_init_global: {
     MachineInstr *MI = MRI->getVRegDef(I.getOperand(1).getReg());
diff --git a/llvm/test/CodeGen/SPIRV/ptrtoaddr-diff.ll b/llvm/test/CodeGen/SPIRV/ptrtoaddr-diff.ll
new file mode 100644
index 0000000000000..68410245a98fd
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrtoaddr-diff.ll
@@ -0,0 +1,28 @@
+; Test sub(ptrtoaddr, ptrtoaddr) -> OpPtrDiff for SPIR-V 1.4+
+; RUN: llc -O0 -mtriple=spirv64v1.4-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV-14
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64v1.4-unknown-unknown %s -o - -filetype=obj | spirv-val --target-env spv1.4 %}
+
+; Test fallback to OpConvertPtrToU + ISub for SPIR-V < 1.4
+; RUN: llc -O0 -mtriple=spirv64v1.3-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV-13
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64v1.3-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-SPIRV-14: %[[#Arg1:]] = OpFunctionParameter %[[#]]
+; CHECK-SPIRV-14: %[[#Arg2:]] = OpFunctionParameter %[[#]]
+; CHECK-SPIRV-14: %[[#]] = OpPtrDiff %[[#]] %[[#Arg1]] %[[#Arg2]]
+; CHECK-SPIRV-14-NOT: OpConvertPtrToU
+
+; CHECK-SPIRV-13: %[[#Arg1:]] = OpFunctionParameter %[[#]]
+; CHECK-SPIRV-13: %[[#Arg2:]] = OpFunctionParameter %[[#]]
+; CHECK-SPIRV-13: %[[#Conv1:]] = OpConvertPtrToU %[[#]] %[[#Arg1]]
+; CHECK-SPIRV-13: %[[#Conv2:]] = OpConvertPtrToU %[[#]] %[[#Arg2]]
+; CHECK-SPIRV-13: %[[#]] = OpISub %[[#]] %[[#Conv1]] %[[#Conv2]]
+; CHECK-SPIRV-13-NOT: OpPtrDiff
+
+define spir_kernel void @test_ptrdiff(ptr addrspace(1) %p1, ptr addrspace(1) %p2, ptr addrspace(1) %res) {
+entry:
+  %a = ptrtoaddr ptr addrspace(1) %p1 to i64
+  %b = ptrtoaddr ptr addrspace(1) %p2 to i64
+  %diff = sub i64 %a, %b
+  store i64 %diff, ptr addrspace(1) %res
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/ptrtoaddr.ll b/llvm/test/CodeGen/SPIRV/ptrtoaddr.ll
new file mode 100644
index 0000000000000..11626649f5bb8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrtoaddr.ll
@@ -0,0 +1,11 @@
+; RUN: llc -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: OpConvertPtrToU
+
+define spir_kernel void @test_ptrtoaddr(ptr addrspace(1) %p, ptr addrspace(1) %res) {
+entry:
+  %addr = ptrtoaddr ptr addrspace(1) %p to i64
+  store i64 %addr, ptr addrspace(1) %res
+  ret void
+}



More information about the llvm-commits mailing list