summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarson Fleming <cflems@cflems.net>2026-03-26 19:46:35 -0700
committerCarson Fleming <cflems@cflems.net>2026-03-26 19:46:35 -0700
commit28157efe6ef65394d8930a79200b9243ee919f47 (patch)
tree5dd492ae8d27e99d066b88e76f5304fad270ee11
parent2e4f713ede25fb6147571858779fde542144c76f (diff)
downloadccc-28157efe6ef65394d8930a79200b9243ee919f47.tar.gz
mostly there except need to implement one more hash map
-rw-r--r--.gitignore1
-rw-r--r--codegen.c139
-rw-r--r--codegen.h8
-rw-r--r--main.c6
-rw-r--r--scope.c42
-rw-r--r--scope.h46
-rw-r--r--test/simple.c2
7 files changed, 225 insertions, 19 deletions
diff --git a/.gitignore b/.gitignore
index 85a7886..e19039e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
ccc
+*.s
*.o
*.out
build/**
diff --git a/codegen.c b/codegen.c
index 37c7a91..cfb4cca 100644
--- a/codegen.c
+++ b/codegen.c
@@ -1,42 +1,95 @@
#include "ccc.h"
-#include "ast.h"
+#include "codegen.h"
+#include "scope.h"
#include <stdlib.h>
#include <stdio.h>
-static void emit_stmt(FILE* outfile, const struct stmt_node* node);
-static void emit_expr(
+#define CGEN_PANIC(format, ...) {\
+ fprintf(\
+ stderr,\
+ "ccc: code gen error: " format "\n" __VA_OPT__(,)\
+ __VA_ARGS__);\
+ exit(1);\
+}
+
+static const struct storage_location RV_LOC = {
+ .type = REGISTER,
+ .label = "rax",
+};
+
+static struct scope* scope;
+
+static void emit_storage_loc(
FILE* outfile,
- const struct expr_node* node,
- const char* reg);
+ const struct storage_location* loc
+) {
+ switch (loc->type) {
+ case REGISTER:
+ case JMP_LABEL:
+ fprintf(outfile, "%s", loc->label);
+ break;
+ case BP_OFFSET:
+ if (loc->offset < 0)
+ fprintf(outfile, "[rbp + %lld]", -loc->offset);
+ else if (loc->offset > 0)
+ fprintf(outfile, "[rbp - %lld]", loc->offset);
+ else
+ fprintf(outfile, "[rbp]");
+ }
+}
static void emit_int_lit(
FILE* outfile,
const struct int_lit_node* node,
- const char* reg
+ const struct storage_location* storage
) {
- fprintf(outfile, "\tmov %s, %lld\n", reg, node->val);
+ if (storage != NULL) {
+ fprintf(outfile, "\tmov ");
+ emit_storage_loc(outfile, storage);
+ fprintf(outfile, ", %lld\n", node->val);
+ }
}
static void emit_expr(
FILE* outfile,
const struct expr_node* node,
- const char* reg
+ const struct storage_location* storage
) {
switch (node->type) {
case EXPR_EMPTY:
break;
case EXPR_INT_LIT:
- emit_int_lit(outfile, &node->as._int_lit, reg);
+ emit_int_lit(outfile, &node->as._int_lit, storage);
break;
}
}
+static void emit_stmt(FILE* outfile, const struct stmt_node* node);
+
+static struct type_def get_type_def(const char* type_name) {
+ struct type_def type_def;
+ if (!scope_get_type(scope, &type_def, type_name))
+ CGEN_PANIC("size of type %s is not known", type_name);
+ return type_def;
+}
+
static void emit_var_decl(FILE* outfile, const struct var_decl_node* node) {
- /* TODO: make do smth */
+ struct type_def type_def = get_type_def(node->type.type);
+ fprintf(outfile, "\tsub rsp, %llu\n", type_def.size);
+ scope->bp_offset += type_def.size;
+ scope_define_var(scope, (struct var_def) {
+ .name = node->ident,
+ .loc = {
+ .type = BP_OFFSET,
+ .offset = scope->bp_offset,
+ },
+ });
}
static void emit_return(FILE* outfile, const struct return_node* node) {
- if (node->ret_val != NULL) emit_expr(outfile, node->ret_val, "rax");
+ if (node->ret_val != NULL) emit_expr(outfile, node->ret_val, &RV_LOC);
+ fprintf(outfile, "\tmov rsp, rbp\n");
+ fprintf(outfile, "\tpop rbp\n");
fprintf(outfile, "\tret\n");
}
@@ -48,6 +101,25 @@ static void emit_group(FILE* outfile, const struct group_node* node) {
}
}
+static void emit_stmt_group(FILE* outfile, const struct group_node* node) {
+ scope_push(&scope);
+ scope->bp_offset = scope->next_out->bp_offset; /* don't reset bp */
+
+ emit_group(outfile, node);
+
+ scope_pop(&scope);
+ if (scope->bp_offset == 0)
+ 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,
+ });
+ fprintf(outfile, "\n");
+ }
+}
+
static void emit_stmt(FILE* outfile, const struct stmt_node* node) {
switch (node->type) {
case STMT_VAR_DECL:
@@ -60,15 +132,41 @@ static void emit_stmt(FILE* outfile, const struct stmt_node* node) {
emit_expr(outfile, &node->as._expr, NULL);
break;
case STMT_GROUP:
- emit_group(outfile, &node->as._group);
+ emit_stmt_group(outfile, &node->as._group);
break;
}
}
static void emit_fn_decl(FILE* outfile, const struct fn_decl_node* node) {
fprintf(outfile, "%s:\n", node->name);
- /* TODO: probably something to map the args to temporaries lol */
+ fprintf(outfile, "\tpush rbp\n");
+ fprintf(outfile, "\tmov rbp, rsp\n");
+
+ scope_push(&scope);
+ scope->bp_offset = 0;
+
+ struct var_decl_node* args_node = node->args_head;
+ long long arg_bp_offset = -8;
+ while (args_node != NULL) {
+ struct type_def type_def = get_type_def(args_node->type.type);
+ scope_define_var(scope, (struct var_def) {
+ .name = args_node->ident,
+ .loc = {
+ .type = BP_OFFSET,
+ .offset = arg_bp_offset,
+ },
+ });
+ arg_bp_offset -= type_def.size;
+ args_node = args_node->next;
+ }
+
emit_group(outfile, &node->body);
+
+ scope_pop(&scope);
+
+ fprintf(outfile, "\tmov rsp, rbp\n");
+ fprintf(outfile, "\tpop rbp\n");
+ fprintf(outfile, "\tret\n");
}
static void emit_root_node(FILE* outfile, const struct root_node* node) {
@@ -84,12 +182,22 @@ void emit_code(const struct root_node* ast, const char* path) {
if (outfile == NULL) CCC_PANIC;
fprintf(outfile, "section .text\n");
+ scope_push(&scope);
/* output all non-static function declarations as globals */
const struct root_node* node = ast;
while (node != NULL) {
- if (node->type == ROOT_FN_DECL)
- fprintf(outfile, "global %s\n", node->as._fn_decl.name);
+ if (node->type == ROOT_FN_DECL) {
+ const char* fn_name = node->as._fn_decl.name;
+ scope_define_var(scope, (struct var_def) {
+ .name = fn_name,
+ .loc = {
+ .type = JMP_LABEL,
+ .label = fn_name,
+ },
+ });
+ fprintf(outfile, "global %s\n", fn_name);
+ }
node = node->next;
}
@@ -104,5 +212,6 @@ void emit_code(const struct root_node* ast, const char* path) {
node = node->next;
}
+ scope_pop(&scope);
fclose(outfile);
}
diff --git a/codegen.h b/codegen.h
new file mode 100644
index 0000000..50bb105
--- /dev/null
+++ b/codegen.h
@@ -0,0 +1,8 @@
+#ifndef CODEGEN_H
+#define CODEGEN_H
+
+#include "ast.h"
+
+void emit_code(const struct root_node* ast, const char* path);
+
+#endif
diff --git a/main.c b/main.c
index c86efdd..e2aca11 100644
--- a/main.c
+++ b/main.c
@@ -1,5 +1,6 @@
#include "lexer.h"
#include "parser.h"
+#include "codegen.h"
#include <stdlib.h>
#include <stdio.h>
@@ -35,8 +36,6 @@ void test_lexer(int argc, char** argv) {
}
}
-void gdb_break_here() {}
-
void test_parser(int argc, char** argv) {
struct root_node* root;
struct root_node** p_cur = &root;
@@ -44,7 +43,8 @@ void test_parser(int argc, char** argv) {
*p_cur = parse(argv[i]);
p_cur = &((*p_cur)->next);
}
- gdb_break_here();
+
+ emit_code(root, "test/simple.s");
ast_destroy(root);
}
diff --git a/scope.c b/scope.c
new file mode 100644
index 0000000..076f1a6
--- /dev/null
+++ b/scope.c
@@ -0,0 +1,42 @@
+#include "scope.h"
+#include <stdlib.h>
+
+static void scope_init(struct scope* scope) {}
+
+static void scope_destroy(struct scope* scope) {}
+
+unsigned long long hash_name(const char* name) {
+ unsigned long long hash = 0, i = 0;
+ while (name[i] != 0) hash = (hash << 5) - hash + name[i++];
+ return hash;
+}
+
+void scope_push(struct scope** p_scope) {
+ struct scope* inner_scope = calloc(1, sizeof(struct scope));
+ scope_init(inner_scope);
+ inner_scope->next_out = *p_scope;
+ *p_scope = inner_scope;
+}
+
+void scope_pop(struct scope** p_scope) {
+ struct scope* discarded_scope = *p_scope;
+ *p_scope = (*p_scope)->next_out;
+ scope_destroy(discarded_scope);
+ free(discarded_scope);
+}
+
+bool scope_get_type(
+ const struct scope* scope,
+ struct type_def* p_entry,
+ const char* name
+) {}
+
+void scope_define_type(struct scope* scope, struct type_def type) {}
+
+bool scope_get_var(
+ const struct scope* scope,
+ struct var_def* p_entry,
+ const char* name
+) {}
+
+void scope_define_var(struct scope* scope, struct var_def var) {}
diff --git a/scope.h b/scope.h
new file mode 100644
index 0000000..b469476
--- /dev/null
+++ b/scope.h
@@ -0,0 +1,46 @@
+#ifndef VARS_H
+#define VARS_H
+
+struct storage_location {
+ enum {
+ REGISTER,
+ JMP_LABEL,
+ BP_OFFSET,
+ } type;
+ union {
+ long long offset;
+ const char* label;
+ };
+};
+
+struct type_def {
+ const char* name;
+ unsigned long long size;
+};
+
+struct var_def {
+ const char* name;
+ struct storage_location loc;
+};
+
+struct scope {
+ struct type_def* types;
+ struct var_def* vars;
+ struct scope* next_out;
+ unsigned long long bp_offset;
+};
+
+void scope_push(struct scope** p_scope);
+void scope_pop(struct scope** p_scope);
+bool scope_get_type(
+ const struct scope* scope,
+ struct type_def* p_entry,
+ const char* name);
+void scope_define_type(struct scope* scope, struct type_def type);
+bool scope_get_var(
+ const struct scope* scope,
+ struct var_def* p_entry,
+ const char* name);
+void scope_define_var(struct scope* scope, struct var_def var);
+
+#endif \ No newline at end of file
diff --git a/test/simple.c b/test/simple.c
index 33c14ce..99d86c4 100644
--- a/test/simple.c
+++ b/test/simple.c
@@ -1,3 +1,3 @@
int main() {
- return 0;
+ return 16;
}