diff options
Diffstat (limited to 'bootstrap/cgen.c')
| -rw-r--r-- | bootstrap/cgen.c | 417 |
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); + } +} |