#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: case TYstruct: case TYunion: case TYeunion: assert(ty->_cname); pri("%s", ty->_cname); break; 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); static void geniniex(struct expr *ex); 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; case 'n': geniniex(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 geniniex(struct expr *ex) { if (ex->t == Ezeroini) pri("{0}"); else { assert(ex->t == Eini); pri("{ "); for (int i = 0; i < ex->ini.args.n; ++i) { struct iniarg *arg = &ex->ini.args.d[i]; if (ex->ty->t == TYarr) pri("[%I] = %e, ", arg->idx, arg->ex); else pri(".%s = %e, ", arg->fld, arg->ex); } pri("}"); } } static void genexpr(struct expr *ex) { const struct type *ty = unconstify(ex->ty); struct decl *decl; 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 if (ex->ref->t == Dlet && ex->ref->var.id >= 0) pri("%s_%d", ex->ref->name, ex->ref->var.id); 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%s[%e]", ex->index.lhs, ex->index.lhs->ty->t == TYslice ? ".ptr" : "", 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; case Ezeroini: if (ty->t == TYarr || ty->t == TYstruct) pri("((%t){0})", ty); else pri("((%t)0)", ty); break; case Eini: pri("((%t)%n)", ty, ex); break; case Eget: pri("%e%s%s", ex->get.lhs, ex->get.lhs->ty->t == TYptr ? "->" : ".", ex->get.fld); break; case Emcall: decl = container_of(ex->mcall.met, struct decl, fn); assert(*decl->_cname); pri("%s(", *decl->_cname); for (int i = 0; i < ex->mcall.args.n; ++i) { pri("%e", &ex->mcall.args.d[i]); if (i < ex->mcall.args.n - 1) pri(", "); } pri(")"); break; case Eslice: ;static int id; // TODO range assertions pri("({ %t __start%d = %e; ", ex->slice.start->ty, id, ex->slice.start); pri("(%t) { %e%s + __start%d, %e - __start%d }; })", ex->ty, ex->slice.lhs, ex->slice.lhs->ty->t == TYslice ? ".ptr" : "", id, ex->slice.end, id); ++id; break; case Elen: pri("%e.len", ex->child); 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_%d", decl.var.ty, decl.name, decl.var.id); if (decl.var.ini) { pri(" = "); if ((decl.var.ty->t == TYstruct || decl.var.ty->t == TYarr) && (decl.var.ini->t == Ezeroini || decl.var.ini->t == Eini)) { geniniex(decl.var.ini); } else { 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("{\n"); for (int i = 0; i < stmt->loop.ini.n; ++i) genstmt(&stmt->loop.ini.d[i]); pri("for (; "); pri("%e;", &stmt->loop.test); if (stmt->loop.next) pri(" %e", stmt->loop.next); pri(")"); genblock(stmt->loop.body); pri("}\n"); 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); static void liftdecl(struct decl *decl); static void liftnestedex(struct expr *ex) { if (ex) switch (ex->t) { case Eintlit: case Eflolit: case Estrlit: case Eboolit: case Enullit: case Ename: case Ezeroini: 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: case Elen: liftnestedex(ex->child); break; case Eini: for (int i = 0; i < ex->ini.args.n; ++i) liftnestedex(&ex->ini.args.d[i].ex); break; case Eget: liftnestedex(ex->get.lhs); break; case Emcall: liftdecl(container_of(ex->mcall.met, struct decl, fn)); for (int i = 0; i < ex->call.args.n; ++i) liftnestedex(&ex->mcall.args.d[i]); break; case Eslice: liftnestedex(ex->slice.lhs); liftnestedex(ex->slice.start); liftnestedex(ex->slice.end); break; } } static void liftdecl(struct decl *decl) { static int id; switch (decl->t) { case Dfn: if (decl->fn.body) { *decl->_cname = xasprintf("__f%s_%d", decl->fn.name, id++); genfn(decl->externp, *decl->_cname, &decl->fn); } break; case Dstatic: if (!decl->externp) { assert(decl->var.ini); *decl->_cname = xasprintf("__s%s_%d", decl->name, id++); genstatic(decl->externp, *decl->_cname, &decl->var); } break; case Dlet: liftnestedex(decl->var.ini); break; default: break; } } static void // lift nested fns and static vars liftnested(struct stmt *stmt) { 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: liftdecl(&stmt->decl); break; case Sifelse: liftnested(stmt->ifelse.f); liftnested(blocktostmt(stmt->ifelse.t)); break; case Swhile: liftnested(blocktostmt(stmt->loop.body)); break; case Sfor: for (int i = 0; i < stmt->loop.ini.n; ++i) liftnested(&stmt->loop.ini.d[i]); 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; const char *kind; if (!ty->_cname) switch (ty->t) { case TYvoid: case TYbool: case TYenum: case TYint: case TYfloat: break; case TYptr: defctype(ty->child, NULL); break; case TYarr: if (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); pri("_Static_assert(sizeof(%s) == %U);\n", *cname, (u64)ty->size); pri("_Static_assert(__alignof__(%s) == %U);\n", *cname, (u64)ty->align); 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 TYstruct: kind = "struct"; goto agg; case TYunion: kind = "union"; agg: if (ty->konst) { defctype(unconstify(ty), NULL); *cname = (char *)unconstify(ty)->_cname; break; } *cname = xasprintf("__ty%s%d", ty->agg.name ? ty->agg.name : "", id++); pri("typedef %s %s %s;\n", kind, *cname, *cname); if (!ty->agg.fwd) { pri("%s %s {\n", kind, *cname); for (int i = 0; i < ty->agg.flds.n; ++i) { struct aggfield fld = ty->agg.flds.d[i]; defctype(ty->agg.flds.d[i].ty, NULL); pri("%t %s;\n", fld.ty, fld.name); } pri("};\n"); pri("_Static_assert(sizeof(%s) == %U);\n", *cname, (u64)ty->size); pri("_Static_assert(__alignof__(%s) == %U);\n", *cname, (u64)ty->align); } for (int i = 0; i < ty->agg.decls.n; ++i) liftdecl(&ty->agg.decls.d[i]); break; case TYeunion: break; } } static void prelude() { pri("#include \n" "#include \n" "#define not !\n" "#define and &&\n" "#define or ||\n"); } void cgen(FILE *fp, const struct comfile *cf) { outfp = fp; prelude(); visittypes(defctype, NULL); for (int i = 0; i < cf->decls.n; ++i) { gendecl(&cf->decls.d[i], 1); } }