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.
This commit is contained in:
Aaron Patterson 2021-03-09 11:01:16 -08:00 коммит произвёл Alan Wu
Родитель 46874b8fb9
Коммит c15a577eda
4 изменённых файлов: 80 добавлений и 52 удалений

Просмотреть файл

@ -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 # Putobject, less-than operator, fixnums
assert_equal '2', %q{ assert_equal '2', %q{
def check_index(index) def check_index(index)

Просмотреть файл

@ -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 // Note: we generate the side-exit before popping operands from the stack
uint8_t* side_exit = yjit_side_exit(jit, ctx); uint8_t* side_exit = yjit_side_exit(jit, ctx);
// TODO: make a helper function for guarding on op-not-redefined if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_LT)) {
// Make sure that minus isn't redefined for integers return YJIT_CANT_COMPILE;
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);
// Get the operands and destination from the stack // Get the operands and destination from the stack
int arg1_type = ctx_get_top_type(ctx); 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 // Create a size-exit to fall back to the interpreter
uint8_t* side_exit = yjit_side_exit(jit, ctx); uint8_t* side_exit = yjit_side_exit(jit, ctx);
// TODO: make a helper function for guarding on op-not-redefined if (!assume_bop_not_redefined(jit->block, ARRAY_REDEFINED_OP_FLAG, BOP_AREF)) {
// Make sure that aref isn't redefined for arrays. return YJIT_CANT_COMPILE;
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);
// Pop the stack operands // Pop the stack operands
x86opnd_t idx_opnd = ctx_stack_pop(ctx, 1); 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 // Note: we generate the side-exit before popping operands from the stack
uint8_t* side_exit = yjit_side_exit(jit, ctx); uint8_t* side_exit = yjit_side_exit(jit, ctx);
// TODO: make a helper function for guarding on op-not-redefined if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_AND)) {
// Make sure that plus isn't redefined for integers return YJIT_CANT_COMPILE;
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);
// Get the operands and destination from the stack // Get the operands and destination from the stack
int arg1_type = ctx_get_top_type(ctx); 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 // Note: we generate the side-exit before popping operands from the stack
uint8_t* side_exit = yjit_side_exit(jit, ctx); uint8_t* side_exit = yjit_side_exit(jit, ctx);
// TODO: make a helper function for guarding on op-not-redefined if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_MINUS)) {
// Make sure that minus isn't redefined for integers return YJIT_CANT_COMPILE;
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);
// Get the operands and destination from the stack // Get the operands and destination from the stack
x86opnd_t arg1 = ctx_stack_pop(ctx, 1); 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 // Note: we generate the side-exit before popping operands from the stack
uint8_t* side_exit = yjit_side_exit(jit, ctx); uint8_t* side_exit = yjit_side_exit(jit, ctx);
// TODO: make a helper function for guarding on op-not-redefined if (!assume_bop_not_redefined(jit->block, INTEGER_REDEFINED_OP_FLAG, BOP_PLUS)) {
// Make sure that plus isn't redefined for integers return YJIT_CANT_COMPILE;
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);
// Get the operands and destination from the stack // Get the operands and destination from the stack
int arg1_type = ctx_get_top_type(ctx); int arg1_type = ctx_get_top_type(ctx);

Просмотреть файл

@ -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; 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 // Remember that the currently compiling block is only valid while cme and cc are valid
void void
assume_method_lookup_stable(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme, block_t *block) 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) { if (blocks_assuming_single_ractor_mode) {
st_delete(blocks_assuming_single_ractor_mode, &as_st_data, NULL); 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 void
@ -440,13 +461,6 @@ iseq_end_index(VALUE self)
return INT2NUM(block->end_idx); 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 static int
block_invalidation_iterator(st_data_t key, st_data_t value, st_data_t data) { block_invalidation_iterator(st_data_t key, st_data_t value, st_data_t data) {
block_t *block = (block_t *)key; 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; 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 */ /* Called when the constant state changes */
void void
rb_yjit_constant_state_changed(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_stable_global_constant_state = st_init_numtable();
blocks_assuming_single_ractor_mode = st_init_numtable(); blocks_assuming_single_ractor_mode = st_init_numtable();
blocks_assuming_bops = st_init_numtable();
yjit_init_core(); yjit_init_core();
yjit_init_codegen(); yjit_init_codegen();

Просмотреть файл

@ -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); 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); 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); 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_single_ractor_mode(block_t *block);
RBIMPL_ATTR_NODISCARD() bool assume_stable_global_constant_state(block_t *block); RBIMPL_ATTR_NODISCARD() bool assume_stable_global_constant_state(block_t *block);