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

950 Коммитов

Автор SHA1 Сообщение Дата
Naoto Ono d9bff41637 Rename a variable name 2024-07-19 16:39:21 +09:00
Naoto Ono 09dd9a0457 Launchable: Aggregate test results based on file level 2024-07-19 16:39:21 +09:00
Koichi Sasada 43aee3393d fix `defined?(@ivar)` with Ractors
`defined?(@ivar)` on the non main Ractor has two issues:

1. raising an exception

```ruby
class C
  @iv1 = []
  def self.defined_iv1 = defined?(@iv1)
end

Ractor.new{
  p C.defined_iv1
  #=> can not get unshareable values from instance variables of classes/modules from non-main Ractors (Ractor::IsolationError)
}.take
```

-> Do not raise an exception but return `"instance-variable"` because
it is defined.

2. returning `"instance-variable"` if there is not defined.

```
class C
  # @iv2 is not defined
  def self.defined_iv2 = defined?(@iv2)
end

Ractor.new{
  p C.defined_iv2 #=> "instance-variable"
}.take
```

-> returns `nil`
2024-07-12 04:43:14 +09:00
Nobuyoshi Nakada 690b56440b
Use `File.write` instead of `Kernel#open` 2024-07-09 13:01:44 +09:00
Naoto Ono 8ede84aa95 Move the file location of launchable.rb 2024-07-08 10:15:04 +09:00
Naoto Ono 5b78925455 Integrate Launchable into make btest 2024-07-08 10:15:04 +09:00
eileencodes b2b8306b46 Fix forwarding for optimized send
Always treat forwarding as a complex call.
2024-07-02 11:48:43 -07:00
eileencodes cc8c4a60b7 Calling into a C func shouldn't fast path when forwarding
When we forward calls to C functions if the callsite is a forwarding
site it might not always be a splat, so we can't use the fast path.

Fixes:

[ruby-core:118418]
2024-07-02 11:48:43 -07:00
Gabriel Lacroix 4d94d28a4a
YJIT: Inline simple ISEQs with unused keyword parameters
This commit expands inlining for simple ISeqs to accept
callees that have unused keyword parameters and callers
that specify unused keywords. The following shows 2 new
callsites that will be inlined:

```ruby
def let(a, checked: true) = a

let(1)
let(1, checked: false)
```

Co-authored-by: Kaan Ozkan <kaan.ozkan@shopify.com>
2024-07-02 18:34:48 +00:00
Koichi Sasada 30a8dbc861 maximum showing line number on btest failure
Some tests in btest uses long src for btest and it is harmful to
check the results. This patch introducing the limitation how many
lines of code is shown on failure.
2024-07-01 12:11:28 +09:00
Aaron Patterson a2c27bae96 [YJIT] Don't expand kwargs on forwarding
Similarly to splat arrays, we shouldn't expand splat kwargs.

[ruby-core:118401]
2024-06-29 11:25:59 -06:00
Aaron Patterson 4cbc41d5e5 [YJIT] Fix block and splat handling when forwarding
This commit fixes splat and block handling when calling in to a
forwarding iseq.  In the case of a splat we need to avoid expanding the
array to the stack.  We need to also ensure the CI write is flushed to
the SP, otherwise it's possible for a block handler to clobber the CI

[ruby-core:118360]
2024-06-26 16:01:26 -04:00
Koichi Sasada b182f2a045 fix sendfwd with `send` and `method_missing`
combination with `send` method (optimized) or `method_missing`
and forwarding send (`...`) needs to respect given
`rb_forwarding_call_data`. Otherwize it causes critical error
such as SEGV.
2024-06-21 00:43:48 +09:00
Aaron Patterson cdf33ed5f3 Optimized forwarding callers and callees
This patch optimizes forwarding callers and callees. It only optimizes methods that only take `...` as their parameter, and then pass `...` to other calls.

Calls it optimizes look like this:

```ruby
def bar(a) = a
def foo(...) = bar(...) # optimized
foo(123)
```

```ruby
def bar(a) = a
def foo(...) = bar(1, 2, ...) # optimized
foo(123)
```

```ruby
def bar(*a) = a

def foo(...)
  list = [1, 2]
  bar(*list, ...) # optimized
end
foo(123)
```

All variants of the above but using `super` are also optimized, including a bare super like this:

```ruby
def foo(...)
  super
end
```

This patch eliminates intermediate allocations made when calling methods that accept `...`.
We can observe allocation elimination like this:

