Feature #14489 ยป 14489.patch
| Makefile.in | ||
|---|---|---|
| 
     update-coverage: update-simplecov update-simplecov-html update-doclie 
   | 
||
| 
     INSNS	= opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \ 
   | 
||
| 
     	  vmtc.inc vm.inc mjit_compile.inc 
   | 
||
| 
     	  vmtc.inc vm.inc mjit_compile.inc mjit_var.inc 
   | 
||
| 
     $(INSNS): $(srcdir)/insns.def vm_opts.h \ 
   | 
||
| 
     	  $(srcdir)/defs/opt_operand.def $(srcdir)/defs/opt_insn_unif.def \ 
   | 
||
| common.mk | ||
|---|---|---|
| 
     $(srcs_vpath)mjit_compile.inc: $(srcdir)/tool/ruby_vm/views/mjit_compile.inc.erb \ 
   | 
||
| 
       $(srcdir)/tool/ruby_vm/views/_mjit_compile_insn.erb $(srcdir)/tool/ruby_vm/views/_mjit_compile_send.erb \ 
   | 
||
| 
       $(srcdir)/tool/ruby_vm/views/_mjit_compile_insn_body.erb $(srcdir)/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb 
   | 
||
| 
     $(srcs_vpath)mjit_var.inc: $(srcdir)/tool/ruby_vm/views/mjit_var.inc.erb 
   | 
||
| 
     common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \ 
   | 
||
| 
     	     srcs-lib srcs-ext incs 
   | 
||
| ... | ... | |
| 
     mjit_compile.$(OBJEXT): {$(VPATH)}mjit.h 
   | 
||
| 
     mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.c 
   | 
||
| 
     mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.inc 
   | 
||
| 
     mjit_compile.$(OBJEXT): {$(VPATH)}mjit_var.inc 
   | 
||
| 
     mjit_compile.$(OBJEXT): {$(VPATH)}vm_core.h 
   | 
||
| 
     load.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h 
   | 
||
| 
     load.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h 
   | 
||
| miniinit.c | ||
|---|---|---|
| 
     #include "ruby/encoding.h" 
   | 
||
| 
     /* loadpath.c */ 
   | 
||
| 
     const char ruby_exec_prefix[] = ""; 
   | 
||
| 
     const char ruby_exec_prefix[] = "./"; 
   | 
||
| 
     const char ruby_initial_load_paths[] = ""; 
   | 
||
| 
     /* localeinit.c */ 
   | 
||
| mjit.c | ||
|---|---|---|
| 
     #ifdef HAVE_FCNTL_H 
   | 
||
| 
     #include <fcntl.h> 
   | 
||
| 
     #endif 
   | 
||
| 
     #include <libgen.h> 
   | 
||
| 
     extern void rb_native_mutex_lock(rb_nativethread_lock_t *lock); 
   | 
||
| 
     extern void rb_native_mutex_unlock(rb_nativethread_lock_t *lock); 
   | 
||
| ... | ... | |
| 
     static int in_gc; 
   | 
||
| 
     /* True when JIT is working.  */ 
   | 
||
| 
     static int in_jit; 
   | 
||
| 
     /* Object cache directory */ 
   | 
||
| 
     static VALUE o_cache_dir = Qfalse; 
   | 
||
| 
     /* Defined in the client thread before starting MJIT threads:  */ 
   | 
||
| 
     /* Used C compiler path.  */ 
   | 
||
| ... | ... | |
| 
         return exit_code == 0; 
   | 
||
| 
     } 
   | 
||
| 
     /* Compile C file to o. It returns 1 if it succeeds. */ 
   | 
||
| 
     static int 
   | 
||
| 
     compile_c_to_o(const char *c_file, const char *o_file) 
   | 
||
| 
     { 
   | 
||
| 
         int exit_code; 
   | 
||
| 
         const char *files[] = { 
   | 
||
| 
     #ifdef __clang__ 
   | 
||
| 
             "-include-pch", NULL, 
   | 
||
| 
     #endif 
   | 
||
| 
             "-c", 
   | 
||
| 
     #ifndef _MSC_VER 
   | 
||
| 
             "-o", 
   | 
||
| 
     #endif 
   | 
||
| 
             NULL, NULL, NULL}; 
   | 
||
| 
         const char *libs[] = { 
   | 
||
| 
     #ifdef _WIN32 
   | 
||
| 
     # ifdef _MSC_VER 
   | 
||
| 
             MJIT_LIBS 
   | 
||
| 
             "-link", 
   | 
||
| 
             libruby_installed, 
   | 
||
| 
             libruby_build, 
   | 
||
| 
     # else 
   | 
||
| 
             /* Look for ruby.dll.a in build and install directories. */ 
   | 
||
| 
             libruby_installed, 
   | 
||
| 
             libruby_build, 
   | 
||
| 
             MJIT_LIBS 
   | 
||
| 
             "-lmsvcrt", 
   | 
||
| 
             "-lgcc", 
   | 
||
| 
     # endif 
   | 
||
| 
     #endif 
   | 
||
| 
             NULL}; 
   | 
||
| 
         char **args; 
   | 
||
| 
     #ifdef _MSC_VER 
   | 
||
| 
         char *p; 
   | 
||
| 
         int olen; 
   | 
||
| 
     #endif 
   | 
||
| 
         files[numberof(files)-2] = c_file; 
   | 
||
| 
     #ifdef _MSC_VER 
   | 
||
| 
         olen = strlen(o_file); 
   | 
||
| 
         files[0] = p = xmalloc(rb_strlen_lit("-Fe") + olen + 1); 
   | 
||
| 
         p = append_lit(p, "-Fe"); 
   | 
||
| 
         p = append_str2(p, o_file, olen); 
   | 
||
| 
         *p = '\0'; 
   | 
||
| 
     #else 
   | 
||
| 
     # ifdef __clang__ 
   | 
||
| 
         files[1] = pch_file; 
   | 
||
| 
     # endif 
   | 
||
| 
         files[numberof(files)-3] = o_file; 
   | 
||
| 
     #endif 
   | 
||
| 
         args = form_args(5, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, 
   | 
||
| 
                          files, libs, CC_DLDFLAGS_ARGS); 
   | 
||
| 
         if (args == NULL) 
   | 
||
| 
             return FALSE; 
   | 
||
| 
         exit_code = exec_process(cc_path, args); 
   | 
||
| 
         xfree(args); 
   | 
||
| 
     #ifdef _MSC_VER 
   | 
||
| 
         xfree((char *)files[0]); 
   | 
||
| 
     #endif 
   | 
||
| 
         if (exit_code != 0) 
   | 
||
| 
             verbose(2, "compile_c_to_o: compile error: %d", exit_code); 
   | 
||
| 
         return exit_code == 0; 
   | 
||
| 
     } 
   | 
