@@ -210,7 +210,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
210210 all_effects = Effects (all_effects; nothrow= false )
211211 end
212212
213- rettype = from_interprocedural! (𝕃ₚ, rettype, sv, arginfo, conditionals)
213+ (; rt, effects) = from_interprocedural! (𝕃ₚ, rettype, all_effects , sv, arginfo, conditionals)
214214
215215 # Also considering inferring the compilation signature for this method, so
216216 # it is available to the compiler in case it ends up needing it.
@@ -219,32 +219,32 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
219219 method = match. method
220220 sig = match. spec_types
221221 mi = specialize_method (match; preexisting= true )
222- if mi != = nothing && ! const_prop_methodinstance_heuristic (interp, mi, arginfo, sv)
222+ if mi != = nothing && ! const_prop_methodinstance_heuristic (interp, mi, arginfo, Effects (), sv)
223223 csig = get_compileable_sig (method, sig, match. sparams)
224224 if csig != = nothing && csig != = sig
225225 abstract_call_method (interp, method, csig, match. sparams, multiple_matches, StmtInfo (false ), sv)
226226 end
227227 end
228228 end
229229
230- if call_result_unused (si) && ! (rettype === Bottom)
230+ if call_result_unused (si) && ! (rt === Bottom)
231231 add_remark! (interp, sv, " Call result type was widened because the return value is unused" )
232232 # We're mainly only here because the optimizer might want this code,
233233 # but we ourselves locally don't typically care about it locally
234234 # (beyond checking if it always throws).
235235 # So avoid adding an edge, since we don't want to bother attempting
236236 # to improve our result even if it does change (to always throw),
237237 # and avoid keeping track of a more complex result type.
238- rettype = Any
238+ rt = Any
239239 end
240- add_call_backedges! (interp, rettype, all_effects , edges, matches, atype, sv)
240+ add_call_backedges! (interp, rt, effects , edges, matches, atype, sv)
241241 if ! isempty (sv. pclimitations) # remove self, if present
242242 delete! (sv. pclimitations, sv)
243243 for caller in sv. callers_in_cycle
244244 delete! (sv. pclimitations, caller)
245245 end
246246 end
247- return CallMeta (rettype, all_effects , info)
247+ return CallMeta (rt, effects , info)
248248end
249249
250250struct FailedMethodMatch
@@ -348,15 +348,24 @@ function find_matching_methods(𝕃::AbstractLattice,
348348 end
349349end
350350
351+ struct InterproceduralResult
352+ rt
353+ effects:: Effects
354+ InterproceduralResult (@nospecialize (rt), effects:: Effects ) = new (rt, effects)
355+ end
356+
351357"""
352- from_interprocedural!(𝕃ₚ::AbstractLattice, rt, sv::InferenceState, arginfo::ArgInfo, maybecondinfo) -> newrt
358+ from_interprocedural!(𝕃ₚ::AbstractLattice, rt, effects::Effects,
359+ sv::InferenceState, arginfo::ArgInfo, maybecondinfo) -> InterproceduralResult
353360
354- Converts inter-procedural return type `rt` into a local lattice element `newrt`,
355- that is appropriate in the context of current local analysis frame `sv`, especially:
361+ Converts extended lattice element `rt` and `effects::Effects` that represent inferred
362+ return type and method call effects into new lattice ement and `Effects` that are
363+ appropriate in the context of current local analysis frame `sv`, especially:
356364- unwraps `rt::LimitedAccuracy` and collects its limitations into the current frame `sv`
357365- converts boolean `rt` to new boolean `newrt` in a way `newrt` can propagate extra conditional
358366 refinement information, e.g. translating `rt::InterConditional` into `newrt::Conditional`
359367 that holds a type constraint information about a variable in `sv`
368+ - recomputes `effects.const_prop_profitable_args` so that they are imposed on call arguments of `sv`
360369
361370This function _should_ be used wherever we propagate results returned from
362371`abstract_call_method` or `abstract_call_method_with_const_args`.
@@ -368,7 +377,8 @@ In such cases `maybecondinfo` should be either of:
368377When we deal with multiple `MethodMatch`es, it's better to precompute `maybecondinfo` by
369378`tmerge`ing argument signature type of each method call.
370379"""
371- function from_interprocedural! (𝕃ₚ:: AbstractLattice , @nospecialize (rt), sv:: InferenceState , arginfo:: ArgInfo , @nospecialize (maybecondinfo))
380+ function from_interprocedural! (𝕃ₚ:: AbstractLattice , @nospecialize (rt), effects:: Effects ,
381+ sv:: InferenceState , arginfo:: ArgInfo , @nospecialize (maybecondinfo))
372382 rt = collect_limitations! (rt, sv)
373383 if isa (rt, InterMustAlias)
374384 rt = from_intermustalias (rt, arginfo)
@@ -380,7 +390,23 @@ function from_interprocedural!(𝕃ₚ::AbstractLattice, @nospecialize(rt), sv::
380390 end
381391 end
382392 @assert ! (rt isa InterConditional || rt isa InterMustAlias) " invalid lattice element returned from inter-procedural context"
383- return rt
393+ if effects. const_prop_profitable_args != = NO_PROFITABLE_ARGS
394+ argsbits = 0x00
395+ fargs = arginfo. fargs
396+ if fargs != = nothing
397+ for i = 1 : length (fargs)
398+ if is_const_prop_profitable_arg (effects, i)
399+ arg = fargs[i]
400+ if is_call_argument (arg, sv) && 1 ≤ slot_id (arg) ≤ 8
401+ argsbits |= 0x01 << (slot_id (arg)- 1 )
402+ end
403+ end
404+ end
405+ end
406+ const_prop_profitable_args = ConstPropProfitableArgs (argsbits)
407+ effects = Effects (effects; const_prop_profitable_args)
408+ end
409+ return InterproceduralResult (rt, effects)
384410end
385411
386412function collect_limitations! (@nospecialize (typ), sv:: InferenceState )
@@ -993,9 +1019,8 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter,
9931019 end
9941020 end
9951021 # try constant prop'
996- inf_cache = get_inference_cache (interp)
9971022 𝕃ᵢ = typeinf_lattice (interp)
998- inf_result = cache_lookup (𝕃ᵢ, mi, arginfo. argtypes, inf_cache )
1023+ inf_result = cache_lookup (𝕃ᵢ, mi, arginfo. argtypes, get_inference_cache (interp) )
9991024 if inf_result === nothing
10001025 # if there might be a cycle, check to make sure we don't end up
10011026 # calling ourselves here.
@@ -1062,7 +1087,7 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter,
10621087 return nothing
10631088 end
10641089 mi = mi:: MethodInstance
1065- if ! force && ! const_prop_methodinstance_heuristic (interp, mi, arginfo, sv)
1090+ if ! force && ! const_prop_methodinstance_heuristic (interp, mi, arginfo, result . effects, sv)
10661091 add_remark! (interp, sv, " [constprop] Disabled by method instance heuristic" )
10671092 return nothing
10681093 end
@@ -1214,7 +1239,7 @@ end
12141239# where we would spend a lot of time, but are probably unlikely to get an improved
12151240# result anyway.
12161241function const_prop_methodinstance_heuristic (interp:: AbstractInterpreter ,
1217- mi:: MethodInstance , arginfo:: ArgInfo , sv:: InferenceState )
1242+ mi:: MethodInstance , arginfo:: ArgInfo , effects :: Effects , sv:: InferenceState )
12181243 method = mi. def:: Method
12191244 if method. is_for_opaque_closure
12201245 # Not inlining an opaque closure can be very expensive, so be generous
@@ -1239,6 +1264,8 @@ function const_prop_methodinstance_heuristic(interp::AbstractInterpreter,
12391264 elseif is_stmt_noinline (flag)
12401265 # this call won't be inlined, thus this constant-prop' will most likely be unfruitful
12411266 return false
1267+ elseif any_const_prop_profitable_args (effects, arginfo. argtypes)
1268+ return true
12421269 else
12431270 # Peek at the inferred result for the method to determine if the optimizer
12441271 # was able to cut it down to something simple (inlineable in particular).
@@ -1256,6 +1283,21 @@ function const_prop_methodinstance_heuristic(interp::AbstractInterpreter,
12561283 return false # the cache isn't inlineable, so this constant-prop' will most likely be unfruitful
12571284end
12581285
1286+ # check if constant information is available on any call argument that has been analyzed as
1287+ # const-prop' profitable
1288+ function any_const_prop_profitable_args (effects:: Effects , argtypes:: Vector{Any} )
1289+ if effects. const_prop_profitable_args === NO_PROFITABLE_ARGS
1290+ return false
1291+ end
1292+ for i in 1 : length (argtypes)
1293+ ai = widenconditional (argtypes[i])
1294+ if isa (ai, Const) && is_const_prop_profitable_arg (effects, i)
1295+ return true
1296+ end
1297+ end
1298+ return false
1299+ end
1300+
12591301# This is only for use with `Conditional`.
12601302# In general, usage of this is wrong.
12611303ssa_def_slot (@nospecialize (arg), sv:: IRCode ) = nothing
@@ -1901,11 +1943,10 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn
19011943 (; rt, effects, const_result, edge) = const_call_result
19021944 end
19031945 end
1904- rt = from_interprocedural! (𝕃ₚ, rt, sv, arginfo, sig)
19051946 effects = Effects (effects; nonoverlayed= ! overlayed)
1906- info = InvokeCallInfo (match, const_result )
1947+ (; rt, effects) = from_interprocedural! (𝕃ₚ, rt, effects, sv, arginfo, sig )
19071948 edge != = nothing && add_invoke_backedge! (sv, lookupsig, edge)
1908- return CallMeta (rt, effects, info )
1949+ return CallMeta (rt, effects, InvokeCallInfo (match, const_result) )
19091950end
19101951
19111952function invoke_rewrite (xs:: Vector{Any} )
@@ -2015,6 +2056,20 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
20152056 return CallMeta (typename_static (argtypes[2 ]), EFFECTS_TOTAL, MethodResultPure ())
20162057 elseif f === Core. _hasmethod
20172058 return _hasmethod_tfunc (interp, argtypes, sv)
2059+ elseif la == 2 && istoptype (f, :Val )
2060+ # `Val` generally encodes constant information into the type domain, so there is
2061+ # generally a high profitability for constant propagation if the argument of the
2062+ # `Val` constructor is a call argument
2063+ fargs = arginfo. fargs
2064+ if fargs != = nothing
2065+ arg = arginfo. fargs[2 ]
2066+ if is_call_argument (arg, sv) && ! isempty (sv. ssavalue_uses[sv. currpc])
2067+ if 1 ≤ slot_id (arg) ≤ 8
2068+ const_prop_profitable_args = ConstPropProfitableArgs (0x01 << (slot_id (arg)- 1 ))
2069+ merge_effects! (interp, sv, Effects (EFFECTS_TOTAL; const_prop_profitable_args))
2070+ end
2071+ end
2072+ end
20182073 end
20192074 atype = argtypes_to_type (argtypes)
20202075 return abstract_call_gf_by_type (interp, f, arginfo, si, atype, sv, max_methods)
@@ -2048,7 +2103,7 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter,
20482103 effects = Effects (effects; nothrow= false )
20492104 end
20502105 end
2051- rt = from_interprocedural! (𝕃ₚ, rt, sv, arginfo, match. spec_types)
2106+ (; rt, effects) = from_interprocedural! (𝕃ₚ, rt, effects , sv, arginfo, match. spec_types)
20522107 info = OpaqueClosureCallInfo (match, const_result)
20532108 edge != = nothing && add_backedge! (sv, edge)
20542109 return CallMeta (rt, effects, info)
@@ -2481,8 +2536,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes:
24812536 override. terminates_globally ? true : effects. terminates,
24822537 override. notaskstate ? true : effects. notaskstate,
24832538 override. inaccessiblememonly ? ALWAYS_TRUE : effects. inaccessiblememonly,
2484- effects. nonoverlayed,
2485- effects. noinbounds)
2539+ effects. nonoverlayed, effects. noinbounds, effects. const_prop_profitable_args)
24862540 end
24872541 return RTEffects (t, effects)
24882542end
@@ -2865,6 +2919,15 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
28652919 @goto branch
28662920 elseif isa (stmt, GotoIfNot)
28672921 condx = stmt. cond
2922+ if is_call_argument (condx, frame)
2923+ # if this condition object is a call argument, there will be a high
2924+ # profitability for constant-propagating it, since it can shape up
2925+ # the generated code by cutting off the dead branch entirely
2926+ if 1 ≤ slot_id (condx) ≤ 8
2927+ const_prop_profitable_args = ConstPropProfitableArgs (0x01 << (slot_id (condx)- 1 ))
2928+ merge_effects! (interp, frame, Effects (EFFECTS_TOTAL; const_prop_profitable_args))
2929+ end
2930+ end
28682931 condt = abstract_eval_value (interp, condx, currstate, frame)
28692932 if condt === Bottom
28702933 ssavaluetypes[currpc] = Bottom
0 commit comments