Skip to content

Commit d479660

Browse files
authored
inference: always bail out const-prop' with non-const result limited (#52836)
Investigating into #52763, I've found that `AbstractInterpreters` which enables the `aggressive_constprop` option, such as `REPLInterpreter`, might perform const-prop' even if the result of a non-const call is `LimitedAccuracy`. This can lead to an unintended infinite loop with a custom aggressive const-prop' implementation. This commit restricts const-prop' for such cases where the non-const call result is limited to avoid the issue. This fix is conservative, but given that accurate inference is mostly impossible when there are unresolvable cycles (which is indicated by limited result), aggressive const-prop' isn't necessary for such cases, and I don't anticipate this leading to any obvious regression. fix #52763
1 parent 89710bf commit d479660

File tree

2 files changed

+42
-27
lines changed

2 files changed

+42
-27
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -814,12 +814,7 @@ end
814814
function abstract_call_method_with_const_args(interp::AbstractInterpreter,
815815
result::MethodCallResult, @nospecialize(f), arginfo::ArgInfo, si::StmtInfo,
816816
match::MethodMatch, sv::AbsIntState, invokecall::Union{Nothing,InvokeCall}=nothing)
817-
818-
if !const_prop_enabled(interp, sv, match)
819-
return nothing
820-
end
821-
if bail_out_const_call(interp, result, si)
822-
add_remark!(interp, sv, "[constprop] No more information to be gained")
817+
if !const_prop_enabled(interp, match, sv) || bail_out_const_call(interp, result, si, sv)
823818
return nothing
824819
end
825820
eligibility = concrete_eval_eligible(interp, f, result, arginfo, sv)
@@ -852,7 +847,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter,
852847
return const_prop_call(interp, mi, result, arginfo, sv, concrete_eval_result)
853848
end
854849

855-
function const_prop_enabled(interp::AbstractInterpreter, sv::AbsIntState, match::MethodMatch)
850+
function const_prop_enabled(interp::AbstractInterpreter, match::MethodMatch, sv::AbsIntState)
856851
if !InferenceParams(interp).ipo_constant_propagation
857852
add_remark!(interp, sv, "[constprop] Disabled by parameter")
858853
return false
@@ -864,9 +859,11 @@ function const_prop_enabled(interp::AbstractInterpreter, sv::AbsIntState, match:
864859
return true
865860
end
866861

867-
function bail_out_const_call(interp::AbstractInterpreter, result::MethodCallResult, si::StmtInfo)
862+
function bail_out_const_call(interp::AbstractInterpreter, result::MethodCallResult,
863+
si::StmtInfo, sv::AbsIntState)
868864
if is_removable_if_unused(result.effects)
869865
if isa(result.rt, Const) || call_result_unused(si)
866+
add_remark!(interp, sv, "[constprop] No more information to be gained (const)")
870867
return true
871868
end
872869
elseif result.rt === Bottom
@@ -876,6 +873,7 @@ function bail_out_const_call(interp::AbstractInterpreter, result::MethodCallResu
876873
# precise enough to let us determine :consistency of `exct`, so we
877874
# would have to force constprop just to determine this, which is too
878875
# expensive.
876+
add_remark!(interp, sv, "[constprop] No more information to be gained (bottom)")
879877
return true
880878
end
881879
end
@@ -974,7 +972,10 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter,
974972
match::MethodMatch, sv::AbsIntState)
975973
method = match.method
976974
force = force_const_prop(interp, f, method)
977-
force || const_prop_entry_heuristic(interp, result, si, sv) || return nothing
975+
if !const_prop_entry_heuristic(interp, result, si, sv, force)
976+
# N.B. remarks are emitted within `const_prop_entry_heuristic`
977+
return nothing
978+
end
978979
nargs::Int = method.nargs
979980
method.isva && (nargs -= 1)
980981
length(arginfo.argtypes) < nargs && return nothing
@@ -1001,8 +1002,17 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter,
10011002
return mi
10021003
end
10031004

1004-
function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodCallResult, si::StmtInfo, sv::AbsIntState)
1005-
if call_result_unused(si) && result.edgecycle
1005+
function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodCallResult,
1006+
si::StmtInfo, sv::AbsIntState, force::Bool)
1007+
if result.rt isa LimitedAccuracy
1008+
# optimizations like inlining are disabled for limited frames,
1009+
# thus there won't be much benefit in constant-prop' here
1010+
# N.B. don't allow forced constprop' for safety (xref #52763)
1011+
add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (limited accuracy)")
1012+
return false
1013+
elseif force
1014+
return true
1015+
elseif call_result_unused(si) && result.edgecycle
10061016
add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (edgecycle with unused result)")
10071017
return false
10081018
end
@@ -1015,27 +1025,21 @@ function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodC
10151025
if rt === Bottom
10161026
add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (erroneous result)")
10171027
return false
1018-
else
1019-
return true
10201028
end
1029+
return true
10211030
elseif isa(rt, PartialStruct) || isa(rt, InterConditional) || isa(rt, InterMustAlias)
10221031
# could be improved to `Const` or a more precise wrapper
10231032
return true
1024-
elseif isa(rt, LimitedAccuracy)
1025-
# optimizations like inlining are disabled for limited frames,
1026-
# thus there won't be much benefit in constant-prop' here
1027-
add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (limited accuracy)")
1028-
return false
1029-
else
1030-
if isa(rt, Const)
1031-
if !is_nothrow(result.effects)
1032-
# Could still be improved to Bottom (or at least could see the effects improved)
1033-
return true
1034-
end
1033+
elseif isa(rt, Const)
1034+
if is_nothrow(result.effects)
1035+
add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (nothrow const)")
1036+
return false
10351037
end
1036-
add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (unimprovable result)")
1037-
return false
1038+
# Could still be improved to Bottom (or at least could see the effects improved)
1039+
return true
10381040
end
1041+
add_remark!(interp, sv, "[constprop] Disabled by entry heuristic (unimprovable result)")
1042+
return false
10391043
end
10401044

