YJIT: Fallback opt_getconstant_path for const_missing (#8623)
* YJIT: Fallback opt_getconstant_path for const_missing * Fix a comment [ci skip] * Remove a wrapper function
This commit is contained in:
parent
0bf1749e9f
commit
5808999d30
15
insns.def
15
insns.def
@ -260,20 +260,7 @@ opt_getconstant_path
|
|||||||
(VALUE val)
|
(VALUE val)
|
||||||
// attr bool leaf = false; /* may autoload or raise */
|
// attr bool leaf = false; /* may autoload or raise */
|
||||||
{
|
{
|
||||||
const ID *segments = ic->segments;
|
val = rb_vm_opt_getconstant_path(ec, GET_CFP(), ic);
|
||||||
struct iseq_inline_constant_cache_entry *ice = ic->entry;
|
|
||||||
if (ice && vm_ic_hit_p(ice, GET_EP())) {
|
|
||||||
val = ice->value;
|
|
||||||
|
|
||||||
VM_ASSERT(val == vm_get_ev_const_chain(ec, segments));
|
|
||||||
} else {
|
|
||||||
ruby_vm_constant_cache_misses++;
|
|
||||||
val = vm_get_ev_const_chain(ec, segments);
|
|
||||||
vm_ic_track_const_chain(GET_CFP(), ic, segments);
|
|
||||||
// Because leaf=false, we need to undo the PC increment to get the address to this instruction
|
|
||||||
// INSN_ATTR(width) == 2
|
|
||||||
vm_ic_update(GET_ISEQ(), ic, val, GET_EP(), GET_PC() - 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get constant variable id. If klass is Qnil and allow_nil is Qtrue, constants
|
/* Get constant variable id. If klass is Qnil and allow_nil is Qtrue, constants
|
||||||
|
@ -476,6 +476,32 @@ class TestYJIT < Test::Unit::TestCase
|
|||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_opt_getconstant_path_general
|
||||||
|
assert_compiles(<<~RUBY, result: [1, 1])
|
||||||
|
module Base
|
||||||
|
Const = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
class Sub
|
||||||
|
def const
|
||||||
|
_const = nil # make a non-entry block for opt_getconstant_path
|
||||||
|
Const
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.const_missing(n)
|
||||||
|
Base.const_get(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
sub = Sub.new
|
||||||
|
result = []
|
||||||
|
result << sub.const # generate the general case
|
||||||
|
result << sub.const # const_missing does not invalidate the block
|
||||||
|
result
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
def test_string_interpolation
|
def test_string_interpolation
|
||||||
assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "foobar", call_threshold: 2)
|
assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "foobar", call_threshold: 2)
|
||||||
def make_str(foo, bar)
|
def make_str(foo, bar)
|
||||||
@ -1257,7 +1283,7 @@ class TestYJIT < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_setivar_on_class
|
def test_setivar_on_class
|
||||||
# Bug in https://github.com/ruby/ruby/pull/8152
|
# Bug in https://github.com/ruby/ruby/pull/8152
|
||||||
assert_compiles(<<~RUBY, result: :ok, exits: { opt_getconstant_path: 2 })
|
assert_compiles(<<~RUBY, result: :ok)
|
||||||
class Base
|
class Base
|
||||||
def self.or_equal
|
def self.or_equal
|
||||||
@or_equal ||= Object.new
|
@or_equal ||= Object.new
|
||||||
@ -1280,7 +1306,7 @@ class TestYJIT < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_nested_send
|
def test_nested_send
|
||||||
#[Bug #19464]
|
#[Bug #19464]
|
||||||
assert_compiles(<<~RUBY, result: [:ok, :ok], exits: { opt_getconstant_path: 1, defineclass: 1 })
|
assert_compiles(<<~RUBY, result: [:ok, :ok], exits: { defineclass: 1 })
|
||||||
klass = Class.new do
|
klass = Class.new do
|
||||||
class << self
|
class << self
|
||||||
alias_method :my_send, :send
|
alias_method :my_send, :send
|
||||||
|
@ -18,22 +18,8 @@ class TestYJITExitLocations < Test::Unit::TestCase
|
|||||||
refute_includes(stderr, "NoMethodError")
|
refute_includes(stderr, "NoMethodError")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_trace_exits_setclassvariable
|
def test_trace_exits_expandarray_splat
|
||||||
script = 'class Foo; def self.foo; @@foo = 1; end; end; Foo.foo'
|
assert_exit_locations('*arr = []')
|
||||||
assert_exit_locations(script)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_trace_exits_putobject
|
|
||||||
assert_exit_locations('true')
|
|
||||||
assert_exit_locations('123')
|
|
||||||
assert_exit_locations(':foo')
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_trace_exits_opt_not
|
|
||||||
assert_exit_locations('!false')
|
|
||||||
assert_exit_locations('!nil')
|
|
||||||
assert_exit_locations('!true')
|
|
||||||
assert_exit_locations('![]')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -5846,6 +5846,27 @@ vm_ic_update(const rb_iseq_t *iseq, IC ic, VALUE val, const VALUE *reg_ep, const
|
|||||||
rb_rjit_constant_ic_update(iseq, ic, pos);
|
rb_rjit_constant_ic_update(iseq, ic, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALUE
|
||||||
|
rb_vm_opt_getconstant_path(rb_execution_context_t *ec, rb_control_frame_t *const reg_cfp, IC ic)
|
||||||
|
{
|
||||||
|
VALUE val;
|
||||||
|
const ID *segments = ic->segments;
|
||||||
|
struct iseq_inline_constant_cache_entry *ice = ic->entry;
|
||||||
|
if (ice && vm_ic_hit_p(ice, GET_EP())) {
|
||||||
|
val = ice->value;
|
||||||
|
|
||||||
|
VM_ASSERT(val == vm_get_ev_const_chain(ec, segments));
|
||||||
|
} else {
|
||||||
|
ruby_vm_constant_cache_misses++;
|
||||||
|
val = vm_get_ev_const_chain(ec, segments);
|
||||||
|
vm_ic_track_const_chain(GET_CFP(), ic, segments);
|
||||||
|
// Undo the PC increment to get the address to this instruction
|
||||||
|
// INSN_ATTR(width) == 2
|
||||||
|
vm_ic_update(GET_ISEQ(), ic, val, GET_EP(), GET_PC() - 2);
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
vm_once_dispatch(rb_execution_context_t *ec, ISEQ iseq, ISE is)
|
vm_once_dispatch(rb_execution_context_t *ec, ISEQ iseq, ISE is)
|
||||||
{
|
{
|
||||||
|
@ -8134,19 +8134,33 @@ fn gen_opt_getconstant_path(
|
|||||||
let ic: *const iseq_inline_constant_cache = const_cache_as_value.as_ptr();
|
let ic: *const iseq_inline_constant_cache = const_cache_as_value.as_ptr();
|
||||||
let idlist: *const ID = unsafe { (*ic).segments };
|
let idlist: *const ID = unsafe { (*ic).segments };
|
||||||
|
|
||||||
// See vm_ic_hit_p(). The same conditions are checked in yjit_constant_ic_update().
|
|
||||||
let ice = unsafe { (*ic).entry };
|
|
||||||
if ice.is_null() {
|
|
||||||
// In this case, leave a block that unconditionally side exits
|
|
||||||
// for the interpreter to invalidate.
|
|
||||||
gen_counter_incr(asm, Counter::opt_getconstant_path_no_ic_entry);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure there is an exit for this block as the interpreter might want
|
// Make sure there is an exit for this block as the interpreter might want
|
||||||
// to invalidate this block from yjit_constant_ic_update().
|
// to invalidate this block from yjit_constant_ic_update().
|
||||||
jit_ensure_block_entry_exit(jit, asm, ocb);
|
jit_ensure_block_entry_exit(jit, asm, ocb);
|
||||||
|
|
||||||
|
// See vm_ic_hit_p(). The same conditions are checked in yjit_constant_ic_update().
|
||||||
|
// If a cache is not filled, fallback to the general C call.
|
||||||
|
let ice = unsafe { (*ic).entry };
|
||||||
|
if ice.is_null() {
|
||||||
|
// Prepare for const_missing
|
||||||
|
jit_prepare_routine_call(jit, asm);
|
||||||
|
|
||||||
|
// If this does not trigger const_missing, vm_ic_update will invalidate this block.
|
||||||
|
extern "C" {
|
||||||
|
fn rb_vm_opt_getconstant_path(ec: EcPtr, cfp: CfpPtr, ic: *const u8) -> VALUE;
|
||||||
|
}
|
||||||
|
let val = asm.ccall(
|
||||||
|
rb_vm_opt_getconstant_path as *const u8,
|
||||||
|
vec![EC, CFP, Opnd::const_ptr(ic as *const u8)],
|
||||||
|
);
|
||||||
|
|
||||||
|
let stack_top = asm.stack_push(Type::Unknown);
|
||||||
|
asm.store(stack_top, val);
|
||||||
|
|
||||||
|
jump_to_next_insn(jit, asm, ocb);
|
||||||
|
return Some(EndBlock);
|
||||||
|
}
|
||||||
|
|
||||||
if !unsafe { (*ice).ic_cref }.is_null() {
|
if !unsafe { (*ice).ic_cref }.is_null() {
|
||||||
// Cache is keyed on a certain lexical scope. Use the interpreter's cache.
|
// Cache is keyed on a certain lexical scope. Use the interpreter's cache.
|
||||||
let inline_cache = asm.load(Opnd::const_ptr(ic as *const u8));
|
let inline_cache = asm.load(Opnd::const_ptr(ic as *const u8));
|
||||||
|
@ -404,7 +404,6 @@ make_counters! {
|
|||||||
opt_case_dispatch_megamorphic,
|
opt_case_dispatch_megamorphic,
|
||||||
|
|
||||||
opt_getconstant_path_ic_miss,
|
opt_getconstant_path_ic_miss,
|
||||||
opt_getconstant_path_no_ic_entry,
|
|
||||||
opt_getconstant_path_multi_ractor,
|
opt_getconstant_path_multi_ractor,
|
||||||
|
|
||||||
expandarray_splat,
|
expandarray_splat,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user