From cfff392aa4e7c59cd31521dd9aeb9c5a42b9cd37 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 23 Apr 2020 15:18:52 -0400 Subject: [PATCH] make `ccallable` work with system images and precompilation fixes #35014 --- base/c.jl | 15 ++-- src/anticodegen.c | 2 - src/aotcompile.cpp | 9 ++- src/cgutils.cpp | 7 ++ src/codegen.cpp | 159 ++++++++++++------------------------------- src/dump.c | 16 +++++ src/gc.c | 2 - src/jitlayers.cpp | 97 +++++++++++++++++--------- src/jltypes.c | 6 +- src/julia.h | 1 + src/julia_internal.h | 2 +- src/method.c | 1 + src/precompile.c | 24 +++++-- src/staticdata.c | 11 ++- test/precompile.jl | 14 ++++ 15 files changed, 198 insertions(+), 168 deletions(-) diff --git a/base/c.jl b/base/c.jl index ab502ef53d0bc..ead91e330cd7d 100644 --- a/base/c.jl +++ b/base/c.jl @@ -463,8 +463,8 @@ function reenable_sigint(f::Function) res end -function ccallable(f::Function, rt::Type, argt::Type, name::Union{AbstractString,Symbol}=string(f)) - ccall(:jl_extern_c, Cvoid, (Any, Any, Any, Cstring), f, rt, argt, name) +function ccallable(rt::Type, sigt::Type) + ccall(:jl_extern_c, Cvoid, (Any, Any), rt, sigt) end function expand_ccallable(rt, def) @@ -480,17 +480,22 @@ function expand_ccallable(rt, def) error("@ccallable requires a return type") end if sig.head === :call - name = sig.args[1] + f = sig.args[1] + if isa(f,Expr) && f.head === :(::) + f = f.args[end] + else + f = :(typeof($f)) + end at = map(sig.args[2:end]) do a if isa(a,Expr) && a.head === :(::) - a.args[2] + a.args[end] else :Any end end return quote $(esc(def)) - ccallable($(esc(name)), $(esc(rt)), $(Expr(:curly, :Tuple, map(esc, at)...)), $(string(name))) + ccallable($(esc(rt)), $(Expr(:curly, :Tuple, esc(f), map(esc, at)...))) end end end diff --git a/src/anticodegen.c b/src/anticodegen.c index 3281b5e69c244..1b157af267b1c 100644 --- a/src/anticodegen.c +++ b/src/anticodegen.c @@ -66,5 +66,3 @@ JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION(void) { return 0; } - -jl_array_t *jl_cfunction_list; diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 0b08eaaf51c90..74ddc5805efd2 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -271,7 +271,14 @@ void *jl_create_native(jl_array_t *methods, const jl_cgparams_t cgparams) continue; size_t i, l; for (i = 0, l = jl_array_len(methods); i < l; i++) { - mi = (jl_method_instance_t*)jl_array_ptr_ref(methods, i); + // each item in this list is either a MethodInstance indicating something + // to compile, or an svec(rettype, sig) describing a C-callable alias to create. + jl_value_t *item = jl_array_ptr_ref(methods, i); + if (jl_is_simplevector(item)) { + jl_compile_extern_c(shadow_output, ¶ms, NULL, jl_svecref(item, 0), jl_svecref(item, 1)); + continue; + } + mi = (jl_method_instance_t*)item; src = NULL; // if this method is generally visible to the current compilation world, // and this is either the primary world, or not applicable in the primary world diff --git a/src/cgutils.cpp b/src/cgutils.cpp index b50a633a9114e..87c3b5ee75f34 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -774,6 +774,13 @@ static Type *julia_struct_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, jl_unionall return _julia_struct_to_llvm(&ctx.emission_context, jt, ua, isboxed); } +bool jl_type_mappable_to_c(jl_value_t *ty) +{ + jl_codegen_params_t params; + bool toboxed; + return _julia_struct_to_llvm(¶ms, ty, NULL, &toboxed) != NULL; +} + static bool is_datatype_all_pointers(jl_datatype_t *dt) { size_t i, l = jl_datatype_nfields(dt); diff --git a/src/codegen.cpp b/src/codegen.cpp index 26722855b92c2..c14daa0c68ada 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4018,8 +4018,8 @@ static void emit_cfunc_invalidate( static Function* gen_cfun_wrapper( Module *into, jl_codegen_params_t ¶ms, - const function_sig_t &sig, jl_value_t *ff, - jl_typemap_entry_t *sf, jl_value_t *declrt, jl_method_instance_t *lam, + const function_sig_t &sig, jl_value_t *ff, const char *aliasname, + jl_value_t *declrt, jl_method_instance_t *lam, jl_unionall_t *unionall_env, jl_svec_t *sparam_vals, jl_array_t **closure_types) { // Generate a c-callable wrapper @@ -4031,12 +4031,11 @@ static Function* gen_cfun_wrapper( jl_value_t *astrt = (jl_value_t*)jl_any_type; void *callptr = NULL; int calltype = 0; - // infer it first, if necessary - // FIXME! pretend this is OK - if (lam) + if (aliasname) + name = aliasname; + else if (lam) name = jl_symbol_name(lam->def.method->name); if (lam && params.cache) { - // if (!into) // TODO: this isn't ideal to be unconditionally calling type inference (and compile) from here codeinst = jl_compile_method_internal(lam, world); assert(codeinst->invoke); @@ -4090,20 +4089,6 @@ static Function* gen_cfun_wrapper( cw->setAttributes(attributes); jl_init_function(cw); Function *cw_proto = into ? cw : function_proto(cw); - // Save the Function object reference - if (sf) { - jl_value_t *oldsf = sf->func.value; - size_t i, oldlen = jl_svec_len(oldsf); - jl_value_t *newsf = (jl_value_t*)jl_alloc_svec(oldlen + 2); - JL_GC_PUSH1(&newsf); - jl_svecset(newsf, 0, sig.rt); - jl_svecset(newsf, 1, jl_box_voidpointer((void*)cw_proto)); - for (i = 0; i < oldlen; i++) - jl_svecset(newsf, i + 2, jl_svecref(oldsf, i)); - sf->func.value = newsf; - jl_gc_wb(sf, sf->func.value); - JL_GC_POP(); - } jl_codectx_t ctx(jl_LLVMContext, params); ctx.f = cw; @@ -4519,6 +4504,10 @@ static Function* gen_cfun_wrapper( cw_proto = into ? cw_make : function_proto(cw_make); } + if (aliasname) { + GlobalAlias::create(cw->getType()->getElementType(), cw->getType()->getAddressSpace(), + GlobalValue::ExternalLinkage, aliasname, cw, M); + } if (!into) jl_finalize_module(std::unique_ptr(M)); @@ -4632,8 +4621,8 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con jl_method_instance_t *lam = sigt ? jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0) : NULL; Value *F = gen_cfun_wrapper( jl_Module, ctx.emission_context, - sig, fexpr_rt.constant, - NULL, declrt, lam, + sig, fexpr_rt.constant, NULL, + declrt, lam, unionall_env, sparam_vals, &closure_types); bool outboxed; if (nest) { @@ -4693,116 +4682,54 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con return mark_julia_type(ctx, F, outboxed, output_type); } -const struct jl_typemap_info cfunction_cache = { - 1, (jl_datatype_t**)&jl_array_any_type -}; - -jl_array_t *jl_cfunction_list; - -Function *jl_cfunction_object(jl_function_t *ff, jl_value_t *declrt, jl_tupletype_t *argt, - jl_codegen_params_t ¶ms) +// do codegen to create a C-callable alias/wrapper, or if sysimg_handle is set, +// restore one from a loaded system image. +Function *jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms) { - // Assumes the codegen lock is acquired. The caller is responsible for that. - jl_ptls_t ptls = jl_get_ptls_states(); - if (ptls->in_pure_callback) - jl_error("cfunction cannot be used in a generated function"); - - // validate and unpack the arguments - JL_TYPECHK(cfunction, type, declrt); - if (!jl_is_tuple_type(argt)) // the C API requires that argt Tuple type actually be an svec - jl_type_error("cfunction", (jl_value_t*)jl_anytuple_type_type, (jl_value_t*)argt); - // trampolines are not supported here: - // check that f is a guaranteed singleton type - jl_value_t *ft = jl_typeof(ff); - if (((jl_datatype_t*)ft)->instance != ff) - jl_error("cfunction: use `@cfunction` to make closures"); - - // check the cache structure - // this has three levels (for the 3 parameters above) - // first split on `ft` using a simple eqtable - // then use the typemap to split on argt - // and finally, pick declrt from the pair-list - jl_typemap_t *cache_l2 = NULL; - jl_typemap_entry_t *cache_l3 = NULL; - if (!jl_cfunction_list) { - jl_cfunction_list = jl_alloc_vec_any(16); - } - else { - cache_l2 = jl_eqtable_get(jl_cfunction_list, ft, NULL); - if (cache_l2) { - struct jl_typemap_assoc search = {(jl_value_t*)argt, 1, NULL, 0, ~(size_t)0}; - cache_l3 = jl_typemap_assoc_by_type(cache_l2, &search, /*offs*/0, /*subtype*/0); - if (cache_l3) { - jl_svec_t *sf = (jl_svec_t*)cache_l3->func.value; - size_t i, l = jl_svec_len(sf); - for (i = 0; i < l; i += 2) { - jl_value_t *ti = jl_svecref(sf, i); - if (jl_egal(ti, declrt)) { - return (Function*)jl_unbox_voidpointer(jl_svecref(sf, i + 1)); - } - } - } - } - } - - if (cache_l3 == NULL) { - jl_typemap_t *insert = cache_l2; - if (!insert) - insert = jl_nothing; - cache_l3 = jl_typemap_insert(&insert, (jl_value_t*)insert, (jl_tupletype_t*)argt, - NULL, jl_emptysvec, (jl_value_t*)jl_emptysvec, /*offs*/0, &cfunction_cache, 1, ~(size_t)0); - if (insert != cache_l2) - jl_cfunction_list = jl_eqtable_put(jl_cfunction_list, ft, insert, NULL); - } - - // compute / validate return type + jl_datatype_t *ft = (jl_datatype_t*)jl_tparam0(sigt); + jl_value_t *ff = ft->instance; + assert(ff); + const char *name = jl_symbol_name(ft->name->mt->name); jl_value_t *crt = declrt; if (jl_is_abstract_ref_type(declrt)) { declrt = jl_tparam0(declrt); - if (jl_is_typevar(declrt)) - jl_error("cfunction: return type Ref should have an element type, not Ref{<:T}"); - if (declrt == (jl_value_t*)jl_any_type) - jl_error("cfunction: return type Ref{Any} is invalid. Use Any or Ptr{Any} instead."); crt = (jl_value_t*)jl_any_type; } bool toboxed; Type *lcrt = _julia_struct_to_llvm(¶ms, crt, NULL, &toboxed); - if (lcrt == NULL) - jl_error("cfunction: return type doesn't correspond to a C type"); - else if (toboxed) + if (toboxed) lcrt = T_prjlvalue; - - // compute / validate method signature - jl_value_t *sigt = NULL; // dispatch sig: type signature (argt) with Ref{} annotations removed and ft added - JL_GC_PUSH1(&sigt); - size_t i, nargs = jl_nparams(argt); - sigt = (jl_value_t*)jl_alloc_svec(nargs + 1); - jl_svecset(sigt, 0, ft); - for (i = 0; i < nargs; i++) { - jl_value_t *ati = jl_tparam(argt, i); - if (jl_is_abstract_ref_type(ati)) { - ati = jl_tparam0(ati); - if (jl_is_typevar(ati)) - jl_error("cfunction: argument type Ref should have an element type, not Ref{<:T}"); - } - if (jl_is_pointer(ati) && jl_is_typevar(jl_tparam0(ati))) - jl_error("cfunction: argument type Ptr should have an element type, Ptr{<:T}"); - jl_svecset(sigt, i + 1, ati); - } - sigt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)sigt); - - // emit cfunction (trampoline) + size_t nargs = jl_nparams(sigt)-1; + jl_svec_t *argtypes = NULL; + JL_GC_PUSH1(&argtypes); + argtypes = jl_alloc_svec(nargs); + for (size_t i = 0; i < nargs; i++) { + jl_svecset(argtypes, i, jl_tparam(sigt, i+1)); + } jl_value_t *err; { // scope block for sig function_sig_t sig("cfunction", lcrt, crt, toboxed, - argt->parameters, NULL, false, CallingConv::C, false, ¶ms); + argtypes, NULL, false, CallingConv::C, false, ¶ms); if (sig.err_msg.empty()) { size_t world = jl_world_counter; size_t min_valid = 0; size_t max_valid = ~(size_t)0; - // try to look up this function for direct invoking - jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0); - Function *F = gen_cfun_wrapper(NULL, params, sig, ff, cache_l3, declrt, lam, NULL, NULL, NULL); + Function *F = NULL; + if (sysimg_handle) { + // restore a ccallable from the system image + void *addr; + int found = jl_dlsym(sysimg_handle, name, &addr, 0); + if (found) { + FunctionType *ftype = sig.functype(); + F = Function::Create(ftype, GlobalVariable::ExternalLinkage, + name, shadow_output); + add_named_global(F, addr); + } + } + else { + jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0); + F = gen_cfun_wrapper((Module*)llvmmod, params, sig, ff, name, declrt, lam, NULL, NULL, NULL); + } JL_GC_POP(); return F; } diff --git a/src/dump.c b/src/dump.c index 822083888c34f..aeb8eff2f007c 100644 --- a/src/dump.c +++ b/src/dump.c @@ -69,6 +69,9 @@ static jl_array_t *serializer_worklist JL_GLOBALLY_ROOTED; // (only used by the incremental serializer in MODE_MODULE) htable_t edges_map; +// list of requested ccallable signatures +static arraylist_t ccallable_list; + #define TAG_SYMBOL 2 #define TAG_SSAVALUE 3 #define TAG_DATATYPE 4 @@ -873,6 +876,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li write_int8(s->s, m->pure); 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->ccallable); jl_serialize_value(s, (jl_value_t*)m->source); jl_serialize_value(s, (jl_value_t*)m->unspecialized); jl_serialize_value(s, (jl_value_t*)m->generator); @@ -1778,6 +1782,11 @@ static jl_value_t *jl_deserialize_value_method(jl_serializer_state *s, jl_value_ m->roots = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&m->roots); if (m->roots) jl_gc_wb(m, m->roots); + m->ccallable = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&m->ccallable); + if (m->ccallable) { + jl_gc_wb(m, m->ccallable); + arraylist_push(&ccallable_list, m->ccallable); + } m->source = jl_deserialize_value(s, &m->source); if (m->source) jl_gc_wb(m, m->source); @@ -3216,6 +3225,7 @@ static jl_value_t *_jl_restore_incremental(ios_t *f, jl_array_t *mod_array) arraylist_new(&backref_list, 4000); arraylist_push(&backref_list, jl_main_module); arraylist_new(&flagref_list, 0); + arraylist_new(&ccallable_list, 0); htable_new(&uniquing_table, 0); jl_serializer_state s = { @@ -3266,6 +3276,12 @@ static jl_value_t *_jl_restore_incremental(ios_t *f, jl_array_t *mod_array) arraylist_free(tracee_list); free(tracee_list); } + for(int i = 0; i < ccallable_list.len; i++) { + jl_svec_t *item = (jl_svec_t*)ccallable_list.items[i]; + JL_GC_PROMISE_ROOTED(item); + jl_compile_extern_c(NULL, NULL, NULL, jl_svecref(item, 0), jl_svecref(item, 1)); + } + arraylist_free(&ccallable_list); jl_value_t *ret = (jl_value_t*)jl_svec(2, restored, init_order); JL_GC_POP(); diff --git a/src/gc.c b/src/gc.c index 5b81785130ed4..d9dd63105fc65 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2670,8 +2670,6 @@ static void mark_roots(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp) gc_mark_queue_obj(gc_cache, sp, jl_current_modules.table[i]); } } - if (jl_cfunction_list != NULL) - gc_mark_queue_obj(gc_cache, sp, jl_cfunction_list); gc_mark_queue_obj(gc_cache, sp, jl_anytuple_type_type); for (size_t i = 0; i < N_CALL_CACHE; i++) if (call_cache[i]) diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 562c5e9b39458..68a9d805cc9c1 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -197,47 +197,82 @@ static jl_callptr_t _jl_compile_codeinst( return fptr; } -// get the address of a C-callable entry point for a function +Function *jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t ¶ms); + +// compile a C-callable alias extern "C" JL_DLLEXPORT -void *jl_function_ptr(jl_function_t *f, jl_value_t *rt, jl_value_t *argt) +void jl_compile_extern_c(void *llvmmod, void *p, void *sysimg, jl_value_t *declrt, jl_value_t *sigt) { - JL_GC_PUSH1(&argt); JL_LOCK(&codegen_lock); jl_codegen_params_t params; - Function *llvmf = jl_cfunction_object(f, rt, (jl_tupletype_t*)argt, params); - jl_jit_globals(params.globals); - assert(params.workqueue.empty()); - jl_add_to_ee(); - JL_GC_POP(); - void *ptr = (void*)getAddressForFunction(llvmf->getName()); + jl_codegen_params_t *pparams = (jl_codegen_params_t*)p; + if (pparams == NULL) + pparams = ¶ms; + jl_generate_ccallable(llvmmod, sysimg, declrt, sigt, *pparams); + if (!sysimg) { + if (p == NULL) { + jl_jit_globals(params.globals); + assert(params.workqueue.empty()); + } + if (llvmmod == NULL) + jl_add_to_ee(); + } JL_UNLOCK(&codegen_lock); - return ptr; } -// export a C-callable entry point for a function (dllexport'ed dlsym), with a given name +bool jl_type_mappable_to_c(jl_value_t *ty); + +// declare a C-callable entry point; called during code loading from the toplevel extern "C" JL_DLLEXPORT -void jl_extern_c(jl_function_t *f, jl_value_t *rt, jl_value_t *argt, char *name) -{ +void jl_extern_c(jl_value_t *declrt, jl_tupletype_t *sigt) +{ + // validate arguments. try to do as many checks as possible here to avoid + // throwing errors later during codegen. + JL_TYPECHK(@ccallable, type, declrt); + if (!jl_is_tuple_type(sigt)) + jl_type_error("@ccallable", (jl_value_t*)jl_anytuple_type_type, (jl_value_t*)sigt); + // check that f is a guaranteed singleton type + jl_datatype_t *ft = (jl_datatype_t*)jl_tparam0(sigt); + if (!jl_is_datatype(ft) || ft->instance == NULL) + jl_error("@ccallable: function object must be a singleton"); + + // compute / validate return type + jl_value_t *crt = declrt; + if (jl_is_abstract_ref_type(declrt)) { + jl_value_t *declrt0 = jl_tparam0(declrt); + if (jl_is_typevar(declrt0)) + jl_error("@ccallable: return type Ref should have an element type, not Ref{<:T}"); + if (declrt0 == (jl_value_t*)jl_any_type) + jl_error("@ccallable: return type Ref{Any} is invalid. Use Any or Ptr{Any} instead."); + crt = (jl_value_t*)jl_any_type; + } JL_LOCK(&codegen_lock); - jl_codegen_params_t params; - Function *llvmf = jl_cfunction_object(f, rt, (jl_tupletype_t*)argt, params); - jl_jit_globals(params.globals); - assert(params.workqueue.empty()); - // force eager emission of the function (llvm 3.3 gets confused otherwise and tries to do recursive compilation) - jl_add_to_ee(); - uint64_t Addr = getAddressForFunction(llvmf->getName()); - - if (imaging_mode) - llvmf = cast(shadow_output->getNamedValue(llvmf->getName())); - - // make the alias to the shadow_module - GlobalAlias *GA = - GlobalAlias::create(llvmf->getType()->getElementType(), llvmf->getType()->getAddressSpace(), - GlobalValue::ExternalLinkage, name, llvmf, shadow_output); - - // make sure the alias name is valid for the current session - jl_ExecutionEngine->addGlobalMapping(GA, (void*)(uintptr_t)Addr); + if (!jl_type_mappable_to_c(crt)) + jl_error("@ccallable: return type doesn't correspond to a C type"); JL_UNLOCK(&codegen_lock); + + // validate method signature + size_t i, nargs = jl_nparams(sigt); + for (i = 1; i < nargs; i++) { + jl_value_t *ati = jl_tparam(sigt, i); + if (jl_is_abstract_ref_type(ati)) { + ati = jl_tparam0(ati); + if (jl_is_typevar(ati)) + jl_error("@ccallable: argument type Ref should have an element type, not Ref{<:T}"); + } + if (jl_is_pointer(ati) && jl_is_typevar(jl_tparam0(ati))) + jl_error("@ccallable: argument type Ptr should have an element type, Ptr{<:T}"); + } + + // save a record of this so that the alias is generated when we write an object file + jl_method_t *meth = (jl_method_t*)jl_methtable_lookup(ft->name->mt, (jl_value_t*)sigt, jl_world_counter); + if (!jl_is_method(meth)) + jl_error("@ccallable: could not find requested method"); + meth->ccallable = jl_svec2(declrt, (jl_value_t*)sigt); + jl_gc_wb(meth, meth->ccallable); + + // create the alias in the current runtime environment + jl_compile_extern_c(NULL, NULL, NULL, declrt, (jl_value_t*)sigt); } // this compiles li and emits fptr diff --git a/src/jltypes.c b/src/jltypes.c index bf38e0320ad81..1f6a1363e8bf1 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2123,7 +2123,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(23, + jl_perm_symsvec(24, "name", "module", "file", @@ -2140,6 +2140,7 @@ void jl_init_types(void) JL_GC_DISABLED "unspecialized", "generator", "roots", + "ccallable", "invokes", "nargs", "called", @@ -2147,7 +2148,7 @@ void jl_init_types(void) JL_GC_DISABLED "nkw", "isva", "pure"), - jl_svec(23, + jl_svec(24, jl_symbol_type, jl_module_type, jl_symbol_type, @@ -2164,6 +2165,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, // jl_method_instance_type jl_any_type, jl_array_any_type, + jl_simplevector_type, jl_any_type, jl_int32_type, jl_int32_type, diff --git a/src/julia.h b/src/julia.h index 772bfd8558b5d..ff8dfd80259ae 100644 --- a/src/julia.h +++ b/src/julia.h @@ -298,6 +298,7 @@ typedef struct _jl_method_t { struct _jl_method_instance_t *unspecialized; // unspecialized executable method instance, or null jl_value_t *generator; // executable code-generating function if available jl_array_t *roots; // pointers in generated code (shared to reduce memory), or null + 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. // cases where this method was called even though it was not necessarily diff --git a/src/julia_internal.h b/src/julia_internal.h index 39c5ad3dbbd86..c2efe2f83ee8a 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -507,7 +507,7 @@ void jl_module_run_initializer(jl_module_t *m); jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) JL_NOTSAFEPOINT; extern jl_array_t *jl_module_init_order JL_GLOBALLY_ROOTED; extern htable_t jl_current_modules JL_GLOBALLY_ROOTED; -extern jl_array_t *jl_cfunction_list JL_GLOBALLY_ROOTED; +JL_DLLEXPORT void jl_compile_extern_c(void *llvmmod, void *params, void *sysimg, jl_value_t *declrt, jl_value_t *sigt); #ifdef JL_USE_INTEL_JITEVENTS extern char jl_using_intel_jitevents; diff --git a/src/method.c b/src/method.c index 1fd2b43e7930b..c4ebc087824fe 100644 --- a/src/method.c +++ b/src/method.c @@ -593,6 +593,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->ambig = jl_nothing; m->resorted = jl_nothing; m->roots = NULL; + m->ccallable = NULL; m->module = module; m->source = NULL; m->unspecialized = NULL; diff --git a/src/precompile.c b/src/precompile.c index c2f2a62f8380e..2134f80c6bf2b 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -362,6 +362,8 @@ static int precompile_enq_all_specializations__(jl_typemap_entry_t *def, void *c precompile_enq_specialization_(mi, closure); } } + if (m->ccallable) + jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)m->ccallable); return 1; } @@ -386,13 +388,21 @@ void *jl_precompile(int all) jl_foreach_reachable_mtable(precompile_enq_all_specializations_, m); m2 = jl_alloc_vec_any(0); for (size_t i = 0; i < jl_array_len(m); i++) { - mi = (jl_method_instance_t*)jl_array_ptr_ref(m, i); - size_t min_world = 0; - size_t max_world = ~(size_t)0; - if (!jl_isa_compileable_sig((jl_tupletype_t*)mi->specTypes, mi->def.method)) - mi = jl_get_specialization1((jl_tupletype_t*)mi->specTypes, jl_world_counter, &min_world, &max_world, 0); - if (mi) - jl_array_ptr_1d_push(m2, (jl_value_t*)mi); + jl_value_t *item = jl_array_ptr_ref(m, i); + if (jl_is_method_instance(item)) { + mi = (jl_method_instance_t*)item; + size_t min_world = 0; + size_t max_world = ~(size_t)0; + if (!jl_isa_compileable_sig((jl_tupletype_t*)mi->specTypes, mi->def.method)) + mi = jl_get_specialization1((jl_tupletype_t*)mi->specTypes, jl_world_counter, &min_world, &max_world, 0); + if (mi) + jl_array_ptr_1d_push(m2, (jl_value_t*)mi); + } + else { + assert(jl_is_simplevector(item)); + assert(jl_svec_len(item) == 2); + jl_array_ptr_1d_push(m2, item); + } } m = NULL; void *native_code = jl_create_native(m2, jl_default_cgparams); diff --git a/src/staticdata.c b/src/staticdata.c index 93d0275bc7161..4e881e432c405 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -783,6 +783,10 @@ static void jl_write_values(jl_serializer_state *s) if (jl_is_method(v)) { write_padding(s->s, sizeof(jl_method_t) - tot); + if (((jl_method_t*)v)->ccallable) { + arraylist_push(&reinit_list, (void*)item); + arraylist_push(&reinit_list, (void*)3); + } } else if (jl_is_code_instance(v)) { jl_code_instance_t *m = (jl_code_instance_t*)v; @@ -1258,6 +1262,11 @@ static void jl_reinit_item(jl_value_t *v, int how) } break; } + case 3: { // install ccallable entry point in JIT + jl_svec_t *sv = ((jl_method_t*)v)->ccallable; + jl_compile_extern_c(NULL, NULL, jl_sysimg_handle, jl_svecref(sv, 0), jl_svecref(sv, 1)); + break; + } default: assert(0 && "corrupt deserialization state"); abort(); @@ -1576,6 +1585,7 @@ static void jl_restore_system_image_from_stream(ios_t *f) s.s = NULL; s.s = f; + jl_init_codegen(); jl_finalize_deserializer(&s); s.s = NULL; @@ -1598,7 +1608,6 @@ static void jl_restore_system_image_from_stream(ios_t *f) } s.s = &sysimg; - jl_init_codegen(); jl_update_all_fptrs(&s); // fptr relocs and registration ios_close(&fptr_record); ios_close(&sysimg); diff --git a/test/precompile.jl b/test/precompile.jl index 0ad001f983adc..bf82c1037f18e 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -170,6 +170,9 @@ try const layout1 = Ptr{Int8}[Ptr{Int8}(0), Ptr{Int8}(1), Ptr{Int8}(-1)] const layout2 = Any[Ptr{Int8}(0), Ptr{Int16}(1), Ptr{Int32}(-1)] const layout3 = collect(x.match for x in eachmatch(r"..", "abcdefghijk"))::Vector{SubString{String}} + + # check that @ccallable works from precompiled modules + Base.@ccallable Cint f35014(x::Cint) = x+Cint(1) end """) # make sure `sin` didn't have a kwfunc (which would invalidate the attempted test) @@ -218,6 +221,17 @@ try @test Foo.layout3 == ["ab", "cd", "ef", "gh", "ij"] end + @eval begin function ccallable_test() + Base.llvmcall( + (""" declare i32 @f35014(i32)""", + """ + %1 = call i32 @f35014(i32 3) + ret i32 %1 + """), Cint, Tuple{}) + end + @test ccallable_test() == 4 + end + cachedir = joinpath(dir, "compiled", "v$(VERSION.major).$(VERSION.minor)") cachedir2 = joinpath(dir2, "compiled", "v$(VERSION.major).$(VERSION.minor)") cachefile = joinpath(cachedir, "$Foo_module.ji")