From b21edc13236320b6dd46cee1cc8907bd9ac2fb38 Mon Sep 17 00:00:00 2001 From: Max Bernstein Date: Wed, 14 Jan 2026 16:58:10 -0500 Subject: [PATCH 01/22] ZJIT: Add assume_no_singleton_classes to avoid invalidation loops (#15871) Make sure we check if we have seen a singleton for this class before assuming we have not. Port the API from YJIT. --- zjit/src/cruby_methods.rs | 9 +- zjit/src/hir.rs | 97 ++++++--- zjit/src/hir/opt_tests.rs | 419 +++++++++++++++++++++----------------- zjit/src/hir/tests.rs | 4 +- zjit/src/invariants.rs | 31 ++- zjit/src/stats.rs | 3 + 6 files changed, 334 insertions(+), 229 deletions(-) diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index b8082024728acc..40b9d138542c87 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -857,6 +857,10 @@ fn inline_kernel_respond_to_p( } (_, _) => return None, // not public and include_all not known, can't compile }; + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + return None; + } fun.push_insn(block, hir::Insn::PatchPoint { invariant: hir::Invariant::NoTracePoint, state }); fun.push_insn(block, hir::Insn::PatchPoint { invariant: hir::Invariant::MethodRedefined { @@ -865,11 +869,6 @@ fn inline_kernel_respond_to_p( cme: target_cme }, state }); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, hir::Insn::PatchPoint { - invariant: hir::Invariant::NoSingletonClass { klass: recv_class }, state - }); - } Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(result) })) } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 7a7d03d75d5a18..cf0625cdad0806 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -7,7 +7,7 @@ #![allow(clippy::match_like_matches_macro)] use crate::{ backend::lir::C_ARG_OPNDS, - cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json + cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, invariants::has_singleton_class_of, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json }; use std::{ cell::RefCell, collections::{BTreeSet, HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter @@ -644,6 +644,9 @@ pub enum SendFallbackReason { ComplexArgPass, /// Caller has keyword arguments but callee doesn't expect them; need to convert to hash. UnexpectedKeywordArgs, + /// A singleton class has been seen for the receiver class, so we skip the optimization + /// to avoid an invalidation loop. + SingletonClassSeen, /// Initial fallback reason for every instruction, which should be mutated to /// a more actionable reason when an attempt to specialize the instruction fails. Uncategorized(ruby_vminsn_type), @@ -680,6 +683,7 @@ impl Display for SendFallbackReason { ArgcParamMismatch => write!(f, "Argument count does not match parameter count"), ComplexArgPass => write!(f, "Complex argument passing"), UnexpectedKeywordArgs => write!(f, "Unexpected Keyword Args"), + SingletonClassSeen => write!(f, "Singleton class previously created for receiver class"), Uncategorized(insn) => write!(f, "Uncategorized({})", insn_name(*insn as usize)), } } @@ -1891,6 +1895,24 @@ impl Function { } } + /// Assume that objects of a given class will have no singleton class. + /// Returns true if safe to assume so and emits a PatchPoint. + /// Returns false if we've already seen a singleton class for this class, + /// to avoid an invalidation loop. + pub fn assume_no_singleton_classes(&mut self, block: BlockId, klass: VALUE, state: InsnId) -> bool { + if !klass.instance_can_have_singleton_class() { + // This class can never have a singleton class, so no patchpoint needed. + return true; + } + if has_singleton_class_of(klass) { + // We've seen a singleton class for this klass. Disable the optimization + // to avoid an invalidation loop. + return false; + } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + true + } + /// Return a copy of the instruction where the instruction and its operands have been read from /// the union-find table (to find the current most-optimized version of this instruction). See /// [`UnionFind`] for more. @@ -2531,8 +2553,8 @@ impl Function { return false; } self.gen_patch_points_for_optimized_ccall(block, class, method_id, cme, state); - if class.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: class }, state }); + if !self.assume_no_singleton_classes(block, class, state) { + return false; } true } @@ -2702,10 +2724,12 @@ impl Function { if !can_direct_send(self, block, iseq, insn_id, args.as_slice()) { self.push_insn_id(block, insn_id); continue; } - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } @@ -2754,10 +2778,12 @@ impl Function { // TODO(alan): Turn this into a ractor belonging guard to work better in multi ractor mode. self.push_insn_id(block, insn_id); continue; } - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); @@ -2788,11 +2814,13 @@ impl Function { if self.is_metaclass(klass) && !self.assume_single_ractor_mode(block, state) { self.push_insn_id(block, insn_id); continue; } + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; + } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); - } if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } @@ -2835,10 +2863,12 @@ impl Function { // No (monomorphic/skewed polymorphic) profile info self.push_insn_id(block, insn_id); continue; }; - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); - if klass.instance_can_have_singleton_class() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !self.assume_no_singleton_classes(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + self.push_insn_id(block, insn_id); continue; } + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); if let Some(profiled_type) = profiled_type { recv = self.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); } @@ -3375,11 +3405,14 @@ impl Function { return Err(()); } + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); + } + // Commit to the replacement. Put PatchPoint. fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); - } if let Some(profiled_type) = profiled_type { // Guard receiver class @@ -3410,11 +3443,15 @@ impl Function { -1 => { // The method gets a pointer to the first argument // func(int argc, VALUE *argv, VALUE recv) - fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); } + + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); + if let Some(profiled_type) = profiled_type { // Guard receiver class recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); @@ -3522,11 +3559,14 @@ impl Function { return Err(()); } + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); + } + // Commit to the replacement. Put PatchPoint. fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); - } let props = ZJITState::get_method_annotations().get_cfunc_properties(cme); if props.is_none() && get_option!(stats) { @@ -3599,11 +3639,14 @@ impl Function { fun.set_dynamic_send_reason(send_insn_id, ComplexArgPass); return Err(()); } else { + // Check singleton class assumption first, before emitting other patchpoints + if !fun.assume_no_singleton_classes(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + return Err(()); + } + fun.gen_patch_points_for_optimized_ccall(block, recv_class, method_id, cme, state); - if recv_class.instance_can_have_singleton_class() { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_class }, state }); - } if let Some(profiled_type) = profiled_type { // Guard receiver class recv = fun.push_insn(block, Insn::GuardType { val: recv, guard_type: Type::from_profiled_type(profiled_type), state }); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 8d49353b30eaf4..2cf76e5e74868f 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -557,8 +557,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(CustomEq@0x1000, !=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(CustomEq@0x1000) + PatchPoint MethodRedefined(CustomEq@0x1000, !=@0x1008, cme:0x1010) v28:HeapObject[class_exact:CustomEq] = GuardType v9, HeapObject[class_exact:CustomEq] v29:BoolExact = CCallWithFrame v28, :BasicObject#!=@0x1038, v9 v20:NilClass = Const Value(nil) @@ -696,8 +696,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v19:BasicObject = SendWithoutBlockDirect v18, :foo (0x1038) CheckInterrupts @@ -724,8 +724,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[1] = Const Value(1) @@ -752,8 +752,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -790,8 +790,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:BasicObject = SendWithoutBlockDirect v20, :foo (0x1038), v11 CheckInterrupts @@ -819,8 +819,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) v23:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] v24:BasicObject = CCallWithFrame v23, :C#fun_new_map@0x1038, block=0x1040 v15:BasicObject = GetLocal :o, l0, EP@3 @@ -876,8 +876,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v19:BasicObject = SendWithoutBlockDirect v18, :foo (0x1038) CheckInterrupts @@ -904,8 +904,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:BasicObject = SendWithoutBlockDirect v20, :Integer (0x1038), v11 CheckInterrupts @@ -934,8 +934,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038), v11, v13 CheckInterrupts @@ -964,12 +964,12 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendWithoutBlockDirect v23, :foo (0x1038) - PatchPoint MethodRedefined(Object@0x1000, bar@0x1040, cme:0x1048) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, bar@0x1040, cme:0x1048) v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v28:BasicObject = SendWithoutBlockDirect v27, :bar (0x1038) CheckInterrupts @@ -994,8 +994,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v19:BasicObject = SendWithoutBlockDirect v18, :foo (0x1038) CheckInterrupts @@ -1021,8 +1021,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:BasicObject = SendWithoutBlockDirect v20, :foo (0x1038), v11 CheckInterrupts @@ -1049,8 +1049,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) v13:Fixnum[4] = Const Value(4) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038), v11, v13 CheckInterrupts @@ -1078,8 +1078,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v11 - PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Object@0x1008) + PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) v22:HeapObject[class_exact*:Object@VALUE(0x1008)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1008)] v23:BasicObject = CCallVariadic v22, :Kernel#puts@0x1040, v12 CheckInterrupts @@ -1603,8 +1603,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact v26:BasicObject = ArrayArefFixnum v25, v14 IncrCounter inline_cfunc_optimized_send_count @@ -1633,8 +1633,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) v25:HashExact = GuardType v9, HashExact v26:BasicObject = HashAref v25, v14 IncrCounter inline_cfunc_optimized_send_count @@ -2318,8 +2318,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v10 @@ -2347,8 +2347,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count PatchPoint NoEPEscape(test) v21:Fixnum[1] = Const Value(1) @@ -2382,8 +2382,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) + PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count v34:StringExact|NilClass = CCall v29, :Module#name@0x1048 PatchPoint NoEPEscape(test) @@ -2412,8 +2412,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count v17:Fixnum[5] = Const Value(5) CheckInterrupts @@ -2553,8 +2553,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count v17:Fixnum[5] = Const Value(5) CheckInterrupts @@ -2604,8 +2604,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BoolExact = IsBlockGiven IncrCounter inline_cfunc_optimized_send_count @@ -2630,8 +2630,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BoolExact = IsBlockGiven IncrCounter inline_cfunc_optimized_send_count @@ -2659,8 +2659,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_cfunc_optimized_send_count v15:Fixnum[5] = Const Value(5) @@ -2717,8 +2717,8 @@ mod hir_opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): v16:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v17:ArrayExact = ArrayDup v16 - PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) IncrCounter inline_iseq_optimized_send_count v31:BasicObject = InvokeBuiltin leaf , v17 CheckInterrupts @@ -2747,8 +2747,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) v20:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) + PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count v26:Class[Module@0x1010] = Const Value(VALUE(0x1010)) IncrCounter inline_cfunc_optimized_send_count @@ -2781,8 +2781,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v22:BasicObject = SendWithoutBlockDirect v21, :foo (0x1038) CheckInterrupts @@ -2919,8 +2919,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038), v11, v13 CheckInterrupts @@ -2949,8 +2949,8 @@ mod hir_opt_tests { v11:Fixnum[3] = Const Value(3) v13:Fixnum[1] = Const Value(1) v15:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v26:BasicObject = SendWithoutBlockDirect v24, :foo (0x1038), v13, v15, v11 CheckInterrupts @@ -2979,8 +2979,8 @@ mod hir_opt_tests { v11:Fixnum[0] = Const Value(0) v13:Fixnum[2] = Const Value(2) v15:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v26:BasicObject = SendWithoutBlockDirect v24, :foo (0x1038), v11, v15, v13 CheckInterrupts @@ -3036,16 +3036,16 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[3] = Const Value(3) v15:Fixnum[4] = Const Value(4) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v37:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v39:BasicObject = SendWithoutBlockDirect v37, :foo (0x1038), v11, v13, v15 v20:Fixnum[1] = Const Value(1) v22:Fixnum[2] = Const Value(2) v24:Fixnum[4] = Const Value(4) v26:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v42:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v44:BasicObject = SendWithoutBlockDirect v42, :foo (0x1038), v20, v22, v26, v24 v30:ArrayExact = NewArray v39, v44 @@ -3367,8 +3367,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) v46:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -3405,8 +3405,8 @@ mod hir_opt_tests { v16:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) v49:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) v52:BasicObject = SendWithoutBlockDirect v49, :initialize (0x1068), v16 CheckInterrupts CheckInterrupts @@ -3436,8 +3436,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(Object@0x1008, new@0x1009, cme:0x1010) v46:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) - PatchPoint MethodRedefined(Object@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(Object@0x1008) + PatchPoint MethodRedefined(Object@0x1008, initialize@0x1038, cme:0x1040) v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -3468,8 +3468,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1009, cme:0x1010) v46:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) - PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(BasicObject@0x1008) + PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1038, cme:0x1040) v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -3532,8 +3532,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) v16:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010) - PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) v57:BasicObject = CCallVariadic v46, :Array.new@0x1040, v16 CheckInterrupts Return v57 @@ -3562,8 +3562,8 @@ mod hir_opt_tests { v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(Set@0x1008, new@0x1009, cme:0x1010) v18:HeapBasicObject = ObjectAlloc v43 - PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) PatchPoint NoSingletonClass(Set@0x1008) + PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) v49:SetExact = GuardType v18, SetExact v50:BasicObject = CCallVariadic v49, :Set#initialize@0x1068 CheckInterrupts @@ -3593,8 +3593,8 @@ mod hir_opt_tests { v43:Class[String@0x1008] = Const Value(VALUE(0x1008)) v13:NilClass = Const Value(nil) PatchPoint MethodRedefined(String@0x1008, new@0x1009, cme:0x1010) - PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) v54:BasicObject = CCallVariadic v43, :String.new@0x1040 CheckInterrupts Return v54 @@ -3625,8 +3625,8 @@ mod hir_opt_tests { v17:StringExact = StringCopy v16 PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) v50:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) - PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) PatchPoint NoSingletonClass(Regexp@0x1008) + PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) v54:BasicObject = CCallVariadic v50, :Regexp#initialize@0x1078, v17 CheckInterrupts CheckInterrupts @@ -3652,8 +3652,8 @@ mod hir_opt_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) v29:CInt64 = ArrayLength v18 v30:Fixnum = BoxFixnum v29 IncrCounter inline_cfunc_optimized_send_count @@ -3680,8 +3680,8 @@ mod hir_opt_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) v29:CInt64 = ArrayLength v18 v30:Fixnum = BoxFixnum v29 IncrCounter inline_cfunc_optimized_send_count @@ -4220,8 +4220,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:HashExact = NewHash - PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4313,8 +4313,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) v22:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = SendWithoutBlock v22, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4407,8 +4407,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = SendWithoutBlock v23, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4502,8 +4502,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v23:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = SendWithoutBlock v23, :-@ # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts @@ -4643,8 +4643,8 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v26:ArrayExact = GuardType v9, ArrayExact - PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) v31:BasicObject = CCallWithFrame v26, :Array#to_s@0x1040 v19:String = AnyToString v9, str: v31 v21:StringExact = StringConcat v13, v19 @@ -4732,8 +4732,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, S) v23:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v13:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Array@0x1010) + PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) v27:BasicObject = ArrayArefFixnum v23, v13 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -4761,8 +4761,8 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v26:Fixnum[5] = Const Value(5) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -4788,8 +4788,8 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[-3] = Const Value(-3) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v26:Fixnum[4] = Const Value(4) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -4815,8 +4815,8 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[-10] = Const Value(-10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v26:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -4842,8 +4842,8 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v26:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -4872,8 +4872,8 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v23:BasicObject = SendWithoutBlockDirect v11, :[] (0x1040), v13 CheckInterrupts Return v23 @@ -4930,8 +4930,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:ArrayExact = ArrayDup v10 - PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) v20:BasicObject = SendWithoutBlockDirect v11, :max (0x1040) CheckInterrupts Return v20 @@ -5007,14 +5007,14 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode - PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) v22:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v30:StaticSymbol[:b] = Const Value(VALUE(0x1038)) PatchPoint SingleRactorMode - PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -5094,8 +5094,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Foo) v22:Class[Foo@0x1008] = Const Value(VALUE(0x1008)) v13:Fixnum[100] = Const Value(100) - PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count CheckInterrupts Return v13 @@ -5384,8 +5384,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact v24:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -5412,8 +5412,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -5534,8 +5534,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:CInt64 = ArrayLength v23 v25:CInt64[0] = Const CInt64(0) @@ -5565,8 +5565,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) v23:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count v25:BoolExact = CCall v23, :Hash#empty?@0x1038 @@ -5595,8 +5595,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) v27:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v28:CBool = IsBitEqual v27, v12 v29:BoolExact = BoxBool v28 @@ -5683,8 +5683,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:NilClass = Const Value(nil) @@ -5719,8 +5719,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -5759,8 +5759,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -5986,8 +5986,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter getivar_fallback_too_complex v22:BasicObject = GetIvar v21, :@foo @@ -6014,8 +6014,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:ArrayExact = ArrayDup v10 - PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) v21:BasicObject = CCallWithFrame v11, :Array#map@0x1040, block=0x1048 CheckInterrupts Return v21 @@ -6056,8 +6056,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1010, B) v39:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) - PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) PatchPoint NoSingletonClass(Array@0x1020) + PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) v43:BasicObject = CCallVariadic v36, :zip@0x1058, v39 v25:BasicObject = GetLocal :result, l0, EP@3 v29:BasicObject = GetLocal :result, l0, EP@3 @@ -6144,8 +6144,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(C@0x1010) + PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) v25:CShape = LoadField v20, :_shape_id@0x1048 v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) v27:NilClass = Const Value(nil) @@ -6179,8 +6179,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(C@0x1010) + PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) v25:CShape = LoadField v20, :_shape_id@0x1048 v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) v27:NilClass = Const Value(nil) @@ -6211,8 +6211,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -6244,8 +6244,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -6346,8 +6346,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v22:BasicObject = LoadField v21, :foo@0x1038 CheckInterrupts @@ -6374,8 +6374,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v22:CPtr = LoadField v21, :_as_heap@0x1038 v23:BasicObject = LoadField v22, :foo@0x1039 @@ -6406,8 +6406,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v18:Fixnum[5] = Const Value(5) CheckInterrupts @@ -6436,8 +6436,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v29:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v30:HeapObject[class_exact:C] = GuardNotFrozen v29 StoreField v30, :foo=@0x1038, v12 @@ -6468,8 +6468,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v29:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v30:HeapObject[class_exact:C] = GuardNotFrozen v29 v31:CPtr = LoadField v30, :_as_heap@0x1038 @@ -6496,8 +6496,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) v20:ArrayExact = CCallWithFrame v10, :Array#reverse@0x1038 CheckInterrupts Return v20 @@ -6523,8 +6523,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) v16:Fixnum[5] = Const Value(5) CheckInterrupts Return v16 @@ -6549,8 +6549,8 @@ mod hir_opt_tests { v10:ArrayExact = NewArray v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:StringExact = StringCopy v12 - PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) v23:StringExact = CCallVariadic v10, :Array#join@0x1040, v13 CheckInterrupts Return v23 @@ -6574,8 +6574,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v11 @@ -6599,8 +6599,8 @@ mod hir_opt_tests { bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts Return v11 @@ -6624,8 +6624,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, to_s@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, to_s@0x1008, cme:0x1010) v22:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6733,8 +6733,8 @@ mod hir_opt_tests { v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v14:ArrayExact = ArrayDup v13 v19:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v30:BasicObject = ArrayArefFixnum v14, v19 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6762,8 +6762,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v27:ArrayExact = GuardType v11, ArrayExact v28:Fixnum = GuardType v12, Fixnum v29:BasicObject = ArrayArefFixnum v27, v28 @@ -6794,8 +6794,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) v27:ArraySubclass[class_exact:C] = GuardType v11, ArraySubclass[class_exact:C] v28:Fixnum = GuardType v12, Fixnum v29:BasicObject = ArrayArefFixnum v27, v28 @@ -6828,8 +6828,8 @@ mod hir_opt_tests { v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v14:HashExact = HashDup v13 v19:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) PatchPoint NoSingletonClass(Hash@0x1008) + PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) v30:BasicObject = HashAref v14, v19 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6857,8 +6857,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) v27:HashExact = GuardType v11, HashExact v28:BasicObject = HashAref v27, v12 IncrCounter inline_cfunc_optimized_send_count @@ -6888,8 +6888,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) v27:HashSubclass[class_exact:C] = GuardType v11, HashSubclass[class_exact:C] v28:BasicObject = CCallWithFrame v27, :Hash#[]@0x1038, v12 CheckInterrupts @@ -6918,8 +6918,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, H) v23:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) v13:StaticSymbol[:a] = Const Value(VALUE(0x1010)) - PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) PatchPoint NoSingletonClass(Hash@0x1018) + PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) v27:BasicObject = HashAref v23, v13 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6951,8 +6951,8 @@ mod hir_opt_tests { PatchPoint NoEPEscape(test) v22:Fixnum[1] = Const Value(1) v24:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) HashAset v13, v22, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -6981,8 +6981,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) v35:HashExact = GuardType v13, HashExact HashAset v35, v14, v15 IncrCounter inline_cfunc_optimized_send_count @@ -7013,8 +7013,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(C@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, []=@0x1008, cme:0x1010) v35:HashSubclass[class_exact:C] = GuardType v13, HashSubclass[class_exact:C] v36:BasicObject = CCallWithFrame v35, :Hash#[]=@0x1038, v14, v15 CheckInterrupts @@ -7041,8 +7041,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Thread) v20:Class[Thread@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) v24:CPtr = LoadEC v25:CPtr = LoadField v24, :thread_ptr@0x1048 v26:BasicObject = LoadField v25, :self@0x1049 @@ -7073,8 +7073,8 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v16:Fixnum[1] = Const Value(1) v18:Fixnum[10] = Const Value(10) - PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) v31:ArrayExact = GuardType v9, ArrayExact v32:ArrayExact = GuardNotFrozen v31 v33:ArrayExact = GuardNotShared v32 @@ -7112,8 +7112,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) v35:ArrayExact = GuardType v13, ArrayExact v36:Fixnum = GuardType v14, Fixnum v37:ArrayExact = GuardNotFrozen v35 @@ -7154,8 +7154,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(MyArray@0x1000, []=@0x1008, cme:0x1010) PatchPoint NoSingletonClass(MyArray@0x1000) + PatchPoint MethodRedefined(MyArray@0x1000, []=@0x1008, cme:0x1010) v35:ArraySubclass[class_exact:MyArray] = GuardType v13, ArraySubclass[class_exact:MyArray] v36:BasicObject = CCallVariadic v35, :Array#[]=@0x1038, v14, v15 CheckInterrupts @@ -7183,8 +7183,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact ArrayPush v25, v14 IncrCounter inline_cfunc_optimized_send_count @@ -7213,8 +7213,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) v24:ArrayExact = GuardType v9, ArrayExact ArrayPush v24, v14 IncrCounter inline_cfunc_optimized_send_count @@ -7245,8 +7245,8 @@ mod hir_opt_tests { v14:Fixnum[1] = Const Value(1) v16:Fixnum[2] = Const Value(2) v18:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) v28:ArrayExact = GuardType v9, ArrayExact v29:BasicObject = CCallVariadic v28, :Array#push@0x1038, v14, v16, v18 CheckInterrupts @@ -7272,8 +7272,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:CInt64 = ArrayLength v23 v25:Fixnum = BoxFixnum v24 @@ -7301,8 +7301,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:CInt64 = ArrayLength v23 v25:Fixnum = BoxFixnum v24 @@ -7331,8 +7331,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) v25:StringExact = GuardType v9, StringExact v26:BasicObject = CCallWithFrame v25, :String#=~@0x1040, v14 CheckInterrupts @@ -7358,8 +7358,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) v26:StringExact = GuardType v11, StringExact v27:Fixnum = GuardType v12, Fixnum v28:CInt64 = UnboxFixnum v27 @@ -7395,8 +7395,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) v30:StringExact = GuardType v11, StringExact v31:Fixnum = GuardType v12, Fixnum v32:CInt64 = UnboxFixnum v31 @@ -7432,8 +7432,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) v30:StringExact = GuardType v13, StringExact v31:Fixnum = GuardType v14, Fixnum v32:Fixnum = GuardType v15, Fixnum @@ -7473,8 +7473,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(MyString@0x1000, setbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(MyString@0x1000) + PatchPoint MethodRedefined(MyString@0x1000, setbyte@0x1008, cme:0x1010) v30:StringSubclass[class_exact:MyString] = GuardType v13, StringSubclass[class_exact:MyString] v31:Fixnum = GuardType v14, Fixnum v32:Fixnum = GuardType v15, Fixnum @@ -7512,8 +7512,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) v30:StringExact = GuardType v13, StringExact v31:BasicObject = CCallWithFrame v30, :String#setbyte@0x1038, v14, v15 CheckInterrupts @@ -7540,8 +7540,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact v24:CInt64 = LoadField v23, :len@0x1038 v25:CInt64[0] = Const CInt64(0) @@ -7573,8 +7573,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) v27:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[4] = Const Value(4) @@ -7869,8 +7869,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:StringExact = StringAppend v27, v28 @@ -7898,8 +7898,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:Fixnum = GuardType v12, Fixnum v29:StringExact = StringAppendCodepoint v27, v28 @@ -7929,8 +7929,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:StringExact = StringAppend v27, v28 @@ -7960,8 +7960,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) PatchPoint NoSingletonClass(MyString@0x1000) + PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) v27:StringSubclass[class_exact:MyString] = GuardType v11, StringSubclass[class_exact:MyString] v28:BasicObject = CCallWithFrame v27, :String#<<@0x1038, v12 CheckInterrupts @@ -7987,8 +7987,8 @@ mod hir_opt_tests { v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 v13:StaticSymbol[:a] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, <<@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, <<@0x1018, cme:0x1020) v24:BasicObject = CCallWithFrame v11, :String#<<@0x1048, v13 CheckInterrupts Return v24 @@ -8038,8 +8038,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ascii_only?@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ascii_only?@0x1008, cme:0x1010) v22:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v24:BoolExact = CCall v22, :String#ascii_only?@0x1038 @@ -8265,8 +8265,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) v23:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count v25:Fixnum = CCall v23, :Hash#size@0x1038 @@ -8295,8 +8295,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) v27:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[5] = Const Value(5) @@ -8326,11 +8326,11 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v28:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8358,12 +8358,12 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8393,11 +8393,11 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v28:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8428,11 +8428,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:FalseClass = Const Value(false) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8463,11 +8463,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:NilClass = Const Value(nil) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8498,11 +8498,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:TrueClass = Const Value(true) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8532,11 +8532,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:Fixnum[4] = Const Value(4) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8566,11 +8566,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:NilClass = Const Value(nil) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v30:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8598,12 +8598,12 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) v30:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -8634,8 +8634,8 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) PatchPoint NoSingletonClass(C@0x1008) + PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v25:BasicObject = CCallVariadic v24, :Kernel#respond_to?@0x1040, v14 CheckInterrupts @@ -8660,8 +8660,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -8687,8 +8687,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:StringExact[VALUE(0x1038)] = Const Value(VALUE(0x1038)) @@ -8714,8 +8714,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:NilClass = Const Value(nil) @@ -8741,8 +8741,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:TrueClass = Const Value(true) @@ -8768,8 +8768,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:FalseClass = Const Value(false) @@ -8795,8 +8795,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[0] = Const Value(0) @@ -8822,8 +8822,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[1] = Const Value(1) @@ -8850,8 +8850,8 @@ mod hir_opt_tests { Jump bb2(v4) bb2(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -8879,8 +8879,8 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) v15:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count CheckInterrupts @@ -9001,8 +9001,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:BoolExact = CCall v27, :String#==@0x1038, v28 @@ -9032,8 +9032,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) v27:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v28:String = GuardType v12, String v29:BoolExact = CCall v27, :String#==@0x1038, v28 @@ -9063,8 +9063,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String v29:BoolExact = CCall v27, :String#==@0x1038, v28 @@ -9092,8 +9092,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) v26:StringExact = GuardType v11, StringExact v27:String = GuardType v12, String v28:BoolExact = CCall v26, :String#==@0x1038, v27 @@ -9123,8 +9123,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(C@0x1000, ===@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, ===@0x1008, cme:0x1010) v26:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v27:String = GuardType v12, String v28:BoolExact = CCall v26, :String#==@0x1038, v27 @@ -9154,8 +9154,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) v26:StringExact = GuardType v11, StringExact v27:String = GuardType v12, String v28:BoolExact = CCall v26, :String#==@0x1038, v27 @@ -9184,8 +9184,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v25:Fixnum = CCall v23, :String#size@0x1038 @@ -9214,8 +9214,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) v27:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[5] = Const Value(5) @@ -9243,8 +9243,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) v22:StringExact = GuardType v9, StringExact v23:CInt64 = LoadField v22, :len@0x1038 v24:Fixnum = BoxFixnum v23 @@ -9274,8 +9274,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) v26:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v18:Fixnum[5] = Const Value(5) @@ -9303,8 +9303,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v25:Fixnum = CCall v23, :String#length@0x1038 @@ -9334,8 +9334,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, String) v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) v30:BoolExact = IsA v9, v26 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -9364,8 +9364,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Kernel) v26:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint MethodRedefined(Module@0x1010, ===@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Module@0x1010) + PatchPoint MethodRedefined(Module@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count v31:BoolExact = CCall v26, :Module#===@0x1048, v9 CheckInterrupts @@ -9393,8 +9393,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1008, is_a?@0x1009, cme:0x1010) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, is_a?@0x1009, cme:0x1010) v28:StringExact = GuardType v9, StringExact v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count @@ -9423,8 +9423,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) v28:StringExact = GuardType v9, StringExact v29:BasicObject = CCallWithFrame v28, :Kernel#is_a?@0x1048, v24 CheckInterrupts @@ -9455,8 +9455,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v21:Fixnum[5] = Const Value(5) @@ -9489,8 +9489,8 @@ mod hir_opt_tests { PatchPoint StableConstantNames(0x1000, Integer) v30:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count v23:Fixnum[5] = Const Value(5) CheckInterrupts @@ -9518,8 +9518,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1008, kind_of?@0x1009, cme:0x1010) PatchPoint NoSingletonClass(String@0x1008) + PatchPoint MethodRedefined(String@0x1008, kind_of?@0x1009, cme:0x1010) v28:StringExact = GuardType v9, StringExact v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count @@ -9548,8 +9548,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) v28:StringExact = GuardType v9, StringExact v29:BasicObject = CCallWithFrame v28, :Kernel#kind_of?@0x1048, v24 CheckInterrupts @@ -9580,8 +9580,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v21:Fixnum[5] = Const Value(5) @@ -9662,8 +9662,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) PatchPoint NoSingletonClass(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) v27:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count v19:Fixnum[4] = Const Value(4) @@ -9702,18 +9702,18 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v40:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v44:Class[C@0x1000] = Const Value(VALUE(0x1000)) IncrCounter inline_cfunc_optimized_send_count v13:StaticSymbol[:_lex_actions] = Const Value(VALUE(0x1038)) 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) - PatchPoint NoSingletonClass(Class@0x1040) v52:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -9741,14 +9741,14 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v23:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v27:Class[C@0x1000] = Const Value(VALUE(0x1000)) IncrCounter inline_cfunc_optimized_send_count - PatchPoint MethodRedefined(Class@0x1038, name@0x1040, cme:0x1048) PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint MethodRedefined(Class@0x1038, name@0x1040, cme:0x1048) IncrCounter inline_cfunc_optimized_send_count v33:StringExact|NilClass = CCall v27, :Module#name@0x1070 CheckInterrupts @@ -9774,8 +9774,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v25:Class[C@0x1000] = Const Value(VALUE(0x1000)) @@ -9827,8 +9827,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) v18:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v22:Class[Object@0x1038] = Const Value(VALUE(0x1038)) @@ -9883,8 +9883,8 @@ mod hir_opt_tests { v59:CShape[0x1003] = Const CShape(0x1003) StoreField v54, :_shape_id@0x1000, v59 v43:Class[VMFrozenCore] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) v64:BasicObject = CCallWithFrame v43, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 v46:BasicObject = GetLocal :a, l0, EP@6 v47:BasicObject = GetLocal :_b, l0, EP@5 @@ -9925,8 +9925,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_OBJ) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozen@0x1010) + PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) v29:Fixnum[1] = Const Value(1) CheckInterrupts Return v29 @@ -9965,8 +9965,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MULTI_FROZEN) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestMultiIvars@0x1010) + PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) v29:Fixnum[20] = Const Value(20) CheckInterrupts Return v29 @@ -10003,8 +10003,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_STR) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenStr@0x1010) + PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) v29:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) CheckInterrupts Return v29 @@ -10041,8 +10041,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_NIL) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenNil@0x1010) + PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) v29:NilClass = Const Value(nil) CheckInterrupts Return v29 @@ -10079,8 +10079,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, UNFROZEN_OBJ) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestUnfrozen@0x1010) + PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) v25:CShape = LoadField v20, :_shape_id@0x1048 v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) v27:BasicObject = LoadField v20, :@a@0x104a @@ -10119,8 +10119,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_READER) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestAttrReader@0x1010) + PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) v29:Fixnum[42] = Const Value(42) CheckInterrupts Return v29 @@ -10157,8 +10157,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_SYM) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenSym@0x1010) + PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) v29:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) CheckInterrupts Return v29 @@ -10195,8 +10195,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_TRUE) v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestFrozenBool@0x1010) + PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) v29:TrueClass = Const Value(true) CheckInterrupts Return v29 @@ -10230,8 +10230,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): - PatchPoint MethodRedefined(TestDynamic@0x1000, val@0x1008, cme:0x1010) PatchPoint NoSingletonClass(TestDynamic@0x1000) + PatchPoint MethodRedefined(TestDynamic@0x1000, val@0x1008, cme:0x1010) v21:HeapObject[class_exact:TestDynamic] = GuardType v9, HeapObject[class_exact:TestDynamic] v24:CShape = LoadField v21, :_shape_id@0x1038 v25:CShape[0x1039] = GuardBitEquals v24, CShape(0x1039) @@ -10272,14 +10272,14 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) v28:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) v53:Fixnum[100] = Const Value(100) PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1048, NESTED_FROZEN) v34:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) v55:Fixnum[200] = Const Value(200) PatchPoint MethodRedefined(Integer@0x1080, +@0x1088, cme:0x1090) v56:Fixnum[300] = Const Value(300) @@ -10309,8 +10309,8 @@ mod hir_opt_tests { PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, S) v20:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) PatchPoint NoSingletonClass(String@0x1010) + PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) v24:CInt64 = LoadField v20, :len@0x1048 v25:Fixnum = BoxFixnum v24 IncrCounter inline_cfunc_optimized_send_count @@ -10338,8 +10338,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) v18:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[42] = Const Value(42) @@ -10395,8 +10395,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(BasicObject@0x1000, initialize@0x1008, cme:0x1010) PatchPoint NoSingletonClass(BasicObject@0x1000) + PatchPoint MethodRedefined(BasicObject@0x1000, initialize@0x1008, cme:0x1010) v20:BasicObjectExact = GuardType v6, BasicObjectExact v21:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count @@ -10477,8 +10477,8 @@ mod hir_opt_tests { EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): - PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) PatchPoint NoSingletonClass(C@0x1000) + PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) v18:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v21:Fixnum[42] = Const Value(42) @@ -10515,4 +10515,43 @@ mod hir_opt_tests { Return v13 "); } + + // Test that when a singleton class has been seen for a class, we skip the + // NoSingletonClass optimization to avoid an invalidation loop. + #[test] + fn test_skip_optimization_after_singleton_class_seen() { + // First, trigger the singleton class callback for String by creating a singleton class. + // This should mark String as having had a singleton class seen. + eval(r#" + "hello".singleton_class + "#); + + // Now define and compile a method that would normally be optimized with NoSingletonClass. + // Since String has had a singleton class, the optimization should be skipped and we + // should fall back to SendWithoutBlock. + eval(r#" + def test(s) + s.length + end + test("asdf") + "#); + + // The output should NOT have NoSingletonClass patchpoint for String, and should + // fall back to SendWithoutBlock instead of the optimized CCall path. + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :s, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v15:BasicObject = SendWithoutBlock v9, :length # SendFallbackReason: Singleton class previously created for receiver class + CheckInterrupts + Return v15 + "); + } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 2ae31e862dc0ef..14a77776233ce5 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -76,8 +76,8 @@ mod snapshot_tests { v13:Fixnum[1] = Const Value(1) v15:Fixnum[2] = Const Value(2) v16:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13, v15], locals: [] } - PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Object@0x1010) + PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) v24:HeapObject[class_exact*:Object@VALUE(0x1010)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1010)] v25:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v13, v15, v11], locals: [] } v26:BasicObject = SendWithoutBlockDirect v24, :foo (0x1048), v13, v15, v11 @@ -111,8 +111,8 @@ mod snapshot_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) v14:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13], locals: [] } - PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) PatchPoint NoSingletonClass(Object@0x1010) + PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) v22:HeapObject[class_exact*:Object@VALUE(0x1010)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1010)] v23:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13], locals: [] } v24:BasicObject = SendWithoutBlockDirect v22, :foo (0x1048), v11, v13 diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 749e0a9e1d38d9..d183eb18abccb8 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -436,6 +436,16 @@ pub extern "C" fn rb_zjit_tracing_invalidate_all() { }); } +/// Returns true if we've seen a singleton class of a given class since boot. +/// This is used to avoid an invalidation loop where we repeatedly compile code +/// that assumes no singleton class, only to have it invalidated. +pub fn has_singleton_class_of(klass: VALUE) -> bool { + ZJITState::get_invariants() + .no_singleton_class_patch_points + .get(&klass) + .map_or(false, |patch_points| patch_points.is_empty()) +} + #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_invalidate_no_singleton_class(klass: VALUE) { if !zjit_enabled_p() { @@ -444,11 +454,22 @@ pub extern "C" fn rb_zjit_invalidate_no_singleton_class(klass: VALUE) { with_vm_lock(src_loc!(), || { let invariants = ZJITState::get_invariants(); - if let Some(patch_points) = invariants.no_singleton_class_patch_points.remove(&klass) { - let cb = ZJITState::get_code_block(); - debug!("Singleton class created for {:?}", klass); - compile_patch_points!(cb, patch_points, "Singleton class created for {:?}", klass); - cb.mark_all_executable(); + match invariants.no_singleton_class_patch_points.get_mut(&klass) { + Some(patch_points) => { + // Invalidate existing patch points and let has_singleton_class_of() + // return true when they are compiled again + let patch_points = mem::take(patch_points); + if !patch_points.is_empty() { + let cb = ZJITState::get_code_block(); + debug!("Singleton class created for {:?}", klass); + compile_patch_points!(cb, patch_points, "Singleton class created for {:?}", klass); + cb.mark_all_executable(); + } + } + None => { + // Let has_singleton_class_of() return true for this class + invariants.no_singleton_class_patch_points.insert(klass, HashSet::new()); + } } }); } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 01bd6e2f597ec8..089037dcd796f6 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -241,6 +241,8 @@ make_counters! { send_fallback_one_or_more_complex_arg_pass, // Caller has keyword arguments but callee doesn't expect them. send_fallback_unexpected_keyword_args, + // Singleton class previously created for receiver class. + send_fallback_singleton_class_seen, send_fallback_bmethod_non_iseq_proc, send_fallback_obj_to_string_not_string, send_fallback_send_cfunc_variadic, @@ -573,6 +575,7 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter SendCfuncArrayVariadic => send_fallback_send_cfunc_array_variadic, ComplexArgPass => send_fallback_one_or_more_complex_arg_pass, UnexpectedKeywordArgs => send_fallback_unexpected_keyword_args, + SingletonClassSeen => send_fallback_singleton_class_seen, ArgcParamMismatch => send_fallback_argc_param_mismatch, BmethodNonIseqProc => send_fallback_bmethod_non_iseq_proc, SendNotOptimizedMethodType(_) => send_fallback_send_not_optimized_method_type, From 068713964a3949c9221da291e5e5f5167e692239 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 13 Jan 2026 17:59:59 -0500 Subject: [PATCH 02/22] [DOC] Add docs for WeakMap#inspect --- weakmap.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/weakmap.c b/weakmap.c index b027604f5e83fe..80ef29b4cce8bc 100644 --- a/weakmap.c +++ b/weakmap.c @@ -207,6 +207,17 @@ wmap_inspect_i(st_data_t k, st_data_t v, st_data_t data) return ST_CONTINUE; } +/* call-seq: + * inspect -> new_string + * + * Returns a new string containing the \WeakMap entries: + * + * m = ObjectSpace::WeakMap.new + * m["one"] = 1 + * m["two"] = 2 + * m.inspect + * # => "# => 1, # => 2>" + */ static VALUE wmap_inspect(VALUE self) { From ac229890660d023c423869601d1ab404187824f4 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 8 Jan 2026 22:42:38 -0500 Subject: [PATCH 03/22] YJIT: Add crashing test for --yjit-dump-insns Co-authored-by: Nobuyoshi Nakada --- test/ruby/test_yjit.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index 53b056d3359c85..d6b9b75648e744 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -1804,6 +1804,18 @@ def bar(w:, x:, y:, z:, **kwrest) = yield kwrest RUBY end + def test_yjit_dump_insns + # Testing that this undocumented debugging feature doesn't crash + args = [ + '--yjit-call-threshold=1', + '--yjit-dump-insns', + '-e def foo(case:) = {case:}[:case]', + '-e foo(case:0)', + ] + _out, _err, status = invoke_ruby(args, '', true, true) + assert_not_predicate(status, :signaled?) + end + private def code_gc_helpers From 993046847931c6c26ca518dfaf9be6c23c34fc78 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 13 Jan 2026 23:08:33 -0500 Subject: [PATCH 04/22] YJIT: Fix --yjit-dump-insns by removing {cpush,cpop}_all() in printers cpush_all() and cpop_all() in theory enabled these `print_*` utilities to work in more spots, but with automatically spilling in asm.ccall(), the benefits are now limited. They also have a bug at the moment. Stop using them to dodge the bug. --- yjit/src/utils.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/yjit/src/utils.rs b/yjit/src/utils.rs index 43fd90142f6e5e..251628fabfae69 100644 --- a/yjit/src/utils.rs +++ b/yjit/src/utils.rs @@ -160,8 +160,6 @@ pub fn print_int(asm: &mut Assembler, opnd: Opnd) { } } - asm.cpush_all(); - let argument = match opnd { Opnd::Mem(_) | Opnd::Reg(_) | Opnd::InsnOut { .. } => { // Sign-extend the value if necessary @@ -176,7 +174,6 @@ pub fn print_int(asm: &mut Assembler, opnd: Opnd) { }; asm.ccall(print_int_fn as *const u8, vec![argument]); - asm.cpop_all(); } /// Generate code to print a pointer @@ -189,9 +186,7 @@ pub fn print_ptr(asm: &mut Assembler, opnd: Opnd) { assert!(opnd.rm_num_bits() == 64); - asm.cpush_all(); asm.ccall(print_ptr_fn as *const u8, vec![opnd]); - asm.cpop_all(); } /// Generate code to print a value @@ -204,9 +199,7 @@ pub fn print_value(asm: &mut Assembler, opnd: Opnd) { assert!(matches!(opnd, Opnd::Value(_))); - asm.cpush_all(); asm.ccall(print_value_fn as *const u8, vec![opnd]); - asm.cpop_all(); } /// Generate code to print constant string to stdout @@ -221,7 +214,6 @@ pub fn print_str(asm: &mut Assembler, str: &str) { } } - asm.cpush_all(); let string_data = asm.new_label("string_data"); let after_string = asm.new_label("after_string"); @@ -233,8 +225,6 @@ pub fn print_str(asm: &mut Assembler, str: &str) { let opnd = asm.lea_jump_target(string_data); asm.ccall(print_str_cfun as *const u8, vec![opnd, Opnd::UImm(str.len() as u64)]); - - asm.cpop_all(); } pub fn stdout_supports_colors() -> bool { From 9f3225d0c5c64c4aac9fd196061f8cfd3f25f271 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 13 Jan 2026 23:01:47 -0500 Subject: [PATCH 05/22] YJIT: Properly preserve register mapping in cpush_all() and cpop_all() Previously, cpop_all() did not in fact restore the register mapping state since it was effectively doing a no-op `self.ctx.set_reg_mapping(self.ctx.get_reg_mapping())`. This desync in bookkeeping led to issues with the --yjit-dump-insns option because print_str() used to use cpush_all() and cpop_all(). --- yjit/src/backend/arm64/mod.rs | 2 +- yjit/src/backend/ir.rs | 8 +++++--- yjit/src/codegen.rs | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index 6fa8fef627df0c..a89d47f75ff265 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -1419,7 +1419,7 @@ mod tests { fn test_emit_cpop_all() { let (mut asm, mut cb) = setup_asm(); - asm.cpop_all(); + asm.cpop_all(crate::core::RegMapping::default()); asm.compile_with_num_regs(&mut cb, 0); } diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 8205d6de76bd2f..3fb67bc7cc1584 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -1824,12 +1824,12 @@ impl Assembler { out } - pub fn cpop_all(&mut self) { + pub fn cpop_all(&mut self, reg_mapping: RegMapping) { self.push_insn(Insn::CPopAll); // Re-enable ccall's RegMappings assertion disabled by cpush_all. // cpush_all + cpop_all preserve all stack temp registers, so it's safe. - self.set_reg_mapping(self.ctx.get_reg_mapping()); + self.set_reg_mapping(reg_mapping); } pub fn cpop_into(&mut self, opnd: Opnd) { @@ -1840,14 +1840,16 @@ impl Assembler { self.push_insn(Insn::CPush(opnd)); } - pub fn cpush_all(&mut self) { + pub fn cpush_all(&mut self) -> RegMapping { self.push_insn(Insn::CPushAll); // Mark all temps as not being in registers. // Temps will be marked back as being in registers by cpop_all. // We assume that cpush_all + cpop_all are used for C functions in utils.rs // that don't require spill_regs for GC. + let mapping = self.ctx.get_reg_mapping(); self.set_reg_mapping(RegMapping::default()); + mapping } pub fn cret(&mut self, opnd: Opnd) { diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 1cf8247a2a905a..0fbca85716dff0 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -8314,7 +8314,7 @@ fn gen_send_iseq( // We also do this after spill_regs() to avoid doubly spilling the same thing on asm.ccall(). if get_option!(gen_stats) { // Protect caller-saved registers in case they're used for arguments - asm.cpush_all(); + let mapping = asm.cpush_all(); // Assemble the ISEQ name string let name_str = get_iseq_name(iseq); @@ -8324,7 +8324,7 @@ fn gen_send_iseq( // Increment the counter for this cfunc asm.ccall(incr_iseq_counter as *const u8, vec![iseq_idx.into()]); - asm.cpop_all(); + asm.cpop_all(mapping); } // The callee might change locals through Kernel#binding and other means. From cdb2b0eed50e1c837adeb85ef8978e533f056327 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Tue, 13 Jan 2026 23:15:47 -0500 Subject: [PATCH 06/22] YJIT: A64: In CPopAll, pop into the register before using MSR Or else we put garbage into the flags. --- yjit/src/backend/arm64/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yjit/src/backend/arm64/mod.rs b/yjit/src/backend/arm64/mod.rs index a89d47f75ff265..4486b46e363efa 100644 --- a/yjit/src/backend/arm64/mod.rs +++ b/yjit/src/backend/arm64/mod.rs @@ -1155,8 +1155,8 @@ impl Assembler let regs = Assembler::get_caller_save_regs(); // Pop the state/flags register - msr(cb, SystemRegister::NZCV, Self::SCRATCH0); emit_pop(cb, Self::SCRATCH0); + msr(cb, SystemRegister::NZCV, Self::SCRATCH0); for reg in regs.into_iter().rev() { emit_pop(cb, A64Opnd::Reg(reg)); From 4a21b83693fdc0e976da209047ba286b2f4084e5 Mon Sep 17 00:00:00 2001 From: Kevin Menard Date: Wed, 14 Jan 2026 19:10:06 -0500 Subject: [PATCH 07/22] ZJIT: Optimize common `invokesuper` cases (#15816) * ZJIT: Profile `invokesuper` instructions * ZJIT: Introduce the `InvokeSuperDirect` HIR instruction The new instruction is an optimized version of `InvokeSuper` when we know the `super` target is an ISEQ. * ZJIT: Expand definition of unspecializable to more complex cases * ZJIT: Ensure `invokesuper` optimization works when the inheritance hierarchy is modified * ZJIT: Simplify `invokesuper` specialization to most common case Looking at ruby-bench, most `super` calls don't pass a block, which means we can use the already optimized `SendWithoutBlockDirect`. * ZJIT: Track `super` method entries directly to avoid GC issues Because the method entry isn't typed as a `VALUE`, we set up barriers on its `VALUE` fields. But, that was insufficient as the method entry itself could be collected in certain cases, resulting in dangling objects. Now we track the method entry as a `VALUE` and can more naturally mark it and its children. * ZJIT: Optimize `super` calls with simple argument forms * ZJIT: Report the reason why we can't optimize an `invokesuper` instance * ZJIT: Revise send fallback reasons for `super` calls * ZJIT: Assert `super` calls are `FCALL` and don't need visibily checks --- insns.def | 1 + test/ruby/test_zjit.rb | 477 +++++++++++++++++++++++++++++++++ yjit/src/cruby_bindings.inc.rs | 49 ++-- zjit.rb | 1 + zjit/src/codegen.rs | 42 ++- zjit/src/cruby_bindings.inc.rs | 49 ++-- zjit/src/hir.rs | 164 +++++++++++- zjit/src/hir/opt_tests.rs | 341 +++++++++++++++++++++++ zjit/src/profile.rs | 57 +++- zjit/src/stats.rs | 57 ++++ 10 files changed, 1180 insertions(+), 58 deletions(-) diff --git a/insns.def b/insns.def index 7df36726157455..ceeaf4128e9abf 100644 --- a/insns.def +++ b/insns.def @@ -1092,6 +1092,7 @@ invokesuper (VALUE val) // attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci); // attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci); +// attr bool zjit_profile = true; { VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, true); val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_super); diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index cf3c46b3ed56a7..bc4f5f2ae8f063 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -843,6 +843,483 @@ def test } end + def test_invokesuper_to_iseq + assert_compiles '["B", "A"]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + def test + B.new.foo + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_args + assert_compiles '["B", 11]', %q{ + class A + def foo(x) + x * 2 + end + end + + class B < A + def foo(x) + ["B", super(x) + 1] + end + end + + def test + B.new.foo(5) + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test super with explicit args when callee has rest parameter. + # This should fall back to dynamic dispatch since we can't handle rest params yet. + def test_invokesuper_with_args_to_rest_param + assert_compiles '["B", "a", ["b", "c"]]', %q{ + class A + def foo(x, *rest) + [x, rest] + end + end + + class B < A + def foo(x, y, z) + ["B", *super(x, y, z)] + end + end + + def test + B.new.foo("a", "b", "c") + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_block + assert_compiles '["B", "from_block"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super { "from_block" }] + end + end + + def test + B.new.foo + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_to_cfunc + assert_compiles '["MyArray", 3]', %q{ + class MyArray < Array + def length + ["MyArray", super] + end + end + + def test + MyArray.new([1, 2, 3]).length + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_multilevel + assert_compiles '["C", ["B", "A"]]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + class C < B + def foo + ["C", super] + end + end + + def test + C.new.foo + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding - super without explicit block should forward caller's block + # Note: We call test twice to ensure ZJIT compiles it before the final call that we check + def test_invokesuper_forwards_block_implicitly + assert_compiles '["B", "forwarded_block"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super] # should forward the block from caller + end + end + + def test + B.new.foo { "forwarded_block" } + end + + test # profile invokesuper + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding with explicit arguments + def test_invokesuper_forwards_block_implicitly_with_args + assert_compiles '["B", ["arg_value", "forwarded"]]', %q{ + class A + def foo(x) + [x, (block_given? ? yield : "no_block")] + end + end + + class B < A + def foo(x) + ["B", super(x)] # explicit args, but block should still be forwarded + end + end + + def test + B.new.foo("arg_value") { "forwarded" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding when no block is given (should not fail) + def test_invokesuper_forwards_block_implicitly_no_block_given + assert_compiles '["B", "no_block"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super] # no block given by caller + end + end + + def test + B.new.foo # called without a block + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding through multiple inheritance levels + def test_invokesuper_forwards_block_implicitly_multilevel + assert_compiles '["C", ["B", "deep_block"]]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo + ["B", super] # forwards block to A + end + end + + class C < B + def foo + ["C", super] # forwards block to B, which forwards to A + end + end + + def test + C.new.foo { "deep_block" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test implicit block forwarding with block parameter syntax + def test_invokesuper_forwards_block_param + assert_compiles '["B", "block_param_forwarded"]', %q{ + class A + def foo + block_given? ? yield : "no_block" + end + end + + class B < A + def foo(&block) + ["B", super] # should forward &block implicitly + end + end + + def test + B.new.foo { "block_param_forwarded" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_blockarg + assert_compiles '["B", "different block"]', %q{ + class A + def foo + block_given? ? yield : "no block" + end + end + + class B < A + def foo(&blk) + other_block = proc { "different block" } + ["B", super(&other_block)] + end + end + + def test + B.new.foo { "passed block" } + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_symbol_to_proc + assert_compiles '["B", [3, 5, 7]]', %q{ + class A + def foo(items, &blk) + items.map(&blk) + end + end + + class B < A + def foo(items) + ["B", super(items, &:succ)] + end + end + + def test + B.new.foo([2, 4, 6]) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_splat + assert_compiles '["B", 6]', %q{ + class A + def foo(a, b, c) + a + b + c + end + end + + class B < A + def foo(*args) + ["B", super(*args)] + end + end + + def test + B.new.foo(1, 2, 3) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_kwargs + assert_compiles '["B", "x=1, y=2"]', %q{ + class A + def foo(x:, y:) + "x=#{x}, y=#{y}" + end + end + + class B < A + def foo(x:, y:) + ["B", super(x: x, y: y)] + end + end + + def test + B.new.foo(x: 1, y: 2) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + def test_invokesuper_with_kw_splat + assert_compiles '["B", "x=1, y=2"]', %q{ + class A + def foo(x:, y:) + "x=#{x}, y=#{y}" + end + end + + class B < A + def foo(**kwargs) + ["B", super(**kwargs)] + end + end + + def test + B.new.foo(x: 1, y: 2) + end + + test # profile + test # compile + run compiled code + }, call_threshold: 2 + end + + # Test that including a module after compilation correctly changes the super target. + # The included module's method should be called, not the original super target. + def test_invokesuper_with_include + assert_compiles '["B", "M"]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + def test + B.new.foo + end + + test # profile invokesuper (super -> A#foo) + test # compile with super -> A#foo + + # Now include a module in B that defines foo - super should go to M#foo instead + module M + def foo + "M" + end + end + B.include(M) + + test # should call M#foo, not A#foo + }, call_threshold: 2 + end + + # Test that prepending a module after compilation correctly changes the super target. + # The prepended module's method should be called, not the original super target. + def test_invokesuper_with_prepend + assert_compiles '["B", "M"]', %q{ + class A + def foo + "A" + end + end + + class B < A + def foo + ["B", super] + end + end + + def test + B.new.foo + end + + test # profile invokesuper (super -> A#foo) + test # compile with super -> A#foo + + # Now prepend a module that defines foo - super should go to M#foo instead + module M + def foo + "M" + end + end + A.prepend(M) + + test # should call M#foo, not A#foo + }, call_threshold: 2 + end + + # Test super with positional and keyword arguments (pattern from chunky_png) + def test_invokesuper_with_keyword_args + assert_compiles '{content: "image data"}', %q{ + class A + def foo(attributes = {}) + @attributes = attributes + end + end + + class B < A + def foo(content = '') + super(content: content) + end + end + + def test + B.new.foo("image data") + end + + test + test + }, call_threshold: 2 + end + def test_invokebuiltin # Not using assert_compiles due to register spill assert_runs '["."]', %q{ diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 61dbf9b5c35929..9fbcf2169f8779 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -924,30 +924,31 @@ pub const YARVINSN_zjit_send: ruby_vminsn_type = 221; pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 222; pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 223; pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 247; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 248; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 249; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit.rb b/zjit.rb index e2aa55f764eb9a..0bd6c1b96d36d0 100644 --- a/zjit.rb +++ b/zjit.rb @@ -184,6 +184,7 @@ def stats_string # Show fallback counters, ordered by the typical amount of fallbacks for the prefix at the time print_counters_with_prefix(prefix: 'unspecialized_send_def_type_', prompt: 'not optimized method types for send', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'unspecialized_send_without_block_def_type_', prompt: 'not optimized method types for send_without_block', buf:, stats:, limit: 20) + print_counters_with_prefix(prefix: 'unspecialized_super_def_type_', prompt: 'not optimized method types for super', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'uncategorized_fallback_yarv_insn_', prompt: 'instructions with uncategorized fallback reason', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'send_fallback_', prompt: 'send fallback reasons', buf:, stats:, limit: 20) print_counters_with_prefix(prefix: 'setivar_fallback_', prompt: 'setivar fallback reasons', buf:, stats:, limit: 5) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 4a186d960c062c..16ac2573f24954 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -15,7 +15,7 @@ use crate::invariants::{ use crate::gc::append_gc_offsets; use crate::payload::{get_or_create_iseq_payload, IseqCodePtrs, IseqVersion, IseqVersionRef, IseqStatus}; use crate::state::ZJITState; -use crate::stats::{CompileError, exit_counter_for_compile_error, exit_counter_for_unhandled_hir_insn, incr_counter, incr_counter_by, send_fallback_counter, send_fallback_counter_for_method_type, send_fallback_counter_ptr_for_opcode, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type}; +use crate::stats::{CompileError, exit_counter_for_compile_error, exit_counter_for_unhandled_hir_insn, incr_counter, incr_counter_by, send_fallback_counter, send_fallback_counter_for_method_type, send_fallback_counter_for_super_method_type, send_fallback_counter_ptr_for_opcode, send_without_block_fallback_counter_for_method_type, send_without_block_fallback_counter_for_optimized_method_type}; use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_ns, exit_compile_error}}; use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr}; use crate::backend::lir::{self, Assembler, C_ARG_OPNDS, C_RET_OPND, CFP, EC, NATIVE_BASE_PTR, NATIVE_STACK_PTR, Opnd, SP, SideExit, Target, asm_ccall, asm_comment}; @@ -401,7 +401,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::Send { cd, blockiseq, state, reason, .. } => gen_send(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::SendForward { cd, blockiseq, state, reason, .. } => gen_send_forward(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::SendWithoutBlock { cd, state, reason, .. } => gen_send_without_block(jit, asm, cd, &function.frame_state(state), reason), - Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_without_block_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state)), + Insn::SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_iseq_direct(cb, jit, asm, *cme, *iseq, opnd!(recv), opnds!(args), &function.frame_state(*state), None), &Insn::InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper(jit, asm, cd, blockiseq, &function.frame_state(state), reason), &Insn::InvokeBlock { cd, state, reason, .. } => gen_invokeblock(jit, asm, cd, &function.frame_state(state), reason), // Ensure we have enough room fit ec, self, and arguments @@ -453,6 +453,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::GuardNotShared { recv, state } => gen_guard_not_shared(jit, asm, opnd!(recv), &function.frame_state(*state)), &Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), &Insn::GuardGreaterEq { left, right, state } => gen_guard_greater_eq(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)), + &Insn::GuardSuperMethodEntry { cme, state } => no_output!(gen_guard_super_method_entry(jit, asm, cme, &function.frame_state(state))), + Insn::GetBlockHandler => gen_get_block_handler(jit, asm), Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))), Insn::CCall { cfunc, recv, args, name, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, *name, opnd!(recv), opnds!(args)), // Give up CCallWithFrame for 7+ args since asm.ccall() supports at most 6 args (recv + args). @@ -715,6 +717,29 @@ fn gen_guard_greater_eq(jit: &JITState, asm: &mut Assembler, left: Opnd, right: left } +/// Guard that the method entry at ep[VM_ENV_DATA_INDEX_ME_CREF] matches the expected CME. +/// This ensures we're calling super from the expected method context. +fn gen_guard_super_method_entry( + jit: &JITState, + asm: &mut Assembler, + cme: *const rb_callable_method_entry_t, + state: &FrameState, +) { + asm_comment!(asm, "guard super method entry"); + let lep = gen_get_lep(jit, asm); + let ep_me_opnd = Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_ME_CREF); + let ep_me = asm.load(ep_me_opnd); + asm.cmp(ep_me, Opnd::UImm(cme as u64)); + asm.jne(side_exit(jit, state, SideExitReason::GuardSuperMethodEntry)); +} + +/// Get the block handler from ep[VM_ENV_DATA_INDEX_SPECVAL] at the local EP (LEP). +fn gen_get_block_handler(jit: &JITState, asm: &mut Assembler) -> Opnd { + asm_comment!(asm, "get block handler from LEP"); + let lep = gen_get_lep(jit, asm); + asm.load(Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)) +} + fn gen_get_constant_path(jit: &JITState, asm: &mut Assembler, ic: *const iseq_inline_constant_cache, state: &FrameState) -> Opnd { unsafe extern "C" { fn rb_vm_opt_getconstant_path(ec: EcPtr, cfp: CfpPtr, ic: *const iseq_inline_constant_cache) -> VALUE; @@ -1321,8 +1346,10 @@ fn gen_send_without_block( ) } -/// Compile a direct jump to an ISEQ call without block -fn gen_send_without_block_direct( +/// Compile a direct call to an ISEQ method. +/// If `block_handler` is provided, it's used as the specval for the new frame (for forwarding blocks). +/// Otherwise, `VM_BLOCK_HANDLER_NONE` is used. +fn gen_send_iseq_direct( cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, @@ -1331,6 +1358,7 @@ fn gen_send_without_block_direct( recv: Opnd, args: Vec, state: &FrameState, + block_handler: Option, ) -> lir::Opnd { gen_incr_counter(asm, Counter::iseq_optimized_send_count); @@ -1357,7 +1385,8 @@ fn gen_send_without_block_direct( let bmethod_specval = (capture.ep.addr() | 1).into(); (bmethod_frame_type, bmethod_specval) } else { - (VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, VM_BLOCK_HANDLER_NONE.into()) + let specval = block_handler.unwrap_or_else(|| VM_BLOCK_HANDLER_NONE.into()); + (VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL, specval) }; // Set up the new frame @@ -2131,6 +2160,9 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso SendNotOptimizedMethodType(method_type) => { gen_incr_counter(asm, send_fallback_counter_for_method_type(method_type)); } + SuperNotOptimizedMethodType(method_type) => { + gen_incr_counter(asm, send_fallback_counter_for_super_method_type(method_type)); + } _ => {} } } diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 5d4fed0c3ac18d..2201bdcffee776 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1713,30 +1713,31 @@ pub const YARVINSN_zjit_send: ruby_vminsn_type = 221; pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 222; pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 223; pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 247; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 248; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 249; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index cf0625cdad0806..48f85c4f236b09 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -484,6 +484,7 @@ pub enum SideExitReason { UnhandledHIRInsn(InsnId), UnhandledYARVInsn(u32), UnhandledCallType(CallType), + UnhandledBlockArg, TooManyKeywordParameters, FixnumAddOverflow, FixnumSubOverflow, @@ -497,6 +498,7 @@ pub enum SideExitReason { GuardNotShared, GuardLess, GuardGreaterEq, + GuardSuperMethodEntry, PatchPoint(Invariant), CalleeSideExit, ObjToStringFallback, @@ -647,6 +649,22 @@ pub enum SendFallbackReason { /// A singleton class has been seen for the receiver class, so we skip the optimization /// to avoid an invalidation loop. SingletonClassSeen, + /// The super call is passed a block that the optimizer does not support. + SuperCallWithBlock, + /// The profiled super class cannot be found. + SuperClassNotFound, + /// The `super` call uses a complex argument pattern that the optimizer does not support. + SuperComplexArgsPass, + /// The cached target of a `super` call could not be found. + SuperTargetNotFound, + /// Attempted to specialize a `super` call that doesn't have profile data. + SuperNoProfiles, + /// Cannot optimize the `super` call due to the target method. + SuperNotOptimizedMethodType(MethodType), + /// The `super` call is polymorpic. + SuperPolymorphic, + /// The `super` target call uses a complex argument pattern that the optimizer does not support. + SuperTargetComplexArgsPass, /// Initial fallback reason for every instruction, which should be mutated to /// a more actionable reason when an attempt to specialize the instruction fails. Uncategorized(ruby_vminsn_type), @@ -684,6 +702,14 @@ impl Display for SendFallbackReason { ComplexArgPass => write!(f, "Complex argument passing"), UnexpectedKeywordArgs => write!(f, "Unexpected Keyword Args"), SingletonClassSeen => write!(f, "Singleton class previously created for receiver class"), + SuperCallWithBlock => write!(f, "super: call made with a block"), + SuperClassNotFound => write!(f, "super: profiled class cannot be found"), + SuperComplexArgsPass => write!(f, "super: complex argument passing to `super` call"), + SuperNoProfiles => write!(f, "super: no profile data available"), + SuperNotOptimizedMethodType(method_type) => write!(f, "super: unsupported target method type {:?}", method_type), + SuperPolymorphic => write!(f, "super: polymorphic call site"), + SuperTargetNotFound => write!(f, "super: profiled target method cannot be found"), + SuperTargetComplexArgsPass => write!(f, "super: complex argument passing to `super` target call"), Uncategorized(insn) => write!(f, "Uncategorized({})", insn_name(*insn as usize)), } } @@ -975,6 +1001,11 @@ pub enum Insn { GuardGreaterEq { left: InsnId, right: InsnId, state: InsnId }, /// Side-exit if left is not less than right (both operands are C long). GuardLess { left: InsnId, right: InsnId, state: InsnId }, + /// Side-exit if the method entry at ep[VM_ENV_DATA_INDEX_ME_CREF] doesn't match the expected CME. + /// Used to ensure super calls are made from the expected method context. + GuardSuperMethodEntry { cme: *const rb_callable_method_entry_t, state: InsnId }, + /// Get the block handler from ep[VM_ENV_DATA_INDEX_SPECVAL] at the local EP (LEP). + GetBlockHandler, /// Generate no code (or padding if necessary) and insert a patch point /// that can be rewritten to a side exit when the Invariant is broken. @@ -1003,7 +1034,7 @@ impl Insn { | Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. } | Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetGlobal { .. } | Insn::SetLocal { .. } | Insn::Throw { .. } | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. } - | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } + | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::GuardSuperMethodEntry { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. } | Insn::ArrayAset { .. } => false, _ => true, @@ -1353,6 +1384,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { Insn::GuardNotShared { recv, .. } => write!(f, "GuardNotShared {recv}"), Insn::GuardLess { left, right, .. } => write!(f, "GuardLess {left}, {right}"), Insn::GuardGreaterEq { left, right, .. } => write!(f, "GuardGreaterEq {left}, {right}"), + Insn::GuardSuperMethodEntry { cme, .. } => write!(f, "GuardSuperMethodEntry {:p}", self.ptr_map.map_ptr(cme)), + Insn::GetBlockHandler => write!(f, "GetBlockHandler"), Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) }, Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) }, Insn::IsBlockGiven => { write!(f, "IsBlockGiven") }, @@ -2015,6 +2048,8 @@ impl Function { &GuardNotShared { recv, state } => GuardNotShared { recv: find!(recv), state }, &GuardGreaterEq { left, right, state } => GuardGreaterEq { left: find!(left), right: find!(right), state }, &GuardLess { left, right, state } => GuardLess { left: find!(left), right: find!(right), state }, + &GuardSuperMethodEntry { cme, state } => GuardSuperMethodEntry { cme, state }, + &GetBlockHandler => GetBlockHandler, &FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state }, &FixnumSub { left, right, state } => FixnumSub { left: find!(left), right: find!(right), state }, &FixnumMult { left, right, state } => FixnumMult { left: find!(left), right: find!(right), state }, @@ -2187,8 +2222,9 @@ impl Function { Insn::SetGlobal { .. } | Insn::Jump(_) | Insn::EntryPoint { .. } | Insn::IfTrue { .. } | Insn::IfFalse { .. } | Insn::Return { .. } | Insn::Throw { .. } | Insn::PatchPoint { .. } | Insn::SetIvar { .. } | Insn::SetClassVar { .. } | Insn::ArrayExtend { .. } - | Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } | Insn::IncrCounter(_) - | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::IncrCounterPtr { .. } + | Insn::ArrayPush { .. } | Insn::SideExit { .. } | Insn::SetLocal { .. } + | Insn::IncrCounter(_) | Insn::IncrCounterPtr { .. } + | Insn::CheckInterrupts { .. } | Insn::GuardBlockParamProxy { .. } | Insn::GuardSuperMethodEntry { .. } | Insn::StoreField { .. } | Insn::WriteBarrier { .. } | Insn::HashAset { .. } | Insn::ArrayAset { .. } => panic!("Cannot infer type of instruction with no output: {}. See Insn::has_output().", self.insns[insn.0]), Insn::Const { val: Const::Value(val) } => Type::from_value(*val), @@ -2296,6 +2332,7 @@ impl Function { Insn::AnyToString { .. } => types::String, Insn::GetLocal { rest_param: true, .. } => types::ArrayExact, Insn::GetLocal { .. } => types::BasicObject, + Insn::GetBlockHandler => types::RubyValue, // The type of Snapshot doesn't really matter; it's never materialized. It's used only // as a reference for FrameState, which we use to generate side-exit code. Insn::Snapshot { .. } => types::Any, @@ -3060,6 +3097,120 @@ impl Function { self.push_insn_id(block, insn_id); }; } + Insn::InvokeSuper { recv, cd, blockiseq, args, state, .. } => { + // Don't handle calls with literal blocks (e.g., super { ... }) + if !blockiseq.is_null() { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperCallWithBlock); + continue; + } + + let ci = unsafe { get_call_data_ci(cd) }; + let flags = unsafe { rb_vm_ci_flag(ci) }; + assert!(flags & VM_CALL_FCALL != 0); + + // Reject calls with complex argument handling. + let complex_arg_types = VM_CALL_ARGS_SPLAT + | VM_CALL_KW_SPLAT + | VM_CALL_KWARG + | VM_CALL_ARGS_BLOCKARG + | VM_CALL_FORWARDING; + + if (flags & complex_arg_types) != 0 { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperComplexArgsPass); + continue; + } + + let frame_state = self.frame_state(state); + + // Get the profiled CME from the current method. + let Some(profiles) = self.profiles.as_ref() else { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperNoProfiles); + continue; + }; + + let Some(current_cme) = profiles.payload.profile.get_super_method_entry(frame_state.insn_idx) else { + self.push_insn_id(block, insn_id); + + // The absence of the super CME could be due to a missing profile, but + // if we've made it this far the value would have been deleted, indicating + // that the call is at least polymorphic and possibly megamorphic. + self.set_dynamic_send_reason(insn_id, SuperPolymorphic); + continue; + }; + + // Get defined_class and method ID from the profiled CME. + let current_defined_class = unsafe { (*current_cme).defined_class }; + let mid = unsafe { get_def_original_id((*current_cme).def) }; + + // Compute superclass: RCLASS_SUPER(RCLASS_ORIGIN(defined_class)) + let superclass = unsafe { rb_class_get_superclass(RCLASS_ORIGIN(current_defined_class)) }; + if superclass.nil_p() { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperClassNotFound); + continue; + } + + // Look up the super method. + let super_cme = unsafe { rb_callable_method_entry(superclass, mid) }; + if super_cme.is_null() { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperTargetNotFound); + continue; + } + + // Check if it's an ISEQ method; bail if it isn't. + let def_type = unsafe { get_cme_def_type(super_cme) }; + if def_type != VM_METHOD_TYPE_ISEQ { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperNotOptimizedMethodType(MethodType::from(def_type))); + continue; + } + + // Check if the super method's parameters support direct send. + // If not, we can't do direct dispatch. + let super_iseq = unsafe { get_def_iseq_ptr((*super_cme).def) }; + if !can_direct_send(self, block, super_iseq, insn_id, args.as_slice()) { + self.push_insn_id(block, insn_id); + self.set_dynamic_send_reason(insn_id, SuperTargetComplexArgsPass); + continue; + } + + // Add PatchPoint for method redefinition. + self.push_insn(block, Insn::PatchPoint { + invariant: Invariant::MethodRedefined { + klass: unsafe { (*super_cme).defined_class }, + method: mid, + cme: super_cme + }, + state + }); + + // Guard that we're calling `super` from the expected method context. + self.push_insn(block, Insn::GuardSuperMethodEntry { cme: current_cme, state }); + + // Guard that no block is being passed (implicit or explicit). + let block_handler = self.push_insn(block, Insn::GetBlockHandler); + self.push_insn(block, Insn::GuardBitEquals { + val: block_handler, + expected: Const::Value(VALUE(VM_BLOCK_HANDLER_NONE as usize)), + reason: SideExitReason::UnhandledBlockArg, + state + }); + + // Use SendWithoutBlockDirect with the super method's CME and ISEQ. + let send_direct = self.push_insn(block, Insn::SendWithoutBlockDirect { + recv, + cd, + cme: super_cme, + iseq: super_iseq, + args, + state + }); + self.make_equal_to(insn_id, send_direct); + } _ => { self.push_insn_id(block, insn_id); } } } @@ -3977,6 +4128,7 @@ impl Function { | &Insn::LoadEC | &Insn::LoadSelf | &Insn::GetLocal { .. } + | &Insn::GetBlockHandler | &Insn::PutSpecialObject { .. } | &Insn::IsBlockGiven | &Insn::IncrCounter(_) @@ -4205,6 +4357,7 @@ impl Function { worklist.push_back(val); } &Insn::GuardBlockParamProxy { state, .. } | + &Insn::GuardSuperMethodEntry { state, .. } | &Insn::GetGlobal { state, .. } | &Insn::GetSpecialSymbol { state, .. } | &Insn::GetSpecialNumber { state, .. } | @@ -4720,6 +4873,8 @@ impl Function { | Insn::Jump { .. } | Insn::EntryPoint { .. } | Insn::GuardBlockParamProxy { .. } + | Insn::GuardSuperMethodEntry { .. } + | Insn::GetBlockHandler | Insn::PatchPoint { .. } | Insn::SideExit { .. } | Insn::IncrCounter { .. } @@ -5397,7 +5552,8 @@ fn unspecializable_c_call_type(flags: u32) -> bool { fn unspecializable_call_type(flags: u32) -> bool { ((flags & VM_CALL_ARGS_SPLAT) != 0) || ((flags & VM_CALL_KW_SPLAT) != 0) || - ((flags & VM_CALL_ARGS_BLOCKARG) != 0) + ((flags & VM_CALL_ARGS_BLOCKARG) != 0) || + ((flags & VM_CALL_FORWARDING) != 0) } /// We have IseqPayload, which keeps track of HIR Types in the interpreter, but this is not useful diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 2cf76e5e74868f..138bbde7181ed1 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -10554,4 +10554,345 @@ mod hir_opt_tests { Return v15 "); } + + #[test] + fn test_invokesuper_to_iseq_optimizes_to_direct() { + eval(" + class A + def foo + 'A' + end + end + + class B < A + def foo + super + end + end + + B.new.foo; B.new.foo + "); + + // A Ruby method as the target of `super` should optimize provided no block is given. + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(!hir.contains("InvokeSuper "), "InvokeSuper should optimize to SendWithoutBlockDirect but got:\n{hir}"); + assert!(hir.contains("SendWithoutBlockDirect"), "Should optimize to SendWithoutBlockDirect for call without args or block:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + PatchPoint MethodRedefined(A@0x1000, foo@0x1008, cme:0x1010) + GuardSuperMethodEntry 0x1038 + v18:RubyValue = GetBlockHandler + v19:FalseClass = GuardBitEquals v18, Value(false) + v20:BasicObject = SendWithoutBlockDirect v6, :foo (0x1040) + CheckInterrupts + Return v20 + "); + } + + #[test] + fn test_invokesuper_with_positional_args_optimizes_to_direct() { + eval(" + class A + def foo(x) + x * 2 + end + end + + class B < A + def foo(x) + super(x) + 1 + end + end + + B.new.foo(5); B.new.foo(5) + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(!hir.contains("InvokeSuper "), "InvokeSuper should optimize to SendWithoutBlockDirect but got:\n{hir}"); + assert!(hir.contains("SendWithoutBlockDirect"), "Should optimize to SendWithoutBlockDirect for call without args or block:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :x, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + PatchPoint MethodRedefined(A@0x1000, foo@0x1008, cme:0x1010) + GuardSuperMethodEntry 0x1038 + v27:RubyValue = GetBlockHandler + v28:FalseClass = GuardBitEquals v27, Value(false) + v29:BasicObject = SendWithoutBlockDirect v8, :foo (0x1040), v9 + v17:Fixnum[1] = Const Value(1) + PatchPoint MethodRedefined(Integer@0x1048, +@0x1050, cme:0x1058) + v32:Fixnum = GuardType v29, Fixnum + v33:Fixnum = FixnumAdd v32, v17 + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v33 + "); + } + + #[test] + fn test_invokesuper_with_forwarded_splat_args_remains_invokesuper() { + eval(" + class A + def foo(x) + x * 2 + end + end + + class B < A + def foo(*x) + super + end + end + + B.new.foo(5); B.new.foo(5) + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for explicit blockarg:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:ArrayExact = GetLocal :x, l0, SP@4, * + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:ArrayExact): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:ArrayExact): + v15:ArrayExact = ToArray v9 + v17:BasicObject = InvokeSuper v8, 0x1000, v15 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_invokesuper_with_block_literal_remains_invokesuper() { + eval(" + class A + def foo + block_given? ? yield : 'no block' + end + end + + class B < A + def foo + super { 'from subclass' } + end + end + + B.new.foo; B.new.foo + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for block literal:\n{hir}"); + + // With a block, we don't optimize to SendWithoutBlockDirect + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeSuper v6, 0x1000 # SendFallbackReason: super: call made with a block + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_invokesuper_to_cfunc_remains_invokesuper() { + eval(" + class MyArray < Array + def length + super + end + end + + MyArray.new.length; MyArray.new.length + "); + + let hir = hir_string_proc("MyArray.new.method(:length)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for CFUNC:\n{hir}"); + + assert_snapshot!(hir, @r" + fn length@:4: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + v11:BasicObject = InvokeSuper v6, 0x1000 # SendFallbackReason: super: unsupported target method type Cfunc + CheckInterrupts + Return v11 + "); + } + + #[test] + fn test_invokesuper_with_blockarg_remains_invokesuper() { + eval(" + class A + def foo + block_given? ? yield : 'no block' + end + end + + class B < A + def foo(&blk) + other_block = proc { 'different block' } + super(&other_block) + end + end + + B.new.foo { 'passed' }; B.new.foo { 'passed' } + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for explicit blockarg:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :blk, l0, SP@5 + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + PatchPoint NoSingletonClass(B@0x1000) + PatchPoint MethodRedefined(B@0x1000, proc@0x1008, cme:0x1010) + v35:HeapObject[class_exact:B] = GuardType v10, HeapObject[class_exact:B] + v36:BasicObject = CCallWithFrame v35, :Kernel#proc@0x1038, block=0x1040 + v18:BasicObject = GetLocal :blk, l0, EP@4 + SetLocal :other_block, l0, EP@3, v36 + v25:BasicObject = GetLocal :other_block, l0, EP@3 + v27:BasicObject = InvokeSuper v10, 0x1048, v25 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v27 + "); + } + + #[test] + fn test_invokesuper_with_symbol_to_proc_remains_invokesuper() { + eval(" + class A + def foo(items, &blk) + items.map(&blk) + end + end + + class B < A + def foo(items) + super(items, &:succ) + end + end + + B.new.foo([1, 2, 3]); B.new.foo([1, 2, 3]) + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + assert!(!hir.contains("SendWithoutBlockDirect"), "Should not optimize to SendWithoutBlockDirect for symbol-to-proc:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:10: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :items, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v15:StaticSymbol[:succ] = Const Value(VALUE(0x1000)) + v17:BasicObject = InvokeSuper v8, 0x1008, v9, v15 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v17 + "); + } + + #[test] + fn test_invokesuper_with_keyword_args_remains_invokesuper() { + eval(" + class A + def foo(attributes = {}) + @attributes = attributes + end + end + + class B < A + def foo(content = '') + super(content: content) + end + end + + B.new.foo('image data'); B.new.foo('image data') + "); + + let hir = hir_string_proc("B.new.method(:foo)"); + assert!(hir.contains("InvokeSuper "), "Expected unoptimized InvokeSuper but got:\n{hir}"); + + assert_snapshot!(hir, @r" + fn foo@:9: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :content, l0, SP@4 + v3:CPtr = LoadPC + v4:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v5:CBool = IsBitEqual v3, v4 + IfTrue v5, bb2(v1, v2) + Jump bb4(v1, v2) + bb1(v9:BasicObject): + EntryPoint JIT(0) + v10:NilClass = Const Value(nil) + Jump bb2(v9, v10) + bb2(v16:BasicObject, v17:BasicObject): + v20:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v21:StringExact = StringCopy v20 + Jump bb4(v16, v21) + bb3(v13:BasicObject, v14:BasicObject): + EntryPoint JIT(1) + Jump bb4(v13, v14) + bb4(v24:BasicObject, v25:BasicObject): + v31:BasicObject = InvokeSuper v24, 0x1018, v25 # SendFallbackReason: super: complex argument passing to `super` call + CheckInterrupts + Return v31 + "); + } } diff --git a/zjit/src/profile.rs b/zjit/src/profile.rs index 867d97641be92f..7a584afd6fd1f6 100644 --- a/zjit/src/profile.rs +++ b/zjit/src/profile.rs @@ -3,6 +3,7 @@ // We use the YARV bytecode constants which have a CRuby-style name #![allow(non_upper_case_globals)] +use std::collections::HashMap; use crate::{cruby::*, payload::get_or_create_iseq_payload, options::{get_option, NumProfiles}}; use crate::distribution::{Distribution, DistributionSummary}; use crate::stats::Counter::profile_time_ns; @@ -90,6 +91,7 @@ fn profile_insn(bare_opcode: ruby_vminsn_type, ec: EcPtr) { YARVINSN_opt_size => profile_operands(profiler, profile, 1), YARVINSN_opt_succ => profile_operands(profiler, profile, 1), YARVINSN_invokeblock => profile_block_handler(profiler, profile), + YARVINSN_invokesuper => profile_invokesuper(profiler, profile), YARVINSN_opt_send_without_block | YARVINSN_send => { let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr(); let argc = unsafe { vm_ci_argc((*cd).ci) }; @@ -153,6 +155,37 @@ fn profile_block_handler(profiler: &mut Profiler, profile: &mut IseqProfile) { types[0].observe(ty); } +fn profile_invokesuper(profiler: &mut Profiler, profile: &mut IseqProfile) { + let cme = unsafe { rb_vm_frame_method_entry(profiler.cfp) }; + let cme_value = VALUE(cme as usize); // CME is a T_IMEMO, which is a VALUE + + match profile.super_cme.get(&profiler.insn_idx) { + None => { + // If `None`, then this is our first time looking at `super` for this instruction. + profile.super_cme.insert(profiler.insn_idx, Some(cme_value)); + }, + Some(Some(existing_cme)) => { + // Check if the stored method entry is the same as the current one. If it isn't, then + // mark the call site as polymorphic. + if *existing_cme != cme_value { + profile.super_cme.insert(profiler.insn_idx, None); + } + } + Some(None) => { + // We've visited this instruction and explicitly stored `None` to mark the call site + // as polymorphic. + } + } + + unsafe { rb_gc_writebarrier(profiler.iseq.into(), cme_value) }; + + let cd: *const rb_call_data = profiler.insn_opnd(0).as_ptr(); + let argc = unsafe { vm_ci_argc((*cd).ci) }; + + // Profile all the arguments and self (+1). + profile_operands(profiler, profile, (argc + 1) as usize); +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Flags(u32); @@ -324,6 +357,9 @@ pub struct IseqProfile { /// Number of profiled executions for each YARV instruction, indexed by the instruction index num_profiles: Vec, + + /// Method entries for `super` calls (stored as VALUE to be GC-safe) + super_cme: HashMap> } impl IseqProfile { @@ -331,6 +367,7 @@ impl IseqProfile { Self { opnd_types: vec![vec![]; iseq_size as usize], num_profiles: vec![0; iseq_size as usize], + super_cme: HashMap::new(), } } @@ -339,6 +376,11 @@ impl IseqProfile { self.opnd_types.get(insn_idx).map(|v| &**v) } + pub fn get_super_method_entry(&self, insn_idx: usize) -> Option<*const rb_callable_method_entry_t> { + self.super_cme.get(&insn_idx) + .and_then(|opt| opt.map(|v| v.0 as *const rb_callable_method_entry_t)) + } + /// Run a given callback with every object in IseqProfile pub fn each_object(&self, callback: impl Fn(VALUE)) { for operands in &self.opnd_types { @@ -349,9 +391,15 @@ impl IseqProfile { } } } + + for cme_value in self.super_cme.values() { + if let Some(cme) = cme_value { + callback(*cme); + } + } } - /// Run a given callback with a mutable reference to every object in IseqProfile + /// Run a given callback with a mutable reference to every object in IseqProfile. pub fn each_object_mut(&mut self, callback: impl Fn(&mut VALUE)) { for operands in &mut self.opnd_types { for distribution in operands { @@ -361,6 +409,13 @@ impl IseqProfile { } } } + + // Update CME references if they move during compaction. + for cme_value in self.super_cme.values_mut() { + if let Some(cme) = cme_value { + callback(cme); + } + } } } diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 089037dcd796f6..506bd82686a3f2 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -175,6 +175,7 @@ make_counters! { exit_unhandled_tailcall, exit_unhandled_splat, exit_unhandled_kwarg, + exit_unhandled_block_arg, exit_unknown_special_variable, exit_unhandled_hir_insn, exit_unhandled_yarv_insn, @@ -195,6 +196,7 @@ make_counters! { exit_guard_not_shared_failure, exit_guard_less_failure, exit_guard_greater_eq_failure, + exit_guard_super_method_entry, exit_patchpoint_bop_redefined, exit_patchpoint_method_redefined, exit_patchpoint_stable_constant_names, @@ -247,6 +249,15 @@ make_counters! { send_fallback_obj_to_string_not_string, send_fallback_send_cfunc_variadic, send_fallback_send_cfunc_array_variadic, + send_fallback_super_call_with_block, + send_fallback_super_class_not_found, + send_fallback_super_complex_args_pass, + send_fallback_super_fallback_no_profile, + send_fallback_super_not_optimized_method_type, + send_fallback_super_polymorphic, + send_fallback_super_target_not_found, + send_fallback_super_target_complex_args_pass, + send_fallback_cannot_send_direct, send_fallback_uncategorized, } @@ -357,6 +368,21 @@ make_counters! { unspecialized_send_def_type_refined, unspecialized_send_def_type_null, + // Super call def_type related to send fallback to dynamic dispatch + unspecialized_super_def_type_iseq, + unspecialized_super_def_type_cfunc, + unspecialized_super_def_type_attrset, + unspecialized_super_def_type_ivar, + unspecialized_super_def_type_bmethod, + unspecialized_super_def_type_zsuper, + unspecialized_super_def_type_alias, + unspecialized_super_def_type_undef, + unspecialized_super_def_type_not_implemented, + unspecialized_super_def_type_optimized, + unspecialized_super_def_type_missing, + unspecialized_super_def_type_refined, + unspecialized_super_def_type_null, + // Unsupported parameter features complex_arg_pass_param_rest, complex_arg_pass_param_post, @@ -503,6 +529,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter { UnknownSpecialVariable(_) => exit_unknown_special_variable, UnhandledHIRInsn(_) => exit_unhandled_hir_insn, UnhandledYARVInsn(_) => exit_unhandled_yarv_insn, + UnhandledBlockArg => exit_unhandled_block_arg, FixnumAddOverflow => exit_fixnum_add_overflow, FixnumSubOverflow => exit_fixnum_sub_overflow, FixnumMultOverflow => exit_fixnum_mult_overflow, @@ -518,6 +545,7 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter { GuardNotShared => exit_guard_not_shared_failure, GuardLess => exit_guard_less_failure, GuardGreaterEq => exit_guard_greater_eq_failure, + GuardSuperMethodEntry => exit_guard_super_method_entry, CalleeSideExit => exit_callee_side_exit, ObjToStringFallback => exit_obj_to_string_fallback, Interrupt => exit_interrupt, @@ -582,6 +610,14 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter SendNotOptimizedNeedPermission => send_fallback_send_not_optimized_need_permission, CCallWithFrameTooManyArgs => send_fallback_ccall_with_frame_too_many_args, ObjToStringNotString => send_fallback_obj_to_string_not_string, + SuperCallWithBlock => send_fallback_super_call_with_block, + SuperClassNotFound => send_fallback_super_class_not_found, + SuperComplexArgsPass => send_fallback_super_complex_args_pass, + SuperNoProfiles => send_fallback_super_fallback_no_profile, + SuperNotOptimizedMethodType(_) => send_fallback_super_not_optimized_method_type, + SuperPolymorphic => send_fallback_super_polymorphic, + SuperTargetNotFound => send_fallback_super_target_not_found, + SuperTargetComplexArgsPass => send_fallback_super_target_complex_args_pass, Uncategorized(_) => send_fallback_uncategorized, } } @@ -641,6 +677,27 @@ pub fn send_fallback_counter_for_method_type(method_type: crate::hir::MethodType } } +pub fn send_fallback_counter_for_super_method_type(method_type: crate::hir::MethodType) -> Counter { + use crate::hir::MethodType::*; + use crate::stats::Counter::*; + + match method_type { + Iseq => unspecialized_super_def_type_iseq, + Cfunc => unspecialized_super_def_type_cfunc, + Attrset => unspecialized_super_def_type_attrset, + Ivar => unspecialized_super_def_type_ivar, + Bmethod => unspecialized_super_def_type_bmethod, + Zsuper => unspecialized_super_def_type_zsuper, + Alias => unspecialized_super_def_type_alias, + Undefined => unspecialized_super_def_type_undef, + NotImplemented => unspecialized_super_def_type_not_implemented, + Optimized => unspecialized_super_def_type_optimized, + Missing => unspecialized_super_def_type_missing, + Refined => unspecialized_super_def_type_refined, + Null => unspecialized_super_def_type_null, + } +} + /// Primitive called in zjit.rb. Zero out all the counters. #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_reset_stats_bang(_ec: EcPtr, _self: VALUE) -> VALUE { From 806031d2ce9cb26e146ce47f6811ca038f1ffe27 Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Wed, 18 Dec 2024 15:13:18 +0100 Subject: [PATCH 08/22] Windows: Remove workaround for LLVM windres It was introduced as part of the Arm64-on-Windows patch: https://github.com/ruby/ruby/pull/8995 But a few days later it was fixed on the LLVM side for llvm-18 and backported to MSYS2: https://github.com/msys2/MINGW-packages/pull/19157#issuecomment-1825285063 Now this code is only unnecessary complexity. --- configure.ac | 2 -- cygwin/GNUmakefile.in | 11 +++-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 90326e2926fc33..4e7367804d3d6b 100644 --- a/configure.ac +++ b/configure.ac @@ -546,7 +546,6 @@ AS_CASE(["$target_os"], ]) rb_cv_binary_elf=no : ${enable_shared=yes} - AS_IF([$WINDRES --version | grep LLVM > /dev/null], [USE_LLVM_WINDRES=yes], [USE_LLVM_WINDRES=no]) ], [hiuxmpp*], [AC_DEFINE(__HIUX_MPP__)]) # by TOYODA Eizi @@ -4414,7 +4413,6 @@ AC_SUBST(MINIOBJS) AC_SUBST(THREAD_MODEL) AC_SUBST(COROUTINE_TYPE, ${coroutine_type}) AC_SUBST(PLATFORM_DIR) -AC_SUBST(USE_LLVM_WINDRES) firstmf=`echo $FIRSTMAKEFILE | sed 's/:.*//'` firsttmpl=`echo $FIRSTMAKEFILE | sed 's/.*://'` diff --git a/cygwin/GNUmakefile.in b/cygwin/GNUmakefile.in index 8e83d73040fe8c..109baa747d9dc9 100644 --- a/cygwin/GNUmakefile.in +++ b/cygwin/GNUmakefile.in @@ -6,14 +6,9 @@ MUNICODE_FLAG := $(if $(filter mingw%,$(target_os)),-municode) override EXE_LDFLAGS += $(MUNICODE_FLAG) DLLWRAP = @DLLWRAP@ --target=$(target_os) --driver-name="$(CC)" -ifeq (@USE_LLVM_WINDRES@,yes) # USE_LLVM_WINDRES - # llvm-windres fails when preprocessor options are added - windres-cpp := -else - windres-cpp := $(CPP) -xc - windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \ - $(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp))) -endif +windres-cpp := $(CPP) -xc +windres-cpp := --preprocessor=$(firstword $(windres-cpp)) \ + $(addprefix --preprocessor-arg=,$(wordlist 2,$(words $(windres-cpp)),$(windres-cpp))) WINDRES = @WINDRES@ $(windres-cpp) -DRC_INVOKED STRIP = @STRIP@ From 189bb64af8951d79bd812f2dca18a0104339b56e Mon Sep 17 00:00:00 2001 From: OKURA Masafumi Date: Thu, 14 Dec 2023 17:52:27 +0900 Subject: [PATCH 09/22] [ci-skip] Shorter example for `Module#instance_method` The previous example code was too complex and includes extra logics that's not relevant to its main usage: `bind`. The new example code focuses on `bind_call` so that readers can understand how it works more easily. --- proc.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/proc.c b/proc.c index b3159e14b743d8..5f23e5fed6a6df 100644 --- a/proc.c +++ b/proc.c @@ -2371,29 +2371,29 @@ rb_obj_singleton_method(VALUE obj, VALUE vid) * * Returns an +UnboundMethod+ representing the given * instance method in _mod_. + * See +UnboundMethod+ about how to utilize it * - * class Interpreter - * def do_a() print "there, "; end - * def do_d() print "Hello "; end - * def do_e() print "!\n"; end - * def do_v() print "Dave"; end - * Dispatcher = { - * "a" => instance_method(:do_a), - * "d" => instance_method(:do_d), - * "e" => instance_method(:do_e), - * "v" => instance_method(:do_v) - * } - * def interpret(string) - * string.each_char {|b| Dispatcher[b].bind(self).call } - * end - * end + * class Person + * def initialize(name) + * @name = name + * end + * + * def hi + * puts "Hi, I'm #{@name}!" + * end + * end + * + * dave = Person.new('Dave') + * thomas = Person.new('Thomas') * - * interpreter = Interpreter.new - * interpreter.interpret('dave') + * hi = Person.instance_method(:hi) + * hi.bind_call(dave) + * hi.bind_call(thomas) * * produces: * - * Hello there, Dave! + * Hi, I'm Dave! + * Hi, I'm Thomas! */ static VALUE From 886202bac80130ec6f2f3f67f306f9d5f860fb01 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Jan 2026 12:30:29 +0900 Subject: [PATCH 10/22] Count assertions in child processes Fix up GH-15785. --- tool/lib/core_assertions.rb | 3 +-- tool/test/testunit/test_assertion.rb | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tool/lib/core_assertions.rb b/tool/lib/core_assertions.rb index 419704448ba2fa..e29a0e3c25a0e3 100644 --- a/tool/lib/core_assertions.rb +++ b/tool/lib/core_assertions.rb @@ -384,11 +384,10 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o end raise if $! abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig)) - assertions = 0 marshal_error = nil assert(!abort, FailDesc[status, nil, stderr]) res.scan(/^\n(.*?)\n(?=<\/error id="#{token_re}">$)/m) do - assertions += $1.to_i + self._assertions += $1.to_i res = Marshal.load($2.unpack1("m")) or next rescue => marshal_error ignore_stderr = nil diff --git a/tool/test/testunit/test_assertion.rb b/tool/test/testunit/test_assertion.rb index 7b1f28a8698b15..d9bdc8f3c55394 100644 --- a/tool/test/testunit/test_assertion.rb +++ b/tool/test/testunit/test_assertion.rb @@ -17,6 +17,29 @@ def test_timeout_separately end end + def test_assertion_count_separately + beginning = self._assertions + + assert_separately([], "") + assertions_at_nothing = self._assertions - beginning + + prev_assertions = self._assertions + assertions_at_nothing + assert_separately([], "assert true") + assert_equal(1, self._assertions - prev_assertions) + + omit unless Process.respond_to?(:fork) + prev_assertions = self._assertions + assertions_at_nothing + assert_separately([], "Process.fork {assert true}; assert true") + assert_equal(2, self._assertions - prev_assertions) + + prev_assertions = self._assertions + assertions_at_nothing + # TODO: assertions before `fork` are counted twice; it is possible + # to reset `_assertions` at `Process._fork`, but the hook can + # interfere in other tests. + assert_separately([], "assert true; Process.fork {assert true}") + assert_equal(3, self._assertions - prev_assertions) + end + def return_in_assert_raise assert_raise(RuntimeError) do return From de17b84a3d557007bbaf00b4f17d843ed854ee7a Mon Sep 17 00:00:00 2001 From: git Date: Thu, 15 Jan 2026 06:55:51 +0000 Subject: [PATCH 11/22] Update bundled gems list as of 2026-01-15 --- NEWS.md | 1 + gems/bundled_gems | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 820a152ff09d5b..2b021e7cddff83 100644 --- a/NEWS.md +++ b/NEWS.md @@ -60,6 +60,7 @@ releases. * typeprof 0.31.1 * debug 1.11.1 * mutex_m 0.3.0 +* resolv-replace 0.2.0 * rdoc 7.1.0 ### RubyGems and Bundler diff --git a/gems/bundled_gems b/gems/bundled_gems index 1caac2448aa0bf..c8414dee7532cc 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -28,7 +28,7 @@ base64 0.3.0 https://github.com/ruby/base64 bigdecimal 4.0.1 https://github.com/ruby/bigdecimal observer 0.1.2 https://github.com/ruby/observer abbrev 0.1.2 https://github.com/ruby/abbrev -resolv-replace 0.1.1 https://github.com/ruby/resolv-replace +resolv-replace 0.2.0 https://github.com/ruby/resolv-replace rinda 0.2.0 https://github.com/ruby/rinda drb 2.2.3 https://github.com/ruby/drb nkf 0.2.0 https://github.com/ruby/nkf From 943b0859498c5a49242e6f8563b1f518599fff0f Mon Sep 17 00:00:00 2001 From: Daisuke Aritomo Date: Wed, 3 Dec 2025 02:25:46 +0900 Subject: [PATCH 12/22] [Bug #21723] Let `binding.irb` properly load irb by fixing force_activate() This patch fixes a problem where `binding.irb` (= force_activate('irb')) fails under `bundle exec` when the Gemfile does not contain `irb` and does contain a gem which is (1) not installed in GEM_HOME (2) sourced using `path:`/`git:`. The original approach constructing a temporary definition fails since it does not set the equalivent of `path:`/`git:`. Always reconstructing a definition from a Gemfile and applying lockfile constraints should be a more robust approach. [Bug #21723] --- lib/bundled_gems.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb index 3ac10aa25606d3..0b6ae60b5f1c4f 100644 --- a/lib/bundled_gems.rb +++ b/lib/bundled_gems.rb @@ -209,17 +209,14 @@ def self.force_activate(gem) builder = Bundler::Dsl.new - if Bundler::SharedHelpers.in_bundle? - if Bundler.locked_gems - Bundler.locked_gems.specs.each{|spec| builder.gem spec.name, spec.version.to_s } - elsif Bundler.definition.gemfiles.size > 0 - Bundler.definition.gemfiles.each{|gemfile| builder.eval_gemfile(gemfile) } - end + if Bundler::SharedHelpers.in_bundle? && Bundler.definition.gemfiles.size > 0 + Bundler.definition.gemfiles.each {|gemfile| builder.eval_gemfile(gemfile) } end builder.gem gem - definition = builder.to_definition(nil, true) + lockfile = Bundler.default_lockfile + definition = builder.to_definition(lockfile, nil) definition.validate_runtime! begin From 270c7fcec126d9de07488205cfa11b6ab37bc191 Mon Sep 17 00:00:00 2001 From: Daisuke Aritomo Date: Thu, 4 Dec 2025 20:56:31 +0900 Subject: [PATCH 13/22] Fake BUNDLE_GEMFILE and BUNDLE_LOCKFILE to let checks pass Bundler::Runtime#setup requires a real existing lockfile (see Bundler::SharedHelpers#default_lockfile). --- lib/bundled_gems.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb index 0b6ae60b5f1c4f..429b34bef031a6 100644 --- a/lib/bundled_gems.rb +++ b/lib/bundled_gems.rb @@ -209,13 +209,24 @@ def self.force_activate(gem) builder = Bundler::Dsl.new + lockfile = nil if Bundler::SharedHelpers.in_bundle? && Bundler.definition.gemfiles.size > 0 Bundler.definition.gemfiles.each {|gemfile| builder.eval_gemfile(gemfile) } + lockfile = begin + Bundler.default_lockfile + rescue Bundler::GemfileNotFound + nil + end + else + # Fake BUNDLE_GEMFILE and BUNDLE_LOCKFILE to let checks pass + orig_gemfile = ENV["BUNDLE_GEMFILE"] + orig_lockfile = ENV["BUNDLE_LOCKFILE"] + Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile" + Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", "Gemfile.lock" end builder.gem gem - lockfile = Bundler.default_lockfile definition = builder.to_definition(lockfile, nil) definition.validate_runtime! @@ -232,6 +243,8 @@ def self.force_activate(gem) rescue Bundler::GemNotFound warn "Failed to activate #{gem}, please install it with 'gem install #{gem}'" ensure + ENV['BUNDLE_GEMFILE'] = orig_gemfile if orig_gemfile + ENV['BUNDLE_LOCKFILE'] = orig_lockfile if orig_lockfile Bundler.ui = orig_ui Bundler::Definition.no_lock = orig_no_lock end From a28c8561835038d564a418c6d5dbd7cabfbcad4f Mon Sep 17 00:00:00 2001 From: Daisuke Aritomo Date: Thu, 4 Dec 2025 21:02:36 +0900 Subject: [PATCH 14/22] Add comments --- lib/bundled_gems.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb index 429b34bef031a6..f6fdad21a1f690 100644 --- a/lib/bundled_gems.rb +++ b/lib/bundled_gems.rb @@ -207,6 +207,7 @@ def self.force_activate(gem) require "bundler" Bundler.reset! + # Build and activate a temporary definition containing the original gems + the requested gem builder = Bundler::Dsl.new lockfile = nil From 2d79cc9bfc3fdd27e3de9d540c37d0367ed16d1e Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 15 Jan 2026 15:29:22 +0900 Subject: [PATCH 15/22] Added example for bundle/inline --- spec/bundled_gems_spec.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spec/bundled_gems_spec.rb b/spec/bundled_gems_spec.rb index e86b5ce6e8c0a7..d985ddad412e01 100644 --- a/spec/bundled_gems_spec.rb +++ b/spec/bundled_gems_spec.rb @@ -393,6 +393,30 @@ def my end end + context "with bundler/inline" do + it "foo is available on LOAD_PATH" do + build_lib "foo", "1.0.0" do |s| + s.write "lib/foo.rb", "puts :foo" + end + + script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s } + #!/usr/bin/env ruby + gemfile do + source "https://gem.repo1" + path "#{lib_path}" do + gem "foo", "1.0.0" + end + end + + Gem::BUNDLED_GEMS.force_activate("csv") + puts $LOAD_PATH + RUBY + + expect(err).to include("gem install csv") + expect(out).to include("foo-1.0.0/lib") + end + end + context "without bundle environment" do it "warns about installation requirement" do expect_any_instance_of(Object).to receive(:warn) From ca9c61800bce426ea9c4b15911a4f639370f3ec4 Mon Sep 17 00:00:00 2001 From: Daisuke Aritomo Date: Wed, 3 Dec 2025 03:41:21 +0900 Subject: [PATCH 16/22] Suppress bundled gem warning on `binding.irb' This patch silences the "this won't work in the next version of Ruby" warning displayed when irb is autoloaded via `binding.irb`. main.rb:1: warning: irb used to be loaded from the standard library, but is not part of the default gems since Ruby 4.0.0. You can add irb to your Gemfile or gemspec to fix this error. /.../irb.rb:9: warning: reline used to be loaded from the standard library, but is not part of the default gems since Ruby 4.0.0. You can add reline to your Gemfile or gemspec to fix this error. From: main.rb @ line 1 : => 1: binding.irb /.../input-method.rb:284: warning: rdoc used to be loaded from the standard library, but is not part of the default gems since Ruby 4.0.0. You can add rdoc to your Gemfile or gemspec to fix this error. This warning is incorrect and misleading: users should not need to include irb (and its dependencies) to their Gemfiles to use `binding.irb`, even in future versions of Ruby. It is agreed that the runtime takes care of that. --- lib/bundled_gems.rb | 4 ++++ prelude.rb | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/lib/bundled_gems.rb b/lib/bundled_gems.rb index f6fdad21a1f690..85f23b2596bed7 100644 --- a/lib/bundled_gems.rb +++ b/lib/bundled_gems.rb @@ -122,6 +122,10 @@ def self.warning?(name, specs: nil) false end + if suppress_list = Thread.current[:__bundled_gems_warning_suppression] + return if suppress_list.include?(name) || suppress_list.include?(feature) + end + return if specs.include?(name) # Don't warn if a hyphenated gem provides this feature diff --git a/prelude.rb b/prelude.rb index 7b5a7668982a93..163aa5c3e5f81e 100644 --- a/prelude.rb +++ b/prelude.rb @@ -1,6 +1,9 @@ class Binding # :nodoc: def irb(...) + suppress = Thread.current[:__bundled_gems_warning_suppression] + Thread.current[:__bundled_gems_warning_suppression] = ['irb', 'reline', 'rdoc'] + begin require 'irb' rescue LoadError, Gem::LoadError @@ -8,6 +11,8 @@ def irb(...) require 'irb' end irb(...) + ensure + Thread.current[:__bundled_gems_warning_suppression] = suppress end # suppress redefinition warning From 6afac93c5c7178c4d836e0ec2b4b3092373ca121 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Thu, 15 Jan 2026 16:52:01 +0900 Subject: [PATCH 17/22] Restore irb to warning target Users should add `irb` to their Gemfile. `Gem::BUNDLED_GEMS.force_activate 'irb'` is workaround for short term. --- prelude.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prelude.rb b/prelude.rb index 163aa5c3e5f81e..b6c610dd5818dc 100644 --- a/prelude.rb +++ b/prelude.rb @@ -2,7 +2,7 @@ class Binding # :nodoc: def irb(...) suppress = Thread.current[:__bundled_gems_warning_suppression] - Thread.current[:__bundled_gems_warning_suppression] = ['irb', 'reline', 'rdoc'] + Thread.current[:__bundled_gems_warning_suppression] = ['reline', 'rdoc'] begin require 'irb' From 11edc286d837f66b37433e7d51c8b8f500e1be84 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Thu, 15 Jan 2026 20:32:20 +0900 Subject: [PATCH 18/22] Make `Array#map` and `Array#select` more tolerant Only when YJIT is enabled, the redefinition of `Array#<<` affects these methods. --- array.rb | 7 +++++-- test/ruby/test_array.rb | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/array.rb b/array.rb index 03663dbb0b7ff1..81beff0b1c4016 100644 --- a/array.rb +++ b/array.rb @@ -245,7 +245,8 @@ def map # :nodoc: value = nil result = Primitive.ary_sized_alloc while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - result << yield(value) + value = yield(value) + Primitive.cexpr!(%q{ rb_ary_push(result, value) }) end result end @@ -270,7 +271,9 @@ def select # :nodoc: value = nil result = Primitive.ary_sized_alloc while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - result << value if yield value + if yield value + Primitive.cexpr!(%q{ rb_ary_push(result, value) }) + end end result end diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb index d93b86e7953b1a..04e15b6d87db72 100644 --- a/test/ruby/test_array.rb +++ b/test/ruby/test_array.rb @@ -1336,6 +1336,28 @@ def test_prepend assert_equal(@cls[@cls[1,2], nil, 'dog', 'cat'], a.prepend(@cls[1, 2])) end + def test_tolerant_to_redefinition + *code = __FILE__, __LINE__+1, "#{<<-"{#"}\n#{<<-'};'}" + {# + module M + def <<(a) + super(a * 2) + end + end + class Array; prepend M; end + ary = [*1..10] + mapped = ary.map {|i| i} + selected = ary.select {true} + module M + remove_method :<< + end + assert_equal(ary, mapped) + assert_equal(ary, selected) + }; + assert_separately(%w[--disable-yjit], *code) + assert_separately(%w[--enable-yjit], *code) + end + def test_push a = @cls[1, 2, 3] assert_equal(@cls[1, 2, 3, 4, 5], a.push(4, 5)) From 065db7cf61cfb69d869f4b403b49b1efc314eead Mon Sep 17 00:00:00 2001 From: nozomemein Date: Fri, 9 Jan 2026 09:56:11 +0900 Subject: [PATCH 19/22] ZJIT: Rename ArrayArefFixnum -> ArrayAref --- zjit/src/codegen.rs | 7 +++--- zjit/src/cruby_methods.rs | 3 ++- zjit/src/hir.rs | 23 ++++++++++---------- zjit/src/hir/opt_tests.rs | 45 +++++++++++++++++++++++---------------- zjit/src/hir/tests.rs | 8 ++++--- zjit/src/hir_type/mod.rs | 11 ++++++++++ 6 files changed, 60 insertions(+), 37 deletions(-) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 16ac2573f24954..25cccd1bd1df9e 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -374,7 +374,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::NewRange { low, high, flag, state } => gen_new_range(jit, asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)), Insn::NewRangeFixnum { low, high, flag, state } => gen_new_range_fixnum(asm, opnd!(low), opnd!(high), *flag, &function.frame_state(*state)), Insn::ArrayDup { val, state } => gen_array_dup(asm, opnd!(val), &function.frame_state(*state)), - Insn::ArrayArefFixnum { array, index, .. } => gen_aref_fixnum(asm, opnd!(array), opnd!(index)), + Insn::ArrayAref { array, index, .. } => gen_array_aref(asm, opnd!(array), opnd!(index)), Insn::ArrayAset { array, index, val } => { no_output!(gen_array_aset(asm, opnd!(array), opnd!(index), opnd!(val))) } @@ -1560,13 +1560,12 @@ fn gen_new_array( } /// Compile array access (`array[index]`) -fn gen_aref_fixnum( +fn gen_array_aref( asm: &mut Assembler, array: Opnd, index: Opnd, ) -> lir::Opnd { - let unboxed_idx = asm.rshift(index, Opnd::UImm(1)); - asm_ccall!(asm, rb_ary_entry, array, unboxed_idx) + asm_ccall!(asm, rb_ary_entry, array, index) } fn gen_array_aset( diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 40b9d138542c87..5f89a5f20e4be5 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -326,7 +326,8 @@ fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In if let &[index] = args { if fun.likely_a(index, types::Fixnum, state) { let index = fun.coerce_to(block, index, types::Fixnum, state); - let result = fun.push_insn(block, hir::Insn::ArrayArefFixnum { array: recv, index }); + let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); + let result = fun.push_insn(block, hir::Insn::ArrayAref { array: recv, index }); return Some(result); } } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 48f85c4f236b09..4a81ce641cf68e 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -759,7 +759,7 @@ pub enum Insn { ArrayExtend { left: InsnId, right: InsnId, state: InsnId }, /// Push `val` onto `array`, where `array` is already `Array`. ArrayPush { array: InsnId, val: InsnId, state: InsnId }, - ArrayArefFixnum { array: InsnId, index: InsnId }, + ArrayAref { array: InsnId, index: InsnId }, ArrayAset { array: InsnId, index: InsnId, val: InsnId }, ArrayPop { array: InsnId, state: InsnId }, /// Return the length of the array as a C `long` ([`types::CInt64`]) @@ -1163,8 +1163,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) } - Insn::ArrayArefFixnum { array, index, .. } => { - write!(f, "ArrayArefFixnum {array}, {index}") + Insn::ArrayAref { array, index, .. } => { + write!(f, "ArrayAref {array}, {index}") } Insn::ArrayAset { array, index, val, ..} => { write!(f, "ArrayAset {array}, {index}, {val}") @@ -2150,7 +2150,7 @@ impl Function { &NewHash { ref elements, state } => NewHash { elements: find_vec!(elements), state: find!(state) }, &NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) }, &NewRangeFixnum { low, high, flag, state } => NewRangeFixnum { low: find!(low), high: find!(high), flag, state: find!(state) }, - &ArrayArefFixnum { array, index } => ArrayArefFixnum { array: find!(array), index: find!(index) }, + &ArrayAref { array, index } => ArrayAref { array: find!(array), index: find!(index) }, &ArrayAset { array, index, val } => ArrayAset { array: find!(array), index: find!(index), val: find!(val) }, &ArrayPop { array, state } => ArrayPop { array: find!(array), state: find!(state) }, &ArrayLength { array } => ArrayLength { array: find!(array) }, @@ -2263,7 +2263,7 @@ impl Function { Insn::ToRegexp { .. } => types::RegexpExact, Insn::NewArray { .. } => types::ArrayExact, Insn::ArrayDup { .. } => types::ArrayExact, - Insn::ArrayArefFixnum { .. } => types::BasicObject, + Insn::ArrayAref { .. } => types::BasicObject, Insn::ArrayPop { .. } => types::BasicObject, Insn::ArrayLength { .. } => types::CInt64, Insn::HashAref { .. } => types::BasicObject, @@ -4073,11 +4073,11 @@ impl Function { _ => None, }) } - Insn::ArrayArefFixnum { array, index } if self.type_of(array).ruby_object_known() + Insn::ArrayAref { array, index } if self.type_of(array).ruby_object_known() && self.type_of(index).ruby_object_known() => { let array_obj = self.type_of(array).ruby_object().unwrap(); if array_obj.is_frozen() { - let index = self.type_of(index).fixnum_value().unwrap(); + let index = self.type_of(index).cint64_value().unwrap(); let val = unsafe { rb_yarv_ary_entry_internal(array_obj, index) }; self.new_insn(Insn::Const { val: Const::Value(val) }) } else { @@ -4271,7 +4271,7 @@ impl Function { worklist.push_back(val); worklist.push_back(state); } - &Insn::ArrayArefFixnum { array, index } => { + &Insn::ArrayAref { array, index } => { worklist.push_back(array); worklist.push_back(index); } @@ -4995,9 +4995,9 @@ impl Function { | Insn::ArrayLength { array, .. } => { self.assert_subtype(insn_id, array, types::Array) } - Insn::ArrayArefFixnum { array, index } => { + Insn::ArrayAref { array, index } => { self.assert_subtype(insn_id, array, types::Array)?; - self.assert_subtype(insn_id, index, types::Fixnum) + self.assert_subtype(insn_id, index, types::CInt64) } Insn::ArrayAset { array, index, .. } => { self.assert_subtype(insn_id, array, types::ArrayExact)?; @@ -6544,7 +6544,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // TODO(max): Add a short-cut path for long indices into an array where the // index is known to be in-bounds let index = fun.push_insn(block, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(i.try_into().unwrap())) }); - let element = fun.push_insn(block, Insn::ArrayArefFixnum { array, index }); + let index = fun.push_insn(block, Insn::UnboxFixnum { val: index }); + let element = fun.push_insn(block, Insn::ArrayAref { array, index }); state.stack_push(element); } } diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 138bbde7181ed1..4086a57833ea16 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -1606,10 +1606,11 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact - v26:BasicObject = ArrayArefFixnum v25, v14 + v26:CInt64 = UnboxFixnum v14 + v27:BasicObject = ArrayAref v25, v26 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v27 "); assert_snapshot!(inspect("test [1,2,3]"), @"1"); } @@ -4734,10 +4735,11 @@ mod hir_opt_tests { v13:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - v27:BasicObject = ArrayArefFixnum v23, v13 + v27:CInt64 = UnboxFixnum v13 + v28:BasicObject = ArrayAref v23, v27 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v28 "); // TODO(max): Check the result of `S[0] = 5; test` using `inspect` to make sure that we // actually do the load at run-time. @@ -4763,10 +4765,11 @@ mod hir_opt_tests { v13:Fixnum[1] = Const Value(1) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v26:Fixnum[5] = Const Value(5) + v24:CInt64 = UnboxFixnum v13 + v25:BasicObject = ArrayAref v11, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v25 "); } @@ -4790,10 +4793,11 @@ mod hir_opt_tests { v13:Fixnum[-3] = Const Value(-3) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v26:Fixnum[4] = Const Value(4) + v24:CInt64 = UnboxFixnum v13 + v25:BasicObject = ArrayAref v11, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v25 "); } @@ -4817,10 +4821,11 @@ mod hir_opt_tests { v13:Fixnum[-10] = Const Value(-10) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v26:NilClass = Const Value(nil) + v24:CInt64 = UnboxFixnum v13 + v25:BasicObject = ArrayAref v11, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v25 "); } @@ -4844,10 +4849,11 @@ mod hir_opt_tests { v13:Fixnum[10] = Const Value(10) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v26:NilClass = Const Value(nil) + v24:CInt64 = UnboxFixnum v13 + v25:BasicObject = ArrayAref v11, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v25 "); } @@ -6735,10 +6741,11 @@ mod hir_opt_tests { v19:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v30:BasicObject = ArrayArefFixnum v14, v19 + v30:CInt64 = UnboxFixnum v19 + v31:BasicObject = ArrayAref v14, v30 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v30 + Return v31 "); } @@ -6766,10 +6773,11 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v27:ArrayExact = GuardType v11, ArrayExact v28:Fixnum = GuardType v12, Fixnum - v29:BasicObject = ArrayArefFixnum v27, v28 + v29:CInt64 = UnboxFixnum v28 + v30:BasicObject = ArrayAref v27, v29 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v29 + Return v30 "); } @@ -6798,10 +6806,11 @@ mod hir_opt_tests { PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) v27:ArraySubclass[class_exact:C] = GuardType v11, ArraySubclass[class_exact:C] v28:Fixnum = GuardType v12, Fixnum - v29:BasicObject = ArrayArefFixnum v27, v28 + v29:CInt64 = UnboxFixnum v28 + v30:BasicObject = ArrayAref v27, v29 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v29 + Return v30 "); } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 14a77776233ce5..6a26b6905155d7 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -3566,9 +3566,11 @@ pub mod hir_build_tests { v22:CInt64 = ArrayLength v21 v23:CInt64[2] = GuardBitEquals v22, CInt64(2) v24:Fixnum[1] = Const Value(1) - v25:BasicObject = ArrayArefFixnum v21, v24 - v26:Fixnum[0] = Const Value(0) - v27:BasicObject = ArrayArefFixnum v21, v26 + v25:CInt64 = UnboxFixnum v24 + v26:BasicObject = ArrayAref v21, v25 + v27:Fixnum[0] = Const Value(0) + v28:CInt64 = UnboxFixnum v27 + v29:BasicObject = ArrayAref v21, v28 PatchPoint NoEPEscape(test) CheckInterrupts Return v13 diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index 061fe1657885a4..d339674d98ce83 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -395,6 +395,17 @@ impl Type { } } + pub fn cint64_value(&self) -> Option { + if self.is_subtype(types::CInt64) { + match self.spec { + Specialization::Int(val) => Some(val as i64), + _ => None, + } + } else { + None + } + } + /// Return true if the Type has object specialization and false otherwise. pub fn ruby_object_known(&self) -> bool { matches!(self.spec, Specialization::Object(_)) From 256c806a1d7adfeeec1b97eecc66448122fdecfb Mon Sep 17 00:00:00 2001 From: nozomemein Date: Sat, 10 Jan 2026 09:13:49 +0900 Subject: [PATCH 20/22] ZJIT: Write the result of UnboxFixnum HIR as type spec so that we can make use of it later (e.g. fold_constants for ArrayAref) --- zjit/src/hir.rs | 25 ++++++++++++++++--------- zjit/src/hir/opt_tests.rs | 36 ++++++++++++++++++------------------ zjit/src/hir/tests.rs | 4 ++-- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 4a81ce641cf68e..c3948f89e31bd8 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2251,7 +2251,13 @@ impl Function { Insn::IsBitNotEqual { .. } => types::CBool, Insn::BoxBool { .. } => types::BoolExact, Insn::BoxFixnum { .. } => types::Fixnum, - Insn::UnboxFixnum { .. } => types::CInt64, + Insn::UnboxFixnum { val } => { + if let Some(fixnum) = self.type_of(*val).fixnum_value() { + Type::from_cint(types::CInt64, fixnum) + } else { + types::CInt64 + } + }, Insn::FixnumAref { .. } => types::Fixnum, Insn::StringCopy { .. } => types::StringExact, Insn::StringIntern { .. } => types::Symbol, @@ -4073,15 +4079,16 @@ impl Function { _ => None, }) } - Insn::ArrayAref { array, index } if self.type_of(array).ruby_object_known() - && self.type_of(index).ruby_object_known() => { + Insn::ArrayAref { array, index } + if self.type_of(array).ruby_object_known() + && self.type_of(index).is_subtype(types::CInt64) => { let array_obj = self.type_of(array).ruby_object().unwrap(); - if array_obj.is_frozen() { - let index = self.type_of(index).cint64_value().unwrap(); - let val = unsafe { rb_yarv_ary_entry_internal(array_obj, index) }; - self.new_insn(Insn::Const { val: Const::Value(val) }) - } else { - insn_id + match (array_obj.is_frozen(), self.type_of(index).cint64_value()) { + (true, Some(index)) => { + let val = unsafe { rb_yarv_ary_entry_internal(array_obj, index) }; + self.new_insn(Insn::Const { val: Const::Value(val) }) + } + _ => insn_id, } } Insn::Test { val } if self.type_of(val).is_known_falsy() => { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 4086a57833ea16..3f8899d61ca0fc 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -1606,7 +1606,7 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact - v26:CInt64 = UnboxFixnum v14 + v26:CInt64[0] = UnboxFixnum v14 v27:BasicObject = ArrayAref v25, v26 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -4735,7 +4735,7 @@ mod hir_opt_tests { v13:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - v27:CInt64 = UnboxFixnum v13 + v27:CInt64[0] = UnboxFixnum v13 v28:BasicObject = ArrayAref v23, v27 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -4765,11 +4765,11 @@ mod hir_opt_tests { v13:Fixnum[1] = Const Value(1) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v24:CInt64 = UnboxFixnum v13 - v25:BasicObject = ArrayAref v11, v24 + v24:CInt64[1] = UnboxFixnum v13 + v27:Fixnum[5] = Const Value(5) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v25 + Return v27 "); } @@ -4793,11 +4793,11 @@ mod hir_opt_tests { v13:Fixnum[-3] = Const Value(-3) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v24:CInt64 = UnboxFixnum v13 - v25:BasicObject = ArrayAref v11, v24 + v24:CInt64[-3] = UnboxFixnum v13 + v27:Fixnum[4] = Const Value(4) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v25 + Return v27 "); } @@ -4821,11 +4821,11 @@ mod hir_opt_tests { v13:Fixnum[-10] = Const Value(-10) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v24:CInt64 = UnboxFixnum v13 - v25:BasicObject = ArrayAref v11, v24 + v24:CInt64[-10] = UnboxFixnum v13 + v27:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v25 + Return v27 "); } @@ -4849,11 +4849,11 @@ mod hir_opt_tests { v13:Fixnum[10] = Const Value(10) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v24:CInt64 = UnboxFixnum v13 - v25:BasicObject = ArrayAref v11, v24 + v24:CInt64[10] = UnboxFixnum v13 + v27:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v25 + Return v27 "); } @@ -6741,7 +6741,7 @@ mod hir_opt_tests { v19:Fixnum[0] = Const Value(0) PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) - v30:CInt64 = UnboxFixnum v19 + v30:CInt64[0] = UnboxFixnum v19 v31:BasicObject = ArrayAref v14, v30 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -7087,11 +7087,11 @@ mod hir_opt_tests { v31:ArrayExact = GuardType v9, ArrayExact v32:ArrayExact = GuardNotFrozen v31 v33:ArrayExact = GuardNotShared v32 - v34:CInt64 = UnboxFixnum v16 + v34:CInt64[1] = UnboxFixnum v16 v35:CInt64 = ArrayLength v33 - v36:CInt64 = GuardLess v34, v35 + v36:CInt64[1] = GuardLess v34, v35 v37:CInt64[0] = Const CInt64(0) - v38:CInt64 = GuardGreaterEq v36, v37 + v38:CInt64[1] = GuardGreaterEq v36, v37 ArrayAset v33, v38, v18 WriteBarrier v33, v18 IncrCounter inline_cfunc_optimized_send_count diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 6a26b6905155d7..b4bd9cbc31ac2f 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -3566,10 +3566,10 @@ pub mod hir_build_tests { v22:CInt64 = ArrayLength v21 v23:CInt64[2] = GuardBitEquals v22, CInt64(2) v24:Fixnum[1] = Const Value(1) - v25:CInt64 = UnboxFixnum v24 + v25:CInt64[1] = UnboxFixnum v24 v26:BasicObject = ArrayAref v21, v25 v27:Fixnum[0] = Const Value(0) - v28:CInt64 = UnboxFixnum v27 + v28:CInt64[0] = UnboxFixnum v27 v29:BasicObject = ArrayAref v21, v28 PatchPoint NoEPEscape(test) CheckInterrupts From 844f072ce16235c0f10df73bb79d40cc01223c28 Mon Sep 17 00:00:00 2001 From: nozomemein Date: Wed, 14 Jan 2026 15:04:46 +0900 Subject: [PATCH 21/22] ZJIT: Inline ArrayAref --- test/ruby/test_zjit.rb | 48 ++++++++++++++++++++++++++ zjit/src/codegen.rs | 7 +++- zjit/src/cruby_methods.rs | 4 +++ zjit/src/hir.rs | 15 ++++---- zjit/src/hir/opt_tests.rs | 72 +++++++++++++++++++++++++++++---------- zjit/src/hir_type/mod.rs | 10 ++---- 6 files changed, 121 insertions(+), 35 deletions(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index bc4f5f2ae8f063..6e46346d5af983 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -2160,6 +2160,54 @@ def test(x) = [1,2,3][x] }, call_threshold: 2, insns: [:opt_aref] end + def test_array_fixnum_aref_negative_index + assert_compiles '3', %q{ + def test(x) = [1,2,3][x] + test(-1) + test(-1) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_fixnum_aref_out_of_bounds_positive + assert_compiles 'nil', %q{ + def test(x) = [1,2,3][x] + test(10) + test(10) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_fixnum_aref_out_of_bounds_negative + assert_compiles 'nil', %q{ + def test(x) = [1,2,3][x] + test(-10) + test(-10) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_fixnum_aref_array_subclass + assert_compiles '3', %q{ + class MyArray < Array; end + def test(arr, idx) = arr[idx] + arr = MyArray[1,2,3] + test(arr, 2) + arr = MyArray[1,2,3] + test(arr, 2) + }, call_threshold: 2, insns: [:opt_aref] + end + + def test_array_aref_non_fixnum_index + assert_compiles 'TypeError', %q{ + def test(arr, idx) = arr[idx] + test([1,2,3], 1) + test([1,2,3], 1) + begin + test([1,2,3], "1") + rescue => e + e.class + end + }, call_threshold: 2 + end + def test_array_fixnum_aset assert_compiles '[1, 2, 7]', %q{ def test(arr, idx) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 25cccd1bd1df9e..1c453aed771055 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -1565,7 +1565,12 @@ fn gen_array_aref( array: Opnd, index: Opnd, ) -> lir::Opnd { - asm_ccall!(asm, rb_ary_entry, array, index) + let unboxed_idx = asm.load(index); + let array = asm.load(array); + let array_ptr = gen_array_ptr(asm, array); + let elem_offset = asm.lshift(unboxed_idx, Opnd::UImm(SIZEOF_VALUE.trailing_zeros() as u64)); + let elem_ptr = asm.add(array_ptr, elem_offset); + asm.load(Opnd::mem(VALUE_BITS, elem_ptr, 0)) } fn gen_array_aset( diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index 5f89a5f20e4be5..357c8b0c122b8d 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -327,6 +327,10 @@ fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::In if fun.likely_a(index, types::Fixnum, 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 }); + let index = fun.push_insn(block, hir::Insn::GuardLess { left: index, right: length, state }); + let zero = fun.push_insn(block, hir::Insn::Const { val: hir::Const::CInt64(0) }); + let index = fun.push_insn(block, hir::Insn::GuardGreaterEq { left: index, right: zero, state }); let result = fun.push_insn(block, hir::Insn::ArrayAref { array: recv, index }); return Some(result); } diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index c3948f89e31bd8..b4608d33808e0f 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -2251,13 +2251,10 @@ impl Function { Insn::IsBitNotEqual { .. } => types::CBool, Insn::BoxBool { .. } => types::BoolExact, Insn::BoxFixnum { .. } => types::Fixnum, - Insn::UnboxFixnum { val } => { - if let Some(fixnum) = self.type_of(*val).fixnum_value() { - Type::from_cint(types::CInt64, fixnum) - } else { - types::CInt64 - } - }, + Insn::UnboxFixnum { val } => self + .type_of(*val) + .fixnum_value() + .map_or(types::CInt64, |fixnum| Type::from_cint(types::CInt64, fixnum)), Insn::FixnumAref { .. } => types::Fixnum, Insn::StringCopy { .. } => types::StringExact, Insn::StringIntern { .. } => types::Symbol, @@ -6548,9 +6545,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let length = fun.push_insn(block, Insn::ArrayLength { array }); fun.push_insn(block, Insn::GuardBitEquals { val: length, expected: Const::CInt64(num as i64), reason: SideExitReason::ExpandArray, state: exit_id }); for i in (0..num).rev() { - // TODO(max): Add a short-cut path for long indices into an array where the - // index is known to be in-bounds let index = fun.push_insn(block, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(i.try_into().unwrap())) }); + // We do not emit a length guard here because in-bounds is already + // ensured by the expandarray length check above. let index = fun.push_insn(block, Insn::UnboxFixnum { val: index }); let element = fun.push_insn(block, Insn::ArrayAref { array, index }); state.stack_push(element); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 3f8899d61ca0fc..08aa4474219032 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -1607,10 +1607,14 @@ mod hir_opt_tests { PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact v26:CInt64[0] = UnboxFixnum v14 - v27:BasicObject = ArrayAref v25, v26 + v27:CInt64 = ArrayLength v25 + v28:CInt64[0] = GuardLess v26, v27 + v29:CInt64[0] = Const CInt64(0) + v30:CInt64[0] = GuardGreaterEq v28, v29 + v31:BasicObject = ArrayAref v25, v30 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v31 "); assert_snapshot!(inspect("test [1,2,3]"), @"1"); } @@ -4736,10 +4740,14 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) v27:CInt64[0] = UnboxFixnum v13 - v28:BasicObject = ArrayAref v23, v27 + 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 v28 + 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. @@ -4766,10 +4774,14 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v24:CInt64[1] = UnboxFixnum v13 - v27:Fixnum[5] = Const Value(5) + v25:CInt64 = ArrayLength v11 + v26:CInt64[1] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[1] = GuardGreaterEq v26, v27 + v31:Fixnum[5] = Const Value(5) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v31 "); } @@ -4794,10 +4806,14 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v24:CInt64[-3] = UnboxFixnum v13 - v27:Fixnum[4] = Const Value(4) + v25:CInt64 = ArrayLength v11 + v26:CInt64[-3] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[-3] = GuardGreaterEq v26, v27 + v31:Fixnum[4] = Const Value(4) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v31 "); } @@ -4822,10 +4838,14 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v24:CInt64[-10] = UnboxFixnum v13 - v27:NilClass = Const Value(nil) + v25:CInt64 = ArrayLength v11 + v26:CInt64[-10] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[-10] = GuardGreaterEq v26, v27 + v31:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v31 "); } @@ -4850,10 +4870,14 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v24:CInt64[10] = UnboxFixnum v13 - v27:NilClass = Const Value(nil) + v25:CInt64 = ArrayLength v11 + v26:CInt64[10] = GuardLess v24, v25 + v27:CInt64[0] = Const CInt64(0) + v28:CInt64[10] = GuardGreaterEq v26, v27 + v31:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v31 "); } @@ -6742,10 +6766,14 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v30:CInt64[0] = UnboxFixnum v19 - v31:BasicObject = ArrayAref v14, v30 + v31:CInt64 = ArrayLength v14 + v32:CInt64[0] = GuardLess v30, v31 + v33:CInt64[0] = Const CInt64(0) + v34:CInt64[0] = GuardGreaterEq v32, v33 + v35:BasicObject = ArrayAref v14, v34 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v31 + Return v35 "); } @@ -6774,10 +6802,14 @@ mod hir_opt_tests { v27:ArrayExact = GuardType v11, ArrayExact v28:Fixnum = GuardType v12, Fixnum v29:CInt64 = UnboxFixnum v28 - v30:BasicObject = ArrayAref v27, v29 + 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 v30 + Return v34 "); } @@ -6807,10 +6839,14 @@ mod hir_opt_tests { v27:ArraySubclass[class_exact:C] = GuardType v11, ArraySubclass[class_exact:C] v28:Fixnum = GuardType v12, Fixnum v29:CInt64 = UnboxFixnum v28 - v30:BasicObject = ArrayAref v27, v29 + 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 v30 + Return v34 "); } diff --git a/zjit/src/hir_type/mod.rs b/zjit/src/hir_type/mod.rs index d339674d98ce83..cc6a208bcd413e 100644 --- a/zjit/src/hir_type/mod.rs +++ b/zjit/src/hir_type/mod.rs @@ -396,13 +396,9 @@ impl Type { } pub fn cint64_value(&self) -> Option { - if self.is_subtype(types::CInt64) { - match self.spec { - Specialization::Int(val) => Some(val as i64), - _ => None, - } - } else { - None + match (self.is_subtype(types::CInt64), &self.spec) { + (true, Specialization::Int(val)) => Some(*val as i64), + _ => None, } } From ce390f19954edda3874ed62768440953b40be0da Mon Sep 17 00:00:00 2001 From: nozomemein Date: Thu, 15 Jan 2026 07:18:31 +0900 Subject: [PATCH 22/22] ZJIT: Remove redundant unboxing --- zjit/src/hir.rs | 3 +-- zjit/src/hir/tests.rs | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index b4608d33808e0f..97a0097073d7bd 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6545,10 +6545,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let length = fun.push_insn(block, Insn::ArrayLength { array }); fun.push_insn(block, Insn::GuardBitEquals { val: length, expected: Const::CInt64(num as i64), reason: SideExitReason::ExpandArray, state: exit_id }); for i in (0..num).rev() { - let index = fun.push_insn(block, Insn::Const { val: Const::Value(VALUE::fixnum_from_usize(i.try_into().unwrap())) }); // We do not emit a length guard here because in-bounds is already // ensured by the expandarray length check above. - let index = fun.push_insn(block, Insn::UnboxFixnum { val: index }); + let index = fun.push_insn(block, Insn::Const { val: Const::CInt64(i.try_into().unwrap()) }); let element = fun.push_insn(block, Insn::ArrayAref { array, index }); state.stack_push(element); } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index b4bd9cbc31ac2f..1ce5488f4731a7 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -3565,12 +3565,10 @@ pub mod hir_build_tests { v21:ArrayExact = GuardType v13, ArrayExact v22:CInt64 = ArrayLength v21 v23:CInt64[2] = GuardBitEquals v22, CInt64(2) - v24:Fixnum[1] = Const Value(1) - v25:CInt64[1] = UnboxFixnum v24 - v26:BasicObject = ArrayAref v21, v25 - v27:Fixnum[0] = Const Value(0) - v28:CInt64[0] = UnboxFixnum v27 - v29:BasicObject = ArrayAref v21, v28 + v24:CInt64[1] = Const CInt64(1) + v25:BasicObject = ArrayAref v21, v24 + v26:CInt64[0] = Const CInt64(0) + v27:BasicObject = ArrayAref v21, v26 PatchPoint NoEPEscape(test) CheckInterrupts Return v13