Skip to content

Commit

Permalink
cosmetic refactoring on compiler code (#45659)
Browse files Browse the repository at this point in the history
Separated from #41199 for easier review.
  • Loading branch information
aviatesk authored Jun 13, 2022
1 parent d92acb9 commit 0dc3a69
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 71 deletions.
32 changes: 20 additions & 12 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,37 @@ end

const empty_bitset = BitSet()

function should_infer_this_call(sv::InferenceState)
if sv.params.unoptimize_throw_blocks
# Disable inference of calls in throw blocks, since we're unlikely to
# need their types. There is one exception however: If up until now, the
# function has not seen any side effects, we would like to make sure there
# aren't any in the throw block either to enable other optimizations.
if is_stmt_throw_block(get_curr_ssaflag(sv))
should_infer_for_effects(sv) || return false
end
end
return true
end

function should_infer_for_effects(sv::InferenceState)
sv.ipo_effects.terminates === ALWAYS_TRUE &&
sv.ipo_effects.effect_free === ALWAYS_TRUE
effects = Effects(sv)
return effects.terminates === ALWAYS_TRUE &&
effects.effect_free === ALWAYS_TRUE
end

function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
arginfo::ArgInfo, @nospecialize(atype),
sv::InferenceState, max_methods::Int)
if !should_infer_for_effects(sv) &&
sv.params.unoptimize_throw_blocks &&
is_stmt_throw_block(get_curr_ssaflag(sv))
# Disable inference of calls in throw blocks, since we're unlikely to
# need their types. There is one exception however: If up until now, the
# function has not seen any side effects, we would like to make sure there
# aren't any in the throw block either to enable other optimizations.
if !should_infer_this_call(sv)
add_remark!(interp, sv, "Skipped call in throw block")
nonoverlayed = false
if isoverlayed(method_table(interp)) && is_nonoverlayed(sv.ipo_effects)
# as we may want to concrete-evaluate this frame in cases when there are
# no overlayed calls, try an additional effort now to check if this call
# isn't overlayed rather than just handling it conservatively
matches = find_matching_methods(arginfo.argtypes, atype, method_table(interp),
InferenceParams(interp).MAX_UNION_SPLITTING, max_methods)
InferenceParams(interp).MAX_UNION_SPLITTING, max_methods)
if !isa(matches, FailedMethodMatch)
nonoverlayed = matches.nonoverlayed
end
Expand Down Expand Up @@ -741,8 +749,8 @@ end

function concrete_eval_eligible(interp::AbstractInterpreter,
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState)
# disable concrete-evaluation since this function call is tainted by some overlayed
# method and currently there is no direct way to execute overlayed methods
# disable concrete-evaluation if this function call is tainted by some overlayed
# method since currently there is no direct way to execute overlayed methods
isoverlayed(method_table(interp)) && !is_nonoverlayed(result.edge_effects) && return false
return f !== nothing &&
result.edge !== nothing &&
Expand Down
154 changes: 95 additions & 59 deletions base/compiler/typelattice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
# end
import Core: Const, PartialStruct
function PartialStruct(typ::DataType, fields::Vector{Any})
for i = 1:length(fields) assert_nested_type(fields[i]) end
for i = 1:length(fields)
assert_nested_slotwrapper(fields[i])
end
return Core._PartialStruct(typ, fields)
end

Expand Down Expand Up @@ -47,7 +49,8 @@ struct Conditional
thentype
elsetype
function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype))
assert_nested_type(thentype); assert_nested_type(elsetype)
assert_nested_slotwrapper(thentype)
assert_nested_slotwrapper(elsetype)
return new(slot, thentype, elsetype)
end
end
Expand All @@ -67,7 +70,8 @@ struct InterConditional
thentype
elsetype
function InterConditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype))
assert_nested_type(thentype); assert_nested_type(elsetype)
assert_nested_slotwrapper(thentype)
assert_nested_slotwrapper(elsetype)
return new(slot, thentype, elsetype)
end
end
Expand Down Expand Up @@ -136,7 +140,38 @@ const CompilerTypes = Union{MaybeUndef, Const, Conditional, NotFound, PartialStr
# lattice logic #
#################

assert_nested_type(@nospecialize t) = @assert !(t isa AnyConditional) "found nested conditional"
# slot wrappers
# =============

function assert_nested_slotwrapper(@nospecialize t)
@assert !(t isa Conditional) "found nested Conditional"
@assert !(t isa InterConditional) "found nested InterConditional"
return t
end

widenslotwrapper(@nospecialize typ) = typ
widenslotwrapper(typ::AnyConditional) = widenconditional(typ)
widenwrappedslotwrapper(@nospecialize typ) = widenslotwrapper(typ)
widenwrappedslotwrapper(typ::LimitedAccuracy) = LimitedAccuracy(widenslotwrapper(typ.typ), typ.causes)

# Conditional
# ===========

function widenconditional(@nospecialize typ)
if isa(typ, AnyConditional)
if typ.thentype === Union{}
return Const(false)
elseif typ.elsetype === Union{}
return Const(true)
else
return Bool
end
end
return typ
end
widenconditional(::LimitedAccuracy) = error("unhandled LimitedAccuracy")
widenwrappedconditional(@nospecialize typ) = widenconditional(typ)
widenwrappedconditional(typ::LimitedAccuracy) = LimitedAccuracy(widenconditional(typ.typ), typ.causes)

# `Conditional` and `InterConditional` are valid in opposite contexts
# (i.e. local inference and inter-procedural call), as such they will never be compared
Expand All @@ -151,8 +186,7 @@ function issubconditional(a::C, b::C) where {C<:AnyConditional}
return false
end

is_same_conditionals(a::Conditional, b::Conditional) = a.slot == b.slot
is_same_conditionals(a::InterConditional, b::InterConditional) = a.slot == b.slot
is_same_conditionals(a::C, b::C) where C<:AnyConditional = a.slot == b.slot

is_lattice_bool(@nospecialize(typ)) = typ !== Bottom && typ Bool

Expand All @@ -164,6 +198,15 @@ function maybe_extract_const_bool(c::AnyConditional)
end
maybe_extract_const_bool(@nospecialize c) = nothing

# LimitedAccuracy
# ===============

ignorelimited(@nospecialize typ) = typ
ignorelimited(typ::LimitedAccuracy) = typ.typ

# lattice order
# =============

"""
a ⊑ b -> Bool
Expand Down Expand Up @@ -277,8 +320,12 @@ where we can safely assume `a ⊑ b` holds.
"""
@nospecialize(a) @nospecialize(b) = !(b, a)

# Check if two lattice elements are partial order equivalent. This is basically
# `a ⊑ b && b ⊑ a` but with extra performance optimizations.
"""
is_lattice_equal(a, b) -> Bool
Check if two lattice elements are partial order equivalent.
This is basically `a ⊑ b && b ⊑ a` but with extra performance optimizations.
"""
function is_lattice_equal(@nospecialize(a), @nospecialize(b))
a === b && return true
if isa(a, PartialStruct)
Expand Down Expand Up @@ -313,10 +360,16 @@ function is_lattice_equal(@nospecialize(a), @nospecialize(b))
return a b && b a
end

# compute typeintersect over the extended inference lattice,
# as precisely as we can,
# where v is in the extended lattice, and t is a Type.
function tmeet(@nospecialize(v), @nospecialize(t))
# lattice operations
# ==================

"""
tmeet(v, t::Type) -> x
Computes typeintersect over the extended inference lattice, as precisely as we can,
where `v` is in the extended lattice, and `t` is a `Type`.
"""
function tmeet(@nospecialize(v), @nospecialize(t::Type))
if isa(v, Const)
if !has_free_typevars(t) && !isa(v.val, t)
return Bottom
Expand Down Expand Up @@ -355,16 +408,25 @@ function tmeet(@nospecialize(v), @nospecialize(t))
return ti
end

widenconst(c::AnyConditional) = Bool
widenconst((; val)::Const) = isa(val, Type) ? Type{val} : typeof(val)
"""
widenconst(x) -> t::Type
Widens extended lattice element `x` to native `Type` representation.
"""
widenconst(::AnyConditional) = Bool
widenconst(c::Const) = (v = c.val; isa(v, Type) ? Type{v} : typeof(v))
widenconst(m::MaybeUndef) = widenconst(m.typ)
widenconst(c::PartialTypeVar) = TypeVar
widenconst(::PartialTypeVar) = TypeVar
widenconst(t::PartialStruct) = t.typ
widenconst(t::PartialOpaque) = t.typ
widenconst(t::Type) = t
widenconst(t::TypeVar) = error("unhandled TypeVar")
widenconst(t::TypeofVararg) = error("unhandled Vararg")
widenconst(t::LimitedAccuracy) = error("unhandled LimitedAccuracy")
widenconst(::TypeVar) = error("unhandled TypeVar")
widenconst(::TypeofVararg) = error("unhandled Vararg")
widenconst(::LimitedAccuracy) = error("unhandled LimitedAccuracy")

####################
# state management #
####################

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

Expand All @@ -380,31 +442,11 @@ end
@inline tchanged(@nospecialize(n), @nospecialize(o)) = o === NOT_FOUND || (n !== NOT_FOUND && !(n o))
@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(n::VarState, o::VarState)))

