Skip to content

Commit 5edc091

Browse files
committed
AbsInt: add interfaces to customize cases when cached results are used
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 ba34d89 commit 5edc091

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)