summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2022-11-17 15:57:11 -0800
committerAaron Patterson <aaron.patterson@gmail.com>2022-11-18 08:31:56 -0800
commit10788166e7e568fdcd0b748e8d5dab442dcdc7ef (patch)
treeee195186a3fed8333bc1875f66ddab0c85c3d7db
parentf0ce1186620273a1182e6084559765143099eb88 (diff)
downloadruby-10788166e7e568fdcd0b748e8d5dab442dcdc7ef.tar.gz
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.
-rw-r--r--gc.c8
-rw-r--r--object.c2
-rw-r--r--shape.c23
-rw-r--r--shape.h3
-rw-r--r--test/ruby/test_shapes.rb19
-rw-r--r--variable.c1
6 files changed, 46 insertions, 10 deletions
diff --git a/gc.c b/gc.c
index 5a5ec869ca..06015e3880 100644
--- a/gc.c
+++ b/gc.c
@@ -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);
}
diff --git a/object.c b/object.c
index 7f4404c462..e50430a4fb 100644
--- a/object.c
+++ b/object.c
@@ -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);
}
diff --git a/shape.c b/shape.c
index bb51abecbf..d98e596eb1 100644
--- a/shape.c
+++ b/shape.c
@@ -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));
diff --git a/shape.h b/shape.h
index a2cbd7c482..9bbc5d35f9 100644
--- a/shape.h
+++ b/shape.h
@@ -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;
}