Skip to content

Commit 9a592dd

Browse files
vchuravytimholyKristofferCgiordanoIanButterworth
authored and
KristofferC
committed
Implement support for object caching through pkgimages (#47184)
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 <[email protected]> Co-authored-by: Kristoffer Carlsson <[email protected]> Co-authored-by: Mosè Giordano <[email protected]> Co-authored-by: Ian Butterworth <[email protected]> Co-authored-by: Max Horn <[email protected]> Co-authored-by: Michael Schlottke-Lakemper <[email protected]> Co-authored-by: Alex Ames <[email protected]> (cherry picked from commit a2db90f)
1 parent 22789c0 commit 9a592dd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1751
-764
lines changed

Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,21 @@ ifeq ($(OS),WINNT)
247247
-$(INSTALL_M) $(wildcard $(build_bindir)/*.dll) $(DESTDIR)$(bindir)/
248248
ifeq ($(JULIA_BUILD_MODE),release)
249249
-$(INSTALL_M) $(build_libdir)/libjulia.dll.a $(DESTDIR)$(libdir)/
250+
-$(INSTALL_M) $(build_libdir)/libjulia-internal.dll.a $(DESTDIR)$(libdir)/
250251
else ifeq ($(JULIA_BUILD_MODE),debug)
251252
-$(INSTALL_M) $(build_libdir)/libjulia-debug.dll.a $(DESTDIR)$(libdir)/
253+
-$(INSTALL_M) $(build_libdir)/libjulia-internal-debug.dll.a $(DESTDIR)$(libdir)/
252254
endif
253255

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

259267
# Copy over .dSYM directories directly for Darwin
@@ -318,6 +326,11 @@ else ifeq ($(JULIA_BUILD_MODE),debug)
318326
$(INSTALL_M) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) $(DESTDIR)$(private_libdir)
319327
endif
320328

329+
# Cache stdlibs
330+
@$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no $(JULIAHOME)/contrib/cache_stdlibs.jl)
331+
# CI uses `--check-bounds=yes` which impacts the cache flags
332+
@$(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes $(JULIAHOME)/contrib/cache_stdlibs.jl)
333+
321334
# Copy in all .jl sources as well
322335
mkdir -p $(DESTDIR)$(datarootdir)/julia/base $(DESTDIR)$(datarootdir)/julia/test
323336
cp -R -L $(JULIAHOME)/base/* $(DESTDIR)$(datarootdir)/julia/base

base/Base.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ include("threadcall.jl")
418418
include("uuid.jl")
419419
include("pkgid.jl")
420420
include("toml_parser.jl")
421+
include("linking.jl")
421422
include("loading.jl")
422423

423424
# misc useful functions & macros

base/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ ifeq ($(DARWIN_FRAMEWORK), 1)
8181
@echo "const DARWIN_FRAMEWORK_NAME = \"$(FRAMEWORK_NAME)\"" >> $@
8282
else
8383
@echo "const DARWIN_FRAMEWORK = false" >> $@
84+
endif
85+
ifeq ($(OS), Darwin)
86+
@echo "const MACOS_PRODUCT_VERSION = \"$(shell sw_vers -productVersion)\"" >> $@
87+
@echo "const MACOS_PLATFORM_VERSION = \"$(shell xcrun --show-sdk-version)\"" >> $@
8488
endif
8589
@echo "const BUILD_TRIPLET = \"$(BB_TRIPLET_LIBGFORTRAN_CXXABI)\"" >> $@
8690

base/linking.jl

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# This file is a part of Julia. License is MIT: https://julialang.org/license
2+
module Linking
3+
4+
import Base.Libc: Libdl
5+
6+
# inlined LLD_jll
7+
# These get calculated in __init__()
8+
const PATH = Ref("")
9+
const LIBPATH = Ref("")
10+
const PATH_list = String[]
11+
const LIBPATH_list = String[]
12+
const lld_path = Ref{String}()
13+
const lld_exe = Sys.iswindows() ? "lld.exe" : "lld"
14+
15+
if Sys.iswindows()
16+
const LIBPATH_env = "PATH"
17+
const LIBPATH_default = ""
18+
const pathsep = ';'
19+
elseif Sys.isapple()
20+
const LIBPATH_env = "DYLD_FALLBACK_LIBRARY_PATH"
21+
const LIBPATH_default = "~/lib:/usr/local/lib:/lib:/usr/lib"
22+
const pathsep = ':'
23+
else
24+
const LIBPATH_env = "LD_LIBRARY_PATH"
25+
const LIBPATH_default = ""
26+
const pathsep = ':'
27+
end
28+
29+
function adjust_ENV!(env::Dict, PATH::String, LIBPATH::String, adjust_PATH::Bool, adjust_LIBPATH::Bool)
30+
if adjust_LIBPATH
31+
LIBPATH_base = get(env, LIBPATH_env, expanduser(LIBPATH_default))
32+
if !isempty(LIBPATH_base)
33+
env[LIBPATH_env] = string(LIBPATH, pathsep, LIBPATH_base)
34+
else
35+
env[LIBPATH_env] = LIBPATH
36+
end
37+
end
38+
if adjust_PATH && (LIBPATH_env != "PATH" || !adjust_LIBPATH)
39+
if !isempty(get(env, "PATH", ""))
40+
env["PATH"] = string(PATH, pathsep, env["PATH"])
41+
else
42+
env["PATH"] = PATH
43+
end
44+
end
45+
return env
46+
end
47+
48+
function __init_lld_path()
49+
# Prefer our own bundled lld, but if we don't have one, pick it up off of the PATH
50+
# If this is an in-tree build, `lld` will live in `tools`. Otherwise, it'll be in `libexec`
51+
for bundled_lld_path in (joinpath(Sys.BINDIR, Base.LIBEXECDIR, lld_exe),
52+
joinpath(Sys.BINDIR, "..", "tools", lld_exe),
53+
joinpath(Sys.BINDIR, lld_exe))
54+
if isfile(bundled_lld_path)
55+
lld_path[] = abspath(bundled_lld_path)
56+
return
57+
end
58+
end
59+
lld_path[] = something(Sys.which(lld_exe), lld_exe)
60+
return
61+
end
62+
63+
const VERBOSE = Ref{Bool}(false)
64+
65+
function __init__()
66+
VERBOSE[] = parse(Bool, get(ENV, "JULIA_VERBOSE_LINKING", "false"))
67+
68+
__init_lld_path()
69+
PATH[] = dirname(lld_path[])
70+
if Sys.iswindows()
71+
# On windows, the dynamic libraries (.dll) are in Sys.BINDIR ("usr\\bin")
72+
append!(LIBPATH_list, [abspath(Sys.BINDIR, Base.LIBDIR, "julia"), Sys.BINDIR])
73+
else
74+
append!(LIBPATH_list, [abspath(Sys.BINDIR, Base.LIBDIR, "julia"), abspath(Sys.BINDIR, Base.LIBDIR)])
75+
end
76+
LIBPATH[] = join(LIBPATH_list, pathsep)
77+
return
78+
end
79+
80+
function lld(; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true)
81+
env = adjust_ENV!(copy(ENV), PATH[], LIBPATH[], adjust_PATH, adjust_LIBPATH)
82+
return Cmd(Cmd([lld_path[]]); env)
83+
end
84+
85+
function ld()
86+
default_args = ``
87+
@static if Sys.iswindows()
88+
# LLD supports mingw style linking
89+
flavor = "gnu"
90+
m = Sys.ARCH == :x86_64 ? "i386pep" : "i386pe"
91+
default_args = `-m $m -Bdynamic --enable-auto-image-base --allow-multiple-definition`
92+
elseif Sys.isapple()
93+
flavor = "darwin"
94+
arch = Sys.ARCH == :aarch64 ? :arm64 : Sys.ARCH
95+
default_args = `-arch $arch -undefined dynamic_lookup -platform_version macos $(Base.MACOS_PRODUCT_VERSION) $(Base.MACOS_PLATFORM_VERSION)`
96+
else
97+
flavor = "gnu"
98+
end
99+
100+
`$(lld()) -flavor $flavor $default_args`
101+
end
102+
103+
const WHOLE_ARCHIVE = if Sys.isapple()
104+
"-all_load"
105+
else
106+
"--whole-archive"
107+
end
108+
109+
const NO_WHOLE_ARCHIVE = if Sys.isapple()
110+
""
111+
else
112+
"--no-whole-archive"
113+
end
114+
115+
const SHARED = if Sys.isapple()
116+
"-dylib"
117+
else
118+
"-shared"
119+
end
120+
121+
is_debug() = ccall(:jl_is_debugbuild, Cint, ()) == 1
122+
libdir() = abspath(Sys.BINDIR, Base.LIBDIR)
123+
private_libdir() = abspath(Sys.BINDIR, Base.PRIVATE_LIBDIR)
124+
if Sys.iswindows()
125+
shlibdir() = Sys.BINDIR
126+
else
127+
shlibdir() = libdir()
128+
end
129+
130+
function link_image_cmd(path, out)
131+
LIBDIR = "-L$(libdir())"
132+
PRIVATE_LIBDIR = "-L$(private_libdir())"
133+
SHLIBDIR = "-L$(shlibdir())"
134+
LIBS = is_debug() ? ("-ljulia-debug", "-ljulia-internal-debug") : ("-ljulia", "-ljulia-internal")
135+
@static if Sys.iswindows()
136+
LIBS = (LIBS..., "-lopenlibm", "-lssp", "-lgcc_s", "-lgcc", "-lmsvcrt")
137+
end
138+
139+
V = VERBOSE[] ? "--verbose" : ""
140+
`$(ld()) $V $SHARED -o $out $WHOLE_ARCHIVE $path $NO_WHOLE_ARCHIVE $LIBDIR $PRIVATE_LIBDIR $SHLIBDIR $LIBS`
141+
end
142+
143+
function link_image(path, out, internal_stderr::IO = stderr, internal_stdout::IO = stdout)
144+
run(link_image_cmd(path, out), Base.DevNull(), stderr, stdout)
145+
end
146+
147+
end # module Linking

0 commit comments

Comments
 (0)