Merge remote-tracking branch 'water111/master' into windows-nodeci

This commit is contained in:
Tyler Wilding
2020-09-03 20:16:28 -04:00
108 changed files with 8301 additions and 2203 deletions
+9 -5
View File
@@ -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
View File
@@ -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();
}
+628
View File
@@ -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
+53
View File
@@ -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();
}
}
}
}
}
}
+659
View File
@@ -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);
}
}
}
}
+6 -9
View File
@@ -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;
}
+19 -14
View File
@@ -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();
}
+1 -2
View File
@@ -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
View File
@@ -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.