diff --git a/base/boot.jl b/base/boot.jl index 1469e4d5c8e69..abdd7987ce901 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -418,9 +418,9 @@ eval(Core, :(LineInfoNode(mod::Module, @nospecialize(method), file::Symbol, line $(Expr(:new, :LineInfoNode, :mod, :method, :file, :line, :inlined_at)))) eval(Core, :(CodeInstance(mi::MethodInstance, @nospecialize(rettype), @nospecialize(inferred_const), @nospecialize(inferred), const_flags::Int32, - min_world::UInt, max_world::UInt) = - ccall(:jl_new_codeinst, Ref{CodeInstance}, (Any, Any, Any, Any, Int32, UInt, UInt), - mi, rettype, inferred_const, inferred, const_flags, min_world, max_world))) + min_world::UInt, max_world::UInt, relocatability::UInt8) = + ccall(:jl_new_codeinst, Ref{CodeInstance}, (Any, Any, Any, Any, Int32, UInt, UInt, UInt8), + mi, rettype, inferred_const, inferred, const_flags, min_world, max_world, relocatability))) eval(Core, :(Const(@nospecialize(v)) = $(Expr(:new, :Const, :v)))) eval(Core, :(PartialStruct(@nospecialize(typ), fields::Array{Any, 1}) = $(Expr(:new, :PartialStruct, :typ, :fields)))) eval(Core, :(PartialOpaque(@nospecialize(typ), @nospecialize(env), isva::Bool, parent::MethodInstance, source::Method) = $(Expr(:new, :PartialOpaque, :typ, :env, :isva, :parent, :source)))) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 5b82a91dd42ec..749462b25fa0b 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -278,7 +278,7 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) end function CodeInstance(result::InferenceResult, @nospecialize(inferred_result), - valid_worlds::WorldRange) + valid_worlds::WorldRange, relocatability::UInt8) local const_flags::Int32 result_type = result.result @assert !(result_type isa LimitedAccuracy) @@ -310,7 +310,7 @@ function CodeInstance(result::InferenceResult, @nospecialize(inferred_result), end return CodeInstance(result.linfo, widenconst(result_type), rettype_const, inferred_result, - const_flags, first(valid_worlds), last(valid_worlds)) + const_flags, first(valid_worlds), last(valid_worlds), relocatability) end # For the NativeInterpreter, we don't need to do an actual cache query to know @@ -384,7 +384,8 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult) # TODO: also don't store inferred code if we've previously decided to interpret this function if !already_inferred inferred_result = transform_result_for_cache(interp, linfo, valid_worlds, result.src) - code_cache(interp)[linfo] = CodeInstance(result, inferred_result, valid_worlds) + relocatability = isa(inferred_result, Vector{UInt8}) ? inferred_result[end] : UInt8(0) + code_cache(interp)[linfo] = CodeInstance(result, inferred_result, valid_worlds, relocatability) end unlock_mi_inference(interp, linfo) nothing diff --git a/src/codegen.cpp b/src/codegen.cpp index 22db7ecbaebcc..2580f693356ea 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7795,8 +7795,11 @@ jl_compile_result_t jl_emit_codeinst( jl_options.debug_level > 1) { // update the stored code if (codeinst->inferred != (jl_value_t*)src) { - if (jl_is_method(def)) + if (jl_is_method(def)) { src = (jl_code_info_t*)jl_compress_ir(def, src); + assert(jl_typeis(src, jl_array_uint8_type)); + codeinst->relocatability = ((uint8_t*)jl_array_data(src))[jl_array_len(src)-1]; + } codeinst->inferred = (jl_value_t*)src; jl_gc_wb(codeinst, src); } diff --git a/src/common_symbols1.inc b/src/common_symbols1.inc index 80038837be0c4..7d445289e80fa 100644 --- a/src/common_symbols1.inc +++ b/src/common_symbols1.inc @@ -97,4 +97,3 @@ jl_symbol("undef"), jl_symbol("sizeof"), jl_symbol("String"), jl_symbol("namedtuple.jl"), -jl_symbol("pop"), diff --git a/src/common_symbols2.inc b/src/common_symbols2.inc index a28f1ef50af24..c9f4e41b83e33 100644 --- a/src/common_symbols2.inc +++ b/src/common_symbols2.inc @@ -1,3 +1,4 @@ +jl_symbol("pop"), jl_symbol("inbounds"), jl_symbol("strings/string.jl"), jl_symbol("Ref"), @@ -251,4 +252,3 @@ jl_symbol("GitError"), jl_symbol("zeros"), jl_symbol("InexactError"), jl_symbol("LogLevel"), -jl_symbol("between"), diff --git a/src/dump.c b/src/dump.c index afc372696a8f2..dc551580a59aa 100644 --- a/src/dump.c +++ b/src/dump.c @@ -528,6 +528,7 @@ static void jl_serialize_code_instance(jl_serializer_state *s, jl_code_instance_ jl_serialize_value(s, NULL); jl_serialize_value(s, jl_any_type); } + write_uint8(s->s, codeinst->relocatability); jl_serialize_code_instance(s, codeinst->next, skip_partial_opaque); } @@ -705,6 +706,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li jl_serialize_value(s, (jl_value_t*)m->slot_syms); jl_serialize_value(s, (jl_value_t*)m->roots); jl_serialize_value(s, (jl_value_t*)m->root_blocks); + write_int32(s->s, m->nroots_sysimg); jl_serialize_value(s, (jl_value_t*)m->ccallable); jl_serialize_value(s, (jl_value_t*)m->source); jl_serialize_value(s, (jl_value_t*)m->unspecialized); @@ -1577,6 +1579,7 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_ m->root_blocks = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&m->root_blocks); if (m->root_blocks) jl_gc_wb(m, m->root_blocks); + m->nroots_sysimg = read_int32(s->s); m->ccallable = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&m->ccallable); if (m->ccallable) { jl_gc_wb(m, m->ccallable); @@ -1661,6 +1664,7 @@ static jl_value_t *jl_deserialize_value_code_instance(jl_serializer_state *s, jl codeinst->invoke = jl_fptr_const_return; if ((flags >> 3) & 1) codeinst->precompile = 1; + codeinst->relocatability = read_uint8(s->s); codeinst->next = (jl_code_instance_t*)jl_deserialize_value(s, (jl_value_t**)&codeinst->next); jl_gc_wb(codeinst, codeinst->next); if (validate) { diff --git a/src/gf.c b/src/gf.c index b111c3f2c0b86..d4a5df81a8d1a 100644 --- a/src/gf.c +++ b/src/gf.c @@ -206,7 +206,7 @@ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_value_t *typ JL_DLLEXPORT jl_code_instance_t* jl_new_codeinst( jl_method_instance_t *mi, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, - int32_t const_flags, size_t min_world, size_t max_world); + int32_t const_flags, size_t min_world, size_t max_world, uint8_t relocatability); JL_DLLEXPORT void jl_mi_cache_insert(jl_method_instance_t *mi JL_ROOTING_ARGUMENT, jl_code_instance_t *ci JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED); @@ -243,7 +243,7 @@ jl_datatype_t *jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_a jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, jl_nothing, jl_nothing, - 0, 1, ~(size_t)0); + 0, 1, ~(size_t)0, 0); jl_mi_cache_insert(mi, codeinst); codeinst->specptr.fptr1 = fptr; codeinst->invoke = jl_fptr_args; @@ -366,7 +366,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( } codeinst = jl_new_codeinst( mi, rettype, NULL, NULL, - 0, min_world, max_world); + 0, min_world, max_world, 0); jl_mi_cache_insert(mi, codeinst); return codeinst; } @@ -374,7 +374,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( jl_method_instance_t *mi, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, - int32_t const_flags, size_t min_world, size_t max_world + int32_t const_flags, size_t min_world, size_t max_world, uint8_t relocatability /*, jl_array_t *edges, int absolute_max*/) { jl_task_t *ct = jl_current_task; @@ -399,6 +399,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_new_codeinst( codeinst->isspecsig = 0; codeinst->precompile = 0; codeinst->next = NULL; + codeinst->relocatability = relocatability; return codeinst; } @@ -2008,7 +2009,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t if (unspec && jl_atomic_load_relaxed(&unspec->invoke)) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, - 0, 1, ~(size_t)0); + 0, 1, ~(size_t)0, 0); codeinst->isspecsig = 0; codeinst->specptr = unspec->specptr; codeinst->rettype_const = unspec->rettype_const; @@ -2026,7 +2027,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t if (!jl_code_requires_compiler(src)) { jl_code_instance_t *codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, - 0, 1, ~(size_t)0); + 0, 1, ~(size_t)0, 0); codeinst->invoke = jl_fptr_interpret_call; jl_mi_cache_insert(mi, codeinst); record_precompile_statement(mi); @@ -2061,7 +2062,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t return ucache; } codeinst = jl_new_codeinst(mi, (jl_value_t*)jl_any_type, NULL, NULL, - 0, 1, ~(size_t)0); + 0, 1, ~(size_t)0, 0); codeinst->isspecsig = 0; codeinst->specptr = ucache->specptr; codeinst->rettype_const = ucache->rettype_const; diff --git a/src/ircode.c b/src/ircode.c index 3bb60eb7b24d2..5be83ed4caac3 100644 --- a/src/ircode.c +++ b/src/ircode.c @@ -26,30 +26,37 @@ typedef struct { // method we're compressing for jl_method_t *method; jl_ptls_t ptls; + uint8_t relocatability; } jl_ircode_state; // --- encoding --- #define jl_encode_value(s, v) jl_encode_value_((s), (jl_value_t*)(v), 0) -static int literal_val_id(jl_ircode_state *s, jl_value_t *v) JL_GC_DISABLED +static void tagged_root(rle_reference *rr, jl_ircode_state *s, int i) +{ + if (!get_root_reference(rr, s->method, i)) + s->relocatability = 0; +} + +static void literal_val_id(rle_reference *rr, jl_ircode_state *s, jl_value_t *v) JL_GC_DISABLED { jl_array_t *rs = s->method->roots; int i, l = jl_array_len(rs); if (jl_is_symbol(v) || jl_is_concrete_type(v)) { for (i = 0; i < l; i++) { if (jl_array_ptr_ref(rs, i) == v) - return i; + return tagged_root(rr, s, i); } } else { for (i = 0; i < l; i++) { if (jl_egal(jl_array_ptr_ref(rs, i), v)) - return i; + return tagged_root(rr, s, i); } } jl_add_method_root(s->method, jl_precompile_toplevel_module, v); - return jl_array_len(rs) - 1; + return tagged_root(rr, s, jl_array_len(rs) - 1); } static void jl_encode_int32(jl_ircode_state *s, int32_t x) @@ -67,6 +74,7 @@ static void jl_encode_int32(jl_ircode_state *s, int32_t x) static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) JL_GC_DISABLED { size_t i; + rle_reference rr; if (v == NULL) { write_uint8(s->s, TAG_NULL); @@ -321,8 +329,13 @@ static void jl_encode_value_(jl_ircode_state *s, jl_value_t *v, int as_literal) if (!as_literal && !(jl_is_uniontype(v) || jl_is_newvarnode(v) || jl_is_tuple(v) || jl_is_linenode(v) || jl_is_upsilonnode(v) || jl_is_pinode(v) || jl_is_slot(v) || jl_is_ssavalue(v))) { - int id = literal_val_id(s, v); + literal_val_id(&rr, s, v); + int id = rr.index; assert(id >= 0); + if (rr.key) { + write_uint8(s->s, TAG_RELOC_METHODROOT); + write_int64(s->s, rr.key); + } if (id < 256) { write_uint8(s->s, TAG_METHODROOT); write_uint8(s->s, id); @@ -577,6 +590,7 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED assert(!ios_eof(s->s)); jl_value_t *v; size_t i, n; + uint64_t key; uint8_t tag = read_uint8(s->s); if (tag > LAST_TAG) return jl_deser_tag(tag); @@ -585,10 +599,15 @@ static jl_value_t *jl_decode_value(jl_ircode_state *s) JL_GC_DISABLED case 0: tag = read_uint8(s->s); return jl_deser_tag(tag); + case TAG_RELOC_METHODROOT: + key = read_uint64(s->s); + tag = read_uint8(s->s); + assert(tag == TAG_METHODROOT || tag == TAG_LONG_METHODROOT); + return lookup_root(s->method, key, tag == TAG_METHODROOT ? read_uint8(s->s) : read_uint16(s->s)); case TAG_METHODROOT: - return jl_array_ptr_ref(s->method->roots, read_uint8(s->s)); + return lookup_root(s->method, 0, read_uint8(s->s)); case TAG_LONG_METHODROOT: - return jl_array_ptr_ref(s->method->roots, read_uint16(s->s)); + return lookup_root(s->method, 0, read_uint16(s->s)); case TAG_SVEC: JL_FALLTHROUGH; case TAG_LONG_SVEC: return jl_decode_value_svec(s, tag); case TAG_COMMONSYM: @@ -706,7 +725,8 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) jl_ircode_state s = { &dest, m, - jl_current_task->ptls + jl_current_task->ptls, + 1 }; jl_code_info_flags_t flags = code_info_flags(code->pure, code->propagate_inbounds, code->inlineable, code->inferred, code->constprop); @@ -756,6 +776,8 @@ JL_DLLEXPORT jl_array_t *jl_compress_ir(jl_method_t *m, jl_code_info_t *code) ios_write(s.s, (char*)jl_array_data(code->codelocs), nstmt * sizeof(int32_t)); } + write_uint8(s.s, s.relocatability); + ios_flush(s.s); jl_array_t *v = jl_take_buffer(&dest); ios_close(s.s); @@ -786,7 +808,8 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t jl_ircode_state s = { &src, m, - jl_current_task->ptls + jl_current_task->ptls, + 1 }; jl_code_info_t *code = jl_new_code_info_uninit(); @@ -831,6 +854,8 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t ios_readall(s.s, (char*)jl_array_data(code->codelocs), nstmt * sizeof(int32_t)); } + (void) read_uint8(s.s); // relocatability + assert(ios_getc(s.s) == -1); ios_close(s.s); JL_GC_PUSH1(&code); diff --git a/src/jltypes.c b/src/jltypes.c index 0042388660362..8e25ff9fa9a5f 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2393,7 +2393,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_method_type = jl_new_datatype(jl_symbol("Method"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(27, + jl_perm_symsvec(28, "name", "module", "file", @@ -2410,6 +2410,7 @@ void jl_init_types(void) JL_GC_DISABLED "generator", // !const "roots", // !const "root_blocks", // !const + "nroots_sysimg", "ccallable", // !const "invokes", // !const "recursion_relation", // !const @@ -2421,7 +2422,7 @@ void jl_init_types(void) JL_GC_DISABLED "pure", "is_for_opaque_closure", "constprop"), - jl_svec(27, + jl_svec(28, jl_symbol_type, jl_module_type, jl_symbol_type, @@ -2438,6 +2439,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, jl_array_any_type, jl_array_uint64_type, + jl_int32_type, jl_simplevector_type, jl_any_type, jl_any_type, @@ -2483,7 +2485,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_code_instance_type = jl_new_datatype(jl_symbol("CodeInstance"), core, jl_any_type, jl_emptysvec, - jl_perm_symsvec(11, + jl_perm_symsvec(12, "def", "next", "min_world", @@ -2493,8 +2495,9 @@ void jl_init_types(void) JL_GC_DISABLED "inferred", //"edges", //"absolute_max", - "isspecsig", "precompile", "invoke", "specptr"), // function object decls - jl_svec(11, + "isspecsig", "precompile", "invoke", "specptr", // function object decls + "relocatability"), + jl_svec(12, jl_method_instance_type, jl_any_type, jl_ulong_type, @@ -2506,7 +2509,8 @@ void jl_init_types(void) JL_GC_DISABLED //jl_bool_type, jl_bool_type, jl_bool_type, - jl_any_type, jl_any_type), // fptrs + jl_any_type, jl_any_type, // fptrs + jl_uint8_type), jl_emptysvec, 0, 1, 1); jl_svecset(jl_code_instance_type->types, 1, jl_code_instance_type); diff --git a/src/julia.h b/src/julia.h index 5acf070f7a25b..d60d3c20f9e60 100644 --- a/src/julia.h +++ b/src/julia.h @@ -294,6 +294,7 @@ typedef struct _jl_method_t { // Identify roots by module-of-origin. We only track the module for roots added during incremental compilation. // May be NULL if no external roots have been added, otherwise it's a Vector{UInt64} jl_array_t *root_blocks; // RLE (build_id, offset) pairs (even/odd indexing) + int32_t nroots_sysimg; // # of roots stored in the system image jl_svec_t *ccallable; // svec(rettype, sig) if a ccallable entry point is requested for this // cache of specializations of this method for invoke(), i.e. @@ -381,6 +382,7 @@ typedef struct _jl_code_instance_t { _Atomic(jl_fptr_sparam_t) fptr3; // 4 interpreter } specptr; // private data for `jlcall entry point + uint8_t relocatability; // nonzero if all roots are built into sysimg or tagged by module key } jl_code_instance_t; // all values are callable as Functions diff --git a/src/julia_internal.h b/src/julia_internal.h index c76d133d270f7..4990268e5f417 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -10,6 +10,7 @@ #include "support/ptrhash.h" #include "support/strtod.h" #include "gc-alloc-profiler.h" +#include "support/rle.h" #include #if !defined(_WIN32) #include @@ -528,6 +529,8 @@ void jl_resolve_globals_in_ir(jl_array_t *stmts, jl_module_t *m, jl_svec_t *spar int binding_effects); JL_DLLEXPORT void jl_add_method_root(jl_method_t *m, jl_module_t *mod, jl_value_t* root); +int get_root_reference(rle_reference *rr, jl_method_t *m, size_t i); +jl_value_t *lookup_root(jl_method_t *m, uint64_t key, int index); int jl_valid_type_param(jl_value_t *v); diff --git a/src/method.c b/src/method.c index 2d39b7e5923d9..d7de06c01592e 100644 --- a/src/method.c +++ b/src/method.c @@ -732,6 +732,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->slot_syms = NULL; m->roots = NULL; m->root_blocks = NULL; + m->nroots_sysimg = 0; m->ccallable = NULL; m->module = module; m->external_mt = NULL; @@ -1035,6 +1036,34 @@ JL_DLLEXPORT void jl_add_method_root(jl_method_t *m, jl_module_t *mod, jl_value_ JL_GC_POP(); } +// given the absolute index i of a root, retrieve its relocatable reference +// returns 1 if the root is relocatable +int get_root_reference(rle_reference *rr, jl_method_t *m, size_t i) +{ + if (!m->root_blocks) { + rr->key = 0; + rr->index = i; + return i < m->nroots_sysimg; + } + rle_index_to_reference(rr, i, (uint64_t*)jl_array_data(m->root_blocks), jl_array_len(m->root_blocks), 0); + if (rr->key) + return 1; + return i < m->nroots_sysimg; +} + +// get a root, given its key and index relative to the key +// this is the relocatable way to get a root from m->roots +jl_value_t *lookup_root(jl_method_t *m, uint64_t key, int index) +{ + if (!m->root_blocks) { + assert(key == 0); + return jl_array_ptr_ref(m->roots, index); + } + rle_reference rr = {key, index}; + size_t i = rle_reference_to_index(&rr, (uint64_t*)jl_array_data(m->root_blocks), jl_array_len(m->root_blocks), 0); + return jl_array_ptr_ref(m->roots, i); +} + #ifdef __cplusplus } #endif diff --git a/src/serialize.h b/src/serialize.h index 07ff46fdf96db..63d7c2d360951 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -62,8 +62,9 @@ extern "C" { #define TAG_GOTOIFNOT 54 #define TAG_RETURNNODE 55 #define TAG_ARGUMENT 56 +#define TAG_RELOC_METHODROOT 57 -#define LAST_TAG 56 +#define LAST_TAG 57 #define write_uint8(s, n) ios_putc((n), (s)) #define read_uint8(s) ((uint8_t)ios_getc(s)) diff --git a/src/staticdata.c b/src/staticdata.c index 68fc08d317a8c..7427e23d391aa 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1688,6 +1688,23 @@ static void jl_strip_all_codeinfos(void) jl_foreach_reachable_mtable(strip_all_codeinfos_, NULL); } +static int set_nroots_sysimg__(jl_typemap_entry_t *def, void *_env) +{ + jl_method_t *m = def->func.method; + m->nroots_sysimg = m->roots ? jl_array_len(m->roots) : 0; + return 1; +} + +static void set_nroots_sysimg_(jl_methtable_t *mt, void *_env) +{ + jl_typemap_visitor(mt->defs, set_nroots_sysimg__, NULL); +} + +static void jl_set_nroots_sysimg(void) +{ + jl_foreach_reachable_mtable(set_nroots_sysimg_, NULL); +} + // --- entry points --- static void jl_init_serializer2(int); @@ -1703,6 +1720,7 @@ static void jl_save_system_image_to_stream(ios_t *f) JL_GC_DISABLED // strip metadata and IR when requested if (jl_options.strip_metadata || jl_options.strip_ir) jl_strip_all_codeinfos(); + jl_set_nroots_sysimg(); int en = jl_gc_enable(0); jl_init_serializer2(1); diff --git a/src/support/Makefile b/src/support/Makefile index 6083823e95408..a884aa5fd47e0 100644 --- a/src/support/Makefile +++ b/src/support/Makefile @@ -9,7 +9,7 @@ JCPPFLAGS += $(CPPFLAGS) JLDFLAGS += $(LDFLAGS) SRCS := hashing timefuncs ptrhash operators utf8 ios htable bitvector \ - int2str libsupportinit arraylist strtod + int2str libsupportinit arraylist strtod rle ifeq ($(OS),WINNT) SRCS += asprintf strptime ifeq ($(ARCH),i686) diff --git a/src/support/rle.c b/src/support/rle.c new file mode 100644 index 0000000000000..6b64fa8cf9700 --- /dev/null +++ b/src/support/rle.c @@ -0,0 +1,92 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include "rle.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* iteration */ + +rle_iter_state rle_iter_init(uint64_t key0) +{ + rle_iter_state state = {-1, 0, key0}; + return state; +} + +int rle_iter_increment(rle_iter_state *state, size_t len, uint64_t *rletable, size_t npairs) +{ + state->i += 1; + size_t i = state->i, j = state->j; + if (i >= len) + return 0; + if (rletable) { + while (j < npairs && i >= rletable[j+1]) { + state->key = rletable[j]; + j += 2; + } + state->j = j; + } + return 1; +} + +/* indexing */ + +void rle_index_to_reference(rle_reference *rr, size_t i, uint64_t *rletable, size_t npairs, uint64_t key0) +{ + if (!rletable) { + rr->key = key0; + rr->index = i; + return; + } + // Determine the active key + uint64_t key = key0; + size_t jj = 0; + while (jj < npairs && i >= rletable[jj+1]) { + key = rletable[jj]; + jj += 2; + } + // Subtract the number of preceding items with different keys + uint64_t ckey = key0; + size_t j, start = 0, index = i; + for (j = 0; j < jj; j+=2) { + if (key != ckey) + index -= rletable[j+1] - start; + ckey = rletable[j]; + start = rletable[j+1]; + } + // Return the result + rr->key = key; + rr->index = index; + return; +} + +size_t rle_reference_to_index(rle_reference *rr, uint64_t *rletable, size_t npairs, uint64_t key0) +{ + uint64_t key = rr->key; + size_t index = rr->index, i = index; + if (!rletable) { + assert(key == key0); + return i; + } + uint64_t ckey = key0; + size_t j, start = 0, n; + for (j = 0; j < npairs; j+=2) { + n = rletable[j+1] - start; + if (key != ckey) + i += n; + else { + if (index < n) + break; + index -= n; + } + ckey = rletable[j]; + start = rletable[j+1]; + } + return i; +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/support/rle.h b/src/support/rle.h new file mode 100644 index 0000000000000..f85d9f35c4b80 --- /dev/null +++ b/src/support/rle.h @@ -0,0 +1,48 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#ifndef JL_RLE_H +#define JL_RLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* Run-length encoding (RLE) utilities */ +/* In the RLE table, even indexes encode the key (the item classification), odd indexes encode the item index */ +/* For example, a table + + {77, 3, 88, 5, 77, 8} + + would represent a list where items at indexes 3-4 have key 77, items at indexes 5-7 have key 88, + and items from 8 onward have key 77. Items prior to index 3 have an implicit key passed in as `key0`. +*/ + +/* iteration */ +typedef struct _rle_iter_state_t { + size_t i; // index for the items + size_t j; // index for the rle table + uint64_t key; // current identifier +} rle_iter_state; + +rle_iter_state rle_iter_init(/* implicit value of key for indexes prior to first explicit rle pair */ uint64_t key0); +int rle_iter_increment(rle_iter_state *state, /* number of items */ size_t len, uint64_t *rletable, /*length of rletable */ size_t npairs); + +/* indexing */ +typedef struct { + uint64_t key; + int index; // number of preceding items in the list with the same key +} rle_reference; + +void rle_index_to_reference(rle_reference *rr, /* item index */ size_t i, uint64_t *rletable, size_t npairs, uint64_t key0); +size_t rle_reference_to_index(rle_reference *rr, uint64_t *rletable, size_t npairs, uint64_t key0); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/support/test/rletest.c b/src/support/test/rletest.c new file mode 100644 index 0000000000000..c5c5caa97fde8 --- /dev/null +++ b/src/support/test/rletest.c @@ -0,0 +1,60 @@ +#include +#include +#include "../rle.h" + +int main() +{ + /* Iteration */ + rle_iter_state state = rle_iter_init(22); + int i = 0; + while (rle_iter_increment(&state, 5, NULL, 0)) { + assert(state.key == 22); + assert(state.i == i); + i++; + } + + uint64_t rletable1[4] = {-1, 2, 22, 3}; + state = rle_iter_init(22); + i = 0; + while (rle_iter_increment(&state, 5, rletable1, 4)) { + assert(state.key == (i < 2 ? 22 : (i < 3 ? -1 : 22))); + assert(state.i == i); + i++; + } + + uint64_t rletable2[4] = {-1, 0, 22, 3}; + state = rle_iter_init(22); + i = 0; + while (rle_iter_increment(&state, 5, rletable2, 4)) { + assert(state.key == (i < 3 ? -1 : 22)); + assert(state.i == i); + i++; + } + + state = rle_iter_init(22); + i = 0; + while (rle_iter_increment(&state, 0, rletable2, 4)) { + abort(); + } + + /* Indexing */ + rle_reference rr; + uint64_t rletable3[8] = {0, 0, 5, 2, 22, 3, 0, 5}; + uint64_t keys3[7] = {0, 0, 5, 22, 22, 0, 0}; + int counts3[7] = {0, 1, 0, 0, 1, 2, 3}; + for (i = 0; i < 7; i++) { + rle_index_to_reference(&rr, i, rletable3, 8, 0); + assert(rr.key == keys3[i]); + assert(rr.index == counts3[i]); + assert(rle_reference_to_index(&rr, rletable3, 8, 0) == i); + } + uint64_t rletable4[6] = {5, 2, 22, 3, 0, 5}; // implicit first block + for (i = 0; i < 7; i++) { + rle_index_to_reference(&rr, i, rletable4, 6, 0); + assert(rr.key == keys3[i]); + assert(rr.index == counts3[i]); + assert(rle_reference_to_index(&rr, rletable4, 6, 0) == i); + } + + return 0; +} diff --git a/test/precompile.jl b/test/precompile.jl index f9522d5409536..1e731c38890ad 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -605,7 +605,15 @@ precompile_test_harness("code caching") do dir fpush(X[]) nothing end + function getelsize(list::Vector{T}) where T + n = 0 + for item in list + n += sizeof(T) + end + return n + end precompile(callboth, ()) + precompile(getelsize, (Vector{Int32},)) end """) Base.compilecache(Base.PkgId(string(Cache_module))) @@ -627,6 +635,16 @@ precompile_test_harness("code caching") do dir @test_broken M.X ∈ groups[Mid] # requires caching external compilation results @test M.X2 ∈ groups[rootid(@__MODULE__)] @test !isempty(groups[Bid]) + minternal = which(M.getelsize, (Vector,)) + mi = minternal.specializations[1] + ci = mi.cache + @test ci.relocatability == 1 + Base.invokelatest() do + M.getelsize(M.X2[]) + end + mi = minternal.specializations[2] + ci = mi.cache + @test ci.relocatability == 0 # PkgA loads PkgB, and both add roots to the same method (both before and after loading B) Cache_module2 = :Cachea1544c83560f0c99 write(joinpath(dir, "$Cache_module2.jl"),