summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/objspace/objspace_dump.c5
-rw-r--r--rjit_c.rb1
-rw-r--r--shape.c53
-rw-r--r--shape.h1
-rwxr-xr-xtool/rjit/bindgen.rb1
-rw-r--r--variable.c3
-rw-r--r--vm_insnhelper.c9
7 files changed, 60 insertions, 13 deletions
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index 963deb13fc..c80c38eba4 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -771,6 +771,11 @@ shape_i(rb_shape_t *shape, void *data)
case SHAPE_FROZEN:
dump_append(dc, "\"FROZEN\"");
break;
+ case SHAPE_CAPACITY_CHANGE:
+ dump_append(dc, "\"CAPACITY_CHANGE\"");
+ dump_append(dc, ", \"capacity\":");
+ dump_append_sizet(dc, shape->capacity);
+ break;
case SHAPE_T_OBJECT:
dump_append(dc, "\"T_OBJECT\"");
break;
diff --git a/rjit_c.rb b/rjit_c.rb
index 7e00fad9b4..df30841d2b 100644
--- a/rjit_c.rb
+++ b/rjit_c.rb
@@ -406,6 +406,7 @@ module RubyVM::RJIT # :nodoc: all
C::RUBY_T_OBJECT = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_OBJECT) }
C::RUBY_T_STRING = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_STRING) }
C::RUBY_T_SYMBOL = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_SYMBOL) }
+ C::SHAPE_CAPACITY_CHANGE = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_CAPACITY_CHANGE) }
C::SHAPE_FLAG_SHIFT = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_FLAG_SHIFT) }
C::SHAPE_FROZEN = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_FROZEN) }
C::SHAPE_ID_NUM_BITS = Primitive.cexpr! %q{ SIZET2NUM(SHAPE_ID_NUM_BITS) }
diff --git a/shape.c b/shape.c
index 13c8d11b82..ef08030ad4 100644
--- a/shape.c
+++ b/shape.c
@@ -44,6 +44,8 @@ static ID id_frozen;
static ID id_t_object;
static ID size_pool_edge_names[SIZE_POOL_COUNT];
+rb_shape_t * rb_shape_transition_shape_capa(rb_shape_t * shape);
+
#define LEAF 0
#define BLACK 0x0
#define RED 0x1
@@ -432,16 +434,12 @@ rb_shape_alloc_new_child(ID id, rb_shape_t * shape, enum shape_type shape_type)
switch (shape_type) {
case SHAPE_IVAR:
- if (UNLIKELY(shape->next_iv_index >= shape->capacity)) {
- RUBY_ASSERT(shape->next_iv_index == shape->capacity);
- new_shape->capacity = (uint32_t)rb_malloc_grow_capa(shape->capacity, sizeof(VALUE));
- }
- RUBY_ASSERT(new_shape->capacity > shape->next_iv_index);
new_shape->next_iv_index = shape->next_iv_index + 1;
if (new_shape->next_iv_index > ANCESTOR_CACHE_THRESHOLD) {
redblack_cache_ancestors(new_shape);
}
break;
+ case SHAPE_CAPACITY_CHANGE:
case SHAPE_FROZEN:
case SHAPE_T_OBJECT:
new_shape->next_iv_index = shape->next_iv_index;
@@ -682,8 +680,16 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
allow_new_shape = RCLASS_EXT(klass)->variation_count < SHAPE_MAX_VARIATIONS;
}
+ if (UNLIKELY(shape->next_iv_index >= shape->capacity)) {
+ RUBY_ASSERT(shape->next_iv_index == shape->capacity);
+ shape = rb_shape_transition_shape_capa(shape);
+ if (UNLIKELY(shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ return shape;
+ }
+ }
+
bool variation_created = false;
- rb_shape_t *new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
+ rb_shape_t * new_shape = get_next_shape_internal(shape, id, SHAPE_IVAR, &variation_created, allow_new_shape);
// Check if we should update max_iv_count on the object's class
if (BUILTIN_TYPE(obj) == T_OBJECT) {
@@ -710,6 +716,29 @@ rb_shape_get_next(rb_shape_t* shape, VALUE obj, ID id)
return new_shape;
}
+static inline rb_shape_t *
+rb_shape_transition_shape_capa_create(rb_shape_t* shape, size_t new_capacity)
+{
+ RUBY_ASSERT(new_capacity < (size_t)MAX_IVARS);
+
+ ID edge_name = rb_make_temporary_id(new_capacity);
+ bool dont_care;
+ rb_shape_t * new_shape = get_next_shape_internal(shape, edge_name, SHAPE_CAPACITY_CHANGE, &dont_care, true);
+ if (rb_shape_id(new_shape) != OBJ_TOO_COMPLEX_SHAPE_ID) {
+ new_shape->capacity = (uint32_t)new_capacity;
+ }
+ return new_shape;
+}
+
+rb_shape_t *
+rb_shape_transition_shape_capa(rb_shape_t* shape)
+{
+ if (UNLIKELY(shape->type == SHAPE_OBJ_TOO_COMPLEX)) {
+ return shape;
+ }
+ return rb_shape_transition_shape_capa_create(shape, rb_malloc_grow_capa(shape->capacity, sizeof(VALUE)));
+}
+
// Same as rb_shape_get_iv_index, but uses a provided valid shape id and index
// to return a result faster if branches of the shape tree are closely related.
bool
@@ -796,6 +825,7 @@ rb_shape_get_iv_index(rb_shape_t * shape, ID id, attr_index_t *value)
RUBY_ASSERT(shape->next_iv_index > 0);
*value = shape->next_iv_index - 1;
return true;
+ case SHAPE_CAPACITY_CHANGE:
case SHAPE_ROOT:
case SHAPE_T_OBJECT:
return false;
@@ -862,6 +892,7 @@ rb_shape_traverse_from_new_root(rb_shape_t *initial_shape, rb_shape_t *dest_shap
}
break;
case SHAPE_ROOT:
+ case SHAPE_CAPACITY_CHANGE:
case SHAPE_T_OBJECT:
break;
case SHAPE_OBJ_TOO_COMPLEX:
@@ -894,10 +925,18 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
switch ((enum shape_type)dest_shape->type) {
case SHAPE_IVAR:
- midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
+ if (midway_shape->capacity <= midway_shape->next_iv_index) {
+ // There isn't enough room to write this IV, so we need to increase the capacity
+ midway_shape = rb_shape_transition_shape_capa(midway_shape);
+ }
+
+ if (LIKELY(rb_shape_id(midway_shape) != OBJ_TOO_COMPLEX_SHAPE_ID)) {
+ midway_shape = rb_shape_get_next_iv_shape(midway_shape, dest_shape->edge_name);
+ }
break;
case SHAPE_ROOT:
case SHAPE_FROZEN:
+ case SHAPE_CAPACITY_CHANGE:
case SHAPE_T_OBJECT:
break;
case SHAPE_OBJ_TOO_COMPLEX:
diff --git a/shape.h b/shape.h
index f1823258da..6d4f3f9811 100644
--- a/shape.h
+++ b/shape.h
@@ -64,6 +64,7 @@ enum shape_type {
SHAPE_ROOT,
SHAPE_IVAR,
SHAPE_FROZEN,
+ SHAPE_CAPACITY_CHANGE,
SHAPE_T_OBJECT,
SHAPE_OBJ_TOO_COMPLEX,
};
diff --git a/tool/rjit/bindgen.rb b/tool/rjit/bindgen.rb
index 328c8b344b..ebb58d8281 100755
--- a/tool/rjit/bindgen.rb
+++ b/tool/rjit/bindgen.rb
@@ -435,6 +435,7 @@ generator = BindingGenerator.new(
RUBY_T_STRING
RUBY_T_SYMBOL
RUBY_T_OBJECT
+ SHAPE_CAPACITY_CHANGE
SHAPE_FLAG_SHIFT
SHAPE_FROZEN
SHAPE_ID_NUM_BITS
diff --git a/variable.c b/variable.c
index 653fa58552..72c0fd7c2b 100644
--- a/variable.c
+++ b/variable.c
@@ -1509,7 +1509,7 @@ generic_ivar_lookup_ensure_size(st_data_t *k, st_data_t *v, st_data_t u, int exi
if (!existing || ivar_lookup->resize) {
if (existing) {
RUBY_ASSERT(ivar_lookup->shape->type == SHAPE_IVAR);
- RUBY_ASSERT(rb_shape_get_shape_by_id(ivar_lookup->shape->parent_id)->capacity < ivar_lookup->shape->capacity);
+ RUBY_ASSERT(rb_shape_get_shape_by_id(ivar_lookup->shape->parent_id)->type == SHAPE_CAPACITY_CHANGE);
}
else {
FL_SET_RAW((VALUE)*k, FL_EXIVAR);
@@ -1895,6 +1895,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu
}
}
return false;
+ case SHAPE_CAPACITY_CHANGE:
case SHAPE_FROZEN:
case SHAPE_T_OBJECT:
return iterate_over_shapes_with_callback(rb_shape_get_parent(shape), callback, itr_data);
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index be70b93588..bea80ed588 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1460,11 +1460,11 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_i
RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID);
}
else if (dest_shape_id != INVALID_SHAPE_ID) {
- rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id);
- if (shape_id == dest_shape->parent_id && dest_shape->edge_name == id && shape->capacity == dest_shape->capacity) {
- RUBY_ASSERT(index < dest_shape->capacity);
+ if (shape_id == dest_shape->parent_id && dest_shape->edge_name == id && dest_shape->type == SHAPE_IVAR) {
+ RUBY_ASSERT(rb_shape_get_shape_by_id(shape_id)->capacity == dest_shape->capacity);
+ RUBY_ASSERT(index < rb_shape_get_shape_by_id(shape_id)->capacity);
}
else {
return Qundef;
@@ -1508,11 +1508,10 @@ vm_setivar(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_index_t i
VM_ASSERT(!rb_ractor_shareable_p(obj));
}
else if (dest_shape_id != INVALID_SHAPE_ID) {
- rb_shape_t *shape = rb_shape_get_shape_by_id(shape_id);
rb_shape_t *dest_shape = rb_shape_get_shape_by_id(dest_shape_id);
shape_id_t source_shape_id = dest_shape->parent_id;
- if (shape_id == source_shape_id && dest_shape->edge_name == id && shape->capacity == dest_shape->capacity) {
+ if (shape_id == source_shape_id && dest_shape->edge_name == id) {
RUBY_ASSERT(dest_shape_id != INVALID_SHAPE_ID && shape_id != INVALID_SHAPE_ID);
ROBJECT_SET_SHAPE_ID(obj, dest_shape_id);