Skip to content

Commit

Permalink
Use original computed edges during serialization instead of trying to…
Browse files Browse the repository at this point in the history
… guess them
  • Loading branch information
vtjnash committed Sep 17, 2024
1 parent d0f9e68 commit 4525784
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 439 deletions.
3 changes: 0 additions & 3 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
update_valid_age!(sv, valid_worlds)
napplicable = length(applicable)
rettype = exctype = Bottom
edges = MethodInstance[]
conditionals = nothing # keeps refinement information of call argument types when the return type is boolean
seen = 0 # number of signatures actually inferred
const_results = nothing # or const_results::Vector{Union{Nothing,ConstResult}} if any const results are available
Expand Down Expand Up @@ -110,7 +109,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
end
const_results[i] = const_result
end
edge === nothing || push!(edges, edge)
this_rt = this_rt ₚ rt
this_exct = this_exct ₚ exct
if bail_out_call(interp, this_rt, sv)
Expand Down Expand Up @@ -167,7 +165,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
end
const_results[i] = const_result
end
edge === nothing || push!(edges, edge)
end
@assert !(this_conditional isa Conditional || this_rt isa MustAlias) "invalid lattice element returned from inter-procedural context"
seen += 1
Expand Down
42 changes: 28 additions & 14 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -649,8 +649,8 @@ end

add_edges!(edges::Vector{Any}, info::MethodResultPure) = add_edges!(edges, info.info)
add_edges!(edges::Vector{Any}, info::ConstCallInfo) = add_edges!(edges, info.call)
add_edges!(edges::Vector{Any}, info::OpaqueClosureCreateInfo) = nothing # merely creating the object does not imply edges
add_edges!(edges::Vector{Any}, info::OpaqueClosureCallInfo) = nothing # TODO: inference appears to have always mis-accounted for these backedges
add_edges!(edges::Vector{Any}, info::OpaqueClosureCreateInfo) = add_edges!(edges, info.unspec.info) # merely creating the object implies edges for OC, unlike normal objects, since calling them doesn't normally have edges in contrast
add_edges!(edges::Vector{Any}, info::OpaqueClosureCallInfo) = add_one_edge!(edges, specialize_method(info.match))
add_edges!(edges::Vector{Any}, info::ReturnTypeCallInfo) = add_edges!(edges, info.info)
function add_edges!(edges::Vector{Any}, info::ApplyCallInfo)
add_edges!(edges, info.call)
Expand All @@ -668,15 +668,6 @@ add_edges!(edges::Vector{Any}, info::FinalizerInfo) = nothing # merely allocatin
add_edges!(edges::Vector{Any}, info::NoCallInfo) = nothing
function add_edges!(edges::Vector{Any}, info::MethodMatchInfo)
matches = info.results.matches
if length(matches) != 1
# TODO: add check for whether this info already exists in the edges
push!(edges, length(matches))
push!(edges, info.atype)
end
for m in matches
mi = specialize_method(m)
length(matches) == 1 ? add_one_edge!(edges, mi) : push!(edges, mi)
end
if isempty(matches) || !(matches[end]::Core.MethodMatch).fully_covers
# add legacy-style missing backedge info also
exists = false
Expand All @@ -691,6 +682,29 @@ function add_edges!(edges::Vector{Any}, info::MethodMatchInfo)
push!(edges, info.atype)
end
end
if length(matches) == 1
# try the optimized format for the representation, if possible and applicable
# if this doesn't succeed, the backedge will be less precise,
# but the forward edge will maintain the precision
m = matches[1]::Core.MethodMatch
mi = specialize_method(m)
if mi.specTypes === m.spec_types
add_one_edge!(edges, mi)
return
end
end
# add check for whether this lookup already existed in the edges list
for i in 1:length(edges)
if edges[i] === length(matches) && edges[i + 1] == info.atype
return
end
end
push!(edges, length(matches))
push!(edges, info.atype)
for m in matches
mi = specialize_method(m::Core.MethodMatch)
push!(edges, mi)
end
nothing
end
add_edges!(edges::Vector{Any}, info::InvokeCallInfo) = add_invoke_edge!(edges, info.atype, specialize_method(info.match))
Expand All @@ -716,14 +730,14 @@ function add_one_edge!(edges::Vector{Any}, mi::MethodInstance)
nothing
end


