From 956f8d490cde0bb6941f21d263287bd3a141348c Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Mon, 8 Dec 2025 16:36:45 +0000 Subject: [PATCH 1/4] ZJIT: Avoid redundant SP save in codegen (#15448) --- zjit/src/codegen.rs | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 8c5fdc816d6450..73c092f72091c5 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -790,7 +790,7 @@ fn gen_ccall_with_frame( // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP // to account for the receiver and arguments (and block arguments if any) - gen_prepare_call_with_gc(asm, state, false); + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, caller_stack_size); gen_spill_stack(jit, asm, state); gen_spill_locals(jit, asm, state); @@ -875,7 +875,7 @@ fn gen_ccall_variadic( // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP // to account for the receiver and arguments (like gen_ccall_with_frame does) - gen_prepare_call_with_gc(asm, state, false); + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, caller_stack_size); gen_spill_stack(jit, asm, state); gen_spill_locals(jit, asm, state); @@ -1304,8 +1304,8 @@ fn gen_send_without_block_direct( gen_stack_overflow_check(jit, asm, state, stack_growth); // Save cfp->pc and cfp->sp for the caller frame - gen_prepare_call_with_gc(asm, state, false); - // Special SP math. Can't use gen_prepare_non_leaf_call + // Can't use gen_prepare_non_leaf_call because we need special SP math. + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, state.stack().len() - args.len() - 1); // -1 for receiver gen_spill_locals(jit, asm, state); @@ -2008,6 +2008,18 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso } } +/// Save only the PC to CFP. Use this when you need to call gen_save_sp() +/// immediately after with a custom stack size (e.g., gen_ccall_with_frame +/// adjusts SP to exclude receiver and arguments). +fn gen_save_pc_for_gc(asm: &mut Assembler, state: &FrameState) { + let opcode: usize = state.get_opcode().try_into().unwrap(); + let next_pc: *const VALUE = unsafe { state.pc.offset(insn_len(opcode) as isize) }; + + gen_incr_counter(asm, Counter::vm_write_pc_count); + asm_comment!(asm, "save PC to CFP"); + asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(next_pc)); +} + /// Save the current PC on the CFP as a preparation for calling a C function /// that may allocate objects and trigger GC. Use gen_prepare_non_leaf_call() /// if it may raise exceptions or call arbitrary methods. @@ -2017,13 +2029,7 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso /// However, to avoid marking uninitialized stack slots, this also updates SP, /// which may have cfp->sp for a past frame or a past non-leaf call. fn gen_prepare_call_with_gc(asm: &mut Assembler, state: &FrameState, leaf: bool) { - let opcode: usize = state.get_opcode().try_into().unwrap(); - let next_pc: *const VALUE = unsafe { state.pc.offset(insn_len(opcode) as isize) }; - - gen_incr_counter(asm, Counter::vm_write_pc_count); - asm_comment!(asm, "save PC to CFP"); - asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::const_ptr(next_pc)); - + gen_save_pc_for_gc(asm, state); gen_save_sp(asm, state.stack_size()); if leaf { asm.expect_leaf_ccall(state.stack_size()); From fd45496f919f202dd30a6a76e7e24fc07abbedc0 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Mon, 8 Dec 2025 11:59:27 -0500 Subject: [PATCH 2/4] Update ZJIT docs (#15449) --- NEWS.md | 2 +- doc/jit/zjit.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 95c02be9513306..033fc2ea76da1b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -384,7 +384,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable. ## JIT * ZJIT - * Introduce an experimental method-based JIT compiler. + * Introduce an [experimental method-based JIT compiler](https://docs.ruby-lang.org/en/master/jit/zjit_md.html). To enable `--zjit` support, build Ruby with Rust 1.85.0 or later. * As of Ruby 4.0.0, ZJIT is faster than the interpreter, but not yet as fast as YJIT. We encourage experimentation with ZJIT, but advise against deploying it in production for now. diff --git a/doc/jit/zjit.md b/doc/jit/zjit.md index 7434d44b9d7d60..1e5a36fd5e6f04 100644 --- a/doc/jit/zjit.md +++ b/doc/jit/zjit.md @@ -1,7 +1,49 @@ # ZJIT: ADVANCED RUBY JIT PROTOTYPE +ZJIT is a method-based just-in-time (JIT) compiler for Ruby. It uses profile +information from the interpreter to guide optimization in the compiler. + +ZJIT is currently supported for macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs. +This project is open source and falls under the same license as CRuby. + +## Current Limitations + +ZJIT may not be suitable for certain applications. It currently only supports macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs. ZJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information. +You can change how much executable memory is allocated using [ZJIT's command-line options](rdoc-ref:@Command-Line+Options). + ## Build Instructions +### For normal use + +To build ZJIT on macOS: + +```bash +./autogen.sh + +./configure \ + --enable-zjit \ + --prefix="$HOME"/.rubies/ruby-zjit \ + --disable-install-doc \ + --with-opt-dir="$(brew --prefix openssl):$(brew --prefix readline):$(brew --prefix libyaml)" + +make -j miniruby +``` + +To build ZJIT on Linux: + +```bash +./autogen.sh + +./configure \ + --enable-zjit \ + --prefix="$HOME"/.rubies/ruby-zjit \ + --disable-install-doc + +make -j miniruby +``` + +### For development + To build ZJIT on macOS: ```bash @@ -47,6 +89,35 @@ make zjit-bindgen ## Documentation +### Command-Line Options + +See `ruby --help` for ZJIT-specific command-line options: + +``` +$ ruby --help +... +ZJIT options: + --zjit-mem-size=num + Max amount of memory that ZJIT can use in MiB (default: 128). + --zjit-call-threshold=num + Number of calls to trigger JIT (default: 30). + --zjit-num-profiles=num + Number of profiled calls before JIT (default: 5). + --zjit-stats[=quiet] + Enable collecting ZJIT statistics (=quiet to suppress output). + --zjit-disable Disable ZJIT for lazily enabling it with RubyVM::ZJIT.enable. + --zjit-perf Dump ISEQ symbols into /tmp/perf-{}.map for Linux perf. + --zjit-log-compiled-iseqs=path + Log compiled ISEQs to the file. The file will be truncated. + --zjit-trace-exits[=counter] + Record source on side-exit. `Counter` picks specific counter. + --zjit-trace-exits-sample-rate=num + Frequency at which to record side exits. Must be `usize`. +$ +``` + +### Source level documentation + You can generate and open the source level documentation in your browser using: ```bash From bd752290e743e3465286b3c656c8e31869f1c4fc Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Tue, 9 Dec 2025 00:50:32 +0900 Subject: [PATCH 3/4] [ruby/timeout] Revert "Exclude constantly-failing test on x86_64-darwin" This reverts commit https://github.com/ruby/timeout/commit/45816b1b2602. https://github.com/ruby/timeout/commit/b54f91e9dd --- test/test_timeout.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test_timeout.rb b/test/test_timeout.rb index 3be9013f7abbc4..51666b73d8e52a 100644 --- a/test/test_timeout.rb +++ b/test/test_timeout.rb @@ -302,6 +302,5 @@ def test_ractor assert_equal :ok, r end; - end if defined?(::Ractor) && RUBY_VERSION >= '4.0' && !RUBY_PLATFORM.include?('x86_64-darwin') - # Exclude on x86_64-darwin as it failed 4 times out of 4 tries in the CI of ruby/ruby-dev-builder + end if defined?(::Ractor) && RUBY_VERSION >= '4.0' end From e61b79b384307ae3358c13d015be3550db7c0b53 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 8 Dec 2025 14:02:08 +0900 Subject: [PATCH 4/4] Fix a typo in the deprecation warning message --- include/ruby/internal/fl_type.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ruby/internal/fl_type.h b/include/ruby/internal/fl_type.h index da8670a8086abf..9bbcb9d2b82433 100644 --- a/include/ruby/internal/fl_type.h +++ b/include/ruby/internal/fl_type.h @@ -260,7 +260,7 @@ ruby_fl_type { RUBY_FL_EXIVAR #if defined(RBIMPL_HAVE_ENUM_ATTRIBUTE) - RBIMPL_ATTR_DEPRECATED(("FL_EXIVAR is an outdated implementation detail, it shoudl be used.")) + RBIMPL_ATTR_DEPRECATED(("FL_EXIVAR is an outdated implementation detail, it should not be used.")) #elif defined(_MSC_VER) # pragma deprecated(RUBY_FL_EXIVAR) #endif