Skip to content

Commit

Permalink
make add_edges! proper CallInfo interface
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk committed Sep 25, 2024
1 parent 3a23d14 commit c83f06d
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 145 deletions.
27 changes: 6 additions & 21 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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);
Expand Down
117 changes: 107 additions & 10 deletions base/compiler/stmtinfo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct CallMeta
end

struct NoCallInfo <: CallInfo end
add_edges_impl(::Vector{Any}, ::NoCallInfo) = nothing

"""
info::MethodMatchInfo <: CallInfo
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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]
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
5 changes: 4 additions & 1 deletion base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Loading

0 comments on commit c83f06d

Please sign in to comment.