function compute_edges!(sv::InferenceState)
edges = sv.edges
for i in 1:length(sv.stmt_info)
info = sv.stmt_info[i]
#rt = sv.ssavaluetypes[i]
#effects = EFFECTS_TOTAL # sv.stmt_effects[i]
#if rt === Any && effects === Effects()
#et = sv.exectiontypes[i]
#effects = EFFECTS_TOTAL # TODO: sv.stmt_effects[i]
#if rt === Any && et === Any && effects === Effects()
# continue
#end
add_edges!(edges, info)
Expand Down
5 changes: 4 additions & 1 deletion src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1821,9 +1821,12 @@ static void invalidate_backedges(jl_method_instance_t *replaced_mi, size_t max_w
// add a backedge from callee to caller
JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_value_t *invokesig, jl_method_instance_t *caller)
{
JL_LOCK(&callee->def.method->writelock);
if (invokesig == jl_nothing)
invokesig = NULL; // julia uses `nothing` but C uses NULL (#undef)
assert(jl_is_method_instance(callee));
assert(jl_is_method_instance(caller));
assert(invokesig == NULL || jl_is_type(invokesig));
JL_LOCK(&callee->def.method->writelock);
int found = 0;
// TODO: use jl_cache_type_(invokesig) like cache_method does to save memory
if (!callee->backedges) {
Expand Down
68 changes: 28 additions & 40 deletions src/staticdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -2617,25 +2617,21 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val, int insert)
}

static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, uint64_t worklist_key,
/* outputs */ jl_array_t **extext_methods, jl_array_t **new_ext_cis,
jl_array_t **method_roots_list, jl_array_t **ext_targets, jl_array_t **edges)
/* outputs */ jl_array_t **extext_methods JL_REQUIRE_ROOTED_SLOT,
jl_array_t **new_ext_cis JL_REQUIRE_ROOTED_SLOT,
jl_array_t **method_roots_list JL_REQUIRE_ROOTED_SLOT,
jl_array_t **edges JL_REQUIRE_ROOTED_SLOT)
{
// extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist
// ext_targets: [invokesig1, callee1, matches1, ...] non-worklist callees of worklist-owned methods
// ordinary dispatch: invokesig=NULL, callee is MethodInstance
// `invoke` dispatch: invokesig is signature, callee is MethodInstance
// abstract call: callee is signature
// edges: [caller1, ext_targets_indexes1, ...] for worklist-owned methods calling external methods
assert(edges_map == NULL);
// edges: [caller1, ext_targets, ...] for worklist-owned methods calling external methods

// Save the inferred code from newly inferred, external methods
*new_ext_cis = queue_external_cis(newly_inferred);

// Collect method extensions and edges data
JL_GC_PUSH1(&edges_map);
if (edges)
edges_map = jl_alloc_memory_any(0);
*extext_methods = jl_alloc_vec_any(0);
internal_methods = jl_alloc_vec_any(0);
JL_GC_PUSH1(&internal_methods);
jl_collect_methtable_from_mod(jl_type_type_mt, *extext_methods);
jl_collect_methtable_from_mod(jl_nonfunction_mt, *extext_methods);
size_t i, len = jl_array_len(mod_array);
Expand All @@ -2648,18 +2644,15 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new

if (edges) {
size_t world = jl_atomic_load_acquire(&jl_world_counter);
jl_collect_missing_backedges(jl_type_type_mt);
jl_collect_missing_backedges(jl_nonfunction_mt);
// jl_collect_extext_methods_from_mod and jl_collect_missing_backedges also accumulate data in callers_with_edges.
// Process this to extract `edges` and `ext_targets`.
*ext_targets = jl_alloc_vec_any(0);
*edges = jl_alloc_vec_any(0);
// jl_collect_extext_methods_from_mod accumulate data in callers_with_edges.
// Process this to extract `new_ext_cis` and `edges`
*method_roots_list = jl_alloc_vec_any(0);
// Collect the new method roots for external specializations
jl_collect_new_roots(&relocatable_ext_cis, *method_roots_list, *new_ext_cis, worklist_key);
jl_collect_edges(*edges, *ext_targets, *new_ext_cis, world);
*edges = jl_alloc_vec_any(0);
jl_collect_edges(*edges, *new_ext_cis, world);
}
assert(edges_map == NULL); // jl_collect_edges clears this when done
internal_methods = NULL;

JL_GC_POP();
}
Expand All @@ -2668,7 +2661,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new
static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
jl_array_t *worklist, jl_array_t *extext_methods,
jl_array_t *new_ext_cis, jl_array_t *method_roots_list,
jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED
jl_array_t *edges) JL_GC_DISABLED
{
htable_new(&field_replace, 0);
// strip metadata and IR when requested
Expand Down Expand Up @@ -2771,7 +2764,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
}
// step 1.1: as needed, serialize the data needed for insertion into the running system
if (extext_methods) {
assert(ext_targets);
assert(edges);
// Queue method extensions
jl_queue_for_serialization(&s, extext_methods);
Expand All @@ -2780,7 +2772,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
// Queue the new roots
jl_queue_for_serialization(&s, method_roots_list);
// Queue the edges
jl_queue_for_serialization(&s, ext_targets);
jl_queue_for_serialization(&s, edges);
}
jl_serialize_reachable(&s);
Expand Down Expand Up @@ -2934,7 +2925,6 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
jl_write_value(&s, extext_methods);
jl_write_value(&s, new_ext_cis);
jl_write_value(&s, method_roots_list);
jl_write_value(&s, ext_targets);
jl_write_value(&s, edges);
}
write_uint32(f, jl_array_len(s.link_ids_gctags));
Expand Down Expand Up @@ -3019,18 +3009,18 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
}

