2
2
3
3
module StaticData
4
4
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
7
7
8
8
const WORLD_AGE_REVALIDATION_SENTINEL:: UInt = 1
9
9
const _jl_debug_method_invalidation = Ref {Union{Nothing,Vector{Any}}} (nothing )
73
73
74
74
get_require_world () = unsafe_load (cglobal (:jl_require_world , UInt))
75
75
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
+
76
121
# Test all edges relevant to a method:
77
122
# - Visit the entire call graph, starting from edges[idx] to determine if that method is valid
78
123
# - 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
84
129
return 0 , world, max_valid2
85
130
end
86
131
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
+
87
138
# Implicitly referenced bindings in the current module do not get explicit edges.
88
139
# If they were invalidated, they'll be in `mwis`. If they weren't, they imply a minworld
89
140
# 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
92
143
# but no implicit edges) is rare and there would be little benefit to lower the minworld for it
93
144
# in any case, so we just always use `get_require_world` here.
94
145
local minworld:: UInt , maxworld:: UInt = get_require_world (), validation_world
95
- def = get_ci_mi (codeinst). def
96
- @assert def isa Method
97
146
if haskey (visiting, codeinst)
98
147
return visiting[codeinst], minworld, maxworld
99
148
end
@@ -225,7 +274,7 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
225
274
end
226
275
@atomic :monotonic child. max_world = maxworld
227
276
if maxworld == validation_world && validation_world == get_world_counter ()
228
- Base . Compiler. store_backedges (child, child. edges)
277
+ Compiler. store_backedges (child, child. edges)
229
278
end
230
279
@assert visiting[child] == length (stack) + 1
231
280
delete! (visiting, child)
@@ -243,7 +292,7 @@ function verify_call(@nospecialize(sig), expecteds::Core.SimpleVector, i::Int, n
243
292
minworld = Ref {UInt} (1 )
244
293
maxworld = Ref {UInt} (typemax (UInt))
245
294
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)
247
296
if result === nothing
248
297
maxworld[] = 0
249
298
else
@@ -306,11 +355,11 @@ function verify_invokesig(@nospecialize(invokesig), expected::Method, world::UIn
306
355
else
307
356
minworld = 1
308
357
maxworld = typemax (UInt)
309
- mt = Base . get_methodtable (expected)
358
+ mt = get_methodtable (expected)
310
359
if mt === nothing
311
360
maxworld = 0
312
361
else
313
- matched, valid_worlds = Base . Compiler. _findsup (invokesig, mt, world)
362
+ matched, valid_worlds = Compiler. _findsup (invokesig, mt, world)
314
363
minworld, maxworld = valid_worlds. min_world, valid_worlds. max_world
315
364
if matched === nothing
316
365
maxworld = 0
0 commit comments