Skip to content

Commit

Permalink
wip: thread min/max validity of ml-matches results out of jl_matching…
Browse files Browse the repository at this point in the history
…_methods into inference
  • Loading branch information
vtjnash committed Sep 22, 2016
1 parent f30f543 commit 7529828
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ BASE_SRCS := $(sort $(shell find $(JULIAHOME)/base -name \*.jl) $(shell find $(B
$(build_private_libdir)/inference.ji: $(CORE_SRCS) | $(build_private_libdir)
@$(call PRINT_JULIA, cd $(JULIAHOME)/base && \
$(call spawn,$(JULIA_EXECUTABLE)) -C $(JULIA_CPU_TARGET) --output-ji $(call cygpath_w,$@) \
--startup-file=no -g0 -O0 coreimg.jl)
--startup-file=no -g1 -O0 coreimg.jl)

RELBUILDROOT := $(shell $(JULIAHOME)/contrib/relative_path.sh "$(JULIAHOME)/base" "$(BUILDROOT)/base/")
COMMA:=,
Expand Down
4 changes: 3 additions & 1 deletion base/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,9 @@ 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 = Ref(typemin(UInt))
max = Ref(typemax(UInt))
mt = Base._methods_by_ftype(Tuple{ft, args...}, -1, typemax(UInt), min, max)
length(mt) == 1 || return (Any, false)
m = first(mt)
# Typeinference
Expand Down
41 changes: 22 additions & 19 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -826,18 +826,15 @@ 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
return Any
end
x::Array{Any,1} = applicable
if isempty(x)
# no methods match
add_mt_backedge(ft.name.mt, argtype, sv)
return Bottom
end
fullmatch = false
for (m::SimpleVector) in x
sig = m[1]::DataType
Expand Down Expand Up @@ -961,8 +958,8 @@ 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 rettype is Bottom we've found a method not found error
#print("=> ", rettype, "\n")
return rettype
end
Expand Down Expand Up @@ -1062,7 +1059,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 @@ -1076,6 +1075,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 @@ -1539,16 +1539,15 @@ function converge_valid_age!(sv::InferenceState)
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)
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 @@ -1558,6 +1557,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 @@ -1568,6 +1568,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 @@ -2683,7 +2684,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 @@ -4084,9 +4087,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
68 changes: 49 additions & 19 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ static jl_tupletype_t *join_tsig(jl_tupletype_t *tt, jl_tupletype_t *sig)

static jl_value_t *ml_matches(union jl_typemap_t ml, int offs,
jl_tupletype_t *type, int lim, int include_ambiguous,
size_t world);
size_t world, size_t *min_valid, size_t *max_valid);

static void jl_cacheable_sig(
jl_tupletype_t *const type, // the specialized type signature for type lambda
Expand Down Expand Up @@ -841,6 +841,8 @@ static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t
need_guard_entries = 1;
}

size_t min_valid = definition->min_world;
size_t max_valid = definition->max_world;
int cache_with_orig = 0;
jl_svec_t* guardsigs = jl_emptysvec;
jl_tupletype_t *origtype = type; // backup the prior value of `type`
Expand All @@ -849,7 +851,7 @@ static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t
temp2 = (jl_value_t*)type;
}
if (need_guard_entries) {
temp = ml_matches(mt->defs, 0, type, -1, 0, 0); // TODO: use MAX_UNSPECIALIZED_CONFLICTS?
temp = ml_matches(mt->defs, 0, type, -1, 0, world, &min_valid, &max_valid); // TODO: use MAX_UNSPECIALIZED_CONFLICTS?
int guards = 0;
if (temp == jl_false) {
cache_with_orig = 1;
Expand Down Expand Up @@ -890,18 +892,23 @@ static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t
guards = 0;
for(i = 0, l = jl_array_len(temp); i < l; i++) {
jl_value_t *m = jl_array_ptr_ref(temp, i);
if (((jl_method_t*)jl_svecref(m,2)) != definition) {
jl_method_t *other = (jl_method_t*)jl_svecref(m, 2);
if (other != definition) {
jl_svecset(guardsigs, guards, (jl_tupletype_t*)jl_svecref(m, 0));
guards++;
//jl_typemap_insert(cache, parent, (jl_tupletype_t*)jl_svecref(m, 0),
// jl_emptysvec, NULL, jl_emptysvec, /*guard*/NULL, jl_cachearg_offset(mt), &lambda_cache, 1, ~(size_t)0, NULL);
// jl_emptysvec, NULL, jl_emptysvec, /*guard*/NULL, jl_cachearg_offset(mt), &lambda_cache, other->min_world, other->max_world, NULL);
}
}
}
}

// here we infer types and specialize the method
newmeth = jl_specializations_get_linfo(definition, type, sparams, world);
if (newmeth->min_world < min_valid)
min_valid = newmeth->min_world;
if (newmeth->max_world < max_valid)
max_valid = newmeth->max_world;

if (cache_with_orig) {
// if there is a need to cache with one of the original signatures,
Expand Down Expand Up @@ -940,7 +947,7 @@ static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t
}

jl_typemap_insert(cache, parent, origtype, jl_emptysvec, type, guardsigs, (jl_value_t*)newmeth, jl_cachearg_offset(mt), &lambda_cache,
newmeth->min_world, newmeth->max_world, NULL);
min_valid, max_valid, NULL);

if (definition->traced && jl_method_tracer && allow_exec)
jl_call_tracer(jl_method_tracer, (jl_value_t*)newmeth);
Expand Down Expand Up @@ -1439,18 +1446,24 @@ jl_method_instance_t *jl_method_lookup(jl_methtable_t *mt, jl_value_t **args, si
//
// lim is the max # of methods to return. if there are more, returns jl_false.
// -1 for no limit.
JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int include_ambiguous, size_t world)
JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int include_ambiguous, size_t world, size_t *min_valid, size_t *max_valid)
{
assert(jl_nparams(types) > 0);
if (jl_tparam0(types) == jl_bottom_type)
return (jl_value_t*)jl_alloc_vec_any(0);
if (!jl_is_datatype(jl_tparam0(types))) {
jl_value_t *matches = NULL;
if (jl_tparam0(types) == jl_bottom_type) {
matches = (jl_value_t*)jl_alloc_vec_any(0);
}
else if (!jl_is_datatype(jl_tparam0(types))) {
return jl_false; // indeterminate - ml_matches can't deal with this case
}
jl_methtable_t *mt = ((jl_datatype_t*)jl_tparam0(types))->name->mt;
if (mt == NULL)
return (jl_value_t*)jl_alloc_vec_any(0);
return ml_matches(mt->defs, 0, types, lim, include_ambiguous, world);
else {
jl_methtable_t *mt = ((jl_datatype_t*)jl_tparam0(types))->name->mt;
if (mt == NULL)
matches = (jl_value_t*)jl_alloc_vec_any(0);
else
matches = ml_matches(mt->defs, 0, types, lim, include_ambiguous, world, min_valid, max_valid);
}
return matches;
}

jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t **pli, size_t world)
Expand Down Expand Up @@ -1520,8 +1533,10 @@ jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world
// if one argument type is DataType, multiple Type{} definitions
// might match. also be conservative with tuples rather than trying
// to analyze them in detail.
size_t min_valid = 0;
size_t max_valid = ~(size_t)0;
if (ti == (jl_value_t*)jl_datatype_type || jl_is_tuple_type(ti)) {
jl_value_t *matches = jl_matching_methods(types, 1, 0, world);
jl_value_t *matches = jl_matching_methods(types, 1, 0, world, &min_valid, &max_valid);
if (matches == jl_false)
return NULL;
break;
Expand Down Expand Up @@ -2259,19 +2274,30 @@ static int tvar_exists_at_top_level(jl_value_t *tv, jl_tupletype_t *sig, int att

struct ml_matches_env {
struct typemap_intersection_env match;
jl_value_t *t; // results: array of svec(argtypes, params, Method)
// results:
jl_value_t *t; // array of svec(argtypes, params, Method)
size_t min_valid;
size_t max_valid;
// temporary:
jl_svec_t *matc; // current working svec
// inputs:
size_t world;
int lim;
int include_ambiguous; // whether ambiguous matches should be included
size_t world;
};
static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure0)
{
struct ml_matches_env *closure = container_of(closure0, struct ml_matches_env, match);
int i;
if (closure->world != 0) // use zero as a flag value for returning all matches
if (closure->world < ml->min_world || closure->world > ml->max_world)
if (closure->world != 0) { // use zero as a flag value for returning all matches
if (ml->min_world < closure->min_valid)
closure->min_valid = ml->min_world;
if (ml->max_world > closure->max_valid)
closure->max_valid = ml->max_world;
if (closure->world < ml->min_world || closure->world > ml->max_world) {
return 1; // ignore method table entries that are not part of this world
}
}
// a method is shadowed if type <: S <: m->sig where S is the
// signature of another applicable method
/*
Expand Down Expand Up @@ -2412,7 +2438,7 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio
// See below for the meaning of lim.
static jl_value_t *ml_matches(union jl_typemap_t defs, int offs,
jl_tupletype_t *type, int lim, int include_ambiguous,
size_t world)
size_t world, size_t *min_valid, size_t *max_valid)
{
size_t l = jl_svec_len(type->parameters);
jl_value_t *va = NULL;
Expand All @@ -2434,9 +2460,13 @@ static jl_value_t *ml_matches(union jl_typemap_t defs, int offs,
env.lim = lim;
env.include_ambiguous = include_ambiguous;
env.world = world;
env.min_valid = *min_valid;
env.max_valid = *max_valid;
JL_GC_PUSH4(&env.t, &env.matc, &env.match.env, &env.match.ti);
jl_typemap_intersection_visitor(defs, offs, &env.match);
JL_GC_POP();
*min_valid = env.min_valid;
*max_valid = env.max_valid;
return env.t;
}

Expand Down

0 comments on commit 7529828

Please sign in to comment.