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

ruby-changes:68842

From: Aaron <ko1@a...>
Date: Thu, 21 Oct 2021 08:14:12 +0900 (JST)
Subject: [ruby-changes:68842] c15a577eda (master): Make Blocks depend on BOPS

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

From c15a577eda78f1944ade1e9ae1bdadeaeee3c8d7 Mon Sep 17 00:00:00 2001
From: Aaron Patterson <tenderlove@r...>
Date: Tue, 9 Mar 2021 11:01:16 -0800
Subject: Make Blocks depend on BOPS

When a BOP is redefined, the BOP redefinition callback will invalidate
any blocks that depend on BOPS.  This allows us to eliminate runtime
checks for BOP redefinition.
---
 bootstraptest/test_yjit.rb | 35 +++++++++++++++++++++++++++
 yjit_codegen.c             | 60 ++++++++++++----------------------------------
 yjit_iface.c               | 36 ++++++++++++++++++++++------
 yjit_iface.h               |  1 +
 4 files changed, 80 insertions(+), 52 deletions(-)

diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index cf79ccd101..f7e1fe4a7e 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1,3 +1,38 @@ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L1
+# BOP redefined methods work when JIT compiled
+assert_equal 'false', %q{
+  def less_than x
+    x < 10
+  end
+
+  class Integer
+    def < x
+      false
+    end
+  end
+
+  less_than 2
+  less_than 2
+  less_than 2
+}
+
+# BOP redefinition works on Integer#<
+assert_equal 'false', %q{
+  def less_than x
+    x < 10
+  end
+
+  less_than 2
+  less_than 2
+
+  class Integer
+    def < x
+      false
+    end
+  end
+
+  less_than 2
+}
+
 # Putobject, less-than operator, fixnums
 assert_equal '2', %q{
     def check_index(index)
diff --git a/yjit_codegen.c b/yjit_codegen.c
index ab16ccbf42..0877517850 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -740,15 +740,9 @@ gen_fixnum_cmp(jitstate_t* jit, ctx_t* ctx, cmov_fn cmov_op) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L740
     // Note: we generate the side-exit before popping operands from the stack
     uint8_t* side_exit = yjit_side_exit(jit, ctx);
 
-    // TODO: make a helper function for guarding on op-not-redefined
-    // Make sure that minus isn't redefined for integers
-    mov(cb, RAX, const_ptr_opnd(ruby_current_vm_ptr));
-    test(
-        cb,
-        member_opnd_idx(RAX, rb_vm_t, redefined_flag, BOP_LT),
-        imm_opnd(INTEGER_REDEFINED_OP_FLAG)
-    );
-    jnz_ptr(cb, side_exit);
+    if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_LT)) {
+        return YJIT_CANT_COMPILE;
+    }
 
     // Get the operands and destination from the stack
     int arg1_type = ctx_get_top_type(ctx);
@@ -821,15 +815,9 @@ gen_opt_aref(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L815
     // Create a size-exit to fall back to the interpreter
     uint8_t* side_exit = yjit_side_exit(jit, ctx);
 
-    // TODO: make a helper function for guarding on op-not-redefined
-    // Make sure that aref isn't redefined for arrays.
-    mov(cb, RAX, const_ptr_opnd(ruby_current_vm_ptr));
-    test(
-        cb,
-        member_opnd_idx(RAX, rb_vm_t, redefined_flag, BOP_AREF),
-        imm_opnd(ARRAY_REDEFINED_OP_FLAG)
-    );
-    jnz_ptr(cb, side_exit);
+    if (!assume_bop_not_redefined(jit->block, ARRAY_REDEFINED_OP_FLAG, BOP_AREF)) {
+        return YJIT_CANT_COMPILE;
+    }
 
     // Pop the stack operands
     x86opnd_t idx_opnd = ctx_stack_pop(ctx, 1);
@@ -881,15 +869,9 @@ gen_opt_and(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L869
     // Note: we generate the side-exit before popping operands from the stack
     uint8_t* side_exit = yjit_side_exit(jit, ctx);
 
-    // TODO: make a helper function for guarding on op-not-redefined
-    // Make sure that plus isn't redefined for integers
-    mov(cb, RAX, const_ptr_opnd(ruby_current_vm_ptr));
-    test(
-        cb,
-        member_opnd_idx(RAX, rb_vm_t, redefined_flag, BOP_AND),
-        imm_opnd(INTEGER_REDEFINED_OP_FLAG)
-    );
-    jnz_ptr(cb, side_exit);
+    if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_AND)) {
+        return YJIT_CANT_COMPILE;
+    }
 
     // Get the operands and destination from the stack
     int arg1_type = ctx_get_top_type(ctx);
