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

ruby-changes:68781

From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:13:32 +0900 (JST)
Subject: [ruby-changes:68781] 48b8c5106c (master): Mark and update object references in generated code

https://git.ruby-lang.org/ruby.git/commit/?id=48b8c5106c

From 48b8c5106ccdd4deb22035b9989e9feb86e199f7 Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
Date: Fri, 19 Feb 2021 15:03:12 -0500
Subject: Mark and update object references in generated code

Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@g...>
---
 bootstraptest/test_ujit.rb | 18 ++++++++++++++++++
 ujit_codegen.c             | 32 ++++++++++++++++++++++++--------
 ujit_core.c                | 13 ++++++++++++-
 ujit_core.h                |  7 +++++--
 ujit_iface.c               | 26 ++++++++++++++++++++++++++
 5 files changed, 85 insertions(+), 11 deletions(-)

diff --git a/bootstraptest/test_ujit.rb b/bootstraptest/test_ujit.rb
index f28d1f37df..5d6695b34a 100644
--- a/bootstraptest/test_ujit.rb
+++ b/bootstraptest/test_ujit.rb
@@ -214,3 +214,21 @@ assert_equal ":special\n", %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_ujit.rb#L214
   p foo(special)
   nil
 }
+
+# Test that object references in generated code get marked and moved
+assert_equal "good", %q{
+  def bar
+    "good"
+  end
+
+  def foo
+    bar
+  end
+
+  foo
+  foo
+
+  GC.verify_compaction_references(double_heap: true, toward: :empty)
+
+  foo
+}
diff --git a/ujit_codegen.c b/ujit_codegen.c
index 0e311f19e2..ac911eee58 100644
--- a/ujit_codegen.c
+++ b/ujit_codegen.c
@@ -60,6 +60,22 @@ jit_get_arg(jitstate_t* jit, size_t arg_idx) https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L60
     return *(jit->pc + arg_idx + 1);
 }
 
+// Load a pointer to a GC'd object into a register and keep track of the reference
+static void
+jit_mov_gc_ptr(jitstate_t* jit, codeblock_t* cb, x86opnd_t reg, VALUE ptr)
+{
+    RUBY_ASSERT(reg.type == OPND_REG && reg.num_bits == 64);
+    RUBY_ASSERT(!SPECIAL_CONST_P(ptr));
+
+    mov(cb, reg, const_ptr_opnd((void*)ptr));
+    // The pointer immediate is encoded as the last part of the mov written out.
+    uint32_t ptr_offset = cb->write_pos - sizeof(VALUE);
+
+    if (!rb_darray_append(&jit->block->gc_object_offsets, ptr_offset)) {
+        rb_bug("allocation failed");
+    }
+}
+
 /**
 Generate an inline exit to return to the interpreter
 */
@@ -1083,7 +1099,7 @@ gen_opt_swb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const r https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1099
     assume_method_lookup_stable(cd->cc, cme, jit->block);
 
     // Bail if receiver class is different from compile-time call cache class
-    mov(cb, REG1, imm_opnd(cd->cc->klass));
+    jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass);
     cmp(cb, klass_opnd, REG1);
     jne_ptr(cb, side_exit);
 
@@ -1107,7 +1123,7 @@ gen_opt_swb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const r https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1123
 
         // Put compile time cme into REG1. It's assumed to be valid because we are notified when
         // any cme we depend on become outdated. See rb_ujit_method_lookup_change().
-        mov(cb, REG1, const_ptr_opnd(cme));
+        jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cme);
         // Write method entry at sp[-3]
         // sp[-3] = me;
         mov(cb, mem_opnd(64, REG0, 8 * -3), REG1);
@@ -1161,9 +1177,9 @@ gen_opt_swb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const r https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1177
 
         // Call check_cfunc_dispatch
         mov(cb, RDI, recv);
-        mov(cb, RSI, const_ptr_opnd(cd));
+        jit_mov_gc_ptr(jit, cb, RSI, (VALUE)cd);
         mov(cb, RDX, const_ptr_opnd((void *)cfunc->func));
-        mov(cb, RCX, const_ptr_opnd(cme));
+        jit_mov_gc_ptr(jit, cb, RCX, (VALUE)cme);
         call_ptr(cb, REG0, (void *)&check_cfunc_dispatch);
 
         // Restore registers
@@ -1242,7 +1258,7 @@ gen_opt_swb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const r https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1258
 
 bool rb_simple_iseq_p(const rb_iseq_t *iseq);
 
-void
+static void
 gen_return_branch(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape)
 {
     switch (shape)
@@ -1316,7 +1332,7 @@ gen_opt_swb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1332
     assume_method_lookup_stable(cd->cc, cme, jit->block);
 
     // Bail if receiver class is different from compile-time call cache class
-    mov(cb, REG1, imm_opnd(cd->cc->klass));
+    jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass);
     cmp(cb, klass_opnd, REG1);
     jne_ptr(cb, side_exit);
 
@@ -1344,7 +1360,7 @@ gen_opt_swb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1360
 
     // Put compile time cme into REG1. It's assumed to be valid because we are notified when
     // any cme we depend on become outdated. See rb_ujit_method_lookup_change().
-    mov(cb, REG1, const_ptr_opnd(cme));
+    jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cme);
     // Write method entry at sp[-3]
     // sp[-3] = me;
     mov(cb, mem_opnd(64, REG0, 8 * -3), REG1);
