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
This commit is contained in:
water111 2020-09-12 13:11:42 -04:00 committed by GitHub
parent de5aa7e5e4
commit d56540f8c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1139 additions and 93 deletions

View file

@ -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;

View file

@ -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

View file

@ -143,13 +143,15 @@ void KernelCheckAndDispatch() {
call_goal(Ptr<Function>(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<u8>(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<Function>(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

View file

@ -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);
}

View file

@ -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);

View file

@ -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;

View file

@ -118,7 +118,7 @@ void SystemThreadInterface::initialization_complete() {
std::unique_lock<std::mutex> mlk(thread.initialization_mutex);
thread.initialization_complete = true;
thread.initialization_cv.notify_all();
printf(" OK\n");
printf("# %s initialized\n", thread.name.c_str());
}
/*!

View file

@ -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))
)
)

View file

@ -0,0 +1 @@
((lambda :inline-only #t (x y z) y) 1 2 3)

View file

@ -0,0 +1,10 @@
(defun return-13 ()
13)
(defun return-12 ()
12)
(defun return-11 ()
11)
(return-12)

View file

@ -0,0 +1,8 @@
(define my-number 36)
(defun return-my-number ()
my-number)
(define my-number 42)
(return-my-number)

View file

@ -0,0 +1,4 @@
(defun return-second-arg (one two three)
two)
(return-second-arg 1 23 4)

View file

@ -0,0 +1,4 @@
(let ((x 1)
(y (test-function 1 2 3 4))
(z 3))
y)

View file

@ -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*

View file

@ -0,0 +1,8 @@
(defun r2 (one two three)
two)
(defun r1 (one two three)
one)
(r2 1 (r1 2 3 4) 5)

View file

@ -0,0 +1,2 @@
(define-extern test-function (function int int int int int))
(test-function 1 2 3 4)

View file

@ -0,0 +1,5 @@
(define-extern inspect (function object object))
(inspect "this is a string")
(define x "this is also a string")
(inspect x)

View file

@ -0,0 +1,2 @@
(define-extern print (function object object))
(print "test string!")

View file

@ -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

View file

@ -9,17 +9,29 @@ constexpr int XMM_SIZE = 16;
CodeGenerator::CodeGenerator(FileEnv* env) : m_fe(env) {}
std::vector<u8> 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();

View file

@ -12,7 +12,7 @@ class CodeGenerator {
std::vector<u8> run();
private:
void do_function(FunctionEnv* env);
void do_function(FunctionEnv* env, int f_idx);
emitter::ObjectGenerator m_gen;
FileEnv* m_fe;
};

View file

@ -10,6 +10,7 @@ using namespace goos;
Compiler::Compiler() {
init_logger();
init_settings();
m_ts.add_builtin_types();
m_global_env = std::make_unique<GlobalEnv>();
m_none = std::make_unique<None>(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));
}

View file

@ -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<u8> 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<RegVal*>& args,
Env* env);
TypeSystem m_ts;
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
@ -65,11 +82,7 @@ class Compiler {
std::unordered_map<std::string, TypeSpec> m_symbol_types;
std::unordered_map<std::shared_ptr<goos::SymbolObject>, goos::Object> m_global_constants;
std::unordered_map<std::shared_ptr<goos::SymbolObject>, 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

View file

@ -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");
}
}

View file

@ -0,0 +1,27 @@
#ifndef JAK_COMPILERSETTINGS_H
#define JAK_COMPILERSETTINGS_H
#include <unordered_map>
#include <string>
#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<std::string, SettingsEntry> m_settings;
};
#endif // JAK_COMPILERSETTINGS_H

View file

@ -156,9 +156,15 @@ std::string FileEnv::print() {
}
void FileEnv::add_function(std::unique_ptr<FunctionEnv> 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<StaticObject> s) {
m_statics.push_back(std::move(s));
}
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));
@ -220,6 +226,7 @@ RegVal* FunctionEnv::make_ireg(TypeSpec ts, emitter::RegKind kind) {
ireg.id = m_iregs.size();
auto rv = std::make_unique<RegVal>(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<std::string, Label>& FunctionEnv::get_label_map() {
std::unordered_map<std::string, Label>& LabelEnv::get_label_map() {
return m_labels;
}
}
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;
}

View file

@ -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> 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<std::string, Label>& get_label_map();
@ -85,9 +85,11 @@ class FileEnv : public Env {
std::string print() override;
void add_function(std::unique_ptr<FunctionEnv> fe);
void add_top_level_function(std::unique_ptr<FunctionEnv> fe);
void add_static(std::unique_ptr<StaticObject> s);
NoEmitEnv* add_no_emit_env();
void debug_print_tl();
const std::vector<std::unique_ptr<FunctionEnv>>& functions() { return m_functions; }
const std::vector<std::unique_ptr<StaticObject>>& statics() { return m_statics; }
bool is_empty();
~FileEnv() = default;
@ -95,6 +97,7 @@ class FileEnv : public Env {
protected:
std::string m_name;
std::vector<std::unique_ptr<FunctionEnv>> m_functions;
std::vector<std::unique_ptr<StaticObject>> m_statics;
std::unique_ptr<NoEmitEnv> m_no_emit_env = nullptr;
// statics
@ -137,11 +140,16 @@ class FunctionEnv : public DeclareEnv {
const std::vector<std::unique_ptr<IR>>& code() { return m_code; }
int max_vars() const { return m_iregs.size(); }
const std::vector<IRegConstraint>& 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 <typename T, class... Args>
T* alloc_val(Args&&... args) {
@ -161,6 +169,7 @@ class FunctionEnv : public DeclareEnv {
std::string method_of_type_name = "#f";
std::vector<UnresolvedGoto> unresolved_gotos;
std::unordered_map<std::string, Val*> params;
protected:
void resolve_gotos();
@ -176,7 +185,6 @@ class FunctionEnv : public DeclareEnv {
bool m_aligned_stack_required = false;
std::unordered_map<std::string, Env*> m_params;
std::unordered_map<std::string, Label> m_labels;
};
@ -192,23 +200,36 @@ class BlockEnv : public Env {
std::vector<TypeSpec> 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<std::string, Val*> vars;
};
class LabelEnv : public Env {
public:
explicit LabelEnv(Env* parent) : Env(parent) {}
std::string print() override { return "labelenv"; }
std::unordered_map<std::string, Label>& get_label_map() override;
protected:
std::unordered_map<std::string, Label> 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<std::shared_ptr<goos::SymbolObject>, goos::Object> macros;
};
template <typename T>
T* get_parent_env_of_type(Env* in) {

View file

@ -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<RegVal*> 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<IRegConstraint>* 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);
}

View file

@ -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<RegVal*> 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<IRegConstraint>* constraints, int my_id) override;
protected:
const RegVal* m_func = nullptr;
const RegVal* m_ret = nullptr;
std::vector<RegVal*> 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

View file

@ -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<GoalArg> params;
goos::Object body;
};
#endif // JAK_LAMBDA_H

View file

@ -0,0 +1,51 @@
#include "third-party/fmt/core.h"
#include "StaticObject.h"
#include "common/goal_constants.h"
namespace {
template <typename T>
uint32_t push_data_to_byte_vector(T data, std::vector<uint8_t>& 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<u32>(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;
}

View file

@ -0,0 +1,36 @@
#ifndef JAK_STATICOBJECT_H
#define JAK_STATICOBJECT_H
#include <string>
#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

View file

@ -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<SymbolMacroEnv>(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<SymbolMacroEnv>(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);
}
}

View file

@ -61,4 +61,17 @@ RegVal* SymbolValueVal::to_reg(Env* fe) {
auto re = fe->make_gpr(m_ts);
fe->emit(std::make_unique<IR_GetSymbolValue>(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<IR_StaticVarAddr>(re, obj));
return re;
}
RegVal* LambdaVal::to_reg(Env* fe) {
auto re = fe->make_gpr(m_ts);
assert(func);
fe->emit(std::make_unique<IR_FunctionAddr>(re, func));
return re;
}

View file

@ -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

View file

@ -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<SymbolVal>(name, m_ts.make_typespec("symbol"));
auto re = fe->alloc_val<SymbolValueVal>(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<StaticString>(str, seg);
auto fe = get_parent_env_of_type<FunctionEnv>(env);
auto result = fe->alloc_val<StaticVal>(obj.get(), m_ts.make_typespec("string"));
auto fie = get_parent_env_of_type<FileEnv>(env);
fie->add_static(std::move(obj));
return result;
}

View file

@ -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();
}

View file

@ -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<IR_SetSymbolValue>(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();
}

View file

@ -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<WithInlineEnv>(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<FunctionEnv>(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<LambdaVal>(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<FunctionEnv>(env, lambda.debug_name);
auto new_func_env = std::make_unique<FunctionEnv>(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<BlockEnv>(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<IR_Return>(return_reg, final_result));
// new_func_env->emit(std::make_unique<IR_Null>())???
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<FileEnv>(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<FunctionEnv>(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<RegVal*> 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<LambdaVal*>(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<LexicalEnv>(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<LabelEnv>(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<IR_RegSet>(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<GprPlace>(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<RegVal*>& args,
Env* env) {
auto fe = get_parent_env_of_type<FunctionEnv>(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<RegVal*> arg_outs;
for (auto& arg : args) {
arg_outs.push_back(env->make_ireg(arg->type(), emitter::RegKind::GPR));
env->emit(std::make_unique<IR_RegSet>(arg_outs.back(), arg));
}
env->emit(std::make_unique<IR_FunctionCall>(function, return_reg, arg_outs));
return return_reg;
}

View file

@ -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");
}

View file

@ -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));

View file

@ -14,11 +14,11 @@ std::vector<u8> 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;
}

View file

@ -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<u8>& 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.

View file

@ -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<u8>& 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<int> m_type_ptr_links_by_seg;
seg_map<int> m_sym_links_by_seg;
seg_vector<RipLink> m_rip_links_by_seg;
std::vector<FunctionRecord> m_all_function_records;
};
} // namespace emitter

View file

@ -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"};

View file

@ -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()); }

View file

@ -233,4 +233,13 @@ ArgumentSpec make_varargs() {
return as;
}
bool Arguments::only_contains_named(const std::unordered_set<std::string>& names) {
for (auto& kv : named) {
if (names.find(kv.first) == names.end()) {
return false;
}
}
return true;
}
} // namespace goos

View file

@ -45,6 +45,7 @@
#include <cassert>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <stdexcept>
@ -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<std::string>& names);
};
class LambdaObject : public HeapObject {

View file

@ -617,8 +617,8 @@ const std::vector<emitter::Register>& get_default_alloc_order_for_var_spill(int
const std::vector<emitter::Register>& 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();

View file

@ -55,36 +55,59 @@ struct CompilerTestRunner {
struct Test {
std::vector<std::string> expected, actual;
std::string test_name;
bool auto_pass = false;
};
std::vector<Test> tests;
void run_test(const std::string& test_file, const std::vector<std::string>& expected) {
void run_test(const std::string& test_file,
const std::vector<std::string>& expected,
MatchParam<int> 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();