Skip to content
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
85 changes: 40 additions & 45 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,16 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
splitsigs = switchtupleunion(sig)
for sig_n in splitsigs
result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, sv)
rt = result.rt
edge = result.edge
(; rt, edge, effects) = result
edge !== nothing && push!(edges, edge)
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
this_arginfo = ArgInfo(fargs, this_argtypes)
const_call_result = abstract_call_method_with_const_args(interp, result,
f, this_arginfo, match, sv)
effects = result.edge_effects
const_result = nothing
if const_call_result !== nothing
const_rt = const_call_result.rt
if const_rt ⊑ rt
rt = const_rt
if const_call_result.rt ⊑ rt
rt = const_call_result.rt
(; effects, const_result) = const_call_result
end
end
Expand Down Expand Up @@ -167,17 +164,16 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
end

result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, sv)
this_conditional = ignorelimited(result.rt)
this_rt = widenwrappedconditional(result.rt)
edge = result.edge
(; rt, edge, effects) = result
this_conditional = ignorelimited(rt)
this_rt = widenwrappedconditional(rt)
edge !== nothing && push!(edges, edge)
# try constant propagation with argtypes for this match
# this is in preparation for inlining, or improving the return result
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
this_arginfo = ArgInfo(fargs, this_argtypes)
const_call_result = abstract_call_method_with_const_args(interp, result,
f, this_arginfo, match, sv)
effects = result.edge_effects
const_result = nothing
if const_call_result !== nothing
this_const_conditional = ignorelimited(const_call_result.rt)
Expand Down Expand Up @@ -606,7 +602,8 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
sparams = recomputed[2]::SimpleVector
end

(; rt, edge, edge_effects) = typeinf_edge(interp, method, sig, sparams, sv)
(; rt, edge, effects) = typeinf_edge(interp, method, sig, sparams, sv)

if edge === nothing
edgecycle = edgelimited = true
end
Expand All @@ -615,16 +612,17 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp
# may have been tainted due to recursion at this point even if it's overridden
if is_effect_overridden(sv, :terminates_globally)
# this frame is known to terminate
edge_effects = Effects(edge_effects, terminates=ALWAYS_TRUE)
effects = Effects(effects, terminates=ALWAYS_TRUE)
elseif is_effect_overridden(method, :terminates_globally)
# this edge is known to terminate
edge_effects = Effects(edge_effects; terminates=ALWAYS_TRUE)
effects = Effects(effects; terminates=ALWAYS_TRUE)
elseif edgecycle
# Some sort of recursion was detected. Even if we did not limit types,
# we cannot guarantee that the call will terminate
edge_effects = Effects(edge_effects; terminates=TRISTATE_UNKNOWN)
effects = Effects(effects; terminates=TRISTATE_UNKNOWN)
end
return MethodCallResult(rt, edgecycle, edgelimited, edge, edge_effects)

return MethodCallResult(rt, edgecycle, edgelimited, edge, effects)
end

function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState)
Expand Down Expand Up @@ -700,13 +698,13 @@ struct MethodCallResult
edgecycle::Bool
edgelimited::Bool
edge::Union{Nothing,MethodInstance}
edge_effects::Effects
effects::Effects
function MethodCallResult(@nospecialize(rt),
edgecycle::Bool,
edgelimited::Bool,
edge::Union{Nothing,MethodInstance},
edge_effects::Effects)
return new(rt, edgecycle, edgelimited, edge, edge_effects)
effects::Effects)
return new(rt, edgecycle, edgelimited, edge, effects)
end
end

Expand Down Expand Up @@ -751,10 +749,10 @@ function concrete_eval_eligible(interp::AbstractInterpreter,
@nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState)
# 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
isoverlayed(method_table(interp)) && !is_nonoverlayed(result.effects) && return false
return f !== nothing &&
result.edge !== nothing &&
is_foldable(result.edge_effects) &&
is_foldable(result.effects) &&
is_all_const_arg(arginfo)
end

