Skip to content

inference: remove MAX_TYPE_DEPTH limit, rewrite tmerge #26628

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

Merged
merged 5 commits into from
Apr 6, 2018
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
2 changes: 1 addition & 1 deletion base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ function abstract_call_method(method::Method, @nospecialize(sig), sparams::Simpl
comparison = method.sig
end
# see if the type is actually too big (relative to the caller), and limit it if required
newsig = limit_type_size(sig, comparison, sv.linfo.specTypes, spec_len)
newsig = limit_type_size(sig, comparison, sv.linfo.specTypes, sv.params.TUPLE_COMPLEXITY_LIMIT_DEPTH, spec_len)

if newsig !== sig
# continue inference, but note that we've limited parameter complexity
Expand Down
6 changes: 4 additions & 2 deletions base/compiler/params.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ struct Params
# when inferring a call to _apply
MAX_APPLY_UNION_ENUM::Int

# parameters limiting large types
# parameters limiting large (tuple) types
MAX_TUPLETYPE_LEN::Int
TUPLE_COMPLEXITY_LIMIT_DEPTH::Int

# when attempting to inlining _apply, abort the optimization if the tuple
# contains more than this many elements
Expand All @@ -41,12 +42,13 @@ struct Params
inline_tupleret_bonus::Int = 400,
max_methods::Int = 4,
tupletype_len::Int = 15,
tupletype_depth::Int = 3,
tuple_splat::Int = 16,
union_splitting::Int = 4,
apply_union_enum::Int = 8)
return new(Vector{InferenceResult}(),
world, inlining, true, false, inline_cost_threshold, inline_nonleaf_penalty,
inline_tupleret_bonus, max_methods, union_splitting, apply_union_enum,
tupletype_len, tuple_splat)
tupletype_len, tupletype_depth, tuple_splat)
end
end
10 changes: 7 additions & 3 deletions base/compiler/tfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -712,9 +712,6 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...)
if isvarargtype(headtype)
return Type
end
if uncertain && type_too_complex(appl, MAX_TYPE_DEPTH)
return Type{<:headtype}
end
if istuple
return Type{<:appl}
end
Expand Down Expand Up @@ -914,3 +911,10 @@ function return_type_tfunc(argtypes::Vector{Any}, vtypes::VarTable, sv::Inferenc
end
return NOT_FOUND
end

# N.B.: typename maps type equivalence classes to a single value
function typename_static(@nospecialize(t))
t = unwrap_unionall(t)
return isType(t) ? _typename(t.parameters[1]) : Core.TypeName
end
typename_static(t::Const) = _typename(t.val)
72 changes: 3 additions & 69 deletions base/compiler/typelattice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ struct PartialTypeVar
end

# Wraps a type and represents that the value may also be undef at this point.
# (only used in optimize, not abstractinterpret)
struct MaybeUndef
typ
MaybeUndef(@nospecialize(typ)) = new(typ)
end

# The type of a variable load is either a value or an UndefVarError
# (only used in abstractinterpret, doesn't appear in optimize)
struct VarState
typ
undef::Bool
Expand All @@ -72,32 +75,6 @@ struct NotFound end

const NOT_FOUND = NotFound()

#####################
# lattice utilities #
#####################

function rewrap(@nospecialize(t), @nospecialize(u))
isa(t, Const) && return t
isa(t, Conditional) && return t
return rewrap_unionall(t, u)
end

_typename(a) = Union{}
_typename(a::Vararg) = Any
_typename(a::TypeVar) = Any
function _typename(a::Union)
ta = _typename(a.a)
tb = _typename(a.b)
ta === tb ? tb : (ta === Any || tb === Any) ? Any : Union{}
end
_typename(union::UnionAll) = _typename(union.body)

_typename(a::DataType) = Const(a.name)

# N.B.: typename maps type equivalence classes to a single value
typename_static(@nospecialize(t)) = isType(t) ? _typename(t.parameters[1]) : Any
typename_static(t::Const) = _typename(t.val)

#################
# lattice logic #
#################
Expand Down Expand Up @@ -176,49 +153,6 @@ widenconst(@nospecialize(t)) = t

issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef)

