YJIT: Initial support for rest args (#7311)

* YJIT: Initial support for rest args

* Update yjit/src/codegen.rs

---------

Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
This commit is contained in:
Jimmy Miller 2023-02-16 11:25:48 -05:00 committed by GitHub
parent 21543ac86c
commit b4c38f3c59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
Notes: git 2023-02-16 16:26:09 +00:00
Merged-By: maximecb <maximecb@ruby-lang.org>
2 changed files with 73 additions and 8 deletions

View File

@ -5280,10 +5280,6 @@ fn gen_send_iseq(
// No support for callees with these parameters yet as they require allocation // No support for callees with these parameters yet as they require allocation
// or complex handling. // or complex handling.
if unsafe { get_iseq_flags_has_rest(iseq) } {
gen_counter_incr!(asm, send_iseq_has_rest);
return CantCompile;
}
if unsafe { get_iseq_flags_has_post(iseq) } { if unsafe { get_iseq_flags_has_post(iseq) } {
gen_counter_incr!(asm, send_iseq_has_post); gen_counter_incr!(asm, send_iseq_has_post);
return CantCompile; return CantCompile;
@ -5305,6 +5301,32 @@ fn gen_send_iseq(
return CantCompile; return CantCompile;
} }
let iseq_has_rest = unsafe { get_iseq_flags_has_rest(iseq) };
if iseq_has_rest && captured_opnd.is_some() {
gen_counter_incr!(asm, send_iseq_has_rest_and_captured);
return CantCompile;
}
if iseq_has_rest && flags & VM_CALL_ARGS_SPLAT != 0 {
gen_counter_incr!(asm, send_iseq_has_rest_and_splat);
return CantCompile;
}
if iseq_has_rest && flags & VM_CALL_OPT_SEND != 0 {
gen_counter_incr!(asm, send_iseq_has_rest_and_send);
return CantCompile;
}
if iseq_has_rest && unsafe { get_iseq_flags_has_block(iseq) } {
gen_counter_incr!(asm, send_iseq_has_rest_and_block);
return CantCompile;
}
if iseq_has_rest && unsafe { get_iseq_flags_has_kw(iseq) } {
gen_counter_incr!(asm, send_iseq_has_rest_and_kw);
return CantCompile;
}
// If we have keyword arguments being passed to a callee that only takes // If we have keyword arguments being passed to a callee that only takes
// positionals, then we need to allocate a hash. For now we're going to // positionals, then we need to allocate a hash. For now we're going to
// call that too complex and bail. // call that too complex and bail.
@ -5367,14 +5389,19 @@ fn gen_send_iseq(
return CantCompile; return CantCompile;
} }
if opts_filled < 0 && flags & VM_CALL_ARGS_SPLAT == 0 { if iseq_has_rest && opt_num != 0 {
gen_counter_incr!(asm, send_iseq_has_rest_and_optional);
return CantCompile;
}
if opts_filled < 0 && flags & VM_CALL_ARGS_SPLAT == 0 {
// Too few arguments and no splat to make up for it // Too few arguments and no splat to make up for it
gen_counter_incr!(asm, send_iseq_arity_error); gen_counter_incr!(asm, send_iseq_arity_error);
return CantCompile; return CantCompile;
} }
if opts_filled > opt_num { if opts_filled > opt_num && !iseq_has_rest {
// Too many arguments // Too many arguments and no place to put them (i.e. rest arg)
gen_counter_incr!(asm, send_iseq_arity_error); gen_counter_incr!(asm, send_iseq_arity_error);
return CantCompile; return CantCompile;
} }
@ -5799,6 +5826,39 @@ fn gen_send_iseq(
argc = lead_num; argc = lead_num;
} }
if iseq_has_rest {
assert!(argc >= required_num);
// We are going to allocate so setting pc and sp.
jit_save_pc(jit, asm);
gen_save_sp(jit, asm, ctx);
let n = (argc - required_num) as u32;
argc = required_num + 1;
// If n is 0, then elts is never going to be read, so we can just pass null
let values_ptr = if n == 0 {
Opnd::UImm(0)
} else {
asm.comment("load pointer to array elts");
let offset_magnitude = SIZEOF_VALUE as u32 * n;
let values_opnd = ctx.sp_opnd(-(offset_magnitude as isize));
asm.lea(values_opnd)
};
let new_ary = asm.ccall(
rb_ec_ary_new_from_values as *const u8,
vec![
EC,
Opnd::UImm(n.into()),
values_ptr
]
);
ctx.stack_pop(n.as_usize());
let stack_ret = ctx.stack_push(Type::CArray);
asm.mov(stack_ret, new_ary);
}
// Points to the receiver operand on the stack unless a captured environment is used // Points to the receiver operand on the stack unless a captured environment is used
let recv = match captured_opnd { let recv = match captured_opnd {
Some(captured_opnd) => asm.load(Opnd::mem(64, captured_opnd, 0)), // captured->self Some(captured_opnd) => asm.load(Opnd::mem(64, captured_opnd, 0)), // captured->self

View File

@ -196,7 +196,6 @@ make_counters! {
send_iseq_only_keywords, send_iseq_only_keywords,
send_iseq_kwargs_req_and_opt_missing, send_iseq_kwargs_req_and_opt_missing,
send_iseq_kwargs_mismatch, send_iseq_kwargs_mismatch,
send_iseq_has_rest,
send_iseq_has_post, send_iseq_has_post,
send_iseq_has_kwrest, send_iseq_has_kwrest,
send_iseq_has_no_kw, send_iseq_has_no_kw,
@ -237,6 +236,12 @@ make_counters! {
send_send_chain_not_string_or_sym, send_send_chain_not_string_or_sym,
send_send_getter, send_send_getter,
send_send_builtin, send_send_builtin,
send_iseq_has_rest_and_captured,
send_iseq_has_rest_and_splat,
send_iseq_has_rest_and_send,
send_iseq_has_rest_and_block,
send_iseq_has_rest_and_kw,
send_iseq_has_rest_and_optional,
send_is_a_class_mismatch, send_is_a_class_mismatch,
send_instance_of_class_mismatch, send_instance_of_class_mismatch,