MicroJIT: compile after ten calls

This commit is contained in:
Alan Wu 2020-10-13 11:15:22 -04:00
parent ef9eb83cbe
commit 8bda11f690
4 changed files with 78 additions and 50 deletions

View File

@ -862,46 +862,16 @@ rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
{ {
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
const void * const *table = rb_vm_get_insns_address_table(); const void * const *table = rb_vm_get_insns_address_table();
unsigned int i;
VALUE *encoded = (VALUE *)iseq->body->iseq_encoded; VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
VALUE translated_insns_buf;
unsigned int insn_idx, translated_idx; for (i = 0; i < iseq->body->iseq_size; /* */ ) {
unsigned int next_ujit_idx = 0; int insn = (int)iseq->body->iseq_encoded[i];
unsigned int translated_len = 0;
bool ujit_enabled = rb_ujit_enabled_p();
VALUE *translated_insns = ALLOCV_N(VALUE, translated_insns_buf, iseq->body->iseq_size);
for (insn_idx = 0; insn_idx < iseq->body->iseq_size; /* */) {
int insn = (int)encoded[insn_idx];
int len = insn_len(insn); int len = insn_len(insn);
VALUE translated; encoded[i] = (VALUE)table[insn];
i += len;
uint8_t* native_code_ptr = NULL;
// If ujit is enabled and hasn't already compiled this instruction
if (ujit_enabled && insn_idx >= next_ujit_idx)
native_code_ptr = ujit_compile_insn(iseq, insn_idx, &next_ujit_idx);
if (native_code_ptr)
translated = (VALUE)native_code_ptr;
else
translated = (VALUE)table[insn];
translated_insns[translated_len++] = translated;
insn_idx += len;
} }
insn_idx = 0;
for (translated_idx = 0; translated_idx < translated_len; translated_idx++) {
int insn = (int)encoded[insn_idx];
int len = insn_len(insn);
encoded[insn_idx] = translated_insns[translated_idx];
insn_idx += len;
}
FL_SET((VALUE)iseq, ISEQ_TRANSLATED); FL_SET((VALUE)iseq, ISEQ_TRANSLATED);
ALLOCV_END(translated_insns_buf);
#endif #endif
return COMPILE_OK; return COMPILE_OK;
} }

8
mjit.h
View File

