summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mjit/insn_compiler.rb6
-rw-r--r--lib/mjit/x86_assembler.rb119
-rw-r--r--lib/ruby_vm/mjit/compiler.rb35
3 files changed, 129 insertions, 31 deletions
diff --git a/lib/mjit/insn_compiler.rb b/lib/mjit/insn_compiler.rb
index dbe3690dff..c6f88b37fb 100644
--- a/lib/mjit/insn_compiler.rb
+++ b/lib/mjit/insn_compiler.rb
@@ -1,17 +1,17 @@
module RubyVM::MJIT
class InsnCompiler
- def on_putnil(_asm)
+ def putnil(_asm)
# TODO
KeepCompiling
end
- def on_leave(asm)
+ def leave(asm)
# pop the current frame (ec->cfp++)
asm.add(:rsi, C.rb_control_frame_t.size)
asm.mov([:rdi, C.rb_execution_context_t.offsetof(:cfp)], :rsi)
# return a value
- asm.mov(:rax, 7)
+ asm.mov(:rax, 1001)
asm.ret
EndBlock
end
diff --git a/lib/mjit/x86_assembler.rb b/lib/mjit/x86_assembler.rb
index f04a9ea6b8..f5414077fc 100644
--- a/lib/mjit/x86_assembler.rb
+++ b/lib/mjit/x86_assembler.rb
@@ -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
class X86Assembler
ByteWriter = CType::Immediate.parse('char')
+ ### prefix ###
+ # REX = 0100WR0B
+ REX_W = 0b01001000
+
def initialize
@bytes = []
end
@@ -23,21 +29,114 @@ module RubyVM::MJIT
@bytes.push(0x48, 0x83, 0xc6, imm)
end
- def mov(reg, val)
- case reg
- when :rax
- # REX.W [C7] RAX imm32
- @bytes.push(0x48, 0xc7, 0xc0, val, 0x00, 0x00, 0x00)
+ def mov(dst, src)
+ case [dst, src]
+ # MOV r/m64, imm32
+ in [Symbol => dst_reg, Integer => src_imm] if r_reg?(dst_reg)
+ # 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
- # REX.W [89] [rdi+val],rsi
- @bytes.push(0x48, 0x89, 0x77, reg.last)
+ raise NotImplementedError, "mov got not-implemented input: #{reg.inspect}, #{val.inspect}"
end
end
+ # RET
def ret
- # Near return
- # [C3]
- @bytes.push(0xc3)
+ # Near return: A return to a procedure within the current code segment
+ insn(opcode: 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
diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb
index c9dadf6790..75314501c1 100644
--- a/lib/ruby_vm/mjit/compiler.rb
+++ b/lib/ruby_vm/mjit/compiler.rb
@@ -26,8 +26,7 @@ module RubyVM::MJIT
return if iseq.body.location.label == '<main>'
iseq.body.jit_func = compile_block(iseq)
rescue Exception => e
- # TODO: check --mjit-verbose
- $stderr.puts e.full_message
+ $stderr.puts e.full_message # TODO: check verbose
end
def write_addr
@@ -36,6 +35,20 @@ module RubyVM::MJIT
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
def compile_block(iseq)
addr = write_addr
@@ -56,26 +69,12 @@ module RubyVM::MJIT
def compile_insn(asm, insn)
case insn.name
- when :putnil then @insn_compiler.on_putnil(asm)
- when :leave then @insn_compiler.on_leave(asm)
+ 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)
- 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)
INSNS.fetch(C.rb_vm_insn_decode(encoded))
end