Add -hack-atomic-flag-barrier option (#1652)
Under the SPIR-V and Vulkan memory model specifications, OpAtomic*
instructions with acquire/release semantics already establish
happens-before ordering for regular memory accesses across threads.
However, some drivers (e.g. under the GLSL450 memory model) fail to
properly enforce this ordering for atomic_flag operations without
explicit memory barriers.
To work around such driver issues, add a new compiler option
-hack-atomic-flag-barrier:
- In replaceAtomicFlagClear: emit an OpMemoryBarrier with Release
semantics immediately prior to OpAtomicStore when release ordering
is requested (memory_order_release, memory_order_seq_cst, or default).
- In replaceAtomicFlagTestAndSet:
- Emit an OpMemoryBarrier with Release semantics immediately prior
to OpAtomicExchange when release ordering is requested
(memory_order_release, memory_order_acq_rel, memory_order_seq_cst,
or default).
- Emit an OpMemoryBarrier with Acquire semantics immediately after
OpAtomicExchange when acquire ordering is requested
(memory_order_acquire, memory_order_acq_rel, memory_order_seq_cst,
or default).
- Do not emit OpMemoryBarrier when memory_order_relaxed is specified,
as Vulkan SPIR-V forbids OpMemoryBarrier with semantics 0 (None).diff --git a/include/clspv/Option.h b/include/clspv/Option.h
index 4a486a2..6bda2ba 100644
--- a/include/clspv/Option.h
+++ b/include/clspv/Option.h
@@ -94,6 +94,10 @@
// requires components to be shuffled to match OpenCL specification.
bool HackImage1dBufferBGRA();
+// Returns true if OpMemoryBarrier should be emitted for non-relaxed atomic_flag
+// builtins. Works around driver bugs.
+bool HackAtomicFlagBarrier();
+
// Returns true if module-scope constants are to be collected into a single
// storage buffer. The binding for that buffer, and its intialization data
// are given in the descriptor map file.
diff --git a/lib/Option.cpp b/lib/Option.cpp
index eeb6043..3b3f947 100644
--- a/lib/Option.cpp
+++ b/lib/Option.cpp
@@ -171,6 +171,11 @@
llvm::cl::desc("Insert a dummy instruction after conversions to float to "
"avoid driver optimization getting rid of the conversion"));
+llvm::cl::opt<bool> hack_atomic_flag_barrier(
+ "hack-atomic-flag-barrier", llvm::cl::init(false),
+ llvm::cl::desc(
+ "Emit OpMemoryBarrier for non-relaxed atomic_flag builtins"));
+
llvm::cl::opt<bool>
pod_ubo("pod-ubo", llvm::cl::init(false),
llvm::cl::desc("POD kernel arguments are in uniform buffers"));
@@ -550,6 +555,7 @@
bool HackLogicalPtrtoint() { return hack_logical_ptrtoint; }
bool HackConvertToFloat() { return hack_convert_to_float; }
bool HackImage1dBufferBGRA() { return hack_image1d_buffer_bgra; }
+bool HackAtomicFlagBarrier() { return hack_atomic_flag_barrier; }
bool ModuleConstantsInStorageBuffer() {
return module_constants_in_storage_buffer;
}
diff --git a/lib/ReplaceOpenCLBuiltinPass.cpp b/lib/ReplaceOpenCLBuiltinPass.cpp
index 3c4549c..2902cc3 100644
--- a/lib/ReplaceOpenCLBuiltinPass.cpp
+++ b/lib/ReplaceOpenCLBuiltinPass.cpp
@@ -3884,6 +3884,42 @@
});
}
+namespace {
+bool OrderHasAcquire(Value *order_arg) {
+ if (order_arg == nullptr) {
+ return true;
+ }
+ if (auto const_order = dyn_cast<ConstantInt>(order_arg)) {
+ auto order_val =
+ static_cast<AtomicMemoryOrder>(const_order->getZExtValue());
+ return order_val == AtomicMemoryOrder::kMemoryOrderAcquire ||
+ order_val == AtomicMemoryOrder::kMemoryOrderAcqRel ||
+ order_val == AtomicMemoryOrder::kMemoryOrderSeqCst;
+ }
+ return true;
+}
+
+bool OrderHasRelease(Value *order_arg) {
+ if (order_arg == nullptr) {
+ return true;
+ }
+ if (auto const_order = dyn_cast<ConstantInt>(order_arg)) {
+ auto order_val =
+ static_cast<AtomicMemoryOrder>(const_order->getZExtValue());
+ return order_val == AtomicMemoryOrder::kMemoryOrderRelease ||
+ order_val == AtomicMemoryOrder::kMemoryOrderAcqRel ||
+ order_val == AtomicMemoryOrder::kMemoryOrderSeqCst;
+ }
+ return true;
+}
+
+void InsertMemoryBarrier(CallInst *Call, Value *scope, Value *semantics) {
+ Type *void_ty = Type::getVoidTy(Call->getContext());
+ InsertSPIRVOp(Call, spv::OpMemoryBarrier, {Attribute::Convergent}, void_ty,
+ {scope, semantics});
+}
+} // namespace
+
bool ReplaceOpenCLBuiltinPass::replaceAtomicFlagTestAndSet(Function &F) {
// convert
// %was_set = OpAtomicFlagTestAndSet %bool %flag %scope %semantics
@@ -3908,10 +3944,26 @@
auto scope = MemoryScope(scope_arg, is_global, Call);
IRBuilder<> builder(Call);
+ uint32_t storage = is_global ? spv::MemorySemanticsUniformMemoryMask
+ : spv::MemorySemanticsWorkgroupMemoryMask;
+
+ if (clspv::Option::HackAtomicFlagBarrier() && OrderHasRelease(order_arg)) {
+ auto release_semantics =
+ builder.getInt32(spv::MemorySemanticsReleaseMask | storage);
+ InsertMemoryBarrier(Call, scope, release_semantics);
+ }
+
auto set_value = builder.getInt32(1);
auto previous_value =
InsertSPIRVOp(Call, spv::OpAtomicExchange, {}, set_value->getType(),
{flag_pointer, scope, semantics, set_value});
+
+ if (clspv::Option::HackAtomicFlagBarrier() && OrderHasAcquire(order_arg)) {
+ auto acquire_semantics =
+ builder.getInt32(spv::MemorySemanticsAcquireMask | storage);
+ InsertMemoryBarrier(Call, scope, acquire_semantics);
+ }
+
return builder.CreateICmpEQ(previous_value, set_value);
});
}
@@ -3941,6 +3993,14 @@
auto scope = MemoryScope(scope_arg, is_global, Call);
IRBuilder<> builder(Call);
+ if (clspv::Option::HackAtomicFlagBarrier() && OrderHasRelease(order_arg)) {
+ uint32_t storage = is_global ? spv::MemorySemanticsUniformMemoryMask
+ : spv::MemorySemanticsWorkgroupMemoryMask;
+ auto release_semantics =
+ builder.getInt32(spv::MemorySemanticsReleaseMask | storage);
+ InsertMemoryBarrier(Call, scope, release_semantics);
+ }
+
auto clear_value = builder.getInt32(0);
return InsertSPIRVOp(Call, spv::OpAtomicStore, {}, builder.getVoidTy(),
{flag_pointer, scope, semantics, clear_value});
diff --git a/test/AtomicBuiltins/atomic_flag_hack_atomic_flag_barrier.cl b/test/AtomicBuiltins/atomic_flag_hack_atomic_flag_barrier.cl
new file mode 100644
index 0000000..c6fc6b4
--- /dev/null
+++ b/test/AtomicBuiltins/atomic_flag_hack_atomic_flag_barrier.cl
@@ -0,0 +1,368 @@
+// RUN: clspv %s --cl-std=CL3.0 --enable-feature-macros=__opencl_c_atomic_order_seq_cst,__opencl_c_atomic_scope_device -hack-atomic-flag-barrier -o %t.spv
+// RUN: spirv-val --target-env vulkan1.1 %t.spv
+// RUN: spirv-dis %t.spv | FileCheck %s
+// RUN: clspv %s --cl-std=CL2.0 -inline-entry-points -hack-atomic-flag-barrier -o %t.spv
+// RUN: spirv-val --target-env vulkan1.1 %t.spv
+// RUN: spirv-dis %t.spv | FileCheck %s
+
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_global:[a-zA-Z0-9_]+]] "flag_global"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_local:[a-zA-Z0-9_]+]] "flag_local"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_set_partial_explicit_global:[a-zA-Z0-9_]+]] "flag_set_partial_explicit_global"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_set_partial_explicit_local:[a-zA-Z0-9_]+]] "flag_set_partial_explicit_local"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_clear_partial_explicit_global:[a-zA-Z0-9_]+]] "flag_clear_partial_explicit_global"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_clear_partial_explicit_local:[a-zA-Z0-9_]+]] "flag_clear_partial_explicit_local"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_set_full_explicit_global:[a-zA-Z0-9_]+]] "flag_set_full_explicit_global"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_set_full_explicit_local:[a-zA-Z0-9_]+]] "flag_set_full_explicit_local"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_clear_full_explicit_global:[a-zA-Z0-9_]+]] "flag_clear_full_explicit_global"
+// CHECK-DAG: OpEntryPoint GLCompute %[[flag_clear_full_explicit_local:[a-zA-Z0-9_]+]] "flag_clear_full_explicit_local"
+
+// CHECK-DAG: %[[UINT:[a-zA-Z0-9_]+]] = OpTypeInt 32 0
+// CHECK-DAG: %[[BOOL:[a-zA-Z0-9_]+]] = OpTypeBool
+
+// 0 = Relaxed
+// CHECK-DAG: %[[UINT_0:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 0
+
+// 1 = Device Scope
+// CHECK-DAG: %[[UINT_1:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 1
+// 2 = Workgroup Scope
+// CHECK-DAG: %[[UINT_2:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 2
+// 3 = Subgroup Scope
+// CHECK-DAG: %[[UINT_3:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 3
+
+// dec 66 = hex 42 = Acquire & UniformMemory
+// CHECK-DAG: %[[UINT_66:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 66
+// dec 68 = hex 44 = Release & UniformMemory
+// CHECK-DAG: %[[UINT_68:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 68
+// dec 72 = hex 48 = AcquireRelease & UniformMemory
+// CHECK-DAG: %[[UINT_72:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 72
+
+// dec 258 = hex 102 Acquire & WorkgroupMemory
+// CHECK-DAG: %[[UINT_258:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 258
+// dec 260 = hex 104 Release & WorkgroupMemory
+// CHECK-DAG: %[[UINT_260:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 260
+// dec 264 = hex 108 AcquireRelease & WorkgroupMemory
+// CHECK-DAG: %[[UINT_264:[a-zA-Z0-9_]+]] = OpConstant %[[UINT]] 264
+
+// Note: Device Scope is the default scope
+// Note: SequentiallyConsistent is the default order
+
+// CHECK: %[[flag_global]] = OpFunction %void
+kernel void flag_global(global int *out, global atomic_flag *flag) {
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set(flag);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear(flag);
+}
+
+// CHECK: %[[flag_local]] = OpFunction %void
+kernel void flag_local(global int *out, local atomic_flag *flag) {
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set(flag);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear(flag);
+}
+
+// explicit scope
+
+// CHECK: %[[flag_set_partial_explicit_global]] = OpFunction %void
+kernel void flag_set_partial_explicit_global(global int *out, global atomic_flag *flag) {
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel);
+
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_66]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_68]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release);
+
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed);
+}
+
+// CHECK: %[[flag_set_partial_explicit_local]] = OpFunction %void
+kernel void flag_set_partial_explicit_local(global int *out, local atomic_flag *flag) {
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel);
+
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_258]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_260]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release);
+
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed);
+}
+
+// CHECK: %[[flag_clear_partial_explicit_global]] = OpFunction %void
+kernel void flag_clear_partial_explicit_global(global atomic_flag *flag) {
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release);
+
+// CHECK: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed);
+}
+
+// CHECK: %[[flag_clear_partial_explicit_local]] = OpFunction %void
+kernel void flag_clear_partial_explicit_local(local atomic_flag *flag) {
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release);
+
+// CHECK: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed);
+}
+
+// explicit order and scope
+
+// CHECK: %[[flag_set_full_explicit_global]] = OpFunction %void
+kernel void flag_set_full_explicit_global(global int *out, global atomic_flag *flag) {
+
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_3]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst, memory_scope_sub_group);
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_3]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel, memory_scope_sub_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_66]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_3]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire, memory_scope_sub_group);
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_68]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release, memory_scope_sub_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed, memory_scope_sub_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst, memory_scope_work_group);
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel, memory_scope_work_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_66]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire, memory_scope_work_group);
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_68]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release, memory_scope_work_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed, memory_scope_work_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst, memory_scope_device);
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_72]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel, memory_scope_device);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_66]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_66]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire, memory_scope_device);
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_68]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release, memory_scope_device);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed, memory_scope_device);
+
+}
+
+// CHECK: %[[flag_set_full_explicit_local]] = OpFunction %void
+kernel void flag_set_full_explicit_local(global int *out, local atomic_flag *flag) {
+
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_3]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst, memory_scope_sub_group);
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_3]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel, memory_scope_sub_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_258]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_3]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire, memory_scope_sub_group);
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_260]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release, memory_scope_sub_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_3]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed, memory_scope_sub_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst, memory_scope_work_group);
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel, memory_scope_work_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_258]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_2]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire, memory_scope_work_group);
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_260]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release, memory_scope_work_group);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_2]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed, memory_scope_work_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_seq_cst, memory_scope_device);
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_264]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acq_rel, memory_scope_device);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_258]] %[[UINT_1]]
+// CHECK-NEXT: OpMemoryBarrier %[[UINT_1]] %[[UINT_258]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_acquire, memory_scope_device);
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_260]]
+// CHECK-NEXT: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_260]] %[[UINT_1]]
+// CHECK: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_release, memory_scope_device);
+// CHECK: %[[previous_value:[a-zA-Z0-9_]+]] = OpAtomicExchange %[[UINT]] {{.*}} %[[UINT_1]] %[[UINT_0]] %[[UINT_1]]
+// CHECK-NEXT: OpIEqual %bool %[[previous_value]] %[[UINT_1]]
+ *out = atomic_flag_test_and_set_explicit(flag, memory_order_relaxed, memory_scope_device);
+
+}
+
+// CHECK: %[[flag_clear_full_explicit_global]] = OpFunction %void
+ kernel void flag_clear_full_explicit_global(global atomic_flag *flag) {
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_3]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst, memory_scope_sub_group);
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_3]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release, memory_scope_sub_group);
+// CHECK: OpAtomicStore {{.*}} %[[UINT_3]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed, memory_scope_sub_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst, memory_scope_work_group);
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release, memory_scope_work_group);
+// CHECK: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed, memory_scope_work_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst, memory_scope_device);
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_68]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_68]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release, memory_scope_device);
+// CHECK: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed, memory_scope_device);
+
+}
+
+// CHECK: %[[flag_clear_full_explicit_local]] = OpFunction %void
+ kernel void flag_clear_full_explicit_local(local atomic_flag *flag) {
+
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_3]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst, memory_scope_sub_group);
+// CHECK: OpMemoryBarrier %[[UINT_3]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_3]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release, memory_scope_sub_group);
+// CHECK: OpAtomicStore {{.*}} %[[UINT_3]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed, memory_scope_sub_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst, memory_scope_work_group);
+// CHECK: OpMemoryBarrier %[[UINT_2]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release, memory_scope_work_group);
+// CHECK: OpAtomicStore {{.*}} %[[UINT_2]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed, memory_scope_work_group);
+
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_seq_cst, memory_scope_device);
+// CHECK: OpMemoryBarrier %[[UINT_1]] %[[UINT_260]]
+// CHECK-NEXT: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_260]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_release, memory_scope_device);
+// CHECK: OpAtomicStore {{.*}} %[[UINT_1]] %[[UINT_0]] %[[UINT_0]]
+ atomic_flag_clear_explicit(flag, memory_order_relaxed, memory_scope_device);
+}