@@ -92,6 +92,22 @@ static const size_t WORLD_AGE_REVALIDATION_SENTINEL = 0x1;
92
92
JL_DLLEXPORT size_t jl_require_world = ~(size_t )0 ;
93
93
JL_DLLEXPORT _Atomic(size_t ) jl_first_image_replacement_world = ~(size_t )0 ;
94
94
95
+ // This structure is used to store hash tables for the memoization
96
+ // of queries in staticdata.c (currently only `type_in_worklist`).
97
+ typedef struct {
98
+ htable_t type_in_worklist ;
99
+ } jl_query_cache ;
100
+
101
+ static void init_query_cache (jl_query_cache * cache )
102
+ {
103
+ htable_new (& cache -> type_in_worklist , 0 );
104
+ }
105
+
106
+ static void destroy_query_cache (jl_query_cache * cache )
107
+ {
108
+ htable_free (& cache -> type_in_worklist );
109
+ }
110
+
95
111
#include "staticdata_utils.c"
96
112
#include "precompile_utils.c"
97
113
@@ -555,6 +571,7 @@ typedef struct {
555
571
jl_array_t * method_roots_list ;
556
572
htable_t method_roots_index ;
557
573
uint64_t worklist_key ;
574
+ jl_query_cache * query_cache ;
558
575
jl_ptls_t ptls ;
559
576
jl_image_t * image ;
560
577
int8_t incremental ;
@@ -702,14 +719,13 @@ static int jl_needs_serialization(jl_serializer_state *s, jl_value_t *v) JL_NOTS
702
719
return 1 ;
703
720
}
704
721
705
-
706
- static int caching_tag (jl_value_t * v ) JL_NOTSAFEPOINT
722
+ static int caching_tag (jl_value_t * v , jl_query_cache * query_cache ) JL_NOTSAFEPOINT
707
723
{
708
724
if (jl_is_method_instance (v )) {
709
725
jl_method_instance_t * mi = (jl_method_instance_t * )v ;
710
726
jl_value_t * m = mi -> def .value ;
711
727
if (jl_is_method (m ) && jl_object_in_image (m ))
712
- return 1 + type_in_worklist (mi -> specTypes );
728
+ return 1 + type_in_worklist (mi -> specTypes , query_cache );
713
729
}
714
730
if (jl_is_binding (v )) {
715
731
jl_globalref_t * gr = ((jl_binding_t * )v )-> globalref ;
@@ -724,24 +740,24 @@ static int caching_tag(jl_value_t *v) JL_NOTSAFEPOINT
724
740
if (jl_is_tuple_type (dt ) ? !dt -> isconcretetype : dt -> hasfreetypevars )
725
741
return 0 ; // aka !is_cacheable from jltypes.c
726
742
if (jl_object_in_image ((jl_value_t * )dt -> name ))
727
- return 1 + type_in_worklist (v );
743
+ return 1 + type_in_worklist (v , query_cache );
728
744
}
729
745
jl_value_t * dtv = jl_typeof (v );
730
746
if (jl_is_datatype_singleton ((jl_datatype_t * )dtv )) {
731
- return 1 - type_in_worklist (dtv ); // these are already recached in the datatype in the image
747
+ return 1 - type_in_worklist (dtv , query_cache ); // these are already recached in the datatype in the image
732
748
}
733
749
return 0 ;
734
750
}
735
751
736
- static int needs_recaching (jl_value_t * v ) JL_NOTSAFEPOINT
752
+ static int needs_recaching (jl_value_t * v , jl_query_cache * query_cache ) JL_NOTSAFEPOINT
737
753
{
738
- return caching_tag (v ) == 2 ;
754
+ return caching_tag (v , query_cache ) == 2 ;
739
755
}
740
756
741
- static int needs_uniquing (jl_value_t * v ) JL_NOTSAFEPOINT
757
+ static int needs_uniquing (jl_value_t * v , jl_query_cache * query_cache ) JL_NOTSAFEPOINT
742
758
{
743
759
assert (!jl_object_in_image (v ));
744
- return caching_tag (v ) == 1 ;
760
+ return caching_tag (v , query_cache ) == 1 ;
745
761
}
746
762
747
763
static void record_field_change (jl_value_t * * addr , jl_value_t * newval ) JL_NOTSAFEPOINT
@@ -861,7 +877,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_
861
877
jl_datatype_t * dt = (jl_datatype_t * )v ;
862
878
// ensure all type parameters are recached
863
879
jl_queue_for_serialization_ (s , (jl_value_t * )dt -> parameters , 1 , 1 );
864
- if (jl_is_datatype_singleton (dt ) && needs_uniquing (dt -> instance )) {
880
+ if (jl_is_datatype_singleton (dt ) && needs_uniquing (dt -> instance , s -> query_cache )) {
865
881
assert (jl_needs_serialization (s , dt -> instance )); // should be true, since we visited dt
866
882
// do not visit dt->instance for our template object as it leads to unwanted cycles here
867
883
// (it may get serialized from elsewhere though)
@@ -872,7 +888,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_
872
888
if (s -> incremental && jl_is_method_instance (v )) {
873
889
jl_method_instance_t * mi = (jl_method_instance_t * )v ;
874
890
jl_value_t * def = mi -> def .value ;
875
- if (needs_uniquing (v )) {
891
+ if (needs_uniquing (v , s -> query_cache )) {
876
892
// we only need 3 specific fields of this (the rest are not used)
877
893
jl_queue_for_serialization (s , mi -> def .value );
878
894
jl_queue_for_serialization (s , mi -> specTypes );
@@ -887,7 +903,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_
887
903
record_field_change ((jl_value_t * * )& mi -> cache , NULL );
888
904
}
889
905
else {
890
- assert (!needs_recaching (v ));
906
+ assert (!needs_recaching (v , s -> query_cache ));
891
907
}
892
908
// n.b. opaque closures cannot be inspected and relied upon like a
893
909
// normal method since they can get improperly introduced by generated
@@ -897,7 +913,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_
897
913
// error now.
898
914
}
899
915
if (s -> incremental && jl_is_binding (v )) {
900
- if (needs_uniquing (v )) {
916
+ if (needs_uniquing (v , s -> query_cache )) {
901
917
jl_binding_t * b = (jl_binding_t * )v ;
902
918
jl_queue_for_serialization (s , b -> globalref -> mod );
903
919
jl_queue_for_serialization (s , b -> globalref -> name );
@@ -1121,9 +1137,9 @@ static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, i
1121
1137
// Items that require postorder traversal must visit their children prior to insertion into
1122
1138
// the worklist/serialization_order (and also before their first use)
1123
1139
if (s -> incremental && !immediate ) {
1124
- if (jl_is_datatype (t ) && needs_uniquing (v ))
1140
+ if (jl_is_datatype (t ) && needs_uniquing (v , s -> query_cache ))
1125
1141
immediate = 1 ;
1126
- if (jl_is_datatype_singleton ((jl_datatype_t * )t ) && needs_uniquing (v ))
1142
+ if (jl_is_datatype_singleton ((jl_datatype_t * )t ) && needs_uniquing (v , s -> query_cache ))
1127
1143
immediate = 1 ;
1128
1144
}
1129
1145
@@ -1286,7 +1302,7 @@ static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t *
1286
1302
1287
1303
static void record_uniquing (jl_serializer_state * s , jl_value_t * fld , uintptr_t offset ) JL_NOTSAFEPOINT
1288
1304
{
1289
- if (s -> incremental && jl_needs_serialization (s , fld ) && needs_uniquing (fld )) {
1305
+ if (s -> incremental && jl_needs_serialization (s , fld ) && needs_uniquing (fld , s -> query_cache )) {
1290
1306
if (jl_is_datatype (fld ) || jl_is_datatype_singleton ((jl_datatype_t * )jl_typeof (fld )))
1291
1307
arraylist_push (& s -> uniquing_types , (void * )(uintptr_t )offset );
1292
1308
else if (jl_is_method_instance (fld ) || jl_is_binding (fld ))
@@ -1510,7 +1526,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED
1510
1526
// write header
1511
1527
if (object_id_expected )
1512
1528
write_uint (f , jl_object_id (v ));
1513
- if (s -> incremental && jl_needs_serialization (s , (jl_value_t * )t ) && needs_uniquing ((jl_value_t * )t ))
1529
+ if (s -> incremental && jl_needs_serialization (s , (jl_value_t * )t ) && needs_uniquing ((jl_value_t * )t , s -> query_cache ))
1514
1530
arraylist_push (& s -> uniquing_types , (void * )(uintptr_t )(ios_pos (f )|1 ));
1515
1531
if (f == s -> const_data )
1516
1532
write_uint (s -> const_data , ((uintptr_t )t -> smalltag << 4 ) | GC_OLD_MARKED | GC_IN_IMAGE );
@@ -1521,7 +1537,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED
1521
1537
layout_table .items [item ] = (void * )(reloc_offset | (f == s -> const_data )); // store the inverse mapping of `serialization_order` (`id` => object-as-streampos)
1522
1538
1523
1539
if (s -> incremental ) {
1524
- if (needs_uniquing (v )) {
1540
+ if (needs_uniquing (v , s -> query_cache )) {
1525
1541
if (jl_typetagis (v , jl_binding_type )) {
1526
1542
jl_binding_t * b = (jl_binding_t * )v ;
1527
1543
if (b -> globalref == NULL )
@@ -1550,7 +1566,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED
1550
1566
assert (jl_is_datatype_singleton (t ) && "unreachable" );
1551
1567
}
1552
1568
}
1553
- else if (needs_recaching (v )) {
1569
+ else if (needs_recaching (v , s -> query_cache )) {
1554
1570
arraylist_push (jl_is_datatype (v ) ? & s -> fixup_types : & s -> fixup_objs , (void * )reloc_offset );
1555
1571
}
1556
1572
}
@@ -1985,7 +2001,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED
1985
2001
}
1986
2002
}
1987
2003
void * superidx = ptrhash_get (& serialization_order , dt -> super );
1988
- if (s -> incremental && superidx != HT_NOTFOUND && from_seroder_entry (superidx ) > item && needs_uniquing ((jl_value_t * )dt -> super ))
2004
+ if (s -> incremental && superidx != HT_NOTFOUND && from_seroder_entry (superidx ) > item && needs_uniquing ((jl_value_t * )dt -> super , s -> query_cache ))
1989
2005
arraylist_push (& s -> uniquing_super , dt -> super );
1990
2006
}
1991
2007
else if (jl_is_typename (v )) {
@@ -2919,13 +2935,14 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val, int insert)
2919
2935
static void jl_prepare_serialization_data (jl_array_t * mod_array , jl_array_t * newly_inferred ,
2920
2936
/* outputs */ jl_array_t * * extext_methods JL_REQUIRE_ROOTED_SLOT ,
2921
2937
jl_array_t * * new_ext_cis JL_REQUIRE_ROOTED_SLOT ,
2922
- jl_array_t * * edges JL_REQUIRE_ROOTED_SLOT )
2938
+ jl_array_t * * edges JL_REQUIRE_ROOTED_SLOT ,
2939
+ jl_query_cache * query_cache )
2923
2940
{
2924
2941
// extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist
2925
2942
// edges: [caller1, ext_targets, ...] for worklist-owned methods calling external methods
2926
2943
2927
2944
// Save the inferred code from newly inferred, external methods
2928
- * new_ext_cis = queue_external_cis (newly_inferred );
2945
+ * new_ext_cis = queue_external_cis (newly_inferred , query_cache );
2929
2946
2930
2947
// Collect method extensions and edges data
2931
2948
* extext_methods = jl_alloc_vec_any (0 );
@@ -2955,7 +2972,8 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new
2955
2972
// In addition to the system image (where `worklist = NULL`), this can also save incremental images with external linkage
2956
2973
static void jl_save_system_image_to_stream (ios_t * f , jl_array_t * mod_array ,
2957
2974
jl_array_t * worklist , jl_array_t * extext_methods ,
2958
- jl_array_t * new_ext_cis , jl_array_t * edges )
2975
+ jl_array_t * new_ext_cis , jl_array_t * edges ,
2976
+ jl_query_cache * query_cache )
2959
2977
{
2960
2978
htable_new (& field_replace , 0 );
2961
2979
htable_new (& bits_replace , 0 );
@@ -3062,6 +3080,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array,
3062
3080
ios_mem (& gvar_record , 0 );
3063
3081
ios_mem (& fptr_record , 0 );
3064
3082
jl_serializer_state s = {0 };
3083
+ s .query_cache = query_cache ;
3065
3084
s .incremental = !(worklist == NULL );
3066
3085
s .s = & sysimg ;
3067
3086
s .const_data = & const_data ;
@@ -3422,11 +3441,14 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
3422
3441
int64_t datastartpos = 0 ;
3423
3442
JL_GC_PUSH4 (& mod_array , & extext_methods , & new_ext_cis , & edges );
3424
3443
3444
+ jl_query_cache query_cache ;
3445
+ init_query_cache (& query_cache );
3446
+
3425
3447
if (worklist ) {
3426
3448
mod_array = jl_get_loaded_modules (); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array)
3427
3449
// Generate _native_data`
3428
3450
if (_native_data != NULL ) {
3429
- jl_prepare_serialization_data (mod_array , newly_inferred , & extext_methods , & new_ext_cis , NULL );
3451
+ jl_prepare_serialization_data (mod_array , newly_inferred , & extext_methods , & new_ext_cis , NULL , & query_cache );
3430
3452
jl_precompile_toplevel_module = (jl_module_t * )jl_array_ptr_ref (worklist , jl_array_len (worklist )- 1 );
3431
3453
* _native_data = jl_precompile_worklist (worklist , extext_methods , new_ext_cis );
3432
3454
jl_precompile_toplevel_module = NULL ;
@@ -3457,7 +3479,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
3457
3479
assert ((ct -> reentrant_timing & 0b1110 ) == 0 );
3458
3480
ct -> reentrant_timing |= 0b1000 ;
3459
3481
if (worklist ) {
3460
- jl_prepare_serialization_data (mod_array , newly_inferred , & extext_methods , & new_ext_cis , & edges );
3482
+ jl_prepare_serialization_data (mod_array , newly_inferred , & extext_methods , & new_ext_cis , & edges , & query_cache );
3461
3483
if (!emit_split ) {
3462
3484
write_int32 (f , 0 ); // No clone_targets
3463
3485
write_padding (f , LLT_ALIGN (ios_pos (f ), JL_CACHE_BYTE_ALIGNMENT ) - ios_pos (f ));
@@ -3469,7 +3491,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
3469
3491
}
3470
3492
if (_native_data != NULL )
3471
3493
native_functions = * _native_data ;
3472
- jl_save_system_image_to_stream (ff , mod_array , worklist , extext_methods , new_ext_cis , edges );
3494
+ jl_save_system_image_to_stream (ff , mod_array , worklist , extext_methods , new_ext_cis , edges , & query_cache );
3473
3495
if (_native_data != NULL )
3474
3496
native_functions = NULL ;
3475
3497
// make sure we don't run any Julia code concurrently before this point
@@ -3498,6 +3520,8 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
3498
3520
}
3499
3521
}
3500
3522
3523
+ destroy_query_cache (& query_cache );
3524
+
3501
3525
JL_GC_POP ();
3502
3526
* s = f ;
3503
3527
if (emit_split )
0 commit comments