diff --git a/base/atomics.jl b/base/atomics.jl index 1a980eb6561ec..6fd9381a4f5f8 100644 --- a/base/atomics.jl +++ b/base/atomics.jl @@ -350,27 +350,28 @@ import ..Base.gc_alignment # All atomic operations have acquire and/or release semantics, depending on # whether the load or store values. Most of the time, this is what one wants # anyway, and it's only moderately expensive on most hardware. -for typ in atomictypes +for typ in atomictypes, container in [Atomic, Ptr] lt = llvmtypes[typ] ilt = llvmtypes[inttype(typ)] rt = "$lt, $lt*" irt = "$ilt, $ilt*" - @eval getindex(x::Atomic{$typ}) = - llvmcall($""" - %ptr = inttoptr i$WORD_SIZE %0 to $lt* - %rv = load atomic $rt %ptr acquire, align $(gc_alignment(typ)) - ret $lt %rv - """, $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x)) - @eval setindex!(x::Atomic{$typ}, v::$typ) = - llvmcall($""" - %ptr = inttoptr i$WORD_SIZE %0 to $lt* - store atomic $lt %1, $lt* %ptr release, align $(gc_alignment(typ)) - ret void - """, Cvoid, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) - + if container == Atomic + @eval getindex(x::Atomic{$typ}) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rv = load atomic $rt %ptr acquire, align $(gc_alignment(typ)) + ret $lt %rv + """, $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x)) + @eval setindex!(x::Atomic{$typ}, v::$typ) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + store atomic $lt %1, $lt* %ptr release, align $(gc_alignment(typ)) + ret void + """, Cvoid, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) + end # Note: atomic_cas! succeeded (i.e. it stored "new") if and only if the result is "cmp" if typ <: Integer - @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = + @eval atomic_cas!(x::$container{$typ}, cmp::$typ, new::$typ) = llvmcall($""" %ptr = inttoptr i$WORD_SIZE %0 to $lt* %rs = cmpxchg $lt* %ptr, $lt %1, $lt %2 acq_rel acquire @@ -379,7 +380,7 @@ for typ in atomictypes """, $typ, Tuple{Ptr{$typ},$typ,$typ}, unsafe_convert(Ptr{$typ}, x), cmp, new) else - @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = + @eval atomic_cas!(x::$container{$typ}, cmp::$typ, new::$typ) = llvmcall($""" %iptr = inttoptr i$WORD_SIZE %0 to $ilt* %icmp = bitcast $lt %1 to $ilt @@ -402,15 +403,14 @@ for typ in atomictypes end if rmwop in arithmetic_ops && !(typ <: ArithmeticTypes) continue end if typ <: Integer - @eval $fn(x::Atomic{$typ}, v::$typ) = + @eval $fn(x::$container{$typ}, v::$typ) = llvmcall($""" %ptr = inttoptr i$WORD_SIZE %0 to $lt* %rv = atomicrmw $rmw $lt* %ptr, $lt %1 acq_rel ret $lt %rv """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) - else - rmwop === :xchg || continue - @eval $fn(x::Atomic{$typ}, v::$typ) = + elseif rmwop === :xchg + @eval $fn(x::$container{$typ}, v::$typ) = llvmcall($""" %iptr = inttoptr i$WORD_SIZE %0 to $ilt* %ival = bitcast $lt %1 to $ilt @@ -418,15 +418,24 @@ for typ in atomictypes %rv = bitcast $ilt %irv to $lt ret $lt %rv """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) + elseif (rmwop === :add || rmwop === :sub) && (typ == Float32 || typ == Float64) + rmw = (rmwop === :add ? "fadd" : "fsub") + @eval $fn(x::$container{$typ}, v::$typ) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rv = atomicrmw $rmw $lt* %ptr, $lt %1 acq_rel + ret $lt %rv + """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) end end end -# Provide atomic floating-point operations via atomic_cas! +# Provide atomic floating-point operations via atomic_cas! for those who lack support by llvm const opnames = Dict{Symbol, Symbol}(:+ => :add, :- => :sub) -for op in [:+, :-, :max, :min] +for op in [:+, :-, :max, :min], container in [Atomic, Ptr] + fT = (op == :+ || op == :-) ? Float16 : FloatTypes opname = get(opnames, op, op) - @eval function $(Symbol("atomic_", opname, "!"))(var::Atomic{T}, val::T) where T<:FloatTypes + @eval function $(Symbol("atomic_", opname, "!"))(var::$container{T}, val::T) where T<:$fT IT = inttype(T) old = var[] while true @@ -440,6 +449,7 @@ for op in [:+, :-, :max, :min] end end + """ Threads.atomic_fence() diff --git a/test/threads_exec.jl b/test/threads_exec.jl index 691fca2fb2afa..e9badef8b1a54 100644 --- a/test/threads_exec.jl +++ b/test/threads_exec.jl @@ -377,6 +377,28 @@ let atomictypes = intersect((Int8, Int16, Int32, Int64, Int128, end end +#Test that atomic operations work (at all) on pointers +#Since code is shared with ops on Atomic{T}, there is no need to test atomicity separately. +let atomictypes = intersect((Int8, Int16, Int32, Int64, Int128, + UInt8, UInt16, UInt32, UInt64, UInt128, + Float16, Float32, Float64), Base.Threads.atomictypes) + + for T in atomictypes + arr = [zero(T)] + ptr = pointer(arr) + GC.@preserve arr begin + @test T(0) == atomic_add!(ptr, T(1)) + @test T(1) == atomic_xchg!(ptr, T(4)) + @test T(4) == atomic_sub!(ptr, T(1)) + @test T(3) == atomic_max!(ptr, T(7)) + @test T(7) == atomic_min!(ptr, T(10)) + @test T(7) == atomic_cas!(ptr, T(7), T(5)) + @test T(5) == atomic_cas!(ptr, T(7), T(6)) + @test arr[1] == T(5) + end + end +end + # Test atomic_cas! and atomic_xchg! function test_atomic_cas!(var::Atomic{T}, range::StepRange{Int,Int}) where T for i in range