Skip to content

Commit 4b8c9dc

Browse files
committed
inference: fix backedge computation for const-prop'ed callsite
With this commit `abstract_call_method_with_const_args` doesn't add backedge but rather returns the backedge to the caller, letting the callers like `abstract_call_gf_by_type` and `abstract_invoke` take the responsibility to add backedge to current context appropriately. As a result, this fixes the backedge calculation for const-prop'ed `invoke` callsite. For example, for the following call graph, ```julia foo(a::Int) = a > 0 ? :int : println(a) foo(a::Integer) = a > 0 ? "integer" : println(a) bar(a::Int) = @invoke foo(a::Integer) ``` Previously we added the wrong backedge `nothing, bar(Int64) from bar(Int64)`: ```julia julia> last(only(code_typed(()->bar(42)))) String julia> let m = only(methods(foo, (UInt,))) @eval Core.Compiler for (sig, caller) in BackedgeIterator($m.specializations[1].backedges) println(sig, ", ", caller) end end Tuple{typeof(Main.foo), Integer}, bar(Int64) from bar(Int64) nothing, bar(Int64) from bar(Int64) ``` but now we only add `invoke`-backedge: ```julia julia> last(only(code_typed(()->bar(42)))) String julia> let m = only(methods(foo, (UInt,))) @eval Core.Compiler for (sig, caller) in BackedgeIterator($m.specializations[1].backedges) println(sig, ", ", caller) end end Tuple{typeof(Main.foo), Integer}, bar(Int64) from bar(Int64) ```
1 parent 386e2c7 commit 4b8c9dc

File tree

1 file changed

+19
-24
lines changed

1 file changed

