@@ -614,6 +614,17 @@ pub enum Insn {
614614 /// `name` is for printing purposes only
615615 CCall { cfun : * const u8 , args : Vec < InsnId > , name : ID , return_type : Type , elidable : bool } ,
616616
617+ /// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv)
618+ /// This handles frame setup, argv creation, and frame teardown all in one
619+ CCallVariadic {
620+ cfun : * const u8 ,
621+ recv : InsnId ,
622+ args : Vec < InsnId > ,
623+ cme : * const rb_callable_method_entry_t ,
624+ name : ID ,
625+ state : InsnId ,
626+ } ,
627+
617628 /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions
618629 /// Ignoring keyword arguments etc for now
619630 SendWithoutBlock {
@@ -931,6 +942,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
931942 }
932943 Ok ( ( ) )
933944 } ,
945+ Insn :: CCallVariadic { cfun, recv, args, name, .. } => {
946+ write ! ( f, "CCallVariadic {}@{:p}, {recv}" , name. contents_lossy( ) , self . ptr_map. map_ptr( cfun) ) ?;
947+ for arg in args {
948+ write ! ( f, ", {arg}" ) ?;
949+ }
950+ Ok ( ( ) )
951+ } ,
934952 Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state. print( self . ptr_map) ) ,
935953 Insn :: Defined { op_type, v, .. } => {
936954 // op_type (enum defined_type) printing logic from iseq.c.
@@ -1412,6 +1430,9 @@ impl Function {
14121430 & HashDup { val, state } => HashDup { val : find ! ( val) , state } ,
14131431 & ObjectAlloc { val, state } => ObjectAlloc { val : find ! ( val) , state } ,
14141432 & CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun, args : find_vec ! ( args) , name, return_type, elidable } ,
1433+ & CCallVariadic { cfun, recv, ref args, cme, name, state } => CCallVariadic {
1434+ cfun, recv : find ! ( recv) , args : find_vec ! ( args) , cme, name, state
1435+ } ,
14151436 & Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v : find ! ( v) , state : find ! ( state) } ,
14161437 & DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val : find ! ( self_val) , pushval, id, state } ,
14171438 & NewArray { ref elements, state } => NewArray { elements : find_vec ! ( elements) , state : find ! ( state) } ,
@@ -1498,6 +1519,7 @@ impl Function {
14981519 Insn :: NewRangeFixnum { .. } => types:: RangeExact ,
14991520 Insn :: ObjectAlloc { .. } => types:: HeapObject ,
15001521 Insn :: CCall { return_type, .. } => * return_type,
1522+ Insn :: CCallVariadic { .. } => types:: BasicObject ,
15011523 Insn :: GuardType { val, guard_type, .. } => self . type_of ( * val) . intersection ( * guard_type) ,
15021524 Insn :: GuardTypeNot { .. } => types:: BasicObject ,
15031525 Insn :: GuardBitEquals { val, expected, .. } => self . type_of ( * val) . intersection ( Type :: from_value ( * expected) ) ,
@@ -2027,9 +2049,42 @@ impl Function {
20272049 return Ok ( ( ) ) ;
20282050 }
20292051 }
2052+ // Variadic method
20302053 -1 => {
2031- // (argc, argv, self) parameter form
2032- // Falling through for now
2054+ // The method gets a pointer to the first argument
2055+ // rb_f_puts(int argc, VALUE *argv, VALUE recv)
2056+ let ci_flags = unsafe { vm_ci_flag ( call_info) } ;
2057+ if ci_flags & VM_CALL_ARGS_SIMPLE != 0 {
2058+ // We need profiled type information for variadic calls
2059+ // to ensure we're calling the right method
2060+ if profiled_type. is_none ( ) {
2061+ return Err ( ( ) ) ;
2062+ }
2063+
2064+ // Add method redefinition guard
2065+ fun. push_insn ( block, Insn :: PatchPoint {
2066+ invariant : Invariant :: MethodRedefined {
2067+ klass : recv_class,
2068+ method : method_id,
2069+ cme : method
2070+ } ,
2071+ state
2072+ } ) ;
2073+
2074+ let cfun = unsafe { get_mct_func ( cfunc) } . cast ( ) ;
2075+ let ccall = fun. push_insn ( block, Insn :: CCallVariadic {
2076+ cfun,
2077+ recv,
2078+ args,
2079+ cme : method,
2080+ name : method_id,
2081+ state,
2082+ } ) ;
2083+
2084+ fun. make_equal_to ( send_insn_id, ccall) ;
2085+ return Ok ( ( ) ) ;
2086+ }
2087+ // Fall through for complex cases (splat, kwargs, etc.)
20332088 }
20342089 -2 => {
20352090 // (self, args_ruby_array) parameter form
@@ -2342,6 +2397,11 @@ impl Function {
23422397 worklist. push_back ( state)
23432398 }
23442399 Insn :: CCall { args, .. } => worklist. extend ( args) ,
2400+ Insn :: CCallVariadic { recv, args, state, .. } => {
2401+ worklist. push_back ( * recv) ;
2402+ worklist. extend ( args) ;
2403+ worklist. push_back ( * state) ;
2404+ }
23452405 & Insn :: GetIvar { self_val, state, .. } | & Insn :: DefinedIvar { self_val, state, .. } => {
23462406 worklist. push_back ( self_val) ;
23472407 worklist. push_back ( state) ;
@@ -6845,6 +6905,26 @@ mod opt_tests {
68456905 " ) ;
68466906 }
68476907
6908+ #[ test]
6909+ fn test_optimize_variadic_ccall ( ) {
6910+ eval ( "
6911+ def test
6912+ puts 'Hello'
6913+ end
6914+ test; test
6915+ " ) ;
6916+ assert_snapshot ! ( hir_string( "test" ) , @r"
6917+ fn test@<compiled>:3:
6918+ bb0(v0:BasicObject):
6919+ v4:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000))
6920+ v6:StringExact = StringCopy v4
6921+ PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018)
6922+ v15:BasicObject = CCallVariadic puts@0x1040, v0, v6
6923+ CheckInterrupts
6924+ Return v15
6925+ " ) ;
6926+ }
6927+
68486928 #[ test]
68496929 fn test_dont_optimize_fixnum_add_if_redefined ( ) {
68506930 eval ( "
0 commit comments