[llvm] [SPIRV] Let atomic load load pointers (PR #202823)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 10 14:52:39 PDT 2026


https://github.com/fineg74 updated https://github.com/llvm/llvm-project/pull/202823

>From 0248f1b829cdaa21de33da2bf9c25aac7fd6305c Mon Sep 17 00:00:00 2001
From: "Fine, Gregory" <gregory.fine at intel.com>
Date: Tue, 9 Jun 2026 17:45:04 -0700
Subject: [PATCH 1/5] Let atomic load load pointers

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 72 ++++++++++++++++---
 .../atomic-load-store-unsupported.ll          | 11 ++-
 .../SPIRV/transcoding/load-atomic-ptr.ll      | 31 ++++++++
 3 files changed, 102 insertions(+), 12 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 34209bc44ba2a..19502bca35241 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1963,10 +1963,10 @@ bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
   unsigned OpOffset = isa<GIntrinsic>(I) ? 1 : 0;
   Register Ptr = I.getOperand(1 + OpOffset).getReg();
 
-  if (!ResType.isTypeIntOrFloat())
-    return diagnoseUnsupported(I,
-                               "Lowering to SPIR-V of atomic load is only "
-                               "allowed for integer or floating point types");
+  if (!ResType.isTypeIntOrFloat() && !ResType.isTypePtr())
+    return diagnoseUnsupported(
+        I, "Lowering to SPIR-V of atomic load is only "
+           "allowed for integer, floating point or pointer types");
 
   assert(I.getNumMemOperands());
   const MachineMemOperand &MemOp = **I.memoperands_begin();
@@ -1985,13 +1985,63 @@ bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
   Register MemSemReg = buildI32Constant(MemSem | StorageClass, I);
 
   MachineIRBuilder MIRBuilder(I);
-  auto AtomicLoad = MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
-                        .addDef(ResVReg)
-                        .addUse(GR.getSPIRVTypeID(ResType))
-                        .addUse(Ptr)
-                        .addUse(ScopeReg)
-                        .addUse(MemSemReg);
-  AtomicLoad.constrainAllUses(TII, TRI, RBI);
+
+  if (ResType.isTypePtr()) {
+    if (!STI.isPhysicalSPIRV())
+      return diagnoseUnsupported(
+          I, "Lowering to SPIR-V of atomic load is only "
+             "allowed for pointer types for physical addressing model");
+    // If data to load is a pointer type we bitcast the Ptr parameter to pointer
+    // to an integer type of the same size as the pointer size and then generate
+    // OpAtomicLoad the return value of that OpAtomicLoad is an integet that is
+    // converted back to a pointer type using OpConvertUToPtr.
+
+    unsigned PtrSize = GR.getPointerSize();
+    SPIRVTypeInst PtrAsIntSpirvType =
+        GR.getOrCreateSPIRVIntegerType(PtrSize, MIRBuilder);
+    Register PtrToUVal =
+        MRI->createGenericVirtualRegister(LLT::scalar(PtrSize));
+    MRI->setRegClass(PtrToUVal, MRI->getRegClassOrNull(ResVReg));
+    GR.assignSPIRVTypeToVReg(PtrAsIntSpirvType, PtrToUVal, MIRBuilder.getMF());
+
+    Register PtrCastedToMatchValReg =
+        MRI->createGenericVirtualRegister(LLT::scalar(PtrSize));
+    MRI->setRegClass(PtrCastedToMatchValReg, MRI->getRegClassOrNull(Ptr));
+    SPIRVTypeInst PtrType = GR.getOrCreateSPIRVPointerType(
+        PtrAsIntSpirvType, MIRBuilder,
+        addressSpaceToStorageClass(MemOp.getAddrSpace(), STI));
+    GR.assignSPIRVTypeToVReg(PtrType, PtrCastedToMatchValReg,
+                             MIRBuilder.getMF());
+
+    MIRBuilder.buildInstr(SPIRV::OpBitcast)
+        .addDef(PtrCastedToMatchValReg)
+        .addUse(GR.getSPIRVTypeID(PtrType))
+        .addUse(Ptr)
+        .constrainAllUses(TII, TRI, RBI);
+    ResType = PtrType;
+    Ptr = PtrCastedToMatchValReg;
+
+    MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
+        .addDef(PtrToUVal)
+        .addUse(GR.getSPIRVTypeID(PtrType))
+        .addUse(PtrCastedToMatchValReg)
+        .addUse(ScopeReg)
+        .addUse(MemSemReg)
+        .constrainAllUses(TII, TRI, RBI);
+    MIRBuilder.buildInstr(SPIRV::OpConvertUToPtr)
+        .addDef(ResVReg)
+        .addUse(GR.getSPIRVTypeID(ResType))
+        .addUse(PtrToUVal)
+        .constrainAllUses(TII, TRI, RBI);
+  } else {
+    auto AtomicLoad = MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
+                          .addDef(ResVReg)
+                          .addUse(GR.getSPIRVTypeID(ResType))
+                          .addUse(Ptr)
+                          .addUse(ScopeReg)
+                          .addUse(MemSemReg);
+    AtomicLoad.constrainAllUses(TII, TRI, RBI);
+  }
   return true;
 }
 
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
index b5ed65c829f7b..37c68ce3108ec 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
@@ -8,9 +8,12 @@
 
 ; RUN: not llc -O0 -mtriple=spirv %t/store-ptr-vulkan.ll -o /dev/null 2>&1 | FileCheck --check-prefix=FAIL-STORE-PTR %s
 
