Skip to content

Commit

Permalink
Inline invoke (take 3)
Browse files Browse the repository at this point in the history
Fix #9608
  • Loading branch information
yuyichao committed Jan 9, 2017
1 parent a430c07 commit 4d7a054
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 34 deletions.
180 changes: 148 additions & 32 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -736,8 +736,8 @@ function invoke_tfunc(f::ANY, types::ANY, argtype::ANY, sv::InferenceState)
return Any
end
meth = entry.func
(ti, env) = ccall(:jl_match_method, Any, (Any, Any, Any),
argtype, meth.sig, meth.tvars)::SimpleVector
(ti, env) = ccall(:jl_match_method, Ref{SimpleVector}, (Any, Any, Any),
argtype, meth.sig, meth.tvars)
return typeinf_edge(meth::Method, ti, env, sv)
end

Expand Down Expand Up @@ -2693,14 +2693,34 @@ end

#### post-inference optimizations ####

function inline_as_constant(val::ANY, argexprs, sv::InferenceState)
immutable InvokeData
mt::MethodTable
entry::TypeMapEntry
types0
fexpr
texpr
end

function inline_as_constant(val::ANY, argexprs, sv::InferenceState,
invoke_data::ANY)
if invoke_data === nothing
invoke_fexpr = nothing
invoke_texpr = nothing
else
invoke_data = invoke_data::InvokeData
invoke_fexpr = invoke_data.fexpr
invoke_texpr = invoke_data.texpr
end
# check if any arguments aren't effect_free and need to be kept around
stmts = Any[]
stmts = invoke_fexpr === nothing ? [] : Any[invoke_fexpr]
for i = 1:length(argexprs)
arg = argexprs[i]
if !effect_free(arg, sv.src, sv.mod, false)
push!(stmts, arg)
end
if i == 1 && !(invoke_texpr === nothing)
push!(stmts, invoke_texpr)
end
end
return (QuoteNode(val), stmts)
end
Expand All @@ -2715,10 +2735,30 @@ function countunionsplit(atypes::Vector{Any})
return nu
end

function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY)
function get_spec_lambda(atypes::ANY, sv, invoke_data::ANY)
if invoke_data === nothing
return ccall(:jl_get_spec_lambda, Any, (Any, UInt), atypes, sv.params.world)
else
invoke_data = invoke_data::InvokeData
atypes <: invoke_data.types0 || return nothing
return ccall(:jl_get_invoke_lambda, Any, (Any, Any, Any, UInt),
invoke_data.mt, invoke_data.entry, atypes, sv.params.world)
end
end

function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY,
invoke_data::ANY)
# converts a :call to :invoke
nu = countunionsplit(atypes)
nu > sv.params.MAX_UNION_SPLITTING && return NF
if invoke_data === nothing
invoke_fexpr = nothing
invoke_texpr = nothing
else
invoke_data = invoke_data::InvokeData
invoke_fexpr = invoke_data.fexpr
invoke_texpr = invoke_data.texpr
end

if nu > 1
spec_hit = nothing
Expand All @@ -2731,6 +2771,10 @@ function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY)
stmts = []
arg_hoisted = false
for i = length(atypes):-1:1
if i == 1 && !(invoke_texpr === nothing)
unshift!(stmts, invoke_texpr)
arg_hoisted = true
end
ti = atypes[i]
if arg_hoisted || isa(ti, Union)
aei = ex.args[i]
Expand All @@ -2742,10 +2786,11 @@ function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY)
end
end
end
invoke_fexpr === nothing || unshift!(stmts, invoke_fexpr)
function splitunion(atypes::Vector{Any}, i::Int)
if i == 0
local sig = argtypes_to_type(atypes)
local li = ccall(:jl_get_spec_lambda, Any, (Any, UInt), sig, sv.params.world)
local li = get_spec_lambda(sig, sv, invoke_data)
li === nothing && return false
add_backedge(li, sv)
local stmt = []
Expand Down Expand Up @@ -2814,14 +2859,25 @@ function invoke_NF(argexprs, etype::ANY, atypes, sv, atype_unlimited::ANY)
return (ret_var, stmts)
end
else
local cache_linfo = ccall(:jl_get_spec_lambda, Any, (Any, UInt), atype_unlimited, sv.params.world)
local cache_linfo = get_spec_lambda(atype_unlimited, sv, invoke_data)
cache_linfo === nothing && return NF
add_backedge(cache_linfo, sv)
unshift!(argexprs, cache_linfo)
ex = Expr(:invoke)
ex.args = argexprs
ex.typ = etype
return ex
if invoke_texpr === nothing
if invoke_fexpr === nothing
return ex
else
return ex, Any[invoke_fexpr]
end
end
newvar = newvar!(sv, atypes[1])
stmts = Any[invoke_fexpr, :($newvar = $(argexprs[1])),
invoke_texpr]
argexprs[1] = newvar
return ex, stmts
end
return NF
end
Expand Down Expand Up @@ -2875,43 +2931,97 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
end
end
end
if isa(f, IntrinsicFunction) || ft IntrinsicFunction ||
invoke_data = nothing
invoke_fexpr = nothing
invoke_texpr = nothing
if f === Core.invoke && length(atypes) >= 3
ft = widenconst(atypes[2])
invoke_tt = widenconst(atypes[3])
if !isleaftype(ft) || !isleaftype(invoke_tt) || !isType(invoke_tt)
return NF
end
if !(isa(invoke_tt.parameters[1], Type) &&
invoke_tt.parameters[1] <: Tuple)
return NF
end
invoke_tt_params = invoke_tt.parameters[1].parameters
invoke_types = Tuple{ft, invoke_tt_params...}
invoke_entry = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt),
invoke_types, sv.params.world)
invoke_entry === nothing && return NF
invoke_fexpr = argexprs[1]
invoke_texpr = argexprs[3]
if effect_free(invoke_fexpr, sv.src, sv.mod, false)
invoke_fexpr = nothing
end
if effect_free(invoke_texpr, sv.src, sv.mod, false)
invoke_fexpr = nothing
end
invoke_data = InvokeData(ft.name.mt, invoke_entry,
invoke_types, invoke_fexpr, invoke_texpr)
atype0 = atypes[2]
argexpr0 = argexprs[2]
atypes = atypes[4:end]
argexprs = argexprs[4:end]
unshift!(atypes, atype0)
unshift!(argexprs, argexpr0)
f = isdefined(ft, :instance) ? ft.instance : nothing
elseif isa(f, IntrinsicFunction) || ft IntrinsicFunction ||
isa(f, Builtin) || ft Builtin
return NF
end

local atype_unlimited = argtypes_to_type(atypes)
atype_unlimited = argtypes_to_type(atypes)
if !(invoke_data === nothing)
invoke_data = invoke_data::InvokeData
# TODO emit a type check and proceed for this case
atype_unlimited <: invoke_data.types0 || return NF
end
if !sv.params.inlining
return invoke_NF(argexprs, e.typ, atypes, sv, atype_unlimited)
return invoke_NF(argexprs, e.typ, atypes, sv, atype_unlimited,
invoke_data)
end

if length(atype_unlimited.parameters) - 1 > sv.params.MAX_TUPLETYPE_LEN
atype = limit_tuple_type(atype_unlimited, sv.params)
else
atype = atype_unlimited
end
min_valid = UInt[typemin(UInt)]
max_valid = UInt[typemax(UInt)]
meth = _methods_by_ftype(atype, 1, sv.params.world, min_valid, max_valid)
if meth === false || length(meth) != 1
return invoke_NF(argexprs, e.typ, atypes, sv, atype_unlimited)
if invoke_data === nothing
min_valid = UInt[typemin(UInt)]
max_valid = UInt[typemax(UInt)]
meth = _methods_by_ftype(atype, 1, sv.params.world, min_valid, max_valid)
if meth === false || length(meth) != 1
return invoke_NF(argexprs, e.typ, atypes, sv,
atype_unlimited, invoke_data)
end
meth = meth[1]::SimpleVector
metharg = meth[1]::Type
methsp = meth[2]::SimpleVector
method = meth[3]::Method
else
invoke_data = invoke_data::InvokeData
method = invoke_data.entry.func
(metharg, methsp) = ccall(:jl_match_method, Ref{SimpleVector},
(Any, Any, Any),
atype_unlimited, method.sig, method.tvars)
methsp = methsp::SimpleVector
end
meth = meth[1]::SimpleVector
metharg = meth[1]::Type
methsp = meth[2]
method = meth[3]::Method
# check whether call can be inlined to just a quoted constant value
if isa(f, widenconst(ft)) && !method.isstaged && (method.source.pure || f === return_type)
if isconstType(e.typ,false)
return inline_as_constant(e.typ.parameters[1], argexprs, sv)
return inline_as_constant(e.typ.parameters[1], argexprs, sv,
invoke_data)
elseif isa(e.typ,Const)
return inline_as_constant(e.typ.val, argexprs, sv)
return inline_as_constant(e.typ.val, argexprs, sv,
invoke_data)
end
end

