diff --git a/base/array.jl b/base/array.jl index a80f481388158..cf4046d1caf56 100644 --- a/base/array.jl +++ b/base/array.jl @@ -151,12 +151,20 @@ function vect(X...) return copyto!(Vector{T}(undef, length(X)), X) end -size(a::Array, d::Integer) = arraysize(a, convert(Int, d)) -size(a::Vector) = (arraysize(a,1),) -size(a::Matrix) = (arraysize(a,1), arraysize(a,2)) -size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(N))) +const ImmutableArray = Core.ImmutableArray +const IMArray{T,N} = Union{Array{T, N}, ImmutableArray{T,N}} +const IMVector{T} = IMArray{T, 1} +const IMMatrix{T} = IMArray{T, 2} -asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) +freeze(a::Array) = Core.arrayfreeze(a) +melt(a::ImmutableArray) = Core.arraymelt(a) + +size(a::IMArray, d::Integer) = arraysize(a, convert(Int, d)) +size(a::IMVector) = (arraysize(a,1),) +size(a::IMMatrix) = (arraysize(a,1), arraysize(a,2)) +size(a::IMArray{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(N))) + +asize_from(a::IMArray, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) """ Base.isbitsunion(::Type{T}) @@ -208,6 +216,13 @@ function isassigned(a::Array, i::Int...) ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii) == 1 end +function isassigned(a::ImmutableArray, i::Int...) + @_inline_meta + ii = (_sub2ind(size(a), i...) % UInt) - 1 + @boundscheck ii < length(a) % UInt || return false + ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii) == 1 +end + ## copy ## """ @@ -728,6 +743,9 @@ function getindex end @eval getindex(A::Array, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1) @eval getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref($(Expr(:boundscheck)), A, i1, i2, I...)) +@eval getindex(A::ImmutableArray, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1) +@eval getindex(A::ImmutableArray, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref($(Expr(:boundscheck)), A, i1, i2, I...)) + # Faster contiguous indexing using copyto! for UnitRange and Colon function getindex(A::Array, I::UnitRange{Int}) @_inline_meta diff --git a/base/compiler/ssair/domtree.jl b/base/compiler/ssair/domtree.jl index 14302c1c19924..8b65e751e7e6f 100644 --- a/base/compiler/ssair/domtree.jl +++ b/base/compiler/ssair/domtree.jl @@ -25,6 +25,13 @@ function dominates(domtree::DomTree, bb1::Int, bb2::Int) return bb1 == bb2 end +function ssadominates(ir::IRCode, domtree::DomTree, ssa1::Int, ssa2::Int) + bb1 = block_for_inst(ir.cfg, ssa1) + bb2 = block_for_inst(ir.cfg, ssa2) + bb1 == bb2 && return ssa1 < ssa2 + return dominates(domtree, bb1, bb2) +end + bb_unreachable(domtree::DomTree, bb::Int) = bb != 1 && domtree.nodes[bb].level == 1 function update_level!(domtree::Vector{DomTreeNode}, node::Int, level::Int) diff --git a/base/compiler/ssair/driver.jl b/base/compiler/ssair/driver.jl index 8f00e39cab03b..dae0e61928a89 100644 --- a/base/compiler/ssair/driver.jl +++ b/base/compiler/ssair/driver.jl @@ -122,6 +122,7 @@ function run_passes(ci::CodeInfo, nargs::Int, sv::OptimizationState) #@Base.show ir.new_nodes #@Base.show ("after_sroa", ir) ir = adce_pass!(ir) + ir = memory_opt!(ir) #@Base.show ("after_adce", ir) @timeit "type lift" ir = type_lift_pass!(ir) @timeit "compact 3" ir = compact!(ir) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index d1dfad473726b..e57d1a80d3954 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1140,3 +1140,76 @@ function cfg_simplify!(ir::IRCode) compact.active_result_bb = length(bb_starts) return finish(compact) end + +function is_allocation(stmt) + isexpr(stmt, :foreigncall) || return false + s = stmt.args[1] + isa(s, QuoteNode) && (s = s.value) + return s === :jl_alloc_array_1d +end + +function memory_opt!(ir::IRCode) + # TODO: This is wrong if there's a use in a phi node. + compact = IncrementalCompact(ir, true) + uses = IdDict{Int, Vector{Int}}() + relevant = IdSet{Int}() + revisit = Int[] + function mark_val(val) + isa(val, SSAValue) || return + val.id in relevant && pop!(relevant, val.id) + end + for (idx, stmt) in compact + if isa(stmt, ReturnNode) + isdefined(stmt, :val) || continue + val = stmt.val + if isa(val, SSAValue) && val.id in relevant + (haskey(uses, val.id)) || (uses[val.id] = Int[]) + push!(uses[val.id], idx) + end + continue + end + (isexpr(stmt, :call) || isexpr(stmt, :foreigncall)) || continue + if is_allocation(stmt) + push!(relevant, idx) + # TODO: Mark everything else here + continue + end + # TODO: Replace this by interprocedural escape analysis + if is_known_call(stmt, arrayset, compact) + # The value being set escapes, everything else doesn't + mark_val(stmt.args[4]) + arr = stmt.args[3] + if isa(arr, SSAValue) && arr.id in relevant + (haskey(uses, arr.id)) || (uses[arr.id] = Int[]) + push!(uses[arr.id], idx) + end + elseif is_known_call(stmt, Core.arrayfreeze, compact) && isa(stmt.args[2], SSAValue) + push!(revisit, idx) + else + # For now we assume everything escapes + for ur in userefs(stmt) + mark_val(ur[]) + end + end + end + ir = finish(compact) + domtree = construct_domtree(ir.cfg) + for idx in revisit + # Make sure that the value we reference didn't escape + id = ir.stmts[idx].args[2].id + (id in relevant) || continue + + # We're ok to steal the memory if we don't dominate any uses + ok = true + for use in uses[id] + if ssadominates(ir, domtree, idx, use) + ok = false + break + end + end + ok || continue + + ir.stmts[idx].args[1] = Core.mutating_arrayfreeze + end + return ir +end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 38d5321b3fca7..ef47048b71937 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1266,6 +1266,21 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, end end return Any + elseif f === Core.arrayfreeze || f === Core.arraymelt + if length(argtypes) != 1 + isva && return Any + return Bottom + end + a = widenconst(argtypes[1]) + at = (f === Core.arrayfreeze ? Array : ImmutableArray) + rt = (f === Core.arrayfreeze ? ImmutableArray : Array) + if a <: at + unw = unwrap_unionall(a) + if isa(unw, DataType) + return rewrap_unionall(rt{unw.parameters[1], unw.parameters[2]}, a) + end + end + return rt elseif f === Expr if length(argtypes) < 1 && !isva return Bottom diff --git a/base/exports.jl b/base/exports.jl index 23b4141a06c5e..cf51c2fef4982 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -916,6 +916,10 @@ export rand, randn, +# mutation + freeze, + melt, + # Macros # parser internal @__FILE__, diff --git a/src/builtin_proto.h b/src/builtin_proto.h index ca3096d3a57e0..fe6006a03229f 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -30,6 +30,8 @@ DECLARE_BUILTIN(getfield); DECLARE_BUILTIN(setfield); DECLARE_BUILTIN(fieldtype); DECLARE_BUILTIN(arrayref); DECLARE_BUILTIN(const_arrayref); DECLARE_BUILTIN(arrayset); DECLARE_BUILTIN(arraysize); +DECLARE_BUILTIN(arrayfreeze); DECLARE_BUILTIN(arraymelt); +DECLARE_BUILTIN(mutating_arrayfreeze); DECLARE_BUILTIN(apply_type); DECLARE_BUILTIN(applicable); DECLARE_BUILTIN(invoke); DECLARE_BUILTIN(_expr); DECLARE_BUILTIN(typeassert); DECLARE_BUILTIN(ifelse); diff --git a/src/builtins.c b/src/builtins.c index 1a01da56ce83a..5d47d8325a221 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1014,7 +1014,9 @@ JL_CALLABLE(jl_f__typevar) JL_CALLABLE(jl_f_arraysize) { JL_NARGS(arraysize, 2, 2); - JL_TYPECHK(arraysize, array, args[0]); + if (!jl_is_arrayish(args[0])) { + jl_type_error("arraysize", (jl_value_t*)jl_array_type, args[0]); + } jl_array_t *a = (jl_array_t*)args[0]; size_t nd = jl_array_ndims(a); JL_TYPECHK(arraysize, long, args[1]); @@ -1053,7 +1055,9 @@ JL_CALLABLE(jl_f_arrayref) { JL_NARGSV(arrayref, 3); JL_TYPECHK(arrayref, bool, args[0]); - JL_TYPECHK(arrayref, array, args[1]); + if (!jl_is_arrayish(args[1])) { + jl_type_error("arrayref", (jl_value_t*)jl_array_type, args[1]); + } jl_array_t *a = (jl_array_t*)args[1]; size_t i = array_nd_index(a, &args[2], nargs - 2, "arrayref"); return jl_arrayref(a, i); @@ -1075,6 +1079,49 @@ JL_CALLABLE(jl_f_arrayset) return args[1]; } +JL_CALLABLE(jl_f_arrayfreeze) +{ + JL_NARGSV(arrayfreeze, 1); + JL_TYPECHK(arrayfreeze, array, args[0]); + jl_array_t *a = (jl_array_t*)args[0]; + jl_datatype_t *it = (jl_datatype_t *)jl_apply_type2((jl_value_t*)jl_immutable_array_type, + jl_tparam0(jl_typeof(a)), jl_tparam1(jl_typeof(a))); + // The idea is to elide this copy if the compiler or runtime can prove that + // doing so is safe to do. + jl_array_t *na = jl_array_copy(a); + jl_set_typeof(na, it); + return (jl_value_t*)na; +} + +JL_CALLABLE(jl_f_mutating_arrayfreeze) +{ + JL_NARGSV(arrayfreeze, 1); + JL_TYPECHK(arrayfreeze, array, args[0]); + jl_array_t *a = (jl_array_t*)args[0]; + jl_datatype_t *it = (jl_datatype_t *)jl_apply_type2((jl_value_t*)jl_immutable_array_type, + jl_tparam0(jl_typeof(a)), jl_tparam1(jl_typeof(a))); + // The idea is to elide this copy if the compiler or runtime can prove that + // doing so is safe to do. + jl_set_typeof(a, it); + return (jl_value_t*)a; +} + +JL_CALLABLE(jl_f_arraymelt) +{ + JL_NARGSV(arrayfreeze, 1); + if (((jl_datatype_t*)jl_typeof(args[0]))->name != jl_immutable_array_typename) { + jl_type_error("arraymelt", (jl_value_t*)jl_immutable_array_type, args[0]); + } + jl_array_t *a = (jl_array_t*)args[0]; + jl_datatype_t *it = (jl_datatype_t *)jl_apply_type2((jl_value_t*)jl_array_type, + jl_tparam0(jl_typeof(a)), jl_tparam1(jl_typeof(a))); + // The idea is to elide this copy if the compiler or runtime can prove that + // doing so is safe to do. + jl_array_t *na = jl_array_copy(a); + jl_set_typeof(na, it); + return (jl_value_t*)na; +} + // IntrinsicFunctions --------------------------------------------------------- static void (*runtime_fp[num_intrinsics])(void); @@ -1218,6 +1265,9 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin_func("const_arrayref", jl_f_arrayref); add_builtin_func("arrayset", jl_f_arrayset); add_builtin_func("arraysize", jl_f_arraysize); + add_builtin_func("arrayfreeze", jl_f_arrayfreeze); + add_builtin_func("mutating_arrayfreeze", jl_f_mutating_arrayfreeze); + add_builtin_func("arraymelt", jl_f_arraymelt); // method table utils add_builtin_func("applicable", jl_f_applicable); @@ -1276,6 +1326,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("AbstractArray", (jl_value_t*)jl_abstractarray_type); add_builtin("DenseArray", (jl_value_t*)jl_densearray_type); add_builtin("Array", (jl_value_t*)jl_array_type); + add_builtin("ImmutableArray", (jl_value_t*)jl_immutable_array_type); add_builtin("Expr", (jl_value_t*)jl_expr_type); add_builtin("LineNumberNode", (jl_value_t*)jl_linenumbernode_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index fafb2c821e9ba..0c842aae42c47 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -367,7 +367,7 @@ static MDNode *best_tbaa(jl_value_t *jt) { // note that this is guaranteed to include jl_isbits static bool jl_justbits(jl_value_t* t) { - return jl_is_immutable_datatype(t) && ((jl_datatype_t*)t)->layout && ((jl_datatype_t*)t)->layout->npointers == 0; + return jl_is_immutable_datatype(t) && !jl_is_arrayish_type(t) && ((jl_datatype_t*)t)->layout && ((jl_datatype_t*)t)->layout->npointers == 0; } // metadata tracking for a llvm Value* during codegen @@ -7244,6 +7244,9 @@ static void init_julia_llvm_env(Module *m) builtin_func_map[jl_f_const_arrayref] = jlcall_func_to_llvm("jl_f_const_arrayref", &jl_f_arrayref, m); builtin_func_map[jl_f_arrayset] = jlcall_func_to_llvm("jl_f_arrayset", &jl_f_arrayset, m); builtin_func_map[jl_f_arraysize] = jlcall_func_to_llvm("jl_f_arraysize", &jl_f_arraysize, m); + builtin_func_map[jl_f_arrayfreeze] = jlcall_func_to_llvm("jl_f_arrayfreeze", &jl_f_arrayfreeze, m); + builtin_func_map[jl_f_mutating_arrayfreeze] = jlcall_func_to_llvm("jl_f_mutating_arrayfreeze", &jl_f_mutating_arrayfreeze, m); + builtin_func_map[jl_f_arraymelt] = jlcall_func_to_llvm("jl_f_arraymelt", &jl_f_arraymelt, m); builtin_func_map[jl_f_apply_type] = jlcall_func_to_llvm("jl_f_apply_type", &jl_f_apply_type, m); jltuple_func = builtin_func_map[jl_f_tuple]; jlgetfield_func = builtin_func_map[jl_f_getfield]; diff --git a/src/datatype.c b/src/datatype.c index dceba56f8ea92..a625951642a92 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -224,6 +224,7 @@ unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) STATIC_INLINE int jl_is_datatype_make_singleton(jl_datatype_t *d) { return (!d->abstract && jl_datatype_size(d) == 0 && d != jl_sym_type && d->name != jl_array_typename && + d->name != jl_immutable_array_typename && d->uid != 0 && !d->mutabl); } @@ -300,7 +301,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) // compute whether this type can be inlined // based on whether its definition is self-referential if (w->types != NULL) { - st->isbitstype = st->isconcretetype && !st->mutabl; + st->isbitstype = st->isconcretetype && !st->mutabl && st->name != jl_immutable_array_typename; size_t i, nf = jl_field_count(st); for (i = 0; i < nf; i++) { jl_value_t *fld = jl_field_type(st, i); diff --git a/src/dump.c b/src/dump.c index 923f63fd14757..a78995169a2ab 100644 --- a/src/dump.c +++ b/src/dump.c @@ -3320,6 +3320,7 @@ void jl_init_serializer(void) arraylist_new(&builtin_typenames, 0); arraylist_push(&builtin_typenames, jl_array_typename); + arraylist_push(&builtin_typenames, jl_immutable_array_typename); arraylist_push(&builtin_typenames, ((jl_datatype_t*)jl_ref_type->body)->name); arraylist_push(&builtin_typenames, jl_pointer_typename); arraylist_push(&builtin_typenames, jl_type_typename); diff --git a/src/jltypes.c b/src/jltypes.c index 0d59bdc3e659e..8541acdd71f15 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -78,6 +78,8 @@ jl_unionall_t *jl_typetype_type; jl_unionall_t *jl_array_type; jl_typename_t *jl_array_typename; +jl_unionall_t *jl_immutable_array_type; +jl_typename_t *jl_immutable_array_typename; jl_value_t *jl_array_uint8_type; jl_value_t *jl_array_any_type; jl_value_t *jl_array_symbol_type; @@ -1963,6 +1965,16 @@ void jl_init_types(void) JL_GC_DISABLED jl_array_uint8_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_uint8_type, jl_box_long(1)); jl_array_int32_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_int32_type, jl_box_long(1)); + tv = jl_svec2(tvar("T"), tvar("N")); + jl_immutable_array_type = (jl_unionall_t*) + jl_new_datatype(jl_symbol("ImmutableArray"), core, + (jl_datatype_t*) + jl_apply_type((jl_value_t*)jl_densearray_type, jl_svec_data(tv), 2), + tv, + jl_emptysvec, jl_emptysvec, 0, 0, 0)->name->wrapper; + jl_immutable_array_typename = ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_immutable_array_type))->name; + jl_compute_field_offsets((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_immutable_array_type)); + jl_expr_type = jl_new_datatype(jl_symbol("Expr"), core, jl_any_type, jl_emptysvec, diff --git a/src/julia.h b/src/julia.h index 0d4f3855c43e0..69b47dc80bac2 100644 --- a/src/julia.h +++ b/src/julia.h @@ -581,6 +581,8 @@ extern JL_DLLEXPORT jl_unionall_t *jl_abstractarray_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_unionall_t *jl_densearray_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_unionall_t *jl_array_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_typename_t *jl_array_typename JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_unionall_t *jl_immutable_array_type JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_typename_t *jl_immutable_array_typename JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_weakref_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_abstractstring_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_string_type JL_GLOBALLY_ROOTED; @@ -1040,11 +1042,25 @@ STATIC_INLINE int jl_is_primitivetype(void *v) JL_NOTSAFEPOINT jl_datatype_size(v) > 0); } +STATIC_INLINE int jl_is_array_type(void *t) JL_NOTSAFEPOINT +{ + return (jl_is_datatype(t) && + (((jl_datatype_t*)(t))->name == jl_array_typename)); +} + +STATIC_INLINE int jl_is_arrayish_type(void *t) JL_NOTSAFEPOINT +{ + return (jl_is_datatype(t) && + (((jl_datatype_t*)(t))->name == jl_array_typename || + ((jl_datatype_t*)(t))->name == jl_immutable_array_typename)); +} + STATIC_INLINE int jl_is_structtype(void *v) JL_NOTSAFEPOINT { return (jl_is_datatype(v) && !((jl_datatype_t*)(v))->abstract && - !jl_is_primitivetype(v)); + !jl_is_primitivetype(v) && + !jl_is_arrayish_type(v)); } STATIC_INLINE int jl_isbits(void *t) JL_NOTSAFEPOINT // corresponding to isbits() in julia @@ -1062,16 +1078,16 @@ STATIC_INLINE int jl_is_abstracttype(void *v) JL_NOTSAFEPOINT return (jl_is_datatype(v) && ((jl_datatype_t*)(v))->abstract); } -STATIC_INLINE int jl_is_array_type(void *t) JL_NOTSAFEPOINT +STATIC_INLINE int jl_is_array(void *v) JL_NOTSAFEPOINT { - return (jl_is_datatype(t) && - ((jl_datatype_t*)(t))->name == jl_array_typename); + jl_value_t *t = jl_typeof(v); + return jl_is_array_type(t); } -STATIC_INLINE int jl_is_array(void *v) JL_NOTSAFEPOINT +STATIC_INLINE int jl_is_arrayish(void *v) JL_NOTSAFEPOINT { jl_value_t *t = jl_typeof(v); - return jl_is_array_type(t); + return jl_is_arrayish_type(t); } STATIC_INLINE int jl_is_cpointer_type(jl_value_t *t) JL_NOTSAFEPOINT @@ -1348,6 +1364,7 @@ JL_DLLEXPORT jl_value_t *jl_array_to_string(jl_array_t *a); JL_DLLEXPORT jl_array_t *jl_alloc_vec_any(size_t n); JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i); // 0-indexed JL_DLLEXPORT jl_value_t *jl_ptrarrayref(jl_array_t *a JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; // 0-indexed +JL_DLLEXPORT jl_array_t *jl_array_copy(jl_array_t *ary); JL_DLLEXPORT void jl_arrayset(jl_array_t *a JL_ROOTING_ARGUMENT, jl_value_t *v JL_ROOTED_ARGUMENT JL_MAYBE_UNROOTED, size_t i); // 0-indexed JL_DLLEXPORT void jl_arrayunset(jl_array_t *a, size_t i); // 0-indexed JL_DLLEXPORT int jl_array_isassigned(jl_array_t *a, size_t i); // 0-indexed diff --git a/src/rtutils.c b/src/rtutils.c index ea1eb6b2ea033..5dafae67e0f0a 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -887,8 +887,8 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, ")"); } } - else if (jl_array_type && jl_is_array_type(vt)) { - n += jl_printf(out, "Array{"); + else if (jl_array_type && jl_is_arrayish_type(vt)) { + n += jl_printf(out, jl_is_array_type(vt) ? "Array{" : "ImmutableArray{"); n += jl_static_show_x(out, (jl_value_t*)jl_tparam0(vt), depth); n += jl_printf(out, ", ("); size_t i, ndims = jl_array_ndims(v); diff --git a/src/staticdata.c b/src/staticdata.c index e41d377cdc231..dd7180d4b3187 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -61,7 +61,8 @@ static const jl_fptr_args_t id_to_fptrs[] = { jl_f_throw, jl_f_is, jl_f_typeof, jl_f_issubtype, jl_f_isa, jl_f_typeassert, jl_f__apply, jl_f__apply_pure, jl_f__apply_latest, jl_f_isdefined, jl_f_tuple, jl_f_svec, jl_f_intrinsic_call, jl_f_invoke_kwsorter, - jl_f_getfield, jl_f_setfield, jl_f_fieldtype, jl_f_nfields, + jl_f_getfield, jl_f_setfield, jl_f_fieldtype, jl_f_nfields, jl_f_arrayfreeze, + jl_f_mutating_arrayfreeze, jl_f_arraymelt, jl_f_arrayref, jl_f_const_arrayref, jl_f_arrayset, jl_f_arraysize, jl_f_apply_type, jl_f_applicable, jl_f_invoke, jl_f_sizeof, jl_f__expr, jl_f__typevar, jl_f_ifelse, @@ -1282,6 +1283,7 @@ static void jl_save_system_image_to_stream(ios_t *f) jl_serialize_value(&s, jl_intrinsic_type->name->mt); jl_serialize_value(&s, jl_sym_type->name->mt); jl_serialize_value(&s, jl_array_typename->mt); + jl_serialize_value(&s, jl_immutable_array_typename->mt); jl_serialize_value(&s, jl_module_type->name->mt); jl_prune_type_cache(jl_tuple_typename->cache); @@ -1352,6 +1354,7 @@ static void jl_save_system_image_to_stream(ios_t *f) jl_write_value(&s, jl_intrinsic_type->name->mt); jl_write_value(&s, jl_sym_type->name->mt); jl_write_value(&s, jl_array_typename->mt); + jl_write_value(&s, jl_immutable_array_typename->mt); jl_write_value(&s, jl_module_type->name->mt); uintptr_t i; for (i = 0; i < builtin_typenames.len; i++) { @@ -1529,6 +1532,7 @@ static void jl_restore_system_image_from_stream(ios_t *f) jl_intrinsic_type->name->mt = (jl_methtable_t*)jl_read_value(&s); jl_sym_type->name->mt = (jl_methtable_t*)jl_read_value(&s); jl_array_typename->mt = (jl_methtable_t*)jl_read_value(&s); + jl_immutable_array_typename->mt = (jl_methtable_t*)jl_read_value(&s); jl_module_type->name->mt = (jl_methtable_t*)jl_read_value(&s); uintptr_t i; @@ -1628,7 +1632,7 @@ static void jl_init_serializer2(int for_serialize) void *tags[] = { ptls->root_task, jl_symbol_type, jl_ssavalue_type, jl_datatype_type, jl_slotnumber_type, - jl_simplevector_type, jl_array_type, jl_typedslot_type, + jl_simplevector_type, jl_array_type, jl_immutable_array_type, jl_typedslot_type, jl_expr_type, jl_globalref_type, jl_string_type, jl_module_type, jl_tvar_type, jl_method_instance_type, jl_method_type, jl_code_instance_type, jl_emptysvec, jl_emptytuple, jl_false, jl_true, jl_nothing, jl_any_type, @@ -1651,6 +1655,7 @@ static void jl_init_serializer2(int for_serialize) ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_ref_type))->name, jl_pointer_typename, jl_simplevector_type->name, jl_datatype_type->name, jl_uniontype_type->name, jl_array_typename, + jl_immutable_array_typename, jl_expr_type->name, jl_typename_type->name, jl_type_typename, jl_methtable_type->name, jl_typemap_level_type->name, jl_typemap_entry_type->name, jl_tvar_type->name, ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_abstractarray_type))->name, @@ -1730,6 +1735,7 @@ static void jl_init_serializer2(int for_serialize) } arraylist_push(&builtin_typenames, jl_array_typename); + arraylist_push(&builtin_typenames, jl_immutable_array_typename); arraylist_push(&builtin_typenames, ((jl_datatype_t*)jl_ref_type->body)->name); arraylist_push(&builtin_typenames, jl_pointer_typename); arraylist_push(&builtin_typenames, jl_type_typename);