Skip to content

Commit 4b731bb

Browse files
topolarityxal-0
authored andcommitted
staticdata: Refactor sysimage loading (#57542)
Introduce `jl_image_buf_t` to represent the in-memory details of an unparsed system image and clean-up several global variables and split loading logic in staticdata.c so that we get (almost) everything we need from the sysimage handle at once. This allows sysimage loading to be separated into three phases: 1. Lookup the raw sysimage buffer as a `jl_image_buf_t` 2. For .so sysimages, parse the sysimage and initialize JIT targets, etc. 3. Finally load the sysimage into a `jl_image_t` Care was taken to preserve the existing behavior of calling `jl_set_sysimg_so` to configure the sysimage before initializing Julia, although this is likely to be next on the chopping block in a follow-up. Dependent on #57523.
1 parent d63aded commit 4b731bb

15 files changed

+263
-189
lines changed

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ SRCS := \
4646
simplevector runtime_intrinsics precompile jloptions mtarraylist \
4747
threading scheduler stackwalk gc gc-debug gc-pages gc-stacks gc-alloc-profiler gc-page-profiler method \
4848
jlapi signal-handling safepoint timing subtype rtutils gc-heap-snapshot \
49-
crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage runtime_ccall
49+
crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage runtime_ccall null_sysimage
5050

5151
RT_LLVMLINK :=
5252
CG_LLVMLINK :=

src/aotcompile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1833,7 +1833,7 @@ void jl_dump_native_impl(void *native_code,
18331833
builder.CreateRet(ConstantInt::get(T_int32, 1));
18341834
}
18351835
if (imaging_mode) {
1836-
auto specs = jl_get_llvm_clone_targets();
1836+
auto specs = jl_get_llvm_clone_targets(jl_options.cpu_target);
18371837
const uint32_t base_flags = has_veccall ? JL_TARGET_VEC_CALL : 0;
18381838
SmallVector<uint8_t, 0> data;
18391839
auto push_i32 = [&] (uint32_t v) {

src/init.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -847,19 +847,30 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
847847
{
848848
JL_TIMING(JULIA_INIT, JULIA_INIT);
849849
jl_resolve_sysimg_location(rel);
850+
850851
// loads sysimg if available, and conditionally sets jl_options.cpu_target
851-
if (rel == JL_IMAGE_IN_MEMORY)
852-
jl_set_sysimg_so(jl_exe_handle);
852+
jl_image_buf_t sysimage = { JL_IMAGE_KIND_NONE };
853+
if (rel == JL_IMAGE_IN_MEMORY) {
854+
sysimage = jl_set_sysimg_so(jl_exe_handle);
855+
jl_options.image_file = jl_options.julia_bin;
856+
}
853857
else if (jl_options.image_file)
854-
jl_preload_sysimg_so(jl_options.image_file);
858+
sysimage = jl_preload_sysimg(jl_options.image_file);
859+
855860
if (jl_options.cpu_target == NULL)
856861
jl_options.cpu_target = "native";
857-
jl_init_codegen();
858862

863+
// Parse image, perform relocations, and init JIT targets, etc.
864+
jl_image_t parsed_image = jl_init_processor_sysimg(sysimage, jl_options.cpu_target);
865+
866+
jl_init_codegen();
859867
jl_init_common_symbols();
860-
if (jl_options.image_file) {
861-
jl_restore_system_image(jl_options.image_file);
868+
869+
if (sysimage.kind != JL_IMAGE_KIND_NONE) {
870+
// Load the .ji or .so sysimage
871+
jl_restore_system_image(&parsed_image, sysimage);
862872
} else {
873+
// No sysimage provided, init a minimal environment
863874
jl_init_types();
864875
jl_global_roots_list = (jl_genericmemory_t*)jl_an_empty_memory_any;
865876
jl_global_roots_keyset = (jl_genericmemory_t*)jl_an_empty_memory_any;
@@ -868,7 +879,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
868879
jl_init_flisp();
869880
jl_init_serializer();
870881

871-
if (!jl_options.image_file) {
882+
if (sysimage.kind == JL_IMAGE_KIND_NONE) {
872883
jl_top_module = jl_core_module;
873884
jl_init_intrinsic_functions();
874885
jl_init_primitives();
@@ -892,7 +903,8 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
892903

893904
jl_gc_enable(1);
894905

895-
if (jl_options.image_file && (!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
906+
if ((sysimage.kind != JL_IMAGE_KIND_NONE) &&
907+
(!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
896908
jl_array_t *init_order = jl_module_init_order;
897909
JL_GC_PUSH1(&init_order);
898910
jl_module_init_order = NULL;

src/jitlayers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ namespace {
11391139
options.ExplicitEmulatedTLS = true;
11401140
#endif
11411141
uint32_t target_flags = 0;
1142-
auto target = jl_get_llvm_target(imaging_default(), target_flags);
1142+
auto target = jl_get_llvm_target(jl_options.cpu_target, jl_generating_output(), target_flags);
11431143
auto &TheCPU = target.first;
11441144
SmallVector<std::string, 10> targetFeatures(target.second.begin(), target.second.end());
11451145
std::string errorstr;

src/jl_exported_funcs.inc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@
370370
XX(jl_pointerset) \
371371
XX(jl_pop_handler) \
372372
XX(jl_pop_handler_noexcept) \
373-
XX(jl_preload_sysimg_so) \
373+
XX(jl_preload_sysimg) \
374374
XX(jl_prepend_cwd) \
375375
XX(jl_printf) \
376376
XX(jl_print_backtrace) \
@@ -400,7 +400,6 @@
400400
XX(jl_restore_incremental) \
401401
XX(jl_restore_package_image_from_file) \
402402
XX(jl_restore_system_image) \
403-
XX(jl_restore_system_image_data) \
404403
XX(jl_rethrow) \
405404
XX(jl_rethrow_other) \
406405
XX(jl_running_on_valgrind) \

src/julia.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2030,6 +2030,25 @@ typedef enum {
20302030
JL_IMAGE_IN_MEMORY = 2
20312031
} JL_IMAGE_SEARCH;
20322032

2033+
typedef enum {
2034+
JL_IMAGE_KIND_NONE = 0,
2035+
JL_IMAGE_KIND_JI,
2036+
JL_IMAGE_KIND_SO,
2037+
} jl_image_kind_t;
2038+
2039+
// A loaded, but unparsed .ji or .so image file
2040+
typedef struct {
2041+
jl_image_kind_t kind;
2042+
void *handle;
2043+
const void *pointers; // jl_image_pointers_t *
2044+
const char *data;
2045+
size_t size;
2046+
uint64_t base;
2047+
} jl_image_buf_t;
2048+
2049+
struct _jl_image_t;
2050+
typedef struct _jl_image_t jl_image_t;
2051+
20332052
JL_DLLIMPORT const char *jl_get_libdir(void);
20342053
JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel);
20352054
JL_DLLEXPORT void jl_init(void);
@@ -2046,11 +2065,10 @@ JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle);
20462065
JL_DLLEXPORT jl_gcframe_t **jl_adopt_thread(void);
20472066

20482067
JL_DLLEXPORT int jl_deserialize_verify_header(ios_t *s);
2049-
JL_DLLEXPORT void jl_preload_sysimg_so(const char *fname);
2050-
JL_DLLEXPORT void jl_set_sysimg_so(void *handle);
2068+
JL_DLLEXPORT jl_image_buf_t jl_preload_sysimg(const char *fname);
2069+
JL_DLLEXPORT jl_image_buf_t jl_set_sysimg_so(void *handle);
20512070
JL_DLLEXPORT void jl_create_system_image(void **, jl_array_t *worklist, bool_t emit_split, ios_t **s, ios_t **z, jl_array_t **udeps, int64_t *srctextpos);
2052-
JL_DLLEXPORT void jl_restore_system_image(const char *fname);
2053-
JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len);
2071+
JL_DLLEXPORT void jl_restore_system_image(jl_image_t *image, jl_image_buf_t buf);
20542072
JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int complete, const char *pkgimage);
20552073

20562074
JL_DLLEXPORT void jl_set_newly_inferred(jl_value_t *newly_inferred);

src/llvm-multiversioning.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ static void annotate_module_clones(Module &M) {
215215
if (auto maybe_specs = get_target_specs(M)) {
216216
specs = std::move(*maybe_specs);
217217
} else {
218-
auto full_specs = jl_get_llvm_clone_targets();
218+
auto full_specs = jl_get_llvm_clone_targets(jl_options.cpu_target);
219219
specs.reserve(full_specs.size());
220220
for (auto &spec: full_specs) {
221221
specs.push_back(TargetSpec::fromSpec(spec));

src/null_sysimage.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
#include <stddef.h>
4+
#include "processor.h"
5+
6+
/**
7+
* These symbols support statically linking the sysimage with libjulia-internal.
8+
*
9+
* Here we provide dummy definitions that are used when these are not linked
10+
* together (the default build configuration). The 0 value of jl_system_image_size
11+
* is used as a sentinel to indicate that the sysimage should be loaded externally.
12+
**/
13+
char jl_system_image_data = 0;
14+
size_t jl_system_image_size = 0;
15+
jl_image_pointers_t jl_image_pointers = { 0 };

src/processor.cpp

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ static inline llvm::SmallVector<TargetData<n>, 0>
504504
parse_cmdline(const char *option, F &&feature_cb)
505505
{
506506
if (!option)
507-
option = "native";
507+
abort();
508+
508509
llvm::SmallVector<TargetData<n>, 0> res;
509510
TargetData<n> arg{};
510511
auto reset_arg = [&] {
@@ -612,36 +613,29 @@ parse_cmdline(const char *option, F &&feature_cb)
612613

613614
// Cached version of command line parsing
614615
template<size_t n, typename F>
615-
static inline llvm::SmallVector<TargetData<n>, 0> &get_cmdline_targets(F &&feature_cb)
616+
static inline llvm::SmallVector<TargetData<n>, 0> &get_cmdline_targets(const char *cpu_target, F &&feature_cb)
616617
{
617618
static llvm::SmallVector<TargetData<n>, 0> targets =
618-
parse_cmdline<n>(jl_options.cpu_target, std::forward<F>(feature_cb));
619+
parse_cmdline<n>(cpu_target, std::forward<F>(feature_cb));
619620
return targets;
620621
}
621622

622-
extern "C" {
623-
void *image_pointers_unavailable;
624-
extern void * JL_WEAK_SYMBOL_OR_ALIAS_DEFAULT(image_pointers_unavailable) jl_image_pointers;
625-
}
626-
627623
// Load sysimg, use the `callback` for dispatch and perform all relocations
628624
// for the selected target.
629625
template<typename F>
630-
static inline jl_image_t parse_sysimg(void *hdl, F &&callback)
626+
static inline jl_image_t parse_sysimg(jl_image_buf_t image, F &&callback, void *ctx)
631627
{
632628
JL_TIMING(LOAD_IMAGE, LOAD_Processor);
633629
jl_image_t res{};
634630

635-
const jl_image_pointers_t *pointers;
636-
if (hdl == jl_exe_handle && &jl_image_pointers != JL_WEAK_SYMBOL_DEFAULT(image_pointers_unavailable))
637-
pointers = (const jl_image_pointers_t *)&jl_image_pointers;
638-
else
639-
jl_dlsym(hdl, "jl_image_pointers", (void**)&pointers, 1);
631+
if (image.kind != JL_IMAGE_KIND_SO)
632+
return res;
640633

634+
const jl_image_pointers_t *pointers = (const jl_image_pointers_t *)image.pointers;
641635
const void *ids = pointers->target_data;
642636
jl_value_t* rejection_reason = nullptr;
643637
JL_GC_PUSH1(&rejection_reason);
644-
uint32_t target_idx = callback(ids, &rejection_reason);
638+
uint32_t target_idx = callback(ctx, ids, &rejection_reason);
645639
if (target_idx == UINT32_MAX) {
646640
jl_error(jl_string_ptr(rejection_reason));
647641
}
@@ -799,17 +793,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback)
799793
res.fptrs.nclones = clones.size();
800794
}
801795

802-
#ifdef _OS_WINDOWS_
803-
res.base = (intptr_t)hdl;
804-
#else
805-
Dl_info dlinfo;
806-
if (dladdr((void*)pointers, &dlinfo) != 0) {
807-
res.base = (intptr_t)dlinfo.dli_fbase;
808-
}
809-
else {
810-
res.base = 0;
811-
}
812-
#endif
796+
res.base = image.base;
813797

814798
{
815799
void *pgcstack_func_slot = pointers->ptls->pgcstack_func_slot;
@@ -1025,7 +1009,7 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void)
10251009
}
10261010

10271011
extern "C" JL_DLLEXPORT jl_value_t* jl_reflect_clone_targets() {
1028-
auto specs = jl_get_llvm_clone_targets();
1012+
auto specs = jl_get_llvm_clone_targets(jl_options.cpu_target);
10291013
const uint32_t base_flags = 0;
10301014
llvm::SmallVector<uint8_t, 0> data;
10311015
auto push_i32 = [&] (uint32_t v) {

src/processor.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ JL_DLLEXPORT int jl_test_cpu_feature(jl_cpu_feature_t feature);
6464
static const uint32_t jl_sysimg_tag_mask = 0x80000000u;
6565
static const uint32_t jl_sysimg_val_mask = ~((uint32_t)0x80000000u);
6666

67+
// A parsed image file
6768
typedef struct _jl_image_fptrs_t {
6869
// number of functions
6970
uint32_t nptrs;
@@ -82,14 +83,14 @@ typedef struct _jl_image_fptrs_t {
8283
const uint32_t *clone_idxs;
8384
} jl_image_fptrs_t;
8485

85-
typedef struct {
86+
struct _jl_image_t {
8687
uint64_t base;
8788
const char *gvars_base;
8889
const int32_t *gvars_offsets;
8990
uint32_t ngvars;
9091
jl_image_fptrs_t fptrs;
9192
void **jl_small_typeof;
92-
} jl_image_t;
93+
};
9394

9495
// The header for each image
9596
// Details important counts about the image
@@ -206,8 +207,8 @@ typedef struct {
206207
*
207208
* Return the data about the function pointers selected.
208209
*/
209-
jl_image_t jl_init_processor_sysimg(void *hdl);
210-
jl_image_t jl_init_processor_pkgimg(void *hdl);
210+
jl_image_t jl_init_processor_sysimg(jl_image_buf_t image, const char *cpu_target);
211+
jl_image_t jl_init_processor_pkgimg(jl_image_buf_t image);
211212

212213
// Return the name of the host CPU as a julia string.
213214
JL_DLLEXPORT jl_value_t *jl_get_cpu_name(void);
@@ -224,6 +225,18 @@ JL_DLLEXPORT int32_t jl_set_zero_subnormals(int8_t isZero);
224225
JL_DLLEXPORT int32_t jl_get_zero_subnormals(void);
225226
JL_DLLEXPORT int32_t jl_set_default_nans(int8_t isDefault);
226227
JL_DLLEXPORT int32_t jl_get_default_nans(void);
228+
229+
/**
230+
* System image contents.
231+
*
232+
* These symbols are typically dummy values, unless statically linking
233+
* libjulia-* and the sysimage together (see null_sysimage.c), in which
234+
* case they allow accessing the local copy of the sysimage.
235+
**/
236+
extern char jl_system_image_data;
237+
extern size_t jl_system_image_size;
238+
extern jl_image_pointers_t jl_image_pointers;
239+
227240
#ifdef __cplusplus
228241
}
229242

@@ -239,7 +252,7 @@ extern JL_DLLEXPORT bool jl_processor_print_help;
239252
* If the detected/specified CPU name is not available on the LLVM version specified,
240253
* a fallback CPU name will be used. Unsupported features will be ignored.
241254
*/
242-
extern "C" JL_DLLEXPORT std::pair<std::string,llvm::SmallVector<std::string, 0>> jl_get_llvm_target(bool imaging, uint32_t &flags) JL_NOTSAFEPOINT;
255+
extern "C" JL_DLLEXPORT std::pair<std::string,llvm::SmallVector<std::string, 0>> jl_get_llvm_target(const char *cpu_target, bool imaging, uint32_t &flags) JL_NOTSAFEPOINT;
243256

244257
/**
245258
* Returns the CPU name and feature string to be used by LLVM disassembler.
@@ -263,7 +276,7 @@ struct jl_target_spec_t {
263276
/**
264277
* Return the list of targets to clone
265278
*/
266-
extern "C" JL_DLLEXPORT llvm::SmallVector<jl_target_spec_t, 0> jl_get_llvm_clone_targets(void) JL_NOTSAFEPOINT;
279+
extern "C" JL_DLLEXPORT llvm::SmallVector<jl_target_spec_t, 0> jl_get_llvm_clone_targets(const char *cpu_target) JL_NOTSAFEPOINT;
267280
// NOLINTEND(clang-diagnostic-return-type-c-linkage)
268281
struct FeatureName {
269282
const char *name;

0 commit comments

Comments
 (0)