diff --git a/base/inference.jl b/base/inference.jl index c16dfc890c020..72482f5f316f1 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1930,8 +1930,12 @@ function abstract_eval(e::ANY, vtypes::VarTable, sv::InferenceState) if isdefined(sv.linfo, :def) spsig = sv.linfo.def.sig if isa(spsig, UnionAll) - env = data_pointer_from_objref(sv.linfo.sparam_vals) + sizeof(Ptr{Void}) - rt = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, env) + if !isempty(sv.linfo.sparam_vals) + env = data_pointer_from_objref(sv.linfo.sparam_vals) + sizeof(Ptr{Void}) + rt = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, env) + else + rt = rewrap_unionall(e.args[2], spsig) + end end end abstract_eval(e.args[1], vtypes, sv) @@ -3290,6 +3294,7 @@ function substitute!(e::ANY, na::Int, argexprs::Vector{Any}, spsig::ANY, spvals: if head === :static_parameter return spvals[e.args[1]] elseif head === :foreigncall + @assert !isa(spsig,UnionAll) || !isempty(spvals) for i = 1:length(e.args) if i == 2 e.args[2] = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, spvals) diff --git a/src/Makefile b/src/Makefile index ae3db0cb4c649..9fde74da895a8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -42,7 +42,7 @@ endif SRCS := \ jltypes gf typemap ast builtins module interpreter symbol \ dlload sys init task array dump toplevel jl_uv datatype \ - simplevector APInt-C runtime_intrinsics runtime_ccall \ + simplevector APInt-C runtime_intrinsics runtime_ccall precompile \ threadgroup threading stackwalk gc gc-debug gc-pages method \ jlapi signal-handling safepoint jloptions timing subtype rtutils diff --git a/src/ccall.cpp b/src/ccall.cpp index 934be68a0ec8c..51436ada49f82 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1464,7 +1464,7 @@ static const std::string verify_ccall_sig(size_t nargs, jl_value_t *&rt, jl_valu } else { static_rt = retboxed || !jl_has_typevar_from_unionall(rt, unionall_env); - if (!static_rt && sparam_vals != NULL) { + if (!static_rt && sparam_vals != NULL && jl_svec_len(sparam_vals) > 0) { rt = jl_instantiate_type_in_env(rt, unionall_env, jl_svec_data(sparam_vals)); // `rt` is gc-rooted by the caller static_rt = true; @@ -1875,7 +1875,8 @@ jl_cgval_t function_sig_t::emit_a_ccall( // if we know the function sparams, try to fill those in now // so that the julia_to_native type checks are more likely to be doable (e.g. leaf types) at compile-time jl_value_t *jargty_in_env = jargty; - if (ctx->spvals_ptr == NULL && !toboxed && unionall_env && jl_has_typevar_from_unionall(jargty, unionall_env)) { + if (ctx->spvals_ptr == NULL && !toboxed && unionall_env && jl_has_typevar_from_unionall(jargty, unionall_env) && + jl_svec_len(ctx->linfo->sparam_vals) > 0) { jargty_in_env = jl_instantiate_type_in_env(jargty_in_env, unionall_env, jl_svec_data(ctx->linfo->sparam_vals)); if (jargty_in_env != jargty) jl_add_method_root(ctx, jargty_in_env); diff --git a/src/gf.c b/src/gf.c index 7518b47c6885d..de023ba2163ea 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3,10 +3,10 @@ /* Generic Functions . method table and lookup - . GF constructor, add_method + . GF constructor . dispatch . static parameter inference - . method specialization, invoking type inference + . method specialization and caching, invoking type inference */ #include #include @@ -282,8 +282,7 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t **pli, size_t world, int forc return src; } - -static int jl_is_rettype_inferred(jl_method_instance_t *li) +int jl_is_rettype_inferred(jl_method_instance_t *li) { if (!li->inferred) return 0; @@ -292,11 +291,11 @@ static int jl_is_rettype_inferred(jl_method_instance_t *li) return 1; } - struct set_world { jl_method_instance_t *replaced; size_t world; }; + static int set_max_world2(jl_typemap_entry_t *entry, void *closure0) { struct set_world *closure = (struct set_world*)closure0; @@ -306,6 +305,7 @@ static int set_max_world2(jl_typemap_entry_t *entry, void *closure0) } return 1; } + static int set_min_world2(jl_typemap_entry_t *entry, void *closure0) { struct set_world *closure = (struct set_world*)closure0; @@ -315,6 +315,7 @@ static int set_min_world2(jl_typemap_entry_t *entry, void *closure0) } return 1; } + static void update_world_bound(jl_method_instance_t *replaced, jl_typemap_visitor_fptr fptr, size_t world) { struct set_world update; @@ -333,7 +334,6 @@ static void update_world_bound(jl_method_instance_t *replaced, jl_typemap_visito jl_typemap_visitor(gf->name->mt->cache, fptr, (void*)&update); } - JL_DLLEXPORT jl_method_instance_t* jl_set_method_inferred( jl_method_instance_t *li, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, @@ -427,7 +427,6 @@ JL_DLLEXPORT jl_method_instance_t* jl_set_method_inferred( return li; } - static int get_spec_unspec_list(jl_typemap_entry_t *l, void *closure) { if (jl_is_method_instance(l->func.value) && !jl_is_rettype_inferred(l->func.linfo)) @@ -441,32 +440,32 @@ static int get_method_unspec_list(jl_typemap_entry_t *def, void *closure) return 1; } - -static void jl_reset_mt_caches(jl_module_t *m, jl_array_t *unspec) +void jl_foreach_mtable_in_module( + jl_module_t *m, + void (*visit)(jl_methtable_t *mt, void *env), + void *env) { - // removes all method caches size_t i; void **table = m->bindings.table; - for(i=1; i < m->bindings.size; i+=2) { + for (i = 1; i < m->bindings.size; i += 2) { if (table[i] != HT_NOTFOUND) { jl_binding_t *b = (jl_binding_t*)table[i]; if (b->owner == m && b->value && b->constp) { - if (jl_is_datatype(b->value)) { - jl_typename_t *tn = ((jl_datatype_t*)b->value)->name; + jl_value_t *v = jl_unwrap_unionall(b->value); + if (jl_is_datatype(v)) { + jl_typename_t *tn = ((jl_datatype_t*)v)->name; if (tn->module == m && tn->name == b->name) { jl_methtable_t *mt = tn->mt; if (mt != NULL && (jl_value_t*)mt != jl_nothing) { - if (mt->defs.unknown != jl_nothing) // make sure not to reset builtin functions - mt->cache.unknown = jl_nothing; - jl_typemap_visitor(mt->defs, get_method_unspec_list, (void*)unspec); + visit(mt, env); } } } - else if (jl_is_module(b->value)) { - jl_module_t *child = (jl_module_t*)b->value; + else if (jl_is_module(v)) { + jl_module_t *child = (jl_module_t*)v; if (child != m && child->parent == m && child->name == b->name) { // this is the original/primary binding for the submodule - jl_reset_mt_caches((jl_module_t*)b->value, unspec); + jl_foreach_mtable_in_module(child, visit, env); } } } @@ -474,6 +473,15 @@ static void jl_reset_mt_caches(jl_module_t *m, jl_array_t *unspec) } } +static void reset_mt_caches(jl_methtable_t *mt, void *env) +{ + // removes all method caches + if (mt->defs.unknown != jl_nothing) // make sure not to reset builtin functions + mt->cache.unknown = jl_nothing; + jl_typemap_visitor(mt->defs, get_method_unspec_list, env); +} + + jl_function_t *jl_typeinf_func = NULL; size_t jl_typeinf_world = 0; @@ -486,7 +494,7 @@ JL_DLLEXPORT void jl_set_typeinf_func(jl_value_t *f) // TODO: also reinfer if max_world != ~(size_t)0 jl_array_t *unspec = jl_alloc_vec_any(0); JL_GC_PUSH1(&unspec); - jl_reset_mt_caches(jl_main_module, unspec); + jl_foreach_mtable_in_module(jl_main_module, reset_mt_caches, (void*)unspec); size_t i, l; for (i = 0, l = jl_array_len(unspec); i < l; i++) { jl_method_instance_t *li = (jl_method_instance_t*)jl_array_ptr_ref(unspec, i); @@ -1170,8 +1178,7 @@ static int check_ambiguous_visitor(jl_typemap_entry_t *oldentry, struct typemap_ return 1; } -static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, - jl_typemap_entry_t *newentry) +static jl_value_t *check_ambiguous_matches(union jl_typemap_t defs, jl_typemap_entry_t *newentry) { jl_tupletype_t *type = newentry->sig; jl_tupletype_t *ttypes = (jl_tupletype_t*)jl_unwrap_unionall((jl_value_t*)type); @@ -1243,11 +1250,10 @@ static void invalidate_method_instance(jl_method_instance_t *replaced, size_t ma assert(replaced->min_world <= max_world && "attempting to set invalid world constraints"); if (JL_DEBUG_METHOD_INVALIDATION) { int d0 = depth; - char space = ' ', nl = '\n'; while (d0-- > 0) - jl_uv_puts(JL_STDOUT, &space, 1); + jl_uv_puts(JL_STDOUT, " ", 1); jl_static_show(JL_STDOUT, (jl_value_t*)replaced); - jl_uv_puts(JL_STDOUT, &nl, 1); + jl_uv_puts(JL_STDOUT, "\n", 1); } replaced->max_world = max_world; update_world_bound(replaced, set_max_world2, max_world); @@ -1267,6 +1273,7 @@ static void invalidate_method_instance(jl_method_instance_t *replaced, size_t ma struct invalidate_conflicting_env { struct typemap_intersection_env match; size_t max_world; + int invalidated; }; static int invalidate_backedges(jl_typemap_entry_t *oldentry, struct typemap_intersection_env *closure0) { @@ -1295,6 +1302,7 @@ static int invalidate_backedges(jl_typemap_entry_t *oldentry, struct typemap_int invalidate_method_instance(replaced[i], closure->max_world, 0); } } + closure->invalidated = 1; def.replaced->backedges = NULL; JL_UNLOCK_NOGC(&def.replaced->def->writelock); } @@ -1361,6 +1369,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method jl_value_t *type = method->sig; jl_value_t *oldvalue = NULL; struct invalidate_conflicting_env env; + env.invalidated = 0; env.max_world = method->min_world - 1; JL_GC_PUSH1(&oldvalue); JL_LOCK(&mt->writelock); @@ -1382,6 +1391,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method if (jl_type_intersection(backedgetyp, (jl_value_t*)type) != (jl_value_t*)jl_bottom_type) { jl_method_instance_t *backedge = (jl_method_instance_t*)backedges[i]; invalidate_method_instance(backedge, env.max_world, 0); + env.invalidated = 1; } else { backedges[ins++] = backedges[i - 1]; @@ -1421,6 +1431,13 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method } } } + if (env.invalidated && JL_DEBUG_METHOD_INVALIDATION) { + jl_uv_puts(JL_STDOUT, ">> ", 3); + jl_static_show(JL_STDOUT, (jl_value_t*)method); + jl_uv_puts(JL_STDOUT, " ", 1); + jl_static_show(JL_STDOUT, (jl_value_t*)type); + jl_uv_puts(JL_STDOUT, "\n", 1); + } update_max_args(mt, type); JL_UNLOCK(&mt->writelock); JL_GC_POP(); @@ -1730,345 +1747,6 @@ jl_tupletype_t *jl_argtype_with_function(jl_function_t *f, jl_tupletype_t *types return (jl_tupletype_t*)tt; } -#if 0 // TODO restore this for jb/subtype -static int tupletype_any_bottom(jl_value_t *sig) -{ - sig = jl_unwrap_unionall(sig); - assert(jl_is_tuple_type(sig)); - jl_svec_t *types = ((jl_tupletype_t*)sig)->types; - size_t i, l = jl_svec_len(types); - for (i = 0; i < l; i++) { - if (jl_svecref(types, i) == jl_bottom_type) - return 1; - } - return 0; -} - -static int _compile_all_tvar_union(jl_tupletype_t *methsig, jl_svec_t *tvars) -{ - // f{<:Union{...}}(...) is a common pattern - // and expanding the Union may give a leaf function - jl_tvar_t **tvs; - int tvarslen; - if (jl_is_typevar(tvars)) { - tvs = (jl_tvar_t**)&tvars; - tvarslen = 1; - } - else { - tvs = (jl_tvar_t**)jl_svec_data(tvars); - tvarslen = jl_svec_len(tvars); - if (tvarslen == 0) { - if (jl_is_leaf_type((jl_value_t*)methsig)) { - // usually can create a specialized version of the function, - // if the signature is already a leaftype - if (jl_compile_hint(methsig)) { - return 1; - } - } - return 0; - } - } - - int complete = 1; - jl_value_t **env; - JL_GC_PUSHARGS(env, 2 * tvarslen); - int *idx = (int*)alloca(sizeof(int) * tvarslen); - int i; - for (i = 0; i < tvarslen; i++) { - idx[i] = 0; - env[2 * i] = (jl_value_t*)tvs[i]; - env[2 * i + 1] = jl_bottom_type; // initialize the list with Union{}, since T<:Union{} is always a valid option - } - - for (i = 0; i < tvarslen; /* incremented by inner loop */) { - jl_value_t *sig; - JL_TRY { - sig = (jl_value_t*) - jl_instantiate_type_with((jl_value_t*)methsig, env, tvarslen); - } - JL_CATCH { - goto getnext; // sigh, we found an invalid type signature. should we warn the user? - } - assert(jl_is_tuple_type(sig)); - if (sig == jl_bottom_type || tupletype_any_bottom(sig)) { - goto getnext; // signature wouldn't be callable / is invalid -- skip it - } - if (jl_is_leaf_type(sig)) { - if (jl_compile_hint((jl_tupletype_t*)sig)) { - if (!jl_has_typevars((jl_value_t*)sig)) goto getnext; // success - } - } - complete = 0; - -getnext: - for (i = 0; i < tvarslen; i++) { - jl_tvar_t *tv = tvs[i]; - if (jl_is_uniontype(tv->ub)) { - jl_uniontype_t *ub = (jl_uniontype_t*)tv->ub; - size_t l = jl_svec_len(ub->types); - size_t j = idx[i]; - if (j == l) { - env[2 * i + 1] = jl_bottom_type; - idx[i] = 0; - } - else { - jl_value_t *ty = jl_svecref(ub->types, j); - if (!jl_is_leaf_type(ty)) - ty = (jl_value_t*)jl_new_typevar(tv->name, tv->lb, ty); - env[2 * i + 1] = ty; - idx[i] = j + 1; - break; - } - } - else { - env[2 * i + 1] = (jl_value_t*)tv; - complete = 0; - } - } - } - JL_GC_POP(); - return complete; -} -#endif -#if 0 -static int _compile_all_union(jl_tupletype_t *sig, jl_svec_t *tvars) -{ - // f(::Union{...}, ...) is a common pattern - // and expanding the Union may give a leaf function - int complete = 1; - size_t count_unions = 0; - size_t i, l = jl_svec_len(sig->parameters); - jl_svec_t *p = NULL; - jl_tupletype_t *methsig = NULL; - - for (i = 0; i < l; i++) { - jl_value_t *ty = jl_svecref(sig->parameters, i); - if (jl_is_uniontype(ty)) { - jl_svec_t *utypes = ((jl_uniontype_t*)ty)->types; - size_t l = jl_svec_len(utypes); - if (l == 0) - return 1; // why does this method exist? - ++count_unions; - } - } - - //if (count_unions == 0) - // return _compile_all_tvar_union(sig, tvars); - - int *idx = (int*)alloca(sizeof(int) * count_unions); - for (i = 0; i < count_unions; i++) { - idx[i] = 0; - } - - JL_GC_PUSH2(&p, &methsig); - int idx_ctr = 0, incr = 0; - while (!incr) { - jl_svec_t *p = jl_alloc_svec_uninit(l); - for (i = 0, idx_ctr = 0, incr = 1; i < l; i++) { - jl_value_t *ty = jl_svecref(sig->parameters, i); - if (jl_is_uniontype(ty)) { - jl_svec_t *utypes = ((jl_uniontype_t*)ty)->types; - size_t l = jl_svec_len(utypes); - size_t j = idx[idx_ctr]; - jl_svecset(p, i, jl_svecref(utypes, j)); - ++j; - if (incr) { - if (j == l) { - idx[idx_ctr] = 0; - } - else { - idx[idx_ctr] = j; - incr = 0; - } - } - ++idx_ctr; - } - else { - jl_svecset(p, i, ty); - } - } - methsig = jl_apply_tuple_type(p); - //if (!_compile_all_tvar_union(methsig, tvars)) - // complete = 0; - } - - JL_GC_POP(); - return complete; -} -#endif - -static void _compile_all_deq(jl_array_t *found) -{ - int found_i, found_l = jl_array_len(found); - jl_printf(JL_STDERR, "found %d uncompiled methods for compile-all\n", (int)found_l); - jl_method_instance_t *linfo = NULL; - jl_code_info_t *src = NULL; - JL_GC_PUSH2(&linfo, &src); - for (found_i = 0; found_i < found_l; found_i++) { - if (found_i % (1 + found_l / 300) == 0 || found_i == found_l - 1) // show 300 progress steps, to show progress without overwhelming log files - jl_printf(JL_STDERR, " %d / %d\r", found_i + 1, found_l); - jl_typemap_entry_t *ml = (jl_typemap_entry_t*)jl_array_ptr_ref(found, found_i); - jl_method_t *m = ml->func.method; - jl_method_instance_t *linfo = m->unspecialized; - if (!linfo) { - linfo = jl_get_specialized(m, (jl_value_t*)ml->sig, jl_emptysvec); - m->unspecialized = linfo; - jl_gc_wb(m, linfo); - } - - //// infer this function now, if necessary - //if (linfo->jlcall_api == 2) - // continue; - //src = jl_type_infer(&linfo, jl_world_counter, 1); - //if (linfo->jlcall_api == 2) - // continue; - - // keep track of whether all possible signatures have been cached (and thus whether it can skip trying to compile the template function) - // this is necessary because many intrinsics try to call static_eval and thus are not compilable unspecialized - int complete = 0;//_compile_all_union(ml->sig, ml->tvars); - if (complete) { - if (linfo->fptr == NULL && linfo->functionObjectsDecls.functionObject == NULL) - // indicate that this method doesn't need to be compiled, because it was fully covered above - // TODO: do this some other way - linfo->fptr = (jl_fptr_t)(uintptr_t)-1; - } - else { - jl_compile_linfo(&linfo, src, jl_world_counter, &jl_default_cgparams); - assert(linfo->functionObjectsDecls.functionObject != NULL); - } - } - JL_GC_POP(); - jl_printf(JL_STDERR, "\n"); -} - -static int _compile_all_enq(jl_typemap_entry_t *ml, void *env) -{ - jl_array_t *found = (jl_array_t*)env; - // method definition -- compile template field - jl_method_t *m = ml->func.method; - if (!m->unspecialized || - (m->unspecialized->functionObjectsDecls.functionObject == NULL && - m->unspecialized->jlcall_api != 2 && - m->unspecialized->fptr == NULL)) { - // found a lambda that still needs to be compiled - jl_array_ptr_1d_push(found, (jl_value_t*)ml); - } - return 1; -} - -static void _compile_all_enq_module(jl_module_t *m, jl_array_t *found) -{ - // scan through all types reachable from 'v' and - // record all jl_method_instance_t objects and signatures in their method tables - size_t i, sz = m->bindings.size; - for(i=1; i < sz; i+=2) { - if (m->bindings.table[i] != HT_NOTFOUND) { - jl_binding_t *b = (jl_binding_t*)m->bindings.table[i]; - if (b->owner == m && b->value && b->constp) { - jl_value_t *v = b->value; - if (jl_is_datatype(v)) { - jl_typename_t *tn = ((jl_datatype_t*)v)->name; - if (tn->module == m && tn->name == b->name) { - jl_methtable_t *mt = tn->mt; - if (mt != NULL && (jl_value_t*)mt != jl_nothing) { - jl_typemap_visitor(mt->defs, _compile_all_enq, (void*)found); - } - } - } - else if (jl_is_module(v)) { - jl_module_t *child = (jl_module_t*)b->value; - if (child != m && child->parent == m && child->name == b->name) { - // this is the original/primary binding for the submodule - _compile_all_enq_module(child, found); - } - } - } - } - } -} - -static void jl_compile_all_defs(void) -{ - // this "found" array will contain - // TypeMapEntries for Methods and MethodInstances that need to be compiled - jl_array_t *m = jl_alloc_vec_any(0); - JL_GC_PUSH1(&m); - while (1) { - _compile_all_enq_module(jl_main_module, m); - size_t changes = jl_array_len(m); - if (!changes) - break; - _compile_all_deq(m); - jl_array_del_end(m, changes); - } - JL_GC_POP(); -} - -static int _precompile_enq_tfunc(jl_typemap_entry_t *l, void *closure) -{ - if (jl_is_method_instance(l->func.value) && - l->func.linfo->functionObjectsDecls.functionObject == NULL && - l->func.linfo->jlcall_api != 2) - jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)l->sig); - return 1; -} - -static int _precompile_enq_spec(jl_typemap_entry_t *def, void *closure) -{ - jl_typemap_visitor(def->func.method->specializations, _precompile_enq_tfunc, closure); - return 1; -} - -static void _precompile_enq_module(jl_module_t *m, jl_array_t *unspec) -{ - // removes all method caches - size_t i; - void **table = m->bindings.table; - for (i = 1; i < m->bindings.size; i += 2) { - if (table[i] != HT_NOTFOUND) { - jl_binding_t *b = (jl_binding_t*)table[i]; - if (b->owner == m && b->value && b->constp) { - if (jl_is_datatype(b->value)) { - jl_typename_t *tn = ((jl_datatype_t*)b->value)->name; - if (tn->module == m && tn->name == b->name) { - jl_methtable_t *mt = tn->mt; - if (mt != NULL && (jl_value_t*)mt != jl_nothing) { - jl_typemap_visitor(mt->defs, _precompile_enq_spec, (void*)unspec); - } - } - } - else if (jl_is_module(b->value)) { - jl_module_t *child = (jl_module_t*)b->value; - if (child != m && child->parent == m && child->name == b->name) { - // this is the original/primary binding for the submodule - _precompile_enq_module((jl_module_t*)b->value, unspec); - } - } - } - } - } -} - -static void jl_compile_specializations(void) -{ - // this "found" array will contain function - // type signatures that were inferred but haven't been compiled - jl_array_t *m = jl_alloc_vec_any(0); - JL_GC_PUSH1(&m); - _precompile_enq_module(jl_main_module, m); - size_t i, l; - for (i = 0, l = jl_array_len(m); i < l; i++) { - jl_compile_hint((jl_tupletype_t*)jl_array_ptr_ref(m, i)); - } - JL_GC_POP(); -} - -void jl_precompile(int all) { - if (all) - jl_compile_all_defs(); - jl_compile_specializations(); -} - #ifdef JL_TRACE static int trace_en = 0; static int error_en = 1; @@ -2112,7 +1790,8 @@ jl_typemap_entry_t *call_cache[N_CALL_CACHE]; static uint8_t pick_which[N_CALL_CACHE]; #ifdef JL_GF_PROFILE size_t ncalls; -void call_cache_stats() { +void call_cache_stats() +{ int pick_which_stat[4] = {0, 0, 0, 0}; int i, count = 0; for (i = 0; i < N_CALL_CACHE; i++) { diff --git a/src/init.c b/src/init.c index 3cc3f04b22dcb..778be20efb38b 100644 --- a/src/init.c +++ b/src/init.c @@ -177,7 +177,7 @@ static void jl_uv_exitcleanup_walk(uv_handle_t *handle, void *arg) void jl_write_coverage_data(void); void jl_write_malloc_log(void); -static void julia_save(void); +void jl_write_compiler_output(void); static struct uv_shutdown_queue_item *next_shutdown_queue_item(struct uv_shutdown_queue_item *item) { @@ -193,7 +193,7 @@ void jl_uv_call_close_callback(jl_value_t *val); JL_DLLEXPORT void jl_atexit_hook(int exitcode) { jl_ptls_t ptls = jl_get_ptls_states(); - if (exitcode == 0) julia_save(); + if (exitcode == 0) jl_write_compiler_output(); jl_print_gc_stats(JL_STDERR); if (jl_options.code_coverage) jl_write_coverage_data(); @@ -710,74 +710,6 @@ void _julia_init(JL_IMAGE_SEARCH rel) jl_install_sigint_handler(); } -extern int asprintf(char **str, const char *fmt, ...); - -JL_DLLEXPORT int jl_generating_output(void) -{ - return jl_options.outputo || jl_options.outputbc || jl_options.outputji; -} - -void jl_precompile(int all); - -static void julia_save(void) -{ - if (!jl_generating_output()) - return; - - if (!jl_options.incremental) - jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL); - - if (!jl_module_init_order) { - jl_printf(JL_STDERR, "WARNING: --output requested, but no modules defined during run\n"); - return; - } - - jl_array_t *worklist = jl_module_init_order; - JL_GC_PUSH1(&worklist); - jl_module_init_order = jl_alloc_vec_any(0); - int i, l = jl_array_len(worklist); - for (i = 0; i < l; i++) { - jl_value_t *m = jl_arrayref(worklist, i); - if (jl_get_global((jl_module_t*)m, jl_symbol("__init__"))) { - jl_array_ptr_1d_push(jl_module_init_order, m); - } - } - - if (jl_options.incremental) { - if (jl_options.outputji) - if (jl_save_incremental(jl_options.outputji, worklist)) - jl_exit(1); - if (jl_options.outputbc) - jl_printf(JL_STDERR, "WARNING: incremental output to a .bc file is not implemented\n"); - if (jl_options.outputo) - jl_printf(JL_STDERR, "WARNING: incremental output to a .o file is not implemented\n"); - } - else { - ios_t *s = NULL; - if (jl_options.outputo || jl_options.outputbc) - s = jl_create_system_image(); - - if (jl_options.outputji) { - if (s == NULL) { - jl_save_system_image(jl_options.outputji); - } - else { - ios_t f; - if (ios_file(&f, jl_options.outputji, 1, 1, 1, 1) == NULL) - jl_errorf("cannot open system image file \"%s\" for writing", jl_options.outputji); - ios_write(&f, (const char*)s->buf, (size_t)s->size); - ios_close(&f); - } - } - - if (jl_options.outputo || jl_options.outputbc) - jl_dump_native(jl_options.outputbc, - jl_options.outputo, - (const char*)s->buf, (size_t)s->size); - } - JL_GC_POP(); -} - static jl_value_t *core(const char *name) { return jl_get_global(jl_core_module, jl_symbol(name)); diff --git a/src/jltypes.c b/src/jltypes.c index 032d992b06298..089a3407e0d4b 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -307,6 +307,35 @@ static int count_union_components(jl_value_t **types, size_t n) return c; } +int jl_count_union_components(jl_value_t *v) +{ + if (!jl_is_uniontype(v)) return 1; + jl_uniontype_t *u = (jl_uniontype_t*)v; + return jl_count_union_components(u->a) + jl_count_union_components(u->b); +} + +// Return the `*pi`th element of a nested type union, according to a +// standard traversal order. Anything that is not itself a `Union` is +// considered an "element". `*pi` is destroyed in the process. +static jl_value_t *nth_union_component(jl_value_t *v, int *pi) +{ + if (!jl_is_uniontype(v)) { + if (*pi == 0) + return v; + (*pi)--; + return NULL; + } + jl_uniontype_t *u = (jl_uniontype_t*)v; + jl_value_t *a = nth_union_component(u->a, pi); + if (a) return a; + return nth_union_component(u->b, pi); +} + +jl_value_t *jl_nth_union_component(jl_value_t *v, int i) +{ + return nth_union_component(v, &i); +} + static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx) { size_t i; @@ -1363,12 +1392,14 @@ static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *en JL_DLLEXPORT jl_value_t *jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals) { - jl_value_t *typ; - JL_TRY { - typ = _jl_instantiate_type_in_env(ty, env, vals, NULL); - } - JL_CATCH { - typ = jl_bottom_type; + jl_value_t *typ = ty; + if (jl_is_unionall(env)) { + JL_TRY { + typ = _jl_instantiate_type_in_env(ty, env, vals, NULL); + } + JL_CATCH { + typ = jl_bottom_type; + } } return typ; } diff --git a/src/julia_internal.h b/src/julia_internal.h index b5d5139bcced4..8eb3842a8a9be 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -433,6 +433,8 @@ jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n); JL_DLLEXPORT jl_value_t *jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals); jl_value_t *jl_substitute_var(jl_value_t *t, jl_tvar_t *var, jl_value_t *val); jl_svec_t *jl_outer_unionall_vars(jl_value_t *u); +int jl_count_union_components(jl_value_t *v); +jl_value_t *jl_nth_union_component(jl_value_t *v, int i); jl_datatype_t *jl_new_uninitialized_datatype(void); jl_datatype_t *jl_new_abstracttype(jl_value_t *name, jl_datatype_t *super, jl_svec_t *parameters); @@ -581,6 +583,7 @@ JL_DLLEXPORT jl_methtable_t *jl_new_method_table(jl_sym_t *name, jl_module_t *mo jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world); JL_DLLEXPORT int jl_has_call_ambiguities(jl_tupletype_t *types, jl_method_t *m); jl_method_instance_t *jl_get_specialized(jl_method_t *m, jl_value_t *types, jl_svec_t *sp); +int jl_is_rettype_inferred(jl_method_instance_t *li); JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_tupletype_t *type, size_t world); JL_DLLEXPORT jl_method_instance_t *jl_specializations_get_linfo(jl_method_t *m, jl_value_t *type, jl_svec_t *sparams, size_t world); JL_DLLEXPORT void jl_method_instance_add_backedge(jl_method_instance_t *callee, jl_method_instance_t *caller); diff --git a/src/precompile.c b/src/precompile.c new file mode 100644 index 0000000000000..8994941d15514 --- /dev/null +++ b/src/precompile.c @@ -0,0 +1,358 @@ +// This file is a part of Julia. License is MIT: http://julialang.org/license + +/* + precompile.c + Generating compiler output artifacts (object files, etc.) +*/ + +#include +#include + +#include "julia.h" +#include "julia_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +JL_DLLEXPORT int jl_generating_output(void) +{ + return jl_options.outputo || jl_options.outputbc || jl_options.outputji; +} + +void jl_precompile(int all); + +void jl_write_compiler_output(void) +{ + if (!jl_generating_output()) + return; + + if (!jl_options.incremental) + jl_precompile(jl_options.compile_enabled == JL_OPTIONS_COMPILE_ALL); + + if (!jl_module_init_order) { + jl_printf(JL_STDERR, "WARNING: --output requested, but no modules defined during run\n"); + return; + } + + jl_array_t *worklist = jl_module_init_order; + JL_GC_PUSH1(&worklist); + jl_module_init_order = jl_alloc_vec_any(0); + int i, l = jl_array_len(worklist); + for (i = 0; i < l; i++) { + jl_value_t *m = jl_arrayref(worklist, i); + if (jl_get_global((jl_module_t*)m, jl_symbol("__init__"))) { + jl_array_ptr_1d_push(jl_module_init_order, m); + } + } + + if (jl_options.incremental) { + if (jl_options.outputji) + if (jl_save_incremental(jl_options.outputji, worklist)) + jl_exit(1); + if (jl_options.outputbc) + jl_printf(JL_STDERR, "WARNING: incremental output to a .bc file is not implemented\n"); + if (jl_options.outputo) + jl_printf(JL_STDERR, "WARNING: incremental output to a .o file is not implemented\n"); + } + else { + ios_t *s = NULL; + if (jl_options.outputo || jl_options.outputbc) + s = jl_create_system_image(); + + if (jl_options.outputji) { + if (s == NULL) { + jl_save_system_image(jl_options.outputji); + } + else { + ios_t f; + if (ios_file(&f, jl_options.outputji, 1, 1, 1, 1) == NULL) + jl_errorf("cannot open system image file \"%s\" for writing", jl_options.outputji); + ios_write(&f, (const char*)s->buf, (size_t)s->size); + ios_close(&f); + } + } + + if (jl_options.outputo || jl_options.outputbc) + jl_dump_native(jl_options.outputbc, + jl_options.outputo, + (const char*)s->buf, (size_t)s->size); + } + JL_GC_POP(); +} + +static int tupletype_any_bottom(jl_value_t *sig) +{ + sig = jl_unwrap_unionall(sig); + assert(jl_is_tuple_type(sig)); + jl_svec_t *types = ((jl_tupletype_t*)sig)->types; + size_t i, l = jl_svec_len(types); + for (i = 0; i < l; i++) { + if (jl_svecref(types, i) == jl_bottom_type) + return 1; + } + return 0; +} + +// f{<:Union{...}}(...) is a common pattern +// and expanding the Union may give a leaf function +static void _compile_all_tvar_union(jl_value_t *methsig) +{ + if (!jl_is_unionall(methsig) && jl_is_leaf_type(methsig)) { + // usually can create a specialized version of the function, + // if the signature is already a leaftype + if (jl_compile_hint((jl_tupletype_t*)methsig)) + return; + } + + int tvarslen = jl_subtype_env_size(methsig); + jl_value_t *sigbody = methsig; + jl_value_t **env; + JL_GC_PUSHARGS(env, 2 * tvarslen); + int *idx = (int*)alloca(sizeof(int) * tvarslen); + int i; + for (i = 0; i < tvarslen; i++) { + assert(jl_is_unionall(sigbody)); + idx[i] = 0; + env[2 * i] = (jl_value_t*)((jl_unionall_t*)sigbody)->var; + env[2 * i + 1] = jl_bottom_type; // initialize the list with Union{}, since T<:Union{} is always a valid option + sigbody = ((jl_unionall_t*)sigbody)->body; + } + + for (i = 0; i < tvarslen; /* incremented by inner loop */) { + jl_value_t *sig; + JL_TRY { + // TODO: wrap in UnionAll for each tvar in env[2*i + 1] ? + // currently doesn't matter much, since jl_compile_hint doesn't work on abstract types + sig = (jl_value_t*)jl_instantiate_type_with(sigbody, env, tvarslen); + } + JL_CATCH { + goto getnext; // sigh, we found an invalid type signature. should we warn the user? + } + assert(jl_is_tuple_type(sig)); + if (sig == jl_bottom_type || tupletype_any_bottom(sig)) + goto getnext; // signature wouldn't be callable / is invalid -- skip it + if (jl_is_leaf_type(sig)) { + if (jl_compile_hint((jl_tupletype_t*)sig)) + goto getnext; // success + } + + getnext: + for (i = 0; i < tvarslen; i++) { + jl_tvar_t *tv = (jl_tvar_t*)env[2 * i]; + if (jl_is_uniontype(tv->ub)) { + size_t l = jl_count_union_components(tv->ub); + size_t j = idx[i]; + if (j == l) { + env[2 * i + 1] = jl_bottom_type; + idx[i] = 0; + } + else { + jl_value_t *ty = jl_nth_union_component(tv->ub, j); + if (!jl_is_leaf_type(ty)) + ty = (jl_value_t*)jl_new_typevar(tv->name, tv->lb, ty); + env[2 * i + 1] = ty; + idx[i] = j + 1; + break; + } + } + else { + env[2 * i + 1] = (jl_value_t*)tv; + } + } + } + JL_GC_POP(); +} + +// f(::Union{...}, ...) is a common pattern +// and expanding the Union may give a leaf function +static void _compile_all_union(jl_value_t *sig) +{ + jl_tupletype_t *sigbody = (jl_tupletype_t*)jl_unwrap_unionall(sig); + size_t count_unions = 0; + size_t i, l = jl_svec_len(sigbody->parameters); + jl_svec_t *p = NULL; + jl_value_t *methsig = NULL; + + for (i = 0; i < l; i++) { + jl_value_t *ty = jl_svecref(sigbody->parameters, i); + if (jl_is_uniontype(ty)) + ++count_unions; + else if (ty == jl_bottom_type) + return; // why does this method exist? + } + + if (count_unions == 0) { + _compile_all_tvar_union(sig); + return; + } + + int *idx = (int*)alloca(sizeof(int) * count_unions); + for (i = 0; i < count_unions; i++) { + idx[i] = 0; + } + + JL_GC_PUSH2(&p, &methsig); + int idx_ctr = 0, incr = 0; + while (!incr) { + jl_svec_t *p = jl_alloc_svec_uninit(l); + for (i = 0, idx_ctr = 0, incr = 1; i < l; i++) { + jl_value_t *ty = jl_svecref(sigbody->parameters, i); + if (jl_is_uniontype(ty)) { + size_t l = jl_count_union_components(ty); + size_t j = idx[idx_ctr]; + jl_svecset(p, i, jl_nth_union_component(ty, j)); + ++j; + if (incr) { + if (j == l) { + idx[idx_ctr] = 0; + } + else { + idx[idx_ctr] = j; + incr = 0; + } + } + ++idx_ctr; + } + else { + jl_svecset(p, i, ty); + } + } + methsig = (jl_value_t*)jl_apply_tuple_type(p); + methsig = jl_rewrap_unionall(methsig, sig); + _compile_all_tvar_union(methsig); + } + + JL_GC_POP(); +} + +static void _compile_all_deq(jl_array_t *found) +{ + int found_i, found_l = jl_array_len(found); + jl_printf(JL_STDERR, "found %d uncompiled methods for compile-all\n", (int)found_l); + jl_method_instance_t *linfo = NULL; + jl_value_t *src = NULL; + JL_GC_PUSH2(&linfo, &src); + for (found_i = 0; found_i < found_l; found_i++) { + if (found_i % (1 + found_l / 300) == 0 || found_i == found_l - 1) // show 300 progress steps, to show progress without overwhelming log files + jl_printf(JL_STDERR, " %d / %d\r", found_i + 1, found_l); + jl_typemap_entry_t *ml = (jl_typemap_entry_t*)jl_array_ptr_ref(found, found_i); + jl_method_t *m = ml->func.method; + if (m->isstaged) // TODO: generic implementations of generated functions + continue; + linfo = m->unspecialized; + if (!linfo) { + linfo = jl_get_specialized(m, (jl_value_t*)m->sig, jl_emptysvec); + m->unspecialized = linfo; + jl_gc_wb(m, linfo); + } + + if (linfo->jlcall_api == 2) + continue; + src = m->source; + // TODO: the `unspecialized` field is not yet world-aware, so we can't store + // an inference result there. + //src = jl_type_infer(&linfo, jl_world_counter, 1); + //m->unspecialized = linfo; + //jl_gc_wb(m, linfo); + //if (linfo->jlcall_api == 2) + // continue; + + // first try to create leaf signatures from the signature declaration and compile those + _compile_all_union((jl_value_t*)ml->sig); + // then also compile the generic fallback + jl_compile_linfo(&linfo, (jl_code_info_t*)src, jl_world_counter, &jl_default_cgparams); + assert(linfo->functionObjectsDecls.functionObject != NULL); + } + JL_GC_POP(); + jl_printf(JL_STDERR, "\n"); +} + +static int compile_all_enq__(jl_typemap_entry_t *ml, void *env) +{ + jl_array_t *found = (jl_array_t*)env; + // method definition -- compile template field + jl_method_t *m = ml->func.method; + if (!m->isstaged && + (!m->unspecialized || + (m->unspecialized->functionObjectsDecls.functionObject == NULL && + m->unspecialized->jlcall_api != 2 && + m->unspecialized->fptr == NULL))) { + // found a lambda that still needs to be compiled + jl_array_ptr_1d_push(found, (jl_value_t*)ml); + } + return 1; +} + + +static void compile_all_enq_(jl_methtable_t *mt, void *env) +{ + jl_typemap_visitor(mt->defs, compile_all_enq__, env); +} + +void jl_foreach_mtable_in_module( + jl_module_t *m, + void (*visit)(jl_methtable_t *mt, void *env), + void *env); + +static void jl_compile_all_defs(void) +{ + // this "found" array will contain + // TypeMapEntries for Methods and MethodInstances that need to be compiled + jl_array_t *m = jl_alloc_vec_any(0); + JL_GC_PUSH1(&m); + while (1) { + jl_foreach_mtable_in_module(jl_main_module, compile_all_enq_, m); + size_t changes = jl_array_len(m); + if (!changes) + break; + _compile_all_deq(m); + jl_array_del_end(m, changes); + } + JL_GC_POP(); +} + +static int precompile_enq_specialization_(jl_typemap_entry_t *l, void *closure) +{ + if (jl_is_method_instance(l->func.value) && + l->func.linfo->functionObjectsDecls.functionObject == NULL && + l->func.linfo->jlcall_api != 2) + jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)l->sig); + return 1; +} + +static int precompile_enq_all_specializations__(jl_typemap_entry_t *def, void *closure) +{ + jl_typemap_visitor(def->func.method->specializations, precompile_enq_specialization_, closure); + return 1; +} + +static void precompile_enq_all_specializations_(jl_methtable_t *mt, void *env) +{ + jl_typemap_visitor(mt->defs, precompile_enq_all_specializations__, env); +} + +static void jl_compile_specializations(void) +{ + // this "found" array will contain function + // type signatures that were inferred but haven't been compiled + jl_array_t *m = jl_alloc_vec_any(0); + JL_GC_PUSH1(&m); + jl_foreach_mtable_in_module(jl_main_module, precompile_enq_all_specializations_, m); + size_t i, l; + for (i = 0, l = jl_array_len(m); i < l; i++) { + jl_compile_hint((jl_tupletype_t*)jl_array_ptr_ref(m, i)); + } + JL_GC_POP(); +} + +void jl_precompile(int all) +{ + if (all) + jl_compile_all_defs(); + jl_compile_specializations(); +} + +#ifdef __cplusplus +} +#endif