<feed xmlns='http://www.w3.org/2005/Atom'>
<title>ruby.git, branch master</title>
<subtitle>The Ruby Programming Language</subtitle>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/'/>
<entry>
<title>ZJIT: Drop redundant type guards via block-local HIR canonicalize (#16828)</title>
<updated>2026-05-12T20:39:11+00:00</updated>
<author>
<name>Daichi Kamiyama</name>
<email>32436625+dak2@users.noreply.github.com</email>
</author>
<published>2026-05-12T20:39:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ece14b61f505eea1ebefb3b8295df0fcf4d22567'/>
<id>ece14b61f505eea1ebefb3b8295df0fcf4d22567</id>
<content type='text'>
## Summary

`GuardType` narrows only its result id, not the original input. 
A later guard on the same input therefore looks unfoldable to `fold_constants` even though the side-exit semantics already prove the narrower type.
Canonicalize rewrites later uses to the most recent `Guard*` result, making the redundant guard foldable.
For example, in this CFG-join shape:

 ```ruby
  def test(n, cond)                                                                                                           
    if cond   
      a = n + 1
    else                                                                                                                      
      a = n + 2
    end                                                                                                                       
    n + a   # `n` gets a redundant Fixnum guard here
  end
 ```

This PR adds a block-local HIR `canonicalize` pass that walks each block in RPO and rewrites every operand through union-find plus a per-block `rewrite_map` keyed on the most recent `Guard*` for that value. 
After canonicalization, `infer_types` can narrow merge-block parameter types and `fold_constants` can then drop the redundant guards in both shapes above.

Inspired by Cranelift's canonicalize https://cfallin.org/blog/2026/04/09/aegraph/

Fixes: https://github.com/Shopify/ruby/issues/978

## Benchmarks

Bench (arm64 linux devcontainer, ruby/ruby-bench, warmup=10 bench=20)

```
  Throughput              master/staged
    lobsters              1.021 (+2.1%, within ±3-6% noise)
    railsbench            1.012 (+1.2%, within ±3-4% noise)

  --zjit-stats            lobsters / railsbench
    code_region_bytes     -1.1%   / -1.0%   (redundant CFG-join guards still removed)
    guard_type_count      -29.4%  / +30.1%  (railsbench likely single-run noise) ⚠
    compile_hir_time      +14.6%  / +13.7%  (canonicalize_time: 67ms / 31ms)
    invalidation_time      ±0%    /  ±0%
```</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
## Summary

`GuardType` narrows only its result id, not the original input. 
A later guard on the same input therefore looks unfoldable to `fold_constants` even though the side-exit semantics already prove the narrower type.
Canonicalize rewrites later uses to the most recent `Guard*` result, making the redundant guard foldable.
For example, in this CFG-join shape:

 ```ruby
  def test(n, cond)                                                                                                           
    if cond   
      a = n + 1
    else                                                                                                                      
      a = n + 2
    end                                                                                                                       
    n + a   # `n` gets a redundant Fixnum guard here
  end
 ```

This PR adds a block-local HIR `canonicalize` pass that walks each block in RPO and rewrites every operand through union-find plus a per-block `rewrite_map` keyed on the most recent `Guard*` for that value. 
After canonicalization, `infer_types` can narrow merge-block parameter types and `fold_constants` can then drop the redundant guards in both shapes above.

Inspired by Cranelift's canonicalize https://cfallin.org/blog/2026/04/09/aegraph/

Fixes: https://github.com/Shopify/ruby/issues/978

## Benchmarks

Bench (arm64 linux devcontainer, ruby/ruby-bench, warmup=10 bench=20)

```
  Throughput              master/staged
    lobsters              1.021 (+2.1%, within ±3-6% noise)
    railsbench            1.012 (+1.2%, within ±3-4% noise)

  --zjit-stats            lobsters / railsbench
    code_region_bytes     -1.1%   / -1.0%   (redundant CFG-join guards still removed)
    guard_type_count      -29.4%  / +30.1%  (railsbench likely single-run noise) ⚠
    compile_hir_time      +14.6%  / +13.7%  (canonicalize_time: 67ms / 31ms)
    invalidation_time      ±0%    /  ±0%
```</pre>
</div>
</content>
</entry>
<entry>
<title>ZJIT: Track param-type changes in infer_types fixpoint</title>
<updated>2026-05-12T17:03:27+00:00</updated>
<author>
<name>Max Bernstein</name>
<email>ruby@bernsteinbear.com</email>
</author>
<published>2026-05-11T16:17:09+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=ab849a434bb1fd12c8e39e601be65a2bda240b39'/>
<id>ab849a434bb1fd12c8e39e601be65a2bda240b39</id>
<content type='text'>
Branch arms (IfTrue, IfFalse, Jump) update target block param types
but were not flagging the fixpoint loop's `changed` bit. With a pure
shuffle block (no non-branch insns to drive `changed` via their
own infer_type), the loop could exit while param types were still
widening. Now each branch arm sets `changed = true` whenever the
union actually grew a param's type.

Add an HIR build test: a self-loop with a 4-cycle param rotation
must reach the full union of all four input types at every param,
which would previously fall short by one type even with parallel
phi semantics.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Branch arms (IfTrue, IfFalse, Jump) update target block param types
but were not flagging the fixpoint loop's `changed` bit. With a pure
shuffle block (no non-branch insns to drive `changed` via their
own infer_type), the loop could exit while param types were still
widening. Now each branch arm sets `changed = true` whenever the
union actually grew a param's type.

Add an HIR build test: a self-loop with a 4-cycle param rotation
must reach the full union of all four input types at every param,
which would previously fall short by one type even with parallel
phi semantics.
</pre>
</div>
</content>
</entry>
<entry>
<title>ZJIT: Infer block param types with parallel phi semantics</title>
<updated>2026-05-12T17:03:27+00:00</updated>
<author>
<name>Max Bernstein</name>
<email>ruby@bernsteinbear.com</email>
</author>
<published>2026-05-11T13:47:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=33a4fde9cb2a6d7fcf9b89e0f24e0e9c88ccf24b'/>
<id>33a4fde9cb2a6d7fcf9b89e0f24e0e9c88ccf24b</id>
<content type='text'>
In infer_types, snapshot the types of all branch arguments before
writing to any target block param. Previously, when a self-loop's
branch args referenced the target's own params (e.g. a phi swap),
updating params sequentially could observe values updated earlier
in the same loop iteration.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
In infer_types, snapshot the types of all branch arguments before
writing to any target block param. Previously, when a self-loop's
branch args referenced the target's own params (e.g. a phi swap),
updating params sequentially could observe values updated earlier
in the same loop iteration.
</pre>
</div>
</content>
</entry>
<entry>
<title>ZJIT: Remove unused GuardTypeNot instruction (#16926)</title>
<updated>2026-05-12T16:16:59+00:00</updated>
<author>
<name>Max Bernstein</name>
<email>tekknolagi@gmail.com</email>
</author>
<published>2026-05-12T16:16:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=f1ca69b9de6243925a00073c54e18fb5db461a10'/>
<id>f1ca69b9de6243925a00073c54e18fb5db461a10</id>
<content type='text'>
We changed how AnyToString worked and no longer need this instruction.</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
We changed how AnyToString worked and no longer need this instruction.</pre>
</div>
</content>
</entry>
<entry>
<title>[Bug #22057] test-bundler: add `--enable-gems` explicitly</title>
<updated>2026-05-12T14:39:26+00:00</updated>
<author>
<name>Tsutomu Katsube</name>
<email>tsuto.katsube@gmail.com</email>
</author>
<published>2026-05-09T11:36:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=9e0261a9dc1efd17a4698b1a804eb3641520d9da'/>
<id>9e0261a9dc1efd17a4698b1a804eb3641520d9da</id>
<content type='text'>
This is because `make test-bundler-parallel` command, which runs
successfully via turbo_tests, does not have `--disable-gems` option
specified.

`--disable-gems` is currently set in `RUN_OPTS`. Removing it would
affect not only test-bundler but the entire test suite, so I have
configured it specifically for this command instead.

This patch will fix an error in `./spec/bundler/bundler/cli_common_spec.rb`
when executing `make test-bundler`.

Co-authored-by: Sutou Kouhei &lt;kou@clear-code.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This is because `make test-bundler-parallel` command, which runs
successfully via turbo_tests, does not have `--disable-gems` option
specified.

`--disable-gems` is currently set in `RUN_OPTS`. Removing it would
affect not only test-bundler but the entire test suite, so I have
configured it specifically for this command instead.

This patch will fix an error in `./spec/bundler/bundler/cli_common_spec.rb`
when executing `make test-bundler`.

Co-authored-by: Sutou Kouhei &lt;kou@clear-code.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>[Bug #21989] Fix FiberError init order to precede all possible usage</title>
<updated>2026-05-12T12:38:35+00:00</updated>
<author>
<name>Yuta Saito</name>
<email>kateinoigakukun@gmail.com</email>
</author>
<published>2026-05-12T08:00:02+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=0fa6cb88f5e238e819cd663b1edc80db59a74b2a'/>
<id>0fa6cb88f5e238e819cd663b1edc80db59a74b2a</id>
<content type='text'>
The `FiberError` class can be used in `fiber_pool_initialize` through
`fiber_pool_expand` but `FiberError` was defined after
`fiber_pool_initialize` in `Init_Cont`. This commit moves the definition
of `FiberError` before `fiber_pool_initialize` to ensure that
`FiberError` is defined before any possible usage.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The `FiberError` class can be used in `fiber_pool_initialize` through
`fiber_pool_expand` but `FiberError` was defined after
`fiber_pool_initialize` in `Init_Cont`. This commit moves the definition
of `FiberError` before `fiber_pool_initialize` to ensure that
`FiberError` is defined before any possible usage.
</pre>
</div>
</content>
</entry>
<entry>
<title>[Bug #20409] Make `break` and `redo` in `END` syntax error</title>
<updated>2026-05-12T11:04:00+00:00</updated>
<author>
<name>Nobuyoshi Nakada</name>
<email>nobu@ruby-lang.org</email>
</author>
<published>2025-11-14T08:38:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=2f9432d2114833b105577ad72e323990daf5101e'/>
<id>2f9432d2114833b105577ad72e323990daf5101e</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>Show "END in method" warning immediately</title>
<updated>2026-05-12T11:04:00+00:00</updated>
<author>
<name>Nobuyoshi Nakada</name>
<email>nobu@ruby-lang.org</email>
</author>
<published>2025-11-14T07:21:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=803b3169e0a8533cb870c995029d50cba190c322'/>
<id>803b3169e0a8533cb870c995029d50cba190c322</id>
<content type='text'>
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
</pre>
</div>
</content>
</entry>
<entry>
<title>[ruby/json] generator.c: Handle stupidly large depth</title>
<updated>2026-05-12T07:07:28+00:00</updated>
<author>
<name>Jean Boussier</name>
<email>jean.boussier@gmail.com</email>
</author>
<published>2026-05-12T06:49:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=95c041bd3bdf0af0be00ed8546947dc73dc92897'/>
<id>95c041bd3bdf0af0be00ed8546947dc73dc92897</id>
<content type='text'>
https://github.com/ruby/json/commit/1d32bd4596
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
https://github.com/ruby/json/commit/1d32bd4596
</pre>
</div>
</content>
</entry>
<entry>
<title>[ruby/json] Add missing write barrier in ParserConfig</title>
<updated>2026-05-12T06:25:10+00:00</updated>
<author>
<name>Jean Boussier</name>
<email>jean.boussier@gmail.com</email>
</author>
<published>2026-05-12T06:19:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.ruby-lang.org/ruby.git/commit/?id=fb369617736b868e6896426c1135fbe23f34cecd'/>
<id>fb369617736b868e6896426c1135fbe23f34cecd</id>
<content type='text'>
This couldn't possibly lead to a crash unless you'd mess
with GC.stress or `send(:initialize)` on an old object.

But for correctness, it should be there.

https://github.com/ruby/json/commit/ab6972d797
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This couldn't possibly lead to a crash unless you'd mess
with GC.stress or `send(:initialize)` on an old object.

But for correctness, it should be there.

https://github.com/ruby/json/commit/ab6972d797
</pre>
</div>
</content>
</entry>
</feed>
