1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

End current block after opt_send_without_block

This commit is contained in:
Maxime Chevalier-Boisvert 2021-01-15 17:10:52 -05:00 committed by Alan Wu
parent 79331368b9
commit 02a9700475
3 changed files with 133 additions and 116 deletions

View file

@ -640,40 +640,103 @@ gen_opt_plus(jitstate_t* jit, ctx_t* ctx)
return true;
}
// Verify that calling with cd on receiver goes to callee
static void
check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_callable_method_entry_t *compile_time_cme)
void
gen_branchunless_branch(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape)
{
if (METHOD_ENTRY_INVALIDATED(compile_time_cme)) {
rb_bug("ujit: output code uses invalidated cme %p", (void *)compile_time_cme);
}
switch (shape)
{
case SHAPE_NEXT0:
jnz_ptr(cb, target1);
break;
bool callee_correct = false;
const rb_callable_method_entry_t *cme = rb_callable_method_entry(CLASS_OF(receiver), vm_ci_mid(cd->ci));
if (cme->def->type == VM_METHOD_TYPE_CFUNC) {
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
if ((void *)cfunc->func == callee) {
callee_correct = true;
}
}
if (!callee_correct) {
rb_bug("ujit: output code calls wrong method cd->cc->klass: %p", (void *)cd->cc->klass);
case SHAPE_NEXT1:
jz_ptr(cb, target0);
break;
case SHAPE_DEFAULT:
jz_ptr(cb, target0);
jmp_ptr(cb, target1);
break;
}
}
MJIT_FUNC_EXPORTED VALUE rb_hash_has_key(VALUE hash, VALUE key);
static bool
gen_branchunless(jitstate_t* jit, ctx_t* ctx)
{
// TODO: we need to eventually do an interrupt check when jumping/branching
// How can we do this while keeping the check logic out of line?
// Maybe we can push the check int into the next block or the stub?
//
// RUBY_VM_CHECK_INTS(ec);
// Test if any bit (outside of the Qnil bit) is on
// RUBY_Qfalse /* ...0000 0000 */
// RUBY_Qnil /* ...0000 1000 */
x86opnd_t val_opnd = ctx_stack_pop(ctx, 1);
test(cb, val_opnd, imm_opnd(~Qnil));
// Get the branch target instruction offsets
uint32_t next_idx = jit_next_idx(jit);
uint32_t jump_idx = next_idx + (uint32_t)jit_get_arg(jit, 0);
blockid_t next_block = { jit->iseq, next_idx };
blockid_t jump_block = { jit->iseq, jump_idx };
// Generate the branch instructions
gen_branch(
jit->block,
ctx,
jump_block,
ctx,
next_block,
ctx,
gen_branchunless_branch
);
return true;
}
void
gen_jump_branch(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape)
{
switch (shape)
{
case SHAPE_NEXT0:
break;
case SHAPE_NEXT1:
assert (false);
break;
case SHAPE_DEFAULT:
jmp_ptr(cb, target0);
break;
}
}
static bool
cfunc_needs_frame(const rb_method_cfunc_t *cfunc)
gen_jump(jitstate_t* jit, ctx_t* ctx)
{
void* fptr = (void*)cfunc->func;
// Get the branch target instruction offsets
uint32_t jump_idx = jit_next_idx(jit) + (int32_t)jit_get_arg(jit, 0);
blockid_t jump_block = { jit->iseq, jump_idx };
// Leaf C functions do not need a stack frame
// or a stack overflow check
return !(
// Hash#key?
fptr == (void*)rb_hash_has_key
//
// TODO:
// RUBY_VM_CHECK_INTS(ec);
//
// Generate the jump instruction
gen_branch(
jit->block,
ctx,
jump_block,
ctx,
BLOCKID_NULL,
ctx,
gen_jump_branch
);
return true;
}
static bool
@ -750,7 +813,7 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
mov(cb, REG0, recv);
// Callee method ID
ID mid = vm_ci_mid(cd->ci);
//ID mid = vm_ci_mid(cd->ci);
//printf("JITting call to C function \"%s\", argc: %lu\n", rb_id2name(mid), argc);
//print_str(cb, "");
//print_str(cb, "calling CFUNC:");
@ -919,99 +982,13 @@ gen_opt_send_without_block(jitstate_t* jit, ctx_t* ctx)
);
}
return true;
}
void
gen_branchunless_branch(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape)
{
switch (shape)
{
case SHAPE_NEXT0:
jnz_ptr(cb, target1);
break;
case SHAPE_NEXT1:
jz_ptr(cb, target0);
break;
case SHAPE_DEFAULT:
jz_ptr(cb, target0);
jmp_ptr(cb, target1);
break;
}
}
static bool
gen_branchunless(jitstate_t* jit, ctx_t* ctx)
{
// TODO: we need to eventually do an interrupt check when jumping/branching
// How can we do this while keeping the check logic out of line?
// Maybe we can push the check int into the next block or the stub?
//
// RUBY_VM_CHECK_INTS(ec);
// Test if any bit (outside of the Qnil bit) is on
// RUBY_Qfalse /* ...0000 0000 */
// RUBY_Qnil /* ...0000 1000 */
x86opnd_t val_opnd = ctx_stack_pop(ctx, 1);
test(cb, val_opnd, imm_opnd(~Qnil));
// Get the branch target instruction offsets
uint32_t next_idx = jit_next_idx(jit);
uint32_t jump_idx = next_idx + (uint32_t)jit_get_arg(jit, 0);
blockid_t next_block = { jit->iseq, next_idx };
blockid_t jump_block = { jit->iseq, jump_idx };
// Generate the branch instructions
// Jump (fall through) to the call continuation block
// We do this to end the current block after the call
blockid_t cont_block = { jit->iseq, jit_next_idx(jit) };
gen_branch(
jit->block,
ctx,
jump_block,
ctx,
next_block,
ctx,
gen_branchunless_branch
);
return true;
}
void
gen_jump_branch(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape)
{
switch (shape)
{
case SHAPE_NEXT0:
break;
case SHAPE_NEXT1:
assert (false);
break;
case SHAPE_DEFAULT:
jmp_ptr(cb, target0);
break;
}
}
static bool
gen_jump(jitstate_t* jit, ctx_t* ctx)
{
// Get the branch target instruction offsets
uint32_t jump_idx = jit_next_idx(jit) + (int32_t)jit_get_arg(jit, 0);
blockid_t jump_block = { jit->iseq, jump_idx };
//
// TODO:
// RUBY_VM_CHECK_INTS(ec);
//
// Generate the jump instruction
gen_branch(
jit->block,
ctx,
jump_block,
cont_block,
ctx,
BLOCKID_NULL,
ctx,
@ -1066,7 +1043,7 @@ ujit_init_codegen(void)
ujit_reg_op(BIN(opt_lt), gen_opt_lt, false);
ujit_reg_op(BIN(opt_minus), gen_opt_minus, false);
ujit_reg_op(BIN(opt_plus), gen_opt_plus, false);
ujit_reg_op(BIN(opt_send_without_block), gen_opt_send_without_block, false);
ujit_reg_op(BIN(branchunless), gen_branchunless, true);
ujit_reg_op(BIN(jump), gen_jump, true);
ujit_reg_op(BIN(opt_send_without_block), gen_opt_send_without_block, true);
}

View file

@ -63,6 +63,42 @@ opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc)
}
}
// Verify that calling with cd on receiver goes to callee
void
check_cfunc_dispatch(VALUE receiver, struct rb_call_data *cd, void *callee, rb_callable_method_entry_t *compile_time_cme)
{
if (METHOD_ENTRY_INVALIDATED(compile_time_cme)) {
rb_bug("ujit: output code uses invalidated cme %p", (void *)compile_time_cme);
}
bool callee_correct = false;
const rb_callable_method_entry_t *cme = rb_callable_method_entry(CLASS_OF(receiver), vm_ci_mid(cd->ci));
if (cme->def->type == VM_METHOD_TYPE_CFUNC) {
const rb_method_cfunc_t *cfunc = UNALIGNED_MEMBER_PTR(cme->def, body.cfunc);
if ((void *)cfunc->func == callee) {
callee_correct = true;
}
}
if (!callee_correct) {
rb_bug("ujit: output code calls wrong method cd->cc->klass: %p", (void *)cd->cc->klass);
}
}
MJIT_FUNC_EXPORTED VALUE rb_hash_has_key(VALUE hash, VALUE key);
bool
cfunc_needs_frame(const rb_method_cfunc_t *cfunc)
{
void* fptr = (void*)cfunc->func;
// Leaf C functions do not need a stack frame
// or a stack overflow check
return !(
// Hash#key?
fptr == (void*)rb_hash_has_key
);
}
// GC root for interacting with the GC
struct ujit_root_struct {};

View file

@ -22,8 +22,12 @@ struct rb_callcache;
void cb_write_pre_call_bytes(codeblock_t* cb);
void cb_write_post_call_bytes(codeblock_t* cb);
void map_addr2insn(void *code_ptr, int insn);
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);
void assume_method_lookup_stable(const struct rb_callcache *cc, const rb_callable_method_entry_t *cme, block_t* block);
#endif // #ifndef UJIT_IFACE_H