Add ST table to gen_ivtbl for complex shapes

On 32-bit systems, we must store the shape ID in the gen_ivtbl to not
lose the shape. If we directly store the ST table into the generic
ivar table, then we lose the shape. This makes it impossible to
determine the shape of the object and whether it is too complex or not.
This commit is contained in:
Peter Zhu 2023-10-31 09:38:35 -04:00
parent 1c45124c49
commit e2d950733e
5 changed files with 118 additions and 88 deletions

View File

@ -2758,8 +2758,8 @@ obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) { if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
struct gen_ivtbl *ivtbl; struct gen_ivtbl *ivtbl;
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl); rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->numiv; i++) { for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
VALUE val = ivtbl->ivptr[i]; VALUE val = ivtbl->as.shape.ivptr[i];
if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1; if (!UNDEF_P(val) && obj_traverse_i(val, data)) return 1;
} }
} }
@ -3229,9 +3229,9 @@ obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) { if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
struct gen_ivtbl *ivtbl; struct gen_ivtbl *ivtbl;
rb_ivar_generic_ivtbl_lookup(obj, &ivtbl); rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
for (uint32_t i = 0; i < ivtbl->numiv; i++) { for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
if (!UNDEF_P(ivtbl->ivptr[i])) { if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
CHECK_AND_REPLACE(ivtbl->ivptr[i]); CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]);
} }
} }
} }

View File

@ -539,7 +539,7 @@ move_iv(VALUE obj, ID id, attr_index_t from, attr_index_t to)
default: { default: {
struct gen_ivtbl *ivtbl; struct gen_ivtbl *ivtbl;
rb_gen_ivtbl_get(obj, id, &ivtbl); rb_gen_ivtbl_get(obj, id, &ivtbl);
ivtbl->ivptr[to] = ivtbl->ivptr[from]; ivtbl->as.shape.ivptr[to] = ivtbl->as.shape.ivptr[from];
break; break;
} }
} }
@ -570,7 +570,7 @@ remove_shape_recursive(VALUE obj, ID id, rb_shape_t * shape, VALUE * removed)
default: { default: {
struct gen_ivtbl *ivtbl; struct gen_ivtbl *ivtbl;
rb_gen_ivtbl_get(obj, id, &ivtbl); rb_gen_ivtbl_get(obj, id, &ivtbl);
*removed = ivtbl->ivptr[index]; *removed = ivtbl->as.shape.ivptr[index];
break; break;
} }
} }

View File

@ -1084,7 +1084,7 @@ rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **ivtbl)
static size_t static size_t
gen_ivtbl_bytes(size_t n) gen_ivtbl_bytes(size_t n)
{ {
return offsetof(struct gen_ivtbl, ivptr) + n * sizeof(VALUE); return offsetof(struct gen_ivtbl, as.shape.ivptr) + n * sizeof(VALUE);
} }
static struct gen_ivtbl * static struct gen_ivtbl *
@ -1092,30 +1092,17 @@ gen_ivtbl_resize(struct gen_ivtbl *old, uint32_t n)
{ {
RUBY_ASSERT(n > 0); RUBY_ASSERT(n > 0);
uint32_t len = old ? old->numiv : 0; uint32_t len = old ? old->as.shape.numiv : 0;
struct gen_ivtbl *ivtbl = xrealloc(old, gen_ivtbl_bytes(n)); struct gen_ivtbl *ivtbl = xrealloc(old, gen_ivtbl_bytes(n));
ivtbl->numiv = n; ivtbl->as.shape.numiv = n;
for (; len < n; len++) { for (; len < n; len++) {
ivtbl->ivptr[len] = Qundef; ivtbl->as.shape.ivptr[len] = Qundef;
} }
return ivtbl; return ivtbl;
} }
#if 0
static struct gen_ivtbl *
gen_ivtbl_dup(const struct gen_ivtbl *orig)
{
size_t s = gen_ivtbl_bytes(orig->numiv);
struct gen_ivtbl *ivtbl = xmalloc(s);
memcpy(ivtbl, orig, s);
return ivtbl;
}
#endif
static int static int
generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing) generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing)
{ {
@ -1126,7 +1113,7 @@ generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing)
if (existing) { if (existing) {
ivtbl = (struct gen_ivtbl *)*v; ivtbl = (struct gen_ivtbl *)*v;
if (ivup->iv_index < ivtbl->numiv) { if (ivup->iv_index < ivtbl->as.shape.numiv) {
ivup->ivtbl = ivtbl; ivup->ivtbl = ivtbl;
return ST_STOP; return ST_STOP;
} }
@ -1142,31 +1129,19 @@ generic_ivar_update(st_data_t *k, st_data_t *v, st_data_t u, int existing)
return ST_CONTINUE; return ST_CONTINUE;
} }
static void
gen_ivtbl_mark_and_update(struct gen_ivtbl *ivtbl)
{
uint32_t i;
for (i = 0; i < ivtbl->numiv; i++) {
rb_gc_mark_and_move(&ivtbl->ivptr[i]);
}
}
void void
rb_mark_and_update_generic_ivar(VALUE obj) rb_mark_and_update_generic_ivar(VALUE obj)
{ {
if (rb_shape_obj_too_complex(obj)) {
st_table *ivtbl;
if (rb_gen_ivtbl_get(obj, 0, (struct gen_ivtbl **)&ivtbl)) {
rb_mark_tbl(ivtbl);
}
}
else {
struct gen_ivtbl *ivtbl; struct gen_ivtbl *ivtbl;
if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
gen_ivtbl_mark_and_update(ivtbl); if (rb_shape_obj_too_complex(obj)) {
rb_mark_tbl(ivtbl->as.complex.table);
}
else {
for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
rb_gc_mark_and_move(&ivtbl->as.shape.ivptr[i]);
}
} }
} }
} }
@ -1184,10 +1159,19 @@ rb_mv_generic_ivar(VALUE rsrc, VALUE dst)
void void
rb_free_generic_ivar(VALUE obj) rb_free_generic_ivar(VALUE obj)
{ {
st_data_t key = (st_data_t)obj, ivtbl; st_data_t key = (st_data_t)obj, value;
if (st_delete(generic_ivtbl_no_ractor_check(obj), &key, &ivtbl)) bool too_complex = rb_shape_obj_too_complex(obj);
xfree((struct gen_ivtbl *)ivtbl);
if (st_delete(generic_ivtbl_no_ractor_check(obj), &key, &value)) {
struct gen_ivtbl *ivtbl = (struct gen_ivtbl *)value;
if (UNLIKELY(too_complex)) {
st_free_table(ivtbl->as.complex.table);
}
xfree(ivtbl);
}
} }
RUBY_FUNC_EXPORTED size_t RUBY_FUNC_EXPORTED size_t
@ -1195,8 +1179,14 @@ rb_generic_ivar_memsize(VALUE obj)
{ {
struct gen_ivtbl *ivtbl; struct gen_ivtbl *ivtbl;
if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
return gen_ivtbl_bytes(ivtbl->numiv); if (rb_shape_obj_too_complex(obj)) {
return sizeof(struct gen_ivtbl) + st_memsize(ivtbl->as.complex.table);
}
else {
return gen_ivtbl_bytes(ivtbl->as.shape.numiv);
}
}
return 0; return 0;
} }
@ -1225,16 +1215,21 @@ rb_generic_shape_id(VALUE obj)
#endif #endif
static size_t static size_t
gen_ivtbl_count(const struct gen_ivtbl *ivtbl) gen_ivtbl_count(VALUE obj, const struct gen_ivtbl *ivtbl)
{ {
uint32_t i; uint32_t i;
size_t n = 0; size_t n = 0;
for (i = 0; i < ivtbl->numiv; i++) { if (rb_shape_obj_too_complex(obj)) {
if (!UNDEF_P(ivtbl->ivptr[i])) { n = st_table_size(ivtbl->as.complex.table);
}
else {
for (i = 0; i < ivtbl->as.shape.numiv; i++) {
if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
n++; n++;
} }
} }
}
return n; return n;
} }
@ -1328,9 +1323,8 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
rb_gen_ivtbl_get(obj, id, &ivtbl); rb_gen_ivtbl_get(obj, id, &ivtbl);
if (rb_shape_obj_too_complex(obj)) { if (rb_shape_obj_too_complex(obj)) {
st_table * iv_table = (st_table *)ivtbl;
VALUE val; VALUE val;
if (rb_st_lookup(iv_table, (st_data_t)id, (st_data_t *)&val)) { if (rb_st_lookup(ivtbl->as.complex.table, (st_data_t)id, (st_data_t *)&val)) {
return val; return val;
} }
else { else {
@ -1341,7 +1335,7 @@ rb_ivar_lookup(VALUE obj, ID id, VALUE undef)
#if !SHAPE_IN_BASIC_FLAGS #if !SHAPE_IN_BASIC_FLAGS
shape_id = ivtbl->shape_id; shape_id = ivtbl->shape_id;
#endif #endif
ivar_list = ivtbl->ivptr; ivar_list = ivtbl->as.shape.ivptr;
} }
else { else {
return undef; return undef;
@ -1411,6 +1405,7 @@ rb_attr_delete(VALUE obj, ID id)
static int static int
rb_complex_ivar_set(VALUE obj, ID id, VALUE val) rb_complex_ivar_set(VALUE obj, ID id, VALUE val)
{ {
struct gen_ivtbl *ivtbl;
st_table *table; st_table *table;
RUBY_ASSERT(rb_shape_obj_too_complex(obj)); RUBY_ASSERT(rb_shape_obj_too_complex(obj));
@ -1423,9 +1418,18 @@ rb_complex_ivar_set(VALUE obj, ID id, VALUE val)
table = RCLASS_IV_HASH(obj); table = RCLASS_IV_HASH(obj);
break; break;
default: default:
if (!rb_gen_ivtbl_get(obj, 0, (struct gen_ivtbl **)&table)) { if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
table = st_init_numtable(); ivtbl = xmalloc(sizeof(struct gen_ivtbl));
st_insert(generic_ivtbl(obj, id, false), (st_data_t)obj, (st_data_t)table); #if !SHAPE_IN_BASIC_FLAGS
ivtbl->shape_id = SHAPE_OBJ_TOO_COMPLEX;
#endif
table = st_init_numtable_with_size(1);
ivtbl->as.complex.table = table;
st_insert(generic_ivtbl(obj, id, false), (st_data_t)obj, (st_data_t)ivtbl);
}
else {
table = ivtbl->as.complex.table;
} }
} }
@ -1444,11 +1448,10 @@ rb_evict_ivars_to_hash(VALUE obj, rb_shape_t * shape)
// Evacuate all previous values from shape into id_table // 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_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)) { switch (BUILTIN_TYPE(obj)) {
case T_OBJECT: case T_OBJECT:
rb_shape_set_too_complex(obj);
if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) { if (!(RBASIC(obj)->flags & ROBJECT_EMBED)) {
xfree(ROBJECT(obj)->as.heap.ivptr); xfree(ROBJECT(obj)->as.heap.ivptr);
} }
@ -1457,6 +1460,8 @@ rb_evict_ivars_to_hash(VALUE obj, rb_shape_t * shape)
break; break;
case T_CLASS: case T_CLASS:
case T_MODULE: case T_MODULE:
rb_shape_set_too_complex(obj);
xfree(RCLASS_IVPTR(obj)); xfree(RCLASS_IVPTR(obj));
RCLASS_SET_IV_HASH(obj, table); RCLASS_SET_IV_HASH(obj, table);
break; break;
@ -1464,17 +1469,27 @@ rb_evict_ivars_to_hash(VALUE obj, rb_shape_t * shape)
RB_VM_LOCK_ENTER(); RB_VM_LOCK_ENTER();
{ {
struct st_table *gen_ivs = generic_ivtbl_no_ractor_check(obj); struct st_table *gen_ivs = generic_ivtbl_no_ractor_check(obj);
st_data_t ivtbl; st_data_t old_ivtbl;
struct gen_ivtbl *ivtbl = NULL;
// Free the old IV table if (st_delete(gen_ivs, &obj, &old_ivtbl)) {
if (st_delete(gen_ivs, &obj, &ivtbl)) ivtbl = (struct gen_ivtbl *)old_ivtbl;
xfree((struct gen_ivtbl *)ivtbl); }
// Insert the hash table ivtbl = xrealloc(ivtbl, sizeof(struct gen_ivtbl));
st_insert(gen_ivs, (st_data_t)obj, (st_data_t)table); ivtbl->as.complex.table = table;
#if SHAPE_IN_BASIC_FLAGS
rb_shape_set_too_complex(obj);
#else
ivtbl->shape_id = OBJ_TOO_COMPLEX_SHAPE_ID;
#endif
st_insert(gen_ivs, (st_data_t)obj, (st_data_t)ivtbl);
} }
RB_VM_LOCK_LEAVE(); RB_VM_LOCK_LEAVE();
} }
RUBY_ASSERT(rb_shape_obj_too_complex(obj));
} }
static void static void
@ -1501,9 +1516,7 @@ generic_ivar_set(VALUE obj, ID id, VALUE val)
index = shape->next_iv_index; index = shape->next_iv_index;
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) { if (next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
if (shape->next_iv_index > 0) {
rb_evict_ivars_to_hash(obj, shape); rb_evict_ivars_to_hash(obj, shape);
}
rb_complex_ivar_set(obj, id, val); rb_complex_ivar_set(obj, id, val);
rb_shape_set_shape(obj, next_shape); rb_shape_set_shape(obj, next_shape);
FL_SET_RAW(obj, FL_EXIVAR); FL_SET_RAW(obj, FL_EXIVAR);
@ -1526,7 +1539,7 @@ generic_ivar_set(VALUE obj, ID id, VALUE val)
} }
RB_VM_LOCK_LEAVE(); RB_VM_LOCK_LEAVE();
ivup.ivtbl->ivptr[ivup.iv_index] = val; ivup.ivtbl->as.shape.ivptr[ivup.iv_index] = val;
RB_OBJ_WRITTEN(obj, Qundef, val); RB_OBJ_WRITTEN(obj, Qundef, val);
if (!found) { if (!found) {
@ -1558,7 +1571,7 @@ rb_ensure_generic_iv_list_size(VALUE obj, rb_shape_t *shape, uint32_t newsize)
RB_VM_LOCK_ENTER(); RB_VM_LOCK_ENTER();
{ {
if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->numiv)) { if (UNLIKELY(!gen_ivtbl_get_unlocked(obj, 0, &ivtbl) || newsize > ivtbl->as.shape.numiv)) {
struct ivar_update ivup = { struct ivar_update ivup = {
.iv_index = newsize - 1, .iv_index = newsize - 1,
.max_index = newsize, .max_index = newsize,
@ -1829,7 +1842,7 @@ iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_fu
iv_list = RCLASS_IVPTR(itr_data->obj); iv_list = RCLASS_IVPTR(itr_data->obj);
break; break;
default: default:
iv_list = itr_data->ivtbl->ivptr; iv_list = itr_data->ivtbl->as.shape.ivptr;
break; break;
} }
VALUE val = iv_list[shape->next_iv_index - 1]; VALUE val = iv_list[shape->next_iv_index - 1];
@ -1931,16 +1944,26 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj)
} }
if (rb_gen_ivtbl_get(obj, 0, &obj_ivtbl)) { if (rb_gen_ivtbl_get(obj, 0, &obj_ivtbl)) {
if (gen_ivtbl_count(obj_ivtbl) == 0) if (gen_ivtbl_count(obj, obj_ivtbl) == 0)
goto clear; goto clear;
new_ivtbl = gen_ivtbl_resize(0, obj_ivtbl->numiv);
FL_SET(clone, FL_EXIVAR); FL_SET(clone, FL_EXIVAR);
for (uint32_t i=0; i<obj_ivtbl->numiv; i++) { if (rb_shape_obj_too_complex(obj)) {
new_ivtbl->ivptr[i] = obj_ivtbl->ivptr[i]; new_ivtbl = xmalloc(sizeof(struct gen_ivtbl));
#if !SHAPE_IN_BASIC_FLAGS
new_ivtbl->shape_id = SHAPE_OBJ_TOO_COMPLEX;
#endif
new_ivtbl->as.complex.table = st_copy(obj_ivtbl->as.complex.table);
}
else {
new_ivtbl = gen_ivtbl_resize(0, obj_ivtbl->as.shape.numiv);
for (uint32_t i=0; i<obj_ivtbl->as.shape.numiv; i++) {
new_ivtbl->as.shape.ivptr[i] = obj_ivtbl->as.shape.ivptr[i];
RB_OBJ_WRITTEN(clone, Qundef, &new_ivtbl[i]); RB_OBJ_WRITTEN(clone, Qundef, &new_ivtbl[i]);
} }
}
/* /*
* c.ivtbl may change in gen_ivar_copy due to realloc, * c.ivtbl may change in gen_ivar_copy due to realloc,
@ -2063,7 +2086,7 @@ rb_ivar_count(VALUE obj)
struct gen_ivtbl *ivtbl; struct gen_ivtbl *ivtbl;
if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) { if (rb_gen_ivtbl_get(obj, 0, &ivtbl)) {
return gen_ivtbl_count(ivtbl); return gen_ivtbl_count(obj, ivtbl);
} }
} }
break; break;

View File

@ -16,8 +16,15 @@ struct gen_ivtbl {
#if !SHAPE_IN_BASIC_FLAGS #if !SHAPE_IN_BASIC_FLAGS
uint16_t shape_id; uint16_t shape_id;
#endif #endif
union {
struct {
uint32_t numiv; uint32_t numiv;
VALUE ivptr[FLEX_ARY_LEN]; VALUE ivptr[1];
} shape;
struct {
st_table *table;
} complex;
} as;
}; };
int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **); int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **);

View File

@ -1227,7 +1227,7 @@ vm_getivar(VALUE obj, ID id, const rb_iseq_t *iseq, IVC ic, const struct rb_call
#if !SHAPE_IN_BASIC_FLAGS #if !SHAPE_IN_BASIC_FLAGS
shape_id = ivtbl->shape_id; shape_id = ivtbl->shape_id;
#endif #endif
ivar_list = ivtbl->ivptr; ivar_list = ivtbl->as.shape.ivptr;
} }
else { else {
return default_value; return default_value;
@ -1456,7 +1456,7 @@ vm_setivar_default(VALUE obj, ID id, VALUE val, shape_id_t dest_shape_id, attr_i
return Qundef; return Qundef;
} }
VALUE *ptr = ivtbl->ivptr; VALUE *ptr = ivtbl->as.shape.ivptr;
RB_OBJ_WRITE(obj, &ptr[index], val); RB_OBJ_WRITE(obj, &ptr[index], val);