Mutating immutables through references


#1

Modifying immutables is annoying. In many cases, one needs to operate on objects with well-defined heap addresses. My current approach is to simply extract the pointer, and pass it to my mutating function. Say, e.g., I have a vec = Vector{somestruct{N}}, and somestruct contains an N-tuple that needs to be changed; then my code has some_function!(Ptr{somestruct{N}}) and gets used by some_function!(pointer(vec,i)). I wanted to ask:

  1. Is the recommended way of going about it?
  2. Is any of the related juleps https://github.com/JuliaLang/julia/issues/17115 https://github.com/JuliaLang/julia/issues/11902 https://github.com/JuliaLang/julia/pull/21912 planned for inclusion in 0.7 / 1.0.
  3. Is there a description somewhere of what I need to think about in order to be 0.7 compatible, with respect to tbaa?

For example, sorting a tuple is a pain (without allocations). Luckily, I can simply call the factory-provided insertion sort! by wrapping a pointer to the tuple into a thin array.

edit: Thin array means e.g.

struct ptrArray{N,T}<:AbstractVector{T}
    ptr::Ptr{T}
end
ptrArray(ptr::Ptr{NTuple{N,T}}) where {N,T} = ptrArray{N,T}(convert(Ptr{T}, ptr))
Base.length(x::ptrArray{N,T}) where {N,T} =N 
Base.size(x::ptrArray{N,T}) where {N,T} =(N,) 
Base.getindex(x::ptrArray,i) = unsafe_load(x.ptr,i)
Base.setindex!(x::ptrArray,v,i) = unsafe_store!(x.ptr,v,i)
Base.IndexStyle(::ptrArray) = Base.IndexLinear()

#2

https://github.com/JuliaArrays/StaticArrays.jl/blob/96bf39c4b723538f2f92e6ad34ce075623e9e9fb/src/MArray.jl#L101 might be somewhat relevant.


#3

So this is the recommended way, thanks. Do you happen to know how the new pointer-tbaa rules on 0.7 work?

That is, if I have a pointer into a Float32-field of some immutable struct foo_T that is inline-stored inside an array, does the compiler figure out that it can alias with reads / writes of the entire struct? The way MArray works, it probably does not have that problem, because all pointer accesses are of the same type.

Or do I have to use @Keno 's new Intrinsics.tbaa_pointerref / Intrinsics.tbaa_pointerset in order to tell the compiler something? And if yes, do I need to propagate to the outermost isbits in case of nested structs?

Same for tuples: Can an Ptr{Int64} access alias with a Ptr{NTuple{N,Int64}} access or do I need to use the new intrinsics? Can I just tell the compiler that a specific access is of unknown tbaa type (aliasing is unrestricted by tbaa), in order to be safe until profiling shows that getting this right is important in my specific code?


#4

We don’t generally make any guarantees about the behavior of a program that uses unsafe_load / unsafe_store on a julia-derived value (such as a reference inside a Julia array). You may find that the compiler does what you want anyways: just be aware that it may break in any future version or patch release as the compiler changes.

If you have a malloc Ptr value, then you can perform any access of it that you want. Those operations (load / store) may be slow (e.g slower than accessing the same sort of value in C or Julia) and may slow down the rest of the function around them, but they are expected to work.