integer constant program working up to ir

This commit is contained in:
water
2020-09-06 12:45:31 -04:00
parent 2b67b1078f
commit 8bf0bd86d3
20 changed files with 1142 additions and 34 deletions
+10 -6
View File
@@ -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?
+78
View File
@@ -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
View File
@@ -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
View File
@@ -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");
}
+36
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+10
View File
@@ -0,0 +1,10 @@
#ifndef JAK_LABEL_H
#define JAK_LABEL_H
struct Label {
};
#endif // JAK_LABEL_H
+96
View File
@@ -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
View File
@@ -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;
}
+13
View File
@@ -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
+177
View File
@@ -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);
}
+16
View File
@@ -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();
}
+5
View File
@@ -1,2 +1,7 @@
add_library(listener SHARED
Listener.cpp)
IF (WIN32)
#
ELSE ()
target_link_libraries(listener pthread)
ENDIF ()
+94
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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;
}