@@ -183,7 +183,7 @@ const BackedgeMT = Pair{Union{DataType,Binding},InstanceNode} # sig=>root
183183abstract type AbstractMethodInvalidations end
184184
185185struct MethodInvalidations <: AbstractMethodInvalidations
186- method:: Union{Method, Binding}
186+ method:: Union{Method, Binding, Nothing }
187187 reason:: Symbol # :inserting, :deleting, or :rebinding
188188 mt_backedges:: Vector{BackedgeMT}
189189 backedges:: Vector{InstanceNode}
@@ -415,10 +415,11 @@ const EdgeNodeType = Union{DataType, Binding, MethodInstance, CodeInstance}
415415
416416struct MultiMethodInvalidations <: AbstractMethodInvalidations
417417 methods:: Union{Binding,Vector{Method}}
418+ reason:: Symbol # :inserting, :deleting, or :rebinding
418419 mt_backedges:: Vector{BackedgeMT}
419420 backedges:: Vector{InstanceNode}
420421end
421- MultiMethodInvalidations(methods = Method[] ) = MultiMethodInvalidations(methods, BackedgeMT[], InstanceNode[])
422+ MultiMethodInvalidations(methods, reason ) = MultiMethodInvalidations(methods, reason , BackedgeMT[], InstanceNode[])
422423
423424function Base. show(io:: IO , methinvs:: MultiMethodInvalidations )
424425 iscompact = get(io, :compact, false ):: Bool
@@ -451,139 +452,59 @@ end
451452Base. isempty(methinvs:: MultiMethodInvalidations ) = isempty(methinvs. backedges) && isempty(methinvs. mt_backedges)
452453
453454function invalidation_trees_logedges(list; exclude_corecompiler:: Bool = true )
454- # transiently we represent the graph as a flat list of nodes, a flat list of children indexes, and a Dict to look up the node index
455- nodes = EdgeNodeType[]
456- calleridxss = Vector{Int}[]
457- nodeidx = IdDict{EdgeNodeType,Int}() # get the index within `nodes` for a given key
458- matchess = Dict{Int,Vector{Method}}() # nodeidx => Method[...]
459-
460- function addnode(item)
461- push!(nodes, item)
462- k = length(nodes)
463- nodeidx[item] = k
464- return k
465- end
466-
467- function addcaller!(listlist, (calleridx, calleeidx))
468- if length(listlist) < calleeidx
469- resize!(listlist, calleeidx)
470- end
471- # calleridxs = get!(Vector{Int}, listlist, calleeidx) # why don't we have this??
472- calleridxs = if isassigned(listlist, calleeidx)
473- listlist[calleeidx]
474- else
475- listlist[calleeidx] = Int[]
476- end
477- push!(calleridxs, calleridx)
478- return calleridxs
479- end
455+ mminvs = MultiMethodInvalidations[]
456+ mmibad = MultiMethodInvalidations(Method[], :unknown)
457+ calleedict = Dict{Union{CodeInstance,MethodInstance},InstanceNode}()
480458
481- i = 0
459+ i, mmi = 0 , nothing
482460 while i + 2 < length(list)
483461 tag = list[i+ 2 ]:: String
484462 if tag == " method_globalref"
485463 def, target = list[i+ 1 ]:: Method , list[i+ 3 ]:: CodeInstance
486464 i += 4
487465 error(" implement me" )
488466 elseif tag == " insert_backedges_callee"
489- edge, target, matches = list[i+ 1 ]:: EdgeNodeType , list[i+ 3 ]:: CodeInstance , list[i+ 4 ]:: Union{ Vector{Any},Nothing }
467+ edge, target, matches = list[i+ 1 ]:: EdgeNodeType , list[i+ 3 ]:: CodeInstance , list[i+ 4 ]:: Vector{Any}
490468 i += 4
491- idx = get(nodeidx, edge, nothing )
492- if idx === nothing
493- idx = addnode(edge)
494- if matches != = nothing
495- matchess[idx] = matches
469+ reason = ! isempty(matches) ? :inserting :
470+ isa(edge, Binding) ? :rebinding : :deleting
471+ matches = reason === :rebinding ? edge : convert(Vector{Method}, matches)
472+ mmi = MultiMethodInvalidations(matches, reason)
473+ push!(mminvs, mmi)
474+ if edge isa Type || edge isa Binding
475+ node = InstanceNode(target, 0 )
476+ calleedict[target] = node
477+ push!(mmi. mt_backedges, edge=> node)
478+ else
479+ root = get(calleedict, edge, nothing )
480+ if root === nothing
481+ root = InstanceNode(edge, 0 )
482+ push!(mmi. backedges, root)
483+ calleedict[edge] = root
496484 end
497- elseif matches != = nothing
498- @assert Set(matches) == Set(matchess[idx])
499- end
500- idxt = get(nodeidx, target, nothing )
501- if idxt === nothing
502- idxt = addnode(target)
485+ node = InstanceNode(target, root)
486+ calleedict[target] = node
503487 end
504- addcaller!(calleridxss, idxt => idx)
505488 elseif tag == " verify_methods"
506489 caller, callee = list[i+ 1 ]:: CodeInstance , list[i+ 3 ]:: CodeInstance
507490 i += 3
508- idx = get(nodeidx, callee, nothing )
509- if idx === nothing
510- idx = addnode(callee)
491+ caller == callee && continue
492+ node = get(calleedict, callee, nothing )
493+ if node === nothing
494+ node = InstanceNode(callee, 0 )
495+ push!(mmibad. backedges, node)
496+ calleedict[callee] = node
511497 end
512- idxt = get(nodeidx, caller, nothing )
513- if idxt === nothing
514- idxt = addnode(caller)
515- end
516- @assert idxt >= idx
517- idxt > idx && addcaller!(calleridxss, idxt => idx)
498+ node = InstanceNode(caller, node)
499+ calleedict[caller] = node
518500 else
519501 error(" tag " , tag, " unknown" )
520502 end
521503 end
522- return mmi_trees!(nodes, calleridxss, matchess)
523- end
524-
525- function mmi_trees!(nodes:: AbstractVector{EdgeNodeType} , calleridxss:: Vector{Vector{Int}} , matchess:: AbstractDict{Int,Vector{Method}} )
526- iscaller = BitSet()
527-
528- function filltree!(mminvs:: MultiMethodInvalidations , i:: Int )
529- node = nodes[i]
530- calleridxs = calleridxss[i]
531- if isa(node, Union{DataType,Binding})
532- while ! isempty(calleridxs)
533- j = pop!(calleridxs)
534- push!(iscaller, j)
535- root = InstanceNode(nodes[j], 0 )
536- push!(mminvs. mt_backedges, node => root)
537- fillnode!(root, j)
538- end
539- else
540- root = InstanceNode(node, 0 )
541- push!(mminvs. backedges, root)
542- fillnode!(root, i)
543- end
544- return mminvs
504+ if ! isempty(mmibad)
505+ push!(mminvs, mmibad)
545506 end
546-
547- function fillnode!(node:: InstanceNode , k)
548- calleridxs = isassigned(calleridxss, k) ? calleridxss[k] : nothing
549- calleridxs === nothing && return
550- while ! isempty(calleridxs)
551- j = pop!(calleridxs)
552- push!(iscaller, j)
553- child = InstanceNode(nodes[j], node)
554- fillnode!(child, j)
555- end
556- end
557-
558- # If anything gets added to `mminv0`, it means the cause occurred outside observation with `@snoop_invalidations`
559- badarg = Method[]
560- mminv0 = MultiMethodInvalidations(badarg)
561-
562- treeindex = Dict{Union{Vector{Method},Binding},Int}()
563- mminvs = MultiMethodInvalidations[]
564- for i in eachindex(nodes)
565- if i ∉ iscaller
566- node = nodes[i]
567- arg = get(matchess, i, node)
568- j = get(treeindex, arg, nothing )
569- if j === nothing
570- if isa(arg, Binding) || (isa(arg, Vector{Method}) && ! isempty(arg))
571- mminv = MultiMethodInvalidations(arg)
572- push!(mminvs, mminv)
573- j = length(mminvs)
574- treeindex[arg] = j
575- else
576- mminv = mminv0
577- end
578- else
579- mminv = mminvs[j]
580- end
581- filltree!(mminv, i)
582- else
583- @assert ! isassigned(calleridxss, i) || isempty(calleridxss[i])
584- end
585- end
586- isempty(mminv0) || push!(mminvs, mminv0)
507+ sort!(mminvs; by= countchildren)
587508 return mminvs
588509end
589510
@@ -641,6 +562,50 @@ function invalidation_trees(list::InvalidationLists; consolidate::Bool=true, kwa
641562 trees = mtrees
642563 mindex = Dict{Union{Method,Binding},Int}(tree. method => i for (i, tree) in enumerate(mtrees)) # map method to index in mtrees
643564 for etree in etrees
565+ if etree. reason === :unknown
566+ push!(trees, MethodInvalidations(
567+ nothing ,
568+ :unknown,
569+ etree. mt_backedges,
570+ etree. backedges,
571+ MethodInstance[], # mt_cache
572+ MethodInstance[] # mt_disable
573+ ))
574+ continue
575+ end
576+ if etree. reason === :deleting
577+ @assert isempty(etree. backedges) # should not have any backedges
578+ # Determine whether any of the deleted methods cover this
579+ covered = false
580+ for (edge, node) in etree. mt_backedges
581+ for mtree in mtrees
582+ mtree. reason === :deleting || continue
583+ mtree. method. sig <: edge || continue
584+ # This edge is covered by the deleted method
585+ join_invalidations!(mtree. mt_backedges, edge => node)
586+ covered = true
587+ end
588+ covered && continue
589+ # Try to find a deleted method that's applicable
590+ # The challenge is we don't know any world information
591+ methodtable = @static isdefinedglobal(Core, :methodtable) ? Core. methodtable : Core. GlobalMethods
592+ methmatches = Method[]
593+ Base. visit(methodtable) do m
594+ if iszero(m. dispatch_status) && m. sig <: edge
595+ push!(methmatches, m)
596+ end
597+ end
598+ m = length(methmatches) == 1 ? first(methmatches) : nothing
599+ push!(trees, MethodInvalidations(
600+ m,
601+ :deleting,
602+ BackedgeMT[edge => node],
603+ InstanceNode[], # backedges
604+ MethodInstance[], # mt_cache
605+ MethodInstance[] # mt_disable
606+ ))
607+ end
608+ end
644609 methods = etree. methods
645610 if isa(methods, Vector{Method})
646611 for method in methods
0 commit comments