[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