void stmt_call_print(struct expr *curr, FILE *f) { switch(curr -> print_type) { case 0: fprintf(stderr, "Expr "); expr_fprint(stderr, curr); fprintf(stderr, " has bad print_type (%d)\n", curr -> print_type); exit(1); break; case 1: expr_func_codegen(curr, "print_boolean", f); register_free(curr -> reg); break; case 2: expr_func_codegen(curr, "print_character", f); register_free(curr -> reg); break; case 3: expr_func_codegen(curr, "print_integer", f); register_free(curr -> reg); break; case 4: expr_func_codegen(curr, "print_string", f); register_free(curr -> reg); break; default: fprintf(stderr, "Expr "); expr_fprint(stderr, curr); fprintf(stderr, " has bad print_type (%d)\n", curr -> print_type); exit(1); break; } }
void print_expr(int mask, struct expr *e, int prevtoken) { if (!(cdebug & mask)) return; expr_fprint(e, stdout); }
static int expr_eq(struct expr *e1, struct expr *e2) { int res, old_count; if (e1->type != e2->type) return 0; switch (e1->type) { case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; case E_SYMBOL: return e1->left.sym == e2->left.sym; case E_NOT: return expr_eq(e1->left.expr, e2->left.expr); case E_AND: case E_OR: e1 = expr_copy(e1); e2 = expr_copy(e2); old_count = trans_count; expr_eliminate_eq(&e1, &e2); res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && e1->left.sym == e2->left.sym); expr_free(e1); expr_free(e2); trans_count = old_count; return res; case E_LIST: case E_RANGE: case E_NONE: /* panic */; } if (DEBUG_EXPR) { expr_fprint(e1, stdout); printf(" = "); expr_fprint(e2, stdout); printf(" ?\n"); } return 0; }
static struct expr *expr_join_and(struct expr *e1, struct expr *e2) { struct expr *tmp; struct symbol *sym1, *sym2; if (expr_eq(e1, e2)) return expr_copy(e1); if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) return NULL; if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) return NULL; if (e1->type == E_NOT) { tmp = e1->left.expr; if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) return NULL; sym1 = tmp->left.sym; } else sym1 = e1->left.sym; if (e2->type == E_NOT) { if (e2->left.expr->type != E_SYMBOL) return NULL; sym2 = e2->left.expr->left.sym; } else sym2 = e2->left.sym; if (sym1 != sym2) return NULL; if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) return NULL; if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) // (a) && (a='y') -> (a='y') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) // (a) && (a!='n') -> (a) return expr_alloc_symbol(sym1); if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) // (a) && (a!='m') -> (a='y') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if (sym1->type == S_TRISTATE) { if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' sym2 = e1->right.sym; if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) : expr_alloc_symbol(&symbol_no); } if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' sym2 = e2->right.sym; if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) : expr_alloc_symbol(&symbol_no); } if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) // (a!='y') && (a!='n') -> (a='m') return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) // (a!='y') && (a!='m') -> (a='n') return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) // (a!='m') && (a!='n') -> (a='m') return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) return NULL; } if (DEBUG_EXPR) { printf("optimize ("); expr_fprint(e1, stdout); printf(") && ("); expr_fprint(e2, stdout); printf(")?\n"); } return NULL; }
/* * e1 || e2 -> ? */ struct expr *expr_join_or(struct expr *e1, struct expr *e2) { struct expr *tmp; struct symbol *sym1, *sym2; if (expr_eq(e1, e2)) return expr_copy(e1); if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) return NULL; if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) return NULL; if (e1->type == E_NOT) { tmp = e1->left.expr; if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) return NULL; sym1 = tmp->left.sym; } else sym1 = e1->left.sym; if (e2->type == E_NOT) { if (e2->left.expr->type != E_SYMBOL) return NULL; sym2 = e2->left.expr->left.sym; } else sym2 = e2->left.sym; if (sym1 != sym2) return NULL; if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) return NULL; if (sym1->type == S_TRISTATE) { if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { // (a='y') || (a='m') -> (a!='n') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); } if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { // (a='y') || (a='n') -> (a!='m') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); } if (e1->type == E_EQUAL && e2->type == E_EQUAL && ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { // (a='m') || (a='n') -> (a!='y') return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); } } if (sym1->type == S_BOOLEAN && sym1 == sym2) { if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) return expr_alloc_symbol(&symbol_yes); } if (DEBUG_EXPR) { printf("optimize ("); expr_fprint(e1, stdout); printf(") || ("); expr_fprint(e2, stdout); printf(")?\n"); } return NULL; }
void stmt_codegen(struct stmt *s, FILE *f) { if(!s) return; switch(s -> kind) { case STMT_DECL: decl_codegen(s -> decl, f, SYMBOL_LOCAL); break; case STMT_EXPR: expr_codegen(s -> expr, f); register_free(s -> expr -> reg); break; case STMT_IF_ELSE: { int done = assembly_jump_label++; expr_codegen(s -> expr, f); fprintf(f, "\tCMP $0, %s\n", register_name(s -> expr -> reg)); register_free(s -> expr -> reg); if(s -> else_body) { int el = assembly_jump_label++; // jump to else fprintf(f, "\tJE L%d\n", el); stmt_codegen(s -> body, f); assembly_comment(f, "\t# jump to done label (don't evaluate else label)\n"); fprintf(f, "JMP L%d\n", done); assembly_comment(f, "\t# else label\n"); fprintf(f, "L%d:\n", el); stmt_codegen(s -> else_body, f); assembly_comment(f, "\t# done label\n"); fprintf(f, "L%d:\n", done); } else { // jump to done fprintf(f, "\tJE L%d\n", done); stmt_codegen(s -> body, f); assembly_comment(f, "\t# done label\n"); fprintf(f, "L%d:\n", done); } break; } case STMT_FOR: { // check for nulls since all exprs optional int loop = assembly_jump_label++; int done = assembly_jump_label++; if(s -> init_expr) { expr_codegen(s -> init_expr, f); register_free(s -> init_expr -> reg); } // loop label assembly_comment(f, "\t# loop label\n"); fprintf(f, "L%d:\n", loop); if(s -> expr) { expr_codegen(s -> expr, f); // don't need the return value fprintf(f, "\tCMP $0, %s\n", register_name(s -> expr -> reg)); register_free(s -> expr -> reg); assembly_comment(f, "\t# jump to done label (break condition)\n"); fprintf(f, "\tJE L%d\n", done); } stmt_codegen(s -> body, f); if(s -> next_expr) { expr_codegen(s -> next_expr, f); register_free(s -> next_expr -> reg); } assembly_comment(f, "\t# unconditional jump to next loop\n"); fprintf(f, "JMP L%d\n", loop); assembly_comment(f, "\t# done label\n"); fprintf(f, "L%d:\n", done); break; } case STMT_WHILE: fprintf(stderr, "CODEGEN_ERROR: cminor does not support while loops\n"); exit(1); break; case STMT_PRINT: { struct expr *curr = s -> expr; while(curr) { expr_codegen(curr, f); int count = 0; if(ASSEMBLY_COMMENT_FLAG) { fprintf(f, "\t# move arg %d (in %s) into %s\n", count, register_name(curr -> reg), register_arg_names[count]); } fprintf(f, "\tMOVQ %s, %s\n", register_name(curr -> reg), register_arg_names[count++]); register_free(curr -> reg); // calls func_codegen which save ret val in reg // so need to free reg beforehand stmt_call_print(curr, f); curr = curr -> next; } break; } case STMT_RET: expr_codegen(s -> expr, f); if(ASSEMBLY_COMMENT_FLAG) { fprintf(f, "\n\t# return statement (return "); expr_fprint(f, s -> expr); fprintf(f, ")\n"); } if(s -> expr) { fprintf(f, "\tMOVQ %s, %%rax\n", register_name(s -> expr -> reg)); register_free(s -> expr -> reg); } // postamble assembly_comment(f, "\t### function postamble\n"); assembly_comment(f, "\t# restore callee-saved registers\n"); fprintf(f, "\tPOPQ %%r15\n"); fprintf(f, "\tPOPQ %%r14\n"); fprintf(f, "\tPOPQ %%r13\n"); fprintf(f, "\tPOPQ %%r12\n"); fprintf(f, "\tPOPQ %%rbx\n"); assembly_comment(f, "\t# reset stack to base pointer\n"); fprintf(f, "\tMOVQ %%rbp, %%rsp\n"); assembly_comment(f, "\t# restore the old base pointer\n"); fprintf(f, "\tPOPQ %%rbp\n"); fprintf(f, "\tRET\n"); break; case STMT_BLOCK: stmt_codegen(s -> body, f); break; } stmt_codegen(s -> next, f); }
void stmt_typecheck(struct stmt *s, struct type *ret, int *returned) { if(!s) return; switch(s -> kind) { struct type *expr; struct expr *curr; case STMT_DECL: decl_typecheck(s -> decl); break; case STMT_EXPR: // need to typecheck, but don't need type type_delete(expr_typecheck(s -> expr)); break; case STMT_IF_ELSE: expr = expr_typecheck(s -> expr); if(expr -> kind != TYPE_BOOLEAN) { fprintf(stderr, "TYPE_ERROR: cannot use a(n) "); type_fprint(stderr, expr); fprintf(stderr, " as the if statement expression (currently "); expr_fprint(stderr, s -> expr); fprintf(stderr, ") requires a boolean\n"); type_error_count++; } stmt_typecheck(s -> body, ret, returned); stmt_typecheck(s -> else_body, ret, returned); type_delete(expr); break; case STMT_FOR: type_delete(expr_typecheck(s -> init_expr)); expr = expr_typecheck(s -> expr); // need to check that the middle // expression is actually there if(expr && expr -> kind != TYPE_BOOLEAN) { fprintf(stderr, "TYPE_ERROR: cannot use a "); type_fprint(stderr, expr); fprintf(stderr, " as the middle expression requires a boolean (or an empty expression)\n"); type_error_count++; } type_delete(expr); type_delete(expr_typecheck(s -> next_expr)); stmt_typecheck(s -> body, ret, returned); break; case STMT_WHILE: break; case STMT_PRINT: curr = s -> expr; while(curr) { expr = expr_typecheck(curr); // pass type through new symbol if(!type_is_atomic(expr)) { fprintf(stderr, "TYPE_ERROR: cannot print (print "); expr_fprint(stderr, s -> expr); fprintf(stderr, ") a non-atomic value ("); type_fprint(stderr, expr); fprintf(stderr, ")\n"); type_error_count++; } switch(expr -> kind) { case TYPE_BOOLEAN: curr -> print_type = 1; break; case TYPE_CHARACTER: curr -> print_type = 2; break; case TYPE_INTEGER: curr -> print_type = 3; break; case TYPE_STRING: curr -> print_type = 4; break; case TYPE_ARRAY: curr -> print_type = 0; fprintf(stderr, "Bad entry into switch on type_kind in stmt_typecheck (case STMT_PRINT)\n"); exit(1); break; case TYPE_ARRAY_DECL: curr -> print_type = 0; fprintf(stderr, "Bad entry into switch on type_kind in stmt_typecheck (case STMT_PRINT)\n"); exit(1); break; case TYPE_FUNCTION: curr -> print_type = 0; fprintf(stderr, "Bad entry into switch on type_kind in stmt_typecheck (case STMT_PRINT)\n"); exit(1); break; case TYPE_VOID: curr -> print_type = 0; fprintf(stderr, "Bad entry into switch on type_kind in stmt_typecheck (case STMT_PRINT)\n"); exit(1); break; } type_delete(expr); curr = curr -> next; } break; case STMT_RET: // always set to 1 *returned = 1; expr = expr_typecheck(s -> expr); if(!s -> expr) { type_delete(expr); expr = type_create(TYPE_VOID, 0, 0, 0); } if(!type_compare(expr, ret)) { fprintf(stderr, "TYPE_ERROR: the return statement (return "); expr_fprint(stderr, s -> expr); fprintf(stderr, ") does not match the function return type ("); type_fprint(stderr, ret); fprintf(stderr, ")\n"); type_error_count++; } type_delete(expr); break; case STMT_BLOCK: stmt_typecheck(s -> body, ret, returned); break; } stmt_typecheck(s -> next, ret, returned); }
void stmt_fprint(FILE *f, struct stmt *s, int indent) { if(!s) return; if(s -> kind != STMT_BLOCK) fprint_indent(f, indent); switch(s -> kind) { case STMT_DECL: decl_fprint(f, s -> decl, indent - 1); /*don't print new line because decl always prints new lines*/ break; case STMT_EXPR: expr_fprint(f, s -> expr); fprintf(f, ";\n"); break; case STMT_IF_ELSE: fprintf(f, "if("); expr_fprint(f, s -> expr); fprintf(f, ")"); stmt_fprint_body(f, s -> body, indent); if(s -> else_body) { fprint_indent(f, indent); fprintf(f, "else"); stmt_fprint_body(f, s -> else_body, indent); } break; case STMT_FOR: fprintf(f, "for("); expr_fprint(f, s -> init_expr); fprintf(f, "; "); expr_fprint(f, s -> expr); fprintf(f, "; "); expr_fprint(f, s -> next_expr); fprintf(f, ")"); stmt_fprint_body(f, s -> body, indent); break; case STMT_WHILE: /* *while loops are not in the CMinor spec so this should not get called. *if it does, it breaks immediately */ break; case STMT_PRINT: fprintf(f, "print"); if(s -> expr) { fprintf(f, " "); expr_fprint(f, s -> expr); } fprintf(f, ";\n"); break; case STMT_RET: fprintf(f, "return"); if(s -> expr) { fprintf(f, " "); expr_fprint(f, s -> expr); } fprintf(f, ";\n"); break; case STMT_BLOCK: fprintf(f, "{\n"); stmt_fprint(f, s -> body, indent + 1); fprint_indent(f, indent); fprintf(f, "}\n"); break; } stmt_fprint(f, s -> next, indent); }