Skip to content

Commit

Permalink
Don't bail out of inference early if effects could still be refined (#…
Browse files Browse the repository at this point in the history
…48263)

We have an early out in inference that bails if the inferred return
type of the method being called is `Any`. This makes sense in the
absence of effects, because once the rt has hit `Any`, there is
nothing new we can learn by looking at any subsequent calls.
However, in the presence of effects, we likely want to keep
going if we can prove all methods of the callsite `:foldable` as
being `:foldable` can save significant inference time down the
line if it enables concrete evaluation of the containing function.
  • Loading branch information
Keno authored Jan 17, 2023
1 parent 9582937 commit 5f40f15
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 3 deletions.
4 changes: 3 additions & 1 deletion base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
conditionals[2][i] = tmerge(conditionals[2][i], cnd.elsetype)
end
end
if bail_out_call(interp, rettype, sv)
if bail_out_call(interp, rettype, sv, effects)
add_remark!(interp, sv, "One of the matched returned maximally imprecise information. Bailing on call.")
break
end
end
Expand Down Expand Up @@ -838,6 +839,7 @@ function concrete_eval_eligible(interp::AbstractInterpreter,
elseif !result.effects.noinbounds && stmt_taints_inbounds_consistency(sv)
# If the current statement is @inbounds or we propagate inbounds, the call's consistency
# is tainted and not consteval eligible.
add_remark!(interp, sv, "[constprop] Concrete evel disabled for inbounds")
return nothing
end
isoverlayed(method_table(interp)) && !is_nonoverlayed(result.effects) && return nothing
Expand Down
4 changes: 2 additions & 2 deletions base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,8 @@ add_remark!(::AbstractInterpreter, sv::Union{InferenceState, IRCode}, remark) =
function bail_out_toplevel_call(::AbstractInterpreter, @nospecialize(callsig), sv::Union{InferenceState, IRCode})
return isa(sv, InferenceState) && sv.restrict_abstract_call_sites && !isdispatchtuple(callsig)
end
function bail_out_call(::AbstractInterpreter, @nospecialize(rt), sv::Union{InferenceState, IRCode})
return rt === Any
function bail_out_call(::AbstractInterpreter, @nospecialize(rt), sv::Union{InferenceState, IRCode}, effects::Effects)
return rt === Any && !is_foldable(effects)
end
function bail_out_apply(::AbstractInterpreter, @nospecialize(rt), sv::Union{InferenceState, IRCode})
return rt === Any
Expand Down
7 changes: 7 additions & 0 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4697,3 +4697,10 @@ Base.@constprop :aggressive type_level_recurse1(x...) = x[1] == 2 ? 1 : (length(
Base.@constprop :aggressive type_level_recurse2(x...) = type_level_recurse1(x...)
type_level_recurse_entry() = Val{type_level_recurse1(1)}()
@test Base.return_types(type_level_recurse_entry, ()) |> only == Val{1}

# Test that inference doesn't give up if it can potentially refine effects,
# even if the return type is Any.
f_no_bail_effects_any(x::Any) = x
f_no_bail_effects_any(x::NamedTuple{(:x,), Tuple{Any}}) = getfield(x, 1)
g_no_bail_effects_any(x::Any) = f_no_bail_effects_any(x)
@test Core.Compiler.is_total(Base.infer_effects(g_no_bail_effects_any, Tuple{Any}))

0 comments on commit 5f40f15

Please sign in to comment.