@@ -156,6 +156,19 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
156156 sv. ssavalue_uses[sv. currpc] = saved_uses
157157 end
158158 end
159+ this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[i]
160+ this_arginfo = ArgInfo (fargs, this_argtypes)
161+
162+ early_const_call_result = abstract_call_method_with_const_args_early (interp,
163+ f, match, this_arginfo, sv)
164+ if early_const_call_result != = nothing
165+ this_conditional = this_rt = early_const_call_result. rt
166+ (; effects, const_result) = early_const_call_result
167+ tristate_merge! (sv, effects)
168+ push! (const_results, const_result)
169+ any_const_result = true
170+ @goto call_computed
171+ end
159172
160173 result = abstract_call_method (interp, method, sig, match. sparams, multiple_matches, sv)
161174 this_conditional = ignorelimited (result. rt)
@@ -164,8 +177,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
164177 edge != = nothing && push! (edges, edge)
165178 # try constant propagation with argtypes for this match
166179 # this is in preparation for inlining, or improving the return result
167- this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[i]
168- this_arginfo = ArgInfo (fargs, this_argtypes)
169180 const_call_result = abstract_call_method_with_const_args (interp, result,
170181 f, this_arginfo, match, sv)
171182 effects = result. edge_effects
@@ -185,6 +196,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
185196 push! (const_results, const_result)
186197 any_const_result |= const_result != = nothing
187198 end
199+ @label call_computed
188200 @assert ! (this_conditional isa Conditional) " invalid lattice element returned from inter-procedural context"
189201 seen += 1
190202 rettype = tmerge (rettype, this_rt)
@@ -674,10 +686,11 @@ end
674686function pure_eval_eligible (interp:: AbstractInterpreter ,
675687 @nospecialize (f), applicable:: Vector{Any} , arginfo:: ArgInfo , sv:: InferenceState )
676688 # XXX we need to check that this pure function doesn't call any overlayed method
677- return f != = nothing &&
678- length (applicable) == 1 &&
679- is_method_pure (applicable[1 ]:: MethodMatch ) &&
680- is_all_const_arg (arginfo)
689+ f != = nothing || return false
690+ length (applicable) == 1 || return false
691+ match = applicable[1 ]:: MethodMatch
692+ is_method_pure (match) || return false
693+ return is_all_const_arg (arginfo)
681694end
682695
683696function is_method_pure (method:: Method , @nospecialize (sig), sparams:: SimpleVector )
@@ -710,16 +723,17 @@ end
710723
711724function concrete_eval_eligible (interp:: AbstractInterpreter ,
712725 @nospecialize (f), result:: MethodCallResult , arginfo:: ArgInfo , sv:: InferenceState )
713- # disable concrete-evaluation since this function call is tainted by some overlayed
714- # method and currently there is no direct way to execute overlayed methods
726+ # disable concrete-evaluation if this function call is tainted by some overlayed
727+ # method since currently there is no direct way to execute overlayed methods
715728 isoverlayed (method_table (interp)) && ! is_nonoverlayed (result. edge_effects) && return false
716- return f != = nothing &&
717- result. edge != = nothing &&
718- is_concrete_eval_eligible (result. edge_effects) &&
719- is_all_const_arg (arginfo)
729+ f != = nothing || return false
730+ result. edge != = nothing || return false
731+ is_concrete_eval_eligible (result. edge_effects) || return false
732+ return is_all_const_arg (arginfo)
720733end
721734
722- function is_all_const_arg ((; argtypes):: ArgInfo )
735+ is_all_const_arg ((; argtypes):: ArgInfo ) = is_all_const_arg (argtypes)
736+ function is_all_const_arg (argtypes:: Vector{Any} )
723737 for i = 2 : length (argtypes)
724738 a = widenconditional (argtypes[i])
725739 isa (a, Const) || isconstType (a) || issingletontype (a) || return false
@@ -738,26 +752,61 @@ end
738752function concrete_eval_call (interp:: AbstractInterpreter ,
739753 @nospecialize (f), result:: MethodCallResult , arginfo:: ArgInfo , sv:: InferenceState )
740754 concrete_eval_eligible (interp, f, result, arginfo, sv) || return nothing
755+ return _concrete_eval_call (interp, f, arginfo, result. edge, sv)
756+ end
757+
758+ function _concrete_eval_call (interp:: AbstractInterpreter ,
759+ @nospecialize (f), arginfo:: ArgInfo , edge:: MethodInstance , sv:: InferenceState )
741760 args = collect_const_args (arginfo)
742761 world = get_world_counter (interp)
743762 value = try
744763 Core. _call_in_world_total (world, f, args... )
745764 catch
746765 # The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime
747- return ConstCallResults (Union{}, ConstResult (result . edge, result . edge_effects ), result . edge_effects )
766+ return ConstCallResults (Union{}, ConstResult (edge, EFFECTS_THROWS ), EFFECTS_THROWS )
748767 end
749768 if is_inlineable_constant (value) || call_result_unused (sv)
750769 # If the constant is not inlineable, still do the const-prop, since the
751770 # code that led to the creation of the Const may be inlineable in the same
752771 # circumstance and may be optimizable.
753- return ConstCallResults (Const (value), ConstResult (result. edge, EFFECTS_TOTAL, value), EFFECTS_TOTAL)
772+ return ConstCallResults (Const (value), ConstResult (edge, EFFECTS_TOTAL, value), EFFECTS_TOTAL)
773+ end
774+ return nothing
775+ end
776+
777+ function early_concrete_eval_eligible (interp:: AbstractInterpreter ,
778+ @nospecialize (f), match:: MethodMatch , arginfo:: ArgInfo , sv:: InferenceState )
779+ # the effects for this match may not be derived yet, so disable concrete-evaluation
780+ # immediately when the interpreter can use overlayed methods
781+ isoverlayed (method_table (interp)) && return false
782+ f != = nothing || return false
783+ is_concrete_eval_eligible (decode_effects_override (match. method. purity)) || return false
784+ return is_all_const_arg (arginfo)
785+ end
786+
787+ function early_concrete_eval (interp:: AbstractInterpreter ,
788+ @nospecialize (f), match:: MethodMatch , arginfo:: ArgInfo , sv:: InferenceState )
789+ early_concrete_eval_eligible (interp, f, match, arginfo, sv) || return nothing
790+ edge = specialize_method (match. method, match. spec_types, match. sparams)
791+ edge === nothing && return nothing
792+ return _concrete_eval_call (interp, f, arginfo, edge, sv)
793+ end
794+
795+ function abstract_call_method_with_const_args_early (interp:: AbstractInterpreter ,
796+ @nospecialize (f), match:: MethodMatch , arginfo:: ArgInfo , sv:: InferenceState )
797+ const_prop_enabled (interp, sv, match) || return nothing
798+ val = early_concrete_eval (interp, f, match, arginfo, sv)
799+ if val != = nothing
800+ add_backedge! (val. const_result. mi, sv)
801+ return val
754802 end
803+ # TODO early constant prop' for `@nospecialize`d methods?
755804 return nothing
756805end
757806
758807function const_prop_enabled (interp:: AbstractInterpreter , sv:: InferenceState , match:: MethodMatch )
759808 if ! InferenceParams (interp). ipo_constant_propagation
760- add_remark! (interp, sv, " [constprop] Disabled by parameter" )
809+ add_remark! (interp, sv, " [constprop] Disabled by inference parameter" )
761810 return false
762811 end
763812 method = match. method
@@ -781,12 +830,10 @@ end
781830function abstract_call_method_with_const_args (interp:: AbstractInterpreter , result:: MethodCallResult ,
782831 @nospecialize (f), arginfo:: ArgInfo , match:: MethodMatch ,
783832 sv:: InferenceState )
784- if ! const_prop_enabled (interp, sv, match)
785- return nothing
786- end
833+ const_prop_enabled (interp, sv, match) || return nothing
787834 val = concrete_eval_call (interp, f, result, arginfo, sv)
788835 if val != = nothing
789- add_backedge! (result . edge , sv)
836+ add_backedge! (val . const_result . mi , sv)
790837 return val
791838 end
792839 mi = maybe_get_const_prop_profitable (interp, result, f, arginfo, match, sv)
0 commit comments