@@ -1092,31 +1092,60 @@ fn gen_ccall_with_frame(
10921092 forwarded_argc : None , // cfunc doesn't support forwarded arguments
10931093 } ) ;
10941094
1095- asm_comment ! ( asm, "switch to new SP register" ) ;
10961095 let sp_offset = ( caller_stack_size + VM_ENV_DATA_SIZE . to_usize ( ) ) * SIZEOF_VALUE ;
1097- let new_sp = asm. add ( SP , sp_offset. into ( ) ) ;
1098- asm. mov ( SP , new_sp) ;
1099-
1100- asm_comment ! ( asm, "switch to new CFP" ) ;
1101- let new_cfp = asm. sub ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
1102- asm. mov ( CFP , new_cfp) ;
1103- asm. store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP ) , CFP ) ;
1096+ gen_enter_cfunc_frame ( asm, sp_offset) ;
11041097
11051098 let mut cfunc_args = vec ! [ recv] ;
11061099 cfunc_args. extend ( args) ;
11071100 asm. count_call_to_with ( || qualified_method_name ( unsafe { ( * cme) . owner } , name) ) ;
11081101 let result = asm. ccall ( cfunc, cfunc_args) ;
11091102
1103+ gen_pop_cfunc_frame ( asm, sp_offset) ;
1104+
1105+ result
1106+ }
1107+
1108+ /// Switch registers to a direct ISEQ callee, but leave ec->cfp on the caller.
1109+ /// gen_entry_point() publishes the callee only after its JITFrame and jit_return are valid.
1110+ fn gen_enter_iseq_frame ( asm : & mut Assembler , sp_offset : usize ) {
1111+ gen_switch_to_callee ( asm, sp_offset) ;
1112+ }
1113+
1114+ /// Enter a C frame whose SP, block_code, and C-frame sentinel are already initialized.
1115+ fn gen_enter_cfunc_frame ( asm : & mut Assembler , sp_offset : usize ) {
1116+ gen_switch_to_callee ( asm, sp_offset) ;
1117+ gen_set_ec_cfp ( asm) ;
1118+ }
1119+
1120+ /// Restore the caller's control frame after a C call.
1121+ fn gen_pop_cfunc_frame ( asm : & mut Assembler , sp_offset : usize ) {
11101122 asm_comment ! ( asm, "pop C frame" ) ;
11111123 let new_cfp = asm. add ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
11121124 asm. mov ( CFP , new_cfp) ;
1125+ gen_set_ec_cfp ( asm) ;
1126+ gen_restore_sp ( asm, sp_offset) ;
1127+ }
1128+
1129+ /// Set `ec->cfp` to the current CFP.
1130+ fn gen_set_ec_cfp ( asm : & mut Assembler ) {
11131131 asm. store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP ) , CFP ) ;
1132+ }
11141133
1134+ /// Restore the caller's SP after a call that switched to a callee frame.
1135+ fn gen_restore_sp ( asm : & mut Assembler , sp_offset : usize ) {
11151136 asm_comment ! ( asm, "restore SP register for the caller" ) ;
11161137 let new_sp = asm. sub ( SP , sp_offset. into ( ) ) ;
11171138 asm. mov ( SP , new_sp) ;
1139+ }
11181140
1119- result
1141+ fn gen_switch_to_callee ( asm : & mut Assembler , sp_offset : usize ) {
1142+ asm_comment ! ( asm, "switch to new SP register" ) ;
1143+ let new_sp = asm. add ( SP , sp_offset. into ( ) ) ;
1144+ asm. mov ( SP , new_sp) ;
1145+
1146+ asm_comment ! ( asm, "switch to new CFP" ) ;
1147+ let new_cfp = asm. sub ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
1148+ asm. mov ( CFP , new_cfp) ;
11201149}
11211150
11221151/// Lowering for [`Insn::CCall`]. This is a low-level raw call that doesn't know
@@ -1183,28 +1212,14 @@ fn gen_ccall_variadic(
11831212 forwarded_argc : None , // cfunc doesn't support forwarded arguments
11841213 } ) ;
11851214
1186- asm_comment ! ( asm, "switch to new SP register" ) ;
11871215 let sp_offset = ( caller_stack_size + VM_ENV_DATA_SIZE . to_usize ( ) ) * SIZEOF_VALUE ;
1188- let new_sp = asm. add ( SP , sp_offset. into ( ) ) ;
1189- asm. mov ( SP , new_sp) ;
1190-
1191- asm_comment ! ( asm, "switch to new CFP" ) ;
1192- let new_cfp = asm. sub ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
1193- asm. mov ( CFP , new_cfp) ;
1194- asm. store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP ) , CFP ) ;
1216+ gen_enter_cfunc_frame ( asm, sp_offset) ;
11951217
11961218 let argv_ptr = gen_push_opnds ( jit, asm, & args) ;
11971219 asm. count_call_to_with ( || qualified_method_name ( unsafe { ( * cme) . owner } , name) ) ;
11981220 let result = asm. ccall ( cfunc, vec ! [ args. len( ) . into( ) , argv_ptr, recv] ) ;
11991221
1200- asm_comment ! ( asm, "pop C frame" ) ;
1201- let new_cfp = asm. add ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
1202- asm. mov ( CFP , new_cfp) ;
1203- asm. store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP ) , CFP ) ;
1204-
1205- asm_comment ! ( asm, "restore SP register for the caller" ) ;
1206- let new_sp = asm. sub ( SP , sp_offset. into ( ) ) ;
1207- asm. mov ( SP , new_sp) ;
1222+ gen_pop_cfunc_frame ( asm, sp_offset) ;
12081223
12091224 result
12101225}
@@ -1744,7 +1759,7 @@ fn gen_push_inline_frame(
17441759 asm_comment ! ( asm, "switch to inlined callee CFP" ) ;
17451760 let new_cfp = asm. sub ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
17461761 asm. mov ( CFP , new_cfp) ;
1747- asm . store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP as i32 ) , CFP ) ;
1762+ gen_set_ec_cfp ( asm ) ;
17481763}
17491764
17501765/// Pop the interpreter frame for an inlined callee, restoring the caller's SP and CFP.
@@ -1762,7 +1777,7 @@ fn gen_pop_inline_frame(
17621777
17631778 asm_comment ! ( asm, "restore caller CFP after inline" ) ;
17641779 asm. add_into ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
1765- asm . store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP as i32 ) , CFP ) ;
1780+ gen_set_ec_cfp ( asm ) ;
17661781}
17671782
17681783/// Compile a direct call to an ISEQ method.
@@ -1869,14 +1884,8 @@ fn gen_send_iseq_direct(
18691884 // the callee will spill the callinfo passed as part of `c_args` into the `...` local.
18701885 }
18711886
1872- asm_comment ! ( asm, "switch to new SP register" ) ;
18731887 let sp_offset = ( state. stack ( ) . len ( ) + local_size - args. len ( ) + VM_ENV_DATA_SIZE . to_usize ( ) ) * SIZEOF_VALUE ;
1874- let new_sp = asm. add ( SP , sp_offset. into ( ) ) ;
1875- asm. mov ( SP , new_sp) ;
1876-
1877- asm_comment ! ( asm, "switch to new CFP" ) ;
1878- let new_cfp = asm. sub ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
1879- asm. mov ( CFP , new_cfp) ; // will be published at `ec->cfp` after callee's entrypoint
1888+ gen_enter_iseq_frame ( asm, sp_offset) ;
18801889
18811890 let params = unsafe { iseq. params ( ) } ;
18821891
@@ -1925,9 +1934,7 @@ fn gen_send_iseq_direct(
19251934 // Restore the C stack pointer on exit
19261935 asm. je ( jit, ZJITState :: get_exit_trampoline ( ) . into ( ) ) ;
19271936
1928- asm_comment ! ( asm, "restore SP register for the caller" ) ;
1929- let new_sp = asm. sub ( SP , sp_offset. into ( ) ) ;
1930- asm. mov ( SP , new_sp) ;
1937+ gen_restore_sp ( asm, sp_offset) ;
19311938
19321939 ret
19331940}
@@ -2067,15 +2074,9 @@ fn gen_invoke_block_iseq_direct(
20672074 forwarded_argc : None , // `...` is not allowed in block arguments
20682075 } ) ;
20692076
2070- asm_comment ! ( asm, "switch to new SP register" ) ;
20712077 let sp_offset = ( stack_size + local_size + VM_ENV_DATA_SIZE . to_usize ( ) ) * SIZEOF_VALUE ;
2072- let new_sp = asm. add ( SP , sp_offset. into ( ) ) ;
2073- asm. mov ( SP , new_sp) ;
2074-
2075- asm_comment ! ( asm, "switch to new CFP" ) ;
2076- let new_cfp = asm. sub ( CFP , RUBY_SIZEOF_CONTROL_FRAME . into ( ) ) ;
2077- asm. mov ( CFP , new_cfp) ;
2078- asm. store ( Opnd :: mem ( 64 , EC , RUBY_OFFSET_EC_CFP ) , CFP ) ;
2078+ gen_switch_to_callee ( asm, sp_offset) ;
2079+ gen_set_ec_cfp ( asm) ;
20792080
20802081 // JIT-to-JIT convention: self as c_args[0], then positional args. The block is
20812082 // gated to simple + lead-only + exact arity, so there are no optionals/kw/block.
@@ -2093,9 +2094,7 @@ fn gen_invoke_block_iseq_direct(
20932094 asm. cmp ( ret, Qundef . into ( ) ) ;
20942095 asm. je ( jit, ZJITState :: get_exit_trampoline ( ) . into ( ) ) ;
20952096
2096- asm_comment ! ( asm, "restore SP register for the caller" ) ;
2097- let new_sp = asm. sub ( SP , sp_offset. into ( ) ) ;
2098- asm. mov ( SP , new_sp) ;
2097+ gen_restore_sp ( asm, sp_offset) ;
20992098
21002099 ret
21012100}
0 commit comments