72#define __EXTENSIONS__ 1
94#ifdef HAVE_SYS_PARAM_H
95# include <sys/param.h>
103# define MAXPATHLEN 1024
107#define dlopen(name,flag) ((void*)LoadLibrary(name))
108#define dlerror() strerror(rb_w32_map_errno(GetLastError()))
109#define dlsym(handle,name) ((void*)GetProcAddress((handle),(name)))
110#define dlclose(handle) (!FreeLibrary(handle))
113#define waitpid(pid,stat_loc,options) (WaitForSingleObject((HANDLE)(pid), INFINITE), GetExitCodeProcess((HANDLE)(pid), (LPDWORD)(stat_loc)), CloseHandle((HANDLE)pid), (pid))
114#define WIFEXITED(S) ((S) != STILL_ACTIVE)
115#define WEXITSTATUS(S) (S)
116#define WIFSIGNALED(S) (0)
121#define MJIT_ATOMIC_SET(var, val) (void)ATOMIC_PTR_EXCHANGE(var, val)
123#define MJIT_TMP_PREFIX "_ruby_mjit_"
192static int current_unit_num;
210static bool stop_worker_p;
212static bool worker_stopped;
218static VALUE valid_class_serials;
221static const char *cc_path;
223static const char **cc_common_args;
225static char **cc_added_args;
227static char *pch_file;
236static char *header_file;
241static char *libruby_pathflag;
246#if defined(__GNUC__) && \
247 (!defined(__clang__) || \
248 (defined(__clang__) && (defined(__FreeBSD__) || defined(__GLIBC__))))
249# define GCC_PIC_FLAGS "-Wfatal-errors", "-fPIC", "-shared", "-w", "-pipe",
250# define MJIT_CFLAGS_PIPE 1
252# define GCC_PIC_FLAGS
253# define MJIT_CFLAGS_PIPE 0
258#if defined __GNUC__ && !defined __clang__ && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(_AIX) && !defined(__OpenBSD__)
259# define GCC_NOSTDLIB_FLAGS "-nodefaultlibs", "-nostdlib",
261# define GCC_NOSTDLIB_FLAGS
264static const char *
const CC_COMMON_ARGS[] = {
275static const char *
const CC_LINKER_ARGS[] = {
276#if defined __GNUC__ && !defined __clang__ && !defined(__OpenBSD__)
282static const char *
const CC_LIBS[] = {
283#if defined(_WIN32) || defined(__CYGWIN__)
286#if defined __GNUC__ && !defined __clang__
292#if defined __ANDROID__
298#define CC_CODEFLAG_ARGS (mjit_opts.debug ? CC_DEBUG_ARGS : CC_OPTIMIZE_ARGS)
308 char *full_format =
alloca(
sizeof(
char) * (
len + 2));
312 full_format[
len] =
'\n';
313 full_format[
len+1] =
'\0';
364remove_file(
const char *filename)
377 char *o_file = unit->
o_file;
390 char *so_file = unit->so_file;
392 unit->so_file =
NULL;
394 remove_file(so_file);
416 mjit_warning(
"failed to close handle for u%d: %s", unit->
id, dlerror());
418 clean_object_files(unit);
424CRITICAL_SECTION_START(
int level,
const char *msg)
426 verbose(level,
"Locking %s", msg);
428 verbose(level,
"Locked %s", msg);
434CRITICAL_SECTION_FINISH(
int level,
const char *msg)
436 verbose(level,
"Unlocked %s", msg);
441sprint_uniq_filename(
char *
str,
size_t size,
unsigned long id,
const char *prefix,
const char *suffix)
448double ruby_real_ms_time(
void);
449# define real_ms_time() ruby_real_ms_time()
454# ifdef HAVE_CLOCK_GETTIME
456# ifdef CLOCK_MONOTONIC
463 return tv.tv_nsec / 1000000.0 + tv.tv_sec * 1000.0;
468 return tv.tv_usec / 1000.0 + tv.tv_sec * 1000.0;
477 CRITICAL_SECTION_START(3,
"in valid_class_serial_p");
479 CRITICAL_SECTION_FINISH(3,
"in valid_class_serial_p");
494 remove_from_list(unit,
list);
504 remove_from_list(best,
list);
511args_len(
char *
const *args)
515 for (
i = 0; (args[
i]) !=
NULL;
i++)
523form_args(
int num, ...)
528 char **args, **res, **tmp;
532 for (
i =
len = 0;
i < num;
i++) {
533 args =
va_arg(argp,
char **);
535 if ((tmp = (
char **)
realloc(res,
sizeof(
char *) * (
len +
n + 1))) ==
NULL) {
555start_process(
const char *abspath,
char *
const *
argv)
585 verbose(1,
"MJIT: Failed to create process: %s", dlerror());
589 if ((pid =
vfork()) == 0) {
603 verbose(1,
"MJIT: Error in execv: %s", abspath);
616exec_process(
const char *
path,
char *
const argv[])
618 int stat, exit_code = -2;
658remove_so_file(
const char *so_file,
struct rb_mjit_unit *unit)
662 unit->so_file =
strdup(so_file);
663 if (unit->so_file ==
NULL)
666 remove_file(so_file);
670#define append_str2(p, str, len) ((char *)memcpy((p), str, (len))+(len))
671#define append_str(p, str) append_str2(p, str, sizeof(str)-1)
672#define append_lit(p, str) append_str2(p, str, rb_strlen_lit(str))
677compile_c_to_so(
const char *c_file,
const char *so_file)
703 files[3] = p =
alloca(
sizeof(
char) * (
strlen(pch_file) + 1));
722 files, CC_LIBS, CC_DLDFLAGS_ARGS);
726 int exit_code = exec_process(cc_path, args);
729 if (exit_code == 0) {
733 remove_file(obj_file);
736 append_lit(before_dot,
".lib"); remove_file(obj_file);
737 append_lit(before_dot,
".exp"); remove_file(obj_file);
738 append_lit(before_dot,
".pdb"); remove_file(obj_file);
742 verbose(2,
"compile_c_to_so: compile error: %d", exit_code);
744 return exit_code == 0;
752 const char *rest_args[] = {
760 "-o", pch_file, header_file,
764 verbose(2,
"Creating precompiled header");
765 char **args = form_args(4, cc_common_args,
CC_CODEFLAG_ARGS, cc_added_args, rest_args);
767 mjit_warning(
"making precompiled header failed on forming args");
768 CRITICAL_SECTION_START(3,
"in make_pch");
770 CRITICAL_SECTION_FINISH(3,
"in make_pch");
774 int exit_code = exec_process(cc_path, args);
777 CRITICAL_SECTION_START(3,
"in make_pch");
778 if (exit_code == 0) {
782 mjit_warning(
"Making precompiled header failed on compilation. Stopping MJIT worker...");
787 CRITICAL_SECTION_FINISH(3,
"in make_pch");
792compile_c_to_o(
const char *c_file,
const char *
o_file)
794 const char *files[] = {
797 "-include-pch", pch_file,
802 char **args = form_args(5, cc_common_args,
CC_CODEFLAG_ARGS, cc_added_args, files, CC_LINKER_ARGS);
806 int exit_code = exec_process(cc_path, args);
810 verbose(2,
"compile_c_to_o: compile error: %d", exit_code);
811 return exit_code == 0;
816link_o_to_so(
const char **o_files,
const char *so_file)
818 const char *options[] = {
826# if defined(__MACH__)
827 extern VALUE rb_libruby_selfpath;
828 const char *loader_args[] = {
"-bundle_loader",
StringValuePtr(rb_libruby_selfpath),
NULL};
830 const char *loader_args[] = {
NULL};
834 options, o_files, loader_args, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS);
838 int exit_code = exec_process(cc_path, args);
842 verbose(2,
"link_o_to_so: link error: %d", exit_code);
843 return exit_code == 0;
849compact_all_jit_code(
void)
853 double start_time, end_time;
854 static const char so_ext[] =
DLEXT;
856 const char **o_files;
861 if (unit ==
NULL)
return;
862 unit->
id = current_unit_num++;
863 sprint_uniq_filename(so_file, (
int)
sizeof(so_file), unit->
id,
MJIT_TMP_PREFIX, so_ext);
866 o_files =
alloca(
sizeof(
char *) * (active_units.
length + 1));
868 CRITICAL_SECTION_START(3,
"in compact_all_jit_code to keep .o files");
874 start_time = real_ms_time();
875 bool success = link_o_to_so(o_files, so_file);
876 end_time = real_ms_time();
882 CRITICAL_SECTION_FINISH(3,
"in compact_all_jit_code to keep .o files");
885 void *
handle = dlopen(so_file, RTLD_NOW);
887 mjit_warning(
"failure in loading code from compacted '%s': %s", so_file, dlerror());
894 add_to_list(unit, &compact_units);
897 remove_so_file(so_file, unit);
899 CRITICAL_SECTION_START(3,
"in compact_all_jit_code to read list");
905 if ((func = dlsym(
handle, funcname)) ==
NULL) {
906 mjit_warning(
"skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
915 CRITICAL_SECTION_FINISH(3,
"in compact_all_jit_code to read list");
916 verbose(1,
"JIT compaction (%.1fms): Compacted %d methods -> %s", end_time - start_time, active_units.
length, so_file);
920 verbose(1,
"JIT compaction failure (%.1fms): Failed to compact methods", end_time - start_time);
928load_func_from_so(
const char *so_file,
const char *funcname,
struct rb_mjit_unit *unit)
932 handle = dlopen(so_file, RTLD_NOW);
934 mjit_warning(
"failure in loading code from '%s': %s", so_file, dlerror());
938 func = dlsym(
handle, funcname);
945header_name_end(
const char *s)
947 const char *e = s +
strlen(s);
949 static const char suffix[] =
".gch";
952 if (e > s+
sizeof(suffix)-1 &&
strcmp(e-
sizeof(suffix)+1, suffix) == 0) {
953 e -=
sizeof(suffix)-1;
962compile_prelude(
FILE *
f)
965 const char *s = pch_file;
966 const char *e = header_name_end(s);
981 fprintf(
f,
"void _pei386_runtime_relocator(void){}\n");
982 fprintf(
f,
"int __stdcall DllMainCRTStartup(void* hinstDLL, unsigned int fdwReason, void* lpvReserved) { return 1; }\n");
991 char c_file_buff[
MAXPATHLEN], *c_file = c_file_buff, *so_file, funcname[35];
995 double start_time, end_time;
996 int c_file_len = (
int)
sizeof(c_file_buff);
997 static const char c_ext[] =
".c";
998 static const char so_ext[] =
DLEXT;
999 const int access_mode =
1003 O_WRONLY|O_EXCL|O_CREAT;
1005 static const char o_ext[] =
".o";
1009 c_file_len = sprint_uniq_filename(c_file_buff, c_file_len, unit->
id,
MJIT_TMP_PREFIX, c_ext);
1010 if (c_file_len >= (
int)
sizeof(c_file_buff)) {
1012 c_file =
alloca(c_file_len);
1013 c_file_len = sprint_uniq_filename(c_file, c_file_len, unit->
id,
MJIT_TMP_PREFIX, c_ext);
1018 o_file =
alloca(c_file_len -
sizeof(c_ext) +
sizeof(o_ext));
1020 memcpy(&
o_file[c_file_len -
sizeof(c_ext)], o_ext,
sizeof(o_ext));
1022 so_file =
alloca(c_file_len -
sizeof(c_ext) +
sizeof(so_ext));
1023 memcpy(so_file, c_file, c_file_len -
sizeof(c_ext));
1024 memcpy(&so_file[c_file_len -
sizeof(c_ext)], so_ext,
sizeof(so_ext));
1032 verbose(1,
"Failed to fopen '%s', giving up JIT for it (%s)", c_file,
strerror(e));
1040 CRITICAL_SECTION_START(3,
"before mjit_compile to wait GC finish");
1042 verbose(3,
"Waiting wakeup from GC");
1050 remove_file(c_file);
1056 CRITICAL_SECTION_FINISH(3,
"before mjit_compile to wait GC finish");
1062 long iseq_lineno = 0;
1071 verbose(2,
"start compilation: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
1072 fprintf(
f,
"/* %s@%s:%ld */\n\n", iseq_label, iseq_path, iseq_lineno);
1076 CRITICAL_SECTION_START(3,
"after mjit_compile to wakeup client for GC");
1078 verbose(3,
"Sending wakeup signal to client in a mjit-worker for GC");
1080 CRITICAL_SECTION_FINISH(3,
"in worker to wakeup client for GC");
1085 remove_file(c_file);
1086 verbose(1,
"JIT failure: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
1090 start_time = real_ms_time();
1092 success = compile_c_to_so(c_file, so_file);
1095 if ((success = compile_c_to_o(c_file,
o_file)) !=
false) {
1096 success = link_o_to_so((
const char *[]){
o_file,
NULL }, so_file);
1106 end_time = real_ms_time();
1109 remove_file(c_file);
1111 verbose(2,
"Failed to generate so: %s", so_file);
1115 func = load_func_from_so(so_file, funcname, unit);
1117 remove_so_file(so_file, unit);
1120 verbose(1,
"JIT success (%.1fms): %s@%s:%ld -> %s",
1121 end_time - start_time, iseq_label, iseq_path, iseq_lineno, c_file);
1137static void mjit_copy_job_handler(
void *data);
1153 CRITICAL_SECTION_START(3,
"in mjit_copy_cache_from_main_thread");
1155 CRITICAL_SECTION_FINISH(3,
"in mjit_copy_cache_from_main_thread");
1160 CRITICAL_SECTION_START(3,
"in mjit_copy_cache_from_main_thread");
1167 CRITICAL_SECTION_FINISH(3,
"in mjit_copy_cache_from_main_thread");
1170 mjit_copy_job_handler((
void *)job);
1173 CRITICAL_SECTION_START(3,
"in MJIT copy job wait");
1177 while (!job->
finish_p && !stop_worker_p) {
1179 verbose(3,
"Getting wakeup from client");
1181 CRITICAL_SECTION_FINISH(3,
"in MJIT copy job wait");
1184 CRITICAL_SECTION_START(3,
"in mjit_copy_cache_from_main_thread");
1194 CRITICAL_SECTION_FINISH(3,
"in mjit_copy_cache_from_main_thread");
1211 CRITICAL_SECTION_START(3,
"in worker to update worker_stopped");
1212 worker_stopped =
true;
1213 verbose(3,
"Sending wakeup signal to client in a mjit-worker");
1215 CRITICAL_SECTION_FINISH(3,
"in worker to update worker_stopped");
1220 while (!stop_worker_p) {
1224 CRITICAL_SECTION_START(3,
"in worker dequeue");
1227 verbose(3,
"Getting wakeup from client");
1229 unit = get_from_list(&unit_queue);
1230 CRITICAL_SECTION_FINISH(3,
"in worker dequeue");
1237 CRITICAL_SECTION_START(3,
"in jit func replace");
1239 verbose(3,
"Waiting wakeup from GC");
1244 add_to_list(unit, &active_units);
1252 CRITICAL_SECTION_FINISH(3,
"in jit func replace");
1258 compact_all_jit_code();
1265 worker_stopped =
true;
void(* rb_postponed_job_func_t)(void *arg)
struct rb_encoding_entry * list
char str[HTML_ESCAPE_MAX_LEN+1]
rb_pid_t ruby_waitpid_locked(rb_vm_t *, rb_pid_t, int *status, int options, rb_nativethread_cond_t *cond)
verbose(int level, const char *format,...)
int rb_workqueue_register(unsigned flags, rb_postponed_job_func_t, void *)
bool mjit_valid_class_serial_p(rb_serial_t class_serial)
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
#define append_str2(p, str, len)
void rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
struct mjit_options mjit_opts
bool mjit_copy_cache_from_main_thread(const rb_iseq_t *iseq, struct rb_call_cache *cc_entries, union iseq_inline_storage_entry *is_entries)
mjit_warning(const char *format,...)
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
#define GCC_NOSTDLIB_FLAGS
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
#define MJIT_ATOMIC_SET(var, val)
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
#define append_lit(p, str)
struct rb_call_cache * cc_entries
union iseq_inline_storage_entry * is_entries
VALUE(* jit_func)(struct rb_execution_context_struct *, struct rb_control_frame_struct *)
rb_iseq_location_t location
long unsigned total_calls
struct rb_mjit_unit * jit_unit
struct rb_iseq_constant_body * body
struct rb_mjit_compile_info compile_info
rb_nativethread_lock_t waitpid_lock
MJIT_FUNC_EXPORTED HANDLE rb_w32_start_process(const char *abspath, char *const *argv, int out_fd)
int gettimeofday(struct timeval *, struct timezone *)
rb_pid_t waitpid(rb_pid_t, int *, int)