Skip to content

[pull] master from ruby:master#937

Merged
pull[bot] merged 8 commits intoturkdevops:masterfrom
ruby:master
Apr 16, 2026
Merged

[pull] master from ruby:master#937
pull[bot] merged 8 commits intoturkdevops:masterfrom
ruby:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull bot commented Apr 16, 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 : )

khasinski and others added 8 commits April 15, 2026 12:34
* ZJIT: Fix getlocal with level=0 reading stale EP data

YARVINSN_getlocal always loaded from EP memory, even for level=0
locals. But setlocal_WC_0 only updates the JIT's FrameState without
writing to EP. When the Ruby compiler emits getlocal (non-WC variant)
for a level=0 local after setlocal_WC_0, the EP load returned stale
data.

This caused Array#pack with buffer: keyword to silently lose the
buffer argument when the array contained local variable references,
because the buffer VALUE read from EP was nil/stale instead of the
actual string.

Fix: when level=0 and locals are not invalidated, use the FrameState
value (same optimization as getlocal_WC_0). This matches what
setlocal_WC_0 writes to.

* ZJIT: Add regression test for getlocal level=0 fix

Verify that Array#pack with a buffer: keyword reads the correct
buffer VALUE when the buffer is a local variable set via
setlocal_WC_0 and read via getlocal (non-WC variant).

Before the fix, this returned b.size == 1 (stale EP data) instead
of 2 (buffer updated by pack).
Add FloatAdd, FloatSub, FloatMul, FloatDiv HIR instructions that
lower to gen_prepare_leaf_call_with_gc followed by a direct ccall to
rb_float_plus/minus/mul/div. This skips CCallWithFrame overhead
(frame push/pop, stack and locals spill) while remaining GC-safe
since PC and SP are saved to cfp before the call.

The inline functions guard the receiver as Flonum (cheap tag check:
(val & 3) == 2) and accept either Flonum or Fixnum as the second
operand. HeapFloat operands fall back to CCallWithFrame via the
default Send path. Using Flonum guards instead of Float guards
avoids an expensive class pointer load from memory.

ruby-bench nbody: 46ms -> 33ms (-28%)
Add FloatToInt HIR instruction that truncates a Flonum to Integer
via rb_jit_flo_to_i with GC preparation. The helper uses trunc()
for truncation toward zero, then returns Fixnum (LONG2FIX) or
Bignum (rb_dbl2big) depending on magnitude.

rb_jit_flo_to_i is a new JIT helper in jit.c, following the same
pattern as rb_jit_fix_div_fix for wrapping a static C function.
Address review feedback:
1. Move the helper from jit.c (shared YJIT/ZJIT glue) to zjit.c since
   it is only used by ZJIT.
2. Instead of duplicating the truncation logic, export flo_to_i from
   numeric.c and call it from the ZJIT helper, keeping the
   implementation centralized.

The new rb_zjit_flo_to_i wrapper in zjit.c delegates directly to
flo_to_i, so there is no deviation from the Float#to_i semantics.
The previous commit exposed flo_to_i as a non-static global, which
tripped tool/leaked-globals because it lacks the rb_ prefix.

Keep flo_to_i static and add a new public wrapper rb_flo_to_i in
numeric.c that delegates to it. ZJIT now calls rb_flo_to_i directly
from Rust (no zjit.c wrapper needed).
The zjit-bindgen CI check regenerates cruby_bindings.inc.rs from
headers and diffs against the committed file. Since rb_flo_to_i is
declared next to rb_float_plus/minus/mul/div in internal/numeric.h,
bindgen emits it there. Match that order in both the allowlist and
the generated output.
Co-authored-by: Bob Singh <bobsingh.dev@users.noreply.gitlab.com>
@pull pull bot locked and limited conversation to collaborators Apr 16, 2026
@pull pull bot added the ⤵️ pull label Apr 16, 2026
@pull pull bot merged commit e6508f0 into turkdevops:master Apr 16, 2026
1 check was pending
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.

3 participants