all basic integer and floating point instructions are implemented

This commit is contained in:
water
2020-09-02 17:34:33 -04:00
parent 4723153a4b
commit ba37e241ce
6 changed files with 724 additions and 450 deletions
+273 -13
View File
@@ -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
-306
View File
@@ -1,306 +0,0 @@
#ifndef JAK1_IGEN_H
#define JAK1_IGEN_H
#include <stdexcept>
#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
-20
View File
@@ -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
-111
View File
@@ -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
+235
View File
@@ -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<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> 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<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, shr_gpr64_cl) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
INT64_MAX, 117, 32, u64(-348473), 83747382};
std::vector<u8> 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<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, sar_gpr64_cl) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> 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<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, shl_gpr64_u8) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> 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<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, shr_gpr64_u8) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
INT64_MAX, 117, 32, u64(-348473), 83747382};
std::vector<u8> 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<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, sar_gpr64_u8) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
INT64_MAX, 117, 32, -348473, 83747382};
std::vector<u8> 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<s64>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterIntegerMath, jumps) {
CodeTester tester;
tester.init_code_buffer(256);
std::vector<int> 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<s32>(off));
}
EXPECT_EQ(tester.dump_to_hex_string(true),
"E9000000000F84000000000F85000000000F8E000000000F8D000000000F8C000000000F8F000000000F86"
"000000000F83000000000F82000000000F8700000000");
}
TEST(EmitterIntegerMath, null) {
auto instr = IGen::null();
EXPECT_EQ(0, instr.emit(nullptr));
}
+216
View File
@@ -440,4 +440,220 @@ TEST(EmitterXmm32, static_store_xmm32) {
tester.execute(as_u32(-44.567f), 0, 0, 0);
EXPECT_EQ(-44.567f, tester.read<float>(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<float> 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<float>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterXmm32, div) {
CodeTester tester;
tester.init_code_buffer(512);
std::vector<float> 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<float>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterXmm32, add) {
CodeTester tester;
tester.init_code_buffer(512);
std::vector<float> 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<float>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterXmm32, sub) {
CodeTester tester;
tester.init_code_buffer(512);
std::vector<float> 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<float>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
}
TEST(EmitterXmm32, float_to_int) {
CodeTester tester;
tester.init_code_buffer(512);
std::vector<float> 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<s32>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}
TEST(EmitterXmm32, int_to_float) {
CodeTester tester;
tester.init_code_buffer(512);
std::vector<s64> 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<float>(0, 0, 0, 0);
EXPECT_EQ(result, expected);
}
}
}
}