Skip to content

Commit c63042e

Browse files
author
Noah Gibbs
authored
Update gen_send_iseq for CRuby PRs ruby#5285 and ruby#5379. (ruby#220)
1 parent 566eeeb commit c63042e

File tree

3 files changed

+140
-98
lines changed

3 files changed

+140
-98
lines changed

yjit.c

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,11 @@ rb_get_def_iseq_ptr(rb_method_definition_t *def)
393393
return def_iseq_ptr(def);
394394
}
395395

396+
rb_iseq_t *
397+
rb_get_iseq_body_local_iseq(rb_iseq_t * iseq) {
398+
return iseq->body->local_iseq;
399+
}
400+
396401
unsigned int
397402
rb_get_iseq_body_local_table_size(rb_iseq_t* iseq) {
398403
return iseq->body->local_table_size;
@@ -413,11 +418,41 @@ rb_get_iseq_body_stack_max(rb_iseq_t* iseq) {
413418
return iseq->body->stack_max;
414419
}
415420

416-
int
421+
bool
417422
rb_get_iseq_flags_has_opt(rb_iseq_t* iseq) {
418423
return iseq->body->param.flags.has_opt;
419424
}
420425

426+
bool
427+
rb_get_iseq_flags_has_kw(rb_iseq_t* iseq) {
428+
return iseq->body->param.flags.has_kw;
429+
}
430+
431+
bool
432+
rb_get_iseq_flags_has_post(rb_iseq_t* iseq) {
433+
return iseq->body->param.flags.has_post;
434+
}
435+
436+
bool
437+
rb_get_iseq_flags_has_kwrest(rb_iseq_t* iseq) {
438+
return iseq->body->param.flags.has_kwrest;
439+
}
440+
441+
bool
442+
rb_get_iseq_flags_has_rest(rb_iseq_t *iseq) {
443+
return iseq->body->param.flags.has_rest;
444+
}
445+
446+
bool
447+
rb_get_iseq_flags_has_block(rb_iseq_t* iseq) {
448+
return iseq->body->param.flags.has_block;
449+
}
450+
451+
bool
452+
rb_get_iseq_flags_has_accepts_no_kwarg(rb_iseq_t* iseq) {
453+
return iseq->body->param.flags.accepts_no_kwarg;
454+
}
455+
421456
const rb_seq_param_keyword_struct*
422457
rb_get_iseq_body_param_keyword(rb_iseq_t* iseq) {
423458
return iseq->body->param.keyword;
@@ -443,22 +478,6 @@ rb_get_iseq_body_param_opt_table(rb_iseq_t* iseq) {
443478
return iseq->body->param.opt_table;
444479
}
445480

446-
// Returns whether the iseq only needs positional (lead) argument setup.
447-
bool
448-
rb_iseq_needs_lead_args_only(const rb_iseq_t *iseq)
449-
{
450-
// When iseq->body->local_iseq == iseq, setup_parameters_complex()
451-
// doesn't do anything to setup the block parameter.
452-
bool takes_block = iseq->body->param.flags.has_block;
453-
return (!takes_block || iseq->body->local_iseq == iseq) &&
454-
iseq->body->param.flags.has_opt == false &&
455-
iseq->body->param.flags.has_rest == false &&
456-
iseq->body->param.flags.has_post == false &&
457-
iseq->body->param.flags.has_kw == false &&
458-
iseq->body->param.flags.has_kwrest == false &&
459-
iseq->body->param.flags.accepts_no_kwarg == false;
460-
}
461-
462481
// If true, the iseq is leaf and it can be replaced by a single C call.
463482
bool
464483
rb_leaf_invokebuiltin_iseq_p(const rb_iseq_t *iseq)

yjit/src/codegen.rs

Lines changed: 82 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ pub fn gen_entry_prologue(cb: &mut CodeBlock, iseq: IseqPtr) -> Option<CodePtr>
676676
// has optional parameters, we'll add a runtime check that the PC we've
677677
// compiled for is the same PC that the interpreter wants us to run with.
678678
// If they don't match, then we'll take a side exit.
679-
if unsafe { get_iseq_flags_has_opt(iseq) } != 0 {
679+
if unsafe { get_iseq_flags_has_opt(iseq) } {
680680
gen_pc_guard(cb, iseq);
681681
}
682682

@@ -3696,80 +3696,100 @@ fn gen_send_iseq(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb:
36963696
// specified at the call site. We need to keep track of the fact that this
36973697
// value is present on the stack in order to properly set up the callee's
36983698
// stack pointer.
3699-
let mut doing_kw_call = false;
3699+
let doing_kw_call = unsafe { get_iseq_flags_has_kw(iseq) };
3700+
let supplying_kws = unsafe { vm_ci_flag(ci) & VM_CALL_KWARG } != 0;
37003701

37013702
if unsafe{ vm_ci_flag(ci) } & VM_CALL_TAILCALL != 0 {
37023703
// We can't handle tailcalls
37033704
gen_counter_incr!(cb, send_iseq_tailcall);
37043705
return CantCompile;
37053706
}
37063707

3707-
// Arity handling and optional parameter setup
3708-
let mut num_params = unsafe { get_iseq_body_param_size(iseq) as i32 };
3709-
let mut start_pc_offset:u32 = 0;
3710-
3711-
if unsafe { iseq_needs_lead_args_only(iseq) } {
3712-
// If we have keyword arguments being passed to a callee that only takes
3713-
// positionals, then we need to allocate a hash. For now we're going to
3714-
// call that too complex and bail.
3715-
if unsafe { vm_ci_flag(ci) } & VM_CALL_KWARG != 0 {
3708+
// No support for callees with these parameters yet as they require allocation
3709+
// or complex handling.
3710+
if unsafe { get_iseq_flags_has_rest(iseq) ||
3711+
get_iseq_flags_has_post(iseq) ||
3712+
get_iseq_flags_has_kwrest(iseq) } {
37163713
gen_counter_incr!(cb, send_iseq_complex_callee);
37173714
return CantCompile;
3718-
}
3715+
}
37193716

3720-
num_params = unsafe { get_iseq_body_param_lead_num(iseq) };
3717+
// If we have keyword arguments being passed to a callee that only takes
3718+
// positionals, then we need to allocate a hash. For now we're going to
3719+
// call that too complex and bail.
3720+
if supplying_kws && ! unsafe { get_iseq_flags_has_kw(iseq) } {
3721+
gen_counter_incr!(cb, send_iseq_complex_callee);
3722+
return CantCompile;
3723+
}
37213724

3722-
if num_params != argc {
3723-
gen_counter_incr!(cb, send_iseq_arity_error);
3724-
return CantCompile;
3725-
}
3725+
// If we have a method accepting no kwargs (**nil), exit if we have passed
3726+
// it any kwargs.
3727+
if supplying_kws && unsafe { get_iseq_flags_has_accepts_no_kwarg(iseq) } {
3728+
gen_counter_incr!(cb, send_iseq_complex_callee);
3729+
return CantCompile;
37263730
}
3727-
else if unsafe { rb_iseq_only_optparam_p(iseq) } {
3728-
// If we have keyword arguments being passed to a callee that only takes
3729-
// positionals and optionals, then we need to allocate a hash. For now
3730-
// we're going to call that too complex and bail.
3731-
if unsafe { vm_ci_flag(ci) } & VM_CALL_KWARG != 0 {
3731+
3732+
// For computing number of locals to set up for the callee
3733+
let mut num_params = unsafe { get_iseq_body_param_size(iseq) };
3734+
3735+
// Block parameter handling. This mirrors setup_parameters_complex().
3736+
if unsafe { get_iseq_flags_has_block(iseq) } {
3737+
if unsafe { get_iseq_body_local_iseq(iseq) == iseq } {
3738+
num_params -= 1;
3739+
} else {
3740+
// In this case (param.flags.has_block && local_iseq != iseq),
3741+
// the block argument is setup as a local variable and requires
3742+
// materialization (allocation). Bail.
37323743
gen_counter_incr!(cb, send_iseq_complex_callee);
37333744
return CantCompile;
37343745
}
3746+
}
37353747

3736-
// These are iseqs with 0 or more required parameters followed by 1
3737-
// or more optional parameters.
3738-
// We follow the logic of vm_call_iseq_setup_normal_opt_start()
3739-
// and these are the preconditions required for using that fast path.
3740-
//RUBY_ASSERT(vm_ci_markable(ci) && ((vm_ci_flag(ci) &
3741-
// (VM_CALL_KW_SPLAT | VM_CALL_KWARG | VM_CALL_ARGS_SPLAT)) == 0));
3748+
let mut start_pc_offset = 0;
3749+
let required_num = unsafe { get_iseq_body_param_lead_num(iseq) };
37423750

3743-
let required_num = unsafe { get_iseq_body_param_lead_num(iseq) };
3744-
let opts_filled = argc - required_num;
3745-
let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) };
3751+
// This struct represents the metadata about the caller-specified
3752+
// keyword arguments.
3753+
let kw_arg = unsafe { vm_ci_kwarg(ci) };
3754+
let kw_arg_num = if kw_arg.is_null() { 0 } else { unsafe { get_cikw_keyword_len(kw_arg) } };
37463755

3747-
if opts_filled < 0 || opts_filled > opt_num {
3748-
gen_counter_incr!(cb, send_iseq_arity_error);
3749-
return CantCompile;
3750-
}
3756+
// Arity handling and optional parameter setup
3757+
let opts_filled = argc - required_num - kw_arg_num;
3758+
let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) };
3759+
let opts_missing:u32 = (opt_num - opts_filled).try_into().unwrap();
3760+
3761+
if opts_filled < 0 || opts_filled > opt_num {
3762+
gen_counter_incr!(cb, send_iseq_arity_error);
3763+
return CantCompile;
3764+
}
3765+
3766+
// If we have unfilled optional arguments and keyword arguments then we
3767+
// would need to move adjust the arguments location to account for that.
3768+
// For now we aren't handling this case.
3769+
if doing_kw_call && opts_missing > 0 {
3770+
gen_counter_incr!(cb, send_iseq_complex_callee);
3771+
return CantCompile;
3772+
}
37513773

3752-
num_params -= opt_num - opts_filled;
3774+
if opt_num > 0 {
3775+
num_params -= opts_missing;
37533776
unsafe {
37543777
let opt_table = get_iseq_body_param_opt_table(iseq);
37553778
start_pc_offset = (*opt_table.offset(opts_filled as isize)).as_u32();
37563779
}
37573780
}
37583781

3759-
else if unsafe { rb_iseq_only_kwparam_p(iseq) } {
3760-
let lead_num = unsafe { get_iseq_body_param_lead_num(iseq) };
3761-
3762-
doing_kw_call = true;
3763-
3782+
if doing_kw_call {
37643783
// Here we're calling a method with keyword arguments and specifying
37653784
// keyword arguments at this call site.
37663785

3767-
// This struct represents the metadata about the caller-specified
3768-
// keyword arguments.
3769-
let kw_arg = unsafe { vm_ci_kwarg(ci) };
3770-
3786+
// This struct represents the metadata about the callee-specified
3787+
// keyword parameters.
37713788
let keyword = unsafe { get_iseq_body_param_keyword(iseq) };
37723789
let keyword_num = unsafe { (*keyword).num };
3790+
let keyword_required_num = unsafe { (*keyword).required_num };
3791+
3792+
let mut required_kwargs_filled = 0;
37733793

37743794
if keyword_num > 30 {
37753795
// We have so many keywords that (1 << num) encoded as a FIXNUM
@@ -3779,13 +3799,8 @@ fn gen_send_iseq(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb:
37793799
return CantCompile;
37803800
}
37813801

3782-
if unsafe { vm_ci_flag(ci) } & VM_CALL_KWARG != 0 {
3783-
// Check that the size of non-keyword arguments matches
3784-
if lead_num != argc - unsafe { get_cikw_keyword_len(kw_arg) } {
3785-
gen_counter_incr!(cb, send_iseq_complex_callee);
3786-
return CantCompile;
3787-
}
3788-
3802+
// Check that the kwargs being passed are valid
3803+
if supplying_kws {
37893804
// This is the list of keyword arguments that the callee specified
37903805
// in its initial declaration.
37913806
let callee_kwargs = unsafe { (*keyword).table };
@@ -3822,35 +3837,22 @@ fn gen_send_iseq(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb:
38223837
gen_counter_incr!(cb, send_iseq_kwargs_mismatch);
38233838
return CantCompile;
38243839
}
3825-
}
3826-
}
3827-
else if argc == lead_num {
3828-
// Here we are calling a method that accepts keyword arguments
3829-
// (optional or required) but we're not passing any keyword
3830-
// arguments at this call site
3831-
3832-
if unsafe { (*keyword).required_num } != 0 {
3833-
// If any of the keywords are required this is a mismatch
3834-
gen_counter_incr!(cb, send_iseq_kwargs_mismatch);
3835-
return CantCompile;
3836-
}
38373840

3838-
doing_kw_call = true;
3841+
// Keep a count to ensure all required kwargs are specified
3842+
if callee_idx < keyword_required_num {
3843+
required_kwargs_filled += 1;
3844+
}
3845+
}
38393846
}
3840-
else {
3841-
gen_counter_incr!(cb, send_iseq_complex_callee);
3847+
assert!(required_kwargs_filled <= keyword_required_num);
3848+
if required_kwargs_filled != keyword_required_num {
3849+
gen_counter_incr!(cb, send_iseq_kwargs_mismatch);
38423850
return CantCompile;
38433851
}
38443852
}
3845-
else {
3846-
// Only handle iseqs that have simple parameter setup.
3847-
// See vm_callee_setup_arg().
3848-
gen_counter_incr!(cb, send_iseq_complex_callee);
3849-
return CantCompile;
3850-
}
38513853

38523854
// Number of locals that are not parameters
3853-
let num_locals = unsafe { get_iseq_body_local_table_size(iseq) as i32 } - num_params;
3855+
let num_locals = unsafe { get_iseq_body_local_table_size(iseq) as i32 } - (num_params as i32);
38543856

38553857
// Create a side-exit to fall back to the interpreter
38563858
let side_exit = get_side_exit(jit, ocb, ctx);
@@ -3906,7 +3908,10 @@ fn gen_send_iseq(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb:
39063908
if doing_kw_call {
39073909
// Here we're calling a method with keyword arguments and specifying
39083910
// keyword arguments at this call site.
3909-
let lead_num = unsafe { get_iseq_body_param_lead_num(iseq) };
3911+
3912+
// Number of positional arguments the callee expects before the first
3913+
// keyword argument
3914+
let args_before_kw = required_num + opt_num;
39103915

39113916
// This struct represents the metadata about the caller-specified
39123917
// keyword arguments.
@@ -4013,8 +4018,8 @@ fn gen_send_iseq(jit: &mut JITState, ctx: &mut Context, cb: &mut CodeBlock, ocb:
40134018
// to perform the actual swapping at runtime.
40144019
let swap_idx_i32:i32 = swap_idx.try_into().unwrap();
40154020
let kwarg_idx_i32:i32 = kwarg_idx.try_into().unwrap();
4016-
let offset0:u16 = (argc - 1 - swap_idx_i32 - lead_num).try_into().unwrap();
4017-
let offset1:u16 = (argc - 1 - kwarg_idx_i32 - lead_num).try_into().unwrap();
4021+
let offset0:u16 = (argc - 1 - swap_idx_i32 - args_before_kw).try_into().unwrap();
4022+
let offset1:u16 = (argc - 1 - kwarg_idx_i32 - args_before_kw).try_into().unwrap();
40184023
stack_swap(ctx, cb, offset0, offset1, REG1, REG0);
40194024

40204025
// Next we're going to do some bookkeeping on our end so

yjit/src/cruby.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ extern "C" {
168168
#[link_name = "rb_iseq_encoded_size"]
169169
pub fn get_iseq_encoded_size(iseq: IseqPtr) -> c_uint;
170170

171+
#[link_name = "rb_get_iseq_body_local_iseq"]
172+
pub fn get_iseq_body_local_iseq(iseq: IseqPtr) -> IseqPtr;
173+
171174
#[link_name = "rb_get_iseq_body_iseq_encoded"]
172175
pub fn get_iseq_body_iseq_encoded(iseq: IseqPtr) -> *mut VALUE;
173176

@@ -178,7 +181,25 @@ extern "C" {
178181
pub fn get_iseq_body_stack_max(iseq:IseqPtr) -> c_uint;
179182

180183
#[link_name = "rb_get_iseq_flags_has_opt"]
181-
pub fn get_iseq_flags_has_opt(iseq: IseqPtr) -> c_int;
184+
pub fn get_iseq_flags_has_opt(iseq: IseqPtr) -> bool;
185+
186+
#[link_name = "rb_get_iseq_flags_has_kw"]
187+
pub fn get_iseq_flags_has_kw(iseq: IseqPtr) -> bool;
188+
189+
#[link_name = "rb_get_iseq_flags_has_rest"]
190+
pub fn get_iseq_flags_has_rest(iseq: IseqPtr) -> bool;
191+
192+
#[link_name = "rb_get_iseq_flags_has_post"]
193+
pub fn get_iseq_flags_has_post(iseq: IseqPtr) -> bool;
194+
195+
#[link_name = "rb_get_iseq_flags_has_kwrest"]
196+
pub fn get_iseq_flags_has_kwrest(iseq: IseqPtr) -> bool;
197+
198+
#[link_name = "rb_get_iseq_flags_has_block"]
199+
pub fn get_iseq_flags_has_block(iseq: IseqPtr) -> bool;
200+
201+
#[link_name = "rb_get_iseq_flags_has_accepts_no_kwarg"]
202+
pub fn get_iseq_flags_has_accepts_no_kwarg(iseq: IseqPtr) -> bool;
182203

183204
#[link_name = "rb_get_iseq_body_local_table_size"]
184205
pub fn get_iseq_body_local_table_size(iseq: IseqPtr) -> c_uint;
@@ -204,9 +225,6 @@ extern "C" {
204225
#[link_name = "rb_get_cikw_keywords_idx"]
205226
pub fn get_cikw_keywords_idx(cikw: *const rb_callinfo_kwarg, idx: c_int) -> VALUE;
206227

207-
#[link_name = "rb_iseq_needs_lead_args_only"]
208-
pub fn iseq_needs_lead_args_only(iseq: *const rb_iseq_t) -> bool;
209-
210228
#[link_name = "rb_get_call_data_ci"]
211229
pub fn get_call_data_ci(cd: * const rb_call_data) -> *const rb_callinfo;
212230

0 commit comments

Comments
 (0)