Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
vtjnash committed Nov 6, 2017
1 parent c85a79d commit 20c6a59
Showing 1 changed file with 76 additions and 147 deletions.
223 changes: 76 additions & 147 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,14 @@ mutable struct InferenceState
false, false, optimize, cached, false, false, false)
result.result = frame
cached && push!(params.cache, result)
global cache_max_len
length(params.cache) > cache_max_len && (cache_max_len = length(params.cache))
return frame
end
end
cache_max_len = 0
cache_nlookups = 0
cache_sizeof = 0

function InferenceState(linfo::MethodInstance,
optimize::Bool, cached::Bool, params::InferenceParams)
Expand Down Expand Up @@ -1969,6 +1974,46 @@ function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nosp
return rettype
end

function cache_lookup(code::MethodInstance, argtypes::Vector{Any}, cache::Vector{InferenceResult})
method = code.def::Method
nargs::Int = method.nargs
method.isva && (nargs -= 1)
global cache_nlookups += 1
global cache_sizeof += length(cache)
for cache_code in cache
# try to search cache first
cache_args = cache_code.args
if cache_code.linfo === code && length(cache_args) >= nargs
cache_match = true
# verify that the trailing args (va) aren't Const
for i in (nargs + 1):length(cache_args)
if isa(cache_args[i], Const)
cache_match = false
break
end
end
cache_match || continue
for i in 1:nargs
a = argtypes[i]
ca = cache_args[i]
# verify that all Const argument types match between the call and cache
if (isa(a, Const) || isa(ca, Const)) && !(a === ca)
cache_match = false
break
end
end
cache_match || continue
if debug_enabled && (length(argtypes) != length(cache_code.args) || !all(i -> argtypes[i] === cache_code.args[i], 1:nargs))
println(argtypes)
println(cache_code)
end
return cache_code
end
end
return nothing
end
debug_enabled = false

