[llvm] [Draft][SPIRV] Lower load/store atomic to OpAtomicLoad/OpAtomicStore (PR #185696)

Juan Manuel Martinez CaamaƱo via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 12 08:48:58 PDT 2026


https://github.com/jmmartinez updated https://github.com/llvm/llvm-project/pull/185696

>From 2e42f0e2cc11306d81805aa595c71304403db62a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Juan=20Manuel=20Martinez=20Caama=C3=B1o?=
 <jmartinezcaamao at gmail.com>
Date: Tue, 10 Mar 2026 17:45:57 +0100
Subject: [PATCH] [SPIRV] Lower load/store atomic to OpAtomicLoad/OpAtomicStore

---
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |  4 +-
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp |  6 +-
 llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp   |  8 +-
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 80 +++++++++++++++++--
 .../Target/SPIRV/SPIRVLegalizePointerCast.cpp |  3 +-
 llvm/lib/Target/SPIRV/SPIRVTypeInst.cpp       | 14 ++++
 llvm/lib/Target/SPIRV/SPIRVTypeInst.h         |  4 +
 .../atomic-load-store-unsupported.ll          | 41 ++++++++++
 .../CodeGen/SPIRV/transcoding/load-atomic.ll  | 44 +++++-----
 .../CodeGen/SPIRV/transcoding/store-atomic.ll | 45 +++++------
 10 files changed, 182 insertions(+), 67 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index ead6d18124381..1e4a0c5251ed3 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -24,8 +24,8 @@ let TargetPrefix = "spv" in {
   def int_spv_unref_global : Intrinsic<[], [llvm_any_ty]>;
 
   def int_spv_gep : Intrinsic<[llvm_anyptr_ty], [llvm_i1_ty, llvm_any_ty, llvm_vararg_ty], [ImmArg<ArgIndex<0>>]>;
-  def int_spv_load : Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i16_ty, llvm_i32_ty], [ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<2>>]>;
-  def int_spv_store : Intrinsic<[], [llvm_any_ty, llvm_anyptr_ty, llvm_i16_ty, llvm_i32_ty], [ImmArg<ArgIndex<2>>, ImmArg<ArgIndex<3>>]>;
+  def int_spv_load : Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i16_ty, llvm_i32_ty, llvm_i8_ty], [ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<2>>, ImmArg<ArgIndex<3>>]>;
+  def int_spv_store : Intrinsic<[], [llvm_any_ty, llvm_anyptr_ty, llvm_i16_ty, llvm_i32_ty, llvm_i8_ty], [ImmArg<ArgIndex<2>>, ImmArg<ArgIndex<3>>, ImmArg<ArgIndex<4>>]>;
   def int_spv_extractv : Intrinsic<[llvm_any_ty], [llvm_i32_ty, llvm_vararg_ty]>;
   def int_spv_insertv : Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_any_ty, llvm_vararg_ty]>;
   def int_spv_extractelt : Intrinsic<[llvm_any_ty], [llvm_any_ty, llvm_anyint_ty]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 47dffcd83c73d..3ec2528a29b1a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2154,7 +2154,8 @@ Instruction *SPIRVEmitIntrinsics::visitLoadInst(LoadInst &I) {
   auto *NewI =
       B.CreateIntrinsic(Intrinsic::spv_load, {I.getOperand(0)->getType()},
                         {I.getPointerOperand(), B.getInt16(Flags),
-                         B.getInt32(I.getAlign().value())});
+                         B.getInt32(I.getAlign().value()),
+                         B.getInt8(static_cast<uint8_t>(I.getOrdering()))});
   replaceMemInstrUses(&I, NewI, B);
   return NewI;
 }