@@ -1379,7 +1395,7 @@ gen_opt_swb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1395
     mov(cb, member_opnd(REG_CFP, rb_control_frame_t, ep), REG0);
     mov(cb, REG0, recv);
     mov(cb, member_opnd(REG_CFP, rb_control_frame_t, self), REG0);
-    mov(cb, REG0, const_ptr_opnd(iseq));
+    jit_mov_gc_ptr(jit, cb, REG0, (VALUE)iseq);
     mov(cb, member_opnd(REG_CFP, rb_control_frame_t, iseq), REG0);
     mov(cb, REG0, const_ptr_opnd(start_pc));
     mov(cb, member_opnd(REG_CFP, rb_control_frame_t, pc), REG0);
diff --git a/ujit_core.c b/ujit_core.c
index e642b67544..9553c96ec3 100644
--- a/ujit_core.c
+++ b/ujit_core.c
@@ -155,7 +155,7 @@ get_first_version(const rb_iseq_t *iseq, unsigned idx) https://github.com/ruby/ruby/blob/trunk/ujit_core.c#L155
     return rb_darray_get(body->ujit_blocks, idx);
 }
 
-// Add a block version to the map. Block should be fully constructed
+// Keep track of a block version. Block should be fully constructed.
 static void
 add_block_version(blockid_t blockid, block_t* block)
 {
@@ -195,6 +195,17 @@ add_block_version(blockid_t blockid, block_t* block) https://github.com/ruby/ruby/blob/trunk/ujit_core.c#L195
         RB_OBJ_WRITTEN(iseq, Qundef, block->dependencies.iseq);
         RB_OBJ_WRITTEN(iseq, Qundef, block->dependencies.cc);
         RB_OBJ_WRITTEN(iseq, Qundef, block->dependencies.cme);
+
+        // Run write barrier for all objects in generated code.
+        uint32_t *offset_element;
+        rb_darray_foreach(block->gc_object_offsets, offset_idx, offset_element) {
+            uint32_t offset_to_value = *offset_element;
+            uint8_t *value_address = cb_get_ptr(cb, offset_to_value);
+
+            VALUE object;
+            memcpy(&object, value_address, SIZEOF_VALUE);
+            RB_OBJ_WRITTEN(iseq, Qundef, object);
+        }
     }
 }
 
diff --git a/ujit_core.h b/ujit_core.h
index 051fadb4a3..710a9fb8de 100644
--- a/ujit_core.h
+++ b/ujit_core.h
@@ -95,6 +95,9 @@ typedef struct BranchEntry https://github.com/ruby/ruby/blob/trunk/ujit_core.h#L95
 
 } branch_t;
 
+
+typedef rb_darray(uint32_t) offset_array_t;
+
 /**
 Basic block version
 Represents a portion of an iseq compiled with a given context
@@ -122,8 +125,8 @@ typedef struct ujit_block_version https://github.com/ruby/ruby/blob/trunk/ujit_core.h#L125
     // Next block version for this blockid (singly-linked list)
     struct ujit_block_version *next;
 
-    // List node for all block versions in an iseq
-    struct list_node iseq_block_node;
+    // Offsets for GC managed objects in the mainline code block
+    offset_array_t gc_object_offsets;
 
     // GC managed objects that this block depend on
     struct {
diff --git a/ujit_iface.c b/ujit_iface.c
index 15a484fb8e..b9368b2fe1 100644
--- a/ujit_iface.c
+++ b/ujit_iface.c
@@ -566,6 +566,17 @@ rb_ujit_iseq_mark(const struct rb_iseq_constant_body *body) https://github.com/ruby/ruby/blob/trunk/ujit_iface.c#L566
             rb_gc_mark_movable(block->dependencies.cc);
             rb_gc_mark_movable(block->dependencies.cme);
             rb_gc_mark_movable(block->dependencies.iseq);
+
+            // Walk over references to objects in generated code.
+            uint32_t *offset_element;
+            rb_darray_foreach(block->gc_object_offsets, offset_idx, offset_element) {
+                uint32_t offset_to_value = *offset_element;
+                uint8_t *value_address = cb_get_ptr(cb, offset_to_value);
+
+                VALUE object;
+                memcpy(&object, value_address, SIZEOF_VALUE);
+                rb_gc_mark_movable(object);
+            }
         }
     }
 }
@@ -581,6 +592,21 @@ rb_ujit_iseq_update_references(const struct rb_iseq_constant_body *body) https://github.com/ruby/ruby/blob/trunk/ujit_iface.c#L592
             block->dependencies.cc = rb_gc_location(block->dependencies.cc);
             block->dependencies.cme = rb_gc_location(block->dependencies.cme);
             block->dependencies.iseq = rb_gc_location(block->dependencies.iseq);
+
+            // Walk over references to objects in generated code.
+            uint32_t *offset_element;
+            rb_darray_foreach(block->gc_object_offsets, offset_idx, offset_element) {
+                uint32_t offset_to_value = *offset_element;
+                uint8_t *value_address = cb_get_ptr(cb, offset_to_value);
+
+                VALUE object;
+                memcpy(&object, value_address, SIZEOF_VALUE);
+                VALUE possibly_moved = rb_gc_location(object);
+                // Only write when the VALUE moves, to be CoW friendly.
+                if (possibly_moved != object) {
+                    memcpy(value_address, &possibly_moved, SIZEOF_VALUE);
+                }
+            }
         }
     }
 }
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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