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

pkgimages: jl_restore_system_image_from_stream doesn't recognize externally-created CodeInstance #48453

Closed
aviatesk opened this issue Jan 30, 2023 · 4 comments
Labels
bug Indicates an unexpected problem or unintended behavior pkgimage

Comments

@aviatesk
Copy link
Member

I found that pkgimage handles an externally created CodeInstance improperly and may end up creating invalid code cache.
MRE is

  1. create a package that is defined as follows:
module Reproduce

const codedict = IdDict{Core.MethodInstance, Core.CodeInstance}()

struct MyCode end

let method = only(methods(identity, (Nothing,)))
    mi = Core.Compiler.specialize_method(method, Tuple{typeof(identity),Nothing}, Core.svec())
    min_world = Base.get_world_counter()
    max_world = typemax(UInt)
    effects = Core.Compiler.encode_effects(Core.Compiler.Effects())
    ext_ci = Core.CodeInstance(mi, Nothing, nothing, MyCode(), Int32(0), min_world, max_world,
        effects, effects, nothing, UInt8(0))
    codedict[mi] = ext_ci
end

end # module Reproduce
  1. precompile it and using it
julia> using Reproduce
[ Info: Precompiling Reproduce [1bfd98f3-51f7-41c1-ad60-a236b7d4a612]
  1. Now, mi (MethodInstance for idetity(nothing)) contains the ext_ci that was created by the Reproduce package in its cache field. Since the inferred code of the ext_ci is the custom object MyCode that is not recognized by our system, it will cause an unexpected error. For example, if we enable assertions, we will see a segfault:
julia> let mis = only(methods(identity, (Nothing,))).specializations
           for mi = mis
               mi isa Core.MethodInstance || continue
               if mi.specTypes === Tuple{typeof(identity), Nothing}
                   println("found")
                   global gmi = mi
               end
           end
       end
found

julia> gmi.cache # this is very problematic
Core.CodeInstance(MethodInstance for identity(::Nothing), #undef, 0x0000000000008357, 0xffffffffffffffff, Nothing, #undef, Reproduce.MyCode(), 0x00000909, 0x00000909, nothing, false, false, 0x00, Ptr{Nothing} @0x0000000000000000, Ptr{Nothing} @0x0000000000000000)

# hits this assertion: https://github.com/JuliaLang/julia/blob/6ab660dc70b3de98a988feb5896ae95c28f9a2f9/src/ircode.c#L945
julia> let 
           Base.Experimental.@force_compile
           identity(nothing)
       end
Assertion failed: (jl_typeis(data, jl_array_uint8_type)), function ijl_ir_flag_inferred, file [...]/julia/src/ircode.c, line 945.

[67438] signal (6): Abort trap: 6
in expression starting at REPL[6]:1
__pthread_kill at /usr/lib/system/libsystem_kernel.dylib (unknown line)
pthread_kill at /usr/lib/system/libsystem_pthread.dylib (unknown line)
abort at /usr/lib/system/libsystem_c.dylib (unknown line)
__assert_rtn at /usr/lib/system/libsystem_c.dylib (unknown line)
ijl_ir_flag_inferred.cold.1 at [...]/julia/src/ircode.c:945
ijl_ir_flag_inferred at [...]/julia/src/ircode.c:945
ijl_rettype_inferred at [...]/julia/src/gf.c:367
get at ./compiler/cicache.jl:56 [inlined]
typeinf_edge at ./compiler/typeinfer.jl:867
abstract_call_method at ./compiler/abstractinterpretation.jl:657
abstract_call_gf_by_type at ./compiler/abstractinterpretation.jl:154
abstract_call_known at ./compiler/abstractinterpretation.jl:2060
abstract_call at ./compiler/abstractinterpretation.jl:2136
abstract_call at ./compiler/abstractinterpretation.jl:2110
abstract_eval_statement_expr at ./compiler/abstractinterpretation.jl:2282
abstract_eval_statement at ./compiler/abstractinterpretation.jl:2534
abstract_eval_basic_statement at ./compiler/abstractinterpretation.jl:2825
typeinf_local at ./compiler/abstractinterpretation.jl:3001
typeinf_nocycle at ./compiler/abstractinterpretation.jl:3089
_typeinf at ./compiler/typeinfer.jl:244
typeinf at ./compiler/typeinfer.jl:215
typeinf_ext_toplevel at ./compiler/typeinfer.jl:1092
typeinf_ext_toplevel at ./compiler/typeinfer.jl:1079
jfptr_typeinf_ext_toplevel_20167 at [...]/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at [...]/julia/src/gf.c:2675
ijl_apply_generic at [...]/julia/src/gf.c:2876
jl_apply at [...]/julia/src/./julia.h:1880 [inlined]
jl_type_infer at [...]/julia/src/gf.c:317
jl_toplevel_eval_flex at [...]/julia/src/toplevel.c:899
jl_toplevel_eval_flex at [...]/julia/src/toplevel.c:853
ijl_toplevel_eval at [...]/julia/src/toplevel.c:919 [inlined]
ijl_toplevel_eval_in at [...]/julia/src/toplevel.c:969
eval at ./boot.jl:370 [inlined]
eval_user_input at [...]/julia/usr/share/julia/stdlib/v1.10/REPL/src/REPL.jl:153
repl_backend_loop at [...]/julia/usr/share/julia/stdlib/v1.10/REPL/src/REPL.jl:249
#start_repl_backend#46 at [...]/julia/usr/share/julia/stdlib/v1.10/REPL/src/REPL.jl:234
kwcall at [...]/julia/usr/share/julia/stdlib/v1.10/REPL/src/REPL.jl:231
_jl_invoke at [...]/julia/src/gf.c:2675
ijl_apply_generic at [...]/julia/src/gf.c:2876
#run_repl#59 at [...]/julia/usr/share/julia/stdlib/v1.10/REPL/src/REPL.jl:377
run_repl at [...]/julia/usr/share/julia/stdlib/v1.10/REPL/src/REPL.jl:363
jfptr_run_repl_60931 at [...]/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at [...]/julia/src/gf.c:2675
ijl_apply_generic at [...]/julia/src/gf.c:2876
#1019 at ./client.jl:421
jfptr_YY.1019_55802 at [...]/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at [...]/julia/src/gf.c:2675
ijl_apply_generic at [...]/julia/src/gf.c:2876
jl_apply at [...]/julia/src/./julia.h:1880 [inlined]
jl_f__call_latest at [...]/julia/src/builtins.c:778
#invokelatest#2 at ./essentials.jl:823 [inlined]
invokelatest at ./essentials.jl:820 [inlined]
run_main_repl at ./client.jl:405
exec_options at ./client.jl:322
_start at ./client.jl:522
jfptr__start_55808 at [...]/julia/usr/lib/julia/sys.dylib (unknown line)
_jl_invoke at [...]/julia/src/gf.c:2675
ijl_apply_generic at [...]/julia/src/gf.c:2876
jl_apply at [...]/julia/src/./julia.h:1880 [inlined]
true_main at [...]/julia/src/jlapi.c:573
jl_repl_entrypoint at [...]/julia/src/jlapi.c:717
Allocations: 2997344 (Pool: 2995576; Big: 1768); GC: 3
[1]    67438 abort      ~/julia/julia/usr/bin/julia

Since ext_ci is not a CodeInstance that is created by the native compilation system, it shouldn't be cached as usual.
I found ext_ci is cached with mi by jl_restore_system_image_from_stream_:

ci->def->cache = ci;

This bug causes a serious problem when precompiling packages like Cthulhu or JET where they create external CodeInstances that has their own data structure within their inferred.

@aviatesk aviatesk added bug Indicates an unexpected problem or unintended behavior pkgimage labels Jan 30, 2023
@aviatesk
Copy link
Member Author

It seems to cause a segmentation fault to precompile Reproduce on 1.9.0-beta3, so let me put this into 1.9 milestones.

@aviatesk aviatesk added this to the 1.9 milestone Jan 30, 2023
@vtjnash vtjnash removed this from the 1.9 milestone Jan 30, 2023
@vtjnash
Copy link
Member

vtjnash commented Jan 30, 2023

I believe this would be a new feature, not a bugfix request, since you are previously not allowed to create these objects outside of the cache. But eligible for backporting if it is fixed in time for the release. There is some comments in the staticdata code around there expressing ways that the user might break this.

aviatesk added a commit that referenced this issue Mar 31, 2023
This PR generalizes the idea from #49199 and uses inference to analyze
the types of REPL expression. This approach offers several advantages
over the current `get_[value|type]`-based implementation:
- The need for various special cases is eliminated, as lowering normalizes
  expressions, and inference handles all language features.
- Constant propagation allows us to obtain accurate completions for complex
  expressions safely (see #36437).

Analysis on arbitrary REPL expressions can be done by the following steps:
- Lower a given expression
- Form a top-level `MethodInstance` from the lowered expression
- Run inference on the top-level `MethodInstance`

This PR implements `REPLInterpreter`, a custom `AbstractInterpreter` that:
- aggressively resolve global bindings to enable reasonable completions
  for lines like `Mod.a.|` (where `|` is the cursor position)
- does not optimize the inferred code, as `REPLInterpreter` is only used
  to obtain the type or constant information of given expressions

In particular, aggressive binding resolution presents challenges for
`REPLInterpreter`'s cache validation (since #40399 hasn't been resolved
yet). To avoid cache validation issue, `REPLInterpreter` only allows
aggressive binding resolution for top-level frame representing REPL
input code (`repl_frame`) and for child `getproperty` frames that are
constant propagated from the `repl_frame`. This works, since
1.) these frames are never cached, and
2.) their results are only observed by the non-cached `repl_frame`

