Skip to content

Changing _setindex(v, i::Integer, first, tail...) implementation to help constant propagation #39944

@OlivierHnt

Description

@OlivierHnt

Follow-up of this discourse thread.

Thanks to @mbauman's investigation, it seems that we loose constant propagation in the definition of _setindex:

return (ifelse(i == 1, v, first), _setindex(v, i - 1, tail...)...)

To illustrate, the following MWE (tested on v1.5.3 and v1.6) shows that the compiler cannot predict the dimension of selectdim(A, 1, 1) if A is an AbstractArray of dimension strictly greater than 2:

julia> f(A) = selectdim(A, 1, 1);

julia> A = rand(2,2);

julia> @code_warntype f(A)
Variables
  #self#::Core.Compiler.Const(f, false)
  A::Array{Float64,2}

Body::SubArray{Float64,1,Array{Float64,2},Tuple{Int64,Base.Slice{Base.OneTo{Int64}}},true}
1%1 = Main.selectdim(A, 1, 1)::SubArray{Float64,1,Array{Float64,2},Tuple{Int64,Base.Slice{Base.OneTo{Int64}}},true}
└──      return %1

julia> A = rand(2,2,2);

julia> @code_warntype f(A)
Variables
  #self#::Core.Compiler.Const(f, false)
  A::Array{Float64,3}

Body::Union{SubArray{Float64,2,Array{Float64,3},Tuple{Int64,Base.Slice{Base.OneTo{Int64}},Base.Slice{Base.OneTo{Int64}}},true}, SubArray{Float64,1,Array{Float64,3},Tuple{Int64,Base.Slice{Base.OneTo{Int64}},Int64},true}}
1%1 = Main.selectdim(A, 1, 1)::Union{SubArray{Float64,2,Array{Float64,3},Tuple{Int64,Base.Slice{Base.OneTo{Int64}},Base.Slice{Base.OneTo{Int64}}},true}, SubArray{Float64,1,Array{Float64,3},Tuple{Int64,Base.Slice{Base.OneTo{Int64}},Int64},true}}
└──      return %1

In the linked discourse discussion, @mbauman proposed the following solution:

julia> @inline function Base._setindex(v, i::Int, args...)
           ntuple(dim->ifelse(i==dim, v, args[dim]), length(args))
       end

julia> f(A)
2×2 view(::Array{Float64, 3}, 1, :, :) with eltype Float64:
 0.901717  0.427261
 0.130018  0.669248

julia> @code_warntype f(A)
Variables
  #self#::Core.Const(f)
  A::Array{Float64, 3}

Body::SubArray{Float64, 2, Array{Float64, 3}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}, Base.Slice{Base.OneTo{Int64}}}, true}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions