Skip to content

Commit

Permalink
Add RARRAY_LITERAL_FLAG for array literals
Browse files Browse the repository at this point in the history
Array created as literals during iseq compilation don't need a
reference count since they can never be modified. The previous
implementation would mutate the hidden array's reference count,
causing copy-on-write invalidation.

This commit adds a RARRAY_LITERAL_FLAG for arrays created through
rb_ary_literal_new. Arrays created with this flag do not have reference
count stored and just assume they have infinite number of references.

Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
  • Loading branch information
peterzhu2118 and byroot committed Jul 20, 2022
1 parent b25ee69 commit 5871ecf
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 14 deletions.
46 changes: 34 additions & 12 deletions array.c
Expand Up @@ -153,15 +153,17 @@ should_not_be_shared_and_embedded(VALUE ary)
const VALUE _value_ = (value); \
assert(!ARY_EMBED_P(_ary_)); \
assert(ARY_SHARED_P(_ary_)); \
assert(ARY_SHARED_ROOT_P(_value_)); \
assert(!ARY_LITERAL_P(_ary_)); \
assert(ARY_SHARED_ROOT_P(_value_) || ARY_LITERAL_P(_value_)); \
RB_OBJ_WRITE(_ary_, &RARRAY(_ary_)->as.heap.aux.shared_root, _value_); \
} while (0)

#define RARRAY_SHARED_ROOT_FLAG FL_USER12
#define ARY_SHARED_ROOT_P(ary) (assert(should_be_T_ARRAY((VALUE)(ary))), \
FL_TEST_RAW((ary), RARRAY_SHARED_ROOT_FLAG))
#define ARY_SHARED_ROOT_REFCNT(ary) \
(assert(ARY_SHARED_ROOT_P(ary)), RARRAY(ary)->as.heap.aux.capa)
#define ARY_SHARED_ROOT_OCCUPIED(ary) (ARY_SHARED_ROOT_REFCNT(ary) == 1)
#define ARY_SHARED_ROOT_OCCUPIED(ary) (!ARY_LITERAL_P(ary) && ARY_SHARED_ROOT_REFCNT(ary) == 1)
#define ARY_SET_SHARED_ROOT_REFCNT(ary, value) do { \
assert(ARY_SHARED_ROOT_P(ary)); \
assert((value) >= 0); \
Expand All @@ -173,6 +175,11 @@ should_not_be_shared_and_embedded(VALUE ary)
FL_SET((ary), RARRAY_SHARED_ROOT_FLAG); \
} while (0)

#define RARRAY_LITERAL_FLAG FL_USER15
#define ARY_LITERAL_P(ary) \
(assert(should_be_T_ARRAY((VALUE)(ary))), \
FL_TEST_RAW((ary), RARRAY_LITERAL_FLAG))

static inline void
ARY_SET(VALUE a, long i, VALUE v)
{
Expand Down Expand Up @@ -249,7 +256,7 @@ ary_verify_(VALUE ary, const char *file, int line)
const VALUE *ptr = ARY_HEAP_PTR(ary);
const VALUE *root_ptr = RARRAY_CONST_PTR_TRANSIENT(root);
long len = ARY_HEAP_LEN(ary), root_len = RARRAY_LEN(root);
assert(FL_TEST(root, RARRAY_SHARED_ROOT_FLAG));
assert(ARY_SHARED_ROOT_P(root) || ARY_LITERAL_P(root));
assert(root_ptr <= ptr && ptr + len <= root_ptr + root_len);
ary_verify(root);
}
Expand Down Expand Up @@ -581,8 +588,10 @@ ary_double_capa(VALUE ary, long min)
static void
rb_ary_decrement_share(VALUE shared_root)
{
long num = ARY_SHARED_ROOT_REFCNT(shared_root);
ARY_SET_SHARED_ROOT_REFCNT(shared_root, num - 1);
if (!ARY_LITERAL_P(shared_root)) {
long num = ARY_SHARED_ROOT_REFCNT(shared_root);
ARY_SET_SHARED_ROOT_REFCNT(shared_root, num - 1);
}
}

