diff options
-rw-r--r-- | class.c | 4 | ||||
-rw-r--r-- | common.mk | 10 | ||||
-rw-r--r-- | ext/-test-/rational/depend | 1 | ||||
-rw-r--r-- | ext/pty/depend | 1 | ||||
-rw-r--r-- | ext/ripper/depend | 1 | ||||
-rw-r--r-- | ext/socket/depend | 15 | ||||
-rw-r--r-- | gc.c | 67 | ||||
-rw-r--r-- | gc.h | 2 | ||||
-rw-r--r-- | gc.rb | 10 | ||||
-rw-r--r-- | include/ruby/internal/core/robject.h | 10 | ||||
-rw-r--r-- | inits.c | 1 | ||||
-rw-r--r-- | internal/class.h | 4 | ||||
-rw-r--r-- | internal/gc.h | 5 | ||||
-rw-r--r-- | internal/variable.h | 3 | ||||
-rw-r--r-- | lib/mjit/compiler.rb | 18 | ||||
-rw-r--r-- | mjit_c.rb | 26 | ||||
-rw-r--r-- | object.c | 77 | ||||
-rw-r--r-- | shape.c | 203 | ||||
-rw-r--r-- | shape.h | 28 | ||||
-rw-r--r-- | test/-ext-/string/test_cstr.rb | 6 | ||||
-rw-r--r-- | test/objspace/test_objspace.rb | 26 | ||||
-rw-r--r-- | test/ruby/test_gc_compact.rb | 13 | ||||
-rw-r--r-- | test/ruby/test_shapes.rb | 19 | ||||
-rw-r--r-- | variable.c | 61 | ||||
-rw-r--r-- | vm.c | 11 | ||||
-rw-r--r-- | vm_core.h | 1 | ||||
-rw-r--r-- | vm_insnhelper.c | 52 | ||||
-rw-r--r-- | yjit/src/cruby_bindings.inc.rs | 9 |
28 files changed, 482 insertions, 202 deletions
@@ -197,7 +197,7 @@ class_alloc(VALUE flags, VALUE klass) { size_t alloc_size = sizeof(struct RClass); -#if USE_RVARGC +#if RCLASS_EXT_EMBEDDED alloc_size += sizeof(rb_classext_t); #endif @@ -206,7 +206,7 @@ class_alloc(VALUE flags, VALUE klass) if (RGENGC_WB_PROTECTED_CLASS) flags |= FL_WB_PROTECTED; RVARGC_NEWOBJ_OF(obj, struct RClass, klass, flags, alloc_size); -#if USE_RVARGC +#if RCLASS_EXT_EMBEDDED memset(RCLASS_EXT(obj), 0, sizeof(rb_classext_t)); #else obj->ptr = ZALLOC(rb_classext_t); @@ -6038,6 +6038,7 @@ enumerator.$(OBJEXT): {$(VPATH)}missing.h enumerator.$(OBJEXT): {$(VPATH)}onigmo.h enumerator.$(OBJEXT): {$(VPATH)}oniguruma.h enumerator.$(OBJEXT): {$(VPATH)}ruby_assert.h +enumerator.$(OBJEXT): {$(VPATH)}shape.h enumerator.$(OBJEXT): {$(VPATH)}st.h enumerator.$(OBJEXT): {$(VPATH)}subst.h error.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h @@ -9376,6 +9377,7 @@ memory_view.$(OBJEXT): {$(VPATH)}internal/xmalloc.h memory_view.$(OBJEXT): {$(VPATH)}memory_view.c memory_view.$(OBJEXT): {$(VPATH)}memory_view.h memory_view.$(OBJEXT): {$(VPATH)}missing.h +memory_view.$(OBJEXT): {$(VPATH)}shape.h memory_view.$(OBJEXT): {$(VPATH)}st.h memory_view.$(OBJEXT): {$(VPATH)}subst.h memory_view.$(OBJEXT): {$(VPATH)}util.h @@ -10629,6 +10631,7 @@ object.$(OBJEXT): {$(VPATH)}shape.h object.$(OBJEXT): {$(VPATH)}st.h object.$(OBJEXT): {$(VPATH)}subst.h object.$(OBJEXT): {$(VPATH)}util.h +object.$(OBJEXT): {$(VPATH)}variable.h pack.$(OBJEXT): $(hdrdir)/ruby/ruby.h pack.$(OBJEXT): $(top_srcdir)/internal/array.h pack.$(OBJEXT): $(top_srcdir)/internal/bits.h @@ -10810,6 +10813,7 @@ pack.$(OBJEXT): {$(VPATH)}onigmo.h pack.$(OBJEXT): {$(VPATH)}oniguruma.h pack.$(OBJEXT): {$(VPATH)}pack.c pack.$(OBJEXT): {$(VPATH)}pack.rbinc +pack.$(OBJEXT): {$(VPATH)}shape.h pack.$(OBJEXT): {$(VPATH)}st.h pack.$(OBJEXT): {$(VPATH)}subst.h pack.$(OBJEXT): {$(VPATH)}util.h @@ -11022,6 +11026,7 @@ parse.$(OBJEXT): {$(VPATH)}ractor.h parse.$(OBJEXT): {$(VPATH)}regenc.h parse.$(OBJEXT): {$(VPATH)}regex.h parse.$(OBJEXT): {$(VPATH)}ruby_assert.h +parse.$(OBJEXT): {$(VPATH)}shape.h parse.$(OBJEXT): {$(VPATH)}st.h parse.$(OBJEXT): {$(VPATH)}subst.h parse.$(OBJEXT): {$(VPATH)}symbol.h @@ -11853,6 +11858,7 @@ random.$(OBJEXT): {$(VPATH)}ractor.h random.$(OBJEXT): {$(VPATH)}random.c random.$(OBJEXT): {$(VPATH)}random.h random.$(OBJEXT): {$(VPATH)}ruby_atomic.h +random.$(OBJEXT): {$(VPATH)}shape.h random.$(OBJEXT): {$(VPATH)}siphash.c random.$(OBJEXT): {$(VPATH)}siphash.h random.$(OBJEXT): {$(VPATH)}st.h @@ -12045,6 +12051,7 @@ range.$(OBJEXT): {$(VPATH)}missing.h range.$(OBJEXT): {$(VPATH)}onigmo.h range.$(OBJEXT): {$(VPATH)}oniguruma.h range.$(OBJEXT): {$(VPATH)}range.c +range.$(OBJEXT): {$(VPATH)}shape.h range.$(OBJEXT): {$(VPATH)}st.h range.$(OBJEXT): {$(VPATH)}subst.h rational.$(OBJEXT): $(hdrdir)/ruby/ruby.h @@ -14024,6 +14031,7 @@ shape.$(OBJEXT): {$(VPATH)}constant.h shape.$(OBJEXT): {$(VPATH)}debug_counter.h shape.$(OBJEXT): {$(VPATH)}defines.h shape.$(OBJEXT): {$(VPATH)}encoding.h +shape.$(OBJEXT): {$(VPATH)}gc.h shape.$(OBJEXT): {$(VPATH)}id.h shape.$(OBJEXT): {$(VPATH)}id_table.h shape.$(OBJEXT): {$(VPATH)}intern.h @@ -16007,6 +16015,7 @@ time.$(OBJEXT): {$(VPATH)}missing.h time.$(OBJEXT): {$(VPATH)}onigmo.h time.$(OBJEXT): {$(VPATH)}oniguruma.h time.$(OBJEXT): {$(VPATH)}ruby_assert.h +time.$(OBJEXT): {$(VPATH)}shape.h time.$(OBJEXT): {$(VPATH)}st.h time.$(OBJEXT): {$(VPATH)}subst.h time.$(OBJEXT): {$(VPATH)}time.c @@ -16371,6 +16380,7 @@ transient_heap.$(OBJEXT): {$(VPATH)}internal/warning_push.h transient_heap.$(OBJEXT): {$(VPATH)}internal/xmalloc.h transient_heap.$(OBJEXT): {$(VPATH)}missing.h transient_heap.$(OBJEXT): {$(VPATH)}ruby_assert.h +transient_heap.$(OBJEXT): {$(VPATH)}shape.h transient_heap.$(OBJEXT): {$(VPATH)}st.h transient_heap.$(OBJEXT): {$(VPATH)}subst.h transient_heap.$(OBJEXT): {$(VPATH)}transient_heap.c diff --git a/ext/-test-/rational/depend b/ext/-test-/rational/depend index 8729695886..ce977821b8 100644 --- a/ext/-test-/rational/depend +++ b/ext/-test-/rational/depend @@ -174,5 +174,6 @@ rat.o: $(top_srcdir)/internal/static_assert.h rat.o: $(top_srcdir)/internal/vm.h rat.o: $(top_srcdir)/internal/warnings.h rat.o: $(top_srcdir)/ruby_assert.h +rat.o: $(top_srcdir)/shape.h rat.o: rat.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/pty/depend b/ext/pty/depend index c43d3dcf9a..f251caae3f 100644 --- a/ext/pty/depend +++ b/ext/pty/depend @@ -181,5 +181,6 @@ pty.o: $(top_srcdir)/internal/process.h pty.o: $(top_srcdir)/internal/signal.h pty.o: $(top_srcdir)/internal/static_assert.h pty.o: $(top_srcdir)/internal/warnings.h +pty.o: $(top_srcdir)/shape.h pty.o: pty.c # AUTOGENERATED DEPENDENCIES END diff --git a/ext/ripper/depend b/ext/ripper/depend index c77e4e1b7a..85520b032e 100644 --- a/ext/ripper/depend +++ b/ext/ripper/depend @@ -252,6 +252,7 @@ ripper.o: $(top_srcdir)/internal/warnings.h ripper.o: $(top_srcdir)/node.h ripper.o: $(top_srcdir)/regenc.h ripper.o: $(top_srcdir)/ruby_assert.h +ripper.o: $(top_srcdir)/shape.h ripper.o: $(top_srcdir)/symbol.h ripper.o: ../../probes.h ripper.o: eventids2.c diff --git a/ext/socket/depend b/ext/socket/depend index ffe2fce844..28c5540cd6 100644 --- a/ext/socket/depend +++ b/ext/socket/depend @@ -197,6 +197,7 @@ ancdata.o: $(top_srcdir)/internal/string.h ancdata.o: $(top_srcdir)/internal/thread.h ancdata.o: $(top_srcdir)/internal/vm.h ancdata.o: $(top_srcdir)/internal/warnings.h +ancdata.o: $(top_srcdir)/shape.h ancdata.o: ancdata.c ancdata.o: constdefs.h ancdata.o: rubysocket.h @@ -388,6 +389,7 @@ basicsocket.o: $(top_srcdir)/internal/string.h basicsocket.o: $(top_srcdir)/internal/thread.h basicsocket.o: $(top_srcdir)/internal/vm.h basicsocket.o: $(top_srcdir)/internal/warnings.h +basicsocket.o: $(top_srcdir)/shape.h basicsocket.o: basicsocket.c basicsocket.o: constdefs.h basicsocket.o: rubysocket.h @@ -579,6 +581,7 @@ constants.o: $(top_srcdir)/internal/string.h constants.o: $(top_srcdir)/internal/thread.h constants.o: $(top_srcdir)/internal/vm.h constants.o: $(top_srcdir)/internal/warnings.h +constants.o: $(top_srcdir)/shape.h constants.o: constants.c constants.o: constdefs.c constants.o: constdefs.h @@ -771,6 +774,7 @@ ifaddr.o: $(top_srcdir)/internal/string.h ifaddr.o: $(top_srcdir)/internal/thread.h ifaddr.o: $(top_srcdir)/internal/vm.h ifaddr.o: $(top_srcdir)/internal/warnings.h +ifaddr.o: $(top_srcdir)/shape.h ifaddr.o: constdefs.h ifaddr.o: ifaddr.c ifaddr.o: rubysocket.h @@ -962,6 +966,7 @@ init.o: $(top_srcdir)/internal/string.h init.o: $(top_srcdir)/internal/thread.h init.o: $(top_srcdir)/internal/vm.h init.o: $(top_srcdir)/internal/warnings.h +init.o: $(top_srcdir)/shape.h init.o: constdefs.h init.o: init.c init.o: rubysocket.h @@ -1153,6 +1158,7 @@ ipsocket.o: $(top_srcdir)/internal/string.h ipsocket.o: $(top_srcdir)/internal/thread.h ipsocket.o: $(top_srcdir)/internal/vm.h ipsocket.o: $(top_srcdir)/internal/warnings.h +ipsocket.o: $(top_srcdir)/shape.h ipsocket.o: constdefs.h ipsocket.o: ipsocket.c ipsocket.o: rubysocket.h @@ -1344,6 +1350,7 @@ option.o: $(top_srcdir)/internal/string.h option.o: $(top_srcdir)/internal/thread.h option.o: $(top_srcdir)/internal/vm.h option.o: $(top_srcdir)/internal/warnings.h +option.o: $(top_srcdir)/shape.h option.o: constdefs.h option.o: option.c option.o: rubysocket.h @@ -1535,6 +1542,7 @@ raddrinfo.o: $(top_srcdir)/internal/string.h raddrinfo.o: $(top_srcdir)/internal/thread.h raddrinfo.o: $(top_srcdir)/internal/vm.h raddrinfo.o: $(top_srcdir)/internal/warnings.h +raddrinfo.o: $(top_srcdir)/shape.h raddrinfo.o: constdefs.h raddrinfo.o: raddrinfo.c raddrinfo.o: rubysocket.h @@ -1726,6 +1734,7 @@ socket.o: $(top_srcdir)/internal/string.h socket.o: $(top_srcdir)/internal/thread.h socket.o: $(top_srcdir)/internal/vm.h socket.o: $(top_srcdir)/internal/warnings.h +socket.o: $(top_srcdir)/shape.h socket.o: constdefs.h socket.o: rubysocket.h socket.o: socket.c @@ -1917,6 +1926,7 @@ sockssocket.o: $(top_srcdir)/internal/string.h sockssocket.o: $(top_srcdir)/internal/thread.h sockssocket.o: $(top_srcdir)/internal/vm.h sockssocket.o: $(top_srcdir)/internal/warnings.h +sockssocket.o: $(top_srcdir)/shape.h sockssocket.o: constdefs.h sockssocket.o: rubysocket.h sockssocket.o: sockport.h @@ -2108,6 +2118,7 @@ tcpserver.o: $(top_srcdir)/internal/string.h tcpserver.o: $(top_srcdir)/internal/thread.h tcpserver.o: $(top_srcdir)/internal/vm.h tcpserver.o: $(top_srcdir)/internal/warnings.h +tcpserver.o: $(top_srcdir)/shape.h tcpserver.o: constdefs.h tcpserver.o: rubysocket.h tcpserver.o: sockport.h @@ -2299,6 +2310,7 @@ tcpsocket.o: $(top_srcdir)/internal/string.h tcpsocket.o: $(top_srcdir)/internal/thread.h tcpsocket.o: $(top_srcdir)/internal/vm.h tcpsocket.o: $(top_srcdir)/internal/warnings.h +tcpsocket.o: $(top_srcdir)/shape.h tcpsocket.o: constdefs.h tcpsocket.o: rubysocket.h tcpsocket.o: sockport.h @@ -2490,6 +2502,7 @@ udpsocket.o: $(top_srcdir)/internal/string.h udpsocket.o: $(top_srcdir)/internal/thread.h udpsocket.o: $(top_srcdir)/internal/vm.h udpsocket.o: $(top_srcdir)/internal/warnings.h +udpsocket.o: $(top_srcdir)/shape.h udpsocket.o: constdefs.h udpsocket.o: rubysocket.h udpsocket.o: sockport.h @@ -2681,6 +2694,7 @@ unixserver.o: $(top_srcdir)/internal/string.h unixserver.o: $(top_srcdir)/internal/thread.h unixserver.o: $(top_srcdir)/internal/vm.h unixserver.o: $(top_srcdir)/internal/warnings.h +unixserver.o: $(top_srcdir)/shape.h unixserver.o: constdefs.h unixserver.o: rubysocket.h unixserver.o: sockport.h @@ -2872,6 +2886,7 @@ unixsocket.o: $(top_srcdir)/internal/string.h unixsocket.o: $(top_srcdir)/internal/thread.h unixsocket.o: $(top_srcdir)/internal/vm.h unixsocket.o: $(top_srcdir)/internal/warnings.h +unixsocket.o: $(top_srcdir)/shape.h unixsocket.o: constdefs.h unixsocket.o: rubysocket.h unixsocket.o: sockport.h @@ -138,6 +138,7 @@ #include "ractor_core.h" #include "builtin.h" +#include "shape.h" #define rb_setjmp(env) RUBY_SETJMP(env) #define rb_jmp_buf rb_jmpbuf_t @@ -2593,6 +2594,12 @@ size_pool_slot_size(unsigned char pool_id) return slot_size; } +size_t +rb_size_pool_slot_size(unsigned char pool_id) +{ + return size_pool_slot_size(pool_id); +} + bool rb_gc_size_allocatable_p(size_t size) { @@ -2797,6 +2804,9 @@ newobj_slowpath(VALUE klass, VALUE flags, rb_objspace_t *objspace, rb_ractor_t * } obj = newobj_alloc(objspace, cr, size_pool_idx, true); +#if SHAPE_IN_BASIC_FLAGS + flags |= (VALUE)(size_pool_idx) << SHAPE_FLAG_SHIFT; +#endif newobj_init(klass, flags, wb_protected, objspace, obj); gc_event_hook_prep(objspace, RUBY_INTERNAL_EVENT_NEWOBJ, obj, newobj_fill(obj, 0, 0, 0)); @@ -2848,6 +2858,9 @@ newobj_of0(VALUE klass, VALUE flags, int wb_protected, rb_ractor_t *cr, size_t a gc_event_hook_available_p(objspace)) && wb_protected) { obj = newobj_alloc(objspace, cr, size_pool_idx, false); +#if SHAPE_IN_BASIC_FLAGS + flags |= (VALUE)size_pool_idx << SHAPE_FLAG_SHIFT; +#endif newobj_init(klass, flags, wb_protected, objspace, obj); } else { @@ -2916,10 +2929,10 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) GC_ASSERT((flags & RUBY_T_MASK) == T_OBJECT); GC_ASSERT(flags & ROBJECT_EMBED); - uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count; - size_t size; #if USE_RVARGC + uint32_t index_tbl_num_entries = RCLASS_EXT(klass)->max_iv_count; + size = rb_obj_embedded_size(index_tbl_num_entries); if (!rb_gc_size_allocatable_p(size)) { size = sizeof(struct RObject); @@ -2932,7 +2945,7 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) #if USE_RVARGC uint32_t capa = (uint32_t)((rb_gc_obj_slot_size(obj) - offsetof(struct RObject, as.ary)) / sizeof(VALUE)); - ROBJECT(obj)->numiv = capa; + ROBJECT_SET_NUMIV(obj, capa); #endif #if RUBY_DEBUG @@ -3454,7 +3467,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj) xfree(RCLASS_SUPERCLASSES(obj)); } -#if !USE_RVARGC +#if SIZE_POOL_COUNT == 1 if (RCLASS_EXT(obj)) xfree(RCLASS_EXT(obj)); #endif @@ -4869,7 +4882,7 @@ obj_memsize_of(VALUE obj, int use_all_types) if (FL_TEST_RAW(obj, RCLASS_SUPERCLASSES_INCLUDE_SELF)) { size += (RCLASS_SUPERCLASS_DEPTH(obj) + 1) * sizeof(VALUE); } -#if !USE_RVARGC +#if SIZE_POOL_COUNT == 1 size += sizeof(rb_classext_t); #endif } @@ -6054,6 +6067,7 @@ invalidate_moved_plane(rb_objspace_t *objspace, struct heap_page *page, uintptr_ gc_move(objspace, object, forwarding_object, GET_HEAP_PAGE(object)->slot_size, page->slot_size); /* forwarding_object is now our actual object, and "object" * is the free slot for the original page */ + struct heap_page *orig_page = GET_HEAP_PAGE(object); orig_page->free_slots++; heap_page_add_freeobj(objspace, orig_page, object); @@ -8387,6 +8401,7 @@ static rb_size_pool_t * gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, VALUE src) { size_t obj_size; + size_t idx = 0; switch (BUILTIN_TYPE(src)) { case T_ARRAY: @@ -8406,17 +8421,16 @@ gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, V } if (rb_gc_size_allocatable_p(obj_size)){ - return &size_pools[size_pool_idx_for_size(obj_size)]; - } - else { - return &size_pools[0]; + idx = size_pool_idx_for_size(obj_size); } + return &size_pools[idx]; } static bool gc_compact_move(rb_objspace_t *objspace, rb_heap_t *heap, rb_size_pool_t *size_pool, VALUE src) { GC_ASSERT(BUILTIN_TYPE(src) != T_MOVED); + rb_heap_t *dheap = SIZE_POOL_EDEN_HEAP(gc_compact_destination_pool(objspace, size_pool, src)); if (gc_compact_heap_cursors_met_p(dheap)) { @@ -10003,9 +10017,10 @@ static void gc_ref_update_object(rb_objspace_t *objspace, VALUE v) { VALUE *ptr = ROBJECT_IVPTR(v); - uint32_t numiv = ROBJECT_NUMIV(v); #if USE_RVARGC + uint32_t numiv = ROBJECT_NUMIV(v); + size_t slot_size = rb_gc_obj_slot_size(v); size_t embed_size = rb_obj_embedded_size(numiv); if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) { @@ -10019,9 +10034,17 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v) xfree(ptr); } ptr = ROBJECT(v)->as.ary; - - uint32_t capa = (uint32_t)((slot_size - offsetof(struct RObject, as.ary)) / sizeof(VALUE)); - ROBJECT(v)->numiv = capa; + size_t size_pool_shape_id = size_pool_idx_for_size(embed_size); + rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id); + rb_shape_t * new_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_shape(v)); + rb_shape_set_shape(v, new_shape); + ROBJECT_SET_NUMIV(v, new_shape->capacity); +#if RUBY_DEBUG + if(RB_TYPE_P(v, T_OBJECT) && ROBJECT_IV_CAPACITY(v) != ROBJECT_NUMIV(v)) { + fprintf(stderr, "shape capa: %d, v capa: %d\n", ROBJECT_IV_CAPACITY(v), ROBJECT_NUMIV(v)); + } +#endif + RUBY_ASSERT(!RB_TYPE_P(v, T_OBJECT) || ROBJECT_IV_CAPACITY(v) == ROBJECT_NUMIV(v)); } #endif @@ -14293,6 +14316,22 @@ rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self) */ #include "gc.rbinc" +/* + * call-seq: + * GC.using_rvargc? -> true or false + * + * Returns true if using experimental feature Variable Width Allocation, false + * otherwise. + */ +static VALUE +gc_using_rvargc_p(VALUE mod) +{ +#if USE_RVARGC + return Qtrue; +#else + return Qfalse; +#endif +} void Init_GC(void) @@ -14371,6 +14410,8 @@ Init_GC(void) rb_define_singleton_method(rb_mGC, "malloc_allocations", gc_malloc_allocations, 0); #endif + rb_define_singleton_method(rb_mGC, "using_rvargc?", gc_using_rvargc_p, 0); + if (GC_COMPACTION_SUPPORTED) { rb_define_singleton_method(rb_mGC, "compact", gc_compact, 0); rb_define_singleton_method(rb_mGC, "auto_compact", gc_get_auto_compact, 0); @@ -120,6 +120,8 @@ VALUE rb_gc_disable_no_rest(void); struct rb_thread_struct; +size_t rb_size_pool_slot_size(unsigned char pool_id); + RUBY_SYMBOL_EXPORT_BEGIN /* exports for objspace module */ @@ -253,16 +253,6 @@ module GC end # call-seq: - # GC.using_rvargc? -> true or false - # - # Returns true if using experimental feature Variable Width Allocation, false - # otherwise. - def self.using_rvargc? # :nodoc: - GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] > 1 - end - - - # call-seq: # GC.measure_total_time = true/false # # Enable to measure GC time. diff --git a/include/ruby/internal/core/robject.h b/include/ruby/internal/core/robject.h index bec0b45fd4..e0514d7dd2 100644 --- a/include/ruby/internal/core/robject.h +++ b/include/ruby/internal/core/robject.h @@ -192,6 +192,16 @@ ROBJECT_NUMIV(VALUE obj) #endif } +static inline void +ROBJECT_SET_NUMIV(VALUE obj, uint32_t capacity) +{ +#if USE_RVARGC + ROBJECT(obj)->numiv = capacity; +#else + ROBJECT(obj)->as.heap.numiv = capacity; +#endif +} + RBIMPL_ATTR_PURE_UNLESS_DEBUG() RBIMPL_ATTR_ARTIFICIAL() /** @@ -20,6 +20,7 @@ static void Init_builtin_prelude(void); void rb_call_inits(void) { + CALL(default_shapes); CALL(Thread_Mutex); #if USE_TRANSIENT_HEAP CALL(TransientHeap); diff --git a/internal/class.h b/internal/class.h index 784d508e20..8080725634 100644 --- a/internal/class.h +++ b/internal/class.h @@ -62,7 +62,7 @@ struct RClass { struct RBasic basic; VALUE super; struct rb_id_table *m_tbl; -#if !USE_RVARGC +#if SIZE_POOL_COUNT == 1 struct rb_classext_struct *ptr; #endif }; @@ -70,7 +70,7 @@ struct RClass { typedef struct rb_subclass_entry rb_subclass_entry_t; typedef struct rb_classext_struct rb_classext_t; -#if USE_RVARGC +#if RCLASS_EXT_EMBEDDED # define RCLASS_EXT(c) ((rb_classext_t *)((char *)(c) + sizeof(struct RClass))) #else # define RCLASS_EXT(c) (RCLASS(c)->ptr) diff --git a/internal/gc.h b/internal/gc.h index 84b7f9fa3e..5b2b9e8f70 100644 --- a/internal/gc.h +++ b/internal/gc.h @@ -14,6 +14,7 @@ #include "internal/compilers.h" /* for __has_attribute */ #include "ruby/ruby.h" /* for rb_event_flag_t */ +#include "shape.h" struct rb_execution_context_struct; /* in vm_core.h */ struct rb_objspace; /* in vm_core.h */ @@ -67,12 +68,14 @@ struct rb_objspace; /* in vm_core.h */ rb_obj_write((VALUE)(a), UNALIGNED_MEMBER_ACCESS((VALUE *)(slot)), \ (VALUE)(b), __FILE__, __LINE__) -#if USE_RVARGC +#if USE_RVARGC && SHAPE_IN_BASIC_FLAGS # define SIZE_POOL_COUNT 5 #else # define SIZE_POOL_COUNT 1 #endif +#define RCLASS_EXT_EMBEDDED (SIZE_POOL_COUNT > 1) + typedef struct ractor_newobj_size_pool_cache { struct RVALUE *freelist; struct heap_page *using_page; diff --git a/internal/variable.h b/internal/variable.h index 734884a5f6..553e87c4a8 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -13,6 +13,7 @@ #include "constant.h" /* for rb_const_entry_t */ #include "ruby/internal/stdbool.h" /* for bool */ #include "ruby/ruby.h" /* for VALUE */ +#include "shape.h" /* for rb_shape_t */ /* global variable */ @@ -53,7 +54,7 @@ VALUE rb_gvar_get(ID); VALUE rb_gvar_set(ID, VALUE); VALUE rb_gvar_defined(ID); void rb_const_warn_if_deprecated(const rb_const_entry_t *, VALUE, ID); -void rb_init_iv_list(VALUE obj); +rb_shape_t * rb_grow_iv_list(VALUE obj); void rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize); struct gen_ivtbl * rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize); MJIT_SYMBOL_EXPORT_END diff --git a/lib/mjit/compiler.rb b/lib/mjit/compiler.rb index 55fcee6b87..575ae6f84c 100644 --- a/lib/mjit/compiler.rb +++ b/lib/mjit/compiler.rb @@ -353,10 +353,20 @@ module RubyVM::MJIT ic_copy = (status.is_entries + (C.iseq_inline_storage_entry.new(operands[1]) - body.is_entries)).iv_cache dest_shape_id = ic_copy.value >> C.SHAPE_FLAG_SHIFT attr_index = ic_copy.value & ((1 << C.SHAPE_FLAG_SHIFT) - 1) + + capa = nil source_shape_id = if dest_shape_id == C.INVALID_SHAPE_ID dest_shape_id else - C.rb_shape_get_shape_by_id(dest_shape_id).parent_id + parent_id = C.rb_shape_get_shape_by_id(dest_shape_id).parent_id + parent = C.rb_shape_get_shape_by_id(parent_id) + + if parent.type == C.SHAPE_CAPACITY_CHANGE + capa = parent.capacity + parent.parent_id + else + parent_id + end end src = +'' @@ -374,9 +384,9 @@ module RubyVM::MJIT src << " const shape_id_t dest_shape_id = (shape_id_t)#{dest_shape_id};\n" src << " if (source_shape_id == ROBJECT_SHAPE_ID(obj) && \n" src << " dest_shape_id != ROBJECT_SHAPE_ID(obj)) {\n" - src << " if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) {\n" - src << " rb_init_iv_list(obj);\n" - src << " }\n" + # Conditionally generate a capacity change if there is one + # between the destination and the parent IV set + src << " rb_ensure_iv_list_size(obj, RBOJECT_NUMIV(obj), #{capa});\n" if capa src << " ROBJECT_SET_SHAPE_ID(obj, dest_shape_id);\n" src << " VALUE *ptr = ROBJECT_IVPTR(obj);\n" src << " RB_OBJ_WRITE(obj, &ptr[index], stack[#{stack_size - 1}]);\n" @@ -13,6 +13,30 @@ module RubyVM::MJIT Primitive.cexpr! 'UINT2NUM(SHAPE_FLAG_SHIFT)' end + def SHAPE_ROOT + Primitive.cexpr! 'UINT2NUM(SHAPE_ROOT)' + end + + def SHAPE_IVAR + Primitive.cexpr! 'UINT2NUM(SHAPE_IVAR)' + end + + def SHAPE_FROZEN + Primitive.cexpr! 'UINT2NUM(SHAPE_FROZEN)' + end + + def SHAPE_CAPACITY_CHANGE + Primitive.cexpr! 'UINT2NUM(SHAPE_CAPACITY_CHANGE)' + end + + def SHAPE_IVAR_UNDEF + Primitive.cexpr! 'UINT2NUM(SHAPE_IVAR_UNDEF)' + end + + def SHAPE_INITIAL_CAPACITY + Primitive.cexpr! 'UINT2NUM(SHAPE_INITIAL_CAPACITY)' + end + def ROBJECT_EMBED_LEN_MAX Primitive.cexpr! 'INT2NUM(RBIMPL_EMBED_LEN_MAX_OF(VALUE))' end @@ -598,7 +622,9 @@ module RubyVM::MJIT edges: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edges)")], edge_name: [self.ID, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), edge_name)")], next_iv_index: [self.attr_index_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), next_iv_index)")], + capacity: [CType::Immediate.parse("uint32_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), capacity)")], type: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), type)")], + size_pool_index: [CType::Immediate.parse("uint8_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), size_pool_index)")], parent_id: [self.shape_id_t, Primitive.cexpr!("OFFSETOF((*((struct rb_shape *)NULL)), parent_id)")], ) end @@ -33,6 +33,7 @@ #include "internal/string.h" #include "internal/symbol.h" #include "internal/variable.h" +#include "variable.h" #include "probes.h" #include "ruby/encoding.h" #include "ruby/st.h" @@ -268,21 +269,64 @@ rb_obj_singleton_class(VALUE obj) MJIT_FUNC_EXPORTED void rb_obj_copy_ivar(VALUE dest, VALUE obj) { - uint32_t dest_len = ROBJECT_NUMIV(dest); - uint32_t src_len = ROBJECT_NUMIV(obj); + RUBY_ASSERT(!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE)); - if (dest_len < src_len) { - rb_ensure_iv_list_size(dest, dest_len, src_len); - RUBY_ASSERT(!(RBASIC(dest)->flags & ROBJECT_EMBED)); + RUBY_ASSERT(BUILTIN_TYPE(dest) == BUILTIN_TYPE(obj)); + uint32_t src_num_ivs = RBASIC_IV_COUNT(obj); + rb_shape_t * src_shape = rb_shape_get_shape(obj); + rb_shape_t * shape_to_set_on_dest = src_shape; + VALUE * src_buf; + VALUE * dest_buf; + + if (!src_num_ivs) { + return; } - else { - RUBY_ASSERT((RBASIC(dest)->flags & ROBJECT_EMBED)); + + // The copy should be mutable, so we don't want the frozen shape + if (rb_shape_frozen_shape_p(src_shape)) { + shape_to_set_on_dest = rb_shape_get_shape_by_id(src_shape->parent_id); + } + + src_buf = ROBJECT_IVPTR(obj); + dest_buf = ROBJECT_IVPTR(dest); + + rb_shape_t * initial_shape = rb_shape_get_shape(dest); + + if (initial_shape->size_pool_index != src_shape->size_pool_index) { + RUBY_ASSERT(initial_shape->parent_id == ROOT_SHAPE_ID || initial_shape->type == SHAPE_ROOT); + + shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape); } - VALUE * dest_buf = ROBJECT_IVPTR(dest); - VALUE * src_buf = ROBJECT_IVPTR(obj); + RUBY_ASSERT(src_num_ivs <= shape_to_set_on_dest->capacity); + if (initial_shape->capacity < shape_to_set_on_dest->capacity) { + rb_ensure_iv_list_size(dest, initial_shape->capacity, shape_to_set_on_dest->capacity); + dest_buf = ROBJECT_IVPTR(dest); + + rb_shape_t * initial_shape = rb_shape_get_shape(dest); + + if (initial_shape->size_pool_index != src_shape->size_pool_index) { + RUBY_ASSERT(initial_shape->parent_id == ROOT_SHAPE_ID || initial_shape->type == SHAPE_ROOT); + + shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape); + } - MEMCPY(dest_buf, src_buf, VALUE, ROBJECT_IV_COUNT(obj)); + RUBY_ASSERT(src_num_ivs <= shape_to_set_on_dest->capacity); + if (initial_shape->capacity < shape_to_set_on_dest->capacity) { + rb_ensure_iv_list_size(dest, initial_shape->capacity, shape_to_set_on_dest->capacity); + dest_buf = ROBJECT_IVPTR(dest); + } + } + + MEMCPY(dest_buf, src_buf, VALUE, src_num_ivs); + + // Fire write barriers + for (uint32_t i = 0; i < src_num_ivs; i++) { + RB_OBJ_WRITTEN(dest, Qundef, dest_buf[i]); + } + + rb_shape_set_shape(dest, shape_to_set_on_dest); + RUBY_ASSERT(!RB_TYPE_P(obj, T_OBJECT) || ROBJECT_IV_CAPACITY(dest) == ROBJECT_NUMIV(dest)); } static void @@ -301,19 +345,6 @@ init_copy(VALUE dest, VALUE obj) if (RB_TYPE_P(obj, T_OBJECT)) { rb_obj_copy_ivar(dest, obj); } - - if (!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE)) { - rb_shape_t *shape_to_set = rb_shape_get_shape(obj); - - // If the object is frozen, the "dup"'d object will *not* be frozen, - // so we need to copy the frozen shape's parent to the new object. - if (rb_shape_frozen_shape_p(shape_to_set)) { - shape_to_set = rb_shape_get_shape_by_id(shape_to_set->parent_id); - } - - // shape ids are different - rb_shape_set_shape(dest, shape_to_set); - } } static VALUE immutable_obj_clone(VALUE obj, VALUE kwfreeze); @@ -1,15 +1,19 @@ #include "vm_core.h" #include "vm_sync.h" #include "shape.h" +#include "gc.h" #include "internal/class.h" #include "internal/symbol.h" #include "internal/variable.h" #include <stdbool.h> +static ID id_frozen; +static ID size_pool_edge_names[SIZE_POOL_COUNT]; + /* * Shape getters */ -static rb_shape_t* +rb_shape_t * rb_shape_get_root_shape(void) { return GET_VM()->root_shape; @@ -21,12 +25,6 @@ rb_shape_id(rb_shape_t * shape) return (shape_id_t)(shape - GET_VM()->shape_list); } -static rb_shape_t* -rb_shape_get_frozen_root_shape(void) -{ - return GET_VM()->frozen_root_shape; -} - bool rb_shape_root_shape_p(rb_shape_t* shape) { @@ -68,7 +66,7 @@ shape_id_t rb_shape_get_shape_id(VALUE obj) { if (RB_SPECIAL_CONST_P(obj)) { - return FROZEN_ROOT_SHAPE_ID; + return SPECIAL_CONST_SHAPE_ID; } #if SHAPE_IN_BASIC_FLAGS @@ -113,12 +111,9 @@ rb_shape_lookup_id(rb_shape_t* shape, ID id, enum shape_type shape_type) } static rb_shape_t* -get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type shape_type) +get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type) { rb_shape_t *res = NULL; - - RUBY_ASSERT(SHAPE_FROZEN != (enum shape_type)shape->type || RB_TYPE_P(obj, T_MODULE) || RB_TYPE_P(obj, T_CLASS)); - RB_VM_LOCK_ENTER(); { if (rb_shape_lookup_id(shape, id, shape_type)) { @@ -142,23 +137,18 @@ get_next_shape_internal(rb_shape_t* shape, ID id, VALUE obj, enum shape_type sha rb_shape_t * new_shape = rb_shape_alloc(id, shape); new_shape->type = (uint8_t)shape_type; + new_shape->capacity = shape->capacity; switch (shape_type) { case SHAPE_IVAR: - new_shape->next_iv_index = rb_shape_get_shape_by_id(new_shape->parent_id)->next_iv_index + 1; - - // Check if we should update next_iv_index on the object's class - if (BUILTIN_TYPE(obj) == T_OBJECT) { - VALUE klass = rb_obj_class(obj); - if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) { - RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index; - } - } + new_shape->next_iv_index = shape->next_iv_index + 1; break; + case SHAPE_CAPACITY_CHANGE: case SHAPE_IVAR_UNDEF: case SHAPE_FROZEN: - new_shape->next_iv_index = rb_shape_get_shape_by_id(new_shape->parent_id)->next_iv_index; + new_shape->next_iv_index = shape->next_iv_index; break; + case SHAPE_INITIAL_CAPACITY: case SHAPE_ROOT: rb_bug("Unreachable"); break; @@ -183,7 +173,7 @@ rb_shape_frozen_shape_p(rb_shape_t* shape) void rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape) { - rb_shape_t* next_shape = get_next_shape_internal(shape, id, obj, SHAPE_IVAR_UNDEF); + rb_shape_t * next_shape = get_next_shape_internal(shape, id, SHAPE_IVAR_UNDEF); if (shape == next_shape) { return; @@ -206,16 +196,11 @@ rb_shape_transition_shape_frozen(VALUE obj) rb_shape_t* next_shape; if (shape == rb_shape_get_root_shape()) { - next_shape = rb_shape_get_frozen_root_shape(); + rb_shape_set_shape_id(obj, SPECIAL_CONST_SHAPE_ID); + return; } - else { - static ID id_frozen; - if (!id_frozen) { - id_frozen = rb_make_internal_id(); - } - next_shape = get_next_shape_internal(shape, (ID)id_frozen, obj, SHAPE_FROZEN); - } + next_shape = get_next_shape_internal(shape, (ID)id_frozen, SHAPE_FROZEN); RUBY_ASSERT(next_shape); rb_shape_set_shape(obj, next_shape); @@ -231,10 +216,39 @@ rb_shape_transition_shape(VALUE obj, ID id, rb_shape_t *shape) rb_shape_set_shape(obj, next_shape); } -rb_shape_t* +/* + * This function is used for assertions where we don't want to increment + * max_iv_count + */ +rb_shape_t * +rb_shape_get_next_iv_shape(rb_shape_t* shape, ID id) +{ + return get_next_shape_internal(shape, id, SHAPE_IVAR); +} + +rb_shape_t * rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id) { - return get_next_shape_internal(shape, id, obj, SHAPE_IVAR); + rb_shape_t * new_shape = rb_shape_get_next_iv_shape(shape, id); + + // Check if we should update max_iv_count on the object's class + if (BUILTIN_TYPE(obj) == T_OBJECT) { + VALUE klass = rb_obj_class(obj); + if (new_shape->next_iv_index > RCLASS_EXT(klass)->max_iv_count) { + RCLASS_EXT(klass)->max_iv_count = new_shape->next_iv_index; + } + } + + return new_shape; +} + +rb_shape_t * +rb_shape_transition_shape_capa(rb_shape_t* shape, uint32_t new_capacity) +{ + ID edge_name = rb_make_temporary_id(new_capacity); + rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE); + new_shape->capacity = new_capacity; + return new_shape; } bool @@ -250,11 +264,13 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) RUBY_ASSERT(shape->next_iv_index > 0); *value = shape->next_iv_index - 1; return true; + case SHAPE_CAPACITY_CHANGE: case SHAPE_IVAR_UNDEF: case SHAPE_ROOT: + case SHAPE_INITIAL_CAPACITY: return false; case SHAPE_FROZEN: - rb_bug("Ivar should not exist on frozen transition\n"); + rb_bug("Ivar should not exist on transition\n"); } } shape = rb_shape_get_shape_by_id(shape->parent_id); @@ -290,9 +306,18 @@ rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id) } rb_shape_t * +rb_shape_alloc_with_size_pool_index(ID edge_name, rb_shape_t * parent, uint8_t size_pool_index) +{ + rb_shape_t * shape = rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent)); + shape->size_pool_index = size_pool_index; + return shape; +} + + +rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent) { - return rb_shape_alloc_with_parent_id(edge_name, rb_shape_id(parent)); + return rb_shape_alloc_with_size_pool_index(edge_name, parent, parent->size_pool_index); } MJIT_FUNC_EXPORTED void @@ -307,6 +332,39 @@ rb_shape_flags_mask(void) return SHAPE_FLAG_MASK; } +rb_shape_t * +rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape) +{ + rb_shape_t * midway_shape; + + if (dest_shape->type != SHAPE_ROOT) { + midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_shape_by_id(dest_shape->parent_id)); + } + else { + midway_shape = initial_shape; + } + + switch (dest_shape->type) { + case SHAPE_IVAR: + if (midway_shape->capacity < midway_shape->next_iv_index) { + // There isn't enough room to write this IV, so we need to increase the capacity + midway_shape = rb_shape_transition_shape_capa(midway_shape, midway_shape->capacity * 2); + } + + midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name); + break; + case SHAPE_IVAR_UNDEF: + midway_shape = get_next_shape_internal(midway_shape, dest_shape->edge_name, SHAPE_IVAR_UNDEF); + break; + case SHAPE_ROOT: + case SHAPE_FROZEN: + case SHAPE_CAPACITY_CHANGE: + break; + } + + return midway_shape; +} + #if VM_CHECK_MODE > 0 VALUE rb_cShape; @@ -336,6 +394,14 @@ rb_shape_type(VALUE self) } static VALUE +rb_shape_capacity(VALUE self) +{ + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + return INT2NUM(shape->capacity); +} + +static VALUE rb_shape_parent_id(VALUE self) { rb_shape_t * shape; @@ -398,11 +464,16 @@ rb_shape_edge_name(VALUE self) rb_shape_t* shape; TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); - if (shape->edge_name) { - return ID2SYM(shape->edge_name); + if ((shape->edge_name & (ID_INTERNAL)) == ID_INTERNAL) { + return INT2NUM(shape->capacity); } else { - return Qnil; + if (shape->edge_name) { + return ID2SYM(shape->edge_name); + } + else { + return Qnil; + } } } @@ -416,6 +487,15 @@ rb_shape_next_iv_index(VALUE self) } static VALUE +rb_shape_size_pool_index(VALUE self) +{ + rb_shape_t * shape; + TypedData_Get_Struct(self, rb_shape_t, &shape_data_type, shape); + + return INT2NUM(shape->size_pool_index); +} + +static VALUE rb_shape_export_depth(VALUE self) { rb_shape_t* shape; @@ -454,12 +534,6 @@ rb_shape_root_shape(VALUE self) return rb_shape_t_to_rb_cShape(rb_shape_get_root_shape()); } -static VALUE -rb_shape_frozen_root_shape(VALUE self) -{ - return rb_shape_t_to_rb_cShape(rb_shape_get_frozen_root_shape()); -} - VALUE rb_obj_shape(rb_shape_t* shape); static enum rb_id_table_iterator_result collect_keys_and_values(ID key, VALUE value, void *ref) @@ -519,6 +593,43 @@ rb_shape_find_by_id(VALUE mod, VALUE id) #endif void +Init_default_shapes(void) +{ + id_frozen = rb_make_internal_id(); + + // Shapes by size pool + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + size_pool_edge_names[i] = rb_make_internal_id(); + } + + // Root shape + rb_shape_t * root = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID); + root->capacity = (uint32_t)((rb_size_pool_slot_size(0) - offsetof(struct RObject, as.ary)) / sizeof(VALUE)); + root->type = SHAPE_ROOT; + root->size_pool_index = 0; + GET_VM()->root_shape = root; + RUBY_ASSERT(rb_shape_id(GET_VM()->root_shape) == ROOT_SHAPE_ID); + + // Shapes by size pool + for (int i = 1; i < SIZE_POOL_COUNT; i++) { + uint32_t capa = (uint32_t)((rb_size_pool_slot_size(i) - offsetof(struct RObject, as.ary)) / sizeof(VALUE)); + rb_shape_t * new_shape = rb_shape_transition_shape_capa(root, capa); + new_shape->type = SHAPE_INITIAL_CAPACITY; + new_shape->size_pool_index = i; + RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i); + } + + // Special const shape +#if RUBY_DEBUG + rb_shape_t * special_const_shape = +#endif + get_next_shape_internal(root, (ID)id_frozen, SHAPE_FROZEN); + RUBY_ASSERT(rb_shape_id(special_const_shape) == SPECIAL_CONST_SHAPE_ID); + RUBY_ASSERT(SPECIAL_CONST_SHAPE_ID == (GET_VM()->next_shape_id - 1)); + RUBY_ASSERT(rb_shape_frozen_shape_p(special_const_shape)); +} + +void Init_shape(void) { #if VM_CHECK_MODE > 0 @@ -530,21 +641,23 @@ Init_shape(void) rb_define_method(rb_cShape, "edges", rb_shape_edges, 0); rb_define_method(rb_cShape, "edge_name", rb_shape_edge_name, 0); rb_define_method(rb_cShape, "next_iv_index", rb_shape_next_iv_index, 0); + rb_define_method(rb_cShape, "size_pool_index", rb_shape_size_pool_index, 0); rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0); rb_define_method(rb_cShape, "id", rb_wrapped_shape_id, 0); rb_define_method(rb_cShape, "type", rb_shape_type, 0); + rb_define_method(rb_cShape, "capacity", rb_shape_capacity, 0); rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT)); rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR)); rb_define_const(rb_cShape, "SHAPE_IVAR_UNDEF", INT2NUM(SHAPE_IVAR_UNDEF)); rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN)); rb_define_const(rb_cShape, "SHAPE_BITS", INT2NUM(SHAPE_BITS)); rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT)); + rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID)); rb_define_singleton_method(rb_cShape, "transition_tree", shape_transition_tree, 0); rb_define_singleton_method(rb_cShape, "find_by_id", rb_shape_find_by_id, 1); rb_define_singleton_method(rb_cShape, "next_shape_id", next_shape_id, 0); rb_define_singleton_method(rb_cShape, "of", rb_shape_debug_shape, 1); rb_define_singleton_method(rb_cShape, "root_shape", rb_shape_root_shape, 0); - rb_define_singleton_method(rb_cShape, "frozen_root_shape", rb_shape_frozen_root_shape, 0); #endif } @@ -40,13 +40,17 @@ typedef uint16_t shape_id_t; # define MAX_SHAPE_ID (SHAPE_MASK - 1) # define INVALID_SHAPE_ID SHAPE_MASK # define ROOT_SHAPE_ID 0x0 -# define FROZEN_ROOT_SHAPE_ID 0x1 +// We use SIZE_POOL_COUNT number of shape IDs for transitions out of different size pools +// The next available shapd ID will be the SPECIAL_CONST_SHAPE_ID +# define SPECIAL_CONST_SHAPE_ID SIZE_POOL_COUNT struct rb_shape { struct rb_id_table * edges; // id_table from ID (ivar) to next shape ID edge_name; // ID (ivar) for transition from parent to rb_shape attr_index_t next_iv_index; + uint32_t capacity; // Total capacity of the object with this shape uint8_t type; + uint8_t size_pool_index; shape_id_t parent_id; }; @@ -56,7 +60,9 @@ enum shape_type { SHAPE_ROOT, SHAPE_IVAR, SHAPE_FROZEN, + SHAPE_CAPACITY_CHANGE, SHAPE_IVAR_UNDEF, + SHAPE_INITIAL_CAPACITY, }; #if SHAPE_IN_BASIC_FLAGS @@ -124,6 +130,7 @@ static inline shape_id_t RCLASS_SHAPE_ID(VALUE obj) { #endif bool rb_shape_root_shape_p(rb_shape_t* shape); +rb_shape_t * rb_shape_get_root_shape(void); rb_shape_t* rb_shape_get_shape_by_id_without_assertion(shape_id_t shape_id); @@ -135,22 +142,38 @@ rb_shape_t* rb_shape_get_shape(VALUE obj); int rb_shape_frozen_shape_p(rb_shape_t* shape); void rb_shape_transition_shape_frozen(VALUE obj); void rb_shape_transition_shape_remove_ivar(VALUE obj, ID id, rb_shape_t *shape); +rb_shape_t * rb_shape_transition_shape_capa(rb_shape_t * shape, uint32_t new_capacity); void rb_shape_transition_shape(VALUE obj, ID id, rb_shape_t *shape); +rb_shape_t * rb_shape_get_next_iv_shape(rb_shape_t * shape, ID id); rb_shape_t* rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id); bool rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t * value); shape_id_t rb_shape_id(rb_shape_t * shape); MJIT_SYMBOL_EXPORT_END +rb_shape_t * rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape); + +static inline uint32_t +ROBJECT_IV_CAPACITY(VALUE obj) +{ + RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); + return rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->capacity; +} + static inline uint32_t ROBJECT_IV_COUNT(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); uint32_t ivc = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj))->next_iv_index; - RUBY_ASSERT(ivc <= ROBJECT_NUMIV(obj)); return ivc; } static inline uint32_t +RBASIC_IV_COUNT(VALUE obj) +{ + return rb_shape_get_shape_by_id(rb_shape_get_shape_id(obj))->next_iv_index; +} + +static inline uint32_t RCLASS_IV_COUNT(VALUE obj) { RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); @@ -159,6 +182,7 @@ RCLASS_IV_COUNT(VALUE obj) } rb_shape_t * rb_shape_alloc(ID edge_name, rb_shape_t * parent); +rb_shape_t * rb_shape_alloc_with_size_pool_index(ID edge_name, rb_shape_t * parent, uint8_t size_pool_index); rb_shape_t * rb_shape_alloc_with_parent_id(ID edge_name, shape_id_t parent_id); bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id); diff --git a/test/-ext-/string/test_cstr.rb b/test/-ext-/string/test_cstr.rb index d909781700..efc64119dc 100644 --- a/test/-ext-/string/test_cstr.rb +++ b/test/-ext-/string/test_cstr.rb @@ -43,7 +43,11 @@ class Test_StringCStr < Test::Unit::TestCase end def test_rb_str_new_frozen_embed - str = Bug::String.cstr_noembed("rbconfig.rb") + # "rbconfi" is the smallest "maximum embeddable string". VWA adds + # a capacity field, which removes one pointer capacity for embedded objects, + # so if VWA is enabled, but there is only one size pool, then the + # maximum embeddable capacity on 32 bit machines is 8 bytes. + str = Bug::String.cstr_noembed("rbconfi") str = Bug::String.rb_str_new_frozen(str) assert_equal true, Bug::String.cstr_embedded?(str) end diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 5994fadeff..2366ec3b61 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -277,7 +277,7 @@ class TestObjSpace < Test::Unit::TestCase info = nil ObjectSpace.trace_object_allocations do line = __LINE__ + 1 - str = "hello world" + str = "hello w" info = ObjectSpace.dump(str) end assert_dump_object(info, line) @@ -289,7 +289,7 @@ class TestObjSpace < Test::Unit::TestCase th = Thread.start {r.read} ObjectSpace.trace_object_allocations do line = __LINE__ + 1 - str = "hello world" + str = "hello w" ObjectSpace.dump(str, output: w) end w.close @@ -301,7 +301,7 @@ class TestObjSpace < Test::Unit::TestCase def assert_dump_object(info, line) loc = caller_locations(1, 1)[0] assert_match(/"type":"STRING"/, info) - assert_match(/"embedded":true, "bytesize":11, "value":"hello world", "encoding":"UTF-8"/, info) + assert_match(/"embedded":true, "bytesize":7, "value":"hello w", "encoding":"UTF-8"/, info) assert_match(/"file":"#{Regexp.escape __FILE__}", "line":#{line}/, info) assert_match(/"method":"#{loc.base_label}"/, info) JSON.parse(info) if defined?(JSON) @@ -549,17 +549,17 @@ class TestObjSpace < Test::Unit::TestCase # # This test makes assertions on the assignment to `str`, so we look for # the second appearance of /TEST STRING/ in the output - test_string_in_dump_all = output.grep(/TEST STRING/) - assert_equal(test_string_in_dump_all.size, 2) + test_string_in_dump_all = output.grep(/TEST2/) + assert_equal(2, test_string_in_dump_all.size, "number of strings") entry_hash = JSON.parse(test_string_in_dump_all[1]) - assert_equal(entry_hash["bytesize"], 11) - assert_equal(entry_hash["value"], "TEST STRING") - assert_equal(entry_hash["encoding"], "UTF-8") - assert_equal(entry_hash["file"], "-") - assert_equal(entry_hash["line"], 4) - assert_equal(entry_hash["method"], "dump_my_heap_please") + assert_equal(5, entry_hash["bytesize"], "bytesize is wrong") + assert_equal("TEST2", entry_hash["value"], "value is wrong") + assert_equal("UTF-8", entry_hash["encoding"], "encoding is wrong") + assert_equal("-", entry_hash["file"], "file is wrong") + assert_equal(4, entry_hash["line"], "line is wrong") + assert_equal("dump_my_heap_please", entry_hash["method"], "method is wrong") assert_not_nil(entry_hash["generation"]) end @@ -571,7 +571,7 @@ class TestObjSpace < Test::Unit::TestCase def dump_my_heap_please ObjectSpace.trace_object_allocations_start GC.start - str = "TEST STRING".force_encoding("UTF-8") + str = "TEST2".force_encoding("UTF-8") ObjectSpace.dump_all(output: :stdout) end @@ -586,7 +586,7 @@ class TestObjSpace < Test::Unit::TestCase def dump_my_heap_please ObjectSpace.trace_object_allocations_start GC.start - (str = "TEST STRING").force_encoding("UTF-8") + (str = "TEST2").force_encoding("UTF-8") ObjectSpace.dump_all().path end diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb index 92a2be1174..bae29a3162 100644 --- a/test/ruby/test_gc_compact.rb +++ b/test/ruby/test_gc_compact.rb @@ -210,7 +210,7 @@ class TestGCCompact < Test::Unit::TestCase end def test_moving_arrays_down_size_pools - omit if !GC.using_rvargc? + omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1 assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) begin; ARY_COUNT = 500 @@ -229,7 +229,8 @@ class TestGCCompact < Test::Unit::TestCase end def test_moving_arrays_up_size_pools - omit if !GC.using_rvargc? + omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1 + assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) begin; ARY_COUNT = 500 @@ -250,6 +251,8 @@ class TestGCCompact < Test::Unit::TestCase end def test_moving_objects_between_size_pools + omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1 + assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) begin; class Foo @@ -274,7 +277,8 @@ class TestGCCompact < Test::Unit::TestCase end def test_moving_strings_up_size_pools - omit if !GC.using_rvargc? + omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1 + assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) begin; STR_COUNT = 500 @@ -292,7 +296,8 @@ class TestGCCompact < Test::Unit::TestCase end def test_moving_strings_down_size_pools - omit if !GC.using_rvargc? + omit if GC::INTERNAL_CONSTANTS[:SIZE_POOL_COUNT] == 1 + assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV) begin; STR_COUNT = 500 diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 23696acc70..326ff3a453 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -86,15 +86,10 @@ class TestShapes < Test::Unit::TestCase assert_equal(2, bar_shape.next_iv_index) end - def test_new_obj_has_root_shape - assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(Object.new)) - end + class TestObject; end - def test_frozen_new_obj_has_frozen_root_shape - assert_shape_equal( - RubyVM::Shape.frozen_root_shape, - RubyVM::Shape.of(Object.new.freeze) - ) + def test_new_obj_has_root_shape + assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new)) end def test_str_has_root_shape @@ -109,12 +104,12 @@ class TestShapes < Test::Unit::TestCase assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of({})) end - def test_true_has_frozen_root_shape - assert_shape_equal(RubyVM::Shape.frozen_root_shape, RubyVM::Shape.of(true)) + def test_true_has_special_const_shape_id + assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of(true).id) end - def test_nil_has_frozen_root_shape - assert_shape_equal(RubyVM::Shape.frozen_root_shape, RubyVM::Shape.of(nil)) + def test_nil_has_special_const_shape_id + assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of(nil).id) end def test_basic_shape_transition diff --git a/variable.c b/variable.c index c9c4be1c43..bdde4d9607 100644 --- a/variable.c +++ b/variable.c @@ -1092,7 +1092,7 @@ rb_generic_shape_id(VALUE obj) shape_id = ivtbl->shape_id; } else if (OBJ_FROZEN(obj)) { - shape_id = FROZEN_ROOT_SHAPE_ID; + shape_id = SPECIAL_CONST_SHAPE_ID; } } RB_VM_LOCK_LEAVE(); @@ -1364,26 +1364,21 @@ rb_obj_transient_heap_evacuate(VALUE obj, int promote) #endif void -rb_ensure_iv_list_size(VALUE obj, uint32_t len, uint32_t newsize) +rb_ensure_iv_list_size(VALUE obj, uint32_t current_capacity, uint32_t new_capacity) { VALUE *ptr = ROBJECT_IVPTR(obj); VALUE *newptr; if (RBASIC(obj)->flags & ROBJECT_EMBED) { - newptr = obj_ivar_heap_alloc(obj, newsize); - MEMCPY(newptr, ptr, VALUE, len); + newptr = obj_ivar_heap_alloc(obj, new_capacity); + MEMCPY(newptr, ptr, VALUE, current_capacity); RB_FL_UNSET_RAW(obj, ROBJECT_EMBED); ROBJECT(obj)->as.heap.ivptr = newptr; } else { - newptr = obj_ivar_heap_realloc(obj, len, newsize); + newptr = obj_ivar_heap_realloc(obj, current_capacity, new_capacity); } - -#if USE_RVARGC - ROBJECT(obj)->numiv = newsize; -#else - ROBJECT(obj)->as.heap.numiv = newsize; -#endif + ROBJECT_SET_NUMIV(obj, new_capacity); } struct gen_ivtbl * @@ -1407,12 +1402,25 @@ rb_ensure_generic_iv_list_size(VALUE obj, uint32_t newsize) } // @note May raise when there are too many instance variables. -void -rb_init_iv_list(VALUE obj) +rb_shape_t * +rb_grow_iv_list(VALUE obj) { - uint32_t newsize = (uint32_t)(rb_shape_get_shape(obj)->next_iv_index * 2.0); uint32_t len = ROBJECT_NUMIV(obj); - rb_ensure_iv_list_size(obj, len, newsize < len ? len : newsize); + RUBY_ASSERT(len > 0); + uint32_t newsize = (uint32_t)(len * 2); + rb_ensure_iv_list_size(obj, len, newsize); + rb_shape_t * res; + +#if USE_RVARGC + ROBJECT_SET_NUMIV(obj, newsize); +#else + ROBJECT(obj)->as.heap.numiv = newsize; +#endif + + res = rb_shape_transition_shape_capa(rb_shape_get_shape(obj), newsize); + rb_shape_set_shape(obj, res); + RUBY_ASSERT(!RB_TYPE_P(obj, T_OBJECT) || ROBJECT_IV_CAPACITY(obj) == ROBJECT_NUMIV(obj)); + return res; } static VALUE @@ -1423,9 +1431,10 @@ obj_ivar_set(VALUE obj, ID id, VALUE val) // Get the current shape rb_shape_t * shape = rb_shape_get_shape_by_id(ROBJECT_SHAPE_ID(obj)); + bool found = true; if (!rb_shape_get_iv_index(shape, id, &index)) { - shape = rb_shape_get_next(shape, obj, id); - index = shape->next_iv_index - 1; + index = shape->next_iv_index; + found = false; } uint32_t len = ROBJECT_NUMIV(obj); @@ -1434,12 +1443,16 @@ obj_ivar_set(VALUE obj, ID id, VALUE val) // on this object until the buffer has been allocated, otherwise // GC could read off the end of the buffer. if (len <= index) { - uint32_t newsize = (uint32_t)((len + 1) * 1.25); - rb_ensure_iv_list_size(obj, len, newsize); + shape = rb_grow_iv_list(obj); + } + + if (!found) { + shape = rb_shape_get_next(shape, obj, id); + RUBY_ASSERT(index == (shape->next_iv_index - 1)); + rb_shape_set_shape(obj, shape); } RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], val); - rb_shape_set_shape(obj, shape); return val; } @@ -1475,7 +1488,7 @@ rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id) RCLASS_EXT(obj)->shape_id = shape_id; break; default: - if (shape_id != FROZEN_ROOT_SHAPE_ID) { + if (shape_id != SPECIAL_CONST_SHAPE_ID) { struct gen_ivtbl *ivtbl = 0; RB_VM_LOCK_ENTER(); { @@ -1599,8 +1612,10 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu callback(shape->edge_name, val, itr_data->arg); } return; - case SHAPE_IVAR_UNDEF: + case SHAPE_INITIAL_CAPACITY: + case SHAPE_CAPACITY_CHANGE: case SHAPE_FROZEN: + case SHAPE_IVAR_UNDEF: iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), callback, itr_data); return; } @@ -3922,7 +3937,7 @@ rb_iv_tbl_copy(VALUE dst, VALUE src) RUBY_ASSERT(rb_type(dst) == rb_type(src)); RUBY_ASSERT(RB_TYPE_P(dst, T_CLASS) || RB_TYPE_P(dst, T_MODULE)); - RUBY_ASSERT(RCLASS_SHAPE_ID(dst) == ROOT_SHAPE_ID); + RUBY_ASSERT(RCLASS_SHAPE_ID(dst) == ROOT_SHAPE_ID || rb_shape_get_shape_by_id(RCLASS_SHAPE_ID(dst))->type == SHAPE_INITIAL_CAPACITY); RUBY_ASSERT(!RCLASS_IVPTR(dst)); rb_ivar_foreach(src, tbl_copy_i, dst); @@ -4044,17 +4044,6 @@ Init_vm_objects(void) if (!vm->shape_list) { rb_memerror(); } - - // Root shape - vm->root_shape = rb_shape_alloc_with_parent_id(0, INVALID_SHAPE_ID); - RUBY_ASSERT(rb_shape_id(vm->root_shape) == ROOT_SHAPE_ID); - - // Frozen root shape - vm->frozen_root_shape = rb_shape_alloc_with_parent_id(rb_make_internal_id(), rb_shape_id(vm->root_shape)); - vm->frozen_root_shape->type = (uint8_t)SHAPE_FROZEN; - RUBY_ASSERT(rb_shape_id(vm->frozen_root_shape) == FROZEN_ROOT_SHAPE_ID); - - vm->next_shape_id = 2; } /* Stub for builtin function when not building YJIT units*/ @@ -691,7 +691,6 @@ typedef struct rb_vm_struct { /* object shapes */ rb_shape_t *shape_list; rb_shape_t *root_shape; - rb_shape_t *frozen_root_shape; shape_id_t next_shape_id; /* load */ diff --git a/vm_insnhelper.c b/vm_insnhelper.c index cff4b9138a..7b24392932 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -50,11 +50,6 @@ MJIT_STATIC VALUE ruby_vm_special_exception_copy(VALUE exc) { VALUE e = rb_obj_alloc(rb_class_real(RBASIC_CLASS(exc))); - rb_shape_t * shape = rb_shape_get_shape(exc); - if (rb_shape_frozen_shape_p(shape)) { - shape = rb_shape_get_shape_by_id(shape->parent_id); - } - rb_shape_set_shape(e, shape); rb_obj_copy_ivar(e, exc); return e; } @@ -1310,37 +1305,33 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, rb_shape_t* shape = rb_shape_get_shape(obj); shape_id_t next_shape_id = ROBJECT_SHAPE_ID(obj); - rb_shape_t* next_shape = rb_shape_get_next(shape, obj, id); + if (!rb_shape_get_iv_index(shape, id, &index)) { + if (UNLIKELY(shape->next_iv_index >= num_iv)) { + RUBY_ASSERT(shape->next_iv_index == num_iv); - if (shape != next_shape) { - RUBY_ASSERT(next_shape->parent_id == rb_shape_id(shape)); - next_shape_id = rb_shape_id(next_shape); - } + shape = rb_grow_iv_list(obj); + RUBY_ASSERT(shape->type == SHAPE_CAPACITY_CHANGE); + } + + index = shape->next_iv_index; - if (rb_shape_get_iv_index(next_shape, id, &index)) { // based off the hash stored in the transition tree if (index >= MAX_IVARS) { rb_raise(rb_eArgError, "too many instance variables"); } - populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); - } - else { - rb_bug("Didn't find instance variable %s\n", rb_id2name(id)); - } - - // Ensure the IV buffer is wide enough to store the IV - if (UNLIKELY(index >= num_iv)) { - RUBY_ASSERT(index == num_iv); - rb_init_iv_list(obj); - } + rb_shape_t * next_shape = rb_shape_get_next(shape, obj, id); + RUBY_ASSERT(next_shape->type == SHAPE_IVAR); + RUBY_ASSERT(index == (next_shape->next_iv_index - 1)); + next_shape_id = rb_shape_id(next_shape); - if (shape != next_shape) { rb_shape_set_shape(obj, next_shape); } + + populate_cache(index, next_shape_id, id, iseq, ic, cc, is_attr); + VALUE *ptr = ROBJECT_IVPTR(obj); RB_OBJ_WRITE(obj, &ptr[index], val); RB_DEBUG_COUNTER_INC(ivar_set_ic_miss_iv_hit); - return val; } case T_CLASS: @@ -1450,17 +1441,18 @@ vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_index_t i else if (dest_shape_id != INVALID_SHAPE_ID) { rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id); shape_id_t source_shape_id = dest_shape->parent_id; - if (shape_id == source_shape_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) { + + RUBY_ASSERT(dest_shape->type == SHAPE_IVAR || dest_shape->type == SHAPE_IVAR_UNDEF); + + if (shape_id == source_shape_id && dest_shape->edge_name == id) { RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID); - if (UNLIKELY(index >= ROBJECT_NUMIV(obj))) { - rb_init_iv_list(obj); - } + RUBY_ASSERT(ROBJECT_IV_CAPACITY(obj) == ROBJECT_NUMIV(obj)); ROBJECT_SET_SHAPE_ID(obj, dest_shape_id); - RUBY_ASSERT(rb_shape_get_next(rb_shape_get_shape_by_id(source_shape_id), obj, id) == dest_shape); + RUBY_ASSERT(rb_shape_get_next_iv_shape(rb_shape_get_shape_by_id(source_shape_id), id) == dest_shape); + RUBY_ASSERT(ROBJECT_IV_CAPACITY(obj) == ROBJECT_NUMIV(obj)); RUBY_ASSERT(index < ROBJECT_NUMIV(obj)); - } else { break; diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 9ede3030ff..d6218385b0 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -248,10 +248,9 @@ extern "C" { } pub const ROBJECT_EMBED: ruby_robject_flags = 8192; pub type ruby_robject_flags = u32; -pub const ROBJECT_OFFSET_NUMIV: i32 = 16; -pub const ROBJECT_OFFSET_AS_HEAP_IVPTR: i32 = 24; -pub const ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL: i32 = 32; -pub const ROBJECT_OFFSET_AS_ARY: i32 = 24; +pub const ROBJECT_OFFSET_AS_HEAP_IVPTR: i32 = 16; +pub const ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL: i32 = 24; +pub const ROBJECT_OFFSET_AS_ARY: i32 = 16; extern "C" { pub static mut rb_mKernel: VALUE; } @@ -420,7 +419,9 @@ pub struct rb_shape { pub edges: *mut rb_id_table, pub edge_name: ID, pub next_iv_index: attr_index_t, + pub capacity: u32, pub type_: u8, + pub size_pool_index: u8, pub parent_id: shape_id_t, } pub type rb_shape_t = rb_shape; |