Implement initial side exit

This commit is contained in:
Takashi Kokubun 2022-12-23 10:43:18 -08:00
parent e750e1e3ee
commit b99d62bf92
3 changed files with 101 additions and 43 deletions

View file

@ -4,9 +4,6 @@ module RubyVM::MJIT
# sp: rbx # sp: rbx
# scratch regs: rax # scratch regs: rax
class InsnCompiler class InsnCompiler
# Ruby constants
Qnil = Fiddle::Qnil
def putnil(asm) def putnil(asm)
asm.mov([:rbx], Qnil) asm.mov([:rbx], Qnil)
KeepCompiling KeepCompiling

View file

@ -27,7 +27,7 @@ module RubyVM::MJIT
def add(dst, src) def add(dst, src)
case [dst, src] case [dst, src]
# ADD r/m64, imm8 # ADD r/m64, imm8
in [Symbol => dst_reg, Integer => src_imm] if r_reg?(dst_reg) && src_imm <= 0xff in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm)
# REX.W + 83 /0 ib # REX.W + 83 /0 ib
# MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32 # MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32
insn( insn(
@ -44,7 +44,7 @@ module RubyVM::MJIT
def mov(dst, src) def mov(dst, src)
case [dst, src] case [dst, src]
# MOV r/m64, imm32 (Mod 00) # MOV r/m64, imm32 (Mod 00)
in [[Symbol => dst_reg], Integer => src_imm] if r_reg?(dst_reg) in [[Symbol => dst_reg], Integer => src_imm] if r64?(dst_reg)
# REX.W + C7 /0 id # REX.W + C7 /0 id
# MI: Operand 1: ModRM:r/m (w), Operand 2: imm8/16/32/64 # MI: Operand 1: ModRM:r/m (w), Operand 2: imm8/16/32/64
insn( insn(
@ -54,7 +54,7 @@ module RubyVM::MJIT
imm: imm32(src_imm), imm: imm32(src_imm),
) )
# MOV r/m64, imm32 (Mod 11) # MOV r/m64, imm32 (Mod 11)
in [Symbol => dst_reg, Integer => src_imm] if r_reg?(dst_reg) in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm32?(src_imm)
# REX.W + C7 /0 id # REX.W + C7 /0 id
# MI: Operand 1: ModRM:r/m (w), Operand 2: imm8/16/32/64 # MI: Operand 1: ModRM:r/m (w), Operand 2: imm8/16/32/64
insn( insn(
@ -63,8 +63,17 @@ module RubyVM::MJIT
mod_rm: mod_rm(mod: 0b11, rm: reg_code(dst_reg)), # Mod 11: reg mod_rm: mod_rm(mod: 0b11, rm: reg_code(dst_reg)), # Mod 11: reg
imm: imm32(src_imm), imm: imm32(src_imm),
) )
# MOV r64, imm64
in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm64?(src_imm)
# REX.W + B8+ rd io
# OI: Operand 1: opcode + rd (w), Operand 2: imm8/16/32/64
insn(
prefix: REX_W,
opcode: 0xb8 + reg_code(dst_reg),
imm: imm64(src_imm),
)
# MOV r/m64, r64 # MOV r/m64, r64
in [[Symbol => dst_reg, Integer => dst_offset], Symbol => src_reg] if r_reg?(dst_reg) && r_reg?(src_reg) && dst_offset <= 0xff in [[Symbol => dst_reg, Integer => dst_offset], Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg) && imm8?(dst_offset)
# REX.W + 89 /r # REX.W + 89 /r
# MR: Operand 1: ModRM:r/m (w), Operand 2: ModRM:reg (r) # MR: Operand 1: ModRM:r/m (w), Operand 2: ModRM:reg (r)
insn( insn(
@ -74,7 +83,7 @@ module RubyVM::MJIT
disp: dst_offset, disp: dst_offset,
) )
# MOV r64, r/m64 (Mod 00) # MOV r64, r/m64 (Mod 00)
in [Symbol => dst_reg, [Symbol => src_reg]] if r_reg?(dst_reg) && r_reg?(src_reg) in [Symbol => dst_reg, [Symbol => src_reg]] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 8B /r # REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -83,7 +92,7 @@ module RubyVM::MJIT
mod_rm: mod_rm(mod: 0b00, reg: reg_code(dst_reg), rm: reg_code(src_reg)), # Mod 00: [reg] mod_rm: mod_rm(mod: 0b00, reg: reg_code(dst_reg), rm: reg_code(src_reg)), # Mod 00: [reg]
) )
# MOV r64, r/m64 (Mod 01) # MOV r64, r/m64 (Mod 01)
in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_offset]] if r_reg?(dst_reg) && r_reg?(src_reg) && src_offset <= 0xff in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_offset]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_offset)
# REX.W + 8B /r # REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -166,27 +175,59 @@ module RubyVM::MJIT
# ib: 1 byte # ib: 1 byte
def imm8(imm) def imm8(imm)
if imm > 0xff unless imm8?(imm)
raise ArgumentError, "unexpected imm8: #{imm}" raise ArgumentError, "unexpected imm8: #{imm}"
end end
[imm] imm_bytes(imm, 1)
end end
# id: 4 bytes # id: 4 bytes
def imm32(imm) def imm32(imm)
unless imm32?(imm)
raise ArgumentError, "unexpected imm32: #{imm}"
end
imm_bytes(imm, 4)
end
# io: 8 bytes
def imm64(imm)
unless imm64?(imm)
raise ArgumentError, "unexpected imm64: #{imm}"
end
imm_bytes(imm, 8)
end
def imm_bytes(imm, num_bytes)
bytes = [] bytes = []
bits = imm bits = imm
4.times do num_bytes.times do
bytes << (bits & 0xff) bytes << (bits & 0xff)
bits >>= 8 bits >>= 8
end end
if bits != 0 if bits != 0
raise ArgumentError, "unexpected imm32: #{imm}" raise ArgumentError, "unexpected imm with #{num_bytes} bytes: #{imm}"
end end
bytes bytes
end end
def r_reg?(reg) def imm8?(imm)
# TODO: consider negative values
imm <= 0xff
end
def imm32?(imm)
# TODO: consider negative values
# TODO: consider rejecting small values
imm <= 0xffff_ffff
end
def imm64?(imm)
# TODO: consider negative values
# TODO: consider rejecting small values
imm <= 0xffff_ffff_ffff_ffff
end
def r64?(reg)
reg.start_with?('r') reg.start_with?('r')
end end
end end

