ruby-changes:68706
From: Maxime <ko1@a...>
Date: Thu, 21 Oct 2021 08:12:30 +0900 (JST)
Subject: [ruby-changes:68706] 02a9700475 (master): End current block after opt_send_without_block
https://git.ruby-lang.org/ruby.git/commit/?id=02a9700475 From 02a97004751eae471e00b2b6c3ce86c9dd6ae259 Mon Sep 17 00:00:00 2001 From: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@s...> Date: Fri, 15 Jan 2021 17:10:52 -0500 Subject: End current block after opt_send_without_block --- ujit_codegen.c | 209 +++++++++++++++++++++++++-------------------------------- ujit_iface.c | 36 ++++++++++ ujit_iface.h | 4 ++ 3 files changed, 133 insertions(+), 116 deletions(-) diff --git a/ujit_codegen.c b/ujit_codegen.c index 24b0dab4b3..006d64fe0f 100644 --- a/ujit_codegen.c +++ b/ujit_codegen.c @@ -640,40 +640,103 @@ gen_opt_plus(jitstate_t* jit, ctx_t* ctx) https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L640 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 }; + + // + // TODO: + // RUBY_VM_CHECK_INTS(ec); + // - // Leaf C functions do not need a stack frame - // or a stack overflow check - return !( - // Hash#key? - fptr == (void*)rb_hash_has_key + // 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) https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L813 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) https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L982 ); } - 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) https://github.com/ruby/ruby/blob/trunk/ujit_codegen.c#L1043 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); } diff --git a/ujit_iface.c b/ujit_iface.c index 583326ce42..d759560cb6 100644 --- a/ujit_iface.c +++ b/ujit_iface.c @@ -63,6 +63,42 @@ opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc) https://github.com/ruby/ruby/blob/trunk/ujit_iface.c#L63 } } +// 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 {}; diff --git a/ujit_iface.h b/ujit_iface.h index 0c6bf2456c..aa335dbf19 100644 --- a/ujit_iface.h +++ b/ujit_iface.h @@ -22,8 +22,12 @@ struct rb_callcache; https://github.com/ruby/ruby/blob/trunk/ujit_iface.h#L22 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* bl (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/