Implement Objects on VWA
This commit implements Objects on Variable Width Allocation. This allows Objects with more ivars to be embedded (i.e. contents directly follow the object header) which improves performance through better cache locality.
This commit is contained in:
parent
7fda741f6e
commit
7424ea184f
Notes:
git
2022-07-15 22:21:27 +09:00
2
debug.c
2
debug.c
@ -53,7 +53,9 @@ const union {
|
|||||||
rb_econv_result_t econv_result;
|
rb_econv_result_t econv_result;
|
||||||
enum ruby_preserved_encindex encoding_index;
|
enum ruby_preserved_encindex encoding_index;
|
||||||
enum ruby_robject_flags robject_flags;
|
enum ruby_robject_flags robject_flags;
|
||||||
|
#if !USE_RVARGC
|
||||||
enum ruby_robject_consts robject_consts;
|
enum ruby_robject_consts robject_consts;
|
||||||
|
#endif
|
||||||
enum ruby_rmodule_flags rmodule_flags;
|
enum ruby_rmodule_flags rmodule_flags;
|
||||||
enum ruby_rstring_flags rstring_flags;
|
enum ruby_rstring_flags rstring_flags;
|
||||||
#if !USE_RVARGC
|
#if !USE_RVARGC
|
||||||
|
116
gc.c
116
gc.c
@ -2865,18 +2865,65 @@ rb_newobj(void)
|
|||||||
return newobj_of(0, T_NONE, 0, 0, 0, FALSE, sizeof(RVALUE));
|
return newobj_of(0, T_NONE, 0, 0, 0, FALSE, sizeof(RVALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
rb_obj_embedded_size(uint32_t numiv)
|
||||||
|
{
|
||||||
|
return offsetof(struct RObject, as.ary) + (sizeof(VALUE) * numiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
rb_class_instance_allocate_internal(VALUE klass, VALUE flags, bool wb_protected)
|
||||||
|
{
|
||||||
|
GC_ASSERT((flags & RUBY_T_MASK) == T_OBJECT);
|
||||||
|
GC_ASSERT(flags & ROBJECT_EMBED);
|
||||||
|
|
||||||
|
st_table *index_tbl = RCLASS_IV_INDEX_TBL(klass);
|
||||||
|
uint32_t index_tbl_num_entries = index_tbl == NULL ? 0 : (uint32_t)index_tbl->num_entries;
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
bool embed = true;
|
||||||
|
#if USE_RVARGC
|
||||||
|
size = rb_obj_embedded_size(index_tbl_num_entries);
|
||||||
|
if (!rb_gc_size_allocatable_p(size)) {
|
||||||
|
size = sizeof(struct RObject);
|
||||||
|
embed = false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
size = sizeof(struct RObject);
|
||||||
|
if (index_tbl_num_entries > ROBJECT_EMBED_LEN_MAX) {
|
||||||
|
embed = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_RVARGC
|
||||||
|
VALUE obj = newobj_of(klass, flags, 0, 0, 0, wb_protected, size);
|
||||||
|
#else
|
||||||
|
VALUE obj = newobj_of(klass, flags, Qundef, Qundef, Qundef, wb_protected, size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (embed) {
|
||||||
|
#if USE_RVARGC
|
||||||
|
uint32_t capa = (uint32_t)((rb_gc_obj_slot_size(obj) - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
|
||||||
|
GC_ASSERT(capa >= index_tbl_num_entries);
|
||||||
|
|
||||||
|
ROBJECT(obj)->numiv = capa;
|
||||||
|
for (size_t i = 0; i < capa; i++) {
|
||||||
|
ROBJECT(obj)->as.ary[i] = Qundef;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rb_init_iv_list(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_newobj_of(VALUE klass, VALUE flags)
|
rb_newobj_of(VALUE klass, VALUE flags)
|
||||||
{
|
{
|
||||||
if ((flags & RUBY_T_MASK) == T_OBJECT) {
|
if ((flags & RUBY_T_MASK) == T_OBJECT) {
|
||||||
st_table *index_tbl = RCLASS_IV_INDEX_TBL(klass);
|
return rb_class_instance_allocate_internal(klass, (flags | ROBJECT_EMBED) & ~FL_WB_PROTECTED, flags & FL_WB_PROTECTED);
|
||||||
|
|
||||||
VALUE obj = newobj_of(klass, (flags | ROBJECT_EMBED) & ~FL_WB_PROTECTED , Qundef, Qundef, Qundef, flags & FL_WB_PROTECTED, sizeof(RVALUE));
|
|
||||||
|
|
||||||
if (index_tbl && index_tbl->num_entries > ROBJECT_EMBED_LEN_MAX) {
|
|
||||||
rb_init_iv_list(obj);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return newobj_of(klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED, sizeof(RVALUE));
|
return newobj_of(klass, flags & ~FL_WB_PROTECTED, 0, 0, 0, flags & FL_WB_PROTECTED, sizeof(RVALUE));
|
||||||
@ -2989,17 +3036,7 @@ rb_imemo_new_debug(enum imemo_type type, VALUE v1, VALUE v2, VALUE v3, VALUE v0,
|
|||||||
VALUE
|
VALUE
|
||||||
rb_class_allocate_instance(VALUE klass)
|
rb_class_allocate_instance(VALUE klass)
|
||||||
{
|
{
|
||||||
st_table *index_tbl = RCLASS_IV_INDEX_TBL(klass);
|
return rb_class_instance_allocate_internal(klass, T_OBJECT | ROBJECT_EMBED, RGENGC_WB_PROTECTED_OBJECT);
|
||||||
|
|
||||||
VALUE flags = T_OBJECT | ROBJECT_EMBED;
|
|
||||||
|
|
||||||
VALUE obj = newobj_of(klass, flags, Qundef, Qundef, Qundef, RGENGC_WB_PROTECTED_OBJECT, sizeof(RVALUE));
|
|
||||||
|
|
||||||
if (index_tbl && index_tbl->num_entries > ROBJECT_EMBED_LEN_MAX) {
|
|
||||||
rb_init_iv_list(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
@ -8322,12 +8359,18 @@ gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, V
|
|||||||
size_t obj_size;
|
size_t obj_size;
|
||||||
|
|
||||||
switch (BUILTIN_TYPE(src)) {
|
switch (BUILTIN_TYPE(src)) {
|
||||||
case T_STRING:
|
|
||||||
obj_size = rb_str_size_as_embedded(src);
|
|
||||||
break;
|
|
||||||
case T_ARRAY:
|
case T_ARRAY:
|
||||||
obj_size = rb_ary_size_as_embedded(src);
|
obj_size = rb_ary_size_as_embedded(src);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_OBJECT:
|
||||||
|
obj_size = rb_obj_embedded_size(ROBJECT_NUMIV(src));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_STRING:
|
||||||
|
obj_size = rb_str_size_as_embedded(src);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return src_pool;
|
return src_pool;
|
||||||
}
|
}
|
||||||
@ -9899,9 +9942,34 @@ static void
|
|||||||
gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
|
gc_ref_update_object(rb_objspace_t *objspace, VALUE v)
|
||||||
{
|
{
|
||||||
VALUE *ptr = ROBJECT_IVPTR(v);
|
VALUE *ptr = ROBJECT_IVPTR(v);
|
||||||
|
uint32_t numiv = ROBJECT_NUMIV(v);
|
||||||
|
|
||||||
uint32_t i, len = ROBJECT_NUMIV(v);
|
#if USE_RVARGC
|
||||||
for (i = 0; i < len; i++) {
|
size_t slot_size = rb_gc_obj_slot_size(v);
|
||||||
|
size_t embed_size = rb_obj_embedded_size(numiv);
|
||||||
|
if (slot_size >= embed_size && !RB_FL_TEST_RAW(v, ROBJECT_EMBED)) {
|
||||||
|
// Object can be re-embedded
|
||||||
|
memcpy(ROBJECT(v)->as.ary, ptr, sizeof(VALUE) * numiv);
|
||||||
|
RB_FL_SET_RAW(v, ROBJECT_EMBED);
|
||||||
|
if (ROBJ_TRANSIENT_P(v)) {
|
||||||
|
ROBJ_TRANSIENT_UNSET(v);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
xfree(ptr);
|
||||||
|
}
|
||||||
|
ptr = ROBJECT(v)->as.ary;
|
||||||
|
|
||||||
|
uint32_t capa = (uint32_t)((slot_size - offsetof(struct RObject, as.ary)) / sizeof(VALUE));
|
||||||
|
ROBJECT(v)->numiv = capa;
|
||||||
|
|
||||||
|
// Fill end with Qundef
|
||||||
|
for (uint32_t i = numiv; i < capa; i++) {
|
||||||
|
ptr[i] = Qundef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < numiv; i++) {
|
||||||
UPDATE_IF_MOVED(objspace, ptr[i]);
|
UPDATE_IF_MOVED(objspace, ptr[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* In released versions of Ruby, this number should not be changed since teeny
|
* In released versions of Ruby, this number should not be changed since teeny
|
||||||
* versions of Ruby should guarantee ABI compatibility.
|
* versions of Ruby should guarantee ABI compatibility.
|
||||||
*/
|
*/
|
||||||
#define RUBY_ABI_VERSION 1
|
#define RUBY_ABI_VERSION 2
|
||||||
|
|
||||||
/* Windows does not support weak symbols so ruby_abi_version will not exist
|
/* Windows does not support weak symbols so ruby_abi_version will not exist
|
||||||
* in the shared library. */
|
* in the shared library. */
|
||||||
|
@ -75,6 +75,7 @@ enum ruby_robject_flags {
|
|||||||
ROBJECT_EMBED = RUBY_FL_USER1
|
ROBJECT_EMBED = RUBY_FL_USER1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if !USE_RVARGC
|
||||||
/**
|
/**
|
||||||
* This is an enum because GDB wants it (rather than a macro). People need not
|
* This is an enum because GDB wants it (rather than a macro). People need not
|
||||||
* bother.
|
* bother.
|
||||||
@ -83,6 +84,7 @@ enum ruby_robject_consts {
|
|||||||
/** Max possible number of instance variables that can be embedded. */
|
/** Max possible number of instance variables that can be embedded. */
|
||||||
ROBJECT_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(VALUE)
|
ROBJECT_EMBED_LEN_MAX = RBIMPL_EMBED_LEN_MAX_OF(VALUE)
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct st_table;
|
struct st_table;
|
||||||
|
|
||||||
@ -95,6 +97,14 @@ struct RObject {
|
|||||||
/** Basic part, including flags and class. */
|
/** Basic part, including flags and class. */
|
||||||
struct RBasic basic;
|
struct RBasic basic;
|
||||||
|
|
||||||
|
#if USE_RVARGC
|
||||||
|
/**
|
||||||
|
* Number of instance variables. This is per object; objects might
|
||||||
|
* differ in this field even if they have the identical classes.
|
||||||
|
*/
|
||||||
|
uint32_t numiv;
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Object's specific fields. */
|
/** Object's specific fields. */
|
||||||
union {
|
union {
|
||||||
|
|
||||||
@ -103,12 +113,13 @@ struct RObject {
|
|||||||
* this pattern.
|
* this pattern.
|
||||||
*/
|
*/
|
||||||
struct {
|
struct {
|
||||||
|
#if !USE_RVARGC
|
||||||
/**
|
/**
|
||||||
* Number of instance variables. This is per object; objects might
|
* Number of instance variables. This is per object; objects might
|
||||||
* differ in this field even if they have the identical classes.
|
* differ in this field even if they have the identical classes.
|
||||||
*/
|
*/
|
||||||
uint32_t numiv;
|
uint32_t numiv;
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Pointer to a C array that holds instance variables. */
|
/** Pointer to a C array that holds instance variables. */
|
||||||
VALUE *ivptr;
|
VALUE *ivptr;
|
||||||
@ -124,14 +135,38 @@ struct RObject {
|
|||||||
struct st_table *iv_index_tbl;
|
struct st_table *iv_index_tbl;
|
||||||
} heap;
|
} heap;
|
||||||
|
|
||||||
|
#if USE_RVARGC
|
||||||
|
/* Embedded instance variables. When an object is small enough, it
|
||||||
|
* uses this area to store the instance variables.
|
||||||
|
*
|
||||||
|
* This is a length 1 array because:
|
||||||
|
* 1. GCC has a bug that does not optimize C flexible array members
|
||||||
|
* (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102452)
|
||||||
|
* 2. Zero length arrays are not supported by all compilers
|
||||||
|
*/
|
||||||
|
VALUE ary[1];
|
||||||
|
#else
|
||||||
/**
|
/**
|
||||||
* Embedded instance variables. When an object is small enough, it
|
* Embedded instance variables. When an object is small enough, it
|
||||||
* uses this area to store the instance variables.
|
* uses this area to store the instance variables.
|
||||||
*/
|
*/
|
||||||
VALUE ary[ROBJECT_EMBED_LEN_MAX];
|
VALUE ary[ROBJECT_EMBED_LEN_MAX];
|
||||||
|
#endif
|
||||||
} as;
|
} as;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Offsets for YJIT */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
# if USE_RVARGC
|
||||||
|
static const int32_t ROBJECT_OFFSET_NUMIV = offsetof(struct RObject, numiv);
|
||||||
|
# else
|
||||||
|
static const int32_t ROBJECT_OFFSET_NUMIV = offsetof(struct RObject, as.heap.numiv);
|
||||||
|
# endif
|
||||||
|
static const int32_t ROBJECT_OFFSET_AS_HEAP_IVPTR = offsetof(struct RObject, as.heap.ivptr);
|
||||||
|
static const int32_t ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL = offsetof(struct RObject, as.heap.iv_index_tbl);
|
||||||
|
static const int32_t ROBJECT_OFFSET_AS_ARY = offsetof(struct RObject, as.ary);
|
||||||
|
#endif
|
||||||
|
|
||||||
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
|
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
|
||||||
RBIMPL_ATTR_ARTIFICIAL()
|
RBIMPL_ATTR_ARTIFICIAL()
|
||||||
/**
|
/**
|
||||||
@ -146,12 +181,16 @@ ROBJECT_NUMIV(VALUE obj)
|
|||||||
{
|
{
|
||||||
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
|
RBIMPL_ASSERT_TYPE(obj, RUBY_T_OBJECT);
|
||||||
|
|
||||||
|
#if USE_RVARGC
|
||||||
|
return ROBJECT(obj)->numiv;
|
||||||
|
#else
|
||||||
if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) {
|
if (RB_FL_ANY_RAW(obj, ROBJECT_EMBED)) {
|
||||||
return ROBJECT_EMBED_LEN_MAX;
|
return ROBJECT_EMBED_LEN_MAX;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return ROBJECT(obj)->as.heap.numiv;
|
return ROBJECT(obj)->as.heap.numiv;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
|
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
|
||||||
|
@ -353,12 +353,16 @@ mjit_compile_body(FILE *f, const rb_iseq_t *iseq, struct compile_status *status)
|
|||||||
// Generate merged ivar guards first if needed
|
// Generate merged ivar guards first if needed
|
||||||
if (!status->compile_info->disable_ivar_cache && status->merge_ivar_guards_p) {
|
if (!status->compile_info->disable_ivar_cache && status->merge_ivar_guards_p) {
|
||||||
fprintf(f, " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT) && (rb_serial_t)%"PRI_SERIALT_PREFIX"u == RCLASS_SERIAL(RBASIC(GET_SELF())->klass) &&", status->ivar_serial);
|
fprintf(f, " if (UNLIKELY(!(RB_TYPE_P(GET_SELF(), T_OBJECT) && (rb_serial_t)%"PRI_SERIALT_PREFIX"u == RCLASS_SERIAL(RBASIC(GET_SELF())->klass) &&", status->ivar_serial);
|
||||||
|
#if USE_RVARGC
|
||||||
|
fprintf(f, "%"PRIuSIZE" < ROBJECT_NUMIV(GET_SELF())", status->max_ivar_index); // index < ROBJECT_NUMIV(obj)
|
||||||
|
#else
|
||||||
if (status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX) {
|
if (status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX) {
|
||||||
fprintf(f, "%"PRIuSIZE" < ROBJECT_NUMIV(GET_SELF())", status->max_ivar_index); // index < ROBJECT_NUMIV(obj) && !RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
|
fprintf(f, "%"PRIuSIZE" < ROBJECT_NUMIV(GET_SELF())", status->max_ivar_index); // index < ROBJECT_NUMIV(obj) && !RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
fprintf(f, "ROBJECT_EMBED_LEN_MAX == ROBJECT_NUMIV(GET_SELF())"); // index < ROBJECT_NUMIV(obj) && RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
|
fprintf(f, "ROBJECT_EMBED_LEN_MAX == ROBJECT_NUMIV(GET_SELF())"); // index < ROBJECT_NUMIV(obj) && RB_FL_ANY_RAW(obj, ROBJECT_EMBED)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
fprintf(f, "))) {\n");
|
fprintf(f, "))) {\n");
|
||||||
fprintf(f, " goto ivar_cancel;\n");
|
fprintf(f, " goto ivar_cancel;\n");
|
||||||
fprintf(f, " }\n");
|
fprintf(f, " }\n");
|
||||||
|
33
object.c
33
object.c
@ -267,34 +267,13 @@ rb_obj_singleton_class(VALUE obj)
|
|||||||
MJIT_FUNC_EXPORTED void
|
MJIT_FUNC_EXPORTED void
|
||||||
rb_obj_copy_ivar(VALUE dest, VALUE obj)
|
rb_obj_copy_ivar(VALUE dest, VALUE obj)
|
||||||
{
|
{
|
||||||
VALUE *dst_buf = 0;
|
VALUE *dest_buf = ROBJECT_IVPTR(dest);
|
||||||
VALUE *src_buf = 0;
|
VALUE *src_buf = ROBJECT_IVPTR(obj);
|
||||||
uint32_t len = ROBJECT_EMBED_LEN_MAX;
|
uint32_t dest_len = ROBJECT_NUMIV(dest);
|
||||||
|
uint32_t src_len = ROBJECT_NUMIV(obj);
|
||||||
|
uint32_t len = dest_len < src_len ? dest_len : src_len;
|
||||||
|
|
||||||
if (RBASIC(obj)->flags & ROBJECT_EMBED) {
|
MEMCPY(dest_buf, src_buf, VALUE, len);
|
||||||
src_buf = ROBJECT(obj)->as.ary;
|
|
||||||
|
|
||||||
// embedded -> embedded
|
|
||||||
if (RBASIC(dest)->flags & ROBJECT_EMBED) {
|
|
||||||
dst_buf = ROBJECT(dest)->as.ary;
|
|
||||||
}
|
|
||||||
// embedded -> extended
|
|
||||||
else {
|
|
||||||
dst_buf = ROBJECT(dest)->as.heap.ivptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// extended -> extended
|
|
||||||
else {
|
|
||||||
RUBY_ASSERT(!(RBASIC(dest)->flags & ROBJECT_EMBED));
|
|
||||||
uint32_t src_len = ROBJECT(obj)->as.heap.numiv;
|
|
||||||
uint32_t dst_len = ROBJECT(dest)->as.heap.numiv;
|
|
||||||
|
|
||||||
len = src_len < dst_len ? src_len : dst_len;
|
|
||||||
dst_buf = ROBJECT(dest)->as.heap.ivptr;
|
|
||||||
src_buf = ROBJECT(obj)->as.heap.ivptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
MEMCPY(dst_buf, src_buf, VALUE, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -249,6 +249,30 @@ class TestGCCompact < Test::Unit::TestCase
|
|||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_moving_objects_between_size_pools
|
||||||
|
assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
|
||||||
|
begin;
|
||||||
|
class Foo
|
||||||
|
def add_ivars
|
||||||
|
10.times do |i|
|
||||||
|
instance_variable_set("@foo" + i.to_s, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
OBJ_COUNT = 500
|
||||||
|
|
||||||
|
GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||||
|
|
||||||
|
ary = OBJ_COUNT.times.map { Foo.new }
|
||||||
|
ary.each(&:add_ivars)
|
||||||
|
|
||||||
|
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||||
|
|
||||||
|
assert_operator(stats[:moved_up][:T_OBJECT], :>=, OBJ_COUNT)
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
|
||||||
def test_moving_strings_up_size_pools
|
def test_moving_strings_up_size_pools
|
||||||
omit if !GC.using_rvargc?
|
omit if !GC.using_rvargc?
|
||||||
assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
|
assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
|
||||||
|
@ -969,23 +969,24 @@ class TestMJIT < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_heap_promotion_of_ivar_in_the_middle_of_jit
|
def test_heap_promotion_of_ivar_in_the_middle_of_jit
|
||||||
|
omit if GC.using_rvargc?
|
||||||
|
|
||||||
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\n", success_count: 2, min_calls: 2)
|
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\n", success_count: 2, min_calls: 2)
|
||||||
begin;
|
begin;
|
||||||
class A
|
class A
|
||||||
def initialize
|
def initialize
|
||||||
@iv0 = nil
|
@iv0 = nil
|
||||||
@iv1 = []
|
@iv1 = []
|
||||||
@iv2 = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test(add)
|
def test(add)
|
||||||
@iv0.nil?
|
@iv0.nil?
|
||||||
@iv2.nil?
|
|
||||||
add_ivar if add
|
add_ivar if add
|
||||||
@iv1.empty?
|
@iv1.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_ivar
|
def add_ivar
|
||||||
|
@iv2 = nil
|
||||||
@iv3 = nil
|
@iv3 = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -30,15 +30,24 @@
|
|||||||
fprintf(f, " VM_ASSERT((rb_serial_t)%"PRI_SERIALT_PREFIX"u == RCLASS_SERIAL(RBASIC(obj)->klass));\n", ic_copy->entry->class_serial);
|
fprintf(f, " VM_ASSERT((rb_serial_t)%"PRI_SERIALT_PREFIX"u == RCLASS_SERIAL(RBASIC(obj)->klass));\n", ic_copy->entry->class_serial);
|
||||||
fprintf(f, " VM_ASSERT(index < ROBJECT_NUMIV(obj));\n");
|
fprintf(f, " VM_ASSERT(index < ROBJECT_NUMIV(obj));\n");
|
||||||
% if insn.name == 'setinstancevariable'
|
% if insn.name == 'setinstancevariable'
|
||||||
|
#if USE_RVARGC
|
||||||
|
fprintf(f, " if (LIKELY(!RB_OBJ_FROZEN_RAW(obj) && index < ROBJECT_NUMIV(obj))) {\n");
|
||||||
|
fprintf(f, " RB_OBJ_WRITE(obj, &ROBJECT_IVPTR(obj)[index], stack[%d]);\n", b->stack_size - 1);
|
||||||
|
#else
|
||||||
fprintf(f, " if (LIKELY(!RB_OBJ_FROZEN_RAW(obj) && %s)) {\n", status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "true" : "RB_FL_ANY_RAW(obj, ROBJECT_EMBED)");
|
fprintf(f, " if (LIKELY(!RB_OBJ_FROZEN_RAW(obj) && %s)) {\n", status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "true" : "RB_FL_ANY_RAW(obj, ROBJECT_EMBED)");
|
||||||
fprintf(f, " RB_OBJ_WRITE(obj, &ROBJECT(obj)->as.%s, stack[%d]);\n",
|
fprintf(f, " RB_OBJ_WRITE(obj, &ROBJECT(obj)->as.%s, stack[%d]);\n",
|
||||||
status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "heap.ivptr[index]" : "ary[index]", b->stack_size - 1);
|
status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "heap.ivptr[index]" : "ary[index]", b->stack_size - 1);
|
||||||
|
#endif
|
||||||
fprintf(f, " }\n");
|
fprintf(f, " }\n");
|
||||||
% else
|
% else
|
||||||
fprintf(f, " VALUE val;\n");
|
fprintf(f, " VALUE val;\n");
|
||||||
|
#if USE_RVARGC
|
||||||
|
fprintf(f, " if (LIKELY(index < ROBJECT_NUMIV(obj) && (val = ROBJECT_IVPTR(obj)[index]) != Qundef)) {\n");
|
||||||
|
#else
|
||||||
fprintf(f, " if (LIKELY(%s && (val = ROBJECT(obj)->as.%s) != Qundef)) {\n",
|
fprintf(f, " if (LIKELY(%s && (val = ROBJECT(obj)->as.%s) != Qundef)) {\n",
|
||||||
status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "true" : "RB_FL_ANY_RAW(obj, ROBJECT_EMBED)",
|
status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "true" : "RB_FL_ANY_RAW(obj, ROBJECT_EMBED)",
|
||||||
status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "heap.ivptr[index]" : "ary[index]");
|
status->max_ivar_index >= ROBJECT_EMBED_LEN_MAX ? "heap.ivptr[index]" : "ary[index]");
|
||||||
|
#endif
|
||||||
fprintf(f, " stack[%d] = val;\n", b->stack_size);
|
fprintf(f, " stack[%d] = val;\n", b->stack_size);
|
||||||
fprintf(f, " }\n");
|
fprintf(f, " }\n");
|
||||||
%end
|
%end
|
||||||
|
@ -1467,6 +1467,8 @@ void
|
|||||||
rb_obj_transient_heap_evacuate(VALUE obj, int promote)
|
rb_obj_transient_heap_evacuate(VALUE obj, int promote)
|
||||||
{
|
{
|
||||||
if (ROBJ_TRANSIENT_P(obj)) {
|
if (ROBJ_TRANSIENT_P(obj)) {
|
||||||
|
assert(!RB_FL_TEST_RAW(obj, ROBJECT_EMBED));
|
||||||
|
|
||||||
uint32_t len = ROBJECT_NUMIV(obj);
|
uint32_t len = ROBJECT_NUMIV(obj);
|
||||||
const VALUE *old_ptr = ROBJECT_IVPTR(obj);
|
const VALUE *old_ptr = ROBJECT_IVPTR(obj);
|
||||||
VALUE *new_ptr;
|
VALUE *new_ptr;
|
||||||
@ -1493,7 +1495,7 @@ init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl)
|
|||||||
if (RBASIC(obj)->flags & ROBJECT_EMBED) {
|
if (RBASIC(obj)->flags & ROBJECT_EMBED) {
|
||||||
newptr = obj_ivar_heap_alloc(obj, newsize);
|
newptr = obj_ivar_heap_alloc(obj, newsize);
|
||||||
MEMCPY(newptr, ptr, VALUE, len);
|
MEMCPY(newptr, ptr, VALUE, len);
|
||||||
RBASIC(obj)->flags &= ~ROBJECT_EMBED;
|
RB_FL_UNSET_RAW(obj, ROBJECT_EMBED);
|
||||||
ROBJECT(obj)->as.heap.ivptr = newptr;
|
ROBJECT(obj)->as.heap.ivptr = newptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1503,7 +1505,11 @@ init_iv_list(VALUE obj, uint32_t len, uint32_t newsize, st_table *index_tbl)
|
|||||||
for (; len < newsize; len++) {
|
for (; len < newsize; len++) {
|
||||||
newptr[len] = Qundef;
|
newptr[len] = Qundef;
|
||||||
}
|
}
|
||||||
|
#if USE_RVARGC
|
||||||
|
ROBJECT(obj)->numiv = newsize;
|
||||||
|
#else
|
||||||
ROBJECT(obj)->as.heap.numiv = newsize;
|
ROBJECT(obj)->as.heap.numiv = newsize;
|
||||||
|
#endif
|
||||||
ROBJECT(obj)->as.heap.iv_index_tbl = index_tbl;
|
ROBJECT(obj)->as.heap.iv_index_tbl = index_tbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,9 @@ fn main() {
|
|||||||
// Import YARV bytecode instruction constants
|
// Import YARV bytecode instruction constants
|
||||||
.allowlist_type("ruby_vminsn_type")
|
.allowlist_type("ruby_vminsn_type")
|
||||||
|
|
||||||
|
// From include/ruby/internal/config.h
|
||||||
|
.allowlist_var("USE_RVARGC")
|
||||||
|
|
||||||
// From include/ruby/internal/intern/string.h
|
// From include/ruby/internal/intern/string.h
|
||||||
.allowlist_function("rb_utf8_str_new")
|
.allowlist_function("rb_utf8_str_new")
|
||||||
.allowlist_function("rb_str_buf_append")
|
.allowlist_function("rb_str_buf_append")
|
||||||
@ -135,7 +138,8 @@ fn main() {
|
|||||||
|
|
||||||
// From include/ruby/internal/core/robject.h
|
// From include/ruby/internal/core/robject.h
|
||||||
.allowlist_type("ruby_robject_flags")
|
.allowlist_type("ruby_robject_flags")
|
||||||
.allowlist_type("ruby_robject_consts")
|
// .allowlist_type("ruby_robject_consts") // Removed when USE_RVARGC
|
||||||
|
.allowlist_var("ROBJECT_OFFSET_.*")
|
||||||
|
|
||||||
// From include/ruby/internal/core/rarray.h
|
// From include/ruby/internal/core/rarray.h
|
||||||
.allowlist_type("ruby_rarray_flags")
|
.allowlist_type("ruby_rarray_flags")
|
||||||
|
@ -1966,9 +1966,17 @@ fn gen_get_ivar(
|
|||||||
ctx.stack_pop(1);
|
ctx.stack_pop(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if USE_RVARGC != 0 {
|
||||||
|
// Check that the ivar table is big enough
|
||||||
|
// Check that the slot is inside the ivar table (num_slots > index)
|
||||||
|
let num_slots = mem_opnd(32, REG0, ROBJECT_OFFSET_NUMIV);
|
||||||
|
cmp(cb, num_slots, uimm_opnd(ivar_index as u64));
|
||||||
|
jle_ptr(cb, counted_exit!(ocb, side_exit, getivar_idx_out_of_range));
|
||||||
|
}
|
||||||
|
|
||||||
// Compile time self is embedded and the ivar index lands within the object
|
// Compile time self is embedded and the ivar index lands within the object
|
||||||
let test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) };
|
let test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) };
|
||||||
if test_result && ivar_index < (ROBJECT_EMBED_LEN_MAX.as_usize()) {
|
if test_result {
|
||||||
// See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
|
// See ROBJECT_IVPTR() from include/ruby/internal/core/robject.h
|
||||||
|
|
||||||
// Guard that self is embedded
|
// Guard that self is embedded
|
||||||
@ -1988,7 +1996,7 @@ fn gen_get_ivar(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Load the variable
|
// Load the variable
|
||||||
let offs = RUBY_OFFSET_ROBJECT_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32;
|
let offs = ROBJECT_OFFSET_AS_ARY + (ivar_index * SIZEOF_VALUE) as i32;
|
||||||
let ivar_opnd = mem_opnd(64, REG0, offs);
|
let ivar_opnd = mem_opnd(64, REG0, offs);
|
||||||
mov(cb, REG1, ivar_opnd);
|
mov(cb, REG1, ivar_opnd);
|
||||||
|
|
||||||
@ -2019,17 +2027,16 @@ fn gen_get_ivar(
|
|||||||
side_exit,
|
side_exit,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if USE_RVARGC == 0 {
|
||||||
// Check that the extended table is big enough
|
// Check that the extended table is big enough
|
||||||
if ivar_index > (ROBJECT_EMBED_LEN_MAX.as_usize()) {
|
|
||||||
// Check that the slot is inside the extended table (num_slots > index)
|
// Check that the slot is inside the extended table (num_slots > index)
|
||||||
let num_slots = mem_opnd(32, REG0, RUBY_OFFSET_ROBJECT_AS_HEAP_NUMIV);
|
let num_slots = mem_opnd(32, REG0, ROBJECT_OFFSET_NUMIV);
|
||||||
|
|
||||||
cmp(cb, num_slots, uimm_opnd(ivar_index as u64));
|
cmp(cb, num_slots, uimm_opnd(ivar_index as u64));
|
||||||
jle_ptr(cb, counted_exit!(ocb, side_exit, getivar_idx_out_of_range));
|
jle_ptr(cb, counted_exit!(ocb, side_exit, getivar_idx_out_of_range));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a pointer to the extended table
|
// Get a pointer to the extended table
|
||||||
let tbl_opnd = mem_opnd(64, REG0, RUBY_OFFSET_ROBJECT_AS_HEAP_IVPTR);
|
let tbl_opnd = mem_opnd(64, REG0, ROBJECT_OFFSET_AS_HEAP_IVPTR);
|
||||||
mov(cb, REG0, tbl_opnd);
|
mov(cb, REG0, tbl_opnd);
|
||||||
|
|
||||||
// Read the ivar from the extended table
|
// Read the ivar from the extended table
|
||||||
|
@ -739,10 +739,6 @@ mod manual_defs {
|
|||||||
pub const RUBY_OFFSET_RSTRUCT_AS_HEAP_PTR: i32 = 24; // struct RStruct, subfield "as.heap.ptr"
|
pub const RUBY_OFFSET_RSTRUCT_AS_HEAP_PTR: i32 = 24; // struct RStruct, subfield "as.heap.ptr"
|
||||||
pub const RUBY_OFFSET_RSTRUCT_AS_ARY: i32 = 16; // struct RStruct, subfield "as.ary"
|
pub const RUBY_OFFSET_RSTRUCT_AS_ARY: i32 = 16; // struct RStruct, subfield "as.ary"
|
||||||
|
|
||||||
pub const RUBY_OFFSET_ROBJECT_AS_ARY: i32 = 16; // struct RObject, subfield "as.ary"
|
|
||||||
pub const RUBY_OFFSET_ROBJECT_AS_HEAP_NUMIV: i32 = 16; // struct RObject, subfield "as.heap.numiv"
|
|
||||||
pub const RUBY_OFFSET_ROBJECT_AS_HEAP_IVPTR: i32 = 24; // struct RObject, subfield "as.heap.ivptr"
|
|
||||||
|
|
||||||
// Constants from rb_control_frame_t vm_core.h
|
// Constants from rb_control_frame_t vm_core.h
|
||||||
pub const RUBY_OFFSET_CFP_PC: i32 = 0;
|
pub const RUBY_OFFSET_CFP_PC: i32 = 0;
|
||||||
pub const RUBY_OFFSET_CFP_SP: i32 = 8;
|
pub const RUBY_OFFSET_CFP_SP: i32 = 8;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/* automatically generated by rust-bindgen 0.59.2 */
|
/* automatically generated by rust-bindgen 0.59.2 */
|
||||||
|
|
||||||
|
pub const USE_RVARGC: u32 = 1;
|
||||||
pub const INTEGER_REDEFINED_OP_FLAG: u32 = 1;
|
pub const INTEGER_REDEFINED_OP_FLAG: u32 = 1;
|
||||||
pub const FLOAT_REDEFINED_OP_FLAG: u32 = 2;
|
pub const FLOAT_REDEFINED_OP_FLAG: u32 = 2;
|
||||||
pub const STRING_REDEFINED_OP_FLAG: u32 = 4;
|
pub const STRING_REDEFINED_OP_FLAG: u32 = 4;
|
||||||
@ -110,8 +111,10 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
pub const ROBJECT_EMBED: ruby_robject_flags = 8192;
|
pub const ROBJECT_EMBED: ruby_robject_flags = 8192;
|
||||||
pub type ruby_robject_flags = u32;
|
pub type ruby_robject_flags = u32;
|
||||||
pub const ROBJECT_EMBED_LEN_MAX: ruby_robject_consts = 3;
|
pub const ROBJECT_OFFSET_NUMIV: i32 = 16;
|
||||||
pub type ruby_robject_consts = u32;
|
pub const ROBJECT_OFFSET_AS_HEAP_IVPTR: i32 = 24;
|
||||||
|
pub const ROBJECT_OFFSET_AS_HEAP_IV_INDEX_TBL: i32 = 32;
|
||||||
|
pub const ROBJECT_OFFSET_AS_ARY: i32 = 24;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub static mut rb_mKernel: VALUE;
|
pub static mut rb_mKernel: VALUE;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user