Skip to content

Commit 8150fe2

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 a589d88 commit 8150fe2

13 files changed

+235
-184
lines changed

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/processor.cpp

Lines changed: 11 additions & 22 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,31 +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

622623
// Load sysimg, use the `callback` for dispatch and perform all relocations
623624
// for the selected target.
624625
template<typename F>
625-
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)
626627
{
627628
JL_TIMING(LOAD_IMAGE, LOAD_Processor);
628629
jl_image_t res{};
629630

630-
const jl_image_pointers_t *pointers;
631-
if (jl_system_image_size == 0)
632-
jl_dlsym(hdl, "jl_image_pointers", (void**)&pointers, 1);
633-
else
634-
pointers = &jl_image_pointers; // libjulia-internal and sysimage statically linked
631+
if (image.kind != JL_IMAGE_KIND_SO)
632+
return res;
635633

634+
const jl_image_pointers_t *pointers = (const jl_image_pointers_t *)image.pointers;
636635
const void *ids = pointers->target_data;
637636
jl_value_t* rejection_reason = nullptr;
638637
JL_GC_PUSH1(&rejection_reason);
639-
uint32_t target_idx = callback(ids, &rejection_reason);
638+
uint32_t target_idx = callback(ctx, ids, &rejection_reason);
640639
if (target_idx == UINT32_MAX) {
641640
jl_error(jl_string_ptr(rejection_reason));
642641
}
@@ -794,17 +793,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback)
794793
res.fptrs.nclones = clones.size();
795794
}
796795

797-
#ifdef _OS_WINDOWS_
798-
res.base = (intptr_t)hdl;
799-
#else
800-
Dl_info dlinfo;
801-
if (dladdr((void*)pointers, &dlinfo) != 0) {
802-
res.base = (intptr_t)dlinfo.dli_fbase;
803-
}
804-
else {
805-
res.base = 0;
806-
}
807-
#endif
796+
res.base = image.base;
808797

809798
{
810799
void *pgcstack_func_slot = pointers->ptls->pgcstack_func_slot;
@@ -1020,7 +1009,7 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void)
10201009
}
10211010

10221011
extern "C" JL_DLLEXPORT jl_value_t* jl_reflect_clone_targets() {
1023-
auto specs = jl_get_llvm_clone_targets();
1012+
auto specs = jl_get_llvm_clone_targets(jl_options.cpu_target);
10241013
const uint32_t base_flags = 0;
10251014
llvm::SmallVector<uint8_t, 0> data;
10261015
auto push_i32 = [&] (uint32_t v) {

src/processor.h

Lines changed: 7 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);
@@ -251,7 +252,7 @@ extern JL_DLLEXPORT bool jl_processor_print_help;
251252
* If the detected/specified CPU name is not available on the LLVM version specified,
252253
* a fallback CPU name will be used. Unsupported features will be ignored.
253254
*/
254-
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;
255256

256257
/**
257258
* Returns the CPU name and feature string to be used by LLVM disassembler.
@@ -275,7 +276,7 @@ struct jl_target_spec_t {
275276
/**
276277
* Return the list of targets to clone
277278
*/
278-
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;
279280
// NOLINTEND(clang-diagnostic-return-type-c-linkage)
280281
struct FeatureName {
281282
const char *name;

src/processor_arm.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,7 @@ static inline void disable_depends(FeatureList<n> &features)
15101510
::disable_depends(features, Feature::deps, sizeof(Feature::deps) / sizeof(FeatureDep));
15111511
}
15121512

1513-
static const llvm::SmallVector<TargetData<feature_sz>, 0> &get_cmdline_targets(void)
1513+
static const llvm::SmallVector<TargetData<feature_sz>, 0> &get_cmdline_targets(const char *cpu_target)
15141514
{
15151515
auto feature_cb = [] (const char *str, size_t len, FeatureList<feature_sz> &list) {
15161516
#ifdef _CPU_AARCH64_
@@ -1527,7 +1527,7 @@ static const llvm::SmallVector<TargetData<feature_sz>, 0> &get_cmdline_targets(v
15271527
set_bit(list, fbit, true);
15281528
return true;
15291529
};
1530-
auto &targets = ::get_cmdline_targets<feature_sz>(feature_cb);
1530+
auto &targets = ::get_cmdline_targets<feature_sz>(cpu_target, feature_cb);
15311531
for (auto &t: targets) {
15321532
if (auto nname = normalize_cpu_name(t.name)) {
15331533
t.name = nname;
@@ -1590,10 +1590,11 @@ static int max_vector_size(const FeatureList<feature_sz> &features)
15901590
#endif
15911591
}
15921592

1593-
static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason)
1593+
static uint32_t sysimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason)
15941594
{
15951595
// First see what target is requested for the JIT.
1596-
auto &cmdline = get_cmdline_targets();
1596+
const char *cpu_target = (const char *)ctx;
1597+
auto &cmdline = get_cmdline_targets(cpu_target);
15971598
TargetData<feature_sz> target = arg_target_data(cmdline[0], true);
15981599
// Then find the best match in the sysimg
15991600
auto sysimg = deserialize_target_data<feature_sz>((const uint8_t*)id);
@@ -1617,7 +1618,7 @@ static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason)
16171618
return match.best_idx;
16181619
}
16191620

1620-
static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason JL_REQUIRE_ROOTED_SLOT)
1621+
static uint32_t pkgimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason JL_REQUIRE_ROOTED_SLOT)
16211622
{
16221623
TargetData<feature_sz> target = jit_targets.front();
16231624
auto pkgimg = deserialize_target_data<feature_sz>((const uint8_t*)id);
@@ -1630,9 +1631,9 @@ static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason JL_
16301631
return match.best_idx;
16311632
}
16321633

