ruby-changes:50079
From: k0kubun <ko1@a...>
Date: Sun, 4 Feb 2018 20:22:38 +0900 (JST)
Subject: [ruby-changes:50079] k0kubun:r62197 (trunk): mjit_compile.c: merge initial JIT compiler
k0kubun 2018-02-04 20:22:28 +0900 (Sun, 04 Feb 2018) New Revision: 62197 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=62197 Log: mjit_compile.c: merge initial JIT compiler which has been developed by Takashi Kokubun <takashikkbn@gmail> as YARV-MJIT. Many of its bugs are fixed by wanabe <s.wanabe@g...>. This JIT compiler is designed to be a safe migration path to introduce JIT compiler to MRI. So this commit does not include any bytecode changes or dynamic instruction modifications, which are done in original MJIT. This commit even strips off some aggressive optimizations from YARV-MJIT, and thus it's slower than YARV-MJIT too. But it's still fairly faster than Ruby 2.5 in some benchmarks (attached below). Note that this JIT compiler passes `make test`, `make test-all`, `make test-spec` without JIT, and even with JIT. Not only it's perfectly safe with JIT disabled because it does not replace VM instructions unlike MJIT, but also with JIT enabled it stably runs Ruby applications including Rails applications. I'm expecting this version as just "initial" JIT compiler. I have many optimization ideas which are skipped for initial merging, and you may easily replace this JIT compiler with a faster one by just replacing mjit_compile.c. `mjit_compile` interface is designed for the purpose. common.mk: update dependencies for mjit_compile.c. internal.h: declare `rb_vm_insn_addr2insn` for MJIT. vm.c: exclude some definitions if `-DMJIT_HEADER` is provided to compiler. This avoids to include some functions which take a long time to compile, e.g. vm_exec_core. Some of the purpose is achieved in transform_mjit_header.rb (see `IGNORED_FUNCTIONS`) but others are manually resolved for now. Load mjit_helper.h for MJIT header. mjit_helper.h: New. This is a file used only by JIT-ed code. I'll refactor `mjit_call_cfunc` later. vm_eval.c: add some #ifdef switches to skip compiling some functions like Init_vm_eval. win32/mkexports.rb: export thread/ec functions, which are used by MJIT. include/ruby/defines.h: add MJIT_FUNC_EXPORTED macro alis to clarify that a function is exported only for MJIT. array.c: export a function used by MJIT. bignum.c: ditto. class.c: ditto. compile.c: ditto. error.c: ditto. gc.c: ditto. hash.c: ditto. iseq.c: ditto. numeric.c: ditto. object.c: ditto. proc.c: ditto. re.c: ditto. st.c: ditto. string.c: ditto. thread.c: ditto. variable.c: ditto. vm_backtrace.c: ditto. vm_insnhelper.c: ditto. vm_method.c: ditto. I would like to improve maintainability of function exports, but I believe this way is acceptable as initial merging if we clarify the new exports are for MJIT (so that we can use them as TODO list to fix) and add unit tests to detect unresolved symbols. I'll add unit tests of JIT compilations in succeeding commits. Author: Takashi Kokubun <takashikkbn@g...> Contributor: wanabe <s.wanabe@g...> Part of [Feature #14235] --- * Known issues * Code generated by gcc is faster than clang. The benchmark may be worse in macOS. Following benchmark result is provided by gcc w/ Linux. * Performance is decreased when Google Chrome is running * JIT can work on MinGW, but it doesn't improve performance at least in short running benchmark. * Currently it doesn't perform well with Rails. We'll try to fix this before release. --- * Benchmark reslts Benchmarked with: Intel 4.0GHz i7-4790K with 16GB memory under x86-64 Ubuntu 8 Cores - 2.0.0-p0: Ruby 2.0.0-p0 - r62186: Ruby trunk (early 2.6.0), before MJIT changes - JIT off: On this commit, but without `--jit` option - JIT on: On this commit, and with `--jit` option ** Optcarrot fps Benchmark: https://github.com/mame/optcarrot | |2.0.0-p0 |r62186 |JIT off |JIT on | |:--------|:--------|:--------|:--------|:--------| |fps |37.32 |51.46 |51.31 |58.88 | |vs 2.0.0 |1.00x |1.38x |1.37x |1.58x | ** MJIT benchmarks Benchmark: https://github.com/benchmark-driver/mjit-benchmarks (Original: https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch/MJIT-benchmarks) | |2.0.0-p0 |r62186 |JIT off |JIT on | |:----------|:--------|:--------|:--------|:--------| |aread |1.00 |1.09 |1.07 |2.19 | |aref |1.00 |1.13 |1.11 |2.22 | |aset |1.00 |1.50 |1.45 |2.64 | |awrite |1.00 |1.17 |1.13 |2.20 | |call |1.00 |1.29 |1.26 |2.02 | |const2 |1.00 |1.10 |1.10 |2.19 | |const |1.00 |1.11 |1.10 |2.19 | |fannk |1.00 |1.04 |1.02 |1.00 | |fib |1.00 |1.32 |1.31 |1.84 | |ivread |1.00 |1.13 |1.12 |2.43 | |ivwrite |1.00 |1.23 |1.21 |2.40 | |mandelbrot |1.00 |1.13 |1.16 |1.28 | |meteor |1.00 |2.97 |2.92 |3.17 | |nbody |1.00 |1.17 |1.15 |1.49 | |nest-ntimes|1.00 |1.22 |1.20 |1.39 | |nest-while |1.00 |1.10 |1.10 |1.37 | |norm |1.00 |1.18 |1.16 |1.24 | |nsvb |1.00 |1.16 |1.16 |1.17 | |red-black |1.00 |1.02 |0.99 |1.12 | |sieve |1.00 |1.30 |1.28 |1.62 | |trees |1.00 |1.14 |1.13 |1.19 | |while |1.00 |1.12 |1.11 |2.41 | ** Discourse's script/bench.rb Benchmark: https://github.com/discourse/discourse/blob/v1.8.7/script/bench.rb NOTE: Rails performance was somehow a little degraded with JIT for now. We should fix this. (At least I know opt_aref is performing badly in JIT and I have an idea to fix it. Please wait for the fix.) *** JIT off Your Results: (note for timings- percentile is first, duration is second in millisecs) categories_admin: 50: 17 75: 18 90: 22 99: 29 home_admin: 50: 21 75: 21 90: 27 99: 40 topic_admin: 50: 17 75: 18 90: 22 99: 32 categories: 50: 35 75: 41 90: 43 99: 77 home: 50: 39 75: 46 90: 49 99: 95 topic: 50: 46 75: 52 90: 56 99: 101 *** JIT on Your Results: (note for timings- percentile is first, duration is second in millisecs) categories_admin: 50: 19 75: 21 90: 25 99: 33 home_admin: 50: 24 75: 26 90: 30 99: 35 topic_admin: 50: 19 75: 20 90: 25 99: 30 categories: 50: 40 75: 44 90: 48 99: 76 home: 50: 42 75: 48 90: 51 99: 89 topic: 50: 49 75: 55 90: 58 99: 99 Added files: trunk/tool/ruby_vm/views/_mjit_compile_insn.erb trunk/tool/ruby_vm/views/_mjit_compile_send.erb trunk/tool/ruby_vm/views/mjit_compile.inc.erb Modified files: trunk/Makefile.in trunk/array.c trunk/bignum.c trunk/class.c trunk/common.mk trunk/compile.c trunk/error.c trunk/gc.c trunk/hash.c trunk/include/ruby/defines.h trunk/insns.def trunk/internal.h trunk/iseq.c trunk/mjit.c trunk/mjit.h trunk/mjit_compile.c trunk/numeric.c trunk/object.c trunk/proc.c trunk/re.c trunk/st.c trunk/string.c trunk/test/-ext-/thread_fd_close/test_thread_fd_close.rb trunk/test/lib/minitest/unit.rb trunk/test/ruby/test_io.rb trunk/test/ruby/test_settracefunc.rb trunk/test/ruby/test_thread.rb trunk/test/rubygems/test_gem_util.rb trunk/test/thread/test_cv.rb trunk/test/thread/test_sync.rb trunk/test/webrick/test_httpserver.rb trunk/test/webrick/test_server.rb trunk/thread.c trunk/tool/transform_mjit_header.rb trunk/tool/update-deps trunk/variable.c trunk/vm.c trunk/vm_backtrace.c trunk/vm_core.h trunk/vm_eval.c trunk/vm_exec.h trunk/vm_insnhelper.c trunk/vm_insnhelper.h trunk/vm_method.c trunk/vm_trace.c trunk/win32/Makefile.sub trunk/win32/mkexports.rb Index: compile.c =================================================================== --- compile.c (revision 62196) +++ compile.c (revision 62197) @@ -754,7 +754,7 @@ rb_iseq_translate_threaded_code(rb_iseq_ https://github.com/ruby/ruby/blob/trunk/compile.c#L754 } #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE -static int +int rb_vm_insn_addr2insn(const void *addr) /* cold path */ { int insn; Index: vm_trace.c =================================================================== --- vm_trace.c (revision 62196) +++ vm_trace.c (revision 62197) @@ -325,7 +325,7 @@ exec_hooks_protected(rb_execution_contex https://github.com/ruby/ruby/blob/trunk/vm_trace.c#L325 return state; } -void +MJIT_FUNC_EXPORTED void rb_exec_event_hooks(rb_trace_arg_t *trace_arg, int pop_p) { rb_execution_context_t *ec = trace_arg->ec; Index: common.mk =================================================================== --- common.mk (revision 62196) +++ common.mk (revision 62197) @@ -890,6 +890,7 @@ $(srcs_vpath)insns.inc: $(srcdir)/tool/r https://github.com/ruby/ruby/blob/trunk/common.mk#L890 $(srcs_vpath)insns_info.inc: $(srcdir)/tool/ruby_vm/views/insns_info.inc.erb $(srcs_vpath)vmtc.inc: $(srcdir)/tool/ruby_vm/views/vmtc.inc.erb $(srcs_vpath)vm.inc: $(srcdir)/tool/ruby_vm/views/vm.inc.erb +$(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 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 @@ -2003,8 +2004,12 @@ mjit.$(OBJEXT): {$(VPATH)}mjit.h https://github.com/ruby/ruby/blob/trunk/common.mk#L2004 mjit.$(OBJEXT): {$(VPATH)}ruby_assert.h mjit.$(OBJEXT): {$(VPATH)}version.h mjit.$(OBJEXT): {$(VPATH)}vm_core.h +mjit_compile.$(OBJEXT): {$(VPATH)}insns.inc +mjit_compile.$(OBJEXT): {$(VPATH)}insns_info.inc mjit_compile.$(OBJEXT): {$(VPATH)}internal.h +mjit_compile.$(OBJEXT): {$(VPATH)}mjit.h mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.c +mjit_compile.$(OBJEXT): {$(VPATH)}mjit_compile.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 Index: win32/Makefile.sub =================================================================== --- win32/Makefile.sub (revision 62196) +++ win32/Makefile.sub (revision 62197) @@ -1192,7 +1192,7 @@ rb_mjit_header.h: PHONY probes.h https://github.com/ruby/ruby/blob/trunk/win32/Makefile.sub#L1192 $(Q) $(IFCHANGE) $@ vm.i INSNS = opt_sc.inc optinsn.inc optunifs.inc insns.inc insns_info.inc \ - vmtc.inc vm.inc + vmtc.inc vm.inc mjit_compile.inc !if [exit > insns_rules.mk] !else if [for %I in ($(INSNS)) do \ Index: object.c =================================================================== --- object.c (revision 62196) +++ object.c (revision 62197) @@ -198,7 +198,7 @@ rb_eql(VALUE obj1, VALUE obj2) https://github.com/ruby/ruby/blob/trunk/object.c#L198 * \private *++ */ -VALUE +MJIT_FUNC_EXPORTED VALUE rb_obj_equal(VALUE obj1, VALUE obj2) { if (obj1 == obj2) return Qtrue; @@ -217,7 +217,7 @@ VALUE rb_obj_hash(VALUE obj); https://github.com/ruby/ruby/blob/trunk/object.c#L217 *++ */ -VALUE +MJIT_FUNC_EXPORTED VALUE rb_obj_not(VALUE obj) { return RTEST(obj) ? Qfalse : Qtrue; @@ -233,7 +233,7 @@ rb_obj_not(VALUE obj) https://github.com/ruby/ruby/blob/trunk/object.c#L233 *++ */ -VALUE +MJIT_FUNC_EXPORTED VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2) { VALUE result = rb_funcall(obj1, id_eq, 1, obj2); @@ -304,7 +304,7 @@ rb_obj_singleton_class(VALUE obj) https://github.com/ruby/ruby/blob/trunk/object.c#L304 } /*! \private */ -void +MJIT_FUNC_EXPORTED void rb_obj_copy_ivar(VALUE dest, VALUE obj) { if (!(RBASIC(dest)->flags & ROBJECT_EMBED) && ROBJECT_IVPTR(dest)) { @@ -3019,7 +3019,7 @@ rb_check_convert_type(VALUE val, int typ https://github.com/ruby/ruby/blob/trunk/object.c#L3019 } /*! \private */ -VALUE +MJIT_FUNC_EXPORTED VALUE rb_check_convert_type_with_id(VALUE val, int type, const char *tname, ID method) { VALUE v; Index: array.c =================================================================== --- array.c (revision 62196) +++ array.c (revision 62197) @@ -506,7 +506,7 @@ VALUE https://github.com/ruby/ruby/blob/trunk/array.c#L506 return ary; } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_ary_tmp_new_from_values(VALUE klass, long n, const VALUE *elts) { VALUE ary; @@ -640,7 +640,7 @@ rb_check_array_type(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L640 return rb_check_convert_type_with_id(ary, T_ARRAY, "Array", idTo_ary); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_check_to_array(VALUE ary) { return rb_check_convert_type_with_id(ary, T_ARRAY, "Array", idTo_a); @@ -1297,7 +1297,7 @@ rb_ary_aref2(VALUE ary, VALUE b, VALUE e https://github.com/ruby/ruby/blob/trunk/array.c#L1297 return rb_ary_subseq(ary, beg, len); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_ary_aref1(VALUE ary, VALUE arg) { long beg, len; Index: numeric.c =================================================================== --- numeric.c (revision 62196) +++ numeric.c (revision 62197) @@ -1157,7 +1157,7 @@ flodivmod(double x, double y, double *di https://github.com/ruby/ruby/blob/trunk/numeric.c#L1157 * An error will be raised if y == 0. */ -double +MJIT_FUNC_EXPORTED double ruby_float_mod(double x, double y) { double mod; @@ -1333,7 +1333,7 @@ num_equal(VALUE x, VALUE y) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1333 * so an implementation-dependent value is returned. */ -VALUE +MJIT_FUNC_EXPORTED VALUE rb_float_equal(VALUE x, VALUE y) { volatile double a, b; @@ -1436,7 +1436,7 @@ flo_cmp(VALUE x, VALUE y) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1436 return rb_dbl_cmp(a, b); } -int +MJIT_FUNC_EXPORTED int rb_float_cmp(VALUE x, VALUE y) { return NUM2INT(flo_cmp(x, y)); Index: gc.c =================================================================== --- gc.c (revision 62196) +++ gc.c (revision 62197) @@ -2216,6 +2216,7 @@ obj_free(rb_objspace_t *objspace, VALUE https://github.com/ruby/ruby/blob/trunk/gc.c#L2216 break; case T_MODULE: case T_CLASS: + mjit_remove_class_serial(RCLASS_SERIAL(obj)); rb_id_table_free(RCLASS_M_TBL(obj)); if (RCLASS_IV_TBL(obj)) { st_free_table(RCLASS_IV_TBL(obj)); @@ -4054,7 +4055,7 @@ stack_check(rb_execution_context_t *ec, https://github.com/ruby/ruby/blob/trunk/gc.c#L4055 #define STACKFRAME_FOR_CALL_CFUNC 838 -int +MJIT_FUNC_EXPORTED int rb_ec_stack_check(rb_execution_context_t *ec) { return stack_check(ec, STACKFRAME_FOR_CALL_CFUNC); @@ -6053,7 +6054,7 @@ rb_gc_writebarrier_unprotect(VALUE obj) https://github.com/ruby/ruby/blob/trunk/gc.c#L6054 /* * remember `obj' if needed. */ -void +MJIT_FUNC_EXPORTED void rb_gc_writebarrier_remember(VALUE obj) { rb_objspace_t *objspace = &rb_objspace; Index: variable.c =================================================================== --- variable.c (revision 62196) +++ variable.c (revision 62197) @@ -480,7 +480,7 @@ struct rb_global_variable { https://github.com/ruby/ruby/blob/trunk/variable.c#L480 struct trace_var *trace; }; -struct rb_global_entry* +MJIT_FUNC_EXPORTED struct rb_global_entry* rb_global_entry(ID id) { struct rb_global_entry *entry; @@ -790,7 +790,7 @@ rb_f_untrace_var(int argc, const VALUE * https://github.com/ruby/ruby/blob/trunk/variable.c#L790 return Qnil; } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_gvar_get(struct rb_global_entry *entry) { struct rb_global_variable *var = entry->var; @@ -823,7 +823,7 @@ trace_en(struct rb_global_variable *var) https://github.com/ruby/ruby/blob/trunk/variable.c#L823 return Qnil; /* not reached */ } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_gvar_set(struct rb_global_entry *entry, VALUE val) { struct trace_data trace; @@ -858,7 +858,7 @@ rb_gv_get(const char *name) https://github.com/ruby/ruby/blob/trunk/variable.c#L858 return rb_gvar_get(entry); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_gvar_defined(struct rb_global_entry *entry) { if (entry->var->getter == rb_gvar_undef_getter) return Qfalse; @@ -2010,7 +2010,7 @@ check_autoload_required(VALUE mod, ID id https://github.com/ruby/ruby/blob/trunk/variable.c#L2010 return 0; } -int +MJIT_FUNC_EXPORTED int rb_autoloading_value(VALUE mod, ID id, VALUE* value) { VALUE load; @@ -2211,7 +2211,7 @@ rb_autoload_p(VALUE mod, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L2211 return (ele = check_autoload_data(load)) ? ele->feature : Qnil; } -void +MJIT_FUNC_EXPORTED void rb_const_warn_if_deprecated(const rb_const_entry_t *ce, VALUE klass, ID id) { if (RB_CONST_DEPRECATED_P(ce)) { @@ -2294,7 +2294,7 @@ rb_const_get_at(VALUE klass, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L2294 return rb_const_get_0(klass, id, TRUE, FALSE, FALSE); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_public_const_get_from(VALUE klass, ID id) { return rb_const_get_0(klass, id, TRUE, TRUE, TRUE); @@ -2306,7 +2306,7 @@ rb_public_const_get(VALUE klass, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L2306 return rb_const_get_0(klass, id, FALSE, TRUE, TRUE); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_public_const_get_at(VALUE klass, ID id) { return rb_const_get_0(klass, id, TRUE, FALSE, TRUE); @@ -2544,7 +2544,7 @@ rb_const_defined_at(VALUE klass, ID id) https://github.com/ruby/ruby/blob/trunk/variable.c#L2544 return rb_const_defined_0(klass, id, TRUE, FALSE, FALSE); } -int +MJIT_FUNC_EXPORTED int rb_public_const_defined_from(VALUE klass, ID id) { return rb_const_defined_0(klass, id, TRUE, TRUE, TRUE); @@ -3123,7 +3123,7 @@ rb_st_copy(VALUE obj, struct st_table *o https://github.com/ruby/ruby/blob/trunk/variable.c#L3123 return new_tbl; } -rb_const_entry_t * +MJIT_FUNC_EXPORTED rb_const_entry_t * rb_const_lookup(VALUE klass, ID id) { struct rb_id_table *tbl = RCLASS_CONST_TBL(klass); Index: string.c =================================================================== --- string.c (revision 62196) +++ string.c (revision 62197) @@ -373,7 +373,7 @@ rb_setup_fake_str(struct RString *fake_s https://github.com/ruby/ruby/blob/trunk/string.c#L373 return setup_fake_str(fake_str, name, len, rb_enc_to_index(enc)); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_fstring_new(const char *ptr, long len) { struct RString fake_str; @@ -1445,7 +1445,7 @@ rb_obj_as_string(VALUE obj) https://github.com/ruby/ruby/blob/trunk/string.c#L1445 return rb_obj_as_string_result(str, obj); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_obj_as_string_result(VALUE str, VALUE obj) { if (!RB_TYPE_P(str, T_STRING)) @@ -2933,7 +2933,7 @@ rb_str_append(VALUE str, VALUE str2) https://github.com/ruby/ruby/blob/trunk/string.c#L2933 #define MIN_PRE_ALLOC_SIZE 48 -VALUE +MJIT_FUNC_EXPORTED VALUE rb_str_concat_literals(size_t num, const VALUE *strary) { VALUE str; @@ -10391,7 +10391,7 @@ rb_str_quote_unprintable(VALUE str) https://github.com/ruby/ruby/blob/trunk/string.c#L10391 return str; } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_id_quote_unprintable(ID id) { return rb_str_quote_unprintable(rb_id2str(id)); @@ -10468,7 +10468,7 @@ sym_to_sym(VALUE sym) https://github.com/ruby/ruby/blob/trunk/string.c#L10468 return sym; } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, VALUE passed_proc) { VALUE obj; Index: st.c =================================================================== --- st.c (revision 62196) +++ st.c (revision 62197) @@ -2171,7 +2171,7 @@ st_insert_generic(st_table *tab, long ar https://github.com/ruby/ruby/blob/trunk/st.c#L2171 /* Mimics ruby's { foo => bar } syntax. This function is placed here because it touches table internals and write barriers at once. */ -void +MJIT_FUNC_EXPORTED void rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash) { st_index_t n; Index: include/ruby/defines.h =================================================================== --- include/ruby/defines.h (revision 62196) +++ include/ruby/defines.h (revision 62197) @@ -270,6 +270,10 @@ void xfree(void*); https://github.com/ruby/ruby/blob/trunk/include/ruby/defines.h#L270 #define RUBY_FUNC_EXPORTED #endif +/* MJIT_FUNC_EXPORTED is used for functions which are exported only for MJIT + and NOT ensured to be exported in future versions. */ +#define MJIT_FUNC_EXPORTED RUBY_FUNC_EXPORTED + #ifndef RUBY_EXTERN #define RUBY_EXTERN extern #endif Index: vm_method.c =================================================================== --- vm_method.c (revision 62196) +++ vm_method.c (revision 62197) @@ -62,6 +62,7 @@ static struct { https://github.com/ruby/ruby/blob/trunk/vm_method.c#L62 static void rb_class_clear_method_cache(VALUE klass, VALUE arg) { + mjit_remove_class_serial(RCLASS_SERIAL(klass)); RCLASS_SERIAL(klass) = rb_next_class_serial(); if (RB_TYPE_P(klass, T_ICLASS)) { @@ -171,7 +172,7 @@ rb_free_method_entry(const rb_method_ent https://github.com/ruby/ruby/blob/trunk/vm_method.c#L172 } static inline rb_method_entry_t *search_method(VALUE klass, ID id, VALUE *defined_class_ptr); -static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2); +extern int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2); static inline rb_method_entry_t * lookup_method_table(VALUE klass, ID id) @@ -222,7 +223,7 @@ setup_method_cfunc_struct(rb_method_cfun https://github.com/ruby/ruby/blob/trunk/vm_method.c#L223 cfunc->invoker = call_cfunc_invoker_func(argc); } -static void +MJIT_FUNC_EXPORTED void method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts) { *(rb_method_definition_t **)&me->def = def; @@ -336,7 +337,7 @@ method_definition_reset(const rb_method_ https://github.com/ruby/ruby/blob/trunk/vm_method.c#L337 } } -static rb_method_definition_t * +MJIT_FUNC_EXPORTED rb_method_definition_t * method_definition_create(rb_method_type_t type, ID mid) { rb_method_definition_t *def; @@ -401,7 +402,7 @@ rb_method_entry_clone(const rb_method_en https://github.com/ruby/ruby/blob/trunk/vm_method.c#L402 return me; } -const rb_callable_method_entry_t * +MJIT_FUNC_EXPORTED const rb_callable_method_entry_t * rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class) { rb_method_definition_t *def = src_me->def; @@ -812,7 +813,7 @@ method_entry_get(VALUE klass, ID id, VAL https://github.com/ruby/ruby/blob/trunk/vm_method.c#L813 return method_entry_get_without_cache(klass, id, defined_class_ptr); } -const rb_method_entry_t * +MJIT_FUNC_EXPORTED const rb_method_entry_t * rb_method_entry(VALUE klass, ID id) { return method_entry_get(klass, id, NULL); @@ -853,7 +854,7 @@ prepare_callable_method_entry(VALUE defi https://github.com/ruby/ruby/blob/trunk/vm_method.c#L854 return cme; } -const rb_callable_method_entry_t * +MJIT_FUNC_EXPORTED const rb_callable_method_entry_t * rb_callable_method_entry(VALUE klass, ID id) { VALUE defined_class; @@ -886,7 +887,7 @@ method_entry_resolve_refinement(VALUE kl https://github.com/ruby/ruby/blob/trunk/vm_method.c#L887 return me; } -const rb_callable_method_entry_t * +MJIT_FUNC_EXPORTED const rb_callable_method_entry_t * rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class_ptr) { VALUE defined_class, *dcp = defined_class_ptr ? defined_class_ptr : &defined_class; @@ -900,7 +901,7 @@ rb_method_entry_without_refinements(VALU https://github.com/ruby/ruby/blob/trunk/vm_method.c#L901 return method_entry_resolve_refinement(klass, id, FALSE, defined_class_ptr); } -const rb_callable_method_entry_t * +MJIT_FUNC_EXPORTED const rb_callable_method_entry_t * rb_callable_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class_ptr) { VALUE defined_class, *dcp = defined_class_ptr ? defined_class_ptr : &defined_class; @@ -1462,7 +1463,7 @@ original_method_definition(const rb_meth https://github.com/ruby/ruby/blob/trunk/vm_method.c#L1463 return def; } -static int +MJIT_FUNC_EXPORTED int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2) { d1 = original_method_definition(d1); Index: error.c =================================================================== --- error.c (revision 62196) +++ error.c (revision 62197) @@ -1161,7 +1161,7 @@ exc_set_backtrace(VALUE exc, VALUE bt) https://github.com/ruby/ruby/blob/trunk/error.c#L1161 return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt)); } -VALUE +MJIT_FUNC_EXPORTED VALUE rb_exc_set_backtrace(VALUE exc, VALUE bt) { return exc_set_backtrace(exc, bt); Index: tool/transform_mjit_header.rb =================================================================== --- tool/transform_mjit_header.rb (revision 62196) +++ tool/transform_mjit_header.rb (revision 62197) @@ -17,6 +17,7 @@ module MJITHeader https://github.com/ruby/ruby/blob/trunk/tool/transform_mjit_header.rb#L17 ] IGNORED_FUNCTIONS = [ + 'vm_search_method_slowpath', # This increases the time to compile when inlined. So we use it as external function. 'rb_equal_opt', # Not used from VM and not compilable ] Index: tool/update-deps =================================================================== --- tool/update-deps (revision 62196) +++ tool/update-deps (revision 62197) @@ -120,6 +120,7 @@ FILES_NEED_VPATH = %w[ https://github.com/ruby/ruby/blob/trunk/tool/update-deps#L120 known_errors.inc lex.c miniprelude.c + mjit_compile.inc newline.c node_name.inc opt_sc.inc Index: tool/ruby_vm/views/_mjit_compile_send.erb =================================================================== --- tool/ruby_vm/views/_mjit_compile_send.erb (nonexistent) +++ tool/ruby_vm/views/_mjit_compile_send.erb (revision 62197) @@ -0,0 +1,74 @@ https://github.com/ruby/ruby/blob/trunk/tool/ruby_vm/views/_mjit_compile_send.erb#L1 +% # -*- mode:c; style:ruby; coding: utf-8; indent-tabs-mode: nil -*- +% # Copyright (c) 2018 Takashi Kokubun. 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. +% +% # Optimized case of send / opt_send_without_block instructions. + { +% # compiler: Prepare operands which may be used by `insn.call_attribute` +% insn.opes.each_with_index do |ope, i| + MAYBE_UNUSED(<%= ope.fetch(:decl) %>) = (<%= ope.fetch(:type) %>)operands[<%= i %>]; +% end +% + const rb_iseq_t *iseq; + unsigned int argc = ci->orig_argc; /* unlike `ci->orig_argc`, `argc` may include blockarg */ +% if insn.name == 'send' + argc += ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0); +% end + + if (inlinable_iseq_p(ci, cc, iseq = get_iseq_if_available(cc))) { + int param_size = iseq->body->param.size; /* TODO: check calling->argc for argument_arity_error */ + +% # JIT: move sp and pc if necessary + fprintf(f, " reg_cfp->pc = (VALUE *)0x%"PRIxVALUE";\n", (VALUE)(body->iseq_encoded + next_pos)); /* ADD_PC(INSN_ATTR(width)); */ + fprintf(f, " reg_cfp->sp = reg_cfp->bp + %d;\n", b->stack_size + 1 - <%= insn.pops.size %>); /* POPN(INSN_ATTR(popn)); */ + +% # 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() != %llu || RCLASS_SERIA (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/