@@ -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.
159168static void
160169add_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
233222block_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
662664void
663665invalidate_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)
0 commit comments