View file

@ -4,9 +4,13 @@ require 'mjit/x86_assembler'
module RubyVM::MJIT module RubyVM::MJIT
# Compilation status # Compilation status
KeepCompiling = :keep_compiling KeepCompiling = :KeepCompiling
CantCompile = :cant_compile CantCompile = :CantCompile
EndBlock = :end_block EndBlock = :EndBlock
# Ruby constants
Qnil = Fiddle::Qnil
Qundef = Fiddle::Qundef
class Compiler class Compiler
attr_accessor :write_pos attr_accessor :write_pos
@ -36,32 +40,6 @@ module RubyVM::MJIT
private private
# ec: rdi
# cfp: rsi
def compile_prologue(asm)
asm.mov(:rbx, [:rsi, C.rb_control_frame_t.offsetof(:sp)]) # rbx = cfp->sp
end
def compile_block(asm, iseq)
index = 0
while index < iseq.body.iseq_size
insn = decode_insn(iseq.body.iseq_encoded[index])
status = compile_insn(asm, insn)
if status == EndBlock
break
end
index += insn.len
end
end
def compile_insn(asm, insn)
case insn.name
when :putnil then @insn_compiler.putnil(asm)
when :leave then @insn_compiler.leave(asm)
else raise NotImplementedError, "insn '#{insn.name}' is not supported yet"
end
end
def compile(asm) def compile(asm)
start_addr = write_addr start_addr = write_addr
@ -76,6 +54,48 @@ module RubyVM::MJIT
start_addr start_addr
end end
# ec: rdi
# cfp: rsi
def compile_prologue(asm)
asm.mov(:rbx, [:rsi, C.rb_control_frame_t.offsetof(:sp)]) # rbx = cfp->sp
end
def compile_block(asm, iseq)
index = 0
while index < iseq.body.iseq_size
insn = decode_insn(iseq.body.iseq_encoded[index])
case compile_insn(asm, insn)
when EndBlock
break
when CantCompile
compile_exit(asm, (iseq.body.iseq_encoded + index).to_i)
break
end
index += insn.len
end
end
def compile_insn(asm, insn)
case insn.name
when :putnil then @insn_compiler.putnil(asm)
#when :leave then @insn_compiler.leave(asm)
else CantCompile
end
end
def compile_exit(asm, exit_pc)
# update pc
asm.mov(:rax, exit_pc) # rax = exit_pc
asm.mov([:rsi, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
# update sp (TODO: consider JIT state)
asm.add(:rbx, C.VALUE.size) # rbx += 1
asm.mov([:rsi, C.rb_control_frame_t.offsetof(:sp)], :rbx) # cfp->sp = rbx
asm.mov(:rax, Qundef)
asm.ret
end
def decode_insn(encoded) def decode_insn(encoded)
INSNS.fetch(C.rb_vm_insn_decode(encoded)) INSNS.fetch(C.rb_vm_insn_decode(encoded))
end end