@@ -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`].
@@ -707,6 +707,9 @@ pub enum Insn {
707707 /// Increment a counter in ZJIT stats
708708 IncrCounter ( Counter ) ,
709709
710+ /// Increment a counter in ZJIT stats for the given unoptimized C function
711+ CountUnoptimizedCFunc { signature : String , counter_ptr : * mut u64 } ,
712+
710713 /// Equivalent of RUBY_VM_CHECK_INTS. Automatically inserted by the compiler before jumps and
711714 /// return instructions.
712715 CheckInterrupts { state : InsnId } ,
@@ -720,7 +723,7 @@ impl Insn {
720723 | Insn :: IfTrue { .. } | Insn :: IfFalse { .. } | Insn :: EntryPoint { .. } | Insn :: Return { .. }
721724 | Insn :: PatchPoint { .. } | Insn :: SetIvar { .. } | Insn :: ArrayExtend { .. }
722725 | Insn :: ArrayPush { .. } | Insn :: SideExit { .. } | Insn :: SetGlobal { .. }
723- | Insn :: SetLocal { .. } | Insn :: Throw { .. } | Insn :: IncrCounter ( _)
726+ | Insn :: SetLocal { .. } | Insn :: Throw { .. } | Insn :: IncrCounter ( _) | Insn :: CountUnoptimizedCFunc { .. }
724727 | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } => false ,
725728 _ => true ,
726729 }
@@ -972,6 +975,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
972975 }
973976 Ok ( ( ) )
974977 } ,
978+ Insn :: CountUnoptimizedCFunc { signature, .. } => write ! ( f, "CountUnoptimizedCFunc {}" , signature) ,
975979 Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state. print( self . ptr_map) ) ,
976980 Insn :: Defined { op_type, v, .. } => {
977981 // op_type (enum defined_type) printing logic from iseq.c.
@@ -1376,6 +1380,7 @@ impl Function {
13761380 | SideExit { ..}
13771381 | EntryPoint { ..}
13781382 | LoadPC
1383+ | CountUnoptimizedCFunc { ..}
13791384 | IncrCounter ( _) ) => result. clone ( ) ,
13801385 & Snapshot { state : FrameState { iseq, insn_idx, pc, ref stack, ref locals } } =>
13811386 Snapshot {
@@ -1525,7 +1530,7 @@ impl Function {
15251530 | Insn :: IfTrue { .. } | Insn :: IfFalse { .. } | Insn :: Return { .. } | Insn :: Throw { .. }
15261531 | Insn :: PatchPoint { .. } | Insn :: SetIvar { .. } | Insn :: ArrayExtend { .. }
15271532 | Insn :: ArrayPush { .. } | Insn :: SideExit { .. } | Insn :: SetLocal { .. } | Insn :: IncrCounter ( _)
1528- | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } =>
1533+ | Insn :: CheckInterrupts { .. } | Insn :: GuardBlockParamProxy { .. } | Insn :: CountUnoptimizedCFunc { .. } =>
15291534 panic ! ( "Cannot infer type of instruction with no output: {}" , self . insns[ insn. 0 ] ) ,
15301535 Insn :: Const { val : Const :: Value ( val) } => Type :: from_value ( * val) ,
15311536 Insn :: Const { val : Const :: CBool ( val) } => Type :: from_cbool ( * val) ,
@@ -2149,9 +2154,9 @@ impl Function {
21492154 self_type : Type ,
21502155 send : Insn ,
21512156 send_insn_id : InsnId ,
2152- ) -> Result < ( ) , ( ) > {
2157+ ) -> Result < ( ) , Option < * const rb_callable_method_entry_struct > > {
21532158 let Insn :: SendWithoutBlock { mut recv, cd, mut args, state, .. } = send else {
2154- return Err ( ( ) ) ;
2159+ return Err ( None ) ;
21552160 } ;
21562161
21572162 let call_info = unsafe { ( * cd) . ci } ;
@@ -2163,20 +2168,20 @@ impl Function {
21632168 ( class, None )
21642169 } else {
21652170 let iseq_insn_idx = fun. frame_state ( state) . insn_idx ;
2166- let Some ( recv_type) = fun. profiled_type_of_at ( recv, iseq_insn_idx) else { return Err ( ( ) ) } ;
2171+ let Some ( recv_type) = fun. profiled_type_of_at ( recv, iseq_insn_idx) else { return Err ( None ) } ;
21672172 ( recv_type. class ( ) , Some ( recv_type) )
21682173 } ;
21692174
21702175 // Do method lookup
2171- let method = unsafe { rb_callable_method_entry ( recv_class, method_id) } ;
2176+ let method: * const rb_callable_method_entry_struct = unsafe { rb_callable_method_entry ( recv_class, method_id) } ;
21722177 if method. is_null ( ) {
2173- return Err ( ( ) ) ;
2178+ return Err ( None ) ;
21742179 }
21752180
21762181 // Filter for C methods
21772182 let def_type = unsafe { get_cme_def_type ( method) } ;
21782183 if def_type != VM_METHOD_TYPE_CFUNC {
2179- return Err ( ( ) ) ;
2184+ return Err ( None ) ;
21802185 }
21812186
21822187 // Find the `argc` (arity) of the C method, which describes the parameters it expects
@@ -2188,15 +2193,15 @@ impl Function {
21882193 //
21892194 // Bail on argc mismatch
21902195 if argc != cfunc_argc as u32 {
2191- return Err ( ( ) ) ;
2196+ return Err ( Some ( method ) ) ;
21922197 }
21932198
21942199 // Filter for a leaf and GC free function
21952200 use crate :: cruby_methods:: FnProperties ;
21962201 let Some ( FnProperties { leaf : true , no_gc : true , return_type, elidable } ) =
21972202 ZJITState :: get_method_annotations ( ) . get_cfunc_properties ( method)
21982203 else {
2199- return Err ( ( ) ) ;
2204+ return Err ( Some ( method ) ) ;
22002205 } ;
22012206
22022207 let ci_flags = unsafe { vm_ci_flag ( call_info) } ;
@@ -2213,13 +2218,13 @@ impl Function {
22132218 cfunc_args. append ( & mut args) ;
22142219 let ccall = fun. push_insn ( block, Insn :: CCall { cfun, args : cfunc_args, name : method_id, return_type, elidable } ) ;
22152220 fun. make_equal_to ( send_insn_id, ccall) ;
2216- return Ok ( ( ) ) ;
2221+ return Ok ( ( ) )
22172222 }
22182223 }
22192224 // Variadic method
22202225 -1 => {
22212226 if unsafe { rb_zjit_method_tracing_currently_enabled ( ) } {
2222- return Err ( ( ) ) ;
2227+ return Err ( None ) ;
22232228 }
22242229 // The method gets a pointer to the first argument
22252230 // func(int argc, VALUE *argv, VALUE recv)
@@ -2251,8 +2256,9 @@ impl Function {
22512256 } ) ;
22522257
22532258 fun. make_equal_to ( send_insn_id, ccall) ;
2254- return Ok ( ( ) ) ;
2259+ return Ok ( ( ) )
22552260 }
2261+
22562262 // Fall through for complex cases (splat, kwargs, etc.)
22572263 }
22582264 -2 => {
@@ -2262,7 +2268,7 @@ impl Function {
22622268 _ => unreachable ! ( "unknown cfunc kind: argc={argc}" )
22632269 }
22642270
2265- Err ( ( ) )
2271+ Err ( Some ( method ) )
22662272 }
22672273
22682274 for block in self . rpo ( ) {
@@ -2271,8 +2277,19 @@ impl Function {
22712277 for insn_id in old_insns {
22722278 if let send @ Insn :: SendWithoutBlock { recv, .. } = self . find ( insn_id) {
22732279 let recv_type = self . type_of ( recv) ;
2274- if reduce_to_ccall ( self , block, recv_type, send, insn_id) . is_ok ( ) {
2275- continue ;
2280+ match reduce_to_ccall ( self , block, recv_type, send, insn_id) {
2281+ Ok ( ( ) ) => continue ,
2282+ Err ( Some ( cme) ) => {
2283+ let owner = unsafe { ( * cme) . owner } ;
2284+ let called_id = unsafe { ( * cme) . called_id } ;
2285+ let class_name = get_class_name ( owner) ;
2286+ let method_name = called_id. contents_lossy ( ) ;
2287+ let signature = format ! ( "{}#{}" , class_name, method_name) ;
2288+ let counter_ptr = get_or_create_unoptimized_cfunc_counter_ptr ( signature. clone ( ) ) ;
2289+
2290+ self . push_insn ( block, Insn :: CountUnoptimizedCFunc { signature, counter_ptr } ) ;
2291+ }
2292+ _ => { }
22762293 }
22772294 }
22782295 self . push_insn_id ( block, insn_id) ;
@@ -2417,7 +2434,8 @@ impl Function {
24172434 | & Insn :: LoadPC { .. }
24182435 | & Insn :: GetLocal { .. }
24192436 | & Insn :: PutSpecialObject { .. }
2420- | & Insn :: IncrCounter ( _) =>
2437+ | & Insn :: IncrCounter ( _)
2438+ | & Insn :: CountUnoptimizedCFunc { .. } =>
24212439 { }
24222440 & Insn :: PatchPoint { state, .. }
24232441 | & Insn :: CheckInterrupts { state }
@@ -9484,6 +9502,7 @@ mod opt_tests {
94849502 bb2(v6:BasicObject):
94859503 v10:Fixnum[1] = Const Value(1)
94869504 v11:Fixnum[0] = Const Value(0)
9505+ CountUnoptimizedCFunc Kernel#itself
94879506 v13:BasicObject = SendWithoutBlock v10, :itself, v11
94889507 CheckInterrupts
94899508 Return v13
@@ -10322,6 +10341,7 @@ mod opt_tests {
1032210341 Jump bb2(v4)
1032310342 bb2(v6:BasicObject):
1032410343 v11:HashExact = NewHash
10344+ CountUnoptimizedCFunc Kernel#dup
1032510345 v13:BasicObject = SendWithoutBlock v11, :dup
1032610346 v15:BasicObject = SendWithoutBlock v13, :freeze
1032710347 CheckInterrupts
@@ -10345,6 +10365,7 @@ mod opt_tests {
1034510365 bb2(v6:BasicObject):
1034610366 v11:HashExact = NewHash
1034710367 v12:NilClass = Const Value(nil)
10368+ CountUnoptimizedCFunc Hash#freeze
1034810369 v14:BasicObject = SendWithoutBlock v11, :freeze, v12
1034910370 CheckInterrupts
1035010371 Return v14
@@ -10409,6 +10430,7 @@ mod opt_tests {
1040910430 Jump bb2(v4)
1041010431 bb2(v6:BasicObject):
1041110432 v11:ArrayExact = NewArray
10433+ CountUnoptimizedCFunc Kernel#dup
1041210434 v13:BasicObject = SendWithoutBlock v11, :dup
1041310435 v15:BasicObject = SendWithoutBlock v13, :freeze
1041410436 CheckInterrupts
@@ -10432,6 +10454,7 @@ mod opt_tests {
1043210454 bb2(v6:BasicObject):
1043310455 v11:ArrayExact = NewArray
1043410456 v12:NilClass = Const Value(nil)
10457+ CountUnoptimizedCFunc Array#freeze
1043510458 v14:BasicObject = SendWithoutBlock v11, :freeze, v12
1043610459 CheckInterrupts
1043710460 Return v14
@@ -10497,6 +10520,7 @@ mod opt_tests {
1049710520 bb2(v6:BasicObject):
1049810521 v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1049910522 v12:StringExact = StringCopy v10
10523+ CountUnoptimizedCFunc String#dup
1050010524 v14:BasicObject = SendWithoutBlock v12, :dup
1050110525 v16:BasicObject = SendWithoutBlock v14, :freeze
1050210526 CheckInterrupts
@@ -10521,6 +10545,7 @@ mod opt_tests {
1052110545 v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1052210546 v12:StringExact = StringCopy v10
1052310547 v13:NilClass = Const Value(nil)
10548+ CountUnoptimizedCFunc String#freeze
1052410549 v15:BasicObject = SendWithoutBlock v12, :freeze, v13
1052510550 CheckInterrupts
1052610551 Return v15
@@ -10586,6 +10611,7 @@ mod opt_tests {
1058610611 bb2(v6:BasicObject):
1058710612 v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1058810613 v12:StringExact = StringCopy v10
10614+ CountUnoptimizedCFunc String#dup
1058910615 v14:BasicObject = SendWithoutBlock v12, :dup
1059010616 v16:BasicObject = SendWithoutBlock v14, :-@
1059110617 CheckInterrupts
@@ -10715,6 +10741,7 @@ mod opt_tests {
1071510741 bb2(v8:BasicObject, v9:BasicObject):
1071610742 v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
1071710743 v25:BasicObject = GuardTypeNot v9, String
10744+ CountUnoptimizedCFunc Array#to_s
1071810745 v26:BasicObject = SendWithoutBlock v9, :to_s
1071910746 v17:String = AnyToString v9, str: v26
1072010747 v19:StringExact = StringConcat v13, v17
0 commit comments