mirror of
https://github.com/ruby/ruby.git
synced 2025-08-27 06:56:13 +02:00
Implement initial opt_lt
This commit is contained in:
parent
21696ad81e
commit
a8dec34961
9 changed files with 143 additions and 34 deletions
|
@ -2,3 +2,9 @@ assert_equal 'true', %q{
|
||||||
def nil_nil = nil == nil
|
def nil_nil = 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
|
# A thin Fiddle wrapper to write bytes to memory
|
||||||
ByteWriter = CType::Immediate.parse('char')
|
ByteWriter = CType::Immediate.parse('char')
|
||||||
|
|
||||||
# Used for rel8 jumps
|
# rel8 jumps are made with labels
|
||||||
class Label < Data.define(:id, :name); end
|
class Label < Data.define(:id, :name); end
|
||||||
|
|
||||||
# rel32 is inserted as [Rel32, Rel32Pad..] and converted on #resolve_rel32
|
# rel32 is inserted as [Rel32, Rel32Pad..] and converted on #resolve_rel32
|
||||||
|
@ -20,7 +20,6 @@ module RubyVM::MJIT
|
||||||
Mod10 = 0b10 # Mod 10: [reg]+disp16
|
Mod10 = 0b10 # Mod 10: [reg]+disp16
|
||||||
Mod11 = 0b11 # Mod 11: reg
|
Mod11 = 0b11 # Mod 11: reg
|
||||||
|
|
||||||
### prefix ###
|
|
||||||
# REX = 0100WR0B
|
# REX = 0100WR0B
|
||||||
REX_B = 0b01000001
|
REX_B = 0b01000001
|
||||||
REX_R = 0b01000100
|
REX_R = 0b01000100
|
||||||
|
@ -94,6 +93,50 @@ module RubyVM::MJIT
|
||||||
insn(opcode: 0xe8, imm: rel32(addr))
|
insn(opcode: 0xe8, imm: rel32(addr))
|
||||||
end
|
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)
|
def jmp(dst)
|
||||||
case dst
|
case dst
|
||||||
# JMP rel32
|
# JMP rel32
|
||||||
|
@ -285,6 +328,16 @@ module RubyVM::MJIT
|
||||||
|
|
||||||
def test(left, right)
|
def test(left, right)
|
||||||
case [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)
|
# TEST r/m32, r32 (Mod 11: reg)
|
||||||
in [Symbol => left_reg, Symbol => right_reg] if r32?(left_reg) && r32?(right_reg)
|
in [Symbol => left_reg, Symbol => right_reg] if r32?(left_reg) && r32?(right_reg)
|
||||||
# 85 /r
|
# 85 /r
|
||||||
|
@ -294,7 +347,7 @@ module RubyVM::MJIT
|
||||||
mod_rm: ModRM[mod: Mod11, reg: right_reg, rm: left_reg],
|
mod_rm: ModRM[mod: Mod11, reg: right_reg, rm: left_reg],
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
raise NotImplementedError, "pop: not-implemented operands: #{dst.inspect}"
|
raise NotImplementedError, "test: not-implemented operands: #{left.inspect}, #{right.inspect}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -442,7 +495,7 @@ module RubyVM::MJIT
|
||||||
unless imm8?(imm)
|
unless imm8?(imm)
|
||||||
raise ArgumentError, "unexpected imm8: #{imm}"
|
raise ArgumentError, "unexpected imm8: #{imm}"
|
||||||
end
|
end
|
||||||
imm_bytes(imm, 1)
|
[imm].pack('c').unpack('c*') # TODO: consider uimm
|
||||||
end
|
end
|
||||||
|
|
||||||
# id: 4 bytes
|
# id: 4 bytes
|
||||||
|
@ -450,7 +503,7 @@ module RubyVM::MJIT
|
||||||
unless imm32?(imm)
|
unless imm32?(imm)
|
||||||
raise ArgumentError, "unexpected imm32: #{imm}"
|
raise ArgumentError, "unexpected imm32: #{imm}"
|
||||||
end
|
end
|
||||||
[imm].pack('l').unpack('c*')
|
[imm].pack('l').unpack('c*') # TODO: consider uimm
|
||||||
end
|
end
|
||||||
|
|
||||||
# io: 8 bytes
|
# io: 8 bytes
|
||||||
|
|
|
@ -16,6 +16,8 @@ module RubyVM::MJIT
|
||||||
EndBlock = :EndBlock
|
EndBlock = :EndBlock
|
||||||
|
|
||||||
# Ruby constants
|
# Ruby constants
|
||||||
|
Qtrue = Fiddle::Qtrue
|
||||||
|
Qfalse = Fiddle::Qfalse
|
||||||
Qnil = Fiddle::Qnil
|
Qnil = Fiddle::Qnil
|
||||||
Qundef = Fiddle::Qundef
|
Qundef = Fiddle::Qundef
|
||||||
|
|
||||||
|
@ -25,6 +27,8 @@ module RubyVM::MJIT
|
||||||
CFP = :r15
|
CFP = :r15
|
||||||
SP = :rbx
|
SP = :rbx
|
||||||
|
|
||||||
|
# Scratch registers: rax, rcx
|
||||||
|
|
||||||
class Compiler
|
class Compiler
|
||||||
attr_accessor :write_pos
|
attr_accessor :write_pos
|
||||||
|
|
||||||
|
@ -134,14 +138,5 @@ module RubyVM::MJIT
|
||||||
index += insn.len
|
index += insn.len
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# vm_core.h: pathobj_path
|
|
||||||
def pathobj_path(pathobj)
|
|
||||||
if pathobj.is_a?(String)
|
|
||||||
pathobj
|
|
||||||
else
|
|
||||||
pathobj.first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
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
|
:stack_size, # @param [Integer] The number of values on the stack
|
||||||
:sp_offset, # @param [Integer] JIT sp offset relative to the interpreter's sp
|
:sp_offset, # @param [Integer] JIT sp offset relative to the interpreter's sp
|
||||||
)
|
)
|
||||||
def initialize(*)
|
def initialize(stack_size: 0, sp_offset: 0) = super
|
||||||
super
|
|
||||||
self.stack_size ||= 0
|
|
||||||
self.sp_offset ||= 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def stack_push(size)
|
def stack_push(size)
|
||||||
self.stack_size += size
|
self.stack_size += size
|
||||||
self.sp_offset += size
|
self.sp_offset += size
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stack_pop(size)
|
||||||
|
stack_push(-size)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,10 +32,10 @@ module RubyVM::MJIT
|
||||||
incr_insn_exit(jit.pc)
|
incr_insn_exit(jit.pc)
|
||||||
|
|
||||||
# Fix pc/sp offsets for the interpreter
|
# 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
|
# 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(SP)
|
||||||
asm.pop(EC)
|
asm.pop(EC)
|
||||||
asm.pop(CFP)
|
asm.pop(CFP)
|
||||||
|
@ -65,6 +65,10 @@ module RubyVM::MJIT
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def pc_to_insn(pc)
|
||||||
|
Compiler.decode_insn(C.VALUE.new(pc).*)
|
||||||
|
end
|
||||||
|
|
||||||
# @param pc [Integer]
|
# @param pc [Integer]
|
||||||
def incr_insn_exit(pc)
|
def incr_insn_exit(pc)
|
||||||
if C.mjit_opts.stats
|
if C.mjit_opts.stats
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
module RubyVM::MJIT
|
module RubyVM::MJIT
|
||||||
# scratch regs: rax
|
|
||||||
#
|
|
||||||
# 5/101
|
|
||||||
class InsnCompiler
|
class InsnCompiler
|
||||||
# @param ocb [CodeBlock]
|
# @param ocb [CodeBlock]
|
||||||
# @param exit_compiler [RubyVM::MJIT::ExitCompiler]
|
# @param exit_compiler [RubyVM::MJIT::ExitCompiler]
|
||||||
|
@ -20,6 +17,7 @@ module RubyVM::MJIT
|
||||||
asm.incr_counter(:mjit_insns_count)
|
asm.incr_counter(:mjit_insns_count)
|
||||||
asm.comment("Insn: #{insn.name}")
|
asm.comment("Insn: #{insn.name}")
|
||||||
|
|
||||||
|
# 5/101
|
||||||
case insn.name
|
case insn.name
|
||||||
# nop
|
# nop
|
||||||
# getlocal
|
# getlocal
|
||||||
|
@ -149,6 +147,7 @@ module RubyVM::MJIT
|
||||||
# @param ctx [RubyVM::MJIT::Context]
|
# @param ctx [RubyVM::MJIT::Context]
|
||||||
# @param asm [RubyVM::MJIT::Assembler]
|
# @param asm [RubyVM::MJIT::Assembler]
|
||||||
def putnil(jit, ctx, asm)
|
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)
|
asm.mov([SP, C.VALUE.size * ctx.stack_size], Qnil)
|
||||||
ctx.stack_push(1)
|
ctx.stack_push(1)
|
||||||
KeepCompiling
|
KeepCompiling
|
||||||
|
@ -165,6 +164,7 @@ module RubyVM::MJIT
|
||||||
|
|
||||||
# Push it to the stack
|
# Push it to the stack
|
||||||
# TODO: GC offsets
|
# TODO: GC offsets
|
||||||
|
raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this
|
||||||
if asm.imm32?(val)
|
if asm.imm32?(val)
|
||||||
asm.mov([SP, C.VALUE.size * ctx.stack_size], val)
|
asm.mov([SP, C.VALUE.size * ctx.stack_size], val)
|
||||||
else # 64-bit immediates can't be directly written to memory
|
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.comment('RUBY_VM_CHECK_INTS(ec)')
|
||||||
asm.mov(:eax, [EC, C.rb_execution_context_t.offsetof(:interrupt_flag)])
|
asm.mov(:eax, [EC, C.rb_execution_context_t.offsetof(:interrupt_flag)])
|
||||||
asm.test(:eax, :eax)
|
asm.test(:eax, :eax)
|
||||||
asm.jnz(compile_side_exit(jit, ctx))
|
asm.jnz(side_exit(jit, ctx))
|
||||||
|
|
||||||
asm.comment('pop stack frame')
|
asm.comment('pop stack frame')
|
||||||
asm.add(CFP, C.rb_control_frame_t.size) # cfp = cfp + 1
|
asm.add(CFP, C.rb_control_frame_t.size) # cfp = cfp + 1
|
||||||
|
@ -268,10 +268,38 @@ module RubyVM::MJIT
|
||||||
return EndBlock
|
return EndBlock
|
||||||
end
|
end
|
||||||
|
|
||||||
unless @invariants.assume_bop_not_redefined(jit, C.INTEGER_REDEFINED_OP_FLAG, C.BOP_LT)
|
comptime_recv = jit.peek_at_stack(1)
|
||||||
return CantCompile
|
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
|
end
|
||||||
CantCompile
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# opt_le
|
# opt_le
|
||||||
|
@ -330,6 +358,11 @@ module RubyVM::MJIT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fixnum?(obj)
|
||||||
|
flag = C.RUBY_FIXNUM_FLAG
|
||||||
|
(C.to_value(obj) & flag) == flag
|
||||||
|
end
|
||||||
|
|
||||||
# @param jit [RubyVM::MJIT::JITState]
|
# @param jit [RubyVM::MJIT::JITState]
|
||||||
# @param ctx [RubyVM::MJIT::Context]
|
# @param ctx [RubyVM::MJIT::Context]
|
||||||
# @param asm [RubyVM::MJIT::Assembler]
|
# @param asm [RubyVM::MJIT::Assembler]
|
||||||
|
@ -354,10 +387,13 @@ module RubyVM::MJIT
|
||||||
|
|
||||||
# @param jit [RubyVM::MJIT::JITState]
|
# @param jit [RubyVM::MJIT::JITState]
|
||||||
# @param ctx [RubyVM::MJIT::Context]
|
# @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
|
asm = Assembler.new
|
||||||
@exit_compiler.compile_side_exit(jit, ctx, asm)
|
@exit_compiler.compile_side_exit(jit, ctx, asm)
|
||||||
@ocb.write(asm)
|
jit.side_exits[jit.pc] = @ocb.write(asm)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
module RubyVM::MJIT
|
module RubyVM::MJIT
|
||||||
class JITState < Struct.new(
|
class JITState < Struct.new(
|
||||||
:iseq, # @param `RubyVM::MJIT::CPointer::Struct_rb_iseq_t`
|
:iseq, # @param `RubyVM::MJIT::CPointer::Struct_rb_iseq_t`
|
||||||
:pc, # @param [Integer] The JIT target PC
|
: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)
|
:cfp, # @param `RubyVM::MJIT::CPointer::Struct_rb_control_frame_t` The JIT source CFP (before MJIT is called)
|
||||||
:block, # @param [RubyVM::MJIT::Block]
|
:block, # @param [RubyVM::MJIT::Block]
|
||||||
|
:side_exits, # @param [Hash{ Integer => Integer }] { PC => address }
|
||||||
)
|
)
|
||||||
|
def initialize(side_exits: {}, **) = super
|
||||||
|
|
||||||
def operand(index)
|
def operand(index)
|
||||||
C.VALUE.new(pc)[index + 1]
|
C.VALUE.new(pc)[index + 1]
|
||||||
end
|
end
|
||||||
|
@ -12,5 +15,12 @@ module RubyVM::MJIT
|
||||||
def at_current_insn?
|
def at_current_insn?
|
||||||
pc == cfp.pc.to_i
|
pc == cfp.pc.to_i
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -289,6 +289,10 @@ module RubyVM::MJIT # :nodoc: all
|
||||||
Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) }
|
Primitive.cexpr! %q{ ULONG2NUM(INVALID_SHAPE_ID) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def C.RUBY_FIXNUM_FLAG
|
||||||
|
Primitive.cexpr! %q{ ULONG2NUM(RUBY_FIXNUM_FLAG) }
|
||||||
|
end
|
||||||
|
|
||||||
def C.SHAPE_MASK
|
def C.SHAPE_MASK
|
||||||
Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) }
|
Primitive.cexpr! %q{ ULONG2NUM(SHAPE_MASK) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -366,6 +366,7 @@ generator = BindingGenerator.new(
|
||||||
],
|
],
|
||||||
ULONG: %w[
|
ULONG: %w[
|
||||||
INVALID_SHAPE_ID
|
INVALID_SHAPE_ID
|
||||||
|
RUBY_FIXNUM_FLAG
|
||||||
SHAPE_MASK
|
SHAPE_MASK
|
||||||
],
|
],
|
||||||
PTR: %w[
|
PTR: %w[
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue