summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstraptest/test_yjit.rb35
-rw-r--r--yjit_codegen.c60
-rw-r--r--yjit_iface.c36
-rw-r--r--yjit_iface.h1
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 @@
+# 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)
// 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)
// 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)
// 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)
// 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)
// 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
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)
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)
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) {
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)
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);
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);