Skip to content

Commit 783968a

Browse files
authored
Merge pull request ruby#67 from Shopify/yjit-2d-blocks
YJIT: Use 2D array to group block versions
2 parents 748f6ae + 35a2b4a commit 783968a

File tree

6 files changed

+102
-84
lines changed

6 files changed

+102
-84
lines changed

darray.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@
6060
#define rb_darray_foreach(ary, idx_name, elem_ptr_var) \
6161
for (int idx_name = 0; idx_name < rb_darray_size(ary) && ((elem_ptr_var) = rb_darray_ref(ary, idx_name)); ++idx_name)
6262

63+
// Iterate over valid indicies in the array in a for loop
64+
//
65+
#define rb_darray_for(ary, idx_name) \
66+
for (int idx_name = 0; idx_name < rb_darray_size(ary); ++idx_name)
67+
6368
// Make a dynamic array of a certain size. All bytes backing the elements are set to zero.
6469
// Return 1 on success and 0 on failure.
6570
//

ujit_codegen.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,13 +578,27 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
578578

579579

580580

581+
/*
582+
num_versions = count_block_versions(this_instruction);
581583
584+
if (num_versions > N)
585+
return JIT_CANT_COMPILE;
582586
583-
/*
587+
584588
if (defer_compilation(this_instruction, ctx))
585589
return JIT_END_BLOCK;
586590
591+
587592
VALUE top_val = jit_peek_at_stack();
593+
594+
595+
596+
597+
class = get_ruby_class(top_val);
598+
599+
600+
601+
guard_object_class(class, current_instr);
588602
*/
589603

590604

@@ -595,6 +609,9 @@ gen_getinstancevariable(jitstate_t* jit, ctx_t* ctx)
595609

596610

597611

612+
613+
614+
598615
// If the class uses the default allocator, instances should all be T_OBJECT
599616
// NOTE: This assumes nobody changes the allocator of the class after allocation.
600617
// Eventually, we can encode whether an object is T_OBJECT or not

ujit_core.c

Lines changed: 50 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,26 @@ int ctx_diff(const ctx_t* src, const ctx_t* dst)
144144
return diff;
145145
}
146146

147-
static block_t *
148-
get_first_version(const rb_iseq_t *iseq, unsigned idx)
147+
// Get all blocks for a particular place in an iseq.
148+
static rb_ujit_block_array_t
149+
get_version_array(const rb_iseq_t *iseq, unsigned idx)
149150
{
150151
struct rb_iseq_constant_body *body = iseq->body;
152+
151153
if (rb_darray_size(body->ujit_blocks) == 0) {
152154
return NULL;
153155
}
156+
154157
RUBY_ASSERT((unsigned)rb_darray_size(body->ujit_blocks) == body->iseq_size);
155158
return rb_darray_get(body->ujit_blocks, idx);
156159
}
157160

161+
// Count the number of block versions matching a given blockid
162+
static size_t get_num_versions(blockid_t blockid)
163+
{
164+
return rb_darray_size(get_version_array(blockid.iseq, blockid.idx));
165+
}
166+
158167
// Keep track of a block version. Block should be fully constructed.
159168
static void
160169
add_block_version(blockid_t blockid, block_t* block)
@@ -181,19 +190,14 @@ add_block_version(blockid_t blockid, block_t* block)
181190
#endif
182191
}
183192

184-
block_t *first_version = get_first_version(iseq, blockid.idx);
193+
RUBY_ASSERT((int32_t)blockid.idx < rb_darray_size(body->ujit_blocks));
194+
rb_ujit_block_array_t *block_array_ref = rb_darray_ref(body->ujit_blocks, blockid.idx);
185195

186-
// If there exists a version for this block id
187-
if (first_version != NULL) {
188-
// Link to the next version in a linked list
189-
RUBY_ASSERT(block->next == NULL);
190-
block->next = first_version;
196+
// Add the new block
197+
if (!rb_darray_append(block_array_ref, block)) {
198+
rb_bug("allocation failed");
191199
}
192200

193-
// Make new block the first version
194-
rb_darray_set(body->ujit_blocks, blockid.idx, block);
195-
RUBY_ASSERT(find_block_version(blockid, &block->ctx) != NULL);
196-
197201
{
198202
// By writing the new block to the iseq, the iseq now
199203
// contains new references to Ruby objects. Run write barriers.
@@ -214,50 +218,28 @@ add_block_version(blockid_t blockid, block_t* block)
214218
}
215219
}
216220

