Skip to content

Commit 7ba7daf

Browse files
authored
make ccallable work with system images and precompilation (#35574)
fixes #35014
1 parent 84d7e67 commit 7ba7daf

File tree

15 files changed

+203
-172
lines changed

15 files changed

+203
-172
lines changed

base/c.jl

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -482,8 +482,8 @@ function exit_on_sigint(on::Bool)
482482
ccall(:jl_exit_on_sigint, Cvoid, (Cint,), on)
483483
end
484484

485-
function ccallable(f::Function, rt::Type, argt::Type, name::Union{AbstractString,Symbol}=string(f))
486-
ccall(:jl_extern_c, Cvoid, (Any, Any, Any, Cstring), f, rt, argt, name)
485+
function _ccallable(rt::Type, sigt::Type)
486+
ccall(:jl_extern_c, Cvoid, (Any, Any), rt, sigt)
487487
end
488488

489489
function expand_ccallable(rt, def)
@@ -499,17 +499,22 @@ function expand_ccallable(rt, def)
499499
error("@ccallable requires a return type")
500500
end
501501
if sig.head === :call
502-
name = sig.args[1]
502+
f = sig.args[1]
503+
if isa(f,Expr) && f.head === :(::)
504+
f = f.args[end]
505+
else
506+
f = :(typeof($f))
507+
end
503508
at = map(sig.args[2:end]) do a
504509
if isa(a,Expr) && a.head === :(::)
505-
a.args[2]
510+
a.args[end]
506511
else
507512
:Any
508513
end
509514
end
510515
return quote
511516
$(esc(def))
512-
ccallable($(esc(name)), $(esc(rt)), $(Expr(:curly, :Tuple, map(esc, at)...)), $(string(name)))
517+
_ccallable($(esc(rt)), $(Expr(:curly, :Tuple, esc(f), map(esc, at)...)))
513518
end
514519
end
515520
end

src/anticodegen.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,3 @@ JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION(void)
6666
{
6767
return 0;
6868
}
69-
70-
jl_array_t *jl_cfunction_list;

src/aotcompile.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,14 @@ void *jl_create_native(jl_array_t *methods, const jl_cgparams_t cgparams)
271271
continue;
272272
size_t i, l;
273273
for (i = 0, l = jl_array_len(methods); i < l; i++) {
274-
mi = (jl_method_instance_t*)jl_array_ptr_ref(methods, i);
274+
// each item in this list is either a MethodInstance indicating something
275+
// to compile, or an svec(rettype, sig) describing a C-callable alias to create.
276+
jl_value_t *item = jl_array_ptr_ref(methods, i);
277+
if (jl_is_simplevector(item) && worlds == 1) {
278+
jl_compile_extern_c(shadow_output, &params, NULL, jl_svecref(item, 0), jl_svecref(item, 1));
279+
continue;
280+
}
281+
mi = (jl_method_instance_t*)item;
275282
src = NULL;
276283
// if this method is generally visible to the current compilation world,
277284
// and this is either the primary world, or not applicable in the primary world

src/cgutils.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,13 @@ static Type *julia_struct_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, jl_unionall
774774
return _julia_struct_to_llvm(&ctx.emission_context, jt, ua, isboxed);
775775
}
776776

777+
bool jl_type_mappable_to_c(jl_value_t *ty)
778+
{
779+
jl_codegen_params_t params;
780+
bool toboxed;
781+
return _julia_struct_to_llvm(&params, ty, NULL, &toboxed) != NULL;
782+
}
783+
777784
static bool is_datatype_all_pointers(jl_datatype_t *dt)
778785
{
779786
size_t i, l = jl_datatype_nfields(dt);

src/codegen.cpp

Lines changed: 43 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -4014,8 +4014,8 @@ static void emit_cfunc_invalidate(
40144014

40154015
static Function* gen_cfun_wrapper(
40164016
Module *into, jl_codegen_params_t &params,
4017-
const function_sig_t &sig, jl_value_t *ff,
4018-
jl_typemap_entry_t *sf, jl_value_t *declrt, jl_method_instance_t *lam,
4017+
const function_sig_t &sig, jl_value_t *ff, const char *aliasname,
4018+
jl_value_t *declrt, jl_method_instance_t *lam,
40194019
jl_unionall_t *unionall_env, jl_svec_t *sparam_vals, jl_array_t **closure_types)
40204020
{
40214021
// Generate a c-callable wrapper
@@ -4027,12 +4027,11 @@ static Function* gen_cfun_wrapper(
40274027
jl_value_t *astrt = (jl_value_t*)jl_any_type;
40284028
void *callptr = NULL;
40294029
int calltype = 0;
4030-
// infer it first, if necessary
4031-
// FIXME! pretend this is OK
4032-
if (lam)
4030+
if (aliasname)
4031+
name = aliasname;
4032+
else if (lam)
40334033
name = jl_symbol_name(lam->def.method->name);
40344034
if (lam && params.cache) {
4035-
// if (!into)
40364035
// TODO: this isn't ideal to be unconditionally calling type inference (and compile) from here
40374036
codeinst = jl_compile_method_internal(lam, world);
40384037
assert(codeinst->invoke);
@@ -4086,20 +4085,6 @@ static Function* gen_cfun_wrapper(
40864085
cw->setAttributes(attributes);
40874086
jl_init_function(cw);
40884087
Function *cw_proto = into ? cw : function_proto(cw);
4089-
// Save the Function object reference
4090-
if (sf) {
4091-
jl_value_t *oldsf = sf->func.value;
4092-
size_t i, oldlen = jl_svec_len(oldsf);
4093-
jl_value_t *newsf = (jl_value_t*)jl_alloc_svec(oldlen + 2);
4094-
JL_GC_PUSH1(&newsf);
4095-
jl_svecset(newsf, 0, sig.rt);
4096-
jl_svecset(newsf, 1, jl_box_voidpointer((void*)cw_proto));
4097-
for (i = 0; i < oldlen; i++)
4098-
jl_svecset(newsf, i + 2, jl_svecref(oldsf, i));
4099-
sf->func.value = newsf;
4100-
jl_gc_wb(sf, sf->func.value);
4101-
JL_GC_POP();
4102-
}
41034088

41044089
jl_codectx_t ctx(jl_LLVMContext, params);
41054090
ctx.f = cw;
@@ -4515,6 +4500,10 @@ static Function* gen_cfun_wrapper(
45154500
cw_proto = into ? cw_make : function_proto(cw_make);
45164501
}
45174502

4503+
if (aliasname) {
4504+
GlobalAlias::create(cw->getType()->getElementType(), cw->getType()->getAddressSpace(),
4505+
GlobalValue::ExternalLinkage, aliasname, cw, M);
4506+
}
45184507
if (!into)
45194508
jl_finalize_module(std::unique_ptr<Module>(M));
45204509

@@ -4628,8 +4617,8 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con
46284617
jl_method_instance_t *lam = sigt ? jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0) : NULL;
46294618
Value *F = gen_cfun_wrapper(
46304619
jl_Module, ctx.emission_context,
4631-
sig, fexpr_rt.constant,
4632-
NULL, declrt, lam,
4620+
sig, fexpr_rt.constant, NULL,
4621+
declrt, lam,
46334622
unionall_env, sparam_vals, &closure_types);
46344623
bool outboxed;
46354624
if (nest) {
@@ -4689,116 +4678,54 @@ static jl_cgval_t emit_cfunction(jl_codectx_t &ctx, jl_value_t *output_type, con
46894678
return mark_julia_type(ctx, F, outboxed, output_type);
46904679
}
46914680

4692-
const struct jl_typemap_info cfunction_cache = {
4693-
1, (jl_datatype_t**)&jl_array_any_type
4694-
};
4695-
4696-
jl_array_t *jl_cfunction_list;
4697-
4698-
Function *jl_cfunction_object(jl_function_t *ff, jl_value_t *declrt, jl_tupletype_t *argt,
4699-
jl_codegen_params_t &params)
4681+
// do codegen to create a C-callable alias/wrapper, or if sysimg_handle is set,
4682+
// restore one from a loaded system image.
4683+
Function *jl_generate_ccallable(void *llvmmod, void *sysimg_handle, jl_value_t *declrt, jl_value_t *sigt, jl_codegen_params_t &params)
47004684
{
4701-
// Assumes the codegen lock is acquired. The caller is responsible for that.
4702-
jl_ptls_t ptls = jl_get_ptls_states();
4703-
if (ptls->in_pure_callback)
4704-
jl_error("cfunction cannot be used in a generated function");
4705-
4706-
// validate and unpack the arguments
4707-
JL_TYPECHK(cfunction, type, declrt);
4708-
if (!jl_is_tuple_type(argt)) // the C API requires that argt Tuple type actually be an svec
4709-
jl_type_error("cfunction", (jl_value_t*)jl_anytuple_type_type, (jl_value_t*)argt);
4710-
// trampolines are not supported here:
4711-
// check that f is a guaranteed singleton type
4712-
jl_value_t *ft = jl_typeof(ff);
4713-
if (((jl_datatype_t*)ft)->instance != ff)
4714-
jl_error("cfunction: use `@cfunction` to make closures");
4715-
4716-
// check the cache structure
4717-
// this has three levels (for the 3 parameters above)
4718-
// first split on `ft` using a simple eqtable
4719-
// then use the typemap to split on argt
4720-
// and finally, pick declrt from the pair-list
4721-
jl_typemap_t *cache_l2 = NULL;
4722-
jl_typemap_entry_t *cache_l3 = NULL;
4723-
if (!jl_cfunction_list) {
4724-
jl_cfunction_list = jl_alloc_vec_any(16);
4725-
}
4726-
else {
4727-
cache_l2 = jl_eqtable_get(jl_cfunction_list, ft, NULL);
4728-
if (cache_l2) {
4729-
struct jl_typemap_assoc search = {(jl_value_t*)argt, 1, NULL, 0, ~(size_t)0};
4730-
cache_l3 = jl_typemap_assoc_by_type(cache_l2, &search, /*offs*/0, /*subtype*/0);
4731-
if (cache_l3) {
4732-
jl_svec_t *sf = (jl_svec_t*)cache_l3->func.value;
4733-
size_t i, l = jl_svec_len(sf);
4734-
for (i = 0; i < l; i += 2) {
4735-
jl_value_t *ti = jl_svecref(sf, i);
4736-
if (jl_egal(ti, declrt)) {
4737-
return (Function*)jl_unbox_voidpointer(jl_svecref(sf, i + 1));
4738-
}
4739-
}
4740-
}
4741-
}
4742-
}
4743-
4744-
if (cache_l3 == NULL) {
4745-
jl_typemap_t *insert = cache_l2;
4746-
if (!insert)
4747-
insert = jl_nothing;
4748-
cache_l3 = jl_typemap_insert(&insert, (jl_value_t*)insert, (jl_tupletype_t*)argt,
4749-
NULL, jl_emptysvec, (jl_value_t*)jl_emptysvec, /*offs*/0, &cfunction_cache, 1, ~(size_t)0);
4750-
if (insert != cache_l2)
4751-
jl_cfunction_list = jl_eqtable_put(jl_cfunction_list, ft, insert, NULL);
4752-
}
4753-
4754-
// compute / validate return type
4685+
jl_datatype_t *ft = (jl_datatype_t*)jl_tparam0(sigt);
4686+
jl_value_t *ff = ft->instance;
4687+
assert(ff);
4688+
const char *name = jl_symbol_name(ft->name->mt->name);
47554689
jl_value_t *crt = declrt;
47564690
if (jl_is_abstract_ref_type(declrt)) {
47574691
declrt = jl_tparam0(declrt);
4758-
if (jl_is_typevar(declrt))
4759-
jl_error("cfunction: return type Ref should have an element type, not Ref{<:T}");
4760-
if (declrt == (jl_value_t*)jl_any_type)
4761-
jl_error("cfunction: return type Ref{Any} is invalid. Use Any or Ptr{Any} instead.");
47624692
crt = (jl_value_t*)jl_any_type;
47634693
}
47644694
bool toboxed;
47654695
Type *lcrt = _julia_struct_to_llvm(&params, crt, NULL, &toboxed);
4766-
if (lcrt == NULL)
4767-
jl_error("cfunction: return type doesn't correspond to a C type");
4768-
else if (toboxed)
4696+
if (toboxed)
47694697
lcrt = T_prjlvalue;
4770-
4771-
// compute / validate method signature
4772-
jl_value_t *sigt = NULL; // dispatch sig: type signature (argt) with Ref{} annotations removed and ft added
4773-
JL_GC_PUSH1(&sigt);
4774-
size_t i, nargs = jl_nparams(argt);
4775-
sigt = (jl_value_t*)jl_alloc_svec(nargs + 1);
4776-
jl_svecset(sigt, 0, ft);
4777-
for (i = 0; i < nargs; i++) {
4778-
jl_value_t *ati = jl_tparam(argt, i);
4779-
if (jl_is_abstract_ref_type(ati)) {
4780-
ati = jl_tparam0(ati);
4781-
if (jl_is_typevar(ati))
4782-
jl_error("cfunction: argument type Ref should have an element type, not Ref{<:T}");
4783-
}
4784-
if (jl_is_pointer(ati) && jl_is_typevar(jl_tparam0(ati)))
4785-
jl_error("cfunction: argument type Ptr should have an element type, Ptr{<:T}");
4786-
jl_svecset(sigt, i + 1, ati);
4787-
}
4788-
sigt = (jl_value_t*)jl_apply_tuple_type((jl_svec_t*)sigt);
4789-
4790-
// emit cfunction (trampoline)
4698+
size_t nargs = jl_nparams(sigt)-1;
4699+
jl_svec_t *argtypes = NULL;
4700+
JL_GC_PUSH1(&argtypes);
4701+
argtypes = jl_alloc_svec(nargs);
4702+
for (size_t i = 0; i < nargs; i++) {
4703+
jl_svecset(argtypes, i, jl_tparam(sigt, i+1));
4704+
}
47914705
jl_value_t *err;
47924706
{ // scope block for sig
47934707
function_sig_t sig("cfunction", lcrt, crt, toboxed,
4794-
argt->parameters, NULL, false, CallingConv::C, false, &params);
4708+
argtypes, NULL, false, CallingConv::C, false, &params);
47954709
if (sig.err_msg.empty()) {
47964710
size_t world = jl_world_counter;
47974711
size_t min_valid = 0;
47984712
size_t max_valid = ~(size_t)0;
4799-
// try to look up this function for direct invoking
4800-
jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0);
4801-
Function *F = gen_cfun_wrapper(NULL, params, sig, ff, cache_l3, declrt, lam, NULL, NULL, NULL);
4713+
Function *F = NULL;
4714+
if (sysimg_handle) {
4715+
// restore a ccallable from the system image
4716+
void *addr;
4717+
int found = jl_dlsym(sysimg_handle, name, &addr, 0);
4718+
if (found) {
4719+
FunctionType *ftype = sig.functype();
4720+
F = Function::Create(ftype, GlobalVariable::ExternalLinkage,
4721+
name, shadow_output);
4722+
add_named_global(F, addr);
4723+
}
4724+
}
4725+
else {
4726+
jl_method_instance_t *lam = jl_get_specialization1((jl_tupletype_t*)sigt, world, &min_valid, &max_valid, 0);
4727+
F = gen_cfun_wrapper((Module*)llvmmod, params, sig, ff, name, declrt, lam, NULL, NULL, NULL);
4728+
}
48024729
JL_GC_POP();
48034730
return F;
48044731
}

src/dump.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ static jl_array_t *serializer_worklist JL_GLOBALLY_ROOTED;
6969
// (only used by the incremental serializer in MODE_MODULE)
7070
htable_t edges_map;
7171

72+
// list of requested ccallable signatures
73+
static arraylist_t ccallable_list;
74+
7275
#define TAG_SYMBOL 2
7376
#define TAG_SSAVALUE 3
7477
#define TAG_DATATYPE 4
@@ -873,6 +876,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li
873876
write_int8(s->s, m->pure);
874877
jl_serialize_value(s, (jl_value_t*)m->slot_syms);
875878
jl_serialize_value(s, (jl_value_t*)m->roots);
879+
jl_serialize_value(s, (jl_value_t*)m->ccallable);
876880
jl_serialize_value(s, (jl_value_t*)m->source);
877881
jl_serialize_value(s, (jl_value_t*)m->unspecialized);
878882
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_
17781782
m->roots = (jl_array_t*)jl_deserialize_value(s, (jl_value_t**)&m->roots);
17791783
if (m->roots)
17801784
jl_gc_wb(m, m->roots);
1785+
m->ccallable = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&m->ccallable);
1786+
if (m->ccallable) {
1787+
jl_gc_wb(m, m->ccallable);
1788+
arraylist_push(&ccallable_list, m->ccallable);
1789+
}
17811790
m->source = jl_deserialize_value(s, &m->source);
17821791
if (m->source)
17831792
jl_gc_wb(m, m->source);
@@ -3217,6 +3226,7 @@ static jl_value_t *_jl_restore_incremental(ios_t *f, jl_array_t *mod_array)
32173226
arraylist_new(&backref_list, 4000);
32183227
arraylist_push(&backref_list, jl_main_module);
32193228
arraylist_new(&flagref_list, 0);
3229+
arraylist_new(&ccallable_list, 0);
32203230
htable_new(&uniquing_table, 0);
32213231

32223232
jl_serializer_state s = {
@@ -3267,6 +3277,12 @@ static jl_value_t *_jl_restore_incremental(ios_t *f, jl_array_t *mod_array)
32673277
arraylist_free(tracee_list);
32683278
free(tracee_list);
32693279
}
3280+
for (int i = 0; i < ccallable_list.len; i++) {
3281+
jl_svec_t *item = (jl_svec_t*)ccallable_list.items[i];
3282+
JL_GC_PROMISE_ROOTED(item);
3283+
jl_compile_extern_c(NULL, NULL, NULL, jl_svecref(item, 0), jl_svecref(item, 1));
3284+
}
3285+
arraylist_free(&ccallable_list);
32703286
jl_value_t *ret = (jl_value_t*)jl_svec(2, restored, init_order);
32713287
JL_GC_POP();
32723288

src/gc.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,8 +2672,6 @@ static void mark_roots(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp)
26722672
gc_mark_queue_obj(gc_cache, sp, jl_current_modules.table[i]);
26732673
}
26742674
}
2675-
if (jl_cfunction_list != NULL)
2676-
gc_mark_queue_obj(gc_cache, sp, jl_cfunction_list);
26772675
gc_mark_queue_obj(gc_cache, sp, jl_anytuple_type_type);
26782676
for (size_t i = 0; i < N_CALL_CACHE; i++)
26792677
if (call_cache[i])

0 commit comments

Comments
 (0)