11# Atomic Functions
22
3- # provides atomic functions that rely on the OpenCL base atomics, as well as the
4- # cl_khr_int64_base_atomics and cl_khr_int64_extended_atomics extensions .
3+ # Integer atomics are emitted as SPIR-V wrapper builtins, so the LLVM SPIR-V
4+ # backend lowers them to OpAtomic* instructions directly .
55
66const atomic_float_types = [Float32, Float64]
77const atomic_integer_types = [UInt32, Int32, UInt64, Int64]
88const atomic_memory_types = [AS. Workgroup, AS. CrossWorkgroup]
9- const atomic_types = vcat (atomic_float_types, atomic_integer_types)
9+
10+ const atomic_scope = Scope. Workgroup
11+
12+ atomic_memory_semantics (:: Val{AS.Workgroup} ) = MemorySemantics. WorkgroupMemory
13+ atomic_memory_semantics (:: Val{AS.CrossWorkgroup} ) = MemorySemantics. CrossWorkgroupMemory
1014
1115
1216# generically typed
1317
14- for gentype in atomic_types, as in atomic_memory_types
18+ for gentype in atomic_integer_types, as in atomic_memory_types
19+ atomic_min_intrinsic = gentype <: Signed ? " __spirv_AtomicSMin" : " __spirv_AtomicUMin"
20+ atomic_max_intrinsic = gentype <: Signed ? " __spirv_AtomicSMax" : " __spirv_AtomicUMax"
1521@eval begin
1622
1723@device_function atomic_add! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
18- @builtin_ccall (" atomic_add" , $ gentype,
19- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
24+ @builtin_ccall (" __spirv_AtomicIAdd" , $ gentype,
25+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
26+ p, UInt32 (atomic_scope),
27+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
2028
2129@device_function atomic_sub! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
22- @builtin_ccall (" atomic_sub" , $ gentype,
23- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
30+ @builtin_ccall (" __spirv_AtomicISub" , $ gentype,
31+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
32+ p, UInt32 (atomic_scope),
33+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
2434
2535@device_function atomic_inc! (p:: LLVMPtr{$gentype,$as} ) =
26- @builtin_ccall ( " atomic_inc " , $ gentype, (LLVMPtr{ $ gentype, $ as},), p )
36+ atomic_add! (p, one ( $ gentype) )
2737
2838@device_function atomic_dec! (p:: LLVMPtr{$gentype,$as} ) =
29- @builtin_ccall ( " atomic_dec " , $ gentype, (LLVMPtr{ $ gentype, $ as},), p )
39+ atomic_sub! (p, one ( $ gentype) )
3040
3141@device_function atomic_min! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
32- @builtin_ccall (" atomic_min" , $ gentype,
33- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
42+ @builtin_ccall ($ atomic_min_intrinsic, $ gentype,
43+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
44+ p, UInt32 (atomic_scope),
45+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
3446
3547@device_function atomic_max! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
36- @builtin_ccall (" atomic_max" , $ gentype,
37- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
48+ @builtin_ccall ($ atomic_max_intrinsic, $ gentype,
49+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
50+ p, UInt32 (atomic_scope),
51+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
3852
3953@device_function atomic_and! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
40- @builtin_ccall (" atomic_and" , $ gentype,
41- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
54+ @builtin_ccall (" __spirv_AtomicAnd" , $ gentype,
55+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
56+ p, UInt32 (atomic_scope),
57+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
4258
4359@device_function atomic_or! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
44- @builtin_ccall (" atomic_or" , $ gentype,
45- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
60+ @builtin_ccall (" __spirv_AtomicOr" , $ gentype,
61+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
62+ p, UInt32 (atomic_scope),
63+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
4664
4765@device_function atomic_xor! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
48- @builtin_ccall (" atomic_xor" , $ gentype,
49- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
50- end
51- if gentype in atomic_integer_types
52- @eval begin
53- @device_function atomic_xchg! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
54- @builtin_ccall (" atomic_xchg" , $ gentype,
55- (LLVMPtr{$ gentype,$ as}, $ gentype), p, val)
56-
57- @device_function atomic_cmpxchg! (p:: LLVMPtr{$gentype,$as} , cmp:: $gentype , val:: $gentype ) =
58- @builtin_ccall (" atomic_cmpxchg" , $ gentype,
59- (LLVMPtr{$ gentype,$ as}, $ gentype, $ gentype), p, cmp, val)
60- end
66+ @builtin_ccall (" __spirv_AtomicXor" , $ gentype,
67+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
68+ p, UInt32 (atomic_scope),
69+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
70+
71+ @device_function atomic_xchg! (p:: LLVMPtr{$gentype,$as} , val:: $gentype ) =
72+ @builtin_ccall (" __spirv_AtomicExchange" , $ gentype,
73+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, $ gentype),
74+ p, UInt32 (atomic_scope),
75+ UInt32 (atomic_memory_semantics (Val ($ as))), val)
76+
77+ @device_function atomic_cmpxchg! (p:: LLVMPtr{$gentype,$as} , cmp:: $gentype , val:: $gentype ) =
78+ @builtin_ccall (" __spirv_AtomicCompareExchange" , $ gentype,
79+ (LLVMPtr{$ gentype,$ as}, UInt32, UInt32, UInt32, $ gentype, $ gentype),
80+ p, UInt32 (atomic_scope),
81+ UInt32 (atomic_memory_semantics (Val ($ as))),
82+ UInt32 (atomic_memory_semantics (Val ($ as))), val, cmp)
6183end
6284end
6385
248270
249271# native atomics
250272# TODO : support inc/dec
251- # TODO : this depends on available extensions
252- # - UInt64: requires cl_khr_int64_base_atomics for add/sub/inc/dec,
253- # requires cl_khr_int64_extended_atomics for min/max/and/or/xor
254- # - Float64: always should hit the fallback
273+ # TODO : this depends on backend support for the corresponding SPIR-V atomic
274+ # operation. Floating-point arithmetic should hit the cmpxchg fallback
275+ # unless a caller explicitly uses a floating-point atomic extension.
255276for (op,impl) in [(+ ) => atomic_add!,
256277 (- ) => atomic_sub!,
257278 (& ) => atomic_and!,
@@ -265,7 +286,7 @@ for (op,impl) in [(+) => atomic_add!,
265286end
266287
267288# fallback using compare-and-swap
268- # TODO : for 64-bit types, this depends on cl_khr_int64_base_atomics
289+ # TODO : for 64-bit types, this depends on backend support for 64-bit cmpxchg.
269290function atomic_arrayset (A:: AbstractArray{T} , I:: Integer , op:: Function , val) where {T}
270291 ptr = pointer (A, I)
271292 old = Base. unsafe_load (ptr, 1 )
0 commit comments