@@ -2185,7 +2186,8 @@ Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &I) {
   auto *NewI = B.CreateIntrinsic(
       Intrinsic::spv_store, {I.getValueOperand()->getType(), PtrOp->getType()},
       {I.getValueOperand(), PtrOp, B.getInt16(Flags),
-       B.getInt32(I.getAlign().value())});
+       B.getInt32(I.getAlign().value()),
+       B.getInt8(static_cast<uint8_t>(I.getOrdering()))});
   NewI->copyMetadata(I);
   I.eraseFromParent();
   return NewI;
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
index 2fcb71939322a..99b7189673a02 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
@@ -115,15 +115,15 @@ void SPIRVTargetLowering::getTgtMemIntrinsic(
     AlignIdx = 2;
     [[fallthrough]];
   case Intrinsic::spv_store: {
-    if (I.getNumOperands() >= AlignIdx + 1) {
-      auto *AlignOp = cast<ConstantInt>(I.getOperand(AlignIdx));
-      Info.align = Align(AlignOp->getZExtValue());
-    }
+    auto *AlignOp = cast<ConstantInt>(I.getOperand(AlignIdx));
+    Info.align = Align(AlignOp->getZExtValue());
     Info.flags = static_cast<MachineMemOperand::Flags>(
         cast<ConstantInt>(I.getOperand(AlignIdx - 1))->getZExtValue());
     Info.memVT = MVT::i64;
     // TODO: take into account opaque pointers (don't use getElementType).
     // MVT::getVT(PtrTy->getElementType());
+    Info.order = static_cast<AtomicOrdering>(
+        cast<ConstantInt>(I.getOperand(AlignIdx + 1))->getZExtValue());
     Infos.push_back(Info);
     return;
   }
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index fcae23432017d..82f0872551cee 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1648,6 +1648,7 @@ static void addMemoryOperands(uint64_t Flags, MachineInstrBuilder &MIB) {
 bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
                                           SPIRVTypeInst ResType,
                                           MachineInstr &I) const {
+  LLVMContext &Context = I.getMF()->getFunction().getContext();
   unsigned OpOffset = isa<GIntrinsic>(I) ? 1 : 0;
   Register Ptr = I.getOperand(1 + OpOffset).getReg();
 
@@ -1671,7 +1672,41 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
     }
   }
 
-  auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
+  MachineIRBuilder MIRBuilder(I);
+
+  if (I.getNumMemOperands()) {
+    const MachineMemOperand *MemOp = *I.memoperands_begin();
+    if (MemOp->isAtomic()) {
+      if (!ResType.isIntegerOrFloatingPointType())
+        return diagnoseUnsupported(
+            I, "atomic load of non-integer or floating point type");
+
+      // This could be relaxed since the volatile attribute on atomic load is
+      // supported with the VulkanMemoryModelKHR capability.
+      if (MemOp->isVolatile())
+        return diagnoseUnsupported(
+            I, "atomic load of volatile memory is not supported");
+
+      uint32_t Scope =
+          static_cast<uint32_t>(getMemScope(Context, MemOp->getSyncScopeID()));
+      Register ScopeReg = buildI32Constant(Scope, I);
+
+      AtomicOrdering AO = MemOp->getSuccessOrdering();
+      uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(AO));
+      Register MemSemReg = buildI32Constant(MemSem, I);
+
+      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 MIB = MIRBuilder.buildInstr(SPIRV::OpLoad)
                  .addDef(ResVReg)
                  .addUse(GR.getSPIRVTypeID(ResType))
                  .addUse(Ptr);
@@ -1681,7 +1716,6 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
                TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS);
     addMemoryOperands(I.getOperand(2 + OpOffset).getImm(), MIB);
   } else {
-    MachineIRBuilder MIRBuilder(I);
     addMemoryOperands(*I.memoperands_begin(), MIB, MIRBuilder, GR);
   }
   MIB.constrainAllUses(TII, TRI, RBI);
@@ -1689,6 +1723,7 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg,
 }
 
 bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
+  LLVMContext &Context = I.getMF()->getFunction().getContext();
   unsigned OpOffset = isa<GIntrinsic>(I) ? 1 : 0;
   Register StoreVal = I.getOperand(0 + OpOffset).getReg();
   Register Ptr = I.getOperand(1 + OpOffset).getReg();
@@ -1723,17 +1758,48 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
     }
   }
 
