JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.44k stars 5.46k forks source link

OpaqueClosure constructor does not survive pre-compilation #55073

Open topolarity opened 2 months ago

topolarity commented 2 months ago
module Foo

using Base.Experimental: @opaque

some_method(x) = 2x
make_oc() = @opaque (x::Int)->some_method(x)

precompile(make_oc, ())

end # module Foo

When using this, I am surprised to see that make_oc() was not pre-compiled after all (or at least is not usable as-is):

julia> using SnoopCompileCore, Foo
julia> tinf = @snoop_inference Foo.make_oc();
julia> only(tinf.children).mi_timing.mi_info.mi
MethodInstance for Foo.make_oc()
topolarity commented 2 months ago

Looks like this might be related to the compile-time OpaqueClosure optimization (https://github.com/JuliaLang/julia/issues/55035 is probably related)

If we make sure that optimization doesn't apply:

make_oc2(x) = begin
    x = Base.inferencebarrier(true) ? x : [] # non-concrete closure environment prevents optimization
    return Base.Experimental.@opaque ()->some_method(x)
end
precompile(make_oc2, (Int,))

Then the constructor itself is fine - we only see inference for the OpaqueClosure method itself:

julia> tinf = @snoop_inference Foo.make_oc2(1)
julia> only(tinf.children).mi_timing.mi_info.mi
MethodInstance for (::Tuple{Int64})()

That inference is expected since it wasn't included in the precompile (it only ends up inferred/pre-compiled if the compile-time optimization applies).

Maybe we're messing up our caches when performing the compile-time optimization, in a way that breaks pre-compilation?