Skip to content

Commit 31ccfb6

Browse files
authored
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 1b4a194 commit 31ccfb6

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::Symbol)

base/loading.jl

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

22992299
ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred)
2300+
ccall(:jl_set_newly_deleted, Cvoid, (Any,), Core.Compiler.newly_deleted)
23002301
Core.Compiler.track_newly_inferred.x = true
23012302
try
23022303
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
@@ -44,6 +44,7 @@ The following is a leaf lock (level 2), and only acquires level 1 locks (safepoi
4444
> * Module->lock
4545
> * JLDebuginfoPlugin::PluginMutex
4646
> * newly_inferred_mutex
47+
> * newly_deleted_mutex
4748
4849
The following is a level 3 lock, which can only acquire level 1 or level 2 locks internally:
4950

src/clangsa/GCChecker.cpp

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

src/gf.c

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

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

1924+
JL_DLLEXPORT void jl_method_table_disable_incremental(jl_methtable_t *mt, jl_method_t *method)
1925+
{
1926+
jl_typemap_entry_t *methodentry = do_typemap_search(mt, method);
1927+
JL_LOCK(&mt->writelock);
1928+
// Narrow the world age on the method to make it uncallable
1929+
// size_t world = jl_atomic_load_acquire(&jl_world_counter);
1930+
size_t world = jl_atomic_fetch_add(&jl_world_counter, 1);
1931+
jl_method_table_invalidate(mt, methodentry, world, 1);
1932+
JL_UNLOCK(&mt->writelock);
1933+
}
1934+
1935+
19241936
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)
19251937
{
19261938
*isect2 = NULL;
@@ -2011,7 +2023,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
20112023
oldvalue = (jl_value_t*)replaced;
20122024
invalidated = 1;
20132025
method_overwrite(newentry, replaced->func.method);
2014-
jl_method_table_invalidate(mt, replaced, max_world);
2026+
jl_method_table_invalidate(mt, replaced, max_world, 0);
20152027
}
20162028
else {
20172029
jl_method_t *const *d;

src/init.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ static void jl_set_io_wait(int v)
709709
extern jl_mutex_t jl_modules_mutex;
710710
extern jl_mutex_t precomp_statement_out_lock;
711711
extern jl_mutex_t newly_inferred_mutex;
712+
extern jl_mutex_t newly_deleted_mutex;
712713
extern jl_mutex_t global_roots_lock;
713714

714715
static void restore_fp_env(void)
@@ -726,6 +727,7 @@ static void init_global_mutexes(void) {
726727
JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex");
727728
JL_MUTEX_INIT(&precomp_statement_out_lock, "precomp_statement_out_lock");
728729
JL_MUTEX_INIT(&newly_inferred_mutex, "newly_inferred_mutex");
730+
JL_MUTEX_INIT(&newly_deleted_mutex, "newly_deleted_mutex");
729731
JL_MUTEX_INIT(&global_roots_lock, "global_roots_lock");
730732
JL_MUTEX_INIT(&jl_codegen_lock, "jl_codegen_lock");
731733
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
@@ -323,6 +323,7 @@
323323
XX(jl_method_instance_add_backedge) \
324324
XX(jl_method_table_add_backedge) \
325325
XX(jl_method_table_disable) \
326+
XX(jl_method_table_disable_incremental) \
326327
XX(jl_method_table_for) \
327328
XX(jl_method_table_insert) \
328329
XX(jl_methtable_lookup) \

src/julia.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,7 @@ extern void JL_GC_PUSH4(void *, void *, void *, void *) JL_NOTSAFEPOINT;
932932
extern void JL_GC_PUSH5(void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
933933
extern void JL_GC_PUSH7(void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
934934
extern void JL_GC_PUSH8(void *, void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
935+
extern void JL_GC_PUSH9(void *, void *, void *, void *, void *, void *, void *, void *, void *) JL_NOTSAFEPOINT;
935936
extern void _JL_GC_PUSHARGS(jl_value_t **, size_t) JL_NOTSAFEPOINT;
936937
// This is necessary, because otherwise the analyzer considers this undefined
937938
// behavior and terminates the exploration
@@ -974,6 +975,9 @@ extern void JL_GC_POP() JL_NOTSAFEPOINT;
974975
#define JL_GC_PUSH8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
975976
void *__gc_stkf[] = {(void*)JL_GC_ENCODE_PUSH(8), jl_pgcstack, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8}; \
976977
jl_pgcstack = (jl_gcframe_t*)__gc_stkf;
978+
#define JL_GC_PUSH9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
979+
void *__gc_stkf[] = {(void*)JL_GC_ENCODE_PUSH(9), jl_pgcstack, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9}; \
980+
jl_pgcstack = (jl_gcframe_t*)__gc_stkf;
977981

978982

979983
#define JL_GC_PUSHARGS(rts_var,n) \
@@ -1901,6 +1905,9 @@ JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *d
19011905

19021906
JL_DLLEXPORT void jl_set_newly_inferred(jl_value_t *newly_inferred);
19031907
JL_DLLEXPORT void jl_push_newly_inferred(jl_value_t *ci);
1908+
JL_DLLEXPORT void jl_method_table_disable_incremental(jl_methtable_t *mt, jl_method_t *m);
1909+
JL_DLLEXPORT void jl_set_newly_deleted(jl_value_t *newly_deleted);
1910+
JL_DLLEXPORT void jl_push_newly_deleted(jl_value_t *m);
19041911
JL_DLLEXPORT void jl_write_compiler_output(void);
19051912

19061913
// parsing

src/staticdata.c

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,7 +2393,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new
23932393
static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
23942394
jl_array_t *worklist, jl_array_t *extext_methods,
23952395
jl_array_t *new_specializations, jl_array_t *method_roots_list,
2396-
jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED
2396+
jl_array_t *ext_targets, jl_array_t *edges, jl_array_t *newly_deleted) JL_GC_DISABLED
23972397
{
23982398
htable_new(&field_replace, 0);
23992399
// strip metadata and IR when requested
@@ -2514,6 +2514,10 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
25142514
jl_queue_for_serialization(&s, ext_targets);
25152515
jl_queue_for_serialization(&s, edges);
25162516
}
2517+
if (newly_deleted) {
2518+
jl_queue_for_serialization(&s, newly_deleted);
2519+
}
2520+
25172521
jl_serialize_reachable(&s);
25182522
// step 1.2: ensure all gvars are part of the sysimage too
25192523
record_gvars(&s, &gvars);
@@ -2647,6 +2651,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
26472651
jl_write_value(&s, method_roots_list);
26482652
jl_write_value(&s, ext_targets);
26492653
jl_write_value(&s, edges);
2654+
jl_write_value(&s, newly_deleted);
26502655
}
26512656
write_uint32(f, jl_array_len(s.link_ids_gctags));
26522657
ios_write(f, (char*)jl_array_data(s.link_ids_gctags), jl_array_len(s.link_ids_gctags) * sizeof(uint32_t));
@@ -2725,11 +2730,11 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
27252730
}
27262731

27272732
jl_array_t *mod_array = NULL, *extext_methods = NULL, *new_specializations = NULL;
2728-
jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
2733+
jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL, *_newly_deleted = NULL;
27292734
int64_t checksumpos = 0;
27302735
int64_t checksumpos_ff = 0;
27312736
int64_t datastartpos = 0;
2732-
JL_GC_PUSH6(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
2737+
JL_GC_PUSH7(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &_newly_deleted);
27332738

27342739
if (worklist) {
27352740
mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
@@ -2776,7 +2781,10 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
27762781
}
27772782
if (_native_data != NULL)
27782783
native_functions = *_native_data;
2779-
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
2784+
// Otherwise serialization will be confused.
2785+
if (newly_deleted)
2786+
_newly_deleted = jl_array_copy(newly_deleted);
2787+
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges, _newly_deleted);
27802788
if (_native_data != NULL)
27812789
native_functions = NULL;
27822790
// make sure we don't run any Julia code concurrently before this point
@@ -2860,6 +2868,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
28602868
jl_array_t **extext_methods,
28612869
jl_array_t **new_specializations, jl_array_t **method_roots_list,
28622870
jl_array_t **ext_targets, jl_array_t **edges,
2871+
jl_array_t **newly_deleted,
28632872
char **base, arraylist_t *ccallable_list, pkgcachesizes *cachesizes) JL_GC_DISABLED
28642873
{
28652874
int en = jl_gc_enable(0);
@@ -2921,7 +2930,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
29212930
assert(!ios_eof(f));
29222931
s.s = f;
29232932
uintptr_t offset_restored = 0, offset_init_order = 0, offset_extext_methods = 0, offset_new_specializations = 0, offset_method_roots_list = 0;
2924-
uintptr_t offset_ext_targets = 0, offset_edges = 0;
2933+
uintptr_t offset_ext_targets = 0, offset_edges = 0, offset_newly_deleted = 0;
29252934
if (!s.incremental) {
29262935
size_t i;
29272936
for (i = 0; tags[i] != NULL; i++) {
@@ -2955,6 +2964,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
29552964
offset_method_roots_list = jl_read_offset(&s);
29562965
offset_ext_targets = jl_read_offset(&s);
29572966
offset_edges = jl_read_offset(&s);
2967+
offset_newly_deleted = jl_read_offset(&s);
29582968
}
29592969
s.buildid_depmods_idxs = depmod_to_imageidx(depmods);
29602970
size_t nlinks_gctags = read_uint32(f);
@@ -2988,6 +2998,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
29882998
*method_roots_list = (jl_array_t*)jl_delayed_reloc(&s, offset_method_roots_list);
29892999
*ext_targets = (jl_array_t*)jl_delayed_reloc(&s, offset_ext_targets);
29903000
*edges = (jl_array_t*)jl_delayed_reloc(&s, offset_edges);
3001+
*newly_deleted = (jl_array_t*)jl_delayed_reloc(&s, offset_newly_deleted);
29913002
if (!*new_specializations)
29923003
*new_specializations = jl_alloc_vec_any(0);
29933004
}
@@ -3175,6 +3186,11 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
31753186
assert(jl_is_datatype(obj));
31763187
jl_cache_type_((jl_datatype_t*)obj);
31773188
}
3189+
3190+
// Delete methods before inserting new ones.
3191+
if (newly_deleted)
3192+
jl_delete_methods(*newly_deleted);
3193+
31783194
// Perform fixups: things like updating world ages, inserting methods & specializations, etc.
31793195
size_t world = jl_atomic_load_acquire(&jl_world_counter);
31803196
for (size_t i = 0; i < s.uniquing_objs.len; i++) {
@@ -3401,11 +3417,11 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
34013417
assert(datastartpos > 0 && datastartpos < dataendpos);
34023418
needs_permalloc = jl_options.permalloc_pkgimg || needs_permalloc;
34033419
jl_value_t *restored = NULL;
3404-
jl_array_t *init_order = NULL, *extext_methods = NULL, *new_specializations = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
3420+
jl_array_t *init_order = NULL, *extext_methods = NULL, *new_specializations = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL, *newly_deleted = NULL;
34053421
jl_svec_t *cachesizes_sv = NULL;
34063422
char *base;
34073423
arraylist_t ccallable_list;
3408-
JL_GC_PUSH8(&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &cachesizes_sv);
3424+
JL_GC_PUSH9(&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &newly_deleted, &cachesizes_sv);
34093425

34103426
{ // make a permanent in-memory copy of f (excluding the header)
34113427
ios_bufmode(f, bm_none);
@@ -3429,11 +3445,12 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
34293445
ios_close(f);
34303446
ios_static_buffer(f, sysimg, len);
34313447
pkgcachesizes cachesizes;
3432-
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);
3448+
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);
34333449
JL_SIGATOMIC_END();
34343450

34353451
// Insert method extensions
34363452
jl_insert_methods(extext_methods);
3453+
34373454
// No special processing of `new_specializations` is required because recaching handled it
34383455
// Add roots to methods
34393456
jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored));
@@ -3469,7 +3486,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
34693486
static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uint32_t checksum)
34703487
{
34713488
JL_TIMING(LOAD_IMAGE, LOAD_Sysimg);
3472-
jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
3489+
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);
34733490
}
34743491

34753492
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, bool needs_permalloc)

0 commit comments

Comments
 (0)