-  MachineBasicBlock &BB = *I.getParent();
-  auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpStore))
-                 .addUse(Ptr)
-                 .addUse(StoreVal);
+  MachineIRBuilder MIRBuilder(I);
+
+  if (I.getNumMemOperands()) {
+    const MachineMemOperand *MemOp = *I.memoperands_begin();
+    if (MemOp->isAtomic()) {
+      SPIRVTypeInst PtrType = GR.getSPIRVTypeForVReg(Ptr);
+      SPIRVTypeInst PointeeType = GR.getPointeeType(PtrType);
+      if (!PointeeType.isIntegerOrFloatingPointType())
+        return diagnoseUnsupported(
+            I, "atomic store of non-integer or floating point type");
+
+      // This could be relaxed since the volatile attribute on atomic store is
+      // supported with the VulkanMemoryModelKHR capability.
+      if (MemOp->isVolatile())
+        return diagnoseUnsupported(
+            I, "atomic store of volatile memory is not supported");
+
+      uint32_t Scope =
+          static_cast<uint32_t>(getMemScope(Context, MemOp->getSyncScopeID()));
+      Register ScopeReg = buildI32Constant(Scope, I);
+
+      AtomicOrdering AO = MemOp->getSuccessOrdering();
+      uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(AO));
+      Register MemSemReg = buildI32Constant(MemSem, I);
+
+      auto AtomicStore = MIRBuilder.buildInstr(SPIRV::OpAtomicStore)
+                             .addUse(Ptr)
+                             .addUse(ScopeReg)
+                             .addUse(MemSemReg)
+                             .addUse(StoreVal);
+      AtomicStore.constrainAllUses(TII, TRI, RBI);
+      return true;
+    }
+  }
+
+  auto MIB = MIRBuilder.buildInstr(SPIRV::OpStore).addUse(Ptr).addUse(StoreVal);
   if (!I.getNumMemOperands()) {
     assert(I.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS ||
            I.getOpcode() ==
                TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS);
     addMemoryOperands(I.getOperand(2 + OpOffset).getImm(), MIB);
   } else {
-    MachineIRBuilder MIRBuilder(I);
     addMemoryOperands(*I.memoperands_begin(), MIB, MIRBuilder, GR);
   }
   MIB.constrainAllUses(TII, TRI, RBI);
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
index c1bfd986bede7..16587d9283951 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
@@ -240,7 +240,8 @@ class SPIRVLegalizePointerCast : public FunctionPass {
 
       Types = {Element->getType(), ElementPtr->getType()};
       Align NewAlign = commonAlignment(Alignment, i * ElemSize);
-      Args = {Element, ElementPtr, B.getInt16(2), B.getInt32(NewAlign.value())};
+      Args = {Element, ElementPtr, B.getInt16(2), B.getInt32(NewAlign.value()),
+              B.getInt8(static_cast<uint8_t>(AtomicOrdering::NotAtomic))};
       B.CreateIntrinsic(Intrinsic::spv_store, {Types}, {Args});
     }
   }
diff --git a/llvm/lib/Target/SPIRV/SPIRVTypeInst.cpp b/llvm/lib/Target/SPIRV/SPIRVTypeInst.cpp
index d54c6c50c1ed5..a79efe061017e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTypeInst.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTypeInst.cpp
@@ -13,6 +13,8 @@
 #include "SPIRVTypeInst.h"
 #include "SPIRVInstrInfo.h"
 
+#include "SPIRV.h"
+
 namespace llvm {
 [[maybe_unused]] static bool definesATypeRegister(const MachineInstr &MI) {
   const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
@@ -23,4 +25,16 @@ SPIRVTypeInst::SPIRVTypeInst(const MachineInstr *MI) : MI(MI) {
   // A SPIRV Type whose result is not a type is invalid.
   assert(!MI || definesATypeRegister(*MI));
 }
+
+bool SPIRVTypeInst::isIntegerType() const {
+  return MI->getOpcode() == SPIRV::OpTypeInt;
+}
+
+bool SPIRVTypeInst::isFloatingPointType() const {
+  return MI->getOpcode() == SPIRV::OpTypeFloat;
+}
+
+bool SPIRVTypeInst::isIntegerOrFloatingPointType() const {
+  return isIntegerType() || isFloatingPointType();
+}
 } // namespace llvm
diff --git a/llvm/lib/Target/SPIRV/SPIRVTypeInst.h b/llvm/lib/Target/SPIRV/SPIRVTypeInst.h
index 36f3bb5c45c51..9fd0a96662344 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTypeInst.h
+++ b/llvm/lib/Target/SPIRV/SPIRVTypeInst.h
@@ -49,6 +49,10 @@ class SPIRVTypeInst {
   operator bool() const { return MI; }
 
   friend struct DenseMapInfo<SPIRVTypeInst>;
+
+  bool isIntegerType() const;
+  bool isFloatingPointType() const;
+  bool isIntegerOrFloatingPointType() const;
 };
 
 template <> struct DenseMapInfo<SPIRVTypeInst> {
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
new file mode 100644
index 0000000000000..66ff0064f8c0a
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
@@ -0,0 +1,41 @@
+; Verify that atomic load/store of vectors and volatile atomic load/store
+; correctly fail to select.
+
+; RUN: split-file %s %t
+
+; RUN: not llc -O0 -mtriple=spirv64-- %t/load-vector.ll -o /dev/null 2>&1 | FileCheck --check-prefix=FAIL-LOAD-VEC %s
+
+; RUN: not llc -O0 -mtriple=spirv64-- %t/load-volatile.ll -o /dev/null 2>&1 | FileCheck --check-prefix=FAIL-LOAD-VOL %s
+
+; RUN: not llc -O0 -mtriple=spirv64-- %t/store-vector.ll -o /dev/null 2>&1 | FileCheck --check-prefix=FAIL-STORE-VEC %s
+
+; RUN: not llc -O0 -mtriple=spirv64-- %t/store-volatile.ll -o /dev/null 2>&1 | FileCheck --check-prefix=FAIL-STORE-VOL %s
+
+; FAIL-LOAD-VEC: error:{{.*}}atomic load of non-integer or floating point type
+; FAIL-LOAD-VOL: error:{{.*}}atomic load of volatile memory is not supported
+; FAIL-STORE-VEC: error:{{.*}}atomic store of non-integer or floating point type
+; FAIL-STORE-VOL: error:{{.*}}atomic store of volatile memory is not supported
+
+;--- load-vector.ll
+define <2 x i32> @load_vector_acquire(ptr addrspace(1) %ptr) {
+  %val = load atomic <2 x i32>, ptr addrspace(1) %ptr acquire, align 8
+  ret <2 x i32> %val
+}
+
+;--- load-volatile.ll
+define i32 @load_i32_acquire_device_volatile(ptr addrspace(1) %ptr) {
+  %val = load atomic volatile i32, ptr addrspace(1) %ptr syncscope("device") acquire, align 4
+  ret i32 %val
+}
+
+;--- store-vector.ll
+define void @store_vector_release(ptr addrspace(1) %ptr, <2 x i32> %val) {
+  store atomic <2 x i32> %val, ptr addrspace(1) %ptr release, align 8
+  ret void
+}
+
+;--- store-volatile.ll
+define void @store_i32_release_device_volatile(ptr addrspace(1) %ptr, i32 %val) {
+  store atomic volatile i32 %val, ptr addrspace(1) %ptr syncscope("device") release, align 4
+  ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/load-atomic.ll b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic.ll
index 0ebd3a5ec20ae..2a871c78bf0fb 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/load-atomic.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/load-atomic.ll
@@ -4,19 +4,24 @@
 ; RUN: llc -O0 -mtriple=spirv32-- %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-- %s -o - -filetype=obj | spirv-val %}
 
-;; Check that 'load atomic' LLVM IR instructions are lowered.
-;; NOTE: The current lowering is incorrect: 'load atomic' should produce
-;; OpAtomicLoad but currently produces OpLoad, silently dropping the atomic
-;; ordering. This test documents the broken behaviour so it can be fixed.
+; Check that 'load atomic' LLVM IR instructions are lowered correctly to
+; OpAtomicLoad with the right Scope and Memory Semantics operands.
+;
+; unordered and monotonic are currently mapped to Memory Semantics `None (Relaxed)` 0x0
 
 ; CHECK-DAG: %[[#Int32:]] = OpTypeInt 32 0
 ; CHECK-DAG: %[[#Float:]] = OpTypeFloat 32
-; CHECK-DAG: %[[#Int32Vec:]] = OpTypeVector %[[#Int32]] 2
+; CHECK-DAG: %[[#Const0:]] = OpConstantNull %[[#Int32]]
+; CHECK-DAG: %[[#Const1:]] = OpConstant %[[#Int32]] 1{{$}}
+; CHECK-DAG: %[[#Const2:]] = OpConstant %[[#Int32]] 2{{$}}
+; CHECK-DAG: %[[#Const3:]] = OpConstant %[[#Int32]] 3{{$}}
+; CHECK-DAG: %[[#Const4:]] = OpConstant %[[#Int32]] 4{{$}}
+; CHECK-DAG: %[[#Const16:]] = OpConstant %[[#Int32]] 16{{$}}
 
 define i32 @load_i32_unordered(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const0]] %[[#Const0]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr unordered, align 4
   ret i32 %val
@@ -25,7 +30,7 @@ define i32 @load_i32_unordered(ptr addrspace(1) %ptr) {
 define i32 @load_i32_monotonic(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const0]] %[[#Const0]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr monotonic, align 4
   ret i32 %val
@@ -34,7 +39,7 @@ define i32 @load_i32_monotonic(ptr addrspace(1) %ptr) {
 define i32 @load_i32_acquire(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const0]] %[[#Const2]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr acquire, align 4
   ret i32 %val
@@ -43,7 +48,7 @@ define i32 @load_i32_acquire(ptr addrspace(1) %ptr) {
 define i32 @load_i32_seq_cst(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const0]] %[[#Const16]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr seq_cst, align 4
   ret i32 %val
@@ -54,7 +59,7 @@ define i32 @load_i32_seq_cst(ptr addrspace(1) %ptr) {
 define i32 @load_i32_acquire_singlethread(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const4]] %[[#Const2]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr syncscope("singlethread") acquire, align 4
   ret i32 %val
@@ -63,7 +68,7 @@ define i32 @load_i32_acquire_singlethread(ptr addrspace(1) %ptr) {
 define i32 @load_i32_acquire_subgroup(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const3]] %[[#Const2]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr syncscope("subgroup") acquire, align 4
   ret i32 %val
@@ -72,7 +77,7 @@ define i32 @load_i32_acquire_subgroup(ptr addrspace(1) %ptr) {
 define i32 @load_i32_acquire_workgroup(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const2]] %[[#Const2]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr syncscope("workgroup") acquire, align 4
   ret i32 %val
@@ -81,7 +86,7 @@ define i32 @load_i32_acquire_workgroup(ptr addrspace(1) %ptr) {
 define i32 @load_i32_acquire_device(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 4
+; CHECK:       %[[#]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const1]] %[[#Const2]]
 ; CHECK:       OpReturnValue
   %val = load atomic i32, ptr addrspace(1) %ptr syncscope("device") acquire, align 4
   ret i32 %val
@@ -92,20 +97,9 @@ define i32 @load_i32_acquire_device(ptr addrspace(1) %ptr) {
 define float @load_float_acquire(ptr addrspace(1) %ptr) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#load:]] = OpLoad %[[#Int32]] %[[#ptr]] Aligned 8
+; CHECK:       %[[#load:]] = OpAtomicLoad %[[#Int32]] %[[#ptr]] %[[#Const0]] %[[#Const2]]
 ; CHECK:       %[[#val:]] = OpBitcast %[[#Float]] %[[#load]]
 ; CHECK:       OpReturnValue %[[#val]]
   %val = load atomic float, ptr addrspace(1) %ptr acquire, align 8
   ret float %val
 }
-
-; -- test with a vector type
-
-define <2 x i32> @load_vector_acquire(ptr addrspace(1) %ptr) {
-; CHECK-LABEL: OpFunction %[[#]]
-; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#]] = OpLoad %[[#Int32Vec]] %[[#ptr]] Aligned 8
-; CHECK:       OpReturnValue
-  %val = load atomic <2 x i32>, ptr addrspace(1) %ptr acquire, align 8
-  ret <2 x i32> %val
-}
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/store-atomic.ll b/llvm/test/CodeGen/SPIRV/transcoding/store-atomic.ll
index b11b26451d086..7337a631a8616 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/store-atomic.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/store-atomic.ll
@@ -4,20 +4,25 @@
 ; RUN: llc -O0 -mtriple=spirv32-- %s -o - | FileCheck %s
 ; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-- %s -o - -filetype=obj | spirv-val %}
 
-;; Check that 'store atomic' LLVM IR instructions are lowered.
-;; NOTE: The current lowering is incorrect: 'store atomic' should produce
-;; OpAtomicStore but currently produces OpStore, silently dropping the atomic
-;; ordering. This test documents the broken behaviour so it can be fixed.
+; Check that 'store atomic' LLVM IR instructions are lowered correctly to
+; OpAtomicStore with the right Scope and Memory Semantics operands.
+;
+; unordered and monotonic are currently mapped to Memory Semantics `None (Relaxed)` 0x0
 
 ; CHECK-DAG: %[[#Int32:]] = OpTypeInt 32 0
 ; CHECK-DAG: %[[#Float:]] = OpTypeFloat 32
-; CHECK-DAG: %[[#Int32Vec:]] = OpTypeVector %[[#Int32]] 2
+; CHECK-DAG: %[[#Const0:]] = OpConstantNull %[[#Int32]]
+; CHECK-DAG: %[[#Const1:]] = OpConstant %[[#Int32]] 1{{$}}
+; CHECK-DAG: %[[#Const2:]] = OpConstant %[[#Int32]] 2{{$}}
+; CHECK-DAG: %[[#Const3:]] = OpConstant %[[#Int32]] 3{{$}}
+; CHECK-DAG: %[[#Const4:]] = OpConstant %[[#Int32]] 4{{$}}
+; CHECK-DAG: %[[#Const16:]] = OpConstant %[[#Int32]] 16{{$}}
 
 define void @store_i32_unordered(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const0]] %[[#Const0]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr unordered, align 4
   ret void
@@ -27,7 +32,7 @@ define void @store_i32_monotonic(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const0]] %[[#Const0]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr monotonic, align 4
   ret void
@@ -37,7 +42,7 @@ define void @store_i32_release(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const0]] %[[#Const4]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr release, align 4
   ret void
@@ -47,7 +52,7 @@ define void @store_i32_seq_cst(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const0]] %[[#Const16]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr seq_cst, align 4
   ret void
@@ -59,7 +64,7 @@ define void @store_i32_release_singlethread(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const4]] %[[#Const4]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr syncscope("singlethread") release, align 4
   ret void
@@ -69,7 +74,7 @@ define void @store_i32_release_subgroup(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const3]] %[[#Const4]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr syncscope("subgroup") release, align 4
   ret void
@@ -79,7 +84,7 @@ define void @store_i32_release_workgroup(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const2]] %[[#Const4]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr syncscope("workgroup") release, align 4
   ret void
@@ -89,7 +94,7 @@ define void @store_i32_release_device(ptr addrspace(1) %ptr, i32 %val) {
 ; CHECK-LABEL: OpFunction %[[#]]
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 4
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const1]] %[[#Const4]] %[[#val]]
 ; CHECK:       OpReturn
   store atomic i32 %val, ptr addrspace(1) %ptr syncscope("device") release, align 4
   ret void
@@ -102,20 +107,8 @@ define void @store_float_release(ptr addrspace(1) %ptr, float %val) {
 ; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
 ; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Float]]
 ; CHECK:       %[[#cast:]] = OpBitcast %[[#Int32]] %[[#val]]
-; CHECK:       OpStore %[[#ptr]] %[[#cast]] Aligned 8
+; CHECK:       OpAtomicStore %[[#ptr]] %[[#Const0]] %[[#Const4]] %[[#cast]]
 ; CHECK:       OpReturn
   store atomic float %val, ptr addrspace(1) %ptr release, align 8
   ret void
 }
-
-; -- test with a vector type
-
-define void @store_vector_release(ptr addrspace(1) %ptr, <2 x i32> %val) {
-; CHECK-LABEL: OpFunction %[[#]]
-; CHECK:       %[[#ptr:]] = OpFunctionParameter %[[#]]
-; CHECK:       %[[#val:]] = OpFunctionParameter %[[#Int32Vec]]
-; CHECK:       OpStore %[[#ptr]] %[[#val]] Aligned 8
-; CHECK:       OpReturn
-  store atomic <2 x i32> %val, ptr addrspace(1) %ptr release, align 8
-  ret void
-}



More information about the llvm-commits mailing list