1300 Commits
Author SHA1 Message Date
Max Bernstein f4c7ad48ce ZJIT: Inline ISEQs marked :always_inline
Add a new `Primitive.attr :always_inline`. Force inline ISEQs with this
attribute. This is helpful for e.g. `BasicObject#!=`, which is otherwise
megamorphic, and very small.

Other JITs also have a "always inline small methods" policy but I didn't
want to wade into that yet.
2026-10-21 20:48:24 -04:00
Max Bernstein 5a11098754 ZJIT: Don't profile splatkw instruction
We don't use the profiles anymore.
2026-10-21 14:40:42 -04:00
Hiroshi SHIBATAandClaude Opus 5.5 d37040f374 YJIT: Read String and Array lengths as a plain rb_len_t operand
The bindings now declare rb_len_t as i64 on every platform, so the 32-bit branch of rb_len_mem can no longer be reached, and load_mem had no other caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:07:39 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 4e746b14fd Make rb_len_t 64 bits on mswin
long is 32 bits on mswin while pointers are 64 bits, so String, Array and MatchData could not reach 2GiB there. config.h now sets SIZEOF_RB_LEN_T to 8 on mswin64, and value.h then defines rb_len_t as int64_t. mingw still uses long.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:07:39 +09:00
Sam Harrison 3e77f97d38 YJIT: Unlink failed batch blocks from the graph
When gen_block_series() fails on a block after the first one, for
example because code memory is full, it frees the blocks it already
compiled with free_block(blockref, false). That skips
remove_from_graph(). A batch block can have a branch to a block
outside the batch, such as a JITReturn branch to an existing
continuation. That block keeps the freed branch in its incoming list.
Invalidating it later, as rb_yjit_invalidate_ep_is_bp() does, reads
the freed branch and crashes.

