diff options
Diffstat (limited to 'src/ssa')
-rw-r--r-- | src/ssa/ssa.c | 982 |
1 files changed, 0 insertions, 982 deletions
diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c deleted file mode 100644 index 44b5589..0000000 --- a/src/ssa/ssa.c +++ /dev/null @@ -1,982 +0,0 @@ -#include "../../include/ssa/ssa.h" -#include "../../include/std/mem.h" -#include "../../include/std/log.h" -#include "../../include/std/hash.h" -#include "../../include/std/thread.h" -#include "../../include/ast.h" -#include "../../include/compiler.h" -#include "../../include/parser.h" -#include <assert.h> - -/* - TODO: Instead of using memcpy to copy data to the ssa, make it cleaner by - defining all the data in structs and copying the structs. Even if it takes more space, - it might even be faster. -*/ - -#define throw(result) do { longjmp(context->env, (result)); } while(0) -#define throw_if_error(result) \ -do { \ - int return_if_result; \ - return_if_result = (result); \ - if((return_if_result) != 0) \ - throw(return_if_result); \ -} while(0) - -/* Max length of a string that fits in u16 */ -#define MAX_STRING_LENGTH UINT16_MAX -#define FUNC_MAX_ARGS 128 - -static CHECK_RESULT SsaRegister variable_generate_ssa(Variable *self, SsaCompilerContext *context); - -static int compare_number(const void *a, const void *b) { - const SsaNumber *lhs = a; - const SsaNumber *rhs = b; - return (rhs->type == lhs->type && rhs->value.integer == lhs->value.integer); -} - -static usize hash_number(const u8 *data, usize size) { - SsaNumber number; - assert(size == sizeof(SsaNumber)); - am_memcpy(&number, data, size); - return number.value.integer; -} - -SsaNumber create_ssa_integer(i64 value) { - SsaNumber result; - result.value.integer = value; - result.type = SSA_NUMBER_TYPE_INTEGER; - return result; -} - -SsaNumber create_ssa_float(f64 value) { - SsaNumber result; - result.value.floating = value; - result.type = SSA_NUMBER_TYPE_FLOAT; - return result; -} - -int ssa_init(Ssa *self, Parser *parser) { - return_if_error(buffer_init(&self->instructions, parser->allocator)); - return_if_error(hash_map_init(&self->intermediates_map, parser->allocator, sizeof(SsaIntermediateIndex), compare_number, hash_number)); - return_if_error(buffer_init(&self->intermediates, parser->allocator)); - return_if_error(hash_map_init(&self->strings_map, parser->allocator, sizeof(SsaStringIndex), hash_map_compare_string, amal_hash_string)); - 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)); - return_if_error(buffer_init(&self->funcs, parser->allocator)); - self->intermediate_counter = 0; - self->string_counter = 0; - self->extern_func_counter = 0; - self->export_func_counter = 0; - self->func_counter = 0; - self->reg_counter = 0; - self->param_counter = 0; - self->label_counter = 0; - self->parser = parser; - return 0; -} - -static CHECK_RESULT int ssa_get_unique_reg(Ssa *self, SsaRegister *result) { - assert(result); - /* Overflow */ - if((u16)self->reg_counter + 1 > INT16_MAX) { - amal_log_error("Ssa too many registers!"); - return -1; - } - assert(self->reg_counter <= INT8_MAX && "TODO: Implement usage of reg higher than 128"); - *result = self->reg_counter++; - return 0; -} - -static CHECK_RESULT int ssa_get_unique_param_reg(Ssa *self, SsaRegister *result) { - assert(result); - /* Overflow */ - if((u16)self->param_counter + 1 > INT16_MAX) { - amal_log_error("Ssa too many param registers!"); - return -1; - } - assert(self->param_counter <= INT8_MAX && "TODO: Implement usage of reg higher than 128"); - *result = self->param_counter++ | REG_FLAG_PARAM; - return 0; -} - -SsaNumber ssa_get_intermediate(Ssa *self, SsaIntermediateIndex index) { - SsaNumber result; - assert(index < buffer_get_size(&self->intermediates, SsaNumber)); - am_memcpy(&result, buffer_get(&self->intermediates, index, sizeof(SsaNumber)), sizeof(SsaNumber)); - return result; -} - -BufferView ssa_get_string(Ssa *self, SsaStringIndex index) { - BufferView result; - assert(index < buffer_get_size(&self->strings, BufferView)); - am_memcpy(&result, buffer_get(&self->strings, index, sizeof(BufferView)), sizeof(BufferView)); - return result; -} - -static CHECK_RESULT int ssa_try_add_intermediate(Ssa *self, SsaNumber number, SsaIntermediateIndex *result_index) { - bool exists; - BufferView key; - - assert(result_index); - key = create_buffer_view((const char*)&number, sizeof(number)); - - exists = hash_map_get(&self->intermediates_map, key, result_index); - if(exists) - return 0; - - /* Overflow */ - if(self->intermediate_counter + 1 <= self->intermediate_counter) { - amal_log_error("Ssa too many intermediates!"); - return -1; - } - - *result_index = self->intermediate_counter; - ++self->intermediate_counter; - switch(number.type) { - case SSA_NUMBER_TYPE_INTEGER: { - amal_log_debug("i%u = %lld", *result_index, number.value.integer); - break; - } - case SSA_NUMBER_TYPE_FLOAT: { - amal_log_debug("i%u = %f", *result_index, number.value.floating); - break; - } - } - - return_if_error(buffer_append(&self->intermediates, &number, sizeof(number))); - return hash_map_insert(&self->intermediates_map, key, result_index); -} - -static CHECK_RESULT int ssa_try_add_string(Ssa *self, BufferView str, SsaStringIndex *result_index) { - bool exists; - assert(result_index); - - exists = hash_map_get(&self->strings_map, str, result_index); - if(exists) - return 0; - - /* Overflow */ - if(self->string_counter + 1 <= self->string_counter) { - amal_log_error("Ssa too many strings!"); - return -1; - } - - if(str.size > MAX_STRING_LENGTH) { - amal_log_error("String \"%.*s\" is longer than %d\n", str.size, str.data, MAX_STRING_LENGTH); - return -2; - } - - *result_index = self->string_counter; - ++self->string_counter; - amal_log_debug("s%u = \"%.*s\"", *result_index, str.size, str.data); - return_if_error(buffer_append(&self->strings, &str, sizeof(str))); - return hash_map_insert(&self->strings_map, str, result_index); -} - -/* - TODO: Right now this has the same scope as a file. This should be global, otherwise you could define multiple - extern func with the same name but different signature as long as they are defined in different files -*/ -static CHECK_RESULT int ssa_try_add_extern_func(Ssa *self, FunctionSignature *func_sig, BufferView name, SsaExternFuncIndex *result_index, BufferView *existing_func) { - bool exists; - assert(result_index); - assert(existing_func); - - exists = hash_map_get(&self->extern_funcs_map, name, result_index); - if(exists) { - const SsaExternFunc *existing_extern_func = buffer_get(&self->extern_funcs, *result_index, sizeof(SsaExternFunc)); - *existing_func = existing_extern_func->name; - if(!function_signature_equals(func_sig, existing_extern_func->func_sig)) - return SSA_ERR_EXTERN_FUNC_SIG_MISMATCH; - return 0; - } - - /* Overflow */ - if(self->extern_func_counter + 1 <= self->extern_func_counter) { - amal_log_error("Ssa too many extern closures!"); - return -1; - } - - *result_index = self->extern_func_counter; - ++self->extern_func_counter; - amal_log_debug("extern_func%u = %.*s", *result_index, name.size, name.data); - { - SsaExternFunc extern_func; - extern_func.func_sig = func_sig; - extern_func.name = name; - return_if_error(buffer_append(&self->extern_funcs, &extern_func, sizeof(extern_func))); - return hash_map_insert(&self->extern_funcs_map, name, result_index); - } -} - -/* - 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("exported_func%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; - ins_form_1.rhs = rhs; - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, &ins_form_1, sizeof(ins_form_1)); -} - -static const char* binop_type_to_string(SsaInstruction binop_type) { - assert(binop_type >= SSA_ADD && binop_type <= SSA_GE); - switch(binop_type) { - case SSA_ADD: return "+"; - case SSA_SUB: return "-"; - case SSA_MUL: return "*"; - case SSA_DIV: return "/"; - case SSA_EQUALS: return "=="; - case SSA_AND: return "&&"; - case SSA_ILT: return "<"; - case SSA_LT: return "<"; - case SSA_ILE: return "<="; - case SSA_LE: return "<="; - case SSA_IGT: return ">"; - case SSA_GT: return ">"; - case SSA_IGE: return ">="; - case SSA_GE: return ">="; - default: return ""; - } -} - -static CHECK_RESULT int ssa_add_ins_form2(Ssa *self, SsaInstruction ins_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result) { - SsaInsForm2 ins_form_2; - return_if_error(ssa_get_unique_reg(self, result)); - ins_form_2.result = *result; - ins_form_2.lhs = lhs; - ins_form_2.rhs = rhs; - amal_log_debug("r%d = r%d %s r%d", *result, lhs, binop_type_to_string(ins_type), rhs); - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, &ins_form_2, sizeof(ins_form_2)); -} - -static CHECK_RESULT int ssa_ins_assign_inter(Ssa *self, SsaRegister dest, SsaNumber number) { - SsaIntermediateIndex index; - return_if_error(ssa_try_add_intermediate(self, number, &index)); - amal_log_debug("r%d = i%u", dest, index); - return ssa_add_ins_form1(self, SSA_ASSIGN_INTER, dest, index); -} - -static CHECK_RESULT int ssa_ins_assign_string(Ssa *self, SsaRegister dest, BufferView str) { - SsaStringIndex index; - return_if_error(ssa_try_add_string(self, str, &index)); - amal_log_debug("r%d = s%u", dest, index); - return ssa_add_ins_form1(self, SSA_ASSIGN_STRING, dest, index); -} - -static CHECK_RESULT int ssa_ins_assign_reg(Ssa *self, SsaRegister dest, SsaRegister src) { - amal_log_debug("r%d = r%d", dest, src); - return ssa_add_ins_form1(self, SSA_ASSIGN_REG, dest, src); -} - -static CHECK_RESULT int ssa_ins_binop(Ssa *self, SsaInstruction binop_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result) { - assert(binop_type >= SSA_ADD && binop_type <= SSA_GE); - return ssa_add_ins_form2(self, binop_type, lhs, rhs, result); -} - -static CHECK_RESULT int ssa_ins_func_start(Ssa *self, u8 func_flags, FunctionSignature *func_sig, SsaFuncIndex *result, usize *func_metadata_index) { - const u8 ins_type = SSA_FUNC_START; - SsaInsFuncStart ins_func_start; - - /* Overflow */ - if(self->func_counter + 1 <= self->func_counter) { - amal_log_error("Ssa too many closures!"); - return -1; - } - - *result = self->func_counter++; - { - SsaFunc 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)); - return 0; -} - -static CHECK_RESULT int ssa_ins_func_end(Ssa *self) { - const u8 ins_type = SSA_FUNC_END; - amal_log_debug("FUNC_END"); - return buffer_append(&self->instructions, &ins_type, 1); -} - -static CHECK_RESULT int ssa_ins_push(Ssa *self, SsaRegister reg) { - const u8 ins_type = SSA_PUSH; - amal_log_debug("PUSH r%d", reg); - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, ®, sizeof(reg)); -} - -static CHECK_RESULT int ssa_ins_push_ret(Ssa *self, SsaRegister reg) { - const u8 ins_type = SSA_PUSH_RET; - amal_log_debug("PUSH RET r%d", reg); - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, ®, sizeof(reg)); -} - -static CHECK_RESULT int ssa_ins_call_start(Ssa *self, u8 num_args) { - const u8 ins_type = SSA_CALL_START; - SsaInsCallStart ins_call_start; - ins_call_start.num_args = num_args; - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, &ins_call_start, sizeof(ins_call_start)); -} - -static CHECK_RESULT int ssa_ins_call(Ssa *self, int import_index, FunctionDecl *func_decl) { - const u8 ins_type = SSA_CALL; - SsaInsFuncCall ins_func_call; - ins_func_call.func_decl = func_decl; - ins_func_call.import_index = import_index; - amal_log_debug("CALL f(%d,%p)", import_index, func_decl); - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, &ins_func_call, sizeof(ins_func_call)); -} - -static CHECK_RESULT int ssa_ins_call_extern(Ssa *self, int import_index, LhsExpr *func_decl_lhs) { - const u8 ins_type = SSA_CALL_EXTERN; - SsaInsFuncCallExtern ins_func_call_extern; - ins_func_call_extern.func_decl_lhs = func_decl_lhs; - ins_func_call_extern.import_index = import_index; - amal_log_debug("CALL_EXTERN ef(%d,%p)", import_index, func_decl_lhs); - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, &ins_func_call_extern, sizeof(ins_func_call_extern)); -} - -static CHECK_RESULT int ssa_ins_jumpzero(Ssa *self, SsaRegister condition_reg, SsaLabelIndex target_label, usize *instruction_offset) { - const u8 ins_type = SSA_JUMP_ZERO; - SsaInsJumpZero ins_jump_zero; - ins_jump_zero.condition_reg = condition_reg; - ins_jump_zero.target_label = target_label; - if(target_label == 0) - amal_log_debug("JUMP_ZERO r%d, DUMMY", condition_reg); - else - amal_log_debug("JUMP_ZERO r%d, l%d", condition_reg, target_label); - *instruction_offset = self->instructions.size; - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, &ins_jump_zero, sizeof(ins_jump_zero)); -} - -static CHECK_RESULT int ssa_ins_jump(Ssa *self, SsaLabelIndex target_label, usize *instruction_offset) { - const u8 ins_type = SSA_JUMP; - SsaInsJump ins_jump; - ins_jump.target_label = target_label; - - if(target_label == 0) - amal_log_debug("JUMP DUMMY"); - else - amal_log_debug("JUMP l%d", target_label); - - if(instruction_offset) - *instruction_offset = self->instructions.size; - - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, &ins_jump, sizeof(ins_jump)); -} - -static CHECK_RESULT int ssa_ins_return(Ssa *self, SsaRegister reg) { - const u8 ins_type = SSA_RET; - return_if_error(buffer_append(&self->instructions, &ins_type, 1)); - return buffer_append(&self->instructions, ®, sizeof(reg)); -} - -static CHECK_RESULT int ssa_ins_label(Ssa *self, u16 *label_index) { - const u8 ins_type = SSA_LABEL; - /* Overflow */ - if(self->label_counter + 1 <= self->label_counter) { - amal_log_error("Ssa too many labels!"); - return -1; - } - *label_index = self->label_counter; - ++self->label_counter; - return buffer_append(&self->instructions, &ins_type, 1); -} - -static CHECK_RESULT int ssa_set_jump_label(Ssa *self, usize jump_ins_index, SsaLabelIndex label) { - switch(self->instructions.data[jump_ins_index]) { - case SSA_JUMP_ZERO: { - /* +1 to skip instruction opcode */ - am_memcpy(self->instructions.data + jump_ins_index + 1 + offsetof(SsaInsJumpZero, target_label), &label, sizeof(SsaLabelIndex)); - break; - } - case SSA_JUMP: { - /* +1 to skip instruction opcode */ - am_memcpy(self->instructions.data + jump_ins_index + 1 + offsetof(SsaInsJump, target_label), &label, sizeof(SsaLabelIndex)); - break; - } - default: - assert(bool_false && "Unexpected error... jump_ins_index doesn't point to a valid index to a jump instruction"); - break; - } - return 0; -} - -static CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context); -static CHECK_RESULT SsaRegister scope_named_object_generate_ssa(ScopeNamedObject *self, SsaCompilerContext *context); - -#if 0 -static bool ast_resolved_type_is_decl(AstResolvedType *self) { - /* TODO: Add more types as they are introduced */ - LhsExpr *lhs_expr; - if(self->type != RESOLVED_TYPE_LHS_EXPR) - return bool_false; - - lhs_expr = self->value.lhs_expr; - switch(lhs_expr->type.type) { - case VARIABLE_TYPE_NONE: - break; - 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; - } - - assert(lhs_expr->rhs_expr); - 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 SsaRegister number_generate_ssa(Number *self, SsaCompilerContext *context) { - SsaRegister reg; - SsaNumber number; - switch(self->value.type) { - case AMAL_NUMBER_SIGNED_INTEGER: - case AMAL_NUMBER_UNSIGNED_INTEGER: - number = create_ssa_integer(self->value.value.integer); - break; - case AMAL_NUMBER_FLOAT: - number = create_ssa_float(self->value.value.floating); - break; - } - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); - return reg; -} - -static CHECK_RESULT SsaRegister lhsexpr_extern_generate_ssa(LhsExpr *self, SsaCompilerContext *context) { - /* TODO: SsaRegister should be extended to include static and extern data */ - if(self->type.type == VARIABLE_TYPE_SIGNATURE) { - int err; - BufferView existing_func; - - err = ssa_try_add_extern_func(context->ssa, self->type.value.signature, self->var_name, &self->extern_index, &existing_func); - if(err == SSA_ERR_EXTERN_FUNC_SIG_MISMATCH) { - Tokenizer *tokenizer; - tokenizer = &context->ssa->parser->tokenizer; - tokenizer_print_error(tokenizer, tokenizer_get_code_reference_index(tokenizer, self->var_name.data), - "Extern closure defined here with the name %.*s doesn't match extern closure with the same name defined in another location", - self->var_name.size, self->var_name.data); - /* - TODO: This wont work right now since the other location might belong to another parser. - There should be a function to get a parser from a code reference (loop all tokens and check code range). - Then the parsers that belong to scopes can also be removed. This is fine, since the lookup is only done on error. - */ - #if 0 - tokenizer_print_error(tokenizer, tokenizer_get_code_reference_index(tokenizer, self->var_name.data), - "Extern closure defined here with the name %.*s doesn't match extern closure with the same name defined in another location", - self->var_name.size, self->var_name.data); - #endif - throw(err); - } - if(err != 0) - throw(err); - } else { - assert(bool_false && "TODO: Implement lhsexpr_extern_generate_ssa for other data than functions"); - } - 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; - - if(LHS_EXPR_IS_EXTERN(self)) - return lhsexpr_extern_generate_ssa(self, context); - - if(self->rhs_expr) { - 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. - Import expression also has no meaning in SSA 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) { - /*assert(bool_false);*/ - return 0; - } - return rhs_reg; - } else { - /* TODO: Do not assign if we dont want default value */ - if(resolve_data->type.type == RESOLVED_TYPE_LHS_EXPR) { - SsaNumber number; - if(resolve_data->type.value.lhs_expr == (LhsExpr*)context->compiler->default_types.i64) - number = create_ssa_integer(0); - else if(resolve_data->type.value.lhs_expr == (LhsExpr*)context->compiler->default_types.f64) - number = create_ssa_float(0.0); - else - assert(bool_false && "TODO: assign default value to reg depending on LhsExpr type"); - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); - } else if(resolve_data->type.type == RESOLVED_TYPE_FUNC_SIG) { - assert(bool_false && "TODO: Implement this when variable can be null. Then the function pointer should be null"); - } else { - assert(bool_false); - } - } - return reg; -} - -static CHECK_RESULT SsaRegister assignmentexpr_generate_ssa(AssignmentExpr *self, SsaCompilerContext *context) { - SsaRegister lhs_reg, rhs_reg; - lhs_reg = ast_generate_ssa(self->lhs_expr, context); - rhs_reg = ast_generate_ssa(self->rhs_expr, context); - throw_if_error(ssa_ins_assign_reg(context->ssa, lhs_reg, rhs_reg)); - return lhs_reg; -} - -static CHECK_RESULT SsaRegister function_parameter_generate_ssa(FunctionParameter *self, SsaCompilerContext *context) { - SsaRegister reg; - if(self->resolve_data.status == AST_SSA_RESOLVED) - return self->resolve_data.ssa_reg; - - throw_if_error(ssa_get_unique_param_reg(context->ssa, ®)); - self->resolve_data.status = AST_SSA_RESOLVED; - self->resolve_data.ssa_reg = reg; - return reg; -} - -static CHECK_RESULT void function_signature_generate_params_ssa(FunctionSignature *self, SsaCompilerContext *context) { - FunctionParameter *param, *param_end; - param = buffer_begin(&self->parameters); - param_end = buffer_end(&self->parameters); - for(; param != param_end; ++param) { - SsaRegister reg; - reg = function_parameter_generate_ssa(param, context); - (void)reg; - } -} - -/* -TODO: Each function declaration should be in separate SSA instances so ast can be converted into ssa -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 - */ - usize func_metadata_index; - u8 func_flags = 0; - context->ssa->reg_counter = 0; - context->ssa->param_counter = 0; - context->ssa->label_counter = 0; - - /* - Parameters need to have generated ssa so the first ssa registers belong to the function. - This way we can know if a register access is for a parameter or not by checking the number - */ - function_signature_generate_params_ssa(self->signature, context); - - amal_log_debug("SSA funcdecl %p", self); - /* Anonymous closure doesn't have lhs_expr, and neither can it have any flags (extern, export etc) */ - if(self->lhs_expr) { - assert(!LHS_EXPR_IS_EXTERN(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->signature, &self->ssa_func_index, &func_metadata_index)); - scope_generate_ssa(&self->body, context); - throw_if_error(ssa_ins_func_end(context->ssa)); - - /* Add the number of registers used to the function metadata (FUNC_START) */ - am_memcpy(context->ssa->instructions.data + func_metadata_index, &context->ssa->reg_counter, sizeof(u16)); - return 0; -} - -static CHECK_RESULT SsaRegister funccall_generate_ssa(FunctionCall *self, SsaCompilerContext *context) { - SsaRegister reg; - FunctionSignature *func_sig; - FunctionDecl *func_decl; - LhsExpr *func_lhs_expr; - int import_index = context->import_index; - context->import_index = 0; - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - - 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; - } - func_decl = func_sig->func_decl; - - /* Push return arguments */ - { - /* - TODO: When amalgam supports multiple return types in assignment/declaration, update this to take - all of them into account. Right now it only uses one return type. - It should also take into account the size of the type. - */ - assert(buffer_get_size(&func_sig->return_types, FunctionReturnType) <= 1); - throw_if_error(ssa_ins_push_ret(context->ssa, reg)); - } - - /* Push parameter arguments */ - assert(buffer_get_size(&self->args, Ast*) <= FUNC_MAX_ARGS); - { - SsaRegister arg_regs[FUNC_MAX_ARGS]; - - Ast **arg = buffer_begin(&self->args); - Ast **arg_end = buffer_end(&self->args); - for(; arg != arg_end; ++arg) { - arg_regs[arg_end - arg] = ast_generate_ssa(*arg, context); - } - - /* - This is done in two steps since first we want the argument expressions to be generated - and then at the end, push the registers. - This allows Amalgam to push arguments directly to registers on abi that uses registers as function arguments (system-v x86_64) - instead of first pushing the registers to stack and then moving them to registers. - */ - - arg = buffer_begin(&self->args); - throw_if_error(ssa_ins_call_start(context->ssa, arg_end - arg)); - for(; arg != arg_end; ++arg) { - throw_if_error(ssa_ins_push(context->ssa, arg_regs[arg_end - arg])); - } - } - - if(func_lhs_expr && LHS_EXPR_IS_EXTERN(func_lhs_expr)) { - throw_if_error(ssa_ins_call_extern(context->ssa, import_index, func_lhs_expr)); - } else { - assert(func_decl); - /* rhs wont be null here because only extern variable can't have rhs */ - throw_if_error(ssa_ins_call(context->ssa, import_index, func_decl)); - } - - return reg; -} - -static CHECK_RESULT SsaRegister structdecl_generate_ssa(StructDecl *self, SsaCompilerContext *context) { - /* TODO: Implement */ - /*assert(bool_false);*/ - scope_generate_ssa(&self->body, context); - return 0; -} - -static CHECK_RESULT SsaRegister structfield_generate_ssa(StructField *self, SsaCompilerContext *context) { - /* TODO: Implement */ - assert(bool_false); - (void)self; - (void)context; - return 0; -} - -static CHECK_RESULT SsaRegister string_generate_ssa(String *self, SsaCompilerContext *context) { - SsaRegister reg; - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - throw_if_error(ssa_ins_assign_string(context->ssa, reg, self->str)); - return reg; -} - -SsaRegister variable_generate_ssa(Variable *self, SsaCompilerContext *context) { - /* TODO: If resolved_var refers to a variable in another file, use a cross file reference that requires no locking (not yet implemented) */ - /* This is not thread-safe:*/ - assert(self->resolved_var.type != NAMED_OBJECT_NONE); - return scope_named_object_generate_ssa(&self->resolved_var, context); -} - -static SsaInstruction binop_type_to_ssa_type(BinopType binop_type, amal_default_type *type) { - switch(binop_type) { - case BINOP_ADD: - return SSA_ADD; - case BINOP_SUB: - return SSA_SUB; - case BINOP_MUL: - return amal_default_type_is_signed(type) ? SSA_IMUL : SSA_MUL; - case BINOP_DIV: - return amal_default_type_is_signed(type) ? SSA_IDIV : SSA_DIV; - case BINOP_DOT: - assert(bool_false && "Binop dot not valid for arithmetic operation and requires special functionality"); - return 0; - case BINOP_EQUALS: - return SSA_EQUALS; - case BINOP_NOT_EQUAL: - return SSA_NOT_EQUAL; - case BINOP_AND: - return SSA_AND; - case BINOP_LESS: - return amal_default_type_is_signed(type) ? SSA_ILT : SSA_LT; - case BINOP_LESS_EQUAL: - return amal_default_type_is_signed(type) ? SSA_ILE : SSA_LE; - case BINOP_GREATER: - return amal_default_type_is_signed(type) ? SSA_IGT : SSA_GT; - case BINOP_GREATER_EQUAL: - return amal_default_type_is_signed(type) ? SSA_IGE : SSA_GE; - } - return 0; -} - -/* Returns the import statement for lhs of binop dot expression, where lhs is a variable name */ -static Import* binop_lhs_get_import_or_null(Binop *self) { - if(self->lhs->type == AST_VARIABLE) { - ScopeNamedObject *resolved_var = &self->lhs->value.variable->resolved_var; - if(resolved_var->type == NAMED_OBJECT_LHS_EXPR && resolved_var->value.lhs_expr->rhs_expr && resolved_var->value.lhs_expr->rhs_expr->type == AST_IMPORT) - return resolved_var->value.lhs_expr->rhs_expr->value.import; - } - return NULL; -} - -static CHECK_RESULT SsaRegister binop_generate_ssa(Binop *self, SsaCompilerContext *context) { - SsaRegister reg; - - /* - Syntax example for binop dot + func_decl expr - const std = @import("std.amal"); - std.printf - */ - if(self->type == BINOP_DOT && self->rhs->type == AST_FUNCTION_CALL) { - Import *lhs_import = binop_lhs_get_import_or_null(self); - if(lhs_import) - context->import_index = 1 + lhs_import->file_scope->import_index; - reg = ast_generate_ssa(self->rhs, context); - context->import_index = 0; - } else { - const SsaRegister lhs_reg = ast_generate_ssa(self->lhs, context); - const SsaRegister rhs_reg = ast_generate_ssa(self->rhs, context); - assert(self->lhs->resolve_data.type.type == RESOLVED_TYPE_LHS_EXPR && "TODO: Implement binop_generate_ssa for function signature"); - throw_if_error(ssa_ins_binop(context->ssa, binop_type_to_ssa_type(self->type, (amal_default_type*)self->lhs->resolve_data.type.value.lhs_expr), lhs_reg, rhs_reg, ®)); - } - return reg; -} - -static void else_if_statement_generate_ssa(ElseIfStatement *else_if_stmt, SsaCompilerContext *context, SsaLabelIndex *skip_other_else_statements_label) { - if(else_if_stmt->condition) { - usize jump_ins_index; - usize jump_skip_else_index; - SsaLabelIndex skip_body_label; - SsaRegister condition_reg = ast_generate_ssa(else_if_stmt->condition, context); - throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0, &jump_ins_index)); - scope_generate_ssa(&else_if_stmt->body, context); - if(else_if_stmt->next_else_if_stmt) - throw_if_error(ssa_ins_jump(context->ssa, 0, &jump_skip_else_index)); - - throw_if_error(ssa_ins_label(context->ssa, &skip_body_label)); - throw_if_error(ssa_set_jump_label(context->ssa, jump_ins_index, skip_body_label)); - if(else_if_stmt->next_else_if_stmt) { - else_if_statement_generate_ssa(else_if_stmt->next_else_if_stmt, context, skip_other_else_statements_label); - /* Skip over all other else if statements, since else_if_statement_generate_ssa is recursive */ - throw_if_error(ssa_set_jump_label(context->ssa, jump_skip_else_index, *skip_other_else_statements_label)); - return; - } - } else { - assert(!else_if_stmt->next_else_if_stmt); - scope_generate_ssa(&else_if_stmt->body, context); - } - - /* Note: The last else if statement doesn't need a jump */ - throw_if_error(ssa_ins_label(context->ssa, skip_other_else_statements_label)); -} - -static void if_statement_generate_ssa(IfStatement *if_stmt, SsaCompilerContext *context) { - usize jump_ins_index; - usize jump_skip_else_index; - SsaLabelIndex skip_body_label; - SsaLabelIndex skip_else_statements_label; - - SsaRegister condition_reg = ast_generate_ssa(if_stmt->condition, context); - throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0, &jump_ins_index)); - scope_generate_ssa(&if_stmt->body, context); - if(if_stmt->else_if_stmt) - throw_if_error(ssa_ins_jump(context->ssa, 0, &jump_skip_else_index)); - - throw_if_error(ssa_ins_label(context->ssa, &skip_body_label)); - throw_if_error(ssa_set_jump_label(context->ssa, jump_ins_index, skip_body_label)); - if(if_stmt->else_if_stmt) { - else_if_statement_generate_ssa(if_stmt->else_if_stmt, context, &skip_else_statements_label); - /* - Skip over all else if statements, since else_if_statement_generate_ssa is recursive. - We want to jump since we want to skip the else if statements if we are inside the first if-statement - */ - throw_if_error(ssa_set_jump_label(context->ssa, jump_skip_else_index, skip_else_statements_label)); - } -} - -static void while_statement_generate_ssa(WhileStatement *while_stmt, SsaCompilerContext *context) { - SsaLabelIndex before_condition_label; - SsaLabelIndex skip_body_label; - usize jump_after_condition_index; - SsaRegister condition_reg; - - throw_if_error(ssa_ins_label(context->ssa, &before_condition_label)); - condition_reg = ast_generate_ssa(while_stmt->condition, context); - throw_if_error(ssa_ins_jumpzero(context->ssa, condition_reg, 0, &jump_after_condition_index)); - scope_generate_ssa(&while_stmt->body, context); - - throw_if_error(ssa_ins_jump(context->ssa, before_condition_label, NULL)); - throw_if_error(ssa_ins_label(context->ssa, &skip_body_label)); - throw_if_error(ssa_set_jump_label(context->ssa, jump_after_condition_index, skip_body_label)); -} - -static void return_expr_generate_ssa(ReturnExpr *self, SsaCompilerContext *context) { - const SsaRegister reg = ast_generate_ssa(self->rhs_expr, context); - throw_if_error(ssa_ins_return(context->ssa, reg)); -} - -static CHECK_RESULT SsaRegister ast_generate_ssa_resolve_data(void *ast_data, AstType ast_type, AstResolveData *resolve_data, SsaCompilerContext *context) { - if(resolve_data->status == AST_SSA_RESOLVED) - return resolve_data->ssa_reg; - - switch(ast_type) { - case AST_NUMBER: - resolve_data->ssa_reg = number_generate_ssa(ast_data, context); - break; - case AST_FUNCTION_DECL: - resolve_data->ssa_reg = funcdecl_generate_ssa(ast_data, context); - break; - case AST_FUNCTION_CALL: - resolve_data->ssa_reg = funccall_generate_ssa(ast_data, context); - break; - case AST_STRUCT_DECL: - resolve_data->ssa_reg = structdecl_generate_ssa(ast_data, context); - break; - case AST_STRUCT_FIELD: - resolve_data->ssa_reg = structfield_generate_ssa(ast_data, context); - break; - case AST_LHS: - resolve_data->ssa_reg = lhsexpr_generate_ssa(ast_data, resolve_data, context); - break; - case AST_ASSIGN: - resolve_data->ssa_reg = assignmentexpr_generate_ssa(ast_data, context); - break; - case AST_IMPORT: - /* TODO: Implement cross file references */ - resolve_data->ssa_reg = 0; - break; - case AST_STRING: - resolve_data->ssa_reg = string_generate_ssa(ast_data, context); - break; - case AST_VARIABLE: - resolve_data->ssa_reg = variable_generate_ssa(ast_data, context); - break; - case AST_BINOP: - resolve_data->ssa_reg = binop_generate_ssa(ast_data, context); - break; - case AST_IF_STATEMENT: - if_statement_generate_ssa(ast_data, context); - break; - case AST_WHILE_STATEMENT: - while_statement_generate_ssa(ast_data, context); - break; - case AST_RETURN: - return_expr_generate_ssa(ast_data, context); - resolve_data->ssa_reg = 0; - break; - } - - resolve_data->status = AST_SSA_RESOLVED; - return resolve_data->ssa_reg; -} - -CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context) { - assert(self); - #ifdef DEBUG - if(self->resolve_data.status != AST_RESOLVED && self->resolve_data.status != AST_SSA_RESOLVED) { - amal_log_error("Ast type not resolved: %d", self->type); - assert(bool_false); - } - #endif - return ast_generate_ssa_resolve_data(self->value.data, self->type, &self->resolve_data, context); -} - -CHECK_RESULT SsaRegister scope_named_object_generate_ssa(ScopeNamedObject *self, SsaCompilerContext *context) { - switch(self->type) { - case NAMED_OBJECT_NONE: - assert(bool_false); - return 0; - case NAMED_OBJECT_LHS_EXPR: - return ast_generate_ssa_resolve_data(self->value.lhs_expr, AST_LHS, self->resolve_data, context); - case NAMED_OBJECT_FUNC_PARAM: - return function_parameter_generate_ssa(self->value.func_param, context); - } - return 0; -} - -void scope_generate_ssa(Scope *self, SsaCompilerContext *context) { - Ast **ast = buffer_begin(&self->ast_objects); - Ast **ast_end = buffer_end(&self->ast_objects); - for(; ast != ast_end; ++ast) { - ignore_result_int(ast_generate_ssa(*ast, context)); - } -} |