Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include "llvm-version.h"
4 : : #include <map>
5 : : #include <string>
6 : : #include <llvm/ADT/StringMap.h>
7 : : #include <llvm/Support/Host.h>
8 : : #include <llvm/Support/raw_ostream.h>
9 : :
10 : : #include "julia.h"
11 : : #include "julia_internal.h"
12 : : #include "processor.h"
13 : : #include "julia_assert.h"
14 : :
15 : : #ifndef _OS_WINDOWS_
16 : : #include <sys/mman.h>
17 : : #if defined(_OS_DARWIN_) && !defined(MAP_ANONYMOUS)
18 : : #define MAP_ANONYMOUS MAP_ANON
19 : : #endif
20 : : #endif
21 : :
22 : : using namespace llvm;
23 : :
24 : : // --- library symbol lookup ---
25 : :
26 : : // map from user-specified lib names to handles
27 : : static std::map<std::string, void*> libMap;
28 : : static jl_mutex_t libmap_lock;
29 : : extern "C"
30 : 293253 : void *jl_get_library_(const char *f_lib, int throw_err)
31 : : {
32 [ + + ]: 293253 : if (f_lib == NULL)
33 : 236553 : return jl_RTLD_DEFAULT_handle;
34 : : #ifdef _OS_WINDOWS_
35 : : if (f_lib == JL_EXE_LIBNAME)
36 : : return jl_exe_handle;
37 : : if (f_lib == JL_LIBJULIA_INTERNAL_DL_LIBNAME)
38 : : return jl_libjulia_internal_handle;
39 : : if (f_lib == JL_LIBJULIA_DL_LIBNAME)
40 : : return jl_libjulia_handle;
41 : : #endif
42 : 56700 : JL_LOCK(&libmap_lock);
43 : : // This is the only operation we do on the map, which doesn't invalidate
44 : : // any references or iterators.
45 : 56700 : void **map_slot = &libMap[f_lib];
46 : 56700 : void *hnd = *map_slot;
47 [ + + ]: 56700 : if (hnd == NULL) {
48 : 22925 : hnd = jl_load_dynamic_library(f_lib, JL_RTLD_DEFAULT, throw_err);
49 [ + + ]: 2924 : if (hnd != NULL)
50 : 2923 : *map_slot = hnd;
51 : : }
52 : 36699 : JL_UNLOCK(&libmap_lock);
53 : 36699 : return hnd;
54 : : }
55 : :
56 : : extern "C" JL_DLLEXPORT
57 : 108242 : void *jl_load_and_lookup(const char *f_lib, const char *f_name, _Atomic(void*) *hnd)
58 : : {
59 : 108242 : void *handle = jl_atomic_load_acquire(hnd);
60 [ + + ]: 108242 : if (!handle)
61 : 22871 : jl_atomic_store_release(hnd, (handle = jl_get_library(f_lib)));
62 : : void * ptr;
63 : 88241 : jl_dlsym(handle, f_name, &ptr, 1);
64 : 88241 : return ptr;
65 : : }
66 : :
67 : : // jl_load_and_lookup, but with library computed at run time on first call
68 : : extern "C" JL_DLLEXPORT
69 : 0 : void *jl_lazy_load_and_lookup(jl_value_t *lib_val, const char *f_name)
70 : : {
71 : : char *f_lib;
72 : :
73 [ # # ]: 0 : if (jl_is_symbol(lib_val))
74 : 0 : f_lib = jl_symbol_name((jl_sym_t*)lib_val);
75 [ # # ]: 0 : else if (jl_is_string(lib_val))
76 : 0 : f_lib = jl_string_data(lib_val);
77 : : else
78 : 0 : jl_type_error("ccall", (jl_value_t*)jl_symbol_type, lib_val);
79 : : void *ptr;
80 : 0 : jl_dlsym(jl_get_library(f_lib), f_name, &ptr, 1);
81 : 0 : return ptr;
82 : : }
83 : :
84 : : // miscellany
85 : 0 : std::string jl_get_cpu_name_llvm(void)
86 : : {
87 : 0 : return llvm::sys::getHostCPUName().str();
88 : : }
89 : :
90 : 0 : std::string jl_get_cpu_features_llvm(void)
91 : : {
92 : 0 : StringMap<bool> HostFeatures;
93 : 0 : llvm::sys::getHostCPUFeatures(HostFeatures);
94 : 0 : std::string attr;
95 [ # # ]: 0 : for (auto &ele: HostFeatures) {
96 [ # # ]: 0 : if (ele.getValue()) {
97 [ # # ]: 0 : if (!attr.empty()) {
98 : 0 : attr.append(",+");
99 : : }
100 : : else {
101 : 0 : attr.append("+");
102 : : }
103 : 0 : attr.append(ele.getKey().str());
104 : : }
105 : : }
106 : : // Explicitly disabled features need to be added at the end so that
107 : : // they are not re-enabled by other features that implies them by default.
108 [ # # ]: 0 : for (auto &ele: HostFeatures) {
109 [ # # ]: 0 : if (!ele.getValue()) {
110 [ # # ]: 0 : if (!attr.empty()) {
111 : 0 : attr.append(",-");
112 : : }
113 : : else {
114 : 0 : attr.append("-");
115 : : }
116 : 0 : attr.append(ele.getKey().str());
117 : : }
118 : : }
119 : 0 : return attr;
120 : : }
121 : :
122 : : extern "C" JL_DLLEXPORT
123 : 563 : jl_value_t *jl_get_JIT(void)
124 : : {
125 : 1126 : const std::string& HostJITName = "ORCJIT";
126 : 563 : return jl_pchar_to_string(HostJITName.data(), HostJITName.size());
127 : : }
128 : :
129 : : #ifndef MAXHOSTNAMELEN
130 : : # define MAXHOSTNAMELEN 256
131 : : #endif
132 : :
133 : : // Form a file name from a pattern made by replacing tokens,
134 : : // similar to many of those provided by ssh_config TOKENS:
135 : : //
136 : : // %% A literal `%'.
137 : : // %p The process PID
138 : : // %d Local user's home directory.
139 : : // %i The local user ID.
140 : : // %L The local hostname.
141 : : // %l The local hostname, including the domain name.
142 : : // %u The local username.
143 : 9 : std::string jl_format_filename(StringRef output_pattern)
144 : : {
145 : 18 : std::string buf;
146 : 18 : raw_string_ostream outfile(buf);
147 : 9 : bool special = false;
148 : : char hostname[MAXHOSTNAMELEN + 1];
149 : : uv_passwd_t pwd;
150 : 9 : bool got_pwd = false;
151 [ + + ]: 225 : for (auto c : output_pattern) {
152 [ + + ]: 216 : if (special) {
153 [ + + + - : 8 : if (!got_pwd && (c == 'i' || c == 'd' || c == 'u')) {
+ + - + ]
154 : 1 : int r = uv_os_get_passwd(&pwd);
155 [ + - ]: 1 : if (r == 0)
156 : 1 : got_pwd = true;
157 : : }
158 [ + + + + : 8 : switch (c) {
+ + ]
159 : 1 : case 'p':
160 : 1 : outfile << uv_os_getpid();
161 : 1 : break;
162 : 1 : case 'd':
163 [ + - ]: 1 : if (got_pwd)
164 : 1 : outfile << pwd.homedir;
165 : 1 : break;
166 : 1 : case 'i':
167 [ + - ]: 1 : if (got_pwd)
168 : 1 : outfile << pwd.uid;
169 : 1 : break;
170 : 2 : case 'l':
171 : : case 'L':
172 [ + - ]: 2 : if (gethostname(hostname, sizeof(hostname)) == 0) {
173 : 2 : hostname[sizeof(hostname) - 1] = '\0'; /* Null terminate, just to be safe. */
174 : 2 : outfile << hostname;
175 : : }
176 : : #ifndef _OS_WINDOWS_
177 [ + + + - : 2 : if (c == 'l' && getdomainname(hostname, sizeof(hostname)) == 0) {
+ + ]
178 : 1 : hostname[sizeof(hostname) - 1] = '\0'; /* Null terminate, just to be safe. */
179 : 1 : outfile << hostname;
180 : : }
181 : : #endif
182 : 2 : break;
183 : 1 : case 'u':
184 [ + - ]: 1 : if (got_pwd)
185 : 1 : outfile << pwd.username;
186 : 1 : break;
187 : 2 : default:
188 : 2 : outfile << c;
189 : 2 : break;
190 : : }
191 : 8 : special = false;
192 : : }
193 [ + + ]: 208 : else if (c == '%') {
194 : 8 : special = true;
195 : : }
196 : : else {
197 : 200 : outfile << c;
198 : : }
199 : : }
200 [ + + ]: 9 : if (got_pwd)
201 : 1 : uv_os_free_passwd(&pwd);
202 : 9 : return outfile.str();
203 : : }
204 : :
205 : 9 : extern "C" JL_DLLEXPORT char *jl_format_filename(const char *output_pattern)
206 : : {
207 : 9 : return strdup(jl_format_filename(StringRef(output_pattern)).c_str());
208 : : }
209 : :
210 : :
211 : : static uv_mutex_t trampoline_lock; // for accesses to the cache and freelist
212 : :
213 : : static void *trampoline_freelist;
214 : :
215 : 2195 : static void *trampoline_alloc() JL_NOTSAFEPOINT // lock taken by caller
216 : : {
217 : 2195 : const int sz = 64; // oversized for most platforms. todo: use precise value?
218 [ + + ]: 2195 : if (!trampoline_freelist) {
219 : 33 : int last_errno = errno;
220 : : #ifdef _OS_WINDOWS_
221 : : DWORD last_error = GetLastError();
222 : : void *mem = VirtualAlloc(NULL, jl_page_size,
223 : : MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
224 : : if (mem == NULL)
225 : : jl_throw(jl_memory_exception);
226 : : SetLastError(last_error);
227 : : #else
228 : 33 : void *mem = mmap(0, jl_page_size, PROT_READ | PROT_WRITE | PROT_EXEC,
229 : : MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
230 : 33 : errno = last_errno;
231 [ - + ]: 33 : if (mem == MAP_FAILED)
232 : 0 : jl_throw(jl_memory_exception);
233 : : #endif
234 : 33 : errno = last_errno;
235 : 33 : void *next = NULL;
236 [ - + ]: 33 : assert(sz < jl_page_size);
237 [ + + ]: 2145 : for (size_t i = 0; i + sz <= jl_page_size; i += sz) {
238 : 2112 : void **curr = (void**)((char*)mem + i);
239 : 2112 : *curr = next;
240 : 2112 : next = (void*)curr;
241 : : }
242 : 33 : trampoline_freelist = next;
243 : : }
244 : 2195 : void *tramp = trampoline_freelist;
245 : 2195 : trampoline_freelist = *(void**)tramp;
246 : 2195 : return tramp;
247 : : }
248 : :
249 : 2181 : static void trampoline_free(void *tramp) JL_NOTSAFEPOINT // lock taken by caller
250 : : {
251 : 2181 : *(void**)tramp = trampoline_freelist;
252 : 2181 : trampoline_freelist = tramp;
253 : 2181 : }
254 : :
255 : 2181 : static void trampoline_deleter(void **f) JL_NOTSAFEPOINT
256 : : {
257 : 2181 : void *tramp = f[0];
258 : 2181 : void *fobj = f[1];
259 : 2181 : void *cache = f[2];
260 : 2181 : void *nval = f[3];
261 : 2181 : f[0] = NULL;
262 : 2181 : f[2] = NULL;
263 : 2181 : f[3] = NULL;
264 : 2181 : uv_mutex_lock(&trampoline_lock);
265 [ + - ]: 2181 : if (tramp)
266 : 2181 : trampoline_free(tramp);
267 [ + - + - ]: 2181 : if (fobj && cache)
268 : 2181 : ptrhash_remove((htable_t*)cache, fobj);
269 [ + - ]: 2181 : if (nval)
270 : 2181 : free(nval);
271 : 2181 : uv_mutex_unlock(&trampoline_lock);
272 : 2181 : }
273 : :
274 : : typedef void *(*init_trampoline_t)(void *tramp, void **nval) JL_NOTSAFEPOINT;
275 : :
276 : : // Use of `cache` is not clobbered in JL_TRY
277 : : JL_GCC_IGNORE_START("-Wclobbered")
278 : : extern "C" JL_DLLEXPORT
279 : 22257 : jl_value_t *jl_get_cfunction_trampoline(
280 : : // dynamic inputs:
281 : : jl_value_t *fobj,
282 : : jl_datatype_t *result_type,
283 : : // call-site constants:
284 : : htable_t *cache, // weakref htable indexed by (fobj, vals)
285 : : jl_svec_t *fill,
286 : : init_trampoline_t init_trampoline,
287 : : jl_unionall_t *env,
288 : : jl_value_t **vals)
289 : : {
290 : : // lookup (fobj, vals) in cache
291 : 22257 : uv_mutex_lock(&trampoline_lock);
292 [ + + ]: 22285 : if (!cache->table)
293 : 19 : htable_new(cache, 1);
294 [ - + ]: 22285 : if (fill != jl_emptysvec) {
295 : 0 : htable_t **cache2 = (htable_t**)ptrhash_bp(cache, (void*)vals);
296 : 0 : cache = *cache2;
297 [ # # ]: 0 : if (cache == HT_NOTFOUND) {
298 : 0 : cache = htable_new((htable_t*)malloc_s(sizeof(htable_t)), 1);
299 : 0 : *cache2 = cache;
300 : : }
301 : : }
302 : 22285 : void *tramp = ptrhash_get(cache, (void*)fobj);
303 : 22285 : uv_mutex_unlock(&trampoline_lock);
304 [ + + ]: 22259 : if (tramp != HT_NOTFOUND) {
305 [ - + ]: 20062 : assert((jl_datatype_t*)jl_typeof(tramp) == result_type);
306 : 20062 : return (jl_value_t*)tramp;
307 : : }
308 : :
309 : : // not found, allocate a new one
310 : 2197 : size_t n = jl_svec_len(fill);
311 : 2197 : void **nval = (void**)malloc_s(sizeof(void*) * (n + 1));
312 : 2193 : nval[0] = (void*)fobj;
313 : : jl_value_t *result;
314 [ + - + + ]: 4377 : JL_TRY {
315 [ - + ]: 2190 : for (size_t i = 0; i < n; i++) {
316 : 0 : jl_value_t *sparam_val = jl_instantiate_type_in_env(jl_svecref(fill, i), env, vals);
317 [ # # ]: 0 : if (sparam_val != (jl_value_t*)jl_any_type)
318 [ # # # # : 0 : if (!jl_is_concrete_type(sparam_val) || !jl_is_immutable(sparam_val))
# # ]
319 : 0 : sparam_val = NULL;
320 : 0 : nval[i + 1] = (void*)sparam_val;
321 : : }
322 : : int permanent =
323 : 4380 : (result_type == jl_voidpointer_type) ||
324 [ + - + - ]: 4380 : jl_is_concrete_type(fobj) ||
325 [ + + ]: 2190 : (((jl_datatype_t*)jl_typeof(fobj))->instance == fobj);
326 [ + + ]: 2190 : if (jl_is_unionall(fobj)) {
327 : 32 : jl_value_t *uw = jl_unwrap_unionall(fobj);
328 [ + - + + ]: 32 : if (jl_is_datatype(uw) && ((jl_datatype_t*)uw)->name->wrapper == fobj)
329 : 2 : permanent = true;
330 : : }
331 [ + + ]: 2190 : if (permanent) {
332 : 14 : result = jl_gc_permobj(sizeof(jl_taggedvalue_t) + jl_datatype_size(result_type), result_type);
333 : 14 : memset(result, 0, jl_datatype_size(result_type));
334 : : }
335 : : else {
336 : 2176 : result = jl_new_struct_uninit(result_type);
337 : : }
338 [ + - ]: 2186 : if (result_type != jl_voidpointer_type) {
339 [ - + ]: 2186 : assert(jl_datatype_size(result_type) == sizeof(void*) * 4);
340 : 2186 : ((void**)result)[1] = (void*)fobj;
341 : : }
342 [ + + ]: 2186 : if (!permanent) {
343 : 2172 : jl_task_t *ct = jl_current_task;
344 : 2172 : jl_gc_add_ptr_finalizer(ct->ptls, result, (void*)(uintptr_t)&trampoline_deleter);
345 : 2170 : ((void**)result)[2] = (void*)cache;
346 : 2170 : ((void**)result)[3] = (void*)nval;
347 : : }
348 : : }
349 [ # # ]: 0 : JL_CATCH {
350 : 0 : free(nval);
351 : 0 : jl_rethrow();
352 : : }
353 : 2184 : uv_mutex_lock(&trampoline_lock);
354 : 2195 : tramp = trampoline_alloc();
355 : 2195 : ((void**)result)[0] = tramp;
356 : 2195 : tramp = init_trampoline(tramp, nval);
357 : 2195 : ptrhash_put(cache, (void*)fobj, result);
358 : 2195 : uv_mutex_unlock(&trampoline_lock);
359 : 2195 : return result;
360 : : }
361 : : JL_GCC_IGNORE_STOP
362 : :
363 : 573 : void jl_init_runtime_ccall(void)
364 : : {
365 : 573 : JL_MUTEX_INIT(&libmap_lock);
366 : 573 : uv_mutex_init(&trampoline_lock);
367 : 573 : }
|