working return integer tests as part of gtest

This commit is contained in:
water
2020-09-06 16:58:25 -04:00
parent 8bf0bd86d3
commit d49b01e310
36 changed files with 648 additions and 266 deletions
+14 -13
View File
@@ -6,12 +6,13 @@ set(CMAKE_CXX_STANDARD 14)
# Set default compile flags for GCC
# optimization level can be set here. Note that game/ overwrites this for building game C++ code.
if(CMAKE_COMPILER_IS_GNUCXX)
if (CMAKE_COMPILER_IS_GNUCXX)
message(STATUS "GCC detected, adding compile flags")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} \
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} \
-Wall \
-Winit-self \
-ggdb \
-Wextra \
-Wcast-align \
-Wcast-qual \
@@ -22,16 +23,16 @@ if(CMAKE_COMPILER_IS_GNUCXX)
-Wredundant-decls \
-Wshadow \
-Wsign-promo")
else()
set(CMAKE_CXX_FLAGS "/EHsc")
endif(CMAKE_COMPILER_IS_GNUCXX)
else ()
set(CMAKE_CXX_FLAGS "/EHsc")
endif (CMAKE_COMPILER_IS_GNUCXX)
IF (WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
ENDIF()
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
ENDIF ()
# includes relative to top level jak-project folder
include_directories(./)
@@ -65,5 +66,5 @@ add_subdirectory(third-party/fmt)
# windows memory management lib
IF (WIN32)
add_subdirectory(third-party/mman)
ENDIF()
add_subdirectory(third-party/mman)
ENDIF ()
+11 -15
View File
@@ -16,7 +16,7 @@ class BinaryWriter {
public:
BinaryWriter() = default;
template<typename T>
template <typename T>
BinaryWriterRef add(const T& obj) {
auto orig_size = data.size();
data.resize(orig_size + sizeof(T));
@@ -24,26 +24,24 @@ class BinaryWriter {
return {orig_size, sizeof(T)};
}
template<typename T>
template <typename T>
void add_at_ref(const T& obj, const BinaryWriterRef& ref) {
assert(ref.write_size == sizeof(T));
assert(ref.offset + ref.write_size < get_size());
memcpy(data.data() + ref.offset, &obj, sizeof(T));
}
void add_str_len(const std::string& str, size_t len) {
add_cstr_len(str.c_str(), len);
}
void add_str_len(const std::string& str, size_t len) { add_cstr_len(str.c_str(), len); }
void add_cstr_len(const char* str, size_t len) {
size_t i = 0;
while(*str) {
while (*str) {
data.push_back(*str);
str++;
i++;
}
while(i < len) {
while (i < len) {
data.push_back(0);
i++;
}
@@ -56,18 +54,16 @@ class BinaryWriter {
return {orig_size, len};
}
size_t get_size() {
return data.size();
}
size_t get_size() { return data.size(); }
void* get_data() {
return data.data();
}
void* get_data() { return data.data(); }
void write_to_file(const std::string& filename) {
auto fp = fopen(filename.c_str(), "wb");
if(!fp) throw std::runtime_error("failed to open " + filename);
if(fwrite(get_data(), get_size(), 1, fp) != 1) throw std::runtime_error("failed to write " + filename);
if (!fp)
throw std::runtime_error("failed to open " + filename);
if (fwrite(get_data(), get_size(), 1, fp) != 1)
throw std::runtime_error("failed to write " + filename);
fclose(fp);
}
+2 -2
View File
@@ -138,8 +138,8 @@ u32 InitRPC() {
*/
void StopIOP() {
x[2] = 0x14; // todo - this type and message
RpcSync(PLAYER_RPC_CHANNEL);
RpcCall(PLAYER_RPC_CHANNEL, 0, false, x, 0x50, nullptr, 0);
// RpcSync(PLAYER_RPC_CHANNEL);
// RpcCall(PLAYER_RPC_CHANNEL, 0, false, x, 0x50, nullptr, 0);
printf("IOP shut down\n");
// sceDmaSync(0x10009000, 0, 0);
printf("DMA shut down\n");
+3
View File
@@ -141,6 +141,9 @@ void ProcessListenerMessage(Ptr<char> msg) {
break;
case LTT_MSG_RESET:
MasterExit = 1;
if (protoBlock.msg_id == UINT64_MAX) {
MasterExit = 2;
}
break;
case LTT_MSG_CODE: {
auto buffer = kmalloc(kdebugheap, MessCount, 0, "listener-link-block");
+3 -1
View File
@@ -10,7 +10,9 @@ int main(int argc, char** argv) {
while (true) {
// run the runtime in a loop so we can reset the game and have it restart cleanly
printf("gk %d.%d\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
exec_runtime(argc, argv);
if (exec_runtime(argc, argv) == 2) {
return 0;
}
}
return 0;
}
+3 -2
View File
@@ -229,7 +229,7 @@ void iop_runner(SystemThreadInterface& iface) {
* Main function to launch the runtime.
* Arguments are currently ignored.
*/
void exec_runtime(int argc, char** argv) {
u32 exec_runtime(int argc, char** argv) {
(void)argc;
(void)argv;
@@ -262,5 +262,6 @@ void exec_runtime(int argc, char** argv) {
// join and exit
tm.join();
printf("GOAL Runtime Shutdown\n");
printf("GOAL Runtime Shutdown (code %d)\n", MasterExit);
return MasterExit;
}
+1 -1
View File
@@ -9,6 +9,6 @@
#include "common/common_types.h"
extern u8* g_ee_main_mem;
void exec_runtime(int argc, char** argv);
u32 exec_runtime(int argc, char** argv);
#endif // JAK1_RUNTIME_H
+2
View File
@@ -0,0 +1,2 @@
; simply return an integer
#x123456789
+1
View File
@@ -0,0 +1 @@
#x17
+1
View File
@@ -0,0 +1 @@
-17
+1
View File
@@ -0,0 +1 @@
-2147483648
+1
View File
@@ -0,0 +1 @@
-2147483649
+1
View File
@@ -0,0 +1 @@
0
+1
View File
@@ -0,0 +1 @@
-123
+116 -2
View File
@@ -1,3 +1,117 @@
#include "CodeGenerator.h"
#include "goalc/emitter/IGen.h"
#include "IR.h"
using namespace emitter;
constexpr int GPR_SIZE = 8;
constexpr int XMM_SIZE = 16;
CodeGenerator::CodeGenerator(FileEnv* env) : m_fe(env) {}
std::vector<u8> CodeGenerator::run() {
// todo, static objects
for (auto& f : m_fe->functions()) {
do_function(f.get());
}
return m_gen.generate_data_v3().to_vector();
}
void CodeGenerator::do_function(FunctionEnv* env) {
auto f_rec = m_gen.add_function_to_seg(env->segment); // todo, extra alignment settings
auto& ri = emitter::gRegInfo;
const auto& allocs = env->alloc_result();
// compute how much stack we will use
int stack_offset = 0;
// back up xmms
for (auto& saved_reg : allocs.used_saved_regs) {
if (saved_reg.is_xmm()) {
m_gen.add_instr_no_ir(f_rec, IGen::sub_gpr64_imm8s(RSP, XMM_SIZE));
m_gen.add_instr_no_ir(f_rec, IGen::store128_gpr64_xmm128(RSP, saved_reg));
stack_offset += XMM_SIZE;
}
}
// back up gprs
for (auto& saved_reg : allocs.used_saved_regs) {
if (saved_reg.is_gpr()) {
m_gen.add_instr_no_ir(f_rec, IGen::push_gpr64(saved_reg));
stack_offset += GPR_SIZE;
}
}
bool bonus_push = false;
int manually_added_stack_offset = GPR_SIZE * allocs.stack_slots;
stack_offset += manually_added_stack_offset;
if (manually_added_stack_offset || allocs.needs_aligned_stack_for_spills ||
env->needs_aligned_stack()) {
if (!(stack_offset & 15)) {
if (manually_added_stack_offset) {
manually_added_stack_offset += 8;
} else {
bonus_push = true;
m_gen.add_instr_no_ir(f_rec, IGen::push_gpr64(ri.get_saved_gpr(0)));
}
stack_offset += 8;
}
assert(stack_offset & 15);
if (manually_added_stack_offset) {
m_gen.add_instr_no_ir(f_rec, IGen::sub_gpr64_imm(RSP, manually_added_stack_offset));
}
}
// TODO EMIT FUNCTIONS
for (int ir_idx = 0; ir_idx < int(env->code().size()); ir_idx++) {
auto& ir = env->code().at(ir_idx);
auto i_rec = m_gen.add_ir(f_rec);
auto& bonus = allocs.stack_ops.at(ir_idx);
for (auto& op : bonus.ops) {
if (op.load) {
assert(false);
}
}
ir->do_codegen(&m_gen, allocs, i_rec);
for (auto& op : bonus.ops) {
if (op.store) {
assert(false);
}
}
}
// EPILOGUE
if (manually_added_stack_offset || allocs.needs_aligned_stack_for_spills ||
env->needs_aligned_stack()) {
if (manually_added_stack_offset) {
m_gen.add_instr_no_ir(f_rec, IGen::add_gpr64_imm(RSP, manually_added_stack_offset));
}
if (bonus_push) {
assert(!manually_added_stack_offset);
m_gen.add_instr_no_ir(f_rec, IGen::pop_gpr64(ri.get_saved_gpr(0)));
}
}
for (int i = int(allocs.used_saved_regs.size()); i-- > 0;) {
auto& saved_reg = allocs.used_saved_regs.at(i);
if (saved_reg.is_gpr()) {
m_gen.add_instr_no_ir(f_rec, IGen::pop_gpr64(saved_reg));
}
}
for (int i = int(allocs.used_saved_regs.size()); i-- > 0;) {
auto& saved_reg = allocs.used_saved_regs.at(i);
if (saved_reg.is_xmm()) {
m_gen.add_instr_no_ir(f_rec, IGen::load128_xmm128_gpr64(saved_reg, RSP));
m_gen.add_instr_no_ir(f_rec, IGen::add_gpr64_imm8s(RSP, XMM_SIZE));
}
}
m_gen.add_instr_no_ir(f_rec, IGen::ret());
}
+13 -1
View File
@@ -3,6 +3,18 @@
#ifndef JAK_CODEGENERATOR_H
#define JAK_CODEGENERATOR_H
class CodeGenerator {};
#include "Env.h"
#include "goalc/emitter/ObjectGenerator.h"
class CodeGenerator {
public:
CodeGenerator(FileEnv* env);
std::vector<u8> run();
private:
void do_function(FunctionEnv* env);
emitter::ObjectGenerator m_gen;
FileEnv* m_fe;
};
#endif // JAK_CODEGENERATOR_H
+73 -13
View File
@@ -1,6 +1,9 @@
#include "Compiler.h"
#include "goalc/logger/Logger.h"
#include "common/link_types.h"
#include "IR.h"
#include "goalc/regalloc/allocate.h"
#include "unistd.h"
using namespace goos;
@@ -14,6 +17,7 @@ Compiler::Compiler() {
}
void Compiler::execute_repl() {
m_listener.connect_to_target();
while (!m_want_exit) {
try {
// 1). get a line from the user (READ)
@@ -29,19 +33,19 @@ void Compiler::execute_repl() {
auto obj_file = compile_object_file("repl", code, m_listener.is_connected());
obj_file->debug_print_tl();
// // 3). color
// color_object_file(obj_file);
//
// // 4). codegen
// auto data = codegen_object_file(obj_file);
//
// // 4). send!
// if(m_listener.is_connected()) {
// m_listener.send_code(data);
// if(!m_listener.most_recent_send_was_acked()) {
// gLogger.log(MSG_ERR, "Runtime is not responding. Did it crash?\n");
// }
// }
// 3). color
color_object_file(obj_file);
// 4). codegen
auto data = codegen_object_file(obj_file);
// 4). send!
if (m_listener.is_connected()) {
m_listener.send_code(data);
if (!m_listener.most_recent_send_was_acked()) {
gLogger.log(MSG_ERR, "Runtime is not responding. Did it crash?\n");
}
}
} catch (std::exception& e) {
gLogger.log(MSG_WARN, "REPL Error: %s\n", e.what());
@@ -126,3 +130,59 @@ void Compiler::ice(const std::string& error) {
gLogger.log(MSG_ICE, "[ICE] %s\n", error.c_str());
throw std::runtime_error("ICE");
}
void Compiler::color_object_file(FileEnv* env) {
for (auto& f : env->functions()) {
AllocationInput input;
for (auto& i : f->code()) {
input.instructions.push_back(i->to_rai());
input.debug_instruction_names.push_back(i->print());
}
input.max_vars = f->max_vars();
input.constraints = f->constraints();
// for now...
input.debug_settings.print_input = true;
input.debug_settings.print_result = true;
input.debug_settings.print_analysis = true;
f->set_allocations(allocate_registers(input));
}
}
std::vector<u8> Compiler::codegen_object_file(FileEnv* env) {
CodeGenerator gen(env);
return gen.run();
}
std::vector<std::string> Compiler::run_test(const std::string& source_code) {
if (!m_listener.is_connected()) {
for (int i = 0; i < 1000; i++) {
m_listener.connect_to_target();
usleep(10000);
if (m_listener.is_connected()) {
break;
}
}
if (!m_listener.is_connected()) {
throw std::runtime_error("Compiler::run_test couldn't connect!");
}
}
auto code = m_goos.reader.read_from_file(source_code);
auto compiled = compile_object_file("test-code", code, true);
color_object_file(compiled);
auto data = codegen_object_file(compiled);
m_listener.record_messages(ListenerMessageKind::MSG_PRINT);
m_listener.send_code(data);
if (!m_listener.most_recent_send_was_acked()) {
gLogger.log(MSG_ERR, "Runtime is not responding after sending test code. Did it crash?\n");
}
return m_listener.stop_recording_messages();
}
void Compiler::shutdown_target() {
if (m_listener.is_connected()) {
m_listener.send_reset(true);
}
}
+8 -2
View File
@@ -5,6 +5,7 @@
#include "Env.h"
#include "goalc/listener/Listener.h"
#include "goalc/goos/Interpreter.h"
#include "goalc/compiler/IR.h"
class Compiler {
public:
@@ -21,14 +22,19 @@ class Compiler {
void ice(const std::string& err);
None* get_none() { return m_none.get(); }
std::vector<std::string> run_test(const std::string& source_code);
void shutdown_target();
private:
void init_logger();
Val* compile_pair(const goos::Object& code, Env* env);
Val* compile_integer(const goos::Object& code, Env* env);
Val* compile_integer(s64 value, Env* env);
void color_object_file(FileEnv* env);
std::vector<u8> codegen_object_file(FileEnv* env);
void for_each_in_list(const goos::Object& list, const std::function<void (const goos::Object&)>& f);
void for_each_in_list(const goos::Object& list,
const std::function<void(const goos::Object&)>& f);
goos::Arguments get_va(const goos::Object& form, const goos::Object& rest);
void va_check(
+9 -7
View File
@@ -1,5 +1,6 @@
#include <stdexcept>
#include "Env.h"
#include "IR.h"
///////////////////
// Env
@@ -93,12 +94,12 @@ Val* GlobalEnv::lexical_lookup(goos::Object sym) {
return nullptr;
}
BlockEnv * GlobalEnv::find_block(const std::string& name) {
BlockEnv* GlobalEnv::find_block(const std::string& name) {
(void)name;
return nullptr;
}
FileEnv * GlobalEnv::add_file(std::string name) {
FileEnv* GlobalEnv::add_file(std::string name) {
m_files.push_back(std::make_unique<FileEnv>(this, std::move(name)));
return m_files.back().get();
}
@@ -142,15 +143,15 @@ void FileEnv::add_top_level_function(std::unique_ptr<FunctionEnv> fe) {
m_top_level_func = m_functions.back().get();
}
NoEmitEnv * FileEnv::add_no_emit_env() {
NoEmitEnv* FileEnv::add_no_emit_env() {
assert(!m_no_emit_env);
m_no_emit_env = std::make_unique<NoEmitEnv>(this);
return m_no_emit_env.get();
}
void FileEnv::debug_print_tl() {
if(m_top_level_func) {
for(auto& code : m_top_level_func->code()) {
if (m_top_level_func) {
for (auto& code : m_top_level_func->code()) {
fmt::print("{}\n", code->print());
}
} else {
@@ -162,7 +163,8 @@ void FileEnv::debug_print_tl() {
// FunctionEnv
///////////////////
FunctionEnv::FunctionEnv(Env* parent, std::string name) : DeclareEnv(parent), m_name(std::move(name)){}
FunctionEnv::FunctionEnv(Env* parent, std::string name)
: DeclareEnv(parent), m_name(std::move(name)) {}
std::string FunctionEnv::print() {
return "function-" + m_name;
@@ -176,7 +178,7 @@ void FunctionEnv::finish() {
// todo resolve gotos
}
RegVal * FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) {
RegVal* FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) {
IRegister ireg;
ireg.kind = kind;
ireg.id = m_iregs.size();
+21 -14
View File
@@ -11,13 +11,15 @@
#include <memory>
#include <vector>
#include "common/type_system/TypeSpec.h"
#include "goalc/regalloc/allocate.h"
#include "goalc/goos/Object.h"
#include "IR.h"
//#include "IR.h"
#include "Label.h"
#include "Val.h"
class FileEnv;
class BlockEnv;
class IR;
/*!
* Parent class for Env's
@@ -84,6 +86,7 @@ class FileEnv : public Env {
void add_top_level_function(std::unique_ptr<FunctionEnv> fe);
NoEmitEnv* add_no_emit_env();
void debug_print_tl();
const std::vector<std::unique_ptr<FunctionEnv>>& functions() { return m_functions; }
// todo - is_empty
~FileEnv() = default;
@@ -118,11 +121,18 @@ class FunctionEnv : public DeclareEnv {
public:
FunctionEnv(Env* parent, std::string name);
std::string print() override;
void set_segment(int seg) { m_segment = seg; }
void set_segment(int seg) { segment = seg; }
void emit(std::unique_ptr<IR> ir) override;
void finish();
RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind) override;
const std::vector<std::unique_ptr<IR>>& code() { return m_code; }
int max_vars() const { return m_iregs.size(); }
const std::vector<IRegConstraint>& constraints() { return m_constraints; }
void set_allocations(const AllocationResult& result) { m_regalloc_result = result; }
const AllocationResult& alloc_result() { return m_regalloc_result; }
bool needs_aligned_stack() const { return m_aligned_stack_required; }
template <typename T, class... Args>
T* alloc_val(Args&&... args) {
@@ -130,7 +140,7 @@ class FunctionEnv : public DeclareEnv {
m_vals.push_back(std::move(new_obj));
return (T*)m_vals.back().get();
}
int segment = -1;
protected:
std::string m_name;
@@ -144,7 +154,6 @@ class FunctionEnv : public DeclareEnv {
std::string m_method_of_type_name = "#f";
bool m_aligned_stack_required = false;
int m_segment = -1;
std::unordered_map<std::string, Env*> m_params;
};
@@ -154,6 +163,7 @@ class BlockEnv : public Env {
BlockEnv(Env* parent, std::string name);
std::string print() override;
BlockEnv* find_block(const std::string& name) override;
protected:
std::string m_name;
Label* m_end_label = nullptr;
@@ -173,20 +183,17 @@ class LabelEnv : public Env {
std::unordered_map<std::string, Label> m_labels;
};
class WithInlineEnv : public Env {
class WithInlineEnv : public Env {};
};
class SymbolMacroEnv : public Env {};
class SymbolMacroEnv : public Env {
};
template<typename T>
template <typename T>
T* get_parent_env_of_type(Env* in) {
for(;;) {
for (;;) {
auto attempt = dynamic_cast<T*>(in);
if(attempt) return attempt;
if(dynamic_cast<GlobalEnv*>(in)) {
if (attempt)
return attempt;
if (dynamic_cast<GlobalEnv*>(in)) {
return nullptr;
}
in = in->parent();
+36 -5
View File
@@ -1,6 +1,18 @@
#include "IR.h"
#include "goalc/emitter/IGen.h"
IR_Return::IR_Return(const RegVal* return_reg, const RegVal* value) : m_return_reg(return_reg), m_value(value) {}
using namespace emitter;
namespace {
Register get_reg(const RegVal* rv, const AllocationResult& allocs, emitter::IR_Record irec) {
auto& ass = allocs.ass_as_ranges.at(rv->ireg().id).get(irec.ir_id);
assert(ass.kind == Assignment::Kind::REGISTER);
return ass.reg;
}
} // namespace
IR_Return::IR_Return(const RegVal* return_reg, const RegVal* value)
: m_return_reg(return_reg), m_value(value) {}
std::string IR_Return::print() {
return fmt::format("ret {} {}", m_return_reg->print(), m_value->print());
}
@@ -9,15 +21,15 @@ RegAllocInstr IR_Return::to_rai() {
RegAllocInstr rai;
rai.write.push_back(m_return_reg->ireg());
rai.read.push_back(m_value->ireg());
if(m_value->ireg().kind == m_return_reg->ireg().kind) {
rai.is_move = true; // only true if we aren't moving from register kind to register kind
if (m_value->ireg().kind == m_return_reg->ireg().kind) {
rai.is_move = true; // only true if we aren't moving from register kind to register kind
}
return rai;
}
void IR_Return::add_constraints(std::vector<IRegConstraint>* constraints, int my_id) {
IRegConstraint c;
if(dynamic_cast<const None*>(m_return_reg)){
if (dynamic_cast<const None*>(m_return_reg)) {
return;
}
@@ -27,10 +39,22 @@ void IR_Return::add_constraints(std::vector<IRegConstraint>* constraints, int my
constraints->push_back(c);
}
IR_LoadConstant64::IR_LoadConstant64(const RegVal* dest, u64 value) : m_dest(dest), m_value(value) {
void IR_Return::do_codegen(emitter::ObjectGenerator* gen,
const AllocationResult& allocs,
emitter::IR_Record irec) {
auto val_reg = get_reg(m_value, allocs, irec);
auto dest_reg = get_reg(m_return_reg, allocs, irec);
if (val_reg == dest_reg) {
gen->add_instr(IGen::null(), irec);
} else {
gen->add_instr(IGen::mov_gpr64_gpr64(dest_reg, val_reg), irec);
}
}
IR_LoadConstant64::IR_LoadConstant64(const RegVal* dest, u64 value)
: m_dest(dest), m_value(value) {}
std::string IR_LoadConstant64::print() {
return fmt::format("mov-ic {}, {}", m_dest->print(), m_value);
}
@@ -39,4 +63,11 @@ RegAllocInstr IR_LoadConstant64::to_rai() {
RegAllocInstr rai;
rai.write.push_back(m_dest->ireg());
return rai;
}
void IR_LoadConstant64::do_codegen(emitter::ObjectGenerator* gen,
const AllocationResult& allocs,
emitter::IR_Record irec) {
auto dest_reg = get_reg(m_dest, allocs, irec);
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, m_value), irec);
}
+10 -3
View File
@@ -5,12 +5,15 @@
#include "CodeGenerator.h"
#include "goalc/regalloc/allocate.h"
#include "Val.h"
#include "goalc/emitter/ObjectGenerator.h"
class IR {
public:
virtual std::string print() = 0;
virtual RegAllocInstr to_rai() = 0;
// virtual void do_codegen(CodeGenerator* gen) = 0;
virtual void do_codegen(emitter::ObjectGenerator* gen,
const AllocationResult& allocs,
emitter::IR_Record irec) = 0;
virtual void add_constraints(std::vector<IRegConstraint>* constraints, int my_id) {
(void)constraints;
(void)my_id;
@@ -29,7 +32,9 @@ class IR_Return : public IR {
std::string print() override;
RegAllocInstr to_rai() override;
void add_constraints(std::vector<IRegConstraint>* constraints, int my_id) override;
// void do_codegen(CodeGenerator* gen) override;
void do_codegen(emitter::ObjectGenerator* gen,
const AllocationResult& allocs,
emitter::IR_Record irec) override;
const RegVal* value() { return m_value; }
protected:
@@ -42,7 +47,9 @@ class IR_LoadConstant64 : public IR {
IR_LoadConstant64(const RegVal* dest, u64 value);
std::string print() override;
RegAllocInstr to_rai() override;
// void do_codegen(CodeGenerator* gen) override;
void do_codegen(emitter::ObjectGenerator* gen,
const AllocationResult& allocs,
emitter::IR_Record irec) override;
protected:
const RegVal* m_dest = nullptr;
+1 -3
View File
@@ -3,8 +3,6 @@
#ifndef JAK_LABEL_H
#define JAK_LABEL_H
struct Label {
};
struct Label {};
#endif // JAK_LABEL_H
+5 -3
View File
@@ -1,4 +1,5 @@
#include "goalc/compiler/Compiler.h"
#include "goalc/compiler/IR.h"
goos::Arguments Compiler::get_va(const goos::Object& form, const goos::Object& rest) {
goos::Arguments args;
@@ -82,15 +83,16 @@ void Compiler::va_check(
}
}
void Compiler::for_each_in_list(const goos::Object& list, const std::function<void(const goos::Object&)>& f) {
void Compiler::for_each_in_list(const goos::Object& list,
const std::function<void(const goos::Object&)>& f) {
const goos::Object* iter = &list;
while(iter->is_pair()) {
while (iter->is_pair()) {
auto lap = iter->as_pair();
f(lap->car);
iter = &lap->cdr;
}
if(!iter->is_empty_list()) {
if (!iter->is_empty_list()) {
throw_compile_error(list, "invalid list in for_each_in_list");
}
}
+9 -9
View File
@@ -1,12 +1,13 @@
#include "Val.h"
#include "Env.h"
#include "IR.h"
/*!
* Fallback to_gpr if a more optimized one is not provided.
*/
const RegVal* Val::to_gpr(FunctionEnv* fe) const {
auto rv = to_reg(fe);
if(rv->ireg().kind == emitter::RegKind::GPR) {
if (rv->ireg().kind == emitter::RegKind::GPR) {
return rv;
} else {
throw std::runtime_error("Val::to_gpr NYI"); // todo
@@ -21,31 +22,30 @@ const RegVal* Val::to_xmm(FunctionEnv* fe) const {
throw std::runtime_error("Val::to_xmm NYI"); // todo
}
const RegVal* RegVal::to_reg(FunctionEnv* fe) const {
(void)fe;
return this;
}
const RegVal * RegVal::to_gpr(FunctionEnv* fe) const {
const RegVal* RegVal::to_gpr(FunctionEnv* fe) const {
(void)fe;
if(m_ireg.kind == emitter::RegKind::GPR) {
if (m_ireg.kind == emitter::RegKind::GPR) {
return this;
} else {
throw std::runtime_error("RegVal::to_gpr NYI"); // todo
throw std::runtime_error("RegVal::to_gpr NYI"); // todo
}
}
const RegVal * RegVal::to_xmm(FunctionEnv* fe) const {
const RegVal* RegVal::to_xmm(FunctionEnv* fe) const {
(void)fe;
if(m_ireg.kind == emitter::RegKind::XMM) {
if (m_ireg.kind == emitter::RegKind::XMM) {
return this;
} else {
throw std::runtime_error("RegVal::to_xmm NYI"); // todo
throw std::runtime_error("RegVal::to_xmm NYI"); // todo
}
}
const RegVal * IntegerConstantVal::to_reg(FunctionEnv* fe) const {
const RegVal* IntegerConstantVal::to_reg(FunctionEnv* fe) const {
auto rv = fe->make_gpr(m_ts);
fe->emit(std::make_unique<IR_LoadConstant64>(rv, m_value));
return rv;
+136 -135
View File
@@ -1,139 +1,141 @@
#include "goalc/compiler/Compiler.h"
#include "goalc/compiler/IR.h"
/*!
* Main table for compiler forms
*/
static const std::unordered_map<std::string,
Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)> goal_forms =
{
// // inline asm
// {".ret", &Compiler::compile_asm},
// {".push", &Compiler::compile_asm},
// {".pop", &Compiler::compile_asm},
// {".jmp", &Compiler::compile_asm},
// {".sub", &Compiler::compile_asm},
// {".ret-reg", &Compiler::compile_asm},
//
// // BLOCK FORMS
static const std::unordered_map<
std::string,
Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)>
goal_forms = {
// // inline asm
// {".ret", &Compiler::compile_asm},
// {".push", &Compiler::compile_asm},
// {".pop", &Compiler::compile_asm},
// {".jmp", &Compiler::compile_asm},
// {".sub", &Compiler::compile_asm},
// {".ret-reg", &Compiler::compile_asm},
//
// // BLOCK FORMS
{"top-level", &Compiler::compile_top_level},
{"begin", &Compiler::compile_begin},
// {"block", &Compiler::compile_block},
// {"return-from", &Compiler::compile_return_from},
// {"label", &Compiler::compile_label},
// {"goto", &Compiler::compile_goto},
//
// // COMPILER CONTROL
// {"gs", &Compiler::compile_gs},
// {"block", &Compiler::compile_block},
// {"return-from", &Compiler::compile_return_from},
// {"label", &Compiler::compile_label},
// {"goto", &Compiler::compile_goto},
//
// // COMPILER CONTROL
// {"gs", &Compiler::compile_gs},
{":exit", &Compiler::compile_exit}
// {"asm-file", &Compiler::compile_asm_file},
// {"test", &Compiler::compile_test},
// {"in-package", &Compiler::compile_in_package},
//
// // CONDITIONAL COMPILATION
// {"#cond", &Compiler::compile_gscond},
// {"defglobalconstant", &Compiler::compile_defglobalconstant},
// {"seval", &Compiler::compile_seval},
//
// // CONTROL FLOW
// {"cond", &Compiler::compile_cond},
// {"when-goto", &Compiler::compile_when_goto},
//
// // DEFINITION
// {"define", &Compiler::compile_define},
// {"define-extern", &Compiler::compile_define_extern},
// {"set!", &Compiler::compile_set},
// {"defun-extern", &Compiler::compile_defun_extern},
// {"declare-method", &Compiler::compile_declare_method},
//
// // DEFTYPE
// {"deftype", &Compiler::compile_deftype},
//
// // ENUM
// {"defenum", &Compiler::compile_defenum},
//
// // Field Access
// {"->", &Compiler::compile_deref},
// {"&", &Compiler::compile_addr_of},
//
//
// // LAMBDA
// {"lambda", &Compiler::compile_lambda},
// {"inline", &Compiler::compile_inline},
// {"with-inline", &Compiler::compile_with_inline},
// {"rlet", &Compiler::compile_rlet},
// {"mlet", &Compiler::compile_mlet},
// {"get-ra-ptr", &Compiler::compile_get_ra_ptr},
//
//
//
// // MACRO
// {"print-type", &Compiler::compile_print_type},
// {"quote", &Compiler::compile_quote},
// {"defconstant", &Compiler::compile_defconstant},
//
// {"declare", &Compiler::compile_declare},
//
//
//
//
// // OBJECT
//
// {"the", &Compiler::compile_the},
// {"the-as", &Compiler::compile_the_as},
//
// {"defmethod", &Compiler::compile_defmethod},
//
// {"current-method-type", &Compiler::compile_current_method_type},
// {"new", &Compiler::compile_new},
// {"method", &Compiler::compile_method},
//
// // PAIR
// {"car", &Compiler::compile_car},
// {"cdr", &Compiler::compile_cdr},
//
// // IT IS MATH
// {"+", &Compiler::compile_add},
// {"-", &Compiler::compile_sub},
// {"*", &Compiler::compile_mult},
// {"/", &Compiler::compile_divide},
// {"shlv", &Compiler::compile_shlv},
// {"shrv", &Compiler::compile_shrv},
// {"sarv", &Compiler::compile_sarv},
// {"shl", &Compiler::compile_shl},
// {"shr", &Compiler::compile_shr},
// {"sar", &Compiler::compile_sar},
// {"mod", &Compiler::compile_mod},
// {"logior", &Compiler::compile_logior},
// {"logxor", &Compiler::compile_logxor},
// {"logand", &Compiler::compile_logand},
// {"lognot", &Compiler::compile_lognot},
// {"=", &Compiler::compile_condition_as_bool},
// {"!=", &Compiler::compile_condition_as_bool},
// {"eq?", &Compiler::compile_condition_as_bool},
// {"not", &Compiler::compile_condition_as_bool},
// {"<=", &Compiler::compile_condition_as_bool},
// {">=", &Compiler::compile_condition_as_bool},
// {"<", &Compiler::compile_condition_as_bool},
// {">", &Compiler::compile_condition_as_bool},
//
// // BUILDER
// {"builder", &Compiler::compile_builder},
//
// // UTIL
// {"set-config!", &Compiler::compile_set_config},
//
//
//
// {"listen-to-target", &Compiler::compile_listen_to_target},
// {"reset-target", &Compiler::compile_reset_target},
// {":status", &Compiler::compile_poke},
//
// // temporary testing hacks...
// {"send-test", &Compiler::compile_send_test_data},
};
// {"asm-file", &Compiler::compile_asm_file},
// {"test", &Compiler::compile_test},
// {"in-package", &Compiler::compile_in_package},
//
// // CONDITIONAL COMPILATION
// {"#cond", &Compiler::compile_gscond},
// {"defglobalconstant", &Compiler::compile_defglobalconstant},
// {"seval", &Compiler::compile_seval},
//
// // CONTROL FLOW
// {"cond", &Compiler::compile_cond},
// {"when-goto", &Compiler::compile_when_goto},
//
// // DEFINITION
// {"define", &Compiler::compile_define},
// {"define-extern", &Compiler::compile_define_extern},
// {"set!", &Compiler::compile_set},
// {"defun-extern", &Compiler::compile_defun_extern},
// {"declare-method", &Compiler::compile_declare_method},
//
// // DEFTYPE
// {"deftype", &Compiler::compile_deftype},
//
// // ENUM
// {"defenum", &Compiler::compile_defenum},
//
// // Field Access
// {"->", &Compiler::compile_deref},
// {"&", &Compiler::compile_addr_of},
//
//
// // LAMBDA
// {"lambda", &Compiler::compile_lambda},
// {"inline", &Compiler::compile_inline},
// {"with-inline", &Compiler::compile_with_inline},
// {"rlet", &Compiler::compile_rlet},
// {"mlet", &Compiler::compile_mlet},
// {"get-ra-ptr", &Compiler::compile_get_ra_ptr},
//
//
//
// // MACRO
// {"print-type", &Compiler::compile_print_type},
// {"quote", &Compiler::compile_quote},
// {"defconstant", &Compiler::compile_defconstant},
//
// {"declare", &Compiler::compile_declare},
//
//
//
//
// // OBJECT
//
// {"the", &Compiler::compile_the},
// {"the-as", &Compiler::compile_the_as},
//
// {"defmethod", &Compiler::compile_defmethod},
//
// {"current-method-type", &Compiler::compile_current_method_type},
// {"new", &Compiler::compile_new},
// {"method", &Compiler::compile_method},
//
// // PAIR
// {"car", &Compiler::compile_car},
// {"cdr", &Compiler::compile_cdr},
//
// // IT IS MATH
// {"+", &Compiler::compile_add},
// {"-", &Compiler::compile_sub},
// {"*", &Compiler::compile_mult},
// {"/", &Compiler::compile_divide},
// {"shlv", &Compiler::compile_shlv},
// {"shrv", &Compiler::compile_shrv},
// {"sarv", &Compiler::compile_sarv},
// {"shl", &Compiler::compile_shl},
// {"shr", &Compiler::compile_shr},
// {"sar", &Compiler::compile_sar},
// {"mod", &Compiler::compile_mod},
// {"logior", &Compiler::compile_logior},
// {"logxor", &Compiler::compile_logxor},
// {"logand", &Compiler::compile_logand},
// {"lognot", &Compiler::compile_lognot},
// {"=", &Compiler::compile_condition_as_bool},
// {"!=", &Compiler::compile_condition_as_bool},
// {"eq?", &Compiler::compile_condition_as_bool},
// {"not", &Compiler::compile_condition_as_bool},
// {"<=", &Compiler::compile_condition_as_bool},
// {">=", &Compiler::compile_condition_as_bool},
// {"<", &Compiler::compile_condition_as_bool},
// {">", &Compiler::compile_condition_as_bool},
//
// // BUILDER
// {"builder", &Compiler::compile_builder},
//
// // UTIL
// {"set-config!", &Compiler::compile_set_config},
//
//
//
// {"listen-to-target", &Compiler::compile_listen_to_target},
// {"reset-target", &Compiler::compile_reset_target},
// {":status", &Compiler::compile_poke},
//
// // temporary testing hacks...
// {"send-test", &Compiler::compile_send_test_data},
};
Val * Compiler::compile(const goos::Object& code, Env* env) {
switch(code.type) {
Val* Compiler::compile(const goos::Object& code, Env* env) {
switch (code.type) {
case goos::ObjectType::PAIR:
return compile_pair(code, env);
case goos::ObjectType::INTEGER:
@@ -144,34 +146,33 @@ Val * Compiler::compile(const goos::Object& code, Env* env) {
return get_none();
}
Val * Compiler::compile_pair(const goos::Object& code, Env* env) {
Val* Compiler::compile_pair(const goos::Object& code, Env* env) {
auto pair = code.as_pair();
auto head = pair->car;
auto rest = pair->cdr;
if(head.is_symbol()) {
if (head.is_symbol()) {
auto head_sym = head.as_symbol();
// first try as a goal compiler form
auto kv_gfs = goal_forms.find(head_sym->name);
if(kv_gfs != goal_forms.end()) {
if (kv_gfs != goal_forms.end()) {
return ((*this).*(kv_gfs->second))(code, rest, env);
}
// todo macro
// todo enum
}
// todo function or method call
ice("unhandled compile_pair on " + code.print());
return nullptr;
}
Val * Compiler::compile_integer(const goos::Object& code, Env* env) {
Val* Compiler::compile_integer(const goos::Object& code, Env* env) {
return compile_integer(code.integer_obj.value, env);
}
Val * Compiler::compile_integer(s64 value, Env* env) {
Val* Compiler::compile_integer(s64 value, Env* env) {
auto fe = get_parent_env_of_type<FunctionEnv>(env);
return fe->alloc_val<IntegerConstantVal>(m_ts.make_typespec("int"), value);
}
+4 -5
View File
@@ -1,16 +1,15 @@
#include "goalc/compiler/Compiler.h"
#include "goalc/compiler/IR.h"
using namespace goos;
Val * Compiler::compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env) {
Val* Compiler::compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env) {
return compile_begin(form, rest, env);
}
Val * Compiler::compile_begin(const goos::Object& form, const goos::Object& rest, Env* env) {
Val* Compiler::compile_begin(const goos::Object& form, const goos::Object& rest, Env* env) {
(void)form;
Val* result = get_none();
for_each_in_list(rest, [&](const Object& o){
result = compile_error_guard(o, env);
});
for_each_in_list(rest, [&](const Object& o) { result = compile_error_guard(o, env); });
return result;
}
@@ -1,11 +1,12 @@
#include "goalc/compiler/Compiler.h"
#include "goalc/compiler/IR.h"
Val * Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, Env* env) {
Val* Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, Env* env) {
(void)env;
auto args = get_va(form, rest);
va_check(form, args, {}, {});
if(m_listener.is_connected()) {
m_listener.send_reset();
if (m_listener.is_connected()) {
m_listener.send_reset(false);
}
m_want_exit = true;
return get_none();
+6 -1
View File
@@ -156,6 +156,11 @@ InstructionRecord ObjectGenerator::add_instr(Instruction inst, IR_Record ir) {
return rec;
}
void ObjectGenerator::add_instr_no_ir(FunctionRecord func, Instruction inst) {
assert(func.func_id == int(m_function_data_by_seg.at(func.seg).size()) - 1);
m_function_data_by_seg.at(func.seg).at(func.func_id).instructions.push_back(inst);
}
/*!
* Create a new static object in the given segment.
*/
@@ -461,7 +466,7 @@ std::vector<u8> ObjectGenerator::generate_header_v3() {
offset += push_data<u32>(N_SEG, result);
offset += sizeof(u32) * N_SEG * 4; // 4 u32's per segment
offset += 4;
struct SizeOffset {
uint32_t offset, size;
};
+1
View File
@@ -44,6 +44,7 @@ class ObjectGenerator {
IR_Record add_ir(const FunctionRecord& func);
IR_Record get_future_ir_record(const FunctionRecord& func, int ir_id);
InstructionRecord add_instr(Instruction inst, IR_Record ir);
void add_instr_no_ir(FunctionRecord func, Instruction inst);
StaticRecord add_static_to_seg(int seg, int min_align = 16);
void link_instruction_jump(InstructionRecord jump_instr, IR_Record destination);
void link_static_type_ptr(StaticRecord rec, int offset, const std::string& type_name);
+31 -17
View File
@@ -18,7 +18,7 @@
#include "common/versions.h"
using namespace versions;
constexpr bool debug_listener = true;
constexpr bool debug_listener = false;
namespace listener {
Listener::Listener() {
@@ -245,10 +245,24 @@ void Listener::receive_func() {
}
}
void Listener::send_code(std::vector<uint8_t> &code) {
void Listener::record_messages(ListenerMessageKind kind) {
if (filter != ListenerMessageKind::MSG_INVALID) {
printf("[Listener] Already recording!\n");
}
filter = kind;
}
std::vector<std::string> Listener::stop_recording_messages() {
filter = ListenerMessageKind::MSG_INVALID;
auto result = message_record;
message_record.clear();
return result;
}
void Listener::send_code(std::vector<uint8_t>& code) {
got_ack = false;
int total_size = code.size() + sizeof(ListenerMessageHeader);
if(total_size > BUFFER_SIZE) {
if (total_size > BUFFER_SIZE) {
printf("[ERROR] Listener send_code got too big of a message\n");
return;
}
@@ -257,7 +271,7 @@ void Listener::send_code(std::vector<uint8_t> &code) {
auto* buffer_data = (char*)(header + 1);
header->deci2_header.rsvd = 0;
header->deci2_header.len = total_size;
header->deci2_header.proto = 0xe042; // todo don't hardcode
header->deci2_header.proto = 0xe042; // todo don't hardcode
header->deci2_header.src = 'H';
header->deci2_header.dst = 'E';
header->msg_size = code.size();
@@ -268,21 +282,21 @@ void Listener::send_code(std::vector<uint8_t> &code) {
send_buffer(total_size);
}
void Listener::send_reset() {
if(!m_connected) {
void Listener::send_reset(bool shutdown) {
if (!m_connected) {
printf("Not connected, so cannot reset target.\n");
return;
}
auto* header = (ListenerMessageHeader*)m_buffer;
header->deci2_header.rsvd = 0;
header->deci2_header.len = sizeof(ListenerMessageHeader);
header->deci2_header.proto = 0xe042; // todo don't hardcode
header->deci2_header.proto = 0xe042; // todo don't hardcode
header->deci2_header.src = 'H';
header->deci2_header.dst = 'E';
header->msg_size = 0;
header->ltt_msg_kind = LTT_MSG_RESET;
header->u6 = 0;
header->u8 = 0;
header->u8 = shutdown ? UINT64_MAX : 0;
send_buffer(sizeof(ListenerMessageHeader));
disconnect();
close(socket_fd);
@@ -292,25 +306,24 @@ void Listener::send_reset() {
void Listener::send_buffer(int sz) {
int wrote = 0;
if(debug_listener) {
if (debug_listener) {
printf("[L -> T] sending %d bytes...\n", sz);
}
got_ack = false;
waiting_for_ack = true;
while(wrote < sz) {
while (wrote < sz) {
auto to_send = std::min(512, sz - wrote);
auto x = write(socket_fd, m_buffer + wrote, to_send);
wrote += x;
}
if(debug_listener) {
if (debug_listener) {
printf(" waiting for ack...\n");
}
if(wait_for_ack()) {
if(debug_listener) {
if (wait_for_ack()) {
if (debug_listener) {
printf("ack buff:\n");
printf("%s\n", ack_recv_buff);
printf(" OK\n");
@@ -321,13 +334,14 @@ void Listener::send_buffer(int sz) {
}
bool Listener::wait_for_ack() {
if(!m_connected) {
if (!m_connected) {
printf("wait_for_ack called when not connected!\n");
return false;
}
for(int i = 0; i < 2000; i++) {
if(got_ack) return true;
for (int i = 0; i < 2000; i++) {
if (got_ack)
return true;
usleep(1000);
}
+4 -7
View File
@@ -21,20 +21,17 @@ class Listener {
~Listener();
bool connect_to_target(const std::string& ip = "127.0.0.1", int port = DECI2_PORT);
void record_messages(ListenerMessageKind kind);
void stop_recording_messages();
std::vector<std::string> stop_recording_messages();
bool is_connected() const;
void send_reset();
void send_reset(bool shutdown);
void disconnect();
void send_code(std::vector<uint8_t> &code);
bool most_recent_send_was_acked() {
return got_ack;
}
void send_code(std::vector<uint8_t>& code);
bool most_recent_send_was_acked() { return got_ack; }
private:
void send_buffer(int sz);
bool wait_for_ack();
char* m_buffer = nullptr; //! buffer for incoming messages
bool m_connected = false; //! do we think we are connected?
bool receive_thread_running = false; //! is the receive thread unjoined?
+1
View File
@@ -4,6 +4,7 @@
void Logger::close() {
if (fp) {
fclose(fp);
fp = nullptr;
}
}
+5 -2
View File
@@ -12,10 +12,12 @@ namespace {
* Print out the input data for debugging.
*/
void print_allocate_input(const AllocationInput& in) {
fmt::print("[RegAlloc] Debug Input:\n");
fmt::print("[RegAlloc] Debug Input Program:\n");
if (in.instructions.size() == in.debug_instruction_names.size()) {
for (size_t i = 0; i < in.instructions.size(); i++) {
fmt::print(" [{:3d}] {:30} -> {:30}\n", in.debug_instruction_names.at(i),
// fmt::print(" [{}] {} -> {}\n", in.debug_instruction_names.at(i),
// in.instructions.at(i).print());
fmt::print(" [{:3d}] {:30} -> {:30}\n", i, in.debug_instruction_names.at(i),
in.instructions.at(i).print());
}
} else {
@@ -23,6 +25,7 @@ void print_allocate_input(const AllocationInput& in) {
fmt::print(" [{:3d}] {}\n", instruction.print());
}
}
fmt::print("[RegAlloc] Debug Input Constraints:\n");
for (const auto& c : in.constraints) {
fmt::print(" {}\n", c.to_string());
}
+1
View File
@@ -14,6 +14,7 @@ add_executable(goalc-test
test_emitter_loads_and_store.cpp
test_emitter_xmm32.cpp
test_emitter_integer_math.cpp
test_compiler_and_runtime.cpp
)
IF (WIN32)
+109
View File
@@ -0,0 +1,109 @@
#include <thread>
#include "gtest/gtest.h"
#include "game/runtime.h"
#include "goalc/listener/Listener.h"
#include "goalc/compiler/Compiler.h"
TEST(CompilerAndRuntime, ConstructCompiler) {
Compiler compiler;
}
TEST(CompilerAndRuntime, StartRuntime) {
std::thread runtime_thread([]() { exec_runtime(0, nullptr); });
listener::Listener listener;
while (!listener.is_connected()) {
listener.connect_to_target();
usleep(1000);
}
listener.send_reset(true);
runtime_thread.join();
}
TEST(CompilerAndRuntime, SendProgram) {
std::thread runtime_thread([]() { exec_runtime(0, nullptr); });
Compiler compiler;
compiler.run_test("goal_src/test/test-return-integer-1.gc");
compiler.shutdown_target();
runtime_thread.join();
}
namespace {
std::string escaped_string(const std::string& in) {
std::string result;
for (auto x : in) {
switch (x) {
case '\n':
result.append("\\n");
break;
case '\t':
result.append("\\t");
break;
default:
result.push_back(x);
}
}
return result;
}
struct CompilerTestRunner {
Compiler* c = nullptr;
struct Test {
std::vector<std::string> expected, actual;
std::string test_name;
};
std::vector<Test> tests;
void run_test(const std::string& test_file, const std::vector<std::string>& expected) {
auto result = c->run_test("goal_src/test/" + test_file);
EXPECT_EQ(result, expected);
tests.push_back({expected, result, test_file});
}
void print_summary() {
fmt::print("~~ Compiler Test Summary for {} tests... ~~\n", tests.size());
int passed = 0;
for (auto& test : tests) {
if (test.expected == test.actual) {
fmt::print("[{:30}] PASS!\n", test.test_name);
passed++;
} else {
fmt::print("[{:30}] FAIL!\n", test.test_name);
fmt::print("expected:\n");
for (auto& x : test.expected) {
fmt::print(" \"{}\"\n", escaped_string(x));
}
fmt::print("result:\n");
for (auto& x : test.actual) {
fmt::print(" \"{}\"\n", escaped_string(x));
}
}
}
fmt::print("Total: passed {}/{} tests\n", passed, tests.size());
}
};
} // namespace
TEST(CompilerAndRuntime, CompilerTests) {
std::thread runtime_thread([]() { exec_runtime(0, nullptr); });
Compiler compiler;
CompilerTestRunner runner;
runner.c = &compiler;
runner.run_test("test-return-integer-1.gc", {"4886718345\n"});
runner.run_test("test-return-integer-2.gc", {"23\n"});
runner.run_test("test-return-integer-3.gc", {"-17\n"});
runner.run_test("test-return-integer-4.gc", {"-2147483648\n"});
runner.run_test("test-return-integer-5.gc", {"-2147483649\n"});
runner.run_test("test-return-integer-6.gc", {"0\n"});
runner.run_test("test-return-integer-7.gc", {"-123\n"});
compiler.shutdown_target();
runtime_thread.join();
runner.print_summary();
}