Skip to content
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

Cache external CodeInstances #43990

Merged
merged 7 commits into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
19 changes: 18 additions & 1 deletion base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ end_base_include = time_ns()
const _sysimage_modules = PkgId[]
in_sysimage(pkgid::PkgId) = pkgid in _sysimage_modules

# Precompiles for Revise
# Precompiles for Revise and other packages
# TODO: move these to contrib/generate_precompile.jl
# The problem is they don't work there
for match = _methods(+, (Int, Int), -1, get_world_counter())
Expand Down Expand Up @@ -461,6 +461,23 @@ for match = _methods(+, (Int, Int), -1, get_world_counter())

# Code loading uses this
sortperm(mtime.(readdir(".")), rev=true)
# JLLWrappers uses these
Dict{UUID,Set{String}}()[UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210")] = Set{String}()
get!(Set{String}, Dict{UUID,Set{String}}(), UUID("692b3bcd-3c85-4b1f-b108-f13ce0eb3210"))
eachindex(IndexLinear(), Expr[])
push!(Expr[], Expr(:return, false))
vcat(String[], String[])
k, v = (:hello => nothing)
precompile(indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int))
precompile(indexed_iterate, (Pair{Symbol, Union{Nothing, String}}, Int, Int))
# Preferences uses these
precompile(get_preferences, (UUID,))
precompile(record_compiletime_preference, (UUID, String))
get(Dict{String,Any}(), "missing", nothing)
delete!(Dict{String,Any}(), "missing")
for (k, v) in Dict{String,Any}()
println(k)
end

break # only actually need to do this once
end
Expand Down
61 changes: 36 additions & 25 deletions base/binaryplatforms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ struct Platform <: AbstractPlatform
# The "compare strategy" allows selective overriding on how a tag is compared
compare_strategies::Dict{String,Function}

function Platform(arch::String, os::String;
# Passing `tags` as a `Dict` avoids the need to infer different NamedTuple specializations
function Platform(arch::String, os::String, _tags::Dict{String};
validate_strict::Bool = false,
compare_strategies::Dict{String,<:Function} = Dict{String,Function}(),
kwargs...)
compare_strategies::Dict{String,<:Function} = Dict{String,Function}())
# A wee bit of normalization
os = lowercase(os)
arch = CPUID.normalize_arch(arch)
Expand All @@ -52,8 +52,9 @@ struct Platform <: AbstractPlatform
"arch" => arch,
"os" => os,
)
for (tag, value) in kwargs
tag = lowercase(string(tag::Symbol))
for (tag, value) in _tags
value = value::Union{String,VersionNumber,Nothing}
tag = lowercase(tag)
if tag ∈ ("arch", "os")
throw(ArgumentError("Cannot double-pass key $(tag)"))
end
Expand All @@ -70,8 +71,8 @@ struct Platform <: AbstractPlatform
if tag ∈ ("libgfortran_version", "libstdcxx_version", "os_version")
if isa(value, VersionNumber)
value = string(value)
elseif isa(value, AbstractString)
v = tryparse(VersionNumber, String(value)::String)
elseif isa(value, String)
v = tryparse(VersionNumber, value)
if isa(v, VersionNumber)
value = string(v)
end
Expand Down Expand Up @@ -110,6 +111,19 @@ struct Platform <: AbstractPlatform
end
end

# Keyword interface (to avoid inference of specialized NamedTuple methods, use the Dict interface for `tags`)
function Platform(arch::String, os::String;
validate_strict::Bool = false,
compare_strategies::Dict{String,<:Function} = Dict{String,Function}(),
kwargs...)
tags = Dict{String,Any}(String(tag)::String=>tagvalue(value) for (tag, value) in kwargs)
return Platform(arch, os, tags; validate_strict, compare_strategies)
end

tagvalue(v::Union{String,VersionNumber,Nothing}) = v
tagvalue(v::Symbol) = String(v)
tagvalue(v::AbstractString) = convert(String, v)::String

