@@ -751,56 +751,58 @@ static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key)
751751 }
752752}
753753
754- static size_t verify_invokesig (jl_value_t * invokesig , jl_method_t * expected , size_t minworld )
754+ static void verify_invokesig (jl_value_t * invokesig , jl_method_t * expected , size_t world , size_t * minworld , size_t * maxworld )
755755{
756756 assert (jl_is_type (invokesig ));
757757 assert (jl_is_method (expected ));
758- size_t min_valid = 0 ;
759- size_t max_valid = ~(size_t )0 ;
760758 if (jl_egal (invokesig , expected -> sig )) {
761759 // the invoke match is `expected` for `expected->sig`, unless `expected` is invalid
762- if (jl_atomic_load_relaxed (& expected -> deleted_world ) < max_valid )
763- max_valid = 0 ;
760+ * minworld = jl_atomic_load_relaxed (& expected -> primary_world );
761+ * maxworld = jl_atomic_load_relaxed (& expected -> deleted_world );
762+ assert (* minworld <= world );
763+ if (* maxworld < world )
764+ * maxworld = 0 ;
764765 }
765766 else {
767+ * minworld = 1 ;
768+ * maxworld = ~(size_t )0 ;
766769 jl_methtable_t * mt = jl_method_get_table (expected );
767770 if ((jl_value_t * )mt == jl_nothing ) {
768- max_valid = 0 ;
771+ * maxworld = 0 ;
769772 }
770773 else {
771- jl_value_t * matches = jl_gf_invoke_lookup_worlds (invokesig , (jl_value_t * )mt , minworld , & min_valid , & max_valid );
774+ jl_value_t * matches = jl_gf_invoke_lookup_worlds (invokesig , (jl_value_t * )mt , world , minworld , maxworld );
772775 if (matches == jl_nothing ) {
773- max_valid = 0 ;
776+ * maxworld = 0 ;
774777 }
775778 else {
776779 if (((jl_method_match_t * )matches )-> method != expected ) {
777- max_valid = 0 ;
780+ * maxworld = 0 ;
778781 }
779782 }
780783 }
781784 }
782- return max_valid ;
783785}
784786
785- static size_t verify_call (jl_value_t * sig , jl_svec_t * expecteds , size_t i , size_t n , size_t minworld , jl_value_t * * matches JL_REQUIRE_ROOTED_SLOT )
787+ static void verify_call (jl_value_t * sig , jl_svec_t * expecteds , size_t i , size_t n , size_t world , size_t * minworld , size_t * maxworld , jl_value_t * * matches JL_REQUIRE_ROOTED_SLOT )
786788{
787789 // verify that these edges intersect with the same methods as before
788- size_t min_valid = 0 ;
789- size_t max_valid = ~(size_t )0 ;
790+ * minworld = 1 ;
791+ * maxworld = ~(size_t )0 ;
790792 int ambig = 0 ;
791793 // TODO: possibly need to included ambiguities too (for the optimizer correctness)?
792794 jl_value_t * result = jl_matching_methods ((jl_tupletype_t * )sig , jl_nothing ,
793795 _jl_debug_method_invalidation ? INT32_MAX : n ,
794- 0 , minworld , & min_valid , & max_valid , & ambig );
796+ 0 , world , minworld , maxworld , & ambig );
795797 * matches = result ;
796798 if (result == jl_nothing ) {
797- max_valid = 0 ;
799+ * maxworld = 0 ;
798800 }
799801 else {
800802 // setdiff!(result, expected)
801803 size_t j , k , ins = 0 ;
802804 if (jl_array_nrows (result ) != n ) {
803- max_valid = 0 ;
805+ * maxworld = 0 ;
804806 }
805807 for (k = 0 ; k < jl_array_nrows (result ); k ++ ) {
806808 jl_method_t * match = ((jl_method_match_t * )jl_array_ptr_ref (result , k ))-> method ;
@@ -822,29 +824,33 @@ static size_t verify_call(jl_value_t *sig, jl_svec_t *expecteds, size_t i, size_
822824 // intersection has a new method or a method was
823825 // deleted--this is now probably no good, just invalidate
824826 // everything about it now
825- max_valid = 0 ;
827+ * maxworld = 0 ;
826828 if (!_jl_debug_method_invalidation )
827829 break ;
828830 jl_array_ptr_set (result , ins ++ , match );
829831 }
830832 }
831- if (max_valid != ~(size_t )0 && _jl_debug_method_invalidation )
833+ if (* maxworld != ~(size_t )0 && _jl_debug_method_invalidation )
832834 jl_array_del_end ((jl_array_t * )result , jl_array_nrows (result ) - ins );
833835 }
834- return max_valid ;
835836}
836837
837838// Test all edges relevant to a method:
838839//// Visit the entire call graph, starting from edges[idx] to determine if that method is valid
839840//// Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable
840841//// and slightly modified with an early termination option once the computation reaches its minimum
841- static int jl_verify_method (jl_code_instance_t * codeinst , size_t minworld , size_t * maxworld , arraylist_t * stack , htable_t * visiting )
842+ static int jl_verify_method (jl_code_instance_t * codeinst , size_t * minworld , size_t * maxworld , arraylist_t * stack , htable_t * visiting )
842843{
844+ size_t world = jl_atomic_load_relaxed (& codeinst -> min_world );
843845 size_t max_valid2 = jl_atomic_load_relaxed (& codeinst -> max_world );
844846 if (max_valid2 != WORLD_AGE_REVALIDATION_SENTINEL ) {
847+ * minworld = world ;
845848 * maxworld = max_valid2 ;
846849 return 0 ;
847850 }
851+ * minworld = 1 ;
852+ size_t current_world = jl_atomic_load_relaxed (& jl_world_counter );
853+ * maxworld = current_world ;
848854 assert (jl_is_method_instance (codeinst -> def ) && jl_is_method (codeinst -> def -> def .method ));
849855 void * * bp = ptrhash_bp (visiting , codeinst );
850856 if (* bp != HT_NOTFOUND )
@@ -862,21 +868,22 @@ static int jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, size_
862868 // verify current edges
863869 for (size_t j = 0 ; j < jl_svec_len (callees ); ) {
864870 jl_value_t * edge = jl_svecref (callees , j );
871+ size_t min_valid2 ;
865872 size_t max_valid2 ;
866873 assert (!jl_is_method (edge )); // `Method`-edge isn't allowed for the optimized one-edge format
867874 if (jl_is_code_instance (edge ))
868875 edge = (jl_value_t * )((jl_code_instance_t * )edge )-> def ;
869876 if (jl_is_method_instance (edge )) {
870877 jl_method_instance_t * mi = (jl_method_instance_t * )edge ;
871878 sig = jl_type_intersection (mi -> def .method -> sig , (jl_value_t * )mi -> specTypes ); // TODO: ??
872- max_valid2 = verify_call (sig , callees , j , 1 , minworld , & matches );
879+ verify_call (sig , callees , j , 1 , world , & min_valid2 , & max_valid2 , & matches );
873880 sig = NULL ;
874881 j += 1 ;
875882 }
876883 else if (jl_is_long (edge )) {
877884 jl_value_t * sig = jl_svecref (callees , j + 1 );
878885 size_t nedges = jl_unbox_long (edge );
879- max_valid2 = verify_call (sig , callees , j + 2 , nedges , minworld , & matches );
886+ verify_call (sig , callees , j + 2 , nedges , world , & min_valid2 , & max_valid2 , & matches );
880887 j += 2 + nedges ;
881888 edge = sig ;
882889 }
@@ -896,9 +903,11 @@ static int jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, size_
896903 assert (jl_is_method (callee ));
897904 meth = (jl_method_t * )callee ;
898905 }
899- max_valid2 = verify_invokesig (edge , meth , minworld );
906+ verify_invokesig (edge , meth , world , & min_valid2 , & max_valid2 );
900907 j += 2 ;
901908 }
909+ if (* minworld < min_valid2 )
910+ * minworld = min_valid2 ;
902911 if (* maxworld > max_valid2 )
903912 * maxworld = max_valid2 ;
904913 if (max_valid2 != ~(size_t )0 && _jl_debug_method_invalidation ) {
@@ -917,14 +926,19 @@ static int jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, size_
917926 // verify recursive edges (if valid, or debugging)
918927 size_t cycle = depth ;
919928 jl_code_instance_t * cause = codeinst ;
920- if (* maxworld == ~( size_t ) 0 || _jl_debug_method_invalidation ) {
929+ if (* maxworld != 0 || _jl_debug_method_invalidation ) {
921930 for (size_t j = 0 ; j < jl_svec_len (callees ); j ++ ) {
922931 jl_value_t * edge = jl_svecref (callees , j );
923932 if (!jl_is_code_instance (edge ))
924933 continue ;
925934 jl_code_instance_t * callee = (jl_code_instance_t * )edge ;
926- size_t max_valid2 = ~(size_t )0 ;
927- size_t child_cycle = jl_verify_method (callee , minworld , & max_valid2 , stack , visiting );
935+ size_t min_valid2 ;
936+ size_t max_valid2 ;
937+ size_t child_cycle = jl_verify_method (callee , & min_valid2 , & max_valid2 , stack , visiting );
938+ if (* minworld < min_valid2 )
939+ * minworld = min_valid2 ;
940+ if (* minworld > max_valid2 )
941+ max_valid2 = 0 ;
928942 if (* maxworld > max_valid2 ) {
929943 cause = callee ;
930944 * maxworld = max_valid2 ;
@@ -947,12 +961,18 @@ static int jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, size_
947961 // cycle as also having a failed edge.
948962 while (stack -> len >= depth ) {
949963 jl_code_instance_t * child = (jl_code_instance_t * )arraylist_pop (stack );
950- if (* maxworld != jl_atomic_load_relaxed (& child -> max_world ))
951- jl_atomic_store_relaxed (& child -> max_world , * maxworld );
964+ if (jl_atomic_load_relaxed (& jl_n_threads ) == 1 ) {
965+ // a different thread might simultaneously come to a different, but equally valid, alternative result
966+ assert (jl_atomic_load_relaxed (& child -> max_world ) == WORLD_AGE_REVALIDATION_SENTINEL );
967+ assert (* minworld <= jl_atomic_load_relaxed (& child -> min_world ));
968+ }
969+ if (* maxworld != 0 )
970+ jl_atomic_store_relaxed (& child -> min_world , * minworld );
971+ jl_atomic_store_relaxed (& child -> max_world , * maxworld );
952972 void * * bp = ptrhash_bp (visiting , codeinst );
953973 assert (* bp == (char * )HT_NOTFOUND + stack -> len + 1 );
954974 * bp = HT_NOTFOUND ;
955- if (_jl_debug_method_invalidation && * maxworld != ~( size_t ) 0 ) {
975+ if (_jl_debug_method_invalidation && * maxworld < current_world ) {
956976 jl_array_ptr_1d_push (_jl_debug_method_invalidation , (jl_value_t * )child );
957977 loctag = jl_cstr_to_string ("verify_methods" );
958978 JL_GC_PUSH1 (& loctag );
@@ -966,26 +986,30 @@ static int jl_verify_method(jl_code_instance_t *codeinst, size_t minworld, size_
966986 return 0 ;
967987}
968988
969- static size_t jl_verify_method_graph (jl_code_instance_t * codeinst , size_t minworld , arraylist_t * stack , htable_t * visiting )
989+ static void jl_verify_method_graph (jl_code_instance_t * codeinst , arraylist_t * stack , htable_t * visiting )
970990{
991+ size_t minworld ;
992+ size_t maxworld ;
971993 assert (stack -> len == 0 );
972994 for (size_t i = 0 , hsz = visiting -> size ; i < hsz ; i ++ )
973995 assert (visiting -> table [i ] == HT_NOTFOUND );
974- size_t maxworld = ~(size_t )0 ;
975- int child_cycle = jl_verify_method (codeinst , minworld , & maxworld , stack , visiting );
996+ int child_cycle = jl_verify_method (codeinst , & minworld , & maxworld , stack , visiting );
976997 assert (child_cycle == 0 ); (void )child_cycle ;
977998 assert (stack -> len == 0 );
978999 for (size_t i = 0 , hsz = visiting -> size / 2 ; i < hsz ; i ++ ) {
9791000 assert (visiting -> table [2 * i + 1 ] == HT_NOTFOUND );
9801001 visiting -> table [2 * i ] = HT_NOTFOUND ;
9811002 }
982- return maxworld ;
1003+ if (jl_atomic_load_relaxed (& jl_n_threads ) == 1 ) { // a different thread might simultaneously come to a different, but equally valid, alternative result
1004+ assert (maxworld == 0 || jl_atomic_load_relaxed (& codeinst -> min_world ) == minworld );
1005+ assert (jl_atomic_load_relaxed (& codeinst -> max_world ) == maxworld );
1006+ }
9831007}
9841008
9851009// Restore backedges to external targets
9861010// `edges` = [caller1, ...], the list of worklist-owned code instances internally
9871011// `ext_ci_list` = [caller1, ...], the list of worklist-owned code instances externally
988- static void jl_insert_backedges (jl_array_t * edges , jl_array_t * ext_ci_list , size_t minworld )
1012+ static void jl_insert_backedges (jl_array_t * edges , jl_array_t * ext_ci_list )
9891013{
9901014 // determine which CodeInstance objects are still valid in our image
9911015 // to enable any applicable new codes
@@ -1001,61 +1025,59 @@ static void jl_insert_backedges(jl_array_t *edges, jl_array_t *ext_ci_list, size
10011025 jl_code_instance_t * codeinst = (jl_code_instance_t * )jl_array_ptr_ref (edges , i );
10021026 jl_svec_t * callees = jl_atomic_load_relaxed (& codeinst -> edges );
10031027 jl_method_instance_t * caller = codeinst -> def ;
1004- if (jl_atomic_load_relaxed (& codeinst -> min_world ) != minworld ) {
1005- if (external && jl_atomic_load_relaxed (& codeinst -> max_world ) != WORLD_AGE_REVALIDATION_SENTINEL ) {
1006- assert (jl_atomic_load_relaxed (& codeinst -> min_world ) == 1 );
1007- assert (jl_atomic_load_relaxed (& codeinst -> max_world ) == ~(size_t )0 );
1008- }
1009- else {
1010- continue ;
1011- }
1012- }
1013- size_t maxvalid = jl_verify_method_graph (codeinst , minworld , & stack , & visiting );
1014- assert (jl_atomic_load_relaxed (& codeinst -> max_world ) == maxvalid );
1015- if (maxvalid == ~(size_t )0 ) {
1016- // if this callee is still valid, add all the backedges
1017- for (size_t j = 0 ; j < jl_svec_len (callees ); ) {
1018- jl_value_t * edge = jl_svecref (callees , j );
1019- if (jl_is_long (edge )) {
1020- j += 2 ; // skip over signature and count but not methods
1021- continue ;
1022- }
1023- else if (jl_is_method (edge )) {
1024- j += 1 ;
1025- continue ;
1026- }
1027- if (jl_is_code_instance (edge ))
1028- edge = (jl_value_t * )((jl_code_instance_t * )edge )-> def ;
1029- if (jl_is_method_instance (edge )) {
1030- jl_method_instance_add_backedge ((jl_method_instance_t * )edge , NULL , codeinst );
1031- j += 1 ;
1032- }
1033- else if (jl_is_mtable (edge )) {
1034- jl_methtable_t * mt = (jl_methtable_t * )edge ;
1035- jl_value_t * sig = jl_svecref (callees , j + 1 );
1036- jl_method_table_add_backedge (mt , sig , codeinst );
1037- j += 2 ;
1038- }
1039- else {
1040- jl_value_t * callee = jl_svecref (callees , j + 1 );
1041- if (jl_is_code_instance (callee ))
1042- callee = (jl_value_t * )((jl_code_instance_t * )callee )-> def ;
1043- else if (jl_is_method (callee )) {
1044- j += 2 ;
1028+ jl_verify_method_graph (codeinst , & stack , & visiting );
1029+ size_t minvalid = jl_atomic_load_relaxed (& codeinst -> min_world );
1030+ size_t maxvalid = jl_atomic_load_relaxed (& codeinst -> max_world );
1031+ if (maxvalid >= minvalid ) {
1032+ if (jl_atomic_load_relaxed (& jl_world_counter ) == maxvalid ) {
1033+ // if this callee is still valid, add all the backedges
1034+ for (size_t j = 0 ; j < jl_svec_len (callees ); ) {
1035+ jl_value_t * edge = jl_svecref (callees , j );
1036+ if (jl_is_long (edge )) {
1037+ j += 2 ; // skip over signature and count but not methods
10451038 continue ;
10461039 }
1047- jl_method_instance_add_backedge ((jl_method_instance_t * )callee , edge , codeinst );
1048- j += 2 ;
1040+ else if (jl_is_method (edge )) {
1041+ j += 1 ;
1042+ continue ;
1043+ }
1044+ if (jl_is_code_instance (edge ))
1045+ edge = (jl_value_t * )((jl_code_instance_t * )edge )-> def ;
1046+ if (jl_is_method_instance (edge )) {
1047+ jl_method_instance_add_backedge ((jl_method_instance_t * )edge , NULL , codeinst );
1048+ j += 1 ;
1049+ }
1050+ else if (jl_is_mtable (edge )) {
1051+ jl_methtable_t * mt = (jl_methtable_t * )edge ;
1052+ jl_value_t * sig = jl_svecref (callees , j + 1 );
1053+ jl_method_table_add_backedge (mt , sig , codeinst );
1054+ j += 2 ;
1055+ }
1056+ else {
1057+ jl_value_t * callee = jl_svecref (callees , j + 1 );
1058+ if (jl_is_code_instance (callee ))
1059+ callee = (jl_value_t * )((jl_code_instance_t * )callee )-> def ;
1060+ else if (jl_is_method (callee )) {
1061+ j += 2 ;
1062+ continue ;
1063+ }
1064+ jl_method_instance_add_backedge ((jl_method_instance_t * )callee , edge , codeinst );
1065+ j += 2 ;
1066+ }
10491067 }
10501068 }
1069+ if (jl_atomic_load_relaxed (& jl_world_counter ) == maxvalid ) {
1070+ maxvalid = ~(size_t )0 ;
1071+ jl_atomic_store_relaxed (& codeinst -> max_world , maxvalid );
1072+ }
10511073 if (external ) {
10521074 jl_value_t * owner = codeinst -> owner ;
10531075 JL_GC_PROMISE_ROOTED (owner );
10541076
10551077 // See #53586, #53109
10561078 assert (jl_atomic_load_relaxed (& codeinst -> inferred ));
10571079
1058- if (jl_rettype_inferred (owner , caller , minworld , maxvalid ) != jl_nothing ) {
1080+ if (jl_rettype_inferred (owner , caller , minvalid , maxvalid ) != jl_nothing ) {
10591081 // We already got a code instance for this world age range from somewhere else - we don't need
10601082 // this one.
10611083 }
0 commit comments