Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce @nospecializeinfer macro to tell the compiler to avoid excess inference #41931

Merged
merged 2 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,10 @@ function abstract_call_method(interp::AbstractInterpreter,
sigtuple = unwrap_unionall(sig)
sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects())

if is_nospecializeinfer(method)
sig = get_nospecializeinfer_sig(method, sig, sparams)
end

# Limit argument type tuple growth of functions:
# look through the parents list to see if there's a call to the same method
# and from the same method.
Expand Down Expand Up @@ -2641,18 +2645,18 @@ struct BestguessInfo{Interp<:AbstractInterpreter}
end
end

function widenreturn(@nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn(@nospecialize(rt), info::BestguessInfo)
return widenreturn(typeinf_lattice(info.interp), rt, info)
end

function widenreturn(𝕃ᵢ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn(𝕃ᵢ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
return widenreturn(widenlattice(𝕃ᵢ), rt, info)
end
function widenreturn_noslotwrapper(𝕃ᵢ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn_noslotwrapper(𝕃ᵢ::AbstractLattice, @nospecialize(rt), info::BestguessInfo)
return widenreturn_noslotwrapper(widenlattice(𝕃ᵢ), rt, info)
end

function widenreturn(𝕃ᵢ::MustAliasesLattice, @nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn(𝕃ᵢ::MustAliasesLattice, @nospecialize(rt), info::BestguessInfo)
if isa(rt, MustAlias)
if 1 ≤ rt.slot ≤ info.nargs
rt = InterMustAlias(rt)
Expand All @@ -2664,7 +2668,7 @@ function widenreturn(𝕃ᵢ::MustAliasesLattice, @nospecialize(rt), info::Bestg
return widenreturn(widenlattice(𝕃ᵢ), rt, info)
end

function widenreturn(𝕃ᵢ::ConditionalsLattice, @nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn(𝕃ᵢ::ConditionalsLattice, @nospecialize(rt), info::BestguessInfo)
⊑ᵢ = ⊑(𝕃ᵢ)
if !(⊑(ipo_lattice(info.interp), info.bestguess, Bool)) || info.bestguess === Bool
# give up inter-procedural constraint back-propagation
Expand Down Expand Up @@ -2701,7 +2705,7 @@ function widenreturn(𝕃ᵢ::ConditionalsLattice, @nospecialize(rt), info::Best
isa(rt, InterConditional) && return rt
return widenreturn(widenlattice(𝕃ᵢ), rt, info)
end
function bool_rt_to_conditional(@nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function bool_rt_to_conditional(@nospecialize(rt), info::BestguessInfo)
bestguess = info.bestguess
if isa(bestguess, InterConditional)
# if the bestguess so far is already `Conditional`, try to convert
Expand All @@ -2719,7 +2723,7 @@ function bool_rt_to_conditional(@nospecialize(rt), info::BestguessInfo)
end
return rt
end
function bool_rt_to_conditional(@nospecialize(rt), slot_id::Int, info::BestguessInfo)
@nospecializeinfer function bool_rt_to_conditional(@nospecialize(rt), slot_id::Int, info::BestguessInfo)
⊑ᵢ = ⊑(typeinf_lattice(info.interp))
old = info.slottypes[slot_id]
new = widenslotwrapper(info.changes[slot_id].typ) # avoid nested conditional
Expand All @@ -2738,13 +2742,13 @@ function bool_rt_to_conditional(@nospecialize(rt), slot_id::Int, info::Bestguess
return rt
end

function widenreturn(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
return widenreturn_partials(𝕃ᵢ, rt, info)
end
function widenreturn_noslotwrapper(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn_noslotwrapper(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
return widenreturn_partials(𝕃ᵢ, rt, info)
end
function widenreturn_partials(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
@nospecializeinfer function widenreturn_partials(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info::BestguessInfo)
if isa(rt, PartialStruct)
fields = copy(rt.fields)
local anyrefine = false
Expand All @@ -2767,21 +2771,21 @@ function widenreturn_partials(𝕃ᵢ::PartialsLattice, @nospecialize(rt), info:
return widenreturn(widenlattice(𝕃ᵢ), rt, info)
end

function widenreturn(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
@nospecializeinfer function widenreturn(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
return widenreturn_consts(rt)
end
function widenreturn_noslotwrapper(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
@nospecializeinfer function widenreturn_noslotwrapper(::ConstsLattice, @nospecialize(rt), ::BestguessInfo)
return widenreturn_consts(rt)
end
function widenreturn_consts(@nospecialize(rt))
@nospecializeinfer function widenreturn_consts(@nospecialize(rt))
isa(rt, Const) && return rt
return widenconst(rt)
end

function widenreturn(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
@nospecializeinfer function widenreturn(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
return widenconst(rt)
end
function widenreturn_noslotwrapper(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
@nospecializeinfer function widenreturn_noslotwrapper(::JLTypeLattice, @nospecialize(rt), ::BestguessInfo)
return widenconst(rt)
end

Expand Down
50 changes: 25 additions & 25 deletions base/compiler/abstractlattice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -161,23 +161,23 @@ If `𝕃` is `JLTypeLattice`, this is equivalent to subtyping.
"""
function ⊑ end

⊑(::JLTypeLattice, @nospecialize(a::Type), @nospecialize(b::Type)) = a <: b
@nospecializeinfer ⊑(::JLTypeLattice, @nospecialize(a::Type), @nospecialize(b::Type)) = a <: b

"""
⊏(𝕃::AbstractLattice, a, b) -> Bool

The strict partial order over the type inference lattice.
This is defined as the irreflexive kernel of `⊑`.
"""
⊏(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = ⊑(𝕃, a, b) && !⊑(𝕃, b, a)
@nospecializeinfer ⊏(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = ⊑(𝕃, a, b) && !⊑(𝕃, b, a)

"""
⋤(𝕃::AbstractLattice, a, b) -> Bool

This order could be used as a slightly more efficient version of the strict order `⊏`,
where we can safely assume `a ⊑ b` holds.
"""
⋤(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = !⊑(𝕃, b, a)
@nospecializeinfer ⋤(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b)) = !⊑(𝕃, b, a)

"""
is_lattice_equal(𝕃::AbstractLattice, a, b) -> Bool
Expand All @@ -186,7 +186,7 @@ Check if two lattice elements are partial order equivalent.
This is basically `a ⊑ b && b ⊑ a` in the lattice of `𝕃`
but (optionally) with extra performance optimizations.
"""
function is_lattice_equal(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b))
@nospecializeinfer function is_lattice_equal(𝕃::AbstractLattice, @nospecialize(a), @nospecialize(b))
a === b && return true
return ⊑(𝕃, a, b) && ⊑(𝕃, b, a)
end
Expand All @@ -197,32 +197,32 @@ end
Determines whether the given lattice element `t` of `𝕃` has non-trivial extended lattice
information that would not be available from the type itself.
"""
has_nontrivial_extended_info(𝕃::AbstractLattice, @nospecialize t) =
@nospecializeinfer has_nontrivial_extended_info(𝕃::AbstractLattice, @nospecialize t) =
has_nontrivial_extended_info(widenlattice(𝕃), t)
function has_nontrivial_extended_info(𝕃::PartialsLattice, @nospecialize t)
@nospecializeinfer function has_nontrivial_extended_info(𝕃::PartialsLattice, @nospecialize t)
isa(t, PartialStruct) && return true
isa(t, PartialOpaque) && return true
return has_nontrivial_extended_info(widenlattice(𝕃), t)
end
function has_nontrivial_extended_info(𝕃::ConstsLattice, @nospecialize t)
@nospecializeinfer function has_nontrivial_extended_info(𝕃::ConstsLattice, @nospecialize t)
isa(t, PartialTypeVar) && return true
if isa(t, Const)
val = t.val
return !issingletontype(typeof(val)) && !(isa(val, Type) && hasuniquerep(val))
end
return has_nontrivial_extended_info(widenlattice(𝕃), t)
end
has_nontrivial_extended_info(::JLTypeLattice, @nospecialize(t)) = false
@nospecializeinfer has_nontrivial_extended_info(::JLTypeLattice, @nospecialize(t)) = false

"""
is_const_prop_profitable_arg(𝕃::AbstractLattice, t) -> Bool

Determines whether the given lattice element `t` of `𝕃` has new extended lattice information
that should be forwarded along with constant propagation.
"""
is_const_prop_profitable_arg(𝕃::AbstractLattice, @nospecialize t) =
@nospecializeinfer is_const_prop_profitable_arg(𝕃::AbstractLattice, @nospecialize t) =
is_const_prop_profitable_arg(widenlattice(𝕃), t)
function is_const_prop_profitable_arg(𝕃::PartialsLattice, @nospecialize t)
@nospecializeinfer function is_const_prop_profitable_arg(𝕃::PartialsLattice, @nospecialize t)
if isa(t, PartialStruct)
return true # might be a bit aggressive, may want to enable some check like follows:
# for i = 1:length(t.fields)
Expand All @@ -236,7 +236,7 @@ function is_const_prop_profitable_arg(𝕃::PartialsLattice, @nospecialize t)
isa(t, PartialOpaque) && return true
return is_const_prop_profitable_arg(widenlattice(𝕃), t)
end
function is_const_prop_profitable_arg(𝕃::ConstsLattice, @nospecialize t)
@nospecializeinfer function is_const_prop_profitable_arg(𝕃::ConstsLattice, @nospecialize t)
if isa(t, Const)
# don't consider mutable values useful constants
val = t.val
Expand All @@ -245,24 +245,24 @@ function is_const_prop_profitable_arg(𝕃::ConstsLattice, @nospecialize t)
isa(t, PartialTypeVar) && return false # this isn't forwardable
return is_const_prop_profitable_arg(widenlattice(𝕃), t)
end
is_const_prop_profitable_arg(::JLTypeLattice, @nospecialize t) = false
@nospecializeinfer is_const_prop_profitable_arg(::JLTypeLattice, @nospecialize t) = false

is_forwardable_argtype(𝕃::AbstractLattice, @nospecialize(x)) =
@nospecializeinfer is_forwardable_argtype(𝕃::AbstractLattice, @nospecialize(x)) =
is_forwardable_argtype(widenlattice(𝕃), x)
function is_forwardable_argtype(𝕃::ConditionalsLattice, @nospecialize x)
@nospecializeinfer function is_forwardable_argtype(𝕃::ConditionalsLattice, @nospecialize x)
isa(x, Conditional) && return true
return is_forwardable_argtype(widenlattice(𝕃), x)
end
function is_forwardable_argtype(𝕃::PartialsLattice, @nospecialize x)
@nospecializeinfer function is_forwardable_argtype(𝕃::PartialsLattice, @nospecialize x)
isa(x, PartialStruct) && return true
isa(x, PartialOpaque) && return true
return is_forwardable_argtype(widenlattice(𝕃), x)
end
function is_forwardable_argtype(𝕃::ConstsLattice, @nospecialize x)
@nospecializeinfer function is_forwardable_argtype(𝕃::ConstsLattice, @nospecialize x)
isa(x, Const) && return true
return is_forwardable_argtype(widenlattice(𝕃), x)
end
function is_forwardable_argtype(::JLTypeLattice, @nospecialize x)
@nospecializeinfer function is_forwardable_argtype(::JLTypeLattice, @nospecialize x)
return false
end

Expand All @@ -281,9 +281,9 @@ External lattice `𝕃ᵢ::ExternalLattice` may overload:
"""
function widenreturn end, function widenreturn_noslotwrapper end

is_valid_lattice(𝕃::AbstractLattice, @nospecialize(elem)) =
@nospecializeinfer is_valid_lattice(𝕃::AbstractLattice, @nospecialize(elem)) =
is_valid_lattice_norec(𝕃, elem) && is_valid_lattice(widenlattice(𝕃), elem)
is_valid_lattice(𝕃::JLTypeLattice, @nospecialize(elem)) = is_valid_lattice_norec(𝕃, elem)
@nospecializeinfer is_valid_lattice(𝕃::JLTypeLattice, @nospecialize(elem)) = is_valid_lattice_norec(𝕃, elem)

has_conditional(𝕃::AbstractLattice) = has_conditional(widenlattice(𝕃))
has_conditional(::AnyConditionalsLattice) = true
Expand All @@ -306,12 +306,12 @@ has_extended_unionsplit(::JLTypeLattice) = false
const fallback_lattice = InferenceLattice(BaseInferenceLattice.instance)
const fallback_ipo_lattice = InferenceLattice(IPOResultLattice.instance)

⊑(@nospecialize(a), @nospecialize(b)) = ⊑(fallback_lattice, a, b)
tmeet(@nospecialize(a), @nospecialize(b)) = tmeet(fallback_lattice, a, b)
tmerge(@nospecialize(a), @nospecialize(b)) = tmerge(fallback_lattice, a, b)
(@nospecialize(a), @nospecialize(b)) = (fallback_lattice, a, b)
(@nospecialize(a), @nospecialize(b)) = (fallback_lattice, a, b)
is_lattice_equal(@nospecialize(a), @nospecialize(b)) = is_lattice_equal(fallback_lattice, a, b)
@nospecializeinfer @nospecialize(a)@nospecialize(b) = ⊑(fallback_lattice, a, b)
@nospecializeinfer @nospecialize(a)@nospecialize(b) = (fallback_lattice, a, b)
@nospecializeinfer @nospecialize(a)@nospecialize(b) = (fallback_lattice, a, b)
@nospecializeinfer tmeet(@nospecialize(a), @nospecialize(b)) = tmeet(fallback_lattice, a, b)
@nospecializeinfer tmerge(@nospecialize(a), @nospecialize(b)) = tmerge(fallback_lattice, a, b)
@nospecializeinfer is_lattice_equal(@nospecialize(a), @nospecialize(b)) = is_lattice_equal(fallback_lattice, a, b)

# Widenlattice with argument
widenlattice(::JLTypeLattice, @nospecialize(t)) = widenconst(t)
Expand Down
Loading