Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions base/c.jl
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,8 @@ function exit_on_sigint(on::Bool)
ccall(:jl_exit_on_sigint, Cvoid, (Cint,), on)
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)
Expand All @@ -499,17 +499,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
Expand Down
2 changes: 0 additions & 2 deletions src/anticodegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,3 @@ JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION(void)
{
return 0;
}

jl_array_t *jl_cfunction_list;
9 changes: 8 additions & 1 deletion src/aotcompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) && worlds == 1) {
jl_compile_extern_c(shadow_output, &params, 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
Expand Down
7 changes: 7 additions & 0 deletions src/cgutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(&params, ty, NULL, &toboxed) != NULL;
}

static bool is_datatype_all_pointers(jl_datatype_t *dt)
{
size_t i, l = jl_datatype_nfields(dt);
Expand Down
159 changes: 43 additions & 116 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4014,8 +4014,8 @@ static void emit_cfunc_invalidate(

static Function* gen_cfun_wrapper(
Module *into, jl_codegen_params_t &params,
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
Expand All @@ -4027,12 +4027,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);
Expand Down Expand Up @@ -4086,20 +4085,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;
Expand Down Expand Up @@ -4515,6 +4500,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<Module>(M));

Expand Down Expand Up @@ -4628,8 +4617,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) {
Expand Down Expand Up @@ -4689,116 +4678,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 &params)
// 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 &params)
{
// 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(&params, 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, &params);
argtypes, NULL, false, CallingConv::C, false, &params);
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;
}
Expand Down
16 changes: 16 additions & 0 deletions src/dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -3217,6 +3226,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 = {
Expand Down Expand Up @@ -3267,6 +3277,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();

Expand Down
2 changes: 0 additions & 2 deletions src/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2672,8 +2672,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])
Expand Down
Loading