Skip to content

Commit 7ac710c

Browse files
authored
add --strip-ir option (#42925)
1 parent 72de6dd commit 7ac710c

File tree

7 files changed

+97
-19
lines changed

7 files changed

+97
-19
lines changed

NEWS.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ Command-line option changes
3838
---------------------------
3939

4040
* New option `--strip-metadata` to remove docstrings, source location information, and local
41-
variable names when building a system image.
41+
variable names when building a system image ([#42513]).
42+
* New option `--strip-ir` to remove the compiler's IR (intermediate representation) of source
43+
code when building a system image. The resulting image will only work if `--compile=all` is
44+
used, or if all needed code is precompiled ([#42925]).
4245

4346
Multi-threading changes
4447
-----------------------

base/options.jl

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct JLOptions
4848
image_codegen::Int8
4949
rr_detach::Int8
5050
strip_metadata::Int8
51+
strip_ir::Int8
5152
end
5253

5354
# This runs early in the sysimage != is not defined yet

src/gf.c

+16-3
Original file line numberDiff line numberDiff line change
@@ -1946,10 +1946,11 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t
19461946
compile_option = ((jl_method_t*)def)->module->compile;
19471947
}
19481948

1949+
// if compilation is disabled or source is unavailable, try calling unspecialized version
19491950
if (compile_option == JL_OPTIONS_COMPILE_OFF ||
1950-
compile_option == JL_OPTIONS_COMPILE_MIN) {
1951+
compile_option == JL_OPTIONS_COMPILE_MIN ||
1952+
def->source == jl_nothing) {
19511953
// copy fptr from the template method definition
1952-
jl_method_t *def = mi->def.method;
19531954
if (jl_is_method(def) && def->unspecialized) {
19541955
jl_code_instance_t *unspec = jl_atomic_load_relaxed(&def->unspecialized->cache);
19551956
if (unspec && jl_atomic_load_relaxed(&unspec->invoke)) {
@@ -1964,6 +1965,10 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t
19641965
return codeinst;
19651966
}
19661967
}
1968+
}
1969+
// if that didn't work and compilation is off, try running in the interpreter
1970+
if (compile_option == JL_OPTIONS_COMPILE_OFF ||
1971+
compile_option == JL_OPTIONS_COMPILE_MIN) {
19671972
jl_code_info_t *src = jl_code_for_interpreter(mi);
19681973
if (!jl_code_requires_compiler(src)) {
19691974
jl_code_instance_t *codeinst = jl_new_codeinst(mi,
@@ -1985,8 +1990,16 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t
19851990
jl_method_instance_t *unspec = jl_get_unspecialized(mi);
19861991
jl_code_instance_t *ucache = jl_get_method_inferred(unspec, (jl_value_t*)jl_any_type, 1, ~(size_t)0);
19871992
// ask codegen to make the fptr for unspec
1988-
if (jl_atomic_load_relaxed(&ucache->invoke) == NULL)
1993+
if (jl_atomic_load_relaxed(&ucache->invoke) == NULL) {
1994+
if (def->source == jl_nothing && (ucache->def->uninferred == jl_nothing ||
1995+
ucache->def->uninferred == NULL)) {
1996+
jl_printf(JL_STDERR, "source not available for ");
1997+
jl_static_show(JL_STDERR, (jl_value_t*)mi);
1998+
jl_printf(JL_STDERR, "\n");
1999+
jl_error("source missing for method that needs to be compiled");
2000+
}
19892001
jl_generate_fptr_for_unspecialized(ucache);
2002+
}
19902003
assert(jl_atomic_load_relaxed(&ucache->invoke) != NULL);
19912004
if (jl_atomic_load_relaxed(&ucache->invoke) != jl_fptr_sparam &&
19922005
jl_atomic_load_relaxed(&ucache->invoke) != jl_fptr_interpret_call) {

src/jitlayers.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,12 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES
318318
}
319319
if (src == NULL && jl_is_method(mi->def.method) &&
320320
jl_symbol_name(mi->def.method->name)[0] != '@') {
321-
// If the caller didn't provide the source,
322-
// see if it is inferred, or try to infer it for ourself.
323-
// (but don't bother with typeinf on macros or toplevel thunks)
324-
src = jl_type_infer(mi, world, 0);
321+
if (mi->def.method->source != jl_nothing) {
322+
// If the caller didn't provide the source and IR is available,
323+
// see if it is inferred, or try to infer it for ourself.
324+
// (but don't bother with typeinf on macros or toplevel thunks)
325+
src = jl_type_infer(mi, world, 0);
326+
}
325327
}
326328
jl_code_instance_t *compiled = jl_method_compiled(mi, world);
327329
if (compiled) {

src/jloptions.c

+7
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ JL_DLLEXPORT void jl_init_options(void)
8080
0, // image-codegen
8181
0, // rr-detach
8282
0, // strip-metadata
83+
0, // strip-ir
8384
};
8485
jl_options_initialized = 1;
8586
}
@@ -165,6 +166,7 @@ static const char opts_hidden[] =
165166
" --output-o name Generate an object file (including system image data)\n"
166167
" --output-ji name Generate a system image data file (.ji)\n"
167168
" --strip-metadata Remove docstrings and source location info from system image\n"
169+
" --strip-ir Remove IR (intermediate representation) of compiled functions\n"
168170

169171
// compiler debugging (see the devdocs for tips on using these options)
170172
" --output-unopt-bc name Generate unoptimized LLVM bitcode (.bc)\n"
@@ -215,6 +217,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
215217
opt_image_codegen,
216218
opt_rr_detach,
217219
opt_strip_metadata,
220+
opt_strip_ir,
218221
};
219222
static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:";
220223
static const struct option longopts[] = {
@@ -269,6 +272,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
269272
{ "image-codegen", no_argument, 0, opt_image_codegen },
270273
{ "rr-detach", no_argument, 0, opt_rr_detach },
271274
{ "strip-metadata", no_argument, 0, opt_strip_metadata },
275+
{ "strip-ir", no_argument, 0, opt_strip_ir },
272276
{ 0, 0, 0, 0 }
273277
};
274278

@@ -696,6 +700,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
696700
case opt_strip_metadata:
697701
jl_options.strip_metadata = 1;
698702
break;
703+
case opt_strip_ir:
704+
jl_options.strip_ir = 1;
705+
break;
699706
default:
700707
jl_errorf("julia: unhandled option -- %c\n"
701708
"This is a bug, please report it.", c);

src/jloptions.h

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ typedef struct {
5252
int8_t image_codegen;
5353
int8_t rr_detach;
5454
int8_t strip_metadata;
55+
int8_t strip_ir;
5556
} jl_options_t;
5657

5758
#endif

src/staticdata.c

+62-11
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ static arraylist_t ccallable_list;
233233
static htable_t fptr_to_id;
234234
void *native_functions;
235235

236+
// table of struct field addresses to rewrite during saving
237+
static htable_t field_replace;
238+
236239
// array of definitions for the predefined function pointers
237240
// (reverse of fptr_to_id)
238241
// This is a manually constructed dual of the fvars array, which would be produced by codegen for Julia code, for C.
@@ -415,6 +418,13 @@ static void jl_serialize_module(jl_serializer_state *s, jl_module_t *m)
415418
}
416419
}
417420

421+
static jl_value_t *get_replaceable_field(jl_value_t **addr)
422+
{
423+
jl_value_t *fld = (jl_value_t*)ptrhash_get(&field_replace, addr);
424+
if (fld == HT_NOTFOUND)
425+
return *addr;
426+
return fld;
427+
}
418428

419429
#define NBOX_C 1024
420430

@@ -515,7 +525,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int recur
515525
size_t i, np = t->layout->npointers;
516526
for (i = 0; i < np; i++) {
517527
uint32_t ptr = jl_ptr_offset(t, i);
518-
jl_value_t *fld = ((jl_value_t* const*)data)[ptr];
528+
jl_value_t *fld = get_replaceable_field(&((jl_value_t**)data)[ptr]);
519529
jl_serialize_value(s, fld);
520530
}
521531
}
@@ -944,7 +954,7 @@ static void jl_write_values(jl_serializer_state *s)
944954
size_t np = t->layout->npointers;
945955
for (i = 0; i < np; i++) {
946956
size_t offset = jl_ptr_offset(t, i) * sizeof(jl_value_t*);
947-
jl_value_t *fld = *(jl_value_t**)&data[offset];
957+
jl_value_t *fld = get_replaceable_field((jl_value_t**)&data[offset]);
948958
if (fld != NULL) {
949959
arraylist_push(&s->relocs_list, (void*)(uintptr_t)(offset + reloc_offset)); // relocation location
950960
arraylist_push(&s->relocs_list, (void*)backref_id(s, fld)); // relocation target
@@ -1527,7 +1537,7 @@ static void jl_prune_type_cache_linear(jl_svec_t *cache)
15271537
}
15281538
}
15291539

1530-
static jl_value_t *strip_codeinfo(jl_method_t *m, jl_value_t *ci_, int isdef)
1540+
static jl_value_t *strip_codeinfo_meta(jl_method_t *m, jl_value_t *ci_, int orig)
15311541
{
15321542
jl_code_info_t *ci = NULL;
15331543
JL_GC_PUSH1(&ci);
@@ -1552,7 +1562,7 @@ static jl_value_t *strip_codeinfo(jl_method_t *m, jl_value_t *ci_, int isdef)
15521562
if (s != (jl_value_t*)jl_unused_sym)
15531563
jl_array_ptr_set(ci->slotnames, i, questionsym);
15541564
}
1555-
if (isdef) {
1565+
if (orig) {
15561566
m->slot_syms = jl_compress_argnames(ci->slotnames);
15571567
jl_gc_wb(m, m->slot_syms);
15581568
}
@@ -1563,33 +1573,69 @@ static jl_value_t *strip_codeinfo(jl_method_t *m, jl_value_t *ci_, int isdef)
15631573
return ret;
15641574
}
15651575

1576+
static void record_field_change(jl_value_t **addr, jl_value_t *newval)
1577+
{
1578+
ptrhash_put(&field_replace, (void*)addr, newval);
1579+
}
1580+
15661581
static void strip_specializations_(jl_method_instance_t *mi)
15671582
{
15681583
assert(jl_is_method_instance(mi));
15691584
jl_code_instance_t *codeinst = mi->cache;
15701585
while (codeinst) {
15711586
if (codeinst->inferred && codeinst->inferred != jl_nothing) {
1572-
codeinst->inferred = strip_codeinfo(mi->def.method, codeinst->inferred, 0);
1573-
jl_gc_wb(codeinst, codeinst->inferred);
1587+
if (jl_options.strip_ir) {
1588+
record_field_change(&codeinst->inferred, jl_nothing);
1589+
}
1590+
else if (jl_options.strip_metadata) {
1591+
codeinst->inferred = strip_codeinfo_meta(mi->def.method, codeinst->inferred, 0);
1592+
jl_gc_wb(codeinst, codeinst->inferred);
1593+
}
15741594
}
15751595
codeinst = jl_atomic_load_relaxed(&codeinst->next);
15761596
}
1597+
if (jl_options.strip_ir) {
1598+
record_field_change(&mi->uninferred, NULL);
1599+
}
15771600
}
15781601

15791602
static int strip_all_codeinfos__(jl_typemap_entry_t *def, void *_env)
15801603
{
15811604
jl_method_t *m = def->func.method;
15821605
if (m->source) {
1583-
m->source = strip_codeinfo(m, m->source, 1);
1584-
jl_gc_wb(m, m->source);
1606+
int stripped_ir = 0;
1607+
if (jl_options.strip_ir) {
1608+
if (m->unspecialized) {
1609+
jl_code_instance_t *unspec = jl_atomic_load_relaxed(&m->unspecialized->cache);
1610+
if (unspec && jl_atomic_load_relaxed(&unspec->invoke)) {
1611+
// we have a generic compiled version, so can remove the IR
1612+
record_field_change(&m->source, jl_nothing);
1613+
stripped_ir = 1;
1614+
}
1615+
}
1616+
if (!stripped_ir) {
1617+
int mod_setting = jl_get_module_compile(m->module);
1618+
// if the method is declared not to be compiled, keep IR for interpreter
1619+
if (!(mod_setting == JL_OPTIONS_COMPILE_OFF || mod_setting == JL_OPTIONS_COMPILE_MIN)) {
1620+
record_field_change(&m->source, jl_nothing);
1621+
stripped_ir = 1;
1622+
}
1623+
}
1624+
}
1625+
if (jl_options.strip_metadata && !stripped_ir) {
1626+
m->source = strip_codeinfo_meta(m, m->source, 1);
1627+
jl_gc_wb(m, m->source);
1628+
}
15851629
}
1586-
jl_svec_t *specializations = def->func.method->specializations;
1630+
jl_svec_t *specializations = m->specializations;
15871631
size_t i, l = jl_svec_len(specializations);
15881632
for (i = 0; i < l; i++) {
15891633
jl_value_t *mi = jl_svecref(specializations, i);
15901634
if (mi != jl_nothing)
15911635
strip_specializations_((jl_method_instance_t*)mi);
15921636
}
1637+
if (m->unspecialized)
1638+
strip_specializations_(m->unspecialized);
15931639
return 1;
15941640
}
15951641

@@ -1610,11 +1656,15 @@ static void jl_cleanup_serializer2(void);
16101656

16111657
static void jl_save_system_image_to_stream(ios_t *f) JL_GC_DISABLED
16121658
{
1613-
if (jl_options.strip_metadata)
1614-
jl_strip_all_codeinfos();
16151659
jl_gc_collect(JL_GC_FULL);
16161660
jl_gc_collect(JL_GC_INCREMENTAL); // sweep finalizers
16171661
JL_TIMING(SYSIMG_DUMP);
1662+
1663+
htable_new(&field_replace, 10000);
1664+
// strip metadata and IR when requested
1665+
if (jl_options.strip_metadata || jl_options.strip_ir)
1666+
jl_strip_all_codeinfos();
1667+
16181668
int en = jl_gc_enable(0);
16191669
jl_init_serializer2(1);
16201670
htable_reset(&backref_table, 250000);
@@ -1759,6 +1809,7 @@ static void jl_save_system_image_to_stream(ios_t *f) JL_GC_DISABLED
17591809
arraylist_free(&ccallable_list);
17601810
arraylist_free(&s.relocs_list);
17611811
arraylist_free(&s.gctags_list);
1812+
htable_free(&field_replace);
17621813
jl_cleanup_serializer2();
17631814

17641815
jl_gc_enable(en);

0 commit comments

Comments
 (0)