1191 Commits
Author SHA1 Message Date
Takashi Kokubun e4beaa2c9c merge revision(s) 9efe557da5, 53f7189ff9: [Backport #22224]
[PATCH] ZJIT: Drop has_blockiseq guard on local variable access (#16113)

	So `has_blockiseq` was masking two other bugs.

	First, that we had only a static EP escape check for an iseq. We need to check
	both that an iseq is not known to statically escape them *and that it has not
	escaped them before*. Only then can we use SSA locals. This popped up in a
	weird PP unit test that was reducible to the test cases added in the PR. So I
	fixed that.

	Second, that we didn't VM lock when code patching for EP escape. That showed up
	in the Ractor btests in my previous PRs #14600 and #15254 #15316, and I had no
	idea what was going on at the time, so I dropped it. But if we add this lock,
	like all the other invariant patching cases have, the problem goes away. Seems
	reasonable.

	Finally, we can use normal full SSA locals even in the presence of a blockiseq.
	We still reload all of them after each send-with-block, but we can in a later
	PR reduce that to just the locals that are (recursively) visibly written to.

	[PATCH] [Bug #22224] Ractors, YJIT: Skip re-reporting recorded EP escapes to the JITs (#18176)

	* [22224] Ractors, YJIT: Skip re-reporting recorded EP escapes to the JITs

	vm_make_env_each reports every EP escape, and both
	rb_yjit_invalidate_ep_is_bp and rb_zjit_invalidate_no_ep_escape take
	the VM lock with a stop-all-Ractors barrier on every call. For a given
	iseq only the first report does anything: afterwards YJIT's
	no_ep_escape_iseqs entry is permanently an empty set and ZJIT's
	ep_escape_iseqs insert is a no-op, so the barrier is paid for nothing.
	Under multiple Ractors this serializes the whole process on every Proc
	created from an already-escaped iseq: a workload creating one lambda
	per iteration runs 150x slower on 8 Ractors than on 1 with YJIT
	enabled.

	Record the report in a jit_ep_escape_recorded flag on the iseq body,
	set by the active JIT while it holds the VM lock, and skip the calls
	in vm_make_env_each once it is set. The flag sits in existing padding
	and the body is ZALLOC'd, so it starts false and dies with the iseq.
	Setting it in the JIT rather than in the caller keeps mid-process
	RubyVM::YJIT.enable correct: escapes reported while no JIT is
	initialized are recorded by nobody and must not set the flag. The
	unlocked read is benign: a stale false causes one redundant,
	idempotent report.

	On the micro-benchmark from the bug, --yjit at 8 Ractors goes from
	0.04M to 11.07M iterations/s (277x).

	* [22224] Deduplicate the EP escape record onto the iseq body

	The jit_ep_escape_recorded flag introduced in the previous commit is
	now the JITs' only record of an EP escape: YJIT's no_ep_escape_iseqs
	keeps only iseqs with live assumptions (the empty-set escape markers
	are gone), and ZJIT's ep_escape_iseqs set is removed together with its
	update_ep_escape_iseqs compaction pass, since the flag lives in the
	iseq body, which the GC does not move. Both JITs read the flag back at
	compile time through a new rb_jit_iseq_ep_escape_recorded_p helper.

	Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>

Only the two correctness changes of 9efe557da5 are backported: the runtime
EP-escape check in iseq_to_hir() and the VM lock in
rb_zjit_invalidate_no_ep_escape().  Without the former, suppressing repeat
escape reports would leave NoEPEscape patch points registered after an
escape never invalidated, because ZJIT here registers them unconditionally.
Its has_blockiseq removal is an optimization and is left out; keeping
has_blockiseq is strictly more conservative.

ZJIT here keeps the name invariants::iseq_escapes_ep() rather than renaming
it to iseq_seen_ep_escape(), since e0184fff1a is not backported.
2026-09-14 15:48:53 -07:00
Takashi Kokubun 1e7d45210e merge revision(s) 5831948a66: [Backport #22303]
[PATCH] 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-14 12:48:48 -07:00
Takashi Kokubun b021821196 YJIT: Fix version_map use-after-free from mutable aliasing UB
Multiple YJIT functions created overlapping `&'static mut IseqPayload`
references by calling `get_iseq_payload()` multiple times for the same
iseq. LLVM's noalias optimizations could then cache stale version_map
Vec headers, causing the version_map to point to freed backing storage.

This manifested as crashes in various YJIT operations (block lookup,
GC marking, block removal) that dereference the stale pointer.

Fix by moving `delayed_deallocation` and `get_or_create_version_list`
from free functions (which each call `get_iseq_payload()` internally)
to methods on `IseqPayload` that operate through `&mut self`. This
lets callers obtain a single payload reference and use it for all
operations without creating overlapping mutable borrows.

The three fixed call sites:

1. `rb_yjit_tracing_invalidate_all` (invariants.rs): The loop called
   `delayed_deallocation()` which internally called `get_iseq_payload()`,
   creating a second `&mut` overlapping with the outer `payload` reference.
   Fix: call `payload.delayed_deallocation()` method instead.

2. `add_block_version` (core.rs): Called `get_or_create_version_list()`
   then later `get_iseq_payload()` for pages, creating two references.
   Fix: use a single `get_or_create_iseq_payload()` call then call the
   `get_or_create_version_list()` method on it for both version_map and
   pages access.

3. `invalidate_block_version` (core.rs): Called `remove_block_version()`
   (which calls `get_iseq_payload()`) then `delayed_deallocation()`
   (which also calls `get_iseq_payload()`), creating two references.
   Fix: get the payload once, inline the version_map removal, and call
   `payload.delayed_deallocation()` method for the rest.

Also adds regression tests exercising tracing invalidation with
on-stack methods and suspended fibers.

(cherry picked from commit a6fcad276a)
2026-07-08 22:02:07 -07:00
Randy Stauner c52a400eef YJIT: Register builtin CMEs before prelude to avoid prepend crash
Split rb_yjit_init into rb_yjit_init_builtin_cmes (called before
ruby_init_prelude) and rb_yjit_init (called after). The prelude may
load bundler via BUNDLER_SETUP which can call Kernel.prepend, moving
core methods to an origin iclass. Registering method codegen before
the prelude ensures we capture CMEs while core classes are pristine.
2026-06-22 09:57:10 -07:00
Ibrahim Awwal cc05825116 YJIT: Fix local register mapping overflow
Previously, local indices greater than 255 were truncated when
converted to RegOpnd::Local. This could make a high-index local alias
a tracked low-index local in the register mapping and produce incorrect
values after compilation.

Cap overflowing indices to MAX_CTX_LOCALS. RegMapping treats that index
as untrackable because it is just outside the tracked local range.

Fixes [Backport #22074].
2026-05-21 18:10:02 -07:00
John Hawthorn a08f356063 Use atomics for kwargs reference count
Fixes [Bug #22075]
2026-05-21 11:17:29 -07:00
Takashi Kokubun 8539f0b386 merge revision(s) c0d86a0103de7130943d54b4a290b76ec7e0c135,47e061277ac194a36659510bcf4f3190bde629a6: [Backport #21952]
class.c: rb_class_duplicate_classext also dup content of cvc_tbl

	[Bug #21952]

	Shallow copying the table result in the same memory being shared
	between multiple box, causing double free when one of the box
	is garbage collected.

	---

	class.c: Make cvc_tbl a managed object

	[Bug #21952]

	Solves the double-free or use after-free concern with boxes.
	Now entries can safely be used for copy-on-write.

	Also is likely necessary to make it save to read cvar from
	secondary ractors, as allowed since: ab32c0e690
2026-05-11 13:54:55 -07:00
Alan Wu 8466e93b1d YJIT: Fix not reading locals from cfp->ep after YJIT.enable and exceptional entry
[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.
2026-03-16 11:03:25 -07:00
John HawthornandAlan Wu 73e930f9f9 JIT: Move EC offsets to jit_bindgen_constants
Co-authored-by: Alan Wu <alanwu@ruby-lang.org>
2025-12-18 13:43:45 -08:00
b1c3060bed Co-authored-by: Luke Gruber <luke.gru@gmail.com>
Co-authored-by: Alan Wu <alanwu@ruby-lang.org>

YJIT: Support calling bmethods in Ractors

Co-authored-by: Luke Gruber <luke.gru@gmail.com>

Suggestion from Alan
2025-12-18 13:43:45 -08:00
John HawthornandLuke Gruber 345ea0c8e1 YJIT: Support calling bmethods in Ractors
Co-authored-by: Luke Gruber <luke.gru@gmail.com>
2025-12-18 13:43:45 -08:00
Alan Wu 656de67d5c JITs: Pass down GNU make jobserver resources when appropriate
To fix warnings from rustc on e.g. Make 4.3, which is in Ubuntu 24.04:

> warning: failed to connect to jobserver from environment variable
2025-12-17 14:49:32 -05:00
Alan Wu eaa952b536 YJIT: Print Rc strong and weak count on assert failure
For <https://bugs.ruby-lang.org/issues/21716>, the panic is looking like
some sort of third party memory corruption, with YJIT taking the fall.
At the point of this assert, the assembler has dropped, so there's
nothing in YJIT's code other than JITState that could be holding on to
these transient `PendingBranchRef`.

The strong count being more than a handful or the weak count is non-zero
shows that someone in the process (likely some native extension)
corrupted the Rc's counts.
2025-12-16 14:56:40 -05:00
Randy Stauner 9168cad4d6 YJIT: Bail out if proc would be stored above stack top
Fixes [Bug #21266].
2025-12-15 23:05:27 -05:00
Alan Wu e1f5e61d6b YJIT: Fix panic from overly loose filtering in identity method inlining
Credits to @rwstauner for noticing this issue in GH-15533.
2025-12-12 21:23:24 -05:00
Alan Wu 2884f53519 YJIT: Add missing local variable type update for fallback setlocal blocks
Previously, the chain_depth>0 version of setlocal blocks did not
update the type of the local variable in the context. This can leave
the context with stale type information and trigger panics like in
[Bug #21772] or lead to miscompilation.

To trigger the issue, YJIT needs to see the same ISEQ before and after
environment escape and have tracked type info before the escape. To
trigger in ISEQs that do not send with a block, it probably requires
Kernel#binding or the use of include/ruby/debug.h APIs.
2025-12-12 15:29:04 -05:00
Alan Wu 029a48176c JITs: Drop cargo and use just rustc for release combo build
So we don't expose builders to network flakiness which cannot be worked
around using cargo's --offline flag.
2025-12-10 16:35:00 -05:00
Alan Wu b0ea9070d4 YJIT: For rustc build, remove cargo touch(1) workaround 2025-12-10 16:35:00 -05:00
Abrar Habib edca81a1bb ZJIT: Add codegen for FixnumDiv (#15452)
Fixes https://github.com/Shopify/ruby/issues/902

This pull request adds code generation for dividing fixnums.
Testing confirms the normal case, flooring, and side-exiting on division by zero.
2025-12-09 12:41:09 +00:00
Alan Wu addeafdd5b YJIT: Fix duplicate make rule warning in combo build 2025-12-05 15:49:25 -05:00
Alan Wu 8132b3d1d8 YJIT: Fix including stats for ZJIT instructions when ZJIT not in build 2025-12-05 15:49:25 -05:00
Alan Wu f01fd2bde2 JITs: Update bindings to include interpreter zjit_ opcodes
Mostly YJIT. ZJIT already has the right bindings and this just tweaks
the CI configuration.
2025-12-05 15:49:25 -05:00
Max Bernstein b43e66d3b3 YJIT: Pass class and shape ID directly instead of object 2025-12-03 16:59:05 -05:00
Max Bernstein a25196395e Add BOP_GTGT
This will help JITs (and maybe later the interpreter) optimize
Integer#>>.
2025-12-01 15:19:26 -08:00
Max Bernstein 8aed311038 ZJIT: Specialize String#<< with Fixnum
Append a codepoint.
2025-12-01 15:19:26 -08:00
Randy Stauner 5f55c9c8fb YJIT: Abort expandarray optimization if method_missing is defined
Fixes: [Bug #21707]
[AW: rewrote comments]
Co-authored-by: Alan Wu <alanwu@ruby-lang.org>
2025-11-26 15:56:27 -05:00
Alan Wu 7a09df45f2 Name the iseq->body->param struct and update bindings for JITs
This will make reading the parameters nicer for the JITs. Should be
no-op for the C side.
2025-11-20 19:52:28 -05:00
Jacob f3f3e76882 Extract KW_SPECIFIED_BITS_MAX for JITs (GH-15039)
Rename to `VM_KW_SPECIFIED_BITS_MAX` now that it's in `vm_core.h`.
2025-11-18 22:32:11 +00:00
Luke Gruber ff2d2fc1bd YJIT: omit single ractor mode assumption for proc#call (#15092)
The comptime receiver, which is a proc, is either shareable or from this
ractor so we don't need to assume single-ractor mode. We should never get
the "defined with an un-shareable Proc in a different ractor" error.
2025-11-18 14:24:49 -05:00
John HawthornandLuke Gruber e4295bc3f7 YJIT: Fix stack handling in rb_str_dup
Previously because we did a stack_push before ccall, in some cases we
could end up pushing an uninitialized value to the VM stack when
spilling regs as part of the ccall.

Co-authored-by: Luke Gruber <luke.gru@gmail.com>
2025-11-14 09:29:09 -08:00
Satoshi Tagomori aaf1f53df0 Follow renaming from Namespace to Ruby::Box 2025-11-07 13:14:54 +09:00
Takashi Kokubun 599de290a0 YJIT, ZJIT: Fix unnecessary use of macros
https://github.com/ruby/ruby/actions/runs/18887695798/job/53907237061?pr=14975
2025-10-28 13:14:48 -07:00
Max Bernstein fa5481bc06 ZJIT: Fetch Primitive.attr!(leaf) for InvokeBuiltin
Fix https://github.com/Shopify/ruby/issues/670
2025-10-22 17:10:14 -07:00
Alan Wu fee863b4df YJIT: Buffer writes to the perf map 2025-10-22 17:24:21 -04:00
Max Bernstein 9ad902e55c ZJIT: Inline String#==, String#=== 2025-10-22 12:39:57 -07:00
Alan Wu bb7f3d17ed YJIT: ZJIT: Extract common bindings to jit.c and remove unnamed enums.
The type name bindgen picks for anonymous enums creates desync issues on
the bindgen CI checks.
2025-10-21 16:48:45 -04:00
Max Bernstein 17368234bf ZJIT: Implement codegen for FixnumMod (#14857)
This is mostly to see what happens to the loops-times benchmark.
2025-10-20 21:30:48 +00:00
Max Bernstein fba349e658 ZJIT: Implement expandarray (#14847)
Only support the simple case: no splat or rest.

lobsters before:

<details>

```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (60.5% of total 11,039,954):
                               Kernel#is_a?: 1,030,769 ( 9.3%)
                                  String#<<:   851,954 ( 7.7%)
                                   Hash#[]=:   742,941 ( 6.7%)
                              Regexp#match?:   399,894 ( 3.6%)
                              String#empty?:   353,775 ( 3.2%)
                                  Hash#key?:   349,147 ( 3.2%)
                         String#start_with?:   334,961 ( 3.0%)
                         Kernel#respond_to?:   316,528 ( 2.9%)
                 ObjectSpace::WeakKeyMap#[]:   238,978 ( 2.2%)
                              TrueClass#===:   235,771 ( 2.1%)
                             FalseClass#===:   231,144 ( 2.1%)
                             Array#include?:   211,385 ( 1.9%)
                                 Hash#fetch:   204,702 ( 1.9%)
                        Kernel#block_given?:   181,797 ( 1.6%)
                                 Kernel#dup:   179,341 ( 1.6%)
                             BasicObject#!=:   175,997 ( 1.6%)
                                  Class#new:   168,079 ( 1.5%)
                            Kernel#kind_of?:   165,600 ( 1.5%)
                                  String#==:   157,735 ( 1.4%)
                       Module#clock_gettime:   144,992 ( 1.3%)
Top-20 not annotated C methods (61.4% of total 11,202,087):
                               Kernel#is_a?: 1,212,660 (10.8%)
                                  String#<<:   851,954 ( 7.6%)
                                   Hash#[]=:   743,120 ( 6.6%)
                              Regexp#match?:   399,894 ( 3.6%)
                              String#empty?:   361,013 ( 3.2%)
                                  Hash#key?:   349,147 ( 3.1%)
                         String#start_with?:   334,961 ( 3.0%)
                         Kernel#respond_to?:   316,528 ( 2.8%)
                 ObjectSpace::WeakKeyMap#[]:   238,978 ( 2.1%)
                              TrueClass#===:   235,771 ( 2.1%)
                             FalseClass#===:   231,144 ( 2.1%)
                             Array#include?:   211,385 ( 1.9%)
                                 Hash#fetch:   204,702 ( 1.8%)
                        Kernel#block_given?:   191,666 ( 1.7%)
                                 Kernel#dup:   179,348 ( 1.6%)
                             BasicObject#!=:   176,181 ( 1.6%)
                                  Class#new:   168,079 ( 1.5%)
                            Kernel#kind_of?:   165,634 ( 1.5%)
                                  String#==:   163,667 ( 1.5%)
                       Module#clock_gettime:   144,992 ( 1.3%)
Top-2 not optimized method types for send (100.0% of total 72,318):
  cfunc: 48,055 (66.4%)
   iseq: 24,263 (33.6%)
Top-6 not optimized method types for send_without_block (100.0% of total 4,523,682):
       iseq: 2,271,936 (50.2%)
    bmethod:   985,636 (21.8%)
  optimized:   949,703 (21.0%)
      alias:   310,747 ( 6.9%)
       null:     5,106 ( 0.1%)
      cfunc:       554 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 4,293,171):
             invokesuper: 2,373,404 (55.3%)
             invokeblock:   811,926 (18.9%)
             sendforward:   505,452 (11.8%)
                  opt_eq:   451,754 (10.5%)
                opt_plus:    74,404 ( 1.7%)
               opt_minus:    36,228 ( 0.8%)
  opt_send_without_block:    21,792 ( 0.5%)
                 opt_neq:     7,231 ( 0.2%)
                opt_mult:     6,752 ( 0.2%)
                  opt_or:     3,753 ( 0.1%)
                  opt_lt:       348 ( 0.0%)
                  opt_ge:        91 ( 0.0%)
                  opt_gt:        36 ( 0.0%)
Top-9 send fallback reasons (100.0% of total 25,530,724):
                send_without_block_polymorphic: 9,722,491 (38.1%)
                              send_no_profiles: 5,894,788 (23.1%)
  send_without_block_not_optimized_method_type: 4,523,682 (17.7%)
                     not_optimized_instruction: 4,293,171 (16.8%)
                send_without_block_no_profiles:   998,746 ( 3.9%)
                send_not_optimized_method_type:    72,318 ( 0.3%)
       send_without_block_cfunc_array_variadic:    15,134 ( 0.1%)
                      obj_to_string_not_string:     9,765 ( 0.0%)
       send_without_block_direct_too_many_args:       629 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 690,950):
         expandarray: 328,490 (47.5%)
        checkkeyword: 190,694 (27.6%)
    getclassvariable:  59,901 ( 8.7%)
  invokesuperforward:  49,503 ( 7.2%)
       getblockparam:  49,119 ( 7.1%)
   opt_duparray_send:  11,978 ( 1.7%)
         getconstant:     952 ( 0.1%)
          checkmatch:     290 ( 0.0%)
                once:      23 ( 0.0%)
Top-3 compile error reasons (100.0% of total 3,718,636):
  register_spill_on_alloc: 3,418,255 (91.9%)
  register_spill_on_ccall:   182,018 ( 4.9%)
        exception_handler:   118,363 ( 3.2%)
Top-14 side exit reasons (100.0% of total 10,860,385):
                        compile_error: 3,718,636 (34.2%)
                   guard_type_failure: 2,638,926 (24.3%)
                  guard_shape_failure: 1,917,209 (17.7%)
                  unhandled_yarv_insn:   690,950 ( 6.4%)
  block_param_proxy_not_iseq_or_ifunc:   535,789 ( 4.9%)
                      unhandled_kwarg:   455,347 ( 4.2%)
                           patchpoint:   370,476 ( 3.4%)
                unknown_newarray_send:   314,786 ( 2.9%)
                      unhandled_splat:   122,071 ( 1.1%)
                   unhandled_hir_insn:    76,397 ( 0.7%)
           block_param_proxy_modified:    19,193 ( 0.2%)
               obj_to_string_fallback:       566 ( 0.0%)
               guard_type_not_failure:        22 ( 0.0%)
                            interrupt:        17 ( 0.0%)
                             send_count: 62,244,604
                     dynamic_send_count: 25,530,724 (41.0%)
                   optimized_send_count: 36,713,880 (59.0%)
              iseq_optimized_send_count: 18,587,512 (29.9%)
      inline_cfunc_optimized_send_count:  7,086,414 (11.4%)
non_variadic_cfunc_optimized_send_count:  8,375,754 (13.5%)
    variadic_cfunc_optimized_send_count:  2,664,200 ( 4.3%)
dynamic_getivar_count:                        7,365,995
dynamic_setivar_count:                        7,245,005
compiled_iseq_count:                              4,796
failed_iseq_count:                                  447
compile_time:                                     814ms
profile_time:                                       9ms
gc_time:                                            9ms
invalidation_time:                                 72ms
vm_write_pc_count:                           64,156,223
vm_write_sp_count:                           62,812,449
vm_write_locals_count:                       62,812,449
vm_write_stack_count:                        62,812,449
vm_write_to_parent_iseq_local_count:            292,458
vm_read_from_parent_iseq_local_count:         6,599,701
code_region_bytes:                           22,953,984
side_exit_count:                             10,860,385
total_insn_count:                           517,606,340
vm_insn_count:                              162,979,530
zjit_insn_count:                            354,626,810
ratio_in_zjit:                                    68.5%
```

</details>

lobsters after:

<details>

```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (59.9% of total 11,291,815):
                               Kernel#is_a?: 1,046,269 ( 9.3%)
                                  String#<<:   851,954 ( 7.5%)
                                   Hash#[]=:   743,274 ( 6.6%)
                              Regexp#match?:   399,894 ( 3.5%)
                              String#empty?:   353,775 ( 3.1%)
                                  Hash#key?:   349,147 ( 3.1%)
                         String#start_with?:   334,961 ( 3.0%)
                         Kernel#respond_to?:   316,502 ( 2.8%)
                 ObjectSpace::WeakKeyMap#[]:   238,978 ( 2.1%)
                              TrueClass#===:   235,771 ( 2.1%)
                             FalseClass#===:   231,144 ( 2.0%)
                                String#sub!:   219,579 ( 1.9%)
                             Array#include?:   211,385 ( 1.9%)
                                 Hash#fetch:   204,702 ( 1.8%)
                        Kernel#block_given?:   181,797 ( 1.6%)
                                 Kernel#dup:   179,341 ( 1.6%)
                             BasicObject#!=:   175,997 ( 1.6%)
                                  Class#new:   168,079 ( 1.5%)
                            Kernel#kind_of?:   165,600 ( 1.5%)
                                  String#==:   157,742 ( 1.4%)
Top-20 not annotated C methods (60.9% of total 11,466,928):
                               Kernel#is_a?: 1,239,923 (10.8%)
                                  String#<<:   851,954 ( 7.4%)
                                   Hash#[]=:   743,453 ( 6.5%)
                              Regexp#match?:   399,894 ( 3.5%)
                              String#empty?:   361,013 ( 3.1%)
                                  Hash#key?:   349,147 ( 3.0%)
                         String#start_with?:   334,961 ( 2.9%)
                         Kernel#respond_to?:   316,502 ( 2.8%)
                 ObjectSpace::WeakKeyMap#[]:   238,978 ( 2.1%)
                              TrueClass#===:   235,771 ( 2.1%)
                             FalseClass#===:   231,144 ( 2.0%)
                                String#sub!:   219,579 ( 1.9%)
                             Array#include?:   211,385 ( 1.8%)
                                 Hash#fetch:   204,702 ( 1.8%)
                        Kernel#block_given?:   191,666 ( 1.7%)
                                 Kernel#dup:   179,348 ( 1.6%)
                             BasicObject#!=:   176,181 ( 1.5%)
                                  Class#new:   168,079 ( 1.5%)
                            Kernel#kind_of?:   165,634 ( 1.4%)
                                  String#==:   163,674 ( 1.4%)
Top-2 not optimized method types for send (100.0% of total 72,318):
  cfunc: 48,055 (66.4%)
   iseq: 24,263 (33.6%)
Top-6 not optimized method types for send_without_block (100.0% of total 4,524,016):
       iseq: 2,272,269 (50.2%)
    bmethod:   985,636 (21.8%)
  optimized:   949,704 (21.0%)
      alias:   310,747 ( 6.9%)
       null:     5,106 ( 0.1%)
      cfunc:       554 ( 0.0%)
Top-13 not optimized instructions (100.0% of total 4,294,241):
             invokesuper: 2,375,446 (55.3%)
             invokeblock:   810,955 (18.9%)
             sendforward:   505,451 (11.8%)
                  opt_eq:   451,754 (10.5%)
                opt_plus:    74,404 ( 1.7%)
               opt_minus:    36,228 ( 0.8%)
  opt_send_without_block:    21,792 ( 0.5%)
                 opt_neq:     7,231 ( 0.2%)
                opt_mult:     6,752 ( 0.2%)
                  opt_or:     3,753 ( 0.1%)
                  opt_lt:       348 ( 0.0%)
                  opt_ge:        91 ( 0.0%)
                  opt_gt:        36 ( 0.0%)
Top-9 send fallback reasons (100.0% of total 25,534,542):
                send_without_block_polymorphic: 9,723,469 (38.1%)
                              send_no_profiles: 5,896,023 (23.1%)
  send_without_block_not_optimized_method_type: 4,524,016 (17.7%)
                     not_optimized_instruction: 4,294,241 (16.8%)
                send_without_block_no_profiles:   998,947 ( 3.9%)
                send_not_optimized_method_type:    72,318 ( 0.3%)
       send_without_block_cfunc_array_variadic:    15,134 ( 0.1%)
                      obj_to_string_not_string:     9,765 ( 0.0%)
       send_without_block_direct_too_many_args:       629 ( 0.0%)
Top-8 unhandled YARV insns (100.0% of total 362,460):
        checkkeyword: 190,694 (52.6%)
    getclassvariable:  59,901 (16.5%)
  invokesuperforward:  49,503 (13.7%)
       getblockparam:  49,119 (13.6%)
   opt_duparray_send:  11,978 ( 3.3%)
         getconstant:     952 ( 0.3%)
          checkmatch:     290 ( 0.1%)
                once:      23 ( 0.0%)
Top-3 compile error reasons (100.0% of total 3,798,744):
  register_spill_on_alloc: 3,495,669 (92.0%)
  register_spill_on_ccall:   184,712 ( 4.9%)
        exception_handler:   118,363 ( 3.1%)
Top-15 side exit reasons (100.0% of total 10,637,319):
                        compile_error: 3,798,744 (35.7%)
                   guard_type_failure: 2,655,504 (25.0%)
                  guard_shape_failure: 1,917,217 (18.0%)
  block_param_proxy_not_iseq_or_ifunc:   535,789 ( 5.0%)
                      unhandled_kwarg:   455,492 ( 4.3%)
                           patchpoint:   370,478 ( 3.5%)
                  unhandled_yarv_insn:   362,460 ( 3.4%)
                unknown_newarray_send:   314,786 ( 3.0%)
                      unhandled_splat:   122,071 ( 1.1%)
                   unhandled_hir_insn:    83,066 ( 0.8%)
           block_param_proxy_modified:    19,193 ( 0.2%)
             guard_int_equals_failure:     1,914 ( 0.0%)
               obj_to_string_fallback:       566 ( 0.0%)
               guard_type_not_failure:        22 ( 0.0%)
                            interrupt:        17 ( 0.0%)
                             send_count: 62,495,067
                     dynamic_send_count: 25,534,542 (40.9%)
                   optimized_send_count: 36,960,525 (59.1%)
              iseq_optimized_send_count: 18,582,072 (29.7%)
      inline_cfunc_optimized_send_count:  7,086,638 (11.3%)
non_variadic_cfunc_optimized_send_count:  8,392,657 (13.4%)
    variadic_cfunc_optimized_send_count:  2,899,158 ( 4.6%)
dynamic_getivar_count:                        7,365,994
dynamic_setivar_count:                        7,248,500
compiled_iseq_count:                              4,780
failed_iseq_count:                                  463
compile_time:                                     816ms
profile_time:                                       9ms
gc_time:                                           11ms
invalidation_time:                                 70ms
vm_write_pc_count:                           64,363,541
vm_write_sp_count:                           63,022,221
vm_write_locals_count:                       63,022,221
vm_write_stack_count:                        63,022,221
vm_write_to_parent_iseq_local_count:            292,458
vm_read_from_parent_iseq_local_count:         6,850,977
code_region_bytes:                           23,019,520
side_exit_count:                             10,637,319
total_insn_count:                           517,303,190
vm_insn_count:                              160,562,103
zjit_insn_count:                            356,741,087
ratio_in_zjit:                                    69.0%
```

</details>

railsbench before:

<details>

```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (66.1% of total 25,524,934):
                                   Hash#[]=: 1,700,237 ( 6.7%)
                             String#getbyte: 1,572,123 ( 6.2%)
                                  String#<<: 1,494,022 ( 5.9%)
                               Kernel#is_a?: 1,429,930 ( 5.6%)
                              String#empty?: 1,370,323 ( 5.4%)
                              Regexp#match?: 1,235,067 ( 4.8%)
                         Kernel#respond_to?: 1,198,251 ( 4.7%)
                                  Hash#key?: 1,087,406 ( 4.3%)
                             String#setbyte:   810,022 ( 3.2%)
                                  Integer#^:   766,624 ( 3.0%)
                        Kernel#block_given?:   603,613 ( 2.4%)
                                  String#==:   590,409 ( 2.3%)
                                  Class#new:   506,216 ( 2.0%)
                                Hash#delete:   455,288 ( 1.8%)
                             BasicObject#!=:   428,771 ( 1.7%)
                                 Hash#fetch:   408,621 ( 1.6%)
                         String#ascii_only?:   373,915 ( 1.5%)
                 ObjectSpace::WeakKeyMap#[]:   287,957 ( 1.1%)
                               NilClass#===:   277,244 ( 1.1%)
                               Kernel#Array:   269,590 ( 1.1%)
Top-20 not annotated C methods (66.8% of total 25,392,654):
                                   Hash#[]=: 1,700,416 ( 6.7%)
                             String#getbyte: 1,572,123 ( 6.2%)
                               Kernel#is_a?: 1,515,672 ( 6.0%)
                                  String#<<: 1,494,022 ( 5.9%)
                              String#empty?: 1,370,478 ( 5.4%)
                              Regexp#match?: 1,235,067 ( 4.9%)
                         Kernel#respond_to?: 1,198,251 ( 4.7%)
                                  Hash#key?: 1,087,406 ( 4.3%)
                             String#setbyte:   810,022 ( 3.2%)
                                  Integer#^:   766,624 ( 3.0%)
                        Kernel#block_given?:   603,613 ( 2.4%)
                                  String#==:   601,115 ( 2.4%)
                                  Class#new:   506,216 ( 2.0%)
                                Hash#delete:   455,288 ( 1.8%)
                             BasicObject#!=:   428,876 ( 1.7%)
                                 Hash#fetch:   408,621 ( 1.6%)
                         String#ascii_only?:   373,915 ( 1.5%)
                 ObjectSpace::WeakKeyMap#[]:   287,957 ( 1.1%)
                               NilClass#===:   277,244 ( 1.1%)
                               Kernel#Array:   269,590 ( 1.1%)
Top-2 not optimized method types for send (100.0% of total 186,159):
   iseq: 112,747 (60.6%)
  cfunc:  73,412 (39.4%)
Top-6 not optimized method types for send_without_block (100.0% of total 8,142,248):
       iseq: 3,464,671 (42.6%)
  optimized: 2,632,884 (32.3%)
    bmethod: 1,290,701 (15.9%)
      alias:   706,020 ( 8.7%)
       null:    47,942 ( 0.6%)
      cfunc:        30 ( 0.0%)
Top-11 not optimized instructions (100.0% of total 8,394,873):
             invokesuper: 5,602,274 (66.7%)
             invokeblock: 1,764,936 (21.0%)
             sendforward:   551,832 ( 6.6%)
                  opt_eq:   441,959 ( 5.3%)
                opt_plus:    31,635 ( 0.4%)
  opt_send_without_block:     1,163 ( 0.0%)
                  opt_lt:       372 ( 0.0%)
                opt_mult:       251 ( 0.0%)
                  opt_ge:       193 ( 0.0%)
                 opt_neq:       149 ( 0.0%)
                  opt_or:       109 ( 0.0%)
Top-8 send fallback reasons (100.0% of total 40,748,753):
                send_without_block_polymorphic: 12,933,923 (31.7%)
                              send_no_profiles:  9,033,636 (22.2%)
                     not_optimized_instruction:  8,394,873 (20.6%)
  send_without_block_not_optimized_method_type:  8,142,248 (20.0%)
                send_without_block_no_profiles:  1,839,228 ( 4.5%)
       send_without_block_cfunc_array_variadic:    215,046 ( 0.5%)
                send_not_optimized_method_type:    186,159 ( 0.5%)
                      obj_to_string_not_string:      3,640 ( 0.0%)
Top-9 unhandled YARV insns (100.0% of total 1,604,456):
    getclassvariable: 458,136 (28.6%)
       getblockparam: 455,921 (28.4%)
        checkkeyword: 265,425 (16.5%)
  invokesuperforward: 239,383 (14.9%)
         expandarray: 137,305 ( 8.6%)
         getconstant:  48,100 ( 3.0%)
          checkmatch:     149 ( 0.0%)
                once:      23 ( 0.0%)
   opt_duparray_send:      14 ( 0.0%)
Top-3 compile error reasons (100.0% of total 5,570,130):
  register_spill_on_alloc: 4,994,130 (89.7%)
        exception_handler:   356,784 ( 6.4%)
  register_spill_on_ccall:   219,216 ( 3.9%)
Top-13 side exit reasons (100.0% of total 12,412,181):
                        compile_error: 5,570,130 (44.9%)
                  unhandled_yarv_insn: 1,604,456 (12.9%)
                  guard_shape_failure: 1,462,872 (11.8%)
                   guard_type_failure:   845,891 ( 6.8%)
  block_param_proxy_not_iseq_or_ifunc:   765,968 ( 6.2%)
                      unhandled_kwarg:   658,341 ( 5.3%)
                           patchpoint:   504,437 ( 4.1%)
                      unhandled_splat:   446,990 ( 3.6%)
                unknown_newarray_send:   332,740 ( 2.7%)
                   unhandled_hir_insn:   160,205 ( 1.3%)
           block_param_proxy_modified:    59,589 ( 0.5%)
               obj_to_string_fallback:       553 ( 0.0%)
                            interrupt:         9 ( 0.0%)
                             send_count: 119,067,587
                     dynamic_send_count:  40,748,753 (34.2%)
                   optimized_send_count:  78,318,834 (65.8%)
              iseq_optimized_send_count:  39,936,542 (33.5%)
      inline_cfunc_optimized_send_count:  12,857,358 (10.8%)
non_variadic_cfunc_optimized_send_count:  19,722,584 (16.6%)
    variadic_cfunc_optimized_send_count:   5,802,350 ( 4.9%)
dynamic_getivar_count:                      10,980,323
dynamic_setivar_count:                      12,962,726
compiled_iseq_count:                             2,531
failed_iseq_count:                                 245
compile_time:                                    414ms
profile_time:                                     21ms
gc_time:                                          33ms
invalidation_time:                                 5ms
vm_write_pc_count:                         129,093,714
vm_write_sp_count:                         126,023,084
vm_write_locals_count:                     126,023,084
vm_write_stack_count:                      126,023,084
vm_write_to_parent_iseq_local_count:           385,461
vm_read_from_parent_iseq_local_count:       11,266,484
code_region_bytes:                          12,156,928
side_exit_count:                            12,412,181
total_insn_count:                          866,780,158
vm_insn_count:                             216,821,134
zjit_insn_count:                           649,959,024
ratio_in_zjit:                                   75.0%
```

</details>

railsbench after:

<details>

```
***ZJIT: Printing ZJIT statistics on exit***
Top-20 not inlined C methods (66.0% of total 25,597,895):
                                   Hash#[]=: 1,724,042 ( 6.7%)
                             String#getbyte: 1,572,123 ( 6.1%)
                                  String#<<: 1,494,022 ( 5.8%)
                               Kernel#is_a?: 1,429,946 ( 5.6%)
                              String#empty?: 1,370,323 ( 5.4%)
                              Regexp#match?: 1,235,067 ( 4.8%)
                         Kernel#respond_to?: 1,198,251 ( 4.7%)
                                  Hash#key?: 1,087,406 ( 4.2%)
                             String#setbyte:   810,022 ( 3.2%)
                                  Integer#^:   766,624 ( 3.0%)
                        Kernel#block_given?:   603,613 ( 2.4%)
                                  String#==:   590,699 ( 2.3%)
                                  Class#new:   506,216 ( 2.0%)
                                Hash#delete:   455,288 ( 1.8%)
                             BasicObject#!=:   428,771 ( 1.7%)
                                 Hash#fetch:   408,621 ( 1.6%)
                         String#ascii_only?:   373,915 ( 1.5%)
                 ObjectSpace::WeakKeyMap#[]:   287,957 ( 1.1%)
                               NilClass#===:   277,244 ( 1.1%)
                               Kernel#Array:   269,590 ( 1.1%)
Top-20 not annotated C methods (66.7% of total 25,465,615):
                                   Hash#[]=: 1,724,221 ( 6.8%)
                             String#getbyte: 1,572,123 ( 6.2%)
                               Kernel#is_a?: 1,515,688 ( 6.0%)
                                  String#<<: 1,494,022 ( 5.9%)
                              String#empty?: 1,370,478 ( 5.4%)
                              Regexp#match?: 1,235,067 ( 4.8%)
                         Kernel#respond_to?: 1,198,251 ( 4.7%)
                                  Hash#key?: 1,087,406 ( 4.3%)
                             String#setbyte:   810,022 ( 3.2%)
                                  Integer#^:   766,624 ( 3.0%)
                        Kernel#block_given?:   603,613 ( 2.4%)
                                  String#==:   601,405 ( 2.4%)
                                  Class#new:   506,216 ( 2.0%)
                                Hash#delete:   455,288 ( 1.8%)
                             BasicObject#!=:   428,876 ( 1.7%)
                                 Hash#fetch:   408,621 ( 1.6%)
                         String#ascii_only?:   373,915 ( 1.5%)
                 ObjectSpace::WeakKeyMap#[]:   287,957 ( 1.1%)
                               NilClass#===:   277,244 ( 1.1%)
                               Kernel#Array:   269,590 ( 1.1%)
Top-2 not optimized method types for send (100.0% of total 186,159):
   iseq: 112,747 (60.6%)
  cfunc:  73,412 (39.4%)
Top-6 not optimized method types for send_without_block (100.0% of total 8,142,248):
       iseq: 3,464,671 (42.6%)
  optimized: 2,632,884 (32.3%)
    bmethod: 1,290,701 (15.9%)
      alias:   706,020 ( 8.7%)
       null:    47,942 ( 0.6%)
      cfunc:        30 ( 0.0%)
Top-11 not optimized instructions (100.0% of total 8,442,456):
             invokesuper: 5,649,857 (66.9%)
             invokeblock: 1,764,936 (20.9%)
             sendforward:   551,832 ( 6.5%)
                  opt_eq:   441,959 ( 5.2%)
                opt_plus:    31,635 ( 0.4%)
  opt_send_without_block:     1,163 ( 0.0%)
                  opt_lt:       372 ( 0.0%)
                opt_mult:       251 ( 0.0%)
                  opt_ge:       193 ( 0.0%)
                 opt_neq:       149 ( 0.0%)
                  opt_or:       109 ( 0.0%)
Top-8 send fallback reasons (100.0% of total 40,796,314):
                send_without_block_polymorphic: 12,933,921 (31.7%)
                              send_no_profiles:  9,033,616 (22.1%)
                     not_optimized_instruction:  8,442,456 (20.7%)
  send_without_block_not_optimized_method_type:  8,142,248 (20.0%)
                send_without_block_no_profiles:  1,839,228 ( 4.5%)
       send_without_block_cfunc_array_variadic:    215,046 ( 0.5%)
                send_not_optimized_method_type:    186,159 ( 0.5%)
                      obj_to_string_not_string:      3,640 ( 0.0%)
Top-8 unhandled YARV insns (100.0% of total 1,467,151):
    getclassvariable: 458,136 (31.2%)
       getblockparam: 455,921 (31.1%)
        checkkeyword: 265,425 (18.1%)
  invokesuperforward: 239,383 (16.3%)
         getconstant:  48,100 ( 3.3%)
          checkmatch:     149 ( 0.0%)
                once:      23 ( 0.0%)
   opt_duparray_send:      14 ( 0.0%)
Top-3 compile error reasons (100.0% of total 5,825,923):
  register_spill_on_alloc: 5,225,940 (89.7%)
        exception_handler:   356,784 ( 6.1%)
  register_spill_on_ccall:   243,199 ( 4.2%)
Top-13 side exit reasons (100.0% of total 12,530,763):
                        compile_error: 5,825,923 (46.5%)
                  unhandled_yarv_insn: 1,467,151 (11.7%)
                  guard_shape_failure: 1,462,876 (11.7%)
                   guard_type_failure:   845,913 ( 6.8%)
  block_param_proxy_not_iseq_or_ifunc:   765,968 ( 6.1%)
                      unhandled_kwarg:   658,341 ( 5.3%)
                           patchpoint:   504,437 ( 4.0%)
                      unhandled_splat:   446,990 ( 3.6%)
                unknown_newarray_send:   332,740 ( 2.7%)
                   unhandled_hir_insn:   160,273 ( 1.3%)
           block_param_proxy_modified:    59,589 ( 0.5%)
               obj_to_string_fallback:       553 ( 0.0%)
                            interrupt:         9 ( 0.0%)
                             send_count: 119,163,569
                     dynamic_send_count:  40,796,314 (34.2%)
                   optimized_send_count:  78,367,255 (65.8%)
              iseq_optimized_send_count:  39,911,967 (33.5%)
      inline_cfunc_optimized_send_count:  12,857,393 (10.8%)
non_variadic_cfunc_optimized_send_count:  19,770,401 (16.6%)
    variadic_cfunc_optimized_send_count:   5,827,494 ( 4.9%)
dynamic_getivar_count:                      10,980,323
dynamic_setivar_count:                      12,986,381
compiled_iseq_count:                             2,523
failed_iseq_count:                                 252
compile_time:                                    420ms
profile_time:                                     21ms
gc_time:                                          30ms
invalidation_time:                                 4ms
vm_write_pc_count:                         128,973,665
vm_write_sp_count:                         125,926,968
vm_write_locals_count:                     125,926,968
vm_write_stack_count:                      125,926,968
vm_write_to_parent_iseq_local_count:           385,752
vm_read_from_parent_iseq_local_count:       11,267,766
code_region_bytes:                          12,189,696
side_exit_count:                            12,530,763
total_insn_count:                          866,667,490
vm_insn_count:                             217,813,201
zjit_insn_count:                           648,854,289
ratio_in_zjit:                                   74.9%
```

</details>
2025-10-20 10:55:52 -04:00
Alan Wu 57bb726884 YJIT: Use mem::take over drain(..).collect() 2025-10-14 18:16:07 -04:00
Vincent Lin 34ee5cbf13 [DOC] Fix minor typos in YJIT comments (#14829)
[DOC] Fix typos in YJIT core
2025-10-14 19:05:18 +08:00
Alan Wu 6be2a5104d YJIT: ZJIT: Fix rustdoc dead links 2025-10-12 00:51:59 -04:00
Alan Wu 32b98d71e6 YJIT: Fix unused warning from cargo test 2025-10-12 00:51:59 -04:00
Alan Wu 20fc91df39 YJIT: Prevent making a branch from a dead block to a live block
I'm seeing some memory corruption in the wild on blocks in
`IseqPayload::dead_blocks`. While I unfortunately can't recreate the
issue, (For all I know, it could be some external code corrupting YJIT's
memory.) establishing a link between dead blocks and live blocks seems
fishy enough that we ought to prevent it. When it did happen, it might've
had bad interacts with Code GC and the optimization to immediately
free empty blocks.
2025-10-02 12:09:40 -04:00
Aiden Fox Ivey 2f1c30cd50 ZJIT: Add --zjit-trace-exits (#14640)
Add side exit tracing functionality for ZJIT
2025-09-30 15:55:33 +00:00
Takashi Kokubun 58030884d8 YJIT: respect the code in master branch
* Originally, k0kubun added a change to respect namespace in gen_block_given
  https://github.com/ruby/ruby/pull/13454/commits/d129669b1729b9570da7958394ea594031e79f59
* Just after the change, XrXr proposes a change on master and he says it can fix the problem
  around block_given?, and it has been merged into the master
  https://github.com/ruby/ruby/pull/14208
* tagomoris respect the commit on master then will see if it works
2025-09-29 01:15:38 +09:00
Satoshi Tagomori 53a1ff71e7 Add and fix dependencies 2025-09-29 01:15:38 +09:00
Stan Lo 93c728ce91 YJIT: Pass iseq pointer to get/set classvariable functions (#14625)
* YJIT: Pass iseq pointer to get/set classvariable functions

Since we already have the iseq pointer, we can actually save one
memory read by passing it directly.

We need to wrap the iseq in a VALUE so it can be marked correctly
by GC.

* YJIT: Fix missing GC marking when passing iseq to rb_vm_setinstancevariable

Without wrapping the iseq in a `Operand::Value`, the iseq would not be
marked correctly by GC and when compacting the heap, the iseq would be
lost and cause a crash.
2025-09-22 14:25:01 -07:00
Takashi Kokubun d3cb347a40 ZJIT: Share more code with YJIT in jit.c (#14520)
* ZJIT: Share more code with YJIT in jit.c

* Fix ZJIT references to JIT
2025-09-12 20:34:55 +00:00
Aiden Fox Ivey cd85fe5a87 ZJIT: Add support for stats_allocator
* Using the shared jit crate, support for a single global_allocator can
function
* Solves --zjit-mem-size
2025-09-11 21:51:47 -07:00
Aiden Fox Ivey f75e1cb362 ZJIT: Move jit.rs to ruby.rs and create a shared crate jit
* ruby.rs should hold the main entrypoint to YJIT and ZJIT
* The crate jit will hold code shared between them
2025-09-11 21:51:47 -07:00