YJIT: getblockparamproxy for when block is a Proc

This commit is contained in:
Alan Wu 2023-07-26 18:12:46 -04:00
Родитель 34825ed20d
Коммит 7633299c50
4 изменённых файлов: 54 добавлений и 10 удалений

Просмотреть файл

@ -441,6 +441,7 @@ fn main() {
.allowlist_function("rb_method_basic_definition_p") .allowlist_function("rb_method_basic_definition_p")
.allowlist_function("rb_yjit_array_len") .allowlist_function("rb_yjit_array_len")
.allowlist_function("rb_obj_class") .allowlist_function("rb_obj_class")
.allowlist_function("rb_obj_is_proc")
// We define VALUE manually, don't import it // We define VALUE manually, don't import it
.blocklist_type("VALUE") .blocklist_type("VALUE")

Просмотреть файл

@ -7784,19 +7784,19 @@ fn gen_getblockparamproxy(
return Some(EndBlock); return Some(EndBlock);
} }
// A mirror of the interpreter code. Checking for the case
// where it's pushing rb_block_param_proxy.
// EP level // EP level
let level = jit.get_arg(1).as_u32(); let level = jit.get_arg(1).as_u32();
// Peek at the block handler so we can check whether it's nil // Peek at the block handler so we can check whether it's nil
let comptime_handler = jit.peek_at_block_handler(level); let comptime_handler = jit.peek_at_block_handler(level);
// When a block handler is present, it should always be a GC-guarded // Filter for the 3 cases we currently handle
// pointer (VM_BH_ISEQ_BLOCK_P) if !(comptime_handler.as_u64() == 0 || // no block given
if comptime_handler.as_u64() != 0 && comptime_handler.as_u64() & 0x3 != 0x1 { comptime_handler.as_u64() & 0x3 == 0x1 || // iseq block (no associated GC managed object)
incr_counter!(gbpp_not_gc_guarded); unsafe { rb_obj_is_proc(comptime_handler) }.test() // block is a Proc
) {
// Missing the symbol case, where we basically need to call Symbol#to_proc at runtime
gen_counter_incr(asm, Counter::gbpp_unsupported_type);
return None; return None;
} }
@ -7818,7 +7818,12 @@ fn gen_getblockparamproxy(
Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL) Opnd::mem(64, ep_opnd, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL)
); );
// Specialize compilation for the case where no block handler is present // Use block handler sample to guide specialization...
// NOTE: we use jit_chain_guard() in this decision tree, and since
// there are only 3 cases, it should never reach the depth limit use
// the exit counter we pass to it.
//
// No block given
if comptime_handler.as_u64() == 0 { if comptime_handler.as_u64() == 0 {
// Bail if there is a block handler // Bail if there is a block handler
asm.cmp(block_handler, Opnd::UImm(0)); asm.cmp(block_handler, Opnd::UImm(0));
@ -7833,7 +7838,7 @@ fn gen_getblockparamproxy(
); );
jit_putobject(asm, Qnil); jit_putobject(asm, Qnil);
} else { } else if comptime_handler.as_u64() & 0x3 == 0x1 {
// Block handler is a tagged pointer. Look at the tag. 0x03 is from VM_BH_ISEQ_BLOCK_P(). // Block handler is a tagged pointer. Look at the tag. 0x03 is from VM_BH_ISEQ_BLOCK_P().
let block_handler = asm.and(block_handler, 0x3.into()); let block_handler = asm.and(block_handler, 0x3.into());
@ -7854,6 +7859,39 @@ fn gen_getblockparamproxy(
let top = asm.stack_push(Type::BlockParamProxy); let top = asm.stack_push(Type::BlockParamProxy);
asm.mov(top, Opnd::const_ptr(unsafe { rb_block_param_proxy }.as_ptr())); asm.mov(top, Opnd::const_ptr(unsafe { rb_block_param_proxy }.as_ptr()));
} else if unsafe { rb_obj_is_proc(comptime_handler) }.test() {
// The block parameter is a Proc
c_callable! {
// We can't hold values across C calls due to a backend limitation,
// so we'll use this thin wrapper around rb_obj_is_proc().
fn is_proc(object: VALUE) -> VALUE {
if unsafe { rb_obj_is_proc(object) }.test() {
// VM_BH_TO_PROC() is the identify function.
object
} else {
Qfalse
}
}
}
// Simple predicate, no need to jit_prepare_routine_call()
let proc_or_false = asm.ccall(is_proc as _, vec![block_handler]);
// Guard for proc
asm.cmp(proc_or_false, Qfalse.into());
jit_chain_guard(
JCC_JE,
jit,
asm,
ocb,
SEND_MAX_DEPTH,
Counter::gbpp_block_handler_not_proc,
);
let top = asm.stack_push(Type::Unknown);
asm.mov(top, proc_or_false);
} else {
unreachable!("absurd given initial filtering");
} }
jump_to_next_insn(jit, asm, ocb); jump_to_next_insn(jit, asm, ocb);

Просмотреть файл

@ -1099,6 +1099,7 @@ extern "C" {
pub fn rb_hash_aref(hash: VALUE, key: VALUE) -> VALUE; pub fn rb_hash_aref(hash: VALUE, key: VALUE) -> VALUE;
pub fn rb_hash_aset(hash: VALUE, key: VALUE, val: VALUE) -> VALUE; pub fn rb_hash_aset(hash: VALUE, key: VALUE, val: VALUE) -> VALUE;
pub fn rb_hash_bulk_insert(argc: ::std::os::raw::c_long, argv: *const VALUE, hash: VALUE); pub fn rb_hash_bulk_insert(argc: ::std::os::raw::c_long, argv: *const VALUE, hash: VALUE);
pub fn rb_obj_is_proc(recv: VALUE) -> VALUE;
pub fn rb_sym2id(obj: VALUE) -> ID; pub fn rb_sym2id(obj: VALUE) -> ID;
pub fn rb_id2sym(id: ID) -> VALUE; pub fn rb_id2sym(id: ID) -> VALUE;
pub fn rb_intern(name: *const ::std::os::raw::c_char) -> ID; pub fn rb_intern(name: *const ::std::os::raw::c_char) -> ID;

Просмотреть файл

@ -355,11 +355,15 @@ make_counters! {
expandarray_not_array, expandarray_not_array,
expandarray_rhs_too_small, expandarray_rhs_too_small,
// getblockparam
gbp_wb_required, gbp_wb_required,
gbpp_not_gc_guarded,
// getblockparamproxy
gbpp_unsupported_type,
gbpp_block_param_modified, gbpp_block_param_modified,
gbpp_block_handler_not_none, gbpp_block_handler_not_none,
gbpp_block_handler_not_iseq, gbpp_block_handler_not_iseq,
gbpp_block_handler_not_proc,
branchif_interrupted, branchif_interrupted,
branchunless_interrupted, branchunless_interrupted,