Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

mjit.c
Go to the documentation of this file.
1 /**********************************************************************
2
3 mjit.c - MRI method JIT compiler functions for Ruby's main thread
4
5 Copyright (C) 2017 Vladimir Makarov <vmakarov@redhat.com>.
6
7 **********************************************************************/
8
9 // Functions in this file are never executed on MJIT worker thread.
10 // So you can safely use Ruby methods and GC in this file.
11
12 // To share variables privately, include mjit_worker.c instead of linking.
13
14 #include "internal.h"
15
16 #if USE_MJIT
17
18 #include "mjit_worker.c"
19
20 #include "constant.h"
21 #include "id_table.h"
22
23 // Copy ISeq's states so that race condition does not happen on compilation.
24 static void
25 mjit_copy_job_handler(void *data)
26 {
27 mjit_copy_job_t *job = data;
28 if (stop_worker_p) { // check if mutex is still alive, before calling CRITICAL_SECTION_START.
29 return;
30 }
31
32 CRITICAL_SECTION_START(3, "in mjit_copy_job_handler");
33 // Make sure that this job is never executed when:
34 // 1. job is being modified
35 // 2. alloca memory inside job is expired
36 // 3. ISeq is GC-ed
37 if (job->finish_p) {
38 CRITICAL_SECTION_FINISH(3, "in mjit_copy_job_handler");
39 return;
40 }
41 else if (job->iseq == NULL) { // ISeq GC notified in mjit_mark_iseq
42 job->finish_p = true;
43 CRITICAL_SECTION_FINISH(3, "in mjit_copy_job_handler");
44 return;
45 }
46
47 const struct rb_iseq_constant_body *body = job->iseq->body;
48 if (job->cc_entries) {
49 unsigned int i;
50 struct rb_call_cache *sink = job->cc_entries;
51 const struct rb_call_data *calls = body->call_data;
52 const struct rb_kwarg_call_data *kw_calls = (struct rb_kwarg_call_data *)&body->call_data[body->ci_size];
53 for (i = 0; i < body->ci_size; i++) {
54 *sink++ = calls[i].cc;
55 }
56 for (i = 0; i < body->ci_kw_size; i++) {
57 *sink++ = kw_calls[i].cc;
58 }
59 }
60 if (job->is_entries) {
61 memcpy(job->is_entries, body->is_entries, sizeof(union iseq_inline_storage_entry) * body->is_size);
62 }
63
64 job->finish_p = true;
65 rb_native_cond_broadcast(&mjit_worker_wakeup);
66 CRITICAL_SECTION_FINISH(3, "in mjit_copy_job_handler");
67 }
68
69 extern int rb_thread_create_mjit_thread(void (*worker_func)(void));
70
71 // Return an unique file name in /tmp with PREFIX and SUFFIX and
72 // number ID. Use getpid if ID == 0. The return file name exists
73 // until the next function call.
74 static char *
75 get_uniq_filename(unsigned long id, const char *prefix, const char *suffix)
76 {
77 char buff[70], *str = buff;
78 int size = sprint_uniq_filename(buff, sizeof(buff), id, prefix, suffix);
79 str = 0;
80 ++size;
81 str = xmalloc(size);
82 if (size <= (int)sizeof(buff)) {
83 memcpy(str, buff, size);
84 }
85 else {
86 sprint_uniq_filename(str, size, id, prefix, suffix);
87 }
88 return str;
89 }
90
91 // Wait until workers don't compile any iseq. It is called at the
92 // start of GC.
93 void
94 mjit_gc_start_hook(void)
95 {
96 if (!mjit_enabled)
97 return;
98 CRITICAL_SECTION_START(4, "mjit_gc_start_hook");
99 while (in_jit) {
100 verbose(4, "Waiting wakeup from a worker for GC");
101 rb_native_cond_wait(&mjit_client_wakeup, &mjit_engine_mutex);
102 verbose(4, "Getting wakeup from a worker for GC");
103 }
104 in_gc = true;
105 CRITICAL_SECTION_FINISH(4, "mjit_gc_start_hook");
106 }
107
108 // Send a signal to workers to continue iseq compilations. It is
109 // called at the end of GC.
110 void
111 mjit_gc_exit_hook(void)
112 {
113 if (!mjit_enabled)
114 return;
115 CRITICAL_SECTION_START(4, "mjit_gc_exit_hook");
116 in_gc = false;
117 verbose(4, "Sending wakeup signal to workers after GC");
118 rb_native_cond_broadcast(&mjit_gc_wakeup);
119 CRITICAL_SECTION_FINISH(4, "mjit_gc_exit_hook");
120 }
121
122 // Deal with ISeq movement from compactor
123 void
124 mjit_update_references(const rb_iseq_t *iseq)
125 {
126 if (!mjit_enabled)
127 return;
128
129 CRITICAL_SECTION_START(4, "mjit_update_references");
130 if (iseq->body->jit_unit) {
131 iseq->body->jit_unit->iseq = (rb_iseq_t *)rb_gc_location((VALUE)iseq->body->jit_unit->iseq);
132 // We need to invalidate JIT-ed code for the ISeq because it embeds pointer addresses.
133 // To efficiently do that, we use the same thing as TracePoint and thus everything is cancelled for now.
134 // See mjit.h and tool/ruby_vm/views/_mjit_compile_insn.erb for how `mjit_call_p` is used.
135 mjit_call_p = false; // TODO: instead of cancelling all, invalidate only this one and recompile it with some threshold.
136 }
137
138 // Units in stale_units (list of over-speculated and invalidated code) are not referenced from
139 // `iseq->body->jit_unit` anymore (because new one replaces that). So we need to check them too.
140 // TODO: we should be able to reduce the number of units checked here.
141 struct rb_mjit_unit *unit = NULL;
142 list_for_each(&stale_units.head, unit, unode) {
143 if (unit->iseq == iseq) {
144 unit->iseq = (rb_iseq_t *)rb_gc_location((VALUE)unit->iseq);
145 }
146 }
147 CRITICAL_SECTION_FINISH(4, "mjit_update_references");
148 }
149
150 // Iseqs can be garbage collected. This function should call when it
151 // happens. It removes iseq from the unit.
152 void
153 mjit_free_iseq(const rb_iseq_t *iseq)
154 {
155 if (!mjit_enabled)
156 return;
157
158 CRITICAL_SECTION_START(4, "mjit_free_iseq");
159 if (mjit_copy_job.iseq == iseq) {
160 mjit_copy_job.iseq = NULL;
161 }
162 if (iseq->body->jit_unit) {
163 // jit_unit is not freed here because it may be referred by multiple
164 // lists of units. `get_from_list` and `mjit_finish` do the job.
165 iseq->body->jit_unit->iseq = NULL;
166 }
167 // Units in stale_units (list of over-speculated and invalidated code) are not referenced from
168 // `iseq->body->jit_unit` anymore (because new one replaces that). So we need to check them too.
169 // TODO: we should be able to reduce the number of units checked here.
170 struct rb_mjit_unit *unit = NULL;
171 list_for_each(&stale_units.head, unit, unode) {
172 if (unit->iseq == iseq) {
173 unit->iseq = NULL;
174 }
175 }
176 CRITICAL_SECTION_FINISH(4, "mjit_free_iseq");
177 }
178
179 // Free unit list. This should be called only when worker is finished
180 // because node of unit_queue and one of active_units may have the same unit
181 // during proceeding unit.
182 static void
183 free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
184 {
185 struct rb_mjit_unit *unit = 0, *next;
186
187 list_for_each_safe(&list->head, unit, next, unode) {
188 list_del(&unit->unode);
189 if (!close_handle_p) unit->handle = NULL; /* Skip dlclose in free_unit() */
190
191 if (list == &stale_units) { // `free_unit(unit)` crashes after GC.compact on `stale_units`
192 /*
193 * TODO: REVERT THIS BRANCH
194 * Debug the crash on stale_units w/ GC.compact and just use `free_unit(unit)`!!
195 */
196 if (unit->handle && dlclose(unit->handle)) {
197 mjit_warning("failed to close handle for u%d: %s", unit->id, dlerror());
198 }
199 clean_object_files(unit);
200 free(unit);
201 }
202 else {
203 free_unit(unit);
204 }
205 }
206 list->length = 0;
207 }
208
209 // MJIT info related to an existing continutaion.
210 struct mjit_cont {
211 rb_execution_context_t *ec; // continuation ec
212 struct mjit_cont *prev, *next; // used to form lists
213 };
214
215 // Double linked list of registered continuations. This is used to detect
216 // units which are in use in unload_units.
217 static struct mjit_cont *first_cont;
218
219 // Register a new continuation with execution context `ec`. Return MJIT info about
220 // the continuation.
221 struct mjit_cont *
222 mjit_cont_new(rb_execution_context_t *ec)
223 {
224 struct mjit_cont *cont;
225
226 cont = ZALLOC(struct mjit_cont);
227 cont->ec = ec;
228
229 CRITICAL_SECTION_START(3, "in mjit_cont_new");
230 if (first_cont == NULL) {
231 cont->next = cont->prev = NULL;
232 }
233 else {
234 cont->prev = NULL;
235 cont->next = first_cont;
236 first_cont->prev = cont;
237 }
238 first_cont = cont;
239 CRITICAL_SECTION_FINISH(3, "in mjit_cont_new");
240
241 return cont;
242 }
243
244 // Unregister continuation `cont`.
245 void
246 mjit_cont_free(struct mjit_cont *cont)
247 {
248 CRITICAL_SECTION_START(3, "in mjit_cont_new");
249 if (cont == first_cont) {
250 first_cont = cont->next;
251 if (first_cont != NULL)
252 first_cont->prev = NULL;
253 }
254 else {
255 cont->prev->next = cont->next;
256 if (cont->next != NULL)
257 cont->next->prev = cont->prev;
258 }
259 CRITICAL_SECTION_FINISH(3, "in mjit_cont_new");
260
261 xfree(cont);
262 }
263
264 // Finish work with continuation info.
265 static void
266 finish_conts(void)
267 {
268 struct mjit_cont *cont, *next;
269
270 for (cont = first_cont; cont != NULL; cont = next) {
271 next = cont->next;
272 xfree(cont);
273 }
274 }
275
276 // Create unit for `iseq`.
277 static void
278 create_unit(const rb_iseq_t *iseq)
279 {
280 struct rb_mjit_unit *unit;
281
282 unit = ZALLOC(struct rb_mjit_unit);
283 if (unit == NULL)
284 return;
285
286 unit->id = current_unit_num++;
287 unit->iseq = (rb_iseq_t *)iseq;
288 iseq->body->jit_unit = unit;
289 }
290
291 // Set up field `used_code_p` for unit iseqs whose iseq on the stack of ec.
292 static void
293 mark_ec_units(rb_execution_context_t *ec)
294 {
295 const rb_control_frame_t *cfp;
296
297 if (ec->vm_stack == NULL)
298 return;
299 for (cfp = RUBY_VM_END_CONTROL_FRAME(ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
300 const rb_iseq_t *iseq;
301 if (cfp->pc && (iseq = cfp->iseq) != NULL
302 && imemo_type((VALUE) iseq) == imemo_iseq
303 && (iseq->body->jit_unit) != NULL) {
304 iseq->body->jit_unit->used_code_p = TRUE;
305 }
306
307 if (cfp == ec->cfp)
308 break; // reached the most recent cfp
309 }
310 }
311
312 // Unload JIT code of some units to satisfy the maximum permitted
313 // number of units with a loaded code.
314 static void
315 unload_units(void)
316 {
317 rb_vm_t *vm = GET_THREAD()->vm;
318 rb_thread_t *th = NULL;
319 struct rb_mjit_unit *unit = 0, *next, *worst;
320 struct mjit_cont *cont;
321 int delete_num, units_num = active_units.length;
322
323 // For now, we don't unload units when ISeq is GCed. We should
324 // unload such ISeqs first here.
325 list_for_each_safe(&active_units.head, unit, next, unode) {
326 if (unit->iseq == NULL) { // ISeq is GCed.
327 remove_from_list(unit, &active_units);
328 free_unit(unit);
329 }
330 }
331
332 // Detect units which are in use and can't be unloaded.
333 list_for_each(&active_units.head, unit, unode) {
334 assert(unit->iseq != NULL && unit->handle != NULL);
335 unit->used_code_p = FALSE;
336 }
337 list_for_each(&vm->living_threads, th, vmlt_node) {
338 mark_ec_units(th->ec);
339 }
340 for (cont = first_cont; cont != NULL; cont = cont->next) {
341 mark_ec_units(cont->ec);
342 }
343 // TODO: check slale_units and unload unused ones! (note that the unit is not associated to ISeq anymore)
344
345 // Remove 1/10 units more to decrease unloading calls.
346 // TODO: Calculate max total_calls in unit_queue and don't unload units
347 // whose total_calls are larger than the max.
348 delete_num = active_units.length / 10;
349 for (; active_units.length > mjit_opts.max_cache_size - delete_num;) {
350 // Find one unit that has the minimum total_calls.
351 worst = NULL;
352 list_for_each(&active_units.head, unit, unode) {
353 if (unit->used_code_p) // We can't unload code on stack.
354 continue;
355
356 if (worst == NULL || worst->iseq->body->total_calls > unit->iseq->body->total_calls) {
357 worst = unit;
358 }
359 }
360 if (worst == NULL)
361 break;
362
363 // Unload the worst node.
364 verbose(2, "Unloading unit %d (calls=%lu)", worst->id, worst->iseq->body->total_calls);
365 assert(worst->handle != NULL);
366 remove_from_list(worst, &active_units);
367 free_unit(worst);
368 }
369 verbose(1, "Too many JIT code -- %d units unloaded", units_num - active_units.length);
370 }
371
372 static void
373 mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_info *compile_info)
374 {
375 if (!mjit_enabled || pch_status == PCH_FAILED)
376 return;
377
378 iseq->body->jit_func = (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC;
379 create_unit(iseq);
380 if (iseq->body->jit_unit == NULL)
381 // Failure in creating the unit.
382 return;
383 if (compile_info != NULL)
384 iseq->body->jit_unit->compile_info = *compile_info;
385
386 CRITICAL_SECTION_START(3, "in add_iseq_to_process");
387 add_to_list(iseq->body->jit_unit, &unit_queue);
388 if (active_units.length >= mjit_opts.max_cache_size) {
389 unload_units();
390 }
391 verbose(3, "Sending wakeup signal to workers in mjit_add_iseq_to_process");
392 rb_native_cond_broadcast(&mjit_worker_wakeup);
393 CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process");
394 }
395
396 // Add ISEQ to be JITed in parallel with the current thread.
397 // Unload some JIT codes if there are too many of them.
398 void
399 rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq)
400 {
401 mjit_add_iseq_to_process(iseq, NULL);
402 }
403
404 // For this timeout seconds, --jit-wait will wait for JIT compilation finish.
405 #define MJIT_WAIT_TIMEOUT_SECONDS 60
406
407 static void
408 mjit_wait(struct rb_iseq_constant_body *body)
409 {
410 struct timeval tv;
411 int tries = 0;
412 tv.tv_sec = 0;
413 tv.tv_usec = 1000;
414 while (body->jit_func == (mjit_func_t)NOT_READY_JIT_ISEQ_FUNC) {
415 tries++;
416 if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS || pch_status == PCH_FAILED) {
417 CRITICAL_SECTION_START(3, "in rb_mjit_wait_call to set jit_func");
418 body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; // JIT worker seems dead. Give up.
419 CRITICAL_SECTION_FINISH(3, "in rb_mjit_wait_call to set jit_func");
420 mjit_warning("timed out to wait for JIT finish");
421 break;
422 }
423
424 CRITICAL_SECTION_START(3, "in rb_mjit_wait_call for a client wakeup");
425 rb_native_cond_broadcast(&mjit_worker_wakeup);
426 CRITICAL_SECTION_FINISH(3, "in rb_mjit_wait_call for a client wakeup");
427 rb_thread_wait_for(tv);
428 }
429 }
430
431 // Wait for JIT compilation finish for --jit-wait, and call the function pointer
432 // if the compiled result is not NOT_COMPILED_JIT_ISEQ_FUNC.
433 VALUE
434 rb_mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body)
435 {
436 mjit_wait(body);
437 if ((uintptr_t)body->jit_func <= (uintptr_t)LAST_JIT_ISEQ_FUNC) {
438 return Qundef;
439 }
440 return body->jit_func(ec, ec->cfp);
441 }
442
443 struct rb_mjit_compile_info*
444 rb_mjit_iseq_compile_info(const struct rb_iseq_constant_body *body)
445 {
446 assert(body->jit_unit != NULL);
447 return &body->jit_unit->compile_info;
448 }
449
450 void
451 rb_mjit_recompile_iseq(const rb_iseq_t *iseq)
452 {
453 if ((uintptr_t)iseq->body->jit_func <= (uintptr_t)LAST_JIT_ISEQ_FUNC)
454 return;
455
456 verbose(1, "JIT recompile: %s@%s:%d", RSTRING_PTR(iseq->body->location.label),
457 RSTRING_PTR(rb_iseq_path(iseq)), FIX2INT(iseq->body->location.first_lineno));
458
459 CRITICAL_SECTION_START(3, "in rb_mjit_recompile_iseq");
460 remove_from_list(iseq->body->jit_unit, &active_units);
461 iseq->body->jit_func = (void *)NOT_ADDED_JIT_ISEQ_FUNC;
462 add_to_list(iseq->body->jit_unit, &stale_units);
463 CRITICAL_SECTION_FINISH(3, "in rb_mjit_recompile_iseq");
464
465 mjit_add_iseq_to_process(iseq, &iseq->body->jit_unit->compile_info);
466 if (UNLIKELY(mjit_opts.wait)) {
467 mjit_wait(iseq->body);
468 }
469 }
470
471 extern VALUE ruby_archlibdir_path, ruby_prefix_path;
472
473 // Initialize header_file, pch_file, libruby_pathflag. Return true on success.
474 static bool
475 init_header_filename(void)
476 {
477 int fd;
478 #ifdef LOAD_RELATIVE
479 // Root path of the running ruby process. Equal to RbConfig::TOPDIR.
480 VALUE basedir_val;
481 #endif
482 const char *basedir = "";
483 size_t baselen = 0;
484 char *p;
485 #ifdef _WIN32
486 static const char libpathflag[] =
487 # ifdef _MSC_VER
488 "-LIBPATH:"
489 # else
490 "-L"
491 # endif
492 ;
493 const size_t libpathflag_len = sizeof(libpathflag) - 1;
494 #endif
495
496 #ifdef LOAD_RELATIVE
497 basedir_val = ruby_prefix_path;
498 basedir = StringValuePtr(basedir_val);
499 baselen = RSTRING_LEN(basedir_val);
500 #else
501 if (getenv("MJIT_SEARCH_BUILD_DIR")) {
502 // This path is not intended to be used on production, but using build directory's
503 // header file here because people want to run `make test-all` without running
504 // `make install`. Don't use $MJIT_SEARCH_BUILD_DIR except for test-all.
505
506 struct stat st;
507 const char *hdr = dlsym(RTLD_DEFAULT, "MJIT_HEADER");
508 if (!hdr) {
509 verbose(1, "No MJIT_HEADER");
510 }
511 else if (hdr[0] != '/') {
512 verbose(1, "Non-absolute header file path: %s", hdr);
513 }
514 else if (stat(hdr, &st) || !S_ISREG(st.st_mode)) {
515 verbose(1, "Non-file header file path: %s", hdr);
516 }
517 else if ((st.st_uid != getuid()) || (st.st_mode & 022) ||
518 !rb_path_check(hdr)) {
519 verbose(1, "Unsafe header file: uid=%ld mode=%#o %s",
520 (long)st.st_uid, (unsigned)st.st_mode, hdr);
521 return FALSE;
522 }
523 else {
524 // Do not pass PRELOADENV to child processes, on
525 // multi-arch environment
526 verbose(3, "PRELOADENV("PRELOADENV")=%s", getenv(PRELOADENV));
527 // assume no other PRELOADENV in test-all
528 unsetenv(PRELOADENV);
529 verbose(3, "MJIT_HEADER: %s", hdr);
530 header_file = ruby_strdup(hdr);
531 if (!header_file) return false;
532 }
533 }
534 else
535 #endif
536 #ifndef _MSC_VER
537 {
538 // A name of the header file included in any C file generated by MJIT for iseqs.
539 static const char header_name[] = MJIT_HEADER_INSTALL_DIR "/" MJIT_MIN_HEADER_NAME;
540 const size_t header_name_len = sizeof(header_name) - 1;
541
542 header_file = xmalloc(baselen + header_name_len + 1);
543 p = append_str2(header_file, basedir, baselen);
544 p = append_str2(p, header_name, header_name_len + 1);
545
546 if ((fd = rb_cloexec_open(header_file, O_RDONLY, 0)) < 0) {
547 verbose(1, "Cannot access header file: %s", header_file);
548 xfree(header_file);
549 header_file = NULL;
550 return false;
551 }
552 (void)close(fd);
553 }
554
555 pch_file = get_uniq_filename(0, MJIT_TMP_PREFIX "h", ".h.gch");
556 #else
557 {
558 static const char pch_name[] = MJIT_HEADER_INSTALL_DIR "/" MJIT_PRECOMPILED_HEADER_NAME;
559 const size_t pch_name_len = sizeof(pch_name) - 1;
560
561 pch_file = xmalloc(baselen + pch_name_len + 1);
562 p = append_str2(pch_file, basedir, baselen);
563 p = append_str2(p, pch_name, pch_name_len + 1);
564 if ((fd = rb_cloexec_open(pch_file, O_RDONLY, 0)) < 0) {
565 verbose(1, "Cannot access precompiled header file: %s", pch_file);
566 xfree(pch_file);
567 pch_file = NULL;
568 return false;
569 }
570 (void)close(fd);
571 }
572 #endif
573
574 #ifdef _WIN32
575 basedir_val = ruby_archlibdir_path;
576 basedir = StringValuePtr(basedir_val);
577 baselen = RSTRING_LEN(basedir_val);
578 libruby_pathflag = p = xmalloc(libpathflag_len + baselen + 1);
579 p = append_str(p, libpathflag);
580 p = append_str2(p, basedir, baselen);
581 *p = '\0';
582 #endif
583
584 return true;
585 }
586
587 static enum rb_id_table_iterator_result
588 valid_class_serials_add_i(ID key, VALUE v, void *unused)
589 {
590 rb_const_entry_t *ce = (rb_const_entry_t *)v;
591 VALUE value = ce->value;
592
593 if (!rb_is_const_id(key)) return ID_TABLE_CONTINUE;
594 if (RB_TYPE_P(value, T_MODULE) || RB_TYPE_P(value, T_CLASS)) {
595 mjit_add_class_serial(RCLASS_SERIAL(value));
596 }
597 return ID_TABLE_CONTINUE;
598 }
599
600 #ifdef _WIN32
601 UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
602 #endif
603
604 static char *
605 system_default_tmpdir(void)
606 {
607 // c.f. ext/etc/etc.c:etc_systmpdir()
608 #ifdef _WIN32
609 WCHAR tmppath[_MAX_PATH];
610 UINT len = rb_w32_system_tmpdir(tmppath, numberof(tmppath));
611 if (len) {
612 int blen = WideCharToMultiByte(CP_UTF8, 0, tmppath, len, NULL, 0, NULL, NULL);
613 char *tmpdir = xmalloc(blen + 1);
614 WideCharToMultiByte(CP_UTF8, 0, tmppath, len, tmpdir, blen, NULL, NULL);
615 tmpdir[blen] = '\0';
616 return tmpdir;
617 }
618 #elif defined _CS_DARWIN_USER_TEMP_DIR
619 char path[MAXPATHLEN];
620 size_t len = confstr(_CS_DARWIN_USER_TEMP_DIR, path, sizeof(path));
621 if (len > 0) {
622 char *tmpdir = xmalloc(len);
623 if (len > sizeof(path)) {
624 confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdir, len);
625 }
626 else {
627 memcpy(tmpdir, path, len);
628 }
629 return tmpdir;
630 }
631 #endif
632 return 0;
633 }
634
635 static int
636 check_tmpdir(const char *dir)
637 {
638 struct stat st;
639
640 if (!dir) return FALSE;
641 if (stat(dir, &st)) return FALSE;
642 #ifndef S_ISDIR
643 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
644 #endif
645 if (!S_ISDIR(st.st_mode)) return FALSE;
646 #ifndef _WIN32
647 # ifndef S_IWOTH
648 # define S_IWOTH 002
649 # endif
650 if (st.st_mode & S_IWOTH) {
651 # ifdef S_ISVTX
652 if (!(st.st_mode & S_ISVTX)) return FALSE;
653 # else
654 return FALSE;
655 # endif
656 }
657 if (access(dir, W_OK)) return FALSE;
658 #endif
659 return TRUE;
660 }
661
662 static char *
663 system_tmpdir(void)
664 {
665 char *tmpdir;
666 # define RETURN_ENV(name) \
667 if (check_tmpdir(tmpdir = getenv(name))) return ruby_strdup(tmpdir)
668 RETURN_ENV("TMPDIR");
669 RETURN_ENV("TMP");
670 tmpdir = system_default_tmpdir();
671 if (check_tmpdir(tmpdir)) return tmpdir;
672 return ruby_strdup("/tmp");
673 # undef RETURN_ENV
674 }
675
676 // Minimum value for JIT cache size.
677 #define MIN_CACHE_SIZE 10
678 // Default permitted number of units with a JIT code kept in memory.
679 #define DEFAULT_MAX_CACHE_SIZE 100
680 // A default threshold used to add iseq to JIT.
681 #define DEFAULT_MIN_CALLS_TO_ADD 10000
682
683 // Start MJIT worker. Return TRUE if worker is successfully started.
684 static bool
685 start_worker(void)
686 {
687 stop_worker_p = false;
688 worker_stopped = false;
689
690 if (!rb_thread_create_mjit_thread(mjit_worker)) {
691 mjit_enabled = false;
692 rb_native_mutex_destroy(&mjit_engine_mutex);
693 rb_native_cond_destroy(&mjit_pch_wakeup);
694 rb_native_cond_destroy(&mjit_client_wakeup);
695 rb_native_cond_destroy(&mjit_worker_wakeup);
696 rb_native_cond_destroy(&mjit_gc_wakeup);
697 verbose(1, "Failure in MJIT thread initialization\n");
698 return false;
699 }
700 return true;
701 }
702
703 // There's no strndup on Windows
704 static char*
705 ruby_strndup(const char *str, size_t n)
706 {
707 char *ret = xmalloc(n + 1);
708 memcpy(ret, str, n);
709 ret[n] = '\0';
710 return ret;
711 }
712
713 // Convert "foo bar" to {"foo", "bar", NULL} array. Caller is responsible for
714 // freeing a returned buffer and its elements.
715 static char **
716 split_flags(const char *flags)
717 {
718 char *buf[MAXPATHLEN];
719 int i = 0;
720 char *next;
721 for (; flags != NULL; flags = next) {
722 next = strchr(flags, ' ');
723 if (next == NULL) {
724 if (strlen(flags) > 0)
725 buf[i++] = strdup(flags);
726 }
727 else {
728 if (next > flags)
729 buf[i++] = ruby_strndup(flags, next - flags);
730 next++; // skip space
731 }
732 }
733
734 char **ret = xmalloc(sizeof(char *) * (i + 1));
735 memcpy(ret, buf, sizeof(char *) * i);
736 ret[i] = NULL;
737 return ret;
738 }
739
740 // Initialize MJIT. Start a thread creating the precompiled header and
741 // processing ISeqs. The function should be called first for using MJIT.
742 // If everything is successful, MJIT_INIT_P will be TRUE.
743 void
744 mjit_init(const struct mjit_options *opts)
745 {
746 mjit_opts = *opts;
747 mjit_enabled = true;
748 mjit_call_p = true;
749
750 // Normalize options
751 if (mjit_opts.min_calls == 0)
752 mjit_opts.min_calls = DEFAULT_MIN_CALLS_TO_ADD;
753 if (mjit_opts.max_cache_size <= 0)
754 mjit_opts.max_cache_size = DEFAULT_MAX_CACHE_SIZE;
755 if (mjit_opts.max_cache_size < MIN_CACHE_SIZE)
756 mjit_opts.max_cache_size = MIN_CACHE_SIZE;
757
758 // Initialize variables for compilation
759 #ifdef _MSC_VER
760 pch_status = PCH_SUCCESS; // has prebuilt precompiled header
761 #else
762 pch_status = PCH_NOT_READY;
763 #endif
764 cc_path = CC_COMMON_ARGS[0];
765 verbose(2, "MJIT: CC defaults to %s", cc_path);
766 cc_common_args = xmalloc(sizeof(CC_COMMON_ARGS));
767 memcpy((void *)cc_common_args, CC_COMMON_ARGS, sizeof(CC_COMMON_ARGS));
768 cc_added_args = split_flags(opts->debug_flags);
769 xfree(opts->debug_flags);
770 #if MJIT_CFLAGS_PIPE
771 // eliminate a flag incompatible with `-pipe`
772 for (size_t i = 0, j = 0; i < sizeof(CC_COMMON_ARGS) / sizeof(char *); i++) {
773 if (CC_COMMON_ARGS[i] && strncmp("-save-temps", CC_COMMON_ARGS[i], strlen("-save-temps")) == 0)
774 continue; // skip -save-temps flag
775 cc_common_args[j] = CC_COMMON_ARGS[i];
776 j++;
777 }
778 #endif
779
780 tmp_dir = system_tmpdir();
781 verbose(2, "MJIT: tmp_dir is %s", tmp_dir);
782
783 if (!init_header_filename()) {
784 mjit_enabled = false;
785 verbose(1, "Failure in MJIT header file name initialization\n");
786 return;
787 }
788 pch_owner_pid = getpid();
789
790 // Initialize mutex
791 rb_native_mutex_initialize(&mjit_engine_mutex);
792 rb_native_cond_initialize(&mjit_pch_wakeup);
793 rb_native_cond_initialize(&mjit_client_wakeup);
794 rb_native_cond_initialize(&mjit_worker_wakeup);
795 rb_native_cond_initialize(&mjit_gc_wakeup);
796
797 // Initialize class_serials cache for compilation
798 valid_class_serials = rb_hash_new();
799 rb_obj_hide(valid_class_serials);
800 rb_gc_register_mark_object(valid_class_serials);
801 mjit_add_class_serial(RCLASS_SERIAL(rb_cObject));
802 mjit_add_class_serial(RCLASS_SERIAL(CLASS_OF(rb_vm_top_self())));
803 if (RCLASS_CONST_TBL(rb_cObject)) {
804 rb_id_table_foreach(RCLASS_CONST_TBL(rb_cObject), valid_class_serials_add_i, NULL);
805 }
806
807 // Initialize worker thread
808 start_worker();
809 }
810
811 static void
812 stop_worker(void)
813 {
814 rb_execution_context_t *ec = GET_EC();
815
816 while (!worker_stopped) {
817 verbose(3, "Sending cancel signal to worker");
818 CRITICAL_SECTION_START(3, "in stop_worker");
819 stop_worker_p = true; // Setting this inside loop because RUBY_VM_CHECK_INTS may make this false.
820 rb_native_cond_broadcast(&mjit_worker_wakeup);
821 CRITICAL_SECTION_FINISH(3, "in stop_worker");
822 RUBY_VM_CHECK_INTS(ec);
823 }
824 }
825
826 // Stop JIT-compiling methods but compiled code is kept available.
827 VALUE
828 mjit_pause(bool wait_p)
829 {
830 if (!mjit_enabled) {
831 rb_raise(rb_eRuntimeError, "MJIT is not enabled");
832 }
833 if (worker_stopped) {
834 return Qfalse;
835 }
836
837 // Flush all queued units with no option or `wait: true`
838 if (wait_p) {
839 struct timeval tv;
840 tv.tv_sec = 0;
841 tv.tv_usec = 1000;
842
843 while (unit_queue.length > 0 && active_units.length < mjit_opts.max_cache_size) { // inverse of condition that waits for mjit_worker_wakeup
844 CRITICAL_SECTION_START(3, "in mjit_pause for a worker wakeup");
845 rb_native_cond_broadcast(&mjit_worker_wakeup);
846 CRITICAL_SECTION_FINISH(3, "in mjit_pause for a worker wakeup");
847 rb_thread_wait_for(tv);
848 }
849 }
850
851 stop_worker();
852 return Qtrue;
853 }
854
855 // Restart JIT-compiling methods after mjit_pause.
856 VALUE
857 mjit_resume(void)
858 {
859 if (!mjit_enabled) {
860 rb_raise(rb_eRuntimeError, "MJIT is not enabled");
861 }
862 if (!worker_stopped) {
863 return Qfalse;
864 }
865
866 if (!start_worker()) {
867 rb_raise(rb_eRuntimeError, "Failed to resume MJIT worker");
868 }
869 return Qtrue;
870 }
871
872 // Skip calling `clean_object_files` for units which currently exist in the list.
873 static void
874 skip_cleaning_object_files(struct rb_mjit_unit_list *list)
875 {
876 struct rb_mjit_unit *unit = NULL, *next;
877
878 // No mutex for list, assuming MJIT worker does not exist yet since it's immediately after fork.
879 list_for_each_safe(&list->head, unit, next, unode) {
880 #ifndef _MSC_VER // Actually mswin does not reach here since it doesn't have fork
881 if (unit->o_file) unit->o_file_inherited_p = true;
882 #endif
883
884 #if defined(_WIN32) // mswin doesn't reach here either. This is for MinGW.
885 if (unit->so_file) unit->so_file = NULL;
886 #endif
887 }
888 }
889
890 // This is called after fork initiated by Ruby's method to launch MJIT worker thread
891 // for child Ruby process.
892 //
893 // In multi-process Ruby applications, child Ruby processes do most of the jobs.
894 // Thus we want child Ruby processes to enqueue ISeqs to MJIT worker's queue and
895 // call the JIT-ed code.
896 //
897 // But unfortunately current MJIT-generated code is process-specific. After the fork,
898 // JIT-ed code created by parent Ruby process cannot be used in child Ruby process
899 // because the code could rely on inline cache values (ivar's IC, send's CC) which
900 // may vary between processes after fork or embed some process-specific addresses.
901 //
902 // So child Ruby process can't request parent process to JIT an ISeq and use the code.
903 // Instead of that, MJIT worker thread is created for all child Ruby processes, even
904 // while child processes would end up with compiling the same ISeqs.
905 void
906 mjit_child_after_fork(void)
907 {
908 if (!mjit_enabled)
909 return;
910
911 /* Let parent process delete the already-compiled object files.
912 This must be done before starting MJIT worker on child process. */
913 skip_cleaning_object_files(&active_units);
914
915 /* MJIT worker thread is not inherited on fork. Start it for this child process. */
916 start_worker();
917 }
918
919 // Finish the threads processing units and creating PCH, finalize
920 // and free MJIT data. It should be called last during MJIT
921 // life.
922 //
923 // If close_handle_p is true, it calls dlclose() for JIT-ed code. So it should be false
924 // if the code can still be on stack. ...But it means to leak JIT-ed handle forever (FIXME).
925 void
926 mjit_finish(bool close_handle_p)
927 {
928 if (!mjit_enabled)
929 return;
930
931 // Wait for pch finish
932 verbose(2, "Stopping worker thread");
933 CRITICAL_SECTION_START(3, "in mjit_finish to wakeup from pch");
934 // As our threads are detached, we could just cancel them. But it
935 // is a bad idea because OS processes (C compiler) started by
936 // threads can produce temp files. And even if the temp files are
937 // removed, the used C compiler still complaint about their
938 // absence. So wait for a clean finish of the threads.
939 while (pch_status == PCH_NOT_READY) {
940 verbose(3, "Waiting wakeup from make_pch");
941 rb_native_cond_wait(&mjit_pch_wakeup, &mjit_engine_mutex);
942 }
943 CRITICAL_SECTION_FINISH(3, "in mjit_finish to wakeup from pch");
944
945 // Stop worker
946 stop_worker();
947
948 rb_native_mutex_destroy(&mjit_engine_mutex);
949 rb_native_cond_destroy(&mjit_pch_wakeup);
950 rb_native_cond_destroy(&mjit_client_wakeup);
951 rb_native_cond_destroy(&mjit_worker_wakeup);
952 rb_native_cond_destroy(&mjit_gc_wakeup);
953
954 #ifndef _MSC_VER // mswin has prebuilt precompiled header
955 if (!mjit_opts.save_temps && getpid() == pch_owner_pid)
956 remove_file(pch_file);
957
958 xfree(header_file); header_file = NULL;
959 #endif
960 xfree((void *)cc_common_args); cc_common_args = NULL;
961 for (char **flag = cc_added_args; *flag != NULL; flag++)
962 xfree(*flag);
963 xfree((void *)cc_added_args); cc_added_args = NULL;
964 xfree(tmp_dir); tmp_dir = NULL;
965 xfree(pch_file); pch_file = NULL;
966
967 mjit_call_p = false;
968 free_list(&unit_queue, close_handle_p);
969 free_list(&active_units, close_handle_p);
970 free_list(&compact_units, close_handle_p);
971 free_list(&stale_units, close_handle_p);
972 finish_conts();
973
974 mjit_enabled = false;
975 verbose(1, "Successful MJIT finish");
976 }
977
978 void
979 mjit_mark(void)
980 {
981 if (!mjit_enabled)
982 return;
983 RUBY_MARK_ENTER("mjit");
984
985 CRITICAL_SECTION_START(4, "mjit_mark");
986 VALUE iseq = (VALUE)mjit_copy_job.iseq;
987 CRITICAL_SECTION_FINISH(4, "mjit_mark");
988
989 // Don't wrap critical section with this. This may trigger GC,
990 // and in that case mjit_gc_start_hook causes deadlock.
991 if (iseq) rb_gc_mark(iseq);
992
993 struct rb_mjit_unit *unit = NULL;
994 CRITICAL_SECTION_START(4, "mjit_mark");
995 list_for_each(&unit_queue.head, unit, unode) {
996 if (unit->iseq) { // ISeq is still not GCed
997 iseq = (VALUE)unit->iseq;
998 CRITICAL_SECTION_FINISH(4, "mjit_mark rb_gc_mark");
999
1000 // Don't wrap critical section with this. This may trigger GC,
1001 // and in that case mjit_gc_start_hook causes deadlock.
1002 rb_gc_mark(iseq);
1003
1004 CRITICAL_SECTION_START(4, "mjit_mark rb_gc_mark");
1005 }
1006 }
1007 CRITICAL_SECTION_FINISH(4, "mjit_mark");
1008
1009 RUBY_MARK_LEAVE("mjit");
1010 }
1011
1012 // A hook to update valid_class_serials.
1013 void
1014 mjit_add_class_serial(rb_serial_t class_serial)
1015 {
1016 if (!mjit_enabled)
1017 return;
1018
1019 // Do not wrap CRITICAL_SECTION here. This function is only called in main thread
1020 // and guarded by GVL, and `rb_hash_aset` may cause GC and deadlock in it.
1021 rb_hash_aset(valid_class_serials, LONG2FIX(class_serial), Qtrue);
1022 }
1023
1024 // A hook to update valid_class_serials.
1025 void
1026 mjit_remove_class_serial(rb_serial_t class_serial)
1027 {
1028 if (!mjit_enabled)
1029 return;
1030
1031 CRITICAL_SECTION_START(3, "in mjit_remove_class_serial");
1032 rb_hash_delete_entry(valid_class_serials, LONG2FIX(class_serial));
1033 CRITICAL_SECTION_FINISH(3, "in mjit_remove_class_serial");
1034 }
1035
1036 #endif
rb_kwarg_call_data
Definition: vm_core.h:257
mjit_copy_job_t::cc_entries
struct rb_call_cache * cc_entries
Definition: mjit_worker.c:1120
UNLIKELY
#define UNLIKELY(x)
Definition: ffi_common.h:126
ID
unsigned long ID
Definition: ruby.h:103
void
void
Definition: rb_mjit_min_header-2.7.0.h:13273
rb_is_const_id
int rb_is_const_id(ID id)
Definition: symbol.c:854
constant.h
rb_path_check
int rb_path_check(const char *path)
Definition: file.c:6168
mjit_add_class_serial
void mjit_add_class_serial(rb_serial_t class_serial)
rb_mjit_iseq_compile_info
struct rb_mjit_compile_info * rb_mjit_iseq_compile_info(const struct rb_iseq_constant_body *body)
TRUE
#define TRUE
Definition: nkf.h:175
stat
Definition: rb_mjit_min_header-2.7.0.h:2384
rb_mjit_unit::compile_info
struct rb_mjit_compile_info compile_info
Definition: mjit_worker.c:148
mjit_cont_free
void mjit_cont_free(struct mjit_cont *cont)
rb_obj_hide
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition: object.c:78
RUBY_MARK_LEAVE
#define RUBY_MARK_LEAVE(msg)
Definition: gc.h:56
assert
#define assert(x)
Definition: dlmalloc.c:1176
FIX2INT
#define FIX2INT(x)
Definition: ruby.h:717
rb_iseq_struct
Definition: vm_core.h:456
rb_hash_new
VALUE rb_hash_new(void)
Definition: hash.c:1501
close
int close(int __fildes)
rb_gc_register_mark_object
void rb_gc_register_mark_object(VALUE obj)
Definition: gc.c:7063
path
VALUE path
Definition: rb_mjit_min_header-2.7.0.h:7351
rb_mjit_unit
Definition: mjit_worker.c:126
PCH_SUCCESS
@ PCH_SUCCESS
Definition: mjit_worker.c:232
rb_iseq_constant_body::jit_unit
struct rb_mjit_unit * jit_unit
Definition: rb_mjit_min_header-2.7.0.h:9595
strchr
char * strchr(char *, char)
mjit_remove_class_serial
void mjit_remove_class_serial(rb_serial_t class_serial)
S_ISVTX
#define S_ISVTX
Definition: rb_mjit_min_header-2.7.0.h:2419
stat
int stat(const char *__restrict __path, struct stat *__restrict __sbuf)
RSTRING_PTR
#define RSTRING_PTR(str)
Definition: ruby.h:1009
MJIT_TMP_PREFIX
#define MJIT_TMP_PREFIX
Definition: mjit_worker.c:123
rb_mjit_unit::unode
struct list_node unode
Definition: mjit_worker.c:146
mjit_init
void mjit_init(const struct mjit_options *opts)
VALUE
unsigned long VALUE
Definition: ruby.h:102
rb_mjit_unit::id
int id
Definition: mjit_worker.c:128
list_del
#define list_del(n)
Definition: rb_mjit_min_header-2.7.0.h:9041
rb_iseq_constant_body::jit_func
VALUE(* jit_func)(struct rb_execution_context_struct *, struct rb_control_frame_struct *)
Definition: rb_mjit_min_header-2.7.0.h:9592
ZALLOC
#define ZALLOC(type)
Definition: ruby.h:1666
RB_TYPE_P
#define RB_TYPE_P(obj, type)
Definition: ruby.h:560
rb_gc_location
VALUE rb_gc_location(VALUE value)
Definition: gc.c:8111
rb_const_entry_struct::value
VALUE value
Definition: constant.h:34
imemo_iseq
@ imemo_iseq
Definition: internal.h:1140
rb_id_table_iterator_result
rb_id_table_iterator_result
Definition: id_table.h:8
rb_iseq_path
VALUE rb_iseq_path(const rb_iseq_t *iseq)
Definition: iseq.c:1027
getenv
#define getenv(name)
Definition: win32.c:73
rb_iseq_constant_body::location
rb_iseq_location_t location
Definition: vm_core.h:399
RUBY_VM_NEXT_CONTROL_FRAME
#define RUBY_VM_NEXT_CONTROL_FRAME(cfp)
Definition: vm_core.h:1385
append_str2
#define append_str2(p, str, len)
Definition: mjit_worker.c:670
mjit_worker.c
rb_iseq_location_struct::first_lineno
VALUE first_lineno
Definition: vm_core.h:276
mjit_copy_job_t::iseq
const rb_iseq_t * iseq
Definition: mjit_worker.c:1119
Qundef
#define Qundef
Definition: ruby.h:470
mjit_copy_job_t::is_entries
union iseq_inline_storage_entry * is_entries
Definition: mjit_worker.c:1121
GET_EC
#define GET_EC()
Definition: vm_core.h:1766
RUBY_VM_CHECK_INTS
#define RUBY_VM_CHECK_INTS(ec)
Definition: vm_core.h:1862
NOT_COMPILED_JIT_ISEQ_FUNC
@ NOT_COMPILED_JIT_ISEQ_FUNC
Definition: rb_mjit_min_header-2.7.0.h:11721
mjit_update_references
void mjit_update_references(const rb_iseq_t *iseq)
Qfalse
#define Qfalse
Definition: ruby.h:467
uintptr_t
unsigned int uintptr_t
Definition: win32.h:106
mjit_finish
void mjit_finish(_Bool close_handle_p)
S_IWOTH
#define S_IWOTH
NULL
#define NULL
Definition: _sdbm.c:101
strlen
size_t strlen(const char *)
rb_thread_struct::ec
rb_execution_context_t * ec
Definition: vm_core.h:915
rb_iseq_constant_body
Definition: vm_core.h:311
strncmp
int strncmp(const char *, const char *, size_t)
MAXPATHLEN
#define MAXPATHLEN
Definition: dln.c:69
rb_mjit_unit::o_file
char * o_file
Definition: mjit_worker.c:134
ruby_archlibdir_path
VALUE ruby_archlibdir_path
Definition: ruby.c:579
access
int access(const char *__path, int __amode)
mjit_copy_job_t::finish_p
bool finish_p
Definition: mjit_worker.c:1122
v
int VALUE v
Definition: rb_mjit_min_header-2.7.0.h:12332
rb_mjit_unit::iseq
rb_iseq_t * iseq
Definition: mjit_worker.c:131
rb_hash_delete_entry
VALUE rb_hash_delete_entry(VALUE hash, VALUE key)
Definition: hash.c:2253
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2669
getuid
rb_uid_t getuid(void)
Definition: win32.c:2765
rb_call_cache
Definition: internal.h:2355
rb_execution_context_struct::cfp
rb_control_frame_t * cfp
Definition: vm_core.h:847
mjit_mark
void mjit_mark(void)
mjit_copy_job_t
Definition: mjit_worker.c:1118
mjit_options::wait
unsigned int wait
Definition: rb_mjit_min_header-2.7.0.h:11730
memcpy
void * memcpy(void *__restrict, const void *__restrict, size_t)
rb_native_cond_initialize
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
imemo_type
imemo_type
Definition: internal.h:1132
rb_id_table_foreach
void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
Definition: id_table.c:292
PCH_FAILED
@ PCH_FAILED
Definition: mjit_worker.c:232
mjit_cont_new
struct mjit_cont * mjit_cont_new(rb_execution_context_t *ec)
mjit_gc_exit_hook
void mjit_gc_exit_hook(void)
RCLASS_CONST_TBL
#define RCLASS_CONST_TBL(c)
Definition: internal.h:1067
rb_iseq_location_struct::label
VALUE label
Definition: vm_core.h:275
rb_iseq_constant_body::is_entries
union iseq_inline_storage_entry * is_entries
Definition: vm_core.h:420
ruby_prefix_path
VALUE ruby_prefix_path
Definition: ruby.c:579
rb_mjit_add_iseq_to_process
void rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq)
rb_mjit_unit::o_file_inherited_p
bool o_file_inherited_p
Definition: mjit_worker.c:138
rb_serial_t
unsigned long rb_serial_t
Definition: internal.h:1014
i
uint32_t i
Definition: rb_mjit_min_header-2.7.0.h:5464
list_for_each
#define list_for_each(h, i, member)
Definition: rb_mjit_min_header-2.7.0.h:9094
rb_vm_top_self
VALUE rb_vm_top_self(void)
Definition: vm.c:3345
MJIT_HEADER_INSTALL_DIR
#define MJIT_HEADER_INSTALL_DIR
Definition: mjit_config.h:7
rb_iseq_constant_body::ci_size
unsigned int ci_size
Definition: vm_core.h:437
mjit_options::debug_flags
char * debug_flags
Definition: rb_mjit_min_header-2.7.0.h:11729
rb_mjit_compile_info
Definition: rb_mjit_min_header-2.7.0.h:11735
RCLASS_SERIAL
#define RCLASS_SERIAL(c)
Definition: internal.h:1078
rb_mjit_unit::handle
void * handle
Definition: mjit_worker.c:130
mjit_options::save_temps
char save_temps
Definition: rb_mjit_min_header-2.7.0.h:11726
mjit_worker
void mjit_worker(void)
Definition: mjit_worker.c:1194
W_OK
#define W_OK
Definition: file.h:17
rb_call_data::cc
struct rb_call_cache cc
Definition: internal.h:2394
rb_execution_context_struct::vm_stack
VALUE * vm_stack
Definition: vm_core.h:845
T_CLASS
#define T_CLASS
Definition: ruby.h:524
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:920
rb_mjit_unit_list
Definition: mjit_worker.c:152
mjit_gc_start_hook
void mjit_gc_start_hook(void)
S_ISDIR
#define S_ISDIR(m)
Definition: dir.c:1923
rb_native_cond_destroy
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
mjit_free_iseq
void mjit_free_iseq(const rb_iseq_t *iseq)
mjit_enabled
#define mjit_enabled
Definition: internal.h:1760
rb_control_frame_struct
Definition: vm_core.h:760
rb_native_cond_broadcast
void rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
StringValuePtr
#define StringValuePtr(v)
Definition: ruby.h:603
size
int size
Definition: encoding.c:58
RTLD_DEFAULT
#define RTLD_DEFAULT
Definition: handle.c:291
FALSE
#define FALSE
Definition: nkf.h:174
list
struct rb_encoding_entry * list
Definition: encoding.c:56
NOT_ADDED_JIT_ISEQ_FUNC
@ NOT_ADDED_JIT_ISEQ_FUNC
Definition: rb_mjit_min_header-2.7.0.h:11719
LAST_JIT_ISEQ_FUNC
@ LAST_JIT_ISEQ_FUNC
Definition: rb_mjit_min_header-2.7.0.h:11722
PCH_NOT_READY
@ PCH_NOT_READY
Definition: mjit_worker.c:232
getpid
pid_t getpid(void)
confstr
size_t confstr(int __name, char *__buf, size_t __len)
key
key
Definition: openssl_missing.h:181
rb_mjit_recompile_iseq
void rb_mjit_recompile_iseq(const rb_iseq_t *iseq)
mjit_warning
mjit_warning(const char *format,...)
Definition: mjit_worker.c:322
mjit_pause
VALUE mjit_pause(_Bool wait_p)
NOT_READY_JIT_ISEQ_FUNC
@ NOT_READY_JIT_ISEQ_FUNC
Definition: rb_mjit_min_header-2.7.0.h:11720
timeval::tv_sec
time_t tv_sec
Definition: missing.h:54
CLASS_OF
#define CLASS_OF(v)
Definition: ruby.h:484
T_MODULE
#define T_MODULE
Definition: ruby.h:526
rb_cObject
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:2010
rb_cloexec_open
int rb_cloexec_open(const char *pathname, int flags, mode_t mode)
Definition: io.c:292
buf
unsigned char buf[MIME_BUF_SIZE]
Definition: nkf.c:4322
n
const char size_t n
Definition: rb_mjit_min_header-2.7.0.h:5456
mjit_child_after_fork
void mjit_child_after_fork(void)
RUBY_MARK_ENTER
#define RUBY_MARK_ENTER(msg)
Definition: gc.h:55
internal.h
rb_native_mutex_destroy
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
strdup
char * strdup(const char *) __attribute__((__malloc__)) __attribute__((__warn_unused_result__))
xmalloc
#define xmalloc
Definition: defines.h:211
timeval
Definition: missing.h:53
str
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
mjit_opts
struct mjit_options mjit_opts
Definition: mjit_worker.c:174
GET_THREAD
#define GET_THREAD()
Definition: vm_core.h:1765
rb_iseq_constant_body::total_calls
long unsigned total_calls
Definition: rb_mjit_min_header-2.7.0.h:9594
rb_control_frame_struct::iseq
const rb_iseq_t * iseq
Definition: vm_core.h:763
mjit_options::max_cache_size
int max_cache_size
Definition: rb_mjit_min_header-2.7.0.h:11733
rb_hash_aset
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Definition: hash.c:2779
rb_vm_struct::living_threads
struct list_head living_threads
Definition: vm_core.h:595
mjit_func_t
VALUE(* mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *)
Definition: rb_mjit_min_header-2.7.0.h:11740
rb_kwarg_call_data::cc
struct rb_call_cache cc
Definition: vm_core.h:258
st
enum ruby_tag_type st
Definition: rb_mjit_min_header-2.7.0.h:11111
free
#define free(x)
Definition: dln.c:52
rb_vm_struct
Definition: vm_core.h:576
xfree
#define xfree
Definition: defines.h:216
ruby_strdup
char * ruby_strdup(const char *)
Definition: util.c:527
rb_gc_mark
void rb_gc_mark(VALUE ptr)
Definition: gc.c:5212
rb_w32_system_tmpdir
UINT rb_w32_system_tmpdir(WCHAR *path, UINT len)
Definition: win32.c:515
Qtrue
#define Qtrue
Definition: ruby.h:468
PRELOADENV
#define PRELOADENV
Definition: mjit_config.h:17
len
uint8_t len
Definition: escape.c:17
mjit_options
Definition: rb_mjit_min_header-2.7.0.h:11724
rb_native_cond_wait
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
rb_iseq_struct::body
struct rb_iseq_constant_body * body
Definition: vm_core.h:460
rb_control_frame_struct::pc
const VALUE * pc
Definition: vm_core.h:761
LONG2FIX
#define LONG2FIX(i)
Definition: ruby.h:265
rb_thread_wait_for
void rb_thread_wait_for(struct timeval)
Definition: thread.c:1347
rb_iseq_constant_body::ci_kw_size
unsigned int ci_kw_size
Definition: vm_core.h:438
iseq
const rb_iseq_t * iseq
Definition: rb_mjit_min_header-2.7.0.h:13504
append_str
#define append_str(p, str)
Definition: mjit_worker.c:671
mjit_resume
VALUE mjit_resume(void)
rb_iseq_constant_body::call_data
struct rb_call_data * call_data
Definition: vm_core.h:421
numberof
#define numberof(array)
Definition: etc.c:618
rb_thread_struct
Definition: vm_core.h:910
unsetenv
int unsetenv(const char *)
rb_call_data
Definition: internal.h:2393
rb_mjit_wait_call
VALUE rb_mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body)
rb_iseq_constant_body::is_size
unsigned int is_size
Definition: vm_core.h:436
RSTRING_LEN
#define RSTRING_LEN(str)
Definition: ruby.h:1005
list_for_each_safe
#define list_for_each_safe(h, i, nxt, member)
Definition: rb_mjit_min_header-2.7.0.h:9097
rb_native_mutex_initialize
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
rb_mjit_unit::used_code_p
char used_code_p
Definition: mjit_worker.c:145
ID_TABLE_CONTINUE
@ ID_TABLE_CONTINUE
Definition: id_table.h:9
MJIT_MIN_HEADER_NAME
#define MJIT_MIN_HEADER_NAME
Definition: mjit_config.h:9
rb_const_entry_struct
Definition: constant.h:31
id_table.h
S_ISREG
#define S_ISREG(m)
cfp
rb_control_frame_t * cfp
Definition: rb_mjit_min_header-2.7.0.h:14544
iseq_inline_storage_entry
Definition: vm_core.h:231
verbose
verbose(int level, const char *format,...)
Definition: mjit_worker.c:303
mjit_call_p
bool mjit_call_p
Definition: mjit_worker.c:180
rb_execution_context_struct
Definition: vm_core.h:843
mjit_options::min_calls
unsigned int min_calls
Definition: rb_mjit_min_header-2.7.0.h:11731