1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Deduplicate side exits

Send instructions currently generate the exact same side exit twice.
Cache the exit the first time we generate it. Also add a comment
explaining what side exits do.

Closes GH-117.
This commit is contained in:
Alan Wu 2021-09-10 11:22:36 -04:00
parent 6e1f2519cc
commit cbb0271dd6
2 changed files with 27 additions and 8 deletions

View file

@ -193,7 +193,7 @@ jit_prepare_routine_call(jitstate_t *jit, ctx_t *ctx, x86opnd_t scratch_reg)
}
// Record the current codeblock write position for rewriting into a jump into
// the outline block later. Used to implement global code invalidation.
// the outlined block later. Used to implement global code invalidation.
static void
record_global_inval_patch(const codeblock_t *cb, uint32_t outline_block_target_pos)
{
@ -355,7 +355,7 @@ yjit_gen_exit(VALUE *exit_pc, ctx_t *ctx, codeblock_t *cb)
// Update the CFP on the EC
mov(cb, member_opnd(REG_EC, rb_execution_context_t, cfp), REG_CFP);
// Put PC into the return register, which the post call bytes dispatches to
// Update CFP->PC
mov(cb, RAX, const_ptr_opnd(exit_pc));
mov(cb, member_opnd(REG_CFP, rb_control_frame_t, pc), RAX);
@ -398,12 +398,27 @@ yjit_gen_leave_exit(codeblock_t *cb)
return code_ptr;
}
// A shorthand for generating an exit in the outline block
// :side-exit:
// Get an exit for the current instruction in the outlined block. The code
// for each instruction often begins with several guards before proceeding
// to do work. When guards fail, an option we have is to exit to the
// interpreter at an instruction boundary. The piece of code that takes
// care of reconstructing interpreter state and exiting out of generated
// code is called the side exit.
//
// No guards change the logic for reconstructing interpreter state at the
// moment, so there is one unique side exit for each context. Note that
// it's incorrect to jump to the side exit after any ctx stack push/pop operations
// since they change the logic required for reconstructing interpreter state.
static uint8_t *
yjit_side_exit(jitstate_t *jit, ctx_t *ctx)
{
uint32_t pos = yjit_gen_exit(jit->pc, ctx, ocb);
return cb_get_ptr(ocb, pos);
if (!jit->side_exit_for_pc) {
uint32_t pos = yjit_gen_exit(jit->pc, ctx, ocb);
jit->side_exit_for_pc = cb_get_ptr(ocb, pos);
}
return jit->side_exit_for_pc;
}
// 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)
// Set the current instruction
jit.insn_idx = insn_idx;
jit.pc = pc;
jit.opcode = opcode;
jit.pc = pc;
jit.side_exit_for_pc = NULL;
// If previous instruction requested to record the boundary
if (jit.record_boundary_patch_point) {

View file

@ -13,7 +13,7 @@ extern uint32_t yjit_codepage_frozen_bytes;
typedef struct JITState
{
// Block version being compiled
block_t* block;
block_t *block;
// Instruction sequence this is associated with
const rb_iseq_t *iseq;
@ -27,9 +27,12 @@ typedef struct JITState
// PC of the instruction being compiled
VALUE *pc;
// Side exit to the instruction being compiled. See :side-exit:.
uint8_t *side_exit_for_pc;
// Execution context when compilation started
// This allows us to peek at run-time values
rb_execution_context_t* ec;
rb_execution_context_t *ec;
// Whether we need to record the code address at
// the end of this bytecode instruction for global invalidation