# Simple tag insertion that performs a little bit of validation
function add_tag!(tags::Dict{String,String}, tag::String, value::String)
# I know we said only alphanumeric and dots, but let's be generous so that we can expand
Expand Down Expand Up @@ -698,21 +712,22 @@ function Base.parse(::Type{Platform}, triplet::AbstractString; validate_strict::
end

# Extract the information we're interested in:
tags = Dict{String,Any}()
arch = get_field(m, arch_mapping)
os = get_field(m, os_mapping)
libc = get_field(m, libc_mapping)
call_abi = get_field(m, call_abi_mapping)
libgfortran_version = get_field(m, libgfortran_version_mapping)
libstdcxx_version = get_field(m, libstdcxx_version_mapping)
cxxstring_abi = get_field(m, cxxstring_abi_mapping)
tags["libc"] = get_field(m, libc_mapping)
tags["call_abi"] = get_field(m, call_abi_mapping)
tags["libgfortran_version"] = get_field(m, libgfortran_version_mapping)
tags["libstdcxx_version"] = get_field(m, libstdcxx_version_mapping)
tags["cxxstring_abi"] = get_field(m, cxxstring_abi_mapping)
function split_tags(tagstr)
tag_fields = split(tagstr, "-"; keepempty=false)
if isempty(tag_fields)
return Pair{String,String}[]
end
return map(v -> Symbol(v[1]) => v[2], split.(tag_fields, "+"))
return map(v -> String(v[1]) => String(v[2]), split.(tag_fields, "+"))
end
tags = split_tags(m["tags"])
merge!(tags, Dict(split_tags(m["tags"])))

# Special parsing of os version number, if any exists
function extract_os_version(os_name, pattern)
Expand All @@ -729,18 +744,9 @@ function Base.parse(::Type{Platform}, triplet::AbstractString; validate_strict::
if os == "freebsd"
os_version = extract_os_version("freebsd", r".*freebsd([\d.]+)")
end
tags["os_version"] = os_version

return Platform(
arch, os;
validate_strict,
libc,
call_abi,
libgfortran_version,
cxxstring_abi,
libstdcxx_version,
os_version,
tags...,
)
return Platform(arch, os, tags; validate_strict)
end
throw(ArgumentError("Platform `$(triplet)` is not an officially supported platform"))
end
Expand Down Expand Up @@ -1067,4 +1073,9 @@ function select_platform(download_info::Dict, platform::AbstractPlatform = HostP
return download_info[p]
end

# precompiles to reduce latency (see https://github.com/JuliaLang/julia/pull/43990#issuecomment-1025692379)
Dict{Platform,String}()[HostPlatform()] = ""
Platform("x86_64", "linux", Dict{String,Any}(); validate_strict=true)
Platform("x86_64", "linux", Dict{String,String}(); validate_strict=false) # called this way from Artifacts.unpack_platform

end # module
10 changes: 10 additions & 0 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

# Tracking of newly-inferred MethodInstances during precompilation
const track_newly_inferred = RefValue{Bool}(false)
const newly_inferred = MethodInstance[]

# build (and start inferring) the inference frame for the top-level MethodInstance
function typeinf(interp::AbstractInterpreter, result::InferenceResult, cache::Symbol)
frame = InferenceState(result, cache, interp)
Expand Down Expand Up @@ -389,6 +393,12 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult)
if !already_inferred
inferred_result = transform_result_for_cache(interp, linfo, valid_worlds, result.src)
code_cache(interp)[linfo] = CodeInstance(result, inferred_result, valid_worlds)
if track_newly_inferred[]
m = linfo.def
if isa(m, Method)
m.module != Core && push!(newly_inferred, linfo)
end
end
end
unlock_mi_inference(interp, linfo)
nothing
Expand Down
8 changes: 6 additions & 2 deletions base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1395,13 +1395,17 @@ function include_package_for_output(pkg::PkgId, input::String, depot_path::Vecto
task_local_storage()[:SOURCE_PATH] = source
end

Core.Compiler.track_newly_inferred.x = true
try
Base.include(Base.__toplevel__, input)
catch ex
precompilableerror(ex) || rethrow()
@debug "Aborting `create_expr_cache'" exception=(ErrorException("Declaration of __precompile__(false) not allowed"), catch_backtrace())
exit(125) # we define status = 125 means PrecompileableError
finally
Core.Compiler.track_newly_inferred.x = false
end
ccall(:jl_set_newly_inferred, Cvoid, (Any,), Core.Compiler.newly_inferred)
end

const PRECOMPILE_TRACE_COMPILE = Ref{String}()
Expand Down Expand Up @@ -2033,12 +2037,12 @@ end
Compile the given function `f` for the argument tuple (of types) `args`, but do not execute it.
"""
function precompile(@nospecialize(f), args::Tuple)
function precompile(@nospecialize(f), @nospecialize(args::Tuple))
precompile(Tuple{Core.Typeof(f), args...})
end

const ENABLE_PRECOMPILE_WARNINGS = Ref(false)
function precompile(argt::Type)
function precompile(@nospecialize(argt::Type))
ret = ccall(:jl_compile_hint, Int32, (Any,), argt) != 0
if !ret && ENABLE_PRECOMPILE_WARNINGS[]
@warn "Inactive precompile statement" maxlog=100 form=argt _module=nothing _file=nothing _line=0
Expand Down
2 changes: 1 addition & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7849,7 +7849,7 @@ jl_compile_result_t jl_emit_codeinst(
// don't delete inlineable code, unless it is constant
(codeinst->invoke == jl_fptr_const_return_addr || !jl_ir_flag_inlineable((jl_array_t*)codeinst->inferred)) &&
// don't delete code when generating a precompile file
!imaging_mode) {
!(imaging_mode || jl_options.incremental)) {
// if not inlineable, code won't be needed again
codeinst->inferred = jl_nothing;
}
Expand Down
Loading