@@ -257,7 +257,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
257257 Insn :: Jump ( branch) => return gen_jump ( jit, asm, branch) ,
258258 Insn :: IfTrue { val, target } => return gen_if_true ( jit, asm, opnd ! ( val) , target) ,
259259 Insn :: IfFalse { val, target } => return gen_if_false ( jit, asm, opnd ! ( val) , target) ,
260- Insn :: SendWithoutBlock { call_info, cd, state, .. } => gen_send_without_block ( jit, asm, call_info, * cd, & function. frame_state ( * state) ) ?,
260+ Insn :: SendWithoutBlock { call_info, cd, state, self_val , args , .. } => gen_send_without_block ( jit, asm, call_info, * cd, & function. frame_state ( * state) , self_val , args ) ?,
261261 Insn :: SendWithoutBlockDirect { iseq, self_val, args, .. } => gen_send_without_block_direct ( cb, jit, asm, * iseq, opnd ! ( self_val) , args) ?,
262262 Insn :: Return { val } => return Some ( gen_return ( asm, opnd ! ( val) ) ?) ,
263263 Insn :: FixnumAdd { left, right, state } => gen_fixnum_add ( asm, opnd ! ( left) , opnd ! ( right) , & function. frame_state ( * state) ) ?,
@@ -443,18 +443,20 @@ fn gen_send_without_block(
443443 call_info : & CallInfo ,
444444 cd : * const rb_call_data ,
445445 state : & FrameState ,
446+ self_val : & InsnId ,
447+ args : & Vec < InsnId > ,
446448) -> Option < lir:: Opnd > {
447- // Spill the virtual stack onto the stack. They need to be marked by GC and may be caller-saved registers.
449+ // Spill the receiver and the arguments onto the stack. They need to be marked by GC and may be caller-saved registers.
448450 // TODO: Avoid spilling operands that have been spilled before.
449- for ( idx, & insn_id) in state . stack ( ) . enumerate ( ) {
451+ for ( idx, & insn_id) in [ * self_val ] . iter ( ) . chain ( args . iter ( ) ) . enumerate ( ) {
450452 // Currently, we don't move the SP register. So it's equal to the base pointer.
451453 let stack_opnd = Opnd :: mem ( 64 , SP , idx as i32 * SIZEOF_VALUE_I32 ) ;
452454 asm. mov ( stack_opnd, jit. get_opnd ( insn_id) ?) ;
453455 }
454456
455457 // Save PC and SP
456458 gen_save_pc ( asm, state) ;
457- gen_save_sp ( asm, state ) ;
459+ gen_save_sp ( asm, 1 + args . len ( ) ) ; // +1 for receiver
458460
459461 asm_comment ! ( asm, "call #{} with dynamic dispatch" , call_info. method_name) ;
460462 unsafe extern "C" {
@@ -678,13 +680,13 @@ fn gen_save_pc(asm: &mut Assembler, state: &FrameState) {
678680}
679681
680682/// Save the current SP on the CFP
681- fn gen_save_sp ( asm : & mut Assembler , state : & FrameState ) {
683+ fn gen_save_sp ( asm : & mut Assembler , stack_size : usize ) {
682684 // Update cfp->sp which will be read by the interpreter. We also have the SP register in JIT
683685 // code, and ZJIT's codegen currently assumes the SP register doesn't move, e.g. gen_param().
684686 // So we don't update the SP register here. We could update the SP register to avoid using
685687 // an extra register for asm.lea(), but you'll need to manage the SP offset like YJIT does.
686- asm_comment ! ( asm, "save SP to CFP: {}" , state . stack_size( ) ) ;
687- let sp_addr = asm. lea ( Opnd :: mem ( 64 , SP , state . stack_size ( ) as i32 * SIZEOF_VALUE_I32 ) ) ;
688+ asm_comment ! ( asm, "save SP to CFP: {}" , stack_size) ;
689+ let sp_addr = asm. lea ( Opnd :: mem ( 64 , SP , stack_size as i32 * SIZEOF_VALUE_I32 ) ) ;
688690 let cfp_sp = Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_SP ) ;
689691 asm. mov ( cfp_sp, sp_addr) ;
690692}
0 commit comments