72 #define __EXTENSIONS__ 1
94 #ifdef HAVE_SYS_PARAM_H
95 # include <sys/param.h>
100 #undef strdup // ruby_strdup may trigger GC
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_"
192 static int current_unit_num;
210 static bool stop_worker_p;
212 static bool worker_stopped;
215 static char *tmp_dir;
218 static VALUE valid_class_serials;
221 static const char *cc_path;
223 static const char **cc_common_args;
225 static char **cc_added_args;
227 static char *pch_file;
236 static char *header_file;
241 static 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 // empty
264 static const char *
const CC_COMMON_ARGS[] = {
275 static const char *
const CC_LINKER_ARGS[] = {
276 #if defined __GNUC__ && !defined __clang__ && !defined(__OpenBSD__)
282 static 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';
352 #if USE_DEBUG_COUNTER
364 remove_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);
424 CRITICAL_SECTION_START(
int level,
const char *msg)
426 verbose(level,
"Locking %s", msg);
428 verbose(level,
"Locked %s", msg);
434 CRITICAL_SECTION_FINISH(
int level,
const char *msg)
436 verbose(level,
"Unlocked %s", msg);
441 sprint_uniq_filename(
char *
str,
size_t size,
unsigned long id,
const char *prefix,
const char *suffix)
448 double 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);
511 args_len(
char *
const *args)
515 for (
i = 0; (args[
i]) !=
NULL;
i++)
523 form_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) {
555 start_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);
616 exec_process(
const char *
path,
char *
const argv[])
618 int stat, exit_code = -2;
658 remove_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))
677 compile_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[] = {
759 "-o", pch_file, header_file,
763 verbose(2,
"Creating precompiled header");
764 char **args = form_args(4, cc_common_args,
CC_CODEFLAG_ARGS, cc_added_args, rest_args);
766 mjit_warning(
"making precompiled header failed on forming args");
767 CRITICAL_SECTION_START(3,
"in make_pch");
769 CRITICAL_SECTION_FINISH(3,
"in make_pch");
773 int exit_code = exec_process(cc_path, args);
776 CRITICAL_SECTION_START(3,
"in make_pch");
777 if (exit_code == 0) {
781 mjit_warning(
"Making precompiled header failed on compilation. Stopping MJIT worker...");
786 CRITICAL_SECTION_FINISH(3,
"in make_pch");
791 compile_c_to_o(
const char *c_file,
const char *
o_file)
793 const char *files[] = {
796 "-include-pch", pch_file,
801 char **args = form_args(5, cc_common_args,
CC_CODEFLAG_ARGS, cc_added_args, files, CC_LINKER_ARGS);
805 int exit_code = exec_process(cc_path, args);
809 verbose(2,
"compile_c_to_o: compile error: %d", exit_code);
810 return exit_code == 0;
815 link_o_to_so(
const char **o_files,
const char *so_file)
817 const char *options[] = {
826 options, o_files, CC_LIBS, CC_DLDFLAGS_ARGS, CC_LINKER_ARGS);
830 int exit_code = exec_process(cc_path, args);
834 verbose(2,
"link_o_to_so: link error: %d", exit_code);
835 return exit_code == 0;
841 compact_all_jit_code(
void)
843 # ifndef _WIN32 // This requires header transformation but we don't transform header on Windows for now
845 double start_time, end_time;
846 static const char so_ext[] =
DLEXT;
848 const char **o_files;
853 if (unit ==
NULL)
return;
854 unit->
id = current_unit_num++;
855 sprint_uniq_filename(so_file, (
int)
sizeof(so_file), unit->
id,
MJIT_TMP_PREFIX, so_ext);
858 o_files =
alloca(
sizeof(
char *) * (active_units.
length + 1));
860 CRITICAL_SECTION_START(3,
"in compact_all_jit_code to keep .o files");
866 start_time = real_ms_time();
867 bool success = link_o_to_so(o_files, so_file);
868 end_time = real_ms_time();
874 CRITICAL_SECTION_FINISH(3,
"in compact_all_jit_code to keep .o files");
877 void *
handle = dlopen(so_file, RTLD_NOW);
879 mjit_warning(
"failure in loading code from compacted '%s': %s", so_file, dlerror());
886 add_to_list(unit, &compact_units);
889 remove_so_file(so_file, unit);
891 CRITICAL_SECTION_START(3,
"in compact_all_jit_code to read list");
897 if ((func = dlsym(
handle, funcname)) ==
NULL) {
898 mjit_warning(
"skipping to reload '%s' from '%s': %s", funcname, so_file, dlerror());
907 CRITICAL_SECTION_FINISH(3,
"in compact_all_jit_code to read list");
908 verbose(1,
"JIT compaction (%.1fms): Compacted %d methods -> %s", end_time - start_time, active_units.
length, so_file);
912 verbose(1,
"JIT compaction failure (%.1fms): Failed to compact methods", end_time - start_time);
920 load_func_from_so(
const char *so_file,
const char *funcname,
struct rb_mjit_unit *unit)
924 handle = dlopen(so_file, RTLD_NOW);
926 mjit_warning(
"failure in loading code from '%s': %s", so_file, dlerror());
930 func = dlsym(
handle, funcname);
937 header_name_end(
const char *s)
939 const char *e = s +
strlen(s);
940 # ifdef __GNUC__ // don't chomp .pch for mswin
941 static const char suffix[] =
".gch";
944 if (e > s+
sizeof(suffix)-1 &&
strcmp(e-
sizeof(suffix)+1, suffix) == 0) {
945 e -=
sizeof(suffix)-1;
954 compile_prelude(
FILE *
f)
956 #ifndef __clang__ // -include-pch is used for Clang
957 const char *s = pch_file;
958 const char *e = header_name_end(s);
973 fprintf(
f,
"void _pei386_runtime_relocator(void){}\n");
974 fprintf(
f,
"int __stdcall DllMainCRTStartup(void* hinstDLL, unsigned int fdwReason, void* lpvReserved) { return 1; }\n");
983 char c_file_buff[
MAXPATHLEN], *c_file = c_file_buff, *so_file, funcname[35];
987 double start_time, end_time;
988 int c_file_len = (
int)
sizeof(c_file_buff);
989 static const char c_ext[] =
".c";
990 static const char so_ext[] =
DLEXT;
991 const int access_mode =
995 O_WRONLY|O_EXCL|O_CREAT;
997 static const char o_ext[] =
".o";
1001 c_file_len = sprint_uniq_filename(c_file_buff, c_file_len, unit->
id,
MJIT_TMP_PREFIX, c_ext);
1002 if (c_file_len >= (
int)
sizeof(c_file_buff)) {
1004 c_file =
alloca(c_file_len);
1005 c_file_len = sprint_uniq_filename(c_file, c_file_len, unit->
id,
MJIT_TMP_PREFIX, c_ext);
1010 o_file =
alloca(c_file_len -
sizeof(c_ext) +
sizeof(o_ext));
1012 memcpy(&
o_file[c_file_len -
sizeof(c_ext)], o_ext,
sizeof(o_ext));
1014 so_file =
alloca(c_file_len -
sizeof(c_ext) +
sizeof(so_ext));
1015 memcpy(so_file, c_file, c_file_len -
sizeof(c_ext));
1016 memcpy(&so_file[c_file_len -
sizeof(c_ext)], so_ext,
sizeof(so_ext));
1024 verbose(1,
"Failed to fopen '%s', giving up JIT for it (%s)", c_file,
strerror(e));
1032 CRITICAL_SECTION_START(3,
"before mjit_compile to wait GC finish");
1034 verbose(3,
"Waiting wakeup from GC");
1042 remove_file(c_file);
1048 CRITICAL_SECTION_FINISH(3,
"before mjit_compile to wait GC finish");
1054 long iseq_lineno = 0;
1063 verbose(2,
"start compilation: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
1064 fprintf(
f,
"/* %s@%s:%ld */\n\n", iseq_label, iseq_path, iseq_lineno);
1068 CRITICAL_SECTION_START(3,
"after mjit_compile to wakeup client for GC");
1070 verbose(3,
"Sending wakeup signal to client in a mjit-worker for GC");
1072 CRITICAL_SECTION_FINISH(3,
"in worker to wakeup client for GC");
1077 remove_file(c_file);
1078 verbose(1,
"JIT failure: %s@%s:%ld -> %s", iseq_label, iseq_path, iseq_lineno, c_file);
1082 start_time = real_ms_time();
1084 success = compile_c_to_so(c_file, so_file);
1087 if ((success = compile_c_to_o(c_file,
o_file)) !=
false) {
1088 success = link_o_to_so((
const char *[]){
o_file,
NULL }, so_file);
1098 end_time = real_ms_time();
1101 remove_file(c_file);
1103 verbose(2,
"Failed to generate so: %s", so_file);
1107 func = load_func_from_so(so_file, funcname, unit);
1109 remove_so_file(so_file, unit);
1112 verbose(1,
"JIT success (%.1fms): %s@%s:%ld -> %s",
1113 end_time - start_time, iseq_label, iseq_path, iseq_lineno, c_file);
1129 static void mjit_copy_job_handler(
void *data);
1145 CRITICAL_SECTION_START(3,
"in mjit_copy_cache_from_main_thread");
1147 CRITICAL_SECTION_FINISH(3,
"in mjit_copy_cache_from_main_thread");
1152 CRITICAL_SECTION_START(3,
"in mjit_copy_cache_from_main_thread");
1159 CRITICAL_SECTION_FINISH(3,
"in mjit_copy_cache_from_main_thread");
1162 mjit_copy_job_handler((
void *)job);
1165 CRITICAL_SECTION_START(3,
"in MJIT copy job wait");
1169 while (!job->finish_p && !stop_worker_p) {
1171 verbose(3,
"Getting wakeup from client");
1173 CRITICAL_SECTION_FINISH(3,
"in MJIT copy job wait");
1176 CRITICAL_SECTION_START(3,
"in mjit_copy_cache_from_main_thread");
1177 bool success_p = job->finish_p;
1180 job->finish_p =
true;
1183 if (job->iseq ==
NULL)
1186 CRITICAL_SECTION_FINISH(3,
"in mjit_copy_cache_from_main_thread");
1203 CRITICAL_SECTION_START(3,
"in worker to update worker_stopped");
1204 worker_stopped =
true;
1205 verbose(3,
"Sending wakeup signal to client in a mjit-worker");
1207 CRITICAL_SECTION_FINISH(3,
"in worker to update worker_stopped");
1212 while (!stop_worker_p) {
1216 CRITICAL_SECTION_START(3,
"in worker dequeue");
1219 verbose(3,
"Getting wakeup from client");
1221 unit = get_from_list(&unit_queue);
1222 CRITICAL_SECTION_FINISH(3,
"in worker dequeue");
1229 CRITICAL_SECTION_START(3,
"in jit func replace");
1231 verbose(3,
"Waiting wakeup from GC");
1236 add_to_list(unit, &active_units);
1244 CRITICAL_SECTION_FINISH(3,
"in jit func replace");
1250 compact_all_jit_code();
1257 worker_stopped =
true;