Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate CodeInstances with no external edges #41961

Merged
merged 2 commits into from
Aug 23, 2021

Conversation

timholy
Copy link
Member

@timholy timholy commented Aug 22, 2021

In #38983 and #41872, it was discovered that only CodeInstances with
external backedges get validated after deserialization.
This stores new CodeInstances in a list, and if they have neither been
validated nor invalidated, it validates them.

Closes #41872

CC @martinholters

@timholy timholy requested a review from vtjnash August 22, 2021 14:16
@timholy timholy added the compiler:precompilation Precompilation of modules label Aug 22, 2021
@vtjnash
Copy link
Member

vtjnash commented Aug 22, 2021

I think the serializer needs to store this information. Sometimes we will have methods without backedges because we know that they are invalid beforehand.

In #38983 and #41872, it was discovered that only CodeInstances with
external backedges got validated after deserialization.
This stores new CodeInstances in a list, and if they have neither been
validated nor invalidated, it validates them.

Closes #41872
@timholy timholy force-pushed the teh/validate_no_external_edges branch from c905725 to 2a47425 Compare August 23, 2021 11:42
Copy link
Member

@vtjnash vtjnash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this seems reasonable to me

@timholy timholy merged commit 0974dfa into master Aug 23, 2021
@timholy timholy deleted the teh/validate_no_external_edges branch August 23, 2021 19:29
@vtjnash
Copy link
Member

vtjnash commented Aug 25, 2021

hm, CI might disagree with it: https://build.julialang.org/#/builders/7/builds/2647

@timholy
Copy link
Member Author

timholy commented Aug 25, 2021

Oh dear. If we need to revert that's OK, but I'm looking into this.

@timholy
Copy link
Member Author

timholy commented Aug 25, 2021

Hmm, strange. It's rare, but if I run the test ~50 times I can get it. From debugging with a key diff

@@ -2083,10 +2089,17 @@ static void validate_new_code_instances(void)
     size_t i;
     for (i = 0; i < new_code_instance_validate.size; i += 2) {
         if (new_code_instance_validate.table[i+1] != HT_NOTFOUND) {
+         jl_printf(JL_STDOUT, "validating ");
+         jl_(((jl_code_instance_t*)new_code_instance_validate.table[i])->def);
             ((jl_code_instance_t*)new_code_instance_validate.table[i])->max_world = ~(size_t)0;
             new_code_instance_validate.table[i+1] = HT_NOTFOUND;
-        }
+        } else if (new_code_instance_validate.table[i] != HT_NOTFOUND) {
+         jl_printf(JL_STDOUT, "for invalid ci, max_world is %ld: ", ((jl_code_instance_t*)new_code_instance_validate.table[i])->max_world);
+         jl_(((jl_code_instance_t*)new_code_instance_validate.table[i])->def);
+       }
     }

I captured

invalidate: g() from g()
validating (::Type{Foo4b3a94a1a081a8cb.MyType{:sym}})() from (::Type{Foo4b3a94a1a081a8cb.MyType{T}})() where {T}
validating (::Type{Foo4b3a94a1a081a8cb.Result})(Base.Missing) from (::Type{Foo4b3a94a1a081a8cb.Result})(Union{Base.Missing, Int64})
for invalid ci, max_world is -1: call_bottom() from call_bottom()
for invalid ci, max_world is 0: g() from g()
validating g() from g()
precompile (1) |         failed at 2021-08-25T15:53:40.961
Test Failed at /home/tim/src/julia-branch/test/precompile.jl:218
  Expression: Foo.g() === 97.0
   Evaluated: 2.0 === 97.0


Test Summary: | Pass  Fail  Total
  Overall     |    7     1      8
    precompile |    7     1      8
    FAILURE

It makes it seem as if it could be in the htable twice. Is that possible? Do I have to do something to ensure the validity of the table?

@vtjnash
Copy link
Member

vtjnash commented Aug 25, 2021

Seems like a case you can quickly catch under rr, then be able to quickly pinpoint when each of those two got added to the cache

@timholy
Copy link
Member Author

timholy commented Aug 25, 2021

I am guessing this will fix it:

diff --git a/src/dump.c b/src/dump.c
index af3290a511..0cd8513426 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -2080,12 +2080,20 @@ static void jl_insert_backedges(jl_array_t *list, jl_array_t *targets)
 
 static void validate_new_code_instances(void)
 {
-    size_t i;
-    for (i = 0; i < new_code_instance_validate.size; i += 2) {
+    size_t i = 0;
+    while (i < new_code_instance_validate.size) {
         if (new_code_instance_validate.table[i+1] != HT_NOTFOUND) {
             ((jl_code_instance_t*)new_code_instance_validate.table[i])->max_world = ~(size_t)0;
             new_code_instance_validate.table[i+1] = HT_NOTFOUND;
-        }
+        } else {
+           // The same key can be inserted in multiple places
+           void *p = new_code_instance_validate.table[i];
+            while (i + 2 < new_code_instance_validate.size && new_code_instance_validate.table[i+2] == p) {
+                i += 2;
+                new_code_instance_validate.table[i+1] = HT_NOTFOUND;
+            }
+       }
+       i += 2;
     }
 }

The same pointer is definitely inserted twice in a row (rarely).

@timholy
Copy link
Member Author

timholy commented Aug 25, 2021

Or would it be better to call ptrhash_get instead of relying on the i+1 value?

timholy added a commit that referenced this pull request Aug 25, 2021
The hash-table iteration scheme used in #41961 is flawed because
the same key (pointer) can appear in (at least) successive
key-slots. For safety, it's best to get the "official" value
assigned to this key before making decisions about validation.
@timholy
Copy link
Member Author

timholy commented Aug 25, 2021

See #42008

LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Feb 22, 2022
In JuliaLang#38983 and JuliaLang#41872, it was discovered that only CodeInstances with
external backedges get validated after deserialization.
This adds a "second chance" for each CodeInstance: it validates any that
have neither been validated nor invalidated by the end of deserialization.

Closes JuliaLang#41872
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Mar 8, 2022
In JuliaLang#38983 and JuliaLang#41872, it was discovered that only CodeInstances with
external backedges get validated after deserialization.
This adds a "second chance" for each CodeInstance: it validates any that
have neither been validated nor invalidated by the end of deserialization.

Closes JuliaLang#41872
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:precompilation Precompilation of modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants