#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: case TYfn: assert(ty->_cname); pri("%s", ty->_cname); return; case TYenum: gentype(ty->enu.intty); break; } 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)); if (ch == 0) fputc(ch, outfp); else 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 'I': fprintf(outfp, "%lld", (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': S = va_arg(ap, const char *); fprintf(outfp, "%s", S ? S : "(null)"); 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 = unconstify(ex->ty); switch (ex->t) { case Eintlit: intlit: if (ty == ty_int) pri("%I", ex->i); else if (ty == ty_u64) pri("((uint64_t)%U)", ex->i); else pri("((%t)%I)", 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->t == Dstatic) && *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.child, ex->unop.op); 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; case Eblock: pri("(\n"); genblock(ex->block); pri(")"); break; case Eas: pri("((%t)%e)", ex->ty, ex->child); break; case Eenumval: pri("/*%s:%s*/", ex->ty->enu.name, ex->enu.vname); ty = ex->ty->enu.intty; goto intlit; break; } } static void genfn(bool externp, const char *cname, struct fn *fn); static void genstatic(bool externp, const char *cname, struct var *var); static void genstmt(struct stmt *stmt) { const char *p = fileid2path(stmt->span.fileid); if (stmt->t != Sblock) pri("#line %d %S\n", stmt->span.line, p, (u64)strlen(p)); 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 Dlet: 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(1, decl.fn.name, &decl.fn); break; case Dstatic: if (decl.externp) genstatic(1, decl.name, &decl.var); 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 (\n"); 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); break; case Siswitch: pri("switch (%e) {", &stmt->iswitch.test); for (int i = 0; i < stmt->iswitch.cs.n; ++i) { struct iswitchcase c = stmt->iswitch.cs.d[i]; for (int i = 0; i < c.es.n; ++i) pri("case %e: ", &c.es.d[i]); genblock(c.t); pri("break;\n"); } if (stmt->iswitch.f) { pri("default: "); genblock(*stmt->iswitch.f); } pri("}\n"); 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"); } #define blocktostmt(Block) \ &(struct stmt) {Sblock, .block = Block} static void liftnested(struct stmt *stmt); // lift nested fns and static vars static void liftnestedex(struct expr *ex) { switch (ex->t) { case Eintlit: case Eflolit: case Estrlit: case Eboolit: case Enullit: case Ename: case Eenumval: break; case Eprefix: liftnestedex(ex->unop.child); break; case Epostfix: liftnestedex(ex->unop.child); break; case Ebinop: liftnestedex(ex->binop.lhs); liftnestedex(ex->binop.rhs); break; case Econd: liftnestedex(ex->cond.test); liftnestedex(ex->cond.t); liftnestedex(ex->cond.f); break; case Ecall: liftnestedex(ex->call.callee); for (int i = 0; i < ex->call.args.n; ++i) liftnestedex(&ex->call.args.d[i]); break; case Eindex: liftnestedex(ex->index.lhs); liftnestedex(ex->index.rhs); break; case Eblock: liftnested(blocktostmt(ex->block)); break; case Eas: liftnestedex(ex->child); break; } } 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.externp, *stmt->decl._cname, &stmt->decl.fn); } break; case Dstatic: if (!stmt->decl.externp) { assert(stmt->decl.var.ini); *stmt->decl._cname = xasprintf("__s%s_%d", stmt->decl.name, id++); genstatic(stmt->decl.externp, *stmt->decl._cname, &stmt->decl.var); } break; case Dlet: 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: if (stmt->retex) liftnestedex(stmt->retex); break; } } static void genfn(bool externp, const char *cname, struct fn *fn) { liftnested(fn->body); if (!externp) pri("static "); 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 genstatic(bool externp, const char *cname, struct var *var) { if (externp && !var->ini) pri("extern "); else if (!externp) pri("static "); pri("%t %s",var->ty, cname); if (var->ini) pri(" = %e", var->ini); pri(";\n"); } static void gendecl(struct decl *decl, bool toplevel) { const char *p = fileid2path(decl->span.fileid); switch (decl->t) { case Dfn: pri("#line %d %S\n", decl->span.line, p, (u64)strlen(p)); if (decl->fn.body) assert(toplevel); if (!*decl->_cname) *decl->_cname = (char *)decl->fn.name; genfn(decl->externp, *decl->_cname, &decl->fn); break; case Dstatic: pri("#line %d %S\n", decl->span.line, p, (u64)strlen(p)); genstatic(decl->externp, *decl->_cname ? *decl->_cname : decl->name, &decl->var); break; case Dlet: 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; case TYenum: *cname = xasprintf("__ty%d", id++); } } static void prelude() { pri("#include \n" "#include \n" "#define not !\n" "#define and &&\n" "#define or ||\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); } }