Skip to content

Commit dbe19e4

Browse files
authored
staticdata: Insert backedges recursively (#57212)
In the new edges scheme, when we perform constant propagation, we create a dummy CodeInstance that is not inserted into the specializations cache, but instead simply serves as a container for all edges that were encountered during the constant propagation. These CodeInstances are not part of either the internal or external edges list collected during pkgimage generation. As such, while we were verifying edges recursively and would thus catch an invalidation prior to package image reload, we were failing to also insert backedges for these CodeInstance. We were thus failing to invalidate such methods if the method was redefined after re-load of the package image. Fix that by moving the storing of the backedges to the end of the validation code, so that it too happens recursively.
1 parent 3952c2c commit dbe19e4

File tree

2 files changed

+50
-22
lines changed

2 files changed

+50
-22
lines changed

base/staticdata.jl

+17-22
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,16 @@ function _insert_backedges(edges::Vector{Any}, stack::Vector{CodeInstance}, visi
3939
verify_method_graph(codeinst, stack, visiting)
4040
minvalid = codeinst.min_world
4141
maxvalid = codeinst.max_world
42-
if maxvalid minvalid
43-
if get_world_counter() == maxvalid
44-
# if this callee is still valid, add all the backedges
45-
Base.Compiler.store_backedges(codeinst, codeinst.edges)
46-
end
47-
if get_world_counter() == maxvalid
48-
maxvalid = typemax(UInt)
49-
@atomic :monotonic codeinst.max_world = maxvalid
50-
end
51-
if external
52-
caller = get_ci_mi(codeinst)
53-
@assert isdefined(codeinst, :inferred) # See #53586, #53109
54-
inferred = @ccall jl_rettype_inferred(
55-
codeinst.owner::Any, caller::Any, minvalid::UInt, maxvalid::UInt)::Any
56-
if inferred !== nothing
57-
# We already got a code instance for this world age range from
58-
# somewhere else - we don't need this one.
59-
else
60-
@ccall jl_mi_cache_insert(caller::Any, codeinst::Any)::Cvoid
61-
end
42+
if maxvalid minvalid && external
43+
caller = get_ci_mi(codeinst)
44+
@assert isdefined(codeinst, :inferred) # See #53586, #53109
45+
inferred = @ccall jl_rettype_inferred(
46+
codeinst.owner::Any, caller::Any, minvalid::UInt, maxvalid::UInt)::Any
47+
if inferred !== nothing
48+
# We already got a code instance for this world age range from
49+
# somewhere else - we don't need this one.
50+
else
51+
@ccall jl_mi_cache_insert(caller::Any, codeinst::Any)::Cvoid
6252
end
6353
end
6454
end
@@ -196,9 +186,14 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
196186
while length(stack) depth
197187
child = pop!(stack)
198188
if maxworld 0
199-
@atomic :monotonic child.min_world = minworld
189+
@atomic :monotonic child.min_world = minworld
190+
end
191+
if maxworld == current_world
192+
Base.Compiler.store_backedges(child, child.edges)
193+
@atomic :monotonic child.max_world = typemax(UInt)
194+
else
195+
@atomic :monotonic child.max_world = maxworld
200196
end
201-
@atomic :monotonic child.max_world = maxworld
202197
@assert visiting[child] == length(stack) + 1
203198
delete!(visiting, child)
204199
invalidations = _jl_debug_method_invalidation[]

test/precompile.jl

+33
Original file line numberDiff line numberDiff line change
@@ -2222,4 +2222,37 @@ precompile_test_harness("No package module") do load_path
22222222
String(take!(io)))
22232223
end
22242224

2225+
precompile_test_harness("Constprop CodeInstance invalidation") do load_path
2226+
write(joinpath(load_path, "DefineTheMethod.jl"),
2227+
"""
2228+
module DefineTheMethod
2229+
export the_method
2230+
the_method_val(::Val{x}) where {x} = x
2231+
the_method_val(::Val{1}) = 0xdeadbeef
2232+
the_method_val(::Val{2}) = 2
2233+
the_method_val(::Val{3}) = 3
2234+
the_method_val(::Val{4}) = 4
2235+
the_method_val(::Val{5}) = 5
2236+
Base.@constprop :aggressive the_method(x) = the_method_val(Val{x}())
2237+
the_method(2)
2238+
end
2239+
""")
2240+
Base.compilecache(Base.PkgId("DefineTheMethod"))
2241+
write(joinpath(load_path, "CallTheMethod.jl"),
2242+
"""
2243+
module CallTheMethod
2244+
using DefineTheMethod
2245+
call_the_method() = the_method(1)
2246+
call_the_method()
2247+
end
2248+
""")
2249+
Base.compilecache(Base.PkgId("CallTheMethod"))
2250+
@eval using DefineTheMethod
2251+
@eval using CallTheMethod
2252+
@eval DefineTheMethod.the_method_val(::Val{1}) = Int(0)
2253+
invokelatest() do
2254+
@test Int(0) == CallTheMethod.call_the_method()
2255+
end
2256+
end
2257+
22252258
finish_precompile_test!()

0 commit comments

Comments
 (0)