зеркало из https://github.com/github/ruby.git
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.
This commit is contained in:
Родитель
46874b8fb9
Коммит
c15a577eda
|
@ -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);
|
||||||
|
|
36
yjit_iface.c
36
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;
|
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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче