Граф коммитов

30 Коммитов

Автор SHA1 Сообщение Дата
Takashi Kokubun 1125274c4e
YJIT: Invalidate redefined methods only through cme (#6734)
Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>

Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
2022-11-15 12:57:43 -08:00
Takashi Kokubun 3dd4e381fe
Reduce the number of branches in jit_exec (#6722)
* Reduce the number of branches in jit_exec

* Address build failure in some configurations

* Refactor yjit.h
2022-11-13 20:35:35 -08:00
John Hawthorn 679ef34586 New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).

This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.

This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.

    $ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
    == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
    0000 opt_getconstant_path                   <ic:0 ::Foo::Bar::Baz>      (   1)[Li]
    0002 leave

The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.

This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
  it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
  invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.

With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.

This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.

There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-09-01 15:20:49 -07:00
Takashi Kokubun c38e911147
Allow aarch64 to build YJIT 2022-08-29 08:47:11 -07:00
Nobuyoshi Nakada ee864beb7c
Simplify around `USE_YJIT` macro (#6240)
* Simplify around `USE_YJIT` macro

- Use `USE_YJIT` macro only instead of `YJIT_BUILD`.
- An intermediate macro `YJIT_SUPPORTED_P` is no longer used.

* Bail out if YJIT is enabled on unsupported platforms
2022-08-15 13:05:12 -04:00
Alan Wu 2085201176 YJIT: Undef YJIT_SUPPORTED_P for hygiene
YJIT_BUILD is more relevant outside of YJIT.
2022-06-26 08:36:10 -04:00
Alan Wu f90549cd38 Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.

The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.

Because we want to be careful, YJIT is guarded behind a configure
option:

```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```

By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.

The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.

The CI tests have been updated and do not take any more resources than
before.

The development history of the Rust port is available at the following
commit for interested parties:
1fd9573d8b

Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.

[issue]: https://bugs.ruby-lang.org/issues/18481

Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-27 11:00:22 -04:00
NAKAMURA Usaku a493732c8e re-enabled YJIT on x64-mswin64
note that YJIT does not work correctly on the platform now.
2021-12-31 20:13:11 +09:00
Takashi Kokubun 91b7be7708
Do not support non-x86_64 platforms
This was probably not intended in 85a426dc86.
2021-12-27 19:57:30 -08:00
U.Nakamura 85a426dc86 Tiny mmap emulation for Windows
- prerequisite of supporting YJIT with VC++.
- note that now can specfily `--yjit` on mswin64, but not enabled
  YJIT'ed code because of YJIT requires `OPT_DIRECT_THREADED_CODE`
  or `OPT_CALL_THREADED_CODE` in `rb_yjit_compile_iseq`.
2021-12-27 15:56:23 +09:00
Takashi Kokubun 11b8aaa26a
Rename --jit to --mjit (#5248)
* Rename --jit to --mjit

[Feature #18349]

* Fix a few more --jit references

* Fix MJIT Actions

* More s/jit/mjit/ and re-introduce --disable-jit

* Update NEWS.md

* Fix test_bug_reporter_add
2021-12-13 16:08:01 -08:00
Alan Wu b7ea66bc32 YJIT: Fix incomplete invalidation from opt_setinlinecache
As part of YJIT's strategy for promoting Ruby constant expressions into
constants in the output native code, the interpreter calls
rb_yjit_constant_ic_update() from opt_setinlinecache.

The block invalidation loop indirectly calls rb_darray_remove_unordered(),
which does a shuffle remove. Because of this, looping with an
incrementing counter like done previously can miss some elements in the
array. Repeatedly invalidate the first element instead.

The bug this commit resolves does not seem to cause crashes or divergent
behaviors.

Co-authored-by: Jemma Issroff <jemmaissroff@gmail.com>
2021-12-06 19:24:41 -05:00
Maxime Chevalier-Boisvert cdebf57ec6
Add --yjit-no-type-prop so we can test YJIT without type propagation (#5135)
* Add --yjit-no-type-prop so we can test YJIT without type propagation

* Fix typo in command line option

* Leave just two test workflows enable for YJIT
2021-11-18 10:44:31 -05:00
Maxime Chevalier-Boisvert 10fe8495cd Update YJIT code owners. Revert accidental commit. 2021-10-25 14:53:22 -04:00
Maxime Chevalier-Boisvert 722d43ad5e
Test PR 2021-10-25 14:29:36 -04:00
Alan Wu 884b6f3dbb Move YJIT internal macros away from yjit.h. Tweak style
Since this file is exposed to the rest of the codebase and they don't
really need to know about things like PLATFORM_SUPPORTED_P.
2021-10-20 18:19:42 -04:00
Alan Wu 735b3a7748 Tweak mjit_exec() to remove YJIT symbol exports
We were exporting a couple of symbols in yjit.h because they could be
used by code generated by MJIT. We don't want MJIT calling into YJIT
code anyways so let's stop exporting them to libruby.so.

Also adjust indentation and comments in mjit_exec().
2021-10-20 18:19:42 -04:00
Alan Wu f6da559d5b Put YJIT into a single compilation unit
For upstreaming, we want functions we export either prefixed with "rb_"
or made static. Historically we haven't been following this rule, so we
were "leaking" a lot of symbols as `make leak-globals` would tell us.

This change unifies everything YJIT into a single compilation unit,
yjit.o, and makes everything unprefixed static to pass `make leak-globals`.
This manual "unified build" setup is similar to that of vm.o.

Having everything in one compilation unit allows static functions to
be visible across YJIT files and removes the need for declarations in
headers in some cases. Unnecessary declarations were removed.

Other changes of note:
  - switched to MJIT_SYMBOL_EXPORT_BEGIN which indicates stuff as being
    off limits for native extensions
  - the first include of each YJIT file is change to be "internal.h"
  - undefined MAP_STACK before explicitly redefining it since it
    collide's with a definition in system headers. Consider renaming?
2021-10-20 18:19:42 -04:00
Noah Gibbs be06112d48 Fix changes from rebase 2021-10-20 18:19:42 -04:00
Alan Wu 4b58d698b1 Count interpreter instructions when -DYJIT_STATS=1
The interpreter instruction count was enabled based on RUBY_DEBUG as
opposed to YJIT_STATS. In builds with YJIT_STATS=1 but RUBY_DEBUG=0,
the count was not available.

Move YJIT_STATS in yjit.h where declarations are expoed to code outside
of YJIT. Also reduce the changes made to the interpreter for calling
into YJIT's instruction counting function.
2021-10-20 18:19:40 -04:00
Alan Wu bd876c243a TracePoint support
This change fixes some cases where YJIT fails to fire tracing events.
Most of the situations YJIT did not handle correctly involves enabling
tracing while running inside generated code.

A new operation to invalidate all generated code is added, which uses
patching to make generated code exit at the next VM instruction
boundary. A new routine called `jit_prepare_routine_call()` is
introduced to facilitate this and should be used when generating code
that could allocate, or could otherwise use `RB_VM_LOCK_ENTER()`.

The `c_return` event is fired in the middle of an instruction as opposed
to at an instruction boundary, so it requires special handling. C method
call return points are patched to go to a fucntion which does everything
the interpreter does, including firing the `c_return` event. The
generated code for C method calls normally does not fire the event.

Invalided code should not change after patching so the exits are not
clobbered. A new variable is introduced to track the region of code that
should not change.
2021-10-20 18:19:39 -04:00
Aaron Patterson e8617d0e7e Make sure we can still compile with the JIT disabled
If `--disable-jit-support` is passed to configure, then `jit_func` is
removed from the iseq body and we can't compile YJIT.  This commit
detects when the JIT function pointer is gone and disables YJIT in that
case.
2021-10-20 18:19:38 -04:00
Aaron Patterson d0174d99c6 Always use `ret` to return to the interpreter
Always using `ret` to return to the interpreter means that we never have
to check the VM_FRAME_FLAG_FINISH flag.

In the case that we return `Qundef`, the interpreter will execute the
cfp.  We can take advantage of this by setting the PC to the instruction
we can't handle, and let the interpreter pick up the ball from there.

If we return a value other than Qundef, the interpreter will take that
value as the "return value" from the JIT and push that to the SP of the
caller

The leave instruction puts the return value on the top of the calling
frame's stack.  YJIT does the same thing for leave instructions.
However, when we're returning back to the interpreter, the leave
instruction _should not_ put the return value on the top of the stack,
but put it in RAX and use RET.  This commit pops the last value from the
stack pointer and puts it in RAX so that the interpreter is happy with
SP.
2021-10-20 18:19:37 -04:00
Maxime Chevalier-Boisvert 7030cae969 Try running with more YJIT options in CI to surface more bugs 2021-10-20 18:19:36 -04:00
Maxime Chevalier-Boisvert b415ceb92e Increase default YJIT call threshold to 10. Add exec mem size arg. (#52) 2021-10-20 18:19:35 -04:00
Maxime Chevalier-Boisvert 96f4f918b0 Implement greedy versioning. Refactor versioning logic. (#10)
* Implement eager versioning. Refactor versioning logic.

* Add --version-limit and --greedy-versioning command-line args
2021-10-20 18:19:34 -04:00
Alan Wu b626dd7211 YJIT: Fancier opt_getinlinecache
Make sure `opt_getinlinecache` is in a block all on its own, and
invalidate it from the interpreter when `opt_setinlinecache`.
It will recompile with a filled cache the second time around.
This lets YJIT runs well when the IC for constant is cold.
2021-10-20 18:19:33 -04:00
Aaron Patterson 4faaa8e5dc Collect statistics about binding allocations / local variable set
This commit collects statistics about how many binding objects are
allocated as well as the number of local variables set on bindings.
Statistics are output along with other YJIT stats.  Here is an example
of the output:

```
***YJIT: Printing runtime counters from yjit.rb***
Number of Bindings Allocated: 195
Number of locals modified through binding: 0
opt_send_without_block exit reasons:
          ivar_get_method    7515891 (40.4%)
       se_cc_klass_differ    3081330 (16.6%)
       iseq_argc_mismatch    1564578 ( 8.4%)
     se_receiver_not_heap    1557663 ( 8.4%)
                 ic_empty    1407064 ( 7.6%)
         optimized_method     995823 ( 5.4%)
          iseq_not_simple     819413 ( 4.4%)
             alias_method     706972 ( 3.8%)
                  bmethod     685253 ( 3.7%)
      callsite_not_simple     225983 ( 1.2%)
                 kw_splat      25999 ( 0.1%)
          ivar_set_method        902 ( 0.0%)
       cfunc_toomany_args        394 ( 0.0%)
           refined_method         42 ( 0.0%)
    cfunc_ruby_array_varg         29 ( 0.0%)
              invalid_cme          4 ( 0.0%)
leave exit reasons:
    se_finish_frame    4067107 (100.0%)
       se_interrupt         24 ( 0.0%)
getinstancevariable exit reasons:
               undef     121177 (100.0%)
    idx_out_of_range          5 ( 0.0%)
opt_aref exit reasons:
    (all relevant counters are zero)
compiled_iseq_count:         3944
main_block_code_size:     1.1 MiB
side_block_code_size:     0.6 MiB
vm_insns_count:        1137268516
yjit_exec_insns_count:  414015644
ratio_in_yjit:              26.7%
avg_len_in_yjit:              7.5
total_exit_count:        55491789
most frequent exit op:
    opt_send_without_block:   18587628 (33.5%)
        opt_getinlinecache:   11075822 (20.0%)
                      send:    4949300 (8.9%)
                     leave:    4067131 (7.3%)
                   defined:    3975196 (7.2%)
       setinstancevariable:    3567315 (6.4%)
               invokesuper:    2982163 (5.4%)
        getblockparamproxy:    2168852 (3.9%)
                 opt_nil_p:    2104524 (3.8%)
                  opt_aref:    2013858 (3.6%)
```

Running RailsBench allocates 195 binding objects but doesn't set any
local variables.
2021-10-20 18:19:32 -04:00
Alan Wu ec1cbbb07d Get rid of dependency on rb_call_cache 2021-10-20 18:19:32 -04:00
Jose Narvaez 4e2eb7695e Yet Another Ruby JIT!
Renaming uJIT to YJIT. AKA s/ujit/yjit/g.
2021-10-20 18:19:31 -04:00