function widenconditional(@nospecialize typ)
if isa(typ, AnyConditional)
if typ.thentype === Union{}
return Const(false)
elseif typ.elsetype === Union{}
return Const(true)
else
return Bool
end
end
return typ
end
widenconditional(t::LimitedAccuracy) = error("unhandled LimitedAccuracy")

widenwrappedconditional(@nospecialize(typ)) = widenconditional(typ)
widenwrappedconditional(typ::LimitedAccuracy) = LimitedAccuracy(widenconditional(typ.typ), typ.causes)

ignorelimited(@nospecialize typ) = typ
ignorelimited(typ::LimitedAccuracy) = typ.typ

# remove any Conditional for this slot from the vartable
function invalidate_conditional(vt::VarState, changeid::Int)
# remove any lattice elements that wrap the reassigned slot object from the vartable
function invalidate_slotwrapper(vt::VarState, changeid::Int, ignore_conditional::Bool)
newtyp = ignorelimited(vt.typ)
if isa(newtyp, Conditional) && newtyp.slot == changeid
newtyp = widenwrappedconditional(vt.typ)
if (!ignore_conditional && isa(newtyp, Conditional) && newtyp.slot == changeid)
newtyp = widenwrappedslotwrapper(vt.typ)
return VarState(newtyp, vt.undef)
end
return nothing
Expand All @@ -419,11 +461,9 @@ function stupdate!(state::VarTable, changes::StateUpdate)
else
newtype = changes.state[i]
end
if !changes.conditional
invalidated = invalidate_conditional(newtype, changeid)
if invalidated !== nothing
newtype = invalidated
end
invalidated = invalidate_slotwrapper(newtype, changeid, changes.conditional)
if invalidated !== nothing
newtype = invalidated
end
oldtype = state[i]
if schanged(newtype, oldtype)
Expand All @@ -449,12 +489,10 @@ end

function stupdate1!(state::VarTable, change::StateUpdate)
changeid = slot_id(change.var)
if !change.conditional
for i = 1:length(state)
invalidated = invalidate_conditional(state[i], changeid)
if invalidated !== nothing
state[i] = invalidated
end
for i = 1:length(state)
invalidated = invalidate_slotwrapper(state[i], changeid, change.conditional)
if invalidated !== nothing
state[i] = invalidated
end
end
# and update the type of it
Expand All @@ -476,12 +514,10 @@ end

function stoverwrite1!(state::VarTable, change::StateUpdate)
changeid = slot_id(change.var)
if !change.conditional
for i = 1:length(state)
invalidated = invalidate_conditional(state[i], changeid)
if invalidated !== nothing
state[i] = invalidated
end
for i = 1:length(state)
invalidated = invalidate_slotwrapper(state[i], changeid, change.conditional)
if invalidated !== nothing
state[i] = invalidated
end
end
# and update the type of it
Expand Down
1 change: 1 addition & 0 deletions base/compiler/typelimits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb))
end
return Bool
end

# type-lattice for Const and PartialStruct wrappers
if ((isa(typea, PartialStruct) || isa(typea, Const)) &&
(isa(typeb, PartialStruct) || isa(typeb, Const)))
Expand Down

0 comments on commit 0dc3a69

Please sign in to comment.