||
| 
     /* Compile var C file and o to so. It returns 1 if it succeeds. */ 
   | 
||
| 
     static int 
   | 
||
| 
     compile_var_and_o_to_so(const char *var_file, const char *o_file, const char *so_file) 
   | 
||
| 
     { 
   | 
||
| 
         int exit_code; 
   | 
||
| 
         const char *files[] = { 
   | 
||
| 
     #ifdef __clang__ 
   | 
||
| 
             "-include-pch", NULL, 
   | 
||
| 
     #endif 
   | 
||
| 
     #ifndef _MSC_VER 
   | 
||
| 
             "-o", 
   | 
||
| 
     #endif 
   | 
||
| 
             NULL, NULL, NULL, NULL}; 
   | 
||
| 
         const char *libs[] = { 
   | 
||
| 
     #ifdef _WIN32 
   | 
||
| 
     # ifdef _MSC_VER 
   | 
||
| 
             MJIT_LIBS 
   | 
||
| 
             "-link", 
   | 
||
| 
             libruby_installed, 
   | 
||
| 
             libruby_build, 
   | 
||
| 
     # else 
   | 
||
| 
             /* Look for ruby.dll.a in build and install directories. */ 
   | 
||
| 
             libruby_installed, 
   | 
||
| 
             libruby_build, 
   | 
||
| 
             MJIT_LIBS 
   | 
||
| 
             "-lmsvcrt", 
   | 
||
| 
             "-lgcc", 
   | 
||
| 
     # endif 
   | 
||
| 
     #endif 
   | 
||
| 
             NULL}; 
   | 
||
| 
         char **args; 
   | 
||
| 
     #ifdef _MSC_VER 
   | 
||
| 
         char *p; 
   | 
||
| 
         int solen; 
   | 
||
| 
     #endif 
   | 
||
| 
         files[numberof(files)-3] = var_file; 
   | 
||
| 
         files[numberof(files)-2] = o_file; 
   | 
||
| 
     #ifdef _MSC_VER 
   | 
||
| 
         solen = strlen(so_file); 
   | 
||
| 
         files[0] = p = xmalloc(rb_strlen_lit("-Fe") + solen + 1); 
   | 
||
| 
         p = append_lit(p, "-Fe"); 
   | 
||
| 
         p = append_str2(p, so_file, solen); 
   | 
||
| 
         *p = '\0'; 
   | 
||
| 
     #else 
   | 
||
| 
     # ifdef __clang__ 
   | 
||
| 
         files[1] = pch_file; 
   | 
||
| 
     # endif 
   | 
||
| 
         files[numberof(files)-4] = so_file; 
   | 
||
| 
     #endif 
   | 
||
| 
         args = form_args(5, CC_LDSHARED_ARGS, CC_CODEFLAG_ARGS, 
   | 
||
| 
                          files, libs, CC_DLDFLAGS_ARGS); 
   | 
||
| 
         if (args == NULL) 
   | 
||
| 
             return FALSE; 
   | 
||
| 
         exit_code = exec_process(cc_path, args); 
   | 
||
| 
         xfree(args); 
   | 
||
| 
     #ifdef _MSC_VER 
   | 
||
| 
         xfree((char *)files[0]); 
   | 
||
| 
     #endif 
   | 
||
| 
         if (exit_code != 0) 
   | 
||
| 
             verbose(2, "compile_c_to_so: compile error: %d", exit_code); 
   | 
||
| 
         return exit_code == 0; 
   | 
||
| 
     } 
   | 
||
| 
     static void * 
   | 
||
| 
     load_func_from_so(const char *so_file, const char *funcname, struct rb_mjit_unit *unit) 
   | 
||
| 
     { 
   | 
||
| ... | ... | |
| 
                 RSTRING_PTR(rb_iseq_path(unit->iseq)), FIX2INT(unit->iseq->body->location.first_lineno), c_file); 
   | 
||
| 
     } 
   | 
||
| 
     static int 
   | 
||
| 
     mkparentdir(char *path) 
   | 
||
| 
     { 
   | 
||
| 
         struct stat sb; 
   | 
||
| 
         char *parent = dirname(strdupa(path)); 
   | 
||
| 
         if (!strcmp(parent, path)) { 
   | 
||
| 
             return 1; 
   | 
||
| 
         } 
   | 
||
| 
         stat(parent, &sb); 
   | 
||
| 
         if (S_ISDIR(sb.st_mode)) { 
   | 
||
| 
             return 0; 
   | 
||
| 
         } else { 
   | 
||
| 
             if (mkparentdir(parent)) { 
   | 
||
| 
                 return 1; 
   | 
||
| 
             } 
   | 
||
| 
             return mkdir(parent, 0 
   | 
||
| 
               | S_IRUSR | S_IWUSR | S_IXUSR 
   | 
||
| 
               | S_IRGRP | S_IWGRP | S_IXGRP 
   | 
||
| 
               | S_IROTH | S_IXOTH | S_IXOTH 
   | 
||
| 
             ); 
   | 
||
| 
         } 
   | 
||
| 
     } 
   | 