217-
// Count the number of block versions matching a given blockid
218-
static size_t count_block_versions(blockid_t blockid)
219-
{
220-
size_t count = 0;
221-
block_t *first_version = get_first_version(blockid.iseq, blockid.idx);
222-
223-
// For each version matching the blockid
224-
for (block_t *version = first_version; version != NULL; version = version->next)
225-
{
226-
count += 1;
227-
}
228-
229-
return count;
230-
}
231-
232221
// Retrieve a basic block version for an (iseq, idx) tuple
233222
block_t* find_block_version(blockid_t blockid, const ctx_t* ctx)
234223
{
235-
block_t *first_version = get_first_version(blockid.iseq, blockid.idx);
236-
237-
// If there exists a version for this block id
238-
if (!first_version) return NULL;
224+
rb_ujit_block_array_t versions = get_version_array(blockid.iseq, blockid.idx);
239225

240226
// Best match found
241227
block_t* best_version = NULL;
242228
int best_diff = INT_MAX;
243229

244230
// For each version matching the blockid
245-
for (block_t* version = first_version; version != NULL; version = version->next)
246-
{
231+
rb_darray_for(versions, idx) {
232+
block_t *version = rb_darray_get(versions, idx);
247233
int diff = ctx_diff(ctx, &version->ctx);
248234

249-
if (diff < best_diff)
250-
{
235+
// Note that we always prefer the first matching
236+
// version because of inline-cache chains
237+
if (diff < best_diff) {
251238
best_version = version;
252239
best_diff = diff;
253240
}
254241
}
255242

256-
if (best_version == NULL)
257-
{
258-
return NULL;
259-
}
260-
261243
return best_version;
262244
}
263245

@@ -393,7 +375,7 @@ uint8_t* branch_stub_hit(uint32_t branch_idx, uint32_t target_idx, rb_execution_
393375
ctx_t generic_ctx = DEFAULT_CTX;
394376
generic_ctx.stack_size = target_ctx->stack_size;
395377
generic_ctx.sp_offset = target_ctx->sp_offset;
396-
if (count_block_versions(target) >= MAX_VERSIONS - 1)
378+
if (get_num_versions(target) >= MAX_VERSIONS - 1)
397379
{
398380
fprintf(stderr, "version limit hit in branch_stub_hit\n");
399381
target_ctx = &generic_ctx;
@@ -559,7 +541,7 @@ void gen_direct_jump(
559541
ctx_t generic_ctx = DEFAULT_CTX;
560542
generic_ctx.stack_size = ctx->stack_size;
561543
generic_ctx.sp_offset = ctx->sp_offset;
562-
if (count_block_versions(target0) >= MAX_VERSIONS - 1)
544+
if (get_num_versions(target0) >= MAX_VERSIONS - 1)
563545
{
564546
fprintf(stderr, "version limit hit in gen_direct_jump\n");
565547
ctx = &generic_ctx;
@@ -658,6 +640,26 @@ ujit_free_block(block_t *block)
658640
free(block);
659641
}
660642

643+
// Remove a block version without reordering the version array
644+
static bool
645+
block_array_remove(rb_ujit_block_array_t block_array, block_t *block)
646+
{
647+
bool after_target = false;
648+
block_t **element;
649+
rb_darray_foreach(block_array, idx, element) {
650+
if (after_target) {
651+
rb_darray_set(block_array, idx - 1, *element);
652+
}
653+
else if (*element == block) {
654+
after_target = true;
655+
}
656+
}
657+
658+
if (after_target) rb_darray_pop_back(block_array);
659+
660+
return after_target;
661+
}
662+
661663
// Invalidate one specific block version
662664
void
663665
invalidate_block_version(block_t* block)
@@ -667,25 +669,11 @@ invalidate_block_version(block_t* block)
667669
// fprintf(stderr, "invalidating block (%p, %d)\n", block->blockid.iseq, block->blockid.idx);
668670
// fprintf(stderr, "block=%p\n", block);
669671

670-
block_t *first_block = get_first_version(iseq, block->blockid.idx);
671-
RUBY_ASSERT(first_block != NULL);
672-
673-
// Remove references to this block
674-
if (first_block == block) {
675-
// Make the next block the new first version
676-
rb_darray_set(iseq->body->ujit_blocks, block->blockid.idx, block->next);
677-
}
678-
else {
679-
bool deleted = false;
680-
for (block_t* cur = first_block; cur != NULL; cur = cur->next) {
681-
if (cur->next == block) {
682-
cur->next = cur->next->next;
683-
deleted = true;
684-
break;
685-
}
686-
}
687-
RUBY_ASSERT(deleted);
688-
}
672+
// Remove this block from the version array
673+
rb_ujit_block_array_t versions = get_version_array(iseq, block->blockid.idx);
674+
RB_UNUSED_VAR(bool removed);
675+
removed = block_array_remove(versions, block);
676+
RUBY_ASSERT(removed);
689677

690678
// Get a pointer to the generated code for this block
691679
uint8_t* code_ptr = cb_get_ptr(cb, block->start_pos);
@@ -741,7 +729,7 @@ invalidate_block_version(block_t* block)
741729
// Should check how it's used in exit and side-exit
742730
const void * const *handler_table = rb_vm_get_insns_address_table();
743731
void* handler_addr = (void*)handler_table[entry_opcode];
744-
iseq->body->iseq_encoded[idx] = (VALUE)handler_addr;
732+
iseq->body->iseq_encoded[idx] = (VALUE)handler_addr;
745733

746734
// TODO:
747735
// May want to recompile a new entry point (for interpreter entry blocks)

ujit_core.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,6 @@ typedef struct ujit_block_version
124124
// Offsets for GC managed objects in the mainline code block
125125
int32_array_t gc_object_offsets;
126126

127-
// Next block version for this blockid (singly-linked list)
128-
struct ujit_block_version *next;
129-
130127
// GC managed objects that this block depend on
131128
struct {
132129
VALUE cc;

ujit_iface.c

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -379,12 +379,15 @@ ujit_blocks_for(VALUE mod, VALUE rb_iseq)
379379
}
380380

381381
const rb_iseq_t *iseq = rb_iseqw_to_iseq(rb_iseq);
382-
block_t **element;
382+
383383
VALUE all_versions = rb_ary_new();
384+
rb_darray_for(iseq->body->ujit_blocks, version_array_idx) {
385+
rb_ujit_block_array_t versions = rb_darray_get(iseq->body->ujit_blocks, version_array_idx);
386+
387+
rb_darray_for(versions, block_idx) {
388+
block_t *block = rb_darray_get(versions, block_idx);
384389

385-
rb_darray_foreach(iseq->body->ujit_blocks, idx, element) {
386-
for (block_t *version = *element; version; version = version->next) {
387-
VALUE rb_block = TypedData_Wrap_Struct(cUjitBlock, &ujit_block_type, version);
390+
VALUE rb_block = TypedData_Wrap_Struct(cUjitBlock, &ujit_block_type, block);
388391
rb_ary_push(all_versions, rb_block);
389392
}
390393
}
@@ -677,11 +680,13 @@ print_ujit_stats(void)
677680
void
678681
rb_ujit_iseq_mark(const struct rb_iseq_constant_body *body)
679682
{
680-
block_t **element;
681-
rb_darray_foreach(body->ujit_blocks, idx, element) {
682-
for (block_t *block = *element; block; block = block->next) {
683-
rb_gc_mark_movable((VALUE)block->blockid.iseq);
683+
rb_darray_for(body->ujit_blocks, version_array_idx) {
684+
rb_ujit_block_array_t version_array = rb_darray_get(body->ujit_blocks, version_array_idx);
684685

686+
rb_darray_for(version_array, block_idx) {
687+
block_t *block = rb_darray_get(version_array, block_idx);
688+
689+
rb_gc_mark_movable((VALUE)block->blockid.iseq);
685690
rb_gc_mark_movable(block->dependencies.cc);
686691
rb_gc_mark_movable(block->dependencies.cme);
687692
rb_gc_mark_movable(block->dependencies.iseq);
@@ -703,9 +708,12 @@ rb_ujit_iseq_mark(const struct rb_iseq_constant_body *body)
703708
void
704709
rb_ujit_iseq_update_references(const struct rb_iseq_constant_body *body)
705710
{
706-
block_t **element;
707-
rb_darray_foreach(body->ujit_blocks, idx, element) {
708-
for (block_t *block = *element; block; block = block->next) {
711+
rb_darray_for(body->ujit_blocks, version_array_idx) {
712+
rb_ujit_block_array_t version_array = rb_darray_get(body->ujit_blocks, version_array_idx);
713+
714+
rb_darray_for(version_array, block_idx) {
715+
block_t *block = rb_darray_get(version_array, block_idx);
716+
709717
block->blockid.iseq = (const rb_iseq_t *)rb_gc_location((VALUE)block->blockid.iseq);
710718

711719
block->dependencies.cc = rb_gc_location(block->dependencies.cc);
@@ -734,14 +742,15 @@ rb_ujit_iseq_update_references(const struct rb_iseq_constant_body *body)
734742
void
735743
rb_ujit_iseq_free(const struct rb_iseq_constant_body *body)
736744
{
737-
block_t **element;
738-
rb_darray_foreach(body->ujit_blocks, idx, element) {
739-
block_t *block = *element;
740-
while (block) {
741-
block_t *next = block->next;
745+
rb_darray_for(body->ujit_blocks, version_array_idx) {
746+
rb_ujit_block_array_t version_array = rb_darray_get(body->ujit_blocks, version_array_idx);
747+
748+
rb_darray_for(version_array, block_idx) {
749+
block_t *block = rb_darray_get(version_array, block_idx);
742750
ujit_free_block(block);
743-
block = next;
744751
}
752+
753+
rb_darray_free(version_array);
745754
}
746755

747756
rb_darray_free(body->ujit_blocks);

vm_core.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,9 @@ pathobj_realpath(VALUE pathobj)
303303
/* Forward declarations */
304304
struct rb_mjit_unit;
305305

306+
// List of YJIT block versions
306307
typedef rb_darray(struct ujit_block_version *) rb_ujit_block_array_t;
308+
typedef rb_darray(rb_ujit_block_array_t) rb_ujit_block_array_array_t;
307309

308310
struct rb_iseq_constant_body {
309311
enum iseq_type {
@@ -441,7 +443,7 @@ struct rb_iseq_constant_body {
441443
struct rb_mjit_unit *jit_unit;
442444
#endif
443445

444-
rb_ujit_block_array_t ujit_blocks; // empty, or has a size equal to iseq_size
446+
rb_ujit_block_array_array_t ujit_blocks; // empty, or has a size equal to iseq_size
445447
};
446448

447449
/* T_IMEMO/iseq */

0 commit comments

Comments
 (0)