Compact prime classext readable/writable flags

To make RClass size smaller, move flags of prime classext readable/writable to:
 readable - use ns_classext_tbl is NULL or not (if NULL, it's readable)
 writable - use FL_USER2 of RBasic flags
This commit is contained in:
Satoshi Tagomori 2025-05-08 22:53:31 +09:00
parent e81d50207b
commit ff790c759e
3 changed files with 73 additions and 48 deletions

28
class.c
View File

@ -39,7 +39,9 @@
* This is done for classes defined from C to allow storing them in global variables. * This is done for classes defined from C to allow storing them in global variables.
* 1: RUBY_FL_SINGLETON * 1: RUBY_FL_SINGLETON
* This class is a singleton class. * This class is a singleton class.
* 2: RCLASS_SUPERCLASSES_INCLUDE_SELF // TODO: Delete this * 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* This class's prime classext is the only classext and writable from any namespaces.
* If unset, the prime classext is writable only from the root namespace.
* if !SHAPE_IN_BASIC_FLAGS * if !SHAPE_IN_BASIC_FLAGS
* 4-19: SHAPE_FLAG_MASK * 4-19: SHAPE_FLAG_MASK
* Shape ID for the class. * Shape ID for the class.
@ -48,9 +50,9 @@
/* Flags of T_ICLASS /* Flags of T_ICLASS
* *
* 0: RICLASS_IS_ORIGIN // TODO: Delete this * 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* 3: RICLASS_ORIGIN_SHARED_MTBL // TODO: Delete this * This module's prime classext is the only classext and writable from any namespaces.
* The T_ICLASS does not own the method table. * If unset, the prime classext is writable only from the root namespace.
* if !SHAPE_IN_BASIC_FLAGS * if !SHAPE_IN_BASIC_FLAGS
* 4-19: SHAPE_FLAG_MASK * 4-19: SHAPE_FLAG_MASK
* Shape ID. This is set but not used. * Shape ID. This is set but not used.
@ -64,7 +66,9 @@
* This is done for classes defined from C to allow storing them in global variables. * This is done for classes defined from C to allow storing them in global variables.
* 1: RMODULE_ALLOCATED_BUT_NOT_INITIALIZED * 1: RMODULE_ALLOCATED_BUT_NOT_INITIALIZED
* Module has not been initialized. * Module has not been initialized.
* 2: RCLASS_SUPERCLASSES_INCLUDE_SELF // TODO: Delete this * 2: RCLASS_PRIME_CLASSEXT_PRIME_WRITABLE
* This module's prime classext is the only classext and writable from any namespaces.
* If unset, the prime classext is writable only from the root namespace.
* 3: RMODULE_IS_REFINEMENT * 3: RMODULE_IS_REFINEMENT
* Module is used for refinements. * Module is used for refinements.
* if !SHAPE_IN_BASIC_FLAGS * if !SHAPE_IN_BASIC_FLAGS
@ -254,7 +258,7 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_n
{ {
rb_classext_t *src = RCLASS_EXT(iclass); rb_classext_t *src = RCLASS_EXT(iclass);
rb_classext_t *ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(iclass, ns); rb_classext_t *ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(iclass, ns);
int table_created = 0; int first_set = 0;
if (ext) { if (ext) {
// iclass classext for the ns is only for cc/callable_m_tbl if it's created earlier than module's one // iclass classext for the ns is only for cc/callable_m_tbl if it's created earlier than module's one
@ -290,9 +294,9 @@ class_duplicate_iclass_classext(VALUE iclass, rb_classext_t *mod_ext, const rb_n
RCLASSEXT_SET_INCLUDER(ext, iclass, RCLASSEXT_INCLUDER(src)); RCLASSEXT_SET_INCLUDER(ext, iclass, RCLASSEXT_INCLUDER(src));
table_created = RCLASS_SET_NAMESPACE_CLASSEXT(iclass, ns, ext); first_set = RCLASS_SET_NAMESPACE_CLASSEXT(iclass, ns, ext);
if (table_created) { if (first_set) {
RCLASS_SET_PRIME_CLASSEXT_READWRITE(iclass, false, false); RCLASS_SET_PRIME_CLASSEXT_WRITABLE(iclass, false);
} }
} }
@ -825,8 +829,8 @@ rb_class_debug_dump_all_classext(VALUE klass)
} }
snprintf(buf, 2048, snprintf(buf, 2048,
"classext-dump:\n readable:%s\n writable:%s\n table size:%zu\n", "classext-dump:\n readable:%s\n writable:%s\n table size:%zu\n",
RCLASS(klass)->prime_classext_readable ? "true" : "false", RCLASS_PRIME_CLASSEXT_READABLE_P(klass) ? "true" : "false",
RCLASS(klass)->prime_classext_writable ? "true" : "false", RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass) ? "true" : "false",
RCLASS(klass)->ns_classext_tbl ? st_table_size(RCLASS(klass)->ns_classext_tbl) : 0); RCLASS(klass)->ns_classext_tbl ? st_table_size(RCLASS(klass)->ns_classext_tbl) : 0);
rb_str_cat_cstr(r, buf); rb_str_cat_cstr(r, buf);
rb_str_cat_cstr(r, "========================\n"); rb_str_cat_cstr(r, "========================\n");
@ -1151,7 +1155,7 @@ class_alloc(VALUE flags, VALUE klass)
RCLASS_PRIME_NS((VALUE)obj) = ns; RCLASS_PRIME_NS((VALUE)obj) = ns;
// Classes/Modules defined in user namespaces are // Classes/Modules defined in user namespaces are
// writable directly because it exists only in a namespace. // writable directly because it exists only in a namespace.
RCLASS_SET_PRIME_CLASSEXT_READWRITE((VALUE)obj, true, NAMESPACE_USER_P(ns) ? true : false); RCLASS_SET_PRIME_CLASSEXT_WRITABLE((VALUE)obj, NAMESPACE_USER_P(ns) ? true : false);
RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj); RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj);
RCLASS_SET_REFINED_CLASS((VALUE)obj, Qnil); RCLASS_SET_REFINED_CLASS((VALUE)obj, Qnil);

View File

@ -136,26 +136,26 @@ STATIC_ASSERT(shape_max_variations, SHAPE_MAX_VARIATIONS < (1 << (sizeof(((rb_cl
struct RClass { struct RClass {
struct RBasic basic; struct RBasic basic;
st_table *ns_classext_tbl; // ns_object -> (rb_classext_t *) st_table *ns_classext_tbl; // ns_object -> (rb_classext_t *)
bool prime_classext_readable : 1; /*
bool prime_classext_writable : 1; * If ns_classext_tbl is NULL, then the prime classext is readable (because no other classext exists).
* For the check whether writable or not, check flag RCLASS_PRIME_CLASSEXT_WRITABLE
*/
}; };
/** TODO: Update or remove this assertion // Assert that classes can be embedded in heaps[2] (which has 160B slot size)
* // Assert that classes can be embedded in heaps[2] (which has 160B slot size) // TODO: check this assertion's validity
* STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE); // STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE);
*/
struct RClass_and_rb_classext_t { struct RClass_and_rb_classext_t {
struct RClass rclass; struct RClass rclass;
rb_classext_t classext; rb_classext_t classext;
}; };
#define RCLASS_PRIME_READABLE_P(obj) (RCLASS(obj)->prime_classext_readable)
#define RCLASS_PRIME_WRITABLE_P(obj) (RCLASS(obj)->prime_classext_writable)
static inline bool RCLASS_SINGLETON_P(VALUE klass); static inline bool RCLASS_SINGLETON_P(VALUE klass);
static inline void RCLASS_SET_PRIME_CLASSEXT_READWRITE(VALUE obj, bool readable, bool writable); static inline bool RCLASS_PRIME_CLASSEXT_READABLE_P(VALUE obj);
static inline bool RCLASS_PRIME_CLASSEXT_WRITABLE_P(VALUE obj);
static inline void RCLASS_SET_PRIME_CLASSEXT_WRITABLE(VALUE obj, bool writable);
#define RCLASS_EXT(c) (&((struct RClass_and_rb_classext_t*)(c))->classext) #define RCLASS_EXT(c) (&((struct RClass_and_rb_classext_t*)(c))->classext)
#define RCLASS_EXT_PRIME_P(ext, c) (&((struct RClass_and_rb_classext_t*)(c))->classext == ext) #define RCLASS_EXT_PRIME_P(ext, c) (&((struct RClass_and_rb_classext_t*)(c))->classext == ext)
@ -217,11 +217,9 @@ static inline void RCLASSEXT_SET_INCLUDER(rb_classext_t *ext, VALUE klass, VALUE
#define RCLASS_M_TBL(c) (RCLASS_EXT_READABLE(c)->m_tbl) #define RCLASS_M_TBL(c) (RCLASS_EXT_READABLE(c)->m_tbl)
#define RCLASS_CONST_TBL(c) (RCLASS_EXT_READABLE(c)->const_tbl) #define RCLASS_CONST_TBL(c) (RCLASS_EXT_READABLE(c)->const_tbl)
/* /*
* Both cc_tbl/callable_m_tbl are cache and always be changed when referreed, * Both cc_tbl/callable_m_tbl are cache-like and always be changed when referreed,
* so always should be writable. * so always those should be writable.
*/ */
// #define RCLASS_CALLABLE_M_TBL(c) (RCLASS_EXT_READABLE(c)->callable_m_tbl)
// #define RCLASS_CC_TBL(c) (RCLASS_EXT_READABLE(c)->cc_tbl)
#define RCLASS_CVC_TBL(c) (RCLASS_EXT_READABLE(c)->cvc_tbl) #define RCLASS_CVC_TBL(c) (RCLASS_EXT_READABLE(c)->cvc_tbl)
#define RCLASS_SUPERCLASS_DEPTH(c) (RCLASS_EXT_READABLE(c)->superclass_depth) #define RCLASS_SUPERCLASS_DEPTH(c) (RCLASS_EXT_READABLE(c)->superclass_depth)
#define RCLASS_SUPERCLASSES(c) (RCLASS_EXT_READABLE(c)->superclasses) #define RCLASS_SUPERCLASSES(c) (RCLASS_EXT_READABLE(c)->superclasses)
@ -290,9 +288,10 @@ static inline void RCLASS_SET_CLASSPATH(VALUE klass, VALUE classpath, bool perma
static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool permanent); static inline void RCLASS_WRITE_CLASSPATH(VALUE klass, VALUE classpath, bool permanent);
#define RCLASS_IS_ROOT FL_USER0 #define RCLASS_IS_ROOT FL_USER0
// #define RICLASS_IS_ORIGIN FL_USER0 // TODO: Delete this // 1 is for RUBY_FL_SINGLETON or RMODULE_ALLOCATED_BUT_NOT_INITIALIZED (see class.c)
// #define RCLASS_SUPERCLASSES_INCLUD_SELF FL_USER2 // TODO: Delete this #define RCLASS_PRIME_CLASSEXT_WRITABLE FL_USER2
// #define RICLASS_ORIGIN_SHARED_MTBL FL_USER3 // TODO: Delete this // 3 is RMODULE_IS_REFINEMENT for RMODULE
// 4-19: SHAPE_FLAG_MASK
VALUE rb_class_debug_duplicate_classext(VALUE klass, VALUE namespace); // TODO: only for development VALUE rb_class_debug_duplicate_classext(VALUE klass, VALUE namespace); // TODO: only for development
VALUE rb_class_debug_dump_all_classext(VALUE klass); // TODO: only for development VALUE rb_class_debug_dump_all_classext(VALUE klass); // TODO: only for development
@ -307,23 +306,47 @@ void rb_class_ensure_writable(VALUE obj);
static inline int static inline int
RCLASS_SET_NAMESPACE_CLASSEXT(VALUE obj, const rb_namespace_t *ns, rb_classext_t *ext) RCLASS_SET_NAMESPACE_CLASSEXT(VALUE obj, const rb_namespace_t *ns, rb_classext_t *ext)
{ {
int tbl_created = 0; int first_set = 0;
st_table *tbl = RCLASS_CLASSEXT_TBL(obj); st_table *tbl = RCLASS_CLASSEXT_TBL(obj);
VM_ASSERT(NAMESPACE_USER_P(ns)); // non-prime classext is only for user namespace, with ns_object VM_ASSERT(NAMESPACE_USER_P(ns)); // non-prime classext is only for user namespace, with ns_object
VM_ASSERT(ns->ns_object);
VM_ASSERT(RCLASSEXT_NS(ext) == ns); VM_ASSERT(RCLASSEXT_NS(ext) == ns);
if (!tbl) { if (!tbl) {
RCLASS_CLASSEXT_TBL(obj) = tbl = st_init_numtable_with_size(1); RCLASS_CLASSEXT_TBL(obj) = tbl = st_init_numtable_with_size(1);
tbl_created = 1; }
if (rb_st_table_size(tbl) == 0) {
first_set = 1;
} }
rb_st_insert(tbl, (st_data_t)ns->ns_object, (st_data_t)ext); rb_st_insert(tbl, (st_data_t)ns->ns_object, (st_data_t)ext);
return tbl_created; return first_set;
}
static inline bool
RCLASS_PRIME_CLASSEXT_READABLE_P(VALUE klass)
{
VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS));
// if the lookup table exists, then it means the prime classext is NOT directly readable.
return RCLASS_CLASSEXT_TBL(klass) == NULL;
}
static inline bool
RCLASS_PRIME_CLASSEXT_WRITABLE_P(VALUE klass)
{
VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS));
return FL_TEST(klass, RCLASS_PRIME_CLASSEXT_WRITABLE);
} }
static inline void static inline void
RCLASS_SET_PRIME_CLASSEXT_READWRITE(VALUE obj, bool readable, bool writable) RCLASS_SET_PRIME_CLASSEXT_WRITABLE(VALUE klass, bool writable)
{ {
RCLASS(obj)->prime_classext_readable = readable; VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS));
RCLASS(obj)->prime_classext_writable = writable;
if (writable) {
FL_SET(klass, RCLASS_PRIME_CLASSEXT_WRITABLE);
}
else {
FL_UNSET(klass, RCLASS_PRIME_CLASSEXT_WRITABLE);
}
} }
static inline rb_classext_t * static inline rb_classext_t *
@ -354,7 +377,7 @@ RCLASS_EXT_READABLE_IN_NS(VALUE obj, const rb_namespace_t *ns)
{ {
if (!ns if (!ns
|| NAMESPACE_BUILTIN_P(ns) || NAMESPACE_BUILTIN_P(ns)
|| RCLASS(obj)->prime_classext_readable) { || RCLASS_PRIME_CLASSEXT_READABLE_P(obj)) {
return RCLASS_EXT(obj); return RCLASS_EXT(obj);
} }
return RCLASS_EXT_READABLE_LOOKUP(obj, ns); return RCLASS_EXT_READABLE_LOOKUP(obj, ns);
@ -364,7 +387,7 @@ static inline rb_classext_t *
RCLASS_EXT_READABLE(VALUE obj) RCLASS_EXT_READABLE(VALUE obj)
{ {
const rb_namespace_t *ns; const rb_namespace_t *ns;
if (RCLASS(obj)->prime_classext_readable) { if (RCLASS_PRIME_CLASSEXT_READABLE_P(obj)) {
return RCLASS_EXT(obj); return RCLASS_EXT(obj);
} }
// delay namespace loading to optimize for unmodified classes // delay namespace loading to optimize for unmodified classes
@ -379,14 +402,13 @@ static inline rb_classext_t *
RCLASS_EXT_WRITABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns) RCLASS_EXT_WRITABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns)
{ {
rb_classext_t *ext; rb_classext_t *ext;
int table_created = 0; int first_set = 0;
ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(obj, ns); ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(obj, ns);
if (ext) if (ext)
return ext; return ext;
if (!rb_shape_obj_too_complex_p(obj)) { if (!rb_shape_obj_too_complex_p(obj)) {
// TODO: Handle object shapes properly
rb_evict_ivars_to_hash(obj); // fallback to ivptr for ivars from shapes rb_evict_ivars_to_hash(obj); // fallback to ivptr for ivars from shapes
} }
@ -396,9 +418,9 @@ RCLASS_EXT_WRITABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns)
ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(obj, ns); ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(obj, ns);
if (!ext) { if (!ext) {
ext = rb_class_duplicate_classext(RCLASS_EXT(obj), obj, ns); ext = rb_class_duplicate_classext(RCLASS_EXT(obj), obj, ns);
table_created = RCLASS_SET_NAMESPACE_CLASSEXT(obj, ns, ext); first_set = RCLASS_SET_NAMESPACE_CLASSEXT(obj, ns, ext);
if (table_created) { if (first_set) {
RCLASS_SET_PRIME_CLASSEXT_READWRITE(obj, false, false); RCLASS_SET_PRIME_CLASSEXT_WRITABLE(obj, false);
} }
} }
} }
@ -411,7 +433,7 @@ RCLASS_EXT_WRITABLE_IN_NS(VALUE obj, const rb_namespace_t *ns)
{ {
if (!ns if (!ns
|| NAMESPACE_BUILTIN_P(ns) || NAMESPACE_BUILTIN_P(ns)
|| RCLASS(obj)->prime_classext_writable) { || RCLASS_PRIME_CLASSEXT_WRITABLE_P(obj)) {
return RCLASS_EXT(obj); return RCLASS_EXT(obj);
} }
return RCLASS_EXT_WRITABLE_LOOKUP(obj, ns); return RCLASS_EXT_WRITABLE_LOOKUP(obj, ns);
@ -421,7 +443,7 @@ static inline rb_classext_t *
RCLASS_EXT_WRITABLE(VALUE obj) RCLASS_EXT_WRITABLE(VALUE obj)
{ {
const rb_namespace_t *ns; const rb_namespace_t *ns;
if (RCLASS(obj)->prime_classext_writable) { if (RCLASS_PRIME_CLASSEXT_WRITABLE_P(obj)) {
return RCLASS_EXT(obj); return RCLASS_EXT(obj);
} }
// delay namespace loading to optimize for unmodified classes // delay namespace loading to optimize for unmodified classes

View File

@ -527,10 +527,9 @@ namespace_initialize(VALUE namespace)
// Set the Namespace object unique/consistent from any namespaces to have just single // Set the Namespace object unique/consistent from any namespaces to have just single
// constant table from any view of every (including main) namespace. // constant table from any view of every (including main) namespace.
// If a code in the namespace adds a constant, the constant will be visible even from main. // If a code in the namespace adds a constant, the constant will be visible even from root/main.
RCLASS_SET_PRIME_CLASSEXT_READWRITE(namespace, true, true); RCLASS_SET_PRIME_CLASSEXT_WRITABLE(namespace, true);
// TODO: Handle object shapes properly
// fallback to ivptr for ivars from shapes to manipulate the constant table // fallback to ivptr for ivars from shapes to manipulate the constant table
rb_evict_ivars_to_hash(namespace); rb_evict_ivars_to_hash(namespace);
@ -568,7 +567,7 @@ rb_namespace_current(VALUE klass)
static VALUE static VALUE
rb_namespace_s_is_builtin_p(VALUE namespace, VALUE klass) rb_namespace_s_is_builtin_p(VALUE namespace, VALUE klass)
{ {
if (RCLASS_PRIME_READABLE_P(klass) && !RCLASS_PRIME_WRITABLE_P(klass)) if (RCLASS_PRIME_CLASSEXT_READABLE_P(klass) && !RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass))
return Qtrue; return Qtrue;
return Qfalse; return Qfalse;
} }