@@ -617,6 +617,16 @@ pub enum Insn {
617617 /// `name` is for printing purposes only
618618 CCall { cfun : * const u8 , args : Vec < InsnId > , name : ID , return_type : Type , elidable : bool } ,
619619
620+ /// Call a variadic C function with signature: func(int argc, VALUE *argv, VALUE recv)
621+ /// This handles frame setup, argv creation, and frame teardown all in one
622+ CCallVariadic {
623+ cfun : * const u8 ,
624+ recv : InsnId ,
625+ args : Vec < InsnId > ,
626+ name : ID ,
627+ state : InsnId ,
628+ } ,
629+
620630 /// Un-optimized fallback implementation (dynamic dispatch) for send-ish instructions
621631 /// Ignoring keyword arguments etc for now
622632 SendWithoutBlock {
@@ -755,6 +765,7 @@ impl Insn {
755765 Insn :: LoadIvarEmbedded { .. } => false ,
756766 Insn :: LoadIvarExtended { .. } => false ,
757767 Insn :: CCall { elidable, .. } => !elidable,
768+ Insn :: CCallVariadic { .. } => true ,
758769 // TODO: NewRange is effects free if we can prove the two ends to be Fixnum,
759770 // but we don't have type information here in `impl Insn`. See rb_range_new().
760771 Insn :: NewRange { .. } => true ,
@@ -930,6 +941,13 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
930941 }
931942 Ok ( ( ) )
932943 } ,
944+ Insn :: CCallVariadic { cfun, recv, args, name, state : _ } => {
945+ write ! ( f, "CCallVariadic {}@{:p}, {recv}" , name. contents_lossy( ) , self . ptr_map. map_ptr( cfun) ) ?;
946+ for arg in args {
947+ write ! ( f, ", {arg}" ) ?;
948+ }
949+ Ok ( ( ) )
950+ } ,
933951 Insn :: Snapshot { state } => write ! ( f, "Snapshot {}" , state. print( self . ptr_map) ) ,
934952 Insn :: Defined { op_type, v, .. } => {
935953 // op_type (enum defined_type) printing logic from iseq.c.
@@ -1411,6 +1429,9 @@ impl Function {
14111429 & HashDup { val, state } => HashDup { val : find ! ( val) , state } ,
14121430 & ObjectAlloc { val, state } => ObjectAlloc { val : find ! ( val) , state } ,
14131431 & CCall { cfun, ref args, name, return_type, elidable } => CCall { cfun, args : find_vec ! ( args) , name, return_type, elidable } ,
1432+ & CCallVariadic { cfun, recv, ref args, name, state } => CCallVariadic {
1433+ cfun, recv : find ! ( recv) , args : find_vec ! ( args) , name, state
1434+ } ,
14141435 & Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v : find ! ( v) , state : find ! ( state) } ,
14151436 & DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val : find ! ( self_val) , pushval, id, state } ,
14161437 & 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) ) ,
@@ -2030,9 +2052,47 @@ impl Function {
20302052 return Ok ( ( ) ) ;
20312053 }
20322054 }
2055+ // Variadic method
20332056 -1 => {
2034- // (argc, argv, self) parameter form
2035- // Falling through for now
2057+ // The method gets a pointer to the first argument
2058+ // rb_f_puts(int argc, VALUE *argv, VALUE recv)
2059+ let ci_flags = unsafe { vm_ci_flag ( call_info) } ;
2060+ if ci_flags & VM_CALL_ARGS_SIMPLE != 0 {
2061+ // We need profiled type information for variadic calls
2062+ // to ensure we're calling the right method
2063+ if profiled_type. is_none ( ) {
2064+ return Err ( ( ) ) ;
2065+ }
2066+
2067+ // Add method redefinition guard
2068+ fun. push_insn ( block, Insn :: PatchPoint {
2069+ invariant : Invariant :: MethodRedefined {
2070+ klass : recv_class,
2071+ method : method_id,
2072+ cme : method
2073+ } ,
2074+ state
2075+ } ) ;
2076+
2077+ fun. push_insn ( block, Insn :: GuardType {
2078+ val : recv,
2079+ guard_type : Type :: from_profiled_type ( profiled_type. unwrap ( ) ) ,
2080+ state
2081+ } ) ;
2082+
2083+ let cfun = unsafe { get_mct_func ( cfunc) } . cast ( ) ;
2084+ let ccall = fun. push_insn ( block, Insn :: CCallVariadic {
2085+ cfun,
2086+ recv,
2087+ args,
2088+ name : method_id,
2089+ state,
2090+ } ) ;
2091+
2092+ fun. make_equal_to ( send_insn_id, ccall) ;
2093+ return Ok ( ( ) ) ;
2094+ }
2095+ // Fall through for complex cases (splat, kwargs, etc.)
20362096 }
20372097 -2 => {
20382098 // (self, args_ruby_array) parameter form
@@ -2345,6 +2405,11 @@ impl Function {
23452405 worklist. push_back ( state)
23462406 }
23472407 Insn :: CCall { args, .. } => worklist. extend ( args) ,
2408+ Insn :: CCallVariadic { recv, args, state, .. } => {
2409+ worklist. push_back ( * recv) ;
2410+ worklist. extend ( args) ;
2411+ worklist. push_back ( * state) ;
2412+ }
23482413 & Insn :: GetIvar { self_val, state, .. } | & Insn :: DefinedIvar { self_val, state, .. } => {
23492414 worklist. push_back ( self_val) ;
23502415 worklist. push_back ( state) ;
0 commit comments