@@ -4287,8 +4287,89 @@ def jit_call_iseq(jit, ctx, asm, cme, calling, iseq, frame_type: nil, prev_ep: n
42874287 end
42884288
42894289 if iseq_has_rest
4290- asm . incr_counter ( :send_iseq_has_rest )
4291- return CantCompile
4290+ # We are going to allocate so setting pc and sp.
4291+ jit_save_pc ( jit , asm ) # clobbers rax
4292+ jit_save_sp ( ctx , asm )
4293+
4294+ if flags & C ::VM_CALL_ARGS_SPLAT != 0
4295+ non_rest_arg_count = argc - 1
4296+ # We start by dupping the array because someone else might have
4297+ # a reference to it.
4298+ array = ctx . stack_pop ( 1 )
4299+ asm . mov ( C_ARGS [ 0 ] , array )
4300+ asm . call ( C . rb_ary_dup )
4301+ array = C_RET
4302+ if non_rest_arg_count > required_num
4303+ # If we have more arguments than required, we need to prepend
4304+ # the items from the stack onto the array.
4305+ diff = ( non_rest_arg_count - required_num )
4306+
4307+ # diff is >0 so no need to worry about null pointer
4308+ asm . comment ( 'load pointer to array elements' )
4309+ offset_magnitude = C . VALUE . size * diff
4310+ values_opnd = ctx . sp_opnd ( -offset_magnitude )
4311+ values_ptr = :rcx
4312+ asm . lea ( values_ptr , values_opnd )
4313+
4314+ asm . comment ( 'prepend stack values to rest array' )
4315+ asm . mov ( C_ARGS [ 0 ] , diff )
4316+ asm . mov ( C_ARGS [ 1 ] , values_ptr )
4317+ asm . mov ( C_ARGS [ 2 ] , array )
4318+ asm . call ( C . rb_yjit_rb_ary_unshift_m )
4319+ ctx . stack_pop ( diff )
4320+
4321+ stack_ret = ctx . stack_push
4322+ asm . mov ( stack_ret , C_RET )
4323+ # We now should have the required arguments
4324+ # and an array of all the rest arguments
4325+ argc = required_num + 1
4326+ elsif non_rest_arg_count < required_num
4327+ # If we have fewer arguments than required, we need to take some
4328+ # from the array and move them to the stack.
4329+ diff = ( required_num - non_rest_arg_count )
4330+ # This moves the arguments onto the stack. But it doesn't modify the array.
4331+ move_rest_args_to_stack ( array , diff , jit , ctx , asm )
4332+
4333+ # We will now slice the array to give us a new array of the correct size
4334+ asm . mov ( C_ARGS [ 0 ] , array )
4335+ asm . mov ( C_ARGS [ 1 ] , diff )
4336+ asm . call ( C . rjit_rb_ary_subseq_length )
4337+ stack_ret = ctx . stack_push
4338+ asm . mov ( stack_ret , C_RET )
4339+
4340+ # We now should have the required arguments
4341+ # and an array of all the rest arguments
4342+ argc = required_num + 1
4343+ else
4344+ # The arguments are equal so we can just push to the stack
4345+ assert_equal ( non_rest_arg_count , required_num )
4346+ stack_ret = ctx . stack_push
4347+ asm . mov ( stack_ret , array )
4348+ end
4349+ else
4350+ assert_equal ( true , argc >= required_num )
4351+ n = ( argc - required_num )
4352+ argc = required_num + 1
4353+ # If n is 0, then elts is never going to be read, so we can just pass null
4354+ if n == 0
4355+ values_ptr = 0
4356+ else
4357+ asm . comment ( 'load pointer to array elements' )
4358+ offset_magnitude = C . VALUE . size * n
4359+ values_opnd = ctx . sp_opnd ( -offset_magnitude )
4360+ values_ptr = :rcx
4361+ asm . lea ( values_ptr , values_opnd )
4362+ end
4363+
4364+ asm . mov ( C_ARGS [ 0 ] , EC )
4365+ asm . mov ( C_ARGS [ 1 ] , n )
4366+ asm . mov ( C_ARGS [ 2 ] , values_ptr )
4367+ asm . call ( C . rb_ec_ary_new_from_values )
4368+
4369+ ctx . stack_pop ( n )
4370+ stack_ret = ctx . stack_push
4371+ asm . mov ( stack_ret , C_RET )
4372+ end
42924373 end
42934374
42944375 if doing_kw_call
@@ -5017,6 +5098,49 @@ def jit_caller_setup_arg(jit, ctx, asm, flags, splat: false)
50175098 end
50185099 end
50195100
5101+ # Pushes arguments from an array to the stack. Differs from push splat because
5102+ # the array can have items left over.
5103+ # @param jit [RubyVM::RJIT::JITState]
5104+ # @param ctx [RubyVM::RJIT::Context]
5105+ # @param asm [RubyVM::RJIT::Assembler]
5106+ def move_rest_args_to_stack ( array , num_args , jit , ctx , asm )
5107+ side_exit = side_exit ( jit , ctx )
5108+
5109+ asm . comment ( 'move_rest_args_to_stack' )
5110+
5111+ # array is :rax
5112+ array_len_opnd = :rcx
5113+ jit_array_len ( asm , array , array_len_opnd )
5114+
5115+ asm . comment ( 'Side exit if length is less than required' )
5116+ asm . cmp ( array_len_opnd , num_args )
5117+ asm . jl ( counted_exit ( side_exit , :send_iseq_has_rest_and_splat_not_equal ) )
5118+
5119+ asm . comment ( 'Push arguments from array' )
5120+
5121+ # Load the address of the embedded array
5122+ # (struct RArray *)(obj)->as.ary
5123+ array_reg = array
5124+
5125+ # Conditionally load the address of the heap array
5126+ # (struct RArray *)(obj)->as.heap.ptr
5127+ flags_opnd = [ array_reg , C . RBasic . offsetof ( :flags ) ]
5128+ asm . test ( flags_opnd , C ::RARRAY_EMBED_FLAG )
5129+ heap_ptr_opnd = [ array_reg , C . RArray . offsetof ( :as , :heap , :ptr ) ]
5130+ # Load the address of the embedded array
5131+ # (struct RArray *)(obj)->as.ary
5132+ ary_opnd = :rdx # NOTE: array :rax is used after move_rest_args_to_stack too
5133+ asm . lea ( :rcx , [ array_reg , C . RArray . offsetof ( :as , :ary ) ] )
5134+ asm . mov ( ary_opnd , heap_ptr_opnd )
5135+ asm . cmovnz ( ary_opnd , :rcx )
5136+
5137+ num_args . times do |i |
5138+ top = ctx . stack_push
5139+ asm . mov ( :rcx , [ ary_opnd , i * C . VALUE . size ] )
5140+ asm . mov ( top , :rcx )
5141+ end
5142+ end
5143+
50205144 # vm_caller_setup_arg_splat (+ CALLER_SETUP_ARG):
50215145 # Pushes arguments from an array to the stack that are passed with a splat (i.e. *args).
50225146 # It optimistically compiles to a static size that is the exact number of arguments needed for the function.
0 commit comments