summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ruby_vm/rjit/assembler.rb16
-rw-r--r--test/ruby/rjit/test_assembler.rb265
2 files changed, 266 insertions, 15 deletions
diff --git a/lib/ruby_vm/rjit/assembler.rb b/lib/ruby_vm/rjit/assembler.rb
index 5637083866..f4a7c235ec 100644
--- a/lib/ruby_vm/rjit/assembler.rb
+++ b/lib/ruby_vm/rjit/assembler.rb
@@ -157,7 +157,7 @@ module RubyVM::RJIT
# D: Operand 1: Offset
insn(opcode: 0xe8, imm: rel32(dst_addr))
# CALL r/m64 (Mod 11: reg)
- in Symbol => dst_reg
+ in Symbol => dst_reg if r64?(dst_reg)
# FF /2
# M: Operand 1: ModRM:r/m (r)
insn(
@@ -172,7 +172,7 @@ module RubyVM::RJIT
def cmove(dst, src)
case [dst, src]
# CMOVE r64, r/m64 (Mod 11: reg)
- in [Symbol => dst_reg, Symbol => src_reg]
+ in [Symbol => dst_reg, Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 0F 44 /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn(
@@ -188,7 +188,7 @@ module RubyVM::RJIT
def cmovg(dst, src)
case [dst, src]
# CMOVG r64, r/m64 (Mod 11: reg)
- in [Symbol => dst_reg, Symbol => src_reg]
+ in [Symbol => dst_reg, Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 0F 4F /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn(
@@ -204,7 +204,7 @@ module RubyVM::RJIT
def cmovge(dst, src)
case [dst, src]
# CMOVGE r64, r/m64 (Mod 11: reg)
- in [Symbol => dst_reg, Symbol => src_reg]
+ in [Symbol => dst_reg, Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 0F 4D /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn(
@@ -220,7 +220,7 @@ module RubyVM::RJIT
def cmovl(dst, src)
case [dst, src]
# CMOVL r64, r/m64 (Mod 11: reg)
- in [Symbol => dst_reg, Symbol => src_reg]
+ in [Symbol => dst_reg, Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 0F 4C /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn(
@@ -236,7 +236,7 @@ module RubyVM::RJIT
def cmovle(dst, src)
case [dst, src]
# CMOVLE r64, r/m64 (Mod 11: reg)
- in [Symbol => dst_reg, Symbol => src_reg]
+ in [Symbol => dst_reg, Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 0F 4E /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn(
@@ -431,11 +431,11 @@ module RubyVM::RJIT
# E9 cd
insn(opcode: 0xe9, imm: rel32(dst_addr))
# JMP r/m64 (Mod 01: [reg]+disp8)
- in Array[Symbol => dst_reg, Integer => dst_disp] if imm8?(dst_disp)
+ in Array[Symbol => dst_reg, Integer => dst_disp] if r64?(dst_reg) && imm8?(dst_disp)
# FF /4
insn(opcode: 0xff, mod_rm: ModRM[mod: Mod01, reg: 4, rm: dst_reg], disp: dst_disp)
# JMP r/m64 (Mod 11: reg)
- in Symbol => dst_reg
+ in Symbol => dst_reg if r64?(dst_reg)
# FF /4
insn(opcode: 0xff, mod_rm: ModRM[mod: Mod11, reg: 4, rm: dst_reg])
else
diff --git a/test/ruby/rjit/test_assembler.rb b/test/ruby/rjit/test_assembler.rb
index 12d73b2410..d32f6676db 100644
--- a/test/ruby/rjit/test_assembler.rb
+++ b/test/ruby/rjit/test_assembler.rb
@@ -19,24 +19,270 @@ module RubyVM::RJIT
def test_add
asm = Assembler.new
- asm.add(:rax, 255)
- assert_compile(asm, '0x0: add rax, 0xff')
+ asm.add([:rcx], 1) # ADD r/m64, imm8 (Mod 00: [reg])
+ asm.add(:rax, 0x7f) # ADD r/m64, imm8 (Mod 11: reg)
+ asm.add(:rbx, 0x7fffffff) # ADD r/m64 imm32 (Mod 11: reg)
+ asm.add(:rsi, :rdi) # ADD r/m64, r64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: add qword ptr [rcx], 1
+ 0x4: add rax, 0x7f
+ 0x8: add rbx, 0x7fffffff
+ 0xf: add rsi, rdi
+ EOS
+ end
+
+ def test_and
+ asm = Assembler.new
+ asm.and(:rax, 0) # AND r/m64, imm8 (Mod 11: reg)
+ asm.and(:rbx, 0x7fffffff) # AND r/m64, imm32 (Mod 11: reg)
+ asm.and(:rcx, [:rdi, 8]) # AND r64, r/m64 (Mod 01: [reg]+disp8)
+ assert_compile(asm, <<~EOS)
+ 0x0: and rax, 0
+ 0x4: and rbx, 0x7fffffff
+ 0xb: and rcx, qword ptr [rdi + 8]
+ EOS
+ end
+
+ def test_call
+ asm = Assembler.new
+ asm.call(rel32(0xff)) # CALL rel32
+ asm.call(:rax) # CALL r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: call 0xff
+ 0x5: call rax
+ EOS
+ end
+
+ def test_cmove
+ asm = Assembler.new
+ asm.cmove(:rax, :rcx) # CMOVE r64, r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: cmove rax, rcx
+ EOS
+ end
+
+ def test_cmovg
+ asm = Assembler.new
+ asm.cmovg(:rbx, :rdi) # CMOVG r64, r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: cmovg rbx, rdi
+ EOS
+ end
+
+ def test_cmovge
+ asm = Assembler.new
+ asm.cmovge(:rsp, :rbp) # CMOVGE r64, r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: cmovge rsp, rbp
+ EOS
+ end
+
+ def test_cmovl
+ asm = Assembler.new
+ asm.cmovl(:rdx, :rsp) # CMOVL r64, r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: cmovl rdx, rsp
+ EOS
+ end
+
+ def test_cmovle
+ asm = Assembler.new
+ asm.cmovle(:rax, :rax) # CMOVLE r64, r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: cmovle rax, rax
+ EOS
+ end
+
+ def test_cmovnz
+ asm = Assembler.new
+ asm.cmovnz(:rax, :rbx) # CMOVNZ r64, r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS) # cmovne == cmovnz
+ 0x0: cmovne rax, rbx
+ EOS
+ end
+
+ def test_cmovz
+ asm = Assembler.new
+ asm.cmovz(:rax, :rbx) # CMOVZ r64, r/m64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS) # cmove == cmovz
+ 0x0: cmove rax, rbx
+ EOS
+ end
+
+ def test_cmp
+ asm = Assembler.new
+ asm.cmp(:rax, 0) # CMP r/m64, imm8 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: cmp rax, 0
+ EOS
+ end
+
+ def test_jbe
+ asm = Assembler.new
+ asm.jbe(rel32(0xff)) # JBE rel32
+ assert_compile(asm, <<~EOS)
+ 0x0: jbe 0xff
+ EOS
+ end
+
+ def test_je
+ asm = Assembler.new
+ asm.je(rel32(0xff)) # JE rel32
+ assert_compile(asm, <<~EOS)
+ 0x0: je 0xff
+ EOS
+ end
+
+ def test_jl
+ asm = Assembler.new
+ asm.jl(rel32(0xff)) # JL rel32
+ assert_compile(asm, <<~EOS)
+ 0x0: jl 0xff
+ EOS
end
def test_jmp
asm = Assembler.new
label = asm.new_label('label')
- asm.jmp(label)
+ asm.jmp(label) # JZ rel8
asm.write_label(label)
- asm.jmp(label)
+ asm.jmp(rel32(0xff)) # JMP rel32
+ asm.jmp([:rax, 8]) # JMP r/m64 (Mod 01: [reg]+disp8)
+ asm.jmp(:rax) # JMP r/m64 (Mod 11: reg)
assert_compile(asm, <<~EOS)
0x0: jmp 0x2
- 0x2: jmp 0x2
+ 0x2: jmp 0xff
+ 0x7: jmp qword ptr [rax + 8]
+ 0xa: jmp rax
+ EOS
+ end
+
+ def test_jne
+ asm = Assembler.new
+ asm.jne(rel32(0xff)) # JNE rel32
+ assert_compile(asm, <<~EOS)
+ 0x0: jne 0xff
+ EOS
+ end
+
+ def test_jnz
+ asm = Assembler.new
+ asm.jnz(rel32(0xff)) # JNZ rel32
+ assert_compile(asm, <<~EOS)
+ 0x0: jne 0xff
+ EOS
+ end
+
+ def test_jo
+ asm = Assembler.new
+ asm.jo(rel32(0xff)) # JO rel32
+ assert_compile(asm, <<~EOS)
+ 0x0: jo 0xff
+ EOS
+ end
+
+ def test_jz
+ asm = Assembler.new
+ asm.jz(rel32(0xff)) # JZ rel32
+ assert_compile(asm, <<~EOS)
+ 0x0: je 0xff
+ EOS
+ end
+
+ def test_lea
+ asm = Assembler.new
+ asm.lea(:rax, [:rax, 8]) # LEA r64,m (Mod 01: [reg]+disp8)
+ asm.lea(:rax, [:rax, 0xffff]) # LEA r64,m (Mod 10: [reg]+disp32)
+ assert_compile(asm, <<~EOS)
+ 0x0: lea rax, [rax + 8]
+ 0x4: lea rax, [rax + 0xffff]
+ EOS
+ end
+
+ def test_mov
+ asm = Assembler.new
+ asm.mov(:rax, :rbx) # MOV r64, r/m64 (Mod 00: [reg])
+ asm.mov(:rax, [:rbx, 8]) # MOV r64, r/m64 (Mod 01: [reg]+disp8)
+ assert_compile(asm, <<~EOS)
+ 0x0: mov rax, rbx
+ 0x3: mov rax, qword ptr [rbx + 8]
+ EOS
+ end
+
+ def test_or
+ asm = Assembler.new
+ asm.or(:rax, 0) # OR r/m64, imm8 (Mod 11: reg)
+ asm.or(:rax, 0xffff) # OR r/m64, imm32 (Mod 11: reg)
+ asm.or(:rax, [:rbx, 8]) # OR r64, r/m64 (Mod 01: [reg]+disp8)
+ assert_compile(asm, <<~EOS)
+ 0x0: or rax, 0
+ 0x4: or rax, 0xffff
+ 0xb: or rax, qword ptr [rbx + 8]
+ EOS
+ end
+
+ def test_push
+ asm = Assembler.new
+ asm.push(:rax) # PUSH r64
+ assert_compile(asm, <<~EOS)
+ 0x0: push rax
+ EOS
+ end
+
+ def test_pop
+ asm = Assembler.new
+ asm.pop(:rax) # POP r64
+ assert_compile(asm, <<~EOS)
+ 0x0: pop rax
+ EOS
+ end
+
+ def test_ret
+ asm = Assembler.new
+ asm.ret # RET
+ assert_compile(asm, "0x0: ret \n")
+ end
+
+ def test_sar
+ asm = Assembler.new
+ asm.sar(:rax, 0) # SAR r/m64, imm8 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: sar rax, 0
+ EOS
+ end
+
+ def test_sub
+ asm = Assembler.new
+ asm.sub(:rax, 0) # SUB r/m64, imm8 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: sub rax, 0
+ EOS
+ end
+
+ def test_test
+ asm = Assembler.new
+ asm.test(BytePtr[:rax, 8], 16) # TEST r/m8*, imm8 (Mod 01: [reg]+disp8)
+ asm.test([:rax, 8], 8) # TEST r/m64, imm32 (Mod 01: [reg]+disp8)
+ asm.test([:rax, 0xffff], 0xffff) # TEST r/m64, imm32 (Mod 10: [reg]+disp32)
+ asm.test(:rax, 0xffff) # TEST r/m64, imm32 (Mod 11: reg)
+ asm.test(:eax, :ebx) # TEST r/m32, r32 (Mod 11: reg)
+ asm.test(:rax, :rbx) # TEST r/m64, r64 (Mod 11: reg)
+ assert_compile(asm, <<~EOS)
+ 0x0: test byte ptr [rax + 8], 0x10
+ 0x4: test qword ptr [rax + 8], 8
+ 0xc: test qword ptr [rax + 0xffff], 0xffff
+ 0x17: test rax, 0xffff
+ 0x1e: test eax, ebx
+ 0x20: test rax, rbx
EOS
end
private
+ def rel32(offset)
+ @cb.write_addr + 0xff
+ end
+
def assert_compile(asm, expected)
actual = compile(asm)
assert_equal expected, actual, "---\n#{actual}---"
@@ -54,8 +300,13 @@ module RubyVM::RJIT
disasm.gsub!(/^ /, '')
disasm.sub!(/\n\z/, '')
- if disasm.lines.size == 1
- disasm.rstrip!
+ disasm.gsub!(/0x(\h{12})/) do
+ offset = $1.to_i(16) - start_addr
+ if offset.negative?
+ "-0x#{offset.to_s(16)}"
+ else
+ "0x#{offset.to_s(16)}"
+ end
end
(start_addr...end_addr).each do |addr|
disasm.gsub!("0x#{addr.to_s(16)}", "0x#{(addr - start_addr).to_s(16)}")