diff options
| author | Carson Fleming <cflems@cflems.net> | 2026-03-28 07:21:26 -1000 |
|---|---|---|
| committer | Carson Fleming <cflems@cflems.net> | 2026-03-28 07:21:26 -1000 |
| commit | ee85f90edd17d9c3fadc0d118021c18a5bb8463c (patch) | |
| tree | 8dadc80019a1af86c6a133cf7195a2cb42115695 | |
| parent | 617732aac7394fb335baf957dc8cebac1fea756d (diff) | |
| download | ccc-ee85f90edd17d9c3fadc0d118021c18a5bb8463c.tar.gz | |
impl x86_64 calling convention
| -rw-r--r-- | codegen.c | 58 | ||||
| -rw-r--r-- | register.c | 48 | ||||
| -rw-r--r-- | register.h | 22 | ||||
| -rw-r--r-- | scope.h | 3 | ||||
| -rw-r--r-- | test/manyargs.c | 7 |
5 files changed, 127 insertions, 11 deletions
@@ -1,6 +1,7 @@ #include "ccc.h" #include "codegen.h" #include "scope.h" +#include "register.h" #include <stdlib.h> #include <stdio.h> @@ -14,17 +15,25 @@ static const struct storage_location RV_LOC = { .type = REGISTER, - .label = "rax", + .reg = &RAX, }; +#define FULL_REG_SZ 8 static struct scope* scope; static void emit_storage_loc( FILE* outfile, - const struct storage_location* loc + const struct storage_location* loc, + unsigned long long sz ) { switch (loc->type) { case REGISTER: + if (sz > 4) fprintf(outfile, "%s", loc->reg->qword); + else if (sz > 2) fprintf(outfile, "%s", loc->reg->dword); + // TODO: for word and byte make sure moving into these zeroes the high register + else if (sz > 1) fprintf(outfile, "%s", loc->reg->word); + else fprintf(outfile, "%s", loc->reg->byte); + break; case JMP_LABEL: fprintf(outfile, "%s", loc->label); break; @@ -45,7 +54,7 @@ static void emit_int_lit( ) { if (storage != NULL) { fprintf(outfile, "\tmov "); - emit_storage_loc(outfile, storage); + emit_storage_loc(outfile, storage, FULL_REG_SZ); fprintf(outfile, ", %lld\n", node->val); } } @@ -61,9 +70,9 @@ static void emit_var_ref( CGEN_PANIC("reference to undefined variable %s", node->ident); fprintf(outfile, "\tmov "); - emit_storage_loc(outfile, storage); + emit_storage_loc(outfile, storage, var_def.sz); fprintf(outfile, ", "); - emit_storage_loc(outfile, &var_def.loc); + emit_storage_loc(outfile, &var_def.loc, var_def.sz); fprintf(outfile, "\n"); } } @@ -108,6 +117,7 @@ static void emit_var_decl(FILE* outfile, const struct var_decl_node* node) { .type = BP_OFFSET, .offset = scope->bp_offset, }, + .sz = type_sz, }); } @@ -137,10 +147,13 @@ static void emit_stmt_group(FILE* outfile, const struct group_node* node) { fprintf(outfile, "\tmov rsp, rbp\n"); else { fprintf(outfile, "\tlea rsp, "); - emit_storage_loc(outfile, &(struct storage_location) { - .type = BP_OFFSET, - .offset = scope->bp_offset, - }); + emit_storage_loc( + outfile, + &(struct storage_location) { + .type = BP_OFFSET, + .offset = scope->bp_offset, + }, + FULL_REG_SZ); fprintf(outfile, "\n"); } } @@ -170,18 +183,41 @@ static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) { scope_push(&scope); scope->bp_offset = 0; + long long spilled_bp_ofs = -16; // return address + old bp + unsigned long long arg_regnum = 0; struct var_decl_node* args_node = node->args_head; while (args_node != NULL) { unsigned long long type_sz = get_type_size(&args_node->type); scope->bp_offset += type_sz; - scope_define_var(scope, (struct var_def) { + struct var_def var_def = { .name = args_node->ident, .loc = { .type = BP_OFFSET, .offset = scope->bp_offset, }, - }); + .sz = type_sz, + }; + scope_define_var(scope, var_def); + + struct storage_location arg_src; + if (arg_regnum < CC_N_REGS) { + arg_src = (struct storage_location) { + .type = REGISTER, + .reg = CALLING_CONV[arg_regnum++] + }; + } else { + arg_src = (struct storage_location) { + .type = BP_OFFSET, + .offset = spilled_bp_ofs, + }; + spilled_bp_ofs -= type_sz; + } + fprintf(outfile, "\tmov "); + emit_storage_loc(outfile, &var_def.loc, type_sz); + fprintf(outfile, ", "); + emit_storage_loc(outfile, &arg_src,type_sz); + fprintf(outfile, "\n"); args_node = args_node->next; } diff --git a/register.c b/register.c new file mode 100644 index 0000000..ca095ea --- /dev/null +++ b/register.c @@ -0,0 +1,48 @@ +#include "register.h" + +const struct reg RAX = { + .qword = "rax", + .dword = "eax", + .word = "ax", + .byte = "al", +}; +const struct reg RDI = { + .qword = "rdi", + .dword = "edi", + .word = "di", + .byte = "dil", +}; +const struct reg RSI = { + .qword = "rsi", + .dword = "esi", + .word = "si", + .byte = "sil", +}; +const struct reg RDX = { + .qword = "rdx", + .dword = "edx", + .word = "dx", + .byte = "dl", +}; +const struct reg R10 = { + .qword = "r10", + .dword = "r10d", + .word = "r10w", + .byte = "r10b", +}; +const struct reg R9 = { + .qword = "r9", + .dword = "r9d", + .word = "r9w", + .byte = "r9b", +}; +const struct reg R8 = { + .qword = "r8", + .dword = "r8d", + .word = "r8w", + .byte = "r8b", +}; + +const struct reg* const CALLING_CONV[] = {&RDI, &RSI, &RDX, &R10, &R9, &R8}; +const unsigned long long CC_N_REGS = + sizeof(CALLING_CONV) / sizeof(const struct reg* const); diff --git a/register.h b/register.h new file mode 100644 index 0000000..323b668 --- /dev/null +++ b/register.h @@ -0,0 +1,22 @@ +#ifndef REGISTER_H +#define REGISTER_H + +struct reg { + const char* qword; + const char* dword; + const char* word; + const char* byte; +}; + +extern const struct reg RAX; +extern const struct reg RDI; +extern const struct reg RSI; +extern const struct reg RDX; +extern const struct reg R10; +extern const struct reg R9; +extern const struct reg R8; + +extern const struct reg* const CALLING_CONV[]; +extern const unsigned long long CC_N_REGS; + +#endif @@ -10,7 +10,9 @@ struct storage_location { union { long long offset; const char* label; + const struct reg* reg; }; + unsigned long long sz; }; struct type_def { @@ -21,6 +23,7 @@ struct type_def { struct var_def { const char* name; struct storage_location loc; + unsigned long long sz; }; struct scope { diff --git a/test/manyargs.c b/test/manyargs.c new file mode 100644 index 0000000..28fbda4 --- /dev/null +++ b/test/manyargs.c @@ -0,0 +1,7 @@ +int get(int a, int b, int c, int d, int e, int f, int g) { + return g; +} + +int main(int argc, char** argv) { + return get(argc, argc, argc, argc, argc, argc, argc); +} |