||
| 
     /* Compile ISeq in UNIT and return function pointer of JIT-ed code. 
   | 
||
| 
        It may return NOT_COMPILABLE_JIT_ISEQ_FUNC if something went wrong. */ 
   | 
||
| 
     static mjit_func_t 
   | 
||
| 
     convert_unit_to_func(struct rb_mjit_unit *unit) 
   | 
||
| 
     { 
   | 
||
| 
         char c_file_buff[70], *c_file = c_file_buff, *so_file, funcname[35]; 
   | 
||
| 
         char c_file_buff[70], *c_file = c_file_buff, *so_file, funcname[35], *var_file; 
   | 
||
| 
         int success; 
   | 
||
| 
         int fd; 
   | 
||
| 
         char create_o_cache = 0; 
   | 
||
| 
         FILE *f; 
   | 
||
| 
         void *func; 
   | 
||
| 
         double start_time, end_time; 
   | 
||
| 
         int c_file_len = (int)sizeof(c_file_buff); 
   | 
||
| 
         static const char c_ext[] = ".c"; 
   | 
||
| 
         static const char so_ext[] = DLEXT; 
   | 
||
| 
         char *o_file = NULL; 
   | 
||
| 
         const int access_mode = 
   | 
||
| 
     #ifdef O_BINARY 
   | 
||
| 
             O_BINARY| 
   | 
||
| 
     #endif 
   | 
||
| 
             O_WRONLY|O_EXCL|O_CREAT; 
   | 
||
| 
         if (mjit_opts.cache_objs) { 
   | 
||
| 
             struct stat o_st, r_st; 
   | 
||
| 
             char *rb_file = RSTRING_PTR(rb_iseq_realpath(unit->iseq)); 
   | 
||
| 
             if (stat(rb_file, &r_st)) { 
   | 
||
| 
                 o_file = NULL; 
   | 
||
| 
                 verbose(2, "Can't get stat of '%s'", rb_file); 
   | 
||
| 
             } else { 
   | 
||
| 
                 o_file = malloc(RSTRING_LEN(o_cache_dir) + strlen(rb_file) + 15); 
   | 
||
| 
                 sprintf(o_file, "%s%s.%d.o", RSTRING_PTR(o_cache_dir), rb_file, FIX2INT(unit->iseq->body->location.first_lineno)); 
   | 
||
| 
                 verbose(2, "Check Cache '%s'", o_file); 
   | 
||
| 
                 if (mkparentdir(o_file)) { 
   | 
||
| 
                     verbose(2, "Can't create directory of '%s'", o_file); 
   | 
||
| 
                     o_file = NULL; 
   | 
||
| 
                 } else if (stat(o_file, &o_st)) { 
   | 
||
| 
                     if (errno == ENOENT) { 
   | 
||
| 
                         create_o_cache = 1; 
   | 
||
| 
                         verbose(2, "Create '%s'", o_file); 
   | 
||
| 
                     } else { 
   | 
||
| 
                         o_file = NULL; 
   | 
||
| 
                         verbose(2, "Can't get stat of '%s'", o_file); 
   | 
||
| 
                     } 
   | 
||
| 
                 } else if(o_st.st_mtime > r_st.st_mtime) { 
   | 
||
| 
                     verbose(2, "Use cache '%s'", o_file); 
   | 
||
| 
                 } else { 
   | 
||
| 
                     create_o_cache = 1; 
   | 
||
| 
                     verbose(2, "Update '%s'", o_file); 
   | 
||
| 
                 } 
   | 
||
| 
             } 
   | 
||
| 
         } 
   | 
||
| 
         c_file_len = sprint_uniq_filename(c_file_buff, c_file_len, unit->id, MJIT_TMP_PREFIX, c_ext); 
   | 
||
| 
         if (c_file_len >= (int)sizeof(c_file_buff)) { 
   | 
||
| 
             ++c_file_len; 
   | 
||
| ... | ... | |
| 
         memcpy(&so_file[c_file_len - sizeof(c_ext)], so_ext, sizeof(so_ext)); 
   | 
||
| 
         sprintf(funcname, "_mjit%d", unit->id); 
   | 
||
| 
         if (o_file) { 
   | 
||
| 
             static const char var_suffix[] = "_var"; 
   | 
||
| 
             var_file = alloca(c_file_len + sizeof(var_suffix)); 
   | 
||
| 
             memcpy(var_file, c_file, c_file_len - sizeof(c_ext)); 
   | 
||
| 
             memcpy(&var_file[c_file_len - sizeof(c_ext)], var_suffix, sizeof(var_suffix)); 
   | 
||
| 
             memcpy(&var_file[c_file_len - sizeof(c_ext) + sizeof(var_suffix) - 1], c_ext, sizeof(c_ext)); 
   | 
||
| 
             verbose(2, "Create '%s'", var_file); 
   | 
||
| 
             fd = rb_cloexec_open(var_file, access_mode, 0600); 
   | 
||
| 
             if (fd < 0 || (f = fdopen(fd, "w")) == NULL) { 
   | 
||
| 
                 int e = errno; 
   | 
||
| 
                 if (fd >= 0) (void)close(fd); 
   | 
||
| 
                 verbose(1, "Failed to fopen '%s', giving up JIT for it (%s)", var_file, strerror(e)); 
   | 
||
| 
                 return (mjit_func_t)NOT_COMPILABLE_JIT_ISEQ_FUNC; 
   | 
||
| 
             } 
   | 
||
| 
     #ifdef __clang__ 
   | 
||
| 
         /* -include-pch is used for Clang */ 
   | 
||
| 
     #else 
   | 
||
| 
         { 
   | 
||
| 
     # ifdef __GNUC__ 
   | 
||
| 
             const char *s = pch_file; 
   | 
||
| 
     # else 
   | 
||
| 
             const char *s = header_file; 
   | 
||
| 
     # endif 
   | 
||
| 
             const char *e = header_name_end(s); 
   | 
||
| 
             fprintf(f, "#include \""); 
   | 
||
| 
             /* print pch_file except .gch */ 
   | 
||
| 
             for (; s < e; s++) { 
   | 
||
| 
                 switch(*s) { 
   | 
||
| 
                   case '\\': case '"': 
   | 
||
| 
                     fputc('\\', f); 
   | 
||
| 
                 } 
   | 
||
| 
                 fputc(*s, f); 
   | 
||
| 
             } 
   | 
||
| 
             fprintf(f, "\"\n"); 
   | 
||
| 
         } 
   | 
||
| 
     #endif 
   | 
||
| 
             mjit_compile_var(f, unit->iseq->body, funcname); 
   | 
||
| 
             fclose(f); 
   | 
||
| 
         } 
   | 
||
| 
         if (!o_file || create_o_cache) { 
   | 
||
| 
         fd = rb_cloexec_open(c_file, access_mode, 0600); 
   | 
||
| 
         if (fd < 0 || (f = fdopen(fd, "w")) == NULL) { 
   | 
||
| 
             int e = errno; 
   | 
||
| ... | ... | |
| 
             verbose(2, "start compile: %s@%s:%d -> %s", label, path, lineno, c_file); 
   | 
||
| 
             fprintf(f, "/* %s@%s:%d */\n\n", label, path, lineno); 
   | 
||
| 
         } 
   | 
||
| 
         success = mjit_compile(f, unit->iseq->body, funcname); 
   | 
||
| 
         success = mjit_compile(f, unit->iseq->body, funcname, create_o_cache); 
   | 
||
| 
         fclose(f); 
   | 
||
| 
         /* release blocking mjit_gc_start_hook */ 
   | 
||
| 
         CRITICAL_SECTION_START(3, "after mjit_compile to wakeup client for GC"); 
   | 
||
| ... | ... | |
| 
         rb_native_cond_signal(&mjit_client_wakeup); 
   | 
||
| 
         CRITICAL_SECTION_FINISH(3, "in worker to wakeup client for GC"); 
   | 
||
| 
         fclose(f); 
   | 
||
| 
         if (!success) { 
   | 
||
| 
             if (!mjit_opts.save_temps) 
   | 
||
| 
                 remove(c_file); 
   | 
||
| 
             print_jit_result("failure", unit, 0, c_file); 
   | 
||
| 
             return (mjit_func_t)NOT_COMPILABLE_JIT_ISEQ_FUNC; 
   | 
||
| 
         } 
   | 
||
| 
         } 
   | 
||
| 
         start_time = real_ms_time(); 
   | 
||
| 
         success = compile_c_to_so(c_file, so_file); 
   | 
||
| 
         if (o_file) { 
   | 
||
| 
             if (create_o_cache) { 
   | 
||
| 
                 success = compile_c_to_o(c_file, o_file); 
   | 
||
| 
                 success = compile_var_and_o_to_so(var_file, o_file, so_file) && success; 
   | 
||
| 
             } else { 
   | 
||
| 
                 success = compile_var_and_o_to_so(var_file, o_file, so_file); 
   | 
||
| 
             } 
   | 
||
| 
             free(o_file); 
   | 
||
| 
         } else { 
   | 
||
| 
             success = compile_c_to_so(c_file, so_file); 
   | 
||
| 
         } 
   | 
||
| 
         end_time = real_ms_time(); 
   | 
||
| 
         if (!mjit_opts.save_temps) 
   | 
||
| ... | ... | |
| 
             rb_native_cond_destroy(&mjit_gc_wakeup); 
   | 
||
| 
             verbose(1, "Failure in MJIT thread initialization\n"); 
   | 
||
| 
         } 
   | 
||
| 
         /* Initialize Object cache directory */ 
   | 
||
| 
         if (mjit_opts.cache_objs) { 
   | 
||
| 
             o_cache_dir = rb_default_home_dir(rb_str_new(0, 0)); 
   | 
||
| 
             rb_str_concat(o_cache_dir, rb_str_new2("/")); 
   | 
||
| 
             rb_str_concat(o_cache_dir, rb_str_new2(".cache")); 
   | 
||
| 
             rb_str_concat(o_cache_dir, rb_str_new2("/")); 
   | 
||
| 
             rb_str_concat(o_cache_dir, rb_str_new2("ruby-mjit")); 
   | 
||
| 
             rb_str_concat(o_cache_dir, rb_str_new2("/")); 
   | 
||
| 
         } 
   | 
||
| 
     } 
   | 
