diff options
author | Carlos Maniero <carlos@maniero.me> | 2023-05-10 22:24:14 -0300 |
---|---|---|
committer | Carlos Maniero <carlos@maniero.me> | 2023-05-10 22:45:31 -0300 |
commit | 5042a4ffc1363d6f0f99a3afd79f76cf2da738d6 (patch) | |
tree | 90c31d77ddf6b9051669fafdc6dfe0fc3b1f35eb /src/gas_assembly_generator.c | |
parent | 6f187a71cbe3aa4ebb32ba287c75562d96c7a3f4 (diff) |
gas: implement function calls
For now function calls are following the C's calling convention, which
means they are using the following registers to pass functions'
arguments:
rdi, rsi, rdx, rcx, r8, r9
If a function has more then 6 parameters, the compilation will fail.
To enable function with more than 6 parameters we will need to save the
extra arguments on stack.
Naming:
parameters: function parameters are the variables a function receives.
arguments: Arguments are the values passed to a function when calling
it.
Calling mechanism:
When a function is called, all the expressions passed as argument are
evaluated, after the evaluation, the result is stored on the register
that represents its argument position, the first argument will be
stored on rdi, the second on rsi and so on.
Receiving mechanism:
When a function starts, the first thing it does is store all the
registers onto the stack. So rdi will be stored on -8(rbp), rsi on
-16(rbp) and so on. And, a ref_entry is created making the
relationship parameter-stack_offset.
Signed-off-by: Carlos Maniero <carlos@maniero.me>
Diffstat (limited to 'src/gas_assembly_generator.c')
-rw-r--r-- | src/gas_assembly_generator.c | 50 |
1 files changed, 49 insertions, 1 deletions
diff --git a/src/gas_assembly_generator.c b/src/gas_assembly_generator.c index 2d6b993..26bf3f9 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); @@ -183,6 +186,7 @@ gas_assembly_generator_compile(gas_assembly_generator_t *gen, ast_node_t *ast) 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"); } @@ -204,10 +208,27 @@ gas_assembly_generator_compile_function(gas_assembly_generator_t *gen, ast_funct uint64_t previous_index = gen->return_label_index; uint64_t return_label_index = gen->return_label_index = gen->counter_label_index++; - fprintf(gen->stream, SVFMT ":\n", SVARG(&func->identifier.name)); + 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"); + 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, ".L%ld:\n", return_label_index); @@ -215,11 +236,38 @@ gas_assembly_generator_compile_function(gas_assembly_generator_t *gen, ast_funct fprintf(gen->stream, " ret\n"); gen->return_label_index = previous_index; + gen->stack_offset = 0; } static void gas_assembly_generator_compile_function_call(gas_assembly_generator_t *gen, ast_function_call_t *func_call) { + 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); } |