This commit is contained in:
Takashi Kokubun 2023-02-07 14:42:58 -08:00
Родитель a026bcedc8
Коммит 5a1cee1d96
8 изменённых файлов: 230 добавлений и 12 удалений

Просмотреть файл

@ -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])}"

Просмотреть файл

@ -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

Просмотреть файл

@ -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