diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bytecode/bytecode.c | 185 | ||||
-rw-r--r-- | src/compiler.c | 21 | ||||
-rw-r--r-- | src/parser.c | 38 | ||||
-rw-r--r-- | src/program.c | 135 | ||||
-rw-r--r-- | src/ssa/ssa.c | 87 | ||||
-rw-r--r-- | src/tokenizer.c | 6 |
6 files changed, 302 insertions, 170 deletions
diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c index 80cc95b..fe3dc0f 100644 --- a/src/bytecode/bytecode.c +++ b/src/bytecode/bytecode.c @@ -23,6 +23,36 @@ int bytecode_init(Bytecode *self, ArenaAllocator *allocator) { return buffer_init(&self->data, allocator); } +/*doc(Bytecode) +The layout of the full bytecode is: Header (Intermediates Strings Functions External_Functions Exported_Functions Instructions)* +*/ + +CHECK_RESULT int buffer_append_header(Buffer *program_data) { + /*doc(Bytecode header) + # Header layout + |Type|Field |Description | + |----|-------------|----------------------------------------------------------------------------| + |u32 |Magic number |The magic number used to identify an amalgam bytecode file. | + |u8 |Major version|The major version of the bytecode. Updates in this is a breaking change. | + |u8 |Minor version|The minor version of the bytecode. Updates in this are backwards compatible.| + |u8 |Patch version|The patch version of the bytecode. Updates in this are only minor bug fixes.| + + The versions in the header only changes for every release, not every change. + */ + + const u32 magic_number = AMAL_BYTECODE_MAGIC_NUMBER; + const u8 major_version = AMAL_BYTECODE_MAJOR_VERSION; + const u8 minor_version = AMAL_BYTECODE_MINOR_VERSION; + const u8 patch_version = AMAL_BYTECODE_PATCH_VERSION; + + return_if_error(buffer_append(program_data, &magic_number, 4)); + return_if_error(buffer_append(program_data, &major_version, 1)); + return_if_error(buffer_append(program_data, &minor_version, 1)); + return_if_error(buffer_append(program_data, &patch_version, 1)); + + return 0; +} + static CHECK_RESULT usize ssa_extract_data(u8 *instruction_data, void *result, usize size) { am_memcpy(result, instruction_data, size); return size; @@ -43,18 +73,12 @@ static void add_intermediates(BytecodeCompilerContext *self) { |u64 |Value|The type of the value depends on the value of @Type.| */ - Ssa *ssa; - Buffer *instructions; - SsaNumber *intermediate; - SsaNumber *intermediates_end; - u32 intemediates_size; - - ssa = self->parser->ssa; - instructions = &self->bytecode.data; - intermediate = buffer_begin(&ssa->intermediates); - intermediates_end = buffer_end(&ssa->intermediates); + Ssa *ssa = self->parser->ssa; + Buffer *instructions = &self->bytecode.data; + SsaNumber *intermediate = buffer_begin(&ssa->intermediates); + SsaNumber *intermediates_end = buffer_end(&ssa->intermediates); - intemediates_size = (sizeof(u8) + sizeof(u64)) * buffer_get_size(&ssa->intermediates, SsaNumber); + u32 intemediates_size = (sizeof(u8) + sizeof(u64)) * buffer_get_size(&ssa->intermediates, SsaNumber); throw_if_error(buffer_expand(instructions, sizeof(u32) + intemediates_size)); throw_if_error(buffer_append(instructions, &intemediates_size, sizeof(u32))); for(; intermediate != intermediates_end; ++intermediate) { @@ -80,17 +104,11 @@ static void add_strings(BytecodeCompilerContext *self) { |u8* |Data|The data of the string, where the size is defined by @Size. Strings are null-terminated.| */ - Ssa *ssa; - Buffer *instructions; - BufferView *string; - BufferView *strings_end; - u32 strings_size; - - ssa = self->parser->ssa; - instructions = &self->bytecode.data; - string = buffer_begin(&ssa->strings); - strings_end = buffer_end(&ssa->strings); - strings_size = 0; + Ssa *ssa = self->parser->ssa; + Buffer *instructions = &self->bytecode.data; + BufferView *string = buffer_begin(&ssa->strings); + BufferView *strings_end = buffer_end(&ssa->strings); + u32 strings_size = 0; for(; string != strings_end; ++string) { strings_size += sizeof(u16) + string->size + 1; /* +1 for null-termination of string */ @@ -136,17 +154,11 @@ static void add_extern_functions(BytecodeCompilerContext *self) { |u8 |name_len|The length of the external function name, in bytes. Excluding the null-terminate character. | |u8* |name |The name of the external function, where the size is defined by @name_len. Names are null-terminated.| */ - - Ssa *ssa; - Buffer *instructions; - SsaExternFunc *extern_func, *extern_func_end; - u32 extern_funcs_size; - - ssa = self->parser->ssa; - instructions = &self->bytecode.data; - extern_func = buffer_begin(&ssa->extern_funcs); - extern_func_end = buffer_end(&ssa->extern_funcs); - extern_funcs_size = 0; + Ssa *ssa = self->parser->ssa; + Buffer *instructions = &self->bytecode.data; + SsaExternFunc *extern_func = buffer_begin(&ssa->extern_funcs); + SsaExternFunc *extern_func_end = buffer_end(&ssa->extern_funcs); + u32 extern_funcs_size = 0; for(; extern_func != extern_func_end; ++extern_func) { extern_funcs_size += sizeof(u8) + sizeof(u8) + extern_func->name.size + 1; /* +1 for null-termination of string */ @@ -158,9 +170,9 @@ static void add_extern_functions(BytecodeCompilerContext *self) { throw_if_error(buffer_append(instructions, &extern_funcs_size, sizeof(u32))); for(; extern_func != extern_func_end; ++extern_func) { const char null_s = '\0'; - u8 num_args; - num_args = buffer_get_size(&extern_func->func_sig->parameters, FunctionParameter); + u8 num_args = buffer_get_size(&extern_func->func_sig->parameters, FunctionParameter); throw_if_error(buffer_append(instructions, &num_args, sizeof(num_args))); + /* TODO: Add namespace to the function name */ throw_if_error(buffer_append(instructions, &extern_func->name.size, sizeof(u8))); throw_if_error(buffer_append(instructions, extern_func->name.data, extern_func->name.size)); throw_if_error(buffer_append(instructions, &null_s, sizeof(char))); @@ -169,6 +181,51 @@ static void add_extern_functions(BytecodeCompilerContext *self) { assert(sizeof(SsaExternFuncIndex) == sizeof(u16) && "Program decoder needs to be updated since size of extern func index has changed"); } +static void add_export_functions(BytecodeCompilerContext *self) { + /*doc(Bytecode exported functions) + # Exported functions layout + |Type |Field |Description | + |-----------------|------------------|-----------------------------------------------------------------------------------------| + |u16 |num_export_func |The number of exported functions. | + |u32 |export_funcs_size |The size of the exported functions section, in bytes. | + |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. | + */ + Ssa *ssa = self->parser->ssa; + Buffer *instructions = &self->bytecode.data; + SsaExportFunc *export_func = buffer_begin(&ssa->export_funcs); + SsaExportFunc *export_func_end = buffer_end(&ssa->export_funcs); + u32 export_funcs_size = 0; + + for(; export_func != export_func_end; ++export_func) { + export_funcs_size += sizeof(u32) + sizeof(u8) + sizeof(u8) + export_func->name.size + 1; /* +1 for null-termination of string */ + } + export_func = buffer_begin(&ssa->export_funcs); + + throw_if_error(buffer_expand(instructions, sizeof(u16) + sizeof(u32) + export_funcs_size)); + throw_if_error(buffer_append(instructions, &ssa->export_func_counter, sizeof(u16))); + 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; + 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))); + throw_if_error(buffer_append(instructions, &export_func->name.size, sizeof(u8))); + throw_if_error(buffer_append(instructions, export_func->name.data, export_func->name.size)); + throw_if_error(buffer_append(instructions, &null_s, sizeof(char))); + } + + assert(sizeof(SsaExportFuncIndex) == sizeof(u16) && "Program decoder needs to be updated since size of export func index has changed"); +} + static void add_ins1(BytecodeCompilerContext *self, AmalOpcode opcode, const char *fmt) { throw_if_error(buffer_append(&self->bytecode.data, &opcode, sizeof(AmalOpcodeType))); fprintf(stderr, fmt); @@ -177,10 +234,8 @@ static void add_ins1(BytecodeCompilerContext *self, AmalOpcode opcode, const cha static void add_ins2(BytecodeCompilerContext *self, AmalOpcode opcode, i8 reg, const char *fmt) { - Buffer *instructions; - size_t index; - instructions = &self->bytecode.data; - index = instructions->size; + Buffer *instructions = &self->bytecode.data; + size_t index = instructions->size; throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(reg))); instructions->data[index] = opcode; @@ -190,10 +245,8 @@ static void add_ins2(BytecodeCompilerContext *self, AmalOpcode opcode, i8 reg, c } static void add_ins3(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_reg, i8 src_reg, const char *fmt) { - Buffer *instructions; - size_t index; - instructions = &self->bytecode.data; - index = instructions->size; + Buffer *instructions = &self->bytecode.data; + size_t index = instructions->size; throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(dst_reg) + sizeof(src_reg))); instructions->data[index] = opcode; @@ -204,10 +257,8 @@ static void add_ins3(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_re } static void add_ins4(BytecodeCompilerContext *self, AmalOpcode opcode, u16 data, const char *fmt) { - Buffer *instructions; - size_t index; - instructions = &self->bytecode.data; - index = instructions->size; + Buffer *instructions = &self->bytecode.data; + size_t index = instructions->size; throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(data))); instructions->data[index] = opcode; @@ -217,10 +268,8 @@ static void add_ins4(BytecodeCompilerContext *self, AmalOpcode opcode, u16 data, } static void add_ins5(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_reg, i8 reg1, i8 reg2, const char *fmt) { - Buffer *instructions; - size_t index; - instructions = &self->bytecode.data; - index = instructions->size; + Buffer *instructions = &self->bytecode.data; + size_t index = instructions->size; throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(dst_reg) + sizeof(reg1) + sizeof(reg2))); instructions->data[index] = opcode; @@ -232,10 +281,8 @@ static void add_ins5(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_re } static void add_ins6(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_reg, u16 data, const char *fmt) { - Buffer *instructions; - size_t index; - instructions = &self->bytecode.data; - index = instructions->size; + Buffer *instructions = &self->bytecode.data; + size_t index = instructions->size; throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(dst_reg) + sizeof(data))); instructions->data[index] = opcode; @@ -246,10 +293,8 @@ static void add_ins6(BytecodeCompilerContext *self, AmalOpcode opcode, i8 dst_re } static void add_ins7(BytecodeCompilerContext *self, AmalOpcode opcode, u16 idx, i8 num_args, i8 dst_reg, const char *fmt) { - Buffer *instructions; - size_t index; - instructions = &self->bytecode.data; - index = instructions->size; + Buffer *instructions = &self->bytecode.data; + size_t index = instructions->size; throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(idx) + sizeof(num_args) + sizeof(dst_reg))); instructions->data[index] = opcode; @@ -260,25 +305,6 @@ static void add_ins7(BytecodeCompilerContext *self, AmalOpcode opcode, u16 idx, fputc('\n', stderr); } -#if 0 -#define NUM_MAX_REGS 256 -#define NUM_MAX_FUNC_ARGS 32 - -static const char* lhs_expr_get_c_name(BytecodeCompilerContext *self, LhsExpr *lhs_expr) { - if(lhs_expr == self->parser->compiler->default_types.i64) { - return "i64"; - } else if(lhs_expr == self->parser->compiler->default_types.f64) { - return "f64"; - } else if(lhs_expr == self->parser->compiler->default_types.str) { - return"const char*"; - } else { - amal_log_error("Invalid rhs type %p", lhs_expr); - assert(bool_false && "TODO: Implement"); - return ""; - } -} -#endif - static void add_instructions(BytecodeCompilerContext *self) { /*doc(Bytecode instructions) # Instructions layout @@ -359,7 +385,7 @@ static void add_instructions(BytecodeCompilerContext *self) { } 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.num_params_regs, ssa_ins_func_start.num_local_vars_regs, "func_start %d, %u"); + 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"); break; } case SSA_FUNC_END: { @@ -424,5 +450,6 @@ void generate_bytecode_from_ssa(BytecodeCompilerContext *self) { add_strings(self); add_functions(self); add_extern_functions(self); + add_export_functions(self); add_instructions(self); } diff --git a/src/compiler.c b/src/compiler.c index fd9e4ae..3ccb86a 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -373,14 +373,17 @@ int amal_compiler_load_file(amal_compiler_options *options, amal_program *progra return result; } -/* TODO: Verify main func has correct signature */ +/* + TODO: Instead of using amal_log_error, print error message with tokenizer to show where the error is. + This requires finding tokenizer by code reference. +*/ static CHECK_RESULT int validate_main_func(FileScopeReference *main_file_scope, LhsExpr **main_func) { const BufferView main_func_name = { "main", 4 }; LhsExpr *main_func_expr; main_func_expr = structdecl_get_field_by_name(&main_file_scope->parser->struct_decl, main_func_name); if(!main_func_expr) { - amal_log_error("main function missing from start file \"%.*s\"", main_file_scope->canonical_path.size, main_file_scope->canonical_path.data); + amal_log_error("main function missing from start file \"%.*s\". Note: the main function has to be in the file scope.", main_file_scope->canonical_path.size, main_file_scope->canonical_path.data); return AMAL_COMPILER_ERR; } *main_func = main_func_expr; @@ -400,6 +403,19 @@ static CHECK_RESULT int validate_main_func(FileScopeReference *main_file_scope, return AMAL_COMPILER_ERR; } + { + const FunctionDecl *func_decl = main_func_expr->rhs_expr->value.func_decl; + if(buffer_get_size(&func_decl->signature->parameters, FunctionParameter) != 0) { + amal_log_error("main function in start file \"%.*s\" has to be a closure with no parameters", main_file_scope->canonical_path.size, main_file_scope->canonical_path.data); + return AMAL_COMPILER_ERR; + } + + if(buffer_get_size(&func_decl->signature->return_types, FunctionReturnType) != 0) { + amal_log_error("main function in start file \"%.*s\" has to be a closure that doesn't return anything", main_file_scope->canonical_path.size, main_file_scope->canonical_path.data); + return AMAL_COMPILER_ERR; + } + } + return 0; } @@ -437,6 +453,7 @@ int amal_compiler_internal_load_file(amal_compiler *self, const char *filepath, */ LhsExpr *main_func; + /* Wait for all parsing to be done */ if(!thread_pool_join_all_tasks(&self->stage_task_thread_pool)) return -1; amal_log_info("Finished parsing all files, resolving AST"); diff --git a/src/parser.c b/src/parser.c index 85165d7..ff34663 100644 --- a/src/parser.c +++ b/src/parser.c @@ -267,12 +267,13 @@ void parser_parse_var_type_def(Parser *self, VariableType *result) { } /* -LHS_DECLARATION = 'extern'? 'pub'? 'const'|'var' TOK_IDENTIFIER VAR_TYPE_DEF? +LHS_DECLARATION = ('extern'|'export')? 'pub'? 'const'|'var' TOK_IDENTIFIER VAR_TYPE_DEF? */ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { LhsExpr *result; DeclFlag decl_flag; bool is_extern; + bool is_export; bool is_pub; bool is_const; BufferView var_name; @@ -283,10 +284,21 @@ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { decl_flag |= DECL_FLAG_EXTERN; if(self->has_func_parent) { self->error = tokenizer_create_error(&self->tokenizer, - tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), + tokenizer_get_error_index(&self->tokenizer), "Only declarations in global structs can be extern"); throw(PARSER_UNEXPECTED_TOKEN); } + } else { + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EXPORT, &is_export)); + if(is_export) { + decl_flag |= DECL_FLAG_EXPORT; + if(self->has_func_parent) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_error_index(&self->tokenizer), + "Only declarations in global structs can be exported"); + throw(PARSER_UNEXPECTED_TOKEN); + } + } } throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_PUB, &is_pub)); @@ -294,7 +306,7 @@ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { decl_flag |= DECL_FLAG_PUB; if(self->has_func_parent) { self->error = tokenizer_create_error(&self->tokenizer, - tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), + tokenizer_get_error_index(&self->tokenizer), "Only declarations in global structs can be public"); throw(PARSER_UNEXPECTED_TOKEN); } @@ -306,10 +318,10 @@ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { } else { bool isVar; - if(is_extern) { + if(is_extern || is_export) { self->error = tokenizer_create_error(&self->tokenizer, - tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), - "Extern variable have to be declared with \"const\""); + tokenizer_get_error_index(&self->tokenizer), + "Extern and exported variables have to be declared with \"const\""); throw(PARSER_UNEXPECTED_TOKEN); } @@ -732,10 +744,10 @@ static Ast* parser_parse_lhs_rhs(Parser *self, LhsExpr *lhs_expr) { func_decl = parser_parse_closure(self); if(func_decl) { - if(buffer_get_size(&func_decl->signature->return_types, FunctionReturnType) > 1 && LHS_EXPR_IS_EXTERN(lhs_expr)) { + if(buffer_get_size(&func_decl->signature->return_types, FunctionReturnType) > 1 && (LHS_EXPR_IS_EXTERN(lhs_expr) || LHS_EXPR_IS_EXPORT(lhs_expr))) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), - "Extern closure can only have one return value"); + "Extern and export closures can only have one return value"); throw(PARSER_ERR); } throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &result)); @@ -796,7 +808,7 @@ Ast* parser_parse_body(Parser *self) { if(LHS_EXPR_IS_EXTERN(lhs_expr)) { throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); if (lhs_expr->type.type == VARIABLE_TYPE_NONE) { - self->error = tokenizer_create_error(&self->tokenizer, self->tokenizer.prev_index, "A variable can't be declared without a type or assignment"); + self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "An extern variable can't be declared without a type"); throw(PARSER_UNEXPECTED_TOKEN); } return result; @@ -804,10 +816,14 @@ Ast* parser_parse_body(Parser *self) { throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); if(match) { if(lhs_expr->type.type == VARIABLE_TYPE_SIGNATURE) { - self->error = tokenizer_create_error(&self->tokenizer, self->tokenizer.prev_index, "Expected function declaration. Only extern functions can have empty declarations."); + self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "Expected function declaration. Only extern functions can have empty declarations."); throw(PARSER_UNEXPECTED_TOKEN); } else if (lhs_expr->type.type == VARIABLE_TYPE_NONE) { - self->error = tokenizer_create_error(&self->tokenizer, self->tokenizer.prev_index, "A variable can't be declared without a type or assignment"); + /* TODO: Allow this for non-const variables */ + self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "A variable can't be declared without a type or assignment"); + throw(PARSER_UNEXPECTED_TOKEN); + } else if(LHS_EXPR_IS_CONST(lhs_expr)) { + self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_code_reference_index(&self->tokenizer, lhs_expr->var_name.data), "A const variable can't be declared without assignment"); throw(PARSER_UNEXPECTED_TOKEN); } return result; diff --git a/src/program.c b/src/program.c index 082f9fd..8a17ac9 100644 --- a/src/program.c +++ b/src/program.c @@ -26,52 +26,26 @@ typedef struct { NumberUnion value; } Number; -/*doc(Bytecode) -The layout of the full bytecode is: Header (Intermediates Strings Functions External_Functions Instructions)* -*/ - -static CHECK_RESULT int amal_program_append_header(amal_program *self) { - /*doc(Bytecode header) - # Header layout - |Type|Field |Description | - |----|-------------|----------------------------------------------------------------------------| - |u32 |Magic number |The magic number used to identify an amalgam bytecode file. | - |u8 |Major version|The major version of the bytecode. Updates in this is a breaking change. | - |u8 |Minor version|The minor version of the bytecode. Updates in this are backwards compatible.| - |u8 |Patch version|The patch version of the bytecode. Updates in this are only minor bug fixes.| - - The versions in the header only changes for every release, not every change. - */ - - const u32 magic_number = AMAL_PROGRAM_MAGIC_NUMBER; - const u8 major_version = AMAL_PROGRAM_MAJOR_VERSION; - const u8 minor_version = AMAL_PROGRAM_MINOR_VERSION; - const u8 patch_version = AMAL_PROGRAM_PATCH_VERSION; - - return_if_error(buffer_append(&self->data, &magic_number, 4)); - return_if_error(buffer_append(&self->data, &major_version, 1)); - return_if_error(buffer_append(&self->data, &minor_version, 1)); - return_if_error(buffer_append(&self->data, &patch_version, 1)); - - return 0; -} - int amal_program_init(amal_program *self) { ignore_result_int(buffer_init(&self->data, NULL)); - self->string_indices = NULL; - self->extern_func_indices = NULL; - self->intermediates_start = NULL; - self->strings_start = NULL; - self->extern_funcs_start = NULL; - self->read_index = 0; - self->num_intermediates = 0; - self->num_strings = 0; - self->num_functions = 0; - self->num_extern_functions = 0; + self->string_indices = NULL; + self->extern_func_indices = NULL; + self->intermediates_start = NULL; + self->strings_start = NULL; + self->extern_funcs_start = NULL; + self->exported_funcs = NULL; + self->exported_funcs_end = NULL; + self->read_index = 0; + self->main_func_instruction_offset = ~0U; + self->num_intermediates = 0; + self->num_strings = 0; + self->num_functions = 0; + self->num_extern_functions = 0; + self->num_exported_functions = 0; cleanup_if_error(arena_allocator_init(&self->allocator)); cleanup_if_error(hash_map_init(&self->extern_funcs_map, &self->allocator, sizeof(ProgramExternFunc), hash_map_compare_string, amal_hash_string)); - cleanup_if_error(amal_program_append_header(self)); + cleanup_if_error(buffer_append_header(&self->data)); return 0; cleanup: @@ -131,6 +105,36 @@ static CHECK_RESULT int amal_program_get_extern_func_by_index(amal_program *self return 0; } +static CHECK_RESULT int amal_program_set_exported_function_instruction_offset_advance(amal_program *self, u32 instruction_offset) { + if(self->exported_funcs >= self->exported_funcs_end) { + amal_log_error("The number of exported functions in the instructions is more than the number of exported instructions in the header"); + return AMAL_PROGRAM_INSTRUCTION_INVALID_EXPORTED_FUNC_INDEX; + } + + { + u8 num_args; + u8 func_name_size; + + if((usize)(self->exported_funcs_end - self->exported_funcs) < sizeof(instruction_offset) + sizeof(num_args) + sizeof(func_name_size)) + return AMAL_PROGRAM_INSTRUCTION_INVALID_EXPORTED_FUNC_INDEX; + + am_memcpy(self->exported_funcs, &instruction_offset, sizeof(instruction_offset)); + num_args = self->exported_funcs[sizeof(instruction_offset)]; + func_name_size = self->exported_funcs[sizeof(instruction_offset) + sizeof(num_args)]; + self->exported_funcs += sizeof(instruction_offset) + sizeof(num_args) + sizeof(func_name_size); + if(self->main_func_instruction_offset == ~0U && am_memeql(self->exported_funcs, "main", 4)) + self->main_func_instruction_offset = instruction_offset; + + /* +1 to skip null-termination character */ + if((usize)(self->exported_funcs_end - self->exported_funcs) < func_name_size + 1U) + return AMAL_PROGRAM_INSTRUCTION_INVALID_EXPORTED_FUNC_INDEX; + + self->exported_funcs += func_name_size + 1; /* +1 to skip null-termination character */ + } + + return 0; +} + int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode) { return buffer_append(&self->data, bytecode->data.data, bytecode->data.size); } @@ -158,14 +162,14 @@ static CHECK_RESULT int amal_program_read_header(amal_program *self) { am_memcpy(&patch_version, &self->data.data[self->read_index], sizeof(patch_version)); self->read_index += sizeof(u8); - if(magic_number != AMAL_PROGRAM_MAGIC_NUMBER) + if(magic_number != AMAL_BYTECODE_MAGIC_NUMBER) return AMAL_PROGRAM_INVALID_MAGIC_NUMBER; /* A program is only incompatible if the major version is newer than the version that is used to run it. TODO: Implement backwards compatible reads, starting from when the program bytecode breaks backwards compatibility */ - if(major_version > AMAL_PROGRAM_MAJOR_VERSION) + if(major_version > AMAL_BYTECODE_MAJOR_VERSION) return AMAL_PROGRAM_INCOMPATIBLE; return AMAL_PROGRAM_OK; @@ -237,7 +241,7 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) { am_memcpy(&string_size, &self->data.data[self->read_index], sizeof(string_size)); self->read_index += sizeof(string_size); - /* +1 to skip null-termination character */ + /* +1 to skip null-termination character */ if(bytes_left_to_read(self) < string_size + 1U) return AMAL_PROGRAM_INVALID_STRINGS; @@ -291,9 +295,9 @@ static CHECK_RESULT int amal_program_read_external_functions(amal_program *self) func_name_size = self->data.data[self->read_index + sizeof(num_args)]; self->read_index += sizeof(num_args) + sizeof(func_name_size); - /* +1 to skip null-termination character */ + /* +1 to skip null-termination character */ if(bytes_left_to_read(self) < func_name_size + 1U) - return AMAL_PROGRAM_INVALID_STRINGS; + return AMAL_PROGRAM_INVALID_EXTERNAL_FUNCTIONS; self->read_index += func_name_size + 1; /* +1 to skip null-termination character */ } @@ -303,6 +307,28 @@ static CHECK_RESULT int amal_program_read_external_functions(amal_program *self) return AMAL_PROGRAM_OK; } +static CHECK_RESULT int amal_program_read_exported_functions(amal_program *self) { + u32 export_funcs_size; + + if(!amal_program_read_advance(self, &self->num_exported_functions, sizeof(u16))) + return AMAL_PROGRAM_INVALID_EXPORTED_FUNCTIONS; + + if(!amal_program_read_advance(self, &export_funcs_size, sizeof(export_funcs_size))) + return AMAL_PROGRAM_INVALID_EXPORTED_FUNCTIONS; + + if(bytes_left_to_read(self) < export_funcs_size) + return AMAL_PROGRAM_INVALID_EXPORTED_FUNCTIONS_SIZE; + + /* + Exported functions doesn't need list of indices to the data, since exported functions + are always sorted in the instructions in the same order as list of exported functions + */ + self->exported_funcs = (u8*)(self->data.data + self->read_index); + self->exported_funcs_end = self->exported_funcs + export_funcs_size; + self->read_index += export_funcs_size; + return AMAL_PROGRAM_OK; +} + static CHECK_RESULT int amal_program_get_intermediate_by_index(amal_program *self, u16 index, Number *result) { if(index >= self->num_intermediates) return AMAL_PROGRAM_INSTRUCTION_INVALID_INTERMEDIATE_INDEX; @@ -507,7 +533,7 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ break; } case AMAL_OP_FUNC_START: { - u8 func_num_param_regs; + u8 func_flags; u16 func_num_local_var_regs; assert(!inside_func); @@ -515,9 +541,10 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ assert(func_counter < self->num_functions); ++func_counter; - func_num_param_regs = self->data.data[self->read_index]; - (void)func_num_param_regs; - am_memcpy(&func_num_local_var_regs, self->data.data + self->read_index + sizeof(func_num_param_regs), sizeof(func_num_local_var_regs)); + func_flags = self->data.data[self->read_index]; + 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)); self->read_index += 3; break; @@ -549,9 +576,15 @@ int amal_program_run(amal_program *self) { cleanup_if_error(amal_program_read_strings(self)); cleanup_if_error(amal_program_read_functions(self)); cleanup_if_error(amal_program_read_external_functions(self)); + cleanup_if_error(amal_program_read_exported_functions(self)); cleanup_if_error(amal_program_read_instructions(self, executor)); } - result = amal_executor_run(executor); + if(self->main_func_instruction_offset == ~0U) { + amal_log_error("The program is missing a main function"); + result = AMAL_PROGRAM_NO_MAIN_FUNC; + goto cleanup; + } + result = amal_executor_run(executor, self->main_func_instruction_offset); cleanup: amal_executor_deinit(executor); diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c index 500555a..940f636 100644 --- a/src/ssa/ssa.c +++ b/src/ssa/ssa.c @@ -61,9 +61,11 @@ int ssa_init(Ssa *self, Parser *parser) { return_if_error(buffer_init(&self->strings, parser->allocator)); return_if_error(hash_map_init(&self->extern_funcs_map, parser->allocator, sizeof(SsaExternFuncIndex), hash_map_compare_string, amal_hash_string)); return_if_error(buffer_init(&self->extern_funcs, parser->allocator)); + return_if_error(buffer_init(&self->export_funcs, parser->allocator)); self->intermediate_counter = 0; self->string_counter = 0; self->extern_func_counter = 0; + self->export_func_counter = 0; self->reg_counter = 0; self->param_counter = 0; self->func_counter = 0; @@ -192,6 +194,29 @@ static CHECK_RESULT int ssa_try_add_extern_func(Ssa *self, FunctionSignature *fu } } +/* + Exported functions with the same name are allowed to exist in different files, but not in the same file. + There is no need to check if there are two exported functions with the same name in the same file + as that is already checked in the AST stage, as you are not allowed to have multiple variables of the same name in the same scope, + exported or not. +*/ +static CHECK_RESULT int ssa_try_add_export_func(Ssa *self, FunctionSignature *func_sig, BufferView name) { + /* Overflow */ + if(self->export_func_counter + 1 <= self->export_func_counter) { + amal_log_error("Ssa too many exported closures!"); + return -1; + } + + amal_log_debug("ef%u = \"%.*s\"", self->export_func_counter, name.size, name.data); + ++self->export_func_counter; + { + SsaExportFunc export_func; + export_func.func_sig = func_sig; + export_func.name = name; + return buffer_append(&self->export_funcs, &export_func, sizeof(export_func)); + } +} + static CHECK_RESULT int ssa_add_ins_form1(Ssa *self, SsaInstruction ins_type, SsaRegister lhs, u16 rhs) { SsaInsForm1 ins_form_1; ins_form_1.lhs = lhs; @@ -247,7 +272,7 @@ static CHECK_RESULT int ssa_ins_binop(Ssa *self, SsaInstruction binop_type, SsaR return ssa_add_ins_form2(self, binop_type, lhs, rhs, result); } -static CHECK_RESULT int ssa_ins_func_start(Ssa *self, SsaRegister num_reg_params, SsaFuncIndex *result, usize *func_metadata_index) { +static CHECK_RESULT int ssa_ins_func_start(Ssa *self, u8 func_flags, SsaFuncIndex *result, usize *func_metadata_index) { const u8 ins_type = SSA_FUNC_START; SsaInsFuncStart ins_func_start; @@ -258,14 +283,13 @@ static CHECK_RESULT int ssa_ins_func_start(Ssa *self, SsaRegister num_reg_params } *result = self->func_counter++; - ins_func_start.func_index = *result; - ins_func_start.num_params_regs = num_reg_params; + 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", *result, num_reg_params); + amal_log_debug("FUNC_START f%u", *result); return 0; } @@ -428,6 +452,16 @@ static CHECK_RESULT SsaRegister lhsexpr_extern_generate_ssa(LhsExpr *self, SsaCo return 0; } +static CHECK_RESULT SsaRegister lhsexpr_export_generate_ssa(LhsExpr *self, SsaCompilerContext *context) { + /* TODO: SsaRegister should be extended to include static and export data */ + if(self->rhs_expr->type == AST_FUNCTION_DECL) { + throw_if_error(ssa_try_add_export_func(context->ssa, self->rhs_expr->value.func_decl->signature, self->var_name)); + } else { + assert(bool_false && "TODO: Implement lhsexpr_export_generate_ssa for other data than functions"); + } + return 0; +} + static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(LhsExpr *self, AstResolveData *resolve_data, SsaCompilerContext *context) { SsaRegister reg; @@ -438,6 +472,10 @@ static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(LhsExpr *self, AstResolveDa Ast *rhs_expr = self->rhs_expr; SsaRegister rhs_reg; rhs_reg = ast_generate_ssa(rhs_expr, context); + + if(LHS_EXPR_IS_EXPORT(self)) + return lhsexpr_export_generate_ssa(self, context); + /* Declarations (struct and function declaration) resolves to itself and in that case this expression is just a compile-time name for the declaration. @@ -513,15 +551,13 @@ in any order. static CHECK_RESULT SsaRegister funcdecl_generate_ssa(FunctionDecl *self, SsaCompilerContext *context) { /* TODO: Implement */ /* - Reset reg counter in each function, because each function has a separate register context - that is reset after function end + Reset reg counter in each function, because each function has a separate register context + that is reset after function end */ - SsaRegister prev_reg_counter; - SsaRegister prev_param_counter; usize func_metadata_index; - - prev_reg_counter = context->ssa->reg_counter; - prev_param_counter = context->ssa->param_counter; + SsaRegister prev_reg_counter = context->ssa->reg_counter; + SsaRegister prev_param_counter = context->ssa->param_counter; + u8 func_flags = 0; context->ssa->reg_counter = 0; /* @@ -533,7 +569,12 @@ static CHECK_RESULT SsaRegister funcdecl_generate_ssa(FunctionDecl *self, SsaCom context->ssa->reg_counter = 0; amal_log_debug("SSA funcdecl %p", self); - throw_if_error(ssa_ins_func_start(context->ssa, context->ssa->param_counter, &self->ssa_func_index, &func_metadata_index)); + /* Anonymous closure doesn't have lhs_expr, and neither can it have any flags (extern, export etc) */ + if(self->lhs_expr) { + if(LHS_EXPR_IS_EXPORT(self->lhs_expr)) + func_flags |= FUNC_FLAG_EXPORTED; + } + throw_if_error(ssa_ins_func_start(context->ssa, func_flags, &self->ssa_func_index, &func_metadata_index)); scope_generate_ssa(&self->body, context); throw_if_error(ssa_ins_func_end(context->ssa)); @@ -585,7 +626,7 @@ static CHECK_RESULT SsaRegister structdecl_generate_ssa(StructDecl *self, SsaCom static CHECK_RESULT SsaRegister structfield_generate_ssa(StructField *self, SsaCompilerContext *context) { /* TODO: Implement */ - /*assert(bool_false);*/ + assert(bool_false); (void)self; (void)context; return 0; @@ -644,8 +685,7 @@ static CHECK_RESULT SsaRegister binop_generate_ssa(Binop *self, SsaCompilerConte } static void else_if_statement_generate_ssa(ElseIfStatement *else_if_stmt, SsaCompilerContext *context) { - usize jump_ins_index; - jump_ins_index = 0; + usize jump_ins_index = 0; if(else_if_stmt->condition) { SsaRegister condition_reg; condition_reg = ast_generate_ssa(else_if_stmt->condition, context); @@ -660,11 +700,8 @@ static void else_if_statement_generate_ssa(ElseIfStatement *else_if_stmt, SsaCom } static void if_statement_generate_ssa(IfStatement *if_stmt, SsaCompilerContext *context) { - SsaRegister condition_reg; - usize jump_ins_index; - - condition_reg = ast_generate_ssa(if_stmt->condition, context); - jump_ins_index = ssa_ins_get_index(context->ssa); + SsaRegister condition_reg = ast_generate_ssa(if_stmt->condition, context); + usize jump_ins_index = ssa_ins_get_index(context->ssa); throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0)); scope_generate_ssa(&if_stmt->body, context); throw_if_error(ssa_ins_jump_set_target(context->ssa, jump_ins_index)); @@ -673,14 +710,10 @@ static void if_statement_generate_ssa(IfStatement *if_stmt, SsaCompilerContext * } static void while_statement_generate_ssa(WhileStatement *while_stmt, SsaCompilerContext *context) { - SsaRegister condition_reg; - usize jump_back_ins_index; - usize jump_condition_ins_index; isize jump_offset; - - jump_back_ins_index = ssa_ins_get_index(context->ssa); - condition_reg = ast_generate_ssa(while_stmt->condition, context); - jump_condition_ins_index = ssa_ins_get_index(context->ssa); + usize jump_back_ins_index = ssa_ins_get_index(context->ssa); + SsaRegister condition_reg = ast_generate_ssa(while_stmt->condition, context); + usize jump_condition_ins_index = ssa_ins_get_index(context->ssa); throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0)); scope_generate_ssa(&while_stmt->body, context); /* Jump back and check condition again before running the content of the loop again */ diff --git a/src/tokenizer.c b/src/tokenizer.c index 556a20b..82ea0f6 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -217,6 +217,9 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } else if(am_memeql(self->value.identifier.data, "extern", 6)) { *token = TOK_EXTERN; return TOKENIZER_OK; + } else if(am_memeql(self->value.identifier.data, "export", 6)) { + *token = TOK_EXPORT; + return TOKENIZER_OK; } else if(am_memeql(self->value.identifier.data, "return", 6)) { *token = TOK_RETURN; return TOKENIZER_OK; @@ -496,6 +499,9 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_EXTERN: str = "extern"; break; + case TOK_EXPORT: + str = "export"; + break; case TOK_RETURN: str = "return"; break; |