Skip to content

Commit 351b730

Browse files
committed
Simplify precompiles
This switches to "do a little work" for precompilation, rather than explicitly spelling out the specific signatures. This should be more maintainable and potentially more exhaustive (or could be made to be so) since it precompiles across runtime dispatch boundaries. This also uses `Base.inferencebarrier` in several places to prevent an argument from being specialized.
1 parent 5925b99 commit 351b730

File tree

5 files changed

+44
-93
lines changed

5 files changed

+44
-93
lines changed

src/callbacks.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ You can use the return value `key` to remove the callback later
4646
(`Revise.remove_callback`) or to update it using another call
4747
to `Revise.add_callback` with `key=key`.
4848
"""
49-
function add_callback(f, files, modules=nothing; all=false, key=gensym())
49+
function add_callback(@nospecialize(f), files, modules=nothing; all=false, key=gensym())
5050
fix_trailing(path) = isdir(path) ? joinpath(path, "") : path # insert a trailing '/' if missing, see https://github.com/timholy/Revise.jl/issues/470#issuecomment-633298553
5151

5252
remove_callback(key)
@@ -91,7 +91,7 @@ end
9191
Remove a callback previously installed by a call to `Revise.add_callback(...)`.
9292
See its docstring for details.
9393
"""
94-
function remove_callback(key)
94+
function remove_callback(@nospecialize(key))
9595
for cbs in values(user_callbacks_by_file)
9696
delete!(cbs, key)
9797
end
@@ -152,7 +152,7 @@ end
152152
This will print "update" every time `"/tmp/watched.txt"` or any of the code defining
153153
`Pkg1` or `Pkg2` gets updated.
154154
"""
155-
function entr(f::Function, files, modules=nothing; all=false, postpone=false, pause=0.02)
155+
function entr(@nospecialize(f), files, modules=nothing; all=false, postpone=false, pause=0.02)
156156
yield()
157157
postpone || f()
158158
key = add_callback(files, modules; all=all) do

src/lowered.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ add_dependencies!(methodinfo::MethodInfo, be::CodeEdges, src, isrequired) = meth
2424
add_includes!(methodinfo::MethodInfo, mod::Module, filename) = methodinfo
2525

2626
# This is not generally used, see `is_method_or_eval` instead
27-
function hastrackedexpr(stmt; heads=LoweredCodeUtils.trackedheads)
27+
function hastrackedexpr(@nospecialize(stmt); heads=LoweredCodeUtils.trackedheads)
2828
haseval = false
2929
if isa(stmt, Expr)
3030
haseval = matches_eval(stmt)
@@ -122,7 +122,7 @@ end
122122
function methods_by_execution(mod::Module, ex::Expr; kwargs...)
123123
methodinfo = MethodInfo()
124124
docexprs = DocExprs()
125-
value, frame = methods_by_execution!(JuliaInterpreter.Compiled(), methodinfo, docexprs, mod, ex; kwargs...)
125+
value, frame = methods_by_execution!(Base.inferencebarrier(JuliaInterpreter.Compiled()), methodinfo, docexprs, mod, ex; kwargs...)
126126
return methodinfo, docexprs, frame
127127
end
128128

@@ -233,7 +233,7 @@ function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, mod
233233
return ret, lwr
234234
end
235235
methods_by_execution!(methodinfo, docexprs, mod::Module, ex::Expr; kwargs...) =
236-
methods_by_execution!(JuliaInterpreter.Compiled(), methodinfo, docexprs, mod, ex; kwargs...)
236+
methods_by_execution!(Base.inferencebarrier(JuliaInterpreter.Compiled()), methodinfo, docexprs, mod, ex; kwargs...)
237237

238238
function methods_by_execution!(@nospecialize(recurse), methodinfo, docexprs, frame::Frame, isrequired::AbstractVector{Bool}; mode::Symbol=:eval, skip_include::Bool=true)
239239
isok(lnn::LineTypes) = !iszero(lnn.line) || lnn.file !== :none # might fail either one, but accept anything

src/packagedef.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ and julia objects, and allows re-evaluation of code in the proper module scope.
119119
It is a dictionary indexed by PkgId:
120120
`pkgdatas[id]` returns a value of type [`Revise.PkgData`](@ref).
121121
"""
122-
const pkgdatas = Dict{PkgId,PkgData}(NOPACKAGE => PkgData(NOPACKAGE))
122+
const pkgdatas = Dict{PkgId,PkgData}()
123123

