diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index 9a5b19709e697..d90049941a89c 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -323,11 +323,11 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(fun if mi === nothing || !const_prop_methodinstance_heuristic(interp, mi, arginfo, sv) csig = get_compileable_sig(method, sig, match.sparams) if csig !== nothing && (!seenall || csig !== sig) # corresponds to whether the first look already looked at this, so repeating abstract_call_method is not useful + #println(sig, " changed to ", csig, " for ", method) sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), csig, method.sig)::SimpleVector - if match.sparams === sp_[2] - mresult = abstract_call_method(interp, method, csig, match.sparams, multiple_matches, StmtInfo(false, false), sv)::Future - isready(mresult) || return false # wait for mresult Future to resolve off the callstack before continuing - end + sparams = sp_[2]::SimpleVector + mresult = abstract_call_method(interp, method, csig, sparams, multiple_matches, StmtInfo(false, false), sv)::Future + isready(mresult) || return false # wait for mresult Future to resolve off the callstack before continuing end end end @@ -1365,7 +1365,8 @@ function const_prop_call(interp::AbstractInterpreter, pop!(callstack) return nothing end - inf_result.ci_as_edge = codeinst_as_edge(interp, frame) + existing_edge = result.edge + inf_result.ci_as_edge = codeinst_as_edge(interp, frame, existing_edge) @assert frame.frameid != 0 && frame.cycleid == frame.frameid @assert frame.parentid == sv.frameid @assert inf_result.result !== nothing diff --git a/Compiler/src/ssair/EscapeAnalysis.jl b/Compiler/src/ssair/EscapeAnalysis.jl index a8c450f5bb9e0..47a7840628bb5 100644 --- a/Compiler/src/ssair/EscapeAnalysis.jl +++ b/Compiler/src/ssair/EscapeAnalysis.jl @@ -1068,7 +1068,10 @@ end # escape statically-resolved call, i.e. `Expr(:invoke, ::MethodInstance, ...)` function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) - mi = first(args)::MethodInstance + mi = first(args) + if !(mi isa MethodInstance) + mi = (mi::CodeInstance).def # COMBAK get escape info directly from CI instead? + end first_idx, last_idx = 2, length(args) add_liveness_changes!(astate, pc, args, first_idx, last_idx) # TODO inspect `astate.ir.stmts[pc][:info]` and use const-prop'ed `InferenceResult` if available diff --git a/Compiler/src/ssair/inlining.jl b/Compiler/src/ssair/inlining.jl index 02b58b518a72a..0c0d14bf8f25a 100644 --- a/Compiler/src/ssair/inlining.jl +++ b/Compiler/src/ssair/inlining.jl @@ -38,7 +38,7 @@ struct SomeCase end struct InvokeCase - invoke::MethodInstance + invoke::Union{CodeInstance,MethodInstance} effects::Effects info::CallInfo end @@ -764,8 +764,9 @@ function rewrite_apply_exprargs!(todo::Vector{Pair{Int,Any}}, return new_argtypes end -function compileable_specialization(mi::MethodInstance, effects::Effects, +function compileable_specialization(code::Union{MethodInstance,CodeInstance}, effects::Effects, et::InliningEdgeTracker, @nospecialize(info::CallInfo), state::InliningState) + mi = code isa CodeInstance ? code.def : code mi_invoke = mi method, atype, sparams = mi.def::Method, mi.specTypes, mi.sparam_vals if OptimizationParams(state.interp).compilesig_invokes @@ -773,10 +774,10 @@ function compileable_specialization(mi::MethodInstance, effects::Effects, new_atype === nothing && return nothing if atype !== new_atype sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), new_atype, method.sig)::SimpleVector - if sparams === sp_[2]::SimpleVector - mi_invoke = specialize_method(method, new_atype, sparams) - mi_invoke === nothing && return nothing - end + sparams = sp_[2]::SimpleVector + mi_invoke = specialize_method(method, new_atype, sparams) + mi_invoke === nothing && return nothing + code = mi_invoke end else # If this caller does not want us to optimize calls to use their @@ -786,8 +787,15 @@ function compileable_specialization(mi::MethodInstance, effects::Effects, return nothing end end - add_inlining_edge!(et, mi_invoke) # to the dispatch lookup - return InvokeCase(mi_invoke, effects, info) + # prefer using a CodeInstance gotten from the cache, since that is where the invoke target should get compiled to normally + # TODO: can this code be gotten directly from inference sometimes? + code = get(code_cache(state), mi_invoke, nothing) + if !isa(code, CodeInstance) + #println("missing code for ", mi_invoke, " for ", mi) + code = mi_invoke + end + add_inlining_edge!(et, code) # to the code and edges + return InvokeCase(code, effects, info) end struct InferredResult @@ -844,18 +852,18 @@ function resolve_todo(mi::MethodInstance, result::Union{Nothing,InferenceResult, src = @atomic :monotonic inferred_result.inferred effects = decode_effects(inferred_result.ipo_purity_bits) edge = inferred_result - else # there is no cached source available, bail out + else # there is no cached source available for this, but there might be code for the compilation sig return compileable_specialization(mi, Effects(), et, info, state) end # the duplicated check might have been done already within `analyze_method!`, but still # we need it here too since we may come here directly using a constant-prop' result if !OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) - return compileable_specialization(edge.def, effects, et, info, state) + return compileable_specialization(edge, effects, et, info, state) end src_inlining_policy(state.interp, src, info, flag) || - return compileable_specialization(edge.def, effects, et, info, state) + return compileable_specialization(edge, effects, et, info, state) add_inlining_edge!(et, edge) if inferred_result isa CodeInstance @@ -1423,7 +1431,8 @@ end function semiconcrete_result_item(result::SemiConcreteResult, @nospecialize(info::CallInfo), flag::UInt32, state::InliningState) - mi = result.edge.def + code = result.edge + mi = code.def et = InliningEdgeTracker(state) if (!OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) || @@ -1431,10 +1440,10 @@ function semiconcrete_result_item(result::SemiConcreteResult, # a `@noinline`-declared method when it's marked as `@constprop :aggressive`. # Suppress the inlining here (unless inlining is requested at the callsite). (is_declared_noinline(mi.def::Method) && !is_stmt_inline(flag))) - return compileable_specialization(mi, result.effects, et, info, state) + return compileable_specialization(code, result.effects, et, info, state) end src_inlining_policy(state.interp, result.ir, info, flag) || - return compileable_specialization(mi, result.effects, et, info, state) + return compileable_specialization(code, result.effects, et, info, state) add_inlining_edge!(et, result.edge) preserve_local_sources = OptimizationParams(state.interp).preserve_local_sources @@ -1466,7 +1475,7 @@ may_inline_concrete_result(result::ConcreteResult) = function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallInfo), state::InliningState) if !may_inline_concrete_result(result) et = InliningEdgeTracker(state) - return compileable_specialization(result.edge.def, result.effects, et, info, state) + return compileable_specialization(result.edge, result.effects, et, info, state) end @assert result.effects === EFFECTS_TOTAL return ConstantCase(quoted(result.result), result.edge) @@ -1522,11 +1531,7 @@ function handle_modifyop!_call!(ir::IRCode, idx::Int, stmt::Expr, info::ModifyOp match = info.results[1]::MethodMatch match.fully_covers || return nothing edge = info.edges[1] - if edge === nothing - edge = specialize_method(match) - else - edge = edge.def - end + edge === nothing && return nothing case = compileable_specialization(edge, Effects(), InliningEdgeTracker(state), info, state) case === nothing && return nothing stmt.head = :invoke_modify @@ -1564,8 +1569,11 @@ function handle_finalizer_call!(ir::IRCode, idx::Int, stmt::Expr, info::Finalize # `Core.Compiler` data structure into the global cache item1 = cases[1].item if isa(item1, InliningTodo) - push!(stmt.args, true) - push!(stmt.args, item1.mi) + code = get(code_cache(state), item1.mi, nothing) # COMBAK: this seems like a bad design, can we use stmt_info instead to store the correct info? + if code isa CodeInstance + push!(stmt.args, true) + push!(stmt.args, code) + end elseif isa(item1, InvokeCase) push!(stmt.args, false) push!(stmt.args, item1.invoke) @@ -1578,7 +1586,10 @@ end function handle_invoke_expr!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stmt::Expr, @nospecialize(info::CallInfo), flag::UInt32, sig::Signature, state::InliningState) - mi = stmt.args[1]::MethodInstance + mi = stmt.args[1] + if !(mi isa MethodInstance) + mi = (mi::CodeInstance).def + end case = resolve_todo(mi, info, flag, state) handle_single_case!(todo, ir, idx, stmt, case, false) return nothing diff --git a/Compiler/src/ssair/irinterp.jl b/Compiler/src/ssair/irinterp.jl index dd5c907d3c25f..e96d27a85bc37 100644 --- a/Compiler/src/ssair/irinterp.jl +++ b/Compiler/src/ssair/irinterp.jl @@ -33,11 +33,15 @@ end function abstract_eval_invoke_inst(interp::AbstractInterpreter, inst::Instruction, irsv::IRInterpretationState) stmt = inst[:stmt] - mi = stmt.args[1]::MethodInstance - world = frame_world(irsv) - mi_cache = WorldView(code_cache(interp), world) - code = get(mi_cache, mi, nothing) - code === nothing && return Pair{Any,Tuple{Bool,Bool}}(nothing, (false, false)) + ci = stmt.args[1] + if ci isa MethodInstance + world = frame_world(irsv) + mi_cache = WorldView(code_cache(interp), world) + code = get(mi_cache, ci, nothing) + code === nothing && return Pair{Any,Tuple{Bool,Bool}}(nothing, (false, false)) + else + code = ci::CodeInstance + end argtypes = collect_argtypes(interp, stmt.args[2:end], StatementState(nothing, false), irsv) argtypes === nothing && return Pair{Any,Tuple{Bool,Bool}}(Bottom, (false, false)) return concrete_eval_invoke(interp, code, argtypes, irsv) @@ -160,7 +164,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, inst::Instruction, result isa Future && (result = result[]) (; rt, effects) = result add_flag!(inst, flags_for_effects(effects)) - elseif head === :invoke + elseif head === :invoke # COMBAK: || head === :invoke_modifyfield (similar to call, but for args[2:end]) rt, (nothrow, noub) = abstract_eval_invoke_inst(interp, inst, irsv) if nothrow add_flag!(inst, IR_FLAG_NOTHROW) diff --git a/Compiler/src/ssair/passes.jl b/Compiler/src/ssair/passes.jl index dad4a09a3e710..e61f3207fc07a 100644 --- a/Compiler/src/ssair/passes.jl +++ b/Compiler/src/ssair/passes.jl @@ -1302,7 +1302,7 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) # at the end of the intrinsic. Detect that here. if length(stmt.args) == 4 && stmt.args[4] === nothing # constant case - elseif length(stmt.args) == 5 && stmt.args[4] isa Bool && stmt.args[5] isa MethodInstance + elseif length(stmt.args) == 5 && stmt.args[4] isa Bool && stmt.args[5] isa Core.CodeInstance # inlining case else continue @@ -1522,9 +1522,9 @@ end # NOTE we resolve the inlining source here as we don't want to serialize `Core.Compiler` # data structure into the global cache (see the comment in `handle_finalizer_call!`) function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, - mi::MethodInstance, @nospecialize(info::CallInfo), inlining::InliningState, + code::CodeInstance, @nospecialize(info::CallInfo), inlining::InliningState, attach_after::Bool) - code = get(code_cache(inlining), mi, nothing) + mi = code.def et = InliningEdgeTracker(inlining) if code isa CodeInstance if use_const_api(code) @@ -1671,11 +1671,11 @@ function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, if inline === nothing # No code in the function - Nothing to do else - mi = finalizer_stmt.args[5]::MethodInstance - if inline::Bool && try_inline_finalizer!(ir, argexprs, loc, mi, info, inlining, attach_after) + ci = finalizer_stmt.args[5]::CodeInstance + if inline::Bool && try_inline_finalizer!(ir, argexprs, loc, ci, info, inlining, attach_after) # the finalizer body has been inlined else - newinst = add_flag(NewInstruction(Expr(:invoke, mi, argexprs...), Nothing), flag) + newinst = add_flag(NewInstruction(Expr(:invoke, ci, argexprs...), Nothing), flag) insert_node!(ir, loc, newinst, attach_after) end end diff --git a/Compiler/src/ssair/show.jl b/Compiler/src/ssair/show.jl index b9ed220d59453..7d7b182655db7 100644 --- a/Compiler/src/ssair/show.jl +++ b/Compiler/src/ssair/show.jl @@ -92,11 +92,14 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), code::Union{IRCode,Co print(io, ", ") print(io, stmt.typ) print(io, ")") - elseif isexpr(stmt, :invoke) && length(stmt.args) >= 2 && isa(stmt.args[1], MethodInstance) + elseif isexpr(stmt, :invoke) && length(stmt.args) >= 2 && isa(stmt.args[1], Union{MethodInstance,CodeInstance}) stmt = stmt::Expr # TODO: why is this here, and not in Base.show_unquoted printstyled(io, " invoke "; color = :light_black) - mi = stmt.args[1]::Core.MethodInstance + mi = stmt.args[1] + if !(mi isa Core.MethodInstance) + mi = (mi::Core.CodeInstance).def + end show_unquoted(io, stmt.args[2], indent) print(io, "(") # XXX: this is wrong if `sig` is not a concretetype method @@ -110,6 +113,7 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), code::Union{IRCode,Co end join(io, (print_arg(i) for i = 3:length(stmt.args)), ", ") print(io, ")") + # TODO: if we have a CodeInstance, should we print that rettype info here, which may differ (wider or narrower than the ssavaluetypes) elseif isexpr(stmt, :call) && length(stmt.args) >= 1 && label_dynamic_calls ft = maybe_argextype(stmt.args[1], code, sptypes) f = singleton_type(ft) diff --git a/Compiler/src/typeinfer.jl b/Compiler/src/typeinfer.jl index 544c5d5739795..83ec0271ea474 100644 --- a/Compiler/src/typeinfer.jl +++ b/Compiler/src/typeinfer.jl @@ -449,9 +449,10 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) maybe_validate_code(me.linfo, me.src, "inferred") # finish populating inference results into the CodeInstance if possible, and maybe cache that globally for use elsewhere - if isdefined(result, :ci) && !limited_ret + if isdefined(result, :ci) result_type = result.result - @assert !(result_type === nothing || result_type isa LimitedAccuracy) + result_type isa LimitedAccuracy && (result_type = result_type.typ) + @assert !(result_type === nothing) if isa(result_type, Const) rettype_const = result_type.val const_flags = is_result_constabi_eligible(result) ? 0x3 : 0x2 @@ -760,16 +761,24 @@ function MethodCallResult(::AbstractInterpreter, sv::AbsIntState, method::Method return MethodCallResult(rt, exct, effects, edge, edgecycle, edgelimited, volatile_inf_result) end -# allocate a dummy `edge::CodeInstance` to be added by `add_edges!` -function codeinst_as_edge(interp::AbstractInterpreter, sv::InferenceState) +# allocate a dummy `edge::CodeInstance` to be added by `add_edges!`, reusing an existing_edge if possible +# TODO: fill this in fully correctly (currently IPO info such as effects and return types are lost) +function codeinst_as_edge(interp::AbstractInterpreter, sv::InferenceState, @nospecialize existing_edge) mi = sv.linfo - owner = cache_owner(interp) min_world, max_world = first(sv.world.valid_worlds), last(sv.world.valid_worlds) if max_world >= get_world_counter() max_world = typemax(UInt) end edges = Core.svec(sv.edges...) - ci = CodeInstance(mi, owner, Any, Any, nothing, nothing, zero(Int32), + if existing_edge isa CodeInstance + # return an existing_edge, if the existing edge has more restrictions already (more edges and narrower worlds) + if existing_edge.min_world >= min_world && + existing_edge.max_world <= max_world && + existing_edge.edges == edges + return existing_edge + end + end + ci = CodeInstance(mi, cache_owner(interp), Any, Any, nothing, nothing, zero(Int32), min_world, max_world, zero(UInt32), nothing, zero(UInt8), nothing, edges) if max_world == typemax(UInt) # if we can record all of the backedges in the global reverse-cache, diff --git a/Compiler/test/inline.jl b/Compiler/test/inline.jl index 9d828fb7a4cfd..5dbf0a01db4a8 100644 --- a/Compiler/test/inline.jl +++ b/Compiler/test/inline.jl @@ -121,7 +121,7 @@ f29083(;μ,σ) = μ + σ*randn() g29083() = f29083(μ=2.0,σ=0.1) let c = code_typed(g29083, ())[1][1].code # make sure no call to kwfunc remains - @test !any(e->(isa(e,Expr) && (e.head === :invoke && e.args[1].def.name === :kwfunc)), c) + @test !any(e->(isa(e,Expr) && (e.head === :invoke && e.args[1].def.def.name === :kwfunc)), c) end @testset "issue #19122: [no]inline of short func. def. with return type annotation" begin @@ -723,7 +723,7 @@ mktempdir() do dir ci, rt = only(code_typed(issue42246)) if any(ci.code) do stmt Meta.isexpr(stmt, :invoke) && - stmt.args[1].def.name === nameof(IOBuffer) + stmt.args[1].def.def.name === nameof(IOBuffer) end exit(0) else @@ -1797,7 +1797,7 @@ end isinvokemodify(y) = @nospecialize(x) -> isinvokemodify(y, x) isinvokemodify(sym::Symbol, @nospecialize(x)) = isinvokemodify(mi->mi.def.name===sym, x) -isinvokemodify(pred::Function, @nospecialize(x)) = isexpr(x, :invoke_modify) && pred(x.args[1]::MethodInstance) +isinvokemodify(pred::Function, @nospecialize(x)) = isexpr(x, :invoke_modify) && pred((x.args[1]::CodeInstance).def) mutable struct Atomic{T} @atomic x::T @@ -2131,7 +2131,7 @@ let src = code_typed1((Type,)) do x end @test count(src.code) do @nospecialize x isinvoke(:no_compile_sig_invokes, x) && - (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),Any} + (x.args[1]::Core.CodeInstance).def.specTypes == Tuple{typeof(no_compile_sig_invokes),Any} end == 1 end let src = code_typed1((Type,); interp=NoCompileSigInvokes()) do x @@ -2139,7 +2139,7 @@ let src = code_typed1((Type,); interp=NoCompileSigInvokes()) do x end @test count(src.code) do @nospecialize x isinvoke(:no_compile_sig_invokes, x) && - (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),Type} + (x.args[1]::Core.CodeInstance).def.specTypes == Tuple{typeof(no_compile_sig_invokes),Type} end == 1 end # test the union split case @@ -2148,7 +2148,7 @@ let src = code_typed1((Union{DataType,UnionAll},)) do x end @test count(src.code) do @nospecialize x isinvoke(:no_compile_sig_invokes, x) && - (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),Any} + (x.args[1]::Core.CodeInstance).def.specTypes == Tuple{typeof(no_compile_sig_invokes),Any} end == 2 end let src = code_typed1((Union{DataType,UnionAll},); interp=NoCompileSigInvokes()) do x @@ -2156,11 +2156,11 @@ let src = code_typed1((Union{DataType,UnionAll},); interp=NoCompileSigInvokes()) end @test count(src.code) do @nospecialize x isinvoke(:no_compile_sig_invokes, x) && - (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),DataType} + (x.args[1]::Core.CodeInstance).def.specTypes == Tuple{typeof(no_compile_sig_invokes),DataType} end == 1 @test count(src.code) do @nospecialize x isinvoke(:no_compile_sig_invokes, x) && - (x.args[1]::MethodInstance).specTypes == Tuple{typeof(no_compile_sig_invokes),UnionAll} + (x.args[1]::Core.CodeInstance).def.specTypes == Tuple{typeof(no_compile_sig_invokes),UnionAll} end == 1 end diff --git a/Compiler/test/irutils.jl b/Compiler/test/irutils.jl index 95525d2f2fe5a..50b3a858d89dc 100644 --- a/Compiler/test/irutils.jl +++ b/Compiler/test/irutils.jl @@ -38,7 +38,7 @@ end # check if `x` is a statically-resolved call of a function whose name is `sym` isinvoke(y) = @nospecialize(x) -> isinvoke(y, x) isinvoke(sym::Symbol, @nospecialize(x)) = isinvoke(mi->mi.def.name===sym, x) -isinvoke(pred::Function, @nospecialize(x)) = isexpr(x, :invoke) && pred(x.args[1]::MethodInstance) +isinvoke(pred::Function, @nospecialize(x)) = isexpr(x, :invoke) && pred((x.args[1]::CodeInstance).def) fully_eliminated(@nospecialize args...; retval=(@__FILE__), kwargs...) = fully_eliminated(code_typed1(args...; kwargs...); retval) diff --git a/base/essentials.jl b/base/essentials.jl index 5683120df8d51..3574116261968 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -1050,6 +1050,7 @@ call obsolete versions of a function `f`. Prior to Julia 1.9, this function was not exported, and was called as `Base.invokelatest`. """ function invokelatest(@nospecialize(f), @nospecialize args...; kwargs...) + @inline kwargs = merge(NamedTuple(), kwargs) if isempty(kwargs) return Core._call_latest(f, args...) @@ -1084,6 +1085,7 @@ of [`invokelatest`](@ref). world age refers to system state unrelated to the main Julia session. """ function invoke_in_world(world::UInt, @nospecialize(f), @nospecialize args...; kwargs...) + @inline kwargs = Base.merge(NamedTuple(), kwargs) if isempty(kwargs) return Core._call_in_world(world, f, args...) diff --git a/base/reflection.jl b/base/reflection.jl index 1b8ed9413a35b..9246b4cb0ac71 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -248,30 +248,17 @@ struct CodegenParams """ trim::Cint - """ - A pointer of type - - typedef jl_value_t *(*jl_codeinstance_lookup_t)(jl_method_instance_t *mi JL_PROPAGATES_ROOT, - size_t min_world, size_t max_world); - - that may be used by external compilers as a callback to look up the code instance corresponding - to a particular method instance. - """ - lookup::Ptr{Cvoid} - function CodegenParams(; track_allocations::Bool=true, code_coverage::Bool=true, prefer_specsig::Bool=false, gnu_pubnames::Bool=true, debug_info_kind::Cint = default_debug_info_kind(), debug_info_level::Cint = Cint(JLOptions().debug_level), safepoint_on_entry::Bool=true, - gcstack_arg::Bool=true, use_jlplt::Bool=true, trim::Cint=Cint(0), - lookup::Ptr{Cvoid}=unsafe_load(cglobal(:jl_rettype_inferred_addr, Ptr{Cvoid}))) + gcstack_arg::Bool=true, use_jlplt::Bool=true, trim::Cint=Cint(0)) return new( Cint(track_allocations), Cint(code_coverage), Cint(prefer_specsig), Cint(gnu_pubnames), debug_info_kind, debug_info_level, Cint(safepoint_on_entry), - Cint(gcstack_arg), Cint(use_jlplt), Cint(trim), - lookup) + Cint(gcstack_arg), Cint(use_jlplt), Cint(trim)) end end diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 583a8201587f7..4b3f1f1171ded 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -289,21 +289,22 @@ static void makeSafeName(GlobalObject &G) G.setName(StringRef(SafeName.data(), SafeName.size())); } -jl_code_instance_t *jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance_t *mi, size_t world) +static jl_code_instance_t *jl_ci_cache_lookup(jl_method_instance_t *mi, size_t world, jl_codeinstance_lookup_t lookup) { ++CICacheLookups; - jl_value_t *ci = cgparams.lookup(mi, world, world); + jl_value_t *ci = lookup(mi, world, world); JL_GC_PROMISE_ROOTED(ci); jl_code_instance_t *codeinst = NULL; if (ci != jl_nothing && jl_atomic_load_relaxed(&((jl_code_instance_t *)ci)->inferred) != jl_nothing) { codeinst = (jl_code_instance_t*)ci; } else { - if (cgparams.lookup != jl_rettype_inferred_addr) { + if (lookup != jl_rettype_inferred_addr) { // XXX: This will corrupt and leak a lot of memory which may be very bad jl_error("Refusing to automatically run type inference with custom cache lookup."); } else { + // XXX: SOURCE_MODE_ABI is wrong here (not sufficient) codeinst = jl_type_infer(mi, world, SOURCE_MODE_ABI); /* Even if this codeinst is ordinarily not cacheable, we need to force * it into the cache here, since it was explicitly requested and is @@ -440,13 +441,15 @@ static void compile_workqueue(jl_codegen_params_t ¶ms, CompilationPolicy pol // `_imaging_mode` controls if raw pointers can be embedded (e.g. the code will be loaded into the same session). // `_external_linkage` create linkages between pkgimages. extern "C" JL_DLLEXPORT_CODEGEN -void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int _policy, int _imaging_mode, int _external_linkage, size_t _world) +void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int _policy, int _imaging_mode, int _external_linkage, size_t _world, jl_codeinstance_lookup_t lookup) { JL_TIMING(NATIVE_AOT, NATIVE_Create); ++CreateNativeCalls; CreateNativeMax.updateMax(jl_array_nrows(methods)); if (cgparams == NULL) cgparams = &jl_default_cgparams; + if (lookup == NULL) + lookup = &jl_rettype_inferred_native; jl_native_code_desc_t *data = new jl_native_code_desc_t; CompilationPolicy policy = (CompilationPolicy) _policy; bool imaging = imaging_default() || _imaging_mode == 1; @@ -511,7 +514,7 @@ void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvm // then we want to compile and emit this if (jl_atomic_load_relaxed(&mi->def.method->primary_world) <= this_world && this_world <= jl_atomic_load_relaxed(&mi->def.method->deleted_world)) { // find and prepare the source code to compile - jl_code_instance_t *codeinst = jl_ci_cache_lookup(*cgparams, mi, this_world); + jl_code_instance_t *codeinst = jl_ci_cache_lookup(mi, this_world, lookup); if (jl_options.trim != JL_TRIM_NO && !codeinst) { // If we're building a small image, we need to compile everything // to ensure that we have all the information we need. diff --git a/src/cgutils.cpp b/src/cgutils.cpp index a166b0a2c4800..157d253ba4f21 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -4485,8 +4485,7 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) (a->debug_info_kind == b->debug_info_kind) && (a->safepoint_on_entry == b->safepoint_on_entry) && (a->gcstack_arg == b->gcstack_arg) && - (a->use_jlplt == b->use_jlplt) && - (a->lookup == b->lookup); + (a->use_jlplt == b->use_jlplt); } #endif diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index 98ac063ba36d6..fe50af3f8e84d 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -70,7 +70,7 @@ JL_DLLEXPORT size_t jl_jit_total_bytes_fallback(void) return 0; } -JL_DLLEXPORT void *jl_create_native_fallback(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int _policy, int _imaging_mode, int _external_linkage, size_t _world) UNAVAILABLE +JL_DLLEXPORT void *jl_create_native_fallback(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int _policy, int _imaging_mode, int _external_linkage, size_t _world, jl_codeinstance_lookup_t lookup) UNAVAILABLE JL_DLLEXPORT void jl_dump_compiles_fallback(void *s) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 85d791052484c..e3225a1a7dec2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5496,10 +5496,19 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR bool handled = false; jl_cgval_t result; if (lival.constant) { - jl_method_instance_t *mi = (jl_method_instance_t*)lival.constant; + jl_method_instance_t *mi; + jl_value_t *ci = nullptr; + if (jl_is_method_instance(lival.constant)) { + mi = (jl_method_instance_t*)lival.constant; + } + else { + ci = lival.constant; + assert(jl_is_code_instance(ci)); + mi = ((jl_code_instance_t*)ci)->def; + } assert(jl_is_method_instance(mi)); if (mi == ctx.linfo) { - // handle self-recursion specially + // handle self-recursion specially (TODO: assuming ci is a valid invoke for mi?) jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; FunctionType *ft = ctx.f->getFunctionType(); StringRef protoname = ctx.f->getName(); @@ -5514,8 +5523,7 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, const jl_cgval_t &lival, ArrayR } } else { - jl_value_t *ci = ctx.params->lookup(mi, ctx.min_world, ctx.max_world); - if (ci != jl_nothing) { + if (ci) { jl_code_instance_t *codeinst = (jl_code_instance_t*)ci; auto invoke = jl_atomic_load_acquire(&codeinst->invoke); // check if we know how to handle this specptr @@ -10343,24 +10351,8 @@ int jl_opaque_ptrs_set = 0; extern "C" void jl_init_llvm(void) { - jl_default_cgparams = { - /* track_allocations */ 1, - /* code_coverage */ 1, - /* prefer_specsig */ 0, -#ifdef _OS_WINDOWS_ - /* gnu_pubnames */ 0, -#else - /* gnu_pubnames */ 1, -#endif - /* debug_info_kind */ (int) DICompileUnit::DebugEmissionKind::FullDebug, - /* debug_info_level */ (int) jl_options.debug_level, - /* safepoint_on_entry */ 1, - /* gcstack_arg */ 1, - /* use_jlplt*/ 1, - /* trim */ 0, - /* lookup */ jl_rettype_inferred_addr }; jl_page_size = jl_getpagesize(); - jl_default_debug_info_kind = (int) DICompileUnit::DebugEmissionKind::FullDebug; + jl_default_debug_info_kind = jl_default_cgparams.debug_info_kind = (int) DICompileUnit::DebugEmissionKind::FullDebug; jl_default_cgparams.debug_info_level = (int) jl_options.debug_level; InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); diff --git a/src/gf.c b/src/gf.c index 97a50cd8339a7..90b874d614b0c 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3196,7 +3196,7 @@ JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) if (mi == NULL) return 0; JL_GC_PROMISE_ROOTED(mi); - jl_compile_method_instance(mi, NULL, world); + jl_compile_method_instance(mi, types, world); return 1; } diff --git a/src/init.c b/src/init.c index b3ca33344d258..1cd14e8556cc6 100644 --- a/src/init.c +++ b/src/init.c @@ -722,7 +722,21 @@ static void restore_fp_env(void) static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct); JL_DLLEXPORT int jl_default_debug_info_kind; -JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; +JL_DLLEXPORT jl_cgparams_t jl_default_cgparams = { + /* track_allocations */ 1, + /* code_coverage */ 1, + /* prefer_specsig */ 0, +#ifdef _OS_WINDOWS_ + /* gnu_pubnames */ 0, +#else + /* gnu_pubnames */ 1, +#endif + /* debug_info_kind */ 0, // later DICompileUnit::DebugEmissionKind::FullDebug, + /* debug_info_level */ 0, // later jl_options.debug_level, + /* safepoint_on_entry */ 1, + /* gcstack_arg */ 1, + /* use_jlplt*/ 1, + /* trim */ 0 }; static void init_global_mutexes(void) { JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex"); diff --git a/src/interpreter.c b/src/interpreter.c index cf2ae1a0d9f44..49a3afed14f0c 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -135,8 +135,9 @@ static jl_value_t *do_invoke(jl_value_t **args, size_t nargs, interpreter_state size_t i; for (i = 1; i < nargs; i++) argv[i-1] = eval_value(args[i], s); - jl_method_instance_t *meth = (jl_method_instance_t*)args[0]; - assert(jl_is_method_instance(meth)); + jl_value_t *c = args[0]; + assert(jl_is_code_instance(c) || jl_is_method_instance(c)); + jl_method_instance_t *meth = jl_is_method_instance(c) ? (jl_method_instance_t*)c : ((jl_code_instance_t*)c)->def; jl_value_t *result = jl_invoke(argv[0], nargs == 2 ? NULL : &argv[1], nargs - 2, meth); JL_GC_POP(); return result; diff --git a/src/julia.h b/src/julia.h index 87979f75e8d80..944fd3c43a297 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2650,8 +2650,6 @@ JL_DLLEXPORT void jl_set_safe_restore(jl_jmp_buf *) JL_NOTSAFEPOINT; // codegen interface ---------------------------------------------------------- // The root propagation here doesn't have to be literal, but callers should // ensure that the return value outlives the MethodInstance -typedef jl_value_t *(*jl_codeinstance_lookup_t)(jl_method_instance_t *mi JL_PROPAGATES_ROOT, - size_t min_world, size_t max_world); typedef struct { int track_allocations; // can we track allocations? int code_coverage; // can we measure coverage? @@ -2667,8 +2665,6 @@ typedef struct { int use_jlplt; // Whether to use the Julia PLT mechanism or emit symbols directly int trim; // can we emit dynamic dispatches? - // Cache access. Default: jl_rettype_inferred_native. - jl_codeinstance_lookup_t lookup; } jl_cgparams_t; extern JL_DLLEXPORT int jl_default_debug_info_kind; extern JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; diff --git a/src/julia_internal.h b/src/julia_internal.h index aadcbfdc94038..cd101533f1b8d 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1946,7 +1946,8 @@ JL_DLLIMPORT jl_value_t *jl_dump_fptr_asm(uint64_t fptr, char emit_mc, const cha JL_DLLIMPORT jl_value_t *jl_dump_function_ir(jl_llvmf_dump_t *dump, char strip_ir_metadata, char dump_module, const char *debuginfo); JL_DLLIMPORT jl_value_t *jl_dump_function_asm(jl_llvmf_dump_t *dump, char emit_mc, const char* asm_variant, const char *debuginfo, char binary, char raw); -JL_DLLIMPORT void *jl_create_native(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int policy, int imaging_mode, int cache, size_t world); +typedef jl_value_t *(*jl_codeinstance_lookup_t)(jl_method_instance_t *mi JL_PROPAGATES_ROOT, size_t min_world, size_t max_world); +JL_DLLIMPORT void *jl_create_native(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int policy, int imaging_mode, int cache, size_t world, jl_codeinstance_lookup_t lookup); JL_DLLIMPORT void jl_dump_native(void *native_code, const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, ios_t *z, ios_t *s, jl_emission_params_t *params); diff --git a/src/opaque_closure.c b/src/opaque_closure.c index 65773f88a3951..e3334c037f5a9 100644 --- a/src/opaque_closure.c +++ b/src/opaque_closure.c @@ -53,7 +53,8 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t jl_method_instance_t *mi = NULL; if (source->source) { mi = jl_specializations_get_linfo(source, sigtype, jl_emptysvec); - } else { + } + else { mi = (jl_method_instance_t *)jl_atomic_load_relaxed(&source->specializations); if (!jl_subtype(sigtype, mi->specTypes)) { jl_error("sigtype mismatch in optimized opaque closure"); @@ -116,7 +117,7 @@ static jl_opaque_closure_t *new_opaque_closure(jl_tupletype_t *argt, jl_value_t // OC wrapper methods are not world dependent and have no edges or other info ci = jl_get_method_inferred(mi_generic, selected_rt, 1, ~(size_t)0, NULL, NULL); if (!jl_atomic_load_acquire(&ci->invoke)) - jl_compile_codeinst(ci); + jl_compile_codeinst(ci); // confusing this actually calls jl_emit_oc_wrapper and never actually compiles ci (which would be impossible) specptr = jl_atomic_load_relaxed(&ci->specptr.fptr); } jl_opaque_closure_t *oc = (jl_opaque_closure_t*)jl_gc_alloc(ct->ptls, sizeof(jl_opaque_closure_t), oc_type); diff --git a/src/precompile_utils.c b/src/precompile_utils.c index d008cd26a28e9..81c60ba70d29f 100644 --- a/src/precompile_utils.c +++ b/src/precompile_utils.c @@ -278,7 +278,8 @@ static void *jl_precompile_(jl_array_t *m, int external_linkage) } } void *native_code = jl_create_native(m2, NULL, NULL, 0, 1, external_linkage, - jl_atomic_load_acquire(&jl_world_counter)); + jl_atomic_load_acquire(&jl_world_counter), + NULL); JL_GC_POP(); return native_code; } @@ -389,7 +390,7 @@ static void *jl_precompile_trimmed(size_t world) jl_cgparams_t params = jl_default_cgparams; params.trim = jl_options.trim; void *native_code = jl_create_native(m, NULL, ¶ms, 0, /* imaging */ 1, 0, - world); + world, NULL); JL_GC_POP(); return native_code; } diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index f7961a205e0b1..daa01f626aeab 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -142,13 +142,13 @@ function repl_workload() # wait for the definitive prompt before start writing to the TTY check_errors(readuntil(output_copy, JULIA_PROMPT)) write(debug_output, "\n#### REPL STARTED ####\n") - sleep(0.1) + sleep(0.01) check_errors(readavailable(output_copy)) # Input our script precompile_lines = split(repl_script::String, '\n'; keepempty=false) curr = 0 for l in precompile_lines - sleep(0.1) + sleep(0.01) # try to let a bit of output accumulate before reading again curr += 1 # consume any other output bytesavailable(output_copy) > 0 && check_errors(readavailable(output_copy)) @@ -168,7 +168,7 @@ function repl_workload() occursin(PKG_PROMPT, strbuf) && break occursin(SHELL_PROMPT, strbuf) && break occursin(HELP_PROMPT, strbuf) && break - sleep(0.1) + sleep(0.01) # try to let a bit of output accumulate before reading again end notify(repl_init_event) check_errors(strbuf) @@ -187,37 +187,15 @@ function repl_workload() nothing end -# Copied from PrecompileTools.jl let - function check_edges(node) - parentmi = node.mi_info.mi - for child in node.children - childmi = child.mi_info.mi - if !(isdefined(childmi, :backedges) && parentmi ∈ childmi.backedges) - precompile(childmi.specTypes) - end - check_edges(child) - end - end - if Base.generating_output() && Base.JLOptions().use_pkgimages != 0 - Core.Compiler.Timings.reset_timings() - Core.Compiler.__set_measure_typeinf(true) - try - repl_workload() - finally - Core.Compiler.__set_measure_typeinf(false) - Core.Compiler.Timings.close_current_timer() - end - roots = Core.Compiler.Timings._timings[1].children - for child in roots - precompile(child.mi_info.mi.specTypes) - check_edges(child) - end + repl_workload() precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Any, Int}) precompile(Tuple{typeof(Base.delete!), Base.Set{Any}, String}) precompile(Tuple{typeof(Base.:(==)), Char, String}) - precompile(Tuple{typeof(Base.reseteof), Base.TTY}) + #for child in copy(Base.newly_inferred) + # precompile((child::Base.CodeInstance).def) + #end end end