aboutsummaryrefslogtreecommitdiff
path: root/src/ssa
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssa')
-rw-r--r--src/ssa/ssa.c982
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, &reg, 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, &reg, 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, &reg, 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, &reg));
- 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, &reg));
- 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, &reg));
- 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, &reg));
-
- 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, &reg));
- 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, &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));
- }
-}