||
| 
     /* Finish the threads processing units and creating PCH, finalize 
   | 
||
| ... | ... | |
| 
         if (!mjit_init_p) 
   | 
||
| 
             return; 
   | 
||
| 
         RUBY_MARK_ENTER("mjit"); 
   | 
||
| 
         RUBY_MARK_UNLESS_NULL(o_cache_dir); 
   | 
||
| 
         CRITICAL_SECTION_START(4, "mjit_mark"); 
   | 
||
| 
         for (node = unit_queue.head; node != NULL; node = node->next) { 
   | 
||
| 
             if (node->unit->iseq) { /* ISeq is still not GCed */ 
   | 
||
| mjit.h | ||
|---|---|---|
| 
         /* Maximal permitted number of iseq JIT codes in a MJIT memory 
   | 
||
| 
            cache.  */ 
   | 
||
| 
         int max_cache_size; 
   | 
||
| 
         char cache_objs; /* flag of MJIT cache object files */ 
   | 
||
| 
     }; 
   | 
||
| 
     typedef VALUE (*mjit_func_t)(rb_execution_context_t *, rb_control_frame_t *); 
   | 
||
| ... | ... | |
| 
     extern mjit_func_t mjit_get_iseq_func(const struct rb_iseq_constant_body *body); 
   | 
