From 0ab78337736476d62233daecc777d5fbb74ef981 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 5 Sep 2025 12:35:45 -0700 Subject: [PATCH 01/10] ruby.c: Fallback FEATURE_BIT(jit) to FEATURE_BIT(zjit) if it's the only JIT enabled in the build --- ruby.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ruby.c b/ruby.c index 5a78a7c7649f90..f64412a9cf0fd2 100644 --- a/ruby.c +++ b/ruby.c @@ -119,7 +119,11 @@ enum feature_flag_bits { EACH_FEATURES(DEFINE_FEATURE, COMMA), DEFINE_FEATURE(frozen_string_literal_set), feature_debug_flag_first, +#if !USE_YJIT && USE_ZJIT + DEFINE_FEATURE(jit) = feature_zjit, +#else DEFINE_FEATURE(jit) = feature_yjit, +#endif feature_jit_mask = FEATURE_BIT(yjit) | FEATURE_BIT(zjit), feature_debug_flag_begin = feature_debug_flag_first - 1, From 76deabd3dbbb8ddcaf122ac80ee3d4ca2bfee993 Mon Sep 17 00:00:00 2001 From: Jun Aruga Date: Fri, 5 Sep 2025 16:24:12 +0100 Subject: [PATCH 02/10] CI: ubuntu-ibm.yml: Fix a typo to print HOME env. --- .github/workflows/ubuntu-ibm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu-ibm.yml b/.github/workflows/ubuntu-ibm.yml index c58934007020a6..4f034e2546ecce 100644 --- a/.github/workflows/ubuntu-ibm.yml +++ b/.github/workflows/ubuntu-ibm.yml @@ -86,7 +86,7 @@ jobs: # https://github.com/IBM/actionspz/issues/30 - name: Set HOME env run: | - echo "HOME: #{HOME}" + echo "HOME: ${HOME}" echo "HOME=$(ls -d ~)" >> $GITHUB_ENV working-directory: if: ${{ endsWith(matrix.os, 'ppc64le') || endsWith(matrix.os, 's390x') }} From c06e7046a6c5b6fbfb1eb2ad09c2157c88dec170 Mon Sep 17 00:00:00 2001 From: Jun Aruga Date: Fri, 5 Sep 2025 16:36:15 +0100 Subject: [PATCH 03/10] CI: ubuntu-ibm.yml: Set the runner user's primary group from the "id -g". Set the runner user's primary group to avoid a mismatch between the group IDs of "id -g" and C function getpwuid(uid_t uid) pw_gid in a test as a better workaround. --- .github/workflows/ubuntu-ibm.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ubuntu-ibm.yml b/.github/workflows/ubuntu-ibm.yml index 4f034e2546ecce..566bca8d3b6b1a 100644 --- a/.github/workflows/ubuntu-ibm.yml +++ b/.github/workflows/ubuntu-ibm.yml @@ -134,15 +134,12 @@ jobs: continue-on-error: true timeout-minutes: 3 - # A temporary workaround: Skip user ground id test - # There is a mismatch between the group IDs of "id -g" and C function - # getpwuid(uid_t uid) pw_gid. + # A temporary workaround: Set the user's primary group to avoid a mismatch + # between the group IDs of "id -g" and C function getpwuid(uid_t uid) + # pw_gid. # https://github.com/IBM/actionspz/issues/31 - - name: Skip user group id test - run: | - sed -i.orig '/^ it "returns user group id" do/a\ skip' \ - ../src/spec/ruby/library/etc/struct_passwd_spec.rb - diff -u ../src/spec/ruby/library/etc/struct_passwd_spec.rb{.orig,} || : + - name: Set user's group id + run: sudo usermod -g "$(id -g)" runner if: ${{ endsWith(matrix.os, 'ppc64le') || endsWith(matrix.os, 's390x') }} - name: make ${{ matrix.test_task }} From ef3c3e65823fd1b93750ddc87ea846ed2b544df9 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 5 Sep 2025 13:27:35 -0700 Subject: [PATCH 04/10] ZJIT: Stop optimizing toplevel locals (#14458) --- .github/workflows/zjit-macos.yml | 4 ++ .github/workflows/zjit-ubuntu.yml | 4 ++ test/ruby/test_zjit.rb | 72 +++++++++++++++++++++++-------- zjit/src/cruby.rs | 11 +++++ zjit/src/hir.rs | 16 ++++--- 5 files changed, 84 insertions(+), 23 deletions(-) diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index b2b6f5e285561b..5a1f3d65488efe 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -47,6 +47,10 @@ jobs: configure: '--enable-zjit=dev' zjit_opts: '--zjit-call-threshold=1' + - test_task: 'test-spec' + configure: '--enable-zjit=dev' + specopts: '-T --zjit-call-threshold=1' + env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} RUN_OPTS: ${{ matrix.zjit_opts }} diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index 46f4f08b43bf0f..c8797b6c14b04e 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -68,6 +68,10 @@ jobs: configure: '--enable-zjit=dev' zjit_opts: '--zjit-call-threshold=1' + - test_task: 'test-spec' + configure: '--enable-zjit=dev' + specopts: '-T --zjit-call-threshold=1' + env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} RUN_OPTS: ${{ matrix.zjit_opts }} diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 2f197084664f03..b751d482e2b41d 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -196,6 +196,32 @@ def test }, insns: [:setglobal] end + def test_toplevel_binding + # Not using assert_compiles, which doesn't use the toplevel frame for `test_script`. + out, err, status = eval_with_jit(%q{ + a = 1 + b = 2 + TOPLEVEL_BINDING.local_variable_set(:b, 3) + c = 4 + print [a, b, c] + }) + assert_success(out, err, status) + assert_equal "[1, 3, 4]", out + end + + def test_toplevel_local_after_eval + # Not using assert_compiles, which doesn't use the toplevel frame for `test_script`. + out, err, status = eval_with_jit(%q{ + a = 1 + b = 2 + eval('b = 3') + c = 4 + print [a, b, c] + }) + assert_success(out, err, status) + assert_equal "[1, 3, 4]", out + end + def test_getlocal_after_eval assert_compiles '2', %q{ def test @@ -2428,12 +2454,8 @@ def assert_runs(expected, test_script, insns: [], assert_compiles: false, **opts IO.open(#{pipe_fd}).write(Marshal.dump(result)) RUBY - status, out, err, result = eval_with_jit(script, pipe_fd:, **opts) - - message = "exited with status #{status.to_i}" - message << "\nstdout:\n```\n#{out}```\n" unless out.empty? - message << "\nstderr:\n```\n#{err}```\n" unless err.empty? - assert status.success?, message + out, err, status, result = eval_with_jit(script, pipe_fd:, **opts) + assert_success(out, err, status) result = Marshal.load(result) assert_equal(expected, result.fetch(:ret_val).inspect) @@ -2462,7 +2484,7 @@ def eval_with_jit( debug: true, allowed_iseqs: nil, timeout: 1000, - pipe_fd: + pipe_fd: nil ) args = ["--disable-gems"] if zjit @@ -2478,18 +2500,25 @@ def eval_with_jit( end end args << "-e" << script_shell_encode(script) - pipe_r, pipe_w = IO.pipe - # Separate thread so we don't deadlock when - # the child ruby blocks writing the output to pipe_fd - pipe_out = nil - pipe_reader = Thread.new do - pipe_out = pipe_r.read - pipe_r.close + ios = {} + if pipe_fd + pipe_r, pipe_w = IO.pipe + # Separate thread so we don't deadlock when + # the child ruby blocks writing the output to pipe_fd + pipe_out = nil + pipe_reader = Thread.new do + pipe_out = pipe_r.read + pipe_r.close + end + ios[pipe_fd] = pipe_w end - out, err, status = EnvUtil.invoke_ruby(args, '', true, true, rubybin: RbConfig.ruby, timeout: timeout, ios: { pipe_fd => pipe_w }) - pipe_w.close - pipe_reader.join(timeout) - [status, out, err, pipe_out] + result = EnvUtil.invoke_ruby(args, '', true, true, rubybin: RbConfig.ruby, timeout: timeout, ios:) + if pipe_fd + pipe_w.close + pipe_reader.join(timeout) + result << pipe_out + end + result ensure pipe_reader&.kill pipe_reader&.join(timeout) @@ -2498,6 +2527,13 @@ def eval_with_jit( jitlist&.unlink end + def assert_success(out, err, status) + message = "exited with status #{status.to_i}" + message << "\nstdout:\n```\n#{out}```\n" unless out.empty? + message << "\nstderr:\n```\n#{err}```\n" unless err.empty? + assert status.success?, message + end + def script_shell_encode(s) # We can't pass utf-8-encoded characters directly in a shell arg. But we can use Ruby \u constants. s.chars.map { |c| c.ascii_only? ? c : "\\u%x" % c.codepoints[0] }.join diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 0ef402f073754c..374faa6d972543 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -294,6 +294,17 @@ pub fn iseq_opcode_at_idx(iseq: IseqPtr, insn_idx: u32) -> u32 { unsafe { rb_iseq_opcode_at_pc(iseq, pc) as u32 } } +/// Return true if the ISEQ always uses a frame with escaped EP. +pub fn iseq_escapes_ep(iseq: IseqPtr) -> bool { + match unsafe { get_iseq_body_type(iseq) } { + // The EP of the
frame points to TOPLEVEL_BINDING + ISEQ_TYPE_MAIN | + // eval frames point to the EP of another frame or scope + ISEQ_TYPE_EVAL => true, + _ => false, + } +} + /// Iterate over all existing ISEQs pub fn for_each_iseq(mut callback: F) { unsafe extern "C" fn callback_wrapper(iseq: IseqPtr, data: *mut c_void) { diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 98381fa0907e14..af44c86cbe93b3 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3019,6 +3019,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let payload = get_or_create_iseq_payload(iseq); let mut profiles = ProfileOracle::new(payload); let mut fun = Function::new(iseq); + // Compute a map of PC->Block by finding jump targets let BytecodeInfo { jump_targets, has_blockiseq } = compute_bytecode_info(iseq); let mut insn_idx_to_block = HashMap::new(); @@ -3029,6 +3030,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { insn_idx_to_block.insert(insn_idx, fun.new_block(insn_idx)); } + // Check if the EP is escaped for the ISEQ from the beginning. We give up + // optimizing locals in that case because they're shared with other frames. + let ep_escaped = iseq_escapes_ep(iseq); + // Iteratively fill out basic blocks using a queue // TODO(max): Basic block arguments at edges let mut queue = std::collections::VecDeque::new(); @@ -3066,7 +3071,6 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let mut visited = HashSet::new(); let iseq_size = unsafe { get_iseq_encoded_size(iseq) }; - let iseq_type = unsafe { get_iseq_body_type(iseq) }; while let Some((incoming_state, block, mut insn_idx, mut local_inval)) = queue.pop_front() { if visited.contains(&block) { continue; } visited.insert(block); @@ -3361,8 +3365,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } YARVINSN_getlocal_WC_0 => { let ep_offset = get_arg(pc, 0).as_u32(); - if iseq_type == ISEQ_TYPE_EVAL || has_blockiseq { - // On eval, the locals are always on the heap, so read the local using EP. + if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here + // Read the local using EP let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0 }); state.setlocal(ep_offset, val); // remember the result to spill on side-exits state.stack_push(val); @@ -3374,6 +3378,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoEPEscape(iseq), state: exit_id }); local_inval = false; } + // Read the local from FrameState let val = state.getlocal(ep_offset); state.stack_push(val); } @@ -3381,8 +3386,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { YARVINSN_setlocal_WC_0 => { let ep_offset = get_arg(pc, 0).as_u32(); let val = state.stack_pop()?; - if iseq_type == ISEQ_TYPE_EVAL || has_blockiseq { - // On eval, the locals are always on the heap, so write the local using EP. + if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here + // Write the local using EP fun.push_insn(block, Insn::SetLocal { val, ep_offset, level: 0 }); } else if local_inval { // If there has been any non-leaf call since JIT entry or the last patch point, @@ -3391,6 +3396,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoEPEscape(iseq), state: exit_id }); local_inval = false; } + // Write the local into FrameState state.setlocal(ep_offset, val); } YARVINSN_getlocal_WC_1 => { From cd07c3cbae7e287350d713ead237aeef27cc2b9e Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 5 Sep 2025 13:57:14 -0700 Subject: [PATCH 05/10] test_gem_command_manager.rb: Skip an unstable test for ruby/ruby This fails way too often these days, probably since https://github.com/rubygems/rubygems/pull/8948. To make sure we can merge ruby/ruby PRs, we need this to not fail randomly. --- test/rubygems/test_gem_command_manager.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb index 4c31eb3bc136fa..4056ecd831c3af 100644 --- a/test/rubygems/test_gem_command_manager.rb +++ b/test/rubygems/test_gem_command_manager.rb @@ -72,6 +72,7 @@ def test_find_command_unknown end def test_find_command_unknown_suggestions + omit "randomly fails on ruby/ruby CI" if ENV["GITHUB_ACTION_REPOSITORY"] == "ruby/ruby" e = assert_raise Gem::UnknownCommandError do @command_manager.find_command "pish" end From 11275d13c1aa4709338702ca1438d1cf21228185 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 5 Sep 2025 14:14:22 -0700 Subject: [PATCH 06/10] ZJIT: Merge `make check` jobs (#14459) --- .github/workflows/zjit-macos.yml | 26 +++++++++++--------------- .github/workflows/zjit-ubuntu.yml | 30 ++++++++++++------------------ 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/.github/workflows/zjit-macos.yml b/.github/workflows/zjit-macos.yml index 5a1f3d65488efe..bb244dd1b28bcd 100644 --- a/.github/workflows/zjit-macos.yml +++ b/.github/workflows/zjit-macos.yml @@ -32,28 +32,22 @@ jobs: fail-fast: false matrix: include: - - test_task: 'zjit-check' + - test_task: 'check' + configure: '--enable-zjit=dev' + run_opts: '--zjit-call-threshold=1' + specopts: '-T --zjit-call-threshold=1' + + - test_task: 'zjit-check' # zjit-test + quick feedback of test_zjit.rb configure: '--enable-yjit=dev --enable-zjit' rust_version: "1.85.0" - - test_task: 'ruby' # build test for combo build + - test_task: 'ruby' + hint: 'combo build test' configure: '--enable-yjit --enable-zjit' - - test_task: 'zjit-test-all' - configure: '--enable-zjit=dev' - testopts: '--seed=11831' - - - test_task: 'test' - configure: '--enable-zjit=dev' - zjit_opts: '--zjit-call-threshold=1' - - - test_task: 'test-spec' - configure: '--enable-zjit=dev' - specopts: '-T --zjit-call-threshold=1' - env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} - RUN_OPTS: ${{ matrix.zjit_opts }} + RUN_OPTS: ${{ matrix.run_opts }} SPECOPTS: ${{ matrix.specopts }} TESTOPTS: ${{ matrix.testopts }} @@ -117,6 +111,7 @@ jobs: RUN_OPTS="$RUN_OPTS" SPECOPTS="$SPECOPTS" TESTOPTS="$TESTOPTS" + TEST_EXCLUDES="$TEST_EXCLUDES" timeout-minutes: 60 env: RUBY_TESTOPTS: '-q --tty=no' @@ -124,6 +119,7 @@ jobs: SYNTAX_SUGGEST_TIMEOUT: '5' PRECHECK_BUNDLED_GEMS: 'no' TESTS: ${{ matrix.tests }} + TEST_EXCLUDES: '--excludes-dir=../src/test/.excludes-zjit --name=!/memory_leak/' continue-on-error: ${{ matrix.continue-on-test_task || false }} result: diff --git a/.github/workflows/zjit-ubuntu.yml b/.github/workflows/zjit-ubuntu.yml index c8797b6c14b04e..3861e2481fc8d9 100644 --- a/.github/workflows/zjit-ubuntu.yml +++ b/.github/workflows/zjit-ubuntu.yml @@ -51,30 +51,23 @@ jobs: fail-fast: false matrix: include: - - test_task: 'zjit-bindgen' - hint: 'To fix: use patch in logs' - configure: '--enable-zjit=dev --with-gcc=clang-14' - libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1' + - test_task: 'check' + configure: '--enable-zjit=dev' + run_opts: '--zjit-call-threshold=1' + specopts: '-T --zjit-call-threshold=1' - - test_task: 'zjit-check' + - test_task: 'zjit-check' # zjit-test + quick feedback of test_zjit.rb configure: '--enable-yjit --enable-zjit=dev' rust_version: '1.85.0' - - test_task: 'zjit-test-all' - configure: '--enable-zjit=dev' - testopts: '--seed=18140' - - - test_task: 'test' - configure: '--enable-zjit=dev' - zjit_opts: '--zjit-call-threshold=1' - - - test_task: 'test-spec' - configure: '--enable-zjit=dev' - specopts: '-T --zjit-call-threshold=1' + - test_task: 'zjit-bindgen' + hint: 'To fix: use patch in logs' + configure: '--enable-zjit=dev --with-gcc=clang-14' + libclang_path: '/usr/lib/llvm-14/lib/libclang.so.1' env: GITPULLOPTIONS: --no-tags origin ${{ github.ref }} - RUN_OPTS: ${{ matrix.zjit_opts }} + RUN_OPTS: ${{ matrix.run_opts }} YJIT_BENCH_OPTS: ${{ matrix.yjit_bench_opts }} SPECOPTS: ${{ matrix.specopts }} TESTOPTS: ${{ matrix.testopts }} @@ -156,12 +149,13 @@ jobs: run: >- make -s ${{ matrix.test_task }} ${TESTS:+TESTS="$TESTS"} RUN_OPTS="$RUN_OPTS" MSPECOPT=--debug SPECOPTS="$SPECOPTS" - TESTOPTS="$TESTOPTS" + TESTOPTS="$TESTOPTS" TEST_EXCLUDES="$TEST_EXCLUDES" ZJIT_BINDGEN_DIFF_OPTS="$ZJIT_BINDGEN_DIFF_OPTS" timeout-minutes: 90 env: RUBY_TESTOPTS: '-q --tty=no' TEST_BUNDLED_GEMS_ALLOW_FAILURES: '' + TEST_EXCLUDES: '--excludes-dir=../src/test/.excludes-zjit --name=!/memory_leak/' PRECHECK_BUNDLED_GEMS: 'no' SYNTAX_SUGGEST_TIMEOUT: '5' ZJIT_BINDGEN_DIFF_OPTS: '--exit-code' From 8aa885c460aeb70878538eebdd155c6318989fd2 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 5 Sep 2025 14:17:22 -0700 Subject: [PATCH 07/10] test_gem_command_manager.rb: Fix the environment variable The previous one failed: https://github.com/ruby/ruby/actions/runs/17504307509/job/49724437021 $GITHUB_ACTION_REPOSITORY points to the owner of the action, so it might not be ruby/ruby. --- test/rubygems/test_gem_command_manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/rubygems/test_gem_command_manager.rb b/test/rubygems/test_gem_command_manager.rb index 4056ecd831c3af..eeda4d94cff1c5 100644 --- a/test/rubygems/test_gem_command_manager.rb +++ b/test/rubygems/test_gem_command_manager.rb @@ -72,7 +72,7 @@ def test_find_command_unknown end def test_find_command_unknown_suggestions - omit "randomly fails on ruby/ruby CI" if ENV["GITHUB_ACTION_REPOSITORY"] == "ruby/ruby" + omit "randomly fails on ruby/ruby CI" if ENV["GITHUB_REPOSITORY"] == "ruby/ruby" e = assert_raise Gem::UnknownCommandError do @command_manager.find_command "pish" end From 72ddfc131f87118a772d30c8692857c793999b4b Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 5 Sep 2025 15:08:21 -0700 Subject: [PATCH 08/10] ZJIT: Fix tests for ZJIT (#14460) --- test/-ext-/bug_reporter/test_bug_reporter.rb | 4 +++- test/.excludes-zjit/TestBugReporter.rb | 1 - test/.excludes-zjit/TestGc.rb | 1 - test/.excludes-zjit/TestProc.rb | 1 - test/.excludes-zjit/TestRubyOptions.rb | 10 ---------- test/lib/jit_support.rb | 6 +++++- test/ruby/test_rubyoptions.rb | 15 ++++++++------- 7 files changed, 16 insertions(+), 22 deletions(-) delete mode 100644 test/.excludes-zjit/TestBugReporter.rb delete mode 100644 test/.excludes-zjit/TestGc.rb delete mode 100644 test/.excludes-zjit/TestProc.rb delete mode 100644 test/.excludes-zjit/TestRubyOptions.rb diff --git a/test/-ext-/bug_reporter/test_bug_reporter.rb b/test/-ext-/bug_reporter/test_bug_reporter.rb index d402ab13825315..1350675a48d3ed 100644 --- a/test/-ext-/bug_reporter/test_bug_reporter.rb +++ b/test/-ext-/bug_reporter/test_bug_reporter.rb @@ -20,7 +20,9 @@ def test_bug_reporter_add no_core = "Process.setrlimit(Process::RLIMIT_CORE, 0); " if defined?(Process.setrlimit) && defined?(Process::RLIMIT_CORE) args = ["-r-test-/bug_reporter", "-C", tmpdir] - args.push("--yjit") if JITSupport.yjit_enabled? # We want the printed description to match this process's RUBY_DESCRIPTION + # We want the printed description to match this process's RUBY_DESCRIPTION + args.push("--yjit") if JITSupport.yjit_enabled? + args.push("--zjit") if JITSupport.zjit_enabled? args.unshift({"RUBY_ON_BUG" => nil}) stdin = "#{no_core}register_sample_bug_reporter(12345); Process.kill :SEGV, $$" assert_in_out_err(args, stdin, [], expected_stderr, encoding: "ASCII-8BIT") diff --git a/test/.excludes-zjit/TestBugReporter.rb b/test/.excludes-zjit/TestBugReporter.rb deleted file mode 100644 index 57d3166d87aa54..00000000000000 --- a/test/.excludes-zjit/TestBugReporter.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_bug_reporter_add, 'Test fails with ZJIT') diff --git a/test/.excludes-zjit/TestGc.rb b/test/.excludes-zjit/TestGc.rb deleted file mode 100644 index 7591da79bc37b1..00000000000000 --- a/test/.excludes-zjit/TestGc.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_interrupt_in_finalizer, "Interrupt checks are still being developed") diff --git a/test/.excludes-zjit/TestProc.rb b/test/.excludes-zjit/TestProc.rb deleted file mode 100644 index 2961b686b6e3e7..00000000000000 --- a/test/.excludes-zjit/TestProc.rb +++ /dev/null @@ -1 +0,0 @@ -exclude(:test_binding_receiver, 'Test fails with ZJIT') diff --git a/test/.excludes-zjit/TestRubyOptions.rb b/test/.excludes-zjit/TestRubyOptions.rb deleted file mode 100644 index 966d0b75511936..00000000000000 --- a/test/.excludes-zjit/TestRubyOptions.rb +++ /dev/null @@ -1,10 +0,0 @@ -exclude(:test_verbose, 'Test crashes with ZJIT') -exclude(:test_segv_setproctitle, 'Test crashes with ZJIT') -exclude(:test_crash_report, 'Test crashes with ZJIT') -exclude(:test_segv_loaded_features, 'Test crashes with ZJIT') -exclude(:test_crash_report_executable_path, 'Test crashes with ZJIT') -exclude(:test_segv_test, 'Test crashes with ZJIT') -exclude(:test_crash_report_script, 'Test crashes with ZJIT') -exclude(:test_crash_report_script_path, 'Test crashes with ZJIT') - -exclude(:test_version, 'Test fails with ZJIT') diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb index 79fdbcce48f410..386a5a6f1efd53 100644 --- a/test/lib/jit_support.rb +++ b/test/lib/jit_support.rb @@ -10,7 +10,7 @@ def yjit_supported? end def yjit_enabled? - defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled? + defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? end def yjit_force_enabled? @@ -22,4 +22,8 @@ def zjit_supported? # nil in mswin @zjit_supported = ![nil, 'no'].include?(RbConfig::CONFIG['ZJIT_SUPPORT']) end + + def zjit_enabled? + defined?(RubyVM::ZJIT) && RubyVM::ZJIT.enabled? + end end diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb index b6c76ac73a662e..2c9f8534b19aad 100644 --- a/test/ruby/test_rubyoptions.rb +++ b/test/ruby/test_rubyoptions.rb @@ -8,8 +8,6 @@ require_relative '../lib/parser_support' class TestRubyOptions < Test::Unit::TestCase - def self.yjit_enabled? = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? - # Here we're defining our own RUBY_DESCRIPTION without "+PRISM". We do this # here so that the various tests that reference RUBY_DESCRIPTION don't have to # worry about it. The flag itself is tested in its own test. @@ -22,8 +20,10 @@ def self.yjit_enabled? = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? NO_JIT_DESCRIPTION = case - when yjit_enabled? - RUBY_DESCRIPTION.sub(/\+YJIT( (dev|dev_nodebug|stats))? /, '') + when JITSupport.yjit_enabled? + RUBY_DESCRIPTION.sub(/\+YJIT( \w+)? /, '') + when JITSupport.zjit_enabled? + RUBY_DESCRIPTION.sub(/\+ZJIT( \w+)? /, '') else RUBY_DESCRIPTION end @@ -181,7 +181,7 @@ def test_debug def test_verbose assert_in_out_err([{'RUBY_YJIT_ENABLE' => nil}, "-vve", ""]) do |r, e| assert_match(VERSION_PATTERN, r[0]) - if self.class.yjit_enabled? && !JITSupport.yjit_force_enabled? + if (JITSupport.yjit_enabled? && !JITSupport.yjit_force_enabled?) || JITSupport.zjit_enabled? assert_equal(NO_JIT_DESCRIPTION, r[0]) else assert_equal(RUBY_DESCRIPTION, r[0]) @@ -247,7 +247,7 @@ def test_version assert_match(VERSION_PATTERN, r[0]) if ENV['RUBY_YJIT_ENABLE'] == '1' assert_equal(NO_JIT_DESCRIPTION, r[0]) - elsif self.class.yjit_enabled? # checking -DYJIT_FORCE_ENABLE + elsif JITSupport.yjit_enabled? || JITSupport.zjit_enabled? # checking -DYJIT_FORCE_ENABLE assert_equal(EnvUtil.invoke_ruby(['-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0]) else assert_equal(RUBY_DESCRIPTION, r[0]) @@ -852,7 +852,8 @@ def assert_segv(args, message=nil, list: SEGVTest::ExpectedStderrList, **opt, &b # We want YJIT to be enabled in the subprocess if it's enabled for us # so that the Ruby description matches. env = Hash === args.first ? args.shift : {} - args.unshift("--yjit") if self.class.yjit_enabled? + args.unshift("--yjit") if JITSupport.yjit_enabled? + args.unshift("--zjit") if JITSupport.zjit_enabled? env.update({'RUBY_ON_BUG' => nil}) # ASAN registers a segv handler which prints out "AddressSanitizer: DEADLYSIGNAL" when # catching sigsegv; we don't expect that output, so suppress it. From 6fe762610952dc4ef3a36409274fc9845f9fd294 Mon Sep 17 00:00:00 2001 From: Burdette Lamar Date: Fri, 5 Sep 2025 17:17:07 -0500 Subject: [PATCH 09/10] [DOC] Fix link in Regexp --- doc/_regexp.rdoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/_regexp.rdoc b/doc/_regexp.rdoc index a544aab089197e..b862c9a49e0594 100644 --- a/doc/_regexp.rdoc +++ b/doc/_regexp.rdoc @@ -1272,13 +1272,13 @@ because the optimization uses memoization (which may invoke large memory consump == References -Read (online PDF books): +Read: -- {Mastering Regular Expressions}[https://ia902508.us.archive.org/10/items/allitebooks-02/Mastering%20Regular%20Expressions%2C%203rd%20Edition.pdf] +- Mastering Regular Expressions by Jeffrey E.F. Friedl. -- {Regular Expressions Cookbook}[https://doc.lagout.org/programmation/Regular%20Expressions/Regular%20Expressions%20Cookbook_%20Detailed%20Solutions%20in%20Eight%20Programming%20Languages%20%282nd%20ed.%29%20%5BGoyvaerts%20%26%20Levithan%202012-09-06%5D.pdf] +- Regular Expressions Cookbook by Jan Goyvaerts & Steven Levithan. -Explore, test (interactive online editor): +Explore, test: -- {Rubular}[https://rubular.com/]. +- {Rubular}[https://rubular.com/]: interactive online editor. From c0d168068f92e53c7ca67702d6d46fee35e2cc6d Mon Sep 17 00:00:00 2001 From: BurdetteLamar Date: Mon, 25 Aug 2025 15:38:54 -0500 Subject: [PATCH 10/10] [DOC] Fix link --- math.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math.c b/math.c index 9e96f3666a3bdd..852620da20be6c 100644 --- a/math.c +++ b/math.c @@ -1054,7 +1054,7 @@ math_gamma(VALUE unused_obj, VALUE x) * * [Math.log(Math.gamma(x).abs), Math.gamma(x) < 0 ? -1 : 1] * - * See {logarithmic gamma function}[https://en.wikipedia.org/wiki/Gamma_function#The_log-gamma_function]. + * See {log gamma function}[https://en.wikipedia.org/wiki/Gamma_function#Log-gamma_function]. * * - Domain: (-INFINITY, INFINITY]. * - Range of first element: (-INFINITY, INFINITY].