Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Compiler/src/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ set_inlineable!(src::CodeInfo, val::Bool) =
function inline_cost_clamp(x::Int)
x > MAX_INLINE_COST && return MAX_INLINE_COST
x < MIN_INLINE_COST && return MIN_INLINE_COST
return convert(InlineCostType, x)
x = ccall(:jl_encode_inlining_cost, UInt8, (InlineCostType,), x)
x = ccall(:jl_decode_inlining_cost, InlineCostType, (UInt8,), x)
return x
end

const SRC_FLAG_DECLARED_INLINE = 0x1
Expand Down
2 changes: 1 addition & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9900,7 +9900,7 @@ void emit_always_inline(orc::ThreadSafeModule &result_m, jl_codegen_params_t &pa
src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred);
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
jl_method_t *def = mi->def.method;
if (src && (jl_value_t*)src != jl_nothing && jl_is_method(def) && jl_ir_inlining_cost((jl_value_t*)src) < UINT16_MAX)
if (src && jl_is_string((jl_value_t*)src) && jl_is_method(def) && jl_ir_inlining_cost((jl_value_t*)src) < UINT16_MAX)
src = jl_uncompress_ir(def, codeinst, (jl_value_t*)src);
if (src && jl_is_code_info(src) && jl_ir_inlining_cost((jl_value_t*)src) < UINT16_MAX) {
jl_llvm_functions_t decls = jl_emit_codeinst(result_m, codeinst, src, params); // contains safepoints
Expand Down
9 changes: 0 additions & 9 deletions src/gc-stock.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,15 +365,6 @@ STATIC_INLINE jl_gc_pagemeta_t *pop_page_metadata_back(jl_gc_pagemeta_t **ppg) J
return v;
}

#ifdef __clang_gcanalyzer__ /* clang may not have __builtin_ffs */
unsigned ffs_u32(uint32_t bitvec) JL_NOTSAFEPOINT;
#else
STATIC_INLINE unsigned ffs_u32(uint32_t bitvec)
{
return __builtin_ffs(bitvec) - 1;
}
#endif

extern bigval_t *oldest_generation_of_bigvals;
extern int64_t buffered_pages;
extern int gc_first_tid;
Expand Down
2 changes: 1 addition & 1 deletion src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ static int emit_codeinst_and_edges(jl_code_instance_t *codeinst)
JL_GC_PUSH1(&code);
jl_method_instance_t *mi = jl_get_ci_mi(codeinst);
jl_method_t *def = mi->def.method;
if (jl_is_string(code) && jl_is_method(def))
if (jl_is_method(def))
code = (jl_value_t*)jl_uncompress_ir(def, codeinst, (jl_value_t*)code);
if (jl_is_code_info(code)) {
jl_emit_codeinst_to_jit(codeinst, (jl_code_info_t*)code);
Expand Down
46 changes: 41 additions & 5 deletions src/ircode.c
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ static int codelocs_nstmts(jl_string_t *cl) JL_NOTSAFEPOINT

#define IR_DATASIZE_FLAGS sizeof(uint16_t)
#define IR_DATASIZE_PURITY sizeof(uint16_t)
#define IR_DATASIZE_INLINING_COST sizeof(uint16_t)
#define IR_DATASIZE_INLINING_COST sizeof(uint8_t)
#define IR_DATASIZE_NSLOTS sizeof(int32_t)
typedef enum {
ir_offset_flags = 0,
Expand Down Expand Up @@ -1044,7 +1044,7 @@ JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
code->ssaflags);
write_uint16(s.s, checked_size(flags.packed, IR_DATASIZE_FLAGS));
write_uint16(s.s, checked_size(code->purity.bits, IR_DATASIZE_PURITY));
write_uint16(s.s, checked_size(code->inlining_cost, IR_DATASIZE_INLINING_COST));
write_uint8(s.s, checked_size(jl_encode_inlining_cost(code->inlining_cost), IR_DATASIZE_INLINING_COST));

size_t nslots = jl_array_nrows(code->slotflags);
assert(nslots >= m->nargs && nslots < INT32_MAX); // required by generated functions
Expand Down Expand Up @@ -1109,6 +1109,8 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
{
if (jl_is_code_info(data))
return (jl_code_info_t*)data;
if (!jl_is_string(data))
return (jl_code_info_t*)jl_nothing;
JL_TIMING(AST_UNCOMPRESS, AST_UNCOMPRESS);
JL_LOCK(&m->writelock); // protect the roots array (Might GC)
assert(jl_is_method(m));
Expand Down Expand Up @@ -1139,7 +1141,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
code->nospecializeinfer = flags.bits.nospecializeinfer;
code->isva = flags.bits.isva;
code->purity.bits = read_uint16(s.s);
code->inlining_cost = read_uint16(s.s);
code->inlining_cost = jl_decode_inlining_cost(read_uint8(s.s));

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

JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_string_t *data)
// 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
JL_DLLEXPORT uint8_t jl_encode_inlining_cost(uint16_t inlining_cost)
{
unsigned shift = 0;
unsigned mantissa;
if (inlining_cost <= 0x1f) {
mantissa = inlining_cost;
}
else {
while (inlining_cost >> 5 >> shift != 0)
shift++;
assert(1 <= shift && shift <= 11);
mantissa = (inlining_cost >> (shift - 1)) & 0x1f;
mantissa += (inlining_cost & ((1 << (shift - 1)) - 1)) != 0; // round up if trailing bits non-zero, overflowing into exp
}
unsigned r = (shift << 5) + mantissa;
if (r > 0xff)
r = 0xff;
return r;
}

JL_DLLEXPORT uint16_t jl_decode_inlining_cost(uint8_t inlining_cost)
{
unsigned shift = inlining_cost >> 5;
if (inlining_cost == 0xff)
return 0xffff;
else if (shift == 0)
return inlining_cost;
else
return 0x20 | ((inlining_cost & 0x1f) << (shift - 1));
}

JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_value_t *data)
{
if (jl_is_uint8(data))
return jl_decode_inlining_cost(*(uint8_t*)data);
if (jl_is_code_info(data))
return ((jl_code_info_t*)data)->inlining_cost;
assert(jl_is_string(data));
uint16_t res = jl_load_unaligned_i16(jl_string_data(data) + ir_offset_inlining_cost);
uint16_t res = jl_decode_inlining_cost(*(uint8_t*)(jl_string_data(data) + ir_offset_inlining_cost));
return res;
}

Expand Down
7 changes: 5 additions & 2 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,10 +438,11 @@ typedef struct _jl_code_instance_t {
jl_value_t *rettype_const; // inferred constant return value, or null

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

JL_DLLEXPORT int jl_is_operator(const char *sym);
JL_DLLEXPORT int jl_is_unary_operator(const char *sym);
Expand Down
2 changes: 1 addition & 1 deletion src/precompile_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ static int precompile_enq_specialization_(jl_method_instance_t *mi, void *closur
jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred);
if (inferred &&
(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL || inferred == jl_nothing ||
((jl_is_string(inferred) || jl_is_code_info(inferred)) && jl_ir_inlining_cost(inferred) == UINT16_MAX))) {
((jl_is_string(inferred) || jl_is_code_info(inferred) || jl_is_uint8(inferred)) && jl_ir_inlining_cost(inferred) == UINT16_MAX))) {
do_compile = 1;
}
else if (jl_atomic_load_relaxed(&codeinst->invoke) != NULL || jl_atomic_load_relaxed(&codeinst->precompile)) {
Expand Down
20 changes: 13 additions & 7 deletions src/staticdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -901,30 +901,36 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_
}
}
jl_value_t *inferred = jl_atomic_load_relaxed(&ci->inferred);
if (inferred && inferred != jl_nothing) { // disregard if there is nothing here to delete (e.g. builtins, unspecialized)
if (inferred && inferred != jl_nothing && !jl_is_uint8(inferred)) { // disregard if there is nothing here to delete (e.g. builtins, unspecialized)
jl_method_t *def = mi->def.method;
if (jl_is_method(def)) { // don't delete toplevel code
int is_relocatable = !s->incremental || jl_is_code_info(inferred) ||
(jl_is_string(inferred) && jl_string_len(inferred) > 0 && jl_string_data(inferred)[jl_string_len(inferred) - 1]);
int discard = 0;
if (!is_relocatable) {
inferred = jl_nothing;
discard = 1;
}
else if (def->source == NULL) {
// don't delete code from optimized opaque closures that can't be reconstructed (and builtins)
}
else if (jl_atomic_load_relaxed(&ci->max_world) != ~(size_t)0 || // delete all code that cannot run
jl_atomic_load_relaxed(&ci->invoke) == jl_fptr_const_return) { // delete all code that just returns a constant
inferred = jl_nothing;
discard = 1;
}
else if (native_functions && // don't delete any code if making a ji file
(ci->owner == jl_nothing) && // don't delete code for external interpreters
!effects_foldable(jl_atomic_load_relaxed(&ci->ipo_purity_bits)) && // don't delete code we may want for irinterp
jl_ir_inlining_cost(inferred) == UINT16_MAX) { // don't delete inlineable code
// delete the code now: if we thought it was worth keeping, it would have been converted to object code
inferred = jl_nothing;
discard = 1;
}
if (inferred == jl_nothing) {
record_field_change((jl_value_t**)&ci->inferred, jl_nothing);
if (discard) {
// keep only the inlining cost, so inference can later decide if it is worth getting the source back
if (jl_is_string(inferred) || jl_is_code_info(inferred))
inferred = jl_box_uint8(jl_encode_inlining_cost(jl_ir_inlining_cost(inferred)));
else
inferred = jl_nothing;
record_field_change((jl_value_t**)&ci->inferred, inferred);
}
else if (s->incremental && jl_is_string(inferred)) {
// New roots for external methods
Expand Down Expand Up @@ -2687,7 +2693,7 @@ static void strip_specializations_(jl_method_instance_t *mi)
jl_code_instance_t *codeinst = jl_atomic_load_relaxed(&mi->cache);
while (codeinst) {
jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred);
if (inferred && inferred != jl_nothing) {
if (inferred && inferred != jl_nothing && !jl_is_uint8(inferred)) {
if (jl_options.strip_ir) {
record_field_change((jl_value_t**)&codeinst->inferred, jl_nothing);
}
Expand Down