Skip to content

Commit

Permalink
make CallInfo propagate the edges list of CodeInstances
Browse files Browse the repository at this point in the history
Remaining TODOs:
- Finalize the format for `sv.edges`. There might be cases where no
  `edge::CodeInstance` exists as a result of `abstract_call_method`,
  and in such cases, we might still need to use `MethodInstance` in the
  `edges` list.
- Ensure that when the local caching mode is specified (i.e. for
  const-prop'ed calls and call-site-inlined calls), the const-propped
  edge should be propagated instead of the regular edge.
- Make use of the `CodeInstance` held by `CallInfo` during inlining
  for slightly better performance by avoiding the global cache lookup.
  • Loading branch information
aviatesk committed Oct 25, 2024
1 parent 711ea69 commit 39b83ce
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 167 deletions.
108 changes: 58 additions & 50 deletions base/compiler/abstractinterpretation.jl

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ struct InliningEdgeTracker
new(state.edges, invokesig)
end

function add_inlining_edge!((; edges, invokesig)::InliningEdgeTracker, mi::MethodInstance)
function add_inlining_edge!(et::InliningEdgeTracker, mi::MethodInstance)
(; edges, invokesig) = et
if invokesig === nothing
add_one_edge!(edges, mi)
else # invoke backedge
Expand Down Expand Up @@ -1119,7 +1120,7 @@ function inline_apply!(todo::Vector{Pair{Int,Any}},
# e.g. rewrite `((t::Tuple)...,)` to `t`
nonempty_idx = 0
𝕃ₒ = optimizer_lattice(state.interp)
for i = (arg_start + 1):length(argtypes)
for i = (arg_start+1):length(argtypes)
ti = argtypes[i]
(𝕃ₒ, ti, Tuple{}) && continue
if (𝕃ₒ, ti, Tuple) && nonempty_idx == 0
Expand All @@ -1137,7 +1138,7 @@ function inline_apply!(todo::Vector{Pair{Int,Any}},
# Try to figure out the signature of the function being called
# and if rewrite_apply_exprargs can deal with this form
arginfos = MaybeAbstractIterationInfo[]
for i = (arg_start + 1):length(argtypes)
for i = (arg_start+1):length(argtypes)
thisarginfo = nothing
if !is_valid_type_for_apply_rewrite(argtypes[i], OptimizationParams(state.interp))
isa(info, ApplyCallInfo) || return nothing
Expand Down Expand Up @@ -1455,7 +1456,7 @@ end

function semiconcrete_result_item(result::SemiConcreteResult,
@nospecialize(info::CallInfo), flag::UInt32, state::InliningState)
mi = result.mi
mi = result.edge.def
et = InliningEdgeTracker(state)

if (!OptimizationParams(state.interp).inlining || is_stmt_noinline(flag) ||
Expand All @@ -1478,7 +1479,7 @@ end

function handle_semi_concrete_result!(cases::Vector{InliningCase}, result::SemiConcreteResult,
match::MethodMatch, @nospecialize(info::CallInfo), flag::UInt32, state::InliningState)
mi = result.mi
mi = result.edge.def
spec_types = match.spec_types
validate_sparams(mi.sparam_vals) || return false
item = semiconcrete_result_item(result, info, flag, state)
Expand All @@ -1502,7 +1503,7 @@ function concrete_result_item(result::ConcreteResult, @nospecialize(info::CallIn
invokesig::Union{Nothing,Vector{Any}}=nothing)
if !may_inline_concrete_result(result)
et = InliningEdgeTracker(state, invokesig)
return compileable_specialization(result.mi, result.effects, et, info;
return compileable_specialization(result.edge.def, result.effects, et, info;
compilesig_invokes=OptimizationParams(state.interp).compilesig_invokes)
end
@assert result.effects === EFFECTS_TOTAL
Expand Down
96 changes: 56 additions & 40 deletions base/compiler/stmtinfo.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,51 +37,58 @@ struct MethodMatchInfo <: CallInfo
mt::MethodTable
atype
fullmatch::Bool
edges::Vector{Union{Nothing,CodeInstance}}
function MethodMatchInfo(
results::MethodLookupResult, mt::MethodTable, @nospecialize(atype), fullmatch::Bool)
edges = fill!(Vector{Union{Nothing,CodeInstance}}(undef, length(results)), nothing)
return new(results, mt, atype, fullmatch, edges)
end
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
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)
push!(edges, info.mt, 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
nmatches = length(info.results)
if nmatches == length(info.edges) == 1
edge = info.edges[1]
if edge !== nothing
# 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
if edge.def.specTypes === info.results[1].spec_types
add_one_edge!(edges, edge.def) # TODO CodeInstance as edge
return nothing
end
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
if edges[i] === nmatches && 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)
push!(edges, nmatches, info.atype)
for i = 1:nmatches
edge = info.edges[i]
if edge !== nothing
push!(edges, edge.def) # TODO CodeInstance as edge
end
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)
if edges[i] === mi && !(i > 1 && edges[i-1] isa Type)
return
end
end
Expand Down Expand Up @@ -137,15 +144,15 @@ struct ConstPropResult <: ConstResult
end

struct ConcreteResult <: ConstResult
mi::MethodInstance
edge::CodeInstance
effects::Effects
result
ConcreteResult(mi::MethodInstance, effects::Effects) = new(mi, effects)
ConcreteResult(mi::MethodInstance, effects::Effects, @nospecialize val) = new(mi, effects, val)
ConcreteResult(edge::CodeInstance, effects::Effects) = new(edge, effects)
ConcreteResult(edge::CodeInstance, effects::Effects, @nospecialize val) = new(edge, effects, val)
end

struct SemiConcreteResult <: ConstResult
mi::MethodInstance
edge::CodeInstance
ir::IRCode
effects::Effects
spec_info::SpecInfo
Expand Down Expand Up @@ -228,8 +235,6 @@ function add_edges_impl(edges::Vector{Any}, info::ApplyCallInfo)
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 @@ -242,8 +247,6 @@ struct UnionSplitApplyCallInfo <: CallInfo
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 @@ -253,24 +256,29 @@ the method that has been processed.
Optionally keeps `info.result::InferenceResult` that keeps constant information.
"""
struct InvokeCallInfo <: CallInfo
edge::Union{Nothing,CodeInstance}
match::MethodMatch
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_edges_impl(edges::Vector{Any}, info::InvokeCallInfo)
edge = info.edge
if edge !== nothing
add_invoke_edge!(edges, info.atype, edge.def) # TODO CodeInstance as edge
end
end
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
if edges[i] === mi
edge_minus_1 = edges[i-1]
if edge_minus_1 isa Type && edge_minus_1 == atype
return nothing
end
end
end
push!(edges, atype)
push!(edges, mi)
push!(edges, atype, 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 @@ -280,13 +288,16 @@ the method that has been processed.
Optionally keeps `info.result::InferenceResult` that keeps constant information.
"""
struct OpaqueClosureCallInfo <: CallInfo
edge::Union{Nothing,CodeInstance}
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
function add_edges_impl(edges::Vector{Any}, info::OpaqueClosureCallInfo)
edge = info.edge
if edge !== nothing
add_one_edge!(edges, edge.def) # TODO CodeInstance as edge
end
end

"""
info::OpaqueClosureCreateInfo <: CallInfo
Expand Down Expand Up @@ -352,4 +363,9 @@ struct ModifyOpInfo <: CallInfo
end
add_edges_impl(edges::Vector{Any}, info::ModifyOpInfo) = add_edges!(edges, info.info)

struct VirtualMethodMatchInfo <: CallInfo
info::CallInfo
end
add_edges_impl(edges::Vector{Any}, info::VirtualMethodMatchInfo) = add_edges!(edges, info.info)

@specialize
18 changes: 15 additions & 3 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3010,7 +3010,11 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any},
rt = Const(true) # has applicable matches
end
if rt !== Bool
info = MethodResultPure(matches.info) # XXX this should probably be something like
for i = 1:napplicable
(; match, edges, edge_idx) = applicable[i]
edges[edge_idx] = codeinstance_for_method_match_edge(interp, specialize_method(match))
end
info = VirtualMethodMatchInfo(matches.info)
end
end
return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, info))
Expand Down Expand Up @@ -3051,12 +3055,20 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv
vinfo = MethodMatchInfo(vresults, mt, types, false) # XXX: this should actually be an info with invoke-type edge
else
rt = Const(true)
vinfo = InvokeCallInfo(match, nothing, types)
edge = codeinstance_for_method_match_edge(interp, specialize_method(match))
vinfo = InvokeCallInfo(edge, match, nothing, types)
end
info = MethodResultPure(vinfo) # XXX this should probably be something like `VirtualizedCallInfo`
info = VirtualMethodMatchInfo(vinfo)
return CallMeta(rt, Union{}, EFFECTS_TOTAL, info)
end

# allocate a dummy `edge::CodeInstance` to be added by `add_edges!`
function codeinstance_for_method_match_edge(interp::AbstractInterpreter, edge::MethodInstance)
return CodeInstance(edge, cache_owner(interp), Any, Any, nothing, nothing, zero(Int32),
get_inference_world(interp), get_inference_world(interp),
zero(UInt32), nothing, zero(UInt8), nothing, empty_edges)
end

# N.B.: typename maps type equivalence classes to a single value
function typename_static(@nospecialize(t))
t isa Const && return _typename(t.val)
Expand Down
Loading

0 comments on commit 39b83ce

Please sign in to comment.