summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarson Fleming <cflems@cflems.net>2026-03-28 07:21:26 -1000
committerCarson Fleming <cflems@cflems.net>2026-03-28 07:21:26 -1000
commitee85f90edd17d9c3fadc0d118021c18a5bb8463c (patch)
tree8dadc80019a1af86c6a133cf7195a2cb42115695
parent617732aac7394fb335baf957dc8cebac1fea756d (diff)
downloadccc-ee85f90edd17d9c3fadc0d118021c18a5bb8463c.tar.gz
impl x86_64 calling convention
-rw-r--r--codegen.c58
-rw-r--r--register.c48
-rw-r--r--register.h22
-rw-r--r--scope.h3
-rw-r--r--test/manyargs.c7
5 files changed, 127 insertions, 11 deletions
diff --git a/codegen.c b/codegen.c
index 9d7f183..4e9f63a 100644
--- a/codegen.c
+++ b/codegen.c
@@ -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
diff --git a/scope.h b/scope.h
index 36b6a93..e09e14e 100644
--- a/scope.h
+++ b/scope.h
@@ -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);
+}