Use rb_gc_mark_and_move for imemo

This commit is contained in:
Peter Zhu 2024-02-16 13:35:33 -05:00
parent 20f03100d5
commit c184aa8740
5 changed files with 105 additions and 216 deletions

271
gc.c
View File

@ -4710,6 +4710,14 @@ is_live_object(rb_objspace_t *objspace, VALUE ptr)
} }
} }
static bool
moved_or_living_object_strictly_p(rb_objspace_t *objspace, VALUE obj)
{
return obj &&
is_pointer_to_heap(objspace, (void *)obj) &&
(is_live_object(objspace, obj) || BUILTIN_TYPE(obj) == T_MOVED);
}
static inline int static inline int
is_markable_object(VALUE obj) is_markable_object(VALUE obj)
{ {
@ -6675,38 +6683,44 @@ rb_mark_hash(st_table *tbl)
} }
static void static void
mark_method_entry(rb_objspace_t *objspace, const rb_method_entry_t *me) mark_and_move_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me, bool reference_updating)
{ {
const rb_method_definition_t *def = me->def; rb_method_definition_t *def = me->def;
gc_mark(objspace, me->owner); rb_gc_mark_and_move(&me->owner);
gc_mark(objspace, me->defined_class); rb_gc_mark_and_move(&me->defined_class);
if (def) { if (def) {
switch (def->type) { switch (def->type) {
case VM_METHOD_TYPE_ISEQ: case VM_METHOD_TYPE_ISEQ:
if (def->body.iseq.iseqptr) gc_mark(objspace, (VALUE)def->body.iseq.iseqptr); if (def->body.iseq.iseqptr) {
gc_mark(objspace, (VALUE)def->body.iseq.cref); rb_gc_mark_and_move_ptr(&def->body.iseq.iseqptr);
}
rb_gc_mark_and_move_ptr(&def->body.iseq.cref);
if (def->iseq_overload && me->defined_class) { if (!reference_updating) {
// it can be a key of "overloaded_cme" table if (def->iseq_overload && me->defined_class) {
// so it should be pinned. // it can be a key of "overloaded_cme" table
gc_mark_and_pin(objspace, (VALUE)me); // so it should be pinned.
gc_mark_and_pin(objspace, (VALUE)me);
}
} }
break; break;
case VM_METHOD_TYPE_ATTRSET: case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR: case VM_METHOD_TYPE_IVAR:
gc_mark(objspace, def->body.attr.location); rb_gc_mark_and_move(&def->body.attr.location);
break; break;
case VM_METHOD_TYPE_BMETHOD: case VM_METHOD_TYPE_BMETHOD:
gc_mark(objspace, def->body.bmethod.proc); rb_gc_mark_and_move(&def->body.bmethod.proc);
if (def->body.bmethod.hooks) rb_hook_list_mark(def->body.bmethod.hooks); if (!reference_updating) {
if (def->body.bmethod.hooks) rb_hook_list_mark(def->body.bmethod.hooks);
}
break; break;
case VM_METHOD_TYPE_ALIAS: case VM_METHOD_TYPE_ALIAS:
gc_mark(objspace, (VALUE)def->body.alias.original_me); rb_gc_mark_and_move_ptr(&def->body.alias.original_me);
return; return;
case VM_METHOD_TYPE_REFINED: case VM_METHOD_TYPE_REFINED:
gc_mark(objspace, (VALUE)def->body.refined.orig_me); rb_gc_mark_and_move_ptr(&def->body.refined.orig_me);
break; break;
case VM_METHOD_TYPE_CFUNC: case VM_METHOD_TYPE_CFUNC:
case VM_METHOD_TYPE_ZSUPER: case VM_METHOD_TYPE_ZSUPER:
@ -7189,65 +7203,80 @@ gc_mark_set_parent(rb_objspace_t *objspace, VALUE obj)
} }
static void static void
gc_mark_imemo(rb_objspace_t *objspace, VALUE obj) gc_mark_and_move_imemo(rb_objspace_t *objspace, VALUE obj, bool reference_updating)
{ {
switch (imemo_type(obj)) { switch (imemo_type(obj)) {
case imemo_env: case imemo_env:
{ {
const rb_env_t *env = (const rb_env_t *)obj; rb_env_t *env = (rb_env_t *)obj;
if (LIKELY(env->ep)) { if (LIKELY(env->ep)) {
// just after newobj() can be NULL here. // just after newobj() can be NULL here.
GC_ASSERT(env->ep[VM_ENV_DATA_INDEX_ENV] == obj); GC_ASSERT(rb_gc_location(env->ep[VM_ENV_DATA_INDEX_ENV]) == rb_gc_location(obj));
GC_ASSERT(VM_ENV_ESCAPED_P(env->ep)); GC_ASSERT(reference_updating || VM_ENV_ESCAPED_P(env->ep));
rb_gc_mark_values((long)env->env_size, env->env);
VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED); for (unsigned int i = 0; i < env->env_size; i++) {
gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env)); rb_gc_mark_and_move((VALUE *)&env->env[i]);
gc_mark(objspace, (VALUE)env->iseq); }
rb_gc_mark_and_move_ptr(&env->iseq);
if (reference_updating) {
UPDATE_IF_MOVED(objspace, env->ep[VM_ENV_DATA_INDEX_ENV]);
}
else {
VM_ENV_FLAGS_SET(env->ep, VM_ENV_FLAG_WB_REQUIRED);
gc_mark(objspace, (VALUE)rb_vm_env_prev_env(env));
}
} }
} }
return; return;
case imemo_cref: case imemo_cref:
gc_mark(objspace, RANY(obj)->as.imemo.cref.klass_or_self); rb_gc_mark_and_move(&RANY(obj)->as.imemo.cref.klass_or_self);
gc_mark(objspace, (VALUE)RANY(obj)->as.imemo.cref.next); rb_gc_mark_and_move_ptr(&RANY(obj)->as.imemo.cref.next);
gc_mark(objspace, RANY(obj)->as.imemo.cref.refinements); rb_gc_mark_and_move(&RANY(obj)->as.imemo.cref.refinements);
return; return;
case imemo_svar: case imemo_svar:
gc_mark(objspace, RANY(obj)->as.imemo.svar.cref_or_me); rb_gc_mark_and_move((VALUE *)&RANY(obj)->as.imemo.svar.cref_or_me);
gc_mark(objspace, RANY(obj)->as.imemo.svar.lastline); rb_gc_mark_and_move((VALUE *)&RANY(obj)->as.imemo.svar.lastline);
gc_mark(objspace, RANY(obj)->as.imemo.svar.backref); rb_gc_mark_and_move((VALUE *)&RANY(obj)->as.imemo.svar.backref);
gc_mark(objspace, RANY(obj)->as.imemo.svar.others); rb_gc_mark_and_move((VALUE *)&RANY(obj)->as.imemo.svar.others);
return; return;
case imemo_throw_data: case imemo_throw_data:
gc_mark(objspace, RANY(obj)->as.imemo.throw_data.throw_obj); rb_gc_mark_and_move((VALUE *)&RANY(obj)->as.imemo.throw_data.throw_obj);
return; return;
case imemo_ifunc: case imemo_ifunc:
gc_mark_maybe(objspace, (VALUE)RANY(obj)->as.imemo.ifunc.data); if (!reference_updating) {
gc_mark_maybe(objspace, (VALUE)RANY(obj)->as.imemo.ifunc.data);
}
return; return;
case imemo_memo: case imemo_memo:
gc_mark(objspace, RANY(obj)->as.imemo.memo.v1); rb_gc_mark_and_move((VALUE *)&RANY(obj)->as.imemo.memo.v1);
gc_mark(objspace, RANY(obj)->as.imemo.memo.v2); rb_gc_mark_and_move((VALUE *)&RANY(obj)->as.imemo.memo.v2);
gc_mark_maybe(objspace, RANY(obj)->as.imemo.memo.u3.value); if (!reference_updating) {
gc_mark_maybe(objspace, RANY(obj)->as.imemo.memo.u3.value);
}
return; return;
case imemo_ment: case imemo_ment:
mark_method_entry(objspace, &RANY(obj)->as.imemo.ment); mark_and_move_method_entry(objspace, &RANY(obj)->as.imemo.ment, reference_updating);
return; return;
case imemo_iseq: case imemo_iseq:
rb_iseq_mark_and_move((rb_iseq_t *)obj, false); rb_iseq_mark_and_move((rb_iseq_t *)obj, reference_updating);
return; return;
case imemo_tmpbuf: case imemo_tmpbuf:
{ {
const rb_imemo_tmpbuf_t *m = &RANY(obj)->as.imemo.alloc; if (!reference_updating) {
do { const rb_imemo_tmpbuf_t *m = &RANY(obj)->as.imemo.alloc;
rb_gc_mark_locations(m->ptr, m->ptr + m->cnt); do {
} while ((m = m->next) != NULL); rb_gc_mark_locations(m->ptr, m->ptr + m->cnt);
} while ((m = m->next) != NULL);
}
} }
return; return;
case imemo_ast: case imemo_ast:
rb_ast_mark(&RANY(obj)->as.imemo.ast); rb_ast_mark_and_move(&RANY(obj)->as.imemo.ast, reference_updating);
return; return;
case imemo_parser_strterm: case imemo_parser_strterm:
return;
case imemo_callinfo: case imemo_callinfo:
return; return;
case imemo_callcache: case imemo_callcache:
@ -7272,15 +7301,32 @@ gc_mark_imemo(rb_objspace_t *objspace, VALUE obj)
*/ */
{ {
const struct rb_callcache *cc = (const struct rb_callcache *)obj; const struct rb_callcache *cc = (const struct rb_callcache *)obj;
if (vm_cc_super_p(cc) || vm_cc_refinement_p(cc)) { if (reference_updating) {
gc_mark(objspace, (VALUE)cc->cme_); if (!cc->klass) {
// already invalidated
}
else {
if (moved_or_living_object_strictly_p(objspace, cc->klass) &&
moved_or_living_object_strictly_p(objspace, (VALUE)cc->cme_)) {
UPDATE_IF_MOVED(objspace, cc->klass);
TYPED_UPDATE_IF_MOVED(objspace, struct rb_callable_method_entry_struct *, cc->cme_);
}
else {
vm_cc_invalidate(cc);
}
}
}
else {
if (vm_cc_super_p(cc) || vm_cc_refinement_p(cc)) {
gc_mark(objspace, (VALUE)cc->cme_);
}
} }
} }
return; return;
case imemo_constcache: case imemo_constcache:
{ {
const struct iseq_inline_constant_cache_entry *ice = (struct iseq_inline_constant_cache_entry *)obj; struct iseq_inline_constant_cache_entry *ice = (struct iseq_inline_constant_cache_entry *)obj;
gc_mark(objspace, ice->value); rb_gc_mark_and_move(&ice->value);
} }
return; return;
#if VM_CHECK_MODE > 0 #if VM_CHECK_MODE > 0
@ -7328,7 +7374,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj)
break; break;
case T_IMEMO: case T_IMEMO:
gc_mark_imemo(objspace, obj); gc_mark_and_move_imemo(objspace, obj, false);
return; return;
default: default:
@ -10359,46 +10405,6 @@ gc_ref_update_hash(rb_objspace_t * objspace, VALUE v)
rb_hash_stlike_foreach_with_replace(v, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace); rb_hash_stlike_foreach_with_replace(v, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace);
} }
static void
gc_ref_update_method_entry(rb_objspace_t *objspace, rb_method_entry_t *me)
{
rb_method_definition_t *def = me->def;
UPDATE_IF_MOVED(objspace, me->owner);
UPDATE_IF_MOVED(objspace, me->defined_class);
if (def) {
switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
if (def->body.iseq.iseqptr) {
TYPED_UPDATE_IF_MOVED(objspace, rb_iseq_t *, def->body.iseq.iseqptr);
}
TYPED_UPDATE_IF_MOVED(objspace, rb_cref_t *, def->body.iseq.cref);
break;
case VM_METHOD_TYPE_ATTRSET:
case VM_METHOD_TYPE_IVAR:
UPDATE_IF_MOVED(objspace, def->body.attr.location);
break;
case VM_METHOD_TYPE_BMETHOD:
UPDATE_IF_MOVED(objspace, def->body.bmethod.proc);
break;
case VM_METHOD_TYPE_ALIAS:
TYPED_UPDATE_IF_MOVED(objspace, struct rb_method_entry_struct *, def->body.alias.original_me);
return;
case VM_METHOD_TYPE_REFINED:
TYPED_UPDATE_IF_MOVED(objspace, struct rb_method_entry_struct *, def->body.refined.orig_me);
break;
case VM_METHOD_TYPE_CFUNC:
case VM_METHOD_TYPE_ZSUPER:
case VM_METHOD_TYPE_MISSING:
case VM_METHOD_TYPE_OPTIMIZED:
case VM_METHOD_TYPE_UNDEF:
case VM_METHOD_TYPE_NOTIMPLEMENTED:
break;
}
}
}
static void static void
gc_update_values(rb_objspace_t *objspace, long n, VALUE *values) gc_update_values(rb_objspace_t *objspace, long n, VALUE *values)
{ {
@ -10415,93 +10421,6 @@ rb_gc_update_values(long n, VALUE *values)
gc_update_values(&rb_objspace, n, values); gc_update_values(&rb_objspace, n, values);
} }
static bool
moved_or_living_object_strictly_p(rb_objspace_t *objspace, VALUE obj)
{
return obj &&
is_pointer_to_heap(objspace, (void *)obj) &&
(is_live_object(objspace, obj) || BUILTIN_TYPE(obj) == T_MOVED);
}
static void
gc_ref_update_imemo(rb_objspace_t *objspace, VALUE obj)
{
switch (imemo_type(obj)) {
case imemo_env:
{
rb_env_t *env = (rb_env_t *)obj;
if (LIKELY(env->ep)) {
// just after newobj() can be NULL here.
TYPED_UPDATE_IF_MOVED(objspace, rb_iseq_t *, env->iseq);
UPDATE_IF_MOVED(objspace, env->ep[VM_ENV_DATA_INDEX_ENV]);
gc_update_values(objspace, (long)env->env_size, (VALUE *)env->env);
}
}
break;
case imemo_cref:
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.cref.klass_or_self);
TYPED_UPDATE_IF_MOVED(objspace, struct rb_cref_struct *, RANY(obj)->as.imemo.cref.next);
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.cref.refinements);
break;
case imemo_svar:
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.cref_or_me);
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.lastline);
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.backref);
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.svar.others);
break;
case imemo_throw_data:
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.throw_data.throw_obj);
break;
case imemo_ifunc:
break;
case imemo_memo:
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.memo.v1);
UPDATE_IF_MOVED(objspace, RANY(obj)->as.imemo.memo.v2);
break;
case imemo_ment:
gc_ref_update_method_entry(objspace, &RANY(obj)->as.imemo.ment);
break;
case imemo_iseq:
rb_iseq_mark_and_move((rb_iseq_t *)obj, true);
break;
case imemo_ast:
rb_ast_update_references((rb_ast_t *)obj);
break;
case imemo_callcache:
{
const struct rb_callcache *cc = (const struct rb_callcache *)obj;
if (!cc->klass) {
// already invalidated
}
else {
if (moved_or_living_object_strictly_p(objspace, cc->klass) &&
moved_or_living_object_strictly_p(objspace, (VALUE)cc->cme_)) {
UPDATE_IF_MOVED(objspace, cc->klass);
TYPED_UPDATE_IF_MOVED(objspace, struct rb_callable_method_entry_struct *, cc->cme_);
}
else {
vm_cc_invalidate(cc);
}
}
}
break;
case imemo_constcache:
{
const struct iseq_inline_constant_cache_entry *ice = (struct iseq_inline_constant_cache_entry *)obj;
UPDATE_IF_MOVED(objspace, ice->value);
}
break;
case imemo_parser_strterm:
case imemo_tmpbuf:
case imemo_callinfo:
break;
default:
rb_bug("not reachable %d", imemo_type(obj));
break;
}
}
static enum rb_id_table_iterator_result static enum rb_id_table_iterator_result
check_id_table_move(VALUE value, void *data) check_id_table_move(VALUE value, void *data)
{ {
@ -10754,7 +10673,7 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
break; break;
case T_IMEMO: case T_IMEMO:
gc_ref_update_imemo(objspace, obj); gc_mark_and_move_imemo(objspace, obj, true);
return; return;
case T_NIL: case T_NIL:

44
node.c
View File

@ -86,7 +86,7 @@ rb_node_buffer_new(void)
#define ruby_xrealloc(var,size) (ast->node_buffer->config->realloc_n((void *)var, 1, size)) #define ruby_xrealloc(var,size) (ast->node_buffer->config->realloc_n((void *)var, 1, size))
#define rb_gc_mark ast->node_buffer->config->gc_mark #define rb_gc_mark ast->node_buffer->config->gc_mark
#define rb_gc_location ast->node_buffer->config->gc_location #define rb_gc_location ast->node_buffer->config->gc_location
#define rb_gc_mark_movable ast->node_buffer->config->gc_mark_movable #define rb_gc_mark_and_move ast->node_buffer->config->gc_mark_and_move
#undef Qnil #undef Qnil
#define Qnil ast->node_buffer->config->qnil #define Qnil ast->node_buffer->config->qnil
#define Qtrue ast->node_buffer->config->qtrue #define Qtrue ast->node_buffer->config->qtrue
@ -367,7 +367,7 @@ iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, vo
} }
static void static void
mark_ast_value(rb_ast_t *ast, void *ctx, NODE *node) mark_and_move_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
{ {
#ifdef UNIVERSAL_PARSER #ifdef UNIVERSAL_PARSER
bug_report_func rb_bug = ast->node_buffer->config->bug; bug_report_func rb_bug = ast->node_buffer->config->bug;
@ -376,53 +376,23 @@ mark_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
switch (nd_type(node)) { switch (nd_type(node)) {
case NODE_MATCH: case NODE_MATCH:
case NODE_LIT: case NODE_LIT:
rb_gc_mark_movable(RNODE_LIT(node)->nd_lit); rb_gc_mark_and_move(&RNODE_LIT(node)->nd_lit);
break; break;
default: default:
rb_bug("unreachable node %s", ruby_node_name(nd_type(node))); rb_bug("unreachable node %s", ruby_node_name(nd_type(node)));
} }
} }
static void
update_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
{
#ifdef UNIVERSAL_PARSER
bug_report_func rb_bug = ast->node_buffer->config->bug;
#endif
switch (nd_type(node)) {
case NODE_MATCH:
case NODE_LIT:
RNODE_LIT(node)->nd_lit = rb_gc_location(RNODE_LIT(node)->nd_lit);
break;
default:
rb_bug("unreachable");
}
}
void void
rb_ast_update_references(rb_ast_t *ast) rb_ast_mark_and_move(rb_ast_t *ast, bool reference_updating)
{ {
if (ast->node_buffer) { if (ast->node_buffer) {
ast->node_buffer->tokens = rb_gc_location(ast->node_buffer->tokens); rb_gc_mark_and_move(&ast->node_buffer->tokens);
node_buffer_t *nb = ast->node_buffer; node_buffer_t *nb = ast->node_buffer;
iterate_node_values(ast, &nb->markable, update_ast_value, NULL); iterate_node_values(ast, &nb->markable, mark_and_move_ast_value, NULL);
if (ast->body.script_lines) ast->body.script_lines = rb_gc_location(ast->body.script_lines); if (ast->body.script_lines) rb_gc_mark_and_move(&ast->body.script_lines);
}
}
void
rb_ast_mark(rb_ast_t *ast)
{
if (ast->node_buffer) {
rb_gc_mark_movable(ast->node_buffer->tokens);
node_buffer_t *nb = ast->node_buffer;
iterate_node_values(ast, &nb->markable, mark_ast_value, NULL);
if (ast->body.script_lines) rb_gc_mark_movable(ast->body.script_lines);
} }
} }

2
node.h
View File

@ -62,7 +62,7 @@ void rb_ast_node_type_change(NODE *n, enum node_type type);
const char *ruby_node_name(int node); const char *ruby_node_name(int node);
void rb_node_init(NODE *n, enum node_type type); void rb_node_init(NODE *n, enum node_type type);
void rb_ast_mark(rb_ast_t*); void rb_ast_mark_and_move(rb_ast_t *ast, bool reference_updating);
void rb_ast_update_references(rb_ast_t*); void rb_ast_update_references(rb_ast_t*);
void rb_ast_free(rb_ast_t*); void rb_ast_free(rb_ast_t*);
void rb_ast_set_tokens(rb_ast_t*, VALUE); void rb_ast_set_tokens(rb_ast_t*, VALUE);

View File

@ -640,7 +640,7 @@ static const rb_parser_config_t rb_global_parser_config = {
.gc_register_mark_object = rb_gc_register_mark_object, .gc_register_mark_object = rb_gc_register_mark_object,
.gc_guard = gc_guard, .gc_guard = gc_guard,
.gc_mark = rb_gc_mark, .gc_mark = rb_gc_mark,
.gc_mark_movable = rb_gc_mark_movable, .gc_mark_and_move = rb_gc_mark_and_move,
.gc_location = rb_gc_location, .gc_location = rb_gc_location,
.reg_compile = rb_reg_compile, .reg_compile = rb_reg_compile,

View File

@ -1366,7 +1366,7 @@ typedef struct rb_parser_config_struct {
void (*gc_register_mark_object)(VALUE object); void (*gc_register_mark_object)(VALUE object);
void (*gc_guard)(VALUE); void (*gc_guard)(VALUE);
void (*gc_mark)(VALUE); void (*gc_mark)(VALUE);
void (*gc_mark_movable)(VALUE ptr); void (*gc_mark_and_move)(VALUE *ptr);
VALUE (*gc_location)(VALUE value); VALUE (*gc_location)(VALUE value);
/* Re */ /* Re */