function abstract_call_method_with_const_args(argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState)
method = match[3]::Method
nargs::Int = method.nargs
Expand All @@ -1991,34 +2036,7 @@ function abstract_call_method_with_const_args(argtypes::Vector{Any}, match::Simp
code = code_for_method(method, sig, sparams, sv.params.world)
code === nothing && return Any
code = code::MethodInstance
inf_result = nothing
for cache_code in sv.params.cache
# try to search cache first
cache_args = cache_code.args
if cache_code.linfo === code && length(cache_args) >= nargs
cache_match = true
for i in (nargs + 1):length(cache_args)
if isa(cache_args[i], Const)
cache_match = false
break
end
end
if cache_match
for i in 1:nargs
a = argtypes[i]
ca = cache_args[i]
if !(isa(a, Const) && isa(ca, Const) && ca.val === a.val)
cache_match = false
break
end
end
if cache_match
inf_result = cache_code
break
end
end
end
end
inf_result = cache_lookup(code, argtypes, sv.params.cache)
if inf_result === nothing
inf_result = InferenceResult(code)
atypes = get_argtypes(inf_result)
Expand All @@ -2028,7 +2046,7 @@ function abstract_call_method_with_const_args(argtypes::Vector{Any}, match::Simp
atypes[i] = a # inject Const argtypes into inference
end
end
frame = InferenceState(inf_result, #=optimize=#false, #=cache=#false, sv.params)
frame = InferenceState(inf_result, #=optimize=#true, #=cache=#false, sv.params)
frame.limited = true
frame.parent = sv
push!(sv.params.cache, inf_result)
Expand Down Expand Up @@ -2548,37 +2566,6 @@ function abstract_call(@nospecialize(f), fargs::Union{Tuple{},Vector{Any}}, argt
return rty
end

if la>2 && argtypes[3] Int
at2 = widenconst(argtypes[2])
if la==3 && at2 <: SimpleVector && istopfunction(tm, f, :getindex)
if isa(argtypes[2], Const) && isa(argtypes[3], Const)
svecval = argtypes[2].val
idx = argtypes[3].val
if isa(idx, Int) && 1 <= idx <= length(svecval) &&
isassigned(svecval, idx)
return Const(getindex(svecval, idx))
end
end
elseif (at2 <: Tuple ||
(isa(at2, DataType) && (at2::DataType).name === Pair_name()))
# allow tuple indexing functions to take advantage of constant
# index arguments.
if istopfunction(tm, f, :getindex) && la==3
return getfield_tfunc(argtypes[2], argtypes[3])
elseif istopfunction(tm, f, :next) && la==3
t1 = widenconst(getfield_tfunc(argtypes[2], argtypes[3]))
return t1===Bottom ? Bottom : Tuple{t1, Int}
elseif istopfunction(tm, f, :indexed_next) && la==4
t1 = widenconst(getfield_tfunc(argtypes[2], argtypes[3]))
return t1===Bottom ? Bottom : Tuple{t1, Int}
end
end
elseif la==2 && argtypes[2] SimpleVector && istopfunction(tm, f, :length)
if isa(argtypes[2], Const)
return Const(length(argtypes[2].val))
end
end

atype = argtypes_to_type(argtypes)
t = pure_eval_call(f, argtypes, atype, sv)
t !== false && return t
Expand Down Expand Up @@ -4742,29 +4729,8 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
isa(methsp[i], TypeVar) && return NF
end

# some gf have special tfunc, meaning they wouldn't have been inferred yet
# check the same conditions from abstract_call to detect this case
force_infer = false
if !isdefined(method, :generator)
if method.module == _topmod(method.module) || (isdefined(Main, :Base) && method.module == Main.Base)
la = length(atypes)
if (la==3 && (method.name == :getindex || method.name == :next)) ||
(la==4 && method.name == :indexed_next)
if atypes[3] Int
at2 = widenconst(atypes[2])
if (at2 <: Tuple || at2 <: SimpleVector ||
(isa(at2, DataType) && (at2::DataType).name === Pair_name()))
force_infer = true
end
end
elseif la == 2 && method.name == :length && atypes[2] SimpleVector
force_infer = true
end
end
end

# 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}
linfo = code_for_method(method, metharg, methsp, sv.params.world, true) # Union{Void, MethodInstance}
isa(linfo, MethodInstance) || return invoke_NF(argexprs0, e.typ, atypes0, sv,
atype_unlimited, invoke_data)
linfo = linfo::MethodInstance
Expand All @@ -4774,60 +4740,30 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
return inline_as_constant(linfo.inferred_const, argexprs, sv, invoke_data)
end

# see if the method has a current InferenceState frame
# or existing inferred code info
frame = nothing # Union{Void, InferenceState}
inferred = nothing # Union{Void, CodeInfo}
if force_infer && la > 2 && isa(atypes[3], Const)
# Since we inferred this with the information that atypes[3]::Const,
# must inline with that same information.
# We do that by overriding the argument type,
# while ensuring we don't cache that information
# This isn't particularly important for `getindex`,
# as we'll be able to fix that up at the end of inlinable when we verify the return type.
# But `next` and `indexed_next` make tuples which would end up burying some of that information in the AST
# where we can't easily correct it afterwards.
inf_result = InferenceResult(linfo)
get_argtypes(inf_result)[3] = atypes[3] # inject Const into atypes[3]
frame = InferenceState(inf_result, #=optimize=#true, #=cache=#false, sv.params)
typeinf(frame)
else
if isdefined(linfo, :inferred) && linfo.inferred !== nothing
# use cache
inferred = linfo.inferred
elseif force_infer
# create inferred code on-demand
# but if we decided in the past not to try to infer this particular signature
# (due to signature coarsening in abstract_call_gf_by_type)
# don't infer it now, as attempting to force it now would be a bad idea (non terminating)
frame = typeinf_frame(linfo, #=optimize=#true, #=cache=#true, sv.params)
end
end

# compute the return value
if isa(frame, InferenceState) && !frame.src.inferred
frame = nothing
end
if isa(frame, InferenceState)
linfo = frame.linfo
inferred = frame.src
if frame.const_api # handle like jlcall_api == 2
if frame.inferred || !frame.cached
add_backedge!(linfo, sv)
else
add_backedge!(frame, sv)
end
if isa(frame.bestguess, Const)
inferred_const = (frame.bestguess::Const).val
else
@assert isconstType(frame.bestguess)
inferred_const = frame.bestguess.parameters[1]
end
return inline_as_constant(inferred_const, argexprs, sv, invoke_data)
end
rettype = widenconst(frame.bestguess)
else
# see if the method has a InferenceResult in the current cache
# or an existing inferred code info store in `.inferred`
inf_result = cache_lookup(linfo, atypes, sv.params.cache) # Union{Void, InferenceResult}
if isa(inf_result, InferenceResult) && isa(inf_result.src, CodeInfo)
linfo = inf_result.linfo
result = inf_result.result
## TODO: enable for pure functions
#if isa(result, Const)
# inferred_const = result.val
#elseif isconstType(result)
# inferred_const = result.parameters[1]
#end
#if @isdefined inferred_const
# add_backedge!(linfo, sv)
# return inline_as_constant(inferred_const, argexprs, sv, invoke_data)
#end
inferred = inf_result.src
rettype = widenconst(result)
elseif isdefined(linfo, :inferred)
inferred = linfo.inferred
rettype = linfo.rettype
else
rettype = Any
inferred = nothing
end

# check that the code is inlineable
Expand All @@ -4842,26 +4778,19 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
invoke_data)
end

# create the backedge
add_backedge!(linfo, sv)

# prepare the code object for mutation
if isa(inferred, CodeInfo)
src = inferred
ast = copy_exprargs(inferred.code)
else
src = ccall(:jl_uncompress_ast, Any, (Any, Any), method, inferred)::CodeInfo
src = ccall(:jl_uncompress_ast, Any, (Any, Any), method, inferred::Vector{UInt8})::CodeInfo
ast = src.code
end
ast = ast::Array{Any,1}

# create the backedge
if isa(frame, InferenceState) && !frame.inferred && frame.cached
# in this case, the actual backedge linfo hasn't been computed
# yet, but will be when inference on the frame finishes
add_backedge!(frame, sv)
else
add_backedge!(linfo, sv)
end

nm = length(unwrap_unionall(metharg).parameters)

body = Expr(:block)
body.args = ast

Expand Down

0 comments on commit 20c6a59

Please sign in to comment.