||
| 
     RUBY_SYMBOL_EXPORT_END 
   | 
||
| 
     extern int mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname); 
   | 
||
| 
     extern int mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname, const char create_o_cache); 
   | 
||
| 
     extern void mjit_compile_var(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname); 
   | 
||
| 
     extern void mjit_init(struct mjit_options *opts); 
   | 
||
| 
     extern void mjit_finish(void); 
   | 
||
| 
     extern void mjit_gc_start_hook(void); 
   | 
||
| mjit_compile.c | ||
|---|---|---|
| 
     } 
   | 
||
| 
     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, const char create_o_cache); 
   | 
||
| 
     /* 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. 
   | 
||
| ... | ... | |
| 
        does not have it can be compiled as usual. */ 
   | 
||
| 
     static unsigned int 
   | 
||
| 
     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, const char create_o_cache) 
   | 
||
| 
     { 
   | 
||
| 
         unsigned int next_pos = pos + insn_len(insn); 
   | 
||
| ... | ... | |
| 
        called multiple times for each branch.  */ 
   | 
||
| 
     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, const char create_o_cache) 
   | 
||
| 
     { 
   | 
||
| 
         int insn; 
   | 
||
| 
         struct compile_branch branch; 
   | 
||
| ... | ... | |
| 
             status->compiled_for_pos[pos] = TRUE; 
   | 
||
| 
             fprintf(f, "\nlabel_%d: /* %s */\n", pos, insn_name(insn)); 
   | 
||
| 
             pos = compile_insn(f, body, insn, body->iseq_encoded + (pos+1), pos, status, &branch); 
   | 
||
| 
             pos = compile_insn(f, body, insn, body->iseq_encoded + (pos+1), pos, status, &branch, create_o_cache); 
   | 
||
| 
             if (status->success && branch.stack_size > body->stack_max) { 
   | 
||
| 
                 if (mjit_opts.warnings || mjit_opts.verbose) 
   | 
||
| 
                     fprintf(stderr, "MJIT warning: JIT stack exceeded its max\n"); 
   | 
||
| ... | ... | |
| 
     /* Compile ISeq to C code in F.  It returns 1 if it succeeds to compile. */ 
   | 
||
| 
     int 
   | 
||
| 
     mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname) 
   | 
||
| 
     mjit_compile(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname, const char create_o_cache) 
   | 
||
| 
     { 
   | 
||
| 
         struct compile_status status; 
   | 
||
| 
         status.success = TRUE; 
   | 
||
| 
         status.compiled_for_pos = ZALLOC_N(int, body->iseq_size); 
   | 
||
| 
         status.local_stack_p = !body->catch_except_p; 
   | 
||
| 
         if (create_o_cache) { 
   | 
||
| 
             unsigned int pos = 0; 
   | 
||
| 
             int insn; 
   | 
||
| 
             const VALUE *operands; 
   | 
||
| 
             fprintf(f, "extern const VALUE *const original_body_iseq;\n"); 
   | 
||
| 
             for (pos = 0; pos < body->iseq_size; pos = pos + insn_len(insn)) { 
   | 
||
| 
     #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE 
   | 
||
| 
                 insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[pos]); 
   | 
||
| 
     #else 
   | 
||
| 
                 insn = (int)body->iseq_encoded[pos]; 
   | 
||
| 
     #endif 
   | 
||
| 
               operands = body->iseq_encoded + (pos+1); 
   | 
||
| 
     #define MJIT_EXTERN 1 
   | 
||
| 
     /*****************/ 
   | 
||
| 
      #include "mjit_var.inc" 
   | 
||
| 
     /*****************/ 
   | 
||
| 
     #undef MJIT_EXTERN 
   | 
||
| 
             } 
   | 
||
| 
         } 
   | 
||
| 
     #ifdef _WIN32 
   | 
||
| 
         fprintf(f, "__declspec(dllexport)\n"); 
   | 
||
| 
     #endif 
   | 
||
| ... | ... | |
| 
         else { 
   | 
||
| 
             fprintf(f, "    VALUE *stack = reg_cfp->sp;\n"); 
   | 
||
| 
         } 
   | 
||
| 
         fprintf(f, "    static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n", 
   | 
||
| 
                 (VALUE)body->iseq_encoded); 
   | 
||
| 
         if (!create_o_cache) { 
   | 
||
| 
             fprintf(f, "    static const VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n", 
   | 
||
| 
                     (VALUE)body->iseq_encoded); 
   | 
||
| 
         } 
   | 
||
| 
         /* Simulate `opt_pc` in setup_parameters_complex */ 
   | 
||
| 
         if (body->param.flags.has_opt) { 
   | 
||
| ... | ... | |
| 
         fprintf(f, "        return Qundef;\n"); 
   | 
||
| 
         fprintf(f, "    }\n"); 
   | 
||
| 
         compile_insns(f, body, 0, 0, &status); 
   | 
||
| 
         compile_insns(f, body, 0, 0, &status, create_o_cache); 
   | 
||
| 
         compile_cancel_handler(f, body, &status); 
   | 
||
| 
         fprintf(f, "\n} /* end of %s */\n", funcname); 
   | 
||
| 
         xfree(status.compiled_for_pos); 
   | 
||
| 
         return status.success; 
   | 
||
| 
     } 
   | 
||
| 
     void 
   | 
||
| 
     mjit_compile_var(FILE *f, const struct rb_iseq_constant_body *body, const char *funcname) 
   | 
||
| 
     { 
   | 
||
| 
         unsigned int pos = 0; 
   | 
||
| 
         int insn; 
   | 
||
| 
         const VALUE *operands; 
   | 
||
| 
         fprintf(f, "VALUE *const original_body_iseq = (VALUE *)0x%"PRIxVALUE";\n", 
   | 
||
| 
                 (VALUE)body->iseq_encoded); 
   | 
||
| 
         for (pos = 0; pos < body->iseq_size; pos = pos + insn_len(insn)) { 
   | 
||
| 
     #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE 
   | 
||
| 
             insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[pos]); 
   | 
||
| 
     #else 
   | 
||
| 
             insn = (int)body->iseq_encoded[pos]; 
   | 
||
| 
     #endif 
   | 
||
| 
           operands = body->iseq_encoded + (pos+1); 
   | 
||
| 
     #define MJIT_EXTERN 0 
   | 
||
| 
     /*****************/ 
   | 
||
| 
      #include "mjit_var.inc" 
   | 
||
| 
     /*****************/ 
   | 
||
| 
     #undef MJIT_EXTERN 
   | 
||
| 
         } 
   | 
||
| 
     } 
   | 
