From 2b67b1078f238f4a874a7c6822b0d245c625bf94 Mon Sep 17 00:00:00 2001 From: water Date: Sat, 5 Sep 2020 18:55:07 -0400 Subject: [PATCH 01/20] add lambda skeleton --- goalc/compiler/Lambda.h | 10 +++++++++ goalc/compiler/Val.cpp | 31 +++++++++++++++++++++------ goalc/compiler/Val.h | 46 +++++++++++++++++++++++++++++++++-------- 3 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 goalc/compiler/Lambda.h diff --git a/goalc/compiler/Lambda.h b/goalc/compiler/Lambda.h new file mode 100644 index 0000000000..21a92ba820 --- /dev/null +++ b/goalc/compiler/Lambda.h @@ -0,0 +1,10 @@ + + +#ifndef JAK_LAMBDA_H +#define JAK_LAMBDA_H + +struct Lambda { + std::string debug_name; +}; + +#endif // JAK_LAMBDA_H diff --git a/goalc/compiler/Val.cpp b/goalc/compiler/Val.cpp index ef5ba521fd..78ae5c6d81 100644 --- a/goalc/compiler/Val.cpp +++ b/goalc/compiler/Val.cpp @@ -3,20 +3,39 @@ /*! * Fallback to_gpr if a more optimized one is not provided. */ -RegVal* Val::to_gpr(FunctionEnv* fe) const { +const RegVal* Val::to_gpr(FunctionEnv* fe) const { (void)fe; - throw std::runtime_error("Val::to_gpr NYI"); + throw std::runtime_error("Val::to_gpr NYI"); // todo } /*! * Fallback to_xmm if a more optimized one is not provided. */ -RegVal* Val::to_xmm(FunctionEnv* fe) const { +const RegVal* Val::to_xmm(FunctionEnv* fe) const { (void)fe; - throw std::runtime_error("Val::to_xmm NYI"); + throw std::runtime_error("Val::to_xmm NYI"); // todo } -RegVal* None::to_reg(FunctionEnv* fe) const { + +const RegVal* RegVal::to_reg(FunctionEnv* fe) const { (void)fe; - throw std::runtime_error("Cannot put None into a register."); + return this; } + +const RegVal * RegVal::to_gpr(FunctionEnv* fe) const { + (void)fe; + if(m_ireg.kind == emitter::RegKind::GPR) { + return this; + } else { + throw std::runtime_error("RegVal::to_gpr NYI"); // todo + } +} + +const RegVal * RegVal::to_xmm(FunctionEnv* fe) const { + (void)fe; + if(m_ireg.kind == emitter::RegKind::XMM) { + return this; + } else { + throw std::runtime_error("RegVal::to_xmm NYI"); // todo + } +} \ No newline at end of file diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index a95f9f285f..f600526acf 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -12,6 +12,7 @@ #include "third-party/fmt/core.h" #include "common/type_system/TypeSystem.h" #include "goalc/regalloc/IRegister.h" +#include "Lambda.h" class RegVal; class FunctionEnv; @@ -30,9 +31,11 @@ class Val { } virtual std::string print() const = 0; - virtual RegVal* to_reg(FunctionEnv* fe) const = 0; - virtual RegVal* to_gpr(FunctionEnv* fe) const; - virtual RegVal* to_xmm(FunctionEnv* fe) const; + virtual const RegVal* to_reg(FunctionEnv* fe) const { + throw std::runtime_error("to_reg called on invalid Val: " + print()); + } + virtual const RegVal* to_gpr(FunctionEnv* fe) const; + virtual const RegVal* to_xmm(FunctionEnv* fe) const; const TypeSpec& type() const { return m_ts; } @@ -47,7 +50,6 @@ class None : public Val { explicit None(TypeSpec _ts) : Val(std::move(_ts)) {} explicit None(const TypeSystem& _ts) : Val(_ts.make_typespec("none")) {} std::string print() const override { return "none"; } - RegVal* to_reg(FunctionEnv* fe) const override; }; /*! @@ -59,16 +61,42 @@ class RegVal : public Val { bool is_register() const override { return true; } IRegister ireg() const override { return m_ireg; } std::string print() const override { return m_ireg.to_string(); }; - RegVal* to_reg(FunctionEnv* fe) const override; - RegVal* to_gpr(FunctionEnv* fe) const override; - RegVal* to_xmm(FunctionEnv* fe) const override; + const RegVal* to_reg(FunctionEnv* fe) const override; + const RegVal* to_gpr(FunctionEnv* fe) const override; + const RegVal* to_xmm(FunctionEnv* fe) const override; protected: IRegister m_ireg; }; -// Symbol -// Lambda +/*! + * A Val representing a symbol. This is confusing but it's not actually the value of the symbol, + * but instead the symbol itself. + */ +class SymbolVal : public Val { + public: + SymbolVal(std::string name, TypeSpec ts) : Val(std::move(ts)), m_name(std::move(name)) {} + const std::string& name() { return m_name; } + std::string print() const override { return "<" + m_name + ">"; } + + protected: + std::string m_name; +}; + +/*! + * A Val representing a GOAL lambda. It can be a "real" x86-64 function, in which case the + * FunctionEnv is set. Otherwise, just contains a Lambda. + */ +class LambdaVal : public Val { + public: + LambdaVal(TypeSpec ts, Lambda lam) : Val(ts), m_lam(lam) {} + std::string print() const override { return "lambda-" + m_lam.debug_name; } + + protected: + Lambda m_lam; + FunctionEnv* fe = nullptr; +}; + // Static // MemOffConstant // MemOffVar From 8bf0bd86d3dbd90cf07f219a77ecbdb4604f282c Mon Sep 17 00:00:00 2001 From: water Date: Sun, 6 Sep 2020 12:45:31 -0400 Subject: [PATCH 02/20] integer constant program working up to ir --- common/listener_common.h | 16 +- common/util/BinaryWriter.h | 78 +++++++ goalc/CMakeLists.txt | 23 ++- goalc/compiler/Compiler.cpp | 110 +++++++++- goalc/compiler/Compiler.h | 36 ++++ goalc/compiler/Env.cpp | 187 ++++++++++++++++- goalc/compiler/Env.h | 195 +++++++++++++++++- goalc/compiler/IR.cpp | 43 +++- goalc/compiler/IR.h | 35 +++- goalc/compiler/Label.h | 10 + goalc/compiler/Util.cpp | 96 +++++++++ goalc/compiler/Val.cpp | 15 +- goalc/compiler/Val.h | 13 ++ goalc/compiler/compilation/Atoms.cpp | 177 ++++++++++++++++ goalc/compiler/compilation/Block.cpp | 16 ++ .../compiler/compilation/CompilerControl.cpp | 12 ++ goalc/listener/CMakeLists.txt | 5 + goalc/listener/Listener.cpp | 94 +++++++++ goalc/listener/Listener.h | 9 + goalc/main.cpp | 6 +- 20 files changed, 1142 insertions(+), 34 deletions(-) create mode 100644 common/util/BinaryWriter.h create mode 100644 goalc/compiler/Label.h create mode 100644 goalc/compiler/Util.cpp create mode 100644 goalc/compiler/compilation/Atoms.cpp create mode 100644 goalc/compiler/compilation/Block.cpp create mode 100644 goalc/compiler/compilation/CompilerControl.cpp diff --git a/common/listener_common.h b/common/listener_common.h index f18c41b4a5..a589919ba6 100644 --- a/common/listener_common.h +++ b/common/listener_common.h @@ -33,7 +33,7 @@ enum class ListenerMessageKind : u16 { /*! * Type of message sent from compiler */ -enum ListenerToTargetMsgKind { +enum ListenerToTargetMsgKind : u16 { LTT_MSG_POKE = 1, //! "Poke" the game and have it flush buffers LTT_MSG_INSEPCT = 5, //! Inspect an object LTT_MSG_PRINT = 6, //! Print an object @@ -47,11 +47,15 @@ enum ListenerToTargetMsgKind { * TODO - there are other copies of this somewhere */ struct ListenerMessageHeader { - Deci2Header deci2_header; //! The header used for DECI2 communication - ListenerMessageKind msg_kind; //! GOAL Listener message kind - u16 u6; //! Unknown - u32 msg_size; //! Size of data after this header - u64 u8; //! Unknown + Deci2Header deci2_header; //! The header used for DECI2 communication + union { + ListenerMessageKind msg_kind; //! GOAL Listener message kind + ListenerToTargetMsgKind ltt_msg_kind; + }; + + u16 u6; //! Unknown + u32 msg_size; //! Size of data after this header + u64 u8; //! Unknown }; constexpr int DECI2_PORT = 8112; // TODO - is this a good choise? diff --git a/common/util/BinaryWriter.h b/common/util/BinaryWriter.h new file mode 100644 index 0000000000..fab9c5b19b --- /dev/null +++ b/common/util/BinaryWriter.h @@ -0,0 +1,78 @@ +#ifndef JAK_BINARYWRITER_H +#define JAK_BINARYWRITER_H + +#include +#include +#include +#include +#include + +struct BinaryWriterRef { + size_t offset; + size_t write_size; +}; + +class BinaryWriter { + public: + BinaryWriter() = default; + + template + BinaryWriterRef add(const T& obj) { + auto orig_size = data.size(); + data.resize(orig_size + sizeof(T)); + memcpy(data.data() + orig_size, &obj, sizeof(T)); + return {orig_size, sizeof(T)}; + } + + 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_cstr_len(const char* str, size_t len) { + size_t i = 0; + while(*str) { + data.push_back(*str); + str++; + i++; + } + + while(i < len) { + data.push_back(0); + i++; + } + } + + BinaryWriterRef add_data(void* d, size_t len) { + auto orig_size = data.size(); + data.resize(orig_size + len); + memcpy(data.data() + orig_size, d, len); + return {orig_size, len}; + } + + size_t get_size() { + return data.size(); + } + + 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); + fclose(fp); + } + + private: + std::vector data; +}; + +#endif // JAK_BINARYWRITER_H diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 21b3fcb92c..c63f22b516 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -2,11 +2,11 @@ add_subdirectory(util) add_subdirectory(goos) IF (WIN32) - # TODO - implement windows listener - message("Windows Listener Not Implemented!") -ELSE() - add_subdirectory(listener) -ENDIF() + # TODO - implement windows listener + message("Windows Listener Not Implemented!") +ELSE () + add_subdirectory(listener) +ENDIF () add_library(compiler SHARED @@ -19,6 +19,10 @@ add_library(compiler compiler/Val.cpp compiler/IR.cpp compiler/CodeGenerator.cpp + compiler/compilation/Atoms.cpp + compiler/compilation/CompilerControl.cpp + compiler/compilation/Block.cpp + compiler/Util.cpp logger/Logger.cpp regalloc/IRegister.cpp regalloc/Allocator.cpp @@ -30,8 +34,11 @@ add_executable(goalc main.cpp ) IF (WIN32) - set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) - target_link_libraries(compiler util goos type_system mman) -ENDIF() + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + target_link_libraries(compiler util goos type_system mman) + +ELSE () + target_link_libraries(compiler util goos type_system listener) +ENDIF () target_link_libraries(goalc util goos compiler type_system) diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index e1353df059..240ded2502 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -1,19 +1,62 @@ #include "Compiler.h" #include "goalc/logger/Logger.h" +#include "common/link_types.h" + +using namespace goos; Compiler::Compiler() { init_logger(); m_ts.add_builtin_types(); + m_global_env = std::make_unique(); + m_none = std::make_unique(m_ts.make_typespec("none")); + + // todo - compile library } -void Compiler::execute_repl() {} +void Compiler::execute_repl() { + while (!m_want_exit) { + try { + // 1). get a line from the user (READ) + std::string prompt; + if (m_listener.is_connected()) { + prompt = "gc"; + } else { + prompt = "g"; + } + Object code = m_goos.reader.read_from_stdin(prompt); + + // 2). compile + 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"); + // } + // } + + } catch (std::exception& e) { + gLogger.log(MSG_WARN, "REPL Error: %s\n", e.what()); + } + } + + m_listener.disconnect(); +} Compiler::~Compiler() { gLogger.close(); } void Compiler::init_logger() { - gLogger.set_file("compiler.txt"); + gLogger.set_file("compiler.txt"); // todo, a better file than this... gLogger.config[MSG_COLOR].kind = LOG_FILE; gLogger.config[MSG_DEBUG].kind = LOG_IGNORE; gLogger.config[MSG_TGT].color = COLOR_GREEN; @@ -21,4 +64,65 @@ void Compiler::init_logger() { gLogger.config[MSG_WARN].color = COLOR_RED; gLogger.config[MSG_ICE].color = COLOR_RED; gLogger.config[MSG_ERR].color = COLOR_RED; -} \ No newline at end of file +} + +FileEnv* Compiler::compile_object_file(const std::string& name, + goos::Object code, + bool allow_emit) { + auto file_env = m_global_env->add_file(name); + Env* compilation_env = file_env; + if (!allow_emit) { + compilation_env = file_env->add_no_emit_env(); + } + + file_env->add_top_level_function( + compile_top_level_function("top-level", std::move(code), compilation_env)); + + return file_env; +} + +std::unique_ptr Compiler::compile_top_level_function(const std::string& name, + const goos::Object& code, + Env* env) { + auto fe = std::make_unique(env, name); + fe->set_segment(TOP_LEVEL_SEGMENT); + + auto result = compile_error_guard(code, fe.get()); + + // only move to return register if we actually got a result + if (!dynamic_cast(result)) { + fe->emit(std::make_unique(fe->make_gpr(result->type()), result->to_gpr(fe.get()))); + } + + fe->finish(); + return fe; +} + +Val* Compiler::compile_error_guard(const goos::Object& code, Env* env) { + try { + return compile(code, env); + } catch (std::runtime_error& e) { + printf( + "------------------------------------------------------------------------------------------" + "-\n"); + auto obj_print = code.print(); + if (obj_print.length() > 80) { + obj_print = obj_print.substr(0, 80); + obj_print += "..."; + } + printf("object: %s\nfrom : %s\n", obj_print.c_str(), + m_goos.reader.db.get_info_for(code).c_str()); + throw e; + } +} + +void Compiler::throw_compile_error(const goos::Object& o, const std::string& err) { + gLogger.log(MSG_ERR, "[Error] Could not compile %s!\nReason: %s\n", o.print().c_str(), + err.c_str()); + throw std::runtime_error(err); +} + +void Compiler::ice(const std::string& error) { + gLogger.log(MSG_ICE, "[ICE] %s\n", error.c_str()); + throw std::runtime_error("ICE"); +} diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 12781a408d..a491416a35 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -2,17 +2,53 @@ #define JAK_COMPILER_H #include "common/type_system/TypeSystem.h" +#include "Env.h" +#include "goalc/listener/Listener.h" +#include "goalc/goos/Interpreter.h" class Compiler { public: Compiler(); ~Compiler(); void execute_repl(); + FileEnv* compile_object_file(const std::string& name, goos::Object code, bool allow_emit); + std::unique_ptr compile_top_level_function(const std::string& name, + const goos::Object& code, + Env* env); + Val* compile(const goos::Object& code, Env* env); + Val* compile_error_guard(const goos::Object& code, Env* env); + void throw_compile_error(const goos::Object& o, const std::string& err); + void ice(const std::string& err); + None* get_none() { return m_none.get(); } 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 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( + const goos::Object& form, + const goos::Arguments& args, + const std::vector>& unnamed, + const std::unordered_map>>& + named); TypeSystem m_ts; + std::unique_ptr m_global_env = nullptr; + std::unique_ptr m_none = nullptr; + bool m_want_exit = false; + listener::Listener m_listener; + goos::Interpreter m_goos; + + public: + Val* compile_exit(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env); }; #endif // JAK_COMPILER_H diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index 785a75a876..dab3d75a76 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -1,3 +1,186 @@ - - +#include #include "Env.h" + +/////////////////// +// Env +/////////////////// + +/*! + * Emit IR into the function currently being compiled. + */ +void Env::emit(std::unique_ptr ir) { + // by default, we don't know how, so pass it up and hope for the best. + m_parent->emit(std::move(ir)); +} + +/*! + * Allocate an IRegister with the given type. + */ +RegVal* Env::make_ireg(TypeSpec ts, emitter::RegKind kind) { + return m_parent->make_ireg(std::move(ts), kind); +} + +/*! + * Apply a register constraint to the current function. + */ +void Env::constrain_reg(IRegConstraint constraint) { + m_parent->constrain_reg(std::move(constraint)); +} + +/*! + * Lookup the given symbol object as a lexical variable. + */ +Val* Env::lexical_lookup(goos::Object sym) { + return m_parent->lexical_lookup(std::move(sym)); +} + +BlockEnv* Env::find_block(const std::string& name) { + return m_parent->find_block(name); +} + +RegVal* Env::make_gpr(TypeSpec ts) { + return make_ireg(std::move(ts), emitter::RegKind::GPR); +} + +RegVal* Env::make_xmm(TypeSpec ts) { + return make_ireg(std::move(ts), emitter::RegKind::XMM); +} + +/////////////////// +// GlobalEnv +/////////////////// + +// Because this is the top of the environment chain, all these end the parent calls and provide +// errors, or return that the items were not found. + +GlobalEnv::GlobalEnv() : Env(nullptr) {} + +std::string GlobalEnv::print() { + return "global-env"; +} + +/*! + * Emit IR into the function currently being compiled. + */ +void GlobalEnv::emit(std::unique_ptr ir) { + // by default, we don't know how, so pass it up and hope for the best. + (void)ir; + throw std::runtime_error("cannot emit to GlobalEnv"); +} + +/*! + * Allocate an IRegister with the given type. + */ +RegVal* GlobalEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) { + (void)ts; + (void)kind; + throw std::runtime_error("cannot alloc reg in GlobalEnv"); +} + +/*! + * Apply a register constraint to the current function. + */ +void GlobalEnv::constrain_reg(IRegConstraint constraint) { + (void)constraint; + throw std::runtime_error("cannot constrain reg in GlobalEnv"); +} + +/*! + * Lookup the given symbol object as a lexical variable. + */ +Val* GlobalEnv::lexical_lookup(goos::Object sym) { + (void)sym; + return nullptr; +} + +BlockEnv * GlobalEnv::find_block(const std::string& name) { + (void)name; + return nullptr; +} + +FileEnv * GlobalEnv::add_file(std::string name) { + m_files.push_back(std::make_unique(this, std::move(name))); + return m_files.back().get(); +} + +/////////////////// +// NoEmitEnv +/////////////////// + +/*! + * Get the name of a NoEmitEnv + */ +std::string NoEmitEnv::print() { + return "no-emit-env"; +} + +/*! + * Emit - which is invalid - into a NoEmitEnv and throw an exception. + */ +void NoEmitEnv::emit(std::unique_ptr ir) { + (void)ir; + throw std::runtime_error("emit into a no-emit env!"); +} + +/////////////////// +// FileEnv +/////////////////// + +FileEnv::FileEnv(Env* parent, std::string name) : Env(parent), m_name(std::move(name)) {} + +std::string FileEnv::print() { + return "file-" + m_name; +} + +void FileEnv::add_function(std::unique_ptr fe) { + m_functions.push_back(std::move(fe)); +} + +void FileEnv::add_top_level_function(std::unique_ptr fe) { + // todo, set FE as top level segment + m_functions.push_back(std::move(fe)); + m_top_level_func = m_functions.back().get(); +} + +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()) { + fmt::print("{}\n", code->print()); + } + } else { + printf("no top level function.\n"); + } +} + +/////////////////// +// FunctionEnv +/////////////////// + +FunctionEnv::FunctionEnv(Env* parent, std::string name) : DeclareEnv(parent), m_name(std::move(name)){} + +std::string FunctionEnv::print() { + return "function-" + m_name; +} + +void FunctionEnv::emit(std::unique_ptr ir) { + ir->add_constraints(&m_constraints, m_code.size()); + m_code.push_back(std::move(ir)); +} +void FunctionEnv::finish() { + // todo resolve gotos +} + +RegVal * FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) { + IRegister ireg; + ireg.kind = kind; + ireg.id = m_iregs.size(); + auto rv = std::make_unique(ireg, ts); + m_iregs.push_back(std::move(rv)); + return m_iregs.back().get(); +} \ No newline at end of file diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index e7c85e61d9..bfef83ffc1 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -1,14 +1,197 @@ - +/*! + * @file Env.h + * The Env tree. The stores all of the nested scopes/contexts during compilation and also + * manages the memory for stuff generated during compiling. + */ #ifndef JAK_ENV_H #define JAK_ENV_H -class Env {}; +#include +#include +#include +#include "common/type_system/TypeSpec.h" +#include "goalc/goos/Object.h" +#include "IR.h" +#include "Label.h" +#include "Val.h" -// global -// noemit -// objectfile -// configuration +class FileEnv; +class BlockEnv; + +/*! + * Parent class for Env's + */ +class Env { + public: + explicit Env(Env* parent) : m_parent(parent) {} + virtual std::string print() = 0; + virtual void emit(std::unique_ptr ir); + virtual RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind); + virtual void constrain_reg(IRegConstraint constraint); + virtual Val* lexical_lookup(goos::Object sym); + virtual BlockEnv* find_block(const std::string& name); + RegVal* make_gpr(TypeSpec ts); + RegVal* make_xmm(TypeSpec ts); + virtual ~Env() = default; + + Env* parent() { return m_parent; } + + protected: + Env* m_parent = nullptr; +}; + +/*! + * The top-level Env. Holds FileEnvs for all files. + */ +class GlobalEnv : public Env { + public: + GlobalEnv(); + std::string print() override; + void emit(std::unique_ptr ir) override; + RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind) override; + void constrain_reg(IRegConstraint constraint) override; + Val* lexical_lookup(goos::Object sym) override; + BlockEnv* find_block(const std::string& name) override; + ~GlobalEnv() = default; + + FileEnv* add_file(std::string name); + + private: + std::vector> m_files; +}; + +/*! + * An Env that doesn't allow emitting to go past it. Used to make sure source code that shouldn't + * generate machine code actually does this. + */ +class NoEmitEnv : public Env { + public: + explicit NoEmitEnv(Env* parent) : Env(parent) {} + std::string print() override; + void emit(std::unique_ptr ir) override; + ~NoEmitEnv() = default; +}; + +/*! + * An Env for an entire file (or input to the REPL) + */ +class FileEnv : public Env { + public: + FileEnv(Env* parent, std::string name); + std::string print() override; + void add_function(std::unique_ptr fe); + void add_top_level_function(std::unique_ptr fe); + NoEmitEnv* add_no_emit_env(); + void debug_print_tl(); + + // todo - is_empty + ~FileEnv() = default; + + protected: + std::string m_name; + std::vector> m_functions; + std::unique_ptr m_no_emit_env = nullptr; + + // statics + FunctionEnv* m_top_level_func = nullptr; +}; + +/*! + * An Env which manages the scope for (declare ...) statements. + */ +class DeclareEnv : public Env { + public: + explicit DeclareEnv(Env* parent) : Env(parent) {} + virtual std::string print() = 0; + ~DeclareEnv() = default; + + struct Settings { + bool is_set = false; // has the user set these with a (declare)? + bool inline_by_default = false; // if a function, inline when possible? + bool save_code = true; // if a function, should we save the code? + bool allow_inline = false; // should we allow the user to use this an inline function + } m_settings; +}; + +class FunctionEnv : public DeclareEnv { + public: + FunctionEnv(Env* parent, std::string name); + std::string print() override; + void set_segment(int seg) { m_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; } + + template + T* alloc_val(Args&&... args) { + std::unique_ptr new_obj = std::make_unique(std::forward(args)...); + m_vals.push_back(std::move(new_obj)); + return (T*)m_vals.back().get(); + } + + + protected: + std::string m_name; + std::vector> m_code; + std::vector> m_iregs; + std::vector> m_vals; + std::vector m_constraints; + // todo, unresolved gotos + AllocationResult m_regalloc_result; + bool m_is_asm_func = false; + + std::string m_method_of_type_name = "#f"; + bool m_aligned_stack_required = false; + int m_segment = -1; + + std::unordered_map m_params; +}; + +class BlockEnv : public Env { + public: + 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; + Val* m_return_value = nullptr; + std::vector m_return_types; +}; + +class LexicalEnv : public Env { + public: + LexicalEnv(Env* parent); + std::string print() override; +}; + +class LabelEnv : public Env { + public: + protected: + std::unordered_map m_labels; +}; + +class WithInlineEnv : public Env { + +}; + +class SymbolMacroEnv : public Env { + +}; + +template +T* get_parent_env_of_type(Env* in) { + for(;;) { + auto attempt = dynamic_cast(in); + if(attempt) return attempt; + if(dynamic_cast(in)) { + return nullptr; + } + in = in->parent(); + } +} // function // block // lexical diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 479292ef2e..287bf6348b 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -1,3 +1,42 @@ - - #include "IR.h" + +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()); +} + +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 + } + return rai; +} + +void IR_Return::add_constraints(std::vector* constraints, int my_id) { + IRegConstraint c; + if(dynamic_cast(m_return_reg)){ + return; + } + + c.ireg = m_return_reg->ireg(); + c.instr_idx = my_id; + c.desired_register = emitter::RAX; + constraints->push_back(c); +} + +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); +} + +RegAllocInstr IR_LoadConstant64::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + return rai; +} \ No newline at end of file diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index f379466466..386e368b79 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -4,18 +4,49 @@ #include #include "CodeGenerator.h" #include "goalc/regalloc/allocate.h" +#include "Val.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(CodeGenerator* gen) = 0; + virtual void add_constraints(std::vector* constraints, int my_id) { + (void)constraints; + (void)my_id; + } }; -class IR_Set : public IR { +// class IR_Set : public IR { +// public: +// std::string print() override; +// RegAllocInstr to_rai() override; +//}; + +class IR_Return : public IR { public: + IR_Return(const RegVal* return_reg, const RegVal* value); std::string print() override; RegAllocInstr to_rai() override; + void add_constraints(std::vector* constraints, int my_id) override; +// void do_codegen(CodeGenerator* gen) override; + const RegVal* value() { return m_value; } + + protected: + const RegVal* m_return_reg = nullptr; + const RegVal* m_value = nullptr; +}; + +class IR_LoadConstant64 : public IR { + public: + IR_LoadConstant64(const RegVal* dest, u64 value); + std::string print() override; + RegAllocInstr to_rai() override; +// void do_codegen(CodeGenerator* gen) override; + + protected: + const RegVal* m_dest = nullptr; + u64 m_value = 0; }; #endif // JAK_IR_H diff --git a/goalc/compiler/Label.h b/goalc/compiler/Label.h new file mode 100644 index 0000000000..025f4b22fa --- /dev/null +++ b/goalc/compiler/Label.h @@ -0,0 +1,10 @@ + + +#ifndef JAK_LABEL_H +#define JAK_LABEL_H + +struct Label { + +}; + +#endif // JAK_LABEL_H diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp new file mode 100644 index 0000000000..540c1be629 --- /dev/null +++ b/goalc/compiler/Util.cpp @@ -0,0 +1,96 @@ +#include "goalc/compiler/Compiler.h" + +goos::Arguments Compiler::get_va(const goos::Object& form, const goos::Object& rest) { + goos::Arguments args; + // loop over forms in list + goos::Object current = rest; + while (!current.is_empty_list()) { + auto arg = current.as_pair()->car; + + // did we get a ":keyword" + if (arg.is_symbol() && arg.as_symbol()->name.at(0) == ':') { + auto key_name = arg.as_symbol()->name.substr(1); + + // check for multiple definition of key + if (args.named.find(key_name) != args.named.end()) { + throw_compile_error(form, "Key argument " + key_name + " multiply defined"); + } + + // check for well-formed :key value expression + current = current.as_pair()->cdr; + if (current.is_empty_list()) { + throw_compile_error(form, "Key argument didn't have a value"); + } + + args.named[key_name] = current.as_pair()->car; + } else { + // not a keyword. Add to unnamed or rest, depending on what we expect + args.unnamed.push_back(arg); + } + current = current.as_pair()->cdr; + } + + return args; +} + +void Compiler::va_check( + const goos::Object& form, + const goos::Arguments& args, + const std::vector>& unnamed, + const std::unordered_map>>& + named) { + assert(args.rest.empty()); + if (unnamed.size() != args.unnamed.size()) { + throw_compile_error(form, "Got " + std::to_string(args.unnamed.size()) + + " arguments, but expected " + std::to_string(unnamed.size())); + } + + for (size_t i = 0; i < unnamed.size(); i++) { + if (unnamed[i] != args.unnamed[i].type) { + assert(!unnamed[i].is_wildcard); + throw_compile_error(form, "Argument " + std::to_string(i) + " has type " + + object_type_to_string(args.unnamed[i].type) + " but " + + object_type_to_string(unnamed[i].value) + " was expected"); + } + } + + for (const auto& kv : named) { + auto kv2 = args.named.find(kv.first); + if (kv2 == args.named.end()) { + // argument not given. + if (kv.second.first) { + // but was required + throw_compile_error(form, "Required named argument \"" + kv.first + "\" was not found"); + } + } else { + // argument given. + if (kv.second.second != kv2->second.type) { + // but is wrong type + assert(!kv.second.second.is_wildcard); + throw_compile_error(form, "Argument \"" + kv.first + "\" has type " + + object_type_to_string(kv2->second.type) + " but " + + object_type_to_string(kv.second.second.value) + + " was expected"); + } + } + } + + for (const auto& kv : args.named) { + if (named.find(kv.first) == named.end()) { + throw_compile_error(form, "Got unrecognized keyword argument \"" + kv.first + "\""); + } + } +} + +void Compiler::for_each_in_list(const goos::Object& list, const std::function& f) { + const goos::Object* iter = &list; + while(iter->is_pair()) { + auto lap = iter->as_pair(); + f(lap->car); + iter = &lap->cdr; + } + + 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 78ae5c6d81..590d411392 100644 --- a/goalc/compiler/Val.cpp +++ b/goalc/compiler/Val.cpp @@ -1,11 +1,16 @@ #include "Val.h" +#include "Env.h" /*! * Fallback to_gpr if a more optimized one is not provided. */ const RegVal* Val::to_gpr(FunctionEnv* fe) const { - (void)fe; - throw std::runtime_error("Val::to_gpr NYI"); // todo + auto rv = to_reg(fe); + if(rv->ireg().kind == emitter::RegKind::GPR) { + return rv; + } else { + throw std::runtime_error("Val::to_gpr NYI"); // todo + } } /*! @@ -38,4 +43,10 @@ const RegVal * RegVal::to_xmm(FunctionEnv* fe) const { } else { throw std::runtime_error("RegVal::to_xmm NYI"); // todo } +} + +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; } \ No newline at end of file diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index f600526acf..9ee9b86f27 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -32,12 +32,14 @@ class Val { virtual std::string print() const = 0; virtual const RegVal* to_reg(FunctionEnv* fe) const { + (void)fe; throw std::runtime_error("to_reg called on invalid Val: " + print()); } virtual const RegVal* to_gpr(FunctionEnv* fe) const; virtual const RegVal* to_xmm(FunctionEnv* fe) const; const TypeSpec& type() const { return m_ts; } + void set_type(TypeSpec ts) { m_ts = std::move(ts); } protected: TypeSpec m_ts; @@ -47,6 +49,7 @@ class Val { * Special None Val used for the value of anything returning (none). */ class None : public Val { + public: explicit None(TypeSpec _ts) : Val(std::move(_ts)) {} explicit None(const TypeSystem& _ts) : Val(_ts.make_typespec("none")) {} std::string print() const override { return "none"; } @@ -103,6 +106,16 @@ class LambdaVal : public Val { // MemDeref // PairEntry // Alias + +class IntegerConstantVal : public Val { + public: + IntegerConstantVal(TypeSpec ts, s64 value) : Val(ts), m_value(value) {} + std::string print() const override { return "integer-constant-" + std::to_string(m_value); } + const RegVal* to_reg(FunctionEnv* fe) const override; + + protected: + s64 m_value = -1; +}; // IntegerConstant // FloatConstant // Bitfield diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp new file mode 100644 index 0000000000..1b967a39d9 --- /dev/null +++ b/goalc/compiler/compilation/Atoms.cpp @@ -0,0 +1,177 @@ +#include "goalc/compiler/Compiler.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 + {"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}, + {":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}, + }; + +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: + return compile_integer(code, env); + default: + ice("Don't know how to compile " + code.print()); + } + return get_none(); +} + +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()) { + 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()) { + 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) { + return compile_integer(code.integer_obj.value, 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 new file mode 100644 index 0000000000..e20da3e492 --- /dev/null +++ b/goalc/compiler/compilation/Block.cpp @@ -0,0 +1,16 @@ +#include "goalc/compiler/Compiler.h" + +using namespace goos; + +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) { + (void)form; + Val* result = get_none(); + 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 new file mode 100644 index 0000000000..b8c2cd2f07 --- /dev/null +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -0,0 +1,12 @@ +#include "goalc/compiler/Compiler.h" + +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(); + } + m_want_exit = true; + return get_none(); +} \ No newline at end of file diff --git a/goalc/listener/CMakeLists.txt b/goalc/listener/CMakeLists.txt index f978b4d633..1ec9728b8d 100644 --- a/goalc/listener/CMakeLists.txt +++ b/goalc/listener/CMakeLists.txt @@ -1,2 +1,7 @@ add_library(listener SHARED Listener.cpp) +IF (WIN32) + # +ELSE () + target_link_libraries(listener pthread) +ENDIF () \ No newline at end of file diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index 2a53f26612..ee9ecea49a 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -1,6 +1,8 @@ /*! * @file Listener.cpp * The Listener can connect to a Deci2Server for debugging. + * + * TODO - msg ID? */ // TODO-Windows @@ -11,10 +13,12 @@ #include #include #include +#include #include "Listener.h" #include "common/versions.h" using namespace versions; +constexpr bool debug_listener = true; namespace listener { Listener::Listener() { @@ -241,5 +245,95 @@ void Listener::receive_func() { } } +void Listener::send_code(std::vector &code) { + got_ack = false; + int total_size = code.size() + sizeof(ListenerMessageHeader); + if(total_size > BUFFER_SIZE) { + printf("[ERROR] Listener send_code got too big of a message\n"); + return; + } + + auto* header = (ListenerMessageHeader*)m_buffer; + 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.src = 'H'; + header->deci2_header.dst = 'E'; + header->msg_size = code.size(); + header->ltt_msg_kind = LTT_MSG_CODE; + header->u6 = 0; + header->u8 = 0; + memcpy(buffer_data, code.data(), code.size()); + send_buffer(total_size); +} + +void Listener::send_reset() { + 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.src = 'H'; + header->deci2_header.dst = 'E'; + header->msg_size = 0; + header->ltt_msg_kind = LTT_MSG_RESET; + header->u6 = 0; + header->u8 = 0; + send_buffer(sizeof(ListenerMessageHeader)); + disconnect(); + close(socket_fd); + printf("closed connection to target\n"); +} + +void Listener::send_buffer(int sz) { + int wrote = 0; + + if(debug_listener) { + printf("[L -> T] sending %d bytes...\n", sz); + } + + got_ack = false; + waiting_for_ack = true; + 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) { + printf(" waiting for ack...\n"); + } + + + if(wait_for_ack()) { + if(debug_listener) { + printf("ack buff:\n"); + printf("%s\n", ack_recv_buff); + printf(" OK\n"); + } + } else { + printf(" NG - target has timed out. If it has died, disconnect with (disconnect-target)\n"); + } +} + +bool Listener::wait_for_ack() { + 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; + usleep(1000); + } + + waiting_for_ack = false; + return false; +} + } // namespace listener #endif diff --git a/goalc/listener/Listener.h b/goalc/listener/Listener.h index 5ae49e2f5f..01f68c576e 100644 --- a/goalc/listener/Listener.h +++ b/goalc/listener/Listener.h @@ -23,9 +23,18 @@ class Listener { void record_messages(ListenerMessageKind kind); void stop_recording_messages(); bool is_connected() const; + void send_reset(); void disconnect(); + 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/main.cpp b/goalc/main.cpp index 57d145c069..b86bda8981 100644 --- a/goalc/main.cpp +++ b/goalc/main.cpp @@ -1,13 +1,13 @@ #include -#include "goalc/goos/Interpreter.h" +#include "goalc/compiler/Compiler.h" int main(int argc, char** argv) { (void)argc; (void)argv; printf("goal compiler\n"); - goos::Interpreter interp; - interp.execute_repl(); + Compiler compiler; + compiler.execute_repl(); return 0; } From d49b01e310f31b176629d61afc7ef8726eb86a6d Mon Sep 17 00:00:00 2001 From: water Date: Sun, 6 Sep 2020 16:58:25 -0400 Subject: [PATCH 03/20] 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 From 1de0cbb6f60bc4bdb6022a205c5597ac3783ba28 Mon Sep 17 00:00:00 2001 From: water Date: Sun, 6 Sep 2020 17:42:20 -0400 Subject: [PATCH 04/20] enable macros --- goal_src/goal-lib.gc | 9 +++++ goalc/CMakeLists.txt | 1 + goalc/compiler/Compiler.cpp | 24 +++++++----- goalc/compiler/Compiler.h | 6 +++ goalc/compiler/Env.cpp | 4 ++ goalc/compiler/Env.h | 3 +- goalc/compiler/compilation/Atoms.cpp | 10 +++-- .../compiler/compilation/CompilerControl.cpp | 12 ++++++ goalc/compiler/compilation/Macro.cpp | 37 +++++++++++++++++++ goalc/goos/Interpreter.h | 19 +++++----- 10 files changed, 101 insertions(+), 24 deletions(-) create mode 100644 goal_src/goal-lib.gc create mode 100644 goalc/compiler/compilation/Macro.cpp diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc new file mode 100644 index 0000000000..af0e1fad03 --- /dev/null +++ b/goal_src/goal-lib.gc @@ -0,0 +1,9 @@ +;; compile, color, and save a file +(defmacro m (file) + `(asm-file ,file :color :write) + ) + +;; compile, color, load and save a file +(defmacro ml (file) + `(asm-file ,file :color :load :write) + ) \ No newline at end of file diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index c63f22b516..63cdb110eb 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(compiler compiler/compilation/Atoms.cpp compiler/compilation/CompilerControl.cpp compiler/compilation/Block.cpp + compiler/compilation/Macro.cpp compiler/Util.cpp logger/Logger.cpp regalloc/IRegister.cpp diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index fe2106f437..d9d062e9b1 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -14,10 +14,12 @@ Compiler::Compiler() { m_none = std::make_unique(m_ts.make_typespec("none")); // todo - compile library + Object library_code = m_goos.reader.read_from_file("goal_src/goal-lib.gc"); + compile_object_file("goal-lib", library_code, false); } void Compiler::execute_repl() { - m_listener.connect_to_target(); + m_listener.connect_to_target(); // todo, remove while (!m_want_exit) { try { // 1). get a line from the user (READ) @@ -33,17 +35,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); + if (!obj_file->is_empty()) { + // 3). color + color_object_file(obj_file); - // 4). codegen - auto data = codegen_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"); + // 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"); + } } } diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index dc5f67f24d..16722ea814 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -27,6 +27,11 @@ class Compiler { private: void init_logger(); + bool try_getting_macro_from_goos(const goos::Object& macro_name, goos::Object* dest); + Val* compile_goos_macro(const goos::Object& o, + const goos::Object& macro_obj, + const goos::Object& rest, + Env* env); 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); @@ -55,6 +60,7 @@ class Compiler { Val* compile_exit(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_seval(const goos::Object& form, const goos::Object& rest, Env* env); }; #endif // JAK_COMPILER_H diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index e6a7b500b6..50bc153280 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -159,6 +159,10 @@ void FileEnv::debug_print_tl() { } } +bool FileEnv::is_empty() { + return m_functions.size() == 1 && m_functions.front().get() == m_top_level_func && + m_top_level_func->code().empty(); +} /////////////////// // FunctionEnv /////////////////// diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index 056c2aa3c1..8a3c7baad6 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -89,6 +89,7 @@ class FileEnv : public Env { const std::vector>& functions() { return m_functions; } // todo - is_empty + bool is_empty(); ~FileEnv() = default; protected: @@ -141,6 +142,7 @@ class FunctionEnv : public DeclareEnv { return (T*)m_vals.back().get(); } int segment = -1; + std::string method_of_type_name = "#f"; protected: std::string m_name; @@ -152,7 +154,6 @@ class FunctionEnv : public DeclareEnv { AllocationResult m_regalloc_result; bool m_is_asm_func = false; - std::string m_method_of_type_name = "#f"; bool m_aligned_stack_required = false; std::unordered_map m_params; diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 00ff0aa74c..7fd4039d52 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -26,7 +26,7 @@ static const std::unordered_map< // // // COMPILER CONTROL // {"gs", &Compiler::compile_gs}, - {":exit", &Compiler::compile_exit} + {":exit", &Compiler::compile_exit}, // {"asm-file", &Compiler::compile_asm_file}, // {"test", &Compiler::compile_test}, // {"in-package", &Compiler::compile_in_package}, @@ -34,7 +34,7 @@ static const std::unordered_map< // // CONDITIONAL COMPILATION // {"#cond", &Compiler::compile_gscond}, // {"defglobalconstant", &Compiler::compile_defglobalconstant}, - // {"seval", &Compiler::compile_seval}, + {"seval", &Compiler::compile_seval}, // // // CONTROL FLOW // {"cond", &Compiler::compile_cond}, @@ -159,7 +159,11 @@ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { return ((*this).*(kv_gfs->second))(code, rest, env); } - // todo macro + goos::Object macro_obj; + if (try_getting_macro_from_goos(head, ¯o_obj)) { + return compile_goos_macro(code, macro_obj, rest, env); + } + // todo enum } diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index c666ac8823..2d83f48ffd 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -10,4 +10,16 @@ Val* Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, } m_want_exit = true; return get_none(); +} + +Val* Compiler::compile_seval(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + try { + for_each_in_list(rest, [&](const goos::Object& o) { + m_goos.eval_with_rewind(o, m_goos.global_environment.as_env()); + }); + } catch (std::runtime_error& e) { + throw_compile_error(form, std::string("seval error: ") + e.what()); + } + return get_none(); } \ No newline at end of file diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp new file mode 100644 index 0000000000..275207c197 --- /dev/null +++ b/goalc/compiler/compilation/Macro.cpp @@ -0,0 +1,37 @@ +#include "goalc/compiler/Compiler.h" + +using namespace goos; + +bool Compiler::try_getting_macro_from_goos(const goos::Object& macro_name, goos::Object* dest) { + Object macro_obj; + bool got_macro = false; + try { + macro_obj = m_goos.eval_symbol(macro_name, m_goos.goal_env.as_env()); + if (macro_obj.is_macro()) { + got_macro = true; + } + } catch (std::runtime_error& e) { + got_macro = false; + } + + if (got_macro) { + *dest = macro_obj; + } + return got_macro; +} + +Val* Compiler::compile_goos_macro(const goos::Object& o, + const goos::Object& macro_obj, + const goos::Object& rest, + Env* env) { + auto macro = macro_obj.as_macro(); + Arguments args = m_goos.get_args(o, rest, macro->args); + auto mac_env_obj = EnvironmentObject::make_new(); + auto mac_env = mac_env_obj.as_env(); + m_goos.set_args_in_env(o, args, macro->args, mac_env); + m_goos.goal_to_goos.enclosing_method_type = + get_parent_env_of_type(env)->method_of_type_name; + auto goos_result = m_goos.eval_list_return_last(macro->body, macro->body, mac_env); + m_goos.goal_to_goos.reset(); + return compile_error_guard(goos_result, env); +} \ No newline at end of file diff --git a/goalc/goos/Interpreter.h b/goalc/goos/Interpreter.h index b19903e68e..d58f46670e 100644 --- a/goalc/goos/Interpreter.h +++ b/goalc/goos/Interpreter.h @@ -23,6 +23,15 @@ class Interpreter { Object eval(Object obj, const std::shared_ptr& env); Object intern(const std::string& name); void disable_printfs(); + Object eval_symbol(const Object& sym, const std::shared_ptr& env); + Arguments get_args(const Object& form, const Object& rest, const ArgumentSpec& spec); + void set_args_in_env(const Object& form, + const Arguments& args, + const ArgumentSpec& arg_spec, + const std::shared_ptr& env); + Object eval_list_return_last(const Object& form, + Object rest, + const std::shared_ptr& env); Reader reader; Object global_environment; @@ -47,14 +56,9 @@ class Interpreter { const std::unordered_map>>& named); Object eval_pair(const Object& o, const std::shared_ptr& env); - Object eval_symbol(const Object& sym, const std::shared_ptr& env); - Arguments get_args(const Object& form, const Object& rest, const ArgumentSpec& spec); void eval_args(Arguments* args, const std::shared_ptr& env); ArgumentSpec parse_arg_spec(const Object& form, Object& rest); - Object eval_list_return_last(const Object& form, - Object rest, - const std::shared_ptr& env); Object quasiquote_helper(const Object& form, const std::shared_ptr& env); IntType number_to_integer(const Object& obj); @@ -206,11 +210,6 @@ class Interpreter { const Object& rest, const std::shared_ptr& env); - void set_args_in_env(const Object& form, - const Arguments& args, - const ArgumentSpec& arg_spec, - const std::shared_ptr& env); - bool want_exit = false; bool disable_printing = false; From ee4eb9f12823b0b4c8ad4a653dbdff1f13492dad Mon Sep 17 00:00:00 2001 From: water Date: Mon, 7 Sep 2020 13:28:16 -0400 Subject: [PATCH 05/20] add some basic symbol stuff --- doc/goal_todo.md | 19 +++ game/kernel/asm_funcs.asm | 8 +- game/kernel/kboot.cpp | 20 ++- game/kernel/kboot.h | 2 + game/kernel/kmachine.cpp | 3 +- game/kernel/kscheme.cpp | 44 +++--- goal_src/goal-lib.gc | 44 ++++++ .../test/test-conditional-compilation-1.gc | 12 ++ goal_src/test/test-define-1.gc | 10 ++ goal_src/test/test-get-symbol-1.gc | 1 + goal_src/test/test-get-symbol-2.gc | 1 + goalc/CMakeLists.txt | 1 + goalc/compiler/Compiler.cpp | 55 ++++--- goalc/compiler/Compiler.h | 33 ++++- goalc/compiler/Env.h | 3 +- goalc/compiler/IR.cpp | 98 +++++++++++++ goalc/compiler/IR.h | 43 ++++++ goalc/compiler/Util.cpp | 8 + goalc/compiler/Val.cpp | 24 ++- goalc/compiler/Val.h | 33 +++-- goalc/compiler/compilation/Atoms.cpp | 50 ++++++- .../compiler/compilation/CompilerControl.cpp | 138 ++++++++++++++++++ goalc/compiler/compilation/Define.cpp | 41 ++++++ goalc/compiler/compilation/Macro.cpp | 52 +++++++ goalc/emitter/ObjectFileData.cpp | 5 + goalc/goos/Interpreter.cpp | 4 +- goalc/goos/Interpreter.h | 1 + goalc/listener/Listener.cpp | 34 ++++- goalc/listener/Listener.h | 5 +- goalc/util/CMakeLists.txt | 2 +- goalc/util/Timer.cpp | 54 +++++++ goalc/util/Timer.h | 47 ++++++ goalc/util/file_io.cpp | 13 ++ goalc/util/file_io.h | 1 + test/test_compiler_and_runtime.cpp | 13 +- 35 files changed, 841 insertions(+), 81 deletions(-) create mode 100644 doc/goal_todo.md create mode 100644 goal_src/test/test-conditional-compilation-1.gc create mode 100644 goal_src/test/test-define-1.gc create mode 100644 goal_src/test/test-get-symbol-1.gc create mode 100644 goal_src/test/test-get-symbol-2.gc create mode 100644 goalc/compiler/compilation/Define.cpp create mode 100644 goalc/util/Timer.cpp create mode 100644 goalc/util/Timer.h diff --git a/doc/goal_todo.md b/doc/goal_todo.md new file mode 100644 index 0000000000..148bdb50b2 --- /dev/null +++ b/doc/goal_todo.md @@ -0,0 +1,19 @@ +Done (has documentation) +- `e` +- `:exit` +- `asm-file`, `m`, `ml` +- `listen-to-target`, `reset-target`, `:status`, `lt`, `r` + +Done (needs documentation) +- `top-level` +- `begin` +- `seval` +- `#cond`, `#when`, `#unless` + +- Macro System + + + + +Todo +- Type System diff --git a/game/kernel/asm_funcs.asm b/game/kernel/asm_funcs.asm index 2b41efe59d..c43173753a 100644 --- a/game/kernel/asm_funcs.asm +++ b/game/kernel/asm_funcs.asm @@ -121,9 +121,9 @@ _call_goal_asm_linux: ;; set GOAL function pointer mov r13, rcx ;; offset - mov r15, r8 + mov r14, r8 ;; symbol table - mov r14, r9 + mov r15, r9 ;; call GOAL by function pointer call r13 @@ -165,8 +165,8 @@ _call_goal_asm_win32: mov rsi, rdx ;; rsi is GOAL second argument, rdx is windows second argument mov rdx, r8 ;; rdx is GOAL third argument, r8 is windows third argument mov r13, r9 ;; r13 is GOAL fp, r9 is windows fourth argument - mov r15, [rsp + 144] ;; symbol table - mov r14, [rsp + 152] ;; offset + mov r14, [rsp + 144] ;; symbol table + mov r15, [rsp + 152] ;; offset call r13 diff --git a/game/kernel/kboot.cpp b/game/kernel/kboot.cpp index 9f6f6de06d..b8605b36f2 100644 --- a/game/kernel/kboot.cpp +++ b/game/kernel/kboot.cpp @@ -12,6 +12,7 @@ #include "kscheme.h" #include "ksocket.h" #include "klisten.h" +#include "kprint.h" #ifdef _WIN32 #include "Windows.h" @@ -133,7 +134,24 @@ void KernelCheckAndDispatch() { auto old_listener = ListenerFunction->value; // dispatch the kernel //(**kernel_dispatcher)(); - call_goal(Ptr(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem); + + // todo remove. this is added while KERNEL.CGO is broken. + if (MasterUseKernel) { + call_goal(Ptr(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem); + } else { + if (ListenerFunction->value != s7.offset) { + auto cptr = Ptr(ListenerFunction->value).c(); + for (int i = 0; i < 40; i++) { + printf("%x ", cptr[i]); + } + printf("\n"); + auto result = + call_goal(Ptr(ListenerFunction->value), 0, 0, 0, s7.offset, g_ee_main_mem); + cprintf("%ld\n", result); + ListenerFunction->value = s7.offset; + } + } + // TODO-WINDOWS #ifdef __linux__ ClearPending(); diff --git a/game/kernel/kboot.h b/game/kernel/kboot.h index fd53697f1b..3ac75533e8 100644 --- a/game/kernel/kboot.h +++ b/game/kernel/kboot.h @@ -73,4 +73,6 @@ void KernelCheckAndDispatch(); */ void KernelShutdown(); +constexpr bool MasterUseKernel = false; + #endif // RUNTIME_KBOOT_H diff --git a/game/kernel/kmachine.cpp b/game/kernel/kmachine.cpp index 4752b90b93..6a030104f8 100644 --- a/game/kernel/kmachine.cpp +++ b/game/kernel/kmachine.cpp @@ -601,7 +601,8 @@ void InitMachineScheme() { intern_from_c("*kernel-boot-level*")->value = intern_from_c(DebugBootLevel).offset; } - if (DiskBoot) { + // todo remove MasterUseKernel + if (DiskBoot && MasterUseKernel) { *EnableMethodSet = (*EnableMethodSet) + 1; load_and_link_dgo_from_c("game", kglobalheap, LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN, diff --git a/game/kernel/kscheme.cpp b/game/kernel/kscheme.cpp index d2c41a96cb..bec0f1be12 100644 --- a/game/kernel/kscheme.cpp +++ b/game/kernel/kscheme.cpp @@ -958,7 +958,10 @@ uint64_t _call_goal_asm_win32(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, * Wrapper around _call_goal_asm for calling a GOAL function from C. */ u64 call_goal(Ptr f, u64 a, u64 b, u64 c, u64 st, void* offset) { - auto st_ptr = (void*)((uint8_t*)(offset) + st); + // auto st_ptr = (void*)((uint8_t*)(offset) + st); updated for the new compiler! + void* st_ptr = (void*)st; + printf("st is 0x%x\n", st); + void* fptr = f.c(); #ifdef __linux__ return _call_goal_asm_linux(a, b, c, fptr, st_ptr, offset); @@ -1828,25 +1831,28 @@ s32 InitHeapAndSymbol() { intern_from_c("*boot-video-mode*")->value = 0; // load the kernel! - method_set_symbol->value++; - load_and_link_dgo_from_c("kernel", kglobalheap, - LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN, - 0x400000); - method_set_symbol->value--; + // todo, remove MasterUseKernel + if (MasterUseKernel) { + method_set_symbol->value++; + load_and_link_dgo_from_c("kernel", kglobalheap, + LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN, + 0x400000); + method_set_symbol->value--; - // check the kernel version! - auto kernel_version = intern_from_c("*kernel-version*")->value; - if (!kernel_version || ((kernel_version >> 0x13) != KERNEL_VERSION_MAJOR)) { - MsgErr("\n"); - MsgErr( - "dkernel: compiled C kernel version is %d.%d but the goal kernel is %d.%d\n\tfrom the " - "goal> prompt (:mch) then mkee your kernel in linux.\n", - KERNEL_VERSION_MAJOR, KERNEL_VERSION_MINOR, kernel_version >> 0x13, - (kernel_version >> 3) & 0xffff); - return -1; - } else { - printf("Got correct kernel version %d.%d\n", kernel_version >> 0x13, - (kernel_version >> 3) & 0xffff); + // check the kernel version! + auto kernel_version = intern_from_c("*kernel-version*")->value; + if (!kernel_version || ((kernel_version >> 0x13) != KERNEL_VERSION_MAJOR)) { + MsgErr("\n"); + MsgErr( + "dkernel: compiled C kernel version is %d.%d but the goal kernel is %d.%d\n\tfrom the " + "goal> prompt (:mch) then mkee your kernel in linux.\n", + KERNEL_VERSION_MAJOR, KERNEL_VERSION_MINOR, kernel_version >> 0x13, + (kernel_version >> 3) & 0xffff); + return -1; + } else { + printf("Got correct kernel version %d.%d\n", kernel_version >> 0x13, + (kernel_version >> 3) & 0xffff); + } } // setup deci2count for message counter. diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index af0e1fad03..09f71dab94 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -6,4 +6,48 @@ ;; compile, color, load and save a file (defmacro ml (file) `(asm-file ,file :color :load :write) + ) + +(defmacro e () + `(:exit) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CONDITIONAL COMPILATION +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmacro #when (clause &rest body) + `(#cond (,clause ,@body)) + ) + +(defmacro #unless (clause &rest body) + `(#cond ((not ,clause) ,@body)) + ) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TARGET CONTROL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmacro lt (&rest args) + ;; shortcut for listen-to-target. also sends a :status command to make sure + ;; all buffers on the target are flushed. + `(begin + (listen-to-target ,@args) + (:status) + ) + ) + +(defmacro r (&rest args) + ;; shortcut to completely reset the target and connect, regardless of current state + `(begin + ;; connect, so we can send reset. if we're already connected, does nothing + (listen-to-target ,@args) + ;; send a reset message, disconnecting us + (reset-target) + ;; establish connection again + (listen-to-target ,@args) + ;; flush buffers + (:status) + ) ) \ No newline at end of file diff --git a/goal_src/test/test-conditional-compilation-1.gc b/goal_src/test/test-conditional-compilation-1.gc new file mode 100644 index 0000000000..27e30228fb --- /dev/null +++ b/goal_src/test/test-conditional-compilation-1.gc @@ -0,0 +1,12 @@ +;; test the use of #cond to evaluate goos expressions at compile time + +(#cond + ((> 2 (+ 2 1)) + 1 + (invalid-code) + ) + + ((< 2 (+ 1 2)) + 3 + ) + ) \ No newline at end of file diff --git a/goal_src/test/test-define-1.gc b/goal_src/test/test-define-1.gc new file mode 100644 index 0000000000..2dfc9036fe --- /dev/null +++ b/goal_src/test/test-define-1.gc @@ -0,0 +1,10 @@ +(define first-var 1) +(define second-var 2) +(define first-var 12) + +(begin + (define first-var 13) + (define second-var 12) + (define first-var 17) + second-var + first-var) diff --git a/goal_src/test/test-get-symbol-1.gc b/goal_src/test/test-get-symbol-1.gc new file mode 100644 index 0000000000..3ac5f6a806 --- /dev/null +++ b/goal_src/test/test-get-symbol-1.gc @@ -0,0 +1 @@ +'#f \ No newline at end of file diff --git a/goal_src/test/test-get-symbol-2.gc b/goal_src/test/test-get-symbol-2.gc new file mode 100644 index 0000000000..8a7c3981c4 --- /dev/null +++ b/goal_src/test/test-get-symbol-2.gc @@ -0,0 +1 @@ +'#t \ No newline at end of file diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 63cdb110eb..1a26b2ec14 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(compiler compiler/compilation/CompilerControl.cpp compiler/compilation/Block.cpp compiler/compilation/Macro.cpp + compiler/compilation/Define.cpp compiler/Util.cpp logger/Logger.cpp regalloc/IRegister.cpp diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index d9d062e9b1..a728753176 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -19,7 +19,6 @@ Compiler::Compiler() { } void Compiler::execute_repl() { - m_listener.connect_to_target(); // todo, remove while (!m_want_exit) { try { // 1). get a line from the user (READ) @@ -86,6 +85,10 @@ FileEnv* Compiler::compile_object_file(const std::string& name, file_env->add_top_level_function( compile_top_level_function("top-level", std::move(code), compilation_env)); + if (!allow_emit && !file_env->is_empty()) { + throw std::runtime_error("Compilation generated code, but wasn't supposed to"); + } + return file_env; } @@ -160,33 +163,45 @@ std::vector Compiler::codegen_object_file(FileEnv* env) { } 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; + try { + 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!"); } } - 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"); + 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(); + } catch (std::exception& e) { + fmt::print("[Compiler] Failed to compile test program {}: {}\n", source_code, e.what()); + return {}; } - return m_listener.stop_recording_messages(); } void Compiler::shutdown_target() { if (m_listener.is_connected()) { m_listener.send_reset(true); } +} + +void Compiler::typecheck(const goos::Object& form, + const TypeSpec& expected, + const TypeSpec& actual, + const std::string& error_message) { + m_ts.typecheck(expected, actual, error_message, true, true); } \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 16722ea814..9f49720f34 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -35,6 +35,9 @@ class Compiler { 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); + Val* compile_symbol(const goos::Object& form, Env* env); + Val* compile_get_symbol_value(const std::string& name, Env* env); + SymbolVal* compile_get_sym_obj(const std::string& name, Env* env); void color_object_file(FileEnv* env); std::vector codegen_object_file(FileEnv* env); @@ -48,6 +51,8 @@ class Compiler { const std::vector>& unnamed, const std::unordered_map>>& named); + std::string as_string(const goos::Object& o); + std::string symbol_string(const goos::Object& o); TypeSystem m_ts; std::unique_ptr m_global_env = nullptr; @@ -55,12 +60,36 @@ class Compiler { bool m_want_exit = false; listener::Listener m_listener; goos::Interpreter m_goos; + std::unordered_map m_symbol_types; + std::unordered_map, goos::Object> m_global_constants; + std::unordered_map, LambdaVal*> m_inlineable_functions; + + void typecheck(const goos::Object& form, + const TypeSpec& expected, + const TypeSpec& actual, + const std::string& error_message = ""); public: - Val* compile_exit(const goos::Object& form, const goos::Object& rest, Env* env); - Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env); + // Atoms + + // Block Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env); + + // CompilerControl Val* compile_seval(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_exit(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_asm_file(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_listen_to_target(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_reset_target(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_poke(const goos::Object& form, const goos::Object& rest, Env* env); + + // Define + Val* compile_define(const goos::Object& form, const goos::Object& rest, Env* env); + + // Macro + Val* compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_quote(const goos::Object& form, const goos::Object& rest, Env* env); }; #endif // JAK_COMPILER_H diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index 8a3c7baad6..c81671c6ea 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -88,7 +88,6 @@ class FileEnv : public Env { void debug_print_tl(); const std::vector>& functions() { return m_functions; } - // todo - is_empty bool is_empty(); ~FileEnv() = default; @@ -115,7 +114,7 @@ class DeclareEnv : public Env { bool inline_by_default = false; // if a function, inline when possible? bool save_code = true; // if a function, should we save the code? bool allow_inline = false; // should we allow the user to use this an inline function - } m_settings; + } settings; }; class FunctionEnv : public DeclareEnv { diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 71e17bf7fc..33afba90b6 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -1,4 +1,6 @@ #include "IR.h" + +#include #include "goalc/emitter/IGen.h" using namespace emitter; @@ -11,6 +13,9 @@ Register get_reg(const RegVal* rv, const AllocationResult& allocs, emitter::IR_R } } // namespace +/////////// +// Return +/////////// IR_Return::IR_Return(const RegVal* return_reg, const RegVal* value) : m_return_reg(return_reg), m_value(value) {} std::string IR_Return::print() { @@ -52,6 +57,9 @@ void IR_Return::do_codegen(emitter::ObjectGenerator* gen, } } +///////////////////// +// LoadConstant64 +///////////////////// IR_LoadConstant64::IR_LoadConstant64(const RegVal* dest, u64 value) : m_dest(dest), m_value(value) {} @@ -70,4 +78,94 @@ void IR_LoadConstant64::do_codegen(emitter::ObjectGenerator* gen, 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); +} + +///////////////////// +// LoadSymbolPointer +///////////////////// +IR_LoadSymbolPointer::IR_LoadSymbolPointer(const RegVal* dest, std::string name) + : m_dest(dest), m_name(std::move(name)) {} + +std::string IR_LoadSymbolPointer::print() { + return fmt::format("mov-symptr {}, '{}", m_dest->print(), m_name); +} + +RegAllocInstr IR_LoadSymbolPointer::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + return rai; +} + +void IR_LoadSymbolPointer::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto dest_reg = get_reg(m_dest, allocs, irec); + // todo, could be single lea opcode + gen->add_instr(IGen::mov_gpr64_gpr64(dest_reg, gRegInfo.get_st_reg()), irec); + auto add = gen->add_instr(IGen::add_gpr64_imm32s(dest_reg, 0x0afecafe), irec); + gen->link_instruction_symbol_ptr(add, m_name); +} + +///////////////////// +// SetSymbolValue +///////////////////// + +IR_SetSymbolValue::IR_SetSymbolValue(const SymbolVal* dest, const RegVal* src) + : m_dest(dest), m_src(src) {} + +std::string IR_SetSymbolValue::print() { + return fmt::format("mov '{}, {}", m_dest->name(), m_src->print()); +} + +RegAllocInstr IR_SetSymbolValue::to_rai() { + RegAllocInstr rai; + rai.read.push_back(m_src->ireg()); + return rai; +} + +void IR_SetSymbolValue::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto src_reg = get_reg(m_src, allocs, irec); + auto instr = + gen->add_instr(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s32( + gRegInfo.get_st_reg(), gRegInfo.get_offset_reg(), src_reg, 0x0badbeef), + irec); + gen->link_instruction_symbol_mem(instr, m_dest->name()); +} + +///////////////////// +// GetSymbolValue +///////////////////// + +IR_GetSymbolValue::IR_GetSymbolValue(const RegVal* dest, const SymbolVal* src, bool sext) + : m_dest(dest), m_src(src), m_sext(sext) {} + +std::string IR_GetSymbolValue::print() { + return fmt::format("mov {}, '{}", m_dest->print(), m_src->name()); +} + +RegAllocInstr IR_GetSymbolValue::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + return rai; +} + +void IR_GetSymbolValue::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto dst_reg = get_reg(m_dest, allocs, irec); + if (m_sext) { + auto instr = + gen->add_instr(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s32( + dst_reg, gRegInfo.get_st_reg(), gRegInfo.get_offset_reg(), 0x0badbeef), + irec); + gen->link_instruction_symbol_mem(instr, m_src->name()); + } else { + auto instr = + gen->add_instr(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s32( + dst_reg, gRegInfo.get_st_reg(), gRegInfo.get_offset_reg(), 0x0badbeef), + irec); + gen->link_instruction_symbol_mem(instr, m_src->name()); + } } \ No newline at end of file diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index be5e6bd523..b06845e0b3 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -56,4 +56,47 @@ class IR_LoadConstant64 : public IR { u64 m_value = 0; }; +class IR_LoadSymbolPointer : public IR { + public: + IR_LoadSymbolPointer(const RegVal* dest, std::string name); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + + protected: + const RegVal* m_dest = nullptr; + std::string m_name; +}; + +class IR_SetSymbolValue : public IR { + public: + IR_SetSymbolValue(const SymbolVal* dest, const RegVal* src); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + + protected: + const SymbolVal* m_dest = nullptr; + const RegVal* m_src = nullptr; +}; + +class IR_GetSymbolValue : public IR { + public: + IR_GetSymbolValue(const RegVal* dest, const SymbolVal* src, bool sext); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + + protected: + const RegVal* m_dest = nullptr; + const SymbolVal* m_src = nullptr; + bool m_sext = false; +}; + #endif // JAK_IR_H diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index ed40906c93..435055de1a 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -95,4 +95,12 @@ void Compiler::for_each_in_list(const goos::Object& list, if (!iter->is_empty_list()) { throw_compile_error(list, "invalid list in for_each_in_list"); } +} + +std::string Compiler::as_string(const goos::Object& o) { + return o.as_string()->data; +} + +std::string Compiler::symbol_string(const goos::Object& o) { + return o.as_symbol()->name; } \ No newline at end of file diff --git a/goalc/compiler/Val.cpp b/goalc/compiler/Val.cpp index c0c4f2e72e..06c3e20302 100644 --- a/goalc/compiler/Val.cpp +++ b/goalc/compiler/Val.cpp @@ -5,7 +5,7 @@ /*! * Fallback to_gpr if a more optimized one is not provided. */ -const RegVal* Val::to_gpr(FunctionEnv* fe) const { +RegVal* Val::to_gpr(Env* fe) { auto rv = to_reg(fe); if (rv->ireg().kind == emitter::RegKind::GPR) { return rv; @@ -17,17 +17,17 @@ const RegVal* Val::to_gpr(FunctionEnv* fe) const { /*! * Fallback to_xmm if a more optimized one is not provided. */ -const RegVal* Val::to_xmm(FunctionEnv* fe) const { +RegVal* Val::to_xmm(Env* fe) { (void)fe; throw std::runtime_error("Val::to_xmm NYI"); // todo } -const RegVal* RegVal::to_reg(FunctionEnv* fe) const { +RegVal* RegVal::to_reg(Env* fe) { (void)fe; return this; } -const RegVal* RegVal::to_gpr(FunctionEnv* fe) const { +RegVal* RegVal::to_gpr(Env* fe) { (void)fe; if (m_ireg.kind == emitter::RegKind::GPR) { return this; @@ -36,7 +36,7 @@ const RegVal* RegVal::to_gpr(FunctionEnv* fe) const { } } -const RegVal* RegVal::to_xmm(FunctionEnv* fe) const { +RegVal* RegVal::to_xmm(Env* fe) { (void)fe; if (m_ireg.kind == emitter::RegKind::XMM) { return this; @@ -45,8 +45,20 @@ const RegVal* RegVal::to_xmm(FunctionEnv* fe) const { } } -const RegVal* IntegerConstantVal::to_reg(FunctionEnv* fe) const { +RegVal* IntegerConstantVal::to_reg(Env* fe) { auto rv = fe->make_gpr(m_ts); fe->emit(std::make_unique(rv, m_value)); return rv; +} + +RegVal* SymbolVal::to_reg(Env* fe) { + auto re = fe->make_gpr(m_ts); + fe->emit(std::make_unique(re, m_name)); + return re; +} + +RegVal* SymbolValueVal::to_reg(Env* fe) { + auto re = fe->make_gpr(m_ts); + fe->emit(std::make_unique(re, m_sym, m_sext)); + return re; } \ No newline at end of file diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index 9ee9b86f27..1313aa6427 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -15,6 +15,7 @@ #include "Lambda.h" class RegVal; +class Env; class FunctionEnv; /*! @@ -31,12 +32,12 @@ class Val { } virtual std::string print() const = 0; - virtual const RegVal* to_reg(FunctionEnv* fe) const { + virtual RegVal* to_reg(Env* fe) { (void)fe; throw std::runtime_error("to_reg called on invalid Val: " + print()); } - virtual const RegVal* to_gpr(FunctionEnv* fe) const; - virtual const RegVal* to_xmm(FunctionEnv* fe) const; + virtual RegVal* to_gpr(Env* fe); + virtual RegVal* to_xmm(Env* fe); const TypeSpec& type() const { return m_ts; } void set_type(TypeSpec ts) { m_ts = std::move(ts); } @@ -64,9 +65,9 @@ class RegVal : public Val { bool is_register() const override { return true; } IRegister ireg() const override { return m_ireg; } std::string print() const override { return m_ireg.to_string(); }; - const RegVal* to_reg(FunctionEnv* fe) const override; - const RegVal* to_gpr(FunctionEnv* fe) const override; - const RegVal* to_xmm(FunctionEnv* fe) const override; + RegVal* to_reg(Env* fe) override; + RegVal* to_gpr(Env* fe) override; + RegVal* to_xmm(Env* fe) override; protected: IRegister m_ireg; @@ -79,13 +80,27 @@ class RegVal : public Val { class SymbolVal : public Val { public: SymbolVal(std::string name, TypeSpec ts) : Val(std::move(ts)), m_name(std::move(name)) {} - const std::string& name() { return m_name; } + const std::string& name() const { return m_name; } std::string print() const override { return "<" + m_name + ">"; } + RegVal* to_reg(Env* fe) override; protected: std::string m_name; }; +class SymbolValueVal : public Val { + public: + SymbolValueVal(const SymbolVal* sym, TypeSpec ts, bool sext) + : Val(std::move(ts)), m_sym(sym), m_sext(sext) {} + const std::string& name() const { return m_sym->name(); } + std::string print() const override { return "[<" + name() + ">]"; } + RegVal* to_reg(Env* fe) override; + + protected: + const SymbolVal* m_sym = nullptr; + bool m_sext = false; +}; + /*! * A Val representing a GOAL lambda. It can be a "real" x86-64 function, in which case the * FunctionEnv is set. Otherwise, just contains a Lambda. @@ -94,10 +109,10 @@ class LambdaVal : public Val { public: LambdaVal(TypeSpec ts, Lambda lam) : Val(ts), m_lam(lam) {} std::string print() const override { return "lambda-" + m_lam.debug_name; } + FunctionEnv* func = nullptr; protected: Lambda m_lam; - FunctionEnv* fe = nullptr; }; // Static @@ -111,7 +126,7 @@ class IntegerConstantVal : public Val { public: IntegerConstantVal(TypeSpec ts, s64 value) : Val(ts), m_value(value) {} std::string print() const override { return "integer-constant-" + std::to_string(m_value); } - const RegVal* to_reg(FunctionEnv* fe) const override; + RegVal* to_reg(Env* fe) override; protected: s64 m_value = -1; diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 7fd4039d52..aa66b84e2d 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -27,12 +27,15 @@ static const std::unordered_map< // // COMPILER CONTROL // {"gs", &Compiler::compile_gs}, {":exit", &Compiler::compile_exit}, - // {"asm-file", &Compiler::compile_asm_file}, + {"asm-file", &Compiler::compile_asm_file}, + {"listen-to-target", &Compiler::compile_listen_to_target}, + {"reset-target", &Compiler::compile_reset_target}, + {":status", &Compiler::compile_poke}, // {"test", &Compiler::compile_test}, // {"in-package", &Compiler::compile_in_package}, // // // CONDITIONAL COMPILATION - // {"#cond", &Compiler::compile_gscond}, + {"#cond", &Compiler::compile_gscond}, // {"defglobalconstant", &Compiler::compile_defglobalconstant}, {"seval", &Compiler::compile_seval}, // @@ -41,7 +44,7 @@ static const std::unordered_map< // {"when-goto", &Compiler::compile_when_goto}, // // // DEFINITION - // {"define", &Compiler::compile_define}, + {"define", &Compiler::compile_define}, // {"define-extern", &Compiler::compile_define_extern}, // {"set!", &Compiler::compile_set}, // {"defun-extern", &Compiler::compile_defun_extern}, @@ -70,7 +73,7 @@ static const std::unordered_map< // // // MACRO // {"print-type", &Compiler::compile_print_type}, - // {"quote", &Compiler::compile_quote}, + {"quote", &Compiler::compile_quote}, // {"defconstant", &Compiler::compile_defconstant}, // // {"declare", &Compiler::compile_declare}, @@ -126,9 +129,7 @@ static const std::unordered_map< // // // - // {"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}, @@ -140,6 +141,8 @@ Val* Compiler::compile(const goos::Object& code, Env* env) { return compile_pair(code, env); case goos::ObjectType::INTEGER: return compile_integer(code, env); + case goos::ObjectType::SYMBOL: + return compile_symbol(code, env); default: ice("Don't know how to compile " + code.print()); } @@ -180,3 +183,36 @@ 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); } + +SymbolVal* Compiler::compile_get_sym_obj(const std::string& name, Env* env) { + auto fe = get_parent_env_of_type(env); + return fe->alloc_val(name, m_ts.make_typespec("symbol")); +} + +Val* Compiler::compile_symbol(const goos::Object& form, Env* env) { + auto name = symbol_string(form); + + if (name == "none") { + return get_none(); + } + + // todo mlet + // todo lexical + // todo global constant + + return compile_get_symbol_value(name, env); +} + +Val* Compiler::compile_get_symbol_value(const std::string& name, Env* env) { + auto existing_symbol = m_symbol_types.find(name); + if (existing_symbol == m_symbol_types.end()) { + throw std::runtime_error("The symbol " + name + " was not defined"); + } + + auto ts = existing_symbol->second; + auto sext = m_ts.lookup_type(ts)->get_load_signed(); + auto fe = get_parent_env_of_type(env); + auto sym = fe->alloc_val(name, m_ts.make_typespec("symbol")); + auto re = fe->alloc_val(sym, ts, sext); + return re; +} \ No newline at end of file diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 2d83f48ffd..ae85bc07e1 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -1,5 +1,7 @@ #include "goalc/compiler/Compiler.h" #include "goalc/compiler/IR.h" +#include "goalc/util/Timer.h" +#include "goalc/util/file_io.h" Val* Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, Env* env) { (void)env; @@ -22,4 +24,140 @@ Val* Compiler::compile_seval(const goos::Object& form, const goos::Object& rest, throw_compile_error(form, std::string("seval error: ") + e.what()); } return get_none(); +} + +Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + int i = 0; + std::string filename; + bool load = false; + bool color = false; + bool write = false; + bool no_code = false; + + std::vector> timing; + Timer total_timer; + + for_each_in_list(rest, [&](const goos::Object& o) { + if (i == 0) { + filename = as_string(o); + } else { + auto setting = symbol_string(o); + if (setting == ":load") { + load = true; + } else if (setting == ":color") { + color = true; + } else if (setting == ":write") { + write = true; + } else if (setting == ":no-code") { + no_code = true; + } else { + throw_compile_error(form, "invalid option " + setting + " in asm-file form"); + } + } + i++; + }); + + Timer reader_timer; + auto code = m_goos.reader.read_from_file(filename); + timing.emplace_back("read", reader_timer.getMs()); + + Timer compile_timer; + std::string obj_file_name = basename(filename.c_str()); + obj_file_name = obj_file_name.substr(0, obj_file_name.find_last_of('.')); + auto obj_file = compile_object_file(obj_file_name, code, !no_code); + timing.emplace_back("compile", compile_timer.getMs()); + + if (color) { + Timer color_timer; + color_object_file(obj_file); + timing.emplace_back("color", color_timer.getMs()); + + Timer codegen_timer; + auto data = codegen_object_file(obj_file); + timing.emplace_back("codegen", codegen_timer.getMs()); + + if (load) { + if (m_listener.is_connected()) { + m_listener.send_code(data); + } else { + printf("WARNING - couldn't load because listener isn't connected\n"); + } + } + + if (write) { + // auto output_dir = as_string(get_constant_or_error(form, "*compiler-output-path*")); + // todo, change extension based on v3/v4 + auto output_name = m_goos.reader.get_source_dir() + "/out/" + obj_file_name + ".o"; + util::write_binary_file(output_name, (void*)data.data(), data.size()); + } + } else { + if (load) { + printf("WARNING - couldn't load because coloring is not enabled\n"); + } + + if (write) { + printf("WARNING - couldn't write because coloring is not enabled\n"); + } + } + + // if(truthy(get_config("print-asm-file-time"))) { + for (auto& e : timing) { + printf(" %12s %4.2f\n", e.first.c_str(), e.second); + } + // } + + return get_none(); +} + +Val* Compiler::compile_listen_to_target(const goos::Object& form, + const goos::Object& rest, + Env* env) { + (void)env; + std::string ip = "127.0.0.1"; + int port = 8112; + bool got_port = false, got_ip = false; + + for_each_in_list(rest, [&](const goos::Object& o) { + if (o.is_string()) { + if (got_ip) { + throw_compile_error(form, "got multiple strings!"); + } + got_ip = true; + ip = o.as_string()->data; + } else if (o.is_int()) { + if (got_port) { + throw_compile_error(form, "got multiple ports!"); + } + got_port = true; + port = o.integer_obj.value; + } else { + throw_compile_error(form, "invalid argument to listen-to-target"); + } + }); + + m_listener.connect_to_target(30, ip, port); + return get_none(); +} + +Val* Compiler::compile_reset_target(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + bool shutdown = false; + for_each_in_list(rest, [&](const goos::Object& o) { + if (o.is_symbol() && symbol_string(o) == ":shutdown") { + shutdown = true; + } else { + throw_compile_error(form, "invalid argument to reset-target"); + } + }); + m_listener.send_reset(shutdown); + return get_none(); +} + +Val* Compiler::compile_poke(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + auto args = get_va(form, rest); + va_check(form, args, {}, {}); + m_listener.send_poke(); + return get_none(); } \ No newline at end of file diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp new file mode 100644 index 0000000000..336d711a7b --- /dev/null +++ b/goalc/compiler/compilation/Define.cpp @@ -0,0 +1,41 @@ +#include "goalc/compiler/Compiler.h" + +Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {}); + auto& sym = args.unnamed.at(0); + auto& val = args.unnamed.at(1); + + // check we aren't duplicated a name as both a symbol and global constant + auto global_constant = m_global_constants.find(sym.as_symbol()); + if (global_constant != m_global_constants.end()) { + throw_compile_error( + form, "it is illegal to define a GOAL symbol with the same name as a GOAL global constant"); + } + + auto fe = get_parent_env_of_type(env); + auto sym_val = fe->alloc_val(symbol_string(sym), m_ts.make_typespec("symbol")); + auto compiled_val = compile_error_guard(val, env); + auto as_lambda = dynamic_cast(compiled_val); + if (as_lambda) { + // there are two cases in which we save a function body that is passed to a define: + // 1. It generated code [so went through the compiler] and the allow_inline flag is set. + // 2. It didn't generate code [so explicitly with :inline-only lambdas] + // The third case - immediate lambdas - don't get passed to a define, + // so this won't cause those to live for longer than they should + if ((as_lambda->func && as_lambda->func->settings.allow_inline) || !as_lambda->func) { + m_inlineable_functions[sym.as_symbol()] = as_lambda; + } + } + + auto in_gpr = compiled_val->to_gpr(fe); + auto existing_type = m_symbol_types.find(sym.as_symbol()->name); + if (existing_type == m_symbol_types.end()) { + m_symbol_types[sym.as_symbol()->name] = in_gpr->type(); + } else { + typecheck(form, existing_type->second, in_gpr->type(), "define on existing symbol"); + } + + fe->emit(std::make_unique(sym_val, in_gpr)); + return in_gpr; +} diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index 275207c197..a5d6b6aa27 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -34,4 +34,56 @@ Val* Compiler::compile_goos_macro(const goos::Object& o, auto goos_result = m_goos.eval_list_return_last(macro->body, macro->body, mac_env); m_goos.goal_to_goos.reset(); return compile_error_guard(goos_result, env); +} + +Val* Compiler::compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env) { + if (!rest.is_pair()) { + throw_compile_error(form, "#cond must have at least one clause, which must be a form"); + } + Val* result = nullptr; + + Object lst = rest; + for (;;) { + if (lst.is_pair()) { + Object current_case = lst.as_pair()->car; + if (!current_case.is_pair()) { + throw_compile_error(lst, "Bad case in #cond"); + } + + // check condition: + Object condition_result = + m_goos.eval_with_rewind(current_case.as_pair()->car, m_goos.global_environment.as_env()); + if (m_goos.truthy(condition_result)) { + if (current_case.as_pair()->cdr.is_empty_list()) { + return get_none(); + } + // got a match! + result = get_none(); + + for_each_in_list(current_case.as_pair()->cdr, + [&](Object o) { result = compile_error_guard(o, env); }); + return result; + } else { + // no match, continue. + lst = lst.as_pair()->cdr; + } + } else if (lst.is_empty_list()) { + return get_none(); + } else { + throw_compile_error(form, "malformed #cond"); + } + } +} + +Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + va_check(form, args, {{}}, {}); + auto thing = args.unnamed.at(0); + switch (thing.type) { + case goos::ObjectType::SYMBOL: + return compile_get_sym_obj(thing.as_symbol()->name, env); + default: + throw_compile_error(form, "Can't quote this"); + } + return get_none(); } \ No newline at end of file diff --git a/goalc/emitter/ObjectFileData.cpp b/goalc/emitter/ObjectFileData.cpp index b18e1468cc..cf338f4b8b 100644 --- a/goalc/emitter/ObjectFileData.cpp +++ b/goalc/emitter/ObjectFileData.cpp @@ -14,6 +14,11 @@ std::vector ObjectFileData::to_vector() const { // data (code + static objects, by segment) for (int seg = N_SEG; seg-- > 0;) { result.insert(result.end(), segment_data[seg].begin(), segment_data[seg].end()); + // printf("seg %d data\n", seg); + // for (auto x : segment_data[seg]) { + // printf("%02x ", x); + // } + // printf("\n"); } return result; } diff --git a/goalc/goos/Interpreter.cpp b/goalc/goos/Interpreter.cpp index 285f607fbe..7e1c411315 100644 --- a/goalc/goos/Interpreter.cpp +++ b/goalc/goos/Interpreter.cpp @@ -805,11 +805,9 @@ Object Interpreter::eval_quasiquote(const Object& form, return quasiquote_helper(rest.as_pair()->car, env); } -namespace { -bool truthy(const Object& o) { +bool Interpreter::truthy(const Object& o) { return !(o.is_symbol() && o.as_symbol()->name == "#f"); } -} // namespace /*! * Scheme "cond" statement - tested by integrated tests only. diff --git a/goalc/goos/Interpreter.h b/goalc/goos/Interpreter.h index d58f46670e..3d58a5c369 100644 --- a/goalc/goos/Interpreter.h +++ b/goalc/goos/Interpreter.h @@ -32,6 +32,7 @@ class Interpreter { Object eval_list_return_last(const Object& form, Object rest, const std::shared_ptr& env); + bool truthy(const Object& o); Reader reader; Object global_environment; diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index f971dc35b8..961a3cb60d 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -51,9 +51,10 @@ bool Listener::is_connected() const { * Attempt to connect to the target. If the target isn't running, this should fail quickly. * Returns true if successfully connected. */ -bool Listener::connect_to_target(const std::string& ip, int port) { +bool Listener::connect_to_target(int n_tries, const std::string& ip, int port) { if (m_connected) { - throw std::runtime_error("attempted a Listener::connect_to_target when already connected!"); + printf("already connected!\n"); + return true; } if (socket_fd >= 0) { @@ -100,12 +101,21 @@ bool Listener::connect_to_target(const std::string& ip, int port) { } // connect! - int rv = connect(socket_fd, (sockaddr*)&server_address, sizeof(server_address)); + int rv, i; + for (i = 0; i < n_tries; i++) { + rv = connect(socket_fd, (sockaddr*)&server_address, sizeof(server_address)); + if (rv >= 0) { + break; + } + usleep(100000); + } if (rv < 0) { printf("[Listener] Failed to connect\n"); close(socket_fd); socket_fd = -1; return false; + } else { + printf("[Listener] Socket connected established! (took %d tries)\n", i); } // get the GOAL version number, to make sure we connected to the right thing @@ -303,6 +313,24 @@ void Listener::send_reset(bool shutdown) { printf("closed connection to target\n"); } +void Listener::send_poke() { + if (!m_connected) { + printf("Not connected, so cannot poke 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.src = 'H'; + header->deci2_header.dst = 'E'; + header->msg_size = 0; + header->ltt_msg_kind = LTT_MSG_POKE; + header->u6 = 0; + header->u8 = 0; + send_buffer(sizeof(ListenerMessageHeader)); +} + void Listener::send_buffer(int sz) { int wrote = 0; diff --git a/goalc/listener/Listener.h b/goalc/listener/Listener.h index 38c453c6d4..b26b619944 100644 --- a/goalc/listener/Listener.h +++ b/goalc/listener/Listener.h @@ -19,11 +19,14 @@ class Listener { static constexpr int BUFFER_SIZE = 32 * 1024 * 1024; Listener(); ~Listener(); - bool connect_to_target(const std::string& ip = "127.0.0.1", int port = DECI2_PORT); + bool connect_to_target(int n_tries = 1, + const std::string& ip = "127.0.0.1", + int port = DECI2_PORT); void record_messages(ListenerMessageKind kind); std::vector stop_recording_messages(); bool is_connected() const; void send_reset(bool shutdown); + void send_poke(); void disconnect(); void send_code(std::vector& code); bool most_recent_send_was_acked() { return got_ack; } diff --git a/goalc/util/CMakeLists.txt b/goalc/util/CMakeLists.txt index 609a1fbb68..fdca90d0c6 100644 --- a/goalc/util/CMakeLists.txt +++ b/goalc/util/CMakeLists.txt @@ -1 +1 @@ -add_library(util SHARED text_util.cpp file_io.cpp) \ No newline at end of file +add_library(util SHARED text_util.cpp file_io.cpp Timer.cpp) \ No newline at end of file diff --git a/goalc/util/Timer.cpp b/goalc/util/Timer.cpp new file mode 100644 index 0000000000..4ac44ab25c --- /dev/null +++ b/goalc/util/Timer.cpp @@ -0,0 +1,54 @@ +#include "Timer.h" + +#ifdef _WIN32 +#include +#define MS_PER_SEC 1000ULL // MS = milliseconds +#define US_PER_MS 1000ULL // US = microseconds +#define HNS_PER_US 10ULL // HNS = hundred-nanoseconds (e.g., 1 hns = 100 ns) +#define NS_PER_US 1000ULL + +#define HNS_PER_SEC (MS_PER_SEC * US_PER_MS * HNS_PER_US) +#define NS_PER_HNS (100ULL) // NS = nanoseconds +#define NS_PER_SEC (MS_PER_SEC * US_PER_MS * NS_PER_US) + +int Timer::clock_gettime_monotonic(struct timespec* tv) { + static LARGE_INTEGER ticksPerSec; + LARGE_INTEGER ticks; + double seconds; + + if (!ticksPerSec.QuadPart) { + QueryPerformanceFrequency(&ticksPerSec); + if (!ticksPerSec.QuadPart) { + errno = ENOTSUP; + return -1; + } + } + + QueryPerformanceCounter(&ticks); + + seconds = (double)ticks.QuadPart / (double)ticksPerSec.QuadPart; + tv->tv_sec = (time_t)seconds; + tv->tv_nsec = (long)((ULONGLONG)(seconds * NS_PER_SEC) % NS_PER_SEC); + + return 0; +} +#endif + +void Timer::start() { +#ifdef __linux__ + clock_gettime(CLOCK_MONOTONIC, &_startTime); +#elif _WIN32 + clock_gettime_monotonic(&_startTime); +#endif +} + +int64_t Timer::getNs() { + struct timespec now = {}; +#ifdef __linux__ + clock_gettime(CLOCK_MONOTONIC, &now); +#elif _WIN32 + clock_gettime_monotonic(&now); +#endif + return (int64_t)(now.tv_nsec - _startTime.tv_nsec) + + 1000000000 * (now.tv_sec - _startTime.tv_sec); +} diff --git a/goalc/util/Timer.h b/goalc/util/Timer.h new file mode 100644 index 0000000000..5972020339 --- /dev/null +++ b/goalc/util/Timer.h @@ -0,0 +1,47 @@ +#ifndef JAK_V2_TIMER_H +#define JAK_V2_TIMER_H + +#include +#include +#include + +/*! + * Timer for measuring time elapsed with clock_monotonic + */ +class Timer { + public: + /*! + * Construct and start timer + */ + explicit Timer() { start(); } + +#ifdef _WIN32 + int clock_gettime_monotonic(struct timespec* tv); +#endif + + /*! + * Start the timer + */ + void start(); + + /*! + * Get milliseconds elapsed + */ + double getMs() { return (double)getNs() / 1.e6; } + + double getUs() { return (double)getNs() / 1.e3; } + + /*! + * Get nanoseconds elapsed + */ + int64_t getNs(); + + /*! + * Get seconds elapsed + */ + double getSeconds() { return (double)getNs() / 1.e9; } + + struct timespec _startTime = {}; +}; + +#endif // JAK_V2_TIMER_H diff --git a/goalc/util/file_io.cpp b/goalc/util/file_io.cpp index d6b35e1735..095ad933a8 100644 --- a/goalc/util/file_io.cpp +++ b/goalc/util/file_io.cpp @@ -29,4 +29,17 @@ std::string combine_path(std::vector path) { return result; } +void write_binary_file(const std::string& name, void* data, size_t size) { + FILE* fp = fopen(name.c_str(), "wb"); + if (!fp) { + throw std::runtime_error("couldn't open file " + name); + } + + if (fwrite(data, size, 1, fp) != 1) { + throw std::runtime_error("couldn't write file " + name); + } + + fclose(fp); +} + } // namespace util diff --git a/goalc/util/file_io.h b/goalc/util/file_io.h index aae7340ee7..9fa7802040 100644 --- a/goalc/util/file_io.h +++ b/goalc/util/file_io.h @@ -8,6 +8,7 @@ namespace util { std::string read_text_file(const std::string& path); std::string combine_path(const std::string& parent, const std::string& child); std::string combine_path(std::vector path); +void write_binary_file(const std::string& name, void* data, size_t size); } // namespace util #endif // JAK1_FILE_IO_H diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp index a03b01177d..fffb74b451 100644 --- a/test/test_compiler_and_runtime.cpp +++ b/test/test_compiler_and_runtime.cpp @@ -69,10 +69,10 @@ struct CompilerTestRunner { int passed = 0; for (auto& test : tests) { if (test.expected == test.actual) { - fmt::print("[{:30}] PASS!\n", test.test_name); + fmt::print("[{:40}] PASS!\n", test.test_name); passed++; } else { - fmt::print("[{:30}] FAIL!\n", test.test_name); + fmt::print("[{:40}] FAIL!\n", test.test_name); fmt::print("expected:\n"); for (auto& x : test.expected) { fmt::print(" \"{}\"\n", escaped_string(x)); @@ -102,6 +102,15 @@ TEST(CompilerAndRuntime, CompilerTests) { 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"}); + runner.run_test("test-conditional-compilation-1.gc", {"3\n"}); + // todo, test-conditional-compilation-2.gc + // these numbers match the game's memory layout for where the symbol table lives. + // it's probably not 100% needed to get this exactly, but it's a good sign that the global + // heap lives in the right spot because there should be no divergence in memory layout when its + // built. This also checks that #t, #f get "hashed" to the correct spot. + runner.run_test("test-get-symbol-1.gc", {"1342756\n"}); // 0x147d24 in hex + runner.run_test("test-get-symbol-2.gc", {"1342764\n"}); // 0x147d2c in hex + runner.run_test("test-define-1.gc", {"17\n"}); compiler.shutdown_target(); runtime_thread.join(); From 1394cf13cd6528773e02a54f855d101fe49f991e Mon Sep 17 00:00:00 2001 From: water Date: Mon, 7 Sep 2020 19:17:48 -0400 Subject: [PATCH 06/20] 17 of 124 compiler tests passing --- game/kernel/kscheme.cpp | 1 - goal_src/test/test-defglobalconstant-1.gc | 5 + goal_src/test/test-defglobalconstant-2.gc | 8 ++ goal_src/test/test-goto-1.gc | 9 ++ goal_src/test/test-nested-blocks-1.gc | 11 ++ goal_src/test/test-nested-blocks-2.gc | 11 ++ goal_src/test/test-nested-blocks-3.gc | 11 ++ goalc/compiler/Compiler.cpp | 1 + goalc/compiler/Compiler.h | 8 ++ goalc/compiler/Env.cpp | 42 ++++++- goalc/compiler/Env.h | 33 +++++- goalc/compiler/IR.cpp | 71 ++++++++++++ goalc/compiler/IR.h | 30 +++++ goalc/compiler/Label.h | 11 +- goalc/compiler/Util.cpp | 14 +++ goalc/compiler/compilation/Atoms.cpp | 25 ++++- goalc/compiler/compilation/Block.cpp | 129 ++++++++++++++++++++++ goalc/compiler/compilation/Macro.cpp | 28 +++++ goalc/emitter/ObjectGenerator.cpp | 9 ++ goalc/emitter/ObjectGenerator.h | 1 + test/test_compiler_and_runtime.cpp | 6 + 21 files changed, 449 insertions(+), 15 deletions(-) create mode 100644 goal_src/test/test-defglobalconstant-1.gc create mode 100644 goal_src/test/test-defglobalconstant-2.gc create mode 100644 goal_src/test/test-goto-1.gc create mode 100644 goal_src/test/test-nested-blocks-1.gc create mode 100644 goal_src/test/test-nested-blocks-2.gc create mode 100644 goal_src/test/test-nested-blocks-3.gc diff --git a/game/kernel/kscheme.cpp b/game/kernel/kscheme.cpp index bec0f1be12..816918ff0f 100644 --- a/game/kernel/kscheme.cpp +++ b/game/kernel/kscheme.cpp @@ -960,7 +960,6 @@ uint64_t _call_goal_asm_win32(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, u64 call_goal(Ptr f, u64 a, u64 b, u64 c, u64 st, void* offset) { // auto st_ptr = (void*)((uint8_t*)(offset) + st); updated for the new compiler! void* st_ptr = (void*)st; - printf("st is 0x%x\n", st); void* fptr = f.c(); #ifdef __linux__ diff --git a/goal_src/test/test-defglobalconstant-1.gc b/goal_src/test/test-defglobalconstant-1.gc new file mode 100644 index 0000000000..ce99baba6b --- /dev/null +++ b/goal_src/test/test-defglobalconstant-1.gc @@ -0,0 +1,5 @@ +(defglobalconstant my-constant 12) +(defglobalconstant my-constant 17) +(defglobalconstant not-my-consant 13) + +my-constant \ No newline at end of file diff --git a/goal_src/test/test-defglobalconstant-2.gc b/goal_src/test/test-defglobalconstant-2.gc new file mode 100644 index 0000000000..781eae4074 --- /dev/null +++ b/goal_src/test/test-defglobalconstant-2.gc @@ -0,0 +1,8 @@ +(defmacro get-goos-by-name (name) + ;; do the lookup in the goos global environment + (eval name) + ) + +(defglobalconstant my-constant 18) + +(get-goos-by-name my-constant) \ No newline at end of file diff --git a/goal_src/test/test-goto-1.gc b/goal_src/test/test-goto-1.gc new file mode 100644 index 0000000000..b310f67891 --- /dev/null +++ b/goal_src/test/test-goto-1.gc @@ -0,0 +1,9 @@ + + +(block my-block + 1 + (goto skip-early-return) + (return-from my-block 2) + (label skip-early-return) + 3 + ) diff --git a/goal_src/test/test-nested-blocks-1.gc b/goal_src/test/test-nested-blocks-1.gc new file mode 100644 index 0000000000..00bc13bca7 --- /dev/null +++ b/goal_src/test/test-nested-blocks-1.gc @@ -0,0 +1,11 @@ +(block outer-block + 1 + 2 + (block inner-block + 3 + 4 + (return-from inner-block 7) + 5 + 6 + ) + ) \ No newline at end of file diff --git a/goal_src/test/test-nested-blocks-2.gc b/goal_src/test/test-nested-blocks-2.gc new file mode 100644 index 0000000000..f421a20275 --- /dev/null +++ b/goal_src/test/test-nested-blocks-2.gc @@ -0,0 +1,11 @@ +(block outer-block + 1 + 2 + (block inner-block + 3 + 4 + (return-from inner-block 7) + 5 + ) + 8 + ) \ No newline at end of file diff --git a/goal_src/test/test-nested-blocks-3.gc b/goal_src/test/test-nested-blocks-3.gc new file mode 100644 index 0000000000..cd1e3b1889 --- /dev/null +++ b/goal_src/test/test-nested-blocks-3.gc @@ -0,0 +1,11 @@ +(block outer-block + 1 + 2 + (block inner-block + 3 + 4 + (return-from outer-block 7) + 5 + ) + 8 + ) \ No newline at end of file diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index a728753176..29c5c4d95f 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -203,5 +203,6 @@ void Compiler::typecheck(const goos::Object& form, const TypeSpec& expected, const TypeSpec& actual, const std::string& error_message) { + (void)form; m_ts.typecheck(expected, actual, error_message, true, true); } \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 9f49720f34..215ee61f0b 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -53,6 +53,9 @@ class Compiler { named); std::string as_string(const goos::Object& o); std::string symbol_string(const goos::Object& o); + const goos::Object& pair_car(const goos::Object& o); + const goos::Object& pair_cdr(const goos::Object& o); + void expect_empty_list(const goos::Object& o); TypeSystem m_ts; std::unique_ptr m_global_env = nullptr; @@ -75,6 +78,10 @@ class Compiler { // Block Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_block(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_return_from(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_label(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_goto(const goos::Object& form, const goos::Object& rest, Env* env); // CompilerControl Val* compile_seval(const goos::Object& form, const goos::Object& rest, Env* env); @@ -90,6 +97,7 @@ class Compiler { // Macro Val* compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_quote(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env); }; #endif // JAK_COMPILER_H diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index 50bc153280..0114dca609 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -47,6 +47,10 @@ RegVal* Env::make_xmm(TypeSpec ts) { return make_ireg(std::move(ts), emitter::RegKind::XMM); } +std::unordered_map& Env::get_label_map() { + return parent()->get_label_map(); +} + /////////////////// // GlobalEnv /////////////////// @@ -123,6 +127,24 @@ void NoEmitEnv::emit(std::unique_ptr ir) { throw std::runtime_error("emit into a no-emit env!"); } +/////////////////// +// BlockEnv +/////////////////// + +BlockEnv::BlockEnv(Env* parent, std::string _name) : Env(parent), name(std::move(_name)) {} + +std::string BlockEnv::print() { + return "block-" + name; +} + +BlockEnv* BlockEnv::find_block(const std::string& block) { + if (name == block) { + return this; + } else { + return parent()->find_block(block); + } +} + /////////////////// // FileEnv /////////////////// @@ -179,7 +201,17 @@ void FunctionEnv::emit(std::unique_ptr ir) { m_code.push_back(std::move(ir)); } void FunctionEnv::finish() { - // todo resolve gotos + resolve_gotos(); +} + +void FunctionEnv::resolve_gotos() { + for (auto& gt : unresolved_gotos) { + auto kv_label = m_labels.find(gt.label); + if (kv_label == m_labels.end()) { + throw std::runtime_error("Invalid goto " + gt.label); + } + gt.ir->resolve(&kv_label->second); + } } RegVal* FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) { @@ -189,4 +221,12 @@ RegVal* FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) { auto rv = std::make_unique(ireg, ts); m_iregs.push_back(std::move(rv)); return m_iregs.back().get(); +} + +std::unordered_map& FunctionEnv::get_label_map() { + return m_labels; +} + +std::unordered_map& LabelEnv::get_label_map() { + return m_labels; } \ No newline at end of file diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index c81671c6ea..9837ac15f6 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -33,6 +33,7 @@ class Env { virtual void constrain_reg(IRegConstraint constraint); virtual Val* lexical_lookup(goos::Object sym); virtual BlockEnv* find_block(const std::string& name); + virtual std::unordered_map& get_label_map(); RegVal* make_gpr(TypeSpec ts); RegVal* make_xmm(TypeSpec ts); virtual ~Env() = default; @@ -117,10 +118,18 @@ class DeclareEnv : public Env { } settings; }; +class IR_GotoLabel; + +struct UnresolvedGoto { + IR_GotoLabel* ir; + std::string label; +}; + class FunctionEnv : public DeclareEnv { public: FunctionEnv(Env* parent, std::string name); std::string print() override; + std::unordered_map& get_label_map() override; void set_segment(int seg) { segment = seg; } void emit(std::unique_ptr ir) override; void finish(); @@ -140,14 +149,26 @@ class FunctionEnv : public DeclareEnv { m_vals.push_back(std::move(new_obj)); return (T*)m_vals.back().get(); } + + template + T* alloc_env(Args&&... args) { + std::unique_ptr new_obj = std::make_unique(std::forward(args)...); + m_envs.push_back(std::move(new_obj)); + return (T*)m_envs.back().get(); + } + int segment = -1; std::string method_of_type_name = "#f"; + std::vector unresolved_gotos; + protected: + void resolve_gotos(); std::string m_name; std::vector> m_code; std::vector> m_iregs; std::vector> m_vals; + std::vector> m_envs; std::vector m_constraints; // todo, unresolved gotos AllocationResult m_regalloc_result; @@ -156,6 +177,7 @@ class FunctionEnv : public DeclareEnv { bool m_aligned_stack_required = false; std::unordered_map m_params; + std::unordered_map m_labels; }; class BlockEnv : public Env { @@ -164,11 +186,10 @@ class BlockEnv : public Env { std::string print() override; BlockEnv* find_block(const std::string& name) override; - protected: - std::string m_name; - Label* m_end_label = nullptr; - Val* m_return_value = nullptr; - std::vector m_return_types; + std::string name; + Label end_label = nullptr; + RegVal* return_value = nullptr; + std::vector return_types; }; class LexicalEnv : public Env { @@ -179,6 +200,8 @@ class LexicalEnv : public Env { class LabelEnv : public Env { public: + std::unordered_map& get_label_map() override; + protected: std::unordered_map m_labels; }; diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 33afba90b6..236662ba1e 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -168,4 +168,75 @@ void IR_GetSymbolValue::do_codegen(emitter::ObjectGenerator* gen, irec); gen->link_instruction_symbol_mem(instr, m_src->name()); } +} + +///////////////////// +// RegSet +///////////////////// + +IR_RegSet::IR_RegSet(const RegVal* dest, const RegVal* src) : m_dest(dest), m_src(src) {} + +RegAllocInstr IR_RegSet::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + rai.read.push_back(m_src->ireg()); + if (m_dest->ireg().kind == m_src->ireg().kind) { + rai.is_move = true; // only true if we aren't moving from register kind to register kind + } + return rai; +} + +void IR_RegSet::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto val_reg = get_reg(m_src, allocs, irec); + auto dest_reg = get_reg(m_dest, 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); + } +} + +std::string IR_RegSet::print() { + return fmt::format("mov {}, {}", m_dest->print(), m_src->print()); +} + +///////////////////// +// GotoLabel +///////////////////// + +IR_GotoLabel::IR_GotoLabel(const Label* dest) : m_dest(dest) { + m_resolved = true; +} + +IR_GotoLabel::IR_GotoLabel() { + m_resolved = false; +} + +std::string IR_GotoLabel::print() { + return fmt::format("goto {}", m_dest->print()); +} + +RegAllocInstr IR_GotoLabel::to_rai() { + assert(m_resolved); + RegAllocInstr rai; + rai.jumps.push_back(m_dest->idx); + rai.fallthrough = false; + return rai; +} + +void IR_GotoLabel::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + (void)allocs; + auto instr = gen->add_instr(IGen::jmp_32(), irec); + gen->link_instruction_jump(instr, gen->get_future_ir_record_in_same_func(irec, m_dest->idx)); +} + +void IR_GotoLabel::resolve(const Label* dest) { + assert(!m_resolved); + m_dest = dest; + m_resolved = true; } \ No newline at end of file diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index b06845e0b3..65e3747426 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -99,4 +99,34 @@ class IR_GetSymbolValue : public IR { bool m_sext = false; }; +class IR_RegSet : public IR { + public: + IR_RegSet(const RegVal* dest, const RegVal* src); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + + protected: + const RegVal* m_dest = nullptr; + const RegVal* m_src = nullptr; +}; + +class IR_GotoLabel : public IR { + public: + IR_GotoLabel(); + void resolve(const Label* dest); + explicit IR_GotoLabel(const Label* dest); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + + protected: + const Label* m_dest = nullptr; + bool m_resolved = false; +}; + #endif // JAK_IR_H diff --git a/goalc/compiler/Label.h b/goalc/compiler/Label.h index 0307a723f1..61336994ab 100644 --- a/goalc/compiler/Label.h +++ b/goalc/compiler/Label.h @@ -1,8 +1,13 @@ - - #ifndef JAK_LABEL_H #define JAK_LABEL_H -struct Label {}; +class FunctionEnv; +struct Label { + Label() = default; + Label(FunctionEnv* f, int _idx = -1) : func(f), idx(_idx) {} + FunctionEnv* func = nullptr; + int idx = -1; + std::string print() const { return "label-" + std::to_string(idx); } +}; #endif // JAK_LABEL_H diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index 435055de1a..c891c5be29 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -103,4 +103,18 @@ std::string Compiler::as_string(const goos::Object& o) { std::string Compiler::symbol_string(const goos::Object& o) { return o.as_symbol()->name; +} + +const goos::Object& Compiler::pair_car(const goos::Object& o) { + return o.as_pair()->car; +} + +const goos::Object& Compiler::pair_cdr(const goos::Object& o) { + return o.as_pair()->cdr; +} + +void Compiler::expect_empty_list(const goos::Object& o) { + if (!o.is_empty_list()) { + throw_compile_error(o, "expected to be an empty list"); + } } \ No newline at end of file diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index aa66b84e2d..9f3be74ce7 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -19,10 +19,10 @@ static const std::unordered_map< // // 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}, + {"block", &Compiler::compile_block}, + {"return-from", &Compiler::compile_return_from}, + {"label", &Compiler::compile_label}, + {"goto", &Compiler::compile_goto}, // // // COMPILER CONTROL // {"gs", &Compiler::compile_gs}, @@ -36,7 +36,7 @@ static const std::unordered_map< // // // CONDITIONAL COMPILATION {"#cond", &Compiler::compile_gscond}, - // {"defglobalconstant", &Compiler::compile_defglobalconstant}, + {"defglobalconstant", &Compiler::compile_defglobalconstant}, {"seval", &Compiler::compile_seval}, // // // CONTROL FLOW @@ -200,6 +200,21 @@ Val* Compiler::compile_symbol(const goos::Object& form, Env* env) { // todo lexical // todo global constant + auto global_constant = m_global_constants.find(form.as_symbol()); + auto existing_symbol = m_symbol_types.find(form.as_symbol()->name); + + if (global_constant != m_global_constants.end()) { + // check there is no symbol with the same name + if (existing_symbol != m_symbol_types.end()) { + throw_compile_error(form, + "symbol is both a runtime symbol and a global constant. Something is " + "likely very wrong."); + } + + // got a global constant + return compile_error_guard(global_constant->second, env); + } + return compile_get_symbol_value(name, env); } diff --git a/goalc/compiler/compilation/Block.cpp b/goalc/compiler/compilation/Block.cpp index 404d022f71..1db6980419 100644 --- a/goalc/compiler/compilation/Block.cpp +++ b/goalc/compiler/compilation/Block.cpp @@ -13,3 +13,132 @@ Val* Compiler::compile_begin(const goos::Object& form, const goos::Object& rest, for_each_in_list(rest, [&](const Object& o) { result = compile_error_guard(o, env); }); return result; } + +Val* Compiler::compile_block(const goos::Object& form, const goos::Object& _rest, Env* env) { + auto rest = &_rest; + auto name = pair_car(*rest); + rest = &pair_cdr(*rest); + + if (!rest->is_pair()) { + throw_compile_error(form, "Block form has an empty or invliad body"); + } + + auto fe = get_parent_env_of_type(env); + + // create environment + auto block_env = fe->alloc_env(env, symbol_string(name)); + + // we need to create a return value register, as a "return-from" statement inside the block may + // set it. for now it has a type of none, but we will set it more accurate after compiling the + // block. + // block_env->return_value = env->alloc_reg(get_base_typespec("none")); + block_env->return_value = env->make_gpr(m_ts.make_typespec("none")); + + // create label to the end of the block (we don't yet know where it is...) + block_env->end_label = Label(fe); + + // compile everything in the body + Val* result = get_none(); + for_each_in_list(*rest, [&](const Object& o) { result = compile_error_guard(o, block_env); }); + + // if no return-from's were used, we can ignore the return_value register, and basically turn this + // into a begin. this allows a block which returns a floating point value to return the value in + // an xmm register, which is likely to eliminate a gpr->xmm move. + if (block_env->return_types.empty()) { + return result; + } + + // determine return type as the lowest common ancestor of the block's last form and any + // return-from's + auto& return_types = block_env->return_types; + return_types.push_back(result->type()); + auto return_type = m_ts.lowest_common_ancestor(return_types); + block_env->return_value->set_type(return_type); + + // an IR to move the result of the block into the block's return register (if no return-from's are + // taken) + auto ir_move_rv = std::make_unique(block_env->return_value, result->to_gpr(fe)); + + // note - one drawback of doing this single pass is that a block always evaluates to a gpr. + // so we may have an unneeded xmm -> gpr move that could have been an xmm -> xmm that could have + // been eliminated. + env->emit(std::move(ir_move_rv)); + + // now we know the end of the block, so we set the label index to be on whatever comes after the + // return move. functions always end with a "null" IR and "null" instruction, so this is safe. + block_env->end_label.idx = block_env->end_label.func->code().size(); + + return block_env->return_value; +} + +Val* Compiler::compile_return_from(const goos::Object& form, const goos::Object& _rest, Env* env) { + const Object* rest = &_rest; + auto block_name = symbol_string(pair_car(*rest)); + rest = &pair_cdr(*rest); + auto value_expression = pair_car(*rest); + expect_empty_list(pair_cdr(*rest)); + + // evaluate expression to return + auto result = compile_error_guard(value_expression, env); + auto fe = get_parent_env_of_type(env); + + // find block to return from + auto block = dynamic_cast(env->find_block(block_name)); + if (!block) { + throw_compile_error(form, + "The return-from form was unable to find a block named " + block_name); + } + + // move result into return register + auto ir_move_rv = std::make_unique(block->return_value, result->to_gpr(fe)); + + // inform block of our possible return type + block->return_types.push_back(result->type()); + + env->emit(std::move(ir_move_rv)); + + // jump to end of block + auto ir_jump = std::make_unique(&block->end_label); + // we know this label is a real label. even though end_label doesn't know where it is, there is an + // actual label object. this means we won't try to resolve this label _by name_ later on when the + // block is done. + // ir_jump->resolved = true; + env->emit(std::move(ir_jump)); + + // In the real GOAL, there is likely a bug here where a non-none value is returned. + return get_none(); +} + +Val* Compiler::compile_label(const goos::Object& form, const goos::Object& rest, Env* env) { + auto label_name = symbol_string(pair_car(rest)); + expect_empty_list(pair_cdr(rest)); + + // make sure we don't have a label with this name already + auto& labels = env->get_label_map(); + auto kv = labels.find(label_name); + if (kv != labels.end()) { + throw_compile_error( + form, "There are two labels named " + label_name + " in the same label environment"); + } + + // make a label pointing to the end of the current function env. + auto func_env = get_parent_env_of_type(env); + labels[label_name] = Label(func_env, func_env->code().size()); + return get_none(); +} + +Val* Compiler::compile_goto(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)form; + auto label_name = symbol_string(pair_car(rest)); + expect_empty_list(pair_cdr(rest)); + + auto ir_goto = std::make_unique(); + // this requires looking up the label by name after, as it may be a goto to a label which has not + // yet been defined. + + // add this goto to the list of gotos to resolve after the function is done. + // it's safe to have this reference, as the FunctionEnv also owns the goto. + get_parent_env_of_type(env)->unresolved_gotos.push_back({ir_goto.get(), label_name}); + env->emit(std::move(ir_goto)); + return get_none(); +} \ No newline at end of file diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index a5d6b6aa27..89b5afb265 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -28,6 +28,7 @@ Val* Compiler::compile_goos_macro(const goos::Object& o, Arguments args = m_goos.get_args(o, rest, macro->args); auto mac_env_obj = EnvironmentObject::make_new(); auto mac_env = mac_env_obj.as_env(); + mac_env->parent_env = m_goos.global_environment.as_env(); m_goos.set_args_in_env(o, args, macro->args, mac_env); m_goos.goal_to_goos.enclosing_method_type = get_parent_env_of_type(env)->method_of_type_name; @@ -85,5 +86,32 @@ Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest, default: throw_compile_error(form, "Can't quote this"); } + return get_none(); +} + +Val* Compiler::compile_defglobalconstant(const goos::Object& form, + const goos::Object& _rest, + Env* env) { + auto rest = &_rest; + (void)env; + if (!rest->is_pair()) { + throw_compile_error(form, "invalid defglobalconstant"); + } + + auto sym = pair_car(*rest).as_symbol(); + rest = &pair_cdr(*rest); + auto value = pair_car(*rest); + + rest = &rest->as_pair()->cdr; + if (!rest->is_empty_list()) { + throw_compile_error(form, "invalid defglobalconstant"); + } + + // GOAL constant + m_global_constants[sym] = value; + + // GOOS constant + m_goos.global_environment.as_env()->vars[sym] = value; + return get_none(); } \ No newline at end of file diff --git a/goalc/emitter/ObjectGenerator.cpp b/goalc/emitter/ObjectGenerator.cpp index 1f9da29214..0c14eb1e37 100644 --- a/goalc/emitter/ObjectGenerator.cpp +++ b/goalc/emitter/ObjectGenerator.cpp @@ -136,6 +136,15 @@ IR_Record ObjectGenerator::get_future_ir_record(const FunctionRecord& func, int return rec; } +IR_Record ObjectGenerator::get_future_ir_record_in_same_func(const IR_Record& irec, int ir_id) { + assert(irec.func_id == int(m_function_data_by_seg.at(irec.seg).size()) - 1); + IR_Record rec; + rec.seg = irec.seg; + rec.func_id = irec.func_id; + rec.ir_id = ir_id; + return rec; +} + /*! * Add a new Instruction for the given IR instruction. */ diff --git a/goalc/emitter/ObjectGenerator.h b/goalc/emitter/ObjectGenerator.h index 968577cbc3..4d3c436c16 100644 --- a/goalc/emitter/ObjectGenerator.h +++ b/goalc/emitter/ObjectGenerator.h @@ -43,6 +43,7 @@ class ObjectGenerator { int min_align = 16); // should align and insert function tag IR_Record add_ir(const FunctionRecord& func); IR_Record get_future_ir_record(const FunctionRecord& func, int ir_id); + IR_Record get_future_ir_record_in_same_func(const IR_Record& irec, 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); diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp index fffb74b451..8192b3a464 100644 --- a/test/test_compiler_and_runtime.cpp +++ b/test/test_compiler_and_runtime.cpp @@ -111,6 +111,12 @@ TEST(CompilerAndRuntime, CompilerTests) { runner.run_test("test-get-symbol-1.gc", {"1342756\n"}); // 0x147d24 in hex runner.run_test("test-get-symbol-2.gc", {"1342764\n"}); // 0x147d2c in hex runner.run_test("test-define-1.gc", {"17\n"}); + runner.run_test("test-nested-blocks-1.gc", {"7\n"}); + runner.run_test("test-nested-blocks-2.gc", {"8\n"}); + runner.run_test("test-nested-blocks-3.gc", {"7\n"}); + runner.run_test("test-goto-1.gc", {"3\n"}); + runner.run_test("test-defglobalconstant-1.gc", {"17\n"}); + runner.run_test("test-defglobalconstant-2.gc", {"18\n"}); compiler.shutdown_target(); runtime_thread.join(); From d35a8a3867942402661938cef710657aca295b5c Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 19:43:03 -0400 Subject: [PATCH 07/20] usleep -> this_thread::sleep_for --- goalc/listener/Listener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index 2081babadb..c3d8be0a4b 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -376,7 +376,7 @@ bool Listener::wait_for_ack() { for (int i = 0; i < 2000; i++) { if (got_ack) return true; - usleep(1000); + std::this_thread::sleep_for(std::chrono::microseconds(1000)); } waiting_for_ack = false; From 413bd66ce79be762ddcf6d6cb6a251347605b5c9 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 19:46:40 -0400 Subject: [PATCH 08/20] fix windows min/max macro issue --- goalc/listener/Listener.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index c3d8be0a4b..a8aec4bc62 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -14,6 +14,10 @@ #define WIN32_LEAN_AND_MEAN #include #include + +// remove the evil windows min/max macros! +#undef min +#undef max #endif // TODO - i think im not including the dependency right..? From f55c2d305d810e494688bef11e6d82d29f3f2d34 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 19:49:58 -0400 Subject: [PATCH 09/20] another fix --- goalc/compiler/Compiler.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 29c5c4d95f..1edede33bd 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -3,7 +3,8 @@ #include "common/link_types.h" #include "IR.h" #include "goalc/regalloc/allocate.h" -#include "unistd.h" +#include +#include using namespace goos; @@ -167,7 +168,7 @@ 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); + std::this_thread::sleep_for(std::chrono::microseconds(10000)); if (m_listener.is_connected()) { break; } From 5612b9e1de36de4c09d697366c94df0ceb1574fa Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 19:57:58 -0400 Subject: [PATCH 10/20] fix again --- goalc/compiler/compilation/CompilerControl.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index ae85bc07e1..6d09bf9067 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -63,7 +63,13 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re timing.emplace_back("read", reader_timer.getMs()); Timer compile_timer; - std::string obj_file_name = basename(filename.c_str()); + std::string obj_file_name = filename; + for (int idx = int(filename.size()) - 1; idx-- > 0;) { + if (filename.at(idx) == '\\' || filename.at(idx) == '/') { + obj_file_name = filename.substr(idx + 1); + } + } + obj_file_name = obj_file_name.substr(0, obj_file_name.find_last_of('.')); auto obj_file = compile_object_file(obj_file_name, code, !no_code); timing.emplace_back("compile", compile_timer.getMs()); From 551d9897328e93ed2e4aed3463906a374692ab82 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:01:46 -0400 Subject: [PATCH 11/20] so close --- test/test_compiler_and_runtime.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp index 8192b3a464..172b13bac1 100644 --- a/test/test_compiler_and_runtime.cpp +++ b/test/test_compiler_and_runtime.cpp @@ -1,4 +1,5 @@ #include +#include #include "gtest/gtest.h" #include "game/runtime.h" @@ -15,7 +16,7 @@ TEST(CompilerAndRuntime, StartRuntime) { listener::Listener listener; while (!listener.is_connected()) { listener.connect_to_target(); - usleep(1000); + std::this_thread::sleep_for(std::chrono::microseconds(1000)); } listener.send_reset(true); From 6de430b4efd6c1cb4597edaf6363a82a5bc20999 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:10:03 -0400 Subject: [PATCH 12/20] temporary hacks in fakeiso to see if it can work in windows --- game/overlord/fake_iso.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/game/overlord/fake_iso.cpp b/game/overlord/fake_iso.cpp index 97f85fa18b..7668ee79ed 100644 --- a/game/overlord/fake_iso.cpp +++ b/game/overlord/fake_iso.cpp @@ -97,10 +97,13 @@ int FS_Init(u8* buffer) { fseek(fp, 0, SEEK_END); size_t len = ftell(fp); rewind(fp); - char* fakeiso = (char*)malloc(len); + char* fakeiso = (char*)malloc(len + 1); if (fread(fakeiso, len, 1, fp) != 1) { +#ifdef __linux__ assert(false); +#endif } + fakeiso[len] = '\0'; // loop over lines char* ptr = fakeiso; @@ -137,6 +140,7 @@ int FS_Init(u8* buffer) { ptr++; i++; } + e->file_path[i] = 0; fake_iso_entry_count++; } From 4825b0d1e59680f15156a033560252287bf32740 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:14:37 -0400 Subject: [PATCH 13/20] add a slash on windows --- game/overlord/fake_iso.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/game/overlord/fake_iso.cpp b/game/overlord/fake_iso.cpp index 7668ee79ed..45603a03c6 100644 --- a/game/overlord/fake_iso.cpp +++ b/game/overlord/fake_iso.cpp @@ -198,9 +198,7 @@ static const char* get_file_path(FileRecord* fr) { assert(fr->location < fake_iso_entry_count); static char path_buffer[1024]; strcpy(path_buffer, next_dir); -#ifdef __linux__ strcat(path_buffer, "/"); -#endif strcat(path_buffer, fake_iso_entries[fr->location].file_path); return path_buffer; } From f73b6de2546c6c071c1acf90e934bc18fe33a70c Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:29:48 -0400 Subject: [PATCH 14/20] try setting a timeout on the accepted socket --- game/system/Deci2Server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/game/system/Deci2Server.cpp b/game/system/Deci2Server.cpp index 4d7f466aab..ca7f1b0f63 100644 --- a/game/system/Deci2Server.cpp +++ b/game/system/Deci2Server.cpp @@ -263,6 +263,7 @@ void Deci2Server::accept_thread_func() { // close new_sock = accept(server_socket, (sockaddr*)&addr, &l); if (new_sock >= 0) { + set_socket_timeout(new_sock, 100000); u32 versions[2] = {versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR}; write_to_socket(new_sock, (char*)&versions, 8); // todo, check result? server_connected = true; From fb62936a94796e58ae4bc1e0f64c30a7ba532846 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:37:03 -0400 Subject: [PATCH 15/20] add debug prints --- game/system/Deci2Server.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/game/system/Deci2Server.cpp b/game/system/Deci2Server.cpp index ca7f1b0f63..e7b7207fb9 100644 --- a/game/system/Deci2Server.cpp +++ b/game/system/Deci2Server.cpp @@ -186,10 +186,13 @@ void Deci2Server::send_proto_ready(Deci2Driver* drivers, int* driver_count) { void Deci2Server::run() { int desired_size = (int)sizeof(Deci2Header); int got = 0; + printf("Deci2Server::run\n"); while (got < desired_size) { assert(got + desired_size < BUFFER_SIZE); + printf("r1\n"); auto x = read_from_socket(new_sock, buffer + got, desired_size - got); + printf("r1 done\n"); if (want_exit()) { return; } @@ -240,7 +243,9 @@ void Deci2Server::run() { // receive from network if (hdr->rsvd < hdr->len) { + printf("r2\n"); auto x = read_from_socket(new_sock, buffer + hdr->rsvd, hdr->len - hdr->rsvd); + printf("r2 done\n"); if (want_exit()) { return; } @@ -251,6 +256,7 @@ void Deci2Server::run() { (driver.handler)(DECI2_READDONE, 0, driver.opt); unlock(); + printf("run done\n"); } /*! From e5631ea5c8c70dffbc1709ec91f1a78bbbc2f79f Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:46:09 -0400 Subject: [PATCH 16/20] debug print --- common/cross_sockets/xsocket.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/cross_sockets/xsocket.cpp b/common/cross_sockets/xsocket.cpp index a7f2827070..eb6e5ce667 100644 --- a/common/cross_sockets/xsocket.cpp +++ b/common/cross_sockets/xsocket.cpp @@ -66,7 +66,8 @@ int set_socket_timeout(int socket, long microSeconds) { } return ret; #elif _WIN32 - unsigned long timeout = microSeconds / 1000; // milliseconds + DWORD timeout = microSeconds / 1000; // milliseconds + printf("setting timeout to %d ms\n", timeout); return set_socket_option(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); #endif } From a47504b812c176490e1048b078ee03df5d5095ab Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:49:37 -0400 Subject: [PATCH 17/20] only take address once --- common/cross_sockets/xsocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/cross_sockets/xsocket.cpp b/common/cross_sockets/xsocket.cpp index eb6e5ce667..7dff6415e3 100644 --- a/common/cross_sockets/xsocket.cpp +++ b/common/cross_sockets/xsocket.cpp @@ -40,7 +40,7 @@ void close_socket(int sock) { } int set_socket_option(int socket, int level, int optname, const void* optval, int optlen) { - int ret = setsockopt(socket, level, optname, (char*)&optval, optlen); + int ret = setsockopt(socket, level, optname, (const char*)optval, optlen); if (ret < 0) { printf("Failed to setsockopt(%d, %d, %d, _, _) - Error: %s\n", socket, level, optname, strerror(errno)); From c04ab1a6ff05da514f81847ed4fbbe2bc43c9994 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:55:48 -0400 Subject: [PATCH 18/20] try different print format for windows --- common/cross_sockets/xsocket.cpp | 1 - game/kernel/kboot.cpp | 4 ++++ game/system/Deci2Server.cpp | 6 ------ 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/common/cross_sockets/xsocket.cpp b/common/cross_sockets/xsocket.cpp index 7dff6415e3..f4c89df7e8 100644 --- a/common/cross_sockets/xsocket.cpp +++ b/common/cross_sockets/xsocket.cpp @@ -67,7 +67,6 @@ int set_socket_timeout(int socket, long microSeconds) { return ret; #elif _WIN32 DWORD timeout = microSeconds / 1000; // milliseconds - printf("setting timeout to %d ms\n", timeout); return set_socket_option(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); #endif } diff --git a/game/kernel/kboot.cpp b/game/kernel/kboot.cpp index d005026d31..791f9d5045 100644 --- a/game/kernel/kboot.cpp +++ b/game/kernel/kboot.cpp @@ -150,7 +150,11 @@ void KernelCheckAndDispatch() { printf("\n"); auto result = call_goal(Ptr(ListenerFunction->value), 0, 0, 0, s7.offset, g_ee_main_mem); +#ifdef __linux__ cprintf("%ld\n", result); +#else + cprintf("%lld\n", result) +#endif ListenerFunction->value = s7.offset; } } diff --git a/game/system/Deci2Server.cpp b/game/system/Deci2Server.cpp index e7b7207fb9..ca7f1b0f63 100644 --- a/game/system/Deci2Server.cpp +++ b/game/system/Deci2Server.cpp @@ -186,13 +186,10 @@ void Deci2Server::send_proto_ready(Deci2Driver* drivers, int* driver_count) { void Deci2Server::run() { int desired_size = (int)sizeof(Deci2Header); int got = 0; - printf("Deci2Server::run\n"); while (got < desired_size) { assert(got + desired_size < BUFFER_SIZE); - printf("r1\n"); auto x = read_from_socket(new_sock, buffer + got, desired_size - got); - printf("r1 done\n"); if (want_exit()) { return; } @@ -243,9 +240,7 @@ void Deci2Server::run() { // receive from network if (hdr->rsvd < hdr->len) { - printf("r2\n"); auto x = read_from_socket(new_sock, buffer + hdr->rsvd, hdr->len - hdr->rsvd); - printf("r2 done\n"); if (want_exit()) { return; } @@ -256,7 +251,6 @@ void Deci2Server::run() { (driver.handler)(DECI2_READDONE, 0, driver.opt); unlock(); - printf("run done\n"); } /*! From 2e32bf792323c00396559090e068e9262c6d9384 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 20:58:03 -0400 Subject: [PATCH 19/20] semicolon --- game/kernel/kboot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game/kernel/kboot.cpp b/game/kernel/kboot.cpp index 791f9d5045..1b54215ada 100644 --- a/game/kernel/kboot.cpp +++ b/game/kernel/kboot.cpp @@ -153,7 +153,7 @@ void KernelCheckAndDispatch() { #ifdef __linux__ cprintf("%ld\n", result); #else - cprintf("%lld\n", result) + cprintf("%lld\n", result); #endif ListenerFunction->value = s7.offset; } From 24e79f66bc6f25bb79cef9b8ab751c5f3e987d63 Mon Sep 17 00:00:00 2001 From: water Date: Tue, 8 Sep 2020 21:26:09 -0400 Subject: [PATCH 20/20] try increasing timeouts and decreasing test iterations to make test shorter --- goalc/listener/Listener.cpp | 2 +- test/test_listener_deci2.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index a8aec4bc62..454dcd46f3 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -83,7 +83,7 @@ bool Listener::connect_to_target(int n_tries, const std::string& ip, int port) { return false; } - if (set_socket_timeout(listen_socket, 100000) < 0) { + if (set_socket_timeout(listen_socket, 500000) < 0) { close_socket(listen_socket); listen_socket = -1; return false; diff --git a/test/test_listener_deci2.cpp b/test/test_listener_deci2.cpp index 182d3dbd49..158a7afb7a 100644 --- a/test/test_listener_deci2.cpp +++ b/test/test_listener_deci2.cpp @@ -46,7 +46,7 @@ TEST(Listener, DeciCheckNoListener) { } TEST(Listener, DeciThenListener) { - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 3; i++) { Deci2Server s(always_false); EXPECT_TRUE(s.init()); EXPECT_FALSE(s.check_for_listener()); @@ -66,7 +66,7 @@ TEST(Listener, DeciThenListener) { } TEST(Listener, DeciThenListener2) { - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 3; i++) { Deci2Server s(always_false); EXPECT_TRUE(s.init()); EXPECT_FALSE(s.check_for_listener()); @@ -80,7 +80,7 @@ TEST(Listener, DeciThenListener2) { } TEST(Listener, ListenerThenDeci) { - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 3; i++) { Listener l; EXPECT_FALSE(l.connect_to_target()); Deci2Server s(always_false);