I wanted to ask about the julia aliasing rules for memory accesses. Unfortunately, the docs are not super clear about that, e.g.
Julia currently uses LLVM’s Type Based Alias Analysis.
To find the comments that document the inclusion relationships, look forstatic MDNode*
insrc/codegen.cpp
The ultimate reason for my question is the following construction: It often makes sense to view the same chunk of memory as either v::Vector{SVector{N, T}}
or as mat::Matrix{T}
with size(mat) = (N, length(vec))
, such that v[idx] = mat[:, idx]
.
Base Reinterpret is inappropriate for this – performance simply sucks / is unreliable.
The classic solution is something like
v = ccall(:jl_reshape_array, Vector{SVector{size(mat)[1], eltype(mat)}}, (Any, Any, Any), Vector{SVector{size(mat)[1], eltype(mat)}}, mat, (size(mat)[2],))
Now my question is: Are reads/writes to v
and to mat
permitted to alias in julia? In theory and in practice?
Is there a programmatic way of finding out whether something is permitted to alias per TBAA?
The next question would be: Is TBAA applied to unsafe_store!
/ unsafe_load
?
If yes, could we maybe have an extra argument to Core.pointerref
et al to say that a load/store is intended to have ccall(:memcpy, ...)
semantics?
PS. It looks like they are allowed to alias in practice?
julia> mat = rand(Int64, 2, 2)
2×2 Matrix{Int64}:
6764168577710771209 -7368470990085874290
7379517068019859522 36090219286728105
julia> v=ccall(:jl_reshape_array, Vector{SVector{size(mat)[1], eltype(mat)}}, (Any, Any, Any), Vector{SVector{size(mat)[1], eltype(mat)}}, mat, (size(mat)[2],))
2-element Vector{SVector{2, Int64}}:
[6764168577710771209, 7379517068019859522]
[-7368470990085874290, 36090219286728105]
julia> function baz(v, mat)
@inbounds v0 = v[1]
@inbounds mat[1] += 1
@inbounds v1 = v[1]
v1==v0
end
baz (generic function with 1 method)
julia> baz(v,mat)
false
Is this guaranteed behavior that will not change before 2.0 ? Or is this code UB that the compiler is just not smart enough to break?
PS. Likewise,
julia> mat2=rand(2,2)
2×2 Matrix{Float64}:
0.932218 0.839201
0.810208 0.952793
julia> x=ccall(:jl_reshape_array, Matrix{Int64}, (Any, Any, Any), Matrix{Int64}, mat2, size(mat2))
2×2 Matrix{Int64}:
4611380756684956808 4605734070903863065
4605472925120407489 4606757212508987398
julia> baz(x, mat2)
false
Is this code UB or is it valid and will return false
until julia 2.0?
(cc @Keno because something along these lines triggered your ReinterpretArray rewrite years ago)