summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Wu <[email protected]>2021-09-10 11:22:36 -0400
committerAlan Wu <[email protected]>2021-10-20 18:19:40 -0400
commitcbb0271dd6256601aa6aea669ceac4f82fdb75fe (patch)
tree5a0becaad690d4fcd5c85c840f79a2ca899b07c0
parent6e1f2519cc851961939c4de61613161de3caf998 (diff)
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.
-rw-r--r--yjit_codegen.c28
-rw-r--r--yjit_codegen.h7
2 files changed, 27 insertions, 8 deletions
diff --git a/yjit_codegen.c b/yjit_codegen.c
index 015e97bb73..9fd70917d2 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -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) {
diff --git a/yjit_codegen.h b/yjit_codegen.h
index 66740a30f5..16c2211656 100644
--- a/yjit_codegen.h
+++ b/yjit_codegen.h
@@ -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