Note that the code cache for `REPLInterpreter` is separated from the
native code cache, ensuring that code caches produced by `REPLInterpreter`,
where bindings are aggressively resolved and the code is not really
optimized, do not affect the native code execution. A hack has
also been added to avoid serializing `CodeInstances`s produced by
`REPLInterpreter` during precompilation to workaround #48453.

closes #36437
replaces #49199
aviatesk added a commit that referenced this issue Apr 1, 2023
This PR generalizes the idea from #49199 and uses inference to analyze
the types of REPL expression. This approach offers several advantages
over the current `get_[value|type]`-based implementation:
- The need for various special cases is eliminated, as lowering normalizes
  expressions, and inference handles all language features.
- Constant propagation allows us to obtain accurate completions for complex
  expressions safely (see #36437).

Analysis on arbitrary REPL expressions can be done by the following steps:
- Lower a given expression
- Form a top-level `MethodInstance` from the lowered expression
- Run inference on the top-level `MethodInstance`

This PR implements `REPLInterpreter`, a custom `AbstractInterpreter` that:
- aggressively resolve global bindings to enable reasonable completions
  for lines like `Mod.a.|` (where `|` is the cursor position)
- aggressively concrete evaluates `:inconsistent` calls to provide
  reasonable completions for cases like `Ref(Some(42))[].|`
- does not optimize the inferred code, as `REPLInterpreter` is only used
  to obtain the type or constant information of given expressions

Aggressive binding resolution presents challenges for `REPLInterpreter`'s
cache validation (since #40399 hasn't been resolved yet). To avoid cache
validation issue, `REPLInterpreter` only allows aggressive binding
resolution for top-level frame representing REPL input code
(`repl_frame`) and for child `getproperty` frames that are
constant propagated from the `repl_frame`. This works, since
1.) these frames are never cached, and
2.) their results are only observed by the non-cached `repl_frame`

