Skip to content

Commit

Permalink
change CodeInfo compression from Array to String
Browse files Browse the repository at this point in the history
String is simpler and smaller (since we do not yet have a Buffer{UInt8}
type, which might be almost as simple) making it easier to manipulate.
Combined with the previous changes, this lets us eliminate many more
relocations from the image (previously every Array->data), which is
quite a few megabytes of array header objects simply gone.

MASTER:
sysimg size breakdown:
     sys data: 79207516
  isbits data: 58603452
      symbols:   463610
    tags list:  1078555
   reloc list:  4491653
    gvar list:   118848
    fptr list:   214000
.text          11109176
.data         144184704

BEFORE (moving existing Strings):
sysimg size breakdown:
     sys data: 75146604
  isbits data: 62679532
      symbols:   463411
    tags list:  1015525
   reloc list:  4481370
    gvar list:   118688
    fptr list:   213952
.text          11129192
.data         144126152

AFTER (making CodeInfo into Strings):
     sys data: 71348124
  isbits data: 63789244
      symbols:   463411
    tags list:   933984
   reloc list:  4398377
    gvar list:   118688
    fptr list:   213904
.text          11132968
.data         141272824
  • Loading branch information
vtjnash committed May 6, 2023
1 parent 160d261 commit 4be81cd
Show file tree
Hide file tree
Showing 18 changed files with 103 additions and 90 deletions.
2 changes: 1 addition & 1 deletion base/compiler/inferencestate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ function IRInterpretationState(interp::AbstractInterpreter,
code::CodeInstance, mi::MethodInstance, argtypes::Vector{Any}, world::UInt)
@assert code.def === mi
src = @atomic :monotonic code.inferred
if isa(src, Vector{UInt8})
if isa(src, String)
src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src)::CodeInfo
else
isa(src, CodeInfo) || return nothing
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const TOP_TUPLE = GlobalRef(Core, :tuple)
const InlineCostType = UInt16
const MAX_INLINE_COST = typemax(InlineCostType)
const MIN_INLINE_COST = InlineCostType(10)
const MaybeCompressed = Union{CodeInfo, Vector{UInt8}}
const MaybeCompressed = Union{CodeInfo, String}

is_inlineable(@nospecialize src::MaybeCompressed) =
ccall(:jl_ir_inlining_cost, InlineCostType, (Any,), src) != MAX_INLINE_COST
Expand Down
6 changes: 3 additions & 3 deletions base/compiler/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ end
function may_have_fcalls(m::Method)
isdefined(m, :source) || return true
src = m.source
isa(src, CodeInfo) || isa(src, Vector{UInt8}) || return true
isa(src, MaybeCompressed) || return true
return ccall(:jl_ir_flag_has_fcall, Bool, (Any,), src)
end

Expand Down Expand Up @@ -982,8 +982,8 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any},
return resolve_todo(mi, match, argtypes, info, flag, state; invokesig)
end

