RJIT: Optimize Kernel#respond_to?
This commit is contained in:
parent
95c4ced39e
commit
83ad1cac81
@ -2944,6 +2944,95 @@ module RubyVM::RJIT
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @param jit [RubyVM::RJIT::JITState]
|
||||||
|
# @param ctx [RubyVM::RJIT::Context]
|
||||||
|
# @param asm [RubyVM::RJIT::Assembler]
|
||||||
|
def jit_obj_respond_to(jit, ctx, asm, argc, known_recv_class)
|
||||||
|
# respond_to(:sym) or respond_to(:sym, true)
|
||||||
|
if argc != 1 && argc != 2
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if known_recv_class.nil?
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
recv_class = known_recv_class
|
||||||
|
|
||||||
|
# Get the method_id from compile time. We will later add a guard against it.
|
||||||
|
mid_sym = jit.peek_at_stack(argc - 1)
|
||||||
|
unless static_symbol?(mid_sym)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
mid = C.rb_sym2id(mid_sym)
|
||||||
|
|
||||||
|
target_cme = C.rb_callable_method_entry_or_negative(recv_class, mid)
|
||||||
|
|
||||||
|
# Should never be null, as in that case we will be returned a "negative CME"
|
||||||
|
assert_equal(false, target_cme.nil?)
|
||||||
|
|
||||||
|
cme_def_type =
|
||||||
|
if C.UNDEFINED_METHOD_ENTRY_P(target_cme)
|
||||||
|
C::VM_METHOD_TYPE_UNDEF
|
||||||
|
else
|
||||||
|
target_cme.def.type
|
||||||
|
end
|
||||||
|
|
||||||
|
if cme_def_type == C::VM_METHOD_TYPE_REFINED
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
visibility = if cme_def_type == C::VM_METHOD_TYPE_UNDEF
|
||||||
|
C::METHOD_VISI_UNDEF
|
||||||
|
else
|
||||||
|
C.METHOD_ENTRY_VISI(target_cme)
|
||||||
|
end
|
||||||
|
|
||||||
|
result =
|
||||||
|
case visibility
|
||||||
|
in C::METHOD_VISI_UNDEF
|
||||||
|
Qfalse # No method => false
|
||||||
|
in C::METHOD_VISI_PUBLIC
|
||||||
|
Qtrue # Public method => true regardless of include_all
|
||||||
|
else
|
||||||
|
return false # not public and include_all not known, can't compile
|
||||||
|
end
|
||||||
|
|
||||||
|
if result != Qtrue
|
||||||
|
# Only if respond_to_missing? hasn't been overridden
|
||||||
|
# In the future, we might want to jit the call to respond_to_missing?
|
||||||
|
unless Invariants.assume_method_basic_definition(jit, recv_class, C.idRespond_to_missing)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Invalidate this block if method lookup changes for the method being queried. This works
|
||||||
|
# both for the case where a method does or does not exist, as for the latter we asked for a
|
||||||
|
# "negative CME" earlier.
|
||||||
|
Invariants.assume_method_lookup_stable(jit, target_cme)
|
||||||
|
|
||||||
|
# Generate a side exit
|
||||||
|
side_exit = side_exit(jit, ctx)
|
||||||
|
|
||||||
|
if argc == 2
|
||||||
|
# pop include_all argument (we only use its type info)
|
||||||
|
ctx.stack_pop(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
sym_opnd = ctx.stack_pop(1)
|
||||||
|
_recv_opnd = ctx.stack_pop(1)
|
||||||
|
|
||||||
|
# This is necessary because we have no guarantee that sym_opnd is a constant
|
||||||
|
asm.comment('guard known mid')
|
||||||
|
asm.mov(:rax, to_value(mid_sym))
|
||||||
|
asm.cmp(sym_opnd, :rax)
|
||||||
|
asm.jne(side_exit)
|
||||||
|
|
||||||
|
putobject(jit, ctx, asm, val: result)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
# @param jit [RubyVM::RJIT::JITState]
|
# @param jit [RubyVM::RJIT::JITState]
|
||||||
# @param ctx [RubyVM::RJIT::Context]
|
# @param ctx [RubyVM::RJIT::Context]
|
||||||
# @param asm [RubyVM::RJIT::Assembler]
|
# @param asm [RubyVM::RJIT::Assembler]
|
||||||
@ -2999,7 +3088,7 @@ module RubyVM::RJIT
|
|||||||
# rb_ary_empty_p() method in array.c
|
# rb_ary_empty_p() method in array.c
|
||||||
register_cfunc_method(Array, :empty?, :jit_rb_ary_empty_p)
|
register_cfunc_method(Array, :empty?, :jit_rb_ary_empty_p)
|
||||||
|
|
||||||
#register_cfunc_method(Kernel, :respond_to?, :jit_obj_respond_to)
|
register_cfunc_method(Kernel, :respond_to?, :jit_obj_respond_to)
|
||||||
#register_cfunc_method(Kernel, :block_given?, :jit_rb_f_block_given_p)
|
#register_cfunc_method(Kernel, :block_given?, :jit_rb_f_block_given_p)
|
||||||
|
|
||||||
# Thread.current
|
# Thread.current
|
||||||
|
@ -38,6 +38,17 @@ module RubyVM::RJIT
|
|||||||
@cme_blocks[cme.to_i] << jit.block
|
@cme_blocks[cme.to_i] << jit.block
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @param jit [RubyVM::RJIT::JITState]
|
||||||
|
def assume_method_basic_definition(jit, klass, mid)
|
||||||
|
if C.rb_method_basic_definition_p(klass, mid)
|
||||||
|
cme = C.rb_callable_method_entry(klass, mid)
|
||||||
|
assume_method_lookup_stable(jit, cme)
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def assume_stable_constant_names(jit, idlist)
|
def assume_stable_constant_names(jit, idlist)
|
||||||
(0..).each do |i|
|
(0..).each do |i|
|
||||||
break if (id = idlist[i]) == 0
|
break if (id = idlist[i]) == 0
|
||||||
|
1
rjit_c.c
1
rjit_c.c
@ -504,6 +504,7 @@ extern VALUE rb_vm_throw(const rb_execution_context_t *ec, rb_control_frame_t *r
|
|||||||
extern VALUE rb_reg_new_ary(VALUE ary, int opt);
|
extern VALUE rb_reg_new_ary(VALUE ary, int opt);
|
||||||
extern void rb_vm_setclassvariable(const rb_iseq_t *iseq, const rb_control_frame_t *cfp, ID id, VALUE val, ICVARC ic);
|
extern void rb_vm_setclassvariable(const rb_iseq_t *iseq, const rb_control_frame_t *cfp, ID id, VALUE val, ICVARC ic);
|
||||||
extern VALUE rb_str_bytesize(VALUE str);
|
extern VALUE rb_str_bytesize(VALUE str);
|
||||||
|
extern const rb_callable_method_entry_t *rb_callable_method_entry_or_negative(VALUE klass, ID mid);
|
||||||
|
|
||||||
#include "rjit_c.rbinc"
|
#include "rjit_c.rbinc"
|
||||||
|
|
||||||
|
24
rjit_c.rb
24
rjit_c.rb
@ -310,6 +310,25 @@ module RubyVM::RJIT # :nodoc: all
|
|||||||
def rb_obj_class(obj)
|
def rb_obj_class(obj)
|
||||||
Primitive.cexpr! 'rb_obj_class(obj)'
|
Primitive.cexpr! 'rb_obj_class(obj)'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rb_sym2id(sym)
|
||||||
|
Primitive.cexpr! 'SIZET2NUM((size_t)rb_sym2id(sym))'
|
||||||
|
end
|
||||||
|
|
||||||
|
def rb_callable_method_entry_or_negative(klass, mid)
|
||||||
|
cme_addr = Primitive.cexpr! 'SIZET2NUM((size_t)rb_callable_method_entry_or_negative(klass, (ID)NUM2SIZET(mid)))'
|
||||||
|
return nil if cme_addr == 0
|
||||||
|
rb_callable_method_entry_t.new(cme_addr)
|
||||||
|
end
|
||||||
|
|
||||||
|
def rb_method_basic_definition_p(klass, mid)
|
||||||
|
Primitive.cexpr! 'RBOOL(rb_method_basic_definition_p(klass, (ID)NUM2SIZET(mid)))'
|
||||||
|
end
|
||||||
|
|
||||||
|
def UNDEFINED_METHOD_ENTRY_P(cme)
|
||||||
|
_cme_addr = cme.to_i
|
||||||
|
Primitive.cexpr! 'RBOOL(UNDEFINED_METHOD_ENTRY_P((const rb_callable_method_entry_t *)NUM2SIZET(_cme_addr)))'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -352,6 +371,7 @@ module RubyVM::RJIT # :nodoc: all
|
|||||||
C::METHOD_VISI_PRIVATE = Primitive.cexpr! %q{ SIZET2NUM(METHOD_VISI_PRIVATE) }
|
C::METHOD_VISI_PRIVATE = Primitive.cexpr! %q{ SIZET2NUM(METHOD_VISI_PRIVATE) }
|
||||||
C::METHOD_VISI_PROTECTED = Primitive.cexpr! %q{ SIZET2NUM(METHOD_VISI_PROTECTED) }
|
C::METHOD_VISI_PROTECTED = Primitive.cexpr! %q{ SIZET2NUM(METHOD_VISI_PROTECTED) }
|
||||||
C::METHOD_VISI_PUBLIC = Primitive.cexpr! %q{ SIZET2NUM(METHOD_VISI_PUBLIC) }
|
C::METHOD_VISI_PUBLIC = Primitive.cexpr! %q{ SIZET2NUM(METHOD_VISI_PUBLIC) }
|
||||||
|
C::METHOD_VISI_UNDEF = Primitive.cexpr! %q{ SIZET2NUM(METHOD_VISI_UNDEF) }
|
||||||
C::OBJ_TOO_COMPLEX_SHAPE_ID = Primitive.cexpr! %q{ SIZET2NUM(OBJ_TOO_COMPLEX_SHAPE_ID) }
|
C::OBJ_TOO_COMPLEX_SHAPE_ID = Primitive.cexpr! %q{ SIZET2NUM(OBJ_TOO_COMPLEX_SHAPE_ID) }
|
||||||
C::OPTIMIZED_METHOD_TYPE_BLOCK_CALL = Primitive.cexpr! %q{ SIZET2NUM(OPTIMIZED_METHOD_TYPE_BLOCK_CALL) }
|
C::OPTIMIZED_METHOD_TYPE_BLOCK_CALL = Primitive.cexpr! %q{ SIZET2NUM(OPTIMIZED_METHOD_TYPE_BLOCK_CALL) }
|
||||||
C::OPTIMIZED_METHOD_TYPE_CALL = Primitive.cexpr! %q{ SIZET2NUM(OPTIMIZED_METHOD_TYPE_CALL) }
|
C::OPTIMIZED_METHOD_TYPE_CALL = Primitive.cexpr! %q{ SIZET2NUM(OPTIMIZED_METHOD_TYPE_CALL) }
|
||||||
@ -433,6 +453,10 @@ module RubyVM::RJIT # :nodoc: all
|
|||||||
Primitive.cexpr! %q{ SIZET2NUM(block_type_iseq) }
|
Primitive.cexpr! %q{ SIZET2NUM(block_type_iseq) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def C.idRespond_to_missing
|
||||||
|
Primitive.cexpr! %q{ SIZET2NUM(idRespond_to_missing) }
|
||||||
|
end
|
||||||
|
|
||||||
def C.imemo_iseq
|
def C.imemo_iseq
|
||||||
Primitive.cexpr! %q{ SIZET2NUM(imemo_iseq) }
|
Primitive.cexpr! %q{ SIZET2NUM(imemo_iseq) }
|
||||||
end
|
end
|
||||||
|
@ -401,6 +401,7 @@ generator = BindingGenerator.new(
|
|||||||
METHOD_VISI_PRIVATE
|
METHOD_VISI_PRIVATE
|
||||||
METHOD_VISI_PROTECTED
|
METHOD_VISI_PROTECTED
|
||||||
METHOD_VISI_PUBLIC
|
METHOD_VISI_PUBLIC
|
||||||
|
METHOD_VISI_UNDEF
|
||||||
OBJ_TOO_COMPLEX_SHAPE_ID
|
OBJ_TOO_COMPLEX_SHAPE_ID
|
||||||
OPTIMIZED_METHOD_TYPE_BLOCK_CALL
|
OPTIMIZED_METHOD_TYPE_BLOCK_CALL
|
||||||
OPTIMIZED_METHOD_TYPE_CALL
|
OPTIMIZED_METHOD_TYPE_CALL
|
||||||
@ -492,6 +493,7 @@ generator = BindingGenerator.new(
|
|||||||
rb_cTrueClass
|
rb_cTrueClass
|
||||||
rb_rjit_global_events
|
rb_rjit_global_events
|
||||||
rb_mRubyVMFrozenCore
|
rb_mRubyVMFrozenCore
|
||||||
|
idRespond_to_missing
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
funcs: %w[
|
funcs: %w[
|
||||||
|
Loading…
x
Reference in New Issue
Block a user