Skip to content

Commit

Permalink
thread min/max validity of ml-matches results out of jl_matching_meth…
Browse files Browse the repository at this point in the history
…ods into inference

eventually should probably switch this to use a Ref,
or return the value, or something similarly more efficient than a
singleton Array, but currently refpointer.jl is not part of Core.Inference
  • Loading branch information
vtjnash committed Sep 23, 2016
1 parent e2dda30 commit 40cf848
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 51 deletions.
7 changes: 5 additions & 2 deletions base/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,14 @@ function get_type_call(expr::Expr)
found ? push!(args, typ) : push!(args, Any)
end
# use _methods_by_ftype as the function is supplied as a type
mt = Base._methods_by_ftype(Tuple{ft, args...}, -1, typemax(UInt))
min = UInt[typemin(UInt)]
max = UInt[typemax(UInt)]
world = typemax(UInt)
mt = Base._methods_by_ftype(Tuple{ft, args...}, -1, world, min, max)
length(mt) == 1 || return (Any, false)
m = first(mt)
# Typeinference
return_type = Core.Inference.typeinf_type(m[3], m[1], m[2])
return_type = Core.Inference.typeinf_type(m[3], m[1], m[2], world)
return_type === nothing && return (Any, false)
return (return_type, true)
end
Expand Down
39 changes: 25 additions & 14 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,9 @@ function abstract_call_gf_by_type(f::ANY, atype::ANY, sv::InferenceState)
return Any
end
end
applicable = _methods_by_ftype(argtype, 4, sv.world)
min_valid = UInt[typemin(UInt)]
max_valid = UInt[typemax(UInt)]
applicable = _methods_by_ftype(argtype, 4, sv.world, min_valid, max_valid)
rettype = Bottom
if is(applicable, false)
# this means too many methods matched
Expand Down Expand Up @@ -956,6 +958,7 @@ function abstract_call_gf_by_type(f::ANY, atype::ANY, sv::InferenceState)
# also need an edge to the method table in case something gets
# added that did not intersect with any existing method
add_mt_backedge(ft.name.mt, argtype, sv)
update_valid_age!(min_valid[1], max_valid[1], sv)
end
if isempty(x)
# TODO: this is needed because type intersection is wrong in some cases
Expand Down Expand Up @@ -1060,7 +1063,9 @@ function pure_eval_call(f::ANY, argtypes::ANY, atype, vtypes, sv::InferenceState
end
end

meth = _methods_by_ftype(atype, 1, sv.world)
min_valid = UInt[typemin(UInt)]
max_valid = UInt[typemax(UInt)]
meth = _methods_by_ftype(atype, 1, sv.world, min_valid, max_valid)
if meth === false || length(meth) != 1
return false
end
Expand All @@ -1074,6 +1079,7 @@ function pure_eval_call(f::ANY, argtypes::ANY, atype, vtypes, sv::InferenceState
args = Any[ isa(a,Const) ? a.val : a.parameters[1] for a in drop(argtypes,1) ]
try
value = Core._apply_pure(f, args)
# TODO: add some sort of edge(s)
return abstract_eval_constant(value)
catch
return false
Expand Down Expand Up @@ -1530,23 +1536,24 @@ function converge_valid_age!(sv::InferenceState)
i.max_valid = sv.max_valid
updated = true
end
@assert !isdefined(i.linfo, :def) || !i.cached || i.min_valid <= i.world <= i.max_valid "invalid age range update"
if updated
converge_valid_age!(i)
end
end
end
nothing
end
function update_valid_age!(edge::InferenceState, sv::InferenceState)
sv.min_valid = max(edge.min_valid, sv.min_valid)
sv.max_valid = min(edge.max_valid, sv.max_valid)
nothing
end
function update_valid_age!(li::MethodInstance, sv::InferenceState)
sv.min_valid = max(sv.min_valid, min_age(li))
sv.max_valid = min(sv.max_valid, max_age(li))

function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::InferenceState)
sv.min_valid = max(sv.min_valid, min_valid)
sv.max_valid = min(sv.max_valid, max_valid)
@assert !isdefined(sv.linfo, :def) || !sv.cached || sv.min_valid <= sv.world <= sv.max_valid "invalid age range update"
nothing
end
update_valid_age!(edge::InferenceState, sv::InferenceState) = update_valid_age!(edge.min_valid, edge.max_valid, sv)
update_valid_age!(li::MethodInstance, sv::InferenceState) = update_valid_age!(min_age(li), max_age(li), sv)

function add_backedge(li::MethodInstance, caller::InferenceState)
isdefined(caller.linfo, :def) || return # don't add backedges to toplevel exprs
if caller.stmt_edges[caller.currpc] === ()
Expand All @@ -1556,6 +1563,7 @@ function add_backedge(li::MethodInstance, caller::InferenceState)
update_valid_age!(li, caller)
nothing
end

function add_mt_backedge(mt::MethodTable, typ::ANY, caller::InferenceState)
isdefined(caller.linfo, :def) || return # don't add backedges to toplevel exprs
if caller.stmt_edges[caller.currpc] === ()
Expand All @@ -1566,6 +1574,7 @@ function add_mt_backedge(mt::MethodTable, typ::ANY, caller::InferenceState)
# TODO: how to compute the affect this has on valid ages for caller?
nothing
end

function finalize_backedges(frame::InferenceState)
caller = frame.linfo
for edges in frame.stmt_edges
Expand Down Expand Up @@ -1666,7 +1675,7 @@ function typeinf_edge(method::Method, atypes::ANY, sparams::SimpleVector, caller
code = code_for_method(method, atypes, sparams, caller.world)
code === nothing && return Any
code = code::MethodInstance
add_backedge(code, caller)
add_backedge(code, caller) # TODO: need to defer the tracking of this backedge till later
if isdefined(code, :inferred)
# return rettype if the code is already inferred
# staged functions make this hard since they have two "inferred" conditions,
Expand Down Expand Up @@ -2682,7 +2691,9 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
else
atype = atype_unlimited
end
meth = _methods_by_ftype(atype, 1, sv.world)
min_valid = UInt[typemin(UInt)]
max_valid = UInt[typemax(UInt)]
meth = _methods_by_ftype(atype, 1, sv.world, min_valid, max_valid)
if meth === false || length(meth) != 1
return invoke_NF()
end
Expand Down Expand Up @@ -4083,9 +4094,9 @@ end
# make sure that typeinf is executed before turning on typeinf_ext
# this ensures that typeinf_ext doesn't recurse before it can add the item to the workq

for m in _methods_by_ftype(Tuple{typeof(typeinf_loop), Vararg{Any}}, 10, typemax(UInt))
for m in _methods_by_ftype(Tuple{typeof(typeinf_loop), Vararg{Any}}, 10, typemax(UInt), UInt[typemin(UInt)], UInt[typemax(UInt)])
typeinf_type(m[3], m[1], m[2], typemax(UInt))
end
for m in _methods_by_ftype(Tuple{typeof(typeinf_edge), Vararg{Any}}, 10, typemax(UInt))
for m in _methods_by_ftype(Tuple{typeof(typeinf_edge), Vararg{Any}}, 10, typemax(UInt), UInt[typemin(UInt)], UInt[typemax(UInt)])
typeinf_type(m[3], m[1], m[2], typemax(UInt))
end
26 changes: 16 additions & 10 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,12 @@ end
function _methods(f::ANY, t::ANY, lim::Int, world::UInt)
ft = isa(f,Type) ? Type{f} : typeof(f)
tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...}
return _methods_by_ftype(tt, lim, world)
min = UInt[typemin(UInt)]
max = UInt[typemax(UInt)]
return _methods_by_ftype(tt, lim, world, min, max)
end

function _methods_by_ftype(t::ANY, lim::Int, world::UInt)
function _methods_by_ftype(t::ANY, lim::Int, world::UInt, min::Array{UInt,1}, max::Array{UInt,1})
tp = t.parameters::SimpleVector
nu = 1
for ti in tp
Expand All @@ -361,31 +363,31 @@ function _methods_by_ftype(t::ANY, lim::Int, world::UInt)
end
end
if 1 < nu <= 64
return _methods(Any[tp...], length(tp), lim, [], world)
return _methods_by_ftype(Any[tp...], length(tp), lim, [], world, min, max)
end
# XXX: the following can return incorrect answers that the above branch would have corrected
return ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt), t, lim, 0, world)
return ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}), t, lim, 0, world, min, max)
end

