From d49b01e310f31b176629d61afc7ef8726eb86a6d Mon Sep 17 00:00:00 2001 From: water Date: Sun, 6 Sep 2020 16:58:25 -0400 Subject: [PATCH] working return integer tests as part of gtest --- CMakeLists.txt | 27 +- common/util/BinaryWriter.h | 26 +- game/kernel/kdgo.cpp | 4 +- game/kernel/klisten.cpp | 3 + game/main.cpp | 4 +- game/runtime.cpp | 5 +- game/runtime.h | 2 +- goal_src/test/test-return-integer-1.gc | 2 + goal_src/test/test-return-integer-2.gc | 1 + goal_src/test/test-return-integer-3.gc | 1 + goal_src/test/test-return-integer-4.gc | 1 + goal_src/test/test-return-integer-5.gc | 1 + goal_src/test/test-return-integer-6.gc | 1 + goal_src/test/test-return-integer-7.gc | 1 + goalc/compiler/CodeGenerator.cpp | 118 +++++++- goalc/compiler/CodeGenerator.h | 14 +- goalc/compiler/Compiler.cpp | 86 +++++- goalc/compiler/Compiler.h | 10 +- goalc/compiler/Env.cpp | 16 +- goalc/compiler/Env.h | 35 ++- goalc/compiler/IR.cpp | 41 ++- goalc/compiler/IR.h | 13 +- goalc/compiler/Label.h | 4 +- goalc/compiler/Util.cpp | 8 +- goalc/compiler/Val.cpp | 18 +- goalc/compiler/compilation/Atoms.cpp | 271 +++++++++--------- goalc/compiler/compilation/Block.cpp | 9 +- .../compiler/compilation/CompilerControl.cpp | 7 +- goalc/emitter/ObjectGenerator.cpp | 7 +- goalc/emitter/ObjectGenerator.h | 1 + goalc/listener/Listener.cpp | 48 ++-- goalc/listener/Listener.h | 11 +- goalc/logger/Logger.cpp | 1 + goalc/regalloc/allocate.cpp | 7 +- test/CMakeLists.txt | 1 + test/test_compiler_and_runtime.cpp | 109 +++++++ 36 files changed, 648 insertions(+), 266 deletions(-) create mode 100644 goal_src/test/test-return-integer-1.gc create mode 100644 goal_src/test/test-return-integer-2.gc create mode 100644 goal_src/test/test-return-integer-3.gc create mode 100644 goal_src/test/test-return-integer-4.gc create mode 100644 goal_src/test/test-return-integer-5.gc create mode 100644 goal_src/test/test-return-integer-6.gc create mode 100644 goal_src/test/test-return-integer-7.gc create mode 100644 test/test_compiler_and_runtime.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b40377dc4a..952c0f5722 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 () diff --git a/common/util/BinaryWriter.h b/common/util/BinaryWriter.h index fab9c5b19b..8140e1ac59 100644 --- a/common/util/BinaryWriter.h +++ b/common/util/BinaryWriter.h @@ -16,7 +16,7 @@ class BinaryWriter { public: BinaryWriter() = default; - template + template 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 + template 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); } diff --git a/game/kernel/kdgo.cpp b/game/kernel/kdgo.cpp index 6181c18bb2..f57fbedf29 100644 --- a/game/kernel/kdgo.cpp +++ b/game/kernel/kdgo.cpp @@ -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"); diff --git a/game/kernel/klisten.cpp b/game/kernel/klisten.cpp index 7f990bf27e..1c2d85d34e 100644 --- a/game/kernel/klisten.cpp +++ b/game/kernel/klisten.cpp @@ -141,6 +141,9 @@ void ProcessListenerMessage(Ptr 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"); diff --git a/game/main.cpp b/game/main.cpp index c0a1fc30fc..e6a1d3c257 100644 --- a/game/main.cpp +++ b/game/main.cpp @@ -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; } \ No newline at end of file diff --git a/game/runtime.cpp b/game/runtime.cpp index 379898a9a1..4fa7a585e2 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -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; } diff --git a/game/runtime.h b/game/runtime.h index b252447ada..f2121d2c4b 100644 --- a/game/runtime.h +++ b/game/runtime.h @@ -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 diff --git a/goal_src/test/test-return-integer-1.gc b/goal_src/test/test-return-integer-1.gc new file mode 100644 index 0000000000..6185b4d3b4 --- /dev/null +++ b/goal_src/test/test-return-integer-1.gc @@ -0,0 +1,2 @@ +; simply return an integer +#x123456789 \ No newline at end of file diff --git a/goal_src/test/test-return-integer-2.gc b/goal_src/test/test-return-integer-2.gc new file mode 100644 index 0000000000..254b43b371 --- /dev/null +++ b/goal_src/test/test-return-integer-2.gc @@ -0,0 +1 @@ +#x17 \ No newline at end of file diff --git a/goal_src/test/test-return-integer-3.gc b/goal_src/test/test-return-integer-3.gc new file mode 100644 index 0000000000..e85c88864c --- /dev/null +++ b/goal_src/test/test-return-integer-3.gc @@ -0,0 +1 @@ +-17 \ No newline at end of file diff --git a/goal_src/test/test-return-integer-4.gc b/goal_src/test/test-return-integer-4.gc new file mode 100644 index 0000000000..f6712382ee --- /dev/null +++ b/goal_src/test/test-return-integer-4.gc @@ -0,0 +1 @@ +-2147483648 \ No newline at end of file diff --git a/goal_src/test/test-return-integer-5.gc b/goal_src/test/test-return-integer-5.gc new file mode 100644 index 0000000000..0a0dce03c3 --- /dev/null +++ b/goal_src/test/test-return-integer-5.gc @@ -0,0 +1 @@ +-2147483649 \ No newline at end of file diff --git a/goal_src/test/test-return-integer-6.gc b/goal_src/test/test-return-integer-6.gc new file mode 100644 index 0000000000..c227083464 --- /dev/null +++ b/goal_src/test/test-return-integer-6.gc @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/goal_src/test/test-return-integer-7.gc b/goal_src/test/test-return-integer-7.gc new file mode 100644 index 0000000000..bf79039cdd --- /dev/null +++ b/goal_src/test/test-return-integer-7.gc @@ -0,0 +1 @@ +-123 \ No newline at end of file diff --git a/goalc/compiler/CodeGenerator.cpp b/goalc/compiler/CodeGenerator.cpp index 2f78d6224d..f196de9d40 100644 --- a/goalc/compiler/CodeGenerator.cpp +++ b/goalc/compiler/CodeGenerator.cpp @@ -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 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()); +} \ No newline at end of file diff --git a/goalc/compiler/CodeGenerator.h b/goalc/compiler/CodeGenerator.h index 6d1ca719f7..cbd1f4ee25 100644 --- a/goalc/compiler/CodeGenerator.h +++ b/goalc/compiler/CodeGenerator.h @@ -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 run(); + + private: + void do_function(FunctionEnv* env); + emitter::ObjectGenerator m_gen; + FileEnv* m_fe; +}; #endif // JAK_CODEGENERATOR_H diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 240ded2502..fe2106f437 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -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 Compiler::codegen_object_file(FileEnv* env) { + CodeGenerator gen(env); + return gen.run(); +} + +std::vector 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); + } +} \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index a491416a35..dc5f67f24d 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -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 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 codegen_object_file(FileEnv* env); - void for_each_in_list(const goos::Object& list, const std::function& f); - + void for_each_in_list(const goos::Object& list, + const std::function& f); goos::Arguments get_va(const goos::Object& form, const goos::Object& rest); void va_check( diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index dab3d75a76..e6a7b500b6 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -1,5 +1,6 @@ #include #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(this, std::move(name))); return m_files.back().get(); } @@ -142,15 +143,15 @@ void FileEnv::add_top_level_function(std::unique_ptr 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(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(); diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index bfef83ffc1..056c2aa3c1 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -11,13 +11,15 @@ #include #include #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 fe); NoEmitEnv* add_no_emit_env(); void debug_print_tl(); + const std::vector>& 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) override; void finish(); RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind) override; const std::vector>& code() { return m_code; } + int max_vars() const { return m_iregs.size(); } + const std::vector& 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 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 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 m_labels; }; -class WithInlineEnv : public Env { +class WithInlineEnv : public Env {}; -}; +class SymbolMacroEnv : public Env {}; -class SymbolMacroEnv : public Env { - -}; - -template +template T* get_parent_env_of_type(Env* in) { - for(;;) { + for (;;) { auto attempt = dynamic_cast(in); - if(attempt) return attempt; - if(dynamic_cast(in)) { + if (attempt) + return attempt; + if (dynamic_cast(in)) { return nullptr; } in = in->parent(); diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 287bf6348b..71e17bf7fc 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -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* constraints, int my_id) { IRegConstraint c; - if(dynamic_cast(m_return_reg)){ + if (dynamic_cast(m_return_reg)) { return; } @@ -27,10 +39,22 @@ void IR_Return::add_constraints(std::vector* 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); } \ No newline at end of file diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 386e368b79..be5e6bd523 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -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* 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* 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; diff --git a/goalc/compiler/Label.h b/goalc/compiler/Label.h index 025f4b22fa..0307a723f1 100644 --- a/goalc/compiler/Label.h +++ b/goalc/compiler/Label.h @@ -3,8 +3,6 @@ #ifndef JAK_LABEL_H #define JAK_LABEL_H -struct Label { - -}; +struct Label {}; #endif // JAK_LABEL_H diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index 540c1be629..ed40906c93 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -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& f) { +void Compiler::for_each_in_list(const goos::Object& list, + const std::function& 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"); } } \ No newline at end of file diff --git a/goalc/compiler/Val.cpp b/goalc/compiler/Val.cpp index 590d411392..c0c4f2e72e 100644 --- a/goalc/compiler/Val.cpp +++ b/goalc/compiler/Val.cpp @@ -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(rv, m_value)); return rv; diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 1b967a39d9..00ff0aa74c 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -1,139 +1,141 @@ #include "goalc/compiler/Compiler.h" +#include "goalc/compiler/IR.h" /*! * Main table for compiler forms */ -static const std::unordered_map 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(env); return fe->alloc_val(m_ts.make_typespec("int"), value); } - diff --git a/goalc/compiler/compilation/Block.cpp b/goalc/compiler/compilation/Block.cpp index e20da3e492..404d022f71 100644 --- a/goalc/compiler/compilation/Block.cpp +++ b/goalc/compiler/compilation/Block.cpp @@ -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; } diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index b8c2cd2f07..c666ac8823 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -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(); diff --git a/goalc/emitter/ObjectGenerator.cpp b/goalc/emitter/ObjectGenerator.cpp index 988d61bd78..1f9da29214 100644 --- a/goalc/emitter/ObjectGenerator.cpp +++ b/goalc/emitter/ObjectGenerator.cpp @@ -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 ObjectGenerator::generate_header_v3() { offset += push_data(N_SEG, result); offset += sizeof(u32) * N_SEG * 4; // 4 u32's per segment - + offset += 4; struct SizeOffset { uint32_t offset, size; }; diff --git a/goalc/emitter/ObjectGenerator.h b/goalc/emitter/ObjectGenerator.h index c7ee449d25..968577cbc3 100644 --- a/goalc/emitter/ObjectGenerator.h +++ b/goalc/emitter/ObjectGenerator.h @@ -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); diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index ee9ecea49a..f971dc35b8 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -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 &code) { +void Listener::record_messages(ListenerMessageKind kind) { + if (filter != ListenerMessageKind::MSG_INVALID) { + printf("[Listener] Already recording!\n"); + } + filter = kind; +} + +std::vector Listener::stop_recording_messages() { + filter = ListenerMessageKind::MSG_INVALID; + auto result = message_record; + message_record.clear(); + return result; +} + +void Listener::send_code(std::vector& 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 &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 &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); } diff --git a/goalc/listener/Listener.h b/goalc/listener/Listener.h index 01f68c576e..38c453c6d4 100644 --- a/goalc/listener/Listener.h +++ b/goalc/listener/Listener.h @@ -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 stop_recording_messages(); bool is_connected() const; - void send_reset(); + void send_reset(bool shutdown); void disconnect(); - void send_code(std::vector &code); - bool most_recent_send_was_acked() { - return got_ack; - } + void send_code(std::vector& 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? diff --git a/goalc/logger/Logger.cpp b/goalc/logger/Logger.cpp index 684d406a6a..63e3596d22 100644 --- a/goalc/logger/Logger.cpp +++ b/goalc/logger/Logger.cpp @@ -4,6 +4,7 @@ void Logger::close() { if (fp) { fclose(fp); + fp = nullptr; } } diff --git a/goalc/regalloc/allocate.cpp b/goalc/regalloc/allocate.cpp index 8e6bf798ea..8b4f5a26f3 100644 --- a/goalc/regalloc/allocate.cpp +++ b/goalc/regalloc/allocate.cpp @@ -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()); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8173c9820c..6987685866 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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) diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp new file mode 100644 index 0000000000..a03b01177d --- /dev/null +++ b/test/test_compiler_and_runtime.cpp @@ -0,0 +1,109 @@ +#include + +#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 expected, actual; + std::string test_name; + }; + + std::vector tests; + + void run_test(const std::string& test_file, const std::vector& 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(); +} \ No newline at end of file