@@ -20,7 +20,7 @@ use crate::stats::{counter_ptr, with_time_stat, Counter, Counter::{compile_time_
2020use crate :: { asm:: CodeBlock , cruby:: * , options:: debug, virtualmem:: CodePtr } ;
2121use crate :: backend:: lir:: { self , asm_comment, asm_ccall, Assembler , Opnd , Target , CFP , C_ARG_OPNDS , C_RET_OPND , EC , NATIVE_STACK_PTR , NATIVE_BASE_PTR , SP } ;
2222use crate :: hir:: { iseq_to_hir, BlockId , BranchEdge , Invariant , RangeType , SideExitReason :: { self , * } , SpecialBackrefSymbol , SpecialObjectType } ;
23- use crate :: hir:: { Const , FrameState , Function , Insn , InsnId , SendFallbackReason } ;
23+ use crate :: hir:: { Const , FrameState , Function , Insn , InsnId , MethodType , SendFallbackReason } ;
2424use crate :: hir_type:: { types, Type } ;
2525use crate :: options:: get_option;
2626use crate :: cast:: IntoUsize ;
@@ -378,6 +378,10 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
378378 Insn :: SendWithoutBlockDirect { cd, state, args, .. } if args. len ( ) + 1 > C_ARG_OPNDS . len ( ) => // +1 for self
379379 gen_send_without_block ( jit, asm, * cd, & function. frame_state ( * state) , SendFallbackReason :: SendWithoutBlockDirectTooManyArgs ) ,
380380 Insn :: SendWithoutBlockDirect { cme, iseq, recv, args, state, .. } => gen_send_without_block_direct ( cb, jit, asm, * cme, * iseq, opnd ! ( recv) , opnds ! ( args) , & function. frame_state ( * state) ) ,
381+ // Give up SendDirect for 6+ args since asm.ccall() doesn't support it.
382+ Insn :: SendDirect { cd, blockiseq, state, args, .. } if args. len ( ) + 1 > C_ARG_OPNDS . len ( ) => // +1 for self
383+ gen_send ( jit, asm, * cd, * blockiseq, & function. frame_state ( * state) , SendFallbackReason :: SendDirectTooManyArgs ) ,
384+ Insn :: SendDirect { cd, cme, blockiseq, recv, args, state, .. } => gen_send_direct ( jit, asm, * cd, * cme, * blockiseq, opnd ! ( recv) , opnds ! ( args) , & function. frame_state ( * state) ) ,
381385 & Insn :: InvokeSuper { cd, blockiseq, state, reason, .. } => gen_invokesuper ( jit, asm, cd, blockiseq, & function. frame_state ( state) , reason) ,
382386 & Insn :: InvokeBlock { cd, state, reason, .. } => gen_invokeblock ( jit, asm, cd, & function. frame_state ( state) , reason) ,
383387 // Ensure we have enough room fit ec, self, and arguments
@@ -682,6 +686,8 @@ fn gen_ccall_with_frame(jit: &mut JITState, asm: &mut Assembler, cfunc: *const u
682686 iseq : None ,
683687 cme,
684688 frame_type : VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL ,
689+ block_handler : None ,
690+ block_iseq : None ,
685691 } ) ;
686692
687693 asm_comment ! ( asm, "switch to new SP register" ) ;
@@ -737,6 +743,8 @@ fn gen_ccall_variadic(
737743 iseq : None ,
738744 cme,
739745 frame_type : VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL ,
746+ block_handler : None ,
747+ block_iseq : None ,
740748 } ) ;
741749
742750 asm_comment ! ( asm, "switch to new SP register" ) ;
@@ -1129,6 +1137,8 @@ fn gen_send_without_block_direct(
11291137 iseq : Some ( iseq) ,
11301138 cme,
11311139 frame_type : VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL ,
1140+ block_handler : None ,
1141+ block_iseq : None ,
11321142 } ) ;
11331143
11341144 asm_comment ! ( asm, "switch to new SP register" ) ;
@@ -1166,6 +1176,90 @@ fn gen_send_without_block_direct(
11661176 ret
11671177}
11681178
1179+ /// Compile a direct jump to an ISEQ call with block
1180+ fn gen_send_direct (
1181+ jit : & mut JITState ,
1182+ asm : & mut Assembler ,
1183+ cd : * const rb_call_data ,
1184+ cme : * const rb_callable_method_entry_t ,
1185+ blockiseq : IseqPtr ,
1186+ recv : Opnd ,
1187+ args : Vec < Opnd > ,
1188+ state : & FrameState ,
1189+ ) -> lir:: Opnd {
1190+ gen_incr_counter ( asm, Counter :: iseq_optimized_send_count) ;
1191+
1192+ let cfunc = unsafe { get_cme_def_body_cfunc ( cme) } ;
1193+ let argc = args. len ( ) as i32 ;
1194+
1195+ let ci = unsafe { get_call_data_ci ( cd) } ;
1196+ let flags = unsafe { vm_ci_flag ( ci) } ;
1197+ // When seeing &block argument, fall back to dynamic dispatch for now
1198+ // TODO: Support block forwarding
1199+ if flags & VM_CALL_ARGS_BLOCKARG != 0 {
1200+ return gen_send (
1201+ jit,
1202+ asm,
1203+ cd,
1204+ blockiseq,
1205+ state,
1206+ SendFallbackReason :: SendNotOptimizedMethodType ( MethodType :: Cfunc ) ,
1207+ ) ;
1208+ }
1209+
1210+ let stack_growth = state. stack_size ( ) ;
1211+ gen_stack_overflow_check ( jit, asm, state, stack_growth) ;
1212+
1213+ // Save cfp->pc and cfp->sp for the caller frame
1214+ gen_prepare_call_with_gc ( asm, state, false ) ;
1215+ // Special SP math. Can't use gen_prepare_non_leaf_call
1216+ gen_save_sp ( asm, state. stack ( ) . len ( ) - args. len ( ) - 1 ) ; // -1 for receiver
1217+
1218+ gen_spill_locals ( jit, asm, state) ;
1219+ gen_spill_stack ( jit, asm, state) ;
1220+
1221+ let block_handler = if !blockiseq. is_null ( ) {
1222+ asm. store (
1223+ Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_BLOCK_CODE ) ,
1224+ VALUE :: from ( blockiseq) . into ( ) ,
1225+ ) ;
1226+ let cfp_self = asm. lea ( Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_SELF ) ) ;
1227+ Some ( asm. or ( cfp_self, Opnd :: Imm ( 1 ) ) )
1228+ } else {
1229+ None
1230+ } ;
1231+
1232+ let frame_type = VM_FRAME_MAGIC_CFUNC | VM_FRAME_FLAG_CFRAME | VM_ENV_FLAG_LOCAL ;
1233+ gen_push_frame ( asm, argc as usize , state, ControlFrame {
1234+ recv,
1235+ iseq : None ,
1236+ cme,
1237+ frame_type,
1238+ block_handler,
1239+ block_iseq : None ,
1240+ } ) ;
1241+
1242+ asm_comment ! ( asm, "set ec->cfp" ) ;
1243+ let new_cfp = asm. lea ( Opnd :: mem ( 64 , CFP , -( RUBY_SIZEOF_CONTROL_FRAME as i32 ) ) ) ;
1244+ asm. mov ( CFP , new_cfp) ;
1245+ asm. store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP ) , CFP ) ;
1246+
1247+ let mut c_args = vec ! [ recv] ;
1248+ c_args. extend ( args) ;
1249+
1250+ asm_comment ! ( asm, "call C function" ) ;
1251+ let cfunc_ptr = unsafe { get_mct_func ( cfunc) } . cast ( ) ;
1252+ let ret = asm. ccall ( cfunc_ptr, c_args) ;
1253+
1254+ asm_comment ! ( asm, "restore ec->cfp" ) ;
1255+ let ec_cfp_opnd = Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP ) ;
1256+ let caller_cfp = asm. add ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
1257+ asm. mov ( CFP , caller_cfp) ;
1258+ asm. store ( ec_cfp_opnd, CFP ) ;
1259+
1260+ ret
1261+ }
1262+
11691263/// Compile for invokeblock
11701264fn gen_invokeblock (
11711265 jit : & mut JITState ,
@@ -1709,6 +1803,8 @@ struct ControlFrame {
17091803 iseq : Option < IseqPtr > ,
17101804 cme : * const rb_callable_method_entry_t ,
17111805 frame_type : u32 ,
1806+ block_handler : Option < Opnd > ,
1807+ block_iseq : Option < IseqPtr > ,
17121808}
17131809
17141810/// Compile an interpreter frame
@@ -1725,9 +1821,24 @@ fn gen_push_frame(asm: &mut Assembler, argc: usize, state: &FrameState, frame: C
17251821 } ;
17261822 let ep_offset = state. stack ( ) . len ( ) as i32 + local_size - argc as i32 + VM_ENV_DATA_SIZE as i32 - 1 ;
17271823 asm. store ( Opnd :: mem ( 64 , SP , ( ep_offset - 2 ) * SIZEOF_VALUE_I32 ) , VALUE :: from ( frame. cme ) . into ( ) ) ;
1728- // ep[-1]: block_handler or prev EP
1729- // block_handler is not supported for now
1730- asm. store ( Opnd :: mem ( 64 , SP , ( ep_offset - 1 ) * SIZEOF_VALUE_I32 ) , VM_BLOCK_HANDLER_NONE . into ( ) ) ;
1824+
1825+ let block_handler_opnd = if let Some ( handler) = frame. block_handler {
1826+ handler
1827+ } else if let Some ( block_iseq) = frame. block_iseq {
1828+ asm. store (
1829+ Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_BLOCK_CODE ) ,
1830+ VALUE :: from ( block_iseq) . into ( ) ,
1831+ ) ;
1832+ let cfp_self_addr = asm. lea ( Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_SELF ) ) ;
1833+ asm. or ( cfp_self_addr, Opnd :: Imm ( 1 ) )
1834+ } else {
1835+ VM_BLOCK_HANDLER_NONE . into ( )
1836+ } ;
1837+
1838+ asm. store (
1839+ Opnd :: mem ( 64 , SP , ( ep_offset - 1 ) * SIZEOF_VALUE_I32 ) ,
1840+ block_handler_opnd,
1841+ ) ;
17311842 // ep[0]: ENV_FLAGS
17321843 asm. store ( Opnd :: mem ( 64 , SP , ep_offset * SIZEOF_VALUE_I32 ) , frame. frame_type . into ( ) ) ;
17331844
0 commit comments