зеркало из https://github.com/github/ruby.git
RJIT: Initial support of splat
This commit is contained in:
Родитель
466aa8010f
Коммит
1dd65f7c55
|
@ -449,6 +449,10 @@ module RubyVM::RJIT
|
|||
|
||||
def jne(dst)
|
||||
case dst
|
||||
# JNE rel8
|
||||
in Label => dst_label
|
||||
# 75 cb
|
||||
insn(opcode: 0x75, imm: dst_label)
|
||||
# JNE rel32
|
||||
in Integer => dst_addr
|
||||
# 0F 85 cd
|
||||
|
|
|
@ -3315,6 +3315,31 @@ module RubyVM::RJIT
|
|||
asm.jne(side_exit)
|
||||
end
|
||||
|
||||
# clobbers object_reg
|
||||
def guard_object_is_not_ruby2_keyword_hash(asm, object_reg, flags_reg, side_exit)
|
||||
asm.comment('guard object is not ruby2 keyword hash')
|
||||
|
||||
not_ruby2_keyword = asm.new_label('not_ruby2_keyword')
|
||||
asm.test(object_reg, C::RUBY_IMMEDIATE_MASK)
|
||||
asm.jnz(not_ruby2_keyword)
|
||||
|
||||
asm.cmp(object_reg, Qfalse)
|
||||
asm.je(not_ruby2_keyword)
|
||||
|
||||
asm.mov(flags_reg, [object_reg, C.RBasic.offsetof(:flags)])
|
||||
type_reg = object_reg
|
||||
asm.mov(type_reg, flags_reg)
|
||||
asm.and(type_reg, C::RUBY_T_MASK)
|
||||
|
||||
asm.cmp(type_reg, C::RUBY_T_HASH)
|
||||
asm.jne(not_ruby2_keyword)
|
||||
|
||||
asm.test(flags_reg, C::RHASH_PASS_AS_KEYWORDS)
|
||||
asm.jnz(side_exit)
|
||||
|
||||
asm.write_label(not_ruby2_keyword)
|
||||
end
|
||||
|
||||
# @param jit [RubyVM::RJIT::JITState]
|
||||
# @param ctx [RubyVM::RJIT::Context]
|
||||
# @param asm [RubyVM::RJIT::Assembler]
|
||||
|
@ -4028,7 +4053,7 @@ module RubyVM::RJIT
|
|||
end
|
||||
end
|
||||
|
||||
# vm_call_iseq_setup
|
||||
# vm_call_iseq_setup (ISEQ only)
|
||||
# @param jit [RubyVM::RJIT::JITState]
|
||||
# @param ctx [RubyVM::RJIT::Context]
|
||||
# @param asm [RubyVM::RJIT::Assembler]
|
||||
|
@ -4051,7 +4076,26 @@ module RubyVM::RJIT
|
|||
# @param ctx [RubyVM::RJIT::Context]
|
||||
# @param asm [RubyVM::RJIT::Assembler]
|
||||
def jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq, block_handler, opt_pc, send_shift:, frame_type:, prev_ep: nil)
|
||||
# We will not have side exits from here. Adjust the stack.
|
||||
# Push splat args, which was skipped in jit_caller_setup_arg.
|
||||
if flags & C::VM_CALL_ARGS_SPLAT != 0
|
||||
if iseq.body.param.opt_num != 0
|
||||
asm.incr_counter(:send_args_splat_opt_num) # not supported yet
|
||||
return CantCompile
|
||||
end
|
||||
|
||||
array_length = jit.peek_at_stack(flags & C::VM_CALL_ARGS_BLOCKARG != 0 ? 1 : 0)&.length || 0 # blockarg is not popped yet
|
||||
if iseq.body.param.lead_num != array_length + argc - 1
|
||||
asm.incr_counter(:send_args_splat_arity_error)
|
||||
return CantCompile
|
||||
end
|
||||
|
||||
# We are going to assume that the splat fills all the remaining arguments.
|
||||
# In the generated code we test if this is true and if not side exit.
|
||||
argc = argc - 1 + array_length
|
||||
jit_caller_setup_arg_splat(jit, ctx, asm, array_length)
|
||||
end
|
||||
|
||||
# We will not have side exits from here. Adjust the stack, which was skipped in jit_call_opt_send.
|
||||
if flags & C::VM_CALL_OPT_SEND != 0
|
||||
jit_call_opt_send_shift_stack(ctx, asm, argc, send_shift:)
|
||||
end
|
||||
|
@ -4316,8 +4360,7 @@ module RubyVM::RJIT
|
|||
asm.incr_counter(:send_optimized_send_send)
|
||||
return CantCompile
|
||||
end
|
||||
# Ideally, we want to shift the stack here, but it's not safe until you reach the point
|
||||
# where you never exit. `send_shift` signals to lazily shift the stack by this amount.
|
||||
# Lazily handle stack shift in jit_call_iseq_setup_normal
|
||||
send_shift += 1
|
||||
|
||||
kw_splat = flags & C::VM_CALL_KW_SPLAT != 0
|
||||
|
@ -4426,6 +4469,7 @@ module RubyVM::RJIT
|
|||
EndBlock
|
||||
end
|
||||
|
||||
# vm_call_opt_send (lazy part)
|
||||
# @param ctx [RubyVM::RJIT::Context]
|
||||
# @param asm [RubyVM::RJIT::Assembler]
|
||||
def jit_call_opt_send_shift_stack(ctx, asm, argc, send_shift:)
|
||||
|
@ -4613,14 +4657,14 @@ module RubyVM::RJIT
|
|||
asm.mov([EC, C.rb_execution_context_t.offsetof(:cfp)], cfp_reg)
|
||||
end
|
||||
|
||||
# vm_callee_setup_arg: Set up args and return opt_pc (or CantCompile)
|
||||
# vm_callee_setup_arg (ISEQ only): Set up args and return opt_pc (or CantCompile)
|
||||
# @param jit [RubyVM::RJIT::JITState]
|
||||
# @param ctx [RubyVM::RJIT::Context]
|
||||
# @param asm [RubyVM::RJIT::Assembler]
|
||||
def jit_callee_setup_arg(jit, ctx, asm, flags, argc, iseq)
|
||||
if flags & C::VM_CALL_KW_SPLAT == 0
|
||||
if C.rb_simple_iseq_p(iseq)
|
||||
if jit_caller_setup_arg(jit, ctx, asm, flags) == CantCompile
|
||||
if jit_caller_setup_arg(jit, ctx, asm, flags, splat: true) == CantCompile
|
||||
return CantCompile
|
||||
end
|
||||
if jit_caller_remove_empty_kw_splat(jit, ctx, asm, flags) == CantCompile
|
||||
|
@ -4806,13 +4850,18 @@ module RubyVM::RJIT
|
|||
# @param jit [RubyVM::RJIT::JITState]
|
||||
# @param ctx [RubyVM::RJIT::Context]
|
||||
# @param asm [RubyVM::RJIT::Assembler]
|
||||
def jit_caller_setup_arg(jit, ctx, asm, flags)
|
||||
def jit_caller_setup_arg(jit, ctx, asm, flags, splat: false)
|
||||
if flags & C::VM_CALL_ARGS_SPLAT != 0 && flags & C::VM_CALL_KW_SPLAT != 0
|
||||
asm.incr_counter(:send_args_splat_kw_splat)
|
||||
return CantCompile
|
||||
elsif flags & C::VM_CALL_ARGS_SPLAT != 0
|
||||
asm.incr_counter(:send_args_splat)
|
||||
return CantCompile
|
||||
if splat
|
||||
# Lazily handle splat in jit_call_iseq_setup_normal
|
||||
else
|
||||
# splat is not supported in this path
|
||||
asm.incr_counter(:send_args_splat)
|
||||
return CantCompile
|
||||
end
|
||||
elsif flags & C::VM_CALL_KW_SPLAT != 0
|
||||
asm.incr_counter(:send_args_kw_splat)
|
||||
return CantCompile
|
||||
|
@ -4822,6 +4871,73 @@ module RubyVM::RJIT
|
|||
end
|
||||
end
|
||||
|
||||
# vm_caller_setup_arg_splat (+ CALLER_SETUP_ARG):
|
||||
# Pushes arguments from an array to the stack that are passed with a splat (i.e. *args).
|
||||
# It optimistically compiles to a static size that is the exact number of arguments needed for the function.
|
||||
# @param jit [RubyVM::RJIT::JITState]
|
||||
# @param ctx [RubyVM::RJIT::Context]
|
||||
# @param asm [RubyVM::RJIT::Assembler]
|
||||
def jit_caller_setup_arg_splat(jit, ctx, asm, required_args)
|
||||
side_exit = side_exit(jit, ctx)
|
||||
|
||||
asm.comment('push_splat_args')
|
||||
|
||||
array_opnd = ctx.stack_opnd(0)
|
||||
array_reg = :rax
|
||||
asm.mov(array_reg, array_opnd)
|
||||
|
||||
guard_object_is_heap(asm, array_reg, counted_exit(side_exit, :send_args_splat_not_array))
|
||||
guard_object_is_array(asm, array_reg, :rcx, counted_exit(side_exit, :send_args_splat_not_array))
|
||||
|
||||
array_len_opnd = :rcx
|
||||
jit_array_len(asm, array_reg, array_len_opnd)
|
||||
|
||||
asm.comment('Side exit if length is not equal to remaining args')
|
||||
asm.cmp(array_len_opnd, required_args)
|
||||
asm.jne(counted_exit(side_exit, :send_args_splat_length_not_equal))
|
||||
|
||||
asm.comment('Check last argument is not ruby2keyword hash')
|
||||
|
||||
ary_opnd = :rcx
|
||||
jit_array_ptr(asm, array_reg, ary_opnd) # clobbers array_reg
|
||||
|
||||
last_array_value = :rax
|
||||
asm.mov(last_array_value, [ary_opnd, (required_args - 1) * C.VALUE.size])
|
||||
|
||||
ruby2_exit = counted_exit(side_exit, :send_args_splat_ruby2_hash);
|
||||
guard_object_is_not_ruby2_keyword_hash(asm, last_array_value, :rcx, ruby2_exit) # clobbers :rax
|
||||
|
||||
asm.comment('Push arguments from array')
|
||||
array_opnd = ctx.stack_pop(1)
|
||||
|
||||
if required_args > 0
|
||||
# Load the address of the embedded array
|
||||
# (struct RArray *)(obj)->as.ary
|
||||
array_reg = :rax
|
||||
asm.mov(array_reg, array_opnd)
|
||||
|
||||
# Conditionally load the address of the heap array
|
||||
# (struct RArray *)(obj)->as.heap.ptr
|
||||
flags_opnd = [array_reg, C.RBasic.offsetof(:flags)]
|
||||
asm.test(flags_opnd, C::RARRAY_EMBED_FLAG)
|
||||
heap_ptr_opnd = [array_reg, C.RArray.offsetof(:as, :heap, :ptr)]
|
||||
# Load the address of the embedded array
|
||||
# (struct RArray *)(obj)->as.ary
|
||||
asm.lea(:rcx, [array_reg, C.RArray.offsetof(:as, :ary)])
|
||||
asm.mov(:rax, heap_ptr_opnd)
|
||||
asm.cmovnz(:rax, :rcx)
|
||||
ary_opnd = :rax
|
||||
|
||||
(0...required_args).each do |i|
|
||||
top = ctx.stack_push
|
||||
asm.mov(:rcx, [ary_opnd, i * C.VALUE.size])
|
||||
asm.mov(top, :rcx)
|
||||
end
|
||||
|
||||
asm.comment('end push_each')
|
||||
end
|
||||
end
|
||||
|
||||
# CALLER_REMOVE_EMPTY_KW_SPLAT: Return CantCompile if not supported
|
||||
# @param jit [RubyVM::RJIT::JITState]
|
||||
# @param ctx [RubyVM::RJIT::Context]
|
||||
|
@ -4853,6 +4969,20 @@ module RubyVM::RJIT
|
|||
asm.cmovz(len_reg, [array_reg, C.RArray.offsetof(:as, :heap, :len)])
|
||||
end
|
||||
|
||||
# Generate RARRAY_CONST_PTR_TRANSIENT (part of RARRAY_AREF)
|
||||
def jit_array_ptr(asm, array_reg, ary_opnd)
|
||||
asm.comment('get array pointer for embedded or heap')
|
||||
|
||||
flags_opnd = [array_reg, C.RBasic.offsetof(:flags)]
|
||||
asm.test(flags_opnd, C::RARRAY_EMBED_FLAG)
|
||||
heap_ptr_opnd = [array_reg, C.RArray.offsetof(:as, :heap, :ptr)]
|
||||
# Load the address of the embedded array
|
||||
# (struct RArray *)(obj)->as.ary
|
||||
asm.lea(array_reg, [array_reg, C.RArray.offsetof(:as, :ary)])
|
||||
asm.mov(ary_opnd, heap_ptr_opnd)
|
||||
asm.cmovnz(ary_opnd, array_reg)
|
||||
end
|
||||
|
||||
def assert_equal(left, right)
|
||||
if left != right
|
||||
raise "'#{left.inspect}' was not '#{right.inspect}'"
|
||||
|
|
5
rjit_c.h
5
rjit_c.h
|
@ -22,6 +22,11 @@ RJIT_RUNTIME_COUNTERS(
|
|||
|
||||
send_args_splat_kw_splat,
|
||||
send_args_splat,
|
||||
send_args_splat_not_array,
|
||||
send_args_splat_length_not_equal,
|
||||
send_args_splat_opt_num,
|
||||
send_args_splat_arity_error,
|
||||
send_args_splat_ruby2_hash,
|
||||
send_kw_splat,
|
||||
send_kwarg,
|
||||
send_klass_megamorphic,
|
||||
|
|
|
@ -386,6 +386,7 @@ module RubyVM::RJIT # :nodoc: all
|
|||
C::RARRAY_EMBED_FLAG = Primitive.cexpr! %q{ SIZET2NUM(RARRAY_EMBED_FLAG) }
|
||||
C::RARRAY_EMBED_LEN_MASK = Primitive.cexpr! %q{ SIZET2NUM(RARRAY_EMBED_LEN_MASK) }
|
||||
C::RARRAY_EMBED_LEN_SHIFT = Primitive.cexpr! %q{ SIZET2NUM(RARRAY_EMBED_LEN_SHIFT) }
|
||||
C::RHASH_PASS_AS_KEYWORDS = Primitive.cexpr! %q{ SIZET2NUM(RHASH_PASS_AS_KEYWORDS) }
|
||||
C::RMODULE_IS_REFINEMENT = Primitive.cexpr! %q{ SIZET2NUM(RMODULE_IS_REFINEMENT) }
|
||||
C::ROBJECT_EMBED = Primitive.cexpr! %q{ SIZET2NUM(ROBJECT_EMBED) }
|
||||
C::RSTRUCT_EMBED_LEN_MASK = Primitive.cexpr! %q{ SIZET2NUM(RSTRUCT_EMBED_LEN_MASK) }
|
||||
|
@ -403,6 +404,7 @@ module RubyVM::RJIT # :nodoc: all
|
|||
C::RUBY_SYMBOL_FLAG = Primitive.cexpr! %q{ SIZET2NUM(RUBY_SYMBOL_FLAG) }
|
||||
C::RUBY_T_ARRAY = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_ARRAY) }
|
||||
C::RUBY_T_CLASS = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_CLASS) }
|
||||
C::RUBY_T_HASH = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_HASH) }
|
||||
C::RUBY_T_ICLASS = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_ICLASS) }
|
||||
C::RUBY_T_MASK = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_MASK) }
|
||||
C::RUBY_T_MODULE = Primitive.cexpr! %q{ SIZET2NUM(RUBY_T_MODULE) }
|
||||
|
@ -1274,6 +1276,11 @@ module RubyVM::RJIT # :nodoc: all
|
|||
rjit_insns_count: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), rjit_insns_count)")],
|
||||
send_args_splat_kw_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat_kw_splat)")],
|
||||
send_args_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat)")],
|
||||
send_args_splat_not_array: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat_not_array)")],
|
||||
send_args_splat_length_not_equal: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat_length_not_equal)")],
|
||||
send_args_splat_opt_num: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat_opt_num)")],
|
||||
send_args_splat_arity_error: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat_arity_error)")],
|
||||
send_args_splat_ruby2_hash: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_args_splat_ruby2_hash)")],
|
||||
send_kw_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_kw_splat)")],
|
||||
send_kwarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_kwarg)")],
|
||||
send_klass_megamorphic: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_rjit_runtime_counters *)NULL)), send_klass_megamorphic)")],
|
||||
|
|
|
@ -428,6 +428,7 @@ generator = BindingGenerator.new(
|
|||
RUBY_T_ARRAY
|
||||
RUBY_T_CLASS
|
||||
RUBY_T_ICLASS
|
||||
RUBY_T_HASH
|
||||
RUBY_T_MASK
|
||||
RUBY_T_MODULE
|
||||
RUBY_T_STRING
|
||||
|
@ -480,6 +481,7 @@ generator = BindingGenerator.new(
|
|||
VM_SPECIAL_OBJECT_VMCORE
|
||||
RUBY_ENCODING_MASK
|
||||
RUBY_FL_FREEZE
|
||||
RHASH_PASS_AS_KEYWORDS
|
||||
],
|
||||
},
|
||||
values: {
|
||||
|
|
Загрузка…
Ссылка в новой задаче