From 6f890976e0272d6d09b96232da06660ba674593e Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Mon, 19 Jan 2026 15:17:11 -0500 Subject: [PATCH 1/3] JITs: Fix comment about ARM64 stack growth direction [ci skip] --- yjit/src/backend/arm64/mod.rs | 7 +++---- zjit/src/backend/arm64/mod.rs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 4486b46e363efa..0521e09d0bf5de 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -878,14 +878,13 @@ impl Assembler } } - /// Emit a push instruction for the given operand by adding to the stack - /// pointer and then storing the given value. + /// Push a value to the stack by subtracting from the stack pointer then storing, + /// leaving an 8-byte gap for alignment. fn emit_push(cb: &mut CodeBlock, opnd: A64Opnd) { str_pre(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, -C_SP_STEP)); } - /// Emit a pop instruction into the given operand by loading the value - /// and then subtracting from the stack pointer. + /// Pop a value from the stack by loading `[sp]` then adding to the stack pointer. fn emit_pop(cb: &mut CodeBlock, opnd: A64Opnd) { ldr_post(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP)); } diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs index 55a65e3ea6161e..0ef22be6319e3e 100644 --- a/zjit/src/backend/arm64/mod.rs +++ b/zjit/src/backend/arm64/mod.rs @@ -1086,14 +1086,13 @@ impl Assembler { gc_offsets.push(ptr_offset); } - /// Emit a push instruction for the given operand by adding to the stack - /// pointer and then storing the given value. + /// Push a value to the stack by subtracting from the stack pointer then storing, + /// leaving an 8-byte gap for alignment. fn emit_push(cb: &mut CodeBlock, opnd: A64Opnd) { str_pre(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, -C_SP_STEP)); } - /// Emit a pop instruction into the given operand by loading the value - /// and then subtracting from the stack pointer. + /// Pop a value from the stack by loading `[sp]` then adding to the stack pointer. fn emit_pop(cb: &mut CodeBlock, opnd: A64Opnd) { ldr_post(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP)); } From c939330b7a0e747f8a0a079df6d272b01d2ecbd0 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Sun, 18 Jan 2026 17:26:24 -0500 Subject: [PATCH 2/3] [DOC] Escape Coverage --- ext/coverage/coverage.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/coverage/coverage.c b/ext/coverage/coverage.c index 1f82193567eb1c..1fda8191ccde75 100644 --- a/ext/coverage/coverage.c +++ b/ext/coverage/coverage.c @@ -467,7 +467,7 @@ rb_coverage_running(VALUE klass) return current_state == RUNNING ? Qtrue : Qfalse; } -/* Coverage provides coverage measurement feature for Ruby. +/* \Coverage provides coverage measurement feature for Ruby. * This feature is experimental, so these APIs may be changed in future. * * Caveat: Currently, only process-global coverage measurement is supported. @@ -503,7 +503,7 @@ rb_coverage_running(VALUE klass) * require "foo.rb" * p Coverage.result #=> {"foo.rb"=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]} * - * == Lines Coverage + * == Lines \Coverage * * If a coverage mode is not explicitly specified when starting coverage, lines * coverage is what will run. It reports the number of line executions for each @@ -523,7 +523,7 @@ rb_coverage_running(VALUE klass) * A +nil+ value means coverage is disabled for this line (lines like +else+ * and +end+). * - * == Oneshot Lines Coverage + * == Oneshot Lines \Coverage * * Oneshot lines coverage tracks and reports on the executed lines while * coverage is running. It will not report how many times a line was executed, @@ -537,7 +537,7 @@ rb_coverage_running(VALUE klass) * The value of the oneshot lines coverage result is an array containing the * line numbers that were executed. * - * == Branches Coverage + * == Branches \Coverage * * Branches coverage reports how many times each branch within each conditional * was executed. @@ -562,7 +562,7 @@ rb_coverage_running(VALUE klass) * 5. The ending line number it appears on in the file. * 6. The ending column number it appears on in the file. * - * == Methods Coverage + * == Methods \Coverage * * Methods coverage reports how many times each method was executed. * @@ -600,7 +600,7 @@ rb_coverage_running(VALUE klass) * 5. The ending line number the method appears on in the file. * 6. The ending column number the method appears on in the file. * - * == All Coverage Modes + * == All \Coverage Modes * * You can also run all modes of coverage simultaneously with this shortcut. * Note that running all coverage modes does not run both lines and oneshot From 16adb9303f912b994e6d03ef4211849e9a9473fb Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Mon, 19 Jan 2026 22:31:13 +0100 Subject: [PATCH 3/3] [ruby/prism] Optimize ripper translator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creating state classes is pretty expensive. Since they are not modifiable, we can reuse them instead. Benchmark script: ```rb require "ripper" require "prism" require "benchmark/ips" codes = Dir["**/*.rb"].map { File.read(it) } Benchmark.ips do |x| x.config(time: 10) x.report("prism") { codes.each { Prism::Translation::Ripper.lex(it) } } x.report("ripper") { codes.each { Ripper.lex(it) } } x.compare! end ``` Before: ``` ruby 4.0.0 (2025-12-25 revision https://github.com/ruby/prism/commit/553f1675f3) +PRISM [x86_64-linux] Warming up -------------------------------------- prism 1.000 i/100ms ripper 1.000 i/100ms Calculating ------------------------------------- prism 0.293 (± 0.0%) i/s (3.42 s/i) - 3.000 in 10.248348s ripper 0.633 (± 0.0%) i/s (1.58 s/i) - 7.000 in 11.055687s Comparison: ripper: 0.6 i/s prism: 0.3 i/s - 2.16x slower ``` After ``` ruby 4.0.0 (2025-12-25 revision https://github.com/ruby/prism/commit/553f1675f3) +PRISM [x86_64-linux] Warming up -------------------------------------- prism 1.000 i/100ms ripper 1.000 i/100ms Calculating ------------------------------------- prism 0.486 (± 0.0%) i/s (2.06 s/i) - 5.000 in 10.280413s ripper 0.635 (± 0.0%) i/s (1.58 s/i) - 7.000 in 11.027169s Comparison: ripper: 0.6 i/s prism: 0.5 i/s - 1.31x slower ``` https://github.com/ruby/prism/commit/bdde16554c --- lib/prism/lex_compat.rb | 4 ++-- lib/prism/translation/ripper/lexer.rb | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index b7c54178ac3193..f7b9a0effc969d 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -667,7 +667,7 @@ def result event = RIPPER.fetch(token.type) value = token.value - lex_state = Translation::Ripper::Lexer::State.new(lex_state) + lex_state = Translation::Ripper::Lexer::State.cached(lex_state) token = case event @@ -734,7 +734,7 @@ def result counter += { on_embexpr_beg: -1, on_embexpr_end: 1 }[current_event] || 0 end - Translation::Ripper::Lexer::State.new(result_value[current_index][1]) + Translation::Ripper::Lexer::State.cached(result_value[current_index][1]) else previous_state end diff --git a/lib/prism/translation/ripper/lexer.rb b/lib/prism/translation/ripper/lexer.rb index bd40fb4c5a2de0..bed863af081b79 100644 --- a/lib/prism/translation/ripper/lexer.rb +++ b/lib/prism/translation/ripper/lexer.rb @@ -38,6 +38,13 @@ def |(i) self.class.new(to_int | i) end def allbits?(i) to_int.allbits?(i) end def anybits?(i) to_int.anybits?(i) end def nobits?(i) to_int.nobits?(i) end + + # Instances are frozen and there are only a handful of them so we cache them here. + STATES = Hash.new { |h,k| h[k] = State.new(k) } + + def self.cached(i) + STATES[i] + end end class Elem @@ -47,7 +54,7 @@ def initialize(pos, event, tok, state, message = nil) @pos = pos @event = event @tok = tok - @state = State.new(state) + @state = State.cached(state) @message = message end