mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
YJIT: Teach getblockparamproxy to handle the no-block case without exiting (#6191)
Teach getblockparamproxy to handle the no-block case without exiting Co-authored-by: John Hawthorn <john@hawthorn.email> Co-authored-by: John Hawthorn <john@hawthorn.email>
This commit is contained in:
parent
18b1e5e6db
commit
ab08a43ec5
Notes:
git
2022-07-29 00:38:40 +09:00
Merged-By: maximecb <maximecb@ruby-lang.org>
5 changed files with 113 additions and 18 deletions
|
@ -1224,6 +1224,19 @@ assert_equal '[1, 2, 42]', %q{
|
||||||
[foo {1}, foo {2}, foo {42}]
|
[foo {1}, foo {2}, foo {42}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# test calling without block param
|
||||||
|
assert_equal '[1, false, 2, false]', %q{
|
||||||
|
def bar
|
||||||
|
block_given? && yield
|
||||||
|
end
|
||||||
|
|
||||||
|
def foo(&block)
|
||||||
|
bar(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
[foo { 1 }, foo, foo { 2 }, foo]
|
||||||
|
}
|
||||||
|
|
||||||
# test calling block param failing
|
# test calling block param failing
|
||||||
assert_equal '42', %q{
|
assert_equal '42', %q{
|
||||||
def foo(&block)
|
def foo(&block)
|
||||||
|
|
|
@ -513,6 +513,22 @@ class TestYJIT < Test::Unit::TestCase
|
||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_getblockparamproxy_with_no_block
|
||||||
|
# Currently side exits on the send
|
||||||
|
assert_compiles(<<~'RUBY', insns: [:getblockparamproxy], exits: { send: 2 })
|
||||||
|
def bar
|
||||||
|
end
|
||||||
|
|
||||||
|
def foo &blk
|
||||||
|
bar(&blk)
|
||||||
|
bar(&blk)
|
||||||
|
end
|
||||||
|
|
||||||
|
foo
|
||||||
|
foo
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
def test_send_splat
|
def test_send_splat
|
||||||
assert_compiles(<<~'RUBY', result: "3#1,2,3/P", exits: {})
|
assert_compiles(<<~'RUBY', result: "3#1,2,3/P", exits: {})
|
||||||
def internal_method(*args)
|
def internal_method(*args)
|
||||||
|
|
11
yjit.c
11
yjit.c
|
@ -699,6 +699,17 @@ rb_get_cfp_ep(struct rb_control_frame_struct *cfp)
|
||||||
return (VALUE*)cfp->ep;
|
return (VALUE*)cfp->ep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VALUE *
|
||||||
|
rb_get_cfp_ep_level(struct rb_control_frame_struct *cfp, uint32_t lv)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
const VALUE *ep = (VALUE*)cfp->ep;
|
||||||
|
for (i = 0; i < lv; i++) {
|
||||||
|
ep = VM_ENV_PREV_EP(ep);
|
||||||
|
}
|
||||||
|
return ep;
|
||||||
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_yarv_class_of(VALUE obj)
|
rb_yarv_class_of(VALUE obj)
|
||||||
{
|
{
|
||||||
|
|
|
@ -214,6 +214,15 @@ fn jit_peek_at_local(jit: &JITState, n: i32) -> VALUE {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn jit_peek_at_block_handler(jit: &JITState, level: u32) -> VALUE {
|
||||||
|
assert!(jit_at_current_insn(jit));
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let ep = get_cfp_ep_level(get_ec_cfp(jit.ec.unwrap()), level);
|
||||||
|
*ep.offset(VM_ENV_DATA_INDEX_SPECVAL as isize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add a comment at the current position in the code block
|
// Add a comment at the current position in the code block
|
||||||
fn add_comment(cb: &mut CodeBlock, comment_str: &str) {
|
fn add_comment(cb: &mut CodeBlock, comment_str: &str) {
|
||||||
if cfg!(feature = "asm_comments") {
|
if cfg!(feature = "asm_comments") {
|
||||||
|
@ -5634,6 +5643,13 @@ fn gen_getblockparamproxy(
|
||||||
cb: &mut CodeBlock,
|
cb: &mut CodeBlock,
|
||||||
ocb: &mut OutlinedCb,
|
ocb: &mut OutlinedCb,
|
||||||
) -> CodegenStatus {
|
) -> CodegenStatus {
|
||||||
|
if !jit_at_current_insn(jit) {
|
||||||
|
defer_compilation(jit, ctx, cb, ocb);
|
||||||
|
return EndBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
let starting_context = *ctx; // make a copy for use with jit_chain_guard
|
||||||
|
|
||||||
// A mirror of the interpreter code. Checking for the case
|
// A mirror of the interpreter code. Checking for the case
|
||||||
// where it's pushing rb_block_param_proxy.
|
// where it's pushing rb_block_param_proxy.
|
||||||
let side_exit = get_side_exit(jit, ocb, ctx);
|
let side_exit = get_side_exit(jit, ocb, ctx);
|
||||||
|
@ -5641,6 +5657,15 @@ fn gen_getblockparamproxy(
|
||||||
// EP level
|
// EP level
|
||||||
let level = jit_get_arg(jit, 1).as_u32();
|
let level = jit_get_arg(jit, 1).as_u32();
|
||||||
|
|
||||||
|
// Peek at the block handler so we can check whether it's nil
|
||||||
|
let comptime_handler = jit_peek_at_block_handler(jit, level);
|
||||||
|
|
||||||
|
// When a block handler is present, it should always be a GC-guarded
|
||||||
|
// pointer (VM_BH_ISEQ_BLOCK_P)
|
||||||
|
if comptime_handler.as_u64() != 0 && comptime_handler.as_u64() & 0x3 != 0x1 {
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
|
|
||||||
// Load environment pointer EP from CFP
|
// Load environment pointer EP from CFP
|
||||||
gen_get_ep(cb, REG0, level);
|
gen_get_ep(cb, REG0, level);
|
||||||
|
|
||||||
|
@ -5669,27 +5694,54 @@ fn gen_getblockparamproxy(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Block handler is a tagged pointer. Look at the tag. 0x03 is from VM_BH_ISEQ_BLOCK_P().
|
// Specialize compilation for the case where no block handler is present
|
||||||
and(cb, REG0_8, imm_opnd(0x3));
|
if comptime_handler.as_u64() == 0 {
|
||||||
|
// Bail if there is a block handler
|
||||||
|
cmp(cb, REG0, uimm_opnd(0));
|
||||||
|
|
||||||
// Bail unless VM_BH_ISEQ_BLOCK_P(bh). This also checks for null.
|
jit_chain_guard(
|
||||||
cmp(cb, REG0_8, imm_opnd(0x1));
|
JCC_JNZ,
|
||||||
jnz_ptr(
|
jit,
|
||||||
cb,
|
&starting_context,
|
||||||
counted_exit!(ocb, side_exit, gbpp_block_handler_not_iseq),
|
cb,
|
||||||
);
|
ocb,
|
||||||
|
SEND_MAX_DEPTH,
|
||||||
|
side_exit,
|
||||||
|
);
|
||||||
|
|
||||||
// Push rb_block_param_proxy. It's a root, so no need to use jit_mov_gc_ptr.
|
jit_putobject(jit, ctx, cb, Qnil);
|
||||||
mov(
|
} else {
|
||||||
cb,
|
// Block handler is a tagged pointer. Look at the tag. 0x03 is from VM_BH_ISEQ_BLOCK_P().
|
||||||
REG0,
|
and(cb, REG0_8, imm_opnd(0x3));
|
||||||
const_ptr_opnd(unsafe { rb_block_param_proxy }.as_ptr()),
|
|
||||||
);
|
|
||||||
assert!(!unsafe { rb_block_param_proxy }.special_const_p());
|
|
||||||
let top = ctx.stack_push(Type::UnknownHeap);
|
|
||||||
mov(cb, top, REG0);
|
|
||||||
|
|
||||||
KeepCompiling
|
// Bail unless VM_BH_ISEQ_BLOCK_P(bh). This also checks for null.
|
||||||
|
cmp(cb, REG0_8, imm_opnd(0x1));
|
||||||
|
|
||||||
|
jit_chain_guard(
|
||||||
|
JCC_JNZ,
|
||||||
|
jit,
|
||||||
|
&starting_context,
|
||||||
|
cb,
|
||||||
|
ocb,
|
||||||
|
SEND_MAX_DEPTH,
|
||||||
|
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(unsafe { rb_block_param_proxy }.as_ptr()),
|
||||||
|
);
|
||||||
|
assert!(!unsafe { rb_block_param_proxy }.special_const_p());
|
||||||
|
|
||||||
|
let top = ctx.stack_push(Type::Unknown);
|
||||||
|
mov(cb, top, REG0);
|
||||||
|
}
|
||||||
|
|
||||||
|
jump_to_next_insn(jit, ctx, cb, ocb);
|
||||||
|
|
||||||
|
EndBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_getblockparam(
|
fn gen_getblockparam(
|
||||||
|
|
|
@ -135,6 +135,9 @@ extern "C" {
|
||||||
#[link_name = "rb_get_cfp_ep"]
|
#[link_name = "rb_get_cfp_ep"]
|
||||||
pub fn get_cfp_ep(cfp: CfpPtr) -> *mut VALUE;
|
pub fn get_cfp_ep(cfp: CfpPtr) -> *mut VALUE;
|
||||||
|
|
||||||
|
#[link_name = "rb_get_cfp_ep_level"]
|
||||||
|
pub fn get_cfp_ep_level(cfp: CfpPtr, lv: u32) -> *const VALUE;
|
||||||
|
|
||||||
#[link_name = "rb_get_cme_def_type"]
|
#[link_name = "rb_get_cme_def_type"]
|
||||||
pub fn get_cme_def_type(cme: *const rb_callable_method_entry_t) -> rb_method_type_t;
|
pub fn get_cme_def_type(cme: *const rb_callable_method_entry_t) -> rb_method_type_t;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue