mirror of
https://github.com/open-goal/jak-project.git
synced 2025-02-12 01:26:04 +00:00
more progress on emitter integers
This commit is contained in:
parent
bea7167fbb
commit
4723153a4b
@ -30,11 +30,37 @@ class CodeTester {
|
||||
return result_T;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int emit_data(T x) {
|
||||
auto ret = code_buffer_size;
|
||||
assert(int(sizeof(T)) + code_buffer_size <= code_buffer_capacity);
|
||||
memcpy(code_buffer + code_buffer_size, &x, sizeof(T));
|
||||
code_buffer_size += sizeof(T);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void clear();
|
||||
~CodeTester();
|
||||
|
||||
/*!
|
||||
* Should allow emitter tests which run code to do the right thing on windows.
|
||||
* Assumes RAX is return and RSP is stack pointer.
|
||||
*/
|
||||
Register get_c_abi_arg_reg(int i) {
|
||||
// todo - this should be different for windows.
|
||||
#ifdef _WIN32
|
||||
switch (i) {
|
||||
case 0:
|
||||
return RCX;
|
||||
case 1:
|
||||
return RDX;
|
||||
case 2:
|
||||
return R8;
|
||||
case 3:
|
||||
return R9;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
#else
|
||||
switch (i) {
|
||||
case 0:
|
||||
return RDI;
|
||||
@ -47,10 +73,31 @@ class CodeTester {
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string reg_name(Register x) { return m_info.get_info(x).name; }
|
||||
|
||||
int size() const { return code_buffer_size; }
|
||||
|
||||
template <typename T>
|
||||
void write(T x, int at) {
|
||||
assert(at >= 0);
|
||||
assert(int(sizeof(T)) + at <= code_buffer_capacity);
|
||||
memcpy(code_buffer + at, &x, sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read(int at) {
|
||||
assert(at >= 0);
|
||||
assert(int(sizeof(T)) + at <= code_buffer_capacity);
|
||||
T result;
|
||||
memcpy(&result, code_buffer + at, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
const u8* data() const { return code_buffer; }
|
||||
|
||||
private:
|
||||
int code_buffer_size = 0;
|
||||
int code_buffer_capacity = 0;
|
||||
|
@ -1147,9 +1147,56 @@ class IGen {
|
||||
return instr;
|
||||
}
|
||||
|
||||
// todo - lea rip
|
||||
// todo - static load and store floating points? (this is probably big for loading float
|
||||
// constants)
|
||||
static Instruction static_store(Register value, s64 offset, int size) {
|
||||
switch (size) {
|
||||
case 1:
|
||||
return store8_rip_s32(value, offset);
|
||||
case 2:
|
||||
return store16_rip_s32(value, offset);
|
||||
case 4:
|
||||
return store32_rip_s32(value, offset);
|
||||
case 8:
|
||||
return store64_rip_s32(value, offset);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static Instruction static_addr(Register dst, s64 offset) {
|
||||
assert(dst.is_gpr());
|
||||
assert(offset >= INT32_MIN && offset <= INT32_MAX);
|
||||
Instruction instr(0x8d);
|
||||
instr.set_modrm_and_rex_for_rip_plus_s32(dst.hw_id(), offset, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction static_load_xmm32(Register xmm_dest, s64 offset) {
|
||||
assert(xmm_dest.is_xmm());
|
||||
assert(offset >= INT32_MIN && offset <= INT32_MAX);
|
||||
|
||||
Instruction instr(0xf3);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x10);
|
||||
instr.set_modrm_and_rex_for_rip_plus_s32(xmm_dest.hw_id(), offset, false);
|
||||
|
||||
instr.swap_op0_rex();
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction static_store_xmm32(Register xmm_value, s64 offset) {
|
||||
assert(xmm_value.is_xmm());
|
||||
assert(offset >= INT32_MIN && offset <= INT32_MAX);
|
||||
|
||||
Instruction instr(0xf3);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x11);
|
||||
instr.set_modrm_and_rex_for_rip_plus_s32(xmm_value.hw_id(), offset, false);
|
||||
|
||||
instr.swap_op0_rex();
|
||||
return instr;
|
||||
}
|
||||
|
||||
// TODO, special load/stores of 128 bit values.
|
||||
|
||||
// TODO, consider specialized stack loads and stores?
|
||||
|
||||
@ -1186,10 +1233,45 @@ class IGen {
|
||||
return Instruction(0x58 + reg.hw_id());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Call a function stored in a 64-bit gpr
|
||||
*/
|
||||
static Instruction call_r64(uint8_t reg) {
|
||||
Instruction instr(0xff);
|
||||
if (reg >= 8) {
|
||||
instr.set(REX(false, false, false, true));
|
||||
reg -= 8;
|
||||
}
|
||||
assert(reg < 8);
|
||||
ModRM mrm;
|
||||
mrm.rm = reg;
|
||||
mrm.reg_op = 2;
|
||||
mrm.mod = 3;
|
||||
instr.set(mrm);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Call a function stored in a 64-bit gpr
|
||||
*/
|
||||
static Instruction jmp_r64(uint8_t reg) {
|
||||
Instruction instr(0xff);
|
||||
if (reg >= 8) {
|
||||
instr.set(REX(false, false, false, true));
|
||||
reg -= 8;
|
||||
}
|
||||
assert(reg < 8);
|
||||
ModRM mrm;
|
||||
mrm.rm = reg;
|
||||
mrm.reg_op = 4;
|
||||
mrm.mod = 3;
|
||||
instr.set(mrm);
|
||||
return instr;
|
||||
}
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// INTEGER MATH
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// todo - add test
|
||||
static Instruction sub_gpr64_imm8s(Register reg, int64_t imm) {
|
||||
assert(reg.is_gpr());
|
||||
assert(imm >= INT8_MIN && imm <= INT8_MAX);
|
||||
@ -1200,33 +1282,170 @@ class IGen {
|
||||
return instr;
|
||||
}
|
||||
|
||||
// sub imm32, imm64, reg
|
||||
static Instruction sub_gpr64_imm32s(Register reg, int64_t imm) {
|
||||
assert(reg.is_gpr());
|
||||
assert(imm >= INT32_MIN && imm <= INT32_MAX);
|
||||
Instruction instr(0x81);
|
||||
instr.set_modrm_and_rex(5, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(4, imm));
|
||||
return instr;
|
||||
}
|
||||
|
||||
// todo - add test
|
||||
static Instruction add_gpr64_imm8s(Register reg, int8_t v) {
|
||||
static Instruction add_gpr64_imm8s(Register reg, int64_t v) {
|
||||
assert(v >= INT8_MIN && v <= INT8_MAX);
|
||||
Instruction instr(0x83);
|
||||
instr.set_modrm_and_rex(0, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(1, v));
|
||||
return instr;
|
||||
}
|
||||
|
||||
// add imm32, imm64, reg
|
||||
static Instruction add_gpr64_imm32s(Register reg, int64_t v) {
|
||||
assert(v >= INT32_MIN && v <= INT32_MAX);
|
||||
Instruction instr(0x81);
|
||||
instr.set_modrm_and_rex(0, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(4, v));
|
||||
return instr;
|
||||
}
|
||||
|
||||
// mul imm8, imm32, imm64, reg
|
||||
static Instruction add_gpr64_imm(Register reg, int64_t imm) {
|
||||
if (imm >= INT8_MIN && imm <= INT8_MAX) {
|
||||
return add_gpr64_imm8s(reg, imm);
|
||||
} else if (imm >= INT32_MIN && imm <= INT32_MAX) {
|
||||
return add_gpr64_imm32s(reg, imm);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// idiv, cdq, movsx for div
|
||||
static Instruction sub_gpr64_imm(Register reg, int64_t imm) {
|
||||
if (imm >= INT8_MIN && imm <= INT8_MAX) {
|
||||
return sub_gpr64_imm8s(reg, imm);
|
||||
} else if (imm >= INT32_MIN && imm <= INT32_MAX) {
|
||||
return sub_gpr64_imm32s(reg, imm);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// cmp imm8, imm32, imm64, reg
|
||||
static Instruction add_gpr64_gpr64(Register dst, Register src) {
|
||||
Instruction instr(0x01);
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
instr.set_modrm_and_rex(src.hw_id(), dst.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction sub_gpr64_gpr64(Register dst, Register src) {
|
||||
Instruction instr(0x29);
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
instr.set_modrm_and_rex(src.hw_id(), dst.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Multiply gprs (32-bit, signed).
|
||||
* (Note - probably worth doing imul on gpr64's to implement the EE's unsigned multiply)
|
||||
*/
|
||||
static Instruction imul_gpr32_gpr32(Register dst, Register src) {
|
||||
Instruction instr(0xf);
|
||||
instr.set_op2(0xaf);
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Divide (idiv, 32 bit)
|
||||
* todo UNTESTED
|
||||
*/
|
||||
static Instruction idiv_gpr32(Register reg) {
|
||||
Instruction instr(0xf7);
|
||||
assert(reg.is_gpr());
|
||||
instr.set_modrm_and_rex(7, reg.hw_id(), 3, false);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Convert doubleword to quadword for division.
|
||||
* todo UNTESTED
|
||||
*/
|
||||
static Instruction cdq() {
|
||||
Instruction instr(0x99);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move from gpr32 to gpr64, with sign extension.
|
||||
* Needed for multiplication/divsion madness.
|
||||
*/
|
||||
static Instruction movsx_r64_r32(Register dst, Register src) {
|
||||
Instruction instr(0x63);
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Compare gpr64. This sets the flags for the jumps.
|
||||
* todo UNTESTED
|
||||
*/
|
||||
static Instruction cmp_gpr64_gpr64(Register a, Register b) {
|
||||
Instruction instr(0x3b);
|
||||
assert(a.is_gpr());
|
||||
assert(b.is_gpr());
|
||||
instr.set_modrm_and_rex(a.hw_id(), b.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// BIT STUFF
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
// and imm8, imm32, imm64, reg
|
||||
// or imm8, imm32, imm64, reg
|
||||
// xor imm8, imm32, imm64, reg
|
||||
// "fancy register zero xor"
|
||||
// not
|
||||
/*!
|
||||
* Or of two gprs
|
||||
*/
|
||||
static Instruction or_gpr64_gpr64(Register dst, Register src) {
|
||||
Instruction instr(0x0b);
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* And of two gprs
|
||||
*/
|
||||
static Instruction and_gpr64_gpr64(Register dst, Register src) {
|
||||
Instruction instr(0x23);
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Xor of two gprs
|
||||
*/
|
||||
static Instruction xor_gpr64_gpr64(Register dst, Register src) {
|
||||
Instruction instr(0x33);
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Bitwise not a gpr
|
||||
*/
|
||||
static Instruction not_gpr64(Register reg) {
|
||||
Instruction instr(0xf7);
|
||||
assert(reg.is_gpr());
|
||||
instr.set_modrm_and_rex(2, reg.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// SHIFTS
|
||||
|
@ -546,6 +546,46 @@ struct Instruction {
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t length() const {
|
||||
if (is_null)
|
||||
return 0;
|
||||
uint8_t count = 0;
|
||||
if (set_rex) {
|
||||
count++;
|
||||
}
|
||||
|
||||
count++;
|
||||
|
||||
if (op2_set) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (op3_set) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (set_modrm) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (set_sib) {
|
||||
count++;
|
||||
}
|
||||
|
||||
if (set_disp_imm) {
|
||||
for (int i = 0; i < disp.size; i++) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (set_imm) {
|
||||
for (int i = 0; i < imm.size; i++) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
};
|
||||
} // namespace emitter
|
||||
|
||||
|
@ -8,185 +8,6 @@
|
||||
namespace goal {
|
||||
class IGen {
|
||||
public:
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// FUNCTION STUFF
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
// /*!
|
||||
// * Call a function stored in a 64-bit gpr
|
||||
// */
|
||||
// static Instruction call_r64(uint8_t reg) {
|
||||
// Instruction instr(0xff);
|
||||
// if (reg >= 8) {
|
||||
// instr.set(REX(false, false, false, true));
|
||||
// reg -= 8;
|
||||
// }
|
||||
// assert(reg < 8);
|
||||
// ModRM mrm;
|
||||
// mrm.rm = reg;
|
||||
// mrm.reg_op = 2;
|
||||
// mrm.mod = 3;
|
||||
// instr.set(mrm);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Call a function stored in a 64-bit gpr
|
||||
// */
|
||||
// static Instruction jmp_r64(uint8_t reg) {
|
||||
// Instruction instr(0xff);
|
||||
// if (reg >= 8) {
|
||||
// instr.set(REX(false, false, false, true));
|
||||
// reg -= 8;
|
||||
// }
|
||||
// assert(reg < 8);
|
||||
// ModRM mrm;
|
||||
// mrm.rm = reg;
|
||||
// mrm.reg_op = 4;
|
||||
// mrm.mod = 3;
|
||||
// instr.set(mrm);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// // INTEGER MATH
|
||||
// //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
//
|
||||
// /*!
|
||||
// * Add 64-bit registers.
|
||||
// */
|
||||
// static Instruction add_gpr64_gpr64(uint8_t dst, uint8_t src) {
|
||||
// Instruction instr(0x01);
|
||||
// instr.set_modrm_and_rex(src, dst, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Add a signed 32 bit immediate to a 64 bit register
|
||||
// * TODO: determine if we can decrease to imm16?
|
||||
// */
|
||||
// static Instruction add_gpr64_imm32s(uint8_t dst, int32_t offset) {
|
||||
// Instruction instr(0x81);
|
||||
// instr.set_modrm_and_rex(0, dst, 3, true);
|
||||
// instr.set(Imm(4, offset));
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Add a signed 32 bit immediate to a 64 bit register
|
||||
// * TODO: determine if we can decrease to imm16?
|
||||
// */
|
||||
// static Instruction add_gpr64_imm8s(uint8_t dst, int8_t v) {
|
||||
// Instruction instr(0x83);
|
||||
// instr.set_modrm_and_rex(0, dst, 3, true);
|
||||
// instr.set(Imm(1, v));
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Subtract 64-bit registers
|
||||
// */
|
||||
// static Instruction sub_gpr64_gpr64(uint8_t dst, uint8_t src) {
|
||||
// Instruction instr(0x29);
|
||||
// instr.set_modrm_and_rex(src, dst, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Multiply gprs (32-bit, signed).
|
||||
// */
|
||||
// static Instruction imul_gpr32_gpr32(uint8_t dst, uint8_t src) {
|
||||
// Instruction instr(0xf);
|
||||
// instr.set_op2(0xaf);
|
||||
// instr.set_modrm_and_rex(dst, src, 3, false);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Divide (idiv, 32 bit)
|
||||
// */
|
||||
// static Instruction idiv_gpr32(uint8_t reg) {
|
||||
// Instruction instr(0xf7);
|
||||
// instr.set_modrm_and_rex(7, reg, 3, false);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Convert doubleword to quadword for division.
|
||||
// * Blame Intel for this disaster.
|
||||
// */
|
||||
// static Instruction cdq() {
|
||||
// Instruction instr(0x99);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Move from gpr32 to gpr64, with sign extension.
|
||||
// * Needed for division madness.
|
||||
// */
|
||||
// static Instruction movsx_r64_r32(uint8_t dst, uint8_t src) {
|
||||
// Instruction instr(0x63);
|
||||
// instr.set_modrm_and_rex(dst, src, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Compare gpr64. This sets the flags for the jumps.
|
||||
// */
|
||||
// static Instruction cmp_gpr64_gpr64(uint8_t a, uint8_t b) {
|
||||
// Instruction instr(0x3b);
|
||||
// instr.set_modrm_and_rex(a, b, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// // BIT STUFF
|
||||
// //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
//
|
||||
// /*!
|
||||
// * Or of two gprs
|
||||
// */
|
||||
// static Instruction or_gpr64_gpr64(uint8_t dst, uint8_t src) {
|
||||
// Instruction instr(0x0b);
|
||||
// instr.set_modrm_and_rex(dst, src, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * And of two gprs
|
||||
// */
|
||||
// static Instruction and_gpr64_gpr64(uint8_t dst, uint8_t src) {
|
||||
// Instruction instr(0x23);
|
||||
// instr.set_modrm_and_rex(dst, src, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Xor of two gprs
|
||||
// */
|
||||
// static Instruction xor_gpr64_gpr64(uint8_t dst, uint8_t src) {
|
||||
// Instruction instr(0x33);
|
||||
// instr.set_modrm_and_rex(dst, src, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * This is the way "real" compilers zero registers, so we should do it too.
|
||||
// */
|
||||
// static Instruction xor_zero_gpr(uint8_t reg) {
|
||||
// Instruction instr(0x31);
|
||||
// instr.set_modrm_and_rex(reg, reg, 3, false);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Bitwise not a gpr
|
||||
// */
|
||||
// static Instruction not_gpr64(uint8_t reg) {
|
||||
// Instruction instr(0xf7);
|
||||
// instr.set_modrm_and_rex(2, reg, 3, true);
|
||||
// return instr;
|
||||
// }
|
||||
//
|
||||
// /*!
|
||||
// * Shift 64-bit gpr left by CL register
|
||||
|
@ -11,6 +11,7 @@ add_executable(goalc-test
|
||||
test_emitter_slow.cpp
|
||||
test_emitter_loads_and_store.cpp
|
||||
test_emitter_xmm32.cpp
|
||||
test_emitter_integer_math.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(goalc-test goos util listener runtime emitter type_system gtest)
|
393
test/test_emitter_integer_math.cpp
Normal file
393
test/test_emitter_integer_math.cpp
Normal file
@ -0,0 +1,393 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
|
||||
using namespace emitter;
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_imm8s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val + imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::add_gpr64_imm8s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::add_gpr64_imm8s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 c4 0c");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_imm32s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val + imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::add_gpr64_imm32s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::add_gpr64_imm32s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 c4 0c 00 00 00");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_imm8s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val - imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::sub_gpr64_imm8s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::sub_gpr64_imm8s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 ec 0c");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_imm32s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val - imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::sub_gpr64_imm32s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::sub_gpr64_imm32s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 ec 0c 00 00 00");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_gpr64) {
|
||||
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};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 + v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::add_gpr64_gpr64(i, j));
|
||||
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, sub_gpr64_gpr64) {
|
||||
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};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 - v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::sub_gpr64_gpr64(i, j));
|
||||
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, mul_gpr32_gpr32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s32> vals = {
|
||||
0, 1, -2, -20, 123123, INT32_MIN, INT32_MAX, INT32_MIN + 1, INT32_MAX - 1};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
// this is kind of weird behavior, but it's what the PS2 CPU does, I think.
|
||||
// the lower 32-bits of the result are sign extended, even if this sign doesn't match
|
||||
// the sign of the real product. This is true for both signed and unsigned multiply.
|
||||
auto expected = ((s64(v1) * s64(v2)) << 32) >> 32;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (s64)v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, (s64)v2));
|
||||
tester.emit(IGen::imul_gpr32_gpr32(i, j));
|
||||
tester.emit(IGen::movsx_r64_r32(RAX, i)); // weird PS2 sign extend.
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
if (result != expected) {
|
||||
fmt::print("fail {} x {}: {}\n", v1, v2, tester.dump_to_hex_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, or_gpr64_gpr64) {
|
||||
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};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 | v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::or_gpr64_gpr64(i, j));
|
||||
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, and_gpr64_gpr64) {
|
||||
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};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 & v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::and_gpr64_gpr64(i, j));
|
||||
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, xor_gpr64_gpr64) {
|
||||
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};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 ^ v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::xor_gpr64_gpr64(i, j));
|
||||
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, not_gpr64) {
|
||||
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};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
auto expected = ~v1;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::not_gpr64(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);
|
||||
}
|
||||
}
|
||||
}
|
@ -2483,4 +2483,31 @@ TEST(EmitterLoadsAndStores, store8_rip_s32) {
|
||||
"88050C000000880D0C00000088150C000000881D0C0000004088250C00000040882D0C0000004088350C00"
|
||||
"000040883D0C0000004488050C00000044880D0C0000004488150C00000044881D0C0000004488250C0000"
|
||||
"0044882D0C0000004488350C00000044883D0C000000");
|
||||
}
|
||||
|
||||
TEST(EmitterLoadsAndStores, static_addr) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, 12345)); // load test reg with junk
|
||||
int start_of_lea = tester.size();
|
||||
auto lea_instr = IGen::static_addr(i, INT32_MAX);
|
||||
tester.emit(lea_instr);
|
||||
// patch instruction to lea the start of this code + 1.
|
||||
tester.write<s32>(-start_of_lea - lea_instr.length() + 1,
|
||||
start_of_lea + lea_instr.offset_of_disp());
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute();
|
||||
EXPECT_EQ(result, (u64)(tester.data()) + 1);
|
||||
}
|
||||
}
|
@ -389,3 +389,55 @@ TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s32) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, static_load_xmm32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
auto loc_of_load = tester.size();
|
||||
auto load_instr = IGen::static_load_xmm32(XMM0 + i, INT32_MAX);
|
||||
|
||||
tester.emit(load_instr);
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto loc_of_float = tester.emit_data(float(1.2345f));
|
||||
|
||||
// patch offset
|
||||
tester.write<s32>(loc_of_float - loc_of_load - load_instr.length(),
|
||||
loc_of_load + load_instr.offset_of_disp());
|
||||
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, 1.2345f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, static_store_xmm32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
auto loc_of_store = tester.size();
|
||||
auto store_instr = IGen::static_store_xmm32(XMM0 + i, INT32_MAX);
|
||||
|
||||
tester.emit(store_instr);
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto loc_of_float = tester.emit_data(float(1.2345f));
|
||||
|
||||
tester.write<s32>(loc_of_float - loc_of_store - store_instr.length(),
|
||||
loc_of_store + store_instr.offset_of_disp());
|
||||
tester.execute(as_u32(-44.567f), 0, 0, 0);
|
||||
EXPECT_EQ(-44.567f, tester.read<float>(loc_of_float));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user