From 2b67b1078f238f4a874a7c6822b0d245c625bf94 Mon Sep 17 00:00:00 2001 From: water Date: Sat, 5 Sep 2020 18:55:07 -0400 Subject: [PATCH 01/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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/50] 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); From eb886d0c458915ce6b6e9c1911c85d3beb0093c2 Mon Sep 17 00:00:00 2001 From: blahpy Date: Wed, 9 Sep 2020 16:54:16 +1200 Subject: [PATCH 21/50] Upload new files --- CMakeLists.txt | 3 +++ common/util/CMakeLists.txt | 5 +++++ common/util/FileUtil.cpp | 26 ++++++++++++++++++++++++++ common/util/FileUtil.h | 11 +++++++++++ test/CMakeLists.txt | 2 +- test/test_common_util.cpp | 11 +++++++++++ 6 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 common/util/CMakeLists.txt create mode 100644 common/util/FileUtil.cpp create mode 100644 common/util/FileUtil.h create mode 100644 test/test_common_util.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b40377dc4a..11066c5f43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,9 @@ add_subdirectory(asset_tool) # build type_system library for compiler/decompiler add_subdirectory(common/type_system) +# build common_util library +add_subdirectory(common/util) + # build decompiler add_subdirectory(decompiler) diff --git a/common/util/CMakeLists.txt b/common/util/CMakeLists.txt new file mode 100644 index 0000000000..d6434a56b8 --- /dev/null +++ b/common/util/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(common_util + SHARED + FileUtil.cpp) + +target_link_libraries(common_util fmt) \ No newline at end of file diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp new file mode 100644 index 0000000000..e6f19d6b4d --- /dev/null +++ b/common/util/FileUtil.cpp @@ -0,0 +1,26 @@ +#include "FileUtil.h" +#include +#include + +std::string FileUtil::get_file_path(std::string input[]) { + int arrSize = std::sizeof(input); + std::string currentPath = std::filesystem::current_path(); + char dirSeparator; + + #ifdef _WIN32 + dirSeparator = '\'; + #else + dirSeparator = '/'; + #endif + + std::string filePath = currentPath; + for (int i = 0; i < arrSize; i++) { + if (arrSize = i+1) { + filePath = filePath << input[i]; + } else { + filePath = filePath << input[i] << dirSeparator; + } + } + + return filePath; +} \ No newline at end of file diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h new file mode 100644 index 0000000000..925cd3d017 --- /dev/null +++ b/common/util/FileUtil.h @@ -0,0 +1,11 @@ +#pragma once +#include + +namespace FileUtil +{ + class FileUtil + { + public: + std::string get_file_path(); + }; +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8173c9820c..1b8a271300 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,7 +14,7 @@ add_executable(goalc-test test_emitter_loads_and_store.cpp test_emitter_xmm32.cpp test_emitter_integer_math.cpp - ) + "test_common_util.cpp") IF (WIN32) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) diff --git a/test/test_common_util.cpp b/test/test_common_util.cpp new file mode 100644 index 0000000000..02a3985f6f --- /dev/null +++ b/test/test_common_util.cpp @@ -0,0 +1,11 @@ +#include "common/util/FileUtil.h" +#include +#include "gtest/gtest.h" + +TEST(test, test) { + + std::string test[] = {"cabbage", "banana", "apple"}; + std::cout << FileUtil::get_file_path(test) << std::endl; + + EXPECT_TRUE(true); +} \ No newline at end of file From 9f1d6792e278ba8865988ce7190a12f24c60725d Mon Sep 17 00:00:00 2001 From: blahpy Date: Wed, 9 Sep 2020 18:41:45 +1200 Subject: [PATCH 22/50] Fix errors in common_util --- common/util/CMakeLists.txt | 2 -- common/util/FileUtil.cpp | 48 +++++++++++++++++++++++--------------- common/util/FileUtil.h | 10 ++++---- test/CMakeLists.txt | 6 ++--- test/test_common_util.cpp | 12 ++++++---- 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/common/util/CMakeLists.txt b/common/util/CMakeLists.txt index d6434a56b8..1e2bef4c7e 100644 --- a/common/util/CMakeLists.txt +++ b/common/util/CMakeLists.txt @@ -1,5 +1,3 @@ add_library(common_util SHARED FileUtil.cpp) - -target_link_libraries(common_util fmt) \ No newline at end of file diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index e6f19d6b4d..b4b9640b1a 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -1,26 +1,36 @@ #include "FileUtil.h" #include -#include +#include /* defines FILENAME_MAX */ -std::string FileUtil::get_file_path(std::string input[]) { - int arrSize = std::sizeof(input); - std::string currentPath = std::filesystem::current_path(); - char dirSeparator; +#ifdef _WIN32 + #include + #define GetCurrentDir _getcwd +#else + #include + #define GetCurrentDir getcwd +#endif - #ifdef _WIN32 - dirSeparator = '\'; - #else - dirSeparator = '/'; - #endif +std::string FileUtil::GetCurrentWorkingDir() { + char buff[FILENAME_MAX]; + GetCurrentDir(buff, FILENAME_MAX); + std::string current_working_dir(buff); + return current_working_dir; +} - std::string filePath = currentPath; - for (int i = 0; i < arrSize; i++) { - if (arrSize = i+1) { - filePath = filePath << input[i]; - } else { - filePath = filePath << input[i] << dirSeparator; - } +std::string FileUtil::get_file_path(std::vector input) { + std::string currentPath = FileUtil::GetCurrentWorkingDir(); // std::filesystem::current_path(); + char dirSeparator; + + #ifdef _WIN32 + dirSeparator = '\\'; + #else + dirSeparator = '/'; + #endif + + std::string filePath = currentPath; + for (int i = 0; i < input.size(); i++) { + filePath = filePath + dirSeparator + input[i]; } - return filePath; -} \ No newline at end of file + return filePath; +} diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h index 925cd3d017..46906534d4 100644 --- a/common/util/FileUtil.h +++ b/common/util/FileUtil.h @@ -1,11 +1,9 @@ #pragma once #include +#include namespace FileUtil { - class FileUtil - { - public: - std::string get_file_path(); - }; -} \ No newline at end of file + std::string GetCurrentWorkingDir(); + std::string get_file_path(std::vector input); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1b8a271300..55f1ea2f17 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,13 +14,13 @@ add_executable(goalc-test test_emitter_loads_and_store.cpp test_emitter_xmm32.cpp test_emitter_integer_math.cpp - "test_common_util.cpp") + test_common_util.cpp) IF (WIN32) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # TODO - implement windows listener message("Windows Listener Not Implemented!") - target_link_libraries(goalc-test mman goos util runtime compiler type_system gtest) + target_link_libraries(goalc-test mman goos util common_util runtime compiler type_system gtest) ELSE() - target_link_libraries(goalc-test goos util listener runtime compiler type_system gtest) + target_link_libraries(goalc-test goos util common_util listener runtime compiler type_system gtest) ENDIF() diff --git a/test/test_common_util.cpp b/test/test_common_util.cpp index 02a3985f6f..e9862573c8 100644 --- a/test/test_common_util.cpp +++ b/test/test_common_util.cpp @@ -1,11 +1,13 @@ #include "common/util/FileUtil.h" -#include #include "gtest/gtest.h" +#include +#include -TEST(test, test) { +TEST(FileUtil, valid_path) { - std::string test[] = {"cabbage", "banana", "apple"}; - std::cout << FileUtil::get_file_path(test) << std::endl; + std::vector test = {"cabbage", "banana", "apple"}; + std::string sampleString = FileUtil::get_file_path(test); + // std::cout << sampleString << std::endl; EXPECT_TRUE(true); -} \ No newline at end of file +} From 83b6db9f334f86c75ba15b64d0077dd3f1737d39 Mon Sep 17 00:00:00 2001 From: water Date: Wed, 9 Sep 2020 17:00:46 -0400 Subject: [PATCH 23/50] update to c++17 --- CMakeLists.txt | 2 +- game/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ecc3e6a860..4ab9843521 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(jak) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) # Set default compile flags for GCC # optimization level can be set here. Note that game/ overwrites this for building game C++ code. diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index e51dde9e25..d73e4968fe 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -1,5 +1,5 @@ # We define our own compilation flags here. -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) # Set default compile flags for GCC # optimization level can be set here. Note that game/ overwrites this for building game C++ code. From efe94dca7975148a085bdf3b9c3258f89e1a6fdb Mon Sep 17 00:00:00 2001 From: blahpy Date: Thu, 10 Sep 2020 10:35:29 +1200 Subject: [PATCH 24/50] Format using clang-format --- common/util/FileUtil.cpp | 28 ++++++++++++++-------------- common/util/FileUtil.h | 9 ++++----- test/test_common_util.cpp | 1 - 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index b4b9640b1a..876f927b3a 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -1,13 +1,13 @@ #include "FileUtil.h" #include -#include /* defines FILENAME_MAX */ +#include /* defines FILENAME_MAX */ #ifdef _WIN32 - #include - #define GetCurrentDir _getcwd +#include +#define GetCurrentDir _getcwd #else - #include - #define GetCurrentDir getcwd +#include +#define GetCurrentDir getcwd #endif std::string FileUtil::GetCurrentWorkingDir() { @@ -17,20 +17,20 @@ std::string FileUtil::GetCurrentWorkingDir() { return current_working_dir; } -std::string FileUtil::get_file_path(std::vector input) { +std::string FileUtil::get_file_path(const std::vector& input) { std::string currentPath = FileUtil::GetCurrentWorkingDir(); // std::filesystem::current_path(); char dirSeparator; - #ifdef _WIN32 - dirSeparator = '\\'; - #else - dirSeparator = '/'; - #endif +#ifdef _WIN32 + dirSeparator = '\\'; +#else + dirSeparator = '/'; +#endif std::string filePath = currentPath; - for (int i = 0; i < input.size(); i++) { - filePath = filePath + dirSeparator + input[i]; - } + for (int i = 0; i < input.size(); i++) { + filePath = filePath + dirSeparator + input[i]; + } return filePath; } diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h index 46906534d4..7b0517503b 100644 --- a/common/util/FileUtil.h +++ b/common/util/FileUtil.h @@ -2,8 +2,7 @@ #include #include -namespace FileUtil -{ - std::string GetCurrentWorkingDir(); - std::string get_file_path(std::vector input); -} +namespace FileUtil { +std::string GetCurrentWorkingDir(); +std::string get_file_path(std::vector input); +} // namespace FileUtil diff --git a/test/test_common_util.cpp b/test/test_common_util.cpp index e9862573c8..96e1492133 100644 --- a/test/test_common_util.cpp +++ b/test/test_common_util.cpp @@ -4,7 +4,6 @@ #include TEST(FileUtil, valid_path) { - std::vector test = {"cabbage", "banana", "apple"}; std::string sampleString = FileUtil::get_file_path(test); // std::cout << sampleString << std::endl; From 01883da47eabc768079559d9109f044940cbe5ee Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Wed, 9 Sep 2020 20:14:13 -0400 Subject: [PATCH 25/50] Fix Listener Socket Timeout on Windows (#28) * try checking for timeouts differently on windows * hopefully this test fails on windows * hopefully this test passes on windows * remove debug prints * remove commented otu code --- common/cross_sockets/xsocket.cpp | 9 +++++++++ common/cross_sockets/xsocket.h | 1 + goalc/listener/Listener.cpp | 13 +++++-------- test/test_listener_deci2.cpp | 18 ++++++++++++++++++ 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/common/cross_sockets/xsocket.cpp b/common/cross_sockets/xsocket.cpp index f4c89df7e8..763cf596f4 100644 --- a/common/cross_sockets/xsocket.cpp +++ b/common/cross_sockets/xsocket.cpp @@ -86,3 +86,12 @@ int read_from_socket(int socket, char* buf, int len) { return recv(socket, buf, len, 0); #endif } + +bool socket_timed_out() { +#ifdef __linux + return errno == EAGAIN; +#elif _WIN32 + auto err = WSAGetLastError(); + return err == WSAETIMEDOUT; +#endif +} \ No newline at end of file diff --git a/common/cross_sockets/xsocket.h b/common/cross_sockets/xsocket.h index eb8e09b38c..d0e4bb1c16 100644 --- a/common/cross_sockets/xsocket.h +++ b/common/cross_sockets/xsocket.h @@ -18,3 +18,4 @@ int set_socket_option(int socket, int level, int optname, const void* optval, in int set_socket_timeout(int socket, long microSeconds); int write_to_socket(int socket, const char* buf, int len); int read_from_socket(int socket, char* buf, int len); +bool socket_timed_out(); \ No newline at end of file diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index 454dcd46f3..1fdb4bbe3d 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -123,7 +123,7 @@ bool Listener::connect_to_target(int n_tries, const std::string& ip, int port) { listen_socket = -1; return false; } else { - printf("[Listener] Socket connected established! (took %d tries)\n", i); + printf("[Listener] Socket connected established! (took %d tries). Waiting for version...\n", i); } // get the GOAL version number, to make sure we connected to the right thing @@ -133,11 +133,8 @@ bool Listener::connect_to_target(int n_tries, const std::string& ip, int port) { bool ok = true; while (prog < 8) { auto r = read_from_socket(listen_socket, (char*)version_buffer + prog, 8 - prog); - if (r < 0) { - ok = false; - break; - } - prog += r; + std::this_thread::sleep_for(std::chrono::microseconds(100000)); + prog += r > 0 ? r : 0; read_tries++; if (read_tries > 50) { ok = false; @@ -181,7 +178,7 @@ void Listener::receive_func() { rcvd += got > 0 ? got : 0; // kick us out if we got a bogus read result - if (got == 0 || (got == -1 && errno != EAGAIN)) { + if (got == 0 || (got == -1 && !socket_timed_out())) { m_connected = false; } @@ -238,7 +235,7 @@ void Listener::receive_func() { got = got > 0 ? got : 0; rcvd += got; msg_prog += got; - if (got == 0 || (got == -1 && errno != EAGAIN)) { + if (got == 0 || (got == -1 && !socket_timed_out())) { m_connected = false; } } diff --git a/test/test_listener_deci2.cpp b/test/test_listener_deci2.cpp index 158a7afb7a..ebc5aedeab 100644 --- a/test/test_listener_deci2.cpp +++ b/test/test_listener_deci2.cpp @@ -45,6 +45,24 @@ TEST(Listener, DeciCheckNoListener) { EXPECT_FALSE(s.check_for_listener()); } +TEST(Listener, CheckConnectionStaysAlive) { + Deci2Server s(always_false); + EXPECT_TRUE(s.init()); + EXPECT_FALSE(s.check_for_listener()); + Listener l; + EXPECT_FALSE(s.check_for_listener()); + bool connected = l.connect_to_target(); + EXPECT_TRUE(connected); + // TODO - some sort of backoff and retry would be better + while (connected && !s.check_for_listener()) { + } + + EXPECT_TRUE(s.check_for_listener()); + std::this_thread::sleep_for(std::chrono::seconds(2)); // sorry for making tests slow. + EXPECT_TRUE(s.check_for_listener()); + EXPECT_TRUE(l.is_connected()); +} + TEST(Listener, DeciThenListener) { for (int i = 0; i < 3; i++) { Deci2Server s(always_false); From 369a1031e12193abc91e110aca352b4ea7d9ad70 Mon Sep 17 00:00:00 2001 From: blahpy Date: Thu, 10 Sep 2020 22:07:23 +1200 Subject: [PATCH 26/50] Update functions --- common/util/FileUtil.cpp | 27 ++++++++++++++++++--------- common/util/FileUtil.h | 4 ++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index 876f927b3a..726407177d 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -3,22 +3,31 @@ #include /* defines FILENAME_MAX */ #ifdef _WIN32 -#include -#define GetCurrentDir _getcwd +#include #else #include -#define GetCurrentDir getcwd #endif -std::string FileUtil::GetCurrentWorkingDir() { - char buff[FILENAME_MAX]; - GetCurrentDir(buff, FILENAME_MAX); - std::string current_working_dir(buff); - return current_working_dir; +std::string FileUtil::GetExecutablePath() { +#ifdef _WIN32 + char buffer[FILENAME_MAX]; + GetModuleFileNameA(NULL, buffer, FILENAME_MAX); + std::string::size_type pos = + std::string(buffer).find_last_of("/\\"); // Strip executable from file path + return std::string(buffer).substr(0, pos); +#else // do Linux stuff + char buffer[FILENAME_MAX]; + readlink("/proc/self/exe", buffer, + FILENAME_MAX); // /proc/self acts like a "virtual folder" containing information about + // the current process + std::string::size_type pos = + std::string(buffer).find_last_of("/\\"); // Strip executable from file path + return std::string(buffer).substr(0, pos); +#endif } std::string FileUtil::get_file_path(const std::vector& input) { - std::string currentPath = FileUtil::GetCurrentWorkingDir(); // std::filesystem::current_path(); + std::string currentPath = FileUtil::GetExecutablePath(); char dirSeparator; #ifdef _WIN32 diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h index 7b0517503b..97e4ea97e5 100644 --- a/common/util/FileUtil.h +++ b/common/util/FileUtil.h @@ -3,6 +3,6 @@ #include namespace FileUtil { -std::string GetCurrentWorkingDir(); -std::string get_file_path(std::vector input); +std::string GetExecutablePath(); +std::string get_file_path(const std::vector& input); } // namespace FileUtil From 2e270eeab40ca95bafee2f7f07f5788d2109e57a Mon Sep 17 00:00:00 2001 From: blahpy Date: Thu, 10 Sep 2020 22:26:13 +1200 Subject: [PATCH 27/50] Update function names and file path stripping --- common/util/FileUtil.cpp | 12 ++++++------ common/util/FileUtil.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index 726407177d..40c13d5a64 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -8,26 +8,26 @@ #include #endif -std::string FileUtil::GetExecutablePath() { +std::string FileUtil::GetProjectPath() { #ifdef _WIN32 char buffer[FILENAME_MAX]; GetModuleFileNameA(NULL, buffer, FILENAME_MAX); std::string::size_type pos = - std::string(buffer).find_last_of("/\\"); // Strip executable from file path - return std::string(buffer).substr(0, pos); + std::string(buffer).rfind("\\jak-project\\"); // Strip file path down to \jak-project\ directory + return std::string(buffer).substr(0, pos + 12); // + 12 to include "\jak-project" in the returned filepath #else // do Linux stuff char buffer[FILENAME_MAX]; readlink("/proc/self/exe", buffer, FILENAME_MAX); // /proc/self acts like a "virtual folder" containing information about // the current process std::string::size_type pos = - std::string(buffer).find_last_of("/\\"); // Strip executable from file path - return std::string(buffer).substr(0, pos); + std::string(buffer).find_last_of("/jak-project/"); // Strip file path down to /jak-project/ directory + return std::string(buffer).substr(0, pos + 12); // + 12 to include "/jak-project" in the returned filepath #endif } std::string FileUtil::get_file_path(const std::vector& input) { - std::string currentPath = FileUtil::GetExecutablePath(); + std::string currentPath = FileUtil::GetProjectPath(); char dirSeparator; #ifdef _WIN32 diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h index 97e4ea97e5..a5ebddcfe8 100644 --- a/common/util/FileUtil.h +++ b/common/util/FileUtil.h @@ -3,6 +3,6 @@ #include namespace FileUtil { -std::string GetExecutablePath(); +std::string GetProjectPath(); std::string get_file_path(const std::vector& input); } // namespace FileUtil From a7ff8d9e1db29d871b310f0ac00f16ddd40d7577 Mon Sep 17 00:00:00 2001 From: blahpy Date: Fri, 11 Sep 2020 09:24:03 +1200 Subject: [PATCH 28/50] Format using clang-format --- common/util/FileUtil.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index 40c13d5a64..163bc7d2ee 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -12,17 +12,19 @@ std::string FileUtil::GetProjectPath() { #ifdef _WIN32 char buffer[FILENAME_MAX]; GetModuleFileNameA(NULL, buffer, FILENAME_MAX); - std::string::size_type pos = - std::string(buffer).rfind("\\jak-project\\"); // Strip file path down to \jak-project\ directory - return std::string(buffer).substr(0, pos + 12); // + 12 to include "\jak-project" in the returned filepath + std::string::size_type pos = std::string(buffer).rfind( + "\\jak-project\\"); // Strip file path down to \jak-project\ directory + return std::string(buffer).substr( + 0, pos + 12); // + 12 to include "\jak-project" in the returned filepath #else // do Linux stuff char buffer[FILENAME_MAX]; readlink("/proc/self/exe", buffer, FILENAME_MAX); // /proc/self acts like a "virtual folder" containing information about // the current process - std::string::size_type pos = - std::string(buffer).find_last_of("/jak-project/"); // Strip file path down to /jak-project/ directory - return std::string(buffer).substr(0, pos + 12); // + 12 to include "/jak-project" in the returned filepath + std::string::size_type pos = std::string(buffer).find_last_of( + "/jak-project/"); // Strip file path down to /jak-project/ directory + return std::string(buffer).substr( + 0, pos + 12); // + 12 to include "/jak-project" in the returned filepath #endif } From 06963ed36ffcdd465437149096cb6c28f7f1833e Mon Sep 17 00:00:00 2001 From: blahpy Date: Fri, 11 Sep 2020 10:30:45 +1200 Subject: [PATCH 29/50] reformat using clang-format --- common/util/FileUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index 163bc7d2ee..5db532eef5 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -16,7 +16,7 @@ std::string FileUtil::GetProjectPath() { "\\jak-project\\"); // Strip file path down to \jak-project\ directory return std::string(buffer).substr( 0, pos + 12); // + 12 to include "\jak-project" in the returned filepath -#else // do Linux stuff +#else // do Linux stuff char buffer[FILENAME_MAX]; readlink("/proc/self/exe", buffer, FILENAME_MAX); // /proc/self acts like a "virtual folder" containing information about From de5aa7e5e4e4e0f952cf315fc09d202816cc94d6 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Thu, 10 Sep 2020 20:03:31 -0400 Subject: [PATCH 30/50] Move duplicated utilities to the common util folder and remove `NEXT_DIR` (#29) * move things to the common library and remove next_dir * fix for windows * one last windows fix * last fix for real this time * debug listener test * fix listener threading bug --- {decompiler => common}/util/BinaryReader.h | 0 common/util/CMakeLists.txt | 3 +- common/util/FileUtil.cpp | 71 +++++++++++++++++-- common/util/FileUtil.h | 11 ++- {goalc => common}/util/MatchParam.h | 2 - {decompiler => common}/util/Timer.cpp | 0 {decompiler => common}/util/Timer.h | 0 decompiler/CMakeLists.txt | 4 +- decompiler/Disasm/InstructionMatching.h | 18 +---- decompiler/ObjectFile/ObjectFileDB.cpp | 13 ++-- decompiler/config.cpp | 3 +- decompiler/main.cpp | 5 +- decompiler/util/FileIO.cpp | 35 --------- decompiler/util/FileIO.h | 4 -- game/CMakeLists.txt | 4 +- game/overlord/fake_iso.cpp | 15 +--- gc.sh | 2 - gk.sh | 2 - goalc/CMakeLists.txt | 7 +- goalc/compiler/Compiler.cpp | 4 +- goalc/compiler/Compiler.h | 5 +- goalc/compiler/Util.cpp | 5 +- .../compiler/compilation/CompilerControl.cpp | 8 +-- goalc/goos/CMakeLists.txt | 2 +- goalc/goos/Interpreter.cpp | 4 +- goalc/goos/Interpreter.h | 6 +- goalc/goos/InterpreterEval.cpp | 4 +- goalc/goos/Object.cpp | 4 +- goalc/goos/Reader.cpp | 20 ++---- goalc/goos/Reader.h | 3 +- goalc/goos/TextDB.cpp | 4 +- goalc/listener/Listener.cpp | 9 ++- goalc/util/CMakeLists.txt | 1 - goalc/util/Timer.cpp | 54 -------------- goalc/util/Timer.h | 47 ------------ goalc/util/file_io.cpp | 45 ------------ goalc/util/file_io.h | 14 ---- goalc/util/text_util.cpp | 16 ----- goalc/util/text_util.h | 13 ---- test-cov.sh | 2 - test.sh | 2 - test/CMakeLists.txt | 4 +- test/test_common_util.cpp | 2 +- test/test_reader.cpp | 10 +-- test_code_coverage.sh | 2 - 45 files changed, 138 insertions(+), 351 deletions(-) rename {decompiler => common}/util/BinaryReader.h (100%) rename {goalc => common}/util/MatchParam.h (93%) rename {decompiler => common}/util/Timer.cpp (100%) rename {decompiler => common}/util/Timer.h (100%) delete mode 100644 goalc/util/CMakeLists.txt delete mode 100644 goalc/util/Timer.cpp delete mode 100644 goalc/util/Timer.h delete mode 100644 goalc/util/file_io.cpp delete mode 100644 goalc/util/file_io.h delete mode 100644 goalc/util/text_util.cpp delete mode 100644 goalc/util/text_util.h diff --git a/decompiler/util/BinaryReader.h b/common/util/BinaryReader.h similarity index 100% rename from decompiler/util/BinaryReader.h rename to common/util/BinaryReader.h diff --git a/common/util/CMakeLists.txt b/common/util/CMakeLists.txt index 1e2bef4c7e..23f5a949df 100644 --- a/common/util/CMakeLists.txt +++ b/common/util/CMakeLists.txt @@ -1,3 +1,4 @@ add_library(common_util SHARED - FileUtil.cpp) + FileUtil.cpp + Timer.cpp) diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index 5db532eef5..01c07c1997 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -1,6 +1,9 @@ #include "FileUtil.h" #include #include /* defines FILENAME_MAX */ +#include +#include +#include #ifdef _WIN32 #include @@ -8,7 +11,7 @@ #include #endif -std::string FileUtil::GetProjectPath() { +std::string file_util::get_project_path() { #ifdef _WIN32 char buffer[FILENAME_MAX]; GetModuleFileNameA(NULL, buffer, FILENAME_MAX); @@ -16,20 +19,21 @@ std::string FileUtil::GetProjectPath() { "\\jak-project\\"); // Strip file path down to \jak-project\ directory return std::string(buffer).substr( 0, pos + 12); // + 12 to include "\jak-project" in the returned filepath -#else // do Linux stuff +#else + // do Linux stuff char buffer[FILENAME_MAX]; readlink("/proc/self/exe", buffer, FILENAME_MAX); // /proc/self acts like a "virtual folder" containing information about // the current process - std::string::size_type pos = std::string(buffer).find_last_of( + std::string::size_type pos = std::string(buffer).rfind( "/jak-project/"); // Strip file path down to /jak-project/ directory return std::string(buffer).substr( 0, pos + 12); // + 12 to include "/jak-project" in the returned filepath #endif } -std::string FileUtil::get_file_path(const std::vector& input) { - std::string currentPath = FileUtil::GetProjectPath(); +std::string file_util::get_file_path(const std::vector& input) { + std::string currentPath = file_util::get_project_path(); char dirSeparator; #ifdef _WIN32 @@ -39,9 +43,64 @@ std::string FileUtil::get_file_path(const std::vector& input) { #endif std::string filePath = currentPath; - for (int i = 0; i < input.size(); i++) { + for (int i = 0; i < int(input.size()); i++) { filePath = filePath + dirSeparator + input[i]; } return filePath; } + +void file_util::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); +} + +void file_util::write_text_file(const std::string& file_name, const std::string& text) { + FILE* fp = fopen(file_name.c_str(), "w"); + if (!fp) { + printf("Failed to fopen %s\n", file_name.c_str()); + throw std::runtime_error("Failed to open file"); + } + fprintf(fp, "%s\n", text.c_str()); + fclose(fp); +} + +std::vector file_util::read_binary_file(const std::string& filename) { + auto fp = fopen(filename.c_str(), "rb"); + if (!fp) + throw std::runtime_error("File " + filename + " cannot be opened"); + fseek(fp, 0, SEEK_END); + auto len = ftell(fp); + rewind(fp); + + std::vector data; + data.resize(len); + + if (fread(data.data(), len, 1, fp) != 1) { + throw std::runtime_error("File " + filename + " cannot be read"); + } + + return data; +} + +std::string file_util::read_text_file(const std::string& path) { + std::ifstream file(path); + if (!file.good()) { + throw std::runtime_error("couldn't open " + path); + } + std::stringstream ss; + ss << file.rdbuf(); + return ss.str(); +} + +bool file_util::is_printable_char(char c) { + return c >= ' ' && c <= '~'; +} \ No newline at end of file diff --git a/common/util/FileUtil.h b/common/util/FileUtil.h index a5ebddcfe8..d2a2b68abc 100644 --- a/common/util/FileUtil.h +++ b/common/util/FileUtil.h @@ -2,7 +2,12 @@ #include #include -namespace FileUtil { -std::string GetProjectPath(); +namespace file_util { +std::string get_project_path(); std::string get_file_path(const std::vector& input); -} // namespace FileUtil +void write_binary_file(const std::string& name, void* data, size_t size); +void write_text_file(const std::string& file_name, const std::string& text); +std::vector read_binary_file(const std::string& filename); +std::string read_text_file(const std::string& path); +bool is_printable_char(char c); +} // namespace file_util diff --git a/goalc/util/MatchParam.h b/common/util/MatchParam.h similarity index 93% rename from goalc/util/MatchParam.h rename to common/util/MatchParam.h index e8a5cc8c12..5c954ea725 100644 --- a/goalc/util/MatchParam.h +++ b/common/util/MatchParam.h @@ -1,7 +1,6 @@ #ifndef JAK1_MATCHPARAM_H #define JAK1_MATCHPARAM_H -namespace util { template struct MatchParam { MatchParam() { is_wildcard = true; } @@ -18,6 +17,5 @@ struct MatchParam { bool operator==(const T& other) const { return is_wildcard || (value == other); } bool operator!=(const T& other) const { return !(*this == other); } }; -} // namespace util #endif // JAK1_MATCHPARAM_H diff --git a/decompiler/util/Timer.cpp b/common/util/Timer.cpp similarity index 100% rename from decompiler/util/Timer.cpp rename to common/util/Timer.cpp diff --git a/decompiler/util/Timer.h b/common/util/Timer.h similarity index 100% rename from decompiler/util/Timer.h rename to common/util/Timer.h diff --git a/decompiler/CMakeLists.txt b/decompiler/CMakeLists.txt index 16aecbbc36..eca54d7d2f 100644 --- a/decompiler/CMakeLists.txt +++ b/decompiler/CMakeLists.txt @@ -12,7 +12,6 @@ add_executable(decompiler util/FileIO.cpp config.cpp util/LispPrint.cpp - util/Timer.cpp Function/BasicBlocks.cpp Disasm/InstructionMatching.cpp TypeSystem/GoalType.cpp @@ -22,4 +21,5 @@ add_executable(decompiler TypeSystem/TypeSpec.cpp Function/CfgVtx.cpp Function/CfgVtx.h) target_link_libraries(decompiler - minilzo) \ No newline at end of file + minilzo + common_util) \ No newline at end of file diff --git a/decompiler/Disasm/InstructionMatching.h b/decompiler/Disasm/InstructionMatching.h index 96098cac92..54375d27a8 100644 --- a/decompiler/Disasm/InstructionMatching.h +++ b/decompiler/Disasm/InstructionMatching.h @@ -2,23 +2,7 @@ #define JAK_DISASSEMBLER_INSTRUCTIONMATCHING_H #include "Instruction.h" - -template -struct MatchParam { - MatchParam() { is_wildcard = true; } - - // intentionally not explicit so you don't have to put MatchParam(blah) everywhere - MatchParam(T x) { - value = x; - is_wildcard = false; - } - - T value; - bool is_wildcard = true; - - bool operator==(const T& other) { return is_wildcard || (value == other); } - bool operator!=(const T& other) { return !(*this == other); } -}; +#include "common/util/MatchParam.h" bool is_no_link_gpr_store(const Instruction& instr, MatchParam size, diff --git a/decompiler/ObjectFile/ObjectFileDB.cpp b/decompiler/ObjectFile/ObjectFileDB.cpp index 92a5c0c8d6..df1fbe711f 100644 --- a/decompiler/ObjectFile/ObjectFileDB.cpp +++ b/decompiler/ObjectFile/ObjectFileDB.cpp @@ -13,9 +13,10 @@ #include "LinkedObjectFileCreation.h" #include "decompiler/config.h" #include "third-party/minilzo/minilzo.h" -#include "decompiler/util/BinaryReader.h" +#include "common/util/BinaryReader.h" #include "decompiler/util/FileIO.h" -#include "decompiler/util/Timer.h" +#include "common/util/Timer.h" +#include "common/util/FileUtil.h" #include "decompiler/Function/BasicBlocks.h" /*! @@ -142,7 +143,7 @@ constexpr int MAX_CHUNK_SIZE = 0x8000; * Load the objects stored in the given DGO into the ObjectFileDB */ void ObjectFileDB::get_objs_from_dgo(const std::string& filename) { - auto dgo_data = read_binary_file(filename); + auto dgo_data = file_util::read_binary_file(filename); stats.total_dgo_bytes += dgo_data.size(); const char jak2_header[] = "oZlB"; @@ -415,7 +416,7 @@ void ObjectFileDB::write_object_file_words(const std::string& output_dir, bool d auto file_text = obj.linked_data.print_words(); auto file_name = combine_path(output_dir, obj.record.to_unique_name() + ".txt"); total_bytes += file_text.size(); - write_text_file(file_name, file_text); + file_util::write_text_file(file_name, file_text); total_files++; } }); @@ -442,7 +443,7 @@ void ObjectFileDB::write_disassembly(const std::string& output_dir, auto file_text = obj.linked_data.print_disassembly(); auto file_name = combine_path(output_dir, obj.record.to_unique_name() + ".func"); total_bytes += file_text.size(); - write_text_file(file_name, file_text); + file_util::write_text_file(file_name, file_text); total_files++; } }); @@ -517,7 +518,7 @@ void ObjectFileDB::find_and_write_scripts(const std::string& output_dir) { }); auto file_name = combine_path(output_dir, "all_scripts.lisp"); - write_text_file(file_name, all_scripts); + file_util::write_text_file(file_name, all_scripts); printf("Found scripts:\n"); printf(" total %.3f ms\n", timer.getMs()); diff --git a/decompiler/config.cpp b/decompiler/config.cpp index f633806745..9f67840882 100644 --- a/decompiler/config.cpp +++ b/decompiler/config.cpp @@ -1,6 +1,7 @@ #include "config.h" #include "third-party/json.hpp" #include "util/FileIO.h" +#include "common/util/FileUtil.h" Config gConfig; @@ -9,7 +10,7 @@ Config& get_config() { } void set_config(const std::string& path_to_config_file) { - auto config_str = read_text_file(path_to_config_file); + auto config_str = file_util::read_text_file(path_to_config_file); // to ignore comments in json, which may be useful auto cfg = nlohmann::json::parse(config_str, nullptr, true, true); diff --git a/decompiler/main.cpp b/decompiler/main.cpp index 034bc819ad..85d63c64a2 100644 --- a/decompiler/main.cpp +++ b/decompiler/main.cpp @@ -5,6 +5,7 @@ #include "config.h" #include "util/FileIO.h" #include "TypeSystem/TypeInfo.h" +#include "common/util/FileUtil.h" int main(int argc, char** argv) { printf("Jak Disassembler\n"); @@ -26,8 +27,8 @@ int main(int argc, char** argv) { } ObjectFileDB db(dgos); - write_text_file(combine_path(out_folder, "dgo.txt"), db.generate_dgo_listing()); - write_text_file(combine_path(out_folder, "obj.txt"), db.generate_obj_listing()); + file_util::write_text_file(combine_path(out_folder, "dgo.txt"), db.generate_dgo_listing()); + file_util::write_text_file(combine_path(out_folder, "obj.txt"), db.generate_obj_listing()); db.process_link_data(); db.find_code(); diff --git a/decompiler/util/FileIO.cpp b/decompiler/util/FileIO.cpp index 5466d18653..c92bb85524 100644 --- a/decompiler/util/FileIO.cpp +++ b/decompiler/util/FileIO.cpp @@ -3,35 +3,10 @@ #include #include -std::string read_text_file(const std::string& path) { - std::ifstream file(path); - std::stringstream ss; - ss << file.rdbuf(); - return ss.str(); -} - std::string combine_path(const std::string& parent, const std::string& child) { return parent + "/" + child; } -std::vector read_binary_file(const std::string& filename) { - auto fp = fopen(filename.c_str(), "rb"); - if (!fp) - throw std::runtime_error("File " + filename + " cannot be opened"); - fseek(fp, 0, SEEK_END); - auto len = ftell(fp); - rewind(fp); - - std::vector data; - data.resize(len); - - if (fread(data.data(), len, 1, fp) != 1) { - throw std::runtime_error("File " + filename + " cannot be read"); - } - - return data; -} - std::string base_name(const std::string& filename) { size_t pos = 0; assert(!filename.empty()); @@ -70,13 +45,3 @@ uint32_t crc32(const uint8_t* data, size_t size) { uint32_t crc32(const std::vector& data) { return crc32(data.data(), data.size()); } - -void write_text_file(const std::string& file_name, const std::string& text) { - FILE* fp = fopen(file_name.c_str(), "w"); - if (!fp) { - printf("Failed to fopen %s\n", file_name.c_str()); - throw std::runtime_error("Failed to open file"); - } - fprintf(fp, "%s\n", text.c_str()); - fclose(fp); -} \ No newline at end of file diff --git a/decompiler/util/FileIO.h b/decompiler/util/FileIO.h index 8f02577f4c..5893997f25 100644 --- a/decompiler/util/FileIO.h +++ b/decompiler/util/FileIO.h @@ -4,12 +4,8 @@ #include #include -std::string read_text_file(const std::string& path); std::string combine_path(const std::string& parent, const std::string& child); -std::vector read_binary_file(const std::string& filename); std::string base_name(const std::string& filename); -void write_text_file(const std::string& file_name, const std::string& text); - void init_crc(); uint32_t crc32(const uint8_t* data, size_t size); uint32_t crc32(const std::vector& data); diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index d73e4968fe..6fb1dacae2 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -78,8 +78,8 @@ add_library(runtime ${RUNTIME_SOURCE}) IF (WIN32) # set stuff for windows - target_link_libraries(gk cross_sockets mman) + target_link_libraries(gk cross_sockets mman common_util) ELSE() # set stuff for other systems - target_link_libraries(gk cross_sockets pthread) + target_link_libraries(gk cross_sockets pthread common_util) ENDIF() diff --git a/game/overlord/fake_iso.cpp b/game/overlord/fake_iso.cpp index 45603a03c6..76284f2531 100644 --- a/game/overlord/fake_iso.cpp +++ b/game/overlord/fake_iso.cpp @@ -15,6 +15,7 @@ #include "game/sce/iop.h" #include "isocommon.h" #include "overlord.h" +#include "common/util/FileUtil.h" using namespace iop; @@ -71,25 +72,15 @@ void fake_iso_init_globals() { read_in_progress = false; } -//! will hold prefix for the source folder. -static const char* next_dir = nullptr; -static const char* fake_iso_path = nullptr; - /*! * Initialize the file system. */ int FS_Init(u8* buffer) { (void)buffer; - // get path to next/. Will be set in the gk.sh launch script. - next_dir = std::getenv("NEXT_DIR"); - assert(next_dir); // get path to next/data/fake_iso.txt, the map file. char fakeiso_path[512]; - strcpy(fakeiso_path, next_dir); - fake_iso_path = std::getenv("FAKE_ISO_PATH"); - assert(fake_iso_path); - strcat(fakeiso_path, fake_iso_path); + strcpy(fakeiso_path, file_util::get_file_path({"game", "fake_iso.txt"}).c_str()); // open the map. FILE* fp = fopen(fakeiso_path, "r"); @@ -197,7 +188,7 @@ FileRecord* FS_FindIN(const char* iso_name) { 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); + strcpy(path_buffer, file_util::get_project_path().c_str()); strcat(path_buffer, "/"); strcat(path_buffer, fake_iso_entries[fr->location].file_path); return path_buffer; diff --git a/gc.sh b/gc.sh index ac079580a1..1d1e30f93a 100755 --- a/gc.sh +++ b/gc.sh @@ -3,6 +3,4 @@ # Directory of this script DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export NEXT_DIR=$DIR -export FAKE_ISO_PATH=/game/fake_iso.txt $DIR/build/goalc/goalc "$@" \ No newline at end of file diff --git a/gk.sh b/gk.sh index d49ccff100..d103729b8f 100755 --- a/gk.sh +++ b/gk.sh @@ -3,6 +3,4 @@ # Directory of this script DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export NEXT_DIR=$DIR -export FAKE_ISO_PATH=/game/fake_iso.txt $DIR/build/game/gk "$@" diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 2e8ab64256..5226e04a7f 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -1,4 +1,3 @@ -add_subdirectory(util) add_subdirectory(goos) add_subdirectory(listener) @@ -28,10 +27,10 @@ add_library(compiler add_executable(goalc main.cpp) IF (WIN32) - target_link_libraries(compiler util goos type_system listener mman) + target_link_libraries(compiler goos type_system listener mman common_util) ELSE () - target_link_libraries(compiler util goos type_system listener) + target_link_libraries(compiler goos type_system listener common_util) ENDIF () -target_link_libraries(goalc util goos compiler type_system) +target_link_libraries(goalc goos compiler type_system) diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 1edede33bd..f950c4e229 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -15,7 +15,7 @@ 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"); + Object library_code = m_goos.reader.read_from_file({"goal_src", "goal-lib.gc"}); compile_object_file("goal-lib", library_code, false); } @@ -178,7 +178,7 @@ std::vector Compiler::run_test(const std::string& source_code) { } } - auto code = m_goos.reader.read_from_file(source_code); + 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); diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 215ee61f0b..5d63eafb8a 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -48,9 +48,8 @@ class Compiler { void va_check( const goos::Object& form, const goos::Arguments& args, - const std::vector>& unnamed, - const std::unordered_map>>& - named); + 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); const goos::Object& pair_car(const goos::Object& o); diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index c891c5be29..4795626bca 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -37,9 +37,8 @@ goos::Arguments Compiler::get_va(const goos::Object& form, const goos::Object& r void Compiler::va_check( const goos::Object& form, const goos::Arguments& args, - const std::vector>& unnamed, - const std::unordered_map>>& - named) { + 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()) + diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 6d09bf9067..be3d944d24 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -1,7 +1,7 @@ #include "goalc/compiler/Compiler.h" #include "goalc/compiler/IR.h" -#include "goalc/util/Timer.h" -#include "goalc/util/file_io.h" +#include "common/util/Timer.h" +#include "common/util/FileUtil.h" Val* Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, Env* env) { (void)env; @@ -59,7 +59,7 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re }); Timer reader_timer; - auto code = m_goos.reader.read_from_file(filename); + auto code = m_goos.reader.read_from_file({filename}); timing.emplace_back("read", reader_timer.getMs()); Timer compile_timer; @@ -95,7 +95,7 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re // 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()); + file_util::write_binary_file(output_name, (void*)data.data(), data.size()); } } else { if (load) { diff --git a/goalc/goos/CMakeLists.txt b/goalc/goos/CMakeLists.txt index a6b2e6b489..170c8df0d8 100644 --- a/goalc/goos/CMakeLists.txt +++ b/goalc/goos/CMakeLists.txt @@ -1,2 +1,2 @@ add_library(goos SHARED Object.cpp TextDB.cpp Reader.cpp Interpreter.cpp InterpreterEval.cpp) -target_link_libraries(goos util) \ No newline at end of file +target_link_libraries(goos common_util) \ No newline at end of file diff --git a/goalc/goos/Interpreter.cpp b/goalc/goos/Interpreter.cpp index 7e1c411315..3c3eb8569e 100644 --- a/goalc/goos/Interpreter.cpp +++ b/goalc/goos/Interpreter.cpp @@ -379,8 +379,8 @@ ArgumentSpec Interpreter::parse_arg_spec(const Object& form, Object& rest) { void Interpreter::vararg_check( const Object& form, const Arguments& args, - const std::vector>& unnamed, - const std::unordered_map>>& named) { + const std::vector>& unnamed, + const std::unordered_map>>& named) { assert(args.rest.empty()); if (unnamed.size() != args.unnamed.size()) { throw_eval_error(form, "Got " + std::to_string(args.unnamed.size()) + diff --git a/goalc/goos/Interpreter.h b/goalc/goos/Interpreter.h index 3d58a5c369..ebc78906ab 100644 --- a/goalc/goos/Interpreter.h +++ b/goalc/goos/Interpreter.h @@ -9,7 +9,7 @@ #include #include "Object.h" #include "Reader.h" -#include "goalc/util/MatchParam.h" +#include "common/util/MatchParam.h" namespace goos { class Interpreter { @@ -53,8 +53,8 @@ class Interpreter { void vararg_check( const Object& form, const Arguments& args, - const std::vector>& unnamed, - const std::unordered_map>>& named); + const std::vector>& unnamed, + const std::unordered_map>>& named); Object eval_pair(const Object& o, const std::shared_ptr& env); void eval_args(Arguments* args, const std::shared_ptr& env); diff --git a/goalc/goos/InterpreterEval.cpp b/goalc/goos/InterpreterEval.cpp index 8f73c46052..bd3e8eac47 100644 --- a/goalc/goos/InterpreterEval.cpp +++ b/goalc/goos/InterpreterEval.cpp @@ -66,7 +66,7 @@ Object Interpreter::eval_read_file(const Object& form, vararg_check(form, args, {ObjectType::STRING}, {}); try { - return reader.read_from_file(args.unnamed.at(0).as_string()->data); + return reader.read_from_file({args.unnamed.at(0).as_string()->data}); } catch (std::runtime_error& e) { throw_eval_error(form, std::string("reader error inside of read-file:\n") + e.what()); } @@ -84,7 +84,7 @@ Object Interpreter::eval_load_file(const Object& form, Object o; try { - o = reader.read_from_file(args.unnamed.at(0).as_string()->data); + o = reader.read_from_file({args.unnamed.at(0).as_string()->data}); } catch (std::runtime_error& e) { throw_eval_error(form, std::string("reader error inside of load-file:\n") + e.what()); } diff --git a/goalc/goos/Object.cpp b/goalc/goos/Object.cpp index 1023ff3581..000202538c 100644 --- a/goalc/goos/Object.cpp +++ b/goalc/goos/Object.cpp @@ -1,5 +1,5 @@ #include "Object.h" -#include "goalc/util/text_util.h" +#include "common/util/FileUtil.h" namespace goos { @@ -53,7 +53,7 @@ std::string fixed_to_string(FloatType x) { template <> std::string fixed_to_string(char x) { char buff[256]; - if (util::is_printable_char(x) && x != ' ') { + if (file_util::is_printable_char(x) && x != ' ') { // can print directly sprintf(buff, "#\\%c", x); return {buff}; diff --git a/goalc/goos/Reader.cpp b/goalc/goos/Reader.cpp index fa77d54996..a830564831 100644 --- a/goalc/goos/Reader.cpp +++ b/goalc/goos/Reader.cpp @@ -11,8 +11,7 @@ #include "Reader.h" #include "third-party/linenoise.h" -#include "goalc/util/file_io.h" -#include "goalc/util/text_util.h" +#include "common/util/FileUtil.h" namespace goos { @@ -98,15 +97,6 @@ Reader::Reader() { for (const char* c = bonus; *c; c++) { valid_symbols_chars[(int)*c] = true; } - - // find the source directory - auto result = std::getenv("NEXT_DIR"); - if (!result) { - throw std::runtime_error( - "Environment variable NEXT_DIR is not set. Please set this to point to next/"); - } - - source_dir = result; } /*! @@ -147,8 +137,8 @@ Object Reader::read_from_string(const std::string& str) { /*! * Read a file */ -Object Reader::read_from_file(const std::string& filename) { - auto textFrag = std::make_shared(util::combine_path(get_source_dir(), filename)); +Object Reader::read_from_file(const std::vector& file_path) { + auto textFrag = std::make_shared(file_util::get_file_path(file_path)); db.insert(textFrag); auto result = internal_read(textFrag); @@ -670,7 +660,7 @@ bool Reader::try_token_as_integer(const Token& tok, Object& obj) { bool Reader::try_token_as_char(const Token& tok, Object& obj) { if (tok.text.size() >= 3 && tok.text[0] == '#' && tok.text[1] == '\\') { - if (tok.text.size() == 3 && util::is_printable_char(tok.text[2]) && tok.text[2] != ' ') { + if (tok.text.size() == 3 && file_util::is_printable_char(tok.text[2]) && tok.text[2] != ' ') { obj = Object::make_char(tok.text[2]); return true; } @@ -705,6 +695,6 @@ void Reader::throw_reader_error(TextStream& here, const std::string& err, int se * Get the source directory of the current project. */ std::string Reader::get_source_dir() { - return source_dir; + return file_util::get_project_path(); } } // namespace goos diff --git a/goalc/goos/Reader.h b/goalc/goos/Reader.h index 1ac7e6a7a1..36920a737a 100644 --- a/goalc/goos/Reader.h +++ b/goalc/goos/Reader.h @@ -70,7 +70,7 @@ class Reader { Reader(); Object read_from_string(const std::string& str); Object read_from_stdin(const std::string& prompt_name); - Object read_from_file(const std::string& filename); + Object read_from_file(const std::vector& file_path); std::string get_source_dir(); @@ -97,7 +97,6 @@ class Reader { char valid_symbols_chars[256]; - std::string source_dir; std::unordered_map reader_macros; }; } // namespace goos diff --git a/goalc/goos/TextDB.cpp b/goalc/goos/TextDB.cpp index 75ad3369a1..318e6b8968 100644 --- a/goalc/goos/TextDB.cpp +++ b/goalc/goos/TextDB.cpp @@ -11,7 +11,7 @@ * (+ 1 (+ a b)) ; compute the sum */ -#include "goalc/util/file_io.h" +#include "common/util/FileUtil.h" #include "TextDB.h" @@ -79,7 +79,7 @@ std::pair SourceText::get_containing_line(int offset) { * Read text from a file. */ FileText::FileText(std::string filename_) : filename(std::move(filename_)) { - text = util::read_text_file(filename); + text = file_util::read_text_file(filename); build_offsets(); } diff --git a/goalc/listener/Listener.cpp b/goalc/listener/Listener.cpp index 1fdb4bbe3d..3a606d1249 100644 --- a/goalc/listener/Listener.cpp +++ b/goalc/listener/Listener.cpp @@ -151,8 +151,8 @@ bool Listener::connect_to_target(int n_tries, const std::string& ip, int port) { printf("Got version %d.%d", version_buffer[0], version_buffer[1]); if (version_buffer[0] == GOAL_VERSION_MAJOR && version_buffer[1] == GOAL_VERSION_MINOR) { printf(" OK!\n"); - rcv_thread = std::thread(&Listener::receive_func, this); m_connected = true; + rcv_thread = std::thread(&Listener::receive_func, this); receive_thread_running = true; return true; } else { @@ -189,10 +189,9 @@ void Listener::receive_func() { } ListenerMessageHeader* hdr = (ListenerMessageHeader*)buff; - // if(debug_listener) { - // printf("[T -> L] received %d bytes, kind %d\n", - // hdr->deci2_hdr.len, hdr->msg_kind); - // } + if (debug_listener) { + printf("[T -> L] received %d bytes, kind %d\n", hdr->deci2_header.len, int(hdr->msg_kind)); + } switch (hdr->msg_kind) { case ListenerMessageKind::MSG_ACK: diff --git a/goalc/util/CMakeLists.txt b/goalc/util/CMakeLists.txt deleted file mode 100644 index fdca90d0c6..0000000000 --- a/goalc/util/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 4ac44ab25c..0000000000 --- a/goalc/util/Timer.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#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 deleted file mode 100644 index 5972020339..0000000000 --- a/goalc/util/Timer.h +++ /dev/null @@ -1,47 +0,0 @@ -#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 deleted file mode 100644 index 095ad933a8..0000000000 --- a/goalc/util/file_io.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "file_io.h" -#include -#include -#include - -namespace util { -std::string read_text_file(const std::string& path) { - std::ifstream file(path); - if (!file.good()) { - throw std::runtime_error("couldn't open " + path); - } - std::stringstream ss; - ss << file.rdbuf(); - return ss.str(); -} - -std::string combine_path(const std::string& parent, const std::string& child) { - return parent + "/" + child; -} - -std::string combine_path(std::vector path) { - if (path.empty()) { - return {}; - } - std::string result = path.front(); - for (size_t i = 1; i < path.size(); i++) { - result = combine_path(result, path.at(i)); - } - 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 deleted file mode 100644 index 9fa7802040..0000000000 --- a/goalc/util/file_io.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef JAK1_FILE_IO_H -#define JAK1_FILE_IO_H - -#include -#include - -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/goalc/util/text_util.cpp b/goalc/util/text_util.cpp deleted file mode 100644 index 9139c06584..0000000000 --- a/goalc/util/text_util.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * @file text_util.cpp - * Utilities for dealing with text. - */ - -#include "text_util.h" - -namespace util { -/*! - * Is c printable? Is true for letters, numbers, symbols, space, false for everything else. - * Note: newline/tab is not considered printable. - */ -bool is_printable_char(char c) { - return c >= ' ' && c <= '~'; -} -} // namespace util diff --git a/goalc/util/text_util.h b/goalc/util/text_util.h deleted file mode 100644 index fc61e5aa29..0000000000 --- a/goalc/util/text_util.h +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * @file text_util.h - * Utilities for dealing with text. - */ - -#ifndef JAK1_TEXT_UTIL_H -#define JAK1_TEXT_UTIL_H - -namespace util { -bool is_printable_char(char c); -} - -#endif // JAK1_TEXT_UTIL_H diff --git a/test-cov.sh b/test-cov.sh index f5b98c515a..4d4e2bcc07 100644 --- a/test-cov.sh +++ b/test-cov.sh @@ -3,8 +3,6 @@ # Directory of this script DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export NEXT_DIR=$DIR -export FAKE_ISO_PATH=/game/fake_iso.txt cd $DIR/build/test make init make gcov diff --git a/test.sh b/test.sh index 573dafc226..fa5c17cf30 100755 --- a/test.sh +++ b/test.sh @@ -3,6 +3,4 @@ # Directory of this script DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export NEXT_DIR=$DIR -export FAKE_ISO_PATH=/game/fake_iso.txt $DIR/build/test/goalc-test --gtest_color=yes "$@" diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index faf41a4da6..adfec63cc2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,9 +22,9 @@ IF (WIN32) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # TODO - split out these declarations for platform specific includes - target_link_libraries(goalc-test cross_sockets listener mman goos util common_util runtime compiler type_system gtest) + target_link_libraries(goalc-test cross_sockets listener mman goos common_util runtime compiler type_system gtest) ELSE() - target_link_libraries(goalc-test cross_sockets goos util common_util listener runtime compiler type_system gtest) + target_link_libraries(goalc-test cross_sockets goos common_util listener runtime compiler type_system gtest) ENDIF() if(CMAKE_COMPILER_IS_GNUCXX AND CODE_COVERAGE) diff --git a/test/test_common_util.cpp b/test/test_common_util.cpp index 96e1492133..91b9f642c7 100644 --- a/test/test_common_util.cpp +++ b/test/test_common_util.cpp @@ -5,7 +5,7 @@ TEST(FileUtil, valid_path) { std::vector test = {"cabbage", "banana", "apple"}; - std::string sampleString = FileUtil::get_file_path(test); + std::string sampleString = file_util::get_file_path(test); // std::cout << sampleString << std::endl; EXPECT_TRUE(true); diff --git a/test/test_reader.cpp b/test/test_reader.cpp index 59ce97e9de..d8f4b68252 100644 --- a/test/test_reader.cpp +++ b/test/test_reader.cpp @@ -6,7 +6,7 @@ #include "gtest/gtest.h" #include "goalc/goos/Reader.h" -#include "goalc/util/file_io.h" +#include "common/util/FileUtil.h" using namespace goos; @@ -316,16 +316,16 @@ TEST(GoosReader, TopLevel) { TEST(GoosReader, FromFile) { Reader reader; - auto result = reader.read_from_file(util::combine_path({"test", "test_reader_file0.gc"})).print(); + auto result = reader.read_from_file({"test", "test_reader_file0.gc"}).print(); EXPECT_TRUE(result == "(top-level (1 2 3 4))"); } TEST(GoosReader, TextDb) { // very specific to this particular test file, but whatever. Reader reader; - auto file_path = util::combine_path({"test", "test_reader_file0.gc"}); - auto result = reader.read_from_file(file_path).as_pair()->cdr.as_pair()->car; - std::string expected = "text from " + util::combine_path(reader.get_source_dir(), file_path) + + auto result = + reader.read_from_file({"test", "test_reader_file0.gc"}).as_pair()->cdr.as_pair()->car; + std::string expected = "text from " + file_util::get_file_path({"test", "test_reader_file0.gc"}) + ", line: 5\n(1 2 3 4)\n"; EXPECT_EQ(expected, reader.db.get_info_for(result)); } diff --git a/test_code_coverage.sh b/test_code_coverage.sh index 482651ed02..82d5464b08 100755 --- a/test_code_coverage.sh +++ b/test_code_coverage.sh @@ -3,7 +3,5 @@ # Directory of this script DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export NEXT_DIR=$DIR -export FAKE_ISO_PATH=/game/fake_iso.txt cd $DIR/build/ make goalc-test_coverage From d56540f8c01ca83eab0439297d4e7f83f71f4c76 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 12 Sep 2020 13:11:42 -0400 Subject: [PATCH 31/50] Add lambda and static objects (#30) * add some more tests for let * support static strings * add function calling * add prints for windows debgu * one test only * try swapping r14 and r15 in windows * swap back * disable defun for now * fix massive bug * fix formatting --- common/type_system/TypeSpec.h | 8 + game/kernel/asm_funcs.asm | 4 +- game/kernel/kboot.cpp | 6 +- game/overlord/iso_queue.cpp | 4 - game/runtime.cpp | 2 +- game/system/Deci2Server.cpp | 9 +- game/system/SystemThread.cpp | 2 +- goal_src/goal-lib.gc | 33 ++ goal_src/test/test-application-lambda-1.gc | 1 + goal_src/test/test-defun-return-constant.gc | 10 + goal_src/test/test-defun-return-symbol.gc | 8 + goal_src/test/test-function-return-arg.gc | 4 + goal_src/test/test-let-1.gc | 4 + goal_src/test/test-let-star-1.gc | 13 + goal_src/test/test-nested-function-call.gc | 8 + goal_src/test/test-simple-function-call.gc | 2 + goal_src/test/test-string-constant-1.gc | 5 + goal_src/test/test-string-constant-2.gc | 2 + goalc/CMakeLists.txt | 3 + goalc/compiler/CodeGenerator.cpp | 22 +- goalc/compiler/CodeGenerator.h | 2 +- goalc/compiler/Compiler.cpp | 16 +- goalc/compiler/Compiler.h | 30 +- goalc/compiler/CompilerSettings.cpp | 21 ++ goalc/compiler/CompilerSettings.h | 27 ++ goalc/compiler/Env.cpp | 43 ++- goalc/compiler/Env.h | 35 +- goalc/compiler/IR.cpp | 111 ++++++ goalc/compiler/IR.h | 44 +++ goalc/compiler/Lambda.h | 13 +- goalc/compiler/StaticObject.cpp | 51 +++ goalc/compiler/StaticObject.h | 36 ++ goalc/compiler/Util.cpp | 51 +++ goalc/compiler/Val.cpp | 13 + goalc/compiler/Val.h | 19 +- goalc/compiler/compilation/Atoms.cpp | 49 ++- .../compiler/compilation/CompilerControl.cpp | 18 +- goalc/compiler/compilation/Define.cpp | 22 ++ goalc/compiler/compilation/Function.cpp | 355 ++++++++++++++++++ goalc/compiler/compilation/Macro.cpp | 3 +- goalc/emitter/IGen.h | 5 +- goalc/emitter/ObjectFileData.cpp | 10 +- goalc/emitter/ObjectGenerator.cpp | 14 +- goalc/emitter/ObjectGenerator.h | 4 + goalc/emitter/Register.cpp | 8 +- goalc/emitter/Register.h | 4 + goalc/goos/Object.cpp | 9 + goalc/goos/Object.h | 3 + goalc/regalloc/Allocator.cpp | 4 +- test/test_compiler_and_runtime.cpp | 62 ++- 50 files changed, 1139 insertions(+), 93 deletions(-) create mode 100644 goal_src/test/test-application-lambda-1.gc create mode 100644 goal_src/test/test-defun-return-constant.gc create mode 100644 goal_src/test/test-defun-return-symbol.gc create mode 100644 goal_src/test/test-function-return-arg.gc create mode 100644 goal_src/test/test-let-1.gc create mode 100644 goal_src/test/test-let-star-1.gc create mode 100644 goal_src/test/test-nested-function-call.gc create mode 100644 goal_src/test/test-simple-function-call.gc create mode 100644 goal_src/test/test-string-constant-1.gc create mode 100644 goal_src/test/test-string-constant-2.gc create mode 100644 goalc/compiler/CompilerSettings.cpp create mode 100644 goalc/compiler/CompilerSettings.h create mode 100644 goalc/compiler/StaticObject.cpp create mode 100644 goalc/compiler/StaticObject.h create mode 100644 goalc/compiler/compilation/Function.cpp diff --git a/common/type_system/TypeSpec.h b/common/type_system/TypeSpec.h index 04a0431b08..acb2bb9a2d 100644 --- a/common/type_system/TypeSpec.h +++ b/common/type_system/TypeSpec.h @@ -43,6 +43,14 @@ class TypeSpec { TypeSpec substitute_for_method_call(const std::string& method_type) const; + size_t arg_count() const { return m_arguments.size(); } + + const TypeSpec& get_arg(int idx) const { return m_arguments.at(idx); } + const TypeSpec& last_arg() const { + assert(!m_arguments.empty()); + return m_arguments.back(); + } + private: friend class TypeSystem; std::string m_type; diff --git a/game/kernel/asm_funcs.asm b/game/kernel/asm_funcs.asm index c43173753a..753725c7ac 100644 --- a/game/kernel/asm_funcs.asm +++ b/game/kernel/asm_funcs.asm @@ -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 r14, [rsp + 144] ;; symbol table - mov r15, [rsp + 152] ;; offset + mov r15, [rsp + 152] ;; symbol table + mov r14, [rsp + 144] ;; offset call r13 diff --git a/game/kernel/kboot.cpp b/game/kernel/kboot.cpp index 1b54215ada..03979e6f8a 100644 --- a/game/kernel/kboot.cpp +++ b/game/kernel/kboot.cpp @@ -143,13 +143,15 @@ void KernelCheckAndDispatch() { call_goal(Ptr(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem); } else { if (ListenerFunction->value != s7.offset) { + fprintf(stderr, "Running Listener Function:\n"); auto cptr = Ptr(ListenerFunction->value).c(); for (int i = 0; i < 40; i++) { - printf("%x ", cptr[i]); + fprintf(stderr, "%x ", cptr[i]); } - printf("\n"); + fprintf(stderr, "\n"); auto result = call_goal(Ptr(ListenerFunction->value), 0, 0, 0, s7.offset, g_ee_main_mem); + fprintf(stderr, "result of listener function: %ld\n", result); #ifdef __linux__ cprintf("%ld\n", result); #else diff --git a/game/overlord/iso_queue.cpp b/game/overlord/iso_queue.cpp index 9804776522..3f327bd111 100644 --- a/game/overlord/iso_queue.cpp +++ b/game/overlord/iso_queue.cpp @@ -103,7 +103,6 @@ void InitBuffers() { IsoBufferHeader* AllocateBuffer(uint32_t size) { IsoBufferHeader* buffer = TryAllocateBuffer(size); if (buffer) { - printf("--------------- allocated buffer size %d\n", size); return buffer; } else { while (true) { @@ -148,7 +147,6 @@ IsoBufferHeader* TryAllocateBuffer(uint32_t size) { */ void FreeBuffer(IsoBufferHeader* buffer) { IsoBufferHeader* b = (IsoBufferHeader*)buffer; - printf("--------------- free buffer size %d\n", b->buffer_size); if (b->buffer_size == BUFFER_PAGE_SIZE) { b->next = sFreeBuffer; sFreeBuffer = (IsoBuffer*)b; @@ -287,7 +285,6 @@ void ProcessMessageData() { // if we're done with the buffer, free it and load the next one (if there is one) if (callback_buffer->data_size == 0) { popped_command->callback_buffer = (IsoBufferHeader*)callback_buffer->next; - printf("free 1\n"); FreeBuffer(callback_buffer); } } @@ -324,7 +321,6 @@ void ReleaseMessage(IsoMessage* cmd) { while (cmd->callback_buffer) { auto old_head = cmd->callback_buffer; cmd->callback_buffer = (IsoBufferHeader*)old_head->next; - printf("free 2\n"); FreeBuffer(old_head); } diff --git a/game/runtime.cpp b/game/runtime.cpp index 160ca28cbc..b53d1b19bb 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -163,7 +163,7 @@ void ee_runner(SystemThreadInterface& iface) { */ void iop_runner(SystemThreadInterface& iface) { IOP iop; - printf("\n\n\n[IOP] Restart!\n"); + printf("[IOP] Restart!\n"); iop.reset_allocator(); ee::LIBRARY_sceSif_register(&iop); iop::LIBRARY_register(&iop); diff --git a/game/system/Deci2Server.cpp b/game/system/Deci2Server.cpp index ca7f1b0f63..ca52c173e6 100644 --- a/game/system/Deci2Server.cpp +++ b/game/system/Deci2Server.cpp @@ -64,20 +64,17 @@ bool Deci2Server::init() { if (set_socket_option(server_socket, SOL_SOCKET, server_socket_opt, &opt, sizeof(opt)) < 0) { close_server_socket(); return false; - } - printf("[Deci2Server] Created Socket Options\n"); + }; if (set_socket_option(server_socket, TCP_SOCKET_LEVEL, TCP_NODELAY, &opt, sizeof(opt)) < 0) { close_server_socket(); return false; } - printf("[Deci2Server] Created TCP Socket Options\n"); if (set_socket_timeout(server_socket, 100000) < 0) { close_server_socket(); return false; } - printf("[Deci2Server] Created Socket Timeout\n"); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; @@ -197,8 +194,8 @@ void Deci2Server::run() { } auto* hdr = (Deci2Header*)(buffer); - printf("[DECI2] Got message:\n"); - printf(" %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto, hdr->src, hdr->dst); + fprintf(stderr, "[DECI2] Got message:\n"); + fprintf(stderr, " %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto, hdr->src, hdr->dst); hdr->rsvd = got; diff --git a/game/system/SystemThread.cpp b/game/system/SystemThread.cpp index 2650245bcb..408d963f2a 100644 --- a/game/system/SystemThread.cpp +++ b/game/system/SystemThread.cpp @@ -118,7 +118,7 @@ void SystemThreadInterface::initialization_complete() { std::unique_lock mlk(thread.initialization_mutex); thread.initialization_complete = true; thread.initialization_cv.notify_all(); - printf(" OK\n"); + printf("# %s initialized\n", thread.name.c_str()); } /*! diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 09f71dab94..da2e5bf547 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -50,4 +50,37 @@ ;; flush buffers (:status) ) + ) + + +;;;;;;;;;;;;;;;;;;; +;; GOAL Syntax +;;;;;;;;;;;;;;;;;;; +;; Bind vars in body +(defmacro let (bindings &rest body) + `((lambda :inline-only #t ,(apply first bindings) ,@body) + ,@(apply second bindings))) + +;; Let, but recursive, allowing you to define variables in terms of others. +(defmacro let* (bindings &rest body) + (if (null? bindings) + `(begin ,@body) + `((lambda :inline-only #t (,(caar bindings)) + (let* ,(cdr bindings) ,@body)) + ,(car (cdar bindings)) + ) + ) + ) + +;; Define a new function +(defmacro defun (name bindings &rest body) + (if (and + (> (length body) 1) ;; more than one thing in function + (string? (first body)) ;; first thing is a string + ) + ;; then it's a docstring and we ignore it. + `(define ,name (lambda :name ,name ,bindings ,@(cdr body))) + ;; otherwise don't ignore it. + `(define ,name (lambda :name ,name ,bindings ,@body)) + ) ) \ No newline at end of file diff --git a/goal_src/test/test-application-lambda-1.gc b/goal_src/test/test-application-lambda-1.gc new file mode 100644 index 0000000000..b52a9418c9 --- /dev/null +++ b/goal_src/test/test-application-lambda-1.gc @@ -0,0 +1 @@ +((lambda :inline-only #t (x y z) y) 1 2 3) \ No newline at end of file diff --git a/goal_src/test/test-defun-return-constant.gc b/goal_src/test/test-defun-return-constant.gc new file mode 100644 index 0000000000..c23ae10ec6 --- /dev/null +++ b/goal_src/test/test-defun-return-constant.gc @@ -0,0 +1,10 @@ +(defun return-13 () + 13) + +(defun return-12 () + 12) + +(defun return-11 () + 11) + +(return-12) \ No newline at end of file diff --git a/goal_src/test/test-defun-return-symbol.gc b/goal_src/test/test-defun-return-symbol.gc new file mode 100644 index 0000000000..727b5e0d7e --- /dev/null +++ b/goal_src/test/test-defun-return-symbol.gc @@ -0,0 +1,8 @@ +(define my-number 36) + +(defun return-my-number () + my-number) + +(define my-number 42) + +(return-my-number) \ No newline at end of file diff --git a/goal_src/test/test-function-return-arg.gc b/goal_src/test/test-function-return-arg.gc new file mode 100644 index 0000000000..dba9c6aeff --- /dev/null +++ b/goal_src/test/test-function-return-arg.gc @@ -0,0 +1,4 @@ +(defun return-second-arg (one two three) + two) + +(return-second-arg 1 23 4) \ No newline at end of file diff --git a/goal_src/test/test-let-1.gc b/goal_src/test/test-let-1.gc new file mode 100644 index 0000000000..edeb37cec0 --- /dev/null +++ b/goal_src/test/test-let-1.gc @@ -0,0 +1,4 @@ + (let ((x 1) + (y (test-function 1 2 3 4)) + (z 3)) + y) \ No newline at end of file diff --git a/goal_src/test/test-let-star-1.gc b/goal_src/test/test-let-star-1.gc new file mode 100644 index 0000000000..9623ac0b68 --- /dev/null +++ b/goal_src/test/test-let-star-1.gc @@ -0,0 +1,13 @@ +(define *test-result* + (let* ((x 1) + (y 2) + (z 3) + (a 4) + (b (test-function x y z a)) + (d 5) + ) + b + ) + ) + +*test-result* \ No newline at end of file diff --git a/goal_src/test/test-nested-function-call.gc b/goal_src/test/test-nested-function-call.gc new file mode 100644 index 0000000000..13e5749515 --- /dev/null +++ b/goal_src/test/test-nested-function-call.gc @@ -0,0 +1,8 @@ +(defun r2 (one two three) + two) + +(defun r1 (one two three) + one) + + +(r2 1 (r1 2 3 4) 5) \ No newline at end of file diff --git a/goal_src/test/test-simple-function-call.gc b/goal_src/test/test-simple-function-call.gc new file mode 100644 index 0000000000..f98a80b7cc --- /dev/null +++ b/goal_src/test/test-simple-function-call.gc @@ -0,0 +1,2 @@ +(define-extern test-function (function int int int int int)) +(test-function 1 2 3 4) \ No newline at end of file diff --git a/goal_src/test/test-string-constant-1.gc b/goal_src/test/test-string-constant-1.gc new file mode 100644 index 0000000000..3f3443b559 --- /dev/null +++ b/goal_src/test/test-string-constant-1.gc @@ -0,0 +1,5 @@ + +(define-extern inspect (function object object)) +(inspect "this is a string") +(define x "this is also a string") +(inspect x) \ No newline at end of file diff --git a/goal_src/test/test-string-constant-2.gc b/goal_src/test/test-string-constant-2.gc new file mode 100644 index 0000000000..8a9164bfea --- /dev/null +++ b/goal_src/test/test-string-constant-2.gc @@ -0,0 +1,2 @@ +(define-extern print (function object object)) +(print "test string!") \ No newline at end of file diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 5226e04a7f..50a6ae967f 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -11,12 +11,15 @@ add_library(compiler compiler/Env.cpp compiler/Val.cpp compiler/IR.cpp + compiler/CompilerSettings.cpp compiler/CodeGenerator.cpp + compiler/StaticObject.cpp compiler/compilation/Atoms.cpp compiler/compilation/CompilerControl.cpp compiler/compilation/Block.cpp compiler/compilation/Macro.cpp compiler/compilation/Define.cpp + compiler/compilation/Function.cpp compiler/Util.cpp logger/Logger.cpp regalloc/IRegister.cpp diff --git a/goalc/compiler/CodeGenerator.cpp b/goalc/compiler/CodeGenerator.cpp index f196de9d40..8864d52939 100644 --- a/goalc/compiler/CodeGenerator.cpp +++ b/goalc/compiler/CodeGenerator.cpp @@ -9,17 +9,29 @@ 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()); + m_gen.add_function_to_seg(f->segment); } + // todo, static objects + for (auto& static_obj : m_fe->statics()) { + static_obj->generate(&m_gen); + } + + for (size_t i = 0; i < m_fe->functions().size(); i++) { + do_function(m_fe->functions().at(i).get(), i); + } + // 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 +void CodeGenerator::do_function(FunctionEnv* env, int f_idx) { + auto f_rec = m_gen.get_existing_function_record(f_idx); + // 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(); diff --git a/goalc/compiler/CodeGenerator.h b/goalc/compiler/CodeGenerator.h index cbd1f4ee25..a680447453 100644 --- a/goalc/compiler/CodeGenerator.h +++ b/goalc/compiler/CodeGenerator.h @@ -12,7 +12,7 @@ class CodeGenerator { std::vector run(); private: - void do_function(FunctionEnv* env); + void do_function(FunctionEnv* env, int f_idx); emitter::ObjectGenerator m_gen; FileEnv* m_fe; }; diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index f950c4e229..b03bdaa400 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -10,6 +10,7 @@ using namespace goos; Compiler::Compiler() { init_logger(); + init_settings(); m_ts.add_builtin_types(); m_global_env = std::make_unique(); m_none = std::make_unique(m_ts.make_typespec("none")); @@ -33,7 +34,9 @@ void Compiler::execute_repl() { // 2). compile auto obj_file = compile_object_file("repl", code, m_listener.is_connected()); - obj_file->debug_print_tl(); + if (m_settings.debug_print_ir) { + obj_file->debug_print_tl(); + } if (!obj_file->is_empty()) { // 3). color @@ -74,6 +77,8 @@ void Compiler::init_logger() { gLogger.config[MSG_ERR].color = COLOR_RED; } +void Compiler::init_settings() {} + FileEnv* Compiler::compile_object_file(const std::string& name, goos::Object code, bool allow_emit) { @@ -149,10 +154,11 @@ void Compiler::color_object_file(FileEnv* env) { 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; + if (m_settings.debug_print_regalloc) { + input.debug_settings.print_input = true; + input.debug_settings.print_result = true; + input.debug_settings.print_analysis = true; + } f->set_allocations(allocate_registers(input)); } diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 5d63eafb8a..aa00e90137 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -6,6 +6,7 @@ #include "goalc/listener/Listener.h" #include "goalc/goos/Interpreter.h" #include "goalc/compiler/IR.h" +#include "CompilerSettings.h" class Compiler { public: @@ -27,6 +28,7 @@ class Compiler { private: void init_logger(); + void init_settings(); 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, @@ -36,7 +38,10 @@ class Compiler { 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_string(const goos::Object& form, Env* env); + Val* compile_string(const std::string& str, Env* env, int seg = MAIN_SEGMENT); Val* compile_get_symbol_value(const std::string& name, Env* env); + Val* compile_function_or_method_call(const goos::Object& form, 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); @@ -55,6 +60,18 @@ class Compiler { 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); + void typecheck(const goos::Object& form, + const TypeSpec& expected, + const TypeSpec& actual, + const std::string& error_message = ""); + + TypeSpec parse_typespec(const goos::Object& src); + bool is_local_symbol(const goos::Object& obj, Env* env); + emitter::RegKind get_preferred_reg_kind(const TypeSpec& ts); + Val* compile_real_function_call(const goos::Object& form, + RegVal* function, + const std::vector& args, + Env* env); TypeSystem m_ts; std::unique_ptr m_global_env = nullptr; @@ -65,11 +82,7 @@ class Compiler { 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 = ""); + CompilerSettings m_settings; public: // Atoms @@ -89,14 +102,21 @@ class Compiler { 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); + Val* compile_gs(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_set_config(const goos::Object& form, const goos::Object& rest, Env* env); // Define Val* compile_define(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_define_extern(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); Val* compile_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env); + + // Function + Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_inline(const goos::Object& form, const goos::Object& rest, Env* env); }; #endif // JAK_COMPILER_H diff --git a/goalc/compiler/CompilerSettings.cpp b/goalc/compiler/CompilerSettings.cpp new file mode 100644 index 0000000000..81463587f0 --- /dev/null +++ b/goalc/compiler/CompilerSettings.cpp @@ -0,0 +1,21 @@ +#include "CompilerSettings.h" + +CompilerSettings::CompilerSettings() { + m_settings["print-ir"].kind = SettingKind::BOOL; + m_settings["print-ir"].boolp = &debug_print_ir; + + m_settings["print-regalloc"].kind = SettingKind::BOOL; + m_settings["print-regalloc"].boolp = &debug_print_regalloc; +} + +void CompilerSettings::set(const std::string& name, const goos::Object& value) { + auto kv = m_settings.find(name); + if (kv == m_settings.end()) { + throw std::runtime_error("Compiler setting \"" + name + "\" was not recognized"); + } + + kv->second.value = value; + if (kv->second.boolp) { + *kv->second.boolp = !(value.is_symbol() && value.as_symbol()->name == "#f"); + } +} \ No newline at end of file diff --git a/goalc/compiler/CompilerSettings.h b/goalc/compiler/CompilerSettings.h new file mode 100644 index 0000000000..fa6d9b56b4 --- /dev/null +++ b/goalc/compiler/CompilerSettings.h @@ -0,0 +1,27 @@ +#ifndef JAK_COMPILERSETTINGS_H +#define JAK_COMPILERSETTINGS_H + +#include +#include +#include "goalc/goos/Object.h" + +class CompilerSettings { + public: + CompilerSettings(); + bool debug_print_ir = false; + bool debug_print_regalloc = false; + void set(const std::string& name, const goos::Object& value); + + private: + enum class SettingKind { BOOL, INVALID }; + + struct SettingsEntry { + SettingKind kind = SettingKind::INVALID; + goos::Object value; + bool* boolp = nullptr; + }; + + std::unordered_map m_settings; +}; + +#endif // JAK_COMPILERSETTINGS_H diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index 0114dca609..07ebf17c30 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -156,9 +156,15 @@ std::string FileEnv::print() { } void FileEnv::add_function(std::unique_ptr fe) { + assert(fe->idx_in_file == -1); + fe->idx_in_file = m_functions.size(); m_functions.push_back(std::move(fe)); } +void FileEnv::add_static(std::unique_ptr s) { + m_statics.push_back(std::move(s)); +} + void FileEnv::add_top_level_function(std::unique_ptr fe) { // todo, set FE as top level segment m_functions.push_back(std::move(fe)); @@ -220,6 +226,7 @@ RegVal* FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) { ireg.id = m_iregs.size(); auto rv = std::make_unique(ireg, ts); m_iregs.push_back(std::move(rv)); + assert(kind != emitter::RegKind::INVALID); return m_iregs.back().get(); } @@ -229,4 +236,38 @@ std::unordered_map& FunctionEnv::get_label_map() { std::unordered_map& LabelEnv::get_label_map() { return m_labels; -} \ No newline at end of file +} + +Val* FunctionEnv::lexical_lookup(goos::Object sym) { + if (!sym.is_symbol()) { + throw std::runtime_error("invalid symbol in lexical_lookup"); + } + + auto kv = params.find(sym.as_symbol()->name); + if (kv == params.end()) { + return parent()->lexical_lookup(sym); + } + + return kv->second; +} + +/////////////////// +// LexicalEnv +/////////////////// + +std::string LexicalEnv::print() { + return "lexical"; +} + +Val* LexicalEnv::lexical_lookup(goos::Object sym) { + if (!sym.is_symbol()) { + throw std::runtime_error("invalid symbol in lexical_lookup"); + } + + auto kv = vars.find(sym.as_symbol()->name); + if (kv == vars.end()) { + return parent()->lexical_lookup(sym); + } + + return kv->second; +} diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index 9837ac15f6..026e6a879e 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -13,7 +13,7 @@ #include "common/type_system/TypeSpec.h" #include "goalc/regalloc/allocate.h" #include "goalc/goos/Object.h" -//#include "IR.h" +#include "StaticObject.h" #include "Label.h" #include "Val.h" @@ -30,7 +30,7 @@ class Env { 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 void constrain_reg(IRegConstraint constraint); // todo, remove! virtual Val* lexical_lookup(goos::Object sym); virtual BlockEnv* find_block(const std::string& name); virtual std::unordered_map& get_label_map(); @@ -85,9 +85,11 @@ class FileEnv : public Env { std::string print() override; void add_function(std::unique_ptr fe); void add_top_level_function(std::unique_ptr fe); + void add_static(std::unique_ptr s); NoEmitEnv* add_no_emit_env(); void debug_print_tl(); const std::vector>& functions() { return m_functions; } + const std::vector>& statics() { return m_statics; } bool is_empty(); ~FileEnv() = default; @@ -95,6 +97,7 @@ class FileEnv : public Env { protected: std::string m_name; std::vector> m_functions; + std::vector> m_statics; std::unique_ptr m_no_emit_env = nullptr; // statics @@ -137,11 +140,16 @@ class FunctionEnv : public DeclareEnv { const std::vector>& code() { return m_code; } int max_vars() const { return m_iregs.size(); } const std::vector& constraints() { return m_constraints; } + void constrain(const IRegConstraint& c) { m_constraints.push_back(c); } void set_allocations(const AllocationResult& result) { m_regalloc_result = result; } + Val* lexical_lookup(goos::Object sym) override; const AllocationResult& alloc_result() { return m_regalloc_result; } bool needs_aligned_stack() const { return m_aligned_stack_required; } + void require_aligned_stack() { m_aligned_stack_required = true; } + + int idx_in_file = -1; template T* alloc_val(Args&&... args) { @@ -161,6 +169,7 @@ class FunctionEnv : public DeclareEnv { std::string method_of_type_name = "#f"; std::vector unresolved_gotos; + std::unordered_map params; protected: void resolve_gotos(); @@ -176,7 +185,6 @@ class FunctionEnv : public DeclareEnv { bool m_aligned_stack_required = false; - std::unordered_map m_params; std::unordered_map m_labels; }; @@ -192,23 +200,36 @@ class BlockEnv : public Env { std::vector return_types; }; -class LexicalEnv : public Env { +class LexicalEnv : public DeclareEnv { public: - LexicalEnv(Env* parent); + explicit LexicalEnv(Env* parent) : DeclareEnv(parent) {} + Val* lexical_lookup(goos::Object sym) override; std::string print() override; + std::unordered_map vars; }; class LabelEnv : public Env { public: + explicit LabelEnv(Env* parent) : Env(parent) {} + std::string print() override { return "labelenv"; } std::unordered_map& get_label_map() override; protected: std::unordered_map m_labels; }; -class WithInlineEnv : public Env {}; +class WithInlineEnv : public Env { + public: + WithInlineEnv(Env* parent, bool _inline_preference) + : Env(parent), inline_preference(_inline_preference) {} + bool inline_preference = false; +}; -class SymbolMacroEnv : public Env {}; +class SymbolMacroEnv : public Env { + public: + explicit SymbolMacroEnv(Env* parent) : Env(parent) {} + std::unordered_map, goos::Object> macros; +}; template T* get_parent_env_of_type(Env* in) { diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 236662ba1e..8d046af51b 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -239,4 +239,115 @@ void IR_GotoLabel::resolve(const Label* dest) { assert(!m_resolved); m_dest = dest; m_resolved = true; +} + +///////////////////// +// FunctionCall +///////////////////// + +IR_FunctionCall::IR_FunctionCall(const RegVal* func, const RegVal* ret, std::vector args) + : m_func(func), m_ret(ret), m_args(std::move(args)) {} + +std::string IR_FunctionCall::print() { + std::string result = fmt::format("call {} (ret {}) (args ", m_func->print(), m_ret->print()); + for (const auto& x : m_args) { + result += fmt::format("{} ", x->print()); + } + result.pop_back(); + return result; +} + +RegAllocInstr IR_FunctionCall::to_rai() { + RegAllocInstr rai; + rai.read.push_back(m_func->ireg()); + rai.write.push_back(m_ret->ireg()); + for (auto& arg : m_args) { + rai.read.push_back(arg->ireg()); + } + + for (int i = 0; i < emitter::RegisterInfo::N_REGS; i++) { + auto info = emitter::gRegInfo.get_info(i); + if (info.temp()) { + rai.clobber.emplace_back(i); + } + } + + // todo, clobber call reg? + + return rai; +} + +void IR_FunctionCall::add_constraints(std::vector* constraints, int my_id) { + for (size_t i = 0; i < m_args.size(); i++) { + IRegConstraint c; + c.ireg = m_args.at(i)->ireg(); + c.instr_idx = my_id; + c.desired_register = emitter::gRegInfo.get_arg_reg(i); + constraints->push_back(c); + } + + IRegConstraint c; + c.ireg = m_ret->ireg(); + c.desired_register = emitter::gRegInfo.get_ret_reg(); + c.instr_idx = my_id; + constraints->push_back(c); +} + +void IR_FunctionCall::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto freg = get_reg(m_func, allocs, irec); + gen->add_instr(IGen::add_gpr64_gpr64(freg, emitter::gRegInfo.get_offset_reg()), irec); + gen->add_instr(IGen::call_r64(freg), irec); +} + +///////////////////// +// StaticVarAddr +///////////////////// + +IR_StaticVarAddr::IR_StaticVarAddr(const RegVal* dest, const StaticObject* src) + : m_dest(dest), m_src(src) {} + +std::string IR_StaticVarAddr::print() { + return fmt::format("mov-sva {}, {}", m_dest->print(), m_src->print()); +} + +RegAllocInstr IR_StaticVarAddr::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + return rai; +} + +void IR_StaticVarAddr::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto dr = get_reg(m_dest, allocs, irec); + auto instr = gen->add_instr(IGen::static_addr(dr, 0), irec); + gen->link_instruction_static(instr, m_src->rec, m_src->get_addr_offset()); + gen->add_instr(IGen::sub_gpr64_gpr64(dr, emitter::gRegInfo.get_offset_reg()), irec); +} + +///////////////////// +// FunctionAddr +/////////////////// + +IR_FunctionAddr::IR_FunctionAddr(const RegVal* dest, FunctionEnv* src) : m_dest(dest), m_src(src) {} + +std::string IR_FunctionAddr::print() { + return fmt::format("mov-fa {}, {}", m_dest->print(), m_src->print()); +} + +RegAllocInstr IR_FunctionAddr::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + return rai; +} + +void IR_FunctionAddr::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto dr = get_reg(m_dest, allocs, irec); + auto instr = gen->add_instr(IGen::static_addr(dr, 0), irec); + gen->link_instruction_to_function(instr, gen->get_existing_function_record(m_src->idx_in_file)); + gen->add_instr(IGen::sub_gpr64_gpr64(dr, emitter::gRegInfo.get_offset_reg()), irec); } \ No newline at end of file diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 65e3747426..d7cfa70f85 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -129,4 +129,48 @@ class IR_GotoLabel : public IR { bool m_resolved = false; }; +class IR_FunctionCall : public IR { + public: + IR_FunctionCall(const RegVal* func, const RegVal* ret, std::vector args); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + void add_constraints(std::vector* constraints, int my_id) override; + + protected: + const RegVal* m_func = nullptr; + const RegVal* m_ret = nullptr; + std::vector m_args; +}; + +class IR_StaticVarAddr : public IR { + public: + IR_StaticVarAddr(const RegVal* dest, const StaticObject* 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 StaticObject* m_src = nullptr; +}; + +class IR_FunctionAddr : public IR { + public: + IR_FunctionAddr(const RegVal* dest, FunctionEnv* 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; + FunctionEnv* m_src = nullptr; +}; + #endif // JAK_IR_H diff --git a/goalc/compiler/Lambda.h b/goalc/compiler/Lambda.h index 21a92ba820..6fa1fc4e47 100644 --- a/goalc/compiler/Lambda.h +++ b/goalc/compiler/Lambda.h @@ -1,10 +1,19 @@ - - #ifndef JAK_LAMBDA_H #define JAK_LAMBDA_H +#include "goalc/goos/Object.h" + +// note - we cannot easily reuse the GOOS argument system because GOAL's is slightly different. +// there's no rest or keyword support. +struct GoalArg { + std::string name; + TypeSpec type; +}; + struct Lambda { std::string debug_name; + std::vector params; + goos::Object body; }; #endif // JAK_LAMBDA_H diff --git a/goalc/compiler/StaticObject.cpp b/goalc/compiler/StaticObject.cpp new file mode 100644 index 0000000000..19dc920c6b --- /dev/null +++ b/goalc/compiler/StaticObject.cpp @@ -0,0 +1,51 @@ +#include "third-party/fmt/core.h" +#include "StaticObject.h" +#include "common/goal_constants.h" + +namespace { +template +uint32_t push_data_to_byte_vector(T data, std::vector& v) { + auto* ptr = (uint8_t*)(&data); + for (std::size_t i = 0; i < sizeof(T); i++) { + v.push_back(ptr[i]); + } + return sizeof(T); +} +} // namespace + +StaticString::StaticString(std::string data, int _seg) : text(std::move(data)), seg(_seg) {} + +std::string StaticString::print() const { + return fmt::format("static-string \"{}\"", text); +} + +StaticObject::LoadInfo StaticString::get_load_info() const { + LoadInfo info; + info.requires_load = false; + info.prefer_xmm = false; + return info; +} + +void StaticString::generate(emitter::ObjectGenerator* gen) { + rec = gen->add_static_to_seg(seg, 16); + auto& d = gen->get_static_data(rec); + + // add "string" type tag: + gen->link_static_type_ptr(rec, d.size(), "string"); + for (int i = 0; i < POINTER_SIZE; i++) { + d.push_back(0xbe); + } + + // add allocated size + push_data_to_byte_vector(text.size() + 1, d); + + // add chars + for (auto c : text) { + d.push_back(c); + } + d.push_back(0); +} + +int StaticString::get_addr_offset() const { + return BASIC_OFFSET; +} \ No newline at end of file diff --git a/goalc/compiler/StaticObject.h b/goalc/compiler/StaticObject.h new file mode 100644 index 0000000000..34dc05b283 --- /dev/null +++ b/goalc/compiler/StaticObject.h @@ -0,0 +1,36 @@ +#ifndef JAK_STATICOBJECT_H +#define JAK_STATICOBJECT_H + +#include +#include "goalc/emitter/ObjectGenerator.h" + +class StaticObject { + public: + virtual std::string print() const = 0; + + struct LoadInfo { + bool requires_load = false; + int load_size = -1; + bool load_signed = false; + bool prefer_xmm = false; + }; + + virtual LoadInfo get_load_info() const = 0; + virtual void generate(emitter::ObjectGenerator* gen) = 0; + virtual int get_addr_offset() const = 0; + + emitter::StaticRecord rec; +}; + +class StaticString : public StaticObject { + public: + explicit StaticString(std::string data, int _seg); + std::string text; + int seg = -1; + std::string print() const override; + LoadInfo get_load_info() const override; + void generate(emitter::ObjectGenerator* gen) override; + int get_addr_offset() const override; +}; + +#endif // JAK_STATICOBJECT_H diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index 4795626bca..a4800126fc 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -116,4 +116,55 @@ void Compiler::expect_empty_list(const goos::Object& o) { if (!o.is_empty_list()) { throw_compile_error(o, "expected to be an empty list"); } +} + +TypeSpec Compiler::parse_typespec(const goos::Object& src) { + if (src.is_symbol()) { + return m_ts.make_typespec(symbol_string(src)); + } else if (src.is_pair()) { + TypeSpec ts = m_ts.make_typespec(symbol_string(pair_car(src))); + const auto& rest = pair_cdr(src); + + for_each_in_list(rest, [&](const goos::Object& o) { ts.add_arg(parse_typespec(o)); }); + + return ts; + } else { + throw_compile_error(src, "invalid typespec"); + } + assert(false); + return {}; +} + +bool Compiler::is_local_symbol(const goos::Object& obj, Env* env) { + // check in the symbol macro env. + auto mlet_env = get_parent_env_of_type(env); + while (mlet_env) { + if (mlet_env->macros.find(obj.as_symbol()) != mlet_env->macros.end()) { + return true; + } + mlet_env = get_parent_env_of_type(mlet_env->parent()); + } + + // check lexical + if (env->lexical_lookup(obj)) { + return true; + } + + // check global constants + if (m_global_constants.find(obj.as_symbol()) != m_global_constants.end()) { + return true; + } + + return false; +} + +emitter::RegKind Compiler::get_preferred_reg_kind(const TypeSpec& ts) { + switch (m_ts.lookup_type(ts)->get_preferred_reg_kind()) { + case RegKind::GPR_64: + return emitter::RegKind::GPR; + case RegKind::FLOAT: + return emitter::RegKind::XMM; + default: + assert(false); + } } \ No newline at end of file diff --git a/goalc/compiler/Val.cpp b/goalc/compiler/Val.cpp index 06c3e20302..f1de3f0e15 100644 --- a/goalc/compiler/Val.cpp +++ b/goalc/compiler/Val.cpp @@ -61,4 +61,17 @@ 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; +} + +RegVal* StaticVal::to_reg(Env* fe) { + auto re = fe->make_gpr(m_ts); + fe->emit(std::make_unique(re, obj)); + return re; +} + +RegVal* LambdaVal::to_reg(Env* fe) { + auto re = fe->make_gpr(m_ts); + assert(func); + fe->emit(std::make_unique(re, func)); + return re; } \ No newline at end of file diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index 1313aa6427..21e4015c72 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -13,6 +13,7 @@ #include "common/type_system/TypeSystem.h" #include "goalc/regalloc/IRegister.h" #include "Lambda.h" +#include "StaticObject.h" class RegVal; class Env; @@ -107,15 +108,21 @@ class SymbolValueVal : public Val { */ 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; } + explicit LambdaVal(TypeSpec ts) : Val(std::move(ts)) {} + std::string print() const override { return "lambda-" + lambda.debug_name; } FunctionEnv* func = nullptr; - - protected: - Lambda m_lam; + Lambda lambda; + RegVal* to_reg(Env* fe) override; +}; + +class StaticVal : public Val { + public: + StaticVal(StaticObject* _obj, TypeSpec _ts) : Val(std::move(_ts)), obj(_obj) {} + StaticObject* obj = nullptr; + std::string print() const override { return "[" + obj->print() + "]"; } + RegVal* to_reg(Env* fe) override; }; -// Static // MemOffConstant // MemOffVar // MemDeref diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 9f3be74ce7..60e2303aba 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -1,3 +1,8 @@ +/*! + * @file Atoms.cpp + * Compiler implementation for atoms - things which aren't compound forms. + */ + #include "goalc/compiler/Compiler.h" #include "goalc/compiler/IR.h" @@ -25,7 +30,7 @@ static const std::unordered_map< {"goto", &Compiler::compile_goto}, // // // COMPILER CONTROL - // {"gs", &Compiler::compile_gs}, + {"gs", &Compiler::compile_gs}, {":exit", &Compiler::compile_exit}, {"asm-file", &Compiler::compile_asm_file}, {"listen-to-target", &Compiler::compile_listen_to_target}, @@ -45,7 +50,7 @@ static const std::unordered_map< // // // DEFINITION {"define", &Compiler::compile_define}, - // {"define-extern", &Compiler::compile_define_extern}, + {"define-extern", &Compiler::compile_define_extern}, // {"set!", &Compiler::compile_set}, // {"defun-extern", &Compiler::compile_defun_extern}, // {"declare-method", &Compiler::compile_declare_method}, @@ -62,7 +67,7 @@ static const std::unordered_map< // // // // LAMBDA - // {"lambda", &Compiler::compile_lambda}, + {"lambda", &Compiler::compile_lambda}, // {"inline", &Compiler::compile_inline}, // {"with-inline", &Compiler::compile_with_inline}, // {"rlet", &Compiler::compile_rlet}, @@ -121,20 +126,20 @@ static const std::unordered_map< // {"<", &Compiler::compile_condition_as_bool}, // {">", &Compiler::compile_condition_as_bool}, // - // // BUILDER + // // BUILDER (build-dgo/build-cgo?) // {"builder", &Compiler::compile_builder}, // // // UTIL - // {"set-config!", &Compiler::compile_set_config}, - // - // - // + {"set-config!", &Compiler::compile_set_config}, // // // temporary testing hacks... // {"send-test", &Compiler::compile_send_test_data}, }; +/*! + * Highest level compile function + */ Val* Compiler::compile(const goos::Object& code, Env* env) { switch (code.type) { case goos::ObjectType::PAIR: @@ -143,6 +148,8 @@ Val* Compiler::compile(const goos::Object& code, Env* env) { return compile_integer(code, env); case goos::ObjectType::SYMBOL: return compile_symbol(code, env); + case goos::ObjectType::STRING: + return compile_string(code, env); default: ice("Don't know how to compile " + code.print()); } @@ -171,8 +178,9 @@ Val* Compiler::compile_pair(const goos::Object& code, Env* env) { } // todo function or method call - ice("unhandled compile_pair on " + code.print()); - return nullptr; + return compile_function_or_method_call(code, env); + // throw_compile_error(code, "Unrecognized symbol at head of form"); + // return nullptr; } Val* Compiler::compile_integer(const goos::Object& code, Env* env) { @@ -197,8 +205,11 @@ Val* Compiler::compile_symbol(const goos::Object& form, Env* env) { } // todo mlet - // todo lexical - // todo global constant + + auto lexical = env->lexical_lookup(form); + if (lexical) { + return lexical; + } auto global_constant = m_global_constants.find(form.as_symbol()); auto existing_symbol = m_symbol_types.find(form.as_symbol()->name); @@ -221,6 +232,7 @@ Val* Compiler::compile_symbol(const goos::Object& form, Env* 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()) { + // assert(false); throw std::runtime_error("The symbol " + name + " was not defined"); } @@ -230,4 +242,17 @@ Val* Compiler::compile_get_symbol_value(const std::string& name, Env* env) { auto sym = fe->alloc_val(name, m_ts.make_typespec("symbol")); auto re = fe->alloc_val(sym, ts, sext); return re; +} + +Val* Compiler::compile_string(const goos::Object& form, Env* env) { + return compile_string(form.as_string()->data, env, MAIN_SEGMENT); +} + +Val* Compiler::compile_string(const std::string& str, Env* env, int seg) { + auto obj = std::make_unique(str, seg); + auto fe = get_parent_env_of_type(env); + auto result = fe->alloc_val(obj.get(), m_ts.make_typespec("string")); + auto fie = get_parent_env_of_type(env); + fie->add_static(std::move(obj)); + return result; } \ No newline at end of file diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index be3d944d24..7ece53faba 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -121,7 +121,7 @@ Val* Compiler::compile_listen_to_target(const goos::Object& form, Env* env) { (void)env; std::string ip = "127.0.0.1"; - int port = 8112; + int port = 8112; // todo, get from some constant somewhere bool got_port = false, got_ip = false; for_each_in_list(rest, [&](const goos::Object& o) { @@ -166,4 +166,20 @@ Val* Compiler::compile_poke(const goos::Object& form, const goos::Object& rest, va_check(form, args, {}, {}); m_listener.send_poke(); return get_none(); +} + +Val* Compiler::compile_gs(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + auto args = get_va(form, rest); + va_check(form, args, {}, {}); + m_goos.execute_repl(); + return get_none(); +} + +Val* Compiler::compile_set_config(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + auto args = get_va(form, rest); + va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {}); + m_settings.set(symbol_string(args.unnamed.at(0)), args.unnamed.at(1)); + return get_none(); } \ No newline at end of file diff --git a/goalc/compiler/compilation/Define.cpp b/goalc/compiler/compilation/Define.cpp index 336d711a7b..9de38d2d1f 100644 --- a/goalc/compiler/compilation/Define.cpp +++ b/goalc/compiler/compilation/Define.cpp @@ -1,4 +1,5 @@ #include "goalc/compiler/Compiler.h" +#include "goalc/logger/Logger.h" Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); @@ -39,3 +40,24 @@ Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest fe->emit(std::make_unique(sym_val, in_gpr)); return in_gpr; } + +Val* Compiler::compile_define_extern(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + auto args = get_va(form, rest); + va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {}); + auto& sym = args.unnamed.at(0); + auto& typespec = args.unnamed.at(1); + + auto new_type = parse_typespec(typespec); + + auto existing_type = m_symbol_types.find(symbol_string(sym)); + if (existing_type != m_symbol_types.end() && existing_type->second != new_type) { + gLogger.log( + MSG_WARN, + "[Warning] define-extern has redefined the type of symbol %s\npreviously: %s\nnow: %s\n", + symbol_string(sym).c_str(), existing_type->second.print().c_str(), + new_type.print().c_str()); + } + m_symbol_types[symbol_string(sym)] = new_type; + return get_none(); +} \ No newline at end of file diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp new file mode 100644 index 0000000000..aea6a32228 --- /dev/null +++ b/goalc/compiler/compilation/Function.cpp @@ -0,0 +1,355 @@ +#include "goalc/compiler/Compiler.h" +#include "goalc/logger/Logger.h" + +namespace { +bool get_inline_preference(Env* env) { + auto ile = get_parent_env_of_type(env); + if (ile) { + return ile->inline_preference; + } else { + return false; + } +} + +const goos::Object& get_lambda_body(const goos::Object& def) { + auto* iter = &def; + while (true) { + auto car = iter->as_pair()->car; + if (car.is_symbol() && car.as_symbol()->name.at(0) == ':') { + iter = &iter->as_pair()->cdr; + iter = &iter->as_pair()->cdr; + } else { + assert(car.is_list()); + return iter->as_pair()->cdr; + } + } +} +} // namespace + +Val* Compiler::compile_inline(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + auto args = get_va(form, rest); + va_check(form, args, {goos::ObjectType::SYMBOL}, {}); + + auto kv = m_inlineable_functions.find(args.unnamed.at(0).as_symbol()); + if (kv == m_inlineable_functions.end()) { + throw_compile_error(form, "Couldn't find function to inline!"); + } + + if (kv->second->func && !kv->second->func->settings.allow_inline) { + throw_compile_error(form, "Found function to inline, but it isn't allowed."); + } + + // todo, this should return a "view" of the lambda which indicates its inlined + // so the correct label namespace behavior can be used. + return kv->second; +} + +Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env) { + auto fe = get_parent_env_of_type(env); + auto args = get_va(form, rest); + if (args.unnamed.empty() || !args.unnamed.front().is_list() || + !args.only_contains_named({"name", "inline-only"})) { + throw_compile_error(form, "Invalid lambda form"); + } + + auto place = fe->alloc_val(get_none()->type()); + auto& lambda = place->lambda; + auto lambda_ts = m_ts.make_typespec("function"); + + // parse the argument list. + for_each_in_list(args.unnamed.front(), [&](const goos::Object& o) { + if (o.is_symbol()) { + // if it has no type, assume object. + lambda.params.push_back({symbol_string(o), m_ts.make_typespec("object")}); + lambda_ts.add_arg(m_ts.make_typespec("object")); + } else { + auto param_args = get_va(o, o); + va_check(o, param_args, {goos::ObjectType::SYMBOL, goos::ObjectType::SYMBOL}, {}); + + GoalArg parm; + parm.name = symbol_string(param_args.unnamed.at(0)); + parm.type = parse_typespec(param_args.unnamed.at(1)); + + lambda.params.push_back(parm); + lambda_ts.add_arg(parm.type); + } + }); + assert(lambda.params.size() == lambda_ts.arg_count()); + + // optional name for debugging + if (args.has_named("name")) { + // todo, this probably prints a nasty error if name isn't a string. + lambda.debug_name = symbol_string(args.get_named("name")); + } + + lambda.body = get_lambda_body(rest); // first is the argument list, rest is body + place->func = nullptr; + + bool inline_only = + args.has_named("inline-only") && symbol_string(args.get_named("inline-only")) != "#f"; + + if (!inline_only) { + // compile a function! First create env + // auto new_func_env = fe->alloc_env(env, lambda.debug_name); + auto new_func_env = std::make_unique(env, lambda.debug_name); + new_func_env->set_segment(MAIN_SEGMENT); + + // set up arguments + assert(lambda.params.size() < 8); // todo graceful error + for (u32 i = 0; i < lambda.params.size(); i++) { + IRegConstraint constr; + constr.instr_idx = 0; // constraint at function start + auto ireg = new_func_env->make_ireg(lambda.params.at(i).type, emitter::RegKind::GPR); + constr.ireg = ireg->ireg(); + constr.desired_register = emitter::gRegInfo.get_arg_reg(i); + new_func_env->params[lambda.params.at(i).name] = ireg; + new_func_env->constrain(constr); + } + + place->func = new_func_env.get(); + + // nasty function block env setup + auto return_reg = new_func_env->make_ireg(get_none()->type(), emitter::RegKind::GPR); + auto func_block_env = new_func_env->alloc_env(new_func_env.get(), "#f"); + func_block_env->return_value = return_reg; + func_block_env->end_label = Label(new_func_env.get()); + + // compile the function! + Val* result = nullptr; + bool first_thing = true; + for_each_in_list(lambda.body, [&](const goos::Object& o) { + result = compile_error_guard(o, func_block_env); + if (first_thing) { + first_thing = false; + // you could probably cheat and do a (begin (blorp) (declare ...)) to get around this. + new_func_env->settings.is_set = true; + } + }); + if (result) { + auto final_result = result->to_gpr(new_func_env.get()); + new_func_env->emit(std::make_unique(return_reg, final_result)); + // new_func_env->emit(std::make_unique())??? + new_func_env->finish(); + lambda_ts.add_arg(final_result->type()); + } else { + lambda_ts.add_arg(m_ts.make_typespec("none")); + } + func_block_env->end_label.idx = new_func_env->code().size(); + + auto obj_env = get_parent_env_of_type(new_func_env.get()); + assert(obj_env); + if (new_func_env->settings.save_code) { + obj_env->add_function(std::move(new_func_env)); + } + } + + place->set_type(lambda_ts); + return place; +} + +Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* env) { + goos::Object f = form; + auto fe = get_parent_env_of_type(env); + + auto args = get_va(form, form); + + auto uneval_head = args.unnamed.at(0); + Val* head = get_none(); + + // determine if this call should be automatically inlined. + // this logic will not trigger for a manually inlined call [using the (inline func) form] + bool auto_inline = false; + if (uneval_head.is_symbol()) { + // we can only auto-inline the function if its name is explicit. + // look it up: + auto kv = m_inlineable_functions.find(uneval_head.as_symbol()); + if (kv != m_inlineable_functions.end()) { + // it's inlinable. However, we do not always inline an inlinable function by default + if (kv->second->func == + nullptr || // only-inline, we must inline it as there is no code generated for it + kv->second->func->settings + .inline_by_default || // inline when possible, so we should inline + (kv->second->func->settings.allow_inline && + get_inline_preference(env))) { // inline is allowed, and we prefer it locally + auto_inline = true; + head = kv->second; + } + } + } + + bool is_method_call = false; + if (!auto_inline) { + // if auto-inlining failed, we must get the thing to call in a different way. + if (uneval_head.is_symbol()) { + if (is_local_symbol(uneval_head, env) || + m_symbol_types.find(symbol_string(uneval_head)) != m_symbol_types.end()) { + // the local environment (mlets, lexicals, constants, globals) defines this symbol. + // this will "win" over a method name lookup, so we should compile as normal + head = compile_error_guard(args.unnamed.front(), env); + } else { + // we don't think compiling the head give us a function, so it's either a method or an error + is_method_call = true; + } + } else { + // the head is some expression. Could be something like (inline my-func) or (-> obj + // func-ptr-field) in either case, compile it - and it can't be a method call. + head = compile_error_guard(args.unnamed.front(), env); + } + } + + if (!is_method_call) { + // typecheck that we got a function + typecheck(form, m_ts.make_typespec("function"), head->type(), "Function call head"); + } + + // compile arguments + std::vector eval_args; + for (uint32_t i = 1; i < args.unnamed.size(); i++) { + auto intermediate = compile_error_guard(args.unnamed.at(i), env); + eval_args.push_back(intermediate->to_reg(env)); + } + + // see if its an "immediate" application. This happens in three cases: + // 1). the user directly puts a (lambda ...) form in the head (like with a (let) macro) + // 2). the user used a (inline my-func) to grab the LambdaPlace of the function. + // 3). the auto-inlining above looked up the LambdaPlace of an inlinable_function. + + // note that an inlineable function looked up by symbol or other way WILL NOT cast to a + // LambdaPlace! so this cast will only succeed if the auto-inliner succeeded, or the user has + // passed use explicitly a lambda either with the lambda form, or with the (inline ...) form. + LambdaVal* head_as_lambda = nullptr; + if (!is_method_call) { + head_as_lambda = dynamic_cast(head); + } + + if (head_as_lambda) { + // inline the function! + + // check args are ok + if (head_as_lambda->lambda.params.size() != eval_args.size()) { + throw_compile_error(form, "invalid argument count"); + } + + // construct a lexical environment + auto lexical_env = fe->alloc_env(env); + + Env* compile_env = lexical_env; + + // if needed create a label env. + // we don't want a separate label env with lets, but we do in other cases. + if (auto_inline) { + // TODO - this misses the case of (inline func)! + compile_env = fe->alloc_env(lexical_env); + } + + // check arg types + if (!head->type().arg_count()) { + if (head->type().arg_count() - 1 != eval_args.size()) { + throw_compile_error(form, "invalid number of arguments to function call (inline)"); + } + for (uint32_t i = 0; i < eval_args.size(); i++) { + typecheck(form, head->type().get_arg(i), eval_args.at(i)->type(), + "function (inline) argument"); + } + } + + // copy args... + for (uint32_t i = 0; i < eval_args.size(); i++) { + auto type = eval_args.at(i)->type(); + auto copy = env->make_ireg(type, get_preferred_reg_kind(type)); + env->emit(std::make_unique(copy, eval_args.at(i))); + lexical_env->vars[head_as_lambda->lambda.params.at(i).name] = copy; + } + + // compile inline! + bool first_thing = true; + Val* result = get_none(); + for_each_in_list(head_as_lambda->lambda.body, [&](const goos::Object& o) { + result = compile_error_guard(o, compile_env); + if (first_thing) { + first_thing = false; + lexical_env->settings.is_set = true; + } + }); + + // this doesn't require a return type. + return result; + } else { + // not an inline call + if (is_method_call) { + // determine the method to call by looking at the type of first argument + if (eval_args.empty()) { + throw_compile_error(form, "0 argument method call is impossible to figure out"); + } + assert(false); // nyi + // head = compile_get_method_of_object(eval_args.front(), symbol_string(uneval_head), env); + } + + // convert the head to a GPR + auto head_as_gpr = + head->to_gpr(env); // std::dynamic_pointer_cast(resolve_to_gpr(head, env)); + if (head_as_gpr) { + return compile_real_function_call(form, head_as_gpr, eval_args, env); + } else { + throw_compile_error(form, "can't figure out this function call!"); + } + } + + throw_compile_error(form, "call_function_or_method unreachable"); + return get_none(); +} + +Val* Compiler::compile_real_function_call(const goos::Object& form, + RegVal* function, + const std::vector& args, + Env* env) { + auto fe = get_parent_env_of_type(env); + fe->require_aligned_stack(); + TypeSpec return_ts; + if (function->type().arg_count() == 0) { + // if the type system doesn't know what the function will return, just make it object. + // the user is responsible for getting this right. + return_ts = m_ts.make_typespec("object"); + gLogger.log(MSG_WARN, "[Warning] Function call could not determine return type: %s\n", + form.print().c_str()); + // todo, should this be a warning? not a great thing if we don't know what a function will + // return? + } else { + return_ts = function->type().last_arg(); + } + + auto return_reg = env->make_ireg(return_ts, emitter::RegKind::GPR); + + // TODO - VERY IMPORTANT + // CREATE A TEMP COPY OF FUNCTION! WILL BE DESTROYED. + + // nope! not anymore. + // for(auto& arg : args) { + // // note: this has to be done in here, because we might want to const prop across lexical + // envs. arg = resolve_to_gpr(arg, env); + // } + + // check arg count: + if (function->type().arg_count()) { + if (function->type().arg_count() - 1 != args.size()) { + printf("got type %s\n", function->type().print().c_str()); + throw_compile_error(form, "invalid number of arguments to function call: got " + + std::to_string(args.size()) + " and expected " + + std::to_string(function->type().arg_count() - 1)); + } + for (uint32_t i = 0; i < args.size(); i++) { + typecheck(form, function->type().get_arg(i), args.at(i)->type(), "function argument"); + } + } + + // set args (introducing a move here makes coloring more likely to be possible) + std::vector arg_outs; + for (auto& arg : args) { + arg_outs.push_back(env->make_ireg(arg->type(), emitter::RegKind::GPR)); + env->emit(std::make_unique(arg_outs.back(), arg)); + } + + env->emit(std::make_unique(function, return_reg, arg_outs)); + return return_reg; +} \ No newline at end of file diff --git a/goalc/compiler/compilation/Macro.cpp b/goalc/compiler/compilation/Macro.cpp index 89b5afb265..0ad7680503 100644 --- a/goalc/compiler/compilation/Macro.cpp +++ b/goalc/compiler/compilation/Macro.cpp @@ -62,7 +62,7 @@ Val* Compiler::compile_gscond(const goos::Object& form, const goos::Object& rest result = get_none(); for_each_in_list(current_case.as_pair()->cdr, - [&](Object o) { result = compile_error_guard(o, env); }); + [&](const Object& o) { result = compile_error_guard(o, env); }); return result; } else { // no match, continue. @@ -83,6 +83,7 @@ Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest, switch (thing.type) { case goos::ObjectType::SYMBOL: return compile_get_sym_obj(thing.as_symbol()->name, env); + // todo... default: throw_compile_error(form, "Can't quote this"); } diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index 978329adf6..fca3273d1b 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -1225,6 +1225,7 @@ class IGen { * Instruction to pop 64 bit gpr from the stack */ static Instruction pop_gpr64(Register reg) { + assert(reg.is_gpr()); if (reg.hw_id() >= 8) { auto i = Instruction(0x58 + reg.hw_id() - 8); i.set(REX(false, false, false, true)); @@ -1236,7 +1237,9 @@ class IGen { /*! * Call a function stored in a 64-bit gpr */ - static Instruction call_r64(uint8_t reg) { + static Instruction call_r64(Register reg_) { + assert(reg_.is_gpr()); + auto reg = reg_.hw_id(); Instruction instr(0xff); if (reg >= 8) { instr.set(REX(false, false, false, true)); diff --git a/goalc/emitter/ObjectFileData.cpp b/goalc/emitter/ObjectFileData.cpp index cf338f4b8b..013dbb7ce6 100644 --- a/goalc/emitter/ObjectFileData.cpp +++ b/goalc/emitter/ObjectFileData.cpp @@ -14,11 +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"); + // printf("seg %d data\n", seg); + // for (auto x : segment_data[seg]) { + // printf("%02x ", x); + // } + // printf("\n"); } return result; } diff --git a/goalc/emitter/ObjectGenerator.cpp b/goalc/emitter/ObjectGenerator.cpp index 0c14eb1e37..d17fcb1ae8 100644 --- a/goalc/emitter/ObjectGenerator.cpp +++ b/goalc/emitter/ObjectGenerator.cpp @@ -103,17 +103,20 @@ FunctionRecord ObjectGenerator::add_function_to_seg(int seg, int min_align) { rec.func_id = int(m_function_data_by_seg.at(seg).size()); m_function_data_by_seg.at(seg).emplace_back(); m_function_data_by_seg.at(seg).back().min_align = min_align; + m_all_function_records.push_back(rec); return rec; } +FunctionRecord ObjectGenerator::get_existing_function_record(int f_idx) { + return m_all_function_records.at(f_idx); +} + /*! * Add a new IR instruction to the function. An IR instruction may contain 0, 1, or multiple * actual Instructions. These Instructions can be added with add_instruction. The IR_Record * can be used as a label for jump targets. */ IR_Record ObjectGenerator::add_ir(const FunctionRecord& func) { - // verify we aren't adding to an old function. not technically an error, but doesn't make sense - assert(func.func_id == int(m_function_data_by_seg.at(func.seg).size()) - 1); IR_Record rec; rec.seg = func.seg; rec.func_id = func.func_id; @@ -149,8 +152,6 @@ IR_Record ObjectGenerator::get_future_ir_record_in_same_func(const IR_Record& ir * Add a new Instruction for the given IR instruction. */ InstructionRecord ObjectGenerator::add_instr(Instruction inst, IR_Record ir) { - // verify we aren't adding to an old instruction or function - assert(ir.func_id == int(m_function_data_by_seg.at(ir.seg).size()) - 1); // only this second condition is an actual error. assert(ir.ir_id == int(m_function_data_by_seg.at(ir.seg).at(ir.func_id).ir_to_instruction.size()) - 1); @@ -166,7 +167,6 @@ InstructionRecord ObjectGenerator::add_instr(Instruction inst, IR_Record ir) { } 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); } @@ -182,6 +182,10 @@ StaticRecord ObjectGenerator::add_static_to_seg(int seg, int min_align) { return rec; } +std::vector& ObjectGenerator::get_static_data(const StaticRecord& rec) { + return m_static_data_by_seg.at(rec.seg).at(rec.static_id).data; +} + /*! * Add linking data to add a type pointer in rec at offset. * This will add an entry to the linking data, which will get patched at runtime, during linking. diff --git a/goalc/emitter/ObjectGenerator.h b/goalc/emitter/ObjectGenerator.h index 4d3c436c16..c8be6c6fd4 100644 --- a/goalc/emitter/ObjectGenerator.h +++ b/goalc/emitter/ObjectGenerator.h @@ -41,12 +41,14 @@ class ObjectGenerator { FunctionRecord add_function_to_seg(int seg, int min_align = 16); // should align and insert function tag + FunctionRecord get_existing_function_record(int f_idx); 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); + std::vector& get_static_data(const StaticRecord& rec); void link_instruction_jump(InstructionRecord jump_instr, IR_Record destination); void link_static_type_ptr(StaticRecord rec, int offset, const std::string& type_name); @@ -168,6 +170,8 @@ class ObjectGenerator { seg_map m_type_ptr_links_by_seg; seg_map m_sym_links_by_seg; seg_vector m_rip_links_by_seg; + + std::vector m_all_function_records; }; } // namespace emitter diff --git a/goalc/emitter/Register.cpp b/goalc/emitter/Register.cpp index 55ba17e9bd..d1e745aef5 100644 --- a/goalc/emitter/Register.cpp +++ b/goalc/emitter/Register.cpp @@ -4,10 +4,10 @@ namespace emitter { RegisterInfo RegisterInfo::make_register_info() { RegisterInfo info; - info.m_info[RAX] = {-1, false, false, "rax"}; - info.m_info[RCX] = {3, false, false, "rcx"}; - info.m_info[RDX] = {2, false, false, "rdx"}; - info.m_info[RBX] = {-1, true, false, "rbx"}; + info.m_info[RAX] = {-1, false, false, "rax"}; // temp + info.m_info[RCX] = {3, false, false, "rcx"}; // temp + info.m_info[RDX] = {2, false, false, "rdx"}; // temp + info.m_info[RBX] = {-1, true, false, "rbx"}; // info.m_info[RSP] = {-1, false, true, "rsp"}; info.m_info[RBP] = {-1, true, false, "rbp"}; info.m_info[RSI] = {1, false, false, "rsi"}; diff --git a/goalc/emitter/Register.h b/goalc/emitter/Register.h index 7537f7bce5..14a51132c7 100644 --- a/goalc/emitter/Register.h +++ b/goalc/emitter/Register.h @@ -101,6 +101,8 @@ class RegisterInfo { static constexpr int N_REGS = 32; static constexpr int N_SAVED_GPRS = 5; static constexpr int N_SAVED_XMMS = 8; + static constexpr int N_TEMP_GPRS = 5; + static constexpr int N_TEMP_XMMS = 8; static_assert(N_REGS - 1 == XMM15, "bad register count"); @@ -111,6 +113,8 @@ class RegisterInfo { bool saved = false; // does the callee save it? bool special = false; // is it a special GOAL register? std::string name; + + bool temp() const { return !saved && !special; } }; const Info& get_info(Register r) const { return m_info.at(r.id()); } diff --git a/goalc/goos/Object.cpp b/goalc/goos/Object.cpp index 000202538c..99b66d5dca 100644 --- a/goalc/goos/Object.cpp +++ b/goalc/goos/Object.cpp @@ -233,4 +233,13 @@ ArgumentSpec make_varargs() { return as; } +bool Arguments::only_contains_named(const std::unordered_set& names) { + for (auto& kv : named) { + if (names.find(kv.first) == names.end()) { + return false; + } + } + return true; +} + } // namespace goos diff --git a/goalc/goos/Object.h b/goalc/goos/Object.h index 926014bc6e..44fc1c9eef 100644 --- a/goalc/goos/Object.h +++ b/goalc/goos/Object.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -299,6 +300,7 @@ class Object { } bool is_empty_list() const { return type == ObjectType::EMPTY_LIST; } + bool is_list() const { return type == ObjectType::EMPTY_LIST || type == ObjectType::PAIR; } bool is_int() const { return type == ObjectType::INTEGER; } bool is_float() const { return type == ObjectType::FLOAT; } bool is_char() const { return type == ObjectType::CHAR; } @@ -527,6 +529,7 @@ struct Arguments { Object get_named(const std::string& name, const Object& default_value); Object get_named(const std::string& name); bool has_named(const std::string& name); + bool only_contains_named(const std::unordered_set& names); }; class LambdaObject : public HeapObject { diff --git a/goalc/regalloc/Allocator.cpp b/goalc/regalloc/Allocator.cpp index 1b9cfdde64..10e928146f 100644 --- a/goalc/regalloc/Allocator.cpp +++ b/goalc/regalloc/Allocator.cpp @@ -617,8 +617,8 @@ const std::vector& get_default_alloc_order_for_var_spill(int const std::vector& get_default_alloc_order_for_var(int v, RegAllocCache* cache) { auto& info = cache->iregs.at(v); - assert(info.kind != emitter::RegKind::INVALID); - if (info.kind == emitter::RegKind::GPR) { + // assert(info.kind != emitter::RegKind::INVALID); + if (info.kind == emitter::RegKind::GPR || info.kind == emitter::RegKind::INVALID) { return emitter::gRegInfo.get_gpr_alloc_order(); } else if (info.kind == emitter::RegKind::XMM) { return emitter::gRegInfo.get_xmm_alloc_order(); diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp index 172b13bac1..f165e020f6 100644 --- a/test/test_compiler_and_runtime.cpp +++ b/test/test_compiler_and_runtime.cpp @@ -55,36 +55,59 @@ struct CompilerTestRunner { struct Test { std::vector expected, actual; std::string test_name; + bool auto_pass = false; }; std::vector tests; - void run_test(const std::string& test_file, const std::vector& expected) { + void run_test(const std::string& test_file, + const std::vector& expected, + MatchParam truncate = {}) { + fprintf(stderr, "Testing %s\n", test_file.c_str()); auto result = c->run_test("goal_src/test/" + test_file); + if (!truncate.is_wildcard) { + for (auto& x : result) { + x = x.substr(0, truncate.value); + } + } + EXPECT_EQ(result, expected); - tests.push_back({expected, result, test_file}); + tests.push_back({expected, result, test_file, false}); + } + + void run_always_pass(const std::string& test_file) { + c->run_test("goal_src/test/" + test_file); + tests.push_back({{}, {}, test_file, true}); } void print_summary() { fmt::print("~~ Compiler Test Summary for {} tests... ~~\n", tests.size()); int passed = 0; + int passable = 0; + int auto_pass = 0; for (auto& test : tests) { - if (test.expected == test.actual) { - fmt::print("[{:40}] PASS!\n", test.test_name); - passed++; + if (test.auto_pass) { + auto_pass++; + fmt::print("[{:40}] AUTO-PASS!\n", test.test_name); } else { - fmt::print("[{:40}] 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)); + passable++; + if (test.expected == test.actual) { + fmt::print("[{:40}] PASS!\n", test.test_name); + passed++; + } else { + fmt::print("[{:40}] 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()); + fmt::print("Total: passed {}/{} passable tests, {} auto-passed\n", passed, passable, auto_pass); } }; @@ -118,7 +141,18 @@ TEST(CompilerAndRuntime, CompilerTests) { 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"}); + runner.run_test("test-simple-function-call.gc", {"30\n"}); + runner.run_test("test-application-lambda-1.gc", {"2\n"}); + runner.run_test("test-let-1.gc", {"30\n"}); + runner.run_test("test-let-star-1.gc", {"30\n"}); + runner.run_always_pass("test-string-constant-1.gc"); + std::string expected = "\"test string!\""; + runner.run_test("test-string-constant-2.gc", {expected}, expected.size()); + runner.run_test("test-defun-return-constant.gc", {"12\n"}); + runner.run_test("test-defun-return-symbol.gc", {"42\n"}); + runner.run_test("test-function-return-arg.gc", {"23\n"}); + runner.run_test("test-nested-function-call.gc", {"2\n"}); compiler.shutdown_target(); runtime_thread.join(); runner.print_summary(); From 90a7e9b4b93d9f69af4ddcd8f81dc1981eac0a05 Mon Sep 17 00:00:00 2001 From: water111 <48171810+water111@users.noreply.github.com> Date: Sat, 12 Sep 2020 20:41:12 -0400 Subject: [PATCH 32/50] Add addition and subtraction for integers, build macros, dgo building, and build/load test (#35) * see if math works on windows * add dgo * windows debug * windows debug 2 * one more debug try * add extra debug print and change logic for slashes * update * again * try again * remove build game * remove build game * add back build-game * remove runtime from test * test * reduce number of files * go to c++ 14 * big stacks * increase stack size again * clean up cmake files --- CMakeLists.txt | 7 +- common/util/CMakeLists.txt | 1 + common/util/DgoWriter.cpp | 28 + common/util/DgoWriter.h | 17 + common/util/FileUtil.cpp | 19 +- game/CMakeLists.txt | 2 +- game/fake_iso.txt | 4 +- game/kernel/klisten.cpp | 1 + goal_src/build/all_files.gc | 511 ++++++++++++++++++ goal_src/goal-lib.gc | 18 + goal_src/test/test-add-function-returns.gc | 16 + goal_src/test/test-add-int-constants.gc | 1 + goal_src/test/test-add-int-multiple-2.gc | 11 + goal_src/test/test-add-int-multiple.gc | 5 + goal_src/test/test-add-int-vars.gc | 5 + goal_src/test/test-build-game.gc | 6 + goal_src/test/test-mul-1.gc | 5 + goal_src/test/test-sub-1.gc | 16 + goal_src/test/test-sub-2.gc | 17 + goalc/CMakeLists.txt | 1 + goalc/compiler/CodeGenerator.cpp | 12 +- goalc/compiler/Compiler.cpp | 10 + goalc/compiler/Compiler.h | 20 + goalc/compiler/CompilerSettings.cpp | 3 + goalc/compiler/CompilerSettings.h | 3 + goalc/compiler/IR.cpp | 52 ++ goalc/compiler/IR.h | 33 ++ goalc/compiler/Val.h | 12 +- goalc/compiler/compilation/Atoms.cpp | 10 +- .../compiler/compilation/CompilerControl.cpp | 44 +- goalc/compiler/compilation/Function.cpp | 9 +- goalc/compiler/compilation/Math.cpp | 203 +++++++ goalc/emitter/IGen.h | 20 + goalc/regalloc/Allocator.cpp | 3 +- goalc/regalloc/Assignment.h | 2 +- test/test_compiler_and_runtime.cpp | 33 +- 36 files changed, 1133 insertions(+), 27 deletions(-) create mode 100644 common/util/DgoWriter.cpp create mode 100644 common/util/DgoWriter.h create mode 100644 goal_src/build/all_files.gc create mode 100644 goal_src/test/test-add-function-returns.gc create mode 100644 goal_src/test/test-add-int-constants.gc create mode 100644 goal_src/test/test-add-int-multiple-2.gc create mode 100644 goal_src/test/test-add-int-multiple.gc create mode 100644 goal_src/test/test-add-int-vars.gc create mode 100644 goal_src/test/test-build-game.gc create mode 100644 goal_src/test/test-mul-1.gc create mode 100644 goal_src/test/test-sub-1.gc create mode 100644 goal_src/test/test-sub-2.gc create mode 100644 goalc/compiler/compilation/Math.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a4719b0e4c..80f850d72f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(jak) -set(CMAKE_CXX_STANDARD 17) +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. @@ -11,8 +11,8 @@ if (CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ -Wall \ - -Winit-self \ - -ggdb \ + -Winit-self \ + -ggdb \ -Wextra \ -Wcast-align \ -Wcast-qual \ @@ -25,6 +25,7 @@ if (CMAKE_COMPILER_IS_GNUCXX) -Wsign-promo") else () set(CMAKE_CXX_FLAGS "/EHsc") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10000000") endif (CMAKE_COMPILER_IS_GNUCXX) IF (WIN32) diff --git a/common/util/CMakeLists.txt b/common/util/CMakeLists.txt index 23f5a949df..caf551f79c 100644 --- a/common/util/CMakeLists.txt +++ b/common/util/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(common_util SHARED FileUtil.cpp + DgoWriter.cpp Timer.cpp) diff --git a/common/util/DgoWriter.cpp b/common/util/DgoWriter.cpp new file mode 100644 index 0000000000..69adf5e950 --- /dev/null +++ b/common/util/DgoWriter.cpp @@ -0,0 +1,28 @@ +#include "BinaryWriter.h" +#include "FileUtil.h" +#include "DgoWriter.h" + +void build_dgo(const DgoDescription& description) { + BinaryWriter writer; + // dgo header + writer.add(description.entries.size()); + writer.add_cstr_len(description.dgo_name.c_str(), 60); + + for (auto& obj : description.entries) { + auto obj_data = file_util::read_binary_file(file_util::get_file_path({"data", obj.file_name})); + // size + writer.add(obj_data.size()); + // name + writer.add_str_len(obj.name_in_dgo, 60); + // data + writer.add_data(obj_data.data(), obj_data.size()); + // pad + while (writer.get_size() & 0xf) { + writer.add(0); + } + } + + printf("DGO: %15s %.3f MB\n", description.dgo_name.c_str(), + (float)(writer.get_size()) / (1 << 20)); + writer.write_to_file(file_util::get_file_path({"out", description.dgo_name})); +} diff --git a/common/util/DgoWriter.h b/common/util/DgoWriter.h new file mode 100644 index 0000000000..1781694b68 --- /dev/null +++ b/common/util/DgoWriter.h @@ -0,0 +1,17 @@ +#ifndef JAK_DGOWRITER_H +#define JAK_DGOWRITER_H + +#include + +struct DgoDescription { + std::string dgo_name; + struct DgoEntry { + std::string file_name; + std::string name_in_dgo; + }; + std::vector entries; +}; + +void build_dgo(const DgoDescription& description); + +#endif // JAK_DGOWRITER_H diff --git a/common/util/FileUtil.cpp b/common/util/FileUtil.cpp index 01c07c1997..88bd4142eb 100644 --- a/common/util/FileUtil.cpp +++ b/common/util/FileUtil.cpp @@ -9,26 +9,29 @@ #include #else #include +#include #endif std::string file_util::get_project_path() { #ifdef _WIN32 char buffer[FILENAME_MAX]; GetModuleFileNameA(NULL, buffer, FILENAME_MAX); - std::string::size_type pos = std::string(buffer).rfind( - "\\jak-project\\"); // Strip file path down to \jak-project\ directory + printf("using path %s\n", buffer); + std::string::size_type pos = + std::string(buffer).rfind("jak-project"); // Strip file path down to \jak-project\ directory + printf("rfind returned %lld\n", pos); return std::string(buffer).substr( - 0, pos + 12); // + 12 to include "\jak-project" in the returned filepath + 0, pos + 11); // + 12 to include "\jak-project" in the returned filepath #else // do Linux stuff char buffer[FILENAME_MAX]; readlink("/proc/self/exe", buffer, FILENAME_MAX); // /proc/self acts like a "virtual folder" containing information about // the current process - std::string::size_type pos = std::string(buffer).rfind( - "/jak-project/"); // Strip file path down to /jak-project/ directory + std::string::size_type pos = + std::string(buffer).rfind("jak-project"); // Strip file path down to /jak-project/ directory return std::string(buffer).substr( - 0, pos + 12); // + 12 to include "/jak-project" in the returned filepath + 0, pos + 11); // + 12 to include "/jak-project" in the returned filepath #endif } @@ -76,7 +79,8 @@ void file_util::write_text_file(const std::string& file_name, const std::string& std::vector file_util::read_binary_file(const std::string& filename) { auto fp = fopen(filename.c_str(), "rb"); if (!fp) - throw std::runtime_error("File " + filename + " cannot be opened"); + throw std::runtime_error("File " + filename + + " cannot be opened: " + std::string(strerror(errno))); fseek(fp, 0, SEEK_END); auto len = ftell(fp); rewind(fp); @@ -87,6 +91,7 @@ std::vector file_util::read_binary_file(const std::string& filename) { if (fread(data.data(), len, 1, fp) != 1) { throw std::runtime_error("File " + filename + " cannot be read"); } + fclose(fp); return data; } diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index 6fb1dacae2..6ded6b6508 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -1,5 +1,5 @@ # We define our own compilation flags here. -set(CMAKE_CXX_STANDARD 17) +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. diff --git a/game/fake_iso.txt b/game/fake_iso.txt index 28f3185fc3..cca0e660ae 100644 --- a/game/fake_iso.txt +++ b/game/fake_iso.txt @@ -2,8 +2,8 @@ ; Each entry should consist of an ISO name, followed by a file name ; note that tweakval, vagdir, screen1 have dummy data for now. -KERNEL.CGO resources/KERNEL.CGO -GAME.CGO resources/GAME.CGO +KERNEL.CGO out/KERNEL.CGO +GAME.CGO out/GAME.CGO TEST.CGO resources/TEST.CGO TWEAKVAL.MUS resources/TWEAKVAL.MUS VAGDIR.AYB resources/VAGDIR.AYB diff --git a/game/kernel/klisten.cpp b/game/kernel/klisten.cpp index 322d049e33..22f9aad10b 100644 --- a/game/kernel/klisten.cpp +++ b/game/kernel/klisten.cpp @@ -149,6 +149,7 @@ void ProcessListenerMessage(Ptr msg) { // this setup allows listener function execution to clean up after itself. ListenerFunction->value = link_and_exec(buffer, "*listener*", 0, kdebugheap, LINK_FLAG_FORCE_DEBUG).offset; + fprintf(stderr, "ListenerFunction is now 0x%x\n", ListenerFunction->value); return; // don't ack yet, this will happen after the function runs. } break; default: diff --git a/goal_src/build/all_files.gc b/goal_src/build/all_files.gc new file mode 100644 index 0000000000..438da9a6b7 --- /dev/null +++ b/goal_src/build/all_files.gc @@ -0,0 +1,511 @@ +(defglobalconstant all-goal-files + ( + "goal_src/kernel/gcommon.gc" + "goal_src/kernel/gstring-h.gc" + "goal_src/kernel/gkernel-h.gc" + "goal_src/kernel/gkernel.gc" + "goal_src/kernel/pskernel.gc" + "goal_src/kernel/gstring.gc" + "goal_src/kernel/dgo-h.gc" + "goal_src/kernel/gstate.gc" + "goal_src/engine/util/types-h.gc" + "goal_src/engine/ps2/vu1-macros.gc" + "goal_src/engine/math/math.gc" + "goal_src/engine/math/vector-h.gc" + "goal_src/engine/physics/gravity-h.gc" + "goal_src/engine/geometry/bounding-box-h.gc" + "goal_src/engine/math/matrix-h.gc" + "goal_src/engine/math/quaternion-h.gc" + "goal_src/engine/math/euler-h.gc" + "goal_src/engine/math/transform-h.gc" + "goal_src/engine/geometry/geometry-h.gc" + "goal_src/engine/math/trigonometry-h.gc" + "goal_src/engine/math/transformq-h.gc" + "goal_src/engine/geometry/bounding-box.gc" + "goal_src/engine/math/matrix.gc" + "goal_src/engine/math/transform.gc" + "goal_src/engine/math/quaternion.gc" + "goal_src/engine/math/euler.gc" + "goal_src/engine/geometry/geometry.gc" + "goal_src/engine/math/trigonometry.gc" + "goal_src/engine/sound/gsound-h.gc" + "goal_src/engine/ps2/timer-h.gc" + "goal_src/engine/ps2/timer.gc" + "goal_src/engine/ps2/vif-h.gc" + "goal_src/engine/dma/dma-h.gc" + "goal_src/engine/gfx/hw/video-h.gc" + "goal_src/engine/gfx/hw/vu1-user-h.gc" + "goal_src/engine/dma/dma.gc" + "goal_src/engine/dma/dma-buffer.gc" + "goal_src/engine/dma/dma-bucket.gc" + "goal_src/engine/dma/dma-disasm.gc" + "goal_src/engine/ps2/pad.gc" + "goal_src/engine/gfx/hw/gs.gc" + "goal_src/engine/gfx/hw/display-h.gc" + "goal_src/engine/math/vector.gc" + "goal_src/engine/load/file-io.gc" + "goal_src/engine/load/loader-h.gc" + "goal_src/engine/gfx/texture-h.gc" + "goal_src/engine/level/level-h.gc" + "goal_src/engine/camera/math-camera-h.gc" + "goal_src/engine/camera/math-camera.gc" + "goal_src/engine/gfx/font-h.gc" + "goal_src/engine/gfx/decomp-h.gc" + "goal_src/engine/gfx/hw/display.gc" + "goal_src/engine/engine/connect.gc" + "goal_src/engine/ui/text-h.gc" + "goal_src/engine/game/settings-h.gc" + "goal_src/engine/gfx/capture.gc" + "goal_src/engine/debug/memory-usage-h.gc" + "goal_src/engine/gfx/texture.gc" + "goal_src/engine/game/main-h.gc" + "goal_src/engine/anim/mspace-h.gc" + "goal_src/engine/draw/drawable-h.gc" + "goal_src/engine/draw/drawable-group-h.gc" + "goal_src/engine/draw/drawable-inline-array-h.gc" + "goal_src/engine/draw/draw-node-h.gc" + "goal_src/engine/draw/drawable-tree-h.gc" + "goal_src/engine/draw/drawable-actor-h.gc" + "goal_src/engine/draw/drawable-ambient-h.gc" + "goal_src/engine/game/task/game-task-h.gc" + "goal_src/engine/game/task/hint-control-h.gc" + "goal_src/engine/gfx/generic/generic-h.gc" + "goal_src/engine/gfx/lights-h.gc" + "goal_src/engine/gfx/ocean/ocean-h.gc" + "goal_src/engine/gfx/ocean/ocean-trans-tables.gc" + "goal_src/engine/gfx/ocean/ocean-tables.gc" + "goal_src/engine/gfx/ocean/ocean-frames.gc" + "goal_src/engine/gfx/sky/sky-h.gc" + "goal_src/engine/gfx/mood-h.gc" + "goal_src/engine/gfx/time-of-day-h.gc" + "goal_src/engine/data/art-h.gc" + "goal_src/engine/gfx/generic/generic-vu1-h.gc" + "goal_src/engine/gfx/merc/merc-h.gc" + "goal_src/engine/gfx/merc/generic-merc-h.gc" + "goal_src/engine/gfx/tie/generic-tie-h.gc" + "goal_src/engine/gfx/generic/generic-work-h.gc" + "goal_src/engine/gfx/shadow/shadow-cpu-h.gc" + "goal_src/engine/gfx/shadow/shadow-vu1-h.gc" + "goal_src/engine/ps2/memcard-h.gc" + "goal_src/engine/game/game-info-h.gc" + "goal_src/engine/gfx/wind-h.gc" + "goal_src/engine/gfx/tie/prototype-h.gc" + "goal_src/engine/anim/joint-h.gc" + "goal_src/engine/anim/bones-h.gc" + "goal_src/engine/engine/engines.gc" + "goal_src/engine/data/res-h.gc" + "goal_src/engine/data/res.gc" + "goal_src/engine/gfx/lights.gc" + "goal_src/engine/physics/dynamics-h.gc" + "goal_src/engine/target/surface-h.gc" + "goal_src/engine/target/pat-h.gc" + "goal_src/engine/game/fact-h.gc" + "goal_src/engine/anim/aligner-h.gc" + "goal_src/engine/game/game-h.gc" + "goal_src/engine/game/generic-obs-h.gc" + "goal_src/engine/camera/pov-camera-h.gc" + "goal_src/engine/util/sync-info-h.gc" + "goal_src/engine/util/smush-control-h.gc" + "goal_src/engine/physics/trajectory-h.gc" + "goal_src/engine/debug/debug-h.gc" + "goal_src/engine/target/joint-mod-h.gc" + "goal_src/engine/collide/collide-func-h.gc" + "goal_src/engine/collide/collide-mesh-h.gc" + "goal_src/engine/collide/collide-shape-h.gc" + "goal_src/engine/collide/collide-target-h.gc" + "goal_src/engine/collide/collide-touch-h.gc" + "goal_src/engine/collide/collide-edge-grab-h.gc" + "goal_src/engine/draw/process-drawable-h.gc" + "goal_src/engine/game/effect-control-h.gc" + "goal_src/engine/collide/collide-frag-h.gc" + "goal_src/engine/game/projectiles-h.gc" + "goal_src/engine/target/target-h.gc" + "goal_src/engine/gfx/depth-cue-h.gc" + "goal_src/engine/debug/stats-h.gc" + "goal_src/engine/gfx/vis/bsp-h.gc" + "goal_src/engine/collide/collide-cache-h.gc" + "goal_src/engine/collide/collide-h.gc" + "goal_src/engine/gfx/shrub/shrubbery-h.gc" + "goal_src/engine/gfx/tie/tie-h.gc" + "goal_src/engine/gfx/tfrag/tfrag-h.gc" + "goal_src/engine/gfx/background-h.gc" + "goal_src/engine/gfx/tfrag/subdivide-h.gc" + "goal_src/engine/entity/entity-h.gc" + "goal_src/engine/gfx/sprite/sprite-h.gc" + "goal_src/engine/gfx/shadow/shadow-h.gc" + "goal_src/engine/gfx/eye-h.gc" + "goal_src/engine/sparticle/sparticle-launcher-h.gc" + "goal_src/engine/sparticle/sparticle-h.gc" + "goal_src/engine/entity/actor-link-h.gc" + "goal_src/engine/camera/camera-h.gc" + "goal_src/engine/camera/cam-debug-h.gc" + "goal_src/engine/camera/cam-interface-h.gc" + "goal_src/engine/camera/cam-update-h.gc" + "goal_src/engine/debug/assert-h.gc" + "goal_src/engine/ui/hud-h.gc" + "goal_src/engine/ui/progress-h.gc" + "goal_src/engine/ps2/rpc-h.gc" + "goal_src/engine/nav/path-h.gc" + "goal_src/engine/nav/navigate-h.gc" + "goal_src/engine/load/load-dgo.gc" + "goal_src/engine/load/ramdisk.gc" + "goal_src/engine/sound/gsound.gc" + "goal_src/engine/math/transformq.gc" + "goal_src/engine/collide/collide-func.gc" + "goal_src/engine/anim/joint.gc" + "goal_src/engine/geometry/cylinder.gc" + "goal_src/engine/gfx/wind.gc" + "goal_src/engine/gfx/vis/bsp.gc" + "goal_src/engine/gfx/tfrag/subdivide.gc" + "goal_src/engine/gfx/sprite/sprite.gc" + "goal_src/engine/gfx/sprite/sprite-distort.gc" + "goal_src/engine/debug/debug-sphere.gc" + "goal_src/engine/debug/debug.gc" + "goal_src/engine/gfx/merc/merc-vu1.gc" + "goal_src/engine/gfx/merc/merc-blend-shape.gc" + "goal_src/engine/gfx/merc/merc.gc" + "goal_src/engine/gfx/ripple.gc" + "goal_src/engine/anim/bones.gc" + "goal_src/engine/gfx/generic/generic-vu0.gc" + "goal_src/engine/gfx/generic/generic.gc" + "goal_src/engine/gfx/generic/generic-vu1.gc" + "goal_src/engine/gfx/generic/generic-effect.gc" + "goal_src/engine/gfx/generic/generic-merc.gc" + "goal_src/engine/gfx/generic/generic-tie.gc" + "goal_src/engine/gfx/shadow/shadow-cpu.gc" + "goal_src/engine/gfx/shadow/shadow-vu1.gc" + "goal_src/engine/gfx/depth-cue.gc" + "goal_src/engine/gfx/font.gc" + "goal_src/engine/load/decomp.gc" + "goal_src/engine/gfx/background.gc" + "goal_src/engine/draw/draw-node.gc" + "goal_src/engine/gfx/shrub/shrubbery.gc" + "goal_src/engine/gfx/shrub/shrub-work.gc" + "goal_src/engine/gfx/tfrag/tfrag-near.gc" + "goal_src/engine/gfx/tfrag/tfrag.gc" + "goal_src/engine/gfx/tfrag/tfrag-methods.gc" + "goal_src/engine/gfx/tfrag/tfrag-work.gc" + "goal_src/engine/gfx/tie/tie.gc" + "goal_src/engine/gfx/tie/tie-near.gc" + "goal_src/engine/gfx/tie/tie-work.gc" + "goal_src/engine/gfx/tie/tie-methods.gc" + "goal_src/engine/util/sync-info.gc" + "goal_src/engine/physics/trajectory.gc" + "goal_src/engine/sparticle/sparticle-launcher.gc" + "goal_src/engine/sparticle/sparticle.gc" + "goal_src/engine/entity/entity-table.gc" + "goal_src/engine/load/loader.gc" + "goal_src/engine/game/task/task-control-h.gc" + "goal_src/engine/game/game-info.gc" + "goal_src/engine/game/game-save.gc" + "goal_src/engine/game/settings.gc" + "goal_src/engine/ambient/mood-tables.gc" + "goal_src/engine/ambient/mood.gc" + "goal_src/engine/ambient/weather-part.gc" + "goal_src/engine/gfx/time-of-day.gc" + "goal_src/engine/gfx/sky/sky-utils.gc" + "goal_src/engine/gfx/sky/sky.gc" + "goal_src/engine/gfx/sky/sky-tng.gc" + "goal_src/engine/level/load-boundary-h.gc" + "goal_src/engine/level/load-boundary.gc" + "goal_src/engine/level/load-boundary-data.gc" + "goal_src/engine/level/level-info.gc" + "goal_src/engine/level/level.gc" + "goal_src/engine/ui/text.gc" + "goal_src/engine/collide/collide-probe.gc" + "goal_src/engine/collide/collide-frag.gc" + "goal_src/engine/collide/collide-mesh.gc" + "goal_src/engine/collide/collide-touch.gc" + "goal_src/engine/collide/collide-edge-grab.gc" + "goal_src/engine/collide/collide-shape.gc" + "goal_src/engine/collide/collide-shape-rider.gc" + "goal_src/engine/collide/collide.gc" + "goal_src/engine/collide/collide-planes.gc" + "goal_src/engine/gfx/merc/merc-death.gc" + "goal_src/engine/gfx/water/water-h.gc" + "goal_src/engine/camera/camera.gc" + "goal_src/engine/camera/cam-interface.gc" + "goal_src/engine/camera/cam-master.gc" + "goal_src/engine/camera/cam-states.gc" + "goal_src/engine/camera/cam-states-dbg.gc" + "goal_src/engine/camera/cam-combiner.gc" + "goal_src/engine/camera/cam-update.gc" + "goal_src/engine/geometry/vol-h.gc" + "goal_src/engine/camera/cam-layout.gc" + "goal_src/engine/camera/cam-debug.gc" + "goal_src/engine/camera/cam-start.gc" + "goal_src/engine/draw/process-drawable.gc" + "goal_src/engine/game/task/hint-control.gc" + "goal_src/engine/ambient/ambient.gc" + "goal_src/engine/debug/assert.gc" + "goal_src/engine/game/generic-obs.gc" + "goal_src/engine/target/target-util.gc" + "goal_src/engine/target/target-part.gc" + "goal_src/engine/collide/collide-reaction-target.gc" + "goal_src/engine/target/logic-target.gc" + "goal_src/engine/target/sidekick.gc" + "goal_src/engine/game/voicebox.gc" + "goal_src/engine/target/target-handler.gc" + "goal_src/engine/target/target.gc" + "goal_src/engine/target/target2.gc" + "goal_src/engine/target/target-death.gc" + "goal_src/engine/debug/menu.gc" + "goal_src/engine/draw/drawable.gc" + "goal_src/engine/draw/drawable-group.gc" + "goal_src/engine/draw/drawable-inline-array.gc" + "goal_src/engine/draw/drawable-tree.gc" + "goal_src/engine/gfx/tie/prototype.gc" + "goal_src/engine/collide/main-collide.gc" + "goal_src/engine/game/video.gc" + "goal_src/engine/game/main.gc" + "goal_src/engine/collide/collide-cache.gc" + "goal_src/engine/entity/relocate.gc" + "goal_src/engine/debug/memory-usage.gc" + "goal_src/engine/entity/entity.gc" + "goal_src/engine/nav/path.gc" + "goal_src/engine/geometry/vol.gc" + "goal_src/engine/nav/navigate.gc" + "goal_src/engine/anim/aligner.gc" + "goal_src/engine/game/effect-control.gc" + "goal_src/engine/gfx/water/water.gc" + "goal_src/engine/game/collectables-part.gc" + "goal_src/engine/game/collectables.gc" + "goal_src/engine/game/task/task-control.gc" + "goal_src/engine/game/task/process-taskable.gc" + "goal_src/engine/camera/pov-camera.gc" + "goal_src/engine/game/powerups.gc" + "goal_src/engine/game/crates.gc" + "goal_src/engine/ui/hud.gc" + "goal_src/engine/ui/hud-classes.gc" + "goal_src/engine/ui/progress/progress-static.gc" + "goal_src/engine/ui/progress/progress-part.gc" + "goal_src/engine/ui/progress/progress-draw.gc" + "goal_src/engine/ui/progress/progress.gc" + "goal_src/engine/ui/credits.gc" + "goal_src/engine/game/projectiles.gc" + "goal_src/engine/gfx/ocean/ocean.gc" + "goal_src/engine/gfx/ocean/ocean-vu0.gc" + "goal_src/engine/gfx/ocean/ocean-texture.gc" + "goal_src/engine/gfx/ocean/ocean-mid.gc" + "goal_src/engine/gfx/ocean/ocean-transition.gc" + "goal_src/engine/gfx/ocean/ocean-near.gc" + "goal_src/engine/gfx/shadow/shadow.gc" + "goal_src/engine/gfx/eye.gc" + "goal_src/engine/util/glist-h.gc" + "goal_src/engine/util/glist.gc" + "goal_src/engine/debug/anim-tester.gc" + "goal_src/engine/debug/viewer.gc" + "goal_src/engine/debug/part-tester.gc" + "goal_src/engine/debug/default-menu.gc" + "goal_src/levels/common/texture-upload.gc" + "goal_src/levels/common/rigid-body-h.gc" + "goal_src/levels/common/water-anim.gc" + "goal_src/levels/common/dark-eco-pool.gc" + "goal_src/levels/common/rigid-body.gc" + "goal_src/levels/common/nav-enemy-h.gc" + "goal_src/levels/common/nav-enemy.gc" + "goal_src/levels/common/baseplat.gc" + "goal_src/levels/common/basebutton.gc" + "goal_src/levels/common/tippy.gc" + "goal_src/levels/common/joint-exploder.gc" + "goal_src/levels/common/babak.gc" + "goal_src/levels/common/sharkey.gc" + "goal_src/levels/common/orb-cache.gc" + "goal_src/levels/common/plat.gc" + "goal_src/levels/common/plat-button.gc" + "goal_src/levels/common/plat-eco.gc" + "goal_src/levels/common/ropebridge.gc" + "goal_src/levels/common/ticky.gc" + "goal_src/levels/common/mistycannon.gc" + "goal_src/levels/common/babak-with-cannon.gc" + "goal_src/levels/beach/air-h.gc" + "goal_src/levels/beach/air.gc" + "goal_src/levels/beach/wobbler.gc" + "goal_src/levels/beach/twister.gc" + "goal_src/levels/beach/beach-obs.gc" + "goal_src/levels/beach/bird-lady.gc" + "goal_src/levels/beach/bird-lady-beach.gc" + "goal_src/levels/beach/mayor.gc" + "goal_src/levels/beach/sculptor.gc" + "goal_src/levels/beach/pelican.gc" + "goal_src/levels/beach/lurkerworm.gc" + "goal_src/levels/beach/lurkercrab.gc" + "goal_src/levels/beach/lurkerpuppy.gc" + "goal_src/levels/beach/beach-rocks.gc" + "goal_src/levels/beach/seagull.gc" + "goal_src/levels/beach/beach-part.gc" + "goal_src/levels/village_common/villagep-obs.gc" + "goal_src/levels/village_common/oracle.gc" + "goal_src/levels/common/battlecontroller.gc" + "goal_src/levels/citadel/citadel-part.gc" + "goal_src/levels/citadel/citadel-obs.gc" + "goal_src/levels/citadel/citb-plat.gc" + "goal_src/levels/citadel/citadel-sages.gc" + "goal_src/levels/common/snow-bunny.gc" + "goal_src/levels/citadel/citb-bunny.gc" + "goal_src/levels/citadel/citb-drop-plat-CIT.gc" + "goal_src/levels/l1_only/citb-drop-plat-L1.gc" + "goal_src/levels/citadel/assistant-citadel.gc" + "goal_src/levels/darkcave/darkcave-obs.gc" + "goal_src/levels/demo/demo-obs.gc" + "goal_src/levels/common/static-screen.gc" + "goal_src/levels/finalboss/robotboss-h.gc" + "goal_src/levels/finalboss/robotboss-part.gc" + "goal_src/levels/finalboss/sage-finalboss-part.gc" + "goal_src/levels/finalboss/light-eco.gc" + "goal_src/levels/finalboss/robotboss-weapon.gc" + "goal_src/levels/finalboss/robotboss-misc.gc" + "goal_src/levels/finalboss/green-eco-lurker.gc" + "goal_src/levels/finalboss/robotboss.gc" + "goal_src/levels/finalboss/final-door.gc" + "goal_src/levels/finalboss/sage-finalboss-FIN.gc" + "goal_src/levels/l1_only/sage-finalboss-L1.gc" + "goal_src/levels/intro/evilbro.gc" + "goal_src/levels/jungleb/jungleb-obs.gc" + "goal_src/levels/jungleb/plat-flip.gc" + "goal_src/levels/jungleb/aphid.gc" + "goal_src/levels/jungleb/plant-boss.gc" + "goal_src/levels/jungle/jungle-elevator.gc" + "goal_src/levels/jungle/bouncer.gc" + "goal_src/levels/jungle/hopper.gc" + "goal_src/levels/jungle/junglesnake.gc" + "goal_src/levels/jungle/darkvine.gc" + "goal_src/levels/jungle/jungle-obs.gc" + "goal_src/levels/jungle/jungle-mirrors.gc" + "goal_src/levels/jungle/junglefish.gc" + "goal_src/levels/jungle/fisher-JUN.gc" + "goal_src/levels/jungle/fisher-JUNGLE-L1.gc" + "goal_src/levels/jungle/jungle-part.gc" + "goal_src/levels/common/launcherdoor.gc" + "goal_src/levels/l1_only/target-racer-h-L1-RACERP.gc" + "goal_src/levels/racer_common/target-racer-h-FIC-LAV-MIS-OGR-ROL.gc" + "goal_src/levels/racer_common/racer-part.gc" + "goal_src/levels/racer_common/racer.gc" + "goal_src/levels/l1_only/target-racer-L1-RACERP.gc" + "goal_src/levels/racer_common/target-racer-FIC-LAV-MIS-OGR-ROL.gc" + "goal_src/levels/l1_only/racer-states-L1-RACERP.gc" + "goal_src/levels/racer_common/racer-states-FIC-LAV-MIS-OGR-ROL.gc" + "goal_src/levels/racer_common/collide-reaction-racer.gc" + "goal_src/levels/common/blocking-plane.gc" + "goal_src/levels/flut_common/flut-part.gc" + "goal_src/levels/flut_common/flutflut.gc" + "goal_src/levels/flut_common/target-flut.gc" + "goal_src/levels/village1/farmer.gc" + "goal_src/levels/village1/explorer.gc" + "goal_src/levels/village1/assistant.gc" + "goal_src/levels/village1/sage.gc" + "goal_src/levels/village1/yakow.gc" + "goal_src/levels/l1_only/village-obs-L1.gc" + "goal_src/levels/village1/village-obs-VI1.gc" + "goal_src/levels/village1/fishermans-boat.gc" + "goal_src/levels/village1/village1-part.gc" + "goal_src/levels/village1/village1-part2.gc" + "goal_src/levels/village1/sequence-a-village1.gc" + "goal_src/levels/training/training-obs.gc" + "goal_src/levels/training/training-part.gc" + "goal_src/levels/misty/misty-obs.gc" + "goal_src/levels/misty/misty-warehouse.gc" + "goal_src/levels/misty/misty-conveyor.gc" + "goal_src/levels/misty/mud.gc" + "goal_src/levels/misty/muse.gc" + "goal_src/levels/misty/bonelurker.gc" + "goal_src/levels/misty/quicksandlurker.gc" + "goal_src/levels/misty/misty-teetertotter.gc" + "goal_src/levels/misty/balloonlurker.gc" + "goal_src/levels/misty/misty-part.gc" + "goal_src/levels/misty/sidekick-human.gc" + "goal_src/levels/firecanyon/firecanyon-part.gc" + "goal_src/levels/firecanyon/assistant-firecanyon.gc" + "goal_src/levels/village2/village2-part.gc" + "goal_src/levels/village2/village2-obs.gc" + "goal_src/levels/village2/village2-part2.gc" + "goal_src/levels/village2/gambler.gc" + "goal_src/levels/village2/warrior.gc" + "goal_src/levels/village2/geologist.gc" + "goal_src/levels/village2/swamp-blimp.gc" + "goal_src/levels/village2/sage-bluehut.gc" + "goal_src/levels/village2/flutflut-bluehut.gc" + "goal_src/levels/village2/assistant-village2.gc" + "goal_src/levels/village2/sunken-elevator.gc" + "goal_src/levels/swamp/swamp-obs.gc" + "goal_src/levels/swamp/swamp-bat.gc" + "goal_src/levels/swamp/swamp-rat.gc" + "goal_src/levels/swamp/swamp-rat-nest.gc" + "goal_src/levels/swamp/kermit.gc" + "goal_src/levels/swamp/swamp-part.gc" + "goal_src/levels/swamp/billy.gc" + "goal_src/levels/maincave/cavecrystal-light.gc" + "goal_src/levels/maincave/maincave-obs.gc" + "goal_src/levels/maincave/maincave-part.gc" + "goal_src/levels/maincave/spiderwebs.gc" + "goal_src/levels/maincave/dark-crystal.gc" + "goal_src/levels/maincave/baby-spider.gc" + "goal_src/levels/maincave/mother-spider-h.gc" + "goal_src/levels/maincave/mother-spider-egg.gc" + "goal_src/levels/maincave/mother-spider-proj.gc" + "goal_src/levels/maincave/mother-spider.gc" + "goal_src/levels/maincave/gnawer.gc" + "goal_src/levels/maincave/driller-lurker.gc" + "goal_src/levels/sunken/sunken-part.gc" + "goal_src/levels/sunken/sunken-part2.gc" + "goal_src/levels/sunken/sunken-part3.gc" + "goal_src/levels/sunken/sunken-part4.gc" + "goal_src/levels/sunken/sunken-part5.gc" + "goal_src/levels/sunken/target-tube.gc" + "goal_src/levels/sunken/sunken-obs.gc" + "goal_src/levels/sunken/shover.gc" + "goal_src/levels/sunken/square-platform.gc" + "goal_src/levels/sunken/sun-iris-door.gc" + "goal_src/levels/sunken/orbit-plat.gc" + "goal_src/levels/sunken/wedge-plats.gc" + "goal_src/levels/sunken/wall-plat.gc" + "goal_src/levels/sunken/qbert-plat.gc" + "goal_src/levels/sunken/steam-cap.gc" + "goal_src/levels/sunken/sun-exit-chamber.gc" + "goal_src/levels/sunken/floating-launcher.gc" + "goal_src/levels/sunken/sunken-water.gc" + "goal_src/levels/sunken/whirlpool.gc" + "goal_src/levels/sunken/sunken-pipegame.gc" + "goal_src/levels/sunken/bully.gc" + "goal_src/levels/sunken/double-lurker.gc" + "goal_src/levels/sunken/helix-water.gc" + "goal_src/levels/sunken/puffer.gc" + "goal_src/levels/sunken/sunken-fish.gc" + "goal_src/levels/rolling/rolling-obs.gc" + "goal_src/levels/rolling/rolling-lightning-mole.gc" + "goal_src/levels/rolling/rolling-robber.gc" + "goal_src/levels/rolling/rolling-race-ring.gc" + "goal_src/levels/firecanyon/firecanyon-obs.gc" + "goal_src/levels/ogre/ogre-part.gc" + "goal_src/levels/ogre/ogreboss.gc" + "goal_src/levels/ogre/ogre-obs.gc" + "goal_src/levels/ogre/flying-lurker.gc" + "goal_src/levels/village3/village3-part.gc" + "goal_src/levels/village3/village3-obs.gc" + "goal_src/levels/village3/minecart.gc" + "goal_src/levels/village3/miners.gc" + "goal_src/levels/village3/assistant-village3.gc" + "goal_src/levels/village3/sage-village3.gc" + "goal_src/levels/robocave/cave-trap.gc" + "goal_src/levels/robocave/spider-egg.gc" + "goal_src/levels/robocave/robocave-part.gc" + "goal_src/levels/snow/target-snowball.gc" + "goal_src/levels/snow/target-ice.gc" + "goal_src/levels/snow/ice-cube.gc" + "goal_src/levels/snow/snow-ball.gc" + "goal_src/levels/snow/snow-obs.gc" + "goal_src/levels/snow/snow-flutflut-obs.gc" + "goal_src/levels/snow/snow-bumper.gc" + "goal_src/levels/snow/snow-ram-h.gc" + "goal_src/levels/snow/snow-ram-boss.gc" + "goal_src/levels/snow/snow-ram.gc" + "goal_src/levels/snow/snow-part.gc" + "goal_src/levels/snow/yeti.gc" + "goal_src/levels/lavatube/lavatube-obs.gc" + "goal_src/levels/lavatube/lavatube-energy.gc" + "goal_src/levels/lavatube/lavatube-part.gc" + "goal_src/levels/lavatube/assistant-lavatube.gc" + "goal_src/levels/title/title-obs.gc" + "goal_src/old/lava/lava.gc" + ) + ) \ No newline at end of file diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index da2e5bf547..2c746a53fb 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -1,3 +1,7 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; BUILD SYSTEM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; compile, color, and save a file (defmacro m (file) `(asm-file ,file :color :write) @@ -8,6 +12,20 @@ `(asm-file ,file :color :load :write) ) +(desfun make-build-command (file) + `(asm-file ,file :color :write) + ) + +(defmacro build-game () + `(begin + ,@(apply make-build-command all-goal-files) + (build-dgos "goal_src/build/dgos.txt") + ) + ) + +;; load list of all goal files +(asm-file "goal_src/build/all_files.gc") + (defmacro e () `(:exit) ) diff --git a/goal_src/test/test-add-function-returns.gc b/goal_src/test/test-add-function-returns.gc new file mode 100644 index 0000000000..85989a928d --- /dev/null +++ b/goal_src/test/test-add-function-returns.gc @@ -0,0 +1,16 @@ +(defun return-one () + 1) + +(defun return-sum ((a integer) (b integer)) + (+ a b) + ) + +(defun return-plus-two ((in integer)) + (+ in 2) + ) + +(defun return-plus-three ((in integer)) + (+ 3 in) + ) + +(+ 2 (return-one) (return-plus-three 2) (return-plus-two 3) (return-sum 1 2) (return-plus-two 3)) \ No newline at end of file diff --git a/goal_src/test/test-add-int-constants.gc b/goal_src/test/test-add-int-constants.gc new file mode 100644 index 0000000000..422b5923f5 --- /dev/null +++ b/goal_src/test/test-add-int-constants.gc @@ -0,0 +1 @@ +(+ 15 -2) \ No newline at end of file diff --git a/goal_src/test/test-add-int-multiple-2.gc b/goal_src/test/test-add-int-multiple-2.gc new file mode 100644 index 0000000000..82a3684b08 --- /dev/null +++ b/goal_src/test/test-add-int-multiple-2.gc @@ -0,0 +1,11 @@ +(defun add-five-v2 ((a int32) (b int32) (c int32) (d int32) (e int32)) + (let* ((total-1 a) + (also-d d) + (total-2 (+ total-1 b)) + (total-3 (+ c total-2)) + ) + (+ total-3 e also-d) + ) + ) + +(add-five 1 2 3 4 5) diff --git a/goal_src/test/test-add-int-multiple.gc b/goal_src/test/test-add-int-multiple.gc new file mode 100644 index 0000000000..5e13e42805 --- /dev/null +++ b/goal_src/test/test-add-int-multiple.gc @@ -0,0 +1,5 @@ +(defun add-five ((a integer) (b integer) (c integer) (d integer) (e integer)) + (+ c d (+ e a) b) + ) + +(add-five 1 2 3 4 5) \ No newline at end of file diff --git a/goal_src/test/test-add-int-vars.gc b/goal_src/test/test-add-int-vars.gc new file mode 100644 index 0000000000..3de3bff655 --- /dev/null +++ b/goal_src/test/test-add-int-vars.gc @@ -0,0 +1,5 @@ +(defun add-two ((x integer) (y integer)) + (+ x y) + ) + +(add-two 3 4) \ No newline at end of file diff --git a/goal_src/test/test-build-game.gc b/goal_src/test/test-build-game.gc new file mode 100644 index 0000000000..83f28a46fb --- /dev/null +++ b/goal_src/test/test-build-game.gc @@ -0,0 +1,6 @@ +(build-game) +;(define-extern global kheap) +;(define-extern dgo-load (function string kheap int int int)) +;(dgo-load "kernel" global #xf #x200000) ; todo, remove once kernel loads itself. +;(dgo-load "game" global #xf #x200000) +1 \ No newline at end of file diff --git a/goal_src/test/test-mul-1.gc b/goal_src/test/test-mul-1.gc new file mode 100644 index 0000000000..40a27765ad --- /dev/null +++ b/goal_src/test/test-mul-1.gc @@ -0,0 +1,5 @@ +(defun product ((a integer) (b integer)) + (* b a) + ) + +(product 4 -3) \ No newline at end of file diff --git a/goal_src/test/test-sub-1.gc b/goal_src/test/test-sub-1.gc new file mode 100644 index 0000000000..a0556c9610 --- /dev/null +++ b/goal_src/test/test-sub-1.gc @@ -0,0 +1,16 @@ +(defun negative ((a integer)) + (- a) + ) + +(defun minus-two ((a integer)) + (- a 2) + ) + + +(defun sub-test ((a integer) (b integer)) + (- 1 a b) + ) + +(let ((x (sub-test 1 (minus-two 7)))) + (- 1 x (- -2)) + ) diff --git a/goal_src/test/test-sub-2.gc b/goal_src/test/test-sub-2.gc new file mode 100644 index 0000000000..321c930931 --- /dev/null +++ b/goal_src/test/test-sub-2.gc @@ -0,0 +1,17 @@ +(defun negative ((a integer)) + (- a) + ) + +(defun minus-two ((a integer)) + (- a 2) + ) + + +(defun sub-test ((a integer) (b integer)) + (- 1 a b) + ) + +(let ((x (sub-test 1 (minus-two 7)))) + (- 1 (negative -2) x) + ) + diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 50a6ae967f..0c9ba1f0f4 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(compiler compiler/compilation/CompilerControl.cpp compiler/compilation/Block.cpp compiler/compilation/Macro.cpp + compiler/compilation/Math.cpp compiler/compilation/Define.cpp compiler/compilation/Function.cpp compiler/Util.cpp diff --git a/goalc/compiler/CodeGenerator.cpp b/goalc/compiler/CodeGenerator.cpp index 8864d52939..c7513e21cf 100644 --- a/goalc/compiler/CodeGenerator.cpp +++ b/goalc/compiler/CodeGenerator.cpp @@ -86,13 +86,21 @@ void CodeGenerator::do_function(FunctionEnv* env, int f_idx) { auto& bonus = allocs.stack_ops.at(ir_idx); for (auto& op : bonus.ops) { if (op.load) { - assert(false); + if (op.reg.is_gpr()) { + m_gen.add_instr(IGen::load64_gpr64_plus_s32(op.reg, op.slot * GPR_SIZE, RSP), i_rec); + } else { + assert(false); + } } } ir->do_codegen(&m_gen, allocs, i_rec); for (auto& op : bonus.ops) { if (op.store) { - assert(false); + if (op.reg.is_gpr()) { + m_gen.add_instr(IGen::store64_gpr64_plus_s32(RSP, op.slot * GPR_SIZE, op.reg), i_rec); + } else { + assert(false); + } } } } diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index b03bdaa400..f50a2826a9 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -158,6 +158,7 @@ void Compiler::color_object_file(FileEnv* env) { input.debug_settings.print_input = true; input.debug_settings.print_result = true; input.debug_settings.print_analysis = true; + input.debug_settings.allocate_log_level = 2; } f->set_allocations(allocate_registers(input)); @@ -186,6 +187,9 @@ std::vector Compiler::run_test(const std::string& source_code) { auto code = m_goos.reader.read_from_file({source_code}); auto compiled = compile_object_file("test-code", code, true); + if (compiled->is_empty()) { + return {}; + } color_object_file(compiled); auto data = codegen_object_file(compiled); m_listener.record_messages(ListenerMessageKind::MSG_PRINT); @@ -200,6 +204,12 @@ std::vector Compiler::run_test(const std::string& source_code) { } } +std::vector Compiler::run_test_no_load(const std::string& source_code) { + auto code = m_goos.reader.read_from_file({source_code}); + auto compiled = compile_object_file("test-code", code, true); + return {}; +} + void Compiler::shutdown_target() { if (m_listener.is_connected()) { m_listener.send_reset(true); diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index aa00e90137..af93e5325b 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -8,6 +8,8 @@ #include "goalc/compiler/IR.h" #include "CompilerSettings.h" +enum MathMode { MATH_INT, MATH_BINT, MATH_FLOAT, MATH_INVALID }; + class Compiler { public: Compiler(); @@ -24,6 +26,7 @@ class Compiler { None* get_none() { return m_none.get(); } std::vector run_test(const std::string& source_code); + std::vector run_test_no_load(const std::string& source_code); void shutdown_target(); private: @@ -83,6 +86,16 @@ class Compiler { std::unordered_map, goos::Object> m_global_constants; std::unordered_map, LambdaVal*> m_inlineable_functions; CompilerSettings m_settings; + MathMode get_math_mode(const TypeSpec& ts); + bool is_number(const TypeSpec& ts); + bool is_float(const TypeSpec& ts); + bool is_integer(const TypeSpec& ts); + bool is_binteger(const TypeSpec& ts); + bool is_singed_integer_or_binteger(const TypeSpec& ts); + Val* number_to_integer(Val* in, Env* env); + Val* number_to_float(Val* in, Env* env); + Val* number_to_binteger(Val* in, Env* env); + Val* to_math_type(Val* in, MathMode mode, Env* env); public: // Atoms @@ -104,6 +117,8 @@ class Compiler { Val* compile_poke(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_gs(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_set_config(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_in_package(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_build_dgo(const goos::Object& form, const goos::Object& rest, Env* env); // Define Val* compile_define(const goos::Object& form, const goos::Object& rest, Env* env); @@ -114,6 +129,11 @@ class Compiler { 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); + // Math + Val* compile_add(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_sub(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_mul(const goos::Object& form, const goos::Object& rest, Env* env); + // Function Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_inline(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/CompilerSettings.cpp b/goalc/compiler/CompilerSettings.cpp index 81463587f0..eec56ac010 100644 --- a/goalc/compiler/CompilerSettings.cpp +++ b/goalc/compiler/CompilerSettings.cpp @@ -6,6 +6,9 @@ CompilerSettings::CompilerSettings() { m_settings["print-regalloc"].kind = SettingKind::BOOL; m_settings["print-regalloc"].boolp = &debug_print_regalloc; + + m_settings["disable-math-const-prop"].kind = SettingKind::BOOL; + m_settings["disable-math-const-prop"].boolp = &disable_math_const_prop; } void CompilerSettings::set(const std::string& name, const goos::Object& value) { diff --git a/goalc/compiler/CompilerSettings.h b/goalc/compiler/CompilerSettings.h index fa6d9b56b4..6bf4badd45 100644 --- a/goalc/compiler/CompilerSettings.h +++ b/goalc/compiler/CompilerSettings.h @@ -10,6 +10,9 @@ class CompilerSettings { CompilerSettings(); bool debug_print_ir = false; bool debug_print_regalloc = false; + bool disable_math_const_prop = false; + bool emit_move_after_return = true; + void set(const std::string& name, const goos::Object& value); private: diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 8d046af51b..5c8ccb8146 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -350,4 +350,56 @@ void IR_FunctionAddr::do_codegen(emitter::ObjectGenerator* gen, auto instr = gen->add_instr(IGen::static_addr(dr, 0), irec); gen->link_instruction_to_function(instr, gen->get_existing_function_record(m_src->idx_in_file)); gen->add_instr(IGen::sub_gpr64_gpr64(dr, emitter::gRegInfo.get_offset_reg()), irec); +} + +///////////////////// +// IntegerMath +/////////////////// + +IR_IntegerMath::IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg) + : m_kind(kind), m_dest(dest), m_arg(arg) {} + +std::string IR_IntegerMath::print() { + switch (m_kind) { + case IntegerMathKind::ADD_64: + return fmt::format("addi {}, {}", m_dest->print(), m_arg->print()); + case IntegerMathKind::SUB_64: + return fmt::format("subi {}, {}", m_dest->print(), m_arg->print()); + case IntegerMathKind::IMUL_32: + return fmt::format("imul {}, {}", m_dest->print(), m_arg->print()); + default: + assert(false); + } +} + +RegAllocInstr IR_IntegerMath::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + rai.read.push_back(m_dest->ireg()); + rai.read.push_back(m_arg->ireg()); + return rai; +} + +void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + switch (m_kind) { + case IntegerMathKind::ADD_64: + gen->add_instr( + IGen::add_gpr64_gpr64(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec); + break; + case IntegerMathKind::SUB_64: + gen->add_instr( + IGen::sub_gpr64_gpr64(get_reg(m_dest, allocs, irec), get_reg(m_arg, allocs, irec)), irec); + break; + case IntegerMathKind::IMUL_32: { + auto dr = get_reg(m_dest, allocs, irec); + gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec); + gen->add_instr(IGen::movsx_r64_r32(dr, dr), irec); + } + + break; + default: + assert(false); + } } \ No newline at end of file diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index d7cfa70f85..0443eb4279 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -173,4 +173,37 @@ class IR_FunctionAddr : public IR { FunctionEnv* m_src = nullptr; }; +enum class IntegerMathKind { + ADD_64, + SUB_64, + IMUL_32, + IDIV_32, + SHLV_64, + SARV_64, + SHRV_64, + SHL_64, + SAR_64, + SHR_64, + IMOD_32, + OR_64, + AND_64, + XOR_64, + NOT_64 +}; + +class IR_IntegerMath : public IR { + public: + IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + + protected: + IntegerMathKind m_kind; + RegVal* m_dest; + RegVal* m_arg; +}; + #endif // JAK_IR_H diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index 21e4015c72..239ee59fe0 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -131,13 +131,23 @@ class StaticVal : public Val { class IntegerConstantVal : public Val { public: - IntegerConstantVal(TypeSpec ts, s64 value) : Val(ts), m_value(value) {} + IntegerConstantVal(TypeSpec ts, s64 value) : Val(std::move(ts)), m_value(value) {} std::string print() const override { return "integer-constant-" + std::to_string(m_value); } RegVal* to_reg(Env* fe) override; protected: s64 m_value = -1; }; + +class FloatConstantVal : public Val { + public: + FloatConstantVal(TypeSpec ts, float value) : Val(std::move(ts)), m_value(value) {} + std::string print() const override { return "float-constant-" + std::to_string(m_value); } + RegVal* to_reg(Env* fe) override; + + protected: + float m_value = -1.f; +}; // IntegerConstant // FloatConstant // Bitfield diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 60e2303aba..e1ade2fb5f 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -37,7 +37,7 @@ static const std::unordered_map< {"reset-target", &Compiler::compile_reset_target}, {":status", &Compiler::compile_poke}, // {"test", &Compiler::compile_test}, - // {"in-package", &Compiler::compile_in_package}, + {"in-package", &Compiler::compile_in_package}, // // // CONDITIONAL COMPILATION {"#cond", &Compiler::compile_gscond}, @@ -102,9 +102,9 @@ static const std::unordered_map< // {"cdr", &Compiler::compile_cdr}, // // // IT IS MATH - // {"+", &Compiler::compile_add}, - // {"-", &Compiler::compile_sub}, - // {"*", &Compiler::compile_mult}, + {"+", &Compiler::compile_add}, + {"-", &Compiler::compile_sub}, + {"*", &Compiler::compile_mul}, // {"/", &Compiler::compile_divide}, // {"shlv", &Compiler::compile_shlv}, // {"shrv", &Compiler::compile_shrv}, @@ -127,7 +127,7 @@ static const std::unordered_map< // {">", &Compiler::compile_condition_as_bool}, // // // BUILDER (build-dgo/build-cgo?) - // {"builder", &Compiler::compile_builder}, + {"build-dgos", &Compiler::compile_build_dgo}, // // // UTIL {"set-config!", &Compiler::compile_set_config}, diff --git a/goalc/compiler/compilation/CompilerControl.cpp b/goalc/compiler/compilation/CompilerControl.cpp index 7ece53faba..48113cb4a6 100644 --- a/goalc/compiler/compilation/CompilerControl.cpp +++ b/goalc/compiler/compilation/CompilerControl.cpp @@ -1,6 +1,7 @@ #include "goalc/compiler/Compiler.h" #include "goalc/compiler/IR.h" #include "common/util/Timer.h" +#include "common/util/DgoWriter.h" #include "common/util/FileUtil.h" Val* Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, Env* env) { @@ -67,6 +68,7 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re for (int idx = int(filename.size()) - 1; idx-- > 0;) { if (filename.at(idx) == '\\' || filename.at(idx) == '/') { obj_file_name = filename.substr(idx + 1); + break; } } @@ -94,7 +96,7 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re 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"; + auto output_name = m_goos.reader.get_source_dir() + "/data/" + obj_file_name + ".o"; file_util::write_binary_file(output_name, (void*)data.data(), data.size()); } } else { @@ -108,9 +110,11 @@ Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& re } // if(truthy(get_config("print-asm-file-time"))) { + printf("F: %36s ", obj_file_name.c_str()); for (auto& e : timing) { - printf(" %12s %4.2f\n", e.first.c_str(), e.second); + printf(" %12s %4.2f", e.first.c_str(), e.second); } + printf("\n"); // } return get_none(); @@ -181,5 +185,41 @@ Val* Compiler::compile_set_config(const goos::Object& form, const goos::Object& auto args = get_va(form, rest); va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {}); m_settings.set(symbol_string(args.unnamed.at(0)), args.unnamed.at(1)); + return get_none(); +} + +Val* Compiler::compile_in_package(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)form; + (void)rest; + (void)env; + return get_none(); +} + +Val* Compiler::compile_build_dgo(const goos::Object& form, const goos::Object& rest, Env* env) { + (void)env; + auto args = get_va(form, rest); + va_check(form, args, {goos::ObjectType::STRING}, {}); + auto dgo_desc = pair_cdr(m_goos.reader.read_from_file({args.unnamed.at(0).as_string()->data})); + + for_each_in_list(dgo_desc, [&](const goos::Object& dgo) { + DgoDescription desc; + auto first = pair_car(dgo); + desc.dgo_name = as_string(first); + auto dgo_rest = pair_cdr(dgo); + + for_each_in_list(dgo_rest, [&](const goos::Object& entry) { + auto e_arg = get_va(dgo, entry); + va_check(dgo, e_arg, {goos::ObjectType::STRING, goos::ObjectType::STRING}, {}); + DgoDescription::DgoEntry o; + o.file_name = as_string(e_arg.unnamed.at(0)); + o.name_in_dgo = as_string(e_arg.unnamed.at(1)); + if (o.file_name.substr(o.file_name.length() - 3) != ".go") { // kill v2's for now. + desc.entries.push_back(o); + } + }); + + build_dgo(desc); + }); + return get_none(); } \ No newline at end of file diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp index aea6a32228..48f33ef192 100644 --- a/goalc/compiler/compilation/Function.cpp +++ b/goalc/compiler/compilation/Function.cpp @@ -351,5 +351,12 @@ Val* Compiler::compile_real_function_call(const goos::Object& form, } env->emit(std::make_unique(function, return_reg, arg_outs)); - return return_reg; + + if (m_settings.emit_move_after_return) { + auto result_reg = env->make_gpr(return_reg->type()); + env->emit(std::make_unique(result_reg, return_reg)); + return result_reg; + } else { + return return_reg; + } } \ No newline at end of file diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp new file mode 100644 index 0000000000..8a9f16689a --- /dev/null +++ b/goalc/compiler/compilation/Math.cpp @@ -0,0 +1,203 @@ +#include "goalc/compiler/Compiler.h" + +MathMode Compiler::get_math_mode(const TypeSpec& ts) { + if (m_ts.typecheck(m_ts.make_typespec("binteger"), ts, "", false, false)) { + return MATH_BINT; + } + + if (m_ts.typecheck(m_ts.make_typespec("integer"), ts, "", false, false)) { + return MATH_INT; + } + + if (m_ts.typecheck(m_ts.make_typespec("float"), ts, "", false, false)) { + return MATH_FLOAT; + } + + return MATH_INVALID; +} + +bool Compiler::is_number(const TypeSpec& ts) { + return m_ts.typecheck(m_ts.make_typespec("number"), ts, "", false, false); +} + +bool Compiler::is_float(const TypeSpec& ts) { + return m_ts.typecheck(m_ts.make_typespec("float"), ts, "", false, false); +} + +bool Compiler::is_integer(const TypeSpec& ts) { + return m_ts.typecheck(m_ts.make_typespec("integer"), ts, "", false, false) && + !m_ts.typecheck(m_ts.make_typespec("binteger"), ts, "", false, false); +} + +bool Compiler::is_binteger(const TypeSpec& ts) { + return m_ts.typecheck(m_ts.make_typespec("binteger"), ts, "", false, false); +} + +bool Compiler::is_singed_integer_or_binteger(const TypeSpec& ts) { + return m_ts.typecheck(m_ts.make_typespec("integer"), ts, "", false, false) && + !m_ts.typecheck(m_ts.make_typespec("uinteger"), ts, "", false, false); +} + +Val* Compiler::number_to_integer(Val* in, Env* env) { + auto ts = in->type(); + if (is_binteger(ts)) { + assert(false); + } else if (is_float(ts)) { + assert(false); + } else if (is_integer(ts)) { + return in; + } else { + assert(false); + } +} + +Val* Compiler::number_to_binteger(Val* in, Env* env) { + auto ts = in->type(); + if (is_binteger(ts)) { + return in; + } else if (is_float(ts)) { + assert(false); + } else if (is_integer(ts)) { + assert(false); + } else { + assert(false); + } +} + +Val* Compiler::number_to_float(Val* in, Env* env) { + auto ts = in->type(); + if (is_binteger(ts)) { + assert(false); + } else if (is_float(ts)) { + return in; + } else if (is_integer(ts)) { + assert(false); + } else { + assert(false); + } +} + +Val* Compiler::to_math_type(Val* in, MathMode mode, Env* env) { + switch (mode) { + case MATH_BINT: + return number_to_binteger(in, env); + case MATH_INT: + return number_to_integer(in, env); + case MATH_FLOAT: + return number_to_float(in, env); + default: + assert(false); + } +} + +Val* Compiler::compile_add(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.empty()) { + throw_compile_error(form, "Invalid + form"); + } + + // look at the first value to determine the math mode + auto first_val = compile_error_guard(args.unnamed.at(0), env); + auto first_type = first_val->type(); + auto math_type = get_math_mode(first_type); + switch (math_type) { + case MATH_INT: { + auto result = env->make_gpr(first_type); + env->emit(std::make_unique(result, first_val->to_gpr(env))); + + for (size_t i = 1; i < args.unnamed.size(); i++) { + env->emit(std::make_unique( + IntegerMathKind::ADD_64, result, + to_math_type(compile_error_guard(args.unnamed.at(i), env), math_type, env) + ->to_gpr(env))); + } + return result; + } + case MATH_INVALID: + throw_compile_error( + form, "Cannot determine the math mode for object of type " + first_type.print()); + break; + default: + assert(false); + } + assert(false); + return get_none(); +} + +Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.empty()) { + throw_compile_error(form, "Invalid * form"); + } + + // look at the first value to determine the math mode + auto first_val = compile_error_guard(args.unnamed.at(0), env); + auto first_type = first_val->type(); + auto math_type = get_math_mode(first_type); + switch (math_type) { + case MATH_INT: { + auto result = env->make_gpr(first_type); + env->emit(std::make_unique(result, first_val->to_gpr(env))); + + for (size_t i = 1; i < args.unnamed.size(); i++) { + env->emit(std::make_unique( + IntegerMathKind::IMUL_32, result, + to_math_type(compile_error_guard(args.unnamed.at(i), env), math_type, env) + ->to_gpr(env))); + } + return result; + } + case MATH_INVALID: + throw_compile_error( + form, "Cannot determine the math mode for object of type " + first_type.print()); + break; + default: + assert(false); + } + assert(false); + return get_none(); +} + +Val* Compiler::compile_sub(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.empty()) { + throw_compile_error(form, "Invalid - form"); + } + + auto first_val = compile_error_guard(args.unnamed.at(0), env); + auto first_type = first_val->type(); + auto math_type = get_math_mode(first_type); + switch (math_type) { + case MATH_INT: + if (args.unnamed.size() == 1) { + auto result = compile_integer(0, env)->to_gpr(env); + env->emit(std::make_unique( + IntegerMathKind::SUB_64, result, + to_math_type(compile_error_guard(args.unnamed.at(0), env), math_type, env) + ->to_gpr(env))); + return result; + } else { + auto result = env->make_gpr(first_type); + env->emit(std::make_unique( + result, to_math_type(compile_error_guard(args.unnamed.at(0), env), math_type, env) + ->to_gpr(env))); + + for (size_t i = 1; i < args.unnamed.size(); i++) { + env->emit(std::make_unique( + IntegerMathKind::SUB_64, result, + to_math_type(compile_error_guard(args.unnamed.at(i), env), math_type, env) + ->to_gpr(env))); + } + return result; + } + + case MATH_INVALID: + throw_compile_error( + form, "Cannot determine the math mode for object of type " + first_type.print()); + break; + default: + assert(false); + } + assert(false); + return get_none(); +} \ No newline at end of file diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index fca3273d1b..481e19f7b7 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -1199,6 +1199,26 @@ class IGen { // TODO, special load/stores of 128 bit values. // TODO, consider specialized stack loads and stores? + static Instruction load64_gpr64_plus_s32(Register dst_reg, int32_t offset, Register src_reg) { + assert(dst_reg.is_gpr()); + assert(src_reg.is_gpr()); + Instruction instr(0x8b); + instr.set_modrm_rex_sib_for_reg_reg_disp32(dst_reg.hw_id(), 2, src_reg.hw_id(), true); + instr.set_disp(Imm(4, offset)); + return instr; + } + + /*! + * Store 64-bits from gpr into memory located at 64-bit reg + 32-bit signed offset. + */ + static Instruction store64_gpr64_plus_s32(Register addr, int32_t offset, Register value) { + assert(addr.is_gpr()); + assert(value.is_gpr()); + Instruction instr(0x89); + instr.set_modrm_rex_sib_for_reg_reg_disp32(value.hw_id(), 2, addr.hw_id(), true); + instr.set_disp(Imm(4, offset)); + return instr; + } //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // FUNCTION STUFF diff --git a/goalc/regalloc/Allocator.cpp b/goalc/regalloc/Allocator.cpp index 10e928146f..bbf70f2ad5 100644 --- a/goalc/regalloc/Allocator.cpp +++ b/goalc/regalloc/Allocator.cpp @@ -485,7 +485,8 @@ bool can_var_be_assigned(int var, if (lr.has_constraint && lr.assignment.at(instr - lr.min).is_assigned()) { if (!(ass.occupies_same_reg(lr.assignment.at(instr - lr.min)))) { if (debug_trace >= 2) { - printf("at idx %d self bad\n", instr); + printf("at idx %d self bad (%s) (%s)\n", instr, + lr.assignment.at(instr - lr.min).to_string().c_str(), ass.to_string().c_str()); } return false; diff --git a/goalc/regalloc/Assignment.h b/goalc/regalloc/Assignment.h index 9752d6b073..fea9994a5b 100644 --- a/goalc/regalloc/Assignment.h +++ b/goalc/regalloc/Assignment.h @@ -21,7 +21,7 @@ struct Assignment { } switch (kind) { case Kind::STACK: - result += fmt::format("stack[{:2d}]", stack_slot); + result += fmt::format("s[{:2d}]", stack_slot); break; case Kind::REGISTER: result += emitter::gRegInfo.get_info(reg).name; diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp index f165e020f6..0a731e9488 100644 --- a/test/test_compiler_and_runtime.cpp +++ b/test/test_compiler_and_runtime.cpp @@ -113,6 +113,26 @@ struct CompilerTestRunner { } // namespace +TEST(CompilerAndRuntime, BuildGame) { + std::thread runtime_thread([]() { exec_runtime(0, nullptr); }); + Compiler compiler; + + fprintf(stderr, "about to run test\n"); + + try { + compiler.run_test("goal_src/test/test-build-game.gc"); + } catch (std::exception& e) { + fprintf(stderr, "caught exception %s\n", e.what()); + EXPECT_TRUE(false); + } + fprintf(stderr, "DONE!\n"); + + // todo, tests after loading the game. + + compiler.shutdown_target(); + runtime_thread.join(); +} + TEST(CompilerAndRuntime, CompilerTests) { std::thread runtime_thread([]() { exec_runtime(0, nullptr); }); Compiler compiler; @@ -153,7 +173,18 @@ TEST(CompilerAndRuntime, CompilerTests) { runner.run_test("test-defun-return-symbol.gc", {"42\n"}); runner.run_test("test-function-return-arg.gc", {"23\n"}); runner.run_test("test-nested-function-call.gc", {"2\n"}); + + // math + runner.run_test("test-add-int-constants.gc", {"13\n"}); + runner.run_test("test-add-int-vars.gc", {"7\n"}); + runner.run_test("test-add-int-multiple.gc", {"15\n"}); + runner.run_test("test-add-int-multiple-2.gc", {"15\n"}); + runner.run_test("test-add-function-returns.gc", {"21\n"}); + runner.run_test("test-sub-1.gc", {"4\n"}); + runner.run_test("test-sub-2.gc", {"4\n"}); + runner.run_test("test-mul-1.gc", {"-12\n"}); + compiler.shutdown_target(); runtime_thread.join(); runner.print_summary(); -} \ No newline at end of file +} From 9ae23514881811b34f3736d6ca2dc179114476d9 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sat, 12 Sep 2020 23:52:26 -0400 Subject: [PATCH 33/50] Ignore third-party folder in PRs, add syntax highlighting for GOAL files --- .gitattributes | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..727015b74f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +third-party/* linguist-vendored +*.gc linguist-language=lisp +*.gd linguist-language=lisp +*.gs linguist-language=Scheme From e7f8620c92543150cecf6754aceb1df062b63d8e Mon Sep 17 00:00:00 2001 From: water Date: Sun, 13 Sep 2020 10:40:21 -0400 Subject: [PATCH 34/50] add floats and inline stuff --- goal_src/goal-lib.gc | 14 ++- goal_src/kernel-defs.gc | 148 ++++++++++++++++++++++++ goal_src/kernel/gcommon.gc | 17 +++ goal_src/test/test-declare-inline.gc | 7 ++ goal_src/test/test-floating-point-1.gc | 1 + goal_src/test/test-inline-call.gc | 7 ++ goal_src/test/test-string-symbol.gc | 1 + goalc/compiler/Compiler.cpp | 2 +- goalc/compiler/Compiler.h | 4 + goalc/compiler/Env.h | 9 +- goalc/compiler/IR.cpp | 45 ++++++- goalc/compiler/IR.h | 15 +++ goalc/compiler/StaticObject.cpp | 32 +++++ goalc/compiler/StaticObject.h | 11 ++ goalc/compiler/Val.cpp | 11 +- goalc/compiler/Val.h | 6 +- goalc/compiler/compilation/Atoms.cpp | 24 +++- goalc/compiler/compilation/Function.cpp | 48 +++++++- goalc/compiler/compilation/Math.cpp | 3 + test/test_compiler_and_runtime.cpp | 50 ++++++++ 20 files changed, 434 insertions(+), 21 deletions(-) create mode 100644 goal_src/kernel-defs.gc create mode 100644 goal_src/test/test-declare-inline.gc create mode 100644 goal_src/test/test-floating-point-1.gc create mode 100644 goal_src/test/test-inline-call.gc create mode 100644 goal_src/test/test-string-symbol.gc diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 2c746a53fb..77ae4e31fb 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -1,3 +1,13 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; OTHER STUFF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; get list of all goal files +(asm-file "goal_src/build/all_files.gc") + +;; tell compiler about stuff defined/implemented in the runtime. +(asm-file "goal_src/kernel-defs.gc") + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; BUILD SYSTEM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -23,13 +33,11 @@ ) ) -;; load list of all goal files -(asm-file "goal_src/build/all_files.gc") - (defmacro e () `(:exit) ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CONDITIONAL COMPILATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/goal_src/kernel-defs.gc b/goal_src/kernel-defs.gc new file mode 100644 index 0000000000..3c97a5608d --- /dev/null +++ b/goal_src/kernel-defs.gc @@ -0,0 +1,148 @@ +;; kernel-defs.gc +;; everything defined in the C Kernel / runtime + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; kscheme - InitHeapAndSymbol +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; fixed symbols +(define-extern #f symbol) +(define-extern #t symbol) +(define-extern function type) +(define-extern basic type) +(define-extern string type) +(define-extern symbol type) +(define-extern type type) +(define-extern object type) +(define-extern link-block type) +(define-extern integer type) +(define-extern sinteger type) +(define-extern uinteger type) +(define-extern binteger type) +(define-extern int8 type) +(define-extern int16 type) +(define-extern int32 type) +(define-extern int64 type) +(define-extern int128 type) +(define-extern uint8 type) +(define-extern uint16 type) +(define-extern uint32 type) +(define-extern uint64 type) +(define-extern uint128 type) +(define-extern float type) +(define-extern process-tree type) +(define-extern process type) +(define-extern thread type) +(define-extern structure type) +(define-extern pair type) +(define-extern pointer type) +(define-extern number type) +(define-extern array type) +(define-extern vu-function type) +(define-extern connectable type) +(define-extern stack-frame type) +(define-extern file-stream type) +(define-extern kheap type) +(define-extern nothing (function none)) +(define-extern delete-basic (function basic none)) +(define-extern static symbol) +(define-extern global kheap) +(define-extern debug kheap) +(define-extern loading-level kheap) ;; not a kheap at boot +(define-extern loading-package kheap) ;; not a kheap at boot +(define-extern process-level-heap kheap) ;; not a kheap at boot +(define-extern stack symbol) +(define-extern scratch symbol) +(define-extern *stratch-top* pointer) +(define-extern zero-func (function int)) +;; asize-of-basic-func - has a bug +;; copy-basic-func - has a bug +;; level - unknown type +;; art-group +;; texture-page-dir +;; texture-page +;; sound +;; dgo +;; top-level + +(define-extern string->symbol (function string symbol)) +(define-extern print (function object object)) +(define-extern inspect (function object object)) +(define-extern load (function string kheap object)) +(define-extern loado (function string kheap object)) +(define-extern unload (function string none)) +(define-extern _format function) +(define-extern malloc (function kheap int pointer)) +(define-extern kmalloc (function kheap int int string)) +(define-extern new-dynamic-structure (function kheap type int structure)) +(define-extern method-set! (function type int function none)) ;; may actually return function. +(define-extern link (function pointer string int kheap int pointer)) +(define-extern dgo-load (function string kheap int int)) +(define-extern link-begin (function pointer string int kheap int int)) +(define-extern link-resume (function int)) +;; mc-run +;; mc-format +;; mc-unformat +;; mc-create-file +;; mc-save +;; mc-load +;; mc-check-result +;; mc-get-slot-info +;; mc-makefile +;; kset-language + +(define-extern *debug-segment* symbol) +(define-extern *enable-method-set* int) +;; *boot-video-mode* ? +(define-extern *deci-count* int) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; klisten - InitListener +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; *listener-link-block* +;; *listener-function* +;; kernel-dispatcher +;; kernel-packages +;; *print-column* + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; kmachine - InitMachineScheme +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; put-display-env +;; syncv +;; sync-path +;; reset-path +;; rest-graph +;; dma-sync +;; gs-put-imr +;; gs-get-imr +;; gs-store-image +;; flush-cache +;; cpad-open +;; cpad-get-data +;; install-handler +;; install-debug-handler +;; file-stream-open +;; file-stream-close +;; file-stream-length +;; file-stream-seek +;; file-stream-read +;; file-stream-write +;; scf-get-language +;; scf-get-time +;; scf-get-aspect +;; scf-get-volume +;; scf-get-territory +;; scf-get-timeout +;; scf-get-inactive-timeout +;; dma-to-iop +;; kernel-shutdown +;; aybabtu +;; *stack-top* +;; *stack-base* +;; *stack-size* +;; *kernel-boot-message* +;; *kernel-boot-mode* +;; *kernel-boot-level* \ No newline at end of file diff --git a/goal_src/kernel/gcommon.gc b/goal_src/kernel/gcommon.gc index 0f99919f9d..057dfb5d3b 100644 --- a/goal_src/kernel/gcommon.gc +++ b/goal_src/kernel/gcommon.gc @@ -5,3 +5,20 @@ ;; name in dgo: gcommon ;; dgos: KERNEL +;; gcommon is the first file compiled and loaded. +;; it's expected that this function will mostly be hand-decompiled + + + +;; The "identity" returns its input unchanged. It uses the special GOAL "object" +;; type, which can basically be anything, so this will work on integers, floats, +;; strings, structures, arrays, etc. The only things which doesn't work with "object" +;; is a 128-bit integer. The upper 64-bits of the integer will usually be lost. +(defun identity ((x object)) + ;; there is an optional "docstring" that can go at the beginning of a function + "Function which returns its input. The first function of the game!" + + ;; the last thing in the function body is the return value. + x + ) + diff --git a/goal_src/test/test-declare-inline.gc b/goal_src/test/test-declare-inline.gc new file mode 100644 index 0000000000..1ca61f64ed --- /dev/null +++ b/goal_src/test/test-declare-inline.gc @@ -0,0 +1,7 @@ +(defun inline-test-function-1 ((x int)) + ;; inline this function by default. + (declare (inline)) + (* 4 x) + ) + +(inline-test-function-1 8) \ No newline at end of file diff --git a/goal_src/test/test-floating-point-1.gc b/goal_src/test/test-floating-point-1.gc new file mode 100644 index 0000000000..96660318c1 --- /dev/null +++ b/goal_src/test/test-floating-point-1.gc @@ -0,0 +1 @@ +1.234 \ No newline at end of file diff --git a/goal_src/test/test-inline-call.gc b/goal_src/test/test-inline-call.gc new file mode 100644 index 0000000000..bc69c9f118 --- /dev/null +++ b/goal_src/test/test-inline-call.gc @@ -0,0 +1,7 @@ +(defun inline-test-function-2 ((x integer)) + ;; inline this function by default. + (declare (allow-inline)) + (* 4 x) + ) + +(+ (inline-test-function-2 8) ((inline inline-test-function-2) 3)) \ No newline at end of file diff --git a/goal_src/test/test-string-symbol.gc b/goal_src/test/test-string-symbol.gc new file mode 100644 index 0000000000..a4dd8b68fb --- /dev/null +++ b/goal_src/test/test-string-symbol.gc @@ -0,0 +1 @@ +(print (string->symbol "test-string")) \ No newline at end of file diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index f50a2826a9..6ca06f87a1 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -206,7 +206,7 @@ std::vector Compiler::run_test(const std::string& source_code) { std::vector Compiler::run_test_no_load(const std::string& source_code) { auto code = m_goos.reader.read_from_file({source_code}); - auto compiled = compile_object_file("test-code", code, true); + compile_object_file("test-code", code, true); return {}; } diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index af93e5325b..d77bd0df34 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -15,6 +15,7 @@ class Compiler { Compiler(); ~Compiler(); void execute_repl(); + goos::Interpreter& get_goos() { return m_goos; } 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, @@ -40,6 +41,8 @@ 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_float(const goos::Object& code, Env* env); + Val* compile_float(float value, Env* env, int seg); Val* compile_symbol(const goos::Object& form, Env* env); Val* compile_string(const goos::Object& form, Env* env); Val* compile_string(const std::string& str, Env* env, int seg = MAIN_SEGMENT); @@ -137,6 +140,7 @@ class Compiler { // Function Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_inline(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_declare(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 026e6a879e..a9fc91c03c 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -90,6 +90,10 @@ class FileEnv : public Env { void debug_print_tl(); const std::vector>& functions() { return m_functions; } const std::vector>& statics() { return m_statics; } + const FunctionEnv& top_level_function() { + assert(m_top_level_func); + return *m_top_level_func; + } bool is_empty(); ~FileEnv() = default; @@ -137,7 +141,7 @@ class FunctionEnv : public DeclareEnv { 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; } + const std::vector>& code() const { return m_code; } int max_vars() const { return m_iregs.size(); } const std::vector& constraints() { return m_constraints; } void constrain(const IRegConstraint& c) { m_constraints.push_back(c); } @@ -167,7 +171,7 @@ class FunctionEnv : public DeclareEnv { int segment = -1; std::string method_of_type_name = "#f"; - + bool is_asm_func = false; std::vector unresolved_gotos; std::unordered_map params; @@ -181,7 +185,6 @@ class FunctionEnv : public DeclareEnv { std::vector m_constraints; // todo, unresolved gotos AllocationResult m_regalloc_result; - bool m_is_asm_func = false; bool m_aligned_stack_required = false; diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index 5c8ccb8146..3174425e23 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -194,8 +194,12 @@ void IR_RegSet::do_codegen(emitter::ObjectGenerator* gen, if (val_reg == dest_reg) { gen->add_instr(IGen::null(), irec); - } else { + } else if (val_reg.is_gpr() && dest_reg.is_gpr()) { gen->add_instr(IGen::mov_gpr64_gpr64(dest_reg, val_reg), irec); + } else if (val_reg.is_xmm() && dest_reg.is_gpr()) { + gen->add_instr(IGen::movd_gpr32_xmm32(dest_reg, val_reg), irec); + } else { + assert(false); } } @@ -329,7 +333,7 @@ void IR_StaticVarAddr::do_codegen(emitter::ObjectGenerator* gen, ///////////////////// // FunctionAddr -/////////////////// +///////////////////// IR_FunctionAddr::IR_FunctionAddr(const RegVal* dest, FunctionEnv* src) : m_dest(dest), m_src(src) {} @@ -354,7 +358,7 @@ void IR_FunctionAddr::do_codegen(emitter::ObjectGenerator* gen, ///////////////////// // IntegerMath -/////////////////// +///////////////////// IR_IntegerMath::IR_IntegerMath(IntegerMathKind kind, RegVal* dest, RegVal* arg) : m_kind(kind), m_dest(dest), m_arg(arg) {} @@ -402,4 +406,39 @@ void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen, default: assert(false); } +} + +///////////////////// +// StaticVarLoad +///////////////////// + +IR_StaticVarLoad::IR_StaticVarLoad(const RegVal* dest, const StaticObject* src) + : m_dest(dest), m_src(src) {} + +std::string IR_StaticVarLoad::print() { + return fmt::format("mov-svl {}, [{}]", m_dest->print(), m_src->print()); +} + +RegAllocInstr IR_StaticVarLoad::to_rai() { + RegAllocInstr rai; + rai.write.push_back(m_dest->ireg()); + return rai; +} + +void IR_StaticVarLoad::do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) { + auto load_info = m_src->get_load_info(); + assert(m_src->get_addr_offset() == 0); + + if (m_dest->ireg().kind == emitter::RegKind::XMM) { + assert(load_info.load_signed == false); + assert(load_info.load_size == 4); + assert(load_info.requires_load == true); + + auto instr = gen->add_instr(IGen::static_load_xmm32(get_reg(m_dest, allocs, irec), 0), irec); + gen->link_instruction_static(instr, m_src->rec, 0); + } else { + assert(false); + } } \ No newline at end of file diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 0443eb4279..635005e391 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -159,6 +159,20 @@ class IR_StaticVarAddr : public IR { const StaticObject* m_src = nullptr; }; +class IR_StaticVarLoad : public IR { + public: + IR_StaticVarLoad(const RegVal* dest, const StaticObject* 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 StaticObject* m_src = nullptr; +}; + class IR_FunctionAddr : public IR { public: IR_FunctionAddr(const RegVal* dest, FunctionEnv* src); @@ -199,6 +213,7 @@ class IR_IntegerMath : public IR { void do_codegen(emitter::ObjectGenerator* gen, const AllocationResult& allocs, emitter::IR_Record irec) override; + IntegerMathKind get_kind() const { return m_kind; } protected: IntegerMathKind m_kind; diff --git a/goalc/compiler/StaticObject.cpp b/goalc/compiler/StaticObject.cpp index 19dc920c6b..dd0ef92add 100644 --- a/goalc/compiler/StaticObject.cpp +++ b/goalc/compiler/StaticObject.cpp @@ -13,6 +13,9 @@ uint32_t push_data_to_byte_vector(T data, std::vector& v) { } } // namespace +//////////////// +// StaticString +//////////////// StaticString::StaticString(std::string data, int _seg) : text(std::move(data)), seg(_seg) {} std::string StaticString::print() const { @@ -48,4 +51,33 @@ void StaticString::generate(emitter::ObjectGenerator* gen) { int StaticString::get_addr_offset() const { return BASIC_OFFSET; +} + +//////////////// +// StaticFloat +//////////////// + +StaticFloat::StaticFloat(float _value, int _seg) : value(_value), seg(_seg) {} + +StaticObject::LoadInfo StaticFloat::get_load_info() const { + LoadInfo info; + info.requires_load = true; + info.load_size = 4; + info.load_signed = false; + info.prefer_xmm = true; + return info; +} + +void StaticFloat::generate(emitter::ObjectGenerator* gen) { + rec = gen->add_static_to_seg(seg, 4); + auto& d = gen->get_static_data(rec); + push_data_to_byte_vector(value, d); +} + +int StaticFloat::get_addr_offset() const { + return 0; +} + +std::string StaticFloat::print() const { + return fmt::format("(sf {})", value); } \ No newline at end of file diff --git a/goalc/compiler/StaticObject.h b/goalc/compiler/StaticObject.h index 34dc05b283..4b116b6cb7 100644 --- a/goalc/compiler/StaticObject.h +++ b/goalc/compiler/StaticObject.h @@ -33,4 +33,15 @@ class StaticString : public StaticObject { int get_addr_offset() const override; }; +class StaticFloat : public StaticObject { + public: + explicit StaticFloat(float _value, int _seg); + float value = 0; + int seg = -1; + std::string print() const override; + LoadInfo get_load_info() const override; + void generate(emitter::ObjectGenerator* gen) override; + int get_addr_offset() const override; +}; + #endif // JAK_STATICOBJECT_H diff --git a/goalc/compiler/Val.cpp b/goalc/compiler/Val.cpp index f1de3f0e15..889cf1cc2a 100644 --- a/goalc/compiler/Val.cpp +++ b/goalc/compiler/Val.cpp @@ -6,11 +6,14 @@ * Fallback to_gpr if a more optimized one is not provided. */ RegVal* Val::to_gpr(Env* fe) { + // TODO - handle 128-bit stuff here! auto rv = to_reg(fe); if (rv->ireg().kind == emitter::RegKind::GPR) { return rv; } else { - throw std::runtime_error("Val::to_gpr NYI"); // todo + auto re = fe->make_gpr(m_ts); + fe->emit(std::make_unique(re, rv)); + return re; } } @@ -74,4 +77,10 @@ RegVal* LambdaVal::to_reg(Env* fe) { assert(func); fe->emit(std::make_unique(re, func)); return re; +} + +RegVal* FloatConstantVal::to_reg(Env* fe) { + auto re = fe->make_xmm(m_ts); + fe->emit(std::make_unique(re, m_value)); + return re; } \ No newline at end of file diff --git a/goalc/compiler/Val.h b/goalc/compiler/Val.h index 239ee59fe0..66190abc62 100644 --- a/goalc/compiler/Val.h +++ b/goalc/compiler/Val.h @@ -141,12 +141,12 @@ class IntegerConstantVal : public Val { class FloatConstantVal : public Val { public: - FloatConstantVal(TypeSpec ts, float value) : Val(std::move(ts)), m_value(value) {} - std::string print() const override { return "float-constant-" + std::to_string(m_value); } + FloatConstantVal(TypeSpec ts, StaticFloat* value) : Val(std::move(ts)), m_value(value) {} + std::string print() const override { return "float-constant-" + m_value->print(); } RegVal* to_reg(Env* fe) override; protected: - float m_value = -1.f; + StaticFloat* m_value = nullptr; }; // IntegerConstant // FloatConstant diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index e1ade2fb5f..bfc4cc7ef9 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -68,7 +68,8 @@ static const std::unordered_map< // // // LAMBDA {"lambda", &Compiler::compile_lambda}, - // {"inline", &Compiler::compile_inline}, + {"declare", &Compiler::compile_declare}, + {"inline", &Compiler::compile_inline}, // {"with-inline", &Compiler::compile_with_inline}, // {"rlet", &Compiler::compile_rlet}, // {"mlet", &Compiler::compile_mlet}, @@ -81,11 +82,6 @@ static const std::unordered_map< {"quote", &Compiler::compile_quote}, // {"defconstant", &Compiler::compile_defconstant}, // - // {"declare", &Compiler::compile_declare}, - // - // - // - // // // OBJECT // // {"the", &Compiler::compile_the}, @@ -150,6 +146,8 @@ Val* Compiler::compile(const goos::Object& code, Env* env) { return compile_symbol(code, env); case goos::ObjectType::STRING: return compile_string(code, env); + case goos::ObjectType::FLOAT: + return compile_float(code, env); default: ice("Don't know how to compile " + code.print()); } @@ -255,4 +253,18 @@ Val* Compiler::compile_string(const std::string& str, Env* env, int seg) { auto fie = get_parent_env_of_type(env); fie->add_static(std::move(obj)); return result; +} + +Val* Compiler::compile_float(const goos::Object& code, Env* env) { + assert(code.is_float()); + return compile_float(code.float_obj.value, env, MAIN_SEGMENT); +} + +Val* Compiler::compile_float(float value, Env* env, int seg) { + auto obj = std::make_unique(value, seg); + auto fe = get_parent_env_of_type(env); + auto result = fe->alloc_val(m_ts.make_typespec("float"), obj.get()); + auto fie = get_parent_env_of_type(env); + fie->add_static(std::move(obj)); + return result; } \ No newline at end of file diff --git a/goalc/compiler/compilation/Function.cpp b/goalc/compiler/compilation/Function.cpp index 48f33ef192..5a92dcf90e 100644 --- a/goalc/compiler/compilation/Function.cpp +++ b/goalc/compiler/compilation/Function.cpp @@ -282,6 +282,7 @@ Val* Compiler::compile_function_or_method_call(const goos::Object& form, Env* en if (eval_args.empty()) { throw_compile_error(form, "0 argument method call is impossible to figure out"); } + printf("BAD %s\n", uneval_head.print().c_str()); assert(false); // nyi // head = compile_get_method_of_object(eval_args.front(), symbol_string(uneval_head), env); } @@ -359,4 +360,49 @@ Val* Compiler::compile_real_function_call(const goos::Object& form, } else { return return_reg; } -} \ No newline at end of file +} + +Val* Compiler::compile_declare(const goos::Object& form, const goos::Object& rest, Env* env) { + auto& settings = get_parent_env_of_type(env)->settings; + + if (settings.is_set) { + throw_compile_error(form, "function has multiple declares"); + } + settings.is_set = true; + + for_each_in_list(rest, [&](const goos::Object& o) { + if (!o.is_pair()) { + throw_compile_error(o, "invalid declare specification"); + } + + auto first = o.as_pair()->car; + auto rrest = o.as_pair()->cdr; + + if (!first.is_symbol()) { + throw_compile_error(first, "invalid declare specification, expected a symbol"); + } + + if (first.as_symbol()->name == "inline") { + if (!rrest.is_empty_list()) { + throw_compile_error(first, "invalid inline declare"); + } + settings.allow_inline = true; + settings.inline_by_default = true; + settings.save_code = true; + } else if (first.as_symbol()->name == "allow-inline") { + if (!rrest.is_empty_list()) { + throw_compile_error(first, "invalid allow-inline declare"); + } + settings.allow_inline = true; + settings.inline_by_default = false; + settings.save_code = true; + } else if (first.as_symbol()->name == "asm-func") { + get_parent_env_of_type(env)->is_asm_func = true; + } + + else { + throw_compile_error(first, "unrecognized declare statement"); + } + }); + return get_none(); +} diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index 8a9f16689a..d9bba8916f 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -39,6 +39,7 @@ bool Compiler::is_singed_integer_or_binteger(const TypeSpec& ts) { } Val* Compiler::number_to_integer(Val* in, Env* env) { + (void)env; auto ts = in->type(); if (is_binteger(ts)) { assert(false); @@ -52,6 +53,7 @@ Val* Compiler::number_to_integer(Val* in, Env* env) { } Val* Compiler::number_to_binteger(Val* in, Env* env) { + (void)env; auto ts = in->type(); if (is_binteger(ts)) { return in; @@ -65,6 +67,7 @@ Val* Compiler::number_to_binteger(Val* in, Env* env) { } Val* Compiler::number_to_float(Val* in, Env* env) { + (void)env; auto ts = in->type(); if (is_binteger(ts)) { assert(false); diff --git a/test/test_compiler_and_runtime.cpp b/test/test_compiler_and_runtime.cpp index 0a731e9488..882bad524d 100644 --- a/test/test_compiler_and_runtime.cpp +++ b/test/test_compiler_and_runtime.cpp @@ -133,6 +133,48 @@ TEST(CompilerAndRuntime, BuildGame) { runtime_thread.join(); } +// TODO -move these into another file? +TEST(CompilerAndRuntime, InlineIsInline) { + Compiler compiler; + auto code = + compiler.get_goos().reader.read_from_file({"goal_src", "test", "test-declare-inline.gc"}); + auto compiled = compiler.compile_object_file("test-code", code, true); + EXPECT_EQ(compiled->functions().size(), 2); + auto& ir = compiled->top_level_function().code(); + bool got_mult = false; + for (auto& x : ir) { + EXPECT_EQ(dynamic_cast(x.get()), nullptr); + auto as_im = dynamic_cast(x.get()); + if (as_im) { + EXPECT_EQ(as_im->get_kind(), IntegerMathKind::IMUL_32); + got_mult = true; + } + } + EXPECT_TRUE(got_mult); +} + +TEST(CompilerAndRuntime, AllowInline) { + Compiler compiler; + auto code = + compiler.get_goos().reader.read_from_file({"goal_src", "test", "test-inline-call.gc"}); + auto compiled = compiler.compile_object_file("test-code", code, true); + EXPECT_EQ(compiled->functions().size(), 2); + auto& ir = compiled->top_level_function().code(); + int got_mult = 0; + int got_call = 0; + for (auto& x : ir) { + if (dynamic_cast(x.get())) { + got_call++; + } + auto as_im = dynamic_cast(x.get()); + if (as_im && as_im->get_kind() == IntegerMathKind::IMUL_32) { + got_mult++; + } + } + EXPECT_EQ(got_mult, 1); + EXPECT_EQ(got_call, 1); +} + TEST(CompilerAndRuntime, CompilerTests) { std::thread runtime_thread([]() { exec_runtime(0, nullptr); }); Compiler compiler; @@ -184,6 +226,14 @@ TEST(CompilerAndRuntime, CompilerTests) { runner.run_test("test-sub-2.gc", {"4\n"}); runner.run_test("test-mul-1.gc", {"-12\n"}); + expected = "test-string"; + runner.run_test("test-string-symbol.gc", {expected}, expected.size()); + runner.run_test("test-declare-inline.gc", {"32\n"}); + runner.run_test("test-inline-call.gc", {"44\n"}); + + // float + runner.run_test("test-floating-point-1.gc", {"1067316150\n"}); + compiler.shutdown_target(); runtime_thread.join(); runner.print_summary(); From f46acdf87a6a3061828dc153f196e0588c7ec1db Mon Sep 17 00:00:00 2001 From: water Date: Sun, 13 Sep 2020 12:11:49 -0400 Subject: [PATCH 35/50] what is going on with clang format --- goalc/compiler/Compiler.h | 1 + goalc/compiler/IR.h | 18 +++++++++++++++++ goalc/compiler/compilation/Atoms.cpp | 2 +- goalc/compiler/compilation/Math.cpp | 29 ++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index d77bd0df34..2dbfb5386b 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -136,6 +136,7 @@ class Compiler { Val* compile_add(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_sub(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_mul(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_div(const goos::Object& form, const goos::Object& rest, Env* env); // Function Val* compile_lambda(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 635005e391..cd9a59ae64 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -221,4 +221,22 @@ class IR_IntegerMath : public IR { RegVal* m_arg; }; +enum class FloatMathKind { DIV_SS }; + +class IR_FloatMath : public IR { + public: + IR_FloatMath(FloatMathKind kind, RegVal* dest, RegVal* arg); + std::string print() override; + RegAllocInstr to_rai() override; + void do_codegen(emitter::ObjectGenerator* gen, + const AllocationResult& allocs, + emitter::IR_Record irec) override; + FloatMathKind get_kind() const { return m_kind; } + + protected: + FloatMathKind m_kind; + RegVal* m_dest; + RegVal* m_arg; +}; + #endif // JAK_IR_H diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index bfc4cc7ef9..e66d14065b 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -101,7 +101,7 @@ static const std::unordered_map< {"+", &Compiler::compile_add}, {"-", &Compiler::compile_sub}, {"*", &Compiler::compile_mul}, - // {"/", &Compiler::compile_divide}, + {"/", &Compiler::compile_div}, // {"shlv", &Compiler::compile_shlv}, // {"shrv", &Compiler::compile_shrv}, // {"sarv", &Compiler::compile_sarv}, diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index d9bba8916f..2fa8634058 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -203,4 +203,33 @@ Val* Compiler::compile_sub(const goos::Object& form, const goos::Object& rest, E } assert(false); return get_none(); +} + +Val* Compiler::compile_div(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.size() != 2) { + throw_compile_error(form, "Invalid / form"); + } + + auto first_val = compile_error_guard(args.unnamed.at(0), env); + auto first_type = first_val->type(); + auto math_type = get_math_mode(first_type); + switch (math_type) { + case MATH_FLOAT: + + { + auto result = env->make_xmm(first_type); + env->emit(std::make_unique(result, first_val->to_xmm(env))); + env->emit(std::make_unique(FloatMathKind::DIV_SS, result, to_math_type(compile_error_guard(args.unnamed.at(1), env), math_type, env)->to_xmm(env)))); + } + + case MATH_INVALID: + throw_compile_error( + form, "Cannot determine the math mode for object of type " + first_type.print()); + break; + default: + assert(false); + } + assert(false); + return get_none(); } \ No newline at end of file From 9ec9b5a22a03d126795e36c3aa288be5d7ea03f2 Mon Sep 17 00:00:00 2001 From: water Date: Sun, 13 Sep 2020 17:34:02 -0400 Subject: [PATCH 36/50] add conditional stuff --- game/overlord/iso.cpp | 5 +- goal_src/goal-lib.gc | 58 ++++++ goal_src/kernel-defs.gc | 2 +- goal_src/kernel/gcommon.gc | 72 ++++++- goal_src/test/test-build-game.gc | 6 +- goal_src/test/test-defsmacro-defgmacro.gc | 13 ++ goal_src/test/test-desfun.gc | 10 + goal_src/test/test-div-1.gc | 1 + goal_src/test/test-div-2.gc | 3 + goal_src/test/test-factorial-loop.gc | 11 + goal_src/test/test-factorial-recursive.gc | 12 ++ goal_src/test/test-mlet.gc | 7 + goal_src/test/test-protect.gc | 15 ++ goal_src/test/test-set-symbol.gc | 18 ++ goal_src/test/test-three-reg-add.gc | 3 + goal_src/test/test-three-reg-mult.gc | 6 + goal_src/test/test-three-reg-sub.gc | 5 + goalc/CMakeLists.txt | 1 + goalc/compiler/Compiler.h | 9 + goalc/compiler/Env.cpp | 17 +- goalc/compiler/Env.h | 29 ++- goalc/compiler/IR.cpp | 176 +++++++++++++++- goalc/compiler/IR.h | 29 +++ goalc/compiler/Util.cpp | 4 + goalc/compiler/Val.cpp | 16 +- goalc/compiler/compilation/Atoms.cpp | 34 ++-- goalc/compiler/compilation/ControlFlow.cpp | 225 +++++++++++++++++++++ goalc/compiler/compilation/Define.cpp | 39 ++++ goalc/compiler/compilation/Macro.cpp | 18 ++ goalc/compiler/compilation/Math.cpp | 27 ++- goalc/main.cpp | 2 +- test/test_compiler_and_runtime.cpp | 14 ++ 32 files changed, 843 insertions(+), 44 deletions(-) create mode 100644 goal_src/test/test-defsmacro-defgmacro.gc create mode 100644 goal_src/test/test-desfun.gc create mode 100644 goal_src/test/test-div-1.gc create mode 100644 goal_src/test/test-div-2.gc create mode 100644 goal_src/test/test-factorial-loop.gc create mode 100644 goal_src/test/test-factorial-recursive.gc create mode 100644 goal_src/test/test-mlet.gc create mode 100644 goal_src/test/test-protect.gc create mode 100644 goal_src/test/test-set-symbol.gc create mode 100644 goal_src/test/test-three-reg-add.gc create mode 100644 goal_src/test/test-three-reg-mult.gc create mode 100644 goal_src/test/test-three-reg-sub.gc create mode 100644 goalc/compiler/compilation/ControlFlow.cpp diff --git a/game/overlord/iso.cpp b/game/overlord/iso.cpp index b374e3388e..17424022be 100644 --- a/game/overlord/iso.cpp +++ b/game/overlord/iso.cpp @@ -597,8 +597,9 @@ u32 RunDGOStateMachine(IsoMessage* _cmd, IsoBufferHeader* buffer) { // once we're done, send the header to the EE, and start reading object data if (cmd->bytes_processed == sizeof(ObjectHeader)) { - printf("[Overlord DGO] Got object header for %s, object size 0x%x bytes (sent to 0x%p)\n", - cmd->objHeader.name, cmd->objHeader.size, cmd->ee_destination_buffer); + // printf("[Overlord DGO] Got object header for %s, object size 0x%x bytes (sent + // to 0x%p)\n", + // cmd->objHeader.name, cmd->objHeader.size, cmd->ee_destination_buffer); DMA_SendToEE(&cmd->objHeader, sizeof(ObjectHeader), cmd->ee_destination_buffer); DMA_Sync(); cmd->ee_destination_buffer += sizeof(ObjectHeader); diff --git a/goal_src/goal-lib.gc b/goal_src/goal-lib.gc index 77ae4e31fb..aaf565df3b 100644 --- a/goal_src/goal-lib.gc +++ b/goal_src/goal-lib.gc @@ -33,6 +33,14 @@ ) ) +(defmacro blg () + `(begin + (build-game) + (dgo-load "kernel" global #xf #x200000) + (dgo-load "game" global #xf #x200000) + ) + ) + (defmacro e () `(:exit) ) @@ -109,4 +117,54 @@ ;; otherwise don't ignore it. `(define ,name (lambda :name ,name ,bindings ,@body)) ) + ) + +(defmacro while (test &rest body) + (with-gensyms (reloop test-exit) + `(begin + (goto ,test-exit) + (label ,reloop) + ,@body + (label ,test-exit) + (when-goto ,test ,reloop) + #f + ) + ) + ) + +;; Backup some values, and restore after executing body. +;; Non-dynamic (nonlocal jumps out of body will skip restore) +(defmacro protect (defs &rest body) + (if (null? defs) + ;; nothing to backup, just insert body (base case) + `(begin ,@body) + + ;; a unique name for the thing we are backing up + (with-gensyms (backup) + ;; store the original value of the first def in backup + `(let ((,backup ,(first defs))) + ;; backup any other things which need backing up + (protect ,(cdr defs) + ;; execute the body + ,@body + ) + ;; restore the first thing + (set! ,(first defs) ,backup) + ) + ) + ) + ) + +(defmacro +! (place amount) + `(set! ,place (+ ,place ,amount)) + ) + +;; todo, handle too many arguments correct +(defmacro if (condition true-case &rest others) + (if (null? others) + `(cond (,condition ,true-case)) + `(cond (,condition ,true-case) + (else ,(first others)) + ) + ) ) \ No newline at end of file diff --git a/goal_src/kernel-defs.gc b/goal_src/kernel-defs.gc index 3c97a5608d..862d2c7ff9 100644 --- a/goal_src/kernel-defs.gc +++ b/goal_src/kernel-defs.gc @@ -77,7 +77,7 @@ (define-extern new-dynamic-structure (function kheap type int structure)) (define-extern method-set! (function type int function none)) ;; may actually return function. (define-extern link (function pointer string int kheap int pointer)) -(define-extern dgo-load (function string kheap int int)) +(define-extern dgo-load (function string kheap int int none)) (define-extern link-begin (function pointer string int kheap int int)) (define-extern link-resume (function int)) ;; mc-run diff --git a/goal_src/kernel/gcommon.gc b/goal_src/kernel/gcommon.gc index 057dfb5d3b..74b27d1feb 100644 --- a/goal_src/kernel/gcommon.gc +++ b/goal_src/kernel/gcommon.gc @@ -18,7 +18,77 @@ ;; there is an optional "docstring" that can go at the beginning of a function "Function which returns its input. The first function of the game!" - ;; the last thing in the function body is the return value. + ;; the last thing in the function body is the return value. This is like "return x;" in C + ;; the return type of the function is figured out automatically by the compiler + ;; you don't have to specify it manually. x ) + +(defun 1/ ((x float)) + "Reciprocal floating point" + + ;; this function computes 1.0 / x. GOAL allows strange function names like "1/". + + ;; Declaring this an inline function is like a C inline function, however code is + ;; still generated so it can be used a function object. GOAL inline functions have type + ;; checking, so they are preferable to macros when possible, to get better error messages. + (declare (inline)) + + ;; the division form will pick the math type (float, int) based on the type of the first + ;; argument. In this case, "1." is a floating point constant, so this becomes a floating point division. + (/ 1. x) + ) + +(defun + ((x int) (y int)) + "Compute the sum of two integers" + + ;; this wraps the compiler's built-in handling of "add two integers" in a GOAL function. + ;; now "+" can be used as a function object, but is limited to adding two integers when used like this. + ;; The compiler is smart enough to not use this function unless "+" is being used as a function object. + ;; ex: (+ a b c), (+ a b) ; won't use this function, uses built-in addition + ;; (set-combination-function! my-thing +) ; + becomes a function pointer in this case + (+ x y) + ) + +(defun - ((x int) (y int)) + "Compute the difference of two integers" + (- x y) + ) + +(defun * ((x int) (y int)) + "Compute the product of two integers" + ;; TODO - verify that this matches the PS2 exactly. + ;; Uses mult (three operand form) in MIPS + (* x y) + ) + +(defun / ((x int) (y int)) + "Compute the quotient of two integers" + ;; TODO - verify this matches the PS2 exactly + (/ x y) + ) + +;; todo ash +;; todo mod +;; todo rem + +(defun abs ((a int)) + "Take the absolute value of an integer" + + ;; short function, good candidate for inlining + (declare (inline)) + + ;; The original implementation was inline assembly, to take advantage of branch delay slots: + ;; (or v0 a0 r0) ;; move input to output unchanged, for positive case + ;; (bltzl v0 end) ;; if negative, execute the branch delay slot below... + ;; (dsubu v0 r0 v0) ;; negate + ;; (label end) + + + (if (> a 0) ;; condition is "a > 0" + a ;; true case, return a + (- a) ;; false case, return -a. (- a) is like (- 0 a) + ) + ) + diff --git a/goal_src/test/test-build-game.gc b/goal_src/test/test-build-game.gc index 83f28a46fb..fe473f2e15 100644 --- a/goal_src/test/test-build-game.gc +++ b/goal_src/test/test-build-game.gc @@ -1,6 +1,4 @@ (build-game) -;(define-extern global kheap) -;(define-extern dgo-load (function string kheap int int int)) -;(dgo-load "kernel" global #xf #x200000) ; todo, remove once kernel loads itself. -;(dgo-load "game" global #xf #x200000) +(dgo-load "kernel" global #xf #x200000) ; todo, remove once kernel loads itself. +(dgo-load "game" global #xf #x200000) 1 \ No newline at end of file diff --git a/goal_src/test/test-defsmacro-defgmacro.gc b/goal_src/test/test-defsmacro-defgmacro.gc new file mode 100644 index 0000000000..8917115af9 --- /dev/null +++ b/goal_src/test/test-defsmacro-defgmacro.gc @@ -0,0 +1,13 @@ +(defsmacro test-macro (x) + ;; goos macro + `(+ ,x 2) + ) + +(defmacro test-macro (x) + ;; goal macro which calls a goos macro of the same name + (let ((goos-expansion (test-macro x))) + `(+ ,goos-expansion 3) + ) + ) + +(test-macro 15) \ No newline at end of file diff --git a/goal_src/test/test-desfun.gc b/goal_src/test/test-desfun.gc new file mode 100644 index 0000000000..5e8f8f100f --- /dev/null +++ b/goal_src/test/test-desfun.gc @@ -0,0 +1,10 @@ +(desfun square (x) + (* x x) + ) + + +(defmacro call-square (x) + (square x) + ) + +(call-square 2) \ No newline at end of file diff --git a/goal_src/test/test-div-1.gc b/goal_src/test/test-div-1.gc new file mode 100644 index 0000000000..d8b1fc6cb5 --- /dev/null +++ b/goal_src/test/test-div-1.gc @@ -0,0 +1 @@ +(+ 1 2 (/ (* 2 5) (+ 1 2))) \ No newline at end of file diff --git a/goal_src/test/test-div-2.gc b/goal_src/test/test-div-2.gc new file mode 100644 index 0000000000..bb66062495 --- /dev/null +++ b/goal_src/test/test-div-2.gc @@ -0,0 +1,3 @@ +(let ((x 30)) + (+ (/ x 10) 4) + ) \ No newline at end of file diff --git a/goal_src/test/test-factorial-loop.gc b/goal_src/test/test-factorial-loop.gc new file mode 100644 index 0000000000..5eac59b436 --- /dev/null +++ b/goal_src/test/test-factorial-loop.gc @@ -0,0 +1,11 @@ +(defun factorial-iterative ((x integer)) + (let ((result 1)) + (while (!= x 1) + (set! result (* result x)) + (set! x (- x 1)) + ) + result + ) + ) + +(factorial-iterative 10) \ No newline at end of file diff --git a/goal_src/test/test-factorial-recursive.gc b/goal_src/test/test-factorial-recursive.gc new file mode 100644 index 0000000000..3909817669 --- /dev/null +++ b/goal_src/test/test-factorial-recursive.gc @@ -0,0 +1,12 @@ +;; for now, recursive functions need to forward declare so they have their +;; return type. +;(defun-extern factorial-recursive ((x integer)) integer) +(define-extern factorial-recursive (function integer integer)) + +(defun factorial-recursive ((x integer)) + (cond ((= x 1) x) + (else (* x (factorial-recursive (- x 1)))) + ) + ) + +(factorial-recursive 10) \ No newline at end of file diff --git a/goal_src/test/test-mlet.gc b/goal_src/test/test-mlet.gc new file mode 100644 index 0000000000..0b034d8cde --- /dev/null +++ b/goal_src/test/test-mlet.gc @@ -0,0 +1,7 @@ +(defglobalconstant c1 3) + +(+ c1 (mlet ((c1 4)) + c1 + ) + c1 + ) diff --git a/goal_src/test/test-protect.gc b/goal_src/test/test-protect.gc new file mode 100644 index 0000000000..7c7025e243 --- /dev/null +++ b/goal_src/test/test-protect.gc @@ -0,0 +1,15 @@ +(let ((var1 10) + (var2 20) + (sum 0) + ) + + (protect (var1 var2) + (set! var1 1) + (set! var2 2) + (+! sum var1) + (+! sum var2) + ) + (+! sum var1) + (+! sum var2) + sum + ) diff --git a/goal_src/test/test-set-symbol.gc b/goal_src/test/test-set-symbol.gc new file mode 100644 index 0000000000..910d3eef60 --- /dev/null +++ b/goal_src/test/test-set-symbol.gc @@ -0,0 +1,18 @@ + +(defun burp ((thing integer)) + (* thing 3) + ) + +(define thing 2) + +(set! thing 3) + + +(+ (let ((thing 4)) + (set! thing (+ thing 1)) + thing + ) + (burp thing) + (set! thing 4) + thing + ) diff --git a/goal_src/test/test-three-reg-add.gc b/goal_src/test/test-three-reg-add.gc new file mode 100644 index 0000000000..16d3aa5265 --- /dev/null +++ b/goal_src/test/test-three-reg-add.gc @@ -0,0 +1,3 @@ +(let ((x 3)) + (+ (+ x 1) x) + ) \ No newline at end of file diff --git a/goal_src/test/test-three-reg-mult.gc b/goal_src/test/test-three-reg-mult.gc new file mode 100644 index 0000000000..5dc9a1ce43 --- /dev/null +++ b/goal_src/test/test-three-reg-mult.gc @@ -0,0 +1,6 @@ + +(let ((x 3)) + (* x 4) + (* x) + x + ) diff --git a/goal_src/test/test-three-reg-sub.gc b/goal_src/test/test-three-reg-sub.gc new file mode 100644 index 0000000000..994d0619cc --- /dev/null +++ b/goal_src/test/test-three-reg-sub.gc @@ -0,0 +1,5 @@ +(let ((x 3)) + (- x 1) + (- x) + x + ) diff --git a/goalc/CMakeLists.txt b/goalc/CMakeLists.txt index 0c9ba1f0f4..3c309bec06 100644 --- a/goalc/CMakeLists.txt +++ b/goalc/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(compiler compiler/compilation/Math.cpp compiler/compilation/Define.cpp compiler/compilation/Function.cpp + compiler/compilation/ControlFlow.cpp compiler/Util.cpp logger/Logger.cpp regalloc/IRegister.cpp diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 2dbfb5386b..2241a52e35 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -99,6 +99,7 @@ class Compiler { Val* number_to_float(Val* in, Env* env); Val* number_to_binteger(Val* in, Env* env); Val* to_math_type(Val* in, MathMode mode, Env* env); + bool is_none(Val* in); public: // Atoms @@ -123,14 +124,22 @@ class Compiler { Val* compile_in_package(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_build_dgo(const goos::Object& form, const goos::Object& rest, Env* env); + // ControlFlow + Condition compile_condition(const goos::Object& condition, Env* env, bool invert); + Val* compile_condition_as_bool(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_when_goto(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_cond(const goos::Object& form, const goos::Object& rest, Env* env); + // Define Val* compile_define(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_define_extern(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_set(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); Val* compile_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_mlet(const goos::Object& form, const goos::Object& rest, Env* env); // Math Val* compile_add(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/Env.cpp b/goalc/compiler/Env.cpp index 07ebf17c30..1e2b5ebd0a 100644 --- a/goalc/compiler/Env.cpp +++ b/goalc/compiler/Env.cpp @@ -31,7 +31,7 @@ void Env::constrain_reg(IRegConstraint constraint) { /*! * Lookup the given symbol object as a lexical variable. */ -Val* Env::lexical_lookup(goos::Object sym) { +RegVal* Env::lexical_lookup(goos::Object sym) { return m_parent->lexical_lookup(std::move(sym)); } @@ -93,7 +93,7 @@ void GlobalEnv::constrain_reg(IRegConstraint constraint) { /*! * Lookup the given symbol object as a lexical variable. */ -Val* GlobalEnv::lexical_lookup(goos::Object sym) { +RegVal* GlobalEnv::lexical_lookup(goos::Object sym) { (void)sym; return nullptr; } @@ -218,6 +218,15 @@ void FunctionEnv::resolve_gotos() { } gt.ir->resolve(&kv_label->second); } + + for (auto& gt : unresolved_cond_gotos) { + auto kv_label = m_labels.find(gt.label); + if (kv_label == m_labels.end()) { + throw std::runtime_error("invalid when-goto destination " + gt.label); + } + gt.ir->label = kv_label->second; + gt.ir->mark_as_resolved(); + } } RegVal* FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) { @@ -238,7 +247,7 @@ std::unordered_map& LabelEnv::get_label_map() { return m_labels; } -Val* FunctionEnv::lexical_lookup(goos::Object sym) { +RegVal* FunctionEnv::lexical_lookup(goos::Object sym) { if (!sym.is_symbol()) { throw std::runtime_error("invalid symbol in lexical_lookup"); } @@ -259,7 +268,7 @@ std::string LexicalEnv::print() { return "lexical"; } -Val* LexicalEnv::lexical_lookup(goos::Object sym) { +RegVal* LexicalEnv::lexical_lookup(goos::Object sym) { if (!sym.is_symbol()) { throw std::runtime_error("invalid symbol in lexical_lookup"); } diff --git a/goalc/compiler/Env.h b/goalc/compiler/Env.h index a9fc91c03c..7f522bb3ac 100644 --- a/goalc/compiler/Env.h +++ b/goalc/compiler/Env.h @@ -31,7 +31,7 @@ class Env { virtual void emit(std::unique_ptr ir); virtual RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind); virtual void constrain_reg(IRegConstraint constraint); // todo, remove! - virtual Val* lexical_lookup(goos::Object sym); + virtual RegVal* 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); @@ -54,7 +54,7 @@ class GlobalEnv : public Env { 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; + RegVal* lexical_lookup(goos::Object sym) override; BlockEnv* find_block(const std::string& name) override; ~GlobalEnv() = default; @@ -128,7 +128,14 @@ class DeclareEnv : public Env { class IR_GotoLabel; struct UnresolvedGoto { - IR_GotoLabel* ir; + IR_GotoLabel* ir = nullptr; + std::string label; +}; + +class IR_ConditionalBranch; + +struct UnresolvedConditionalGoto { + IR_ConditionalBranch* ir = nullptr; std::string label; }; @@ -146,13 +153,18 @@ class FunctionEnv : public DeclareEnv { const std::vector& constraints() { return m_constraints; } void constrain(const IRegConstraint& c) { m_constraints.push_back(c); } void set_allocations(const AllocationResult& result) { m_regalloc_result = result; } - Val* lexical_lookup(goos::Object sym) override; + RegVal* lexical_lookup(goos::Object sym) override; const AllocationResult& alloc_result() { return m_regalloc_result; } bool needs_aligned_stack() const { return m_aligned_stack_required; } void require_aligned_stack() { m_aligned_stack_required = true; } + Label* alloc_unnamed_label() { + m_unnamed_labels.emplace_back(std::make_unique