Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix the optimizer's use of cached varargs type info for _apply unrolling #28079

Merged
merged 2 commits into from
Jul 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 43 additions & 47 deletions base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -557,22 +557,43 @@ function spec_lambda(@nospecialize(atype), sv::OptimizationState, @nospecialize(
linfo
end

function rewrite_apply_exprargs!(ir::IRCode, idx::Int, argexprs::Vector{Any}, sv::OptimizationState)
function rewrite_apply_exprargs!(ir::IRCode, idx::Int, argexprs::Vector{Any}, atypes::Vector{Any}, sv::OptimizationState)
new_argexprs = Any[argexprs[2]]
# Flatten all tuples
for arg in argexprs[3:end]
tupT = argextype(arg, ir, sv.sp)
t = widenconst(tupT)
for i = 1:length(t.parameters)
# Insert a getfield call here
new_call = Expr(:call, Core.getfield, arg, i)
typ = getfield_tfunc(tupT, Const(i))
new_arg = insert_node!(ir, idx, typ, new_call)
push!(new_argexprs, new_arg)
new_atypes = Any[atypes[2]]
# loop over original arguments and flatten any known iterators
for i in 3:length(argexprs)
def = argexprs[i]
# As a special case, if we can see the tuple() call, look at it's arguments to find
# our types. They can be more precise (e.g. f(Bool, A...) would be lowered as
# _apply(f, tuple(Bool)::Tuple{DataType}, A), which might not be precise enough to
# get a good method match). This pattern is used in the array code a bunch.
if isa(def, SSAValue) && is_tuple_call(ir, ir[def])
def_args = ir[def].args
def_atypes = Any[argextype(def_args[i], ir, sv.sp) for i in 2:length(def_args)]
elseif isa(def, Argument) && def.n === length(ir.argtypes) && !isempty(sv.result_vargs)
def_atypes = sv.result_vargs
else
def_atypes = Any[]
for p in widenconst(atypes[i]).parameters
if isa(p, DataType) && isdefined(p, :instance)
# replace singleton types with their equivalent Const object
p = Const(p.instance)
elseif isconstType(p)
p = Const(p.parameters[1])
end
push!(def_atypes, p)
end
end
# now push flattened types into new_atypes and getfield exprs into new_argexprs
for j in 1:length(def_atypes)
def_atype = def_atypes[j]
new_call = Expr(:call, Core.getfield, def, j)
new_argexpr = insert_node!(ir, idx, def_atype, new_call)
push!(new_argexprs, new_argexpr)
push!(new_atypes, def_atype)
end
end
argexprs = new_argexprs
return argexprs
return new_argexprs, new_atypes
end

function rewrite_invoke_exprargs!(inserter, argexprs::Vector{Any})
Expand Down Expand Up @@ -780,52 +801,27 @@ function assemble_inline_todo!(ir::IRCode, linetable::Vector{LineInfoNode}, sv::
isapply = isinvoke = false

# Handle _apply
isapply = false
if f === Core._apply
new_atypes = Any[]
ft = argextype(stmt.args[2], ir, sv.sp)
ft = atypes[2]
has_free_typevars(ft) && continue
f = singleton_type(ft)
# Push function type
push!(new_atypes, ft)
# Try to figure out the signature of the function being called
# and if rewrite_apply_exprargs can deal with this form
ok = true
for (typ, def) in zip(atypes[3:end], stmt.args[3:end])
typ = widenconst(typ)
# We don't know what'll be in a SimpleVector, bail out
isa(typ, SimpleVector) && (ok = false; break)
for i = 3:length(atypes)
typ = widenconst(atypes[i])
# TODO: We could basically run the iteration protocol here
if !isa(typ, DataType) || typ.name !== Tuple.name || isvatuple(typ)
if !isa(typ, DataType) || typ.name !== Tuple.name ||
isvatuple(typ) || length(typ.parameters) > sv.params.MAX_TUPLE_SPLAT
ok = false
break
end
if length(typ.parameters) > sv.params.MAX_TUPLE_SPLAT
ok = false
break
end
# As a special case, if we can see the tuple() call, look at it's arguments to find
# our types. They can be more precise (e.g. f(Bool, A...) would be lowered as
# _apply(f, tuple(Bool)::Tuple{DataType}, A), which might not be precise enough to
# get a good method match). This pattern is used in the array code a bunch.
if isa(def, SSAValue) && is_tuple_call(ir, ir[def])
for tuparg in ir[def].args[2:end]
push!(new_atypes, argextype(tuparg, ir, sv.sp))
end
elseif isa(def, Argument) && def.n === length(ir.argtypes) && !isempty(sv.result_vargs)
append!(new_atypes, sv.result_vargs)
else
append!(new_atypes, typ.parameters)
end
end
ok || continue
atypes = new_atypes
isapply = true
end

# Independent of whether we can inline, the above analysis allows us to rewrite
# this apply call to a regular call
if isapply
stmt.args = rewrite_apply_exprargs!(ir, idx, stmt.args, sv)
# Independent of whether we can inline, the above analysis allows us to rewrite
# this apply call to a regular call
stmt.args, atypes = rewrite_apply_exprargs!(ir, idx, stmt.args, atypes, sv)
end

if f !== Core.invoke && (isa(f, IntrinsicFunction) || ft ⊑ IntrinsicFunction || isa(f, Builtin) || ft ⊑ Builtin)
Expand Down
9 changes: 9 additions & 0 deletions test/compiler/compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1739,3 +1739,12 @@ T27078 = Vector{Vector{T}} where T
# issue #28070
g28070(f, args...) = f(args...)
@test @inferred g28070(Core._apply, Base.:/, (1.0, 1.0)) == 1.0

# issue #28079
struct Foo28079 end
@inline h28079(x, args...) = g28079(x, args...)
@inline g28079(::Any, f, args...) = f(args...)
test28079(p, n, m) = h28079(Foo28079(), Base.pointerref, p, n, m)
cinfo_unoptimized = code_typed(test28079, (Ptr{Float32}, Int, Int); optimize=false)[].first
cinfo_optimized = code_typed(test28079, (Ptr{Float32}, Int, Int); optimize=true)[].first
@test cinfo_unoptimized.ssavaluetypes[end-1] === cinfo_optimized.ssavaluetypes[end-1] === Float32