[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]