@@ -1297,6 +1297,20 @@ fn get_local_var_name_for_printer(iseq: Option<IseqPtr>, level: u32, ep_offset:
12971297 Some ( format ! ( ":{}" , id. contents_lossy( ) ) )
12981298}
12991299
1300+ /// Construct a qualified method name for display/debug output.
1301+ /// Returns strings like "Array#length" for instance methods or "Foo.bar" for singleton methods.
1302+ fn qualified_method_name ( class : VALUE , method_id : ID ) -> String {
1303+ let method_name = method_id. contents_lossy ( ) ;
1304+ // rb_zjit_singleton_class_p also checks if it's a class
1305+ if unsafe { rb_zjit_singleton_class_p ( class) } {
1306+ let class_name = get_class_name ( unsafe { rb_class_attached_object ( class) } ) ;
1307+ format ! ( "{class_name}.{method_name}" )
1308+ } else {
1309+ let class_name = get_class_name ( class) ;
1310+ format ! ( "{class_name}#{method_name}" )
1311+ }
1312+ }
1313+
13001314static REGEXP_FLAGS : & [ ( u32 , & str ) ] = & [
13011315 ( ONIG_OPTION_MULTILINE , "MULTILINE" ) ,
13021316 ( ONIG_OPTION_IGNORECASE , "IGNORECASE" ) ,
@@ -3504,6 +3518,40 @@ impl Function {
35043518 } ;
35053519 }
35063520 Insn :: InvokeSuper { recv, cd, blockiseq, args, state, .. } => {
3521+ // Helper to emit common guards for super call optimization.
3522+ fn emit_super_call_guards (
3523+ fun : & mut Function ,
3524+ block : BlockId ,
3525+ super_cme : * const rb_callable_method_entry_t ,
3526+ current_cme : * const rb_callable_method_entry_t ,
3527+ mid : ID ,
3528+ state : InsnId ,
3529+ ) {
3530+ fun. push_insn ( block, Insn :: PatchPoint {
3531+ invariant : Invariant :: MethodRedefined {
3532+ klass : unsafe { ( * super_cme) . defined_class } ,
3533+ method : mid,
3534+ cme : super_cme
3535+ } ,
3536+ state
3537+ } ) ;
3538+
3539+ let lep = fun. push_insn ( block, Insn :: GetLEP ) ;
3540+ fun. push_insn ( block, Insn :: GuardSuperMethodEntry {
3541+ lep,
3542+ cme : current_cme,
3543+ state
3544+ } ) ;
3545+
3546+ let block_handler = fun. push_insn ( block, Insn :: GetBlockHandler { lep } ) ;
3547+ fun. push_insn ( block, Insn :: GuardBitEquals {
3548+ val : block_handler,
3549+ expected : Const :: Value ( VALUE ( VM_BLOCK_HANDLER_NONE as usize ) ) ,
3550+ reason : SideExitReason :: UnhandledBlockArg ,
3551+ state
3552+ } ) ;
3553+ }
3554+
35073555 // Don't handle calls with literal blocks (e.g., super { ... })
35083556 if !blockiseq. is_null ( ) {
35093557 self . push_insn_id ( block, insn_id) ;
@@ -3567,68 +3615,107 @@ impl Function {
35673615 continue ;
35683616 }
35693617
3570- // Check if it's an ISEQ method; bail if it isn't.
35713618 let def_type = unsafe { get_cme_def_type ( super_cme) } ;
3572- if def_type != VM_METHOD_TYPE_ISEQ {
3573- self . push_insn_id ( block, insn_id) ;
3574- self . set_dynamic_send_reason ( insn_id, SuperNotOptimizedMethodType ( MethodType :: from ( def_type) ) ) ;
3575- continue ;
3576- }
35773619
3578- // Check if the super method's parameters support direct send.
3579- // If not, we can't do direct dispatch.
3580- let super_iseq = unsafe { get_def_iseq_ptr ( ( * super_cme) . def ) } ;
3581- // TODO: pass Option<blockiseq> to can_direct_send when we start specializing super { ... }
3582- if !can_direct_send ( self , block, super_iseq, ci, insn_id, args. as_slice ( ) , None ) {
3583- self . push_insn_id ( block, insn_id) ;
3584- self . set_dynamic_send_reason ( insn_id, SuperTargetComplexArgsPass ) ;
3585- continue ;
3586- }
3620+ if def_type == VM_METHOD_TYPE_ISEQ {
3621+ // Check if the super method's parameters support direct send.
3622+ // If not, we can't do direct dispatch.
3623+ let super_iseq = unsafe { get_def_iseq_ptr ( ( * super_cme) . def ) } ;
3624+ // TODO: pass Option<blockiseq> to can_direct_send when we start specializing `super { ... }`.
3625+ if !can_direct_send ( self , block, super_iseq, ci, insn_id, args. as_slice ( ) , None ) {
3626+ self . push_insn_id ( block, insn_id) ;
3627+ self . set_dynamic_send_reason ( insn_id, SuperTargetComplexArgsPass ) ;
3628+ continue ;
3629+ }
35873630
3588- // Add PatchPoint for method redefinition.
3589- self . push_insn ( block, Insn :: PatchPoint {
3590- invariant : Invariant :: MethodRedefined {
3591- klass : unsafe { ( * super_cme) . defined_class } ,
3592- method : mid,
3593- cme : super_cme
3594- } ,
3595- state
3596- } ) ;
3631+ let Ok ( ( send_state, processed_args, kw_bits) ) = self . prepare_direct_send_args ( block, & args, ci, super_iseq, state)
3632+ . inspect_err ( |& reason| self . set_dynamic_send_reason ( insn_id, reason) ) else {
3633+ self . push_insn_id ( block, insn_id) ; continue ;
3634+ } ;
35973635
3598- // Guard that we're calling `super` from the expected method context.
3599- let lep = self . push_insn ( block, Insn :: GetLEP ) ;
3600- self . push_insn ( block, Insn :: GuardSuperMethodEntry {
3601- lep,
3602- cme : current_cme,
3603- state
3604- } ) ;
3636+ emit_super_call_guards ( self , block, super_cme, current_cme, mid, state) ;
36053637
3606- // Guard that no block is being passed (implicit or explicit).
3607- let block_handler = self . push_insn ( block, Insn :: GetBlockHandler { lep } ) ;
3608- self . push_insn ( block, Insn :: GuardBitEquals {
3609- val : block_handler,
3610- expected : Const :: Value ( VALUE ( VM_BLOCK_HANDLER_NONE as usize ) ) ,
3611- reason : SideExitReason :: UnhandledBlockArg ,
3612- state
3613- } ) ;
3638+ // Use SendDirect with the super method's CME and ISEQ.
3639+ let send_direct = self . push_insn ( block, Insn :: SendDirect {
3640+ recv,
3641+ cd,
3642+ cme : super_cme,
3643+ iseq : super_iseq,
3644+ args : processed_args,
3645+ kw_bits,
3646+ state : send_state,
3647+ blockiseq : None ,
3648+ } ) ;
3649+ self . make_equal_to ( insn_id, send_direct) ;
36143650
3615- let Ok ( ( send_state, processed_args, kw_bits) ) = self . prepare_direct_send_args ( block, & args, ci, super_iseq, state)
3616- . inspect_err ( |& reason| self . set_dynamic_send_reason ( insn_id, reason) ) else {
3617- self . push_insn_id ( block, insn_id) ; continue ;
3618- } ;
3651+ } else if def_type == VM_METHOD_TYPE_CFUNC {
3652+ let cfunc = unsafe { get_cme_def_body_cfunc ( super_cme) } ;
3653+ let cfunc_argc = unsafe { get_mct_argc ( cfunc) } ;
3654+ let cfunc_ptr = unsafe { get_mct_func ( cfunc) } . cast ( ) ;
3655+
3656+ match cfunc_argc {
3657+ // C function with fixed argument count.
3658+ 0 .. => {
3659+ // Check argc matches
3660+ if args. len ( ) != cfunc_argc as usize {
3661+ self . push_insn_id ( block, insn_id) ;
3662+ self . set_dynamic_send_reason ( insn_id, ArgcParamMismatch ) ;
3663+ continue ;
3664+ }
36193665
3620- // Use SendDirect with the super method's CME and ISEQ.
3621- let send_direct = self . push_insn ( block, Insn :: SendDirect {
3622- recv,
3623- cd,
3624- cme : super_cme,
3625- iseq : super_iseq,
3626- args : processed_args,
3627- kw_bits,
3628- state : send_state,
3629- blockiseq : None ,
3630- } ) ;
3631- self . make_equal_to ( insn_id, send_direct) ;
3666+ emit_super_call_guards ( self , block, super_cme, current_cme, mid, state) ;
3667+
3668+ // Use CCallWithFrame for the C function.
3669+ let name = rust_str_to_id ( & qualified_method_name ( unsafe { ( * super_cme) . owner } , unsafe { ( * super_cme) . called_id } ) ) ;
3670+ let ccall = self . push_insn ( block, Insn :: CCallWithFrame {
3671+ cd,
3672+ cfunc : cfunc_ptr,
3673+ recv,
3674+ args : args. clone ( ) ,
3675+ cme : super_cme,
3676+ name,
3677+ state,
3678+ return_type : types:: BasicObject ,
3679+ elidable : false ,
3680+ blockiseq : None ,
3681+ } ) ;
3682+ self . make_equal_to ( insn_id, ccall) ;
3683+ }
3684+
3685+ // Variadic C function: func(int argc, VALUE *argv, VALUE recv)
3686+ -1 => {
3687+ emit_super_call_guards ( self , block, super_cme, current_cme, mid, state) ;
3688+
3689+ // Use CCallVariadic for the variadic C function.
3690+ let name = rust_str_to_id ( & qualified_method_name ( unsafe { ( * super_cme) . owner } , unsafe { ( * super_cme) . called_id } ) ) ;
3691+ let ccall = self . push_insn ( block, Insn :: CCallVariadic {
3692+ cfunc : cfunc_ptr,
3693+ recv,
3694+ args : args. clone ( ) ,
3695+ cme : super_cme,
3696+ name,
3697+ state,
3698+ return_type : types:: BasicObject ,
3699+ elidable : false ,
3700+ blockiseq : None ,
3701+ } ) ;
3702+ self . make_equal_to ( insn_id, ccall) ;
3703+ }
3704+
3705+ // Array-variadic: (self, args_ruby_array).
3706+ -2 => {
3707+ self . push_insn_id ( block, insn_id) ;
3708+ self . set_dynamic_send_reason ( insn_id, SuperNotOptimizedMethodType ( MethodType :: Cfunc ) ) ;
3709+ continue ;
3710+ }
3711+ _ => unreachable ! ( "unknown cfunc argc: {}" , cfunc_argc)
3712+ }
3713+ } else {
3714+ // Other method types (not ISEQ or CFUNC)
3715+ self . push_insn_id ( block, insn_id) ;
3716+ self . set_dynamic_send_reason ( insn_id, SuperNotOptimizedMethodType ( MethodType :: from ( def_type) ) ) ;
3717+ continue ;
3718+ }
36323719 }
36333720 _ => { self . push_insn_id ( block, insn_id) ; }
36343721 }
@@ -4296,18 +4383,6 @@ impl Function {
42964383 Err ( ( ) )
42974384 }
42984385
4299- fn qualified_method_name ( class : VALUE , method_id : ID ) -> String {
4300- let method_name = method_id. contents_lossy ( ) ;
4301- // rb_zjit_singleton_class_p also checks if it's a class
4302- if unsafe { rb_zjit_singleton_class_p ( class) } {
4303- let class_name = get_class_name ( unsafe { rb_class_attached_object ( class) } ) ;
4304- format ! ( "{class_name}.{method_name}" )
4305- } else {
4306- let class_name = get_class_name ( class) ;
4307- format ! ( "{class_name}#{method_name}" )
4308- }
4309- }
4310-
43114386 fn count_not_inlined_cfunc ( fun : & mut Function , block : BlockId , cme : * const rb_callable_method_entry_t ) {
43124387 let owner = unsafe { ( * cme) . owner } ;
43134388 let called_id = unsafe { ( * cme) . called_id } ;
0 commit comments