summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/ruby/test_yjit.rb28
-rw-r--r--vm_insnhelper.c6
-rw-r--r--yjit.rb1
-rw-r--r--yjit_codegen.c46
-rw-r--r--yjit_iface.h2
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)
{
diff --git a/yjit.rb b/yjit.rb
index 3c447b4b11..f7e283dc05 100644
--- a/yjit.rb
+++ b/yjit.rb
@@ -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,