ruby-changes:68850
From: Alan <ko1@a...>
Date: Thu, 21 Oct 2021 08:14:24 +0900 (JST)
Subject: [ruby-changes:68850] ec1cbbb07d (master): Get rid of dependency on rb_call_cache
https://git.ruby-lang.org/ruby.git/commit/?id=ec1cbbb07d From ec1cbbb07d00828e6265074ca4977a8dae6b8b29 Mon Sep 17 00:00:00 2001 From: Alan Wu <XrXr@u...> Date: Wed, 17 Mar 2021 19:07:20 -0400 Subject: Get rid of dependency on rb_call_cache --- common.mk | 1 + vm_method.c | 8 +- yjit.h | 4 +- yjit_codegen.c | 4 +- yjit_core.c | 4 +- yjit_core.h | 9 +- yjit_iface.c | 291 +++++++++++++++++++++++++++++++++++++++++---------------- yjit_iface.h | 2 +- 8 files changed, 232 insertions(+), 91 deletions(-) diff --git a/common.mk b/common.mk index d78351bb28..0b2ba1953c 100644 --- a/common.mk +++ b/common.mk @@ -17319,6 +17319,7 @@ yjit_iface.$(OBJEXT): {$(VPATH)}config.h https://github.com/ruby/ruby/blob/trunk/common.mk#L17319 yjit_iface.$(OBJEXT): {$(VPATH)}darray.h yjit_iface.$(OBJEXT): {$(VPATH)}debug_counter.h yjit_iface.$(OBJEXT): {$(VPATH)}defines.h +yjit_iface.$(OBJEXT): {$(VPATH)}gc.h yjit_iface.$(OBJEXT): {$(VPATH)}id.h yjit_iface.$(OBJEXT): {$(VPATH)}id_table.h yjit_iface.$(OBJEXT): {$(VPATH)}insns.def diff --git a/vm_method.c b/vm_method.c index 5fd8f6a8bc..5260f2d05c 100644 --- a/vm_method.c +++ b/vm_method.c @@ -123,7 +123,6 @@ rb_vm_cc_invalidate(const struct rb_callcache *cc) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L123 VM_ASSERT(cc->klass != 0); // should be enable *(VALUE *)&cc->klass = 0; - rb_yjit_method_lookup_change((VALUE)cc); RB_DEBUG_COUNTER_INC(cc_ent_invalidate); } @@ -135,7 +134,8 @@ vm_cme_invalidate(rb_callable_method_entry_t *cme) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L134 VM_ASSERT(callable_method_entry_p(cme)); METHOD_ENTRY_INVALIDATED_SET(cme); RB_DEBUG_COUNTER_INC(cc_cme_invalidate); - rb_yjit_method_lookup_change((VALUE)cme); + + rb_yjit_cme_invalidate((VALUE)cme); } void @@ -238,6 +238,8 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L238 invalidate_negative_cache(mid); } } + + rb_yjit_method_lookup_change(klass, mid); } static void @@ -305,6 +307,8 @@ void https://github.com/ruby/ruby/blob/trunk/vm_method.c#L307 rb_clear_method_cache_all(void) { rb_objspace_each_objects(invalidate_all_cc, NULL); + + rb_yjit_invalidate_all_method_lookup_assumptions(); } void diff --git a/yjit.h b/yjit.h index f2dfb31142..b72de1a16e 100644 --- a/yjit.h +++ b/yjit.h @@ -47,8 +47,10 @@ bool rb_yjit_enabled_p(void); https://github.com/ruby/ruby/blob/trunk/yjit.h#L47 unsigned rb_yjit_call_threshold(void); RUBY_SYMBOL_EXPORT_END +void rb_yjit_invalidate_all_method_lookup_assumptions(void); +void rb_yjit_method_lookup_change(VALUE klass, ID mid); +void rb_yjit_cme_invalidate(VALUE cme); void rb_yjit_collect_vm_usage_insn(int insn); -void rb_yjit_method_lookup_change(VALUE cme_or_cc); void rb_yjit_compile_iseq(const rb_iseq_t *iseq, rb_execution_context_t *ec); void rb_yjit_init(struct rb_yjit_options *options); void rb_yjit_bop_redefined(VALUE klass, const rb_method_entry_t *me, enum ruby_basic_operators bop); diff --git a/yjit_codegen.c b/yjit_codegen.c index e1d27c5b45..bd3ab5543c 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -1399,7 +1399,7 @@ gen_oswb_cfunc(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_c https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1399 x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass)); // FIXME: This leaks when st_insert raises NoMemoryError - assume_method_lookup_stable(cd->cc, cme, jit->block); + assume_method_lookup_stable(cd->cc->klass, cme, jit->block); // Bail if receiver class is different from compile-time call cache class jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass); @@ -1632,7 +1632,7 @@ gen_oswb_iseq(jitstate_t* jit, ctx_t* ctx, struct rb_call_data * cd, const rb_ca https://github.com/ruby/ruby/blob/trunk/yjit_codegen.c#L1632 // Pointer to the klass field of the receiver &(recv->klass) x86opnd_t klass_opnd = mem_opnd(64, REG0, offsetof(struct RBasic, klass)); - assume_method_lookup_stable(cd->cc, cme, jit->block); + assume_method_lookup_stable(cd->cc->klass, cme, jit->block); // Bail if receiver class is different from compile-time call cache class jit_mov_gc_ptr(jit, cb, REG1, (VALUE)cd->cc->klass); diff --git a/yjit_core.c b/yjit_core.c index bd1f6bdd59..cfd1abf901 100644 --- a/yjit_core.c +++ b/yjit_core.c @@ -209,8 +209,8 @@ add_block_version(blockid_t blockid, block_t* block) https://github.com/ruby/ruby/blob/trunk/yjit_core.c#L209 { // By writing the new block to the iseq, the iseq now // contains new references to Ruby objects. Run write barriers. - RB_OBJ_WRITTEN(iseq, Qundef, block->dependencies.cc); - RB_OBJ_WRITTEN(iseq, Qundef, block->dependencies.cme); + RB_OBJ_WRITTEN(iseq, Qundef, block->receiver_klass); + RB_OBJ_WRITTEN(iseq, Qundef, block->callee_cme); // Run write barriers for all objects in generated code. uint32_t *offset_element; diff --git a/yjit_core.h b/yjit_core.h index c9a2c5c888..1467395210 100644 --- a/yjit_core.h +++ b/yjit_core.h @@ -127,11 +127,10 @@ typedef struct yjit_block_version https://github.com/ruby/ruby/blob/trunk/yjit_core.h#L127 // Offsets for GC managed objects in the mainline code block int32_array_t gc_object_offsets; - // GC managed objects that this block depend on - struct { - VALUE cc; - VALUE cme; - } dependencies; + // In case this block is invalidated, these two pieces of info + // help to remove all pointers to this block in the system. + VALUE receiver_klass; + VALUE callee_cme; // Index one past the last instruction in the iseq uint32_t end_idx; diff --git a/yjit_iface.c b/yjit_iface.c index 0229f8e1ba..e5789d3dbc 100644 --- a/yjit_iface.c +++ b/yjit_iface.c @@ -5,6 +5,7 @@ https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L5 #include "vm_sync.h" #include "vm_callinfo.h" #include "builtin.h" +#include "gc.h" #include "internal/compile.h" #include "internal/class.h" #include "insns_info.inc" @@ -141,66 +142,112 @@ struct yjit_root_struct { https://github.com/ruby/ruby/blob/trunk/yjit_iface.c#L142 int unused; // empty structs are not legal in C99 }; -static void -block_array_shuffle_remove(rb_yjit_block_array_t blocks, block_t *to_remove) { - block_t **elem; - rb_darray_foreach(blocks, i, elem) { - if (*elem == to_remove) { - // Remove the current element by moving the last element here then popping. - *elem = rb_darray_get(blocks, rb_darray_size(blocks) - 1); - rb_darray_pop_back(blocks); - break; +// Hash table of BOP blocks +static st_table *blocks_assuming_bops; + +bool +assume_bop_not_redefined(block_t *block, int redefined_flag, enum ruby_basic_operators bop) +{ + if (BASIC_OP_UNREDEFINED_P(bop, redefined_flag)) { + if (blocks_assuming_bops) { + st_insert(blocks_assuming_bops, (st_data_t)block, 0); } + return true; + } + else { + return false; } } -// Map cme_or_cc => [block] +// Map klass => id_table[mid, set of blocks] +// While a block `b` is in the table, b->callee_cme == rb_callable_method_entry(klass, mid). +// See assume_method_lookup_stable() static st_table *method_lookup_dependency; +// For adding to method_lookup_dependency data with st_update +struct lookup_dependency_insertion { + block_t *block; + ID mid; +}; + +// Map cme => set of blocks +// See assume_method_lookup_stable() +static st_table *cme_validity_dependency; + static int -add_lookup_dependency_i(st_data_t *key, st_data_t *value, st_data_t data, int existing) +add_cme_validity_dependency_i(st_data_t *key, st_data_t *value, st_data_t new_block, int existing) { - block_t *new_block = (block_t *)data; - - rb_yjit_block_array_t blocks = NULL; + st_table *block_set; if (existing) { - blocks = (rb_yjit_block_array_t)*value; + block_set = (st_table *)*value; } - if (!rb_darray_append(&blocks, new_block)) { - rb_bug("yjit: failed to add method lookup dependency"); // TODO: we could bail out of compiling instead + else { + // Make the set and put it into cme_validity_dependency + block_set = st_init_numtable(); + *value = (st_data_t)block_set; } - *value = (st_data_t)blocks; + // Put block into set + st_insert(block_set, new_block, 1); + return ST_CONTINUE; } -// Hash table of BOP blocks -static st_table *blocks_assuming_bops; - -bool -assume_bop_not_redefined(block_t *block, int redefined_flag, enum ruby_basic_operators bop) +static int +add_lookup_dependency_i(st_data_t *key, st_data_t *value, st_data_t data, int existing) { - if (BASIC_OP_UNREDEFINED_P(bop, redefined_flag)) { - if (blocks_assuming_bops) { - st_insert(blocks_assuming_bops, (st_data_t)block, 0); - } - return true; + struct lookup_dependency_insertion *info = (void *)data; + + // Find or make an id table + struct rb_id_table *id2blocks; + if (existing) { + id2blocks = (void *)*value; } else { - return false; + // Make an id table and put it into the st_table + id2blocks = rb_id_table_create(1); + *value = (st_data_t)id2blocks; + } + + // Find or make a block set + st_table *block_set; + { + VALUE blocks; + if (rb_id_table_lookup(id2blocks, info->mid, &blocks)) { + // Take existing set + block_set = (st_table *)blocks; + } + else { + // Make new block set and put it into the id table + block_set = st_init_numtable(); + rb_id_table_insert(id2blocks, info->mid, (VALUE)block_set); + } } + + st_insert(block_set, (st_data_t)info->block, 1); + + return ST_CONTINUE; } -// Remember that the currently (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/