Skip to content

Commit

Permalink
inference: avoid unnecessary specializations in callinfo constructions
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk committed Oct 30, 2021
1 parent bf1b5fa commit 4626cf6
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 53 deletions.
8 changes: 4 additions & 4 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1009,19 +1009,19 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::
nargs = length(aargtypes)
splitunions = 1 < unionsplitcost(aargtypes) <= InferenceParams(interp).MAX_APPLY_UNION_ENUM
ctypes = [Any[aft]]
infos = [Union{Nothing, AbstractIterationInfo}[]]
infos = Vector{MaybeAbstractIterationInfo}[MaybeAbstractIterationInfo[]]
for i = 1:nargs
ctypes´ = Vector{Any}[]
infos′ = Vector{Union{Nothing, AbstractIterationInfo}}[]
infos′ = Vector{MaybeAbstractIterationInfo}[]
for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]])
if !isvarargtype(ti)
cti_info = precise_container_type(interp, itft, ti, sv)
cti = cti_info[1]::Vector{Any}
info = cti_info[2]::Union{Nothing,AbstractIterationInfo}
info = cti_info[2]::MaybeAbstractIterationInfo
else
cti_info = precise_container_type(interp, itft, unwrapva(ti), sv)
cti = cti_info[1]::Vector{Any}
info = cti_info[2]::Union{Nothing,AbstractIterationInfo}
info = cti_info[2]::MaybeAbstractIterationInfo
# We can't represent a repeating sequence of the same types,
# so tmerge everything together to get one type that represents
# everything.
Expand Down
2 changes: 0 additions & 2 deletions base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1045,15 +1045,13 @@ end

function narrow_opaque_closure!(ir::IRCode, stmt::Expr, @nospecialize(info), state::InliningState)
if isa(info, OpaqueClosureCreateInfo)
isa(info.unspec.info, OpaqueClosureCallInfo) || return
lbt = argextype(stmt.args[3], ir, ir.sptypes)
lb, exact = instanceof_tfunc(lbt)
exact || return
ubt = argextype(stmt.args[4], ir, ir.sptypes)
ub, exact = instanceof_tfunc(ubt)
exact || return
# Narrow opaque closure type

newT = widenconst(tmeet(tmerge(lb, info.unspec.rt), ub))
if newT != ub
# N.B.: Narrowing the ub requires a backdge on the mi whose type
Expand Down
117 changes: 70 additions & 47 deletions base/compiler/stmtinfo.jl
Original file line number Diff line number Diff line change
@@ -1,139 +1,162 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

@nospecialize

"""
struct MethodMatchInfo
call::CallMeta
Captures the result of a `method_matches` lookup for the given call. This
info may then be used by the optimizer to inline the matches, without having
to re-consult the method table. This info is illegal on any statement that is
not a call to a generic function.
A simple struct that captures both the return type (`call.rt`)
and any additional information (`call.info`) for a given generic call.
"""
struct MethodMatchInfo
results::MethodLookupResult
struct CallMeta
rt::Any
info::Any
end

"""
struct MethodResultPure
info::MethodMatchInfo
This struct represents a method result constant was proven to be
effect-free, including being no-throw (typically because the value was computed
by calling an `@pure` function).
Captures the result of a `:jl_matching_methods` lookup for the given call (`info.results`).
This info may then be used by the optimizer to inline the matches, without having
to re-consult the method table. This info is illegal on any statement that is
not a call to a generic function.
"""
struct MethodResultPure
info::Any
end
let instance = MethodResultPure(false)
global MethodResultPure
MethodResultPure() = instance
struct MethodMatchInfo
results::MethodLookupResult
end

"""
struct UnionSplitInfo
info::UnionSplitInfo
If inference decides to partition the method search space by splitting unions,
it will issue a method lookup query for each such partition. This info indicates
that such partitioning happened and wraps the corresponding MethodMatchInfo for
each partition. This info is illegal on any statement that is not a call to a
generic function.
that such partitioning happened and wraps the corresponding `MethodMatchInfo` for
each partition (`info.matches::Vector{MethodMatchInfo}`).
This info is illegal on any statement that is not a call to a generic function.
"""
struct UnionSplitInfo
matches::Vector{MethodMatchInfo}
end

"""
struct CallMeta
info::MethodResultPure
A simple struct that captures both the return type (`rt`) and any additional information
(`info`) for a given generic call.
This struct represents a method result constant was proven to be
effect-free, including being no-throw (typically because the value was computed
by calling an `@pure` function).
"""
struct CallMeta
rt::Any
info::Any
struct MethodResultPure
info::Union{MethodMatchInfo,UnionSplitInfo,Bool}
end
let instance = MethodResultPure(false)
global MethodResultPure
MethodResultPure() = instance
end

"""
struct AbstractIterationInfo
info::AbstractIterationInfo
Captures all the information for abstract iteration analysis of a single value.
Each (abstract) call to `iterate`, corresponds to one entry in `each`.
Each (abstract) call to `iterate`, corresponds to one entry in `info.each::Vector{CallMeta}`.
"""
struct AbstractIterationInfo
each::Vector{CallMeta}
end

const MaybeAbstractIterationInfo = Union{Nothing, AbstractIterationInfo}

"""
struct ApplyCallInfo
info::ApplyCallInfo
This info applies to any call of `_apply_iterate(...)` and captures both the
info of the actual call being applied and the info for any implicit call
to the `iterate` function. Note that it is possible for the call itself
to be yet another `_apply_iterate`, in which case the `.call` field will
to be yet another `_apply_iterate`, in which case the `info.call` field will
be another `ApplyCallInfo`. This info is illegal on any statement that is
not an `_apply_iterate` call.
"""
struct ApplyCallInfo
# The info for the call itself
call::Any
# AbstractIterationInfo for each argument, if applicable
arginfo::Vector{Union{Nothing, AbstractIterationInfo}}
arginfo::Vector{MaybeAbstractIterationInfo}
end

"""
struct UnionSplitApplyCallInfo
info::UnionSplitApplyCallInfo
Like `UnionSplitInfo`, but for `ApplyCallInfo` rather than MethodMatchInfo.
Like `UnionSplitInfo`, but for `ApplyCallInfo` rather than `MethodMatchInfo`.
This info is illegal on any statement that is not an `_apply_iterate` call.
"""
struct UnionSplitApplyCallInfo
infos::Vector{ApplyCallInfo}
end

"""
call::ConstCallInfo
info::ConstCallInfo
The precision of this call was improved using constant information.
In addition to the original call information `call.call`, `call` also keeps
the inference results with constant information `call.results::Vector{Union{Nothing,InferenceResult}}`.
In addition to the original call information `info.call`, this info also keeps
the inference results with constant information `info.results::Vector{Union{Nothing,InferenceResult}}`.
"""
struct ConstCallInfo
call::Union{MethodMatchInfo,UnionSplitInfo}
results::Vector{Union{Nothing,InferenceResult}}
end

"""
call::InvokeCallInfo
info::InvokeCallInfo
Represents a resolved call to `invoke`, carrying the `call.match::MethodMatch` of the
method that has been processed.
Optionally keeps `result::InferenceResult` that carries constant information.
Represents a resolved call to `Core.invoke`, carrying the `info.match::MethodMatch` of
the method that has been processed.
Optionally keeps `info.result::InferenceResult` that keeps constant information.
"""
struct InvokeCallInfo
match::MethodMatch
result::Union{Nothing,InferenceResult}
end

"""
call::OpaqueClosureCallInfo
info::OpaqueClosureCallInfo
Represents a resolved call of opaque closure, carrying the `call.match::MethodMatch` of the
method that has been processed.
Optionally keeps `result::InferenceResult` that carries constant information.
Represents a resolved call of opaque closure, carrying the `info.match::MethodMatch` of
the method that has been processed.
Optionally keeps `info.result::InferenceResult` that keeps constant information.
"""
struct OpaqueClosureCallInfo
match::MethodMatch
result::Union{Nothing,InferenceResult}
end

"""
info::OpaqueClosureCreateInfo
This info may be constructed upon opaque closure construction, with `info.unspec::CallMeta`
carrying out inference result of an unreal, partially specialized call (i.e. specialized on
the closure environment, but not on the argument types of the opaque closure) in order to
allow the optimizer to rewrite the return type parameter of the `OpaqueClosure` based on it.
"""
struct OpaqueClosureCreateInfo
unspec::CallMeta
function OpaqueClosureCreateInfo(unspec::CallMeta)
@assert isa(unspec.info, OpaqueClosureCallInfo)
return new(unspec)
end
end

# 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
# the AbstractInterpreter.

"""
info::ReturnTypeCallInfo
Represents a resolved call of `Core.Compiler.return_type`.
`info.call` wraps the info corresponding to the call that `Core.Compiler.return_type` call
was supposed to analyze.
"""
struct ReturnTypeCallInfo
# The info corresponding to the call that return_type was supposed to
# analyze.
info::Any
end

@specialize

0 comments on commit 4626cf6

Please sign in to comment.