function retrieve_ir_for_inlining(mi::MethodInstance, src::Array{UInt8, 1})
src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src::Vector{UInt8})::CodeInfo
function retrieve_ir_for_inlining(mi::MethodInstance, src::String)
src = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def, C_NULL, src)::CodeInfo
return inflate_ir!(src, mi)
end
retrieve_ir_for_inlining(mi::MethodInstance, src::CodeInfo) = inflate_ir(src, mi)
Expand Down
16 changes: 11 additions & 5 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,15 @@ function CodeInstance(interp::AbstractInterpreter, result::InferenceResult,
const_flags = 0x00
end
end
relocatability = isa(inferred_result, Vector{UInt8}) ? inferred_result[end] :
inferred_result === nothing ? UInt8(1) : UInt8(0)
# relocatability = isa(inferred_result, Vector{UInt8}) ? inferred_result[end] : UInt8(0)
relocatability = 0x0
if isa(inferred_result, String)
t = @_gc_preserve_begin inferred_result
relocatability = unsafe_load(unsafe_convert(Ptr{UInt8}, inferred_result), Core.sizeof(inferred_result))
@_gc_preserve_end t
elseif inferred_result === nothing
relocatability = 0x1
end
# relocatability = isa(inferred_result, String) ? inferred_result[end] : UInt8(0)
return CodeInstance(result.linfo,
widenconst(result_type), rettype_const, inferred_result,
const_flags, first(valid_worlds), last(valid_worlds),
Expand All @@ -352,7 +358,7 @@ function maybe_compress_codeinfo(interp::AbstractInterpreter, linfo::MethodInsta
nslots = length(ci.slotflags)
resize!(ci.slottypes::Vector{Any}, nslots)
resize!(ci.slotnames, nslots)
return ccall(:jl_compress_ir, Vector{UInt8}, (Any, Any), def, ci)
return ccall(:jl_compress_ir, String, (Any, Any), def, ci)
else
return ci
end
Expand Down Expand Up @@ -1031,7 +1037,7 @@ function typeinf_ext(interp::AbstractInterpreter, mi::MethodInstance)
inf.rettype = code.rettype
end
return inf
elseif isa(inf, Vector{UInt8})
elseif isa(inf, String)
ccall(:jl_typeinf_timing_end, Cvoid, (UInt64,), start_time)
inf = _uncompressed_ir(code, inf)
return inf
Expand Down
2 changes: 1 addition & 1 deletion base/compiler/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function retrieve_code_info(linfo::MethodInstance, world::UInt)
if src === nothing
# can happen in images built with --strip-ir
return nothing
elseif isa(src, Array{UInt8,1})
elseif isa(src, String)
c = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, src)
else
c = copy(src::CodeInfo)
Expand Down
4 changes: 2 additions & 2 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1163,8 +1163,8 @@ uncompressed_ir(m::Method) = isdefined(m, :source) ? _uncompressed_ir(m, m.sourc
isdefined(m, :generator) ? error("Method is @generated; try `code_lowered` instead.") :
error("Code for this Method is not available.")
_uncompressed_ir(m::Method, s::CodeInfo) = copy(s)
_uncompressed_ir(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, s)::CodeInfo
_uncompressed_ir(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompress_ir, Any, (Any, Any, Any), ci.def.def::Method, ci, s)::CodeInfo
_uncompressed_ir(m::Method, s::String) = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, s)::CodeInfo
_uncompressed_ir(ci::Core.CodeInstance, s::String) = ccall(:jl_uncompress_ir, Any, (Any, Any, Any), ci.def.def::Method, ci, s)::CodeInfo
# for backwards compat
const uncompressed_ast = uncompressed_ir
const _uncompressed_ast = _uncompressed_ir
Expand Down
15 changes: 8 additions & 7 deletions src/aotcompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance
if ((jl_value_t*)*src_out == jl_nothing)
*src_out = NULL;
if (*src_out && jl_is_method(def))
*src_out = jl_uncompress_ir(def, codeinst, (jl_array_t*)*src_out);
*src_out = jl_uncompress_ir(def, codeinst, (jl_value_t*)*src_out);
}
if (*src_out == NULL || !jl_is_code_info(*src_out)) {
if (cgparams.lookup != jl_rettype_inferred) {
Expand Down Expand Up @@ -2028,17 +2028,18 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz
jl_value_t *jlrettype = (jl_value_t*)jl_any_type;
jl_code_info_t *src = NULL;
JL_GC_PUSH2(&src, &jlrettype);
if (jl_is_method(mi->def.method) && mi->def.method->source != NULL && jl_ir_flag_inferred((jl_array_t*)mi->def.method->source)) {
if (jl_is_method(mi->def.method) && mi->def.method->source != NULL && mi->def.method->source != jl_nothing && jl_ir_flag_inferred(mi->def.method->source)) {
src = (jl_code_info_t*)mi->def.method->source;
if (src && !jl_is_code_info(src))
src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src);
} else {
src = jl_uncompress_ir(mi->def.method, NULL, (jl_value_t*)src);
}
else {
jl_value_t *ci = jl_rettype_inferred(mi, world, world);
if (ci != jl_nothing) {
jl_code_instance_t *codeinst = (jl_code_instance_t*)ci;
src = (jl_code_info_t*)jl_atomic_load_relaxed(&codeinst->inferred);
if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method))
src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src);
src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src);
jlrettype = codeinst->rettype;
}
if (!src || (jl_value_t*)src == jl_nothing) {
Expand All @@ -2047,8 +2048,8 @@ void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, siz
jlrettype = src->rettype;
else if (jl_is_method(mi->def.method)) {
src = mi->def.method->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)mi->def.method->source;
if (src && !jl_is_code_info(src) && jl_is_method(mi->def.method))
src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src);
if (src && (jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method))
src = jl_uncompress_ir(mi->def.method, NULL, (jl_value_t*)src);
}
// TODO: use mi->uninferred
}
Expand Down
12 changes: 6 additions & 6 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5252,7 +5252,7 @@ static std::pair<Function*, Function*> get_oc_function(jl_codectx_t &ctx, jl_met
}
++EmittedOpaqueClosureFunctions;

