aboutsummaryrefslogtreecommitdiff
path: root/src/program.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/program.c')
-rw-r--r--src/program.c368
1 files changed, 338 insertions, 30 deletions
diff --git a/src/program.c b/src/program.c
index 167f4c4..5e63c04 100644
--- a/src/program.c
+++ b/src/program.c
@@ -2,6 +2,7 @@
#include "../include/std/mem.h"
#include "../include/std/alloc.h"
#include "../include/std/log.h"
+#include "../include/std/buffer_view.h"
#include <stdio.h>
#include <errno.h>
#include <assert.h>
@@ -19,13 +20,12 @@ typedef union {
f64 floating;
} NumberUnion;
-int amal_program_init(amal_program *self) {
- ignore_result_int(buffer_init(&self->data, NULL));
- ignore_result_int(buffer_init(&self->string_indices, NULL));
- self->intermediates_start = NULL;
- self->strings_start = NULL;
- self->read_index = 0;
+typedef struct {
+ NumberType type;
+ NumberUnion value;
+} Number;
+static CHECK_RESULT int amal_program_append_header(amal_program *self) {
/*doc(Bytecode header)
# Header layout
|Size|Name |Description |
@@ -50,9 +50,37 @@ int amal_program_init(amal_program *self) {
return 0;
}
+int amal_program_init(amal_program *self) {
+ ignore_result_int(buffer_init(&self->data, NULL));
+ self->string_indices = NULL;
+ self->intermediates_start = NULL;
+ self->strings_start = NULL;
+ 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;
+
+ cleanup:
+ amal_program_deinit(self);
+ return -1;
+}
+
void amal_program_deinit(amal_program *self) {
buffer_deinit(&self->data);
- buffer_deinit(&self->string_indices);
+ 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) {
@@ -113,6 +141,8 @@ static CHECK_RESULT int amal_program_read_intermediates(amal_program *self) {
}
self->intermediates_start = &self->data.data[self->read_index];
+ if(intermediates_size > 0)
+ self->num_intermediates = intermediates_size / (sizeof(u8) + sizeof(u64));
/*
read_end = self->read_index + intermediates_size;
while(self->read_index < read_end) {
@@ -140,10 +170,11 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) {
am_memcpy(&num_strings, &self->data.data[self->read_index], sizeof(num_strings));
self->read_index += sizeof(num_strings);
+ self->num_strings = num_strings;
- if(buffer_append_empty(&self->string_indices, num_strings) != 0)
+ if(am_malloc(sizeof(u32) * num_strings, (void**)&self->string_indices) != 0)
return AMAL_PROGRAM_STRING_ALLOC_FAILURE;
- string_index_ptr = (u32*)self->string_indices.data;
+ string_index_ptr = self->string_indices;
if(bytes_left_to_read(self) < sizeof(strings_size))
return AMAL_PROGRAM_INVALID_STRINGS;
@@ -178,9 +209,58 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) {
return AMAL_PROGRAM_OK;
}
+static CHECK_RESULT int amal_program_get_intermediate_by_index(amal_program *self, u16 index, Number *result) {
+ if(index >= self->num_intermediates)
+ return AMAL_PROGRAM_INSTRUCTION_INVALID_INTERMEDIATE_INDEX;
+
+ am_memcpy(&result->type, &self->intermediates_start[(sizeof(u8) + sizeof(u64)) * (usize)index], sizeof(u8));
+ am_memcpy(&result->value, &self->intermediates_start[(sizeof(u8) + sizeof(u64)) * (usize)index + sizeof(u8)], sizeof(u64));
+ return 0;
+}
+
+static CHECK_RESULT int amal_program_get_data_by_index(amal_program *self, u16 index, char **result) {
+ char *str_ptr;
+
+ if(index >= self->num_strings) {
+ amal_log_error("Data index %ld is out of range (%ld)", index, self->num_strings);
+ return AMAL_PROGRAM_INSTRUCTION_INVALID_DATA_INDEX;
+ }
+
+ str_ptr = self->strings_start + self->string_indices[index];
+ am_memcpy(result, &str_ptr, sizeof(char**));
+ 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;
+}
+
+static int assert_reg_outside_stack() {
+ assert(bool_false && "Register outside stack!");
+ return 0;
+}
+
static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
u32 instructions_size;
+ u32 read_start;
u32 read_end;
+ bool inside_func;
+ u16 func_num_registers;
+
+ func_num_registers = 0;
+ inside_func = bool_false;
if(bytes_left_to_read(self) < sizeof(instructions_size))
return AMAL_PROGRAM_INVALID_INSTRUCTIONS_SIZE;
@@ -191,68 +271,296 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self) {
if(bytes_left_to_read(self) < instructions_size)
return AMAL_PROGRAM_INVALID_INSTRUCTIONS_SIZE;
- read_end = self->read_index + instructions_size;
+ /*
+ 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) {
AmalOpcode opcode;
opcode = self->data.data[self->read_index];
self->read_index += sizeof(AmalOpcodeType);
switch(opcode) {
- case AMAL_OP_NOP:
+ case AMAL_OP_NOP: {
+ return_if_error(asm_nop(&self->asm));
break;
- case AMAL_OP_SETZ:
+ }
+ 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;
self->read_index += 1;
break;
- case AMAL_OP_MOV:
+ }
+ 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]];
self->read_index += 2;
break;
- case AMAL_OP_MOVI:
+ }
+ 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));
+ }
+
+ self->read_index += 3;
+ break;
+ }
+ case AMAL_OP_MOVD: {
+ u8 dst_reg;
+ u16 data_index;
+ char *data_ptr;
+ AsmPtr dst;
+
+ 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));
+
+ 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));
+
self->read_index += 3;
break;
- case AMAL_OP_MOVD:
+ }
+ 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));
+
self->read_index += 3;
break;
- case AMAL_OP_ADD:
+ }
+ 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));
+
self->read_index += 3;
break;
- case AMAL_OP_SUB:
+ }
+ 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
+
self->read_index += 3;
break;
- case AMAL_OP_MUL:
+ }
+ case AMAL_OP_IDIV: {
self->read_index += 3;
break;
- case AMAL_OP_DIV:
+ }
+ case AMAL_OP_DIV: {
self->read_index += 3;
break;
- case AMAL_OP_PUSH:
+ }
+ 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
self->read_index += 1;
break;
- case AMAL_OP_PUSHI:
+ }
+ case AMAL_OP_PUSHI: {
+ u16 intermediate_index;
+ Number number;
+
+ 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;
self->read_index += 2;
break;
- case AMAL_OP_PUSHD:
+ }
+ case AMAL_OP_PUSHD: {
+ u16 data_index;
+ char *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;
self->read_index += 2;
break;
+ }
case AMAL_OP_CALL:
- self->read_index += 2;
+ /*assert(bool_false && "TODO: Implement CALL");*/
+ self->read_index += 3;
break;
case AMAL_OP_CALLR:
- self->read_index += 1;
+ /*assert(bool_false && "TODO: Implement CALLR");*/
+ self->read_index += 2;
break;
- case AMAL_OP_CMP:
+ 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]];
self->read_index += 3;
break;
- case AMAL_OP_JZ:
+ }
+ 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
self->read_index += 3;
break;
- case AMAL_OP_JMP:
+ }
+ 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
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");
break;
- case AMAL_OP_FUNC_START:
+ case AMAL_OP_FUNC_START: {
+ 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)));
+ self->read_index += 2;
break;
- case AMAL_OP_FUNC_END:
+ }
+ 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));
break;
+ }
}
}
@@ -266,7 +574,7 @@ int amal_program_run(amal_program *self) {
return_if_error(amal_program_read_strings(self));
return_if_error(amal_program_read_instructions(self));
}
- return AMAL_PROGRAM_OK;
+ return asm_execute(&self->asm);
}
int amal_program_save(amal_program *self, const char *filepath) {