10411045
# determines heuristically whether if constant propagation can be worthwhile

test/compiler/inference.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4937,10 +4937,21 @@ g() = empty_nt_values(Base.inferencebarrier(Tuple{}))
49374937
# This is somewhat sensitive to the exact recursion level that inference is willing to do, but the intention
49384938
# is to test the case where inference limited a recursion, but then a forced constprop nevertheless managed
49394939
# to terminate the call.
4940+
@newinterp RecurseInterpreter
4941+
let CC = Core.Compiler
4942+
function CC.const_prop_entry_heuristic(interp::RecurseInterpreter, result::CC.MethodCallResult,
4943+
si::CC.StmtInfo, sv::CC.AbsIntState, force::Bool)
4944+
if result.rt isa CC.LimitedAccuracy
4945+
return force # allow forced constprop to recurse into unresolved cycles
4946+
end
4947+
return @invoke CC.const_prop_entry_heuristic(interp::CC.AbstractInterpreter, result::CC.MethodCallResult,
4948+
si::CC.StmtInfo, sv::CC.AbsIntState, force::Bool)
4949+
end
4950+
end
49404951
Base.@constprop :aggressive type_level_recurse1(x...) = x[1] == 2 ? 1 : (length(x) > 100 ? x : type_level_recurse2(x[1] + 1, x..., x...))
49414952
Base.@constprop :aggressive type_level_recurse2(x...) = type_level_recurse1(x...)
49424953
type_level_recurse_entry() = Val{type_level_recurse1(1)}()
4943-
@test Base.return_types(type_level_recurse_entry, ()) |> only == Val{1}
4954+
@test Base.infer_return_type(type_level_recurse_entry, (); interp=RecurseInterpreter()) == Val{1}
49444955

49454956
# Test that inference doesn't give up if it can potentially refine effects,
49464957
# even if the return type is Any.

0 commit comments

Comments
 (0)