diff options
-rw-r--r-- | README.md | 4 | ||||
-rwxr-xr-x | build.sh | 5 | ||||
-rw-r--r-- | doc/Documentation.md | 9 | ||||
-rwxr-xr-x | doc/doc_extract.py | 2 | ||||
-rw-r--r-- | executor/executor.h | 46 | ||||
-rw-r--r-- | executor/x86_64/executor.c | 261 | ||||
-rw-r--r-- | include/program.h | 9 | ||||
-rw-r--r-- | src/program.c | 251 | ||||
-rwxr-xr-x | tools/highlevel_c.py | 2 |
9 files changed, 369 insertions, 220 deletions
@@ -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. @@ -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(®1, RBP, get_register_stack_offset(src_reg1)); + asm_ptr_init_disp(®2, RBP, get_register_stack_offset(src_reg2)); + + return_if_error(asm_mov_rm(&impl->asm, RAX, ®1)); + return_if_error(asm_mov_rm(&impl->asm, RCX, ®2)); + 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(®1, RBP, get_register_stack_offset(src_reg1)); + asm_ptr_init_disp(®2, RBP, get_register_stack_offset(src_reg2)); + + return_if_error(asm_mov_rm(&impl->asm, RAX, ®1)); + return_if_error(asm_mov_rm(&impl->asm, RCX, ®2)); + 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(®1, RBP, get_register_stack_offset(src_reg1)); + asm_ptr_init_disp(®2, RBP, get_register_stack_offset(src_reg2)); + + return_if_error(asm_mov_rm(&impl->asm, RAX, ®1)); + return_if_error(asm_mov_rm(&impl->asm, RCX, ®2)); + 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(®1, RBP, -(i32)get_register_at_offset(1)); + asm_ptr_init_disp(®2, RBP, -(i32)get_register_at_offset(2)); + + return_if_error(asm_mov_rm(&self->asm, RAX, ®1)); + return_if_error(asm_mov_rm(&self->asm, RCX, ®2)); + 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(®1, RBP, -(i32)get_register_at_offset(1)); - asm_ptr_init_disp(®2, RBP, -(i32)get_register_at_offset(2)); - - return_if_error(asm_mov_rm(&self->asm, RAX, ®1)); - return_if_error(asm_mov_rm(&self->asm, RCX, ®2)); - 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(®1, RBP, -(i32)get_register_at_offset(1)); - asm_ptr_init_disp(®2, RBP, -(i32)get_register_at_offset(2)); - - return_if_error(asm_mov_rm(&self->asm, RAX, ®1)); - return_if_error(asm_mov_rm(&self->asm, RCX, ®2)); - 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(®1, RBP, -(i32)get_register_at_offset(1)); - asm_ptr_init_disp(®2, RBP, -(i32)get_register_at_offset(2)); - - return_if_error(asm_mov_rm(&self->asm, RAX, ®1)); - return_if_error(asm_mov_rm(&self->asm, RCX, ®2)); - 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(®1, RBP, -(i32)get_register_at_offset(1)); - asm_ptr_init_disp(®2, RBP, -(i32)get_register_at_offset(2)); - - return_if_error(asm_mov_rm(&self->asm, RAX, ®1)); - return_if_error(asm_mov_rm(&self->asm, RCX, ®2)); - 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(®, 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, ®)); - #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) |