@ -16,6 +16,7 @@
#include "debug_counter.h" #include "debug_counter.h"
#include "ruby.h" #include "ruby.h"
#include "ujit_compile.h"
// Special address values of a function generated from the // Special address values of a function generated from the
// corresponding iseq by MJIT: // corresponding iseq by MJIT:
@ -142,7 +143,7 @@ mjit_exec(rb_execution_context_t *ec)
const rb_iseq_t *iseq; const rb_iseq_t *iseq;
struct rb_iseq_constant_body *body; struct rb_iseq_constant_body *body;
if (!mjit_call_p) if (!mjit_call_p && !rb_ujit_enabled_p())
return Qundef; return Qundef;
RB_DEBUG_COUNTER_INC(mjit_exec); RB_DEBUG_COUNTER_INC(mjit_exec);
@ -150,6 +151,11 @@ mjit_exec(rb_execution_context_t *ec)
body = iseq->body; body = iseq->body;
body->total_calls++; body->total_calls++;
const int ujit_call_threashold = 10;
if (body->total_calls == ujit_call_threashold) {
rb_ujit_compile_iseq(iseq);
}
mjit_func_t func = body->jit_func; mjit_func_t func = body->jit_func;
if (UNLIKELY((uintptr_t)func <= LAST_JIT_ISEQ_FUNC)) { if (UNLIKELY((uintptr_t)func <= LAST_JIT_ISEQ_FUNC)) {
# ifdef MJIT_HEADER # ifdef MJIT_HEADER

View File

@ -4,6 +4,7 @@
#include "vm_core.h" #include "vm_core.h"
#include "vm_callinfo.h" #include "vm_callinfo.h"
#include "builtin.h" #include "builtin.h"
#include "internal/compile.h"
#include "insns_info.inc" #include "insns_info.inc"
#include "ujit_compile.h" #include "ujit_compile.h"
#include "ujit_asm.h" #include "ujit_asm.h"
@ -19,6 +20,8 @@
#define PLATFORM_SUPPORTED_P 1 #define PLATFORM_SUPPORTED_P 1
#endif #endif
bool rb_ujit_enabled;
// Hash table of encoded instructions // Hash table of encoded instructions
extern st_table *rb_encoded_insn_data; extern st_table *rb_encoded_insn_data;
@ -31,6 +34,8 @@ typedef struct ctx_struct
// Difference between the current stack pointer and actual stack top // Difference between the current stack pointer and actual stack top
int32_t stack_diff; int32_t stack_diff;
const rb_iseq_t *iseq;
} ctx_t; } ctx_t;
// MicroJIT code generation function signature // MicroJIT code generation function signature
@ -63,12 +68,25 @@ addr2insn_bookkeeping(void *code_ptr, int insn)
} }
} }
static int
opcode_at_pc(const rb_iseq_t *iseq, const VALUE *pc)
{
const VALUE at_pc = *pc;
if (FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)) {
return rb_vm_insn_addr2insn((const void *)at_pc);
}
else {
return (int)at_pc;
}
}
// Get the current instruction opcode from the context object // Get the current instruction opcode from the context object
int ctx_get_opcode(ctx_t *ctx) int ctx_get_opcode(ctx_t *ctx)
{ {
return (int)(*ctx->pc); return opcode_at_pc(ctx->iseq, ctx->pc);
} }
// Get an instruction argument from the context object // Get an instruction argument from the context object
VALUE ctx_get_arg(ctx_t* ctx, size_t arg_idx) VALUE ctx_get_arg(ctx_t* ctx, size_t arg_idx)
{ {
@ -167,7 +185,7 @@ ujit_side_exit(codeblock_t* cb, ctx_t* ctx, VALUE* exit_pc)
// Otherwise the interpreter may jump right back to the // Otherwise the interpreter may jump right back to the
// JITted code we're trying to exit // JITted code we're trying to exit
const void * const *table = rb_vm_get_insns_address_table(); const void * const *table = rb_vm_get_insns_address_table();
int opcode = (int)(*exit_pc); int opcode = opcode_at_pc(ctx->iseq, exit_pc);
void* old_instr = (void*)table[opcode]; void* old_instr = (void*)table[opcode];
mov(cb, RAX, const_ptr_opnd(exit_pc)); mov(cb, RAX, const_ptr_opnd(exit_pc));
mov(cb, RCX, const_ptr_opnd(old_instr)); mov(cb, RCX, const_ptr_opnd(old_instr));
@ -192,11 +210,12 @@ System V ABI reference:
https://wiki.osdev.org/System_V_ABI#x86-64 https://wiki.osdev.org/System_V_ABI#x86-64
*/ */
uint8_t * uint8_t *
ujit_compile_insn(rb_iseq_t *iseq, unsigned int insn_idx, unsigned int* next_ujit_idx) ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int* next_ujit_idx)
{ {
if (!cb) { if (!cb) {
return NULL; return NULL;
} }
VALUE *encoded = iseq->body->iseq_encoded;
// NOTE: if we are ever deployed in production, we // NOTE: if we are ever deployed in production, we
// should probably just log an error and return NULL here, // should probably just log an error and return NULL here,
@ -218,19 +237,20 @@ ujit_compile_insn(rb_iseq_t *iseq, unsigned int insn_idx, unsigned int* next_uji
//printf("write pos: %ld\n", cb->write_pos); //printf("write pos: %ld\n", cb->write_pos);
// Get the first opcode in the sequence // Get the first opcode in the sequence
int first_opcode = (int)iseq->body->iseq_encoded[insn_idx]; int first_opcode = opcode_at_pc(iseq, &encoded[insn_idx]);
// Create codegen context // Create codegen context
ctx_t ctx; ctx_t ctx;
ctx.pc = NULL; ctx.pc = NULL;
ctx.stack_diff = 0; ctx.stack_diff = 0;
ctx.iseq = iseq;
// For each instruction to compile // For each instruction to compile
size_t num_instrs; size_t num_instrs;
for (num_instrs = 0;; ++num_instrs) for (num_instrs = 0;; ++num_instrs)
{ {
// Set the current PC // Set the current PC
ctx.pc = &iseq->body->iseq_encoded[insn_idx]; ctx.pc = &encoded[insn_idx];
// Get the current opcode // Get the current opcode
int opcode = ctx_get_opcode(&ctx); int opcode = ctx_get_opcode(&ctx);
@ -616,10 +636,33 @@ gen_opt_send_without_block(codeblock_t* cb, codeblock_t* ocb, ctx_t* ctx)
*/ */
} }
bool
rb_ujit_enabled_p(void) void
rb_ujit_compile_iseq(const rb_iseq_t *iseq)
{ {
return !!cb; #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
unsigned int insn_idx;
unsigned int next_ujit_idx = 0;
for (insn_idx = 0; insn_idx < iseq->body->iseq_size; /* */) {
int insn = opcode_at_pc(iseq, &encoded[insn_idx]);
int len = insn_len(insn);
uint8_t *native_code_ptr = NULL;
// If ujit hasn't already compiled this instruction
if (insn_idx >= next_ujit_idx) {
native_code_ptr = ujit_compile_insn(iseq, insn_idx, &next_ujit_idx);
}
if (native_code_ptr) {
encoded[insn_idx] = (VALUE)native_code_ptr;
}
insn_idx += len;
}
#endif
} }
void void
@ -630,6 +673,7 @@ rb_ujit_init(void)
return; return;
} }
rb_ujit_enabled = true;
// Initialize the code blocks // Initialize the code blocks
size_t mem_size = 128 * 1024 * 1024; size_t mem_size = 128 * 1024 * 1024;
uint8_t* mem_block = alloc_exec_mem(mem_size); uint8_t* mem_block = alloc_exec_mem(mem_size);

View File

@ -10,8 +10,16 @@ typedef struct rb_iseq_struct rb_iseq_t;
#define rb_iseq_t rb_iseq_t #define rb_iseq_t rb_iseq_t
#endif #endif
extern bool rb_ujit_enabled;
static inline
bool rb_ujit_enabled_p(void)
{
return rb_ujit_enabled;
}
void rb_ujit_init(void); void rb_ujit_init(void);
bool rb_ujit_enabled_p(void); uint8_t *ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *next_ujit_idx);
uint8_t* ujit_compile_insn(rb_iseq_t *iseq, unsigned int insn_idx, unsigned int* next_ujit_idx); void rb_ujit_compile_iseq(const rb_iseq_t *iseq);
#endif #endif