mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
add some basic symbol stuff
This commit is contained in:
parent
1de0cbb6f6
commit
ee4eb9f128
19
doc/goal_todo.md
Normal file
19
doc/goal_todo.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Done (has documentation)
|
||||||
|
- `e`
|
||||||
|
- `:exit`
|
||||||
|
- `asm-file`, `m`, `ml`
|
||||||
|
- `listen-to-target`, `reset-target`, `:status`, `lt`, `r`
|
||||||
|
|
||||||
|
Done (needs documentation)
|
||||||
|
- `top-level`
|
||||||
|
- `begin`
|
||||||
|
- `seval`
|
||||||
|
- `#cond`, `#when`, `#unless`
|
||||||
|
|
||||||
|
- Macro System
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Todo
|
||||||
|
- Type System
|
|
@ -121,9 +121,9 @@ _call_goal_asm_linux:
|
||||||
;; set GOAL function pointer
|
;; set GOAL function pointer
|
||||||
mov r13, rcx
|
mov r13, rcx
|
||||||
;; offset
|
;; offset
|
||||||
mov r15, r8
|
mov r14, r8
|
||||||
;; symbol table
|
;; symbol table
|
||||||
mov r14, r9
|
mov r15, r9
|
||||||
;; call GOAL by function pointer
|
;; call GOAL by function pointer
|
||||||
call r13
|
call r13
|
||||||
|
|
||||||
|
@ -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 r15, [rsp + 144] ;; symbol table
|
mov r14, [rsp + 144] ;; symbol table
|
||||||
mov r14, [rsp + 152] ;; offset
|
mov r15, [rsp + 152] ;; offset
|
||||||
|
|
||||||
call r13
|
call r13
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "kscheme.h"
|
#include "kscheme.h"
|
||||||
#include "ksocket.h"
|
#include "ksocket.h"
|
||||||
#include "klisten.h"
|
#include "klisten.h"
|
||||||
|
#include "kprint.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
|
@ -133,7 +134,24 @@ void KernelCheckAndDispatch() {
|
||||||
auto old_listener = ListenerFunction->value;
|
auto old_listener = ListenerFunction->value;
|
||||||
// dispatch the kernel
|
// dispatch the kernel
|
||||||
//(**kernel_dispatcher)();
|
//(**kernel_dispatcher)();
|
||||||
call_goal(Ptr<Function>(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem);
|
|
||||||
|
// todo remove. this is added while KERNEL.CGO is broken.
|
||||||
|
if (MasterUseKernel) {
|
||||||
|
call_goal(Ptr<Function>(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem);
|
||||||
|
} else {
|
||||||
|
if (ListenerFunction->value != s7.offset) {
|
||||||
|
auto cptr = Ptr<u8>(ListenerFunction->value).c();
|
||||||
|
for (int i = 0; i < 40; i++) {
|
||||||
|
printf("%x ", cptr[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
auto result =
|
||||||
|
call_goal(Ptr<Function>(ListenerFunction->value), 0, 0, 0, s7.offset, g_ee_main_mem);
|
||||||
|
cprintf("%ld\n", result);
|
||||||
|
ListenerFunction->value = s7.offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO-WINDOWS
|
// TODO-WINDOWS
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
ClearPending();
|
ClearPending();
|
||||||
|
|
|
@ -73,4 +73,6 @@ void KernelCheckAndDispatch();
|
||||||
*/
|
*/
|
||||||
void KernelShutdown();
|
void KernelShutdown();
|
||||||
|
|
||||||
|
constexpr bool MasterUseKernel = false;
|
||||||
|
|
||||||
#endif // RUNTIME_KBOOT_H
|
#endif // RUNTIME_KBOOT_H
|
||||||
|
|
|
@ -601,7 +601,8 @@ void InitMachineScheme() {
|
||||||
intern_from_c("*kernel-boot-level*")->value = intern_from_c(DebugBootLevel).offset;
|
intern_from_c("*kernel-boot-level*")->value = intern_from_c(DebugBootLevel).offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DiskBoot) {
|
// todo remove MasterUseKernel
|
||||||
|
if (DiskBoot && MasterUseKernel) {
|
||||||
*EnableMethodSet = (*EnableMethodSet) + 1;
|
*EnableMethodSet = (*EnableMethodSet) + 1;
|
||||||
load_and_link_dgo_from_c("game", kglobalheap,
|
load_and_link_dgo_from_c("game", kglobalheap,
|
||||||
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
|
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
|
||||||
|
|
|
@ -958,7 +958,10 @@ uint64_t _call_goal_asm_win32(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr,
|
||||||
* Wrapper around _call_goal_asm for calling a GOAL function from C.
|
* Wrapper around _call_goal_asm for calling a GOAL function from C.
|
||||||
*/
|
*/
|
||||||
u64 call_goal(Ptr<Function> f, u64 a, u64 b, u64 c, u64 st, void* offset) {
|
u64 call_goal(Ptr<Function> f, u64 a, u64 b, u64 c, u64 st, void* offset) {
|
||||||
auto st_ptr = (void*)((uint8_t*)(offset) + st);
|
// auto st_ptr = (void*)((uint8_t*)(offset) + st); updated for the new compiler!
|
||||||
|
void* st_ptr = (void*)st;
|
||||||
|
printf("st is 0x%x\n", st);
|
||||||
|
|
||||||
void* fptr = f.c();
|
void* fptr = f.c();
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
return _call_goal_asm_linux(a, b, c, fptr, st_ptr, offset);
|
return _call_goal_asm_linux(a, b, c, fptr, st_ptr, offset);
|
||||||
|
@ -1828,25 +1831,28 @@ s32 InitHeapAndSymbol() {
|
||||||
intern_from_c("*boot-video-mode*")->value = 0;
|
intern_from_c("*boot-video-mode*")->value = 0;
|
||||||
|
|
||||||
// load the kernel!
|
// load the kernel!
|
||||||
method_set_symbol->value++;
|
// todo, remove MasterUseKernel
|
||||||
load_and_link_dgo_from_c("kernel", kglobalheap,
|
if (MasterUseKernel) {
|
||||||
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
|
method_set_symbol->value++;
|
||||||
0x400000);
|
load_and_link_dgo_from_c("kernel", kglobalheap,
|
||||||
method_set_symbol->value--;
|
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
|
||||||
|
0x400000);
|
||||||
|
method_set_symbol->value--;
|
||||||
|
|
||||||
// check the kernel version!
|
// check the kernel version!
|
||||||
auto kernel_version = intern_from_c("*kernel-version*")->value;
|
auto kernel_version = intern_from_c("*kernel-version*")->value;
|
||||||
if (!kernel_version || ((kernel_version >> 0x13) != KERNEL_VERSION_MAJOR)) {
|
if (!kernel_version || ((kernel_version >> 0x13) != KERNEL_VERSION_MAJOR)) {
|
||||||
MsgErr("\n");
|
MsgErr("\n");
|
||||||
MsgErr(
|
MsgErr(
|
||||||
"dkernel: compiled C kernel version is %d.%d but the goal kernel is %d.%d\n\tfrom the "
|
"dkernel: compiled C kernel version is %d.%d but the goal kernel is %d.%d\n\tfrom the "
|
||||||
"goal> prompt (:mch) then mkee your kernel in linux.\n",
|
"goal> prompt (:mch) then mkee your kernel in linux.\n",
|
||||||
KERNEL_VERSION_MAJOR, KERNEL_VERSION_MINOR, kernel_version >> 0x13,
|
KERNEL_VERSION_MAJOR, KERNEL_VERSION_MINOR, kernel_version >> 0x13,
|
||||||
(kernel_version >> 3) & 0xffff);
|
(kernel_version >> 3) & 0xffff);
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
printf("Got correct kernel version %d.%d\n", kernel_version >> 0x13,
|
printf("Got correct kernel version %d.%d\n", kernel_version >> 0x13,
|
||||||
(kernel_version >> 3) & 0xffff);
|
(kernel_version >> 3) & 0xffff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup deci2count for message counter.
|
// setup deci2count for message counter.
|
||||||
|
|
|
@ -6,4 +6,48 @@
|
||||||
;; compile, color, load and save a file
|
;; compile, color, load and save a file
|
||||||
(defmacro ml (file)
|
(defmacro ml (file)
|
||||||
`(asm-file ,file :color :load :write)
|
`(asm-file ,file :color :load :write)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defmacro e ()
|
||||||
|
`(:exit)
|
||||||
|
)
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; CONDITIONAL COMPILATION
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defmacro #when (clause &rest body)
|
||||||
|
`(#cond (,clause ,@body))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defmacro #unless (clause &rest body)
|
||||||
|
`(#cond ((not ,clause) ,@body))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; TARGET CONTROL
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defmacro lt (&rest args)
|
||||||
|
;; shortcut for listen-to-target. also sends a :status command to make sure
|
||||||
|
;; all buffers on the target are flushed.
|
||||||
|
`(begin
|
||||||
|
(listen-to-target ,@args)
|
||||||
|
(:status)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(defmacro r (&rest args)
|
||||||
|
;; shortcut to completely reset the target and connect, regardless of current state
|
||||||
|
`(begin
|
||||||
|
;; connect, so we can send reset. if we're already connected, does nothing
|
||||||
|
(listen-to-target ,@args)
|
||||||
|
;; send a reset message, disconnecting us
|
||||||
|
(reset-target)
|
||||||
|
;; establish connection again
|
||||||
|
(listen-to-target ,@args)
|
||||||
|
;; flush buffers
|
||||||
|
(:status)
|
||||||
|
)
|
||||||
)
|
)
|
12
goal_src/test/test-conditional-compilation-1.gc
Normal file
12
goal_src/test/test-conditional-compilation-1.gc
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
;; test the use of #cond to evaluate goos expressions at compile time
|
||||||
|
|
||||||
|
(#cond
|
||||||
|
((> 2 (+ 2 1))
|
||||||
|
1
|
||||||
|
(invalid-code)
|
||||||
|
)
|
||||||
|
|
||||||
|
((< 2 (+ 1 2))
|
||||||
|
3
|
||||||
|
)
|
||||||
|
)
|
10
goal_src/test/test-define-1.gc
Normal file
10
goal_src/test/test-define-1.gc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
(define first-var 1)
|
||||||
|
(define second-var 2)
|
||||||
|
(define first-var 12)
|
||||||
|
|
||||||
|
(begin
|
||||||
|
(define first-var 13)
|
||||||
|
(define second-var 12)
|
||||||
|
(define first-var 17)
|
||||||
|
second-var
|
||||||
|
first-var)
|
1
goal_src/test/test-get-symbol-1.gc
Normal file
1
goal_src/test/test-get-symbol-1.gc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
'#f
|
1
goal_src/test/test-get-symbol-2.gc
Normal file
1
goal_src/test/test-get-symbol-2.gc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
'#t
|
|
@ -23,6 +23,7 @@ add_library(compiler
|
||||||
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/Util.cpp
|
compiler/Util.cpp
|
||||||
logger/Logger.cpp
|
logger/Logger.cpp
|
||||||
regalloc/IRegister.cpp
|
regalloc/IRegister.cpp
|
||||||
|
|
|
@ -19,7 +19,6 @@ Compiler::Compiler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::execute_repl() {
|
void Compiler::execute_repl() {
|
||||||
m_listener.connect_to_target(); // todo, remove
|
|
||||||
while (!m_want_exit) {
|
while (!m_want_exit) {
|
||||||
try {
|
try {
|
||||||
// 1). get a line from the user (READ)
|
// 1). get a line from the user (READ)
|
||||||
|
@ -86,6 +85,10 @@ FileEnv* Compiler::compile_object_file(const std::string& name,
|
||||||
file_env->add_top_level_function(
|
file_env->add_top_level_function(
|
||||||
compile_top_level_function("top-level", std::move(code), compilation_env));
|
compile_top_level_function("top-level", std::move(code), compilation_env));
|
||||||
|
|
||||||
|
if (!allow_emit && !file_env->is_empty()) {
|
||||||
|
throw std::runtime_error("Compilation generated code, but wasn't supposed to");
|
||||||
|
}
|
||||||
|
|
||||||
return file_env;
|
return file_env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,33 +163,45 @@ std::vector<u8> Compiler::codegen_object_file(FileEnv* env) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Compiler::run_test(const std::string& source_code) {
|
std::vector<std::string> Compiler::run_test(const std::string& source_code) {
|
||||||
if (!m_listener.is_connected()) {
|
try {
|
||||||
for (int i = 0; i < 1000; i++) {
|
if (!m_listener.is_connected()) {
|
||||||
m_listener.connect_to_target();
|
for (int i = 0; i < 1000; i++) {
|
||||||
usleep(10000);
|
m_listener.connect_to_target();
|
||||||
if (m_listener.is_connected()) {
|
usleep(10000);
|
||||||
break;
|
if (m_listener.is_connected()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!m_listener.is_connected()) {
|
||||||
|
throw std::runtime_error("Compiler::run_test couldn't connect!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!m_listener.is_connected()) {
|
|
||||||
throw std::runtime_error("Compiler::run_test couldn't connect!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto code = m_goos.reader.read_from_file(source_code);
|
auto code = m_goos.reader.read_from_file(source_code);
|
||||||
auto compiled = compile_object_file("test-code", code, true);
|
auto compiled = compile_object_file("test-code", code, true);
|
||||||
color_object_file(compiled);
|
color_object_file(compiled);
|
||||||
auto data = codegen_object_file(compiled);
|
auto data = codegen_object_file(compiled);
|
||||||
m_listener.record_messages(ListenerMessageKind::MSG_PRINT);
|
m_listener.record_messages(ListenerMessageKind::MSG_PRINT);
|
||||||
m_listener.send_code(data);
|
m_listener.send_code(data);
|
||||||
if (!m_listener.most_recent_send_was_acked()) {
|
if (!m_listener.most_recent_send_was_acked()) {
|
||||||
gLogger.log(MSG_ERR, "Runtime is not responding after sending test code. Did it crash?\n");
|
gLogger.log(MSG_ERR, "Runtime is not responding after sending test code. Did it crash?\n");
|
||||||
|
}
|
||||||
|
return m_listener.stop_recording_messages();
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
fmt::print("[Compiler] Failed to compile test program {}: {}\n", source_code, e.what());
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
return m_listener.stop_recording_messages();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::shutdown_target() {
|
void Compiler::shutdown_target() {
|
||||||
if (m_listener.is_connected()) {
|
if (m_listener.is_connected()) {
|
||||||
m_listener.send_reset(true);
|
m_listener.send_reset(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compiler::typecheck(const goos::Object& form,
|
||||||
|
const TypeSpec& expected,
|
||||||
|
const TypeSpec& actual,
|
||||||
|
const std::string& error_message) {
|
||||||
|
m_ts.typecheck(expected, actual, error_message, true, true);
|
||||||
}
|
}
|
|
@ -35,6 +35,9 @@ class Compiler {
|
||||||
Val* compile_pair(const goos::Object& code, Env* env);
|
Val* compile_pair(const goos::Object& code, Env* env);
|
||||||
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_get_symbol_value(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);
|
||||||
|
|
||||||
|
@ -48,6 +51,8 @@ class Compiler {
|
||||||
const std::vector<util::MatchParam<goos::ObjectType>>& unnamed,
|
const std::vector<util::MatchParam<goos::ObjectType>>& unnamed,
|
||||||
const std::unordered_map<std::string, std::pair<bool, util::MatchParam<goos::ObjectType>>>&
|
const std::unordered_map<std::string, std::pair<bool, util::MatchParam<goos::ObjectType>>>&
|
||||||
named);
|
named);
|
||||||
|
std::string as_string(const goos::Object& o);
|
||||||
|
std::string symbol_string(const goos::Object& o);
|
||||||
|
|
||||||
TypeSystem m_ts;
|
TypeSystem m_ts;
|
||||||
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
|
std::unique_ptr<GlobalEnv> m_global_env = nullptr;
|
||||||
|
@ -55,12 +60,36 @@ class Compiler {
|
||||||
bool m_want_exit = false;
|
bool m_want_exit = false;
|
||||||
listener::Listener m_listener;
|
listener::Listener m_listener;
|
||||||
goos::Interpreter m_goos;
|
goos::Interpreter m_goos;
|
||||||
|
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 = "");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Val* compile_exit(const goos::Object& form, const goos::Object& rest, Env* env);
|
// Atoms
|
||||||
Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env);
|
|
||||||
|
// Block
|
||||||
Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env);
|
Val* compile_begin(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||||
|
Val* compile_top_level(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||||
|
|
||||||
|
// CompilerControl
|
||||||
Val* compile_seval(const goos::Object& form, const goos::Object& rest, Env* env);
|
Val* compile_seval(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||||
|
Val* compile_exit(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||||
|
Val* compile_asm_file(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_poke(const goos::Object& form, const goos::Object& rest, Env* env);
|
||||||
|
|
||||||
|
// Define
|
||||||
|
Val* compile_define(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);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // JAK_COMPILER_H
|
#endif // JAK_COMPILER_H
|
||||||
|
|
|
@ -88,7 +88,6 @@ class FileEnv : public 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; }
|
||||||
|
|
||||||
// todo - is_empty
|
|
||||||
bool is_empty();
|
bool is_empty();
|
||||||
~FileEnv() = default;
|
~FileEnv() = default;
|
||||||
|
|
||||||
|
@ -115,7 +114,7 @@ class DeclareEnv : public Env {
|
||||||
bool inline_by_default = false; // if a function, inline when possible?
|
bool inline_by_default = false; // if a function, inline when possible?
|
||||||
bool save_code = true; // if a function, should we save the code?
|
bool save_code = true; // if a function, should we save the code?
|
||||||
bool allow_inline = false; // should we allow the user to use this an inline function
|
bool allow_inline = false; // should we allow the user to use this an inline function
|
||||||
} m_settings;
|
} settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FunctionEnv : public DeclareEnv {
|
class FunctionEnv : public DeclareEnv {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "IR.h"
|
#include "IR.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
#include "goalc/emitter/IGen.h"
|
#include "goalc/emitter/IGen.h"
|
||||||
|
|
||||||
using namespace emitter;
|
using namespace emitter;
|
||||||
|
@ -11,6 +13,9 @@ Register get_reg(const RegVal* rv, const AllocationResult& allocs, emitter::IR_R
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
///////////
|
||||||
|
// Return
|
||||||
|
///////////
|
||||||
IR_Return::IR_Return(const RegVal* return_reg, const RegVal* value)
|
IR_Return::IR_Return(const RegVal* return_reg, const RegVal* value)
|
||||||
: m_return_reg(return_reg), m_value(value) {}
|
: m_return_reg(return_reg), m_value(value) {}
|
||||||
std::string IR_Return::print() {
|
std::string IR_Return::print() {
|
||||||
|
@ -52,6 +57,9 @@ void IR_Return::do_codegen(emitter::ObjectGenerator* gen,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// LoadConstant64
|
||||||
|
/////////////////////
|
||||||
IR_LoadConstant64::IR_LoadConstant64(const RegVal* dest, u64 value)
|
IR_LoadConstant64::IR_LoadConstant64(const RegVal* dest, u64 value)
|
||||||
: m_dest(dest), m_value(value) {}
|
: m_dest(dest), m_value(value) {}
|
||||||
|
|
||||||
|
@ -70,4 +78,94 @@ void IR_LoadConstant64::do_codegen(emitter::ObjectGenerator* gen,
|
||||||
emitter::IR_Record irec) {
|
emitter::IR_Record irec) {
|
||||||
auto dest_reg = get_reg(m_dest, allocs, irec);
|
auto dest_reg = get_reg(m_dest, allocs, irec);
|
||||||
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, m_value), irec);
|
gen->add_instr(IGen::mov_gpr64_u64(dest_reg, m_value), irec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// LoadSymbolPointer
|
||||||
|
/////////////////////
|
||||||
|
IR_LoadSymbolPointer::IR_LoadSymbolPointer(const RegVal* dest, std::string name)
|
||||||
|
: m_dest(dest), m_name(std::move(name)) {}
|
||||||
|
|
||||||
|
std::string IR_LoadSymbolPointer::print() {
|
||||||
|
return fmt::format("mov-symptr {}, '{}", m_dest->print(), m_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegAllocInstr IR_LoadSymbolPointer::to_rai() {
|
||||||
|
RegAllocInstr rai;
|
||||||
|
rai.write.push_back(m_dest->ireg());
|
||||||
|
return rai;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_LoadSymbolPointer::do_codegen(emitter::ObjectGenerator* gen,
|
||||||
|
const AllocationResult& allocs,
|
||||||
|
emitter::IR_Record irec) {
|
||||||
|
auto dest_reg = get_reg(m_dest, allocs, irec);
|
||||||
|
// todo, could be single lea opcode
|
||||||
|
gen->add_instr(IGen::mov_gpr64_gpr64(dest_reg, gRegInfo.get_st_reg()), irec);
|
||||||
|
auto add = gen->add_instr(IGen::add_gpr64_imm32s(dest_reg, 0x0afecafe), irec);
|
||||||
|
gen->link_instruction_symbol_ptr(add, m_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// SetSymbolValue
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
IR_SetSymbolValue::IR_SetSymbolValue(const SymbolVal* dest, const RegVal* src)
|
||||||
|
: m_dest(dest), m_src(src) {}
|
||||||
|
|
||||||
|
std::string IR_SetSymbolValue::print() {
|
||||||
|
return fmt::format("mov '{}, {}", m_dest->name(), m_src->print());
|
||||||
|
}
|
||||||
|
|
||||||
|
RegAllocInstr IR_SetSymbolValue::to_rai() {
|
||||||
|
RegAllocInstr rai;
|
||||||
|
rai.read.push_back(m_src->ireg());
|
||||||
|
return rai;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_SetSymbolValue::do_codegen(emitter::ObjectGenerator* gen,
|
||||||
|
const AllocationResult& allocs,
|
||||||
|
emitter::IR_Record irec) {
|
||||||
|
auto src_reg = get_reg(m_src, allocs, irec);
|
||||||
|
auto instr =
|
||||||
|
gen->add_instr(IGen::store32_gpr64_gpr64_plus_gpr64_plus_s32(
|
||||||
|
gRegInfo.get_st_reg(), gRegInfo.get_offset_reg(), src_reg, 0x0badbeef),
|
||||||
|
irec);
|
||||||
|
gen->link_instruction_symbol_mem(instr, m_dest->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// GetSymbolValue
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
IR_GetSymbolValue::IR_GetSymbolValue(const RegVal* dest, const SymbolVal* src, bool sext)
|
||||||
|
: m_dest(dest), m_src(src), m_sext(sext) {}
|
||||||
|
|
||||||
|
std::string IR_GetSymbolValue::print() {
|
||||||
|
return fmt::format("mov {}, '{}", m_dest->print(), m_src->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
RegAllocInstr IR_GetSymbolValue::to_rai() {
|
||||||
|
RegAllocInstr rai;
|
||||||
|
rai.write.push_back(m_dest->ireg());
|
||||||
|
return rai;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IR_GetSymbolValue::do_codegen(emitter::ObjectGenerator* gen,
|
||||||
|
const AllocationResult& allocs,
|
||||||
|
emitter::IR_Record irec) {
|
||||||
|
auto dst_reg = get_reg(m_dest, allocs, irec);
|
||||||
|
if (m_sext) {
|
||||||
|
auto instr =
|
||||||
|
gen->add_instr(IGen::load32s_gpr64_gpr64_plus_gpr64_plus_s32(
|
||||||
|
dst_reg, gRegInfo.get_st_reg(), gRegInfo.get_offset_reg(), 0x0badbeef),
|
||||||
|
irec);
|
||||||
|
gen->link_instruction_symbol_mem(instr, m_src->name());
|
||||||
|
} else {
|
||||||
|
auto instr =
|
||||||
|
gen->add_instr(IGen::load32u_gpr64_gpr64_plus_gpr64_plus_s32(
|
||||||
|
dst_reg, gRegInfo.get_st_reg(), gRegInfo.get_offset_reg(), 0x0badbeef),
|
||||||
|
irec);
|
||||||
|
gen->link_instruction_symbol_mem(instr, m_src->name());
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -56,4 +56,47 @@ class IR_LoadConstant64 : public IR {
|
||||||
u64 m_value = 0;
|
u64 m_value = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IR_LoadSymbolPointer : public IR {
|
||||||
|
public:
|
||||||
|
IR_LoadSymbolPointer(const RegVal* dest, std::string name);
|
||||||
|
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;
|
||||||
|
std::string m_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IR_SetSymbolValue : public IR {
|
||||||
|
public:
|
||||||
|
IR_SetSymbolValue(const SymbolVal* dest, const RegVal* 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 SymbolVal* m_dest = nullptr;
|
||||||
|
const RegVal* m_src = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IR_GetSymbolValue : public IR {
|
||||||
|
public:
|
||||||
|
IR_GetSymbolValue(const RegVal* dest, const SymbolVal* src, bool sext);
|
||||||
|
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 SymbolVal* m_src = nullptr;
|
||||||
|
bool m_sext = false;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // JAK_IR_H
|
#endif // JAK_IR_H
|
||||||
|
|
|
@ -95,4 +95,12 @@ void Compiler::for_each_in_list(const goos::Object& list,
|
||||||
if (!iter->is_empty_list()) {
|
if (!iter->is_empty_list()) {
|
||||||
throw_compile_error(list, "invalid list in for_each_in_list");
|
throw_compile_error(list, "invalid list in for_each_in_list");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Compiler::as_string(const goos::Object& o) {
|
||||||
|
return o.as_string()->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Compiler::symbol_string(const goos::Object& o) {
|
||||||
|
return o.as_symbol()->name;
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
/*!
|
/*!
|
||||||
* Fallback to_gpr if a more optimized one is not provided.
|
* Fallback to_gpr if a more optimized one is not provided.
|
||||||
*/
|
*/
|
||||||
const RegVal* Val::to_gpr(FunctionEnv* fe) const {
|
RegVal* Val::to_gpr(Env* fe) {
|
||||||
auto rv = to_reg(fe);
|
auto rv = to_reg(fe);
|
||||||
if (rv->ireg().kind == emitter::RegKind::GPR) {
|
if (rv->ireg().kind == emitter::RegKind::GPR) {
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -17,17 +17,17 @@ const RegVal* Val::to_gpr(FunctionEnv* fe) const {
|
||||||
/*!
|
/*!
|
||||||
* Fallback to_xmm if a more optimized one is not provided.
|
* Fallback to_xmm if a more optimized one is not provided.
|
||||||
*/
|
*/
|
||||||
const RegVal* Val::to_xmm(FunctionEnv* fe) const {
|
RegVal* Val::to_xmm(Env* fe) {
|
||||||
(void)fe;
|
(void)fe;
|
||||||
throw std::runtime_error("Val::to_xmm NYI"); // todo
|
throw std::runtime_error("Val::to_xmm NYI"); // todo
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegVal* RegVal::to_reg(FunctionEnv* fe) const {
|
RegVal* RegVal::to_reg(Env* fe) {
|
||||||
(void)fe;
|
(void)fe;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegVal* RegVal::to_gpr(FunctionEnv* fe) const {
|
RegVal* RegVal::to_gpr(Env* fe) {
|
||||||
(void)fe;
|
(void)fe;
|
||||||
if (m_ireg.kind == emitter::RegKind::GPR) {
|
if (m_ireg.kind == emitter::RegKind::GPR) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -36,7 +36,7 @@ const RegVal* RegVal::to_gpr(FunctionEnv* fe) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegVal* RegVal::to_xmm(FunctionEnv* fe) const {
|
RegVal* RegVal::to_xmm(Env* fe) {
|
||||||
(void)fe;
|
(void)fe;
|
||||||
if (m_ireg.kind == emitter::RegKind::XMM) {
|
if (m_ireg.kind == emitter::RegKind::XMM) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -45,8 +45,20 @@ const RegVal* RegVal::to_xmm(FunctionEnv* fe) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RegVal* IntegerConstantVal::to_reg(FunctionEnv* fe) const {
|
RegVal* IntegerConstantVal::to_reg(Env* fe) {
|
||||||
auto rv = fe->make_gpr(m_ts);
|
auto rv = fe->make_gpr(m_ts);
|
||||||
fe->emit(std::make_unique<IR_LoadConstant64>(rv, m_value));
|
fe->emit(std::make_unique<IR_LoadConstant64>(rv, m_value));
|
||||||
return rv;
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegVal* SymbolVal::to_reg(Env* fe) {
|
||||||
|
auto re = fe->make_gpr(m_ts);
|
||||||
|
fe->emit(std::make_unique<IR_LoadSymbolPointer>(re, m_name));
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
#include "Lambda.h"
|
#include "Lambda.h"
|
||||||
|
|
||||||
class RegVal;
|
class RegVal;
|
||||||
|
class Env;
|
||||||
class FunctionEnv;
|
class FunctionEnv;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -31,12 +32,12 @@ class Val {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::string print() const = 0;
|
virtual std::string print() const = 0;
|
||||||
virtual const RegVal* to_reg(FunctionEnv* fe) const {
|
virtual RegVal* to_reg(Env* fe) {
|
||||||
(void)fe;
|
(void)fe;
|
||||||
throw std::runtime_error("to_reg called on invalid Val: " + print());
|
throw std::runtime_error("to_reg called on invalid Val: " + print());
|
||||||
}
|
}
|
||||||
virtual const RegVal* to_gpr(FunctionEnv* fe) const;
|
virtual RegVal* to_gpr(Env* fe);
|
||||||
virtual const RegVal* to_xmm(FunctionEnv* fe) const;
|
virtual RegVal* to_xmm(Env* fe);
|
||||||
|
|
||||||
const TypeSpec& type() const { return m_ts; }
|
const TypeSpec& type() const { return m_ts; }
|
||||||
void set_type(TypeSpec ts) { m_ts = std::move(ts); }
|
void set_type(TypeSpec ts) { m_ts = std::move(ts); }
|
||||||
|
@ -64,9 +65,9 @@ class RegVal : public Val {
|
||||||
bool is_register() const override { return true; }
|
bool is_register() const override { return true; }
|
||||||
IRegister ireg() const override { return m_ireg; }
|
IRegister ireg() const override { return m_ireg; }
|
||||||
std::string print() const override { return m_ireg.to_string(); };
|
std::string print() const override { return m_ireg.to_string(); };
|
||||||
const RegVal* to_reg(FunctionEnv* fe) const override;
|
RegVal* to_reg(Env* fe) override;
|
||||||
const RegVal* to_gpr(FunctionEnv* fe) const override;
|
RegVal* to_gpr(Env* fe) override;
|
||||||
const RegVal* to_xmm(FunctionEnv* fe) const override;
|
RegVal* to_xmm(Env* fe) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IRegister m_ireg;
|
IRegister m_ireg;
|
||||||
|
@ -79,13 +80,27 @@ class RegVal : public Val {
|
||||||
class SymbolVal : public Val {
|
class SymbolVal : public Val {
|
||||||
public:
|
public:
|
||||||
SymbolVal(std::string name, TypeSpec ts) : Val(std::move(ts)), m_name(std::move(name)) {}
|
SymbolVal(std::string name, TypeSpec ts) : Val(std::move(ts)), m_name(std::move(name)) {}
|
||||||
const std::string& name() { return m_name; }
|
const std::string& name() const { return m_name; }
|
||||||
std::string print() const override { return "<" + m_name + ">"; }
|
std::string print() const override { return "<" + m_name + ">"; }
|
||||||
|
RegVal* to_reg(Env* fe) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SymbolValueVal : public Val {
|
||||||
|
public:
|
||||||
|
SymbolValueVal(const SymbolVal* sym, TypeSpec ts, bool sext)
|
||||||
|
: Val(std::move(ts)), m_sym(sym), m_sext(sext) {}
|
||||||
|
const std::string& name() const { return m_sym->name(); }
|
||||||
|
std::string print() const override { return "[<" + name() + ">]"; }
|
||||||
|
RegVal* to_reg(Env* fe) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const SymbolVal* m_sym = nullptr;
|
||||||
|
bool m_sext = false;
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* A Val representing a GOAL lambda. It can be a "real" x86-64 function, in which case the
|
* A Val representing a GOAL lambda. It can be a "real" x86-64 function, in which case the
|
||||||
* FunctionEnv is set. Otherwise, just contains a Lambda.
|
* FunctionEnv is set. Otherwise, just contains a Lambda.
|
||||||
|
@ -94,10 +109,10 @@ class LambdaVal : public Val {
|
||||||
public:
|
public:
|
||||||
LambdaVal(TypeSpec ts, Lambda lam) : Val(ts), m_lam(lam) {}
|
LambdaVal(TypeSpec ts, Lambda lam) : Val(ts), m_lam(lam) {}
|
||||||
std::string print() const override { return "lambda-" + m_lam.debug_name; }
|
std::string print() const override { return "lambda-" + m_lam.debug_name; }
|
||||||
|
FunctionEnv* func = nullptr;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Lambda m_lam;
|
Lambda m_lam;
|
||||||
FunctionEnv* fe = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
|
@ -111,7 +126,7 @@ class IntegerConstantVal : public Val {
|
||||||
public:
|
public:
|
||||||
IntegerConstantVal(TypeSpec ts, s64 value) : Val(ts), m_value(value) {}
|
IntegerConstantVal(TypeSpec ts, s64 value) : Val(ts), m_value(value) {}
|
||||||
std::string print() const override { return "integer-constant-" + std::to_string(m_value); }
|
std::string print() const override { return "integer-constant-" + std::to_string(m_value); }
|
||||||
const RegVal* to_reg(FunctionEnv* fe) const override;
|
RegVal* to_reg(Env* fe) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
s64 m_value = -1;
|
s64 m_value = -1;
|
||||||
|
|
|
@ -27,12 +27,15 @@ static const std::unordered_map<
|
||||||
// // 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},
|
||||||
|
{"reset-target", &Compiler::compile_reset_target},
|
||||||
|
{":status", &Compiler::compile_poke},
|
||||||
// {"test", &Compiler::compile_test},
|
// {"test", &Compiler::compile_test},
|
||||||
// {"in-package", &Compiler::compile_in_package},
|
// {"in-package", &Compiler::compile_in_package},
|
||||||
//
|
//
|
||||||
// // CONDITIONAL COMPILATION
|
// // CONDITIONAL COMPILATION
|
||||||
// {"#cond", &Compiler::compile_gscond},
|
{"#cond", &Compiler::compile_gscond},
|
||||||
// {"defglobalconstant", &Compiler::compile_defglobalconstant},
|
// {"defglobalconstant", &Compiler::compile_defglobalconstant},
|
||||||
{"seval", &Compiler::compile_seval},
|
{"seval", &Compiler::compile_seval},
|
||||||
//
|
//
|
||||||
|
@ -41,7 +44,7 @@ static const std::unordered_map<
|
||||||
// {"when-goto", &Compiler::compile_when_goto},
|
// {"when-goto", &Compiler::compile_when_goto},
|
||||||
//
|
//
|
||||||
// // 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},
|
||||||
|
@ -70,7 +73,7 @@ static const std::unordered_map<
|
||||||
//
|
//
|
||||||
// // MACRO
|
// // MACRO
|
||||||
// {"print-type", &Compiler::compile_print_type},
|
// {"print-type", &Compiler::compile_print_type},
|
||||||
// {"quote", &Compiler::compile_quote},
|
{"quote", &Compiler::compile_quote},
|
||||||
// {"defconstant", &Compiler::compile_defconstant},
|
// {"defconstant", &Compiler::compile_defconstant},
|
||||||
//
|
//
|
||||||
// {"declare", &Compiler::compile_declare},
|
// {"declare", &Compiler::compile_declare},
|
||||||
|
@ -126,9 +129,7 @@ static const std::unordered_map<
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// {"listen-to-target", &Compiler::compile_listen_to_target},
|
|
||||||
// {"reset-target", &Compiler::compile_reset_target},
|
|
||||||
// {":status", &Compiler::compile_poke},
|
|
||||||
//
|
//
|
||||||
// // temporary testing hacks...
|
// // temporary testing hacks...
|
||||||
// {"send-test", &Compiler::compile_send_test_data},
|
// {"send-test", &Compiler::compile_send_test_data},
|
||||||
|
@ -140,6 +141,8 @@ Val* Compiler::compile(const goos::Object& code, Env* env) {
|
||||||
return compile_pair(code, env);
|
return compile_pair(code, env);
|
||||||
case goos::ObjectType::INTEGER:
|
case goos::ObjectType::INTEGER:
|
||||||
return compile_integer(code, env);
|
return compile_integer(code, env);
|
||||||
|
case goos::ObjectType::SYMBOL:
|
||||||
|
return compile_symbol(code, env);
|
||||||
default:
|
default:
|
||||||
ice("Don't know how to compile " + code.print());
|
ice("Don't know how to compile " + code.print());
|
||||||
}
|
}
|
||||||
|
@ -180,3 +183,36 @@ Val* Compiler::compile_integer(s64 value, Env* env) {
|
||||||
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||||
return fe->alloc_val<IntegerConstantVal>(m_ts.make_typespec("int"), value);
|
return fe->alloc_val<IntegerConstantVal>(m_ts.make_typespec("int"), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SymbolVal* Compiler::compile_get_sym_obj(const std::string& name, Env* env) {
|
||||||
|
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||||
|
return fe->alloc_val<SymbolVal>(name, m_ts.make_typespec("symbol"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* Compiler::compile_symbol(const goos::Object& form, Env* env) {
|
||||||
|
auto name = symbol_string(form);
|
||||||
|
|
||||||
|
if (name == "none") {
|
||||||
|
return get_none();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo mlet
|
||||||
|
// todo lexical
|
||||||
|
// todo global constant
|
||||||
|
|
||||||
|
return compile_get_symbol_value(name, 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()) {
|
||||||
|
throw std::runtime_error("The symbol " + name + " was not defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ts = existing_symbol->second;
|
||||||
|
auto sext = m_ts.lookup_type(ts)->get_load_signed();
|
||||||
|
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||||
|
auto sym = fe->alloc_val<SymbolVal>(name, m_ts.make_typespec("symbol"));
|
||||||
|
auto re = fe->alloc_val<SymbolValueVal>(sym, ts, sext);
|
||||||
|
return re;
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
#include "goalc/compiler/Compiler.h"
|
#include "goalc/compiler/Compiler.h"
|
||||||
#include "goalc/compiler/IR.h"
|
#include "goalc/compiler/IR.h"
|
||||||
|
#include "goalc/util/Timer.h"
|
||||||
|
#include "goalc/util/file_io.h"
|
||||||
|
|
||||||
Val* Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, Env* env) {
|
Val* Compiler::compile_exit(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||||
(void)env;
|
(void)env;
|
||||||
|
@ -22,4 +24,140 @@ Val* Compiler::compile_seval(const goos::Object& form, const goos::Object& rest,
|
||||||
throw_compile_error(form, std::string("seval error: ") + e.what());
|
throw_compile_error(form, std::string("seval error: ") + e.what());
|
||||||
}
|
}
|
||||||
return get_none();
|
return get_none();
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* Compiler::compile_asm_file(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||||
|
(void)env;
|
||||||
|
int i = 0;
|
||||||
|
std::string filename;
|
||||||
|
bool load = false;
|
||||||
|
bool color = false;
|
||||||
|
bool write = false;
|
||||||
|
bool no_code = false;
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, float>> timing;
|
||||||
|
Timer total_timer;
|
||||||
|
|
||||||
|
for_each_in_list(rest, [&](const goos::Object& o) {
|
||||||
|
if (i == 0) {
|
||||||
|
filename = as_string(o);
|
||||||
|
} else {
|
||||||
|
auto setting = symbol_string(o);
|
||||||
|
if (setting == ":load") {
|
||||||
|
load = true;
|
||||||
|
} else if (setting == ":color") {
|
||||||
|
color = true;
|
||||||
|
} else if (setting == ":write") {
|
||||||
|
write = true;
|
||||||
|
} else if (setting == ":no-code") {
|
||||||
|
no_code = true;
|
||||||
|
} else {
|
||||||
|
throw_compile_error(form, "invalid option " + setting + " in asm-file form");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
});
|
||||||
|
|
||||||
|
Timer reader_timer;
|
||||||
|
auto code = m_goos.reader.read_from_file(filename);
|
||||||
|
timing.emplace_back("read", reader_timer.getMs());
|
||||||
|
|
||||||
|
Timer compile_timer;
|
||||||
|
std::string obj_file_name = basename(filename.c_str());
|
||||||
|
obj_file_name = obj_file_name.substr(0, obj_file_name.find_last_of('.'));
|
||||||
|
auto obj_file = compile_object_file(obj_file_name, code, !no_code);
|
||||||
|
timing.emplace_back("compile", compile_timer.getMs());
|
||||||
|
|
||||||
|
if (color) {
|
||||||
|
Timer color_timer;
|
||||||
|
color_object_file(obj_file);
|
||||||
|
timing.emplace_back("color", color_timer.getMs());
|
||||||
|
|
||||||
|
Timer codegen_timer;
|
||||||
|
auto data = codegen_object_file(obj_file);
|
||||||
|
timing.emplace_back("codegen", codegen_timer.getMs());
|
||||||
|
|
||||||
|
if (load) {
|
||||||
|
if (m_listener.is_connected()) {
|
||||||
|
m_listener.send_code(data);
|
||||||
|
} else {
|
||||||
|
printf("WARNING - couldn't load because listener isn't connected\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
// auto output_dir = as_string(get_constant_or_error(form, "*compiler-output-path*"));
|
||||||
|
// todo, change extension based on v3/v4
|
||||||
|
auto output_name = m_goos.reader.get_source_dir() + "/out/" + obj_file_name + ".o";
|
||||||
|
util::write_binary_file(output_name, (void*)data.data(), data.size());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (load) {
|
||||||
|
printf("WARNING - couldn't load because coloring is not enabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
printf("WARNING - couldn't write because coloring is not enabled\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if(truthy(get_config("print-asm-file-time"))) {
|
||||||
|
for (auto& e : timing) {
|
||||||
|
printf(" %12s %4.2f\n", e.first.c_str(), e.second);
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
|
||||||
|
return get_none();
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* Compiler::compile_listen_to_target(const goos::Object& form,
|
||||||
|
const goos::Object& rest,
|
||||||
|
Env* env) {
|
||||||
|
(void)env;
|
||||||
|
std::string ip = "127.0.0.1";
|
||||||
|
int port = 8112;
|
||||||
|
bool got_port = false, got_ip = false;
|
||||||
|
|
||||||
|
for_each_in_list(rest, [&](const goos::Object& o) {
|
||||||
|
if (o.is_string()) {
|
||||||
|
if (got_ip) {
|
||||||
|
throw_compile_error(form, "got multiple strings!");
|
||||||
|
}
|
||||||
|
got_ip = true;
|
||||||
|
ip = o.as_string()->data;
|
||||||
|
} else if (o.is_int()) {
|
||||||
|
if (got_port) {
|
||||||
|
throw_compile_error(form, "got multiple ports!");
|
||||||
|
}
|
||||||
|
got_port = true;
|
||||||
|
port = o.integer_obj.value;
|
||||||
|
} else {
|
||||||
|
throw_compile_error(form, "invalid argument to listen-to-target");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_listener.connect_to_target(30, ip, port);
|
||||||
|
return get_none();
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* Compiler::compile_reset_target(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||||
|
(void)env;
|
||||||
|
bool shutdown = false;
|
||||||
|
for_each_in_list(rest, [&](const goos::Object& o) {
|
||||||
|
if (o.is_symbol() && symbol_string(o) == ":shutdown") {
|
||||||
|
shutdown = true;
|
||||||
|
} else {
|
||||||
|
throw_compile_error(form, "invalid argument to reset-target");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_listener.send_reset(shutdown);
|
||||||
|
return get_none();
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* Compiler::compile_poke(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||||
|
(void)env;
|
||||||
|
auto args = get_va(form, rest);
|
||||||
|
va_check(form, args, {}, {});
|
||||||
|
m_listener.send_poke();
|
||||||
|
return get_none();
|
||||||
}
|
}
|
41
goalc/compiler/compilation/Define.cpp
Normal file
41
goalc/compiler/compilation/Define.cpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include "goalc/compiler/Compiler.h"
|
||||||
|
|
||||||
|
Val* Compiler::compile_define(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||||
|
auto args = get_va(form, rest);
|
||||||
|
va_check(form, args, {goos::ObjectType::SYMBOL, {}}, {});
|
||||||
|
auto& sym = args.unnamed.at(0);
|
||||||
|
auto& val = args.unnamed.at(1);
|
||||||
|
|
||||||
|
// check we aren't duplicated a name as both a symbol and global constant
|
||||||
|
auto global_constant = m_global_constants.find(sym.as_symbol());
|
||||||
|
if (global_constant != m_global_constants.end()) {
|
||||||
|
throw_compile_error(
|
||||||
|
form, "it is illegal to define a GOAL symbol with the same name as a GOAL global constant");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fe = get_parent_env_of_type<FunctionEnv>(env);
|
||||||
|
auto sym_val = fe->alloc_val<SymbolVal>(symbol_string(sym), m_ts.make_typespec("symbol"));
|
||||||
|
auto compiled_val = compile_error_guard(val, env);
|
||||||
|
auto as_lambda = dynamic_cast<LambdaVal*>(compiled_val);
|
||||||
|
if (as_lambda) {
|
||||||
|
// there are two cases in which we save a function body that is passed to a define:
|
||||||
|
// 1. It generated code [so went through the compiler] and the allow_inline flag is set.
|
||||||
|
// 2. It didn't generate code [so explicitly with :inline-only lambdas]
|
||||||
|
// The third case - immediate lambdas - don't get passed to a define,
|
||||||
|
// so this won't cause those to live for longer than they should
|
||||||
|
if ((as_lambda->func && as_lambda->func->settings.allow_inline) || !as_lambda->func) {
|
||||||
|
m_inlineable_functions[sym.as_symbol()] = as_lambda;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto in_gpr = compiled_val->to_gpr(fe);
|
||||||
|
auto existing_type = m_symbol_types.find(sym.as_symbol()->name);
|
||||||
|
if (existing_type == m_symbol_types.end()) {
|
||||||
|
m_symbol_types[sym.as_symbol()->name] = in_gpr->type();
|
||||||
|
} else {
|
||||||
|
typecheck(form, existing_type->second, in_gpr->type(), "define on existing symbol");
|
||||||
|
}
|
||||||
|
|
||||||
|
fe->emit(std::make_unique<IR_SetSymbolValue>(sym_val, in_gpr));
|
||||||
|
return in_gpr;
|
||||||
|
}
|
|
@ -34,4 +34,56 @@ Val* Compiler::compile_goos_macro(const goos::Object& o,
|
||||||
auto goos_result = m_goos.eval_list_return_last(macro->body, macro->body, mac_env);
|
auto goos_result = m_goos.eval_list_return_last(macro->body, macro->body, mac_env);
|
||||||
m_goos.goal_to_goos.reset();
|
m_goos.goal_to_goos.reset();
|
||||||
return compile_error_guard(goos_result, env);
|
return compile_error_guard(goos_result, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* Compiler::compile_gscond(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||||
|
if (!rest.is_pair()) {
|
||||||
|
throw_compile_error(form, "#cond must have at least one clause, which must be a form");
|
||||||
|
}
|
||||||
|
Val* result = nullptr;
|
||||||
|
|
||||||
|
Object lst = rest;
|
||||||
|
for (;;) {
|
||||||
|
if (lst.is_pair()) {
|
||||||
|
Object current_case = lst.as_pair()->car;
|
||||||
|
if (!current_case.is_pair()) {
|
||||||
|
throw_compile_error(lst, "Bad case in #cond");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check condition:
|
||||||
|
Object condition_result =
|
||||||
|
m_goos.eval_with_rewind(current_case.as_pair()->car, m_goos.global_environment.as_env());
|
||||||
|
if (m_goos.truthy(condition_result)) {
|
||||||
|
if (current_case.as_pair()->cdr.is_empty_list()) {
|
||||||
|
return get_none();
|
||||||
|
}
|
||||||
|
// got a match!
|
||||||
|
result = get_none();
|
||||||
|
|
||||||
|
for_each_in_list(current_case.as_pair()->cdr,
|
||||||
|
[&](Object o) { result = compile_error_guard(o, env); });
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
// no match, continue.
|
||||||
|
lst = lst.as_pair()->cdr;
|
||||||
|
}
|
||||||
|
} else if (lst.is_empty_list()) {
|
||||||
|
return get_none();
|
||||||
|
} else {
|
||||||
|
throw_compile_error(form, "malformed #cond");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Val* Compiler::compile_quote(const goos::Object& form, const goos::Object& rest, Env* env) {
|
||||||
|
auto args = get_va(form, rest);
|
||||||
|
va_check(form, args, {{}}, {});
|
||||||
|
auto thing = args.unnamed.at(0);
|
||||||
|
switch (thing.type) {
|
||||||
|
case goos::ObjectType::SYMBOL:
|
||||||
|
return compile_get_sym_obj(thing.as_symbol()->name, env);
|
||||||
|
default:
|
||||||
|
throw_compile_error(form, "Can't quote this");
|
||||||
|
}
|
||||||
|
return get_none();
|
||||||
}
|
}
|
|
@ -14,6 +14,11 @@ std::vector<u8> ObjectFileData::to_vector() const {
|
||||||
// data (code + static objects, by segment)
|
// data (code + static objects, by segment)
|
||||||
for (int seg = N_SEG; seg-- > 0;) {
|
for (int seg = N_SEG; seg-- > 0;) {
|
||||||
result.insert(result.end(), segment_data[seg].begin(), segment_data[seg].end());
|
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");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -805,11 +805,9 @@ Object Interpreter::eval_quasiquote(const Object& form,
|
||||||
return quasiquote_helper(rest.as_pair()->car, env);
|
return quasiquote_helper(rest.as_pair()->car, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
bool Interpreter::truthy(const Object& o) {
|
||||||
bool truthy(const Object& o) {
|
|
||||||
return !(o.is_symbol() && o.as_symbol()->name == "#f");
|
return !(o.is_symbol() && o.as_symbol()->name == "#f");
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Scheme "cond" statement - tested by integrated tests only.
|
* Scheme "cond" statement - tested by integrated tests only.
|
||||||
|
|
|
@ -32,6 +32,7 @@ class Interpreter {
|
||||||
Object eval_list_return_last(const Object& form,
|
Object eval_list_return_last(const Object& form,
|
||||||
Object rest,
|
Object rest,
|
||||||
const std::shared_ptr<EnvironmentObject>& env);
|
const std::shared_ptr<EnvironmentObject>& env);
|
||||||
|
bool truthy(const Object& o);
|
||||||
|
|
||||||
Reader reader;
|
Reader reader;
|
||||||
Object global_environment;
|
Object global_environment;
|
||||||
|
|
|
@ -51,9 +51,10 @@ bool Listener::is_connected() const {
|
||||||
* Attempt to connect to the target. If the target isn't running, this should fail quickly.
|
* Attempt to connect to the target. If the target isn't running, this should fail quickly.
|
||||||
* Returns true if successfully connected.
|
* Returns true if successfully connected.
|
||||||
*/
|
*/
|
||||||
bool Listener::connect_to_target(const std::string& ip, int port) {
|
bool Listener::connect_to_target(int n_tries, const std::string& ip, int port) {
|
||||||
if (m_connected) {
|
if (m_connected) {
|
||||||
throw std::runtime_error("attempted a Listener::connect_to_target when already connected!");
|
printf("already connected!\n");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (socket_fd >= 0) {
|
if (socket_fd >= 0) {
|
||||||
|
@ -100,12 +101,21 @@ bool Listener::connect_to_target(const std::string& ip, int port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect!
|
// connect!
|
||||||
int rv = connect(socket_fd, (sockaddr*)&server_address, sizeof(server_address));
|
int rv, i;
|
||||||
|
for (i = 0; i < n_tries; i++) {
|
||||||
|
rv = connect(socket_fd, (sockaddr*)&server_address, sizeof(server_address));
|
||||||
|
if (rv >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
printf("[Listener] Failed to connect\n");
|
printf("[Listener] Failed to connect\n");
|
||||||
close(socket_fd);
|
close(socket_fd);
|
||||||
socket_fd = -1;
|
socket_fd = -1;
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
printf("[Listener] Socket connected established! (took %d tries)\n", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the GOAL version number, to make sure we connected to the right thing
|
// get the GOAL version number, to make sure we connected to the right thing
|
||||||
|
@ -303,6 +313,24 @@ void Listener::send_reset(bool shutdown) {
|
||||||
printf("closed connection to target\n");
|
printf("closed connection to target\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Listener::send_poke() {
|
||||||
|
if (!m_connected) {
|
||||||
|
printf("Not connected, so cannot poke target.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto* header = (ListenerMessageHeader*)m_buffer;
|
||||||
|
header->deci2_header.rsvd = 0;
|
||||||
|
header->deci2_header.len = sizeof(ListenerMessageHeader);
|
||||||
|
header->deci2_header.proto = 0xe042; // todo don't hardcode
|
||||||
|
header->deci2_header.src = 'H';
|
||||||
|
header->deci2_header.dst = 'E';
|
||||||
|
header->msg_size = 0;
|
||||||
|
header->ltt_msg_kind = LTT_MSG_POKE;
|
||||||
|
header->u6 = 0;
|
||||||
|
header->u8 = 0;
|
||||||
|
send_buffer(sizeof(ListenerMessageHeader));
|
||||||
|
}
|
||||||
|
|
||||||
void Listener::send_buffer(int sz) {
|
void Listener::send_buffer(int sz) {
|
||||||
int wrote = 0;
|
int wrote = 0;
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,14 @@ class Listener {
|
||||||
static constexpr int BUFFER_SIZE = 32 * 1024 * 1024;
|
static constexpr int BUFFER_SIZE = 32 * 1024 * 1024;
|
||||||
Listener();
|
Listener();
|
||||||
~Listener();
|
~Listener();
|
||||||
bool connect_to_target(const std::string& ip = "127.0.0.1", int port = DECI2_PORT);
|
bool connect_to_target(int n_tries = 1,
|
||||||
|
const std::string& ip = "127.0.0.1",
|
||||||
|
int port = DECI2_PORT);
|
||||||
void record_messages(ListenerMessageKind kind);
|
void record_messages(ListenerMessageKind kind);
|
||||||
std::vector<std::string> stop_recording_messages();
|
std::vector<std::string> stop_recording_messages();
|
||||||
bool is_connected() const;
|
bool is_connected() const;
|
||||||
void send_reset(bool shutdown);
|
void send_reset(bool shutdown);
|
||||||
|
void send_poke();
|
||||||
void disconnect();
|
void disconnect();
|
||||||
void send_code(std::vector<uint8_t>& code);
|
void send_code(std::vector<uint8_t>& code);
|
||||||
bool most_recent_send_was_acked() { return got_ack; }
|
bool most_recent_send_was_acked() { return got_ack; }
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
add_library(util SHARED text_util.cpp file_io.cpp)
|
add_library(util SHARED text_util.cpp file_io.cpp Timer.cpp)
|
54
goalc/util/Timer.cpp
Normal file
54
goalc/util/Timer.cpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "Timer.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#define MS_PER_SEC 1000ULL // MS = milliseconds
|
||||||
|
#define US_PER_MS 1000ULL // US = microseconds
|
||||||
|
#define HNS_PER_US 10ULL // HNS = hundred-nanoseconds (e.g., 1 hns = 100 ns)
|
||||||
|
#define NS_PER_US 1000ULL
|
||||||
|
|
||||||
|
#define HNS_PER_SEC (MS_PER_SEC * US_PER_MS * HNS_PER_US)
|
||||||
|
#define NS_PER_HNS (100ULL) // NS = nanoseconds
|
||||||
|
#define NS_PER_SEC (MS_PER_SEC * US_PER_MS * NS_PER_US)
|
||||||
|
|
||||||
|
int Timer::clock_gettime_monotonic(struct timespec* tv) {
|
||||||
|
static LARGE_INTEGER ticksPerSec;
|
||||||
|
LARGE_INTEGER ticks;
|
||||||
|
double seconds;
|
||||||
|
|
||||||
|
if (!ticksPerSec.QuadPart) {
|
||||||
|
QueryPerformanceFrequency(&ticksPerSec);
|
||||||
|
if (!ticksPerSec.QuadPart) {
|
||||||
|
errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryPerformanceCounter(&ticks);
|
||||||
|
|
||||||
|
seconds = (double)ticks.QuadPart / (double)ticksPerSec.QuadPart;
|
||||||
|
tv->tv_sec = (time_t)seconds;
|
||||||
|
tv->tv_nsec = (long)((ULONGLONG)(seconds * NS_PER_SEC) % NS_PER_SEC);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Timer::start() {
|
||||||
|
#ifdef __linux__
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &_startTime);
|
||||||
|
#elif _WIN32
|
||||||
|
clock_gettime_monotonic(&_startTime);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t Timer::getNs() {
|
||||||
|
struct timespec now = {};
|
||||||
|
#ifdef __linux__
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
#elif _WIN32
|
||||||
|
clock_gettime_monotonic(&now);
|
||||||
|
#endif
|
||||||
|
return (int64_t)(now.tv_nsec - _startTime.tv_nsec) +
|
||||||
|
1000000000 * (now.tv_sec - _startTime.tv_sec);
|
||||||
|
}
|
47
goalc/util/Timer.h
Normal file
47
goalc/util/Timer.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef JAK_V2_TIMER_H
|
||||||
|
#define JAK_V2_TIMER_H
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Timer for measuring time elapsed with clock_monotonic
|
||||||
|
*/
|
||||||
|
class Timer {
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* Construct and start timer
|
||||||
|
*/
|
||||||
|
explicit Timer() { start(); }
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int clock_gettime_monotonic(struct timespec* tv);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Start the timer
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get milliseconds elapsed
|
||||||
|
*/
|
||||||
|
double getMs() { return (double)getNs() / 1.e6; }
|
||||||
|
|
||||||
|
double getUs() { return (double)getNs() / 1.e3; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get nanoseconds elapsed
|
||||||
|
*/
|
||||||
|
int64_t getNs();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get seconds elapsed
|
||||||
|
*/
|
||||||
|
double getSeconds() { return (double)getNs() / 1.e9; }
|
||||||
|
|
||||||
|
struct timespec _startTime = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // JAK_V2_TIMER_H
|
|
@ -29,4 +29,17 @@ std::string combine_path(std::vector<std::string> path) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void write_binary_file(const std::string& name, void* data, size_t size) {
|
||||||
|
FILE* fp = fopen(name.c_str(), "wb");
|
||||||
|
if (!fp) {
|
||||||
|
throw std::runtime_error("couldn't open file " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(data, size, 1, fp) != 1) {
|
||||||
|
throw std::runtime_error("couldn't write file " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace util {
|
||||||
std::string read_text_file(const std::string& path);
|
std::string read_text_file(const std::string& path);
|
||||||
std::string combine_path(const std::string& parent, const std::string& child);
|
std::string combine_path(const std::string& parent, const std::string& child);
|
||||||
std::string combine_path(std::vector<std::string> path);
|
std::string combine_path(std::vector<std::string> path);
|
||||||
|
void write_binary_file(const std::string& name, void* data, size_t size);
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
#endif // JAK1_FILE_IO_H
|
#endif // JAK1_FILE_IO_H
|
||||||
|
|
|
@ -69,10 +69,10 @@ struct CompilerTestRunner {
|
||||||
int passed = 0;
|
int passed = 0;
|
||||||
for (auto& test : tests) {
|
for (auto& test : tests) {
|
||||||
if (test.expected == test.actual) {
|
if (test.expected == test.actual) {
|
||||||
fmt::print("[{:30}] PASS!\n", test.test_name);
|
fmt::print("[{:40}] PASS!\n", test.test_name);
|
||||||
passed++;
|
passed++;
|
||||||
} else {
|
} else {
|
||||||
fmt::print("[{:30}] FAIL!\n", test.test_name);
|
fmt::print("[{:40}] FAIL!\n", test.test_name);
|
||||||
fmt::print("expected:\n");
|
fmt::print("expected:\n");
|
||||||
for (auto& x : test.expected) {
|
for (auto& x : test.expected) {
|
||||||
fmt::print(" \"{}\"\n", escaped_string(x));
|
fmt::print(" \"{}\"\n", escaped_string(x));
|
||||||
|
@ -102,6 +102,15 @@ TEST(CompilerAndRuntime, CompilerTests) {
|
||||||
runner.run_test("test-return-integer-5.gc", {"-2147483649\n"});
|
runner.run_test("test-return-integer-5.gc", {"-2147483649\n"});
|
||||||
runner.run_test("test-return-integer-6.gc", {"0\n"});
|
runner.run_test("test-return-integer-6.gc", {"0\n"});
|
||||||
runner.run_test("test-return-integer-7.gc", {"-123\n"});
|
runner.run_test("test-return-integer-7.gc", {"-123\n"});
|
||||||
|
runner.run_test("test-conditional-compilation-1.gc", {"3\n"});
|
||||||
|
// todo, test-conditional-compilation-2.gc
|
||||||
|
// these numbers match the game's memory layout for where the symbol table lives.
|
||||||
|
// it's probably not 100% needed to get this exactly, but it's a good sign that the global
|
||||||
|
// heap lives in the right spot because there should be no divergence in memory layout when its
|
||||||
|
// built. This also checks that #t, #f get "hashed" to the correct spot.
|
||||||
|
runner.run_test("test-get-symbol-1.gc", {"1342756\n"}); // 0x147d24 in hex
|
||||||
|
runner.run_test("test-get-symbol-2.gc", {"1342764\n"}); // 0x147d2c in hex
|
||||||
|
runner.run_test("test-define-1.gc", {"17\n"});
|
||||||
|
|
||||||
compiler.shutdown_target();
|
compiler.shutdown_target();
|
||||||
runtime_thread.join();
|
runtime_thread.join();
|
||||||
|
|
Loading…
Reference in a new issue