ruby-changes:55368
From: tenderlove <ko1@a...>
Date: Wed, 17 Apr 2019 12:17:47 +0900 (JST)
Subject: [ruby-changes:55368] tenderlove:r67576 (trunk): Adding `GC.compact` and compacting GC support.
tenderlove 2019-04-17 12:17:25 +0900 (Wed, 17 Apr 2019) New Revision: 67576 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=67576 Log: Adding `GC.compact` and compacting GC support. This commit adds the new method `GC.compact` and compacting GC support. Please see this issue for caveats: https://bugs.ruby-lang.org/issues/15626 [Feature #15626] Added files: trunk/test/ruby/test_gc_compact.rb Modified files: trunk/NEWS trunk/class.c trunk/constant.h trunk/ext/json/generator/generator.c trunk/ext/json/parser/parser.c trunk/gc.c trunk/gc.h trunk/hash.c trunk/id_table.c trunk/id_table.h trunk/include/ruby/intern.h trunk/include/ruby/ruby.h trunk/include/ruby/st.h trunk/internal.h trunk/iseq.c trunk/iseq.h trunk/method.h trunk/st.c trunk/symbol.c trunk/symbol.h trunk/thread.c trunk/transient_heap.c trunk/transient_heap.h trunk/variable.c trunk/vm.c trunk/vm_core.h trunk/vm_eval.c Index: symbol.c =================================================================== --- symbol.c (revision 67575) +++ symbol.c (revision 67576) @@ -60,12 +60,7 @@ enum id_entry_type { https://github.com/ruby/ruby/blob/trunk/symbol.c#L60 ID_ENTRY_SIZE }; -static struct symbols { - rb_id_serial_t last_id; - st_table *str_sym; - VALUE ids; - VALUE dsymbol_fstr_hash; -} global_symbols = {tNEXT_ID-1}; +rb_symbols_t global_symbols = {tNEXT_ID-1}; static const struct st_hash_type symhash = { rb_str_hash_cmp, Index: symbol.h =================================================================== --- symbol.h (revision 67575) +++ symbol.h (revision 67576) @@ -54,6 +54,13 @@ id_type(ID id) https://github.com/ruby/ruby/blob/trunk/symbol.h#L54 typedef uint32_t rb_id_serial_t; +typedef struct { + rb_id_serial_t last_id; + st_table *str_sym; + VALUE ids; + VALUE dsymbol_fstr_hash; +} rb_symbols_t; + static inline rb_id_serial_t rb_id_to_serial(ID id) { Index: method.h =================================================================== --- method.h (revision 67575) +++ method.h (revision 67576) @@ -40,9 +40,9 @@ typedef struct rb_scope_visi_struct { https://github.com/ruby/ruby/blob/trunk/method.h#L40 /*! CREF (Class REFerence) */ typedef struct rb_cref_struct { VALUE flags; - const VALUE refinements; - const VALUE klass; - struct rb_cref_struct * const next; + VALUE refinements; + VALUE klass; + struct rb_cref_struct * next; const rb_scope_visibility_t scope_visi; } rb_cref_t; @@ -50,10 +50,10 @@ typedef struct rb_cref_struct { https://github.com/ruby/ruby/blob/trunk/method.h#L50 typedef struct rb_method_entry_struct { VALUE flags; - const VALUE defined_class; + VALUE defined_class; struct rb_method_definition_struct * const def; ID called_id; - const VALUE owner; + VALUE owner; } rb_method_entry_t; typedef struct rb_callable_method_entry_struct { /* same fields with rb_method_entry_t */ @@ -123,8 +123,8 @@ typedef struct rb_iseq_struct rb_iseq_t; https://github.com/ruby/ruby/blob/trunk/method.h#L123 #endif typedef struct rb_method_iseq_struct { - const rb_iseq_t * const iseqptr; /*!< iseq pointer, should be separated from iseqval */ - rb_cref_t * const cref; /*!< class reference, should be marked */ + rb_iseq_t * iseqptr; /*!< iseq pointer, should be separated from iseqval */ + rb_cref_t * cref; /*!< class reference, should be marked */ } rb_method_iseq_t; /* check rb_add_method_iseq() when modify the fields */ typedef struct rb_method_cfunc_struct { @@ -135,20 +135,20 @@ typedef struct rb_method_cfunc_struct { https://github.com/ruby/ruby/blob/trunk/method.h#L135 typedef struct rb_method_attr_struct { ID id; - const VALUE location; /* should be marked */ + VALUE location; /* should be marked */ } rb_method_attr_t; typedef struct rb_method_alias_struct { - const struct rb_method_entry_struct * const original_me; /* original_me->klass is original owner */ + struct rb_method_entry_struct * original_me; /* original_me->klass is original owner */ } rb_method_alias_t; typedef struct rb_method_refined_struct { - const struct rb_method_entry_struct * const orig_me; - const VALUE owner; + struct rb_method_entry_struct * orig_me; + VALUE owner; } rb_method_refined_t; typedef struct rb_method_bmethod_struct { - const VALUE proc; /* should be marked */ + VALUE proc; /* should be marked */ struct rb_hook_list_struct *hooks; } rb_method_bmethod_t; Index: include/ruby/intern.h =================================================================== --- include/ruby/intern.h (revision 67575) +++ include/ruby/intern.h (revision 67576) @@ -507,10 +507,15 @@ COLDFUNC NORETURN(void rb_memerror(void) https://github.com/ruby/ruby/blob/trunk/include/ruby/intern.h#L507 PUREFUNC(int rb_during_gc(void)); void rb_gc_mark_locations(const VALUE*, const VALUE*); void rb_mark_tbl(struct st_table*); +void rb_mark_tbl_no_pin(struct st_table*); +void rb_gc_update_tbl_refs(st_table *ptr); void rb_mark_set(struct st_table*); void rb_mark_hash(struct st_table*); +void rb_update_st_references(struct st_table *ht); void rb_gc_mark_maybe(VALUE); void rb_gc_mark(VALUE); +void rb_gc_mark_no_pin(VALUE); +VALUE rb_gc_new_location(VALUE); void rb_gc_force_recycle(VALUE); void rb_gc(void); void rb_gc_copy_finalizer(VALUE,VALUE); @@ -622,6 +627,7 @@ VALUE rb_obj_trust(VALUE); https://github.com/ruby/ruby/blob/trunk/include/ruby/intern.h#L627 VALUE rb_obj_freeze(VALUE); PUREFUNC(VALUE rb_obj_frozen_p(VALUE)); VALUE rb_obj_id(VALUE); +VALUE rb_memory_id(VALUE); VALUE rb_obj_class(VALUE); PUREFUNC(VALUE rb_class_real(VALUE)); PUREFUNC(VALUE rb_class_inherited_p(VALUE, VALUE)); Index: include/ruby/ruby.h =================================================================== --- include/ruby/ruby.h (revision 67575) +++ include/ruby/ruby.h (revision 67576) @@ -512,6 +512,7 @@ enum ruby_value_type { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L512 RUBY_T_NODE = 0x1b, RUBY_T_ICLASS = 0x1c, RUBY_T_ZOMBIE = 0x1d, + RUBY_T_MOVED = 0x1e, RUBY_T_MASK = 0x1f }; @@ -542,6 +543,7 @@ enum ruby_value_type { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L543 #define T_UNDEF RUBY_T_UNDEF #define T_NODE RUBY_T_NODE #define T_ZOMBIE RUBY_T_ZOMBIE +#define T_MOVED RUBY_T_MOVED #define T_MASK RUBY_T_MASK #define RB_BUILTIN_TYPE(x) (int)(((struct RBasic*)(x))->flags & RUBY_T_MASK) @@ -845,6 +847,7 @@ enum ruby_fl_type { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L847 RUBY_FL_FINALIZE = (1<<7), RUBY_FL_TAINT = (1<<8), RUBY_FL_UNTRUSTED = RUBY_FL_TAINT, + RUBY_FL_SEEN_OBJ_ID = (1<<9), RUBY_FL_EXIVAR = (1<<10), RUBY_FL_FREEZE = (1<<11), @@ -883,7 +886,7 @@ enum ruby_fl_type { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L886 struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { VALUE flags; - const VALUE klass; + VALUE klass; }; VALUE rb_obj_hide(VALUE obj); @@ -1105,7 +1108,7 @@ struct RArray { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1108 struct RRegexp { struct RBasic basic; struct re_pattern_buffer *ptr; - const VALUE src; + VALUE src; unsigned long usecnt; }; #define RREGEXP_PTR(r) (RREGEXP(r)->ptr) @@ -1144,7 +1147,8 @@ struct rb_data_type_struct { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1147 void (*dmark)(void*); void (*dfree)(void*); size_t (*dsize)(const void *); - void *reserved[2]; /* For future extension. + void (*dcompact)(void*); + void *reserved[1]; /* For future extension. This array *must* be filled with ZERO. */ } function; const rb_data_type_t *parent; @@ -1255,6 +1259,7 @@ int rb_big_sign(VALUE); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1259 #define RBIGNUM_NEGATIVE_P(b) (RBIGNUM_SIGN(b)==0) #define R_CAST(st) (struct st*) +#define RMOVED(obj) (R_CAST(RMoved)(obj)) #define RBASIC(obj) (R_CAST(RBasic)(obj)) #define ROBJECT(obj) (R_CAST(RObject)(obj)) #define RCLASS(obj) (R_CAST(RClass)(obj)) @@ -1273,6 +1278,7 @@ int rb_big_sign(VALUE); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1278 #define FL_FINALIZE ((VALUE)RUBY_FL_FINALIZE) #define FL_TAINT ((VALUE)RUBY_FL_TAINT) #define FL_UNTRUSTED ((VALUE)RUBY_FL_UNTRUSTED) +#define FL_SEEN_OBJ_ID ((VALUE)RUBY_FL_SEEN_OBJ_ID) #define FL_EXIVAR ((VALUE)RUBY_FL_EXIVAR) #define FL_FREEZE ((VALUE)RUBY_FL_FREEZE) Index: include/ruby/st.h =================================================================== --- include/ruby/st.h (revision 67575) +++ include/ruby/st.h (revision 67576) @@ -96,7 +96,7 @@ struct st_table { https://github.com/ruby/ruby/blob/trunk/include/ruby/st.h#L96 #define st_is_member(table,key) st_lookup((table),(key),(st_data_t *)0) -enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK}; +enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK, ST_REPLACE}; st_table *st_init_table(const struct st_hash_type *); st_table *st_init_table_with_size(const struct st_hash_type *, st_index_t); @@ -118,6 +118,7 @@ typedef int st_update_callback_func(st_d https://github.com/ruby/ruby/blob/trunk/include/ruby/st.h#L118 * results of hash() are same and compare() returns 0, otherwise the * behavior is undefined */ int st_update(st_table *table, st_data_t key, st_update_callback_func *func, st_data_t arg); +int st_foreach_with_replace(st_table *tab, int (*func)(ANYARGS), st_update_callback_func *replace, st_data_t arg); int st_foreach(st_table *, int (*)(ANYARGS), st_data_t); int st_foreach_check(st_table *, int (*)(ANYARGS), st_data_t, st_data_t); st_index_t st_keys(st_table *table, st_data_t *keys, st_index_t size); Index: id_table.c =================================================================== --- id_table.c (revision 67575) +++ id_table.c (revision 67576) @@ -267,6 +267,28 @@ rb_id_table_delete(struct rb_id_table *t https://github.com/ruby/ruby/blob/trunk/id_table.c#L267 } void +rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data) +{ + int i, capa = tbl->capa; + + for (i=0; i<capa; i++) { + if (ITEM_KEY_ISSET(tbl, i)) { + const id_key_t key = ITEM_GET_KEY(tbl, i); + enum rb_id_table_iterator_result ret = (*func)(Qundef, tbl->items[i].val, data); + assert(key != 0); + + if (ret == ID_TABLE_REPLACE) { + VALUE val = tbl->items[i].val; + ret = (*replace)(NULL, &val, data, TRUE); + tbl->items[i].val = val; + } + else if (ret == ID_TABLE_STOP) + return; + } + } +} + +void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data) { int i, capa = tbl->capa; Index: id_table.h =================================================================== --- id_table.h (revision 67575) +++ id_table.h (revision 67576) @@ -9,6 +9,7 @@ enum rb_id_table_iterator_result { https://github.com/ruby/ruby/blob/trunk/id_table.h#L9 ID_TABLE_CONTINUE = ST_CONTINUE, ID_TABLE_STOP = ST_STOP, ID_TABLE_DELETE = ST_DELETE, + ID_TABLE_REPLACE = ST_REPLACE, ID_TABLE_ITERATOR_RESULT_END }; @@ -23,9 +24,11 @@ int rb_id_table_insert(struct rb_id_tabl https://github.com/ruby/ruby/blob/trunk/id_table.h#L24 int rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp); int rb_id_table_delete(struct rb_id_table *tbl, ID id); +typedef enum rb_id_table_iterator_result rb_id_table_update_callback_func_t(ID *id, VALUE *val, void *data, int existing); typedef enum rb_id_table_iterator_result rb_id_table_foreach_func_t(ID id, VALUE val, void *data); typedef enum rb_id_table_iterator_result rb_id_table_foreach_values_func_t(VALUE val, void *data); void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data); +void rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data); void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data); #endif /* RUBY_ID_TABLE_H */ Index: variable.c =================================================================== --- variable.c (revision 67575) +++ variable.c (revision 67576) @@ -1201,6 +1201,16 @@ rb_mark_generic_ivar(VALUE obj) https://github.com/ruby/ruby/blob/trunk/variable.c#L1201 } void +rb_mv_generic_ivar(VALUE rsrc, VALUE dst) +{ + st_data_t key = (st_data_t)rsrc; + struct gen_ivtbl *ivtbl; + + if (st_delete(generic_iv_tbl, &key, (st_data_t *)&ivtbl)) + st_insert(generic_iv_tbl, (st_data_t)dst, (st_data_t)ivtbl); +} + +void rb_free_generic_ivar(VALUE obj) { st_data_t key = (st_data_t)obj; @@ -1950,7 +1960,7 @@ rb_mod_const_missing(VALUE klass, VALUE https://github.com/ruby/ruby/blob/trunk/variable.c#L1960 static void autoload_mark(void *ptr) { - rb_mark_tbl((st_table *)ptr); + rb_mark_tbl_no_pin((st_table *)ptr); } static void @@ -1966,9 +1976,15 @@ autoload_memsize(const void *ptr) https://github.com/ruby/ruby/blob/trunk/variable.c#L1976 return st_memsize(tbl); } +static void +autoload_compact(void *ptr) +{ + rb_gc_update_tbl_refs((st_table *)ptr); +} + static const rb_data_type_t autoload_data_type = { "autoload", - {autoload_mark, autoload_free, autoload_memsize,}, + {autoload_mark, autoload_free, autoload_memsize, autoload_compact,}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; @@ -2015,11 +2031,18 @@ struct autoload_data_i { https://github.com/ruby/ruby/blob/trunk/variable.c#L2031 }; static void +autoload_i_compact(void *ptr) +{ + struct autoload_data_i *p = ptr; + p->feature = rb_gc_new_location(p->feature); +} + +static void autoload_i_mark(void *ptr) { struct autoload_data_i *p = ptr; - rb_gc_mark(p->feature); + rb_gc_mark_no_pin(p->feature); /* allow GC to free us if no modules refer to this via autoload_const.ad */ if (list_empty(&p->constants)) { @@ -2046,7 +2069,7 @@ autoload_i_memsize(const void *ptr) https://github.com/ruby/ruby/blob/trunk/variable.c#L2069 static const rb_data_type_t autoload_data_i_type = { "autoload_i", - {autoload_i_mark, autoload_i_free, autoload_i_memsize,}, + {autoload_i_mark, autoload_i_free, autoload_i_memsize, autoload_i_compact}, 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; @@ -2971,6 +2994,7 @@ rb_define_const(VALUE klass, const char https://github.com/ruby/ruby/blob/trunk/variable.c#L2994 if (!rb_is_const_id(id)) { rb_warn("rb_define_const: invalid name `%s' for constant", name); } + rb_gc_register_mark_object(val); rb_const_set(klass, id, val); } Index: vm_core.h =================================================================== --- vm_core.h (revision 67575) +++ vm_core.h (revision 67576) @@ -346,7 +346,7 @@ struct rb_iseq_constant_body { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L346 } type; /* instruction sequence type */ unsigned int iseq_size; - const VALUE *iseq_encoded; /* encoded iseq (insn addr and operands) */ + VALUE *iseq_encoded; /* encoded iseq (insn addr and operands) */ /** * parameter information @@ -414,7 +414,7 @@ struct rb_iseq_constant_body { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L414 int bits_start; int rest_start; const ID *table; - const VALUE *default_values; + VALUE *default_values; } *keyword; } param; @@ -433,7 +433,7 @@ struct rb_iseq_constant_body { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L433 const ID *local_table; /* must free */ /* catch table */ - const struct iseq_catch_table *catch_table; + struct iseq_catch_table *catch_table; /* for child iseq */ const struct rb_iseq_struct *parent_iseq; @@ -1029,7 +1029,7 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/vm_core.h#L1029 typedef struct { VALUE flags; /* imemo header */ - const rb_iseq_t *iseq; + rb_iseq_t *iseq; const VALUE *ep; const VALUE *env; unsigned int env_size; Index: iseq.c =================================================================== --- iseq.c (revision 67575) +++ iseq.c (revision 67576) @@ -137,11 +137,11 @@ rb_vm_insn_null_translator(const void *a https://github.com/ruby/ruby/blob/trunk/iseq.c#L137 return (VALUE)addr; } -typedef void iseq_value_itr_t(void *ctx, VALUE obj); +typedef VALUE iseq_value_itr_t(void *ctx, VALUE obj); typedef VALUE rb_vm_insns_translator_t(const void *addr); static int -iseq_extract_values(const VALUE *code, size_t pos, iseq_value_itr_t * func, void *data, rb_vm_insns_translator_t * translator) +iseq_extract_values(VALUE *code, size_t pos, iseq_value_itr_t * func, void *data, rb_vm_insns_translator_t * translator) { VALUE insn = translator((void *)code[pos]); int len = insn_len(insn); @@ -157,7 +157,10 @@ iseq_extract_values(const VALUE *code, s https://github.com/ruby/ruby/blob/trunk/iseq.c#L157 { VALUE op = code[pos + op_no + 1]; if (!SPECIAL_CONST_P(op)) { - func(data, op); + VALUE newop = func(data, op); + if (newop != op) { + code[pos + op_no + 1] = newop; + } } break; } @@ -165,7 +168,10 @@ iseq_extract_values(const VALUE *code, s https://github.com/ruby/ruby/blob/trunk/iseq.c#L168 { union iseq_inline_storage_entry *const is = (union iseq_inline_storage_entry *)code[pos + op_no + 1]; if (is->once.value) { - func(data, is->once.value); + VALUE nv = func(data, is->once.value); + if (is->once.value != nv) { + is->once.value = nv; + } } break; } @@ -181,7 +187,7 @@ static void https://github.com/ruby/ruby/blob/trunk/iseq.c#L187 rb_iseq_each_value(const rb_iseq_t *iseq, iseq_value_itr_t * func, void *data) { unsigned int size; - const VALUE *code; + VALUE *code; size_t n; rb_vm_insns_translator_t * translator; const struct rb_iseq_constant_body *const body = iseq->body; @@ -205,10 +211,65 @@ rb_iseq_each_value(const rb_iseq_t *iseq https://github.com/ruby/ruby/blob/trunk/iseq.c#L211 } } -static void +static VALUE +update_each_insn_value(void *ctx, VALUE obj) +{ + return rb_gc_new_location(obj); +} + +void +rb_iseq_update_references(rb_iseq_t *iseq) +{ + if (iseq->body) { + struct rb_iseq_constant_body *body = iseq->body; + + body->variable.coverage = rb_gc_new_location(body->variable.coverage); + body->variable.pc2branchindex = rb_gc_new_location(body->variable.pc2branchindex); + body->location.label = rb_gc_new_location(body->location.label); + body->location.base_label = rb_gc_new_location(body->location.base_label); + body->location.pathobj = rb_gc_new_location(body->location.pathobj); + if (body->local_iseq) { + body->local_iseq = (struct rb_iseq_struct *)rb_gc_new_location((VALUE)body->local_iseq); + } + if (body->parent_iseq) { + body->parent_iseq = (struct rb_iseq_struct *)rb_gc_new_location((VALUE)body->parent_iseq); + } + if (FL_TEST(iseq, ISEQ_MARKABLE_ISEQ)) { + rb_iseq_each_value(iseq, update_each_insn_value, NULL); + } + + if (body->param.flags.has_kw && ISEQ_COMPILE_DATA(iseq) == NULL) { + int i, j; + + i = body->param.keyword->required_num; + + for (j = 0; i < body->param.keyword->num; i++, j++) { + VALUE obj = body->param.keyword->default_values[j]; + if (obj != Qundef) { + body->param.keyword->default_values[j] = rb_gc_new_location(obj); + } + } + } + + if (body->catch_table) { + struct iseq_catch_table *table = body->catch_table; + unsigned int i; + for(i = 0; i < table->size; i++) { + struct iseq_catch_table_entry *entry; + entry = &table->entries[i]; + if (entry->iseq) { + entry->iseq = (rb_iseq_t *)rb_gc_new_location((VALUE)entry->iseq); + } + } + } + } +} + +static VALUE each_insn_value(void *ctx, VALUE obj) { - rb_gc_mark(obj); + rb_gc_mark_no_pin(obj); + return obj; } void @@ -225,12 +286,12 @@ rb_iseq_mark(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/iseq.c#L286 rb_iseq (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/