Skip to content

Commit 6121477

Browse files
authored
add support for storing just the inferred inlining_cost in CodeInstance (JuliaLang#58662)
In preparation for giving us the option of not storing code that does not seem useful immediately, but which we previously kept around just in case inference or codegen (always_inline) ever needed to decide if it was worthwhile to inline later. And also for investigating issues like the recently closed JuliaLang#58449 to examine whether the code was removed for either heuristic or correctness reasons.
1 parent 66ec6ee commit 6121477

File tree

8 files changed

+65
-27
lines changed

8 files changed

+65
-27
lines changed

Compiler/src/optimize.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ set_inlineable!(src::CodeInfo, val::Bool) =
113113
function inline_cost_clamp(x::Int)
114114
x > MAX_INLINE_COST && return MAX_INLINE_COST
115115
x < MIN_INLINE_COST && return MIN_INLINE_COST
116-
return convert(InlineCostType, x)
116+
x = ccall(:jl_encode_inlining_cost, UInt8, (InlineCostType,), x)
117+
x = ccall(:jl_decode_inlining_cost, InlineCostType, (UInt8,), x)
118+
return x
117119
end
118120

119121
const SRC_FLAG_DECLARED_INLINE = 0x1

src/codegen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9900,7 +9900,7 @@ void emit_always_inline(orc::ThreadSafeModule &result_m, jl_codegen_params_t &pa
99009900
src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred);
99019901
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
99029902
jl_method_t *def = mi->def.method;
9903-
if (src && (jl_value_t*)src != jl_nothing && jl_is_method(def) && jl_ir_inlining_cost((jl_value_t*)src) < UINT16_MAX)
9903+
if (src && jl_is_string((jl_value_t*)src) && jl_is_method(def) && jl_ir_inlining_cost((jl_value_t*)src) < UINT16_MAX)
99049904
src = jl_uncompress_ir(def, codeinst, (jl_value_t*)src);
99059905
if (src && jl_is_code_info(src) && jl_ir_inlining_cost((jl_value_t*)src) < UINT16_MAX) {
99069906
jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, src, params); // contains safepoints

src/gc-stock.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -365,15 +365,6 @@ STATIC_INLINE jl_gc_pagemeta_t *pop_page_metadata_back(jl_gc_pagemeta_t **ppg) J
365365
return v;
366366
}
367367

368-
#ifdef __clang_gcanalyzer__ /* clang may not have __builtin_ffs */
369-
unsigned ffs_u32(uint32_t bitvec) JL_NOTSAFEPOINT;
370-
#else
371-
STATIC_INLINE unsigned ffs_u32(uint32_t bitvec)
372-
{
373-
return __builtin_ffs(bitvec) - 1;
374-
}
375-
#endif
376-
377368
extern bigval_t *oldest_generation_of_bigvals;
378369
extern int64_t buffered_pages;
379370
extern int gc_first_tid;

src/gf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ static int emit_codeinst_and_edges(jl_code_instance_t *codeinst)
357357
JL_GC_PUSH1(&code);
358358
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
359359
jl_method_t *def = mi->def.method;
360-
if (jl_is_string(code) && jl_is_method(def))
360+
if (jl_is_method(def))
361361
code = (jl_value_t*)jl_uncompress_ir(def, codeinst, (jl_value_t*)code);
362362
if (jl_is_code_info(code)) {
363363
jl_emit_codeinst_to_jit(codeinst, (jl_code_info_t*)code);

src/ircode.c

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ static int codelocs_nstmts(jl_string_t *cl) JL_NOTSAFEPOINT
989989

990990
#define IR_DATASIZE_FLAGS sizeof(uint16_t)
991991
#define IR_DATASIZE_PURITY sizeof(uint16_t)
992-
#define IR_DATASIZE_INLINING_COST sizeof(uint16_t)
992+
#define IR_DATASIZE_INLINING_COST sizeof(uint8_t)
993993
#define IR_DATASIZE_NSLOTS sizeof(int32_t)
994994
typedef enum {
995995
ir_offset_flags = 0,
@@ -1044,7 +1044,7 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
10441044
code->ssaflags);
10451045
write_uint16(s.s, checked_size(flags.packed, IR_DATASIZE_FLAGS));
10461046
write_uint16(s.s, checked_size(code->purity.bits, IR_DATASIZE_PURITY));
1047-
write_uint16(s.s, checked_size(code->inlining_cost, IR_DATASIZE_INLINING_COST));
1047+
write_uint8(s.s, checked_size(jl_encode_inlining_cost(code->inlining_cost), IR_DATASIZE_INLINING_COST));
10481048

10491049
size_t nslots = jl_array_nrows(code->slotflags);
10501050
assert(nslots >= m->nargs && nslots < INT32_MAX); // required by generated functions
@@ -1109,6 +1109,8 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
11091109
{
11101110
if (jl_is_code_info(data))
11111111
return (jl_code_info_t*)data;
1112+
if (!jl_is_string(data))
1113+
return (jl_code_info_t*)jl_nothing;
11121114
JL_TIMING(AST_UNCOMPRESS, AST_UNCOMPRESS);
11131115
JL_LOCK(&m->writelock); // protect the roots array (Might GC)
11141116
assert(jl_is_method(m));
@@ -1139,7 +1141,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
11391141
code->nospecializeinfer = flags.bits.nospecializeinfer;
11401142
code->isva = flags.bits.isva;
11411143
code->purity.bits = read_uint16(s.s);
1142-
code->inlining_cost = read_uint16(s.s);
1144+
code->inlining_cost = jl_decode_inlining_cost(read_uint8(s.s));
11431145

11441146
size_t nslots = read_int32(s.s);
11451147
code->slotflags = jl_alloc_array_1d(jl_array_uint8_type, nslots);
@@ -1240,12 +1242,46 @@ JL_DLLEXPORT uint8_t jl_ir_flag_has_image_globalref(jl_string_t *data)
12401242
return flags.bits.has_image_globalref;
12411243
}
12421244

1243-
JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_string_t *data)
1245+
// create a compressed u16 value with range 0..3968, 3 bits exponent, 5 bits mantissa, implicit first digit, rounding up, full accuracy over 0..63
1246+
JL_DLLEXPORT uint8_t jl_encode_inlining_cost(uint16_t inlining_cost)
12441247
{
1248+
unsigned shift = 0;
1249+
unsigned mantissa;
1250+
if (inlining_cost <= 0x1f) {
1251+
mantissa = inlining_cost;
1252+
}
1253+
else {
1254+
while (inlining_cost >> 5 >> shift != 0)
1255+
shift++;
1256+
assert(1 <= shift && shift <= 11);
1257+
mantissa = (inlining_cost >> (shift - 1)) & 0x1f;
1258+
mantissa += (inlining_cost & ((1 << (shift - 1)) - 1)) != 0; // round up if trailing bits non-zero, overflowing into exp
1259+
}
1260+
unsigned r = (shift << 5) + mantissa;
1261+
if (r > 0xff)
1262+
r = 0xff;
1263+
return r;
1264+
}
1265+
1266+
JL_DLLEXPORT uint16_t jl_decode_inlining_cost(uint8_t inlining_cost)
1267+
{
1268+
unsigned shift = inlining_cost >> 5;
1269+
if (inlining_cost == 0xff)
1270+
return 0xffff;
1271+
else if (shift == 0)
1272+
return inlining_cost;
1273+
else
1274+
return (0x20 | (inlining_cost & 0x1f)) << (shift - 1);
1275+
}
1276+
1277+
JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_value_t *data)
1278+
{
1279+
if (jl_is_uint8(data))
1280+
return jl_decode_inlining_cost(*(uint8_t*)data);
12451281
if (jl_is_code_info(data))
12461282
return ((jl_code_info_t*)data)->inlining_cost;
12471283
assert(jl_is_string(data));
1248-
uint16_t res = jl_load_unaligned_i16(jl_string_data(data) + ir_offset_inlining_cost);
1284+
uint16_t res = jl_decode_inlining_cost(*(uint8_t*)(jl_string_data(data) + ir_offset_inlining_cost));
12491285
return res;
12501286
}
12511287

