mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Unify comment styles across MJIT sources
I'm writing `//` comments in newer MJIT code after C99 enablement (because I write 1-line comments more often than multi-line comments and `//` requires fewer chars on 1-line) and then they are mixed with `/* */` now. For consistency and to avoid the conversion in future changes, let me finish the rewrite in MJIT-related code. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67533 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
19513c88d5
commit
fa13bb1a6f
5 changed files with 321 additions and 327 deletions
231
mjit.c
231
mjit.c
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
/* Functions in this file are never executed on MJIT worker thread.
|
// Functions in this file are never executed on MJIT worker thread.
|
||||||
So you can safely use Ruby methods and GC in this file. */
|
// So you can safely use Ruby methods and GC in this file.
|
||||||
|
|
||||||
/* To share variables privately, include mjit_worker.c instead of linking. */
|
// To share variables privately, include mjit_worker.c instead of linking.
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
@ -20,12 +20,12 @@
|
||||||
#include "constant.h"
|
#include "constant.h"
|
||||||
#include "id_table.h"
|
#include "id_table.h"
|
||||||
|
|
||||||
/* Copy ISeq's states so that race condition does not happen on compilation. */
|
// Copy ISeq's states so that race condition does not happen on compilation.
|
||||||
static void
|
static void
|
||||||
mjit_copy_job_handler(void *data)
|
mjit_copy_job_handler(void *data)
|
||||||
{
|
{
|
||||||
mjit_copy_job_t *job = data;
|
mjit_copy_job_t *job = data;
|
||||||
if (stop_worker_p) { /* check if mutex is still alive, before calling CRITICAL_SECTION_START. */
|
if (stop_worker_p) { // check if mutex is still alive, before calling CRITICAL_SECTION_START.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +54,9 @@ mjit_copy_job_handler(void *data)
|
||||||
|
|
||||||
extern int rb_thread_create_mjit_thread(void (*worker_func)(void));
|
extern int rb_thread_create_mjit_thread(void (*worker_func)(void));
|
||||||
|
|
||||||
/* Return an unique file name in /tmp with PREFIX and SUFFIX and
|
// Return an unique file name in /tmp with PREFIX and SUFFIX and
|
||||||
number ID. Use getpid if ID == 0. The return file name exists
|
// number ID. Use getpid if ID == 0. The return file name exists
|
||||||
until the next function call. */
|
// until the next function call.
|
||||||
static char *
|
static char *
|
||||||
get_uniq_filename(unsigned long id, const char *prefix, const char *suffix)
|
get_uniq_filename(unsigned long id, const char *prefix, const char *suffix)
|
||||||
{
|
{
|
||||||
|
@ -74,8 +74,8 @@ get_uniq_filename(unsigned long id, const char *prefix, const char *suffix)
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait until workers don't compile any iseq. It is called at the
|
// Wait until workers don't compile any iseq. It is called at the
|
||||||
start of GC. */
|
// start of GC.
|
||||||
void
|
void
|
||||||
mjit_gc_start_hook(void)
|
mjit_gc_start_hook(void)
|
||||||
{
|
{
|
||||||
|
@ -91,8 +91,8 @@ mjit_gc_start_hook(void)
|
||||||
CRITICAL_SECTION_FINISH(4, "mjit_gc_start_hook");
|
CRITICAL_SECTION_FINISH(4, "mjit_gc_start_hook");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send a signal to workers to continue iseq compilations. It is
|
// Send a signal to workers to continue iseq compilations. It is
|
||||||
called at the end of GC. */
|
// called at the end of GC.
|
||||||
void
|
void
|
||||||
mjit_gc_finish_hook(void)
|
mjit_gc_finish_hook(void)
|
||||||
{
|
{
|
||||||
|
@ -105,8 +105,8 @@ mjit_gc_finish_hook(void)
|
||||||
CRITICAL_SECTION_FINISH(4, "mjit_gc_finish_hook");
|
CRITICAL_SECTION_FINISH(4, "mjit_gc_finish_hook");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Iseqs can be garbage collected. This function should call when it
|
// Iseqs can be garbage collected. This function should call when it
|
||||||
happens. It removes iseq from the unit. */
|
// happens. It removes iseq from the unit.
|
||||||
void
|
void
|
||||||
mjit_free_iseq(const rb_iseq_t *iseq)
|
mjit_free_iseq(const rb_iseq_t *iseq)
|
||||||
{
|
{
|
||||||
|
@ -114,16 +114,16 @@ mjit_free_iseq(const rb_iseq_t *iseq)
|
||||||
return;
|
return;
|
||||||
CRITICAL_SECTION_START(4, "mjit_free_iseq");
|
CRITICAL_SECTION_START(4, "mjit_free_iseq");
|
||||||
if (iseq->body->jit_unit) {
|
if (iseq->body->jit_unit) {
|
||||||
/* jit_unit is not freed here because it may be referred by multiple
|
// jit_unit is not freed here because it may be referred by multiple
|
||||||
lists of units. `get_from_list` and `mjit_finish` do the job. */
|
// lists of units. `get_from_list` and `mjit_finish` do the job.
|
||||||
iseq->body->jit_unit->iseq = NULL;
|
iseq->body->jit_unit->iseq = NULL;
|
||||||
}
|
}
|
||||||
CRITICAL_SECTION_FINISH(4, "mjit_free_iseq");
|
CRITICAL_SECTION_FINISH(4, "mjit_free_iseq");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free unit list. This should be called only when worker is finished
|
// Free unit list. This should be called only when worker is finished
|
||||||
because node of unit_queue and one of active_units may have the same unit
|
// because node of unit_queue and one of active_units may have the same unit
|
||||||
during proceeding unit. */
|
// during proceeding unit.
|
||||||
static void
|
static void
|
||||||
free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
|
free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
|
||||||
{
|
{
|
||||||
|
@ -137,18 +137,18 @@ free_list(struct rb_mjit_unit_list *list, bool close_handle_p)
|
||||||
list->length = 0;
|
list->length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MJIT info related to an existing continutaion. */
|
// MJIT info related to an existing continutaion.
|
||||||
struct mjit_cont {
|
struct mjit_cont {
|
||||||
rb_execution_context_t *ec; /* continuation ec */
|
rb_execution_context_t *ec; // continuation ec
|
||||||
struct mjit_cont *prev, *next; /* used to form lists */
|
struct mjit_cont *prev, *next; // used to form lists
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Double linked list of registered continuations. This is used to detect
|
// Double linked list of registered continuations. This is used to detect
|
||||||
units which are in use in unload_units. */
|
// units which are in use in unload_units.
|
||||||
static struct mjit_cont *first_cont;
|
static struct mjit_cont *first_cont;
|
||||||
|
|
||||||
/* Register a new continuation with thread TH. Return MJIT info about
|
// Register a new continuation with execution context `ec`. Return MJIT info about
|
||||||
the continuation. */
|
// the continuation.
|
||||||
struct mjit_cont *
|
struct mjit_cont *
|
||||||
mjit_cont_new(rb_execution_context_t *ec)
|
mjit_cont_new(rb_execution_context_t *ec)
|
||||||
{
|
{
|
||||||
|
@ -172,7 +172,7 @@ mjit_cont_new(rb_execution_context_t *ec)
|
||||||
return cont;
|
return cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unregister continuation CONT. */
|
// Unregister continuation `cont`.
|
||||||
void
|
void
|
||||||
mjit_cont_free(struct mjit_cont *cont)
|
mjit_cont_free(struct mjit_cont *cont)
|
||||||
{
|
{
|
||||||
|
@ -192,7 +192,7 @@ mjit_cont_free(struct mjit_cont *cont)
|
||||||
xfree(cont);
|
xfree(cont);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finish work with continuation info. */
|
// Finish work with continuation info.
|
||||||
static void
|
static void
|
||||||
finish_conts(void)
|
finish_conts(void)
|
||||||
{
|
{
|
||||||
|
@ -204,7 +204,7 @@ finish_conts(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create unit for ISEQ. */
|
// Create unit for `iseq`.
|
||||||
static void
|
static void
|
||||||
create_unit(const rb_iseq_t *iseq)
|
create_unit(const rb_iseq_t *iseq)
|
||||||
{
|
{
|
||||||
|
@ -219,7 +219,7 @@ create_unit(const rb_iseq_t *iseq)
|
||||||
iseq->body->jit_unit = unit;
|
iseq->body->jit_unit = unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up field used_code_p for unit iseqs whose iseq on the stack of ec. */
|
// Set up field `used_code_p` for unit iseqs whose iseq on the stack of ec.
|
||||||
static void
|
static void
|
||||||
mark_ec_units(rb_execution_context_t *ec)
|
mark_ec_units(rb_execution_context_t *ec)
|
||||||
{
|
{
|
||||||
|
@ -236,12 +236,12 @@ mark_ec_units(rb_execution_context_t *ec)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfp == ec->cfp)
|
if (cfp == ec->cfp)
|
||||||
break; /* reached the most recent cfp */
|
break; // reached the most recent cfp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unload JIT code of some units to satisfy the maximum permitted
|
// Unload JIT code of some units to satisfy the maximum permitted
|
||||||
number of units with a loaded code. */
|
// number of units with a loaded code.
|
||||||
static void
|
static void
|
||||||
unload_units(void)
|
unload_units(void)
|
||||||
{
|
{
|
||||||
|
@ -251,16 +251,16 @@ unload_units(void)
|
||||||
struct mjit_cont *cont;
|
struct mjit_cont *cont;
|
||||||
int delete_num, units_num = active_units.length;
|
int delete_num, units_num = active_units.length;
|
||||||
|
|
||||||
/* For now, we don't unload units when ISeq is GCed. We should
|
// For now, we don't unload units when ISeq is GCed. We should
|
||||||
unload such ISeqs first here. */
|
// unload such ISeqs first here.
|
||||||
list_for_each_safe(&active_units.head, unit, next, unode) {
|
list_for_each_safe(&active_units.head, unit, next, unode) {
|
||||||
if (unit->iseq == NULL) { /* ISeq is GCed. */
|
if (unit->iseq == NULL) { // ISeq is GCed.
|
||||||
remove_from_list(unit, &active_units);
|
remove_from_list(unit, &active_units);
|
||||||
free_unit(unit);
|
free_unit(unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detect units which are in use and can't be unloaded. */
|
// Detect units which are in use and can't be unloaded.
|
||||||
list_for_each(&active_units.head, unit, unode) {
|
list_for_each(&active_units.head, unit, unode) {
|
||||||
assert(unit->iseq != NULL && unit->handle != NULL);
|
assert(unit->iseq != NULL && unit->handle != NULL);
|
||||||
unit->used_code_p = FALSE;
|
unit->used_code_p = FALSE;
|
||||||
|
@ -272,15 +272,15 @@ unload_units(void)
|
||||||
mark_ec_units(cont->ec);
|
mark_ec_units(cont->ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove 1/10 units more to decrease unloading calls. */
|
// Remove 1/10 units more to decrease unloading calls.
|
||||||
/* TODO: Calculate max total_calls in unit_queue and don't unload units
|
// TODO: Calculate max total_calls in unit_queue and don't unload units
|
||||||
whose total_calls are larger than the max. */
|
// whose total_calls are larger than the max.
|
||||||
delete_num = active_units.length / 10;
|
delete_num = active_units.length / 10;
|
||||||
for (; active_units.length > mjit_opts.max_cache_size - delete_num;) {
|
for (; active_units.length > mjit_opts.max_cache_size - delete_num;) {
|
||||||
/* Find one unit that has the minimum total_calls. */
|
// Find one unit that has the minimum total_calls.
|
||||||
worst = NULL;
|
worst = NULL;
|
||||||
list_for_each(&active_units.head, unit, unode) {
|
list_for_each(&active_units.head, unit, unode) {
|
||||||
if (unit->used_code_p) /* We can't unload code on stack. */
|
if (unit->used_code_p) // We can't unload code on stack.
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (worst == NULL || worst->iseq->body->total_calls > unit->iseq->body->total_calls) {
|
if (worst == NULL || worst->iseq->body->total_calls > unit->iseq->body->total_calls) {
|
||||||
|
@ -290,7 +290,7 @@ unload_units(void)
|
||||||
if (worst == NULL)
|
if (worst == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Unload the worst node. */
|
// Unload the worst node.
|
||||||
verbose(2, "Unloading unit %d (calls=%lu)", worst->id, worst->iseq->body->total_calls);
|
verbose(2, "Unloading unit %d (calls=%lu)", worst->id, worst->iseq->body->total_calls);
|
||||||
assert(worst->handle != NULL);
|
assert(worst->handle != NULL);
|
||||||
remove_from_list(worst, &active_units);
|
remove_from_list(worst, &active_units);
|
||||||
|
@ -310,7 +310,7 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf
|
||||||
if (compile_info != NULL)
|
if (compile_info != NULL)
|
||||||
iseq->body->jit_unit->compile_info = *compile_info;
|
iseq->body->jit_unit->compile_info = *compile_info;
|
||||||
if (iseq->body->jit_unit == NULL)
|
if (iseq->body->jit_unit == NULL)
|
||||||
/* Failure in creating the unit. */
|
// Failure in creating the unit.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CRITICAL_SECTION_START(3, "in add_iseq_to_process");
|
CRITICAL_SECTION_START(3, "in add_iseq_to_process");
|
||||||
|
@ -323,15 +323,15 @@ mjit_add_iseq_to_process(const rb_iseq_t *iseq, const struct rb_mjit_compile_inf
|
||||||
CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process");
|
CRITICAL_SECTION_FINISH(3, "in add_iseq_to_process");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add ISEQ to be JITed in parallel with the current thread.
|
// Add ISEQ to be JITed in parallel with the current thread.
|
||||||
Unload some JIT codes if there are too many of them. */
|
// Unload some JIT codes if there are too many of them.
|
||||||
void
|
void
|
||||||
rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq)
|
rb_mjit_add_iseq_to_process(const rb_iseq_t *iseq)
|
||||||
{
|
{
|
||||||
mjit_add_iseq_to_process(iseq, NULL);
|
mjit_add_iseq_to_process(iseq, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For this timeout seconds, --jit-wait will wait for JIT compilation finish. */
|
// For this timeout seconds, --jit-wait will wait for JIT compilation finish.
|
||||||
#define MJIT_WAIT_TIMEOUT_SECONDS 60
|
#define MJIT_WAIT_TIMEOUT_SECONDS 60
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -345,7 +345,7 @@ mjit_wait(struct rb_iseq_constant_body *body)
|
||||||
tries++;
|
tries++;
|
||||||
if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS || pch_status == PCH_FAILED) {
|
if (tries / 1000 > MJIT_WAIT_TIMEOUT_SECONDS || pch_status == PCH_FAILED) {
|
||||||
CRITICAL_SECTION_START(3, "in mjit_wait_call to set jit_func");
|
CRITICAL_SECTION_START(3, "in mjit_wait_call to set jit_func");
|
||||||
body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; /* JIT worker seems dead. Give up. */
|
body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC; // JIT worker seems dead. Give up.
|
||||||
CRITICAL_SECTION_FINISH(3, "in mjit_wait_call to set jit_func");
|
CRITICAL_SECTION_FINISH(3, "in mjit_wait_call to set jit_func");
|
||||||
mjit_warning("timed out to wait for JIT finish");
|
mjit_warning("timed out to wait for JIT finish");
|
||||||
break;
|
break;
|
||||||
|
@ -358,8 +358,8 @@ mjit_wait(struct rb_iseq_constant_body *body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for JIT compilation finish for --jit-wait, and call the function pointer
|
// Wait for JIT compilation finish for --jit-wait, and call the function pointer
|
||||||
if the compiled result is not NOT_COMPILED_JIT_ISEQ_FUNC. */
|
// if the compiled result is not NOT_COMPILED_JIT_ISEQ_FUNC.
|
||||||
VALUE
|
VALUE
|
||||||
mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body)
|
mjit_wait_call(rb_execution_context_t *ec, struct rb_iseq_constant_body *body)
|
||||||
{
|
{
|
||||||
|
@ -406,7 +406,7 @@ init_header_filename(void)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
#ifdef LOAD_RELATIVE
|
#ifdef LOAD_RELATIVE
|
||||||
/* Root path of the running ruby process. Equal to RbConfig::TOPDIR. */
|
// Root path of the running ruby process. Equal to RbConfig::TOPDIR.
|
||||||
VALUE basedir_val;
|
VALUE basedir_val;
|
||||||
#endif
|
#endif
|
||||||
const char *basedir = NULL;
|
const char *basedir = NULL;
|
||||||
|
@ -429,9 +429,9 @@ init_header_filename(void)
|
||||||
baselen = RSTRING_LEN(basedir_val);
|
baselen = RSTRING_LEN(basedir_val);
|
||||||
#else
|
#else
|
||||||
if (getenv("MJIT_SEARCH_BUILD_DIR")) {
|
if (getenv("MJIT_SEARCH_BUILD_DIR")) {
|
||||||
/* This path is not intended to be used on production, but using build directory's
|
// This path is not intended to be used on production, but using build directory's
|
||||||
header file here because people want to run `make test-all` without running
|
// header file here because people want to run `make test-all` without running
|
||||||
`make install`. Don't use $MJIT_SEARCH_BUILD_DIR except for test-all. */
|
// `make install`. Don't use $MJIT_SEARCH_BUILD_DIR except for test-all.
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
const char *hdr = dlsym(RTLD_DEFAULT, "MJIT_HEADER");
|
const char *hdr = dlsym(RTLD_DEFAULT, "MJIT_HEADER");
|
||||||
|
@ -451,10 +451,10 @@ init_header_filename(void)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Do not pass PRELOADENV to child processes, on
|
// Do not pass PRELOADENV to child processes, on
|
||||||
* multi-arch environment */
|
// multi-arch environment
|
||||||
verbose(3, "PRELOADENV("PRELOADENV")=%s", getenv(PRELOADENV));
|
verbose(3, "PRELOADENV("PRELOADENV")=%s", getenv(PRELOADENV));
|
||||||
/* assume no other PRELOADENV in test-all */
|
// assume no other PRELOADENV in test-all
|
||||||
unsetenv(PRELOADENV);
|
unsetenv(PRELOADENV);
|
||||||
verbose(3, "MJIT_HEADER: %s", hdr);
|
verbose(3, "MJIT_HEADER: %s", hdr);
|
||||||
header_file = ruby_strdup(hdr);
|
header_file = ruby_strdup(hdr);
|
||||||
|
@ -465,7 +465,7 @@ init_header_filename(void)
|
||||||
#endif
|
#endif
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
{
|
{
|
||||||
/* A name of the header file included in any C file generated by MJIT for iseqs. */
|
// A name of the header file included in any C file generated by MJIT for iseqs.
|
||||||
static const char header_name[] = MJIT_HEADER_INSTALL_DIR "/" MJIT_MIN_HEADER_NAME;
|
static const char header_name[] = MJIT_HEADER_INSTALL_DIR "/" MJIT_MIN_HEADER_NAME;
|
||||||
const size_t header_name_len = sizeof(header_name) - 1;
|
const size_t header_name_len = sizeof(header_name) - 1;
|
||||||
|
|
||||||
|
@ -534,7 +534,7 @@ UINT rb_w32_system_tmpdir(WCHAR *path, UINT len);
|
||||||
static char *
|
static char *
|
||||||
system_default_tmpdir(void)
|
system_default_tmpdir(void)
|
||||||
{
|
{
|
||||||
/* c.f. ext/etc/etc.c:etc_systmpdir() */
|
// c.f. ext/etc/etc.c:etc_systmpdir()
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WCHAR tmppath[_MAX_PATH];
|
WCHAR tmppath[_MAX_PATH];
|
||||||
UINT len = rb_w32_system_tmpdir(tmppath, numberof(tmppath));
|
UINT len = rb_w32_system_tmpdir(tmppath, numberof(tmppath));
|
||||||
|
@ -610,7 +610,7 @@ system_tmpdir(void)
|
||||||
// A default threshold used to add iseq to JIT.
|
// A default threshold used to add iseq to JIT.
|
||||||
#define DEFAULT_MIN_CALLS_TO_ADD 10000
|
#define DEFAULT_MIN_CALLS_TO_ADD 10000
|
||||||
|
|
||||||
/* Start MJIT worker. Return TRUE if worker is successfully started. */
|
// Start MJIT worker. Return TRUE if worker is successfully started.
|
||||||
static bool
|
static bool
|
||||||
start_worker(void)
|
start_worker(void)
|
||||||
{
|
{
|
||||||
|
@ -630,9 +630,9 @@ start_worker(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize MJIT. Start a thread creating the precompiled header and
|
// Initialize MJIT. Start a thread creating the precompiled header and
|
||||||
processing ISeqs. The function should be called first for using MJIT.
|
// processing ISeqs. The function should be called first for using MJIT.
|
||||||
If everything is successful, MJIT_INIT_P will be TRUE. */
|
// If everything is successful, MJIT_INIT_P will be TRUE.
|
||||||
void
|
void
|
||||||
mjit_init(struct mjit_options *opts)
|
mjit_init(struct mjit_options *opts)
|
||||||
{
|
{
|
||||||
|
@ -640,7 +640,7 @@ mjit_init(struct mjit_options *opts)
|
||||||
mjit_enabled = true;
|
mjit_enabled = true;
|
||||||
mjit_call_p = true;
|
mjit_call_p = true;
|
||||||
|
|
||||||
/* Normalize options */
|
// Normalize options
|
||||||
if (mjit_opts.min_calls == 0)
|
if (mjit_opts.min_calls == 0)
|
||||||
mjit_opts.min_calls = DEFAULT_MIN_CALLS_TO_ADD;
|
mjit_opts.min_calls = DEFAULT_MIN_CALLS_TO_ADD;
|
||||||
if (mjit_opts.max_cache_size <= 0)
|
if (mjit_opts.max_cache_size <= 0)
|
||||||
|
@ -648,9 +648,9 @@ mjit_init(struct mjit_options *opts)
|
||||||
if (mjit_opts.max_cache_size < MIN_CACHE_SIZE)
|
if (mjit_opts.max_cache_size < MIN_CACHE_SIZE)
|
||||||
mjit_opts.max_cache_size = MIN_CACHE_SIZE;
|
mjit_opts.max_cache_size = MIN_CACHE_SIZE;
|
||||||
|
|
||||||
/* Initialize variables for compilation */
|
// Initialize variables for compilation
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
pch_status = PCH_SUCCESS; /* has prebuilt precompiled header */
|
pch_status = PCH_SUCCESS; // has prebuilt precompiled header
|
||||||
#else
|
#else
|
||||||
pch_status = PCH_NOT_READY;
|
pch_status = PCH_NOT_READY;
|
||||||
#endif
|
#endif
|
||||||
|
@ -659,11 +659,11 @@ mjit_init(struct mjit_options *opts)
|
||||||
cc_common_args = xmalloc(sizeof(CC_COMMON_ARGS));
|
cc_common_args = xmalloc(sizeof(CC_COMMON_ARGS));
|
||||||
memcpy((void *)cc_common_args, CC_COMMON_ARGS, sizeof(CC_COMMON_ARGS));
|
memcpy((void *)cc_common_args, CC_COMMON_ARGS, sizeof(CC_COMMON_ARGS));
|
||||||
#if MJIT_CFLAGS_PIPE
|
#if MJIT_CFLAGS_PIPE
|
||||||
{ /* eliminate a flag incompatible with `-pipe` */
|
{ // eliminate a flag incompatible with `-pipe`
|
||||||
size_t i, j;
|
size_t i, j;
|
||||||
for (i = 0, j = 0; i < sizeof(CC_COMMON_ARGS) / sizeof(char *); i++) {
|
for (i = 0, j = 0; i < sizeof(CC_COMMON_ARGS) / sizeof(char *); i++) {
|
||||||
if (CC_COMMON_ARGS[i] && strncmp("-save-temps", CC_COMMON_ARGS[i], strlen("-save-temps")) == 0)
|
if (CC_COMMON_ARGS[i] && strncmp("-save-temps", CC_COMMON_ARGS[i], strlen("-save-temps")) == 0)
|
||||||
continue; /* skip -save-temps flag */
|
continue; // skip -save-temps flag
|
||||||
cc_common_args[j] = CC_COMMON_ARGS[i];
|
cc_common_args[j] = CC_COMMON_ARGS[i];
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
@ -680,14 +680,14 @@ mjit_init(struct mjit_options *opts)
|
||||||
}
|
}
|
||||||
pch_owner_pid = getpid();
|
pch_owner_pid = getpid();
|
||||||
|
|
||||||
/* Initialize mutex */
|
// Initialize mutex
|
||||||
rb_native_mutex_initialize(&mjit_engine_mutex);
|
rb_native_mutex_initialize(&mjit_engine_mutex);
|
||||||
rb_native_cond_initialize(&mjit_pch_wakeup);
|
rb_native_cond_initialize(&mjit_pch_wakeup);
|
||||||
rb_native_cond_initialize(&mjit_client_wakeup);
|
rb_native_cond_initialize(&mjit_client_wakeup);
|
||||||
rb_native_cond_initialize(&mjit_worker_wakeup);
|
rb_native_cond_initialize(&mjit_worker_wakeup);
|
||||||
rb_native_cond_initialize(&mjit_gc_wakeup);
|
rb_native_cond_initialize(&mjit_gc_wakeup);
|
||||||
|
|
||||||
/* Initialize class_serials cache for compilation */
|
// Initialize class_serials cache for compilation
|
||||||
valid_class_serials = rb_hash_new();
|
valid_class_serials = rb_hash_new();
|
||||||
rb_obj_hide(valid_class_serials);
|
rb_obj_hide(valid_class_serials);
|
||||||
rb_gc_register_mark_object(valid_class_serials);
|
rb_gc_register_mark_object(valid_class_serials);
|
||||||
|
@ -697,7 +697,7 @@ mjit_init(struct mjit_options *opts)
|
||||||
rb_id_table_foreach(RCLASS_CONST_TBL(rb_cObject), valid_class_serials_add_i, NULL);
|
rb_id_table_foreach(RCLASS_CONST_TBL(rb_cObject), valid_class_serials_add_i, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize worker thread */
|
// Initialize worker thread
|
||||||
start_worker();
|
start_worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +716,7 @@ stop_worker(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stop JIT-compiling methods but compiled code is kept available. */
|
// Stop JIT-compiling methods but compiled code is kept available.
|
||||||
VALUE
|
VALUE
|
||||||
mjit_pause(bool wait_p)
|
mjit_pause(bool wait_p)
|
||||||
{
|
{
|
||||||
|
@ -727,13 +727,13 @@ mjit_pause(bool wait_p)
|
||||||
return Qfalse;
|
return Qfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flush all queued units with no option or `wait: true` */
|
// Flush all queued units with no option or `wait: true`
|
||||||
if (wait_p) {
|
if (wait_p) {
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = 1000;
|
tv.tv_usec = 1000;
|
||||||
|
|
||||||
while (unit_queue.length > 0 && active_units.length < mjit_opts.max_cache_size) { /* inverse of condition that waits for mjit_worker_wakeup */
|
while (unit_queue.length > 0 && active_units.length < mjit_opts.max_cache_size) { // inverse of condition that waits for mjit_worker_wakeup
|
||||||
CRITICAL_SECTION_START(3, "in mjit_pause for a worker wakeup");
|
CRITICAL_SECTION_START(3, "in mjit_pause for a worker wakeup");
|
||||||
rb_native_cond_broadcast(&mjit_worker_wakeup);
|
rb_native_cond_broadcast(&mjit_worker_wakeup);
|
||||||
CRITICAL_SECTION_FINISH(3, "in mjit_pause for a worker wakeup");
|
CRITICAL_SECTION_FINISH(3, "in mjit_pause for a worker wakeup");
|
||||||
|
@ -745,7 +745,7 @@ mjit_pause(bool wait_p)
|
||||||
return Qtrue;
|
return Qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restart JIT-compiling methods after mjit_pause. */
|
// Restart JIT-compiling methods after mjit_pause.
|
||||||
VALUE
|
VALUE
|
||||||
mjit_resume(void)
|
mjit_resume(void)
|
||||||
{
|
{
|
||||||
|
@ -762,40 +762,39 @@ mjit_resume(void)
|
||||||
return Qtrue;
|
return Qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Skip calling `clean_object_files` for units which currently exist in the list. */
|
// Skip calling `clean_object_files` for units which currently exist in the list.
|
||||||
static void
|
static void
|
||||||
skip_cleaning_object_files(struct rb_mjit_unit_list *list)
|
skip_cleaning_object_files(struct rb_mjit_unit_list *list)
|
||||||
{
|
{
|
||||||
struct rb_mjit_unit *unit = NULL, *next;
|
struct rb_mjit_unit *unit = NULL, *next;
|
||||||
|
|
||||||
/* No mutex for list, assuming MJIT worker does not exist yet since it's immediately after fork. */
|
// No mutex for list, assuming MJIT worker does not exist yet since it's immediately after fork.
|
||||||
list_for_each_safe(&list->head, unit, next, unode) {
|
list_for_each_safe(&list->head, unit, next, unode) {
|
||||||
#ifndef _MSC_VER /* Actually mswin does not reach here since it doesn't have fork */
|
#ifndef _MSC_VER // Actually mswin does not reach here since it doesn't have fork
|
||||||
if (unit->o_file) unit->o_file_inherited_p = true;
|
if (unit->o_file) unit->o_file_inherited_p = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32) /* mswin doesn't reach here either. This is for MinGW. */
|
#if defined(_WIN32) // mswin doesn't reach here either. This is for MinGW.
|
||||||
if (unit->so_file) unit->so_file = NULL;
|
if (unit->so_file) unit->so_file = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is called after fork initiated by Ruby's method to launch MJIT worker thread
|
// This is called after fork initiated by Ruby's method to launch MJIT worker thread
|
||||||
for child Ruby process.
|
// for child Ruby process.
|
||||||
|
//
|
||||||
In multi-process Ruby applications, child Ruby processes do most of the jobs.
|
// In multi-process Ruby applications, child Ruby processes do most of the jobs.
|
||||||
Thus we want child Ruby processes to enqueue ISeqs to MJIT worker's queue and
|
// Thus we want child Ruby processes to enqueue ISeqs to MJIT worker's queue and
|
||||||
call the JIT-ed code.
|
// call the JIT-ed code.
|
||||||
|
//
|
||||||
But unfortunately current MJIT-generated code is process-specific. After the fork,
|
// But unfortunately current MJIT-generated code is process-specific. After the fork,
|
||||||
JIT-ed code created by parent Ruby process cannot be used in child Ruby process
|
// JIT-ed code created by parent Ruby process cannot be used in child Ruby process
|
||||||
because the code could rely on inline cache values (ivar's IC, send's CC) which
|
// because the code could rely on inline cache values (ivar's IC, send's CC) which
|
||||||
may vary between processes after fork or embed some process-specific addresses.
|
// may vary between processes after fork or embed some process-specific addresses.
|
||||||
|
//
|
||||||
So child Ruby process can't request parent process to JIT an ISeq and use the code.
|
// So child Ruby process can't request parent process to JIT an ISeq and use the code.
|
||||||
Instead of that, MJIT worker thread is created for all child Ruby processes, even
|
// Instead of that, MJIT worker thread is created for all child Ruby processes, even
|
||||||
while child processes would end up with compiling the same ISeqs.
|
// while child processes would end up with compiling the same ISeqs.
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
mjit_child_after_fork(void)
|
mjit_child_after_fork(void)
|
||||||
{
|
{
|
||||||
|
@ -810,33 +809,33 @@ mjit_child_after_fork(void)
|
||||||
start_worker();
|
start_worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finish the threads processing units and creating PCH, finalize
|
// Finish the threads processing units and creating PCH, finalize
|
||||||
and free MJIT data. It should be called last during MJIT
|
// and free MJIT data. It should be called last during MJIT
|
||||||
life.
|
// life.
|
||||||
|
//
|
||||||
If close_handle_p is true, it calls dlclose() for JIT-ed code. So it should be false
|
// If close_handle_p is true, it calls dlclose() for JIT-ed code. So it should be false
|
||||||
if the code can still be on stack. ...But it means to leak JIT-ed handle forever (FIXME). */
|
// if the code can still be on stack. ...But it means to leak JIT-ed handle forever (FIXME).
|
||||||
void
|
void
|
||||||
mjit_finish(bool close_handle_p)
|
mjit_finish(bool close_handle_p)
|
||||||
{
|
{
|
||||||
if (!mjit_enabled)
|
if (!mjit_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Wait for pch finish */
|
// Wait for pch finish
|
||||||
verbose(2, "Stopping worker thread");
|
verbose(2, "Stopping worker thread");
|
||||||
CRITICAL_SECTION_START(3, "in mjit_finish to wakeup from pch");
|
CRITICAL_SECTION_START(3, "in mjit_finish to wakeup from pch");
|
||||||
/* As our threads are detached, we could just cancel them. But it
|
// As our threads are detached, we could just cancel them. But it
|
||||||
is a bad idea because OS processes (C compiler) started by
|
// is a bad idea because OS processes (C compiler) started by
|
||||||
threads can produce temp files. And even if the temp files are
|
// threads can produce temp files. And even if the temp files are
|
||||||
removed, the used C compiler still complaint about their
|
// removed, the used C compiler still complaint about their
|
||||||
absence. So wait for a clean finish of the threads. */
|
// absence. So wait for a clean finish of the threads.
|
||||||
while (pch_status == PCH_NOT_READY) {
|
while (pch_status == PCH_NOT_READY) {
|
||||||
verbose(3, "Waiting wakeup from make_pch");
|
verbose(3, "Waiting wakeup from make_pch");
|
||||||
rb_native_cond_wait(&mjit_pch_wakeup, &mjit_engine_mutex);
|
rb_native_cond_wait(&mjit_pch_wakeup, &mjit_engine_mutex);
|
||||||
}
|
}
|
||||||
CRITICAL_SECTION_FINISH(3, "in mjit_finish to wakeup from pch");
|
CRITICAL_SECTION_FINISH(3, "in mjit_finish to wakeup from pch");
|
||||||
|
|
||||||
/* Stop worker */
|
// Stop worker
|
||||||
stop_worker();
|
stop_worker();
|
||||||
|
|
||||||
rb_native_mutex_destroy(&mjit_engine_mutex);
|
rb_native_mutex_destroy(&mjit_engine_mutex);
|
||||||
|
@ -845,7 +844,7 @@ mjit_finish(bool close_handle_p)
|
||||||
rb_native_cond_destroy(&mjit_worker_wakeup);
|
rb_native_cond_destroy(&mjit_worker_wakeup);
|
||||||
rb_native_cond_destroy(&mjit_gc_wakeup);
|
rb_native_cond_destroy(&mjit_gc_wakeup);
|
||||||
|
|
||||||
#ifndef _MSC_VER /* mswin has prebuilt precompiled header */
|
#ifndef _MSC_VER // mswin has prebuilt precompiled header
|
||||||
if (!mjit_opts.save_temps && getpid() == pch_owner_pid)
|
if (!mjit_opts.save_temps && getpid() == pch_owner_pid)
|
||||||
remove_file(pch_file);
|
remove_file(pch_file);
|
||||||
|
|
||||||
|
@ -884,12 +883,12 @@ mjit_mark(void)
|
||||||
struct rb_mjit_unit *unit = NULL;
|
struct rb_mjit_unit *unit = NULL;
|
||||||
CRITICAL_SECTION_START(4, "mjit_mark");
|
CRITICAL_SECTION_START(4, "mjit_mark");
|
||||||
list_for_each(&unit_queue.head, unit, unode) {
|
list_for_each(&unit_queue.head, unit, unode) {
|
||||||
if (unit->iseq) { /* ISeq is still not GCed */
|
if (unit->iseq) { // ISeq is still not GCed
|
||||||
iseq = (VALUE)unit->iseq;
|
iseq = (VALUE)unit->iseq;
|
||||||
CRITICAL_SECTION_FINISH(4, "mjit_mark rb_gc_mark");
|
CRITICAL_SECTION_FINISH(4, "mjit_mark rb_gc_mark");
|
||||||
|
|
||||||
/* Don't wrap critical section with this. This may trigger GC,
|
// Don't wrap critical section with this. This may trigger GC,
|
||||||
and in that case mjit_gc_start_hook causes deadlock. */
|
// and in that case mjit_gc_start_hook causes deadlock.
|
||||||
rb_gc_mark(iseq);
|
rb_gc_mark(iseq);
|
||||||
|
|
||||||
CRITICAL_SECTION_START(4, "mjit_mark rb_gc_mark");
|
CRITICAL_SECTION_START(4, "mjit_mark rb_gc_mark");
|
||||||
|
@ -900,19 +899,19 @@ mjit_mark(void)
|
||||||
RUBY_MARK_LEAVE("mjit");
|
RUBY_MARK_LEAVE("mjit");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A hook to update valid_class_serials. */
|
// A hook to update valid_class_serials.
|
||||||
void
|
void
|
||||||
mjit_add_class_serial(rb_serial_t class_serial)
|
mjit_add_class_serial(rb_serial_t class_serial)
|
||||||
{
|
{
|
||||||
if (!mjit_enabled)
|
if (!mjit_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Do not wrap CRITICAL_SECTION here. This function is only called in main thread
|
// Do not wrap CRITICAL_SECTION here. This function is only called in main thread
|
||||||
and guarded by GVL, and `rb_hash_aset` may cause GC and deadlock in it. */
|
// and guarded by GVL, and `rb_hash_aset` may cause GC and deadlock in it.
|
||||||
rb_hash_aset(valid_class_serials, LONG2FIX(class_serial), Qtrue);
|
rb_hash_aset(valid_class_serials, LONG2FIX(class_serial), Qtrue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A hook to update valid_class_serials. */
|
// A hook to update valid_class_serials.
|
||||||
void
|
void
|
||||||
mjit_remove_class_serial(rb_serial_t class_serial)
|
mjit_remove_class_serial(rb_serial_t class_serial)
|
||||||
{
|
{
|
||||||
|
|
64
mjit.h
64
mjit.h
|
@ -14,44 +14,44 @@
|
||||||
|
|
||||||
#if USE_MJIT
|
#if USE_MJIT
|
||||||
|
|
||||||
/* Special address values of a function generated from the
|
// Special address values of a function generated from the
|
||||||
corresponding iseq by MJIT: */
|
// corresponding iseq by MJIT:
|
||||||
enum rb_mjit_iseq_func {
|
enum rb_mjit_iseq_func {
|
||||||
/* ISEQ was not queued yet for the machine code generation */
|
// ISEQ was not queued yet for the machine code generation
|
||||||
NOT_ADDED_JIT_ISEQ_FUNC = 0,
|
NOT_ADDED_JIT_ISEQ_FUNC = 0,
|
||||||
/* ISEQ is already queued for the machine code generation but the
|
// ISEQ is already queued for the machine code generation but the
|
||||||
code is not ready yet for the execution */
|
// code is not ready yet for the execution
|
||||||
NOT_READY_JIT_ISEQ_FUNC = 1,
|
NOT_READY_JIT_ISEQ_FUNC = 1,
|
||||||
/* ISEQ included not compilable insn, some internal assertion failed
|
// ISEQ included not compilable insn, some internal assertion failed
|
||||||
or the unit is unloaded */
|
// or the unit is unloaded
|
||||||
NOT_COMPILED_JIT_ISEQ_FUNC = 2,
|
NOT_COMPILED_JIT_ISEQ_FUNC = 2,
|
||||||
/* End mark */
|
// End mark
|
||||||
LAST_JIT_ISEQ_FUNC = 3
|
LAST_JIT_ISEQ_FUNC = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
/* MJIT options which can be defined on the MRI command line. */
|
// MJIT options which can be defined on the MRI command line.
|
||||||
struct mjit_options {
|
struct mjit_options {
|
||||||
/* Converted from "jit" feature flag to tell the enablement
|
// Converted from "jit" feature flag to tell the enablement
|
||||||
information to ruby_show_version(). */
|
// information to ruby_show_version().
|
||||||
char on;
|
char on;
|
||||||
/* Save temporary files after MRI finish. The temporary files
|
// Save temporary files after MRI finish. The temporary files
|
||||||
include the pre-compiled header, C code file generated for ISEQ,
|
// include the pre-compiled header, C code file generated for ISEQ,
|
||||||
and the corresponding object file. */
|
// and the corresponding object file.
|
||||||
char save_temps;
|
char save_temps;
|
||||||
/* Print MJIT warnings to stderr. */
|
// Print MJIT warnings to stderr.
|
||||||
char warnings;
|
char warnings;
|
||||||
/* Disable compiler optimization and add debug symbols. It can be
|
// Disable compiler optimization and add debug symbols. It can be
|
||||||
very slow. */
|
// very slow.
|
||||||
char debug;
|
char debug;
|
||||||
/* If not 0, all ISeqs are synchronously compiled. For testing. */
|
// If not 0, all ISeqs are synchronously compiled. For testing.
|
||||||
unsigned int wait;
|
unsigned int wait;
|
||||||
/* Number of calls to trigger JIT compilation. For testing. */
|
// Number of calls to trigger JIT compilation. For testing.
|
||||||
unsigned int min_calls;
|
unsigned int min_calls;
|
||||||
/* Force printing info about MJIT work of level VERBOSE or
|
// Force printing info about MJIT work of level VERBOSE or
|
||||||
less. 0=silence, 1=medium, 2=verbose. */
|
// less. 0=silence, 1=medium, 2=verbose.
|
||||||
int verbose;
|
int verbose;
|
||||||
/* Maximal permitted number of iseq JIT codes in a MJIT memory
|
// Maximal permitted number of iseq JIT codes in a MJIT memory
|
||||||
cache. */
|
// cache.
|
||||||
int max_cache_size;
|
int max_cache_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,11 +88,11 @@ extern void mjit_cont_free(struct mjit_cont *cont);
|
||||||
extern void mjit_add_class_serial(rb_serial_t class_serial);
|
extern void mjit_add_class_serial(rb_serial_t class_serial);
|
||||||
extern void mjit_remove_class_serial(rb_serial_t class_serial);
|
extern void mjit_remove_class_serial(rb_serial_t class_serial);
|
||||||
|
|
||||||
/* A threshold used to reject long iseqs from JITting as such iseqs
|
// A threshold used to reject long iseqs from JITting as such iseqs
|
||||||
takes too much time to be compiled. */
|
// takes too much time to be compiled.
|
||||||
#define JIT_ISEQ_SIZE_THRESHOLD 1000
|
#define JIT_ISEQ_SIZE_THRESHOLD 1000
|
||||||
|
|
||||||
/* Return TRUE if given ISeq body should be compiled by MJIT */
|
// Return TRUE if given ISeq body should be compiled by MJIT
|
||||||
static inline int
|
static inline int
|
||||||
mjit_target_iseq_p(struct rb_iseq_constant_body *body)
|
mjit_target_iseq_p(struct rb_iseq_constant_body *body)
|
||||||
{
|
{
|
||||||
|
@ -100,8 +100,8 @@ mjit_target_iseq_p(struct rb_iseq_constant_body *body)
|
||||||
&& body->iseq_size < JIT_ISEQ_SIZE_THRESHOLD;
|
&& body->iseq_size < JIT_ISEQ_SIZE_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to execute the current iseq in ec. Use JIT code if it is ready.
|
// Try to execute the current iseq in ec. Use JIT code if it is ready.
|
||||||
If it is not, add ISEQ to the compilation queue and return Qundef. */
|
// If it is not, add ISEQ to the compilation queue and return Qundef.
|
||||||
static inline VALUE
|
static inline VALUE
|
||||||
mjit_exec(rb_execution_context_t *ec)
|
mjit_exec(rb_execution_context_t *ec)
|
||||||
{
|
{
|
||||||
|
@ -142,7 +142,7 @@ mjit_exec(rb_execution_context_t *ec)
|
||||||
case NOT_COMPILED_JIT_ISEQ_FUNC:
|
case NOT_COMPILED_JIT_ISEQ_FUNC:
|
||||||
RB_DEBUG_COUNTER_INC(mjit_exec_not_compiled);
|
RB_DEBUG_COUNTER_INC(mjit_exec_not_compiled);
|
||||||
return Qundef;
|
return Qundef;
|
||||||
default: /* to avoid warning with LAST_JIT_ISEQ_FUNC */
|
default: // to avoid warning with LAST_JIT_ISEQ_FUNC
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ mjit_exec(rb_execution_context_t *ec)
|
||||||
|
|
||||||
void mjit_child_after_fork(void);
|
void mjit_child_after_fork(void);
|
||||||
|
|
||||||
#else /* USE_MJIT */
|
#else // USE_MJIT
|
||||||
static inline struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec){return NULL;}
|
static inline struct mjit_cont *mjit_cont_new(rb_execution_context_t *ec){return NULL;}
|
||||||
static inline void mjit_cont_free(struct mjit_cont *cont){}
|
static inline void mjit_cont_free(struct mjit_cont *cont){}
|
||||||
static inline void mjit_postponed_job_register_start_hook(void){}
|
static inline void mjit_postponed_job_register_start_hook(void){}
|
||||||
|
@ -172,5 +172,5 @@ static inline void mjit_remove_class_serial(rb_serial_t class_serial){}
|
||||||
static inline VALUE mjit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
|
static inline VALUE mjit_exec(rb_execution_context_t *ec) { return Qundef; /* unreachable */ }
|
||||||
static inline void mjit_child_after_fork(void){}
|
static inline void mjit_child_after_fork(void){}
|
||||||
|
|
||||||
#endif /* USE_MJIT */
|
#endif // USE_MJIT
|
||||||
#endif /* RUBY_MJIT_H */
|
#endif // RUBY_MJIT_H
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
/* NOTE: All functions in this file are executed on MJIT worker. So don't
|
// NOTE: All functions in this file are executed on MJIT worker. So don't
|
||||||
call Ruby methods (C functions that may call rb_funcall) or trigger
|
// call Ruby methods (C functions that may call rb_funcall) or trigger
|
||||||
GC (using ZALLOC, xmalloc, xfree, etc.) in this file. */
|
// GC (using ZALLOC, xmalloc, xfree, etc.) in this file.
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
@ -21,13 +21,13 @@
|
||||||
#include "insns_info.inc"
|
#include "insns_info.inc"
|
||||||
#include "vm_insnhelper.h"
|
#include "vm_insnhelper.h"
|
||||||
|
|
||||||
/* Macros to check if a position is already compiled using compile_status.stack_size_for_pos */
|
// Macros to check if a position is already compiled using compile_status.stack_size_for_pos
|
||||||
#define NOT_COMPILED_STACK_SIZE -1
|
#define NOT_COMPILED_STACK_SIZE -1
|
||||||
#define ALREADY_COMPILED_P(status, pos) (status->stack_size_for_pos[pos] != NOT_COMPILED_STACK_SIZE)
|
#define ALREADY_COMPILED_P(status, pos) (status->stack_size_for_pos[pos] != NOT_COMPILED_STACK_SIZE)
|
||||||
|
|
||||||
/* Storage to keep compiler's status. This should have information
|
// Storage to keep compiler's status. This should have information
|
||||||
which is global during one `mjit_compile` call. Ones conditional
|
// which is global during one `mjit_compile` call. Ones conditional
|
||||||
in each branch should be stored in `compile_branch`. */
|
// in each branch should be stored in `compile_branch`.
|
||||||
struct compile_status {
|
struct compile_status {
|
||||||
bool success; // has true if compilation has had no issue
|
bool success; // has true if compilation has had no issue
|
||||||
int *stack_size_for_pos; // stack_size_for_pos[pos] has stack size for the position (otherwise -1)
|
int *stack_size_for_pos; // stack_size_for_pos[pos] has stack size for the position (otherwise -1)
|
||||||
|
@ -41,9 +41,9 @@ struct compile_status {
|
||||||
struct rb_mjit_compile_info *compile_info;
|
struct rb_mjit_compile_info *compile_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Storage to keep data which is consistent in each conditional branch.
|
// Storage to keep data which is consistent in each conditional branch.
|
||||||
This is created and used for one `compile_insns` call and its values
|
// This is created and used for one `compile_insns` call and its values
|
||||||
should be copied for extra `compile_insns` call. */
|
// should be copied for extra `compile_insns` call.
|
||||||
struct compile_branch {
|
struct compile_branch {
|
||||||
unsigned int stack_size; // this simulates sp (stack pointer) of YARV
|
unsigned int stack_size; // this simulates sp (stack pointer) of YARV
|
||||||
bool finish_p; // if true, compilation in this branch should stop and let another branch to be compiled
|
bool finish_p; // if true, compilation in this branch should stop and let another branch to be compiled
|
||||||
|
@ -92,7 +92,7 @@ compile_case_dispatch_each(VALUE key, VALUE value, VALUE arg)
|
||||||
return ST_CONTINUE;
|
return ST_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calling rb_id2str in MJIT worker causes random SEGV. So this is disabled by default. */
|
// Calling rb_id2str in MJIT worker causes random SEGV. So this is disabled by default.
|
||||||
static void
|
static void
|
||||||
comment_id(FILE *f, ID id)
|
comment_id(FILE *f, ID id)
|
||||||
{
|
{
|
||||||
|
@ -120,12 +120,12 @@ comment_id(FILE *f, ID id)
|
||||||
static void compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
|
static void compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
|
||||||
unsigned int pos, struct compile_status *status);
|
unsigned int pos, struct compile_status *status);
|
||||||
|
|
||||||
/* Main function of JIT compilation, vm_exec_core counterpart for JIT. Compile one insn to `f`, may modify
|
// Main function of JIT compilation, vm_exec_core counterpart for JIT. Compile one insn to `f`, may modify
|
||||||
b->stack_size and return next position.
|
// b->stack_size and return next position.
|
||||||
|
//
|
||||||
When you add a new instruction to insns.def, it would be nice to have JIT compilation support here but
|
// When you add a new instruction to insns.def, it would be nice to have JIT compilation support here but
|
||||||
it's optional. This JIT compiler just ignores ISeq which includes unknown instruction, and ISeq which
|
// it's optional. This JIT compiler just ignores ISeq which includes unknown instruction, and ISeq which
|
||||||
does not have it can be compiled as usual. */
|
// does not have it can be compiled as usual.
|
||||||
static unsigned int
|
static unsigned int
|
||||||
compile_insn(FILE *f, const struct rb_iseq_constant_body *body, const int insn, const VALUE *operands,
|
compile_insn(FILE *f, const struct rb_iseq_constant_body *body, const int insn, const VALUE *operands,
|
||||||
const unsigned int pos, struct compile_status *status, struct compile_branch *b)
|
const unsigned int pos, struct compile_status *status, struct compile_branch *b)
|
||||||
|
@ -136,12 +136,12 @@ compile_insn(FILE *f, const struct rb_iseq_constant_body *body, const int insn,
|
||||||
#include "mjit_compile.inc"
|
#include "mjit_compile.inc"
|
||||||
/*****************/
|
/*****************/
|
||||||
|
|
||||||
/* If next_pos is already compiled and this branch is not finished yet,
|
// If next_pos is already compiled and this branch is not finished yet,
|
||||||
next instruction won't be compiled in C code next and will need `goto`. */
|
// next instruction won't be compiled in C code next and will need `goto`.
|
||||||
if (!b->finish_p && next_pos < body->iseq_size && ALREADY_COMPILED_P(status, next_pos)) {
|
if (!b->finish_p && next_pos < body->iseq_size && ALREADY_COMPILED_P(status, next_pos)) {
|
||||||
fprintf(f, "goto label_%d;\n", next_pos);
|
fprintf(f, "goto label_%d;\n", next_pos);
|
||||||
|
|
||||||
/* Verify stack size assumption is the same among multiple branches */
|
// Verify stack size assumption is the same among multiple branches
|
||||||
if ((unsigned int)status->stack_size_for_pos[next_pos] != b->stack_size) {
|
if ((unsigned int)status->stack_size_for_pos[next_pos] != b->stack_size) {
|
||||||
if (mjit_opts.warnings || mjit_opts.verbose)
|
if (mjit_opts.warnings || mjit_opts.verbose)
|
||||||
fprintf(stderr, "MJIT warning: JIT stack assumption is not the same between branches (%d != %u)\n",
|
fprintf(stderr, "MJIT warning: JIT stack assumption is not the same between branches (%d != %u)\n",
|
||||||
|
@ -153,8 +153,8 @@ compile_insn(FILE *f, const struct rb_iseq_constant_body *body, const int insn,
|
||||||
return next_pos;
|
return next_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile one conditional branch. If it has branchXXX insn, this should be
|
// Compile one conditional branch. If it has branchXXX insn, this should be
|
||||||
called multiple times for each branch. */
|
// called multiple times for each branch.
|
||||||
static void
|
static void
|
||||||
compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
|
compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int stack_size,
|
||||||
unsigned int pos, struct compile_status *status)
|
unsigned int pos, struct compile_status *status)
|
||||||
|
@ -185,7 +185,7 @@ compile_insns(FILE *f, const struct rb_iseq_constant_body *body, unsigned int st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print the block to cancel JIT execution. */
|
// Print the block to cancel JIT execution.
|
||||||
static void
|
static void
|
||||||
compile_cancel_handler(FILE *f, const struct rb_iseq_constant_body *body, struct compile_status *status)
|
compile_cancel_handler(FILE *f, const struct rb_iseq_constant_body *body, struct compile_status *status)
|
||||||
{
|
{
|
||||||
|
@ -222,7 +222,7 @@ mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname)
|
||||||
&& !mjit_copy_cache_from_main_thread(iseq, status.cc_entries, status.is_entries))
|
&& !mjit_copy_cache_from_main_thread(iseq, status.cc_entries, status.is_entries))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* For performance, we verify stack size only on compilation time (mjit_compile.inc.erb) without --jit-debug */
|
// For performance, we verify stack size only on compilation time (mjit_compile.inc.erb) without --jit-debug
|
||||||
if (!mjit_opts.debug) {
|
if (!mjit_opts.debug) {
|
||||||
fprintf(f, "#undef OPT_CHECKED_RUN\n");
|
fprintf(f, "#undef OPT_CHECKED_RUN\n");
|
||||||
fprintf(f, "#define OPT_CHECKED_RUN 0\n\n");
|
fprintf(f, "#define OPT_CHECKED_RUN 0\n\n");
|
||||||
|
@ -242,8 +242,8 @@ mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname)
|
||||||
fprintf(f, " static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
|
fprintf(f, " static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n",
|
||||||
(VALUE)body->iseq_encoded);
|
(VALUE)body->iseq_encoded);
|
||||||
|
|
||||||
/* Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables
|
// Simulate `opt_pc` in setup_parameters_complex. Other PCs which may be passed by catch tables
|
||||||
are not considered since vm_exec doesn't call mjit_exec for catch tables. */
|
// are not considered since vm_exec doesn't call mjit_exec for catch tables.
|
||||||
if (body->param.flags.has_opt) {
|
if (body->param.flags.has_opt) {
|
||||||
int i;
|
int i;
|
||||||
fprintf(f, "\n");
|
fprintf(f, "\n");
|
||||||
|
@ -262,4 +262,4 @@ mjit_compile(FILE *f, const rb_iseq_t *iseq, const char *funcname)
|
||||||
return status.success;
|
return status.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* USE_MJIT */
|
#endif // USE_MJIT
|
||||||
|
|
297
mjit_worker.c
297
mjit_worker.c
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
/* NOTE: All functions in this file are executed on MJIT worker. So don't
|
// NOTE: All functions in this file are executed on MJIT worker. So don't
|
||||||
call Ruby methods (C functions that may call rb_funcall) or trigger
|
// call Ruby methods (C functions that may call rb_funcall) or trigger
|
||||||
GC (using ZALLOC, xmalloc, xfree, etc.) in this file. */
|
// GC (using ZALLOC, xmalloc, xfree, etc.) in this file.
|
||||||
|
|
||||||
/* We utilize widely used C compilers (GCC and LLVM Clang) to
|
/* We utilize widely used C compilers (GCC and LLVM Clang) to
|
||||||
implement MJIT. We feed them a C code generated from ISEQ. The
|
implement MJIT. We feed them a C code generated from ISEQ. The
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
#include "dln.h"
|
#include "dln.h"
|
||||||
|
|
||||||
#include "ruby/util.h"
|
#include "ruby/util.h"
|
||||||
#undef strdup /* ruby_strdup may trigger GC */
|
#undef strdup // ruby_strdup may trigger GC
|
||||||
|
|
||||||
#ifndef MAXPATHLEN
|
#ifndef MAXPATHLEN
|
||||||
# define MAXPATHLEN 1024
|
# define MAXPATHLEN 1024
|
||||||
|
@ -117,41 +117,41 @@
|
||||||
typedef intptr_t pid_t;
|
typedef intptr_t pid_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Atomically set function pointer if possible. */
|
// Atomically set function pointer if possible.
|
||||||
#define MJIT_ATOMIC_SET(var, val) (void)ATOMIC_PTR_EXCHANGE(var, val)
|
#define MJIT_ATOMIC_SET(var, val) (void)ATOMIC_PTR_EXCHANGE(var, val)
|
||||||
|
|
||||||
#define MJIT_TMP_PREFIX "_ruby_mjit_"
|
#define MJIT_TMP_PREFIX "_ruby_mjit_"
|
||||||
|
|
||||||
/* The unit structure that holds metadata of ISeq for MJIT. */
|
// The unit structure that holds metadata of ISeq for MJIT.
|
||||||
struct rb_mjit_unit {
|
struct rb_mjit_unit {
|
||||||
/* Unique order number of unit. */
|
// Unique order number of unit.
|
||||||
int id;
|
int id;
|
||||||
/* Dlopen handle of the loaded object file. */
|
// Dlopen handle of the loaded object file.
|
||||||
void *handle;
|
void *handle;
|
||||||
const rb_iseq_t *iseq;
|
const rb_iseq_t *iseq;
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
/* This value is always set for `compact_all_jit_code`. Also used for lazy deletion. */
|
// This value is always set for `compact_all_jit_code`. Also used for lazy deletion.
|
||||||
char *o_file;
|
char *o_file;
|
||||||
/* true if it's inherited from parent Ruby process and lazy deletion should be skipped.
|
// true if it's inherited from parent Ruby process and lazy deletion should be skipped.
|
||||||
`o_file = NULL` can't be used to skip lazy deletion because `o_file` could be used
|
// `o_file = NULL` can't be used to skip lazy deletion because `o_file` could be used
|
||||||
by child for `compact_all_jit_code`. */
|
// by child for `compact_all_jit_code`.
|
||||||
bool o_file_inherited_p;
|
bool o_file_inherited_p;
|
||||||
#endif
|
#endif
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
/* DLL cannot be removed while loaded on Windows. If this is set, it'll be lazily deleted. */
|
// DLL cannot be removed while loaded on Windows. If this is set, it'll be lazily deleted.
|
||||||
char *so_file;
|
char *so_file;
|
||||||
#endif
|
#endif
|
||||||
/* Only used by unload_units. Flag to check this unit is currently on stack or not. */
|
// Only used by unload_units. Flag to check this unit is currently on stack or not.
|
||||||
char used_code_p;
|
char used_code_p;
|
||||||
struct list_node unode;
|
struct list_node unode;
|
||||||
// mjit_compile's optimization switches
|
// mjit_compile's optimization switches
|
||||||
struct rb_mjit_compile_info compile_info;
|
struct rb_mjit_compile_info compile_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Linked list of struct rb_mjit_unit. */
|
// Linked list of struct rb_mjit_unit.
|
||||||
struct rb_mjit_unit_list {
|
struct rb_mjit_unit_list {
|
||||||
struct list_head head;
|
struct list_head head;
|
||||||
int length; /* the list length */
|
int length; // the list length
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
|
extern void rb_native_mutex_lock(rb_nativethread_lock_t *lock);
|
||||||
|
@ -165,12 +165,12 @@ extern void rb_native_cond_signal(rb_nativethread_cond_t *cond);
|
||||||
extern void rb_native_cond_broadcast(rb_nativethread_cond_t *cond);
|
extern void rb_native_cond_broadcast(rb_nativethread_cond_t *cond);
|
||||||
extern void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
|
extern void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex);
|
||||||
|
|
||||||
/* process.c */
|
// process.c
|
||||||
extern rb_pid_t ruby_waitpid_locked(rb_vm_t *, rb_pid_t, int *status, int options, rb_nativethread_cond_t *cond);
|
extern rb_pid_t ruby_waitpid_locked(rb_vm_t *, rb_pid_t, int *status, int options, rb_nativethread_cond_t *cond);
|
||||||
|
|
||||||
/* A copy of MJIT portion of MRI options since MJIT initialization. We
|
// A copy of MJIT portion of MRI options since MJIT initialization. We
|
||||||
need them as MJIT threads still can work when the most MRI data were
|
// need them as MJIT threads still can work when the most MRI data were
|
||||||
freed. */
|
// freed.
|
||||||
struct mjit_options mjit_opts;
|
struct mjit_options mjit_opts;
|
||||||
|
|
||||||
// true if MJIT is enabled.
|
// true if MJIT is enabled.
|
||||||
|
@ -179,28 +179,28 @@ bool mjit_enabled = false;
|
||||||
// and `mjit_call_p == false`, any JIT-ed code execution is cancelled as soon as possible.
|
// and `mjit_call_p == false`, any JIT-ed code execution is cancelled as soon as possible.
|
||||||
bool mjit_call_p = false;
|
bool mjit_call_p = false;
|
||||||
|
|
||||||
/* Priority queue of iseqs waiting for JIT compilation.
|
// Priority queue of iseqs waiting for JIT compilation.
|
||||||
This variable is a pointer to head unit of the queue. */
|
// This variable is a pointer to head unit of the queue.
|
||||||
static struct rb_mjit_unit_list unit_queue = { LIST_HEAD_INIT(unit_queue.head) };
|
static struct rb_mjit_unit_list unit_queue = { LIST_HEAD_INIT(unit_queue.head) };
|
||||||
/* List of units which are successfully compiled. */
|
// List of units which are successfully compiled.
|
||||||
static struct rb_mjit_unit_list active_units = { LIST_HEAD_INIT(active_units.head) };
|
static struct rb_mjit_unit_list active_units = { LIST_HEAD_INIT(active_units.head) };
|
||||||
/* List of compacted so files which will be cleaned up by `free_list()` in `mjit_finish()`. */
|
// List of compacted so files which will be cleaned up by `free_list()` in `mjit_finish()`.
|
||||||
static struct rb_mjit_unit_list compact_units = { LIST_HEAD_INIT(compact_units.head) };
|
static struct rb_mjit_unit_list compact_units = { LIST_HEAD_INIT(compact_units.head) };
|
||||||
// List of units before recompilation and just waiting for dlclose().
|
// List of units before recompilation and just waiting for dlclose().
|
||||||
static struct rb_mjit_unit_list stale_units = { LIST_HEAD_INIT(stale_units.head) };
|
static struct rb_mjit_unit_list stale_units = { LIST_HEAD_INIT(stale_units.head) };
|
||||||
/* The number of so far processed ISEQs, used to generate unique id. */
|
// The number of so far processed ISEQs, used to generate unique id.
|
||||||
static int current_unit_num;
|
static int current_unit_num;
|
||||||
/* A mutex for conitionals and critical sections. */
|
// A mutex for conitionals and critical sections.
|
||||||
static rb_nativethread_lock_t mjit_engine_mutex;
|
static rb_nativethread_lock_t mjit_engine_mutex;
|
||||||
/* A thread conditional to wake up `mjit_finish` at the end of PCH thread. */
|
// A thread conditional to wake up `mjit_finish` at the end of PCH thread.
|
||||||
static rb_nativethread_cond_t mjit_pch_wakeup;
|
static rb_nativethread_cond_t mjit_pch_wakeup;
|
||||||
/* A thread conditional to wake up the client if there is a change in
|
// A thread conditional to wake up the client if there is a change in
|
||||||
executed unit status. */
|
// executed unit status.
|
||||||
static rb_nativethread_cond_t mjit_client_wakeup;
|
static rb_nativethread_cond_t mjit_client_wakeup;
|
||||||
/* A thread conditional to wake up a worker if there we have something
|
// A thread conditional to wake up a worker if there we have something
|
||||||
to add or we need to stop MJIT engine. */
|
// to add or we need to stop MJIT engine.
|
||||||
static rb_nativethread_cond_t mjit_worker_wakeup;
|
static rb_nativethread_cond_t mjit_worker_wakeup;
|
||||||
/* A thread conditional to wake up workers if at the end of GC. */
|
// A thread conditional to wake up workers if at the end of GC.
|
||||||
static rb_nativethread_cond_t mjit_gc_wakeup;
|
static rb_nativethread_cond_t mjit_gc_wakeup;
|
||||||
// True when GC is working.
|
// True when GC is working.
|
||||||
static bool in_gc;
|
static bool in_gc;
|
||||||
|
@ -211,31 +211,31 @@ static bool stop_worker_p;
|
||||||
// Set to true if worker is stopped.
|
// Set to true if worker is stopped.
|
||||||
static bool worker_stopped;
|
static bool worker_stopped;
|
||||||
|
|
||||||
/* Path of "/tmp", which can be changed to $TMP in MinGW. */
|
// Path of "/tmp", which can be changed to $TMP in MinGW.
|
||||||
static char *tmp_dir;
|
static char *tmp_dir;
|
||||||
/* Hash like { 1 => true, 2 => true, ... } whose keys are valid `class_serial`s.
|
// Hash like { 1 => true, 2 => true, ... } whose keys are valid `class_serial`s.
|
||||||
This is used to invalidate obsoleted CALL_CACHE. */
|
// This is used to invalidate obsoleted CALL_CACHE.
|
||||||
static VALUE valid_class_serials;
|
static VALUE valid_class_serials;
|
||||||
|
|
||||||
/* Used C compiler path. */
|
// Used C compiler path.
|
||||||
static const char *cc_path;
|
static const char *cc_path;
|
||||||
/* Used C compiler flags. */
|
// Used C compiler flags.
|
||||||
static const char **cc_common_args;
|
static const char **cc_common_args;
|
||||||
/* Name of the precompiled header file. */
|
// Name of the precompiled header file.
|
||||||
static char *pch_file;
|
static char *pch_file;
|
||||||
/* The process id which should delete the pch_file on mjit_finish. */
|
// The process id which should delete the pch_file on mjit_finish.
|
||||||
static rb_pid_t pch_owner_pid;
|
static rb_pid_t pch_owner_pid;
|
||||||
/* Status of the precompiled header creation. The status is
|
// Status of the precompiled header creation. The status is
|
||||||
shared by the workers and the pch thread. */
|
// shared by the workers and the pch thread.
|
||||||
static enum {PCH_NOT_READY, PCH_FAILED, PCH_SUCCESS} pch_status;
|
static enum {PCH_NOT_READY, PCH_FAILED, PCH_SUCCESS} pch_status;
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
/* Name of the header file. */
|
// Name of the header file.
|
||||||
static char *header_file;
|
static char *header_file;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
/* Linker option to enable libruby. */
|
// Linker option to enable libruby.
|
||||||
static char *libruby_pathflag;
|
static char *libruby_pathflag;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ static char *libruby_pathflag;
|
||||||
#if defined __GNUC__ && !defined __clang__ && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(_AIX) && !defined(__OpenBSD__)
|
#if defined __GNUC__ && !defined __clang__ && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(_AIX) && !defined(__OpenBSD__)
|
||||||
# define GCC_NOSTDLIB_FLAGS "-nodefaultlibs", "-nostdlib",
|
# define GCC_NOSTDLIB_FLAGS "-nodefaultlibs", "-nostdlib",
|
||||||
#else
|
#else
|
||||||
# define GCC_NOSTDLIB_FLAGS /* empty */
|
# define GCC_NOSTDLIB_FLAGS // empty
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const char *const CC_COMMON_ARGS[] = {
|
static const char *const CC_COMMON_ARGS[] = {
|
||||||
|
@ -291,8 +291,8 @@ static const char *const CC_LIBS[] = {
|
||||||
|
|
||||||
#define CC_CODEFLAG_ARGS (mjit_opts.debug ? CC_DEBUG_ARGS : CC_OPTIMIZE_ARGS)
|
#define CC_CODEFLAG_ARGS (mjit_opts.debug ? CC_DEBUG_ARGS : CC_OPTIMIZE_ARGS)
|
||||||
|
|
||||||
/* Print the arguments according to FORMAT to stderr only if MJIT
|
// Print the arguments according to FORMAT to stderr only if MJIT
|
||||||
verbose option value is more or equal to LEVEL. */
|
// verbose option value is more or equal to LEVEL.
|
||||||
PRINTF_ARGS(static void, 2, 3)
|
PRINTF_ARGS(static void, 2, 3)
|
||||||
verbose(int level, const char *format, ...)
|
verbose(int level, const char *format, ...)
|
||||||
{
|
{
|
||||||
|
@ -301,7 +301,7 @@ verbose(int level, const char *format, ...)
|
||||||
size_t len = strlen(format);
|
size_t len = strlen(format);
|
||||||
char *full_format = alloca(sizeof(char) * (len + 2));
|
char *full_format = alloca(sizeof(char) * (len + 2));
|
||||||
|
|
||||||
/* Creating `format + '\n'` to atomically print format and '\n'. */
|
// Creating `format + '\n'` to atomically print format and '\n'.
|
||||||
memcpy(full_format, format, len);
|
memcpy(full_format, format, len);
|
||||||
full_format[len] = '\n';
|
full_format[len] = '\n';
|
||||||
full_format[len+1] = '\0';
|
full_format[len+1] = '\0';
|
||||||
|
@ -326,8 +326,8 @@ mjit_warning(const char *format, ...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add unit node to the tail of doubly linked LIST. It should be not in
|
// Add unit node to the tail of doubly linked `list`. It should be not in
|
||||||
the list before. */
|
// the list before.
|
||||||
static void
|
static void
|
||||||
add_to_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
|
add_to_list(struct rb_mjit_unit *unit, struct rb_mjit_unit_list *list)
|
||||||
{
|
{
|
||||||
|
@ -360,7 +360,7 @@ remove_file(const char *filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lazily delete .o and/or .so files. */
|
// Lazily delete .o and/or .so files.
|
||||||
static void
|
static void
|
||||||
clean_object_files(struct rb_mjit_unit *unit)
|
clean_object_files(struct rb_mjit_unit *unit)
|
||||||
{
|
{
|
||||||
|
@ -369,8 +369,8 @@ clean_object_files(struct rb_mjit_unit *unit)
|
||||||
char *o_file = unit->o_file;
|
char *o_file = unit->o_file;
|
||||||
|
|
||||||
unit->o_file = NULL;
|
unit->o_file = NULL;
|
||||||
/* For compaction, unit->o_file is always set when compilation succeeds.
|
// For compaction, unit->o_file is always set when compilation succeeds.
|
||||||
So save_temps needs to be checked here. */
|
// So save_temps needs to be checked here.
|
||||||
if (!mjit_opts.save_temps && !unit->o_file_inherited_p)
|
if (!mjit_opts.save_temps && !unit->o_file_inherited_p)
|
||||||
remove_file(o_file);
|
remove_file(o_file);
|
||||||
free(o_file);
|
free(o_file);
|
||||||
|
@ -389,30 +389,29 @@ clean_object_files(struct rb_mjit_unit *unit)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is called in the following situations:
|
// This is called in the following situations:
|
||||||
1) On dequeue or `unload_units()`, associated ISeq is already GCed.
|
// 1) On dequeue or `unload_units()`, associated ISeq is already GCed.
|
||||||
2) The unit is not called often and unloaded by `unload_units()`.
|
// 2) The unit is not called often and unloaded by `unload_units()`.
|
||||||
3) Freeing lists on `mjit_finish()`.
|
// 3) Freeing lists on `mjit_finish()`.
|
||||||
|
//
|
||||||
`jit_func` value does not matter for 1 and 3 since the unit won't be used anymore.
|
// `jit_func` value does not matter for 1 and 3 since the unit won't be used anymore.
|
||||||
For the situation 2, this sets the ISeq's JIT state to NOT_COMPILED_JIT_ISEQ_FUNC
|
// For the situation 2, this sets the ISeq's JIT state to NOT_COMPILED_JIT_ISEQ_FUNC
|
||||||
to prevent the situation that the same methods are continuously compiled. */
|
// to prevent the situation that the same methods are continuously compiled.
|
||||||
static void
|
static void
|
||||||
free_unit(struct rb_mjit_unit *unit)
|
free_unit(struct rb_mjit_unit *unit)
|
||||||
{
|
{
|
||||||
if (unit->iseq) { /* ISeq is not GCed */
|
if (unit->iseq) { // ISeq is not GCed
|
||||||
unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
|
unit->iseq->body->jit_func = (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
|
||||||
unit->iseq->body->jit_unit = NULL;
|
unit->iseq->body->jit_unit = NULL;
|
||||||
}
|
}
|
||||||
if (unit->handle && dlclose(unit->handle)) { /* handle is NULL if it's in queue */
|
if (unit->handle && dlclose(unit->handle)) { // handle is NULL if it's in queue
|
||||||
mjit_warning("failed to close handle for u%d: %s", unit->id, dlerror());
|
mjit_warning("failed to close handle for u%d: %s", unit->id, dlerror());
|
||||||
}
|
}
|
||||||
clean_object_files(unit);
|
clean_object_files(unit);
|
||||||
free(unit);
|
free(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start a critical section. Use message MSG to print debug info at
|
// Start a critical section. Use message `msg` to print debug info at `level`.
|
||||||
LEVEL. */
|
|
||||||
static inline void
|
static inline void
|
||||||
CRITICAL_SECTION_START(int level, const char *msg)
|
CRITICAL_SECTION_START(int level, const char *msg)
|
||||||
{
|
{
|
||||||
|
@ -421,8 +420,8 @@ CRITICAL_SECTION_START(int level, const char *msg)
|
||||||
verbose(level, "Locked %s", msg);
|
verbose(level, "Locked %s", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finish the current critical section. Use message MSG to print
|
// Finish the current critical section. Use message `msg` to print
|
||||||
debug info at LEVEL. */
|
// debug info at `level`.
|
||||||
static inline void
|
static inline void
|
||||||
CRITICAL_SECTION_FINISH(int level, const char *msg)
|
CRITICAL_SECTION_FINISH(int level, const char *msg)
|
||||||
{
|
{
|
||||||
|
@ -436,7 +435,7 @@ sprint_uniq_filename(char *str, size_t size, unsigned long id, const char *prefi
|
||||||
return snprintf(str, size, "%s/%sp%"PRI_PIDT_PREFIX"uu%lu%s", tmp_dir, prefix, getpid(), id, suffix);
|
return snprintf(str, size, "%s/%sp%"PRI_PIDT_PREFIX"uu%lu%s", tmp_dir, prefix, getpid(), id, suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return time in milliseconds as a double. */
|
// Return time in milliseconds as a double.
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
double ruby_real_ms_time(void);
|
double ruby_real_ms_time(void);
|
||||||
# define real_ms_time() ruby_real_ms_time()
|
# define real_ms_time() ruby_real_ms_time()
|
||||||
|
@ -473,17 +472,17 @@ mjit_valid_class_serial_p(rb_serial_t class_serial)
|
||||||
return found_p;
|
return found_p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the best unit from list. The best is the first
|
// Return the best unit from list. The best is the first
|
||||||
high priority unit or the unit whose iseq has the biggest number
|
// high priority unit or the unit whose iseq has the biggest number
|
||||||
of calls so far. */
|
// of calls so far.
|
||||||
static struct rb_mjit_unit *
|
static struct rb_mjit_unit *
|
||||||
get_from_list(struct rb_mjit_unit_list *list)
|
get_from_list(struct rb_mjit_unit_list *list)
|
||||||
{
|
{
|
||||||
struct rb_mjit_unit *unit = NULL, *next, *best = NULL;
|
struct rb_mjit_unit *unit = NULL, *next, *best = NULL;
|
||||||
|
|
||||||
/* Find iseq with max total_calls */
|
// Find iseq with max total_calls
|
||||||
list_for_each_safe(&list->head, unit, next, unode) {
|
list_for_each_safe(&list->head, unit, next, unode) {
|
||||||
if (unit->iseq == NULL) { /* ISeq is GCed. */
|
if (unit->iseq == NULL) { // ISeq is GCed.
|
||||||
remove_from_list(unit, list);
|
remove_from_list(unit, list);
|
||||||
free_unit(unit);
|
free_unit(unit);
|
||||||
continue;
|
continue;
|
||||||
|
@ -499,8 +498,7 @@ get_from_list(struct rb_mjit_unit_list *list)
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return length of NULL-terminated array ARGS excluding the NULL
|
// Return length of NULL-terminated array `args` excluding the NULL marker.
|
||||||
marker. */
|
|
||||||
static size_t
|
static size_t
|
||||||
args_len(char *const *args)
|
args_len(char *const *args)
|
||||||
{
|
{
|
||||||
|
@ -511,9 +509,8 @@ args_len(char *const *args)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Concatenate NUM passed NULL-terminated arrays of strings, put the
|
// Concatenate `num` passed NULL-terminated arrays of strings, put the
|
||||||
result (with NULL end marker) into the heap, and return the
|
// result (with NULL end marker) into the heap, and return the result.
|
||||||
result. */
|
|
||||||
static char **
|
static char **
|
||||||
form_args(int num, ...)
|
form_args(int num, ...)
|
||||||
{
|
{
|
||||||
|
@ -543,16 +540,14 @@ COMPILER_WARNING_PUSH
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
|
COMPILER_WARNING_IGNORED(-Wdeprecated-declarations)
|
||||||
#endif
|
#endif
|
||||||
/* Start an OS process of absolute executable path with arguments ARGV.
|
// Start an OS process of absolute executable path with arguments `argv`.
|
||||||
Return PID of the process. */
|
// Return PID of the process.
|
||||||
static pid_t
|
static pid_t
|
||||||
start_process(const char *abspath, char *const *argv)
|
start_process(const char *abspath, char *const *argv)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
/*
|
// Not calling non-async-signal-safe functions between vfork
|
||||||
* Not calling non-async-signal-safe functions between vfork
|
// and execv for safety
|
||||||
* and execv for safety
|
|
||||||
*/
|
|
||||||
int dev_null = rb_cloexec_open(ruby_null_device, O_WRONLY, 0);
|
int dev_null = rb_cloexec_open(ruby_null_device, O_WRONLY, 0);
|
||||||
|
|
||||||
if (mjit_opts.verbose >= 2) {
|
if (mjit_opts.verbose >= 2) {
|
||||||
|
@ -569,9 +564,9 @@ start_process(const char *abspath, char *const *argv)
|
||||||
extern HANDLE rb_w32_start_process(const char *abspath, char *const *argv, int out_fd);
|
extern HANDLE rb_w32_start_process(const char *abspath, char *const *argv, int out_fd);
|
||||||
int out_fd = 0;
|
int out_fd = 0;
|
||||||
if (mjit_opts.verbose <= 1) {
|
if (mjit_opts.verbose <= 1) {
|
||||||
/* Discard cl.exe's outputs like:
|
// Discard cl.exe's outputs like:
|
||||||
_ruby_mjit_p12u3.c
|
// _ruby_mjit_p12u3.c
|
||||||
Creating library C:.../_ruby_mjit_p12u3.lib and object C:.../_ruby_mjit_p12u3.exp */
|
// Creating library C:.../_ruby_mjit_p12u3.lib and object C:.../_ruby_mjit_p12u3.exp
|
||||||
out_fd = dev_null;
|
out_fd = dev_null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,17 +580,17 @@ start_process(const char *abspath, char *const *argv)
|
||||||
if ((pid = vfork()) == 0) { /* TODO: reuse some function in process.c */
|
if ((pid = vfork()) == 0) { /* TODO: reuse some function in process.c */
|
||||||
umask(0077);
|
umask(0077);
|
||||||
if (mjit_opts.verbose == 0) {
|
if (mjit_opts.verbose == 0) {
|
||||||
/* CC can be started in a thread using a file which has been
|
// CC can be started in a thread using a file which has been
|
||||||
already removed while MJIT is finishing. Discard the
|
// already removed while MJIT is finishing. Discard the
|
||||||
messages about missing files. */
|
// messages about missing files.
|
||||||
dup2(dev_null, STDERR_FILENO);
|
dup2(dev_null, STDERR_FILENO);
|
||||||
dup2(dev_null, STDOUT_FILENO);
|
dup2(dev_null, STDOUT_FILENO);
|
||||||
}
|
}
|
||||||
(void)close(dev_null);
|
(void)close(dev_null);
|
||||||
pid = execv(abspath, argv); /* Pid will be negative on an error */
|
pid = execv(abspath, argv); // Pid will be negative on an error
|
||||||
/* Even if we successfully found CC to compile PCH we still can
|
// Even if we successfully found CC to compile PCH we still can
|
||||||
fail with loading the CC in very rare cases for some reasons.
|
// fail with loading the CC in very rare cases for some reasons.
|
||||||
Stop the forked process in this case. */
|
// Stop the forked process in this case.
|
||||||
verbose(1, "MJIT: Error in execv: %s", abspath);
|
verbose(1, "MJIT: Error in execv: %s", abspath);
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
@ -605,9 +600,9 @@ start_process(const char *abspath, char *const *argv)
|
||||||
}
|
}
|
||||||
COMPILER_WARNING_POP
|
COMPILER_WARNING_POP
|
||||||
|
|
||||||
/* Execute an OS process of executable PATH with arguments ARGV.
|
// Execute an OS process of executable PATH with arguments ARGV.
|
||||||
Return -1 or -2 if failed to execute, otherwise exit code of the process.
|
// Return -1 or -2 if failed to execute, otherwise exit code of the process.
|
||||||
TODO: Use a similar function in process.c */
|
// TODO: Use a similar function in process.c
|
||||||
static int
|
static int
|
||||||
exec_process(const char *path, char *const argv[])
|
exec_process(const char *path, char *const argv[])
|
||||||
{
|
{
|
||||||
|
@ -655,8 +650,8 @@ static void
|
||||||
remove_so_file(const char *so_file, struct rb_mjit_unit *unit)
|
remove_so_file(const char *so_file, struct rb_mjit_unit *unit)
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
/* Windows can't remove files while it's used. */
|
// Windows can't remove files while it's used.
|
||||||
unit->so_file = strdup(so_file); /* lazily delete on `clean_object_files()` */
|
unit->so_file = strdup(so_file); // lazily delete on `clean_object_files()`
|
||||||
if (unit->so_file == NULL)
|
if (unit->so_file == NULL)
|
||||||
mjit_warning("failed to allocate memory to lazily remove '%s': %s", so_file, strerror(errno));
|
mjit_warning("failed to allocate memory to lazily remove '%s': %s", so_file, strerror(errno));
|
||||||
#else
|
#else
|
||||||
|
@ -678,39 +673,39 @@ compile_c_to_so(const char *c_file, const char *so_file)
|
||||||
char **args;
|
char **args;
|
||||||
char *p, *obj_file;
|
char *p, *obj_file;
|
||||||
|
|
||||||
/* files[0] = "-Fe*.dll" */
|
// files[0] = "-Fe*.dll"
|
||||||
files[0] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fe") + strlen(so_file) + 1));
|
files[0] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fe") + strlen(so_file) + 1));
|
||||||
p = append_lit(p, "-Fe");
|
p = append_lit(p, "-Fe");
|
||||||
p = append_str2(p, so_file, strlen(so_file));
|
p = append_str2(p, so_file, strlen(so_file));
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
/* files[1] = "-Fo*.obj" */
|
// files[1] = "-Fo*.obj"
|
||||||
/* We don't need .obj file, but it's somehow created to cwd without -Fo and we want to control the output directory. */
|
// We don't need .obj file, but it's somehow created to cwd without -Fo and we want to control the output directory.
|
||||||
files[1] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fo") + strlen(so_file) - rb_strlen_lit(DLEXT) + rb_strlen_lit(".obj") + 1));
|
files[1] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fo") + strlen(so_file) - rb_strlen_lit(DLEXT) + rb_strlen_lit(".obj") + 1));
|
||||||
obj_file = p = append_lit(p, "-Fo");
|
obj_file = p = append_lit(p, "-Fo");
|
||||||
p = append_str2(p, so_file, strlen(so_file) - rb_strlen_lit(DLEXT));
|
p = append_str2(p, so_file, strlen(so_file) - rb_strlen_lit(DLEXT));
|
||||||
p = append_lit(p, ".obj");
|
p = append_lit(p, ".obj");
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
/* files[2] = "-Yu*.pch" */
|
// files[2] = "-Yu*.pch"
|
||||||
files[2] = p = alloca(sizeof(char) * (rb_strlen_lit("-Yu") + strlen(pch_file) + 1));
|
files[2] = p = alloca(sizeof(char) * (rb_strlen_lit("-Yu") + strlen(pch_file) + 1));
|
||||||
p = append_lit(p, "-Yu");
|
p = append_lit(p, "-Yu");
|
||||||
p = append_str2(p, pch_file, strlen(pch_file));
|
p = append_str2(p, pch_file, strlen(pch_file));
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
/* files[3] = "C:/.../rb_mjit_header-*.obj" */
|
// files[3] = "C:/.../rb_mjit_header-*.obj"
|
||||||
files[3] = p = alloca(sizeof(char) * (strlen(pch_file) + 1));
|
files[3] = p = alloca(sizeof(char) * (strlen(pch_file) + 1));
|
||||||
p = append_str2(p, pch_file, strlen(pch_file) - strlen(".pch"));
|
p = append_str2(p, pch_file, strlen(pch_file) - strlen(".pch"));
|
||||||
p = append_lit(p, ".obj");
|
p = append_lit(p, ".obj");
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
/* files[4] = "-Tc*.c" */
|
// files[4] = "-Tc*.c"
|
||||||
files[4] = p = alloca(sizeof(char) * (rb_strlen_lit("-Tc") + strlen(c_file) + 1));
|
files[4] = p = alloca(sizeof(char) * (rb_strlen_lit("-Tc") + strlen(c_file) + 1));
|
||||||
p = append_lit(p, "-Tc");
|
p = append_lit(p, "-Tc");
|
||||||
p = append_str2(p, c_file, strlen(c_file));
|
p = append_str2(p, c_file, strlen(c_file));
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
|
||||||
/* files[5] = "-Fd*.pdb" */
|
// files[5] = "-Fd*.pdb"
|
||||||
files[5] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fd") + strlen(pch_file) + 1));
|
files[5] = p = alloca(sizeof(char) * (rb_strlen_lit("-Fd") + strlen(pch_file) + 1));
|
||||||
p = append_lit(p, "-Fd");
|
p = append_lit(p, "-Fd");
|
||||||
p = append_str2(p, pch_file, strlen(pch_file) - rb_strlen_lit(".pch"));
|
p = append_str2(p, pch_file, strlen(pch_file) - rb_strlen_lit(".pch"));
|
||||||
|
@ -726,7 +721,7 @@ compile_c_to_so(const char *c_file, const char *so_file)
|
||||||
free(args);
|
free(args);
|
||||||
|
|
||||||
if (exit_code == 0) {
|
if (exit_code == 0) {
|
||||||
/* remove never-used files (.obj, .lib, .exp, .pdb). XXX: Is there any way not to generate this? */
|
// remove never-used files (.obj, .lib, .exp, .pdb). XXX: Is there any way not to generate this?
|
||||||
if (!mjit_opts.save_temps) {
|
if (!mjit_opts.save_temps) {
|
||||||
char *before_dot;
|
char *before_dot;
|
||||||
remove_file(obj_file);
|
remove_file(obj_file);
|
||||||
|
@ -742,9 +737,9 @@ compile_c_to_so(const char *c_file, const char *so_file)
|
||||||
}
|
}
|
||||||
return exit_code == 0;
|
return exit_code == 0;
|
||||||
}
|
}
|
||||||
#else /* _MSC_VER */
|
#else // _MSC_VER
|
||||||
|
|
||||||
/* The function producing the pre-compiled header. */
|
// The function producing the pre-compiled header.
|
||||||
static void
|
static void
|
||||||
make_pch(void)
|
make_pch(void)
|
||||||
{
|
{
|
||||||
|
@ -849,12 +844,12 @@ link_o_to_so(const char **o_files, const char *so_file)
|
||||||
return exit_code == 0;
|
return exit_code == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Link all cached .o files and build a .so file. Reload all JIT func from it. This
|
// Link all cached .o files and build a .so file. Reload all JIT func from it. This
|
||||||
allows to avoid JIT code fragmentation and improve performance to call JIT-ed code. */
|
// allows to avoid JIT code fragmentation and improve performance to call JIT-ed code.
|
||||||
static void
|
static void
|
||||||
compact_all_jit_code(void)
|
compact_all_jit_code(void)
|
||||||
{
|
{
|
||||||
# ifndef _WIN32 /* This requires header transformation but we don't transform header on Windows for now */
|
# ifndef _WIN32 // This requires header transformation but we don't transform header on Windows for now
|
||||||
struct rb_mjit_unit *unit, *cur = 0;
|
struct rb_mjit_unit *unit, *cur = 0;
|
||||||
double start_time, end_time;
|
double start_time, end_time;
|
||||||
static const char so_ext[] = DLEXT;
|
static const char so_ext[] = DLEXT;
|
||||||
|
@ -862,13 +857,13 @@ compact_all_jit_code(void)
|
||||||
const char **o_files;
|
const char **o_files;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
/* Abnormal use case of rb_mjit_unit that doesn't have ISeq */
|
// Abnormal use case of rb_mjit_unit that doesn't have ISeq
|
||||||
unit = calloc(1, sizeof(struct rb_mjit_unit)); /* To prevent GC, don't use ZALLOC */
|
unit = calloc(1, sizeof(struct rb_mjit_unit)); // To prevent GC, don't use ZALLOC
|
||||||
if (unit == NULL) return;
|
if (unit == NULL) return;
|
||||||
unit->id = current_unit_num++;
|
unit->id = current_unit_num++;
|
||||||
sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
|
sprint_uniq_filename(so_file, (int)sizeof(so_file), unit->id, MJIT_TMP_PREFIX, so_ext);
|
||||||
|
|
||||||
/* NULL-ending for form_args */
|
// NULL-ending for form_args
|
||||||
o_files = alloca(sizeof(char *) * (active_units.length + 1));
|
o_files = alloca(sizeof(char *) * (active_units.length + 1));
|
||||||
o_files[active_units.length] = NULL;
|
o_files[active_units.length] = NULL;
|
||||||
CRITICAL_SECTION_START(3, "in compact_all_jit_code to keep .o files");
|
CRITICAL_SECTION_START(3, "in compact_all_jit_code to keep .o files");
|
||||||
|
@ -881,10 +876,10 @@ compact_all_jit_code(void)
|
||||||
bool success = link_o_to_so(o_files, so_file);
|
bool success = link_o_to_so(o_files, so_file);
|
||||||
end_time = real_ms_time();
|
end_time = real_ms_time();
|
||||||
|
|
||||||
/* TODO: Shrink this big critical section. For now, this is needed to prevent failure by missing .o files.
|
// TODO: Shrink this big critical section. For now, this is needed to prevent failure by missing .o files.
|
||||||
This assumes that o -> so link doesn't take long time because the bottleneck, which is compiler optimization,
|
// This assumes that o -> so link doesn't take long time because the bottleneck, which is compiler optimization,
|
||||||
is already done. But actually it takes about 500ms for 5,000 methods on my Linux machine, so it's better to
|
// is already done. But actually it takes about 500ms for 5,000 methods on my Linux machine, so it's better to
|
||||||
finish this critical section before link_o_to_so by disabling unload_units. */
|
// finish this critical section before link_o_to_so by disabling unload_units.
|
||||||
CRITICAL_SECTION_FINISH(3, "in compact_all_jit_code to keep .o files");
|
CRITICAL_SECTION_FINISH(3, "in compact_all_jit_code to keep .o files");
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -896,7 +891,7 @@ compact_all_jit_code(void)
|
||||||
}
|
}
|
||||||
unit->handle = handle;
|
unit->handle = handle;
|
||||||
|
|
||||||
/* lazily dlclose handle (and .so file for win32) on `mjit_finish()`. */
|
// lazily dlclose handle (and .so file for win32) on `mjit_finish()`.
|
||||||
add_to_list(unit, &compact_units);
|
add_to_list(unit, &compact_units);
|
||||||
|
|
||||||
if (!mjit_opts.save_temps)
|
if (!mjit_opts.save_temps)
|
||||||
|
@ -905,7 +900,7 @@ compact_all_jit_code(void)
|
||||||
CRITICAL_SECTION_START(3, "in compact_all_jit_code to read list");
|
CRITICAL_SECTION_START(3, "in compact_all_jit_code to read list");
|
||||||
list_for_each(&active_units.head, cur, unode) {
|
list_for_each(&active_units.head, cur, unode) {
|
||||||
void *func;
|
void *func;
|
||||||
char funcname[35]; /* TODO: reconsider `35` */
|
char funcname[35]; // TODO: reconsider `35`
|
||||||
sprintf(funcname, "_mjit%d", cur->id);
|
sprintf(funcname, "_mjit%d", cur->id);
|
||||||
|
|
||||||
if ((func = dlsym(handle, funcname)) == NULL) {
|
if ((func = dlsym(handle, funcname)) == NULL) {
|
||||||
|
@ -913,8 +908,8 @@ compact_all_jit_code(void)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cur->iseq) { /* Check whether GCed or not */
|
if (cur->iseq) { // Check whether GCed or not
|
||||||
/* Usage of jit_code might be not in a critical section. */
|
// Usage of jit_code might be not in a critical section.
|
||||||
MJIT_ATOMIC_SET(cur->iseq->body->jit_func, (mjit_func_t)func);
|
MJIT_ATOMIC_SET(cur->iseq->body->jit_func, (mjit_func_t)func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -925,10 +920,10 @@ compact_all_jit_code(void)
|
||||||
free(unit);
|
free(unit);
|
||||||
verbose(1, "JIT compaction failure (%.1fms): Failed to compact methods", end_time - start_time);
|
verbose(1, "JIT compaction failure (%.1fms): Failed to compact methods", end_time - start_time);
|
||||||
}
|
}
|
||||||
# endif /* _WIN32 */
|
# endif // _WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _MSC_VER */
|
#endif // _MSC_VER
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit *unit)
|
load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit *unit)
|
||||||
|
@ -959,10 +954,10 @@ static const char *
|
||||||
header_name_end(const char *s)
|
header_name_end(const char *s)
|
||||||
{
|
{
|
||||||
const char *e = s + strlen(s);
|
const char *e = s + strlen(s);
|
||||||
# ifdef __GNUC__ /* don't chomp .pch for mswin */
|
# ifdef __GNUC__ // don't chomp .pch for mswin
|
||||||
static const char suffix[] = ".gch";
|
static const char suffix[] = ".gch";
|
||||||
|
|
||||||
/* chomp .gch suffix */
|
// chomp .gch suffix
|
||||||
if (e > s+sizeof(suffix)-1 && strcmp(e-sizeof(suffix)+1, suffix) == 0) {
|
if (e > s+sizeof(suffix)-1 && strcmp(e-sizeof(suffix)+1, suffix) == 0) {
|
||||||
e -= sizeof(suffix)-1;
|
e -= sizeof(suffix)-1;
|
||||||
}
|
}
|
||||||
|
@ -971,16 +966,16 @@ header_name_end(const char *s)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Print platform-specific prerequisites in generated code. */
|
// Print platform-specific prerequisites in generated code.
|
||||||
static void
|
static void
|
||||||
compile_prelude(FILE *f)
|
compile_prelude(FILE *f)
|
||||||
{
|
{
|
||||||
#ifndef __clang__ /* -include-pch is used for Clang */
|
#ifndef __clang__ // -include-pch is used for Clang
|
||||||
const char *s = pch_file;
|
const char *s = pch_file;
|
||||||
const char *e = header_name_end(s);
|
const char *e = header_name_end(s);
|
||||||
|
|
||||||
fprintf(f, "#include \"");
|
fprintf(f, "#include \"");
|
||||||
/* print pch_file except .gch for gcc, but keep .pch for mswin */
|
// print pch_file except .gch for gcc, but keep .pch for mswin
|
||||||
for (; s < e; s++) {
|
for (; s < e; s++) {
|
||||||
switch(*s) {
|
switch(*s) {
|
||||||
case '\\': case '"':
|
case '\\': case '"':
|
||||||
|
@ -997,12 +992,12 @@ compile_prelude(FILE *f)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile ISeq in UNIT and return function pointer of JIT-ed code.
|
// Compile ISeq in UNIT and return function pointer of JIT-ed code.
|
||||||
It may return NOT_COMPILED_JIT_ISEQ_FUNC if something went wrong. */
|
// It may return NOT_COMPILED_JIT_ISEQ_FUNC if something went wrong.
|
||||||
static mjit_func_t
|
static mjit_func_t
|
||||||
convert_unit_to_func(struct rb_mjit_unit *unit)
|
convert_unit_to_func(struct rb_mjit_unit *unit)
|
||||||
{
|
{
|
||||||
char c_file_buff[MAXPATHLEN], *c_file = c_file_buff, *so_file, funcname[35]; /* TODO: reconsider `35` */
|
char c_file_buff[MAXPATHLEN], *c_file = c_file_buff, *so_file, funcname[35]; // TODO: reconsider `35`
|
||||||
int fd;
|
int fd;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
void *func;
|
void *func;
|
||||||
|
@ -1047,17 +1042,17 @@ convert_unit_to_func(struct rb_mjit_unit *unit)
|
||||||
return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
|
return (mjit_func_t)NOT_COMPILED_JIT_ISEQ_FUNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print #include of MJIT header, etc. */
|
// print #include of MJIT header, etc.
|
||||||
compile_prelude(f);
|
compile_prelude(f);
|
||||||
|
|
||||||
/* wait until mjit_gc_finish_hook is called */
|
// wait until mjit_gc_finish_hook is called
|
||||||
CRITICAL_SECTION_START(3, "before mjit_compile to wait GC finish");
|
CRITICAL_SECTION_START(3, "before mjit_compile to wait GC finish");
|
||||||
while (in_gc) {
|
while (in_gc) {
|
||||||
verbose(3, "Waiting wakeup from GC");
|
verbose(3, "Waiting wakeup from GC");
|
||||||
rb_native_cond_wait(&mjit_gc_wakeup, &mjit_engine_mutex);
|
rb_native_cond_wait(&mjit_gc_wakeup, &mjit_engine_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need to check again here because we could've waited on GC above */
|
// We need to check again here because we could've waited on GC above
|
||||||
if (unit->iseq == NULL) {
|
if (unit->iseq == NULL) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
if (!mjit_opts.save_temps)
|
if (!mjit_opts.save_temps)
|
||||||
|
@ -1083,7 +1078,7 @@ convert_unit_to_func(struct rb_mjit_unit *unit)
|
||||||
}
|
}
|
||||||
bool success = mjit_compile(f, unit->iseq, funcname);
|
bool success = mjit_compile(f, unit->iseq, funcname);
|
||||||
|
|
||||||
/* release blocking mjit_gc_start_hook */
|
// release blocking mjit_gc_start_hook
|
||||||
CRITICAL_SECTION_START(3, "after mjit_compile to wakeup client for GC");
|
CRITICAL_SECTION_START(3, "after mjit_compile to wakeup client for GC");
|
||||||
in_jit = false;
|
in_jit = false;
|
||||||
verbose(3, "Sending wakeup signal to client in a mjit-worker for GC");
|
verbose(3, "Sending wakeup signal to client in a mjit-worker for GC");
|
||||||
|
@ -1102,13 +1097,13 @@ convert_unit_to_func(struct rb_mjit_unit *unit)
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
success = compile_c_to_so(c_file, so_file);
|
success = compile_c_to_so(c_file, so_file);
|
||||||
#else
|
#else
|
||||||
/* splitting .c -> .o step and .o -> .so step, to cache .o files in the future */
|
// splitting .c -> .o step and .o -> .so step, to cache .o files in the future
|
||||||
if ((success = compile_c_to_o(c_file, o_file)) != false) {
|
if ((success = compile_c_to_o(c_file, o_file)) != false) {
|
||||||
const char *o_files[2] = { NULL, NULL };
|
const char *o_files[2] = { NULL, NULL };
|
||||||
o_files[0] = o_file;
|
o_files[0] = o_file;
|
||||||
success = link_o_to_so(o_files, so_file);
|
success = link_o_to_so(o_files, so_file);
|
||||||
|
|
||||||
/* Always set o_file for compaction. The value is also used for lazy deletion. */
|
// Always set o_file for compaction. The value is also used for lazy deletion.
|
||||||
unit->o_file = strdup(o_file);
|
unit->o_file = strdup(o_file);
|
||||||
if (unit->o_file == NULL) {
|
if (unit->o_file == NULL) {
|
||||||
mjit_warning("failed to allocate memory to remember '%s' (%s), removing it...", o_file, strerror(errno));
|
mjit_warning("failed to allocate memory to remember '%s' (%s), removing it...", o_file, strerror(errno));
|
||||||
|
@ -1146,13 +1141,13 @@ typedef struct {
|
||||||
bool finish_p;
|
bool finish_p;
|
||||||
} mjit_copy_job_t;
|
} mjit_copy_job_t;
|
||||||
|
|
||||||
/* Singleton MJIT copy job. This is made global since it needs to be durable even when MJIT worker thread is stopped.
|
// Singleton MJIT copy job. This is made global since it needs to be durable even when MJIT worker thread is stopped.
|
||||||
(ex: register job -> MJIT pause -> MJIT resume -> dispatch job. Actually this should be just cancelled by finish_p check) */
|
// (ex: register job -> MJIT pause -> MJIT resume -> dispatch job. Actually this should be just cancelled by finish_p check)
|
||||||
static mjit_copy_job_t mjit_copy_job = { .iseq = NULL, .finish_p = true };
|
static mjit_copy_job_t mjit_copy_job = { .iseq = NULL, .finish_p = true };
|
||||||
|
|
||||||
static void mjit_copy_job_handler(void *data);
|
static void mjit_copy_job_handler(void *data);
|
||||||
|
|
||||||
/* vm_trace.c */
|
// vm_trace.c
|
||||||
int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t , void *);
|
int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t , void *);
|
||||||
|
|
||||||
// Copy inline cache values of `iseq` to `cc_entries` and `is_entries`.
|
// Copy inline cache values of `iseq` to `cc_entries` and `is_entries`.
|
||||||
|
@ -1209,9 +1204,9 @@ mjit_copy_cache_from_main_thread(const rb_iseq_t *iseq, struct rb_call_cache *cc
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The function implementing a worker. It is executed in a separate
|
// The function implementing a worker. It is executed in a separate
|
||||||
thread by rb_thread_create_mjit_thread. It compiles precompiled header
|
// thread by rb_thread_create_mjit_thread. It compiles precompiled header
|
||||||
and then compiles requested ISeqs. */
|
// and then compiles requested ISeqs.
|
||||||
void
|
void
|
||||||
mjit_worker(void)
|
mjit_worker(void)
|
||||||
{
|
{
|
||||||
|
@ -1227,14 +1222,14 @@ mjit_worker(void)
|
||||||
verbose(3, "Sending wakeup signal to client in a mjit-worker");
|
verbose(3, "Sending wakeup signal to client in a mjit-worker");
|
||||||
rb_native_cond_signal(&mjit_client_wakeup);
|
rb_native_cond_signal(&mjit_client_wakeup);
|
||||||
CRITICAL_SECTION_FINISH(3, "in worker to update worker_stopped");
|
CRITICAL_SECTION_FINISH(3, "in worker to update worker_stopped");
|
||||||
return; /* TODO: do the same thing in the latter half of mjit_finish */
|
return; // TODO: do the same thing in the latter half of mjit_finish
|
||||||
}
|
}
|
||||||
|
|
||||||
/* main worker loop */
|
// main worker loop
|
||||||
while (!stop_worker_p) {
|
while (!stop_worker_p) {
|
||||||
struct rb_mjit_unit *unit;
|
struct rb_mjit_unit *unit;
|
||||||
|
|
||||||
/* wait until unit is available */
|
// wait until unit is available
|
||||||
CRITICAL_SECTION_START(3, "in worker dequeue");
|
CRITICAL_SECTION_START(3, "in worker dequeue");
|
||||||
while ((list_empty(&unit_queue.head) || active_units.length >= mjit_opts.max_cache_size) && !stop_worker_p) {
|
while ((list_empty(&unit_queue.head) || active_units.length >= mjit_opts.max_cache_size) && !stop_worker_p) {
|
||||||
rb_native_cond_wait(&mjit_worker_wakeup, &mjit_engine_mutex);
|
rb_native_cond_wait(&mjit_worker_wakeup, &mjit_engine_mutex);
|
||||||
|
@ -1254,18 +1249,18 @@ mjit_worker(void)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
CRITICAL_SECTION_START(3, "in jit func replace");
|
CRITICAL_SECTION_START(3, "in jit func replace");
|
||||||
while (in_gc) { /* Make sure we're not GC-ing when touching ISeq */
|
while (in_gc) { // Make sure we're not GC-ing when touching ISeq
|
||||||
verbose(3, "Waiting wakeup from GC");
|
verbose(3, "Waiting wakeup from GC");
|
||||||
rb_native_cond_wait(&mjit_gc_wakeup, &mjit_engine_mutex);
|
rb_native_cond_wait(&mjit_gc_wakeup, &mjit_engine_mutex);
|
||||||
}
|
}
|
||||||
if (unit->iseq) { /* Check whether GCed or not */
|
if (unit->iseq) { // Check whether GCed or not
|
||||||
/* Usage of jit_code might be not in a critical section. */
|
// Usage of jit_code might be not in a critical section.
|
||||||
MJIT_ATOMIC_SET(unit->iseq->body->jit_func, func);
|
MJIT_ATOMIC_SET(unit->iseq->body->jit_func, func);
|
||||||
}
|
}
|
||||||
CRITICAL_SECTION_FINISH(3, "in jit func replace");
|
CRITICAL_SECTION_FINISH(3, "in jit func replace");
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
/* Combine .o files to one .so and reload all jit_func to improve memory locality */
|
// Combine .o files to one .so and reload all jit_func to improve memory locality
|
||||||
if ((!mjit_opts.wait && unit_queue.length == 0 && active_units.length > 1)
|
if ((!mjit_opts.wait && unit_queue.length == 0 && active_units.length > 1)
|
||||||
|| active_units.length == mjit_opts.max_cache_size) {
|
|| active_units.length == mjit_opts.max_cache_size) {
|
||||||
compact_all_jit_code();
|
compact_all_jit_code();
|
||||||
|
|
|
@ -54,4 +54,4 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* OPT_IC_FOR_IVAR */
|
#endif // OPT_IC_FOR_IVAR
|
||||||
|
|
Loading…
Reference in a new issue