Skip to content

[DO NOT MERGE] 1.12 backport: Add finalizer support for --trim #58065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: release-1.12
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions Compiler/src/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1243,8 +1243,29 @@ function typeinf_type(interp::AbstractInterpreter, mi::MethodInstance)
return ci.rettype
end

# Resolve a call, as described by `argtype` to a single matching
# Method and return a compilable MethodInstance for the call, if
# it will be runtime-dispatched to exactly that MethodInstance
function compileable_specialization_for_call(interp::AbstractInterpreter, @nospecialize(argtype))
matches = findall(argtype, method_table(interp); limit = 1)
matches === nothing && return nothing
length(matches.matches) == 0 && return nothing
match = only(matches.matches)

compileable_atype = get_compileable_sig(match.method, match.spec_types, match.sparams)
compileable_atype === nothing && return nothing
if match.spec_types !== compileable_atype
sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), compileable_atype, match.method.sig)::SimpleVector
sparams = sp_[2]::SimpleVector
mi = specialize_method(match.method, compileable_atype, sparams)
else
mi = specialize_method(match.method, compileable_atype, match.sparams)
end
return mi
end

# collect a list of all code that is needed along with CodeInstance to codegen it fully
function collectinvokes!(wq::Vector{CodeInstance}, ci::CodeInfo)
function collectinvokes!(wq::Vector{CodeInstance}, ci::CodeInfo, sptypes::Vector{VarState}, seen::Union{Nothing,IdSet{MethodInstance}})
src = ci.code
for i = 1:length(src)
stmt = src[i]
Expand All @@ -1253,6 +1274,34 @@ function collectinvokes!(wq::Vector{CodeInstance}, ci::CodeInfo)
edge = stmt.args[1]
edge isa CodeInstance && isdefined(edge, :inferred) && push!(wq, edge)
end

if isexpr(stmt, :call) && seen !== nothing
farg = stmt.args[1]
!applicable(argextype, farg, ci, sptypes) && continue # TODO: Why is this failing during bootstrap
ftyp = widenconst(argextype(farg, ci, sptypes))
if ftyp <: Builtin
# TODO: Make interp elsewhere
interp = NativeInterpreter(Base.get_world_counter())
if Core.finalizer isa ftyp && length(stmt.args) == 3
finalizer = argextype(stmt.args[2], ci, sptypes)
obj = argextype(stmt.args[3], ci, sptypes)
atype = argtypes_to_type(Any[finalizer, obj])

mi = compileable_specialization_for_call(interp, atype)
mi === nothing && continue

mi in seen && continue
push!(seen, mi)

if mi.def.primary_world <= Base.get_world_counter() <= mi.def.deleted_world
ci = typeinf_ext(interp, mi, SOURCE_MODE_NOT_REQUIRED)
# TODO: separate workqueue for NativeInterpreter
ci isa CodeInstance && push!(wq, ci)
end
# push!(wq, mi)
end
end
end
# TODO: handle other StmtInfo like @cfunction and OpaqueClosure?
end
end
Expand Down Expand Up @@ -1289,7 +1338,10 @@ function add_codeinsts_to_jit!(interp::AbstractInterpreter, ci, source_mode::UIn
end
end
push!(inspected, callee)
collectinvokes!(tocompile, src)
mi = get_ci_mi(callee)
sptypes = sptypes_from_meth_instance(mi)
collectinvokes!(tocompile, src, sptypes, nothing)
# collectinvokes!(tocompile, src)
mi = get_ci_mi(callee)
if iszero(ccall(:jl_mi_cache_has_ci, Cint, (Any, Any), mi, callee))
cached = ccall(:jl_get_ci_equiv, Any, (Any, UInt), callee, get_inference_world(interp))::CodeInstance
Expand Down Expand Up @@ -1328,6 +1380,7 @@ const TRIM_UNSAFE_WARN = 3
function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_mode::Int)
inspected = IdSet{CodeInstance}()
tocompile = Vector{CodeInstance}()
inspected_mis = IdSet{MethodInstance}()
codeinfos = []
# first compute the ABIs of everything
latest = true # whether this_world == world_counter()
Expand Down Expand Up @@ -1389,7 +1442,8 @@ function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_m
end
push!(inspected, callee)
if src isa CodeInfo
collectinvokes!(tocompile, src)
sptypes = sptypes_from_meth_instance(mi)
collectinvokes!(tocompile, src, sptypes, inspected_mis)
# try to reuse an existing CodeInstance from before to avoid making duplicates in the cache
if iszero(ccall(:jl_mi_cache_has_ci, Cint, (Any, Any), mi, callee))
cached = ccall(:jl_get_ci_equiv, Any, (Any, UInt), callee, this_world)::CodeInstance
Expand Down
16 changes: 13 additions & 3 deletions Compiler/src/verifytrim.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

import ..Compiler: verify_typeinf_trim
import ..Compiler: verify_typeinf_trim, NativeInterpreter, argtypes_to_type, compileable_specialization_for_call

using ..Compiler:
# operators
Expand Down Expand Up @@ -199,6 +199,8 @@ function verify_codeinstance!(codeinst::CodeInstance, codeinfo::CodeInfo, inspec
if !may_dispatch(ftyp)
continue
end
# TODO: Make interp elsewhere
interp = NativeInterpreter(Base.get_world_counter())
if Core._apply_iterate isa ftyp
if length(stmt.args) >= 3
# args[1] is _apply_iterate object
Expand All @@ -219,9 +221,17 @@ function verify_codeinstance!(codeinst::CodeInstance, codeinfo::CodeInfo, inspec
end
elseif Core.finalizer isa ftyp
if length(stmt.args) == 3
# TODO: check that calling `args[1](args[2])` is defined before warning
finalizer = argextype(stmt.args[2], codeinfo, sptypes)
obj = argextype(stmt.args[3], codeinfo, sptypes)
atype = argtypes_to_type(Any[finalizer, obj])

mi = compileable_specialization_for_call(interp, atype)
if mi !== nothing
ci = get(caches, mi, nothing)
ci isa CodeInstance && continue
end

error = "unresolved finalizer registered"
warn = true
end
else
error = "unresolved call to builtin"
Expand Down
8 changes: 7 additions & 1 deletion base/runtime_internals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1570,12 +1570,18 @@ end

is_nospecialized(method::Method) = method.nospecialize ≠ 0
is_nospecializeinfer(method::Method) = method.nospecializeinfer && is_nospecialized(method)

"""
Return MethodInstance corresponding to `atype` and `sparams`.

No widening / narrowing / compileable-normalization of `atype` is performed.
"""
function specialize_method(method::Method, @nospecialize(atype), sparams::SimpleVector; preexisting::Bool=false)
@inline
if isa(atype, UnionAll)
atype, sparams = normalize_typevars(method, atype, sparams)
end
if is_nospecializeinfer(method)
if is_nospecializeinfer(method) # TODO: this shouldn't be here
atype = get_nospecializeinfer_sig(method, atype, sparams)
end
if preexisting
Expand Down
1 change: 1 addition & 0 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -3219,6 +3219,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *matc
}

// compile-time method lookup
// intersect types with the MT, and return a single compileable specialization that covers the intersection.
jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world, int mt_cache)
{
if (jl_has_free_typevars((jl_value_t*)types))
Expand Down
Loading