jl_array_t *mod_array = NULL, *extext_methods = NULL, *new_ext_cis = NULL;
jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
jl_array_t *method_roots_list = NULL, *edges = NULL;
int64_t checksumpos = 0;
int64_t checksumpos_ff = 0;
int64_t datastartpos = 0;
JL_GC_PUSH6(&mod_array, &extext_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges);
JL_GC_PUSH5(&mod_array, &extext_methods, &new_ext_cis, &method_roots_list, &edges);

if (worklist) {
mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
// Generate _native_data`
if (_native_data != NULL) {
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist),
&extext_methods, &new_ext_cis, NULL, NULL, NULL);
&extext_methods, &new_ext_cis, NULL, NULL);
jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1);
*_native_data = jl_precompile_worklist(worklist, extext_methods, new_ext_cis);
jl_precompile_toplevel_module = NULL;
Expand Down Expand Up @@ -3059,7 +3049,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
if (worklist) {
htable_new(&relocatable_ext_cis, 0);
jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist),
&extext_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges);
&extext_methods, &new_ext_cis, &method_roots_list, &edges);
if (!emit_split) {
write_int32(f, 0); // No clone_targets
write_padding(f, LLT_ALIGN(ios_pos(f), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(f));
Expand All @@ -3071,7 +3061,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
}
if (_native_data != NULL)
native_functions = *_native_data;
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, method_roots_list, ext_targets, edges);
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, method_roots_list, edges);
if (_native_data != NULL)
native_functions = NULL;
if (worklist)
Expand Down Expand Up @@ -3161,7 +3151,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
/* outputs */ jl_array_t **restored, jl_array_t **init_order,
jl_array_t **extext_methods, jl_array_t **internal_methods,
jl_array_t **new_ext_cis, jl_array_t **method_roots_list,
jl_array_t **ext_targets, jl_array_t **edges,
jl_array_t **edges,
char **base, arraylist_t *ccallable_list, pkgcachesizes *cachesizes) JL_GC_DISABLED
{
jl_task_t *ct = jl_current_task;
Expand Down Expand Up @@ -3232,7 +3222,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
assert(!ios_eof(f));
s.s = f;
uintptr_t offset_restored = 0, offset_init_order = 0, offset_extext_methods = 0, offset_new_ext_cis = 0, offset_method_roots_list = 0;
uintptr_t offset_ext_targets = 0, offset_edges = 0;
uintptr_t offset_edges = 0;
if (!s.incremental) {
size_t i;
for (i = 0; tags[i] != NULL; i++) {
Expand Down Expand Up @@ -3265,7 +3255,6 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
offset_extext_methods = jl_read_offset(&s);
offset_new_ext_cis = jl_read_offset(&s);
offset_method_roots_list = jl_read_offset(&s);
offset_ext_targets = jl_read_offset(&s);
offset_edges = jl_read_offset(&s);
}
s.buildid_depmods_idxs = depmod_to_imageidx(depmods);
Expand All @@ -3292,13 +3281,12 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
uint32_t external_fns_begin = read_uint32(f);
jl_read_arraylist(s.s, ccallable_list ? ccallable_list : &s.ccallable_list);
if (s.incremental) {
assert(restored && init_order && extext_methods && internal_methods && new_ext_cis && method_roots_list && ext_targets && edges);
assert(restored && init_order && extext_methods && internal_methods && new_ext_cis && method_roots_list && edges);
*restored = (jl_array_t*)jl_delayed_reloc(&s, offset_restored);
*init_order = (jl_array_t*)jl_delayed_reloc(&s, offset_init_order);
*extext_methods = (jl_array_t*)jl_delayed_reloc(&s, offset_extext_methods);
*new_ext_cis = (jl_array_t*)jl_delayed_reloc(&s, offset_new_ext_cis);
*method_roots_list = (jl_array_t*)jl_delayed_reloc(&s, offset_method_roots_list);
*ext_targets = (jl_array_t*)jl_delayed_reloc(&s, offset_ext_targets);
*edges = (jl_array_t*)jl_delayed_reloc(&s, offset_edges);
*internal_methods = jl_alloc_vec_any(0);
}
Expand Down Expand Up @@ -3736,9 +3724,9 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
arraylist_t ccallable_list;

jl_value_t *restored = NULL;
jl_array_t *init_order = NULL, *extext_methods = NULL, *internal_methods = NULL, *new_ext_cis = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
jl_array_t *init_order = NULL, *extext_methods = NULL, *internal_methods = NULL, *new_ext_cis = NULL, *method_roots_list = NULL, *edges = NULL;
jl_svec_t *cachesizes_sv = NULL;
JL_GC_PUSH9(&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges, &cachesizes_sv);
JL_GC_PUSH8(&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list, &edges, &cachesizes_sv);

{ // make a permanent in-memory copy of f (excluding the header)
ios_bufmode(f, bm_none);
Expand All @@ -3763,7 +3751,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
ios_static_buffer(f, sysimg, len);
pkgcachesizes cachesizes;
jl_restore_system_image_from_stream_(f, image, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list,
&ext_targets, &edges, &base, &ccallable_list, &cachesizes);
&edges, &base, &ccallable_list, &cachesizes);
JL_SIGATOMIC_END();

// No special processing of `new_ext_cis` is required because recaching handled it
Expand All @@ -3777,7 +3765,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
// allow users to start running in this updated world
jl_atomic_store_release(&jl_world_counter, world);
// but one of those immediate users is going to be our cache updates
jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_ext_cis, world); // restore external backedges (needs to be last)
jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)new_ext_cis, world); // restore external backedges (needs to be last)
// now permit more methods to be added again
JL_UNLOCK(&world_counter_lock);
// reinit ccallables
Expand All @@ -3793,9 +3781,9 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
jl_svecset(cachesizes_sv, 4, jl_box_long(cachesizes.reloclist));
jl_svecset(cachesizes_sv, 5, jl_box_long(cachesizes.gvarlist));
jl_svecset(cachesizes_sv, 6, jl_box_long(cachesizes.fptrlist));
restored = (jl_value_t*)jl_svec(8, restored, init_order, extext_methods,
restored = (jl_value_t*)jl_svec(7, restored, init_order, extext_methods,
new_ext_cis ? (jl_value_t*)new_ext_cis : jl_nothing,
method_roots_list, ext_targets, edges, cachesizes_sv);
method_roots_list, edges, cachesizes_sv);
}
else {
restored = (jl_value_t*)jl_svec(2, restored, init_order);
Expand All @@ -3810,7 +3798,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uint32_t checksum)
{
JL_TIMING(LOAD_IMAGE, LOAD_Sysimg);
jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
}

JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(void* pkgimage_handle, const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname, int needs_permalloc)
Expand Down
Loading

0 comments on commit 4525784

Please sign in to comment.