@@ -158,6 +158,19 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
158158 sv. ssavalue_uses[sv. currpc] = saved_uses
159159 end
160160 end
161+ this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[i]
162+ this_arginfo = ArgInfo (fargs, this_argtypes)
163+
164+ early_const_call_result = abstract_call_method_with_const_args_early (interp,
165+ f, match, this_arginfo, sv)
166+ if early_const_call_result != = nothing
167+ this_conditional = this_rt = early_const_call_result. rt
168+ (; effects, const_result) = early_const_call_result
169+ tristate_merge! (sv, effects)
170+ push! (const_results, const_result)
171+ any_const_result = true
172+ @goto call_computed
173+ end
161174
162175 result = abstract_call_method (interp, method, sig, match. sparams, multiple_matches, sv)
163176 this_conditional = ignorelimited (result. rt)
@@ -166,8 +179,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
166179 edge != = nothing && push! (edges, edge)
167180 # try constant propagation with argtypes for this match
168181 # this is in preparation for inlining, or improving the return result
169- this_argtypes = isa (matches, MethodMatches) ? argtypes : matches. applicable_argtypes[i]
170- this_arginfo = ArgInfo (fargs, this_argtypes)
171182 const_call_result = abstract_call_method_with_const_args (interp, result,
172183 f, this_arginfo, match, sv)
173184 effects = result. edge_effects
@@ -187,6 +198,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
187198 push! (const_results, const_result)
188199 any_const_result |= const_result != = nothing
189200 end
201+ @label call_computed
190202 @assert ! (this_conditional isa Conditional) " invalid lattice element returned from inter-procedural context"
191203 seen += 1
192204 rettype = tmerge (rettype, this_rt)
@@ -682,10 +694,11 @@ end
682694function pure_eval_eligible (interp:: AbstractInterpreter ,
683695 @nospecialize (f), applicable:: Vector{Any} , arginfo:: ArgInfo , sv:: InferenceState )
684696 # XXX we need to check that this pure function doesn't call any overlayed method
685- return f != = nothing &&
686- length (applicable) == 1 &&
687- is_method_pure (applicable[1 ]:: MethodMatch ) &&
688- is_all_const_arg (arginfo)
697+ f != = nothing || return false
698+ length (applicable) == 1 || return false
699+ match = applicable[1 ]:: MethodMatch
700+ is_method_pure (match) || return false
701+ return is_all_const_arg (arginfo)
689702end
690703
691704function is_method_pure (method:: Method , @nospecialize (sig), sparams:: SimpleVector )
@@ -718,16 +731,17 @@ end
718731
719732function concrete_eval_eligible (interp:: AbstractInterpreter ,
720733 @nospecialize (f), result:: MethodCallResult , arginfo:: ArgInfo , sv:: InferenceState )
721- # disable concrete-evaluation since this function call is tainted by some overlayed
722- # method and currently there is no direct way to execute overlayed methods
734+ # disable concrete-evaluation if this function call is tainted by some overlayed
735+ # method since currently there is no direct way to execute overlayed methods
723736 isoverlayed (method_table (interp)) && ! is_nonoverlayed (result. edge_effects) && return false
724- return f != = nothing &&
725- result. edge != = nothing &&
726- is_concrete_eval_eligible (result. edge_effects) &&
727- is_all_const_arg (arginfo)
737+ f != = nothing || return false
738+ result. edge != = nothing || return false
739+ is_concrete_eval_eligible (result. edge_effects) || return false
740+ return is_all_const_arg (arginfo)
728741end
729742
730- function is_all_const_arg ((; argtypes):: ArgInfo )
743+ is_all_const_arg ((; argtypes):: ArgInfo ) = is_all_const_arg (argtypes)
744+ function is_all_const_arg (argtypes:: Vector{Any} )
731745 for i = 2 : length (argtypes)
732746 a = widenconditional (argtypes[i])
733747 isa (a, Const) || isconstType (a) || issingletontype (a) || return false
@@ -746,26 +760,61 @@ end
746760function concrete_eval_call (interp:: AbstractInterpreter ,
747761 @nospecialize (f), result:: MethodCallResult , arginfo:: ArgInfo , sv:: InferenceState )
748762 concrete_eval_eligible (interp, f, result, arginfo, sv) || return nothing
763+ return _concrete_eval_call (interp, f, arginfo, result. edge, sv)
764+ end
765+
766+ function _concrete_eval_call (interp:: AbstractInterpreter ,
767+ @nospecialize (f), arginfo:: ArgInfo , edge:: MethodInstance , sv:: InferenceState )
749768 args = collect_const_args (arginfo)
750769 world = get_world_counter (interp)
751770 value = try
752771 Core. _call_in_world_total (world, f, args... )
753772 catch
754773 # The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime
755- return ConstCallResults (Union{}, ConcreteResult (result . edge, result . edge_effects ), result . edge_effects )
774+ return ConstCallResults (Union{}, ConcreteResult (edge, EFFECTS_THROWS ), EFFECTS_THROWS )
756775 end
757776 if is_inlineable_constant (value) || call_result_unused (sv)
758777 # If the constant is not inlineable, still do the const-prop, since the
759778 # code that led to the creation of the Const may be inlineable in the same
760779 # circumstance and may be optimizable.
761- return ConstCallResults (Const (value), ConcreteResult (result . edge, EFFECTS_TOTAL, value), EFFECTS_TOTAL)
780+ return ConstCallResults (Const (value), ConcreteResult (edge, EFFECTS_TOTAL, value), EFFECTS_TOTAL)
762781 end
763782 return nothing
764783end
765784
785+ function early_concrete_eval_eligible (interp:: AbstractInterpreter ,
786+ @nospecialize (f), match:: MethodMatch , arginfo:: ArgInfo , sv:: InferenceState )
787+ # the effects for this match may not be derived yet, so disable concrete-evaluation
788+ # immediately when the interpreter can use overlayed methods
789+ isoverlayed (method_table (interp)) && return false
790+ f != = nothing || return false
791+ is_concrete_eval_eligible (decode_effects_override (match. method. purity)) || return false
792+ return is_all_const_arg (arginfo)
793+ end
794+
795+ function early_concrete_eval (interp:: AbstractInterpreter ,
796+ @nospecialize (f), match:: MethodMatch , arginfo:: ArgInfo , sv:: InferenceState )
797+ early_concrete_eval_eligible (interp, f, match, arginfo, sv) || return nothing
798+ edge = specialize_method (match. method, match. spec_types, match. sparams)
799+ edge === nothing && return nothing
800+ return _concrete_eval_call (interp, f, arginfo, edge, sv)
801+ end
802+
803+ function abstract_call_method_with_const_args_early (interp:: AbstractInterpreter ,
804+ @nospecialize (f), match:: MethodMatch , arginfo:: ArgInfo , sv:: InferenceState )
805+ const_prop_enabled (interp, sv, match) || return nothing
806+ val = early_concrete_eval (interp, f, match, arginfo, sv)
807+ if val != = nothing
808+ add_backedge! (val. const_result. mi, sv)
809+ return val
810+ end
811+ # TODO early constant prop' for `@nospecialize`d methods?
812+ return nothing
813+ end
814+
766815function const_prop_enabled (interp:: AbstractInterpreter , sv:: InferenceState , match:: MethodMatch )
767816 if ! InferenceParams (interp). ipo_constant_propagation
768- add_remark! (interp, sv, " [constprop] Disabled by parameter" )
817+ add_remark! (interp, sv, " [constprop] Disabled by inference parameter" )
769818 return false
770819 end
771820 method = match. method
@@ -789,12 +838,10 @@ end
789838function abstract_call_method_with_const_args (interp:: AbstractInterpreter , result:: MethodCallResult ,
790839 @nospecialize (f), arginfo:: ArgInfo , match:: MethodMatch ,
791840 sv:: InferenceState )
792- if ! const_prop_enabled (interp, sv, match)
793- return nothing
794- end
841+ const_prop_enabled (interp, sv, match) || return nothing
795842 val = concrete_eval_call (interp, f, result, arginfo, sv)
796843 if val != = nothing
797- add_backedge! (result . edge , sv)
844+ add_backedge! (val . const_result . mi , sv)
798845 return val
799846 end
800847 mi = maybe_get_const_prop_profitable (interp, result, f, arginfo, match, sv)
0 commit comments