summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstraptest/test_yjit.rb50
-rw-r--r--yjit_codegen.c49
-rw-r--r--yjit_core.h2
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{
# 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)
{
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)
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)
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 @@
#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