зеркало из https://github.com/github/ruby.git
MicroJIT: avoid having to invalidate running output code
This commit is contained in:
Родитель
9ce9f613b0
Коммит
188c54428c
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
require 'test/unit'
|
||||
|
||||
class TestMicroJIT < Test::Unit::TestCase
|
||||
# MicroJIT's code invalidation mechanism can't invalidate
|
||||
# code that is executing. Test that we don't try to do that.
|
||||
def test_code_invalidation
|
||||
klass = Class.new do
|
||||
def alias_then_hash(klass, method_to_redefine)
|
||||
klass.alias_method(method_to_redefine, :itself)
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
instance = klass.new
|
||||
i = 0
|
||||
while i < 12
|
||||
if i < 11
|
||||
instance.alias_then_hash(klass, :bar)
|
||||
else
|
||||
ret = instance.alias_then_hash(klass, :hash)
|
||||
assert(instance.equal?(ret))
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,6 +25,8 @@
|
|||
#define UJIT_CHECK_MODE 0
|
||||
#endif
|
||||
|
||||
// >= 1: print when output code invalidation happens
|
||||
// >= 2: dump list of instructions when regions compile
|
||||
#ifndef UJIT_DUMP_MODE
|
||||
#define UJIT_DUMP_MODE 0
|
||||
#endif
|
||||
|
@ -358,22 +360,20 @@ Compile a sequence of bytecode instructions starting at `insn_idx`.
|
|||
Return the index to the first instruction not compiled in the sequence
|
||||
through `next_ujit_idx`. Return `NULL` in case compilation fails.
|
||||
*/
|
||||
uint8_t *
|
||||
static uint8_t *
|
||||
ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *next_ujit_idx)
|
||||
{
|
||||
assert (cb != NULL);
|
||||
|
||||
unsigned first_insn_idx = insn_idx;
|
||||
VALUE *encoded = iseq->body->iseq_encoded;
|
||||
|
||||
// NOTE: if we are ever deployed in production, we
|
||||
// should probably just log an error and return NULL here,
|
||||
// so we can fail more gracefully
|
||||
if (cb->write_pos + 1024 >= cb->mem_size)
|
||||
{
|
||||
if (cb->write_pos + 1024 >= cb->mem_size) {
|
||||
rb_bug("out of executable memory");
|
||||
}
|
||||
if (ocb->write_pos + 1024 >= ocb->mem_size)
|
||||
{
|
||||
if (ocb->write_pos + 1024 >= ocb->mem_size) {
|
||||
rb_bug("out of executable memory (outlined block)");
|
||||
}
|
||||
|
||||
|
@ -396,9 +396,8 @@ ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *ne
|
|||
ctx.replacement_idx = insn_idx;
|
||||
|
||||
// For each instruction to compile
|
||||
size_t num_instrs;
|
||||
for (num_instrs = 0;; ++num_instrs)
|
||||
{
|
||||
unsigned num_instrs;
|
||||
for (num_instrs = 0;; ++num_instrs) {
|
||||
// Set the current PC
|
||||
ctx.pc = &encoded[insn_idx];
|
||||
|
||||
|
@ -407,16 +406,14 @@ ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *ne
|
|||
|
||||
// Lookup the codegen function for this instruction
|
||||
st_data_t st_gen_fn;
|
||||
if (!rb_st_lookup(gen_fns, opcode, &st_gen_fn))
|
||||
{
|
||||
if (!rb_st_lookup(gen_fns, opcode, &st_gen_fn)) {
|
||||
//print_int(cb, imm_opnd(num_instrs));
|
||||
//print_str(cb, insn_name(opcode));
|
||||
break;
|
||||
}
|
||||
|
||||
// Write the pre call bytes before the first instruction
|
||||
if (num_instrs == 0)
|
||||
{
|
||||
if (num_instrs == 0) {
|
||||
ujit_gen_entry(cb);
|
||||
|
||||
// Load the current SP from the CFP into REG_SP
|
||||
|
@ -425,29 +422,46 @@ ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *ne
|
|||
|
||||
// Call the code generation function
|
||||
codegen_fn gen_fn = (codegen_fn)st_gen_fn;
|
||||
if (!gen_fn(cb, ocb, &ctx))
|
||||
{
|
||||
if (!gen_fn(cb, ocb, &ctx)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Move to the next instruction
|
||||
insn_idx += insn_len(opcode);
|
||||
|
||||
// Ensure we only have one send per region. Our code invalidation mechanism can't
|
||||
// invalidate running code and one send could invalidate the other if we had
|
||||
// multiple in the same region.
|
||||
if (opcode == BIN(opt_send_without_block)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Let the caller know how many instructions ujit compiled
|
||||
*next_ujit_idx = insn_idx;
|
||||
|
||||
// If no instructions were compiled
|
||||
if (num_instrs == 0)
|
||||
{
|
||||
if (num_instrs == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Generate code to exit to the interpreter
|
||||
ujit_gen_exit(cb, &ctx, ctx.pc);
|
||||
ujit_gen_exit(cb, &ctx, &encoded[*next_ujit_idx]);
|
||||
|
||||
addr2insn_bookkeeping(code_ptr, first_opcode);
|
||||
|
||||
if (UJIT_DUMP_MODE >= 2) {
|
||||
// Dump list of compiled instrutions
|
||||
fprintf(stderr, "Compiled the following for iseq=%p:\n", (void *)iseq);
|
||||
VALUE *pc = &encoded[first_insn_idx];
|
||||
VALUE *end_pc = &encoded[*next_ujit_idx];
|
||||
while (pc < end_pc) {
|
||||
int opcode = opcode_at_pc(iseq, pc);
|
||||
fprintf(stderr, " %04td %s\n", pc - encoded, insn_name(opcode));
|
||||
pc += insn_len(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
return code_ptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ bool rb_ujit_enabled_p(void)
|
|||
|
||||
void rb_ujit_method_lookup_change(VALUE cme_or_cc);
|
||||
void rb_ujit_init(void);
|
||||
uint8_t *ujit_compile_insn(const rb_iseq_t *iseq, unsigned int insn_idx, unsigned int *next_ujit_idx);
|
||||
void rb_ujit_compile_iseq(const rb_iseq_t *iseq);
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче