Mutating immutables through references

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()
1 Like

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

1 Like

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?

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.