summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstraptest/test_yjit.rb22
-rw-r--r--ujit_codegen.c47
-rw-r--r--ujit_core.c54
-rw-r--r--ujit_core.h18
-rw-r--r--version.c2
5 files changed, 94 insertions, 49 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 0f67c6ebbb..cf79ccd101 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -190,6 +190,28 @@ assert_normal_exit %q{
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
assert_equal "nil\n", %q{
class A
diff --git a/ujit_codegen.c b/ujit_codegen.c
index 0b2c52a844..86e1242f21 100644
--- a/ujit_codegen.c
+++ b/ujit_codegen.c
@@ -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
// Meaning we are compiling the instruction that is next to execute
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;
- return (stub_pc == jit->pc);
+ const VALUE* ec_pc = jit->ec->cfp->pc;
+ return (ec_pc == jit->pc);
}
// Peek at the topmost value on the Ruby stack
static VALUE
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;
@@ -317,7 +317,13 @@ ujit_gen_block(ctx_t* ctx, block_t* block, rb_execution_context_t* ec)
// Call the code generation function
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;
}
@@ -572,32 +578,20 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
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);
-
-
- /*
- 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);
+ // TODO: play with deferred compilation and sidechains! :)
- 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);
jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
-
if (METHOD_ENTRY_VISI(cme) == METHOD_VISI_PROTECTED) {
// Generate ancestry guard for protected callee.
jit_protected_guard(jit, cb, cme, side_exit);
diff --git a/ujit_core.c b/ujit_core.c
index f2215cc9bf..c1d2bfb650 100644
--- a/ujit_core.c
+++ b/ujit_core.c
@@ -113,6 +113,15 @@ Returns INT_MAX if incompatible
*/
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)
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, "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
// 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;
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;
}
@@ -542,7 +552,7 @@ void gen_direct_jump(
generic_ctx.sp_offset = ctx->sp_offset;
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;
}
@@ -588,42 +598,50 @@ void gen_direct_jump(
// Create a stub to force the code up to this point to be executed
void defer_compilation(
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);
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
branch_t branch_entry = {
start_pos,
end_pos,
- *ctx,
+ *cur_ctx,
{ target0, BLOCKID_NULL },
- { *ctx, *ctx },
+ { next_ctx, next_ctx },
{ dst_addr0, NULL },
gen_jump_branch,
- branch_shape
+ SHAPE_DEFAULT
};
branch_entries[branch_idx] = branch_entry;
- */
-
-
}
// Remove all references to a block then free it.
diff --git a/ujit_core.h b/ujit_core.h
index 92d2dd594f..cf82dfe5af 100644
--- a/ujit_core.h
+++ b/ujit_core.h
@@ -20,12 +20,18 @@
// Maximum number of temp value types we keep track of
#define MAX_TEMP_TYPES 8
+// Default versioning context (no type information)
+#define DEFAULT_CTX ( (ctx_t){ 0 } )
+
/**
Code generation context
Contains information we can use to optimize code
*/
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
// Values are `ruby_value_type`
// T_NONE==0 is the unknown type
@@ -58,12 +64,12 @@ typedef struct BlockId
static const blockid_t BLOCKID_NULL = { 0, 0 };
/// Branch code shape enumeration
-enum uint8_t
+typedef enum : int8_t
{
SHAPE_NEXT0, // Target 0 is next
SHAPE_NEXT1, // Target 1 is next
SHAPE_DEFAULT // Neither target is next
-};
+} branch_shape_t;
// Branch code generation function signature
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;
// Shape of the branch
- uint8_t shape;
+ branch_shape_t shape;
} branch_t;
@@ -159,6 +165,12 @@ void gen_direct_jump(
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 ujit_init_core(void);
diff --git a/version.c b/version.c
index 9c1aaff32d..7a5a2cb229 100644
--- a/version.c
+++ b/version.c
@@ -126,7 +126,7 @@ ruby_show_version(void)
}
if (rb_ujit_enabled_p()) {
- fputs("uJIT is enabled\n", stdout);
+ fputs("YJIT is enabled\n", stdout);
}
#ifdef RUBY_LAST_COMMIT_TITLE
fputs("last_commit=" RUBY_LAST_COMMIT_TITLE, stdout);