which has been developed by Takashi Kokubun <takashikkbn@gmail> as
YARV-MJIT. Many of its bugs are fixed by wanabe <s.wanabe@gmail.com>.
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@gmail.com>
Contributor: wanabe <s.wanabe@gmail.com>
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
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62197 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_scan_args), include/ruby/ruby.h (rb_scan_args_set):
return non-keywords elements only in the last hash when keyword
arguments are extracted from it, as well as methods defined in
ruby level. [ruby-core:82427] [Bug #13830]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59626 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_keyword_error_new): get rid of an intermediate
string and check if keys are symbols.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59622 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This hasn't been used since r36709 (2012-08-15)
("Kernel#inspect: improve consistency and do not call #to_s.")
and was never part of public API in include/ruby/
* class.c (rb_obj_basic_to_s_p): remove function
* internal.h (rb_obj_basic_to_s_p): remove declaration
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59208 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (ensure_includable): cannot include refinement
module, or the type and the class do not match.
[ruby-core:79632] [Bug #13236]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58083 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (ensure_includable): extract checks to include and
prepend.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58082 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_extract_keywords): keep the class of non-keyword
elements hash as the original. [ruby-core:77813] [Bug #12884]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57360 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_get_kwargs): when values are stored, corresponding
keys have been remove from the keyword hash, and the hash should
be empty in that case. [ruby-dev:49893] [Bug #13004]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56981 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (singleton_class_of): just copy FROZEN flag without
conditions.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56739 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_undef_methods_from): undefine methods defined in
super from klass.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56482 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* array.c, class.c: Fixed documentation where Fixnum was referred
directly to use Integer, as Fixnum and Bignum are now unified
into Integer and direct usage is deprecated. [Fix GH-1459]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56382 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This helps hit inline method caches more frequently. Before this
commit:
```
[aaron@TC ruby (trunk)]$ time ./ruby -v benchmark/bm_vm2_poly_singleton.rb
ruby 2.4.0dev (2016-09-12 trunk 56141) [x86_64-darwin15]
real 0m3.679s
user 0m3.632s
sys 0m0.022s
```
After this commit:
```
[aaron@TC ruby (trunk)]$ time ./ruby -v benchmark/bm_vm2_poly_singleton.rb
ruby 2.4.0dev (2016-09-12 trunk 56141) [x86_64-darwin15]
last_commit=Copy the serial number from the super class to the singleton class
real 0m2.246s
user 0m2.203s
sys 0m0.020s
```
[Feature #12364]
[ruby-core:75425]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56144 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (ins_methods_i, ins_methods_prot_i, ins_methods_priv_i),
(ins_methods_pub_i): check for each conditions to match.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56086 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (Init_class_hierarchy): prevent rb_cObject which is the
class tree root, from GC. [ruby-dev:49666] [Bug #12492]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55429 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
effect of `#undef rb_scan_args` the minimum.
* include/ruby/ruby.h (rb_scan_args): overwrite only if GCC and
optimized. Visual C++ 14 or later can compile it but make it
conservative.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55110 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This reverts "* include/ruby/ruby.h (rb_scan_args): don't use ALWAYS_INLINE with"
This rb_scan_args macro is GCCism.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55104 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_define_class, rb_define_class_id_under): raise
ArgumentError if super is 0, deprecated behavior which has been
warned long time.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@54015 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This further avoids class name resolution issues which came
about due to relying on hash table ordering before r53376.
Pre-caching the class name when it is never used raises memory
use, but the overall gain from moving away from st still gives
us a small gain. Reverting r53376 and this patch and testing with
"valgrind -v ./ruby -rrdoc -eexit" on x86 (32-bit) shows:
before:
in use at exit: 1,662,239 bytes in 25,286 blocks
total heap usage: 49,514 allocs, 24,228 frees, 6,005,561 bytes allocated
after, with this change:
in use at exit: 1,646,529 bytes in 24,572 blocks
total heap usage: 48,891 allocs, 24,319 frees, 6,003,921 bytes allocated
* class.c (Init_class_hierarchy): resolve name for rb_cObject ASAP
* object.c (rb_mod_const_set): move name resolution to rb_const_set
* variable.c (rb_const_set): do class resolution here
[ruby-core:72807] [Bug #11977]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@53518 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* gc.c (internal_object_p): should not expose singleton classes
without a metaclass. based on patches by ko1 and shugo.
[Bug #11740]
* class.c (rb_singleton_class_object_p): added.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@53243 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* variable.c (rb_class_ivar_set): rename as class specific ivar
setter, and st_table is no longer involved.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52380 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Minor simplification; this will hopefully make future patches
for switching to id_table easier-to-review.
* internal.h (rb_st_insert_id_and_value): update prototype
* variable.c (rb_st_insert_id_and_value): reduce args
(find_class_path): adjust call for less args
(rb_ivar_set): ditto
(rb_cvar_set): ditto
* class.c (rb_singleton_class_attached): ditto
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52374 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
from an original class to a created (cloned) method entry.
* test/ruby/test_refinement.rb: add a test.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51728 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
(such as ST_CONTINUE) for enum rb_id_table_iterator_result.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51544 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
[Feature #11420]
This table only manage ID->VALUE table to reduce overhead of st.
Some functions prefixed rb_id_table_* are provided.
* id_table.c: implement rb_id_table_*.
There are several algorithms to implement it.
Now, there are roughly 4 types:
* st
* array
* hash (implemented by Yura Sokolov)
* mix of array and hash
The macro ID_TABLE_IMPL can choose implementation.
You can see detailes about them at the head of id_table.c.
At the default, I choose 34 (mix of list and hash).
This is not final decision.
Please report your suitable parameters or
your data structure.
* symbol.c: introduce rb_id_serial_t and rb_id_to_serial()
to represent ID by serial number.
* internal.h: use id_table for method tables.
* class.c, gc.c, marshal.c, vm.c, vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51541 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
All contents of previous rb_iseq_t is in rb_iseq_t::body.
Remove rb_iseq_t::self because rb_iseq_t is an object.
RubyVM::InstructionSequence is wrapper object points T_IMEMO/iseq.
So RubyVM::ISeq.of(something) method returns different wrapper
objects but they point the same T_IMEMO/iseq object.
This patch is big, but most of difference is replacement of
iseq->xxx to iseq->body->xxx.
(previous) rb_iseq_t::compile_data is also located to
rb_iseq_t::compile_data.
It was moved from rb_iseq_body::compile_data.
Now rb_iseq_t has empty two pointers.
I will split rb_iseq_body data into static data and dynamic data.
* compile.c: rename some functions/macros.
Now, we don't need to separate iseq and iseqval (only VALUE).
* eval.c (ruby_exec_internal): `n' is rb_iseq_t (T_IMEMO/iseq).
* ext/objspace/objspace.c (count_imemo_objects): count T_IMEMO/iseq.
* gc.c: check T_IMEMO/iseq.
* internal.h: add imemo_type::imemo_iseq.
* iseq.c: define RubyVM::InstructionSequnce as T_OBJECT.
Methods are implemented by functions named iseqw_....
* load.c (rb_load_internal0): rb_iseq_new_top() returns
rb_iseq_t (T_IMEMO/iesq).
* method.h (rb_add_method_iseq): accept rb_iseq_t (T_IMEMO/iseq).
* vm_core.h (GetISeqPtr): removed because it is not T_DATA now.
* vm_core.h (struct rb_iseq_body): remove padding for
[Bug #10037][ruby-core:63721].
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51327 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
clone iseq any more.
* class.c (clone_method): share iseq between cloned methods. All of
method dependent information are able to refer from method entry.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51171 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
rb_control_frame_t::klass.
[Bug #11278], [Bug #11279]
rb_method_entry_t data belong to modules/classes.
rb_method_entry_t::owner points defined module or class.
module M
def foo; end
end
In this case, owner is M.
rb_callable_method_entry_t data belong to only classes.
For modules, MRI creates corresponding T_ICLASS internally.
rb_callable_method_entry_t can also belong to T_ICLASS.
rb_callable_method_entry_t::defined_class points T_CLASS or
T_ICLASS.
rb_method_entry_t data for classes (not for modules) are also
rb_callable_method_entry_t data because it is completely same data.
In this case, rb_method_entry_t::owner == rb_method_entry_t::defined_class.
For example, there are classes C and D, and incldues M,
class C; include M; end
class D; include M; end
then, two T_ICLASS objects for C's super class and D's super class
will be created.
When C.new.foo is called, then M#foo is searcheed and
rb_callable_method_t data is used by VM to invoke M#foo.
rb_method_entry_t data is only one for M#foo.
However, rb_callable_method_entry_t data are two (and can be more).
It is proportional to the number of including (and prepending)
classes (the number of T_ICLASS which point to the module).
Now, created rb_callable_method_entry_t are collected when
the original module M was modified. We can think it is a cache.
We need to select what kind of method entry data is needed.
To operate definition, then you need to use rb_method_entry_t.
You can access them by the following functions.
* rb_method_entry(VALUE klass, ID id);
* rb_method_entry_with_refinements(VALUE klass, ID id);
* rb_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
To invoke methods, then you need to use rb_callable_method_entry_t
which you can get by the following APIs corresponding to the
above listed functions.
* rb_callable_method_entry(VALUE klass, ID id);
* rb_callable_method_entry_with_refinements(VALUE klass, ID id);
* rb_callable_method_entry_without_refinements(VALUE klass, ID id);
* rb_resolve_refined_method_callable(VALUE refinements, const rb_callable_method_entry_t *me);
VM pushes rb_callable_method_entry_t, so that rb_vm_frame_method_entry()
returns rb_callable_method_entry_t.
You can check a super class of current method by
rb_callable_method_entry_t::defined_class.
* method.h: renamed from rb_method_entry_t::klass to
rb_method_entry_t::owner.
* internal.h: add rb_classext_struct::callable_m_tbl to cache
rb_callable_method_entry_t data.
We need to consider abotu this field again because it is only
active for T_ICLASS.
* class.c (method_entry_i): ditto.
* class.c (rb_define_attr): rb_method_entry() does not takes
defiend_class_ptr.
* gc.c (mark_method_entry): mark RCLASS_CALLABLE_M_TBL() for T_ICLASS.
* cont.c (fiber_init): rb_control_frame_t::klass is removed.
* proc.c: fix `struct METHOD' data structure because
rb_callable_method_t has all information.
* vm_core.h: remove several fields.
* rb_control_frame_t::klass.
* rb_block_t::klass.
And catch up changes.
* eval.c: catch up changes.
* gc.c: ditto.
* insns.def: ditto.
* vm.c: ditto.
* vm_args.c: ditto.
* vm_backtrace.c: ditto.
* vm_dump.c: ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51126 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This simplifies all the callers and makes code easier to use
and review. I was confused about the need for RB_GC_GUARD
in define_{aset,aref}_method of struct.c without reading
rb_add_method_iseq.
Likewise, do the same for rb_iseq_clone, where the GC guard
only seems neccesary iff RGenGC is disabled.
* vm_method.c (rb_add_method_iseq): add RB_GC_GUARD
* class.c (clone_method): remove RB_GC_GUARD
* struct.c (define_aref_method): ditto
(define_aset_method): ditto
* vm.c (vm_define_method):
* iseq.c (rb_iseq_clone): add RB_GC_GUARD
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51079 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_check_inheritable): preserve encoding in an error
message when the superclass is not a class.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51050 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (rb_define_class_id_under): raise TypeError exception
same as ruby level class definition when superclass mismatch.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@51048 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
rb_method_entry_t.
r50728 changed sharing `def's to isolating `def's
on alias and so on. However, this change conflicts
future improvement plan. So I change back to sharing approach.
* method.h: move rb_method_definition_t::flags to
rb_method_entry_t::attr::flags.
rb_method_entry_t::attr is union with VALUE because this field
should have same size of VALUE. rb_method_entry_t is T_IMEMO).
And also add the following access macros to it's fileds.
* METHOD_ENTRY_VISI(me)
* METHOD_ENTRY_BASIC(me)
* METHOD_ENTRY_SAFE(me)
* vm_method.c (rb_method_definition_addref): added instead of
rb_method_definition_clone().
Do not create new definition, but increment alias_count.
* class.c (clone_method): catch up this fix.
* class.c (method_entry_i): ditto.
* proc.c (mnew_internal): ditto.
* proc.c (mnew_missing): ditto.
* vm_eval.c: ditto.
* vm_insnhelper.c: ditto.
* vm_method.c: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50792 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
* class.c (ins_methods_push): suppress a signed and unsigned
comparison warning.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50789 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
from long to rb_method_visibility_t.
* class.c (ins_methods_i): catch up this fix.
* class.c (method_entry_i): cast to st_data_t instead of `long'.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@50783 b2dd03c8-39d4-4d8f-98ff-823fe69b080e