From 1a4b4cd7f8b53fc52191c1ddcf3f624a5e687c24 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 15 Feb 2023 15:18:01 +0100 Subject: [PATCH] Move `attached_object` into `rb_classext_struct` Given that signleton classes don't have an allocator, we can re-use these bytes to store the attached object in `rb_classext_struct` without making it larger. --- class.c | 7 +- defs/id.def | 1 - gc.c | 8 ++ internal/class.h | 32 ++++- marshal.c | 2 +- vm_method.c | 5 +- yjit/bindgen/src/main.rs | 1 + yjit/src/codegen.rs | 2 +- yjit/src/cruby_bindings.inc.rs | 215 ++++++++++++++++----------------- 9 files changed, 152 insertions(+), 121 deletions(-) diff --git a/class.c b/class.c index 1545cc0b9f..84cc4c2cbf 100644 --- a/class.c +++ b/class.c @@ -221,7 +221,7 @@ class_alloc(VALUE flags, VALUE klass) */ RCLASS_SET_ORIGIN((VALUE)obj, (VALUE)obj); RB_OBJ_WRITE(obj, &RCLASS_REFINED_CLASS(obj), Qnil); - RCLASS_ALLOCATOR(obj) = 0; + RCLASS_SET_ALLOCATOR((VALUE)obj, NULL); return (VALUE)obj; } @@ -514,7 +514,7 @@ rb_mod_init_copy(VALUE clone, VALUE orig) RBASIC_SET_CLASS(clone, rb_singleton_class_clone(orig)); rb_singleton_class_attached(METACLASS_OF(clone), (VALUE)clone); } - RCLASS_ALLOCATOR(clone) = RCLASS_ALLOCATOR(orig); + RCLASS_SET_ALLOCATOR(clone, RCLASS_ALLOCATOR(orig)); copy_tables(clone, orig); if (RCLASS_M_TBL(orig)) { struct clone_method_arg arg; @@ -550,7 +550,7 @@ rb_mod_init_copy(VALUE clone, VALUE orig) prev_clone_p = clone_p; RCLASS_M_TBL(clone_p) = RCLASS_M_TBL(p); RCLASS_CONST_TBL(clone_p) = RCLASS_CONST_TBL(p); - RCLASS_ALLOCATOR(clone_p) = RCLASS_ALLOCATOR(p); + RCLASS_SET_ALLOCATOR(clone_p, RCLASS_ALLOCATOR(p)); if (RB_TYPE_P(clone, T_CLASS)) { RCLASS_SET_INCLUDER(clone_p, clone); } @@ -635,7 +635,6 @@ rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach) } RCLASS_SET_SUPER(clone, RCLASS_SUPER(klass)); - RCLASS_ALLOCATOR(clone) = RCLASS_ALLOCATOR(klass); rb_iv_tbl_copy(clone, klass); if (RCLASS_CONST_TBL(klass)) { struct clone_const_arg arg; diff --git a/defs/id.def b/defs/id.def index ebf00506ea..c8d7b6af3e 100644 --- a/defs/id.def +++ b/defs/id.def @@ -25,7 +25,6 @@ firstline, predefined = __LINE__+1, %[\ lambda send __send__ - __attached__ __recursive_key__ initialize initialize_copy diff --git a/gc.c b/gc.c index 42de20060b..1db1a13d3e 100644 --- a/gc.c +++ b/gc.c @@ -7264,6 +7264,10 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_CLASS: + if (FL_TEST(obj, FL_SINGLETON)) { + gc_mark(objspace, RCLASS_ATTACHED_OBJECT(obj)); + } + // Continue to the shared T_CLASS/T_MODULE case T_MODULE: if (RCLASS_SUPER(obj)) { gc_mark(objspace, RCLASS_SUPER(obj)); @@ -10566,6 +10570,10 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_CLASS: + if (FL_TEST(obj, FL_SINGLETON)) { + UPDATE_IF_MOVED(objspace, RCLASS_ATTACHED_OBJECT(obj)); + } + // Continue to the shared T_CLASS/T_MODULE case T_MODULE: if (RCLASS_SUPER((VALUE)obj)) { UPDATE_IF_MOVED(objspace, RCLASS(obj)->super); diff --git a/internal/class.h b/internal/class.h index b777548174..9905e4e388 100644 --- a/internal/class.h +++ b/internal/class.h @@ -53,7 +53,14 @@ struct rb_classext_struct { struct rb_subclass_entry *module_subclass_entry; const VALUE origin_; const VALUE refined_class; - rb_alloc_func_t allocator; + union { + struct { + rb_alloc_func_t allocator; + } class; + struct { + VALUE attached_object; + } singleton_class; + } as; const VALUE includer; #if !SHAPE_IN_BASIC_FLAGS shape_id_t shape_id; @@ -97,11 +104,10 @@ STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t #define RCLASS_INCLUDER(c) (RCLASS_EXT(c)->includer) #define RCLASS_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->subclass_entry) #define RCLASS_MODULE_SUBCLASS_ENTRY(c) (RCLASS_EXT(c)->module_subclass_entry) -#define RCLASS_ALLOCATOR(c) (RCLASS_EXT(c)->allocator) #define RCLASS_SUBCLASSES(c) (RCLASS_EXT(c)->subclasses) #define RCLASS_SUPERCLASS_DEPTH(c) (RCLASS_EXT(c)->superclass_depth) #define RCLASS_SUPERCLASSES(c) (RCLASS_EXT(c)->superclasses) -#define RCLASS_ATTACHED_OBJECT(c) (rb_attr_get(c, id__attached__)) +#define RCLASS_ATTACHED_OBJECT(c) (RCLASS_EXT(c)->as.singleton_class.attached_object) #define RICLASS_IS_ORIGIN FL_USER0 #define RCLASS_CLONED FL_USER1 @@ -147,6 +153,22 @@ VALUE rb_class_inherited(VALUE, VALUE); VALUE rb_keyword_error_new(const char *, VALUE); MJIT_SYMBOL_EXPORT_END +static inline rb_alloc_func_t +RCLASS_ALLOCATOR(VALUE klass) +{ + if (FL_TEST_RAW(klass, FL_SINGLETON)) { + return NULL; + } + return RCLASS_EXT(klass)->as.class.allocator; +} + +static inline void +RCLASS_SET_ALLOCATOR(VALUE klass, rb_alloc_func_t allocator) +{ + assert(!FL_TEST(klass, FL_SINGLETON)); + RCLASS_EXT(klass)->as.class.allocator = allocator; +} + static inline void RCLASS_SET_ORIGIN(VALUE klass, VALUE origin) { @@ -204,9 +226,9 @@ static inline VALUE RCLASS_SET_ATTACHED_OBJECT(VALUE klass, VALUE attached_object) { assert(BUILTIN_TYPE(klass) == T_CLASS); - assert(FL_TEST(klass, FL_SINGLETON)); + assert(FL_TEST_RAW(klass, FL_SINGLETON)); - rb_class_ivar_set(klass, id__attached__, attached_object); + RB_OBJ_WRITE(klass, &RCLASS_EXT(klass)->as.singleton_class.attached_object, attached_object); return attached_object; } diff --git a/marshal.c b/marshal.c index 78e5bde0a2..af952048af 100644 --- a/marshal.c +++ b/marshal.c @@ -523,7 +523,7 @@ hash_each(VALUE key, VALUE value, VALUE v) #define SINGLETON_DUMP_UNABLE_P(klass) \ (rb_id_table_size(RCLASS_M_TBL(klass)) > 0 || \ - rb_ivar_count(klass) > 1) + rb_ivar_count(klass) > 0) static void w_extended(VALUE klass, struct dump_arg *arg, int check) diff --git a/vm_method.c b/vm_method.c index 9af240a2cc..98af316fe6 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1151,7 +1151,10 @@ void rb_define_alloc_func(VALUE klass, VALUE (*func)(VALUE)) { Check_Type(klass, T_CLASS); - RCLASS_ALLOCATOR(klass) = func; + if (FL_TEST_RAW(klass, FL_SINGLETON)) { + rb_raise(rb_eTypeError, "can't define an allocator for a singleton class"); + } + RCLASS_SET_ALLOCATOR(klass, func); } void diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index a961c1290a..74280e4362 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -141,6 +141,7 @@ fn main() { .allowlist_function("rb_ary_tmp_new_from_values") // From include/ruby/internal/intern/class.h + .allowlist_function("rb_class_attached_object") .allowlist_function("rb_singleton_class") // From include/ruby/internal/core/rclass.h diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 15d2c0598f..b83dfa15cc 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -3863,7 +3863,7 @@ fn jit_guard_known_klass( } } else if unsafe { FL_TEST(known_klass, VALUE(RUBY_FL_SINGLETON as usize)) != VALUE(0) - && sample_instance == rb_attr_get(known_klass, id__attached__ as ID) + && sample_instance == rb_class_attached_object(known_klass) } { // Singleton classes are attached to one specific object, so we can // avoid one memory access (and potentially the is_heap check) by diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 8cd61ac8c4..59d065dabd 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -377,66 +377,65 @@ pub const tProc: ruby_method_ids = 190; pub const tLambda: ruby_method_ids = 191; pub const tSend: ruby_method_ids = 192; pub const t__send__: ruby_method_ids = 193; -pub const t__attached__: ruby_method_ids = 194; -pub const t__recursive_key__: ruby_method_ids = 195; -pub const tInitialize: ruby_method_ids = 196; -pub const tInitialize_copy: ruby_method_ids = 197; -pub const tInitialize_clone: ruby_method_ids = 198; -pub const tInitialize_dup: ruby_method_ids = 199; -pub const tTo_int: ruby_method_ids = 200; -pub const tTo_ary: ruby_method_ids = 201; -pub const tTo_str: ruby_method_ids = 202; -pub const tTo_sym: ruby_method_ids = 203; -pub const tTo_hash: ruby_method_ids = 204; -pub const tTo_proc: ruby_method_ids = 205; -pub const tTo_io: ruby_method_ids = 206; -pub const tTo_a: ruby_method_ids = 207; -pub const tTo_s: ruby_method_ids = 208; -pub const tTo_i: ruby_method_ids = 209; -pub const tTo_f: ruby_method_ids = 210; -pub const tTo_r: ruby_method_ids = 211; -pub const tBt: ruby_method_ids = 212; -pub const tBt_locations: ruby_method_ids = 213; -pub const tCall: ruby_method_ids = 214; -pub const tMesg: ruby_method_ids = 215; -pub const tException: ruby_method_ids = 216; -pub const tLocals: ruby_method_ids = 217; -pub const tNOT: ruby_method_ids = 218; -pub const tAND: ruby_method_ids = 219; -pub const tOR: ruby_method_ids = 220; -pub const tDiv: ruby_method_ids = 221; -pub const tDivmod: ruby_method_ids = 222; -pub const tFdiv: ruby_method_ids = 223; -pub const tQuo: ruby_method_ids = 224; -pub const tName: ruby_method_ids = 225; -pub const tNil: ruby_method_ids = 226; -pub const tPath: ruby_method_ids = 227; -pub const tUScore: ruby_method_ids = 228; -pub const tNUMPARAM_1: ruby_method_ids = 229; -pub const tNUMPARAM_2: ruby_method_ids = 230; -pub const tNUMPARAM_3: ruby_method_ids = 231; -pub const tNUMPARAM_4: ruby_method_ids = 232; -pub const tNUMPARAM_5: ruby_method_ids = 233; -pub const tNUMPARAM_6: ruby_method_ids = 234; -pub const tNUMPARAM_7: ruby_method_ids = 235; -pub const tNUMPARAM_8: ruby_method_ids = 236; -pub const tNUMPARAM_9: ruby_method_ids = 237; -pub const tDefault: ruby_method_ids = 238; -pub const tTOKEN_LOCAL_END: ruby_method_ids = 239; -pub const tTOKEN_INSTANCE_BEGIN: ruby_method_ids = 238; -pub const tTOKEN_INSTANCE_END: ruby_method_ids = 239; -pub const tTOKEN_GLOBAL_BEGIN: ruby_method_ids = 238; -pub const tLASTLINE: ruby_method_ids = 239; -pub const tBACKREF: ruby_method_ids = 240; -pub const tERROR_INFO: ruby_method_ids = 241; -pub const tTOKEN_GLOBAL_END: ruby_method_ids = 242; -pub const tTOKEN_CONST_BEGIN: ruby_method_ids = 241; -pub const tTOKEN_CONST_END: ruby_method_ids = 242; -pub const tTOKEN_CLASS_BEGIN: ruby_method_ids = 241; -pub const tTOKEN_CLASS_END: ruby_method_ids = 242; -pub const tTOKEN_ATTRSET_BEGIN: ruby_method_ids = 241; -pub const tTOKEN_ATTRSET_END: ruby_method_ids = 242; -pub const tNEXT_ID: ruby_method_ids = 242; +pub const t__recursive_key__: ruby_method_ids = 194; +pub const tInitialize: ruby_method_ids = 195; +pub const tInitialize_copy: ruby_method_ids = 196; +pub const tInitialize_clone: ruby_method_ids = 197; +pub const tInitialize_dup: ruby_method_ids = 198; +pub const tTo_int: ruby_method_ids = 199; +pub const tTo_ary: ruby_method_ids = 200; +pub const tTo_str: ruby_method_ids = 201; +pub const tTo_sym: ruby_method_ids = 202; +pub const tTo_hash: ruby_method_ids = 203; +pub const tTo_proc: ruby_method_ids = 204; +pub const tTo_io: ruby_method_ids = 205; +pub const tTo_a: ruby_method_ids = 206; +pub const tTo_s: ruby_method_ids = 207; +pub const tTo_i: ruby_method_ids = 208; +pub const tTo_f: ruby_method_ids = 209; +pub const tTo_r: ruby_method_ids = 210; +pub const tBt: ruby_method_ids = 211; +pub const tBt_locations: ruby_method_ids = 212; +pub const tCall: ruby_method_ids = 213; +pub const tMesg: ruby_method_ids = 214; +pub const tException: ruby_method_ids = 215; +pub const tLocals: ruby_method_ids = 216; +pub const tNOT: ruby_method_ids = 217; +pub const tAND: ruby_method_ids = 218; +pub const tOR: ruby_method_ids = 219; +pub const tDiv: ruby_method_ids = 220; +pub const tDivmod: ruby_method_ids = 221; +pub const tFdiv: ruby_method_ids = 222; +pub const tQuo: ruby_method_ids = 223; +pub const tName: ruby_method_ids = 224; +pub const tNil: ruby_method_ids = 225; +pub const tPath: ruby_method_ids = 226; +pub const tUScore: ruby_method_ids = 227; +pub const tNUMPARAM_1: ruby_method_ids = 228; +pub const tNUMPARAM_2: ruby_method_ids = 229; +pub const tNUMPARAM_3: ruby_method_ids = 230; +pub const tNUMPARAM_4: ruby_method_ids = 231; +pub const tNUMPARAM_5: ruby_method_ids = 232; +pub const tNUMPARAM_6: ruby_method_ids = 233; +pub const tNUMPARAM_7: ruby_method_ids = 234; +pub const tNUMPARAM_8: ruby_method_ids = 235; +pub const tNUMPARAM_9: ruby_method_ids = 236; +pub const tDefault: ruby_method_ids = 237; +pub const tTOKEN_LOCAL_END: ruby_method_ids = 238; +pub const tTOKEN_INSTANCE_BEGIN: ruby_method_ids = 237; +pub const tTOKEN_INSTANCE_END: ruby_method_ids = 238; +pub const tTOKEN_GLOBAL_BEGIN: ruby_method_ids = 237; +pub const tLASTLINE: ruby_method_ids = 238; +pub const tBACKREF: ruby_method_ids = 239; +pub const tERROR_INFO: ruby_method_ids = 240; +pub const tTOKEN_GLOBAL_END: ruby_method_ids = 241; +pub const tTOKEN_CONST_BEGIN: ruby_method_ids = 240; +pub const tTOKEN_CONST_END: ruby_method_ids = 241; +pub const tTOKEN_CLASS_BEGIN: ruby_method_ids = 240; +pub const tTOKEN_CLASS_END: ruby_method_ids = 241; +pub const tTOKEN_ATTRSET_BEGIN: ruby_method_ids = 240; +pub const tTOKEN_ATTRSET_END: ruby_method_ids = 241; +pub const tNEXT_ID: ruby_method_ids = 241; pub const idMax: ruby_method_ids = 2721; pub const idMin: ruby_method_ids = 2737; pub const idFreeze: ruby_method_ids = 2753; @@ -461,54 +460,53 @@ pub const idProc: ruby_method_ids = 3041; pub const idLambda: ruby_method_ids = 3057; pub const idSend: ruby_method_ids = 3073; pub const id__send__: ruby_method_ids = 3089; -pub const id__attached__: ruby_method_ids = 3105; -pub const id__recursive_key__: ruby_method_ids = 3121; -pub const idInitialize: ruby_method_ids = 3137; -pub const idInitialize_copy: ruby_method_ids = 3153; -pub const idInitialize_clone: ruby_method_ids = 3169; -pub const idInitialize_dup: ruby_method_ids = 3185; -pub const idTo_int: ruby_method_ids = 3201; -pub const idTo_ary: ruby_method_ids = 3217; -pub const idTo_str: ruby_method_ids = 3233; -pub const idTo_sym: ruby_method_ids = 3249; -pub const idTo_hash: ruby_method_ids = 3265; -pub const idTo_proc: ruby_method_ids = 3281; -pub const idTo_io: ruby_method_ids = 3297; -pub const idTo_a: ruby_method_ids = 3313; -pub const idTo_s: ruby_method_ids = 3329; -pub const idTo_i: ruby_method_ids = 3345; -pub const idTo_f: ruby_method_ids = 3361; -pub const idTo_r: ruby_method_ids = 3377; -pub const idBt: ruby_method_ids = 3393; -pub const idBt_locations: ruby_method_ids = 3409; -pub const idCall: ruby_method_ids = 3425; -pub const idMesg: ruby_method_ids = 3441; -pub const idException: ruby_method_ids = 3457; -pub const idLocals: ruby_method_ids = 3473; -pub const idNOT: ruby_method_ids = 3489; -pub const idAND: ruby_method_ids = 3505; -pub const idOR: ruby_method_ids = 3521; -pub const idDiv: ruby_method_ids = 3537; -pub const idDivmod: ruby_method_ids = 3553; -pub const idFdiv: ruby_method_ids = 3569; -pub const idQuo: ruby_method_ids = 3585; -pub const idName: ruby_method_ids = 3601; -pub const idNil: ruby_method_ids = 3617; -pub const idPath: ruby_method_ids = 3633; -pub const idUScore: ruby_method_ids = 3649; -pub const idNUMPARAM_1: ruby_method_ids = 3665; -pub const idNUMPARAM_2: ruby_method_ids = 3681; -pub const idNUMPARAM_3: ruby_method_ids = 3697; -pub const idNUMPARAM_4: ruby_method_ids = 3713; -pub const idNUMPARAM_5: ruby_method_ids = 3729; -pub const idNUMPARAM_6: ruby_method_ids = 3745; -pub const idNUMPARAM_7: ruby_method_ids = 3761; -pub const idNUMPARAM_8: ruby_method_ids = 3777; -pub const idNUMPARAM_9: ruby_method_ids = 3793; -pub const idDefault: ruby_method_ids = 3809; -pub const idLASTLINE: ruby_method_ids = 3831; -pub const idBACKREF: ruby_method_ids = 3847; -pub const idERROR_INFO: ruby_method_ids = 3863; +pub const id__recursive_key__: ruby_method_ids = 3105; +pub const idInitialize: ruby_method_ids = 3121; +pub const idInitialize_copy: ruby_method_ids = 3137; +pub const idInitialize_clone: ruby_method_ids = 3153; +pub const idInitialize_dup: ruby_method_ids = 3169; +pub const idTo_int: ruby_method_ids = 3185; +pub const idTo_ary: ruby_method_ids = 3201; +pub const idTo_str: ruby_method_ids = 3217; +pub const idTo_sym: ruby_method_ids = 3233; +pub const idTo_hash: ruby_method_ids = 3249; +pub const idTo_proc: ruby_method_ids = 3265; +pub const idTo_io: ruby_method_ids = 3281; +pub const idTo_a: ruby_method_ids = 3297; +pub const idTo_s: ruby_method_ids = 3313; +pub const idTo_i: ruby_method_ids = 3329; +pub const idTo_f: ruby_method_ids = 3345; +pub const idTo_r: ruby_method_ids = 3361; +pub const idBt: ruby_method_ids = 3377; +pub const idBt_locations: ruby_method_ids = 3393; +pub const idCall: ruby_method_ids = 3409; +pub const idMesg: ruby_method_ids = 3425; +pub const idException: ruby_method_ids = 3441; +pub const idLocals: ruby_method_ids = 3457; +pub const idNOT: ruby_method_ids = 3473; +pub const idAND: ruby_method_ids = 3489; +pub const idOR: ruby_method_ids = 3505; +pub const idDiv: ruby_method_ids = 3521; +pub const idDivmod: ruby_method_ids = 3537; +pub const idFdiv: ruby_method_ids = 3553; +pub const idQuo: ruby_method_ids = 3569; +pub const idName: ruby_method_ids = 3585; +pub const idNil: ruby_method_ids = 3601; +pub const idPath: ruby_method_ids = 3617; +pub const idUScore: ruby_method_ids = 3633; +pub const idNUMPARAM_1: ruby_method_ids = 3649; +pub const idNUMPARAM_2: ruby_method_ids = 3665; +pub const idNUMPARAM_3: ruby_method_ids = 3681; +pub const idNUMPARAM_4: ruby_method_ids = 3697; +pub const idNUMPARAM_5: ruby_method_ids = 3713; +pub const idNUMPARAM_6: ruby_method_ids = 3729; +pub const idNUMPARAM_7: ruby_method_ids = 3745; +pub const idNUMPARAM_8: ruby_method_ids = 3761; +pub const idNUMPARAM_9: ruby_method_ids = 3777; +pub const idDefault: ruby_method_ids = 3793; +pub const idLASTLINE: ruby_method_ids = 3815; +pub const idBACKREF: ruby_method_ids = 3831; +pub const idERROR_INFO: ruby_method_ids = 3847; pub const tLAST_OP_ID: ruby_method_ids = 169; pub const idLAST_OP_ID: ruby_method_ids = 10; pub type ruby_method_ids = u32; @@ -1071,6 +1069,7 @@ pub const RUBY_OFFSET_RSTRING_EMBED_LEN: rstring_offsets = 16; pub type rstring_offsets = u32; pub type rb_seq_param_keyword_struct = rb_iseq_constant_body__bindgen_ty_1_rb_iseq_param_keyword; extern "C" { + pub fn rb_class_attached_object(klass: VALUE) -> VALUE; pub fn rb_singleton_class(obj: VALUE) -> VALUE; pub fn rb_get_alloc_func(klass: VALUE) -> rb_alloc_func_t; pub fn rb_method_basic_definition_p(klass: VALUE, mid: ID) -> ::std::os::raw::c_int;