Skip to content

Commit 09a27b3

Browse files
authored
AbsInt: add interfaces to customize cases when cached results are used (#53318)
For external abstract interpreters like JET, which propagate custom data interprocedurally, it is essential to inject custom behaviors into where cached regular/constant inference results are used. Previously, this was accomplished by overloading both `abstract_call_method` and `get(::WorldView{CustomView}, ...)` (and `const_prop_call` and `cached_lookup`), that method was admittedly hacky and should probably better to be avoided. Moreover, after #52233, doing so has become infeasible when the external abstract interpreter uses `InternalCodeCache`. To address this issue, this commit provides an interface named `return_cached_result`. This allows external abstract interpreters to inject custom interprocedural data propagation during abstract interpretation even when they use `InternalCodeCache`.
1 parent 4a13e9e commit 09a27b3

File tree

2 files changed

+48
-34
lines changed

2 files changed

+48
-34
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,45 +1221,53 @@ function semi_concrete_eval_call(interp::AbstractInterpreter,
12211221
return nothing
12221222
end
12231223

1224+
const_prop_result(inf_result::InferenceResult) =
1225+
ConstCallResults(inf_result.result, inf_result.exc_result, ConstPropResult(inf_result),
1226+
inf_result.ipo_effects, inf_result.linfo)
1227+
1228+
# return cached constant analysis result
1229+
return_cached_result(::AbstractInterpreter, inf_result::InferenceResult, ::AbsIntState) =
1230+
const_prop_result(inf_result)
1231+
12241232
function const_prop_call(interp::AbstractInterpreter,
12251233
mi::MethodInstance, result::MethodCallResult, arginfo::ArgInfo, sv::AbsIntState,
12261234
concrete_eval_result::Union{Nothing, ConstCallResults}=nothing)
12271235
inf_cache = get_inference_cache(interp)
12281236
𝕃ᵢ = typeinf_lattice(interp)
12291237
inf_result = cache_lookup(𝕃ᵢ, mi, arginfo.argtypes, inf_cache)
1230-
if inf_result === nothing
1231-
# fresh constant prop'
1232-
argtypes = has_conditional(𝕃ᵢ, sv) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes)
1233-
inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp))
1234-
if !any(inf_result.overridden_by_const)
1235-
add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes")
1236-
return nothing
1237-
end
1238-
frame = InferenceState(inf_result, #=cache_mode=#:local, interp)
1239-
if frame === nothing
1240-
add_remark!(interp, sv, "[constprop] Could not retrieve the source")
1241-
return nothing # this is probably a bad generated function (unsound), but just ignore it
1242-
end
1243-
frame.parent = sv
1244-
if !typeinf(interp, frame)
1245-
add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle")
1246-
return nothing
1247-
end
1248-
@assert inf_result.result !== nothing
1249-
if concrete_eval_result !== nothing
1250-
# override return type and effects with concrete evaluation result if available
1251-
inf_result.result = concrete_eval_result.rt
1252-
inf_result.ipo_effects = concrete_eval_result.effects
1253-
end
1254-
else
1238+
if inf_result !== nothing
12551239
# found the cache for this constant prop'
12561240
if inf_result.result === nothing
12571241
add_remark!(interp, sv, "[constprop] Found cached constant inference in a cycle")
12581242
return nothing
12591243
end
1244+
@assert inf_result.linfo === mi "MethodInstance for cached inference result does not match"
1245+
return return_cached_result(interp, inf_result, sv)
1246+
end
1247+
# perform fresh constant prop'
1248+
argtypes = has_conditional(𝕃ᵢ, sv) ? ConditionalArgtypes(arginfo, sv) : SimpleArgtypes(arginfo.argtypes)
1249+
inf_result = InferenceResult(mi, argtypes, typeinf_lattice(interp))
1250+
if !any(inf_result.overridden_by_const)
1251+
add_remark!(interp, sv, "[constprop] Could not handle constant info in matching_cache_argtypes")
1252+
return nothing
1253+
end
1254+
frame = InferenceState(inf_result, #=cache_mode=#:local, interp)
1255+
if frame === nothing
1256+
add_remark!(interp, sv, "[constprop] Could not retrieve the source")
1257+
return nothing # this is probably a bad generated function (unsound), but just ignore it
1258+
end
1259+
frame.parent = sv
1260+
if !typeinf(interp, frame)
1261+
add_remark!(interp, sv, "[constprop] Fresh constant inference hit a cycle")
1262+
return nothing
1263+
end
1264+
@assert inf_result.result !== nothing
1265+
if concrete_eval_result !== nothing
1266+
# override return type and effects with concrete evaluation result if available
1267+
inf_result.result = concrete_eval_result.rt
1268+
inf_result.ipo_effects = concrete_eval_result.effects
12601269
end
1261-
return ConstCallResults(inf_result.result, inf_result.exc_result,
1262-
ConstPropResult(inf_result), inf_result.ipo_effects, mi)
1270+
return const_prop_result(inf_result)
12631271
end
12641272

12651273
# TODO implement MustAlias forwarding

base/compiler/typeinfer.jl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -818,23 +818,29 @@ struct EdgeCallResult
818818
end
819819
end
820820

821+
# return cached regular inference result
822+
function return_cached_result(::AbstractInterpreter, codeinst::CodeInstance, caller::AbsIntState)
823+
rt = cached_return_type(codeinst)
824+
effects = ipo_effects(codeinst)
825+
update_valid_age!(caller, WorldRange(min_world(codeinst), max_world(codeinst)))
826+
return EdgeCallResult(rt, codeinst.exctype, codeinst.def, effects)
827+
end
828+
821829
# compute (and cache) an inferred AST and return the current best estimate of the result type
822830
function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState)
823831
mi = specialize_method(method, atype, sparams)::MethodInstance
824-
code = get(code_cache(interp), mi, nothing)
832+
codeinst = get(code_cache(interp), mi, nothing)
825833
force_inline = is_stmt_inline(get_curr_ssaflag(caller))
826-
if code isa CodeInstance # return existing rettype if the code is already inferred
827-
inferred = @atomic :monotonic code.inferred
834+
if codeinst isa CodeInstance # return existing rettype if the code is already inferred
835+
inferred = @atomic :monotonic codeinst.inferred
828836
if inferred === nothing && force_inline
829837
# we already inferred this edge before and decided to discard the inferred code,
830838
# nevertheless we re-infer it here again in order to propagate the re-inferred
831839
# source to the inliner as a volatile result
832840
cache_mode = CACHE_MODE_VOLATILE
833841
else
834-
rt = cached_return_type(code)
835-
effects = ipo_effects(code)
836-
update_valid_age!(caller, WorldRange(min_world(code), max_world(code)))
837-
return EdgeCallResult(rt, code.exctype, mi, effects)
842+
@assert codeinst.def === mi "MethodInstance for cached edge does not match"
843+
return return_cached_result(interp, codeinst, caller)
838844
end
839845
else
840846
cache_mode = CACHE_MODE_GLOBAL # cache edge targets globally by default

0 commit comments

Comments
 (0)