From a1177699a78b299d43b0a84bf800349e9973c684 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 6 Jun 2024 01:37:55 +0000 Subject: [PATCH] inference: allocate CodeInstance with inference results ASAP [WIP] Update the CodeInstance after optimizations with code (if possible), but construct it early so that there will be the option of using it for edges and invoke during optimizations, without problems with cycles. --- base/boot.jl | 2 +- base/compiler/compiler.jl | 1 + base/compiler/typeinfer.jl | 90 +++++++++++++++++++++----------------- src/gf.c | 25 ++++++++++- 4 files changed, 77 insertions(+), 41 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 5d2527efd44c09..734738b48aa99f 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -504,7 +504,7 @@ function CodeInstance( mi::MethodInstance, owner, @nospecialize(rettype), @nospecialize(exctype), @nospecialize(inferred_const), @nospecialize(inferred), const_flags::Int32, min_world::UInt, max_world::UInt, ipo_effects::UInt32, effects::UInt32, @nospecialize(analysis_results), - relocatability::UInt8, edges::DebugInfo) + relocatability::UInt8, edges::Union{DebugInfo,Nothing}) return ccall(:jl_new_codeinst, Ref{CodeInstance}, (Any, Any, Any, Any, Any, Any, Int32, UInt, UInt, UInt32, UInt32, Any, UInt8, Any), mi, owner, rettype, exctype, inferred_const, inferred, const_flags, min_world, max_world, diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 12d6d5eb38764b..fb59b9b63b02d8 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -14,6 +14,7 @@ const setproperty! = Core.setfield! const swapproperty! = Core.swapfield! const modifyproperty! = Core.modifyfield! const replaceproperty! = Core.replacefield! +const _DOCS_ALIASING_WARNING = "" ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Compiler, false) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 7233cf62f6b933..f7dd70e58d6535 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -216,7 +216,8 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState) end end -function finish!(interp::AbstractInterpreter, caller::InferenceState) +function finish!(interp::AbstractInterpreter, caller::InferenceState; + can_discard_trees::Bool=may_discard_trees(interp)) result = caller.result valid_worlds = result.valid_worlds if last(valid_worlds) >= get_world_counter() @@ -228,6 +229,44 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState) if opt isa OptimizationState result.src = opt = ir_to_codeinf!(opt) end + if isdefined(result, :ci) + ci = result.ci + relocatability = 0x0 + if is_result_constabi_eligible(result) && can_discard_trees + inferred_result = nothing + relocatability = 0x1 + const_flag = true + else + const_flag = false + inferred_result = transform_result_for_cache(interp, result.linfo, result.valid_worlds, result, can_discard_trees) + if inferred_result isa CodeInfo + edges = inferred_result.debuginfo + uncompressed = inferred_result + inferred_result = maybe_compress_codeinfo(interp, result.linfo, inferred_result, can_discard_trees) + result.is_src_volatile |= uncompressed !== inferred_result + elseif ci.owner === nothing + # The global cache can only handle objects that codegen understands + inferred_result = nothing + end + if isa(inferred_result, String) + t = @_gc_preserve_begin inferred_result + relocatability = unsafe_load(unsafe_convert(Ptr{UInt8}, inferred_result), Core.sizeof(inferred_result)) + @_gc_preserve_end t + elseif inferred_result === nothing + relocatability = 0x1 + end + end + # n.b. relocatability = (isa(inferred_result, String) && inferred_result[end]) || inferred_result === nothing + if !@isdefined edges + edges = DebugInfo(result.linfo) + end + ccall(:jl_update_codeinst, Cvoid, (Any, Any, Int32, UInt, UInt, UInt32, UInt32, Any, UInt8, Any), + ci, inferred_result, const_flag, + first(result.valid_worlds), last(result.valid_worlds), + # TODO: Actually do something with non-IPO effects + encode_effects(result.ipo_effects), encode_effects(result.ipo_effects), result.analysis_results, + relocatability, edges) + end return nothing end @@ -251,7 +290,10 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) for caller in frames caller.valid_worlds = cycle_valid_worlds caller.ipo_effects = cycle_effects - finish(caller, caller.interp) + finishinfer(caller, caller.interp) + if is_cached(caller) + cache_result!(caller.interp, caller.result) + end end for caller in frames opt = caller.result.src @@ -261,9 +303,7 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) end for caller in frames finish!(caller.interp, caller) - if is_cached(caller) - cache_result!(caller.interp, caller.result) - end + unlock_mi_inference(caller.interp, caller.linfo) end empty!(frames) return true @@ -273,8 +313,7 @@ function is_result_constabi_eligible(result::InferenceResult) result_type = result.result return isa(result_type, Const) && is_foldable_nothrow(result.ipo_effects) && is_inlineable_constant(result_type.val) end -function CodeInstance(interp::AbstractInterpreter, result::InferenceResult; - can_discard_trees::Bool=may_discard_trees(interp)) +function CodeInstance(interp::AbstractInterpreter, result::InferenceResult) local const_flags::Int32 result_type = result.result @assert !(result_type === nothing || result_type isa LimitedAccuracy) @@ -308,38 +347,12 @@ function CodeInstance(interp::AbstractInterpreter, result::InferenceResult; end relocatability = 0x0 owner = cache_owner(interp) - if const_flags == 0x3 && can_discard_trees - inferred_result = nothing - relocatability = 0x1 - else - inferred_result = transform_result_for_cache(interp, result.linfo, result.valid_worlds, result, can_discard_trees) - if inferred_result isa CodeInfo - edges = inferred_result.debuginfo - uncompressed = inferred_result - inferred_result = maybe_compress_codeinfo(interp, result.linfo, inferred_result, can_discard_trees) - result.is_src_volatile |= uncompressed !== inferred_result - elseif owner === nothing - # The global cache can only handle objects that codegen understands - inferred_result = nothing - end - if isa(inferred_result, String) - t = @_gc_preserve_begin inferred_result - relocatability = unsafe_load(unsafe_convert(Ptr{UInt8}, inferred_result), Core.sizeof(inferred_result)) - @_gc_preserve_end t - elseif inferred_result === nothing - relocatability = 0x1 - end - end - # n.b. relocatability = (isa(inferred_result, String) && inferred_result[end]) || inferred_result === nothing - if !@isdefined edges - edges = DebugInfo(result.linfo) - end return CodeInstance(result.linfo, owner, - widenconst(result_type), widenconst(result.exc_result), rettype_const, inferred_result, + widenconst(result_type), widenconst(result.exc_result), rettype_const, nothing, const_flags, first(result.valid_worlds), last(result.valid_worlds), # TODO: Actually do something with non-IPO effects encode_effects(result.ipo_effects), encode_effects(result.ipo_effects), result.analysis_results, - relocatability, edges) + relocatability, nothing) end function transform_result_for_cache(interp::AbstractInterpreter, @@ -402,7 +415,6 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult) end end end - unlock_mi_inference(interp, mi) nothing end @@ -521,7 +533,7 @@ end # inference completed on `me` # update the MethodInstance -function finish(me::InferenceState, interp::AbstractInterpreter) +function finishinfer(me::InferenceState, interp::AbstractInterpreter) # prepare to run optimization passes on fulltree s_edges = me.stmt_edges[1] if s_edges === nothing @@ -566,7 +578,6 @@ function finish(me::InferenceState, interp::AbstractInterpreter) me.result.src = nothing me.cache_mode = CACHE_MODE_NULL set_inlineable!(me.src, false) - unlock_mi_inference(interp, me.linfo) elseif limited_src # a type result will be cached still, but not this intermediate work: # we can throw everything else away now @@ -1140,7 +1151,8 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance, source_mod # We construct a new CodeInstance for it that is not part of the cache hierarchy. can_discard_trees = source_mode ≠ SOURCE_MODE_FORCE_SOURCE && is_result_constabi_eligible(result) - code = CodeInstance(interp, result; can_discard_trees) + code = CodeInstance(interp, result) + finish!(interp, frame; can_discard_trees) # If the caller cares about the code and this is constabi, still use our synthesis function # anyway, because we will have not finished inferring the code inside the CodeInstance once diff --git a/src/gf.c b/src/gf.c index e5a33ecf68c5d5..a80708f54cf9a1 100644 --- a/src/gf.c +++ b/src/gf.c @@ -543,7 +543,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( if ((const_flags & 2) == 0) inferred_const = NULL; codeinst->rettype_const = inferred_const; - jl_atomic_store_relaxed(&codeinst->debuginfo, edges); + jl_atomic_store_relaxed(&codeinst->debuginfo, (jl_value_t*)edges == jl_nothing ? NULL : edges); jl_atomic_store_relaxed(&codeinst->specptr.fptr, NULL); jl_atomic_store_relaxed(&codeinst->invoke, NULL); if ((const_flags & 1) != 0) { @@ -560,6 +560,29 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( return codeinst; } +JL_DLLEXPORT void jl_update_codeinst( + jl_code_instance_t *codeinst, jl_value_t *inferred, + int32_t const_flags, size_t min_world, size_t max_world, + uint32_t ipo_effects, uint32_t effects, jl_value_t *analysis_results, + uint8_t relocatability, jl_debuginfo_t *edges /* , int absolute_max*/) +{ + jl_atomic_store_relaxed(&codeinst->min_world, min_world); + jl_atomic_store_relaxed(&codeinst->max_world, max_world); + codeinst->relocatability = relocatability; + codeinst->analysis_results = analysis_results; + jl_gc_wb(codeinst, analysis_results); + codeinst->ipo_purity_bits = ipo_effects; + jl_atomic_store_relaxed(&codeinst->purity_bits, effects); + jl_atomic_store_relaxed(&codeinst->debuginfo, edges); + jl_gc_wb(codeinst, edges); + if ((const_flags & 1) != 0) { + assert(codeinst->rettype_const); + jl_atomic_store_release(&codeinst->invoke, jl_fptr_const_return); + } + jl_atomic_store_release(&codeinst->inferred, inferred); + jl_gc_wb(codeinst, inferred); +} + JL_DLLEXPORT void jl_mi_cache_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, jl_code_instance_t *ci JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED) {