function tmerge(@nospecialize(typea), @nospecialize(typeb))
typea ⊑ typeb && return typeb
typeb ⊑ typea && return typea
if isa(typea, MaybeUndef) || isa(typeb, MaybeUndef)
return MaybeUndef(tmerge(
isa(typea, MaybeUndef) ? typea.typ : typea,
isa(typeb, MaybeUndef) ? typeb.typ : typeb))
end
if isa(typea, Conditional) && isa(typeb, Conditional)
if typea.var === typeb.var
vtype = tmerge(typea.vtype, typeb.vtype)
elsetype = tmerge(typea.elsetype, typeb.elsetype)
if vtype != elsetype
return Conditional(typea.var, vtype, elsetype)
end
end
return Bool
end
typea, typeb = widenconst(typea), widenconst(typeb)
typea === typeb && return typea
if !(isa(typea,Type) || isa(typea,TypeVar)) || !(isa(typeb,Type) || isa(typeb,TypeVar))
return Any
end
if (typea <: Tuple) && (typeb <: Tuple)
if isa(typea, DataType) && isa(typeb, DataType) && length(typea.parameters) == length(typeb.parameters) && !isvatuple(typea) && !isvatuple(typeb)
return typejoin(typea, typeb)
end
if isa(typea, Union) || isa(typeb, Union) || (isa(typea,DataType) && length(typea.parameters)>3) ||
(isa(typeb,DataType) && length(typeb.parameters)>3)
# widen tuples faster (see #6704), but not too much, to make sure we can infer
# e.g. (t::Union{Tuple{Bool},Tuple{Bool,Int}})[1]
return Tuple
end
end
u = Union{typea, typeb}
if unionlen(u) > MAX_TYPEUNION_LEN || type_too_complex(u, MAX_TYPE_DEPTH)
# don't let type unions get too big
# TODO: something smarter, like a common supertype
return Any
end
return u
end

function smerge(sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState})
sa === sb && return sa
sa === NOT_FOUND && return sb
Expand Down
133 changes: 114 additions & 19 deletions base/compiler/typelimits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
# limitation parameters #
#########################

const MAX_TYPEUNION_LEN = 3
const MAX_TYPE_DEPTH = 8
const MAX_TYPEUNION_LEN = 4
const MAX_INLINE_CONST_SIZE = 256
const TUPLE_COMPLEXITY_LIMIT_DEPTH = 3

#########################
# limitation heuristics #
Expand All @@ -31,10 +29,10 @@ end
# limit the complexity of type `t` to be simpler than the comparison type `compare`
# no new values may be introduced, so the parameter `source` encodes the set of all values already present
# the outermost tuple type is permitted to have up to `allowed_tuplelen` parameters
function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tuplelen::Int)
function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tupledepth::Int, allowed_tuplelen::Int)
source = svec(unwrap_unionall(compare), unwrap_unionall(source))
source[1] === source[2] && (source = svec(source[1]))
type_more_complex(t, compare, source, 1, TUPLE_COMPLEXITY_LIMIT_DEPTH, allowed_tuplelen) || return t
type_more_complex(t, compare, source, 1, allowed_tupledepth, allowed_tuplelen) || return t
r = _limit_type_size(t, compare, source, 1, allowed_tuplelen)
@assert t <: r
#@assert r === _limit_type_size(r, t, source) # this monotonicity constraint is slightly stronger than actually required,
Expand Down Expand Up @@ -304,21 +302,118 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe
return true
end

function type_too_complex(@nospecialize(t), d::Int)
if d < 0
return true
elseif isa(t, Union)
return type_too_complex(t.a, d - 1) || type_too_complex(t.b, d - 1)
elseif isa(t, TypeVar)
return type_too_complex(t.lb, d - 1) || type_too_complex(t.ub, d - 1)
elseif isa(t, UnionAll)
return type_too_complex(t.var, d) || type_too_complex(t.body, d)
elseif isa(t, DataType)
for x in (t.parameters)::SimpleVector
if type_too_complex(x, d - 1)
return true
function tmerge(@nospecialize(typea), @nospecialize(typeb))
typea ⊑ typeb && return typeb
typeb ⊑ typea && return typea
if isa(typea, MaybeUndef) || isa(typeb, MaybeUndef)
return MaybeUndef(tmerge(
isa(typea, MaybeUndef) ? typea.typ : typea,
isa(typeb, MaybeUndef) ? typeb.typ : typeb))
end
if isa(typea, Conditional) && isa(typeb, Conditional)
if typea.var === typeb.var
vtype = tmerge(typea.vtype, typeb.vtype)
elsetype = tmerge(typea.elsetype, typeb.elsetype)
if vtype != elsetype
return Conditional(typea.var, vtype, elsetype)
end
end
return Bool
end
return false
typea, typeb = widenconst(typea), widenconst(typeb)
typea === typeb && return typea
if !(isa(typea, Type) || isa(typea, TypeVar)) ||
!(isa(typeb, Type) || isa(typeb, TypeVar))
# XXX: this should never happen
return Any
end
# if we didn't start with any unions, then always OK to form one now
if !(typea isa Union || typeb isa Union)
# except if we might have switched Union and Tuple below, or would do so
if (isconcretetype(typea) && isconcretetype(typeb)) || !(typea <: Tuple && typeb <: Tuple)
return Union{typea, typeb}
end
end
# collect the list of types from past tmerge calls returning Union
# and then reduce over that list
types = Any[]
_uniontypes(typea, types)
_uniontypes(typeb, types)
typenames = Vector{Core.TypeName}(undef, length(types))
for i in 1:length(types)
# check that we will be able to analyze (and simplify) everything
# bail if everything isn't a well-formed DataType
ti = types[i]
uw = unwrap_unionall(ti)
(uw isa DataType && ti <: uw.name.wrapper) || return Any
typenames[i] = uw.name
end
# see if any of the union elements have the same TypeName
# in which case, simplify this tmerge by replacing it with
# the widest possible version of itself (the wrapper)
for i in 1:length(types)
ti = types[i]
for j in (i + 1):length(types)
if typenames[i] === typenames[j]
tj = types[j]
if ti <: tj
types[i] = Union{}
break
elseif tj <: ti
types[j] = Union{}
typenames[j] = Any.name
else
widen = typenames[i].wrapper
if typenames[i] === Tuple.name
# try to widen Tuple slower: make a single non-concrete Tuple containing both
# converge the Tuple element-wise if they are the same length
# see 4ee2b41552a6bc95465c12ca66146d69b354317b, be59686f7613a2ccfd63491c7b354d0b16a95c05,
if nothing !== tuplelen(ti) === tuplelen(tj)
widen = tuplemerge(ti, tj)
end
# TODO: else, try to merge them into a single Tuple{Vararg{T}} instead (#22120)?
end
types[i] = Union{}
types[j] = widen
break
end
end
end
end
u = Union{types...}
if unionlen(u) <= MAX_TYPEUNION_LEN
# don't let type unions get too big, if the above didn't reduce it enough
return u
end
# finally, just return the widest possible type
return Any
end

# the inverse of switchtupleunion, with limits on max element union size
function tuplemerge(@nospecialize(a), @nospecialize(b))
if isa(a, UnionAll)
return UnionAll(a.var, tuplemerge(a.body, b))
elseif isa(b, UnionAll)
return UnionAll(b.var, tuplemerge(a, b.body))
elseif isa(a, Union)
return tuplemerge(tuplemerge(a.a, a.b), b)
elseif isa(b, Union)
return tuplemerge(a, tuplemerge(b.a, b.b))
end
a = a::DataType
b = b::DataType
ap, bp = a.parameters, b.parameters
lar = length(ap)::Int
lbr = length(bp)::Int
@assert lar === lbr && a.name === b.name === Tuple.name "assertion failure"
p = Vector{Any}(undef, lar)
for i = 1:lar
ui = Union{ap[i], bp[i]}
if unionlen(ui) < MAX_TYPEUNION_LEN
p[i] = ui
else
p[i] = Any
end
end
return Tuple{p...}
end
44 changes: 43 additions & 1 deletion base/compiler/typeutils.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

const _TYPE_NAME = Type.body.name
#####################
# lattice utilities #
#####################

function rewrap(@nospecialize(t), @nospecialize(u))
isa(t, Const) && return t
isa(t, Conditional) && return t
return rewrap_unionall(t, u)
end

const _TYPE_NAME = Type.body.name
isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _TYPE_NAME

# true if Type{T} is inlineable as constant T
Expand Down Expand Up @@ -73,6 +82,19 @@ function tvar_extent(@nospecialize t)
return t
end

_typename(@nospecialize a) = Union{}
_typename(a::TypeVar) = Core.TypeName
function _typename(a::Union)
ta = _typename(a.a)
tb = _typename(a.b)
ta === tb && return ta # same type-name
(ta === Union{} || tb === Union{}) && return Union{} # threw an error
(ta isa Const && tb isa Const) && return Union{} # will throw an error (different type-names)
return Core.TypeName # uncertain result
end
_typename(union::UnionAll) = _typename(union.body)
_typename(a::DataType) = Const(a.name)

function tuple_tail_elem(@nospecialize(init), ct)
return Vararg{widenconst(foldl((a, b) -> tmerge(a, tvar_extent(unwrapva(b))), init, ct))}
end
Expand Down Expand Up @@ -117,3 +139,23 @@ function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospeci
end
return tunion
end

tuplelen(@nospecialize tpl) = nothing
function tuplelen(tpl::DataType)
l = length(tpl.parameters)::Int
if l > 0
last = unwrap_unionall(tpl.parameters[l])
if isvarargtype(last)
N = last.parameters[2]
N isa Int || return nothing
l += N - 1
end
end
return l
end
tuplelen(tpl::UnionAll) = tuplelen(tpl.body)
function tuplelen(tpl::Union)
la, lb = tuplelen(tpl.a), tuplelen(tpl.b)
la == lb && return la
return nothing
end
Loading