summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2023-01-02 22:53:14 -0800
committerTakashi Kokubun <takashikkbn@gmail.com>2023-03-05 22:11:20 -0800
commita8dec34961d7734ad975e44efa8aae361a1c4f5a (patch)
tree5849c716f37c959d9276ddb8c598fe1142a7f6fa /lib
parent21696ad81ec40cf9cd146836dc8c945f1e485774 (diff)
downloadruby-a8dec34961d7734ad975e44efa8aae361a1c4f5a.tar.gz
Implement initial opt_lt
Diffstat (limited to 'lib')
-rw-r--r--lib/ruby_vm/mjit/assembler.rb63
-rw-r--r--lib/ruby_vm/mjit/compiler.rb13
-rw-r--r--lib/ruby_vm/mjit/context.rb10
-rw-r--r--lib/ruby_vm/mjit/exit_compiler.rb8
-rw-r--r--lib/ruby_vm/mjit/insn_compiler.rb54
-rw-r--r--lib/ruby_vm/mjit/jit_state.rb18
6 files changed, 132 insertions, 34 deletions
diff --git a/lib/ruby_vm/mjit/assembler.rb b/lib/ruby_vm/mjit/assembler.rb
index c55df0235d..bc42ad1a6a 100644
--- a/lib/ruby_vm/mjit/assembler.rb
+++ b/lib/ruby_vm/mjit/assembler.rb
@@ -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
diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb
index 56e23a1f77..0e018a31f2 100644
--- a/lib/ruby_vm/mjit/compiler.rb
+++ b/lib/ruby_vm/mjit/compiler.rb
@@ -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
diff --git a/lib/ruby_vm/mjit/context.rb b/lib/ruby_vm/mjit/context.rb
index 8433cc5f95..2799b63eb3 100644
--- a/lib/ruby_vm/mjit/context.rb
+++ b/lib/ruby_vm/mjit/context.rb
@@ -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
diff --git a/lib/ruby_vm/mjit/exit_compiler.rb b/lib/ruby_vm/mjit/exit_compiler.rb
index 4577f65e20..7e1c605d25 100644
--- a/lib/ruby_vm/mjit/exit_compiler.rb
+++ b/lib/ruby_vm/mjit/exit_compiler.rb
@@ -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
diff --git a/lib/ruby_vm/mjit/insn_compiler.rb b/lib/ruby_vm/mjit/insn_compiler.rb
index 262d33387a..2950536e8e 100644
--- a/lib/ruby_vm/mjit/insn_compiler.rb
+++ b/lib/ruby_vm/mjit/insn_compiler.rb
@@ -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
diff --git a/lib/ruby_vm/mjit/jit_state.rb b/lib/ruby_vm/mjit/jit_state.rb
index 97e5b7ffdd..97331e8108 100644
--- a/lib/ruby_vm/mjit/jit_state.rb
+++ b/lib/ruby_vm/mjit/jit_state.rb
@@ -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