From bd1cfb7919199a6bdabfd60a4f08e9c91def0ecf Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 16 Apr 2022 09:28:32 -0500 Subject: [PATCH] Avoid unnecessary invalidations for `invoke`d fallbacks While investigating a report in https://discourse.julialang.org/t/package-load-time-regressions-in-v1-8-beta3/78875 it became clear that some precompilation statements were not effective due to invalidation. It turns out that these have the pattern f(arg::SpecificType) = invoke(f, Tuple{GenericType}, arg) When `f` gets compiled for a new subtype of `SpecificType`, the external cached MethodInstance gets invalidated because of the reliance on a less specific method. This is a hack that works around this issue for `copyto!`, but being able to mark calls as having been issued from an `invoke` would be a much better solution. --- src/dump.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/dump.c b/src/dump.c index 63c504d5813c7..5306b95ca4c87 100644 --- a/src/dump.c +++ b/src/dump.c @@ -2231,7 +2231,21 @@ static void jl_insert_method_instances(jl_array_t *list) jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(mt->defs, &search, /*offs*/0, /*subtype*/1); if (entry) { jl_value_t *mworld = entry->func.value; - if (jl_is_method(mworld) && mi->def.method != (jl_method_t*)mworld && jl_type_morespecific(((jl_method_t*)mworld)->sig, mi->def.method->sig)) { + // If a newer method would now be selected by dispatch, we need to + // invalidate. + // + // Note: the check of the module primary_world is a hack to prevent + // invoke(f, Tuple{AbstractType, ...}, args...) + // calls from a more specific method of `f` being spuriously invalidated. + // (An example is `copyto!(::BitArray, ::Broadcasted)` which calls the + // fallback `copyto!(::AbstractArray, ::Broadcasted)` for small outputs.) + // But this only works if the caller is from the same module as the + // fallback method. To do better we need the ability to mark specific + // MethodInstances as having been called from `invoke`. + if (jl_is_method(mworld) && + mi->def.method != (jl_method_t*)mworld && + mi->def.method->module->primary_world < ((jl_method_t*)mworld)->module->primary_world && + jl_type_morespecific(((jl_method_t*)mworld)->sig, mi->def.method->sig)) { jl_array_uint8_set(valids, i, 0); invalidate_backedges(&remove_code_instance_from_validation, mi, world, "jl_insert_method_instance"); // The codeinst of this mi haven't yet been removed