Skip to content

Commit acb48a4

Browse files
committed
precompute all possible ml-matches lookup results
Store the full interference graph rather than re-computing it every time. This representation may exclude some edges that are implied by transitivity since sort_mlmatches will ensure the correct result.
1 parent c3282ce commit acb48a4

File tree

8 files changed

+256
-160
lines changed

8 files changed

+256
-160
lines changed

base/Base.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,9 @@ include("uuid.jl")
267267
include("pkgid.jl")
268268
include("toml_parser.jl")
269269
include("linking.jl")
270+
module StaticData
270271
include("staticdata.jl")
272+
end
271273
include("loading.jl")
272274

273275
# BinaryPlatforms, used by Artifacts. Needs `Sort`.

base/staticdata.jl

Lines changed: 110 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3-
module StaticData
4-
53
using .Core: CodeInstance, MethodInstance
6-
using .Base: JLOptions, Compiler, get_world_counter, _methods_by_ftype, get_methodtable, get_ci_mi
4+
using .Base: JLOptions, Compiler, get_world_counter, _methods_by_ftype, get_methodtable, get_ci_mi, morespecific
75

86
const WORLD_AGE_REVALIDATION_SENTINEL::UInt = 1
97
const _jl_debug_method_invalidation = Ref{Union{Nothing,Vector{Any}}}(nothing)
@@ -282,22 +280,47 @@ function verify_method(codeinst::CodeInstance, stack::Vector{CodeInstance}, visi
282280
return 0, minworld, maxworld
283281
end
284282

283+
function get_method_from_edge(@nospecialize t)
284+
if t isa Method
285+
return t
286+
else
287+
if t isa CodeInstance
288+
t = get_ci_mi(t)::MethodInstance
289+
else
290+
t = t::MethodInstance
291+
end
292+
return t.def::Method
293+
end
294+
end
295+
285296
function verify_call(@nospecialize(sig), expecteds::Core.SimpleVector, i::Int, n::Int, world::UInt, fully_covers::Bool)
286297
# verify that these edges intersect with the same methods as before
287298
mi = nothing
288-
if n == 1
299+
expected_deleted = false
300+
for j = 1:n
301+
t = expecteds[i+j-1]
302+
meth = get_method_from_edge(t)
303+
if iszero(meth.dispatch_status & METHOD_SIG_LATEST_WHICH)
304+
expected_deleted = true
305+
break
306+
end
307+
end
308+
if expected_deleted
309+
if _jl_debug_method_invalidation[] === nothing && world == get_world_counter()
310+
result = Any[] # result is unused
311+
return UInt(1), UInt(0), result
312+
end
313+
elseif n == 1
289314
# first, fast-path a check if the expected method simply dominates its sig anyways
290315
# so the result of ml_matches is already simply known
291316
let t = expecteds[i], meth, minworld, maxworld, result
292-
if t isa Method
293-
meth = t
294-
else
317+
meth = get_method_from_edge(t)
318+
if !(t isa Method)
295319
if t isa CodeInstance
296320
mi = get_ci_mi(t)::MethodInstance
297321
else
298322
mi = t::MethodInstance
299323
end
300-
meth = mi.def::Method
301324
# Fast path is legal when fully_covers=true OR when METHOD_SIG_LATEST_HAS_NOTMORESPECIFIC is unset
302325
if (fully_covers || iszero(meth.dispatch_status & METHOD_SIG_LATEST_HAS_NOTMORESPECIFIC)) &&
303326
!iszero(mi.dispatch_status & METHOD_SIG_LATEST_ONLY)
@@ -319,7 +342,83 @@ function verify_call(@nospecialize(sig), expecteds::Core.SimpleVector, i::Int, n
319342
end
320343
end
321344
end
322-
end
345+
elseif n > 1
346+
# Try the interference set fast path: check if all interference sets are covered by expecteds
347+
interference_fast_path_success = true
348+
if !fully_covers
349+
# If expected doesn't fully cover, then need to make sure that no new methods are possibly present
350+
for j = 1:n
351+
meth = get_method_from_edge(expecteds[i+j-1])
352+
if !iszero(meth.dispatch_status & METHOD_SIG_LATEST_HAS_NOTMORESPECIFIC)
353+
interference_fast_path_success = false
354+
for j = reverse(1:n)
355+
meth2 = get_method_from_edge(expecteds[i+j-1])
356+
if meth !== meth2 && typeintersect(sig, meth.sig) <: meth2.sig && !(meth2.sig <: meth.sig)
357+
interference_fast_path_success = true
358+
end
359+
end
360+
if !interference_fast_path_success
361+
break
362+
end
363+
end
364+
end
365+
end
366+
if interference_fast_path_success
367+
local interference_minworld::UInt = 1
368+
for j = 1:n
369+
meth = get_method_from_edge(expecteds[i+j-1])
370+
if interference_minworld < meth.primary_world
371+
interference_minworld = meth.primary_world
372+
end
373+
interferences = meth.interferences
374+
for k = 1:length(interferences)
375+
isassigned(interferences, k) || break # no more entries
376+
interference_method = interferences[k]::Method
377+
if iszero(interference_method.dispatch_status & METHOD_SIG_LATEST_WHICH)
378+
# detected a deleted interference_method, so need the full lookup to compute minworld
379+
interference_fast_path_success = false
380+
break
381+
end
382+
world < interference_method.primary_world && break # this and later entries are for a future world
383+
local found_in_expecteds = false
384+
for j = 1:n
385+
if interference_method === get_method_from_edge(expecteds[i+j-1])
386+
found_in_expecteds = true
387+
break
388+
end
389+
end
390+
if !found_in_expecteds
391+
ti = typeintersect(sig, interference_method.sig)
392+
if !(ti === Union{})
393+
# try looking for a different expected method that fully hides the interference_method anyways
394+
for j = 1:n
395+
meth2 = get_method_from_edge(expecteds[i+j-1])
396+
if meth2 !== interference_method && ti <: meth2.sig && morespecific(meth2, interference_method)
397+
found_in_expecteds = true
398+
break
399+
end
400+
end
401+
if !found_in_expecteds
402+
meth2 = get_method_from_edge(expecteds[i])
403+
interference_fast_path_success = false
404+
break
405+
end
406+
end
407+
end
408+
end
409+
if !interference_fast_path_success
410+
break
411+
end
412+
end
413+
if interference_fast_path_success
414+
# All interference sets are covered by expecteds, can return success
415+
@assert interference_minworld world
416+
maxworld = typemax(UInt)
417+
result = Any[] # result is unused
418+
return interference_minworld, maxworld, result
419+
end
420+
end
421+
end
323422
# next, compare the current result of ml_matches to the old result
324423
lim = _jl_debug_method_invalidation[] !== nothing ? Int(typemax(Int32)) : n
325424
minworld = Ref{UInt}(1)
@@ -339,17 +438,7 @@ function verify_call(@nospecialize(sig), expecteds::Core.SimpleVector, i::Int, n
339438
local found = false
340439
for j = 1:n
341440
t = expecteds[i+j-1]
342-
if t isa Method
343-
meth = t
344-
else
345-
if t isa CodeInstance
346-
t = get_ci_mi(t)::MethodInstance
347-
else
348-
t = t::MethodInstance
349-
end
350-
meth = t.def::Method
351-
end
352-
if match.method == meth
441+
if match.method == get_method_from_edge(t)
353442
found = true
354443
break
355444
end
@@ -412,6 +501,4 @@ function verify_invokesig(@nospecialize(invokesig), expected::Method, world::UIn
412501
end
413502
end
414503
return minworld, maxworld, matched
415-
end
416-
417-
end # module StaticData
504+
end

0 commit comments

Comments
 (0)