mirror of
https://github.com/open-goal/jak-project
synced 2026-06-02 18:19:07 -04:00
integer constant program working up to ir
This commit is contained in:
@@ -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?
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef JAK_BINARYWRITER_H
|
||||
#define JAK_BINARYWRITER_H
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
struct BinaryWriterRef {
|
||||
size_t offset;
|
||||
size_t write_size;
|
||||
};
|
||||
|
||||
class BinaryWriter {
|
||||
public:
|
||||
BinaryWriter() = default;
|
||||
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
void add_at_ref(const T& obj, const BinaryWriterRef& ref) {
|
||||
assert(ref.write_size == sizeof(T));
|
||||
assert(ref.offset + ref.write_size < get_size());
|
||||
memcpy(data.data() + ref.offset, &obj, sizeof(T));
|
||||
}
|
||||
|
||||
void add_str_len(const std::string& str, size_t len) {
|
||||
add_cstr_len(str.c_str(), len);
|
||||
}
|
||||
|
||||
void add_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<uint8_t> data;
|
||||
};
|
||||
|
||||
#endif // JAK_BINARYWRITER_H
|
||||
+15
-8
@@ -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)
|
||||
|
||||
+107
-3
@@ -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<GlobalEnv>();
|
||||
m_none = std::make_unique<None>(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;
|
||||
}
|
||||
}
|
||||
|
||||
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<FunctionEnv> Compiler::compile_top_level_function(const std::string& name,
|
||||
const goos::Object& code,
|
||||
Env* env) {
|
||||
auto fe = std::make_unique<FunctionEnv>(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<const None*>(result)) {
|
||||
fe->emit(std::make_unique<IR_Return>(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");
|
||||
}
|
||||
|
||||
@@ -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<FunctionEnv> 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<void (const goos::Object&)>& 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<util::MatchParam<goos::ObjectType>>& unnamed,
|
||||
const std::unordered_map<std::string, std::pair<bool, util::MatchParam<goos::ObjectType>>>&
|
||||
named);
|
||||
|
||||
TypeSystem m_ts;
|
||||
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
|
||||
std::unique_ptr<None> 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
|
||||
|
||||
+185
-2
@@ -1,3 +1,186 @@
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include "Env.h"
|
||||
|
||||
///////////////////
|
||||
// Env
|
||||
///////////////////
|
||||
|
||||
/*!
|
||||
* Emit IR into the function currently being compiled.
|
||||
*/
|
||||
void Env::emit(std::unique_ptr<IR> 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> 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<FileEnv>(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> 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<FunctionEnv> fe) {
|
||||
m_functions.push_back(std::move(fe));
|
||||
}
|
||||
|
||||
void FileEnv::add_top_level_function(std::unique_ptr<FunctionEnv> 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<NoEmitEnv>(this);
|
||||
return m_no_emit_env.get();
|
||||
}
|
||||
|
||||
void FileEnv::debug_print_tl() {
|
||||
if(m_top_level_func) {
|
||||
for(auto& code : m_top_level_func->code()) {
|
||||
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) {
|
||||
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<RegVal>(ireg, ts);
|
||||
m_iregs.push_back(std::move(rv));
|
||||
return m_iregs.back().get();
|
||||
}
|
||||
+189
-6
@@ -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 <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#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> 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> 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<std::unique_ptr<FileEnv>> 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> 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<FunctionEnv> fe);
|
||||
void add_top_level_function(std::unique_ptr<FunctionEnv> fe);
|
||||
NoEmitEnv* add_no_emit_env();
|
||||
void debug_print_tl();
|
||||
|
||||
// todo - is_empty
|
||||
~FileEnv() = default;
|
||||
|
||||
protected:
|
||||
std::string m_name;
|
||||
std::vector<std::unique_ptr<FunctionEnv>> m_functions;
|
||||
std::unique_ptr<NoEmitEnv> 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> ir) override;
|
||||
void finish();
|
||||
RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind) override;
|
||||
const std::vector<std::unique_ptr<IR>>& code() { return m_code; }
|
||||
|
||||
template <typename T, class... Args>
|
||||
T* alloc_val(Args&&... args) {
|
||||
std::unique_ptr<T> new_obj = std::make_unique<T>(std::forward<Args>(args)...);
|
||||
m_vals.push_back(std::move(new_obj));
|
||||
return (T*)m_vals.back().get();
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
std::string m_name;
|
||||
std::vector<std::unique_ptr<IR>> m_code;
|
||||
std::vector<std::unique_ptr<RegVal>> m_iregs;
|
||||
std::vector<std::unique_ptr<Val>> m_vals;
|
||||
std::vector<IRegConstraint> 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<std::string, Env*> 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<TypeSpec> m_return_types;
|
||||
};
|
||||
|
||||
class LexicalEnv : public Env {
|
||||
public:
|
||||
LexicalEnv(Env* parent);
|
||||
std::string print() override;
|
||||
};
|
||||
|
||||
class LabelEnv : public Env {
|
||||
public:
|
||||
protected:
|
||||
std::unordered_map<std::string, Label> m_labels;
|
||||
};
|
||||
|
||||
class WithInlineEnv : public Env {
|
||||
|
||||
};
|
||||
|
||||
class SymbolMacroEnv : public Env {
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T* get_parent_env_of_type(Env* in) {
|
||||
for(;;) {
|
||||
auto attempt = dynamic_cast<T*>(in);
|
||||
if(attempt) return attempt;
|
||||
if(dynamic_cast<GlobalEnv*>(in)) {
|
||||
return nullptr;
|
||||
}
|
||||
in = in->parent();
|
||||
}
|
||||
}
|
||||
// function
|
||||
// block
|
||||
// lexical
|
||||
|
||||
+41
-2
@@ -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<IRegConstraint>* constraints, int my_id) {
|
||||
IRegConstraint c;
|
||||
if(dynamic_cast<const None*>(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;
|
||||
}
|
||||
+33
-2
@@ -4,18 +4,49 @@
|
||||
#include <string>
|
||||
#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<IRegConstraint>* 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<IRegConstraint>* 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
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
#ifndef JAK_LABEL_H
|
||||
#define JAK_LABEL_H
|
||||
|
||||
struct Label {
|
||||
|
||||
};
|
||||
|
||||
#endif // JAK_LABEL_H
|
||||
@@ -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<util::MatchParam<goos::ObjectType>>& unnamed,
|
||||
const std::unordered_map<std::string, std::pair<bool, util::MatchParam<goos::ObjectType>>>&
|
||||
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<void(const goos::Object&)>& 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");
|
||||
}
|
||||
}
|
||||
+13
-2
@@ -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<IR_LoadConstant64>(rv, m_value));
|
||||
return rv;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
/*!
|
||||
* Main table for compiler forms
|
||||
*/
|
||||
static const std::unordered_map<std::string,
|
||||
Val* (Compiler::*)(const goos::Object& form, const goos::Object& rest, Env* env)> goal_forms =
|
||||
{
|
||||
// // inline asm
|
||||
// {".ret", &Compiler::compile_asm},
|
||||
// {".push", &Compiler::compile_asm},
|
||||
// {".pop", &Compiler::compile_asm},
|
||||
// {".jmp", &Compiler::compile_asm},
|
||||
// {".sub", &Compiler::compile_asm},
|
||||
// {".ret-reg", &Compiler::compile_asm},
|
||||
//
|
||||
// // BLOCK FORMS
|
||||
{"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<FunctionEnv>(env);
|
||||
return fe->alloc_val<IntegerConstantVal>(m_ts.make_typespec("int"), value);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,2 +1,7 @@
|
||||
add_library(listener SHARED
|
||||
Listener.cpp)
|
||||
IF (WIN32)
|
||||
#
|
||||
ELSE ()
|
||||
target_link_libraries(listener pthread)
|
||||
ENDIF ()
|
||||
@@ -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 <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#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<uint8_t> &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
|
||||
|
||||
@@ -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<uint8_t> &code);
|
||||
bool most_recent_send_was_acked() {
|
||||
return got_ack;
|
||||
}
|
||||
|
||||
private:
|
||||
void send_buffer(int sz);
|
||||
bool wait_for_ack();
|
||||
|
||||
|
||||
char* m_buffer = nullptr; //! buffer for incoming messages
|
||||
bool m_connected = false; //! do we think we are connected?
|
||||
bool receive_thread_running = false; //! is the receive thread unjoined?
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
#include <cstdio>
|
||||
#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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user