+; RUN: not llc -O0 -mtriple=spirv %t/load-ptr-vulkan.ll -o /dev/null 2>&1 | FileCheck --check-prefix=FAIL-LOAD-PTR %s
+
 ; FAIL-LOAD-VEC: error:{{.*}}atomic load is only allowed for integer or floating point types
 ; FAIL-STORE-VEC: error:{{.*}}atomic store is only allowed for integer or floating point types
 ; FAIL-STORE-PTR: error:{{.*}}atomic store is only allowed for pointer types for physical addressing model
+; FAIL-LOAD-PTR: error:{{.*}}atomic load is only allowed for pointer types for physical addressing model
 
 ;--- load-vector.ll
 define <2 x i32> @load_vector_acquire(ptr addrspace(1) %ptr) {
@@ -25,7 +28,13 @@ define void @store_vector_release(ptr addrspace(1) %ptr, <2 x i32> %val) {
 }
 
 ;--- store-ptr-vulkan.ll
-define void @store_vector_release(ptr addrspace(1) %ptr, ptr addrspace(1) %val) {
+define void @store_ptr_release(ptr addrspace(1) %ptr, ptr addrspace(1) %val) {
   store atomic ptr addrspace(1) %val, ptr addrspace(1) %ptr monotonic, align 8
   ret void
 }
+
+;--- load-ptr-vulkan.ll
+define ptr addrspace(1) @load_ptr_release(ptr addrspace(1) %ptr) {
+  %val = load atomic ptr addrspace(1), ptr addrspace(1) %ptr monotonic, align 8
+  ret ptr addrspace(1) %val
+}
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
new file mode 100644
index 0000000000000..a17ee0023f2e0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
@@ -0,0 +1,31 @@
+; RUN: llc -O0 -mtriple=spirv32 %s -o - | FileCheck %s --check-prefixes=CHECK,SPIRV32
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-- %s -o - -filetype=obj | spirv-val %}
+
+; RUN: llc -O0 -mtriple=spirv64 %s -o - | FileCheck %s --check-prefixes=CHECK,SPIRV64
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-- %s -o - -filetype=obj | spirv-val %}
+
+
+; Check that 'load atomic' LLVM IR instructions are lowered correctly to
+; OpAtomicLoad when a pointer is passed as value.
+
+; SPIRV32-DAG: %[[#Int32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#Int8:]] = OpTypeInt 8 0
+; SPIRV32-DAG: %[[#Int32Ptr:]] = OpTypePointer Generic %[[#Int32]]
+; CHECK-DAG: %[[#Int8Ptr:]] = OpTypePointer Generic %[[#Int8]]
+; CHECK-DAG: %[[#PtrInt8Ptr:]] = OpTypePointer Generic %[[#Int8Ptr]]
+; SPIRV64-DAG: %[[#Int64:]] = OpTypeInt 64 0
+; SPIRV64-DAG: %[[#Int64Ptr:]] = OpTypePointer Generic %[[#Int64]]
+
+define ptr addrspace(4)  @load_ptr(ptr addrspace(4) %ptr) {
+; CHECK-LABEL: OpFunction %[[#]]
+; CHECK:       %[[#Ptr:]] = OpFunctionParameter %[[#PtrInt8Ptr]]
+; SPIRV32:       %[[#ConvertPtr:]] = OpBitcast %[[#Int32Ptr]] %[[#Ptr]]
+; SPIRV32:       %[[#Val:]] = OpAtomicLoad %[[#Int32Ptr]] %[[#ConvertPtr]] %[[#]] %[[#]]
+; SPIRV32:       %[[#ConvertVal:]] = OpConvertUToPtr %[[#Int32Ptr]] %[[#Val]]
+; SPIRV64:       %[[#ConvertPtr:]] = OpBitcast %[[#Int64Ptr]] %[[#Ptr]]
+; SPIRV64:       %[[#Val:]] = OpAtomicLoad %[[#Int64Ptr]] %[[#ConvertPtr]] %[[#]] %[[#]]
+; SPIRV64:       %[[#ConvertVal:]] = OpConvertUToPtr %[[#Int64Ptr]] %[[#Val]]
+; CHECK:       OpReturnValue %[[#ConvertVal]]
+  %val = load atomic ptr addrspace(4), ptr addrspace(4) %ptr monotonic, align 8
+  ret ptr addrspace(4) %val
+}

>From c04a5636a968ded2712de54df2974f718942e3ba Mon Sep 17 00:00:00 2001
From: "Fine, Gregory" <gregory.fine at intel.com>
Date: Tue, 9 Jun 2026 18:25:15 -0700
Subject: [PATCH 2/5] Fix a test break

---
 .../CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
index 37c68ce3108ec..67ec10e24c5e9 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
@@ -10,7 +10,7 @@
 
 ; RUN: not llc -O0 -mtriple=spirv %t/load-ptr-vulkan.ll -o /dev/null 2>&1 | FileCheck --check-prefix=FAIL-LOAD-PTR %s
 
-; FAIL-LOAD-VEC: error:{{.*}}atomic load is only allowed for integer or floating point types
+; FAIL-LOAD-VEC: error:{{.*}}atomic load is only allowed for integer, floating point or pointer types
 ; FAIL-STORE-VEC: error:{{.*}}atomic store is only allowed for integer or floating point types
 ; FAIL-STORE-PTR: error:{{.*}}atomic store is only allowed for pointer types for physical addressing model
 ; FAIL-LOAD-PTR: error:{{.*}}atomic load is only allowed for pointer types for physical addressing model

>From f9cfb5932d20ba017672bb7b99287469fe70e3c6 Mon Sep 17 00:00:00 2001
From: "Fine, Gregory" <gregory.fine at intel.com>
Date: Tue, 9 Jun 2026 19:18:54 -0700
Subject: [PATCH 3/5] Fix a test break

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp     | 4 +---
 llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll | 7 +++----
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 19502bca35241..c5e40d314838a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -2018,12 +2018,10 @@ bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
         .addUse(GR.getSPIRVTypeID(PtrType))
         .addUse(Ptr)
         .constrainAllUses(TII, TRI, RBI);
-    ResType = PtrType;
-    Ptr = PtrCastedToMatchValReg;
 
     MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
         .addDef(PtrToUVal)
-        .addUse(GR.getSPIRVTypeID(PtrType))
+        .addUse(GR.getSPIRVTypeID(PtrAsIntSpirvType))
         .addUse(PtrCastedToMatchValReg)
         .addUse(ScopeReg)
         .addUse(MemSemReg)
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
index a17ee0023f2e0..070dc7f1c4b7c 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
@@ -20,11 +20,10 @@ define ptr addrspace(4)  @load_ptr(ptr addrspace(4) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#Ptr:]] = OpFunctionParameter %[[#PtrInt8Ptr]]
 ; SPIRV32:       %[[#ConvertPtr:]] = OpBitcast %[[#Int32Ptr]] %[[#Ptr]]
-; SPIRV32:       %[[#Val:]] = OpAtomicLoad %[[#Int32Ptr]] %[[#ConvertPtr]] %[[#]] %[[#]]
-; SPIRV32:       %[[#ConvertVal:]] = OpConvertUToPtr %[[#Int32Ptr]] %[[#Val]]
+; SPIRV32:       %[[#Val:]] = OpAtomicLoad %[[#Int32]] %[[#ConvertPtr]] %[[#]] %[[#]]
 ; SPIRV64:       %[[#ConvertPtr:]] = OpBitcast %[[#Int64Ptr]] %[[#Ptr]]
-; SPIRV64:       %[[#Val:]] = OpAtomicLoad %[[#Int64Ptr]] %[[#ConvertPtr]] %[[#]] %[[#]]
-; SPIRV64:       %[[#ConvertVal:]] = OpConvertUToPtr %[[#Int64Ptr]] %[[#Val]]
+; SPIRV64:       %[[#Val:]] = OpAtomicLoad %[[#Int64]] %[[#ConvertPtr]] %[[#]] %[[#]]
+; CHECK:         %[[#ConvertVal:]] = OpConvertUToPtr %[[#Int8Ptr]] %[[#Val]]
 ; CHECK:       OpReturnValue %[[#ConvertVal]]
   %val = load atomic ptr addrspace(4), ptr addrspace(4) %ptr monotonic, align 8
   ret ptr addrspace(4) %val

>From ea511032d801544aac99fcb1395b88d0b0344787 Mon Sep 17 00:00:00 2001
From: "Fine, Gregory" <gregory.fine at intel.com>
Date: Wed, 10 Jun 2026 09:47:57 -0700
Subject: [PATCH 4/5] Address PR comments

---
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 21 ++++++++++---------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index c5e40d314838a..08f948b56f998 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -2001,7 +2001,7 @@ bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
         GR.getOrCreateSPIRVIntegerType(PtrSize, MIRBuilder);
     Register PtrToUVal =
         MRI->createGenericVirtualRegister(LLT::scalar(PtrSize));
-    MRI->setRegClass(PtrToUVal, MRI->getRegClassOrNull(ResVReg));
+    MRI->setRegClass(PtrToUVal, GR.getRegClass(PtrAsIntSpirvType));
     GR.assignSPIRVTypeToVReg(PtrAsIntSpirvType, PtrToUVal, MIRBuilder.getMF());
 
     Register PtrCastedToMatchValReg =
@@ -2031,15 +2031,16 @@ bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
         .addUse(GR.getSPIRVTypeID(ResType))
         .addUse(PtrToUVal)
         .constrainAllUses(TII, TRI, RBI);
-  } else {
-    auto AtomicLoad = MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
-                          .addDef(ResVReg)
-                          .addUse(GR.getSPIRVTypeID(ResType))
-                          .addUse(Ptr)
-                          .addUse(ScopeReg)
-                          .addUse(MemSemReg);
-    AtomicLoad.constrainAllUses(TII, TRI, RBI);
+    return true;
   }
+  auto AtomicLoad = MIRBuilder.buildInstr(SPIRV::OpAtomicLoad)
+                        .addDef(ResVReg)
+                        .addUse(GR.getSPIRVTypeID(ResType))
+                        .addUse(Ptr)
+                        .addUse(ScopeReg)
+                        .addUse(MemSemReg);
+  AtomicLoad.constrainAllUses(TII, TRI, RBI);
+
   return true;
 }
 
@@ -2142,7 +2143,7 @@ bool SPIRVInstructionSelector::selectAtomicStore(MachineInstr &I) const {
 
     Register PtrToUVal =
         MRI->createGenericVirtualRegister(LLT::scalar(PtrSize));
-    MRI->setRegClass(PtrToUVal, MRI->getRegClassOrNull(StoreVal));
+    MRI->setRegClass(PtrToUVal, GR.getRegClass(PtrAsIntSpirvType));
     GR.assignSPIRVTypeToVReg(PtrAsIntSpirvType, PtrToUVal, MIRBuilder.getMF());
     MIRBuilder.buildInstr(SPIRV::OpConvertPtrToU)
         .addDef(PtrToUVal)

>From 15086b2b9603e4111716256a856ff4dafb773640 Mon Sep 17 00:00:00 2001
From: fineg74 <61437305+fineg74 at users.noreply.github.com>
Date: Wed, 10 Jun 2026 14:52:29 -0700
Subject: [PATCH 5/5] Update
 llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll

Co-authored-by: Dmitry Sidorov <dsidorov at amd.com>
---
 llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
index 070dc7f1c4b7c..a5cab068932a4 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic-ptr.ll
@@ -16,7 +16,7 @@
 ; SPIRV64-DAG: %[[#Int64:]] = OpTypeInt 64 0
 ; SPIRV64-DAG: %[[#Int64Ptr:]] = OpTypePointer Generic %[[#Int64]]
 
-define ptr addrspace(4)  @load_ptr(ptr addrspace(4) %ptr) {
+define ptr addrspace(4) @load_ptr(ptr addrspace(4) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#Ptr:]] = OpFunctionParameter %[[#PtrInt8Ptr]]
 ; SPIRV32:       %[[#ConvertPtr:]] = OpBitcast %[[#Int32Ptr]] %[[#Ptr]]



More information about the llvm-commits mailing list