зеркало из https://github.com/github/ruby.git
Implement mov encoding properly
This commit is contained in:
Родитель
2700d35b7b
Коммит
396d575451
|
@ -1,17 +1,17 @@
|
||||||
module RubyVM::MJIT
|
module RubyVM::MJIT
|
||||||
class InsnCompiler
|
class InsnCompiler
|
||||||
def on_putnil(_asm)
|
def putnil(_asm)
|
||||||
# TODO
|
# TODO
|
||||||
KeepCompiling
|
KeepCompiling
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_leave(asm)
|
def leave(asm)
|
||||||
# pop the current frame (ec->cfp++)
|
# pop the current frame (ec->cfp++)
|
||||||
asm.add(:rsi, C.rb_control_frame_t.size)
|
asm.add(:rsi, C.rb_control_frame_t.size)
|
||||||
asm.mov([:rdi, C.rb_execution_context_t.offsetof(:cfp)], :rsi)
|
asm.mov([:rdi, C.rb_execution_context_t.offsetof(:cfp)], :rsi)
|
||||||
|
|
||||||
# return a value
|
# return a value
|
||||||
asm.mov(:rax, 7)
|
asm.mov(:rax, 1001)
|
||||||
asm.ret
|
asm.ret
|
||||||
EndBlock
|
EndBlock
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
# https://www.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf
|
||||||
module RubyVM::MJIT
|
module RubyVM::MJIT
|
||||||
class X86Assembler
|
class X86Assembler
|
||||||
ByteWriter = CType::Immediate.parse('char')
|
ByteWriter = CType::Immediate.parse('char')
|
||||||
|
|
||||||
|
### prefix ###
|
||||||
|
# REX = 0100WR0B
|
||||||
|
REX_W = 0b01001000
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@bytes = []
|
@bytes = []
|
||||||
end
|
end
|
||||||
|
@ -23,21 +29,114 @@ module RubyVM::MJIT
|
||||||
@bytes.push(0x48, 0x83, 0xc6, imm)
|
@bytes.push(0x48, 0x83, 0xc6, imm)
|
||||||
end
|
end
|
||||||
|
|
||||||
def mov(reg, val)
|
def mov(dst, src)
|
||||||
case reg
|
case [dst, src]
|
||||||
when :rax
|
# MOV r/m64, imm32
|
||||||
# REX.W [C7] RAX imm32
|
in [Symbol => dst_reg, Integer => src_imm] if r_reg?(dst_reg)
|
||||||
@bytes.push(0x48, 0xc7, 0xc0, val, 0x00, 0x00, 0x00)
|
# REX.W + C7 /0
|
||||||
|
insn(
|
||||||
|
prefix: REX_W,
|
||||||
|
opcode: 0xc7,
|
||||||
|
mod_rm: mod_rm(mod: 0b11, reg: reg_code(dst_reg)),
|
||||||
|
imm: imm32(src_imm),
|
||||||
|
)
|
||||||
|
# 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
|
||||||
|
# REX.W + 89 /r
|
||||||
|
insn(
|
||||||
|
prefix: REX_W,
|
||||||
|
opcode: 0x89,
|
||||||
|
mod_rm: mod_rm(mod: 0b01, reg: reg_code(src_reg), rm: reg_code(dst_reg)), # disp8
|
||||||
|
disp: dst_offset,
|
||||||
|
)
|
||||||
else
|
else
|
||||||
# REX.W [89] [rdi+val],rsi
|
raise NotImplementedError, "mov got not-implemented input: #{reg.inspect}, #{val.inspect}"
|
||||||
@bytes.push(0x48, 0x89, 0x77, reg.last)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# RET
|
||||||
def ret
|
def ret
|
||||||
# Near return
|
# Near return: A return to a procedure within the current code segment
|
||||||
# [C3]
|
insn(opcode: 0xc3)
|
||||||
@bytes.push(0xc3)
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def insn(prefix: nil, opcode:, mod_rm: nil, disp: nil, imm: nil)
|
||||||
|
if prefix
|
||||||
|
@bytes.push(prefix)
|
||||||
|
end
|
||||||
|
@bytes.push(opcode)
|
||||||
|
if mod_rm
|
||||||
|
@bytes.push(mod_rm)
|
||||||
|
end
|
||||||
|
if disp
|
||||||
|
if disp < 0 || disp > 0xff # TODO: support displacement in 2 or 4 bytes as well
|
||||||
|
raise NotImplementedError, "not-implemented disp: #{disp}"
|
||||||
|
end
|
||||||
|
@bytes.push(disp)
|
||||||
|
end
|
||||||
|
if imm
|
||||||
|
@bytes.push(*imm)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reg_code(reg)
|
||||||
|
case reg
|
||||||
|
when :al, :ax, :eax, :rax then 0
|
||||||
|
when :cl, :cx, :ecx, :rcx then 1
|
||||||
|
when :dl, :dx, :edx, :rdx then 2
|
||||||
|
when :bl, :bx, :ebx, :rbx then 3
|
||||||
|
when :ah, :sp, :esp, :rsp then 4
|
||||||
|
when :ch, :bp, :ebp, :rbp then 5
|
||||||
|
when :dh, :si, :esi, :rsi then 6
|
||||||
|
when :bh, :di, :edi, :rdi then 7
|
||||||
|
else raise ArgumentError, "unexpected reg: #{reg.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte
|
||||||
|
#
|
||||||
|
# 7 6 5 4 3 2 1 0
|
||||||
|
# +--+--+--+--+--+--+--+--+
|
||||||
|
# | Mod | Reg/ | R/M |
|
||||||
|
# | | Opcode | |
|
||||||
|
# +--+--+--+--+--+--+--+--+
|
||||||
|
#
|
||||||
|
# The r/m field can specify a register as an operand or it can be combined
|
||||||
|
# with the mod field to encode an addressing mode.
|
||||||
|
#
|
||||||
|
# /0: R/M is 0 (not used)
|
||||||
|
# /r: R/M is a register
|
||||||
|
def mod_rm(mod:, reg:, rm: 0)
|
||||||
|
if mod > 0b11
|
||||||
|
raise ArgumentError, "too large Mod: #{mod}"
|
||||||
|
end
|
||||||
|
if reg > 0b111
|
||||||
|
raise ArgumentError, "too large Reg/Opcode: #{reg}"
|
||||||
|
end
|
||||||
|
if rm > 0b111
|
||||||
|
raise ArgumentError, "too large R/M: #{rm}"
|
||||||
|
end
|
||||||
|
(mod << 6) + (reg << 3) + rm
|
||||||
|
end
|
||||||
|
|
||||||
|
# id: 4 bytes
|
||||||
|
def imm32(imm)
|
||||||
|
bytes = []
|
||||||
|
bits = imm
|
||||||
|
4.times do
|
||||||
|
bytes << (bits & 0xff)
|
||||||
|
bits >>= 8
|
||||||
|
end
|
||||||
|
if bits != 0
|
||||||
|
raise ArgumentError, "unexpected imm32: #{imm}"
|
||||||
|
end
|
||||||
|
bytes
|
||||||
|
end
|
||||||
|
|
||||||
|
def r_reg?(reg)
|
||||||
|
reg.start_with?('r')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,8 +26,7 @@ module RubyVM::MJIT
|
||||||
return if iseq.body.location.label == '<main>'
|
return if iseq.body.location.label == '<main>'
|
||||||
iseq.body.jit_func = compile_block(iseq)
|
iseq.body.jit_func = compile_block(iseq)
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
# TODO: check --mjit-verbose
|
$stderr.puts e.full_message # TODO: check verbose
|
||||||
$stderr.puts e.full_message
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_addr
|
def write_addr
|
||||||
|
@ -36,6 +35,20 @@ module RubyVM::MJIT
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def compile(asm)
|
||||||
|
start_addr = write_addr
|
||||||
|
|
||||||
|
C.mjit_mark_writable
|
||||||
|
@write_pos += asm.compile(start_addr)
|
||||||
|
C.mjit_mark_executable
|
||||||
|
|
||||||
|
end_addr = write_addr
|
||||||
|
if C.mjit_opts.dump_disasm && start_addr < end_addr
|
||||||
|
dump_disasm(start_addr, end_addr)
|
||||||
|
end
|
||||||
|
start_addr
|
||||||
|
end
|
||||||
|
|
||||||
# ec -> RDI, cfp -> RSI
|
# ec -> RDI, cfp -> RSI
|
||||||
def compile_block(iseq)
|
def compile_block(iseq)
|
||||||
addr = write_addr
|
addr = write_addr
|
||||||
|
@ -56,26 +69,12 @@ module RubyVM::MJIT
|
||||||
|
|
||||||
def compile_insn(asm, insn)
|
def compile_insn(asm, insn)
|
||||||
case insn.name
|
case insn.name
|
||||||
when :putnil then @insn_compiler.on_putnil(asm)
|
when :putnil then @insn_compiler.putnil(asm)
|
||||||
when :leave then @insn_compiler.on_leave(asm)
|
when :leave then @insn_compiler.leave(asm)
|
||||||
else raise NotImplementedError, "insn '#{insn.name}' is not supported yet"
|
else raise NotImplementedError, "insn '#{insn.name}' is not supported yet"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def compile(asm)
|
|
||||||
start_addr = write_addr
|
|
||||||
|
|
||||||
C.mjit_mark_writable
|
|
||||||
@write_pos += asm.compile(start_addr)
|
|
||||||
C.mjit_mark_executable
|
|
||||||
|
|
||||||
end_addr = write_addr
|
|
||||||
if C.mjit_opts.dump_disasm && start_addr < end_addr
|
|
||||||
dump_disasm(start_addr, end_addr)
|
|
||||||
end
|
|
||||||
start_addr
|
|
||||||
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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче