@@ -581,6 +581,17 @@ pub enum Insn {
581581 /// `name` is for printing purposes only
582582 CCall { cfun : * const u8 , args : Vec < InsnId > , name : ID , return_type : Type , elidable : bool } ,
583583
584+ /// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv)
585+ /// This handles frame setup, argv creation, and frame teardown all in one
586+ CCallVariadic {
587+ cfun : * const u8 ,
588+ recv : InsnId ,
589+ args : Vec < InsnId > ,
590+ cme : * const rb_callable_method_entry_t ,
591+ name : ID ,
592+ state : InsnId ,
593+ } ,
594+
584595 /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions
585596 /// Ignoring keyword arguments etc for now
586597 SendWithoutBlock { recv : InsnId , cd : * const rb_call_data , args : Vec < InsnId > , state : InsnId } ,
@@ -713,6 +724,7 @@ impl Insn {
713724 Insn :: LoadIvarEmbedded { .. } => false ,
714725 Insn :: LoadIvarExtended { .. } => false ,
715726 Insn :: CCall { elidable, .. } => !elidable,
727+ Insn :: CCallVariadic { .. } => true ,
716728 // TODO: NewRange is effects free if we can prove the two ends to be Fixnum,
717729 // but we don't have type information here in `impl Insn`. See rb_range_new().
718730 Insn :: NewRange { .. } => true ,
@@ -888,6 +900,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
888900 }
889901 Ok ( ( ) )
890902 } ,
903+ Insn :: CCallVariadic { cfun, recv, args, name, .. } => {
904+ write ! ( f, "CCallVariadic {}@{:p}, {recv}" , name. contents_lossy( ) , self . ptr_map. map_ptr( cfun) ) ?;
905+ for arg in args {
906+ write ! ( f, ", {arg}" ) ?;
907+ }
908+ Ok ( ( ) )
909+ } ,
891910 Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state. print( self . ptr_map) ) ,
892911 Insn :: Defined { op_type, v, .. } => {
893912 // op_type (enum defined_type) printing logic from iseq.c.
@@ -1368,6 +1387,9 @@ impl Function {
13681387 & HashDup { val, state } => HashDup { val : find ! ( val) , state } ,
13691388 & ObjectAlloc { val, state } => ObjectAlloc { val : find ! ( val) , state } ,
13701389 & CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun, args : find_vec ! ( args) , name, return_type, elidable } ,
1390+ & CCallVariadic { cfun, recv, ref args, cme, name, state } => CCallVariadic {
1391+ cfun, recv : find ! ( recv) , args : find_vec ! ( args) , cme, name, state
1392+ } ,
13711393 & Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v : find ! ( v) , state : find ! ( state) } ,
13721394 & DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val : find ! ( self_val) , pushval, id, state } ,
13731395 & NewArray { ref elements, state } => NewArray { elements : find_vec ! ( elements) , state : find ! ( state) } ,
@@ -1455,6 +1477,7 @@ impl Function {
14551477 Insn :: NewRangeFixnum { .. } => types:: RangeExact ,
14561478 Insn :: ObjectAlloc { .. } => types:: HeapObject ,
14571479 Insn :: CCall { return_type, .. } => * return_type,
1480+ Insn :: CCallVariadic { .. } => types:: BasicObject ,
14581481 Insn :: GuardType { val, guard_type, .. } => self . type_of ( * val) . intersection ( * guard_type) ,
14591482 Insn :: GuardTypeNot { .. } => types:: BasicObject ,
14601483 Insn :: GuardBitEquals { val, expected, .. } => self . type_of ( * val) . intersection ( Type :: from_value ( * expected) ) ,
@@ -1982,9 +2005,42 @@ impl Function {
19822005 return Ok ( ( ) ) ;
19832006 }
19842007 }
2008+ // Variadic method
19852009 -1 => {
1986- // (argc, argv, self) parameter form
1987- // Falling through for now
2010+ // The method gets a pointer to the first argument
2011+ // rb_f_puts(int argc, VALUE *argv, VALUE recv)
2012+ let ci_flags = unsafe { vm_ci_flag ( call_info) } ;
2013+ if ci_flags & VM_CALL_ARGS_SIMPLE != 0 {
2014+ // We need profiled type information for variadic calls
2015+ // to ensure we're calling the right method
2016+ if profiled_type. is_none ( ) {
2017+ return Err ( ( ) ) ;
2018+ }
2019+
2020+ // Add method redefinition guard
2021+ fun. push_insn ( block, Insn :: PatchPoint {
2022+ invariant : Invariant :: MethodRedefined {
2023+ klass : recv_class,
2024+ method : method_id,
2025+ cme : method
2026+ } ,
2027+ state
2028+ } ) ;
2029+
2030+ let cfun = unsafe { get_mct_func ( cfunc) } . cast ( ) ;
2031+ let ccall = fun. push_insn ( block, Insn :: CCallVariadic {
2032+ cfun,
2033+ recv,
2034+ args,
2035+ cme : method,
2036+ name : method_id,
2037+ state,
2038+ } ) ;
2039+
2040+ fun. make_equal_to ( send_insn_id, ccall) ;
2041+ return Ok ( ( ) ) ;
2042+ }
2043+ // Fall through for complex cases (splat, kwargs, etc.)
19882044 }
19892045 -2 => {
19902046 // (self, args_ruby_array) parameter form
@@ -2297,6 +2353,11 @@ impl Function {
22972353 worklist. push_back ( state)
22982354 }
22992355 Insn :: CCall { args, .. } => worklist. extend ( args) ,
2356+ Insn :: CCallVariadic { recv, args, state, .. } => {
2357+ worklist. push_back ( * recv) ;
2358+ worklist. extend ( args) ;
2359+ worklist. push_back ( * state) ;
2360+ }
23002361 & Insn :: GetIvar { self_val, state, .. } | & Insn :: DefinedIvar { self_val, state, .. } => {
23012362 worklist. push_back ( self_val) ;
23022363 worklist. push_back ( state) ;
@@ -6797,6 +6858,26 @@ mod opt_tests {
67976858 " ) ;
67986859 }
67996860
6861+ #[ test]
6862+ fn test_optimize_variadic_ccall ( ) {
6863+ eval ( "
6864+ def test
6865+ puts 'Hello'
6866+ end
6867+ test; test
6868+ " ) ;
6869+ assert_snapshot ! ( hir_string( "test" ) , @r"
6870+ fn test@<compiled>:3:
6871+ bb0(v0:BasicObject):
6872+ v4:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
6873+ v6:StringExact = StringCopy v4
6874+ PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018)
6875+ v15:BasicObject = CCallVariadic puts@0x1040, v0, v6
6876+ CheckInterrupts
6877+ Return v15
6878+ " ) ;
6879+ }
6880+
68006881 #[ test]
68016882 fn test_dont_optimize_fixnum_add_if_redefined ( ) {
68026883 eval ( "
0 commit comments