diff options
author | Aaron Patterson <tenderlove@ruby-lang.org> | 2022-11-17 15:57:11 -0800 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2022-11-18 08:31:56 -0800 |
commit | 10788166e7e568fdcd0b748e8d5dab442dcdc7ef (patch) | |
tree | ee195186a3fed8333bc1875f66ddab0c85c3d7db | |
parent | f0ce1186620273a1182e6084559765143099eb88 (diff) |
Differentiate T_OBJECT shapes from other objects
We would like to differentiate types of objects via their shape. This
commit adds a special T_OBJECT shape when we allocate an instance of
T_OBJECT. This allows us to avoid testing whether an object is an
instance of a T_OBJECT or not, we can just check the shape.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/6758
-rw-r--r-- | gc.c | 8 | ||||
-rw-r--r-- | object.c | 2 | ||||
-rw-r--r-- | shape.c | 23 | ||||
-rw-r--r-- | shape.h | 3 | ||||
-rw-r--r-- | test/ruby/test_shapes.rb | 19 | ||||
-rw-r--r-- | variable.c | 1 |
6 files changed, 46 insertions, 10 deletions
@@ -2942,6 +2942,12 @@ rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected) #endif VALUE obj = newobj_of(klass, flags, 0, 0, 0, wb_protected, size); + RUBY_ASSERT(rb_shape_get_shape(obj)->type == SHAPE_ROOT || + rb_shape_get_shape(obj)->type == SHAPE_INITIAL_CAPACITY); + + // Set the shape to the specific T_OBJECT shape which is always + // SIZE_POOL_COUNT away from the root shape. + ROBJECT_SET_SHAPE_ID(obj, ROBJECT_SHAPE_ID(obj) + SIZE_POOL_COUNT); #if RUBY_DEBUG VALUE *ptr = ROBJECT_IVPTR(obj); @@ -10030,7 +10036,7 @@ gc_ref_update_object(rb_objspace_t *objspace, VALUE v) } ptr = ROBJECT(v)->as.ary; size_t size_pool_shape_id = size_pool_idx_for_size(embed_size); - rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id); + rb_shape_t * initial_shape = rb_shape_get_shape_by_id((shape_id_t)size_pool_shape_id + SIZE_POOL_COUNT); rb_shape_t * new_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_shape(v)); rb_shape_set_shape(v, new_shape); } @@ -293,7 +293,7 @@ rb_obj_copy_ivar(VALUE dest, VALUE obj) rb_shape_t * initial_shape = rb_shape_get_shape(dest); if (initial_shape->size_pool_index != src_shape->size_pool_index) { - RUBY_ASSERT(initial_shape->parent_id == ROOT_SHAPE_ID || initial_shape->type == SHAPE_ROOT); + RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT); shape_to_set_on_dest = rb_shape_rebuild_shape(initial_shape, src_shape); } @@ -8,6 +8,7 @@ #include <stdbool.h> static ID id_frozen; +static ID id_t_object; static ID size_pool_edge_names[SIZE_POOL_COUNT]; /* @@ -152,6 +153,7 @@ get_next_shape_internal(rb_shape_t * shape, ID id, enum shape_type shape_type) case SHAPE_CAPACITY_CHANGE: case SHAPE_IVAR_UNDEF: case SHAPE_FROZEN: + case SHAPE_T_OBJECT: new_shape->next_iv_index = shape->next_iv_index; break; case SHAPE_INITIAL_CAPACITY: @@ -264,6 +266,7 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value) case SHAPE_IVAR_UNDEF: case SHAPE_ROOT: case SHAPE_INITIAL_CAPACITY: + case SHAPE_T_OBJECT: return false; case SHAPE_FROZEN: rb_bug("Ivar should not exist on transition\n"); @@ -333,14 +336,16 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape) { rb_shape_t * midway_shape; - if (dest_shape->type != SHAPE_ROOT) { + RUBY_ASSERT(initial_shape->type == SHAPE_T_OBJECT); + + if (dest_shape->type != initial_shape->type) { midway_shape = rb_shape_rebuild_shape(initial_shape, rb_shape_get_parent(dest_shape)); } else { midway_shape = initial_shape; } - switch (dest_shape->type) { + switch ((enum shape_type)dest_shape->type) { case SHAPE_IVAR: if (midway_shape->capacity <= midway_shape->next_iv_index) { // There isn't enough room to write this IV, so we need to increase the capacity @@ -355,6 +360,8 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape) case SHAPE_ROOT: case SHAPE_FROZEN: case SHAPE_CAPACITY_CHANGE: + case SHAPE_INITIAL_CAPACITY: + case SHAPE_T_OBJECT: break; } @@ -592,6 +599,7 @@ void Init_default_shapes(void) { id_frozen = rb_make_internal_id(); + id_t_object = rb_make_internal_id(); // Shapes by size pool for (int i = 0; i < SIZE_POOL_COUNT; i++) { @@ -615,6 +623,16 @@ Init_default_shapes(void) RUBY_ASSERT(rb_shape_id(new_shape) == (shape_id_t)i); } + // Make shapes for T_OBJECT + for (int i = 0; i < SIZE_POOL_COUNT; i++) { + rb_shape_t * shape = rb_shape_get_shape_by_id(i); +#if RUBY_DEBUG + rb_shape_t * t_object_shape = +#endif + get_next_shape_internal(shape, id_t_object, SHAPE_T_OBJECT); + RUBY_ASSERT(rb_shape_id(t_object_shape) == (shape_id_t)(i + SIZE_POOL_COUNT)); + } + // Special const shape #if RUBY_DEBUG rb_shape_t * special_const_shape = @@ -644,6 +662,7 @@ Init_shape(void) rb_define_method(rb_cShape, "capacity", rb_shape_capacity, 0); rb_define_const(rb_cShape, "SHAPE_ROOT", INT2NUM(SHAPE_ROOT)); rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR)); + rb_define_const(rb_cShape, "SHAPE_T_OBJECT", INT2NUM(SHAPE_T_OBJECT)); rb_define_const(rb_cShape, "SHAPE_IVAR_UNDEF", INT2NUM(SHAPE_IVAR_UNDEF)); rb_define_const(rb_cShape, "SHAPE_FROZEN", INT2NUM(SHAPE_FROZEN)); rb_define_const(rb_cShape, "SHAPE_BITS", INT2NUM(SHAPE_BITS)); @@ -42,7 +42,7 @@ typedef uint16_t shape_id_t; # define ROOT_SHAPE_ID 0x0 // We use SIZE_POOL_COUNT number of shape IDs for transitions out of different size pools // The next available shapd ID will be the SPECIAL_CONST_SHAPE_ID -# define SPECIAL_CONST_SHAPE_ID SIZE_POOL_COUNT +# define SPECIAL_CONST_SHAPE_ID (SIZE_POOL_COUNT * 2) struct rb_shape { struct rb_id_table * edges; // id_table from ID (ivar) to next shape @@ -63,6 +63,7 @@ enum shape_type { SHAPE_CAPACITY_CHANGE, SHAPE_IVAR_UNDEF, SHAPE_INITIAL_CAPACITY, + SHAPE_T_OBJECT, }; #if SHAPE_IN_BASIC_FLAGS diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index 326ff3a453..848bb4971a 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -88,8 +88,8 @@ class TestShapes < Test::Unit::TestCase class TestObject; end - def test_new_obj_has_root_shape - assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new)) + def test_new_obj_has_t_object_shape + assert_shape_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(TestObject.new).parent) end def test_str_has_root_shape @@ -114,14 +114,23 @@ class TestShapes < Test::Unit::TestCase def test_basic_shape_transition obj = Example.new - refute_equal(RubyVM::Shape.root_shape, RubyVM::Shape.of(obj)) - assert_shape_equal(RubyVM::Shape.root_shape.edges[:@a], RubyVM::Shape.of(obj)) + shape = RubyVM::Shape.of(obj) + refute_equal(RubyVM::Shape.root_shape, shape) + assert_equal :@a, shape.edge_name + assert_equal RubyVM::Shape::SHAPE_IVAR, shape.type + + shape = shape.parent + assert_equal RubyVM::Shape::SHAPE_T_OBJECT, shape.type + + shape = shape.parent + assert_equal(RubyVM::Shape.root_shape.id, shape.id) assert_equal(obj.instance_variable_get(:@a), 1) end def test_different_objects_make_same_transition - obj = Example.new + obj = [] obj2 = "" + obj.instance_variable_set(:@a, 1) obj2.instance_variable_set(:@a, 1) assert_shape_equal(RubyVM::Shape.of(obj), RubyVM::Shape.of(obj2)) end diff --git a/variable.c b/variable.c index daae90396e..15e18f21ae 100644 --- a/variable.c +++ b/variable.c @@ -1606,6 +1606,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu case SHAPE_CAPACITY_CHANGE: case SHAPE_FROZEN: case SHAPE_IVAR_UNDEF: + case SHAPE_T_OBJECT: iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data); return; } |