Skip to content

Commit ad61241

Browse files
authored
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 1ff98a5 commit ad61241

13 files changed

+237
-185
lines changed

src/aotcompile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2196,7 +2196,7 @@ void jl_dump_native_impl(void *native_code,
21962196
builder.CreateRet(ConstantInt::get(T_int32, 1));
21972197
}
21982198
if (imaging_mode) {
2199-
auto specs = jl_get_llvm_clone_targets();
2199+
auto specs = jl_get_llvm_clone_targets(jl_options.cpu_target);
22002200
const uint32_t base_flags = has_veccall ? JL_TARGET_VEC_CALL : 0;
22012201
SmallVector<uint8_t, 0> data;
22022202
auto push_i32 = [&] (uint32_t v) {

src/init.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -868,21 +868,33 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
868868
{
869869
JL_TIMING(JULIA_INIT, JULIA_INIT);
870870
jl_resolve_sysimg_location(rel);
871+
871872
// loads sysimg if available, and conditionally sets jl_options.cpu_target
873+
jl_image_buf_t sysimage = { JL_IMAGE_KIND_NONE };
872874
if (rel == JL_IMAGE_IN_MEMORY) {
873-
jl_set_sysimg_so(jl_exe_handle);
875+
sysimage = jl_set_sysimg_so(jl_exe_handle);
874876
jl_options.image_file = jl_options.julia_bin;
875877
}
876878
else if (jl_options.image_file)
877-
jl_preload_sysimg_so(jl_options.image_file);
879+
sysimage = jl_preload_sysimg(jl_options.image_file);
880+
881+
if (sysimage.kind == JL_IMAGE_KIND_SO)
882+
jl_gc_notify_image_load(sysimage.data, sysimage.size);
883+
878884
if (jl_options.cpu_target == NULL)
879885
jl_options.cpu_target = "native";
880-
jl_init_codegen();
881886

887+
// Parse image, perform relocations, and init JIT targets, etc.
888+
jl_image_t parsed_image = jl_init_processor_sysimg(sysimage, jl_options.cpu_target);
889+
890+
jl_init_codegen();
882891
jl_init_common_symbols();
883-
if (jl_options.image_file) {
884-
jl_restore_system_image(jl_options.image_file);
892+
893+
if (sysimage.kind != JL_IMAGE_KIND_NONE) {
894+
// Load the .ji or .so sysimage
895+
jl_restore_system_image(&parsed_image, sysimage);
885896
} else {
897+
// No sysimage provided, init a minimal environment
886898
jl_init_types();
887899
jl_global_roots_list = (jl_genericmemory_t*)jl_an_empty_memory_any;
888900
jl_global_roots_keyset = (jl_genericmemory_t*)jl_an_empty_memory_any;
@@ -891,7 +903,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
891903
jl_init_flisp();
892904
jl_init_serializer();
893905

894-
if (!jl_options.image_file) {
906+
if (sysimage.kind == JL_IMAGE_KIND_NONE) {
895907
jl_top_module = jl_core_module;
896908
jl_init_intrinsic_functions();
897909
jl_init_primitives();
@@ -919,7 +931,8 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_
919931

920932
jl_gc_enable(1);
921933

922-
if (jl_options.image_file && (!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
934+
if ((sysimage.kind != JL_IMAGE_KIND_NONE) &&
935+
(!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
923936
jl_array_t *init_order = jl_module_init_order;
924937
JL_GC_PUSH1(&init_order);
925938
jl_module_init_order = NULL;

src/jitlayers.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1416,7 +1416,7 @@ namespace {
14161416
#endif
14171417
#endif
14181418
uint32_t target_flags = 0;
1419-
auto target = jl_get_llvm_target(jl_generating_output(), target_flags);
1419+
auto target = jl_get_llvm_target(jl_options.cpu_target, jl_generating_output(), target_flags);
14201420
auto &TheCPU = target.first;
14211421
SmallVector<std::string, 10> targetFeatures(target.second.begin(), target.second.end());
14221422
std::string errorstr;

src/jl_exported_funcs.inc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@
366366
XX(jl_pointerset) \
367367
XX(jl_pop_handler) \
368368
XX(jl_pop_handler_noexcept) \
369-
XX(jl_preload_sysimg_so) \
369+
XX(jl_preload_sysimg) \
370370
XX(jl_prepend_cwd) \
371371
XX(jl_printf) \
372372
XX(jl_print_backtrace) \
@@ -396,7 +396,6 @@
396396
XX(jl_restore_incremental) \
397397
XX(jl_restore_package_image_from_file) \
398398
XX(jl_restore_system_image) \
399-
XX(jl_restore_system_image_data) \
400399
XX(jl_rethrow) \
401400
XX(jl_rethrow_other) \
402401
XX(jl_running_on_valgrind) \

src/julia.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,6 +2159,25 @@ typedef enum {
21592159
JL_IMAGE_IN_MEMORY = 2
21602160
} JL_IMAGE_SEARCH;
21612161

2162+
typedef enum {
2163+
JL_IMAGE_KIND_NONE = 0,
2164+
JL_IMAGE_KIND_JI,
2165+
JL_IMAGE_KIND_SO,
2166+
} jl_image_kind_t;
2167+
2168+
// A loaded, but unparsed .ji or .so image file
2169+
typedef struct {
2170+
jl_image_kind_t kind;
2171+
void *handle;
2172+
const void *pointers; // jl_image_pointers_t *
2173+
const char *data;
2174+
size_t size;
2175+
uint64_t base;
2176+
} jl_image_buf_t;
2177+
2178+
struct _jl_image_t;
2179+
typedef struct _jl_image_t jl_image_t;
2180+
21622181
JL_DLLIMPORT const char *jl_get_libdir(void);
21632182
JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel);
21642183
JL_DLLEXPORT void jl_init(void);
@@ -2175,11 +2194,10 @@ JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle);
21752194
JL_DLLEXPORT jl_gcframe_t **jl_adopt_thread(void);
21762195

21772196
JL_DLLEXPORT int jl_deserialize_verify_header(ios_t *s);
2178-
JL_DLLEXPORT void jl_preload_sysimg_so(const char *fname);
2179-
JL_DLLEXPORT void jl_set_sysimg_so(void *handle);
2197+
JL_DLLEXPORT jl_image_buf_t jl_preload_sysimg(const char *fname);
2198+
JL_DLLEXPORT jl_image_buf_t jl_set_sysimg_so(void *handle);
21802199
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);
2181-
JL_DLLEXPORT void jl_restore_system_image(const char *fname);
2182-
JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len);
2200+
JL_DLLEXPORT void jl_restore_system_image(jl_image_t *image, jl_image_buf_t buf);
21832201
JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int complete, const char *pkgimage);
21842202
JL_DLLEXPORT jl_value_t *jl_object_top_module(jl_value_t* v) JL_NOTSAFEPOINT;
21852203

src/llvm-multiversioning.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ static void annotate_module_clones(Module &M) {
216216
if (auto maybe_specs = get_target_specs(M)) {
217217
specs = std::move(*maybe_specs);
218218
} else {
219-
auto full_specs = jl_get_llvm_clone_targets();
219+
auto full_specs = jl_get_llvm_clone_targets(jl_options.cpu_target);
220220
specs.reserve(full_specs.size());
221221
for (auto &spec: full_specs) {
222222
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;
@@ -1024,7 +1013,7 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void)
10241013
}
10251014

10261015
extern "C" JL_DLLEXPORT jl_value_t* jl_reflect_clone_targets() {
1027-
auto specs = jl_get_llvm_clone_targets();
1016+
auto specs = jl_get_llvm_clone_targets(jl_options.cpu_target);
10281017
const uint32_t base_flags = 0;
10291018
llvm::SmallVector<uint8_t, 0> data;
10301019
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
@@ -1519,7 +1519,7 @@ static inline void disable_depends(FeatureList<n> &features)
15191519
::disable_depends(features, Feature::deps, sizeof(Feature::deps) / sizeof(FeatureDep));
15201520
}
15211521

1522-
static const llvm::SmallVector<TargetData<feature_sz>, 0> &get_cmdline_targets(void)
1522+
static const llvm::SmallVector<TargetData<feature_sz>, 0> &get_cmdline_targets(const char *cpu_target)
15231523
{
15241524
auto feature_cb = [] (const char *str, size_t len, FeatureList<feature_sz> &list) {
15251525
#ifdef _CPU_AARCH64_
@@ -1536,7 +1536,7 @@ static const llvm::SmallVector<TargetData<feature_sz>, 0> &get_cmdline_targets(v
15361536
set_bit(list, fbit, true);
15371537
return true;
15381538
};
1539-
auto &targets = ::get_cmdline_targets<feature_sz>(feature_cb);
1539+
auto &targets = ::get_cmdline_targets<feature_sz>(cpu_target, feature_cb);
15401540
for (auto &t: targets) {
15411541
if (auto nname = normalize_cpu_name(t.name)) {
15421542
t.name = nname;
@@ -1599,10 +1599,11 @@ static int max_vector_size(const FeatureList<feature_sz> &features)
15991599
#endif
16001600
}
16011601

1602-
static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason)
1602+
static uint32_t sysimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason)
16031603
{
16041604
// First see what target is requested for the JIT.
1605-
auto &cmdline = get_cmdline_targets();
1605+
const char *cpu_target = (const char *)ctx;
1606+
auto &cmdline = get_cmdline_targets(cpu_target);
16061607
TargetData<feature_sz> target = arg_target_data(cmdline[0], true);
16071608
// Then find the best match in the sysimg
16081609
auto sysimg = deserialize_target_data<feature_sz>((const uint8_t*)id);
@@ -1626,7 +1627,7 @@ static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason)
16261627
return match.best_idx;
16271628
}
16281629

1629-
static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason JL_REQUIRE_ROOTED_SLOT)
1630+
static uint32_t pkgimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason JL_REQUIRE_ROOTED_SLOT)
16301631
{
16311632
TargetData<feature_sz> target = jit_targets.front();
16321633
auto pkgimg = deserialize_target_data<feature_sz>((const uint8_t*)id);
@@ -1639,9 +1640,9 @@ static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason JL_
16391640
return match.best_idx;
16401641
}
16411642

1642-
static void ensure_jit_target(bool imaging)
1643+
static void ensure_jit_target(const char *cpu_target, bool imaging)
16431644
{
1644-
auto &cmdline = get_cmdline_targets();
1645+
auto &cmdline = get_cmdline_targets(cpu_target);
16451646
check_cmdline(cmdline, imaging);
16461647
if (!jit_targets.empty())
16471648
return;
@@ -1852,36 +1853,36 @@ JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits)
18521853
#endif
18531854
}
18541855

1855-
jl_image_t jl_init_processor_sysimg(void *hdl)
1856+
jl_image_t jl_init_processor_sysimg(jl_image_buf_t image, const char *cpu_target)
18561857
{
18571858
if (!jit_targets.empty())
18581859
jl_error("JIT targets already initialized");
1859-
return parse_sysimg(hdl, sysimg_init_cb);
1860+
return parse_sysimg(image, sysimg_init_cb, (void *)cpu_target);
18601861
}
18611862

1862-
jl_image_t jl_init_processor_pkgimg(void *hdl)
1863+
jl_image_t jl_init_processor_pkgimg(jl_image_buf_t image)
18631864
{
18641865
if (jit_targets.empty())
18651866
jl_error("JIT targets not initialized");
18661867
if (jit_targets.size() > 1)
18671868
jl_error("Expected only one JIT target");
1868-
return parse_sysimg(hdl, pkgimg_init_cb);
1869+
return parse_sysimg(image, pkgimg_init_cb, NULL);
18691870
}
18701871

18711872
JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data)
18721873
{
18731874
jl_value_t *rejection_reason = NULL;
18741875
JL_GC_PUSH1(&rejection_reason);
1875-
uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason);
1876+
uint32_t match_idx = pkgimg_init_cb(NULL, data, &rejection_reason);
18761877
JL_GC_POP();
18771878
if (match_idx == UINT32_MAX)
18781879
return rejection_reason;
18791880
return jl_nothing;
18801881
}
18811882

1882-
std::pair<std::string,llvm::SmallVector<std::string, 0>> jl_get_llvm_target(bool imaging, uint32_t &flags)
1883+
std::pair<std::string,llvm::SmallVector<std::string, 0>> jl_get_llvm_target(const char *cpu_target, bool imaging, uint32_t &flags)
18831884
{
1884-
ensure_jit_target(imaging);
1885+
ensure_jit_target(cpu_target, imaging);
18851886
flags = jit_targets[0].en.flags;
18861887
return get_llvm_target_vec(jit_targets[0]);
18871888
}
@@ -1900,10 +1901,10 @@ const std::pair<std::string,std::string> &jl_get_llvm_disasm_target(void)
19001901
}
19011902

19021903
#ifndef __clang_gcanalyzer__
1903-
llvm::SmallVector<jl_target_spec_t, 0> jl_get_llvm_clone_targets(void)
1904+
llvm::SmallVector<jl_target_spec_t, 0> jl_get_llvm_clone_targets(const char *cpu_target)
19041905
{
19051906

1906-
auto &cmdline = get_cmdline_targets();
1907+
auto &cmdline = get_cmdline_targets(cpu_target);
19071908
check_cmdline(cmdline, true);
19081909
llvm::SmallVector<TargetData<feature_sz>, 0> image_targets;
19091910
for (auto &arg: cmdline) {

0 commit comments

Comments
 (0)