diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index bb68ef117649ea..b4ce45c228f9c4 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -275,12 +275,6 @@ any_ambig(info::MethodMatchInfo) = any_ambig(info.results) any_ambig(m::MethodMatches) = any_ambig(m.info) fully_covering(info::MethodMatchInfo) = info.fullmatch fully_covering(m::MethodMatches) = fully_covering(m.info) -function add_uncovered_edges!(sv::AbsIntState, info::MethodMatchInfo, @nospecialize(atype)) - fully_covering(info) || add_mt_backedge!(sv, info.mt, atype) - nothing -end -add_uncovered_edges!(sv::AbsIntState, matches::MethodMatches, @nospecialize(atype)) = - add_uncovered_edges!(sv, matches.info, atype) struct UnionSplitMethodMatches applicable::Vector{Any} @@ -292,23 +286,14 @@ any_ambig(info::UnionSplitInfo) = any(any_ambig, info.split) any_ambig(m::UnionSplitMethodMatches) = any_ambig(m.info) fully_covering(info::UnionSplitInfo) = all(fully_covering, info.split) fully_covering(m::UnionSplitMethodMatches) = fully_covering(m.info) -function add_uncovered_edges!(sv::AbsIntState, info::UnionSplitInfo, @nospecialize(atype)) - all(fully_covering, info.split) && return nothing - # add mt backedges with removing duplications - for mt in uncovered_method_tables(info) - add_mt_backedge!(sv, mt, atype) - end -end -add_uncovered_edges!(sv::AbsIntState, matches::UnionSplitMethodMatches, @nospecialize(atype)) = - add_uncovered_edges!(sv, matches.info, atype) -function uncovered_method_tables(info::UnionSplitInfo) - mts = MethodTable[] + +nmatches(info::MethodMatchInfo) = length(info.results) +function nmatches(info::UnionSplitInfo) + n = 0 for mminfo in info.split - fully_covering(mminfo) && continue - any(mt′::MethodTable->mt′===mminfo.mt, mts) && continue - push!(mts, mminfo.mt) + n += nmatches(mminfo) end - return mts + return n end function find_method_matches(interp::AbstractInterpreter, argtypes::Vector{Any}, @nospecialize(atype); diff --git a/base/compiler/stmtinfo.jl b/base/compiler/stmtinfo.jl index 03458644d76da0..8ec9e83278e1ba 100644 --- a/base/compiler/stmtinfo.jl +++ b/base/compiler/stmtinfo.jl @@ -22,6 +22,7 @@ struct CallMeta end struct NoCallInfo <: CallInfo end +add_edges_impl(::Vector{Any}, ::NoCallInfo) = nothing """ info::MethodMatchInfo <: CallInfo @@ -37,6 +38,56 @@ struct MethodMatchInfo <: CallInfo atype fullmatch::Bool end +function add_edges_impl(edges::Vector{Any}, info::MethodMatchInfo) + matches = info.results.matches + if !fully_covering(info) + # 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 + exists = true + break + end + end + if !exists + push!(edges, info.mt) + push!(edges, info.atype) + end + end + if length(matches) == 1 + # try the optimized format for the representation, if possible and applicable + # if this doesn't succeed, the backedge will be less precise, + # but the forward edge will maintain the precision + m = matches[1]::Core.MethodMatch + mi = specialize_method(m) + if mi.specTypes === m.spec_types + add_one_edge!(edges, mi) + return nothing + end + end + # add check for whether this lookup already existed in the edges list + for i in 1:length(edges) + if edges[i] === length(matches) && edges[i + 1] == info.atype + return nothing + end + end + push!(edges, length(matches)) + push!(edges, info.atype) + for m in matches + mi = specialize_method(m::Core.MethodMatch) + push!(edges, mi) + end + 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 nsplit_impl(info::MethodMatchInfo) = 1 getsplit_impl(info::MethodMatchInfo, idx::Int) = (@assert idx == 1; info.results) getresult_impl(::MethodMatchInfo, ::Int) = nothing @@ -57,15 +108,8 @@ This info is illegal on any statement that is not a call to a generic function. struct UnionSplitInfo <: CallInfo split::Vector{MethodMatchInfo} end - -nmatches(info::MethodMatchInfo) = length(info.results) -function nmatches(info::UnionSplitInfo) - n = 0 - for mminfo in info.split - n += nmatches(mminfo) - end - return n -end +add_edges_impl(edges::Vector{Any}, info::UnionSplitInfo) = + for split in info.split; add_edges!(edges, split); end nsplit_impl(info::UnionSplitInfo) = length(info.split) getsplit_impl(info::UnionSplitInfo, idx::Int) = getsplit(info.split[idx], 1) getresult_impl(::UnionSplitInfo, ::Int) = nothing @@ -76,6 +120,15 @@ function add_uncovered_edges_impl(edges::Vector{Any}, info::UnionSplitInfo, @nos push!(edges, mt, atype) end end +function uncovered_method_tables(info::UnionSplitInfo) + mts = MethodTable[] + for mminfo in info.split + fully_covering(mminfo) && continue + any(mt′::MethodTable->mt′===mminfo.mt, mts) && continue + push!(mts, mminfo.mt) + end + return mts +end abstract type ConstResult end @@ -116,6 +169,8 @@ struct ConstCallInfo <: CallInfo call::Union{MethodMatchInfo,UnionSplitInfo} results::Vector{Union{Nothing,ConstResult}} end +add_edges_impl(edges::Vector{Any}, info::ConstCallInfo) = + add_edges!(edges, info.call) nsplit_impl(info::ConstCallInfo) = nsplit(info.call) getsplit_impl(info::ConstCallInfo, idx::Int) = getsplit(info.call, idx) getresult_impl(info::ConstCallInfo, idx::Int) = info.results[idx] @@ -135,6 +190,8 @@ let instance = MethodResultPure(NoCallInfo()) global MethodResultPure MethodResultPure() = instance end +add_edges_impl(edges::Vector{Any}, info::MethodResultPure) = + add_edges!(edges, info.info) """ ainfo::AbstractIterationInfo @@ -161,10 +218,21 @@ not an `_apply_iterate` call. """ struct ApplyCallInfo <: CallInfo # The info for the call itself - call::Any + call::CallInfo # AbstractIterationInfo for each argument, if applicable arginfo::Vector{MaybeAbstractIterationInfo} end +function add_edges_impl(edges::Vector{Any}, info::ApplyCallInfo) + add_edges!(edges, info.call) + for arg in info.arginfo + arg === nothing && continue + for edge in arg.each + add_edges!(edges, edge.info) + end + end +end +# N.B. `ApplyCallInfo` doesn't need to implement the interfaces for the inlining, +# since `Core._apply_iterate` is handled by the special case """ info::UnionSplitApplyCallInfo <: CallInfo @@ -175,6 +243,10 @@ This info is illegal on any statement that is not an `_apply_iterate` call. struct UnionSplitApplyCallInfo <: CallInfo infos::Vector{ApplyCallInfo} end +add_edges_impl(edges::Vector{Any}, info::UnionSplitApplyCallInfo) = + for split in info.infos; add_edges!(edges, split); end +# N.B. `UnionSplitApplyCallInfo` doesn't need to implement the interfaces for the inlining, +# since `Core._apply_iterate` is handled by the special case """ info::InvokeCallInfo @@ -188,6 +260,20 @@ struct InvokeCallInfo <: CallInfo result::Union{Nothing,ConstResult} atype # ::Type end +add_edges_impl(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] == atype + return + end + end + push!(edges, atype) + push!(edges, mi) + nothing +end +# N.B. `InvokeCallInfo` doesn't need to implement the interfaces for the inlining, +# since `Core.invoke` is handled by the special case """ info::OpaqueClosureCallInfo @@ -200,6 +286,10 @@ struct OpaqueClosureCallInfo <: CallInfo match::MethodMatch result::Union{Nothing,ConstResult} end +add_edges_impl(edges::Vector{Any}, info::OpaqueClosureCallInfo) = + add_one_edge!(edges, specialize_method(info.match)) +# N.B. `OpaqueClosureCallInfo` doesn't need to implement the interfaces for the inlining, +# since `Core.invoke` is handled by the special case """ info::OpaqueClosureCreateInfo <: CallInfo @@ -216,6 +306,9 @@ struct OpaqueClosureCreateInfo <: CallInfo return new(unspec) end end +# merely creating the object implies edges for OC, unlike normal objects, +# since calling them doesn't normally have edges in contrast +add_edges_impl(edges::Vector{Any}, info::OpaqueClosureCreateInfo) = add_edges!(edges, info.unspec.info) # Stmt infos that are used by external consumers, but not by optimization. # These are not produced by default and must be explicitly opted into by @@ -231,6 +324,7 @@ was supposed to analyze. struct ReturnTypeCallInfo <: CallInfo info::CallInfo end +add_edges_impl(edges::Vector{Any}, info::ReturnTypeCallInfo) = add_edges!(edges, info.info) """ info::FinalizerInfo <: CallInfo @@ -242,6 +336,8 @@ struct FinalizerInfo <: CallInfo info::CallInfo # the callinfo for the finalizer call effects::Effects # the effects for the finalizer call end +# merely allocating a finalizer does not imply edges (unless it gets inlined later) +add_edges_impl(edges::Vector{Any}, info::FinalizerInfo) = nothing """ info::ModifyOpInfo <: CallInfo @@ -257,5 +353,6 @@ Represents a resolved call of one of: struct ModifyOpInfo <: CallInfo info::CallInfo # the callinfo for the `op(getval(), x)` call end +add_edges_impl(edges::Vector{Any}, info::ModifyOpInfo) = add_edges!(edges, info.info) @specialize diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index bbec2451608541..fdafb3a9634ada 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -3024,7 +3024,10 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv update_valid_age!(sv, valid_worlds) if match === nothing rt = Const(false) - add_edges!(sv.edges, MethodMatchInfo(MethodLookupResult(Any[], valid_worlds, true), types, mt)) # XXX: this should actually be an invoke-type backedge + let vresults = MethodLookupResult(Any[], valid_worlds, true) + vinfo = MethodMatchInfo(vresults, mt, types, false) + add_edges!(sv.edges, vinfo) # XXX: this should actually be an invoke-type backedge + end else rt = Const(true) add_edges!(sv.edges, InvokeCallInfo(match, nothing, types)) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 4ae11ca24259a8..4aba45912d7899 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -213,7 +213,7 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState; result = caller.result opt = result.src if opt isa OptimizationState - result.src = opt = ir_to_codeinf!(opt) + result.src = ir_to_codeinf!(opt) end valid_worlds = result.valid_worlds if last(valid_worlds) >= get_world_counter() @@ -292,8 +292,8 @@ end function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cycleid::Int) cycle_valid_worlds = WorldRange() cycle_valid_effects = EFFECTS_TOTAL - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState @assert caller.cycleid == cycleid # converge the world age range and effects for this cycle here: # all frames in the cycle should have the same bits of `valid_worlds` and `effects` @@ -302,20 +302,20 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cyclei cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects) finishinfer!(caller, caller.interp) end - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState opt = caller.result.src if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` optimize(caller.interp, opt, caller.result) end end - for caller in cycleid:length(frames) - caller = frames[caller]::InferenceState + for frameid = cycleid:length(frames) + caller = frames[frameid]::InferenceState finish!(caller.interp, caller) end resize!(frames, cycleid - 1) @@ -647,100 +647,10 @@ function store_backedges(caller::MethodInstance, edges::Vector{Any}) return nothing 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) = add_edges!(edges, info.unspec.info) # merely creating the object implies edges for OC, unlike normal objects, since calling them doesn't normally have edges in contrast -add_edges!(edges::Vector{Any}, info::OpaqueClosureCallInfo) = add_one_edge!(edges, specialize_method(info.match)) -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) - for arg in info.arginfo - arg === nothing && continue - for edge in arg.each - add_edges!(edges, edge.info) - end - end -end -add_edges!(edges::Vector{Any}, info::ModifyOpInfo) = add_edges!(edges, info.info) -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 # 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 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 - exists = true - break - end - end - if !exists - push!(edges, info.mt) - push!(edges, info.atype) - end - end - if length(matches) == 1 - # try the optimized format for the representation, if possible and applicable - # if this doesn't succeed, the backedge will be less precise, - # but the forward edge will maintain the precision - m = matches[1]::Core.MethodMatch - mi = specialize_method(m) - if mi.specTypes === m.spec_types - add_one_edge!(edges, mi) - return - end - end - # add check for whether this lookup already existed in the edges list - for i in 1:length(edges) - if edges[i] === length(matches) && edges[i + 1] == info.atype - return - end - end - push!(edges, length(matches)) - push!(edges, info.atype) - for m in matches - mi = specialize_method(m::Core.MethodMatch) - push!(edges, mi) - end - nothing -end -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] == atype - return - end - end - 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 = sv.edges for i in 1:length(sv.stmt_info) - info = sv.stmt_info[i] - #rt = sv.ssavaluetypes[i] - #et = sv.exectiontypes[i] - #effects = EFFECTS_TOTAL # TODO: sv.stmt_effects[i] - #if rt === Any && et === Any && effects === Effects() - # continue - #end - add_edges!(edges, info) + add_edges!(edges, sv.stmt_info[i]) end if sv.src.edges !== nothing && sv.src.edges !== Core.svec() append!(edges, sv.src.edges) @@ -748,7 +658,6 @@ function compute_edges!(sv::InferenceState) nothing end - function record_slot_assign!(sv::InferenceState) # look at all assignments to slots # and union the set of types stored there @@ -1154,13 +1063,13 @@ function typeinf_frame(interp::AbstractInterpreter, mi::MethodInstance, run_opti if run_optimizer if result_is_constabi(interp, frame.result) rt = frame.result.result::Const - opt = codeinfo_for_const(interp, frame.linfo, rt.val) + src = codeinfo_for_const(interp, frame.linfo, rt.val) else opt = OptimizationState(frame, interp) optimize(interp, opt, frame.result) - opt = ir_to_codeinf!(opt) + src = ir_to_codeinf!(opt) end - result.src = frame.src = opt + result.src = frame.src = src end return frame end diff --git a/base/compiler/types.jl b/base/compiler/types.jl index 6853542bd66f0a..02d06a85b91369 100644 --- a/base/compiler/types.jl +++ b/base/compiler/types.jl @@ -447,18 +447,21 @@ abstract type CallInfo end @nospecialize +add_edges!(edges::Vector{Any}, info::CallInfo) = add_edges_impl(edges, info) nsplit(info::CallInfo) = nsplit_impl(info)::Union{Nothing,Int} getsplit(info::CallInfo, idx::Int) = getsplit_impl(info, idx)::MethodLookupResult add_uncovered_edges!(edges::Vector{Any}, info::CallInfo, @nospecialize(atype)) = add_uncovered_edges_impl(edges, info, atype) - -getresult(info::CallInfo, idx::Int) = getresult_impl(info, idx) - -# must implement `nsplit`, `getsplit`, and `add_uncovered_edges!` to opt in to inlining -nsplit_impl(::CallInfo) = nothing -getsplit_impl(::CallInfo, ::Int) = error("unexpected call into `getsplit`") -add_uncovered_edges_impl(::Vector{Any}, ::CallInfo, _) = error("unexpected call into `add_uncovered_edges!`") - -# must implement `getresult` to opt in to extended lattice return information +getresult(info::CallInfo, idx::Int) = getresult_impl(info, idx)#=::Union{Nothing,ConstResult}=# + +add_edges_impl(::Vector{Any}, ::CallInfo) = error(""" + All `CallInfo` is required to implement `add_edges_impl(::Vector{Any}, ::CallInfo)`""") +nsplit_impl(info::CallInfo) = nothing +getsplit_impl(info::CallInfo, idx::Int) = error(""" + A `info::CallInfo` that implements `nsplit_impl(info::CallInfo) -> Int` must implement `getsplit_impl(info::CallInfo, idx::Int) -> MethodLookupResult` + in order to correctly opt in to inlining""") +add_uncovered_edges_impl(edges::Vector{Any}, info::CallInfo, atype) = error(""" + A `info::CallInfo` that implements `nsplit_impl(info::CallInfo) -> Int` must implement `add_uncovered_edges_impl(edges::Vector{Any}, info::CallInfo, atype)` + in order to correctly opt in to inlining""") getresult_impl(::CallInfo, ::Int) = nothing @specialize