<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ruby.git/test/ruby/test_yjit.rb, branch v4.0.2</title>
<subtitle>The Ruby Programming Language</subtitle>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/'/>
<entry>
<title>YJIT: Fix not reading locals from `cfp-&gt;ep` after `YJIT.enable` and exceptional entry</title>
<updated>2026-03-16T18:03:25+00:00</updated>
<author>
<name>Alan Wu</name>
<email>XrXr@users.noreply.github.com</email>
</author>
<published>2026-03-12T22:33:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=8466e93b1d6eb85ad5952ab3a10575fa453e77e2'/>
<id>8466e93b1d6eb85ad5952ab3a10575fa453e77e2</id>
<content type='text'>
[Backport #21941]

In case of `--yjit-disable`, YJIT only starts to record environment
escapes after `RubyVM::YJIT.enable`. Previously we falsely assumed that
we always have a full history all the way back to VM boot. This had YJIT
install and run code that assume EP=BP when EP≠BP for some exceptional
entry into the middle of a running frame, if the environment escaped
before `YJIT.enable`.

The fix is to reject exceptional entry with an escaped environment.
Rename things and explain in more detail how the predicate for deciding
to assume EP=BP works. It's quite subtle since it reasons about all
parties in the system that push a control frame and then run JIT code.

Note that while can_assume_on_stack_env() checks the currently running
environment if it so happens to be the one YJIT is compiling against, it
can return true for any ISEQ. The check isn't necessary for fixing the
bug, and the load bearing part of this patch is the change to
exceptional entries.

This fix is flat on speed and space on ruby-bench headline benchmarks.

Many thanks for the community effort to create a small test case for
this bug.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
[Backport #21941]

In case of `--yjit-disable`, YJIT only starts to record environment
escapes after `RubyVM::YJIT.enable`. Previously we falsely assumed that
we always have a full history all the way back to VM boot. This had YJIT
install and run code that assume EP=BP when EP≠BP for some exceptional
entry into the middle of a running frame, if the environment escaped
before `YJIT.enable`.

The fix is to reject exceptional entry with an escaped environment.
Rename things and explain in more detail how the predicate for deciding
to assume EP=BP works. It's quite subtle since it reasons about all
parties in the system that push a control frame and then run JIT code.

Note that while can_assume_on_stack_env() checks the currently running
environment if it so happens to be the one YJIT is compiling against, it
can return true for any ISEQ. The check isn't necessary for fixing the
bug, and the load bearing part of this patch is the change to
exceptional entries.

This fix is flat on speed and space on ruby-bench headline benchmarks.

Many thanks for the community effort to create a small test case for
this bug.
</pre>
</div>
</content>
</entry>
<entry>
<title>YJIT: Bail out if proc would be stored above stack top</title>
<updated>2025-12-16T04:05:27+00:00</updated>
<author>
<name>Randy Stauner</name>
<email>randy@r4s6.net</email>
</author>
<published>2025-12-11T22:56:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=9168cad4d63a5d281d443bde4edea6be213b0b25'/>
<id>9168cad4d63a5d281d443bde4edea6be213b0b25</id>
<content type='text'>
Fixes [Bug #21266].
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fixes [Bug #21266].
</pre>
</div>
</content>
</entry>
<entry>
<title>Remove `opt_aref_with` and `opt_aset_with`</title>
<updated>2025-08-26T20:02:17+00:00</updated>
<author>
<name>Aaron Patterson</name>
<email>tenderlove@ruby-lang.org</email>
</author>
<published>2025-08-25T21:31:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=fb6e3a80009a744a4e0b75660f1ce6da65e20e6c'/>
<id>fb6e3a80009a744a4e0b75660f1ce6da65e20e6c</id>
<content type='text'>
When these instructions were introduced it was common to read from a
hash with mutable string literals.  However, these days, I think these
instructions are fairly rare.

I tested this with the lobsters benchmark, and saw no difference in
speed.  In order to be sure, I tracked down every use of this
instruction in the lobsters benchmark, and there were only 4 places
where it was used.

Additionally, this patch fixes a case where "chilled strings" should
emit a warning but they don't.

```ruby
class Foo
  def self.[](x)= x.gsub!(/hello/, "hi")
end

Foo["hello world"]
```

Removing these instructions shows this warning:

```
&gt; ./miniruby -vw test.rb
ruby 3.5.0dev (2025-08-25T21:36:50Z rm-opt_aref_with dca08e286c) +PRISM [arm64-darwin24]
test.rb:2: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)
```

[Feature #21553]
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
When these instructions were introduced it was common to read from a
hash with mutable string literals.  However, these days, I think these
instructions are fairly rare.

I tested this with the lobsters benchmark, and saw no difference in
speed.  In order to be sure, I tracked down every use of this
instruction in the lobsters benchmark, and there were only 4 places
where it was used.

Additionally, this patch fixes a case where "chilled strings" should
emit a warning but they don't.

```ruby
class Foo
  def self.[](x)= x.gsub!(/hello/, "hi")
end

Foo["hello world"]
```

Removing these instructions shows this warning:

```
&gt; ./miniruby -vw test.rb
ruby 3.5.0dev (2025-08-25T21:36:50Z rm-opt_aref_with dca08e286c) +PRISM [arm64-darwin24]
test.rb:2: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)
```

[Feature #21553]
</pre>
</div>
</content>
</entry>
<entry>
<title>Prevent enabling yjit when zjit enabled (GH-13358)</title>
<updated>2025-05-16T17:31:43+00:00</updated>
<author>
<name>Daniel Colson</name>
<email>danieljamescolson@gmail.com</email>
</author>
<published>2025-05-16T17:31:43+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=eead83160bcc5f49706e05669e5a7e2620b9b605'/>
<id>eead83160bcc5f49706e05669e5a7e2620b9b605</id>
<content type='text'>
`ruby --yjit --zjit` already warns and exits, but it was still possible
to enable both with `ruby --zjit -e 'RubyVM:YJIT.enable`.

This commit prevents that with a warning and an early return. (We could
also exit, but that seems a bit unfriendly once we're already running
the program.)

Co-authored-by: ywenc &lt;ywenc@github.com&gt;</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
`ruby --yjit --zjit` already warns and exits, but it was still possible
to enable both with `ruby --zjit -e 'RubyVM:YJIT.enable`.

This commit prevents that with a warning and an early return. (We could
also exit, but that seems a bit unfriendly once we're already running
the program.)

Co-authored-by: ywenc &lt;ywenc@github.com&gt;</pre>
</div>
</content>
</entry>
<entry>
<title>Remove leading `nop` from block when we don't need it</title>
<updated>2025-03-20T18:49:13+00:00</updated>
<author>
<name>Aaron Patterson</name>
<email>tenderlove@ruby-lang.org</email>
</author>
<published>2025-03-20T16:48:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=62cc3464d902ee7a399ec8c38606fdc0ee98f05e'/>
<id>62cc3464d902ee7a399ec8c38606fdc0ee98f05e</id>
<content type='text'>
Blocks insert a leading `nop` instruction in order to execute a "block
call" tracepoint. Block compilation unconditionally inserts a leading
`nop` plus a label after the instruction:

  https://github.com/ruby/ruby/blob/641f15b1c6bd8921407a1f045573d3b0605f00d3/prism_compile.c#L6867-L6869

This `nop` instruction is used entirely for firing the block entry
tracepoint.  The label exists so that the block can contain a loop but
the block entry tracepoint is executed only once.

For example, the following code is an infinite loop, but should only
execute the b_call tracepoint once:

```ruby
-&gt; { redo }.call
```

Previous to this commit, we would eliminate the `nop` instruction, but
only if there were no other jump instructions inside the block.  This
means that the following code would still contain a leading `nop` even
though the label following the `nop` is unused:

```ruby
-&gt; { nil if bar }
```

```
== disasm: #&lt;ISeq:block in &lt;main&gt;@test.rb:1 (1,2)-(1,17)&gt; (catch: FALSE)
0000 nop                                                              (   1)[Bc]
0001 putself                                [Li]
0002 opt_send_without_block                 &lt;calldata!mid:bar, argc:0, FCALL|VCALL|ARGS_SIMPLE&gt;
0004 branchunless                           8
0006 putnil
0007 leave                                  [Br]
0008 putnil
0009 leave                                  [Br]
```

This commit checks to see if the label inserted after the `nop` is
actually a jump target.  If it's not a jump target, then we should be
safe to eliminate the leading `nop`:

```
&gt; build-master/miniruby --dump=insns test.rb
== disasm: #&lt;ISeq:&lt;main&gt;@test.rb:1 (1,0)-(1,17)&gt;
0000 putspecialobject                       1                         (   1)[Li]
0002 send                                   &lt;calldata!mid:lambda, argc:0, FCALL&gt;, block in &lt;main&gt;
0005 leave

== disasm: #&lt;ISeq:block in &lt;main&gt;@test.rb:1 (1,2)-(1,17)&gt;
0000 putself                                                          (   1)[LiBc]
0001 opt_send_without_block                 &lt;calldata!mid:bar, argc:0, FCALL|VCALL|ARGS_SIMPLE&gt;
0003 branchunless                           7
0005 putnil
0006 leave                                  [Br]
0007 putnil
0008 leave                                  [Br]
```

We have a test for b_call tracepoints that use `redo` here:

  https://github.com/ruby/ruby/blob/aebf96f371c8d874398e0041b798892e545fa206/test/ruby/test_settracefunc.rb#L1728-L1736
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Blocks insert a leading `nop` instruction in order to execute a "block
call" tracepoint. Block compilation unconditionally inserts a leading
`nop` plus a label after the instruction:

  https://github.com/ruby/ruby/blob/641f15b1c6bd8921407a1f045573d3b0605f00d3/prism_compile.c#L6867-L6869

This `nop` instruction is used entirely for firing the block entry
tracepoint.  The label exists so that the block can contain a loop but
the block entry tracepoint is executed only once.

For example, the following code is an infinite loop, but should only
execute the b_call tracepoint once:

```ruby
-&gt; { redo }.call
```

Previous to this commit, we would eliminate the `nop` instruction, but
only if there were no other jump instructions inside the block.  This
means that the following code would still contain a leading `nop` even
though the label following the `nop` is unused:

```ruby
-&gt; { nil if bar }
```

```
== disasm: #&lt;ISeq:block in &lt;main&gt;@test.rb:1 (1,2)-(1,17)&gt; (catch: FALSE)
0000 nop                                                              (   1)[Bc]
0001 putself                                [Li]
0002 opt_send_without_block                 &lt;calldata!mid:bar, argc:0, FCALL|VCALL|ARGS_SIMPLE&gt;
0004 branchunless                           8
0006 putnil
0007 leave                                  [Br]
0008 putnil
0009 leave                                  [Br]
```

This commit checks to see if the label inserted after the `nop` is
actually a jump target.  If it's not a jump target, then we should be
safe to eliminate the leading `nop`:

```
&gt; build-master/miniruby --dump=insns test.rb
== disasm: #&lt;ISeq:&lt;main&gt;@test.rb:1 (1,0)-(1,17)&gt;
0000 putspecialobject                       1                         (   1)[Li]
0002 send                                   &lt;calldata!mid:lambda, argc:0, FCALL&gt;, block in &lt;main&gt;
0005 leave

== disasm: #&lt;ISeq:block in &lt;main&gt;@test.rb:1 (1,2)-(1,17)&gt;
0000 putself                                                          (   1)[LiBc]
0001 opt_send_without_block                 &lt;calldata!mid:bar, argc:0, FCALL|VCALL|ARGS_SIMPLE&gt;
0003 branchunless                           7
0005 putnil
0006 leave                                  [Br]
0007 putnil
0008 leave                                  [Br]
```

We have a test for b_call tracepoints that use `redo` here:

  https://github.com/ruby/ruby/blob/aebf96f371c8d874398e0041b798892e545fa206/test/ruby/test_settracefunc.rb#L1728-L1736
</pre>
</div>
</content>
</entry>
<entry>
<title>Allow YJIT `mem-size` and `call-threshold` to be set at runtime via `YJIT.enable()` (#12505)</title>
<updated>2025-03-03T20:45:39+00:00</updated>
<author>
<name>annichai-stripe</name>
<email>annichai@stripe.com</email>
</author>
<published>2025-03-03T20:45:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=5085ec3ed90beb54125d5eb9329e202ae1542b5c'/>
<id>5085ec3ed90beb54125d5eb9329e202ae1542b5c</id>
<content type='text'>
* first commit

* yjit.rb change

* revert formatting

* rename mem-size to exec-mem-size for correctness

* wip, move setting into rb_yjit_enable directly

* remove unused helper functions

* add in call threshold

* input validation with extensive eprintln

* delete test script

* exec-mem-size -&gt; mem-size

* handle input validation with asserts

* add test cases related to input validation

* modify test cases

* move validation out of rs, into rb

* add comments

* remove trailing spaces

* remove logging

Co-authored-by: Takashi Kokubun &lt;takashikkbn@gmail.com&gt;

* remove helper fn

* Update test/ruby/test_yjit.rb

Co-authored-by: Takashi Kokubun &lt;takashikkbn@gmail.com&gt;

* trailing white space

---------

Co-authored-by: Alan Wu &lt;XrXr@users.noreply.github.com&gt;
Co-authored-by: Takashi Kokubun &lt;takashikkbn@gmail.com&gt;
Co-authored-by: Maxime Chevalier-Boisvert &lt;maxime.chevalierboisvert@shopify.com&gt;</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* first commit

* yjit.rb change

* revert formatting

* rename mem-size to exec-mem-size for correctness

* wip, move setting into rb_yjit_enable directly

* remove unused helper functions

* add in call threshold

* input validation with extensive eprintln

* delete test script

* exec-mem-size -&gt; mem-size

* handle input validation with asserts

* add test cases related to input validation

* modify test cases

* move validation out of rs, into rb

* add comments

* remove trailing spaces

* remove logging

Co-authored-by: Takashi Kokubun &lt;takashikkbn@gmail.com&gt;

* remove helper fn

* Update test/ruby/test_yjit.rb

Co-authored-by: Takashi Kokubun &lt;takashikkbn@gmail.com&gt;

* trailing white space

---------

Co-authored-by: Alan Wu &lt;XrXr@users.noreply.github.com&gt;
Co-authored-by: Takashi Kokubun &lt;takashikkbn@gmail.com&gt;
Co-authored-by: Maxime Chevalier-Boisvert &lt;maxime.chevalierboisvert@shopify.com&gt;</pre>
</div>
</content>
</entry>
<entry>
<title>Only count VM instructions in YJIT stats builds</title>
<updated>2025-02-14T19:39:35+00:00</updated>
<author>
<name>Aaron Patterson</name>
<email>tenderlove@ruby-lang.org</email>
</author>
<published>2025-02-13T17:56:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=8cafa5b8ce5e35881bf5077d2bfafc03274189f2'/>
<id>8cafa5b8ce5e35881bf5077d2bfafc03274189f2</id>
<content type='text'>
The instruction counter is slowing multi-Ractor applications.  I had
changed it to use a thread local, but using a thread local is slowing
single threaded applications.  This commit only enables the instruction
counter in YJIT stats builds until we can figure out a way to gather the
information with lower overhead.

Co-authored-by: Randy Stauner &lt;randy.stauner@shopify.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The instruction counter is slowing multi-Ractor applications.  I had
changed it to use a thread local, but using a thread local is slowing
single threaded applications.  This commit only enables the instruction
counter in YJIT stats builds until we can figure out a way to gather the
information with lower overhead.

Co-authored-by: Randy Stauner &lt;randy.stauner@shopify.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>YJIT: Fix crash when yielding keyword arguments</title>
<updated>2025-01-04T17:53:20+00:00</updated>
<author>
<name>Alan Wu</name>
<email>XrXr@users.noreply.github.com</email>
</author>
<published>2025-01-04T16:41:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=c71addc5227f2f7a04db2b2fb4c14d464639f3f6'/>
<id>c71addc5227f2f7a04db2b2fb4c14d464639f3f6</id>
<content type='text'>
Previously, the code for dropping surplus arguments when yielding
into blocks erroneously attempted to drop keyword arguments when there
is in fact no surplus arguments. Fix the condition and test that
supplying the exact number of keyword arguments as require compiles
without fallback.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Previously, the code for dropping surplus arguments when yielding
into blocks erroneously attempted to drop keyword arguments when there
is in fact no surplus arguments. Fix the condition and test that
supplying the exact number of keyword arguments as require compiles
without fallback.
</pre>
</div>
</content>
</entry>
<entry>
<title>YJIT: Replace Array#each only when YJIT is enabled (#11955)</title>
<updated>2024-11-04T16:14:28+00:00</updated>
<author>
<name>Takashi Kokubun</name>
<email>takashikkbn@gmail.com</email>
</author>
<published>2024-11-04T16:14:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=478e0fc710b8fefaa3bdb7cb41dda8716e29927a'/>
<id>478e0fc710b8fefaa3bdb7cb41dda8716e29927a</id>
<content type='text'>
* YJIT: Replace Array#each only when YJIT is enabled

* Add comments about BUILTIN_ATTR_C_TRACE

* Make Ruby Array#each available with --yjit as well

* Fix all paths that expect a C location

* Use method_basic_definition_p to detect patches

* Copy a comment about C_TRACE flag to compilers

* Rephrase a comment about add_yjit_hook

* Give METHOD_ENTRY_BASIC flag to Array#each

* Add --yjit-c-builtin option

* Allow inconsistent source_location in test-spec

* Refactor a check of BUILTIN_ATTR_C_TRACE

* Set METHOD_ENTRY_BASIC without touching vm-&gt;running</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* YJIT: Replace Array#each only when YJIT is enabled

* Add comments about BUILTIN_ATTR_C_TRACE

* Make Ruby Array#each available with --yjit as well

* Fix all paths that expect a C location

* Use method_basic_definition_p to detect patches

* Copy a comment about C_TRACE flag to compilers

* Rephrase a comment about add_yjit_hook

* Give METHOD_ENTRY_BASIC flag to Array#each

* Add --yjit-c-builtin option

* Allow inconsistent source_location in test-spec

* Refactor a check of BUILTIN_ATTR_C_TRACE

* Set METHOD_ENTRY_BASIC without touching vm-&gt;running</pre>
</div>
</content>
</entry>
<entry>
<title>Skip GC compaction tests in test_yjit.rb when not supported</title>
<updated>2024-10-16T14:17:54+00:00</updated>
<author>
<name>Peter Zhu</name>
<email>peter@peterzhu.ca</email>
</author>
<published>2024-10-16T13:45:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ebfe615a0cdffc28a73f279e3df8068831ddbf6e'/>
<id>ebfe615a0cdffc28a73f279e3df8068831ddbf6e</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
</feed>