```ruby
def m
  x = GC.stat(:total_allocated_objects)
  yield
  GC.stat(:total_allocated_objects) - x
end

def bar(a) = a
def foo(...) = bar(...)

def test
  m { foo(123) }
end

test
p test # allocates 1 object on master, but 0 objects with this patch
```

```ruby
def bar(a, b:) = a + b
def foo(...) = bar(...)

def test
  m { foo(1, b: 2) }
end

test
p test # allocates 2 objects on master, but 0 objects with this patch
```

How does it work?
-----------------

This patch works by using a dynamic stack size when passing forwarded parameters to callees.
The caller's info object (known as the "CI") contains the stack size of the
parameters, so we pass the CI object itself as a parameter to the callee.
When forwarding parameters, the forwarding ISeq uses the caller's CI to determine how much stack to copy, then copies the caller's stack before calling the callee.
The CI at the forwarded call site is adjusted using information from the caller's CI.

I think this description is kind of confusing, so let's walk through an example with code.

```ruby
def delegatee(a, b) = a + b

def delegator(...)
  delegatee(...)  # CI2 (FORWARDING)
end

def caller
  delegator(1, 2) # CI1 (argc: 2)
end
```

Before we call the delegator method, the stack looks like this:

```
Executing Line | Code                                  | Stack
---------------+---------------------------------------+--------
              1| def delegatee(a, b) = a + b           | self
              2|                                       | 1
              3| def delegator(...)                    | 2
              4|   #                                   |
              5|   delegatee(...)  # CI2 (FORWARDING)  |
              6| end                                   |
              7|                                       |
              8| def caller                            |
          ->  9|   delegator(1, 2) # CI1 (argc: 2)     |
             10| end                                   |
```

The ISeq for `delegator` is tagged as "forwardable", so when `caller` calls in
to `delegator`, it writes `CI1` on to the stack as a local variable for the
`delegator` method.  The `delegator` method has a special local called `...`
that holds the caller's CI object.

Here is the ISeq disasm fo `delegator`:

```
== disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] "..."@0
0000 putself                                                          (   1)[LiCa]
0001 getlocal_WC_0                          "..."@0
0003 send                                   <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil
0006 leave                                  [Re]
```

The local called `...` will contain the caller's CI: CI1.

Here is the stack when we enter `delegator`:

```
Executing Line | Code                                  | Stack
---------------+---------------------------------------+--------
              1| def delegatee(a, b) = a + b           | self
              2|                                       | 1
              3| def delegator(...)                    | 2
           -> 4|   #                                   | CI1 (argc: 2)
              5|   delegatee(...)  # CI2 (FORWARDING)  | cref_or_me
              6| end                                   | specval
              7|                                       | type
              8| def caller                            |
              9|   delegator(1, 2) # CI1 (argc: 2)     |
             10| end                                   |
```

The CI at `delegatee` on line 5 is tagged as "FORWARDING", so it knows to
memcopy the caller's stack before calling `delegatee`.  In this case, it will
memcopy self, 1, and 2 to the stack before calling `delegatee`.  It knows how much
memory to copy from the caller because `CI1` contains stack size information
(argc: 2).

Before executing the `send` instruction, we push `...` on the stack.  The
`send` instruction pops `...`, and because it is tagged with `FORWARDING`, it
knows to memcopy (using the information in the CI it just popped):

```
== disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] "..."@0
0000 putself                                                          (   1)[LiCa]
0001 getlocal_WC_0                          "..."@0
0003 send                                   <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil
0006 leave                                  [Re]
```

Instruction 001 puts the caller's CI on the stack.  `send` is tagged with
FORWARDING, so it reads the CI and _copies_ the callers stack to this stack:

```
Executing Line | Code                                  | Stack
---------------+---------------------------------------+--------
              1| def delegatee(a, b) = a + b           | self
              2|                                       | 1
              3| def delegator(...)                    | 2
              4|   #                                   | CI1 (argc: 2)
           -> 5|   delegatee(...)  # CI2 (FORWARDING)  | cref_or_me
              6| end                                   | specval
              7|                                       | type
              8| def caller                            | self
              9|   delegator(1, 2) # CI1 (argc: 2)     | 1
             10| end                                   | 2
```

The "FORWARDING" call site combines information from CI1 with CI2 in order
to support passing other values in addition to the `...` value, as well as
perfectly forward splat args, kwargs, etc.

Since we're able to copy the stack from `caller` in to `delegator`'s stack, we
can avoid allocating objects.

I want to do this to eliminate object allocations for delegate methods.
My long term goal is to implement `Class#new` in Ruby and it uses `...`.

