зеркало из https://github.com/github/ruby.git
Implement initial opt_lt
This commit is contained in:
Родитель
21696ad81e
Коммит
a8dec34961
|
@ -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[
|
||||
|
|
Загрузка…
Ссылка в новой задаче