Skip to content

Commit d744c8c

Browse files
committed
Support deleting methods during precompilation for stdlib excision (#51641)
The problem with the `delete_method` in `__init__` approach is that we invalidate the method-table, after we have performed all of the caching work. A package dependent on `Random`, will still see the stub method in Base and thus when we delete the stub, we may invalidate useful work. Instead we delete the methods when Random is being loaded, thus a dependent package only ever sees the method table with all the methods in Random, and non of the stubs methods. The only invalidation that thus may happen are calls to `rand` and `randn` without first doing an `import Random`.
1 parent 7463975 commit d744c8c

File tree

12 files changed

+95
-23
lines changed

12 files changed

+95
-23
lines changed

base/compiler/typeinfer.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Tracking of newly-inferred CodeInstances during precompilation
44
const track_newly_inferred = RefValue{Bool}(false)
55
const newly_inferred = CodeInstance[]
6+
const newly_deleted = Method[]
67

78
# build (and start inferring) the inference frame for the top-level MethodInstance
89
function typeinf(interp::AbstractInterpreter, result::InferenceResult, cache_mode::Symbol)

base/loading.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,7 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto
23502350
end
23512351

23522352
ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred)
2353+
ccall(:jl_set_newly_deleted, Cvoid, (Any,), Core.Compiler.newly_deleted)
23532354
Core.Compiler.track_newly_inferred.x = true
23542355
try
23552356
Base.include(Base.__toplevel__, input)

base/stubs.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ function delete_stubs(mod)
3535
if obj isa Function
3636
ms = Base.methods(obj, mod)
3737
for m in ms
38-
Base.delete_method(m)
38+
ccall(:jl_push_newly_deleted, Cvoid, (Any,), m)
39+
ccall(:jl_method_table_disable_incremental, Cvoid, (Any, Any), Base.get_methodtable(m), m)
3940
end
4041
end
4142
end

