Skip to content

[pull] master from ruby:master#790

Merged
pull[bot] merged 4 commits intoturkdevops:masterfrom
ruby:master
Feb 19, 2026
Merged

[pull] master from ruby:master#790
pull[bot] merged 4 commits intoturkdevops:masterfrom
ruby:master

Conversation

@pull
Copy link

@pull pull bot commented Feb 19, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

jhawthorn and others added 4 commits February 18, 2026 12:56
Similar to the way ZJIT already folds +, -, and * operations. One
complication is that the % operator behaves differently in Ruby than in
Rust for negative values. For example in Ruby:

```
ruby -e "puts 11 % -3" # => -1
```

vs Rust:

```
println!("{}", 11 % -3); // => 2
```

Rust has the `#rem_euclid()` method; however, it also differs from Ruby.

My solution was to call into the cruby #rb_fix_mod_fix method to get the
correct behaviour.

Note that any `% 0` operations are not folded as they need to raise a
ZeroDivisionError.

One concern was an integer overflow. Fortunately that's not possible.
Given any two Fixnums `a`, and `b`, by definition the result of `a % b`
must be closer to 0 than `b`, making an overflow impossible.

One adjacent edge case is when calculating `RUBY_FIXNUM_MIN % -1`. The
result is 0 which is obviously fine; however, the related result of
`RUBY_FIXNUM_MIN / -1` would overflow. The #rb_fix_mod_fix method avoids
this problem by not calculating the `div` portion. See:
https://github.com/ruby/ruby/blob/07f5126c104507ec70c5bfb2eb5204ceb22b7ebd/internal/fixnum.h#L164
When --zjit-trace-exits is enabled, rb_zjit_record_exit_stack was called before compile_exit restored the VM state (cfp->pc, cfp->sp, stack, locals). This ccall clobbers caller-saved registers that may hold stack/local operands, causing garbage values to be written to the VM stack when compile_exit runs afterward. This led to crashes like `klass: T_FALSE` in the interpreter when those corrupted values were used as receivers.

Fix by splitting compile_exit into compile_exit_save_state and compile_exit_return, and emitting the full exit inline when recording: save VM state first, then call rb_zjit_record_exit_stack (so rb_profile_frames sees valid cfp->pc), then return.

Before this I was able to cause a crash with
```
ruby --zjit-trace-exits -rffi -e1
```
@pull pull bot locked and limited conversation to collaborators Feb 19, 2026
@pull pull bot added the ⤵️ pull label Feb 19, 2026
@pull pull bot merged commit e318316 into turkdevops:master Feb 19, 2026
1 check failed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments