mirror of
https://github.com/open-goal/jak-project
synced 2026-07-07 22:22:21 -04:00
implement some loads and stores
This commit is contained in:
+1
-2
@@ -7,7 +7,6 @@ set(CMAKE_CXX_FLAGS "-O0 -ggdb -Wall \
|
||||
|
||||
enable_language(ASM_NASM)
|
||||
set(RUNTIME_SOURCE
|
||||
main.cpp
|
||||
runtime.cpp
|
||||
system/SystemThread.cpp
|
||||
system/IOP_Kernel.cpp
|
||||
@@ -49,7 +48,7 @@ set(RUNTIME_SOURCE
|
||||
overlord/stream.cpp)
|
||||
|
||||
# the runtime should be built without any static/dynamic libraries.
|
||||
add_executable(gk ${RUNTIME_SOURCE})
|
||||
add_executable(gk ${RUNTIME_SOURCE} main.cpp)
|
||||
|
||||
# we also build a runtime library for testing. This version is likely unable to call GOAL code correctly, but
|
||||
# can be used to test other things.
|
||||
|
||||
@@ -199,6 +199,7 @@ char* basename_goal(char* s) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Original code, has memory bug.
|
||||
// back up...
|
||||
for (;;) {
|
||||
if (pt < input) {
|
||||
@@ -211,6 +212,20 @@ char* basename_goal(char* s) {
|
||||
return pt + 1; // and return one past
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// back up...
|
||||
for (;;) {
|
||||
if (pt <= input) {
|
||||
return input;
|
||||
}
|
||||
pt--;
|
||||
char c = *pt;
|
||||
// until we hit a slash.
|
||||
if (c == '\\' || c == '/') { // slashes
|
||||
return pt + 1; // and return one past
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -34,9 +34,9 @@ class Deci2Server {
|
||||
void accept_thread_func();
|
||||
bool kill_accept_thread = false;
|
||||
char* buffer = nullptr;
|
||||
int server_fd;
|
||||
int server_fd = -1;
|
||||
sockaddr_in addr;
|
||||
int new_sock;
|
||||
int new_sock = -1;
|
||||
bool server_initialized = false;
|
||||
bool accept_thread_running = false;
|
||||
bool server_connected = false;
|
||||
|
||||
@@ -1 +1,106 @@
|
||||
#include <sys/mman.h>
|
||||
#include "CodeTester.h"
|
||||
#include "IGen.h"
|
||||
|
||||
namespace emitter {
|
||||
|
||||
CodeTester::CodeTester() : m_info(RegisterInfo::make_register_info()) {
|
||||
|
||||
}
|
||||
|
||||
std::string CodeTester::dump_to_hex_string(bool nospace) {
|
||||
std::string result;
|
||||
char buff[32];
|
||||
for (int i = 0; i < code_buffer_size; i++) {
|
||||
if(nospace) {
|
||||
sprintf(buff, "%02X", code_buffer[i]);
|
||||
} else {
|
||||
sprintf(buff, "%02x ", code_buffer[i]);
|
||||
}
|
||||
|
||||
result += buff;
|
||||
}
|
||||
|
||||
// remove trailing space
|
||||
if (!nospace && !result.empty()) {
|
||||
result.pop_back();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CodeTester::emit(const Instruction& instr) {
|
||||
code_buffer_size += instr.emit(code_buffer + code_buffer_size);
|
||||
assert(code_buffer_size <= code_buffer_capacity);
|
||||
}
|
||||
|
||||
void CodeTester::emit_set_gpr_as_return(Register gpr) {
|
||||
assert(gpr.is_gpr());
|
||||
emit(IGen::mov_gpr64_gpr64(RAX, gpr));
|
||||
}
|
||||
|
||||
void CodeTester::emit_return() {
|
||||
emit(IGen::ret());
|
||||
}
|
||||
|
||||
void CodeTester::emit_pop_all_gprs(bool exclude_rax) {
|
||||
for (int i = 16; i-- > 0;) {
|
||||
if (i != RAX || !exclude_rax) {
|
||||
emit(IGen::pop_gpr64(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeTester::emit_push_all_gprs(bool exclude_rax) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i != RAX || !exclude_rax) {
|
||||
emit(IGen::push_gpr64(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeTester::emit_push_all_xmms() {
|
||||
emit(IGen::sub_gpr64_imm8s(RSP, 8));
|
||||
for(int i = 0; i < 16; i++) {
|
||||
emit(IGen::sub_gpr64_imm8s(RSP, 16));
|
||||
emit(IGen::store128_gpr64_xmm128(RSP, XMM0 + i));
|
||||
}
|
||||
}
|
||||
|
||||
void CodeTester::emit_pop_all_xmms() {
|
||||
for(int i = 0; i < 16; i++) {
|
||||
emit(IGen::load128_xmm128_gpr64(XMM0 + i, RSP));
|
||||
emit(IGen::add_gpr64_imm8s(RSP, 16));
|
||||
}
|
||||
emit(IGen::add_gpr64_imm8s(RSP, 8));
|
||||
}
|
||||
|
||||
void CodeTester::clear() {
|
||||
code_buffer_size = 0;
|
||||
}
|
||||
|
||||
u64 CodeTester::execute() {
|
||||
return ((u64(*)())code_buffer)();
|
||||
}
|
||||
|
||||
u64 CodeTester::execute(u64 in0, u64 in1, u64 in2, u64 in3) {
|
||||
return ((u64(*)(u64, u64, u64, u64))code_buffer)(in0, in1, in2, in3);
|
||||
}
|
||||
|
||||
void CodeTester::init_code_buffer(int capacity) {
|
||||
code_buffer = (u8*)mmap(nullptr, capacity, PROT_EXEC | PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
if (code_buffer == (u8*)(-1)) {
|
||||
printf("[CodeTester] Failed to map memory!\n");
|
||||
assert(false);
|
||||
}
|
||||
|
||||
code_buffer_capacity = capacity;
|
||||
code_buffer_size = 0;
|
||||
}
|
||||
|
||||
CodeTester::~CodeTester() {
|
||||
if (code_buffer_capacity) {
|
||||
munmap(code_buffer, code_buffer_capacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,54 @@
|
||||
#ifndef JAK_CODETESTER_H
|
||||
#define JAK_CODETESTER_H
|
||||
|
||||
class CodeTester {};
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "Register.h"
|
||||
#include "Instruction.h"
|
||||
|
||||
namespace emitter {
|
||||
class CodeTester {
|
||||
public:
|
||||
CodeTester();
|
||||
std::string dump_to_hex_string(bool nospace = false);
|
||||
void init_code_buffer(int capacity);
|
||||
void emit_push_all_gprs(bool exclude_rax = false);
|
||||
void emit_pop_all_gprs(bool exclude_rax = false);
|
||||
void emit_push_all_xmms();
|
||||
void emit_pop_all_xmms();
|
||||
void emit_return();
|
||||
void emit_set_gpr_as_return(Register gpr);
|
||||
void emit(const Instruction& instr);
|
||||
u64 execute();
|
||||
u64 execute(u64 in0, u64 in1, u64 in2, u64 in3);
|
||||
void clear();
|
||||
~CodeTester();
|
||||
|
||||
Register get_c_abi_arg_reg(int i) {
|
||||
// todo - this should be different for windows.
|
||||
switch(i) {
|
||||
case 0:
|
||||
return RDI;
|
||||
case 1:
|
||||
return RSI;
|
||||
case 2:
|
||||
return RDX;
|
||||
case 3:
|
||||
return RCX;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string reg_name(Register x) {
|
||||
return m_info.get_info(x).name;
|
||||
}
|
||||
|
||||
private:
|
||||
int code_buffer_size = 0;
|
||||
int code_buffer_capacity = 0;
|
||||
u8* code_buffer = nullptr;
|
||||
RegisterInfo m_info;
|
||||
};
|
||||
} // namespace goal
|
||||
#endif // JAK_CODETESTER_H
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
#ifndef JAK_IGEN_H
|
||||
#define JAK_IGEN_H
|
||||
|
||||
#include <cassert>
|
||||
#include "Register.h"
|
||||
#include "Instruction.h"
|
||||
|
||||
namespace emitter {
|
||||
class IGen {
|
||||
public:
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// MOVES
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
/*!
|
||||
* Move data from src to dst. Moves all 64-bits of the GPR.
|
||||
*/
|
||||
static Instruction mov_gpr64_gpr64(Register dst, Register src) {
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_gpr());
|
||||
Instruction instr(0x89);
|
||||
instr.set_modrm_and_rex(src.hw_id(), dst.hw_id(), 3, true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move a 64-bit constant into a register.
|
||||
*/
|
||||
static Instruction mov_gpr64_u64(Register dst, uint64_t val) {
|
||||
assert(dst.is_gpr());
|
||||
bool rex_b = false;
|
||||
auto dst_hw_id = dst.hw_id();
|
||||
if (dst_hw_id >= 8) {
|
||||
dst_hw_id -= 8;
|
||||
rex_b = true;
|
||||
}
|
||||
Instruction instr(0xb8 + dst_hw_id);
|
||||
instr.set(REX(true, false, false, rex_b));
|
||||
instr.set(Imm(8, val));
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move a 32-bit constant into a register. Zeros the upper 32 bits.
|
||||
*/
|
||||
static Instruction mov_gpr64_u32(Register dst, uint64_t val) {
|
||||
assert(val <= UINT32_MAX);
|
||||
assert(dst.is_gpr());
|
||||
auto dst_hw_id = dst.hw_id();
|
||||
bool rex_b = false;
|
||||
if (dst_hw_id >= 8) {
|
||||
dst_hw_id -= 8;
|
||||
rex_b = true;
|
||||
}
|
||||
|
||||
Instruction instr(0xb8 + dst_hw_id);
|
||||
if (rex_b) {
|
||||
instr.set(REX(false, false, false, rex_b));
|
||||
}
|
||||
instr.set(Imm(4, val));
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move a signed 32-bit constant into a register. Sign extends for the upper 32 bits.
|
||||
* When possible prefer mov_gpr64_u32. (use this only for negative values...)
|
||||
* This is always bigger than mov_gpr64_u32, but smaller than a mov_gpr_u64.
|
||||
*/
|
||||
static Instruction mov_gpr64_s32(Register dst, int64_t val) {
|
||||
assert(val >= INT32_MIN && val <= INT32_MAX);
|
||||
assert(dst.is_gpr());
|
||||
Instruction instr(0xc7);
|
||||
instr.set_modrm_and_rex(0, dst.hw_id(), 3, true);
|
||||
instr.set(Imm(4, val));
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move 32-bits of xmm to 32 bits of gpr (no sign extension).
|
||||
*/
|
||||
static Instruction movd_gpr32_xmm32(Register dst, Register src) {
|
||||
assert(dst.is_gpr());
|
||||
assert(src.is_xmm());
|
||||
Instruction instr(0x66);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x7e);
|
||||
instr.set_modrm_and_rex(src.hw_id(), dst.hw_id(), 3, false);
|
||||
instr.swap_op0_rex();
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move 32-bits of gpr to 32-bits of xmm (no sign extension)
|
||||
*/
|
||||
static Instruction movd_xmm32_gpr32(Register dst, Register src) {
|
||||
assert(dst.is_xmm());
|
||||
assert(src.is_gpr());
|
||||
Instruction instr(0x66);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x6e);
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false);
|
||||
instr.swap_op0_rex();
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Move 32-bits between xmm's
|
||||
*/
|
||||
static Instruction mov_xmm32_xmm32(Register dst, Register src) {
|
||||
assert(dst.is_xmm());
|
||||
assert(src.is_xmm());
|
||||
Instruction instr(0xf3);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x10);
|
||||
instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, false);
|
||||
return instr;
|
||||
}
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// LOADS n' STORES - reg + reg addr
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
/*!
|
||||
* movsx dst, BYTE PTR [addr1 + addr2]
|
||||
* addr1 and addr2 have to be different registers.
|
||||
* Cannot use rsp.
|
||||
*/
|
||||
static Instruction load8s_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) {
|
||||
assert(dst.is_gpr());
|
||||
assert(addr1.is_gpr());
|
||||
assert(addr2.is_gpr());
|
||||
assert(addr1 != addr2);
|
||||
assert(addr1 != RSP);
|
||||
assert(addr2 != RSP);
|
||||
Instruction instr(0xf);
|
||||
instr.set_op2(0xbe);
|
||||
instr.set_modrm_and_rex_for_reg_plus_reg_addr(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), true,
|
||||
false);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* movzx dst, BYTE PTR [addr1 + addr2]
|
||||
* addr1 and addr2 have to be different registers.
|
||||
* Cannot use rsp.
|
||||
*/
|
||||
static Instruction load8u_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) {
|
||||
assert(dst.is_gpr());
|
||||
assert(addr1.is_gpr());
|
||||
assert(addr2.is_gpr());
|
||||
assert(addr1 != addr2);
|
||||
assert(addr1 != RSP);
|
||||
assert(addr2 != RSP);
|
||||
Instruction instr(0xf);
|
||||
instr.set_op2(0xb6);
|
||||
instr.set_modrm_and_rex_for_reg_plus_reg_addr(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), true,
|
||||
false);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* movsx dst, WORD PTR [addr1 + addr2]
|
||||
* addr1 and addr2 have to be different registers.
|
||||
* Cannot use rsp.
|
||||
*/
|
||||
static Instruction load16s_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) {
|
||||
assert(dst.is_gpr());
|
||||
assert(addr1.is_gpr());
|
||||
assert(addr2.is_gpr());
|
||||
assert(addr1 != addr2);
|
||||
assert(addr1 != RSP);
|
||||
assert(addr2 != RSP);
|
||||
Instruction instr(0xf);
|
||||
instr.set_op2(0xbf);
|
||||
instr.set_modrm_and_rex_for_reg_plus_reg_addr(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), true,
|
||||
false);
|
||||
return instr;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* movzx dst, WORD PTR [addr1 + addr2]
|
||||
* addr1 and addr2 have to be different registers.
|
||||
* Cannot use rsp.
|
||||
*/
|
||||
static Instruction load16u_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) {
|
||||
assert(dst.is_gpr());
|
||||
assert(addr1.is_gpr());
|
||||
assert(addr2.is_gpr());
|
||||
assert(addr1 != addr2);
|
||||
assert(addr1 != RSP);
|
||||
assert(addr2 != RSP);
|
||||
Instruction instr(0xf);
|
||||
instr.set_op2(0xb7);
|
||||
instr.set_modrm_and_rex_for_reg_plus_reg_addr(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), true,
|
||||
false);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* movsxd dst, DWORD PTR [addr1 + addr2]
|
||||
* addr1 and addr2 have to be different registers.
|
||||
* Cannot use rsp.
|
||||
*/
|
||||
static Instruction load32s_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) {
|
||||
assert(dst.is_gpr());
|
||||
assert(addr1.is_gpr());
|
||||
assert(addr2.is_gpr());
|
||||
assert(addr1 != addr2);
|
||||
assert(addr1 != RSP);
|
||||
assert(addr2 != RSP);
|
||||
Instruction instr(0x63);
|
||||
instr.set_modrm_and_rex_for_reg_plus_reg_addr(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* movzxd dst, DWORD PTR [addr1 + addr2]
|
||||
* addr1 and addr2 have to be different registers.
|
||||
* Cannot use rsp.
|
||||
*/
|
||||
static Instruction load32u_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) {
|
||||
assert(dst.is_gpr());
|
||||
assert(addr1.is_gpr());
|
||||
assert(addr2.is_gpr());
|
||||
assert(addr1 != addr2);
|
||||
assert(addr1 != RSP);
|
||||
assert(addr2 != RSP);
|
||||
Instruction instr(0x8b);
|
||||
instr.set_modrm_and_rex_for_reg_plus_reg_addr(dst.hw_id(), addr1.hw_id(), addr2.hw_id());
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* mov dst, QWORD PTR [addr1 + addr2]
|
||||
* addr1 and addr2 have to be different registers.
|
||||
* Cannot use rsp.
|
||||
*/
|
||||
static Instruction load64_gpr64_gpr64_plus_gpr64(Register dst, Register addr1, Register addr2) {
|
||||
assert(dst.is_gpr());
|
||||
assert(addr1.is_gpr());
|
||||
assert(addr2.is_gpr());
|
||||
assert(addr1 != addr2);
|
||||
assert(addr1 != RSP);
|
||||
assert(addr2 != RSP);
|
||||
Instruction instr(0x8b);
|
||||
instr.set_modrm_and_rex_for_reg_plus_reg_addr(dst.hw_id(), addr1.hw_id(), addr2.hw_id(), true);
|
||||
return instr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Store a 128-bit xmm into a register, no offset
|
||||
*/
|
||||
static Instruction store128_gpr64_xmm128(Register gpr_addr, Register xmm_value) {
|
||||
assert(gpr_addr.is_gpr());
|
||||
assert(xmm_value.is_xmm());
|
||||
Instruction instr(0x66);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x7f);
|
||||
instr.set_modrm_and_rex_for_reg_addr(xmm_value.hw_id(), gpr_addr.hw_id(), false);
|
||||
instr.swap_op0_rex();
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction load128_xmm128_gpr64(Register xmm_dest, Register gpr_addr) {
|
||||
assert(gpr_addr.is_gpr());
|
||||
assert(xmm_dest.is_xmm());
|
||||
Instruction instr(0x66);
|
||||
instr.set_op2(0x0f);
|
||||
instr.set_op3(0x6f);
|
||||
instr.set_modrm_and_rex_for_reg_addr(xmm_dest.hw_id(), gpr_addr.hw_id(), false);
|
||||
instr.swap_op0_rex();
|
||||
return instr;
|
||||
}
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// FUNCTION STUFF
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
/*!
|
||||
* Function return. Pops the 64-bit return address (real) off the stack and jumps to it.
|
||||
*/
|
||||
static Instruction ret() { return Instruction(0xc3); }
|
||||
|
||||
/*!
|
||||
* Instruction to push gpr (64-bits) onto the stack
|
||||
*/
|
||||
static Instruction push_gpr64(Register reg) {
|
||||
assert(reg.is_gpr());
|
||||
if (reg.hw_id() >= 8) {
|
||||
auto i = Instruction(0x50 + reg.hw_id() - 8);
|
||||
i.set(REX(false, false, false, true));
|
||||
return i;
|
||||
}
|
||||
return Instruction(0x50 + reg.hw_id());
|
||||
}
|
||||
|
||||
/*!
|
||||
* Instruction to pop 64 bit gpr from the stack
|
||||
*/
|
||||
static Instruction pop_gpr64(Register reg) {
|
||||
if (reg.hw_id() >= 8) {
|
||||
auto i = Instruction(0x58 + reg.hw_id() - 8);
|
||||
i.set(REX(false, false, false, true));
|
||||
return i;
|
||||
}
|
||||
return Instruction(0x58 + reg.hw_id());
|
||||
}
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// INTEGER MATH
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
static Instruction sub_gpr64_imm8s(Register reg, int64_t imm) {
|
||||
assert(reg.is_gpr());
|
||||
assert(imm >= INT8_MIN && imm <= INT8_MAX);
|
||||
// SUB r/m64, imm8 : REX.W + 83 /5 ib
|
||||
Instruction instr(0x83);
|
||||
instr.set_modrm_and_rex(5, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(1, imm));
|
||||
return instr;
|
||||
}
|
||||
|
||||
static Instruction add_gpr64_imm8s(Register reg, int8_t v) {
|
||||
Instruction instr(0x83);
|
||||
instr.set_modrm_and_rex(0, reg.hw_id(), 3, true);
|
||||
instr.set(Imm(1, v));
|
||||
return instr;
|
||||
}
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// BIT STUFF
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// CONTROL FLOW
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// FLOAT MATH
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
// UTILITIES
|
||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
};
|
||||
} // namespace emitter
|
||||
|
||||
#endif // JAK_IGEN_H
|
||||
@@ -1,15 +1,10 @@
|
||||
/*!
|
||||
* @file Instruction.h
|
||||
* x86-64 instruction encoding
|
||||
*/
|
||||
#ifndef JAK1_INSTRUCTION_H
|
||||
#define JAK1_INSTRUCTION_H
|
||||
#ifndef JAK_INSTRUCTION_H
|
||||
#define JAK_INSTRUCTION_H
|
||||
|
||||
#include <cassert>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace goal {
|
||||
|
||||
namespace emitter {
|
||||
/*!
|
||||
* The ModRM byte
|
||||
*/
|
||||
@@ -174,11 +169,76 @@ struct Instruction {
|
||||
}
|
||||
}
|
||||
|
||||
void set_modrm_and_rex_for_reg_plus_reg_addr(uint8_t reg, uint8_t addr1, uint8_t addr2, bool rex_w = false, bool rex_always = false) {
|
||||
bool rex_b = false, rex_r = false, rex_x = false;
|
||||
bool addr1_ext = false;
|
||||
bool addr2_ext = false;
|
||||
|
||||
if (addr1 >= 8) {
|
||||
addr1 -= 8;
|
||||
addr1_ext = true;
|
||||
}
|
||||
|
||||
if (addr2 >= 8) {
|
||||
addr2 -= 8;
|
||||
addr2_ext = true;
|
||||
}
|
||||
|
||||
if (reg >= 8) {
|
||||
reg -= 8;
|
||||
rex_r = true;
|
||||
}
|
||||
|
||||
ModRM modrm;
|
||||
modrm.mod = 0; // no disp
|
||||
modrm.rm = 4; // sib!
|
||||
modrm.reg_op = reg;
|
||||
|
||||
SIB sib;
|
||||
sib.scale = 0;
|
||||
|
||||
if(addr1 == 5 && addr2 == 5) {
|
||||
sib.index = addr1;
|
||||
sib.base = addr2;
|
||||
rex_x = addr1_ext;
|
||||
rex_b = addr2_ext;
|
||||
modrm.mod = 1;
|
||||
set(Imm(1, 0));
|
||||
|
||||
} else {
|
||||
// default addr1 in index
|
||||
bool flipped = (addr1 == 4) || (addr2 == 5);
|
||||
|
||||
if(flipped) {
|
||||
sib.index = addr2;
|
||||
sib.base = addr1;
|
||||
rex_x = addr2_ext;
|
||||
rex_b = addr1_ext;
|
||||
} else {
|
||||
// addr1 in index
|
||||
sib.index = addr1;
|
||||
sib.base = addr2;
|
||||
rex_x = addr1_ext;
|
||||
rex_b = addr2_ext;
|
||||
}
|
||||
assert(sib.base != 5);
|
||||
assert(sib.index != 4);
|
||||
}
|
||||
|
||||
|
||||
if(rex_b || rex_w || rex_r || rex_x || rex_always) {
|
||||
set(REX(rex_w, rex_r, rex_x, rex_b));
|
||||
}
|
||||
|
||||
set(modrm);
|
||||
set(sib);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set modrm and rex as needed for two regs for an addressing mode.
|
||||
* Will set SIB if R12 or RSP indexing is used.
|
||||
*/
|
||||
void set_modrm_and_rex_for_addr(uint8_t reg, uint8_t rm, uint8_t mod, bool rex_w = false) {
|
||||
void set_modrm_and_rex_for_reg_addr(uint8_t reg, uint8_t rm, bool rex_w = false) {
|
||||
bool rex_b = false, rex_r = false;
|
||||
|
||||
if (rm >= 8) {
|
||||
@@ -192,12 +252,10 @@ struct Instruction {
|
||||
}
|
||||
|
||||
ModRM modrm;
|
||||
modrm.mod = mod;
|
||||
modrm.mod = 0;
|
||||
modrm.reg_op = reg;
|
||||
modrm.rm = rm;
|
||||
|
||||
set(modrm);
|
||||
|
||||
if (rm == 4) {
|
||||
SIB sib;
|
||||
sib.scale = 0;
|
||||
@@ -207,6 +265,12 @@ struct Instruction {
|
||||
set(sib);
|
||||
}
|
||||
|
||||
if (rm == 5) {
|
||||
modrm.mod = 1; // 1 byte imm
|
||||
set(Imm(1, 0));
|
||||
}
|
||||
|
||||
set(modrm);
|
||||
if (rex_b || rex_w || rex_r) {
|
||||
set(REX(rex_w, rex_r, false, rex_b));
|
||||
}
|
||||
@@ -343,6 +407,6 @@ struct Instruction {
|
||||
return count;
|
||||
}
|
||||
};
|
||||
} // namespace goal
|
||||
} // namespace emitter
|
||||
|
||||
#endif // JAK1_INSTRUCTION_H
|
||||
#endif // JAK_INSTRUCTION_H
|
||||
@@ -1 +1,36 @@
|
||||
#include "Register.h"
|
||||
#include "Register.h"
|
||||
|
||||
namespace emitter {
|
||||
RegisterInfo RegisterInfo::make_register_info() {
|
||||
RegisterInfo info;
|
||||
|
||||
info.m_info[RAX] = {-1, false, false, "rax"};
|
||||
info.m_info[RCX] = {3, false, false, "rcx"};
|
||||
info.m_info[RDX] = {2, false, false, "rdx"};
|
||||
info.m_info[RBX] = {-1, true, false, "rbx"};
|
||||
info.m_info[RSP] = {-1, false, true, "rsp"};
|
||||
info.m_info[RBP] = {-1, true, false, "rbp"};
|
||||
info.m_info[RSI] = {1, false, false, "rsi"};
|
||||
info.m_info[RDI] = {0, false, false, "rdi"};
|
||||
|
||||
info.m_info[R8] = {4, false, false, "r8"};
|
||||
info.m_info[R9] = {5, false, false, "r9"};
|
||||
info.m_info[R10] = {6, true, false, "r10"};
|
||||
info.m_info[R11] = {7, true, false, "r11"};
|
||||
info.m_info[R12] = {-1, true, false, "r12"};
|
||||
info.m_info[R13] = {-1, false, true, "r13"}; // pp?
|
||||
info.m_info[R14] = {-1, false, true, "r14"}; // st?
|
||||
info.m_info[R15] = {-1, false, true, "r15"}; // offset.
|
||||
|
||||
info.m_arg_regs = std::array<Register, N_ARGS>({RDI, RSI, RDX, RCX, R8, R9, R10, R11});
|
||||
info.m_saved_gprs = std::array<Register, N_SAVED_GPRS>({RBX, RBP, R10, R11, R12});
|
||||
info.m_saved_xmms = std::array<Register, N_SAVED_XMMS>({XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15});
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+62
-30
@@ -20,8 +20,8 @@ enum X86_REG : u8 {
|
||||
RDX, // arg 2
|
||||
RBX, // X saved
|
||||
|
||||
RSP, // stack pointer
|
||||
RBP, // X base pointer (like fp)
|
||||
RSP, // stack pointer !!!!
|
||||
RBP, // saved !!!!
|
||||
RSI, // arg 1
|
||||
RDI, // arg 0
|
||||
|
||||
@@ -29,8 +29,8 @@ enum X86_REG : u8 {
|
||||
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)
|
||||
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,
|
||||
@@ -51,8 +51,7 @@ enum X86_REG : u8 {
|
||||
XMM15
|
||||
};
|
||||
|
||||
constexpr int N_REGS = 32;
|
||||
static_assert(N_REGS - 1 == XMM15, "bad register count");
|
||||
|
||||
|
||||
class Register {
|
||||
public:
|
||||
@@ -61,11 +60,11 @@ class Register {
|
||||
// intentionally not explicit so we can use X86_REGs in place of Registers
|
||||
Register(int id) : m_id(id) {}
|
||||
|
||||
bool is_xmm() { return m_id >= XMM0 && m_id <= XMM15; }
|
||||
bool is_xmm() const { return m_id >= XMM0 && m_id <= XMM15; }
|
||||
|
||||
bool is_gpr() { return m_id >= RAX && m_id <= R15; }
|
||||
bool is_gpr() const { return m_id >= RAX && m_id <= R15; }
|
||||
|
||||
int hw_id() {
|
||||
int hw_id() const {
|
||||
if (is_xmm()) {
|
||||
return m_id - XMM0;
|
||||
} else if (is_gpr()) {
|
||||
@@ -76,47 +75,80 @@ class Register {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
int id() const {
|
||||
return m_id;
|
||||
}
|
||||
|
||||
struct hash {
|
||||
auto operator()(const Register& x) const { return std::hash<u8>()(x.m_id); }
|
||||
};
|
||||
|
||||
bool operator==(const Register& x) const {
|
||||
return m_id == x.m_id;
|
||||
}
|
||||
bool operator==(const Register& x) const { return m_id == x.m_id; }
|
||||
|
||||
bool operator!=(const Register& x) const {
|
||||
return m_id != x.m_id;
|
||||
}
|
||||
bool operator!=(const Register& x) const { return m_id != x.m_id; }
|
||||
|
||||
private:
|
||||
u8 m_id = 0xff;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class RegisterInfo {
|
||||
public:
|
||||
static constexpr int N_ARGS = 8;
|
||||
static constexpr int N_REGS = 32;
|
||||
static constexpr int N_SAVED_GPRS = 5;
|
||||
static constexpr int N_SAVED_XMMS = 8;
|
||||
|
||||
static_assert(N_REGS - 1 == XMM15, "bad register count");
|
||||
|
||||
static RegisterInfo make_register_info();
|
||||
|
||||
struct Info {
|
||||
int argument_id = -1; // -1 if not argument
|
||||
bool saved = false; // does the callee save it?
|
||||
bool special = false; // is it a special GOAL register?
|
||||
int argument_id = -1; // -1 if not argument
|
||||
bool saved = false; // does the callee save it?
|
||||
bool special = false; // is it a special GOAL register?
|
||||
std::string name;
|
||||
};
|
||||
|
||||
const Info& get_info(Register r);
|
||||
const Info& get_info(Register r) const {
|
||||
return m_info.at(r.id());
|
||||
}
|
||||
|
||||
int get_arg_reg_count();
|
||||
Register get_arg_reg(int id);
|
||||
Register get_arg_reg(int id) const {
|
||||
return m_arg_regs.at(id);
|
||||
}
|
||||
|
||||
int get_saved_reg_count();
|
||||
Register get_saved_reg(int id);
|
||||
Register get_saved_gpr(int id) const {
|
||||
return m_saved_gprs.at(id);
|
||||
}
|
||||
|
||||
Register get_process_reg();
|
||||
Register get_st_reg();
|
||||
Register get_offset_reg();
|
||||
Register get_ret_reg();
|
||||
Register get_saved_xmm(int id) const {
|
||||
return m_saved_xmms.at(id);
|
||||
}
|
||||
|
||||
Register get_process_reg() const {
|
||||
return R13;
|
||||
}
|
||||
|
||||
Register get_st_reg() const {
|
||||
return R14;
|
||||
}
|
||||
|
||||
Register get_offset_reg() const {
|
||||
return R15;
|
||||
}
|
||||
|
||||
Register get_ret_reg() const {
|
||||
return RAX;
|
||||
}
|
||||
|
||||
private:
|
||||
RegisterInfo() = default;
|
||||
std::array<Info, N_REGS> m_info;
|
||||
std::array<Register, N_ARGS> m_arg_regs;
|
||||
std::array<Register, N_SAVED_GPRS> m_saved_gprs;
|
||||
std::array<Register, N_SAVED_XMMS> m_saved_xmms;
|
||||
};
|
||||
|
||||
|
||||
} // namespace emitter
|
||||
|
||||
#endif // JAK_REGISTER_H
|
||||
|
||||
@@ -85,6 +85,14 @@ Interpreter::Interpreter() {
|
||||
load_goos_library();
|
||||
}
|
||||
|
||||
|
||||
Interpreter::~Interpreter() {
|
||||
// There are some circular references that prevent shared_ptrs from cleaning up if we
|
||||
// don't do this.
|
||||
global_environment.as_env()->vars.clear();
|
||||
goal_env.as_env()->vars.clear();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Disable printfs on errors, to make test output look less messy.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace goos {
|
||||
class Interpreter {
|
||||
public:
|
||||
Interpreter();
|
||||
~Interpreter();
|
||||
void execute_repl();
|
||||
void throw_eval_error(const Object& o, const std::string& err);
|
||||
Object eval_with_rewind(const Object& obj, const std::shared_ptr<EnvironmentObject>& env);
|
||||
|
||||
+5
-2
@@ -5,8 +5,11 @@ add_executable(goalc-test
|
||||
test_goos.cpp
|
||||
test_listener_deci2.cpp
|
||||
test_kernel.cpp
|
||||
test_CodeTester.cpp
|
||||
all_jak1_symbols.cpp
|
||||
test_type_system.cpp)
|
||||
test_type_system.cpp
|
||||
test_CodeTester.cpp
|
||||
test_emitter_slow.cpp
|
||||
test_emitter_fast.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(goalc-test goos util listener runtime emitter type_system gtest)
|
||||
+162
-159
@@ -2,166 +2,169 @@
|
||||
* @file test_CodeTester.cpp
|
||||
* Tests for the CodeTester, a tool for testing the emitter by emitting code and running it
|
||||
* from within the test application.
|
||||
*
|
||||
* These tests should just make sure the basic functionality of CodeTester works, and that it
|
||||
* can generate prologues/epilogues, and execute them without crashing.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
//#include "goalc/emitter/IGen.h"
|
||||
//
|
||||
//using namespace goal;
|
||||
//
|
||||
//TEST(CodeTester, prologue) {
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
// tester.emit_push_all_gprs();
|
||||
// // check we generate the right code for pushing all gpr's
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
// "50 51 52 53 54 55 56 57 41 50 41 51 41 52 41 53 41 54 41 55 41 56 41 57");
|
||||
//}
|
||||
//
|
||||
//TEST(CodeTester, epilogue) {
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
// tester.emit_pop_all_gprs();
|
||||
// // check we generate the right code for popping all gpr's
|
||||
// EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
// "41 5f 41 5e 41 5d 41 5c 41 5b 41 5a 41 59 41 58 5f 5e 5d 5c 5b 5a 59 58");
|
||||
//}
|
||||
//
|
||||
//TEST(CodeTester, execute_return) {
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
// // test creating a function which simply returns
|
||||
// tester.emit_return();
|
||||
// // and execute it!
|
||||
// tester.execute();
|
||||
//}
|
||||
//
|
||||
//TEST(CodeTester, execute_push_pop_gprs) {
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
// // test we can push/pop gprs without crashing.
|
||||
// tester.emit_push_all_gprs();
|
||||
// tester.emit_pop_all_gprs();
|
||||
// tester.emit_return();
|
||||
// tester.execute();
|
||||
//}
|
||||
//
|
||||
//TEST(CodeTester, load_constant_64_and_move_gpr_gpr_64) {
|
||||
// std::vector<u64> u64_constants = {0, UINT64_MAX, INT64_MAX, 7, 12};
|
||||
//
|
||||
// // test we can load a 64-bit constant into all gprs, move it to any other gpr, and return it.
|
||||
// // rsp is skipping because that's the stack pointer and would prevent us from popping gprs after
|
||||
//
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
//
|
||||
// for (auto constant : u64_constants) {
|
||||
// for (int r1 = 0; r1 < 16; r1++) {
|
||||
// if (r1 == RSP) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// for (int r2 = 0; r2 < 16; r2++) {
|
||||
// if (r2 == RSP) {
|
||||
// continue;
|
||||
// }
|
||||
// tester.clear();
|
||||
// tester.emit_push_all_gprs(true);
|
||||
// tester.emit(IGen::mov_gpr64_u64(r1, constant));
|
||||
// tester.emit(IGen::mov_gpr64_gpr64(r2, r1));
|
||||
// tester.emit(IGen::mov_gpr64_gpr64(RAX, r2));
|
||||
// tester.emit_pop_all_gprs(true);
|
||||
// tester.emit_return();
|
||||
// EXPECT_EQ(tester.execute(), constant);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//TEST(CodeTester, load_constant_32_unsigned) {
|
||||
// std::vector<u64> u64_constants = {0, UINT32_MAX, INT32_MAX, 7, 12};
|
||||
//
|
||||
// // test loading 32-bit constants, with all upper 32-bits zero.
|
||||
// // this uses a different opcode than 64-bit loads.
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
//
|
||||
// for (auto constant : u64_constants) {
|
||||
// for (int r1 = 0; r1 < 16; r1++) {
|
||||
// if (r1 == RSP) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit_push_all_gprs(true);
|
||||
// tester.emit(IGen::mov_gpr64_u32(r1, constant));
|
||||
// tester.emit(IGen::mov_gpr64_gpr64(RAX, r1));
|
||||
// tester.emit_pop_all_gprs(true);
|
||||
// tester.emit_return();
|
||||
// EXPECT_EQ(tester.execute(), constant);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//TEST(CodeTester, load_constant_32_signed) {
|
||||
// std::vector<s32> s32_constants = {0, 1, INT32_MAX, INT32_MIN, 12, -1};
|
||||
//
|
||||
// // test loading signed 32-bit constants. for values < 0 this will sign extend.
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
//
|
||||
// for (auto constant : s32_constants) {
|
||||
// for (int r1 = 0; r1 < 16; r1++) {
|
||||
// if (r1 == RSP) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// tester.clear();
|
||||
// tester.emit_push_all_gprs(true);
|
||||
// tester.emit(IGen::mov_gpr64_s32(r1, constant));
|
||||
// tester.emit(IGen::mov_gpr64_gpr64(RAX, r1));
|
||||
// tester.emit_pop_all_gprs(true);
|
||||
// tester.emit_return();
|
||||
// EXPECT_EQ(tester.execute(), constant);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//TEST(CodeTester, xmm_move) {
|
||||
// std::vector<u32> u32_constants = {0, INT32_MAX, UINT32_MAX, 17};
|
||||
//
|
||||
// // test moving between xmms (32-bit) and gprs.
|
||||
// CodeTester tester;
|
||||
// tester.init_code_buffer(256);
|
||||
//
|
||||
// for (auto constant : u32_constants) {
|
||||
// for (int r1 = 0; r1 < 16; r1++) {
|
||||
// if (r1 == RSP) {
|
||||
// continue;
|
||||
// }
|
||||
// for (int r2 = 0; r2 < 16; r2++) {
|
||||
// if (r2 == RSP) {
|
||||
// continue;
|
||||
// }
|
||||
// for (int r3 = 0; r3 < 16; r3++) {
|
||||
// for (int r4 = 0; r4 < 16; r4++) {
|
||||
// tester.clear();
|
||||
// tester.emit_push_all_gprs(true);
|
||||
// // move constant to gpr
|
||||
// tester.emit(IGen::mov_gpr64_u32(r1, constant));
|
||||
// // move gpr to xmm
|
||||
// tester.emit(IGen::movd_xmm32_gpr32(get_nth_xmm(r3), r1));
|
||||
// // move xmm to xmm
|
||||
// tester.emit(IGen::mov_xmm32_xmm32(get_nth_xmm(r4), get_nth_xmm(r3)));
|
||||
// // move xmm to gpr
|
||||
// tester.emit(IGen::movd_gpr32_xmm32(r2, get_nth_xmm(r4)));
|
||||
// // return!
|
||||
// tester.emit(IGen::mov_gpr64_gpr64(RAX, r2));
|
||||
// tester.emit_return();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
#include "goalc/emitter/IGen.h"
|
||||
|
||||
using namespace emitter;
|
||||
|
||||
TEST(CodeTester, prologue) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
tester.emit_push_all_gprs();
|
||||
// check we generate the right code for pushing all gpr's
|
||||
EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
"50 51 52 53 54 55 56 57 41 50 41 51 41 52 41 53 41 54 41 55 41 56 41 57");
|
||||
}
|
||||
|
||||
TEST(CodeTester, epilogue) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
tester.emit_pop_all_gprs();
|
||||
// check we generate the right code for popping all gpr's
|
||||
EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
"41 5f 41 5e 41 5d 41 5c 41 5b 41 5a 41 59 41 58 5f 5e 5d 5c 5b 5a 59 58");
|
||||
}
|
||||
|
||||
TEST(CodeTester, execute_return) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
// test creating a function which simply returns
|
||||
tester.emit_return();
|
||||
// and execute it!
|
||||
tester.execute();
|
||||
}
|
||||
|
||||
TEST(CodeTester, execute_push_pop_gprs) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
// test we can push/pop gprs without crashing.
|
||||
tester.emit_push_all_gprs();
|
||||
tester.emit_pop_all_gprs();
|
||||
tester.emit_return();
|
||||
tester.execute();
|
||||
}
|
||||
|
||||
TEST(CodeTester, xmm_store_128) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
// movdqa [rbx], xmm3
|
||||
// movdqa [r14], xmm3
|
||||
// movdqa [rbx], xmm14
|
||||
// movdqa [r14], xmm13
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM3));
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R14, XMM3));
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBX, XMM14));
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R14, XMM13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
"66 0f 7f 1b 66 41 0f 7f 1e 66 44 0f 7f 33 66 45 0f 7f 2e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RSP, XMM1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 0c 24"); // requires SIB byte.
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R12, XMM13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 2c 24"); // requires SIB byte and REX byte
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 7f 4d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(RBP, XMM11));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 7f 5d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R13, XMM2));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 7f 55 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::store128_gpr64_xmm128(R13, XMM12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 7f 65 00");
|
||||
}
|
||||
|
||||
TEST(CodeTester, sub_gpr64_imm8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.emit(IGen::sub_gpr64_imm8s(i, -1));
|
||||
}
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true),
|
||||
"4883E8FF4883E9FF4883EAFF4883EBFF4883ECFF4883EDFF4883EEFF4883EFFF4983E8FF4983E9FF4983EA"
|
||||
"FF4983EBFF4983ECFF4983EDFF4983EEFF4983EFFF");
|
||||
}
|
||||
|
||||
TEST(CodeTester, add_gpr64_imm8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.emit(IGen::add_gpr64_imm8s(i, -1));
|
||||
}
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true),
|
||||
"4883C0FF4883C1FF4883C2FF4883C3FF4883C4FF4883C5FF4883C6FF4883C7FF4983C0FF4983C1FF4983C2"
|
||||
"FF4983C3FF4983C4FF4983C5FF4983C6FF4983C7FF");
|
||||
}
|
||||
|
||||
TEST(CodeTester, xmm_load_128) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM3, RBX));
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM3, R14));
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM14, RBX));
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM13, R14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(),
|
||||
"66 0f 6f 1b 66 41 0f 6f 1e 66 44 0f 6f 33 66 45 0f 6f 2e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM1, RSP));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 0c 24"); // requires SIB byte.
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM13, R12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 2c 24"); // requires SIB byte and REX byte
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM1, RBP));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 0f 6f 4d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM11, RBP));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 44 0f 6f 5d 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM2, R13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 41 0f 6f 55 00");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load128_xmm128_gpr64(XMM12, R13));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "66 45 0f 6f 65 00");
|
||||
}
|
||||
|
||||
TEST(CodeTester, push_pop_xmms) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
tester.execute();
|
||||
}
|
||||
|
||||
TEST(CodeTester, push_pop_all_the_things) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs();
|
||||
|
||||
// ...
|
||||
tester.emit_pop_all_gprs();
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
tester.execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,585 @@
|
||||
/*!
|
||||
* @file test_emitter_slow.cpp
|
||||
* Tests for the emitter which are fast (checking 100's of functions)
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
//
|
||||
using namespace emitter;
|
||||
|
||||
TEST(Emitter, load_constant_64_and_move_gpr_gpr_64) {
|
||||
std::vector<u64> u64_constants = {0, UINT64_MAX, INT64_MAX, 7, 12};
|
||||
|
||||
// test we can load a 64-bit constant into all gprs, move it to any other gpr, and return it.
|
||||
// rsp is skipping because that's the stack pointer and would prevent us from popping gprs after
|
||||
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
for (auto constant : u64_constants) {
|
||||
for (int r1 = 0; r1 < 16; r1++) {
|
||||
if (r1 == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int r2 = 0; r2 < 16; r2++) {
|
||||
if (r2 == RSP) {
|
||||
continue;
|
||||
}
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(r1, constant));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(r2, r1));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, r2));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
EXPECT_EQ(tester.execute(), constant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load_constant_32_unsigned) {
|
||||
std::vector<u64> u64_constants = {0, UINT32_MAX, INT32_MAX, 7, 12};
|
||||
|
||||
// test loading 32-bit constants, with all upper 32-bits zero.
|
||||
// this uses a different opcode than 64-bit loads.
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
for (auto constant : u64_constants) {
|
||||
for (int r1 = 0; r1 < 16; r1++) {
|
||||
if (r1 == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(r1, UINT64_MAX));
|
||||
tester.emit(IGen::mov_gpr64_u32(r1, constant));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, r1));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
EXPECT_EQ(tester.execute(), constant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load_constant_32_signed) {
|
||||
std::vector<s32> s32_constants = {0, 1, INT32_MAX, INT32_MIN, 12, -1};
|
||||
|
||||
// test loading signed 32-bit constants. for values < 0 this will sign extend.
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
for (auto constant : s32_constants) {
|
||||
for (int r1 = 0; r1 < 16; r1++) {
|
||||
if (r1 == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_s32(r1, constant));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, r1));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
EXPECT_EQ(tester.execute(), constant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load8s_gpr64_goal_ptr_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(RAX, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 0f be 04 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(R12, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4c 0f be 24 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(R12, R15, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4e 0f be 24 3e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(R12, R15, R14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4f 0f be 24 3e");
|
||||
|
||||
int iter = 0;
|
||||
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 (int k = 0; k < 16; k++) {
|
||||
if (k == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// fill k with junk
|
||||
if(k != i && k != j) {
|
||||
tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX));
|
||||
}
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(k, i, j));
|
||||
|
||||
// move k to return register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 3, 0, 0)), -2);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 2, 0, 0)), -3);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 4, 0, 0)), -1);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 5, 0, 0)), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load8u_gpr64_goal_ptr_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64(RAX, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 0f b6 04 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64(R12, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4c 0f b6 24 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64(R12, R15, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4e 0f b6 24 3e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load8u_gpr64_gpr64_plus_gpr64(R12, R15, R14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4f 0f b6 24 3e");
|
||||
|
||||
int iter = 0;
|
||||
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 (int k = 0; k < 16; k++) {
|
||||
if (k == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// fill k with junk
|
||||
if(k != i && k != j) {
|
||||
tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX));
|
||||
}
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load8s_gpr64_gpr64_plus_gpr64(k, i, j));
|
||||
|
||||
// move k to return register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
u8 memory[8] = {0, 0, 0xfd, 0xfe, 0xff, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 3, 0, 0)), -2);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 2, 0, 0)), -3);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 4, 0, 0)), -1);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 5, 0, 0)), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load16s_gpr64_goal_ptr_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64(RAX, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 0f bf 04 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64(R12, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4c 0f bf 24 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64(R12, R15, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4e 0f bf 24 3e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64(R12, R15, R14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4f 0f bf 24 3e");
|
||||
|
||||
int iter = 0;
|
||||
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 (int k = 0; k < 16; k++) {
|
||||
if (k == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// fill k with junk
|
||||
if(k != i && k != j) {
|
||||
tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX));
|
||||
}
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load16s_gpr64_gpr64_plus_gpr64(k, i, j));
|
||||
|
||||
// move k to return register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
s16 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 6, 0, 0)), -2);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 4, 0, 0)), -3);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 8, 0, 0)), -1);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 10, 0, 0)), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load16u_gpr64_goal_ptr_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64(RAX, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 0f b7 04 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64(R12, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4c 0f b7 24 1e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64(R12, R15, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4e 0f b7 24 3e");
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64(R12, R15, R14));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "4f 0f b7 24 3e");
|
||||
|
||||
int iter = 0;
|
||||
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 (int k = 0; k < 16; k++) {
|
||||
if (k == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// fill k with junk
|
||||
if(k != i && k != j) {
|
||||
tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX));
|
||||
}
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load16u_gpr64_gpr64_plus_gpr64(k, i, j));
|
||||
|
||||
// move k to return register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
s16 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 6, 0, 0)), 0xfffe);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 4, 0, 0)), 0xfffd);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 8, 0, 0)), 0xffff);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 10, 0, 0)), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load32s_gpr64_goal_ptr_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64(RAX, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 63 04 1e");
|
||||
|
||||
|
||||
int iter = 0;
|
||||
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 (int k = 0; k < 16; k++) {
|
||||
if (k == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// fill k with junk
|
||||
if(k != i && k != j) {
|
||||
tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX));
|
||||
}
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32s_gpr64_gpr64_plus_gpr64(k, i, j));
|
||||
|
||||
// move k to return register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
s32 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 12, 0, 0)), -2);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 8, 0, 0)), -3);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 16, 0, 0)), -1);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 20, 0, 0)), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load32u_gpr64_goal_ptr_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64(RAX, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "8b 04 1e");
|
||||
|
||||
|
||||
int iter = 0;
|
||||
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 (int k = 0; k < 16; k++) {
|
||||
if (k == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// fill k with junk
|
||||
if(k != i && k != j) {
|
||||
tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX));
|
||||
}
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32u_gpr64_gpr64_plus_gpr64(k, i, j));
|
||||
|
||||
// move k to return register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
s32 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 12, 0, 0)), 0xfffffffe);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 8, 0, 0)), 0xfffffffd);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 16, 0, 0)), 0xffffffff);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 20, 0, 0)), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Emitter, load64_gpr64_goal_ptr_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64(RAX, RBX, RSI));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 8b 04 1e");
|
||||
|
||||
|
||||
int iter = 0;
|
||||
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 (int k = 0; k < 16; k++) {
|
||||
if (k == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1)));
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
// pop args into appropriate register
|
||||
tester.emit(IGen::pop_gpr64(i)); // i will have offset 0
|
||||
tester.emit(IGen::pop_gpr64(j)); // j will have offset 1
|
||||
|
||||
// fill k with junk
|
||||
if(k != i && k != j) {
|
||||
tester.emit(IGen::mov_gpr64_u64(k, (iter&1)?0:UINT64_MAX));
|
||||
}
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load64_gpr64_gpr64_plus_gpr64(k, i, j));
|
||||
|
||||
// move k to return register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
s64 memory[8] = {0, 0, -3, -2, -1, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 24, 0, 0)), -2);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 16, 0, 0)), -3);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 32, 0, 0)), -1);
|
||||
EXPECT_EQ(s64(tester.execute((u64)memory, 40, 0, 0)), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* @file test_emitter_slow.cpp
|
||||
* Tests for the emitter which take over 1 second. (Checking 10,000's of functions).
|
||||
*
|
||||
* It may make sense to exclude these tests when developing to save time.
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
//
|
||||
using namespace emitter;
|
||||
|
||||
|
||||
|
||||
TEST(EmitterSlow, xmm32_move) {
|
||||
std::vector<u32> u32_constants = {0, INT32_MAX, UINT32_MAX, 17};
|
||||
|
||||
// test moving between xmms (32-bit) and gprs.
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
for (auto constant : u32_constants) {
|
||||
for (int r1 = 0; r1 < 16; r1++) {
|
||||
if (r1 == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int r2 = 0; r2 < 16; r2++) {
|
||||
if (r2 == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int r3 = 0; r3 < 16; r3++) {
|
||||
for (int r4 = 0; r4 < 16; r4++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// move constant to gpr
|
||||
tester.emit(IGen::mov_gpr64_u32(r1, constant));
|
||||
// move gpr to xmm
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + r3, r1));
|
||||
// move xmm to xmm
|
||||
tester.emit(IGen::mov_xmm32_xmm32(XMM0 + r4, XMM0 + r3));
|
||||
// move xmm to gpr
|
||||
tester.emit(IGen::movd_gpr32_xmm32(r2, XMM0 + r4));
|
||||
// return!
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, r2));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user