[llvm] cefb937 - [SPIRV] Add support for G_PTRMASK (#201450)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 08:04:04 PDT 2026


Author: Nick Sarnie
Date: 2026-06-09T15:03:59Z
New Revision: cefb93799312e772f2d71529075bc4ab1b94b845

URL: https://github.com/llvm/llvm-project/commit/cefb93799312e772f2d71529075bc4ab1b94b845
DIFF: https://github.com/llvm/llvm-project/commit/cefb93799312e772f2d71529075bc4ab1b94b845.diff

LOG: [SPIRV] Add support for G_PTRMASK (#201450)

This instruction is generated by the
[llvm.ptrmask](https://llvm.org/docs/LangRef.html#llvm-ptrmask-intrinsic)
intrinsic, which is used for Clang builtins like
[__builtin_align_up](https://clang.llvm.org/docs/LanguageExtensions.html#alignment-builtins)
which is used in `libc`.

We are working on building `libc` for SPIR-V, so we hit this problem.

Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply at anthropic.com>

---------

Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
Co-authored-by: Claude Sonnet 4.5 <noreply at anthropic.com>

Added: 
    llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
    llvm/test/CodeGen/SPIRV/ptrmask-vec.ll
    llvm/test/CodeGen/SPIRV/ptrmask32.ll
    llvm/test/CodeGen/SPIRV/ptrmask64-32.ll
    llvm/test/CodeGen/SPIRV/ptrmask64.ll

Modified: 
    llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
    llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 3b0242d970359..d9bbf6c9371df 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;
 
@@ -1264,6 +1267,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());
@@ -2694,6 +2699,60 @@ 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 {
+  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();
+
+  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 62827a2151a64..6c9b7eb2ef37e 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-logical.ll b/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
new file mode 100644
index 0000000000000..4a9bbb2e97a76
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/ptrmask-logical.ll
@@ -0,0 +1,16 @@
+; 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 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" }

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)


        


More information about the llvm-commits mailing list