ruby-changes:72613
From: Peter <ko1@a...>
Date: Thu, 21 Jul 2022 02:14:17 +0900 (JST)
Subject: [ruby-changes:72613] 5871ecf956 (master): Add RARRAY_LITERAL_FLAG for array literals
https://git.ruby-lang.org/ruby.git/commit/?id=5871ecf956 From 5871ecf956711fcacad7c03f2aef95115ed25bc4 Mon Sep 17 00:00:00 2001 From: Peter Zhu <peter@p...> Date: Tue, 19 Jul 2022 11:15:05 -0400 Subject: Add RARRAY_LITERAL_FLAG for array literals 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@g...> --- array.c | 46 ++++++++++++++++++++++++++++++++++------------ compile.c | 4 ++-- internal/array.h | 1 + 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/array.c b/array.c index 7f8c25ac08..deea0834d7 100644 --- a/array.c +++ b/array.c @@ -153,15 +153,17 @@ should_not_be_shared_and_embedded(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L153 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); \ @@ -173,6 +175,11 @@ should_not_be_shared_and_embedded(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L175 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) { @@ -249,7 +256,7 @@ ary_verify_(VALUE ary, const char *file, int line) https://github.com/ruby/ruby/blob/trunk/array.c#L256 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); } @@ -581,8 +588,10 @@ ary_double_capa(VALUE ary, long min) https://github.com/ruby/ruby/blob/trunk/array.c#L588 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 @@ -610,9 +619,11 @@ rb_ary_reset(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L619 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; } @@ -971,6 +982,15 @@ rb_ary_tmp_new_fill(long capa) https://github.com/ruby/ruby/blob/trunk/array.c#L982 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) { @@ -1024,6 +1044,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/array.c#L1044 ary_make_shared(VALUE ary) { assert(!ARY_EMBED_P(ary)); + assert(!ARY_LITERAL_P(ary)); ary_verify(ary); if (ARY_SHARED_P(ary)) { @@ -1034,8 +1055,8 @@ ary_make_shared(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L1055 } 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; } @@ -1324,10 +1345,11 @@ ary_make_partial(VALUE ary, VALUE klass, long offset, long len) https://github.com/ruby/ruby/blob/trunk/array.c#L1345 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); diff --git a/compile.c b/compile.c index f95b3bb143..8c975782d7 100644 --- a/compile.c +++ b/compile.c @@ -4369,7 +4369,7 @@ compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int pop https://github.com/ruby/ruby/blob/trunk/compile.c#L4369 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) @@ -12349,7 +12349,7 @@ ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_heade https://github.com/ruby/ruby/blob/trunk/compile.c#L12349 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++) { diff --git a/internal/array.h b/internal/array.h index 690196a1e2..aa3b540153 100644 --- a/internal/array.h +++ b/internal/array.h @@ -33,6 +33,7 @@ void rb_ary_cancel_sharing(VALUE ary); https://github.com/ruby/ruby/blob/trunk/internal/array.h#L33 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); -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/