Skip to content

Commit 05cecc1

Browse files
committed
[WIP] NoSingletonOverride patchpoint. New functionality, same old NoSingleton name
1 parent b1220ac commit 05cecc1

8 files changed

Lines changed: 399 additions & 44 deletions

File tree

class.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "internal/box.h"
2525
#include "internal/class.h"
2626
#include "internal/eval.h"
27+
#include "internal/error.h"
2728
#include "internal/hash.h"
2829
#include "internal/object.h"
2930
#include "internal/string.h"
@@ -55,6 +56,9 @@
5556
* 2: RCLASS_PRIME_CLASSEXT_WRITABLE
5657
* This module's prime classext is the only classext and writable from any boxes.
5758
* If unset, the prime classext is writable only from the root box.
59+
* 3: RICLASS_IS_SINGLETON_ANCESTOR
60+
* This iclass is in the singleton class portion of an ancestry chain.
61+
* Set when a module is included/prepended into a singleton class.
5862
* 4: RCLASS_BOXABLE
5963
* Is a builtin class that may be boxed. It larger than a normal class.
6064
*/
@@ -1422,7 +1426,6 @@ make_singleton_class(VALUE obj)
14221426
RBASIC_SET_CLASS(obj, klass);
14231427
rb_singleton_class_attached(klass, obj);
14241428
rb_yjit_invalidate_no_singleton_class(orig_class);
1425-
rb_zjit_invalidate_no_singleton_class(orig_class);
14261429

14271430
SET_METACLASS_OF(klass, METACLASS_OF(rb_class_real(orig_class)));
14281431
return klass;
@@ -1855,6 +1858,18 @@ clear_module_cache_i(ID id, VALUE val, void *data)
18551858
return ID_TABLE_CONTINUE;
18561859
}
18571860

1861+
static enum rb_id_table_iterator_result
1862+
zjit_invalid_no_singleton_override_i(ID method_id, VALUE val, void *data)
1863+
{
1864+
VALUE insertion_point = (VALUE)data;
1865+
// If this method is an override, bust invariant and stop iterating
1866+
if (rb_method_entry(insertion_point, method_id)) {
1867+
rb_zjit_invalidate_no_singleton_class(rb_class_real(insertion_point));
1868+
return ID_TABLE_STOP;
1869+
}
1870+
return ID_TABLE_CONTINUE;
1871+
}
1872+
18581873
static bool
18591874
module_in_super_chain(const VALUE klass, VALUE module)
18601875
{
@@ -1944,6 +1959,15 @@ do_include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super
19441959

19451960
// setup T_ICLASS for the include/prepend module
19461961
iclass = rb_include_class_new(module, super_class);
1962+
// Mark the iclass as being in the singleton portion of the chain.
1963+
// c is the insertion point; if it's a singleton class or already in
1964+
// the singleton portion, the new iclass is too.
1965+
if (rb_zjit_enabled_p && (RCLASS_SINGLETON_P(c) || (RB_TYPE_P(c, T_ICLASS) && RICLASS_SINGLETON_ANCESTOR_P(c)))) {
1966+
FL_SET(iclass, RICLASS_IS_SINGLETON_ANCESTOR);
1967+
if (method_changed && tbl && rb_id_table_size(tbl)) {
1968+
rb_id_table_foreach(tbl, zjit_invalid_no_singleton_override_i, (void *)c);
1969+
}
1970+
}
19471971
c = rb_class_set_super(c, iclass);
19481972
RCLASS_SET_INCLUDER(iclass, klass);
19491973
if (module != RCLASS_ORIGIN(module)) {
@@ -2082,6 +2106,10 @@ rb_prepend_module(VALUE klass, VALUE module)
20822106
rb_id_table_foreach(RCLASS_M_TBL(subclass), clear_module_cache_i, (void *)subclass);
20832107
RCLASS_WRITE_M_TBL(subclass, klass_m_tbl);
20842108
VALUE origin = rb_include_class_new(klass_origin, RCLASS_SUPER(subclass));
2109+
if (rb_zjit_enabled_p && RICLASS_SINGLETON_ANCESTOR_P(subclass)) {
2110+
RUBY_ASSERT_BUILTIN_TYPE(subclass, T_ICLASS);
2111+
FL_SET(origin, RICLASS_IS_SINGLETON_ANCESTOR);
2112+
}
20852113
rb_class_set_super(subclass, origin);
20862114
RCLASS_SET_INCLUDER(origin, RCLASS_INCLUDER(subclass));
20872115
RCLASS_WRITE_ORIGIN(subclass, origin);

internal/class.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool per
293293
#define RCLASS_PRIME_CLASSEXT_WRITABLE FL_USER2
294294
#define RCLASS_IS_INITIALIZED FL_USER3
295295
// 3 is RMODULE_IS_REFINEMENT for RMODULE
296+
// 3 is also RICLASS_IS_SINGLETON_ANCESTOR for T_ICLASS (not T_CLASS/T_MODULE)
297+
#define RICLASS_IS_SINGLETON_ANCESTOR FL_USER3
296298
#define RCLASS_BOXABLE FL_USER4
297299

298300
static inline st_table *
@@ -708,6 +710,12 @@ RICLASS_OWNS_M_TBL_P(VALUE iclass)
708710
return RCLASSEXT_ICLASS_IS_ORIGIN(ext) && !RCLASSEXT_ICLASS_ORIGIN_SHARED_MTBL(ext);
709711
}
710712

713+
static inline bool
714+
RICLASS_SINGLETON_ANCESTOR_P(VALUE iclass)
715+
{
716+
return FL_TEST_RAW(iclass, RICLASS_IS_SINGLETON_ANCESTOR);
717+
}
718+
711719
static inline void
712720
RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass)
713721
{

vm_method.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,17 @@ check_override_opt_method(VALUE klass, VALUE mid)
13551355
}
13561356
}
13571357

1358+
static void
1359+
check_singleton_override_by_method_i(VALUE subclass, VALUE arg)
1360+
{
1361+
ID mid = arg;
1362+
VM_ASSERT_TYPE(subclass, T_ICLASS);
1363+
if (RICLASS_SINGLETON_ANCESTOR_P(subclass) && rb_method_entry(subclass, mid)) {
1364+
// TODO fuse real and search above
1365+
rb_zjit_invalidate_no_singleton_class(rb_class_real(subclass));
1366+
}
1367+
}
1368+
13581369
static inline rb_method_entry_t* search_method0(VALUE klass, ID id, VALUE *defined_class_ptr, bool skip_refined);
13591370
/*
13601371
* klass->method_table[mid] = method_entry(defined_class, visi, def)
@@ -1485,6 +1496,16 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil
14851496
rb_warn("redefining '%s' may cause serious problems", rb_id2name(mid));
14861497
}
14871498
}
1499+
// Check ZJIT NoSingletonClassOverride invariant
1500+
if (rb_zjit_enabled_p) {
1501+
// Invalidate ZJIT code that assumes no singleton class override
1502+
if (RCLASS_SINGLETON_P(klass) && rb_method_entry(klass, mid)) {
1503+
rb_zjit_invalidate_no_singleton_class(rb_class_real(klass));
1504+
}
1505+
else if (RB_TYPE_P(klass, T_MODULE)) {
1506+
rb_class_foreach_subclass(klass, check_singleton_override_by_method_i, (VALUE)mid);
1507+
}
1508+
}
14881509

14891510
if (make_refined) {
14901511
make_method_entry_refined(klass, me);

zjit/src/codegen.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::backend::current::ALLOC_REGS;
1111
use crate::invariants::{
1212
track_bop_assumption, track_cme_assumption, track_no_ep_escape_assumption, track_no_trace_point_assumption,
1313
track_single_ractor_assumption, track_stable_constant_names_assumption, track_no_singleton_class_assumption,
14-
track_root_box_assumption
14+
track_no_singleton_override_assumption, track_root_box_assumption
1515
};
1616
use crate::gc::append_gc_offsets;
1717
use crate::payload::{get_or_create_iseq_payload, IseqCodePtrs, IseqVersion, IseqVersionRef, IseqStatus};
@@ -877,6 +877,9 @@ pub fn split_patch_point(asm: &mut Assembler, target: &Target, invariant: Invari
877877
Invariant::NoSingletonClass { klass } => {
878878
track_no_singleton_class_assumption(klass, code_ptr, side_exit_ptr, version);
879879
}
880+
Invariant::NoSingletonClassOverride { klass } => {
881+
track_no_singleton_override_assumption(klass, code_ptr, side_exit_ptr, version);
882+
}
880883
Invariant::RootBoxOnly => {
881884
track_root_box_assumption(code_ptr, side_exit_ptr, version);
882885
}

zjit/src/hir.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ pub enum Invariant {
153153
NoSingletonClass {
154154
klass: VALUE,
155155
},
156+
/// No method override has occurred in the singleton portion of this class's ancestry.
157+
/// Invalidated when a method is added to a singleton class or a module is
158+
/// included/prepended into the singleton chain that introduces methods.
159+
NoSingletonClassOverride {
160+
klass: VALUE,
161+
},
156162
/// Only the root box is active, so we can safely read from the prime classext.
157163
/// Invalidated if a non-root box duplicates any classext.
158164
RootBoxOnly,
@@ -295,6 +301,12 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
295301
class_name,
296302
self.ptr_map.map_ptr(klass.as_ptr::<VALUE>()))
297303
}
304+
Invariant::NoSingletonClassOverride { klass } => {
305+
let class_name = get_class_name(klass);
306+
write!(f, "NoSingletonClassOverride({}@{:p})",
307+
class_name,
308+
self.ptr_map.map_ptr(klass.as_ptr::<VALUE>()))
309+
}
298310
Invariant::RootBoxOnly => write!(f, "RootBoxOnly"),
299311
}
300312
}

0 commit comments

Comments
 (0)