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

ruby-changes:68678

From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:12:18 +0900 (JST)
Subject: [ruby-changes:68678] 188c54428c (master): MicroJIT: avoid having to invalidate running output code

https://git.ruby-lang.org/ruby.git/commit/?id=188c54428c

From 188c54428c46c1098cda0e366ee8c974f25ac07b Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
Date: Thu, 29 Oct 2020 17:26:49 -0400
Subject: MicroJIT: avoid having to invalidate running output code

---
 test/ruby/test_microjit.rb | 27 +++++++++++++++++++++++++
 ujit_compile.c             | 50 +++++++++++++++++++++++++++++-----------------
 ujit_compile.h             |  1 -
 3 files changed, 59 insertions(+), 19 deletions(-)
 create mode 100644 test/ruby/test_microjit.rb

diff --git a/test/ruby/test_microjit.rb b/test/ruby/test_microjit.rb
new file mode 100644
index 0000000000..f0c876b70f
--- /dev/null
+++ b/test/ruby/test_microjit.rb
@@ -0,0 +1,27 @@ https://github.com/ruby/ruby/blob/trunk/test/ruby/test_microjit.rb#L1
+# frozen_string_literal: true
+require 'test/unit'
+
+class TestMicroJIT < Test::Unit::TestCase
+  # MicroJIT's code invalidation mechanism can't invalidate
+  # code that is executing. Test that we don't try to do that.
+  def test_code_invalidation
+    klass = Class.new do
+      def alias_then_hash(klass, method_to_redefine)
+        klass.alias_method(method_to_redefine, :itself)
+        hash
+      end
+    end
+
+    instance = klass.new
+    i = 0
+    while i < 12
+      if i < 11
+        instance.alias_then_hash(klass, :bar)
+      else
+        ret = instance.alias_then_hash(klass, :hash)
+        assert(instance.equal?(ret))
+      end
+      i += 1
+    end
+  end
+end
diff --git a/ujit_compile.c b/ujit_compile.c
index df9b9e3d0a..7a8e809879 100644
--- a/ujit_compile.c
+++ b/ujit_compile.c
@@ -25,6 +25,8 @@ https://github.com/ruby/ruby/blob/trunk/ujit_compile.c#L25
 #define UJIT_CHECK_MODE 0
 #endif
 
+// >= 1: print when output code invalidation happens
+// >= 2: dump list of instructions when regions compile
 #ifndef UJIT_DUMP_MODE
 #define UJIT_DUMP_MODE 0
 #endif
@@ -358,22 +360,20 @@ Compile a sequence of bytecode instructions starting at `insn_idx`. https://github.com/ruby/ruby/blob/trunk/ujit_compile.c#L360
 Return the index to the first instruction not compiled in the sequence
 through `next_ujit_idx`. Return `NULL` in case compilation fails.
 */
-uint8_t *
+static uint8_t *
 ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *next_ujit_idx)
 {
     assert (cb != NULL);
-
+    unsigned first_insn_idx = insn_idx;
     VALUE *encoded = iseq->body->iseq_encoded;
 
     // NOTE: if we are ever deployed in production, we
     // should probably just log an error and return NULL here,
     // so we can fail more gracefully
-    if (cb->write_pos + 1024 >= cb->mem_size)
-    {
+    if (cb->write_pos + 1024 >= cb->mem_size) {
         rb_bug("out of executable memory");
     }
-    if (ocb->write_pos + 1024 >= ocb->mem_size)
-    {
+    if (ocb->write_pos + 1024 >= ocb->mem_size) {
         rb_bug("out of executable memory (outlined block)");
     }
 
@@ -396,9 +396,8 @@ ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *ne https://github.com/ruby/ruby/blob/trunk/ujit_compile.c#L396
     ctx.replacement_idx = insn_idx;
 
     // For each instruction to compile
-    size_t num_instrs;
-    for (num_instrs = 0;; ++num_instrs)
-    {
+    unsigned num_instrs;
+    for (num_instrs = 0;; ++num_instrs) {
         // Set the current PC
         ctx.pc = &encoded[insn_idx];
 
@@ -407,16 +406,14 @@ ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *ne https://github.com/ruby/ruby/blob/trunk/ujit_compile.c#L406
 
         // Lookup the codegen function for this instruction
         st_data_t st_gen_fn;
-        if (!rb_st_lookup(gen_fns, opcode, &st_gen_fn))
-        {
+        if (!rb_st_lookup(gen_fns, opcode, &st_gen_fn)) {
             //print_int(cb, imm_opnd(num_instrs));
             //print_str(cb, insn_name(opcode));
             break;
         }
 
         // Write the pre call bytes before the first instruction
-        if (num_instrs == 0)
-        {
+        if (num_instrs == 0) {
             ujit_gen_entry(cb);
 
             // Load the current SP from the CFP into REG_SP
@@ -425,29 +422,46 @@ ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *ne https://github.com/ruby/ruby/blob/trunk/ujit_compile.c#L422
 
         // Call the code generation function
         codegen_fn gen_fn = (codegen_fn)st_gen_fn;
-        if (!gen_fn(cb, ocb, &ctx))
-        {
+        if (!gen_fn(cb, ocb, &ctx)) {
             break;
         }
 
     	// Move to the next instruction
         insn_idx += insn_len(opcode);
+
+        // Ensure we only have one send per region. Our code invalidation mechanism can't
+        // invalidate running code and one send could invalidate the other if we had
+        // multiple in the same region.
+        if (opcode == BIN(opt_send_without_block)) {
+            break;
+        }
     }
 
     // Let the caller know how many instructions ujit compiled
     *next_ujit_idx = insn_idx;
 
     // If no instructions were compiled
-    if (num_instrs == 0)
-    {
+    if (num_instrs == 0) {
         return NULL;
     }
 
     // Generate code to exit to the interpreter
-    ujit_gen_exit(cb, &ctx, ctx.pc);
+    ujit_gen_exit(cb, &ctx, &encoded[*next_ujit_idx]);
 
     addr2insn_bookkeeping(code_ptr, first_opcode);
 
+    if (UJIT_DUMP_MODE >= 2) {
+        // Dump list of compiled instrutions
+        fprintf(stderr, "Compiled the following for iseq=%p:\n", (void *)iseq);
+        VALUE *pc = &encoded[first_insn_idx];
+        VALUE *end_pc = &encoded[*next_ujit_idx];
+        while (pc < end_pc) {
+            int opcode = opcode_at_pc(iseq, pc);
+            fprintf(stderr, "  %04td %s\n", pc - encoded, insn_name(opcode));
+            pc += insn_len(opcode);
+        }
+    }
+
     return code_ptr;
 }
 
diff --git a/ujit_compile.h b/ujit_compile.h
index 4b2031d1a8..15078386ff 100644
--- a/ujit_compile.h
+++ b/ujit_compile.h
@@ -25,7 +25,6 @@ bool rb_ujit_enabled_p(void) https://github.com/ruby/ruby/blob/trunk/ujit_compile.h#L25
 
 void rb_ujit_method_lookup_change(VALUE cme_or_cc);
 void rb_ujit_init(void);
-uint8_t *ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *next_ujit_idx);
 void rb_ujit_compile_iseq(const rb_iseq_t *iseq);
 
 #endif
-- 
cgit v1.2.1


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

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