@@ -790,7 +790,7 @@ fn gen_ccall_with_frame(
790790
791791 // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP
792792 // to account for the receiver and arguments (and block arguments if any)
793- gen_prepare_call_with_gc ( asm, state, false ) ;
793+ gen_save_pc_for_gc ( asm, state) ;
794794 gen_save_sp ( asm, caller_stack_size) ;
795795 gen_spill_stack ( jit, asm, state) ;
796796 gen_spill_locals ( jit, asm, state) ;
@@ -875,7 +875,7 @@ fn gen_ccall_variadic(
875875
876876 // Can't use gen_prepare_non_leaf_call() because we need to adjust the SP
877877 // to account for the receiver and arguments (like gen_ccall_with_frame does)
878- gen_prepare_call_with_gc ( asm, state, false ) ;
878+ gen_save_pc_for_gc ( asm, state) ;
879879 gen_save_sp ( asm, caller_stack_size) ;
880880 gen_spill_stack ( jit, asm, state) ;
881881 gen_spill_locals ( jit, asm, state) ;
@@ -1304,8 +1304,8 @@ fn gen_send_without_block_direct(
13041304 gen_stack_overflow_check ( jit, asm, state, stack_growth) ;
13051305
13061306 // Save cfp->pc and cfp->sp for the caller frame
1307- gen_prepare_call_with_gc ( asm , state , false ) ;
1308- // Special SP math. Can't use gen_prepare_non_leaf_call
1307+ // Can't use gen_prepare_non_leaf_call because we need special SP math.
1308+ gen_save_pc_for_gc ( asm , state ) ;
13091309 gen_save_sp ( asm, state. stack ( ) . len ( ) - args. len ( ) - 1 ) ; // -1 for receiver
13101310
13111311 gen_spill_locals ( jit, asm, state) ;
@@ -2008,6 +2008,18 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso
20082008 }
20092009}
20102010
2011+ /// Save only the PC to CFP. Use this when you need to call gen_save_sp()
2012+ /// immediately after with a custom stack size (e.g., gen_ccall_with_frame
2013+ /// adjusts SP to exclude receiver and arguments).
2014+ fn gen_save_pc_for_gc ( asm : & mut Assembler , state : & FrameState ) {
2015+ let opcode: usize = state. get_opcode ( ) . try_into ( ) . unwrap ( ) ;
2016+ let next_pc: * const VALUE = unsafe { state. pc . offset ( insn_len ( opcode) as isize ) } ;
2017+
2018+ gen_incr_counter ( asm, Counter :: vm_write_pc_count) ;
2019+ asm_comment ! ( asm, "save PC to CFP" ) ;
2020+ asm. mov ( Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_PC ) , Opnd :: const_ptr ( next_pc) ) ;
2021+ }
2022+
20112023/// Save the current PC on the CFP as a preparation for calling a C function
20122024/// that may allocate objects and trigger GC. Use gen_prepare_non_leaf_call()
20132025/// if it may raise exceptions or call arbitrary methods.
@@ -2017,13 +2029,7 @@ fn gen_incr_send_fallback_counter(asm: &mut Assembler, reason: SendFallbackReaso
20172029/// However, to avoid marking uninitialized stack slots, this also updates SP,
20182030/// which may have cfp->sp for a past frame or a past non-leaf call.
20192031fn gen_prepare_call_with_gc ( asm : & mut Assembler , state : & FrameState , leaf : bool ) {
2020- let opcode: usize = state. get_opcode ( ) . try_into ( ) . unwrap ( ) ;
2021- let next_pc: * const VALUE = unsafe { state. pc . offset ( insn_len ( opcode) as isize ) } ;
2022-
2023- gen_incr_counter ( asm, Counter :: vm_write_pc_count) ;
2024- asm_comment ! ( asm, "save PC to CFP" ) ;
2025- asm. mov ( Opnd :: mem ( 64 , CFP , RUBY_OFFSET_CFP_PC ) , Opnd :: const_ptr ( next_pc) ) ;
2026-
2032+ gen_save_pc_for_gc ( asm, state) ;
20272033 gen_save_sp ( asm, state. stack_size ( ) ) ;
20282034 if leaf {
20292035 asm. expect_leaf_ccall ( state. stack_size ( ) ) ;
0 commit comments