`REPLInterpreter` also aggressively concrete evaluate `:inconsistent`
calls within `repl_frame`, allowing it to get get accurate type
information about complex expressions that otherwise can not be constant
folded, in a safe way, i.e. it still doesn't evaluate effectful
expressions like `pop!(xs)`. Similarly to the aggressive binding
resolution, aggressive concrete evaluation doesn't present any cache
validation issues because `repl_frame` is never cached.

Also note that the code cache for `REPLInterpreter` is separated from the
native code cache, ensuring that code caches produced by `REPLInterpreter`,
where bindings are aggressively resolved and the code is not really
optimized, do not affect the native code execution. A hack has
also been added to avoid serializing `CodeInstances`s produced by
`REPLInterpreter` during precompilation to workaround #48453.

closes #36437
replaces #49199
aviatesk added a commit that referenced this issue Apr 2, 2023
This PR generalizes the idea from #49199 and uses inference to analyze
the types of REPL expression. This approach offers several advantages
over the current `get_[value|type]`-based implementation:
- The need for various special cases is eliminated, as lowering normalizes
  expressions, and inference handles all language features.
- Constant propagation allows us to obtain accurate completions for complex
  expressions safely (see #36437).

Analysis on arbitrary REPL expressions can be done by the following steps:
- Lower a given expression
- Form a top-level `MethodInstance` from the lowered expression
- Run inference on the top-level `MethodInstance`

This PR implements `REPLInterpreter`, a custom `AbstractInterpreter` that:
- aggressively resolve global bindings to enable reasonable completions
  for lines like `Mod.a.|` (where `|` is the cursor position)
- aggressively concrete evaluates `:inconsistent` calls to provide
  reasonable completions for cases like `Ref(Some(42))[].|`
- does not optimize the inferred code, as `REPLInterpreter` is only used
  to obtain the type or constant information of given expressions

Aggressive binding resolution presents challenges for `REPLInterpreter`'s
cache validation (since #40399 hasn't been resolved yet). To avoid cache
validation issue, `REPLInterpreter` only allows aggressive binding
resolution for top-level frame representing REPL input code
(`repl_frame`) and for child `getproperty` frames that are
constant propagated from the `repl_frame`. This works, since
1.) these frames are never cached, and
2.) their results are only observed by the non-cached `repl_frame`

