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
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
125 {
126  if (!mjit_enabled)
127  return;
128 
129  CRITICAL_SECTION_START(4, "mjit_update_references");
130  if (iseq->body->jit_unit) {
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
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 *
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
303  && (iseq->body->jit_unit) != NULL) {
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 
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
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
435 {
436  mjit_wait(body);
438  return Qundef;
439  }
440  return body->jit_func(ec, ec->cfp);
441 }
442 
443 struct rb_mjit_compile_info*
445 {
446  assert(body->jit_unit != NULL);
447  return &body->jit_unit->compile_info;
448 }
449 
450 void
452 {
454  return;
455 
456  verbose(1, "JIT recompile: %s@%s:%d", RSTRING_PTR(iseq->body->location.label),
458 
459  CRITICAL_SECTION_START(3, "in rb_mjit_recompile_iseq");
460  remove_from_list(iseq->body->jit_unit, &active_units);
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 
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
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 {
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)) {
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);
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 {
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
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
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