From 9fbd135550f240b0a45822008a0d5fb6a0ce28b1 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 28 May 2020 14:06:23 -0400 Subject: [PATCH 1/6] IdDict: handle size zero inputs gracefully --- src/gf.c | 10 +++++----- src/iddict.c | 9 +++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/gf.c b/src/gf.c index ca51b5f24917b..49f3c5a5709e3 100644 --- a/src/gf.c +++ b/src/gf.c @@ -418,11 +418,11 @@ static void foreach_mtable_in_module( jl_module_t *m, void (*visit)(jl_methtable_t *mt, void *env), void *env, - jl_array_t *visited) + jl_array_t **visited) { size_t i; void **table = m->bindings.table; - jl_eqtable_put(visited, (jl_value_t*)m, jl_true, NULL); + *visited = jl_eqtable_put(*visited, (jl_value_t*)m, jl_true, NULL); for (i = 1; i < m->bindings.size; i += 2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; @@ -440,7 +440,7 @@ static void foreach_mtable_in_module( else if (jl_is_module(v)) { jl_module_t *child = (jl_module_t*)v; if (child != m && child->parent == m && child->name == b->name && - !jl_eqtable_get(visited, v, NULL)) { + !jl_eqtable_get(*visited, v, NULL)) { // this is the original/primary binding for the submodule foreach_mtable_in_module(child, visit, env, visited); } @@ -463,11 +463,11 @@ void jl_foreach_reachable_mtable(void (*visit)(jl_methtable_t *mt, void *env), v jl_module_t *m = (jl_module_t*)jl_array_ptr_ref(mod_array, i); assert(jl_is_module(m)); if (!jl_eqtable_get(visited, (jl_value_t*)m, NULL)) - foreach_mtable_in_module(m, visit, env, visited); + foreach_mtable_in_module(m, visit, env, &visited); } } else { - foreach_mtable_in_module(jl_main_module, visit, env, visited); + foreach_mtable_in_module(jl_main_module, visit, env, &visited); } JL_GC_POP(); } diff --git a/src/iddict.c b/src/iddict.c index 6bec76563d07d..4245ce61a5f80 100644 --- a/src/iddict.c +++ b/src/iddict.c @@ -37,7 +37,11 @@ static int jl_table_assign_bp(jl_array_t **pa, jl_value_t *key, jl_value_t *val) jl_array_t *a = *pa; size_t orig, index, iter, empty_slot; size_t newsz, sz = hash_size(a); - assert(sz >= 1); + if (sz == 0) { + a = jl_alloc_vec_any(HT_N_INLINE); + sz = hash_size(a); + *pa = a; + } size_t maxprobe = max_probe(sz); void **tab = (void **)a->data; @@ -108,7 +112,8 @@ static int jl_table_assign_bp(jl_array_t **pa, jl_value_t *key, jl_value_t *val) jl_value_t **jl_table_peek_bp(jl_array_t *a, jl_value_t *key) JL_NOTSAFEPOINT { size_t sz = hash_size(a); - assert(sz >= 1); + if (sz == 0) + return NULL; size_t maxprobe = max_probe(sz); void **tab = (void **)a->data; uint_t hv = keyhash(key); From 40bb06d62f51d1f213d43a188b5b9fc8f976177b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 01:35:33 -0400 Subject: [PATCH 2/6] fix bad tests --- test/channels.jl | 2 ++ test/reflection.jl | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/channels.jl b/test/channels.jl index 0eb1f589f4f5c..d4d8cf6e2e67d 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -390,6 +390,7 @@ end t = Timer(0) do t tc[] += 1 end + Libc.systemsleep(0.005) @test isopen(t) Base.process_events() @test !isopen(t) @@ -402,6 +403,7 @@ end t = Timer(0) do t tc[] += 1 end + Libc.systemsleep(0.005) @test isopen(t) close(t) @test !isopen(t) diff --git a/test/reflection.jl b/test/reflection.jl index 4b77f2e2ac058..23a6ff0dfbe69 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -507,7 +507,6 @@ f18888() = nothing let world = Core.Compiler.get_world_counter() m = first(methods(f18888, Tuple{})) - @test isempty(m.specializations) ft = typeof(f18888) code_typed(f18888, Tuple{}; optimize=false) From 166031c06cefa1b10d6e7eb1ae1af4a87d05328f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 02:01:24 -0400 Subject: [PATCH 3/6] improve code quality --- base/errorshow.jl | 2 +- stdlib/REPL/src/REPLCompletions.jl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/errorshow.jl b/base/errorshow.jl index 05c6c6d787ecd..2f925c11b29ca 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -176,7 +176,7 @@ function showerror(io::IO, ex::InexactError) Experimental.show_error_hints(io, ex) end -typesof(args...) = Tuple{Any[ Core.Typeof(a) for a in args ]...} +typesof(@nospecialize args...) = Tuple{Any[ Core.Typeof(args[i]) for i in 1:length(args) ]...} function print_with_compare(io::IO, @nospecialize(a::DataType), @nospecialize(b::DataType), color::Symbol) if a.name === b.name diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index a16565bba3751..4007122aee41d 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -395,7 +395,7 @@ end # Returns the return type. example: get_type(:(Base.strip("", ' ')), Main) returns (String, true) function try_get_type(sym::Expr, fn::Module) val, found = get_value(sym, fn) - found && return Base.typesof(val).parameters[1], found + found && return Core.Typeof(val), found if sym.head === :call # getfield call is special cased as the evaluation of getfield provides good type information, # is inexpensive and it is also performed in the complete_symbol function. @@ -403,7 +403,7 @@ function try_get_type(sym::Expr, fn::Module) if isa(a1,GlobalRef) && isconst(a1.mod,a1.name) && isdefined(a1.mod,a1.name) && eval(a1) === Core.getfield val, found = get_value_getfield(sym, Main) - return found ? Base.typesof(val).parameters[1] : Any, found + return found ? Core.Typeof(val) : Any, found end return get_type_call(sym) elseif sym.head === :thunk @@ -430,7 +430,7 @@ end function get_type(sym, fn::Module) val, found = get_value(sym, fn) - return found ? Base.typesof(val).parameters[1] : Any, found + return found ? Core.Typeof(val) : Any, found end # Method completion on function call expression that look like :(max(1)) From f4a983d51b91f3fe7ef2e4f4c27ff51fabfe8add Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 14:13:50 -0400 Subject: [PATCH 4/6] remove incorrect assertion Since we are doing a subtype search (like a the top of the function), the resulting object from the search does not need to be the same. --- src/gf.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/gf.c b/src/gf.c index 49f3c5a5709e3..91d0d89afcd3d 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1072,26 +1072,15 @@ static jl_method_instance_t *cache_method( temp2 = (jl_value_t*)simplett; } - // short-circuit if this exact entry is already present - // to avoid adding a new duplicate copy of it - if (cachett != tt && simplett == NULL) { - struct jl_typemap_assoc search = {(jl_value_t*)cachett, min_valid, NULL, 0, ~(size_t)0}; + // short-circuit if an existing entry is already present + // that satisfies our requirements + if (cachett != tt) { + struct jl_typemap_assoc search = {(jl_value_t*)cachett, world, NULL, 0, ~(size_t)0}; jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(*cache, &search, offs, /*subtype*/1); - if (entry && (jl_value_t*)entry->simplesig == jl_nothing) { - if (jl_egal((jl_value_t*)guardsigs, (jl_value_t*)entry->guardsigs)) { - // just update the existing entry to reflect new knowledge - if (entry->min_world > min_valid) - entry->min_world = min_valid; - if (entry->max_world < max_valid) - entry->max_world = max_valid; - if (entry->func.linfo == NULL) { - entry->func.linfo = newmeth; - jl_gc_wb(entry, newmeth); - } - assert(entry->func.linfo == newmeth); - JL_GC_POP(); - return newmeth; - } + if (entry && jl_egal((jl_value_t*)entry->simplesig, simplett ? (jl_value_t*)simplett : jl_nothing) && + jl_egal((jl_value_t*)guardsigs, (jl_value_t*)entry->guardsigs)) { + JL_GC_POP(); + return entry->func.linfo; } } From be77eb8c1d7eb99ce60a7ece077dbbf85b1ab16c Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 28 May 2020 14:44:12 -0400 Subject: [PATCH 5/6] For dispatch, move from using a tree to a hash lookup of leaf types This lets us put more objects in here without incurring additional search code (just the initial cost of computing the hash for the tuple type lookup computation). --- src/ast.c | 2 +- src/builtins.c | 3 +- src/datatype.c | 1 + src/dump.c | 3 - src/gf.c | 213 ++++++++++++++++++++++++++++++++++--------- src/init.c | 1 - src/jltypes.c | 42 ++++----- src/julia.h | 1 + src/julia_internal.h | 17 ++-- src/precompile.c | 7 -- src/typemap.c | 61 +++++++------ 11 files changed, 232 insertions(+), 119 deletions(-) diff --git a/src/ast.c b/src/ast.c index 3b4b3e5d78f7d..51cf262ad9ff2 100644 --- a/src/ast.c +++ b/src/ast.c @@ -962,7 +962,7 @@ static jl_value_t *jl_invoke_julia_macro(jl_array_t *args, jl_module_t *inmodule jl_value_t *result; JL_TRY { margs[0] = jl_toplevel_eval(*ctx, margs[0]); - jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, 1, world); + jl_method_instance_t *mfunc = jl_method_lookup(margs, nargs, world); JL_GC_PROMISE_ROOTED(mfunc); if (mfunc == NULL) { jl_method_error(margs[0], &margs[1], nargs, world); diff --git a/src/builtins.c b/src/builtins.c index 9c9fec857f9da..3ea207cf7d55a 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -990,8 +990,7 @@ JL_CALLABLE(jl_f_applicable) { JL_NARGSV(applicable, 1); size_t world = jl_get_ptls_states()->world_age; - return jl_method_lookup(args, nargs, 1, world) != NULL ? - jl_true : jl_false; + return jl_method_lookup(args, nargs, world) != NULL ? jl_true : jl_false; } JL_CALLABLE(jl_f_invoke) diff --git a/src/datatype.c b/src/datatype.c index 0260281c7470b..d22551895d99a 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -49,6 +49,7 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo mt->name = jl_demangle_typename(name); mt->module = module; mt->defs = jl_nothing; + mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; mt->cache = jl_nothing; mt->max_args = 0; mt->kwsorter = NULL; diff --git a/src/dump.c b/src/dump.c index 7cb4ffc47d092..35303932bc161 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2237,9 +2237,6 @@ STATIC_INLINE jl_value_t *verify_type(jl_value_t *v) JL_NOTSAFEPOINT } #endif -jl_datatype_t *jl_lookup_cache_type_(jl_datatype_t *type); -void jl_cache_type_(jl_datatype_t *type); - static jl_datatype_t *jl_recache_type(jl_datatype_t *dt) JL_GC_DISABLED { jl_datatype_t *t; // the type after unique'ing diff --git a/src/gf.c b/src/gf.c index 91d0d89afcd3d..08de860090bed 100644 --- a/src/gf.c +++ b/src/gf.c @@ -231,7 +231,8 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a m->sig = (jl_value_t*)jl_anytuple_type; m->slot_syms = jl_an_empty_string; - JL_GC_PUSH1(&m); + jl_typemap_entry_t *newentry = NULL; + JL_GC_PUSH2(&m, &newentry); jl_method_instance_t *mi = jl_get_specialized(m, (jl_value_t*)jl_anytuple_type, jl_emptysvec); m->unspecialized = mi; jl_gc_wb(m, mi); @@ -242,8 +243,10 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a codeinst->invoke = jl_fptr_args; jl_methtable_t *mt = dt->name->mt; - jl_typemap_insert(&mt->cache, (jl_value_t*)mt, jl_anytuple_type, - NULL, jl_emptysvec, (jl_value_t*)mi, 0, &lambda_cache, 1, ~(size_t)0); + newentry = jl_typemap_alloc(jl_anytuple_type, NULL, jl_emptysvec, + (jl_value_t*)mi, 1, ~(size_t)0); + jl_typemap_insert(&mt->cache, (jl_value_t*)mt, newentry, 0, &lambda_cache); + mt->frozen = 1; JL_GC_POP(); return dt; @@ -475,8 +478,10 @@ void jl_foreach_reachable_mtable(void (*visit)(jl_methtable_t *mt, void *env), v static void reset_mt_caches(jl_methtable_t *mt, void *env) { // removes all method caches - if (mt->defs != jl_nothing) // make sure not to reset builtin functions + if (mt->defs != jl_nothing) { // make sure not to reset builtin functions + mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; mt->cache = jl_nothing; + } jl_typemap_visitor(mt->defs, get_method_unspec_list, env); } @@ -543,9 +548,10 @@ jl_value_t *jl_nth_slot_type(jl_value_t *sig, size_t i) // return 1; //} -static jl_value_t *ml_matches(jl_typemap_t *ml, int offs, +static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, jl_tupletype_t *type, int lim, int include_ambiguous, - size_t world, size_t *min_valid, size_t *max_valid); + size_t world, size_t *min_valid, size_t *max_valid, + int cache_result); // get the compilation signature specialization for this method static void jl_compilation_sig( @@ -930,6 +936,19 @@ JL_DLLEXPORT int jl_isa_compileable_sig( return 1; } +static inline jl_typemap_entry_t *lookup_leafcache(jl_array_t *leafcache JL_PROPAGATES_ROOT, jl_value_t *tt, size_t world) JL_NOTSAFEPOINT +{ + jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_eqtable_get(leafcache, (jl_value_t*)tt, NULL); + if (entry) { + do { + if (entry->min_world <= world && world <= entry->max_world) + return entry; + entry = entry->next; + } while ((jl_value_t*)entry != jl_nothing); + } + return NULL; +} + static jl_method_instance_t *cache_method( jl_methtable_t *mt, jl_typemap_t **cache, jl_value_t *parent JL_PROPAGATES_ROOT, jl_tupletype_t *tt, // the original tupletype of the signature @@ -941,6 +960,12 @@ static jl_method_instance_t *cache_method( // short-circuit (now that we hold the lock) if this entry is already present int8_t offs = mt ? jl_cachearg_offset(mt) : 1; { // scope block + if (mt) { + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + if (entry) + return entry->func.linfo; + } struct jl_typemap_assoc search = {(jl_value_t*)tt, world, NULL, 0, ~(size_t)0}; jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(*cache, &search, offs, /*subtype*/1); if (entry && entry->func.value) @@ -976,7 +1001,7 @@ static jl_method_instance_t *cache_method( if (!cache_with_orig && mt) { // now examine what will happen if we chose to use this sig in the cache // TODO: should we first check `compilationsig <: definition`? - temp = ml_matches(mt->defs, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid, &max_valid); + temp = ml_matches(mt, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid, &max_valid, 0); int guards = 0; if (temp == jl_false) { cache_with_orig = 1; @@ -1039,7 +1064,7 @@ static jl_method_instance_t *cache_method( if (cache_with_orig && mt) { // now examine defs to determine the min/max-valid range for this lookup result - (void)ml_matches(mt->defs, 0, cachett, -1, 0, world, &min_valid, &max_valid); + (void)ml_matches(mt, 0, cachett, -1, 0, world, &min_valid, &max_valid, 0); } assert(mt == NULL || min_valid > 1); @@ -1084,9 +1109,27 @@ static jl_method_instance_t *cache_method( } } - jl_typemap_insert(cache, parent, cachett, simplett, guardsigs, - (jl_value_t*)newmeth, offs, &lambda_cache, - min_valid, max_valid); + jl_typemap_entry_t *newentry = jl_typemap_alloc(cachett, simplett, guardsigs, (jl_value_t*)newmeth, min_valid, max_valid); + temp = (jl_value_t*)newentry; + if (mt && cachett == tt && simplett == NULL && jl_svec_len(guardsigs) == 0) { + if (!jl_has_free_typevars((jl_value_t*)tt) && jl_lookup_cache_type_(tt) == NULL) { + // if this type isn't normally in the cache, force it in there now + // anyways so that we can depend on it as a token (especially since + // we just cached it in memory as this method signature anyways) + JL_LOCK(&typecache_lock); + if (jl_lookup_cache_type_(tt) == NULL) + jl_cache_type_(tt); + JL_UNLOCK(&typecache_lock); // Might GC + } + jl_typemap_entry_t *old = (jl_typemap_entry_t*)jl_eqtable_get(mt->leafcache, (jl_value_t*)tt, jl_nothing); + newentry->next = old; + jl_gc_wb(newentry, old); + mt->leafcache = jl_eqtable_put(mt->leafcache, (jl_value_t*)tt, (jl_value_t*)newentry, NULL); + jl_gc_wb(mt, mt->leafcache); + } + else { + jl_typemap_insert(cache, parent, newentry, offs, &lambda_cache); + } JL_GC_POP(); return newmeth; @@ -1160,12 +1203,20 @@ static jl_typemap_entry_t *jl_typemap_morespecific_by_type(jl_typemap_entry_t *f return candidate; } -static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *tt, int mt_cache, size_t world) +static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *tt, size_t world) { // caller must hold the mt->writelock + assert(tt->isdispatchtuple || tt->hasfreetypevars); + if (tt->isdispatchtuple) { + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + if (entry) + return entry->func.linfo; + } + struct jl_typemap_assoc search = {(jl_value_t*)tt, world, NULL, 0, ~(size_t)0}; jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, &search, jl_cachearg_offset(mt), /*subtype*/1); - if (entry && entry->func.value) + if (entry) return entry->func.linfo; jl_method_instance_t *nf = NULL; @@ -1178,17 +1229,7 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype entry = jl_typemap_morespecific_by_type(entry, (jl_value_t*)tt, &search.env, world); if (entry != NULL) { jl_method_t *m = entry->func.method; - jl_svec_t *env = search.env; - if (!mt_cache) { - intptr_t nspec = (mt == jl_type_type_mt ? m->nargs + 1 : mt->max_args + 2); - jl_compilation_sig(tt, env, m, nspec, &newparams); - if (newparams) - tt = jl_apply_tuple_type(newparams); - nf = jl_specializations_get_linfo(m, (jl_value_t*)tt, env); - } - else { - nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, env); - } + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, search.env); } } JL_GC_POP(); @@ -1675,15 +1716,22 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho (void)check_ambiguous_matches(mt->defs, methodentry, check_disabled_ambiguous_visitor); // drop this method from mt->cache struct invalidate_mt_env mt_cache_env; - mt_cache_env.max_world = methodentry->max_world - 1; + mt_cache_env.max_world = methodentry->max_world; mt_cache_env.shadowed = (jl_value_t*)method; jl_typemap_visitor(mt->cache, invalidate_mt_cache, (void*)&mt_cache_env); + jl_array_t *leafcache = mt->leafcache; + size_t i, l = jl_array_len(leafcache); + for (i = 1; i < l; i += 2) { + jl_value_t *l = jl_array_ptr_ref(leafcache, i); + if (l && l != jl_nothing) + invalidate_mt_cache((jl_typemap_entry_t*)l, (void*)&mt_cache_env); + } // Invalidate the backedges - jl_svec_t *specializations = methodentry->func.method->specializations; int invalidated = 0; + jl_svec_t *specializations = methodentry->func.method->specializations; jl_value_t *loctag = NULL; JL_GC_PUSH1(&loctag); - size_t i, l = jl_svec_len(specializations); + l = jl_svec_len(specializations); for (i = 0; i < l; i++) { jl_method_instance_t *mi = (jl_method_instance_t*)jl_svecref(specializations, i); if (mi) { @@ -1718,7 +1766,8 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method size_t max_world = method->primary_world - 1; int invalidated = 0; jl_value_t *loctag = NULL; // debug info for invalidation - JL_GC_PUSH2(&oldvalue, &loctag); + jl_typemap_entry_t *newentry = NULL; + JL_GC_PUSH3(&oldvalue, &newentry, &loctag); JL_LOCK(&mt->writelock); // first delete the existing entry (we'll disable it later) struct jl_typemap_assoc search = {(jl_value_t*)type, method->primary_world, NULL, 0, ~(size_t)0}; @@ -1728,9 +1777,9 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method // TODO: just append our new entry right here } // then add our new entry - jl_typemap_entry_t *newentry = jl_typemap_insert(&mt->defs, (jl_value_t*)mt, - (jl_tupletype_t*)type, simpletype, jl_emptysvec, (jl_value_t*)method, 0, &method_defs, - method->primary_world, method->deleted_world); + newentry = jl_typemap_alloc((jl_tupletype_t*)type, simpletype, jl_emptysvec, + (jl_value_t*)method, method->primary_world, method->deleted_world); + jl_typemap_insert(&mt->defs, (jl_value_t*)mt, newentry, 0, &method_defs); oldvalue = check_ambiguous_matches(mt->defs, newentry, check_ambiguous_visitor); if (oldentry) { oldvalue = oldentry->func.value; @@ -1769,9 +1818,17 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method mt_cache_env.max_world = max_world; mt_cache_env.shadowed = oldvalue; jl_typemap_visitor(mt->cache, invalidate_mt_cache, (void*)&mt_cache_env); - //TODO: if it's small, might it be better to drop it all too? + jl_array_t *leafcache = mt->leafcache; + size_t i, l = jl_array_len(leafcache); + for (i = 1; i < l; i += 2) { + jl_value_t *l = jl_array_ptr_ref(leafcache, i); + if (l && l != jl_nothing) + invalidate_mt_cache((jl_typemap_entry_t*)l, (void*)&mt_cache_env); + } + //TODO: if it's small, might it be better to drop it all? //if (mt != jl_type_type_mt) { // mt->cache = jl_nothing; + // mt->leafcache = jl_an_empty_vec_any; //} jl_value_t **d; @@ -1855,17 +1912,21 @@ jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs return jl_inst_arg_tuple_type(arg1, args, nargs, 1); } -jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, int cache, size_t world) +jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, size_t world) { assert(nargs > 0 && "expected caller to handle this case"); jl_methtable_t *mt = jl_gf_mtable(args[0]); jl_typemap_entry_t *entry = jl_typemap_assoc_exact(mt->cache, args[0], &args[1], nargs, jl_cachearg_offset(mt), world); if (entry) return entry->func.linfo; - JL_LOCK(&mt->writelock); jl_tupletype_t *tt = arg_type_tuple(args[0], &args[1], nargs); + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + if (entry) + return entry->func.linfo; JL_GC_PUSH1(&tt); - jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, tt, cache, world); + JL_LOCK(&mt->writelock); + jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, tt, world); JL_GC_POP(); JL_UNLOCK(&mt->writelock); return sf; @@ -1888,7 +1949,7 @@ JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int jl_methtable_t *mt = jl_method_table_for(unw); if ((jl_value_t*)mt == jl_nothing) return jl_false; // indeterminate - ml_matches can't deal with this case - return ml_matches(mt->defs, 0, types, lim, include_ambiguous, world, min_valid, max_valid); + return ml_matches(mt, 0, types, lim, include_ambiguous, world, min_valid, max_valid, 1); } jl_method_instance_t *jl_get_unspecialized(jl_method_instance_t *method JL_PROPAGATES_ROOT) @@ -2335,16 +2396,23 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t LOOP_BODY(3); #undef LOOP_BODY i = 4; - // if no method was found in the associative cache, check the full cache + jl_tupletype_t *tt = NULL; + int64_t last_alloc; if (i == 4) { + // if no method was found in the associative cache, check the full cache JL_TIMING(METHOD_LOOKUP_FAST); mt = jl_gf_mtable(F); entry = jl_typemap_assoc_exact(mt->cache, F, args, nargs, jl_cachearg_offset(mt), world); + if (entry == NULL) { + last_alloc = jl_options.malloc_log ? jl_gc_diff_total_bytes() : 0; + tt = arg_type_tuple(F, args, nargs); + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world); + } if (entry && entry->isleafsig && entry->simplesig == (void*)jl_nothing && entry->guardsigs == jl_emptysvec) { // put the entry into the cache if it's valid for a leafsig lookup, // using pick_which to slightly randomize where it ends up call_cache[cache_idx[++pick_which[cache_idx[0]] & 3]] = entry; - goto have_entry; } } @@ -2354,13 +2422,12 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t *F, jl_value_t mfunc = entry->func.linfo; } else { - int64_t last_alloc = jl_options.malloc_log ? jl_gc_diff_total_bytes() : 0; + JL_GC_PUSH1(&tt); + assert(tt); JL_LOCK(&mt->writelock); // cache miss case JL_TIMING(METHOD_LOOKUP_SLOW); - jl_tupletype_t *tt = arg_type_tuple(F, args, nargs); - JL_GC_PUSH1(&tt); - mfunc = jl_mt_assoc_by_type(mt, tt, /*cache*/1, world); + mfunc = jl_mt_assoc_by_type(mt, tt, world); JL_GC_POP(); JL_UNLOCK(&mt->writelock); if (jl_options.malloc_log) @@ -2767,10 +2834,14 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio // // Returns a match as an array of svec(argtypes, static_params, Method). // See below for the meaning of lim. -static jl_value_t *ml_matches(jl_typemap_t *defs, int offs, +static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, jl_tupletype_t *type, int lim, int include_ambiguous, - size_t world, size_t *min_valid, size_t *max_valid) + size_t world, size_t *min_valid, size_t *max_valid, + int cache_result) { + jl_typemap_t *defs = mt->defs; + if (defs == jl_nothing) // special-case: ignore builtin functions + return jl_an_empty_vec_any; jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)type); assert(jl_is_datatype(unw)); size_t l = jl_svec_len(((jl_datatype_t*)unw)->parameters); @@ -2792,10 +2863,54 @@ static jl_value_t *ml_matches(jl_typemap_t *defs, int offs, env.world = world; env.min_valid = *min_valid; env.max_valid = *max_valid; - struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, env.min_valid, env.max_valid}; + struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; JL_GC_PUSH5(&env.t, &env.matc, &env.match.env, &search.env, &env.match.ti); + + if (((jl_datatype_t*)unw)->isdispatchtuple) { + jl_array_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache); + jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)type, world); + if (entry) { + jl_method_instance_t *mi = entry->func.linfo; + jl_method_t *meth = mi->def.method; + if (jl_egal((jl_value_t*)type, mi->specTypes)) { + env.match.env = mi->sparam_vals; + env.match.ti = mi->specTypes; + } + else { + // TODO: should we use jl_subtype_env instead (since we know that `type <: meth->sig` by transitivity) + env.match.ti = jl_type_intersection_env((jl_value_t*)type, (jl_value_t*)meth->sig, &env.match.env); + } + env.matc = jl_svec(3, env.match.ti, env.match.env, meth); + env.t = (jl_value_t*)jl_alloc_vec_any(1); + jl_array_ptr_set(env.t, 0, env.matc); + if (*min_valid < entry->min_world) + *min_valid = entry->min_world; + if (*max_valid > entry->max_world) + *max_valid = entry->max_world; + JL_GC_POP(); + return env.t; + } + } + if (((jl_datatype_t*)unw)->isdispatchtuple) { + jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, &search, jl_cachearg_offset(mt), /*subtype*/1); + if (entry && (((jl_datatype_t*)unw)->isdispatchtuple || entry->guardsigs == jl_emptysvec)) { + jl_method_instance_t *mi = entry->func.linfo; + jl_method_t *meth = mi->def.method; + // TODO: should we use jl_subtype_env instead (since we know that `type <: meth->sig` by transitivity) + env.match.ti = jl_type_intersection_env((jl_value_t*)type, (jl_value_t*)meth->sig, &env.match.env); + env.matc = jl_svec(3, env.match.ti, env.match.env, meth); + env.t = (jl_value_t*)jl_alloc_vec_any(1); + jl_array_ptr_set(env.t, 0, env.matc); + *min_valid = entry->min_world; + *max_valid = entry->max_world; + JL_GC_POP(); + return env.t; + } + } htable_new(&env.visited, 0); if (((jl_datatype_t*)unw)->isdispatchtuple) { + search.min_valid = env.min_valid; + search.max_valid = env.max_valid; jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(defs, &search, offs, /*subtype*/1); env.min_valid = search.min_valid; env.max_valid = search.max_valid; @@ -2810,6 +2925,14 @@ static jl_value_t *ml_matches(jl_typemap_t *defs, int offs, jl_typemap_intersection_visitor(defs, offs, &env.match); } htable_free(&env.visited); + if (cache_result && ((jl_datatype_t*)unw)->isdispatchtuple) { // cache_result parameter keeps this from being recursive + if (env.t != jl_false && jl_array_len(env.t) == 1) { + env.matc = (jl_svec_t*)jl_array_ptr_ref(env.t, 0); + jl_method_t *meth = (jl_method_t*)jl_svecref(env.matc, 2); + jl_svec_t *tpenv = (jl_svec_t*)jl_svecref(env.matc, 1); + cache_method(mt, &mt->cache, (jl_value_t*)mt, type, meth, world, tpenv); + } + } JL_GC_POP(); *min_valid = env.min_valid; *max_valid = env.max_valid; diff --git a/src/init.c b/src/init.c index 415c4b0316e8c..8345d288e31db 100644 --- a/src/init.c +++ b/src/init.c @@ -721,7 +721,6 @@ void _julia_init(JL_IMAGE_SEARCH rel) else { jl_init_types(); jl_init_codegen(); - jl_an_empty_vec_any = (jl_value_t*)jl_alloc_vec_any(0); // used internally } jl_init_tasks(); diff --git a/src/jltypes.c b/src/jltypes.c index bba9362e9a64f..0e9b911f7d22d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -822,17 +822,9 @@ static void cache_insert_type_linear(jl_datatype_t *type, ssize_t insert_at) #ifndef NDEBUG static int is_cacheable(jl_datatype_t *type) { - // only cache types whose behavior will not depend on the identities - // of contained TypeVars - assert(jl_is_datatype(type)); - jl_svec_t *t = type->parameters; - if (jl_svec_len(t) == 0 && jl_emptytuple_type != NULL) // Tuple{} is the only type eligible for this that doesn't have parameters - return 0; - // cache abstract types with no free type vars - if (jl_is_abstracttype(type)) - return !jl_has_free_typevars((jl_value_t*)type); - // ... or concrete types - return jl_is_concrete_type((jl_value_t*)type); + // ensure cache only contains types whose behavior will not depend on the + // identities of contained TypeVars + return !jl_has_free_typevars((jl_value_t*)type); } #endif @@ -1943,18 +1935,19 @@ void jl_init_types(void) JL_GC_DISABLED jl_methtable_type->name->mt = jl_nonfunction_mt; jl_methtable_type->super = jl_any_type; jl_methtable_type->parameters = jl_emptysvec; - jl_methtable_type->name->names = jl_perm_symsvec(11, "name", "defs", - "cache", "max_args", + jl_methtable_type->name->names = jl_perm_symsvec(12, "name", "defs", + "leafcache", "cache", "max_args", "kwsorter", "module", "backedges", "", "", "offs", ""); - jl_methtable_type->types = jl_svec(11, jl_symbol_type, jl_any_type, jl_any_type, jl_any_type/*jl_long*/, + jl_methtable_type->types = jl_svec(12, jl_symbol_type, jl_any_type, jl_any_type, + jl_any_type, jl_any_type/*jl_long*/, jl_any_type, jl_any_type/*module*/, jl_any_type/*any vector*/, jl_any_type/*long*/, jl_any_type/*int32*/, jl_any_type/*uint8*/, jl_any_type/*uint8*/); jl_methtable_type->instance = NULL; jl_methtable_type->abstract = 0; jl_methtable_type->mutabl = 1; - jl_methtable_type->ninitialized = 4; + jl_methtable_type->ninitialized = 5; jl_precompute_memoized_dt(jl_methtable_type, 1); jl_symbol_type->name = jl_new_typename_in(jl_symbol("Symbol"), core); @@ -2152,6 +2145,9 @@ void jl_init_types(void) JL_GC_DISABLED jl_array_symbol_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_symbol_type, jl_box_long(1)); jl_array_uint8_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_uint8_type, jl_box_long(1)); jl_array_int32_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_int32_type, jl_box_long(1)); + jl_an_empty_vec_any = (jl_value_t*)jl_alloc_vec_any(0); // used internally + jl_nonfunction_mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; + jl_type_type_mt->leafcache = (jl_array_t*)jl_an_empty_vec_any; jl_expr_type = jl_new_datatype(jl_symbol("Expr"), core, @@ -2466,18 +2462,18 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_typename_type->types, 1, jl_module_type); jl_svecset(jl_typename_type->types, 6, jl_long_type); jl_svecset(jl_typename_type->types, 3, jl_type_type); - jl_svecset(jl_methtable_type->types, 3, jl_long_type); - jl_svecset(jl_methtable_type->types, 5, jl_module_type); - jl_svecset(jl_methtable_type->types, 6, jl_array_any_type); + jl_svecset(jl_methtable_type->types, 4, jl_long_type); + jl_svecset(jl_methtable_type->types, 6, jl_module_type); + jl_svecset(jl_methtable_type->types, 7, jl_array_any_type); #ifdef __LP64__ - jl_svecset(jl_methtable_type->types, 7, jl_int64_type); // unsigned long - jl_svecset(jl_methtable_type->types, 8, jl_int64_type); // uint32_t plus alignment + jl_svecset(jl_methtable_type->types, 8, jl_int64_type); // unsigned long + jl_svecset(jl_methtable_type->types, 9, jl_int64_type); // uint32_t plus alignment #else - jl_svecset(jl_methtable_type->types, 7, jl_int32_type); // DWORD - jl_svecset(jl_methtable_type->types, 8, jl_int32_type); // uint32_t + jl_svecset(jl_methtable_type->types, 8, jl_int32_type); // DWORD + jl_svecset(jl_methtable_type->types, 9, jl_int32_type); // uint32_t #endif - jl_svecset(jl_methtable_type->types, 9, jl_uint8_type); jl_svecset(jl_methtable_type->types, 10, jl_uint8_type); + jl_svecset(jl_methtable_type->types, 11, jl_uint8_type); jl_svecset(jl_method_type->types, 13, jl_method_instance_type); jl_svecset(jl_method_instance_type->types, 5, jl_code_instance_type); jl_svecset(jl_code_instance_type->types, 8, jl_voidpointer_type); diff --git a/src/julia.h b/src/julia.h index a6b10f84287a3..1d61b2ca961eb 100644 --- a/src/julia.h +++ b/src/julia.h @@ -537,6 +537,7 @@ typedef struct _jl_methtable_t { JL_DATA_TYPE jl_sym_t *name; // sometimes a hack used by serialization to handle kwsorter jl_typemap_t *defs; + jl_array_t *leafcache; jl_typemap_t *cache; intptr_t max_args; // max # of non-vararg arguments in a signature jl_value_t *kwsorter; // keyword argument sorter function diff --git a/src/julia_internal.h b/src/julia_internal.h index 61123c4b4423d..1fee22750c418 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -462,6 +462,8 @@ jl_datatype_t *jl_new_uninitialized_datatype(void); void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable); jl_datatype_t *jl_wrap_Type(jl_value_t *t); // x -> Type{x} jl_value_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n); +jl_datatype_t *jl_lookup_cache_type_(jl_datatype_t *type); +void jl_cache_type_(jl_datatype_t *type); void jl_assign_bits(void *dest, jl_value_t *bits) JL_NOTSAFEPOINT; void set_nth_field(jl_datatype_t *st, void *v, size_t i, jl_value_t *rhs) JL_NOTSAFEPOINT; jl_expr_t *jl_exprn(jl_sym_t *head, size_t n); @@ -482,7 +484,7 @@ int jl_is_toplevel_only_expr(jl_value_t *e) JL_NOTSAFEPOINT; jl_value_t *jl_call_scm_on_ast(const char *funcname, jl_value_t *expr, jl_module_t *inmodule); void jl_linenumber_to_lineinfo(jl_code_info_t *ci, jl_value_t *name); -jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, int cache, size_t world); +jl_method_instance_t *jl_method_lookup(jl_value_t **args, size_t nargs, size_t world); jl_value_t *jl_gf_invoke(jl_value_t *types, jl_value_t *f, jl_value_t **args, size_t nargs); jl_method_instance_t *jl_lookup_generic(jl_value_t **args, uint32_t nargs, uint32_t callsite, size_t world) JL_ALWAYS_LEAFTYPE; JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int include_ambiguous, @@ -1048,13 +1050,12 @@ struct jl_typemap_info { jl_datatype_t **jl_contains; // the type that is being put in this }; -jl_typemap_entry_t *jl_typemap_insert(jl_typemap_t **cache, - jl_value_t *parent JL_PROPAGATES_ROOT, - jl_tupletype_t *type, - jl_tupletype_t *simpletype, jl_svec_t *guardsigs, - jl_value_t *newvalue, int8_t offs, - const struct jl_typemap_info *tparams, - size_t min_world, size_t max_world); +void jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, + jl_typemap_entry_t *newrec, int8_t offs, + const struct jl_typemap_info *tparams); +jl_typemap_entry_t *jl_typemap_alloc( + jl_tupletype_t *type, jl_tupletype_t *simpletype, jl_svec_t *guardsigs, + jl_value_t *newvalue, size_t min_world, size_t max_world); struct jl_typemap_assoc { // inputs diff --git a/src/precompile.c b/src/precompile.c index 13ce3d92d7d1c..4aede27d7c1eb 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -314,12 +314,6 @@ static void jl_compile_all_defs(void) JL_GC_POP(); } -static int precompile_enq_all_cache__(jl_typemap_entry_t *l, void *closure) -{ - jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)l->func.linfo); - return 1; -} - static int precompile_enq_specialization_(jl_method_instance_t *mi, void *closure) { assert(jl_is_method_instance(mi)); @@ -370,7 +364,6 @@ static int precompile_enq_all_specializations__(jl_typemap_entry_t *def, void *c static void precompile_enq_all_specializations_(jl_methtable_t *mt, void *env) { jl_typemap_visitor(mt->defs, precompile_enq_all_specializations__, env); - jl_typemap_visitor(mt->cache, precompile_enq_all_cache__, env); } void jl_compile_now(jl_method_instance_t *mi); diff --git a/src/typemap.c b/src/typemap.c index 849ae6a8e2e02..8b0bf64305fdd 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -872,18 +872,34 @@ static void jl_typemap_level_insert_( jl_typemap_list_insert_(map, &cache->linear, (jl_value_t*)cache, newrec, tparams); } -jl_typemap_entry_t *jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, - jl_tupletype_t *type, - jl_tupletype_t *simpletype, jl_svec_t *guardsigs, - jl_value_t *newvalue, int8_t offs, - const struct jl_typemap_info *tparams, - size_t min_world, size_t max_world) +jl_typemap_entry_t *jl_typemap_alloc( + jl_tupletype_t *type, jl_tupletype_t *simpletype, jl_svec_t *guardsigs, + jl_value_t *newvalue, size_t min_world, size_t max_world) { jl_ptls_t ptls = jl_get_ptls_states(); assert(min_world > 0 && max_world > 0); if (!simpletype) simpletype = (jl_tupletype_t*)jl_nothing; jl_value_t *ttype = jl_unwrap_unionall((jl_value_t*)type); + assert(jl_is_tuple_type(ttype)); + // compute the complexity of this type signature + int isva = jl_is_va_tuple((jl_datatype_t*)ttype); + int issimplesig = !jl_is_unionall(type); // a TypeVar environment needs a complex matching test + int isleafsig = issimplesig && !isva; // entirely leaf types don't need to be sorted + size_t i, l; + for (i = 0, l = jl_nparams(ttype); i < l && issimplesig; i++) { + jl_value_t *decl = jl_tparam(ttype, i); + if (jl_is_kind(decl)) + isleafsig = 0; // Type{} may have a higher priority than a kind + else if (jl_is_type_type(decl)) + isleafsig = 0; // Type{} may need special processing to compute the match + else if (jl_is_vararg_type(decl)) + isleafsig = 0; // makes iteration easier when the endpoints are the same + else if (decl == (jl_value_t*)jl_any_type) + isleafsig = 0; // Any needs to go in the general cache + else if (!jl_is_concrete_type(decl)) // anything else needs to go through the general subtyping test + isleafsig = issimplesig = 0; + } jl_typemap_entry_t *newrec = (jl_typemap_entry_t*)jl_gc_alloc(ptls, sizeof(jl_typemap_entry_t), @@ -895,32 +911,19 @@ jl_typemap_entry_t *jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, newrec->next = (jl_typemap_entry_t*)jl_nothing; newrec->min_world = min_world; newrec->max_world = max_world; - // compute the complexity of this type signature - newrec->va = jl_is_va_tuple((jl_datatype_t*)ttype); - newrec->issimplesig = !jl_is_unionall(type); // a TypeVar environment needs a complex matching test - newrec->isleafsig = newrec->issimplesig && !newrec->va; // entirely leaf types don't need to be sorted - JL_GC_PUSH1(&newrec); - assert(jl_is_tuple_type(ttype)); - size_t i, l; - for (i = 0, l = jl_nparams(ttype); i < l && newrec->issimplesig; i++) { - jl_value_t *decl = jl_tparam(ttype, i); - if (jl_is_kind(decl)) - newrec->isleafsig = 0; // Type{} may have a higher priority than a kind - else if (jl_is_type_type(decl)) - newrec->isleafsig = 0; // Type{} may need special processing to compute the match - else if (jl_is_vararg_type(decl)) - newrec->isleafsig = 0; // makes iteration easier when the endpoints are the same - else if (decl == (jl_value_t*)jl_any_type) - newrec->isleafsig = 0; // Any needs to go in the general cache - else if (!jl_is_concrete_type(decl)) // anything else needs to go through the general subtyping test - newrec->isleafsig = newrec->issimplesig = 0; - } - // TODO: assert that guardsigs == jl_emptysvec && simplesig == jl_nothing if isleafsig and optimize with that knowledge? - jl_typemap_insert_generic(*cache, cache, parent, newrec, offs, tparams); - JL_GC_POP(); + newrec->va = isva; + newrec->issimplesig = issimplesig; + newrec->isleafsig = isleafsig; return newrec; } +void jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, + jl_typemap_entry_t *newrec, int8_t offs, + const struct jl_typemap_info *tparams) +{ + jl_typemap_insert_generic(*cache, cache, parent, newrec, offs, tparams); +} + static void jl_typemap_list_insert_sorted( jl_typemap_t *map, jl_typemap_entry_t **pml, jl_value_t *parent, jl_typemap_entry_t *newrec, const struct jl_typemap_info *tparams) From 6c54523cbcc5d9b9b729e62828778cff1deb01d2 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 5 Jun 2020 01:03:41 -0400 Subject: [PATCH 6/6] avoid recomputing ml-matches worlds during method-caching [NFCI] --- src/gf.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/gf.c b/src/gf.c index 08de860090bed..38a91c281fdbd 100644 --- a/src/gf.c +++ b/src/gf.c @@ -953,7 +953,7 @@ static jl_method_instance_t *cache_method( jl_methtable_t *mt, jl_typemap_t **cache, jl_value_t *parent JL_PROPAGATES_ROOT, jl_tupletype_t *tt, // the original tupletype of the signature jl_method_t *definition, - size_t world, + size_t world, size_t min_valid, size_t max_valid, jl_svec_t *sparams) { // caller must hold the mt->writelock @@ -996,12 +996,12 @@ static jl_method_instance_t *cache_method( jl_tupletype_t *cachett = tt; jl_svec_t* guardsigs = jl_emptysvec; - size_t min_valid = 1; - size_t max_valid = ~(size_t)0; if (!cache_with_orig && mt) { // now examine what will happen if we chose to use this sig in the cache // TODO: should we first check `compilationsig <: definition`? - temp = ml_matches(mt, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid, &max_valid, 0); + size_t min_valid2 = 1; + size_t max_valid2 = ~(size_t)0; + temp = ml_matches(mt, 0, compilationsig, MAX_UNSPECIALIZED_CONFLICTS, 1, world, &min_valid2, &max_valid2, 0); int guards = 0; if (temp == jl_false) { cache_with_orig = 1; @@ -1052,22 +1052,14 @@ static jl_method_instance_t *cache_method( } } } - if (cache_with_orig) { - min_valid = 1; - max_valid = ~(size_t)0; - } - else { + if (!cache_with_orig) { // determined above that there's no ambiguity in also using compilationsig as the cacheablesig + min_valid = min_valid2; + max_valid = max_valid2; cachett = compilationsig; } } - if (cache_with_orig && mt) { - // now examine defs to determine the min/max-valid range for this lookup result - (void)ml_matches(mt, 0, cachett, -1, 0, world, &min_valid, &max_valid, 0); - } - assert(mt == NULL || min_valid > 1); - // now scan `cachett` and ensure that `Type{T}` in the cache will be matched exactly by `typeof(T)` // and also reduce the complexity of rejecting this entry in the cache // by replacing non-simple types with jl_any_type to build a new `type` @@ -1229,7 +1221,7 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype entry = jl_typemap_morespecific_by_type(entry, (jl_value_t*)tt, &search.env, world); if (entry != NULL) { jl_method_t *m = entry->func.method; - nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, search.env); + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, tt, m, world, search.min_valid, search.max_valid, search.env); } } JL_GC_POP(); @@ -2088,7 +2080,13 @@ jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES return NULL; // find if exactly 1 method matches (issue #7302) - jl_value_t *matches = jl_matching_methods(types, 1, 1, world, min_valid, max_valid); + size_t min_valid2 = 1; + size_t max_valid2 = ~(size_t)0; + jl_value_t *matches = jl_matching_methods(types, 1, 1, world, &min_valid2, &max_valid2); + if (*min_valid < min_valid2) + *min_valid = min_valid2; + if (*max_valid > max_valid2) + *max_valid = max_valid2; if (matches == jl_false || jl_array_len(matches) != 1) return NULL; jl_tupletype_t *tt = NULL; @@ -2109,7 +2107,7 @@ jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types JL_PROPAGATES // inject it there now if we think it will be // used via dispatch later (e.g. because it was hinted via a call to `precompile`) JL_LOCK(&mt->writelock); - nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, ti, m, world, env); + nf = cache_method(mt, &mt->cache, (jl_value_t*)mt, ti, m, world, min_valid2, max_valid2, env); JL_UNLOCK(&mt->writelock); } else { @@ -2538,7 +2536,7 @@ static jl_value_t *jl_gf_invoke_by_method(jl_method_t *method, jl_value_t *gf, j if (method->invokes == NULL) method->invokes = jl_nothing; - mfunc = cache_method(NULL, &method->invokes, (jl_value_t*)method, tt, method, 1, tpenv); + mfunc = cache_method(NULL, &method->invokes, (jl_value_t*)method, tt, method, 1, 1, ~(size_t)0, tpenv); JL_UNLOCK(&method->writelock); JL_GC_POP(); if (jl_options.malloc_log) @@ -2588,7 +2586,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_typemap_entry_t *entry, jl_valu method->invokes = jl_nothing; jl_method_instance_t *mfunc = cache_method(NULL, &method->invokes, (jl_value_t*)method, - (jl_tupletype_t*)tt, method, 1, tpenv); + (jl_tupletype_t*)tt, method, 1, 1, ~(size_t)0, tpenv); JL_GC_POP(); JL_UNLOCK(&method->writelock); return (jl_value_t*)mfunc; @@ -2930,7 +2928,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, int offs, env.matc = (jl_svec_t*)jl_array_ptr_ref(env.t, 0); jl_method_t *meth = (jl_method_t*)jl_svecref(env.matc, 2); jl_svec_t *tpenv = (jl_svec_t*)jl_svecref(env.matc, 1); - cache_method(mt, &mt->cache, (jl_value_t*)mt, type, meth, world, tpenv); + cache_method(mt, &mt->cache, (jl_value_t*)mt, type, meth, world, env.min_valid, env.max_valid, tpenv); } } JL_GC_POP();