aboutsummaryrefslogtreecommitdiff
path: root/bootstrap/cgen.c
diff options
context:
space:
mode:
Diffstat (limited to 'bootstrap/cgen.c')
-rw-r--r--bootstrap/cgen.c417
1 files changed, 417 insertions, 0 deletions
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c
new file mode 100644
index 0000000..6445bf8
--- /dev/null
+++ b/bootstrap/cgen.c
@@ -0,0 +1,417 @@
+#include "all.h"
+
+static FILE *outfp;
+
+static void pri(const char *fmt, ...);
+
+static void
+gentype(const struct type *ty) {
+ assert(ty);
+ switch (ty->t) {
+ case TYvoid:
+ pri("void");
+ break;
+ case TYbool:
+ pri("bool");
+ break;
+ case TYint:
+ pri("%sint%z_t", ty->int_signed ? "" : "u",
+ 8 * ty->size);
+ break;
+ case TYfloat:
+ pri("%s", ty->size == 4 ? "float" : "double");
+ break;
+ case TYptr:
+ pri("%t *", ty->child);
+ break;
+ case TYarr:
+ assert(ty->length >= 0);
+ case TYslice:
+ assert(ty->_cname);
+ pri("%s", ty->_cname);
+ return;
+ case TYfn:
+ assert(ty->_cname);
+ pri("%s", ty->_cname);
+ return;
+ }
+ if (ty->konst)
+ pri(" const");
+}
+
+static void
+pristring(const char *s, u64 n) {
+ extern int isprint(int);
+ pri("\"");
+ for (int i = 0; i < n; ++i) {
+ if (isprint(s[i]))
+ pri("%c", s[i]);
+ else
+ fprintf(outfp, "\\%.3o", s[i]);
+ }
+ pri("\"");
+}
+
+static void genexpr(struct expr *expr);
+
+void
+pri(const char *fmt, ...) {
+ va_list ap;
+ unsigned ch;
+ const char *S;
+ int ws;
+
+ va_start(ap, fmt);
+ for (char c; (c = *fmt++);) {
+ if (c != '%') {
+ fputc(c, outfp);
+ continue;
+ }
+ switch ((c = *fmt++)) {
+ case '%':
+ fputc(c, outfp);
+ break;
+ case 'c':
+ ch = bswap32(va_arg(ap, int));
+ while (ch) {
+ if (ch & 0xFF)
+ fputc(ch, outfp);
+ ch >>= 8;
+ }
+ break;
+ case 'd':
+ fprintf(outfp, "%d", va_arg(ap, int));
+ break;
+ case 'U':
+ fprintf(outfp, "%llu", (unsigned long long)va_arg(ap, u64));
+ break;
+ case 'z':
+ fprintf(outfp, "%zu", va_arg(ap, size_t));
+ break;
+ case 'f':
+ fprintf(outfp, "%.20f", va_arg(ap, double));
+ break;
+ case 's':
+ fprintf(outfp, "%s", va_arg(ap, const char *));
+ break;
+ case 'w':
+ ws = va_arg(ap, int);
+ for (int i = 0; i < ws; ++i)
+ fprintf(outfp, " ");
+ break;
+ case 'S':
+ S = va_arg(ap, const char *);
+ pristring(S, va_arg(ap, u64));
+ break;
+ case 't':
+ gentype(va_arg(ap, const struct type *));
+ break;
+ case 'e':
+ genexpr(va_arg(ap, struct expr *));
+ break;
+ default:
+ fprintf(outfp, "pri(): bad fmt '%%%c'\n", c);
+ abort();
+ break;
+ }
+ }
+ va_end(ap);
+}
+
+static void genblock(struct blockstmt block);
+
+static void
+genexpr(struct expr *ex) {
+ const struct type *ty = ex->ty;
+ switch (ex->t) {
+ case Eintlit:
+ if (ty == ty_int)
+ pri("%U", ex->i);
+ else
+ pri("((%t)%U)", ty, ex->i);
+ break;
+ case Eflolit:
+ pri("%f%s", ex->f, ty->size == 4 ? "f" : "");
+ break;
+ case Estrlit:
+ pri("%S", ex->strlit.d, ex->strlit.n);
+ break;
+ case Eboolit:
+ pri("((_Bool)%U)", ex->i);
+ break;
+ case Enullit:
+ pri("NULL");
+ break;
+ case Ename:
+ if (ex->ref->t == Dfn && ex->ref->_cname && *ex->ref->_cname)
+ pri("%s", *ex->ref->_cname);
+ else
+ pri("%s", ex->ref->name);
+ break;
+ case Eprefix:
+ pri("%c(%e)", ex->unop.op, ex->unop.child);
+ break;
+ case Epostfix:
+ pri("(%e)%c", ex->unop.op, ex->unop.child);
+ break;
+ case Ebinop:
+ pri("(%e %c %e)", ex->binop.lhs, ex->binop.op, ex->binop.rhs);
+ break;
+ case Econd:
+ pri("(%e) ? (%e) : (%e)", ex->cond.test, ex->cond.t, ex->cond.f);
+ break;
+ case Ecall:
+ pri("%e(", ex->call.callee);
+ for (int i = 0; i < ex->call.args.n; ++i) {
+ pri("%e", &ex->call.args.d[i]);
+ if (i < ex->call.args.n - 1)
+ pri(", ");
+ }
+ pri(")");
+ break;
+ case Eindex:
+ pri("%e[%e]", ex->index.lhs, ex->index.rhs);
+ break;
+
+ }
+}
+
+static void genfn(const char *cname, struct fn *fn);
+
+static void
+genstmt(struct stmt *stmt) {
+ switch (stmt->t) {
+ case Sblock:
+ genblock(stmt->block);
+ break;
+ case Sexpr:
+ genexpr(&stmt->expr);
+ pri(";\n");
+ break;
+ case Sdecl:
+ ; struct decl decl = stmt->decl;
+ switch (decl.t) {
+ case Dvar:
+ pri("%t %s", decl.var.ty, decl.name);
+ if (decl.var.ini)
+ pri(" = %e", decl.var.ini);
+ pri(";\n");
+ break;
+ case Dfn:
+ if (decl.externp)
+ genfn(decl.fn.name, &decl.fn);
+ break;
+ case Dtype: case Dmacro:
+ break;
+ }
+ break;
+ case Sifelse:
+ pri("if (%e) ", &stmt->ifelse.test);
+ genblock(stmt->ifelse.t);
+ if (stmt->ifelse.f) {
+ pri("else ");
+ genstmt(stmt->ifelse.f);
+ }
+ break;
+ case Swhile:
+ pri("while (%e) ", &stmt->loop.test);
+ genblock(stmt->loop.body);
+ break;
+ case Sfor:
+ pri("for (");
+ if (stmt->loop.ini)
+ genstmt(stmt->loop.ini);
+ pri("%e;", &stmt->loop.test);
+ if (stmt->loop.next)
+ pri(" %e", &stmt->loop.next);
+ pri(")");
+ genblock(stmt->loop.body);
+ case Siswitch:
+ break;
+ case Sreturn:
+ pri("return");
+ if (stmt->retex)
+ pri(" %e", stmt->retex);
+ pri(";\n");
+ break;
+ }
+}
+
+static void
+genblock(struct blockstmt block) {
+ pri("{\n");
+ for (int i = 0; i < block.stmts.n; ++i)
+ genstmt(&block.stmts.d[i]);
+ pri("}\n");
+}
+
+static void
+liftnestedex(struct expr *expr) {
+ // TODO implement when statement expressions exist
+ //switch (expr->t) {
+ //}
+}
+
+#define blocktostmt(Block) \
+ &(struct stmt) {Sblock, .block = Block}
+
+static void // lift nested fns and static vars
+liftnested(struct stmt *stmt) {
+ static int id;
+ if (!stmt)
+ return;
+ switch (stmt->t) {
+ case Sblock:
+ for (int i = 0; i < stmt->block.stmts.n; ++i)
+ liftnested(&stmt->block.stmts.d[i]);
+ break;
+ case Sexpr:
+ liftnestedex(&stmt->expr);
+ break;
+ case Sdecl:
+ switch (stmt->decl.t) {
+ case Dfn:
+ if (stmt->decl.fn.body) {
+ *stmt->decl._cname = xasprintf("__f%s_%d", stmt->decl.fn.name, id++);
+ genfn(*stmt->decl._cname, &stmt->decl.fn);
+ }
+ break;
+ case Dvar:
+ liftnestedex(stmt->decl.var.ini);
+ break;
+ default:
+ break;
+ }
+ break;
+ case Sifelse:
+ liftnested(stmt->ifelse.f);
+ liftnested(blocktostmt(stmt->ifelse.t));
+ break;
+ case Swhile:
+ liftnested(blocktostmt(stmt->loop.body));
+ break;
+ case Sfor:
+ liftnested(stmt->loop.ini);
+ liftnestedex(&stmt->loop.test);
+ liftnestedex(stmt->loop.next);
+ liftnested(blocktostmt(stmt->loop.body));
+ break;
+ case Siswitch:
+ liftnestedex(&stmt->iswitch.test);
+ for (int i = 0; i < stmt->iswitch.cs.n; ++i) {
+ // not visiting constant exprs
+ liftnested(blocktostmt(stmt->iswitch.cs.d[i].t));
+ }
+ if (stmt->iswitch.f)
+ liftnested(blocktostmt(*stmt->iswitch.f));
+ break;
+ case Sreturn:
+ liftnestedex(stmt->retex);
+ break;
+ }
+}
+
+static void
+genfn(const char *cname, struct fn *fn) {
+ liftnested(fn->body);
+ if (fn->body)
+ pri("\n");
+ pri("%t %s(", fn->retty, cname);
+ for (int i = 0; i < fn->params.n; ++i) {
+ pri("%t %s", fn->params.d[i].ty, fn->params.d[i].name);
+ if (i < fn->params.n - 1 || fn->variadic)
+ pri(", ");
+ }
+ if (fn->variadic)
+ pri("...");
+ pri(")");
+ if (!fn->body) {
+ pri(";\n");
+ } else {
+ genblock(fn->body->block);
+ }
+
+}
+
+static void
+gendecl(struct decl *decl, bool toplevel) {
+ switch (decl->t) {
+ case Dfn:
+ if (!decl->externp)
+ pri("static ");
+ if (decl->fn.body)
+ assert(toplevel);
+ if (!*decl->_cname)
+ *decl->_cname = (char *)decl->fn.name;
+ genfn(*decl->_cname, &decl->fn);
+ break;
+ case Dvar:
+ assert(!toplevel);
+ break;
+ case Dtype:
+ case Dmacro:
+ break;
+ }
+}
+
+static void
+defctype(const struct type *ty, void *_) {
+ static int id;
+ char **cname = (char **)&ty->_cname;
+
+ if (!ty->_cname)
+ switch (ty->t) {
+ case TYvoid: case TYbool:
+ case TYint: case TYfloat:
+ case TYptr:
+ return;
+ case TYarr:
+ assert(ty->length >= 0);
+ defctype(ty->child, NULL);
+ *cname = xasprintf("__ty%d", id++);
+ pri("typedef %t %s[%U];\n", ty->child, *cname, ty->length);
+ break;
+ case TYslice:
+ defctype(ty->child, NULL);
+ *cname = xasprintf("__ty%d", id++);
+ pri("typedef struct { %t *ptr; size_t len; } %s;\n",
+ ty->child, *cname);
+ break;
+ case TYfn:
+ *cname = xasprintf("__ty%d", id++);
+ defctype(ty->fn.retty, NULL);
+ for (int i = 0; i < ty->fn.params.n; ++i)
+ defctype(ty->fn.params.d[i], NULL);
+ pri("typedef %t %s(", ty->fn.retty, *cname);
+ for (int i = 0; i < ty->fn.params.n; ++i) {
+ pri("%t", ty->fn.params.d[i]);
+ if (i < ty->fn.params.n - 1 || ty->fn.variadic)
+ pri(", ");
+ }
+ if (ty->fn.variadic)
+ pri("...");
+ pri(");\n");
+
+
+ break;
+ }
+
+}
+
+static void
+prelude() {
+ pri("#include <stdint.h>\n"
+ "#include <stddef.h>\n");
+}
+
+void
+cgen(FILE *fp, const struct transunit *tu) {
+ outfp = fp;
+
+ prelude();
+ visittypes(defctype, NULL);
+
+ for (int i = 0; i < tu->decls.n; ++i) {
+ gendecl(&tu->decls.d[i], 1);
+ }
+}