Commit graph

1026 commits

Author SHA1 Message Date
Peter Zhu
f1049aa55d Don't check for presence of ENV['GITHUB_WORKFLOW']
We already check whether `ENV['GITHUB_WORKFLOW']` is equal to `Compilations`,
so we don't need to check that it's not nil.
2025-01-10 10:17:16 -05:00
Hiroshi SHIBATA
3a1414a70b Rewrite Benchmark to Tempfile on bootstraptest/test_ractor.rb 2025-01-10 10:19:39 +09:00
Alan Wu
dd80d9b089 YJIT: Filter & calls from specialized C method codegen
Evident with the crash reported in [Bug #20997], the C replacement
codegen functions aren't authored to handle block arguments (nor
should they because the extra code from the complexity defeats
optimization). Filter sites with VM_CALL_ARGS_BLOCKARG.
2025-01-08 19:47:39 -05:00
Nick Dower
37356b713c YJIT: Add crashing test for yielding keyword args
Code like the following is crashing for us on 3.4.1:

```ruby
def a(&) = yield(x: 0)

1000.times { a { |x:| x } }
```

Crash:

```
ruby: YJIT has panicked. More info to follow...
thread '<unnamed>' panicked at ./yjit/src/codegen.rs:8018:13:
assertion `left == right` failed
  left: 0
 right: 1
```

Co-authored-by: Dani Acherkan <dtl.117@gmail.com>
2025-01-04 12:53:20 -05:00
Nobuyoshi Nakada
a37a2f5fef
Clear failure_log by assignment like as appending by assignment 2024-12-27 11:23:23 +09:00
Luke Gruber
38af38edcb Fix ractor move of unshareable frozen objects
These objects didn't retain their frozen status after the move

