Skip to content

Commit 92cdcba

Browse files
authored
trimming: follow-ups to finalizer support (#58311)
Should cover all the post-review comments from #58014 ~~Also disables the eager compilation of finalizers unless `--trim` is enabled, but I'm happy to re-enable that.~~ edit: re-enabled
2 parents da6356b + e9d534c commit 92cdcba

File tree

4 files changed

+59
-24
lines changed

4 files changed

+59
-24
lines changed

Compiler/src/typeinfer.jl

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,29 +1611,28 @@ const TRIM_UNSAFE = 2
16111611
const TRIM_UNSAFE_WARN = 3
16121612
function typeinf_ext_toplevel(methods::Vector{Any}, worlds::Vector{UInt}, trim_mode::Int)
16131613
inf_params = InferenceParams(; force_enable_inference = trim_mode != TRIM_NO)
1614+
1615+
# Create an "invokelatest" queue to enable eager compilation of speculative
1616+
# invokelatest calls such as from `Core.finalizer` and `ccallable`
16141617
invokelatest_queue = CompilationQueue(;
16151618
interp = NativeInterpreter(get_world_counter(); inf_params)
16161619
)
1620+
16171621
codeinfos = []
1618-
is_latest_world = true # whether this_world == world_counter()
16191622
workqueue = CompilationQueue(; interp = nothing)
16201623
for this_world in reverse!(sort!(worlds))
16211624
workqueue = CompilationQueue(workqueue;
16221625
interp = NativeInterpreter(this_world; inf_params)
16231626
)
16241627

16251628
append!(workqueue, methods)
1626-
if is_latest_world
1627-
# Provide the `invokelatest` queue so that we trigger "best-effort" code generation
1628-
# for, e.g., finalizers and cfunction.
1629-
#
1630-
# The queue is intentionally aliased, to handle e.g. a `finalizer` calling `Core.finalizer`
1631-
# (it will enqueue into itself and immediately drain)
1632-
compile!(codeinfos, workqueue; invokelatest_queue = workqueue)
1633-
else
1634-
compile!(codeinfos, workqueue)
1635-
end
1636-
is_latest_world = false
1629+
compile!(codeinfos, workqueue; invokelatest_queue)
1630+
end
1631+
1632+
if invokelatest_queue !== nothing
1633+
# This queue is intentionally aliased, to handle e.g. a `finalizer` calling `Core.finalizer`
1634+
# (it will enqueue into itself and immediately drain)
1635+
compile!(codeinfos, invokelatest_queue; invokelatest_queue)
16371636
end
16381637

16391638
if trim_mode != TRIM_NO && trim_mode != TRIM_UNSAFE

Compiler/src/verifytrim.jl

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ using ..Compiler:
1515
hasintersect, haskey, in, isdispatchelem, isempty, isexpr, iterate, length, map!, max,
1616
pop!, popfirst!, push!, pushfirst!, reinterpret, reverse!, reverse, setindex!,
1717
setproperty!, similar, singleton_type, sptypes_from_meth_instance,
18-
unsafe_pointer_to_objref, widenconst,
18+
unsafe_pointer_to_objref, widenconst, isconcretetype,
1919
# misc
20-
@nospecialize, C_NULL
20+
@nospecialize, @assert, C_NULL
2121
using ..IRShow: LineInfoNode, print, show, println, append_scopes!, IOContext, IO, normalize_method_name
2222
using ..Base: Base, sourceinfo_slotnames
2323
using ..Base.StackTraces: StackFrame
@@ -166,7 +166,7 @@ function may_dispatch(@nospecialize ftyp)
166166
end
167167
end
168168

169-
function verify_codeinstance!(codeinst::CodeInstance, codeinfo::CodeInfo, inspected::IdSet{CodeInstance}, caches::IdDict{MethodInstance,CodeInstance}, parents::ParentMap, errors::ErrorList)
169+
function verify_codeinstance!(interp::NativeInterpreter, codeinst::CodeInstance, codeinfo::CodeInfo, inspected::IdSet{CodeInstance}, caches::IdDict{MethodInstance,CodeInstance}, parents::ParentMap, errors::ErrorList)
170170
mi = get_ci_mi(codeinst)
171171
sptypes = sptypes_from_meth_instance(mi)
172172
src = codeinfo.code
@@ -199,9 +199,9 @@ function verify_codeinstance!(codeinst::CodeInstance, codeinfo::CodeInfo, inspec
199199
if !may_dispatch(ftyp)
200200
continue
201201
end
202-
# TODO: Make interp elsewhere
203-
interp = NativeInterpreter(Base.get_world_counter())
204-
if Core._apply_iterate isa ftyp
202+
if !isconcretetype(ftyp)
203+
error = "unresolved call to (unknown) builtin"
204+
elseif Core._apply_iterate isa ftyp
205205
if length(stmt.args) >= 3
206206
# args[1] is _apply_iterate object
207207
# args[2] is invoke object
@@ -233,9 +233,23 @@ function verify_codeinstance!(codeinst::CodeInstance, codeinfo::CodeInfo, inspec
233233

234234
error = "unresolved finalizer registered"
235235
end
236-
else
237-
error = "unresolved call to builtin"
238-
end
236+
elseif Core._apply isa ftyp
237+
error = "trim verification not yet implemented for builtin `Core._apply`"
238+
elseif Core._call_in_world_total isa ftyp
239+
error = "trim verification not yet implemented for builtin `Core._call_in_world_total`"
240+
elseif Core.invoke isa ftyp
241+
error = "trim verification not yet implemented for builtin `Core.invoke`"
242+
elseif Core.invoke_in_world isa ftyp
243+
error = "trim verification not yet implemented for builtin `Core.invoke_in_world`"
244+
elseif Core.invokelatest isa ftyp
245+
error = "trim verification not yet implemented for builtin `Core.invokelatest`"
246+
elseif Core.modifyfield! isa ftyp
247+
error = "trim verification not yet implemented for builtin `Core.modifyfield!`"
248+
elseif Core.modifyglobal! isa ftyp
249+
error = "trim verification not yet implemented for builtin `Core.modifyglobal!`"
250+
elseif Core.memoryrefmodify! isa ftyp
251+
error = "trim verification not yet implemented for builtin `Core.memoryrefmodify!`"
252+
else @assert false "unexpected builtin" end
239253
end
240254
extyp = argextype(SSAValue(i), codeinfo, sptypes)
241255
if extyp === Union{}
@@ -267,6 +281,7 @@ end
267281

268282
function get_verify_typeinf_trim(codeinfos::Vector{Any})
269283
this_world = get_world_counter()
284+
interp = NativeInterpreter(this_world)
270285
inspected = IdSet{CodeInstance}()
271286
caches = IdDict{MethodInstance,CodeInstance}()
272287
errors = ErrorList()
@@ -287,7 +302,7 @@ function get_verify_typeinf_trim(codeinfos::Vector{Any})
287302
item = codeinfos[i]
288303
if item isa CodeInstance
289304
src = codeinfos[i + 1]::CodeInfo
290-
verify_codeinstance!(item, src, inspected, caches, parents, errors)
305+
verify_codeinstance!(interp, item, src, inspected, caches, parents, errors)
291306
elseif item isa SimpleVector
292307
rt = item[1]::Type
293308
sig = item[2]::Type

Compiler/test/verifytrim.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,24 @@ let infos = Any[]
1818
@test isempty(parents)
1919
end
2020

21+
finalizer(@nospecialize(f), @nospecialize(o)) = Core.finalizer(f, o)
22+
23+
let infos = typeinf_ext_toplevel(Any[Core.svec(Nothing, Tuple{typeof(finalizer), typeof(identity), Any})], [Base.get_world_counter()], TRIM_UNSAFE)
24+
errors, parents = get_verify_typeinf_trim(infos)
25+
@test !isempty(errors) # unresolvable finalizer
26+
27+
# the only error should be a CallMissing error for the Core.finalizer builtin
28+
(warn, desc) = only(errors)
29+
@test !warn
30+
@test desc isa CallMissing
31+
@test occursin("finalizer", desc.desc)
32+
repr = sprint(verify_print_error, desc, parents)
33+
@test occursin(
34+
r"""^unresolved finalizer registered from statement \(Core.finalizer\)\(f::Any, o::Any\)::Nothing
35+
Stacktrace:
36+
\[1\] finalizer\(f::Any, o::Any\)""", repr)
37+
end
38+
2139
make_cfunction() = @cfunction(+, Float64, (Int64,Int64))
2240

2341
# use TRIM_UNSAFE to bypass verifier inside typeinf_ext_toplevel

HISTORY.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ New language features
55
---------------------
66

77
* New option `--trim` creates smaller binaries by removing code that was not proven to be reachable from
8-
entry points. Entry points can be marked using `Base.Experimental.entrypoint` ([#55047]).
9-
* Redefinition of constants is now well defined and follows world age semantics. Additional redefinitions (e.g. of structs) are now allowed. See [the new manual chapter on world age](https://docs.julialang.org/en/v1.13-dev/manual/worldage/).
8+
entry points. Entry points can be marked using `Base.Experimental.entrypoint` ([#55047]). To support
9+
Core.finalizer, inference will now opportunistically discover future invokelatest calls and compile
10+
the required code for them.
11+
* Redefinition of constants is now well defined and follows world age semantics. Additional redefinitions
12+
(e.g. of structs) are now allowed. See [the new manual chapter on world age](https://docs.julialang.org/en/v1.13-dev/manual/worldage/).
1013
* A new keyword argument `usings::Bool` has been added to `names`, returning all names visible
1114
via `using` ([#54609]).
1215
* The `@atomic` macro family now supports reference assignment syntax, e.g. `@atomic :monotonic v[3] += 4`,

0 commit comments

Comments
 (0)