This commit is contained in:
Takashi Kokubun 2023-01-02 22:53:14 -08:00
Родитель 21696ad81e
Коммит a8dec34961
9 изменённых файлов: 143 добавлений и 34 удалений

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

@ -2,3 +2,9 @@ assert_equal 'true', %q{
def nil_nil = nil == nil
nil_nil
}
assert_equal 'true', %q{
def lt(a, b) = a < b
lt(1, 2)
lt('a', 'b')
}

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

@ -6,7 +6,7 @@ module RubyVM::MJIT
# A thin Fiddle wrapper to write bytes to memory
ByteWriter = CType::Immediate.parse('char')
# Used for rel8 jumps
# rel8 jumps are made with labels
class Label < Data.define(:id, :name); end
# rel32 is inserted as [Rel32, Rel32Pad..] and converted on #resolve_rel32
@ -20,7 +20,6 @@ module RubyVM::MJIT
Mod10 = 0b10 # Mod 10: [reg]+disp16
Mod11 = 0b11 # Mod 11: reg
### prefix ###
# REX = 0100WR0B
REX_B = 0b01000001
REX_R = 0b01000100
@ -94,6 +93,50 @@ module RubyVM::MJIT
insn(opcode: 0xe8, imm: rel32(addr))
end
def cmovl(dst, src)
case [dst, src]
# CMOVL r64, r/m64 (Mod 11: reg)
in [Symbol => dst_reg, Symbol => src_reg]
# REX.W + 0F 4C /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn(
prefix: REX_W,
opcode: [0x0f, 0x4c],
mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg],
)
else
raise NotImplementedError, "cmovl: not-implemented operands: #{dst.inspect}, #{src.inspect}"
end
end
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]
# REX.W + 39 /r
# MR: Operand 1: ModRM:r/m (r), Operand 2: ModRM:reg (r)
insn(
prefix: REX_W,
opcode: 0x39,
mod_rm: ModRM[mod: Mod01, reg: right_reg, rm: left_reg],
disp: left_disp,
)
else
raise NotImplementedError, "cmp: not-implemented operands: #{left.inspect}, #{right.inspect}"
end
end
def je(dst)
case dst
# JE rel32
in Integer => dst_addr
# 0F 84 cd
insn(opcode: [0x0f, 0x84], imm: rel32(dst_addr))
else
raise NotImplementedError, "je: not-implemented operands: #{dst.inspect}"
end
end
def jmp(dst)
case dst
# JMP rel32
@ -285,6 +328,16 @@ module RubyVM::MJIT
def test(left, right)
case [left, right]
# TEST r/m8*, imm8 (Mod 01: [reg]+disp8)
in [[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if imm8?(right_imm)
# REX + F6 /0 ib
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
insn(
opcode: 0xf6,
mod_rm: ModRM[mod: Mod01, reg: 0, rm: left_reg],
disp: left_disp,
imm: imm8(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
@ -294,7 +347,7 @@ module RubyVM::MJIT
mod_rm: ModRM[mod: Mod11, reg: right_reg, rm: left_reg],
)
else
raise NotImplementedError, "pop: not-implemented operands: #{dst.inspect}"
raise NotImplementedError, "test: not-implemented operands: #{left.inspect}, #{right.inspect}"
end
end
@ -442,7 +495,7 @@ module RubyVM::MJIT
unless imm8?(imm)
raise ArgumentError, "unexpected imm8: #{imm}"
end
imm_bytes(imm, 1)
[imm].pack('c').unpack('c*') # TODO: consider uimm
end
# id: 4 bytes
@ -450,7 +503,7 @@ module RubyVM::MJIT
unless imm32?(imm)
raise ArgumentError, "unexpected imm32: #{imm}"
end
[imm].pack('l').unpack('c*')
[imm].pack('l').unpack('c*') # TODO: consider uimm
end
# io: 8 bytes

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

@ -16,6 +16,8 @@ module RubyVM::MJIT
EndBlock = :EndBlock
# Ruby constants
Qtrue = Fiddle::Qtrue
Qfalse = Fiddle::Qfalse
Qnil = Fiddle::Qnil
Qundef = Fiddle::Qundef
@ -25,6 +27,8 @@ module RubyVM::MJIT
CFP = :r15
SP = :rbx
# Scratch registers: rax, rcx
class Compiler
attr_accessor :write_pos
@ -134,14 +138,5 @@ module RubyVM::MJIT
index += insn.len
end
end
# vm_core.h: pathobj_path
def pathobj_path(pathobj)
if pathobj.is_a?(String)
pathobj
else
pathobj.first
end
end
end
end

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

@ -2,14 +2,14 @@ class RubyVM::MJIT::Context < Struct.new(
:stack_size, # @param [Integer] The number of values on the stack
:sp_offset, # @param [Integer] JIT sp offset relative to the interpreter's sp
)
def initialize(*)
super
self.stack_size ||= 0
self.sp_offset ||= 0
end
def initialize(stack_size: 0, sp_offset: 0) = super
def stack_push(size)
self.stack_size += size
self.sp_offset += size
end
def stack_pop(size)
stack_push(-size)
end
end

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

@ -32,10 +32,10 @@ module RubyVM::MJIT
incr_insn_exit(jit.pc)
# Fix pc/sp offsets for the interpreter
save_pc_and_sp(jit, ctx, asm)
save_pc_and_sp(jit, ctx.dup, asm) # dup to avoid sp_offset update
# Restore callee-saved registers
asm.comment('exit to interpreter')
asm.comment("exit to interpreter on #{pc_to_insn(jit.pc).name}")
asm.pop(SP)
asm.pop(EC)
asm.pop(CFP)
@ -65,6 +65,10 @@ module RubyVM::MJIT
private
def pc_to_insn(pc)
Compiler.decode_insn(C.VALUE.new(pc).*)
end
# @param pc [Integer]
def incr_insn_exit(pc)
if C.mjit_opts.stats

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

@ -1,7 +1,4 @@
module RubyVM::MJIT
# scratch regs: rax
#
# 5/101
class InsnCompiler
# @param ocb [CodeBlock]
# @param exit_compiler [RubyVM::MJIT::ExitCompiler]
@ -20,6 +17,7 @@ module RubyVM::MJIT
asm.incr_counter(:mjit_insns_count)
asm.comment("Insn: #{insn.name}")
# 5/101
case insn.name
# nop
# getlocal
@ -149,6 +147,7 @@ module RubyVM::MJIT
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::Assembler]
def putnil(jit, ctx, asm)
raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this
asm.mov([SP, C.VALUE.size * ctx.stack_size], Qnil)
ctx.stack_push(1)
KeepCompiling
@ -165,6 +164,7 @@ module RubyVM::MJIT
# Push it to the stack
# TODO: GC offsets
raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this
if asm.imm32?(val)
asm.mov([SP, C.VALUE.size * ctx.stack_size], val)
else # 64-bit immediates can't be directly written to memory
@ -226,7 +226,7 @@ module RubyVM::MJIT
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(compile_side_exit(jit, ctx))
asm.jnz(side_exit(jit, ctx))
asm.comment('pop stack frame')
asm.add(CFP, C.rb_control_frame_t.size) # cfp = cfp + 1
@ -268,10 +268,38 @@ module RubyVM::MJIT
return EndBlock
end
unless @invariants.assume_bop_not_redefined(jit, C.INTEGER_REDEFINED_OP_FLAG, C.BOP_LT)
return CantCompile
comptime_recv = jit.peek_at_stack(1)
comptime_obj = jit.peek_at_stack(0)
if fixnum?(comptime_recv) && fixnum?(comptime_obj)
unless @invariants.assume_bop_not_redefined(jit, C.INTEGER_REDEFINED_OP_FLAG, C.BOP_LT)
return CantCompile
end
raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this
recv_index = ctx.stack_size - 2
obj_index = ctx.stack_size - 1
asm.comment('guard recv is fixnum');
asm.test([SP, C.VALUE.size * recv_index], C.RUBY_FIXNUM_FLAG)
asm.je(side_exit(jit, ctx))
asm.comment('guard obj is fixnum');
asm.test([SP, C.VALUE.size * obj_index], C.RUBY_FIXNUM_FLAG)
asm.je(side_exit(jit, ctx))
asm.mov(:rax, [SP, C.VALUE.size * obj_index])
asm.cmp([SP, C.VALUE.size * recv_index], :rax)
asm.mov(:rax, Qfalse)
asm.mov(:rcx, Qtrue)
asm.cmovl(:rax, :rcx)
asm.mov([SP, C.VALUE.size * recv_index], :rax)
ctx.stack_pop(1)
KeepCompiling
else
CantCompile # TODO: delegate to send
end
CantCompile
end
# opt_le
@ -330,6 +358,11 @@ module RubyVM::MJIT
end
end
def fixnum?(obj)
flag = C.RUBY_FIXNUM_FLAG
(C.to_value(obj) & flag) == flag
end
# @param jit [RubyVM::MJIT::JITState]
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::Assembler]
@ -354,10 +387,13 @@ module RubyVM::MJIT
# @param jit [RubyVM::MJIT::JITState]
# @param ctx [RubyVM::MJIT::Context]
def compile_side_exit(jit, ctx)
def side_exit(jit, ctx)
if side_exit = jit.side_exits[jit.pc]
return side_exit
end
asm = Assembler.new
@exit_compiler.compile_side_exit(jit, ctx, asm)
@ocb.write(asm)
jit.side_exits[jit.pc] = @ocb.write(asm)
end
end
end

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

@ -1,10 +1,13 @@
module RubyVM::MJIT
class JITState < Struct.new(
:iseq, # @param `RubyVM::MJIT::CPointer::Struct_rb_iseq_t`
:pc, # @param [Integer] The JIT target PC
:cfp, # @param `RubyVM::MJIT::CPointer::Struct_rb_control_frame_t` The JIT source CFP (before MJIT is called)
:block, # @param [RubyVM::MJIT::Block]
:iseq, # @param `RubyVM::MJIT::CPointer::Struct_rb_iseq_t`
:pc, # @param [Integer] The JIT target PC
:cfp, # @param `RubyVM::MJIT::CPointer::Struct_rb_control_frame_t` The JIT source CFP (before MJIT is called)
:block, # @param [RubyVM::MJIT::Block]
:side_exits, # @param [Hash{ Integer => Integer }] { PC => address }
)
def initialize(side_exits: {}, **) = super
def operand(index)
C.VALUE.new(pc)[index + 1]
end
@ -12,5 +15,12 @@ module RubyVM::MJIT
def at_current_insn?
pc == cfp.pc.to_i
end
def peek_at_stack(offset_from_top)
raise 'not at current insn' unless at_current_insn?
offset = -(1 + offset_from_top)
value = (cfp.sp + offset).*
C.to_ruby(value)
end
end
end

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

@ -289,6 +289,10 @@ module RubyVM::MJIT # :nodoc: all
Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) }
end
def C.RUBY_FIXNUM_FLAG
Primitive.cexpr! %q{ ULONG2NUM(RUBY_FIXNUM_FLAG) }
end
def C.SHAPE_MASK
Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) }
end

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

@ -366,6 +366,7 @@ generator = BindingGenerator.new(
],
ULONG: %w[
INVALID_SHAPE_ID
RUBY_FIXNUM_FLAG
SHAPE_MASK
],
PTR: %w[