`REPLInterpreter` also aggressively concrete evaluate `:inconsistent`
calls within `repl_frame`, allowing it to get get accurate type
information about complex expressions that otherwise can not be constant
folded, in a safe way, i.e. it still doesn't evaluate effectful
expressions like `pop!(xs)`. Similarly to the aggressive binding
resolution, aggressive concrete evaluation doesn't present any cache
validation issues because `repl_frame` is never cached.

Also note that the code cache for `REPLInterpreter` is separated from the
native code cache, ensuring that code caches produced by `REPLInterpreter`,
where bindings are aggressively resolved and the code is not really
optimized, do not affect the native code execution. A hack has
also been added to avoid serializing `CodeInstances`s produced by
`REPLInterpreter` during precompilation to workaround #48453.

closes #36437
replaces #49199
aviatesk added a commit that referenced this issue Apr 2, 2023
This PR generalizes the idea from #49199 and uses inference to analyze
the types of REPL expression. This approach offers several advantages
over the current `get_[value|type]`-based implementation:
- The need for various special cases is eliminated, as lowering normalizes
  expressions, and inference handles all language features.
- Constant propagation allows us to obtain accurate completions for complex
  expressions safely (see #36437).

Analysis on arbitrary REPL expressions can be done by the following steps:
- Lower a given expression
- Form a top-level `MethodInstance` from the lowered expression
- Run inference on the top-level `MethodInstance`

This PR implements `REPLInterpreter`, a custom `AbstractInterpreter` that:
- aggressively resolve global bindings to enable reasonable completions
  for lines like `Mod.a.|` (where `|` is the cursor position)
- aggressively concrete evaluates `:inconsistent` calls to provide
  reasonable completions for cases like `Ref(Some(42))[].|`
- does not optimize the inferred code, as `REPLInterpreter` is only used
  to obtain the type or constant information of given expressions

Aggressive binding resolution presents challenges for `REPLInterpreter`'s
cache validation (since #40399 hasn't been resolved yet). To avoid cache
validation issue, `REPLInterpreter` only allows aggressive binding
resolution for top-level frame representing REPL input code
(`repl_frame`) and for child `getproperty` frames that are
constant propagated from the `repl_frame`. This works, since
1.) these frames are never cached, and
2.) their results are only observed by the non-cached `repl_frame`

`REPLInterpreter` also aggressively concrete evaluate `:inconsistent`
calls within `repl_frame`, allowing it to get get accurate type
information about complex expressions that otherwise can not be constant
folded, in a safe way, i.e. it still doesn't evaluate effectful
expressions like `pop!(xs)`. Similarly to the aggressive binding
resolution, aggressive concrete evaluation doesn't present any cache
validation issues because `repl_frame` is never cached.

Also note that the code cache for `REPLInterpreter` is separated from the
native code cache, ensuring that code caches produced by `REPLInterpreter`,
where bindings are aggressively resolved and the code is not really
optimized, do not affect the native code execution. A hack has
also been added to avoid serializing `CodeInstances`s produced by
`REPLInterpreter` during precompilation to workaround #48453.

closes #36437
replaces #49199
oscardssmith pushed a commit that referenced this issue Apr 3, 2023
This PR generalizes the idea from #49199 and uses inference to analyze
the types of REPL expression. This approach offers several advantages
over the current `get_[value|type]`-based implementation:
- The need for various special cases is eliminated, as lowering normalizes
  expressions, and inference handles all language features.
