From ba37e241cefd3cb776c63e8fbcc3d8c1c47dddf6 Mon Sep 17 00:00:00 2001 From: water Date: Wed, 2 Sep 2020 17:34:33 -0400 Subject: [PATCH] all basic integer and floating point instructions are implemented --- goalc/emitter/IGen.h | 286 +++++++++++++++++++++++++-- goalc/old_emitter/IGen.h | 306 ----------------------------- goalc/old_emitter/registers.cpp | 20 -- goalc/old_emitter/registers.h | 111 ----------- test/test_emitter_integer_math.cpp | 235 ++++++++++++++++++++++ test/test_emitter_xmm32.cpp | 216 ++++++++++++++++++++ 6 files changed, 724 insertions(+), 450 deletions(-) delete mode 100644 goalc/old_emitter/IGen.h delete mode 100644 goalc/old_emitter/registers.cpp delete mode 100644 goalc/old_emitter/registers.h diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index 1703295f37..978329adf6 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -1451,35 +1451,295 @@ class IGen { // SHIFTS //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // sllv reg, imm - // srlv reg, imm - // srav reg, imm + /*! + * Shift 64-bit gpr left by CL register + */ + static Instruction shl_gpr64_cl(uint8_t reg) { + Instruction instr(0xd3); + instr.set_modrm_and_rex(4, reg, 3, true); + return instr; + } + + /*! + * Shift 64-bit gpr right (logical) by CL register + */ + static Instruction shr_gpr64_cl(uint8_t reg) { + Instruction instr(0xd3); + instr.set_modrm_and_rex(5, reg, 3, true); + return instr; + } + + /*! + * Shift 64-bit gpr right (arithmetic) by CL register + */ + static Instruction sar_gpr64_cl(uint8_t reg) { + Instruction instr(0xd3); + instr.set_modrm_and_rex(7, reg, 3, true); + return instr; + } + + /*! + * Shift 64-ptr left (logical) by the constant shift amount "sa". + */ + static Instruction shl_gpr64_u8(uint8_t reg, uint8_t sa) { + Instruction instr(0xc1); + instr.set_modrm_and_rex(4, reg, 3, true); + instr.set(Imm(1, sa)); + return instr; + } + + /*! + * Shift 64-ptr right (logical) by the constant shift amount "sa". + */ + static Instruction shr_gpr64_u8(uint8_t reg, uint8_t sa) { + Instruction instr(0xc1); + instr.set_modrm_and_rex(5, reg, 3, true); + instr.set(Imm(1, sa)); + return instr; + } + + /*! + * Shift 64-ptr right (arithmetic) by the constant shift amount "sa". + */ + static Instruction sar_gpr64_u8(uint8_t reg, uint8_t sa) { + Instruction instr(0xc1); + instr.set_modrm_and_rex(7, reg, 3, true); + instr.set(Imm(1, sa)); + return instr; + } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // CONTROL FLOW //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // call, jump reg + /*! + * Jump, 32-bit constant offset. The offset is by default 0 and must be patched later. + */ + static Instruction jmp_32() { + Instruction instr(0xe9); + instr.set(Imm(4, 0)); + return instr; + } - // jump imm8, imm32 ?? (is there an imm16?) + /*! + * Jump if equal. + */ + static Instruction je_32() { + Instruction instr(0x0f); + instr.set_op2(0x84); + instr.set(Imm(4, 0)); + return instr; + } - // je, jne, jle, jge, jl, jg, jbe, jae, jb, ja + /*! + * Jump not equal. + */ + static Instruction jne_32() { + Instruction instr(0x0f); + instr.set_op2(0x85); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump less than or equal. + */ + static Instruction jle_32() { + Instruction instr(0x0f); + instr.set_op2(0x8e); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump greater than or equal. + */ + static Instruction jge_32() { + Instruction instr(0x0f); + instr.set_op2(0x8d); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump less than + */ + static Instruction jl_32() { + Instruction instr(0x0f); + instr.set_op2(0x8c); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump greater than + */ + static Instruction jg_32() { + Instruction instr(0x0f); + instr.set_op2(0x8f); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump below or equal + */ + static Instruction jbe_32() { + Instruction instr(0x0f); + instr.set_op2(0x86); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump above or equal + */ + static Instruction jae_32() { + Instruction instr(0x0f); + instr.set_op2(0x83); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump below + */ + static Instruction jb_32() { + Instruction instr(0x0f); + instr.set_op2(0x82); + instr.set(Imm(4, 0)); + return instr; + } + + /*! + * Jump above + */ + static Instruction ja_32() { + Instruction instr(0x0f); + instr.set_op2(0x87); + instr.set(Imm(4, 0)); + return instr; + } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // FLOAT MATH //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // cmp_flt - // mulss - // divss - // subss - // addss - // float to int - // int to float + /*! + * Compare two floats and set flag register for jump (ucomiss) + */ + static Instruction cmp_flt_flt(Register a, Register b) { + assert(a.is_xmm()); + assert(b.is_xmm()); + Instruction instr(0x0f); + instr.set_op2(0x2e); + instr.set_modrm_and_rex(a.hw_id(), b.hw_id(), 3, false); + return instr; + } + + /*! + * Multiply two floats in xmm's + */ + static Instruction mulss_xmm_xmm(Register dst, Register src) { + assert(dst.is_xmm()); + assert(src.is_xmm()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x59); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + + /*! + * Divide two floats in xmm's + */ + static Instruction divss_xmm_xmm(Register dst, Register src) { + assert(dst.is_xmm()); + assert(src.is_xmm()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x5e); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + + /*! + * Subtract two floats in xmm's + */ + static Instruction subss_xmm_xmm(Register dst, Register src) { + assert(dst.is_xmm()); + assert(src.is_xmm()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x5c); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + + /*! + * Add two floats in xmm's + */ + static Instruction addss_xmm_xmm(Register dst, Register src) { + assert(dst.is_xmm()); + assert(src.is_xmm()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x58); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + + /*! + * Convert GPR int32 to XMM float (single precision) + */ + static Instruction int32_to_float(Register dst, Register src) { + assert(dst.is_xmm()); + assert(src.is_gpr()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x2a); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + + /*! + * Convert XMM float to GPR int32(single precision) (truncate) + */ + static Instruction float_to_int32(Register dst, Register src) { + assert(dst.is_gpr()); + assert(src.is_xmm()); + Instruction instr(0xf3); + instr.set_op2(0x0f); + instr.set_op3(0x2c); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false); + instr.swap_op0_rex(); + return instr; + } + + // eventually... + // sqrt + // rsqrt + // abs //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // UTILITIES //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + /*! + * A "null" instruction. This instruction does not generate any bytes + * but can be referred to by a label. Useful to insert in place of a real instruction + * if the real instruction has been optimized out. + */ + static Instruction null() { + Instruction i(0); + i.is_null = true; + return i; + } }; } // namespace emitter diff --git a/goalc/old_emitter/IGen.h b/goalc/old_emitter/IGen.h deleted file mode 100644 index ba95ca87ba..0000000000 --- a/goalc/old_emitter/IGen.h +++ /dev/null @@ -1,306 +0,0 @@ -#ifndef JAK1_IGEN_H -#define JAK1_IGEN_H - -#include -#include "Instruction.h" -#include "registers.h" - -namespace goal { -class IGen { - public: - // - // /*! - // * Shift 64-bit gpr left by CL register - // */ - // static Instruction shl_gpr64_cl(uint8_t reg) { - // Instruction instr(0xd3); - // instr.set_modrm_and_rex(4, reg, 3, true); - // return instr; - // } - // - // /*! - // * Shift 64-bit gpr right (logical) by CL register - // */ - // static Instruction shr_gpr64_cl(uint8_t reg) { - // Instruction instr(0xd3); - // instr.set_modrm_and_rex(5, reg, 3, true); - // return instr; - // } - // - // /*! - // * Shift 64-bit gpr right (arithmetic) by CL register - // */ - // static Instruction sar_gpr64_cl(uint8_t reg) { - // Instruction instr(0xd3); - // instr.set_modrm_and_rex(7, reg, 3, true); - // return instr; - // } - // - // /*! - // * Shift 64-ptr left (logical) by the constant shift amount "sa". - // */ - // static Instruction shl_gpr64_u8(uint8_t reg, uint8_t sa) { - // Instruction instr(0xc1); - // instr.set_modrm_and_rex(4, reg, 3, true); - // instr.set(Imm(1, sa)); - // return instr; - // } - // - // /*! - // * Shift 64-ptr right (logical) by the constant shift amount "sa". - // */ - // static Instruction shr_gpr64_u8(uint8_t reg, uint8_t sa) { - // Instruction instr(0xc1); - // instr.set_modrm_and_rex(5, reg, 3, true); - // instr.set(Imm(1, sa)); - // return instr; - // } - // - // /*! - // * Shift 64-ptr right (arithmetic) by the constant shift amount "sa". - // */ - // static Instruction sar_gpr64_u8(uint8_t reg, uint8_t sa) { - // Instruction instr(0xc1); - // instr.set_modrm_and_rex(7, reg, 3, true); - // instr.set(Imm(1, sa)); - // return instr; - // } - // - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // // CONTROL FLOW - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // - // /*! - // * Jump, 32-bit constant offset. The offset is by default 0 and must be patched later. - // */ - // static Instruction jmp_32() { - // Instruction instr(0xe9); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump if equal. - // * TODO - can we get away with 16 bits? - // */ - // static Instruction je_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x84); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump not equal. - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jne_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x85); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump less than or equal. - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jle_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x8e); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump greater than or equal. - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jge_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x8d); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump less than - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jl_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x8c); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump greater than - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jg_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x8f); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump below or equal - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jbe_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x86); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump above or equal - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jae_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x83); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump below - // * TODO - can we get away with 16 bits? - // */ - // static Instruction jb_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x82); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // /*! - // * Jump above - // * TODO - can we get away with 16 bits? - // */ - // static Instruction ja_32() { - // Instruction instr(0x0f); - // instr.set_op2(0x87); - // instr.set(Imm(4, 0)); - // return instr; - // } - // - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // // FLOAT MATH - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // - // /*! - // * Compare two floats and set flag register for jump - // */ - // static Instruction cmp_flt_flt(uint8_t a, uint8_t b) { - // Instruction instr(0x0f); - // instr.set_op2(0x2e); - // instr.set_modrm_and_rex(a, b, 3, false); - // return instr; - // } - // - // /*! - // * Multiply two floats in xmm's - // */ - // static Instruction mulss_xmm_xmm(uint8_t dst, uint8_t src) { - // Instruction instr(0xf3); - // instr.set_op2(0x0f); - // instr.set_op3(0x59); - // instr.set_modrm_and_rex(dst, src, 3, false); - // instr.swap_op0_rex(); - // return instr; - // } - // - // /*! - // * Divide two floats in xmm's - // */ - // static Instruction divss_xmm_xmm(uint8_t dst, uint8_t src) { - // Instruction instr(0xf3); - // instr.set_op2(0x0f); - // instr.set_op3(0x5e); - // instr.set_modrm_and_rex(dst, src, 3, false); - // instr.swap_op0_rex(); - // return instr; - // } - // - // /*! - // * Subtract two floats in xmm's - // */ - // static Instruction subss_xmm_xmm(uint8_t dst, uint8_t src) { - // Instruction instr(0xf3); - // instr.set_op2(0x0f); - // instr.set_op3(0x5c); - // instr.set_modrm_and_rex(dst, src, 3, false); - // instr.swap_op0_rex(); - // return instr; - // } - // - // /*! - // * Add two floats in xmm's - // */ - // static Instruction addss_xmm_xmm(uint8_t dst, uint8_t src) { - // Instruction instr(0xf3); - // instr.set_op2(0x0f); - // instr.set_op3(0x58); - // instr.set_modrm_and_rex(dst, src, 3, false); - // instr.swap_op0_rex(); - // return instr; - // } - // - // /*! - // * Convert GPR int32 to XMM float (single precision) - // */ - // static Instruction int32_to_float(uint8_t dst, uint8_t src) { - // Instruction instr(0xf3); - // instr.set_op2(0x0f); - // instr.set_op3(0x2a); - // instr.set_modrm_and_rex(dst, src, 3, false); - // instr.swap_op0_rex(); - // return instr; - // } - // - // /*! - // * Convert XMM float to GPR int32(single precision) (truncate) - // */ - // static Instruction float_to_int64(uint8_t dst, uint8_t src) { - // Instruction instr(0xf3); - // instr.set_op2(0x0f); - // instr.set_op3(0x2c); - // instr.set_modrm_and_rex(dst, src, 3, true); - // instr.swap_op0_rex(); - // return instr; - // } - // - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // // UTILITIES - // //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - // - // /*! - // * A "null" instruction. This instruction does not generate any bytes - // * but can be referred to by a label. Useful to insert in place of a real instruction - // * if the real instruction has been optimized out. - // */ - // static Instruction null() { - // Instruction i(0); - // i.is_null = true; - // return i; - // } - // - // /*! - // * A "function start" instruction. This emits no opcodes, but is used - // * to determine where to insert the function type tag and how to align a function. - // */ - // static Instruction function_start() { - // Instruction i(0); - // i.is_null = true; - // i.is_function_start = true; - // return i; - // } -}; -} // namespace goal - -#endif // JAK1_IGEN_H diff --git a/goalc/old_emitter/registers.cpp b/goalc/old_emitter/registers.cpp deleted file mode 100644 index 480386bf57..0000000000 --- a/goalc/old_emitter/registers.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "registers.h" - -namespace goal { -bool is_gpr(u8 reg) { - return reg <= R15; -} - -u8 get_nth_xmm(u8 id) { - return id + XMM0; -} - -bool is_xmm(u8 reg) { - return reg >= XMM0 && reg <= XMM15; -} - -u8 xmm_to_id(u8 reg) { - return reg - 16; -} - -} // namespace goal \ No newline at end of file diff --git a/goalc/old_emitter/registers.h b/goalc/old_emitter/registers.h deleted file mode 100644 index 4d6892c189..0000000000 --- a/goalc/old_emitter/registers.h +++ /dev/null @@ -1,111 +0,0 @@ -/*! - * @file registers.h - * Definitions and conventions for x86-64 registers. - */ - -#ifndef JAK1_REGISTERS_H -#define JAK1_REGISTERS_H - -#include "common/common_types.h" - -namespace goal { -enum X86R : u8 { - RAX, // return, temp - RCX, // arg 3 - RDX, // arg 2 - RBX, // X saved - - RSP, // stack pointer - RBP, // X base pointer (like fp) - RSI, // arg 1 - RDI, // arg 0 - - R8, // arg 4 - R9, // arg 5, saved - R10, // arg 6, saved (arg in GOAL only) - R11, // arg 7, saved (arg in GOAL only) - R12, // X saved - pp register (like s6) - R13, // X saved - function call register (like t9) - R14, // X saved - offset (added in GOAL x86) - R15, // X saved - st (like s7) - XMM0, - XMM1, - XMM2, - XMM3, - XMM4, - XMM5, - XMM6, - XMM7, - XMM8, - XMM9, - XMM10, - XMM11, - XMM12, - XMM13, - XMM14, - XMM15 -}; - -// the argument registers of GOAL. -// We must have 8 to be compatible with GOAL's 8-argument function calls. -constexpr int ARG_REG_COUNT = 8; - -// the first 6 are shared with Linux, and the last two are unique to GOAL. -constexpr X86R ARG_REGS[ARG_REG_COUNT] = { - X86R::RDI, X86R::RSI, X86R::RDX, X86R::RCX, X86R::R8, X86R::R9, X86R::R10, X86R::R11, -}; - -// The saved registers of GOAL. Note that RSP, RBP, R12, R13, R14, R15 shouldn't be changed by the -// caller, but these are special registers and won't be allocated to hold variables. -constexpr int SAVED_REG_COUNT = 4; -constexpr X86R SAVED_REGS[SAVED_REG_COUNT] = {X86R::RBX, X86R::R9, X86R::R10, X86R::R11}; - -// special registers -constexpr X86R PP_REG = X86R::R12; -constexpr X86R FUNC_REG = X86R::R13; -constexpr X86R OFF_REG = X86R::R14; -constexpr X86R ST_REG = X86R::R15; -constexpr X86R FP_REG = X86R::RBP; -constexpr X86R RET_REG = X86R::RAX; - -// size in bytes of a pointer -constexpr int PTR_SIZE = 4; - -// size in bytes of a general purpose register -constexpr int GPR_SIZE = 8; - -constexpr const char* x86_gpr_names[] = { - "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", - "r11", "r12", "r13", "r14", "r15", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", - "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"}; - -/* - Name Arg ID Clobber? Special - RAX - y return - RCX 3 y arg - RDX 2 y arg - RBX - n - - RSP - n stack pointer - RBP - n base pointer - RSI 1 y arg - RDI 0 y arg - - R8 4 y arg - R9 5 n arg - R10 6 n arg - R11 7 n arg - R12 - n pp - R13 - n func - R14 - n - R15 - */ - -bool is_gpr(u8 reg); -u8 get_nth_xmm(u8 id); -bool is_xmm(u8 reg); -u8 xmm_to_id(u8 reg); - -} // namespace goal - -#endif // JAK1_REGISTERS_H diff --git a/test/test_emitter_integer_math.cpp b/test/test_emitter_integer_math.cpp index 325e28f278..1eb8dd926a 100644 --- a/test/test_emitter_integer_math.cpp +++ b/test/test_emitter_integer_math.cpp @@ -390,4 +390,239 @@ TEST(EmitterIntegerMath, not_gpr64) { EXPECT_EQ(result, expected); } } +} + +TEST(EmitterIntegerMath, shl_gpr64_cl) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector sas = {0, 1, 23, 53, 64}; + + for (int i = 0; i < 16; i++) { + if (i == RSP || i == RCX) { + continue; + } + for (auto v : vals) { + for (auto sa : sas) { + auto expected = v << sa; + tester.clear(); + tester.emit_push_all_gprs(true); + tester.emit(IGen::mov_gpr64_u64(i, v)); + tester.emit(IGen::mov_gpr64_u64(RCX, sa)); + tester.emit(IGen::shl_gpr64_cl(i)); + tester.emit(IGen::mov_gpr64_gpr64(RAX, i)); + tester.emit_pop_all_gprs(true); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, shr_gpr64_cl) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN), + INT64_MAX, 117, 32, u64(-348473), 83747382}; + std::vector sas = {0, 1, 23, 53, 64}; + + for (int i = 0; i < 16; i++) { + if (i == RSP || i == RCX) { + continue; + } + for (auto v : vals) { + for (auto sa : sas) { + auto expected = v >> sa; + tester.clear(); + tester.emit_push_all_gprs(true); + tester.emit(IGen::mov_gpr64_u64(i, v)); + tester.emit(IGen::mov_gpr64_u64(RCX, sa)); + tester.emit(IGen::shr_gpr64_cl(i)); + tester.emit(IGen::mov_gpr64_gpr64(RAX, i)); + tester.emit_pop_all_gprs(true); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, sar_gpr64_cl) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector sas = {0, 1, 23, 53, 64}; + + for (int i = 0; i < 16; i++) { + if (i == RSP || i == RCX) { + continue; + } + for (auto v : vals) { + for (auto sa : sas) { + auto expected = v >> sa; + tester.clear(); + tester.emit_push_all_gprs(true); + tester.emit(IGen::mov_gpr64_u64(i, v)); + tester.emit(IGen::mov_gpr64_u64(RCX, sa)); + tester.emit(IGen::sar_gpr64_cl(i)); + tester.emit(IGen::mov_gpr64_gpr64(RAX, i)); + tester.emit_pop_all_gprs(true); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, shl_gpr64_u8) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector sas = {0, 1, 23, 53, 64}; + + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (auto v : vals) { + for (auto sa : sas) { + auto expected = v << sa; + tester.clear(); + tester.emit_push_all_gprs(true); + tester.emit(IGen::mov_gpr64_u64(i, v)); + tester.emit(IGen::shl_gpr64_u8(i, sa)); + tester.emit(IGen::mov_gpr64_gpr64(RAX, i)); + tester.emit_pop_all_gprs(true); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, shr_gpr64_u8) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN), + INT64_MAX, 117, 32, u64(-348473), 83747382}; + std::vector sas = {0, 1, 23, 53, 64}; + + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (auto v : vals) { + for (auto sa : sas) { + auto expected = v >> sa; + tester.clear(); + tester.emit_push_all_gprs(true); + tester.emit(IGen::mov_gpr64_u64(i, v)); + tester.emit(IGen::shr_gpr64_u8(i, sa)); + tester.emit(IGen::mov_gpr64_gpr64(RAX, i)); + tester.emit_pop_all_gprs(true); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, sar_gpr64_u8) { + CodeTester tester; + tester.init_code_buffer(256); + std::vector vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN, + INT64_MAX, 117, 32, -348473, 83747382}; + std::vector sas = {0, 1, 23, 53, 64}; + + for (int i = 0; i < 16; i++) { + if (i == RSP) { + continue; + } + for (auto v : vals) { + for (auto sa : sas) { + auto expected = v >> sa; + tester.clear(); + tester.emit_push_all_gprs(true); + tester.emit(IGen::mov_gpr64_u64(i, v)); + tester.emit(IGen::sar_gpr64_u8(i, sa)); + tester.emit(IGen::mov_gpr64_gpr64(RAX, i)); + tester.emit_pop_all_gprs(true); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterIntegerMath, jumps) { + CodeTester tester; + tester.init_code_buffer(256); + + std::vector reads; + + auto x = IGen::jmp_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::je_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jne_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jle_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jge_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jl_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jg_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jbe_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jae_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::jb_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + x = IGen::ja_32(); + reads.push_back(tester.size() + x.offset_of_imm()); + tester.emit(x); + + for (auto off : reads) { + EXPECT_EQ(0, tester.read(off)); + } + + EXPECT_EQ(tester.dump_to_hex_string(true), + "E9000000000F84000000000F85000000000F8E000000000F8D000000000F8C000000000F8F000000000F86" + "000000000F83000000000F82000000000F8700000000"); +} + +TEST(EmitterIntegerMath, null) { + auto instr = IGen::null(); + EXPECT_EQ(0, instr.emit(nullptr)); } \ No newline at end of file diff --git a/test/test_emitter_xmm32.cpp b/test/test_emitter_xmm32.cpp index fd19191e44..97b1651214 100644 --- a/test/test_emitter_xmm32.cpp +++ b/test/test_emitter_xmm32.cpp @@ -440,4 +440,220 @@ TEST(EmitterXmm32, static_store_xmm32) { tester.execute(as_u32(-44.567f), 0, 0, 0); EXPECT_EQ(-44.567f, tester.read(loc_of_float)); } +} + +TEST(EmitterXmm32, ucomiss) { + CodeTester tester; + tester.init_code_buffer(512); + tester.emit(IGen::cmp_flt_flt(XMM13, XMM14)); + EXPECT_EQ("45 0f 2e ee", tester.dump_to_hex_string()); +} + +TEST(EmitterXmm32, mul) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f}; + + for (auto f : vals) { + for (auto g : vals) { + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + if (i == j) { + continue; + } + auto expected = f * g; + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + u64 val = 0; + memcpy(&val, &f, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX)); + memcpy(&val, &g, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX)); + tester.emit(IGen::mulss_xmm_xmm(XMM0 + j, XMM0 + i)); + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j)); + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, div) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector vals = {1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f}; + + for (auto f : vals) { + for (auto g : vals) { + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + if (i == j) { + continue; + } + auto expected = g / f; + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + u64 val = 0; + memcpy(&val, &f, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX)); + memcpy(&val, &g, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX)); + tester.emit(IGen::divss_xmm_xmm(XMM0 + j, XMM0 + i)); + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j)); + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, add) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f}; + for (auto f : vals) { + for (auto g : vals) { + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + if (i == j) { + continue; + } + auto expected = g + f; + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + u64 val = 0; + memcpy(&val, &f, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX)); + memcpy(&val, &g, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX)); + tester.emit(IGen::addss_xmm_xmm(XMM0 + j, XMM0 + i)); + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j)); + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, sub) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f}; + + for (auto f : vals) { + for (auto g : vals) { + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + if (i == j) { + continue; + } + auto expected = g - f; + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + u64 val = 0; + memcpy(&val, &f, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX)); + memcpy(&val, &g, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX)); + tester.emit(IGen::subss_xmm_xmm(XMM0 + j, XMM0 + i)); + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j)); + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } + } +} + +TEST(EmitterXmm32, float_to_int) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, + 7.545f, 0.1f, 0.9f, -0.1f, -0.9f}; + + for (auto g : vals) { + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + if (j == RSP) { + continue; + } + s32 expected = g; + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + u64 val = 0; + memcpy(&val, &g, sizeof(float)); + tester.emit(IGen::mov_gpr64_u64(RAX, val)); + tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX)); + tester.emit(IGen::float_to_int32(j, XMM0 + i)); + tester.emit(IGen::mov_gpr64_gpr64(RAX, j)); + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } +} + +TEST(EmitterXmm32, int_to_float) { + CodeTester tester; + tester.init_code_buffer(512); + + std::vector vals = {0, 1, -1, INT32_MAX, -3457343, 7, INT32_MIN}; + + for (auto g : vals) { + for (int i = 0; i < 16; i++) { + for (int j = 0; j < 16; j++) { + if (j == RSP) { + continue; + } + float expected = g; + tester.clear(); + tester.emit_push_all_xmms(); + tester.emit_push_all_gprs(true); + tester.emit(IGen::mov_gpr64_u64(j, g)); + tester.emit(IGen::int32_to_float(XMM0 + i, j)); + tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + i)); + tester.emit_pop_all_gprs(true); + tester.emit_pop_all_xmms(); + tester.emit_return(); + auto result = tester.execute_ret(0, 0, 0, 0); + EXPECT_EQ(result, expected); + } + } + } } \ No newline at end of file