From a86daa6b7c4f059a58bcdd67edd7a0b164b0401c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Wed, 5 Feb 2025 09:13:57 -0500 Subject: [PATCH 1/6] Add CodeInstances to JIT for interpreters defining a codegen cache --- .../CompilerDevTools/src/CompilerDevTools.jl | 5 +- Compiler/src/typeinfer.jl | 81 +++++++++++-------- Compiler/src/types.jl | 18 +++++ Compiler/test/AbstractInterpreter.jl | 15 ++++ 4 files changed, 83 insertions(+), 36 deletions(-) diff --git a/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl b/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl index 5d0df5ccaa4e4..46612b5e772e9 100644 --- a/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl +++ b/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl @@ -9,12 +9,13 @@ struct SplitCacheInterp <: Compiler.AbstractInterpreter inf_params::Compiler.InferenceParams opt_params::Compiler.OptimizationParams inf_cache::Vector{Compiler.InferenceResult} + codegen_cache::IdDict{CodeInstance,CodeInfo} function SplitCacheInterp(; world::UInt = Base.get_world_counter(), inf_params::Compiler.InferenceParams = Compiler.InferenceParams(), opt_params::Compiler.OptimizationParams = Compiler.OptimizationParams(), inf_cache::Vector{Compiler.InferenceResult} = Compiler.InferenceResult[]) - new(world, inf_params, opt_params, inf_cache) + new(world, inf_params, opt_params, inf_cache, IdDict{CodeInstance,CodeInfo}()) end end @@ -23,6 +24,7 @@ Compiler.OptimizationParams(interp::SplitCacheInterp) = interp.opt_params Compiler.get_inference_world(interp::SplitCacheInterp) = interp.world Compiler.get_inference_cache(interp::SplitCacheInterp) = interp.inf_cache Compiler.cache_owner(::SplitCacheInterp) = SplitCacheOwner() +Compiler.codegen_cache(interp::SplitCacheInterp) = interp.codegen_cache import Core.OptimizedGenerics.CompilerPlugins: typeinf, typeinf_edge @eval @noinline typeinf(::SplitCacheOwner, mi::MethodInstance, source_mode::UInt8) = @@ -40,6 +42,7 @@ function with_new_compiler(f, args...) new_compiler_ci = Core.OptimizedGenerics.CompilerPlugins.typeinf( SplitCacheOwner(), mi, Compiler.SOURCE_MODE_ABI ) + Compiler.add_codeinsts_to_jit!(SplitCacheInterp(; world), new_compiler_ci, Compiler.SOURCE_MODE_ABI) invoke(f, new_compiler_ci, args...) end diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 96f58943e3255..e1565bf67de27 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -147,9 +147,10 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState) ci, inferred_result, const_flag, first(result.valid_worlds), last(result.valid_worlds), encode_effects(result.ipo_effects), result.analysis_results, di, edges) engine_reject(interp, ci) - if !discard_src && isdefined(interp, :codegen) && uncompressed isa CodeInfo + codegen = codegen_cache(interp) + if !discard_src && codegen !== nothing && uncompressed isa CodeInfo # record that the caller could use this result to generate code when required, if desired, to avoid repeating n^2 work - interp.codegen[ci] = uncompressed + codegen[ci] = uncompressed if bootstrapping_compiler && inferred_result == nothing # This is necessary to get decent bootstrapping performance # when compiling the compiler to inject everything eagerly @@ -189,8 +190,9 @@ function finish!(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInstan ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any), ci, nothing, const_flag, min_world, max_world, ipo_effects, nothing, di, edges) code_cache(interp)[mi] = ci - if isdefined(interp, :codegen) - interp.codegen[ci] = src + codegen = codegen_cache(interp) + if codegen !== nothing + codegen[ci] = src end engine_reject(interp, ci) return nothing @@ -1156,7 +1158,10 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod ci = result.ci # reload from result in case it changed @assert frame.cache_mode != CACHE_MODE_NULL - @assert is_result_constabi_eligible(result) || (!isdefined(interp, :codegen) || haskey(interp.codegen, ci)) + @assert is_result_constabi_eligible(result) || begin + codegen = codegen_cache(interp) + codegen === nothing || haskey(codegen, ci) + end @assert is_result_constabi_eligible(result) == use_const_api(ci) @assert isdefined(ci, :inferred) "interpreter did not fulfill our expectations" if !is_cached(frame) && source_mode == SOURCE_MODE_ABI @@ -1222,41 +1227,47 @@ function collectinvokes!(wq::Vector{CodeInstance}, ci::CodeInfo) end end -# This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match -function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8) - interp = NativeInterpreter(world) - ci = typeinf_ext(interp, mi, source_mode) - if source_mode == SOURCE_MODE_ABI && ci isa CodeInstance && !ci_has_invoke(ci) - inspected = IdSet{CodeInstance}() - tocompile = Vector{CodeInstance}() - push!(tocompile, ci) - while !isempty(tocompile) - # ci_has_real_invoke(ci) && return ci # optimization: cease looping if ci happens to get compiled (not just jl_fptr_wait_for_compiled, but fully jl_is_compiled_codeinst) - callee = pop!(tocompile) - ci_has_invoke(callee) && continue - callee in inspected && continue - src = get(interp.codegen, callee, nothing) +function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UInt8) + source_mode == SOURCE_MODE_ABI || return + ci isa CodeInstance && !ci_has_invoke(ci) || return + codegen = codegen_cache(interp) + codegen !== nothing || return + inspected = IdSet{CodeInstance}() + tocompile = Vector{CodeInstance}() + push!(tocompile, ci) + while !isempty(tocompile) + # ci_has_real_invoke(ci) && return ci # optimization: cease looping if ci happens to get compiled (not just jl_fptr_wait_for_compiled, but fully jl_is_compiled_codeinst) + callee = pop!(tocompile) + ci_has_invoke(callee) && continue + callee in inspected && continue + src = get(codegen, callee, nothing) + if !isa(src, CodeInfo) + src = @atomic :monotonic callee.inferred + if isa(src, String) + src = _uncompressed_ir(callee, src) + end if !isa(src, CodeInfo) - src = @atomic :monotonic callee.inferred - if isa(src, String) - src = _uncompressed_ir(callee, src) - end - if !isa(src, CodeInfo) - newcallee = typeinf_ext(interp, callee.def, source_mode) - if newcallee isa CodeInstance - callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee - push!(tocompile, newcallee) - #else - # println("warning: could not get source code for ", callee.def) - end - continue + newcallee = typeinf_ext(interp, callee.def, source_mode) + if newcallee isa CodeInstance + callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee + push!(tocompile, newcallee) + #else + # println("warning: could not get source code for ", callee.def) end + continue end - push!(inspected, callee) - collectinvokes!(tocompile, src) - ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src) end + push!(inspected, callee) + collectinvokes!(tocompile, src) + ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src) end +end + +# This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match +function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8) + interp = NativeInterpreter(world) + ci = typeinf_ext(interp, mi, source_mode) + add_codeinsts_to_jit!(interp, ci, source_mode) return ci end diff --git a/Compiler/src/types.jl b/Compiler/src/types.jl index 6ffb5402682f3..bcfa3c125bcc2 100644 --- a/Compiler/src/types.jl +++ b/Compiler/src/types.jl @@ -23,6 +23,11 @@ the following methods to satisfy the `AbstractInterpreter` API requirement: - `get_inference_world(interp::NewInterpreter)` - return the world age for this interpreter - `get_inference_cache(interp::NewInterpreter)` - return the local inference cache - `cache_owner(interp::NewInterpreter)` - return the owner of any new cache entries + +If `CodeInstance`s compiled using `interp::NewInterpreter` are meant to be executed with `invoke`, +a method `codegen_cache(interp::NewInterpreter) -> IdDict{CodeInstance, CodeInfo}` must be defined. +After inference, to prepare compiled `CodeInstance`s for execution, `add_codeinsts_to_jit!` must be +called, after which the codegen cache may be discarded. """ abstract type AbstractInterpreter end @@ -430,6 +435,19 @@ to incorporate customized dispatches for the overridden methods. method_table(interp::AbstractInterpreter) = InternalMethodTable(get_inference_world(interp)) method_table(interp::NativeInterpreter) = interp.method_table +""" + codegen_cache(interp::AbstractInterpreter) -> Union{Nothing, IdDict{CodeInstance, CodeInfo}} + +Optionally return a cache associating a `CodeInfo` to a `CodeInstance` that should be added to the JIT +for future execution via `invoke(f, ::CodeInstance, args...)`. This cache is filled during inference, +and is meant to be used afterwards with `add_codeinsts_to_jit!`. + +By default, a value of `nothing` is returned indicating that `CodeInstance`s should not be added to the JIT. +Attempting to execute them via `invoke` will result in an error. +""" +codegen_cache(interp::AbstractInterpreter) = nothing +codegen_cache(interp::NativeInterpreter) = interp.codegen + """ By default `AbstractInterpreter` implements the following inference bail out logic: - `bail_out_toplevel_call(::AbstractInterpreter, sig, ::InferenceState)`: bail out from diff --git a/Compiler/test/AbstractInterpreter.jl b/Compiler/test/AbstractInterpreter.jl index 533eaf93937a3..9f6dfecab4535 100644 --- a/Compiler/test/AbstractInterpreter.jl +++ b/Compiler/test/AbstractInterpreter.jl @@ -534,3 +534,18 @@ let interp = DebugInterp() end @test found end + +@newinterp InvokeInterp +struct InvokeOwner end +codegen = IdDict{CodeInstance, CodeInfo}() +Compiler.cache_owner(::InvokeInterp) = InvokeOwner() +Compiler.codegen_cache(::InvokeInterp) = codegen +let interp = InvokeInterp() + source_mode = Compiler.SOURCE_MODE_ABI + f = (+) + args = (1, 1) + mi = @ccall jl_method_lookup(Any[f, args...]::Ptr{Any}, (1+length(args))::Csize_t, Base.tls_world_age()::Csize_t)::Ref{Core.MethodInstance} + ci = Compiler.typeinf_ext(interp, mi, source_mode) + Compiler.add_codeinsts_to_jit!(interp, ci, source_mode) + @test invoke(f, ci, args...) == 2 +end From 0794583070f15d54fb76dc01cf71adc8ddc8d377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Fri, 7 Feb 2025 03:28:49 -0500 Subject: [PATCH 2/6] Do not expose `add_codeinsts_to_jit!` to the API, instead use `typeinf_ext_toplevel` --- .../extras/CompilerDevTools/src/CompilerDevTools.jl | 3 +-- Compiler/src/typeinfer.jl | 12 ++++++++---- Compiler/src/types.jl | 9 ++++----- Compiler/test/AbstractInterpreter.jl | 3 +-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl b/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl index 46612b5e772e9..dd32564d7fa8d 100644 --- a/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl +++ b/Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl @@ -28,7 +28,7 @@ Compiler.codegen_cache(interp::SplitCacheInterp) = interp.codegen_cache import Core.OptimizedGenerics.CompilerPlugins: typeinf, typeinf_edge @eval @noinline typeinf(::SplitCacheOwner, mi::MethodInstance, source_mode::UInt8) = - Base.invoke_in_world(which(typeinf, Tuple{SplitCacheOwner, MethodInstance, UInt8}).primary_world, Compiler.typeinf_ext, SplitCacheInterp(; world=Base.tls_world_age()), mi, source_mode) + Base.invoke_in_world(which(typeinf, Tuple{SplitCacheOwner, MethodInstance, UInt8}).primary_world, Compiler.typeinf_ext_toplevel, SplitCacheInterp(; world=Base.tls_world_age()), mi, source_mode) @eval @noinline function typeinf_edge(::SplitCacheOwner, mi::MethodInstance, parent_frame::Compiler.InferenceState, world::UInt, source_mode::UInt8) # TODO: This isn't quite right, we're just sketching things for now @@ -42,7 +42,6 @@ function with_new_compiler(f, args...) new_compiler_ci = Core.OptimizedGenerics.CompilerPlugins.typeinf( SplitCacheOwner(), mi, Compiler.SOURCE_MODE_ABI ) - Compiler.add_codeinsts_to_jit!(SplitCacheInterp(; world), new_compiler_ci, Compiler.SOURCE_MODE_ABI) invoke(f, new_compiler_ci, args...) end diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 8c68a7ab862f3..0ae42e4c4b6cb 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1231,7 +1231,7 @@ function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UIn source_mode == SOURCE_MODE_ABI || return ci isa CodeInstance && !ci_has_invoke(ci) || return codegen = codegen_cache(interp) - codegen !== nothing || return + codegen === nothing && return inspected = IdSet{CodeInstance}() tocompile = Vector{CodeInstance}() push!(tocompile, ci) @@ -1263,14 +1263,18 @@ function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UIn end end -# This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match -function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8) - interp = NativeInterpreter(world) +function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8) ci = typeinf_ext(interp, mi, source_mode) add_codeinsts_to_jit!(interp, ci, source_mode) return ci end +# This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match +function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8) + interp = NativeInterpreter(world) + return typeinf_ext_toplevel(interp, mi, source_mode) +end + # This is a bridge for the C code calling `jl_typeinf_func()` on set of Method matches function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim::Bool) inspected = IdSet{CodeInstance}() diff --git a/Compiler/src/types.jl b/Compiler/src/types.jl index bcfa3c125bcc2..9b4479772c798 100644 --- a/Compiler/src/types.jl +++ b/Compiler/src/types.jl @@ -25,9 +25,8 @@ the following methods to satisfy the `AbstractInterpreter` API requirement: - `cache_owner(interp::NewInterpreter)` - return the owner of any new cache entries If `CodeInstance`s compiled using `interp::NewInterpreter` are meant to be executed with `invoke`, -a method `codegen_cache(interp::NewInterpreter) -> IdDict{CodeInstance, CodeInfo}` must be defined. -After inference, to prepare compiled `CodeInstance`s for execution, `add_codeinsts_to_jit!` must be -called, after which the codegen cache may be discarded. +a method `codegen_cache(interp::NewInterpreter) -> IdDict{CodeInstance, CodeInfo}` must be defined, +and inference must be triggered via `typeinf_ext_toplevel` with source mode `SOURCE_MODE_ABI`. """ abstract type AbstractInterpreter end @@ -439,8 +438,8 @@ method_table(interp::NativeInterpreter) = interp.method_table codegen_cache(interp::AbstractInterpreter) -> Union{Nothing, IdDict{CodeInstance, CodeInfo}} Optionally return a cache associating a `CodeInfo` to a `CodeInstance` that should be added to the JIT -for future execution via `invoke(f, ::CodeInstance, args...)`. This cache is filled during inference, -and is meant to be used afterwards with `add_codeinsts_to_jit!`. +for future execution via `invoke(f, ::CodeInstance, args...)`. This cache is used during `typeinf_ext_toplevel`, +and may be safely discarded between calls to this function. By default, a value of `nothing` is returned indicating that `CodeInstance`s should not be added to the JIT. Attempting to execute them via `invoke` will result in an error. diff --git a/Compiler/test/AbstractInterpreter.jl b/Compiler/test/AbstractInterpreter.jl index 9f6dfecab4535..83218d73cad69 100644 --- a/Compiler/test/AbstractInterpreter.jl +++ b/Compiler/test/AbstractInterpreter.jl @@ -545,7 +545,6 @@ let interp = InvokeInterp() f = (+) args = (1, 1) mi = @ccall jl_method_lookup(Any[f, args...]::Ptr{Any}, (1+length(args))::Csize_t, Base.tls_world_age()::Csize_t)::Ref{Core.MethodInstance} - ci = Compiler.typeinf_ext(interp, mi, source_mode) - Compiler.add_codeinsts_to_jit!(interp, ci, source_mode) + ci = Compiler.typeinf_ext_toplevel(interp, mi, source_mode) @test invoke(f, ci, args...) == 2 end From 3a35dcb68816626b11421166f11d1fe1f2c9164b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Tue, 11 Feb 2025 12:12:10 -0500 Subject: [PATCH 3/6] Try with inlining --- Compiler/src/typeinfer.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 3a9c1e81857b0..24430ba27e84d 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1276,7 +1276,7 @@ end function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8) ci = typeinf_ext(interp, mi, source_mode) - add_codeinsts_to_jit!(interp, ci, source_mode) + @inline add_codeinsts_to_jit!(interp, ci, source_mode) return ci end From c565f9126d727d7a868b951ecb483c474e328351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Wed, 12 Feb 2025 06:30:30 -0500 Subject: [PATCH 4/6] See if crashes come from typeinf_ext_toplevel changes --- Compiler/src/typeinfer.jl | 45 ++++++++++++++++++++++++---- Compiler/test/AbstractInterpreter.jl | 4 +-- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 24430ba27e84d..e62673e630e0b 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1274,16 +1274,49 @@ function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UIn end end -function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8) - ci = typeinf_ext(interp, mi, source_mode) - @inline add_codeinsts_to_jit!(interp, ci, source_mode) - return ci -end +# function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8) +# ci = typeinf_ext(interp, mi, source_mode) +# @inline add_codeinsts_to_jit!(interp, ci, source_mode) +# return ci +# end # This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8) interp = NativeInterpreter(world) - return typeinf_ext_toplevel(interp, mi, source_mode) + ci = typeinf_ext(interp, mi, source_mode) + source_mode == SOURCE_MODE_ABI || return + ci isa CodeInstance && !ci_has_invoke(ci) || return + codegen = codegen_cache(interp) + codegen === nothing && return + inspected = IdSet{CodeInstance}() + tocompile = Vector{CodeInstance}() + push!(tocompile, ci) + while !isempty(tocompile) + # ci_has_real_invoke(ci) && return ci # optimization: cease looping if ci happens to get compiled (not just jl_fptr_wait_for_compiled, but fully jl_is_compiled_codeinst) + callee = pop!(tocompile) + ci_has_invoke(callee) && continue + callee in inspected && continue + src = get(codegen, callee, nothing) + if !isa(src, CodeInfo) + src = @atomic :monotonic callee.inferred + if isa(src, String) + src = _uncompressed_ir(callee, src) + end + if !isa(src, CodeInfo) + newcallee = typeinf_ext(interp, callee.def, source_mode) + if newcallee isa CodeInstance + callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee + push!(tocompile, newcallee) + #else + # println("warning: could not get source code for ", callee.def) + end + continue + end + end + push!(inspected, callee) + collectinvokes!(tocompile, src) + ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src) + end end # This is a bridge for the C code calling `jl_typeinf_func()` on set of Method matches diff --git a/Compiler/test/AbstractInterpreter.jl b/Compiler/test/AbstractInterpreter.jl index 83218d73cad69..5f531be913af4 100644 --- a/Compiler/test/AbstractInterpreter.jl +++ b/Compiler/test/AbstractInterpreter.jl @@ -545,6 +545,6 @@ let interp = InvokeInterp() f = (+) args = (1, 1) mi = @ccall jl_method_lookup(Any[f, args...]::Ptr{Any}, (1+length(args))::Csize_t, Base.tls_world_age()::Csize_t)::Ref{Core.MethodInstance} - ci = Compiler.typeinf_ext_toplevel(interp, mi, source_mode) - @test invoke(f, ci, args...) == 2 + # ci = Compiler.typeinf_ext_toplevel(interp, mi, source_mode) + # @test invoke(f, ci, args...) == 2 end From 8644cb25c0d0c87900f255824cc0c43b82ec785c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Wed, 12 Feb 2025 07:34:52 -0500 Subject: [PATCH 5/6] Return CodeInstance --- Compiler/src/typeinfer.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index e62673e630e0b..4f8d32e9cf2f7 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1284,10 +1284,10 @@ end function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8) interp = NativeInterpreter(world) ci = typeinf_ext(interp, mi, source_mode) - source_mode == SOURCE_MODE_ABI || return - ci isa CodeInstance && !ci_has_invoke(ci) || return + source_mode == SOURCE_MODE_ABI || return ci + ci isa CodeInstance && !ci_has_invoke(ci) || return ci codegen = codegen_cache(interp) - codegen === nothing && return + codegen === nothing && return ci inspected = IdSet{CodeInstance}() tocompile = Vector{CodeInstance}() push!(tocompile, ci) @@ -1317,6 +1317,7 @@ function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt collectinvokes!(tocompile, src) ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src) end + return ci end # This is a bridge for the C code calling `jl_typeinf_func()` on set of Method matches From 4d0f67c1151d7cf055a3b3630975c7a4ed4d1bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Wed, 12 Feb 2025 11:42:04 -0500 Subject: [PATCH 6/6] Attempt to fix crashes --- Compiler/src/typeinfer.jl | 53 ++++++---------------------- Compiler/test/AbstractInterpreter.jl | 4 +-- 2 files changed, 12 insertions(+), 45 deletions(-) diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 4f8d32e9cf2f7..611f91d37b0a2 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -1239,10 +1239,10 @@ function collectinvokes!(wq::Vector{CodeInstance}, ci::CodeInfo) end function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UInt8) - source_mode == SOURCE_MODE_ABI || return - ci isa CodeInstance && !ci_has_invoke(ci) || return + source_mode == SOURCE_MODE_ABI || return ci + ci isa CodeInstance && !ci_has_invoke(ci) || return ci codegen = codegen_cache(interp) - codegen === nothing && return + codegen === nothing && return ci inspected = IdSet{CodeInstance}() tocompile = Vector{CodeInstance}() push!(tocompile, ci) @@ -1272,52 +1272,19 @@ function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UIn collectinvokes!(tocompile, src) ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src) end + ci end -# function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8) -# ci = typeinf_ext(interp, mi, source_mode) -# @inline add_codeinsts_to_jit!(interp, ci, source_mode) -# return ci -# end +function typeinf_ext_toplevel(interp::AbstractInterpreter, mi::MethodInstance, source_mode::UInt8) + ci = typeinf_ext(interp, mi, source_mode) + ci = add_codeinsts_to_jit!(interp, ci, source_mode) + return ci +end # This is a bridge for the C code calling `jl_typeinf_func()` on a single Method match function typeinf_ext_toplevel(mi::MethodInstance, world::UInt, source_mode::UInt8) interp = NativeInterpreter(world) - ci = typeinf_ext(interp, mi, source_mode) - source_mode == SOURCE_MODE_ABI || return ci - ci isa CodeInstance && !ci_has_invoke(ci) || return ci - codegen = codegen_cache(interp) - codegen === nothing && return ci - inspected = IdSet{CodeInstance}() - tocompile = Vector{CodeInstance}() - push!(tocompile, ci) - while !isempty(tocompile) - # ci_has_real_invoke(ci) && return ci # optimization: cease looping if ci happens to get compiled (not just jl_fptr_wait_for_compiled, but fully jl_is_compiled_codeinst) - callee = pop!(tocompile) - ci_has_invoke(callee) && continue - callee in inspected && continue - src = get(codegen, callee, nothing) - if !isa(src, CodeInfo) - src = @atomic :monotonic callee.inferred - if isa(src, String) - src = _uncompressed_ir(callee, src) - end - if !isa(src, CodeInfo) - newcallee = typeinf_ext(interp, callee.def, source_mode) - if newcallee isa CodeInstance - callee === ci && (ci = newcallee) # ci stopped meeting the requirements after typeinf_ext last checked, try again with newcallee - push!(tocompile, newcallee) - #else - # println("warning: could not get source code for ", callee.def) - end - continue - end - end - push!(inspected, callee) - collectinvokes!(tocompile, src) - ccall(:jl_add_codeinst_to_jit, Cvoid, (Any, Any), callee, src) - end - return ci + return typeinf_ext_toplevel(interp, mi, source_mode) end # This is a bridge for the C code calling `jl_typeinf_func()` on set of Method matches diff --git a/Compiler/test/AbstractInterpreter.jl b/Compiler/test/AbstractInterpreter.jl index 5f531be913af4..83218d73cad69 100644 --- a/Compiler/test/AbstractInterpreter.jl +++ b/Compiler/test/AbstractInterpreter.jl @@ -545,6 +545,6 @@ let interp = InvokeInterp() f = (+) args = (1, 1) mi = @ccall jl_method_lookup(Any[f, args...]::Ptr{Any}, (1+length(args))::Csize_t, Base.tls_world_age()::Csize_t)::Ref{Core.MethodInstance} - # ci = Compiler.typeinf_ext_toplevel(interp, mi, source_mode) - # @test invoke(f, ci, args...) == 2 + ci = Compiler.typeinf_ext_toplevel(interp, mi, source_mode) + @test invoke(f, ci, args...) == 2 end