implement some loads and stores

This commit is contained in:
water
2020-08-29 19:13:29 -04:00
parent 2ac07ae6bd
commit bf7a496bb0
15 changed files with 1510 additions and 211 deletions
+1 -2
View File
@@ -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.
+15
View File
@@ -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
}
}
}
/*!
+2 -2
View File
@@ -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;
+105
View File
@@ -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);
}
}
}
+49 -1
View File
@@ -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
+346
View File
@@ -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
+36 -1
View File
@@ -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
View File
@@ -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
+8
View File
@@ -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.
*/
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
+585
View File
@@ -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++;
}
}
}
}
+55
View File
@@ -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();
}
}
}
}
}
}