@@ -14,7 +14,7 @@ use std::{
1414use crate :: hir_type:: { Type , types} ;
1515use crate :: bitset:: BitSet ;
1616use crate :: profile:: { TypeDistributionSummary , ProfiledType } ;
17- use crate :: stats:: Counter ;
17+ use crate :: stats:: { Counter , get_or_create_unoptimized_cfunc_counter_ptr } ;
1818
1919/// An index of an [`Insn`] in a [`Function`]. This is a popular
2020/// type since this effectively acts as a pointer to an [`Insn`].
@@ -711,6 +711,9 @@ pub enum Insn {
711711 /// Increment a counter in ZJIT stats
712712 IncrCounter ( Counter ) ,
713713
714+ /// Increment a counter in ZJIT stats for the given unoptimized C function
715+ CountUnoptimizedCFunc { signature : String , counter_ptr : * mut u64 } ,
716+
714717 /// Equivalent of RUBY_VM_CHECK_INTS. Automatically inserted by the compiler before jumps and
715718 /// return instructions.
716719 CheckInterrupts { state : InsnId } ,
@@ -724,7 +727,7 @@ impl Insn {
724727 | Insn :: IfTrue { .. } | Insn :: IfFalse { .. } | Insn :: EntryPoint { .. } | Insn :: Return { .. }
725728 | Insn :: PatchPoint { .. } | Insn :: SetIvar { .. } | Insn :: ArrayExtend { .. }
726729 | Insn :: ArrayPush { .. } | Insn :: SideExit { .. } | Insn :: SetGlobal { .. }
727- | Insn :: SetLocal { .. } | Insn :: Throw { .. } | Insn :: IncrCounter ( _)
730+ | Insn :: SetLocal { .. } | Insn :: Throw { .. } | Insn :: IncrCounter ( _) | Insn :: CountUnoptimizedCFunc { .. }
728731 | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } => false ,
729732 _ => true ,
730733 }
@@ -978,6 +981,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
978981 }
979982 Ok ( ( ) )
980983 } ,
984+ Insn :: CountUnoptimizedCFunc { signature, .. } => write ! ( f, "CountUnoptimizedCFunc {}" , signature) ,
981985 Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state. print( self . ptr_map) ) ,
982986 Insn :: Defined { op_type, v, .. } => {
983987 // op_type (enum defined_type) printing logic from iseq.c.
@@ -1385,6 +1389,7 @@ impl Function {
13851389 | EntryPoint { ..}
13861390 | LoadPC
13871391 | LoadSelf
1392+ | CountUnoptimizedCFunc { ..}
13881393 | IncrCounter ( _) ) => result. clone ( ) ,
13891394 & Snapshot { state : FrameState { iseq, insn_idx, pc, ref stack, ref locals } } =>
13901395 Snapshot {
@@ -1534,7 +1539,7 @@ impl Function {
15341539 | Insn :: IfTrue { .. } | Insn :: IfFalse { .. } | Insn :: Return { .. } | Insn :: Throw { .. }
15351540 | Insn :: PatchPoint { .. } | Insn :: SetIvar { .. } | Insn :: ArrayExtend { .. }
15361541 | Insn :: ArrayPush { .. } | Insn :: SideExit { .. } | Insn :: SetLocal { .. } | Insn :: IncrCounter ( _)
1537- | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } =>
1542+ | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } | Insn :: CountUnoptimizedCFunc { .. } =>
15381543 panic ! ( "Cannot infer type of instruction with no output: {}" , self . insns[ insn. 0 ] ) ,
15391544 Insn :: Const { val : Const :: Value ( val) } => Type :: from_value ( * val) ,
15401545 Insn :: Const { val : Const :: CBool ( val) } => Type :: from_cbool ( * val) ,
@@ -2154,9 +2159,9 @@ impl Function {
21542159 self_type : Type ,
21552160 send : Insn ,
21562161 send_insn_id : InsnId ,
2157- ) -> Result < ( ) , ( ) > {
2162+ ) -> Result < ( ) , Option < * const rb_callable_method_entry_struct > > {
21582163 let Insn :: SendWithoutBlock { mut recv, cd, mut args, state, .. } = send else {
2159- return Err ( ( ) ) ;
2164+ return Err ( None ) ;
21602165 } ;
21612166
21622167 let call_info = unsafe { ( * cd) . ci } ;
@@ -2168,20 +2173,20 @@ impl Function {
21682173 ( class, None )
21692174 } else {
21702175 let iseq_insn_idx = fun. frame_state ( state) . insn_idx ;
2171- let Some ( recv_type) = fun. profiled_type_of_at ( recv, iseq_insn_idx) else { return Err ( ( ) ) } ;
2176+ let Some ( recv_type) = fun. profiled_type_of_at ( recv, iseq_insn_idx) else { return Err ( None ) } ;
21722177 ( recv_type. class ( ) , Some ( recv_type) )
21732178 } ;
21742179
21752180 // Do method lookup
2176- let method = unsafe { rb_callable_method_entry ( recv_class, method_id) } ;
2181+ let method: * const rb_callable_method_entry_struct = unsafe { rb_callable_method_entry ( recv_class, method_id) } ;
21772182 if method. is_null ( ) {
2178- return Err ( ( ) ) ;
2183+ return Err ( None ) ;
21792184 }
21802185
21812186 // Filter for C methods
21822187 let def_type = unsafe { get_cme_def_type ( method) } ;
21832188 if def_type != VM_METHOD_TYPE_CFUNC {
2184- return Err ( ( ) ) ;
2189+ return Err ( None ) ;
21852190 }
21862191
21872192 // Find the `argc` (arity) of the C method, which describes the parameters it expects
@@ -2193,15 +2198,15 @@ impl Function {
21932198 //
21942199 // Bail on argc mismatch
21952200 if argc != cfunc_argc as u32 {
2196- return Err ( ( ) ) ;
2201+ return Err ( Some ( method ) ) ;
21972202 }
21982203
21992204 // Filter for a leaf and GC free function
22002205 use crate :: cruby_methods:: FnProperties ;
22012206 let Some ( FnProperties { leaf : true , no_gc : true , return_type, elidable } ) =
22022207 ZJITState :: get_method_annotations ( ) . get_cfunc_properties ( method)
22032208 else {
2204- return Err ( ( ) ) ;
2209+ return Err ( Some ( method ) ) ;
22052210 } ;
22062211
22072212 let ci_flags = unsafe { vm_ci_flag ( call_info) } ;
@@ -2218,13 +2223,13 @@ impl Function {
22182223 cfunc_args. append ( & mut args) ;
22192224 let ccall = fun. push_insn ( block, Insn :: CCall { cfun, args : cfunc_args, name : method_id, return_type, elidable } ) ;
22202225 fun. make_equal_to ( send_insn_id, ccall) ;
2221- return Ok ( ( ) ) ;
2226+ return Ok ( ( ) )
22222227 }
22232228 }
22242229 // Variadic method
22252230 -1 => {
22262231 if unsafe { rb_zjit_method_tracing_currently_enabled ( ) } {
2227- return Err ( ( ) ) ;
2232+ return Err ( None ) ;
22282233 }
22292234 // The method gets a pointer to the first argument
22302235 // func(int argc, VALUE *argv, VALUE recv)
@@ -2256,8 +2261,9 @@ impl Function {
22562261 } ) ;
22572262
22582263 fun. make_equal_to ( send_insn_id, ccall) ;
2259- return Ok ( ( ) ) ;
2264+ return Ok ( ( ) )
22602265 }
2266+
22612267 // Fall through for complex cases (splat, kwargs, etc.)
22622268 }
22632269 -2 => {
@@ -2267,7 +2273,7 @@ impl Function {
22672273 _ => unreachable ! ( "unknown cfunc kind: argc={argc}" )
22682274 }
22692275
2270- Err ( ( ) )
2276+ Err ( Some ( method ) )
22712277 }
22722278
22732279 for block in self . rpo ( ) {
@@ -2276,8 +2282,19 @@ impl Function {
22762282 for insn_id in old_insns {
22772283 if let send @ Insn :: SendWithoutBlock { recv, .. } = self . find ( insn_id) {
22782284 let recv_type = self . type_of ( recv) ;
2279- if reduce_to_ccall ( self , block, recv_type, send, insn_id) . is_ok ( ) {
2280- continue ;
2285+ match reduce_to_ccall ( self , block, recv_type, send, insn_id) {
2286+ Ok ( ( ) ) => continue ,
2287+ Err ( Some ( cme) ) => {
2288+ let owner = unsafe { ( * cme) . owner } ;
2289+ let called_id = unsafe { ( * cme) . called_id } ;
2290+ let class_name = get_class_name ( owner) ;
2291+ let method_name = called_id. contents_lossy ( ) ;
2292+ let signature = format ! ( "{}#{}" , class_name, method_name) ;
2293+ let counter_ptr = get_or_create_unoptimized_cfunc_counter_ptr ( signature. clone ( ) ) ;
2294+
2295+ self . push_insn ( block, Insn :: CountUnoptimizedCFunc { signature, counter_ptr } ) ;
2296+ }
2297+ _ => { }
22812298 }
22822299 }
22832300 self . push_insn_id ( block, insn_id) ;
@@ -2423,7 +2440,8 @@ impl Function {
24232440 | & Insn :: LoadSelf
24242441 | & Insn :: GetLocal { .. }
24252442 | & Insn :: PutSpecialObject { .. }
2426- | & Insn :: IncrCounter ( _) =>
2443+ | & Insn :: IncrCounter ( _)
2444+ | & Insn :: CountUnoptimizedCFunc { .. } =>
24272445 { }
24282446 & Insn :: PatchPoint { state, .. }
24292447 | & Insn :: CheckInterrupts { state }
@@ -9841,6 +9859,7 @@ mod opt_tests {
98419859 bb2(v6:BasicObject):
98429860 v10:Fixnum[1] = Const Value(1)
98439861 v11:Fixnum[0] = Const Value(0)
9862+ CountUnoptimizedCFunc Kernel#itself
98449863 v13:BasicObject = SendWithoutBlock v10, :itself, v11
98459864 CheckInterrupts
98469865 Return v13
@@ -10720,6 +10739,7 @@ mod opt_tests {
1072010739 Jump bb2(v4)
1072110740 bb2(v6:BasicObject):
1072210741 v11:HashExact = NewHash
10742+ CountUnoptimizedCFunc Kernel#dup
1072310743 v13:BasicObject = SendWithoutBlock v11, :dup
1072410744 v15:BasicObject = SendWithoutBlock v13, :freeze
1072510745 CheckInterrupts
@@ -10744,6 +10764,7 @@ mod opt_tests {
1074410764 bb2(v6:BasicObject):
1074510765 v11:HashExact = NewHash
1074610766 v12:NilClass = Const Value(nil)
10767+ CountUnoptimizedCFunc Hash#freeze
1074710768 v14:BasicObject = SendWithoutBlock v11, :freeze, v12
1074810769 CheckInterrupts
1074910770 Return v14
@@ -10811,6 +10832,7 @@ mod opt_tests {
1081110832 Jump bb2(v4)
1081210833 bb2(v6:BasicObject):
1081310834 v11:ArrayExact = NewArray
10835+ CountUnoptimizedCFunc Kernel#dup
1081410836 v13:BasicObject = SendWithoutBlock v11, :dup
1081510837 v15:BasicObject = SendWithoutBlock v13, :freeze
1081610838 CheckInterrupts
@@ -10835,6 +10857,7 @@ mod opt_tests {
1083510857 bb2(v6:BasicObject):
1083610858 v11:ArrayExact = NewArray
1083710859 v12:NilClass = Const Value(nil)
10860+ CountUnoptimizedCFunc Array#freeze
1083810861 v14:BasicObject = SendWithoutBlock v11, :freeze, v12
1083910862 CheckInterrupts
1084010863 Return v14
@@ -10903,6 +10926,7 @@ mod opt_tests {
1090310926 bb2(v6:BasicObject):
1090410927 v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1090510928 v12:StringExact = StringCopy v10
10929+ CountUnoptimizedCFunc String#dup
1090610930 v14:BasicObject = SendWithoutBlock v12, :dup
1090710931 v16:BasicObject = SendWithoutBlock v14, :freeze
1090810932 CheckInterrupts
@@ -10928,6 +10952,7 @@ mod opt_tests {
1092810952 v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1092910953 v12:StringExact = StringCopy v10
1093010954 v13:NilClass = Const Value(nil)
10955+ CountUnoptimizedCFunc String#freeze
1093110956 v15:BasicObject = SendWithoutBlock v12, :freeze, v13
1093210957 CheckInterrupts
1093310958 Return v15
@@ -10996,6 +11021,7 @@ mod opt_tests {
1099611021 bb2(v6:BasicObject):
1099711022 v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1099811023 v12:StringExact = StringCopy v10
11024+ CountUnoptimizedCFunc String#dup
1099911025 v14:BasicObject = SendWithoutBlock v12, :dup
1100011026 v16:BasicObject = SendWithoutBlock v14, :-@
1100111027 CheckInterrupts
@@ -11133,6 +11159,7 @@ mod opt_tests {
1113311159 bb2(v8:BasicObject, v9:BasicObject):
1113411160 v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1113511161 v25:BasicObject = GuardTypeNot v9, String
11162+ CountUnoptimizedCFunc Array#to_s
1113611163 v26:BasicObject = SendWithoutBlock v9, :to_s
1113711164 v17:String = AnyToString v9, str: v26
1113811165 v19:StringExact = StringConcat v13, v17
0 commit comments