diff --git a/src/datatype.c b/src/datatype.c index eb25907647157..2df2bdb2aaa71 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -62,6 +62,7 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo mt->cache = mc; mt->name = name; mt->module = module; + mt->backedges = (jl_genericmemory_t*)jl_an_empty_memory_any; JL_GC_POP(); return mt; } @@ -88,7 +89,6 @@ JL_DLLEXPORT jl_typename_t *jl_new_typename_in(jl_sym_t *name, jl_module_t *modu tn->partial = NULL; tn->atomicfields = NULL; tn->constfields = NULL; - tn->backedges = NULL; tn->max_methods = 0; jl_atomic_store_relaxed(&tn->max_args, 0); jl_atomic_store_relaxed(&tn->cache_entry_count, 0); diff --git a/src/gf.c b/src/gf.c index 8205cf70b99c3..57dc2760eecac 100644 --- a/src/gf.c +++ b/src/gf.c @@ -777,55 +777,117 @@ JL_DLLEXPORT int jl_mi_try_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, return ret; } -static int foreach_typename_in_module( - jl_module_t *m, - int (*visit)(jl_typename_t *tn, void *env), - void *env) +enum top_typename_facts { + EXACTLY_ANY = 1 << 0, + HAVE_TYPE = 1 << 1, + EXACTLY_TYPE = 1 << 2, + HAVE_FUNCTION = 1 << 3, + EXACTLY_FUNCTION = 1 << 4, + HAVE_KWCALL = 1 << 5, + EXACTLY_KWCALL = 1 << 6, + SHORT_TUPLE = 1 << 7, +}; + +static void foreach_top_nth_typename(void (*f)(jl_typename_t*, int, void*), jl_value_t *a JL_PROPAGATES_ROOT, int n, unsigned *facts, void *env) { - jl_svec_t *table = jl_atomic_load_relaxed(&m->bindings); - for (size_t i = 0; i < jl_svec_len(table); i++) { - jl_binding_t *b = (jl_binding_t*)jl_svecref(table, i); - if ((void*)b == jl_nothing) - break; - jl_sym_t *name = b->globalref->name; - jl_value_t *v = jl_get_latest_binding_value_if_const(b); - if (v) { - jl_value_t *uw = jl_unwrap_unionall(v); - if (jl_is_datatype(uw)) { - jl_typename_t *tn = ((jl_datatype_t*)uw)->name; - if (tn->module == m && tn->name == name && tn->wrapper == v) { - // this is the original/primary binding for the type (name/wrapper) - if (!visit(((jl_datatype_t*)uw)->name, env)) - return 0; - } + if (jl_is_datatype(a)) { + if (n <= 0) { + jl_datatype_t *dt = ((jl_datatype_t*)a); + if (dt->name == jl_type_typename) { // key Type{T} on T instead of Type + *facts |= HAVE_TYPE; + foreach_top_nth_typename(f, jl_tparam0(a), -1, facts, env); } - else if (jl_is_module(v)) { - jl_module_t *child = (jl_module_t*)v; - if (child != m && child->parent == m && child->name == name) { - // this is the original/primary binding for the submodule - if (!foreach_typename_in_module(child, visit, env)) - return 0; + else if (dt == jl_function_type) { + if (n == -1) // key Type{>:Function} as Type instead of Function + *facts |= EXACTLY_TYPE; // HAVE_TYPE is already set + else + *facts |= HAVE_FUNCTION | EXACTLY_FUNCTION; + } + else if (dt == jl_any_type) { + if (n == -1) // key Type{>:Any} and kinds as Type instead of Any + *facts |= EXACTLY_TYPE; // HAVE_TYPE is already set + else + *facts |= EXACTLY_ANY; + } + else if (dt == jl_kwcall_type) { + if (n == -1) // key Type{>:typeof(kwcall)} as exactly kwcall + *facts |= EXACTLY_KWCALL; + else + *facts |= HAVE_KWCALL; + } + else { + while (1) { + jl_datatype_t *super = dt->super; + if (super == jl_function_type) { + *facts |= HAVE_FUNCTION; + break; + } + if (super == jl_any_type || super->super == dt) + break; + dt = super; } + f(dt->name, 1, env); } } - table = jl_atomic_load_relaxed(&m->bindings); - } + else if (jl_is_tuple_type(a)) { + if (jl_nparams(a) >= n) + foreach_top_nth_typename(f, jl_tparam(a, n - 1), 0, facts, env); + else + *facts |= SHORT_TUPLE; + } + } + else if (jl_is_typevar(a)) { + foreach_top_nth_typename(f, ((jl_tvar_t*)a)->ub, n, facts, env); + } + else if (jl_is_unionall(a)) { + foreach_top_nth_typename(f, ((jl_unionall_t*)a)->body, n, facts, env); + } + else if (jl_is_uniontype(a)) { + jl_uniontype_t *u = (jl_uniontype_t*)a; + foreach_top_nth_typename(f, u->a, n, facts, env); + foreach_top_nth_typename(f, u->b, n, facts, env); + } +} + +// Inspect type `argtypes` for all backedge keys that might be relevant to it, splitting it +// up on some commonly observed patterns to make a better distribution. +// (It could do some of that balancing automatically, but for now just hard-codes kwcall.) +// Along the way, record some facts about what was encountered, so that those additional +// calls can be added later if needed for completeness. +// The `int explct` argument instructs the caller if the callback is due to an exactly +// encountered type or if it rather encountered a subtype. +// This is not capable of walking to all top-typenames for an explicitly encountered +// Function or Any, so the caller a fallback that can scan the entire in that case. +// We do not de-duplicate calls when encountering a Union. +static int jl_foreach_top_typename_for(void (*f)(jl_typename_t*, int, void*), jl_value_t *argtypes JL_PROPAGATES_ROOT, int all_subtypes, void *env) +{ + unsigned facts = 0; + foreach_top_nth_typename(f, argtypes, 1, &facts, env); + if (facts & HAVE_KWCALL) { + // split kwcall on the 3rd argument instead, using the same logic + unsigned kwfacts = 0; + foreach_top_nth_typename(f, argtypes, 3, &kwfacts, env); + // copy kwfacts to original facts + if (kwfacts & SHORT_TUPLE) + kwfacts |= (all_subtypes ? EXACTLY_ANY : EXACTLY_KWCALL); + facts |= kwfacts; + } + if (all_subtypes && (facts & (EXACTLY_FUNCTION | EXACTLY_TYPE | EXACTLY_ANY))) + // flag that we have an explct match than is necessitating a full table scan + return 0; + // or inform caller of only which supertypes are applicable + if (facts & HAVE_FUNCTION) + f(jl_function_type->name, facts & EXACTLY_FUNCTION ? 1 : 0, env); + if (facts & HAVE_TYPE) + f(jl_type_typename, facts & EXACTLY_TYPE ? 1 : 0, env); + if (facts & (HAVE_KWCALL | EXACTLY_KWCALL)) + f(jl_kwcall_type->name, facts & EXACTLY_KWCALL ? 1 : 0, env); + f(jl_any_type->name, facts & EXACTLY_ANY ? 1 : 0, env); return 1; } -static int jl_foreach_reachable_typename(int (*visit)(jl_typename_t *tn, void *env), jl_array_t *mod_array, void *env) -{ - for (size_t i = 0; i < jl_array_nrows(mod_array); i++) { - jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(mod_array, i); - assert(jl_is_module(m)); - if (m->parent == m) // some toplevel modules (really just Base) aren't actually - if (!foreach_typename_in_module(m, visit, env)) - return 0; - } - return 1; -} -int foreach_mtable_in_module( +static int foreach_mtable_in_module( jl_module_t *m, int (*visit)(jl_methtable_t *mt, void *env), void *env) @@ -2102,41 +2164,56 @@ JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, } +static int jl_foreach_top_typename_for(void (*f)(jl_typename_t*, int, void*), jl_value_t *argtypes JL_PROPAGATES_ROOT, int all_subtypes, void *env); + struct _typename_add_backedge { jl_value_t *typ; jl_value_t *caller; }; -static void _typename_add_backedge(jl_typename_t *tn, void *env0) +static void _typename_add_backedge(jl_typename_t *tn, int explct, void *env0) { struct _typename_add_backedge *env = (struct _typename_add_backedge*)env0; JL_GC_PROMISE_ROOTED(env->typ); JL_GC_PROMISE_ROOTED(env->caller); - if (jl_atomic_load_relaxed(&allow_new_worlds)) { - if (!tn->backedges) { - // lazy-init the backedges array - tn->backedges = jl_alloc_vec_any(2); - jl_gc_wb(tn, tn->backedges); - jl_array_ptr_set(tn->backedges, 0, env->typ); - jl_array_ptr_set(tn->backedges, 1, env->caller); + if (!explct) + return; + jl_genericmemory_t *allbackedges = jl_method_table->backedges; + jl_array_t *backedges = (jl_array_t*)jl_eqtable_get(allbackedges, (jl_value_t*)tn, NULL); + if (backedges == NULL) { + backedges = jl_alloc_vec_any(2); + JL_GC_PUSH1(&backedges); + jl_array_del_end(backedges, 2); + jl_genericmemory_t *newtable = jl_eqtable_put(allbackedges, (jl_value_t*)tn, (jl_value_t*)backedges, NULL); + JL_GC_POP(); + if (newtable != allbackedges) { + jl_method_table->backedges = newtable; + jl_gc_wb(jl_method_table, newtable); } - else { - // check if the edge is already present and avoid adding a duplicate - size_t i, l = jl_array_nrows(tn->backedges); - // reuse an already cached instance of this type, if possible - // TODO: use jl_cache_type_(tt) like cache_method does, instead of this linear scan? - for (i = 1; i < l; i += 2) { - if (jl_array_ptr_ref(tn->backedges, i) != env->caller) { - if (jl_types_equal(jl_array_ptr_ref(tn->backedges, i - 1), env->typ)) { - env->typ = jl_array_ptr_ref(tn->backedges, i - 1); - break; - } - } + } + // check if the edge is already present and avoid adding a duplicate + size_t i, l = jl_array_nrows(backedges); + // reuse an already cached instance of this type, if possible + // TODO: use jl_cache_type_(tt) like cache_method does, instead of this linear scan? + // TODO: use as_global_root and de-dup edges array too + for (i = 1; i < l; i += 2) { + if (jl_array_ptr_ref(backedges, i) == env->caller) { + if (jl_types_equal(jl_array_ptr_ref(backedges, i - 1), env->typ)) { + env->typ = jl_array_ptr_ref(backedges, i - 1); + return; // this edge already recorded } - jl_array_ptr_1d_push(tn->backedges, env->typ); - jl_array_ptr_1d_push(tn->backedges, env->caller); } } + for (i = 1; i < l; i += 2) { + if (jl_array_ptr_ref(backedges, i) != env->caller) { + if (jl_types_equal(jl_array_ptr_ref(backedges, i - 1), env->typ)) { + env->typ = jl_array_ptr_ref(backedges, i - 1); + break; + } + } + } + jl_array_ptr_1d_push(backedges, env->typ); + jl_array_ptr_1d_push(backedges, env->caller); } // add a backedge from a non-existent signature to caller @@ -2146,11 +2223,13 @@ JL_DLLEXPORT void jl_method_table_add_backedge(jl_value_t *typ, jl_code_instance if (!jl_atomic_load_relaxed(&allow_new_worlds)) return; // try to pick the best cache(s) for this typ edge - struct _typename_add_backedge env = {typ, (jl_value_t*)caller}; - jl_methcache_t *mc = jl_method_table->cache; + jl_methtable_t *mt = jl_method_table; + jl_methcache_t *mc = mt->cache; JL_LOCK(&mc->writelock); - if (jl_atomic_load_relaxed(&allow_new_worlds)) - jl_foreach_top_typename_for(_typename_add_backedge, typ, &env); + if (jl_atomic_load_relaxed(&allow_new_worlds)) { + struct _typename_add_backedge env = {typ, (jl_value_t*)caller}; + jl_foreach_top_typename_for(_typename_add_backedge, typ, 0, &env); + } JL_UNLOCK(&mc->writelock); } @@ -2164,65 +2243,66 @@ struct _typename_invalidate_backedge { int invalidated; }; -static void _typename_invalidate_backedges(jl_typename_t *tn, void *env0) +static void _typename_invalidate_backedges(jl_typename_t *tn, int explct, void *env0) { struct _typename_invalidate_backedge *env = (struct _typename_invalidate_backedge*)env0; JL_GC_PROMISE_ROOTED(env->type); JL_GC_PROMISE_ROOTED(env->isect); // isJuliaType considers jl_value_t** to be a julia object too JL_GC_PROMISE_ROOTED(env->isect2); // isJuliaType considers jl_value_t** to be a julia object too - if (tn->backedges) { - jl_value_t **backedges = jl_array_ptr_data(tn->backedges); - size_t i, na = jl_array_nrows(tn->backedges); - size_t ins = 0; - for (i = 1; i < na; i += 2) { - jl_value_t *backedgetyp = backedges[i - 1]; - JL_GC_PROMISE_ROOTED(backedgetyp); - int missing = 0; - if (jl_type_intersection2(backedgetyp, (jl_value_t*)env->type, env->isect, env->isect2)) { - // See if the intersection was actually already fully - // covered, but that the new method is ambiguous. - // -> no previous method: now there is one, need to update the missing edge - // -> one+ previously matching method(s): - // -> more specific then all of them: need to update the missing edge - // -> some may have been ambiguous: now there is a replacement - // -> some may have been called: now there is a replacement (also will be detected in the loop later) - // -> less specific or ambiguous with any one of them: can ignore the missing edge (not missing) - // -> some may have been ambiguous: still are - // -> some may have been called: they may be partly replaced (will be detected in the loop later) - // c.f. `is_replacing`, which is a similar query, but with an existing method match to compare against - missing = 1; - for (size_t j = 0; j < env->n; j++) { - jl_method_t *m = env->d[j]; - JL_GC_PROMISE_ROOTED(m); - if (jl_subtype(*env->isect, m->sig) || (*env->isect2 && jl_subtype(*env->isect2, m->sig))) { - // We now know that there actually was a previous - // method for this part of the type intersection. - if (!jl_type_morespecific(env->type, m->sig)) { - missing = 0; - break; - } + jl_array_t *backedges = (jl_array_t*)jl_eqtable_get(jl_method_table->backedges, (jl_value_t*)tn, NULL); + if (backedges == NULL) + return; + jl_value_t **d = jl_array_ptr_data(backedges); + size_t i, na = jl_array_nrows(backedges); + size_t ins = 0; + for (i = 1; i < na; i += 2) { + jl_value_t *backedgetyp = d[i - 1]; + JL_GC_PROMISE_ROOTED(backedgetyp); + int missing = 0; + if (jl_type_intersection2(backedgetyp, (jl_value_t*)env->type, env->isect, env->isect2)) { + // See if the intersection was actually already fully + // covered, but that the new method is ambiguous. + // -> no previous method: now there is one, need to update the missing edge + // -> one+ previously matching method(s): + // -> more specific then all of them: need to update the missing edge + // -> some may have been ambiguous: now there is a replacement + // -> some may have been called: now there is a replacement (also will be detected in the loop later) + // -> less specific or ambiguous with any one of them: can ignore the missing edge (not missing) + // -> some may have been ambiguous: still are + // -> some may have been called: they may be partly replaced (will be detected in the loop later) + // c.f. `is_replacing`, which is a similar query, but with an existing method match to compare against + missing = 1; + for (size_t j = 0; j < env->n; j++) { + jl_method_t *m = env->d[j]; + JL_GC_PROMISE_ROOTED(m); + if (jl_subtype(*env->isect, m->sig) || (*env->isect2 && jl_subtype(*env->isect2, m->sig))) { + // We now know that there actually was a previous + // method for this part of the type intersection. + if (!jl_type_morespecific(env->type, m->sig)) { + missing = 0; + break; } } } - *env->isect = *env->isect2 = NULL; - if (missing) { - jl_code_instance_t *backedge = (jl_code_instance_t*)backedges[i]; - JL_GC_PROMISE_ROOTED(backedge); - invalidate_code_instance(backedge, env->max_world, 0); - env->invalidated = 1; - if (_jl_debug_method_invalidation) - jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)backedgetyp); - } - else { - backedges[ins++] = backedges[i - 1]; - backedges[ins++] = backedges[i - 0]; - } } - if (ins == 0) - tn->backedges = NULL; - else - jl_array_del_end(tn->backedges, na - ins); + *env->isect = *env->isect2 = NULL; + if (missing) { + jl_code_instance_t *backedge = (jl_code_instance_t*)d[i]; + JL_GC_PROMISE_ROOTED(backedge); + invalidate_code_instance(backedge, env->max_world, 0); + env->invalidated = 1; + if (_jl_debug_method_invalidation) + jl_array_ptr_1d_push(_jl_debug_method_invalidation, (jl_value_t*)backedgetyp); + } + else { + d[ins++] = d[i - 1]; + d[ins++] = d[i - 0]; + } } + if (ins == 0) + jl_eqtable_pop(jl_method_table->backedges, (jl_value_t*)tn, NULL, NULL); + else if (na != ins) + jl_array_del_end(backedges, na - ins); } struct invalidate_mt_env { @@ -2390,14 +2470,6 @@ static int erase_all_backedges(jl_methtable_t *mt, void *env) return jl_typemap_visitor(jl_atomic_load_relaxed(&mt->defs), erase_method_backedges, env); } -static int erase_all_mc_backedges(jl_typename_t *tn, void *env) -{ - tn->backedges = NULL; - return 1; -} - -static int jl_foreach_reachable_typename(int (*visit)(jl_typename_t *tn, void *env), jl_array_t *mod_array, void *env); - JL_DLLEXPORT void jl_disable_new_worlds(void) { if (jl_generating_output()) @@ -2410,7 +2482,7 @@ JL_DLLEXPORT void jl_disable_new_worlds(void) jl_foreach_reachable_mtable(erase_all_backedges, mod_array, (void*)NULL); JL_LOCK(&jl_method_table->cache->writelock); - jl_foreach_reachable_typename(erase_all_mc_backedges, mod_array, (void*)NULL); + jl_method_table->backedges = (jl_genericmemory_t*)jl_an_empty_memory_any; JL_UNLOCK(&jl_method_table->cache->writelock); JL_GC_POP(); } @@ -2590,7 +2662,16 @@ void jl_method_table_activate(jl_typemap_entry_t *newentry) jl_methcache_t *mc = jl_method_table->cache; JL_LOCK(&mc->writelock); struct _typename_invalidate_backedge typename_env = {type, &isect, &isect2, d, n, max_world, invalidated}; - jl_foreach_top_typename_for(_typename_invalidate_backedges, type, &typename_env); + if (!jl_foreach_top_typename_for(_typename_invalidate_backedges, type, 1, &typename_env)) { + // if the new method cannot be split into exact backedges, scan the whole table for anything that might be affected + jl_genericmemory_t *allbackedges = jl_method_table->backedges; + for (size_t i = 0, n = allbackedges->length; i < n; i += 2) { + jl_value_t *tn = jl_genericmemory_ptr_ref(allbackedges, i); + jl_value_t *backedges = jl_genericmemory_ptr_ref(allbackedges, i+1); + if (tn && tn != jl_nothing && backedges) + _typename_invalidate_backedges((jl_typename_t*)tn, 0, &typename_env); + } + } invalidated |= typename_env.invalidated; if (oldmi && jl_array_nrows(oldmi)) { // search mc->cache and leafcache and drop anything that might overlap with the new method diff --git a/src/jltypes.c b/src/jltypes.c index 7d731082bd786..9b664ce25861c 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -3004,23 +3004,22 @@ void jl_init_types(void) JL_GC_DISABLED jl_typename_type->name->wrapper = (jl_value_t*)jl_typename_type; jl_typename_type->super = jl_any_type; jl_typename_type->parameters = jl_emptysvec; - jl_typename_type->name->n_uninitialized = 19 - 2; - jl_typename_type->name->names = jl_perm_symsvec(19, "name", "module", "singletonname", + jl_typename_type->name->n_uninitialized = 18 - 2; + jl_typename_type->name->names = jl_perm_symsvec(18, "name", "module", "singletonname", "names", "atomicfields", "constfields", "wrapper", "Typeofwrapper", "cache", "linearcache", - "backedges", "partial", - "hash", "max_args", "n_uninitialized", + "partial", "hash", "max_args", "n_uninitialized", "flags", // "abstract", "mutable", "mayinlinealloc", "cache_entry_count", "max_methods", "constprop_heuristic"); - const static uint32_t typename_constfields[1] = { 0b0001101000001001011 }; // TODO: put back atomicfields and constfields in this list - const static uint32_t typename_atomicfields[1] = { 0b0010010001110000000 }; + const static uint32_t typename_constfields[1] = { 0b000110100001001011 }; // TODO: put back atomicfields and constfields in this list + const static uint32_t typename_atomicfields[1] = { 0b001001001110000000 }; jl_typename_type->name->constfields = typename_constfields; jl_typename_type->name->atomicfields = typename_atomicfields; jl_precompute_memoized_dt(jl_typename_type, 1); - jl_typename_type->types = jl_svec(19, jl_symbol_type, jl_any_type /*jl_module_type*/, jl_symbol_type, + jl_typename_type->types = jl_svec(18, jl_symbol_type, jl_any_type /*jl_module_type*/, jl_symbol_type, jl_simplevector_type, jl_any_type/*jl_voidpointer_type*/, jl_any_type/*jl_voidpointer_type*/, - jl_type_type, jl_type_type, jl_simplevector_type, jl_simplevector_type, + jl_type_type, jl_simplevector_type, jl_simplevector_type, jl_methcache_type, jl_any_type, jl_any_type /*jl_long_type*/, jl_any_type /*jl_int32_type*/, @@ -3046,13 +3045,13 @@ void jl_init_types(void) JL_GC_DISABLED jl_methtable_type->super = jl_any_type; jl_methtable_type->parameters = jl_emptysvec; jl_methtable_type->name->n_uninitialized = 0; - jl_methtable_type->name->names = jl_perm_symsvec(4, "defs", "cache", "name", "module"); - const static uint32_t methtable_constfields[1] = { 0b1110 }; - const static uint32_t methtable_atomicfields[1] = { 0b0001 }; + jl_methtable_type->name->names = jl_perm_symsvec(5, "defs", "cache", "name", "module", "backedges"); + const static uint32_t methtable_constfields[1] = { 0b01110 }; + const static uint32_t methtable_atomicfields[1] = { 0b00001 }; jl_methtable_type->name->constfields = methtable_constfields; jl_methtable_type->name->atomicfields = methtable_atomicfields; jl_precompute_memoized_dt(jl_methtable_type, 1); - jl_methtable_type->types = jl_svec(4, jl_any_type, jl_methcache_type, jl_symbol_type, jl_any_type /*jl_module_type*/); + jl_methtable_type->types = jl_svec(5, jl_any_type, jl_methcache_type, jl_symbol_type, jl_any_type /*jl_module_type*/, jl_any_type); jl_symbol_type->name = jl_new_typename_in(jl_symbol("Symbol"), core, 0, 1); jl_symbol_type->name->wrapper = (jl_value_t*)jl_symbol_type; @@ -3378,6 +3377,7 @@ void jl_init_types(void) JL_GC_DISABLED core = jl_core_module; jl_method_table->module = core; jl_atomic_store_relaxed(&jl_method_table->cache->leafcache, (jl_genericmemory_t*)jl_an_empty_memory_any); + jl_method_table->backedges = (jl_genericmemory_t*)jl_an_empty_memory_any; jl_atomic_store_relaxed(&core->bindingkeyset, (jl_genericmemory_t*)jl_an_empty_memory_any); // export own name, so "using Foo" makes "Foo" itself visible jl_set_initial_const(core, core->name, (jl_value_t*)core, 1); @@ -3842,13 +3842,13 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_typename_type->types, 5, jl_voidpointer_type); jl_svecset(jl_typename_type->types, 6, jl_type_type); jl_svecset(jl_typename_type->types, 7, jl_type_type); - jl_svecset(jl_typename_type->types, 12, jl_long_type); + jl_svecset(jl_typename_type->types, 11, jl_long_type); + jl_svecset(jl_typename_type->types, 12, jl_int32_type); jl_svecset(jl_typename_type->types, 13, jl_int32_type); - jl_svecset(jl_typename_type->types, 14, jl_int32_type); + jl_svecset(jl_typename_type->types, 14, jl_uint8_type); jl_svecset(jl_typename_type->types, 15, jl_uint8_type); jl_svecset(jl_typename_type->types, 16, jl_uint8_type); jl_svecset(jl_typename_type->types, 17, jl_uint8_type); - jl_svecset(jl_typename_type->types, 18, jl_uint8_type); jl_svecset(jl_methcache_type->types, 2, jl_long_type); // voidpointer jl_svecset(jl_methcache_type->types, 3, jl_long_type); // uint32_t plus alignment jl_svecset(jl_methtable_type->types, 3, jl_module_type); diff --git a/src/julia.h b/src/julia.h index 6c1c8af0a788b..3dd2088ecf207 100644 --- a/src/julia.h +++ b/src/julia.h @@ -520,7 +520,6 @@ typedef struct { _Atomic(jl_value_t*) Typeofwrapper; // cache for Type{wrapper} _Atomic(jl_svec_t*) cache; // sorted array _Atomic(jl_svec_t*) linearcache; // unsorted array - jl_array_t *backedges; // uncovered (sig => caller::CodeInstance) pairs with this type as the function jl_array_t *partial; // incomplete instantiations of this type intptr_t hash; _Atomic(int32_t) max_args; // max # of non-vararg arguments in a signature with this type as the function @@ -882,6 +881,7 @@ typedef struct _jl_methtable_t { jl_methcache_t *cache; jl_sym_t *name; // sometimes used for debug printing jl_module_t *module; // sometimes used for debug printing + jl_genericmemory_t *backedges; // IdDict{top typenames, Vector{uncovered (sig => caller::CodeInstance)}} } jl_methtable_t; typedef struct { diff --git a/src/julia_internal.h b/src/julia_internal.h index 24fb7fdb05f90..ed5aea6c80aef 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -857,7 +857,6 @@ jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); jl_function_t *jl_new_generic_function(jl_sym_t *name, jl_module_t *module, size_t new_world); jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_t *module, jl_datatype_t *st, size_t new_world); int jl_foreach_reachable_mtable(int (*visit)(jl_methtable_t *mt, void *env), jl_array_t *mod_array, void *env); -int foreach_mtable_in_module(jl_module_t *m, int (*visit)(jl_methtable_t *mt, void *env), void *env); void jl_init_main_module(void); JL_DLLEXPORT int jl_is_submodule(jl_module_t *child, jl_module_t *parent) JL_NOTSAFEPOINT; jl_array_t *jl_get_loaded_modules(void); @@ -939,7 +938,6 @@ JL_DLLEXPORT jl_methtable_t *jl_method_get_table( jl_method_t *method JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; JL_DLLEXPORT jl_methcache_t *jl_method_get_cache( jl_method_t *method JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT; -void jl_foreach_top_typename_for(void (*f)(jl_typename_t*, void*), jl_value_t *argtypes JL_PROPAGATES_ROOT, void *env); JL_DLLEXPORT int jl_pointer_egal(jl_value_t *t); JL_DLLEXPORT jl_value_t *jl_nth_slot_type(jl_value_t *sig JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; diff --git a/src/method.c b/src/method.c index 77863b27e24b6..eba485ff9eb69 100644 --- a/src/method.c +++ b/src/method.c @@ -1154,39 +1154,6 @@ JL_DLLEXPORT jl_value_t *jl_declare_const_gf(jl_module_t *mod, jl_sym_t *name) return gf; } -static void foreach_top_nth_typename(void (*f)(jl_typename_t*, void*), jl_value_t *a JL_PROPAGATES_ROOT, int n, void *env) -{ - if (jl_is_datatype(a)) { - if (n == 0) { - jl_datatype_t *dt = ((jl_datatype_t*)a); - jl_typename_t *tn = NULL; - while (1) { - if (dt != jl_any_type && dt != jl_function_type) - tn = dt->name; - if (dt->super == dt) - break; - dt = dt->super; - } - if (tn) - f(tn, env); - } - else if (jl_is_tuple_type(a)) { - if (jl_nparams(a) >= n) - foreach_top_nth_typename(f, jl_tparam(a, n - 1), 0, env); - } - } - else if (jl_is_typevar(a)) { - foreach_top_nth_typename(f, ((jl_tvar_t*)a)->ub, n, env); - } - else if (jl_is_unionall(a)) { - foreach_top_nth_typename(f, ((jl_unionall_t*)a)->body, n, env); - } - else if (jl_is_uniontype(a)) { - jl_uniontype_t *u = (jl_uniontype_t*)a; - foreach_top_nth_typename(f, u->a, n, env); - foreach_top_nth_typename(f, u->b, n, env); - } -} // get the MethodTable for dispatch, or `nothing` if cannot be determined JL_DLLEXPORT jl_methtable_t *jl_method_table_for(jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT @@ -1200,11 +1167,6 @@ JL_DLLEXPORT jl_methcache_t *jl_method_cache_for(jl_value_t *argtypes JL_PROPAGA return jl_method_table->cache; } -void jl_foreach_top_typename_for(void (*f)(jl_typename_t*, void*), jl_value_t *argtypes JL_PROPAGATES_ROOT, void *env) -{ - foreach_top_nth_typename(f, argtypes, 1, env); -} - jl_methcache_t *jl_kwmethod_cache_for(jl_value_t *argtypes JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { return jl_method_table->cache; diff --git a/src/staticdata.c b/src/staticdata.c index 92e7f494ad35d..3df8e7414420c 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -867,20 +867,30 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ assert(!jl_object_in_image((jl_value_t*)tn->module)); assert(!jl_object_in_image((jl_value_t*)tn->wrapper)); } + } + if (jl_is_mtable(v)) { + jl_methtable_t *mt = (jl_methtable_t*)v; // Any back-edges will be re-validated and added by staticdata.jl, so // drop them from the image here if (s->incremental || jl_options.trim || jl_options.strip_ir) { - record_field_change((jl_value_t**)&tn->backedges, NULL); + record_field_change((jl_value_t**)&mt->backedges, jl_an_empty_memory_any); } else { // don't recurse into all backedges memory (yet) - jl_value_t *backedges = get_replaceable_field((jl_value_t**)&tn->backedges, 1); - if (backedges) { - jl_queue_for_serialization_(s, (jl_value_t*)((jl_array_t*)backedges)->ref.mem, 0, 1); - for (size_t i = 0, n = jl_array_nrows(backedges); i < n; i += 2) { - jl_value_t *t = jl_array_ptr_ref(backedges, i); - assert(!jl_is_code_instance(t)); - jl_queue_for_serialization(s, t); + jl_value_t *allbackedges = get_replaceable_field((jl_value_t**)&mt->backedges, 1); + jl_queue_for_serialization_(s, allbackedges, 0, 1); + for (size_t i = 0, n = ((jl_genericmemory_t*)allbackedges)->length; i < n; i += 2) { + jl_value_t *tn = jl_genericmemory_ptr_ref(allbackedges, i); + jl_queue_for_serialization(s, tn); + jl_value_t *backedges = jl_genericmemory_ptr_ref(allbackedges, i + 1); + if (backedges && backedges != jl_nothing) { + jl_queue_for_serialization_(s, (jl_value_t*)((jl_array_t*)backedges)->ref.mem, 0, 1); + jl_queue_for_serialization(s, backedges); + for (size_t i = 0, n = jl_array_nrows(backedges); i < n; i += 2) { + jl_value_t *t = jl_array_ptr_ref(backedges, i); + assert(!jl_is_code_instance(t)); + jl_queue_for_serialization(s, t); + } } } } @@ -2573,8 +2583,6 @@ static void jl_prune_mi_backedges(jl_array_t *backedges) static void jl_prune_tn_backedges(jl_array_t *backedges) { - if (backedges == NULL) - return; size_t i = 0, ins = 0, n = jl_array_nrows(backedges); for (i = 1; i < n; i += 2) { jl_value_t *ci = jl_array_ptr_ref(backedges, i); @@ -2586,6 +2594,15 @@ static void jl_prune_tn_backedges(jl_array_t *backedges) jl_array_del_end(backedges, n - ins); } +static void jl_prune_mt_backedges(jl_genericmemory_t *allbackedges) +{ + for (size_t i = 0, n = allbackedges->length; i < n; i += 2) { + jl_value_t *tn = jl_genericmemory_ptr_ref(allbackedges, i); + jl_value_t *backedges = jl_genericmemory_ptr_ref(allbackedges, i + 1); + if (tn && tn != jl_nothing && backedges) + jl_prune_tn_backedges((jl_array_t*)backedges); + } +} static void jl_prune_binding_backedges(jl_array_t *backedges) { @@ -3240,8 +3257,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_prune_type_cache_hash(jl_atomic_load_relaxed(&tn->cache))); jl_gc_wb(tn, jl_atomic_load_relaxed(&tn->cache)); jl_prune_type_cache_linear(jl_atomic_load_relaxed(&tn->linearcache)); - jl_value_t *backedges = get_replaceable_field((jl_value_t**)&tn->backedges, 1); - jl_prune_tn_backedges((jl_array_t*)backedges); } else if (jl_is_method_instance(v)) { jl_method_instance_t *mi = (jl_method_instance_t*)v; @@ -3253,6 +3268,11 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_value_t *backedges = get_replaceable_field((jl_value_t**)&b->backedges, 1); jl_prune_binding_backedges((jl_array_t*)backedges); } + else if (jl_is_mtable(v)) { + jl_methtable_t *mt = (jl_methtable_t*)v; + jl_value_t *backedges = get_replaceable_field((jl_value_t**)&mt->backedges, 1); + jl_prune_mt_backedges((jl_genericmemory_t*)backedges); + } } } diff --git a/test/misc.jl b/test/misc.jl index d72ad5b58ad12..2b87a2ad26f0f 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -1632,10 +1632,10 @@ end let errs = IOBuffer() run(`$(Base.julia_cmd()) -e ' using Test - @test isdefined(Type.body.name, :backedges) + @test !isempty(Core.GlobalMethods.backedges) Base.Experimental.disable_new_worlds() @test_throws "disable_new_worlds" @eval f() = 1 - @test !isdefined(Type.body.name, :backedges) + @test isempty(Core.GlobalMethods.backedges) @test_throws "disable_new_worlds" Base.delete_method(which(+, (Int, Int))) @test 1+1 == 2 using Dates