@@ -925,15 +907,9 @@ gen_opt_minus(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L907
     // Note: we generate the side-exit before popping operands from the stack
     uint8_t* side_exit = yjit_side_exit(jit, ctx);
 
-    // TODO: make a helper function for guarding on op-not-redefined
-    // Make sure that minus isn't redefined for integers
-    mov(cb, RAX, const_ptr_opnd(ruby_current_vm_ptr));
-    test(
-        cb,
-        member_opnd_idx(RAX, rb_vm_t, redefined_flag, BOP_MINUS),
-        imm_opnd(INTEGER_REDEFINED_OP_FLAG)
-    );
-    jnz_ptr(cb, side_exit);
+    if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_MINUS)) {
+        return YJIT_CANT_COMPILE;
+    }
 
     // Get the operands and destination from the stack
     x86opnd_t arg1 = ctx_stack_pop(ctx, 1);
@@ -965,15 +941,9 @@ gen_opt_plus(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L941
     // Note: we generate the side-exit before popping operands from the stack
     uint8_t* side_exit = yjit_side_exit(jit, ctx);
 
-    // TODO: make a helper function for guarding on op-not-redefined
-    // Make sure that plus isn't redefined for integers
-    mov(cb, RAX, const_ptr_opnd(ruby_current_vm_ptr));
-    test(
-        cb,
-        member_opnd_idx(RAX, rb_vm_t, redefined_flag, BOP_PLUS),
-        imm_opnd(INTEGER_REDEFINED_OP_FLAG)
-    );
-    jnz_ptr(cb, side_exit);
+    if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_PLUS)) {
+        return YJIT_CANT_COMPILE;
+    }
 
     // Get the operands and destination from the stack
     int arg1_type = ctx_get_top_type(ctx);
diff --git a/yjit_iface.c b/yjit_iface.c
index 6c43a53728..6424d34938 100644
--- a/yjit_iface.c
+++ b/yjit_iface.c
@@ -174,6 +174,23 @@ add_lookup_dependency_i(st_data_t *key, st_data_t *value, st_data_t data, int ex https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L174
     return ST_CONTINUE;
 }
 
+// Hash table of BOP blocks
+static st_table *blocks_assuming_bops;
+
+bool
+assume_bop_not_redefined(block_t *block, int redefined_flag, enum ruby_basic_operators bop)
+{
+    if (BASIC_OP_UNREDEFINED_P(bop, redefined_flag)) {
+        if (blocks_assuming_bops) {
+            st_insert(blocks_assuming_bops, (st_data_t)block, 0);
+        }
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
 // Remember that the currently compiling block is only valid while cme and cc are valid
 void
 assume_method_lookup_stable(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme, block_t *block)
@@ -341,6 +358,10 @@ yjit_block_assumptions_free(block_t *block) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L358
     if (blocks_assuming_single_ractor_mode) {
         st_delete(blocks_assuming_single_ractor_mode, &as_st_data, NULL);
     }
+
+    if (blocks_assuming_bops) {
+        st_delete(blocks_assuming_bops, &as_st_data, NULL);
+    }
 }
 
 void
@@ -440,13 +461,6 @@ iseq_end_index(VALUE self) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L461
     return INT2NUM(block->end_idx);
 }
 
-/* Called when a basic operation is redefined */
-void
-rb_yjit_bop_redefined(VALUE klass, const rb_method_entry_t *me, enum ruby_basic_operators bop)
-{
-    //fprintf(stderr, "bop redefined\n");
-}
-
 static int
 block_invalidation_iterator(st_data_t key, st_data_t value, st_data_t data) {
     block_t *block = (block_t *)key;
@@ -454,6 +468,13 @@ block_invalidation_iterator(st_data_t key, st_data_t value, st_data_t data) { https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L468
     return ST_CONTINUE;
 }
 
+/* Called when a basic operation is redefined */
+void
+rb_yjit_bop_redefined(VALUE klass, const rb_method_entry_t *me, enum ruby_basic_operators bop)
+{
+    st_foreach(blocks_assuming_bops, block_invalidation_iterator, 0);
+}
+
 /* Called when the constant state changes */
 void
 rb_yjit_constant_state_changed(void)
@@ -782,6 +803,7 @@ rb_yjit_init(struct rb_yjit_options *options) https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L803
 
     blocks_assuming_stable_global_constant_state = st_init_numtable();
     blocks_assuming_single_ractor_mode = st_init_numtable();
+    blocks_assuming_bops = st_init_numtable();
 
     yjit_init_core();
     yjit_init_codegen();
diff --git a/yjit_iface.h b/yjit_iface.h
index 600fa47a34..d525337d24 100644
--- a/yjit_iface.h
+++ b/yjit_iface.h
@@ -78,6 +78,7 @@ int opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc); https://github.com/ruby/ruby/blob/trunk/yjit_iface.h#L78
 void check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_callable_method_entry_t *compile_time_cme);
 bool cfunc_needs_frame(const rb_method_cfunc_t *cfunc);
 
+RBIMPL_ATTR_NODISCARD() bool assume_bop_not_redefined(block_t *block, int redefined_flag, enum ruby_basic_operators bop);
 void assume_method_lookup_stable(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme, block_t* block);
 RBIMPL_ATTR_NODISCARD() bool assume_single_ractor_mode(block_t *block);
 RBIMPL_ATTR_NODISCARD() bool assume_stable_global_constant_state(block_t *block);
-- 
cgit v1.2.1


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

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