8000 Merge pull request #69 from Shopify/yjit-deferred-comp · jamatthews/ruby@e77ca64 · GitHub
[go: up one dir, main page]

Skip to content

Commit e77ca64

Browse files
authored
Merge pull request ruby#69 from Shopify/yjit-deferred-comp
Machinery to implement deferred compilation
2 parents eea3435 + 8ace921 commit e77ca64

File tree

5 files changed

+88
-49
lines changed

5 files changed

+88
-49
lines changed

bootstraptest/test_yjit.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,28 @@ def itself
190190
end
191191
}
192192

193+
# Test getinstancevariable and inline caches
194+
assert_equal '6', %q{
195+
class Foo
196+
def initialize
197+
@x1 = 1
198+
@x2 = 1
199+
@x2 = 1
200+
@x3 = 1
201+
@x4 = 3
202+
end
203+
204+
def bar
205+
x = 1
206+
@x4 + @x4
207+
end
208+
end
209+
210+
f = Foo.new
211+
f.bar
212+
f.bar
213+
}
214+
193215
# Test that getinstancevariable codegen checks for extended table size
194216
assert_equal "nil\n", %q{
195217
class A

ujit_codegen.c

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,17 @@ jit_mov_gc_ptr(jitstate_t* jit, codeblock_t* cb, x86opnd_t reg, VALUE ptr)
8181
// Check if we are compiling the instruction at the stub PC
8282
// Meaning we are compiling the instruction that is next to execute
8383
static bool
84-
jit_at_current_insn(jitstate_t* jit, ctx_t* ctx)
84+
jit_at_current_insn(jitstate_t* jit)
8585
{
86-
const VALUE* stub_pc = jit->ec->cfp->pc;
87-
return (stub_pc == jit->pc);
86+
const VALUE* ec_pc = jit->ec->cfp->pc;
87+
return (ec_pc == jit->pc);
8888
}
8989

9090
// Peek at the topmost value on the Ruby stack
9191
static VALUE
9292
jit_peek_at_stack(jitstate_t* jit, ctx_t* ctx)
9393
{
94-
RUBY_ASSERT(jit_at_current_insn(jit, ctx));
94+
RUBY_ASSERT(jit_at_current_insn(jit));
9595

9696
VALUE* sp = jit->ec->cfp->sp + ctx->sp_offset;
9797

@@ -317,6 +317,9 @@ ujit_gen_block(ctx_t* ctx, block_t* block, rb_execution_context_t* ec)
317317
// Call the code generation function
318318
codegen_status_t status = gen_fn(&jit, ctx);
319319

320+
// For now, reset the chain depth after each instruction
321+
ctx->chain_depth = 0;
322+
320323
// If we can't compile this instruction
321324
// exit to the interpreter and stop compiling
322325
if (status == UJIT_CANT_COMPILE) {
@@ -574,32 +577,20 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
574577
return UJIT_CANT_COMPILE;
575578
}
576579

580+
// Defer compilation so we can peek at the topmost object
581+
if (!jit_at_current_insn(jit))
582+
{
583+
defer_compilation(jit->block, jit->insn_idx, ctx);
584+
return UJIT_END_BLOCK;
585+
}
577586

587+
// Peek at the topmost value on the stack at compilation time
588+
VALUE top_val = jit_peek_at_stack(jit, ctx);
578589

579-
580-
581-
/*
582-
num_versions = count_block_versions(this_instruction);
583-
584-
if (num_versions > N)
585-
return JIT_CANT_COMPILE;
586-
587-
588-
if (defer_compilation(this_instruction, ctx))
589-
return JIT_END_BLOCK;
590-
591-
592-
VALUE top_val = jit_peek_at_stack();
593-
594-
595-
596-
597-
class = get_ruby_class(top_val);
590+
// TODO: play with deferred compilation and sidechains! :)
598591

599592

600593

601-
guard_object_class(class, current_instr);
602-
*/
603594

604595

605596

@@ -1453,7 +1444,6 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca
14531444
cmp(cb, klass_opnd, REG1);
14541445
jne_ptr(cb, COUNTED_EXIT(side_exit, oswb_se_cc_klass_differ));
14551446

1456-
14571447
if (METHOD_ENTRY_VISI(cme) == METHOD_VISI_PROTECTED) {
14581448
// Generate ancestry guard for protected callee.
14591449
jit_protected_guard(jit, cb, cme, side_exit);

ujit_core.c

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ Returns INT_MAX if incompatible
113113
*/
114114
int ctx_diff(const ctx_t* src, const ctx_t* dst)
115115
{
116+
// Can only lookup the first version in the chain
117+
if (dst->chain_depth != 0)
118+
return INT_MAX;
119+
120+
// Blocks with depth > 0 always produce new versions
121+
// Sidechains cannot overlap
122+
if (src->chain_depth != 0)
123+
return INT_MAX;
124+
116125
if (dst->stack_size != src->stack_size)
117126
return INT_MAX;
118127

@@ -353,6 +362,7 @@ uint8_t* branch_stub_hit(uint32_t branch_idx, uint32_t target_idx, rb_execution_
353362

354363
//fprintf(stderr, "\nstub hit, branch idx: %d, target idx: %d\n", branch_idx, target_idx);
355364
//fprintf(stderr, "blockid.iseq=%p, blockid.idx=%d\n", target.iseq, target.idx);
365+
//fprintf(stderr, "chain_depth=%d\n", target_ctx->chain_depth);
356366

357367
// Update the PC in the current CFP, because it
358368
// 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_
376386
generic_ctx.sp_offset = target_ctx->sp_offset;
377387
if (get_num_versions(target) >= MAX_VERSIONS - 1)
378388
{
379-
fprintf(stderr, "version limit hit in branch_stub_hit\n");
389+
//fprintf(stderr, "version limit hit in branch_stub_hit\n");
380390
target_ctx = &generic_ctx;
381391
}
382392

@@ -542,7 +552,7 @@ void gen_direct_jump(
542552
generic_ctx.sp_offset = ctx->sp_offset;
543553
if (get_num_versions(target0) >= MAX_VERSIONS - 1)
544554
{
545-
fprintf(stderr, "version limit hit in gen_direct_jump\n");
555+
//fprintf(stderr, "version limit hit in gen_direct_jump\n");
546556
ctx = &generic_ctx;
547557
}
548558

@@ -588,42 +598,50 @@ void gen_direct_jump(
588598
// Create a stub to force the code up to this point to be executed
589599
void defer_compilation(
590600
block_t* block,
591-
ctx_t* cur_ctx,
592-
uint32_t insn_idx
601+
uint32_t insn_idx,
602+
ctx_t* cur_ctx
593603
)
594604
{
605+
//fprintf(stderr, "defer compilation at (%p, %d) depth=%d\n", block->blockid.iseq, insn_idx, cur_ctx->chain_depth);
595606

607+
if (cur_ctx->chain_depth != 0) {
608+
rb_backtrace();
609+
exit(1);
610+
}
596611

612+
ctx_t next_ctx = *cur_ctx;
597613

614+
if (next_ctx.chain_depth >= UINT8_MAX) {
615+
rb_bug("max block version chain depth reached");
616+
}
598617

618+
next_ctx.chain_depth += 1;
599619

600-
601-
602-
603-
604-
605-
606-
607-
/*
608620
RUBY_ASSERT(num_branches < MAX_BRANCHES);
609621
uint32_t branch_idx = num_branches++;
610622

623+
// Get the branch targets or stubs
624+
blockid_t target0 = (blockid_t){ block->blockid.iseq, insn_idx };
625+
uint8_t* dst_addr0 = get_branch_target(target0, &next_ctx, branch_idx, 0);
626+
627+
// Call the branch generation function
628+
uint32_t start_pos = cb->write_pos;
629+
gen_jump_branch(cb, dst_addr0, NULL, SHAPE_DEFAULT);
630+
uint32_t end_pos = cb->write_pos;
631+
611632
// Register this branch entry
612633
branch_t branch_entry = {
613634
start_pos,
614635
end_pos,
615-
*ctx,
636+
*cur_ctx,
616637
{ target0, BLOCKID_NULL },
617-
{ *ctx, *ctx },
638+
{ next_ctx, next_ctx },
618639
{ dst_addr0, NULL },
619640
gen_jump_branch,
620-
branch_shape
641+
SHAPE_DEFAULT
621642
};
622643

623644
branch_entries[branch_idx] = branch_entry;
624-
*/
625-
626-
627645
}
628646

629647
// Remove all references to a block then free it.

ujit_core.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@
2121
#define MAX_TEMP_TYPES 8
2222

2323
// Default versioning context (no type information)
24-
#define DEFAULT_CTX ( (ctx_t){ { 0 }, 0 } )
24+
#define DEFAULT_CTX ( (ctx_t){ 0 } )
2525

2626
/**
2727
Code generation context
2828
Contains information we can use to optimize code
2929
*/
3030
typedef struct CtxStruct
3131
{
32+
// Depth of this block in the sidechain (eg: inline-cache chain)
33+
uint8_t chain_depth;
34+
3235
// Temporary variable types we keep track of
3336
// Values are `ruby_value_type`
3437
// T_NONE==0 is the unknown type
@@ -61,12 +64,12 @@ typedef struct BlockId
6164
static const blockid_t BLOCKID_NULL = { 0, 0 };
6265

6366
/// Branch code shape enumeration
64-
enum uint8_t
67+
typedef enum branch_shape
6568
{
6669
SHAPE_NEXT0, // Target 0 is next
6770
SHAPE_NEXT1, // Target 1 is next
6871
SHAPE_DEFAULT // Neither target is next
69-
};
72+
} branch_shape_t;
7073

7174
// Branch code generation function signature
7275
typedef void (*branchgen_fn)(codeblock_t* cb, uint8_t* target0, uint8_t* target1, uint8_t shape);
@@ -95,7 +98,7 @@ typedef struct BranchEntry
9598
branchgen_fn gen_fn;
9699

97100
// Shape of the branch
98-
uint8_t shape;
101+
branch_shape_t shape;
99102

100103
} branch_t;
101104

@@ -162,6 +165,12 @@ void gen_direct_jump(
162165
blockid_t target0
163166
);
164167

168+
void defer_compilation(
169+
block_t* block,
170+
uint32_t insn_idx,
171+
ctx_t* cur_ctx
172+
);
173+
165174
void invalidate_block_version(block_t* block);
166175

167176
void ujit_init_core(void);

version.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ ruby_show_version(void)
127127
}
128128

129129
if (rb_ujit_enabled_p()) {
130-
fputs("uJIT is enabled\n", stdout);
130+
fputs("YJIT is enabled\n", stdout);
131131
}
132132
#ifdef RUBY_LAST_COMMIT_TITLE
133133
fputs("last_commit=" RUBY_LAST_COMMIT_TITLE, stdout);

0 commit comments

Comments
 (0)
0