mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 00:57:44 -04:00
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:
parent
de5aa7e5e4
commit
d56540f8c0
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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))
|
||||
)
|
||||
)
|
1
goal_src/test/test-application-lambda-1.gc
Normal file
1
goal_src/test/test-application-lambda-1.gc
Normal file
|
@ -0,0 +1 @@
|
|||
((lambda :inline-only #t (x y z) y) 1 2 3)
|
10
goal_src/test/test-defun-return-constant.gc
Normal file
10
goal_src/test/test-defun-return-constant.gc
Normal file
|
@ -0,0 +1,10 @@
|
|||
(defun return-13 ()
|
||||
13)
|
||||
|
||||
(defun return-12 ()
|
||||
12)
|
||||
|
||||
(defun return-11 ()
|
||||
11)
|
||||
|
||||
(return-12)
|
8
goal_src/test/test-defun-return-symbol.gc
Normal file
8
goal_src/test/test-defun-return-symbol.gc
Normal file
|
@ -0,0 +1,8 @@
|
|||
(define my-number 36)
|
||||
|
||||
(defun return-my-number ()
|
||||
my-number)
|
||||
|
||||
(define my-number 42)
|
||||
|
||||
(return-my-number)
|
4
goal_src/test/test-function-return-arg.gc
Normal file
4
goal_src/test/test-function-return-arg.gc
Normal file
|
@ -0,0 +1,4 @@
|
|||
(defun return-second-arg (one two three)
|
||||
two)
|
||||
|
||||
(return-second-arg 1 23 4)
|
4
goal_src/test/test-let-1.gc
Normal file
4
goal_src/test/test-let-1.gc
Normal file
|
@ -0,0 +1,4 @@
|
|||
(let ((x 1)
|
||||
(y (test-function 1 2 3 4))
|
||||
(z 3))
|
||||
y)
|
13
goal_src/test/test-let-star-1.gc
Normal file
13
goal_src/test/test-let-star-1.gc
Normal 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*
|
8
goal_src/test/test-nested-function-call.gc
Normal file
8
goal_src/test/test-nested-function-call.gc
Normal file
|
@ -0,0 +1,8 @@
|
|||
(defun r2 (one two three)
|
||||
two)
|
||||
|
||||
(defun r1 (one two three)
|
||||
one)
|
||||
|
||||
|
||||
(r2 1 (r1 2 3 4) 5)
|
2
goal_src/test/test-simple-function-call.gc
Normal file
2
goal_src/test/test-simple-function-call.gc
Normal file
|
@ -0,0 +1,2 @@
|
|||
(define-extern test-function (function int int int int int))
|
||||
(test-function 1 2 3 4)
|
5
goal_src/test/test-string-constant-1.gc
Normal file
5
goal_src/test/test-string-constant-1.gc
Normal 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)
|
2
goal_src/test/test-string-constant-2.gc
Normal file
2
goal_src/test/test-string-constant-2.gc
Normal file
|
@ -0,0 +1,2 @@
|
|||
(define-extern print (function object object))
|
||||
(print "test string!")
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
21
goalc/compiler/CompilerSettings.cpp
Normal file
21
goalc/compiler/CompilerSettings.cpp
Normal 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");
|
||||
}
|
||||
}
|
27
goalc/compiler/CompilerSettings.h
Normal file
27
goalc/compiler/CompilerSettings.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
51
goalc/compiler/StaticObject.cpp
Normal file
51
goalc/compiler/StaticObject.cpp
Normal 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;
|
||||
}
|
36
goalc/compiler/StaticObject.h
Normal file
36
goalc/compiler/StaticObject.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
355
goalc/compiler/compilation/Function.cpp
Normal file
355
goalc/compiler/compilation/Function.cpp
Normal 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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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()); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue