Skip to content

Commit 3c31a06

Browse files
timholyvtjnash
authored andcommitted
Encode link_id in tagged linkage
On 64-bit, we have enough space to encode (1) the tag, (2) the `depmods` index, and (3) the offset all in a single 64-bit pointer field. This means we don't need the external `link_id` arrays, which reduces the size of many pkgimages by ~5%. On 32-bit, we don't have enough bits to implement this strategy. However, most linkages seem to be against the sysimage, and so by giving that a separate tag we can achieve similar compression because the `link_id` lists will be much shorter.
1 parent 49b361a commit 3c31a06

File tree

2 files changed

+145
-25
lines changed

2 files changed

+145
-25
lines changed

src/staticdata.c

Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,12 @@ typedef struct {
357357
arraylist_t fixup_types; // a list of locations of types requiring (re)caching
358358
arraylist_t fixup_objs; // a list of locations of objects requiring (re)caching
359359
arraylist_t ccallable_list; // @ccallable entry points to install
360+
#ifdef _P64
361+
// On 64-bit, we can encode the "`depmods` index" in the reftag, so all we need is a mapping between
362+
// the buildid_idx & depmods_idx:
363+
jl_array_t *buildid_depmods_idxs;
364+
// On 64bit, all the `link_ids_*` arrays below will be NULL.
365+
#endif
360366
// record of build_ids for all external linkages, in order of serialization for the current sysimg/pkgimg
361367
// conceptually, the base pointer for the jth externally-linked item is determined from
362368
// i = findfirst(==(link_ids[j]), jl_build_ids)
@@ -387,7 +393,8 @@ enum RefTags {
387393
SymbolRef, // symbols
388394
FunctionRef, // generic functions
389395
BuiltinFunctionRef, // builtin functions
390-
ExternalLinkage // items defined externally (used when serializing packages)
396+
SysimageLinkage, // reference to the sysimage (from pkgimage)
397+
ExternalLinkage // reference to some other pkgimage
391398
};
392399

393400
// calling conventions for internal entry points.
@@ -405,9 +412,11 @@ typedef enum {
405412

406413
#ifdef _P64
407414
#define RELOC_TAG_OFFSET 61
415+
#define DEPS_IDX_OFFSET 40 // only on 64-bit can we encode the dependency-index as part of the tagged reloc
408416
#else
409417
// this supports up to 8 RefTags, 512MB of pointer data, and 4/2 (64/32-bit) GB of constant data.
410418
#define RELOC_TAG_OFFSET 29
419+
#define DEPS_IDX_OFFSET RELOC_TAG_OFFSET
411420
#endif
412421

413422
#if RELOC_TAG_OFFSET <= 32
@@ -759,7 +768,7 @@ done_fields: ;
759768
}
760769
arraylist_push(&serialization_queue, (void*) v);
761770
size_t idx = serialization_queue.len - 1;
762-
assert(serialization_queue.len < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "too many items to serialize");
771+
assert(serialization_queue.len < ((uintptr_t)1 << DEPS_IDX_OFFSET) && "too many items to serialize");
763772

764773
*bp = (void*)((char*)HT_NOTFOUND + 1 + idx);
765774
}
@@ -864,29 +873,40 @@ static void write_pointer(ios_t *s) JL_NOTSAFEPOINT
864873
static uintptr_t add_external_linkage(jl_serializer_state *s, jl_value_t *v, jl_array_t *link_ids) {
865874
size_t i = external_blob_index(v);
866875
if (i < n_linkage_blobs()) {
876+
// We found the sysimg/pkg that this item links against
877+
// Compute the relocation code
878+
size_t offset = (uintptr_t)v - (uintptr_t)jl_linkage_blobs.items[2*i];
879+
offset /= sizeof(void*);
880+
assert(offset < ((uintptr_t)1 << DEPS_IDX_OFFSET) && "offset to external image too large");
881+
882+
if (i == 0)
883+
return ((uintptr_t)SysimageLinkage << RELOC_TAG_OFFSET) + offset; // sysimage
867884
assert(link_ids && jl_is_array(link_ids));
885+
#ifdef _P64
886+
uint64_t *link_id_data = (uint64_t*)jl_array_data(link_ids);
887+
return ((uintptr_t)ExternalLinkage << RELOC_TAG_OFFSET)
888+
+ (((uintptr_t)link_id_data[i]) << DEPS_IDX_OFFSET) + offset; // on 64-bit, link_ids stores the mapping from build_id_idx -> deps_idx
889+
#else
890+
// On 32bit, we store the image key in `link_ids`
868891
assert(jl_build_ids && jl_is_array(jl_build_ids));
869892
uint64_t *build_id_data = (uint64_t*)jl_array_data(jl_build_ids);
870-
// We found the sysimg/pkg that this item links against
871-
// Store the image key in `link_ids`
872893
jl_array_grow_end(link_ids, 1);
873-
uint64_t *link_id_data = (uint64_t*)jl_array_data(link_ids);
894+
uint64_t *link_id_data = (uint64_t*)jl_array_data(link_ids); // wait until after the `grow`
874895
link_id_data[jl_array_len(link_ids)-1] = build_id_data[i];
875-
// Compute the relocation code
876-
size_t offset = (uintptr_t)v - (uintptr_t)jl_linkage_blobs.items[2*i];
877-
offset /= sizeof(void*);
878-
assert(offset < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "offset to external image too large");
879-
// jl_printf(JL_STDOUT, "External link %ld against blob %d with key %ld at position 0x%lx with offset 0x%lx to \n", jl_array_len(link_ids), i, build_id_data[i>>1], ios_pos(s->s), offset);
880-
// jl_(v);
881896
return ((uintptr_t)ExternalLinkage << RELOC_TAG_OFFSET) + offset;
897+
#endif
882898
}
883899
return 0;
884900
}
885901

