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

Fix serialization of code instances. #46373

Merged
merged 1 commit into from
Sep 27, 2022
Merged

Conversation

maleadt
Copy link
Member

@maleadt maleadt commented Aug 17, 2022

If a code instance is encountered in a Julia object directly, as opposed to e.g. during serialization of a method instance, we'll already have populated the backref table at the start of jl_serialize_value. That means jl_serialize_code_instance can't just bail out, even though I presume that's still useful for nested calls. It would be cleaner to remove the check and call jl_serialize_value for nested code instances, but then the undocumented internal flag gets lost, so cc @Keno @timholy for thoughts on this.

Fixes #46296
Introduced by #39512, so we could backport this all the way back to 1.7 if we want.

@maleadt maleadt added compiler:precompilation Precompilation of modules bugfix This change fixes an existing bug backport 1.7 backport 1.8 Change should be backported to release-1.8 labels Aug 17, 2022
@maleadt maleadt requested a review from Keno August 17, 2022 08:42
Copy link
Member

@timholy timholy left a comment

Choose a reason for hiding this comment

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

This seems like a valid idea, but I'm wondering if we need to be more general about tracking how we got to jl_serialize_code_instance. I've not seen these before so I'm not sure whether MethodInstances could also be part of Julia objects, but presumably a package like

module MyPackage
const some_mis = [first(methods(sum)).specializations[1]]
end

would serve as a test case.

@@ -678,7 +680,7 @@ static void jl_serialize_code_instance(jl_serializer_state *s, jl_code_instance_
if (write_ret_type && codeinst->rettype_const &&
jl_typeis(codeinst->rettype_const, jl_partial_opaque_type)) {
if (skip_partial_opaque) {
jl_serialize_code_instance(s, codeinst->next, skip_partial_opaque, internal);
jl_serialize_code_instance(s, codeinst->next, skip_partial_opaque, internal, 0);
Copy link
Member

Choose a reason for hiding this comment

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

Why not pass force here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm just preserving how this function worked before. Presumably there was a reason that jl_serialize_code_instance didn't serialize CIs with backref entries on recursive calls to jl_serialize_code_instance, so I'm just preserving that behavior. Instead, I'm only forcing the CIs to be serialized when I know that the backref table initialization doesn't mean that the object has been serialized, i.e., from the jl_is_code_instance branch in jl_serialize_value.

@@ -962,10 +964,10 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li
}
jl_serialize_value(s, (jl_value_t*)backedges);
jl_serialize_value(s, (jl_value_t*)NULL); //callbacks
jl_serialize_code_instance(s, mi->cache, 1, internal);
jl_serialize_code_instance(s, mi->cache, 1, internal, 0);
Copy link
Member

Choose a reason for hiding this comment

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

What if a MethodInstance is a code-object? Would you want to serialize the CodeInstances that it points to? If so...we might have to keep track at a higher level of how we got here: if by traversing a module, then don't force, but if we're caching code, then force.

Copy link
Member Author

Choose a reason for hiding this comment

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

Not setting force=0 doesn't mean that the CI won't be serialized, it only means that it can't have a backref table entry. Since we're calling jl_serialize_code_instance here on a field that wasn't the direct input to jl_serialize_value (which populates the backref table), IIUC that means we will either serialize it for the first time, or the backref table will have an entry point to a successfully serialized CI.

And this does indeed seem to work as expected. With your example above, I can successfully precompile the array of MIs and inspect the CI in the cache:

module CodeInstancePrecompile

const some_mis = [first(methods(identity)).specializations[1]]

__init__() = @show some_mis[].cache

end
❯ JULIA_LOAD_PATH=. jl --startup-file=no -e 'using CodeInstancePrecompile'
(some_mis[]).cache = Core.CodeInstance(MethodInstance for identity(::Base.BottomRF{typeof(Base.add_sum)}), #undef, 0x0000000000000001, 0xffffffffffffffff, Base.BottomRF{typeof(Base.add_sum)}, Base.BottomRF{typeof(Base.add_sum)}(Base.add_sum), nothing, 0x00000155, 0x00000155, nothing, false, false, Ptr{Nothing} @0x00000001034d3a08, Ptr{Nothing} @0x0000000000000000, 0x00)

@KristofferC KristofferC mentioned this pull request Sep 7, 2022
28 tasks
test/precompile.jl Outdated Show resolved Hide resolved
@maleadt maleadt force-pushed the tb/precompile_codeinstance branch from 57ede8f to 0930304 Compare September 15, 2022 08:18
If they are encountered in a Julia object directly, as opposed to
e.g. during serialization of a method instance, we'll have first
populated the backref table as part of jl_serialize_value, so we
need to skip that check during code instance serialization.
@maleadt maleadt force-pushed the tb/precompile_codeinstance branch from 0930304 to 31c0187 Compare September 15, 2022 08:31
@vtjnash
Copy link
Member

vtjnash commented Sep 15, 2022

It is a confusing bit of code needing to track if we came via jl_serialize_generic already or not, but this PR seems right

@vtjnash vtjnash merged commit ff4f86d into master Sep 27, 2022
@vtjnash vtjnash deleted the tb/precompile_codeinstance branch September 27, 2022 13:47
DilumAluthge added a commit that referenced this pull request Sep 28, 2022
vtjnash pushed a commit that referenced this pull request Sep 28, 2022
@vtjnash
Copy link
Member

vtjnash commented Sep 28, 2022

ERROR: LoadError: MethodError: no method matching Core.CodeInstance(::Core.MethodInstance, ::Type{Any}, ::Nothing, ::Nothing, ::Int32, ::UInt64, ::UInt64, ::UInt32, ::UInt32, ::Nothing, ::UInt8)
Closest candidates are:
  Core.CodeInstance(::Core.MethodInstance, ::Any, ::Any, ::Any, ::Int32, ::UInt32, ::UInt32, ::UInt32, ::UInt32, ::Any, ::UInt8)
   @ Core boot.jl:423
  Core.CodeInstance(::Core.Compiler.InferenceResult, ::Any, ::Core.Compiler.WorldRange)
   @ Core compiler/typeinfer.jl:286

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix This change fixes an existing bug compiler:precompilation Precompilation of modules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Assertion/hang precompiling code that calls inference with custom AbstractInterpreter
4 participants