ruby-changes:55272
From: tenderlove <ko1@a...>
Date: Wed, 10 Apr 2019 05:32:11 +0900 (JST)
Subject: [ruby-changes:55272] tenderlove:r67479 (trunk): Adding `GC.compact` and compacting GC support.
tenderlove 2019-04-10 05:32:04 +0900 (Wed, 10 Apr 2019) New Revision: 67479 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=67479 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/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/test/ruby/test_gc.rb trunk/transient_heap.c trunk/transient_heap.h trunk/variable.c trunk/vm.c trunk/vm_core.h trunk/vm_eval.c trunk/vm_method.c Index: include/ruby/ruby.h =================================================================== --- include/ruby/ruby.h (revision 67478) +++ include/ruby/ruby.h (revision 67479) @@ -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) @@ -881,9 +883,14 @@ enum ruby_fl_type { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L883 RUBY_FL_SINGLETON = RUBY_FL_USER0 }; +struct RMoved { + VALUE flags; + VALUE destination; +}; + struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { VALUE flags; - const VALUE klass; + VALUE klass; }; VALUE rb_obj_hide(VALUE obj); @@ -1105,7 +1112,7 @@ struct RArray { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1112 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 +1151,8 @@ struct rb_data_type_struct { https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1151 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 +1263,7 @@ int rb_big_sign(VALUE); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1263 #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)) Index: symbol.c =================================================================== --- symbol.c (revision 67478) +++ symbol.c (revision 67479) @@ -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 67478) +++ symbol.h (revision 67479) @@ -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: hash.c =================================================================== --- hash.c (revision 67478) +++ hash.c (revision 67479) @@ -781,7 +781,7 @@ ar_add_direct_with_hash(VALUE hash, st_d https://github.com/ruby/ruby/blob/trunk/hash.c#L781 } static int -ar_foreach(VALUE hash, int (*func)(ANYARGS), st_data_t arg) +ar_general_foreach(VALUE hash, int (*func)(ANYARGS), st_update_callback_func *replace, st_data_t arg) { if (RHASH_AR_TABLE_SIZE(hash) > 0) { unsigned i, bound = RHASH_AR_TABLE_BOUND(hash); @@ -799,6 +799,20 @@ ar_foreach(VALUE hash, int (*func)(ANYAR https://github.com/ruby/ruby/blob/trunk/hash.c#L799 case ST_CHECK: case ST_STOP: return 0; + case ST_REPLACE: + if (replace) { + VALUE key; + VALUE value; + + key = cur_entry->key; + value = cur_entry->record; + retval = (*replace)(&key, &value, arg, TRUE); + + ar_table_entry *entry = RHASH_AR_TABLE_REF(hash, i); + entry->key = key; + entry->record = value; + } + break; case ST_DELETE: ar_clear_entry(RHASH_AR_TABLE_REF(hash, i)); RHASH_AR_TABLE_SIZE_DEC(hash); @@ -810,6 +824,18 @@ ar_foreach(VALUE hash, int (*func)(ANYAR https://github.com/ruby/ruby/blob/trunk/hash.c#L824 } static int +ar_foreach_with_replace(VALUE hash, int (*func)(ANYARGS), st_update_callback_func *replace, st_data_t arg) +{ + return ar_general_foreach(hash, func, replace, arg); +} + +static int +ar_foreach(VALUE hash, int (*func)(ANYARGS), st_data_t arg) +{ + return ar_general_foreach(hash, func, NULL, arg); +} + +static int ar_foreach_check(VALUE hash, int (*func)(ANYARGS), st_data_t arg, st_data_t never) { @@ -845,6 +871,7 @@ ar_foreach_check(VALUE hash, int (*func) https://github.com/ruby/ruby/blob/trunk/hash.c#L871 case ST_CONTINUE: break; case ST_STOP: + case ST_REPLACE: return 0; case ST_DELETE: { if (!ar_empty_entry(cur_entry)) { @@ -1257,6 +1284,17 @@ rb_hash_stlike_foreach(VALUE hash, int ( https://github.com/ruby/ruby/blob/trunk/hash.c#L1284 } } +int +rb_hash_stlike_foreach_with_replace(VALUE hash, int (*func)(ANYARGS), st_update_callback_func *replace, st_data_t arg) +{ + if (RHASH_AR_TABLE_P(hash)) { + return ar_foreach_with_replace(hash, func, replace, arg); + } + else { + return st_foreach_with_replace(RHASH_ST_TABLE(hash), func, replace, arg); + } +} + static VALUE hash_foreach_call(VALUE arg) { Index: method.h =================================================================== --- method.h (revision 67478) +++ method.h (revision 67479) @@ -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: internal.h =================================================================== --- internal.h (revision 67478) +++ internal.h (revision 67479) @@ -731,8 +731,8 @@ struct RBignum { https://github.com/ruby/ruby/blob/trunk/internal.h#L731 struct RRational { struct RBasic basic; - const VALUE num; - const VALUE den; + VALUE num; + VALUE den; }; #define RRATIONAL(obj) (R_CAST(RRational)(obj)) @@ -748,8 +748,8 @@ struct RFloat { https://github.com/ruby/ruby/blob/trunk/internal.h#L748 struct RComplex { struct RBasic basic; - const VALUE real; - const VALUE imag; + VALUE real; + VALUE imag; }; #define RCOMPLEX(obj) (R_CAST(RComplex)(obj)) @@ -816,8 +816,8 @@ struct RHash { https://github.com/ruby/ruby/blob/trunk/internal.h#L816 st_table *st; struct ar_table_struct *ar; /* possibly 0 */ } as; - const int iter_lev; - const VALUE ifnone; + int iter_lev; + VALUE ifnone; }; #ifdef RHASH_ITER_LEV @@ -947,7 +947,7 @@ struct rb_classext_struct { https://github.com/ruby/ruby/blob/trunk/internal.h#L947 */ rb_subclass_entry_t **module_subclasses; rb_serial_t class_serial; - const VALUE origin_; + VALUE origin_; VALUE refined_class; rb_alloc_func_t allocator; }; @@ -1065,10 +1065,10 @@ imemo_type_p(VALUE imemo, enum imemo_typ https://github.com/ruby/ruby/blob/trunk/internal.h#L1065 /*! SVAR (Special VARiable) */ struct vm_svar { VALUE flags; - const VALUE cref_or_me; /*!< class reference or rb_method_entry_t */ - const VALUE lastline; - const VALUE backref; - const VALUE others; + VALUE cref_or_me; /*!< class reference or rb_method_entry_t */ + VALUE lastline; + VALUE backref; + VALUE others; }; @@ -1078,7 +1078,7 @@ struct vm_svar { https://github.com/ruby/ruby/blob/trunk/internal.h#L1078 struct vm_throw_data { VALUE flags; VALUE reserved; - const VALUE throw_obj; + VALUE throw_obj; const struct rb_control_frame_struct *catch_frame; VALUE throw_state; }; @@ -1101,7 +1101,7 @@ struct vm_ifunc { https://github.com/ruby/ruby/blob/trunk/internal.h#L1101 VALUE flags; VALUE reserved; VALUE (*func)(ANYARGS); - const void *data; + void *data; struct vm_ifunc_argc argc; }; @@ -1158,12 +1158,12 @@ void rb_strterm_mark(VALUE obj); https://github.com/ruby/ruby/blob/trunk/internal.h#L1158 struct MEMO { VALUE flags; VALUE reserved; - const VALUE v1; - const VALUE v2; + VALUE v1; + VALUE v2; union { long cnt; long state; - const VALUE value; + VALUE value; VALUE (*func)(ANYARGS); } u3; }; @@ -1566,6 +1566,7 @@ void rb_hash_bulk_insert(long, const VAL https://github.com/ruby/ruby/blob/trunk/internal.h#L1566 int rb_hash_stlike_lookup(VALUE hash, st_data_t key, st_data_t *pval); int rb_hash_stlike_delete(VALUE hash, st_data_t *pkey, st_data_t *pval); int rb_hash_stlike_foreach(VALUE hash, int (*func)(ANYARGS), st_data_t arg); +int rb_hash_stlike_foreach_with_replace(VALUE hash, int (*func)(ANYARGS), st_update_callback_func *replace, st_data_t arg); int rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func func, st_data_t arg); /* inits.c */ @@ -2330,6 +2331,7 @@ extern unsigned long ruby_scan_digits(co https://github.com/ruby/ruby/blob/trunk/internal.h#L2331 /* variable.c (export) */ void rb_mark_generic_ivar(VALUE); +void rb_mv_generic_ivar(VALUE src, VALUE dst); VALUE rb_const_missing(VALUE klass, VALUE name); int rb_class_ivar_set(VALUE klass, ID vid, VALUE value); st_table *rb_st_copy(VALUE obj, struct st_table *orig_tbl); @@ -2341,9 +2343,10 @@ VALUE rb_wb_unprotected_newobj_of(VALUE, https://github.com/ruby/ruby/blob/trunk/internal.h#L2343 size_t rb_obj_memsize_of(VALUE); void rb_gc_verify_internal_consistency(void); -#define RB_OBJ_GC_FLAGS_MAX 5 +#define RB_OBJ_GC_FLAGS_MAX 6 size_t rb_obj_gc_flags(VALUE, ID[], size_t); void rb_gc_mark_values(long n, const VALUE *values); +void rb_gc_mark_stack_values(long n, const VALUE *values); #if IMEMO_DEBUG VALUE rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0, const char *file, int line); Index: vm_method.c =================================================================== --- vm_method.c (revision 67478) +++ vm_method.c (revision 67479) @@ -671,7 +671,7 @@ MJIT_FUNC_EXPORTED void https://github.com/ruby/ruby/blob/trunk/vm_method.c#L671 rb_add_method_iseq(VALUE klass, ID mid, const rb_iseq_t *iseq, rb_cref_t *cref, rb_method_visibility_t visi) { struct { /* should be same fields with rb_method_iseq_struct */ - const rb_iseq_t *iseqptr; + rb_iseq_t *iseqptr; rb_cref_t *cref; } iseq_body; Index: class.c =================================================================== --- class.c (revision 67478) +++ class.c (revision 67479) @@ -539,6 +539,7 @@ boot_defclass(const char *name, VALUE su https://github.com/ruby/ruby/blob/trunk/class.c#L539 rb_name_class(obj, id); rb_const_set((rb_cObject ? rb_cObject : obj), id, obj); + rb_vm_add_root_module(id, obj); return obj; } @@ -730,6 +731,9 @@ rb_define_class_id_under(VALUE outer, ID https://github.com/ruby/ruby/blob/trunk/class.c#L731 " (%"PRIsVALUE" is given but was %"PRIsVALUE")", outer, rb_id2str(id), RCLASS_SUPER(klass), super); } + /* Class may have been defined in Ruby and not pin-rooted */ + rb_vm_add_root_module(id, klass); + return klass; } if (!super) { @@ -740,6 +744,7 @@ rb_define_class_id_under(VALUE outer, ID https://github.com/ruby/ruby/blob/trunk/class.c#L744 rb_set_class_path_string(klass, outer, rb_id2str(id)); rb_const_set(outer, id, klass); rb_class_inherited(super, klass); + rb_vm_add_root_module(id, klass); rb_gc_register_mark_object(klass); return klass; @@ -777,10 +782,13 @@ rb_define_module(const char *name) https://github.com/ruby/ruby/blob/trunk/class.c#L782 rb_raise(rb_eTypeError, "%s is not a module (%"PRIsVALUE")", name, rb_obj_class(module)); } + /* Module may have been defined in Ruby and not pin-rooted */ + rb_vm_add_root_module(id, module); return module; } module = rb_define_module_id(id); rb_vm_add_root_module(id, module); + rb_gc_register_mark_object(module); rb_const_set(rb_cObject, id, module); return module; Index: st.c =================================================================== --- st.c (revision 67478) +++ st.c (revision 67479) @@ -1548,7 +1548,7 @@ st_update(st_table *tab, st_data_t key, https://github.com/ruby/ruby/blob/trunk/st.c#L1548 different for ST_CHECK and when the current element is removed during traversing. */ static inline int -st_general_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg, +st_general_foreach(st_table *tab, int (*func)(ANYARGS), st_update_callback_func *replace, st_data_t arg, int check_p) { st_index_t bin; @@ -1572,6 +1572,15 @@ st_general_foreach(st_table *tab, int (* https://github.com/ruby/ruby/blob/trunk/st.c#L1572 rebuilds_num = tab->rebuilds_num; hash = curr_entry_ptr->hash; retval = (*func)(key, curr_entry_ptr->record, arg, 0); + + if (retval == ST_REPLACE && replace) { + st_data_t value; + value = curr_entry_ptr->record; + retval = (*replace)(&key, &value, arg, TRUE); + curr_entry_ptr->key = key; + curr_entry_ptr->record = value; + } + if (rebuilds_num != tab->rebuilds_num) { retry: entries = tab->entries; @@ -1600,6 +1609,8 @@ st_general_foreach(st_table *tab, int (* https://github.com/ruby/ruby/blob/trunk/st.c#L1609 curr_entry_ptr = &entries[i]; } switch (retval) { + case ST_REPLACE: + break; case ST_CONTINUE: break; case ST_CHECK: @@ -1648,9 +1659,15 @@ st_general_foreach(st_table *tab, int (* https://github.com/ruby/ruby/blob/trunk/st.c#L1659 } int +st_foreach_with_replace(st_table *tab, int (*func)(ANYARGS), st_update_callback_func *replace, st_data_t arg) +{ + return st_general_foreach(tab, func, replace, arg, TRUE); +} + +int st_foreach(st_table *tab, int (*func)(ANYARGS), st_data_t arg) { - return st_general_foreach(tab, func, arg, FALSE); + return st_general_foreach(tab, func, NULL, arg, FALSE); } /* See comments for function st_delete_safe. */ @@ -1658,7 +1675,7 @@ int https://github.com/ruby/ruby/blob/trunk/st.c#L1675 st_foreach_check(st_table *tab, int (*func)(ANYARGS), st_data_t arg, st_data_t never ATTRIBUTE_UNUSED) { - return st_general_foreach(tab, func, arg, TRUE); + return st_general_foreach(tab, func, NULL, arg, TRUE); } /* Set up array KEYS by at most SIZE keys of head table TAB entries. Index: constant.h =================================================================== --- constant.h (revision 67478) +++ constant.h (revision 67479) @@ -31,8 +31,8 @@ typedef enum { https://github.com/ruby/ruby/blob/trunk/constant.h#L31 typedef struct rb_const_entry_struct { rb_const_flag_t flag; int line; - const VALUE value; /* should be mark */ - const VALUE file; /* should be mark */ + VALUE value; /* should be mark */ + VALUE file; /* should be mark */ } rb_const_entry_t; VALUE rb_mod_private_constant(int argc, const VALUE *argv, VALUE obj); Index: transient_heap.c =================================================================== --- transient_heap.c (revision 67478) +++ transient_heap.c (revision 67479) @@ -796,6 +796,56 @@ blocks_clear_marked_index(struct transie https://github.com/ruby/ruby/blob/trunk/transient_heap.c#L796 } } +static void +transient_heap_block_update_refs(struct transient_heap* theap, struct transient_heap_block* block) +{ + int i=0, n=0; + + while (i<block->info.index) { + void *ptr = &block->buff[i]; + struct transient_alloc_header *header = ptr; + + unpoison_memory_region(header, sizeof *header, false); + + void *poisoned = __asan_region_is_poisoned(header->obj, SIZEOF_VALUE); + unpoison_object(header->obj, false); + + header->obj = rb_gc_new_location(header->obj); + + if (poisoned) { + poison_object(header->obj); + } + + i += header->size; + poison_memory_region(header, sizeof *header); + n++; + } +} + +static void +transient_heap_blocks_update_refs(struct transient_heap* theap, struct transient_heap_block *block, const char *type_str) +{ + while (block) { + transient_heap_block_update_refs(theap, block); + block = block->info.next_block; + } +} + +void +rb_transient_heap_update_references(void) +{ + struct transient_heap* theap = transient_heap_get(); + int i; + + transient_heap_blocks_update_refs(theap, theap->using_blocks, "using_blocks"); + transient_heap_blocks_update_refs(theap, theap->marked_blocks, "marked_blocks"); + + for (i=0; i<theap->promoted_objects_index; i++) { + VALUE obj = theap->promoted_objects[i]; + theap->promoted_objects[i] == rb_gc_new_location(obj); + } +} + void rb_transient_heap_start_marking(int full_marking) { Index: transient_heap.h =================================================================== --- tran (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/