Skip to content

Commit

Permalink
Implement support for object caching through pkgimages (#47184)
Browse files Browse the repository at this point in the history
This implements caching native code in "package images" (pkgimages).
We now write two serialization files, one ending in `*.ji` and the other with
the platform dynamic library extension (e.g., `*.so`). The `*.ji` contains
"extended" header information (include the source-code dump for Revise),
whereas the dynamic library includes the Julia objects, including LLVM-generated
native code.

Native code is compiled once during precompilation and then a second time
to build a "clean" module. When we find an edge to an external function (already
cached in anloaded pkgimage), we emit a global variable which we will patch during
loading with the address of the function to call. This allows us to leverage the
standard multiversioning capabilities.

Co-authored-by: Tim Holy <tim.holy@gmail.com>
Co-authored-by: Kristoffer Carlsson <kristoffer.carlsson@chalmers.se>
Co-authored-by: Mosè Giordano <giordano@users.noreply.github.com>
Co-authored-by: Ian Butterworth <i.r.butterworth@gmail.com>
Co-authored-by: Max Horn <max@quendi.de>
Co-authored-by: Michael Schlottke-Lakemper <michael@sloede.com>
Co-authored-by: Alex Ames <alexander.m.ames@gmail.com>
  • Loading branch information
8 people authored Dec 27, 2022
1 parent 03bdf15 commit a2db90f
Show file tree
Hide file tree
Showing 48 changed files with 1,750 additions and 764 deletions.
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,21 @@ ifeq ($(OS),WINNT)
-$(INSTALL_M) $(wildcard $(build_bindir)/*.dll) $(DESTDIR)$(bindir)/
ifeq ($(JULIA_BUILD_MODE),release)
-$(INSTALL_M) $(build_libdir)/libjulia.dll.a $(DESTDIR)$(libdir)/
-$(INSTALL_M) $(build_libdir)/libjulia-internal.dll.a $(DESTDIR)$(libdir)/
else ifeq ($(JULIA_BUILD_MODE),debug)
-$(INSTALL_M) $(build_libdir)/libjulia-debug.dll.a $(DESTDIR)$(libdir)/
-$(INSTALL_M) $(build_libdir)/libjulia-internal-debug.dll.a $(DESTDIR)$(libdir)/
endif

# We have a single exception; we want 7z.dll to live in libexec, not bin, so that 7z.exe can find it.
-mv $(DESTDIR)$(bindir)/7z.dll $(DESTDIR)$(libexecdir)/
-$(INSTALL_M) $(build_bindir)/libopenlibm.dll.a $(DESTDIR)$(libdir)/
-$(INSTALL_M) $(build_libdir)/libssp.dll.a $(DESTDIR)$(libdir)/
# The rest are compiler dependencies, as an example memcpy is exported by msvcrt
# These are files from mingw32 and required for creating shared libraries like our caches.
-$(INSTALL_M) $(build_libdir)/libgcc_s.a $(DESTDIR)$(libdir)/
-$(INSTALL_M) $(build_libdir)/libgcc.a $(DESTDIR)$(libdir)/
-$(INSTALL_M) $(build_libdir)/libmsvcrt.a $(DESTDIR)$(libdir)/
else

# Copy over .dSYM directories directly for Darwin
Expand Down Expand Up @@ -333,6 +341,11 @@ else ifeq ($(JULIA_BUILD_MODE),debug)
$(INSTALL_M) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) $(DESTDIR)$(private_libdir)
endif

# Cache stdlibs
@$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no $(JULIAHOME)/contrib/cache_stdlibs.jl)
# CI uses `--check-bounds=yes` which impacts the cache flags
@$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes $(JULIAHOME)/contrib/cache_stdlibs.jl)

# Copy in all .jl sources as well
mkdir -p $(DESTDIR)$(datarootdir)/julia/base $(DESTDIR)$(datarootdir)/julia/test
cp -R -L $(JULIAHOME)/base/* $(DESTDIR)$(datarootdir)/julia/base
Expand Down
1 change: 1 addition & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ include("threadcall.jl")
include("uuid.jl")
include("pkgid.jl")
include("toml_parser.jl")
include("linking.jl")
include("loading.jl")

# misc useful functions & macros
Expand Down
4 changes: 4 additions & 0 deletions base/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ ifeq ($(DARWIN_FRAMEWORK), 1)
@echo "const DARWIN_FRAMEWORK_NAME = \"$(FRAMEWORK_NAME)\"" >> $@
else
@echo "const DARWIN_FRAMEWORK = false" >> $@
endif
ifeq ($(OS), Darwin)
@echo "const MACOS_PRODUCT_VERSION = \"$(shell sw_vers -productVersion)\"" >> $@
@echo "const MACOS_PLATFORM_VERSION = \"$(shell xcrun --show-sdk-version)\"" >> $@
endif
@echo "const BUILD_TRIPLET = \"$(BB_TRIPLET_LIBGFORTRAN_CXXABI)\"" >> $@

Expand Down
147 changes: 147 additions & 0 deletions base/linking.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license
module Linking

import Base.Libc: Libdl

# inlined LLD_jll
# These get calculated in __init__()
const PATH = Ref("")
const LIBPATH = Ref("")
const PATH_list = String[]
const LIBPATH_list = String[]
const lld_path = Ref{String}()
const lld_exe = Sys.iswindows() ? "lld.exe" : "lld"

if Sys.iswindows()
const LIBPATH_env = "PATH"
const LIBPATH_default = ""
const pathsep = ';'
elseif Sys.isapple()
const LIBPATH_env = "DYLD_FALLBACK_LIBRARY_PATH"
const LIBPATH_default = "~/lib:/usr/local/lib:/lib:/usr/lib"
const pathsep = ':'
else
const LIBPATH_env = "LD_LIBRARY_PATH"
const LIBPATH_default = ""
const pathsep = ':'
end

function adjust_ENV!(env::Dict, PATH::String, LIBPATH::String, adjust_PATH::Bool, adjust_LIBPATH::Bool)
if adjust_LIBPATH
LIBPATH_base = get(env, LIBPATH_env, expanduser(LIBPATH_default))
if !isempty(LIBPATH_base)
env[LIBPATH_env] = string(LIBPATH, pathsep, LIBPATH_base)
else
env[LIBPATH_env] = LIBPATH
end
end
if adjust_PATH && (LIBPATH_env != "PATH" || !adjust_LIBPATH)
if !isempty(get(env, "PATH", ""))
env["PATH"] = string(PATH, pathsep, env["PATH"])
else
env["PATH"] = PATH
end
end
return env
end

function __init_lld_path()
# Prefer our own bundled lld, but if we don't have one, pick it up off of the PATH
# If this is an in-tree build, `lld` will live in `tools`. Otherwise, it'll be in `libexec`
for bundled_lld_path in (joinpath(Sys.BINDIR, Base.LIBEXECDIR, lld_exe),
joinpath(Sys.BINDIR, "..", "tools", lld_exe),
joinpath(Sys.BINDIR, lld_exe))
if isfile(bundled_lld_path)
lld_path[] = abspath(bundled_lld_path)
return
end
end
lld_path[] = something(Sys.which(lld_exe), lld_exe)
return
end

const VERBOSE = Ref{Bool}(false)

function __init__()
VERBOSE[] = parse(Bool, get(ENV, "JULIA_VERBOSE_LINKING", "false"))

__init_lld_path()
PATH[] = dirname(lld_path[])
if Sys.iswindows()
# On windows, the dynamic libraries (.dll) are in Sys.BINDIR ("usr\\bin")
append!(LIBPATH_list, [abspath(Sys.BINDIR, Base.LIBDIR, "julia"), Sys.BINDIR])
else
append!(LIBPATH_list, [abspath(Sys.BINDIR, Base.LIBDIR, "julia"), abspath(Sys.BINDIR, Base.LIBDIR)])
end
LIBPATH[] = join(LIBPATH_list, pathsep)
return
end

function lld(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
env = adjust_ENV!(copy(ENV), PATH[], LIBPATH[], adjust_PATH, adjust_LIBPATH)
return Cmd(Cmd([lld_path[]]); env)
end

function ld()
default_args = ``
@static if Sys.iswindows()
# LLD supports mingw style linking
flavor = "gnu"
m = Sys.ARCH == :x86_64 ? "i386pep" : "i386pe"
default_args = `-m $m -Bdynamic --enable-auto-image-base --allow-multiple-definition`
elseif Sys.isapple()
flavor = "darwin"
arch = Sys.ARCH == :aarch64 ? :arm64 : Sys.ARCH
default_args = `-arch $arch -undefined dynamic_lookup -platform_version macos $(Base.MACOS_PRODUCT_VERSION) $(Base.MACOS_PLATFORM_VERSION)`
else
flavor = "gnu"
end

`$(lld()) -flavor $flavor $default_args`
end

const WHOLE_ARCHIVE = if Sys.isapple()
"-all_load"
else
"--whole-archive"
end

const NO_WHOLE_ARCHIVE = if Sys.isapple()
""
else
"--no-whole-archive"
end

const SHARED = if Sys.isapple()
"-dylib"
else
"-shared"
end

is_debug() = ccall(:jl_is_debugbuild, Cint, ()) == 1
libdir() = abspath(Sys.BINDIR, Base.LIBDIR)
private_libdir() = abspath(Sys.BINDIR, Base.PRIVATE_LIBDIR)
if Sys.iswindows()
shlibdir() = Sys.BINDIR
else
shlibdir() = libdir()
end

function link_image_cmd(path, out)
LIBDIR = "-L$(libdir())"
PRIVATE_LIBDIR = "-L$(private_libdir())"
SHLIBDIR = "-L$(shlibdir())"
LIBS = is_debug() ? ("-ljulia-debug", "-ljulia-internal-debug") : ("-ljulia", "-ljulia-internal")
@static if Sys.iswindows()
LIBS = (LIBS..., "-lopenlibm", "-lssp", "-lgcc_s", "-lgcc", "-lmsvcrt")
end

V = VERBOSE[] ? "--verbose" : ""
`$(ld()) $V $SHARED -o $out $WHOLE_ARCHIVE $path $NO_WHOLE_ARCHIVE $LIBDIR $PRIVATE_LIBDIR $SHLIBDIR $LIBS`
end

function link_image(path, out, internal_stderr::IO = stderr, internal_stdout::IO = stdout)
run(link_image_cmd(path, out), Base.DevNull(), stderr, stdout)
end

end # module Linking
Loading

2 comments on commit a2db90f

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily package evaluation, I will reply here when finished:

@nanosoldier runtests(isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your package evaluation job has completed - possible new issues were detected. A full report can be found here.

Please sign in to comment.