Bug [#19408]
2024-12-24 11:38:44 +09:00
Alan Wu
57825bf390 bootstraptest: On -j failure, show total test count
It used to always try to divide by zero like:

    FAIL 1/0 tests failed
2024-12-12 19:54:37 -05:00
Koichi Sasada
0bdb38ba6b Ractor.set_if_absent(key)
to initialize ractor local storage in thread-safety.
[Feature #20875]
2024-12-13 06:22:13 +09:00
Matt Valentine-House
58b4e249ed [MMTk/CI] Skip Ractor btests with MMTk
currently these are flaky, so until we can make them more robust, we'll
skip them for MMTk CI
2024-12-06 09:48:30 +00:00
Naoto Ono
de50236c3a
Enable Launchable integration in compilers.yml (#12136) 2024-12-03 15:02:18 +09:00
Randy Stauner
8f9b9aecd0
YJIT: Implement opt_reverse insn (#12175) 2024-11-26 16:49:24 -05:00
Randy Stauner
1dd40ec18a
Optimize instructions when creating an array just to call include? (#12123)
* Add opt_duparray_send insn to skip the allocation on `#include?`

If the method isn't going to modify the array we don't need to copy it.
This avoids the allocation / array copy for things like `[:a, :b].include?(x)`.

This adds a BOP for include? and tracks redefinition for it on Array.

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>

* YJIT: Implement opt_duparray_send include_p

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>

* Update opt_newarray_send to support simple forms of include?(arg)

Similar to opt_duparray_send but for non-static arrays.

* YJIT: Implement opt_newarray_send include_p

---------

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>
2024-11-26 14:31:08 -05:00
Yusuke Endoh
cabfaa9fb4 Extend the timeout of fork-exit bootstraptest
It often fails randomly.

http://ci.rvm.jp/results/trunk-yjit@ruby-sp2-noble-docker/5421564
```
 Fstderr output is not empty
   bootstraptest.test_fork.rb_78_287.rb:16:in 'block in <main>': failed (RuntimeError)
           from <internal:numeric>:257:in 'Integer#times'
           from bootstraptest.test_fork.rb_78_287.rb:10:in '<main>'
```

I'm not sure why the frequency of failure has suddenly increased,
though.
2024-11-21 04:23:15 -06:00
Takashi Kokubun
31bdffb5b9
YJIT: Specialize String#dup (#12090) 2024-11-14 18:15:39 -05:00
Takashi Kokubun
7e2f9eaccd
YJIT: Specialize Integer#pred (#12082) 2024-11-14 12:04:48 -05:00
Randy Stauner
beafae9750
YJIT: Specialize String#[] (String#slice) with fixnum arguments (#12069)
* YJIT: Specialize `String#[]` (`String#slice`) with fixnum arguments

String#[] is in the top few C calls of several YJIT benchmarks:
liquid-compile rubocop mail sudoku

This speeds up these benchmarks by 1-2%.

* YJIT: Try harder to get type info for `String#[]`

In the large generated code of the mail gem the context doesn't have
the type info.  In that case if we peek at the stack and add a guard
we can still apply the specialization
and it speeds up the mail benchmark by 5%.

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>

---------

Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Takashi Kokubun (k0kubun) <takashikkbn@gmail.com>
2024-11-13 12:25:09 -05:00
Koichi Sasada
78064d0770 skip SystemStackError
with -O0 build, prism parser consumes a lot of machine stack and
it doesn't work with minimum machine stack for threads, which
specified with `RUBY_THREAD_MACHINE_STACK_SIZE=1`.

So simply ignore `SystemStackError` for btest.
2024-11-08 18:02:46 +09:00
Koichi Sasada
aa63699d10 support require in non-main Ractors
Many libraries should be loaded on the main ractor because of
setting constants with unshareable objects and so on.

This patch allows to call `requore` on non-main Ractors by
asking the main ractor to call `require` on it. The calling ractor
waits for the result of `require` from the main ractor.

If the `require` call failed with some reasons, an exception
objects will be deliverred from the main ractor to the calling ractor
if it is copy-able.

Same on `require_relative` and `require` by `autoload`.

Now `Ractor.new{pp obj}` works well (the first call of `pp` requires
`pp` library implicitly).

[Feature #20627]
2024-11-08 18:02:46 +09:00
Étienne Barrié
257f78fb67 Show where mutated chilled strings were allocated
[Feature #20205]

The warning now suggests running with --debug-frozen-string-literal:

```
test.rb:3: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)
```

When using --debug-frozen-string-literal, the location where the string
was created is shown:

```
test.rb:3: warning: literal string will be frozen in the future
test.rb:1: info: the string was created here
```

When resurrecting strings and debug mode is not enabled, the overhead is a simple FL_TEST_RAW.
When mutating chilled strings and deprecation warnings are not enabled,
the overhead is a simple warning category enabled check.

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-10-21 12:33:02 +02:00
Aaron Patterson
a0ecdbfbfe Remove "simple" flag from forwarded ICs
I don't think we should ever consider forwarded IC's to be "simple".
Previously, the "simple" flag would be copied to the derived IC and this
happened to cause struct set / get iseqs to write an invalid CC
fastpath:

  f45eb3dcb9/vm_insnhelper.c (L4726-L4729)

[Bug #20799]
2024-10-15 19:06:07 -07:00
Peter Zhu
f45eb3dcb9 Use GC.respond_to?(:compact) in bootstraptest/test_yjit.rb
defined?(GC.compact) will always return true even when compaction is not
supported. We should use GC.respond_to?(:compact) instead.
2024-10-15 14:34:24 -04:00
tompng
f4e548924e Update bootstraptest test for colon-style hash inspect 2024-10-03 18:47:09 +09:00
Jeremy Evans
268c72377b
Raise a compile error for break/next/redo inside eval in cases where it is optimized away
In cases where break/next/redo are not valid syntax, they should
raise a SyntaxError even if inside a conditional block that is
optimized away.

Fixes [Bug #20597]

Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2024-09-18 16:54:56 -07:00
Takashi Kokubun
5b129c899a
YJIT: Pass method arguments using registers (#11280)
* YJIT: Pass method arguments using registers

* s/at_current_insn/at_compile_target/

* Implement register shuffle
2024-08-27 17:04:43 -07:00
John Hawthorn
87a85550ed Re-initialize vm->ractor.sched.lock after fork
Previously under certain conditions it was possible to encounter a
deadlock in the forked child process if ractor.sched.lock was held.

Co-authored-by: Nathan Froyd <froydnj@gmail.com>
2024-08-13 11:52:24 -07:00
Randy Stauner
acbb8d4fb5 Expand opt_newarray_send to support Array#pack with buffer keyword arg
Use an enum for the method arg instead of needing to add an id
that doesn't map to an actual method name.

$ ruby --dump=insns -e 'b = "x"; [v].pack("E*", buffer: b)'

before:

```
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] b@0
0000 putchilledstring                       "x"                       (   1)[Li]
0002 setlocal_WC_0                          b@0
0004 putself
0005 opt_send_without_block                 <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0007 newarray                               1
0009 putchilledstring                       "E*"
0011 getlocal_WC_0                          b@0
0013 opt_send_without_block                 <calldata!mid:pack, argc:2, kw:[#<Symbol:0x000000000023110c>], KWARG>
0015 leave
```

after:

```
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] b@0
0000 putchilledstring                       "x"                       (   1)[Li]
0002 setlocal_WC_0                          b@0
0004 putself
0005 opt_send_without_block                 <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0007 putchilledstring                       "E*"
0009 getlocal                               b@0, 0
0012 opt_newarray_send                      3, 5
0015 leave
```
2024-07-29 16:26:58 -04:00
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