Skip to content

Commit 65a82e8

Browse files
committed
allow @nospecialize-d push! to take arbitrary items
Currently the `@nospecialize`-d `push!(::Vector{Any}, ...)` can only take a single item and we will end up with runtime dispatch when we try to call it with multiple items: ```julia julia> code_typed(push!, (Vector{Any}, Any)) 1-element Vector{Any}: CodeInfo( 1 ─ $(Expr(:foreigncall, :(:jl_array_grow_end), Nothing, svec(Any, UInt64), 0, :(:ccall), Core.Argument(2), 0x0000000000000001, 0x0000000000000001))::Nothing │ %2 = Base.arraylen(a)::Int64 │ Base.arrayset(true, a, item, %2)::Vector{Any} └── return a ) => Vector{Any} julia> code_typed(push!, (Vector{Any}, Any, Any)) 1-element Vector{Any}: CodeInfo( 1 ─ %1 = Base.append!(a, iter)::Vector{Any} └── return %1 ) => Vector{Any} ``` This commit extends it so that it can take arbitrary-length items. Our compiler should still be able to optimize the single-input case as before by unrolling using the constant item length information: ```julia julia> code_typed(push!, (Vector{Any}, Any)) 1-element Vector{Any}: CodeInfo( 1 ─ $(Expr(:foreigncall, :(:jl_array_grow_end), Nothing, svec(Any, UInt64), 0, :(:ccall), Core.Argument(2), 0x0000000000000001, 0x0000000000000001))::Nothing │ %2 = Base.arraylen(a)::Int64 │ Base.arrayset(true, a, item, %2)::Vector{Any} └── return a ) => Vector{Any} julia> code_typed(push!, (Vector{Any}, Any, Any)) 1-element Vector{Any}: CodeInfo( 1 ─ %1 = Base.arraylen(a)::Int64 │ $(Expr(:foreigncall, :(:jl_array_grow_end), Nothing, svec(Any, UInt64), 0, :(:ccall), Core.Argument(2), 0x0000000000000002, 0x0000000000000002))::Nothing └── goto #7 if not true 2 ┄ %4 = φ (#1 => 1, #6 => %14)::Int64 │ %5 = φ (#1 => 1, #6 => %15)::Int64 │ %6 = Base.getfield(x, %4, true)::Any │ %7 = Base.add_int(%1, %4)::Int64 │ Base.arrayset(true, a, %6, %7)::Vector{Any} │ %9 = (%5 === 2)::Bool └── goto #4 if not %9 3 ─ goto #5 4 ─ %12 = Base.add_int(%5, 1)::Int64 └── goto #5 5 ┄ %14 = φ (#4 => %12)::Int64 │ %15 = φ (#4 => %12)::Int64 │ %16 = φ (#3 => true, #4 => false)::Bool │ %17 = Base.not_int(%16)::Bool └── goto #7 if not %17 6 ─ goto #2 7 ┄ return a ) => Vector{Any} ```
1 parent 51c8812 commit 65a82e8

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

base/array.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,9 +1053,13 @@ function push!(a::Array{T,1}, item) where T
10531053
return a
10541054
end
10551055

1056-
function push!(a::Array{Any,1}, @nospecialize item)
1057-
_growend!(a, 1)
1058-
arrayset(true, a, item, length(a))
1056+
function push!(a::Vector{Any}, @nospecialize x...)
1057+
na = length(a)
1058+
nx = length(x)
1059+
_growend!(a, nx)
1060+
for i = 1:nx
1061+
arrayset(true, a, x[i], na+i)
1062+
end
10591063
return a
10601064
end
10611065

test/compiler/inline.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,3 +1375,21 @@ let src = code_typed1() do
13751375
@test count(isnew, src.code) == 1
13761376
@test count(isinvoke(:noinline_finalizer), src.code) == 1
13771377
end
1378+
1379+
# optimize `push!(::Vector{Any}, x...)`
1380+
let src = code_typed1((Vector{Any}, Any)) do xs, x
1381+
push!(xs, x)
1382+
end
1383+
@test count(iscall((src, push!)), src.code) == 0
1384+
end
1385+
let src = code_typed1((Vector{Any}, Any, Any)) do xs, x, y
1386+
push!(xs, x, y)
1387+
end
1388+
@test count(iscall((src, push!)), src.code) == 0
1389+
end
1390+
let xs = Any[]
1391+
push!(xs, :x, "y", 'z')
1392+
@test xs[1] === :x
1393+
@test xs[2] == "y"
1394+
@test xs[3] === 'z'
1395+
end

0 commit comments

Comments
 (0)