diff options
-rw-r--r-- | test/ruby/test_yjit.rb | 28 | ||||
-rw-r--r-- | vm_insnhelper.c | 6 | ||||
-rw-r--r-- | yjit.rb | 1 | ||||
-rw-r--r-- | yjit_codegen.c | 46 | ||||
-rw-r--r-- | yjit_iface.h | 2 |
5 files changed, 71 insertions, 12 deletions
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index e6325a4d1c..e00f15ecf5 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -289,6 +289,34 @@ class TestYJIT < Test::Unit::TestCase RUBY end + def test_opt_getinlinecache_slowpath + assert_compiles(<<~RUBY, exits: { opt_getinlinecache: 1 }, result: [42, 42, 1, 1], min_calls: 2) + class A + FOO = 42 + class << self + def foo + _foo = nil + FOO + end + end + end + + result = [] + + result << A.foo + result << A.foo + + class << A + FOO = 1 + end + + result << A.foo + result << A.foo + + result + RUBY + end + def test_string_interpolation assert_compiles(<<~'RUBY', insns: %i[checktype concatstrings], result: "foobar", min_calls: 2) def make_str(foo, bar) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 6c2458f32e..a33f6b5ea3 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -4778,6 +4778,12 @@ vm_ic_hit_p(const struct iseq_inline_constant_cache_entry *ice, const VALUE *reg return vm_inlined_ic_hit_p(ice->flags, ice->value, ice->ic_cref, ice->ic_serial, reg_ep); } +bool +rb_vm_ic_hit_p(IC ic, const VALUE *reg_ep) +{ + return ic->entry && vm_ic_hit_p(ic->entry, reg_ep); +} + static void vm_ic_update(const rb_iseq_t *iseq, IC ic, VALUE val, const VALUE *reg_ep) { @@ -159,6 +159,7 @@ module YJIT print_counters(stats, prefix: 'setivar_', prompt: 'setinstancevariable exit reasons:') print_counters(stats, prefix: 'oaref_', prompt: 'opt_aref exit reasons: ') print_counters(stats, prefix: 'expandarray_', prompt: 'expandarray exit reasons: ') + print_counters(stats, prefix: 'opt_getinlinecache_', prompt: 'opt_getinlinecache exit reasons: ') side_exits = total_exit_count(stats) total_exits = side_exits + stats[:leave_interp_return] diff --git a/yjit_codegen.c b/yjit_codegen.c index ce5fb4e344..2ee00cd464 100644 --- a/yjit_codegen.c +++ b/yjit_codegen.c @@ -4047,25 +4047,47 @@ gen_opt_getinlinecache(jitstate_t* jit, ctx_t* ctx, codeblock_t* cb) // See vm_ic_hit_p(). The same conditions are checked in yjit_constant_ic_update(). struct iseq_inline_constant_cache_entry *ice = ic->entry; if (!ice || // cache not filled - ice->ic_serial != ruby_vm_global_constant_state || // cache out of date - ice->ic_cref /* cache only valid for certain lexical scopes */) { + ice->ic_serial != ruby_vm_global_constant_state /* cache out of date */) { // In these cases, leave a block that unconditionally side exits // for the interpreter to invalidate. return YJIT_CANT_COMPILE; } - // Optimize for single ractor mode. - // FIXME: This leaks when st_insert raises NoMemoryError - if (!assume_single_ractor_mode(jit->block)) return YJIT_CANT_COMPILE; + if (ice->ic_cref) { + // Cache is keyed on a certain lexical scope. Use the interpreter's cache. + uint8_t *side_exit = yjit_side_exit(jit, ctx); - // Invalidate output code on any and all constant writes - // FIXME: This leaks when st_insert raises NoMemoryError - assume_stable_global_constant_state(jit->block); + // Call function to verify the cache + bool rb_vm_ic_hit_p(IC ic, const VALUE *reg_ep); + mov(cb, C_ARG_REGS[0], const_ptr_opnd((void *)ic)); + mov(cb, C_ARG_REGS[1], member_opnd(REG_CFP, rb_control_frame_t, ep)); + call_ptr(cb, REG0, (void *)rb_vm_ic_hit_p); - val_type_t type = yjit_type_of_value(ice->value); - x86opnd_t stack_top = ctx_stack_push(ctx, type); - jit_mov_gc_ptr(jit, cb, REG0, ice->value); - mov(cb, stack_top, REG0); + // Check the result. _Bool is one byte in SysV. + test(cb, AL, AL); + jz_ptr(cb, COUNTED_EXIT(side_exit, opt_getinlinecache_miss)); + + // Push ic->entry->value + mov(cb, REG0, const_ptr_opnd((void *)ic)); + mov(cb, REG0, member_opnd(REG0, struct iseq_inline_constant_cache, entry)); + x86opnd_t stack_top = ctx_stack_push(ctx, TYPE_UNKNOWN); + mov(cb, REG0, member_opnd(REG0, struct iseq_inline_constant_cache_entry, value)); + mov(cb, stack_top, REG0); + } + else { + // Optimize for single ractor mode. + // FIXME: This leaks when st_insert raises NoMemoryError + if (!assume_single_ractor_mode(jit->block)) return YJIT_CANT_COMPILE; + + // Invalidate output code on any and all constant writes + // FIXME: This leaks when st_insert raises NoMemoryError + assume_stable_global_constant_state(jit->block); + + val_type_t type = yjit_type_of_value(ice->value); + x86opnd_t stack_top = ctx_stack_push(ctx, type); + jit_mov_gc_ptr(jit, cb, REG0, ice->value); + mov(cb, stack_top, REG0); + } // Jump over the code for filling the cache uint32_t jump_idx = jit_next_insn_idx(jit) + (int32_t)jump_offset; diff --git a/yjit_iface.h b/yjit_iface.h index 452d27fea8..946ab5bb85 100644 --- a/yjit_iface.h +++ b/yjit_iface.h @@ -82,6 +82,8 @@ YJIT_DECLARE_COUNTERS( oaref_argc_not_one, oaref_arg_not_fixnum, + opt_getinlinecache_miss, + binding_allocations, binding_set, |