From 107699df9d456955ed2a062f1f26fb654f74c52a Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Mon, 23 Feb 2026 09:05:44 -0800 Subject: [PATCH 01/12] [ruby/erb] Freeze src in initialize (https://github.com/ruby/erb/pull/105) https://github.com/ruby/erb/commit/3d4dc31905 --- lib/erb.rb | 1 + test/erb/test_erb.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/erb.rb b/lib/erb.rb index 445d4795b018bb..1bc6da6f713bea 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -833,6 +833,7 @@ def initialize(str, trim_mode: nil, eoutvar: '_erbout') compiler = make_compiler(trim_mode) set_eoutvar(compiler, eoutvar) @src, @encoding, @frozen_string = *compiler.compile(str) + @src.freeze @filename = nil @lineno = 0 @_init = self.class.singleton_class diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb index 09496d31e25ca2..f6133b1b58a44e 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -598,10 +598,10 @@ def stags def test_frozen_string_literal bug12031 = '[ruby-core:73561] [Bug #12031]' e = @erb.new("<%#encoding: us-ascii%>a") - e.src.sub!(/\A#(?:-\*-)?(.*)(?:-\*-)?/) { + src = e.src.sub(/\A#(?:-\*-)?(.*)(?:-\*-)?/) { '# -*- \1; frozen-string-literal: true -*-' } - assert_equal("a", e.result, bug12031) + assert_equal("a", eval(src), bug12031) %w(false true).each do |flag| erb = @erb.new("<%#frozen-string-literal: #{flag}%><%=''.frozen?%>") From ffbe288f53ca91c61f94788d28df5518ad97434e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Co=C3=AAlho?= <36938811+tomascco@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:12:24 -0300 Subject: [PATCH 02/12] ZJIT: constant fold bitwise XOR (^) operations (#16225) --- zjit/src/hir.rs | 6 ++++ zjit/src/hir/opt_tests.rs | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 1fb526e569f46d..3443d710444dc6 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -4550,6 +4550,12 @@ impl Function { _ => None, }) } + Insn::FixnumXor { left, right, .. } => { + self.fold_fixnum_bop(insn_id, left, right, |l, r| match (l, r) { + (Some(l), Some(r)) => Some(l ^ r), + _ => None, + }) + } Insn::FixnumEq { left, right, .. } => { self.fold_fixnum_pred(insn_id, left, right, |l, r| match (l, r) { (Some(l), Some(r)) => Some(l == r), diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 952f2fe77f4356..3cb3b829e003c1 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -450,6 +450,64 @@ mod hir_opt_tests { "); } + #[test] + fn test_fold_fixnum_xor() { + eval(" + def test + 2 ^ 5 + end + "); + + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v10:Fixnum[2] = Const Value(2) + v12:Fixnum[5] = Const Value(5) + PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) + v24:Fixnum[7] = Const Value(7) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v24 + "); + } + + #[test] + fn test_fold_fixnum_xor_same_negative_number() { + eval(" + def test + 123 ^ -123 + end + "); + + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v10:Fixnum[123] = Const Value(123) + v12:Fixnum[-123] = Const Value(-123) + PatchPoint MethodRedefined(Integer@0x1000, ^@0x1008, cme:0x1010) + v24:Fixnum[-2] = Const Value(-2) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v24 + "); + } + #[test] fn test_fold_fixnum_less() { eval(" From 6f0244e0782cfd9f638056b9010a0f8e0495f516 Mon Sep 17 00:00:00 2001 From: Nozomi Hijikata <121233810+nozomemein@users.noreply.github.com> Date: Tue, 24 Feb 2026 02:14:34 +0900 Subject: [PATCH 03/12] ZJIT: Pull GetEP out of GetLocal (with level > 0) (#16215) Closes: https://github.com/Shopify/ruby/issues/944 As a first step toward enabling CSE/LVN-based deduplication of frame EP loads, splitting `GetLocal` handling for `level > 0` into an explicit `GetEP + LoadField` path. A follow-up PR will handle the `level == 0` case. --- zjit/src/hir.rs | 61 +++++++++++++++++++++++++++++---------- zjit/src/hir/opt_tests.rs | 13 +++++---- zjit/src/hir/tests.rs | 40 ++++++++++++++----------- 3 files changed, 76 insertions(+), 38 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 3443d710444dc6..cc47c447400cae 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -854,6 +854,7 @@ pub enum Insn { /// Get a local variable from a higher scope or the heap. /// If `use_sp` is true, it uses the SP register to optimize the read. /// `rest_param` is used by infer_types to infer the ArrayExact type. + /// TODO: Replace the level == 0 + use_sp path with LoadSP + LoadField. GetLocal { level: u32, ep_offset: u32, use_sp: bool, rest_param: bool }, /// Check whether VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM is set in the environment flags. /// Returns CBool (0/1). @@ -1169,7 +1170,7 @@ impl Insn { Insn::GetEP { .. } => effects::Empty, Insn::GetLEP { .. } => effects::Empty, Insn::LoadSelf { .. } => Effect::read_write(abstract_heaps::Frame, abstract_heaps::Empty), - Insn::LoadField { .. } => Effect::read_write(abstract_heaps::Other, abstract_heaps::Empty), + Insn::LoadField { .. } => Effect::read_write(abstract_heaps::Memory, abstract_heaps::Empty), Insn::StoreField { .. } => effects::Any, Insn::WriteBarrier { .. } => effects::Any, Insn::GetLocal { .. } => Effect::read_write(abstract_heaps::Locals, abstract_heaps::Empty), @@ -1272,6 +1273,15 @@ pub struct InsnPrinter<'a> { iseq: Option, } +fn get_local_var_id(iseq: IseqPtr, level: u32, ep_offset: u32) -> ID { + let mut current_iseq = iseq; + for _ in 0..level { + current_iseq = unsafe { rb_get_iseq_body_parent_iseq(current_iseq) }; + } + let local_idx = ep_offset_to_local_idx(current_iseq, ep_offset); + unsafe { rb_zjit_local_id(current_iseq, local_idx.try_into().unwrap()) } +} + /// Get the name of a local variable given iseq, level, and ep_offset. /// Returns /// - `":name"` if iseq is available and name is a real identifier, @@ -1281,12 +1291,7 @@ pub struct InsnPrinter<'a> { /// /// This mimics local_var_name() from iseq.c. fn get_local_var_name_for_printer(iseq: Option, level: u32, ep_offset: u32) -> Option { - let mut current_iseq = iseq?; - for _ in 0..level { - current_iseq = unsafe { rb_get_iseq_body_parent_iseq(current_iseq) }; - } - let local_idx = ep_offset_to_local_idx(current_iseq, ep_offset); - let id: ID = unsafe { rb_zjit_local_id(current_iseq, local_idx.try_into().unwrap()) }; + let id = get_local_var_id(iseq?, level, ep_offset); if id.0 == 0 || unsafe { rb_id2str(id) } == Qfalse { return Some(String::from("")); @@ -3057,6 +3062,29 @@ impl Function { self.push_insn(block, Insn::GuardNoBitsSet { val: flags, mask: Const::CUInt64(RUBY_ELTS_SHARED as u64), mask_name: Some(ID!(RUBY_ELTS_SHARED)), reason: SideExitReason::GuardNotShared, state }); } + // TODO: This helper is currently used for level>0 local reads only. + // Split GetLocal(level==0) into explicit SP/EP helpers in a follow-up. + fn get_local_from_ep( + &mut self, + block: BlockId, + ep_offset: u32, + level: u32, + return_type: Type, + ) -> InsnId { + let ep = self.push_insn(block, Insn::GetEP { level }); + let local_id = get_local_var_id(self.iseq, level, ep_offset); + let ep_offset = i32::try_from(ep_offset) + .unwrap_or_else(|_| panic!("Could not convert ep_offset {ep_offset} to i32")); + let offset = -(SIZEOF_VALUE_I32 * ep_offset); + + self.push_insn(block, Insn::LoadField { + recv: ep, + id: local_id, + offset, + return_type, + }) + } + /// Rewrite eligible Send opcodes into SendDirect /// opcodes if we know the target ISEQ statically. This removes run-time method lookups and /// opens the door for inlining. @@ -6836,7 +6864,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } YARVINSN_getlocal_WC_1 => { let ep_offset = get_arg(pc, 0).as_u32(); - state.stack_push(fun.push_insn(block, Insn::GetLocal { ep_offset, level: 1, use_sp: false, rest_param: false })); + state.stack_push(fun.get_local_from_ep(block, ep_offset, 1, types::BasicObject)); } YARVINSN_setlocal_WC_1 => { let ep_offset = get_arg(pc, 0).as_u32(); @@ -6845,7 +6873,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { YARVINSN_getlocal => { let ep_offset = get_arg(pc, 0).as_u32(); let level = get_arg(pc, 1).as_u32(); - state.stack_push(fun.push_insn(block, Insn::GetLocal { ep_offset, level, use_sp: false, rest_param: false })); + if level == 0 { + state.stack_push(fun.push_insn(block, Insn::GetLocal { ep_offset, level, use_sp: false, rest_param: false })); + } else { + state.stack_push(fun.get_local_from_ep(block, ep_offset, level, types::BasicObject)); + } } YARVINSN_setlocal => { let ep_offset = get_arg(pc, 0).as_u32(); @@ -6956,12 +6988,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { })); // Push modified block: read Proc from EP. - let modified_val = fun.push_insn(modified_block, Insn::GetLocal { - ep_offset, - level, - use_sp: false, - rest_param: false, - }); + let modified_val = if level == 0 { + fun.push_insn(modified_block, Insn::GetLocal { ep_offset, level, use_sp: false, rest_param: false }) + } else { + fun.get_local_from_ep(modified_block, ep_offset, level, types::BasicObject) + }; finish_getblockparam_branch( &mut fun, modified_block, diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 3cb3b829e003c1..0c16528d13f322 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -4461,14 +4461,15 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:CBool = IsBlockParamModified l1 IfTrue v10, bb4(v6) - v19:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v6, v19) + v20:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v6, v20) bb4(v11:BasicObject): - v17:BasicObject = GetLocal :block, l1, EP@3 - Jump bb6(v11, v17) - bb6(v21:BasicObject, v22:BasicObject): + v17:CPtr = GetEP 1 + v18:BasicObject = LoadField v17, :block@0x1000 + Jump bb6(v11, v18) + bb6(v22:BasicObject, v23:BasicObject): CheckInterrupts - Return v22 + Return v23 "); } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index f94057d414bbf7..7cdb907fa825cf 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -946,18 +946,23 @@ pub mod hir_build_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v10:BasicObject = GetLocal :l2, l2, EP@4 - SetLocal :l1, l1, EP@3, v10 - v15:BasicObject = GetLocal :l1, l1, EP@3 - v17:BasicObject = GetLocal :l2, l2, EP@4 - v20:BasicObject = Send v15, :+, v17 # SendFallbackReason: Uncategorized(opt_plus) - SetLocal :l2, l2, EP@4, v20 - v25:BasicObject = GetLocal :l2, l2, EP@4 - v27:BasicObject = GetLocal :l3, l3, EP@5 - v30:BasicObject = Send v25, :+, v27 # SendFallbackReason: Uncategorized(opt_plus) - SetLocal :l3, l3, EP@5, v30 + v10:CPtr = GetEP 2 + v11:BasicObject = LoadField v10, :l2@0x1000 + SetLocal :l1, l1, EP@3, v11 + v16:CPtr = GetEP 1 + v17:BasicObject = LoadField v16, :l1@0x1001 + v19:CPtr = GetEP 2 + v20:BasicObject = LoadField v19, :l2@0x1000 + v23:BasicObject = Send v17, :+, v20 # SendFallbackReason: Uncategorized(opt_plus) + SetLocal :l2, l2, EP@4, v23 + v28:CPtr = GetEP 2 + v29:BasicObject = LoadField v28, :l2@0x1000 + v31:CPtr = GetEP 3 + v32:BasicObject = LoadField v31, :l3@0x1002 + v35:BasicObject = Send v29, :+, v32 # SendFallbackReason: Uncategorized(opt_plus) + SetLocal :l3, l3, EP@5, v35 CheckInterrupts - Return v30 + Return v35 " ); } @@ -3088,14 +3093,15 @@ pub mod hir_build_tests { IfTrue v10, bb4(v6) Jump bb5(v6) bb4(v11:BasicObject): - v17:BasicObject = GetLocal :block, l1, EP@3 - Jump bb6(v11, v17) + v17:CPtr = GetEP 1 + v18:BasicObject = LoadField v17, :block@0x1000 + Jump bb6(v11, v18) bb5(v13:BasicObject): - v19:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v13, v19) - bb6(v21:BasicObject, v22:BasicObject): + v20:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v13, v20) + bb6(v22:BasicObject, v23:BasicObject): CheckInterrupts - Return v22 + Return v23 "); } From 20eeb72085faf196553981b2dacb360f13b927f6 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Mon, 23 Feb 2026 09:20:20 -0800 Subject: [PATCH 04/12] Commit a diff shown on check_misc https://github.com/ruby/ruby/actions/runs/22316806245/job/64563939588 Local `make update-man-date` generates nothing but CI complains about it, so just pushing this diff to make it happy. --- man/erb.1 | 2 +- man/goruby.1 | 2 +- man/ruby.1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/man/erb.1 b/man/erb.1 index ed1e599cddd52e..7f606ae5faa1d3 100644 --- a/man/erb.1 +++ b/man/erb.1 @@ -1,5 +1,5 @@ .\"Ruby is copyrighted by Yukihiro Matsumoto . -.Dd February 22, 2026 +.Dd February 24, 2026 .Dt ERB 1 "Ruby Programmer's Reference Guide" .Os UNIX .Sh NAME diff --git a/man/goruby.1 b/man/goruby.1 index 4d137341f11f0b..1d6a32663a050c 100644 --- a/man/goruby.1 +++ b/man/goruby.1 @@ -1,5 +1,5 @@ .\"Ruby is copyrighted by Yukihiro Matsumoto . -.Dd February 22, 2026 +.Dd February 24, 2026 .Dt GORUBY 1 "Ruby Programmer's Reference Guide" .Os UNIX .Sh NAME diff --git a/man/ruby.1 b/man/ruby.1 index cf5b09121b734f..177e8ac5ddf8c1 100644 --- a/man/ruby.1 +++ b/man/ruby.1 @@ -1,5 +1,5 @@ .\"Ruby is copyrighted by Yukihiro Matsumoto . -.Dd February 22, 2026 +.Dd February 24, 2026 .Dt RUBY 1 "Ruby Programmer's Reference Guide" .Os UNIX .Sh NAME From 2d8e24c8c4284fa32552107e0ec64280730e9b18 Mon Sep 17 00:00:00 2001 From: Randy Stauner Date: Mon, 23 Feb 2026 13:31:13 -0700 Subject: [PATCH 05/12] ZJIT: Guard receiver type in inline Array method handlers (#16141) inline_array_push, inline_array_pop, and inline_array_aref did not guard/coerce the receiver to Array type before emitting Array-specific HIR instructions (ArrayPush, ArrayPop, ArrayLength, ArrayAref). This caused a MismatchedOperandType validation failure when these handlers were called from the InvokeSuper CFUNC path, where the receiver (self) has type BasicObject rather than Array. The crash was triggered by representable gem's Binding::Map#<< calling super(binding) to invoke Array#<< on an Array subclass. Add likely_a + coerce_to checks matching the pattern used by inline_array_aset. This is a no-op in the normal SendWithoutBlock path where the caller already adds a GuardType, but correctly inserts the guard when called from InvokeSuper. --- zjit/src/cruby_methods.rs | 9 ++- zjit/src/hir/opt_tests.rs | 160 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 1 deletion(-) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index f44c33845cf63b..d6cbda0c698144 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -342,7 +342,10 @@ fn inline_kernel_block_given_p(fun: &mut hir::Function, block: hir::BlockId, _re fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { if let &[index] = args { - if fun.likely_a(index, types::Fixnum, state) { + if fun.likely_a(recv, types::Array, state) + && fun.likely_a(index, types::Fixnum, state) + { + let recv = fun.coerce_to(block, recv, types::Array, state); let index = fun.coerce_to(block, index, types::Fixnum, state); let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); let length = fun.push_insn(block, hir::Insn::ArrayLength { array: recv }); @@ -386,6 +389,8 @@ fn inline_array_aset(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In fn inline_array_push(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { // Inline only the case of `<<` or `push` when called with a single argument. if let &[val] = args { + if !fun.likely_a(recv, types::Array, state) { return None; } + let recv = fun.coerce_to(block, recv, types::Array, state); let _ = fun.push_insn(block, hir::Insn::ArrayPush { array: recv, val, state }); return Some(recv); } @@ -395,6 +400,8 @@ fn inline_array_push(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In fn inline_array_pop(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { // Only inline the case of no arguments. let &[] = args else { return None; }; + if !fun.likely_a(recv, types::Array, state) { return None; } + let recv = fun.coerce_to(block, recv, types::Array, state); fun.guard_not_shared(block, recv, state); Some(fun.push_insn(block, hir::Insn::ArrayPop { array: recv, state })) } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 0c16528d13f322..5d3f8513278833 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -8539,6 +8539,166 @@ mod hir_opt_tests { "); } + #[test] + fn test_optimize_array_push_with_array_subclass() { + eval(" + class PushSubArray < Array + def <<(val) = super + end + test = PushSubArray.new + test << 1 + "); + assert_snapshot!(hir_string_proc("PushSubArray.new.method(:<<)"), @r" + fn <<@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :val, l0, SP@4 + Jump bb3(v1, v2) + bb2(): + EntryPoint JIT(0) + v5:BasicObject = LoadArg :self@0 + v6:BasicObject = LoadArg :val@1 + Jump bb3(v5, v6) + bb3(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) + v22:CPtr = GetLEP + v23:RubyValue = LoadField v22, :_ep_method_entry@0x1038 + v24:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v23, Value(VALUE(0x1040)) + v25:RubyValue = LoadField v22, :_ep_specval@0x1048 + v26:FalseClass = GuardBitEquals v25, Value(false) + v27:Array = GuardType v8, Array + ArrayPush v27, v9 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_optimize_array_pop_with_array_subclass() { + eval(" + class PopSubArray < Array + def pop = super + end + test = PopSubArray.new([1]) + test.pop + "); + assert_snapshot!(hir_string_proc("PopSubArray.new.method(:pop)"), @r" + fn pop@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, pop@0x1008, cme:0x1010) + v18:CPtr = GetLEP + v19:RubyValue = LoadField v18, :_ep_method_entry@0x1038 + v20:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v19, Value(VALUE(0x1040)) + v21:RubyValue = LoadField v18, :_ep_specval@0x1048 + v22:FalseClass = GuardBitEquals v21, Value(false) + PatchPoint MethodRedefined(Array@0x1000, pop@0x1008, cme:0x1010) + v28:CPtr = GetLEP + v29:RubyValue = LoadField v28, :_ep_method_entry@0x1038 + v30:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v29, Value(VALUE(0x1040)) + v31:RubyValue = LoadField v28, :_ep_specval@0x1048 + v32:FalseClass = GuardBitEquals v31, Value(false) + v23:Array = GuardType v6, Array + v24:CUInt64 = LoadField v23, :_rbasic_flags@0x1049 + v25:CUInt64 = GuardNoBitsSet v24, RUBY_ELTS_SHARED=CUInt64(4096) + v26:BasicObject = ArrayPop v23 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_optimize_array_aref_with_array_subclass_and_fixnum() { + eval(" + class ArefSubArray < Array + def [](idx) = super + end + test = ArefSubArray.new([1]) + test[0] + "); + assert_snapshot!(hir_string_proc("ArefSubArray.new.method(:[])"), @r" + fn []@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :idx, l0, SP@4 + Jump bb3(v1, v2) + bb2(): + EntryPoint JIT(0) + v5:BasicObject = LoadArg :self@0 + v6:BasicObject = LoadArg :idx@1 + Jump bb3(v5, v6) + bb3(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) + v22:CPtr = GetLEP + v23:RubyValue = LoadField v22, :_ep_method_entry@0x1038 + v24:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v23, Value(VALUE(0x1040)) + v25:RubyValue = LoadField v22, :_ep_specval@0x1048 + v26:FalseClass = GuardBitEquals v25, Value(false) + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) + v36:CPtr = GetLEP + v37:RubyValue = LoadField v36, :_ep_method_entry@0x1038 + v38:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v37, Value(VALUE(0x1040)) + v39:RubyValue = LoadField v36, :_ep_specval@0x1048 + v40:FalseClass = GuardBitEquals v39, Value(false) + v27:Array = GuardType v8, Array + v28:Fixnum = GuardType v9, Fixnum + v29:CInt64 = UnboxFixnum v28 + v30:CInt64 = ArrayLength v27 + v31:CInt64 = GuardLess v29, v30 + v32:CInt64[0] = Const CInt64(0) + v33:CInt64 = GuardGreaterEq v31, v32 + v34:BasicObject = ArrayAref v27, v33 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v34 + "); + } + + #[test] + fn test_dont_optimize_array_aref_with_array_subclass_and_non_fixnum() { + eval(" + class ArefSubArrayRange < Array + def [](idx) = super + end + test = ArefSubArrayRange.new([1, 2, 3]) + test[0..1] + "); + assert_snapshot!(hir_string_proc("ArefSubArrayRange.new.method(:[])"), @r" + fn []@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :idx, l0, SP@4 + Jump bb3(v1, v2) + bb2(): + EntryPoint JIT(0) + v5:BasicObject = LoadArg :self@0 + v6:BasicObject = LoadArg :idx@1 + Jump bb3(v5, v6) + bb3(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) + v22:CPtr = GetLEP + v23:RubyValue = LoadField v22, :_ep_method_entry@0x1038 + v24:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v23, Value(VALUE(0x1040)) + v25:RubyValue = LoadField v22, :_ep_specval@0x1048 + v26:FalseClass = GuardBitEquals v25, Value(false) + v27:BasicObject = CCallVariadic v8, :Array#[]@0x1050, v9 + CheckInterrupts + Return v27 + "); + } + #[test] fn test_optimize_array_length() { eval(" From 070b7d40d35f8dbcf81809c6b19be2bbba1c8bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Co=C3=AAlho?= <36938811+tomascco@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:03:22 -0300 Subject: [PATCH 06/12] ZJIT: constant fold bitwise AND (&) operations (#16226) Following up https://github.com/ruby/ruby/pull/16225, I'm implementing fold on AND (&) operations. - Ruby turns this operation into a C `&` in https://github.com/tomascco/ruby/blob/880eb3c76f09c44c85389ac5efbc555afafa3e6f/numeric.c#L5197-L5210 - Rustc compiler emits an `and` assembly instruction on x86: https://godbolt.org/z/Ee74vKTfb So the operations are equivalent. I tested manually some cases and the results match. --- zjit/src/hir.rs | 6 ++++ zjit/src/hir/opt_tests.rs | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index cc47c447400cae..a3dd9697f67515 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -4584,6 +4584,12 @@ impl Function { _ => None, }) } + Insn::FixnumAnd { left, right, .. } => { + self.fold_fixnum_bop(insn_id, left, right, |l, r| match (l, r) { + (Some(l), Some(r)) => Some(l & r), + _ => None, + }) + } Insn::FixnumEq { left, right, .. } => { self.fold_fixnum_pred(insn_id, left, right, |l, r| match (l, r) { (Some(l), Some(r)) => Some(l == r), diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 5d3f8513278833..8268245e58a4c0 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -508,6 +508,66 @@ mod hir_opt_tests { "); } + #[test] + fn test_fold_fixnum_and() { + eval(" + def test + 4 & -7 + end + "); + + assert_snapshot!(inspect("test"), @"0"); + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v10:Fixnum[4] = Const Value(4) + v12:Fixnum[-7] = Const Value(-7) + PatchPoint MethodRedefined(Integer@0x1000, &@0x1008, cme:0x1010) + v25:Fixnum[0] = Const Value(0) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_fold_fixnum_and_with_negative_self() { + eval(" + def test + -4 & 7 + end + "); + + assert_snapshot!(inspect("test"), @"4"); + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v10:Fixnum[-4] = Const Value(-4) + v12:Fixnum[7] = Const Value(7) + PatchPoint MethodRedefined(Integer@0x1000, &@0x1008, cme:0x1010) + v25:Fixnum[4] = Const Value(4) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + #[test] fn test_fold_fixnum_less() { eval(" From f3ee4bd0abe2f72bf804a9f564ab763fd5a59d48 Mon Sep 17 00:00:00 2001 From: Nozomi Hijikata <121233810+nozomemein@users.noreply.github.com> Date: Tue, 24 Feb 2026 08:58:04 +0900 Subject: [PATCH 07/12] ZJIT: Remove redundant snapshot for opt_getconstant_path (#16229) --- zjit/src/hir.rs | 4 +- zjit/src/hir/opt_tests.rs | 428 +++++++++++++++++++------------------- zjit/src/hir/tests.rs | 24 +-- 3 files changed, 227 insertions(+), 229 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index a3dd9697f67515..7d98dce666b874 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6714,9 +6714,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } YARVINSN_opt_getconstant_path => { let ic = get_arg(pc, 0).as_ptr(); - // TODO: Remove this extra Snapshot and pass `exit_id` to `GetConstantPath` instead. - let snapshot = fun.push_insn(block, Insn::Snapshot { state: exit_state }); - state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic, state: snapshot })); + state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic, state: exit_id })); } YARVINSN_branchunless | YARVINSN_branchunless_without_ints => { if opcode == YARVINSN_branchunless { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 8268245e58a4c0..7b831b1484f1f3 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -2830,10 +2830,10 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - v15:Fixnum[5] = Const Value(5) + v10:BasicObject = GetConstantPath 0x1000 + v14:Fixnum[5] = Const Value(5) CheckInterrupts - Return v15 + Return v14 "); } @@ -2976,15 +2976,15 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:NilClass): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) - v30:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count - v35:StringExact|NilClass = CCall v30, :Module#name@0x1048 + v34:StringExact|NilClass = CCall v29, :Module#name@0x1048 PatchPoint NoEPEscape(test) - v22:Fixnum[1] = Const Value(1) + v21:Fixnum[1] = Const Value(1) CheckInterrupts - Return v22 + Return v21 "); } @@ -3037,9 +3037,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v19:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v18:Class[C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -3062,19 +3062,19 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v30:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, Class) - v33:Class[Class@0x1018] = Const Value(VALUE(0x1018)) + v29:Class[Class@0x1018] = Const Value(VALUE(0x1018)) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1020, Module) - v36:Class[Module@0x1028] = Const Value(VALUE(0x1028)) + v32:Class[Module@0x1028] = Const Value(VALUE(0x1028)) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1030, BasicObject) - v39:Class[BasicObject@0x1038] = Const Value(VALUE(0x1038)) - v22:ArrayExact = NewArray v30, v33, v36, v39 + v35:Class[BasicObject@0x1038] = Const Value(VALUE(0x1038)) + v18:ArrayExact = NewArray v26, v29, v32, v35 CheckInterrupts - Return v22 + Return v18 "); } @@ -3097,13 +3097,13 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Enumerable) - v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v22:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, Kernel) - v27:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) - v16:ArrayExact = NewArray v24, v27 + v25:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + v14:ArrayExact = NewArray v22, v25 CheckInterrupts - Return v16 + Return v14 "); } @@ -3128,9 +3128,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MY_MODULE) - v19:ModuleSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:ModuleSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -3357,14 +3357,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) - v21:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count - v27:Class[Module@0x1010] = Const Value(VALUE(0x1010)) + v26:Class[Module@0x1010] = Const Value(VALUE(0x1010)) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v26 "); } @@ -3992,9 +3992,9 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 + v10:BasicObject = GetConstantPath 0x1000 CheckInterrupts - Return v11 + Return v10 "); } @@ -4016,9 +4016,9 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 + v10:BasicObject = GetConstantPath 0x1000 CheckInterrupts - Return v11 + Return v10 "); } @@ -4041,9 +4041,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v19:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -4072,9 +4072,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Foo::Bar::C) - v19:Class[Foo::Bar::C@0x1008] = Const Value(VALUE(0x1008)) + v18:Class[Foo::Bar::C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -4098,17 +4098,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v44:Class[C@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) - v47:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) + v46:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) - v51:NilClass = Const Value(nil) + v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); } @@ -4136,17 +4136,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v47:Class[C@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) - v16:Fixnum[1] = Const Value(1) + v46:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) - v50:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) + v49:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) - v53:BasicObject = SendDirect v50, 0x1068, :initialize (0x1078), v16 + v52:BasicObject = SendDirect v49, 0x1068, :initialize (0x1078), v15 CheckInterrupts CheckInterrupts - Return v50 + Return v49 "); } @@ -4169,17 +4169,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Object) - v44:Class[Object@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[Object@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Object@0x1008, new@0x1009, cme:0x1010) - v47:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) + v46:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) PatchPoint NoSingletonClass(Object@0x1008) PatchPoint MethodRedefined(Object@0x1008, initialize@0x1038, cme:0x1040) - v51:NilClass = Const Value(nil) + v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); } @@ -4202,17 +4202,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, BasicObject) - v44:Class[BasicObject@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[BasicObject@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1009, cme:0x1010) - v47:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) + v46:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) PatchPoint NoSingletonClass(BasicObject@0x1008) PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1038, cme:0x1040) - v51:NilClass = Const Value(nil) + v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); } @@ -4235,15 +4235,15 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Hash) - v44:Class[Hash@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[Hash@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Hash@0x1008, new@0x1009, cme:0x1010) - v47:HashExact = ObjectAllocClass Hash:VALUE(0x1008) + v46:HashExact = ObjectAllocClass Hash:VALUE(0x1008) IncrCounter complex_arg_pass_param_block - v20:BasicObject = Send v47, :initialize # SendFallbackReason: Complex argument passing + v19:BasicObject = Send v46, :initialize # SendFallbackReason: Complex argument passing CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); assert_snapshot!(inspect("test"), @"{}"); } @@ -4267,15 +4267,15 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Array) - v47:Class[Array@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) - v16:Fixnum[1] = Const Value(1) + v46:Class[Array@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) - v58:BasicObject = CCallVariadic v47, :Array.new@0x1040, v16 + v57:BasicObject = CCallVariadic v46, :Array.new@0x1040, v15 CheckInterrupts - Return v58 + Return v57 "); } @@ -4298,17 +4298,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Set) - v44:Class[Set@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[Set@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Set@0x1008, new@0x1009, cme:0x1010) - v18:HeapBasicObject = ObjectAlloc v44 + v17:HeapBasicObject = ObjectAlloc v43 PatchPoint NoSingletonClass(Set@0x1008) PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) - v50:SetExact = GuardType v18, SetExact - v51:BasicObject = CCallVariadic v50, :Set#initialize@0x1068 + v49:SetExact = GuardType v17, SetExact + v50:BasicObject = CCallVariadic v49, :Set#initialize@0x1068 CheckInterrupts CheckInterrupts - Return v18 + Return v17 "); } @@ -4331,14 +4331,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v44:Class[String@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(String@0x1008, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) - v55:BasicObject = CCallVariadic v44, :String.new@0x1040 + v54:BasicObject = CCallVariadic v43, :String.new@0x1040 CheckInterrupts - Return v55 + Return v54 "); } @@ -4361,18 +4361,18 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Regexp) - v48:Class[Regexp@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) - v16:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) - v17:StringExact = StringCopy v16 + v47:Class[Regexp@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v15:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v16:StringExact = StringCopy v15 PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) - v51:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) + v50:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) PatchPoint NoSingletonClass(Regexp@0x1008) PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) - v55:BasicObject = CCallVariadic v51, :Regexp#initialize@0x1078, v17 + v54:BasicObject = CCallVariadic v50, :Regexp#initialize@0x1078, v16 CheckInterrupts CheckInterrupts - Return v51 + Return v50 "); } @@ -5784,19 +5784,19 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, S) - v24:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:Fixnum[0] = Const Value(0) + v23:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - v28:CInt64[0] = UnboxFixnum v13 - v29:CInt64 = ArrayLength v24 - v30:CInt64[0] = GuardLess v28, v29 - v31:CInt64[0] = Const CInt64(0) - v32:CInt64[0] = GuardGreaterEq v30, v31 - v33:BasicObject = ArrayAref v24, v32 + v27:CInt64[0] = UnboxFixnum v12 + v28:CInt64 = ArrayLength v23 + v29:CInt64[0] = GuardLess v27, v28 + v30:CInt64[0] = Const CInt64(0) + v31:CInt64[0] = GuardGreaterEq v29, v30 + v32:BasicObject = ArrayAref v23, v31 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v33 + Return v32 "); // TODO(max): Check the result of `S[0] = 5; test` using `inspect` to make sure that we // actually do the load at run-time. @@ -6048,9 +6048,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MY_SET) - v19:SetExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:SetExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -6185,13 +6185,13 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Foo) - v23:Class[Foo@0x1008] = Const Value(VALUE(0x1008)) - v13:Fixnum[100] = Const Value(100) + v22:Class[Foo@0x1008] = Const Value(VALUE(0x1008)) + v12:Fixnum[100] = Const Value(100) PatchPoint NoSingletonClass(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count CheckInterrupts - Return v13 + Return v12 "); } @@ -7221,17 +7221,17 @@ mod hir_opt_tests { v13:ArrayExact = NewArray PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, A) - v37:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v35:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, B) - v40:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + v38:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) PatchPoint NoSingletonClass(Array@0x1020) PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) - v44:BasicObject = CCallVariadic v37, :zip@0x1058, v40 - v24:BasicObject = GetLocal :result, l0, EP@3 + v42:BasicObject = CCallVariadic v35, :zip@0x1058, v38 + v22:BasicObject = GetLocal :result, l0, EP@3 PatchPoint NoEPEscape(test) CheckInterrupts - Return v24 + Return v22 "); } @@ -7392,14 +7392,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v26:CShape = LoadField v21, :_shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) - v28:NilClass = Const Value(nil) + v25:CShape = LoadField v20, :_shape_id@0x1048 + v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) + v27:NilClass = Const Value(nil) CheckInterrupts - Return v28 + Return v27 "); } @@ -7428,14 +7428,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v26:CShape = LoadField v21, :_shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) - v28:NilClass = Const Value(nil) + v25:CShape = LoadField v20, :_shape_id@0x1048 + v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) + v27:NilClass = Const Value(nil) CheckInterrupts - Return v28 + Return v27 "); } @@ -8231,14 +8231,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, H) - v24:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:StaticSymbol[:a] = Const Value(VALUE(0x1010)) + v23:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:StaticSymbol[:a] = Const Value(VALUE(0x1010)) PatchPoint NoSingletonClass(Hash@0x1018) PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) - v28:BasicObject = HashAref v24, v13 + v27:BasicObject = HashAref v23, v12 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v28 + Return v27 "); } @@ -8365,15 +8365,15 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Thread) - v21:Class[Thread@0x1008] = Const Value(VALUE(0x1008)) + v20:Class[Thread@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) - v25:CPtr = LoadEC - v26:CPtr = LoadField v25, :thread_ptr@0x1048 - v27:BasicObject = LoadField v26, :self@0x1049 + v24:CPtr = LoadEC + v25:CPtr = LoadField v24, :thread_ptr@0x1048 + v26:BasicObject = LoadField v25, :self@0x1049 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v26 "); } @@ -11114,14 +11114,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v27:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) PatchPoint NoSingletonClass(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) - v31:BoolExact = IsA v9, v27 + v30:BoolExact = IsA v9, v26 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v31 + Return v30 "); } @@ -11146,14 +11146,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v27:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v26:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) PatchPoint NoSingletonClass(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count - v32:BoolExact = CCall v27, :Module#===@0x1048, v9 + v31:BoolExact = CCall v26, :Module#===@0x1048, v9 CheckInterrupts - Return v32 + Return v31 "); } @@ -11178,14 +11178,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v25:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, is_a?@0x1009, cme:0x1010) - v29:StringExact = GuardType v9, StringExact - v30:BoolExact = IsA v29, v25 + v28:StringExact = GuardType v9, StringExact + v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v30 + Return v29 "); } @@ -11210,13 +11210,13 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v25:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) - v29:StringExact = GuardType v9, StringExact - v30:BasicObject = CCallWithFrame v29, :Kernel#is_a?@0x1048, v25 + v28:StringExact = GuardType v9, StringExact + v29:BasicObject = CCallWithFrame v28, :Kernel#is_a?@0x1048, v24 CheckInterrupts - Return v30 + Return v29 "); } @@ -11244,14 +11244,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) - v29:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) + v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) - v33:StringExact = GuardType v9, StringExact + v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count - v21:Fixnum[5] = Const Value(5) + v20:Fixnum[5] = Const Value(5) CheckInterrupts - Return v21 + Return v20 "); } @@ -11279,14 +11279,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) - v31:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) + v30:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) PatchPoint NoSingletonClass(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count - v23:Fixnum[5] = Const Value(5) + v22:Fixnum[5] = Const Value(5) CheckInterrupts - Return v23 + Return v22 "); } @@ -11311,14 +11311,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v25:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1008) PatchPoint MethodRedefined(String@0x1008, kind_of?@0x1009, cme:0x1010) - v29:StringExact = GuardType v9, StringExact - v30:BoolExact = IsA v29, v25 + v28:StringExact = GuardType v9, StringExact + v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v30 + Return v29 "); } @@ -11343,13 +11343,13 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v25:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) - v29:StringExact = GuardType v9, StringExact - v30:BasicObject = CCallWithFrame v29, :Kernel#kind_of?@0x1048, v25 + v28:StringExact = GuardType v9, StringExact + v29:BasicObject = CCallWithFrame v28, :Kernel#kind_of?@0x1048, v24 CheckInterrupts - Return v30 + Return v29 "); } @@ -11377,14 +11377,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) - v29:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) + v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) - v33:StringExact = GuardType v9, StringExact + v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count - v21:Fixnum[5] = Const Value(5) + v20:Fixnum[5] = Const Value(5) CheckInterrupts - Return v21 + Return v20 "); } @@ -11737,12 +11737,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_OBJ) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozen@0x1010) PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) - v30:Fixnum[1] = Const Value(1) + v29:Fixnum[1] = Const Value(1) CheckInterrupts - Return v30 + Return v29 "); } @@ -11778,12 +11778,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MULTI_FROZEN) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestMultiIvars@0x1010) PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) - v30:Fixnum[20] = Const Value(20) + v29:Fixnum[20] = Const Value(20) CheckInterrupts - Return v30 + Return v29 "); } @@ -11817,12 +11817,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_STR) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenStr@0x1010) PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) - v30:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) + v29:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v30 + Return v29 "); } @@ -11856,12 +11856,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_NIL) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenNil@0x1010) PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) - v30:NilClass = Const Value(nil) + v29:NilClass = Const Value(nil) CheckInterrupts - Return v30 + Return v29 "); } @@ -11895,14 +11895,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, UNFROZEN_OBJ) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestUnfrozen@0x1010) PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) - v26:CShape = LoadField v21, :_shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) - v28:BasicObject = LoadField v21, :@a@0x104a + v25:CShape = LoadField v20, :_shape_id@0x1048 + v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) + v27:BasicObject = LoadField v20, :@a@0x104a CheckInterrupts - Return v28 + Return v27 "); } @@ -11936,12 +11936,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_READER) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestAttrReader@0x1010) PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) - v30:Fixnum[42] = Const Value(42) + v29:Fixnum[42] = Const Value(42) CheckInterrupts - Return v30 + Return v29 "); } @@ -11975,12 +11975,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_SYM) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenSym@0x1010) PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) - v30:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) + v29:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v30 + Return v29 "); } @@ -12014,12 +12014,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_TRUE) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestFrozenBool@0x1010) PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) - v30:TrueClass = Const Value(true) + v29:TrueClass = Const Value(true) CheckInterrupts - Return v30 + Return v29 "); } @@ -12094,21 +12094,21 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) - v29:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v27:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) - v54:Fixnum[100] = Const Value(100) + v52:Fixnum[100] = Const Value(100) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1048, NESTED_FROZEN) - v35:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v33:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) - v56:Fixnum[200] = Const Value(200) + v54:Fixnum[200] = Const Value(200) PatchPoint MethodRedefined(Integer@0x1080, +@0x1088, cme:0x1090) - v57:Fixnum[300] = Const Value(300) + v55:Fixnum[300] = Const Value(300) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v57 + Return v55 "); } @@ -12132,14 +12132,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, S) - v21:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v20:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoSingletonClass(String@0x1010) PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) - v25:CInt64 = LoadField v21, :len@0x1048 - v26:Fixnum = BoxFixnum v25 + v24:CInt64 = LoadField v20, :len@0x1048 + v25:Fixnum = BoxFixnum v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v25 "); } @@ -12196,10 +12196,10 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } @@ -12252,10 +12252,10 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:BasicObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :initialize # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:BasicObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :initialize # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } @@ -12280,10 +12280,10 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:ObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :toplevel_method # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:ObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :toplevel_method # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } @@ -12340,10 +12340,10 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } @@ -13164,27 +13164,27 @@ mod hir_opt_tests { v70:Falsy = RefineType v59, Falsy PatchPoint NoSingletonClass(Object@0x1018) PatchPoint MethodRedefined(Object@0x1018, lambda@0x1020, cme:0x1028) - v115:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)] - v116:BasicObject = CCallWithFrame v115, :Kernel#lambda@0x1050, block=0x1058 + v114:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)] + v115:BasicObject = CCallWithFrame v114, :Kernel#lambda@0x1050, block=0x1058 v74:BasicObject = GetLocal :list, l0, EP@6 v76:BasicObject = GetLocal :iter_method, l0, EP@4 v77:BasicObject = GetLocal :kwsplat, l0, EP@3 - SetLocal :sep, l0, EP@5, v116 - Jump bb8(v57, v74, v116, v76, v77) + SetLocal :sep, l0, EP@5, v115 + Jump bb8(v57, v74, v115, v76, v77) bb8(v81:BasicObject, v82:BasicObject, v83:BasicObject, v84:BasicObject, v85:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1060, CONST) - v111:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068)) - SetLocal :kwsplat, l0, EP@3, v111 - v95:BasicObject = GetLocal :list, l0, EP@6 - v97:BasicObject = GetLocal :iter_method, l0, EP@4 - v99:BasicObject = Send v95, 0x1070, :__send__, v97 # SendFallbackReason: Send: unsupported method type Optimized - v100:BasicObject = GetLocal :list, l0, EP@6 - v101:BasicObject = GetLocal :sep, l0, EP@5 - v102:BasicObject = GetLocal :iter_method, l0, EP@4 - v103:BasicObject = GetLocal :kwsplat, l0, EP@3 - CheckInterrupts - Return v99 + v110:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068)) + SetLocal :kwsplat, l0, EP@3, v110 + v94:BasicObject = GetLocal :list, l0, EP@6 + v96:BasicObject = GetLocal :iter_method, l0, EP@4 + v98:BasicObject = Send v94, 0x1070, :__send__, v96 # SendFallbackReason: Send: unsupported method type Optimized + v99:BasicObject = GetLocal :list, l0, EP@6 + v100:BasicObject = GetLocal :sep, l0, EP@5 + v101:BasicObject = GetLocal :iter_method, l0, EP@4 + v102:BasicObject = GetLocal :kwsplat, l0, EP@3 + CheckInterrupts + Return v98 "); } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 7cdb907fa825cf..9a204d83ecad6c 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2284,20 +2284,20 @@ pub mod hir_build_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - v13:NilClass = Const Value(nil) - v16:CBool = IsMethodCFunc v11, :new - IfFalse v16, bb4(v6, v13, v11) - v18:HeapBasicObject = ObjectAlloc v11 - v20:BasicObject = Send v18, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block) + v10:BasicObject = GetConstantPath 0x1000 + v12:NilClass = Const Value(nil) + v15:CBool = IsMethodCFunc v10, :new + IfFalse v15, bb4(v6, v12, v10) + v17:HeapBasicObject = ObjectAlloc v10 + v19:BasicObject = Send v17, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts - Jump bb5(v6, v18, v20) - bb4(v24:BasicObject, v25:NilClass, v26:BasicObject): - v29:BasicObject = Send v26, :new # SendFallbackReason: Uncategorized(opt_send_without_block) - Jump bb5(v24, v29, v25) - bb5(v32:BasicObject, v33:BasicObject, v34:BasicObject): + Jump bb5(v6, v17, v19) + bb4(v23:BasicObject, v24:NilClass, v25:BasicObject): + v28:BasicObject = Send v25, :new # SendFallbackReason: Uncategorized(opt_send_without_block) + Jump bb5(v23, v28, v24) + bb5(v31:BasicObject, v32:BasicObject, v33:BasicObject): CheckInterrupts - Return v33 + Return v32 "); } From b479c41dd4fc2a3bd53bba545434eae495aec556 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 24 Feb 2026 09:40:34 +0900 Subject: [PATCH 08/12] Use .bundle gems dependencies directly --- spec/bundled_gems_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/bundled_gems_spec.rb b/spec/bundled_gems_spec.rb index 0c9b45a4ad66dd..7b8ab64f252daf 100644 --- a/spec/bundled_gems_spec.rb +++ b/spec/bundled_gems_spec.rb @@ -24,15 +24,15 @@ def self.ruby=(ruby) require_relative "bundler/support/rubygems_ext" Spec::Helpers.install_dev_bundler FileUtils.mkdir_p Spec::Path.gem_path + + %w[sinatra rack tilt rack-protection rack-session rack-test mustermann base64 logger compact_index].each do |gem| + path, = Dir[File.expand_path("../.bundle/gems/#{gem}-*/lib", __dir__)] + $LOAD_PATH.unshift(path) if path + end end config.around(:each) do |example| FileUtils.cp_r Spec::Path.pristine_system_gem_path, Spec::Path.system_gem_path - FileUtils.mkdir_p Spec::Path.base_system_gem_path.join("gems") - %w[sinatra rack tilt rack-protection rack-session rack-test mustermann base64 logger compact_index].each do |gem| - path, = Dir[File.expand_path("../.bundle/gems/#{gem}-*", __dir__)] - FileUtils.cp_r path, Spec::Path.base_system_gem_path.join("gems") - end with_gem_path_as(system_gem_path) do Bundler.ui.silence { example.run } From 8e9eb698b6638e0f3e76748c756f699e79d2e97d Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Wed, 13 Mar 2024 17:17:20 -0700 Subject: [PATCH 09/12] Freeze singleton class chain The following code: ```ruby x = Object.new sc1 = x.singleton_class sc2 = sc1.singleton_class x.freeze ``` Would freeze sc1 but not sc2, even though sc1 would be frozen. Handle this by walking the class pointer chain for the object. If the class is a singleton class, and it isn't frozen, and the attached object for the singleton class is the object, the singleton class should be frozen, and we move to the next iteration. Fixes [Bug #20319] --- class.c | 22 +++++++++++---------- test/ruby/test_object.rb | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/class.c b/class.c index 81e740b2099ef7..cc4c6845b49977 100644 --- a/class.c +++ b/class.c @@ -2884,16 +2884,18 @@ singleton_class_of(VALUE obj, bool ensure_eigenclass) } void -rb_freeze_singleton_class(VALUE x) -{ - /* should not propagate to meta-meta-class, and so on */ - if (!RCLASS_SINGLETON_P(x)) { - VALUE klass = RBASIC_CLASS(x); - if (klass && // no class when hidden from ObjectSpace - FL_TEST_RAW(klass, FL_SINGLETON) && - !OBJ_FROZEN_RAW(klass)) { - OBJ_FREEZE(klass); - } +rb_freeze_singleton_class(VALUE attached_object) +{ + VALUE klass; + + /* Freeze singleton classes of singleton class, as singleton class is frozen, and so on */ + /* In each iteration, check the current object's class pointer is the singleton class of the object. */ + while ((klass = RBASIC_CLASS(attached_object)) && + FL_TEST_RAW(klass, FL_SINGLETON) && + !OBJ_FROZEN_RAW(klass) && + (RCLASS_ATTACHED_OBJECT(klass) == attached_object)) { + attached_object = klass; + OBJ_FREEZE(attached_object); } } diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index f4dfe2251b884f..2f340788be43b2 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -1025,6 +1025,48 @@ def test_singleton_class_freeze assert_predicate(ys, :frozen?, '[Bug #19169]') end + def test_singleton_class_of_singleton_class_freeze + x = Object.new + xs = x.singleton_class + xxs = xs.singleton_class + xxxs = xxs.singleton_class + x.freeze + assert_predicate(xs, :frozen?, '[Bug #20319]') + assert_predicate(xxs, :frozen?, '[Bug #20319]') + assert_predicate(xxxs, :frozen?, '[Bug #20319]') + + m = Module.new + y = Object.new + ys = y.singleton_class + ys.prepend(Module.new) + yys = ys.singleton_class + yys.prepend(Module.new) + yyys = yys.singleton_class + yyys.prepend(Module.new) + y.freeze + assert_predicate(ys, :frozen?, '[Bug #20319]') + assert_predicate(yys, :frozen?, '[Bug #20319]') + assert_predicate(yyys, :frozen?, '[Bug #20319]') + + c = Class.new + cs = c.singleton_class + ccs = cs.singleton_class + cccs = ccs.singleton_class + d = Class.new(c) + ds = d.singleton_class + dds = ds.singleton_class + ddds = dds.singleton_class + d.freeze + assert_predicate(d, :frozen?, '[Bug #20319]') + assert_predicate(ds, :frozen?, '[Bug #20319]') + assert_predicate(dds, :frozen?, '[Bug #20319]') + assert_predicate(ddds, :frozen?, '[Bug #20319]') + assert_not_predicate(c, :frozen?, '[Bug #20319]') + assert_not_predicate(cs, :frozen?, '[Bug #20319]') + assert_not_predicate(ccs, :frozen?, '[Bug #20319]') + assert_not_predicate(cccs, :frozen?, '[Bug #20319]') + end + def test_redef_method_missing bug5473 = '[ruby-core:40287]' ['ArgumentError.new("bug5473")', 'ArgumentError, "bug5473"', '"bug5473"'].each do |code| From 997ac19b74259b13f777a548b813a0f2c2228554 Mon Sep 17 00:00:00 2001 From: Randy Stauner Date: Mon, 23 Feb 2026 20:06:32 -0700 Subject: [PATCH 10/12] ZJIT: Remove redundant PatchPoints within basic blocks (#16231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new optimization pass that deduplicates PatchPoint instructions asserting the same invariant within a basic block when no intervening instruction could invalidate it. This is common in practice — e.g., `1 + 2 + 3` produces two identical `PatchPoint MethodRedefined(Integer, +, cme)` because each `+` call inserts one. The second is redundant since nothing between them can redefine Integer#+. The approach adds a `PatchPoint` leaf to the abstract heap DAG so the effect system can distinguish instructions that could invalidate invariants (writes to PatchPoint, e.g. Send/CCall) from those that cannot (e.g. IncrCounter, Const). The pass maintains a HashSet of seen invariants per block and skips duplicates, clearing the set when an instruction writes to the PatchPoint heap. Removed PatchPoints' orphaned Snapshot dependencies are cleaned up by the subsequent DCE pass. I added a new test with snapshots to `zjit/src/hir/tests.rs` in the first commit so that you can see how the change affects it in the second commit: https://github.com/ruby/ruby/pull/16231/changes/88cf26b6380cdf577522d11b4f3c838fed2a71b3#diff-38be021d8d0035cca77d3eab5ccc530109e7832f4711a5beb4dfced7001787ae closes https://github.com/Shopify/ruby/issues/779 --- zjit/src/hir.rs | 38 +++++++++++++++++++++------ zjit/src/hir/opt_tests.rs | 17 ------------ zjit/src/hir/tests.rs | 35 ++++++++++++++++++++++++ zjit/src/hir_effect/gen_hir_effect.rb | 1 + zjit/src/hir_effect/hir_effect.inc.rs | 12 ++++++--- zjit/src/stats.rs | 1 + 6 files changed, 75 insertions(+), 29 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 7d98dce666b874..ae316d409b0287 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -118,7 +118,7 @@ impl std::fmt::Display for BranchEdge { } /// Invalidation reasons -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Invariant { /// Basic operation is redefined BOPRedefined { @@ -1237,10 +1237,10 @@ impl Insn { Insn::GuardNoBitsSet { .. } => effects::Any, Insn::GuardGreaterEq { .. } => effects::Any, Insn::GuardLess { .. } => effects::Any, - Insn::PatchPoint { .. } => effects::Any, + Insn::PatchPoint { .. } => Effect::read_write(abstract_heaps::PatchPoint, abstract_heaps::Control), Insn::SideExit { .. } => effects::Any, - Insn::IncrCounter(_) => effects::Any, - Insn::IncrCounterPtr { .. } => effects::Any, + Insn::IncrCounter(_) => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Other), + Insn::IncrCounterPtr { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Other), Insn::CheckInterrupts { .. } => effects::Any, Insn::InvokeProc { .. } => effects::Any, Insn::RefineType { .. } => effects::Empty, @@ -5037,6 +5037,29 @@ impl Function { } } + /// Remove duplicate PatchPoint instructions within each basic block. + /// Two PatchPoints are redundant if they assert the same Invariant and no + /// intervening instruction could invalidate it (i.e., writes to PatchPoint). + fn remove_redundant_patch_points(&mut self) { + for block_id in self.rpo() { + let mut seen = HashSet::new(); + let insns = std::mem::take(&mut self.blocks[block_id.0].insns); + let mut new_insns = Vec::with_capacity(insns.len()); + for insn_id in insns { + let insn = self.find(insn_id); + if let Insn::PatchPoint { invariant, .. } = insn { + if !seen.insert(invariant) { + continue; + } + } else if insn.effects_of().write_bits().overlaps(abstract_heaps::PatchPoint) { + seen.clear(); + } + new_insns.push(insn_id); + } + self.blocks[block_id.0].insns = new_insns; + } + } + /// Return a list that has entry_block and then jit_entry_blocks fn entry_blocks(&self) -> Vec { let mut entry_blocks = self.jit_entry_blocks.clone(); @@ -5260,6 +5283,8 @@ impl Function { Counter::compile_hir_fold_constants_time_ns } else if ident_equal!($name, clean_cfg) { Counter::compile_hir_clean_cfg_time_ns + } else if ident_equal!($name, remove_redundant_patch_points) { + Counter::compile_hir_remove_redundant_patch_points_time_ns } else if ident_equal!($name, eliminate_dead_code) { Counter::compile_hir_eliminate_dead_code_time_ns } else { @@ -5286,6 +5311,7 @@ impl Function { run_pass!(optimize_c_calls); run_pass!(fold_constants); run_pass!(clean_cfg); + run_pass!(remove_redundant_patch_points); run_pass!(eliminate_dead_code); if should_dump { @@ -8473,8 +8499,6 @@ mod graphviz_tests { bb3 [label=< - - @@ -8536,7 +8560,6 @@ mod graphviz_tests { -
bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject) 
PatchPoint NoTracePoint 
PatchPoint NoTracePoint 
PatchPoint NoTracePoint 
PatchPoint MethodRedefined(Integer@0x1000, |@0x1008, cme:0x1010) 
v27:Fixnum = GuardType v11, Fixnum 
v28:Fixnum = GuardType v12, Fixnum 
v18:Truthy = RefineType v9, Truthy 
PatchPoint NoTracePoint 
v21:Fixnum[3] = Const Value(3) 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v21 
>]; @@ -8545,7 +8568,6 @@ mod graphviz_tests { bb4(v26:BasicObject, v27:Falsy)  PatchPoint NoTracePoint  v31:Fixnum[4] = Const Value(4)  - PatchPoint NoTracePoint  CheckInterrupts  Return v31  >]; diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 7b831b1484f1f3..94c70d29031e94 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -115,10 +115,7 @@ mod hir_opt_tests { v10:Fixnum[1] = Const Value(1) v12:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Integer@0x1000, +@0x1008, cme:0x1010) - v34:Fixnum[3] = Const Value(3) IncrCounter inline_cfunc_optimized_send_count - v17:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Integer@0x1000, +@0x1008, cme:0x1010) v35:Fixnum[6] = Const Value(6) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -147,10 +144,7 @@ mod hir_opt_tests { v10:Fixnum[5] = Const Value(5) v12:Fixnum[3] = Const Value(3) PatchPoint MethodRedefined(Integer@0x1000, -@0x1008, cme:0x1010) - v34:Fixnum[2] = Const Value(2) IncrCounter inline_cfunc_optimized_send_count - v17:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Integer@0x1000, -@0x1008, cme:0x1010) v35:Fixnum[1] = Const Value(1) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -3063,13 +3057,10 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, Class) v29:Class[Class@0x1018] = Const Value(VALUE(0x1018)) - PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1020, Module) v32:Class[Module@0x1028] = Const Value(VALUE(0x1028)) - PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1030, BasicObject) v35:Class[BasicObject@0x1038] = Const Value(VALUE(0x1038)) v18:ArrayExact = NewArray v26, v29, v32, v35 @@ -3098,7 +3089,6 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Enumerable) v22:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, Kernel) v25:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) v14:ArrayExact = NewArray v22, v25 @@ -5226,7 +5216,6 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) v11:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts Return v11 "); @@ -5323,7 +5312,6 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts Return v11 "); @@ -5420,7 +5408,6 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts Return v11 "); @@ -7222,7 +7209,6 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, A) v35:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, B) v38:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) PatchPoint NoSingletonClass(Array@0x1020) @@ -11514,7 +11500,6 @@ mod hir_opt_tests { v15:TrueClass = Const Value(true) PatchPoint NoSingletonClass(Class@0x1040) PatchPoint MethodRedefined(Class@0x1040, respond_to?@0x1048, cme:0x1050) - PatchPoint NoSingletonClass(Class@0x1040) PatchPoint MethodRedefined(Class@0x1040, _lex_actions@0x1078, cme:0x1080) v55:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count @@ -12098,10 +12083,8 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(TestNestedAccess@0x1010) PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) v52:Fixnum[100] = Const Value(100) - PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1048, NESTED_FROZEN) v33:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestNestedAccess@0x1010) PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) v54:Fixnum[200] = Const Value(200) PatchPoint MethodRedefined(Integer@0x1080, +@0x1088, cme:0x1090) diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 9a204d83ecad6c..bcfecaa38867b0 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -24,6 +24,41 @@ mod snapshot_tests { format!("{}", FunctionPrinter::with_snapshot(&function)) } + #[test] + fn test_remove_redundant_patch_points() { + eval(" + def test = 1 + 2 + 3 + test + test + "); + assert_snapshot!(optimized_hir_string("test"), @r" + fn test@:2: + bb0(): + Entries bb1, bb2 + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v8:Any = Snapshot FrameState { pc: 0x1000, stack: [], locals: [] } + PatchPoint NoTracePoint + v10:Fixnum[1] = Const Value(1) + v12:Fixnum[2] = Const Value(2) + v13:Any = Snapshot FrameState { pc: 0x1008, stack: [v10, v12], locals: [] } + PatchPoint MethodRedefined(Integer@0x1010, +@0x1018, cme:0x1020) + IncrCounter inline_cfunc_optimized_send_count + v35:Fixnum[6] = Const Value(6) + IncrCounter inline_cfunc_optimized_send_count + v21:Any = Snapshot FrameState { pc: 0x1048, stack: [v35], locals: [] } + CheckInterrupts + Return v35 + "); + } + #[test] fn test_new_array_with_elements() { eval("def test(a, b) = [a, b]"); diff --git a/zjit/src/hir_effect/gen_hir_effect.rb b/zjit/src/hir_effect/gen_hir_effect.rb index 5d13ebafa2e185..cd4ca73c103a03 100644 --- a/zjit/src/hir_effect/gen_hir_effect.rb +++ b/zjit/src/hir_effect/gen_hir_effect.rb @@ -48,6 +48,7 @@ def to_graphviz effect, f allocator = any.subeffect 'Allocator' control = any.subeffect 'Control' memory = any.subeffect 'Memory' +patch_point = any.subeffect 'PatchPoint' other = memory.subeffect 'Other' frame = memory.subeffect 'Frame' pc = frame.subeffect 'PC' diff --git a/zjit/src/hir_effect/hir_effect.inc.rs b/zjit/src/hir_effect/hir_effect.inc.rs index d9566b3eaa5574..7c5fc53ab3bb46 100644 --- a/zjit/src/hir_effect/hir_effect.inc.rs +++ b/zjit/src/hir_effect/hir_effect.inc.rs @@ -1,7 +1,7 @@ // This file is @generated by src/hir/gen_hir_effect.rb. mod bits { pub const Allocator: u8 = 1u8 << 0; - pub const Any: u8 = Allocator | Control | Memory; + pub const Any: u8 = Allocator | Control | Memory | PatchPoint; pub const Control: u8 = 1u8 << 1; pub const Empty: u8 = 0u8; pub const Frame: u8 = Locals | PC | Stack; @@ -9,12 +9,14 @@ mod bits { pub const Memory: u8 = Frame | Other; pub const Other: u8 = 1u8 << 3; pub const PC: u8 = 1u8 << 4; - pub const Stack: u8 = 1u8 << 5; - pub const AllBitPatterns: [(&str, u8); 10] = [ + pub const PatchPoint: u8 = 1u8 << 5; + pub const Stack: u8 = 1u8 << 6; + pub const AllBitPatterns: [(&str, u8); 11] = [ ("Any", Any), ("Memory", Memory), ("Frame", Frame), ("Stack", Stack), + ("PatchPoint", PatchPoint), ("PC", PC), ("Other", Other), ("Locals", Locals), @@ -22,7 +24,7 @@ mod bits { ("Allocator", Allocator), ("Empty", Empty), ]; - pub const NumEffectBits: u8 = 6; + pub const NumEffectBits: u8 = 7; } pub mod effect_types { pub type EffectBits = u8; @@ -38,6 +40,7 @@ pub mod abstract_heaps { pub const Memory: AbstractHeap = AbstractHeap::from_bits(bits::Memory); pub const Other: AbstractHeap = AbstractHeap::from_bits(bits::Other); pub const PC: AbstractHeap = AbstractHeap::from_bits(bits::PC); + pub const PatchPoint: AbstractHeap = AbstractHeap::from_bits(bits::PatchPoint); pub const Stack: AbstractHeap = AbstractHeap::from_bits(bits::Stack); } pub mod effects { @@ -51,5 +54,6 @@ pub mod effects { pub const Memory: Effect = Effect::promote(abstract_heaps::Memory); pub const Other: Effect = Effect::promote(abstract_heaps::Other); pub const PC: Effect = Effect::promote(abstract_heaps::PC); + pub const PatchPoint: Effect = Effect::promote(abstract_heaps::PatchPoint); pub const Stack: Effect = Effect::promote(abstract_heaps::Stack); } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 23ff9b16cf77e1..059964b8e8dd07 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -169,6 +169,7 @@ make_counters! { compile_hir_strength_reduce_time_ns, compile_hir_fold_constants_time_ns, compile_hir_clean_cfg_time_ns, + compile_hir_remove_redundant_patch_points_time_ns, compile_hir_eliminate_dead_code_time_ns, compile_lir_time_ns, } From fc16000e92766c3cdf863cddc460815303372a65 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 23 Feb 2026 18:28:05 +0900 Subject: [PATCH 11/12] CI: Highlight diff of depend files [ci skip] --- .github/workflows/check_dependencies.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index 1ba1b0388d6376..b84ecaa88b65a7 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -50,7 +50,7 @@ jobs: - run: make fix-depends - - run: git diff --no-ext-diff --ignore-submodules --exit-code + - run: git diff --color --no-ext-diff --ignore-submodules --exit-code - uses: ./.github/actions/slack with: From 70c625148892553c4dd50adefda955d45b46ff47 Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Tue, 24 Feb 2026 14:27:52 +0900 Subject: [PATCH 12/12] Use rev-parse HEAD instead of log --format=%H -1 --- defs/gmake.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/defs/gmake.mk b/defs/gmake.mk index 475ad61602d4aa..ea12729f148e92 100644 --- a/defs/gmake.mk +++ b/defs/gmake.mk @@ -276,7 +276,7 @@ pull-github: fetch-github $(call pull-github,$(PR)) define pull-github - $(eval GITHUB_MERGE_BASE := $(shell $(GIT_LOG_FORMAT)%H -1) + $(eval GITHUB_MERGE_BASE := $(shell $(GIT) rev-parse HEAD) $(eval GITHUB_MERGE_BRANCH := $(shell $(GIT_IN_SRC) symbolic-ref --short HEAD)) $(eval GITHUB_MERGE_WORKTREE := $(shell mktemp -d "$(srcdir)/gh-$(1)-XXXXXX")) $(GIT_IN_SRC) worktree prune @@ -434,7 +434,7 @@ ifneq ($(DOT_WAIT),) endif ifeq ($(HAVE_GIT),yes) -REVISION_LATEST := $(shell $(GIT_LOG_FORMAT)%H -1 2>/dev/null) +REVISION_LATEST := $(shell $(GIT) rev-parse HEAD) else REVISION_LATEST := update endif