#include "chibicc.h" #include #define GP_MAX 6 #define FP_MAX 8 int is_bad_number(double x) { return (x != x) || (x == 1./0.) || (-x == 1./0.); } /////////// PRINTING static FILE *CURR_BUFFER, // the typedefs at the very beginning *TYPE_BUFFER, // everything after the typedefs *MAIN_BUFFER, // code inside a function (after all declarations, which go into // the main buffer immediately) *CODE_BUFFER; FILE *BUFFER_STACK[8]; int N_BUFFER_STACK; static void push_buffer(FILE *which) { assert(N_BUFFER_STACK < 8); BUFFER_STACK[N_BUFFER_STACK++] = which; CURR_BUFFER = which; } static void pop_buffer(FILE *which) { assert(N_BUFFER_STACK--); assert(BUFFER_STACK[N_BUFFER_STACK] == which); CURR_BUFFER = BUFFER_STACK[N_BUFFER_STACK - 1]; } static void flush_buffer(FILE *to_buffer, FILE *from_buffer) { rewind(from_buffer); int c; while ((c = fgetc(from_buffer)) != EOF) fputc(c, to_buffer); rewind(from_buffer); ftruncate(fileno(from_buffer), 0); } __attribute__((format(printf, 1, 2))) static void printnoln(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(CURR_BUFFER, fmt, ap); va_end(ap); } __attribute__((format(printf, 1, 2))) static void println(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(CURR_BUFFER, fmt, ap); va_end(ap); fputc('\n', CURR_BUFFER); } /////////// END PRINTING static int count(void) { static int i = 1; return i++; } static void emit_line(struct Token *tok) { if (opt_line_numbers && tok && tok->line_no && tok->filename) println("#line %d \"%s\"", tok->line_no, tok->filename); } static int RETURN_TMP; void print_label(char *label) { printnoln("_l"); for (; *label; label++) { if (*label == '.') printnoln("_"); else printnoln("%c", *label); } } static void print_tok(Token *tok) { if (tok->str) printnoln("%s", tok->str); else { assert(tok->loc); for (int i = 0; i < tok->len; i++) printnoln("%c", tok->loc[i]); } } void print_obj(Obj *obj) { assert(obj->offset >= 0); if (obj->is_local || strchr(obj->name, '.')) { printnoln("_"); for (char *c = obj->name; *c; c++) if (*c == '.') printnoln("_"); else printnoln("%c", *c); printnoln("_%d", obj->offset); } else { printnoln("%s", obj->name); } } ///////// TYPES const void typedecl(Type *type) { if (type->id) return; Type *hashed = hash_lookup(type); if (hashed && !(hashed->typedecling)) { assert(hashed->id); type->id = hashed->id; if (hashed->pointer_type) type->pointer_type = hashed->pointer_type; if (hashed->return_ty) type->return_ty = hashed->return_ty; if (hashed->params) type->params = hashed->params; return; } type->typedecling = 1; type->id = count(); hash_insert(type); switch (type->kind) { case TY_FLOAT: println("typedef float Type_%d ;", type->id); break; case TY_DOUBLE: println("typedef double Type_%d ;", type->id); break; case TY_LDOUBLE: println("typedef long double Type_%d ;", type->id); break; case TY_INT: if (type->is_unsigned) println("typedef unsigned int Type_%d ;", type->id); else println("typedef int Type_%d ;", type->id); break; case TY_LONG: if (type->is_unsigned) println("typedef unsigned long Type_%d ;", type->id); else println("typedef long Type_%d ;", type->id); break; case TY_SHORT: if (type->is_unsigned) println("typedef unsigned short Type_%d ;", type->id); else println("typedef short Type_%d ;", type->id); break; case TY_VOID: println("typedef void Type_%d ;", type->id); break; case TY_BOOL: println("typedef _Bool Type_%d ;", type->id); break; case TY_CHAR: println("typedef char Type_%d ;", type->id); break; case TY_PTR: // When we recurse, we need to set the type id to 0 because we haven't // printed it out yet. type->id = 0; typedecl(type->base); if (!type->id) type->id = count(); println("typedef Type_%d * Type_%d ;", type->base->id, type->id); break; case TY_FUNC: { type->id = 0; typedecl(type->return_ty); for (Type *p = type->params; p; p = p->next) typedecl(p); if (!type->id) type->id = count(); printnoln("typedef Type_%d Type_%d ( ", type->return_ty->id, type->id); int i = 0; for (Type *p = type->params; p; p = p->next, i++) if (i) printnoln(", Type_%d ", p->id); else printnoln("Type_%d ", p->id); if (type->is_variadic) { if (i) printnoln(", ... "); // else printnoln("... "); } println(") ;"); break; } case TY_ARRAY: type->id = 0; typedecl(type->base); if (!type->id) type->id = count(); if (type->array_len == -1) println("typedef Type_%d Type_%d [ ] ;", type->base->id, type->id); else println("typedef Type_%d Type_%d [ %d ] ;", type->base->id, type->id, type->array_len); break; case TY_STRUCT: case TY_UNION: if (type->kind == TY_STRUCT) println("typedef struct Struct_%d Type_%d ;", type->id, type->id); else println("typedef union Union_%d Type_%d ;", type->id, type->id); for (Member *m = type->members; m; m = m->next) typedecl(m->ty); if (type->kind == TY_STRUCT) printnoln("struct Struct_%d { ", type->id); else printnoln("union Union_%d { ", type->id); for (Member *m = type->members; m; m = m->next) { printnoln("Type_%d ", m->ty->id); if (m->name) print_tok(m->name); else if (m->ty->kind == TY_STRUCT || m->ty->kind == TY_UNION) printnoln("___dietc_f%d", m->idx); else printnoln("__field_%d", count()); printnoln(" ; "); } println("} ;"); break; case TY_ENUM: println("typedef enum Enum_%d { EN_%d } Type_%d ;", type->id, type->id, type->id); break; case TY_VLA: assert(!"unimplemented vla?"); break; default: assert(!"unimplemented?"); break; } type->typedecling = 0; } void ensure_pointer_to(Type *type) { if (type->pointer_type) return; type->pointer_type = pointer_to(type); } //////////// END TYPES const void print_type(Type *type) { if (type->kind == TY_FUNC) { ensure_pointer_to(type); print_type(type->pointer_type); } else { push_buffer(TYPE_BUFFER); typedecl(type); pop_buffer(TYPE_BUFFER); assert(type->id); printnoln("Type_%d", type->id); } } static Obj *current_fn; static void gen_expr(Node *node, int to_tmp); static void gen_stmt(Node *node); // Round up `n` to the nearest multiple of `align`. For instance, // align_to(5, 8) returns 8 and align_to(11, 8) returns 16. int align_to(int n, int align) { return (n + align - 1) / align * align; } void decltmp(Type *type, int c) { // Write directly to the main buffer so it decls before the code. push_buffer(MAIN_BUFFER); printnoln("\t"); print_type(type); println(" t%d ;", c); pop_buffer(MAIN_BUFFER); } void decltmpptr(Type *type, int c) { ensure_pointer_to(type); decltmp(type->pointer_type, c); } // Compute the absolute address of a given node. // It's an error if a given node does not reside in memory. static void gen_addr(Node *node, int to_tmp) { switch (node->kind) { case ND_VAR: decltmpptr(node->ty, to_tmp); printnoln("\tt%d = & ", to_tmp); print_obj(node->var); println(" ;"); return; case ND_DEREF: gen_expr(node->lhs, to_tmp); return; case ND_COMMA: gen_expr(node->lhs, count()); gen_addr(node->rhs, to_tmp); return; case ND_MEMBER: { int s = count(); gen_addr(node->lhs, s); decltmpptr(node->ty, to_tmp); printnoln("\tt%d = FIELDPTR ( t%d , ", to_tmp, s); if (node->member->name) print_tok(node->member->name); else printnoln("___dietc_f%d", node->member->idx); println(" ) ;"); return; } case ND_ASSIGN: case ND_COND: if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION) { gen_expr(node, to_tmp); return; } break; case ND_VLA_PTR: assert(0); } error_tok(node->tok, "not an lvalue"); } // Generate code for a given node. static void gen_expr(Node *node, int to_tmp) { emit_line(node->tok); if (opt_type_builtins && (node->is_sizeof || node->is_alignof)) { decltmp(node->ty, to_tmp); if (node->is_sizeof) printnoln("\tt%d = sizeof ( ", to_tmp); else printnoln("\tt%d = _Alignof ( ", to_tmp); print_type(node->is_sizeof); println(" ) ;"); return; } switch (node->kind) { case ND_NULL_EXPR: return; case ND_NUM: { decltmp(node->ty, to_tmp); switch (node->ty->kind) { case TY_FLOAT: case TY_DOUBLE: case TY_LDOUBLE: println("\tt%d = %Lf ;", to_tmp, node->fval); return; default: println("\tt%d = %ld ;", to_tmp, node->val); return; } } case ND_NEG: { int c = count(); gen_expr(node->lhs, c); decltmp(node->ty, to_tmp); println("\tt%d = - t%d ;", to_tmp, c); return; } case ND_VAR: { // we inline the *getaddr(node) if (node->ty->kind == TY_ARRAY) { decltmpptr(node->ty->base, to_tmp); printnoln("\tt%d = ", to_tmp); print_obj(node->var); println(" ;"); } else { decltmp(node->ty, to_tmp); printnoln("\tt%d = ", to_tmp); print_obj(node->var); println(" ;"); } return; } case ND_MEMBER: { int c = count(); gen_addr(node, c); // TODO: what to do in this case? if (node->ty->kind == TY_ARRAY) { decltmpptr(node->ty->base, to_tmp); println("\tt%d = * t%d ;", to_tmp, c); } else { decltmp(node->ty, to_tmp); println("\tt%d = * t%d ;", to_tmp, c); } if (node->member->is_bitfield) fprintf(stderr, "WARNING: bitfields ignored"); return; } case ND_DEREF: { int c = count(); gen_expr(node->lhs, c); // TODO: deref a pointer to an array; is the temporary a pointer, or an // array?? if (node->ty->kind == TY_ARRAY) { decltmpptr(node->ty->base, to_tmp); println("\tt%d = * t%d ;", to_tmp, c); } else { decltmp(node->ty, to_tmp); println("\tt%d = * t%d ;", to_tmp, c); } return; } case ND_ADDR: gen_addr(node->lhs, to_tmp); return; case ND_ASSIGN: { int lhsa = count(); gen_addr(node->lhs, lhsa); gen_expr(node->rhs, to_tmp); if (node->lhs->kind == ND_MEMBER && node->lhs->member->is_bitfield) fprintf(stderr, "WARNING: bitfields ignored"); println("\t* t%d = t%d ;", lhsa, to_tmp); return; } case ND_STMT_EXPR: for (Node *n = node->body; n; n = n->next) { if (n->next || n->kind != ND_EXPR_STMT) gen_stmt(n); else gen_expr(n->lhs, to_tmp); } return; case ND_COMMA: gen_expr(node->lhs, count()); gen_expr(node->rhs, to_tmp); return; case ND_CAST: { if (definitely_same_type(node->lhs->ty, node->ty)) return gen_expr(node->lhs, to_tmp); int c = count(); if (node->ty->kind == TY_VOID) { gen_expr(node->lhs, c); push_buffer(TYPE_BUFFER); typedecl(node->ty); pop_buffer(TYPE_BUFFER); println("\t( Type_%d ) t%d ;", node->ty->id, c); } else if (node->lhs->ty->kind == TY_UNION) { // union *tuptr = &union int uptr = count(); gen_addr(node->lhs, uptr); // T * tc = ( T * ) tuptr decltmpptr(node->ty, c); println("\tt%d = ( Type_%d ) t%d ;", c, node->ty->pointer_type->id, uptr); // dereference it decltmp(node->ty, to_tmp); println("\tt%d = * t%d ;", to_tmp, c); } else { gen_expr(node->lhs, c); decltmp(node->ty, to_tmp); println("\tt%d = ( Type_%d ) t%d ;", to_tmp, node->ty->id, c); } return; } case ND_MEMZERO: printnoln("\tMEMZERO ( "); print_obj(node->var); println(" ) ;"); return; case ND_COND: { int c = count(), cond = count(), condfalse = count(), tmp1 = count(), tmp2 = count(); gen_expr(node->cond, cond); decltmp(ty_int, condfalse); if (node->ty->kind != TY_VOID) decltmp(node->ty, to_tmp); println("\tt%d = ! t%d ;", condfalse, cond); println("\tif ( t%d ) goto _L_else_%d ;", condfalse, c); gen_expr(node->then, tmp1); if (node->ty->kind != TY_VOID) println("\tt%d = t%d ;", to_tmp, tmp1); println("\tgoto _L_end_%d ;", c); println("\t_L_else_%d :", c); gen_expr(node->els, tmp2); if (node->ty->kind != TY_VOID) println("\tt%d = t%d ;", to_tmp, tmp2); println("\t_L_end_%d :", c); return; } case ND_NOT: { int c = count(); gen_expr(node->lhs, c); decltmp(ty_int, to_tmp); println("\tt%d = ! t%d ;", to_tmp, c); return; } case ND_BITNOT: { int c = count(); gen_expr(node->lhs, c); decltmp(node->ty, to_tmp); println("\tt%d = ~ t%d ;", to_tmp, c); return; } case ND_LOGAND: { int c = count(), lhs = count(), lhsfalse = count(), rhs = count(), rhsfalse = count(); decltmp(ty_int, to_tmp); decltmp(ty_int, lhsfalse); decltmp(ty_int, rhsfalse); gen_expr(node->lhs, lhs); println("\tt%d = 0 ;", to_tmp); println("\tt%d = ! t%d ;", lhsfalse, lhs); println("\tif ( t%d ) goto _L_false_%d ;", lhsfalse, c); gen_expr(node->rhs, rhs); println("\tt%d = ! t%d ;", rhsfalse, rhs); println("\tif ( t%d ) goto _L_false_%d ;", rhsfalse, c); println("\tt%d = 1 ;", to_tmp); println("\tgoto _L_end_%d;", c); println("\t_L_false_%d :", c); println("\t_L_end_%d :", c); return; } case ND_LOGOR: { int c = count(), lhs = count(), rhs = count(); decltmp(ty_int, to_tmp); println("\tt%d = 0 ;", to_tmp); gen_expr(node->lhs, lhs); println("\tif ( t%d ) goto _L_true_%d ;", lhs, c); gen_expr(node->rhs, rhs); println("\tif ( t%d ) goto _L_true_%d ;", rhs, c); println("\tgoto _L_end_%d ;", c); println("\t_L_true_%d :", c); println("\tt%d = 1 ;", to_tmp); println("\t_L_end_%d :", c); return; } case ND_FUNCALL: { int fnc = count(); gen_expr(node->lhs, fnc); int n_args = 0; for (Node *arg = node->args; arg; arg = arg->next) n_args++; int *args = calloc(n_args, sizeof(int)), arg_i = 0; for (Node *arg = node->args; arg; arg = arg->next, arg_i++) { args[arg_i] = count(); gen_expr(arg, args[arg_i]); } if (node->ty->kind != TY_VOID) { decltmp(node->ty, to_tmp); printnoln("\tt%d = ", to_tmp); } else { printnoln("\t"); } printnoln("t%d ( ", fnc); for (int i = 0; i < n_args; i++) { if (i) printnoln(", "); printnoln("t%d ", args[i]); } println(") ;"); return; } case ND_LABEL_VAL: assert(0); case ND_CAS: assert(0); case ND_EXCH: assert(0); } int rhsc = count(), lhsc = count(); gen_expr(node->rhs, rhsc); gen_expr(node->lhs, lhsc); char *op = NULL; switch (node->kind) { case ND_ADD: op = "+"; break; case ND_SUB: op = "-"; break; case ND_MUL: op = "*"; break; case ND_DIV: op = "/"; break; case ND_MOD: op = "%"; break; case ND_BITAND: op = "&"; break; case ND_BITOR: op = "|"; break; case ND_BITXOR: op = "^"; break; case ND_EQ: op = "=="; break; case ND_NE: op = "!="; break; case ND_LT: op = "<"; break; case ND_LE: op = "<="; break; case ND_SHL: op = "<<"; break; case ND_SHR: op = ">>"; break; } decltmp(node->ty, to_tmp); if (node->ty->base) { println("\tt%d = PTR_BINARY ( t%d , %s , t%d ) ;", to_tmp, lhsc, op, rhsc); } else { println("\tt%d = BINARY ( t%d , %s , t%d ) ;", to_tmp, lhsc, op, rhsc); } } static void gen_stmt(Node *node) { emit_line(node->tok); switch (node->kind) { case ND_IF: { int cond = count(), condfalse = count(), c = count(); gen_expr(node->cond, cond); decltmp(ty_int, condfalse); println("\tt%d = ! t%d ;", condfalse, cond); println("\tif ( t%d ) goto _L_else_%d ;", condfalse, c); gen_stmt(node->then); println("\tgoto _L_end_%d ;", c); println("\t_L_else_%d :", c); if (node->els) gen_stmt(node->els); println("\t_L_end_%d :", c); return; } case ND_FOR: { int c = count(), cond = count(), condfalse = count(); if (node->init) gen_stmt(node->init); println("\t_L_begin_%d :", c); if (node->cond) { gen_expr(node->cond, cond); decltmp(ty_int, condfalse); println("\tt%d = ! t%d ;", condfalse, cond); printnoln("\tif ( t%d ) goto ", condfalse); print_label(node->brk_label); println(" ;"); } gen_stmt(node->then); printnoln("\t"); print_label(node->cont_label); println(" :"); if (node->inc) gen_expr(node->inc, count()); println("\tgoto _L_begin_%d ;", c); printnoln("\t"); print_label(node->brk_label); println(" :"); return; } case ND_DO: { int c = count(), cond = count(); println("\t_L_begin_%d :", c); gen_stmt(node->then); printnoln("\t"); print_label(node->cont_label); println(" :"); gen_expr(node->cond, cond); println("\tif ( t%d ) goto _L_begin_%d ;", cond, c); printnoln("\t"); print_label(node->brk_label); println(" :"); return; } case ND_SWITCH: { int cond = count(), eqtmp = count(); gen_expr(node->cond, cond); decltmp(ty_int, eqtmp); for (Node *n = node->case_next; n; n = n->case_next) { if (n->begin == n->end) { println("\tt%d = BINARY ( t%d , == , %ld ) ;", eqtmp, cond, n->begin); printnoln("\tif ( t%d ) goto ", eqtmp); print_label(n->label); println(" ;"); continue; } // [GNU] Case ranges assert(!"unimplemented"); } if (node->default_case) { printnoln("\tgoto "); print_label(node->default_case->label); println(" ;"); } printnoln("\tgoto "); print_label(node->brk_label); println(" ;"); gen_stmt(node->then); printnoln("\t"); print_label(node->brk_label); println(" :"); return; } case ND_CASE: printnoln("\t"); print_label(node->label); println(" :"); gen_stmt(node->lhs); return; case ND_BLOCK: for (Node *n = node->body; n; n = n->next) gen_stmt(n); return; case ND_GOTO: printnoln("\tgoto "); print_label(node->unique_label); println(" ;"); return; case ND_GOTO_EXPR: assert(0); return; case ND_LABEL: printnoln("\t"); print_label(node->unique_label); println(" :"); gen_stmt(node->lhs); return; case ND_RETURN: if (node->lhs) { int expr = count(); gen_expr(node->lhs, expr); println("\tt%d = t%d ;", RETURN_TMP, expr); } println("\tgoto _L_RETURN ;"); return; case ND_EXPR_STMT: gen_expr(node->lhs, count()); return; case ND_ASM: assert(0); return; } error_tok(node->tok, "invalid statement"); } // Assign offsets to local variables. static void assign_lvar_offsets(Obj *prog) { for (Obj *fn = prog; fn; fn = fn->next) { if (!fn->is_function) continue; for (Obj *var = fn->params; var; var = var->next) var->offset = count(); for (Obj *var = fn->locals; var; var = var->next) if (!(var->offset)) var->offset = count(); } } void emit_constant(int pos, char *data, Relocation **rel, Type *type) { switch (type->kind) { case TY_STRUCT: { int i = 0; printnoln("{ "); for (struct Member *m = type->members; m; m = m->next, i++) { if (i) printnoln(", "); assert(!m->is_bitfield); emit_constant(pos + m->offset, data, rel, m->ty); } printnoln("} "); break; } case TY_UNION: { struct Member *biggest = type->members; for (struct Member *m = type->members; m; m = m->next) if (m->ty->size > biggest->ty->size) biggest = m; assert(biggest); printnoln("{ "); emit_constant(pos, data, rel, biggest->ty); printnoln("} "); break; } case TY_ARRAY: { printnoln("{ "); for (int i = 0; i < type->array_len; i++) { if (i) printnoln(", "); emit_constant(pos, data, rel, type->base); pos += type->base->size; } printnoln("} "); break; } case TY_CHAR: assert(type->size == 1); // we don't output ' ' so lexing can be done by splitting on whitespace if (data[pos] > ' ' && data[pos] <= '~' && data[pos] != '\'' && data[pos] != '\\') printnoln("'%c' ", data[pos++]); else printnoln("%d ", (int)data[pos++]); break; case TY_BOOL: assert(type->size == 1); printnoln("%d ", *((char*)(data+pos))); break; case TY_SHORT: assert(type->size == sizeof(short)); printnoln("%d ", *((short*)(data+pos))); break; case TY_INT: assert(type->size == sizeof(int)); printnoln("%d ", *((int*)(data+pos))); break; case TY_LONG: assert(type->size == sizeof(long)); printnoln("%ld ", *((long*)(data+pos))); break; case TY_FLOAT: { assert(type->size == sizeof(float)); float v = *((float*)(data + pos)); if (v != v) printnoln("0./0. "); else if (v == 1./0.) printnoln("1./0. "); else if (-v == 1./0.) printnoln("-1./0. "); else printnoln("%f ", v); break; } case TY_DOUBLE: { assert(type->size == sizeof(double)); double v = *((double*)(data + pos)); if (v != v) printnoln("0./0. "); else if (v == 1./0.) printnoln("1./0. "); else if (-v == 1./0.) printnoln("-1./0. "); else printnoln("%lf ", v); break; } case TY_LDOUBLE: { assert(type->size == sizeof(long double)); long double v = *((long double*)(data + pos)); if (v != v) printnoln("0./0. "); else if (v == 1./0.) printnoln("1./0. "); else if (-v == 1./0.) printnoln("-1./0. "); else printnoln("%Lf ", v); break; } case TY_PTR: assert(type->size == 8); if (*rel && (*rel)->offset == pos) { // TODO: should addend be divided by (*rel)->ty->size? printnoln("( void * ) ( & "); if (strchr(*((*rel)->label), '.')) { printnoln("_"); for (char *c = *((*rel)->label); *c; c++) { if (*c == '.') printnoln("_"); else printnoln("%c", *c); } printnoln("_0 ) + %ld ", (*rel)->addend); } else { printnoln("%s ) + %ld ", *((*rel)->label), (*rel)->addend); } *rel = (*rel)->next; } else { // TODO: actually read the absolute address out printnoln("( void * ) ( %ld ) + 0 ", *((uint64_t*)(data+pos))); } break; case TY_ENUM: switch (type->size) { case sizeof(int): printnoln("%d ", *((int*)(data+pos))); break; case sizeof(long): printnoln("%ld ", *((long*)(data+pos))); break; default: assert(0); } break; default: fprintf(stderr, "Got: %d\n", type->kind); assert(0); } } static void emit_data(Obj *prog) { for (Obj *var = prog; var; var = var->next) { push_buffer(TYPE_BUFFER); typedecl(var->ty); pop_buffer(TYPE_BUFFER); if (var->is_static && var->is_function) { printnoln("static Type_%d ", var->ty->id); print_obj(var); println(" ;"); } else if (var->is_static) { printnoln("static Type_%d ", var->ty->id); print_obj(var); println(" ;"); } else { printnoln("extern Type_%d ", var->ty->id); print_obj(var); println(" ;"); } } for (Obj *var = prog; var; var = var->next) { if (var->is_function || !var->is_definition) continue; if (var->is_static) printnoln("static "); print_type(var->ty); printnoln(" "); print_obj(var); // Common symbol assert(!(opt_fcommon && var->is_tentative)); // .data or .tdata if (var->init_data) { printnoln(" = "); Relocation *rel = var->rel; emit_constant(0, var->init_data, &rel, var->ty); } println(";"); } } static void emit_text(Obj *prog) { for (Obj *fn = prog; fn; fn = fn->next) { if (!fn->is_function || !fn->is_definition) continue; emit_line(fn->body->tok); // No code is emitted for "static inline" functions // if no one is referencing them. if (!fn->is_live) continue; current_fn = fn; if (fn->is_static) printnoln("static "); print_type(fn->ty->return_ty); printnoln(" "); printnoln("%s ( ", fn->name); int p = 0; for (Obj *param = fn->params; param; param = param->next, p++) { if (p) printnoln(", "); print_type(param->ty); printnoln(" "); print_obj(param); param->is_param = 1; printnoln(" "); } if (fn->va_area) { if (p) printnoln(", ... "); // else printnoln("... "); } println(") {"); RETURN_TMP = 0; if (fn->ty->return_ty->kind != TY_VOID) { RETURN_TMP = count(); decltmp(fn->ty->return_ty, RETURN_TMP); } for (Obj *var = fn->locals; var; var = var->next) { if (var->is_param) continue; if (var == fn->va_area) continue; if (var == fn->alloca_bottom) continue; printnoln("\t"); print_type(var->ty); printnoln(" "); print_obj(var); println(" ;"); } // Emit code push_buffer(CODE_BUFFER); gen_stmt(fn->body); pop_buffer(CODE_BUFFER); flush_buffer(MAIN_BUFFER, CODE_BUFFER); println("\t_L_RETURN :"); if (fn->ty->return_ty->kind != TY_VOID) { println("\treturn t%d ;", RETURN_TMP); } else { println("\treturn ;"); } println("}"); } } static char DIETC_HELPERS_H[] = { #include "scripts/dietc_helpers.h.bytes" , 0x00 }; void codegen(Obj *prog, FILE *out) { TYPE_BUFFER = tmpfile(); MAIN_BUFFER = tmpfile(); CODE_BUFFER = tmpfile(); assign_lvar_offsets(prog); // Everything defaults to writing to main push_buffer(MAIN_BUFFER); emit_data(prog); emit_text(prog); pop_buffer(MAIN_BUFFER); fprintf(out, "%s\n", DIETC_HELPERS_H); flush_buffer(out, TYPE_BUFFER); flush_buffer(out, MAIN_BUFFER); }