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; 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: private:
friend class TypeSystem; friend class TypeSystem;
std::string m_type; 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 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 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 r13, r9 ;; r13 is GOAL fp, r9 is windows fourth argument
mov r14, [rsp + 144] ;; symbol table mov r15, [rsp + 152] ;; symbol table
mov r15, [rsp + 152] ;; offset mov r14, [rsp + 144] ;; offset
call r13 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); call_goal(Ptr<Function>(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem);
} else { } else {
if (ListenerFunction->value != s7.offset) { if (ListenerFunction->value != s7.offset) {
fprintf(stderr, "Running Listener Function:\n");
auto cptr = Ptr<u8>(ListenerFunction->value).c(); auto cptr = Ptr<u8>(ListenerFunction->value).c();
for (int i = 0; i < 40; i++) { for (int i = 0; i < 40; i++) {
printf("%x ", cptr[i]); fprintf(stderr, "%x ", cptr[i]);
} }
printf("\n"); fprintf(stderr, "\n");
auto result = auto result =
call_goal(Ptr<Function>(ListenerFunction->value), 0, 0, 0, s7.offset, g_ee_main_mem); 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__ #ifdef __linux__
cprintf("%ld\n", result); cprintf("%ld\n", result);
#else #else

View file

@ -103,7 +103,6 @@ void InitBuffers() {
IsoBufferHeader* AllocateBuffer(uint32_t size) { IsoBufferHeader* AllocateBuffer(uint32_t size) {
IsoBufferHeader* buffer = TryAllocateBuffer(size); IsoBufferHeader* buffer = TryAllocateBuffer(size);
if (buffer) { if (buffer) {
printf("--------------- allocated buffer size %d\n", size);
return buffer; return buffer;
} else { } else {
while (true) { while (true) {
@ -148,7 +147,6 @@ IsoBufferHeader* TryAllocateBuffer(uint32_t size) {
*/ */
void FreeBuffer(IsoBufferHeader* buffer) { void FreeBuffer(IsoBufferHeader* buffer) {
IsoBufferHeader* b = (IsoBufferHeader*)buffer; IsoBufferHeader* b = (IsoBufferHeader*)buffer;
printf("--------------- free buffer size %d\n", b->buffer_size);
if (b->buffer_size == BUFFER_PAGE_SIZE) { if (b->buffer_size == BUFFER_PAGE_SIZE) {
b->next = sFreeBuffer; b->next = sFreeBuffer;
sFreeBuffer = (IsoBuffer*)b; 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 we're done with the buffer, free it and load the next one (if there is one)
if (callback_buffer->data_size == 0) { if (callback_buffer->data_size == 0) {
popped_command->callback_buffer = (IsoBufferHeader*)callback_buffer->next; popped_command->callback_buffer = (IsoBufferHeader*)callback_buffer->next;
printf("free 1\n");
FreeBuffer(callback_buffer); FreeBuffer(callback_buffer);
} }
} }
@ -324,7 +321,6 @@ void ReleaseMessage(IsoMessage* cmd) {
while (cmd->callback_buffer) { while (cmd->callback_buffer) {
auto old_head = cmd->callback_buffer; auto old_head = cmd->callback_buffer;
cmd->callback_buffer = (IsoBufferHeader*)old_head->next; cmd->callback_buffer = (IsoBufferHeader*)old_head->next;
printf("free 2\n");
FreeBuffer(old_head); FreeBuffer(old_head);
} }

View file

@ -163,7 +163,7 @@ void ee_runner(SystemThreadInterface& iface) {
*/ */
void iop_runner(SystemThreadInterface& iface) { void iop_runner(SystemThreadInterface& iface) {
IOP iop; IOP iop;
printf("\n\n\n[IOP] Restart!\n"); printf("[IOP] Restart!\n");
iop.reset_allocator(); iop.reset_allocator();
ee::LIBRARY_sceSif_register(&iop); ee::LIBRARY_sceSif_register(&iop);
iop::LIBRARY_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) { if (set_socket_option(server_socket, SOL_SOCKET, server_socket_opt, &opt, sizeof(opt)) < 0) {
close_server_socket(); close_server_socket();
return false; return false;
} };
printf("[Deci2Server] Created Socket Options\n");
if (set_socket_option(server_socket, TCP_SOCKET_LEVEL, TCP_NODELAY, &opt, sizeof(opt)) < 0) { if (set_socket_option(server_socket, TCP_SOCKET_LEVEL, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
close_server_socket(); close_server_socket();
return false; return false;
} }
printf("[Deci2Server] Created TCP Socket Options\n");
if (set_socket_timeout(server_socket, 100000) < 0) { if (set_socket_timeout(server_socket, 100000) < 0) {
close_server_socket(); close_server_socket();
return false; return false;
} }
printf("[Deci2Server] Created Socket Timeout\n");
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; addr.sin_addr.s_addr = INADDR_ANY;
@ -197,8 +194,8 @@ void Deci2Server::run() {
} }
auto* hdr = (Deci2Header*)(buffer); auto* hdr = (Deci2Header*)(buffer);
printf("[DECI2] Got message:\n"); fprintf(stderr, "[DECI2] Got message:\n");
printf(" %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto, hdr->src, hdr->dst); fprintf(stderr, " %d %d 0x%x %c -> %c\n", hdr->len, hdr->rsvd, hdr->proto, hdr->src, hdr->dst);
hdr->rsvd = got; hdr->rsvd = got;

View file

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

View file

@ -51,3 +51,36 @@
(:status) (: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/Env.cpp
compiler/Val.cpp compiler/Val.cpp
compiler/IR.cpp compiler/IR.cpp
compiler/CompilerSettings.cpp
compiler/CodeGenerator.cpp compiler/CodeGenerator.cpp
compiler/StaticObject.cpp
compiler/compilation/Atoms.cpp compiler/compilation/Atoms.cpp
compiler/compilation/CompilerControl.cpp compiler/compilation/CompilerControl.cpp
compiler/compilation/Block.cpp compiler/compilation/Block.cpp
compiler/compilation/Macro.cpp compiler/compilation/Macro.cpp
compiler/compilation/Define.cpp compiler/compilation/Define.cpp
compiler/compilation/Function.cpp
compiler/Util.cpp compiler/Util.cpp
logger/Logger.cpp logger/Logger.cpp
regalloc/IRegister.cpp regalloc/IRegister.cpp

View file

@ -9,17 +9,29 @@ constexpr int XMM_SIZE = 16;
CodeGenerator::CodeGenerator(FileEnv* env) : m_fe(env) {} CodeGenerator::CodeGenerator(FileEnv* env) : m_fe(env) {}
std::vector<u8> CodeGenerator::run() { std::vector<u8> CodeGenerator::run() {
// todo, static objects
for (auto& f : m_fe->functions()) { 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(); return m_gen.generate_data_v3().to_vector();
} }
void CodeGenerator::do_function(FunctionEnv* env) { void CodeGenerator::do_function(FunctionEnv* env, int f_idx) {
auto f_rec = m_gen.add_function_to_seg(env->segment); // todo, extra alignment settings 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; auto& ri = emitter::gRegInfo;
const auto& allocs = env->alloc_result(); const auto& allocs = env->alloc_result();

View file

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

View file

@ -10,6 +10,7 @@ using namespace goos;
Compiler::Compiler() { Compiler::Compiler() {
init_logger(); init_logger();
init_settings();
m_ts.add_builtin_types(); m_ts.add_builtin_types();
m_global_env = std::make_unique<GlobalEnv>(); m_global_env = std::make_unique<GlobalEnv>();
m_none = std::make_unique<None>(m_ts.make_typespec("none")); m_none = std::make_unique<None>(m_ts.make_typespec("none"));
@ -33,7 +34,9 @@ void Compiler::execute_repl() {
// 2). compile // 2). compile
auto obj_file = compile_object_file("repl", code, m_listener.is_connected()); auto obj_file = compile_object_file("repl", code, m_listener.is_connected());
if (m_settings.debug_print_ir) {
obj_file->debug_print_tl(); obj_file->debug_print_tl();
}
if (!obj_file->is_empty()) { if (!obj_file->is_empty()) {
// 3). color // 3). color
@ -74,6 +77,8 @@ void Compiler::init_logger() {
gLogger.config[MSG_ERR].color = COLOR_RED; gLogger.config[MSG_ERR].color = COLOR_RED;
} }
void Compiler::init_settings() {}
FileEnv* Compiler::compile_object_file(const std::string& name, FileEnv* Compiler::compile_object_file(const std::string& name,
goos::Object code, goos::Object code,
bool allow_emit) { bool allow_emit) {
@ -149,10 +154,11 @@ void Compiler::color_object_file(FileEnv* env) {
input.max_vars = f->max_vars(); input.max_vars = f->max_vars();
input.constraints = f->constraints(); input.constraints = f->constraints();
// for now... if (m_settings.debug_print_regalloc) {
input.debug_settings.print_input = true; input.debug_settings.print_input = true;
input.debug_settings.print_result = true; input.debug_settings.print_result = true;
input.debug_settings.print_analysis = true; input.debug_settings.print_analysis = true;
}
f->set_allocations(allocate_registers(input)); f->set_allocations(allocate_registers(input));
} }

View file

@ -6,6 +6,7 @@
#include "goalc/listener/Listener.h" #include "goalc/listener/Listener.h"
#include "goalc/goos/Interpreter.h" #include "goalc/goos/Interpreter.h"
#include "goalc/compiler/IR.h" #include "goalc/compiler/IR.h"
#include "CompilerSettings.h"
class Compiler { class Compiler {
public: public:
@ -27,6 +28,7 @@ class Compiler {
private: private:
void init_logger(); void init_logger();
void init_settings();
bool try_getting_macro_from_goos(const goos::Object& macro_name, goos::Object* dest); bool try_getting_macro_from_goos(const goos::Object& macro_name, goos::Object* dest);
Val* compile_goos_macro(const goos::Object& o, Val* compile_goos_macro(const goos::Object& o,
const goos::Object& macro_obj, const goos::Object& macro_obj,
@ -36,7 +38,10 @@ class Compiler {
Val* compile_integer(const goos::Object& code, Env* env); Val* compile_integer(const goos::Object& code, Env* env);
Val* compile_integer(s64 value, Env* env); Val* compile_integer(s64 value, Env* env);
Val* compile_symbol(const goos::Object& form, 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_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); SymbolVal* compile_get_sym_obj(const std::string& name, Env* env);
void color_object_file(FileEnv* env); void color_object_file(FileEnv* env);
std::vector<u8> codegen_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_car(const goos::Object& o);
const goos::Object& pair_cdr(const goos::Object& o); const goos::Object& pair_cdr(const goos::Object& o);
void expect_empty_list(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; TypeSystem m_ts;
std::unique_ptr<GlobalEnv> m_global_env = nullptr; 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::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>, goos::Object> m_global_constants;
std::unordered_map<std::shared_ptr<goos::SymbolObject>, LambdaVal*> m_inlineable_functions; std::unordered_map<std::shared_ptr<goos::SymbolObject>, LambdaVal*> m_inlineable_functions;
CompilerSettings m_settings;
void typecheck(const goos::Object& form,
const TypeSpec& expected,
const TypeSpec& actual,
const std::string& error_message = "");
public: public:
// Atoms // Atoms
@ -89,14 +102,21 @@ class Compiler {
Val* compile_listen_to_target(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_listen_to_target(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_reset_target(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_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_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 // Define
Val* compile_define(const goos::Object& form, const goos::Object& rest, Env* env); 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 // Macro
Val* compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env); 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_quote(const goos::Object& form, const goos::Object& rest, Env* env);
Val* compile_defglobalconstant(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_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 #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) { 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)); 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) { void FileEnv::add_top_level_function(std::unique_ptr<FunctionEnv> fe) {
// todo, set FE as top level segment // todo, set FE as top level segment
m_functions.push_back(std::move(fe)); 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(); ireg.id = m_iregs.size();
auto rv = std::make_unique<RegVal>(ireg, ts); auto rv = std::make_unique<RegVal>(ireg, ts);
m_iregs.push_back(std::move(rv)); m_iregs.push_back(std::move(rv));
assert(kind != emitter::RegKind::INVALID);
return m_iregs.back().get(); return m_iregs.back().get();
} }
@ -230,3 +237,37 @@ std::unordered_map<std::string, Label>& FunctionEnv::get_label_map() {
std::unordered_map<std::string, Label>& LabelEnv::get_label_map() { std::unordered_map<std::string, Label>& LabelEnv::get_label_map() {
return m_labels; 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 "common/type_system/TypeSpec.h"
#include "goalc/regalloc/allocate.h" #include "goalc/regalloc/allocate.h"
#include "goalc/goos/Object.h" #include "goalc/goos/Object.h"
//#include "IR.h" #include "StaticObject.h"
#include "Label.h" #include "Label.h"
#include "Val.h" #include "Val.h"
@ -30,7 +30,7 @@ class Env {
virtual std::string print() = 0; virtual std::string print() = 0;
virtual void emit(std::unique_ptr<IR> ir); virtual void emit(std::unique_ptr<IR> ir);
virtual RegVal* make_ireg(TypeSpec ts, emitter::RegKind kind); 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 Val* lexical_lookup(goos::Object sym);
virtual BlockEnv* find_block(const std::string& name); virtual BlockEnv* find_block(const std::string& name);
virtual std::unordered_map<std::string, Label>& get_label_map(); virtual std::unordered_map<std::string, Label>& get_label_map();
@ -85,9 +85,11 @@ class FileEnv : public Env {
std::string print() override; std::string print() override;
void add_function(std::unique_ptr<FunctionEnv> fe); void add_function(std::unique_ptr<FunctionEnv> fe);
void add_top_level_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(); NoEmitEnv* add_no_emit_env();
void debug_print_tl(); void debug_print_tl();
const std::vector<std::unique_ptr<FunctionEnv>>& functions() { return m_functions; } 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(); bool is_empty();
~FileEnv() = default; ~FileEnv() = default;
@ -95,6 +97,7 @@ class FileEnv : public Env {
protected: protected:
std::string m_name; std::string m_name;
std::vector<std::unique_ptr<FunctionEnv>> m_functions; 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; std::unique_ptr<NoEmitEnv> m_no_emit_env = nullptr;
// statics // statics
@ -137,11 +140,16 @@ class FunctionEnv : public DeclareEnv {
const std::vector<std::unique_ptr<IR>>& code() { return m_code; } const std::vector<std::unique_ptr<IR>>& code() { return m_code; }
int max_vars() const { return m_iregs.size(); } int max_vars() const { return m_iregs.size(); }
const std::vector<IRegConstraint>& constraints() { return m_constraints; } 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; } 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; } const AllocationResult& alloc_result() { return m_regalloc_result; }
bool needs_aligned_stack() const { return m_aligned_stack_required; } 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> template <typename T, class... Args>
T* alloc_val(Args&&... args) { T* alloc_val(Args&&... args) {
@ -161,6 +169,7 @@ class FunctionEnv : public DeclareEnv {
std::string method_of_type_name = "#f"; std::string method_of_type_name = "#f";
std::vector<UnresolvedGoto> unresolved_gotos; std::vector<UnresolvedGoto> unresolved_gotos;
std::unordered_map<std::string, Val*> params;
protected: protected:
void resolve_gotos(); void resolve_gotos();
@ -176,7 +185,6 @@ class FunctionEnv : public DeclareEnv {
bool m_aligned_stack_required = false; bool m_aligned_stack_required = false;
std::unordered_map<std::string, Env*> m_params;
std::unordered_map<std::string, Label> m_labels; std::unordered_map<std::string, Label> m_labels;
}; };
@ -192,23 +200,36 @@ class BlockEnv : public Env {
std::vector<TypeSpec> return_types; std::vector<TypeSpec> return_types;
}; };
class LexicalEnv : public Env { class LexicalEnv : public DeclareEnv {
public: public:
LexicalEnv(Env* parent); explicit LexicalEnv(Env* parent) : DeclareEnv(parent) {}
Val* lexical_lookup(goos::Object sym) override;
std::string print() override; std::string print() override;
std::unordered_map<std::string, Val*> vars;
}; };
class LabelEnv : public Env { class LabelEnv : public Env {
public: public:
explicit LabelEnv(Env* parent) : Env(parent) {}
std::string print() override { return "labelenv"; }
std::unordered_map<std::string, Label>& get_label_map() override; std::unordered_map<std::string, Label>& get_label_map() override;
protected: protected:
std::unordered_map<std::string, Label> m_labels; 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> template <typename T>
T* get_parent_env_of_type(Env* in) { T* get_parent_env_of_type(Env* in) {

View file

@ -240,3 +240,114 @@ void IR_GotoLabel::resolve(const Label* dest) {
m_dest = dest; m_dest = dest;
m_resolved = true; 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; 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 #endif // JAK_IR_H

View file

@ -1,10 +1,19 @@
#ifndef JAK_LAMBDA_H #ifndef JAK_LAMBDA_H
#define 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 { struct Lambda {
std::string debug_name; std::string debug_name;
std::vector<GoalArg> params;
goos::Object body;
}; };
#endif // JAK_LAMBDA_H #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

@ -117,3 +117,54 @@ void Compiler::expect_empty_list(const goos::Object& o) {
throw_compile_error(o, "expected to be an 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

@ -62,3 +62,16 @@ RegVal* SymbolValueVal::to_reg(Env* fe) {
fe->emit(std::make_unique<IR_GetSymbolValue>(re, m_sym, m_sext)); fe->emit(std::make_unique<IR_GetSymbolValue>(re, m_sym, m_sext));
return re; 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 "common/type_system/TypeSystem.h"
#include "goalc/regalloc/IRegister.h" #include "goalc/regalloc/IRegister.h"
#include "Lambda.h" #include "Lambda.h"
#include "StaticObject.h"
class RegVal; class RegVal;
class Env; class Env;
@ -107,15 +108,21 @@ class SymbolValueVal : public Val {
*/ */
class LambdaVal : public Val { class LambdaVal : public Val {
public: public:
LambdaVal(TypeSpec ts, Lambda lam) : Val(ts), m_lam(lam) {} explicit LambdaVal(TypeSpec ts) : Val(std::move(ts)) {}
std::string print() const override { return "lambda-" + m_lam.debug_name; } std::string print() const override { return "lambda-" + lambda.debug_name; }
FunctionEnv* func = nullptr; FunctionEnv* func = nullptr;
Lambda lambda;
protected: RegVal* to_reg(Env* fe) override;
Lambda m_lam; };
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 // MemOffConstant
// MemOffVar // MemOffVar
// MemDeref // 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/Compiler.h"
#include "goalc/compiler/IR.h" #include "goalc/compiler/IR.h"
@ -25,7 +30,7 @@ static const std::unordered_map<
{"goto", &Compiler::compile_goto}, {"goto", &Compiler::compile_goto},
// //
// // COMPILER CONTROL // // COMPILER CONTROL
// {"gs", &Compiler::compile_gs}, {"gs", &Compiler::compile_gs},
{":exit", &Compiler::compile_exit}, {":exit", &Compiler::compile_exit},
{"asm-file", &Compiler::compile_asm_file}, {"asm-file", &Compiler::compile_asm_file},
{"listen-to-target", &Compiler::compile_listen_to_target}, {"listen-to-target", &Compiler::compile_listen_to_target},
@ -45,7 +50,7 @@ static const std::unordered_map<
// //
// // DEFINITION // // DEFINITION
{"define", &Compiler::compile_define}, {"define", &Compiler::compile_define},
// {"define-extern", &Compiler::compile_define_extern}, {"define-extern", &Compiler::compile_define_extern},
// {"set!", &Compiler::compile_set}, // {"set!", &Compiler::compile_set},
// {"defun-extern", &Compiler::compile_defun_extern}, // {"defun-extern", &Compiler::compile_defun_extern},
// {"declare-method", &Compiler::compile_declare_method}, // {"declare-method", &Compiler::compile_declare_method},
@ -62,7 +67,7 @@ static const std::unordered_map<
// //
// //
// // LAMBDA // // LAMBDA
// {"lambda", &Compiler::compile_lambda}, {"lambda", &Compiler::compile_lambda},
// {"inline", &Compiler::compile_inline}, // {"inline", &Compiler::compile_inline},
// {"with-inline", &Compiler::compile_with_inline}, // {"with-inline", &Compiler::compile_with_inline},
// {"rlet", &Compiler::compile_rlet}, // {"rlet", &Compiler::compile_rlet},
@ -121,20 +126,20 @@ static const std::unordered_map<
// {"<", &Compiler::compile_condition_as_bool}, // {"<", &Compiler::compile_condition_as_bool},
// {">", &Compiler::compile_condition_as_bool}, // {">", &Compiler::compile_condition_as_bool},
// //
// // BUILDER // // BUILDER (build-dgo/build-cgo?)
// {"builder", &Compiler::compile_builder}, // {"builder", &Compiler::compile_builder},
// //
// // UTIL // // UTIL
// {"set-config!", &Compiler::compile_set_config}, {"set-config!", &Compiler::compile_set_config},
//
//
//
// //
// // temporary testing hacks... // // temporary testing hacks...
// {"send-test", &Compiler::compile_send_test_data}, // {"send-test", &Compiler::compile_send_test_data},
}; };
/*!
* Highest level compile function
*/
Val* Compiler::compile(const goos::Object& code, Env* env) { Val* Compiler::compile(const goos::Object& code, Env* env) {
switch (code.type) { switch (code.type) {
case goos::ObjectType::PAIR: case goos::ObjectType::PAIR:
@ -143,6 +148,8 @@ Val* Compiler::compile(const goos::Object& code, Env* env) {
return compile_integer(code, env); return compile_integer(code, env);
case goos::ObjectType::SYMBOL: case goos::ObjectType::SYMBOL:
return compile_symbol(code, env); return compile_symbol(code, env);
case goos::ObjectType::STRING:
return compile_string(code, env);
default: default:
ice("Don't know how to compile " + code.print()); 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 // todo function or method call
ice("unhandled compile_pair on " + code.print()); return compile_function_or_method_call(code, env);
return nullptr; // throw_compile_error(code, "Unrecognized symbol at head of form");
// return nullptr;
} }
Val* Compiler::compile_integer(const goos::Object& code, Env* env) { 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 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 global_constant = m_global_constants.find(form.as_symbol());
auto existing_symbol = m_symbol_types.find(form.as_symbol()->name); 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) { Val* Compiler::compile_get_symbol_value(const std::string& name, Env* env) {
auto existing_symbol = m_symbol_types.find(name); auto existing_symbol = m_symbol_types.find(name);
if (existing_symbol == m_symbol_types.end()) { if (existing_symbol == m_symbol_types.end()) {
// assert(false);
throw std::runtime_error("The symbol " + name + " was not defined"); throw std::runtime_error("The symbol " + name + " was not defined");
} }
@ -231,3 +243,16 @@ Val* Compiler::compile_get_symbol_value(const std::string& name, Env* env) {
auto re = fe->alloc_val<SymbolValueVal>(sym, ts, sext); auto re = fe->alloc_val<SymbolValueVal>(sym, ts, sext);
return re; 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) { Env* env) {
(void)env; (void)env;
std::string ip = "127.0.0.1"; 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; bool got_port = false, got_ip = false;
for_each_in_list(rest, [&](const goos::Object& o) { for_each_in_list(rest, [&](const goos::Object& o) {
@ -167,3 +167,19 @@ Val* Compiler::compile_poke(const goos::Object& form, const goos::Object& rest,
m_listener.send_poke(); m_listener.send_poke();
return get_none(); 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/compiler/Compiler.h"
#include "goalc/logger/Logger.h"
Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) { Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) {
auto args = get_va(form, rest); 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)); fe->emit(std::make_unique<IR_SetSymbolValue>(sym_val, in_gpr));
return 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(); result = get_none();
for_each_in_list(current_case.as_pair()->cdr, 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; return result;
} else { } else {
// no match, continue. // no match, continue.
@ -83,6 +83,7 @@ Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest,
switch (thing.type) { switch (thing.type) {
case goos::ObjectType::SYMBOL: case goos::ObjectType::SYMBOL:
return compile_get_sym_obj(thing.as_symbol()->name, env); return compile_get_sym_obj(thing.as_symbol()->name, env);
// todo...
default: default:
throw_compile_error(form, "Can't quote this"); 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 * Instruction to pop 64 bit gpr from the stack
*/ */
static Instruction pop_gpr64(Register reg) { static Instruction pop_gpr64(Register reg) {
assert(reg.is_gpr());
if (reg.hw_id() >= 8) { if (reg.hw_id() >= 8) {
auto i = Instruction(0x58 + reg.hw_id() - 8); auto i = Instruction(0x58 + reg.hw_id() - 8);
i.set(REX(false, false, false, true)); i.set(REX(false, false, false, true));
@ -1236,7 +1237,9 @@ class IGen {
/*! /*!
* Call a function stored in a 64-bit gpr * 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); Instruction instr(0xff);
if (reg >= 8) { if (reg >= 8) {
instr.set(REX(false, false, false, true)); instr.set(REX(false, false, false, true));

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()); 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).emplace_back();
m_function_data_by_seg.at(seg).back().min_align = min_align; m_function_data_by_seg.at(seg).back().min_align = min_align;
m_all_function_records.push_back(rec);
return 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 * 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 * actual Instructions. These Instructions can be added with add_instruction. The IR_Record
* can be used as a label for jump targets. * can be used as a label for jump targets.
*/ */
IR_Record ObjectGenerator::add_ir(const FunctionRecord& func) { 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; IR_Record rec;
rec.seg = func.seg; rec.seg = func.seg;
rec.func_id = func.func_id; 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. * Add a new Instruction for the given IR instruction.
*/ */
InstructionRecord ObjectGenerator::add_instr(Instruction inst, IR_Record ir) { 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. // only this second condition is an actual error.
assert(ir.ir_id == assert(ir.ir_id ==
int(m_function_data_by_seg.at(ir.seg).at(ir.func_id).ir_to_instruction.size()) - 1); 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) { 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); 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; 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. * 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. * 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, FunctionRecord add_function_to_seg(int seg,
int min_align = 16); // should align and insert function tag 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 add_ir(const FunctionRecord& func);
IR_Record get_future_ir_record(const FunctionRecord& func, int ir_id); 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); IR_Record get_future_ir_record_in_same_func(const IR_Record& irec, int ir_id);
InstructionRecord add_instr(Instruction inst, IR_Record ir); InstructionRecord add_instr(Instruction inst, IR_Record ir);
void add_instr_no_ir(FunctionRecord func, Instruction inst); void add_instr_no_ir(FunctionRecord func, Instruction inst);
StaticRecord add_static_to_seg(int seg, int min_align = 16); 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_instruction_jump(InstructionRecord jump_instr, IR_Record destination);
void link_static_type_ptr(StaticRecord rec, int offset, const std::string& type_name); 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_type_ptr_links_by_seg;
seg_map<int> m_sym_links_by_seg; seg_map<int> m_sym_links_by_seg;
seg_vector<RipLink> m_rip_links_by_seg; seg_vector<RipLink> m_rip_links_by_seg;
std::vector<FunctionRecord> m_all_function_records;
}; };
} // namespace emitter } // namespace emitter

View file

@ -4,10 +4,10 @@ namespace emitter {
RegisterInfo RegisterInfo::make_register_info() { RegisterInfo RegisterInfo::make_register_info() {
RegisterInfo info; RegisterInfo info;
info.m_info[RAX] = {-1, false, false, "rax"}; info.m_info[RAX] = {-1, false, false, "rax"}; // temp
info.m_info[RCX] = {3, false, false, "rcx"}; info.m_info[RCX] = {3, false, false, "rcx"}; // temp
info.m_info[RDX] = {2, false, false, "rdx"}; info.m_info[RDX] = {2, false, false, "rdx"}; // temp
info.m_info[RBX] = {-1, true, false, "rbx"}; info.m_info[RBX] = {-1, true, false, "rbx"}; //
info.m_info[RSP] = {-1, false, true, "rsp"}; info.m_info[RSP] = {-1, false, true, "rsp"};
info.m_info[RBP] = {-1, true, false, "rbp"}; info.m_info[RBP] = {-1, true, false, "rbp"};
info.m_info[RSI] = {1, false, false, "rsi"}; 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_REGS = 32;
static constexpr int N_SAVED_GPRS = 5; static constexpr int N_SAVED_GPRS = 5;
static constexpr int N_SAVED_XMMS = 8; 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"); static_assert(N_REGS - 1 == XMM15, "bad register count");
@ -111,6 +113,8 @@ class RegisterInfo {
bool saved = false; // does the callee save it? bool saved = false; // does the callee save it?
bool special = false; // is it a special GOAL register? bool special = false; // is it a special GOAL register?
std::string name; std::string name;
bool temp() const { return !saved && !special; }
}; };
const Info& get_info(Register r) const { return m_info.at(r.id()); } const Info& get_info(Register r) const { return m_info.at(r.id()); }

View file

@ -233,4 +233,13 @@ ArgumentSpec make_varargs() {
return as; 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 } // namespace goos

View file

@ -45,6 +45,7 @@
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <stdexcept> #include <stdexcept>
@ -299,6 +300,7 @@ class Object {
} }
bool is_empty_list() const { return type == ObjectType::EMPTY_LIST; } 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_int() const { return type == ObjectType::INTEGER; }
bool is_float() const { return type == ObjectType::FLOAT; } bool is_float() const { return type == ObjectType::FLOAT; }
bool is_char() const { return type == ObjectType::CHAR; } 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, const Object& default_value);
Object get_named(const std::string& name); Object get_named(const std::string& name);
bool has_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 { 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) { const std::vector<emitter::Register>& get_default_alloc_order_for_var(int v, RegAllocCache* cache) {
auto& info = cache->iregs.at(v); auto& info = cache->iregs.at(v);
assert(info.kind != emitter::RegKind::INVALID); // assert(info.kind != emitter::RegKind::INVALID);
if (info.kind == emitter::RegKind::GPR) { if (info.kind == emitter::RegKind::GPR || info.kind == emitter::RegKind::INVALID) {
return emitter::gRegInfo.get_gpr_alloc_order(); return emitter::gRegInfo.get_gpr_alloc_order();
} else if (info.kind == emitter::RegKind::XMM) { } else if (info.kind == emitter::RegKind::XMM) {
return emitter::gRegInfo.get_xmm_alloc_order(); return emitter::gRegInfo.get_xmm_alloc_order();

View file

@ -55,20 +55,42 @@ struct CompilerTestRunner {
struct Test { struct Test {
std::vector<std::string> expected, actual; std::vector<std::string> expected, actual;
std::string test_name; std::string test_name;
bool auto_pass = false;
}; };
std::vector<Test> tests; 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); 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); 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() { void print_summary() {
fmt::print("~~ Compiler Test Summary for {} tests... ~~\n", tests.size()); fmt::print("~~ Compiler Test Summary for {} tests... ~~\n", tests.size());
int passed = 0; int passed = 0;
int passable = 0;
int auto_pass = 0;
for (auto& test : tests) { for (auto& test : tests) {
if (test.auto_pass) {
auto_pass++;
fmt::print("[{:40}] AUTO-PASS!\n", test.test_name);
} else {
passable++;
if (test.expected == test.actual) { if (test.expected == test.actual) {
fmt::print("[{:40}] PASS!\n", test.test_name); fmt::print("[{:40}] PASS!\n", test.test_name);
passed++; passed++;
@ -84,7 +106,8 @@ struct CompilerTestRunner {
} }
} }
} }
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-goto-1.gc", {"3\n"});
runner.run_test("test-defglobalconstant-1.gc", {"17\n"}); runner.run_test("test-defglobalconstant-1.gc", {"17\n"});
runner.run_test("test-defglobalconstant-2.gc", {"18\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(); compiler.shutdown_target();
runtime_thread.join(); runtime_thread.join();
runner.print_summary(); runner.print_summary();