@@ -193,7 +193,7 @@ jit_prepare_routine_call(jitstate_t *jit, ctx_t *ctx, x86opnd_t scratch_reg)
193193}
194194
195195// Record the current codeblock write position for rewriting into a jump into
196- // the outline block later. Used to implement global code invalidation.
196+ // the outlined block later. Used to implement global code invalidation.
197197static void
198198record_global_inval_patch (const codeblock_t * cb , uint32_t outline_block_target_pos )
199199{
@@ -355,7 +355,7 @@ yjit_gen_exit(VALUE *exit_pc, ctx_t *ctx, codeblock_t *cb)
355355 // Update the CFP on the EC
356356 mov (cb , member_opnd (REG_EC , rb_execution_context_t , cfp ), REG_CFP );
357357
358- // Put PC into the return register, which the post call bytes dispatches to
358+ // Update CFP->PC
359359 mov (cb , RAX , const_ptr_opnd (exit_pc ));
360360 mov (cb , member_opnd (REG_CFP , rb_control_frame_t , pc ), RAX );
361361
@@ -398,12 +398,27 @@ yjit_gen_leave_exit(codeblock_t *cb)
398398 return code_ptr ;
399399}
400400
401- // A shorthand for generating an exit in the outline block
401+ // :side-exit:
402+ // Get an exit for the current instruction in the outlined block. The code
403+ // for each instruction often begins with several guards before proceeding
404+ // to do work. When guards fail, an option we have is to exit to the
405+ // interpreter at an instruction boundary. The piece of code that takes
406+ // care of reconstructing interpreter state and exiting out of generated
407+ // code is called the side exit.
408+ //
409+ // No guards change the logic for reconstructing interpreter state at the
410+ // moment, so there is one unique side exit for each context. Note that
411+ // it's incorrect to jump to the side exit after any ctx stack push/pop operations
412+ // since they change the logic required for reconstructing interpreter state.
402413static uint8_t *
403414yjit_side_exit (jitstate_t * jit , ctx_t * ctx )
404415{
405- uint32_t pos = yjit_gen_exit (jit -> pc , ctx , ocb );
406- return cb_get_ptr (ocb , pos );
416+ if (!jit -> side_exit_for_pc ) {
417+ uint32_t pos = yjit_gen_exit (jit -> pc , ctx , ocb );
418+ jit -> side_exit_for_pc = cb_get_ptr (ocb , pos );
419+ }
420+
421+ return jit -> side_exit_for_pc ;
407422}
408423
409424// Generate a runtime guard that ensures the PC is at the start of the iseq,
@@ -638,8 +653,9 @@ yjit_gen_block(block_t *block, rb_execution_context_t *ec)
638653
639654 // Set the current instruction
640655 jit .insn_idx = insn_idx ;
641- jit .pc = pc ;
642656 jit .opcode = opcode ;
657+ jit .pc = pc ;
658+ jit .side_exit_for_pc = NULL ;
643659
644660 // If previous instruction requested to record the boundary
645661 if (jit .record_boundary_patch_point ) {
0 commit comments