@@ -89,68 +89,12 @@ for (julia, llvm) in pairs(_llvm_from_julia_ordering)
8989 @eval llvm_from_julia_ordering (:: Val{$(QuoteNode(julia))} ) = Val {$llvm} ()
9090end
9191
92- """
93- @dynamic_order(order) do order
94- ... use order ...
95- end
96- It is expanded to an expression similar to:
97- if order === :not_atomic
98- let order = Val(:not_atomic)
99- ... use order ...
100- end
101- elseif order === :unordered
102- let order = Val(:unordered)
103- ... use order ...
104- end
105- elseif ...
106- ...
107- else
108- throw(ConcurrencyViolationError(...))
109- end
110- This is used for helping the compiler to optimize expressions such as
111- `atomic_pointerref(ptr, :monotonic)` and also to avoid abstract run-time dispatch.
112- """
113- macro dynamic_order (thunk, order)
114- @assert Meta. isexpr (thunk, :-> , 2 ) && Meta. isexpr (thunk. args[1 ], :tuple , 1 )
115- ordervar = esc (thunk. args[1 ]. args[1 ])
116- body = esc (thunk. args[2 ])
117- expr = foldr (
118- keys (_llvm_from_julia_ordering),
119- init = :(throw (ConcurrencyViolationError (" invalid atomic ordering: " , order))),
120- ) do key, r
121- quote
122- if order === $ (QuoteNode (key))
123- let $ ordervar = Val {$(QuoteNode(key))} ()
124- $ body
125- end
126- else
127- $ r
128- end
129- end
130- end
131- quote
132- order = $ (esc (order))
133- $ expr
134- end
135- end
136-
13792_valueof (:: Val{x} ) where {x} = x
13893
139- @inline function atomic_pointerref (pointer, order:: Symbol )
140- @dynamic_order (order) do order
141- atomic_pointerref (pointer, order)
142- end
143- end
144-
145- @inline function atomic_pointerset (pointer, x, order:: Symbol )
146- @dynamic_order (order) do order
147- atomic_pointerset (pointer, x, order)
148- end
149- end
150-
151- @generated function atomic_pointerref (ptr:: LLVMPtr{T,A} , order:: AllOrdering ) where {T,A}
94+ @generated function atomic_pointerref (ptr:: LLVMPtr{T,A} , order:: AllOrdering , sync) where {T,A}
15295 sizeof (T) == 0 && return T. instance
153- llvm_order = _valueof (llvm_from_julia_ordering (order ()))
96+ llvm_order = _valueof (llvm_from_julia_ordering (order ()))
97+ llvm_syncscope = _valueof (sync ())
15498 @dispose ctx = Context () begin
15599 eltyp = convert (LLVMType, T)
156100
170114 typed_ptr = bitcast! (builder, parameters (llvm_f)[1 ], T_typed_ptr)
171115 ld = load! (builder, eltyp, typed_ptr)
172116 ordering! (ld, llvm_order)
117+ syncscope! (ld, SyncScope (string (llvm_syncscope)))
173118
174119 if A != 0
175120 metadata (ld)[LLVM. MD_tbaa] = tbaa_addrspace (A)
187132 ptr:: LLVMPtr{T,A} ,
188133 x:: T ,
189134 order:: AllOrdering ,
135+ sync,
190136) where {T,A}
191137 if sizeof (T) == 0
192138 # Mimicking what `Core.Intrinsics.atomic_pointerset` generates.
197143 return ptr
198144 end
199145 end
200- llvm_order = _valueof (llvm_from_julia_ordering (order ()))
146+ llvm_order = _valueof (llvm_from_julia_ordering (order ()))
147+ llvm_syncscope = _valueof (sync ())
201148 @dispose ctx = Context () begin
202149 eltyp = convert (LLVMType, T)
203150 T_ptr = convert (LLVMType, ptr)
216163 val = parameters (llvm_f)[2 ]
217164 st = store! (builder, val, typed_ptr)
218165 ordering! (st, llvm_order)
166+ syncscope! (st, SyncScope (string (llvm_syncscope)))
219167
220168 if A != 0
221169 metadata (st)[LLVM. MD_tbaa] = tbaa_addrspace (A)
@@ -254,14 +202,12 @@ const binoptable = [
254202
255203const AtomicRMWBinOpVal = Union{(Val{binop} for (_, _, binop) in binoptable). .. }
256204
257- # LLVM API accepts string literal as a syncscope argument.
258- @inline syncscope_to_string (:: Type{Val{S}} ) where {S} = string (S)
259-
260205@generated function llvm_atomic_op (
261206 binop:: AtomicRMWBinOpVal ,
262207 ptr:: LLVMPtr{T,A} ,
263208 val:: T ,
264209 order:: LLVMOrderingVal ,
210+ sync,
265211) where {T,A}
266212 @dispose ctx = Context () begin
267213 T_val = convert (LLVMType, T)
@@ -270,21 +216,21 @@ const AtomicRMWBinOpVal = Union{(Val{binop} for (_, _, binop) in binoptable)...}
270216 T_typed_ptr = LLVM. PointerType (T_val, A)
271217
272218 llvm_f, _ = create_function (T_val, [T_ptr, T_val])
219+ llvm_syncscope = _valueof (sync ())
273220
274221 @dispose builder = IRBuilder () begin
275222 entry = BasicBlock (llvm_f, " entry" )
276223 position! (builder, entry)
277224
278225 typed_ptr = bitcast! (builder, parameters (llvm_f)[1 ], T_typed_ptr)
279226
280- single_threaded = false
281227 rv = atomic_rmw! (
282228 builder,
283229 _valueof (binop ()),
284230 typed_ptr,
285231 parameters (llvm_f)[2 ],
286232 _valueof (order ()),
287- single_threaded,
233+ SyncScope ( string (llvm_syncscope))
288234 )
289235
290236 ret! (builder, rv)
@@ -299,15 +245,17 @@ end
299245 ptr:: LLVMPtr{T,A} ,
300246 val:: T ,
301247 order:: LLVMOrderingVal ,
302- syncscope :: Val{S} ,
303- ) where {T,A,S }
248+ sync ,
249+ ) where {T,A}
304250 @dispose ctx = Context () begin
305251 T_val = convert (LLVMType, T)
306252 T_ptr = convert (LLVMType, ptr)
307253
308254 T_typed_ptr = LLVM. PointerType (T_val, A)
309255 llvm_f, _ = create_function (T_val, [T_ptr, T_val])
310256
257+ llvm_syncscope = _valueof (sync ())
258+
311259 @dispose builder = IRBuilder () begin
312260 entry = BasicBlock (llvm_f, " entry" )
313261 position! (builder, entry)
319267 typed_ptr,
320268 parameters (llvm_f)[2 ],
321269 _valueof (order ()),
322- syncscope_to_string (syncscope ),
270+ SyncScope ( string (llvm_syncscope) ),
323271 )
324272
325273 ret! (builder, rv)
@@ -328,118 +276,17 @@ end
328276 end
329277end
330278
331- @inline function atomic_pointermodify (pointer, op:: OP , x, order:: Symbol ) where {OP}
332- @dynamic_order (order) do order
333- atomic_pointermodify (pointer, op, x, order)
334- end
335- end
336-
337- @inline function atomic_pointermodify (
338- ptr:: LLVMPtr{T} ,
339- op,
340- x:: T ,
341- :: Val{:not_atomic} ,
342- ) where {T}
343- old = atomic_pointerref (ptr, Val (:not_atomic ))
344- new = op (old, x)
345- atomic_pointerset (ptr, new, Val (:not_atomic ))
346- return old => new
347- end
348-
349- @inline function atomic_pointermodify (
350- ptr:: LLVMPtr{T} ,
351- :: typeof (right),
352- x:: T ,
353- order:: AtomicOrdering ,
354- ) where {T}
355- old = llvm_atomic_op (
356- Val (LLVM. API. LLVMAtomicRMWBinOpXchg),
357- ptr,
358- x,
359- llvm_from_julia_ordering (order),
360- )
361- return old => x
362- end
363-
364- const atomictypes = Any[
365- Int8,
366- Int16,
367- Int32,
368- Int64,
369- Int128,
370- UInt8,
371- UInt16,
372- UInt32,
373- UInt64,
374- UInt128,
375- Float16,
376- Float32,
377- Float64,
378- ]
379-
380- for (opname, op, llvmop) in binoptable
381- opname === :xchg && continue
382- types = if opname in (:min , :max )
383- filter (t -> t <: Signed , atomictypes)
384- elseif opname in (:umin , :umax )
385- filter (t -> t <: Unsigned , atomictypes)
386- elseif opname in (:fadd , :fsub , :fmin , :fmax )
387- filter (t -> t <: AbstractFloat , atomictypes)
388- else
389- filter (t -> t <: Integer , atomictypes)
390- end
391- for T in types
392- @eval @inline function atomic_pointermodify (
393- ptr:: LLVMPtr{$T} ,
394- :: $ (typeof (op)),
395- x:: $T ,
396- order:: AtomicOrdering ,
397- syncscope:: Val{S} = Val {:system} (),
398- ) where {S}
399- old =
400- syncscope isa Val{:system } ?
401- llvm_atomic_op ($ (Val (llvmop)), ptr, x, llvm_from_julia_ordering (order)) :
402- llvm_atomic_op (
403- $ (Val (llvmop)),
404- ptr,
405- x,
406- llvm_from_julia_ordering (order),
407- syncscope,
408- )
409- return old => $ op (old, x)
410- end
411- end
412- end
413-
414- @inline atomic_pointerswap (pointer, new) = first (atomic_pointermodify (pointer, right, new))
415- @inline atomic_pointerswap (pointer, new, order) =
416- first (atomic_pointermodify (pointer, right, new, order))
417-
418- @inline function atomic_pointermodify (
419- ptr:: LLVMPtr{T} ,
420- op,
421- x:: T ,
422- order:: AllOrdering ,
423- ) where {T}
424- # Should `fail_order` be stronger? Ref: https://github.com/JuliaLang/julia/issues/45256
425- fail_order = Val (:monotonic )
426- old = atomic_pointerref (ptr, fail_order)
427- while true
428- new = op (old, x)
429- (old, success) = atomic_pointerreplace (ptr, old, new, order, fail_order)
430- success && return old => new
431- end
432- end
433-
434279@generated function llvm_atomic_cas (
435280 ptr:: LLVMPtr{T,A} ,
436281 cmp:: T ,
437282 val:: T ,
438283 success_order:: LLVMOrderingVal ,
439284 fail_order:: LLVMOrderingVal ,
285+ sync,
440286) where {T,A}
441287 llvm_success = _valueof (success_order ())
442288 llvm_fail = _valueof (fail_order ())
289+ llvm_syncscope = _valueof (sync ())
443290 @dispose ctx = Context () begin
444291 T_val = convert (LLVMType, T)
445292 T_pointee = T_val
@@ -471,15 +318,14 @@ end
471318 val_int = bitcast! (builder, val_int, T_pointee)
472319 end
473320
474- single_threaded = false
475321 res = atomic_cmpxchg! (
476322 builder,
477323 typed_ptr,
478324 cmp_int,
479325 val_int,
480326 llvm_success,
481327 llvm_fail,
482- single_threaded ,
328+ SyncScope ( string (llvm_syncscope)) ,
483329 )
484330
485331 rv = extract_value! (builder, res, 0 )
513359 end
514360 end
515361end
516-
517- @inline function atomic_pointerreplace (
518- pointer,
519- expected,
520- desired,
521- success_order:: Symbol ,
522- fail_order:: Symbol ,
523- )
524- # This avoids abstract dispatch at run-time but probably too much codegen?
525- #=
526- @dynamic_order(success_order) do success_order
527- @dynamic_order(fail_order) do fail_order
528- atomic_pointerreplace(pointer, expected, desired, success_order, fail_order)
529- end
530- end
531- =#
532-
533- # This avoids excessive codegen while hopefully imposes no cost when const-prop works:
534- so = @dynamic_order (success_order) do success_order
535- success_order
536- end
537- fo = @dynamic_order (fail_order) do fail_order
538- fail_order
539- end
540- return atomic_pointerreplace (pointer, expected, desired, so, fo)
541- end
542-
543- @inline function atomic_pointerreplace (
544- ptr:: LLVMPtr{T} ,
545- expected:: T ,
546- desired:: T ,
547- :: Val{:not_atomic} ,
548- :: Val{:not_atomic} ,
549- ) where {T}
550- old = atomic_pointerref (ptr, Val (:not_atomic ))
551- if old === expected
552- atomic_pointerset (ptr, desired, Val (:not_atomic ))
553- success = true
554- else
555- success = false
556- end
557- return (; old, success)
558- end
559-
560- @inline atomic_pointerreplace (
561- ptr:: LLVMPtr{T} ,
562- expected:: T ,
563- desired:: T ,
564- success_order:: _julia_ordering (∉ ((:not_atomic , :unordered ))),
565- fail_order:: _julia_ordering (∉ ((:not_atomic , :unordered , :release , :acquire_release ))),
566- ) where {T} = llvm_atomic_cas (
567- ptr,
568- expected,
569- desired,
570- llvm_from_julia_ordering (success_order),
571- llvm_from_julia_ordering (fail_order),
572- )
0 commit comments