+19
-24
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
126126
for sig_n in splitsigs
127127
result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, sv)
128128
(; rt, edge, effects) = result
129-
edge === nothing || push!(edges, edge)
130129
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
131130
this_arginfo = ArgInfo(fargs, this_argtypes)
132131
const_call_result = abstract_call_method_with_const_args(interp, result,
@@ -135,12 +134,13 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
135134
if const_call_result !== nothing
136135
if const_call_result.rt ᵢ rt
137136
rt = const_call_result.rt
138-
(; effects, const_result) = const_call_result
137+
(; effects, const_result, edge) = const_call_result
139138
end
140139
end
141140
all_effects = merge_effects(all_effects, effects)
142141
push!(const_results, const_result)
143142
any_const_result |= const_result !== nothing
143+
edge === nothing || push!(edges, edge)
144144
this_rt = tmerge(this_rt, rt)
145145
if bail_out_call(interp, this_rt, sv)
146146
break
@@ -153,7 +153,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
153153
(; rt, edge, effects) = result
154154
this_conditional = ignorelimited(rt)
155155
this_rt = widenwrappedconditional(rt)
156-
edge === nothing || push!(edges, edge)
157156
# try constant propagation with argtypes for this match
158157
# this is in preparation for inlining, or improving the return result
159158
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
@@ -169,12 +168,13 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
169168
if this_const_rt ᵢ this_rt
170169
this_conditional = this_const_conditional
171170
this_rt = this_const_rt
172-
(; effects, const_result) = const_call_result
171+
(; effects, const_result, edge) = const_call_result
173172
end
174173
end
175174
all_effects = merge_effects(all_effects, effects)
176175
push!(const_results, const_result)
177176
any_const_result |= const_result !== nothing
177+
edge === nothing || push!(edges, edge)
178178
end
179179
@assert !(this_conditional isa Conditional) "invalid lattice element returned from inter-procedural context"
180180
seen += 1
@@ -831,17 +831,18 @@ function concrete_eval_call(interp::AbstractInterpreter,
831831
if eligible
832832
args = collect_const_args(arginfo, #=start=#2)
833833
world = get_world_counter(interp)
834+
edge = result.edge::MethodInstance
834835
value = try
835836
Core._call_in_world_total(world, f, args...)
836837
catch
837838
# The evaluation threw. By :consistent-cy, we're guaranteed this would have happened at runtime
838-
return ConstCallResults(Union{}, ConcreteResult(result.edge::MethodInstance, result.effects), result.effects)
839+
return ConstCallResults(Union{}, ConcreteResult(edge, result.effects), result.effects, edge)
839840
end
840841
if is_inlineable_constant(value) || call_result_unused(sv)
841842
# If the constant is not inlineable, still do the const-prop, since the
842843
# code that led to the creation of the Const may be inlineable in the same
843844
# circumstance and may be optimizable.
844-
return ConstCallResults(Const(value), ConcreteResult(result.edge::MethodInstance, EFFECTS_TOTAL, value), EFFECTS_TOTAL)
845+
return ConstCallResults(Const(value), ConcreteResult(edge, EFFECTS_TOTAL, value), EFFECTS_TOTAL, edge)
845846
end
846847
return false
847848
else # eligible for semi-concrete evaluation
@@ -868,27 +869,22 @@ struct ConstCallResults
868869
rt::Any
869870
const_result::ConstResult
870871
effects::Effects
872+
edge::MethodInstance
871873
ConstCallResults(@nospecialize(rt),
872874
const_result::ConstResult,
873-
effects::Effects) =
874-
new(rt, const_result, effects)
875+
effects::Effects,
876+
edge::MethodInstance) =
877+
new(rt, const_result, effects, edges)
875878
end
876879

877880
function abstract_call_method_with_const_args(interp::AbstractInterpreter, result::MethodCallResult,
878881
@nospecialize(f), arginfo::ArgInfo, match::MethodMatch,
879-
sv::InferenceState, @nospecialize(invoketypes=nothing))
882+
sv::InferenceState)
880883
if !const_prop_enabled(interp, sv, match)
881884
return nothing
882885
end
883886
res = concrete_eval_call(interp, f, result, arginfo, sv)
884-
if isa(res, ConstCallResults)
885-
if invoketypes === nothing
886-
add_backedge!(sv, res.const_result.mi)
887-
else
888-
add_invoke_backedge!(sv, invoketypes, res.const_result.mi)
889-
end
890-
return res
891-
end
887+
isa(res, ConstCallResults) && return res
892888
mi = maybe_get_const_prop_profitable(interp, result, f, arginfo, match, sv)
893889
mi === nothing && return nothing
894890
# try semi-concrete evaluation
@@ -900,7 +896,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, resul
900896
if isa(ir, IRCode)
901897
T = ir_abstract_constant_propagation(interp, mi_cache, sv, mi, ir, arginfo.argtypes)
902898
if !isa(T, Type) || typeintersect(T, Bool) === Union{}
903-
return ConstCallResults(T, SemiConcreteResult(mi, ir, result.effects), result.effects)
899+
return ConstCallResults(T, SemiConcreteResult(mi, ir, result.effects), result.effects, mi)
904900
end
905901
end
906902
end
@@ -933,8 +929,7 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, resul
933929
result = inf_result.result
934930
# if constant inference hits a cycle, just bail out
935931
isa(result, InferenceState) && return nothing
936-
add_backedge!(sv, mi)
937-
return ConstCallResults(result, ConstPropResult(inf_result), inf_result.ipo_effects)
932+
return ConstCallResults(result, ConstPropResult(inf_result), inf_result.ipo_effects, mi)
938933
end
939934

940935
# if there's a possibility we could get a better result with these constant arguments
@@ -1689,7 +1684,6 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn
16891684
ti = tienv[1]; env = tienv[2]::SimpleVector
16901685
result = abstract_call_method(interp, method, ti, env, false, sv)
16911686
(; rt, edge, effects) = result
1692-
edge !== nothing && add_invoke_backedge!(sv, types, edge::MethodInstance)
16931687
match = MethodMatch(ti, env, method, argtype <: method.sig)
16941688
res = nothing
16951689
sig = match.spec_types
@@ -1706,10 +1700,11 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn
17061700
const_result = nothing
17071701
if const_call_result !== nothing
17081702
if (typeinf_lattice(interp), const_call_result.rt, rt)
1709-
(; rt, effects, const_result) = const_call_result
1703+
(; rt, effects, const_result, edge) = const_call_result
17101704
end
17111705
end
17121706
effects = Effects(effects; nonoverlayed=!overlayed)
1707+
edge !== nothing && add_invoke_backedge!(sv, types, edge)
17131708
return CallMeta(from_interprocedural!(ipo_lattice(interp), rt, sv, arginfo, sig), effects, InvokeCallInfo(match, const_result))
17141709
end
17151710

@@ -1843,7 +1838,6 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter,
18431838
sig = argtypes_to_type(arginfo.argtypes)
18441839
result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv)
18451840
(; rt, edge, effects) = result
1846-
edge !== nothing && add_backedge!(sv, edge)
18471841
tt = closure.typ
18481842
sigT = (unwrap_unionall(tt)::DataType).parameters[1]
18491843
match = MethodMatch(sig, Core.svec(), closure.source, sig <: rewrap_unionall(sigT, tt))
@@ -1853,7 +1847,7 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter,
18531847
nothing, arginfo, match, sv)
18541848
if const_call_result !== nothing
18551849
if const_call_result.rt rt
1856-
(; rt, effects, const_result) = const_call_result
1850+
(; rt, effects, const_result, edge) = const_call_result
18571851
end
18581852
end
18591853
end
@@ -1869,6 +1863,7 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter,
18691863
end
18701864
end
18711865
rt = from_interprocedural!(ipo, rt, sv, arginfo, match.spec_types)
1866+
edge !== nothing && add_backedge!(sv, edge)
18721867
return CallMeta(rt, effects, info)
18731868
end
18741869

0 commit comments

Comments
 (0)