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.
* 1: RUBY_FL_SINGLETON
* 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
* 4-19: SHAPE_FLAG_MASK
* Shape ID for the class.
@ -48,9 +50,9 @@
/* Flags of T_ICLASS
*
* 0: RICLASS_IS_ORIGIN // TODO: Delete this
* 3: RICLASS_ORIGIN_SHARED_MTBL // TODO: Delete this
* The T_ICLASS does not own the method table.
* 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.
* if !SHAPE_IN_BASIC_FLAGS
* 4-19: SHAPE_FLAG_MASK
* 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.
* 1: RMODULE_ALLOCATED_BUT_NOT_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
* Module is used for refinements.
* 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 *ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(iclass, ns);
int table_created = 0;
int first_set = 0;
if (ext) {
// 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));
table_created = RCLASS_SET_NAMESPACE_CLASSEXT(iclass, ns, ext);
if (table_created) {
RCLASS_SET_PRIME_CLASSEXT_READWRITE(iclass, false, false);
first_set = RCLASS_SET_NAMESPACE_CLASSEXT(iclass, ns, ext);
if (first_set) {
RCLASS_SET_PRIME_CLASSEXT_WRITABLE(iclass, false);
}
}
@ -825,8 +829,8 @@ rb_class_debug_dump_all_classext(VALUE klass)
}
snprintf(buf, 2048,
"classext-dump:\n readable:%s\n writable:%s\n table size:%zu\n",
RCLASS(klass)->prime_classext_readable ? "true" : "false",
RCLASS(klass)->prime_classext_writable ? "true" : "false",
RCLASS_PRIME_CLASSEXT_READABLE_P(klass) ? "true" : "false",
RCLASS_PRIME_CLASSEXT_WRITABLE_P(klass) ? "true" : "false",
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, "========================\n");
@ -1151,7 +1155,7 @@ class_alloc(VALUE flags, VALUE klass)
RCLASS_PRIME_NS((VALUE)obj) = ns;
// Classes/Modules defined in user namespaces are
// 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_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 RBasic basic;
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)
* STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_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);
struct RClass_and_rb_classext_t {
struct RClass rclass;
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 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_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_CONST_TBL(c) (RCLASS_EXT_READABLE(c)->const_tbl)
/*
* Both cc_tbl/callable_m_tbl are cache and always be changed when referreed,
* so always should be writable.
* Both cc_tbl/callable_m_tbl are cache-like and always be changed when referreed,
* 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_SUPERCLASS_DEPTH(c) (RCLASS_EXT_READABLE(c)->superclass_depth)
#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);
#define RCLASS_IS_ROOT FL_USER0
// #define RICLASS_IS_ORIGIN FL_USER0 // TODO: Delete this
// #define RCLASS_SUPERCLASSES_INCLUD_SELF FL_USER2 // TODO: Delete this
// #define RICLASS_ORIGIN_SHARED_MTBL FL_USER3 // TODO: Delete this
// 1 is for RUBY_FL_SINGLETON or RMODULE_ALLOCATED_BUT_NOT_INITIALIZED (see class.c)
#define RCLASS_PRIME_CLASSEXT_WRITABLE FL_USER2
// 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_dump_all_classext(VALUE klass); // TODO: only for development
@ -307,23 +306,47 @@ void rb_class_ensure_writable(VALUE obj);
static inline int
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);
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);
if (!tbl) {
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);
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
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;
RCLASS(obj)->prime_classext_writable = writable;
VM_ASSERT(RB_TYPE_P(klass, T_CLASS) || RB_TYPE_P(klass, T_MODULE) || RB_TYPE_P(klass, T_ICLASS));
if (writable) {
FL_SET(klass, RCLASS_PRIME_CLASSEXT_WRITABLE);
}
else {
FL_UNSET(klass, RCLASS_PRIME_CLASSEXT_WRITABLE);
}
}
static inline rb_classext_t *
@ -354,7 +377,7 @@ RCLASS_EXT_READABLE_IN_NS(VALUE obj, const rb_namespace_t *ns)
{
if (!ns
|| NAMESPACE_BUILTIN_P(ns)
|| RCLASS(obj)->prime_classext_readable) {
|| RCLASS_PRIME_CLASSEXT_READABLE_P(obj)) {
return RCLASS_EXT(obj);
}
return RCLASS_EXT_READABLE_LOOKUP(obj, ns);
@ -364,7 +387,7 @@ static inline rb_classext_t *
RCLASS_EXT_READABLE(VALUE obj)
{
const rb_namespace_t *ns;
if (RCLASS(obj)->prime_classext_readable) {
if (RCLASS_PRIME_CLASSEXT_READABLE_P(obj)) {
return RCLASS_EXT(obj);
}
// 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)
{
rb_classext_t *ext;
int table_created = 0;
int first_set = 0;
ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(obj, ns);
if (ext)
return ext;
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
}
@ -396,9 +418,9 @@ RCLASS_EXT_WRITABLE_LOOKUP(VALUE obj, const rb_namespace_t *ns)
ext = RCLASS_EXT_TABLE_LOOKUP_INTERNAL(obj, ns);
if (!ext) {
ext = rb_class_duplicate_classext(RCLASS_EXT(obj), obj, ns);
table_created = RCLASS_SET_NAMESPACE_CLASSEXT(obj, ns, ext);
if (table_created) {
RCLASS_SET_PRIME_CLASSEXT_READWRITE(obj, false, false);
first_set = RCLASS_SET_NAMESPACE_CLASSEXT(obj, ns, ext);
if (first_set) {
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
|| NAMESPACE_BUILTIN_P(ns)
|| RCLASS(obj)->prime_classext_writable) {
|| RCLASS_PRIME_CLASSEXT_WRITABLE_P(obj)) {
return RCLASS_EXT(obj);
}
return RCLASS_EXT_WRITABLE_LOOKUP(obj, ns);
@ -421,7 +443,7 @@ static inline rb_classext_t *
RCLASS_EXT_WRITABLE(VALUE obj)
{
const rb_namespace_t *ns;
if (RCLASS(obj)->prime_classext_writable) {
if (RCLASS_PRIME_CLASSEXT_WRITABLE_P(obj)) {
return RCLASS_EXT(obj);
}
// 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
// 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.
RCLASS_SET_PRIME_CLASSEXT_READWRITE(namespace, true, true);
// If a code in the namespace adds a constant, the constant will be visible even from root/main.
RCLASS_SET_PRIME_CLASSEXT_WRITABLE(namespace, true);
// TODO: Handle object shapes properly
// fallback to ivptr for ivars from shapes to manipulate the constant table
rb_evict_ivars_to_hash(namespace);
@ -568,7 +567,7 @@ rb_namespace_current(VALUE klass)
static VALUE
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 Qfalse;
}