Put the default GC implementation back into gc.o

We discovered that having gc.o and gc_impl.o in separate translation
units diminishes codegen quality with GCC 11 on x86-64. This commit
solves that problem by including default/gc.c into gc.c, letting the
optimizer have visibility into the body of functions again in builds
not using link-time optimization, which are common.

This effectively restores things to the way they were before
[Feature #20470] from the optimizer's perspective while maintaining the
ability to build gc/default.c as a DSO.

There were a few functions duplicated across gc.c and gc/default.c.
Extract them and put them into gc/gc.h.
This commit is contained in:
Alan Wu 2024-07-25 17:35:24 -04:00
parent cef959df90
commit 0ada02abe2
5 changed files with 257 additions and 536 deletions

194
common.mk
View File

@ -130,7 +130,6 @@ COMMONOBJS = array.$(OBJEXT) \
eval.$(OBJEXT) \
file.$(OBJEXT) \
gc.$(OBJEXT) \
gc_impl.$(OBJEXT) \
hash.$(OBJEXT) \
inits.$(OBJEXT) \
imemo.$(OBJEXT) \
@ -7238,6 +7237,7 @@ gc.$(OBJEXT): $(CCAN_DIR)/str/str.h
gc.$(OBJEXT): $(hdrdir)/ruby.h
gc.$(OBJEXT): $(hdrdir)/ruby/ruby.h
gc.$(OBJEXT): $(hdrdir)/ruby/version.h
gc.$(OBJEXT): $(top_srcdir)/gc/default.c
gc.$(OBJEXT): $(top_srcdir)/gc/gc.h
gc.$(OBJEXT): $(top_srcdir)/gc/gc_impl.h
gc.$(OBJEXT): $(top_srcdir)/internal/array.h
@ -7503,198 +7503,6 @@ gc.$(OBJEXT): {$(VPATH)}vm_core.h
gc.$(OBJEXT): {$(VPATH)}vm_debug.h
gc.$(OBJEXT): {$(VPATH)}vm_opts.h
gc.$(OBJEXT): {$(VPATH)}vm_sync.h
gc_impl.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
gc_impl.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
gc_impl.$(OBJEXT): $(CCAN_DIR)/list/list.h
gc_impl.$(OBJEXT): $(CCAN_DIR)/str/str.h
gc_impl.$(OBJEXT): $(hdrdir)/ruby/ruby.h
gc_impl.$(OBJEXT): $(top_srcdir)/gc/default.c
gc_impl.$(OBJEXT): $(top_srcdir)/gc/gc.h
gc_impl.$(OBJEXT): $(top_srcdir)/gc/gc_impl.h
gc_impl.$(OBJEXT): $(top_srcdir)/internal/bits.h
gc_impl.$(OBJEXT): $(top_srcdir)/internal/compilers.h
gc_impl.$(OBJEXT): $(top_srcdir)/internal/hash.h
gc_impl.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
gc_impl.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
gc_impl.$(OBJEXT): $(top_srcdir)/internal/string.h
gc_impl.$(OBJEXT): $(top_srcdir)/internal/warnings.h
gc_impl.$(OBJEXT): {$(VPATH)}assert.h
gc_impl.$(OBJEXT): {$(VPATH)}atomic.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/assume.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/attributes.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/bool.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/gcc_version_since.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/inttypes.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/limits.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
gc_impl.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
gc_impl.$(OBJEXT): {$(VPATH)}config.h
gc_impl.$(OBJEXT): {$(VPATH)}darray.h
gc_impl.$(OBJEXT): {$(VPATH)}debug.h
gc_impl.$(OBJEXT): {$(VPATH)}debug_counter.h
gc_impl.$(OBJEXT): {$(VPATH)}defines.h
gc_impl.$(OBJEXT): {$(VPATH)}encoding.h
gc_impl.$(OBJEXT): {$(VPATH)}intern.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/abi.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/anyargs.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/double.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/fixnum.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/gid_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/int.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/intptr_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/long.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/long_long.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/mode_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/off_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/pid_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/short.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/size_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/st_data_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/arithmetic/uid_t.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/assume.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/alloc_size.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/artificial.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/cold.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/const.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/constexpr.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/deprecated.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/diagnose_if.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/enum_extensibility.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/error.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/flag_enum.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/forceinline.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/format.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/maybe_unused.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noalias.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/nodiscard.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noexcept.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noinline.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/nonnull.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/noreturn.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/packed_struct.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/pure.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/restrict.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/returns_nonnull.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/warning.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/attr/weakref.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/cast.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/apple.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/clang.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/gcc.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/intel.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/msvc.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_is/sunpro.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/compiler_since.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/config.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/constant_p.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rarray.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rbasic.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rbignum.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rclass.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rdata.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rfile.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rhash.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/robject.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rregexp.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rstring.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rstruct.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/core/rtypeddata.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/ctype.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/dllexport.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/dosish.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/coderange.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/ctype.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/encoding.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/pathname.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/re.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/sprintf.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/string.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/symbol.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/encoding/transcode.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/error.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/eval.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/event.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/fl_type.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/gc.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/glob.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/globals.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/attribute.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/builtin.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/c_attribute.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/cpp_attribute.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/declspec_attribute.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/extension.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/feature.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/has/warning.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/array.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/bignum.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/class.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/compar.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/complex.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/cont.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/dir.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/enum.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/enumerator.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/error.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/eval.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/file.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/hash.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/io.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/load.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/marshal.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/numeric.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/object.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/parse.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/proc.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/process.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/random.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/range.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/rational.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/re.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/ruby.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/select.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/select/largesize.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/signal.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/sprintf.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/string.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/struct.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/thread.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/time.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/variable.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/intern/vm.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/interpreter.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/iterator.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/memory.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/method.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/module.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/newobj.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/scan_args.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/special_consts.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/static_assert.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/stdalign.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/stdbool.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/stdckdint.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/symbol.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/value.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/value_type.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/variable.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/warning_push.h
gc_impl.$(OBJEXT): {$(VPATH)}internal/xmalloc.h
gc_impl.$(OBJEXT): {$(VPATH)}missing.h
gc_impl.$(OBJEXT): {$(VPATH)}onigmo.h
gc_impl.$(OBJEXT): {$(VPATH)}oniguruma.h
gc_impl.$(OBJEXT): {$(VPATH)}probes.dmyh
gc_impl.$(OBJEXT): {$(VPATH)}probes.h
gc_impl.$(OBJEXT): {$(VPATH)}st.h
gc_impl.$(OBJEXT): {$(VPATH)}subst.h
gc_impl.$(OBJEXT): {$(VPATH)}thread.h
gc_impl.$(OBJEXT): {$(VPATH)}util.h
gc_impl.$(OBJEXT): {$(VPATH)}vm.h
goruby.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
goruby.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
goruby.$(OBJEXT): $(CCAN_DIR)/list/list.h

138
gc.c
View File

@ -83,7 +83,6 @@
#include "debug_counter.h"
#include "eval_intern.h"
#include "gc/gc.h"
#include "gc/gc_impl.h"
#include "id_table.h"
#include "internal.h"
#include "internal/class.h"
@ -565,6 +564,10 @@ rb_gc_guarded_ptr_val(volatile VALUE *ptr, VALUE val)
}
#endif
static const char *obj_type_name(VALUE obj);
#define RB_AMALGAMATED_DEFAULT_GC
#include "gc/default.c"
#if USE_SHARED_GC && !defined(HAVE_DLOPEN)
# error "Shared GC requires dlopen"
#elif USE_SHARED_GC
@ -1899,43 +1902,6 @@ set_zero(st_data_t key, st_data_t val, st_data_t arg)
return ST_CONTINUE;
}
static VALUE
type_sym(size_t type)
{
switch (type) {
#define COUNT_TYPE(t) case (t): return ID2SYM(rb_intern(#t)); break;
COUNT_TYPE(T_NONE);
COUNT_TYPE(T_OBJECT);
COUNT_TYPE(T_CLASS);
COUNT_TYPE(T_MODULE);
COUNT_TYPE(T_FLOAT);
COUNT_TYPE(T_STRING);
COUNT_TYPE(T_REGEXP);
COUNT_TYPE(T_ARRAY);
COUNT_TYPE(T_HASH);
COUNT_TYPE(T_STRUCT);
COUNT_TYPE(T_BIGNUM);
COUNT_TYPE(T_FILE);
COUNT_TYPE(T_DATA);
COUNT_TYPE(T_MATCH);
COUNT_TYPE(T_COMPLEX);
COUNT_TYPE(T_RATIONAL);
COUNT_TYPE(T_NIL);
COUNT_TYPE(T_TRUE);
COUNT_TYPE(T_FALSE);
COUNT_TYPE(T_SYMBOL);
COUNT_TYPE(T_FIXNUM);
COUNT_TYPE(T_IMEMO);
COUNT_TYPE(T_UNDEF);
COUNT_TYPE(T_NODE);
COUNT_TYPE(T_ICLASS);
COUNT_TYPE(T_ZOMBIE);
COUNT_TYPE(T_MOVED);
#undef COUNT_TYPE
default: return SIZET2NUM(type); break;
}
}
struct count_objects_data {
size_t counts[T_MASK+1];
size_t freed;
@ -2392,16 +2358,6 @@ rb_mark_tbl(st_table *tbl)
st_foreach(tbl, rb_mark_tbl_i, (st_data_t)rb_gc_get_objspace());
}
static int
gc_mark_tbl_no_pin_i(st_data_t key, st_data_t value, st_data_t data)
{
void *objspace = (void *)data;
rb_gc_impl_mark(objspace, (VALUE)value);
return ST_CONTINUE;
}
static void
gc_mark_tbl_no_pin(void *objspace, st_table *tbl)
{
@ -2963,8 +2919,6 @@ gc_ref_update_array(void *objspace, VALUE v)
}
}
static void gc_ref_update_table_values_only(void *objspace, st_table *tbl);
static void
gc_ref_update_object(void *objspace, VALUE v)
{
@ -2990,90 +2944,12 @@ gc_ref_update_object(void *objspace, VALUE v)
}
}
static int
hash_replace_ref(st_data_t *key, st_data_t *value, st_data_t argp, int existing)
{
void *objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*key)) {
*key = rb_gc_impl_location(objspace, (VALUE)*key);
}
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) {
*value = rb_gc_impl_location(objspace, (VALUE)*value);
}
return ST_CONTINUE;
}
static int
hash_foreach_replace(st_data_t key, st_data_t value, st_data_t argp, int error)
{
void *objspace;
objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)key)) {
return ST_REPLACE;
}
if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) {
return ST_REPLACE;
}
return ST_CONTINUE;
}
static int
hash_replace_ref_value(st_data_t *key, st_data_t *value, st_data_t argp, int existing)
{
void *objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) {
*value = rb_gc_impl_location(objspace, (VALUE)*value);
}
return ST_CONTINUE;
}
static int
hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error)
{
void *objspace;
objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) {
return ST_REPLACE;
}
return ST_CONTINUE;
}
static void
gc_ref_update_table_values_only(void *objspace, st_table *tbl)
{
if (!tbl || tbl->num_entries == 0) return;
if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) {
rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
void
rb_gc_ref_update_table_values_only(st_table *tbl)
{
gc_ref_update_table_values_only(rb_gc_get_objspace(), tbl);
}
static void
gc_update_table_refs(void *objspace, st_table *tbl)
{
if (!tbl || tbl->num_entries == 0) return;
if (st_foreach_with_replace(tbl, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace)) {
rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
/* Update MOVED references in a VALUE=>VALUE st_table */
void
rb_gc_update_tbl_refs(st_table *ptr)
@ -4257,12 +4133,6 @@ ruby_malloc_size_overflow(size_t count, size_t elsize)
count, elsize);
}
static inline size_t
xmalloc2_size(const size_t count, const size_t elsize)
{
return size_mul_or_raise(count, elsize, rb_eArgError);
}
void *
ruby_xmalloc2(size_t n, size_t size)
{

View File

@ -248,8 +248,10 @@ int ruby_rgengc_debug;
# define RGENGC_CHECK_MODE 0
#endif
#ifndef GC_ASSERT
// Note: using RUBY_ASSERT_WHEN() extend a macro in expr (info by nobu).
#define GC_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(RGENGC_CHECK_MODE > 0, expr, #expr)
# define GC_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(RGENGC_CHECK_MODE > 0, expr, #expr)
#endif
/* RGENGC_PROFILE
* 0: disable RGenGC profiling
@ -875,13 +877,17 @@ RVALUE_AGE_SET(VALUE obj, int age)
#define gc_config_full_mark_set(b) (((int)b), objspace->gc_config.full_mark = (b))
#define gc_config_full_mark_val (objspace->gc_config.full_mark)
#define DURING_GC_COULD_MALLOC_REGION_START() \
#ifndef DURING_GC_COULD_MALLOC_REGION_START
# define DURING_GC_COULD_MALLOC_REGION_START() \
assert(rb_during_gc()); \
bool _prev_enabled = rb_gc_impl_gc_enabled_p(objspace); \
rb_gc_impl_gc_disable(objspace, false)
#endif
#define DURING_GC_COULD_MALLOC_REGION_END() \
#ifndef DURING_GC_COULD_MALLOC_REGION_END
# define DURING_GC_COULD_MALLOC_REGION_END() \
if (_prev_enabled) rb_gc_impl_gc_enable(objspace)
#endif
static inline enum gc_mode
gc_mode_verify(enum gc_mode mode)
@ -7340,84 +7346,6 @@ gc_ref_update(void *vstart, void *vend, size_t stride, rb_objspace_t *objspace,
return 0;
}
static int
hash_replace_ref_value(st_data_t *key, st_data_t *value, st_data_t argp, int existing)
{
void *objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) {
*value = rb_gc_impl_location(objspace, (VALUE)*value);
}
return ST_CONTINUE;
}
static int
hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error)
{
void *objspace;
objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) {
return ST_REPLACE;
}
return ST_CONTINUE;
}
static void
gc_ref_update_table_values_only(void *objspace, st_table *tbl)
{
if (!tbl || tbl->num_entries == 0) return;
if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) {
rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
static int
hash_foreach_replace(st_data_t key, st_data_t value, st_data_t argp, int error)
{
void *objspace;
objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)key)) {
return ST_REPLACE;
}
if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) {
return ST_REPLACE;
}
return ST_CONTINUE;
}
static int
hash_replace_ref(st_data_t *key, st_data_t *value, st_data_t argp, int existing)
{
void *objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*key)) {
*key = rb_gc_impl_location(objspace, (VALUE)*key);
}
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) {
*value = rb_gc_impl_location(objspace, (VALUE)*value);
}
return ST_CONTINUE;
}
static void
gc_update_table_refs(void *objspace, st_table *tbl)
{
if (!tbl || tbl->num_entries == 0) return;
if (st_foreach_with_replace(tbl, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace)) {
rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
static void
gc_update_references(rb_objspace_t *objspace)
{
@ -8581,12 +8509,6 @@ rb_gc_impl_calloc(void *objspace_ptr, size_t size)
return objspace_malloc_fixup(objspace, mem, size);
}
static inline size_t
xmalloc2_size(const size_t count, const size_t elsize)
{
return rb_size_mul_or_raise(count, elsize, rb_eArgError);
}
void *
rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size)
{
@ -9363,43 +9285,6 @@ gc_get_auto_compact(VALUE _)
#endif
#if GC_CAN_COMPILE_COMPACTION
static VALUE
type_sym(size_t type)
{
switch (type) {
#define COUNT_TYPE(t) case (t): return ID2SYM(rb_intern(#t)); break;
COUNT_TYPE(T_NONE);
COUNT_TYPE(T_OBJECT);
COUNT_TYPE(T_CLASS);
COUNT_TYPE(T_MODULE);
COUNT_TYPE(T_FLOAT);
COUNT_TYPE(T_STRING);
COUNT_TYPE(T_REGEXP);
COUNT_TYPE(T_ARRAY);
COUNT_TYPE(T_HASH);
COUNT_TYPE(T_STRUCT);
COUNT_TYPE(T_BIGNUM);
COUNT_TYPE(T_FILE);
COUNT_TYPE(T_DATA);
COUNT_TYPE(T_MATCH);
COUNT_TYPE(T_COMPLEX);
COUNT_TYPE(T_RATIONAL);
COUNT_TYPE(T_NIL);
COUNT_TYPE(T_TRUE);
COUNT_TYPE(T_FALSE);
COUNT_TYPE(T_SYMBOL);
COUNT_TYPE(T_FIXNUM);
COUNT_TYPE(T_IMEMO);
COUNT_TYPE(T_UNDEF);
COUNT_TYPE(T_NODE);
COUNT_TYPE(T_ICLASS);
COUNT_TYPE(T_ZOMBIE);
COUNT_TYPE(T_MOVED);
#undef COUNT_TYPE
default: return SIZET2NUM(type); break;
}
}
/*
* call-seq:
* GC.latest_compact_info -> hash
@ -9654,14 +9539,6 @@ pin_value(st_data_t key, st_data_t value, st_data_t data)
void rb_gc_impl_mark(void *objspace_ptr, VALUE obj);
static int
gc_mark_tbl_no_pin_i(st_data_t key, st_data_t value, st_data_t data)
{
rb_gc_impl_mark((void *)data, (VALUE)value);
return ST_CONTINUE;
}
#if MALLOC_ALLOCATED_SIZE
/*
* call-seq:

146
gc/gc.h
View File

@ -1,7 +1,16 @@
#ifndef GC_GC_H
#define GC_GC_H
/**
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @brief Private header for the default GC and other GC implementations
* first introduced for [Feature #20470].
*/
#include "ruby/ruby.h"
#include "gc/gc_impl.h"
RUBY_SYMBOL_EXPORT_BEGIN
unsigned int rb_gc_vm_lock(void);
@ -39,4 +48,139 @@ RUBY_SYMBOL_EXPORT_END
void rb_ractor_finish_marking(void);
// -------------------Private section begin------------------------
// Functions in this section are private to the default GC and gc.c
static int
hash_foreach_replace_value(st_data_t key, st_data_t value, st_data_t argp, int error)
{
void *objspace;
objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) {
return ST_REPLACE;
}
return ST_CONTINUE;
}
static int
hash_replace_ref_value(st_data_t *key, st_data_t *value, st_data_t argp, int existing)
{
void *objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) {
*value = rb_gc_impl_location(objspace, (VALUE)*value);
}
return ST_CONTINUE;
}
static void
gc_ref_update_table_values_only(void *objspace, st_table *tbl)
{
if (!tbl || tbl->num_entries == 0) return;
if (st_foreach_with_replace(tbl, hash_foreach_replace_value, hash_replace_ref_value, (st_data_t)objspace)) {
rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
static int
gc_mark_tbl_no_pin_i(st_data_t key, st_data_t value, st_data_t data)
{
void *objspace = (void *)data;
rb_gc_impl_mark(objspace, (VALUE)value);
return ST_CONTINUE;
}
static int
hash_foreach_replace(st_data_t key, st_data_t value, st_data_t argp, int error)
{
void *objspace;
objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)key)) {
return ST_REPLACE;
}
if (rb_gc_impl_object_moved_p(objspace, (VALUE)value)) {
return ST_REPLACE;
}
return ST_CONTINUE;
}
static int
hash_replace_ref(st_data_t *key, st_data_t *value, st_data_t argp, int existing)
{
void *objspace = (void *)argp;
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*key)) {
*key = rb_gc_impl_location(objspace, (VALUE)*key);
}
if (rb_gc_impl_object_moved_p(objspace, (VALUE)*value)) {
*value = rb_gc_impl_location(objspace, (VALUE)*value);
}
return ST_CONTINUE;
}
static void
gc_update_table_refs(void *objspace, st_table *tbl)
{
if (!tbl || tbl->num_entries == 0) return;
if (st_foreach_with_replace(tbl, hash_foreach_replace, hash_replace_ref, (st_data_t)objspace)) {
rb_raise(rb_eRuntimeError, "hash modified during iteration");
}
}
static inline size_t
xmalloc2_size(const size_t count, const size_t elsize)
{
return rb_size_mul_or_raise(count, elsize, rb_eArgError);
}
static VALUE
type_sym(size_t type)
{
switch (type) {
#define COUNT_TYPE(t) case (t): return ID2SYM(rb_intern(#t)); break;
COUNT_TYPE(T_NONE);
COUNT_TYPE(T_OBJECT);
COUNT_TYPE(T_CLASS);
COUNT_TYPE(T_MODULE);
COUNT_TYPE(T_FLOAT);
COUNT_TYPE(T_STRING);
COUNT_TYPE(T_REGEXP);
COUNT_TYPE(T_ARRAY);
COUNT_TYPE(T_HASH);
COUNT_TYPE(T_STRUCT);
COUNT_TYPE(T_BIGNUM);
COUNT_TYPE(T_FILE);
COUNT_TYPE(T_DATA);
COUNT_TYPE(T_MATCH);
COUNT_TYPE(T_COMPLEX);
COUNT_TYPE(T_RATIONAL);
COUNT_TYPE(T_NIL);
COUNT_TYPE(T_TRUE);
COUNT_TYPE(T_FALSE);
COUNT_TYPE(T_SYMBOL);
COUNT_TYPE(T_FIXNUM);
COUNT_TYPE(T_IMEMO);
COUNT_TYPE(T_UNDEF);
COUNT_TYPE(T_NODE);
COUNT_TYPE(T_ICLASS);
COUNT_TYPE(T_ZOMBIE);
COUNT_TYPE(T_MOVED);
#undef COUNT_TYPE
default: return SIZET2NUM(type); break;
}
}
// -------------------Private section end------------------------
#endif