ir = jl_uncompress_ir(closure_method, ci, (jl_array_t*)inferred);
ir = jl_uncompress_ir(closure_method, ci, (jl_value_t*)inferred);

// TODO: Emit this inline and outline it late using LLVM's coroutine support.
orc::ThreadSafeModule closure_m = jl_create_ts_module(
Expand Down Expand Up @@ -8648,7 +8648,7 @@ jl_llvm_functions_t jl_emit_codeinst(
return jl_emit_oc_wrapper(m, params, codeinst->def, codeinst->rettype);
}
if (src && (jl_value_t*)src != jl_nothing && jl_is_method(def))
src = jl_uncompress_ir(def, codeinst, (jl_array_t*)src);
src = jl_uncompress_ir(def, codeinst, (jl_value_t*)src);
if (!src || !jl_is_code_info(src)) {
JL_GC_POP();
m = orc::ThreadSafeModule();
Expand Down Expand Up @@ -8677,7 +8677,7 @@ jl_llvm_functions_t jl_emit_codeinst(
}

if (params.world) {// don't alter `inferred` when the code is not directly being used
auto inferred = jl_atomic_load_relaxed(&codeinst->inferred);
jl_value_t *inferred = jl_atomic_load_relaxed(&codeinst->inferred);
// don't change inferred state
if (inferred) {
jl_method_t *def = codeinst->def->def.method;
Expand All @@ -8689,8 +8689,8 @@ jl_llvm_functions_t jl_emit_codeinst(
if (inferred != (jl_value_t*)src) {
if (jl_is_method(def)) {
src = (jl_code_info_t*)jl_compress_ir(def, src);
assert(jl_typetagis(src, jl_array_uint8_type));
codeinst->relocatability = ((uint8_t*)jl_array_data(src))[jl_array_len(src)-1];
assert(jl_is_string(src));
codeinst->relocatability = jl_string_data(src)[jl_string_len(src)-1];
}
jl_atomic_store_release(&codeinst->inferred, (jl_value_t*)src);
jl_gc_wb(codeinst, src);
Expand All @@ -8701,7 +8701,7 @@ jl_llvm_functions_t jl_emit_codeinst(
inferred != jl_nothing &&
// don't delete inlineable code, unless it is constant
(jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr ||
(jl_ir_inlining_cost((jl_array_t*)inferred) == UINT16_MAX)) &&
(jl_ir_inlining_cost(inferred) == UINT16_MAX)) &&
// don't delete code when generating a precompile file
!(params.imaging || jl_options.incremental)) {
// if not inlineable, code won't be needed again
Expand Down
2 changes: 1 addition & 1 deletion src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ JL_DLLEXPORT jl_value_t *jl_rettype_inferred(jl_method_instance_t *mi, size_t mi
while (codeinst) {
if (codeinst->min_world <= min_world && max_world <= codeinst->max_world) {
jl_value_t *code = jl_atomic_load_relaxed(&codeinst->inferred);
if (code && (code == jl_nothing || jl_ir_flag_inferred((jl_array_t*)code)))
if (code && (code == jl_nothing || jl_ir_flag_inferred(code)))
return (jl_value_t*)codeinst;
}
codeinst = jl_atomic_load_relaxed(&codeinst->next);
Expand Down
4 changes: 2 additions & 2 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *mi, size_t world)
}
if (src && (jl_value_t*)src != jl_nothing) {
JL_GC_PUSH1(&src);
src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src);
src = jl_uncompress_ir(mi->def.method, NULL, (jl_value_t*)src);
jl_atomic_store_release(&mi->uninferred, (jl_value_t*)src);
jl_gc_wb(mi, src);
JL_GC_POP();
Expand Down Expand Up @@ -703,7 +703,7 @@ JL_DLLEXPORT jl_callptr_t jl_fptr_interpret_call_addr = &jl_fptr_interpret_call;
jl_value_t *jl_interpret_opaque_closure(jl_opaque_closure_t *oc, jl_value_t **args, size_t nargs)
{
jl_method_t *source = oc->source;
jl_code_info_t *code = jl_uncompress_ir(source, NULL, (jl_array_t*)source->source);
jl_code_info_t *code = jl_uncompress_ir(source, NULL, (jl_value_t*)source->source);
interpreter_state *s;
unsigned nroots = jl_source_nslots(code) + jl_source_nssavalues(code) + 2;
jl_task_t *ct = jl_current_task;
Expand Down
50 changes: 26 additions & 24 deletions src/ircode.c
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,9 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED

// --- entry points ---

JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
typedef jl_value_t jl_string_t; // for local expressibility

JL_DLLEXPORT jl_string_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
{
JL_TIMING(AST_COMPRESS, AST_COMPRESS);
JL_LOCK(&m->writelock); // protect the roots array (Might GC)
Expand Down Expand Up @@ -841,7 +843,7 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
write_uint8(s.s, s.relocatability);

ios_flush(s.s);
jl_array_t *v = jl_take_buffer(&dest);
jl_string_t *v = jl_pchar_to_string(s.s->buf, s.s->size);
ios_close(s.s);
if (jl_array_len(m->roots) == 0) {
m->roots = NULL;
Expand All @@ -854,19 +856,19 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code)
return v;
}

JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_array_t *data)
JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t *metadata, jl_string_t *data)
{
if (jl_is_code_info(data))
return (jl_code_info_t*)data;
JL_TIMING(AST_UNCOMPRESS, AST_UNCOMPRESS);
JL_LOCK(&m->writelock); // protect the roots array (Might GC)
assert(jl_is_method(m));
assert(jl_typetagis(data, jl_array_uint8_type));
assert(jl_is_string(data));
size_t i;
ios_t src;
ios_mem(&src, 0);
ios_setbuf(&src, (char*)data->data, jl_array_len(data), 0);
src.size = jl_array_len(data);
ios_setbuf(&src, (char*)jl_string_data(data), jl_string_len(data), 0);
src.size = jl_string_len(data);
int en = jl_gc_enable(0); // Might GC
jl_ircode_state s = {
&src,
Expand Down Expand Up @@ -939,42 +941,42 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t
return code;
}

JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_array_t *data)
JL_DLLEXPORT uint8_t jl_ir_flag_inferred(jl_string_t *data)
{
if (jl_is_code_info(data))
return ((jl_code_info_t*)data)->inferred;
assert(jl_typetagis(data, jl_array_uint8_type));
assert(jl_is_string(data));
jl_code_info_flags_t flags;
flags.packed = ((uint8_t*)data->data)[0];
flags.packed = jl_string_data(data)[0];
return flags.bits.inferred;
}

JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_array_t *data)
JL_DLLEXPORT uint8_t jl_ir_flag_inlining(jl_string_t *data)
{
if (jl_is_code_info(data))
return ((jl_code_info_t*)data)->inlining;
assert(jl_typetagis(data, jl_array_uint8_type));
assert(jl_is_string(data));
jl_code_info_flags_t flags;
flags.packed = ((uint8_t*)data->data)[0];
flags.packed = jl_string_data(data)[0];
return flags.bits.inlining;
}

JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_array_t *data)
JL_DLLEXPORT uint8_t jl_ir_flag_has_fcall(jl_string_t *data)
{
if (jl_is_code_info(data))
return ((jl_code_info_t*)data)->has_fcall;
assert(jl_typetagis(data, jl_array_uint8_type));
assert(jl_is_string(data));
jl_code_info_flags_t flags;
flags.packed = ((uint8_t*)data->data)[0];
flags.packed = jl_string_data(data)[0];
return flags.bits.has_fcall;
}

JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_array_t *data)
JL_DLLEXPORT uint16_t jl_ir_inlining_cost(jl_string_t *data)
{
if (jl_is_code_info(data))
return ((jl_code_info_t*)data)->inlining_cost;
assert(jl_typetagis(data, jl_array_uint8_type));
uint16_t res = jl_load_unaligned_i16((char*)data->data + 2);
assert(jl_is_string(data));
uint16_t res = jl_load_unaligned_i16(jl_string_data(data) + 2);
return res;
}

Expand Down Expand Up @@ -1004,26 +1006,26 @@ JL_DLLEXPORT jl_value_t *jl_compress_argnames(jl_array_t *syms)
return str;
}

JL_DLLEXPORT ssize_t jl_ir_nslots(jl_array_t *data)
JL_DLLEXPORT ssize_t jl_ir_nslots(jl_value_t *data)
{
if (jl_is_code_info(data)) {
jl_code_info_t *func = (jl_code_info_t*)data;
return jl_array_len(func->slotnames);
}
else {
assert(jl_typetagis(data, jl_array_uint8_type));
int nslots = jl_load_unaligned_i32((char*)data->data + 2 + sizeof(uint16_t));
assert(jl_is_string(data));
int nslots = jl_load_unaligned_i32(jl_string_data(data) + 2 + sizeof(uint16_t));
return nslots;
}
}

JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_array_t *data, size_t i)
JL_DLLEXPORT uint8_t jl_ir_slotflag(jl_string_t *data, size_t i)
{
assert(i < jl_ir_nslots(data));
if (jl_is_code_info(data))
return ((uint8_t*)((jl_code_info_t*)data)->slotflags->data)[i];
assert(jl_typetagis(data, jl_array_uint8_type));
return ((uint8_t*)data->data)[2 + sizeof(uint16_t) + sizeof(int32_t) + i];
assert(jl_is_string(data));
return jl_string_data(data)[2 + sizeof(uint16_t) + sizeof(int32_t) + i];
}

JL_DLLEXPORT jl_array_t *jl_uncompress_argnames(jl_value_t *syms)
Expand Down
6 changes: 3 additions & 3 deletions src/jitlayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ jl_code_instance_t *jl_generate_fptr_impl(jl_method_instance_t *mi JL_PROPAGATES
if ((jl_value_t*)src == jl_nothing)
src = NULL;
else if (jl_is_method(mi->def.method))
src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src);
src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src);
}
else {
// identify whether this is an invalidated method that is being recompiled
Expand Down Expand Up @@ -546,7 +546,7 @@ void jl_generate_fptr_for_unspecialized_impl(jl_code_instance_t *unspec)
src = jl_code_for_staged(unspec->def, ~(size_t)0);
}
if (src && (jl_value_t*)src != jl_nothing)
src = jl_uncompress_ir(def, NULL, (jl_array_t*)src);
src = jl_uncompress_ir(def, NULL, (jl_value_t*)src);
}
else {
src = (jl_code_info_t*)jl_atomic_load_relaxed(&unspec->def->uninferred);
Expand Down Expand Up @@ -606,7 +606,7 @@ jl_value_t *jl_dump_method_asm_impl(jl_method_instance_t *mi, size_t world,
src = def->generator ? jl_code_for_staged(mi, world) : (jl_code_info_t*)def->source;
}
if (src && (jl_value_t*)src != jl_nothing)
src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src);
src = jl_uncompress_ir(mi->def.method, codeinst, (jl_value_t*)src);
}
fptr = (uintptr_t)jl_atomic_load_acquire(&codeinst->invoke);
specfptr = (uintptr_t)jl_atomic_load_relaxed(&codeinst->specptr.fptr);
Expand Down
Loading

0 comments on commit 4be81cd

Please sign in to comment.