Skip to content

Commit 3cbf778

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 777efa1 commit 3cbf778

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
@@ -2306,6 +2306,7 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto
23062306
end
23072307

23082308
ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred)
2309+
ccall(:jl_set_newly_deleted, Cvoid, (Any,), Core.Compiler.newly_deleted)
23092310
Core.Compiler.track_newly_inferred.x = true
23102311
try
23112312
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));
@@ -2726,11 +2731,11 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
27262731
}
27272732

27282733
jl_array_t *mod_array = NULL, *extext_methods = NULL, *new_specializations = NULL;
2729-
jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
2734+
jl_array_t *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL, *_newly_deleted = NULL;
27302735
int64_t checksumpos = 0;
27312736
int64_t checksumpos_ff = 0;
27322737
int64_t datastartpos = 0;
2733-
JL_GC_PUSH6(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges);
2738+
JL_GC_PUSH7(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &_newly_deleted);
27342739

27352740
if (worklist) {
27362741
mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
@@ -2777,7 +2782,10 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
27772782
}
27782783
if (_native_data != NULL)
27792784
native_functions = *_native_data;
2780-
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
2785+
// Otherwise serialization will be confused.
2786+
if (newly_deleted)
2787+
_newly_deleted = jl_array_copy(newly_deleted);
2788+
jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges, _newly_deleted);
27812789
if (_native_data != NULL)
27822790
native_functions = NULL;
27832791
// make sure we don't run any Julia code concurrently before this point
@@ -2861,6 +2869,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
28612869
jl_array_t **extext_methods,
28622870
jl_array_t **new_specializations, jl_array_t **method_roots_list,
28632871
jl_array_t **ext_targets, jl_array_t **edges,
2872+
jl_array_t **newly_deleted,
28642873
char **base, arraylist_t *ccallable_list, pkgcachesizes *cachesizes) JL_GC_DISABLED
28652874
{
28662875
int en = jl_gc_enable(0);
@@ -2922,7 +2931,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
29222931
assert(!ios_eof(f));
29232932
s.s = f;
29242933
uintptr_t offset_restored = 0, offset_init_order = 0, offset_extext_methods = 0, offset_new_specializations = 0, offset_method_roots_list = 0;
2925-
uintptr_t offset_ext_targets = 0, offset_edges = 0;
2934+
uintptr_t offset_ext_targets = 0, offset_edges = 0, offset_newly_deleted = 0;
29262935
if (!s.incremental) {
29272936
size_t i;
29282937
for (i = 0; tags[i] != NULL; i++) {
@@ -2956,6 +2965,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
29562965
offset_method_roots_list = jl_read_offset(&s);
29572966
offset_ext_targets = jl_read_offset(&s);
29582967
offset_edges = jl_read_offset(&s);
2968+
offset_newly_deleted = jl_read_offset(&s);
29592969
}
29602970
s.buildid_depmods_idxs = depmod_to_imageidx(depmods);
29612971
size_t nlinks_gctags = read_uint32(f);
@@ -2989,6 +2999,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
29892999
*method_roots_list = (jl_array_t*)jl_delayed_reloc(&s, offset_method_roots_list);
29903000
*ext_targets = (jl_array_t*)jl_delayed_reloc(&s, offset_ext_targets);
29913001
*edges = (jl_array_t*)jl_delayed_reloc(&s, offset_edges);
3002+
*newly_deleted = (jl_array_t*)jl_delayed_reloc(&s, offset_newly_deleted);
29923003
if (!*new_specializations)
29933004
*new_specializations = jl_alloc_vec_any(0);
29943005
}
@@ -3176,6 +3187,11 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
31763187
assert(jl_is_datatype(obj));
31773188
jl_cache_type_((jl_datatype_t*)obj);
31783189
}
3190+
3191+
// Delete methods before inserting new ones.
3192+
if (newly_deleted)
3193+
jl_delete_methods(*newly_deleted);
3194+
31793195
// Perform fixups: things like updating world ages, inserting methods & specializations, etc.
31803196
size_t world = jl_atomic_load_acquire(&jl_world_counter);
31813197
for (size_t i = 0; i < s.uniquing_objs.len; i++) {
@@ -3402,11 +3418,11 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
34023418
assert(datastartpos > 0 && datastartpos < dataendpos);
34033419
needs_permalloc = jl_options.permalloc_pkgimg || needs_permalloc;
34043420
jl_value_t *restored = NULL;
3405-
jl_array_t *init_order = NULL, *extext_methods = NULL, *new_specializations = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL;
3421+
jl_array_t *init_order = NULL, *extext_methods = NULL, *new_specializations = NULL, *method_roots_list = NULL, *ext_targets = NULL, *edges = NULL, *newly_deleted = NULL;
34063422
jl_svec_t *cachesizes_sv = NULL;
34073423
char *base;
34083424
arraylist_t ccallable_list;
3409-
JL_GC_PUSH8(&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &cachesizes_sv);
3425+
JL_GC_PUSH9(&restored, &init_order, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &newly_deleted, &cachesizes_sv);
34103426

34113427
{ // make a permanent in-memory copy of f (excluding the header)
34123428
ios_bufmode(f, bm_none);
@@ -3430,11 +3446,12 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i
34303446
ios_close(f);
34313447
ios_static_buffer(f, sysimg, len);
34323448
pkgcachesizes cachesizes;
3433-
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);
3449+
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);
34343450
JL_SIGATOMIC_END();
34353451

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

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