aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-08-14 01:30:08 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commit664fbc5f5c947aaa04bbbf132d9c935959e34a9c (patch)
treefb25c4d6b8ccc5c6c7d02ad1170947096ff684e9
parentea97370f973374f863e4296c2bb872be8b5235a3 (diff)
Move program code generation and execution out of program (make it generic)
-rw-r--r--README.md4
-rwxr-xr-xbuild.sh5
-rw-r--r--doc/Documentation.md9
-rwxr-xr-xdoc/doc_extract.py2
-rw-r--r--executor/executor.h46
-rw-r--r--executor/x86_64/executor.c261
-rw-r--r--include/program.h9
-rw-r--r--src/program.c251
-rwxr-xr-xtools/highlevel_c.py2
9 files changed, 369 insertions, 220 deletions
diff --git a/README.md b/README.md
index fc7d054..a96972f 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,10 @@ Files have to be in utf-8 format and can optionally have utf-8 BOM.
Every stage of the compiler is multithreaded and data copy is kept to a minimal, for example tokenization
is done without storing tokens in a list. Almost all allocation is done using an arena allocator that
is only cleaned up once (when the compiler is finished), and the data is allocated sequentially.
+# Dependencies
+Right now only the C standard library (C89) is required for a release build. In the future dependency on the C standard library might be removed
+and then amalgam would have 0 dependencies.\
+python2 is needed for tests to run additional code analyzis.
# TODO
Build with -nostdlib and replace use of libc with syscalls (on linux).\
Don't parse files unless the variable they are assigned to (with @import) is used. This is useful when only using small parts of a library.
diff --git a/build.sh b/build.sh
index 26edf31..07a4ee2 100755
--- a/build.sh
+++ b/build.sh
@@ -60,7 +60,7 @@ build_compile_commands() {
build_test() {
CFLAGS="$CFLAGS -g -O0 -DDEBUG"
- BUILD_ARGS="$source_files $CFLAGS $LIBS -shared -fpic -o libamalgam.so"
+ BUILD_ARGS="$source_files $CFLAGS $LIBS -shared -fPIC -o libamalgam.so"
set -x
time $CC $BUILD_ARGS
if [ -n "$SCAN_BUILD" ]; then
@@ -80,7 +80,7 @@ build_test() {
build_release() {
CFLAGS="$CFLAGS -O2 -DNDEBUG -s"
- BUILD_ARGS="$source_files $CFLAGS $LIBS -shared -fpic -o libamalgam.so"
+ BUILD_ARGS="$source_files $CFLAGS $LIBS -shared -fPIC -o libamalgam.so"
set -x
time $CC $BUILD_ARGS
if [ -n "$SCAN_BUILD" ]; then
@@ -94,6 +94,7 @@ build_release() {
case "$1" in
"test")
build_test
+ ./tools/highlevel_c.py
;;
"release")
build_release
diff --git a/doc/Documentation.md b/doc/Documentation.md
index 2fcfe07..602d0e1 100644
--- a/doc/Documentation.md
+++ b/doc/Documentation.md
@@ -68,4 +68,11 @@ The versions in the header only changes for every release, not every change.
|Type |Field |Description |
|-----------|-----------------|---------------------------------------------------------------------------|
|u32 |Instructions size|The size of the instructions section, in bytes. |
-|Instruction|Instructions data|The instructions data. Each instructions begins with an opcode, see #Opcode| \ No newline at end of file
+|Instruction|Instructions data|The instructions data. Each instructions begins with an opcode, see #Opcode|
+
+# Execution backend
+Amalgam supports multiple execution backend and they can be implemented with minimal
+effort. The only requirement is implementation of all the functions in executor/executor.h
+and adding the source file with the implementation to the build script. See executor/interpreter/executor.c
+as an example.\
+These functions are then called by amalgam as amalgam parses the amalgam bytecode when `amal_program_run` is called. \ No newline at end of file
diff --git a/doc/doc_extract.py b/doc/doc_extract.py
index ce192cb..1ce7dc9 100755
--- a/doc/doc_extract.py
+++ b/doc/doc_extract.py
@@ -69,9 +69,11 @@ def main():
amalgam_base = os.path.dirname(script_dir)
amalgam_includes = os.path.join(amalgam_base, "include")
amalgam_sources = os.path.join(amalgam_base, "src")
+ amalgam_executor_sources = os.path.join(amalgam_base, "executor")
source_files = get_source_files_recursive(amalgam_includes)
source_files += get_source_files_recursive(amalgam_sources)
+ source_files += get_source_files_recursive(amalgam_executor_sources)
doc_data = []
for filepath in source_files:
docs = extract_docs(filepath)
diff --git a/executor/executor.h b/executor/executor.h
new file mode 100644
index 0000000..fdf6e67
--- /dev/null
+++ b/executor/executor.h
@@ -0,0 +1,46 @@
+#ifndef AMAL_EXECUTOR_H
+#define AMAL_EXECUTOR_H
+
+#include "../include/std/misc.h"
+#include "../include/std/types.h"
+#include "../include/std/buffer_view.h"
+
+/*doc(Execution backend)
+Amalgam supports multiple execution backend and they can be implemented with minimal
+effort. The only requirement is implementation of all the functions in executor/executor.h
+and adding the source file with the implementation to the build script. See executor/interpreter/executor.c
+as an example.\
+These functions are then called by amalgam as amalgam parses the amalgam bytecode when `amal_program_run` is called.
+*/
+
+struct amal_executor_impl;
+typedef struct amal_executor amal_executor;
+
+CHECK_RESULT int amal_executor_init(amal_executor **self);
+void amal_executor_deinit(amal_executor *self);
+CHECK_RESULT int amal_executor_run(amal_executor *self);
+
+CHECK_RESULT int amal_exec_nop(amal_executor *self);
+CHECK_RESULT int amal_exec_setz(amal_executor *self, u8 dst_reg);
+CHECK_RESULT int amal_exec_mov(amal_executor *self, u8 dst_reg, u8 src_reg);
+CHECK_RESULT int amal_exec_movi(amal_executor *self, u8 dst_reg, i64 imm);
+CHECK_RESULT int amal_exec_movd(amal_executor *self, u8 dst_reg, BufferView data);
+CHECK_RESULT int amal_exec_add(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
+CHECK_RESULT int amal_exec_sub(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
+CHECK_RESULT int amal_exec_imul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
+CHECK_RESULT int amal_exec_mul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
+CHECK_RESULT int amal_exec_idiv(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
+CHECK_RESULT int amal_exec_div(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
+CHECK_RESULT int amal_exec_push(amal_executor *self, u8 reg);
+CHECK_RESULT int amal_exec_pushi(amal_executor *self, i64 imm);
+CHECK_RESULT int amal_exec_pushd(amal_executor *self, BufferView data);
+/*CHECK_RESULT int amal_exec_call(u8 dst_reg, BufferView data);
+CHECK_RESULT int amal_exec_callr(u8 dst_reg, BufferView data);*/
+CHECK_RESULT int amal_exec_cmp(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
+CHECK_RESULT int amal_exec_jz(amal_executor *self, u8 dst_reg, i16 offset);
+CHECK_RESULT int amal_exec_jmp(amal_executor *self, i16 offset);
+CHECK_RESULT int amal_exec_ret(amal_executor *self);
+CHECK_RESULT int amal_exec_func_start(amal_executor *self, u16 num_regs);
+CHECK_RESULT int amal_exec_func_end(amal_executor *self);
+
+#endif
diff --git a/executor/x86_64/executor.c b/executor/x86_64/executor.c
new file mode 100644
index 0000000..b53ccea
--- /dev/null
+++ b/executor/x86_64/executor.c
@@ -0,0 +1,261 @@
+#include "../executor.h"
+#include "../../include/std/alloc.h"
+#include "asm.h"
+#include <assert.h>
+
+/*
+ TODO: Currently almost all operations are performed on memory. This should be optimized
+ to take advantage of registers.
+
+ TODO: Operations with memory registers could access outside the stack. Should this be checked?
+*/
+
+typedef struct {
+ Asm asm;
+} amal_executor_impl;
+
+#define IMPL \
+ amal_executor_impl *impl; \
+ impl = (amal_executor_impl*)self;
+
+#define get_register_stack_offset(reg) -(i32)(reg * (int)sizeof(usize) + (int)sizeof(usize))
+
+static i64 abs_i64(i64 value) {
+ return value >= 0 ? value : -value;
+}
+
+int amal_executor_init(amal_executor **self) {
+ amal_executor_impl **impl;
+ return_if_error(am_malloc(sizeof(amal_executor_impl), (void**)self));
+ impl = (amal_executor_impl**)self;
+ return asm_init(&(*impl)->asm);
+}
+
+void amal_executor_deinit(amal_executor *self) {
+ IMPL
+ asm_deinit(&impl->asm);
+ am_free(impl);
+}
+
+int amal_executor_run(amal_executor *self) {
+ IMPL
+ return asm_execute(&impl->asm);
+}
+
+int amal_exec_nop(amal_executor *self) {
+ IMPL
+ return asm_nop(&impl->asm);
+}
+
+int amal_exec_setz(amal_executor *self, u8 dst_reg) {
+ AsmPtr dst;
+ IMPL
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ return asm_mov_mi(&impl->asm, &dst, 0);
+}
+
+int amal_exec_mov(amal_executor *self, u8 dst_reg, u8 src_reg) {
+ AsmPtr ptr;
+ IMPL
+
+ asm_ptr_init_disp(&ptr, RBP, get_register_stack_offset(src_reg));
+ return_if_error(asm_mov_rm(&impl->asm, RAX, &ptr));
+
+ asm_ptr_init_disp(&ptr, RBP, get_register_stack_offset(dst_reg));
+ return asm_mov_mr(&impl->asm, &ptr, RAX);
+}
+
+int amal_exec_movi(amal_executor *self, u8 dst_reg, i64 imm) {
+ IMPL
+ /* TODO: if @number is a float then use float instructions */
+ if(abs_i64(imm) <= INT32_MAX) {
+ AsmPtr dst;
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ return_if_error(asm_mov_mi(&impl->asm, &dst, imm));
+ } else {
+ AsmPtr dst;
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ return_if_error(asm_mov_ri(&impl->asm, RAX, imm));
+ return_if_error(asm_mov_mr(&impl->asm, &dst, RAX));
+ }
+ return 0;
+}
+
+int amal_exec_movd(amal_executor *self, u8 dst_reg, BufferView data) {
+ AsmPtr dst;
+ IMPL
+
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ return_if_error(asm_mov_ri(&impl->asm, RAX, (uintptr_t)data.data));
+ return asm_mov_mr(&impl->asm, &dst, RAX);
+}
+
+int amal_exec_add(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ AsmPtr dst;
+ AsmPtr reg1;
+ AsmPtr reg2;
+ IMPL
+
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ asm_ptr_init_disp(&reg1, RBP, get_register_stack_offset(src_reg1));
+ asm_ptr_init_disp(&reg2, RBP, get_register_stack_offset(src_reg2));
+
+ return_if_error(asm_mov_rm(&impl->asm, RAX, &reg1));
+ return_if_error(asm_mov_rm(&impl->asm, RCX, &reg2));
+ return_if_error(asm_add_rr(&impl->asm, RAX, RCX));
+ return asm_mov_mr(&impl->asm, &dst, RAX);
+}
+
+int amal_exec_sub(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ AsmPtr dst;
+ AsmPtr reg1;
+ AsmPtr reg2;
+ IMPL
+
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ asm_ptr_init_disp(&reg1, RBP, get_register_stack_offset(src_reg1));
+ asm_ptr_init_disp(&reg2, RBP, get_register_stack_offset(src_reg2));
+
+ return_if_error(asm_mov_rm(&impl->asm, RAX, &reg1));
+ return_if_error(asm_mov_rm(&impl->asm, RCX, &reg2));
+ return_if_error(asm_sub_rr(&impl->asm, RAX, RCX));
+ return asm_mov_mr(&impl->asm, &dst, RAX);
+}
+
+int amal_exec_imul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ AsmPtr dst;
+ AsmPtr reg1;
+ AsmPtr reg2;
+ IMPL
+
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ asm_ptr_init_disp(&reg1, RBP, get_register_stack_offset(src_reg1));
+ asm_ptr_init_disp(&reg2, RBP, get_register_stack_offset(src_reg2));
+
+ return_if_error(asm_mov_rm(&impl->asm, RAX, &reg1));
+ return_if_error(asm_mov_rm(&impl->asm, RCX, &reg2));
+ return_if_error(asm_imul_rr(&impl->asm, RAX, RCX));
+ return asm_mov_mr(&impl->asm, &dst, RAX);
+}
+
+int amal_exec_mul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ (void)self;
+ (void)dst_reg;
+ (void)src_reg1;
+ (void)src_reg2;
+ /* TODO: Implement! */
+ #if 0
+ AsmPtr dst;
+ AsmPtr reg1;
+ AsmPtr reg2;
+
+ asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
+ asm_ptr_init_disp(&reg1, RBP, -(i32)get_register_at_offset(1));
+ asm_ptr_init_disp(&reg2, RBP, -(i32)get_register_at_offset(2));
+
+ return_if_error(asm_mov_rm(&self->asm, RAX, &reg1));
+ return_if_error(asm_mov_rm(&self->asm, RCX, &reg2));
+ return_if_error(asm_mul_rr(&self->asm, RAX, RCX));
+ return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
+ #endif
+ return 0;
+}
+
+int amal_exec_idiv(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ (void)self;
+ (void)dst_reg;
+ (void)src_reg1;
+ (void)src_reg2;
+ /* TODO: Implement! */
+ return 0;
+}
+
+int amal_exec_div(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ (void)self;
+ (void)dst_reg;
+ (void)src_reg1;
+ (void)src_reg2;
+ /* TODO: Implement! */
+ return 0;
+}
+
+int amal_exec_push(amal_executor *self, u8 reg) {
+ (void)self;
+ (void)reg;
+ /* TODO: Implement! */
+ return 0;
+}
+
+int amal_exec_pushi(amal_executor *self, i64 imm) {
+ (void)self;
+ (void)imm;
+ /* TODO: Implement! */
+ return 0;
+}
+
+int amal_exec_pushd(amal_executor *self, BufferView data) {
+ (void)self;
+ (void)data;
+ /* TODO: Implement! */
+ return 0;
+}
+
+/*int amal_exec_call(u8 dst_reg, BufferView data);
+int amal_exec_callr(u8 dst_reg, BufferView data);*/
+int amal_exec_cmp(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ (void)self;
+ (void)dst_reg;
+ (void)src_reg1;
+ (void)src_reg2;
+ /* TODO: Implement! */
+ return 0;
+}
+
+int amal_exec_jz(amal_executor *self, u8 dst_reg, i16 offset) {
+ (void)self;
+ (void)dst_reg;
+ (void)offset;
+ /* TODO: Implement! */
+ return 0;
+}
+
+int amal_exec_jmp(amal_executor *self, i16 offset) {
+ (void)self;
+ (void)offset;
+ /* TODO: Implement! */
+ return 0;
+}
+
+int amal_exec_ret(amal_executor *self) {
+ (void)self;
+ /* TODO: Implement! */
+ assert(bool_false && "TODO: Implement RET. RET needs to restore the stack before returning");
+ return 0;
+}
+
+int amal_exec_func_start(amal_executor *self, u16 num_regs) {
+ /*
+ TODO: Validate stack size, or maybe remove all validation? do we really need validation?
+ If we need security, we could fork the process instead.
+ */
+
+ /*
+ Some registers need to be preserved before entering a function scope and these registers are different on different platforms.
+ 32-bit: EBX, ESI, EDI, EBP
+ 64-bit Windows: RBX, RSI, RDI, RBP, R12-R15, XMM6-XMM15
+ 64-bit Linux,BSD,Mac: RBX, RBP, R12-R15
+ */
+ IMPL
+ return_if_error(asm_pushr(&impl->asm, RBX));
+ return_if_error(asm_pushr(&impl->asm, RBP));
+ return_if_error(asm_mov_rr(&impl->asm, RBP, RSP));
+ return asm_sub_rm64_imm(&impl->asm, RSP, num_regs * sizeof(usize));
+}
+
+int amal_exec_func_end(amal_executor *self) {
+ IMPL
+ return_if_error(asm_mov_rr(&impl->asm, RSP, RBP));
+ return_if_error(asm_popr(&impl->asm, RBP));
+ return_if_error(asm_popr(&impl->asm, RBX));
+ return asm_ret(&impl->asm, 0);
+}
diff --git a/include/program.h b/include/program.h
index a0ed4ed..b98c0a6 100644
--- a/include/program.h
+++ b/include/program.h
@@ -3,7 +3,7 @@
#include "std/buffer.h"
#include "bytecode/bytecode.h"
-#include "../executor/x86_64/asm.h"
+#include "../executor/executor.h"
#define AMAL_PROGRAM_OK 0
#define AMAL_PROGRAM_INVALID_HEADER -1
@@ -38,13 +38,6 @@ typedef struct {
u16 num_strings;
u16 num_intermediates;
-
- u64 reg[AMAL_PROGRAM_NUM_REGISTERS];
- u64 *stack;
- usize stack_size;
- usize stack_index;
-
- Asm asm;
} amal_program;
CHECK_RESULT int amal_program_init(amal_program *self);
diff --git a/src/program.c b/src/program.c
index 60e9870..54709b1 100644
--- a/src/program.c
+++ b/src/program.c
@@ -59,13 +59,6 @@ int amal_program_init(amal_program *self) {
self->read_index = 0;
self->num_strings = 0;
self->num_intermediates = 0;
- am_memset(self->reg, 0, sizeof(self->reg));
- self->stack = NULL;
- self->stack_size = 4096;
- am_memset(&self->asm, 0, sizeof(self->asm));
- cleanup_if_error(am_malloc(self->stack_size, (void**)&self->stack));
- cleanup_if_error(asm_init(&self->asm));
- self->stack_index = 0;
cleanup_if_error(amal_program_append_header(self));
return 0;
@@ -79,9 +72,6 @@ void amal_program_deinit(amal_program *self) {
buffer_deinit(&self->data);
am_free(self->string_indices);
self->string_indices = NULL;
- am_free(self->stack);
- self->stack = NULL;
- asm_deinit(&self->asm);
}
int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode) {
@@ -192,7 +182,7 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) {
if(bytes_left_to_read(self) < string_size)
return AMAL_PROGRAM_INVALID_STRINGS;
- self->read_index += string_size +1; /* +1 to skip null-termination character */
+ self->read_index += string_size + 1; /* +1 to skip null-termination character */
}
assert(self->read_index == read_end);
@@ -208,7 +198,7 @@ static CHECK_RESULT int amal_program_get_intermediate_by_index(amal_program *sel
return 0;
}
-static CHECK_RESULT int amal_program_get_data_by_index(amal_program *self, u16 index, char **result) {
+static CHECK_RESULT int amal_program_get_data_by_index(amal_program *self, u16 index, BufferView *result) {
char *str_ptr;
if(index >= self->num_strings) {
@@ -217,41 +207,17 @@ static CHECK_RESULT int amal_program_get_data_by_index(amal_program *self, u16 i
}
str_ptr = self->strings_start + self->string_indices[index];
- am_memcpy(result, &str_ptr, sizeof(char**));
+ am_memcpy(&result->size, str_ptr, sizeof(u16));
+ result->data = str_ptr + sizeof(u16);
return 0;
}
-static CHECK_RESULT int ensure_stack_capacity_for_push(amal_program *self) {
- if(self->stack_index >= self->stack_size) {
- self->stack_size *= 2;
- /* 4MB */
- if(self->stack_size >= (1<<22))
- return AMAL_PROGRAM_INSTRUCTION_STACK_OVERFLOW;
- if(am_realloc(self->stack, self->stack_size, (void**)&self->stack) != 0)
- return AMAL_PROGRAM_INSTRUCTION_STACK_OOM;
- }
- return 0;
-}
-
-static i64 abs_i64(i64 value) {
- return value >= 0 ? value : -value;
-}
-
-#ifdef DEBUG
-static int assert_reg_outside_stack() {
- assert(bool_false && "Register outside stack!");
- return 0;
-}
-#endif
-
-static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
+static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_executor *executor) {
u32 instructions_size;
u32 read_start;
u32 read_end;
bool inside_func;
- u16 func_num_registers;
- func_num_registers = 0;
inside_func = bool_false;
(void)inside_func;
@@ -267,20 +233,8 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
/*
TODO: self->reg should be of type Number and each arithmetic operation should operate
on the type of the register.
-
- TODO: Currently almost all operations are performed on memory. This should be optimized
- to take advantage of registers.
-
- TODO: Operations with memory registers could access outside the stack. Should this be checked?
*/
- #ifdef DEBUG
- #define get_register_at_offset(offset) \
- (self->data.data[self->read_index + (offset)] < func_num_registers ? self->data.data[self->read_index + (offset)] * (int)sizeof(usize) + (int)sizeof(usize) : assert_reg_outside_stack())
- #else
- #define get_register_at_offset(offset) (self->data.data[self->read_index + (offset)] * (int)sizeof(usize) + (int)sizeof(usize))
- #endif
-
read_start = self->read_index;
read_end = read_start + instructions_size;
while(self->read_index < read_end) {
@@ -289,162 +243,74 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
self->read_index += sizeof(AmalOpcodeType);
switch(opcode) {
case AMAL_OP_NOP: {
- return_if_error(asm_nop(&self->asm));
+ return_if_error(amal_exec_nop(executor));
break;
}
case AMAL_OP_SETZ: {
- AsmPtr dst;
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- return_if_error(asm_mov_mi(&self->asm, &dst, 0));
- self->reg[(u8)self->data.data[self->read_index]] = 0;
+ return_if_error(amal_exec_setz(executor, self->data.data[self->read_index]));
self->read_index += 1;
break;
}
case AMAL_OP_MOV: {
- AsmPtr ptr;
- asm_ptr_init_disp(&ptr, RBP, -(i32)get_register_at_offset(1));
- return_if_error(asm_mov_rm(&self->asm, RAX, &ptr));
-
- asm_ptr_init_disp(&ptr, RBP, -(i32)get_register_at_offset(0));
- return_if_error(asm_mov_mr(&self->asm, &ptr, RAX));
-
- self->reg[(u8)self->data.data[self->read_index]] = self->reg[(u8)self->data.data[self->read_index + 1]];
+ return_if_error(amal_exec_mov(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1]));
self->read_index += 2;
break;
}
case AMAL_OP_MOVI: {
- u8 dst_reg;
u16 intermediate_index;
Number number;
- dst_reg = self->reg[(u8)self->data.data[self->read_index]];
am_memcpy(&intermediate_index, &self->data.data[self->read_index + sizeof(u8)], sizeof(intermediate_index));
-
return_if_error(amal_program_get_intermediate_by_index(self, intermediate_index, &number));
- self->reg[dst_reg] = number.value.integer;
-
- /* TODO: if @number is a float then use float instructions */
- if(abs_i64(number.value.integer) <= INT32_MAX) {
- AsmPtr dst;
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- return_if_error(asm_mov_mi(&self->asm, &dst, number.value.integer));
- } else {
- AsmPtr dst;
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- return_if_error(asm_mov_ri(&self->asm, RAX, number.value.integer));
- return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
- }
+ return_if_error(amal_exec_movi(executor, self->data.data[self->read_index], number.value.integer));
self->read_index += 3;
break;
}
case AMAL_OP_MOVD: {
- u8 dst_reg;
u16 data_index;
- char *data_ptr;
- AsmPtr dst;
+ BufferView data_ptr;
- dst_reg = self->reg[(u8)self->data.data[self->read_index]];
am_memcpy(&data_index, &self->data.data[self->read_index + sizeof(u8)], sizeof(data_index));
return_if_error(amal_program_get_data_by_index(self, data_index, &data_ptr));
- self->reg[dst_reg] = (uintptr_t)data_ptr;
-
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- return_if_error(asm_mov_ri(&self->asm, RAX, (uintptr_t)data_ptr));
- return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
+ return_if_error(amal_exec_movd(executor, self->data.data[self->read_index], data_ptr));
self->read_index += 3;
break;
}
case AMAL_OP_ADD: {
- AsmPtr dst;
- AsmPtr reg1;
- AsmPtr reg2;
-
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- asm_ptr_init_disp(&reg1, RBP, -(i32)get_register_at_offset(1));
- asm_ptr_init_disp(&reg2, RBP, -(i32)get_register_at_offset(2));
-
- return_if_error(asm_mov_rm(&self->asm, RAX, &reg1));
- return_if_error(asm_mov_rm(&self->asm, RCX, &reg2));
- return_if_error(asm_add_rr(&self->asm, RAX, RCX));
- return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
-
+ return_if_error(amal_exec_add(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_SUB: {
- AsmPtr dst;
- AsmPtr reg1;
- AsmPtr reg2;
-
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- asm_ptr_init_disp(&reg1, RBP, -(i32)get_register_at_offset(1));
- asm_ptr_init_disp(&reg2, RBP, -(i32)get_register_at_offset(2));
-
- return_if_error(asm_mov_rm(&self->asm, RAX, &reg1));
- return_if_error(asm_mov_rm(&self->asm, RCX, &reg2));
- return_if_error(asm_sub_rr(&self->asm, RAX, RCX));
- return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
-
+ return_if_error(amal_exec_sub(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_IMUL: {
- AsmPtr dst;
- AsmPtr reg1;
- AsmPtr reg2;
-
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- asm_ptr_init_disp(&reg1, RBP, -(i32)get_register_at_offset(1));
- asm_ptr_init_disp(&reg2, RBP, -(i32)get_register_at_offset(2));
-
- return_if_error(asm_mov_rm(&self->asm, RAX, &reg1));
- return_if_error(asm_mov_rm(&self->asm, RCX, &reg2));
- return_if_error(asm_imul_rr(&self->asm, RAX, RCX));
- return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
-
+ return_if_error(amal_exec_imul(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_MUL: {
- #if 0
- AsmPtr dst;
- AsmPtr reg1;
- AsmPtr reg2;
-
- asm_ptr_init_disp(&dst, RBP, -(i32)get_register_at_offset(0));
- asm_ptr_init_disp(&reg1, RBP, -(i32)get_register_at_offset(1));
- asm_ptr_init_disp(&reg2, RBP, -(i32)get_register_at_offset(2));
-
- return_if_error(asm_mov_rm(&self->asm, RAX, &reg1));
- return_if_error(asm_mov_rm(&self->asm, RCX, &reg2));
- return_if_error(asm_mul_rr(&self->asm, RAX, RCX));
- return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
- #endif
-
+ return_if_error(amal_exec_mul(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_IDIV: {
+ return_if_error(amal_exec_idiv(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_DIV: {
+ return_if_error(amal_exec_div(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_PUSH: {
- #if 0
- AsmPtr reg;
- asm_ptr_init_disp(&reg, RBP, -(i32)get_register_at_offset(0));
-
- return_if_error(ensure_stack_capacity_for_push(self));
- self->stack[self->stack_index] = self->reg[(u8)self->data.data[self->read_index]];
- ++self->stack_index;
- return_if_error(asm_pushm(&self->asm, &reg));
- #endif
+ return_if_error(amal_exec_push(executor, self->data.data[self->read_index]));
self->read_index += 1;
break;
}
@@ -454,23 +320,17 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
am_memcpy(&intermediate_index, &self->data.data[self->read_index], sizeof(intermediate_index));
return_if_error(amal_program_get_intermediate_by_index(self, intermediate_index, &number));
- return_if_error(ensure_stack_capacity_for_push(self));
-
- self->stack[self->stack_index] = number.value.integer;
- ++self->stack_index;
+ return_if_error(amal_exec_pushi(executor, number.value.integer));
self->read_index += 2;
break;
}
case AMAL_OP_PUSHD: {
u16 data_index;
- char *data_ptr;
+ BufferView data_ptr;
am_memcpy(&data_index, &self->data.data[self->read_index], sizeof(data_index));
return_if_error(amal_program_get_data_by_index(self, data_index, &data_ptr));
- return_if_error(ensure_stack_capacity_for_push(self));
-
- self->stack[self->stack_index] = (uintptr_t)data_ptr;
- ++self->stack_index;
+ return_if_error(amal_exec_pushd(executor, data_ptr));
self->read_index += 2;
break;
}
@@ -483,75 +343,41 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
self->read_index += 2;
break;
case AMAL_OP_CMP: {
- self->reg[(u8)self->data.data[self->read_index]] =
- self->reg[(u8)self->data.data[self->read_index + 1]] == self->reg[(u8)self->data.data[self->read_index + 2]];
+ return_if_error(amal_exec_cmp(executor, self->data.data[self->read_index], self->data.data[self->read_index + 1], self->data.data[self->read_index + 2]));
self->read_index += 3;
break;
}
case AMAL_OP_JZ: {
- #if 0
- u8 reg;
i16 jump_offset;
- u32 jump_target;
-
- reg = (u8)self->data.data[self->read_index];
- am_memcpy(&jump_offset, &self->data.data[self->read_index + 1], sizeof(jump_offset));
- jump_target = (isize)self->read_index + jump_offset;
- if(jump_target < read_start || jump_target >= read_end)
- return AMAL_PROGRAM_INSTRUCTION_ILLEGAL_JUMP_TARGET;
- #endif
+ am_memcpy(&jump_offset, &self->data.data[self->read_index + sizeof(u8)], sizeof(jump_offset));
+ return_if_error(amal_exec_jz(executor, self->data.data[self->read_index], jump_offset));
self->read_index += 3;
break;
}
case AMAL_OP_JMP: {
- #if 0
i16 jump_offset;
- u32 jump_target;
-
am_memcpy(&jump_offset, &self->data.data[self->read_index], sizeof(jump_offset));
- jump_target = (isize)self->read_index + jump_offset;
- if(jump_target < read_start || jump_target >= read_end)
- return AMAL_PROGRAM_INSTRUCTION_ILLEGAL_JUMP_TARGET;
- #endif
+ return_if_error(amal_exec_jmp(executor, jump_offset));
self->read_index += 2;
break;
}
case AMAL_OP_RET:
- /* return_if_error(asm_ret(&self->asm, 0)); */
- assert(bool_false && "TODO: Implement RET. RET needs to restore the stack before returning");
+ return_if_error(amal_exec_ret(executor));
break;
case AMAL_OP_FUNC_START: {
+ u16 func_num_registers;
assert(!inside_func);
inside_func = bool_true;
am_memcpy(&func_num_registers, &self->data.data[self->read_index], sizeof(func_num_registers));
- /*
- TODO: Validate stack size, or maybe remove all validation? do we really need validation?
- If we need security, we could fork the process instead.
- */
-
- /*
- Some registers need to be preserved before entering a function scope and these registers are different on different platforms.
- 32-bit: EBX, ESI, EDI, EBP
- 64-bit Windows: RBX, RSI, RDI, RBP, R12-R15, XMM6-XMM15
- 64-bit Linux,BSD,Mac: RBX, RBP, R12-R15
- */
- return_if_error(asm_pushr(&self->asm, RBX));
- return_if_error(asm_pushr(&self->asm, RBP));
- return_if_error(asm_mov_rr(&self->asm, RBP, RSP));
- return_if_error(asm_sub_rm64_imm(&self->asm, RSP, func_num_registers * sizeof(usize)));
+ return_if_error(amal_exec_func_start(executor, func_num_registers));
self->read_index += 2;
break;
}
case AMAL_OP_FUNC_END: {
assert(inside_func);
inside_func = bool_false;
- /*assert(bool_false && "TODO: Implement FUNC_END");*/
/* TODO: Validate FUNC_END is called for every FUNC_START, otherwise stack will be corrupted */
- /* TODO: Use mov_rr(RSP, RBP) instead? why doesn't gcc do this? */
- return_if_error(asm_mov_rr(&self->asm, RSP, RBP));
- return_if_error(asm_popr(&self->asm, RBP));
- return_if_error(asm_popr(&self->asm, RBX));
- return_if_error(asm_ret(&self->asm, 0));
+ return_if_error(amal_exec_func_end(executor));
break;
}
}
@@ -561,13 +387,22 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
}
int amal_program_run(amal_program *self) {
- return_if_error(amal_program_read_header(self));
+ int result;
+ amal_executor *executor;
+ result = 0;
+ return_if_error(amal_executor_init(&executor));
+
+ cleanup_if_error(amal_program_read_header(self));
while(bytes_left_to_read(self) > 0) {
- return_if_error(amal_program_read_intermediates(self));
- return_if_error(amal_program_read_strings(self));
- return_if_error(amal_program_read_instructions(self));
+ cleanup_if_error(amal_program_read_intermediates(self));
+ cleanup_if_error(amal_program_read_strings(self));
+ cleanup_if_error(amal_program_read_instructions(self, executor));
}
- return asm_execute(&self->asm);
+ result = amal_executor_run(executor);
+
+ cleanup:
+ amal_executor_deinit(executor);
+ return result;
}
int amal_program_save(amal_program *self, const char *filepath) {
diff --git a/tools/highlevel_c.py b/tools/highlevel_c.py
index 2cff3b3..b1c2dc3 100755
--- a/tools/highlevel_c.py
+++ b/tools/highlevel_c.py
@@ -72,7 +72,7 @@ def main():
idx = clang.cindex.Index.create()
for filepath in compile_commands_get_files(compile_commands_file):
- print("Parsing file: %s" % filepath)
+ #print("Parsing file: %s" % filepath)
tu = idx.parse(filepath, args=['-std=c89'], options=0)
parse(tu.cursor)