Skip to content

Commit

Permalink
Loosen hardlimit application heuristic slightly
Browse files Browse the repository at this point in the history
Ordinarily our recursion heuristic looks for recursion of edges,
widening types to force convergence if a a recursion of edges is
detected. However, under some circumstances (currently - as of
#31734 when there are multiple applicable methods), we fall back
to simple recursion of methods. This can be quite limiting for
packages that have a big central dispatch method (e.g. Diffractor).
This attempts to find a middle ground by applying the hardlimit
fallback only if the split signatures are not concrete. The kind of
case that we want to catch here often arise from signatures with
unresolved Vararg (where there's a lot of destructuring methods,
but since the size of the vararg is not known, we never terminate)
or overly abstract types. I'm hoping this provides a better middle
ground tradeoff by still prohibiting those kinds of cases while
allowing otherwise very concretely inferred code that just happens
to dispatch through a central higher-order method.
  • Loading branch information
Keno committed Jul 17, 2023
1 parent 5f41931 commit 6cf3f72
Showing 1 changed file with 13 additions and 11 deletions.
24 changes: 13 additions & 11 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
if splitunions
splitsigs = switchtupleunion(sig)
for sig_n in splitsigs
result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, si, sv)
apply_hardlimit = multiple_matches && !isdispatchtuple(sig_n)
result = abstract_call_method(interp, method, sig_n, svec(), apply_hardlimit, si, sv)
(; rt, edge, effects) = result
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
this_arginfo = ArgInfo(fargs, this_argtypes)
Expand All @@ -92,7 +93,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
this_conditional = ignorelimited(this_rt)
this_rt = widenwrappedconditional(this_rt)
else
result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, si, sv)
apply_hardlimit = multiple_matches && !isdispatchtuple(sig)
result = abstract_call_method(interp, method, sig, match.sparams, apply_hardlimit, si, sv)
(; rt, edge, effects) = result
this_conditional = ignorelimited(rt)
this_rt = widenwrappedconditional(rt)
Expand Down Expand Up @@ -519,6 +521,13 @@ function abstract_call_method(interp::AbstractInterpreter,
edgecycle = edgelimited = false
topmost = nothing

# The `method_for_inference_heuristics` will expand the given method's generator if
# necessary in order to retrieve this field from the generated `CodeInfo`, if it exists.
# The other `CodeInfo`s we inspect will already have this field inflated, so we just
# access it directly instead (to avoid regeneration).
world = get_world_counter(interp)
callee_method2 = method_for_inference_heuristics(method, sig, sparams, world) # Union{Method, Nothing}

for sv′ in AbsIntStackUnwind(sv)
infmi = frame_instance(sv′)
if method === infmi.def
Expand All @@ -538,7 +547,7 @@ function abstract_call_method(interp::AbstractInterpreter,
break
end
topmost === nothing || continue
if edge_matches_sv(interp, sv′, method, sig, sparams, hardlimit, sv)
if edge_matches_sv(interp, sv′, method, sig, callee_method2, hardlimit, sv)
topmost = sv′
edgecycle = true
end
Expand Down Expand Up @@ -654,15 +663,8 @@ function abstract_call_method(interp::AbstractInterpreter,
end

function edge_matches_sv(interp::AbstractInterpreter, frame::AbsIntState,
method::Method, @nospecialize(sig), sparams::SimpleVector,
method::Method, @nospecialize(sig), callee_method2::Union{Method, Nothing},
hardlimit::Bool, sv::AbsIntState)
# The `method_for_inference_heuristics` will expand the given method's generator if
# necessary in order to retrieve this field from the generated `CodeInfo`, if it exists.
# The other `CodeInfo`s we inspect will already have this field inflated, so we just
# access it directly instead (to avoid regeneration).
world = get_world_counter(interp)
callee_method2 = method_for_inference_heuristics(method, sig, sparams, world) # Union{Method, Nothing}

inf_method2 = method_for_inference_limit_heuristics(frame) # limit only if user token match
inf_method2 isa Method || (inf_method2 = nothing)
if callee_method2 !== inf_method2
Expand Down

0 comments on commit 6cf3f72

Please sign in to comment.