From 678d86f5e46d57432169d0d5eaf71a41c6d21526 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 29 Apr 2026 17:23:39 +0900 Subject: [PATCH 1/4] shape.c: transition to complex when `max_capacity` is reached Now that we have 1024B slots, we can store up to 126 fields inline. Objects larger than this are rare if not non-existent, hence we can get rid of the `malloc` path for imemo/fields and simply transition to `TOO_COMPLEX`. This additionally allows to shrink `attr_index_t` from 16 to 8B. Note: the ZJIT "ivar on extended" tests are renamed as "complex" because "extended" AKA malloc allocated imemo/fields no longer exists. They're now complex fields, AKA st tables. rb_class_allocate_instance: start as complex when over max_fields If `RCLASS_MAX_IV_COUNT` is over `max_fields`, allocating a large slot to end up transitioning to `TOO_COMPLEX` is wasteful. We might as well start as complex directly. --- gc.c | 43 +++++++++---- imemo.c | 29 +++------ internal/class.h | 4 +- iseq.c | 8 ++- shape.c | 16 +++-- shape.h | 4 +- test/ruby/test_shapes.rb | 54 +++++++++++++--- variable.c | 14 +++-- yjit/src/cruby_bindings.inc.rs | 2 +- zjit/src/cruby_bindings.inc.rs | 2 +- zjit/src/hir/opt_tests.rs | 110 +++++++++++++-------------------- 11 files changed, 160 insertions(+), 126 deletions(-) diff --git a/gc.c b/gc.c index 463145edda22e7..0f964d2a679be0 100644 --- a/gc.c +++ b/gc.c @@ -1069,27 +1069,46 @@ rb_newobj_of(VALUE klass, VALUE flags, size_t size) return rb_newobj(GET_EC(), klass, flags, ROOT_SHAPE_ID, true, size); } +static +VALUE class_allocate_complex_instance(VALUE klass, uint32_t capacity) +{ + shape_id_t initial_shape_id = rb_shape_root(rb_gc_heap_id_for_size(sizeof(struct RObject))); + VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, initial_shape_id, sizeof(struct RObject)); + rb_obj_init_too_complex(obj, rb_st_init_numtable_with_size(capacity)); + return obj; +} + VALUE rb_class_allocate_instance(VALUE klass) { uint32_t index_tbl_num_entries = RCLASS_MAX_IV_COUNT(klass); + VALUE obj; - size_t size = rb_obj_embedded_size(index_tbl_num_entries); - if (!rb_gc_size_allocatable_p(size)) { - size = sizeof(struct RObject); + // Directly start as TOO_COMPLEX if we know we're over the limit. + RUBY_ASSERT(rb_shape_tree.max_capacity > 0); + if (RB_UNLIKELY(index_tbl_num_entries > rb_shape_tree.max_capacity)) { + obj = class_allocate_complex_instance(klass, index_tbl_num_entries); } + else { + size_t size = rb_obj_embedded_size(index_tbl_num_entries); + if (!rb_gc_size_allocatable_p(size)) { + size = sizeof(struct RObject); + } - // There might be a NEWOBJ tracepoint callback, and it may set fields. - // So the shape must be passed to `NEWOBJ_OF`. - VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, rb_shape_root(rb_gc_heap_id_for_size(size)), size); + // There might be a NEWOBJ tracepoint callback, and it may set fields. + // So the shape must be passed to `NEWOBJ_OF`. + obj = rb_newobj_of_with_shape(klass, T_OBJECT, rb_shape_root(rb_gc_heap_id_for_size(size)), size); -#if RUBY_DEBUG - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); - VALUE *ptr = ROBJECT_FIELDS(obj); - size_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj)); - for (size_t i = fields_count; i < ROBJECT_FIELDS_CAPACITY(obj); i++) { - ptr[i] = Qundef; + #if RUBY_DEBUG + VALUE *ptr = ROBJECT_FIELDS(obj); + size_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj)); + for (size_t i = fields_count; i < ROBJECT_FIELDS_CAPACITY(obj); i++) { + ptr[i] = Qundef; + } + #endif } + +#if RUBY_DEBUG if (rb_obj_class(obj) != rb_class_real(klass)) { rb_bug("Expected rb_class_allocate_instance to set the class correctly"); } diff --git a/imemo.c b/imemo.c index 50d96595adb81d..9483266735f7a2 100644 --- a/imemo.c +++ b/imemo.c @@ -128,16 +128,10 @@ rb_imemo_fields_new(VALUE owner, shape_id_t shape_id, bool shareable) { size_t capa = RSHAPE_CAPACITY(shape_id); size_t embedded_size = offsetof(struct rb_fields, as.embed) + capa * sizeof(VALUE); - VALUE fields; - if (rb_gc_size_allocatable_p(embedded_size)) { - fields = rb_imemo_new(imemo_fields, owner, embedded_size, shareable); - } - else { - fields = rb_imemo_new(imemo_fields, owner, sizeof(struct rb_fields), shareable); - IMEMO_OBJ_FIELDS(fields)->as.external.ptr = ALLOC_N(VALUE, capa); - FL_SET_RAW(fields, OBJ_FIELD_HEAP); - } + RUBY_ASSERT(rb_gc_size_allocatable_p(embedded_size)); + VALUE fields = rb_imemo_new(imemo_fields, owner, embedded_size, shareable); RBASIC_SET_SHAPE_ID(fields, shape_id); + RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_fields)); return fields; } @@ -265,13 +259,8 @@ rb_imemo_memsize(VALUE obj) case imemo_cvar_entry: break; case imemo_fields: - if (FL_TEST_RAW(obj, OBJ_FIELD_HEAP)) { - if (rb_shape_obj_too_complex_p(obj)) { - size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table); - } - else { - size += RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)) * sizeof(VALUE); - } + if (rb_shape_obj_too_complex_p(obj)) { + size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table); } break; default: @@ -572,12 +561,8 @@ imemo_fields_free(struct rb_fields *fields) { if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_HEAP)) { shape_id_t shape_id = RBASIC_SHAPE_ID((VALUE)fields); - if (rb_shape_too_complex_p(shape_id)) { - st_free_table(fields->as.complex.table); - } - else { - SIZED_FREE_N(fields->as.external.ptr, RSHAPE_CAPACITY(shape_id)); - } + RUBY_ASSERT(rb_shape_too_complex_p(shape_id)); + st_free_table(fields->as.complex.table); } } diff --git a/internal/class.h b/internal/class.h index 3ead74cf59a1f1..7fef08a84e5fdd 100644 --- a/internal/class.h +++ b/internal/class.h @@ -83,9 +83,9 @@ struct rb_classext_struct { const VALUE includer; } iclass; } as; - attr_index_t max_iv_count; uint16_t superclass_depth; - unsigned char variation_count; + attr_index_t max_iv_count; + uint8_t variation_count; bool permanent_classpath : 1; bool cloned : 1; bool shared_const_tbl : 1; diff --git a/iseq.c b/iseq.c index 8be62cdd9b4c68..e7fa5d410307f0 100644 --- a/iseq.c +++ b/iseq.c @@ -3041,7 +3041,7 @@ rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq) } } - attr_index_t count = (attr_index_t)iv_names.num_entries; + size_t count = iv_names.num_entries; VALUE superclass = rb_class_superclass(klass); if (!NIL_P(superclass)) { // BasicObject doesn't have a superclass @@ -3050,7 +3050,11 @@ rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq) set_free_embedded_table(&iv_names); - return count; + if (count > (attr_index_t)-1) { + return (attr_index_t)-1; + } + + return (attr_index_t)count; } /* diff --git a/shape.c b/shape.c index ee1cb5b4cf7567..1ff1c47c7d0c97 100644 --- a/shape.c +++ b/shape.c @@ -519,8 +519,7 @@ shape_grow_capa(attr_index_t current_capa) return capa; } } - - return (attr_index_t)rb_malloc_grow_capa(current_capa, sizeof(VALUE)); + return capacities[rb_shape_tree.heaps_count - 1]; } static rb_shape_t * @@ -536,6 +535,7 @@ rb_shape_alloc_new_child(ID id, rb_shape_t *shape, enum shape_type shape_type) RUBY_ASSERT(shape->next_field_index == shape->capacity); new_shape->capacity = shape_grow_capa(shape->capacity); } + RUBY_ASSERT(new_shape->capacity > shape->next_field_index); new_shape->next_field_index = shape->next_field_index + 1; if (new_shape->next_field_index > ANCESTOR_CACHE_THRESHOLD) { @@ -781,6 +781,11 @@ shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE klass, ID id } #endif + RUBY_ASSERT(rb_shape_tree.max_capacity > 0); + if (UNLIKELY(shape->next_field_index >= rb_shape_tree.max_capacity)) { + return NULL; + } + bool allow_new_shape = RCLASS_VARIATION_COUNT(klass) < SHAPE_MAX_VARIATIONS; bool variation_created = false; rb_shape_t *new_shape = get_next_shape_internal(shape, id, shape_type, &variation_created, allow_new_shape); @@ -1530,13 +1535,16 @@ Init_default_shapes(void) size_t index; for (index = 0; index < heaps_count; index++) { if (heap_sizes[index] > sizeof(struct RBasic)) { - rb_shape_tree.capacities[index] = (heap_sizes[index] - sizeof(struct RBasic)) / sizeof(VALUE); + size_t capa = (heap_sizes[index] - sizeof(struct RBasic)) / sizeof(VALUE); + RUBY_ASSERT(capa < ATTR_INDEX_NOT_SET); + rb_shape_tree.capacities[index] = (attr_index_t)capa; } else { rb_shape_tree.capacities[index] = 0; } } rb_shape_tree.heaps_count = heaps_count; + rb_shape_tree.max_capacity = rb_shape_tree.capacities[heaps_count - 1]; #ifdef HAVE_MMAP size_t shape_list_mmap_size = rb_size_mul_or_raise(SHAPE_BUFFER_SIZE, sizeof(rb_shape_t), rb_eRuntimeError); @@ -1626,7 +1634,7 @@ Init_shape(void) rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS)); rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT)); rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS)); - rb_define_const(rb_cShape, "SHAPE_MAX_EMBEDDED_CAPACITY", INT2NUM(rb_shape_tree.capacities[rb_shape_tree.heaps_count - 1])); + rb_define_const(rb_cShape, "SHAPE_MAX_FIELDS", INT2NUM(rb_shape_tree.max_capacity)); rb_define_const(rb_cShape, "SIZEOF_RB_SHAPE_T", INT2NUM(sizeof(rb_shape_t))); rb_define_const(rb_cShape, "SIZEOF_REDBLACK_NODE_T", INT2NUM(sizeof(redblack_node_t))); rb_define_const(rb_cShape, "SHAPE_BUFFER_SIZE", INT2NUM(sizeof(rb_shape_t) * SHAPE_BUFFER_SIZE)); diff --git a/shape.h b/shape.h index 61b161e8dd9ed6..07ae2721c59eaf 100644 --- a/shape.h +++ b/shape.h @@ -4,7 +4,7 @@ #include "internal/gc.h" #include "internal/struct.h" -typedef uint16_t attr_index_t; +typedef uint8_t attr_index_t; typedef uint32_t shape_id_t; #define SHAPE_ID_NUM_BITS 32 #define SHAPE_ID_OFFSET_NUM_BITS 19 @@ -64,7 +64,6 @@ enum shape_id_mask { typedef uint32_t redblack_id_t; -#define SHAPE_MAX_FIELDS (attr_index_t)(-1) #define SHAPE_FLAG_SHIFT ((SIZEOF_VALUE * CHAR_BIT) - SHAPE_ID_NUM_BITS) #define SHAPE_FLAG_MASK (((VALUE)-1) >> SHAPE_ID_NUM_BITS) @@ -106,6 +105,7 @@ enum shape_flags { typedef struct { rb_shape_t *shape_list; + attr_index_t max_capacity; attr_index_t heaps_count; attr_index_t capacities[SHAPE_ID_HEAP_INDEX_MAX]; } rb_shape_tree_t; diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index d50b59c1d8914a..374a8bff7ef2a1 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -6,7 +6,7 @@ # These test the functionality of object shapes class TestShapes < Test::Unit::TestCase - MANY_IVS = RubyVM::Shape::SHAPE_MAX_EMBEDDED_CAPACITY + 1 + MANY_IVS = RubyVM::Shape::SHAPE_MAX_FIELDS + 1 class IVOrder def expected_ivs @@ -95,15 +95,15 @@ def write_iv # shapes def assert_shape_equal(e, a) assert_equal( - {id: e.id, parent_offset: e.parent_offset, depth: e.depth, type: e.type}, - {id: a.id, parent_offset: a.parent_offset, depth: a.depth, type: a.type}, + {id: e.offset, parent_offset: e.parent_offset, depth: e.depth, type: e.type, name: e.edge_name}, + {id: a.offset, parent_offset: a.parent_offset, depth: a.depth, type: a.type, name: e.edge_name}, ) end def refute_shape_equal(e, a) refute_equal( - {id: e.id, parent_offset: e.parent_offset, depth: e.depth, type: e.type}, - {id: a.id, parent_offset: a.parent_offset, depth: a.depth, type: a.type}, + {id: e.offset, parent_offset: e.parent_offset, depth: e.depth, type: e.type, name: e.edge_name}, + {id: a.offset, parent_offset: a.parent_offset, depth: a.depth, type: a.type, name: e.edge_name}, ) end @@ -199,7 +199,7 @@ def test_too_many_ivs_on_class obj.instance_variable_set(:"@a#{_1}", 1) end - refute_predicate RubyVM::Shape.of(obj), :too_complex? + assert_predicate RubyVM::Shape.of(obj), :too_complex? end def test_removing_when_too_many_ivs_on_class @@ -1155,7 +1155,7 @@ def test_freezing_and_duplicating_object_with_ivars obj = Example.new.freeze obj2 = obj.dup refute_predicate(obj2, :frozen?) - refute_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) + refute_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) assert_equal(obj2.instance_variable_get(:@a), 1) end @@ -1181,7 +1181,7 @@ def test_cloning_with_freeze_option obj = Object.new obj2 = obj.clone(freeze: true) assert_predicate(obj2, :frozen?) - refute_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) + refute_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) assert_predicate(RubyVM::Shape.of(obj2), :shape_frozen?) end @@ -1251,4 +1251,42 @@ def test_class_too_complex_during_delete def test_generic_too_complex_during_delete assert_too_complex_during_delete(Class.new(Array).new) end + + def assert_too_complex_max_fields(obj) + extra_fields = RubyVM::Shape::SHAPE_MAX_FIELDS - obj.instance_variables.size + extra_fields.times do |i| + obj.instance_variable_set("@camel_ivar#{i}", i) + end + refute_predicate RubyVM::Shape.of(obj), :too_complex? + obj.instance_variable_set("@camel_straw", true) + assert_predicate RubyVM::Shape.of(obj), :too_complex? + end + + def test_max_fields_complex + assert_too_complex_max_fields(Class.new(Object).new) + end + + def test_generic_max_fields_complex + assert_too_complex_max_fields(Class.new(Array).new) + end + + def test_class_max_fields_complex + assert_too_complex_max_fields(Class.new(Module).new) + end + + def test_max_initial_fields + klass = Class.new + init_ivars = (RubyVM::Shape::SHAPE_MAX_FIELDS + 1).times.map { |i| "@ivar_#{i} = #{i}" } + klass.class_eval(<<~RUBY) + def initialize(init = false) + if init + #{init_ivars.join(";")} + end + end + RUBY + assert_predicate RubyVM::Shape.of(klass.new), :too_complex? + assert_predicate RubyVM::Shape.of(klass.new.dup), :too_complex? + assert_predicate RubyVM::Shape.of(klass.new(true)), :too_complex? + assert_predicate RubyVM::Shape.of(klass.new(true).dup), :too_complex? + end end if defined?(RubyVM::Shape) diff --git a/variable.c b/variable.c index ffe0be30d5cdab..9a50550fb16f7c 100644 --- a/variable.c +++ b/variable.c @@ -1769,11 +1769,17 @@ void rb_obj_init_too_complex(VALUE obj, st_table *table) { // This method is meant to be called on newly allocated object. - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); RUBY_ASSERT(rb_shape_canonical_p(RBASIC_SHAPE_ID(obj))); RUBY_ASSERT(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)) == 0); - obj_transition_too_complex(obj, table); + if (rb_shape_obj_too_complex_p(obj)) { + st_table *old_table = ROBJECT_FIELDS_HASH(obj); + ROBJECT_SET_FIELDS_HASH(obj, table); + if (old_table) st_free_table(old_table); + } + else { + obj_transition_too_complex(obj, table); + } } static int @@ -1889,10 +1895,6 @@ generic_shape_ivar(VALUE obj, ID id, bool *new_ivar_out) if (!rb_shape_too_complex_p(current_shape_id)) { if (!rb_shape_find_ivar(current_shape_id, id, &target_shape_id)) { - if (RSHAPE_LEN(current_shape_id) >= SHAPE_MAX_FIELDS) { - rb_raise(rb_eArgError, "too many instance variables"); - } - new_ivar = true; target_shape_id = rb_obj_shape_transition_add_ivar(obj, id); } diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 52a05155149c51..608b8ba93455d2 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -634,7 +634,7 @@ pub const VM_ENV_FLAG_ESCAPED: vm_frame_env_flags = 4; pub const VM_ENV_FLAG_WB_REQUIRED: vm_frame_env_flags = 8; pub const VM_ENV_FLAG_ISOLATED: vm_frame_env_flags = 16; pub type vm_frame_env_flags = u32; -pub type attr_index_t = u16; +pub type attr_index_t = u8; pub type shape_id_t = u32; pub const SHAPE_ID_HAS_IVAR_MASK: shape_id_mask = 8912894; pub type shape_id_mask = u32; diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 1474551b5d2a41..ab27b2890f0e46 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1482,7 +1482,7 @@ pub const VM_ENV_FLAG_ESCAPED: vm_frame_env_flags = 4; pub const VM_ENV_FLAG_WB_REQUIRED: vm_frame_env_flags = 8; pub const VM_ENV_FLAG_ISOLATED: vm_frame_env_flags = 16; pub type vm_frame_env_flags = u32; -pub type attr_index_t = u16; +pub type attr_index_t = u8; pub type shape_id_t = u32; pub const SHAPE_ID_HEAP_INDEX_MASK: shape_id_fl_type = 7864320; pub const SHAPE_ID_FL_TOO_COMPLEX: shape_id_fl_type = 8388608; diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 8ab9441a147b9d..525755d03813c3 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -7506,7 +7506,7 @@ mod hir_opt_tests { } #[test] - fn test_optimize_getivar_extended() { + fn test_optimize_getivar_complex() { eval(r#" class C attr_reader :foo @@ -7540,12 +7540,9 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1010, cme:0x1018) v23:ObjectSubclass[class_exact:C] = GuardType v10, ObjectSubclass[class_exact:C] - v26:CShape = LoadField v23, :_shape_id@0x1040 - v27:CShape[0x1041] = GuardBitEquals v26, CShape(0x1041) - v28:CPtr = LoadField v23, :_as_heap@0x1042 - v29:BasicObject = LoadField v28, :@foo@0x1043 + v24:BasicObject = GetIvar v23, :@foo CheckInterrupts - Return v29 + Return v24 "); } @@ -7647,7 +7644,7 @@ mod hir_opt_tests { } #[test] - fn test_optimize_getivar_on_module_extended() { + fn test_optimize_getivar_on_module_complex() { eval(r#" module M @foo = 42 @@ -7670,15 +7667,9 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint SingleRactorMode - v17:Module = GuardType v6, Module - v18:CShape = LoadField v17, :_shape_id@0x1000 - v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) - PatchPoint RootBoxOnly - v21:RubyValue = LoadField v17, :_fields_obj@0x1002 - v22:CPtr = LoadField v21, :_as_heap@0x1003 - v23:BasicObject = LoadField v22, :@foo@0x1004 + v11:BasicObject = GetIvar v6, :@foo CheckInterrupts - Return v23 + Return v11 "); } @@ -7725,7 +7716,7 @@ mod hir_opt_tests { } #[test] - fn test_optimize_getivar_on_class_extended() { + fn test_optimize_getivar_on_class_complex() { eval(r#" class C @foo = 42 @@ -7748,15 +7739,9 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint SingleRactorMode - v17:Class = GuardType v6, Class - v18:CShape = LoadField v17, :_shape_id@0x1000 - v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) - PatchPoint RootBoxOnly - v21:RubyValue = LoadField v17, :_fields_obj@0x1002 - v22:CPtr = LoadField v21, :_as_heap@0x1003 - v23:BasicObject = LoadField v22, :@foo@0x1004 + v11:BasicObject = GetIvar v6, :@foo CheckInterrupts - Return v23 + Return v11 "); } @@ -7830,7 +7815,7 @@ mod hir_opt_tests { } #[test] - fn test_optimize_getivar_on_typed_data_heap_fields() { + fn test_optimize_getivar_on_typed_data_complex_fields() { // Typed T_DATA with enough ivars to force heap field storage eval(" class C < Thread @@ -7855,14 +7840,9 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint SingleRactorMode - v17:TypedTData = GuardType v6, TypedTData - v18:CShape = LoadField v17, :_shape_id@0x1000 - v19:CShape[0x1001] = GuardBitEquals v18, CShape(0x1001) - v20:RubyValue = LoadField v17, :_fields_obj@0x1002 - v21:CPtr = LoadField v20, :_as_heap@0x1002 - v22:BasicObject = LoadField v21, :@var1000@0x1003 + v11:BasicObject = GetIvar v6, :@var1000 CheckInterrupts - Return v22 + Return v11 "); } @@ -7928,12 +7908,12 @@ mod hir_opt_tests { class C def foo_then_many @foo = 1 - 1000.times { |i| instance_variable_set(:"@v#{i}", i) } + 10.times { |i| instance_variable_set(:"@v#{i}", i) } @bar = 2 end def many_then_foo - 1000.times { |i| instance_variable_set(:"@v#{i}", i) } + 10.times { |i| instance_variable_set(:"@v#{i}", i) } @bar = 3 @foo = 4 end @@ -7968,29 +7948,28 @@ mod hir_opt_tests { v17:CInt64 = IntAnd v12, v14 v18:CBool = IsBitEqual v17, v16 IfTrue v18, bb5() - v23:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f) - v24:CPtr[CPtr(0x1002)] = Const CPtr(0x1002) - v25 = RefineType v24, CUInt64 - v26:CInt64 = IntAnd v12, v23 - v27:CBool = IsBitEqual v26, v25 - IfTrue v27, bb6() - v32:BasicObject = GetIvar v11, :@foo - Jump bb4(v32) + v22:CUInt64[0xffffffff0000001f] = Const CUInt64(0xffffffff0000001f) + v23:CPtr[CPtr(0x1002)] = Const CPtr(0x1002) + v24 = RefineType v23, CUInt64 + v25:CInt64 = IntAnd v12, v22 + v26:CBool = IsBitEqual v25, v24 + IfTrue v26, bb6() + v31:BasicObject = GetIvar v11, :@foo + Jump bb4(v31) bb5(): - v20:CPtr = LoadField v11, :_as_heap@0x1003 - v21:BasicObject = LoadField v20, :@foo@0x1004 - Jump bb4(v21) + v20:BasicObject = LoadField v11, :@foo@0x1003 + Jump bb4(v20) bb6(): - v29:CPtr = LoadField v11, :_as_heap@0x1003 - v30:BasicObject = LoadField v29, :@foo@0x1000 - Jump bb4(v30) + v28:CPtr = LoadField v11, :_as_heap@0x1004 + v29:BasicObject = LoadField v28, :@foo@0x1000 + Jump bb4(v29) bb4(v13:BasicObject): - v35:Fixnum[1] = Const Value(1) + v34:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v46:Fixnum = GuardType v13, Fixnum - v47:Fixnum = FixnumAdd v46, v35 + v45:Fixnum = GuardType v13, Fixnum + v46:Fixnum = FixnumAdd v45, v34 CheckInterrupts - Return v47 + Return v46 "); } @@ -8003,12 +7982,12 @@ mod hir_opt_tests { class C def foo_then_many @foo = 1 - 1000.times { |i| instance_variable_set(:"@v#{i}", i) } + 100.times { |i| instance_variable_set(:"@v#{i}", i) } @bar = 2 end def many_then_foo - 1000.times { |i| instance_variable_set(:"@v#{i}", i) } + 100.times { |i| instance_variable_set(:"@v#{i}", i) } @bar = 3 @foo = 4 end @@ -8052,26 +8031,25 @@ mod hir_opt_tests { v26:CInt64 = IntAnd v12, v23 v27:CBool = IsBitEqual v26, v25 IfTrue v27, bb6() - v45:CShape = LoadField v11, :_shape_id@0x1003 - v46:CShape[0x1004] = GuardBitEquals v45, CShape(0x1004) - v47:CPtr = LoadField v11, :_as_heap@0x1005 - v48:BasicObject = LoadField v47, :@foo@0x1000 - Jump bb4(v48) + v44:CShape = LoadField v11, :_shape_id@0x1003 + v45:CShape[0x1004] = GuardBitEquals v44, CShape(0x1004) + v46:CPtr = LoadField v11, :_as_heap@0x1005 + v47:BasicObject = LoadField v46, :@foo@0x1000 + Jump bb4(v47) bb5(): v20:CPtr = LoadField v11, :_as_heap@0x1005 v21:BasicObject = LoadField v20, :@foo@0x1000 Jump bb4(v21) bb6(): - v29:CPtr = LoadField v11, :_as_heap@0x1005 - v30:BasicObject = LoadField v29, :@foo@0x1006 - Jump bb4(v30) + v29:BasicObject = LoadField v11, :@foo@0x1006 + Jump bb4(v29) bb4(v13:BasicObject): - v35:Fixnum[1] = Const Value(1) + v34:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Integer@0x1008, +@0x1010, cme:0x1018) - v51:Fixnum = GuardType v13, Fixnum - v52:Fixnum = FixnumAdd v51, v35 + v50:Fixnum = GuardType v13, Fixnum + v51:Fixnum = FixnumAdd v50, v34 CheckInterrupts - Return v52 + Return v51 "); } From 4eeec614bb9379ebac78ecc4fa98238a8311d9eb Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 6 May 2026 08:42:37 +0200 Subject: [PATCH 2/4] shapes: Rename `TOO_COMPLEX` in just `COMPLEX` The `too_` prefix wasn't consistently used and just make the thing longer for no benefit. --- bootstraptest/test_ractor.rb | 4 +- debug_counter.h | 2 +- ext/objspace/objspace_dump.c | 4 +- gc.c | 24 ++++---- imemo.c | 12 ++-- internal/class.h | 2 +- internal/imemo.h | 2 +- internal/variable.h | 2 +- jit.c | 4 +- object.c | 6 +- ractor.c | 4 +- shape.c | 32 +++++----- shape.h | 40 ++++++------- test/objspace/test_objspace.rb | 14 ++--- test/ruby/test_gc_compact.rb | 2 +- test/ruby/test_marshal.rb | 6 +- test/ruby/test_object.rb | 10 ++-- test/ruby/test_object_id.rb | 12 ++-- test/ruby/test_shapes.rb | 106 ++++++++++++++++----------------- variable.c | 78 ++++++++++++------------ vm_insnhelper.c | 6 +- yjit.c | 4 +- yjit/bindgen/src/main.rs | 4 +- yjit/src/codegen.rs | 22 +++---- yjit/src/cruby.rs | 4 +- yjit/src/cruby_bindings.inc.rs | 4 +- zjit/bindgen/src/main.rs | 2 +- zjit/src/codegen_tests.rs | 2 +- zjit/src/cruby.rs | 4 +- zjit/src/cruby_bindings.inc.rs | 4 +- zjit/src/hir.rs | 26 ++++---- zjit/src/hir/opt_tests.rs | 4 +- zjit/src/stats.rs | 8 +-- 33 files changed, 230 insertions(+), 230 deletions(-) diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 987f146dc68ebb..b40c908f6fbced 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -2163,7 +2163,7 @@ def ==(o) # move object with complex generic ivars assert_equal 'ok', %q{ - # Make Array too_complex + # Make Array complex 30.times { |i| [].instance_variable_set(:"@complex#{i}", 1) } ractor = Ractor.new { Ractor.receive } @@ -2190,7 +2190,7 @@ def ==(o) # copy object with complex generic ivars assert_equal 'ok', %q{ - # Make Array too_complex + # Make Array complex 30.times { |i| [].instance_variable_set(:"@complex#{i}", 1) } ractor = Ractor.new { Ractor.receive } diff --git a/debug_counter.h b/debug_counter.h index 723e9e1249f4f0..d326aa0f1851c1 100644 --- a/debug_counter.h +++ b/debug_counter.h @@ -240,7 +240,7 @@ RB_DEBUG_COUNTER(obj_wb_unprotect) RB_DEBUG_COUNTER(obj_obj_embed) RB_DEBUG_COUNTER(obj_obj_ptr) -RB_DEBUG_COUNTER(obj_obj_too_complex) +RB_DEBUG_COUNTER(obj_obj_complex) RB_DEBUG_COUNTER(obj_str_ptr) RB_DEBUG_COUNTER(obj_str_embed) diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index cb0f9124994196..446a3024fee1ec 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -598,8 +598,8 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, ", \"ivars\":"); dump_append_lu(dc, ROBJECT_FIELDS_COUNT(obj)); - if (rb_shape_obj_too_complex_p(obj)) { - dump_append(dc, ", \"too_complex_shape\":true"); + if (rb_shape_obj_complex_p(obj)) { + dump_append(dc, ", \"complex_shape\":true"); } break; diff --git a/gc.c b/gc.c index 0f964d2a679be0..6b0ea041abf34b 100644 --- a/gc.c +++ b/gc.c @@ -1074,7 +1074,7 @@ VALUE class_allocate_complex_instance(VALUE klass, uint32_t capacity) { shape_id_t initial_shape_id = rb_shape_root(rb_gc_heap_id_for_size(sizeof(struct RObject))); VALUE obj = rb_newobj_of_with_shape(klass, T_OBJECT, initial_shape_id, sizeof(struct RObject)); - rb_obj_init_too_complex(obj, rb_st_init_numtable_with_size(capacity)); + rb_obj_init_complex(obj, rb_st_init_numtable_with_size(capacity)); return obj; } @@ -1084,7 +1084,7 @@ rb_class_allocate_instance(VALUE klass) uint32_t index_tbl_num_entries = RCLASS_MAX_IV_COUNT(klass); VALUE obj; - // Directly start as TOO_COMPLEX if we know we're over the limit. + // Directly start as COMPLEX if we know we're over the limit. RUBY_ASSERT(rb_shape_tree.max_capacity > 0); if (RB_UNLIKELY(index_tbl_num_entries > rb_shape_tree.max_capacity)) { obj = class_allocate_complex_instance(klass, index_tbl_num_entries); @@ -1543,8 +1543,8 @@ rb_gc_obj_free(void *objspace, VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { - if (rb_shape_obj_too_complex_p(obj)) { - RB_DEBUG_COUNTER_INC(obj_obj_too_complex); + if (rb_shape_obj_complex_p(obj)) { + RB_DEBUG_COUNTER_INC(obj_obj_complex); st_free_table(ROBJECT_FIELDS_HASH(obj)); } else { @@ -2146,8 +2146,8 @@ static inline VALUE object_id_get(VALUE obj, shape_id_t shape_id) { VALUE id; - if (rb_shape_too_complex_p(shape_id)) { - id = rb_obj_field_get(obj, ROOT_TOO_COMPLEX_WITH_OBJ_ID); + if (rb_shape_complex_p(shape_id)) { + id = rb_obj_field_get(obj, ROOT_COMPLEX_WITH_OBJ_ID); } else { id = rb_obj_field_get(obj, rb_shape_object_id(shape_id)); @@ -2585,7 +2585,7 @@ rb_obj_memsize_of(VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { size += rb_st_memsize(ROBJECT_FIELDS_HASH(obj)); } else { @@ -3491,7 +3491,7 @@ rb_gc_mark_children(void *objspace, VALUE obj) case T_OBJECT: { uint32_t len; - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { gc_mark_tbl_no_pin(ROBJECT_FIELDS_HASH(obj)); len = ROBJECT_FIELDS_COUNT_COMPLEX(obj); } @@ -3582,7 +3582,7 @@ rb_gc_obj_optimal_size(VALUE obj) } case T_OBJECT: - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { return sizeof(struct RObject); } else { @@ -3849,7 +3849,7 @@ gc_ref_update_object(void *objspace, VALUE v) VALUE *ptr = ROBJECT_FIELDS(v); if (FL_TEST_RAW(v, ROBJECT_HEAP)) { - if (rb_shape_obj_too_complex_p(v)) { + if (rb_shape_obj_complex_p(v)) { gc_ref_update_table_values_only(ROBJECT_FIELDS_HASH(v)); return; } @@ -5037,9 +5037,9 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU case T_OBJECT: { if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { size_t hash_len = rb_st_table_size(ROBJECT_FIELDS_HASH(obj)); - APPEND_F("(too_complex) len:%zu", hash_len); + APPEND_F("(complex) len:%zu", hash_len); } else { APPEND_F("(embed) len:%d capa:%d", RSHAPE_LEN(RBASIC_SHAPE_ID(obj)), ROBJECT_FIELDS_CAPACITY(obj)); diff --git a/imemo.c b/imemo.c index 9483266735f7a2..18941407efa92d 100644 --- a/imemo.c +++ b/imemo.c @@ -177,7 +177,7 @@ rb_imemo_fields_clone(VALUE fields_obj) shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj); VALUE clone; - if (rb_shape_too_complex_p(shape_id)) { + if (rb_shape_complex_p(shape_id)) { st_table *src_table = rb_imemo_fields_complex_tbl(fields_obj); st_table *dest_table = xcalloc(1, sizeof(st_table)); @@ -205,8 +205,8 @@ rb_imemo_fields_clear(VALUE fields_obj) { // When replacing an imemo/fields by another one, we must clear // its shape so that gc.c:obj_free_object_id won't be called. - if (rb_shape_obj_too_complex_p(fields_obj)) { - RBASIC_SET_SHAPE_ID(fields_obj, ROOT_TOO_COMPLEX_SHAPE_ID); + if (rb_shape_obj_complex_p(fields_obj)) { + RBASIC_SET_SHAPE_ID(fields_obj, ROOT_COMPLEX_SHAPE_ID); } else { RBASIC_SET_SHAPE_ID(fields_obj, ROOT_SHAPE_ID); @@ -259,7 +259,7 @@ rb_imemo_memsize(VALUE obj) case imemo_cvar_entry: break; case imemo_fields: - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table); } break; @@ -513,7 +513,7 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating) // imemo_fields can refer unshareable objects // even if the imemo_fields is shareable. - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { st_table *tbl = rb_imemo_fields_complex_tbl(obj); if (reference_updating) { rb_gc_ref_update_table_values_only(tbl); @@ -561,7 +561,7 @@ imemo_fields_free(struct rb_fields *fields) { if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_HEAP)) { shape_id_t shape_id = RBASIC_SHAPE_ID((VALUE)fields); - RUBY_ASSERT(rb_shape_too_complex_p(shape_id)); + RUBY_ASSERT(rb_shape_complex_p(shape_id)); st_free_table(fields->as.complex.table); } } diff --git a/internal/class.h b/internal/class.h index 7fef08a84e5fdd..368e9cfc408462 100644 --- a/internal/class.h +++ b/internal/class.h @@ -550,7 +550,7 @@ RCLASS_FIELDS_COUNT(VALUE obj) VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj); if (fields_obj) { - if (rb_shape_obj_too_complex_p(fields_obj)) { + if (rb_shape_obj_complex_p(fields_obj)) { return (uint32_t)rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj)); } else { diff --git a/internal/imemo.h b/internal/imemo.h index 23e76875c7d889..fae2334fa0ffc1 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -291,7 +291,7 @@ rb_imemo_fields_complex_tbl(VALUE fields_obj) RUBY_ASSERT(FL_TEST_RAW(fields_obj, OBJ_FIELD_HEAP)); // Some codepaths unconditionally access the fields_ptr, and assume it can be used as st_table if the - // shape is too_complex. + // shape is complex. RUBY_ASSERT((st_table *)rb_imemo_fields_ptr(fields_obj) == IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table); return IMEMO_OBJ_FIELDS(fields_obj)->as.complex.table; diff --git a/internal/variable.h b/internal/variable.h index 5dbcd320d967ce..58364941c12156 100644 --- a/internal/variable.h +++ b/internal/variable.h @@ -48,7 +48,7 @@ void rb_gvar_box_dynamic(const char *name); VALUE rb_mod_set_temporary_name(VALUE, VALUE); void rb_obj_copy_ivs_to_hash_table(VALUE obj, st_table *table); -void rb_obj_init_too_complex(VALUE obj, st_table *table); +void rb_obj_init_complex(VALUE obj, st_table *table); void rb_evict_ivars_to_hash(VALUE obj); VALUE rb_obj_field_get(VALUE obj, shape_id_t target_shape_id); void rb_ivar_set_internal(VALUE obj, ID id, VALUE val); diff --git a/jit.c b/jit.c index e335875322bb24..1a997605aafc47 100644 --- a/jit.c +++ b/jit.c @@ -549,9 +549,9 @@ rb_set_cfp_sp(struct rb_control_frame_struct *cfp, VALUE *sp) } bool -rb_jit_shape_too_complex_p(shape_id_t shape_id) +rb_jit_shape_complex_p(shape_id_t shape_id) { - return rb_shape_too_complex_p(shape_id); + return rb_shape_complex_p(shape_id); } bool diff --git a/object.c b/object.c index 10044c51331980..4e98d9a5ccb30a 100644 --- a/object.c +++ b/object.c @@ -338,7 +338,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) shape_id_t src_shape_id = RBASIC_SHAPE_ID(obj); - if (rb_shape_too_complex_p(src_shape_id)) { + if (rb_shape_complex_p(src_shape_id)) { rb_shape_copy_complex_ivars(dest, obj, src_shape_id, ROBJECT_FIELDS_HASH(obj)); return; } @@ -347,10 +347,10 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) RUBY_ASSERT(RSHAPE_TYPE_P(initial_shape_id, SHAPE_ROOT)); shape_id_t dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id); - if (UNLIKELY(rb_shape_too_complex_p(dest_shape_id))) { + if (UNLIKELY(rb_shape_complex_p(dest_shape_id))) { st_table *table = rb_st_init_numtable_with_size(src_num_ivs); rb_obj_copy_ivs_to_hash_table(obj, table); - rb_obj_init_too_complex(dest, table); + rb_obj_init_complex(dest, table); return; } diff --git a/ractor.c b/ractor.c index 8b5cc61b40d862..0176206afd23ca 100644 --- a/ractor.c +++ b/ractor.c @@ -1809,7 +1809,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) if (UNLIKELY(rb_obj_gen_fields_p(obj))) { VALUE fields_obj = rb_obj_fields_no_ractor_check(obj); - if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) { + if (UNLIKELY(rb_shape_obj_complex_p(obj))) { struct obj_traverse_replace_callback_data d = { .stop = false, .data = data, @@ -1846,7 +1846,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) case T_OBJECT: { - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { struct obj_traverse_replace_callback_data d = { .stop = false, .data = data, diff --git a/shape.c b/shape.c index 1ff1c47c7d0c97..2b4256e5f3068c 100644 --- a/shape.c +++ b/shape.c @@ -658,7 +658,7 @@ get_next_shape_internal(rb_shape_t *shape, ID id, enum shape_type shape_type, bo // If we didn't find the shape we're looking for we create it. if (!res) { // If we're not allowed to create a new variation, of if we're out of shapes - // we return TOO_COMPLEX_SHAPE. + // we return COMPLEX_SHAPE. if (!new_variations_allowed || rb_shapes_count() > MAX_SHAPE_ID) { res = NULL; } @@ -697,7 +697,7 @@ rb_shape_transition_object_id(shape_id_t original_shape_id) bool dont_care; rb_shape_t *shape = get_next_shape_internal(RSHAPE(original_shape_id), id_object_id, SHAPE_OBJ_ID, &dont_care, true); if (!shape) { - return ROOT_TOO_COMPLEX_WITH_OBJ_ID | RSHAPE_FLAGS(original_shape_id); + return ROOT_COMPLEX_WITH_OBJ_ID | RSHAPE_FLAGS(original_shape_id); } RUBY_ASSERT(shape); @@ -791,7 +791,7 @@ shape_get_next(rb_shape_t *shape, enum shape_type shape_type, VALUE klass, ID id rb_shape_t *new_shape = get_next_shape_internal(shape, id, shape_type, &variation_created, allow_new_shape); if (!new_shape) { - // We could create a new variation, transitioning to TOO_COMPLEX. + // We could create a new variation, transitioning to COMPLEX. return NULL; } @@ -883,7 +883,7 @@ rb_obj_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_ shape_id_t original_shape_id = RBASIC_SHAPE_ID(obj); RUBY_ASSERT(!rb_shape_frozen_p(original_shape_id)); - if (rb_shape_too_complex_p(original_shape_id)) { + if (rb_shape_complex_p(original_shape_id)) { return original_shape_id; } @@ -899,7 +899,7 @@ rb_obj_shape_transition_remove_ivar(VALUE obj, ID id, shape_id_t *removed_shape_ } else if (removed_shape) { // We found the shape to remove, but couldn't create a new variation. - // We must transition to TOO_COMPLEX. + // We must transition to COMPLEX. shape_id_t next_shape_id = rb_shape_transition_complex(original_shape_id); RUBY_ASSERT(rb_shape_has_object_id(next_shape_id) == rb_shape_has_object_id(original_shape_id)); return next_shape_id; @@ -1027,7 +1027,7 @@ shape_find_ivar(rb_shape_t *shape, ID id, rb_shape_t **ivar_shape) bool rb_shape_find_ivar(shape_id_t current_shape_id, ID id, shape_id_t *ivar_shape_id) { - RUBY_ASSERT(!rb_shape_too_complex_p(current_shape_id)); + RUBY_ASSERT(!rb_shape_complex_p(current_shape_id)); rb_shape_t *shape = RSHAPE(current_shape_id); rb_shape_t *ivar_shape; @@ -1054,7 +1054,7 @@ rb_shape_get_iv_index(shape_id_t shape_id, ID id, attr_index_t *value) { // It doesn't make sense to ask for the index of an IV that's stored // on an object that is "too complex" as it uses a hash for storing IVs - RUBY_ASSERT(!rb_shape_too_complex_p(shape_id)); + RUBY_ASSERT(!rb_shape_complex_p(shape_id)); shape_id_t ivar_shape_id; if (rb_shape_find_ivar(shape_id, id, &ivar_shape_id)) { @@ -1106,8 +1106,8 @@ shape_rebuild(rb_shape_t *initial_shape, rb_shape_t *dest_shape) shape_id_t rb_shape_rebuild(shape_id_t initial_shape_id, shape_id_t dest_shape_id) { - RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id)); - RUBY_ASSERT(!rb_shape_too_complex_p(dest_shape_id)); + RUBY_ASSERT(!rb_shape_complex_p(initial_shape_id)); + RUBY_ASSERT(!rb_shape_complex_p(dest_shape_id)); shape_id_t next_shape_id; // The shape has a SHAPE_OBJ_ID edge, it needs to be rebuilt. @@ -1162,13 +1162,13 @@ rb_shape_copy_fields(VALUE dest, VALUE *dest_buf, shape_id_t dest_shape_id, VALU void rb_shape_copy_complex_ivars(VALUE dest, VALUE obj, shape_id_t src_shape_id, st_table *fields_table) { - // obj is TOO_COMPLEX so we can copy its iv_hash + // obj is COMPLEX so we can copy its iv_hash st_table *table = st_copy(fields_table); if (rb_shape_has_object_id(src_shape_id)) { st_data_t id = (st_data_t)id_object_id; st_delete(table, &id, NULL); } - rb_obj_init_too_complex(dest, table); + rb_obj_init_complex(dest, table); rb_gc_writebarrier_remember(dest); } @@ -1202,7 +1202,7 @@ rb_shape_memsize(shape_id_t shape_id) bool rb_shape_foreach_field(shape_id_t initial_shape_id, rb_shape_foreach_transition_callback func, void *data) { - RUBY_ASSERT(!rb_shape_too_complex_p(initial_shape_id)); + RUBY_ASSERT(!rb_shape_complex_p(initial_shape_id)); rb_shape_t *shape = RSHAPE(initial_shape_id); if (shape->type == SHAPE_ROOT) { @@ -1261,7 +1261,7 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id) } // Make sure SHAPE_ID_HAS_IVAR_MASK is valid. - if (rb_shape_too_complex_p(shape_id)) { + if (rb_shape_complex_p(shape_id)) { RUBY_ASSERT(shape_id & SHAPE_ID_HAS_IVAR_MASK); // Ensure complex object don't appear as embedded @@ -1309,10 +1309,10 @@ rb_shape_verify_consistency(VALUE obj, shape_id_t shape_id) */ static VALUE -shape_too_complex(VALUE self) +shape_complex(VALUE self) { shape_id_t shape_id = NUM2INT(rb_struct_getmember(self, rb_intern("id"))); - return RBOOL(rb_shape_too_complex_p(shape_id)); + return RBOOL(rb_shape_complex_p(shape_id)); } static VALUE @@ -1625,7 +1625,7 @@ Init_shape(void) rb_define_method(rb_cShape, "parent", rb_shape_parent, 0); rb_define_method(rb_cShape, "edges", rb_shape_edges, 0); rb_define_method(rb_cShape, "depth", rb_shape_export_depth, 0); - rb_define_method(rb_cShape, "too_complex?", shape_too_complex, 0); + rb_define_method(rb_cShape, "complex?", shape_complex, 0); rb_define_method(rb_cShape, "shape_frozen?", shape_frozen, 0); rb_define_method(rb_cShape, "has_object_id?", shape_has_object_id_p, 0); diff --git a/shape.h b/shape.h index 07ae2721c59eaf..78506a80e50074 100644 --- a/shape.h +++ b/shape.h @@ -31,7 +31,7 @@ STATIC_ASSERT(shape_id_num_bits, SHAPE_ID_NUM_BITS == sizeof(shape_id_t) * CHAR_ // Whether the object is frozen or not. // 24 SHAPE_ID_FL_HAS_OBJECT_ID // Whether the object has an `SHAPE_OBJ_ID` transition. -// 25 SHAPE_ID_FL_TOO_COMPLEX +// 25 SHAPE_ID_FL_COMPLEX // The object is backed by a `st_table`. enum shape_id_fl_type { @@ -39,12 +39,12 @@ enum shape_id_fl_type { SHAPE_ID_HEAP_INDEX_MASK = ((1 << SHAPE_ID_HEAP_INDEX_BITS) - 1) << SHAPE_ID_HEAP_INDEX_OFFSET, - SHAPE_ID_FL_TOO_COMPLEX = RBIMPL_SHAPE_ID_FL(0), + SHAPE_ID_FL_COMPLEX = RBIMPL_SHAPE_ID_FL(0), SHAPE_ID_FL_FROZEN = RBIMPL_SHAPE_ID_FL(1), SHAPE_ID_FL_HAS_OBJECT_ID = RBIMPL_SHAPE_ID_FL(2), SHAPE_ID_FL_NON_CANONICAL_MASK = SHAPE_ID_FL_FROZEN | SHAPE_ID_FL_HAS_OBJECT_ID, - SHAPE_ID_FLAGS_MASK = SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_NON_CANONICAL_MASK | SHAPE_ID_FL_TOO_COMPLEX, + SHAPE_ID_FLAGS_MASK = SHAPE_ID_HEAP_INDEX_MASK | SHAPE_ID_FL_NON_CANONICAL_MASK | SHAPE_ID_FL_COMPLEX, #undef RBIMPL_SHAPE_ID_FL }; @@ -52,7 +52,7 @@ enum shape_id_fl_type { // This mask allows to check if a shape_id contains any ivar. // It relies on ROOT_SHAPE_WITH_OBJ_ID==1. enum shape_id_mask { - SHAPE_ID_HAS_IVAR_MASK = SHAPE_ID_FL_TOO_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1), + SHAPE_ID_HAS_IVAR_MASK = SHAPE_ID_FL_COMPLEX | (SHAPE_ID_OFFSET_MASK - 1), }; // The interpreter doesn't care about frozen status, slot size or object id when reading ivars. @@ -74,8 +74,8 @@ typedef uint32_t redblack_id_t; #define ROOT_SHAPE_ID 0x0 #define ROOT_SHAPE_WITH_OBJ_ID 0x1 -#define ROOT_TOO_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_TOO_COMPLEX) -#define ROOT_TOO_COMPLEX_WITH_OBJ_ID (ROOT_SHAPE_WITH_OBJ_ID | SHAPE_ID_FL_TOO_COMPLEX | SHAPE_ID_FL_HAS_OBJECT_ID) +#define ROOT_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_COMPLEX) +#define ROOT_COMPLEX_WITH_OBJ_ID (ROOT_SHAPE_WITH_OBJ_ID | SHAPE_ID_FL_COMPLEX | SHAPE_ID_FL_HAS_OBJECT_ID) enum shape_type { SHAPE_ROOT, @@ -98,7 +98,7 @@ typedef struct rb_shape rb_shape_t; enum shape_flags { SHAPE_FL_FROZEN = 1 << 0, SHAPE_FL_HAS_OBJECT_ID = 1 << 1, - SHAPE_FL_TOO_COMPLEX = 1 << 2, + SHAPE_FL_COMPLEX = 1 << 2, SHAPE_FL_NON_CANONICAL_MASK = SHAPE_FL_FROZEN | SHAPE_FL_HAS_OBJECT_ID, }; @@ -209,15 +209,15 @@ rb_shape_frozen_p(shape_id_t shape_id) } static inline bool -rb_shape_too_complex_p(shape_id_t shape_id) +rb_shape_complex_p(shape_id_t shape_id) { - return shape_id & SHAPE_ID_FL_TOO_COMPLEX; + return shape_id & SHAPE_ID_FL_COMPLEX; } static inline bool -rb_shape_obj_too_complex_p(VALUE obj) +rb_shape_obj_complex_p(VALUE obj) { - return !RB_SPECIAL_CONST_P(obj) && rb_shape_too_complex_p(RBASIC_SHAPE_ID(obj)); + return !RB_SPECIAL_CONST_P(obj) && rb_shape_complex_p(RBASIC_SHAPE_ID(obj)); } static inline bool @@ -322,7 +322,7 @@ ROBJECT_FIELDS_CAPACITY(VALUE obj) RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); // Asking for capacity doesn't make sense when the object is using // a hash table for storing instance variables - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); return RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)); } @@ -330,7 +330,7 @@ static inline st_table * ROBJECT_FIELDS_HASH(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(rb_shape_obj_complex_p(obj)); RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP)); return ROBJECT(obj)->as.hash; @@ -340,7 +340,7 @@ static inline void ROBJECT_SET_FIELDS_HASH(VALUE obj, st_table *tbl) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(rb_shape_obj_complex_p(obj)); RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP)); ROBJECT(obj)->as.hash = tbl; @@ -356,14 +356,14 @@ static inline uint32_t ROBJECT_FIELDS_COUNT_NOT_COMPLEX(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); return RSHAPE(RBASIC_SHAPE_ID(obj))->next_field_index; } static inline uint32_t ROBJECT_FIELDS_COUNT(VALUE obj) { - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { return ROBJECT_FIELDS_COUNT_COMPLEX(obj); } else { @@ -398,7 +398,7 @@ rb_shape_obj_has_ivars(VALUE obj) static inline bool rb_shape_has_fields(shape_id_t shape_id) { - return shape_id & (SHAPE_ID_OFFSET_MASK | SHAPE_ID_FL_TOO_COMPLEX); + return shape_id & (SHAPE_ID_OFFSET_MASK | SHAPE_ID_FL_COMPLEX); } static inline bool @@ -451,10 +451,10 @@ rb_shape_transition_frozen(shape_id_t shape_id) static inline shape_id_t rb_shape_transition_complex(shape_id_t shape_id) { - shape_id_t next_shape_id = ROOT_TOO_COMPLEX_SHAPE_ID; + shape_id_t next_shape_id = ROOT_COMPLEX_SHAPE_ID; if (rb_shape_has_object_id(shape_id)) { - next_shape_id = ROOT_TOO_COMPLEX_WITH_OBJ_ID; + next_shape_id = ROOT_COMPLEX_WITH_OBJ_ID; } uint8_t heap_index = rb_shape_heap_index(shape_id); @@ -617,7 +617,7 @@ rb_setivar_cache_revalidate(shape_id_t shape_id, rb_setivar_cache cache) RUBY_ASSERT(cache.source_shape_offset == cache.dest_shape_offset || RSHAPE_DIRECT_CHILD_P(shape_id, cache.dest_shape_offset)); RUBY_ASSERT(cache.index < RSHAPE_CAPACITY(shape_id)); RUBY_ASSERT(!rb_shape_frozen_p(shape_id)); - RUBY_ASSERT(!rb_shape_too_complex_p(shape_id)); + RUBY_ASSERT(!rb_shape_complex_p(shape_id)); // We use the cached offset, but combined with the current shape flags. return rb_shape_transition_offset(shape_id, cache.dest_shape_offset); diff --git a/test/objspace/test_objspace.rb b/test/objspace/test_objspace.rb index 8d019e587a63e2..faa22f1424f90a 100644 --- a/test/objspace/test_objspace.rb +++ b/test/objspace/test_objspace.rb @@ -376,7 +376,7 @@ def test_dump_flag_age if defined?(RubyVM::Shape) class TooComplex; end - def test_dump_too_complex_shape + def test_dump_complex_shape omit "flaky test" RubyVM::Shape::SHAPE_MAX_VARIATIONS.times do @@ -385,26 +385,26 @@ def test_dump_too_complex_shape tc = TooComplex.new info = ObjectSpace.dump(tc) - assert_not_match(/"too_complex_shape"/, info) + assert_not_match(/"complex_shape"/, info) tc.instance_variable_set(:@new_ivar, 1) info = ObjectSpace.dump(tc) - assert_match(/"too_complex_shape":true/, info) + assert_match(/"complex_shape":true/, info) if defined?(JSON) - assert_true(JSON.parse(info)["too_complex_shape"]) + assert_true(JSON.parse(info)["complex_shape"]) end end end class NotTooComplex ; end - def test_dump_not_too_complex_shape + def test_dump_not_complex_shape tc = NotTooComplex.new tc.instance_variable_set(:@new_ivar, 1) info = ObjectSpace.dump(tc) - assert_not_match(/"too_complex_shape"/, info) + assert_not_match(/"complex_shape"/, info) if defined?(JSON) - assert_nil(JSON.parse(info)["too_complex_shape"]) + assert_nil(JSON.parse(info)["complex_shape"]) end end diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb index b8d53d71973acc..cb5e9d6ccb4b12 100644 --- a/test/ruby/test_gc_compact.rb +++ b/test/ruby/test_gc_compact.rb @@ -469,7 +469,7 @@ def set_a end; end - def test_moving_too_complex_generic_ivar + def test_moving_complex_generic_ivar omit "not compiled with SHAPE_DEBUG" unless defined?(RubyVM::Shape) assert_separately([], <<~RUBY) diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index 9ee7331e6cf7ad..1e4de74503de6a 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -471,7 +471,7 @@ def test_marshal_private_class class TooComplex def initialize - @marshal_too_complex = 1 + @marshal_complex = 1 end end @@ -487,10 +487,10 @@ def test_complex_shape_object_id_not_dumped obj.instance_variable_set(ivar, 1) if defined?(RubyVM::Shape) - assert_predicate(RubyVM::Shape.of(obj), :too_complex?) + assert_predicate(RubyVM::Shape.of(obj), :complex?) end obj.object_id - assert_equal "\x04\bo:\x1CTestMarshal::TooComplex\a:\x19@marshal_too_complexi\x06:\f#{ivar}i\x06".b, Marshal.dump(obj) + assert_equal "\x04\bo:\x1CTestMarshal::TooComplex\a:\x15@marshal_complexi\x06:\f#{ivar}i\x06".b, Marshal.dump(obj) end def test_marshal_complex diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index 35b45471353222..53ae4fb1105f47 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -985,8 +985,8 @@ def test_inspect_mutating_ivar 10.times { |i| assert_include result, "@v#{i}=0" } end - def test_inspect_mutating_ivar_too_complex - # Force too_complex by creating many shape variations on the same class + def test_inspect_mutating_ivar_complex + # Force complex by creating many shape variations on the same class c = Class.new 50.times do |i| o = c.new @@ -1001,11 +1001,11 @@ def test_inspect_mutating_ivar_too_complex end obj.instance_variable_set(:@evil, evil) 10.times { |i| obj.instance_variable_set(:"@v#{i}", 0) } - # too_complex objects use st_foreach which handles mutation gracefully + # complex objects use st_foreach which handles mutation gracefully obj.inspect end - def test_inspect_too_complex + def test_inspect_complex kernel_inspect = Kernel.instance_method(:inspect) klasses = [ @@ -1015,7 +1015,7 @@ def test_inspect_too_complex Class.new(Hash), Struct.new(:x), Class.new(Thread::Mutex), - # It's very difficult to get a too_complex T_CLASS, so that isn't tested here + # It's very difficult to get a complex T_CLASS, so that isn't tested here ] klasses.each_with_index do |klass, idx| diff --git a/test/ruby/test_object_id.rb b/test/ruby/test_object_id.rb index adb819febce57c..034674e5be1a47 100644 --- a/test/ruby/test_object_id.rb +++ b/test/ruby/test_object_id.rb @@ -140,7 +140,7 @@ def setup class TestObjectIdTooComplex < TestObjectId class TooComplex def initialize - @too_complex_obj_id_test = 1 + @complex_obj_id_test = 1 end end @@ -155,7 +155,7 @@ def setup @obj.instance_variable_set("@a#{rand(10_000)}", 1) if defined?(RubyVM::Shape) - assert_predicate(RubyVM::Shape.of(@obj), :too_complex?) + assert_predicate(RubyVM::Shape.of(@obj), :complex?) end end end @@ -181,7 +181,7 @@ def setup @obj.instance_variable_set("@test", 1) if defined?(RubyVM::Shape) - assert_predicate(RubyVM::Shape.of(@obj), :too_complex?) + assert_predicate(RubyVM::Shape.of(@obj), :complex?) end end end @@ -202,7 +202,7 @@ def setup @obj.instance_variable_set("@a#{rand(10_000)}", 1) if defined?(RubyVM::Shape) - assert_predicate(RubyVM::Shape.of(@obj), :too_complex?) + assert_predicate(RubyVM::Shape.of(@obj), :complex?) end end end @@ -282,7 +282,7 @@ def setup class TestObjectIdStructTooComplex < TestObjectId StructTooComplex = Struct.new(:a) do def initialize - @too_complex_obj_id_test = 1 + @complex_obj_id_test = 1 end end @@ -297,7 +297,7 @@ def setup @obj.instance_variable_set("@a#{rand(10_000)}", 1) if defined?(RubyVM::Shape) - assert_predicate(RubyVM::Shape.of(@obj), :too_complex?) + assert_predicate(RubyVM::Shape.of(@obj), :complex?) end end end diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 374a8bff7ef2a1..ef5dbd9fb15ee5 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -117,12 +117,12 @@ def test_iv_order_correct_on_complex_objects assert_equal obj.expected_ivs, iv_list.map(&:to_s) end - def test_too_complex + def test_complex ensure_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? end def test_ordered_alloc_is_not_complex @@ -185,7 +185,7 @@ class Hi; end obj.instance_variable_set(:@c, 1) obj.instance_variable_set(:@d, 1) - assert_predicate RubyVM::Shape.of(obj), :too_complex? + assert_predicate RubyVM::Shape.of(obj), :complex? end; end @@ -193,13 +193,13 @@ def test_too_many_ivs_on_class obj = Class.new obj.instance_variable_set(:@test_too_many_ivs_on_class, 1) - refute_predicate RubyVM::Shape.of(obj), :too_complex? + refute_predicate RubyVM::Shape.of(obj), :complex? MANY_IVS.times do obj.instance_variable_set(:"@a#{_1}", 1) end - assert_predicate RubyVM::Shape.of(obj), :too_complex? + assert_predicate RubyVM::Shape.of(obj), :complex? end def test_removing_when_too_many_ivs_on_class @@ -228,7 +228,7 @@ def test_removing_when_too_many_ivs_on_module assert_empty obj.instance_variables end - def test_too_complex_geniv + def test_complex_geniv assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; class TooComplex < Hash @@ -628,7 +628,7 @@ class Hi; end end; end - def test_too_complex_ractor + def test_complex_ractor assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; $VERBOSE = nil @@ -643,14 +643,14 @@ class TooComplex tc = TooComplex.new tc.instance_variable_set(:"@very_unique", 3) - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.very_unique assert_equal 3, Ractor.new(tc) { |x| x.very_unique }.value assert_equal tc.instance_variables.sort, Ractor.new(tc) { |x| x.instance_variables }.value.sort end; end - def test_too_complex_ractor_shareable + def test_complex_ractor_shareable assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; $VERBOSE = nil @@ -665,13 +665,13 @@ class TooComplex tc = TooComplex.new tc.instance_variable_set(:"@very_unique", 3) - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.very_unique assert_equal 3, Ractor.make_shareable(tc).very_unique end; end - def test_too_complex_and_frozen + def test_complex_and_frozen assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; $VERBOSE = nil @@ -687,12 +687,12 @@ class TooComplex tc.instance_variable_set(:"@very_unique", 3) shape = RubyVM::Shape.of(tc) - assert_predicate shape, :too_complex? + assert_predicate shape, :complex? refute_predicate shape, :shape_frozen? tc.freeze frozen_shape = RubyVM::Shape.of(tc) refute_equal shape.id, frozen_shape.id - assert_predicate frozen_shape, :too_complex? + assert_predicate frozen_shape, :complex? assert_predicate frozen_shape, :shape_frozen? assert_equal 3, tc.very_unique @@ -700,7 +700,7 @@ class TooComplex end; end - def test_object_id_transition_too_complex + def test_object_id_transition_complex assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; obj = Object.new @@ -724,7 +724,7 @@ class Hi; end end; end - def test_too_complex_and_frozen_and_object_id + def test_complex_and_frozen_and_object_id assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; $VERBOSE = nil @@ -740,12 +740,12 @@ class TooComplex tc.instance_variable_set(:"@very_unique", 3) shape = RubyVM::Shape.of(tc) - assert_predicate shape, :too_complex? + assert_predicate shape, :complex? refute_predicate shape, :shape_frozen? tc.freeze frozen_shape = RubyVM::Shape.of(tc) refute_equal shape.id, frozen_shape.id - assert_predicate frozen_shape, :too_complex? + assert_predicate frozen_shape, :complex? assert_predicate frozen_shape, :shape_frozen? refute_predicate frozen_shape, :has_object_id? @@ -753,7 +753,7 @@ class TooComplex id_shape = RubyVM::Shape.of(tc) refute_equal frozen_shape.id, id_shape.id - assert_predicate id_shape, :too_complex? + assert_predicate id_shape, :complex? assert_predicate id_shape, :has_object_id? assert_predicate id_shape, :shape_frozen? @@ -762,7 +762,7 @@ class TooComplex end; end - def test_too_complex_obj_ivar_ractor_share + def test_complex_obj_ivar_ractor_share assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; $VERBOSE = nil @@ -780,7 +780,7 @@ def test_too_complex_obj_ivar_ractor_share end; end - def test_too_complex_generic_ivar_ractor_share + def test_complex_generic_ivar_ractor_share assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") begin; $VERBOSE = nil @@ -803,7 +803,7 @@ def test_read_iv_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.a3_m end @@ -812,7 +812,7 @@ def test_read_method_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.a3_m assert_equal 3, tc.a3 end @@ -822,7 +822,7 @@ def test_write_method_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? tc.write_iv_method tc.write_iv_method assert_equal 12345, tc.a3_m @@ -834,7 +834,7 @@ def test_write_iv_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? tc.write_iv tc.write_iv assert_equal 12345, tc.a3_m @@ -846,7 +846,7 @@ def test_iv_read_via_method_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.a3_m assert_equal 3, tc.instance_variable_get(:@a3) end @@ -856,7 +856,7 @@ def test_delete_iv_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.a3_m # make sure IV is initialized assert tc.instance_variable_defined?(:@a3) @@ -870,7 +870,7 @@ def test_delete_iv_after_complex_and_object_id tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.a3_m # make sure IV is initialized assert tc.instance_variable_defined?(:@a3) @@ -885,7 +885,7 @@ def test_delete_iv_after_complex_and_freeze tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal 3, tc.a3_m # make sure IV is initialized assert tc.instance_variable_defined?(:@a3) @@ -902,7 +902,7 @@ def test_delete_undefined_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? refute tc.instance_variable_defined?(:@a3) assert_raise(NameError) do @@ -996,11 +996,11 @@ def test_freeze_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? tc.freeze assert_raise(FrozenError) { tc.a3_m } # doesn't transition to frozen shape in this case - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? end def test_read_undefined_iv_after_complex @@ -1008,9 +1008,9 @@ def test_read_undefined_iv_after_complex tc = TooComplex.new tc.send("a#{RubyVM::Shape::SHAPE_MAX_VARIATIONS}_m") - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? assert_equal nil, tc.iv_not_defined - assert_predicate RubyVM::Shape.of(tc), :too_complex? + assert_predicate RubyVM::Shape.of(tc), :complex? end def test_shape_order @@ -1128,7 +1128,7 @@ def test_duplicating_objects assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) end - def test_duplicating_too_complex_objects_memory_leak + def test_duplicating_complex_objects_memory_leak assert_no_memory_leak([], "#{<<~'begin;'}", "#{<<~'end;'}", "[Bug #20162]", rss: true) RubyVM::Shape.exhaust_shapes @@ -1226,52 +1226,52 @@ def ensure_complex end end - def assert_too_complex_during_delete(obj) + def assert_complex_during_delete(obj) obj.instance_variable_set("@___#{SecureRandom.hex}", 1) (RubyVM::Shape::SHAPE_MAX_VARIATIONS * 2).times do |i| obj.instance_variable_set("@ivar#{i}", i) end - refute_predicate RubyVM::Shape.of(obj), :too_complex? + refute_predicate RubyVM::Shape.of(obj), :complex? (RubyVM::Shape::SHAPE_MAX_VARIATIONS * 2).times do |i| obj.remove_instance_variable("@ivar#{i}") end - assert_predicate RubyVM::Shape.of(obj), :too_complex? + assert_predicate RubyVM::Shape.of(obj), :complex? end - def test_object_too_complex_during_delete - assert_too_complex_during_delete(Class.new.new) + def test_object_complex_during_delete + assert_complex_during_delete(Class.new.new) end - def test_class_too_complex_during_delete - assert_too_complex_during_delete(Module.new) + def test_class_complex_during_delete + assert_complex_during_delete(Module.new) end - def test_generic_too_complex_during_delete - assert_too_complex_during_delete(Class.new(Array).new) + def test_generic_complex_during_delete + assert_complex_during_delete(Class.new(Array).new) end - def assert_too_complex_max_fields(obj) + def assert_complex_max_fields(obj) extra_fields = RubyVM::Shape::SHAPE_MAX_FIELDS - obj.instance_variables.size extra_fields.times do |i| obj.instance_variable_set("@camel_ivar#{i}", i) end - refute_predicate RubyVM::Shape.of(obj), :too_complex? + refute_predicate RubyVM::Shape.of(obj), :complex? obj.instance_variable_set("@camel_straw", true) - assert_predicate RubyVM::Shape.of(obj), :too_complex? + assert_predicate RubyVM::Shape.of(obj), :complex? end def test_max_fields_complex - assert_too_complex_max_fields(Class.new(Object).new) + assert_complex_max_fields(Class.new(Object).new) end def test_generic_max_fields_complex - assert_too_complex_max_fields(Class.new(Array).new) + assert_complex_max_fields(Class.new(Array).new) end def test_class_max_fields_complex - assert_too_complex_max_fields(Class.new(Module).new) + assert_complex_max_fields(Class.new(Module).new) end def test_max_initial_fields @@ -1284,9 +1284,9 @@ def initialize(init = false) end end RUBY - assert_predicate RubyVM::Shape.of(klass.new), :too_complex? - assert_predicate RubyVM::Shape.of(klass.new.dup), :too_complex? - assert_predicate RubyVM::Shape.of(klass.new(true)), :too_complex? - assert_predicate RubyVM::Shape.of(klass.new(true).dup), :too_complex? + assert_predicate RubyVM::Shape.of(klass.new), :complex? + assert_predicate RubyVM::Shape.of(klass.new.dup), :complex? + assert_predicate RubyVM::Shape.of(klass.new(true)), :complex? + assert_predicate RubyVM::Shape.of(klass.new(true).dup), :complex? end end if defined?(RubyVM::Shape) diff --git a/variable.c b/variable.c index 9a50550fb16f7c..f8057b6d5fcdb5 100644 --- a/variable.c +++ b/variable.c @@ -1441,7 +1441,7 @@ rb_obj_field_get(VALUE obj, shape_id_t target_shape_id) break; } - if (UNLIKELY(rb_shape_too_complex_p(target_shape_id))) { + if (UNLIKELY(rb_shape_complex_p(target_shape_id))) { st_table *fields_hash = rb_imemo_fields_complex_tbl(fields_obj); VALUE value = Qundef; st_lookup(fields_hash, RSHAPE_EDGE_NAME(target_shape_id), &value); @@ -1494,7 +1494,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj); - if (UNLIKELY(rb_shape_too_complex_p(shape_id))) { + if (UNLIKELY(rb_shape_complex_p(shape_id))) { st_table *iv_table = rb_imemo_fields_complex_tbl(fields_obj); VALUE val; if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) { @@ -1579,9 +1579,9 @@ void rb_obj_copy_fields_to_hash_table(VALUE obj, st_table *table); static VALUE imemo_fields_complex_from_obj(VALUE owner, VALUE source_fields_obj, shape_id_t shape_id); static shape_id_t -obj_transition_too_complex(VALUE obj, st_table *table) +obj_transition_complex(VALUE obj, st_table *table) { - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); shape_id_t shape_id = rb_obj_shape_transition_complex(obj); switch (BUILTIN_TYPE(obj)) { @@ -1622,28 +1622,28 @@ obj_transition_too_complex(VALUE obj, st_table *table) static shape_id_t rb_evict_fields_to_hash(VALUE obj) { - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); st_table *table = st_init_numtable_with_size(RSHAPE_LEN(RBASIC_SHAPE_ID(obj))); rb_obj_copy_fields_to_hash_table(obj, table); - shape_id_t new_shape_id = obj_transition_too_complex(obj, table); + shape_id_t new_shape_id = obj_transition_complex(obj, table); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(rb_shape_obj_complex_p(obj)); return new_shape_id; } void rb_evict_ivars_to_hash(VALUE obj) { - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); st_table *table = st_init_numtable_with_size(rb_ivar_count(obj)); // Evacuate all previous values from shape into id_table rb_obj_copy_ivs_to_hash_table(obj, table); - obj_transition_too_complex(obj, table); + obj_transition_complex(obj, table); - RUBY_ASSERT(rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(rb_shape_obj_complex_p(obj)); } static VALUE @@ -1688,8 +1688,8 @@ rb_ivar_delete(VALUE obj, ID id, VALUE undef) shape_id_t removed_shape_id; shape_id_t next_shape_id = rb_obj_shape_transition_remove_ivar(fields_obj, id, &removed_shape_id); - if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) { - if (UNLIKELY(!rb_shape_too_complex_p(old_shape_id))) { + if (UNLIKELY(rb_shape_complex_p(next_shape_id))) { + if (UNLIKELY(!rb_shape_complex_p(old_shape_id))) { if (type == T_OBJECT) { rb_evict_fields_to_hash(obj); } @@ -1766,19 +1766,19 @@ rb_attr_delete(VALUE obj, ID id) } void -rb_obj_init_too_complex(VALUE obj, st_table *table) +rb_obj_init_complex(VALUE obj, st_table *table) { // This method is meant to be called on newly allocated object. RUBY_ASSERT(rb_shape_canonical_p(RBASIC_SHAPE_ID(obj))); RUBY_ASSERT(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)) == 0); - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { st_table *old_table = ROBJECT_FIELDS_HASH(obj); ROBJECT_SET_FIELDS_HASH(obj, table); if (old_table) st_free_table(old_table); } else { - obj_transition_too_complex(obj, table); + obj_transition_complex(obj, table); } } @@ -1833,8 +1833,8 @@ imemo_fields_set(VALUE owner, VALUE fields_obj, shape_id_t target_shape_id, ID f const VALUE original_fields_obj = fields_obj; shape_id_t current_shape_id = fields_obj ? RBASIC_SHAPE_ID(fields_obj) : ROOT_SHAPE_ID; - if (UNLIKELY(rb_shape_too_complex_p(target_shape_id))) { - if (rb_shape_too_complex_p(current_shape_id)) { + if (UNLIKELY(rb_shape_complex_p(target_shape_id))) { + if (rb_shape_complex_p(current_shape_id)) { if (concurrent) { // In multi-ractor case, we must always work on a copy because // even if the field already exist, inserting in a st_table may @@ -1883,7 +1883,7 @@ generic_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE va VALUE fields_obj = imemo_fields_set(obj, original_fields_obj, target_shape_id, field_name, val, false); rb_obj_set_fields(obj, fields_obj, field_name, original_fields_obj); - return rb_shape_too_complex_p(target_shape_id) ? ATTR_INDEX_NOT_SET : RSHAPE_INDEX(target_shape_id); + return rb_shape_complex_p(target_shape_id) ? ATTR_INDEX_NOT_SET : RSHAPE_INDEX(target_shape_id); } static shape_id_t @@ -1893,7 +1893,7 @@ generic_shape_ivar(VALUE obj, ID id, bool *new_ivar_out) shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj); shape_id_t target_shape_id = current_shape_id; - if (!rb_shape_too_complex_p(current_shape_id)) { + if (!rb_shape_complex_p(current_shape_id)) { if (!rb_shape_find_ivar(current_shape_id, id, &target_shape_id)) { new_ivar = true; target_shape_id = rb_obj_shape_transition_add_ivar(obj, id); @@ -1915,7 +1915,7 @@ generic_ivar_set(VALUE obj, ID id, VALUE val) void rb_ensure_iv_list_size(VALUE obj, uint32_t current_len, uint32_t new_capacity) { - RUBY_ASSERT(!rb_shape_obj_too_complex_p(obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { SIZED_REALLOC_N(ROBJECT(obj)->as.heap.fields, VALUE, new_capacity, current_len); @@ -1955,8 +1955,8 @@ obj_field_set(VALUE obj, shape_id_t target_shape_id, ID field_name, VALUE val) { shape_id_t current_shape_id = RBASIC_SHAPE_ID(obj); - if (UNLIKELY(rb_shape_too_complex_p(target_shape_id))) { - if (UNLIKELY(!rb_shape_too_complex_p(current_shape_id))) { + if (UNLIKELY(rb_shape_complex_p(target_shape_id))) { + if (UNLIKELY(!rb_shape_complex_p(current_shape_id))) { current_shape_id = rb_evict_fields_to_hash(obj); } @@ -2104,7 +2104,7 @@ ivar_defined0(VALUE obj, ID id) { attr_index_t index; - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { VALUE idx; st_table *table = NULL; switch (BUILTIN_TYPE(obj)) { @@ -2184,12 +2184,12 @@ iterate_over_shapes_callback(shape_id_t shape_id, void *data) VALUE *fields; switch (BUILTIN_TYPE(itr_data->obj)) { case T_OBJECT: - RUBY_ASSERT(!rb_shape_obj_too_complex_p(itr_data->obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(itr_data->obj)); fields = ROBJECT_FIELDS(itr_data->obj); break; case T_IMEMO: RUBY_ASSERT(IMEMO_TYPE_P(itr_data->obj, imemo_fields)); - RUBY_ASSERT(!rb_shape_obj_too_complex_p(itr_data->obj)); + RUBY_ASSERT(!rb_shape_obj_complex_p(itr_data->obj)); fields = rb_imemo_fields_ptr(itr_data->obj); break; @@ -2238,7 +2238,7 @@ obj_fields_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg, b }; shape_id_t shape_id = RBASIC_SHAPE_ID(obj); - if (rb_shape_too_complex_p(shape_id)) { + if (rb_shape_complex_p(shape_id)) { st_foreach_safe(ROBJECT_FIELDS_HASH(obj), each_hash_iv, (st_data_t)&itr_data); } else { @@ -2261,7 +2261,7 @@ imemo_fields_each(VALUE fields_obj, rb_ivar_foreach_callback_func *func, st_data }; shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj); - if (rb_shape_too_complex_p(shape_id)) { + if (rb_shape_complex_p(shape_id)) { rb_st_foreach(rb_imemo_fields_complex_tbl(fields_obj), each_hash_iv, (st_data_t)&itr_data); } else { @@ -2291,7 +2291,7 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj) goto clear; } - if (rb_shape_too_complex_p(src_shape_id)) { + if (rb_shape_complex_p(src_shape_id)) { rb_shape_copy_complex_ivars(dest, obj, src_shape_id, rb_imemo_fields_complex_tbl(fields_obj)); return; } @@ -2303,10 +2303,10 @@ rb_copy_generic_ivar(VALUE dest, VALUE obj) RUBY_ASSERT(RSHAPE_TYPE_P(initial_shape_id, SHAPE_ROOT)); dest_shape_id = rb_shape_rebuild(initial_shape_id, src_shape_id); - if (UNLIKELY(rb_shape_too_complex_p(dest_shape_id))) { + if (UNLIKELY(rb_shape_complex_p(dest_shape_id))) { st_table *table = rb_st_init_numtable_with_size(src_num_ivs); rb_obj_copy_ivs_to_hash_table(obj, table); - rb_obj_init_too_complex(dest, table); + rb_obj_init_complex(dest, table); return; } } @@ -2437,7 +2437,7 @@ rb_ivar_count(VALUE obj) if (!fields_obj) { return 0; } - if (rb_shape_obj_too_complex_p(fields_obj)) { + if (rb_shape_obj_complex_p(fields_obj)) { iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj)); } else { @@ -2449,7 +2449,7 @@ rb_ivar_count(VALUE obj) case T_IMEMO: RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields)); - if (rb_shape_obj_too_complex_p(obj)) { + if (rb_shape_obj_complex_p(obj)) { iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(obj)); } else { @@ -2461,7 +2461,7 @@ rb_ivar_count(VALUE obj) { VALUE fields_obj = rb_obj_fields_no_ractor_check(obj); if (fields_obj) { - if (rb_shape_obj_too_complex_p(fields_obj)) { + if (rb_shape_obj_complex_p(fields_obj)) { iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj)); } else { @@ -4610,17 +4610,17 @@ class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool conc fields_obj = original_fields_obj ? original_fields_obj : rb_imemo_fields_new(klass, ROOT_SHAPE_ID, true); shape_id_t current_shape_id = RBASIC_SHAPE_ID(fields_obj); - shape_id_t next_shape_id = current_shape_id; // for too_complex - if (UNLIKELY(rb_shape_too_complex_p(current_shape_id))) { - goto too_complex; + shape_id_t next_shape_id = current_shape_id; // for complex + if (UNLIKELY(rb_shape_complex_p(current_shape_id))) { + goto complex; } bool new_ivar; next_shape_id = generic_shape_ivar(fields_obj, id, &new_ivar); - if (UNLIKELY(rb_shape_too_complex_p(next_shape_id))) { + if (UNLIKELY(rb_shape_complex_p(next_shape_id))) { fields_obj = imemo_fields_complex_from_obj(klass, fields_obj, next_shape_id); - goto too_complex; + goto complex; } attr_index_t index = RSHAPE_INDEX(next_shape_id); @@ -4653,7 +4653,7 @@ class_fields_ivar_set(VALUE klass, VALUE fields_obj, ID id, VALUE val, bool conc *new_ivar_out = new_ivar; return index; -too_complex: +complex: { if (concurrent && fields_obj == original_fields_obj) { // In multi-ractor case, we must always work on a copy because diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 966a7a93eab35c..24835cda265cca 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1329,7 +1329,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call } #endif - if (UNLIKELY(rb_shape_too_complex_p(shape_id))) { + if (UNLIKELY(rb_shape_complex_p(shape_id))) { st_table *table = (st_table *)ivar_list; RUBY_ASSERT(table); @@ -1343,7 +1343,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call shape_id_t previous_cached_offset = cache.shape_offset; if (rb_shape_get_iv_index_with_hint(shape_id, id, &cache.index, &cache.shape_offset)) { if (cache.shape_offset != previous_cached_offset) { - RUBY_ASSERT(!rb_shape_too_complex_p(cache.shape_offset)); + RUBY_ASSERT(!rb_shape_complex_p(cache.shape_offset)); RUBY_ASSERT(cache.shape_offset != INVALID_SHAPE_ID); uint64_t packed_cache = rb_getivar_cache_pack(cache.shape_offset, cache.index); @@ -1400,7 +1400,7 @@ vm_setivar_slowpath(VALUE obj, ID id, VALUE val, const rb_iseq_t *iseq, IVC ic, attr_index_t index = rb_ivar_set_index(obj, id, val); shape_id_t next_shape_id = RBASIC_SHAPE_ID(obj); - if (!rb_shape_too_complex_p(next_shape_id)) { + if (!rb_shape_complex_p(next_shape_id)) { uint64_t packed_cache = rb_setivar_cache_pack(RSHAPE_OFFSET(previous_shape_id), RSHAPE_OFFSET(next_shape_id), index); vm_cache_attr_index_set(is_attr, ic, cc, packed_cache); } diff --git a/yjit.c b/yjit.c index 2e7216a1915406..70477230232ab0 100644 --- a/yjit.c +++ b/yjit.c @@ -434,9 +434,9 @@ rb_object_shape_count(void) } bool -rb_yjit_shape_obj_too_complex_p(VALUE obj) +rb_yjit_shape_obj_complex_p(VALUE obj) { - return rb_shape_obj_too_complex_p(obj); + return rb_shape_obj_complex_p(obj); } attr_index_t diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index fd99d529041077..73e9c847465ea6 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -87,7 +87,7 @@ fn main() { .allowlist_function("rb_shape_id_offset") .allowlist_function("rb_shape_get_iv_index") .allowlist_function("rb_shape_transition_add_ivar_no_warnings") - .allowlist_function("rb_yjit_shape_obj_too_complex_p") + .allowlist_function("rb_yjit_shape_obj_complex_p") .allowlist_function("rb_yjit_shape_capacity") .allowlist_function("rb_yjit_shape_index") .allowlist_var("SHAPE_ID_NUM_BITS") @@ -277,7 +277,7 @@ fn main() { .allowlist_function("rb_jit_str_concat_codepoint") .allowlist_type("rstring_offsets") .allowlist_function("rb_assert_holding_vm_lock") - .allowlist_function("rb_jit_shape_too_complex_p") + .allowlist_function("rb_jit_shape_complex_p") .allowlist_function("rb_jit_multi_ractor_p") .allowlist_function("rb_jit_vm_lock_then_barrier") .allowlist_function("rb_jit_vm_unlock") diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 69f5e7221850bb..98b659721c6cf7 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -2950,7 +2950,7 @@ fn gen_get_ivar( // NOTE: This assumes T_OBJECT can't ever have the same shape_id as any other type. // too-complex shapes can't use index access, so we use rb_ivar_get for them too. - if !comptime_receiver.heap_object_p() || comptime_receiver.shape_too_complex() || megamorphic { + if !comptime_receiver.heap_object_p() || comptime_receiver.shape_complex() || megamorphic { // General case. Call rb_ivar_get(). // VALUE rb_ivar_get(VALUE obj, ID id) asm_comment!(asm, "call rb_ivar_get()"); @@ -3168,8 +3168,8 @@ fn gen_set_ivar( } // Get the iv index - let shape_too_complex = comptime_receiver.shape_too_complex(); - let ivar_index = if !comptime_receiver.special_const_p() && !shape_too_complex { + let shape_complex = comptime_receiver.shape_complex(); + let ivar_index = if !comptime_receiver.special_const_p() && !shape_complex { let shape_id = comptime_receiver.shape_id_of(); let mut ivar_index: attr_index_t = 0; if unsafe { rb_shape_get_iv_index(shape_id, ivar_name, &mut ivar_index) } { @@ -3182,23 +3182,23 @@ fn gen_set_ivar( }; // The current shape doesn't contain this iv, we need to transition to another shape. - let mut new_shape_too_complex = false; - let new_shape = if !shape_too_complex && receiver_t_object && ivar_index.is_none() { + let mut new_shape_complex = false; + let new_shape = if !shape_complex && receiver_t_object && ivar_index.is_none() { let current_shape_id = comptime_receiver.shape_id_of(); // We don't need to check about imemo_fields here because we're definitely looking at a T_OBJECT. let klass = unsafe { rb_obj_class(comptime_receiver) }; let next_shape_id = unsafe { rb_shape_transition_add_ivar_no_warnings(current_shape_id, ivar_name, klass) }; // If the VM ran out of shapes, or this class generated too many leaf, - // it may be de-optimized into OBJ_TOO_COMPLEX_SHAPE (hash-table). - new_shape_too_complex = unsafe { rb_jit_shape_too_complex_p(next_shape_id) }; - if new_shape_too_complex { + // it may be de-optimized into OBJ_COMPLEX_SHAPE (hash-table). + new_shape_complex = unsafe { rb_jit_shape_complex_p(next_shape_id) }; + if new_shape_complex { Some((next_shape_id, None, 0_usize)) } else { let current_capacity = unsafe { rb_yjit_shape_capacity(current_shape_id) }; let next_capacity = unsafe { rb_yjit_shape_capacity(next_shape_id) }; - // If the new shape has a different capacity, or is TOO_COMPLEX, we'll have to + // If the new shape has a different capacity, or is COMPLEX, we'll have to // reallocate it. let needs_extension = next_capacity != current_capacity; @@ -3218,7 +3218,7 @@ fn gen_set_ivar( // If the receiver isn't a T_OBJECT, then just write out the IV write as a function call. // too-complex shapes can't use index access, so we use rb_ivar_get for them too. - if !receiver_t_object || shape_too_complex || new_shape_too_complex || megamorphic { + if !receiver_t_object || shape_complex || new_shape_complex || megamorphic { // The function could raise FrozenError. // Note that this modifies REG_SP, which is why we do it first jit_prepare_non_leaf_call(jit, asm); @@ -3435,7 +3435,7 @@ fn gen_definedivar( // Specialize base on compile time values let comptime_receiver = jit.peek_at_self(); - if comptime_receiver.special_const_p() || comptime_receiver.shape_too_complex() || asm.ctx.get_chain_depth() >= GET_IVAR_MAX_DEPTH { + if comptime_receiver.special_const_p() || comptime_receiver.shape_complex() || asm.ctx.get_chain_depth() >= GET_IVAR_MAX_DEPTH { // Fall back to calling rb_ivar_defined // Save the PC and SP because the callee may allocate diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 62fc9d8f06a1be..8b99a8154a665f 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -444,8 +444,8 @@ impl VALUE { unsafe { rb_obj_frozen_p(self) != VALUE(0) } } - pub fn shape_too_complex(self) -> bool { - unsafe { rb_yjit_shape_obj_too_complex_p(self) } + pub fn shape_complex(self) -> bool { + unsafe { rb_yjit_shape_obj_complex_p(self) } } pub fn shape_id_of(self) -> u32 { diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 608b8ba93455d2..0ce6485e0d471c 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -1205,7 +1205,7 @@ extern "C" { line: ::std::os::raw::c_int, ); pub fn rb_object_shape_count() -> VALUE; - pub fn rb_yjit_shape_obj_too_complex_p(obj: VALUE) -> bool; + pub fn rb_yjit_shape_obj_complex_p(obj: VALUE) -> bool; pub fn rb_yjit_shape_capacity(shape_id: shape_id_t) -> attr_index_t; pub fn rb_yjit_shape_index(shape_id: shape_id_t) -> attr_index_t; pub fn rb_yjit_sendish_sp_pops(ci: *const rb_callinfo) -> usize; @@ -1308,7 +1308,7 @@ extern "C" { pub fn rb_jit_array_len(a: VALUE) -> ::std::os::raw::c_long; pub fn rb_set_cfp_pc(cfp: *mut rb_control_frame_struct, pc: *const VALUE); pub fn rb_set_cfp_sp(cfp: *mut rb_control_frame_struct, sp: *mut VALUE); - pub fn rb_jit_shape_too_complex_p(shape_id: shape_id_t) -> bool; + pub fn rb_jit_shape_complex_p(shape_id: shape_id_t) -> bool; pub fn rb_jit_multi_ractor_p() -> bool; pub fn rb_jit_vm_lock_then_barrier( recursive_lock_level: *mut ::std::os::raw::c_uint, diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 8261f18ddbf118..f85839a5a2fb0d 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -316,7 +316,7 @@ fn main() { .allowlist_type("zjit_struct_offsets") .allowlist_var("ZJIT_JIT_RETURN_POISON") .allowlist_function("rb_assert_holding_vm_lock") - .allowlist_function("rb_jit_shape_too_complex_p") + .allowlist_function("rb_jit_shape_complex_p") .allowlist_function("rb_jit_multi_ractor_p") .allowlist_function("rb_jit_class_fields_embedded_p") .allowlist_function("rb_jit_typed_data_p") diff --git a/zjit/src/codegen_tests.rs b/zjit/src/codegen_tests.rs index be8465b91f60c1..fec8179bcb1fcd 100644 --- a/zjit/src/codegen_tests.rs +++ b/zjit/src/codegen_tests.rs @@ -5492,7 +5492,7 @@ fn test_tracepoint_return_value_with_rescue() { // Too-complex shapes use hash tables for ivar storage, and rb_shape_get_iv_index() // doesn't work for them. The polymorphic path must fall through to GetIvar instead. #[test] -fn test_polymorphic_getivar_too_complex_shape() { +fn test_polymorphic_getivar_complex_shape() { // Need threshold >= 3 so both shapes get profiled before compilation set_call_threshold(3); assert_snapshot!(inspect(r#" diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index dfaede11bd5c68..5cd5924b62c2b9 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -274,8 +274,8 @@ impl ShapeId { self != INVALID_SHAPE_ID } - pub fn is_too_complex(self) -> bool { - unsafe { rb_jit_shape_too_complex_p(self.0) } + pub fn is_complex(self) -> bool { + unsafe { rb_jit_shape_complex_p(self.0) } } pub fn is_frozen(self) -> bool { diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index ab27b2890f0e46..23db792a52b06a 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -1485,7 +1485,7 @@ pub type vm_frame_env_flags = u32; pub type attr_index_t = u8; pub type shape_id_t = u32; pub const SHAPE_ID_HEAP_INDEX_MASK: shape_id_fl_type = 7864320; -pub const SHAPE_ID_FL_TOO_COMPLEX: shape_id_fl_type = 8388608; +pub const SHAPE_ID_FL_COMPLEX: shape_id_fl_type = 8388608; pub const SHAPE_ID_FL_FROZEN: shape_id_fl_type = 16777216; pub const SHAPE_ID_FL_HAS_OBJECT_ID: shape_id_fl_type = 33554432; pub const SHAPE_ID_FL_NON_CANONICAL_MASK: shape_id_fl_type = 50331648; @@ -2282,7 +2282,7 @@ unsafe extern "C" { pub fn rb_jit_array_len(a: VALUE) -> ::std::os::raw::c_long; pub fn rb_set_cfp_pc(cfp: *mut rb_control_frame_struct, pc: *const VALUE); pub fn rb_set_cfp_sp(cfp: *mut rb_control_frame_struct, sp: *mut VALUE); - pub fn rb_jit_shape_too_complex_p(shape_id: shape_id_t) -> bool; + pub fn rb_jit_shape_complex_p(shape_id: shape_id_t) -> bool; pub fn rb_jit_multi_ractor_p() -> bool; pub fn rb_jit_class_fields_embedded_p(klass: VALUE) -> bool; pub fn rb_jit_typed_data_fields_embedded_p(obj: VALUE) -> bool; diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 5dc456a2cc0297..4d006af1ab2c96 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -4508,7 +4508,7 @@ impl Function { fn load_ivar(&mut self, block: BlockId, self_val: InsnId, recv_type: ProfiledType, id: ID, state: InsnId) -> InsnId { // Too-complex shapes use hash tables; rb_shape_get_iv_index doesn't support them. // Callers must filter these out before calling load_ivar. - assert!(!recv_type.shape().is_too_complex(), "load_ivar called with too-complex shape"); + assert!(!recv_type.shape().is_complex(), "load_ivar called with too-complex shape"); let mut ivar_index: attr_index_t = 0; if ! unsafe { rb_shape_get_iv_index(recv_type.shape().0, id, &mut ivar_index) } { // If there is no IVAR index, then the ivar was undefined when we @@ -4569,9 +4569,9 @@ impl Function { self.push_insn_id(block, insn_id); continue; } assert!(recv_type.shape().is_valid()); - if recv_type.shape().is_too_complex() { + if recv_type.shape().is_complex() { // too-complex shapes can't use index access - self.count(block, Counter::getivar_fallback_too_complex); + self.count(block, Counter::getivar_fallback_complex); self.push_insn_id(block, insn_id); continue; } if self.policy.no_side_exits { @@ -4605,9 +4605,9 @@ impl Function { self.count(block, Counter::definedivar_fallback_not_t_object); self.push_insn_id(block, insn_id); continue; } - if recv_type.shape().is_too_complex() { + if recv_type.shape().is_complex() { // too-complex shapes can't use index access - self.count(block, Counter::definedivar_fallback_too_complex); + self.count(block, Counter::definedivar_fallback_complex); self.push_insn_id(block, insn_id); continue; } let self_val = self.load_ivar_guard_type(block, self_val, recv_type, state); @@ -4642,9 +4642,9 @@ impl Function { self.count(block, Counter::setivar_fallback_not_t_object); self.push_insn_id(block, insn_id); continue; } - if recv_type.shape().is_too_complex() { + if recv_type.shape().is_complex() { // too-complex shapes can't use index access - self.count(block, Counter::setivar_fallback_too_complex); + self.count(block, Counter::setivar_fallback_complex); self.push_insn_id(block, insn_id); continue; } if recv_type.shape().is_frozen() { @@ -4662,18 +4662,18 @@ impl Function { assert!(recv_type.flags().is_t_object()); next_shape_id = ShapeId(unsafe { rb_shape_transition_add_ivar_no_warnings(current_shape_id.0, id, class) }); // If the VM ran out of shapes, or this class generated too many leaf, - // it may be de-optimized into OBJ_TOO_COMPLEX_SHAPE (hash-table). - let new_shape_too_complex = unsafe { rb_jit_shape_too_complex_p(next_shape_id.0) }; + // it may be de-optimized into OBJ_COMPLEX_SHAPE (hash-table). + let new_shape_complex = unsafe { rb_jit_shape_complex_p(next_shape_id.0) }; // TODO(max): Is it OK to bail out here after making a shape transition? - if new_shape_too_complex { - self.count(block, Counter::setivar_fallback_new_shape_too_complex); + if new_shape_complex { + self.count(block, Counter::setivar_fallback_new_shape_complex); self.push_insn_id(block, insn_id); continue; } let ivar_result = unsafe { rb_shape_get_iv_index(next_shape_id.0, id, &mut ivar_index) }; assert!(ivar_result, "New shape must have the ivar index"); let current_capacity = unsafe { rb_jit_shape_capacity(current_shape_id.0) }; let next_capacity = unsafe { rb_jit_shape_capacity(next_shape_id.0) }; - // If the new shape has a different capacity, or is TOO_COMPLEX, we'll have to + // If the new shape has a different capacity, or is COMPLEX, we'll have to // reallocate it. let needs_extension = next_capacity != current_capacity; if needs_extension { @@ -8283,7 +8283,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // Too-complex shapes use hash tables for ivars; // rb_shape_get_iv_index doesn't work for them. // Let the fallthrough GetIvar handle these. - if expected_shape.is_too_complex() { continue; } + if expected_shape.is_complex() { continue; } if seen_shape_and_flags.contains(&expected_rbasic_flags) { continue; } seen_shape_and_flags.push(expected_rbasic_flags); let rbasic_flags_mask = fun.push_insn(block, Insn::Const { val: Const::CUInt64(rbasic_flags_mask) }); diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 525755d03813c3..41fcfb16986ed4 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -5795,7 +5795,7 @@ mod hir_opt_tests { } #[test] - fn test_dont_specialize_setivar_when_next_shape_is_too_complex() { + fn test_dont_specialize_setivar_when_next_shape_is_complex() { eval(r#" class AboutToBeTooComplex def test = @abc = 5 @@ -8243,7 +8243,7 @@ mod hir_opt_tests { } #[test] - fn test_dont_optimize_getivar_with_too_complex_shape() { + fn test_dont_optimize_getivar_with_complex_shape() { eval(r#" class C attr_accessor :foo diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 2e43706e855fd0..412dd045bfcef1 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -310,10 +310,10 @@ make_counters! { setivar_fallback_not_monomorphic, setivar_fallback_immediate, setivar_fallback_not_t_object, - setivar_fallback_too_complex, + setivar_fallback_complex, setivar_fallback_frozen, setivar_fallback_shape_transition, - setivar_fallback_new_shape_too_complex, + setivar_fallback_new_shape_complex, setivar_fallback_new_shape_needs_extension, } @@ -323,7 +323,7 @@ make_counters! { getivar_fallback_not_monomorphic, getivar_fallback_immediate, getivar_fallback_not_t_object, - getivar_fallback_too_complex, + getivar_fallback_complex, } // Ivar fallback counters that are summed as dynamic_definedivar_count @@ -332,7 +332,7 @@ make_counters! { definedivar_fallback_not_monomorphic, definedivar_fallback_immediate, definedivar_fallback_not_t_object, - definedivar_fallback_too_complex, + definedivar_fallback_complex, } // compile_error_: Compile error reasons From deceb9f62ed9863ba877d500ee20369f52baa98a Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 6 May 2026 08:45:20 +0200 Subject: [PATCH 3/4] Rename `rb_shape_obj_*` into `rb_obj_shape_*` For consistency. --- ext/objspace/objspace_dump.c | 2 +- gc.c | 24 ++++++++++++------------ imemo.c | 6 +++--- internal/class.h | 2 +- ractor.c | 4 ++-- shape.h | 20 ++++++++++---------- string.c | 4 ++-- struct.c | 2 +- variable.c | 30 +++++++++++++++--------------- yjit.c | 2 +- 10 files changed, 48 insertions(+), 48 deletions(-) diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index 446a3024fee1ec..b644c489b88579 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -598,7 +598,7 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, ", \"ivars\":"); dump_append_lu(dc, ROBJECT_FIELDS_COUNT(obj)); - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { dump_append(dc, ", \"complex_shape\":true"); } break; diff --git a/gc.c b/gc.c index 6b0ea041abf34b..fa903752bf111e 100644 --- a/gc.c +++ b/gc.c @@ -1341,7 +1341,7 @@ rb_gc_imemo_needs_cleanup_p(VALUE obj) return ((rb_imemo_tmpbuf_t *)obj)->ptr != NULL; case imemo_fields: - return FL_TEST_RAW(obj, OBJ_FIELD_HEAP) || (id2ref_tbl && rb_shape_obj_has_id(obj)); + return FL_TEST_RAW(obj, OBJ_FIELD_HEAP) || (id2ref_tbl && rb_obj_shape_has_id(obj)); } UNREACHABLE_RETURN(true); } @@ -1543,7 +1543,7 @@ rb_gc_obj_free(void *objspace, VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { RB_DEBUG_COUNTER_INC(obj_obj_complex); st_free_table(ROBJECT_FIELDS_HASH(obj)); } @@ -2179,7 +2179,7 @@ object_id0(VALUE obj) rb_obj_field_set(obj, object_id_shape_id, 0, id); RUBY_ASSERT(RBASIC_SHAPE_ID(obj) == object_id_shape_id); - RUBY_ASSERT(rb_shape_obj_has_id(obj)); + RUBY_ASSERT(rb_obj_shape_has_id(obj)); if (RB_UNLIKELY(id2ref_tbl)) { RB_VM_LOCKING() { @@ -2231,13 +2231,13 @@ build_id2ref_i(VALUE obj, void *data) break; case T_IMEMO: RUBY_ASSERT(!rb_objspace_garbage_object_p(obj)); - if (IMEMO_TYPE_P(obj, imemo_fields) && rb_shape_obj_has_id(obj)) { + if (IMEMO_TYPE_P(obj, imemo_fields) && rb_obj_shape_has_id(obj)) { st_insert(id2ref_tbl, rb_obj_id(obj), rb_imemo_fields_owner(obj)); } break; case T_OBJECT: RUBY_ASSERT(!rb_objspace_garbage_object_p(obj)); - if (rb_shape_obj_has_id(obj)) { + if (rb_obj_shape_has_id(obj)) { st_insert(id2ref_tbl, rb_obj_id(obj), obj); } break; @@ -2504,7 +2504,7 @@ rb_obj_id(VALUE obj) bool rb_obj_id_p(VALUE obj) { - return !RB_TYPE_P(obj, T_IMEMO) && rb_shape_obj_has_id(obj); + return !RB_TYPE_P(obj, T_IMEMO) && rb_obj_shape_has_id(obj); } /* @@ -2585,7 +2585,7 @@ rb_obj_memsize_of(VALUE obj) switch (BUILTIN_TYPE(obj)) { case T_OBJECT: if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { size += rb_st_memsize(ROBJECT_FIELDS_HASH(obj)); } else { @@ -3491,7 +3491,7 @@ rb_gc_mark_children(void *objspace, VALUE obj) case T_OBJECT: { uint32_t len; - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { gc_mark_tbl_no_pin(ROBJECT_FIELDS_HASH(obj)); len = ROBJECT_FIELDS_COUNT_COMPLEX(obj); } @@ -3549,7 +3549,7 @@ rb_gc_mark_children(void *objspace, VALUE obj) gc_mark_internal(ptr[i]); } - if (rb_shape_obj_has_fields(obj) && !FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) { + if (rb_obj_shape_has_fields(obj) && !FL_TEST_RAW(obj, RSTRUCT_GEN_FIELDS)) { gc_mark_internal(RSTRUCT_FIELDS_OBJ(obj)); } @@ -3582,7 +3582,7 @@ rb_gc_obj_optimal_size(VALUE obj) } case T_OBJECT: - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { return sizeof(struct RObject); } else { @@ -3849,7 +3849,7 @@ gc_ref_update_object(void *objspace, VALUE v) VALUE *ptr = ROBJECT_FIELDS(v); if (FL_TEST_RAW(v, ROBJECT_HEAP)) { - if (rb_shape_obj_complex_p(v)) { + if (rb_obj_shape_complex_p(v)) { gc_ref_update_table_values_only(ROBJECT_FIELDS_HASH(v)); return; } @@ -5037,7 +5037,7 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU case T_OBJECT: { if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { size_t hash_len = rb_st_table_size(ROBJECT_FIELDS_HASH(obj)); APPEND_F("(complex) len:%zu", hash_len); } diff --git a/imemo.c b/imemo.c index 18941407efa92d..f236194cd27377 100644 --- a/imemo.c +++ b/imemo.c @@ -205,7 +205,7 @@ rb_imemo_fields_clear(VALUE fields_obj) { // When replacing an imemo/fields by another one, we must clear // its shape so that gc.c:obj_free_object_id won't be called. - if (rb_shape_obj_complex_p(fields_obj)) { + if (rb_obj_shape_complex_p(fields_obj)) { RBASIC_SET_SHAPE_ID(fields_obj, ROOT_COMPLEX_SHAPE_ID); } else { @@ -259,7 +259,7 @@ rb_imemo_memsize(VALUE obj) case imemo_cvar_entry: break; case imemo_fields: - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table); } break; @@ -513,7 +513,7 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating) // imemo_fields can refer unshareable objects // even if the imemo_fields is shareable. - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { st_table *tbl = rb_imemo_fields_complex_tbl(obj); if (reference_updating) { rb_gc_ref_update_table_values_only(tbl); diff --git a/internal/class.h b/internal/class.h index 368e9cfc408462..8b66f50bc6a6cb 100644 --- a/internal/class.h +++ b/internal/class.h @@ -550,7 +550,7 @@ RCLASS_FIELDS_COUNT(VALUE obj) VALUE fields_obj = RCLASS_WRITABLE_FIELDS_OBJ(obj); if (fields_obj) { - if (rb_shape_obj_complex_p(fields_obj)) { + if (rb_obj_shape_complex_p(fields_obj)) { return (uint32_t)rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj)); } else { diff --git a/ractor.c b/ractor.c index 0176206afd23ca..8fe4b150445948 100644 --- a/ractor.c +++ b/ractor.c @@ -1809,7 +1809,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) if (UNLIKELY(rb_obj_gen_fields_p(obj))) { VALUE fields_obj = rb_obj_fields_no_ractor_check(obj); - if (UNLIKELY(rb_shape_obj_complex_p(obj))) { + if (UNLIKELY(rb_obj_shape_complex_p(obj))) { struct obj_traverse_replace_callback_data d = { .stop = false, .data = data, @@ -1846,7 +1846,7 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data) case T_OBJECT: { - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { struct obj_traverse_replace_callback_data d = { .stop = false, .data = data, diff --git a/shape.h b/shape.h index 78506a80e50074..a3763ba1c5faf6 100644 --- a/shape.h +++ b/shape.h @@ -215,7 +215,7 @@ rb_shape_complex_p(shape_id_t shape_id) } static inline bool -rb_shape_obj_complex_p(VALUE obj) +rb_obj_shape_complex_p(VALUE obj) { return !RB_SPECIAL_CONST_P(obj) && rb_shape_complex_p(RBASIC_SHAPE_ID(obj)); } @@ -322,7 +322,7 @@ ROBJECT_FIELDS_CAPACITY(VALUE obj) RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); // Asking for capacity doesn't make sense when the object is using // a hash table for storing instance variables - RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); return RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)); } @@ -330,7 +330,7 @@ static inline st_table * ROBJECT_FIELDS_HASH(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); - RUBY_ASSERT(rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(rb_obj_shape_complex_p(obj)); RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP)); return ROBJECT(obj)->as.hash; @@ -340,7 +340,7 @@ static inline void ROBJECT_SET_FIELDS_HASH(VALUE obj, st_table *tbl) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); - RUBY_ASSERT(rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(rb_obj_shape_complex_p(obj)); RUBY_ASSERT(FL_TEST_RAW(obj, ROBJECT_HEAP)); ROBJECT(obj)->as.hash = tbl; @@ -356,14 +356,14 @@ static inline uint32_t ROBJECT_FIELDS_COUNT_NOT_COMPLEX(VALUE obj) { RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT); - RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); return RSHAPE(RBASIC_SHAPE_ID(obj))->next_field_index; } static inline uint32_t ROBJECT_FIELDS_COUNT(VALUE obj) { - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { return ROBJECT_FIELDS_COUNT_COMPLEX(obj); } else { @@ -378,7 +378,7 @@ RBASIC_FIELDS_COUNT(VALUE obj) } static inline bool -rb_shape_obj_has_id(VALUE obj) +rb_obj_shape_has_id(VALUE obj) { return rb_shape_has_object_id(RBASIC_SHAPE_ID(obj)); } @@ -390,7 +390,7 @@ rb_shape_has_ivars(shape_id_t shape_id) } static inline bool -rb_shape_obj_has_ivars(VALUE obj) +rb_obj_shape_has_ivars(VALUE obj) { return rb_shape_has_ivars(RBASIC_SHAPE_ID(obj)); } @@ -402,7 +402,7 @@ rb_shape_has_fields(shape_id_t shape_id) } static inline bool -rb_shape_obj_has_fields(VALUE obj) +rb_obj_shape_has_fields(VALUE obj) { return rb_shape_has_fields(RBASIC_SHAPE_ID(obj)); } @@ -420,7 +420,7 @@ rb_obj_gen_fields_p(VALUE obj) default: break; } - return rb_shape_obj_has_fields(obj); + return rb_obj_shape_has_fields(obj); } static inline bool diff --git a/string.c b/string.c index 9c6cfb700a3a8d..dae7700887c4c4 100644 --- a/string.c +++ b/string.c @@ -370,7 +370,7 @@ static VALUE register_fstring(VALUE str, bool copy, bool force_precompute_hash); static inline bool BARE_STRING_P(VALUE str) { - return RBASIC_CLASS(str) == rb_cString && !rb_shape_obj_has_ivars(str); + return RBASIC_CLASS(str) == rb_cString && !rb_obj_shape_has_ivars(str); } static inline st_index_t @@ -547,7 +547,7 @@ fstring_concurrent_set_create(VALUE str, void *data) RUBY_ASSERT(RB_TYPE_P(str, T_STRING)); RUBY_ASSERT(OBJ_FROZEN(str)); RUBY_ASSERT(!FL_TEST_RAW(str, STR_FAKESTR)); - RUBY_ASSERT(!rb_shape_obj_has_ivars(str)); + RUBY_ASSERT(!rb_obj_shape_has_ivars(str)); RUBY_ASSERT(RBASIC_CLASS(str) == rb_cString); RUBY_ASSERT(!rb_objspace_garbage_object_p(str)); diff --git a/struct.c b/struct.c index e97c4632444640..5ac67dad2c3eb0 100644 --- a/struct.c +++ b/struct.c @@ -839,7 +839,7 @@ struct_alloc(VALUE klass) NEWOBJ_OF(st, struct RStruct, klass, flags, embedded_size); if (RCLASS_MAX_IV_COUNT(klass) == 0) { - if (!rb_shape_obj_has_fields((VALUE)st) + if (!rb_obj_shape_has_fields((VALUE)st) && embedded_size < rb_gc_obj_slot_size((VALUE)st)) { FL_UNSET_RAW((VALUE)st, RSTRUCT_GEN_FIELDS); RSTRUCT_SET_FIELDS_OBJ((VALUE)st, 0); diff --git a/variable.c b/variable.c index f8057b6d5fcdb5..253d175c7deee2 100644 --- a/variable.c +++ b/variable.c @@ -1281,7 +1281,7 @@ rb_obj_fields(VALUE obj, ID field_name) ivar_ractor_check(obj, field_name); VALUE fields_obj = 0; - if (rb_shape_obj_has_fields(obj)) { + if (rb_obj_shape_has_fields(obj)) { switch (BUILTIN_TYPE(obj)) { case T_DATA: if (LIKELY(RTYPEDDATA_P(obj))) { @@ -1581,7 +1581,7 @@ static VALUE imemo_fields_complex_from_obj(VALUE owner, VALUE source_fields_obj, static shape_id_t obj_transition_complex(VALUE obj, st_table *table) { - RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); shape_id_t shape_id = rb_obj_shape_transition_complex(obj); switch (BUILTIN_TYPE(obj)) { @@ -1622,20 +1622,20 @@ obj_transition_complex(VALUE obj, st_table *table) static shape_id_t rb_evict_fields_to_hash(VALUE obj) { - RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); st_table *table = st_init_numtable_with_size(RSHAPE_LEN(RBASIC_SHAPE_ID(obj))); rb_obj_copy_fields_to_hash_table(obj, table); shape_id_t new_shape_id = obj_transition_complex(obj, table); - RUBY_ASSERT(rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(rb_obj_shape_complex_p(obj)); return new_shape_id; } void rb_evict_ivars_to_hash(VALUE obj) { - RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); st_table *table = st_init_numtable_with_size(rb_ivar_count(obj)); @@ -1643,7 +1643,7 @@ rb_evict_ivars_to_hash(VALUE obj) rb_obj_copy_ivs_to_hash_table(obj, table); obj_transition_complex(obj, table); - RUBY_ASSERT(rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(rb_obj_shape_complex_p(obj)); } static VALUE @@ -1772,7 +1772,7 @@ rb_obj_init_complex(VALUE obj, st_table *table) RUBY_ASSERT(rb_shape_canonical_p(RBASIC_SHAPE_ID(obj))); RUBY_ASSERT(RSHAPE_LEN(RBASIC_SHAPE_ID(obj)) == 0); - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { st_table *old_table = ROBJECT_FIELDS_HASH(obj); ROBJECT_SET_FIELDS_HASH(obj, table); if (old_table) st_free_table(old_table); @@ -1915,7 +1915,7 @@ generic_ivar_set(VALUE obj, ID id, VALUE val) void rb_ensure_iv_list_size(VALUE obj, uint32_t current_len, uint32_t new_capacity) { - RUBY_ASSERT(!rb_shape_obj_complex_p(obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(obj)); if (FL_TEST_RAW(obj, ROBJECT_HEAP)) { SIZED_REALLOC_N(ROBJECT(obj)->as.heap.fields, VALUE, new_capacity, current_len); @@ -2104,7 +2104,7 @@ ivar_defined0(VALUE obj, ID id) { attr_index_t index; - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { VALUE idx; st_table *table = NULL; switch (BUILTIN_TYPE(obj)) { @@ -2184,12 +2184,12 @@ iterate_over_shapes_callback(shape_id_t shape_id, void *data) VALUE *fields; switch (BUILTIN_TYPE(itr_data->obj)) { case T_OBJECT: - RUBY_ASSERT(!rb_shape_obj_complex_p(itr_data->obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(itr_data->obj)); fields = ROBJECT_FIELDS(itr_data->obj); break; case T_IMEMO: RUBY_ASSERT(IMEMO_TYPE_P(itr_data->obj, imemo_fields)); - RUBY_ASSERT(!rb_shape_obj_complex_p(itr_data->obj)); + RUBY_ASSERT(!rb_obj_shape_complex_p(itr_data->obj)); fields = rb_imemo_fields_ptr(itr_data->obj); break; @@ -2437,7 +2437,7 @@ rb_ivar_count(VALUE obj) if (!fields_obj) { return 0; } - if (rb_shape_obj_complex_p(fields_obj)) { + if (rb_obj_shape_complex_p(fields_obj)) { iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj)); } else { @@ -2449,7 +2449,7 @@ rb_ivar_count(VALUE obj) case T_IMEMO: RUBY_ASSERT(IMEMO_TYPE_P(obj, imemo_fields)); - if (rb_shape_obj_complex_p(obj)) { + if (rb_obj_shape_complex_p(obj)) { iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(obj)); } else { @@ -2461,7 +2461,7 @@ rb_ivar_count(VALUE obj) { VALUE fields_obj = rb_obj_fields_no_ractor_check(obj); if (fields_obj) { - if (rb_shape_obj_complex_p(fields_obj)) { + if (rb_obj_shape_complex_p(fields_obj)) { iv_count = rb_st_table_size(rb_imemo_fields_complex_tbl(fields_obj)); } else { @@ -2472,7 +2472,7 @@ rb_ivar_count(VALUE obj) break; } - if (rb_shape_obj_has_id(obj)) { + if (rb_obj_shape_has_id(obj)) { iv_count--; } diff --git a/yjit.c b/yjit.c index 70477230232ab0..cf8b58daa5288c 100644 --- a/yjit.c +++ b/yjit.c @@ -436,7 +436,7 @@ rb_object_shape_count(void) bool rb_yjit_shape_obj_complex_p(VALUE obj) { - return rb_shape_obj_complex_p(obj); + return rb_obj_shape_complex_p(obj); } attr_index_t From 070ebb3f0e302233be2f434e6c69114ffb96d06e Mon Sep 17 00:00:00 2001 From: Kevin Menard Date: Tue, 5 May 2026 19:19:36 -0400 Subject: [PATCH 4/4] ZJIT: Skip redundant patch point rewrites on no-EP-escape invalidation The interpreter may fire the `rb_zjit_invalidate_no_ep_escape` callback multiple times for the same ISEQ. The first one will process all patch points so subsequent processing is unnecessary. Consuming the no-EP-escape patch points on first invocation avoids redundant repeated computation. --- zjit/src/invariants.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 546f8594f1cfed..0fbeabab643418 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -216,7 +216,7 @@ pub extern "C" fn rb_zjit_invalidate_no_ep_escape(iseq: IseqPtr) { invariants.ep_escape_iseqs.insert(iseq); // If the ISEQ has been compiled assuming it doesn't escape EP, invalidate the JIT code. - if let Some(patch_points) = invariants.no_ep_escape_iseq_patch_points.get(&iseq) { + if let Some(patch_points) = invariants.no_ep_escape_iseq_patch_points.remove(&iseq) { debug!("EP is escaped: {}", iseq_name(iseq)); // Invalidate the patch points for this ISEQ