Skip to content

Commit b7637cb

Browse files
committed
add atomics builtins for globals, memory, and setonce
Adds an AtomicMemory{T} type (GenericMemory{:atomic,T,Core.CPU}) for doing element-wise atomic operations on an array (with a lock per-element if required). Adds the full complement of atomic operations for global variables. Also add a *setonce set of functions for atomically setting a value, but only if it is unset. The complementary operation (Atomic `_unsetindex!` for AtomicMemory) is not yet provided.
1 parent 3d8b107 commit b7637cb

38 files changed

+3211
-1041
lines changed

base/Base.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,36 @@ function replaceproperty!(x, f::Symbol, expected, desired, success_order::Symbol
9191
val = desired isa ty ? desired : convert(ty, desired)
9292
return Core.replacefield!(x, f, expected, val, success_order, fail_order)
9393
end
94+
function setpropertyonce!(x, f::Symbol, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order)
95+
@inline
96+
ty = fieldtype(typeof(x), f)
97+
val = desired isa ty ? desired : convert(ty, desired)
98+
return Core.setfieldonce!(x, f, val, success_order, fail_order)
99+
end
100+
101+
function swapproperty!(x::Module, f::Symbol, v, order::Symbol=:not_atomic)
102+
@inline
103+
ty = Core.get_binding_type(x, f)
104+
val = v isa ty ? v : convert(ty, v)
105+
return Core.swapglobal!(x, f, val, order)
106+
end
107+
function modifyproperty!(x::Module, f::Symbol, op, v, order::Symbol=:not_atomic)
108+
@inline
109+
return Core.modifyglobal!(x, f, op, v, order)
110+
end
111+
function replaceproperty!(x::Module, f::Symbol, expected, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order)
112+
@inline
113+
ty = Core.get_binding_type(x, f)
114+
val = desired isa ty ? desired : convert(ty, desired)
115+
return Core.replaceglobal!(x, f, expected, val, success_order, fail_order)
116+
end
117+
function setpropertyonce!(x::Module, f::Symbol, desired, success_order::Symbol=:not_atomic, fail_order::Symbol=success_order)
118+
@inline
119+
ty = Core.get_binding_type(x, f)
120+
val = desired isa ty ? desired : convert(ty, desired)
121+
return Core.setglobalonce!(x, f, val, success_order, fail_order)
122+
end
123+
94124

95125
convert(::Type{Any}, Core.@nospecialize x) = x
96126
convert(::Type{T}, x::T) where {T} = x

base/boot.jl

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
#end
6666

6767
# struct GenericMemoryRef{kind::Symbol, T, AS::AddrSpace}
68-
# mem::Memory{kind, T, AS}
68+
# mem::GenericMemory{kind, T, AS}
6969
# data::Ptr{Cvoid} # make this GenericPtr{addrspace, Cvoid}
7070
#end
7171

@@ -191,8 +191,8 @@ export
191191
Tuple, Type, UnionAll, TypeVar, Union, Nothing, Cvoid,
192192
AbstractArray, DenseArray, NamedTuple, Pair,
193193
# special objects
194-
Function, Method, Array, Memory, MemoryRef, GenericMemory, GenericMemoryRef,
195-
Module, Symbol, Task, UndefInitializer, undef, WeakRef, VecElement,
194+
Function, Method, Module, Symbol, Task, UndefInitializer, undef, WeakRef, VecElement,
195+
Array, Memory, MemoryRef, AtomicMemory, AtomicMemoryRef, GenericMemory, GenericMemoryRef,
196196
# numeric types
197197
Number, Real, Integer, Bool, Ref, Ptr,
198198
AbstractFloat, Float16, Float32, Float64,
@@ -209,10 +209,10 @@ export
209209
# AST representation
210210
Expr, QuoteNode, LineNumberNode, GlobalRef,
211211
# object model functions
212-
fieldtype, getfield, setfield!, swapfield!, modifyfield!, replacefield!,
212+
fieldtype, getfield, setfield!, swapfield!, modifyfield!, replacefield!, setfieldonce!,
213213
nfields, throw, tuple, ===, isdefined, eval,
214214
# access to globals
215-
getglobal, setglobal!,
215+
getglobal, setglobal!, swapglobal!, modifyglobal!, replaceglobal!, setglobalonce!,
216216
# ifelse, sizeof # not exported, to avoid conflicting with Base
217217
# type reflection
218218
<:, typeof, isa, typeassert,
@@ -516,22 +516,24 @@ const undef = UndefInitializer()
516516
(self::Type{GenericMemory{kind,T,addrspace}})(::UndefInitializer, d::NTuple{1,Int}) where {T,kind,addrspace} = self(undef, getfield(d,1))
517517
# empty vector constructor
518518
(self::Type{GenericMemory{kind,T,addrspace}})() where {T,kind,addrspace} = self(undef, 0)
519-
# copy constructors
520519

521-
const Memory{T} = GenericMemory{:not_atomic, T, CPU}
522-
const MemoryRef{T} = GenericMemoryRef{:not_atomic, T, CPU}
523520
GenericMemoryRef(mem::GenericMemory) = memoryref(mem)
524521
GenericMemoryRef(ref::GenericMemoryRef, i::Integer) = memoryref(ref, Int(i), @_boundscheck)
525522
GenericMemoryRef(mem::GenericMemory, i::Integer) = memoryref(memoryref(mem), Int(i), @_boundscheck)
526-
MemoryRef(mem::Memory) = memoryref(mem)
527-
MemoryRef(ref::MemoryRef, i::Integer) = memoryref(ref, Int(i), @_boundscheck)
528-
MemoryRef(mem::Memory, i::Integer) = memoryref(memoryref(mem), Int(i), @_boundscheck)
529-
MemoryRef{T}(mem::Memory{T}) where {T} = memoryref(mem)
530-
MemoryRef{T}(ref::MemoryRef{T}, i::Integer) where {T} = memoryref(ref, Int(i), @_boundscheck)
531-
MemoryRef{T}(mem::Memory{T}, i::Integer) where {T} = memoryref(memoryref(mem), Int(i), @_boundscheck)
523+
GenericMemoryRef{kind,<:Any,AS}(mem::GenericMemory{kind,<:Any,AS}) where {kind,AS} = memoryref(mem)
524+
GenericMemoryRef{kind,<:Any,AS}(ref::GenericMemoryRef{kind,<:Any,AS}, i::Integer) where {kind,AS} = memoryref(ref, Int(i), @_boundscheck)
525+
GenericMemoryRef{kind,<:Any,AS}(mem::GenericMemory{kind,<:Any,AS}, i::Integer) where {kind,AS} = memoryref(memoryref(mem), Int(i), @_boundscheck)
526+
GenericMemoryRef{kind,T,AS}(mem::GenericMemory{kind,T,AS}) where {kind,T,AS} = memoryref(mem)
527+
GenericMemoryRef{kind,T,AS}(ref::GenericMemoryRef{kind,T,AS}, i::Integer) where {kind,T,AS} = memoryref(ref, Int(i), @_boundscheck)
528+
GenericMemoryRef{kind,T,AS}(mem::GenericMemory{kind,T,AS}, i::Integer) where {kind,T,AS} = memoryref(memoryref(mem), Int(i), @_boundscheck)
529+
530+
const Memory{T} = GenericMemory{:not_atomic, T, CPU}
531+
const MemoryRef{T} = GenericMemoryRef{:not_atomic, T, CPU}
532+
const AtomicMemory{T} = GenericMemory{:atomic, T, CPU}
533+
const AtomicMemoryRef{T} = GenericMemoryRef{:atomic, T, CPU}
532534

533535
# construction helpers for Array
534-
new_as_memoryref(self::Type{GenericMemoryRef{isatomic,T,addrspace}}, m::Int) where {T,isatomic,addrspace} = memoryref(fieldtype(self, :mem)(undef, m))
536+
new_as_memoryref(self::Type{GenericMemoryRef{kind,T,addrspace}}, m::Int) where {T,kind,addrspace} = memoryref(fieldtype(self, :mem)(undef, m))
535537

536538
# checked-multiply intrinsic function for dimensions
537539
_checked_mul_dims() = 1, false

base/compiler/abstractinterpretation.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,12 +1113,12 @@ function const_prop_function_heuristic(interp::AbstractInterpreter, @nospecializ
11131113
if !still_nothrow || ismutabletype(arrty)
11141114
return false
11151115
end
1116-
elseif (𝕃ᵢ, arrty, Array)
1116+
elseif (𝕃ᵢ, arrty, Array) || (𝕃ᵢ, arrty, GenericMemory)
11171117
return false
11181118
end
11191119
elseif istopfunction(f, :iterate)
11201120
itrty = argtypes[2]
1121-
if (𝕃ᵢ, itrty, Array)
1121+
if (𝕃ᵢ, itrty, Array) || (𝕃ᵢ, itrty, GenericMemory)
11221122
return false
11231123
end
11241124
end
@@ -1501,7 +1501,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
15011501
# Return Bottom if this is not an iterator.
15021502
# WARNING: Changes to the iteration protocol must be reflected here,
15031503
# this is not just an optimization.
1504-
# TODO: this doesn't realize that Array, SimpleVector, Tuple, and NamedTuple do not use the iterate protocol
1504+
# TODO: this doesn't realize that Array, GenericMemory, SimpleVector, Tuple, and NamedTuple do not use the iterate protocol
15051505
stateordonet === Bottom && return AbstractIterationResult(Any[Bottom], AbstractIterationInfo(CallMeta[CallMeta(Bottom, Any, call.effects, info)], true))
15061506
valtype = statetype = Bottom
15071507
ret = Any[]
@@ -2076,8 +2076,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
20762076
return abstract_apply(interp, argtypes, si, sv, max_methods)
20772077
elseif f === invoke
20782078
return abstract_invoke(interp, arginfo, si, sv)
2079-
elseif f === modifyfield!
2080-
return abstract_modifyfield!(interp, argtypes, si, sv)
2079+
elseif f === modifyfield! || f === Core.modifyglobal! || f === Core.memoryrefmodify! || f === atomic_pointermodify
2080+
return abstract_modifyop!(interp, f, argtypes, si, sv)
20812081
elseif f === Core.finalizer
20822082
return abstract_finalizer(interp, argtypes, sv)
20832083
elseif f === applicable

base/compiler/ssair/inlining.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,10 +1286,18 @@ function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stat
12861286
end
12871287
end
12881288

1289-
if (sig.f !== Core.invoke && sig.f !== Core.finalizer && sig.f !== modifyfield!) &&
1290-
is_builtin(optimizer_lattice(state.interp), sig)
1291-
# No inlining for builtins (other invoke/apply/typeassert/finalizer)
1292-
return nothing
1289+
if is_builtin(optimizer_lattice(state.interp), sig)
1290+
let f = sig.f
1291+
if (f !== Core.invoke &&
1292+
f !== Core.finalizer &&
1293+
f !== modifyfield! &&
1294+
f !== Core.modifyglobal! &&
1295+
f !== Core.memoryrefmodify! &&
1296+
f !== atomic_pointermodify)
1297+
# No inlining defined for most builtins (just invoke/apply/typeassert/finalizer), so attempt an early exit for them
1298+
return nothing
1299+
end
1300+
end
12931301
end
12941302

12951303
# Special case inliners for regular functions
@@ -1571,7 +1579,7 @@ function handle_opaque_closure_call!(todo::Vector{Pair{Int,Any}},
15711579
return nothing
15721580
end
15731581

1574-
function handle_modifyfield!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyFieldInfo, state::InliningState)
1582+
function handle_modifyop!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyOpInfo, state::InliningState)
15751583
info = info.info
15761584
info isa MethodResultPure && (info = info.info)
15771585
info isa ConstCallInfo && (info = info.call)
@@ -1679,8 +1687,8 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
16791687
# handle special cased builtins
16801688
if isa(info, OpaqueClosureCallInfo)
16811689
handle_opaque_closure_call!(todo, ir, idx, stmt, info, flag, sig, state)
1682-
elseif isa(info, ModifyFieldInfo)
1683-
handle_modifyfield!_call!(ir, idx, stmt, info, state)
1690+
elseif isa(info, ModifyOpInfo)
1691+
handle_modifyop!_call!(ir, idx, stmt, info, state)
16841692
elseif isa(info, InvokeCallInfo)
16851693
handle_invoke_call!(todo, ir, idx, stmt, info, flag, sig, state)
16861694
elseif isa(info, FinalizerInfo)

base/compiler/stmtinfo.jl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,18 @@ struct FinalizerInfo <: CallInfo
222222
end
223223

224224
"""
225-
info::ModifyFieldInfo <: CallInfo
225+
info::ModifyOpInfo <: CallInfo
226226
227-
Represents a resolved all of `modifyfield!(obj, name, op, x, [order])`.
228-
`info.info` wraps the call information of `op(getfield(obj, name), x)`.
227+
Represents a resolved call of one of:
228+
- `modifyfield!(obj, name, op, x, [order])`
229+
- `modifyglobal!(mod, var, op, x, order)`
230+
- `memoryrefmodify!(memref, op, x, order, boundscheck)`
231+
- `Intrinsics.atomic_pointermodify(ptr, op, x, order)`
232+
233+
`info.info` wraps the call information of `op(getval(), x)`.
229234
"""
230-
struct ModifyFieldInfo <: CallInfo
231-
info::CallInfo # the callinfo for the `op(getfield(obj, name), x)` call
235+
struct ModifyOpInfo <: CallInfo
236+
info::CallInfo # the callinfo for the `op(getval(), x)` call
232237
end
233238

234239
@specialize

0 commit comments

Comments
 (0)