@@ -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
0 commit comments