diff options
author | Aaron Patterson <[email protected]> | 2023-10-19 11:00:54 -0700 |
---|---|---|
committer | Aaron Patterson <[email protected]> | 2023-10-24 10:52:06 -0700 |
commit | caf6a72348431e0e6b61be84919cd06c7a745189 (patch) | |
tree | a8340c3033d0885aaa3d0ec460035d52bcac6afc /variable.c | |
parent | 27c75319396b8e9fa43b33aca99725b7352a6dcb (diff) |
remove IV limit / support complex shapes on classes
Diffstat (limited to 'variable.c')
-rw-r--r-- | variable.c | 212 |
1 files changed, 151 insertions, 61 deletions
diff --git a/variable.c b/variable.c index 15dc4831d8..0c9e6b2618 100644 --- a/variable.c +++ b/variable.c @@ -72,6 +72,22 @@ struct ivar_update { #endif }; +static inline st_table * +RCLASS_IV_HASH(VALUE obj) +{ + RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); + RUBY_ASSERT(RCLASS_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID); + return (st_table *)RCLASS_IVPTR(obj); +} + +static inline void +RCLASS_SET_IV_HASH(VALUE obj, const st_table *tbl) +{ + RUBY_ASSERT(RB_TYPE_P(obj, RUBY_T_CLASS) || RB_TYPE_P(obj, RUBY_T_MODULE)); + RUBY_ASSERT(RCLASS_SHAPE_ID(obj) == OBJ_TOO_COMPLEX_SHAPE_ID); + RCLASS_IVPTR(obj) = (VALUE *)tbl; +} + void Init_var_tables(void) { @@ -1231,7 +1247,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) case T_CLASS: case T_MODULE: { - bool found; + bool found = false; VALUE val; RB_VM_LOCK_ENTER(); @@ -1240,18 +1256,30 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef) shape_id = RCLASS_SHAPE_ID(obj); #endif - attr_index_t index = 0; - shape = rb_shape_get_shape_by_id(shape_id); - found = rb_shape_get_iv_index(shape, id, &index); - - if (found) { - ivar_list = RCLASS_IVPTR(obj); - RUBY_ASSERT(ivar_list); - - val = ivar_list[index]; + if (rb_shape_obj_too_complex(obj)) { + st_table * iv_table = RCLASS_IV_HASH(obj); + if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) { + found = true; + } + else { + val = undef; + } } else { - val = undef; + + attr_index_t index = 0; + shape = rb_shape_get_shape_by_id(shape_id); + found = rb_shape_get_iv_index(shape, id, &index); + + if (found) { + ivar_list = RCLASS_IVPTR(obj); + RUBY_ASSERT(ivar_list); + + val = ivar_list[index]; + } + else { + val = undef; + } } } RB_VM_LOCK_LEAVE(); @@ -1439,6 +1467,60 @@ rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize) return ivtbl; } +static int +rb_complex_ivar_set(VALUE obj, ID id, VALUE val) +{ + st_table * table; + RUBY_ASSERT(rb_shape_obj_too_complex(obj)); + + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + table = ROBJECT_IV_HASH(obj); + break; + case T_CLASS: + case T_MODULE: + table = RCLASS_IV_HASH(obj); + break; + default: + rb_bug("oh no"); + } + + int found = st_insert(table, (st_data_t)id, (st_data_t)val); + RB_OBJ_WRITTEN(obj, Qundef, val); + return found; +} + +static void +rb_evict_ivars_to_hash(VALUE obj, rb_shape_t * shape) +{ + RUBY_ASSERT(!rb_shape_obj_too_complex(obj)); + + st_table * table = st_init_numtable_with_size(shape->next_iv_index); + + // Evacuate all previous values from shape into id_table + rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table); + + rb_shape_set_too_complex(obj); + RUBY_ASSERT(rb_shape_obj_too_complex(obj)); + + switch (BUILTIN_TYPE(obj)) { + case T_OBJECT: + if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { + xfree(ROBJECT(obj)->as.heap.ivptr); + } + + ROBJECT_SET_IV_HASH(obj, table); + break; + case T_CLASS: + case T_MODULE: + xfree(RCLASS_IVPTR(obj)); + RCLASS_SET_IV_HASH(obj, table); + break; + default: + rb_bug("oops!"); + } +} + // @note May raise when there are too many instance variables. rb_shape_t * rb_grow_iv_list(VALUE obj) @@ -1446,10 +1528,13 @@ rb_grow_iv_list(VALUE obj) rb_shape_t * initial_shape = rb_shape_get_shape(obj); RUBY_ASSERT(initial_shape->capacity > 0); rb_shape_t * res = rb_shape_transition_shape_capa(initial_shape); - - rb_ensure_iv_list_size(obj, initial_shape->capacity, res->capacity); - - rb_shape_set_shape(obj, res); + if (res->type == SHAPE_OBJ_TOO_COMPLEX) { // Out of shapes + rb_evict_ivars_to_hash(obj, initial_shape); + } + else { + rb_ensure_iv_list_size(obj, initial_shape->capacity, res->capacity); + rb_shape_set_shape(obj, res); + } return res; } @@ -1470,12 +1555,12 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val) uint32_t num_iv = shape->capacity; if (rb_shape_obj_too_complex(obj)) { - st_table * table = ROBJECT_IV_HASH(obj); - st_insert(table, (st_data_t)id, (st_data_t)val); - RB_OBJ_WRITTEN(obj, Qundef, val); + rb_complex_ivar_set(obj, id, val); return 0; } + rb_shape_t *next_shape; + if (!rb_shape_get_iv_index(shape, id, &index)) { index = shape->next_iv_index; if (index >= MAX_IVARS) { @@ -1487,31 +1572,20 @@ rb_obj_ivar_set(VALUE obj, ID id, VALUE val) if (UNLIKELY(shape->next_iv_index >= num_iv)) { RUBY_ASSERT(shape->next_iv_index == num_iv); - shape = rb_grow_iv_list(obj); + next_shape = rb_grow_iv_list(obj); + if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) { + rb_complex_ivar_set(obj, id, val); + return 0; + } + shape = next_shape; RUBY_ASSERT(shape->type == SHAPE_CAPACITY_CHANGE); } - rb_shape_t *next_shape = rb_shape_get_next(shape, obj, id); + next_shape = rb_shape_get_next(shape, obj, id); if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) { - st_table * table = st_init_numtable_with_size(shape->next_iv_index); - - // Evacuate all previous values from shape into id_table - rb_ivar_foreach(obj, rb_obj_evacuate_ivs_to_hash_table, (st_data_t)table); - - // Insert new value too - st_insert(table, (st_data_t)id, (st_data_t)val); - RB_OBJ_WRITTEN(obj, Qundef, val); - - rb_shape_set_too_complex(obj); - RUBY_ASSERT(rb_shape_obj_too_complex(obj)); - - if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { - xfree(ROBJECT(obj)->as.heap.ivptr); - } - - ROBJECT(obj)->as.heap.ivptr = (VALUE *)table; - + rb_evict_ivars_to_hash(obj, shape); + rb_complex_ivar_set(obj, id, val); return 0; } else { @@ -1765,7 +1839,13 @@ class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg) struct iv_itr_data itr_data; itr_data.obj = obj; itr_data.arg = arg; - iterate_over_shapes_with_callback(shape, func, &itr_data); + itr_data.func = func; + if (rb_shape_obj_too_complex(obj)) { + rb_st_foreach(RCLASS_IV_HASH(obj), each_hash_iv, (st_data_t)&itr_data); + } + else { + iterate_over_shapes_with_callback(shape, func, &itr_data); + } } void @@ -3989,40 +4069,50 @@ int rb_class_ivar_set(VALUE obj, ID key, VALUE value) { RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE)); - int found; + int found = 0; rb_check_frozen(obj); RB_VM_LOCK_ENTER(); { rb_shape_t * shape = rb_shape_get_shape(obj); - attr_index_t idx; - found = rb_shape_get_iv_index(shape, key, &idx); - - if (found) { - // Changing an existing instance variable - RUBY_ASSERT(RCLASS_IVPTR(obj)); - - RCLASS_IVPTR(obj)[idx] = value; - RB_OBJ_WRITTEN(obj, Qundef, value); + if (shape->type == SHAPE_OBJ_TOO_COMPLEX) { + found = rb_complex_ivar_set(obj, key, value); } else { - // Creating and setting a new instance variable + attr_index_t idx; + found = rb_shape_get_iv_index(shape, key, &idx); - // Move to a shape which fits the new ivar - idx = shape->next_iv_index; - shape = rb_shape_get_next(shape, obj, key); + if (found) { + // Changing an existing instance variable + RUBY_ASSERT(RCLASS_IVPTR(obj)); - // We always allocate a power of two sized IV array. This way we - // only need to realloc when we expand into a new power of two size - if ((idx & (idx - 1)) == 0) { - size_t newsize = idx ? idx * 2 : 1; - REALLOC_N(RCLASS_IVPTR(obj), VALUE, newsize); + RCLASS_IVPTR(obj)[idx] = value; + RB_OBJ_WRITTEN(obj, Qundef, value); } + else { + // Creating and setting a new instance variable + + // Move to a shape which fits the new ivar + idx = shape->next_iv_index; + rb_shape_t * next_shape = rb_shape_get_next(shape, obj, key); + if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) { + rb_evict_ivars_to_hash(obj, shape); + rb_complex_ivar_set(obj, key, value); + } + else { + // We always allocate a power of two sized IV array. This way we + // only need to realloc when we expand into a new power of two size + if ((idx & (idx - 1)) == 0) { + size_t newsize = idx ? idx * 2 : 1; + REALLOC_N(RCLASS_IVPTR(obj), VALUE, newsize); + } - RUBY_ASSERT(RCLASS_IVPTR(obj)); + RUBY_ASSERT(RCLASS_IVPTR(obj)); - RB_OBJ_WRITE(obj, &RCLASS_IVPTR(obj)[idx], value); - rb_shape_set_shape(obj, shape); + RB_OBJ_WRITE(obj, &RCLASS_IVPTR(obj)[idx], value); + rb_shape_set_shape(obj, next_shape); + } + } } } RB_VM_LOCK_LEAVE(); |