aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bytecode/bytecode.c185
-rw-r--r--src/compiler.c21
-rw-r--r--src/parser.c38
-rw-r--r--src/program.c135
-rw-r--r--src/ssa/ssa.c87
-rw-r--r--src/tokenizer.c6
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;