src/julia.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,10 +438,11 @@ typedef struct _jl_code_instance_t {
438438
jl_value_t *rettype_const; // inferred constant return value, or null
439439

440440
// Inferred result. When part of the runtime cache, either
441-
// - A jl_code_info_t (may be compressed) containing the inferred IR
441+
// - A jl_code_info_t (may be compressed as a String) containing the inferred IR
442442
// - jl_nothing, indicating that inference was completed, but the result was
443443
// deleted to save space.
444-
// - null, indicating that inference was not yet completed or did not succeed
444+
// - UInt8, indicating that inference recorded the estimated inlining cost, but deleted the result to save space
445+
// - NULL, indicating that inference was not yet completed or did not succeed
445446
_Atomic(jl_value_t *) inferred;
446447
_Atomic(jl_debuginfo_t *) debuginfo; // stored information about edges from this object (set once, with a happens-before both source and invoke)
447448
_Atomic(jl_svec_t *) edges; // forward edge info
@@ -2311,6 +2312,8 @@ JL_DLLEXPORT jl_value_t *jl_uncompress_argname_n(jl_value_t *syms, size_t i);
23112312
JL_DLLEXPORT struct jl_codeloc_t jl_uncompress1_codeloc(jl_value_t *cl, size_t pc) JL_NOTSAFEPOINT;
23122313
JL_DLLEXPORT jl_value_t *jl_compress_codelocs(int32_t firstline, jl_value_t *codelocs, size_t nstmts);
23132314
JL_DLLEXPORT jl_value_t *jl_uncompress_codelocs(jl_value_t *cl, size_t nstmts);
2315+
JL_DLLEXPORT uint8_t jl_encode_inlining_cost(uint16_t inlining_cost) JL_NOTSAFEPOINT;
2316+
JL_DLLEXPORT uint16_t jl_decode_inlining_cost(uint8_t inlining_cost) JL_NOTSAFEPOINT;
23142317

23152318
JL_DLLEXPORT int jl_is_operator(const char *sym);
23162319
JL_DLLEXPORT int jl_is_unary_operator(const char *sym);

src/precompile_utils.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ static int precompile_enq_specialization_(jl_method_instance_t *mi, void *closur
208208
jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred);
209209
if (inferred &&
210210
(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL || inferred == jl_nothing ||
211-
((jl_is_string(inferred) || jl_is_code_info(inferred)) && jl_ir_inlining_cost(inferred) == UINT16_MAX))) {
211+
((jl_is_string(inferred) || jl_is_code_info(inferred) || jl_is_uint8(inferred)) && jl_ir_inlining_cost(inferred) == UINT16_MAX))) {
212212
do_compile = 1;
213213
}
214214
else if (jl_atomic_load_relaxed(&codeinst->invoke) != NULL || jl_atomic_load_relaxed(&codeinst->precompile)) {

src/staticdata.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -911,30 +911,36 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_
911911
}
912912
}
913913
jl_value_t *inferred = jl_atomic_load_relaxed(&ci->inferred);
914-
if (inferred && inferred != jl_nothing) { // disregard if there is nothing here to delete (e.g. builtins, unspecialized)
914+
if (inferred && inferred != jl_nothing && !jl_is_uint8(inferred)) { // disregard if there is nothing here to delete (e.g. builtins, unspecialized)
915915
jl_method_t *def = mi->def.method;
916916
if (jl_is_method(def)) { // don't delete toplevel code
917917
int is_relocatable = !s->incremental || jl_is_code_info(inferred) ||
918918
(jl_is_string(inferred) && jl_string_len(inferred) > 0 && jl_string_data(inferred)[jl_string_len(inferred) - 1]);
919+
int discard = 0;
919920
if (!is_relocatable) {
920-
inferred = jl_nothing;
921+
discard = 1;
921922
}
922923
else if (def->source == NULL) {
923924
// don't delete code from optimized opaque closures that can't be reconstructed (and builtins)
924925
}
925926
else if (jl_atomic_load_relaxed(&ci->max_world) != ~(size_t)0 || // delete all code that cannot run
926927
jl_atomic_load_relaxed(&ci->invoke) == jl_fptr_const_return) { // delete all code that just returns a constant
927-
inferred = jl_nothing;
928+
discard = 1;
928929
}
929930
else if (native_functions && // don't delete any code if making a ji file
930931
(ci->owner == jl_nothing) && // don't delete code for external interpreters
931932
!effects_foldable(jl_atomic_load_relaxed(&ci->ipo_purity_bits)) && // don't delete code we may want for irinterp
932933
jl_ir_inlining_cost(inferred) == UINT16_MAX) { // don't delete inlineable code
933934
// delete the code now: if we thought it was worth keeping, it would have been converted to object code
934-
inferred = jl_nothing;
935+
discard = 1;
935936
}
936-
if (inferred == jl_nothing) {
937-
record_field_change((jl_value_t**)&ci->inferred, jl_nothing);
937+
if (discard) {
938+
// keep only the inlining cost, so inference can later decide if it is worth getting the source back
939+
if (jl_is_string(inferred) || jl_is_code_info(inferred))
940+
inferred = jl_box_uint8(jl_encode_inlining_cost(jl_ir_inlining_cost(inferred)));
941+
else
942+
inferred = jl_nothing;
943+
record_field_change((jl_value_t**)&ci->inferred, inferred);
938944
}
939945
else if (s->incremental && jl_is_string(inferred)) {
940946
// New roots for external methods
@@ -2704,7 +2710,7 @@ static void strip_specializations_(jl_method_instance_t *mi)
27042710
jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&mi->cache);
27052711
while (codeinst) {
27062712
jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred);
2707-
if (inferred && inferred != jl_nothing) {
2713+
if (inferred && inferred != jl_nothing && !jl_is_uint8(inferred)) {
27082714
if (jl_options.strip_ir) {
27092715
record_field_change((jl_value_t**)&codeinst->inferred, jl_nothing);
27102716
}

0 commit comments

Comments
 (0)