Skip to content

Commit a8c6ba2

Browse files
YJIT: Rest and keyword (non-supplying) (ruby#7608)
* YJIT: Rest and keyword (non-supplying) * Update yjit/src/codegen.rs --------- Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
1 parent b168141 commit a8c6ba2

3 files changed

Lines changed: 104 additions & 89 deletions

File tree

bootstraptest/test_yjit.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,3 +3800,18 @@ def post(*args, &block)
38003800
38013801
post "/a" => "b", as: :c
38023802
}
3803+
3804+
# Test rest and kw_args
3805+
assert_equal '[[["test"], nil, true], [["test"], :base, true]]', %q{
3806+
def my_func(*args, base: nil, sort: true)
3807+
[args, base, sort]
3808+
end
3809+
3810+
def calling_my_func
3811+
result = []
3812+
result << my_func("test")
3813+
result << my_func("test", base: :base)
3814+
end
3815+
3816+
calling_my_func
3817+
}

yjit/src/codegen.rs

Lines changed: 88 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -5468,8 +5468,8 @@ fn gen_send_iseq(
54685468
return CantCompile;
54695469
}
54705470

5471-
if iseq_has_rest && unsafe { get_iseq_flags_has_kw(iseq) } {
5472-
gen_counter_incr!(asm, send_iseq_has_rest_and_kw);
5471+
if iseq_has_rest && unsafe { get_iseq_flags_has_kw(iseq) } && supplying_kws {
5472+
gen_counter_incr!(asm, send_iseq_has_rest_and_kw_supplying);
54735473
return CantCompile;
54745474
}
54755475

@@ -5800,6 +5800,92 @@ fn gen_send_iseq(
58005800
handle_opt_send_shift_stack(asm, argc, ctx);
58015801
}
58025802

5803+
if iseq_has_rest {
5804+
// We are going to allocate so setting pc and sp.
5805+
jit_save_pc(jit, asm);
5806+
gen_save_sp(asm, ctx);
5807+
5808+
if flags & VM_CALL_ARGS_SPLAT != 0 {
5809+
let non_rest_arg_count = argc - 1;
5810+
// We start by dupping the array because someone else might have
5811+
// a reference to it.
5812+
let array = ctx.stack_pop(1);
5813+
let array = asm.ccall(
5814+
rb_ary_dup as *const u8,
5815+
vec![array],
5816+
);
5817+
if non_rest_arg_count > required_num {
5818+
// If we have more arguments than required, we need to prepend
5819+
// the items from the stack onto the array.
5820+
let diff = (non_rest_arg_count - required_num) as u32;
5821+
5822+
// diff is >0 so no need to worry about null pointer
5823+
asm.comment("load pointer to array elements");
5824+
let offset_magnitude = SIZEOF_VALUE as u32 * diff;
5825+
let values_opnd = ctx.sp_opnd(-(offset_magnitude as isize));
5826+
let values_ptr = asm.lea(values_opnd);
5827+
5828+
asm.comment("prepend stack values to rest array");
5829+
let array = asm.ccall(
5830+
rb_yjit_rb_ary_unshift_m as *const u8,
5831+
vec![Opnd::UImm(diff as u64), values_ptr, array],
5832+
);
5833+
ctx.stack_pop(diff as usize);
5834+
5835+
let stack_ret = ctx.stack_push(Type::TArray);
5836+
asm.mov(stack_ret, array);
5837+
// We now should have the required arguments
5838+
// and an array of all the rest arguments
5839+
argc = required_num + 1;
5840+
} else if non_rest_arg_count < required_num {
5841+
// If we have fewer arguments than required, we need to take some
5842+
// from the array and move them to the stack.
5843+
let diff = (required_num - non_rest_arg_count) as u32;
5844+
// This moves the arguments onto the stack. But it doesn't modify the array.
5845+
move_rest_args_to_stack(array, diff, ctx, asm, ocb, side_exit);
5846+
5847+
// We will now slice the array to give us a new array of the correct size
5848+
let ret = asm.ccall(rb_yjit_rb_ary_subseq_length as *const u8, vec![array, Opnd::UImm(diff as u64)]);
5849+
let stack_ret = ctx.stack_push(Type::TArray);
5850+
asm.mov(stack_ret, ret);
5851+
5852+
// We now should have the required arguments
5853+
// and an array of all the rest arguments
5854+
argc = required_num + 1;
5855+
} else {
5856+
// The arguments are equal so we can just push to the stack
5857+
assert!(non_rest_arg_count == required_num);
5858+
let stack_ret = ctx.stack_push(Type::TArray);
5859+
asm.mov(stack_ret, array);
5860+
}
5861+
} else {
5862+
assert!(argc >= required_num);
5863+
let n = (argc - required_num) as u32;
5864+
argc = required_num + 1;
5865+
// If n is 0, then elts is never going to be read, so we can just pass null
5866+
let values_ptr = if n == 0 {
5867+
Opnd::UImm(0)
5868+
} else {
5869+
asm.comment("load pointer to array elements");
5870+
let offset_magnitude = SIZEOF_VALUE as u32 * n;
5871+
let values_opnd = ctx.sp_opnd(-(offset_magnitude as isize));
5872+
asm.lea(values_opnd)
5873+
};
5874+
5875+
let new_ary = asm.ccall(
5876+
rb_ec_ary_new_from_values as *const u8,
5877+
vec![
5878+
EC,
5879+
Opnd::UImm(n.into()),
5880+
values_ptr
5881+
]
5882+
);
5883+
ctx.stack_pop(n.as_usize());
5884+
let stack_ret = ctx.stack_push(Type::CArray);
5885+
asm.mov(stack_ret, new_ary);
5886+
}
5887+
}
5888+
58035889
if doing_kw_call {
58045890
// Here we're calling a method with keyword arguments and specifying
58055891
// keyword arguments at this call site.
@@ -5966,93 +6052,7 @@ fn gen_send_iseq(
59666052
argc = lead_num;
59676053
}
59686054

5969-
if iseq_has_rest {
5970-
5971-
// We are going to allocate so setting pc and sp.
5972-
jit_save_pc(jit, asm);
5973-
gen_save_sp(asm, ctx);
5974-
5975-
if flags & VM_CALL_ARGS_SPLAT != 0 {
5976-
let non_rest_arg_count = argc - 1;
5977-
// We start by dupping the array because someone else might have
5978-
// a reference to it.
5979-
let array = ctx.stack_pop(1);
5980-
let array = asm.ccall(
5981-
rb_ary_dup as *const u8,
5982-
vec![array],
5983-
);
5984-
if non_rest_arg_count > required_num {
5985-
// If we have more arguments than required, we need to prepend
5986-
// the items from the stack onto the array.
5987-
let diff = (non_rest_arg_count - required_num) as u32;
5988-
5989-
// diff is >0 so no need to worry about null pointer
5990-
asm.comment("load pointer to array elements");
5991-
let offset_magnitude = SIZEOF_VALUE as u32 * diff;
5992-
let values_opnd = ctx.sp_opnd(-(offset_magnitude as isize));
5993-
let values_ptr = asm.lea(values_opnd);
5994-
5995-
asm.comment("prepend stack values to rest array");
5996-
let array = asm.ccall(
5997-
rb_yjit_rb_ary_unshift_m as *const u8,
5998-
vec![Opnd::UImm(diff as u64), values_ptr, array],
5999-
);
6000-
ctx.stack_pop(diff as usize);
6001-
6002-
let stack_ret = ctx.stack_push(Type::TArray);
6003-
asm.mov(stack_ret, array);
6004-
// We now should have the required arguments
6005-
// and an array of all the rest arguments
6006-
argc = required_num + 1;
6007-
} else if non_rest_arg_count < required_num {
6008-
// If we have fewer arguments than required, we need to take some
6009-
// from the array and move them to the stack.
6010-
let diff = (required_num - non_rest_arg_count) as u32;
6011-
// This moves the arguments onto the stack. But it doesn't modify the array.
6012-
move_rest_args_to_stack(array, diff, ctx, asm, ocb, side_exit);
6013-
6014-
// We will now slice the array to give us a new array of the correct size
6015-
let ret = asm.ccall(rb_yjit_rb_ary_subseq_length as *const u8, vec![array, Opnd::UImm(diff as u64)]);
6016-
let stack_ret = ctx.stack_push(Type::TArray);
6017-
asm.mov(stack_ret, ret);
60186055

6019-
// We now should have the required arguments
6020-
// and an array of all the rest arguments
6021-
argc = required_num + 1;
6022-
} else {
6023-
// The arguments are equal so we can just push to the stack
6024-
assert!(non_rest_arg_count == required_num);
6025-
let stack_ret = ctx.stack_push(Type::TArray);
6026-
asm.mov(stack_ret, array);
6027-
}
6028-
} else {
6029-
assert!(argc >= required_num);
6030-
let n = (argc - required_num) as u32;
6031-
argc = required_num + 1;
6032-
// If n is 0, then elts is never going to be read, so we can just pass null
6033-
let values_ptr = if n == 0 {
6034-
Opnd::UImm(0)
6035-
} else {
6036-
asm.comment("load pointer to array elements");
6037-
let offset_magnitude = SIZEOF_VALUE as u32 * n;
6038-
let values_opnd = ctx.sp_opnd(-(offset_magnitude as isize));
6039-
asm.lea(values_opnd)
6040-
};
6041-
6042-
let new_ary = asm.ccall(
6043-
rb_ec_ary_new_from_values as *const u8,
6044-
vec![
6045-
EC,
6046-
Opnd::UImm(n.into()),
6047-
values_ptr
6048-
]
6049-
);
6050-
6051-
ctx.stack_pop(n.as_usize());
6052-
let stack_ret = ctx.stack_push(Type::CArray);
6053-
asm.mov(stack_ret, new_ary);
6054-
}
6055-
}
60566056

60576057
// Points to the receiver operand on the stack unless a captured environment is used
60586058
let recv = match captured_opnd {

yjit/src/stats.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ make_counters! {
254254
send_send_builtin,
255255
send_iseq_has_rest_and_captured,
256256
send_iseq_has_rest_and_send,
257-
send_iseq_has_rest_and_kw,
257+
send_iseq_has_rest_and_kw_supplying,
258258
send_iseq_has_rest_and_optional,
259259
send_iseq_has_rest_and_splat_not_equal,
260260
send_is_a_class_mismatch,

0 commit comments

Comments
 (0)