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

481 Коммитов

Автор SHA1 Сообщение Дата
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
Alan Wu 5c61caa481
Fix strict aliasing issue with call to rb_id_table_lookup()
Previously, GCC 11 with -O2 LTO issues -Wmaybe-uninitialized here.
2022-04-25 14:30:54 -04:00
Nobuyoshi Nakada 7b1ece9b94
Get rid of type-punning pointer casts 2022-04-07 19:19:13 +09:00
Kevin Newton 8ee4a82e8c
RubyVM.stat constant cache metrics (#5766)
Before the new constant cache behavior, caches were invalidated by a
single global variable. You could inspect the value of this variable
with RubyVM.stat(:global_constant_state). This was mostly useful to
verify the behavior of the VM or to test constant loading like in Rails.

With the new constant cache behavior, we introduced
RubyVM.stat(:constant_cache) which returned a hash with symbol keys and
integer values that represented the number of live constant caches
associated with the given symbol. Additionally, we removed the old
RubyVM.stat(:global_constant_state).

This was proven to be not very useful, so it doesn't help you diagnose
constant loading issues. So, instead we added the global constant state
back into the RubyVM output. However, that number can be misleading as
now when you invalidate something like `Foo::Bar::Baz` you're actually
invalidating 3 different lists of inline caches.

This commit attempts to get the best of both worlds. We remove
RubyVM.stat(:global_constant_state) like we did originally, as it
doesn't have the same semantic meaning and it could be confusing going
forward. Instead we add RubyVM.stat(:constant_cache_invalidations) and
RubyVM.stat(:constant_cache_misses). These two metrics should provide
enough information to diagnose any constant loading issues, as well as
provide a replacement for the old global constant state.
2022-04-05 16:37:00 -04:00
Kevin Newton 42000664be Bring back RubyVM.stat(:global_constant_state)
This was removed as part of [Feature #18589]. But some applications were relying on this behavior. So bringing this back to make it better for backward compatibility going forward.
2022-04-04 20:41:05 +02:00
Kevin Newton 6068da8937 Finer-grained constant cache invalidation (take 2)
This commit reintroduces finer-grained constant cache invalidation.
After 8008fb7 got merged, it was causing issues on token-threaded
builds (such as on Windows).

The issue was that when you're iterating through instruction sequences
and using the translator functions to get back the instruction structs,
you're either using `rb_vm_insn_null_translator` or
`rb_vm_insn_addr2insn2` depending if it's a direct-threading build.
`rb_vm_insn_addr2insn2` does some normalization to always return to
you the non-trace version of whatever instruction you're looking at.
`rb_vm_insn_null_translator` does not do that normalization.

This means that when you're looping through the instructions if you're
trying to do an opcode comparison, it can change depending on the type
of threading that you're using. This can be very confusing. So, this
commit creates a new translator function
`rb_vm_insn_normalizing_translator` to always return the non-trace
version so that opcode comparisons don't have to worry about different
configurations.

[Feature #18589]
2022-04-01 14:48:22 -04:00
Nobuyoshi Nakada 69967ee64e
Revert "Finer-grained inline constant cache invalidation"
This reverts commits for [Feature #18589]:
* 8008fb7352
  "Update formatting per feedback"
* 8f6eaca2e1
  "Delete ID from constant cache table if it becomes empty on ISEQ free"
* 629908586b
  "Finer-grained inline constant cache invalidation"

MSWin builds on AppVeyor have been crashing since the merger.
2022-03-25 20:29:09 +09:00
Kevin Newton 629908586b Finer-grained inline constant cache invalidation
Current behavior - caches depend on a global counter. All constant mutations cause caches to be invalidated.

```ruby
class A
  B = 1
end

def foo
  A::B # inline cache depends on global counter
end

foo # populate inline cache
foo # hit inline cache

C = 1 # global counter increments, all caches are invalidated

foo # misses inline cache due to `C = 1`
```

Proposed behavior - caches depend on name components. Only constant mutations with corresponding names will invalidate the cache.

```ruby
class A
  B = 1
end

def foo
  A::B # inline cache depends constants named "A" and "B"
end

foo # populate inline cache
foo # hit inline cache

C = 1 # caches that depend on the name "C" are invalidated

foo # hits inline cache because IC only depends on "A" and "B"
```

Examples of breaking the new cache:

```ruby
module C
  # Breaks `foo` cache because "A" constant is set and the cache in foo depends
  # on "A" and "B"
  class A; end
end

B = 1
```

We expect the new cache scheme to be invalidated less often because names aren't frequently reused. With the cache being invalidated less, we can rely on its stability more to keep our constant references fast and reduce the need to throw away generated code in YJIT.
2022-03-24 09:14:38 -07:00
Peter Zhu 5f10bd634f Add ISEQ_BODY macro
Use ISEQ_BODY macro to get the rb_iseq_constant_body of the ISeq. Using
this macro will make it easier for us to change the allocation strategy
of rb_iseq_constant_body when using Variable Width Allocation.
2022-03-24 10:03:51 -04:00
Jeremy Evans 0c6e24d102 Fix visibility of alias of zsuper methods
This was broken by 71c746379d.

Fixes [Bug #18600]
2022-03-10 08:35:26 -08:00
Yuta Saito 2b5097b890 vm_method.c: avoid signature mismatch in rb_f_notimplement call
`rb_f_notimplement` has a similar signature with arity=-1, but it has an
extra marker argument to distinguish it from other methods in
compile-time type check in rb_define_method.
This trick is introduced to override a given arity to be -1 since
9ef51b0b89

However, the trailing extra argument introduces a signature mismatch
between caller and callee expectation.
This patch adds rb_f_notimplement_internal, which has canonical arity=-1
signature, and makes rb_define_method family to inserts a method entry
with rb_f_notimplement_internal instead of rb_f_notimplement.
2022-03-02 17:13:40 +09:00
Nobuyoshi Nakada e89d80702b Fix memory leak at the same named alias [Bug #18516]
When aliasing a method to the same name method, set a separate bit
flag on that method definition, instead of the reference count
increment.  Although this kind of alias has no actual effect at
runtime, is used as the hack to suppress the method re-definition
warning.
2022-01-27 15:46:08 +09:00
Nobuyoshi Nakada 069cca6f74
Negative RBOOL usage 2022-01-01 17:02:04 +09:00
Koichi Sasada ca032d5eea undef `rb_vm_lookup_overloaded_cme()`
Some callable method entries (cme) can be a key of `overloaded_cme_table`
and the keys should be pinned because the table is numtable (VALUE is a key).
Before the patch GC checks the cme is in `overloaded_cme_table` by looking up
the table, but it needs VM locking.

It works well in normal GC marking because it is protected by the VM lock,
but it doesn't work on `rb_objspace_reachable_objects_from` because it doesn't
use VM lock.

Now, the number of target cmes are small enough, I decide to pin down
all possible cmes instead of using looking up the table.
2021-12-23 16:49:49 +09:00
Koichi Sasada ad450c9fe5 make `overloaded_cme_table` truly weak key map
`overloaded_cme_table` keeps cme -> monly_cme pairs to manage
corresponding `monly_cme` for `cme`. The lifetime of the `monly_cme`
should be longer than `monly_cme`, but the previous patch losts the
reference to the living `monly_cme`.

Now `overloaded_cme_table` values are always root (keys are only weak
reference), it means `monly_cme` does not freed until corresponding
`cme` is invalidated.

To make managing easy, move `overloaded_cme_table` to `rb_vm_t`.
2021-12-21 15:21:30 +09:00
Koichi Sasada df48db987d `mandatory_only_cme` should not be in `def`
`def` (`rb_method_definition_t`) is shared by multiple callable
method entries (cme, `rb_callable_method_entry_t`).

There are two issues:

* old -> young reference: `cme1->def->mandatory_only_cme = monly_cme`
  if `cme1` is young and `monly_cme` is young, there is no problem.
  Howevr, another old `cme2` can refer `def`, in this case, old `cme2`
  points young `monly_cme` and it violates gengc assumption.
* cme can have different `defined_class` but `monly_cme` only has
  one `defined_class`. It does not make sense and `monly_cme`
  should be created for a cme (not `def`).

To solve these issues, this patch allocates `monly_cme` per `cme`.
`cme` does not have another room to store a pointer to the `monly_cme`,
so this patch introduces `overloaded_cme_table`, which is weak key map
`[cme] -> [monly_cme]`.

`def::body::iseqptr::monly_cme` is deleted.

The first issue is reported by Alan Wu.
2021-12-21 11:03:09 +09:00
Koichi Sasada 82ea287018 optimize `Struct` getter/setter
Introduce new optimized method type
`OPTIMIZED_METHOD_TYPE_STRUCT_AREF/ASET` with index information.
2021-11-19 08:32:39 +09:00
Koichi Sasada be71c95b88 `rb_method_optimized_t` for further extension
Now `rb_method_optimized_t optimized` field is added to represent
optimized method type.
2021-11-19 08:32:39 +09:00
Jeremy Evans ab737b1919 Update documentation for Module#{private,public,protected,module_function}
Also, update NEWS for this change and the Kernel#load change.
2021-11-18 10:51:14 -08:00
Jeremy Evans 75ecbda438 Make Module#{public,private,protected,module_function} return arguments
Previously, each of these methods returned self, but it is
more useful to return arguments, to allow for simpler method
decorators, such as:

```ruby
cached private def foo; some_long_calculation; end
```

Where cached sets up caching for the method.

For each of these methods, the following behavior is used:

1) No arguments returns nil
2) Single argument is returned
3) Multiple arguments are returned as an array

The single argument case is really the case we are trying to
optimize for, for the same reason that def was changed to return
a symbol for the method.

Idea and initial patch from Herwin Quarantainenet.

Implements [Feature #12495]
2021-11-18 09:47:40 -08:00
Peter Zhu c400165afa Fix crash when clearing method cache for builtin method
Builtin methods do not always have their mandatory_only_cme created (it
is only created when called with only mandatory parameters), so it could
be null. If we try to clear the cme, it will crash because it is null.

Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
2021-11-17 09:02:57 -05:00
Koichi Sasada b1b73936c1 `Primitive.mandatory_only?` for fast path
Compare with the C methods, A built-in methods written in Ruby is
slower if only mandatory parameters are given because it needs to
check the argumens and fill default values for optional and keyword
parameters (C methods can check the number of parameters with `argc`,
so there are no overhead). Passing mandatory arguments are common
(optional arguments are exceptional, in many cases) so it is important
to provide the fast path for such common cases.

`Primitive.mandatory_only?` is a special builtin function used with
`if` expression like that:

```ruby
  def self.at(time, subsec = false, unit = :microsecond, in: nil)
    if Primitive.mandatory_only?
      Primitive.time_s_at1(time)
    else
      Primitive.time_s_at(time, subsec, unit, Primitive.arg!(:in))
    end
  end
```

and it makes two ISeq,

```
  def self.at(time, subsec = false, unit = :microsecond, in: nil)
    Primitive.time_s_at(time, subsec, unit, Primitive.arg!(:in))
  end

  def self.at(time)
    Primitive.time_s_at1(time)
  end
```

and (2) is pointed by (1). Note that `Primitive.mandatory_only?`
should be used only in a condition of an `if` statement and the
`if` statement should be equal to the methdo body (you can not
put any expression before and after the `if` statement).

A method entry with `mandatory_only?` (`Time.at` on the above case)
is marked as `iseq_overload`. When the method will be dispatch only
with mandatory arguments (`Time.at(0)` for example), make another
method entry with ISeq (2) as mandatory only method entry and it
will be cached in an inline method cache.

The idea is similar discussed in https://bugs.ruby-lang.org/issues/16254
but it only checks mandatory parameters or more, because many cases
only mandatory parameters are given. If we find other cases (optional
or keyword parameters are used frequently and it hurts performance),
we can extend the feature.
2021-11-15 15:58:56 +09:00
Aaron Patterson 0d63600e4f Partial revert of ceebc7fc98
I'm looking through the places where YJIT needs notifications.  It looks
like these changes to gc.c and vm_callinfo.h have become unnecessary
since 84ab77ba59.  This commit just makes the diff against upstream
smaller, but otherwise shouldn't change any behavior.
2021-10-20 18:19:36 -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
Aaron Patterson ab5760307b add a callback for when method cache changes 2021-10-20 18:19:28 -04:00
Maxime Chevalier-Boisvert e4c65ec49c Refactor uJIT code into more files for readability 2021-10-20 18:19:26 -04:00
Alan Wu c378c7a7cb MicroJIT: generate less code for CFUNCs
Added UJIT_CHECK_MODE. Set to 1 to double check method dispatch in
generated code.

It's surprising to me that we need to watch both cc and cme. There might
be opportunities to simplify there.
2021-10-20 18:19:26 -04:00
Jeremy Evans e8d6076fbd Fix typo in static function name 2021-10-01 08:12:46 -09:00
Nobuyoshi Nakada a0a8f2abf5 Get rid of type-punning pointer casts [Bug #18062] 2021-08-11 12:07:44 +09:00
S.H 378e8cdad6
Using RBOOL macro 2021-08-02 12:06:44 +09:00
Jeremy Evans 693ce6af0a Update documentation for ruby2_keywords
Point out that the method should be used for backwards compatibility
with code prior to Ruby 3.0 instead of Ruby 2.7.  It's still needed
in Ruby 2.7. It isn't needed in Ruby 3.0, as the methods using it
could switch to delegating both positional and keyword arguments.

Add a link to the www.ruby-lang.org web page that goes into detail
describing when and how ruby2_keywords should be used.
2021-07-29 08:10:20 -07:00
Nobuyoshi Nakada e4f891ce8d
Adjust styles [ci skip]
* --braces-after-func-def-line
* --dont-cuddle-else
* --procnames-start-lines
* --space-after-for
* --space-after-if
* --space-after-while
2021-06-17 10:13:40 +09:00
Takashi Kokubun e1b03b0c2b
Enable VM_ASSERT in --jit CIs (#4543) 2021-06-01 00:15:51 -07:00
Alan Wu 636d4f7eb9 Avoid setting the visibility of refinement method entries
Since refinement search is always performed, these entries should always
be public. The method entry that the refinement search returns decides
the visibility.

Fixes [Bug #17822]
2021-05-21 12:12:31 -04:00
Alan Wu 39a2ba5cc5
Method cache: fix refinement entry handling
To invalidate some callable method entries, we replace the entry in the
class. Most types of method entries are on the method table of the
origin class, but refinement entries without an orig_me are housed in
the method table of the class itself. They are there because refinements
take priority over prepended methods.

By unconditionally inserting a copy of the refinement entry into the
origin class, clearing the method cache created situations where there
are refinement entry duplicates in the lookup chain, leading to infinite
loops and other problems.

Update the replacement logic to use the right class that houses the
method entry. Also, be more selective about cache invalidation when
moving refinement entries for prepend. This avoids calling
clear_method_cache_by_id_in_class() before refinement entries are in the
place it expects.

[Bug #17806]
2021-05-11 12:05:06 -04:00
Nobuyoshi Nakada 0bbab1e515
Protoized old pre-ANSI K&R style declarations and definitions 2021-05-07 00:04:36 +09:00
Jeremy Evans 4b36a597f4 Fix setting method visibility for a refinement without an origin class
If a class has been refined but does not have an origin class,
there is a single method entry marked with VM_METHOD_TYPE_REFINED,
but it contains the original method entry.  If the original method
entry is present, we shouldn't skip the method when searching even
when skipping refined methods.

Fixes [Bug #17519]
2021-04-23 16:31:18 -07:00
Jeremy Evans 58660e9434 Skip refined method when exporting methods with changed visibility
Previously, attempting to change the visibility of a method in a
singleton class for a class/module that is prepended to and refined
would raise a NoMethodError.

Fixes [Bug #17519]
2021-03-16 12:10:11 -07:00
Koichi Sasada 9c769575bf invalidate negative cache any time.
negative cache on a class which does not have subclasses was not
invalidated, but it should be invalidated because other classes
can cache this negative cache.
[Bug #17553]
2021-02-19 16:54:31 +09:00
Jeremy Evans 49d3830f44 Fix documentation for Module#ruby2_keywords
It returns nil, not self.

Fixes [Bug #17560]
2021-02-09 14:47:36 -08:00
Nobuyoshi Nakada 71c746379d Make alias for aliased original method
Chaining aliased methods increases searching cost linearly.
2021-02-03 19:59:35 +09:00
Nobuyoshi Nakada ea47a9506a
Adjusted indent [ci skip] 2021-02-03 13:42:03 +09:00
Matt Valentine-House e0f999a2ed Add RCLASS_SUBCLASSES Macro 2021-02-01 08:42:54 -08:00
Matt Valentine-House 7341b01465 Add RCLASS_ALLOCATOR Macro 2021-02-01 08:42:54 -08:00
Koichi Sasada 1ecda21366 global call-cache cache table for rb_funcall*
rb_funcall* (rb_funcall(), rb_funcallv(), ...) functions invokes
Ruby's method with given receiver. Ruby 2.7 introduced inline method
cache with static memory area. However, Ruby 3.0 reimplemented the
method cache data structures and the inline cache was removed.

Without inline cache, rb_funcall* searched methods everytime.
Most of cases per-Class Method Cache (pCMC) will be helped but
pCMC requires VM-wide locking and it hurts performance on
multi-Ractor execution, especially all Ractors calls methods
with rb_funcall*.

This patch introduced Global Call-Cache Cache Table (gccct) for
rb_funcall*. Call-Cache was introduced from Ruby 3.0 to manage
method cache entry atomically and gccct enables method-caching
without VM-wide locking. This table solves the performance issue
on multi-ractor execution.
[Bug #17497]

Ruby-level method invocation does not use gccct because it has
inline-method-cache and the table size is limited. Basically
rb_funcall* is not used frequently, so 1023 entries can be enough.
We will revisit the table size if it is not enough.
2021-01-29 16:22:12 +09:00
Nobuyoshi Nakada 8dfae85adb
Warn the defined location as deprecation as well as the main message
[Bug #17575]
2021-01-23 19:58:39 +09:00
Nobuyoshi Nakada eeacdcb9a0 Fixed premature return
After setting ruby2_keywords for bmethod, the rest of arguments
had been ignored. [Bug #17558]
2021-01-19 17:59:37 +09:00
Alan Wu e812b36205 Fix typo: invaldate -> invalidate 2021-01-18 14:02:19 -05:00
Aaron Patterson 0ed71b37fa Don't try to clear cache on garbage objects
Method cache can be cleared during lazy sweeping.  An object that will
be collected during lazy sweep *should not* have it's method cache
cleared.  Soon-to-be-collected objects can be in an inconsistent state and
this can lead to a crash.  This patch just leaves early if the object is
going to be collected.

Fixes [Bug #17536]

Co-Authored-By: John Hawthorn <john@hawthorn.email>
Co-Authored-By: Alan Wu <XrXr@users.noreply.github.com>
2021-01-15 15:23:16 -08:00