124124
const moduledeps = Dict{Module,DepDict}()
125125
function get_depdict(mod::Module)
@@ -461,7 +461,7 @@ end
461461
function eval_with_signatures(mod, ex::Expr; mode=:eval, kwargs...)
462462
methodinfo = CodeTrackingMethodInfo(ex)
463463
docexprs = DocExprs()
464-
frame = methods_by_execution!(finish_and_return!, methodinfo, docexprs, mod, ex; mode=mode, kwargs...)[2]
464+
frame = methods_by_execution!(Base.inferencebarrier(finish_and_return!), methodinfo, docexprs, mod, ex; mode=mode, kwargs...)[2]
465465
return methodinfo.allsigs, methodinfo.deps, methodinfo.includes, frame
466466
end
467467

@@ -1281,6 +1281,8 @@ function __init__()
12811281
for m in methods(includet)
12821282
push!(JuliaInterpreter.compiled_methods, m)
12831283
end
1284+
# Add the dummy package for user callbacks
1285+
pkgdatas[NOPACKAGE] = PkgData(NOPACKAGE)
12841286
# Set up a repository for methods defined at the REPL
12851287
id = PkgId(nothing, "@REPL")
12861288
pkgdatas[id] = pkgdata = PkgData(id, nothing)
@@ -1359,9 +1361,10 @@ function setup_atom(atommod::Module)::Nothing
13591361
return nothing
13601362
end
13611363

1362-
function add_revise_deps()
1364+
function add_revise_deps(skip_revise::Bool=false)
13631365
# Populate CodeTracking data for dependencies and initialize watching on code that Revise depends on
13641366
for mod in (CodeTracking, OrderedCollections, JuliaInterpreter, LoweredCodeUtils, Revise)
1367+
skip_revise && mod === Revise && continue
13651368
id = PkgId(mod)
13661369
pkgdata = parse_pkg_files(id)
13671370
init_watching(pkgdata, srcfiles(pkgdata))

src/precompile.jl

Lines changed: 31 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,38 @@
1-
macro warnpcfail(ex::Expr)
2-
modl = __module__
3-
file = __source__.file === nothing ? "?" : String(__source__.file)
4-
line = __source__.line
5-
quote
6-
$(esc(ex)) || @warn """precompile directive
7-
$($(Expr(:quote, ex)))
8-
failed. Please report an issue in $($modl) (after checking for duplicates) or remove this directive.""" _file=$file _line=$line
9-
end
1+
module __RInternal__
2+
ftmp() = 1
103
end
114

125
function _precompile_()
136
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
7+
# These are blocking so don't actually call them
8+
precompile(Tuple{TaskThunk})
9+
precompile(Tuple{typeof(wait_changed), String})
10+
precompile(Tuple{typeof(revise_dir_queued), String})
11+
precompile(Tuple{typeof(revise_file_queued), PkgData, String})
12+
precompile(Tuple{typeof(watch_manifest), String})
13+
# This excludes Revise itself
14+
precompile(Tuple{typeof(watch_package_callback), PkgId})
15+
# Too complicated to bother
16+
precompile(Tuple{typeof(includet), String})
17+
precompile(Tuple{typeof(track), Module, String})
18+
precompile(Tuple{typeof(get_def), Method})
19+
precompile(Tuple{typeof(entr), Any, Vector{String}})
1420

15-
@warnpcfail precompile(Tuple{TaskThunk})
16-
@warnpcfail precompile(Tuple{typeof(wait_changed), String})
17-
@warnpcfail precompile(Tuple{typeof(watch_package), PkgId})
18-
@warnpcfail precompile(Tuple{typeof(watch_includes), Module, String})
19-
@warnpcfail precompile(Tuple{typeof(watch_manifest), String})
20-
@warnpcfail precompile(Tuple{typeof(revise_dir_queued), String})
21-
@warnpcfail precompile(Tuple{typeof(revise_file_queued), PkgData, String})
22-
@warnpcfail precompile(Tuple{typeof(init_watching), PkgData, Vector{String}})
23-
@warnpcfail precompile(Tuple{typeof(add_revise_deps)})
24-
@warnpcfail precompile(Tuple{typeof(watch_package_callback), PkgId})
25-
26-
@warnpcfail precompile(Tuple{typeof(revise)})
27-
@warnpcfail precompile(Tuple{typeof(revise_first), Expr})
28-
@warnpcfail precompile(Tuple{typeof(includet), String})
29-
@warnpcfail precompile(Tuple{typeof(track), Module, String})
30-
# setindex! doesn't fully precompile, but it's still beneficial to do it
31-
# (it shaves off a bit of the time)
32-
# See https://github.com/JuliaLang/julia/pull/31466
33-
@warnpcfail precompile(Tuple{typeof(setindex!), ExprsSigs, Nothing, RelocatableExpr})
34-
@warnpcfail precompile(Tuple{typeof(setindex!), ExprsSigs, Vector{Any}, RelocatableExpr})
35-
@warnpcfail precompile(Tuple{typeof(setindex!), ModuleExprsSigs, ExprsSigs, Module})
36-
@warnpcfail precompile(Tuple{typeof(setindex!), Dict{PkgId,PkgData}, PkgData, PkgId})
37-
@warnpcfail precompile(Tuple{Type{WatchList}})
38-
@warnpcfail precompile(Tuple{typeof(setindex!), Dict{String,WatchList}, WatchList, String})
39-
40-
MI = CodeTrackingMethodInfo
41-
@warnpcfail precompile(Tuple{typeof(minimal_evaluation!), MI, Core.CodeInfo, Symbol})
42-
@warnpcfail precompile(Tuple{typeof(minimal_evaluation!), Any, MI, Core.CodeInfo, Symbol})
43-
@warnpcfail precompile(Tuple{typeof(methods_by_execution!), Any, MI, DocExprs, Module, Expr})
44-
@warnpcfail precompile(Tuple{typeof(methods_by_execution!), Any, MI, DocExprs, JuliaInterpreter.Frame, Vector{Bool}})
45-
@warnpcfail precompile(Tuple{typeof(Core.kwfunc(methods_by_execution!)),
46-
NamedTuple{(:mode,),Tuple{Symbol}},
47-
typeof(methods_by_execution!), Function, MI, DocExprs, Module, Expr})
48-
@warnpcfail precompile(Tuple{typeof(Core.kwfunc(methods_by_execution!)),
49-
NamedTuple{(:skip_include,),Tuple{Bool}},
50-
typeof(methods_by_execution!), Function, MI, DocExprs, Module, Expr})
51-
@warnpcfail precompile(Tuple{typeof(Core.kwfunc(methods_by_execution!)),
52-
NamedTuple{(:mode, :skip_include),Tuple{Symbol,Bool}},
53-
typeof(methods_by_execution!), Function, MI, DocExprs, Module, Expr})
54-
@warnpcfail precompile(Tuple{typeof(Core.kwfunc(methods_by_execution!)),
55-
NamedTuple{(:mode,),Tuple{Symbol}},
56-
typeof(methods_by_execution!), Function, MI, DocExprs, Frame, Vector{Bool}})
57-
@warnpcfail precompile(Tuple{typeof(Core.kwfunc(methods_by_execution!)),
58-
NamedTuple{(:mode, :skip_include),Tuple{Symbol,Bool}},
59-
typeof(methods_by_execution!), Function, MI, DocExprs, Frame, Vector{Bool}})
60-
61-
mex = which(methods_by_execution!, (Function, MI, DocExprs, Module, Expr))
62-
mbody = bodymethod(mex)
63-
# use `typeof(pairs(NamedTuple()))` here since it actually differs between Julia versions
64-
@warnpcfail precompile(Tuple{mbody.sig.parameters[1], Symbol, Bool, Bool, typeof(pairs(NamedTuple())), typeof(methods_by_execution!), Any, MI, DocExprs, Module, Expr})
65-
@warnpcfail precompile(Tuple{mbody.sig.parameters[1], Symbol, Bool, Bool, Iterators.Pairs{Symbol,Bool,Tuple{Symbol},NamedTuple{(:skip_include,),Tuple{Bool}}}, typeof(methods_by_execution!), Any, MI, DocExprs, Module, Expr})
66-
mfr = which(methods_by_execution!, (Function, MI, DocExprs, Frame, Vector{Bool}))
67-
mbody = bodymethod(mfr)
68-
@warnpcfail precompile(Tuple{mbody.sig.parameters[1], Symbol, Bool, typeof(methods_by_execution!), Any, MI, DocExprs, Frame, Vector{Bool}})
69-
70-
@warnpcfail precompile(Tuple{typeof(hastrackedexpr), Expr})
71-
@warnpcfail precompile(Tuple{typeof(get_def), Method})
72-
@warnpcfail precompile(Tuple{typeof(parse_pkg_files), PkgId})
73-
if isdefined(Revise, :filter_valid_cachefiles)
74-
@warnpcfail precompile(Tuple{typeof(filter_valid_cachefiles), String, Vector{String}})
75-
end
76-
@warnpcfail precompile(Tuple{typeof(pkg_fileinfo), PkgId})
77-
@warnpcfail precompile(Tuple{typeof(push!), WatchList, Pair{String,PkgId}})
78-
@warnpcfail precompile(Tuple{typeof(pushex!), ExprsSigs, Expr})
79-
@warnpcfail precompile(Tuple{Type{ModuleExprsSigs}, Module})
80-
@warnpcfail precompile(Tuple{Type{FileInfo}, Module, String})
81-
@warnpcfail precompile(Tuple{Type{PkgData}, PkgId})
82-
@warnpcfail precompile(Tuple{typeof(Base._deleteat!), Vector{Tuple{Module,String,Float64}}, Vector{Int}})
83-
@warnpcfail precompile(Tuple{typeof(add_require), String, Module, String, String, Expr})
84-
@warnpcfail precompile(Tuple{Core.kwftype(typeof(maybe_add_includes_to_pkgdata!)),NamedTuple{(:eval_now,), Tuple{Bool}},typeof(maybe_add_includes_to_pkgdata!),PkgData,String,Vector{Pair{Module, String}}})
85-
86-
for TT in (Tuple{Module,Expr}, Tuple{DataType,MethodSummary})
87-
@warnpcfail precompile(Tuple{Core.kwftype(typeof(Base.CoreLogging.handle_message)),NamedTuple{(:time, :deltainfo), Tuple{Float64, TT}},typeof(Base.CoreLogging.handle_message),ReviseLogger,LogLevel,String,Module,String,Symbol,String,Int})
88-
end
21+
watch_package(REVISE_ID)
22+
watch_includes(Revise, "src/Revise.jl")
23+
add_revise_deps(true)
24+
revise()
25+
revise_first(:(1+1))
26+
eval_with_signatures(__RInternal__, :(f() = 1))
27+
eval_with_signatures(__RInternal__, :(f2() = 1); skip_include=true)
28+
add_require(pathof(LoweredCodeUtils), LoweredCodeUtils, "295af30f-e4ad-537b-8983-00126c2a3abe", "Revise", :(include("somefile.jl")))
29+
add_require(pathof(JuliaInterpreter), JuliaInterpreter, "295af30f-e4ad-537b-8983-00126c2a3abe", "Revise", :(f(x) = 7))
30+
pkgdata = pkgdatas[PkgId(LoweredCodeUtils)]
31+
eval_require_now(pkgdata, length(pkgdata.info.files), last(pkgdata.info.files)*"__@require__", joinpath(basedir(pkgdata), last(pkgdata.info.files)), Revise, :(__RInternal__.ftmp(::Int) = 0))
32+
# Now empty the stores to prevent them from being serialized
33+
empty!(watched_files)
34+
empty!(watched_manifests)
35+
empty!(pkgdatas)
36+
empty!(included_files)
8937
return nothing
9038
end

test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1200,7 +1200,7 @@ end
12001200
frame = Frame(ChangeDocstring, lwr.args[1])
12011201
methodinfo = Revise.MethodInfo()
12021202
docexprs = Revise.DocExprs()
1203-
ret = Revise.methods_by_execution!(JuliaInterpreter.finish_and_return!, methodinfo,
1203+
ret = Revise.methods_by_execution!(Base.inferencebarrier(JuliaInterpreter.finish_and_return!), methodinfo,
12041204
docexprs, frame, trues(length(frame.framecode.src.code)); mode=:sigs)
12051205
ds = @doc(ChangeDocstring.f)
12061206
@test get_docstring(ds) == "g"

0 commit comments

Comments
 (0)