886902
// Return the integer `id` for `v`. Generically this is looked up in `serialization_order`,
887903
// but symbols, small integers, and a couple of special items (`nothing` and the root Task)
888904
// have special handling.
905+
#ifdef _P64
906+
#define backref_id(s, v, link_ids) _backref_id(s, (jl_value_t*)(v), s->buildid_depmods_idxs)
907+
#else
889908
#define backref_id(s, v, link_ids) _backref_id(s, (jl_value_t*)(v), link_ids)
909+
#endif
890910
static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t *link_ids) JL_NOTSAFEPOINT
891911
{
892912
assert(v != NULL && "cannot get backref to NULL object");
@@ -899,7 +919,7 @@ static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t *
899919
write_uint32(s->symbols, l);
900920
ios_write(s->symbols, jl_symbol_name((jl_sym_t*)v), l + 1);
901921
size_t offset = ++nsym_tag;
902-
assert(offset < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "too many symbols");
922+
assert(offset < ((uintptr_t)1 << DEPS_IDX_OFFSET) && "too many symbols");
903923
idx = (void*)((char*)HT_NOTFOUND + ((uintptr_t)SymbolRef << RELOC_TAG_OFFSET) + offset);
904924
*pidx = idx;
905925
}
@@ -1144,7 +1164,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED
11441164
// write data and relocations
11451165
newa->data = NULL; // relocation offset
11461166
data /= sizeof(void*);
1147-
assert(data < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "offset to constant data too large");
1167+
assert(data < ((uintptr_t)1 << DEPS_IDX_OFFSET) && "offset to constant data too large");
11481168
arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_array_t, data))); // relocation location
11491169
arraylist_push(&s->relocs_list, (void*)(((uintptr_t)ConstDataRef << RELOC_TAG_OFFSET) + data)); // relocation target
11501170
if (jl_is_cpointer_type(et)) {
@@ -1247,7 +1267,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED
12471267
uintptr_t data = LLT_ALIGN(ios_pos(s->const_data), 8);
12481268
write_padding(s->const_data, data - ios_pos(s->const_data));
12491269
data /= sizeof(void*);
1250-
assert(data < ((uintptr_t)1 << RELOC_TAG_OFFSET) && "offset to constant data too large");
1270+
assert(data < ((uintptr_t)1 << DEPS_IDX_OFFSET) && "offset to constant data too large");
12511271
arraylist_push(&s->relocs_list, (void*)(reloc_offset + 8)); // relocation location
12521272
arraylist_push(&s->relocs_list, (void*)(((uintptr_t)ConstDataRef << RELOC_TAG_OFFSET) + data)); // relocation target
12531273
void *pdata = jl_unbox_voidpointer(jl_get_nth_field(v, 2));
@@ -1534,6 +1554,8 @@ static uintptr_t get_reloc_for_item(uintptr_t reloc_item, size_t reloc_offset)
15341554
case FunctionRef:
15351555
assert(offset < JL_API_MAX && "unknown function pointer id");
15361556
break;
1557+
case SysimageLinkage:
1558+
break;
15371559
case ExternalLinkage:
15381560
break;
15391561
default:
@@ -1600,15 +1622,25 @@ static inline uintptr_t get_item_for_reloc(jl_serializer_state *s, uintptr_t bas
16001622
//default:
16011623
assert("corrupt relocation item id");
16021624
}
1625+
case SysimageLinkage:
1626+
assert(jl_linkage_blobs.len > 0);
1627+
return (uintptr_t)jl_linkage_blobs.items[0] + offset*sizeof(void*);
16031628
case ExternalLinkage:
16041629
assert(link_ids);
1605-
assert(link_index);
16061630
assert(jl_build_ids);
16071631
uint64_t *link_id_data = (uint64_t*)jl_array_data(link_ids);
16081632
uint64_t *build_id_data = (uint64_t*)jl_array_data(jl_build_ids);
1633+
#ifdef _P64
1634+
size_t depsidx = offset >> DEPS_IDX_OFFSET;
1635+
offset &= ((size_t)1 << DEPS_IDX_OFFSET) - 1;
1636+
assert(depsidx < jl_array_len(link_ids)); // on 64-bit, here link_ids is a build_id lookup by depmod index
1637+
uint64_t build_id = link_id_data[depsidx];
1638+
#else
1639+
assert(link_index);
16091640
assert(0 <= *link_index && *link_index < jl_array_len(link_ids));
16101641
uint64_t build_id = link_id_data[*link_index];
16111642
*link_index += 1;
1643+
#endif
16121644
size_t i = 0, nids = jl_array_len(jl_build_ids);
16131645
while (i < nids) {
16141646
if (build_id == build_id_data[i])
@@ -1800,8 +1832,12 @@ static jl_value_t *jl_delayed_reloc(jl_serializer_state *s, uintptr_t offset) JL
18001832
uintptr_t base = (uintptr_t)&s->s->buf[0];
18011833
size_t size = s->s->size;
18021834
int link_index = 0;
1835+
#ifdef _P64
1836+
jl_value_t *ret = (jl_value_t*)get_item_for_reloc(s, base, size, offset, s->buildid_depmods_idxs, &link_index);
1837+
#else
18031838
jl_value_t *ret = (jl_value_t*)get_item_for_reloc(s, base, size, offset, s->link_ids_relocs, &link_index);
18041839
assert(link_index < jl_array_len(s->link_ids_relocs));
1840+
#endif
18051841
return ret;
18061842
}
18071843

@@ -1891,13 +1927,24 @@ static void jl_update_all_gvars(jl_serializer_state *s, jl_image_t *image, uint3
18911927
reloc_t *gvars = (reloc_t*)&s->gvar_record->buf[0];
18921928
int gvar_link_index = 0;
18931929
int external_fns_link_index = 0;
1930+
jl_array_t *link_ids = NULL;
18941931
for (i = 0; i < l; i++) {
18951932
uintptr_t offset = gvars[i];
18961933
uintptr_t v = 0;
18971934
if (i < external_fns_begin) {
1898-
v = get_item_for_reloc(s, base, size, offset, s->link_ids_gvars, &gvar_link_index);
1935+
#ifdef _P64
1936+
link_ids = s->buildid_depmods_idxs;
1937+
#else
1938+
link_ids = s->link_ids_gvars;
1939+
#endif
1940+
v = get_item_for_reloc(s, base, size, offset, link_ids, &gvar_link_index);
18991941
} else {
1900-
v = get_item_for_reloc(s, base, size, offset, s->link_ids_external_fnvars, &external_fns_link_index);
1942+
#ifdef _P64
1943+
link_ids = s->buildid_depmods_idxs;
1944+
#else
1945+
link_ids = s->link_ids_external_fnvars;
1946+
#endif
1947+
v = get_item_for_reloc(s, base, size, offset, link_ids, &external_fns_link_index);
19011948
}
19021949
uintptr_t *gv = sysimg_gvars(image->gvars_base, image->gvars_offsets, i);
19031950
*gv = v;
@@ -2218,7 +2265,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new
22182265
}
22192266

22202267
// In addition to the system image (where `worklist = NULL`), this can also save incremental images with external linkage
2221-
static void jl_save_system_image_to_stream(ios_t *f,
2268+
static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *udeps,
22222269
jl_array_t *worklist, jl_array_t *extext_methods,
22232270
jl_array_t *new_specializations, jl_array_t *method_roots_list,
22242271
jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED
@@ -2266,10 +2313,15 @@ static void jl_save_system_image_to_stream(ios_t *f,
22662313
arraylist_new(&s.fixup_types, 0);
22672314
arraylist_new(&s.fixup_objs, 0);
22682315
arraylist_new(&s.ccallable_list, 0);
2316+
#ifdef _P64
2317+
s.buildid_depmods_idxs = image_to_depmodidx(jl_build_ids, udeps);
2318+
s.link_ids_relocs = s.link_ids_gctags = s.link_ids_gvars = s.link_ids_external_fnvars = NULL;
2319+
#else
22692320
s.link_ids_relocs = jl_alloc_array_1d(jl_array_uint64_type, 0);
22702321
s.link_ids_gctags = jl_alloc_array_1d(jl_array_uint64_type, 0);
22712322
s.link_ids_gvars = jl_alloc_array_1d(jl_array_uint64_type, 0);
22722323
s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_uint64_type, 0);
2324+
#endif
22732325
htable_new(&s.callers_with_edges, 0);
22742326
jl_value_t **const*const tags = get_tags(); // worklist == NULL ? get_tags() : NULL;
22752327

@@ -2368,21 +2420,21 @@ static void jl_save_system_image_to_stream(ios_t *f,
23682420
jl_write_relocations(&s);
23692421
}
23702422

2371-
if (sysimg.size > ((uintptr_t)1 << RELOC_TAG_OFFSET)) {
2423+
if (sysimg.size > ((uintptr_t)1 << DEPS_IDX_OFFSET)) {
23722424
jl_printf(
23732425
JL_STDERR,
23742426
"ERROR: system image too large: sysimg.size is %jd but the limit is %" PRIxPTR "\n",
23752427
(intmax_t)sysimg.size,
2376-
((uintptr_t)1 << RELOC_TAG_OFFSET)
2428+
((uintptr_t)1 << DEPS_IDX_OFFSET)
23772429
);
23782430
jl_exit(1);
23792431
}
2380-
if (const_data.size / sizeof(void*) > ((uintptr_t)1 << RELOC_TAG_OFFSET)) {
2432+
if (const_data.size / sizeof(void*) > ((uintptr_t)1 << DEPS_IDX_OFFSET)) {
23812433
jl_printf(
23822434
JL_STDERR,
23832435
"ERROR: system image too large: const_data.size is %jd but the limit is %" PRIxPTR "\n",
23842436
(intmax_t)const_data.size,
2385-
((uintptr_t)1 << RELOC_TAG_OFFSET)*sizeof(void*)
2437+
((uintptr_t)1 << DEPS_IDX_OFFSET)*sizeof(void*)
23862438
);
23872439
jl_exit(1);
23882440
}
@@ -2458,6 +2510,7 @@ static void jl_save_system_image_to_stream(ios_t *f,
24582510
jl_write_value(&s, ext_targets);
24592511
jl_write_value(&s, edges);
24602512
}
2513+
#ifndef _P64
24612514
write_uint32(f, jl_array_len(s.link_ids_gctags));
24622515
ios_write(f, (char*)jl_array_data(s.link_ids_gctags), jl_array_len(s.link_ids_gctags)*sizeof(uint64_t));
24632516
write_uint32(f, jl_array_len(s.link_ids_relocs));
@@ -2466,6 +2519,7 @@ static void jl_save_system_image_to_stream(ios_t *f,
24662519
ios_write(f, (char*)jl_array_data(s.link_ids_gvars), jl_array_len(s.link_ids_gvars)*sizeof(uint64_t));
24672520
write_uint32(f, jl_array_len(s.link_ids_external_fnvars));
24682521
ios_write(f, (char*)jl_array_data(s.link_ids_external_fnvars), jl_array_len(s.link_ids_external_fnvars)*sizeof(uint64_t));
2522+
#endif
24692523
write_uint32(f, external_fns_begin);
24702524
jl_write_arraylist(s.s, &s.ccallable_list);
24712525
}
@@ -2592,7 +2646,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli
25922646
}
25932647
if (_native_data != NULL)
25942648
native_functions = *_native_data;
2595-
jl_save_system_image_to_stream(ff, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
2649+
jl_save_system_image_to_stream(ff, *udeps, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges);
25962650
if (_native_data != NULL)
25972651
native_functions = NULL;
25982652
// make sure we don't run any Julia code concurrently before this point
@@ -2765,6 +2819,9 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
27652819
offset_ext_targets = jl_read_offset(&s);
27662820
offset_edges = jl_read_offset(&s);
27672821
}
2822+
#ifdef _P64
2823+
s.buildid_depmods_idxs = depmod_to_imageidx(depmods);
2824+
#else
27682825
size_t nlinks_gctags = read_uint32(f);
27692826
if (nlinks_gctags > 0) {
27702827
s.link_ids_gctags = jl_alloc_array_1d(jl_array_uint64_type, nlinks_gctags);
@@ -2785,6 +2842,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
27852842
s.link_ids_external_fnvars = jl_alloc_array_1d(jl_array_uint64_type, nlinks_external_fnvars);
27862843
ios_read(f, (char*)jl_array_data(s.link_ids_external_fnvars), nlinks_external_fnvars * sizeof(uint64_t));
27872844
}
2845+
#endif
27882846
uint32_t external_fns_begin = read_uint32(f);
27892847
jl_read_arraylist(s.s, ccallable_list ? ccallable_list : &s.ccallable_list);
27902848
if (s.incremental) {
@@ -2811,10 +2869,19 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl
28112869
*base = image_base;
28122870

28132871
s.s = &sysimg;
2814-
jl_read_reloclist(&s, s.link_ids_gctags, GC_OLD); // gctags
2872+
#ifdef _P64
2873+
jl_array_t *link_ids = NULL;
2874+
#else
2875+
jl_array_t *link_ids = s.link_ids_gctags;
2876+
#endif
2877+
jl_read_reloclist(&s, link_ids, GC_OLD); // gctags
28152878
size_t sizeof_tags = ios_pos(&relocs);
28162879
(void)sizeof_tags;
2817-
jl_read_reloclist(&s, s.link_ids_relocs, 0); // general relocs
2880+
#ifdef _P64
2881+
#else
2882+
link_ids = s.link_ids_relocs;
2883+
#endif
2884+
jl_read_reloclist(&s, link_ids, 0); // general relocs
28182885
// s.link_ids_gvars will be processed in `jl_update_all_gvars`
28192886
// s.link_ids_external_fns will be processed in `jl_update_all_gvars`
28202887
jl_update_all_gvars(&s, image, external_fns_begin); // gvars relocs

src/staticdata_utils.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,3 +1231,56 @@ JL_DLLEXPORT uint64_t jl_read_verify_header(ios_t *s, uint8_t *pkgimage, int64_t
12311231
}
12321232
return checksum;
12331233
}
1234+
1235+
// Returns `depmodidxs` where `udeps[depmodidxs[i]]` corresponds to `build_ids[i]`
1236+
static jl_array_t *image_to_depmodidx(jl_array_t *build_ids, jl_array_t *udeps)
1237+
{
1238+
if (!udeps || !build_ids)
1239+
return NULL;
1240+
size_t j = 0, lbids = jl_array_len(build_ids), ldeps = jl_array_len(udeps);
1241+
uint64_t *bids = (uint64_t*)jl_array_data(build_ids);
1242+
jl_array_t *depmodidxs = jl_alloc_array_1d(jl_array_int32_type, lbids);
1243+
int32_t *dmidxs = (int32_t*)jl_array_data(depmodidxs);
1244+
for (size_t i = 0; i < lbids; i++) {
1245+
dmidxs[i] = -1;
1246+
uint64_t bid = bids[i];
1247+
j = 0; // sad that this is of O(M*N)
1248+
while (j < ldeps) {
1249+
jl_value_t *deptuple = jl_array_ptr_ref(udeps, j);
1250+
jl_module_t *depmod = (jl_module_t*)jl_fieldref(deptuple, 0); // evaluating module
1251+
jl_module_t *depmod_top = depmod;
1252+
while (depmod_top->parent != jl_main_module && depmod_top->parent != depmod_top)
1253+
depmod_top = depmod_top->parent;
1254+
if (depmod_top == jl_base_module) {
1255+
dmidxs[i] = 0;
1256+
break;
1257+
}
1258+
if (depmod_top->build_id.lo == bid) {
1259+
dmidxs[i] = j;
1260+
break;
1261+
}
1262+
j++;
1263+
}
1264+
assert(dmidxs[i] >= 0);
1265+
}
1266+
return depmodidxs;
1267+
}
1268+
1269+
// Returns `imageidxs` where `imageidxs[i]` is the blob corresponding to `depmods[i]`
1270+
static jl_array_t *depmod_to_imageidx(jl_array_t *depmods)
1271+
{
1272+
if (!depmods)
1273+
return NULL;
1274+
size_t ldeps = jl_array_len(depmods);
1275+
jl_array_t *imageidxs = jl_alloc_array_1d(jl_array_int32_type, ldeps);
1276+
int32_t *imgidxs = (int32_t*)jl_array_data(imageidxs);
1277+
for (size_t i = 0; i < ldeps; i++) {
1278+
imgidxs[i] = -1;
1279+
jl_value_t *depmod = jl_array_ptr_ref(depmods, i);
1280+
assert(jl_is_module(depmod));
1281+
size_t j = external_blob_index(depmod);
1282+
assert(j < 1<<31);
1283+
imgidxs[i] = (int32_t)j;
1284+
}
1285+
return imageidxs;
1286+
}

0 commit comments

Comments
 (0)