Free the batch blocks with graph_intact=true. They were compiled in
this call and every block they link to still exists, so it is safe to
unlink them.
2026-10-16 02:40:22 +09:00
Jean Boussier 8264d60f97 Fix RubyVM::YJIT.max_compile_time_ns= when YJIT is disabled
[Bug #22421]
2026-10-11 09:19:12 +02:00
Hiroshi SHIBATAandClaude Opus 5.5 05917fd1e6 Return rb_len_t from rb_RSTRING_LEN
unsigned long can be narrower than rb_len_t, which would truncate the length that YJIT and ZJIT read through this function.

https://bugs.ruby-lang.org/issues/22400

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:21:06 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 9f3a5c227c YJIT: Use rb_len_t instead of long for lengths
The length fields of String and Array are rb_len_t, which is not necessarily long. The Fixnum range checks stay based on long. The bindings still declare rb_len_t as c_long, so the build now fails if rb_len_t gets wider without them, instead of YJIT reading only part of each length.

https://bugs.ruby-lang.org/issues/22400

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:21:06 +09:00
Hiroshi SHIBATAandClaude Opus 5 d9ed0a3f42 Use rb_len_t for lengths in the JIT helpers
`make yjit-bindgen` and `make zjit-bindgen` pick the typedef up from the C API, so the Rust bindings are regenerated here too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-09 14:00:15 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 2f428c37c3 YJIT: Take the base and offset in c_long_mem
Nothing in it was specific to lengths, and building the C long memory operand inside saves each caller from spelling out its width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 45c20a09af YJIT: Add Assembler#load_mem like ZJIT's
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 03b90e670f YJIT: Check Fixnum overflow with a single branch on LLP64
Fixnum operands fit in 32 bits where long is 32-bit, so the 64-bit overflow check never fires there and the range check alone decides. Code generated where long is 64-bit is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 661785e01c YJIT: Make the LLP64 helpers Assembler methods
They emit instructions, which a call like asm.cmp_fixnum_fits_long(val) makes clearer than a free function taking asm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 6b770b5d43 Regenerate the JIT bindings for the helpers declared in internal/jit.h
bindgen now finds rb_jit_get_page_size and rb_jit_reserve_addr_space in internal/jit.h, which moves them in both generated files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 d5d28336a9 YJIT: Do not suggest ./configure --enable-yjit=dev on mswin
The disasm options and RubyVM::YJIT.disasm need a dev build, which win32/configure.bat cannot make. The warning for builds without YJIT also mentions --enable-yjit, since installing rustc alone does not enable it on mswin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 5b0dc214cb YJIT: Ignore the perf map of --yjit-perf on Windows
/tmp/perf-{pid}.map resolves to a usually missing directory on the current drive, so the first compilation panicked, and Linux perf could not read the map there anyway. --yjit-perf=fp is unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 3c75df83f3 YJIT: Detect a console with rb_w32_isatty on mswin
UCRT's isatty returns 0x40 for a character device, so the comparison with 1 never held and --yjit-dump-disasm was never colored. Checking for non-zero instead would also color output sent to NUL, which is a character device too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 3cf882a6d7 YJIT: Register unwind info for the JIT region on Windows
RtlUnwindEx(), which longjmp() and C++ exceptions use, needs unwind info for every frame, so raising through JIT frames skipped __finally blocks and C++ destructors. Exception dispatch also could not walk through JIT frames to reach the unhandled exception filter that the compaction read barrier relies on. Describe the entry frame, which now always has a frame pointer on Windows, for the whole region.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 0701dfd2e9 Manage JIT code memory with the Windows virtual memory API
Reserve the region with VirtualAlloc and commit pages when they become writable, in place of mmap and mprotect. Freed pages stay committed with their contents discarded, since rb_jit_mark_executable() covers the whole mapped region and VirtualProtect() fails on decommitted or reserved-only pages, and YJIT frees only the mapped part of the last code page for the same reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 a180ecc0d1 YJIT: Use the Win64 calling convention on Windows
Generated code called C functions and Rust callbacks with the SysV convention. On Windows, pass the first four arguments in RCX, RDX, R8 and R9 and the rest on the stack above the 32-byte shadow space, and keep RSI and RDI out of the temp registers since they are callee-saved there, which limits --yjit-temp-regs to 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 94270ae8e7 YJIT: Convert stats to Integer without assuming Fixnum
Stats and counters can exceed the Fixnum range, which is 31 bits on Windows, and failed the assertion in VALUE::fixnum_from_usize.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 29f3aa2f92 YJIT: Guard Fixnum results to fit in long on LLP64
A Fixnum holds a C long, which is 32-bit on Windows, so an arithmetic result or a length that did not overflow 64 bits could still be out of the Fixnum range and make an unnormalized Fixnum. Side-exit there as each instruction does on a 64-bit overflow, with opt_mult chaining to send, and treat String#length as a possible Bignum, whose allocation needs the PC and SP saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 de92824dd2 YJIT: Fix C integer widths on LLP64
The bindings are generated on LP64, where ID and st_data_t are unsigned long and long is 64-bit. On Windows long is 32-bit and both are unsigned long long, so pin them to u64, cast long arguments with the platform width, and widen the RString and RArray length fields before mixing them with 64-bit operands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 f3b6992215 YJIT: Keep dump-disasm and log output files as File
std::os::unix::io::RawFd is not available on Windows. Leaking the opened File works everywhere and needs no fd round trip on each write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 51b4af5d7a YJIT: Type the output of load_sext as 64 bits
LoadSExt exists to widen a narrower operand, but load_sext typed its output with the operand width, so the result could not be mixed with 64-bit operands and the arm64 emitter passed a 32-bit destination to sxtw. The register allocator still sizes the out register by the operand, so both emitters widen it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:11:37 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 bd4810d204 YJIT: Treat memory bases and narrower views as sources when reordering moves
find_safe_move compared register sources with a destination including their width and ignored memory bases, so a C argument could be written into a register before another argument read it through a 32-bit view or loaded through it as its base. Compare reg_no for both, as ZJIT's reorder_reg_moves does for memory bases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:21:52 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 be5482e32f YJIT: Reject an out-of-range --yjit-temp-regs as an invalid option
The assert panicked across the extern "C" boundary and aborted the process. Report the value like the other invalid ones, together with the limit, which the generic invalid option error does not show.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 09:13:13 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 8a0e9ef4d3 YJIT: Pick a free temp register only among the enabled ones
The fallback in find_unused_reg searched all MAX_MAPPED_REGS slots, so fewer registers, as with --yjit-temp-regs=3, could get an index beyond them and panic with an out of bounds access in TEMP_REGS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 09:13:13 +09:00
Max Bernstein 7eab8b5d48 ZJIT: Add null support for opt_case_dispatch (#19040)
We are not going to optimized switch-based dispatch with the hash table, at
least not yet, but we should let the interpreter profile the execution paths
before ZJIT runs them.

This should inject a lot more type information for the compiler. For example,
on rubyboy, we go from ~1080ms/iter to ~820ms/iter.

Later, we can make the dispatch faster too.
2026-09-26 15:38:37 +00:00
Jean Boussier c29739cb3e Implement RubyVM::YJIT.max_compile_time_ns
[Feature #22236]

Allows to give a time budget to YJIT. When YJIT goes over the budget
it pauses compilation until the budget is raised.

It is best effort, YJIT may go over, or have no choice but to compile
in some cases, but the goal is to be able to limit the CPU time overhead
of YJIT during warmup, so that it's spread over a longer period of
time and doesn't impact service latency as much.
2026-09-24 23:54:37 +02:00
Takashi Kokubun 9f062cf473 ZJIT: Remove the now-unused getblockparamproxy profiling 2026-09-18 09:07:51 -07:00
Hiroshi SHIBATAandClaude Opus 5 111cc79210 Look up the nearest user box for :caller_user_box
The box of the immediately previous frame can be the root box, e.g. when
a proc made in the root box calls Marshal.load, and the classes of the
caller's data are invisible there. Walk the caller frames up to the
nearest user box instead, and rename the attribute after what it finds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 12:39:17 +09:00
Hiroshi SHIBATAandClaude Fable 5 cdd238ab32 Add Primitive.attr! :caller_box
Builtin methods written in Ruby are defined in the master box, so the
box resolution stops at their frames and cannot see classes defined in
the caller's box. The new attribute makes such methods operate on the
caller's box, as CFUNC frames already do.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-14 12:39:17 +09:00
Yusuke Endoh dac86d0d35 Remove the dependency from internal/cont.h to prism. (#13984) 2026-09-12 08:11:50 +00:00
Guillaume Malette fc04a83092 YJIT: Inline Struct member writes in gen_struct_aset (GH-17477) 2026-09-11 12:41:32 -04:00
Rafael Mendonça França 5831948a66 Fix the intern VM instruction to be leaf=false (also in YJIT, ZJIT)
rb_str_intern can raise EncodingError and call its initializer.
This call can push a Ruby frame.

Fix in the VM, YJIT, and ZJIT.
2026-09-10 15:43:31 -04:00
Koichi SasadaandClaude Fable 5 2e5bf429e4 Ractor: restrict class/module modification to the creator Ractor
Only the Ractor which created a class/module may modify it: define, remove or
undefine methods, alias, include/prepend, refine, define or remove constants,
register an autoload, and set the class's own instance variables.  Reading is
unchanged.  This replaces the assorted "main Ractor only" rules with one
creator-based rule, and in exchange a Ractor gets full use of the classes it
creates itself -- unshareable constant and instance variable values included,
which even the main Ractor could not do for its own classes before.

Three paths reach a class's tables without going through rb_class_modify_check,
so they get the check of their own: Module#refine (which writes its refinement
tables into the receiver as well as into the refined class), Class#initialize
(which writes the superclass of a not-yet-initialized class, and had no frozen
check either), and Module#ruby2_keywords.

The constant inline cache has to move with the rule.  Its hit condition was
"shareable value, or we are the main Ractor", which was sound only because a
non-main Ractor could not have an unshareable constant in the first place.  Now
it can, so an owner priming the cache would let any other Ractor read that value
straight out of it, never reaching the owner check in vm_get_ev_const.  The
entry records the Ractor which filled it instead -- the one that passed the
check -- which also gives a non-main owner a fast path it never had.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:02:03 +09:00
Takashi Kokubun ab75c6508f Bindgen RSTRUCT_EMBED_LEN_MASK as usize 2026-09-09 09:57:01 -07:00
Takashi Kokubun 0c6ae574be Generate RSTRUCT_EMBED_LEN_MASK with bindgen for YJIT and ZJIT 2026-09-09 09:57:01 -07:00
Takashi Kokubun d279f27d63 Generate ISEQ_TRANSLATED with bindgen for YJIT
ISEQ_TRANSLATED is defined through a chain of macros that ends in an
enum value (IMEMO_FL_USER3 -> FL_USER8 -> RUBY_FL_USER8), which bindgen
cannot evaluate, so YJIT and ZJIT had been defining the constant
manually in cruby.rs. ZJIT doesn't even use the constant, so drop it
there, and re-expose it for YJIT through enum yjit_bindgen_constants
in yjit.c so that the Rust side can no longer go out of sync with
iseq.h.

https://github.com/ruby/ruby/pull/18597#discussion_r3914719597
2026-09-09 09:57:01 -07:00
Peter Zhu 1d8d84b3d6 Increase IMEMO_MASK by one bit
We are out of imemo types because IMEMO_MASK uses 4 bits and we have 16
imemo types. This makes it not possible to experiment with new imemo types,
so we can increase it by one bit to have 16 more imemo types.
2026-09-03 08:38:46 +09:00
nozomemein f791417e71 ZJIT: Specialize caller splats with monomorphic lengths
Use the profiled splat array length to expand caller splats into fixed
positional arguments for SendDirect. Guard the runtime length and fall
back to the original Send when it changes or ruby2_keywords semantics
apply.

Validate direct sends using the expanded argument count, then apply the
existing keyword setup and rest parameter packing. Move the
ruby2_keywords splat predicate into shared JIT code so both YJIT and
ZJIT can use it.
2026-09-02 15:10:29 -07:00
Peter Zhu 90e8610e0d JIT: Reduce jit_entry_calls/jit_exception_calls to 32 bits
We don't need 64 bits for jit_entry_calls/jit_exception_calls so dropping
them down to 32 bits will allow us to save 8 bytes per iseq.
2026-09-02 13:00:08 +09:00
Peter Zhu d039466bbe Introduce rb_zjit_compiling_p/rb_yjit_compiling_p 2026-09-01 12:51:25 +09:00
Peter Zhu 91582950b7 Unify jit_payload in iseq_constant_body
We cannot have YJIT and ZJIT enabled at the same time, so we can use a
single jit_payload pointer in iseq_constant_body to reduce the size by
8 bytes.
2026-08-28 08:10:40 +09:00
Peter Zhu c56fa9401b Don't record EP escape in JIT when not enabled
We shouldn't record JIT information before the JIT is actually enabled.

This change doesn't seem to affect ZJIT or YJIT performance beyond the
margin of error:

    ZJIT:

      --------------  ------------  ------------  --------------  -------------
      bench            master (ms)   branch (ms)  branch 1st itr  master/branch
      activerecord     50.1 ± 7.4%   49.0 ± 5.7%           0.948          1.022
      chunky-png      164.2 ± 2.1%  166.8 ± 2.8%           0.985          0.985
      erubi-rails     247.0 ± 2.8%  264.6 ± 2.2%           0.945          0.934
      hexapdf         571.3 ± 1.0%  565.4 ± 1.2%           0.959          1.010
      liquid-c        17.6 ± 13.1%  19.2 ± 12.9%           0.976          0.920
      liquid-compile  15.7 ± 11.0%   15.6 ± 7.5%           0.978          1.003
      liquid-il        79.8 ± 2.0%   78.2 ± 2.2%           0.993          1.021
      liquid-render    32.3 ± 6.8%   32.3 ± 6.3%           1.001          0.999
      lobsters        243.8 ± 4.9%  242.8 ± 4.7%           0.976          1.004
      mail             38.8 ± 3.1%   39.5 ± 5.1%           0.993          0.982
      psych-load      612.4 ± 1.5%  610.0 ± 1.9%           1.001          1.004
      railsbench      343.8 ± 2.9%  350.8 ± 2.2%           0.991          0.980
      rubocop         50.0 ± 10.6%  51.2 ± 12.0%           1.019          0.978
      ruby-lsp         42.9 ± 3.8%   43.2 ± 3.7%           0.974          0.993
      sequel           18.5 ± 6.6%   18.7 ± 6.6%           1.021          0.991
      shipit          344.3 ± 1.3%  339.8 ± 3.4%           0.981          1.013
      --------------  ------------  ------------  --------------  -------------

    YJIT:

      --------------  ------------  ------------  --------------  -------------
      bench            master (ms)   branch (ms)  branch 1st itr  master/branch
      activerecord     42.6 ± 7.7%   40.6 ± 5.5%           1.099          1.048
      chunky-png      164.7 ± 1.3%  164.8 ± 1.1%           1.060          0.999
      erubi-rails     256.7 ± 3.3%  241.3 ± 2.3%           1.035          1.064
      hexapdf         445.6 ± 1.0%  444.6 ± 1.0%           1.012          1.002
      liquid-c        16.4 ± 11.2%  16.7 ± 13.3%           1.037          0.986
      liquid-compile  14.5 ± 11.5%   14.3 ± 7.8%           0.990          1.011
      liquid-il        74.9 ± 2.6%   73.4 ± 3.6%           0.993          1.021
      liquid-render    23.1 ± 8.9%   23.3 ± 8.7%           1.001          0.992
      lobsters        239.9 ± 9.9%  238.7 ± 9.3%           0.989          1.005
      mail             29.2 ± 4.6%   30.0 ± 5.8%           0.977          0.973
      psych-load      515.5 ± 1.2%  511.5 ± 0.7%           0.973          1.008
      railsbench      317.4 ± 2.2%  317.9 ± 2.5%           1.008          0.998
      rubocop         42.1 ± 18.5%  42.0 ± 14.4%           1.052          1.004
      ruby-lsp         41.5 ± 3.8%   41.6 ± 3.8%           1.013          0.998
      sequel           18.5 ± 6.6%   18.2 ± 5.4%           1.150          1.018
      shipit          282.8 ± 3.8%  285.5 ± 5.2%           1.000          0.991
      --------------  ------------  ------------  --------------  -------------
2026-08-27 11:25:39 +09:00
Nobuyoshi Nakada 9ee713a92c Revert "Adjust indent [ci skip]"
This reverts commit c0a526dd97.
2026-08-27 09:27:53 +09:00
Nobuyoshi Nakada c0a526dd97 Adjust indent [ci skip] 2026-08-27 09:26:49 +09:00
Peter Zhu 0b2edb5b9a Implement support for AR table backed identity hashes
This commit adds support for using AR tables for identity hashes (hashes
created by method Hash#compare_by_identity). Before this change, identity
hashes always used ST tables. However, this used a lot more memory because
ST tables are quite large. This commit adds a new flag RHASH_COMPARE_BY_IDENTITY
for AR tables that are compare-by-identity.

For example, for an empty identity hash, the size goes from 256 bytes to
160 bytes:

    require "objspace"

    h = {}.compare_by_identity
    puts ObjectSpace.memsize_of(h)
2026-08-26 21:04:27 +09:00
Ian Ker-Seymer 90529c255e Raise YJIT’s case specialization limit to 256 (#18374)
Raise YJIT case dispatch specialization limit to 256
2026-08-24 11:13:54 -07:00