зеркало из https://github.com/github/ruby.git
Implement getivar
This commit is contained in:
Родитель
a026bcedc8
Коммит
5a1cee1d96
|
@ -1,5 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
module RubyVM::MJIT
|
||||
# 32-bit memory access
|
||||
class DwordPtr < Data.define(:reg, :disp); end
|
||||
|
||||
# https://www.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf
|
||||
# Mostly an x86_64 assembler, but this also has some stuff that is useful for any architecture.
|
||||
class Assembler
|
||||
|
@ -123,8 +126,28 @@ module RubyVM::MJIT
|
|||
|
||||
def cmp(left, right)
|
||||
case [left, right]
|
||||
# CMP r/m64 r64 (Mod 01: [reg]+disp8)
|
||||
in [[Symbol => left_reg, Integer => left_disp], Symbol => right_reg]
|
||||
# CMP r/m32, imm32 (Mod 01: [reg]+disp8)
|
||||
in [DwordPtr[reg: left_reg, disp: left_disp], Integer => right_imm] if imm8?(left_disp) && imm32?(right_imm)
|
||||
# 81 /7 id
|
||||
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
|
||||
insn(
|
||||
opcode: 0x81,
|
||||
mod_rm: ModRM[mod: Mod01, reg: 7, rm: left_reg],
|
||||
disp: left_disp,
|
||||
imm: imm32(right_imm),
|
||||
)
|
||||
# CMP r/m64, imm8 (Mod 11: reg)
|
||||
in [Symbol => left_reg, Integer => right_imm] if r64?(left_reg) && imm8?(right_imm)
|
||||
# REX.W + 83 /7 ib
|
||||
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
|
||||
insn(
|
||||
prefix: REX_W,
|
||||
opcode: 0x83,
|
||||
mod_rm: ModRM[mod: Mod11, reg: 7, rm: left_reg],
|
||||
imm: imm8(right_imm),
|
||||
)
|
||||
# CMP r/m64, r64 (Mod 01: [reg]+disp8)
|
||||
in [[Symbol => left_reg, Integer => left_disp], Symbol => right_reg] if r64?(right_reg)
|
||||
# REX.W + 39 /r
|
||||
# MR: Operand 1: ModRM:r/m (r), Operand 2: ModRM:reg (r)
|
||||
insn(
|
||||
|
@ -453,6 +476,16 @@ module RubyVM::MJIT
|
|||
disp: left_disp,
|
||||
imm: imm32(right_imm),
|
||||
)
|
||||
# TEST r/m64, imm32 (Mod 11: reg)
|
||||
in [Symbol => left_reg, Integer => right_imm] if r64?(left_reg) && imm32?(right_imm)
|
||||
# REX.W + F7 /0 id
|
||||
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
|
||||
insn(
|
||||
prefix: REX_W,
|
||||
opcode: 0xf7,
|
||||
mod_rm: ModRM[mod: Mod11, reg: 0, rm: left_reg],
|
||||
imm: imm32(right_imm),
|
||||
)
|
||||
# TEST r/m32, r32 (Mod 11: reg)
|
||||
in [Symbol => left_reg, Symbol => right_reg] if r32?(left_reg) && r32?(right_reg)
|
||||
# 85 /r
|
||||
|
|
|
@ -55,7 +55,14 @@ module RubyVM::MJIT
|
|||
define_singleton_method(:size) { size }
|
||||
|
||||
# Return the offset to a field
|
||||
define_singleton_method(:offsetof) { |field| members.fetch(field).last / 8 }
|
||||
define_singleton_method(:offsetof) do |field, *fields|
|
||||
member, offset = members.fetch(field)
|
||||
offset /= 8
|
||||
unless fields.empty?
|
||||
offset += member.offsetof(*fields)
|
||||
end
|
||||
offset
|
||||
end
|
||||
|
||||
# Return member names
|
||||
define_singleton_method(:members) { members.keys }
|
||||
|
@ -127,6 +134,15 @@ module RubyVM::MJIT
|
|||
# Return the size of this type
|
||||
define_singleton_method(:sizeof) { sizeof }
|
||||
|
||||
# Part of Struct's offsetof implementation
|
||||
define_singleton_method(:offsetof) do |*fields|
|
||||
if fields.size == 1
|
||||
0
|
||||
else
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
||||
define_method(:initialize) do |addr|
|
||||
super(addr, sizeof, members)
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ module RubyVM::MJIT
|
|||
asm.incr_counter(:mjit_insns_count)
|
||||
asm.comment("Insn: #{insn.name}")
|
||||
|
||||
# 11/101
|
||||
# 13/101
|
||||
case insn.name
|
||||
# nop
|
||||
# getlocal
|
||||
|
@ -27,7 +27,7 @@ module RubyVM::MJIT
|
|||
# getblockparamproxy
|
||||
# getspecial
|
||||
# setspecial
|
||||
# getinstancevariable
|
||||
when :getinstancevariable then getinstancevariable(jit, ctx, asm)
|
||||
# setinstancevariable
|
||||
# getclassvariable
|
||||
# setclassvariable
|
||||
|
@ -137,7 +137,22 @@ module RubyVM::MJIT
|
|||
# getblockparamproxy
|
||||
# getspecial
|
||||
# setspecial
|
||||
# getinstancevariable
|
||||
|
||||
# @param jit [RubyVM::MJIT::JITState]
|
||||
# @param ctx [RubyVM::MJIT::Context]
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def getinstancevariable(jit, ctx, asm)
|
||||
unless jit.at_current_insn?
|
||||
defer_compilation(jit, ctx, asm)
|
||||
return EndBlock
|
||||
end
|
||||
|
||||
id = jit.operand(0)
|
||||
comptime_obj = jit.peek_at_self
|
||||
|
||||
jit_getivar(jit, ctx, asm, comptime_obj, id)
|
||||
end
|
||||
|
||||
# setinstancevariable
|
||||
# getclassvariable
|
||||
# setclassvariable
|
||||
|
@ -242,7 +257,7 @@ module RubyVM::MJIT
|
|||
def leave(jit, ctx, asm)
|
||||
assert_equal(ctx.stack_size, 1)
|
||||
|
||||
compile_check_ints(jit, ctx, asm)
|
||||
jit_check_ints(jit, ctx, asm)
|
||||
|
||||
asm.comment('pop stack frame')
|
||||
asm.lea(:rax, [CFP, C.rb_control_frame_t.size])
|
||||
|
@ -520,16 +535,81 @@ module RubyVM::MJIT
|
|||
# Helpers
|
||||
#
|
||||
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def guard_object_is_heap(asm, object_opnd, side_exit)
|
||||
asm.comment('guard object is heap')
|
||||
# Test that the object is not an immediate
|
||||
asm.test(object_opnd, C.RUBY_IMMEDIATE_MASK)
|
||||
asm.jnz(side_exit)
|
||||
|
||||
# Test that the object is not false
|
||||
asm.cmp(object_opnd, Qfalse)
|
||||
asm.je(side_exit)
|
||||
end
|
||||
|
||||
# rb_vm_check_ints
|
||||
# @param jit [RubyVM::MJIT::JITState]
|
||||
# @param ctx [RubyVM::MJIT::Context]
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def compile_check_ints(jit, ctx, asm)
|
||||
def jit_check_ints(jit, ctx, asm)
|
||||
asm.comment('RUBY_VM_CHECK_INTS(ec)')
|
||||
asm.mov(:eax, [EC, C.rb_execution_context_t.offsetof(:interrupt_flag)])
|
||||
asm.test(:eax, :eax)
|
||||
asm.jnz(side_exit(jit, ctx))
|
||||
end
|
||||
|
||||
# vm_getivar
|
||||
# @param jit [RubyVM::MJIT::JITState]
|
||||
# @param ctx [RubyVM::MJIT::Context]
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def jit_getivar(jit, ctx, asm, comptime_obj, ivar_id)
|
||||
side_exit = side_exit(jit, ctx)
|
||||
|
||||
# Guard not special const
|
||||
if C.SPECIAL_CONST_P(comptime_obj)
|
||||
asm.incr_counter(:getivar_special_const)
|
||||
return CantCompile
|
||||
end
|
||||
asm.mov(:rax, [CFP, C.rb_control_frame_t.offsetof(:self)])
|
||||
guard_object_is_heap(asm, :rax, side_exit) # TODO: counted side exit
|
||||
|
||||
case C.BUILTIN_TYPE(comptime_obj)
|
||||
when C.T_OBJECT
|
||||
# This is the only supported case for now
|
||||
else
|
||||
asm.incr_counter(:getivar_not_t_object)
|
||||
return CantCompile
|
||||
end
|
||||
|
||||
shape_id = C.rb_shape_get_shape_id(comptime_obj)
|
||||
if shape_id == C.OBJ_TOO_COMPLEX_SHAPE_ID
|
||||
asm.incr_counter(:getivar_too_complex)
|
||||
return CantCompile
|
||||
end
|
||||
|
||||
asm.comment('guard shape')
|
||||
asm.cmp(DwordPtr[:rax, C.rb_shape_id_offset], shape_id)
|
||||
asm.jne(side_exit) # TODO: counted side exit
|
||||
|
||||
index = C.rb_shape_get_iv_index(shape_id, ivar_id)
|
||||
if index
|
||||
if C.FL_TEST_RAW(comptime_obj, C.ROBJECT_EMBED)
|
||||
asm.mov(:rax, [:rax, C.RObject.offsetof(:as, :ary) + (index * C.VALUE.size)])
|
||||
val_opnd = :rax
|
||||
else
|
||||
asm.incr_counter(:getivar_too_complex)
|
||||
return CantCompile
|
||||
end
|
||||
else
|
||||
val_opnd = Qnil
|
||||
end
|
||||
|
||||
stack_opnd = ctx.stack_push
|
||||
asm.mov(stack_opnd, val_opnd)
|
||||
|
||||
KeepCompiling
|
||||
end
|
||||
|
||||
# vm_call_method (vm_sendish -> vm_call_general -> vm_call_method)
|
||||
# @param jit [RubyVM::MJIT::JITState]
|
||||
# @param ctx [RubyVM::MJIT::Context]
|
||||
|
@ -792,6 +872,7 @@ module RubyVM::MJIT
|
|||
def jit_caller_remove_empty_kw_splat(jit, ctx, asm, flags)
|
||||
if (flags & C.VM_CALL_KW_SPLAT) > 0
|
||||
# We don't support removing the last Hash argument
|
||||
asm.incr_counter(:send_kw_splat)
|
||||
return CantCompile
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,5 +26,9 @@ module RubyVM::MJIT
|
|||
value = (cfp.sp + offset).*
|
||||
C.to_ruby(value)
|
||||
end
|
||||
|
||||
def peek_at_self
|
||||
C.to_ruby(cfp.self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,6 +35,7 @@ module RubyVM::MJIT
|
|||
$stderr.puts("***MJIT: Printing MJIT statistics on exit***")
|
||||
|
||||
print_counters(stats, prefix: 'send_', prompt: 'method call exit reasons')
|
||||
print_counters(stats, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons')
|
||||
|
||||
$stderr.puts "compiled_block_count: #{format('%10d', stats[:compiled_block_count])}"
|
||||
$stderr.puts "side_exit_count: #{format('%10d', stats[:side_exit_count])}"
|
||||
|
|
5
mjit_c.h
5
mjit_c.h
|
@ -119,6 +119,11 @@ MJIT_RUNTIME_COUNTERS(
|
|||
send_kwarg,
|
||||
send_tailcall,
|
||||
|
||||
getivar_not_embedded,
|
||||
getivar_not_t_object,
|
||||
getivar_special_const,
|
||||
getivar_too_complex,
|
||||
|
||||
compiled_block_count
|
||||
)
|
||||
#undef MJIT_RUNTIME_COUNTERS
|
||||
|
|
81
mjit_c.rb
81
mjit_c.rb
|
@ -94,6 +94,39 @@ module RubyVM::MJIT # :nodoc: all
|
|||
Primitive.cexpr! 'RBOOL(rb_simple_iseq_p((rb_iseq_t *)NUM2SIZET(_iseq_addr)))'
|
||||
end
|
||||
|
||||
def SPECIAL_CONST_P(obj)
|
||||
_value = to_value(obj)
|
||||
Primitive.cexpr! 'RBOOL(SPECIAL_CONST_P((VALUE)NUM2SIZET(_value)))'
|
||||
end
|
||||
|
||||
def BUILTIN_TYPE(obj)
|
||||
_value = to_value(obj)
|
||||
Primitive.cexpr! 'INT2NUM(BUILTIN_TYPE((VALUE)NUM2SIZET(_value)))'
|
||||
end
|
||||
|
||||
def rb_shape_get_shape_id(obj)
|
||||
_value = to_value(obj)
|
||||
Primitive.cexpr! 'UINT2NUM((unsigned int)rb_shape_get_shape_id((VALUE)NUM2SIZET(_value)))'
|
||||
end
|
||||
|
||||
def rb_shape_id_offset
|
||||
Primitive.cexpr! 'INT2NUM(rb_shape_id_offset())'
|
||||
end
|
||||
|
||||
def rb_shape_get_iv_index(shape_id, ivar_id)
|
||||
Primitive.cstmt! %{
|
||||
rb_shape_t *shape = rb_shape_get_shape_by_id((shape_id_t)NUM2SIZET(shape_id));
|
||||
attr_index_t index;
|
||||
bool found = rb_shape_get_iv_index(shape, (ID)NUM2SIZET(ivar_id), &index);
|
||||
return found ? UINT2NUM(index) : Qnil;
|
||||
}
|
||||
end
|
||||
|
||||
def FL_TEST_RAW(obj, flags)
|
||||
_value = to_value(obj)
|
||||
Primitive.cexpr! 'RBOOL(FL_TEST_RAW((VALUE)NUM2SIZET(_value), (VALUE)NUM2SIZET(flags)))'
|
||||
end
|
||||
|
||||
#========================================================================================
|
||||
#
|
||||
# Old stuff
|
||||
|
@ -278,6 +311,10 @@ module RubyVM::MJIT # :nodoc: all
|
|||
Primitive.cexpr! %q{ UINT2NUM(METHOD_VISI_PUBLIC) }
|
||||
end
|
||||
|
||||
def C.ROBJECT_EMBED
|
||||
Primitive.cexpr! %q{ UINT2NUM(ROBJECT_EMBED) }
|
||||
end
|
||||
|
||||
def C.RUBY_EVENT_CLASS
|
||||
Primitive.cexpr! %q{ UINT2NUM(RUBY_EVENT_CLASS) }
|
||||
end
|
||||
|
@ -310,6 +347,10 @@ module RubyVM::MJIT # :nodoc: all
|
|||
Primitive.cexpr! %q{ UINT2NUM(SHAPE_ROOT) }
|
||||
end
|
||||
|
||||
def C.T_OBJECT
|
||||
Primitive.cexpr! %q{ UINT2NUM(T_OBJECT) }
|
||||
end
|
||||
|
||||
def C.VM_BLOCK_HANDLER_NONE
|
||||
Primitive.cexpr! %q{ UINT2NUM(VM_BLOCK_HANDLER_NONE) }
|
||||
end
|
||||
|
@ -366,10 +407,18 @@ module RubyVM::MJIT # :nodoc: all
|
|||
Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) }
|
||||
end
|
||||
|
||||
def C.OBJ_TOO_COMPLEX_SHAPE_ID
|
||||
Primitive.cexpr! %q{ ULONG2NUM(OBJ_TOO_COMPLEX_SHAPE_ID) }
|
||||
end
|
||||
|
||||
def C.RUBY_FIXNUM_FLAG
|
||||
Primitive.cexpr! %q{ ULONG2NUM(RUBY_FIXNUM_FLAG) }
|
||||
end
|
||||
|
||||
def C.RUBY_IMMEDIATE_MASK
|
||||
Primitive.cexpr! %q{ ULONG2NUM(RUBY_IMMEDIATE_MASK) }
|
||||
end
|
||||
|
||||
def C.SHAPE_MASK
|
||||
Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) }
|
||||
end
|
||||
|
@ -414,6 +463,22 @@ module RubyVM::MJIT # :nodoc: all
|
|||
@RB_BUILTIN ||= self.rb_builtin_function
|
||||
end
|
||||
|
||||
def C.RObject
|
||||
@RObject ||= CType::Struct.new(
|
||||
"RObject", Primitive.cexpr!("SIZEOF(struct RObject)"),
|
||||
basic: [self.RBasic, Primitive.cexpr!("OFFSETOF((*((struct RObject *)NULL)), basic)")],
|
||||
as: [CType::Union.new(
|
||||
"", Primitive.cexpr!("SIZEOF(((struct RObject *)NULL)->as)"),
|
||||
heap: CType::Struct.new(
|
||||
"", Primitive.cexpr!("SIZEOF(((struct RObject *)NULL)->as.heap)"),
|
||||
ivptr: [CType::Pointer.new { self.VALUE }, Primitive.cexpr!("OFFSETOF(((struct RObject *)NULL)->as.heap, ivptr)")],
|
||||
iv_index_tbl: [CType::Pointer.new { self.rb_id_table }, Primitive.cexpr!("OFFSETOF(((struct RObject *)NULL)->as.heap, iv_index_tbl)")],
|
||||
),
|
||||
ary: CType::Pointer.new { self.VALUE },
|
||||
), Primitive.cexpr!("OFFSETOF((*((struct RObject *)NULL)), as)")],
|
||||
)
|
||||
end
|
||||
|
||||
def C.attr_index_t
|
||||
@attr_index_t ||= CType::Immediate.parse("uint32_t")
|
||||
end
|
||||
|
@ -799,6 +864,10 @@ module RubyVM::MJIT # :nodoc: all
|
|||
send_args_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_args_splat)")],
|
||||
send_kwarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_kwarg)")],
|
||||
send_tailcall: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_tailcall)")],
|
||||
getivar_not_embedded: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), getivar_not_embedded)")],
|
||||
getivar_not_t_object: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), getivar_not_t_object)")],
|
||||
getivar_special_const: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), getivar_special_const)")],
|
||||
getivar_too_complex: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), getivar_too_complex)")],
|
||||
compiled_block_count: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), compiled_block_count)")],
|
||||
)
|
||||
end
|
||||
|
@ -848,6 +917,14 @@ module RubyVM::MJIT # :nodoc: all
|
|||
@shape_id_t ||= CType::Immediate.find(Primitive.cexpr!("SIZEOF(shape_id_t)"), Primitive.cexpr!("SIGNED_TYPE_P(shape_id_t)"))
|
||||
end
|
||||
|
||||
def C.RBasic
|
||||
CType::Stub.new(:RBasic)
|
||||
end
|
||||
|
||||
def C.rb_id_table
|
||||
CType::Stub.new(:rb_id_table)
|
||||
end
|
||||
|
||||
def C._Bool
|
||||
CType::Bool.new
|
||||
end
|
||||
|
@ -892,10 +969,6 @@ module RubyVM::MJIT # :nodoc: all
|
|||
CType::Stub.new(:rb_fiber_t)
|
||||
end
|
||||
|
||||
def C.rb_id_table
|
||||
CType::Stub.new(:rb_id_table)
|
||||
end
|
||||
|
||||
def C.rb_ensure_list_t
|
||||
CType::Stub.new(:rb_ensure_list_t)
|
||||
end
|
||||
|
|
|
@ -354,6 +354,7 @@ generator = BindingGenerator.new(
|
|||
METHOD_VISI_PRIVATE
|
||||
METHOD_VISI_PROTECTED
|
||||
METHOD_VISI_PUBLIC
|
||||
ROBJECT_EMBED
|
||||
RUBY_EVENT_CLASS
|
||||
SHAPE_CAPACITY_CHANGE
|
||||
SHAPE_FLAG_SHIFT
|
||||
|
@ -362,6 +363,7 @@ generator = BindingGenerator.new(
|
|||
SHAPE_INITIAL_CAPACITY
|
||||
SHAPE_IVAR
|
||||
SHAPE_ROOT
|
||||
T_OBJECT
|
||||
VM_BLOCK_HANDLER_NONE
|
||||
VM_CALL_ARGS_BLOCKARG
|
||||
VM_CALL_ARGS_SPLAT
|
||||
|
@ -378,7 +380,9 @@ generator = BindingGenerator.new(
|
|||
],
|
||||
ULONG: %w[
|
||||
INVALID_SHAPE_ID
|
||||
OBJ_TOO_COMPLEX_SHAPE_ID
|
||||
RUBY_FIXNUM_FLAG
|
||||
RUBY_IMMEDIATE_MASK
|
||||
SHAPE_MASK
|
||||
],
|
||||
PTR: %w[
|
||||
|
@ -395,6 +399,7 @@ generator = BindingGenerator.new(
|
|||
IC
|
||||
IVC
|
||||
RB_BUILTIN
|
||||
RObject
|
||||
attr_index_t
|
||||
compile_branch
|
||||
compile_status
|
||||
|
|
Загрузка…
Ссылка в новой задаче