From b864b8e991caebc6bf36c8f8d2b83757bed2e561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Belmant?= Date: Fri, 30 May 2025 10:16:33 +0000 Subject: [PATCH] Adjust to changes on nightly --- demos/abstract_analyze_versions.jl | 3 ++- src/MethodAnalysis.jl | 6 ++--- src/backedges.jl | 19 ++++++++++++++- src/findcallers.jl | 38 ++++++++++++++++++++++-------- src/visit.jl | 22 ++++++++++++++--- 5 files changed, 70 insertions(+), 18 deletions(-) diff --git a/demos/abstract_analyze_versions.jl b/demos/abstract_analyze_versions.jl index 773d500..e9d728f 100644 --- a/demos/abstract_analyze_versions.jl +++ b/demos/abstract_analyze_versions.jl @@ -63,7 +63,8 @@ sigs, c1, c2 = split_comparable(sigstable, sigmaster) nz1, nz2 = tally0(c1, c2) println("$stablever has $nz1 with no backedges, master has $nz2") mx1, mx2 = maximum(c1), maximum(c2) -isexported(sig) = (ft = Base.unwrap_unionall(sig).parameters[1]; isdefined(Main, ft.name.mt.name)) +get_fname(@nospecialize(fT::DataType)) = @static VERSION ≥ v"1.13.0-DEV.647" ? fT.name.singletonname : fT.name.mt.name +isexported(sig) = (ft = Base.unwrap_unionall(sig).parameters[1]; isdefined(Main, get_fname(ft))) colors = [isexported(sig) ? "magenta" : "green" for sig in sigs] function on_click(event) diff --git a/src/MethodAnalysis.jl b/src/MethodAnalysis.jl index 1348cac..f712b3a 100644 --- a/src/MethodAnalysis.jl +++ b/src/MethodAnalysis.jl @@ -3,7 +3,7 @@ module MethodAnalysis using AbstractTrees using Base: Callable, IdSet -using Core: MethodInstance, CodeInfo, SimpleVector, MethodTable +using Core: MethodInstance, CodeInstance, CodeInfo, SimpleVector, MethodTable using Base.Meta: isexpr export visit, visit_withmodule @@ -66,11 +66,11 @@ end # A few fields are deliberately unchecked function equal(ci1::Core.CodeInfo, ci2::Core.CodeInfo) ret = ci1.code == ci2.code && - ci1.codelocs == ci2.codelocs && + (@static hasfield(Core.CodeInfo, :debuginfo) ? ci1.debuginfo == ci2.debuginfo : + ci1.codelocs == ci2.codelocs && ci1.linetable == ci2.linetable) && ci1.ssavaluetypes == ci2.ssavaluetypes && ci1.ssaflags == ci2.ssaflags && ci1.method_for_inference_limit_heuristics == ci2.method_for_inference_limit_heuristics && - ci1.linetable == ci2.linetable && ci1.slotnames == ci2.slotnames && ci1.slotflags == ci2.slotflags if VERSION >= v"1.2" diff --git a/src/backedges.jl b/src/backedges.jl index ed18373..86b5bbd 100644 --- a/src/backedges.jl +++ b/src/backedges.jl @@ -69,6 +69,19 @@ function direct_backedges(f::Union{Method,Callable}; skip::Bool=true) end end return false + elseif isa(item, Core.TypeName) + tn = item::Core.TypeName + if isdefined(tn, :backedges) + # `tn.backedges` is typed as `Core.MethodCache`, but is actually + # a `Vector{Any}`; the pointer shenanigans mimic a reinterpret. + sigmis = unsafe_pointer_to_objref(pointer_from_objref(tn.backedges))::Vector{Any} + for i = 1:2:length(sigmis) + sig, edge = sigmis[i], sigmis[i+1]::CodeInstance + mi = Core.Compiler.get_ci_mi(edge) + push!(bes, Pair{Any,MethodInstance}(sig, mi)) + push!(_skip, mi) + end + end elseif isa(item, MethodInstance) callee = item::MethodInstance if isdefined(callee, :backedges) @@ -91,6 +104,7 @@ if isdefined(Core.Compiler, :BackedgeIterator) else function push_unskipped_backedges!(bes, callee, skip, _skip) for caller in callee.backedges + isa(caller, CodeInstance) && (caller = Core.Compiler.get_ci_mi(caller)) skip && caller ∈ _skip && continue push!(bes, callee=>caller) end @@ -107,7 +121,10 @@ meaning it returns an empty list even when `mi.backedges` is not defined. function direct_backedges(mi::MethodInstance) out = MethodInstance[] if isdefined(mi, :backedges) - append!(out, mi.backedges) + for edge in mi.backedges + isa(edge, CodeInstance) && (edge = Core.Compiler.get_ci_mi(edge)) + push!(out, edge) + end end return out end diff --git a/src/findcallers.jl b/src/findcallers.jl index 08a3a11..5b798c9 100644 --- a/src/findcallers.jl +++ b/src/findcallers.jl @@ -3,15 +3,19 @@ export findcallers function get_typed_instances!(srcs, @nospecialize(tt), method::Method, world, interp) # This is essentially taken from code_typed_by_type matches = Base._methods_by_ftype(tt, -1, world) - if matches === false - error("signature $(item.specTypes) does not correspond to a generic function") + if matches === false || matches === nothing + error("signature $tt does not correspond to a generic function") end for match in matches match.method == method || continue - meth = Base.func_for_method_checked(match.method, tt, match.sparams) - (src, ty) = isdefined(Core.Compiler, :NativeInterpreter) ? + meth = match.method + if isdefined(Base, :func_for_method_checked) + meth = Base.func_for_method_checked(meth, tt, match.sparams) + end + ret = isdefined(Core.Compiler, :NativeInterpreter) ? Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, false) : Core.Compiler.typeinf_code(meth, match.spec_types, match.sparams, false, interp) + src = isa(ret, Tuple) ? ret[1] : ret::CodeInfo push!(srcs, (src, match.sparams)) end return srcs @@ -22,10 +26,10 @@ defaultinterp(world) = isdefined(Core.Compiler, :NativeInterpreter) ? Core.Compiler.NativeInterpreter(world) : Core.Compiler.Params(world) -function get_typed_instances(mi::MethodInstance; world=typemax(UInt), interp=defaultinterp(world)) +function get_typed_instances(mi::MethodInstance; world=default_world(), interp=defaultinterp(world)) return get_typed_instances!(Tuple{CodeInfo,Core.SimpleVector}[], mi, world, interp) end -function get_typed_instances(@nospecialize(tt), method::Method; world=typemax(UInt), interp=defaultinterp(world)) +function get_typed_instances(@nospecialize(tt), method::Method; world=default_world(), interp=defaultinterp(world)) return get_typed_instances!(Tuple{CodeInfo,Core.SimpleVector}[], tt, method, world, interp) end @@ -95,8 +99,8 @@ callers3 = findcallers(f, argtyps->length(argtyps) == 1 && argtyps[1] === Vector `findcallers` is not guaranteed to find all calls. Calls can be "obfuscated" by many mechanisms, including calls from top level, calls where the function is a runtime variable, etc. """ -function findcallers(f, argmatch::Union{Function,Nothing}, mis::AbstractVector{Core.MethodInstance}; - callhead::Symbol=:call, world=typemax(UInt), interp=defaultinterp(world)) +function findcallers(f, argmatch::Union{Function,Nothing}, mis::AbstractVector{MethodInstance}; + callhead::Symbol=:call, world=default_world(), interp=defaultinterp(world)) callhead === :call || callhead === :invoke || callhead === :iterate || error(":call and :invoke are supported, got ", callhead) # Check that f is not a type with specified parameters if f isa DataType && !isempty(f.parameters) @@ -105,7 +109,7 @@ function findcallers(f, argmatch::Union{Function,Nothing}, mis::AbstractVector{C # Construct a GlobalRef version of `f` ref = GlobalRef(parentmodule(f), nameof(f)) callers = CallMatch[] - srcs = Tuple{CodeInfo,Core.SimpleVector}[] + srcs = Tuple{CodeInfo,SimpleVector}[] for item in mis empty!(srcs) try @@ -140,6 +144,9 @@ function findcallers(f, argmatch::Union{Function,Nothing}, mis::AbstractVector{C throw(err) end end + if isa(callee, Core.Const) + callee = callee.val + end matches = false if callee === f matches = true @@ -188,6 +195,12 @@ function findcallers(f, argmatch::Union{Function,Nothing}, mis::AbstractVector{C return callers end +@static if isdefined(Base, :get_world_counter) + default_world() = Base.get_world_counter() +else + default_world() = typemax(UInt) +end + isglobalref(@nospecialize(g), mod::Module, name::Symbol) = isa(g, GlobalRef) && g.mod === mod && g.name === name extract(a, sparams) = isa(a, Core.Const) ? Core.Typeof(a.val) : @@ -203,7 +216,12 @@ function eval_ssa(src, sparams, id) if stmt.head === :call callee = stmt.args[1] if isglobalref(callee, Core, :apply_type) - return stmt.args[2] + ret = stmt.args[2] + if isa(ret, Core.SSAValue) + # try one level deeper + ret = eval_ssa(src, sparams, ret.id) + end + return ret elseif isglobalref(callee, Base, :getproperty) modg, objq = stmt.args[2], stmt.args[3] if isa(modg, Core.SSAValue) diff --git a/src/visit.jl b/src/visit.jl index 59ff1f3..12c5f86 100644 --- a/src/visit.jl +++ b/src/visit.jl @@ -127,7 +127,11 @@ function _visit(@nospecialize(operation), @nospecialize(f::Callable), visited::I print && println("Callable ", f) if operation(f) ml = methods(f) - _visit(operation, ml.mt, visited, print) + @static if hasfield(Base.MethodList, :mt) + _visit(operation, ml.mt, visited, print) + else + _visit(operation, ml.tn, visited, print) + end for m in ml.ms _visit(operation, m, visited, print) end @@ -146,14 +150,17 @@ function _visit(@nospecialize(operation), ml::Base.MethodList, visited::IdSet{An ml ∈ visited && return nothing push!(visited, ml) print && println("MethodList ", ml) - _visit(operation, ml.mt, visited, print) + @static if hasfield(Base.MethodList, :mt) + _visit(operation, ml.mt, visited, print) + else + _visit(operation, ml.tn, visited, print) + end for m in ml.ms _visit(operation, m, visited, print) end return nothing end - function _visit(@nospecialize(operation), mt::MethodTable, visited::IdSet{Any}, print::Bool) mt ∈ visited && return nothing push!(visited, mt) @@ -162,6 +169,14 @@ function _visit(@nospecialize(operation), mt::MethodTable, visited::IdSet{Any}, return nothing end +function _visit(@nospecialize(operation), tn::Core.TypeName, visited::IdSet{Any}, print::Bool) + tn ∈ visited && return nothing + push!(visited, tn) + print && println("TypeName ", tn) + operation(tn) + return nothing +end + function _visit(@nospecialize(operation), m::Method, visited::IdSet{Any}, print::Bool) m ∈ visited && return nothing push!(visited, m) @@ -285,6 +300,7 @@ else push!(visited, mi) if operation(mi) && isdefined(mi, :backedges) for edge in mi.backedges + isa(edge, CodeInstance) && (edge = Core.Compiler.get_ci_mi(edge)) _visit_backedges(operation, edge, visited) end end