[llvm] [SPIRV] Support `Volatile` memory semantics operand in atomic load/store (PR #195978)
via llvm-commits
llvm-commits at lists.llvm.org
Tue May 5 19:20:14 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-spir-v
Author: Victor Mustya (vmustya)
<details>
<summary>Changes</summary>
The Vulkan memory model supports the `Volatile` memory semantics being
used with atomic operations. This patch adds the support to the SPIR-V
backend.
When the memory model is OpenCL, the `Volatile` memory semantics is not
supported. In this case, we ignore it and emit a regular `OpAtomicLoad`
or `OpAtomicStore` instruction. It should be safe, because the atomic
operations aren't eliminated anyway.
Assisted-by: Claude Opus 4.6 <noreply@<!-- -->anthropic.com>
---
Full diff: https://github.com/llvm/llvm-project/pull/195978.diff
4 Files Affected:
- (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+7-14)
- (modified) llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td (+1)
- (modified) llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll (+1-20)
- (added) llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-volatile.ll (+32)
``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index aee3a29c6e42b..cb0f4d3bd1246 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1739,9 +1739,9 @@ bool SPIRVInstructionSelector::selectPopCount(Register ResVReg,
SPIRVTypeInst ResType,
MachineInstr &I,
unsigned Opcode) const {
- // Vulkan restricts OpBitCount to 32-bit integers or vectors of 32-bit
- // integers unless VK_KHR_maintenance9 is enabled. Until VK_KHR_maintaince9
- // is core we will not generate OpBitCount with any other types when
+ // Vulkan restricts OpBitCount to 32-bit integers or vectors of 32-bit
+ // integers unless VK_KHR_maintenance9 is enabled. Until VK_KHR_maintaince9
+ // is core we will not generate OpBitCount with any other types when
// targeting Vulkan.
if (!STI.getTargetTriple().isVulkanOS())
return selectUnOp(ResVReg, ResType, I, Opcode);
@@ -1944,11 +1944,6 @@ bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
assert(I.getNumMemOperands());
const MachineMemOperand &MemOp = **I.memoperands_begin();
assert(MemOp.isAtomic());
- // TODO: This must be relaxed since the volatile attribute on atomic load is
- // supported with the VulkanMemoryModelKHR capability.
- if (MemOp.isVolatile())
- return diagnoseUnsupported(I, "Lowering to SPIR-V of atomic load of "
- "volatile memory is not supported");
uint32_t Scope =
static_cast<uint32_t>(getMemScope(Context, MemOp.getSyncScopeID()));
@@ -1958,6 +1953,8 @@ bool SPIRVInstructionSelector::selectAtomicLoad(Register ResVReg,
uint32_t StorageClass = static_cast<uint32_t>(getMemSemanticsForStorageClass(
addressSpaceToStorageClass(MemOp.getAddrSpace(), STI)));
uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(AO));
+ if (MemOp.isVolatile() && STI.getTargetTriple().isVulkanOS())
+ MemSem |= static_cast<uint32_t>(SPIRV::MemorySemantics::Volatile);
Register MemSemReg = buildI32Constant(MemSem | StorageClass, I);
MachineIRBuilder MIRBuilder(I);
@@ -2044,12 +2041,6 @@ bool SPIRVInstructionSelector::selectAtomicStore(MachineInstr &I) const {
const MachineMemOperand &MemOp = **I.memoperands_begin();
assert(MemOp.isAtomic());
- // TODO: This must be relaxed since the volatile attribute on atomic store is
- // supported with the VulkanMemoryModelKHR capability.
- if (MemOp.isVolatile())
- return diagnoseUnsupported(I, "Lowering to SPIR-V of atomic store of "
- "volatile memory is not supported");
-
uint32_t Scope =
static_cast<uint32_t>(getMemScope(Context, MemOp.getSyncScopeID()));
Register ScopeReg = buildI32Constant(Scope, I);
@@ -2058,6 +2049,8 @@ bool SPIRVInstructionSelector::selectAtomicStore(MachineInstr &I) const {
uint32_t StorageClass = static_cast<uint32_t>(getMemSemanticsForStorageClass(
addressSpaceToStorageClass(MemOp.getAddrSpace(), STI)));
uint32_t MemSem = static_cast<uint32_t>(getMemSemantics(AO));
+ if (MemOp.isVolatile() && STI.getTargetTriple().isVulkanOS())
+ MemSem |= static_cast<uint32_t>(SPIRV::MemorySemantics::Volatile);
Register MemSemReg = buildI32Constant(MemSem | StorageClass, I);
MachineIRBuilder MIRBuilder(I);
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index cd52181bfc436..acb104ddfbea1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -1672,6 +1672,7 @@ defm ImageMemory : MemorySemanticsOperand<0x800, 0, 0, [], []>;
defm OutputMemoryKHR : MemorySemanticsOperand<0x1000, 0, 0, [], [VulkanMemoryModelKHR]>;
defm MakeAvailableKHR : MemorySemanticsOperand<0x2000, 0, 0, [], [VulkanMemoryModelKHR]>;
defm MakeVisibleKHR : MemorySemanticsOperand<0x4000, 0, 0, [], [VulkanMemoryModelKHR]>;
+defm Volatile : MemorySemanticsOperand<0x8000, 0, 0, [], [VulkanMemoryModelKHR]>;
//===----------------------------------------------------------------------===//
// Multiclass used to define MemoryOperand enum values and at the same time
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 4382c3cf87097..b6c055ff3b7de 100644
--- a/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
+++ b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-unsupported.ll
@@ -1,20 +1,13 @@
-; Verify that atomic load/store of vectors and volatile atomic load/store
-; correctly fail to select.
+; Verify that atomic load/store of vectors 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 is only allowed for integer or floating point types
-; FAIL-LOAD-VOL: error:{{.*}}atomic load of volatile memory is not supported
; FAIL-STORE-VEC: error:{{.*}}atomic store is only allowed for integer or floating point types
-; 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) {
@@ -22,20 +15,8 @@ define <2 x i32> @load_vector_acquire(ptr addrspace(1) %ptr) {
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/atomic-load-store-volatile.ll b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-volatile.ll
new file mode 100644
index 0000000000000..a988cd9311c57
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/atomic-load-store-volatile.ll
@@ -0,0 +1,32 @@
+; Verify that volatile atomic load/store are accepted and produce
+; OpAtomicLoad/OpAtomicStore. On non-Vulkan targets the volatile qualifier
+; is silently dropped; on Vulkan the Volatile memory semantics bit is set.
+
+; Non-Vulkan: no Volatile memory semantics.
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=NONVK
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Vulkan: Volatile memory semantics (0x8000) ORed into the semantics operand.
+; RUN: llc -O0 -mtriple=spirv-unknown-vulkan1.3-compute %s -o - | FileCheck %s --check-prefix=VULKAN
+
+; NONVK-DAG: %[[#C514:]] = OpConstant %[[#]] 514
+; NONVK-DAG: %[[#C516:]] = OpConstant %[[#]] 516
+; NONVK: OpAtomicLoad %[[#]] %[[#]] %[[#]] %[[#C514]]
+; NONVK: OpAtomicStore %[[#]] %[[#]] %[[#C516]] %[[#]]
+
+; 0x8000 | 0x2 (Acquire) | 0x200 (CrossWorkgroupMemory) = 33282
+; 0x8000 | 0x4 (Release) | 0x200 (CrossWorkgroupMemory) = 33284
+; VULKAN-DAG: %[[#VA:]] = OpConstant %[[#]] 33282
+; VULKAN-DAG: %[[#VR:]] = OpConstant %[[#]] 33284
+; VULKAN: OpAtomicLoad %[[#]] %[[#]] %[[#]] %[[#VA]]
+; VULKAN: OpAtomicStore %[[#]] %[[#]] %[[#VR]] %[[#]]
+
+define i32 @load_volatile(ptr addrspace(1) %ptr) {
+ %val = load atomic volatile i32, ptr addrspace(1) %ptr syncscope("device") acquire, align 4
+ ret i32 %val
+}
+
+define void @store_volatile(ptr addrspace(1) %ptr, i32 %val) {
+ store atomic volatile i32 %val, ptr addrspace(1) %ptr syncscope("device") release, align 4
+ ret void
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/195978
More information about the llvm-commits
mailing list