||
| ruby.c | ||
|---|---|---|
| 
             M("--jit-debug",         "", "Enable MJIT debugging (very slow)"), 
   | 
||
| 
             M("--jit-wait",          "", "Wait until JIT compilation is finished everytime (for testing)"), 
   | 
||
| 
             M("--jit-save-temps",    "", "Save MJIT temporary files in $TMP or /tmp (for testing)"), 
   | 
||
| 
             M("--jit-cache-objs",    "", "Cache object files"), 
   | 
||
| 
             M("--jit-verbose=num",   "", "Print MJIT logs of level num or less to stderr (default: 0)"), 
   | 
||
| 
             M("--jit-max-cache=num", "", "Max number of methods to be JIT-ed in a cache (default: 1000)"), 
   | 
||
| 
             M("--jit-min-calls=num", "", "Number of calls to trigger JIT (for testing, default: 5)"), 
   | 
||
| ... | ... | |
| 
         else if (strncmp(s, "-min-calls=", 11) == 0) { 
   | 
||
| 
             mjit_opt->min_calls = atoi(s + 11); 
   | 
||
| 
         } 
   | 
||
| 
         else if (strcmp(s, "-cache-objs") == 0) { 
   | 
||
| 
             mjit_opt->cache_objs = 1; 
   | 
||
| 
         } 
   | 
