@@ -612,7 +612,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
612612 & Insn :: Send { cd, blockiseq : None , state, reason, .. } => gen_send_without_block ( jit, asm, cd, & function. frame_state ( state) , reason) ,
613613 & Insn :: Send { cd, blockiseq : Some ( blockiseq) , state, reason, .. } => gen_send ( jit, asm, cd, blockiseq, & function. frame_state ( state) , reason) ,
614614 & Insn :: SendForward { cd, blockiseq, state, reason, .. } => gen_send_forward ( jit, asm, cd, blockiseq, & function. frame_state ( state) , reason) ,
615- Insn :: SendDirect { cme, iseq, recv, args, kw_bits, blockiseq, state, .. } => gen_send_iseq_direct ( cb, jit, asm, * cme, * iseq, opnd ! ( recv) , opnds ! ( args) , * kw_bits, & function. frame_state ( * state) , * blockiseq) ,
615+ & Insn :: SendDirect { cme, iseq, recv, ref args, kw_bits, blockiseq, state, .. } => gen_send_iseq_direct (
616+ cb, jit, asm, cme, iseq, opnd ! ( recv) , opnds ! ( args) ,
617+ kw_bits, & function. frame_state ( state) , blockiseq,
618+ ) . ok_or ( state) ?,
616619 & Insn :: InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper ( jit, asm, cd, blockiseq, & function. frame_state ( state) , reason) ,
617620 & Insn :: InvokeSuperForward { cd, blockiseq, state, reason, .. } => gen_invokesuperforward ( jit, asm, cd, blockiseq, & function. frame_state ( state) , reason) ,
618621 & Insn :: InvokeBlock { cd, state, reason, .. } => gen_invokeblock ( jit, asm, cd, & function. frame_state ( state) , reason) ,
@@ -1535,7 +1538,7 @@ fn gen_send_iseq_direct(
15351538 kw_bits: u32,
15361539 state: & FrameState ,
15371540 blockiseq: Option <IseqPtr >,
1538- ) -> lir:: Opnd {
1541+ ) -> Option < lir:: Opnd > {
15391542 gen_incr_counter ( asm, Counter :: iseq_optimized_send_count) ;
15401543
15411544 let local_size = unsafe { get_iseq_body_local_table_size ( iseq) } . to_usize ( ) ;
@@ -1651,7 +1654,7 @@ fn gen_send_iseq_direct(
16511654 } ;
16521655
16531656 // Make a method call. The target address will be rewritten once compiled.
1654- let iseq_call = IseqCall :: new ( iseq, num_optionals_passed) ;
1657+ let iseq_call = IseqCall :: new ( iseq, num_optionals_passed. try_into ( ) . ok ( ) ? , args . len ( ) . try_into ( ) . ok ( ) ? ) ;
16551658 let dummy_ptr = cb. get_write_ptr ( ) . raw_ptr ( cb) ;
16561659 jit. iseq_calls . push ( iseq_call. clone ( ) ) ;
16571660 let ret = asm. ccall_with_iseq_call ( dummy_ptr, c_args, & iseq_call) ;
@@ -1668,7 +1671,7 @@ fn gen_send_iseq_direct(
16681671 let new_sp = asm. sub ( SP , sp_offset. into ( ) ) ;
16691672 asm. mov ( SP , new_sp) ;
16701673
1671- ret
1674+ Some ( ret)
16721675}
16731676
16741677/// Compile for invokeblock
@@ -2937,22 +2940,44 @@ c_callable! {
29372940 // function_stub_hit_body() may allocate and call gc_validate_pc(), so we always set PC.
29382941 let iseq_call = unsafe { Rc :: from_raw( iseq_call_ptr as * const IseqCall ) } ;
29392942 let iseq = iseq_call. iseq. get( ) ;
2943+ let argc = iseq_call. argc;
2944+ let num_opts_filled = iseq_call. jit_entry_idx;
29402945 let entry_insn_idxs = crate :: hir:: jit_entry_insns( iseq) ;
29412946 let pc = unsafe { rb_iseq_pc_at_idx( iseq, entry_insn_idxs[ iseq_call. jit_entry_idx. to_usize( ) ] ) } ;
29422947 unsafe { rb_set_cfp_pc( cfp, pc) } ;
29432948
29442949 // JIT-to-JIT calls don't eagerly fill nils to non-parameter locals.
29452950 // If we side-exit from function_stub_hit (before JIT code runs), we need to set them here.
2946- fn prepare_for_exit( iseq: IseqPtr , cfp: CfpPtr , sp: * mut VALUE , compile_error: & CompileError ) {
2951+ fn prepare_for_exit( iseq: IseqPtr , cfp: CfpPtr , sp: * mut VALUE , argc : u16 , num_opts_filled : u16 , compile_error: & CompileError ) {
29472952 unsafe {
29482953 // Set SP which gen_push_frame() doesn't set
29492954 rb_set_cfp_sp( cfp, sp) ;
29502955
2951- // Fill nils to uninitialized (non-argument) locals
29522956 let local_size = get_iseq_body_local_table_size( iseq) . to_usize( ) ;
2953- let num_params = iseq. params( ) . size. to_usize( ) ;
2954- let base = sp. offset( -local_size_and_idx_to_bp_offset( local_size, num_params) as isize ) ;
2955- slice:: from_raw_parts_mut( base, local_size - num_params) . fill( Qnil ) ;
2957+ let params = iseq. params( ) ;
2958+ let params_size = params. size. to_usize( ) ;
2959+ let frame_base = sp. offset( -local_size_and_idx_to_bp_offset( local_size, 0 ) as isize ) ;
2960+ let locals = slice:: from_raw_parts_mut( frame_base, local_size) ;
2961+ // Fill nils to uninitialized (non-parameter) locals
2962+ locals. get_mut( params_size..) . unwrap_or_default( ) . fill( Qnil ) ;
2963+
2964+ // When there are unfilled optionals
2965+ let opt_num: usize = params. opt_num. try_into( ) . expect( "ISEQ opt_num should be non-negative" ) ;
2966+ let opts_unfilled = opt_num. saturating_sub( num_opts_filled. to_usize( ) ) ;
2967+ if opts_unfilled > 0 {
2968+ let lead_num: usize = params. lead_num. try_into( ) . expect( "ISEQ lead_num should be non-negative" ) ;
2969+ let param_locals = & mut locals[ ..params_size] ;
2970+ let args_after_opts = lead_num + num_opts_filled. to_usize( ) ..argc. to_usize( ) ;
2971+ let after_opts_param_idx = lead_num + opt_num;
2972+ // Move to the right args that are after the unfilled optionals
2973+ if let Some ( moved_by) = after_opts_param_idx. checked_sub( args_after_opts. start) {
2974+ if after_opts_param_idx + args_after_opts. len( ) <= param_locals. len( ) {
2975+ param_locals. copy_within( args_after_opts. clone( ) , after_opts_param_idx) ;
2976+ // Nil-fill unfilled optionals
2977+ param_locals. get_mut( args_after_opts. start..args_after_opts. start + moved_by) . unwrap_or_default( ) . fill( Qnil ) ;
2978+ }
2979+ }
2980+ }
29562981 }
29572982
29582983 // Increment a compile error counter for --zjit-stats
@@ -2976,7 +3001,7 @@ c_callable! {
29763001 // We'll use this Rc again, so increment the ref count decremented by from_raw.
29773002 unsafe { Rc :: increment_strong_count( iseq_call_ptr as * const IseqCall ) ; }
29783003
2979- prepare_for_exit( iseq, cfp, sp, compile_error) ;
3004+ prepare_for_exit( iseq, cfp, sp, argc , num_opts_filled , compile_error) ;
29803005 return ZJITState :: get_exit_trampoline_with_counter( ) . raw_ptr( cb) ;
29813006 }
29823007
@@ -2991,7 +3016,7 @@ c_callable! {
29913016 // We'll use this Rc again, so increment the ref count decremented by from_raw.
29923017 unsafe { Rc :: increment_strong_count( iseq_call_ptr as * const IseqCall ) ; }
29933018
2994- prepare_for_exit( iseq, cfp, sp, & compile_error) ;
3019+ prepare_for_exit( iseq, cfp, sp, argc , num_opts_filled , & compile_error) ;
29953020 ZJITState :: get_exit_trampoline_with_counter( )
29963021 } ) ;
29973022 cb. mark_all_executable( ) ;
@@ -3316,7 +3341,10 @@ pub struct IseqCall {
33163341 pub iseq : Cell < IseqPtr > ,
33173342
33183343 /// Index that corresponds to [crate::hir::jit_entry_insns]
3319- jit_entry_idx : u32 ,
3344+ jit_entry_idx : u16 ,
3345+
3346+ /// Argument count passing to the HIR function
3347+ argc : u16 ,
33203348
33213349 /// Position where the call instruction starts
33223350 start_addr : Cell < Option < CodePtr > > ,
@@ -3329,12 +3357,13 @@ pub type IseqCallRef = Rc<IseqCall>;
33293357
33303358impl IseqCall {
33313359 /// Allocate a new IseqCall
3332- fn new ( iseq : IseqPtr , jit_entry_idx : u32 ) -> IseqCallRef {
3360+ fn new ( iseq : IseqPtr , jit_entry_idx : u16 , argc : u16 ) -> IseqCallRef {
33333361 let iseq_call = IseqCall {
33343362 iseq : Cell :: new ( iseq) ,
33353363 start_addr : Cell :: new ( None ) ,
33363364 end_addr : Cell :: new ( None ) ,
33373365 jit_entry_idx,
3366+ argc,
33383367 } ;
33393368 Rc :: new ( iseq_call)
33403369 }
0 commit comments