@@ -697,6 +697,9 @@ pub enum Insn {
697697 /// Increment a counter in ZJIT stats
698698 IncrCounter ( Counter ) ,
699699
700+ /// Increment a counter in ZJIT stats for the given C function
701+ CountUnoptimizedCFunc { cme : * const rb_callable_method_entry_struct } ,
702+
700703 /// Equivalent of RUBY_VM_CHECK_INTS. Automatically inserted by the compiler before jumps and
701704 /// return instructions.
702705 CheckInterrupts { state : InsnId } ,
@@ -710,7 +713,7 @@ impl Insn {
710713 | Insn :: IfTrue { .. } | Insn :: IfFalse { .. } | Insn :: Return { .. }
711714 | Insn :: PatchPoint { .. } | Insn :: SetIvar { .. } | Insn :: ArrayExtend { .. }
712715 | Insn :: ArrayPush { .. } | Insn :: SideExit { .. } | Insn :: SetGlobal { .. }
713- | Insn :: SetLocal { .. } | Insn :: Throw { .. } | Insn :: IncrCounter ( _)
716+ | Insn :: SetLocal { .. } | Insn :: Throw { .. } | Insn :: IncrCounter ( _) | Insn :: CountUnoptimizedCFunc { .. }
714717 | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } => false ,
715718 _ => true ,
716719 }
@@ -956,6 +959,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
956959 }
957960 Ok ( ( ) )
958961 } ,
962+ Insn :: CountUnoptimizedCFunc { .. } => write ! ( f, "CountUnoptimizedCFunc" ) ,
959963 Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state. print( self . ptr_map) ) ,
960964 Insn :: Defined { op_type, v, .. } => {
961965 // op_type (enum defined_type) printing logic from iseq.c.
@@ -1347,6 +1351,7 @@ impl Function {
13471351 | GetGlobal { ..}
13481352 | GetLocal { ..}
13491353 | SideExit { ..}
1354+ | CountUnoptimizedCFunc { ..}
13501355 | IncrCounter ( _) ) => result. clone ( ) ,
13511356 & Snapshot { state : FrameState { iseq, insn_idx, pc, ref stack, ref locals } } =>
13521357 Snapshot {
@@ -1495,7 +1500,7 @@ impl Function {
14951500 | Insn :: IfTrue { .. } | Insn :: IfFalse { .. } | Insn :: Return { .. } | Insn :: Throw { .. }
14961501 | Insn :: PatchPoint { .. } | Insn :: SetIvar { .. } | Insn :: ArrayExtend { .. }
14971502 | Insn :: ArrayPush { .. } | Insn :: SideExit { .. } | Insn :: SetLocal { .. } | Insn :: IncrCounter ( _)
1498- | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } =>
1503+ | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } | Insn :: CountUnoptimizedCFunc { .. } =>
14991504 panic ! ( "Cannot infer type of instruction with no output: {}" , self . insns[ insn. 0 ] ) ,
15001505 Insn :: Const { val : Const :: Value ( val) } => Type :: from_value ( * val) ,
15011506 Insn :: Const { val : Const :: CBool ( val) } => Type :: from_cbool ( * val) ,
@@ -2083,9 +2088,9 @@ impl Function {
20832088 self_type : Type ,
20842089 send : Insn ,
20852090 send_insn_id : InsnId ,
2086- ) -> Result < ( ) , ( ) > {
2091+ ) -> Result < ( ) , Option < * const rb_callable_method_entry_struct > > {
20872092 let Insn :: SendWithoutBlock { mut recv, cd, mut args, state, .. } = send else {
2088- return Err ( ( ) ) ;
2093+ return Err ( None ) ;
20892094 } ;
20902095
20912096 let call_info = unsafe { ( * cd) . ci } ;
@@ -2097,20 +2102,20 @@ impl Function {
20972102 ( class, None )
20982103 } else {
20992104 let iseq_insn_idx = fun. frame_state ( state) . insn_idx ;
2100- let Some ( recv_type) = fun. profiled_type_of_at ( recv, iseq_insn_idx) else { return Err ( ( ) ) } ;
2105+ let Some ( recv_type) = fun. profiled_type_of_at ( recv, iseq_insn_idx) else { return Err ( None ) } ;
21012106 ( recv_type. class ( ) , Some ( recv_type) )
21022107 } ;
21032108
21042109 // Do method lookup
2105- let method = unsafe { rb_callable_method_entry ( recv_class, method_id) } ;
2110+ let method: * const rb_callable_method_entry_struct = unsafe { rb_callable_method_entry ( recv_class, method_id) } ;
21062111 if method. is_null ( ) {
2107- return Err ( ( ) ) ;
2112+ return Err ( None ) ;
21082113 }
21092114
21102115 // Filter for C methods
21112116 let def_type = unsafe { get_cme_def_type ( method) } ;
21122117 if def_type != VM_METHOD_TYPE_CFUNC {
2113- return Err ( ( ) ) ;
2118+ return Err ( None ) ;
21142119 }
21152120
21162121 // Find the `argc` (arity) of the C method, which describes the parameters it expects
@@ -2122,15 +2127,18 @@ impl Function {
21222127 //
21232128 // Bail on argc mismatch
21242129 if argc != cfunc_argc as u32 {
2125- return Err ( ( ) ) ;
2130+ return Err ( Some ( method ) ) ;
21262131 }
21272132
21282133 // Filter for a leaf and GC free function
21292134 use crate :: cruby_methods:: FnProperties ;
2130- let Some ( FnProperties { leaf : true , no_gc : true , return_type, elidable } ) =
2131- ZJITState :: get_method_annotations ( ) . get_cfunc_properties ( method)
2132- else {
2133- return Err ( ( ) ) ;
2135+ let cfunc_properties = ZJITState :: get_method_annotations ( ) . get_cfunc_properties ( method) ;
2136+
2137+ let ( return_type, elidable) = match cfunc_properties {
2138+ Some ( FnProperties { leaf : true , no_gc : true , return_type, elidable } ) => {
2139+ ( return_type, elidable)
2140+ }
2141+ _ => return Err ( Some ( method) )
21342142 } ;
21352143
21362144 let ci_flags = unsafe { vm_ci_flag ( call_info) } ;
@@ -2147,13 +2155,15 @@ impl Function {
21472155 cfunc_args. append ( & mut args) ;
21482156 let ccall = fun. push_insn ( block, Insn :: CCall { cfun, args : cfunc_args, name : method_id, return_type, elidable } ) ;
21492157 fun. make_equal_to ( send_insn_id, ccall) ;
2150- return Ok ( ( ) ) ;
2158+ Ok ( ( ) )
2159+ } else {
2160+ Err ( Some ( method) )
21512161 }
21522162 }
21532163 // Variadic method
21542164 -1 => {
21552165 if unsafe { rb_zjit_method_tracing_currently_enabled ( ) } {
2156- return Err ( ( ) ) ;
2166+ return Err ( None ) ;
21572167 }
21582168 // The method gets a pointer to the first argument
21592169 // func(int argc, VALUE *argv, VALUE recv)
@@ -2185,18 +2195,19 @@ impl Function {
21852195 } ) ;
21862196
21872197 fun. make_equal_to ( send_insn_id, ccall) ;
2188- return Ok ( ( ) ) ;
2198+ Ok ( ( ) )
2199+ } else {
2200+ // Fall through for complex cases (splat, kwargs, etc.)
2201+ Err ( Some ( method) )
21892202 }
2190- // Fall through for complex cases (splat, kwargs, etc.)
21912203 }
21922204 -2 => {
21932205 // (self, args_ruby_array) parameter form
21942206 // Falling through for now
2207+ Err ( Some ( method) )
21952208 }
21962209 _ => unreachable ! ( "unknown cfunc kind: argc={argc}" )
21972210 }
2198-
2199- Err ( ( ) )
22002211 }
22012212
22022213 for block in self . rpo ( ) {
@@ -2205,8 +2216,12 @@ impl Function {
22052216 for insn_id in old_insns {
22062217 if let send @ Insn :: SendWithoutBlock { recv, .. } = self . find ( insn_id) {
22072218 let recv_type = self . type_of ( recv) ;
2208- if reduce_to_ccall ( self , block, recv_type, send, insn_id) . is_ok ( ) {
2209- continue ;
2219+ match reduce_to_ccall ( self , block, recv_type, send, insn_id) {
2220+ Ok ( ( ) ) => continue ,
2221+ Err ( Some ( cme) ) => {
2222+ self . push_insn ( block, Insn :: CountUnoptimizedCFunc { cme } ) ;
2223+ }
2224+ _ => { }
22102225 }
22112226 }
22122227 self . push_insn_id ( block, insn_id) ;
@@ -2349,7 +2364,8 @@ impl Function {
23492364 | & Insn :: Param { .. }
23502365 | & Insn :: GetLocal { .. }
23512366 | & Insn :: PutSpecialObject { .. }
2352- | & Insn :: IncrCounter ( _) =>
2367+ | & Insn :: IncrCounter ( _)
2368+ | & Insn :: CountUnoptimizedCFunc { .. } =>
23532369 { }
23542370 & Insn :: PatchPoint { state, .. }
23552371 | & Insn :: CheckInterrupts { state }
@@ -8072,6 +8088,7 @@ mod opt_tests {
80728088 bb0(v0:BasicObject):
80738089 v4:Fixnum[1] = Const Value(1)
80748090 v5:Fixnum[0] = Const Value(0)
8091+ CountUnoptimizedCFunc
80758092 v7:BasicObject = SendWithoutBlock v4, :itself, v5
80768093 CheckInterrupts
80778094 Return v7
@@ -8709,6 +8726,7 @@ mod opt_tests {
87098726 fn test@<compiled>:2:
87108727 bb0(v0:BasicObject):
87118728 v5:HashExact = NewHash
8729+ CountUnoptimizedCFunc
87128730 v7:BasicObject = SendWithoutBlock v5, :dup
87138731 v9:BasicObject = SendWithoutBlock v7, :freeze
87148732 CheckInterrupts
@@ -8726,6 +8744,7 @@ mod opt_tests {
87268744 bb0(v0:BasicObject):
87278745 v5:HashExact = NewHash
87288746 v6:NilClass = Const Value(nil)
8747+ CountUnoptimizedCFunc
87298748 v8:BasicObject = SendWithoutBlock v5, :freeze, v6
87308749 CheckInterrupts
87318750 Return v8
@@ -8772,6 +8791,7 @@ mod opt_tests {
87728791 fn test@<compiled>:2:
87738792 bb0(v0:BasicObject):
87748793 v5:ArrayExact = NewArray
8794+ CountUnoptimizedCFunc
87758795 v7:BasicObject = SendWithoutBlock v5, :dup
87768796 v9:BasicObject = SendWithoutBlock v7, :freeze
87778797 CheckInterrupts
@@ -8789,6 +8809,7 @@ mod opt_tests {
87898809 bb0(v0:BasicObject):
87908810 v5:ArrayExact = NewArray
87918811 v6:NilClass = Const Value(nil)
8812+ CountUnoptimizedCFunc
87928813 v8:BasicObject = SendWithoutBlock v5, :freeze, v6
87938814 CheckInterrupts
87948815 Return v8
@@ -8836,6 +8857,7 @@ mod opt_tests {
88368857 bb0(v0:BasicObject):
88378858 v4:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
88388859 v6:StringExact = StringCopy v4
8860+ CountUnoptimizedCFunc
88398861 v8:BasicObject = SendWithoutBlock v6, :dup
88408862 v10:BasicObject = SendWithoutBlock v8, :freeze
88418863 CheckInterrupts
@@ -8854,6 +8876,7 @@ mod opt_tests {
88548876 v4:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
88558877 v6:StringExact = StringCopy v4
88568878 v7:NilClass = Const Value(nil)
8879+ CountUnoptimizedCFunc
88578880 v9:BasicObject = SendWithoutBlock v6, :freeze, v7
88588881 CheckInterrupts
88598882 Return v9
@@ -8901,6 +8924,7 @@ mod opt_tests {
89018924 bb0(v0:BasicObject):
89028925 v4:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
89038926 v6:StringExact = StringCopy v4
8927+ CountUnoptimizedCFunc
89048928 v8:BasicObject = SendWithoutBlock v6, :dup
89058929 v10:BasicObject = SendWithoutBlock v8, :-@
89068930 CheckInterrupts
@@ -9000,6 +9024,7 @@ mod opt_tests {
90009024 bb0(v0:BasicObject, v1:BasicObject):
90019025 v5:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
90029026 v17:BasicObject = GuardTypeNot v1, String
9027+ CountUnoptimizedCFunc
90039028 v18:BasicObject = SendWithoutBlock v1, :to_s
90049029 v9:String = AnyToString v1, str: v18
90059030 v11:StringExact = StringConcat v5, v9
0 commit comments