From 902a81528b9d2edcf93226a2ca13da6fcc1839e5 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 23 Dec 2019 08:57:48 +0100 Subject: wip: function pointers and other stuff --- LICENSE | 2 +- README.md | 9 ++ doc/DESIGN.md | 14 ++- doc/Documentation.md | 35 ++++---- executor/executor.h | 2 +- executor/x86_64/asm.c | 11 +++ executor/x86_64/asm.h | 1 + executor/x86_64/executor.c | 45 +++++++++- include/ast.h | 9 ++ include/bytecode/bytecode.h | 8 +- include/compiler.h | 1 + include/ir/ir.h | 6 ++ include/program.h | 2 +- include/tokenizer.h | 4 +- src/ast.c | 79 +++++++++++----- src/bytecode/bytecode.c | 50 +++++++---- src/compiler.c | 7 +- src/ir/ir.c | 214 ++++++++++++++++++++++++++++++-------------- src/parser.c | 48 +++++++--- src/program.c | 9 +- src/tokenizer.c | 19 +++- std/io.amal | 6 +- tests/bug.amal | 5 ++ tests/closure.amal | 37 ++++++++ tests/conditions.amal | 16 ++++ tests/glfw.amal | 4 +- tests/hello_world.amal | 5 ++ 27 files changed, 476 insertions(+), 172 deletions(-) create mode 100644 tests/bug.amal create mode 100644 tests/closure.amal create mode 100644 tests/conditions.amal mode change 100644 => 100755 tests/glfw.amal create mode 100644 tests/hello_world.amal diff --git a/LICENSE b/LICENSE index 9d3f9f8..ab1d20c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2019 dec05eba +Copyright 2020 dec05eba Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 36997eb..110709f 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,15 @@ should be in the bytecode before the files that they are used from, to reduce de * Make function calls work for functions that return no value or returns multiple values. * Some bytecode instructions take 3 operands. These should be optimized to operate directly on the destination if the second or third operand is also the destination. +* Align the instruction at the start of a function and the start of an expensive loop to 16-bytes. This improves performance +and the block might even fit inside instruction cache.\ +From intel instruction manual: +``` +3.4.1.5 - Assembly/Compiler Coding Rule 12. (M impact, H generality) +All branch targets should be 16-byte aligned. +``` +* Reduce the number of branches by making memory allocation/reallocation throw instead of returning error. +* Implement pub for imports and show compile error when using non-pub symbols in pub closures/structs/etc. # Documents Documents are located under doc. The file doc/Documentation.md is generated from source files by running doc/doc_extract.py but there is no need to run this script unless you are modifying documentation in the source. diff --git a/doc/DESIGN.md b/doc/DESIGN.md index fae9ef2..f9e3cef 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -13,7 +13,9 @@ const main = fn { ``` const main = fn { var value = 23 + 50; - if value < 23 + if value == 0 + stderr.writeln("zero!"); + else if value < 23 stderr.writeln("less!"); else stderr.writeln("more!"); @@ -28,20 +30,14 @@ const main = fn { ## Closure Parentheses after `fn` is only required when the closure has parameters or returns data ``` -const apply = fn(func: () bool) { +const apply = fn(func: fn() bool) { const result = func(); } const main = fn { - // Return type is automatically deduced. If function returns multiple different types at different points, - // then you get an error and are required to specify the return type - apply(fn { - return true; - }) - apply(fn() bool { return true; - }) + }); // Or store in a variable and use it const func = fn { diff --git a/doc/Documentation.md b/doc/Documentation.md index 96cd3ef..493fcfa 100644 --- a/doc/Documentation.md +++ b/doc/Documentation.md @@ -16,8 +16,9 @@ Instructions can be in 7 different formats: 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) +6.4 Opcode(u8) + register(AmalReg) + index(u16)\ +6.5 Opcode(u8) + flags(u8) + num_local_var_reg(u16)\ +6.6 Opcode(u8) + index(u8) + index(u16) ## Registers Registers have a range of 128. Parameters have the most significant bit set while local variables dont. @@ -88,15 +89,15 @@ The versions in the header only changes for every release, not every change. |Function[]|Functions |Multiple non-extern functions, where the number of functions is defined by @num_funcs.| ## Function -|Type|Field |Description | -|----|-------------------------|------------------------------------------------------------------------------------------------------------------------| -|u32 |func_offset |The offset in the program code (machine code) where the function starts. Is always 0 until the program has been started.| -|u8 |num_params |The number of parameters. | -|u32 |params_num_pointers |The number of pointers in the parameters. | -|u32 |params_fixed_size |The size of all non-pointer type parameters, in bytes. | -|u8 |num_return_types |The number of return values. | -|u32 |return_types_num_pointers|The number of pointers in the return types. | -|u32 |return_types_fixed_size |The size of all non-pointer type return types, in bytes. | +|Type|Field |Description | +|----|-------------------------|-------------------------------------------------------------------------------------------------------------------------| +|u32 |func_offset |The offset in the program code (machine code) where the function starts. Is always ~0 until the program has been started.| +|u8 |num_params |The number of parameters. | +|u32 |params_num_pointers |The number of pointers in the parameters. | +|u32 |params_fixed_size |The size of all non-pointer type parameters, in bytes. | +|u8 |num_return_types |The number of return values. | +|u32 |return_types_num_pointers|The number of pointers in the return types. | +|u32 |return_types_fixed_size |The size of all non-pointer type return types, in bytes. | # Bytecode external functions ## External functions layout @@ -128,12 +129,12 @@ The versions in the header only changes for every release, not every change. |Exported function[]|Exported functions|Multiple exported functions, where the number of functions is defined by @num_export_func| ## Exported function -|Type|Field |Description | -|----|------------------|--------------------------------------------------------------------------------------------------------------------------| -|u32 |instruction_offset|The offset in the instruction data where the exported function is defined. Is always 0 until the program has been started.| -|u8 |num_args |The number of arguments the functions has. | -|u8 |name_len |The length of the exported function name, in bytes. Excluding the null-terminate character. | -|u8[]|name |The name of the exported function, where the size is defined by @name_len. Names are null-terminated. | +|Type|Field |Description | +|----|------------------|---------------------------------------------------------------------------------------------------------------------------| +|u32 |instruction_offset|The offset in the instruction data where the exported function is defined. Is always ~0 until the program has been started.| +|u8 |num_args |The number of arguments the functions has. | +|u8 |name_len |The length of the exported function name, in bytes. Excluding the null-terminate character. | +|u8[]|name |The name of the exported function, where the size is defined by @name_len. Names are null-terminated. | # Bytecode imports ## Imports layout diff --git a/executor/executor.h b/executor/executor.h index 70d9637..2ef4995 100644 --- a/executor/executor.h +++ b/executor/executor.h @@ -43,8 +43,8 @@ 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, 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(AmalReg dst_reg, BufferView data);*/ CHECK_RESULT int amal_exec_calle(amal_executor *self, void *func, AmalReg dst_reg); +CHECK_RESULT int amal_exec_callr(amal_executor *self, AmalReg reg, AmalReg dst_reg); CHECK_RESULT int amal_exec_eq(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); CHECK_RESULT int amal_exec_neq(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); CHECK_RESULT int amal_exec_ilt(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2); diff --git a/executor/x86_64/asm.c b/executor/x86_64/asm.c index a400656..cf56b69 100644 --- a/executor/x86_64/asm.c +++ b/executor/x86_64/asm.c @@ -70,6 +70,7 @@ static void ins_end(Asm *self, const char *fmt, ...) { va_list args; va_start(args, fmt); + fprintf(stderr, "%06x | ", (unsigned int)ins_start_offset); ins_end_offset = (u8*)self->code_it - (u8*)self->code; for(i = ins_start_offset; i < ins_end_offset; ++i) { fprintf(stderr, "%02x ", ((u8*)self->code)[i]); @@ -145,6 +146,8 @@ static const char* asm_ptr_to_string(AsmPtr *self) { return buf; } #else +#include + static void ins_start(Asm *self) { (void)self; } @@ -478,6 +481,14 @@ void asm_callr(Asm *self, Reg64 reg) { ins_end(self, "call %s", reg64_to_str(reg)); } +void asm_callm(Asm *self, AsmPtr *ptr) { + ins_start(self); + *self->code_it++ = rex_rm(ptr, 0); + *self->code_it++ = 0xFF; + asm_rm(self, ptr, 0x2); + ins_end(self, "call %s", asm_ptr_to_string(ptr)); +} + /* Note: This is sometimes called with @relative 0 (will print call -5), in which case it's most likely a dummy call until the relative position is later changed with @asm_overwrite_call_rel32. TODO: Update the ins_end debug print to take that into account somehow diff --git a/executor/x86_64/asm.h b/executor/x86_64/asm.h index 7d68bc0..355e133 100644 --- a/executor/x86_64/asm.h +++ b/executor/x86_64/asm.h @@ -96,6 +96,7 @@ void asm_idiv_rax_r(Asm *self, Reg64 src); void asm_pushr(Asm *self, Reg64 reg); void asm_popr(Asm *self, Reg64 reg); void asm_callr(Asm *self, Reg64 reg); +void asm_callm(Asm *self, AsmPtr *ptr); /* In x86 assembly, the @relative position starts from the next instruction. This offset shouldn't be calculated by the caller and is instead managed diff --git a/executor/x86_64/executor.c b/executor/x86_64/executor.c index 65f8baa..378bc31 100644 --- a/executor/x86_64/executor.c +++ b/executor/x86_64/executor.c @@ -136,6 +136,17 @@ static void asm_cmp(Asm *self, AsmOperand *op1, AsmOperand *op2) { } } +static void asm_call(Asm *self, AsmOperand *op) { + switch(op->type) { + case OPERAND_TYPE_REG: + asm_callr(self, op->value.reg); + break; + case OPERAND_TYPE_MEM: + asm_callm(self, &op->value.mem); + break; + } +} + int amal_executor_init(amal_executor **self) { amal_executor_impl **impl; impl = (amal_executor_impl**)self; @@ -388,7 +399,7 @@ int amal_exec_call(amal_executor *self, u32 code_offset, AmalReg dst_reg) { /* 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); + isize asm_offset; /* TODO: Do not push */ int num_pushed_stack = impl->num_pushed_values;/* + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS;*/ ASM_ENSURE_CAPACITY @@ -398,6 +409,7 @@ int amal_exec_call(amal_executor *self, u32 code_offset, AmalReg dst_reg) { ++num_pushed_stack; asm_sub_rm64_imm(&impl->asm, RSP, sizeof(isize)); } + asm_offset = asm_get_size(&impl->asm); assert(code_offset < asm_offset); asm_call_rel32(&impl->asm, (isize)code_offset - asm_offset); @@ -453,11 +465,36 @@ int amal_exec_calle(amal_executor *self, void *func, AmalReg dst_reg) { return 0; } -/* -int amal_exec_callr(AmalReg dst_reg, BufferView data) { +int amal_exec_callr(amal_executor *self, AmalReg reg, AmalReg dst_reg) { + amal_executor_impl *impl = (amal_executor_impl*)self; + /* TODO: Preserve necessary registers before call? */ + /* TODO: This assumes all arguments are isize */ + /* TODO: Do not push */ + int num_pushed_stack = impl->num_pushed_values;/* + impl->num_saved_params_for_call - (int)NUM_REG_PARAMS;*/ + AsmOperand func_ptr_op; + ASM_ENSURE_CAPACITY + + /*assert((num_pushed_stack <= 0 || num_pushed_stack % 2 == 0) && "TODO: Align stack to 16-bytes before calling functions");*/ + if(num_pushed_stack & 1) { + ++num_pushed_stack; + asm_sub_rm64_imm(&impl->asm, RSP, sizeof(isize)); + } + func_ptr_op = amal_reg_to_asm_operand(reg); + asm_call(&impl->asm, &func_ptr_op); + /* Function result */ + { + 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(&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; + return 0; } -*/ int amal_exec_eq(amal_executor *self, AmalReg dst_reg, AmalReg src_reg1, AmalReg src_reg2) { AsmOperand dst_op = amal_reg_to_asm_operand(dst_reg); diff --git a/include/ast.h b/include/ast.h index eaaf93e..f56c180 100644 --- a/include/ast.h +++ b/include/ast.h @@ -29,6 +29,7 @@ typedef struct Import Import; typedef struct String String; typedef struct Variable Variable; typedef struct Number Number; +typedef struct AstBool AstBool; typedef struct Binop Binop; typedef struct IfStatement IfStatement; typedef struct ElseIfStatement ElseIfStatement; @@ -47,6 +48,7 @@ typedef union { Import *import; String *string; Number *number; + AstBool *bool; Variable *variable; Binop *binop; IfStatement *if_stmt; @@ -64,6 +66,7 @@ typedef enum { AST_IMPORT, AST_STRING, AST_NUMBER, + AST_BOOL, AST_VARIABLE, AST_BINOP, AST_IF_STATEMENT, @@ -266,6 +269,11 @@ struct Number { BufferView code_ref; }; +struct AstBool { + bool value; + BufferView code_ref; +}; + struct Binop { Ast *lhs; Ast *rhs; @@ -332,6 +340,7 @@ void assignmentexpr_init(AssignmentExpr *self, Ast *lhs_expr, Ast *rhs_expr); void import_init(Import *self, BufferView path); CHECK_RESULT int string_init(String *self, BufferView str); void number_init(Number *self, AmalNumber *value, BufferView code_ref); +void ast_bool_init(AstBool *self, bool value, BufferView code_ref); void variable_init(Variable *self, BufferView name); void binop_init(Binop *self); CHECK_RESULT int if_statement_init(IfStatement *self, Scope *parent, ArenaAllocator *allocator); diff --git a/include/bytecode/bytecode.h b/include/bytecode/bytecode.h index a70bb6f..0547a35 100644 --- a/include/bytecode/bytecode.h +++ b/include/bytecode/bytecode.h @@ -26,8 +26,9 @@ 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) + 6.4 Opcode(u8) + register(AmalReg) + index(u16)\ + 6.5 Opcode(u8) + flags(u8) + num_local_var_reg(u16)\ + 6.6 Opcode(u8) + index(u8) + index(u16) # Registers Registers have a range of 128. Parameters have the most significant bit set while local variables dont. @@ -45,6 +46,7 @@ typedef enum { AMAL_OP_MOV, /* mov dst, src - Move src register to dst register */ AMAL_OP_MOVI, /* movi dst, src - Move src intermediate to dst register */ AMAL_OP_MOVD, /* movd dst, src - Move src data to dst register */ + AMAL_OP_LOADF, /* loadf dst, fi - Move function by index @fi to register @dst. @f1 is u16 */ AMAL_OP_ADD, /* add dst, reg1, reg2 - Add reg1 and reg2 and put the result in dst */ AMAL_OP_SUB, /* sub dst, reg1, reg2 - Substract reg2 from reg1 and put the result in dst */ AMAL_OP_IMUL, /* imul dst, reg1, reg2 - Signed multiplication */ @@ -57,8 +59,8 @@ typedef enum { AMAL_OP_PUSH_RET, /* push_ret reg - Push register as a return value of the next function call */ AMAL_OP_CALL_START, /* call_start num_args - Start of a CALL with @num_args number of arguments. Arguments for the next CALL is pushed immediately after this, followed by a CALL. @num_args is u8 */ AMAL_OP_CALL, /* call ii, fi - Call a function in imported file (ii, import index) using function index (fi). The number of arguments is the number of values pushed to stack. ii is u8, fi is u16 */ - 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_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_EQ, /* eq dst, reg1, reg2 - Set dst to 1 if reg1 equals reg2, otherwise set it to 0 */ AMAL_OP_NEQ, /* neq dst, reg1, reg2 - Set dst to 1 if reg1 is not equal to reg2, otherwise set it to 0 */ AMAL_OP_ILT, /* ilt dst, reg1, reg2 - Set dst to 1 if reg1 is less than reg2 (signed comparison), otherwise set it to 0 */ diff --git a/include/compiler.h b/include/compiler.h index 481a107..3cde9da 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -22,6 +22,7 @@ typedef struct { } amal_default_type; typedef struct { + amal_default_type *void_type; amal_default_type *i8; amal_default_type *i16; amal_default_type *i32; diff --git a/include/ir/ir.h b/include/ir/ir.h index f7aa4a7..dcc5ea0 100644 --- a/include/ir/ir.h +++ b/include/ir/ir.h @@ -15,6 +15,7 @@ typedef enum { IR_ASSIGN_INTER, IR_ASSIGN_STRING, IR_ASSIGN_REG, + IR_ASSIGN_FUNC, IR_ADD, IR_SUB, IR_IMUL, @@ -39,6 +40,7 @@ typedef enum { IR_CALL_START, IR_CALL, IR_CALL_EXTERN, + IR_CALLR, IR_JUMP_ZERO, IR_JUMP, IR_RET, @@ -161,5 +163,9 @@ typedef struct { /* longjump to context->env on failure */ void scope_generate_ir(Scope *self, IrCompilerContext *context); +/* longjump to context->env on failure */ +void scope_generate_function_ids(Scope *self, IrCompilerContext *context); +/* longjump to context->env on failure */ +void scope_generate_functions_ir(Scope *self, IrCompilerContext *context); #endif diff --git a/include/program.h b/include/program.h index dc65cb7..9104898 100644 --- a/include/program.h +++ b/include/program.h @@ -68,7 +68,7 @@ typedef struct { ArenaAllocator allocator; /* Owned. Used by @extern_funcs_map */ HashMapType(BufferView, ProgramExternFunc) extern_funcs_map; - /* key=((func_index<<32)&funcs_start), value=Buffer */ + /* key=((func_index<<32)|funcs_start), value=Buffer */ HashMapType(u64, Buffer) deferred_func_calls; i8 return_values_stack[AMAL_PROGRAM_MAX_RETURN_VALUES]; int return_value_index; diff --git a/include/tokenizer.h b/include/tokenizer.h index ceb7acf..5d59d73 100644 --- a/include/tokenizer.h +++ b/include/tokenizer.h @@ -22,6 +22,8 @@ typedef enum { TOK_CONST, TOK_VAR, TOK_STRING, + TOK_NUMBER, + TOK_BOOL, TOK_FN, TOK_STRUCT, TOK_EQUALS, @@ -32,7 +34,6 @@ typedef enum { TOK_OPEN_BRACE, TOK_CLOSING_BRACE, TOK_IMPORT, - TOK_NUMBER, TOK_SEMICOLON, TOK_COLON, TOK_BINOP, @@ -66,6 +67,7 @@ struct Tokenizer { BinopType binop_type; } value; AmalNumber number; + bool bool_value; ArenaAllocator *allocator; /* borrowed */ const amal_compiler_options *compiler_options; /* borrowed */ }; diff --git a/src/ast.c b/src/ast.c index 04ec646..504c1f9 100644 --- a/src/ast.c +++ b/src/ast.c @@ -169,6 +169,9 @@ BufferView ast_get_name(Ast *self) { case AST_NUMBER: name = self->value.number->code_ref; break; + case AST_BOOL: + name = self->value.bool->code_ref; + break; case AST_LHS: name = self->value.lhs_expr->var_name; break; @@ -352,6 +355,11 @@ void number_init(Number *self, AmalNumber *value, BufferView code_ref) { self->code_ref = code_ref; } +void ast_bool_init(AstBool *self, bool value, BufferView code_ref) { + self->value = value; + self->code_ref = code_ref; +} + void variable_init(Variable *self, BufferView name) { self->name = name; scope_named_object_init(&self->resolved_var); @@ -464,8 +472,7 @@ static void __scope_get_resolved_variable(Scope *self, Scope *start, AstCompiler exists = hash_map_get(&self->named_objects, name, &ast_result); if(!exists) { if(self->function_signature) { - FunctionParameter *func_param; - func_param = function_signature_get_parameter_by_name(self->function_signature, name); + FunctionParameter *func_param = function_signature_get_parameter_by_name(self->function_signature, name); if(func_param) { prev_scope = context->scope; context->scope = self; @@ -477,6 +484,9 @@ static void __scope_get_resolved_variable(Scope *self, Scope *start, AstCompiler result->resolve_data = &func_param->resolve_data; return; } + + /* TODO: Remove this when closures can capture variables */ + assert(self->parent == &context->parser->struct_decl.body); } if(self->parent) { @@ -721,8 +731,7 @@ static Scope* ast_resolved_type_get_scope(AstResolvedType *self) { } static void funcdecl_resolve(Ast *self, AstCompilerContext *context) { - FunctionDecl *func_decl; - func_decl = self->value.func_decl; + FunctionDecl *func_decl = self->value.func_decl; function_signature_resolve(func_decl->signature, context); scope_resolve(&func_decl->body, context); self->resolve_data.type.type = RESOLVED_TYPE_FUNC_SIG; @@ -734,27 +743,39 @@ static usize min(usize a, usize b) { } static bool is_c_pointer_compatible(VariableType *self) { - return self->variable_type_flags & (VARIABLE_TYPE_FLAG_OPTIONAL | VARIABLE_TYPE_FLAG_BORROW); + return self->variable_type_flags & 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 is_arg_str_and_param_c_str(AstResolvedType *arg_type, VariableType *param_type, AstCompilerContext *context) { + return arg_type->value.data == &context->compiler->default_types.str->lhs_expr && + param_type->type == VARIABLE_TYPE_VARIABLE && + param_type->value.variable->resolved_var.value.data == context->compiler->default_types.c_char && + is_c_pointer_compatible(param_type); } static bool resolve_data_type_equals(AstResolvedType *self, AstResolvedType *other) { - return self->value.data == other->value.data; + if(self->type != other->type) + return bool_false; + + switch(self->type) { + case RESOLVED_TYPE_NONE: + case RESOLVED_TYPE_LHS_EXPR: + return self->value.data == other->value.data; + case RESOLVED_TYPE_FUNC_SIG: + return function_signature_equals(self->value.func_sig, other->value.func_sig); + } + assert(bool_false); } -static bool function_parameter_is_vararg(FunctionParameter *self, AstCompilerContext *context) { +static bool function_parameter_is_c_vararg(FunctionParameter *self, AstCompilerContext *context) { amal_default_type *vararg_type = context->compiler->default_types.c_varargs; 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); + is_arg_str_and_param_c_str(arg, ¶m->type, context) || + function_parameter_is_c_vararg(param, context); } /* Pointers, isize and usize are returned with size 4, as that is the smallest possible size for them */ @@ -814,7 +835,7 @@ static void funccall_resolve_signature_types(FunctionCall *func_call, FunctionSi if(num_missing_args > 0) { FunctionParameter *vararg_param = buffer_begin(&func_sig->parameters); - bool has_vararg = num_params > 0 && function_parameter_is_vararg(&vararg_param[num_params - 1], context); + bool has_vararg = num_params > 0 && function_parameter_is_c_vararg(&vararg_param[num_params - 1], context); if (has_vararg) num_missing_args -= 1; if(num_missing_args > 0) { @@ -845,10 +866,14 @@ static void funccall_resolve(Ast *self, AstCompilerContext *context) { { func_sig = self->resolve_data.type.value.func_sig; - FunctionReturnType *return_type = buffer_begin(&func_sig->return_types); - assert(buffer_get_size(&func_sig->return_types, FunctionReturnType) == 1); self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; - self->resolve_data.type = return_type->resolved_type; + if(func_sig->return_types.size > 0) { + FunctionReturnType *return_type = buffer_begin(&func_sig->return_types); + self->resolve_data.type = return_type->resolved_type; + } else { + self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; + self->resolve_data.type.value.lhs_expr = &context->compiler->default_types.void_type->lhs_expr; + } } ast = buffer_begin(&func_call->args); @@ -918,12 +943,11 @@ static bool is_struct_decl(Ast *self) { return resolved_type->rhs_expr && resolved_type->rhs_expr->type == AST_STRUCT_DECL; } -static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) { +static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context, ScopeNamedObject *rhs_resolved_var) { Binop *self; Scope *lhs_scope; BufferView caller_code_ref; BufferView callee_code_ref; - ScopeNamedObject rhs_resolved_var; assert(ast->type == AST_BINOP); self = ast->value.binop; @@ -935,8 +959,8 @@ static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) { } lhs_scope = ast_resolved_type_get_scope(&self->lhs->resolve_data.type); - scope_get_resolved_variable(lhs_scope, context, self->rhs->value.variable->name, &rhs_resolved_var); - self->rhs->resolve_data.type = scope_named_object_get_resolved_type(&rhs_resolved_var, context); + scope_get_resolved_variable(lhs_scope, context, self->rhs->value.variable->name, rhs_resolved_var); + self->rhs->resolve_data.type = scope_named_object_get_resolved_type(rhs_resolved_var, context); caller_code_ref = ast_get_code_reference(self->rhs); callee_code_ref = ast_resolved_type_get_name(&self->rhs->resolve_data.type); @@ -982,13 +1006,15 @@ static void binop_resolve(Ast *ast, AstCompilerContext *context) { self = ast->value.binop; ast_resolve(self->lhs, context); if(self->type == BINOP_DOT && (self->rhs->type == AST_VARIABLE || self->rhs->type == AST_FUNCTION_CALL)) { - binop_resolve_dot_access(ast, context); + ScopeNamedObject rhs_resolved_var; + binop_resolve_dot_access(ast, context, &rhs_resolved_var); /* Only function call has extra data that needs to be resolved (args) */ if(self->rhs->type == AST_FUNCTION_CALL) { - Scope *prev_scope = context->scope; - context->scope = ast_resolved_type_get_scope(&self->lhs->resolve_data.type); + /*Scope *prev_scope = context->scope;*/ + /*context->scope = ast_resolved_type_get_scope(&self->lhs->resolve_data.type);*/ + self->rhs->value.func_call->func.resolved_var = rhs_resolved_var; ast_resolve(self->rhs, context); - context->scope = prev_scope; + /*context->scope = prev_scope;*/ } self->rhs->resolve_data.status = AST_RESOLVED; ast->resolve_data.type = self->rhs->resolve_data.type; @@ -1206,6 +1232,11 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { case AST_NUMBER: number_resolve(self, context); break; + case AST_BOOL: { + self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; + self->resolve_data.type.value.lhs_expr = &context->compiler->default_types.bool->lhs_expr; + break; + } case AST_FUNCTION_DECL: funcdecl_resolve(self, context); break; diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c index af9251b..add351f 100644 --- a/src/bytecode/bytecode.c +++ b/src/bytecode/bytecode.c @@ -177,15 +177,15 @@ static void add_functions(BytecodeCompilerContext *self) { |Function[]|Functions |Multiple non-extern functions, where the number of functions is defined by @num_funcs.| # Function - |Type|Field |Description | - |----|-------------------------|------------------------------------------------------------------------------------------------------------------------| - |u32 |func_offset |The offset in the program code (machine code) where the function starts. Is always 0 until the program has been started.| - |u8 |num_params |The number of parameters. | - |u32 |params_num_pointers |The number of pointers in the parameters. | - |u32 |params_fixed_size |The size of all non-pointer type parameters, in bytes. | - |u8 |num_return_types |The number of return values. | - |u32 |return_types_num_pointers|The number of pointers in the return types. | - |u32 |return_types_fixed_size |The size of all non-pointer type return types, in bytes. | + |Type|Field |Description | + |----|-------------------------|-------------------------------------------------------------------------------------------------------------------------| + |u32 |func_offset |The offset in the program code (machine code) where the function starts. Is always ~0 until the program has been started.| + |u8 |num_params |The number of parameters. | + |u32 |params_num_pointers |The number of pointers in the parameters. | + |u32 |params_fixed_size |The size of all non-pointer type parameters, in bytes. | + |u8 |num_return_types |The number of return values. | + |u32 |return_types_num_pointers|The number of pointers in the return types. | + |u32 |return_types_fixed_size |The size of all non-pointer type return types, in bytes. | */ Ir *ir = self->parser->ir; @@ -204,7 +204,7 @@ static void add_functions(BytecodeCompilerContext *self) { TypeSize params_total_size = function_signature_get_params_size(func->func_sig); TypeSize return_types_total_size = function_signature_get_return_types_size(func->func_sig); - header_func.func_offset = 0; + header_func.func_offset = ~(u32)0UL; header_func.num_params = buffer_get_size(&func->func_sig->parameters, FunctionParameter); header_func.params_num_pointers = params_total_size.num_pointers; @@ -301,12 +301,12 @@ static void add_export_functions(BytecodeCompilerContext *self) { |Exported function[]|Exported functions|Multiple exported functions, where the number of functions is defined by @num_export_func| # Exported function - |Type|Field |Description | - |----|------------------|--------------------------------------------------------------------------------------------------------------------------| - |u32 |instruction_offset|The offset in the instruction data where the exported function is defined. Is always 0 until the program has been started.| - |u8 |num_args |The number of arguments the functions has. | - |u8 |name_len |The length of the exported function name, in bytes. Excluding the null-terminate character. | - |u8[]|name |The name of the exported function, where the size is defined by @name_len. Names are null-terminated. | + |Type|Field |Description | + |----|------------------|---------------------------------------------------------------------------------------------------------------------------| + |u32 |instruction_offset|The offset in the instruction data where the exported function is defined. Is always ~0 until the program has been started.| + |u8 |num_args |The number of arguments the functions has. | + |u8 |name_len |The length of the exported function name, in bytes. Excluding the null-terminate character. | + |u8[]|name |The name of the exported function, where the size is defined by @name_len. Names are null-terminated. | */ Ir *ir = self->parser->ir; Buffer *instructions = &self->bytecode->data; @@ -324,7 +324,7 @@ static void add_export_functions(BytecodeCompilerContext *self) { throw_if_error(buffer_append(instructions, &export_funcs_size, sizeof(u32))); for(; export_func != export_func_end; ++export_func) { const char null_s = '\0'; - const u32 instruction_offset = 0; + const u32 instruction_offset = ~(u32)0UL; u8 num_args = buffer_get_size(&export_func->func_sig->parameters, FunctionParameter); throw_if_error(buffer_append(instructions, &instruction_offset, sizeof(instruction_offset))); throw_if_error(buffer_append(instructions, &num_args, sizeof(num_args))); @@ -472,6 +472,11 @@ static void add_instructions(BytecodeCompilerContext *self) { u32 num_instructions_index = self->bytecode->data.size; throw_if_error(buffer_append_empty(&self->bytecode->data, sizeof(num_instructions_index))); + /* + TODO: Limit registers from u16 to u7. Right now they are downcasted and will cause bugs + if there are too many registers used in the program. + */ + while(instruction != instructions_end) { IrInstruction ins = (IrInstruction)*instruction++; switch(ins) { @@ -490,6 +495,11 @@ static void add_instructions(BytecodeCompilerContext *self) { add_ins3(self, AMAL_OP_MOV, ir_ins_form1.lhs, ir_ins_form1.rhs, "mov r%d, r%d"); break; } + case IR_ASSIGN_FUNC: { + instruction += ir_extract_data(instruction, &ir_ins_form1, sizeof(ir_ins_form1)); + add_ins6(self, AMAL_OP_LOADF, ir_ins_form1.lhs, ir_ins_form1.rhs, "loadf r%d, f%d"); + break; + } case IR_ADD: { instruction += ir_extract_data(instruction, &ir_ins_form2, sizeof(ir_ins_form2)); add_ins5(self, AMAL_OP_ADD, ir_ins_form2.result, ir_ins_form2.lhs, ir_ins_form2.rhs, "add r%d, r%d, r%d"); @@ -614,6 +624,12 @@ static void add_instructions(BytecodeCompilerContext *self) { add_ins6(self, AMAL_OP_CALLE, ir_ins_func_call_extern.import_index, ir_ins_func_call_extern.func_decl_lhs->extern_index, "calle ef(%d,%d)"); break; } + case IR_CALLR: { + IrRegister reg; + instruction += ir_extract_data(instruction, ®, sizeof(reg)); + add_ins2(self, AMAL_OP_CALLR, reg, "callr r%d"); + break; + } case IR_JUMP_ZERO: { instruction += ir_extract_data(instruction, &ir_ins_jump_zero, sizeof(ir_ins_jump_zero)); add_ins6(self, AMAL_OP_JZ, ir_ins_jump_zero.condition_reg, ir_ins_jump_zero.target_label, "jz r%d, l%d"); diff --git a/src/compiler.c b/src/compiler.c index 48ba71e..f9d9355 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -59,6 +59,7 @@ static CHECK_RESULT int create_default_type_fixed_size(amal_compiler *compiler, static CHECK_RESULT int init_default_types(amal_compiler *compiler) { /* Plain old datatype */ + return_if_error(create_default_type_fixed_size(compiler, "void", 0, &compiler->default_types.void_type, bool_false)); return_if_error(create_default_type_fixed_size(compiler, "i8", 1, &compiler->default_types.i8, bool_true)); return_if_error(create_default_type_fixed_size(compiler, "i16", 2, &compiler->default_types.i16, bool_true)); return_if_error(create_default_type_fixed_size(compiler, "i32", 4, &compiler->default_types.i32, bool_true)); @@ -266,8 +267,10 @@ static CHECK_RESULT int thread_generate_ir(Parser *parser) { amal_log_debug("Generating IR for file: %.*s", parser->tokenizer.code_name.size, parser->tokenizer.code_name.data); result = setjmp(compiler_context.env); - if(result == 0) - scope_generate_ir(&parser->struct_decl.body, &compiler_context); + if(result == 0) { + scope_generate_function_ids(&parser->struct_decl.body, &compiler_context); + scope_generate_functions_ir(&parser->struct_decl.body, &compiler_context); + } return result; } diff --git a/src/ir/ir.c b/src/ir/ir.c index 62d3516..1ca154a 100644 --- a/src/ir/ir.c +++ b/src/ir/ir.c @@ -294,34 +294,44 @@ static CHECK_RESULT int ir_ins_assign_reg(Ir *self, IrRegister dest, IrRegister return ir_add_ins_form1(self, IR_ASSIGN_REG, dest, src); } +static CHECK_RESULT int ir_ins_assign_func(Ir *self, IrRegister dest, IrFuncIndex func_index) { + amal_log_debug("r%d = f%d", dest, func_index); + return ir_add_ins_form1(self, IR_ASSIGN_FUNC, dest, func_index); +} + static CHECK_RESULT int ir_ins_binop(Ir *self, IrInstruction binop_type, IrRegister lhs, IrRegister rhs, IrRegister *result) { assert(binop_type >= IR_ADD && binop_type <= IR_GE); return ir_add_ins_form2(self, binop_type, lhs, rhs, result); } -static CHECK_RESULT int ir_ins_func_start(Ir *self, u8 func_flags, FunctionSignature *func_sig, IrFuncIndex *result, usize *func_metadata_index) { +static CHECK_RESULT int ir_ins_func_start(Ir *self, u8 func_flags, FunctionSignature *func_sig, usize *func_metadata_index) { const u8 ins_type = IR_FUNC_START; IrInsFuncStart ins_func_start; + IrFunc func; - /* Overflow */ - if(self->func_counter + 1 <= self->func_counter) { - amal_log_error("Ir too many closures!"); - return -1; - } + func.func_sig = func_sig; + return_if_error(buffer_append(&self->funcs, &func, sizeof(func))); - *result = self->func_counter++; - { - IrFunc func; - func.func_sig = func_sig; - return_if_error(buffer_append(&self->funcs, &func, sizeof(func))); - } ins_func_start.flags = func_flags; /* Dont set number of local registers yet. That will be set by @func_metadata_index later when it's known */ /*ins_func_start.num_local_vars_regs = ---*/ return_if_error(buffer_append(&self->instructions, &ins_type, 1)); return_if_error(buffer_append(&self->instructions, &ins_func_start, sizeof(ins_func_start))); *func_metadata_index = self->instructions.size - sizeof(ins_func_start.num_local_vars_regs); - amal_log_debug("FUNC_START f%u(%d) %d", *result, buffer_get_size(&func_sig->parameters, FunctionParameter), buffer_get_size(&func_sig->return_types, FunctionReturnType)); + amal_log_debug("FUNC_START f%d(%d) %d", + func_sig->func_decl ? func_sig->func_decl->ir_func_index : -1, + buffer_get_size(&func_sig->parameters, FunctionParameter), + buffer_get_size(&func_sig->return_types, FunctionReturnType)); + return 0; +} + +static CHECK_RESULT int ir_inc_func_index(Ir *self, IrFuncIndex *result) { + /* Overflow */ + if(self->func_counter + 1 <= self->func_counter) { + amal_log_error("Ir too many closures!"); + return -1; + } + *result = self->func_counter++; return 0; } @@ -373,6 +383,13 @@ static CHECK_RESULT int ir_ins_call_extern(Ir *self, int import_index, LhsExpr * return buffer_append(&self->instructions, &ins_func_call_extern, sizeof(ins_func_call_extern)); } +static CHECK_RESULT int ir_ins_call_reg(Ir *self, IrRegister reg) { + const u8 ins_type = IR_CALLR; + amal_log_debug("CALLR r%d", reg); + return_if_error(buffer_append(&self->instructions, &ins_type, 1)); + return buffer_append(&self->instructions, ®, sizeof(reg)); +} + static CHECK_RESULT int ir_ins_jumpzero(Ir *self, IrRegister condition_reg, IrLabelIndex target_label, usize *instruction_offset) { const u8 ins_type = IR_JUMP_ZERO; IrInsJumpZero ins_jump_zero; @@ -466,23 +483,6 @@ static bool ast_resolved_type_is_decl(AstResolvedType *self) { return lhs_expr->rhs_expr->type == AST_FUNCTION_DECL || lhs_expr->rhs_expr->type == AST_STRUCT_DECL; } #endif -static bool lhs_expr_is_decl(LhsExpr *self) { - if(self->rhs_expr) { - return self->rhs_expr->type == AST_FUNCTION_DECL || self->rhs_expr->type == AST_STRUCT_DECL; - } else { - switch(self->type.type) { - case VARIABLE_TYPE_NONE: - assert(bool_false); - return 0; - case VARIABLE_TYPE_VARIABLE: - return bool_false; - case VARIABLE_TYPE_SIGNATURE: - /* TODO: This should return bool_false when it's possible to use signature in expressions */ - return bool_true; - } - return 0; - } -} static CHECK_RESULT IrRegister number_generate_ir(Number *self, IrCompilerContext *context) { IrRegister reg; @@ -501,6 +501,17 @@ static CHECK_RESULT IrRegister number_generate_ir(Number *self, IrCompilerContex return reg; } +static CHECK_RESULT IrRegister ast_bool_generate_ir(AstBool *self, IrCompilerContext *context) { + IrRegister reg; + IrNumber number; + number.type = IR_NUMBER_TYPE_INTEGER; + number.value.integer = self->value; + throw_if_error(ir_get_unique_reg(context->ir, ®)); + /* TODO: Maybe bool shouldn't be a regular number? */ + throw_if_error(ir_ins_assign_inter(context->ir, reg, number)); + return reg; +} + static CHECK_RESULT IrRegister lhsexpr_extern_generate_ir(LhsExpr *self, IrCompilerContext *context) { /* TODO: IrRegister should be extended to include static and extern data */ if(self->type.type == VARIABLE_TYPE_SIGNATURE) { @@ -534,6 +545,7 @@ static CHECK_RESULT IrRegister lhsexpr_extern_generate_ir(LhsExpr *self, IrCompi return 0; } +#if 0 static CHECK_RESULT IrRegister lhsexpr_export_generate_ir(LhsExpr *self, IrCompilerContext *context) { /* TODO: IrRegister should be extended to include static and export data */ if(self->rhs_expr->type == AST_FUNCTION_DECL) { @@ -543,6 +555,7 @@ static CHECK_RESULT IrRegister lhsexpr_export_generate_ir(LhsExpr *self, IrCompi } return 0; } +#endif static CHECK_RESULT IrRegister lhsexpr_generate_ir(LhsExpr *self, AstResolveData *resolve_data, IrCompilerContext *context) { IrRegister reg; @@ -552,19 +565,17 @@ static CHECK_RESULT IrRegister lhsexpr_generate_ir(LhsExpr *self, AstResolveData if(self->rhs_expr) { Ast *rhs_expr = self->rhs_expr; - IrRegister rhs_reg; - rhs_reg = ast_generate_ir(rhs_expr, context); + IrRegister rhs_reg = ast_generate_ir(rhs_expr, context); +#if 0 if(LHS_EXPR_IS_EXPORT(self)) return lhsexpr_export_generate_ir(self, context); - +#endif /* - Declarations (struct and function declaration) resolves to itself and in that case this expression - is just a compile-time name for the declaration. Import expression also has no meaning in IR until it's used. TODO: Shouldn't lhsexpr that have struct/function declaration as rhs be different ast expression types? */ - if(lhs_expr_is_decl(self) || rhs_expr->type == AST_IMPORT) { + if(rhs_expr->type == AST_IMPORT) { /*assert(bool_false);*/ return 0; } @@ -620,12 +631,7 @@ static CHECK_RESULT void function_signature_generate_params_ir(FunctionSignature } } -/* -TODO: Each function declaration should be in separate IR instances so ast can be converted into ir -in any order. -*/ static CHECK_RESULT IrRegister funcdecl_generate_ir(FunctionDecl *self, IrCompilerContext *context) { - /* TODO: Implement */ /* Reset reg counter in each function, because each function has a separate register context that is reset after function end @@ -636,10 +642,7 @@ static CHECK_RESULT IrRegister funcdecl_generate_ir(FunctionDecl *self, IrCompil context->ir->param_counter = 0; context->ir->label_counter = 0; - /* - Parameters need to have generated ir so the first ir registers belong to the function. - This way we can know if a register access is for a parameter or not by checking the number - */ + /* All parameters need to be generated, so that the parameter matches its index... */ function_signature_generate_params_ir(self->signature, context); amal_log_debug("IR funcdecl %p", self); @@ -649,7 +652,7 @@ static CHECK_RESULT IrRegister funcdecl_generate_ir(FunctionDecl *self, IrCompil if(LHS_EXPR_IS_EXPORT(self->lhs_expr)) func_flags |= FUNC_FLAG_EXPORTED; } - throw_if_error(ir_ins_func_start(context->ir, func_flags, self->signature, &self->ir_func_index, &func_metadata_index)); + throw_if_error(ir_ins_func_start(context->ir, func_flags, self->signature, &func_metadata_index)); scope_generate_ir(&self->body, context); throw_if_error(ir_ins_func_end(context->ir)); @@ -658,27 +661,57 @@ static CHECK_RESULT IrRegister funcdecl_generate_ir(FunctionDecl *self, IrCompil return 0; } +static CHECK_RESULT IrRegister funcdecl_ref_generate_ir(FunctionDecl *self, IrCompilerContext *context) { + IrRegister reg; + if(self->lhs_expr && LHS_EXPR_IS_EXTERN(self->lhs_expr)) { + assert(bool_false && "TODO: Implement assign func for extern closures"); + throw(-1); + } + throw_if_error(ir_get_unique_reg(context->ir, ®)); + throw_if_error(ir_ins_assign_func(context->ir, reg, self->ir_func_index)); + return reg; +} + + static CHECK_RESULT IrRegister funccall_generate_ir(FunctionCall *self, IrCompilerContext *context) { IrRegister reg; FunctionSignature *func_sig; FunctionDecl *func_decl; LhsExpr *func_lhs_expr; + FunctionParameter *func_param_expr; int import_index = context->import_index; + + func_lhs_expr = NULL; + func_param_expr = NULL; context->import_index = 0; throw_if_error(ir_get_unique_reg(context->ir, ®)); - assert(self->func.resolved_var.type == NAMED_OBJECT_LHS_EXPR); - func_lhs_expr = self->func.resolved_var.value.lhs_expr; - if(func_lhs_expr->type.type == VARIABLE_TYPE_SIGNATURE) { - func_sig = func_lhs_expr->type.value.signature; - } else if(func_lhs_expr->type.type == VARIABLE_TYPE_VARIABLE) { - AstResolveData *resolve_data = func_lhs_expr->type.value.variable->resolved_var.resolve_data; - assert(resolve_data->type.type == RESOLVED_TYPE_FUNC_SIG); - func_sig = resolve_data->type.value.func_sig; - } else { - assert(func_lhs_expr->rhs_expr && func_lhs_expr->rhs_expr->resolve_data.type.type == RESOLVED_TYPE_FUNC_SIG); - func_sig = func_lhs_expr->rhs_expr->resolve_data.type.value.func_sig; + switch(self->func.resolved_var.type) { + case NAMED_OBJECT_NONE: + assert(bool_false); + break; + case NAMED_OBJECT_LHS_EXPR: { + func_lhs_expr = self->func.resolved_var.value.lhs_expr; + if(func_lhs_expr->type.type == VARIABLE_TYPE_SIGNATURE) { + func_sig = func_lhs_expr->type.value.signature; + } else if(func_lhs_expr->type.type == VARIABLE_TYPE_VARIABLE) { + AstResolveData *resolve_data = func_lhs_expr->type.value.variable->resolved_var.resolve_data; + assert(resolve_data->type.type == RESOLVED_TYPE_FUNC_SIG); + func_sig = resolve_data->type.value.func_sig; + } else { + assert(func_lhs_expr->rhs_expr && func_lhs_expr->rhs_expr->resolve_data.type.type == RESOLVED_TYPE_FUNC_SIG); + func_sig = func_lhs_expr->rhs_expr->resolve_data.type.value.func_sig; + } + break; + } + case NAMED_OBJECT_FUNC_PARAM: { + func_param_expr = self->func.resolved_var.value.func_param; + assert(func_param_expr->type.type == VARIABLE_TYPE_SIGNATURE); + func_sig = func_param_expr->type.value.signature; + break; + } } + func_decl = func_sig->func_decl; /* Push return arguments */ @@ -719,15 +752,16 @@ static CHECK_RESULT IrRegister funccall_generate_ir(FunctionCall *self, IrCompil if(func_lhs_expr && LHS_EXPR_IS_EXTERN(func_lhs_expr)) { throw_if_error(ir_ins_call_extern(context->ir, import_index, func_lhs_expr)); - } else { - assert(func_decl); - /* rhs wont be null here because only extern variable can't have rhs */ + } else if(func_decl) { throw_if_error(ir_ins_call(context->ir, import_index, func_decl)); + } else if(func_param_expr) { + throw_if_error(ir_ins_call_reg(context->ir, function_parameter_generate_ir(func_param_expr, context))); } return reg; } +#if 0 static CHECK_RESULT IrRegister structdecl_generate_ir(StructDecl *self, IrCompilerContext *context) { /* TODO: Implement */ /*assert(bool_false);*/ @@ -742,6 +776,7 @@ static CHECK_RESULT IrRegister structfield_generate_ir(StructField *self, IrComp (void)context; return 0; } +#endif static CHECK_RESULT IrRegister string_generate_ir(String *self, IrCompilerContext *context) { IrRegister reg; @@ -895,24 +930,29 @@ static void return_expr_generate_ir(ReturnExpr *self, IrCompilerContext *context } static CHECK_RESULT IrRegister ast_generate_ir_resolve_data(void *ast_data, AstType ast_type, AstResolveData *resolve_data, IrCompilerContext *context) { - if(resolve_data->status == AST_IR_RESOLVED) - return resolve_data->ir_reg; + /*if(resolve_data->status == AST_IR_RESOLVED) + return resolve_data->ir_reg;*/ switch(ast_type) { case AST_NUMBER: resolve_data->ir_reg = number_generate_ir(ast_data, context); break; + case AST_BOOL: + resolve_data->ir_reg = ast_bool_generate_ir(ast_data, context); + break; case AST_FUNCTION_DECL: - resolve_data->ir_reg = funcdecl_generate_ir(ast_data, context); + /* The IR for function declarations is done separately, in @scope_generate_functions_ir */ + resolve_data->ir_reg = funcdecl_ref_generate_ir(ast_data, context); break; case AST_FUNCTION_CALL: resolve_data->ir_reg = funccall_generate_ir(ast_data, context); break; case AST_STRUCT_DECL: - resolve_data->ir_reg = structdecl_generate_ir(ast_data, context); + resolve_data->ir_reg = 0;/*structdecl_generate_ir(ast_data, context);*/ break; case AST_STRUCT_FIELD: - resolve_data->ir_reg = structfield_generate_ir(ast_data, context); + assert(bool_false); + resolve_data->ir_reg = 0;/*structfield_generate_ir(ast_data, context);*/ break; case AST_LHS: resolve_data->ir_reg = lhsexpr_generate_ir(ast_data, resolve_data, context); @@ -941,7 +981,6 @@ static CHECK_RESULT IrRegister ast_generate_ir_resolve_data(void *ast_data, AstT break; case AST_RETURN: return_expr_generate_ir(ast_data, context); - resolve_data->ir_reg = 0; break; } @@ -950,13 +989,13 @@ static CHECK_RESULT IrRegister ast_generate_ir_resolve_data(void *ast_data, AstT } CHECK_RESULT IrRegister ast_generate_ir(Ast *self, IrCompilerContext *context) { +#ifdef DEBUG assert(self); - #ifdef DEBUG if(self->resolve_data.status != AST_RESOLVED && self->resolve_data.status != AST_IR_RESOLVED) { amal_log_error("Ast type not resolved: %d", self->type); assert(bool_false); } - #endif +#endif return ast_generate_ir_resolve_data(self->value.data, self->type, &self->resolve_data, context); } @@ -980,3 +1019,42 @@ void scope_generate_ir(Scope *self, IrCompilerContext *context) { ignore_result_int(ast_generate_ir(*ast, context)); } } + +void scope_generate_function_ids(Scope *self, IrCompilerContext *context) { + Ast **ast = buffer_begin(&self->ast_objects); + Ast **ast_end = buffer_end(&self->ast_objects); + for(; ast != ast_end; ++ast) { + if((*ast)->type == AST_LHS && (*ast)->value.lhs_expr->rhs_expr->type == AST_FUNCTION_DECL) { + LhsExpr *lhs_expr = (*ast)->value.lhs_expr; + FunctionDecl *func_decl = lhs_expr->rhs_expr->value.func_decl; + /* + Going depth first will optimize scope private closures, so they are declared + before the function they are defined in. This means calling them wont create a deferred function call + in program.c + */ + scope_generate_functions_ir(&func_decl->body, context); + /* TODO: Should this not be done for extern closures? */ + throw_if_error(ir_inc_func_index(context->ir, &func_decl->ir_func_index)); + } + } +} + +void scope_generate_functions_ir(Scope *self, IrCompilerContext *context) { + Ast **ast = buffer_begin(&self->ast_objects); + Ast **ast_end = buffer_end(&self->ast_objects); + for(; ast != ast_end; ++ast) { + if((*ast)->type == AST_LHS && (*ast)->value.lhs_expr->rhs_expr->type == AST_FUNCTION_DECL) { + LhsExpr *lhs_expr = (*ast)->value.lhs_expr; + FunctionDecl *func_decl = lhs_expr->rhs_expr->value.func_decl; + /* + Going depth first will optimize scope private closures, so they are declared + before the function they are defined in. This means calling them wont create a deferred function call + in program.c + */ + scope_generate_functions_ir(&func_decl->body, context); + ignore_result_int(funcdecl_generate_ir(func_decl, context)); + if(LHS_EXPR_IS_EXPORT(lhs_expr)) + throw_if_error(ir_try_add_export_func(context->ir, func_decl->signature, lhs_expr->var_name)); + } + } +} diff --git a/src/parser.c b/src/parser.c index fb21b19..204e391 100644 --- a/src/parser.c +++ b/src/parser.c @@ -54,10 +54,6 @@ int parser_init(Parser *self, amal_compiler *compiler, ArenaAllocator *allocator return PARSER_OK; } -static bool parser_is_current_scope_file_scope(Parser *self) { - return self->current_scope == &self->struct_decl.body; -} - /* BODY_LOOP = BODY* @end_token */ @@ -410,7 +406,6 @@ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { } /* - CLOSURE = FUNC_SIGNATURE '{' BODY_LOOP '}' */ static CHECK_RESULT FunctionDecl* parser_parse_closure(Parser *self) { @@ -422,12 +417,6 @@ static CHECK_RESULT FunctionDecl* parser_parse_closure(Parser *self) { if(!signature) return NULL; - /* - TODO: Implement function declaration inside other functions. - Such functions should be moved to the file scope in the bytecode generation - */ - assert(parser_is_current_scope_file_scope(self) && "TODO: Implement function declaration inside other functions."); - throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)&result)); throw_if_error(funcdecl_init(result, signature, self->current_scope, self->allocator)); signature->func_decl = result; @@ -652,15 +641,17 @@ static CHECK_RESULT WhileStatement* parser_parse_while_statement(Parser *self) { return result; } +/* +NUMBER = TOK_NUMBER +*/ static CHECK_RESULT Ast* parser_parse_number(Parser *self) { Ast *result; bool match; Number *number; - result = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_NUMBER, &match)); if(!match) - return result; + return NULL; throw_if_error(arena_allocator_alloc(self->allocator, sizeof(Number), (void**)&number)); number_init(number, &self->tokenizer.number, self->tokenizer.value.identifier); @@ -669,10 +660,29 @@ static CHECK_RESULT Ast* parser_parse_number(Parser *self) { } /* -RHS_S = STRING | NUMBER | FUNC_CALL_OR_VARIABLE +BOOL = TOK_BOOL +*/ +static CHECK_RESULT Ast* parser_parse_bool(Parser *self) { + Ast *result; + bool match; + AstBool *ast_bool; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_BOOL, &match)); + if(!match) + return NULL; + + throw_if_error(arena_allocator_alloc(self->allocator, sizeof(AstBool), (void**)&ast_bool)); + ast_bool_init(ast_bool, self->tokenizer.bool_value, self->tokenizer.value.identifier); + throw_if_error(ast_create(self->allocator, ast_bool, AST_BOOL, &result)); + return result; +} + +/* +RHS_S = STRING | NUMBER | BOOL | CLOSURE | FUNC_CALL_OR_VARIABLE */ static CHECK_RESULT Ast* parser_parse_rhs_single_expr(Parser *self) { Ast *result; + FunctionDecl *func_decl; bool match; result = NULL; @@ -689,6 +699,16 @@ static CHECK_RESULT Ast* parser_parse_rhs_single_expr(Parser *self) { if(result) return result; + result = parser_parse_bool(self); + if(result) + return result; + + func_decl = parser_parse_closure(self); + if(func_decl) { + throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &result)); + return result; + } + result = parser_parse_function_call_or_variable(self); if(result) return result; diff --git a/src/program.c b/src/program.c index 6c5ac7b..31485ca 100644 --- a/src/program.c +++ b/src/program.c @@ -605,7 +605,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ self->return_value_index = 0; /* func_offset will only be non-zero when the function has been decoded (FUNC_START) */ - if(func_def.func_offset != 0) { + if(func_def.func_offset != ~(u32)0UL) { /* TODO: Instead of pushing num args, push the sum of sizeof the last num_args */ return_if_error(amal_exec_call(executor, func_def.func_offset, dst_reg)); } else { @@ -633,7 +633,12 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ break; } case AMAL_OP_CALLR: { - assert(bool_false && "TODO: Implement CALLR"); + AmalReg dst_reg; + AmalReg func_ptr_reg = self->data.data[self->read_index]; + assert(self->return_value_index == 1); + dst_reg = self->return_values_stack[0]; + self->return_value_index = 0; + return_if_error(amal_exec_callr(executor, func_ptr_reg, dst_reg)); self->read_index += 1; break; } diff --git a/src/tokenizer.c b/src/tokenizer.c index c93add3..73dd83d 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -203,6 +203,10 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { if(am_memeql(self->value.identifier.data, "else", 4)) { *token = TOK_ELSE; return TOKENIZER_OK; + } else if(am_memeql(self->value.identifier.data, "true", 4)) { + *token = TOK_BOOL; + self->bool_value = bool_true; + return TOKENIZER_OK; } break; } @@ -213,6 +217,10 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } else if(am_memeql(self->value.identifier.data, "while", 5)) { *token = TOK_WHILE; return TOKENIZER_OK; + } else if(am_memeql(self->value.identifier.data, "false", 5)) { + *token = TOK_BOOL; + self->bool_value = bool_false; + return TOKENIZER_OK; } break; } @@ -259,6 +267,8 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { return TOKENIZER_ERR; } + /* TODO: Check if the number fits in the result bits */ + if(dot_index == -1) { int result = string_to_integer_unchecked(number_str, &self->number.value.integer); if(result != 0) { @@ -542,6 +552,12 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_STRING: str = "string"; break; + case TOK_NUMBER: + str = "number"; + break; + case TOK_BOOL: + str = "bool"; + break; case TOK_FN: str = "fn"; break; @@ -572,9 +588,6 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_IMPORT: str = "import"; break; - case TOK_NUMBER: - str = "number"; - break; case TOK_BINOP: /* TODO: binop_to_string */ str = "binop"; diff --git a/std/io.amal b/std/io.amal index 6acfb1b..ea0845f 100644 --- a/std/io.amal +++ b/std/io.amal @@ -1,6 +1,6 @@ extern const print_extern: fn() i32; -extern const printf: fn(fmt: ?&c_char, ...) c_int; +extern const printf: fn(fmt: &c_char, ...) c_int; -pub const print = fn(fmt: str, arg1: str) i32 { - return printf(fmt, arg1); +pub const print1 = fn(fmt: str) i32 { + return printf(fmt); } \ No newline at end of file diff --git a/tests/bug.amal b/tests/bug.amal new file mode 100644 index 0000000..170e72c --- /dev/null +++ b/tests/bug.amal @@ -0,0 +1,5 @@ +const func = fn(arg: i32) i32 { return 20; } + +const main = fn { + func(23); +} \ No newline at end of file diff --git a/tests/closure.amal b/tests/closure.amal new file mode 100644 index 0000000..c5b3e15 --- /dev/null +++ b/tests/closure.amal @@ -0,0 +1,37 @@ +// extern const printf: fn(fmt: &c_char, ...) c_int; + +/* +const apply = fn(func: fn() bool) i32 { + //const f = func; + const result = func(); + //const result2 = f(); +} + +const main = fn { + // TODO: Test this + // const f = printf; + // f("hello"); + + apply(fn() bool { + return true; + }); + + // Or store in a variable and use it + const func = fn() bool { + return true; + } + apply(func); +} +*/ + +const f = fn(func: fn() i32) i32 { + func(); +} + +const bla = fn() i32 { + +} + +const main = fn { + f(bla); +} \ No newline at end of file diff --git a/tests/conditions.amal b/tests/conditions.amal new file mode 100644 index 0000000..89ded06 --- /dev/null +++ b/tests/conditions.amal @@ -0,0 +1,16 @@ +extern const printf: fn(fmt: &c_char, ...) c_int; + +const main = fn { + var value = 23 + 50; + if value == 0 + printf("zero!\n"); + else if value < 23 + printf("less!\n"); + else + printf("more!\n"); + + while value > 0 { + printf("value: %d\n", value); + value = value - 1; + } +} \ No newline at end of file diff --git a/tests/glfw.amal b/tests/glfw.amal old mode 100644 new mode 100755 index 37608fc..7c4a8ab --- a/tests/glfw.amal +++ b/tests/glfw.amal @@ -7,9 +7,9 @@ extern const glfwWindowShouldClose: fn(window: u64) i32; const main = fn { glfwInit(); - const window = glfwCreateWindow(50i32, 50i32, "hello, world", 0, 0); + const window = glfwCreateWindow(50i32, 50i32, "hello, world", 0u64, 0u64); while glfwWindowShouldClose(window) == 0 { } glfwTerminate(); -} \ No newline at end of file +} diff --git a/tests/hello_world.amal b/tests/hello_world.amal new file mode 100644 index 0000000..7c68d3f --- /dev/null +++ b/tests/hello_world.amal @@ -0,0 +1,5 @@ +extern const printf: fn(fmt: &c_char, ...) c_int; + +const main = fn { + printf("hello, world!\n"); +} \ No newline at end of file -- cgit v1.2.3