||
| 
         else { 
   | 
||
| 
             rb_raise(rb_eRuntimeError, 
   | 
||
| 
                      "invalid MJIT option `%s' (--help will show valid MJIT options)", s + 1); 
   | 
||
| tool/ruby_vm/models/instructions.rb | ||
|---|---|---|
| 
     RubyVM::Instructions = RubyVM::BareInstructions.to_a + \ 
   | 
||
| 
                            RubyVM::OperandsUnifications.to_a + \ 
   | 
||
| 
                            RubyVM::InstructionsUnifications.to_a 
   | 
||
| 
     RubyVM::MJIT::UnsupportedInstructions = [ 
   | 
||
| 
         'getblockparamproxy',  # TODO: support this 
   | 
||
| 
         'defineclass',         # low priority 
   | 
||
| 
         'opt_call_c_function', # low priority 
   | 
||
| 
     ] 
   | 
||
| 
     require_relative 'trace_instructions' 
   | 
||
| 
     RubyVM::Instructions.freeze 
   | 
||
| tool/ruby_vm/views/_mjit_compile_insn.erb | ||
|---|---|---|
| 
     % 
   | 
||
| 
     % # JIT: Initialize operands 
   | 
||
| 
     % insn.opes.each_with_index do |ope, i| 
   | 
||
| 
             fprintf(f, "    <%= ope.fetch(:name) %> = (<%= ope.fetch(:type) %>)0x%"PRIxVALUE";", operands[<%= i %>]); 
   | 
||
| 
     %   if !%w(... OFFSET lindex_t rb_num_t).include?(ope.fetch(:type)) 
   | 
||
| 
             if (create_o_cache) { 
   | 
||
| 
                 fprintf(f, "    <%= ope.fetch(:name) %> = mjit_var_%d_<%= i %>;", pos); 
   | 
||
| 
             } else { 
   | 
||
| 
     %   else 
   | 
||
| 
             { 
   | 
||
| 
     %   end 
   | 
||
| 
                 fprintf(f, "    <%= ope.fetch(:name) %> = (<%= ope.fetch(:type) %>)0x%"PRIxVALUE";", operands[<%= i %>]); 
   | 
||
| 
             } 
   | 
||
| 
     %   case ope.fetch(:type) 
   | 
||
| 
     %   when 'ID' 
   | 
||
| 
             comment_id(f, (ID)operands[<%= i %>]); 
   | 
||
| ... | ... | |
| 
     % 
   | 
||
| 
     % # compiler: If insn has conditional JUMP, the branch which is not targeted by JUMP should be compiled too. 
   | 
||
| 
     % if insn.expr.expr =~ /if\s+\([^{}]+\)\s+\{[^{}]+JUMP\([^)]+\);[^{}]+\}/ 
   | 
||
| 
         compile_insns(f, body, b->stack_size, pos + insn_len(insn), status); 
   | 
||
| 
         compile_insns(f, body, b->stack_size, pos + insn_len(insn), status, create_o_cache); 
   | 
||
| 
     % end 
   | 
||
| 
     % 
   | 
||
| 
     % # compiler: If insn returns (leave) or does longjmp (throw), the branch should no longer be compiled. TODO: create attr for it? 
   | 
||
| tool/ruby_vm/views/_mjit_compile_send.erb | ||
|---|---|---|
| 
     <%= render 'mjit_compile_pc_and_sp', locals: { insn: insn } -%> 
   | 
||
| 
     % # JIT: Invalidate call cache if it requires vm_search_method. This allows to inline some of following things. 
   | 
||
| 
                 fprintf(f, "    if (UNLIKELY(GET_GLOBAL_METHOD_STATE() != %"PRI_SERIALT_PREFIX"u ||\n", cc->method_state); 
   | 
||
| 
                 fprintf(f, "        RCLASS_SERIAL(CLASS_OF(stack[%d])) != %"PRI_SERIALT_PREFIX"u)) {\n", b->stack_size - 1 - argc, cc->class_serial); 
   | 
||
| 
                 if (create_o_cache) { 
   | 
||
| 
                     fprintf(f, "    if (UNLIKELY(GET_GLOBAL_METHOD_STATE() != mjit_var_%d_method_state ||\n", pos); 
   | 
||
| 
                     fprintf(f, "        RCLASS_SERIAL(CLASS_OF(stack[%d])) != mjit_var_%d_class_serial)) {\n", b->stack_size - 1 - argc, pos); 
   | 
||
| 
                 } else { 
   | 
||
| 
                     fprintf(f, "    if (UNLIKELY(GET_GLOBAL_METHOD_STATE() != %"PRI_SERIALT_PREFIX"u ||\n", cc->method_state); 
   | 
||
| 
                     fprintf(f, "        RCLASS_SERIAL(CLASS_OF(stack[%d])) != %"PRI_SERIALT_PREFIX"u)) {\n", b->stack_size - 1 - argc, cc->class_serial); 
   | 
||
| 
                 } 
   | 
||
| 
                 fprintf(f, "        reg_cfp->pc = original_body_iseq + %d;\n", pos); 
   | 
||
| 
                 fprintf(f, "        goto cancel;\n"); 
   | 
||
| 
                 fprintf(f, "    }\n"); 
   | 
||
| ... | ... | |
| 
                 fprintf(f, "    {\n"); 
   | 
||
| 
                 fprintf(f, "        struct rb_calling_info calling;\n"); 
   | 
||
| 
     % if insn.name == 'send' 
   | 
||
| 
                 fprintf(f, "        vm_caller_setup_arg_block(ec, reg_cfp, &calling, 0x%"PRIxVALUE", 0x%"PRIxVALUE", FALSE);\n", operands[0], operands[2]); 
   | 
||
| 
                 if (create_o_cache) { 
   | 
||
| 
                     fprintf(f, "        vm_caller_setup_arg_block(ec, reg_cfp, &calling, mjit_var_%d_0, mjit_var_%d_2, FALSE);\n", pos, pos); 
   | 
||
| 
                 } else { 
   | 
||
| 
                     fprintf(f, "        vm_caller_setup_arg_block(ec, reg_cfp, &calling, 0x%"PRIxVALUE", 0x%"PRIxVALUE", FALSE);\n", operands[0], operands[2]); 
   | 
||
| 
                 } 
   | 
||
| 
     % else 
   | 
||
| 
                 fprintf(f, "        calling.block_handler = VM_BLOCK_HANDLER_NONE;\n"); 
   | 
||
| 
     % end 
   | 
||
| ... | ... | |
| 
                 fprintf(f, "            VALUE v;\n"); 
   | 
||
| 
                 fprintf(f, "            VALUE *argv = reg_cfp->sp - calling.argc;\n"); 
   | 
||
| 
                 fprintf(f, "            reg_cfp->sp = argv - 1;\n"); /* recv */ 
   | 
||
| 
                 fprintf(f, "            vm_push_frame(ec, (const rb_iseq_t *)0x%"PRIxVALUE", VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling.recv, " 
   | 
||
| 
                         "calling.block_handler, 0x%"PRIxVALUE", (const VALUE *)0x%"PRIxVALUE", argv + %d, %d, %d);\n", 
   | 
||
| 
                         (VALUE)iseq, (VALUE)cc->me, (VALUE)iseq->body->iseq_encoded, param_size, iseq->body->local_table_size - param_size, iseq->body->stack_max); 
   | 
||
| 
                 if (create_o_cache) { 
   | 
||
| 
                     fprintf(f, "            vm_push_frame(ec, (const rb_iseq_t *)mjit_var_%d_iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling.recv, " 
   | 
||
| 
                             "calling.block_handler, mjit_var_%d_ccme, (const VALUE *)mjit_var_%d_iseq_encoded, argv + %d, %d, %d);\n", 
   | 
||
| 
                             pos, pos, pos, param_size, iseq->body->local_table_size - param_size, iseq->body->stack_max); 
   | 
||
| 
                 } else { 
   | 
||
| 
                     fprintf(f, "            vm_push_frame(ec, (const rb_iseq_t *)0x%"PRIxVALUE", VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, calling.recv, " 
   | 
||
| 
                             "calling.block_handler, 0x%"PRIxVALUE", (const VALUE *)0x%"PRIxVALUE", argv + %d, %d, %d);\n", 
   | 
||
| 
                             (VALUE)iseq, (VALUE)cc->me, (VALUE)iseq->body->iseq_encoded, param_size, iseq->body->local_table_size - param_size, iseq->body->stack_max); 
   | 
||
| 
                 } 
   | 
||
| 
                 if (iseq->body->catch_except_p) { 
   | 
||
| 
                     fprintf(f, "            VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);\n"); 
   | 
||
| 
                     fprintf(f, "            v = vm_exec(ec, TRUE);\n"); 
   | 
||
| tool/ruby_vm/views/mjit_compile.inc.erb | ||
|---|---|---|
| 
         edit: __FILE__, 
   | 
||
| 
     } -%> 
   | 
||
| 
     % 
   | 
||
| 
     % unsupported_insns = [ 
   | 
||
| 
     %   'getblockparamproxy',  # TODO: support this 
   | 
||
| 
     %   'defineclass',         # low priority 
   | 
||
| 
     %   'opt_call_c_function', # low priority 
   | 
||
| 
     % ] 
   | 
||
| 
     % unsupported_insns = RubyVM::MJIT::UnsupportedInstructions 
   | 
||
| 
     % 
   | 
||
| 
     % opt_send_without_block = RubyVM::Instructions.find { |i| i.name == 'opt_send_without_block' } 
   | 
||
| 
     % if opt_send_without_block.nil? 
   | 
||
| tool/ruby_vm/views/mjit_var.inc.erb | ||
|---|---|---|
| 
     /* -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- */ 
   | 
||
| 
     % # Copyright (c)   All rights reserved. 
   | 
||
| 
     % # 
   | 
||
| 
     % # This file is a part of  the programming language Ruby.  Permission is hereby 
   | 
||
| 
     % # granted, to either  redistribute and/or modify this file,  provided that the 
   | 
||
| 
     % # conditions mentioned  in the  file COPYING  are met.   Consult the  file for 
   | 
||
| 
     % # details. 
   | 
||
| 
     <%= render 'copyright' %> 
   | 
||
| 
     % 
   | 
||
| 
     % # This is an ERB template that generates Ruby code that generates C code that 
   | 
||
| 
     % # generates JIT-ed C code. 
   | 
||
| 
     <%= render 'notice', locals: { 
   | 
||
| 
         this_file: 'is the main part of compile_insn() in mjit_compile.c', 
   | 
||
| 
         edit: __FILE__, 
   | 
||
| 
     } -%> 
   | 
||
| 
     % 
   | 
||
| 
     % unsupported_insns = RubyVM::MJIT::UnsupportedInstructions 
   | 
||
| 
     switch (insn) { 
   | 
||
| 
     % (RubyVM::BareInstructions.to_a + RubyVM::OperandsUnifications.to_a).each do |insn| 
   | 
||
| 
     %   next if unsupported_insns.include?(insn.name) 
   | 
||
| 
       case BIN(<%= insn.name %>): 
   | 
||
| 
     %   if %w[opt_send_without_block send opt_aref].include?(insn.name) 
   | 
||
| 
     #if MJIT_EXTERN 
   | 
||
| 
         fprintf(f, "extern const rb_serial_t mjit_var_%d_method_state;\n", pos); 
   | 
||
| 
         fprintf(f, "extern const rb_serial_t mjit_var_%d_class_serial;\n", pos); 
   | 
||
| 
         fprintf(f, "extern const VALUE mjit_var_%d_iseq;\n", pos); 
   | 
||
| 
         fprintf(f, "extern const VALUE mjit_var_%d_ccme;\n", pos); 
   | 
||
| 
         fprintf(f, "extern const VALUE mjit_var_%d_iseq_encoded;\n", pos); 
   | 
||
| 
     #else 
   | 
||
| 
         { 
   | 
||
| 
             CALL_INFO ci = (CALL_INFO)operands[0]; 
   | 
||
| 
             CALL_CACHE cc = (CALL_CACHE)operands[1]; 
   | 
||
| 
             const rb_iseq_t *iseq = get_iseq_if_available(cc); 
   | 
||
| 
             if (inlinable_iseq_p(ci, cc, iseq = get_iseq_if_available(cc))) { 
   | 
||
| 
                 fprintf(f, "const rb_serial_t mjit_var_%d_method_state = %"PRI_SERIALT_PREFIX"u;", pos, cc->method_state); 
   | 
||
| 
                 fprintf(f, "const rb_serial_t mjit_var_%d_class_serial = %"PRI_SERIALT_PREFIX"u;", pos, cc->class_serial); 
   | 
||
| 
                 fprintf(f, "const VALUE mjit_var_%d_iseq = 0x%"PRIxVALUE";\n", pos, (VALUE)iseq); 
   | 
||
| 
                 fprintf(f, "const VALUE mjit_var_%d_ccme = 0x%"PRIxVALUE";\n", pos, (VALUE)cc->me); 
   | 
||
| 
                 fprintf(f, "const VALUE mjit_var_%d_iseq_encoded = 0x%"PRIxVALUE";\n", pos, (VALUE)iseq->body->iseq_encoded); 
   | 
||
| 
             } else { 
   | 
||
| 
                 fprintf(f, "const rb_serial_t mjit_var_%d_method_state = 0u;", pos); 
   | 
||
| 
                 fprintf(f, "const rb_serial_t mjit_var_%d_class_serial = 0u;", pos); 
   | 
||
| 
                 fprintf(f, "const VALUE mjit_var_%d_iseq = 0x0;\n", pos); 
   | 
||
| 
                 fprintf(f, "const VALUE mjit_var_%d_ccme = 0x0;\n", pos); 
   | 
||
| 
                 fprintf(f, "const VALUE mjit_var_%d_iseq_encoded = 0x0;\n", pos); 
   | 
||
| 
             } 
   | 
||
| 
         } 
   | 
||
| 
     #endif 
   | 
||
| 
     %   end 
   | 
||
| 
     %   insn.opes.each_with_index do |ope, i| 
   | 
||
| 
     %     next if %w(... OFFSET lindex_t rb_num_t).include?(ope.fetch(:type)) 
   | 
||
| 
         fprintf(f, "/* <%= insn.name %> <%= ope.fetch(:type) %> */;\n"); 
   | 
||
| 
     #if MJIT_EXTERN 
   | 
||
| 
         fprintf(f, "extern const <%= ope.fetch(:type) %> mjit_var_%d_<%= i %>;\n", pos); 
   | 
||
| 
     #else 
   | 
||
| 
         fprintf(f, "const <%= ope.fetch(:type) %> mjit_var_%d_<%= i %> = 0x%"PRIxVALUE";\n", pos, operands[<%= i %>]); 
   | 
||
| 
     #endif 
   | 
||
| 
     %   end 
   | 
||
| 
         break; 
   | 
||
| 
     % end 
   | 
||
| 
     } 
   | 
||