diff options
| author | Stan Lo <stan.lo@shopify.com> | 2025-10-02 17:03:25 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-02 09:03:25 -0700 |
| commit | 2ed5a02fcca4da4acf4c8c3d7ee4c392fc18d948 (patch) | |
| tree | 834a2702d97afc92dcd2c905cace45cff46be5e2 | |
| parent | 81f253577a77a934bfa02a33d80ca2a7c6af9a04 (diff) | |
ZJIT: Add `NoSingletonClass` patch point (#14680)
* ZJIT: Add NoSingletonClass patch point
This patch point makes sure that when the object has a singleton class,
the JIT code is invalidated. As of now, this is only needed for C call
optimization.
In YJIT, the singleton class guard only applies to Array, Hash, and String.
But in ZJIT, we may optimize C calls from gems (e.g. `sqlite3`). So the
patch point needs to be applied to a broader range of classes.
* ZJIT: Only generate NoSingletonClass guard when the type can have singleton class
* ZJIT: Update or forget NoSingletonClass patch point when needed
| -rw-r--r-- | class.c | 2 | ||||
| -rw-r--r-- | depend | 1 | ||||
| -rw-r--r-- | gc.c | 3 | ||||
| -rw-r--r-- | test/ruby/test_zjit.rb | 59 | ||||
| -rw-r--r-- | zjit.h | 3 | ||||
| -rw-r--r-- | zjit/src/codegen.rs | 8 | ||||
| -rw-r--r-- | zjit/src/cruby.rs | 10 | ||||
| -rw-r--r-- | zjit/src/gc.rs | 10 | ||||
| -rw-r--r-- | zjit/src/hir.rs | 331 | ||||
| -rw-r--r-- | zjit/src/invariants.rs | 55 |
10 files changed, 346 insertions, 136 deletions
@@ -31,6 +31,7 @@ #include "ruby/st.h" #include "vm_core.h" #include "yjit.h" +#include "zjit.h" /* Flags of T_CLASS * @@ -1309,6 +1310,7 @@ make_singleton_class(VALUE obj) RBASIC_SET_CLASS(obj, klass); rb_singleton_class_attached(klass, obj); rb_yjit_invalidate_no_singleton_class(orig_class); + rb_zjit_invalidate_no_singleton_class(orig_class); SET_METACLASS_OF(klass, METACLASS_OF(rb_class_real(orig_class))); return klass; @@ -1170,6 +1170,7 @@ class.$(OBJEXT): {$(VPATH)}vm_debug.h class.$(OBJEXT): {$(VPATH)}vm_opts.h class.$(OBJEXT): {$(VPATH)}vm_sync.h class.$(OBJEXT): {$(VPATH)}yjit.h +class.$(OBJEXT): {$(VPATH)}zjit.h compar.$(OBJEXT): $(hdrdir)/ruby/ruby.h compar.$(OBJEXT): $(hdrdir)/ruby/version.h compar.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h @@ -1311,6 +1311,9 @@ rb_gc_obj_free(void *objspace, VALUE obj) break; case T_MODULE: case T_CLASS: +#if USE_ZJIT + rb_zjit_klass_free(obj); +#endif args.klass = obj; rb_class_classext_foreach(obj, classext_free, (void *)&args); if (RCLASS_CLASSEXT_TBL(obj)) { diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 937cf44e19..83af7347d6 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -2912,6 +2912,65 @@ class TestZJIT < Test::Unit::TestCase }, call_threshold: 2, insns: [:opt_new] end + def test_singleton_class_invalidation_annotated_ccall + assert_compiles '[false, true]', %q{ + def define_singleton(obj, define) + if define + # Wrap in C method frame to avoid exiting JIT on defineclass + [nil].reverse_each do + class << obj + def ==(_) + true + end + end + end + end + false + end + + def test(define) + obj = BasicObject.new + # This == call gets compiled to a CCall + obj == define_singleton(obj, define) + end + + result = [] + result << test(false) # Compiles BasicObject#== + result << test(true) # Should use singleton#== now + result + }, call_threshold: 2 + end + + def test_singleton_class_invalidation_optimized_variadic_ccall + assert_compiles '[1, 1000]', %q{ + def define_singleton(arr, define) + if define + # Wrap in C method frame to avoid exiting JIT on defineclass + [nil].reverse_each do + class << arr + def push(x) + super(x * 1000) + end + end + end + end + 1 + end + + def test(define) + arr = [] + val = define_singleton(arr, define) + arr.push(val) # This CCall should be invalidated if singleton was defined + arr[0] + end + + result = [] + result << test(false) # Compiles Array#push as CCall + result << test(true) # Singleton defined, CCall should be invalidated + result + }, call_threshold: 2 + end + private # Assert that every method call in `test_script` can be compiled by ZJIT @@ -19,6 +19,7 @@ void rb_zjit_profile_enable(const rb_iseq_t *iseq); void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop); void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme); void rb_zjit_cme_free(const rb_callable_method_entry_t *cme); +void rb_zjit_klass_free(VALUE klass); void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq); void rb_zjit_constant_state_changed(ID id); void rb_zjit_iseq_mark(void *payload); @@ -26,6 +27,7 @@ void rb_zjit_iseq_update_references(void *payload); void rb_zjit_iseq_free(const rb_iseq_t *iseq); void rb_zjit_before_ractor_spawn(void); void rb_zjit_tracing_invalidate_all(void); +void rb_zjit_invalidate_no_singleton_class(VALUE klass); #else #define rb_zjit_enabled_p false static inline void rb_zjit_compile_iseq(const rb_iseq_t *iseq, bool jit_exception) {} @@ -37,6 +39,7 @@ static inline void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq) {} static inline void rb_zjit_constant_state_changed(ID id) {} static inline void rb_zjit_before_ractor_spawn(void) {} static inline void rb_zjit_tracing_invalidate_all(void) {} +static inline void rb_zjit_invalidate_no_singleton_class(VALUE klass) {} #endif // #if USE_ZJIT #endif // #ifndef ZJIT_H diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 23631ae6ba..b7c3dc3532 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -9,7 +9,10 @@ use std::slice; use crate::asm::Label; use crate::backend::current::{Reg, ALLOC_REGS}; -use crate::invariants::{track_bop_assumption, track_cme_assumption, track_no_ep_escape_assumption, track_no_trace_point_assumption, track_single_ractor_assumption, track_stable_constant_names_assumption}; +use crate::invariants::{ + track_bop_assumption, track_cme_assumption, track_no_ep_escape_assumption, track_no_trace_point_assumption, + track_single_ractor_assumption, track_stable_constant_names_assumption, track_no_singleton_class_assumption +}; use crate::gc::{append_gc_offsets, get_or_create_iseq_payload, get_or_create_iseq_payload_ptr, IseqCodePtrs, IseqPayload, IseqStatus}; use crate::state::ZJITState; use crate::stats::{send_fallback_counter, exit_counter_for_compile_error, incr_counter, incr_counter_by, send_fallback_counter_for_method_type, send_fallback_counter_ptr_for_opcode, CompileError}; @@ -654,6 +657,9 @@ fn gen_patch_point(jit: &mut JITState, asm: &mut Assembler, invariant: &Invarian Invariant::SingleRactorMode => { track_single_ractor_assumption(code_ptr, side_exit_ptr, payload_ptr); } + Invariant::NoSingletonClass { klass } => { + track_no_singleton_class_assumption(klass, code_ptr, side_exit_ptr, payload_ptr); + } } }); } diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index baba0992cc..1f514787f1 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -450,6 +450,16 @@ impl VALUE { self.static_sym_p() || self.dynamic_sym_p() } + pub fn instance_can_have_singleton_class(self) -> bool { + if self == unsafe { rb_cInteger } || self == unsafe { rb_cFloat } || + self == unsafe { rb_cSymbol } || self == unsafe { rb_cNilClass } || + self == unsafe { rb_cTrueClass } || self == unsafe { rb_cFalseClass } { + + return false + } + true + } + /// Return true for a static (non-heap) Ruby symbol (RB_STATIC_SYM_P) pub fn static_sym_p(self) -> bool { let VALUE(cval) = self; diff --git a/zjit/src/gc.rs b/zjit/src/gc.rs index 0974c5bfce..934e1e8dca 100644 --- a/zjit/src/gc.rs +++ b/zjit/src/gc.rs @@ -148,6 +148,16 @@ pub extern "C" fn rb_zjit_cme_free(cme: *const rb_callable_method_entry_struct) invariants.forget_cme(cme); } +/// GC callback for finalizing a class +#[unsafe(no_mangle)] +pub extern "C" fn rb_zjit_klass_free(klass: VALUE) { + if !ZJITState::has_instance() { + return; + } + let invariants = ZJITState::get_invariants(); + invariants.forget_klass(klass); +} + /// GC callback for updating object references after all object moves #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_root_update_references() { diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 5588544b4f..4c123f5621 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -141,6 +141,11 @@ pub enum Invariant { NoEPEscape(IseqPtr), /// There is one ractor running. If a non-root ractor gets spawned, this is invalidated. SingleRactorMode, + /// Objects of this class have no singleton class. + /// When a singleton class is created for an object of this class, this is invalidated. + NoSingletonClass { + klass: VALUE, + }, } impl Invariant { @@ -255,6 +260,12 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> { Invariant::NoTracePoint => write!(f, "NoTracePoint"), Invariant::NoEPEscape(iseq) => write!(f, "NoEPEscape({})", &iseq_name(iseq)), Invariant::SingleRactorMode => write!(f, "SingleRactorMode"), + Invariant::NoSingletonClass { klass } => { + let class_name = get_class_name(klass); + write!(f, "NoSingletonClass({}@{:p})", + class_name, + self.ptr_map.map_ptr(klass.as_ptr::<VALUE>())) + } } } } @@ -2021,6 +2032,9 @@ impl Function { 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 }); } @@ -2028,6 +2042,9 @@ impl Function { self.make_equal_to(insn_id, send_direct); } else if def_type == VM_METHOD_TYPE_IVAR && args.is_empty() { 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 }); } @@ -2097,6 +2114,7 @@ impl Function { }; if recv_type.is_string() { + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_type.class() }, state }); let guard = self.push_insn(block, Insn::GuardType { val, guard_type: types::String, state }); // Infer type so AnyToString can fold off this self.insn_types[guard.0] = self.infer_type(guard); @@ -2224,6 +2242,11 @@ impl Function { /// Optimize SendWithoutBlock that land in a C method to a direct CCall without /// runtime lookup. fn optimize_c_calls(&mut self) { + fn gen_patch_points_for_optimized_ccall(fun: &mut Function, block: BlockId, recv_class: VALUE, method_id: ID, method: *const rb_callable_method_entry_struct, state: InsnId) { + fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoTracePoint, state }); + fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass: recv_class, method: method_id, cme: method }, state }); + } + // Try to reduce one SendWithoutBlock to a CCall fn reduce_to_ccall( fun: &mut Function, @@ -2285,12 +2308,16 @@ impl Function { let ci_flags = unsafe { vm_ci_flag(call_info) }; // Filter for simple call sites (i.e. no splats etc.) if ci_flags & VM_CALL_ARGS_SIMPLE != 0 { - // Commit to the replacement. Put PatchPoint. - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass: recv_class, method: method_id, cme: method }, state }); + gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, 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 }); } + let cfun = unsafe { get_mct_func(cfunc) }.cast(); let mut cfunc_args = vec![recv]; cfunc_args.append(&mut args); @@ -2308,16 +2335,11 @@ impl Function { // func(int argc, VALUE *argv, VALUE recv) let ci_flags = unsafe { vm_ci_flag(call_info) }; if ci_flags & VM_CALL_ARGS_SIMPLE != 0 { - fun.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoTracePoint, state }); - fun.push_insn(block, Insn::PatchPoint { - invariant: Invariant::MethodRedefined { - klass: recv_class, - method: method_id, - cme: method - }, - state - }); + gen_patch_points_for_optimized_ccall(fun, block, recv_class, method_id, method, 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 }); @@ -8448,10 +8470,11 @@ mod opt_tests { Jump bb2(v4) bb2(v6:BasicObject): 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) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v20:BasicObject = SendWithoutBlockDirect v19, :foo (0x1038) CheckInterrupts - Return v19 + Return v20 "); } @@ -8504,10 +8527,11 @@ mod opt_tests { Jump bb2(v4) bb2(v6:BasicObject): 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) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v20:BasicObject = SendWithoutBlockDirect v19, :foo (0x1038) CheckInterrupts - Return v19 + Return v20 "); } @@ -8531,10 +8555,11 @@ mod opt_tests { bb2(v6:BasicObject): v10:Fixnum[3] = Const Value(3) PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) - v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v20:BasicObject = SendWithoutBlockDirect v19, :Integer (0x1038), v10 + PatchPoint NoSingletonClass(Object@0x1000) + v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v21:BasicObject = SendWithoutBlockDirect v20, :Integer (0x1038), v10 CheckInterrupts - Return v20 + Return v21 "); } @@ -8561,10 +8586,11 @@ mod opt_tests { v10:Fixnum[1] = Const Value(1) v11:Fixnum[2] = Const Value(2) 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), v10, v11 + PatchPoint NoSingletonClass(Object@0x1000) + v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v22:BasicObject = SendWithoutBlockDirect v21, :foo (0x1038), v10, v11 CheckInterrupts - Return v21 + Return v22 "); } @@ -8592,13 +8618,15 @@ mod opt_tests { Jump bb2(v4) bb2(v6:BasicObject): 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) + PatchPoint NoSingletonClass(Object@0x1000) + 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) - v25:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] - v26:BasicObject = SendWithoutBlockDirect v25, :bar (0x1038) + PatchPoint NoSingletonClass(Object@0x1000) + v27:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v28:BasicObject = SendWithoutBlockDirect v27, :bar (0x1038) CheckInterrupts - Return v26 + Return v28 "); } @@ -8623,10 +8651,11 @@ mod opt_tests { v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v10 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 puts@0x1040, v22, v12 + PatchPoint NoSingletonClass(Object@0x1008) + v23:HeapObject[class_exact*:Object@VALUE(0x1008)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1008)] + v24:BasicObject = CCallVariadic puts@0x1040, v23, v12 CheckInterrupts - Return v23 + Return v24 "); } @@ -9652,10 +9681,10 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) - v21:Fixnum = GuardType v9, Fixnum - v22:BasicObject = CCall itself@0x1038, v21 + v22:Fixnum = GuardType v9, Fixnum + v23:BasicObject = CCall itself@0x1038, v22 CheckInterrupts - Return v22 + Return v23 "); } @@ -9676,9 +9705,10 @@ mod opt_tests { bb2(v6:BasicObject): v11:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) - v20:BasicObject = CCall itself@0x1038, v11 + PatchPoint NoSingletonClass(Array@0x1000) + v22:BasicObject = CCall itself@0x1038, v11 CheckInterrupts - Return v20 + Return v22 "); } @@ -9704,7 +9734,8 @@ mod opt_tests { bb2(v8:BasicObject, v9:NilClass): v14:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) - v28:BasicObject = CCall itself@0x1038, v14 + PatchPoint NoSingletonClass(Array@0x1000) + v30:BasicObject = CCall itself@0x1038, v14 PatchPoint NoEPEscape(test) v21:Fixnum[1] = Const Value(1) CheckInterrupts @@ -9738,7 +9769,8 @@ mod opt_tests { PatchPoint StableConstantNames(0x1000, M) v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) - v31:StringExact|NilClass = CCall name@0x1048, v29 + PatchPoint NoSingletonClass(Module@0x1010) + v33:StringExact|NilClass = CCall name@0x1048, v29 PatchPoint NoEPEscape(test) v21:Fixnum[1] = Const Value(1) CheckInterrupts @@ -9768,7 +9800,8 @@ mod opt_tests { bb2(v8:BasicObject, v9:NilClass): v14:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) - v28:Fixnum = CCall length@0x1038, v14 + PatchPoint NoSingletonClass(Array@0x1000) + v30:Fixnum = CCall length@0x1038, v14 v21:Fixnum[5] = Const Value(5) CheckInterrupts Return v21 @@ -9910,7 +9943,8 @@ mod opt_tests { bb2(v8:BasicObject, v9:NilClass): v14:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) - v28:Fixnum = CCall size@0x1038, v14 + PatchPoint NoSingletonClass(Array@0x1000) + v30:Fixnum = CCall size@0x1038, v14 v21:Fixnum[5] = Const Value(5) CheckInterrupts Return v21 @@ -9991,9 +10025,10 @@ mod opt_tests { v16:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v18:ArrayExact = ArrayDup v16 PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) - v29:BasicObject = SendWithoutBlockDirect v18, :first (0x1040) + PatchPoint NoSingletonClass(Array@0x1008) + v30:BasicObject = SendWithoutBlockDirect v18, :first (0x1040) CheckInterrupts - Return v29 + Return v30 "); } @@ -10019,9 +10054,10 @@ mod opt_tests { PatchPoint StableConstantNames(0x1000, M) v21:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) - v23:BasicObject = SendWithoutBlockDirect v21, :class (0x1048) + PatchPoint NoSingletonClass(Module@0x1010) + v24:BasicObject = SendWithoutBlockDirect v21, :class (0x1048) CheckInterrupts - Return v23 + Return v24 "); } @@ -10052,10 +10088,11 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): 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) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v23:BasicObject = SendWithoutBlockDirect v22, :foo (0x1038) CheckInterrupts - Return v22 + Return v23 "); } @@ -10233,9 +10270,10 @@ mod opt_tests { v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v10 PatchPoint MethodRedefined(String@0x1008, bytesize@0x1010, cme:0x1018) - v21:Fixnum = CCall bytesize@0x1040, v12 + PatchPoint NoSingletonClass(String@0x1008) + v23:Fixnum = CCall bytesize@0x1040, v12 CheckInterrupts - Return v21 + Return v23 "); } @@ -10361,7 +10399,8 @@ mod opt_tests { PatchPoint MethodRedefined(C@0x1008, new@0x1010, cme:0x1018) v43:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1040, cme:0x1048) - v45:NilClass = CCall initialize@0x1070, v43 + PatchPoint NoSingletonClass(C@0x1008) + v47:NilClass = CCall initialize@0x1070, v43 CheckInterrupts CheckInterrupts Return v43 @@ -10397,7 +10436,8 @@ mod opt_tests { PatchPoint MethodRedefined(C@0x1008, new@0x1010, cme:0x1018) v45:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1040, cme:0x1048) - v47:BasicObject = SendWithoutBlockDirect v45, :initialize (0x1070), v13 + PatchPoint NoSingletonClass(C@0x1008) + v48:BasicObject = SendWithoutBlockDirect v45, :initialize (0x1070), v13 CheckInterrupts CheckInterrupts Return v45 @@ -10427,7 +10467,8 @@ mod opt_tests { PatchPoint MethodRedefined(Object@0x1008, new@0x1010, cme:0x1018) v43:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) PatchPoint MethodRedefined(Object@0x1008, initialize@0x1040, cme:0x1048) - v45:NilClass = CCall initialize@0x1070, v43 + PatchPoint NoSingletonClass(Object@0x1008) + v47:NilClass = CCall initialize@0x1070, v43 CheckInterrupts CheckInterrupts Return v43 @@ -10457,7 +10498,8 @@ mod opt_tests { PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1010, cme:0x1018) v43:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1040, cme:0x1048) - v45:NilClass = CCall initialize@0x1070, v43 + PatchPoint NoSingletonClass(BasicObject@0x1008) + v47:NilClass = CCall initialize@0x1070, v43 CheckInterrupts CheckInterrupts Return v43 @@ -10517,9 +10559,10 @@ mod opt_tests { v13:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Array@0x1008, new@0x1010, cme:0x1018) PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) - v51:BasicObject = CCallVariadic new@0x1048, v42, v13 + PatchPoint NoSingletonClass(Class@0x1040) + v53:BasicObject = CCallVariadic new@0x1048, v42, v13 CheckInterrupts - Return v51 + Return v53 "); } @@ -10546,8 +10589,9 @@ mod opt_tests { PatchPoint MethodRedefined(Set@0x1008, new@0x1010, cme:0x1018) v16:HeapObject = ObjectAlloc v40 PatchPoint MethodRedefined(Set@0x1008, initialize@0x1040, cme:0x1048) - v45:SetExact = GuardType v16, SetExact - v46:BasicObject = CCallVariadic initialize@0x1070, v45 + PatchPoint NoSingletonClass(Set@0x1008) + v46:SetExact = GuardType v16, SetExact + v47:BasicObject = CCallVariadic initialize@0x1070, v46 CheckInterrupts CheckInterrupts Return v16 @@ -10576,9 +10620,10 @@ mod opt_tests { v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(String@0x1008, new@0x1010, cme:0x1018) PatchPoint MethodRedefined(Class@0x1040, new@0x1010, cme:0x1018) - v49:BasicObject = CCallVariadic new@0x1048, v40 + PatchPoint NoSingletonClass(Class@0x1040) + v51:BasicObject = CCallVariadic new@0x1048, v40 CheckInterrupts - Return v49 + Return v51 "); } @@ -10607,7 +10652,8 @@ mod opt_tests { PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) v47:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) - v50:BasicObject = CCallVariadic initialize@0x1078, v47, v15 + PatchPoint NoSingletonClass(Regexp@0x1008) + v51:BasicObject = CCallVariadic initialize@0x1078, v47, v15 CheckInterrupts CheckInterrupts Return v47 @@ -10633,9 +10679,10 @@ mod opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:ArrayExact = NewArray v11, v12 PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) - v28:Fixnum = CCall length@0x1038, v17 + PatchPoint NoSingletonClass(Array@0x1000) + v30:Fixnum = CCall length@0x1038, v17 CheckInterrupts - Return v28 + Return v30 "); } @@ -10658,9 +10705,10 @@ mod opt_tests { bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:ArrayExact = NewArray v11, v12 PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) - v28:Fixnum = CCall size@0x1038, v17 + PatchPoint NoSingletonClass(Array@0x1000) + v30:Fixnum = CCall size@0x1038, v17 CheckInterrupts - Return v28 + Return v30 "); } @@ -11169,8 +11217,9 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v25:String = GuardType v9, String - v19:StringExact = StringConcat v13, v25 + PatchPoint NoSingletonClass(String@0x1008) + v26:String = GuardType v9, String + v19:StringExact = StringConcat v13, v26 CheckInterrupts Return v19 "); @@ -11200,8 +11249,9 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v25:String = GuardType v9, String - v19:StringExact = StringConcat v13, v25 + PatchPoint NoSingletonClass(MyString@0x1008) + v26:String = GuardType v9, String + v19:StringExact = StringConcat v13, v26 CheckInterrupts Return v19 "); @@ -11289,9 +11339,9 @@ mod opt_tests { v13:Fixnum[1] = Const Value(1) CheckInterrupts PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) - v33:BasicObject = CCall itself@0x1038, v13 + v34:BasicObject = CCall itself@0x1038, v13 CheckInterrupts - Return v33 + Return v34 "); } @@ -11443,9 +11493,10 @@ mod opt_tests { v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:ArrayExact = ArrayDup v10 PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) - v21:BasicObject = SendWithoutBlockDirect v12, :max (0x1040) + PatchPoint NoSingletonClass(Array@0x1008) + v22:BasicObject = SendWithoutBlockDirect v12, :max (0x1040) CheckInterrupts - Return v21 + Return v22 "); } @@ -11515,9 +11566,9 @@ mod opt_tests { bb2(v6:BasicObject): v10:NilClass = Const Value(nil) PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) - v21:TrueClass = CCall nil?@0x1038, v10 + v22:TrueClass = CCall nil?@0x1038, v10 CheckInterrupts - Return v21 + Return v22 "); } @@ -11564,9 +11615,9 @@ mod opt_tests { bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) - v21:FalseClass = CCall nil?@0x1038, v10 + v22:FalseClass = CCall nil?@0x1038, v10 CheckInterrupts - Return v21 + Return v22 "); } @@ -11615,10 +11666,10 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) - v23:NilClass = GuardType v9, NilClass - v24:TrueClass = CCall nil?@0x1038, v23 + v24:NilClass = GuardType v9, NilClass + v25:TrueClass = CCall nil?@0x1038, v24 CheckInterrupts - Return v24 + Return v25 "); } @@ -11641,10 +11692,10 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(FalseClass@0x1000, nil?@0x1008, cme:0x1010) - v23:FalseClass = GuardType v9, FalseClass - v24:FalseClass = CCall nil?@0x1038, v23 + v24:FalseClass = GuardType v9, FalseClass + v25:FalseClass = CCall nil?@0x1038, v24 CheckInterrupts - Return v24 + Return v25 "); } @@ -11667,10 +11718,10 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(TrueClass@0x1000, nil?@0x1008, cme:0x1010) - v23:TrueClass = GuardType v9, TrueClass - v24:FalseClass = CCall nil?@0x1038, v23 + v24:TrueClass = GuardType v9, TrueClass + v25:FalseClass = CCall nil?@0x1038, v24 CheckInterrupts - Return v24 + Return v25 "); } @@ -11693,10 +11744,10 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Symbol@0x1000, nil?@0x1008, cme:0x1010) - v23:StaticSymbol = GuardType v9, StaticSymbol - v24:FalseClass = CCall nil?@0x1038, v23 + v24:StaticSymbol = GuardType v9, StaticSymbol + v25:FalseClass = CCall nil?@0x1038, v24 CheckInterrupts - Return v24 + Return v25 "); } @@ -11719,10 +11770,10 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) - v23:Fixnum = GuardType v9, Fixnum - v24:FalseClass = CCall nil?@0x1038, v23 + v24:Fixnum = GuardType v9, Fixnum + v25:FalseClass = CCall nil?@0x1038, v24 CheckInterrupts - Return v24 + Return v25 "); } @@ -11745,10 +11796,10 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Float@0x1000, nil?@0x1008, cme:0x1010) - v23:Flonum = GuardType v9, Flonum - v24:FalseClass = CCall nil?@0x1038, v23 + v24:Flonum = GuardType v9, Flonum + v25:FalseClass = CCall nil?@0x1038, v24 CheckInterrupts - Return v24 + Return v25 "); } @@ -11771,10 +11822,11 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) - v23:StringExact = GuardType v9, StringExact - v24:FalseClass = CCall nil?@0x1038, v23 + PatchPoint NoSingletonClass(String@0x1000) + v25:StringExact = GuardType v9, StringExact + v26:FalseClass = CCall nil?@0x1038, v25 CheckInterrupts - Return v24 + Return v26 "); } @@ -11797,10 +11849,11 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) - v23:ArrayExact = GuardType v9, ArrayExact - v24:BoolExact = CCall !@0x1038, v23 + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + v26:BoolExact = CCall !@0x1038, v25 CheckInterrupts - Return v24 + Return v26 "); } @@ -11823,10 +11876,11 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) - v23:ArrayExact = GuardType v9, ArrayExact - v24:BoolExact = CCall empty?@0x1038, v23 + PatchPoint NoSingletonClass(Array@0x1000) + v25:ArrayExact = GuardType v9, ArrayExact + v26:BoolExact = CCall empty?@0x1038, v25 CheckInterrupts - Return v24 + Return v26 "); } @@ -11849,10 +11903,11 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) - v23:HashExact = GuardType v9, HashExact - v24:BoolExact = CCall empty?@0x1038, v23 + PatchPoint NoSingletonClass(Hash@0x1000) + v25:HashExact = GuardType v9, HashExact + v26:BoolExact = CCall empty?@0x1038, v25 CheckInterrupts - Return v24 + Return v26 "); } @@ -11877,10 +11932,11 @@ mod opt_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) - v26:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] - v27:BoolExact = CCall ==@0x1038, v26, v12 + PatchPoint NoSingletonClass(C@0x1000) + v28:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] + v29:BoolExact = CCall ==@0x1038, v28, v12 CheckInterrupts - Return v27 + Return v29 "); } @@ -11960,10 +12016,11 @@ mod opt_tests { Jump bb2(v4) bb2(v6:BasicObject): 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) + PatchPoint NoSingletonClass(Object@0x1000) + v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] + v20:BasicObject = SendWithoutBlockDirect v19, :foo (0x1038) CheckInterrupts - Return v19 + Return v20 "); } @@ -11994,11 +12051,12 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v24:HeapObject[class_exact:C] = GuardShape v21, 0x1038 - v25:BasicObject = LoadIvarEmbedded v24, :@foo@0x1039 + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:BasicObject = LoadIvarEmbedded v25, :@foo@0x1039 CheckInterrupts - Return v25 + Return v26 "); } @@ -12029,11 +12087,12 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v24:HeapObject[class_exact:C] = GuardShape v21, 0x1038 - v25:BasicObject = LoadIvarEmbedded v24, :@foo@0x1039 + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:BasicObject = LoadIvarEmbedded v25, :@foo@0x1039 CheckInterrupts - Return v25 + Return v26 "); } @@ -12106,10 +12165,11 @@ mod opt_tests { PatchPoint StableConstantNames(0x1000, O) v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v25:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 - v26:NilClass = Const Value(nil) + PatchPoint NoSingletonClass(C@0x1010) + v26:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 + v27:NilClass = Const Value(nil) CheckInterrupts - Return v26 + Return v27 "); } @@ -12139,10 +12199,11 @@ mod opt_tests { PatchPoint StableConstantNames(0x1000, O) v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v25:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 - v26:NilClass = Const Value(nil) + PatchPoint NoSingletonClass(C@0x1010) + v26:HeapObject[VALUE(0x1008)] = GuardShape v21, 0x1048 + v27:NilClass = Const Value(nil) CheckInterrupts - Return v26 + Return v27 "); } @@ -12169,11 +12230,12 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v24:HeapObject[class_exact:C] = GuardShape v21, 0x1038 - v25:NilClass = Const Value(nil) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:NilClass = Const Value(nil) CheckInterrupts - Return v25 + Return v26 "); } @@ -12200,11 +12262,12 @@ mod opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) - v21:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - v24:HeapObject[class_exact:C] = GuardShape v21, 0x1038 - v25:NilClass = Const Value(nil) + PatchPoint NoSingletonClass(C@0x1000) + v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] + v25:HeapObject[class_exact:C] = GuardShape v22, 0x1038 + v26:NilClass = Const Value(nil) CheckInterrupts - Return v25 + Return v26 "); } diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 75f900c38d..119aa5ca52 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -2,7 +2,7 @@ use std::{collections::{HashMap, HashSet}, mem}; -use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID}, gc::IseqPayload, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr}; +use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID, VALUE}, gc::IseqPayload, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr}; use crate::stats::with_time_stat; use crate::stats::Counter::invalidation_time_ns; use crate::gc::remove_gc_offsets; @@ -59,6 +59,10 @@ pub struct Invariants { /// Set of patch points that assume that the interpreter is running with only one ractor single_ractor_patch_points: HashSet<PatchPoint>, + + /// Map from a class to a set of patch points that assume objects of the class + /// will have no singleton class. + no_singleton_class_patch_points: HashMap<VALUE, HashSet<PatchPoint>>, } impl Invariants { @@ -67,6 +71,7 @@ impl Invariants { self.update_ep_escape_iseqs(); self.update_no_ep_escape_iseq_patch_points(); self.update_cme_patch_points(); + self.update_no_singleton_class_patch_points(); } /// Forget an ISEQ when freeing it. We need to because a) if the address is reused, we'd be @@ -85,6 +90,11 @@ impl Invariants { self.cme_patch_points.remove(&cme); } + /// Forget a class when freeing it. See [Self::forget_iseq] for reasoning. + pub fn forget_klass(&mut self, klass: VALUE) { + self.no_singleton_class_patch_points.remove(&klass); + } + /// Update ISEQ references in Invariants::ep_escape_iseqs fn update_ep_escape_iseqs(&mut self) { let updated = std::mem::take(&mut self.ep_escape_iseqs) @@ -116,6 +126,17 @@ impl Invariants { .collect(); self.cme_patch_points = updated_cme_patch_points; } + + fn update_no_singleton_class_patch_points(&mut self) { + let updated_no_singleton_class_patch_points = std::mem::take(&mut self.no_singleton_class_patch_points) + .into_iter() + .map(|(klass, patch_points)| { + let new_klass = unsafe { rb_gc_location(klass) }; + (new_klass, patch_points) + }) + .collect(); + self.no_singleton_class_patch_points = updated_no_singleton_class_patch_points; + } } /// Called when a basic operator is redefined. Note that all the blocks assuming @@ -245,6 +266,21 @@ pub fn track_stable_constant_names_assumption( } } +/// Track a patch point for objects of a given class will have no singleton class. +pub fn track_no_singleton_class_assumption( + klass: VALUE, + patch_point_ptr: CodePtr, + side_exit_ptr: CodePtr, + payload_ptr: *mut IseqPayload, +) { + let invariants = ZJITState::get_invariants(); + invariants.no_singleton_class_patch_points.entry(klass).or_default().insert(PatchPoint { + patch_point_ptr, + side_exit_ptr, + payload_ptr, + }); +} + /// Called when a method is redefined. Invalidates all JIT code that depends on the CME. #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_cme_invalidate(cme: *const rb_callable_method_entry_t) { @@ -357,3 +393,20 @@ pub extern "C" fn rb_zjit_tracing_invalidate_all() { cb.mark_all_executable(); }); } + +#[unsafe(no_mangle)] +pub extern "C" fn rb_zjit_invalidate_no_singleton_class(klass: VALUE) { + if !zjit_enabled_p() { + return; + } + + 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(); + } + }); +} |
