@@ -211,7 +211,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
211211 all_effects = Effects (all_effects; nothrow= false )
212212 end
213213
214- rettype = from_interprocedural! (ipo_lattice (interp), rettype, sv, arginfo, conditionals)
214+ (; rt, effects) = from_interprocedural! (ipo_lattice (interp), rettype, all_effects , sv, arginfo, conditionals)
215215
216216 # Also considering inferring the compilation signature for this method, so
217217 # it is available to the compiler in case it ends up needing it.
@@ -220,32 +220,32 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
220220 method = match. method
221221 sig = match. spec_types
222222 mi = specialize_method (match; preexisting= true )
223- if mi != = nothing && ! const_prop_methodinstance_heuristic (interp, match, mi, arginfo, sv)
223+ if mi != = nothing && ! const_prop_methodinstance_heuristic (interp, mi, arginfo, Effects () , sv)
224224 csig = get_compileable_sig (method, sig, match. sparams)
225225 if csig != = nothing && csig != = sig
226226 abstract_call_method (interp, method, csig, match. sparams, multiple_matches, StmtInfo (false ), sv)
227227 end
228228 end
229229 end
230230
231- if call_result_unused (si) && ! (rettype === Bottom)
231+ if call_result_unused (si) && ! (rt === Bottom)
232232 add_remark! (interp, sv, " Call result type was widened because the return value is unused" )
233233 # We're mainly only here because the optimizer might want this code,
234234 # but we ourselves locally don't typically care about it locally
235235 # (beyond checking if it always throws).
236236 # So avoid adding an edge, since we don't want to bother attempting
237237 # to improve our result even if it does change (to always throw),
238238 # and avoid keeping track of a more complex result type.
239- rettype = Any
239+ rt = Any
240240 end
241- add_call_backedges! (interp, rettype, all_effects , edges, matches, atype, sv)
241+ add_call_backedges! (interp, rt, effects , edges, matches, atype, sv)
242242 if ! isempty (sv. pclimitations) # remove self, if present
243243 delete! (sv. pclimitations, sv)
244244 for caller in sv. callers_in_cycle
245245 delete! (sv. pclimitations, caller)
246246 end
247247 end
248- return CallMeta (rettype, all_effects , info)
248+ return CallMeta (rt, effects , info)
249249end
250250
251251struct FailedMethodMatch
@@ -348,15 +348,24 @@ function find_matching_methods(argtypes::Vector{Any}, @nospecialize(atype), meth
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!(ipo_lattice::AbstractLattice, rt, sv::InferenceState, arginfo::ArgInfo, maybecondinfo) -> newrt
358+ from_interprocedural!(ipo_lattice::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! (ipo_lattice:: AbstractLattice , @nospecialize (rt), sv:: InferenceState , arginfo:: ArgInfo , @nospecialize (maybecondinfo))
380+ function from_interprocedural! (ipo_lattice:: AbstractLattice , @nospecialize (rt), effects:: Effects ,
381+ sv:: InferenceState , arginfo:: ArgInfo , @nospecialize (maybecondinfo))
372382 rt = collect_limitations! (rt, sv)
373383 if is_lattice_bool (ipo_lattice, rt)
374384 if maybecondinfo === nothing
@@ -378,7 +388,23 @@ function from_interprocedural!(ipo_lattice::AbstractLattice, @nospecialize(rt),
378388 end
379389 end
380390 @assert ! (rt isa InterConditional) " invalid lattice element returned from inter-procedural context"
381- return rt
391+ if effects. const_prop_profitable_args != = NO_PROFITABLE_ARGS
392+ argsbits = 0x00
393+ fargs = arginfo. fargs
394+ if fargs != = nothing
395+ for i = 1 : length (fargs)
396+ if is_const_prop_profitable_arg (effects, i)
397+ arg = fargs[i]
398+ if is_call_argument (arg, sv) && 1 ≤ slot_id (arg) ≤ 8
399+ argsbits |= 0x01 << (slot_id (arg)- 1 )
400+ end
401+ end
402+ end
403+ end
404+ const_prop_profitable_args = ConstPropProfitableArgs (argsbits)
405+ effects = Effects (effects; const_prop_profitable_args)
406+ end
407+ return InterproceduralResult (rt, effects)
382408end
383409
384410function collect_limitations! (@nospecialize (typ), sv:: InferenceState )
@@ -906,8 +932,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter,
906932 end
907933 end
908934 # try constant prop'
909- inf_cache = get_inference_cache (interp)
910- inf_result = cache_lookup (typeinf_lattice (interp), mi, arginfo. argtypes, inf_cache)
935+ inf_result = cache_lookup (typeinf_lattice (interp), mi, arginfo. argtypes, get_inference_cache (interp))
911936 if inf_result === nothing
912937 # if there might be a cycle, check to make sure we don't end up
913938 # calling ourselves here.
@@ -964,7 +989,7 @@ function maybe_get_const_prop_profitable(interp::AbstractInterpreter,
964989 return nothing
965990 end
966991 mi = mi:: MethodInstance
967- if ! force && ! const_prop_methodinstance_heuristic (interp, match, mi, arginfo, sv)
992+ if ! force && ! const_prop_methodinstance_heuristic (interp, mi, arginfo, result . effects , sv)
968993 add_remark! (interp, sv, " [constprop] Disabled by method instance heuristic" )
969994 return nothing
970995 end
@@ -1128,8 +1153,8 @@ end
11281153# where we would spend a lot of time, but are probably unlikely to get an improved
11291154# result anyway.
11301155function const_prop_methodinstance_heuristic (interp:: AbstractInterpreter ,
1131- match :: MethodMatch , mi:: MethodInstance , arginfo:: ArgInfo , sv:: InferenceState )
1132- method = match . method
1156+ mi:: MethodInstance , arginfo:: ArgInfo , effects :: Effects , sv:: InferenceState )
1157+ method = mi . def :: Method
11331158 if method. is_for_opaque_closure
11341159 # Not inlining an opaque closure can be very expensive, so be generous
11351160 # with the const-prop-ability. It is quite possible that we can't infer
@@ -1153,6 +1178,8 @@ function const_prop_methodinstance_heuristic(interp::AbstractInterpreter,
11531178 elseif is_stmt_noinline (flag)
11541179 # this call won't be inlined, thus this constant-prop' will most likely be unfruitful
11551180 return false
1181+ elseif any_const_prop_profitable_args (effects, arginfo. argtypes)
1182+ return true
11561183 else
11571184 code = get (code_cache (interp), mi, nothing )
11581185 if isdefined (code, :inferred )
@@ -1161,7 +1188,6 @@ function const_prop_methodinstance_heuristic(interp::AbstractInterpreter,
11611188 else
11621189 inferred = code. inferred
11631190 end
1164- # TODO propagate a specific `CallInfo` that conveys information about this call
11651191 if inlining_policy (interp, inferred, NoCallInfo (), IR_FLAG_NULL, mi, arginfo. argtypes) != = nothing
11661192 return true
11671193 end
@@ -1171,6 +1197,21 @@ function const_prop_methodinstance_heuristic(interp::AbstractInterpreter,
11711197 return false # the cache isn't inlineable, so this constant-prop' will most likely be unfruitful
11721198end
11731199
1200+ # check if constant information is available on any call argument that has been analyzed as
1201+ # const-prop' profitable
1202+ function any_const_prop_profitable_args (effects:: Effects , argtypes:: Vector{Any} )
1203+ if effects. const_prop_profitable_args === NO_PROFITABLE_ARGS
1204+ return false
1205+ end
1206+ for i in 1 : length (argtypes)
1207+ ai = widenconditional (argtypes[i])
1208+ if isa (ai, Const) && is_const_prop_profitable_arg (effects, i)
1209+ return true
1210+ end
1211+ end
1212+ return false
1213+ end
1214+
11741215# This is only for use with `Conditional`.
11751216# In general, usage of this is wrong.
11761217ssa_def_slot (@nospecialize (arg), sv:: IRCode ) = nothing
@@ -1711,8 +1752,9 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn
17111752 end
17121753 end
17131754 effects = Effects (effects; nonoverlayed= ! overlayed)
1755+ (; rt, effects) = from_interprocedural! (ipo_lattice (interp), rt, effects, sv, arginfo, sig)
17141756 edge != = nothing && add_invoke_backedge! (sv, lookupsig, edge)
1715- return CallMeta (from_interprocedural! ( ipo_lattice (interp), rt, sv, arginfo, sig) , effects, InvokeCallInfo (match, const_result))
1757+ return CallMeta (rt , effects, InvokeCallInfo (match, const_result))
17161758end
17171759
17181760function invoke_rewrite (xs:: Vector{Any} )
@@ -1824,6 +1866,20 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
18241866 val = _pure_eval_call (f, arginfo)
18251867 return CallMeta (val === nothing ? Type : val, EFFECTS_TOTAL, MethodResultPure ())
18261868 end
1869+ elseif la == 2 && istoptype (f, :Val )
1870+ # `Val` generally encodes constant information into the type domain, so there is
1871+ # generally a high profitability for constant propagation if the argument of the
1872+ # `Val` constructor is a call argument
1873+ fargs = arginfo. fargs
1874+ if fargs != = nothing
1875+ arg = arginfo. fargs[2 ]
1876+ if is_call_argument (arg, sv) && ! isempty (sv. ssavalue_uses[sv. currpc])
1877+ if 1 ≤ slot_id (arg) ≤ 8
1878+ const_prop_profitable_args = ConstPropProfitableArgs (0x01 << (slot_id (arg)- 1 ))
1879+ merge_effects! (interp, sv, Effects (EFFECTS_TOTAL; const_prop_profitable_args))
1880+ end
1881+ end
1882+ end
18271883 end
18281884 atype = argtypes_to_type (argtypes)
18291885 return abstract_call_gf_by_type (interp, f, arginfo, si, atype, sv, max_methods)
@@ -1858,7 +1914,7 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter,
18581914 effects = Effects (effects; nothrow= false )
18591915 end
18601916 end
1861- rt = from_interprocedural! (ipo, rt, sv, arginfo, match. spec_types)
1917+ (; rt, effects) = from_interprocedural! (ipo, rt, effects , sv, arginfo, match. spec_types)
18621918 edge != = nothing && add_backedge! (sv, edge)
18631919 return CallMeta (rt, effects, info)
18641920end
@@ -2226,7 +2282,7 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes:
22262282 override. terminates_globally ? true : effects. terminates,
22272283 override. notaskstate ? true : effects. notaskstate,
22282284 override. inaccessiblememonly ? ALWAYS_TRUE : effects. inaccessiblememonly,
2229- effects. nonoverlayed)
2285+ effects. nonoverlayed, effects . const_prop_profitable_args )
22302286 end
22312287 return RTEffects (t, effects)
22322288end
@@ -2513,6 +2569,15 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
25132569 @goto branch
25142570 elseif isa (stmt, GotoIfNot)
25152571 condx = stmt. cond
2572+ if is_call_argument (condx, frame)
2573+ # if this condition object is a call argument, there will be a high
2574+ # profitability for constant-propagating it, since it can shape up
2575+ # the generated code by cutting off the dead branch entirely
2576+ if 1 ≤ slot_id (condx) ≤ 8
2577+ const_prop_profitable_args = ConstPropProfitableArgs (0x01 << (slot_id (condx)- 1 ))
2578+ merge_effects! (interp, frame, Effects (EFFECTS_TOTAL; const_prop_profitable_args))
2579+ end
2580+ end
25162581 condt = abstract_eval_value (interp, condx, currstate, frame)
25172582 if condt === Bottom
25182583 ssavaluetypes[currpc] = Bottom
0 commit comments