ruby-changes:68907
From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:16:06 +0900 (JST)
Subject: [ruby-changes:68907] 7eef8f09c0 (master): Implement getblockparamproxy
https://git.ruby-lang.org/ruby.git/commit/?id=7eef8f09c0 From 7eef8f09c09d054b0554a304456a0bbd9df40d86 Mon Sep 17 00:00:00 2001 From: Alan Wu <XrXr@u...> Date: Mon, 10 May 2021 17:05:12 -0400 Subject: Implement getblockparamproxy * Implement getblockparamproxy * Parallel runner: wait for timeout thread to terminate after killing Or else the leak cheaker could sees the thread as running and cause test failures in test-tool. * Add a comment, use jne * Comment about where 0x3 comes from --- bootstraptest/test_yjit.rb | 50 ++++++++++++++++++++++++++++++++++------------ yjit_codegen.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ yjit_core.h | 2 ++ 3 files changed, 88 insertions(+), 13 deletions(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index abbe40e9e7..524b387502 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -858,26 +858,50 @@ assert_equal 'raised', %q{ https://github.com/ruby/ruby/blob/trunk/bootstraptest/test_yjit.rb#L858 # test calling Ruby method with a block assert_equal '[1, 2, 42]', %q{ -def thing(a, b) - [a, b, yield] -end + def thing(a, b) + [a, b, yield] + end -def use - thing(1,2) { 42 } -end + def use + thing(1,2) { 42 } + end -use -use + use + use } # test calling C method with a block assert_equal '[42, 42]', %q{ -def use(array, initial) - array.reduce(initial) { |a, b| a + b } -end + def use(array, initial) + array.reduce(initial) { |a, b| a + b } + end + + use([], 0) + [use([2, 2], 38), use([14, 14, 14], 0)] +} + +# test calling block param +assert_equal '[1, 2, 42]', %q{ + def foo(&block) + block.call + end -use([], 0) -[use([2, 2], 38), use([14, 14, 14], 0)] + [foo {1}, foo {2}, foo {42}] +} + +# test calling block param failing +assert_equal '42', %q{ + def foo(&block) + block.call + end + + foo {} # warmup + + begin + foo + rescue NoMethodError => e + 42 if nil == e.receiver + end } # test calling method taking block param diff --git a/yjit_codegen.c b/yjit_codegen.c index 3d3e309772..e7910987c0 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -221,6 +221,8 @@ yjit_gen_exit(jitstate_t *jit, ctx_t *ctx, codeblock_t *cb) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L221 { uint8_t *code_ptr = cb_get_ptr(cb, cb->write_pos); + ADD_COMMENT(cb, "exit to interpreter"); + VALUE *exit_pc = jit->pc; // YJIT only ever patches the first instruction in an iseq @@ -2527,6 +2529,52 @@ gen_opt_getinlinecache(jitstate_t *jit, ctx_t *ctx) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L2529 return YJIT_END_BLOCK; } +// Push the explict block parameter onto the temporary stack. Part of the +// interpreter's scheme for avoiding Proc allocations when delegating +// explict block parameters. +static codegen_status_t +gen_getblockparamproxy(jitstate_t *jit, ctx_t *ctx) +{ + // A mirror of the interpreter code. Checking for the case + // where it's pushing rb_block_param_proxy. + uint8_t *side_exit = yjit_side_exit(jit, ctx); + + // EP level + VALUE level = jit_get_arg(jit, 1); + + if (level != 0) { + // Bail on non zero level to make getting the ep simple + return YJIT_CANT_COMPILE; + } + + // Load environment pointer EP from CFP + mov(cb, REG0, member_opnd(REG_CFP, rb_control_frame_t, ep)); + + // Bail when VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM) is non zero + test(cb, mem_opnd(64, REG0, SIZEOF_VALUE * VM_ENV_DATA_INDEX_FLAGS), imm_opnd(VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)); + jnz_ptr(cb, side_exit); + + // Load the block handler for the current frame + // note, VM_ASSERT(VM_ENV_LOCAL_P(ep)) + mov(cb, REG0, mem_opnd(64, REG0, SIZEOF_VALUE * VM_ENV_DATA_INDEX_SPECVAL)); + + // Block handler is a tagged pointer. Look at the tag. 0x03 is from VM_BH_ISEQ_BLOCK_P(). + and(cb, REG0_8, imm_opnd(0x3)); + + // Bail unless VM_BH_ISEQ_BLOCK_P(bh). This also checks for null. + cmp(cb, REG0_8, imm_opnd(0x1)); + jne_ptr(cb, side_exit); + + // Push rb_block_param_proxy. It's a root, so no need to use jit_mov_gc_ptr. + mov(cb, REG0, const_ptr_opnd((void *)rb_block_param_proxy)); + RUBY_ASSERT(!SPECIAL_CONST_P(rb_block_param_proxy)); + x86opnd_t top = ctx_stack_push(ctx, TYPE_HEAP); + mov(cb, top, REG0); + + return YJIT_KEEP_COMPILING; +} + + static void yjit_reg_op(int opcode, codegen_fn gen_fn) { @@ -2588,6 +2636,7 @@ yjit_init_codegen(void) https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L2636 yjit_reg_op(BIN(branchunless), gen_branchunless); yjit_reg_op(BIN(branchnil), gen_branchnil); yjit_reg_op(BIN(jump), gen_jump); + yjit_reg_op(BIN(getblockparamproxy), gen_getblockparamproxy); yjit_reg_op(BIN(opt_send_without_block), gen_opt_send_without_block); yjit_reg_op(BIN(send), gen_send); yjit_reg_op(BIN(leave), gen_leave); diff --git a/yjit_core.h b/yjit_core.h index e44b9fe30c..948ea3bd72 100644 --- a/yjit_core.h +++ b/yjit_core.h @@ -17,6 +17,8 @@ https://github.com/ruby/ruby/blob/trunk/yjit_core.h#L17 #define REG0_32 EAX #define REG1_32 ECX +#define REG0_8 AL + // Maximum number of temp value types we keep track of #define MAX_TEMP_TYPES 8 -- cgit v1.2.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/