diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index c886eddde2d3e..d2657bcfdf405 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -178,7 +178,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), rettype = excttype = Any all_effects = Effects() elseif isa(matches, MethodMatches) ? (!matches.fullmatch || any_ambig(matches)) : - (!all(matches.fullmatches) || any_ambig(matches)) + (!matches.fullmatch || any_ambig(matches)) # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. all_effects = Effects(all_effects; nothrow=false) excttype = tmerge(𝕃ₚ, excttype, MethodError) @@ -213,7 +213,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # and avoid keeping track of a more complex result type. rettype = Any end - add_call_backedges!(interp, rettype, all_effects, edges, matches, atype, sv) if isa(sv, InferenceState) # TODO (#48913) implement a proper recursion handling for irinterp: # This works just because currently the `:terminate` condition guarantees that @@ -249,8 +248,7 @@ struct UnionSplitMethodMatches applicable_argtypes::Vector{Vector{Any}} info::UnionSplitInfo valid_worlds::WorldRange - mts::Vector{MethodTable} - fullmatches::Vector{Bool} + fullmatch::Bool end any_ambig(m::UnionSplitMethodMatches) = any(any_ambig, m.info.matches) @@ -274,8 +272,7 @@ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes:: applicable = Any[] applicable_argtypes = Vector{Any}[] # arrays like `argtypes`, including constants, for each match valid_worlds = WorldRange() - mts = MethodTable[] - fullmatches = Bool[] + fullmatch = true for i in 1:length(split_argtypes) arg_n = split_argtypes[i]::Vector{Any} sig_n = argtypes_to_type(arg_n) @@ -292,23 +289,11 @@ function find_union_split_method_matches(interp::AbstractInterpreter, argtypes:: push!(applicable_argtypes, arg_n) end valid_worlds = intersect(valid_worlds, matches.valid_worlds) - thisfullmatch = any(match::MethodMatch->match.fully_covers, matches) - found = false - for (i, mt′) in enumerate(mts) - if mt′ === mt - fullmatches[i] &= thisfullmatch - found = true - break - end - end - if !found - push!(mts, mt) - push!(fullmatches, thisfullmatch) - end + fullmatch = fullmatch && any(match::MethodMatch->match.fully_covers, matches) end info = UnionSplitInfo(infos) return UnionSplitMethodMatches( - applicable, applicable_argtypes, info, valid_worlds, mts, fullmatches) + applicable, applicable_argtypes, info, valid_worlds, fullmatch) end function find_simple_method_matches(interp::AbstractInterpreter, @nospecialize(atype), max_methods::Int) @@ -492,34 +477,6 @@ function conditional_argtype(𝕃ᵢ::AbstractLattice, @nospecialize(rt), @nospe end end -function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype), all_effects::Effects, - edges::Vector{MethodInstance}, matches::Union{MethodMatches,UnionSplitMethodMatches}, @nospecialize(atype), - sv::AbsIntState) - # don't bother to add backedges when both type and effects information are already - # maximized to the top since a new method couldn't refine or widen them anyway - if rettype === Any - # ignore the `:nonoverlayed` property if `interp` doesn't use overlayed method table - # since it will never be tainted anyway - if !isoverlayed(method_table(interp)) - all_effects = Effects(all_effects; nonoverlayed=ALWAYS_FALSE) - end - all_effects === Effects() && return nothing - end - for edge in edges - add_backedge!(sv, edge) - end - # also need an edge to the method table in case something gets - # added that did not intersect with any existing method - if isa(matches, MethodMatches) - matches.fullmatch || add_mt_backedge!(sv, matches.mt, atype) - else - for (thisfullmatch, mt) in zip(matches.fullmatches, matches.mts) - thisfullmatch || add_mt_backedge!(sv, mt, atype) - end - end - return nothing -end - const RECURSION_UNUSED_MSG = "Bounded recursion detected with unused result. Annotated return type may be wider than true result." const RECURSION_MSG = "Bounded recursion detected. Call was widened to force convergence." const RECURSION_MSG_HARDLIMIT = "Bounded recursion detected under hardlimit. Call was widened to force convergence." @@ -2054,7 +2011,6 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn end rt = from_interprocedural!(interp, rt, sv, arginfo, sig) info = InvokeCallInfo(match, const_result, lookupsig) - edge !== nothing && add_invoke_backedge!(sv, lookupsig, edge) return CallMeta(rt, Any, effects, info) end @@ -2232,7 +2188,6 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, end rt = from_interprocedural!(interp, rt, sv, arginfo, match.spec_types) info = OpaqueClosureCallInfo(match, const_result) - edge !== nothing && add_backedge!(sv, edge) return CallMeta(rt, Any, effects, info) end @@ -3225,7 +3180,6 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) for currpc in bbstart:bbend frame.currpc = currpc - empty_backedges!(frame, currpc) stmt = frame.src.code[currpc] # If we're at the end of the basic block ... if currpc == bbend diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index c358b1177251f..36283bcf9146a 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -247,7 +247,7 @@ mutable struct InferenceState # TODO: Could keep this sparsely by doing structural liveness analysis ahead of time. bb_vartables::Vector{Union{Nothing,VarTable}} # nothing if not analyzed yet ssavaluetypes::Vector{Any} - stmt_edges::Vector{Vector{Any}} + edges::Vector{Any} stmt_info::Vector{CallInfo} #= intermediate states for interprocedural abstract interpretation =# @@ -298,7 +298,7 @@ mutable struct InferenceState nssavalues = src.ssavaluetypes::Int ssavalue_uses = find_ssavalue_uses(code, nssavalues) nstmts = length(code) - stmt_edges = Vector{Vector{Any}}(undef, nstmts) + edges = [] stmt_info = CallInfo[ NoCallInfo() for i = 1:nstmts ] nslots = length(src.slotflags) @@ -350,7 +350,7 @@ mutable struct InferenceState this = new( mi, world, mod, sptypes, slottypes, src, cfg, method_info, - currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, + currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, edges, stmt_info, pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, @@ -813,26 +813,9 @@ function add_cycle_backedge!(caller::InferenceState, frame::InferenceState) update_valid_age!(caller, frame.valid_worlds) backedge = (caller, caller.currpc) contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge) - add_backedge!(caller, frame.linfo) return frame end -function get_stmt_edges!(caller::InferenceState, currpc::Int=caller.currpc) - stmt_edges = caller.stmt_edges - if !isassigned(stmt_edges, currpc) - return stmt_edges[currpc] = Any[] - else - return stmt_edges[currpc] - end -end - -function empty_backedges!(frame::InferenceState, currpc::Int=frame.currpc) - if isassigned(frame.stmt_edges, currpc) - empty!(frame.stmt_edges[currpc]) - end - return nothing -end - function print_callstack(sv::InferenceState) print("=================== Callstack: ==================\n") idx = 0 @@ -1017,32 +1000,6 @@ function iterate(unw::AbsIntStackUnwind, (sv, cyclei)::Tuple{AbsIntState, Int}) return (parent, (parent, cyclei)) end -# temporarily accumulate our edges to later add as backedges in the callee -function add_backedge!(caller::InferenceState, mi::MethodInstance) - isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance - return push!(get_stmt_edges!(caller), mi) -end -function add_backedge!(irsv::IRInterpretationState, mi::MethodInstance) - return push!(irsv.edges, mi) -end - -function add_invoke_backedge!(caller::InferenceState, @nospecialize(invokesig::Type), mi::MethodInstance) - isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance - return push!(get_stmt_edges!(caller), invokesig, mi) -end -function add_invoke_backedge!(irsv::IRInterpretationState, @nospecialize(invokesig::Type), mi::MethodInstance) - return push!(irsv.edges, invokesig, mi) -end - -# used to temporarily accumulate our no method errors to later add as backedges in the callee method table -function add_mt_backedge!(caller::InferenceState, mt::MethodTable, @nospecialize(typ)) - isa(caller.linfo.def, Method) || return nothing # don't add backedges to toplevel method instance - return push!(get_stmt_edges!(caller), mt, typ) -end -function add_mt_backedge!(irsv::IRInterpretationState, mt::MethodTable, @nospecialize(typ)) - return push!(irsv.edges, mt, typ) -end - get_curr_ssaflag(sv::InferenceState) = sv.src.ssaflags[sv.currpc] get_curr_ssaflag(sv::IRInterpretationState) = sv.ir.stmts[sv.curridx][:flag] diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 85d4a92b3919a..99fc6f7e40182 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -131,8 +131,7 @@ struct InliningState{Interp<:AbstractInterpreter} interp::Interp end function InliningState(sv::InferenceState, interp::AbstractInterpreter) - edges = sv.stmt_edges[1] - return InliningState(edges, sv.world, interp) + return InliningState(sv.edges, sv.world, interp) end function InliningState(interp::AbstractInterpreter) return InliningState(Any[], get_inference_world(interp), interp) @@ -215,6 +214,7 @@ include("compiler/ssair/irinterp.jl") function ir_to_codeinf!(opt::OptimizationState) (; linfo, src) = opt src = ir_to_codeinf!(src, opt.ir::IRCode) + src.edges = opt.inlining.edges opt.ir = nothing maybe_validate_code(linfo, src, "optimized") return src diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index a77a67ab262de..752ee55e5d24c 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -65,11 +65,11 @@ struct InliningEdgeTracker new(state.edges, invokesig) end -function add_inlining_backedge!((; edges, invokesig)::InliningEdgeTracker, mi::MethodInstance) +function add_inlining_edge!((; edges, invokesig)::InliningEdgeTracker, mi::MethodInstance) if invokesig === nothing - push!(edges, mi) + add_one_edge!(edges, mi) else # invoke backedge - push!(edges, invoke_signature(invokesig), mi) + add_invoke_edge!(edges, invoke_signature(invokesig), mi) end return nothing end @@ -794,8 +794,8 @@ function compileable_specialization(mi::MethodInstance, effects::Effects, return nothing end end - add_inlining_backedge!(et, mi) # to the dispatch lookup - mi_invoke !== mi && push!(et.edges, method.sig, mi_invoke) # add_inlining_backedge to the invoke call, if that is different + add_inlining_edge!(et, mi) # to the dispatch lookup + mi_invoke !== mi && add_invoke_edge!(et.edges, method.sig, mi_invoke) # add_inlining_edge to the invoke call, if that is different return InvokeCase(mi_invoke, effects, info) end @@ -850,7 +850,7 @@ function resolve_todo(mi::MethodInstance, result::Union{Nothing,InferenceResult, inferred_result = get_cached_result(state, mi) end if inferred_result isa ConstantCase - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, mi) return inferred_result end if inferred_result isa InferredResult @@ -874,7 +874,7 @@ function resolve_todo(mi::MethodInstance, result::Union{Nothing,InferenceResult, return compileable_specialization(mi, effects, et, info; compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, mi) ir = inferred_result isa CodeInstance ? retrieve_ir_for_inlining(inferred_result, src) : retrieve_ir_for_inlining(mi, src, preserve_local_sources) return InliningTodo(mi, ir, effects) @@ -891,7 +891,7 @@ function resolve_todo(mi::MethodInstance, @nospecialize(info::CallInfo), flag::U cached_result = get_cached_result(state, mi) if cached_result isa ConstantCase - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, mi) return cached_result end if cached_result isa InferredResult @@ -908,7 +908,7 @@ function resolve_todo(mi::MethodInstance, @nospecialize(info::CallInfo), flag::U src_inlining_policy(state.interp, src, info, flag) || return nothing ir = cached_result isa CodeInstance ? retrieve_ir_for_inlining(cached_result, src) : retrieve_ir_for_inlining(mi, src, preserve_local_sources) - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, mi) return InliningTodo(mi, ir, effects) end @@ -1456,7 +1456,7 @@ function semiconcrete_result_item(result::SemiConcreteResult, return compileable_specialization(mi, result.effects, et, info; compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes) - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, mi) preserve_local_sources = OptimizationParams(state.interp).preserve_local_sources ir = retrieve_ir_for_inlining(mi, result.ir, preserve_local_sources) return InliningTodo(mi, ir, result.effects) diff --git a/base/compiler/ssair/irinterp.jl b/base/compiler/ssair/irinterp.jl index 83881354e494e..00249149655e3 100644 --- a/base/compiler/ssair/irinterp.jl +++ b/base/compiler/ssair/irinterp.jl @@ -434,12 +434,6 @@ function _ir_abstract_constant_propagation(interp::AbstractInterpreter, irsv::IR (nothrow | noub) || break end - if last(irsv.valid_worlds) >= get_world_counter() - # if we aren't cached, we don't need this edge - # but our caller might, so let's just make it anyways - store_backedges(frame_instance(irsv), irsv.edges) - end - return Pair{Any,Tuple{Bool,Bool}}(maybe_singleton_const(ultimate_rt), (nothrow, noub)) end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 6b29c9b2fe949..f86fbadc28b92 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1498,7 +1498,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, if code isa CodeInstance if use_const_api(code) # No code in the function - Nothing to do - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, mi) return true end src = @atomic :monotonic code.inferred @@ -1513,7 +1513,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int, length(src.cfg.blocks) == 1 || return false # Ok, we're committed to inlining the finalizer - add_inlining_backedge!(et, mi) + add_inlining_edge!(et, mi) # TODO: Should there be a special line number node for inlined finalizers? inline_at = ir[SSAValue(idx)][:line] diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 61b7858e641cb..0df48f9b3a4bb 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2944,30 +2944,14 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any}, else (; valid_worlds, applicable) = matches update_valid_age!(sv, valid_worlds) - - # also need an edge to the method table in case something gets - # added that did not intersect with any existing method - if isa(matches, MethodMatches) - matches.fullmatch || add_mt_backedge!(sv, matches.mt, atype) - else - for (thisfullmatch, mt) in zip(matches.fullmatches, matches.mts) - thisfullmatch || add_mt_backedge!(sv, mt, atype) - end - end + add_edges!(sv.edges, matches.info) napplicable = length(applicable) if napplicable == 0 rt = Const(false) # never any matches else rt = Const(true) # has applicable matches - for i in 1:napplicable - match = applicable[i]::MethodMatch - edge = specialize_method(match)::MethodInstance - add_backedge!(sv, edge) - end - - if isa(matches, MethodMatches) ? (!matches.fullmatch || any_ambig(matches)) : - (!all(matches.fullmatches) || any_ambig(matches)) + if !matches.fullmatch || any_ambig(matches) # Account for the fact that we may encounter a MethodError with a non-covered or ambiguous signature. rt = Bool end @@ -3007,11 +2991,10 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv update_valid_age!(sv, valid_worlds) if match === nothing rt = Const(false) - add_mt_backedge!(sv, mt, types) # this should actually be an invoke-type backedge + add_edges!(sv.edges, MethodMatchInfo(MethodLookupResult(Any[], valid_worlds, true), types, mt)) # XXX: this should actually be an invoke-type backedge else rt = Const(true) - edge = specialize_method(match)::MethodInstance - add_invoke_backedge!(sv, types, edge) + add_edges!(sv.edges, InvokeCallInfo(match, nothing, types)) end return CallMeta(rt, Any, EFFECTS_TOTAL, NoCallInfo()) end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 59771944f1257..ab7678bdc42d8 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -211,28 +211,28 @@ end function finish!(interp::AbstractInterpreter, caller::InferenceState; can_discard_trees::Bool=may_discard_trees(interp)) result = caller.result + opt = result.src + if opt isa OptimizationState + result.src = opt = ir_to_codeinf!(opt) + end valid_worlds = result.valid_worlds if last(valid_worlds) >= get_world_counter() # if we aren't cached, we don't need this edge # but our caller might, so let's just make it anyways - store_backedges(result, caller.stmt_edges[1]) - end - opt = result.src - if opt isa OptimizationState - result.src = opt = ir_to_codeinf!(opt) + store_backedges(result, caller.edges) end + isa(caller.linfo.def, Method) || empty!(caller.edges) # don't add backedges to toplevel method instance if isdefined(result, :ci) ci = result.ci inferred_result = nothing - edges = Core.svec() # This should be a computed input, for now it is approximated (badly) here + edges = Core.svec(caller.edges...) relocatability = 0x1 const_flag = is_result_constabi_eligible(result) if !can_discard_trees || (is_cached(caller) && !const_flag) inferred_result = transform_result_for_cache(interp, result.linfo, result.valid_worlds, result, can_discard_trees) + # TODO: do we want to augment edges here with any :invoke targets that we got from inlining (such that we didn't have a direct edge to it already)? relocatability = 0x0 if inferred_result isa CodeInfo - edges = ccall(:jl_ir_edges_legacy, Any, (Any,), inferred_result.code) - inferred_result.edges = edges di = inferred_result.debuginfo uncompressed = inferred_result inferred_result = maybe_compress_codeinfo(interp, result.linfo, inferred_result, can_discard_trees) @@ -520,22 +520,6 @@ end # update the MethodInstance function finishinfer!(me::InferenceState, interp::AbstractInterpreter) # prepare to run optimization passes on fulltree - s_edges = get_stmt_edges!(me, 1) - for i = 2:length(me.stmt_edges) - isassigned(me.stmt_edges, i) || continue - edges = me.stmt_edges[i] - append!(s_edges, edges) - empty!(edges) - end - #s_edges_new = compute_edges(me) - #println(Any[z isa MethodTable ? z.name : z for z in s_edges_new]) - #if length(s_edges) != length(s_edges_new) || !all(i -> isassigned(s_edges, i) ? isassigned(s_edges_new, i) && s_edges_new[i] === s_edges[i] : !isassigned(s_edges_new, i), length(s_edges)) - # println(sizehint!(s_edges, length(s_edges))) - # println(sizehint!(s_edges_new, length(s_edges_new))) - #end - if me.src.edges !== nothing && me.src.edges !== Core.svec() - append!(s_edges, me.src.edges::Vector) - end # inspect whether our inference had a limited result accuracy, # else it may be suitable to cache bestguess = me.bestguess = cycle_fix_limited(me.bestguess, me) @@ -560,6 +544,9 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) me.src.rettype = widenconst(ignorelimited(bestguess)) me.src.min_world = first(me.valid_worlds) me.src.max_world = last(me.valid_worlds) + if isa(me.linfo.def, Method) # don't add backedges to toplevel method instance + compute_edges!(me) + end if limited_ret # a parent may be cached still, but not this intermediate work: @@ -590,6 +577,7 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) end maybe_validate_code(me.linfo, me.src, "inferred") + isa(me.linfo.def, Method) || empty!(me.edges) # don't add backedges to toplevel method instance # finish populating inference results into the CodeInstance if possible, and maybe cache that globally for use elsewhere if isdefined(result, :ci) && !limited_ret @@ -619,7 +607,7 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) end relocatability = 0x0 di = nothing - edges = Core.svec() + edges = Core.svec(me.edges...) ccall(:jl_fill_codeinst, Cvoid, (Any, Any, Any, Any, Int32, UInt, UInt, UInt32, Any, Any, Any), result.ci, widenconst(result_type), widenconst(result.exc_result), rettype_const, const_flags, first(result.valid_worlds), last(result.valid_worlds), @@ -652,7 +640,8 @@ end add_edges!(edges::Vector{Any}, info::MethodResultPure) = add_edges!(edges, info.info) add_edges!(edges::Vector{Any}, info::ConstCallInfo) = add_edges!(edges, info.call) -add_edges!(edges::Vector{Any}, info::OpaqueClosureCreateInfo) = nothing # TODO(jwn) +add_edges!(edges::Vector{Any}, info::OpaqueClosureCreateInfo) = nothing # merely creating the object does not imply edges +add_edges!(edges::Vector{Any}, info::OpaqueClosureCallInfo) = nothing # TODO: inference appears to have always mis-accounted for these backedges add_edges!(edges::Vector{Any}, info::ReturnTypeCallInfo) = add_edges!(edges, info.info) function add_edges!(edges::Vector{Any}, info::ApplyCallInfo) add_edges!(edges, info.call) @@ -666,20 +655,21 @@ end add_edges!(edges::Vector{Any}, info::ModifyOpInfo) = add_edges!(edges, info.call) add_edges!(edges::Vector{Any}, info::UnionSplitInfo) = for split in info.matches; add_edges!(edges, split); end add_edges!(edges::Vector{Any}, info::UnionSplitApplyCallInfo) = for split in info.infos; add_edges!(edges, split); end -add_edges!(edges::Vector{Any}, info::FinalizerInfo) = nothing +add_edges!(edges::Vector{Any}, info::FinalizerInfo) = nothing # merely allocating a finalizer does not imply edges (unless it gets inlined later) add_edges!(edges::Vector{Any}, info::NoCallInfo) = nothing function add_edges!(edges::Vector{Any}, info::MethodMatchInfo) matches = info.results.matches - #if length(matches) == 1 && !info.results.ambig && (matches[end]::Core.MethodMatch).fully_covers - # push!(edges, specialize_method(matches[1])) - #elseif isempty(matches) || info.results.ambig || !(matches[end]::Core.MethodMatch).fully_covers - #else - # push!(edges, length(matches)) - # for m in matches - # push!(edges, specialize_method(m)) - # end - #end + if length(matches) != 1 + # TODO: add check for whether this info already exists in the edges + push!(edges, length(matches)) + push!(edges, info.atype) + end + for m in matches + mi = specialize_method(m) + length(matches) == 1 ? add_one_edge!(edges, mi) : push!(edges, mi) + end if isempty(matches) || !(matches[end]::Core.MethodMatch).fully_covers + # add legacy-style missing backedge info also exists = false for i in 1:length(edges) if edges[i] === info.mt && edges[i + 1] == info.atype @@ -692,37 +682,34 @@ function add_edges!(edges::Vector{Any}, info::MethodMatchInfo) push!(edges, info.atype) end end - for m in matches - mi = specialize_method(m) - exists = false - for i in 1:length(edges) - if edges[i] === mi && !(i > 1 && edges[i - 1] isa Type) - exists = true - break - end - end - exists || push!(edges, mi) - end + nothing end -function add_edges!(edges::Vector{Any}, info::InvokeCallInfo) - #push!(edges, 1) - mi = specialize_method(info.match) - exists = false +add_edges!(edges::Vector{Any}, info::InvokeCallInfo) = add_invoke_edge!(edges, info.atype, specialize_method(info.match)) + +function add_invoke_edge!(edges::Vector{Any}, @nospecialize(atype), mi::MethodInstance) for i in 2:length(edges) - if edges[i] === mi && edges[i - 1] isa Type && edges[i - 1] == info.atype - exists = true - break + if edges[i] === mi && edges[i - 1] isa Type && edges[i - 1] == atype + return end end - if !exists - push!(edges, info.atype) - push!(edges, mi) + push!(edges, atype) + push!(edges, mi) + nothing +end + +function add_one_edge!(edges::Vector{Any}, mi::MethodInstance) + for i in 1:length(edges) + if edges[i] === mi && !(i > 1 && edges[i - 1] isa Type) + return + end end + push!(edges, mi) nothing end -function compute_edges(sv::InferenceState) - edges = [] + +function compute_edges!(sv::InferenceState) + edges = sv.edges for i in 1:length(sv.stmt_info) info = sv.stmt_info[i] #rt = sv.ssavaluetypes[i] @@ -732,7 +719,10 @@ function compute_edges(sv::InferenceState) #end add_edges!(edges, info) end - return edges + if sv.src.edges !== nothing && sv.src.edges !== Core.svec() + append!(edges, sv.src.edges) + end + nothing end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 527c6ab42eb2d..c37824cd4e37a 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -355,11 +355,14 @@ end function iterate(iter::BackedgeIterator, i::Int=1) backedges = iter.backedges - i > length(backedges) && return nothing - item = backedges[i] - isa(item, MethodInstance) && return BackedgePair(nothing, item), i+1 # regular dispatch - isa(item, MethodTable) && return BackedgePair(backedges[i+1], item), i+2 # abstract dispatch - return BackedgePair(item, backedges[i+1]::MethodInstance), i+2 # `invoke` calls + while true + i > length(backedges) && return nothing + item = backedges[i] + item isa Int && (i += 2; continue) # ignore the query information if present + isa(item, MethodInstance) && return BackedgePair(nothing, item), i+1 # regular dispatch + isa(item, MethodTable) && return BackedgePair(backedges[i+1], item), i+2 # abstract dispatch + return BackedgePair(item, backedges[i+1]::MethodInstance), i+2 # `invoke` calls + end end ######### diff --git a/src/ircode.c b/src/ircode.c index df11d06e64a17..bec8d46513eef 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -868,37 +868,6 @@ typedef enum { #define checked_size(data, macro_size) \ (declaration_context(static_assert(sizeof(data) == macro_size, #macro_size " does not match written size")), data) -// n.b. this does not compute edges correctly, but is just a temporary legacy helper while porting -JL_DLLEXPORT jl_value_t *jl_ir_edges_legacy(jl_array_t *src) -{ - arraylist_t edges; - arraylist_new(&edges, 0); - for (size_t i = 0; i < jl_array_dim0(src); i++) { - jl_value_t *v = jl_array_ptr_ref(src, i); - if (jl_is_expr(v)) { - jl_expr_t *e = (jl_expr_t*)v; - if (e->head == jl_assign_sym && jl_expr_nargs(e) == 2 && jl_is_expr(jl_exprarg(e, 1))) { - e = (jl_expr_t*)jl_exprarg(e, 1); - } - if (e->head == jl_invoke_sym) { - jl_value_t *target = jl_array_ptr_ref(e->args, 0); - if (jl_is_code_instance(target) || jl_is_method_instance(target)) { - size_t j; - for (j = 0; j < edges.len; j++) - if (edges.items[j] == (void*)target) - break; - if (j == edges.len) - arraylist_push(&edges, target); - } - } - } - } - jl_value_t *e = jl_f_svec(NULL, (jl_value_t**)edges.items, edges.len); - arraylist_free(&edges); - return e; -} - - JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) { JL_TIMING(AST_COMPRESS, AST_COMPRESS);