From 93e34fe42e81d72a3e45501bf3f200ca5e78442f Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Fri, 10 Mar 2023 14:09:34 -0800 Subject: [PATCH] RJIT: Write initial tests for Assembler --- lib/ruby_vm/rjit/assembler.rb | 16 +- test/ruby/rjit/test_assembler.rb | 265 ++++++++++++++++++++++++++++++- 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)}")