diff --git a/Makefile b/Makefile index 2716c9c1ec5a4..55938a1cd7619 100644 --- a/Makefile +++ b/Makefile @@ -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:=, diff --git a/base/REPLCompletions.jl b/base/REPLCompletions.jl index 9624c2c57dd64..fec552892978c 100644 --- a/base/REPLCompletions.jl +++ b/base/REPLCompletions.jl @@ -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 diff --git a/base/inference.jl b/base/inference.jl index ab8a68cd2e769..7f375eec2cdda 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -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 @@ -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 @@ -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 @@ -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 @@ -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] === () @@ -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] === () @@ -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 @@ -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 @@ -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 diff --git a/base/reflection.jl b/base/reflection.jl index 75f481bc27b84..f37c1cb5cdff8 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -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 @@ -361,16 +363,16 @@ 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 @@ -378,14 +380,14 @@ function _methods(t::Array, i, lim::Integer, matching::Array{Any,1}, world::UInt 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 @@ -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) @@ -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 diff --git a/src/gf.c b/src/gf.c index 0299e533ca6e1..7a01cac5f3e35 100644 --- a/src/gf.c +++ b/src/gf.c @@ -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 @@ -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` @@ -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; @@ -890,11 +892,12 @@ 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); } } } @@ -902,6 +905,10 @@ static jl_method_instance_t *cache_method(jl_methtable_t *mt, union jl_typemap_t // 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, @@ -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); @@ -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) @@ -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; @@ -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 /* @@ -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; @@ -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; }