doc/src/devdocs/locks.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ The following is a leaf lock (level 2), and only acquires level 1 locks (safepoi
4545
> * Module->lock
4646
> * JLDebuginfoPlugin::PluginMutex
4747
> * newly_inferred_mutex
48+
> * newly_deleted_mutex
4849
4950
The following is a level 3 lock, which can only acquire level 1 or level 2 locks internally:
5051

src/clangsa/GCChecker.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1440,7 +1440,8 @@ bool GCChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
14401440
} else if (name == "JL_GC_PUSH1" || name == "JL_GC_PUSH2" ||
14411441
name == "JL_GC_PUSH3" || name == "JL_GC_PUSH4" ||
14421442
name == "JL_GC_PUSH5" || name == "JL_GC_PUSH6" ||
1443-
name == "JL_GC_PUSH7" || name == "JL_GC_PUSH8") {
1443+
name == "JL_GC_PUSH7" || name == "JL_GC_PUSH8" ||
1444+
name == "JL_GC_PUSH9") {
14441445
ProgramStateRef State = C.getState();
14451446
// Transform slots to roots, transform values to rooted
14461447
unsigned NumArgs = CE->getNumArgs();

src/gf.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,9 +1857,9 @@ static jl_typemap_entry_t *do_typemap_search(jl_methtable_t *mt JL_PROPAGATES_RO
18571857
}
18581858
#endif
18591859

1860-
static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *methodentry, size_t max_world)
1860+
static void jl_method_table_invalidate(jl_methtable_t *mt, jl_typemap_entry_t *methodentry, size_t max_world, int tracked)
18611861
{
1862-
if (jl_options.incremental && jl_generating_output())
1862+
if (!tracked && jl_options.incremental && jl_generating_output())
18631863
jl_error("Method deletion is not possible during Module precompile.");
18641864
jl_method_t *method = methodentry->func.method;
18651865
assert(!method->is_for_opaque_closure);
@@ -1916,10 +1916,22 @@ JL_DLLEXPORT void jl_method_table_disable(jl_methtable_t *mt, jl_method_t *metho
19161916
JL_LOCK(&mt->writelock);
19171917
// Narrow the world age on the method to make it uncallable
19181918
size_t world = jl_atomic_fetch_add(&jl_world_counter, 1);
1919-
jl_method_table_invalidate(mt, methodentry, world);
1919+
jl_method_table_invalidate(mt, methodentry, world, 0);
19201920
JL_UNLOCK(&mt->writelock);
19211921
}
19221922

1923+
JL_DLLEXPORT void jl_method_table_disable_incremental(jl_methtable_t *mt, jl_method_t *method)
1924+
{
1925+
jl_typemap_entry_t *methodentry = do_typemap_search(mt, method);
1926+
JL_LOCK(&mt->writelock);
1927+
// Narrow the world age on the method to make it uncallable
1928+
// size_t world = jl_atomic_load_acquire(&jl_world_counter);
1929+
size_t world = jl_atomic_fetch_add(&jl_world_counter, 1);
1930+
jl_method_table_invalidate(mt, methodentry, world, 1);
1931+
JL_UNLOCK(&mt->writelock);
1932+
}
1933+
1934+
19231935
static int jl_type_intersection2(jl_value_t *t1, jl_value_t *t2, jl_value_t **isect JL_REQUIRE_ROOTED_SLOT, jl_value_t **isect2 JL_REQUIRE_ROOTED_SLOT)
19241936
{
19251937
*isect2 = NULL;
@@ -2010,7 +2022,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
20102022
oldvalue = (jl_value_t*)replaced;
20112023
invalidated = 1;
20122024
method_overwrite(newentry, replaced->func.method);
2013-
jl_method_table_invalidate(mt, replaced, max_world);
2025+
jl_method_table_invalidate(mt, replaced, max_world, 0);
20142026
}
20152027
else {
20162028
jl_method_t *const *d;

src/init.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,7 @@ static void jl_set_io_wait(int v)
718718
extern jl_mutex_t jl_modules_mutex;
719719
extern jl_mutex_t precomp_statement_out_lock;
720720
extern jl_mutex_t newly_inferred_mutex;
721+
extern jl_mutex_t newly_deleted_mutex;
721722
extern jl_mutex_t global_roots_lock;
722723
extern jl_mutex_t profile_show_peek_cond_lock;
723724

@@ -736,6 +737,7 @@ static void init_global_mutexes(void) {
736737
JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex");
737738
JL_MUTEX_INIT(&precomp_statement_out_lock, "precomp_statement_out_lock");
738739
JL_MUTEX_INIT(&newly_inferred_mutex, "newly_inferred_mutex");
740+
JL_MUTEX_INIT(&newly_deleted_mutex, "newly_deleted_mutex");
739741
JL_MUTEX_INIT(&global_roots_lock, "global_roots_lock");
740742
JL_MUTEX_INIT(&jl_codegen_lock, "jl_codegen_lock");
741743
JL_MUTEX_INIT(&typecache_lock, "typecache_lock");

src/jl_exported_funcs.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@
310310
XX(jl_method_instance_add_backedge) \
311311
XX(jl_method_table_add_backedge) \
312312
XX(jl_method_table_disable) \
313+
XX(jl_method_table_disable_incremental) \
313314
XX(jl_method_table_for) \
314315
XX(jl_method_table_insert) \
315316
XX(jl_methtable_lookup) \

src/julia.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,7 @@ extern void JL_GC_PUSH4(void *, void *, void *, void *) JL_NOTSAFEPOINT;
948948
extern void JL_GC_PUSH5(void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
949949
extern void JL_GC_PUSH7(void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
950950
extern void JL_GC_PUSH8(void *, void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
951+
extern void JL_GC_PUSH9(void *, void *, void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
951952
extern void _JL_GC_PUSHARGS(jl_value_t **, size_t) JL_NOTSAFEPOINT;
952953
// This is necessary, because otherwise the analyzer considers this undefined
953954
// behavior and terminates the exploration
@@ -990,6 +991,9 @@ extern void JL_GC_POP() JL_NOTSAFEPOINT;
990991
#define JL_GC_PUSH8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
991992
void *__gc_stkf[] = {(void*)JL_GC_ENCODE_PUSH(8), jl_pgcstack, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8}; \
992993
jl_pgcstack = (jl_gcframe_t*)__gc_stkf;
994+
#define JL_GC_PUSH9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
995+
void *__gc_stkf[] = {(void*)JL_GC_ENCODE_PUSH(9), jl_pgcstack, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9}; \
996+
jl_pgcstack = (jl_gcframe_t*)__gc_stkf;
993997

994998

995999
#define JL_GC_PUSHARGS(rts_var,n) \
@@ -2041,6 +2045,9 @@ JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *d
20412045

20422046
JL_DLLEXPORT void jl_set_newly_inferred(jl_value_t *newly_inferred);
20432047
JL_DLLEXPORT void jl_push_newly_inferred(jl_value_t *ci);
2048+
JL_DLLEXPORT void jl_method_table_disable_incremental(jl_methtable_t *mt, jl_method_t *m);
2049+
JL_DLLEXPORT void jl_set_newly_deleted(jl_value_t *newly_deleted);
2050+
JL_DLLEXPORT void jl_push_newly_deleted(jl_value_t *m);
20442051
JL_DLLEXPORT void jl_write_compiler_output(void);
20452052

20462053
// parsing

src/staticdata.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2523,7 +2523,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new
25232523
static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
25242524
jl_array_t *worklist, jl_array_t *extext_methods,
25252525
jl_array_t *new_specializations, jl_array_t *method_roots_list,
2526-
jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED
2526+
jl_array_t *ext_targets, jl_array_t *edges, jl_array_t *newly_deleted) JL_GC_DISABLED
25272527
{
25282528
htable_new(&field_replace, 0);
25292529
// strip metadata and IR when requested
@@ -2644,6 +2644,10 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
26442644
jl_queue_for_serialization(&s, ext_targets);
26452645
jl_queue_for_serialization(&s, edges);
26462646
}
2647+
if (newly_deleted) {
2648+
jl_queue_for_serialization(&s, newly_deleted);
2649+
}
2650+
26472651
jl_serialize_reachable(&s);
26482652
// step 1.2: ensure all gvars are part of the sysimage too
26492653
record_gvars(&s, &gvars);
@@ -2793,6 +2797,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
27932797
jl_write_value(&s, method_roots_list);
27942798
jl_write_value(&s, ext_targets);
27952799
jl_write_value(&s, edges);
2800+
jl_write_value(&s, newly_deleted);
27962801
}
27972802
write_uint32(f, jl_array_len(s.link_ids_gctags));
27982803
ios_write(f, (char*)jl_array_data(s.link_ids_gctags, uint32_t), jl_array_len(s.link_ids_gctags) * sizeof(uint32_t));
@@ -2874,11 +2879,11 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
28742879
}
28752880

28762881
jl_array_t *mod_array = NULL, *extext_methods = NULL, *new_specializations = NULL;
2877-
jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
2882+
jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL, *_newly_deleted = NULL;
28782883
int64_t checksumpos = 0;
28792884
int64_t checksumpos_ff = 0;
28802885
int64_t datastartpos = 0;
2881-
JL_GC_PUSH6(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
2886+
JL_GC_PUSH7(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &_newly_deleted);
28822887

28832888
if (worklist) {
28842889
mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
@@ -2925,7 +2930,10 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
29252930
}
29262931
if (_native_data != NULL)
29272932
native_functions = *_native_data;
2928-
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
2933+
// Otherwise serialization will be confused.
2934+
if (newly_deleted)
2935+
_newly_deleted = jl_array_copy(newly_deleted);
2936+
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges, _newly_deleted);
29292937
if (_native_data != NULL)
29302938
native_functions = NULL;
29312939
// make sure we don't run any Julia code concurrently before this point
@@ -3009,6 +3017,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
30093017
jl_array_t **extext_methods,
30103018
jl_array_t **new_specializations, jl_array_t **method_roots_list,
30113019
jl_array_t **ext_targets, jl_array_t **edges,
3020+
jl_array_t **newly_deleted,
30123021
char **base, arraylist_t *ccallable_list, pkgcachesizes *cachesizes) JL_GC_DISABLED
30133022
{
30143023
int en = jl_gc_enable(0);
@@ -3070,7 +3079,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
30703079
assert(!ios_eof(f));
30713080
s.s = f;
30723081
uintptr_t offset_restored = 0, offset_init_order = 0, offset_extext_methods = 0, offset_new_specializations = 0, offset_method_roots_list = 0;
3073-
uintptr_t offset_ext_targets = 0, offset_edges = 0;
3082+
uintptr_t offset_ext_targets = 0, offset_edges = 0, offset_newly_deleted = 0;
30743083
if (!s.incremental) {
30753084
size_t i;
30763085
for (i = 0; tags[i] != NULL; i++) {
@@ -3104,6 +3113,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
31043113
offset_method_roots_list = jl_read_offset(&s);
31053114
offset_ext_targets = jl_read_offset(&s);
31063115
offset_edges = jl_read_offset(&s);
3116+
offset_newly_deleted = jl_read_offset(&s);
31073117
}
31083118
s.buildid_depmods_idxs = depmod_to_imageidx(depmods);
31093119
size_t nlinks_gctags = read_uint32(f);
@@ -3137,6 +3147,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
31373147
*method_roots_list = (jl_array_t*)jl_delayed_reloc(&s, offset_method_roots_list);
31383148
*ext_targets = (jl_array_t*)jl_delayed_reloc(&s, offset_ext_targets);
31393149
*edges = (jl_array_t*)jl_delayed_reloc(&s, offset_edges);
3150+
*newly_deleted = (jl_array_t*)jl_delayed_reloc(&s, offset_newly_deleted);
31403151
if (!*new_specializations)
31413152
*new_specializations = jl_alloc_vec_any(0);
31423153
}
@@ -3326,6 +3337,11 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
33263337
assert(jl_is_datatype(obj));
33273338
jl_cache_type_((jl_datatype_t*)obj);
33283339
}
3340+
3341+
// Delete methods before inserting new ones.
3342+
if (newly_deleted)
3343+
jl_delete_methods(*newly_deleted);
3344+
33293345
// Perform fixups: things like updating world ages, inserting methods & specializations, etc.
33303346
size_t world = jl_atomic_load_acquire(&jl_world_counter);
33313347
for (size_t i = 0; i < s.uniquing_objs.len; i++) {
@@ -3549,11 +3565,11 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
35493565
assert(datastartpos > 0 && datastartpos < dataendpos);
35503566
needs_permalloc = jl_options.permalloc_pkgimg || needs_permalloc;
35513567
jl_value_t *restored = NULL;
3552-
jl_array_t *init_order = NULL, *extext_methods = NULL, *new_specializations = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
3568+
jl_array_t *init_order = NULL, *extext_methods = NULL, *new_specializations = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL, *newly_deleted = NULL;
35533569
jl_svec_t *cachesizes_sv = NULL;
35543570
char *base;
35553571
arraylist_t ccallable_list;
3556-
JL_GC_PUSH8(&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &cachesizes_sv);
3572+
JL_GC_PUSH9(&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &newly_deleted, &cachesizes_sv);
35573573

35583574
{ // make a permanent in-memory copy of f (excluding the header)
35593575
ios_bufmode(f, bm_none);
@@ -3577,11 +3593,12 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
35773593
ios_close(f);
35783594
ios_static_buffer(f, sysimg, len);
35793595
pkgcachesizes cachesizes;
3580-
jl_restore_system_image_from_stream_(f, image, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &base, &ccallable_list, &cachesizes);
3596+
jl_restore_system_image_from_stream_(f, image, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &newly_deleted, &base, &ccallable_list, &cachesizes);
35813597
JL_SIGATOMIC_END();
35823598

35833599
// Insert method extensions
35843600
jl_insert_methods(extext_methods);
3601+
35853602
// No special processing of `new_specializations` is required because recaching handled it
35863603
// Add roots to methods
35873604
jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored));
@@ -3617,7 +3634,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
36173634
static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uint32_t checksum)
36183635
{
36193636
JL_TIMING(LOAD_IMAGE, LOAD_Sysimg);
3620-
jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
3637+
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);
36213638
}
36223639

36233640
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)

0 commit comments

Comments
 (0)