From f5dc9ad48db4d22e7d6f15e340063dc7cb14c1e1 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 29 Sep 2019 23:47:52 +0200 Subject: Implicit cast from str to ?&c_char, fix use of parameters (to use sys v registers) --- build.sh | 4 +- executor/executor.h | 37 ++-- executor/interpreter/executor.c | 197 --------------------- executor/x86_64/asm.c | 2 +- executor/x86_64/asm.h | 2 +- executor/x86_64/executor.c | 378 +++++++++++++++++++++++++--------------- include/binop_type.h | 3 +- include/bytecode/bytecode.h | 31 +++- include/ssa/ssa.h | 1 + include/tokenizer.h | 1 + src/ast.c | 19 +- src/bytecode/bytecode.c | 13 +- src/parser.c | 11 +- src/program.c | 29 ++- src/ssa/ssa.c | 31 ++-- src/tokenizer.c | 11 ++ std/io.amal | 5 +- tests/bytecode.amal | 4 +- 18 files changed, 373 insertions(+), 406 deletions(-) delete mode 100644 executor/interpreter/executor.c diff --git a/build.sh b/build.sh index 415fac5..c309380 100755 --- a/build.sh +++ b/build.sh @@ -17,7 +17,9 @@ if [ "$cpu_arch" = "x86_64" ]; then source_files="$source_files $(find "executor/x86_64" -name "*.c")" else echo "WARNING: There is no machine code implementation for your cpu architecture: $cpu_arch. An interpreter will be used instead" - source_files="$source_files $(find "executor/interpreter" -name "*.c")" + echo "TODO: Implement interpreter" + exit 2 + #source_files="$source_files $(find "executor/interpreter" -name "*.c")" fi if [ -z "$CC" ]; then diff --git a/executor/executor.h b/executor/executor.h index 7f9793e..245e64c 100644 --- a/executor/executor.h +++ b/executor/executor.h @@ -4,6 +4,7 @@ #include "../include/std/misc.h" #include "../include/std/types.h" #include "../include/std/buffer_view.h" +#include "../include/bytecode/bytecode.h" /*doc(Execution backend) Amalgam supports multiple execution backend and they can be implemented with minimal @@ -26,29 +27,29 @@ CHECK_RESULT int amal_executor_instructions_start(amal_executor *self, u16 num_f CHECK_RESULT int amal_executor_instructions_end(amal_executor *self); CHECK_RESULT int amal_exec_nop(amal_executor *self); -CHECK_RESULT int amal_exec_setz(amal_executor *self, i8 dst_reg); -CHECK_RESULT int amal_exec_mov(amal_executor *self, i8 dst_reg, i8 src_reg); -CHECK_RESULT int amal_exec_movi(amal_executor *self, i8 dst_reg, i64 imm); -CHECK_RESULT int amal_exec_movd(amal_executor *self, i8 dst_reg, BufferView data); -CHECK_RESULT int amal_exec_add(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2); -CHECK_RESULT int amal_exec_sub(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2); -CHECK_RESULT int amal_exec_imul(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2); -CHECK_RESULT int amal_exec_mul(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2); -CHECK_RESULT int amal_exec_idiv(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2); -CHECK_RESULT int amal_exec_div(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2); -CHECK_RESULT int amal_exec_push(amal_executor *self, i8 reg); +CHECK_RESULT int amal_exec_setz(amal_executor *self, AmalReg dst_reg); +CHECK_RESULT int amal_exec_mov(amal_executor *self, AmalReg dst_reg, AmalReg src_reg); +CHECK_RESULT int amal_exec_movi(amal_executor *self, AmalReg dst_reg, i64 imm); +CHECK_RESULT int amal_exec_movd(amal_executor *self, AmalReg dst_reg, BufferView data); +CHECK_RESULT int amal_exec_add(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); +CHECK_RESULT int amal_exec_sub(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); +CHECK_RESULT int amal_exec_imul(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); +CHECK_RESULT int amal_exec_mul(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); +CHECK_RESULT int amal_exec_idiv(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); +CHECK_RESULT int amal_exec_div(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); +CHECK_RESULT int amal_exec_push(amal_executor *self, AmalReg 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_start(amal_executor *self, u8 num_args); -CHECK_RESULT int amal_exec_call(amal_executor *self, u32 code_offset, i8 dst_reg); +CHECK_RESULT int amal_exec_call(amal_executor *self, u32 code_offset, AmalReg dst_reg); void amal_exec_call_overwrite(amal_executor *self, u32 call_code_offset, i32 new_target_rel32); -/*CHECK_RESULT int amal_exec_callr(i8 dst_reg, BufferView data);*/ -CHECK_RESULT int amal_exec_calle(amal_executor *self, void *func, i8 dst_reg); -CHECK_RESULT int amal_exec_cmp(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2); -CHECK_RESULT int amal_exec_jz(amal_executor *self, i8 reg, u16 target_label); +/*CHECK_RESULT int amal_exec_callr(AmalReg dst_reg, BufferView data);*/ +CHECK_RESULT int amal_exec_calle(amal_executor *self, void *func, AmalReg dst_reg); +CHECK_RESULT int amal_exec_cmp(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); +CHECK_RESULT int amal_exec_jz(amal_executor *self, AmalReg reg, u16 target_label); CHECK_RESULT int amal_exec_jmp(amal_executor *self, u16 target_label); -CHECK_RESULT int amal_exec_ret(amal_executor *self, i8 reg); -CHECK_RESULT int amal_exec_func_start(amal_executor *self, u16 num_regs); +CHECK_RESULT int amal_exec_ret(amal_executor *self, AmalReg reg); +CHECK_RESULT int amal_exec_func_start(amal_executor *self, u8 num_params, u16 num_regs); CHECK_RESULT int amal_exec_func_end(amal_executor *self); CHECK_RESULT int amal_exec_label(amal_executor *self); diff --git a/executor/interpreter/executor.c b/executor/interpreter/executor.c deleted file mode 100644 index 0180b08..0000000 --- a/executor/interpreter/executor.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "../executor.h" -#include "../../include/program.h" -#include "../../include/std/alloc.h" -#include - -typedef struct { - usize *stack; - usize stack_size; - usize stack_index; - isize reg[AMAL_PROGRAM_NUM_REGISTERS]; -} amal_executor_impl; - -#define IMPL \ - amal_executor_impl *impl; \ - impl = (amal_executor_impl*)self; - -#define FOUR_MEGABYTES 1024*1024*4 - -static int executor_ensure_stack_capacity(amal_executor_impl *self, usize bytes_to_add) { - const isize overflow = self->stack_size - self->stack_index * sizeof(isize) + bytes_to_add; - if(overflow > 0) { - void *new_data; - const usize new_stack_size = self->stack_size * 1.5; - if(new_stack_size > FOUR_MEGABYTES) - return AMAL_PROGRAM_INSTRUCTION_STACK_OVERFLOW; - return_if_error(am_realloc(self->stack, new_stack_size, &new_data)); - self->stack = new_data; - self->stack_size = new_stack_size; - } - return 0; -} - -int amal_executor_init(amal_executor **self) { - amal_executor_impl **impl; - impl = (amal_executor_impl**)self; - *impl = NULL; - return_if_error(am_malloc(sizeof(amal_executor_impl), (void**)impl)); - (*impl)->stack_size = 4096; - return_if_error(am_malloc((*impl)->stack_size, (void**)&(*impl)->stack)); - (*impl)->stack_index = 0; - return 0; -} - -void amal_executor_deinit(amal_executor *self) { - IMPL - am_free(impl->stack); - am_free(impl); -} - -int amal_executor_run(amal_executor *self) { - (void)self; - assert(bool_false && "TODO: Implement!"); - return 0; -} - -int amal_exec_nop(amal_executor *self) { - (void)self; - return 0; -} - -int amal_exec_setz(amal_executor *self, u8 dst_reg) { - IMPL - impl->reg[dst_reg] = 0; - return 0; -} - -int amal_exec_mov(amal_executor *self, u8 dst_reg, u8 src_reg) { - IMPL - impl->reg[dst_reg] = impl->reg[src_reg]; - return 0; -} - -int amal_exec_movi(amal_executor *self, u8 dst_reg, i64 imm) { - IMPL - impl->reg[dst_reg] = imm; - return 0; -} - -int amal_exec_movd(amal_executor *self, u8 dst_reg, BufferView data) { - IMPL - impl->reg[dst_reg] = (uintptr_t)data.data; - return 0; -} - -int amal_exec_add(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) { - IMPL - impl->reg[dst_reg] = impl->reg[src_reg1] + impl->reg[src_reg2]; - return 0; -} - -int amal_exec_sub(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) { - IMPL - impl->reg[dst_reg] = impl->reg[src_reg1] - impl->reg[src_reg2]; - return 0; -} - -int amal_exec_imul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) { - IMPL - impl->reg[dst_reg] = impl->reg[src_reg1] * impl->reg[src_reg2]; - return 0; -} - -int amal_exec_mul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) { - IMPL - impl->reg[dst_reg] = impl->reg[src_reg1] * impl->reg[src_reg2]; - return 0; -} - -int amal_exec_idiv(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) { - IMPL - impl->reg[dst_reg] = impl->reg[src_reg1] / impl->reg[src_reg2]; - return 0; -} - -int amal_exec_div(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) { - IMPL - impl->reg[dst_reg] = impl->reg[src_reg1] / impl->reg[src_reg2]; - return 0; -} - -int amal_exec_push(amal_executor *self, u8 reg) { - IMPL - return_if_error(executor_ensure_stack_capacity(impl, sizeof(isize))); - impl->stack[impl->stack_index++] = impl->reg[reg]; - return 0; -} - -int amal_exec_pushi(amal_executor *self, i64 imm) { - IMPL - return_if_error(executor_ensure_stack_capacity(impl, sizeof(isize))); - impl->stack[impl->stack_index++] = imm; - return 0; -} - -int amal_exec_pushd(amal_executor *self, BufferView data) { - IMPL - return_if_error(executor_ensure_stack_capacity(impl, sizeof(isize))); - impl->stack[impl->stack_index++] = (uintptr_t)data.data; - 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) { - IMPL - impl->reg[dst_reg] = (impl->reg[src_reg1] == impl->reg[src_reg2]); - return 0; -} - -int amal_exec_jz(amal_executor *self, u8 dst_reg, i16 offset) { - (void)self; - (void)dst_reg; - (void)offset; - /* TODO: Implement! */ - assert(bool_false && "TODO: Implement!"); - return 0; -} - -int amal_exec_jmp(amal_executor *self, i16 offset) { - (void)self; - (void)offset; - /* TODO: Implement! */ - assert(bool_false && "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 - */ - /* TODO: Preserve registers and stack frame */ - /*return executor_ensure_stack_capacity(impl, num_regs * sizeof(isize));*/ - (void)self; - (void)num_regs; - return 0; -} - -int amal_exec_func_end(amal_executor *self) { - (void)self; - /* TODO: Restore registers and stack frame and ret */ - return 0; -} diff --git a/executor/x86_64/asm.c b/executor/x86_64/asm.c index 60b1752..f7bc19c 100644 --- a/executor/x86_64/asm.c +++ b/executor/x86_64/asm.c @@ -442,7 +442,7 @@ void asm_cqo(Asm *self) { ins_end(self, "cqo"); } -void asm_idiv_rr(Asm *self, Reg64 src) { +void asm_idiv_rax_r(Asm *self, Reg64 src) { ins_start(self); *self->code_it++ = rex_rr(src, 0); *self->code_it++ = 0xF7; diff --git a/executor/x86_64/asm.h b/executor/x86_64/asm.h index b82a63e..dacc248 100644 --- a/executor/x86_64/asm.h +++ b/executor/x86_64/asm.h @@ -89,7 +89,7 @@ void asm_cqo(Asm *self); Divide RDX:RAX by @src. Store the quotient in RAX and the remainder in RDX. @asm_cqo should be called before this, since RAX needs to be sign extended into RDX */ -void asm_idiv_rr(Asm *self, Reg64 src); +void asm_idiv_rax_r(Asm *self, Reg64 src); void asm_pushr(Asm *self, Reg64 reg); void asm_popr(Asm *self, Reg64 reg); diff --git a/executor/x86_64/executor.c b/executor/x86_64/executor.c index c918c13..6f3c1de 100644 --- a/executor/x86_64/executor.c +++ b/executor/x86_64/executor.c @@ -32,32 +32,109 @@ typedef struct { int label_counter; int num_args; int num_pushed_values; + int num_saved_params_for_call; } amal_executor_impl; #define ASM_ENSURE_CAPACITY return_if_error(asm_ensure_capacity(&impl->asm, 256)); -#define IMPL_START \ - amal_executor_impl *impl = (amal_executor_impl*)self; \ - ASM_ENSURE_CAPACITY - -/* - @reg will be a positive value when accessing local variables, in which case the first - local variable is located at -sizeof(usize) and the next one is at -(2 * sizeof(usize)). - @reg will be a negative value starting at -1 when accessing parameters. - The first parameter is located at 3*sizeof(usize) and the next one is at 4*sizeof(usize). - Parameter starts at 3*sizeof(usize) because offset 0 is the return address, offset 1*sizeof(usize) is the - saved RBP and 2*sizeof(usize) is saved RBX. - TODO: Use different offset when saving more registers, for example on Microsoft Windows. -*/ -#define get_register_stack_offset(reg) \ - (reg >= 0 ? (i32)(-reg * (int)sizeof(usize) - sizeof(usize)) : (i32)(-reg * (int)sizeof(usize) + 2*sizeof(usize))) +const Reg64 SYS_V_REG_PARAMS[] = { RDI, RSI, RDX, RCX, R8, R9, R10, R11 }; +const int NUM_REG_PARAMS = 8; static i64 abs_i64(i64 value) { return value >= 0 ? value : -value; } -const Reg64 SYS_V_REG_PARAMS[] = { RDI, RSI, RDX, RCX, R8, R9, R10, R11 }; -const int NUM_REG_PARAMS = 8; +typedef enum { + OPERAND_TYPE_REG, + OPERAND_TYPE_MEM +} AsmOperandType; + +typedef struct { + AsmOperandType type; + union { + AsmPtr mem; + Reg64 reg; + } value; +} AsmOperand; + +static AsmOperand amal_reg_to_asm_operand(AmalReg reg) { + AsmOperand result; + AmalReg reg_value = AMAL_REG_VALUE(reg); + if(reg & REG_FLAG_PARAM) { + if(reg_value < NUM_REG_PARAMS) { + result.type = OPERAND_TYPE_REG; + result.value.reg = SYS_V_REG_PARAMS[reg_value]; + } else { + result.type = OPERAND_TYPE_MEM; + asm_ptr_init_disp(&result.value.mem, RBP, (i32)reg_value * sizeof(usize) + 2 * sizeof(usize)); + } + } else { + result.type = OPERAND_TYPE_MEM; + asm_ptr_init_disp(&result.value.mem, RBP, (i32)-reg_value * sizeof(usize) - sizeof(usize)); + } + return result; +} + +static AsmOperand asm_reg_to_operand(Reg64 reg) { + AsmOperand result; + result.type = OPERAND_TYPE_REG; + result.value.reg = reg; + return result; +} + +/* Note: both operands can't be memory operands */ +static void asm_mov(Asm *self, AsmOperand *dst, AsmOperand *src) { + switch(dst->type) { + case OPERAND_TYPE_REG: { + switch(src->type) { + case OPERAND_TYPE_REG: + asm_mov_rr(self, dst->value.reg, src->value.reg); + break; + case OPERAND_TYPE_MEM: + asm_mov_rm(self, dst->value.reg, &src->value.mem); + break; + } + break; + } + case OPERAND_TYPE_MEM: { + assert(src->type == OPERAND_TYPE_REG && "Both operands can't be memory operands"); + asm_mov_mr(self, &dst->value.mem, src->value.reg); + break; + } + } +} + +static void asm_movi(Asm *self, AsmOperand *dst, i64 immediate) { + switch(dst->type) { + case OPERAND_TYPE_REG: + asm_mov_ri(self, dst->value.reg, immediate); + break; + case OPERAND_TYPE_MEM: + asm_mov_mi(self, &dst->value.mem, immediate); + break; + } +} + +static void asm_cmp(Asm *self, AsmOperand *op1, AsmOperand *op2) { + switch(op1->type) { + case OPERAND_TYPE_REG: { + switch(op2->type) { + case OPERAND_TYPE_REG: + asm_cmp_rm64(self, op1->value.reg, op2->value.reg); + break; + case OPERAND_TYPE_MEM: + asm_cmp_rm(self, op1->value.reg, &op2->value.mem); + break; + } + break; + } + case OPERAND_TYPE_MEM: { + assert(op2->type == OPERAND_TYPE_REG && "Both operands can't be memory operands"); + asm_cmp_rm(self, op2->value.reg, &op1->value.mem); + break; + } + } +} int amal_executor_init(amal_executor **self) { amal_executor_impl **impl; @@ -68,6 +145,7 @@ int amal_executor_init(amal_executor **self) { (*impl)->label_counter = 0; (*impl)->num_args = 0; (*impl)->num_pushed_values = 0; + (*impl)->num_saved_params_for_call = 0; ignore_result_int(buffer_init(&(*impl)->jump_defer, NULL)); return asm_init(&(*impl)->asm); } @@ -102,109 +180,107 @@ int amal_executor_instructions_end(amal_executor *self) { } int amal_exec_nop(amal_executor *self) { - IMPL_START + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY asm_nop(&impl->asm); return 0; } -int amal_exec_setz(amal_executor *self, i8 dst_reg) { - AsmPtr dst; - IMPL_START - asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg)); - asm_mov_mi(&impl->asm, &dst, 0); +int amal_exec_setz(amal_executor *self, AmalReg dst_reg) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY + asm_movi(&impl->asm, &dst_op, 0); return 0; } -int amal_exec_mov(amal_executor *self, i8 dst_reg, i8 src_reg) { - AsmPtr ptr; - IMPL_START - - asm_ptr_init_disp(&ptr, RBP, get_register_stack_offset(src_reg)); - asm_mov_rm(&impl->asm, RAX, &ptr); +int amal_exec_mov(amal_executor *self, AmalReg dst_reg, AmalReg src_reg) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand src_op = amal_reg_to_asm_operand(src_reg); + AsmOperand rax_op = asm_reg_to_operand(RAX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_ptr_init_disp(&ptr, RBP, get_register_stack_offset(dst_reg)); - asm_mov_mr(&impl->asm, &ptr, RAX); + asm_mov(&impl->asm, &rax_op, &src_op); + asm_mov(&impl->asm, &dst_op, &rax_op); return 0; } -int amal_exec_movi(amal_executor *self, i8 dst_reg, i64 imm) { - IMPL_START +int amal_exec_movi(amal_executor *self, AmalReg dst_reg, i64 imm) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY /* 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)); - asm_mov_mi(&impl->asm, &dst, imm); + asm_movi(&impl->asm, &dst_op, imm); } else { - AsmPtr dst; - asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg)); - asm_mov_ri(&impl->asm, RAX, imm); - asm_mov_mr(&impl->asm, &dst, RAX); + AsmOperand rax_op = asm_reg_to_operand(RAX); + asm_movi(&impl->asm, &rax_op, imm); + asm_mov(&impl->asm, &dst_op, &rax_op); } return 0; } -int amal_exec_movd(amal_executor *self, i8 dst_reg, BufferView data) { - AsmPtr dst; - IMPL_START +int amal_exec_movd(amal_executor *self, AmalReg dst_reg, BufferView data) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand rax_op = asm_reg_to_operand(RAX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg)); - asm_mov_ri(&impl->asm, RAX, (uintptr_t)data.data); - asm_mov_mr(&impl->asm, &dst, RAX); + asm_mov_ri(&impl->asm, rax_op.value.reg, (uintptr_t)data.data); + asm_mov(&impl->asm, &dst_op, &rax_op); return 0; } -int amal_exec_add(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { - AsmPtr dst; - AsmPtr reg1; - AsmPtr reg2; - IMPL_START - - 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)); +int amal_exec_add(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1); + AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2); + AsmOperand rax_op = asm_reg_to_operand(RAX); + AsmOperand rcx_op = asm_reg_to_operand(RCX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_mov_rm(&impl->asm, RAX, ®1); - asm_mov_rm(&impl->asm, RCX, ®2); + asm_mov(&impl->asm, &rax_op, &src_op1); + asm_mov(&impl->asm, &rcx_op, &src_op2); asm_add_rr(&impl->asm, RAX, RCX); - asm_mov_mr(&impl->asm, &dst, RAX); + asm_mov(&impl->asm, &dst_op, &rax_op); return 0; } -int amal_exec_sub(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { - AsmPtr dst; - AsmPtr reg1; - AsmPtr reg2; - IMPL_START - - 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)); +int amal_exec_sub(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1); + AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2); + AsmOperand rax_op = asm_reg_to_operand(RAX); + AsmOperand rcx_op = asm_reg_to_operand(RCX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_mov_rm(&impl->asm, RAX, ®1); - asm_mov_rm(&impl->asm, RCX, ®2); + asm_mov(&impl->asm, &rax_op, &src_op1); + asm_mov(&impl->asm, &rcx_op, &src_op2); asm_sub_rr(&impl->asm, RAX, RCX); - asm_mov_mr(&impl->asm, &dst, RAX); + asm_mov(&impl->asm, &dst_op, &rax_op); return 0; } -int amal_exec_imul(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { - AsmPtr dst; - AsmPtr reg1; - AsmPtr reg2; - IMPL_START - - 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)); +int amal_exec_imul(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1); + AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2); + AsmOperand rax_op = asm_reg_to_operand(RAX); + AsmOperand rcx_op = asm_reg_to_operand(RCX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_mov_rm(&impl->asm, RAX, ®1); - asm_mov_rm(&impl->asm, RCX, ®2); + asm_mov(&impl->asm, &rax_op, &src_op1); + asm_mov(&impl->asm, &rcx_op, &src_op2); asm_imul_rr(&impl->asm, RAX, RCX); - asm_mov_mr(&impl->asm, &dst, RAX); + asm_mov(&impl->asm, &dst_op, &rax_op); return 0; } -int amal_exec_mul(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { +int amal_exec_mul(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { (void)self; (void)dst_reg; (void)src_reg1; @@ -228,25 +304,24 @@ int amal_exec_mul(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { return 0; } -int amal_exec_idiv(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { - AsmPtr dst; - AsmPtr reg1; - AsmPtr reg2; - IMPL_START - - 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)); +int amal_exec_idiv(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1); + AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2); + AsmOperand rax_op = asm_reg_to_operand(RAX); + AsmOperand rcx_op = asm_reg_to_operand(RCX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_mov_rm(&impl->asm, RAX, ®1); - asm_mov_rm(&impl->asm, RCX, ®2); + asm_mov(&impl->asm, &rax_op, &src_op1); + asm_mov(&impl->asm, &rcx_op, &src_op2); asm_cqo(&impl->asm); - asm_idiv_rr(&impl->asm, RCX); - asm_mov_mr(&impl->asm, &dst, RAX); + asm_idiv_rax_r(&impl->asm, RCX); + asm_mov(&impl->asm, &dst_op, &rax_op); return 0; } -int amal_exec_div(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { +int amal_exec_div(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { (void)self; (void)dst_reg; (void)src_reg1; @@ -256,17 +331,32 @@ int amal_exec_div(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { return 0; } -int amal_exec_push(amal_executor *self, i8 reg) { - AsmPtr reg_ptr; - IMPL_START +int amal_exec_push(amal_executor *self, AmalReg reg) { + AsmOperand op = amal_reg_to_asm_operand(reg); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_ptr_init_disp(®_ptr, RBP, get_register_stack_offset(reg)); if(impl->num_pushed_values < NUM_REG_PARAMS) { - asm_mov_rm(&impl->asm, SYS_V_REG_PARAMS[impl->num_pushed_values], ®_ptr); + ++impl->num_saved_params_for_call; + /* + TODO: If the arguments to the function are taken from the parameter of the current + function, then this can be optimized to either swap registers or no-op + */ + AsmOperand dst_reg = asm_reg_to_operand(SYS_V_REG_PARAMS[impl->num_pushed_values]); + /* + Backup parameter. + TODO: Remove this, copy it to a temporary register instead. + This should also only be done if the parameter is actually used in the current function + and only if it is used after this point + */ + asm_pushr(&impl->asm, dst_reg.value.reg); + asm_mov(&impl->asm, &dst_reg, &op); } else { - asm_mov_rm(&impl->asm, RAX, ®_ptr); + AsmOperand rax_op = asm_reg_to_operand(RAX); + asm_mov(&impl->asm, &rax_op, &op); asm_pushr(&impl->asm, RAX); } + ++impl->num_pushed_values; return 0; } @@ -293,26 +383,27 @@ int amal_exec_call_start(amal_executor *self, u8 num_args) { return 0; } -int amal_exec_call(amal_executor *self, u32 code_offset, i8 dst_reg) { +int amal_exec_call(amal_executor *self, u32 code_offset, AmalReg dst_reg) { amal_executor_impl *impl = (amal_executor_impl*)self; /* TODO: Preserve necessary registers before call? */ /* TODO: This assumes all arguments are isize */ /* Do the function call */ isize asm_offset = asm_get_size(&impl->asm); - int num_pushed_stack = impl->num_pushed_values - (int)NUM_REG_PARAMS; + int num_pushed_stack = impl->num_pushed_values + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS; ASM_ENSURE_CAPACITY assert((num_pushed_stack <= 0 || num_pushed_stack % 2 == 0) && "TODO: Align stack to 16-bytes before calling functions"); assert(code_offset < asm_offset); asm_call_rel32(&impl->asm, (isize)code_offset - asm_offset); - /* Handle function result and cleanup */ + /* Function result */ { - AsmPtr dst; - asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg)); + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand rax_op = asm_reg_to_operand(RAX); /* TODO: Make this work when result is not stored in RAX (multiple return results) */ - asm_mov_mr(&impl->asm, &dst, RAX); + asm_mov(&impl->asm, &dst_op, &rax_op); } + /* Function cleanup */ if(num_pushed_stack > 0) asm_add_rm64_imm(&impl->asm, RSP, num_pushed_stack * sizeof(isize)); impl->num_pushed_values = 0; @@ -332,10 +423,11 @@ void amal_exec_call_overwrite(amal_executor *self, u32 call_code_offset, i32 new The rest are passed in the stack. */ /* TODO: Make this work when function returns something else than a POD */ -int amal_exec_calle(amal_executor *self, void *func, i8 dst_reg) { - AsmPtr dst; +int amal_exec_calle(amal_executor *self, void *func, AmalReg dst_reg) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand rax_op = asm_reg_to_operand(RAX); amal_executor_impl *impl = (amal_executor_impl*)self; - int num_pushed_stack = impl->num_pushed_values - (int)NUM_REG_PARAMS; + int num_pushed_stack = impl->num_pushed_values + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS; ASM_ENSURE_CAPACITY assert((num_pushed_stack <= 0 || num_pushed_stack % 2 == 0) && "TODO: Align stack to 16-bytes before calling functions"); @@ -344,8 +436,7 @@ int amal_exec_calle(amal_executor *self, void *func, i8 dst_reg) { /* TODO: This assumes all arguments are isize */ asm_mov_ri(&impl->asm, RAX, (intptr_t)func); asm_callr(&impl->asm, RAX); - asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg)); - asm_mov_mr(&impl->asm, &dst, RAX); + asm_mov(&impl->asm, &dst_op, &rax_op); if(num_pushed_stack > 0) asm_add_rm64_imm(&impl->asm, RSP, num_pushed_stack * sizeof(isize)); impl->num_pushed_values = 0; @@ -353,37 +444,39 @@ int amal_exec_calle(amal_executor *self, void *func, i8 dst_reg) { } /* -int amal_exec_callr(i8 dst_reg, BufferView data) { +int amal_exec_callr(AmalReg dst_reg, BufferView data) { } */ -int amal_exec_cmp(amal_executor *self, i8 dst_reg, i8 src_reg1, i8 src_reg2) { - AsmPtr dst, src1, src2; - IMPL_START - - asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg)); - asm_ptr_init_disp(&src1, RBP, get_register_stack_offset(src_reg1)); - asm_ptr_init_disp(&src2, RBP, get_register_stack_offset(src_reg2)); +int amal_exec_cmp(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { + AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); + AsmOperand src_op1 = amal_reg_to_asm_operand(src_reg1); + AsmOperand src_op2 = amal_reg_to_asm_operand(src_reg2); + AsmOperand rax_op = asm_reg_to_operand(RAX); + AsmOperand rcx_op = asm_reg_to_operand(RCX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_mov_rm(&impl->asm, RCX, &dst); - asm_xor_rm64(&impl->asm, RCX, RCX); + asm_mov(&impl->asm, &rcx_op, &dst_op); + asm_xor_rm64(&impl->asm, rcx_op.value.reg, rcx_op.value.reg); - asm_mov_rm(&impl->asm, RAX, &src1); - asm_cmp_rm(&impl->asm, RAX, &src2); - asm_sete_r(&impl->asm, RCX); - asm_mov_mr(&impl->asm, &dst, RCX); + asm_mov(&impl->asm, &rax_op, &src_op1); + asm_cmp(&impl->asm, &rax_op, &src_op2); + asm_sete_r(&impl->asm, rcx_op.value.reg); + asm_mov(&impl->asm, &dst_op, &rcx_op); return 0; } -int amal_exec_jz(amal_executor *self, i8 reg, u16 target_label) { - AsmPtr ptr; +int amal_exec_jz(amal_executor *self, AmalReg reg, u16 target_label) { + AsmOperand op = amal_reg_to_asm_operand(reg); + AsmOperand rax_op = asm_reg_to_operand(RAX); u32 asm_offset; - IMPL_START + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_ptr_init_disp(&ptr, RBP, get_register_stack_offset(reg)); - asm_mov_rm(&impl->asm, RAX, &ptr); - asm_cmp_rm64_imm(&impl->asm, RAX, 0); + asm_mov(&impl->asm, &rax_op, &op); + asm_cmp_rm64_imm(&impl->asm, rax_op.value.reg, 0); asm_offset = asm_get_size(&impl->asm); if(target_label < impl->label_counter) { @@ -424,13 +517,14 @@ int amal_exec_jmp(amal_executor *self, u16 target_label) { } } -int amal_exec_ret(amal_executor *self, i8 reg) { - AsmPtr ret_reg; - IMPL_START +int amal_exec_ret(amal_executor *self, AmalReg reg) { + AsmOperand op = amal_reg_to_asm_operand(reg); + AsmOperand rax_op = asm_reg_to_operand(RAX); + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY - asm_ptr_init_disp(&ret_reg, RBP, get_register_stack_offset(reg)); /* Result is returned in RAX register. TODO: Make this work when returning more than one result */ - asm_mov_rm(&impl->asm, RAX, &ret_reg); + asm_mov(&impl->asm, &rax_op, &op); return amal_exec_func_end(self); } @@ -438,7 +532,7 @@ static u32 get_next_uneven_number(u32 value) { return value + !(value & 1); } -int amal_exec_func_start(amal_executor *self, u16 num_regs) { +int amal_exec_func_start(amal_executor *self, u8 num_params, 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. @@ -450,7 +544,9 @@ int amal_exec_func_start(amal_executor *self, u16 num_regs) { 64-bit Windows: RBX, RSI, RDI, RBP, R12-R15, XMM6-XMM15 64-bit Linux,BSD,Mac: RBX, RBP, R12-R15 */ - IMPL_START + amal_executor_impl *impl = (amal_executor_impl*)self; + ASM_ENSURE_CAPACITY + (void)num_params; /* TODO: Allow use of parameter registers that do not need to be preserved since they are not used */ asm_pushr(&impl->asm, RBX); asm_pushr(&impl->asm, RBP); asm_mov_rr(&impl->asm, RBP, RSP); diff --git a/include/binop_type.h b/include/binop_type.h index 502392c..94d2b7a 100644 --- a/include/binop_type.h +++ b/include/binop_type.h @@ -7,7 +7,8 @@ typedef enum { BINOP_MUL, BINOP_DIV, BINOP_DOT, - BINOP_EQUALS + BINOP_EQUALS, + BINOP_AND } BinopType; #endif diff --git a/include/bytecode/bytecode.h b/include/bytecode/bytecode.h index 23bc4cf..30f3bb5 100644 --- a/include/bytecode/bytecode.h +++ b/include/bytecode/bytecode.h @@ -10,29 +10,33 @@ /*doc(Instructions) Variable length instructions. Instruction size ranges from 1 to 4 bytes. + # Instruction formats Instructions can be in 7 different formats: 1. 1 byte: Opcode(u8) - 2. 2 bytes: Opcode(u8) + register(i8) - 3. 3 bytes: Opcode(u8) + register(i8) + register(i8) + 2. 2 bytes: Opcode(u8) + register(AmalReg) + 3. 3 bytes: Opcode(u8) + register(AmalReg) + register(AmalReg) 4. 3 bytes:\ 4.1 Opcode(u8) + intermediate(u16)\ 4.2 Opcode(u8) + data(u16)\ 4.3 Opcode(u8) + label(i16)\ - 4.4 Opcode(u8) + register(i8) + num_args(u8) - 5. 4 bytes: Opcode(u8) + register(i8) + register(i8) + register(i8) + 4.4 Opcode(u8) + register(AmalReg) + num_args(u8) + 5. 4 bytes: Opcode(u8) + register(AmalReg) + register(AmalReg) + register(AmalReg) 6. 4 bytes:\ - 6.1 Opcode(u8) + register(i8) + label(i16)\ - 6.2 Opcode(u8) + register(i8) + intermediate(u16)\ - 6.3 Opcode(u8) + register(i8) + data(u16)\ + 6.1 Opcode(u8) + register(AmalReg) + label(i16)\ + 6.2 Opcode(u8) + register(AmalReg) + intermediate(u16)\ + 6.3 Opcode(u8) + register(AmalReg) + data(u16)\ 6.4 Opcode(u8) + flags(u8) + num_local_var_reg(u16)\ 6.5 Opcode(u8) + index(u8) + index(u16) + # Registers - Registers have a range of 128. Local variables start from register 0 and increment while parameters start from -1 - and decrement. Registers have the scope of functions and reset after instructions reach a new function (AMAL_OP_FUNC_START). + Registers have a range of 128. Parameters have the most significant bit set while local variables dont. + Registers have the scope of functions and reset after instructions reach a new function (AMAL_OP_FUNC_START). If import index for call and calle is 0, then that means the function resides in the same file the function call is being called from. Which means that import index 1 is actually import index 0 into the import list. + + @AmalReg is an alias for u8. */ /* Important: The number of fields in this enum can't exceed 255 */ typedef enum { @@ -56,6 +60,7 @@ typedef enum { AMAL_OP_CALLR, /* callr reg - Call a function using a register. Used for function pointers. The number of arguments is the number of values pushed to stack */ AMAL_OP_CALLE, /* calle ii, efi - Call an extern function in imported file (ii, import index) using extern function index (efi). The number of arguments is the number of values pushed to stack. ii is u8, efi is u16 */ AMAL_OP_CMP, /* cmp dst, reg1, reg2 - Set dst to 1 if reg1 equals reg2, otherwise set it to 0 */ + AMAL_OP_BIT_AND, /* and dst, reg1, reg2 - Perform bit and on reg1 and reg2, store the result in dst */ AMAL_OP_JZ, /* jz reg, label - Jump to label in the current function if reg is zero. label is u16 */ AMAL_OP_JMP, /* jmp label - Unconditional jump to label in the current function. label is u16 */ AMAL_OP_RET, /* ret reg - Return from the function with reg result */ @@ -81,7 +86,15 @@ typedef enum { FUNC_FLAG_VARARGS = 1 << 1 } amal_func_flag; +typedef enum { + REG_FLAG_NONE = 0, + REG_FLAG_PARAM = 1 << 7 +} amal_reg_flag; + typedef u8 AmalOpcodeType; +typedef u8 AmalReg; + +#define AMAL_REG_VALUE(reg) ((reg)&0x7f) /* TODO: Make sure this pragma pack works on all platforms */ #pragma pack(push, 1) diff --git a/include/ssa/ssa.h b/include/ssa/ssa.h index 0ec43b0..c6d58f6 100644 --- a/include/ssa/ssa.h +++ b/include/ssa/ssa.h @@ -22,6 +22,7 @@ typedef enum { SSA_IDIV, SSA_DIV, SSA_EQUALS, + SSA_AND, SSA_FUNC_START, SSA_FUNC_END, SSA_PUSH, diff --git a/include/tokenizer.h b/include/tokenizer.h index 53351a4..4735d6f 100644 --- a/include/tokenizer.h +++ b/include/tokenizer.h @@ -42,6 +42,7 @@ typedef enum { TOK_EXPORT, TOK_RETURN, TOK_QUESTION_MARK, + TOK_AMPERSAND, TOK_C_VARARGS } Token; diff --git a/src/ast.c b/src/ast.c index 90af981..5336e1e 100644 --- a/src/ast.c +++ b/src/ast.c @@ -733,8 +733,17 @@ static usize min(usize a, usize b) { return a < b ? a : b; } +static bool is_c_pointer_compatible(VariableType *self) { + return self->variable_type_flags & (VARIABLE_TYPE_FLAG_OPTIONAL | VARIABLE_TYPE_FLAG_BORROW); +} + +static bool is_type_compatible_with(AstResolvedType *self, AstResolvedType *other, AstCompilerContext *context) { + return self->value.data == &context->compiler->default_types.str->lhs_expr && + other->value.data == context->compiler->default_types.c_char && is_c_pointer_compatible(&other->value.lhs_expr->type); +} + static bool resolve_data_type_equals(AstResolvedType *self, AstResolvedType *other) { - return self->type == other->type && self->value.data == other->value.data; + return self->value.data == other->value.data; } static bool function_parameter_is_vararg(FunctionParameter *self, AstCompilerContext *context) { @@ -742,6 +751,12 @@ static bool function_parameter_is_vararg(FunctionParameter *self, AstCompilerCon return self->resolve_data.type.value.data == &vararg_type->lhs_expr; } +static bool is_function_arg_compatible_with_parameter(AstResolvedType *arg, FunctionParameter *param, AstCompilerContext *context) { + return resolve_data_type_equals(arg, ¶m->resolve_data.type) || + is_type_compatible_with(arg, ¶m->resolve_data.type, context) || + function_parameter_is_vararg(param, context); +} + static void verify_func_call_matches_func_signature(FunctionCall *func_call, FunctionSignature *func_sig, AstCompilerContext *context) { Ast **arg = buffer_begin(&func_call->args); Ast **arg_end = buffer_end(&func_call->args); @@ -755,7 +770,7 @@ static void verify_func_call_matches_func_signature(FunctionCall *func_call, Fun usize num_check = min(num_args, num_params); usize i = 0; for(; i < num_check; ++i) { - if(!resolve_data_type_equals(&(*arg)->resolve_data.type, &func_param->resolve_data.type) && !function_parameter_is_vararg(func_param, context)) { + if(!is_function_arg_compatible_with_parameter(&(*arg)->resolve_data.type, func_param, context)) { BufferView arg_name = ast_resolved_type_get_name(&(*arg)->resolve_data.type); BufferView param_name = ast_resolved_type_get_name(&func_param->resolve_data.type); compiler_print_error(context->compiler, func_call->func.name.data, diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c index a5e3abc..b947985 100644 --- a/src/bytecode/bytecode.c +++ b/src/bytecode/bytecode.c @@ -387,7 +387,7 @@ static void add_ins1(BytecodeCompilerContext *self, AmalOpcode opcode, const cha } } -static void add_ins2(BytecodeCompilerContext *self, AmalOpcode opcode, i8 reg, const char *fmt) { +static void add_ins2(BytecodeCompilerContext *self, AmalOpcode opcode, AmalReg reg, const char *fmt) { Buffer *instructions = &self->bytecode->data; size_t index = instructions->size; @@ -398,7 +398,7 @@ static void add_ins2(BytecodeCompilerContext *self, AmalOpcode opcode, i8 reg, c fputc('\n', stderr); } -static void add_ins3(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_reg, i8 src_reg, const char *fmt) { +static void add_ins3(BytecodeCompilerContext *self, AmalOpcode opcode, AmalReg dst_reg, AmalReg src_reg, const char *fmt) { Buffer *instructions = &self->bytecode->data; size_t index = instructions->size; @@ -421,7 +421,7 @@ static void add_ins4(BytecodeCompilerContext *self, AmalOpcode opcode, u16 data, fputc('\n', stderr); } -static void add_ins5(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_reg, i8 reg1, i8 reg2, const char *fmt) { +static void add_ins5(BytecodeCompilerContext *self, AmalOpcode opcode, AmalReg dst_reg, AmalReg reg1, AmalReg reg2, const char *fmt) { Buffer *instructions = &self->bytecode->data; size_t index = instructions->size; @@ -434,7 +434,7 @@ static void add_ins5(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_re fputc('\n', stderr); } -static void add_ins6(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_reg, u16 data, const char *fmt) { +static void add_ins6(BytecodeCompilerContext *self, AmalOpcode opcode, AmalReg dst_reg, u16 data, const char *fmt) { Buffer *instructions = &self->bytecode->data; size_t index = instructions->size; @@ -525,6 +525,11 @@ static void add_instructions(BytecodeCompilerContext *self) { add_ins5(self, AMAL_OP_CMP, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "cmp r%d, r%d, r%d"); break; } + case SSA_AND: { + instruction += ssa_extract_data(instruction, &ssa_ins_form2, sizeof(ssa_ins_form2)); + add_ins5(self, AMAL_OP_BIT_AND, ssa_ins_form2.result, ssa_ins_form2.lhs, ssa_ins_form2.rhs, "and r%d, r%d, r%d"); + break; + } case SSA_FUNC_START: { instruction += ssa_extract_data(instruction, &ssa_ins_func_start, sizeof(ssa_ins_func_start)); add_ins6(self, AMAL_OP_FUNC_START, ssa_ins_func_start.flags, ssa_ins_func_start.num_local_vars_regs, "func_start 0x%02x, %u"); diff --git a/src/parser.c b/src/parser.c index dc39a6a..be19a33 100644 --- a/src/parser.c +++ b/src/parser.c @@ -290,14 +290,9 @@ void parser_parse_var_type(Parser *self, VariableType *result) { if(match) result->variable_type_flags |= VARIABLE_TYPE_FLAG_OPTIONAL; - throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_BINOP, &match)); - if(match) { - if(self->tokenizer.value.binop_type != BINOP_MUL) { - self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), "Expected '*', type or closure signature"); - throw(PARSER_UNEXPECTED_TOKEN); - } + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_AMPERSAND, &match)); + if(match) result->variable_type_flags |= VARIABLE_TYPE_FLAG_BORROW; - } throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); if(match) { @@ -329,7 +324,7 @@ void parser_parse_var_type_def(Parser *self, VariableType *result) { if(result->type == VARIABLE_TYPE_NONE) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), - "Expected type or closure signature"); + "Expected '?', '&', type or closure signature"); throw(PARSER_UNEXPECTED_TOKEN); } } diff --git a/src/program.c b/src/program.c index 4c0f1a2..ac3c923 100644 --- a/src/program.c +++ b/src/program.c @@ -445,6 +445,14 @@ static void header_func_set_offset(amal_program *self, u16 func_index, u32 code_ am_memcpy(&header_func->func_offset, &code_offset, sizeof(code_offset)); } +static u8 header_func_get_num_params(amal_program *self, u16 func_index) { + u8 result; + BytecodeHeaderFunction *header_func = ((BytecodeHeaderFunction*)self->funcs_start) + func_index; + assert(sizeof(header_func->num_params) == sizeof(result)); + am_memcpy(&result, &header_func->num_params, sizeof(result)); + return result; +} + static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_executor *executor) { u32 instructions_size; u32 read_start; @@ -496,7 +504,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ u16 intermediate_index; Number number; - am_memcpy(&intermediate_index, &self->data.data[self->read_index + sizeof(i8)], sizeof(intermediate_index)); + am_memcpy(&intermediate_index, &self->data.data[self->read_index + sizeof(AmalReg)], sizeof(intermediate_index)); return_if_error(amal_program_get_intermediate_by_index(self, intermediate_index, &number)); return_if_error(amal_exec_movi(executor, self->data.data[self->read_index], number.value.integer)); @@ -507,7 +515,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ u16 data_index; BufferView data_ptr; - am_memcpy(&data_index, &self->data.data[self->read_index + sizeof(i8)], sizeof(data_index)); + am_memcpy(&data_index, &self->data.data[self->read_index + sizeof(AmalReg)], sizeof(data_index)); return_if_error(amal_program_get_data_by_index(self, data_index, &data_ptr)); return_if_error(amal_exec_movd(executor, self->data.data[self->read_index], data_ptr)); @@ -586,7 +594,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ u8 import_index; u16 func_index; BytecodeHeaderFunction func_def; - i8 dst_reg; + AmalReg dst_reg; am_memcpy(&import_index, self->data.data + self->read_index, sizeof(import_index)); am_memcpy(&func_index, self->data.data + self->read_index + sizeof(import_index), sizeof(func_index)); @@ -632,7 +640,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ case AMAL_OP_CALLE: { u8 import_index; u16 extern_func_index; - i8 dst_reg; + AmalReg dst_reg; am_memcpy(&import_index, self->data.data + self->read_index, sizeof(import_index)); am_memcpy(&extern_func_index, self->data.data + self->read_index + sizeof(import_index), sizeof(extern_func_index)); @@ -653,8 +661,13 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ self->read_index += 3; break; } + case AMAL_OP_BIT_AND: { + assert(bool_false && "TODO: Implement!"); + self->read_index += 3; + break; + } case AMAL_OP_JZ: { - i8 reg; + AmalReg reg; u16 target_label; reg = self->data.data[self->read_index]; am_memcpy(&target_label, self->data.data + self->read_index + sizeof(reg), sizeof(target_label)); @@ -670,19 +683,21 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ break; } case AMAL_OP_RET: { - const i8 reg = self->data.data[self->read_index]; + const AmalReg reg = self->data.data[self->read_index]; return_if_error(amal_exec_ret(executor, reg)); self->read_index += 1; break; } case AMAL_OP_FUNC_START: { u8 func_flags; + u8 func_num_params; u16 func_num_local_var_regs; assert(!inside_func); inside_func = bool_true; assert(func_counter < self->num_functions); + func_num_params = header_func_get_num_params(self, func_counter); header_func_set_offset(self, func_counter, amal_exec_get_code_offset(executor)); return_if_error(resolve_deferred_func_calls(self, executor, func_counter)); ++func_counter; @@ -691,7 +706,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ am_memcpy(&func_num_local_var_regs, self->data.data + self->read_index + sizeof(func_flags), sizeof(func_num_local_var_regs)); if(func_flags & FUNC_FLAG_EXPORTED) return_if_error(amal_program_set_exported_function_instruction_offset_advance(self, amal_exec_get_code_offset(executor))); - return_if_error(amal_exec_func_start(executor, func_num_local_var_regs)); + return_if_error(amal_exec_func_start(executor, func_num_params, func_num_local_var_regs)); self->read_index += 3; break; } diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c index 9955d9d..d875ea4 100644 --- a/src/ssa/ssa.c +++ b/src/ssa/ssa.c @@ -81,7 +81,7 @@ int ssa_init(Ssa *self, Parser *parser) { static CHECK_RESULT int ssa_get_unique_reg(Ssa *self, SsaRegister *result) { assert(result); /* Overflow */ - if((u16)self->reg_counter + self->param_counter + 1 > INT16_MAX) { + if((u16)self->reg_counter + 1 > INT16_MAX) { amal_log_error("Ssa too many registers!"); return -1; } @@ -90,6 +90,18 @@ static CHECK_RESULT int ssa_get_unique_reg(Ssa *self, SsaRegister *result) { return 0; } +static CHECK_RESULT int ssa_get_unique_param_reg(Ssa *self, SsaRegister *result) { + assert(result); + /* Overflow */ + if((u16)self->param_counter + 1 > INT16_MAX) { + amal_log_error("Ssa too many param registers!"); + return -1; + } + assert(self->param_counter <= INT8_MAX && "TODO: Implement usage of reg higher than 128"); + *result = self->param_counter++ | REG_FLAG_PARAM; + return 0; +} + SsaNumber ssa_get_intermediate(Ssa *self, SsaIntermediateIndex index) { SsaNumber result; assert(index < buffer_get_size(&self->intermediates, SsaNumber)); @@ -239,6 +251,7 @@ static const char* binop_type_to_string(SsaInstruction binop_type) { case SSA_MUL: return "*"; case SSA_DIV: return "/"; case SSA_EQUALS: return "=="; + case SSA_AND: return "&&"; default: return ""; } } @@ -274,7 +287,7 @@ static CHECK_RESULT int ssa_ins_assign_reg(Ssa *self, SsaRegister dest, SsaRegis } static CHECK_RESULT int ssa_ins_binop(Ssa *self, SsaInstruction binop_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result) { - assert(binop_type >= SSA_ADD && binop_type <= SSA_EQUALS); + assert(binop_type >= SSA_ADD && binop_type <= SSA_AND); return ssa_add_ins_form2(self, binop_type, lhs, rhs, result); } @@ -577,14 +590,7 @@ static CHECK_RESULT SsaRegister function_parameter_generate_ssa(FunctionParamete if(self->resolve_data.status == AST_SSA_RESOLVED) return self->resolve_data.ssa_reg; - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - /* Parameters start at -1 and decrement */ - if((u16)reg - 1 >= (u16)reg) { - amal_log_error("Ssa too many parameters!"); - throw(-1); - } - reg = -1 - reg; - assert(reg >= INT8_MIN && "TODO: Implement more than 128 params"); + throw_if_error(ssa_get_unique_param_reg(context->ssa, ®)); self->resolve_data.status = AST_SSA_RESOLVED; self->resolve_data.ssa_reg = reg; return reg; @@ -614,6 +620,7 @@ static CHECK_RESULT SsaRegister funcdecl_generate_ssa(FunctionDecl *self, SsaCom usize func_metadata_index; u8 func_flags = 0; context->ssa->reg_counter = 0; + context->ssa->param_counter = 0; context->ssa->label_counter = 0; /* @@ -621,8 +628,6 @@ static CHECK_RESULT SsaRegister funcdecl_generate_ssa(FunctionDecl *self, SsaCom This way we can know if a register access is for a parameter or not by checking the number */ function_signature_generate_params_ssa(self->signature, context); - context->ssa->param_counter = context->ssa->reg_counter; - context->ssa->reg_counter = 0; amal_log_debug("SSA funcdecl %p", self); /* Anonymous closure doesn't have lhs_expr, and neither can it have any flags (extern, export etc) */ @@ -754,6 +759,8 @@ static SsaInstruction binop_type_to_ssa_type(BinopType binop_type, amal_default_ return 0; case BINOP_EQUALS: return SSA_EQUALS; + case BINOP_AND: + return SSA_AND; } return 0; } diff --git a/src/tokenizer.c b/src/tokenizer.c index d753b20..da6ad53 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -339,6 +339,14 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } else { *token = TOK_EQUALS; } + } else if(c == '&') { + ++self->index; + if(self->index < (int)self->code.size && tokenizer_get_char(self) == '&') { + ++self->index; + SET_BINOP(BINOP_AND); + } else { + *token = TOK_AMPERSAND; + } } else if(c == '(') { ++self->index; *token = TOK_OPEN_PAREN; @@ -517,6 +525,9 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_QUESTION_MARK: str = "?"; break; + case TOK_AMPERSAND: + str = "&"; + break; case TOK_C_VARARGS: str = "..."; break; diff --git a/std/io.amal b/std/io.amal index 9023280..6acfb1b 100644 --- a/std/io.amal +++ b/std/io.amal @@ -1,5 +1,6 @@ extern const print_extern: fn() i32; +extern const printf: fn(fmt: ?&c_char, ...) c_int; -pub const print = fn() i32 { - return print_extern(); +pub const print = fn(fmt: str, arg1: str) i32 { + return printf(fmt, arg1); } \ No newline at end of file diff --git a/tests/bytecode.amal b/tests/bytecode.amal index d27a240..35dc3da 100644 --- a/tests/bytecode.amal +++ b/tests/bytecode.amal @@ -1,5 +1,5 @@ -extern const printf: fn(fmt: ?*c_char, ...) c_int; +const io = @import("../std/io.amal"); const main = fn { - printf("hello, world!\n"); + io.print("hello %s", "world!"); } -- cgit v1.2.3