function _methods(t::Array, i, lim::Integer, matching::Array{Any,1}, world::UInt)
function _methods_by_ftype(t::Array, i, lim::Integer, matching::Array{Any,1}, world::UInt, min::Array{UInt,1}, max::Array{UInt,1})
if i == 0
world = typemax(UInt)
new = ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt), Tuple{t...}, lim, 0, world)
new = ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}), Tuple{t...}, lim, 0, world, min, max)
new === false && return false
append!(matching, new::Array{Any,1})
else
ti = t[i]
if isa(ti, Union)
for ty in (ti::Union).types
t[i] = ty
if _methods(t, i - 1, lim, matching, world) === false
if _methods_by_ftype(t, i - 1, lim, matching, world, min, max) === false
t[i] = ti
return false
end
end
t[i] = ti
else
return _methods(t, i - 1, lim, matching, world)
return _methods_by_ftype(t, i - 1, lim, matching, world, min, max)
end
end
return matching
Expand Down Expand Up @@ -435,7 +437,9 @@ function methods_including_ambiguous(f::ANY, t::ANY)
ft = isa(f,Type) ? Type{f} : typeof(f)
tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...}
world = typemax(UInt)
ms = ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt), tt, -1, 1, world)::Array{Any,1}
min = UInt[typemin(UInt)]
max = UInt[typemax(UInt)]
ms = ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}), tt, -1, 1, world, min, max)::Array{Any,1}
return MethodList(Method[m[3] for m in ms], typeof(f).name.mt)
end
function methods(f::ANY)
Expand Down Expand Up @@ -745,7 +749,9 @@ end
function isambiguous(m1::Method, m2::Method)
ti = typeintersect(m1.sig, m2.sig)
ti === Bottom && return false
ml = _methods_by_ftype(ti, -1, typemax(UInt))
min = UInt[typemin(UInt)]
max = UInt[typemax(UInt)]
ml = _methods_by_ftype(ti, -1, typemax(UInt), min, max)
isempty(ml) && return true
for m in ml
if ti <: m[3].sig
Expand Down
Loading

0 comments on commit 40cf848

Please sign in to comment.