static void
Expand Down Expand Up @@ -610,9 +619,11 @@ rb_ary_reset(VALUE ary)
static VALUE
rb_ary_increment_share(VALUE shared_root)
{
long num = ARY_SHARED_ROOT_REFCNT(shared_root);
assert(num >= 0);
ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1);
if (!ARY_LITERAL_P(shared_root)) {
long num = ARY_SHARED_ROOT_REFCNT(shared_root);
assert(num >= 0);
ARY_SET_SHARED_ROOT_REFCNT(shared_root, num + 1);
}
return shared_root;
}

Expand Down Expand Up @@ -971,6 +982,15 @@ rb_ary_tmp_new_fill(long capa)
return ary;
}

VALUE
rb_ary_literal_new(long capa)
{
VALUE ary = ary_new(0, capa);
rb_ary_transient_heap_evacuate(ary, TRUE);
FL_SET(ary, RARRAY_LITERAL_FLAG);
return ary;
}

void
rb_ary_free(VALUE ary)
{
Expand Down Expand Up @@ -1024,6 +1044,7 @@ static VALUE
ary_make_shared(VALUE ary)
{
assert(!ARY_EMBED_P(ary));
assert(!ARY_LITERAL_P(ary));
ary_verify(ary);

if (ARY_SHARED_P(ary)) {
Expand All @@ -1034,8 +1055,8 @@ ary_make_shared(VALUE ary)
}
else if (OBJ_FROZEN(ary)) {
rb_ary_transient_heap_evacuate(ary, TRUE);
ary_shrink_capa(ary);
FL_SET_SHARED_ROOT(ary);
ary_shrink_capa(ary);
FL_SET_SHARED_ROOT(ary);
ARY_SET_SHARED_ROOT_REFCNT(ary, 1);
return ary;
}
Expand Down Expand Up @@ -1324,10 +1345,11 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len)
return result;
}
else {
VALUE shared, result = ary_alloc_heap(klass);
VALUE result = ary_alloc_heap(klass);
assert(!ARY_EMBED_P(result));

shared = ary_make_shared(ary);
VALUE shared = ARY_LITERAL_P(ary) ? ary : ary_make_shared(ary);

ARY_SET_PTR(result, RARRAY_CONST_PTR_TRANSIENT(ary));
ARY_SET_LEN(result, RARRAY_LEN(ary));
rb_ary_set_shared(result, shared);
Expand Down
4 changes: 2 additions & 2 deletions compile.c
Expand Up @@ -4369,7 +4369,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop

if ((first_chunk && stack_len == 0 && !node_tmp) || count >= min_tmp_ary_len) {
/* The literal contains only optimizable elements, or the subarray is long enough */
VALUE ary = rb_ary_tmp_new(count);
VALUE ary = rb_ary_literal_new(count);

/* Create a hidden array */
for (; count; count--, node = node->nd_next)
Expand Down Expand Up @@ -12349,7 +12349,7 @@ ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_heade

const long len = (long)ibf_load_small_value(load, &reading_pos);

VALUE ary = rb_ary_new_capa(len);
VALUE ary = header->internal ? rb_ary_literal_new(len) : rb_ary_new_capa(len);
int i;

for (i=0; i<len; i++) {
Expand Down
1 change: 1 addition & 0 deletions internal/array.h
Expand Up @@ -33,6 +33,7 @@ void rb_ary_cancel_sharing(VALUE ary);
size_t rb_ary_size_as_embedded(VALUE ary);
void rb_ary_make_embedded(VALUE ary);
bool rb_ary_embeddable_p(VALUE ary);
VALUE rb_ary_literal_new(long capa);

static inline VALUE rb_ary_entry_internal(VALUE ary, long offset);
static inline bool ARY_PTR_USING_P(VALUE ary);
Expand Down

0 comments on commit 5871ecf

Please sign in to comment.