Expand Down Expand Up @@ -785,7 +783,7 @@ function concrete_eval_call(interp::AbstractInterpreter,
Core._call_in_world_total(world, f, args...)
catch
# The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime
return ConstCallResults(Union{}, ConcreteResult(result.edge::MethodInstance, result.edge_effects), result.edge_effects)
return ConstCallResults(Union{}, ConcreteResult(result.edge::MethodInstance, result.effects), result.effects)
end
if is_inlineable_constant(value) || call_result_unused(sv)
# If the constant is not inlineable, still do the const-prop, since the
Expand Down Expand Up @@ -929,7 +927,7 @@ function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodC
return false
else
if isa(rt, Const)
if result.edge_effects.nothrow !== ALWAYS_TRUE
if result.effects.nothrow !== ALWAYS_TRUE
# Could still be improved to Bottom (or at least could see the effects improved)
return true
end
Expand Down Expand Up @@ -1596,8 +1594,8 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn
method = match.method
tienv = ccall(:jl_type_intersection_with_env, Any, (Any, Any), nargtype, method.sig)::SimpleVector
ti = tienv[1]; env = tienv[2]::SimpleVector
(; rt, edge) = result = abstract_call_method(interp, method, ti, env, false, sv)
effects = result.edge_effects
result = abstract_call_method(interp, method, ti, env, false, sv)
(; rt, edge, effects) = result
edge !== nothing && add_backedge!(edge::MethodInstance, sv)
match = MethodMatch(ti, env, method, argtype <: method.sig)
res = nothing
Expand Down Expand Up @@ -1746,9 +1744,11 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
return abstract_call_gf_by_type(interp, f, arginfo, atype, sv, max_methods)
end

function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::PartialOpaque, arginfo::ArgInfo, sv::InferenceState)
function abstract_call_opaque_closure(interp::AbstractInterpreter,
closure::PartialOpaque, arginfo::ArgInfo, sv::InferenceState, check::Bool=true)
sig = argtypes_to_type(arginfo.argtypes)
(; rt, edge, edge_effects) = result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv)
result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv)
(; rt, edge, effects) = result
edge !== nothing && add_backedge!(edge, sv)
tt = closure.typ
sigT = (unwrap_unionall(tt)::DataType).parameters[1]
Expand All @@ -1759,12 +1759,21 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::Part
nothing, arginfo, match, sv)
if const_call_result !== nothing
if const_call_result.rt ⊑ rt
(; rt, const_result) = const_call_result
(; rt, effects, const_result) = const_call_result
end
end
end
info = OpaqueClosureCallInfo(match, const_result)
return CallMeta(from_interprocedural!(rt, sv, arginfo, match.spec_types), edge_effects, info)
if check # analyze implicit type asserts on argument and return type
ftt = closure.typ
(aty, rty) = (unwrap_unionall(ftt)::DataType).parameters
rty = rewrap_unionall(rty isa TypeVar ? rty.lb : rty, ftt)
if !(rt ⊑ rty && tuple_tfunc(arginfo.argtypes[2:end]) ⊑ rewrap_unionall(aty, ftt))
effects = Effects(effects; nothrow=TRISTATE_UNKNOWN)
end
end
Comment on lines +1767 to +1774
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Keno could you please confirm this implementation is correct?
The previous implementation is equivalent to

Suggested change
if check # analyze implicit type asserts on argument and return type
ftt = closure.typ
(aty, rty) = (unwrap_unionall(ftt)::DataType).parameters
rty = rewrap_unionall(rty isa TypeVar ? rty.lb : rty, ftt)
if !(rt rty && tuple_tfunc(arginfo.argtypes[2:end]) rewrap_unionall(aty, ftt))
effects = Effects(effects; nothrow=TRISTATE_UNKNOWN)
end
end
if check # analyze implicit type asserts on argument and return type
ftt = closure.typ
(aty, rty) = (unwrap_unionall(ftt)::DataType).parameters
rty = rewrap_unionall(rty isa TypeVar ? rty.lb : rty, ftt)
if rt rty && tuple_tfunc(arginfo.argtypes[2:end]) rewrap_unionall(aty, ftt)
effects = Effects(effects; nothrow=TRISTATE_UNKNOWN)
end
end

whose nothrow check is flipped I think.

rt = from_interprocedural!(rt, sv, arginfo, match.spec_types)
return CallMeta(rt, effects, info)
end

function most_general_argtypes(closure::PartialOpaque)
Expand All @@ -1786,22 +1795,8 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo,
if isa(ft, PartialOpaque)
newargtypes = copy(argtypes)
newargtypes[1] = ft.env
body_call = abstract_call_opaque_closure(interp, ft, ArgInfo(arginfo.fargs, newargtypes), sv)
# Analyze implicit type asserts on argument and return type
ftt = ft.typ
(at, rt) = (unwrap_unionall(ftt)::DataType).parameters
if isa(rt, TypeVar)
rt = rewrap_unionall(rt.lb, ftt)
else
rt = rewrap_unionall(rt, ftt)
end
nothrow = body_call.rt ⊑ rt
if nothrow
nothrow = tuple_tfunc(newargtypes[2:end]) ⊑ rewrap_unionall(at, ftt)
end
return CallMeta(body_call.rt, Effects(body_call.effects,
nothrow = nothrow ? TRISTATE_UNKNOWN : body_call.effects.nothrow),
body_call.info)
return abstract_call_opaque_closure(interp,
ft, ArgInfo(arginfo.fargs, newargtypes), sv, #=check=#true)
elseif (uft = unwrap_unionall(widenconst(ft)); isa(uft, DataType) && uft.name === typename(Core.OpaqueClosure))
return CallMeta(rewrap_unionall((uft::DataType).parameters[2], widenconst(ft)), Effects(), false)
elseif f === nothing
Expand Down Expand Up @@ -2027,7 +2022,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
argtypes = most_general_argtypes(t)
pushfirst!(argtypes, t.env)
callinfo = abstract_call_opaque_closure(interp, t,
ArgInfo(nothing, argtypes), sv)
ArgInfo(nothing, argtypes), sv, #=check=#false)
sv.stmt_info[sv.currpc] = OpaqueClosureCreateInfo(callinfo)
end
end
Expand Down
6 changes: 3 additions & 3 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -840,11 +840,11 @@ ipo_effects(code::CodeInstance) = decode_effects(code.ipo_purity_bits)
struct EdgeCallResult
rt #::Type
edge::Union{Nothing,MethodInstance}
edge_effects::Effects
effects::Effects
function EdgeCallResult(@nospecialize(rt),
edge::Union{Nothing,MethodInstance},
edge_effects::Effects)
return new(rt, edge, edge_effects)
effects::Effects)
return new(rt, edge, effects)
end
end

Expand Down
2 changes: 1 addition & 1 deletion base/compiler/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ function normalize_typevars(method::Method, @nospecialize(atype), sparams::Simpl
sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), at2, method.sig)::SimpleVector
sparams = sp_[2]::SimpleVector
end
return atype, sparams
return Pair{Any,SimpleVector}(atype, sparams)
end

# get a handle to the unique specialization object representing a particular instantiation of a call
Expand Down
8 changes: 4 additions & 4 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1108,10 +1108,10 @@ const SLOT_USED = 0x8
ast_slotflag(@nospecialize(code), i) = ccall(:jl_ir_slotflag, UInt8, (Any, Csize_t), code, i - 1)

"""
may_invoke_generator(method, atype, sparams)
may_invoke_generator(method, atype, sparams) -> Bool

Computes whether or not we may invoke the generator for the given `method` on
the given atype and sparams. For correctness, all generated function are
the given `atype` and `sparams`. For correctness, all generated function are
required to return monotonic answers. However, since we don't expect users to
be able to successfully implement this criterion, we only call generated
functions on concrete types. The one exception to this is that we allow calling
Expand All @@ -1122,8 +1122,8 @@ computes whether we are in either of these cases.
Unlike normal functions, the compilation heuristics still can't generate good dispatch
in some cases, but this may still allow inference not to fall over in some limited cases.
"""
function may_invoke_generator(method::MethodInstance)
return may_invoke_generator(method.def::Method, method.specTypes, method.sparam_vals)
function may_invoke_generator(mi::MethodInstance)
return may_invoke_generator(mi.def::Method, mi.specTypes, mi.sparam_vals)
end
function may_invoke_generator(method::Method, @nospecialize(atype), sparams::SimpleVector)
# If we have complete information, we may always call the generator
Expand Down