diff options
Diffstat (limited to 'test/ruby/rjit/test_assembler.rb')
-rw-r--r-- | test/ruby/rjit/test_assembler.rb | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/test/ruby/rjit/test_assembler.rb b/test/ruby/rjit/test_assembler.rb new file mode 100644 index 0000000000..fbf780d6c3 --- /dev/null +++ b/test/ruby/rjit/test_assembler.rb @@ -0,0 +1,368 @@ +require 'test/unit' +require_relative '../../lib/jit_support' + +return unless JITSupport.rjit_supported? +return unless RubyVM::RJIT.enabled? +return unless RubyVM::RJIT::C.HAVE_LIBCAPSTONE + +require 'stringio' +require 'ruby_vm/rjit/assembler' + +module RubyVM::RJIT + class TestAssembler < Test::Unit::TestCase + MEM_SIZE = 16 * 1024 + + def setup + @mem_block ||= C.mmap(MEM_SIZE) + @cb = CodeBlock.new(mem_block: @mem_block, mem_size: MEM_SIZE) + end + + def test_add + asm = Assembler.new + 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_cmovne + asm = Assembler.new + asm.cmovne(:rax, :rbx) # CMOVNE r64, r/m64 (Mod 11: reg) + assert_compile(asm, <<~EOS) # cmovne == cmovnz + 0x0: cmovne rax, rbx + 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(BytePtr[:rax, 8], 8) # CMP r/m8, imm8 (Mod 01: [reg]+disp8) + asm.cmp(DwordPtr[:rax, 8], 0x100) # CMP r/m32, imm32 (Mod 01: [reg]+disp8) + asm.cmp([:rax, 8], 8) # CMP r/m64, imm8 (Mod 01: [reg]+disp8) + asm.cmp([:rbx, 8], 0x100) # CMP r/m64, imm32 (Mod 01: [reg]+disp8) + asm.cmp([:rax, 0x100], 8) # CMP r/m64, imm8 (Mod 10: [reg]+disp32) + asm.cmp(:rax, 8) # CMP r/m64, imm8 (Mod 11: reg) + asm.cmp(:rax, 0x100) # CMP r/m64, imm32 (Mod 11: reg) + asm.cmp([:rax, 8], :rbx) # CMP r/m64, r64 (Mod 01: [reg]+disp8) + asm.cmp([:rax, -0x100], :rbx) # CMP r/m64, r64 (Mod 10: [reg]+disp32) + asm.cmp(:rax, :rbx) # CMP r/m64, r64 (Mod 11: reg) + assert_compile(asm, <<~EOS) + 0x0: cmp byte ptr [rax + 8], 8 + 0x4: cmp dword ptr [rax + 8], 0x100 + 0xb: cmp qword ptr [rax + 8], 8 + 0x10: cmp qword ptr [rbx + 8], 0x100 + 0x18: cmp qword ptr [rax + 0x100], 8 + 0x20: cmp rax, 8 + 0x24: cmp rax, 0x100 + 0x2b: cmp qword ptr [rax + 8], rbx + 0x2f: cmp qword ptr [rax - 0x100], rbx + 0x36: cmp rax, rbx + 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) # JZ rel8 + asm.write_label(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 2 + 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(:eax, DwordPtr[:rbx, 8]) # MOV r32 r/m32 (Mod 01: [reg]+disp8) + asm.mov(:eax, 0x100) # MOV r32, imm32 (Mod 11: reg) + asm.mov(:rax, [:rbx]) # MOV r64, r/m64 (Mod 00: [reg]) + asm.mov(:rax, [:rbx, 8]) # MOV r64, r/m64 (Mod 01: [reg]+disp8) + asm.mov(:rax, [:rbx, 0x100]) # MOV r64, r/m64 (Mod 10: [reg]+disp32) + asm.mov(:rax, :rbx) # MOV r64, r/m64 (Mod 11: reg) + asm.mov(:rax, 0x100) # MOV r/m64, imm32 (Mod 11: reg) + asm.mov(:rax, 0x100000000) # MOV r64, imm64 + asm.mov(DwordPtr[:rax, 8], 0x100) # MOV r/m32, imm32 (Mod 01: [reg]+disp8) + asm.mov([:rax], 0x100) # MOV r/m64, imm32 (Mod 00: [reg]) + asm.mov([:rax], :rbx) # MOV r/m64, r64 (Mod 00: [reg]) + asm.mov([:rax, 8], 0x100) # MOV r/m64, imm32 (Mod 01: [reg]+disp8) + asm.mov([:rax, 8], :rbx) # MOV r/m64, r64 (Mod 01: [reg]+disp8) + asm.mov([:rax, 0x100], 0x100) # MOV r/m64, imm32 (Mod 10: [reg]+disp32) + asm.mov([:rax, 0x100], :rbx) # MOV r/m64, r64 (Mod 10: [reg]+disp32) + assert_compile(asm, <<~EOS) + 0x0: mov eax, dword ptr [rbx + 8] + 0x3: mov eax, 0x100 + 0x8: mov rax, qword ptr [rbx] + 0xb: mov rax, qword ptr [rbx + 8] + 0xf: mov rax, qword ptr [rbx + 0x100] + 0x16: mov rax, rbx + 0x19: mov rax, 0x100 + 0x20: movabs rax, 0x100000000 + 0x2a: mov dword ptr [rax + 8], 0x100 + 0x31: mov qword ptr [rax], 0x100 + 0x38: mov qword ptr [rax], rbx + 0x3b: mov qword ptr [rax + 8], 0x100 + 0x43: mov qword ptr [rax + 8], rbx + 0x47: mov qword ptr [rax + 0x100], 0x100 + 0x52: mov qword ptr [rax + 0x100], rbx + 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, 8) # SUB r/m64, imm8 (Mod 11: reg) + asm.sub(:rax, :rbx) # SUB r/m64, r64 (Mod 11: reg) + assert_compile(asm, <<~EOS) + 0x0: sub rax, 8 + 0x4: sub rax, rbx + 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 + + def test_xor + asm = Assembler.new + asm.xor(:rax, :rbx) + assert_compile(asm, <<~EOS) + 0x0: xor 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}---" + end + + def compile(asm) + start_addr = @cb.write_addr + @cb.write(asm) + end_addr = @cb.write_addr + + io = StringIO.new + @cb.dump_disasm(start_addr, end_addr, io:, color: false, test: true) + io.seek(0) + disasm = io.read + + disasm.gsub!(/^ /, '') + disasm.sub!(/\n\z/, '') + disasm + end + end +end |