22
33module StaticData
44
5- using Core: CodeInstance, MethodInstance
6- using Base: get_world_counter
5+ using . Core: CodeInstance, MethodInstance
6+ using . Base: JLOptions, Compiler, get_world_counter, _methods_by_ftype, get_methodtable
77
88const WORLD_AGE_REVALIDATION_SENTINEL:: UInt = 1
99const _jl_debug_method_invalidation = Ref {Union{Nothing,Vector{Any}}} (nothing )
7373
7474get_require_world () = unsafe_load (cglobal (:jl_require_world , UInt))
7575
76+ function gen_staged_sig (def:: Method , mi:: MethodInstance )
77+ isdefined (def, :generator ) || return nothing
78+ isdispatchtuple (mi. specTypes) || return nothing
79+ gen = Core. Typeof (def. generator)
80+ return Tuple{gen, UInt, Method, Vararg}
81+ # # more precise method lookup, but more costly and likely not actually better?
82+ # tts = (mi.specTypes::DataType).parameters
83+ # sps = Any[Core.Typeof(mi.sparam_vals[i]) for i in 1:length(mi.sparam_vals)]
84+ # if def.isva
85+ # return Tuple{gen, UInt, Method, sps..., tts[1:def.nargs - 1]..., Tuple{tts[def.nargs - 1:end]...}}
86+ # else
87+ # return Tuple{gen, UInt, Method, sps..., tts...}
88+ # end
89+ end
90+
91+ function needs_instrumentation (codeinst:: CodeInstance , mi:: MethodInstance , def:: Method , validation_world:: UInt )
92+ if JLOptions (). code_coverage != 0 || JLOptions (). malloc_log != 0
93+ # test if the code needs to run with instrumentation, in which case we cannot use existing generated code
94+ if isdefined (def, :debuginfo ) ? # generated_only functions do not have debuginfo, so fall back to considering their codeinst debuginfo though this may be slower (and less accurate?)
95+ Compiler. should_instrument (def. module, def. debuginfo) :
96+ Compiler. should_instrument (def. module, codeinst. debuginfo)
97+ return true
98+ end
99+ gensig = gen_staged_sig (def, mi)
100+ if gensig != = nothing
101+ # if this is defined by a generator, try to consider forcing re-running the generators too, to add coverage for them
102+ minworld = Ref {UInt} (1 )
103+ maxworld = Ref {UInt} (typemax (UInt))
104+ has_ambig = Ref {Int32} (0 )
105+ result = _methods_by_ftype (gensig, nothing , - 1 , validation_world, #= ambig=# false , minworld, maxworld, has_ambig)
106+ if result != = nothing
107+ for k = 1 : length (result)
108+ match = result[k]:: Core.MethodMatch
109+ genmethod = match. method
110+ # no, I refuse to refuse to recurse into your cursed generated function generators and will only test one level deep here
111+ if isdefined (genmethod, :debuginfo ) && Compiler. should_instrument (genmethod. module, genmethod. debuginfo)
112+ return true
113+ end
114+ end
115+ end
116+ end
117+ end
118+ return false
119+ end
120+
76121# Test all edges relevant to a method:
77122# - Visit the entire call graph, starting from edges[idx] to determine if that method is valid
78123# - Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable
@@ -84,6 +129,12 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
84129 return 0 , world, max_valid2
85130 end
86131 end
132+ mi = get_ci_mi (codeinst)
133+ def = mi. def:: Method
134+ if needs_instrumentation (codeinst, mi, def, validation_world)
135+ return 0 , world, UInt (0 )
136+ end
137+
87138 # Implicitly referenced bindings in the current module do not get explicit edges.
88139 # If they were invalidated, they'll be in `mwis`. If they weren't, they imply a minworld
89140 # of `get_require_world`. In principle, this is only required for methods that do reference
@@ -92,8 +143,6 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
92143 # but no implicit edges) is rare and there would be little benefit to lower the minworld for it
93144 # in any case, so we just always use `get_require_world` here.
94145 local minworld:: UInt , maxworld:: UInt = get_require_world (), validation_world
95- def = get_ci_mi (codeinst). def
96- @assert def isa Method
97146 if haskey (visiting, codeinst)
98147 return visiting[codeinst], minworld, maxworld
99148 end
@@ -225,7 +274,7 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
225274 end
226275 @atomic :monotonic child. max_world = maxworld
227276 if maxworld == validation_world && validation_world == get_world_counter ()
228- Base . Compiler. store_backedges (child, child. edges)
277+ Compiler. store_backedges (child, child. edges)
229278 end
230279 @assert visiting[child] == length (stack) + 1
231280 delete! (visiting, child)
@@ -243,7 +292,7 @@ function verify_call(@nospecialize(sig), expecteds::Core.SimpleVector, i::Int, n
243292 minworld = Ref {UInt} (1 )
244293 maxworld = Ref {UInt} (typemax (UInt))
245294 has_ambig = Ref {Int32} (0 )
246- result = Base . _methods_by_ftype (sig, nothing , lim, world, #= ambig=# false , minworld, maxworld, has_ambig)
295+ result = _methods_by_ftype (sig, nothing , lim, world, #= ambig=# false , minworld, maxworld, has_ambig)
247296 if result === nothing
248297 maxworld[] = 0
249298 else
@@ -306,11 +355,11 @@ function verify_invokesig(@nospecialize(invokesig), expected::Method, world::UIn
306355 else
307356 minworld = 1
308357 maxworld = typemax (UInt)
309- mt = Base . get_methodtable (expected)
358+ mt = get_methodtable (expected)
310359 if mt === nothing
311360 maxworld = 0
312361 else
313- matched, valid_worlds = Base . Compiler. _findsup (invokesig, mt, world)
362+ matched, valid_worlds = Compiler. _findsup (invokesig, mt, world)
314363 minworld, maxworld = valid_worlds. min_world, valid_worlds. max_world
315364 if matched === nothing
316365 maxworld = 0
0 commit comments