[llvm] [SPIRV] Add support for G_PTRMASK (PR #201450)
Nick Sarnie via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 9 07:15:03 PDT 2026
https://github.com/sarnex updated https://github.com/llvm/llvm-project/pull/201450
>From 918b970cdf420e2574817919ca86ffc651342e20 Mon Sep 17 00:00:00 2001
From: Nick Sarnie <nick.sarnie at intel.com>
Date: Wed, 3 Jun 2026 13:37:38 -0700
Subject: [PATCH 1/3] [SPIRV] Add support for G_PTRMASK
Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply at anthropic.com>
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 56 +++++++++++++++++++
llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 12 ++++
llvm/test/CodeGen/SPIRV/ptrmask-vec.ll | 31 ++++++++++
llvm/test/CodeGen/SPIRV/ptrmask32.ll | 24 ++++++++
llvm/test/CodeGen/SPIRV/ptrmask64-32.ll | 25 +++++++++
llvm/test/CodeGen/SPIRV/ptrmask64.ll | 24 ++++++++
6 files changed, 172 insertions(+)
create mode 100644 llvm/test/CodeGen/SPIRV/ptrmask-vec.ll
create mode 100644 llvm/test/CodeGen/SPIRV/ptrmask32.ll
create mode 100644 llvm/test/CodeGen/SPIRV/ptrmask64-32.ll
create mode 100644 llvm/test/CodeGen/SPIRV/ptrmask64.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index c3f21fe025bd5..0e1867591e023 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -185,6 +185,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
bool selectAddrSpaceCast(Register ResVReg, SPIRVTypeInst ResType,
MachineInstr &I) const;
+ bool selectPtrMask(Register ResVReg, SPIRVTypeInst ResType,
+ MachineInstr &I) const;
+
bool selectAnyOrAll(Register ResVReg, SPIRVTypeInst ResType, MachineInstr &I,
unsigned OpType) const;
@@ -1259,6 +1262,8 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
return selectBitcast(ResVReg, ResType, I);
case TargetOpcode::G_ADDRSPACE_CAST:
return selectAddrSpaceCast(ResVReg, ResType, I);
+ case TargetOpcode::G_PTRMASK:
+ return selectPtrMask(ResVReg, ResType, I);
case TargetOpcode::G_PTR_ADD: {
// Currently, we get G_PTR_ADD only applied to global variables.
assert(I.getOperand(1).isReg() && I.getOperand(2).isReg());
@@ -2682,6 +2687,57 @@ bool SPIRVInstructionSelector::selectAddrSpaceCast(Register ResVReg,
return false;
}
+// G_PTRMASK - Apply a bitmask to a pointer value.
+// Result = Ptr & Mask
+// We need to convert the pointer to an integer, perform the AND operation,
+// and convert back to a pointer.
+bool SPIRVInstructionSelector::selectPtrMask(Register ResVReg,
+ SPIRVTypeInst ResType,
+ MachineInstr &I) const {
+ MachineBasicBlock &BB = *I.getParent();
+ MachineFunction &MF = *BB.getParent();
+ const DebugLoc &DL = I.getDebugLoc();
+
+ Register PtrReg = I.getOperand(1).getReg();
+ Register MaskReg = I.getOperand(2).getReg();
+
+ SPIRVTypeInst MaskType = GR.getSPIRVTypeForVReg(MaskReg);
+
+ // Convert pointer to integer.
+ Register PtrAsInt = MRI->createVirtualRegister(GR.getRegClass(MaskType));
+ GR.assignSPIRVTypeToVReg(MaskType, PtrAsInt, MF);
+
+ BuildMI(BB, I, DL, TII.get(SPIRV::OpConvertPtrToU))
+ .addDef(PtrAsInt)
+ .addUse(GR.getSPIRVTypeID(MaskType))
+ .addUse(PtrReg)
+ .constrainAllUses(TII, TRI, RBI);
+
+ // Perform bitwise AND.
+ Register MaskedInt = MRI->createVirtualRegister(GR.getRegClass(MaskType));
+ GR.assignSPIRVTypeToVReg(MaskType, MaskedInt, MF);
+
+ unsigned AndOpcode = GR.getScalarOrVectorComponentCount(MaskType) > 1
+ ? SPIRV::OpBitwiseAndV
+ : SPIRV::OpBitwiseAndS;
+
+ BuildMI(BB, I, DL, TII.get(AndOpcode))
+ .addDef(MaskedInt)
+ .addUse(GR.getSPIRVTypeID(MaskType))
+ .addUse(PtrAsInt)
+ .addUse(MaskReg)
+ .constrainAllUses(TII, TRI, RBI);
+
+ // Convert integer back to pointer.
+ BuildMI(BB, I, DL, TII.get(SPIRV::OpConvertUToPtr))
+ .addDef(ResVReg)
+ .addUse(GR.getSPIRVTypeID(ResType))
+ .addUse(MaskedInt)
+ .constrainAllUses(TII, TRI, RBI);
+
+ return true;
+}
+
static unsigned getFCmpOpcode(unsigned PredNum) {
auto Pred = static_cast<CmpInst::Predicate>(PredNum);
switch (Pred) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 099243acc8819..31bf0c1ff9c62 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -402,6 +402,18 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
.legalIf(
all(typeInSet(0, allPtrs), typeOfExtendedScalars(1, IsExtendedInts)));
+ getActionDefinitionsBuilder(G_PTRMASK)
+ .legalForCartesianProduct(allPtrs, allIntScalars)
+ .legalIf(
+ all(typeInSet(0, allPtrs), typeOfExtendedScalars(1, IsExtendedInts)))
+ .legalIf([](const LegalityQuery &Query) {
+ const LLT PtrTy = Query.Types[0];
+ const LLT MaskTy = Query.Types[1];
+ return PtrTy.isPointerVector() && MaskTy.isVector() &&
+ !MaskTy.isPointer() &&
+ PtrTy.getNumElements() == MaskTy.getNumElements();
+ });
+
// ST.canDirectlyComparePointers() for pointer args is supported in
// legalizeCustom().
getActionDefinitionsBuilder(G_ICMP)
diff --git a/llvm/test/CodeGen/SPIRV/ptrmask-vec.ll b/llvm/test/CodeGen/SPIRV/ptrmask-vec.ll
new file mode 100644
index 0000000000000..37abf025f4cbb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrmask-vec.ll
@@ -0,0 +1,31 @@
+; RUN: llc -O0 --spirv-ext=+SPV_INTEL_masked_gather_scatter -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 --spirv-ext=+SPV_INTEL_masked_gather_scatter -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that G_PTRMASK works with vector of pointers.
+; This requires the SPV_INTEL_masked_gather_scatter extension.
+
+; CHECK-DAG: OpCapability MaskedGatherScatterINTEL
+; CHECK-DAG: OpExtension "SPV_INTEL_masked_gather_scatter"
+; CHECK-DAG: %[[#INT8_TY:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#PTR_TY:]] = OpTypePointer CrossWorkgroup %[[#INT8_TY]]
+; CHECK-DAG: %[[#VEC_PTR_TY:]] = OpTypeVector %[[#PTR_TY]] 2
+; CHECK-DAG: %[[#INT64_TY:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#VEC_INT64_TY:]] = OpTypeVector %[[#INT64_TY]] 2
+; CHECK: %[[#PTR_VEC_PARAM:]] = OpFunctionParameter %[[#VEC_PTR_TY]]
+; CHECK: %[[#MASK_VEC_PARAM:]] = OpFunctionParameter %[[#VEC_INT64_TY]]
+; CHECK: %[[#OUT_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#PTR_AS_INT:]] = OpConvertPtrToU %[[#VEC_INT64_TY]] %[[#PTR_VEC_PARAM]]
+; CHECK: %[[#MASKED_INT:]] = OpBitwiseAnd %[[#VEC_INT64_TY]] %[[#PTR_AS_INT]] %[[#MASK_VEC_PARAM]]
+; CHECK: %[[#MASKED_PTR:]] = OpConvertUToPtr %[[#VEC_PTR_TY]] %[[#MASKED_INT]]
+; CHECK: %[[#ELEM0:]] = OpCompositeExtract %[[#PTR_TY]] %[[#MASKED_PTR]] 0
+; CHECK: OpStore %[[#OUT_PARAM]] %[[#ELEM0]] Aligned 8
+
+define spir_kernel void @test_ptrmask_vec(<2 x ptr addrspace(1)> %ptr_vec, <2 x i64> %mask_vec, ptr addrspace(1) %out) {
+entry:
+ %masked_ptr_vec = call <2 x ptr addrspace(1)> @llvm.ptrmask.v2p1.v2i64(<2 x ptr addrspace(1)> %ptr_vec, <2 x i64> %mask_vec)
+ %elem0 = extractelement <2 x ptr addrspace(1)> %masked_ptr_vec, i32 0
+ store ptr addrspace(1) %elem0, ptr addrspace(1) %out, align 8
+ ret void
+}
+
+declare <2 x ptr addrspace(1)> @llvm.ptrmask.v2p1.v2i64(<2 x ptr addrspace(1)>, <2 x i64>)
diff --git a/llvm/test/CodeGen/SPIRV/ptrmask32.ll b/llvm/test/CodeGen/SPIRV/ptrmask32.ll
new file mode 100644
index 0000000000000..c2a830502d7ed
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrmask32.ll
@@ -0,0 +1,24 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Test that G_PTRMASK is correctly lowered to SPIR-V for 32-bit targets.
+; Result = Ptr & Mask
+; This should generate: OpConvertPtrToU -> OpBitwiseAnd -> OpConvertUToPtr
+
+; CHECK-LABEL: Begin function test_ptrmask_i32
+; CHECK: %[[#PTR_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#MASK_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#OUT_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#PTR_AS_INT:]] = OpConvertPtrToU %[[#]] %[[#PTR_PARAM]]
+; CHECK-NEXT: %[[#MASKED_INT:]] = OpBitwiseAnd %[[#]] %[[#PTR_AS_INT]] %[[#MASK_PARAM]]
+; CHECK-NEXT: %[[#MASKED_PTR:]] = OpConvertUToPtr %[[#]] %[[#MASKED_INT]]
+; CHECK-NEXT: OpStore %[[#OUT_PARAM]] %[[#MASKED_PTR]] Aligned 4
+
+define spir_kernel void @test_ptrmask_i32(ptr addrspace(1) %ptr, i32 %mask, ptr addrspace(1) %out) {
+entry:
+ %masked_ptr = call ptr addrspace(1) @llvm.ptrmask.p1.i32(ptr addrspace(1) %ptr, i32 %mask)
+ store ptr addrspace(1) %masked_ptr, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+declare ptr addrspace(1) @llvm.ptrmask.p1.i32(ptr addrspace(1), i32)
diff --git a/llvm/test/CodeGen/SPIRV/ptrmask64-32.ll b/llvm/test/CodeGen/SPIRV/ptrmask64-32.ll
new file mode 100644
index 0000000000000..87bf59a180a6f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrmask64-32.ll
@@ -0,0 +1,25 @@
+; 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 %}
+
+; Test G_PTRMASK with 32-bit mask on 64-bit SPIR-V target.
+; The 32-bit mask should be zero-extended to 64-bit before the AND operation.
+
+; CHECK-LABEL: Begin function test_ptrmask_i32_mask
+; CHECK: %[[#PTR_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#MASK_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#OUT_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#MASK_64:]] = OpUConvert %[[#]] %[[#MASK_PARAM]]
+; CHECK-NEXT: %[[#PTR_AS_INT:]] = OpConvertPtrToU %[[#]] %[[#PTR_PARAM]]
+; CHECK-NEXT: %[[#MASKED_INT:]] = OpBitwiseAnd %[[#]] %[[#PTR_AS_INT]] %[[#MASK_64]]
+; CHECK-NEXT: %[[#MASKED_PTR:]] = OpConvertUToPtr %[[#]] %[[#MASKED_INT]]
+; CHECK-NEXT: OpStore %[[#OUT_PARAM]] %[[#MASKED_PTR]] Aligned 8
+
+define spir_kernel void @test_ptrmask_i32_mask(ptr addrspace(1) %ptr, i32 %mask, ptr addrspace(1) %out) {
+entry:
+ %mask_64 = zext i32 %mask to i64
+ %masked_ptr = call ptr addrspace(1) @llvm.ptrmask.p1.i64(ptr addrspace(1) %ptr, i64 %mask_64)
+ store ptr addrspace(1) %masked_ptr, ptr addrspace(1) %out, align 8
+ ret void
+}
+
+declare ptr addrspace(1) @llvm.ptrmask.p1.i64(ptr addrspace(1), i64)
diff --git a/llvm/test/CodeGen/SPIRV/ptrmask64.ll b/llvm/test/CodeGen/SPIRV/ptrmask64.ll
new file mode 100644
index 0000000000000..77fd41de768a7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrmask64.ll
@@ -0,0 +1,24 @@
+; 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 %}
+
+; Test that G_PTRMASK is correctly lowered to SPIR-V for 64-bit targets.
+; Result = Ptr & Mask
+; This should generate: OpConvertPtrToU -> OpBitwiseAnd -> OpConvertUToPtr
+
+; CHECK-LABEL: Begin function test_ptrmask_i64
+; CHECK: %[[#PTR_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#MASK_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#OUT_PARAM:]] = OpFunctionParameter
+; CHECK: %[[#PTR_AS_INT:]] = OpConvertPtrToU %[[#]] %[[#PTR_PARAM]]
+; CHECK-NEXT: %[[#MASKED_INT:]] = OpBitwiseAnd %[[#]] %[[#PTR_AS_INT]] %[[#MASK_PARAM]]
+; CHECK-NEXT: %[[#MASKED_PTR:]] = OpConvertUToPtr %[[#]] %[[#MASKED_INT]]
+; CHECK-NEXT: OpStore %[[#OUT_PARAM]] %[[#MASKED_PTR]] Aligned 8
+
+define spir_kernel void @test_ptrmask_i64(ptr addrspace(1) %ptr, i64 %mask, ptr addrspace(1) %out) {
+entry:
+ %masked_ptr = call ptr addrspace(1) @llvm.ptrmask.p1.i64(ptr addrspace(1) %ptr, i64 %mask)
+ store ptr addrspace(1) %masked_ptr, ptr addrspace(1) %out, align 8
+ ret void
+}
+
+declare ptr addrspace(1) @llvm.ptrmask.p1.i64(ptr addrspace(1), i64)
>From c0c4f879f97d9e02066ca342ff11dc7e8558ffa8 Mon Sep 17 00:00:00 2001
From: Nick Sarnie <nick.sarnie at intel.com>
Date: Mon, 8 Jun 2026 10:16:54 -0700
Subject: [PATCH 2/3] fix logical spirv
Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
---
.../Target/SPIRV/SPIRVInstructionSelector.cpp | 3 +++
llvm/test/CodeGen/SPIRV/ptrmask-logical.ll | 16 ++++++++++++++++
2 files changed, 19 insertions(+)
create mode 100644 llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 0e1867591e023..f5267c7b34a74 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -2694,6 +2694,9 @@ bool SPIRVInstructionSelector::selectAddrSpaceCast(Register ResVReg,
bool SPIRVInstructionSelector::selectPtrMask(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I) const {
+ if (STI.isLogicalSPIRV())
+ return diagnoseUnsupported(
+ I, "G_PTRMASK is not supported with logical SPIR-V");
MachineBasicBlock &BB = *I.getParent();
MachineFunction &MF = *BB.getParent();
const DebugLoc &DL = I.getDebugLoc();
diff --git a/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll b/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
new file mode 100644
index 0000000000000..ce9e9856a02fb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
@@ -0,0 +1,16 @@
+; RUN: not llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 2>&1 | FileCheck %s
+
+; Test that G_PTRMASK is errors for logical SPIR-V.
+
+; CHECK: G_PTRMASK is not supported with logical SPIR-V
+
+define spir_kernel void @test_ptrmask_i64(ptr addrspace(1) %ptr, i64 %mask, ptr addrspace(1) %out) #1 {
+entry:
+ %masked_ptr = call ptr addrspace(1) @llvm.ptrmask.p1.i64(ptr addrspace(1) %ptr, i64 %mask)
+ store ptr addrspace(1) %masked_ptr, ptr addrspace(1) %out, align 8
+ ret void
+}
+
+declare ptr addrspace(1) @llvm.ptrmask.p1.i64(ptr addrspace(1), i64)
+
+attributes #1 = { "hlsl.numthreads"="4,8,16" "hlsl.shader"="compute" }
>From b60c6e405f98c60618e7ae7772c926a6a58ca56d Mon Sep 17 00:00:00 2001
From: Nick Sarnie <nick.sarnie at intel.com>
Date: Tue, 9 Jun 2026 07:14:43 -0700
Subject: [PATCH 3/3] address feedback
Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
---
llvm/test/CodeGen/SPIRV/ptrmask-logical.ll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll b/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
index ce9e9856a02fb..4a9bbb2e97a76 100644
--- a/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
+++ b/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
@@ -1,10 +1,10 @@
-; RUN: not llc -O0 -mtriple=spirv-unknown-vulkan %s -o - 2>&1 | FileCheck %s
+; RUN: not llc -O0 -mtriple=spirv-unknown-vulkan %s -o /dev/null 2>&1 | FileCheck %s
; Test that G_PTRMASK is errors for logical SPIR-V.
; CHECK: G_PTRMASK is not supported with logical SPIR-V
-define spir_kernel void @test_ptrmask_i64(ptr addrspace(1) %ptr, i64 %mask, ptr addrspace(1) %out) #1 {
+define void @test_ptrmask_i64(ptr addrspace(1) %ptr, i64 %mask, ptr addrspace(1) %out) #1 {
entry:
%masked_ptr = call ptr addrspace(1) @llvm.ptrmask.p1.i64(ptr addrspace(1) %ptr, i64 %mask)
store ptr addrspace(1) %masked_ptr, ptr addrspace(1) %out, align 8
More information about the llvm-commits
mailing list