mirror of
https://github.com/open-goal/jak-project
synced 2026-09-08 20:02:39 -04:00
Merge remote-tracking branch 'water111/master' into windows-nodeci
This commit is contained in:
+9
-5
@@ -6,11 +6,15 @@ add_executable(goalc-test
|
||||
test_reader.cpp
|
||||
test_goos.cpp
|
||||
test_listener_deci2.cpp
|
||||
all_jak1_symbols.cpp
|
||||
#test_kernel.cpp
|
||||
#test_CodeTester.cpp
|
||||
#test_type_system.cpp
|
||||
)
|
||||
test_kernel.cpp
|
||||
all_jak1_symbols.cpp
|
||||
test_type_system.cpp
|
||||
test_CodeTester.cpp
|
||||
test_emitter_slow.cpp
|
||||
test_emitter_loads_and_store.cpp
|
||||
test_emitter_xmm32.cpp
|
||||
test_emitter_integer_math.cpp
|
||||
)
|
||||
|
||||
IF (WIN32)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
|
||||
+102
-100
@@ -2,13 +2,16 @@
|
||||
* @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;
|
||||
using namespace emitter;
|
||||
|
||||
TEST(CodeTester, prologue) {
|
||||
CodeTester tester;
|
||||
@@ -47,121 +50,120 @@ TEST(CodeTester, execute_push_pop_gprs) {
|
||||
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
|
||||
|
||||
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");
|
||||
|
||||
for (auto constant : u64_constants) {
|
||||
for (int r1 = 0; r1 < 16; r1++) {
|
||||
if (r1 == RSP) {
|
||||
continue;
|
||||
}
|
||||
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.
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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, 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.
|
||||
TEST(CodeTester, sub_gpr64_imm8) {
|
||||
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);
|
||||
}
|
||||
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, 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.
|
||||
TEST(CodeTester, add_gpr64_imm8) {
|
||||
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);
|
||||
}
|
||||
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_move) {
|
||||
std::vector<u32> u32_constants = {0, INT32_MAX, UINT32_MAX, 17};
|
||||
|
||||
// test moving between xmms (32-bit) and gprs.
|
||||
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");
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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,628 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
|
||||
using namespace emitter;
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_imm8s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val + imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::add_gpr64_imm8s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::add_gpr64_imm8s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 c4 0c");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_imm32s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val + imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::add_gpr64_imm32s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::add_gpr64_imm32s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 c4 0c 00 00 00");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_imm8s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val - imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::sub_gpr64_imm8s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::sub_gpr64_imm8s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 83 ec 0c");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_imm32s) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX};
|
||||
std::vector<s64> imms = {0, 1, -1, INT8_MIN, INT8_MAX, INT32_MIN, INT32_MAX};
|
||||
|
||||
// test the ones that aren't rsp
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto val : vals) {
|
||||
for (auto imm : imms) {
|
||||
auto expected = val - imm;
|
||||
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
// move initial value to register
|
||||
tester.emit(IGen::mov_gpr64_gpr64(i, tester.get_c_abi_arg_reg(0)));
|
||||
// do the add
|
||||
tester.emit(IGen::sub_gpr64_imm32s(i, imm));
|
||||
// move for return
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
|
||||
auto result = tester.execute_ret<s64>(val, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tester.clear();
|
||||
tester.emit(IGen::sub_gpr64_imm32s(RSP, 12));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "48 81 ec 0c 00 00 00");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, add_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 + v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::add_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sub_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 - v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::sub_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, mul_gpr32_gpr32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s32> vals = {
|
||||
0, 1, -2, -20, 123123, INT32_MIN, INT32_MAX, INT32_MIN + 1, INT32_MAX - 1};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
// this is kind of weird behavior, but it's what the PS2 CPU does, I think.
|
||||
// the lower 32-bits of the result are sign extended, even if this sign doesn't match
|
||||
// the sign of the real product. This is true for both signed and unsigned multiply.
|
||||
auto expected = ((s64(v1) * s64(v2)) << 32) >> 32;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (s64)v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, (s64)v2));
|
||||
tester.emit(IGen::imul_gpr32_gpr32(i, j));
|
||||
tester.emit(IGen::movsx_r64_r32(RAX, i)); // weird PS2 sign extend.
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
if (result != expected) {
|
||||
fmt::print("fail {} x {}: {}\n", v1, v2, tester.dump_to_hex_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, or_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 | v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::or_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, and_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 & v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::and_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, xor_gpr64_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP || j == i) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
for (auto v2 : vals) {
|
||||
auto expected = v1 ^ v2;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::mov_gpr64_u64(j, v2));
|
||||
tester.emit(IGen::xor_gpr64_gpr64(i, j));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, not_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v1 : vals) {
|
||||
auto expected = ~v1;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v1));
|
||||
tester.emit(IGen::not_gpr64(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shl_gpr64_cl) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP || i == RCX) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v << sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
|
||||
tester.emit(IGen::shl_gpr64_cl(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shr_gpr64_cl) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
|
||||
INT64_MAX, 117, 32, u64(-348473), 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP || i == RCX) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
|
||||
tester.emit(IGen::shr_gpr64_cl(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sar_gpr64_cl) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP || i == RCX) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::mov_gpr64_u64(RCX, sa));
|
||||
tester.emit(IGen::sar_gpr64_cl(i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shl_gpr64_u8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v << sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::shl_gpr64_u8(i, sa));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, shr_gpr64_u8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<u64> vals = {0, 1, u64(-2), u64(INT32_MIN), INT32_MAX, u64(INT64_MIN),
|
||||
INT64_MAX, 117, 32, u64(-348473), 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::shr_gpr64_u8(i, sa));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, sar_gpr64_u8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
std::vector<s64> vals = {0, 1, -2, INT32_MIN, INT32_MAX, INT64_MIN,
|
||||
INT64_MAX, 117, 32, -348473, 83747382};
|
||||
std::vector<u8> sas = {0, 1, 23, 53, 64};
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (i == RSP) {
|
||||
continue;
|
||||
}
|
||||
for (auto v : vals) {
|
||||
for (auto sa : sas) {
|
||||
auto expected = v >> sa;
|
||||
tester.clear();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(i, v));
|
||||
tester.emit(IGen::sar_gpr64_u8(i, sa));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s64>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, jumps) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(256);
|
||||
|
||||
std::vector<int> reads;
|
||||
|
||||
auto x = IGen::jmp_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::je_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jne_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jle_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jge_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jl_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jg_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jbe_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jae_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::jb_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
x = IGen::ja_32();
|
||||
reads.push_back(tester.size() + x.offset_of_imm());
|
||||
tester.emit(x);
|
||||
|
||||
for (auto off : reads) {
|
||||
EXPECT_EQ(0, tester.read<s32>(off));
|
||||
}
|
||||
|
||||
EXPECT_EQ(tester.dump_to_hex_string(true),
|
||||
"E9000000000F84000000000F85000000000F8E000000000F8D000000000F8C000000000F8F000000000F86"
|
||||
"000000000F83000000000F82000000000F8700000000");
|
||||
}
|
||||
|
||||
TEST(EmitterIntegerMath, null) {
|
||||
auto instr = IGen::null();
|
||||
EXPECT_EQ(0, instr.emit(nullptr));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
/*!
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/emitter/CodeTester.h"
|
||||
#include "goalc/emitter/IGen.h"
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
using namespace emitter;
|
||||
|
||||
TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(XMM3, RAX, RBX));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 1c 03");
|
||||
|
||||
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++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
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)));
|
||||
|
||||
// fill k with junk
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// 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
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64(XMM0 + k, i, j));
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 3 * sizeof(float), 0, 0), 3.45f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 2 * sizeof(float), 0, 0), 1.23f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 4 * sizeof(float), 0, 0), 5.67f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 5 * sizeof(float), 0, 0), 0);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM3, RAX, RBX, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 5c 03 ff");
|
||||
|
||||
auto instr = IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM3, RBX, RSI, -3);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(s8(buff[instr.offset_of_disp()]), -3);
|
||||
|
||||
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++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
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)));
|
||||
|
||||
// fill k with junk
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// 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
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s8(XMM0 + k, i, j, -3));
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 3 * sizeof(float) + 3, 0, 0), 3.45f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 2 * sizeof(float) + 3, 0, 0), 1.23f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 4 * sizeof(float) + 3, 0, 0), 5.67f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 5 * sizeof(float) + 3, 0, 0), 0);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, load32_xmm32_gpr64_plus_gpr64_plus_s32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM3, RAX, RBX, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 10 9c 03 ff ff ff ff");
|
||||
|
||||
auto instr = IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM3, RBX, RSI, -1234);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(*(s32*)(buff + instr.offset_of_disp()), -1234);
|
||||
|
||||
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++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
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)));
|
||||
|
||||
// fill k with junk
|
||||
tester.emit(IGen::mov_gpr64_u64(i, (iter & 1) ? 0 : UINT64_MAX));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// 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
|
||||
|
||||
s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN;
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::load32_xmm32_gpr64_plus_gpr64_plus_s32(XMM0 + k, i, j, offset));
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 3 * sizeof(float) - offset, 0, 0), 3.45f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 2 * sizeof(float) - offset, 0, 0), 1.23f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 4 * sizeof(float) - offset, 0, 0), 5.67f);
|
||||
EXPECT_EQ(tester.execute_ret<float>((u64)memory, 5 * sizeof(float) - offset, 0, 0), 0);
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
float as_float(T x) {
|
||||
float result;
|
||||
memcpy(&result, &x, sizeof(float));
|
||||
return result;
|
||||
}
|
||||
|
||||
u32 as_u32(float x) {
|
||||
u32 result;
|
||||
memcpy(&result, &x, 4);
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(RAX, RBX, XMM7));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 3c 03");
|
||||
|
||||
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++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value
|
||||
|
||||
// pop value into addr1 GPR
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
// move to XMM
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop addrs
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
tester.emit(IGen::pop_gpr64(j));
|
||||
|
||||
// store
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64(i, j, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
tester.execute((u64)memory, 12, as_u32(1.234f), 0);
|
||||
EXPECT_EQ(memory[2], 1.23f);
|
||||
EXPECT_EQ(memory[3], 1.234f);
|
||||
EXPECT_EQ(memory[4], 5.67f);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s8) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(RAX, RBX, XMM3, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 5c 03 ff");
|
||||
|
||||
auto instr = IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(RBX, RSI, XMM3, -3);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(s8(buff[instr.offset_of_disp()]), -3);
|
||||
|
||||
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++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value
|
||||
|
||||
// pop value into addr1 GPR
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
// move to XMM
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop addrs
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
tester.emit(IGen::pop_gpr64(j));
|
||||
|
||||
s64 offset = (iter & 1) ? INT8_MAX : INT8_MIN;
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s8(i, j, XMM0 + k, offset));
|
||||
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
tester.execute((u64)memory, 12 - offset, as_u32(1.234f), 0);
|
||||
EXPECT_EQ(memory[2], 1.23f);
|
||||
EXPECT_EQ(memory[3], 1.234f);
|
||||
EXPECT_EQ(memory[4], 5.67f);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, store32_xmm32_gpr64_plus_gpr64_plus_s32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(RAX, RBX, XMM3, -1));
|
||||
EXPECT_EQ(tester.dump_to_hex_string(), "f3 0f 11 9c 03 ff ff ff ff");
|
||||
|
||||
auto instr = IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(RBX, RSI, XMM3, -1234);
|
||||
u8 buff[256];
|
||||
instr.emit(buff);
|
||||
EXPECT_EQ(*(s32*)(buff + instr.offset_of_disp()), -1234);
|
||||
|
||||
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++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
// push args to the stack
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(1))); // addr2
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(0))); // addr1
|
||||
tester.emit(IGen::push_gpr64(tester.get_c_abi_arg_reg(2))); // value
|
||||
|
||||
// pop value into addr1 GPR
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
// move to XMM
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + k, i));
|
||||
|
||||
// pop addrs
|
||||
tester.emit(IGen::pop_gpr64(i));
|
||||
tester.emit(IGen::pop_gpr64(j));
|
||||
|
||||
s64 offset = (iter & 1) ? INT32_MAX : INT32_MIN;
|
||||
|
||||
// load into k
|
||||
tester.emit(IGen::store32_xmm32_gpr64_plus_gpr64_plus_s32(i, j, XMM0 + k, offset));
|
||||
|
||||
// move to return
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + k));
|
||||
|
||||
// return!
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
|
||||
// prepare the memory:
|
||||
float memory[8] = {0, 0, 1.23f, 3.45f, 5.67f, 0, 0, 0};
|
||||
|
||||
// run!
|
||||
tester.execute((u64)memory, 12 - offset, as_u32(1.234f), 0);
|
||||
EXPECT_EQ(memory[2], 1.23f);
|
||||
EXPECT_EQ(memory[3], 1.234f);
|
||||
EXPECT_EQ(memory[4], 5.67f);
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, static_load_xmm32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
|
||||
auto loc_of_load = tester.size();
|
||||
auto load_instr = IGen::static_load_xmm32(XMM0 + i, INT32_MAX);
|
||||
|
||||
tester.emit(load_instr);
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto loc_of_float = tester.emit_data(float(1.2345f));
|
||||
|
||||
// patch offset
|
||||
tester.write<s32>(loc_of_float - loc_of_load - load_instr.length(),
|
||||
loc_of_load + load_instr.offset_of_disp());
|
||||
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, 1.2345f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, static_store_xmm32) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, tester.get_c_abi_arg_reg(0)));
|
||||
|
||||
auto loc_of_store = tester.size();
|
||||
auto store_instr = IGen::static_store_xmm32(XMM0 + i, INT32_MAX);
|
||||
|
||||
tester.emit(store_instr);
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto loc_of_float = tester.emit_data(float(1.2345f));
|
||||
|
||||
tester.write<s32>(loc_of_float - loc_of_store - store_instr.length(),
|
||||
loc_of_store + store_instr.offset_of_disp());
|
||||
tester.execute(as_u32(-44.567f), 0, 0, 0);
|
||||
EXPECT_EQ(-44.567f, tester.read<float>(loc_of_float));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, ucomiss) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
tester.emit(IGen::cmp_flt_flt(XMM13, XMM14));
|
||||
EXPECT_EQ("45 0f 2e ee", tester.dump_to_hex_string());
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, mul) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = f * g;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::mulss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, div) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = g / f;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::divss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, add) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = g + f;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::addss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, sub) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f, 7.545f};
|
||||
|
||||
for (auto f : vals) {
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
auto expected = g - f;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &f, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + j, RAX));
|
||||
tester.emit(IGen::subss_xmm_xmm(XMM0 + j, XMM0 + i));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, float_to_int) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<float> vals = {0.f, 1.f, 0.2f, -1.f, 1235423.2f, -3457343.3f,
|
||||
7.545f, 0.1f, 0.9f, -0.1f, -0.9f};
|
||||
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP) {
|
||||
continue;
|
||||
}
|
||||
s32 expected = g;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
u64 val = 0;
|
||||
memcpy(&val, &g, sizeof(float));
|
||||
tester.emit(IGen::mov_gpr64_u64(RAX, val));
|
||||
tester.emit(IGen::movd_xmm32_gpr32(XMM0 + i, RAX));
|
||||
tester.emit(IGen::float_to_int32(j, XMM0 + i));
|
||||
tester.emit(IGen::mov_gpr64_gpr64(RAX, j));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<s32>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EmitterXmm32, int_to_float) {
|
||||
CodeTester tester;
|
||||
tester.init_code_buffer(512);
|
||||
|
||||
std::vector<s64> vals = {0, 1, -1, INT32_MAX, -3457343, 7, INT32_MIN};
|
||||
|
||||
for (auto g : vals) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < 16; j++) {
|
||||
if (j == RSP) {
|
||||
continue;
|
||||
}
|
||||
float expected = g;
|
||||
tester.clear();
|
||||
tester.emit_push_all_xmms();
|
||||
tester.emit_push_all_gprs(true);
|
||||
tester.emit(IGen::mov_gpr64_u64(j, g));
|
||||
tester.emit(IGen::int32_to_float(XMM0 + i, j));
|
||||
tester.emit(IGen::movd_gpr32_xmm32(RAX, XMM0 + i));
|
||||
tester.emit_pop_all_gprs(true);
|
||||
tester.emit_pop_all_xmms();
|
||||
tester.emit_return();
|
||||
auto result = tester.execute_ret<float>(0, 0, 0, 0);
|
||||
EXPECT_EQ(result, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <unordered_set>
|
||||
#include "gtest/gtest.h"
|
||||
#include "common/symbols.h"
|
||||
#include "common/goal_constants.h"
|
||||
#include "game/kernel/fileio.h"
|
||||
#include "game/kernel/kboot.h"
|
||||
#include "game/kernel/kprint.h"
|
||||
@@ -263,8 +264,7 @@ TEST(Kernel, itoa_base_16) {
|
||||
EXPECT_EQ("1", std::string(buffer));
|
||||
|
||||
kitoa(buffer, -1, 16, -1, ' ', 0);
|
||||
EXPECT_EQ("ffffffffffffffff",
|
||||
std::string(buffer));
|
||||
EXPECT_EQ("ffffffffffffffff", std::string(buffer));
|
||||
|
||||
kitoa(buffer, -1, 16, 5, ' ', 0);
|
||||
EXPECT_EQ("-fffff", std::string(buffer));
|
||||
@@ -282,8 +282,7 @@ TEST(Kernel, itoa_base_16) {
|
||||
EXPECT_EQ("jjjjbadbeef", std::string(buffer));
|
||||
|
||||
kitoa(buffer, INT64_MIN, 16, 0, ' ', 0);
|
||||
EXPECT_EQ("8000000000000000",
|
||||
std::string(buffer));
|
||||
EXPECT_EQ("8000000000000000", std::string(buffer));
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -343,7 +342,6 @@ TEST(Kernel, PrintBuffer) {
|
||||
// more complicated tests for format will be done from within GOAL.
|
||||
}
|
||||
|
||||
|
||||
TEST(Kernel, HashTable) {
|
||||
constexpr int size = 32 * 1024 * 1024;
|
||||
auto mem = new u8[size];
|
||||
@@ -360,7 +358,7 @@ TEST(Kernel, HashTable) {
|
||||
std::unordered_map<std::string, u32> symbol_locations;
|
||||
std::unordered_set<u32> unique_locations;
|
||||
|
||||
for(auto name : all_syms) {
|
||||
for (auto name : all_syms) {
|
||||
auto loc = intern_from_c(name).offset;
|
||||
symbol_locations[name] = loc;
|
||||
unique_locations.insert(loc);
|
||||
@@ -369,7 +367,7 @@ TEST(Kernel, HashTable) {
|
||||
EXPECT_EQ(7941, symbol_locations.size());
|
||||
EXPECT_EQ(7941, unique_locations.size());
|
||||
|
||||
for(auto name : all_syms) {
|
||||
for (auto name : all_syms) {
|
||||
EXPECT_EQ(symbol_locations.at(name), intern_from_c(name).offset);
|
||||
}
|
||||
|
||||
@@ -380,11 +378,10 @@ TEST(Kernel, HashTable) {
|
||||
|
||||
// expect no crc32 hash collisions. This doesn't matter, but it's nice to know.
|
||||
std::unordered_set<u32> crc32s;
|
||||
for(auto name : all_syms) {
|
||||
for (auto name : all_syms) {
|
||||
crc32s.insert(crc32((const u8*)name, strlen(name)));
|
||||
}
|
||||
EXPECT_EQ(7941, crc32s.size());
|
||||
|
||||
delete[] mem;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "goalc/listener/Listener.h"
|
||||
#include "goalc/listener/Deci2Server.h"
|
||||
#include "game/system/Deci2Server.h"
|
||||
|
||||
using namespace listener;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace {
|
||||
bool always_false() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(Listener, ListenerCreation) {
|
||||
listener::Listener l;
|
||||
@@ -25,7 +25,7 @@ TEST(Listener, DeciInit) {
|
||||
EXPECT_TRUE(s.init());
|
||||
}
|
||||
|
||||
//TEST(Listener, TwoDeciServers) {
|
||||
// TEST(Listener, TwoDeciServers) {
|
||||
// Deci2Server s1, s2;
|
||||
// EXPECT_TRUE(s1.init());
|
||||
// EXPECT_TRUE(s2.init());
|
||||
@@ -36,6 +36,11 @@ TEST(Listener, DeciInit) {
|
||||
*/
|
||||
TEST(Listener, ListenToNothing) {
|
||||
Listener l;
|
||||
if (l.connect_to_target()) {
|
||||
printf(
|
||||
"~~~~~~ Test connected to a runtime when there shouldn't be anything running! Check that "
|
||||
"you don't have gk running in the background!\n");
|
||||
}
|
||||
EXPECT_FALSE(l.connect_to_target());
|
||||
l.disconnect();
|
||||
}
|
||||
@@ -49,7 +54,7 @@ TEST(Listener, DeciCheckNoListener) {
|
||||
}
|
||||
|
||||
TEST(Listener, DeciThenListener) {
|
||||
for(int i = 0; i < 10; i++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Deci2Server s(always_false);
|
||||
EXPECT_TRUE(s.init());
|
||||
EXPECT_FALSE(s.check_for_listener());
|
||||
@@ -60,8 +65,8 @@ TEST(Listener, DeciThenListener) {
|
||||
EXPECT_FALSE(s.check_for_listener());
|
||||
EXPECT_TRUE(l.connect_to_target());
|
||||
// kind of a hack.
|
||||
while(!s.check_for_listener()) {
|
||||
printf("...\n");
|
||||
while (!s.check_for_listener()) {
|
||||
// printf("...\n");
|
||||
}
|
||||
|
||||
EXPECT_TRUE(s.check_for_listener());
|
||||
@@ -69,7 +74,7 @@ TEST(Listener, DeciThenListener) {
|
||||
}
|
||||
|
||||
TEST(Listener, DeciThenListener2) {
|
||||
for(int i = 0; i < 10; i++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Deci2Server s(always_false);
|
||||
EXPECT_TRUE(s.init());
|
||||
EXPECT_FALSE(s.check_for_listener());
|
||||
@@ -83,15 +88,15 @@ TEST(Listener, DeciThenListener2) {
|
||||
}
|
||||
|
||||
TEST(Listener, ListenerThenDeci) {
|
||||
for(int i = 0; i < 10; i++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Listener l;
|
||||
EXPECT_FALSE(l.connect_to_target());
|
||||
Deci2Server s(always_false);
|
||||
EXPECT_TRUE(s.init());
|
||||
EXPECT_FALSE(s.check_for_listener());
|
||||
EXPECT_TRUE(l.connect_to_target());
|
||||
while(!s.check_for_listener()) {
|
||||
printf("...\n");
|
||||
while (!s.check_for_listener()) {
|
||||
// printf("...\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,8 +109,8 @@ TEST(Listener, ListenerMultipleDecis) {
|
||||
EXPECT_TRUE(s.init());
|
||||
EXPECT_FALSE(s.check_for_listener());
|
||||
EXPECT_TRUE(l.connect_to_target());
|
||||
while(!s.check_for_listener()) {
|
||||
printf("...\n");
|
||||
while (!s.check_for_listener()) {
|
||||
// printf("...\n");
|
||||
}
|
||||
l.disconnect();
|
||||
}
|
||||
@@ -115,8 +120,8 @@ TEST(Listener, ListenerMultipleDecis) {
|
||||
EXPECT_TRUE(s.init());
|
||||
EXPECT_FALSE(s.check_for_listener());
|
||||
EXPECT_TRUE(l.connect_to_target());
|
||||
while(!s.check_for_listener()) {
|
||||
printf("...\n");
|
||||
while (!s.check_for_listener()) {
|
||||
// printf("...\n");
|
||||
}
|
||||
l.disconnect();
|
||||
}
|
||||
|
||||
@@ -316,8 +316,7 @@ TEST(GoosReader, TopLevel) {
|
||||
|
||||
TEST(GoosReader, FromFile) {
|
||||
Reader reader;
|
||||
auto result =
|
||||
reader.read_from_file(util::combine_path({"test", "test_reader_file0.gc"})).print();
|
||||
auto result = reader.read_from_file(util::combine_path({"test", "test_reader_file0.gc"})).print();
|
||||
EXPECT_TRUE(result == "(top-level (1 2 3 4))");
|
||||
}
|
||||
|
||||
|
||||
+304
-11
@@ -3,23 +3,316 @@
|
||||
#include "third-party/fmt/core.h"
|
||||
|
||||
TEST(TypeSystem, Construction) {
|
||||
// test that we can add all builtin types without any type errors
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
fmt::print("{}", ts.print_all_type_information());
|
||||
|
||||
// useful for debugging.
|
||||
// fmt::print("{}", ts.print_all_type_information());
|
||||
}
|
||||
|
||||
TEST(TypeSystem, DefaultMethods) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
// check that default methods have the right ID's used by the kernel
|
||||
ts.assert_method_id("object", "new", GOAL_NEW_METHOD);
|
||||
ts.assert_method_id("object", "delete", GOAL_DEL_METHOD);
|
||||
ts.assert_method_id("object", "print", GOAL_PRINT_METHOD);
|
||||
ts.assert_method_id("object", "inspect", GOAL_INSPECT_METHOD);
|
||||
ts.assert_method_id("object", "length", GOAL_LENGTH_METHOD);
|
||||
ts.assert_method_id("object", "asize-of", GOAL_ASIZE_METHOD);
|
||||
ts.assert_method_id("object", "copy", GOAL_COPY_METHOD);
|
||||
ts.assert_method_id("object", "relocate", GOAL_RELOC_METHOD);
|
||||
ts.assert_method_id("object", "mem-usage", GOAL_MEMUSAGE_METHOD);
|
||||
|
||||
ts.assert_method_id("object", "new", 0);
|
||||
ts.assert_method_id("object", "delete", 1);
|
||||
ts.assert_method_id("object", "print", 2);
|
||||
ts.assert_method_id("object", "inspect", 3);
|
||||
ts.assert_method_id("object", "length", 4);
|
||||
ts.assert_method_id("object", "asize-of", 5);
|
||||
ts.assert_method_id("object", "copy", 6);
|
||||
ts.assert_method_id("object", "relocate", 7);
|
||||
ts.assert_method_id("object", "mem-usage", 8);
|
||||
}
|
||||
// check that they are inherited.
|
||||
ts.assert_method_id("function", "new", GOAL_NEW_METHOD);
|
||||
ts.assert_method_id("function", "delete", GOAL_DEL_METHOD);
|
||||
ts.assert_method_id("function", "print", GOAL_PRINT_METHOD);
|
||||
ts.assert_method_id("function", "inspect", GOAL_INSPECT_METHOD);
|
||||
ts.assert_method_id("function", "length", GOAL_LENGTH_METHOD);
|
||||
ts.assert_method_id("function", "asize-of", GOAL_ASIZE_METHOD);
|
||||
ts.assert_method_id("function", "copy", GOAL_COPY_METHOD);
|
||||
ts.assert_method_id("function", "relocate", GOAL_RELOC_METHOD);
|
||||
ts.assert_method_id("function", "mem-usage", GOAL_MEMUSAGE_METHOD);
|
||||
}
|
||||
|
||||
TEST(TypeSystem, TypeSpec) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
// try some simple typespecs
|
||||
auto string_typespec = ts.make_typespec("string");
|
||||
auto function_typespec = ts.make_typespec("function");
|
||||
EXPECT_EQ(string_typespec.print(), "string");
|
||||
EXPECT_EQ(string_typespec.base_type(), "string");
|
||||
EXPECT_TRUE(function_typespec == function_typespec);
|
||||
EXPECT_FALSE(function_typespec == string_typespec);
|
||||
|
||||
// try some pointer typespecs
|
||||
auto pointer_function_typespec = ts.make_pointer_typespec("function");
|
||||
EXPECT_EQ(pointer_function_typespec.print(), "(pointer function)");
|
||||
EXPECT_EQ(pointer_function_typespec.get_single_arg(), ts.make_typespec("function"));
|
||||
EXPECT_EQ(pointer_function_typespec.base_type(), "pointer");
|
||||
|
||||
// try some function typespecs
|
||||
auto test_function_typespec = ts.make_function_typespec({"string", "symbol"}, "integer");
|
||||
EXPECT_EQ(test_function_typespec.base_type(), "function");
|
||||
EXPECT_EQ(test_function_typespec.print(), "(function string symbol integer)");
|
||||
|
||||
// try the none typespec
|
||||
EXPECT_EQ(ts.make_typespec("none").base_type(), "none");
|
||||
}
|
||||
|
||||
TEST(TypeSystem, TypeSpecEquality) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
auto pointer_to_function = ts.make_pointer_typespec("function");
|
||||
auto ia_to_function = ts.make_inline_array_typespec("function");
|
||||
auto pointer_to_string = ts.make_pointer_typespec("string");
|
||||
|
||||
EXPECT_TRUE(pointer_to_function == pointer_to_function);
|
||||
EXPECT_FALSE(pointer_to_function == ia_to_function);
|
||||
EXPECT_FALSE(pointer_to_string == pointer_to_function);
|
||||
}
|
||||
|
||||
TEST(TypeSystem, RuntimeTypes) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
// pointers and inline arrays should all become simple pointers
|
||||
EXPECT_EQ(ts.get_runtime_type(ts.make_typespec("pointer")), "pointer");
|
||||
EXPECT_EQ(ts.get_runtime_type(ts.make_typespec("inline-array")), "pointer");
|
||||
EXPECT_EQ(ts.get_runtime_type(ts.make_pointer_typespec("function")), "pointer");
|
||||
EXPECT_EQ(ts.get_runtime_type(ts.make_inline_array_typespec("function")), "pointer");
|
||||
|
||||
// functions of any kind should become function
|
||||
EXPECT_EQ(ts.get_runtime_type(ts.make_function_typespec({"integer", "string"}, "symbol")),
|
||||
"function");
|
||||
}
|
||||
|
||||
TEST(TypeSystem, ForwardDeclaration) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
// before forward declaring, lookup and creating a typespec should fail
|
||||
EXPECT_ANY_THROW(ts.lookup_type("test-type"));
|
||||
EXPECT_ANY_THROW(ts.make_typespec("test-type"));
|
||||
|
||||
// after forward declaring, we should be able to create typespec, but not do a full lookup
|
||||
ts.forward_declare_type("test-type");
|
||||
|
||||
EXPECT_TRUE(ts.make_typespec("test-type").print() == "test-type");
|
||||
EXPECT_ANY_THROW(ts.lookup_type("test-type"));
|
||||
}
|
||||
|
||||
TEST(TypeSystem, DerefInfoNoLoadInfoOrStride) {
|
||||
// test the parts of deref info, other than the part where it tells you how to load or the stride.
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
// can't dereference a non-pointer
|
||||
EXPECT_FALSE(ts.get_deref_info(ts.make_typespec("string")).can_deref);
|
||||
// can't deref a pointer with no type
|
||||
EXPECT_FALSE(ts.get_deref_info(ts.make_typespec("pointer")).can_deref);
|
||||
EXPECT_FALSE(ts.get_deref_info(ts.make_typespec("inline-array")).can_deref);
|
||||
|
||||
// test pointer to reference type
|
||||
auto type_spec =
|
||||
ts.make_pointer_typespec(ts.make_function_typespec({"string", "symbol"}, "int32"));
|
||||
auto info = ts.get_deref_info(type_spec);
|
||||
EXPECT_TRUE(info.can_deref);
|
||||
EXPECT_TRUE(info.mem_deref);
|
||||
EXPECT_FALSE(info.sign_extend); // it's a memory address being loaded
|
||||
EXPECT_EQ(info.reg, RegKind::GPR_64);
|
||||
EXPECT_EQ(info.stride, 4);
|
||||
EXPECT_EQ(info.result_type.print(), "(function string symbol int32)");
|
||||
EXPECT_EQ(info.load_size, 4);
|
||||
|
||||
// test pointer to value type
|
||||
type_spec = ts.make_pointer_typespec("int64");
|
||||
info = ts.get_deref_info(type_spec);
|
||||
EXPECT_TRUE(info.can_deref);
|
||||
EXPECT_TRUE(info.mem_deref);
|
||||
EXPECT_EQ(info.load_size, 8);
|
||||
EXPECT_EQ(info.stride, 8);
|
||||
EXPECT_EQ(info.sign_extend, true);
|
||||
EXPECT_EQ(info.reg, RegKind::GPR_64);
|
||||
EXPECT_EQ(info.result_type.print(), "int64");
|
||||
|
||||
// test inline-array (won't work because type is dynamically sized)
|
||||
type_spec = ts.make_inline_array_typespec("type");
|
||||
info = ts.get_deref_info(type_spec);
|
||||
EXPECT_FALSE(info.can_deref);
|
||||
|
||||
// TODO - replace with a better type.
|
||||
// TODO - maybe block basic or structure from being inline-array-able?
|
||||
type_spec = ts.make_inline_array_typespec("basic");
|
||||
auto type = ts.lookup_type("basic");
|
||||
info = ts.get_deref_info(type_spec);
|
||||
EXPECT_TRUE(info.can_deref);
|
||||
EXPECT_FALSE(info.mem_deref);
|
||||
EXPECT_EQ(info.stride, (type->get_size_in_memory() + 15) & (~15));
|
||||
EXPECT_EQ(info.result_type.print(), "basic");
|
||||
EXPECT_EQ(info.load_size, -1);
|
||||
}
|
||||
|
||||
TEST(TypeSystem, AddMethodAndLookupMethod) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
auto parent_info = ts.add_method(ts.lookup_type("structure"), "test-method-1",
|
||||
ts.make_function_typespec({"integer"}, "string"));
|
||||
|
||||
// when trying to add the same method to a child, should return the parent's method
|
||||
auto child_info_same = ts.add_method(ts.lookup_type("basic"), "test-method-1",
|
||||
ts.make_function_typespec({"integer"}, "string"));
|
||||
|
||||
EXPECT_EQ(parent_info.id, child_info_same.id);
|
||||
EXPECT_EQ(parent_info.id, GOAL_MEMUSAGE_METHOD + 1);
|
||||
|
||||
// any amount of fiddling with method types should cause an error
|
||||
EXPECT_ANY_THROW(ts.add_method(ts.lookup_type("basic"), "test-method-1",
|
||||
ts.make_function_typespec({"integer"}, "integer")));
|
||||
EXPECT_ANY_THROW(ts.add_method(ts.lookup_type("basic"), "test-method-1",
|
||||
ts.make_function_typespec({}, "string")));
|
||||
EXPECT_ANY_THROW(ts.add_method(ts.lookup_type("basic"), "test-method-1",
|
||||
ts.make_function_typespec({"integer", "string"}, "string")));
|
||||
EXPECT_ANY_THROW(ts.add_method(ts.lookup_type("basic"), "test-method-1",
|
||||
ts.make_function_typespec({"string"}, "string")));
|
||||
|
||||
ts.add_method(ts.lookup_type("basic"), "test-method-2",
|
||||
ts.make_function_typespec({"integer"}, "string"));
|
||||
|
||||
EXPECT_EQ(parent_info.id, ts.lookup_method("basic", "test-method-1").id);
|
||||
EXPECT_EQ(parent_info.id, ts.lookup_method("structure", "test-method-1").id);
|
||||
EXPECT_EQ(parent_info.id + 1, ts.lookup_method("basic", "test-method-2").id);
|
||||
EXPECT_ANY_THROW(ts.lookup_method("structure", "test-method-2"));
|
||||
|
||||
EXPECT_EQ(ts.lookup_method("basic", "test-method-1").defined_in_type, "structure");
|
||||
EXPECT_EQ(ts.lookup_method("basic", "test-method-1").type.print(), "(function integer string)");
|
||||
EXPECT_EQ(ts.lookup_method("basic", "test-method-1").name, "test-method-1");
|
||||
}
|
||||
|
||||
TEST(TypeSystem, NewMethod) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
ts.add_type("test-1", std::make_unique<BasicType>("basic", "test-1"));
|
||||
ts.add_method(ts.lookup_type("test-1"), "new",
|
||||
ts.make_function_typespec({"symbol", "string"}, "test-1"));
|
||||
ts.add_type("test-2", std::make_unique<BasicType>("test-1", "test-2"));
|
||||
ts.add_method(ts.lookup_type("test-2"), "new",
|
||||
ts.make_function_typespec({"symbol", "string", "symbol"}, "test-2"));
|
||||
|
||||
EXPECT_EQ(ts.lookup_method("test-1", "new").type.print(), "(function symbol string test-1)");
|
||||
EXPECT_EQ(ts.lookup_method("test-2", "new").type.print(),
|
||||
"(function symbol string symbol test-2)");
|
||||
|
||||
ts.add_type("test-3", std::make_unique<BasicType>("test-1", "test-3"));
|
||||
EXPECT_EQ(ts.lookup_method("test-3", "new").type.print(), "(function symbol string test-1)");
|
||||
|
||||
ts.add_type("test-4", std::make_unique<BasicType>("test-2", "test-4"));
|
||||
EXPECT_EQ(ts.lookup_method("test-4", "new").type.print(),
|
||||
"(function symbol string symbol test-2)");
|
||||
}
|
||||
|
||||
TEST(TypeSystem, MethodSubstitute) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
ts.add_type("test-1", std::make_unique<BasicType>("basic", "test-1"));
|
||||
ts.add_method(ts.lookup_type("test-1"), "new",
|
||||
ts.make_function_typespec({"symbol", "string", "_type_"}, "_type_"));
|
||||
|
||||
auto final_type = ts.lookup_method("test-1", "new").type.substitute_for_method_call("test-1");
|
||||
EXPECT_EQ(final_type.print(), "(function symbol string test-1 test-1)");
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool ts_name_name(TypeSystem& ts, const std::string& ex, const std::string& act) {
|
||||
return ts.typecheck(ts.make_typespec(ex), ts.make_typespec(act), "", false, false);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(TypeSystem, TypeCheck) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
EXPECT_TRUE(ts_name_name(ts, "none",
|
||||
"none")); // none - none _shouldn't_ fail (for function return types!)
|
||||
EXPECT_TRUE(ts_name_name(ts, "object", "object"));
|
||||
EXPECT_TRUE(ts_name_name(ts, "object", "type"));
|
||||
EXPECT_TRUE(ts_name_name(ts, "basic", "type"));
|
||||
EXPECT_FALSE(ts_name_name(ts, "type", "basic"));
|
||||
EXPECT_TRUE(ts_name_name(ts, "type", "type"));
|
||||
|
||||
auto f = ts.make_typespec("function");
|
||||
auto f_s_n = ts.make_function_typespec({"string"}, "none");
|
||||
auto f_b_n = ts.make_function_typespec({"basic"}, "none");
|
||||
|
||||
// complex
|
||||
EXPECT_TRUE(ts.typecheck(f, f_s_n));
|
||||
EXPECT_TRUE(ts.typecheck(f_s_n, f_s_n));
|
||||
EXPECT_TRUE(ts.typecheck(f_b_n, f_s_n));
|
||||
|
||||
EXPECT_FALSE(ts.typecheck(f_s_n, f, "", false, false));
|
||||
EXPECT_FALSE(ts.typecheck(f_s_n, f_b_n, "", false, false));
|
||||
|
||||
// number of parameter mismatch
|
||||
auto f_s_s_n = ts.make_function_typespec({"string", "string"}, "none");
|
||||
EXPECT_FALSE(ts.typecheck(f_s_n, f_s_s_n, "", false, false));
|
||||
EXPECT_FALSE(ts.typecheck(f_s_s_n, f_s_n, "", false, false));
|
||||
}
|
||||
|
||||
TEST(TypeSystem, FieldLookup) {
|
||||
// note - this test isn't testing the specific needs_deref, type of the returned info. Until more
|
||||
// stuff is set up that test is kinda useless - it would just be testing against the exact
|
||||
// implementation of lookup_field_info
|
||||
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
|
||||
EXPECT_EQ(ts.lookup_field_info("type", "parent").field.offset(), 8);
|
||||
EXPECT_EQ(ts.lookup_field_info("string", "data").type.print(), "(pointer uint8)");
|
||||
|
||||
EXPECT_ANY_THROW(ts.lookup_field_info("type", "not-a-real-field"));
|
||||
}
|
||||
|
||||
TEST(TypeSystem, get_path_up_tree) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
EXPECT_EQ(ts.get_path_up_tree("type"),
|
||||
std::vector<std::string>({"type", "basic", "structure", "object"}));
|
||||
}
|
||||
|
||||
TEST(TypeSystem, lca) {
|
||||
TypeSystem ts;
|
||||
ts.add_builtin_types();
|
||||
EXPECT_EQ(
|
||||
ts.lowest_common_ancestor(ts.make_typespec("string"), ts.make_typespec("basic")).print(),
|
||||
"basic");
|
||||
EXPECT_EQ(
|
||||
ts.lowest_common_ancestor(ts.make_typespec("basic"), ts.make_typespec("string")).print(),
|
||||
"basic");
|
||||
EXPECT_EQ(
|
||||
ts.lowest_common_ancestor(ts.make_typespec("string"), ts.make_typespec("int32")).print(),
|
||||
"object");
|
||||
EXPECT_EQ(
|
||||
ts.lowest_common_ancestor(ts.make_typespec("int32"), ts.make_typespec("string")).print(),
|
||||
"object");
|
||||
EXPECT_EQ(
|
||||
ts.lowest_common_ancestor({ts.make_typespec("int32"), ts.make_typespec("string")}).print(),
|
||||
"object");
|
||||
EXPECT_EQ(ts.lowest_common_ancestor({ts.make_typespec("int32")}).print(), "int32");
|
||||
EXPECT_EQ(ts.lowest_common_ancestor({ts.make_typespec("string"), ts.make_typespec("kheap"),
|
||||
ts.make_typespec("pointer")})
|
||||
.print(),
|
||||
"object");
|
||||
|
||||
EXPECT_EQ(ts.lowest_common_ancestor(ts.make_pointer_typespec("int32"),
|
||||
ts.make_pointer_typespec("string"))
|
||||
.print(),
|
||||
"(pointer object)");
|
||||
}
|
||||
// TODO - a big test to make sure all the builtin types are what we expect.
|
||||
|
||||
Reference in New Issue
Block a user