/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/runtime_ccall.c
460 строк
15 KB
Cody Tapscott
codegen: drop the redundant `nargs` field from `jl_abi_t` (#62419)
18 июл 2026, 04:10
Не верифицирован
18 июл 2026, 04:10
c79f61f
Код
Авторство
О чём код?
// This file is a part of Julia. License is MIT: https://julialang.org/license #include <string.h> #include <stdio.h> #include "julia.h" #include "julia_internal.h" #include "processor.h" #include "julia_assert.h" #ifndef _OS_WINDOWS_ #include <sys/mman.h> #if defined(_OS_DARWIN_) && !defined(MAP_ANONYMOUS) #define MAP_ANONYMOUS MAP_ANON #endif #endif #include "support/ios.h" #include "support/strhash.h" // --- library symbol lookup --- jl_value_t *jl_libdl_dlopen_func JL_GLOBALLY_ROOTED; // map from user-specified lib names to handles static htable_t libMap; static jl_mutex_t libmap_lock; void *jl_get_library_(const char *f_lib, int throw_err) JL_CANSAFEPOINT { if (f_lib == NULL) return jl_RTLD_DEFAULT_handle; if (f_lib == JL_EXE_LIBNAME) return jl_exe_handle; if (f_lib == JL_LIBJULIA_INTERNAL_DL_LIBNAME) return jl_libjulia_internal_handle; if (f_lib == JL_LIBJULIA_DL_LIBNAME) return jl_libjulia_handle; JL_LOCK(&libmap_lock); // This is the only operation we do on the map, which doesn't invalidate // any references or iterators. void **map_slot = strhash_bp(&libMap, (void*)f_lib); void *hnd = *map_slot; if (hnd == HT_NOTFOUND) { hnd = jl_load_dynamic_library(f_lib, JL_RTLD_DEFAULT, throw_err); if (hnd != NULL) *map_slot = hnd; } JL_UNLOCK(&libmap_lock); return hnd; } JL_DLLEXPORT void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd) { void *handle = jl_atomic_load_acquire(hnd); if (!handle) jl_atomic_store_release(hnd, (handle = jl_get_library(f_lib))); void * ptr; jl_dlsym(handle, f_name, &ptr, 1, 1); return ptr; } // jl_load_and_lookup, but with library computed at run time on first call JL_DLLEXPORT void *jl_lazy_load_and_lookup(jl_value_t *lib_val, jl_value_t *f_name) { void *lib_ptr; const char *fname_str; if (jl_is_symbol(f_name)) fname_str = jl_symbol_name((jl_sym_t*)f_name); else if (jl_is_string(f_name)) fname_str = jl_string_data(f_name); else jl_type_error("cglobal/ccall function name", (jl_value_t*)jl_symbol_type, f_name); if (lib_val) { if (jl_is_symbol(lib_val)) lib_ptr = jl_get_library(jl_symbol_name((jl_sym_t*)lib_val)); else if (jl_is_string(lib_val)) lib_ptr = jl_get_library(jl_string_data(lib_val)); else if (jl_libdl_dlopen_func != NULL) { lib_ptr = jl_unbox_voidpointer(jl_apply_generic(jl_libdl_dlopen_func, &lib_val, 1)); } else jl_type_error("cglobal/ccall", (jl_value_t*)jl_symbol_type, lib_val); } else { // If the user didn't supply a library name, try to find it now from the runtime value of f_name lib_ptr = jl_get_library(jl_dlfind(fname_str)); } void *ptr; jl_dlsym(lib_ptr, fname_str, &ptr, 1, 1); return ptr; } // miscellany JL_DLLEXPORT jl_value_t *jl_get_JIT(void) JL_CANSAFEPOINT { const char *JITName = "ORCJIT"; return jl_pchar_to_string(JITName, strlen(JITName)); } #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 256 #endif // Form a file name from a pattern made by replacing tokens, // similar to many of those provided by ssh_config TOKENS: // // %% A literal `%'. // %p The process PID // %d Local user's home directory. // %i The local user ID. // %L The local hostname. // %l The local hostname, including the domain name. // %u The local username. JL_DLLEXPORT char *jl_format_filename(const char *output_pattern) JL_NOTSAFEPOINT { ios_t buf; ios_mem(&buf, 128); int special = 0; char hostname[MAXHOSTNAMELEN + 1]; uv_passwd_t pwd; int got_pwd = 0; for (const char *p = output_pattern; *p; p++) { char c = *p; if (special) { if (!got_pwd && (c == 'i' || c == 'd' || c == 'u')) { int r = uv_os_get_passwd(&pwd); if (r == 0) got_pwd = 1; } switch (c) { case 'p': ios_printf(&buf, "%d", (int)uv_os_getpid()); break; case 'd': if (got_pwd) ios_puts(pwd.homedir, &buf); break; case 'i': if (got_pwd) ios_printf(&buf, "%ld", (long)pwd.uid); break; case 'l': case 'L': if (gethostname(hostname, sizeof(hostname)) == 0) { hostname[sizeof(hostname) - 1] = '\0'; /* Null terminate, just to be safe. */ ios_puts(hostname, &buf); } #ifndef _OS_WINDOWS_ if (c == 'l' && getdomainname(hostname, sizeof(hostname)) == 0) { hostname[sizeof(hostname) - 1] = '\0'; /* Null terminate, just to be safe. */ ios_puts(hostname, &buf); } #endif break; case 'u': if (got_pwd) ios_puts(pwd.username, &buf); break; default: ios_putc(c, &buf); break; } special = 0; } else if (c == '%') { special = 1; } else { ios_putc(c, &buf); } } if (got_pwd) uv_os_free_passwd(&pwd); size_t len; // ios_take_buffer nul-terminates and transfers ownership to caller return ios_take_buffer(&buf, &len); } static uv_mutex_t trampoline_lock; // for accesses to the cache and freelist static void *trampoline_freelist; static void *trampoline_alloc(void) JL_NOTSAFEPOINT // lock taken by caller { const int sz = 64; // oversized for most platforms. todo: use precise value? if (!trampoline_freelist) { int last_errno = errno; #ifdef _OS_WINDOWS_ DWORD last_error = GetLastError(); void *mem = VirtualAlloc(NULL, jl_page_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (mem == NULL) jl_throw(jl_memory_exception); SetLastError(last_error); #else void *mem = mmap(0, jl_page_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); errno = last_errno; if (mem == MAP_FAILED) jl_throw(jl_memory_exception); #endif errno = last_errno; void *next = NULL; assert(sz < jl_page_size); for (size_t i = 0; i + sz <= jl_page_size; i += sz) { void **curr = (void**)((char*)mem + i); *curr = next; next = (void*)curr; } trampoline_freelist = next; } void *tramp = trampoline_freelist; trampoline_freelist = *(void**)tramp; return tramp; } static void trampoline_free(void *tramp) JL_NOTSAFEPOINT // lock taken by caller { *(void**)tramp = trampoline_freelist; trampoline_freelist = tramp; } static void trampoline_deleter(void **f) JL_NOTSAFEPOINT { void *tramp = f[0]; void *fobj = f[1]; void *cache = f[2]; void *nval = f[3]; f[0] = NULL; f[2] = NULL; f[3] = NULL; uv_mutex_lock(&trampoline_lock); if (tramp) trampoline_free(tramp); if (fobj && cache) ptrhash_remove((htable_t*)cache, fobj); if (nval) free(nval); uv_mutex_unlock(&trampoline_lock); } typedef void *(*init_trampoline_t)(void *tramp, void **nval) JL_NOTSAFEPOINT; // Use of `cache` is not clobbered in JL_TRY JL_GCC_IGNORE_START("-Wclobbered") JL_DLLEXPORT jl_value_t *jl_get_cfunction_trampoline( // dynamic inputs: jl_value_t *fobj, jl_datatype_t *result_type, // call-site constants: htable_t *cache, // weakref htable indexed by (fobj, vals) jl_svec_t *fill, init_trampoline_t init_trampoline, jl_unionall_t *env, jl_value_t **vals) { // lookup (fobj, vals) in cache uv_mutex_lock(&trampoline_lock); if (!cache->table) htable_new(cache, 1); if (fill != jl_emptysvec) { htable_t **cache2 = (htable_t**)ptrhash_bp(cache, (void*)vals); cache = *cache2; if ((void*)cache == HT_NOTFOUND) { cache = htable_new((htable_t*)malloc_s(sizeof(htable_t)), 1); *cache2 = cache; } } void *tramp = ptrhash_get(cache, (void*)fobj); uv_mutex_unlock(&trampoline_lock); if (tramp != HT_NOTFOUND) { assert((jl_datatype_t*)jl_typeof(tramp) == result_type); return (jl_value_t*)tramp; } // not found, allocate a new one size_t n = jl_svec_len(fill); void **nval = (void**)malloc_s(sizeof(void*) * (n + 1)); nval[0] = (void*)fobj; jl_value_t *result; JL_TRY { for (size_t i = 0; i < n; i++) { jl_value_t *sparam_val = jl_instantiate_type_in_env(jl_svecref(fill, i), env, vals); if (sparam_val != (jl_value_t*)jl_any_type) if (!jl_is_concrete_type(sparam_val) || !jl_is_immutable(sparam_val)) sparam_val = NULL; nval[i + 1] = (void*)sparam_val; } int permanent = (result_type == jl_voidpointer_type) || jl_is_concrete_type(fobj) || (((jl_datatype_t*)jl_typeof(fobj))->instance == fobj); if (jl_is_unionall(fobj)) { jl_value_t *uw = jl_unwrap_unionall(fobj); if (jl_is_datatype(uw) && ((jl_datatype_t*)uw)->name->wrapper == fobj) permanent = 1; } if (permanent) { jl_task_t *ct = jl_current_task; result = jl_gc_permobj(ct->ptls, sizeof(jl_taggedvalue_t) + jl_datatype_size(result_type), result_type, 0); memset(result, 0, jl_datatype_size(result_type)); } else { result = jl_new_struct_uninit(result_type); } if (result_type != jl_voidpointer_type) { assert(jl_datatype_size(result_type) == sizeof(void*) * 4); ((void**)result)[1] = (void*)fobj; } if (!permanent) { jl_task_t *ct = jl_current_task; jl_gc_add_ptr_finalizer(ct->ptls, result, (void*)(uintptr_t)&trampoline_deleter); ((void**)result)[2] = (void*)cache; ((void**)result)[3] = (void*)nval; } } JL_CATCH { free(nval); jl_rethrow(); } uv_mutex_lock(&trampoline_lock); tramp = trampoline_alloc(); ((void**)result)[0] = tramp; init_trampoline(tramp, nval); ptrhash_put(cache, (void*)fobj, result); uv_mutex_unlock(&trampoline_lock); return result; } JL_GCC_IGNORE_STOP struct cfuncdata_t { _Atomic(void *) fptr; _Atomic(size_t) last_world; jl_code_instance_t** plast_codeinst; jl_code_instance_t* last_codeinst; void *unspecialized; jl_value_t *const *const declrt; jl_value_t *const *const sigt; size_t flags; }; static inline const char *name_from_method_instance(jl_method_instance_t *mi) JL_NOTSAFEPOINT { assert(jl_is_method_instance(mi)); return jl_is_method(mi->def.method) ? jl_symbol_name(mi->def.method->name) : "top-level scope"; } static jl_mutex_t cfun_lock; // (get_abi_converter / method table mutating thread) // release jl_world_counter // release theFptr // release last_world_v // // (dispatch site) // acquire last_world_v // acquire theFptr // read jl_world_counter // // The above ordering requirements are intended to guarantee that if the // dispatch site observes last_world == jl_world_counter then the loaded // fptr is consistent with both of them, meaning it was published for // exactly that world. JL_DLLEXPORT void *jl_get_abi_converter(jl_task_t *ct, void *data) { struct cfuncdata_t *cfuncdata = (struct cfuncdata_t*)data; jl_value_t *sigt = *cfuncdata->sigt; JL_GC_PROMISE_ROOTED(sigt); jl_value_t *declrt = *cfuncdata->declrt; JL_GC_PROMISE_ROOTED(declrt); int specsig = cfuncdata->flags & 1; jl_value_t *mi; jl_code_instance_t *codeinst; size_t world; // check first, while behind this lock, of the validity of the current contents of this cfunc thunk JL_LOCK(&cfun_lock); do { size_t last_world_v = jl_atomic_load_relaxed(&cfuncdata->last_world); void *f = jl_atomic_load_relaxed(&cfuncdata->fptr); jl_code_instance_t *last_ci = cfuncdata->plast_codeinst ? *cfuncdata->plast_codeinst : NULL; JL_GC_PROMISE_ROOTED(last_ci); // cached CI is retained by the MI cache or by an image literal root slot world = jl_atomic_load_acquire(&jl_world_counter); ct->world_age = world; if (world == last_world_v) { JL_UNLOCK(&cfun_lock); return f; } mi = jl_get_specialization1((jl_tupletype_t*)sigt, world); if (f != NULL) { if (last_ci == NULL) { if (mi == jl_nothing) { jl_atomic_store_release(&cfuncdata->last_world, world); JL_UNLOCK(&cfun_lock); return f; } } else { if ((jl_value_t*)jl_get_ci_mi(last_ci) == mi && jl_atomic_load_relaxed(&last_ci->max_world) >= world) { // same dispatch and source jl_atomic_store_release(&cfuncdata->last_world, world); JL_UNLOCK(&cfun_lock); return f; } } } JL_UNLOCK(&cfun_lock); // next, try to figure out what the target should look like (outside of the lock since this is very slow) codeinst = mi != jl_nothing ? jl_type_infer((jl_method_instance_t*)mi, world, SOURCE_MODE_ABI, jl_options.trim) : NULL; // relock for the remainder of the function JL_LOCK(&cfun_lock); } while (jl_atomic_load_acquire(&jl_world_counter) != world); // restart entirely, since jl_world_counter changed thus jl_get_specialization1 might have changed // double-check if the values were set on another thread size_t last_world_v = jl_atomic_load_relaxed(&cfuncdata->last_world); void *f = jl_atomic_load_relaxed(&cfuncdata->fptr); if (world == last_world_v) { JL_UNLOCK(&cfun_lock); return f; // another thread fixed this up while we were away } int is_opaque_closure = 0; jl_abi_t from_abi = { sigt, declrt, specsig, is_opaque_closure }; if (codeinst == NULL) { // Generate an adapter to a dynamic dispatch if (cfuncdata->unspecialized == NULL) cfuncdata->unspecialized = jl_jit_abi_converter(ct, from_abi, NULL); f = cfuncdata->unspecialized; } else { jl_value_t *astrt = codeinst->rettype; if (astrt != (jl_value_t*)jl_bottom_type && jl_type_intersection(astrt, declrt) == jl_bottom_type) { // Do not warn if the function never returns since it is // occasionally required by the C API (typically error callbacks) // even though we're likely to encounter memory errors in that case jl_printf(JL_STDERR, "WARNING: cfunction: return type of %s does not match\n", name_from_method_instance((jl_method_instance_t*)mi)); } f = jl_jit_abi_converter(ct, from_abi, codeinst); } cfuncdata->plast_codeinst = &cfuncdata->last_codeinst; cfuncdata->last_codeinst = codeinst; jl_atomic_store_release(&cfuncdata->fptr, f); jl_atomic_store_release(&cfuncdata->last_world, world); JL_UNLOCK(&cfun_lock); return f; } void jl_init_runtime_ccall(void) { JL_MUTEX_INIT(&libmap_lock, "libmap_lock"); strhash_new(&libMap, 16); uv_mutex_init(&trampoline_lock); }