View File

@ -1,83 +1,105 @@
#ifndef GC_GC_IMPL_H
#define GC_GC_IMPL_H
/**
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @brief Header for GC implementations introduced in [Feature #20470].
*/
#include "ruby/ruby.h"
// Bootup
void *rb_gc_impl_objspace_alloc(void);
void rb_gc_impl_objspace_init(void *objspace_ptr);
void rb_gc_impl_objspace_free(void *objspace_ptr);
void *rb_gc_impl_ractor_cache_alloc(void *objspace_ptr);
void rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache);
void rb_gc_impl_set_params(void *objspace_ptr);
void rb_gc_impl_init(void);
void rb_gc_impl_initial_stress_set(VALUE flag);
size_t *rb_gc_impl_size_pool_sizes(void *objspace_ptr);
// Shutdown
void rb_gc_impl_shutdown_free_objects(void *objspace_ptr);
// GC
void rb_gc_impl_start(void *objspace_ptr, bool full_mark, bool immediate_mark, bool immediate_sweep, bool compact);
bool rb_gc_impl_during_gc_p(void *objspace_ptr);
void rb_gc_impl_prepare_heap(void *objspace_ptr);
void rb_gc_impl_gc_enable(void *objspace_ptr);
void rb_gc_impl_gc_disable(void *objspace_ptr, bool finish_current_gc);
bool rb_gc_impl_gc_enabled_p(void *objspace_ptr);
void rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag);
VALUE rb_gc_impl_stress_get(void *objspace_ptr);
VALUE rb_gc_impl_config_get(void *objspace_ptr);
VALUE rb_gc_impl_config_set(void *objspace_ptr, VALUE hash);
// Object allocation
VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size);
size_t rb_gc_impl_obj_slot_size(VALUE obj);
size_t rb_gc_impl_size_pool_id_for_size(void *objspace_ptr, size_t size);
bool rb_gc_impl_size_allocatable_p(size_t size);
// Malloc
void *rb_gc_impl_malloc(void *objspace_ptr, size_t size);
void *rb_gc_impl_calloc(void *objspace_ptr, size_t size);
void *rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size);
void rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size);
void rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff);
// Marking
void rb_gc_impl_mark(void *objspace_ptr, VALUE obj);
void rb_gc_impl_mark_and_move(void *objspace_ptr, VALUE *ptr);
void rb_gc_impl_mark_and_pin(void *objspace_ptr, VALUE obj);
void rb_gc_impl_mark_maybe(void *objspace_ptr, VALUE obj);
void rb_gc_impl_mark_weak(void *objspace_ptr, VALUE *ptr);
void rb_gc_impl_remove_weak(void *objspace_ptr, VALUE parent_obj, VALUE *ptr);
void rb_gc_impl_objspace_mark(void *objspace_ptr);
// Compaction
bool rb_gc_impl_object_moved_p(void *objspace_ptr, VALUE obj);
VALUE rb_gc_impl_location(void *objspace_ptr, VALUE value);
// Write barriers
void rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b);
void rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj);
void rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj);
// Heap walking
void rb_gc_impl_each_objects(void *objspace_ptr, int (*callback)(void *, void *, size_t, void *), void *data);
void rb_gc_impl_each_object(void *objspace_ptr, void (*func)(VALUE obj, void *data), void *data);
// Finalizers
void rb_gc_impl_make_zombie(void *objspace_ptr, VALUE obj, void (*dfree)(void *), void *data);
VALUE rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block);
void rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj);
void rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj);
void rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr);
// Object ID
VALUE rb_gc_impl_object_id(void *objspace_ptr, VALUE obj);
VALUE rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id);
// Statistics
VALUE rb_gc_impl_set_measure_total_time(void *objspace_ptr, VALUE flag);
VALUE rb_gc_impl_get_measure_total_time(void *objspace_ptr);
VALUE rb_gc_impl_get_profile_total_time(void *objspace_ptr);
size_t rb_gc_impl_gc_count(void *objspace_ptr);
VALUE rb_gc_impl_latest_gc_info(void *objspace_ptr, VALUE key);
size_t rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym);
size_t rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym);
// Miscellaneous
size_t rb_gc_impl_obj_flags(void *objspace_ptr, VALUE obj, ID* flags, size_t max);
bool rb_gc_impl_pointer_to_heap_p(void *objspace_ptr, const void *ptr);
bool rb_gc_impl_garbage_object_p(void *objspace_ptr, VALUE obj);
void rb_gc_impl_set_event_hook(void *objspace_ptr, const rb_event_flag_t event);
void rb_gc_impl_copy_attributes(void *objspace_ptr, VALUE dest, VALUE obj);
// `GC_IMPL_FN` is an implementation detail of `!USE_SHARED_GC` builds
// to have the default GC in the same translation unit as gc.c for
// the sake of optimizer visibility. It expands to nothing unless
// you're the default GC.
//
// For the default GC, do not copy-paste this when implementing
// these functions. This takes advantage of internal linkage winning
// when appearing first. See C99 6.2.2p4.
#ifdef RB_AMALGAMATED_DEFAULT_GC
# define GC_IMPL_FN static
#else
# define GC_IMPL_FN
#endif
// Bootup
GC_IMPL_FN void *rb_gc_impl_objspace_alloc(void);
GC_IMPL_FN void rb_gc_impl_objspace_init(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_objspace_free(void *objspace_ptr);
GC_IMPL_FN void *rb_gc_impl_ractor_cache_alloc(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache);
GC_IMPL_FN void rb_gc_impl_set_params(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_init(void);
GC_IMPL_FN void rb_gc_impl_initial_stress_set(VALUE flag);
GC_IMPL_FN size_t *rb_gc_impl_size_pool_sizes(void *objspace_ptr);
// Shutdown
GC_IMPL_FN void rb_gc_impl_shutdown_free_objects(void *objspace_ptr);
// GC
GC_IMPL_FN void rb_gc_impl_start(void *objspace_ptr, bool full_mark, bool immediate_mark, bool immediate_sweep, bool compact);
GC_IMPL_FN bool rb_gc_impl_during_gc_p(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_prepare_heap(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_gc_enable(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_gc_disable(void *objspace_ptr, bool finish_current_gc);
GC_IMPL_FN bool rb_gc_impl_gc_enabled_p(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag);
GC_IMPL_FN VALUE rb_gc_impl_stress_get(void *objspace_ptr);
GC_IMPL_FN VALUE rb_gc_impl_config_get(void *objspace_ptr);
GC_IMPL_FN VALUE rb_gc_impl_config_set(void *objspace_ptr, VALUE hash);
// Object allocation
GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, VALUE v1, VALUE v2, VALUE v3, bool wb_protected, size_t alloc_size);
GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj);
GC_IMPL_FN size_t rb_gc_impl_size_pool_id_for_size(void *objspace_ptr, size_t size);
GC_IMPL_FN bool rb_gc_impl_size_allocatable_p(size_t size);
// Malloc
GC_IMPL_FN void *rb_gc_impl_malloc(void *objspace_ptr, size_t size);
GC_IMPL_FN void *rb_gc_impl_calloc(void *objspace_ptr, size_t size);
GC_IMPL_FN void *rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size);
GC_IMPL_FN void rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size);
GC_IMPL_FN void rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff);
// Marking
GC_IMPL_FN void rb_gc_impl_mark(void *objspace_ptr, VALUE obj);
GC_IMPL_FN void rb_gc_impl_mark_and_move(void *objspace_ptr, VALUE *ptr);
GC_IMPL_FN void rb_gc_impl_mark_and_pin(void *objspace_ptr, VALUE obj);
GC_IMPL_FN void rb_gc_impl_mark_maybe(void *objspace_ptr, VALUE obj);
GC_IMPL_FN void rb_gc_impl_mark_weak(void *objspace_ptr, VALUE *ptr);
GC_IMPL_FN void rb_gc_impl_remove_weak(void *objspace_ptr, VALUE parent_obj, VALUE *ptr);
GC_IMPL_FN void rb_gc_impl_objspace_mark(void *objspace_ptr);
// Compaction
GC_IMPL_FN bool rb_gc_impl_object_moved_p(void *objspace_ptr, VALUE obj);
GC_IMPL_FN VALUE rb_gc_impl_location(void *objspace_ptr, VALUE value);
// Write barriers
GC_IMPL_FN void rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b);
GC_IMPL_FN void rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj);
GC_IMPL_FN void rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj);
// Heap walking
GC_IMPL_FN void rb_gc_impl_each_objects(void *objspace_ptr, int (*callback)(void *, void *, size_t, void *), void *data);
GC_IMPL_FN void rb_gc_impl_each_object(void *objspace_ptr, void (*func)(VALUE obj, void *data), void *data);
// Finalizers
GC_IMPL_FN void rb_gc_impl_make_zombie(void *objspace_ptr, VALUE obj, void (*dfree)(void *), void *data);
GC_IMPL_FN VALUE rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block);
GC_IMPL_FN void rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj);
GC_IMPL_FN void rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj);
GC_IMPL_FN void rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr);
// Object ID
GC_IMPL_FN VALUE rb_gc_impl_object_id(void *objspace_ptr, VALUE obj);
GC_IMPL_FN VALUE rb_gc_impl_object_id_to_ref(void *objspace_ptr, VALUE object_id);
// Statistics
GC_IMPL_FN VALUE rb_gc_impl_set_measure_total_time(void *objspace_ptr, VALUE flag);
GC_IMPL_FN VALUE rb_gc_impl_get_measure_total_time(void *objspace_ptr);
GC_IMPL_FN VALUE rb_gc_impl_get_profile_total_time(void *objspace_ptr);
GC_IMPL_FN size_t rb_gc_impl_gc_count(void *objspace_ptr);
GC_IMPL_FN VALUE rb_gc_impl_latest_gc_info(void *objspace_ptr, VALUE key);
GC_IMPL_FN size_t rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym);
GC_IMPL_FN size_t rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym);
// Miscellaneous
GC_IMPL_FN size_t rb_gc_impl_obj_flags(void *objspace_ptr, VALUE obj, ID* flags, size_t max);
GC_IMPL_FN bool rb_gc_impl_pointer_to_heap_p(void *objspace_ptr, const void *ptr);
GC_IMPL_FN bool rb_gc_impl_garbage_object_p(void *objspace_ptr, VALUE obj);
GC_IMPL_FN void rb_gc_impl_set_event_hook(void *objspace_ptr, const rb_event_flag_t event);
GC_IMPL_FN void rb_gc_impl_copy_attributes(void *objspace_ptr, VALUE dest, VALUE obj);
#undef GC_IMPL_FN
#endif