- Constant propagation allows us to obtain accurate completions for complex
  expressions safely (see #36437).

Analysis on arbitrary REPL expressions can be done by the following steps:
- Lower a given expression
- Form a top-level `MethodInstance` from the lowered expression
- Run inference on the top-level `MethodInstance`

This PR implements `REPLInterpreter`, a custom `AbstractInterpreter` that:
- aggressively resolve global bindings to enable reasonable completions
  for lines like `Mod.a.|` (where `|` is the cursor position)
- aggressively concrete evaluates `:inconsistent` calls to provide
  reasonable completions for cases like `Ref(Some(42))[].|`
- does not optimize the inferred code, as `REPLInterpreter` is only used
  to obtain the type or constant information of given expressions

Aggressive binding resolution presents challenges for `REPLInterpreter`'s
cache validation (since #40399 hasn't been resolved yet). To avoid cache
validation issue, `REPLInterpreter` only allows aggressive binding
resolution for top-level frame representing REPL input code
(`repl_frame`) and for child `getproperty` frames that are
constant propagated from the `repl_frame`. This works, since
1.) these frames are never cached, and
2.) their results are only observed by the non-cached `repl_frame`

`REPLInterpreter` also aggressively concrete evaluate `:inconsistent`
calls within `repl_frame`, allowing it to get get accurate type
information about complex expressions that otherwise can not be constant
folded, in a safe way, i.e. it still doesn't evaluate effectful
expressions like `pop!(xs)`. Similarly to the aggressive binding
resolution, aggressive concrete evaluation doesn't present any cache
validation issues because `repl_frame` is never cached.

Also note that the code cache for `REPLInterpreter` is separated from the
native code cache, ensuring that code caches produced by `REPLInterpreter`,
where bindings are aggressively resolved and the code is not really
optimized, do not affect the native code execution. A hack has
also been added to avoid serializing `CodeInstances`s produced by
`REPLInterpreter` during precompilation to workaround #48453.

closes #36437
replaces #49199
Xnartharax pushed a commit to Xnartharax/julia that referenced this issue Apr 19, 2023
…g#49206)

This PR generalizes the idea from JuliaLang#49199 and uses inference to analyze
the types of REPL expression. This approach offers several advantages
over the current `get_[value|type]`-based implementation:
- The need for various special cases is eliminated, as lowering normalizes
  expressions, and inference handles all language features.
- Constant propagation allows us to obtain accurate completions for complex
  expressions safely (see JuliaLang#36437).

Analysis on arbitrary REPL expressions can be done by the following steps:
- Lower a given expression
- Form a top-level `MethodInstance` from the lowered expression
- Run inference on the top-level `MethodInstance`

This PR implements `REPLInterpreter`, a custom `AbstractInterpreter` that:
- aggressively resolve global bindings to enable reasonable completions
  for lines like `Mod.a.|` (where `|` is the cursor position)
- aggressively concrete evaluates `:inconsistent` calls to provide
  reasonable completions for cases like `Ref(Some(42))[].|`
- does not optimize the inferred code, as `REPLInterpreter` is only used
  to obtain the type or constant information of given expressions

Aggressive binding resolution presents challenges for `REPLInterpreter`'s
cache validation (since JuliaLang#40399 hasn't been resolved yet). To avoid cache
validation issue, `REPLInterpreter` only allows aggressive binding
resolution for top-level frame representing REPL input code
(`repl_frame`) and for child `getproperty` frames that are
constant propagated from the `repl_frame`. This works, since
1.) these frames are never cached, and
2.) their results are only observed by the non-cached `repl_frame`

`REPLInterpreter` also aggressively concrete evaluate `:inconsistent`
calls within `repl_frame`, allowing it to get get accurate type
information about complex expressions that otherwise can not be constant
folded, in a safe way, i.e. it still doesn't evaluate effectful
expressions like `pop!(xs)`. Similarly to the aggressive binding
resolution, aggressive concrete evaluation doesn't present any cache
validation issues because `repl_frame` is never cached.

Also note that the code cache for `REPLInterpreter` is separated from the
native code cache, ensuring that code caches produced by `REPLInterpreter`,
where bindings are aggressively resolved and the code is not really
optimized, do not affect the native code execution. A hack has
also been added to avoid serializing `CodeInstances`s produced by
`REPLInterpreter` during precompilation to workaround JuliaLang#48453.

closes JuliaLang#36437
replaces JuliaLang#49199
@vchuravy
Copy link
Member

I think this is still an overarching issue?

@Keno
Copy link
Member

Keno commented Jan 12, 2024

Fixed by #52852

@Keno Keno closed this as completed Jan 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Indicates an unexpected problem or unintended behavior pkgimage
Projects
None yet
Development

No branches or pull requests

4 participants