methsig = method.sig
if !(atype <: metharg)
return invoke_NF(argexprs, e.typ, atypes, sv, atype_unlimited)
return invoke_NF(argexprs, e.typ, atypes, sv, atype_unlimited,
invoke_data)
end

argexprs0 = argexprs
Expand Down Expand Up @@ -2959,12 +3069,13 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference

# see if the method has been previously inferred (and cached)
linfo = code_for_method(method, metharg, methsp, sv.params.world, !force_infer) # Union{Void, MethodInstance}
isa(linfo, MethodInstance) || return invoke_NF(argexprs0, e.typ, atypes, sv, atype_unlimited)
isa(linfo, MethodInstance) || return invoke_NF(argexprs0, e.typ, atypes, sv,
atype_unlimited, invoke_data)
linfo = linfo::MethodInstance
if linfo.jlcall_api == 2
# in this case function can be inlined to a constant
add_backedge(linfo, sv)
return inline_as_constant(linfo.inferred, argexprs, sv)
return inline_as_constant(linfo.inferred, argexprs, sv, invoke_data)
end

# see if the method has a current InferenceState frame
Expand Down Expand Up @@ -3016,19 +3127,21 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
@assert isconstType(frame.bestguess, true)
inferred_const = frame.bestguess.parameters[1]
end
return inline_as_constant(inferred_const, argexprs, sv)
return inline_as_constant(inferred_const, argexprs, sv, invoke_data)
end
rettype = widenconst(frame.bestguess)
else
rettype = linfo.rettype
end

# check that the code is inlineable
isa(src, CodeInfo) || return invoke_NF(argexprs0, e.typ, atypes, sv, atype_unlimited)
isa(src, CodeInfo) || return invoke_NF(argexprs0, e.typ, atypes, sv,
atype_unlimited, invoke_data)
src = src::CodeInfo
ast = src.code
if !src.inferred || !src.inlineable
return invoke_NF(argexprs0, e.typ, atypes, sv, atype_unlimited)
return invoke_NF(argexprs0, e.typ, atypes, sv, atype_unlimited,
invoke_data)
end

# create the backedge
Expand All @@ -3051,8 +3164,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
end
end

methargs = metharg.parameters
nm = length(methargs)
nm = length(metharg.parameters)

if !isa(ast, Array{Any,1})
ast = ccall(:jl_uncompress_ast, Any, (Any, Any), method, ast)
Expand All @@ -3066,14 +3178,17 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
propagate_inbounds = src.propagate_inbounds

# see if each argument occurs only once in the body expression
stmts = Any[]
prelude_stmts = Any[]
stmts = []
prelude_stmts = []
stmts_free = true # true = all entries of stmts are effect_free

for i=na:-1:1 # stmts_free needs to be calculated in reverse-argument order
#args_i = args[i]
aei = argexprs[i]
aeitype = argtype = widenconst(exprtype(aei, sv.src, sv.mod))
if i == 1 && !(invoke_texpr === nothing)
unshift!(prelude_stmts, invoke_texpr)
end

# ok for argument to occur more than once if the actual argument
# is a symbol or constant, or is not affected by previous statements
Expand Down Expand Up @@ -3107,6 +3222,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
end
end
end
invoke_fexpr === nothing || unshift!(prelude_stmts, invoke_fexpr)

# re-number the SSAValues and copy their type-info to the new ast
ssavalue_types = src.ssavaluetypes
Expand Down
Loading

0 comments on commit 4d7a054

Please sign in to comment.