@@ -595,6 +595,10 @@ pub enum SendFallbackReason {
595595 SendWithoutBlockNotOptimizedMethodTypeOptimized ( OptimizedMethodType ) ,
596596 SendWithoutBlockBopRedefined ,
597597 SendWithoutBlockOperandsNotFixnum ,
598+ SendWithoutBlockDirectKeywordMismatch ,
599+ SendWithoutBlockDirectOptionalKeywords ,
600+ SendWithoutBlockDirectKeywordCountMismatch ,
601+ SendWithoutBlockDirectMissingKeyword ,
598602 SendPolymorphic ,
599603 SendMegamorphic ,
600604 SendNoProfiles ,
@@ -830,6 +834,7 @@ pub enum Insn {
830834 iseq : IseqPtr ,
831835 args : Vec < InsnId > ,
832836 state : InsnId ,
837+ kwarg : * const rb_callinfo_kwarg ,
833838 } ,
834839
835840 // Invoke a builtin function
@@ -1122,11 +1127,14 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
11221127 }
11231128 Ok ( ( ) )
11241129 }
1125- Insn :: SendWithoutBlockDirect { recv, cd, iseq, args, .. } => {
1130+ Insn :: SendWithoutBlockDirect { recv, cd, iseq, args, kwarg , .. } => {
11261131 write ! ( f, "SendWithoutBlockDirect {recv}, :{} ({:?})" , ruby_call_method_name( * cd) , self . ptr_map. map_ptr( iseq) ) ?;
11271132 for arg in args {
11281133 write ! ( f, ", {arg}" ) ?;
11291134 }
1135+ if !kwarg. is_null ( ) {
1136+ write ! ( f, " [kw]" ) ?;
1137+ }
11301138 Ok ( ( ) )
11311139 }
11321140 Insn :: Send { recv, cd, args, blockiseq, .. } => {
@@ -1475,7 +1483,20 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq
14751483 use Counter :: * ;
14761484 if unsafe { rb_get_iseq_flags_has_rest ( iseq) } { count_failure ( complex_arg_pass_param_rest) }
14771485 if unsafe { rb_get_iseq_flags_has_post ( iseq) } { count_failure ( complex_arg_pass_param_post) }
1478- if unsafe { rb_get_iseq_flags_has_kw ( iseq) } { count_failure ( complex_arg_pass_param_kw) }
1486+
1487+ // We support required-only keywords, but not optional keywords yet
1488+ if unsafe { rb_get_iseq_flags_has_kw ( iseq) } {
1489+ let keyword = unsafe { get_iseq_body_param_keyword ( iseq) } ;
1490+ if !keyword. is_null ( ) {
1491+ let num = unsafe { ( * keyword) . num } ;
1492+ let required_num = unsafe { ( * keyword) . required_num } ;
1493+ // Only support required keywords for now (no optional keywords)
1494+ if num != required_num {
1495+ count_failure ( complex_arg_pass_param_kw_opt)
1496+ }
1497+ }
1498+ }
1499+
14791500 if unsafe { rb_get_iseq_flags_has_kwrest ( iseq) } { count_failure ( complex_arg_pass_param_kwrest) }
14801501 if unsafe { rb_get_iseq_flags_has_block ( iseq) } { count_failure ( complex_arg_pass_param_block) }
14811502 if unsafe { rb_get_iseq_flags_forwardable ( iseq) } { count_failure ( complex_arg_pass_param_forwardable) }
@@ -1489,9 +1510,12 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq
14891510 // relies on rejecting features above.
14901511 let lead_num = unsafe { get_iseq_body_param_lead_num ( iseq) } ;
14911512 let opt_num = unsafe { get_iseq_body_param_opt_num ( iseq) } ;
1513+ let keyword = unsafe { get_iseq_body_param_keyword ( iseq) } ;
1514+ let kw_req_num = if keyword. is_null ( ) { 0 } else { unsafe { ( * keyword) . required_num } } ;
1515+ let req_num = lead_num + kw_req_num;
14921516 can_send = c_int:: try_from ( args. len ( ) )
14931517 . as_ref ( )
1494- . map ( |argc| ( lead_num ..=lead_num + opt_num) . contains ( argc) )
1518+ . map ( |argc| ( req_num ..=req_num + opt_num) . contains ( argc) )
14951519 . unwrap_or ( false ) ;
14961520 if !can_send {
14971521 function. set_dynamic_send_reason ( send_insn, ArgcParamMismatch ) ;
@@ -1820,13 +1844,14 @@ impl Function {
18201844 state,
18211845 reason,
18221846 } ,
1823- & SendWithoutBlockDirect { recv, cd, cme, iseq, ref args, state } => SendWithoutBlockDirect {
1847+ & SendWithoutBlockDirect { recv, cd, cme, iseq, ref args, state, kwarg } => SendWithoutBlockDirect {
18241848 recv : find ! ( recv) ,
18251849 cd,
18261850 cme,
18271851 iseq,
18281852 args : find_vec ! ( args) ,
18291853 state,
1854+ kwarg,
18301855 } ,
18311856 & Send { recv, cd, blockiseq, ref args, state, reason } => Send {
18321857 recv : find ! ( recv) ,
@@ -2396,7 +2421,16 @@ impl Function {
23962421 if let Some ( profiled_type) = profiled_type {
23972422 recv = self . push_insn ( block, Insn :: GuardType { val : recv, guard_type : Type :: from_profiled_type ( profiled_type) , state } ) ;
23982423 }
2399- let send_direct = self . push_insn ( block, Insn :: SendWithoutBlockDirect { recv, cd, cme, iseq, args, state } ) ;
2424+
2425+ // Check if caller is passing keywords but callee doesn't expect them.
2426+ let kwarg = unsafe { rb_vm_ci_kwarg ( ci) } ;
2427+ if !kwarg. is_null ( ) && !unsafe { rb_get_iseq_flags_has_kw ( iseq) } {
2428+ // Caller has keywords but callee doesn't; Need to convert to hash.
2429+ self . set_dynamic_send_reason ( insn_id, ComplexArgPass ) ;
2430+ self . push_insn_id ( block, insn_id) ; continue ;
2431+ }
2432+
2433+ let send_direct = self . push_insn ( block, Insn :: SendWithoutBlockDirect { recv, cd, cme, iseq, args, state, kwarg } ) ;
24002434 self . make_equal_to ( insn_id, send_direct) ;
24012435 } else if def_type == VM_METHOD_TYPE_BMETHOD {
24022436 let procv = unsafe { rb_get_def_bmethod_proc ( ( * cme) . def ) } ;
@@ -2431,7 +2465,16 @@ impl Function {
24312465 if let Some ( profiled_type) = profiled_type {
24322466 recv = self . push_insn ( block, Insn :: GuardType { val : recv, guard_type : Type :: from_profiled_type ( profiled_type) , state } ) ;
24332467 }
2434- let send_direct = self . push_insn ( block, Insn :: SendWithoutBlockDirect { recv, cd, cme, iseq, args, state } ) ;
2468+
2469+ // Check if caller is passing keywords but callee doesn't expect them.
2470+ let kwarg = unsafe { rb_vm_ci_kwarg ( ci) } ;
2471+ if !kwarg. is_null ( ) && !unsafe { rb_get_iseq_flags_has_kw ( iseq) } {
2472+ // Caller has keywords but callee doesn't; Need to convert to hash.
2473+ self . set_dynamic_send_reason ( insn_id, ComplexArgPass ) ;
2474+ self . push_insn_id ( block, insn_id) ; continue ;
2475+ }
2476+
2477+ let send_direct = self . push_insn ( block, Insn :: SendWithoutBlockDirect { recv, cd, cme, iseq, args, state, kwarg } ) ;
24352478 self . make_equal_to ( insn_id, send_direct) ;
24362479 } else if def_type == VM_METHOD_TYPE_IVAR && args. is_empty ( ) {
24372480 self . push_insn ( block, Insn :: PatchPoint { invariant : Invariant :: MethodRedefined { klass, method : mid, cme } , state } ) ;
@@ -4477,7 +4520,6 @@ fn unhandled_call_type(flags: u32) -> Result<(), CallType> {
44774520
44784521/// If a given call uses overly complex arguments, then we won't specialize.
44794522fn unspecializable_call_type ( flags : u32 ) -> bool {
4480- ( ( flags & VM_CALL_KWARG ) != 0 ) ||
44814523 ( ( flags & VM_CALL_ARGS_SPLAT ) != 0 ) ||
44824524 ( ( flags & VM_CALL_ARGS_BLOCKARG ) != 0 )
44834525}
0 commit comments