Machinery to implement deferred compilation
This commit is contained in:
parent
f8b4082be4
commit
7f4000b1f4
@ -190,6 +190,28 @@ assert_normal_exit %q{
|
|||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Test getinstancevariable and inline caches
|
||||||
|
assert_equal '6', %q{
|
||||||
|
class Foo
|
||||||
|
def initialize
|
||||||
|
@x1 = 1
|
||||||
|
@x2 = 1
|
||||||
|
@x2 = 1
|
||||||
|
@x3 = 1
|
||||||
|
@x4 = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
def bar
|
||||||
|
x = 1
|
||||||
|
@x4 + @x4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
f = Foo.new
|
||||||
|
f.bar
|
||||||
|
f.bar
|
||||||
|
}
|
||||||
|
|
||||||
# Test that getinstancevariable codegen checks for extended table size
|
# Test that getinstancevariable codegen checks for extended table size
|
||||||
assert_equal "nil\n", %q{
|
assert_equal "nil\n", %q{
|
||||||
class A
|
class A
|
||||||
|
@ -82,17 +82,17 @@ jit_mov_gc_ptr(jitstate_t* jit, codeblock_t* cb, x86opnd_t reg, VALUE ptr)
|
|||||||
// Check if we are compiling the instruction at the stub PC
|
// Check if we are compiling the instruction at the stub PC
|
||||||
// Meaning we are compiling the instruction that is next to execute
|
// Meaning we are compiling the instruction that is next to execute
|
||||||
static bool
|
static bool
|
||||||
jit_at_current_insn(jitstate_t* jit, ctx_t* ctx)
|
jit_at_current_insn(jitstate_t* jit)
|
||||||
{
|
{
|
||||||
const VALUE* stub_pc = jit->ec->cfp->pc;
|
const VALUE* ec_pc = jit->ec->cfp->pc;
|
||||||
return (stub_pc == jit->pc);
|
return (ec_pc == jit->pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peek at the topmost value on the Ruby stack
|
// Peek at the topmost value on the Ruby stack
|
||||||
static VALUE
|
static VALUE
|
||||||
jit_peek_at_stack(jitstate_t* jit, ctx_t* ctx)
|
jit_peek_at_stack(jitstate_t* jit, ctx_t* ctx)
|
||||||
{
|
{
|
||||||
RUBY_ASSERT(jit_at_current_insn(jit, ctx));
|
RUBY_ASSERT(jit_at_current_insn(jit));
|
||||||
|
|
||||||
VALUE* sp = jit->ec->cfp->sp + ctx->sp_offset;
|
VALUE* sp = jit->ec->cfp->sp + ctx->sp_offset;
|
||||||
|
|
||||||
@ -317,7 +317,13 @@ ujit_gen_block(ctx_t* ctx, block_t* block, rb_execution_context_t* ec)
|
|||||||
// Call the code generation function
|
// Call the code generation function
|
||||||
bool continue_generating = p_desc->gen_fn(&jit, ctx);
|
bool continue_generating = p_desc->gen_fn(&jit, ctx);
|
||||||
|
|
||||||
if (!continue_generating) {
|
// For now, reset the chain depth after each instruction
|
||||||
|
ctx->chain_depth = 0;
|
||||||
|
|
||||||
|
// If we can't compile this instruction
|
||||||
|
// exit to the interpreter and stop compiling
|
||||||
|
if (status == UJIT_CANT_COMPILE) {
|
||||||
|
ujit_gen_exit(&jit, ctx, cb, jit.pc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,33 +578,21 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
|
|||||||
return UJIT_CANT_COMPILE;
|
return UJIT_CANT_COMPILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defer compilation so we can peek at the topmost object
|
||||||
|
if (!jit_at_current_insn(jit))
|
||||||
|
{
|
||||||
|
defer_compilation(jit->block, jit->insn_idx, ctx);
|
||||||
|
return UJIT_END_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek at the topmost value on the stack at compilation time
|
||||||
|
VALUE top_val = jit_peek_at_stack(jit, ctx);
|
||||||
|
|
||||||
|
// TODO: play with deferred compilation and sidechains! :)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
num_versions = count_block_versions(this_instruction);
|
|
||||||
|
|
||||||
if (num_versions > N)
|
|
||||||
return JIT_CANT_COMPILE;
|
|
||||||
|
|
||||||
|
|
||||||
if (defer_compilation(this_instruction, ctx))
|
|
||||||
return JIT_END_BLOCK;
|
|
||||||
|
|
||||||
|
|
||||||
VALUE top_val = jit_peek_at_stack();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class = get_ruby_class(top_val);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
guard_object_class(class, current_instr);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1452,7 +1446,6 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca
|
|||||||
cmp(cb, klass_opnd, REG1);
|
cmp(cb, klass_opnd, REG1);
|
||||||
jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
|
jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
|
||||||
|
|
||||||
|
|
||||||
if (METHOD_ENTRY_VISI(cme) == METHOD_VISI_PROTECTED) {
|
if (METHOD_ENTRY_VISI(cme) == METHOD_VISI_PROTECTED) {
|
||||||
// Generate ancestry guard for protected callee.
|
// Generate ancestry guard for protected callee.
|
||||||
jit_protected_guard(jit, cb, cme, side_exit);
|
jit_protected_guard(jit, cb, cme, side_exit);
|
||||||
|
54
ujit_core.c
54
ujit_core.c
@ -113,6 +113,15 @@ Returns INT_MAX if incompatible
|
|||||||
*/
|
*/
|
||||||
int ctx_diff(const ctx_t* src, const ctx_t* dst)
|
int ctx_diff(const ctx_t* src, const ctx_t* dst)
|
||||||
{
|
{
|
||||||
|
// Can only lookup the first version in the chain
|
||||||
|
if (dst->chain_depth != 0)
|
||||||
|
return INT_MAX;
|
||||||
|
|
||||||
|
// Blocks with depth > 0 always produce new versions
|
||||||
|
// Sidechains cannot overlap
|
||||||
|
if (src->chain_depth != 0)
|
||||||
|
return INT_MAX;
|
||||||
|
|
||||||
if (dst->stack_size != src->stack_size)
|
if (dst->stack_size != src->stack_size)
|
||||||
return INT_MAX;
|
return INT_MAX;
|
||||||
|
|
||||||
@ -353,6 +362,7 @@ uint8_t* branch_stub_hit(uint32_t branch_idx, uint32_t target_idx, rb_execution_
|
|||||||
|
|
||||||
//fprintf(stderr, "\nstub hit, branch idx: %d, target idx: %d\n", branch_idx, target_idx);
|
//fprintf(stderr, "\nstub hit, branch idx: %d, target idx: %d\n", branch_idx, target_idx);
|
||||||
//fprintf(stderr, "blockid.iseq=%p, blockid.idx=%d\n", target.iseq, target.idx);
|
//fprintf(stderr, "blockid.iseq=%p, blockid.idx=%d\n", target.iseq, target.idx);
|
||||||
|
//fprintf(stderr, "chain_depth=%d\n", target_ctx->chain_depth);
|
||||||
|
|
||||||
// Update the PC in the current CFP, because it
|
// Update the PC in the current CFP, because it
|
||||||
// may be out of sync in JITted code
|
// may be out of sync in JITted code
|
||||||
@ -376,7 +386,7 @@ uint8_t* branch_stub_hit(uint32_t branch_idx, uint32_t target_idx, rb_execution_
|
|||||||
generic_ctx.sp_offset = target_ctx->sp_offset;
|
generic_ctx.sp_offset = target_ctx->sp_offset;
|
||||||
if (get_num_versions(target) >= MAX_VERSIONS - 1)
|
if (get_num_versions(target) >= MAX_VERSIONS - 1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "version limit hit in branch_stub_hit\n");
|
//fprintf(stderr, "version limit hit in branch_stub_hit\n");
|
||||||
target_ctx = &generic_ctx;
|
target_ctx = &generic_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +552,7 @@ void gen_direct_jump(
|
|||||||
generic_ctx.sp_offset = ctx->sp_offset;
|
generic_ctx.sp_offset = ctx->sp_offset;
|
||||||
if (get_num_versions(target0) >= MAX_VERSIONS - 1)
|
if (get_num_versions(target0) >= MAX_VERSIONS - 1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "version limit hit in gen_direct_jump\n");
|
//fprintf(stderr, "version limit hit in gen_direct_jump\n");
|
||||||
ctx = &generic_ctx;
|
ctx = &generic_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,42 +598,50 @@ void gen_direct_jump(
|
|||||||
// Create a stub to force the code up to this point to be executed
|
// Create a stub to force the code up to this point to be executed
|
||||||
void defer_compilation(
|
void defer_compilation(
|
||||||
block_t* block,
|
block_t* block,
|
||||||
ctx_t* cur_ctx,
|
uint32_t insn_idx,
|
||||||
uint32_t insn_idx
|
ctx_t* cur_ctx
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
//fprintf(stderr, "defer compilation at (%p, %d) depth=%d\n", block->blockid.iseq, insn_idx, cur_ctx->chain_depth);
|
||||||
|
|
||||||
|
if (cur_ctx->chain_depth != 0) {
|
||||||
|
rb_backtrace();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_t next_ctx = *cur_ctx;
|
||||||
|
|
||||||
|
if (next_ctx.chain_depth >= UINT8_MAX) {
|
||||||
|
rb_bug("max block version chain depth reached");
|
||||||
|
}
|
||||||
|
|
||||||
|
next_ctx.chain_depth += 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
RUBY_ASSERT(num_branches < MAX_BRANCHES);
|
RUBY_ASSERT(num_branches < MAX_BRANCHES);
|
||||||
uint32_t branch_idx = num_branches++;
|
uint32_t branch_idx = num_branches++;
|
||||||
|
|
||||||
|
// Get the branch targets or stubs
|
||||||
|
blockid_t target0 = (blockid_t){ block->blockid.iseq, insn_idx };
|
||||||
|
uint8_t* dst_addr0 = get_branch_target(target0, &next_ctx, branch_idx, 0);
|
||||||
|
|
||||||
|
// Call the branch generation function
|
||||||
|
uint32_t start_pos = cb->write_pos;
|
||||||
|
gen_jump_branch(cb, dst_addr0, NULL, SHAPE_DEFAULT);
|
||||||
|
uint32_t end_pos = cb->write_pos;
|
||||||
|
|
||||||
// Register this branch entry
|
// Register this branch entry
|
||||||
branch_t branch_entry = {
|
branch_t branch_entry = {
|
||||||
start_pos,
|
start_pos,
|
||||||
end_pos,
|
end_pos,
|
||||||
*ctx,
|
*cur_ctx,
|
||||||
{ target0, BLOCKID_NULL },
|
{ target0, BLOCKID_NULL },
|
||||||
{ *ctx, *ctx },
|
{ next_ctx, next_ctx },
|
||||||
{ dst_addr0, NULL },
|
{ dst_addr0, NULL },
|
||||||
gen_jump_branch,
|
gen_jump_branch,
|
||||||
branch_shape
|
SHAPE_DEFAULT
|
||||||
};
|
};
|
||||||
|
|
||||||
branch_entries[branch_idx] = branch_entry;
|
branch_entries[branch_idx] = branch_entry;
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all references to a block then free it.
|
// Remove all references to a block then free it.
|
||||||
|
18
ujit_core.h
18
ujit_core.h
@ -20,12 +20,18 @@
|
|||||||
// Maximum number of temp value types we keep track of
|
// Maximum number of temp value types we keep track of
|
||||||
#define MAX_TEMP_TYPES 8
|
#define MAX_TEMP_TYPES 8
|
||||||
|
|
||||||
|
// Default versioning context (no type information)
|
||||||
|
#define DEFAULT_CTX ( (ctx_t){ 0 } )
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Code generation context
|
Code generation context
|
||||||
Contains information we can use to optimize code
|
Contains information we can use to optimize code
|
||||||
*/
|
*/
|
||||||
typedef struct CtxStruct
|
typedef struct CtxStruct
|
||||||
{
|
{
|
||||||
|
// Depth of this block in the sidechain (eg: inline-cache chain)
|
||||||
|
int8_t chain_depth;
|
||||||
|
|
||||||
// Temporary variable types we keep track of
|
// Temporary variable types we keep track of
|
||||||
// Values are `ruby_value_type`
|
// Values are `ruby_value_type`
|
||||||
// T_NONE==0 is the unknown type
|
// T_NONE==0 is the unknown type
|
||||||
@ -58,12 +64,12 @@ typedef struct BlockId
|
|||||||
static const blockid_t BLOCKID_NULL = { 0, 0 };
|
static const blockid_t BLOCKID_NULL = { 0, 0 };
|
||||||
|
|
||||||
/// Branch code shape enumeration
|
/// Branch code shape enumeration
|
||||||
enum uint8_t
|
typedef enum : int8_t
|
||||||
{
|
{
|
||||||
SHAPE_NEXT0, // Target 0 is next
|
SHAPE_NEXT0, // Target 0 is next
|
||||||
SHAPE_NEXT1, // Target 1 is next
|
SHAPE_NEXT1, // Target 1 is next
|
||||||
SHAPE_DEFAULT // Neither target is next
|
SHAPE_DEFAULT // Neither target is next
|
||||||
};
|
} branch_shape_t;
|
||||||
|
|
||||||
// Branch code generation function signature
|
// Branch code generation function signature
|
||||||
typedef void (*branchgen_fn)(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape);
|
typedef void (*branchgen_fn)(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape);
|
||||||
@ -92,7 +98,7 @@ typedef struct BranchEntry
|
|||||||
branchgen_fn gen_fn;
|
branchgen_fn gen_fn;
|
||||||
|
|
||||||
// Shape of the branch
|
// Shape of the branch
|
||||||
uint8_t shape;
|
branch_shape_t shape;
|
||||||
|
|
||||||
} branch_t;
|
} branch_t;
|
||||||
|
|
||||||
@ -159,6 +165,12 @@ void gen_direct_jump(
|
|||||||
blockid_t target0
|
blockid_t target0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void defer_compilation(
|
||||||
|
block_t* block,
|
||||||
|
uint32_t insn_idx,
|
||||||
|
ctx_t* cur_ctx
|
||||||
|
);
|
||||||
|
|
||||||
void invalidate_block_version(block_t* block);
|
void invalidate_block_version(block_t* block);
|
||||||
|
|
||||||
void ujit_init_core(void);
|
void ujit_init_core(void);
|
||||||
|
@ -126,7 +126,7 @@ ruby_show_version(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rb_ujit_enabled_p()) {
|
if (rb_ujit_enabled_p()) {
|
||||||
fputs("uJIT is enabled\n", stdout);
|
fputs("YJIT is enabled\n", stdout);
|
||||||
}
|
}
|
||||||
#ifdef RUBY_LAST_COMMIT_TITLE
|
#ifdef RUBY_LAST_COMMIT_TITLE
|
||||||
fputs("last_commit=" RUBY_LAST_COMMIT_TITLE, stdout);
|
fputs("last_commit=" RUBY_LAST_COMMIT_TITLE, stdout);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user