diff options
author | dec05eba <dec05eba@protonmail.com> | 2019-07-31 01:25:05 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2020-07-25 14:36:46 +0200 |
commit | 1f28c3c733ea3ae4234bff91e1c55e61b1ee3e96 (patch) | |
tree | 0ab52e362da03fde741ce8159ef8a4110cd1fb6a /src/program.c | |
parent | ec1a48e7b86fcd00127dd5a88d56c42083af1d78 (diff) |
Starting on asm, implementing extern function call so progress is visible
Diffstat (limited to 'src/program.c')
-rw-r--r-- | src/program.c | 368 |
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(®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)); + 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(®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)); + 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(®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)); + 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(®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 + 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(®, 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 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) { |