I was able to implement `Class#new` in Ruby
[here](https://github.com/ruby/ruby/pull/9289).
If we adopt the technique in this patch, then we can optimize allocating
objects that take keyword parameters for `initialize`.

For example, this code will allocate 2 objects: one for `SomeObject`, and one
for the kwargs:

```ruby
SomeObject.new(foo: 1)
```

If we combine this technique, plus implement `Class#new` in Ruby, then we can
reduce allocations for this common operation.

Co-Authored-By: John Hawthorn <john@hawthorn.email>
Co-Authored-By: Alan Wu <XrXr@users.noreply.github.com>
2024-06-18 09:28:25 -07:00
Matt Valentine-House 4ab7cc1818 Guard against GC.compact when using in tests
This test will error on platforms that don't implement GC.compact

Co-Authored-By: Peter Zhu <peter@peterzhu.ca>
2024-06-13 18:58:49 -04:00
Matt Valentine-House c093fd86d2 Don't return inside assert_equal
The implementation of assert_equal inside bootstraptest/runner.rb wraps
a print around all the test code specified in the string, making returns
useless.

This change fixes this test for platforms that don't implement
GC.compact

Co-Authored-By: Peter Zhu <peter@peterzhu.ca>
2024-06-13 18:58:49 -04:00
Dmitry Ukolov b988ae3a06
RJIT: Fixed and/or reg+disp32 operations (#10856)
Fixed RJIT `and reg+disp32` and `or reg+disp32` operation.
2024-06-13 15:55:01 -07:00
Aaron Patterson 1271ff72d5 Don't call `Warning.warn` unless the category is enabled
The warning category should be enabled if we want to call
`Warning.warn`.

This commit speeds up the following benchmark:

```ruby
eval "def test; " +
  1000.times.map { "'  '.chomp!" }.join(";") + "; end"

def run_benchmark count
  i = 0
  while i < count
    start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    yield
    ms = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
    puts "itr ##{i}: #{(ms * 1000).to_i}ms"
    i += 1
  end
end

run_benchmark(25) do
  250.times do
    test
  end
end
```

On `master` this runs at about 92ms per iteration. With this patch, it
is 7ms per iteration.

[Bug #20573]
2024-06-11 14:54:15 -07:00
Koichi Sasada 448efa90af respect `RUBY_TEST_TIMEOUT_SCALE`
GC benchmarks will spend long time with assertions so we need
to respect `RUBY_TEST_SUBPROCESS_TIMEOUT_SCALE` environment variable.

@nobu pointed out that now `RUBY_TEST_TIMEOUT_SCALE` is primary
(and `RUBY_TEST_SUBPROCESS_TIMEOUT_SCALE` was obsolete so check both
and will remove it later.
2024-06-10 13:32:14 +09:00
Jean Boussier f8abd24b1f Improve YJIT performance warning regression test
[Bug #20522]
2024-06-05 09:22:15 +02:00
Matt Valentine-House c90cb4d743 Fix pathes => paths typo in bootstrap test runner 2024-06-04 20:34:19 +01:00
Jean Boussier f7b53a75b6 Do not emit shape transition warnings when YJIT is compiling
[Bug #20522]

If `Warning.warn` is redefined in Ruby, emitting a warning would invoke
Ruby code, which can't safely be done when YJIT is compiling.
2024-06-04 19:21:01 +02:00
Takashi Kokubun a2147eb694
YJIT: Fix getconstant exits after opt_ltlt fusion (#10903)
Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
2024-06-04 10:17:40 -04:00
Alan Wu c4056b0e43 YJIT: Add another regression test for empty splat
Follow-up for 6c8ae44a38 ("YJIT: Fix out
of bounds access when splatting empty array"). This test crashes Ruby
3.3.2.
2024-06-03 20:20:02 -04:00
Takashi Kokubun e7e83a313c Skip flaky RJIT tests
https://github.com/ruby/ruby/actions/runs/9352283948/job/25740049841
https://github.com/ruby/ruby/actions/runs/9350872852/job/25735280811

Not a high priority to fix right now, so skipping until we get to fix
it.
2024-06-03 16:50:43 -07:00
Alan Wu 6c8ae44a38 YJIT: Fix out of bounds access when splatting empty array
Previously, we read the last element array even when the array was
empty, doing an out-of-bounds access. This sometimes caused a SEGV.

[Bug #20496]
2024-05-31 18:37:13 -04:00
Luke Gruber 6747fbe77d
Fix interrupts during Ractor.select
Fixes [Bug #20168]
2024-05-05 15:14:53 +00:00
Alan Wu 2a978ee047
YJIT: Fix `Struct` accessors not firing tracing events (#10690)
* YJIT: Fix `Struct` accessors not firing tracing events

Reading and writing to structs should fire `c_call` and `c_return`, but
YJIT wasn't correctly dropping those calls when tracing.
This has been missing since this functionality was added in 3081c83169,
but the added test only fails when ran in isolation with
`--yjit-call-threshold=1`. The test sometimes failed on CI.

* RJIT: YJIT: Fix `Struct` readers not firing tracing events

Same issue as YJIT, but it looks like RJIT doesn't support writing to
structs, so only reading needs changing.
2024-05-01 10:22:41 -04:00
Randy Stauner adae813c5f
YJIT: Expand codegen for `TrueClass#===` to `FalseClass` and `NilClass` (#10679) 2024-04-29 19:25:22 +00:00
Randy Stauner 845f2db136
YJIT: Add specialized codegen function for `TrueClass#===` (#10640)
* YJIT: Add specialized codegen function for `TrueClass#===`

TrueClass#=== is currently number 10 in the most frequent C calls list of the lobsters benchmark.

```
require "benchmark/ips"

def wrap
  true === true
  true === false
  true === :x
end

Benchmark.ips do |x|
  x.report(:wrap) do
    wrap
  end
end
```

```
before
Warming up --------------------------------------
                wrap     1.791M i/100ms
Calculating -------------------------------------
                wrap     17.806M (± 1.0%) i/s -     89.544M in   5.029363s

after
Warming up --------------------------------------
                wrap     4.024M i/100ms
Calculating -------------------------------------
                wrap     40.149M (± 1.1%) i/s -    201.223M in   5.012527s
```

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
Co-authored-by: Kevin Menard <kevin.menard@shopify.com>
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>

* Fix the new test for RJIT

---------

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
Co-authored-by: Kevin Menard <kevin.menard@shopify.com>
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
2024-04-29 14:32:07 -04:00
Nobuyoshi Nakada 937cb1176d
Fix regexps for abbreviated options 2024-04-28 00:06:24 +09:00
Nobuyoshi Nakada a0b4f0bcc9
Timeout scale in bootstraptest 2024-04-28 00:03:09 +09:00
Nobuyoshi Nakada 602193ded7
Timeout in bootstraptest 2024-04-28 00:02:33 +09:00
Takashi Kokubun a1db69f0c9 Skip a flaky Ractor test for YJIT
https://github.com/ruby/ruby/actions/runs/8852277192/job/24310631888
https://github.com/ruby/ruby/actions/runs/8851325573/job/24307638329

This seems like an existing, separate issue from what we're currently
investigating. The `iseq->body->jit_entry` setup is not Ractor-safe?
Let me suppress this for now and revisit this after resolving the
ongoing issue.
2024-04-26 11:11:30 -07:00
Alan Wu 9b5bc8e6ea YJIT: Relax `--yjit-verify-ctx` after singleton class creation
Types like `Type::CString` really only assert that at one point the object had
its class field equal to `String`. Once a singleton class is created for any
strings, the type makes no assertion about any class field anymore, and becomes
the same as `Type::TString`.

Previously, the `--yjit-verify-ctx` option wasn't allowing objects of these
kind that have have singleton classes to pass verification even though the code
generators handle it just fine.

Found through `ruby/spec`.
2024-04-25 18:38:14 -04:00
Takashi Kokubun 7ab1a608e7
YJIT: Optimize local variables when EP == BP (take 2) (#10607)
* Revert "Revert "YJIT: Optimize local variables when EP == BP" (#10584)"

This reverts commit c878344195.

* YJIT: Take care of GC references in ISEQ invariants

Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>

---------

Co-authored-by: Alan Wu <alansi.xingwu@shopify.com>
2024-04-25 10:04:53 -04:00
Kevin Menard afc7799c32
YJIT: Add a specialized codegen function for `Class#superclass`. (#10613)
Add a specialized codegen function for `Class#superclass`.

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
Co-authored-by: Randy Stauner <randy.stauner@shopify.com>
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
2024-04-24 10:31:35 -04:00
Alan Wu aeb08bc8a7
YJIT: Fix String#setbyte crashing for converted arguments
Previously, passing objects that respond to #to_int to `String#setbyte`
resulted in a crash when compiled by YJIT. This was due to the lazily
pushed frame from rb_yjit_lazy_push_frame() lingering and not being
popped by an exception as expected.

The fix is to ensure that `ec->cfp` is restored to before the lazy frame
push in case the method call for conversion succeeds. Right now, this is
only for conversion to integers.

Found running `ruby/spec`.

* clarify comment

We just need to make sure `ec->cfp` is always preserved and this can
convert without rising when `raise` is true.
2024-04-22 18:33:34 +00:00
Alan Wu 1bb7638e7a
YJIT: Fix shrinking block with assumption too much (#10585)
* YJIT: Fix shrinking block with assumption too much

Under the very specific circumstances, discovered by a test case in
`ruby/spec`, an `expandarray` block can contain just a branch and carry
a method lookup assumption. Previously, when we regenerated the branch,
we allowed it to shrink to empty, since we put the code at the jump
target immediately after it. That was incorrect and caused a crash while
the block is invalidated, since that left no room to patch in an exit.

When regenerating a branch that makes up a block entirely, and the block
could be invalidated, we need to ensure there is room for invalidation.
When there is code before the branch, they should act as padding, so we
don't need to worry about those cases.

* skip on RJIT
2024-04-22 11:16:46 -04:00
Alan Wu 478b4ef9de
Fix nil error with KNOWNBUGS.rb
Previously, `make test-knownbugs` crashed with `NoMethodError` due to
the failed regex match if there is a test case in KNOWNBUGS.rb.

The note about 1.8 compatibility is probably bogus as we require a way
more recent BASERUBY now.
2024-04-08 12:31:33 -04:00
Kevin Newton 4ef99905a6 [PRISM] Enhance syntax error message extraction in test_literal_suffix btest 2024-04-03 17:34:12 -04:00
Kevin Newton ffba484754 [PRISM] Fix up syntax error message parsing in btests 2024-04-03 17:34:12 -04:00
Peter Zhu e26ac3ab71 Test finalizer is ran in bootstraptest 2024-04-01 10:26:16 -04:00
Alan Wu f3c35749fe YJIT: Optimize putobject+opt_ltlt for integers
In `jit_rb_int_lshift()`, we guard against the right hand side changing
since we want to avoid generating variable length shifts. When control
reaches a `putobject` and `opt_ltlt` pair, though, we know that the right
hand side never changes.

This commit detects this situation and substitutes an implementation
that does not guard against the right hand side changing, saving that
work.

Deleted some `putobject` Rust tests since they aren't that valuable and
cause linking issues.

Nice boost to `optcarrot` and `protoboeuf`:

```
----------  ------------------
bench       yjit-pre/yjit-post
optcarrot   1.09
protoboeuf  1.12
----------  ------------------
```
2024-03-28 17:41:01 -04:00
Kevin Newton eb995a6410 [PRISM] Include file and line in error message 2024-03-27 14:19:58 -04:00
Alan Wu de742b425f YJIT: Inline simple getlocal+leave iseqs
This mainly targets things like `T.unsafe()` from Sorbet, which is just an
identity function at runtime and only a hint for the static checker.
Only deal with simple caller and callees (no keywords and splat etc.).

Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
2024-03-25 17:50:40 -04:00
Takashi Kokubun 46bf6ae886
YJIT: Propagate Array, Hash, and String classes (#10323) 2024-03-25 12:06:47 -04:00
Étienne Barrié 12be40ae6b Implement chilled strings
[Feature #20205]

As a path toward enabling frozen string literals by default in the future,
this commit introduce "chilled strings". From a user perspective chilled
strings pretend to be frozen, but on the first attempt to mutate them,
they lose their frozen status and emit a warning rather than to raise a
`FrozenError`.

Implementation wise, `rb_compile_option_struct.frozen_string_literal` is
no longer a boolean but a tri-state of `enabled/disabled/unset`.

When code is compiled with frozen string literals neither explictly enabled
or disabled, string literals are compiled with a new `putchilledstring`
instruction. This instruction is identical to `putstring` except it marks
the String with the `STR_CHILLED (FL_USER3)` and `FL_FREEZE` flags.

Chilled strings have the `FL_FREEZE` flag as to minimize the need to check
for chilled strings across the codebase, and to improve compatibility with
C extensions.

Notes:
  - `String#freeze`: clears the chilled flag.
  - `String#-@`: acts as if the string was mutable.
  - `String#+@`: acts as if the string was mutable.
  - `String#clone`: copies the chilled flag.

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-03-19 09:26:49 +01:00
Alan Wu 802e857ae6
YJIT: Support arity=-2 cfuncs (#10268)
This type of cfuncs shows up as consume a lot of cycles in profiles of
the lobsters benchmark, even though in the stats they don't happen that
frequently. Might be a bug in the profiling, but these calls are not
too bad to support, so might as well do it.

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
2024-03-18 16:02:22 +00:00
Nobuyoshi Nakada 0d5b16599a
[Bug #20218] Reject keyword arguments in index 2024-03-17 13:20:23 +09:00