From b225a5e08c61704c965e3f34b2db027f508c36ed Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 13 Dec 2017 04:56:15 -0600 Subject: [PATCH 1/4] Support method deletion --- base/reflection.jl | 15 ++++++ src/gf.c | 77 +++++++++++++++++++++++++-- test/reflection.jl | 126 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+), 3 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 63165a62226db1..65116b827fa972 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1083,6 +1083,21 @@ function isambiguous(m1::Method, m2::Method; ambiguous_bottom::Bool=false) return true end +""" + delete_method(m::Method) + +Make method `m` uncallable and force recompilation of any methods that use(d) it. +""" +function delete_method(m::Method) + ccall(:jl_method_table_disable, Void, (Any, Any), MethodTable(m), m) +end + +function MethodTable(m::Method) + ft = ccall(:jl_first_argument_datatype, Any, (Any,), m.sig) + ft == C_NULL && error("Method ", m, " does not correspond to a function type") + (ft::DataType).name.mt +end + """ has_bottom_parameter(t) -> Bool diff --git a/src/gf.c b/src/gf.c index 5c1fcfef0525ea..fcab86e85ff9df 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1131,6 +1131,8 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_ closure->after = 1; return 1; } + if (oldentry->max_world < ~(size_t)0) + return 1; union jl_typemap_t map = closure->defs; jl_tupletype_t *type = (jl_tupletype_t*)closure->match.type; jl_method_t *m = closure->newentry->func.method; @@ -1212,7 +1214,7 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_ return 1; } -static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_entry_t *newentry) +static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_entry_t *newentry, jl_typemap_intersection_visitor_fptr fptr) { jl_tupletype_t *type = newentry->sig; jl_tupletype_t *ttypes = (jl_tupletype_t*)jl_unwrap_unionall((jl_value_t*)type); @@ -1226,7 +1228,7 @@ static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_e va = NULL; } struct ambiguous_matches_env env; - env.match.fptr = check_ambiguous_visitor; + env.match.fptr = fptr; env.match.type = (jl_value_t*)type; env.match.va = va; env.match.ti = NULL; @@ -1241,6 +1243,47 @@ static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_e return env.shadowed; } +static int check_disabled_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_intersection_env *closure0) +{ + struct ambiguous_matches_env *closure = container_of(closure0, struct ambiguous_matches_env, match); + if (oldentry == closure->newentry) { + closure->after = 1; + return 1; + } + if (!closure->after || oldentry->max_world < ~(size_t)0) // the second condition prevents us from confusion in multiple cycles of add/delete + return 1; + jl_tupletype_t *sig = oldentry->sig; + jl_value_t *isect = closure->match.ti; + if (closure->shadowed == NULL) + closure->shadowed = (jl_value_t*)jl_alloc_vec_any(0); + + int i, l = jl_array_len(closure->shadowed); + for (i = 0; i < l; i++) { + jl_method_t *mth = (jl_method_t*)jl_array_ptr_ref(closure->shadowed, i); + jl_value_t *isect2 = jl_type_intersection(mth->sig, (jl_value_t*)sig); + // see if the intersection was covered by precisely the disabled method + // that means we now need to record the ambiguity + if (jl_types_equal(isect, isect2)) { + jl_method_t *mambig = mth; + jl_method_t *m = oldentry->func.method; + if (m->ambig == jl_nothing) { + m->ambig = (jl_value_t*) jl_alloc_vec_any(0); + jl_gc_wb(m, m->ambig); + } + if (mambig->ambig == jl_nothing) { + mambig->ambig = (jl_value_t*) jl_alloc_vec_any(0); + jl_gc_wb(mambig, mambig->ambig); + } + jl_array_ptr_1d_push((jl_array_t*) m->ambig, (jl_value_t*) mambig); + jl_array_ptr_1d_push((jl_array_t*) mambig->ambig, (jl_value_t*) m); + } + } + + jl_array_ptr_1d_push((jl_array_t*)closure->shadowed, oldentry->func.value); + return 1; +} + + static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue) { // method overwritten @@ -1405,6 +1448,34 @@ void jl_method_instance_delete(jl_method_instance_t *mi) jl_uv_puts(JL_STDOUT, "<<<\n", 4); } +static int typemap_search(jl_typemap_entry_t *entry, void *closure) +{ + if ((void*)(entry->func.method) == *(jl_method_t**)closure) { + *(jl_typemap_entry_t**)closure = entry; + return 0; + } + return 1; +} + +//JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_typemap_entry_t *methodentry) +JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *method) +{ + struct invalidate_conflicting_env env; + env.invalidated = 0; + jl_typemap_entry_t *methodentry = (jl_typemap_entry_t*)(method); + if (jl_typemap_visitor(mt->defs, typemap_search, &methodentry)) + jl_error("method not in method table"); + JL_LOCK(&mt->writelock); + // Narrow the world age on the method to make it uncallable + methodentry->max_world = jl_world_counter++; + // Recompute ambiguities (deleting a more specific method might reveal ambiguities that it previously resolved) + env.max_world = methodentry->max_world; + check_ambiguous_matches(mt->defs, methodentry, check_disabled_ambiguous_visitor); // TODO: decrease repeated work? + // Invalidate the backedges + jl_typemap_visitor(methodentry->func.method->specializations, (jl_typemap_visitor_fptr)invalidate_backedges, &env); + JL_UNLOCK(&mt->writelock); +} + JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method, jl_tupletype_t *simpletype) { assert(jl_is_method(method)); @@ -1430,7 +1501,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method method_overwrite(newentry, (jl_method_t*)oldvalue); } else { - oldvalue = check_ambiguous_matches(mt->defs, newentry); + oldvalue = check_ambiguous_matches(mt->defs, newentry, check_ambiguous_visitor); if (mt->backedges) { jl_value_t **backedges = (jl_value_t**)jl_array_data(mt->backedges); size_t i, na = jl_array_len(mt->backedges); diff --git a/test/reflection.jl b/test/reflection.jl index 0af98d038865d0..74d1539cd6074c 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -771,3 +771,129 @@ cinfo = cinfos[] test_similar_codeinfo(cinfo, cinfo_generated) @test_throws ErrorException code_lowered(f22979, typeof.(x22979), false) + +module MethodDeletion +using Test + +# Deletion after compiling top-level call +bar1(x) = 1 +bar1(x::Int) = 2 +foo1(x) = bar1(x) +faz1(x) = foo1(x) +@test faz1(1) == 2 +@test faz1(1.0) == 1 +m = first(methods(bar1, Tuple{Int})) +Base.delete_method(m) +@test bar1(1) == 1 +@test bar1(1.0) == 1 +@test foo1(1) == 1 +@test foo1(1.0) == 1 +@test faz1(1) == 1 +@test faz1(1.0) == 1 + +# Deletion after compiling middle-level call +bar2(x) = 1 +bar2(x::Int) = 2 +foo2(x) = bar2(x) +faz2(x) = foo2(x) +@test foo2(1) == 2 +@test foo2(1.0) == 1 +m = first(methods(bar2, Tuple{Int})) +Base.delete_method(m) +@test bar2(1.0) == 1 +@test bar2(1) == 1 +@test foo2(1) == 1 +@test foo2(1.0) == 1 +@test faz2(1) == 1 +@test faz2(1.0) == 1 + +# Deletion after compiling low-level call +bar3(x) = 1 +bar3(x::Int) = 2 +foo3(x) = bar3(x) +faz3(x) = foo3(x) +@test bar3(1) == 2 +@test bar3(1.0) == 1 +m = first(methods(bar3, Tuple{Int})) +Base.delete_method(m) +@test bar3(1) == 1 +@test bar3(1.0) == 1 +@test foo3(1) == 1 +@test foo3(1.0) == 1 +@test faz3(1) == 1 +@test faz3(1.0) == 1 + +# Deletion before any compilation +bar4(x) = 1 +bar4(x::Int) = 2 +foo4(x) = bar4(x) +faz4(x) = foo4(x) +m = first(methods(bar4, Tuple{Int})) +Base.delete_method(m) +@test bar4(1) == 1 +@test bar4(1.0) == 1 +@test foo4(1) == 1 +@test foo4(1.0) == 1 +@test faz4(1) == 1 +@test faz4(1.0) == 1 + +# Methods with keyword arguments +fookw(x; direction=:up) = direction +fookw(y::Int) = 2 +@test fookw("string") == :up +@test fookw(1) == 2 +m = collect(methods(fookw))[2] +Base.delete_method(m) +@test fookw(1) == 2 +@test_throws MethodError fookw("string") + +# functions with many methods +types = (Float64, Int32, String) +for T1 in types, T2 in types, T3 in types + @eval foomany(x::$T1, y::$T2, z::$T3) = y +end +@test foomany(Int32(5), "hello", 3.2) == "hello" +m = first(methods(foomany, Tuple{Int32, String, Float64})) +Base.delete_method(m) +@test_throws MethodError foomany(Int32(5), "hello", 3.2) + +struct EmptyType end +Base.convert(::Type{EmptyType}, x::Integer) = EmptyType() +m = first(methods(convert, Tuple{Type{EmptyType}, Integer})) +Base.delete_method(m) +@test_throws MethodError convert(EmptyType, 1) + +# parametric methods +parametric(A::Array{T,N}, i::Vararg{Int,N}) where {T,N} = N +@test parametric(rand(2,2), 1, 1) == 2 +m = first(methods(parametric)) +Base.delete_method(m) +@test_throws MethodError parametric(rand(2,2), 1, 1) + +# Deletion and ambiguity detection +foo(::Int, ::Int) = 1 +foo(::Real, ::Int) = 2 +foo(::Int, ::Real) = 3 +@test all(map(g->g.ambig==nothing, methods(foo))) +Base.delete_method(first(methods(foo))) +@test !all(map(g->g.ambig==nothing, methods(foo))) +@test_throws MethodError foo(1, 1) +foo(::Int, ::Int) = 1 +foo(1, 1) +@test map(g->g.ambig==nothing, methods(foo)) == [true, false, false] +Base.delete_method(first(methods(foo))) +@test_throws MethodError foo(1, 1) +@test map(g->g.ambig==nothing, methods(foo)) == [false, false] + +# multiple deletions and ambiguities +typeparam(::Type{T}, a::Array{T}) where T<:AbstractFloat = 1 +typeparam(::Type{T}, a::Array{T}) where T = 2 +for mth in collect(methods(typeparam)) + Base.delete_method(mth) +end +typeparam(::Type{T}, a::AbstractArray{T}) where T<:AbstractFloat = 1 +typeparam(::Type{T}, a::AbstractArray{T}) where T = 2 +@test typeparam(Float64, rand(2)) == 1 +@test typeparam(Int, rand(Int, 2)) == 2 + +end From 4a6c4420220a8bef224a0f533e5e7e7e2a626cfa Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 13 Dec 2017 08:43:42 -0600 Subject: [PATCH 2/4] WIP --- src/gf.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gf.c b/src/gf.c index fcab86e85ff9df..dc2d43c1189d6b 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1457,7 +1457,6 @@ static int typemap_search(jl_typemap_entry_t *entry, void *closure) return 1; } -//JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_typemap_entry_t *methodentry) JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *method) { struct invalidate_conflicting_env env; From dd3230e6d6197127ac774519ff73654f52dbc9db Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 14 Dec 2017 07:32:04 -0600 Subject: [PATCH 3/4] tidy --- src/gf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gf.c b/src/gf.c index dc2d43c1189d6b..f7882133e8f0be 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1459,8 +1459,6 @@ static int typemap_search(jl_typemap_entry_t *entry, void *closure) JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *method) { - struct invalidate_conflicting_env env; - env.invalidated = 0; jl_typemap_entry_t *methodentry = (jl_typemap_entry_t*)(method); if (jl_typemap_visitor(mt->defs, typemap_search, &methodentry)) jl_error("method not in method table"); @@ -1468,9 +1466,11 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho // Narrow the world age on the method to make it uncallable methodentry->max_world = jl_world_counter++; // Recompute ambiguities (deleting a more specific method might reveal ambiguities that it previously resolved) - env.max_world = methodentry->max_world; check_ambiguous_matches(mt->defs, methodentry, check_disabled_ambiguous_visitor); // TODO: decrease repeated work? // Invalidate the backedges + struct invalidate_conflicting_env env; + env.invalidated = 0; + env.max_world = methodentry->max_world; jl_typemap_visitor(methodentry->func.method->specializations, (jl_typemap_visitor_fptr)invalidate_backedges, &env); JL_UNLOCK(&mt->writelock); } From 9d65a35a01cc2b1024c912152e27b1af77efb325 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 14 Dec 2017 07:33:14 -0600 Subject: [PATCH 4/4] Add max_world_mask to typemap_assoc/lookup --- src/codegen.cpp | 2 +- src/dump.c | 15 ++++++++++-- src/gf.c | 56 +++++++++++++++++++++++++++----------------- src/julia_internal.h | 3 ++- src/typemap.c | 29 ++++++++++++----------- 5 files changed, 66 insertions(+), 39 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 9a71d00ae99d1e..495a2b839ac90f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4523,7 +4523,7 @@ static Function *jl_cfunction_object(jl_function_t *ff, jl_value_t *declrt, jl_t // check the cache jl_typemap_entry_t *sf = NULL; if (jl_cfunction_list.unknown != jl_nothing) { - sf = jl_typemap_assoc_by_type(jl_cfunction_list, (jl_tupletype_t*)cfunc_sig, NULL, /*subtype*/0, /*offs*/0, /*world*/1); + sf = jl_typemap_assoc_by_type(jl_cfunction_list, (jl_tupletype_t*)cfunc_sig, NULL, /*subtype*/0, /*offs*/0, /*world*/1, /*max_world_mask*/0); if (sf) { jl_value_t *v = sf->func.value; if (v) { diff --git a/src/dump.c b/src/dump.c index 0181d0e83c791b..08ce1faa638f31 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2585,14 +2585,25 @@ static void jl_update_backref_list(jl_value_t *old, jl_value_t *_new, size_t sta static jl_method_t *jl_lookup_method_worldset(jl_methtable_t *mt, jl_datatype_t *sig, arraylist_t *dependent_worlds) { size_t world = jl_world_counter; + jl_typemap_entry_t *entry; jl_method_t *_new; while (1) { - _new = (jl_method_t*)jl_methtable_lookup(mt, sig, world); - assert(_new && jl_is_method(_new)); + entry = jl_typemap_assoc_by_type( + mt->defs, sig, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0); + if (!entry) + break; + _new = (jl_method_t*)entry->func.value; world = lowerbound_dependent_world_set(_new->min_world, dependent_worlds); if (world == _new->min_world) return _new; } + // If we failed to find a method (perhaps due to method deletion), grab anything but invalidate it + entry = jl_typemap_assoc_by_type( + mt->defs, sig, NULL, /*subtype*/0, /*offs*/0, /*world*/0, /*max_world_mask*/(~(size_t)0) >> 1); + assert(entry); + if (jl_world_counter > entry->max_world) + invalidate_backedges_methinst(entry->func.linfo, jl_world_counter); + return (jl_method_t*)entry->func.value; } static jl_method_t *jl_recache_method(jl_method_t *m, size_t start, arraylist_t *dependent_worlds) diff --git a/src/gf.c b/src/gf.c index f7882133e8f0be..950c16664dceda 100644 --- a/src/gf.c +++ b/src/gf.c @@ -146,7 +146,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, assert(world >= m->min_world && "typemap lookup is corrupted"); JL_LOCK(&m->writelock); jl_typemap_entry_t *sf = - jl_typemap_assoc_by_type(m->specializations, (jl_tupletype_t*)type, NULL, /*subtype*/0, /*offs*/0, world); + jl_typemap_assoc_by_type(m->specializations, (jl_tupletype_t*)type, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0); if (sf && jl_is_method_instance(sf->func.value)) { jl_method_instance_t *linfo = (jl_method_instance_t*)sf->func.value; assert(linfo->min_world <= sf->min_world && linfo->max_world >= sf->max_world); @@ -180,7 +180,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, JL_DLLEXPORT jl_value_t *jl_specializations_lookup(jl_method_t *m, jl_tupletype_t *type, size_t world) { jl_typemap_entry_t *sf = jl_typemap_assoc_by_type( - m->specializations, type, NULL, /*subtype*/0, /*offs*/0, world); + m->specializations, type, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0); if (!sf) return jl_nothing; return sf->func.value; @@ -189,7 +189,7 @@ JL_DLLEXPORT jl_value_t *jl_specializations_lookup(jl_method_t *m, jl_tupletype_ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_tupletype_t *type, size_t world) { jl_typemap_entry_t *sf = jl_typemap_assoc_by_type( - mt->defs, type, NULL, /*subtype*/0, /*offs*/0, world); + mt->defs, type, NULL, /*subtype*/0, /*offs*/0, world, /*max_world_mask*/0); if (!sf) return jl_nothing; return sf->func.value; @@ -1070,7 +1070,7 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype jl_method_instance_t *nf = NULL; JL_GC_PUSH4(&env, &entry, &func, &sig); - entry = jl_typemap_assoc_by_type(mt->defs, tt, &env, /*subtype*/1, /*offs*/0, world); + entry = jl_typemap_assoc_by_type(mt->defs, tt, &env, /*subtype*/1, /*offs*/0, world, /*max_world_mask*/0); if (entry != NULL) { jl_method_t *m = entry->func.method; if (!jl_has_call_ambiguities(tt, m)) { @@ -1168,7 +1168,7 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_ // that isect == type or isect == sig and return the original match) jl_typemap_entry_t *l = jl_typemap_assoc_by_type( map, (jl_tupletype_t*)isect, NULL, /*subtype*/0, /*offs*/0, - closure->newentry->min_world); + closure->newentry->min_world, /*max_world_mask*/0); if (l != NULL) // ok, intersection is covered return 1; jl_method_t *mambig = oldentry->func.method; @@ -1348,6 +1348,30 @@ static void invalidate_method_instance(jl_method_instance_t *replaced, size_t ma JL_UNLOCK_NOGC(&replaced->def.method->writelock); } +static void invalidate_backedges_methinst_(struct set_world def) +{ + JL_LOCK_NOGC(&def.replaced->def.method->writelock); + jl_array_t *backedges = def.replaced->backedges; + if (backedges) { + size_t i, l = jl_array_len(backedges); + jl_method_instance_t **replaced = (jl_method_instance_t**)jl_array_data(backedges); + for (i = 0; i < l; i++) { + invalidate_method_instance(replaced[i], def.world, 0); + } + } + def.replaced->backedges = NULL; + JL_UNLOCK_NOGC(&def.replaced->def.method->writelock); + +} + +void invalidate_backedges_methinst(jl_method_instance_t *replaced, size_t max_world) +{ + struct set_world def; + def.replaced = replaced; + def.world = max_world; + invalidate_backedges_methinst_(def); +} + // invalidate cached methods that overlap this definition struct invalidate_conflicting_env { struct typemap_intersection_env match; @@ -1372,18 +1396,8 @@ static int invalidate_backedges(jl_typemap_entry_t *oldentry, struct typemap_int jl_typemap_visitor(gf->name->mt->cache, set_max_world2, (void*)&def); // invalidate backedges - JL_LOCK_NOGC(&def.replaced->def.method->writelock); - jl_array_t *backedges = def.replaced->backedges; - if (backedges) { - size_t i, l = jl_array_len(backedges); - jl_method_instance_t **replaced = (jl_method_instance_t**)jl_array_data(backedges); - for (i = 0; i < l; i++) { - invalidate_method_instance(replaced[i], closure->max_world, 0); - } - } + invalidate_backedges_methinst_(def); closure->invalidated = 1; - def.replaced->backedges = NULL; - JL_UNLOCK_NOGC(&def.replaced->def.method->writelock); } return 1; } @@ -1636,7 +1650,7 @@ jl_tupletype_t *arg_type_tuple(jl_value_t **args, size_t nargs) jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_t *types, int cache, int allow_exec, size_t world) { - jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world); + jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world, /*max_world_mask*/0); if (entry) { jl_method_instance_t *linfo = (jl_method_instance_t*)entry->func.value; assert(linfo->min_world <= entry->min_world && linfo->max_world >= entry->max_world && @@ -1644,7 +1658,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ return linfo; } JL_LOCK(&mt->writelock); - entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world); + entry = jl_typemap_assoc_by_type(mt->cache, types, NULL, /*subtype*/1, jl_cachearg_offset(mt), world, /*max_world_mask*/0); if (entry) { jl_method_instance_t *linfo = (jl_method_instance_t*)entry->func.value; assert(linfo->min_world <= entry->min_world && linfo->max_world >= entry->max_world && @@ -2088,7 +2102,7 @@ JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup(jl_datatype_t *types, size_t world) jl_svec_t *env = jl_emptysvec; JL_GC_PUSH1(&env); jl_typemap_entry_t *entry = jl_typemap_assoc_by_type( - mt->defs, types, /*env*/&env, /*subtype*/1, /*offs*/0, world); + mt->defs, types, /*env*/&env, /*subtype*/1, /*offs*/0, world, /*max_world_mask*/0); JL_GC_POP(); if (!entry) return jl_nothing; @@ -2206,7 +2220,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt, jl_typemap_entry_t *tm = NULL; if (method->invokes.unknown != NULL) { tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, /*subtype*/1, - jl_cachearg_offset(mt), world); + jl_cachearg_offset(mt), world, /*max_world_mask*/0); if (tm) { return (jl_value_t*)tm->func.linfo; } @@ -2215,7 +2229,7 @@ JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt, JL_LOCK(&method->writelock); if (method->invokes.unknown != NULL) { tm = jl_typemap_assoc_by_type(method->invokes, tt, NULL, /*subtype*/1, - jl_cachearg_offset(mt), world); + jl_cachearg_offset(mt), world, /*max_world_mask*/0); if (tm) { jl_method_instance_t *mfunc = tm->func.linfo; JL_UNLOCK(&method->writelock); diff --git a/src/julia_internal.h b/src/julia_internal.h index c3b4b8cc78eb69..0e6d7291caf731 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -644,6 +644,7 @@ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_tupletype_t JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, jl_value_t *type, jl_svec_t *sparams, size_t world); JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_method_instance_t *caller); JL_DLLEXPORT void jl_method_table_add_backedge(jl_methtable_t *mt, jl_value_t *typ, jl_value_t *caller); +void invalidate_backedges_methinst(jl_method_instance_t *replaced, size_t max_world); uint32_t jl_module_next_counter(jl_module_t *m); void jl_fptr_to_llvm(jl_fptr_t fptr, jl_method_instance_t *lam, int specsig); @@ -927,7 +928,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par jl_typemap_entry_t *jl_typemap_assoc_by_type( union jl_typemap_t ml_or_cache, jl_tupletype_t *types, jl_svec_t **penv, - int8_t subtype, int8_t offs, size_t world); + int8_t subtype, int8_t offs, size_t world, size_t max_world_mask); jl_typemap_entry_t *jl_typemap_level_assoc_exact(jl_typemap_level_t *cache, jl_value_t **args, size_t n, int8_t offs, size_t world); jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *mn, jl_value_t **args, size_t n, size_t world); STATIC_INLINE jl_typemap_entry_t *jl_typemap_assoc_exact(union jl_typemap_t ml_or_cache, jl_value_t **args, size_t n, int8_t offs, size_t world) diff --git a/src/typemap.c b/src/typemap.c index b166a849ea51e6..f49a73f0281810 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -562,12 +562,12 @@ int jl_typemap_intersection_visitor(union jl_typemap_t map, int offs, (the function) is always the same for most functions. */ static jl_typemap_entry_t *jl_typemap_assoc_by_type_(jl_typemap_entry_t *ml, jl_tupletype_t *types, - jl_svec_t **penv, size_t world) + jl_svec_t **penv, size_t world, size_t max_world_mask) { size_t n = jl_field_count(types); int typesisva = n == 0 ? 0 : jl_is_vararg_type(jl_tparam(types, n-1)); for (; ml != (void*)jl_nothing; ml = ml->next) { - if (world < ml->min_world || world > ml->max_world) + if (world < ml->min_world || world > (ml->max_world | max_world_mask)) continue; // ignore replaced methods size_t lensig = jl_field_count(jl_unwrap_unionall((jl_value_t*)ml->sig)); if (lensig == n || (ml->va && lensig <= n+1)) { @@ -617,10 +617,11 @@ static jl_typemap_entry_t *jl_typemap_assoc_by_type_(jl_typemap_entry_t *ml, jl_ int jl_obviously_unequal(jl_value_t *a, jl_value_t *b); -static jl_typemap_entry_t *jl_typemap_lookup_by_type_(jl_typemap_entry_t *ml, jl_tupletype_t *types, size_t world) +static jl_typemap_entry_t *jl_typemap_lookup_by_type_(jl_typemap_entry_t *ml, jl_tupletype_t *types, + size_t world, size_t max_world_mask) { for (; ml != (void*)jl_nothing; ml = ml->next) { - if (world < ml->min_world || world > ml->max_world) + if (world < ml->min_world || world > (ml->max_world | max_world_mask)) continue; // TODO: more efficient jl_value_t *a = (jl_value_t*)types; @@ -651,7 +652,7 @@ static jl_typemap_entry_t *jl_typemap_lookup_by_type_(jl_typemap_entry_t *ml, jl // this is the general entry point for looking up a type in the cache // as a subtype, or with type_equal jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_tupletype_t *types, jl_svec_t **penv, - int8_t subtype, int8_t offs, size_t world) + int8_t subtype, int8_t offs, size_t world, size_t max_world_mask) { if (jl_typeof(ml_or_cache.unknown) == (jl_value_t*)jl_typemap_level_type) { jl_typemap_level_t *cache = ml_or_cache.node; @@ -678,7 +679,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ // If there is a type at offs, look in the optimized caches if (!subtype) { if (ty && jl_is_any(ty)) - return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world); + return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world, max_world_mask); if (isva) // in lookup mode, want to match Vararg exactly, not as a subtype ty = NULL; } @@ -689,7 +690,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ union jl_typemap_t ml = mtcache_hash_lookup(&cache->targ, a0, 1, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = - jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world); + jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world, max_world_mask); if (li) return li; } } @@ -699,7 +700,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ union jl_typemap_t ml = mtcache_hash_lookup(&cache->arg1, ty, 0, offs); if (ml.unknown != jl_nothing) { jl_typemap_entry_t *li = - jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world); + jl_typemap_assoc_by_type(ml, types, penv, subtype, offs + 1, world, max_world_mask); if (li) return li; } } @@ -707,18 +708,18 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(union jl_typemap_t ml_or_cache, jl_ } // Always check the list (since offs doesn't always start at 0) if (subtype) { - jl_typemap_entry_t *li = jl_typemap_assoc_by_type_(cache->linear, types, penv, world); + jl_typemap_entry_t *li = jl_typemap_assoc_by_type_(cache->linear, types, penv, world, max_world_mask); if (li) return li; - return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world); + return jl_typemap_assoc_by_type(cache->any, types, penv, subtype, offs + 1, world, max_world_mask); } else { - return jl_typemap_lookup_by_type_(cache->linear, types, world); + return jl_typemap_lookup_by_type_(cache->linear, types, world, max_world_mask); } } else { return subtype ? - jl_typemap_assoc_by_type_(ml_or_cache.leaf, types, penv, world) : - jl_typemap_lookup_by_type_(ml_or_cache.leaf, types, world); + jl_typemap_assoc_by_type_(ml_or_cache.leaf, types, penv, world, max_world_mask) : + jl_typemap_lookup_by_type_(ml_or_cache.leaf, types, world, max_world_mask); } } @@ -977,7 +978,7 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par jl_value_t *ttype = jl_unwrap_unionall((jl_value_t*)type); if ((jl_value_t*)simpletype == jl_nothing) { - jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*cache, type, NULL, 0, offs, min_world); + jl_typemap_entry_t *ml = jl_typemap_assoc_by_type(*cache, type, NULL, 0, offs, min_world, 0); if (ml && ml->simplesig == (void*)jl_nothing) { if (overwritten != NULL) *overwritten = ml->func.value;