@@ -367,7 +367,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
367367 Insn :: AnyToString { val, str, state } => gen_anytostring ( asm, opnd ! ( val) , opnd ! ( str ) , & function. frame_state ( * state) ) ?,
368368 Insn :: Defined { op_type, obj, pushval, v } => gen_defined ( jit, asm, * op_type, * obj, * pushval, opnd ! ( v) ) ?,
369369 & Insn :: IncrCounter ( counter) => return Some ( gen_incr_counter ( asm, counter) ) ,
370- Insn :: ArrayExtend { .. }
370+ Insn :: ToArray { val, state } => gen_to_array ( jit, asm, opnd ! ( val) , & function. frame_state ( * state) ) ?,
371+ Insn :: ArrayExtend { left, right, state } => return gen_array_extend ( jit, asm, opnd ! ( left) , opnd ! ( right) , & function. frame_state ( * state) ) ,
371372 | Insn :: ArrayMax { .. }
372373 | Insn :: ArrayPush { .. }
373374 | Insn :: DefinedIvar { .. }
@@ -379,7 +380,6 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
379380 | Insn :: Send { .. }
380381 | Insn :: StringIntern { .. }
381382 | Insn :: Throw { .. }
382- | Insn :: ToArray { .. }
383383 | Insn :: ToNewArray { .. }
384384 | Insn :: Const { .. }
385385 => {
@@ -768,12 +768,9 @@ fn gen_send_without_block(
768768 self_val : Opnd ,
769769 args : Vec < Opnd > ,
770770) -> Option < lir:: Opnd > {
771- // Spill locals onto the stack.
772- // TODO: Don't spill locals eagerly; lazily reify frames
773- asm_comment ! ( asm, "spill locals" ) ;
774- for ( idx, & insn_id) in state. locals ( ) . enumerate ( ) {
775- asm. mov ( Opnd :: mem ( 64 , SP , ( -local_idx_to_ep_offset ( jit. iseq , idx) - 1 ) * SIZEOF_VALUE_I32 ) , jit. get_opnd ( insn_id) ?) ;
776- }
771+
772+ spill_locals ( jit, asm, state) ;
773+
777774 // Spill the receiver and the arguments onto the stack.
778775 // They need to be on the interpreter stack to let the interpreter access them.
779776 // TODO: Avoid spilling operands that have been spilled before.
@@ -819,13 +816,8 @@ fn gen_send_without_block_direct(
819816
820817 // Spill the virtual stack and the locals of the caller onto the stack
821818 // TODO: Lazily materialize caller frames on side exits or when needed
822- asm_comment ! ( asm, "spill locals and stack" ) ;
823- for ( idx, & insn_id) in state. locals ( ) . enumerate ( ) {
824- asm. mov ( Opnd :: mem ( 64 , SP , ( -local_idx_to_ep_offset ( jit. iseq , idx) - 1 ) * SIZEOF_VALUE_I32 ) , jit. get_opnd ( insn_id) ?) ;
825- }
826- for ( idx, & insn_id) in state. stack ( ) . enumerate ( ) {
827- asm. mov ( Opnd :: mem ( 64 , SP , idx as i32 * SIZEOF_VALUE_I32 ) , jit. get_opnd ( insn_id) ?) ;
828- }
819+ spill_locals ( jit, asm, state) ;
820+ spill_stack ( jit, asm, state) ;
829821
830822 // Set up the new frame
831823 // TODO: Lazily materialize caller frames on side exits or when needed
@@ -1042,6 +1034,38 @@ fn gen_anytostring(asm: &mut Assembler, val: lir::Opnd, str: lir::Opnd, state: &
10421034 Some ( asm_ccall ! ( asm, rb_obj_as_string_result, str , val) )
10431035}
10441036
1037+ fn gen_to_array ( jit : & mut JITState , asm : & mut Assembler , val : lir:: Opnd , state : & FrameState ) -> Option < lir:: Opnd > {
1038+ // Save PC
1039+ gen_save_pc ( asm, state) ;
1040+
1041+ // Save SP, dump locals and stack as rb_Array can call user-defined `to_a` method
1042+ gen_save_sp ( asm, state. stack ( ) . len ( ) ) ;
1043+ spill_locals ( jit, asm, state) ;
1044+ spill_stack ( jit, asm, state) ;
1045+
1046+ // ToArray is used in both concattoarray and splatarray instructions
1047+ // It needs to satisfy the following conditions:
1048+ // - Check if the value is already an array. If it is, return it
1049+ // - Check if the value can be converted into an array. If it is, return it
1050+ // - Return an array with just the value in it
1051+ //
1052+ // rb_Array satisfies the above conditions, so we can use it here
1053+ Some ( asm_ccall ! ( asm, rb_Array, val) )
1054+ }
1055+
1056+ fn gen_array_extend ( jit : & mut JITState , asm : & mut Assembler , left : lir:: Opnd , right : lir:: Opnd , state : & FrameState ) -> Option < ( ) > {
1057+ // Save PC
1058+ gen_save_pc ( asm, state) ;
1059+
1060+ // Save SP, dump locals and stack as rb_ary_concat calls `rb_to_array_type`, which can call user-defined `to_ary` method
1061+ gen_save_sp ( asm, state. stack ( ) . len ( ) ) ;
1062+ spill_locals ( jit, asm, state) ;
1063+ spill_stack ( jit, asm, state) ;
1064+
1065+ asm_ccall ! ( asm, rb_ary_concat, left, right) ;
1066+ Some ( ( ) )
1067+ }
1068+
10451069/// Evaluate if a value is truthy
10461070/// Produces a CBool type (0 or 1)
10471071/// In Ruby, only nil and false are falsy
@@ -1263,6 +1287,28 @@ fn max_num_params(function: &Function) -> usize {
12631287 } ) . max ( ) . unwrap_or ( 0 )
12641288}
12651289
1290+ /// Spill locals onto the stack.
1291+ /// TODO: Don't spill locals eagerly; lazily reify frames
1292+ fn spill_locals ( jit : & mut JITState , asm : & mut Assembler , state : & FrameState ) -> Option < ( ) > {
1293+ asm_comment ! ( asm, "spill locals" ) ;
1294+ for ( idx, & insn_id) in state. locals ( ) . enumerate ( ) {
1295+ let local_mem = Opnd :: mem ( 64 , SP , ( -local_idx_to_ep_offset ( jit. iseq , idx) - 1 ) * SIZEOF_VALUE_I32 ) ;
1296+ asm. mov ( local_mem, jit. get_opnd ( insn_id) ?) ;
1297+ }
1298+ Some ( ( ) )
1299+ }
1300+
1301+ /// Spill stack onto the stack.
1302+ /// TODO: Don't spill stack eagerly; lazily reify frames
1303+ fn spill_stack ( jit : & mut JITState , asm : & mut Assembler , state : & FrameState ) -> Option < ( ) > {
1304+ asm_comment ! ( asm, "spill stack" ) ;
1305+ for ( idx, & insn_id) in state. stack ( ) . enumerate ( ) {
1306+ let stack_mem = Opnd :: mem ( 64 , SP , idx as i32 * SIZEOF_VALUE_I32 ) ;
1307+ asm. mov ( stack_mem, jit. get_opnd ( insn_id) ?) ;
1308+ }
1309+ Some ( ( ) )
1310+ }
1311+
12661312#[ cfg( target_arch = "x86_64" ) ]
12671313macro_rules! c_callable {
12681314 ( $( #[ $outer: meta] ) *
0 commit comments