diff options
Diffstat (limited to 'src/gas_assembly_generator.c')
-rw-r--r-- | src/gas_assembly_generator.c | 402 |
1 files changed, 352 insertions, 50 deletions
diff --git a/src/gas_assembly_generator.c b/src/gas_assembly_generator.c index 4b898ed..7641147 100644 --- a/src/gas_assembly_generator.c +++ b/src/gas_assembly_generator.c @@ -22,6 +22,9 @@ #include <stdlib.h> #include <string.h> +size_t available_calling_registers = 6; +char *calling_registers[] = { "rdi", "rsi", "rdx", "rcx", "r8", "r9" }; + static void gas_assembly_generator_binary_operation(gas_assembly_generator_t *gen, ast_binary_operation_t *binary_operation); @@ -29,6 +32,9 @@ static void gas_assembly_generator_compile_function(gas_assembly_generator_t *gen, ast_function_declaration_t *func); static void +gas_assembly_generator_compile_function_call(gas_assembly_generator_t *gen, ast_function_call_t *func_call); + +static void gas_assembly_generator_compile_literal(gas_assembly_generator_t *gen, ast_literal_t *literal); static void @@ -45,6 +51,9 @@ static void gas_assembly_generator_compile_block(gas_assembly_generator_t *gen, ast_block_t *block); static void +gas_assembly_generator_compile_if_stmt(gas_assembly_generator_t *gen, ast_if_stmt_t *if_stmt); + +static void gas_assembly_generator_compile_variable(gas_assembly_generator_t *gen, ast_variable_t *variable); static void @@ -55,6 +64,13 @@ gas_assembly_generator_set_latest_evaluation_as_literal(gas_assembly_generator_t } static void +gas_assembly_generator_set_latest_evaluation_as_literal_boolean(gas_assembly_generator_t *gen, bool value) +{ + gen->latest_evaluation = + (evaluation_result_t){ .kind = EVALUATION_RESULT_AS_LITERAL_BOOL, .data = { .literal_bool = value } }; +} + +static void gas_assembly_generator_set_latest_evaluation_to_rax(gas_assembly_generator_t *gen) { gen->latest_evaluation = (evaluation_result_t){ .kind = EVALUATION_RESULT_ON_RAX }; @@ -67,6 +83,14 @@ gas_assembly_generator_set_latest_evaluation_on_stack(gas_assembly_generator_t * (evaluation_result_t){ .kind = EVALUATION_RESULT_ON_STACK, .data = { .stack_offset = stack_offset } }; } +static void +gas_assembly_generator_compile_condition(gas_assembly_generator_t *gen, + ast_node_t *condition, + uint64_t true_label_index, + bool jump_when_true, + uint64_t false_label_index, + bool jump_when_false); + typedef struct ref_entry_t { ast_identifier_t *id; @@ -87,16 +111,29 @@ ref_entry_new(void) return entry; } -static ref_entry_t * -find_ref_entry(vector_t *refs, ast_identifier_t *identifier) +static int +ref_find_variable_reference_stack_offset(vector_t *refs, ast_identifier_t *identifier) { for (int i = refs->size - 1; i >= 0; --i) { ref_entry_t *entry = (ref_entry_t *)vector_at(refs, i); if (entry->id == identifier) { - return entry; + return entry->stack_offset; } } - return NULL; + assert(false); +} + +static void +ref_set_variable_reference_stack_offset(vector_t *refs, ast_identifier_t *identifier, int stack_offset) +{ + ref_entry_t *ref_entry = ref_entry_new(); + + *ref_entry = (ref_entry_t){ + .id = identifier, + .stack_offset = stack_offset, + }; + + vector_push_back(refs, ref_entry); } void @@ -106,6 +143,8 @@ gas_assembly_generator_init(gas_assembly_generator_t *gen, FILE *stream) gen->stream = stream; gen->refs = vector_new(); gen->stack_offset = 0; + gen->counter_label_index = 1; + gen->return_label_index = 0; } void @@ -114,12 +153,18 @@ gas_assembly_generator_compile(gas_assembly_generator_t *gen, ast_node_t *ast) gen->latest_evaluation.kind = EVALUATION_RESULT_VOID; switch (ast->kind) { + case AST_NAMESPACE: + gas_assembly_generator_compile_ns(gen, &ast->data.ns); + break; case AST_BINARY_OPERATION: gas_assembly_generator_binary_operation(gen, &ast->data.binary_operation); break; case AST_FUNCTION_DECLARATION: gas_assembly_generator_compile_function(gen, &ast->data.function); break; + case AST_FUNCTION_CALL: + gas_assembly_generator_compile_function_call(gen, &ast->data.function_call); + break; case AST_LITERAL: gas_assembly_generator_compile_literal(gen, &ast->data.literal); break; @@ -139,115 +184,237 @@ gas_assembly_generator_compile(gas_assembly_generator_t *gen, ast_node_t *ast) gas_assembly_generator_compile_block(gen, &ast->data.block); break; case AST_IF_STMT: + gas_assembly_generator_compile_if_stmt(gen, &ast->data.if_stmt); + break; + case AST_FUNCTION_PARAMETER: case AST_UNKOWN_NODE: assert(false && "unreachable"); } } +void +gas_assembly_generator_compile_ns(gas_assembly_generator_t *gen, ast_namespace_t *ns) +{ + for (size_t i = 0; i < ns->nodes->size; i++) { + gas_assembly_generator_compile(gen, vector_at(ns->nodes, i)); + } +} + static void gas_assembly_generator_compile_function(gas_assembly_generator_t *gen, ast_function_declaration_t *func) { assert(func); - if (!string_view_eq(func->identifier.name, string_view_from_str("main"))) { - fprintf(stderr, "[ERROR]: no main function has been defined!\n"); - exit(EXIT_FAILURE); - } + uint64_t previous_index = gen->return_label_index; + uint64_t return_label_index = gen->return_label_index = gen->counter_label_index++; - fprintf(gen->stream, ".global _start\n"); - fprintf(gen->stream, ".text\n"); - fprintf(gen->stream, "_start:\n"); + assert((func->prototype.parameters->size <= available_calling_registers) && + "Not enough registers to process this function. Stack parameters will come soon."); + + fprintf(gen->stream, SVFMT ":\n", SVARG(&func->prototype.identifier.name)); fprintf(gen->stream, " push %%rbp\n"); fprintf(gen->stream, " mov %%rsp, %%rbp\n"); + fprintf(gen->stream, " subq $16, %%rsp\n"); + + for (size_t i = 0; i < func->prototype.parameters->size; i++) { + char *reg = calling_registers[i]; + ast_node_t *parameter = vector_at(func->prototype.parameters, i); + + assert(parameter->kind == AST_FUNCTION_PARAMETER); + + gen->stack_offset -= 8; + + fprintf(gen->stream, " mov %%%s, %d(%%rbp)\n", reg, gen->stack_offset); + + ref_set_variable_reference_stack_offset( + gen->refs, ¶meter->data.function_parameter.identifier, gen->stack_offset); + } gas_assembly_generator_compile(gen, func->body); - fprintf(gen->stream, " pop %%rbp\n"); + fprintf(gen->stream, ".L%ld:\n", return_label_index); + fprintf(gen->stream, " leave\n"); + fprintf(gen->stream, " ret\n"); + + gen->return_label_index = previous_index; + gen->stack_offset = 0; } static void -gas_assembly_generator_compile_return_stmt(gas_assembly_generator_t *gen, ast_return_stmt_t *return_stmt) +gas_assembly_generator_compile_function_call(gas_assembly_generator_t *gen, ast_function_call_t *func_call) { - assert(gen && return_stmt); + assert((func_call->arguments->size <= available_calling_registers) && + "Not enough registers to process this function. Stack parameters will come soon."); + + for (size_t i = 0; i < func_call->arguments->size; i++) { + char *reg = calling_registers[i]; + ast_node_t *argument = vector_at(func_call->arguments, i); + + gas_assembly_generator_compile(gen, argument); + + switch (gen->latest_evaluation.kind) { + case EVALUATION_RESULT_AS_LITERAL_INTEGER: + fprintf(gen->stream, " mov $%ld, %%%s\n", gen->latest_evaluation.data.literal_int, reg); + break; + case EVALUATION_RESULT_ON_RAX: + fprintf(gen->stream, " mov %%rax, %%%s\n", reg); + break; + case EVALUATION_RESULT_ON_STACK: + fprintf(gen->stream, " mov %d(%%rbp), %%%s\n", gen->latest_evaluation.data.stack_offset, reg); + break; + case EVALUATION_RESULT_AS_LITERAL_BOOL: + fprintf(gen->stream, " mov %d(%%rbp), %%%s\n", gen->latest_evaluation.data.literal_bool, reg); + break; + case EVALUATION_RESULT_VOID: + assert(false && "Unexpected"); + } + } + fprintf(gen->stream, " call " SVFMT "\n", SVARG(&func_call->identifier->name)); + gas_assembly_generator_set_latest_evaluation_to_rax(gen); +} - gas_assembly_generator_compile(gen, return_stmt->argument); +void +gas_assembly_generator_compile_linux_main(gas_assembly_generator_t *gen, ast_node_t *ns) +{ + if (ast_node_ns_get_function_node_by_name(ns, "main") == NULL) { + fprintf(stderr, "[ERROR]: no main function has been defined!\n"); + exit(EXIT_FAILURE); + } + + fprintf(gen->stream, ".global _start\n"); + fprintf(gen->stream, ".text\n"); + + gas_assembly_generator_compile(gen, ns); - if (gen->latest_evaluation.kind == EVALUATION_RESULT_AS_LITERAL_INTEGER) { - fprintf(gen->stream, " mov $%ld, %%rdi\n", gen->latest_evaluation.data.literal_int); - } else { - fprintf(gen->stream, " mov %%rax, %%rdi\n"); + fprintf(gen->stream, "_start:\n"); + fprintf(gen->stream, " call main\n"); + + switch (gen->latest_evaluation.kind) { + case EVALUATION_RESULT_AS_LITERAL_INTEGER: + fprintf(gen->stream, " mov $%ld, %%rdi\n", gen->latest_evaluation.data.literal_int); + break; + case EVALUATION_RESULT_ON_RAX: + fprintf(gen->stream, " mov %%rax, %%rdi\n"); + break; + case EVALUATION_RESULT_ON_STACK: + fprintf(gen->stream, " mov %d(%%rbp), %%rdi\n", gen->latest_evaluation.data.stack_offset); + break; + case EVALUATION_RESULT_VOID: + fprintf(gen->stream, " xor %%rdi, %%rdi\n"); + break; + case EVALUATION_RESULT_AS_LITERAL_BOOL: + assert(false && "Unexpected"); + break; } + fprintf(gen->stream, " mov $60, %%rax\n"); fprintf(gen->stream, " syscall\n"); } static void -gas_assembly_generator_compile_variable_declaration(gas_assembly_generator_t *gen, - ast_variable_declaration_t *variable_declaration) +gas_assembly_generator_compile_return_stmt(gas_assembly_generator_t *gen, ast_return_stmt_t *return_stmt) { - gen->stack_offset -= 8; - gas_assembly_generator_compile(gen, variable_declaration->value); + assert(gen && return_stmt); + assert(gen->return_label_index != 0 && "There are no return label index set"); - ref_entry_t *entry = ref_entry_new(); - *entry = (ref_entry_t){ .id = &variable_declaration->identifier, .stack_offset = gen->stack_offset }; - vector_push_back(gen->refs, entry); + gas_assembly_generator_compile(gen, return_stmt->argument); switch (gen->latest_evaluation.kind) { case EVALUATION_RESULT_AS_LITERAL_INTEGER: - fprintf(gen->stream, " movq $%ld, %d(%%rbp)\n", gen->latest_evaluation.data.literal_int, entry->stack_offset); + fprintf(gen->stream, " mov $%ld, %%rax\n", gen->latest_evaluation.data.literal_int); + gas_assembly_generator_set_latest_evaluation_to_rax(gen); break; case EVALUATION_RESULT_ON_RAX: - fprintf(gen->stream, " mov %%rax, %d(%%rbp)\n", entry->stack_offset); + gas_assembly_generator_set_latest_evaluation_to_rax(gen); break; case EVALUATION_RESULT_ON_STACK: - fprintf(gen->stream, " movq %d(%%rbp), %%rax\n", gen->latest_evaluation.data.stack_offset); - fprintf(gen->stream, " movq %%rax, %d(%%rbp)\n", entry->stack_offset); + fprintf(gen->stream, " mov %d(%%rbp), %%rax\n", gen->latest_evaluation.data.stack_offset); + gas_assembly_generator_set_latest_evaluation_to_rax(gen); break; case EVALUATION_RESULT_VOID: - assert(false && "Unexpected void result for variable declaration"); - // FIXME: store the latest node at latest evaluation and print_ast + break; + case EVALUATION_RESULT_AS_LITERAL_BOOL: + assert(false && "Unexpected"); + break; } + + fprintf(gen->stream, " jmp .L%ld\n", gen->return_label_index); } static void -gas_assembly_generator_compile_variable_assignment(gas_assembly_generator_t *gen, - ast_variable_assignment_t *variable_assignment) +gas_assembly_generator_compile_variable_assign_value(gas_assembly_generator_t *gen, + ast_node_t *expression, + int stack_offset) { - ref_entry_t *entry = find_ref_entry(gen->refs, variable_assignment->identifier); - assert(entry && "reference not found"); + if (expression->result_type == TYPE_BOOL) { + if (expression->kind == AST_LITERAL) { + assert(expression->data.literal.kind == AST_LITERAL_BOOL); - gas_assembly_generator_compile(gen, variable_assignment->expression); + fprintf(gen->stream, " movq $%d, %d(%%rbp)\n", expression->data.literal.value.boolean, stack_offset); + return; + } + + uint64_t true_label_index = gen->counter_label_index++; + uint64_t false_label_index = gen->counter_label_index++; + uint64_t after_assign_label_index = gen->counter_label_index++; + + gas_assembly_generator_compile_condition(gen, expression, true_label_index, false, false_label_index, true); + + fprintf(gen->stream, ".L_%ld:\n", true_label_index); + fprintf(gen->stream, " movq $1, %d(%%rbp)\n", stack_offset); + fprintf(gen->stream, " jmp .L_%ld\n", after_assign_label_index); + + fprintf(gen->stream, ".L_%ld:\n", false_label_index); + fprintf(gen->stream, " movq $0, %d(%%rbp)\n", stack_offset); + + fprintf(gen->stream, ".L_%ld:\n", after_assign_label_index); + return; + } + + gas_assembly_generator_compile(gen, expression); switch (gen->latest_evaluation.kind) { case EVALUATION_RESULT_AS_LITERAL_INTEGER: - fprintf(gen->stream, " movq $%ld, %d(%%rbp)\n", gen->latest_evaluation.data.literal_int, entry->stack_offset); + fprintf(gen->stream, " movq $%ld, %d(%%rbp)\n", gen->latest_evaluation.data.literal_int, stack_offset); break; case EVALUATION_RESULT_ON_RAX: - fprintf(gen->stream, " mov %%rax, %d(%%rbp)\n", entry->stack_offset); + fprintf(gen->stream, " mov %%rax, %d(%%rbp)\n", stack_offset); break; case EVALUATION_RESULT_ON_STACK: fprintf(gen->stream, " movq %d(%%rbp), %%rax\n", gen->latest_evaluation.data.stack_offset); - fprintf(gen->stream, " movq %%rax, %d(%%rbp)\n", entry->stack_offset); + fprintf(gen->stream, " movq %%rax, %d(%%rbp)\n", stack_offset); break; + case EVALUATION_RESULT_AS_LITERAL_BOOL: case EVALUATION_RESULT_VOID: assert(false && "Unexpected void result for variable declaration"); // FIXME: store the latest node at latest evaluation and print_ast } - // Today we dont't support chain-assignment like this: - // - // a = b = 1; - // - // If we start supporting it, we need to change the latest evaluation kind to - // stack. - gen->latest_evaluation.kind = EVALUATION_RESULT_VOID; +} + +static void +gas_assembly_generator_compile_variable_declaration(gas_assembly_generator_t *gen, + ast_variable_declaration_t *variable_declaration) +{ + gen->stack_offset -= 8; + ref_set_variable_reference_stack_offset(gen->refs, &variable_declaration->identifier, gen->stack_offset); + gas_assembly_generator_compile_variable_assign_value(gen, variable_declaration->value, gen->stack_offset); +} + +static void +gas_assembly_generator_compile_variable_assignment(gas_assembly_generator_t *gen, + ast_variable_assignment_t *variable_assignment) +{ + int stack_offset = ref_find_variable_reference_stack_offset(gen->refs, variable_assignment->identifier); + + gas_assembly_generator_compile_variable_assign_value(gen, variable_assignment->expression, stack_offset); } static void gas_assembly_generator_compile_variable(gas_assembly_generator_t *gen, ast_variable_t *variable) { - ref_entry_t *entry = find_ref_entry(gen->refs, variable->identifier); - assert(entry && "reference not found"); - gas_assembly_generator_set_latest_evaluation_on_stack(gen, entry->stack_offset); + int stack_offset = ref_find_variable_reference_stack_offset(gen->refs, variable->identifier); + + gas_assembly_generator_set_latest_evaluation_on_stack(gen, stack_offset); } static void @@ -257,6 +424,9 @@ gas_assembly_generator_compile_literal(gas_assembly_generator_t *gen, ast_litera case AST_LITERAL_INTEGER: gas_assembly_generator_set_latest_evaluation_as_literal(gen, literal->value.integer); return; + case AST_LITERAL_BOOL: + gas_assembly_generator_set_latest_evaluation_as_literal_boolean(gen, literal->value.boolean); + return; default: assert(false && "no code generation strategy."); } @@ -295,6 +465,9 @@ gas_assembly_generator_binary_operation(gas_assembly_generator_t *gen, ast_binar case EVALUATION_RESULT_ON_STACK: fprintf(gen->stream, " movq %d(%%rbp), %%rcx\n", right_evaluation.data.stack_offset); break; + case EVALUATION_RESULT_AS_LITERAL_BOOL: + fprintf(gen->stream, " mov $%d, %%rcx\n", right_evaluation.data.literal_bool); + break; case EVALUATION_RESULT_VOID: assert(false && "Unexpected void result for binary operation"); // FIXME: store the latest node at latest evaluation and print_ast @@ -311,6 +484,9 @@ gas_assembly_generator_binary_operation(gas_assembly_generator_t *gen, ast_binar break; case EVALUATION_RESULT_ON_RAX: break; + case EVALUATION_RESULT_AS_LITERAL_BOOL: + fprintf(gen->stream, " mov $%d, %%rax\n", left_evaluation.data.literal_bool); + break; case EVALUATION_RESULT_VOID: assert(false && "Unexpected void result for binary operation"); } @@ -336,5 +512,131 @@ gas_assembly_generator_binary_operation(gas_assembly_generator_t *gen, ast_binar return; } + if (binary_operation->kind == AST_BINOP_LT || binary_operation->kind == AST_BINOP_GT || + binary_operation->kind == AST_BINOP_EQUAL || binary_operation->kind == AST_BINOP_NOT_EQUAL || + binary_operation->kind == AST_BINOP_GT || binary_operation->kind == AST_BINOP_LT || + binary_operation->kind == AST_BINOP_LT_EQUAL || binary_operation->kind == AST_BINOP_GT_EQUAL) { + fprintf(gen->stream, " cmpq %%rcx, %%rax\n"); + return; + } + assert(false && "No strategy defined for binary operation"); } + +static char *jump_map[AST_BINOP_N] = { + [AST_BINOP_EQUAL] = "je", [AST_BINOP_NOT_EQUAL] = "jne", [AST_BINOP_LT] = "jl", + [AST_BINOP_LT_EQUAL] = "jle", [AST_BINOP_GT] = "jg", [AST_BINOP_GT_EQUAL] = "jge", +}; + +static char *inverse_jump_map[AST_BINOP_N] = { + [AST_BINOP_EQUAL] = "jne", [AST_BINOP_NOT_EQUAL] = "je", [AST_BINOP_LT] = "jge", + [AST_BINOP_LT_EQUAL] = "jg", [AST_BINOP_GT] = "jle", [AST_BINOP_GT_EQUAL] = "jl", +}; + +static void +gas_assembly_generator_compile_condition(gas_assembly_generator_t *gen, + ast_node_t *condition, + uint64_t true_label_index, + bool jump_when_true, + uint64_t false_label_index, + bool jump_when_false) +{ + assert(condition->kind == AST_BINARY_OPERATION || condition->kind == AST_LITERAL); + + if (condition->kind == AST_LITERAL) { + assert(condition->data.literal.kind == AST_LITERAL_BOOL); + + if (jump_when_false && !condition->data.literal.value.boolean) { + fprintf(gen->stream, " jmp .L_%ld\n", false_label_index); + return; + } + + if (jump_when_true && condition->data.literal.value.boolean) { + fprintf(gen->stream, " jmp .L_%ld\n", true_label_index); + } + + return; + } + + if (condition->data.binary_operation.kind == AST_BINOP_OR) { + uint64_t or_false_branch_label_index = gen->counter_label_index++; + + gas_assembly_generator_compile_condition( + gen, condition->data.binary_operation.left, true_label_index, true, or_false_branch_label_index, false); + + fprintf(gen->stream, ".L_%ld:\n", or_false_branch_label_index); + + gas_assembly_generator_compile_condition( + gen, condition->data.binary_operation.right, true_label_index, false, false_label_index, true); + return; + } + + if (condition->data.binary_operation.kind == AST_BINOP_AND) { + uint64_t and_true_branch_label_index = gen->counter_label_index++; + + gas_assembly_generator_compile_condition( + gen, condition->data.binary_operation.left, and_true_branch_label_index, false, false_label_index, true); + + fprintf(gen->stream, ".L_%ld:\n", and_true_branch_label_index); + + gas_assembly_generator_compile_condition( + gen, condition->data.binary_operation.right, true_label_index, false, false_label_index, true); + return; + } + + gas_assembly_generator_compile(gen, condition); + + if (jump_when_false) { + char *jumper = inverse_jump_map[condition->data.binary_operation.kind]; + + assert(jumper != NULL); + + fprintf(gen->stream, " %s .L_%ld\n", jumper, false_label_index); + return; + } + + if (jump_when_true) { + char *jumper = jump_map[condition->data.binary_operation.kind]; + + assert(jumper != NULL); + + fprintf(gen->stream, " %s .L_%ld\n", jumper, true_label_index); + } +} + +static void +gas_assembly_generator_compile_if_stmt(gas_assembly_generator_t *gen, ast_if_stmt_t *if_stmt) +{ + switch (if_stmt->condition->kind) { + case AST_BINARY_OPERATION: + case AST_LITERAL: { + uint64_t true_label_index = gen->counter_label_index++; + uint64_t false_label_index = gen->counter_label_index++; + + gas_assembly_generator_compile_condition( + gen, if_stmt->condition, true_label_index, false, false_label_index, true); + + fprintf(gen->stream, ".L_%ld:\n", true_label_index); + gas_assembly_generator_compile(gen, if_stmt->body); + + fprintf(gen->stream, ".L_%ld:\n", false_label_index); + break; + } + case AST_VARIABLE: { + uint64_t false_label_index = gen->counter_label_index++; + int stack_offset = + ref_find_variable_reference_stack_offset(gen->refs, if_stmt->condition->data.variable.identifier); + + fprintf(gen->stream, " movq %d(%%rbp), %%rax\n", stack_offset); + fprintf(gen->stream, " cmpq $1, %%rax\n"); + fprintf(gen->stream, " jne .L_%ld\n", false_label_index); + + gas_assembly_generator_compile(gen, if_stmt->body); + + fprintf(gen->stream, ".L_%ld:\n", false_label_index); + break; + } + default: + assert(false); + } +} |