1633-
static void ensure_jit_target(bool imaging)
1634+
static void ensure_jit_target(const char *cpu_target, bool imaging)
16341635
{
1635-
auto &cmdline = get_cmdline_targets();
1636+
auto &cmdline = get_cmdline_targets(cpu_target);
16361637
check_cmdline(cmdline, imaging);
16371638
if (!jit_targets.empty())
16381639
return;
@@ -1843,36 +1844,36 @@ JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits)
18431844
#endif
18441845
}
18451846

1846-
jl_image_t jl_init_processor_sysimg(void *hdl)
1847+
jl_image_t jl_init_processor_sysimg(jl_image_buf_t image, const char *cpu_target)
18471848
{
18481849
if (!jit_targets.empty())
18491850
jl_error("JIT targets already initialized");
1850-
return parse_sysimg(hdl, sysimg_init_cb);
1851+
return parse_sysimg(image, sysimg_init_cb, (void *)cpu_target);
18511852
}
18521853

1853-
jl_image_t jl_init_processor_pkgimg(void *hdl)
1854+
jl_image_t jl_init_processor_pkgimg(jl_image_buf_t image)
18541855
{
18551856
if (jit_targets.empty())
18561857
jl_error("JIT targets not initialized");
18571858
if (jit_targets.size() > 1)
18581859
jl_error("Expected only one JIT target");
1859-
return parse_sysimg(hdl, pkgimg_init_cb);
1860+
return parse_sysimg(image, pkgimg_init_cb, NULL);
18601861
}
18611862

18621863
JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data)
18631864
{
18641865
jl_value_t *rejection_reason = NULL;
18651866
JL_GC_PUSH1(&rejection_reason);
1866-
uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason);
1867+
uint32_t match_idx = pkgimg_init_cb(NULL, data, &rejection_reason);
18671868
JL_GC_POP();
18681869
if (match_idx == UINT32_MAX)
18691870
return rejection_reason;
18701871
return jl_nothing;
18711872
}
18721873

1873-
std::pair<std::string,llvm::SmallVector<std::string, 0>> jl_get_llvm_target(bool imaging, uint32_t &flags)
1874+
std::pair<std::string,llvm::SmallVector<std::string, 0>> jl_get_llvm_target(const char *cpu_target, bool imaging, uint32_t &flags)
18741875
{
1875-
ensure_jit_target(imaging);
1876+
ensure_jit_target(cpu_target, imaging);
18761877
flags = jit_targets[0].en.flags;
18771878
return get_llvm_target_vec(jit_targets[0]);
18781879
}
@@ -1891,10 +1892,10 @@ const std::pair<std::string,std::string> &jl_get_llvm_disasm_target(void)
18911892
}
18921893

18931894
#ifndef __clang_gcanalyzer__
1894-
llvm::SmallVector<jl_target_spec_t, 0> jl_get_llvm_clone_targets(void)
1895+
llvm::SmallVector<jl_target_spec_t, 0> jl_get_llvm_clone_targets(const char *cpu_target)
18951896
{
18961897

1897-
auto &cmdline = get_cmdline_targets();
1898+
auto &cmdline = get_cmdline_targets(cpu_target);
18981899
check_cmdline(cmdline, true);
18991900
llvm::SmallVector<TargetData<feature_sz>, 0> image_targets;
19001901
for (auto &arg: cmdline) {

0 commit comments

Comments
 (0)